|
|
|
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 && !App.getOption("dev")) {
|
|
|
|
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.MarkAsDone, 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.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: "",
|
|
|
|
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 + (self.klass ? "." + self.klass : ""));
|
|
|
|
},
|
|
|
|
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(self.pointer = 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,
|
|
|
|
klass: "choice",
|
|
|
|
afterInject: function() {
|
|
|
|
var self = this;
|
|
|
|
var wrapper = new Element("div.select_wrapper.icon-dropdown").grab(self.select = new Element("select.select", {
|
|
|
|
events: {
|
|
|
|
change: self.addSelection.bind(self)
|
|
|
|
}
|
|
|
|
}).grab(new Element("option[text=Add option]")));
|
|
|
|
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);
|
|
|
|
});
|
|
|
|
wrapper.inject(self.input, "after");
|
|
|
|
},
|
|
|
|
addSelection: function() {
|
|
|
|
var self = this;
|
|
|
|
self.input.set("value", self.input.get("value") + self.select.get("value"));
|
|
|
|
self.input.fireEvent("change");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
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();
|
|
|
|
});
|
|
|
|
|
|
|
|
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
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|