You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2287 lines
81 KiB

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) {
var self = this;
if (!e.meta && self.isMac() || !e.control && !self.isMac()) {
e.preventDefault();
var url = e.target.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.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: "<span>" + self.name.capitalize() + "</span>",
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.. <a href="#">Maybe add a movie.</a>',
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 <a href="' + App.createUrl("settings/searcher/providers/") + '">Settings</a>.',
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.content[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", "&laquo; " + 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:" + '<iframe src="https://couchpota.to/donate.html" style="border:none; height: 200px;" scrolling="no"></iframe>'
}));
},
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();
10 years ago
});
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
});
});
}
});