Notifications

This commit is contained in:
Ruud
2011-04-09 20:18:51 +02:00
parent 288aa8c0a1
commit 85fc1c01ee
67 changed files with 3270 additions and 400 deletions
+37 -33
View File
@@ -34,6 +34,7 @@ Block.Search = new Class({
self.spinner = new Spinner(self.result_container);
self.OuterClickStack = new EventStack.OuterClick();
History.addEvent('change', self.hideResults.bind(self, true));
//debug
//self.input.set('value', 'kick ass')
@@ -126,16 +127,16 @@ Block.Search = new Class({
Object.each(json.movies, function(movie){
if(!movie.imdb || (movie.imdb && !self.results.getElement('#'+movie.imdb))){
// if(!movie.imdb || (movie.imdb && !self.results.getElement('#'+movie.imdb))){
var m = new Block.Search.Item(movie);
$(m).inject(self.results)
self.movies[movie.imdb || 'r-'+Math.floor(Math.random()*10000)] = m
}
else {
self.movies[movie.imdb].alternativeName({
'name': movie.name
})
}
// }
// else {
// self.movies[movie.imdb].alternativeTitle({
// 'title': movie.title
// })
// }
});
@@ -157,7 +158,7 @@ Block.Search.Item = new Class({
var self = this;
self.info = info;
self.alternative_names = [];
self.alternative_titles = [];
self.create();
@@ -167,7 +168,7 @@ Block.Search.Item = new Class({
create: function(){
var self = this;
var info = self.info
var info = self.info;
self.el = new Element('div.movie', {
'id': info.imdb
@@ -182,12 +183,12 @@ Block.Search.Item = new Class({
'click': self.showOptions.bind(self)
}
}).adopt(
self.thumbnail = info.poster ? new Element('img.thumbnail', {
'src': info.poster
self.thumbnail = info.images.posters.length > 0 ? new Element('img.thumbnail', {
'src': info.images.posters[0]
}) : null,
new Element('div.info').adopt(
self.name = new Element('h2', {
'text': info.name
self.title = new Element('h2', {
'text': info.titles[0]
}).adopt(
self.year = info.year ? new Element('span', {
'text': info.year
@@ -214,15 +215,18 @@ Block.Search.Item = new Class({
})
}
self.alternativeName({
'name': info.name
});
info.titles.each(function(title){
self.alternativeTitle({
'title': title
});
})
},
alternativeName: function(alternative){
alternativeTitle: function(alternative){
var self = this;
self.alternative_names.include(alternative);
self.alternative_titles.include(alternative);
},
showOptions: function(){
@@ -246,8 +250,8 @@ Block.Search.Item = new Class({
Api.request('movie.add', {
'data': {
'identifier': self.info.imdb,
'name': self.name_select.get('value'),
'quality': self.quality_select.get('value')
'title': self.title_select.get('value'),
'profile_id': self.profile_select.get('value')
},
'useSpinner': true,
'spinnerTarget': self.options,
@@ -277,14 +281,14 @@ Block.Search.Item = new Class({
self.options.adopt(
new Element('div').adopt(
self.info.poster ? new Element('img.thumbnail', {
'src': self.info.poster
self.info.images.posters.length > 0 ? new Element('img.thumbnail', {
'src': self.info.images.posters[0]
}) : null,
self.name_select = new Element('select', {
'name': 'name'
self.title_select = new Element('select', {
'name': 'title'
}),
self.quality_select = new Element('select', {
'name': 'profile_identifier'
self.profile_select = new Element('select', {
'name': 'profile'
}),
new Element('a.button', {
'text': 'Add',
@@ -295,17 +299,17 @@ Block.Search.Item = new Class({
)
);
Array.each(self.alternative_names, function(alt){
Array.each(self.alternative_titles, function(alt){
new Element('option', {
'text': alt.name
}).inject(self.name_select)
'text': alt.title
}).inject(self.title_select)
})
Array.each(Quality.profiles, function(q){
Array.each(Quality.profiles, function(profile){
new Element('option', {
'value': q.indentifier,
'text': q.label
}).inject(self.quality_select)
'value': profile.id ? profile.id : profile.data.id,
'text': profile.label ? profile.label : profile.data.label
}).inject(self.profile_select)
});
self.options.addClass('set');
+13 -2
View File
@@ -114,8 +114,8 @@ var ApiClass = new Class({
}, options)).send()
},
createUrl: function(action){
return this.options.url + (action || 'default') + '/'
createUrl: function(action, params){
return this.options.url + (action || 'default') + '/' + (params ? '?'+Object.toQueryString(params) : '')
},
getOption: function(name){
@@ -186,6 +186,17 @@ var p = function(){
console.log(arguments)
};
function randomString(length, extra) {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz" + (extra ? '-._!@#$%^&*()+=' : '');
var stringLength = length || 8;
var randomString = '';
for (var i = 0; i < stringLength; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomString += chars.charAt(rnum);
}
return randomString;
}
(function(){
var keyPaths = [];
+77
View File
@@ -0,0 +1,77 @@
var File = new Class({
initialize: function(file){
var self = this;
self.data = file;
self.type = File.Type.get(file.type_id);
self['create'+(self.type.type).capitalize()]()
},
createImage: function(){
var self = this;
self.el = new Element('div.type_image').adopt(
new Element('img', {
'src': Api.createUrl('file.cache') + self.data.path.substring(1) + '/'
})
)
},
toElement: function(){
return this.el;
}
});
var FileSelect = new Class({
multiple: function(type, files, single){
var results = files.filter(function(file){
return file.type_id == File.Type.get(type).id;
});
if(single){
results = new File(results.pop());
}
else {
}
return results;
},
single: function(type, files){
return this.multiple(type, files, true);
}
});
window.File.Select = new FileSelect();
var FileTypeBase = new Class({
setup: function(types){
var self = this;
self.typesById = {};
self.typesByKey = {};
Object.each(types, function(type){
self.typesByKey[type.identifier] = type;
self.typesById[type.id] = type;
});
},
get: function(identifier){
if(typeOf(identifier) == 'number')
return this.typesById[identifier]
else
return this.typesByKey[identifier]
}
});
window.File.Type = new FileTypeBase();
+81 -2
View File
@@ -3,10 +3,10 @@
MooTools: the javascript framework
web build:
- http://mootools.net/core/bd6349d3fbc489736e5aefb01157c8a8
- http://mootools.net/core/c1215700e7dedaa9d48503126daf2111
packager build:
- packager build Core/Class Core/Class.Extras Core/Element Core/Element.Style Core/Element.Dimensions Core/Fx.Tween Core/Fx.Transitions Core/Request.JSON Core/DOMReady
- packager build Core/Class Core/Class.Extras Core/Element Core/Element.Style Core/Element.Dimensions Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request.JSON Core/DOMReady
/*
---
@@ -4085,6 +4085,85 @@ Element.implement({
});
/*
---
name: Fx.Morph
description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
license: MIT-style license.
requires: Fx.CSS
provides: Fx.Morph
...
*/
Fx.Morph = new Class({
Extends: Fx.CSS,
initialize: function(element, options){
this.element = this.subject = document.id(element);
this.parent(options);
},
set: function(now){
if (typeof now == 'string') now = this.search(now);
for (var p in now) this.render(this.element, p, now[p], this.options.unit);
return this;
},
compute: function(from, to, delta){
var now = {};
for (var p in from) now[p] = this.parent(from[p], to[p], delta);
return now;
},
start: function(properties){
if (!this.check(properties)) return this;
if (typeof properties == 'string') properties = this.search(properties);
var from = {}, to = {};
for (var p in properties){
var parsed = this.prepare(this.element, p, properties[p]);
from[p] = parsed.from;
to[p] = parsed.to;
}
return this.parent(from, to);
}
});
Element.Properties.morph = {
set: function(options){
this.get('morph').cancel().setOptions(options);
return this;
},
get: function(){
var morph = this.retrieve('morph');
if (!morph){
morph = new Fx.Morph(this, {link: 'cancel'});
this.store('morph', morph);
}
return morph;
}
};
Element.implement({
morph: function(props){
this.get('morph').start(props);
return this;
}
});
/*
---
@@ -1,6 +1,6 @@
// MooTools: the javascript framework.
// Load this file's selection again by visiting: http://mootools.net/more/a6033a03c64d978cd0033a1d420e16e0
// Or build this file again with packager using: packager build More/Element.Forms More/Element.Delegation More/Element.Shortcuts More/Request.JSONP More/Spinner
// Load this file's selection again by visiting: http://mootools.net/more/2b832e45b9bf2f9e5fdbdafc9b16febf
// Or build this file again with packager using: packager build More/Element.Forms More/Element.Delegation More/Element.Shortcuts More/Fx.Slide More/Sortables More/Request.JSONP More/Spinner
/*
---
@@ -767,6 +767,849 @@ Document.implement({
});
/*
---
script: Fx.Slide.js
name: Fx.Slide
description: Effect to slide an element in and out of view.
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Fx
- Core/Element.Style
- /MooTools.More
provides: [Fx.Slide]
...
*/
Fx.Slide = new Class({
Extends: Fx,
options: {
mode: 'vertical',
wrapper: false,
hideOverflow: true,
resetHeight: false
},
initialize: function(element, options){
element = this.element = this.subject = document.id(element);
this.parent(options);
options = this.options;
var wrapper = element.retrieve('wrapper'),
styles = element.getStyles('margin', 'position', 'overflow');
if (options.hideOverflow) styles = Object.append(styles, {overflow: 'hidden'});
if (options.wrapper) wrapper = document.id(options.wrapper).setStyles(styles);
if (!wrapper) wrapper = new Element('div', {
styles: styles
}).wraps(element);
element.store('wrapper', wrapper).setStyle('margin', 0);
if (element.getStyle('overflow') == 'visible') element.setStyle('overflow', 'hidden');
this.now = [];
this.open = true;
this.wrapper = wrapper;
this.addEvent('complete', function(){
this.open = (wrapper['offset' + this.layout.capitalize()] != 0);
if (this.open && options.resetHeight) wrapper.setStyle('height', '');
}, true);
},
vertical: function(){
this.margin = 'margin-top';
this.layout = 'height';
this.offset = this.element.offsetHeight;
},
horizontal: function(){
this.margin = 'margin-left';
this.layout = 'width';
this.offset = this.element.offsetWidth;
},
set: function(now){
this.element.setStyle(this.margin, now[0]);
this.wrapper.setStyle(this.layout, now[1]);
return this;
},
compute: function(from, to, delta){
return [0, 1].map(function(i){
return Fx.compute(from[i], to[i], delta);
});
},
start: function(how, mode){
if (!this.check(how, mode)) return this;
this[mode || this.options.mode]();
var margin = this.element.getStyle(this.margin).toInt(),
layout = this.wrapper.getStyle(this.layout).toInt(),
caseIn = [[margin, layout], [0, this.offset]],
caseOut = [[margin, layout], [-this.offset, 0]],
start;
switch (how){
case 'in': start = caseIn; break;
case 'out': start = caseOut; break;
case 'toggle': start = (layout == 0) ? caseIn : caseOut;
}
return this.parent(start[0], start[1]);
},
slideIn: function(mode){
return this.start('in', mode);
},
slideOut: function(mode){
return this.start('out', mode);
},
hide: function(mode){
this[mode || this.options.mode]();
this.open = false;
return this.set([-this.offset, 0]);
},
show: function(mode){
this[mode || this.options.mode]();
this.open = true;
return this.set([0, this.offset]);
},
toggle: function(mode){
return this.start('toggle', mode);
}
});
Element.Properties.slide = {
set: function(options){
this.get('slide').cancel().setOptions(options);
return this;
},
get: function(){
var slide = this.retrieve('slide');
if (!slide){
slide = new Fx.Slide(this, {link: 'cancel'});
this.store('slide', slide);
}
return slide;
}
};
Element.implement({
slide: function(how, mode){
how = how || 'toggle';
var slide = this.get('slide'), toggle;
switch (how){
case 'hide': slide.hide(mode); break;
case 'show': slide.show(mode); break;
case 'toggle':
var flag = this.retrieve('slide:flag', slide.open);
slide[flag ? 'slideOut' : 'slideIn'](mode);
this.store('slide:flag', !flag);
toggle = true;
break;
default: slide.start(how, mode);
}
if (!toggle) this.eliminate('slide:flag');
return this;
}
});
/*
---
script: Drag.js
name: Drag
description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
license: MIT-style license
authors:
- Valerio Proietti
- Tom Occhinno
- Jan Kassens
requires:
- Core/Events
- Core/Options
- Core/Element.Event
- Core/Element.Style
- Core/Element.Dimensions
- /MooTools.More
provides: [Drag]
...
*/
var Drag = new Class({
Implements: [Events, Options],
options: {/*
onBeforeStart: function(thisElement){},
onStart: function(thisElement, event){},
onSnap: function(thisElement){},
onDrag: function(thisElement, event){},
onCancel: function(thisElement){},
onComplete: function(thisElement, event){},*/
snap: 6,
unit: 'px',
grid: false,
style: true,
limit: false,
handle: false,
invert: false,
preventDefault: false,
stopPropagation: false,
modifiers: {x: 'left', y: 'top'}
},
initialize: function(){
var params = Array.link(arguments, {
'options': Type.isObject,
'element': function(obj){
return obj != null;
}
});
this.element = document.id(params.element);
this.document = this.element.getDocument();
this.setOptions(params.options || {});
var htype = typeOf(this.options.handle);
this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
this.mouse = {'now': {}, 'pos': {}};
this.value = {'start': {}, 'now': {}};
this.selection = (Browser.ie) ? 'selectstart' : 'mousedown';
if (Browser.ie && !Drag.ondragstartFixed){
document.ondragstart = Function.from(false);
Drag.ondragstartFixed = true;
}
this.bound = {
start: this.start.bind(this),
check: this.check.bind(this),
drag: this.drag.bind(this),
stop: this.stop.bind(this),
cancel: this.cancel.bind(this),
eventStop: Function.from(false)
};
this.attach();
},
attach: function(){
this.handles.addEvent('mousedown', this.bound.start);
return this;
},
detach: function(){
this.handles.removeEvent('mousedown', this.bound.start);
return this;
},
start: function(event){
var options = this.options;
if (event.rightClick) return;
if (options.preventDefault) event.preventDefault();
if (options.stopPropagation) event.stopPropagation();
this.mouse.start = event.page;
this.fireEvent('beforeStart', this.element);
var limit = options.limit;
this.limit = {x: [], y: []};
var styles = this.element.getStyles('left', 'right', 'top', 'bottom');
this._invert = {
x: options.modifiers.x == 'left' && styles.left == 'auto' && !isNaN(styles.right.toInt()) && (options.modifiers.x = 'right'),
y: options.modifiers.y == 'top' && styles.top == 'auto' && !isNaN(styles.bottom.toInt()) && (options.modifiers.y = 'bottom')
};
var z, coordinates;
for (z in options.modifiers){
if (!options.modifiers[z]) continue;
var style = this.element.getStyle(options.modifiers[z]);
// Some browsers (IE and Opera) don't always return pixels.
if (style && !style.match(/px$/)){
if (!coordinates) coordinates = this.element.getCoordinates(this.element.getOffsetParent());
style = coordinates[options.modifiers[z]];
}
if (options.style) this.value.now[z] = (style || 0).toInt();
else this.value.now[z] = this.element[options.modifiers[z]];
if (options.invert) this.value.now[z] *= -1;
if (this._invert[z]) this.value.now[z] *= -1;
this.mouse.pos[z] = event.page[z] - this.value.now[z];
if (limit && limit[z]){
var i = 2;
while (i--){
var limitZI = limit[z][i];
if (limitZI || limitZI === 0) this.limit[z][i] = (typeof limitZI == 'function') ? limitZI() : limitZI;
}
}
}
if (typeOf(this.options.grid) == 'number') this.options.grid = {
x: this.options.grid,
y: this.options.grid
};
var events = {
mousemove: this.bound.check,
mouseup: this.bound.cancel
};
events[this.selection] = this.bound.eventStop;
this.document.addEvents(events);
},
check: function(event){
if (this.options.preventDefault) event.preventDefault();
var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
if (distance > this.options.snap){
this.cancel();
this.document.addEvents({
mousemove: this.bound.drag,
mouseup: this.bound.stop
});
this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
}
},
drag: function(event){
var options = this.options;
if (options.preventDefault) event.preventDefault();
this.mouse.now = event.page;
for (var z in options.modifiers){
if (!options.modifiers[z]) continue;
this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
if (options.invert) this.value.now[z] *= -1;
if (this._invert[z]) this.value.now[z] *= -1;
if (options.limit && this.limit[z]){
if ((this.limit[z][1] || this.limit[z][1] === 0) && (this.value.now[z] > this.limit[z][1])){
this.value.now[z] = this.limit[z][1];
} else if ((this.limit[z][0] || this.limit[z][0] === 0) && (this.value.now[z] < this.limit[z][0])){
this.value.now[z] = this.limit[z][0];
}
}
if (options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % options.grid[z]);
if (options.style) this.element.setStyle(options.modifiers[z], this.value.now[z] + options.unit);
else this.element[options.modifiers[z]] = this.value.now[z];
}
this.fireEvent('drag', [this.element, event]);
},
cancel: function(event){
this.document.removeEvents({
mousemove: this.bound.check,
mouseup: this.bound.cancel
});
if (event){
this.document.removeEvent(this.selection, this.bound.eventStop);
this.fireEvent('cancel', this.element);
}
},
stop: function(event){
var events = {
mousemove: this.bound.drag,
mouseup: this.bound.stop
};
events[this.selection] = this.bound.eventStop;
this.document.removeEvents(events);
if (event) this.fireEvent('complete', [this.element, event]);
}
});
Element.implement({
makeResizable: function(options){
var drag = new Drag(this, Object.merge({
modifiers: {
x: 'width',
y: 'height'
}
}, options));
this.store('resizer', drag);
return drag.addEvent('drag', function(){
this.fireEvent('resize', drag);
}.bind(this));
}
});
/*
---
script: Drag.Move.js
name: Drag.Move
description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
license: MIT-style license
authors:
- Valerio Proietti
- Tom Occhinno
- Jan Kassens
- Aaron Newton
- Scott Kyle
requires:
- Core/Element.Dimensions
- /Drag
provides: [Drag.Move]
...
*/
Drag.Move = new Class({
Extends: Drag,
options: {/*
onEnter: function(thisElement, overed){},
onLeave: function(thisElement, overed){},
onDrop: function(thisElement, overed, event){},*/
droppables: [],
container: false,
precalculate: false,
includeMargins: true,
checkDroppables: true
},
initialize: function(element, options){
this.parent(element, options);
element = this.element;
this.droppables = $$(this.options.droppables);
this.container = document.id(this.options.container);
if (this.container && typeOf(this.container) != 'element')
this.container = document.id(this.container.getDocument().body);
if (this.options.style){
if (this.options.modifiers.x == "left" && this.options.modifiers.y == "top"){
var parentStyles,
parent = element.getOffsetParent();
var styles = element.getStyles('left', 'top');
if (parent && (styles.left == 'auto' || styles.top == 'auto')){
element.setPosition(element.getPosition(parent));
}
}
if (element.getStyle('position') == 'static') element.setStyle('position', 'absolute');
}
this.addEvent('start', this.checkDroppables, true);
this.overed = null;
},
start: function(event){
if (this.container) this.options.limit = this.calculateLimit();
if (this.options.precalculate){
this.positions = this.droppables.map(function(el){
return el.getCoordinates();
});
}
this.parent(event);
},
calculateLimit: function(){
var element = this.element,
container = this.container,
offsetParent = document.id(element.getOffsetParent()) || document.body,
containerCoordinates = container.getCoordinates(offsetParent),
elementMargin = {},
elementBorder = {},
containerMargin = {},
containerBorder = {},
offsetParentPadding = {};
['top', 'right', 'bottom', 'left'].each(function(pad){
elementMargin[pad] = element.getStyle('margin-' + pad).toInt();
elementBorder[pad] = element.getStyle('border-' + pad).toInt();
containerMargin[pad] = container.getStyle('margin-' + pad).toInt();
containerBorder[pad] = container.getStyle('border-' + pad).toInt();
offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
}, this);
var width = element.offsetWidth + elementMargin.left + elementMargin.right,
height = element.offsetHeight + elementMargin.top + elementMargin.bottom,
left = 0,
top = 0,
right = containerCoordinates.right - containerBorder.right - width,
bottom = containerCoordinates.bottom - containerBorder.bottom - height;
if (this.options.includeMargins){
left += elementMargin.left;
top += elementMargin.top;
} else {
right += elementMargin.right;
bottom += elementMargin.bottom;
}
if (element.getStyle('position') == 'relative'){
var coords = element.getCoordinates(offsetParent);
coords.left -= element.getStyle('left').toInt();
coords.top -= element.getStyle('top').toInt();
left -= coords.left;
top -= coords.top;
if (container.getStyle('position') != 'relative'){
left += containerBorder.left;
top += containerBorder.top;
}
right += elementMargin.left - coords.left;
bottom += elementMargin.top - coords.top;
if (container != offsetParent){
left += containerMargin.left + offsetParentPadding.left;
top += ((Browser.ie6 || Browser.ie7) ? 0 : containerMargin.top) + offsetParentPadding.top;
}
} else {
left -= elementMargin.left;
top -= elementMargin.top;
if (container != offsetParent){
left += containerCoordinates.left + containerBorder.left;
top += containerCoordinates.top + containerBorder.top;
}
}
return {
x: [left, right],
y: [top, bottom]
};
},
getDroppableCoordinates: function(element){
var position = element.getCoordinates();
if (element.getStyle('position') == 'fixed'){
var scroll = window.getScroll();
position.left += scroll.x;
position.right += scroll.x;
position.top += scroll.y;
position.bottom += scroll.y;
}
return position;
},
checkDroppables: function(){
var overed = this.droppables.filter(function(el, i){
el = this.positions ? this.positions[i] : this.getDroppableCoordinates(el);
var now = this.mouse.now;
return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
}, this).getLast();
if (this.overed != overed){
if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
if (overed) this.fireEvent('enter', [this.element, overed]);
this.overed = overed;
}
},
drag: function(event){
this.parent(event);
if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
},
stop: function(event){
this.checkDroppables();
this.fireEvent('drop', [this.element, this.overed, event]);
this.overed = null;
return this.parent(event);
}
});
Element.implement({
makeDraggable: function(options){
var drag = new Drag.Move(this, options);
this.store('dragger', drag);
return drag;
}
});
/*
---
script: Sortables.js
name: Sortables
description: Class for creating a drag and drop sorting interface for lists of items.
license: MIT-style license
authors:
- Tom Occhino
requires:
- Core/Fx.Morph
- /Drag.Move
provides: [Sortables]
...
*/
var Sortables = new Class({
Implements: [Events, Options],
options: {/*
onSort: function(element, clone){},
onStart: function(element, clone){},
onComplete: function(element){},*/
opacity: 1,
clone: false,
revert: false,
handle: false,
dragOptions: {}
},
initialize: function(lists, options){
this.setOptions(options);
this.elements = [];
this.lists = [];
this.idle = true;
this.addLists($$(document.id(lists) || lists));
if (!this.options.clone) this.options.revert = false;
if (this.options.revert) this.effect = new Fx.Morph(null, Object.merge({
duration: 250,
link: 'cancel'
}, this.options.revert));
},
attach: function(){
this.addLists(this.lists);
return this;
},
detach: function(){
this.lists = this.removeLists(this.lists);
return this;
},
addItems: function(){
Array.flatten(arguments).each(function(element){
this.elements.push(element);
var start = element.retrieve('sortables:start', function(event){
this.start.call(this, event, element);
}.bind(this));
(this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
}, this);
return this;
},
addLists: function(){
Array.flatten(arguments).each(function(list){
this.lists.include(list);
this.addItems(list.getChildren());
}, this);
return this;
},
removeItems: function(){
return $$(Array.flatten(arguments).map(function(element){
this.elements.erase(element);
var start = element.retrieve('sortables:start');
(this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
return element;
}, this));
},
removeLists: function(){
return $$(Array.flatten(arguments).map(function(list){
this.lists.erase(list);
this.removeItems(list.getChildren());
return list;
}, this));
},
getClone: function(event, element){
if (!this.options.clone) return new Element(element.tagName).inject(document.body);
if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
var clone = element.clone(true).setStyles({
margin: 0,
position: 'absolute',
visibility: 'hidden',
width: element.getStyle('width')
}).addEvent('mousedown', function(event){
element.fireEvent('mousedown', event);
});
//prevent the duplicated radio inputs from unchecking the real one
if (clone.get('html').test('radio')){
clone.getElements('input[type=radio]').each(function(input, i){
input.set('name', 'clone_' + i);
if (input.get('checked')) element.getElements('input[type=radio]')[i].set('checked', true);
});
}
return clone.inject(this.list).setPosition(element.getPosition(element.getOffsetParent()));
},
getDroppables: function(){
var droppables = this.list.getChildren().erase(this.clone).erase(this.element);
if (!this.options.constrain) droppables.append(this.lists).erase(this.list);
return droppables;
},
insert: function(dragging, element){
var where = 'inside';
if (this.lists.contains(element)){
this.list = element;
this.drag.droppables = this.getDroppables();
} else {
where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
}
this.element.inject(element, where);
this.fireEvent('sort', [this.element, this.clone]);
},
start: function(event, element){
if (
!this.idle ||
event.rightClick ||
['button', 'input', 'a'].contains(event.target.get('tag'))
) return;
this.idle = false;
this.element = element;
this.opacity = element.get('opacity');
this.list = element.getParent();
this.clone = this.getClone(event, element);
this.drag = new Drag.Move(this.clone, Object.merge({
droppables: this.getDroppables()
}, this.options.dragOptions)).addEvents({
onSnap: function(){
event.stop();
this.clone.setStyle('visibility', 'visible');
this.element.set('opacity', this.options.opacity || 0);
this.fireEvent('start', [this.element, this.clone]);
}.bind(this),
onEnter: this.insert.bind(this),
onCancel: this.end.bind(this),
onComplete: this.end.bind(this)
});
this.clone.inject(this.element, 'before');
this.drag.start(event);
},
end: function(){
this.drag.detach();
this.element.set('opacity', this.opacity);
if (this.effect){
var dim = this.element.getStyles('width', 'height'),
clone = this.clone,
pos = clone.computePosition(this.element.getPosition(this.clone.getOffsetParent()));
var destroy = function(){
this.removeEvent('cancel', destroy);
clone.destroy();
};
this.effect.element = clone;
this.effect.start({
top: pos.top,
left: pos.left,
width: dim.width,
height: dim.height,
opacity: 0.25
}).addEvent('cancel', destroy).chain(destroy);
} else {
this.clone.destroy();
}
this.reset();
},
reset: function(){
this.idle = true;
this.fireEvent('complete', this.element);
},
serialize: function(){
var params = Array.link(arguments, {
modifier: Type.isFunction,
index: function(obj){
return obj != null;
}
});
var serial = this.lists.map(function(list){
return list.getChildren().map(params.modifier || function(element){
return element.get('id');
}, this);
}, this);
var index = params.index;
if (this.lists.length == 1) index = 0;
return (index || index === 0) && index >= 0 && index < this.lists.length ? serial[index] : serial;
}
});
/*
---
+47 -31
View File
@@ -11,6 +11,12 @@ Page.Settings = new Class({
},
'providers': {
'label': 'Providers'
},
'downloaders': {
'label': 'Downloaders'
},
'notifications': {
'label': 'Notifications'
}
},
@@ -27,13 +33,13 @@ Page.Settings = new Class({
openTab: function(action){
var self = this;
action = action || self.action
var action = action || self.action;
if(self.current)
self.toggleTab(self.current, true);
self.toggleTab(action)
self.current = action;
var tab = self.toggleTab(action)
self.current = tab == self.tabs.general ? 'general' : action;
},
@@ -47,6 +53,7 @@ Page.Settings = new Class({
t.tab[a](c);
t.content[a](c);
return t
},
getData: function(onComplete){
@@ -121,7 +128,7 @@ Page.Settings = new Class({
// Add options to group
group.options.sortBy('order').each(function(option){
var class_name = (option.type || 'input').capitalize();
var class_name = (option.type || 'string').capitalize();
var input = new Option[class_name](self, section_name, option.name, option);
input.inject(group_el);
});
@@ -145,13 +152,13 @@ Page.Settings = new Class({
var tab_el = new Element('li').adopt(
new Element('a', {
'href': '/'+self.name+'/'+tab_name+'/',
'text': tab.label.capitalize()
'text': (tab.label || tab.name).capitalize()
})
).inject(self.tabs_container);
if(!self.tabs[tab_name])
self.tabs[tab_name] = {
'label': tab.label
'label': tab.label || tab.name
}
self.tabs[tab_name] = Object.merge(self.tabs[tab_name], {
@@ -171,7 +178,7 @@ Page.Settings = new Class({
'class': group.advanced ? 'inlineLabels advanced' : 'inlineLabels'
}).adopt(
new Element('h2', {
'text': group.label
'text': group.label || group.name.capitalize()
}).adopt(
new Element('span.hint', {
'text': group.description
@@ -222,6 +229,13 @@ var OptionBase = new Class({
},
create: function(){},
createLabel: function(){
var self = this;
return new Element('label', {
'text': self.options.label || self.options.name.capitalize()
})
},
setAdvanced: function(){
this.el.addClass(this.options.advanced ? 'advanced': '')
@@ -319,9 +333,7 @@ Option.String = new Class({
var self = this
self.el.adopt(
new Element('label', {
'text': self.options.label
}),
self.createLabel(),
self.input = new Element('input', {
'type': 'text',
'name': self.postName(),
@@ -337,18 +349,17 @@ Option.Dropdown = new Class({
create: function(){
var self = this
new Element('label', {
'text': self.options.label
}).adopt(
self.el.adopt(
self.createLabel(),
self.input = new Element('select', {
'name': self.postName()
})
).inject(self.el)
)
Object.each(self.options.values, function(label, value){
Object.each(self.options.values, function(value){
new Element('option', {
'text': label,
'value': value
'text': value[0],
'value': value[1]
}).inject(self.input)
})
@@ -366,24 +377,31 @@ Option.Checkbox = new Class({
var randomId = 'option-'+Math.floor(Math.random()*1000000)
new Element('label', {
'text': self.options.label,
'for': randomId
}).inject(self.el);
self.input = new Element('input', {
'type': 'checkbox',
'value': self.getSettingValue(),
'checked': self.getSettingValue() !== undefined,
'id': randomId
}).inject(self.el);
self.el.adopt(
self.createLabel().set('for', randomId),
self.input = new Element('input', {
'type': 'checkbox',
'value': self.getSettingValue(),
'checked': self.getSettingValue() !== undefined,
'id': randomId
})
)
}
});
Option.Password = new Class({
Extends: Option.String,
type: 'password'
});
Option.Bool = new Class({
Extends: Option.Checkbox
});
Option.Enabler = new Class({
Extends: Option.Bool
});
Option.Int = new Class({
Extends: Option.String
});
@@ -401,9 +419,7 @@ Option.Directory = new Class({
self.el.adopt(
new Element('label', {
'text': self.options.label
}),
self.createLabel(),
self.input = new Element('span', {
'text': self.getSettingValue(),
'events': {
+277 -4
View File
@@ -18,19 +18,28 @@ Page.Wanted = new Class({
if(!self.movie_container)
self.movie_container = new Element('div.movies').inject(self.el);
self.movie_container.empty();
Object.each(self.movies, function(info){
var m = new Movie(self, {}, info);
$(m).inject(self.movie_container);
});
self.movie_container.addEvents({
'mouseenter:relay(.movie)': function(e, el){
el.addClass('hover')
},
'mouseleave:relay(.movie)': function(e, el){
el.removeClass('hover')
}
})
},
get: function(status, onComplete){
var self = this
if(self.movies.length == 0)
Api.request('movie', {
Api.request('movie.list', {
'data': {},
'onComplete': function(json){
self.store(json.movies);
@@ -58,16 +67,280 @@ var Movie = new Class({
self.data = data;
self.profile = Quality.getProfile(data.profile_id);
self.parent(self, options);
},
create: function(){
var self = this;
self.el = new Element('div.movie', {
'text': self.data.name
self.el = new Element('div.movie').adopt(
self.data_container = new Element('div').adopt(
self.thumbnail = File.Select.single('poster', self.data.library.files),
self.title = new Element('div.title', {
'text': self.getTitle()
}),
self.description = new Element('div.description', {
'text': self.data.library.plot
}),
self.rating = new Element('div.rating', {
'text': self.data.library.rating || 10
}),
self.year = new Element('div.year', {
'text': self.data.library.year || 'Unknown'
}),
self.quality = new Element('div.quality', {
'text': self.profile.get('label')
}),
self.actions = new Element('div.actions').adopt(
self.action_imdb = new Movie.Action.IMDB(self),
self.action_edit = new Movie.Action.Edit(self),
self.action_refresh = new Movie.Action.Refresh(self),
self.action_delete = new Movie.Action.Delete(self)
)
)
);
},
getTitle: function(){
var self = this;
var titles = self.data.library.titles;
var title = titles.filter(function(title){
return title['default']
}).pop()
if(title)
return title.title
else if(titles.length > 0)
return titles[0].title
return 'Unknown movie'
},
get: function(attr){
return this.data[attr] || this.data.library[attr]
}
});
var MovieAction = new Class({
class_name: 'action',
initialize: function(movie){
var self = this;
self.movie = movie;
self.create();
self.el.addClass(self.class_name)
},
create: function(){},
disable: function(){
this.el.addClass('disable')
},
enable: function(){
this.el.removeClass('disable')
},
toElement: function(){
return this.el
}
})
Movie.Action = {}
Movie.Action.Edit = new Class({
Extends: MovieAction,
create: function(){
var self = this;
self.el = new Element('a.edit', {
'text': 'edit',
'title': 'Refresh the movie info and do a forced search',
'events': {
'click': self.editMovie.bind(self)
}
});
},
editMovie: function(e){
var self = this;
(e).stop();
self.optionContainer = new Element('div.options').adopt(
$(self.movie.thumbnail).clone(),
self.title_select = new Element('select', {
'name': 'title'
}),
self.profile_select = new Element('select', {
'name': 'profile'
}),
new Element('a.button.edit', {
'text': 'Save',
'events': {
'click': self.save.bind(self)
}
})
).inject(self.movie, 'top');
},
save: function(){
var self = this;
Api.request('movie.edit', {
'data': {
'default_title': self.title_select.get('value'),
'profile_id': self.profile_select.get('value')
},
'useSpinner': true,
'spinnerTarget': self.movie
})
}
})
Movie.Action.IMDB = new Class({
Extends: MovieAction,
id: null,
create: function(){
var self = this;
self.id = self.movie.get('identifier');
self.el = new Element('a.imdb', {
'text': 'imdb',
'title': 'Go to the IMDB page of ' + self.movie.getTitle(),
'events': {
'click': self.gotoIMDB.bind(self)
}
});
if(!self.id) self.disable();
},
gotoIMDB: function(e){
var self = this;
(e).stop();
window.open('http://www.imdb.com/title/'+self.id+'/');
}
})
Movie.Action.Refresh = new Class({
Extends: MovieAction,
create: function(){
var self = this;
self.el = new Element('a.refresh', {
'text': 'refresh',
'title': 'Refresh the movie info and do a forced search',
'events': {
'click': self.doSearch.bind(self)
}
});
},
doSearch: function(e){
var self = this;
(e).stop();
Api.request('movie.refresh', {
'data': {
'id': self.movie.get('id')
}
})
}
})
Movie.Action.Delete = new Class({
Extends: MovieAction,
Implements: [Chain],
create: function(){
var self = this;
self.el = new Element('a.delete', {
'text': 'delete',
'title': 'Remove the movie from your wanted list',
'events': {
'click': self.showConfirm.bind(self)
}
});
},
showConfirm: function(e){
var self = this;
(e).stop();
self.mask = $(self.movie).mask({
'destroyOnHide': true
});
$(self.mask).adopt(
new Element('a.button.delete', {
'text': 'Delete movie',
'events': {
'click': self.del.bind(self)
}
}),
new Element('span', {
'text': 'or'
}),
new Element('a.button.cancel', {
'text': 'Cancel',
'events': {
'click': self.mask.hide.bind(self.mask)
}
})
);
},
del: function(e){
(e).stop()
var self = this;
var movie = $(self.movie);
self.chain(
function(){
$(self.mask).empty().addClass('loading');
self.callChain();
},
function(){
Api.request('movie.delete', {
'data': {
'id': self.movie.get('id')
},
'onComplete': function(){
p(movie, $(self.movie))
movie.slide('in');
}
})
}
);
self.callChain();
}
})
+95 -39
View File
@@ -6,13 +6,19 @@ var QualityBase = new Class({
setup: function(data){
var self = this;
self.profiles = data.profiles;
self.qualities = data.qualities;
self.profiles = {}
Object.each(data.profiles, self.createProfilesClass.bind(self));
App.addEvent('load', self.addSettings.bind(self))
},
getProfile: function(id){
return this.profiles[id]
},
addSettings: function(){
var self = this;
@@ -47,22 +53,34 @@ var QualityBase = new Class({
new Element('a.add_new', {
'text': 'Create a new quality profile',
'events': {
'click': self.createNewProfile.bind(self)
'click': function(){
var profile = self.createProfilesClass();
$(profile).inject(self.profile_container, 'top')
}
}
}),
self.profile_container = new Element('div.container')
)
Object.each(self.profiles, self.createNewProfile.bind(self))
Object.each(self.profiles, function(profile){
if(!profile.isCore())
$(profile).inject(self.profile_container, 'top')
})
},
createNewProfile: function(data, nr){
createProfilesClass: function(data){
var self = this;
self.profiles[nr] = new Profile(data);
$(self.profiles[nr]).inject(self.profile_container)
if(data){
return self.profiles[data.id] = new Profile(data);
}
else {
var data = {
'id': randomString()
}
return self.profiles[data.id] = new Profile(data);
}
},
/**
@@ -95,7 +113,7 @@ var QualityBase = new Class({
window.Quality = new QualityBase();
var Profile = new Class({
data: {},
types: [],
@@ -119,12 +137,9 @@ var Profile = new Class({
var data = self.data;
self.el = new Element('div', {
'class': 'profile'
}).adopt(
new Element('h4', {'text': data.label}),
new Element('span.delete', {
'html': 'del',
self.el = new Element('div.profile').adopt(
self.header = new Element('h4', {'text': data.label}),
new Element('span.delete.icon', {
'events': {
'click': self.del.bind(self)
}
@@ -135,25 +150,29 @@ var Profile = new Class({
new Element('label', {'text':'Name'}),
new Element('input.label.textInput.large', {
'type':'text',
'value': data.label
'value': data.label,
'events': {
'keyup': function(){
self.header.set('text', this.get('value'))
}
}
})
),
new Element('div.ctrlHolder').adopt(
new Element('label', {'text':'Wait'}),
new Element('input.wait_for.textInput.xsmall', {
'type':'text',
'value': data.wait_for
'value': data.types && data.types.length > 0 ? data.types[0].wait_for : 0
}),
new Element('span', {'text':' day(s) for better quality.'})
),
new Element('div.ctrlHolder').adopt(
new Element('label', {'text': 'Qualities'}),
self.type_container = new Element('div.types').adopt(
new Element('div.head').adopt(
new Element('span.quality_type', {'text': 'Search for'}),
new Element('span.finish', {'html': '<acronym title="Won\'t download anything else if it has found this quality.">Finish</acronym>'})
)
new Element('div.head').adopt(
new Element('span.quality_type', {'text': 'Search for'}),
new Element('span.finish', {'html': '<acronym title="Won\'t download anything else if it has found this quality.">Finish</acronym>'})
),
self.type_container = new Element('ol.types'),
new Element('a.addType', {
'text': 'Add another quality to search for.',
'href': '#',
@@ -164,6 +183,8 @@ var Profile = new Class({
)
);
self.makeSortable()
if(data.types)
Object.each(data.types, self.addType.bind(self))
},
@@ -174,11 +195,19 @@ var Profile = new Class({
if(self.save_timer) clearTimeout(self.save_timer);
self.save_timer = (function(){
var data = self.getData();
if(data.types.length < 2) return;
Api.request('profile.save', {
'data': self.getData(),
'useSpinner': true,
'spinnerOptions': {
'target': self.el
},
'onComplete': function(json){
if(json.success){
self.data = json.profile
}
}
});
}).delay(delay, self)
@@ -194,12 +223,15 @@ var Profile = new Class({
'wait_for' : self.el.getElement('.wait_for').get('value'),
'types': []
}
Object.each(self.types, function(type){
if(!type.deleted)
data.types.include(type.getData());
Array.each(self.type_container.getElements('.type'), function(type){
if(!type.hasClass('deleted'))
data.types.include({
'quality_id': type.getElement('select').get('value'),
'finish': +type.getElement('input[type=checkbox]').checked
});
})
return data
},
@@ -208,6 +240,7 @@ var Profile = new Class({
var t = new Profile.Type(data);
$(t).inject(self.type_container);
self.sortable.addItems($(t));
self.types.include(t);
@@ -216,6 +249,8 @@ var Profile = new Class({
del: function(){
var self = this;
if(!confirm('Are you sure you want to delete this profile?')) return
Api.request('profile.delete', {
'data': {
'id': self.data.id
@@ -224,12 +259,35 @@ var Profile = new Class({
'spinnerOptions': {
'target': self.el
},
'onComplete': function(){
self.el.destroy();
'onComplete': function(json){
if(json.success)
self.el.destroy();
else
alert(json.message)
}
});
},
makeSortable: function(){
var self = this;
self.sortable = new Sortables(self.type_container, {
'revert': true,
//'clone': true,
'handle': '.handle',
'opacity': 0.5,
'onComplete': self.save.bind(self, 300)
});
},
get: function(attr){
return this.data[attr]
},
isCore: function(){
return this.data.core
},
toElement: function(){
return this.el
}
@@ -252,7 +310,7 @@ Profile.Type = Class({
var self = this;
var data = self.data;
self.el = new Element('div.type').adopt(
self.el = new Element('li.type').adopt(
new Element('span.quality_type').adopt(
self.fillQualities()
),
@@ -263,15 +321,12 @@ Profile.Type = Class({
'checked': data.finish
})
),
new Element('span.delete', {
'html': 'del',
new Element('span.delete.icon', {
'events': {
'click': self.del.bind(self)
}
}),
new Element('span', {
'class':'handle'
})
new Element('span.handle')
)
},
@@ -284,21 +339,21 @@ Profile.Type = Class({
Object.each(Quality.qualities, function(q){
new Element('option', {
'text': q.label,
'value': q.identifier
'value': q.id
}).inject(self.qualities)
});
self.qualities.set('value', self.data.quality);
self.qualities.set('value', self.data.quality_id);
return self.qualities;
},
getData: function(){
var self = this;
return {
'quality': self.qualities.get('value'),
'quality_id': self.qualities.get('value'),
'finish': +self.finish.checked
}
},
@@ -306,6 +361,7 @@ Profile.Type = Class({
del: function(){
var self = this;
self.el.addClass('deleted');
self.el.hide();
self.deleted = true;
},
+11
View File
@@ -0,0 +1,11 @@
var StatusBase = new Class({
setup: function(statuses){
var self = this;
self.statuses = statuses;
}
});
window.Status = new StatusBase();