var Uniform = new Class({ Implements: [ Options ], options: { focusedClass: "focused", holderClass: "ctrlHolder" }, initialize: function(options) { this.setOptions(options); var focused = this.options.focusedClass; var holder = "." + this.options.holderClass; $(document.body).addEvents({ "focus:relay(input, select, textarea)": function() { var parent = this.getParent(holder); if (parent) parent.addClass(focused); }, "blur:relay(input, select, textarea)": function() { var parent = this.getParent(holder); if (parent) parent.removeClass(focused); } }); } }); var Question = new Class({ initialize: function(question, hint, answers) { var self = this; self.question = question; self.hint = hint; self.answers = answers; self.createQuestion(); self.answers.each(function(answer) { self.createAnswer(answer); }); }, createQuestion: function() { var self = this, h3, hint; self.container = new Element("div.mask.question").grab(self.inner = new Element("div.inner").adopt(h3 = new Element("h3", { html: this.question }), hint = this.hint ? new Element("div.hint", { html: this.hint }) : null)).inject(document.body); setTimeout(function() { self.container.addClass("show"); self.inner.getElements("> *").each(function(el, nr) { dynamics.css(el, { opacity: 0, translateY: 50 }); dynamics.animate(el, { opacity: 1, translateY: 0 }, { type: dynamics.spring, frequency: 200, friction: 300, duration: 800, delay: 400 + nr * 100 }); }); }, 10); }, createAnswer: function(options) { var self = this; var answer = new Element("a", Object.merge(options, { class: "answer button " + (options["class"] || "") + (options.cancel ? " cancel" : "") })).inject(this.inner); if (options.cancel) { answer.addEvent("click", self.close.bind(self)); } else if (options.request) { answer.addEvent("click", function(e) { e.stop(); new Request(Object.merge(options, { url: options.href, onComplete: function() { (options.onComplete || function() {})(); self.close(); } })).send(); }); } }, close: function() { var self = this; var ended = function() { self.container.dispose(); self.container.removeEventListener("transitionend", ended); }; self.container.addEventListener("transitionend", ended, false); self.inner.getElements("> *").reverse().each(function(el, nr) { dynamics.css(el, { opacity: 1, translateY: 0 }); dynamics.animate(el, { opacity: 0, translateY: 50 }, { type: dynamics.spring, frequency: 200, friction: 300, duration: 800, anticipationSize: 175, anticipationStrength: 400, delay: nr * 100 }); }); dynamics.setTimeout(function() { self.container.removeClass("show"); }, 200); }, toElement: function() { return this.container; } }); var ScrollSpy = new Class({ Implements: [ Options, Events ], options: { container: window, max: 0, min: 0, mode: "vertical" }, initialize: function(options) { this.setOptions(options); this.container = document.id(this.options.container); this.enters = this.leaves = 0; this.inside = false; var self = this; this.listener = function(e) { var position = self.container.getScroll(), xy = position[self.options.mode == "vertical" ? "y" : "x"], min = typeOf(self.options.min) == "function" ? self.options.min() : self.options.min, max = typeOf(self.options.max) == "function" ? self.options.max() : self.options.max; if (xy >= min && (max === 0 || xy <= max)) { if (!self.inside) { self.inside = true; self.enters++; self.fireEvent("enter", [ position, self.enters, e ]); } self.fireEvent("tick", [ position, self.inside, self.enters, self.leaves, e ]); } else if (self.inside) { self.inside = false; self.leaves++; self.fireEvent("leave", [ position, self.leaves, e ]); } self.fireEvent("scroll", [ position, self.inside, self.enters, self.leaves, e ]); }; this.addListener(); }, start: function() { this.container.addEvent("scroll", this.listener); }, stop: function() { this.container.removeEvent("scroll", this.listener); }, addListener: function() { this.start(); } }); var CouchPotato = new Class({ Implements: [ Events, Options ], defaults: { page: "home", action: "index", params: {} }, pages: [], block: [], initialize: function() { var self = this; self.global_events = {}; }, setup: function(options) { var self = this; self.setOptions(options); self.c = $(document.body); self.createLayout(); self.createPages(); if (window.location.hash) History.handleInitialState(); else self.openPage(window.location.pathname); History.addEvent("change", self.openPage.bind(self)); self.c.addEvent("click:relay(.header a, .navigation a, .movie_details a, .list_list .movie)", self.ripple.bind(self)); self.c.addEvent("click:relay(a[href^=/]:not([target]))", self.pushState.bind(self)); self.c.addEvent("click:relay(a[href^=http])", self.openDerefered.bind(self)); self.touch_device = "ontouchstart" in window || navigator.msMaxTouchPoints; if (self.touch_device) { self.c.addClass("touch_enabled"); FastClick.attach(document.body); } window.addEvent("resize", self.resize.bind(self)); self.resize(); }, resize: function() { var self = this; self.mobile_screen = Math.max(document.documentElement.clientWidth, window.innerWidth || 0) <= 480; self.c[self.mobile_screen ? "addClass" : "removeClass"]("mobile"); }, ripple: function(e, el) { var self = this, button = el.getCoordinates(), x = e.page.x - button.left, y = e.page.y - button.top, ripple = new Element("div.ripple", { styles: { left: x, top: y } }); ripple.inject(el); setTimeout(function() { ripple.addClass("animate"); }, 0); setTimeout(function() { ripple.dispose(); }, 2100); }, getOption: function(name) { try { return this.options[name]; } catch (e) { return null; } }, pushState: function(e, el) { var self = this; if (!e.meta && App.isMac() || !e.control && !App.isMac()) { e.preventDefault(); var url = el.get("href"); if (e.event && e.event.button === 1) window.open(url); else if (History.getPath() != url) History.push(url); } }, isMac: function() { return Browser.platform == "mac"; }, createLayout: function() { var self = this; self.block.header = new BlockBase(); self.c.adopt($(self.block.header).addClass("header").adopt(self.block.navigation = new BlockHeader(self, {}), self.block.search = new BlockSearch(self, {}), self.block.more = new BlockMenu(self, { button_class: "icon-settings" })), new Element("div.corner_background"), self.content = new Element("div.content").adopt(self.pages_container = new Element("div.pages"), self.block.footer = new BlockFooter(self, {}))); var setting_links = [ new Element("a", { text: "About CouchPotato", href: App.createUrl("settings/about") }), new Element("a", { text: "Check for Updates", events: { click: self.checkForUpdate.bind(self, null) } }), new Element("a", { text: "Settings", href: App.createUrl("settings/general") }), new Element("a", { text: "Logs", href: App.createUrl("log") }), new Element("a", { text: "Restart", events: { click: self.restartQA.bind(self) } }), new Element("a", { text: "Shutdown", events: { click: self.shutdownQA.bind(self) } }) ]; setting_links.each(function(a) { self.block.more.addLink(a); }); new ScrollSpy({ min: 10, onLeave: function() { $(self.block.header).removeClass("with_shadow"); }, onEnter: function() { $(self.block.header).addClass("with_shadow"); } }); }, createPages: function() { var self = this; var pages = []; Object.each(Page, function(page_class, class_name) { var pg = new Page[class_name](self, { level: 1 }); self.pages[class_name] = pg; pages.include({ order: pg.order, name: class_name, class: pg }); }); pages.stableSort(self.sortPageByOrder).each(function(page) { page["class"].load(); self.fireEvent("load" + page.name); $(page["class"]).inject(self.getPageContainer()); }); self.fireEvent("load"); }, sortPageByOrder: function(a, b) { return (a.order || 100) - (b.order || 100); }, openPage: function(url) { var self = this, route = new Route(self.defaults); route.parse(rep(History.getPath())); var page_name = route.getPage().capitalize(), action = route.getAction(), params = route.getParams(), current_url = route.getCurrentUrl(), page; if (current_url == self.current_url) return; if (self.current_page) self.current_page.hide(); try { page = self.pages[page_name] || self.pages.Home; page.open(action, params, current_url); page.show(); } catch (e) { console.error("Can't open page:" + url, e); } self.current_page = page; self.current_url = current_url; }, getBlock: function(block_name) { return this.block[block_name]; }, getPage: function(name) { return this.pages[name]; }, getPageContainer: function() { return this.pages_container; }, shutdown: function() { var self = this; self.blockPage("You have shutdown. This is what is supposed to happen ;)"); Api.request("app.shutdown", { onComplete: self.blockPage.bind(self) }); self.checkAvailable(1e3); }, shutdownQA: function() { var self = this; var q = new Question("Are you sure you want to shutdown CouchPotato?", "", [ { text: "Shutdown", class: "shutdown red", events: { click: function(e) { e.preventDefault(); self.shutdown(); q.close.delay(100, q); } } }, { text: "No, nevah!", cancel: true } ]); }, restart: function(message, title) { var self = this; self.blockPage(message || "Restarting... please wait. If this takes too long, something must have gone wrong.", title); Api.request("app.restart"); self.checkAvailable(1e3); }, restartQA: function(e, message, title) { var self = this; var q = new Question("Are you sure you want to restart CouchPotato?", "", [ { text: "Restart", class: "restart orange", events: { click: function(e) { e.preventDefault(); self.restart(message, title); q.close.delay(100, q); } } }, { text: "No, nevah!", cancel: true } ]); }, checkForUpdate: function(onComplete) { var self = this; Updater.check(onComplete); self.blockPage("Please wait. If this takes too long, something must have gone wrong.", "Checking for updates"); self.checkAvailable(3e3); }, checkAvailable: function(delay, onAvailable) { var self = this; (function() { var onFailure = function() { self.checkAvailable.delay(1e3, self, [ delay, onAvailable ]); self.fireEvent("unload"); }; var request = Api.request("app.available", { timeout: 2e3, onTimeout: function() { request.cancel(); onFailure(); }, onFailure: onFailure, onSuccess: function() { if (onAvailable) onAvailable(); self.unBlockPage(); self.fireEvent("reload"); } }); }).delay(delay || 0); }, blockPage: function(message, title) { var self = this; self.unBlockPage(); self.mask = new Element("div.mask.with_message").adopt(new Element("div.message").adopt(new Element("h1", { text: title || "Unavailable" }), new Element("div", { text: message || "Something must have crashed.. check the logs ;)" }))).inject(document.body); createSpinner(self.mask); setTimeout(function() { self.mask.addClass("show"); }, 10); }, unBlockPage: function() { var self = this; if (self.mask) self.mask.get("tween").start("opacity", 0).chain(function() { this.element.destroy(); }); }, createUrl: function(action, params) { return this.options.base_url + (action ? action + "/" : "") + (params ? "?" + Object.toQueryString(params) : ""); }, openDerefered: function(e, el) { var self = this; e.stop(); var url = "http://www.dereferer.org/?" + el.get("href"); if (el.get("target") == "_blank" || e.meta && self.isMac() || e.control && !self.isMac()) window.open(url); else window.location = url; }, createUserscriptButtons: function() { var host_url = window.location.protocol + "//" + window.location.host; return new Element("div.group_userscript").adopt(new Element("a.userscript.button", { text: "Install extension", href: "https://couchpota.to/extension/", target: "_blank" }), new Element("span.or[text=or]"), new Element("span.bookmarklet").adopt(new Element("a.button.orange", { text: "+CouchPotato", href: "javascript:void((function(){var e=document.createElement('script');e.setAttribute('type','text/javascript');e.setAttribute('charset','UTF-8');e.setAttribute('src','" + host_url + Api.createUrl("userscript.bookmark") + "?host=" + encodeURI(host_url + Api.createUrl("userscript.get") + randomString() + "/") + "&r='+Math.random()*99999999);document.body.appendChild(e)})());", target: "", events: { click: function(e) { e.stop(); alert("Drag it to your bookmark ;)"); } } }), new Element("span", { text: "⇽ Drag this to your bookmarks" }))); }, on: function(name, handle) { var self = this; if (!self.global_events[name]) self.global_events[name] = []; self.global_events[name].push(handle); }, trigger: function(name, args, on_complete) { var self = this; if (!self.global_events[name]) { return; } if (!on_complete && typeOf(args) == "function") { on_complete = args; args = []; } self.global_events[name].each(function(handle) { setTimeout(function() { var results = handle.apply(handle, args || []); if (on_complete) on_complete(results); }, 0); }); }, off: function(name, handle) { var self = this; if (!self.global_events[name]) return; if (handle) { self.global_events[name] = self.global_events[name].erase(handle); } else { self.global_events[name] = []; } } }); window.App = new CouchPotato(); var Route = new Class({ defaults: null, page: "", action: "index", params: {}, initialize: function(defaults) { var self = this; self.defaults = defaults || {}; }, parse: function(path) { var self = this; if (path == "/" && location.hash) { path = rep(location.hash.replace("#", "/")); } self.current = path.replace(/^\/+|\/+$/g, ""); var url = self.current.split("/"); self.page = url.length > 0 ? url.shift() : self.defaults.page; self.action = url.length > 0 ? url.join("/") : self.defaults.action; self.params = Object.merge({}, self.defaults.params); if (url.length > 1) { var key; url.each(function(el, nr) { if (nr % 2 === 0) key = el; else if (key) { self.params[key] = el; key = null; } }); } else if (url.length == 1) { self.params[url] = true; } return self; }, getPage: function() { return this.page; }, getAction: function() { return this.action; }, getParams: function() { return this.params; }, getCurrentUrl: function() { return this.current; }, get: function(param) { return this.params[param]; } }); var p = function() { if (typeof console !== "undefined" && console !== null) console.log(arguments); }; (function() { var events; var check = function(e) { var target = $(e.target); var parents = target.getParents(); events.each(function(item) { var element = item.element; if (element != target && !parents.contains(element)) item.fn.call(element, e); }); }; Element.Events.outerClick = { onAdd: function(fn) { if (!events) { document.addEvent("click", check); events = []; } events.push({ element: this, fn: fn }); }, onRemove: function(fn) { events = events.filter(function(item) { return item.element != this || item.fn != fn; }, this); if (!events.length) { document.removeEvent("click", check); events = null; } } }; })(); function randomString(length, extra) { var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz" + (extra ? "-._!@#$%^&*()+=" : ""), string_length = length || 8, random_string = ""; for (var i = 0; i < string_length; i++) { var rnum = Math.floor(Math.random() * chars.length); random_string += chars.charAt(rnum); } return random_string; } (function() { var keyPaths = []; var saveKeyPath = function(path) { keyPaths.push({ sign: path[0] === "+" || path[0] === "-" ? parseInt(path.shift() + 1) : 1, path: path }); }; var valueOf = function(object, path) { var ptr = object; path.each(function(key) { ptr = ptr[key]; }); return ptr; }; var comparer = function(a, b) { for (var i = 0, l = keyPaths.length; i < l; i++) { var aVal = valueOf(a, keyPaths[i].path), bVal = valueOf(b, keyPaths[i].path); if (aVal > bVal) return keyPaths[i].sign; if (aVal < bVal) return -keyPaths[i].sign; } return 0; }; Array.implement({ sortBy: function() { keyPaths.empty(); Array.each(arguments, function(argument) { switch (typeOf(argument)) { case "array": saveKeyPath(argument); break; case "string": saveKeyPath(argument.match(/[+-]|[^.]+/g)); break; } }); return this.stableSort(comparer); } }); })(); var createSpinner = function(container) { var spinner = new Element("div.spinner"); container.grab(spinner); return spinner; }; var rep = function(pa) { return pa.replace(Api.getOption("url"), "/").replace(App.getOption("base_url"), "/"); }; var ApiClass = new Class({ setup: function(options) { var self = this; self.options = options; }, request: function(type, options) { var self = this, r_type = self.options.is_remote ? "JSONP" : "JSON"; return new Request[r_type](Object.merge({ callbackKey: "callback_func", method: "get", url: self.createUrl(type, { t: randomString() }) }, options)).send(); }, createUrl: function(action, params) { return this.options.url + (action || "default") + "/" + (params ? "?" + Object.toQueryString(params) : ""); }, getOption: function(name) { return this.options[name]; } }); window.Api = new ApiClass(); var PageBase = new Class({ Implements: [ Options, Events ], options: {}, order: 1, has_tab: true, name: "", icon: null, parent_page: null, sub_pages: null, initialize: function(parent_page, options) { var self = this; self.parent_page = parent_page; self.setOptions(options); self.el = new Element("div", { class: "page " + self.getPageClass() + (" level_" + (options.level || 0)) }).grab(self.content = new Element("div.scroll_content")); App.addEvent("load", function() { setTimeout(function() { if (!App.mobile_screen) { self.content.addEvent("scroll", self.preventHover.bind(self)); } }, 100); }); }, load: function() { var self = this; if (self.has_tab) { var nav; if (self.parent_page && self.parent_page.navigation) { nav = self.parent_page.navigation; } else { nav = App.getBlock("navigation"); } self.tab = nav.addTab(self.name, { href: App.createUrl(self.getPageUrl()), title: self.title, html: "" + self.name.capitalize() + "", class: self.icon ? "icon-" + self.icon : null }); } if (self.sub_pages) { self.loadSubPages(); } }, loadSubPages: function() { var self = this; var sub_pages = self.sub_pages; self.sub_pages = []; sub_pages.each(function(class_name) { var pg = new (window[self.name.capitalize() + class_name])(self, { level: 2 }); self.sub_pages[class_name] = pg; self.sub_pages.include({ order: pg.order, name: class_name, class: pg }); }); self.sub_pages.stableSort(self.sortPageByOrder).each(function(page) { page["class"].load(); self.fireEvent("load" + page.name); $(page["class"]).inject(App.getPageContainer()); }); }, open: function(action, params) { var self = this; try { var elements; if (!self[action + "Action"]) { elements = self.defaultAction(action, params); } else { elements = self[action + "Action"](params); } if (elements !== undefined) { self.content.empty(); self.content.adopt(elements); } App.getBlock("navigation").activate(self.name); self.fireEvent("opened"); } catch (e) { self.errorAction(e); self.fireEvent("error"); } }, openUrl: function(url) { if (History.getPath() != url) History.push(url); }, getPageUrl: function() { var self = this; return (self.parent_page && self.parent_page.getPageUrl ? self.parent_page.getPageUrl() + "/" : "") + self.name; }, getPageClass: function() { var self = this; return (self.parent_page && self.parent_page.getPageClass ? self.parent_page.getPageClass() + "_" : "") + self.name; }, errorAction: function(e) { p("Error, action not found", e); }, getName: function() { return this.name; }, show: function() { this.el.addClass("active"); }, hide: function() { var self = this; self.el.removeClass("active"); if (self.sub_pages) { self.sub_pages.each(function(sub_page) { sub_page["class"].hide(); }); } }, preventHover: function() { var self = this; if (self.hover_timer) clearTimeout(self.hover_timer); self.el.addClass("disable_hover"); self.hover_timer = setTimeout(function() { self.el.removeClass("disable_hover"); }, 200); }, toElement: function() { return this.el; } }); var Page = {}; var BlockBase = new Class({ Implements: [ Options, Events ], options: {}, initialize: function(parent, options) { var self = this; self.setOptions(options); self.page = parent; self.create(); }, create: function() { this.el = new Element("div.block"); }, getParent: function() { return this.page; }, hide: function() { this.el.hide(); }, show: function() { this.el.show(); }, toElement: function() { return this.el; } }); var BlockNavigation = new Class({ Extends: BlockBase, create: function() { var self = this; self.el = new Element("div.navigation").grab(self.nav = new Element("ul")); }, addTab: function(name, tab) { var self = this; return new Element("li.tab_" + (name || "unknown")).grab(new Element("a", tab)).inject(self.nav); }, activate: function(name) { var self = this; self.nav.getElements(".active").removeClass("active"); self.nav.getElements(".tab_" + name).addClass("active"); } }); var BlockHeader = new Class({ Extends: BlockNavigation, create: function() { var self = this, animation_options = { type: dynamics.spring }, couch, potato; self.parent(); self.el.adopt(self.logo = new Element("a.logo", { href: App.createUrl(""), events: { mouseenter: function() { dynamics.animate(couch, { opacity: 0, translateX: -50 }, animation_options); dynamics.animate(potato, { opacity: 1, translateX: 0 }, animation_options); }, mouseleave: function() { dynamics.animate(couch, { opacity: 1, translateX: 0 }, animation_options); dynamics.animate(potato, { opacity: 0, translateX: 50 }, animation_options); } } }).adopt(couch = new Element("span[text=Couch]"), potato = new Element("span[text=Potato]")), self.nav); } }); var BlockFooter = new Class({ Extends: BlockBase, create: function() { var self = this; self.el = new Element("div.footer"); } }); var BlockMenu = new Class({ Extends: BlockBase, options: { class: "menu" }, lis: null, create: function() { var self = this; self.lis = []; self.shown = false; self.el = new Element("div", { class: "more_menu " + self.options["class"] }).adopt(self.wrapper = new Element("div.wrapper").adopt(self.more_option_ul = new Element("ul")), self.button = new Element("a" + (self.options.button_class ? "." + self.options.button_class : ""), { text: self.options.button_text || "", events: { click: function() { if (!self.shown) { dynamics.css(self.wrapper, { opacity: 0, scale: .1, display: "block" }); dynamics.animate(self.wrapper, { opacity: 1, scale: 1 }, { type: dynamics.spring, frequency: 200, friction: 270, duration: 800 }); if (self.lis === null) self.lis = self.more_option_ul.getElements("> li").slice(0, 10); self.lis.each(function(li, nr) { dynamics.css(li, { opacity: 0, translateY: 20 }); dynamics.animate(li, { opacity: 1, translateY: 0 }, { type: dynamics.spring, frequency: 300, friction: 435, duration: 1e3, delay: 100 + nr * 40 }); }); self.shown = true; } else { self.hide(); } self.fireEvent(self.shown ? "open" : "close"); if (self.shown) { self.el.addEvent("outerClick", self.removeOuterClick.bind(self)); this.addEvent("outerClick", function(e) { if (e.target.get("tag") != "input") self.removeOuterClick(); }); } else { self.removeOuterClick(); } } } })); }, hide: function() { var self = this; dynamics.animate(self.wrapper, { opacity: 0, scale: .1 }, { type: dynamics.easeInOut, duration: 300, friction: 100, complete: function() { dynamics.css(self.wrapper, { display: "none" }); } }); self.shown = false; }, removeOuterClick: function() { var self = this; self.hide(); self.el.removeClass("show"); self.el.removeEvents("outerClick"); self.button.removeEvents("outerClick"); }, addLink: function(tab, position) { var self = this, li = new Element("li").adopt(tab).inject(self.more_option_ul, position || "bottom"); self.lis = null; return li; } }); Page.Home = new Class({ Extends: PageBase, name: "home", title: "Manage new stuff for things and such", icon: "home", indexAction: function() { var self = this; if (self.soon_list) { self.available_list.update(); if (self.late_list) self.late_list.update(); return; } self.chain = new Chain(); self.chain.chain(self.createAvailable.bind(self), self.createSoon.bind(self), self.createSuggestions.bind(self), self.createCharts.bind(self), self.createLate.bind(self)); self.chain.callChain(); }, createAvailable: function() { var self = this; self.available_list = new MovieList({ navigation: false, identifier: "snatched", load_more: false, view: "list", actions: [ MA.IMDB, MA.Release, MA.Trailer, MA.Refresh, MA.Readd, MA.Delete, MA.Category, MA.Profile ], title: "Snatched & Available", description: "These movies have been snatched or have finished downloading", on_empty_element: new Element("div").adopt(new Element("h2", { text: "Snatched & Available" }), new Element("span.no_movies", { html: 'No snatched movies or anything!? Damn.. Maybe add a movie.', events: { click: function(e) { e.preventDefault(); $(document.body).getElement(".search_form .icon-search").click(); } } })), filter: { release_status: "snatched,missing,available,downloaded,done,seeding", with_tags: "recent" }, limit: null, onLoaded: function() { self.chain.callChain(); }, onMovieAdded: function(notification) { var after_search = function(data) { if (notification.data._id != data.data._id) return; self.available_list.update(); App.off("movie.searcher.ended", after_search); }; App.on("movie.searcher.ended", after_search); } }); $(self.available_list).inject(self.content); }, createSoon: function() { var self = this; self.soon_list = new MovieList({ navigation: false, identifier: "soon", limit: 12, title: "Available soon", description: "These are being searched for and should be available soon as they will be released on DVD in the next few weeks.", filter: { random: true }, actions: [ MA.IMDB, MA.Release, MA.Trailer, MA.Refresh, MA.Delete, MA.Category, MA.Profile ], load_more: false, view: "thumb", force_view: true, api_call: "dashboard.soon", onLoaded: function() { self.chain.callChain(); } }); $(self.soon_list).inject(self.content); }, createSuggestions: function() { var self = this; self.suggestions_list = new MovieList({ navigation: false, identifier: "suggest", limit: 12, title: "Suggestions", description: "Based on your current wanted and managed items", actions: [ MA.Add, MA.SuggestIgnore, MA.SuggestSeen, MA.IMDB, MA.Trailer ], load_more: false, view: "thumb", force_view: true, api_call: "suggestion.view", onLoaded: function() { self.chain.callChain(); } }); $(self.suggestions_list).inject(self.content); }, createCharts: function() { var self = this; self.charts_list = new Charts({ onCreated: function() { self.chain.callChain(); } }); $(self.charts_list).inject(self.content); }, createLate: function() { var self = this; self.late_list = new MovieList({ navigation: false, identifier: "late", limit: 50, title: "Still not available", description: 'Try another quality profile or maybe add more providers in Settings.', filter: { late: true }, loader: false, load_more: false, view: "list", actions: [ MA.IMDB, MA.Trailer, MA.Refresh, MA.Delete, MA.Category, MA.Profile ], api_call: "dashboard.soon", onLoaded: function() { self.chain.callChain(); } }); $(self.late_list).inject(self.content); } }); Page.Settings = new Class({ Extends: PageBase, order: 50, name: "settings", title: "Change settings.", wizard_only: false, tabs: {}, lists: {}, current: "about", has_tab: false, open: function(action, params) { var self = this; self.action = action == "index" ? self.default_action : action; self.params = params; if (!self.data) self.getData(self.create.bind(self)); else { self.openTab(action); } App.getBlock("navigation").activate(self.name); }, openTab: function(action) { var self = this; action = (action == "index" ? "about" : action) || self.action; if (self.current) self.toggleTab(self.current, true); var tab = self.toggleTab(action); self.current = tab == self.tabs.general ? "general" : action; }, toggleTab: function(tab_name, hide) { var self = this; var a = hide ? "removeClass" : "addClass"; var c = "active"; tab_name = tab_name.split("/")[0]; var t = self.tabs[tab_name] || self.tabs[self.action] || self.tabs.general; var subtab = null; Object.each(self.params, function(param, subtab_name) { subtab = param; }); self.content.getElements("li." + c + " , .tab_content." + c).each(function(active) { active.removeClass(c); }); if (t.subtabs[subtab]) { t.tab[a](c); t.subtabs[subtab].tab[a](c); t.subtabs[subtab].content[a](c); if (!hide) t.subtabs[subtab].content.fireEvent("activate"); } else { t.tab[a](c); t.content[a](c); if (!hide) t.content.fireEvent("activate"); } return t; }, getData: function(onComplete) { var self = this; if (onComplete) Api.request("settings", { useSpinner: true, spinnerOptions: { target: self.content }, onComplete: function(json) { self.data = json; onComplete(json); } }); return self.data; }, getValue: function(section, name) { var self = this; try { return self.data.values[section][name]; } catch (e) { return ""; } }, showAdvanced: function() { var self = this; var c = self.advanced_toggle.checked ? "addClass" : "removeClass"; self.el[c]("show_advanced"); Cookie.write("advanced_toggle_checked", +self.advanced_toggle.checked, { duration: 365 }); }, sortByOrder: function(a, b) { return (a.order || 100) - (b.order || 100); }, create: function(json) { var self = this; self.navigation = new Element("div.navigation").adopt(new Element("h2[text=Settings]"), new Element("div.advanced_toggle").adopt(new Element("span", { text: "Show advanced" }), new Element("label.switch").adopt(self.advanced_toggle = new Element("input[type=checkbox]", { checked: +Cookie.read("advanced_toggle_checked"), events: { change: self.showAdvanced.bind(self) } }), new Element("div.toggle")))); self.tabs_container = new Element("ul.tabs"); self.containers = new Element("form.uniForm.containers", { events: { "click:relay(.enabler.disabled h2)": function(e, el) { el.getPrevious().getElements(".check").fireEvent("click"); } } }); self.showAdvanced(); var options = []; Object.each(json.options, function(section, section_name) { section.section_name = section_name; options.include(section); }); options.stableSort(self.sortByOrder).each(function(section) { var section_name = section.section_name; section.groups.stableSort(self.sortByOrder).each(function(group) { if (group.hidden) return; if (self.wizard_only && !group.wizard) return; if (!self.tabs[group.tab] || !self.tabs[group.tab].groups) self.createTab(group.tab, {}); var content_container = self.tabs[group.tab].content; if (group.subtab) { if (!self.tabs[group.tab].subtabs[group.subtab]) self.createSubTab(group.subtab, group, self.tabs[group.tab], group.tab); content_container = self.tabs[group.tab].subtabs[group.subtab].content; } if (group.list && !self.lists[group.list]) { self.lists[group.list] = self.createList(content_container); } if (!self.tabs[group.tab].groups[group.name]) self.tabs[group.tab].groups[group.name] = self.createGroup(group).inject(group.list ? self.lists[group.list] : content_container).addClass("section_" + section_name); if (group.type && group.type == "list") { if (!self.lists[group.name]) self.lists[group.name] = self.createList(content_container); else self.lists[group.name].inject(self.tabs[group.tab].groups[group.name]); } group.options.stableSort(self.sortByOrder).each(function(option) { if (option.hidden) return; var class_name = (option.type || "string").capitalize(); var input = new Option[class_name](section_name, option.name, self.getValue(section_name, option.name), option); input.inject(self.tabs[group.tab].groups[group.name]); input.fireEvent("injected"); }); }); }); setTimeout(function() { self.content.adopt(self.navigation, self.tabs_container, self.containers); self.fireEvent("create"); self.openTab(); }, 0); }, createTab: function(tab_name, tab) { var self = this; if (self.tabs[tab_name] && self.tabs[tab_name].tab) return self.tabs[tab_name].tab; var label = tab.label || (tab.name || tab_name).capitalize(); var tab_el = new Element("li.t_" + tab_name).adopt(new Element("a", { href: App.createUrl(self.name + "/" + tab_name), text: label }).adopt()).inject(self.tabs_container); if (!self.tabs[tab_name]) self.tabs[tab_name] = { label: label }; self.tabs[tab_name] = Object.merge(self.tabs[tab_name], { tab: tab_el, subtabs: {}, content: new Element("div.tab_content.tab_" + tab_name).inject(self.containers), groups: {} }); return self.tabs[tab_name]; }, createSubTab: function(tab_name, tab, parent_tab, parent_tab_name) { var self = this; if (parent_tab.subtabs[tab_name]) return parent_tab.subtabs[tab_name]; if (!parent_tab.subtabs_el) parent_tab.subtabs_el = new Element("ul.subtabs").inject(parent_tab.tab); var label = tab.subtab_label || tab_name.replace("_", " ").capitalize(); var tab_el = new Element("li.t_" + tab_name).adopt(new Element("a", { href: App.createUrl(self.name + "/" + parent_tab_name + "/" + tab_name), text: label }).adopt()).inject(parent_tab.subtabs_el); if (!parent_tab.subtabs[tab_name]) parent_tab.subtabs[tab_name] = { label: label }; parent_tab.subtabs[tab_name] = Object.merge(parent_tab.subtabs[tab_name], { tab: tab_el, content: new Element("div.tab_content.tab_" + tab_name).inject(self.containers), groups: {} }); return parent_tab.subtabs[tab_name]; }, createGroup: function(group) { var hint; if (typeOf(group.description) == "array") { hint = new Element("span.hint.more_hint", { html: group.description[0] }); createTooltip(group.description[1]).inject(hint, "top"); } else { hint = new Element("span.hint", { html: group.description || "" }); } var icon; if (group.icon) { icon = new Element("span.icon").grab(new Element("img", { src: "data:image/png;base64," + group.icon })); } var label = new Element("span.group_label", { text: group.label || group.name.capitalize() }); return new Element("fieldset", { class: (group.advanced ? "inlineLabels advanced" : "inlineLabels") + " group_" + (group.name || "") + " subtab_" + (group.subtab || "") }).grab(new Element("h2").adopt(icon, label, hint)); }, createList: function(content_container) { return new Element("div.option_list").inject(content_container); } }); var OptionBase = new Class({ Implements: [ Options, Events ], klass: "textInput", focused_class: "focused", save_on_change: true, initialize: function(section, name, value, options) { var self = this; self.setOptions(options); self.section = section; self.name = name; self.value = self.previous_value = value; self.createBase(); self.create(); self.createHint(); self.setAdvanced(); self.input.addEvents({ change: self.changed.bind(self), keyup: self.changed.bind(self) }); self.addEvent("injected", self.afterInject.bind(self)); }, createBase: function() { var self = this; self.el = new Element("div.ctrlHolder." + self.section + "_" + self.name); }, create: function() {}, createLabel: function() { var self = this; return new Element("label", { text: (self.options.label || self.options.name.replace("_", " ")).capitalize() }); }, setAdvanced: function() { this.el.addClass(this.options.advanced ? "advanced" : ""); }, createHint: function() { var self = this; if (self.options.description) { if (typeOf(self.options.description) == "array") { var hint = new Element("p.formHint.more_hint", { html: self.options.description[0] }).inject(self.el); createTooltip(self.options.description[1]).inject(hint, "top"); } else { new Element("p.formHint", { html: self.options.description || "" }).inject(self.el); } } }, afterInject: function() {}, changed: function() { var self = this; if (self.getValue() != self.previous_value) { if (self.save_on_change) { if (self.changed_timer) clearTimeout(self.changed_timer); self.changed_timer = self.save.delay(300, self); } self.fireEvent("change"); } }, save: function() { var self = this; Api.request("settings.save", { data: { section: self.section, name: self.name, value: self.getValue() }, useSpinner: true, spinnerOptions: { target: self.el }, onComplete: self.saveCompleted.bind(self) }); }, saveCompleted: function(json) { var self = this; var sc = json.success ? "save_success" : "save_failed"; self.previous_value = self.getValue(); self.el.addClass(sc); (function() { self.el.removeClass(sc); }).delay(3e3, self); }, setName: function(name) { this.name = name; }, postName: function() { var self = this; return self.section + "[" + self.name + "]"; }, getValue: function() { var self = this; return self.input.get("value"); }, getSettingValue: function() { return this.value; }, inject: function(el, position) { this.el.inject(el, position); return this.el; }, toElement: function() { return this.el; } }); var Option = {}; Option.String = new Class({ Extends: OptionBase, type: "string", create: function() { var self = this; self.el.adopt(self.createLabel(), self.input = new Element("input", { type: "text", name: self.postName(), value: self.getSettingValue(), placeholder: self.getPlaceholder() })); }, getPlaceholder: function() { return this.options.placeholder; } }); Option.Dropdown = new Class({ Extends: OptionBase, create: function() { var self = this; self.el.adopt(self.createLabel(), new Element("div.select_wrapper.icon-dropdown").grab(self.input = new Element("select", { name: self.postName() }))); Object.each(self.options.values, function(value) { new Element("option", { text: value[0], value: value[1] }).inject(self.input); }); self.input.set("value", self.getSettingValue()); } }); Option.Checkbox = new Class({ Extends: OptionBase, type: "checkbox", create: function() { var self = this; var randomId = "r-" + randomString(); self.el.adopt(self.createLabel().set("for", randomId), self.input = new Element("input", { name: self.postName(), type: "checkbox", checked: self.getSettingValue(), id: randomId })); }, getValue: function() { var self = this; return +self.input.checked; } }); Option.Password = new Class({ Extends: Option.String, type: "password", create: function() { var self = this; self.el.adopt(self.createLabel(), self.input = new Element("input", { type: "text", name: self.postName(), value: self.getSettingValue() ? "********" : "", placeholder: self.getPlaceholder() })); self.input.addEvent("focus", function() { self.input.set("value", ""); self.input.set("type", "password"); }); } }); Option.Bool = new Class({ Extends: Option.Checkbox }); Option.Enabler = new Class({ Extends: Option.Bool, create: function() { var self = this; self.el.adopt(new Element("label.switch").adopt(self.input = new Element("input", { type: "checkbox", checked: self.getSettingValue(), id: "r-" + randomString() }), new Element("div.toggle"))); }, changed: function() { this.parent(); this.checkState(); }, checkState: function() { var self = this, enabled = self.getValue(); self.parentFieldset[enabled ? "removeClass" : "addClass"]("disabled"); }, afterInject: function() { var self = this; self.parentFieldset = self.el.getParent("fieldset").addClass("enabler"); self.parentList = self.parentFieldset.getParent(".option_list"); self.el.inject(self.parentFieldset, "top"); self.checkState(); } }); Option.Int = new Class({ Extends: Option.String }); Option.Float = new Class({ Extends: Option.Int }); Option.Directory = new Class({ Extends: OptionBase, type: "span", browser: null, save_on_change: false, use_cache: false, current_dir: "", create: function() { var self = this; self.el.adopt(self.createLabel(), self.directory_inlay = new Element("span.directory", { events: { click: self.showBrowser.bind(self) } }).adopt(self.input = new Element("input", { value: self.getSettingValue(), events: { change: self.filterDirectory.bind(self), keydown: function(e) { if (e.key == "enter" || e.key == "tab") e.stop(); }, keyup: self.filterDirectory.bind(self), paste: self.filterDirectory.bind(self) } }))); self.cached = {}; }, filterDirectory: function(e) { var self = this, value = self.getValue(), path_sep = Api.getOption("path_sep"), active_selector = "li:not(.blur):not(.empty)", first; if (e.key == "enter" || e.key == "tab") { e.stop(); first = self.dir_list.getElement(active_selector); if (first) { self.selectDirectory(first.get("data-value")); } } else { if (value.substr(-1) == path_sep) { if (self.current_dir != value) self.selectDirectory(value); } else { var pd = self.getParentDir(value); if (self.current_dir != pd) self.getDirs(pd); var folder_filter = value.split(path_sep).getLast(); self.dir_list.getElements("li").each(function(li) { var valid = li.get("text").substr(0, folder_filter.length).toLowerCase() != folder_filter.toLowerCase(); li[valid ? "addClass" : "removeClass"]("blur"); }); first = self.dir_list.getElement(active_selector); if (first) { if (!self.dir_list_scroll) self.dir_list_scroll = new Fx.Scroll(self.dir_list, { transition: "quint:in:out" }); self.dir_list_scroll.toElement(first); } } } }, selectDirectory: function(dir) { var self = this; self.input.set("value", dir); self.getDirs(); }, previousDirectory: function() { var self = this; self.selectDirectory(self.getParentDir()); }, caretAtEnd: function() { var self = this; self.input.focus(); if (typeof self.input.selectionStart == "number") { self.input.selectionStart = self.input.selectionEnd = self.input.get("value").length; } else if (typeof el.createTextRange != "undefined") { self.input.focus(); var range = self.input.createTextRange(); range.collapse(false); range.select(); } }, showBrowser: function() { var self = this; if (!self.browser || self.browser && !self.browser.isVisible()) self.caretAtEnd(); if (!self.browser) { self.browser = new Element("div.directory_list").adopt(new Element("div.pointer"), new Element("div.wrapper").adopt(new Element("div.actions").adopt(self.back_button = new Element("a.back", { html: "", events: { click: self.previousDirectory.bind(self) } }), new Element("label", { text: "Hidden folders" }).adopt(self.show_hidden = new Element("input[type=checkbox]", { events: { change: function() { self.getDirs(); } } }))), self.dir_list = new Element("ul", { events: { "click:relay(li:not(.empty))": function(e, el) { e.preventDefault(); self.selectDirectory(el.get("data-value")); }, mousewheel: function(e) { e.stopPropagation(); } } }), new Element("div.actions").adopt(new Element("a.clear.button", { text: "Clear", events: { click: function(e) { self.input.set("value", ""); self.hideBrowser(e, true); } } }), new Element("a.cancel", { text: "Cancel", events: { click: self.hideBrowser.bind(self) } }), new Element("span", { text: "or" }), self.save_button = new Element("a.button.save", { text: "Save", events: { click: function(e) { self.hideBrowser(e, true); } } })))).inject(self.directory_inlay, "before"); } self.initial_directory = self.input.get("value"); self.getDirs(); self.browser.show(); self.el.addEvent("outerClick", self.hideBrowser.bind(self)); }, hideBrowser: function(e, save) { var self = this; e.preventDefault(); if (save) self.save(); else self.input.set("value", self.initial_directory); self.browser.hide(); self.el.removeEvents("outerClick"); }, fillBrowser: function(json) { var self = this, v = self.getValue(); self.data = json; var previous_dir = json.parent; if (v === "") self.input.set("value", json.home); if (previous_dir.length >= 1 && !json.is_root) { var prev_dirname = self.getCurrentDirname(previous_dir); if (previous_dir == json.home) prev_dirname = "Home Folder"; else if (previous_dir == "/" && json.platform == "nt") prev_dirname = "Computer"; self.back_button.set("data-value", previous_dir); self.back_button.set("html", "« " + prev_dirname); self.back_button.show(); } else { self.back_button.hide(); } if (self.use_cache) if (!json) json = self.cached[v]; else self.cached[v] = json; self.dir_list.empty(); if (json.dirs.length > 0) json.dirs.each(function(dir) { new Element("li", { "data-value": dir, text: self.getCurrentDirname(dir) }).inject(self.dir_list); }); else new Element("li.empty", { text: "Selected folder is empty" }).inject(self.dir_list); self.dir_list.setStyle("webkitTransform", "scale(1)"); self.caretAtEnd(); }, getDirs: function(dir) { var self = this, c = dir || self.getValue(); if (self.cached[c] && self.use_cache) { self.fillBrowser(); } else { Api.request("directory.list", { data: { path: c, show_hidden: +self.show_hidden.checked }, onComplete: function(json) { self.current_dir = c; self.fillBrowser(json); } }); } }, getParentDir: function(dir) { var self = this; if (!dir && self.data && self.data.parent) return self.data.parent; var v = dir || self.getValue(); var sep = Api.getOption("path_sep"); var dirs = v.split(sep); if (dirs.pop() === "") dirs.pop(); return dirs.join(sep) + sep; }, getCurrentDirname: function(dir) { var dir_split = dir.split(Api.getOption("path_sep")); return dir_split[dir_split.length - 2] || Api.getOption("path_sep"); }, getValue: function() { var self = this; return self.input.get("value"); } }); Option.Directories = new Class({ Extends: Option.String, directories: [], delimiter: "::", afterInject: function() { var self = this; self.el.setStyle("display", "none"); self.directories = []; self.getValue().split(self.delimiter).each(function(value) { self.addDirectory(value); }); self.addDirectory(); }, addDirectory: function(value) { var self = this; var has_empty = false; self.directories.each(function(dir) { if (!dir.getValue()) has_empty = true; }); if (has_empty) return; var dir = new Option.Directory(self.section, self.name, value || "", self.options); var parent = self.el.getParent("fieldset"); var dirs = parent.getElements(".multi_directory"); if (dirs.length === 0) $(dir).inject(parent); else $(dir).inject(dirs.getLast(), "after"); dir.save = self.saveItems.bind(self); $(dir).getElement("label").set("text", "Movie Folder"); $(dir).getElement(".formHint").destroy(); $(dir).addClass("multi_directory"); if (!value) $(dir).addClass("is_empty"); new Element("a.icon2.delete", { events: { click: self.delItem.bind(self, dir) } }).inject(dir); self.directories.include(dir); }, delItem: function(dir) { var self = this; self.directories.erase(dir); $(dir).destroy(); self.saveItems(); self.addDirectory(); }, saveItems: function() { var self = this; var dirs = []; self.directories.each(function(dir) { if (dir.getValue()) { $(dir).removeClass("is_empty"); dirs.include(dir.getValue()); } else $(dir).addClass("is_empty"); }); self.input.set("value", dirs.join(self.delimiter)); self.input.fireEvent("change"); self.addDirectory(); } }); Option.Choice = new Class({ Extends: Option.String, afterInject: function() { var self = this; self.tags = []; self.replaceInput(); self.select = new Element("select").adopt(new Element("option[text=Add option]")).inject(self.tag_input, "after"); var o = self.options.options; Object.each(o.choices, function(label, choice) { new Element("option", { text: label, value: o.pre + choice + o.post }).inject(self.select); }); self.select = new Form.Dropdown(self.select, { onChange: self.addSelection.bind(self) }); }, replaceInput: function() { var self = this; self.initialized = self.initialized ? self.initialized + 1 : 1; var value = self.getValue(); var matches = value.match(/<([^>]*)>/g); self.tag_input = new Element("ul", { events: { click: function(e) { if (e.target == self.tag_input) { var input = self.tag_input.getElement("li:last-child input"); input.fireEvent("focus"); input.focus(); input.setCaretPosition(input.get("value").length); } self.el.addEvent("outerClick", function() { self.reset(); self.el.removeEvents("outerClick"); }); } } }).inject(self.input, "after"); self.el.addClass("tag_input"); var mtches = []; if (matches) matches.each(function(match, mnr) { var pos = value.indexOf(match), msplit = [ value.substr(0, pos), value.substr(pos, match.length), value.substr(pos + match.length) ]; msplit.each(function(matchsplit, snr) { if (msplit.length - 1 == snr) { value = matchsplit; if (matches.length - 1 == mnr) mtches.append([ value ]); return; } mtches.append([ value == matchsplit ? match : matchsplit ]); }); }); if (mtches.length === 0 && value !== "") mtches.include(value); mtches.each(self.addTag.bind(self)); self.addLastTag(); self.sortable = new Sortables(self.tag_input, { revert: true, handle: "", opacity: .5, onComplete: function() { self.setOrder(); self.reset(); } }); var input_group = self.tag_input.getParent(".tab_content"); input_group.addEvent("activate", self.setAllWidth.bind(self)); }, addLastTag: function() { if (this.tag_input.getElement("li.choice:last-child") || !this.tag_input.getElement("li")) this.addTag(""); }, addTag: function(tag) { var self = this; tag = new Option.Choice.Tag(tag, { onChange: self.setOrder.bind(self), onBlur: function() { self.addLastTag(); }, onGoLeft: function() { self.goLeft(this); }, onGoRight: function() { self.goRight(this); } }); $(tag).inject(self.tag_input); if (self.initialized > 1) tag.setWidth(); else (function() { tag.setWidth(); }).delay(10, self); self.tags.include(tag); return tag; }, goLeft: function(from_tag) { var self = this; from_tag.blur(); var prev_index = self.tags.indexOf(from_tag) - 1; if (prev_index >= 0) self.tags[prev_index].selectFrom("right"); else from_tag.focus(); }, goRight: function(from_tag) { var self = this; from_tag.blur(); var next_index = self.tags.indexOf(from_tag) + 1; if (next_index < self.tags.length) self.tags[next_index].selectFrom("left"); else from_tag.focus(); }, setOrder: function() { var self = this; var value = ""; self.tag_input.getElements("li").each(function(el) { value += el.getElement("span").get("text"); }); self.addLastTag(); self.input.set("value", value); self.input.fireEvent("change"); self.setAllWidth(); }, addSelection: function() { var self = this; var tag = self.addTag(self.el.getElement(".selection input").get("value")); self.sortable.addItems($(tag)); self.setOrder(); self.setAllWidth(); }, reset: function() { var self = this; self.tag_input.destroy(); self.sortable.detach(); self.replaceInput(); self.setAllWidth(); }, setAllWidth: function() { var self = this; self.tags.each(function(tag) { tag.setWidth.delay(10, tag); }); } }); Option.Choice.Tag = new Class({ Implements: [ Options, Events ], options: { pre: "<", post: ">" }, initialize: function(tag, options) { var self = this; self.setOptions(options); self.tag = tag; self.is_choice = tag.substr(0, 1) == self.options.pre && tag.substr(-1) == self.options.post; self.create(); }, create: function() { var self = this; self.el = new Element("li", { class: self.is_choice ? "choice" : "", styles: { border: 0 }, events: { mouseover: !self.is_choice ? self.fireEvent.bind(self, "focus") : function() {} } }).adopt(self.input = new Element(self.is_choice ? "span" : "input", { text: self.tag, value: self.tag, styles: { width: 0 }, events: { keyup: self.is_choice ? null : function(e) { var current_caret_pos = self.input.getCaretPosition(); if (e.key == "left" && current_caret_pos == self.last_caret_pos) { self.fireEvent("goLeft"); } else if (e.key == "right" && self.last_caret_pos === current_caret_pos) { self.fireEvent("goRight"); } self.last_caret_pos = self.input.getCaretPosition(); self.setWidth(); self.fireEvent("change"); }, focus: self.fireEvent.bind(self, "focus"), blur: self.fireEvent.bind(self, "blur") } }), self.span = !self.is_choice ? new Element("span", { text: self.tag }) : null, self.del_button = new Element("a.delete", { events: { click: self.del.bind(self) } })); self.addEvent("focus", self.setWidth.bind(self)); }, blur: function() { var self = this; self.input.blur(); self.selected = false; self.el.removeClass("selected"); self.input.removeEvents("outerClick"); }, focus: function() { var self = this; if (!self.is_choice) { this.input.focus(); } else { if (self.selected) return; self.selected = true; self.el.addClass("selected"); self.input.addEvent("outerClick", self.blur.bind(self)); var temp_input = new Element("input", { events: { keydown: function(e) { e.stop(); if (e.key == "right") { self.fireEvent("goRight"); this.destroy(); } else if (e.key == "left") { self.fireEvent("goLeft"); this.destroy(); } else if (e.key == "backspace") { self.del(); this.destroy(); self.fireEvent("goLeft"); } } }, styles: { height: 0, width: 0, position: "absolute", top: -200 } }); self.el.adopt(temp_input); temp_input.focus(); } }, selectFrom: function(direction) { var self = this; if (!direction || self.is_choice) { self.focus(); } else { self.focus(); var position = direction == "left" ? 0 : self.input.get("value").length; self.input.setCaretPosition(position); } }, setWidth: function() { var self = this; if (self.span && self.input) { self.span.set("text", self.input.get("value")); self.input.setStyle("width", self.span.getSize().x + 2); } }, del: function() { var self = this; self.el.destroy(); self.fireEvent("change"); }, getValue: function() { return this.span.get("text"); }, toElement: function() { return this.el; } }); Option.Combined = new Class({ Extends: Option.String, afterInject: function() { var self = this; self.fieldset = self.input.getParent("fieldset"); self.combined_list = new Element("div.combined_table").inject(self.fieldset.getElement("h2"), "after"); self.values = {}; self.inputs = {}; self.items = []; self.labels = {}; self.descriptions = {}; self.options.combine.each(function(name) { self.inputs[name] = self.fieldset.getElement("input[name=" + self.section + "[" + name + "]]"); var values = self.inputs[name].get("value").split(","); values.each(function(value, nr) { if (!self.values[nr]) self.values[nr] = {}; self.values[nr][name] = value.trim(); }); self.inputs[name].getParent(".ctrlHolder").setStyle("display", "none"); self.inputs[name].addEvent("change", self.addEmpty.bind(self)); }); var head = new Element("div.head").inject(self.combined_list); Object.each(self.inputs, function(input, name) { var _in = input.getNext(); self.labels[name] = input.getPrevious().get("text"); self.descriptions[name] = _in ? _in.get("text") : ""; new Element("abbr", { class: name, text: self.labels[name], title: self.descriptions[name] }).inject(head); }); Object.each(self.values, function(item) { self.createItem(item); }); self.addEmpty(); }, add_empty_timeout: 0, addEmpty: function() { var self = this; if (self.add_empty_timeout) clearTimeout(self.add_empty_timeout); var has_empty = 0; self.items.each(function(ctrl_holder) { var empty_count = 0; self.options.combine.each(function(name) { var input = ctrl_holder.getElement("input." + name); if (input.get("value") === "" || input.get("type") == "checkbox") empty_count++; }); has_empty += empty_count == self.options.combine.length ? 1 : 0; ctrl_holder[empty_count == self.options.combine.length ? "addClass" : "removeClass"]("is_empty"); }); if (has_empty > 0) return; self.add_empty_timeout = setTimeout(function() { self.createItem({ use: true }); }, 10); }, createItem: function(values) { var self = this; var item = new Element("div.ctrlHolder").inject(self.combined_list), value_count = 0, value_empty = 0; self.options.combine.each(function(name) { var value = values[name] || ""; if (name.indexOf("use") != -1) { var checkbox = new Element("input[type=checkbox]." + name, { checked: +value, events: { click: self.saveCombined.bind(self), change: self.saveCombined.bind(self) } }).inject(item); } else { value_count++; new Element("input[type=text]." + name, { value: value, placeholder: self.labels[name] || name, events: { keyup: self.saveCombined.bind(self), change: self.saveCombined.bind(self) } }).inject(item); if (!value) value_empty++; } }); item[value_empty == value_count ? "addClass" : "removeClass"]("is_empty"); new Element("a.icon-cancel.delete", { events: { click: self.deleteCombinedItem.bind(self) } }).inject(item); self.items.include(item); }, saveCombined: function() { var self = this, temp = {}; self.items.each(function(item, nr) { self.options.combine.each(function(name) { var input = item.getElement("input." + name); if (item.hasClass("is_empty")) return; if (!temp[name]) temp[name] = []; temp[name][nr] = input.get("type") == "checkbox" ? +input.get("checked") : input.get("value").trim(); }); }); self.options.combine.each(function(name) { self.inputs[name].set("value", (temp[name] || []).join(",")); self.inputs[name].fireEvent("change"); }); self.addEmpty(); }, deleteCombinedItem: function(e) { var self = this; e.preventDefault(); var item = e.target.getParent(); self.items.erase(item); item.destroy(); self.saveCombined(); } }); var createTooltip = function(description) { var tip = new Element("div.tooltip", { events: { mouseenter: function() { tip.addClass("shown"); }, mouseleave: function() { tip.removeClass("shown"); } } }).adopt(new Element("a.icon2.info"), new Element("div.tip", { html: description })); return tip; }; var AboutSettingTab = new Class({ tab: "", content: "", initialize: function() { var self = this; App.addEvent("loadSettings", self.addSettings.bind(self)); }, addSettings: function() { var self = this; self.settings = App.getPage("Settings"); self.settings.addEvent("create", function() { var tab = self.settings.createTab("about", { label: "About", name: "about" }); self.tab = tab.tab; self.content = tab.content; self.createAbout(); }); self.settings.default_action = "about"; }, createAbout: function() { var self = this; var millennium = new Date(2008, 7, 16), today = new Date(), one_day = 1e3 * 60 * 60 * 24; self.settings.createGroup({ label: "About This CouchPotato", name: "variables" }).inject(self.content).adopt(new Element("dl.info").adopt(new Element("dt[text=Version]"), self.version_text = new Element("dd.version", { text: "Getting version...", events: { click: App.checkForUpdate.bind(App, function(json) { self.fillVersion(json.info); }), mouseenter: function() { this.set("text", "Check for updates"); }, mouseleave: function() { self.fillVersion(Updater.getInfo()); } } }), new Element("dt[text=Updater]"), self.updater_type = new Element("dd.updater"), new Element("dt[text=ID]"), new Element("dd", { text: App.getOption("pid") }), new Element("dt[text=Directories]"), new Element("dd", { text: App.getOption("app_dir") }), new Element("dd", { text: App.getOption("data_dir") }), new Element("dt[text=Startup Args]"), new Element("dd", { html: App.getOption("args") }), new Element("dd", { html: App.getOption("options") }))); if (!self.fillVersion(Updater.getInfo())) Updater.addEvent("loaded", self.fillVersion.bind(self)); self.settings.createGroup({ name: "Help Support CouchPotato" }).inject(self.content).adopt(new Element("div.usenet").adopt(new Element("span", { text: "Help support CouchPotato and save some money for yourself by signing up for an account at" }), new Element("a", { href: "https://usenetserver.com/partners/?a_aid=couchpotato&a_bid=3f357c6f", target: "_blank", text: "UsenetServer" }), new Element("span[text=or]"), new Element("a", { href: "http://www.newshosting.com/partners/?a_aid=couchpotato&a_bid=a0b022df", target: "_blank", text: "Newshosting" }), new Element("span", { text: ". For as low as $7.95 per month, you’ll get:" }), new Element("ul").adopt(new Element("li.icon-ok", { text: Math.ceil((today.getTime() - millennium.getTime()) / one_day) + " days retention" }), new Element("li.icon-ok[text=No speed or download limits]"), new Element("li.icon-ok[text=Free SSL Encrypted connections]"))), new Element("div.donate", { html: "Or support me via:" + '' })); }, fillVersion: function(json) { if (!json) return; var self = this; var date = new Date(json.version.date * 1e3); self.version_text.set("text", json.version.hash + (json.version.date ? " (" + date.toLocaleString() + ")" : "")); self.updater_type.set("text", json.version.type != json.branch ? json.version.type + ", " + json.branch : json.branch); } }); window.addEvent("domready", function() { new AboutSettingTab(); }); window.addEvent("domready", function() { var b = $(document.body), login_page = b.hasClass("login"); if (login_page) { var form = b.getElement("form"), els = b.getElements("h1, .username, .password, .remember_me, .button"); els.each(function(el, nr) { dynamics.css(el, { opacity: 0, translateY: 50 }); dynamics.animate(el, { opacity: 1, translateY: 0 }, { type: dynamics.spring, frequency: 200, friction: 300, duration: 800, anticipationSize: 175, anticipationStrength: 400, delay: nr * 100 }); }); } });