|
|
|
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));
|
|
|
|
|
|
|
|
// Check if device is touchenabled
|
|
|
|
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();
|
|
|
|
|
|
|
|
//self.checkCache();
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
checkCache: function(){
|
|
|
|
window.addEventListener('load', function() {
|
|
|
|
window.applicationCache.addEventListener('updateready', function(e) {
|
|
|
|
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
|
|
|
|
window.applicationCache.swapCache();
|
|
|
|
window.location.reload();
|
|
|
|
}
|
|
|
|
}, false);
|
|
|
|
|
|
|
|
}, false);
|
|
|
|
},
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
requestTimeout(function(){ ripple.addClass('animate'); }, 0);
|
|
|
|
requestTimeout(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');
|
|
|
|
|
|
|
|
// Middle click
|
|
|
|
if(e.event && e.event.button === 1)
|
|
|
|
window.open(url);
|
|
|
|
else if(History.getPath() != url)
|
|
|
|
History.push(url);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
self.fireEvent('history.push');
|
|
|
|
},
|
|
|
|
|
|
|
|
isMac: function(){
|
|
|
|
return Browser.platform == 'mac';
|
|
|
|
},
|
|
|
|
|
|
|
|
createLayout: function(){
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
// TODO : sorry, it's a crutch... Need to move self.hide_update initialization to appropriate place..
|
|
|
|
// WebUI Feature:
|
|
|
|
self.hide_update = !! App.options && App.options.webui_feature && App.options.webui_feature.hide_menuitem_update;
|
|
|
|
|
|
|
|
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.support = new Element('a.donate.icon-donate', {
|
|
|
|
'href': 'https://couchpota.to/support/',
|
|
|
|
'target': '_blank'
|
|
|
|
}).grab(
|
|
|
|
new Element('span', {
|
|
|
|
'text': 'Donate'
|
|
|
|
})
|
|
|
|
),
|
|
|
|
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': '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)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
];
|
|
|
|
|
|
|
|
if (!self.hide_update){
|
|
|
|
setting_links.splice(1, 0, new Element('a', {
|
|
|
|
'text': 'Check for Updates',
|
|
|
|
'events': {
|
|
|
|
'click': self.checkForUpdate.bind(self, null)
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
setting_links.each(function(a){
|
|
|
|
self.block.more.addLink(a);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Set theme
|
|
|
|
self.addEvent('setting.save.core.dark_theme', function(enabled){
|
|
|
|
document.html[enabled ? 'addClass' : 'removeClass']('dark');
|
|
|
|
});
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
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(1000);
|
|
|
|
},
|
|
|
|
|
|
|
|
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();
|
|
|
|
requestTimeout(q.close.bind(q), 100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
'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(1000);
|
|
|
|
},
|
|
|
|
|
|
|
|
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);
|
|
|
|
requestTimeout(q.close.bind(q), 100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
'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(3000);
|
|
|
|
},
|
|
|
|
|
|
|
|
checkAvailable: function(delay, onAvailable){
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
requestTimeout(function(){
|
|
|
|
|
|
|
|
var onFailure = function(){
|
|
|
|
requestTimeout(function(){
|
|
|
|
self.checkAvailable(delay, onAvailable);
|
|
|
|
}, 1000);
|
|
|
|
self.fireEvent('unload');
|
|
|
|
};
|
|
|
|
|
|
|
|
var request = Api.request('app.available', {
|
|
|
|
'timeout': 2000,
|
|
|
|
'onTimeout': function(){
|
|
|
|
request.cancel();
|
|
|
|
onFailure();
|
|
|
|
},
|
|
|
|
'onFailure': onFailure,
|
|
|
|
'onSuccess': function(){
|
|
|
|
if(onAvailable)
|
|
|
|
onAvailable();
|
|
|
|
self.unBlockPage();
|
|
|
|
self.fireEvent('reload');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
}, 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);
|
|
|
|
|
|
|
|
requestTimeout(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 = el.get('href');
|
|
|
|
if(self.getOption('dereferer')){
|
|
|
|
url = self.getOption('dereferer') + 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('div').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', {
|
|
|
|
'text': '+CouchPotato',
|
|
|
|
/* jshint ignore:start */
|
|
|
|
'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)})());",
|
|
|
|
/* jshint ignore:end */
|
|
|
|
'target': '',
|
|
|
|
'events': {
|
|
|
|
'click': function(e){
|
|
|
|
(e).stop();
|
|
|
|
alert('Drag it to your bookmark ;)');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
new Element('span', {
|
|
|
|
'text': '⇽ Drag this to your bookmarks'
|
|
|
|
})
|
|
|
|
)
|
|
|
|
),
|
|
|
|
new Element('img', {
|
|
|
|
'src': 'https://couchpota.to/media/images/userscript.gif'
|
|
|
|
})
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Global events
|
|
|
|
*/
|
|
|
|
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 = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create parallel callback
|
|
|
|
self.global_events[name].each(function(handle){
|
|
|
|
|
|
|
|
requestTimeout(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;
|
|
|
|
|
|
|
|
// Remove single
|
|
|
|
if(handle){
|
|
|
|
self.global_events[name] = self.global_events[name].erase(handle);
|
|
|
|
}
|
|
|
|
// Reset full event
|
|
|
|
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'), '/');
|
|
|
|
};
|