Searcher, settings styling

This commit is contained in:
Ruud
2011-03-06 03:01:31 +01:00
parent 44395ff195
commit 322762d702
26 changed files with 8058 additions and 663 deletions
-4
View File
@@ -17,10 +17,6 @@ var BlockBase = new Class({
this.el = new Element('div.block');
},
api: function(){
return this.getParent().getApi()
},
getParent: function(){
return this.page
},
+134 -31
View File
@@ -8,17 +8,57 @@ Block.Search = new Class({
var self = this;
self.el = new Element('div.search_form').adopt(
self.input = new Element('input', {
'events': {
'keyup': self.keyup.bind(self)
new Element('div.input').adopt(
self.input = new Element('input', {
'events': {
'keyup': self.keyup.bind(self),
'focus': self.hideResults.bind(self, false)
}
}),
new Element('a', {
'events': {
'click': self.clear.bind(self)
}
})
),
self.result_container = new Element('div.results_container', {
'tween': {
'duration': 200
}
}),
self.results = new Element('div.results')
}).adopt(
new Element('div.pointer'),
self.results = new Element('div.results')
).fade('hide')
);
// Debug
self.input.set('value', 'iron man');
self.autocomplete(0)
self.spinner = new Spinner(self.result_container);
self.OuterClickStack = new EventStack.OuterClick();
},
clear: function(e){
var self = this;
(e).stop();
self.input.set('value', '');
self.input.focus()
self.movies = []
self.results.empty()
},
hideResults: function(bool){
var self = this;
if(self.hidden == bool) return;
self.result_container.fade(bool ? 0 : 1)
if(!bool && self.OuterClickStack.stack.length == 0)
self.OuterClickStack.push(self.hideResults.bind(self, true), self.el);
self.hidden = bool;
},
keyup: function(e){
@@ -36,6 +76,13 @@ Block.Search = new Class({
autocomplete: function(delay){
var self = this;
if(!self.q()){
self.hideResults(true)
return
}
self.spinner.show()
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
self.autocomplete_timer = self.list.delay((delay || 300), self)
},
@@ -48,13 +95,16 @@ Block.Search = new Class({
var q = self.q();
var cache = self.cache[q];
if(!cache)
self.api_request = self.api().request('movie.add.search', {
self.hideResults(false)
if(!cache){
self.api_request = Api.request('movie.add.search', {
'data': {
'q': q
},
'onComplete': self.fill.bind(self, q)
})
}
else
self.fill(q, cache)
@@ -65,20 +115,28 @@ Block.Search = new Class({
fill: function(q, json){
var self = this;
self.spinner.hide();
self.cache[q] = json
self.movies = []
self.results.empty()
Object.each(json.movies, function(movie){
var m = new Block.Search.Item(movie);
$(m).inject(self.results)
if(!movie.imdb || (movie.imdb && !self.results.getElement('#'+movie.imdb))){
var m = new Block.Search.Item(movie);
$(m).inject(self.results)
}
self.movies.include(m)
});
},
loading: function(bool){
this.el[bool ? 'addClass' : 'removeClass']('loading')
},
q: function(){
return this.input.get('value').trim();
}
@@ -93,31 +151,58 @@ Block.Search.Item = new Class({
self.info = info;
self.create();
self.OuterClickStack = new EventStack.OuterClick();
},
create: function(){
var self = this;
self.el = new Element('div.movie').adopt(
self.name = new Element('h2', {
'text': self.info.name
}),
self.tagline = new Element('span', {
'text': self.info.tagline
}),
self.year = self.info.year ? new Element('span', {
'text': self.info.year
}) : null,
self.director = self.info.director ? new Element('span', {
'text': 'Director:' + self.info.director
}) : null,
self.starring = self.info.actors ? new Element('span', {
'text': 'Starring:'
}) : null
var info = self.info
self.el = new Element('div.movie', {
'id': info.imdb
}).adopt(
new Element('div.add').adopt(
new Element('span', {
'text': 'test'
})
),
self.data_container = new Element('div.data', {
'tween': {
duration: 400,
transition: 'quint:in:out'
},
'events': {
'click': self.showOptions.bind(self)
}
}).adopt(
self.thumbnail = info.poster ? new Element('img.thumbnail', {
'src': info.poster
}) : null,
new Element('div.info').adopt(
self.name = new Element('h2', {
'text': info.name
}).adopt(
self.year = info.year ? new Element('span', {
'text': info.year
}) : null
),
self.tagline = new Element('span', {
'text': info.tagline
}),
self.director = self.info.director ? new Element('span', {
'text': 'Director:' + info.director
}) : null,
self.starring = info.actors ? new Element('span', {
'text': 'Starring:'
}) : null
)
)
)
if(self.info.actors){
Object.each(self.info.actors, function(actor){
if(info.actors){
Object.each(info.actors, function(actor){
new Element('span', {
'text': actor.name
}).inject(self.starring)
@@ -125,6 +210,24 @@ Block.Search.Item = new Class({
}
},
showOptions: function(){
var self = this;
if(!self.width)
self.width = self.data_container.getCoordinates().width
self.data_container.tween('margin-left', 0, self.width);
self.OuterClickStack.push(self.closeOptions.bind(self), self.el);
},
closeOptions: function(){
var self = this;
self.data_container.tween('margin-left', self.width, 0);
},
toElement: function(){
return this.el
}
+51 -16
View File
@@ -18,7 +18,6 @@ var CouchPotato = new Class({
self.c = $(document.body)
self.route = new Route(self.defaults);
self.api = new Api(self.options.api)
self.createLayout();
self.createPages();
@@ -43,8 +42,10 @@ var CouchPotato = new Class({
self.c.adopt(
self.header = new Element('div.header').adopt(
self.block.navigation = new Block.Navigation(self, {}),
self.block.search = new Block.Search(self, {})
new Element('div').adopt(
self.block.navigation = new Block.Navigation(self, {}),
self.block.search = new Block.Search(self, {})
)
),
self.content = new Element('div.content'),
self.block.footer = new Block.Footer(self, {})
@@ -71,36 +72,29 @@ var CouchPotato = new Class({
var action = self.route.getAction();
var params = self.route.getParams();
if(self.current_page)
self.current_page.hide()
var page = self.pages[page_name];
page.open(action, params);
page.show();
if(self.current_page)
self.current_page.hide()
self.current_page = page;
},
getBlock: function(block_name){
return this.block[block_name]
},
getApi: function(){
return this.api
}
});
var Api = new Class({
var ApiClass = new Class({
url: '',
initialize: function(options){
setup: function(options){
var self = this
self.options = options;
},
request: function(type, options){
@@ -123,6 +117,7 @@ var Api = new Class({
}
});
window.Api = new ApiClass()
var Route = new Class({
@@ -183,4 +178,44 @@ var Route = new Class({
var p = function(){
if(typeof(console) !== 'undefined' && console != null)
console.log(arguments)
}
};
(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++) {
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.sort(comparer);
});
})();
+71
View File
@@ -0,0 +1,71 @@
/*
---
name: EventStack
description: Helps you Escape.
authors: Christoph Pojer (@cpojer)
license: MIT-style license.
requires: [Core/Class.Extras, Core/Element.Event, Class-Extras/Class.Binds]
provides: EventStack
...
*/
(function(){
this.EventStack = new Class({
Implements: [Options, Class.Binds],
options: {
event: 'keyup',
condition: function(event){
return (event.key == 'esc');
}
},
initialize: function(options){
this.setOptions(options);
this.stack = [];
this.data = [];
document.addEvent(this.options.event, this.bound('condition'));
},
condition: function(event){
if (this.options.condition.call(this, event, this.data.getLast()))
this.pop(event);
},
erase: function(fn){
this.data.erase(this.data[this.stack.indexOf(fn)]);
this.stack.erase(fn);
return this;
},
push: function(fn, data){
this.erase(fn);
this.data.push(data || null);
this.stack.push(fn);
return this;
},
pop: function(event){
var fn = this.stack.pop(),
data = this.data.pop();
if (fn) fn.call(this, event, data);
return this;
}
});
}).call(this);
@@ -0,0 +1,30 @@
/*
---
name: EventStack.OuterClick
description: Helps you escape from clicks outside of a certain area.
authors: Christoph Pojer (@cpojer)
license: MIT-style license.
requires: [EventStack]
provides: EventStack.OuterClick
...
*/
EventStack.OuterClick = new Class({
Extends: EventStack,
options: {
event: 'click',
condition: function(event, element){
return element && !element.contains(event.target);
}
}
});
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,140 @@
/*
---
name: Fx.Tween.CSS3.Replacement
script: Fx.Tween.CSS3.Replacement.js
license: MIT-style license.
description: Same behavior like Fx.Tween but tries to use native CSS3 transition if possible (Overwrites Fx.Tween).
copyright: Copyright (c) 2010, Dipl.-Ing. (FH) André Fiedler <kontakt at visualdrugs dot net>, based on code by eskimoblood (mootools users group)
authors: [André Fiedler, eskimoblood]
requires: [Core/Class.Extras, Core/Element.Event, Core/Element.Style, Core/Fx.Tween]
provides: [Fx.Tween]
...
*/
Element.NativeEvents.transitionend = 2;
Element.NativeEvents.webkitTransitionEnd = 2;
Element.NativeEvents.oTransitionEnd = 2;
Element.Events.transitionend = {
base: Browser.safari || Browser.chrome ? 'webkitTransitionEnd' : (Browser.opera ? 'oTransitionEnd' : 'transitionend')
};
Event.implement({
getPropertyName: function(){
return this.event.propertyName;
},
getElapsedTime: function(nativeTime){
return nativeTime ? this.event.elapsedTime : (this.event.elapsedTime * 1000).toInt();
}
});
Element.implement({
supportStyle: function(style){
var value = this.style[style];
return !!(value || value == '');
},
supportVendorStyle: function(style){
var prefixedStyle = null;
return this.supportStyle(style) ? style : ['webkit', 'Moz', 'o'].some(function(prefix){
prefixedStyle = prefix + style.capitalize();
return this.supportStyle(prefixedStyle);
}, this) ? prefixedStyle : null;
}
});
Fx.TweenCSS2 = Fx.Tween;
Fx.Tween = new Class({
Extends: Fx.TweenCSS2,
transitionTimings: {
'linear' : '0,0,1,1',
'expo:in' : '0.71,0.01,0.83,0',
'expo:out' : '0.14,1,0.32,0.99',
'expo:in:out' : '0.85,0,0.15,1',
'circ:in' : '0.34,0,0.96,0.23',
'circ:out' : '0,0.5,0.37,0.98',
'circ:in:out' : '0.88,0.1,0.12,0.9',
'sine:in' : '0.22,0.04,0.36,0',
'sine:out' : '0.04,0,0.5,1',
'sine:in:out' : '0.37,0.01,0.63,1',
'quad:in' : '0.14,0.01,0.49,0',
'quad:out' : '0.01,0,0.43,1',
'quad:in:out' : '0.47,0.04,0.53,0.96',
'cubic:in' : '0.35,0,0.65,0',
'cubic:out' : '0.09,0.25,0.24,1',
'cubic:in:out' : '0.66,0,0.34,1',
'quart:in' : '0.69,0,0.76,0.17',
'quart:out' : '0.26,0.96,0.44,1',
'quart:in:out' : '0.76,0,0.24,1',
'quint:in' : '0.64,0,0.78,0',
'quint:out' : '0.22,1,0.35,1',
'quint:in:out' : '0.9,0,0.1,1'
},
initialize: function(element, options){
options.transition = options.transition || 'sine:in:out';
this.parent(element, options);
if (typeof this.options.transition != 'string') alert('Only short notated transitions (like \'sine:in\') are supported by Fx.Tween.CSS3');
this.options.transition = this.options.transition.toLowerCase();
this.transition = this.element.supportVendorStyle('transition');
this.css3Supported = !!this.transition && !!this.transitionTimings[this.options.transition];
},
check: function(){
if (!this.boundComplete) return true;
return this.parent();
},
start: function(property, from, to){
if (this.css3Supported){
if (!this.check(property, from, to)) return this;
var args = Array.flatten(arguments);
this.property = this.options.property || args.shift();
var parsed = this.prepare(this.element, this.property, args);
this.from = parsed.from;
this.to = parsed.to;
this.boundComplete = function(event){
if (event.getPropertyName() == this.property /* && event.getElapsedTime() == this.options.duration */ ){
this.element.removeEvent('transitionend', this.boundComplete);
this.boundComplete = null;
this.fireEvent('complete', this.subject);
}
}.bind(this);
this.element.addEvent('transitionend', this.boundComplete);
var trans = function(){
this.element.setStyle(this.transition, this.property + ' ' + this.options.duration + 'ms cubic-bezier(' + this.transitionTimings[this.options.transition] + ')');
this.element.setStyle(this.property, this.to[0].value + + this.options.unit);
}.bind(this);
if (args[1]){
this.element.setStyle(this.transition, 'none');
this.element.setStyle(this.property, this.from[0].value + + this.options.unit);
trans.delay(0.1);
} else
trans();
this.fireEvent('start', this.subject);
return this;
}
return this.parent(property, from, to);
},
cancel: function(){
if (this.css3Supported){
this.element.setStyle(this.transition, 'none');
this.element.removeEvent('transitionend', this.boundComplete);
this.boundComplete = null;
}
this.parent();
return this;
}
});
-4
View File
@@ -57,10 +57,6 @@ var PageBase = new Class({
getParent: function(){
return this.app
},
api: function(){
return this.getParent().getApi()
},
show: function(){
this.el.addClass('active');
+93 -38
View File
@@ -5,11 +5,19 @@ Page.Settings = new Class({
name: 'settings',
title: 'Change settings.',
groups: {},
tabs: {
'general': {
'label': 'General'
},
'providers': {
'label': 'Providers'
}
},
open: function(action, params){
var self = this
//p('open config', action, params)
self.action = action;
self.params = params;
if(!self.data)
self.getData(self.create.bind(self))
@@ -19,6 +27,7 @@ Page.Settings = new Class({
openTab: function(action){
var self = this;
action = action || self.action
if(self.current)
self.toggleTab(self.current, true);
@@ -27,16 +36,16 @@ Page.Settings = new Class({
self.current = action;
},
toggleTab: function(tab, hide){
toggleTab: function(tab_name, hide){
var self = this;
var a = hide ? 'removeClass' : 'addClass';
var c = 'active';
var g = self.groups[tab] || self.groups.general;
g.tab[a](c);
g.group[a](c);
var t = self.tabs[tab_name] || self.tabs[self.action] || self.tabs.general;
t.tab[a](c);
t.content[a](c);
},
@@ -44,7 +53,7 @@ Page.Settings = new Class({
var self = this;
if(onComplete)
self.api().request('settings', {
Api.request('settings', {
'useSpinner': true,
'spinnerOptions': {
'target': self.el
@@ -68,38 +77,79 @@ Page.Settings = new Class({
}
},
showAdvanced: function(){
var self = this;
var c = self.advanced_toggle.checked ? 'addClass' : 'removeClass';
self.el[c]('show_advanced')
},
create: function(json){
var self = this
self.el.adopt(
self.tabs = new Element('ul.tabs'),
self.containers = new Element('form.uniForm.containers')
self.tabs_container = new Element('ul.tabs'),
self.containers = new Element('form.uniForm.containers').adopt(
new Element('label.advanced_toggle').adopt(
new Element('span', {
'text': 'Show advanced settings'
}),
self.advanced_toggle = new Element('input[type=checkbox]', {
'events': {
'change': self.showAdvanced.bind(self)
}
})
)
)
);
// Create tabs
Object.each(self.tabs, function(tab, tab_name){
if(!self.tabs[tab_name].tab){
var tab_el = new Element('li').adopt(
new Element('a', {
'href': '/'+self.name+'/'+tab_name+'/',
'text': tab.label.capitalize()
})
).inject(self.tabs_container);
self.tabs[tab_name] = Object.merge(self.tabs[tab_name], {
'tab': tab_el,
'content': new Element('div.tab_content').inject(self.containers),
'groups': {}
})
}
});
// Add content to tabs
Object.each(json.options, function(section, section_name){
// Create tab
var tab = new Element('li').adopt(
new Element('a', {
'href': '/'+self.name+'/'+section.tab+'/',
'text': section.tab.capitalize()
})
).inject(self.tabs);
var group = new Element('div.group').inject(self.containers);
// Add groups to content
section.groups.sortBy('order').each(function(group){
self.groups[section.tab] = {
'tab': tab,
'group': group
}
// Create the group
var group_el = new Element('fieldset', {
'class': group.advanced ? 'inlineLabels advanced' : 'inlineLabels'
}).adopt(
new Element('h2', {
'text': group.label
}).adopt(
new Element('span.hint', {
'text': group.description
})
)
).inject(self.tabs[group.tab].content);
self.tabs[group.tab].groups[group.name] = group_el
// Add options to group
group.options.sortBy('order').each(function(option){
var class_name = (option.type || 'input').capitalize();
var input = new Option[class_name](self, section_name, option.name, option);
input.inject(group_el);
});
// Add section
var fieldset = new Element('fieldset.inlineLabels').inject(group)
Object.each(section.options, function(option, option_name){
var class_name = (option.type || 'input').capitalize();
var input = new Option[class_name](self, section_name, option_name, option);
input.inject(fieldset);
});
});
self.openTab();
@@ -251,6 +301,7 @@ Option.String = new Class({
'text': self.options.label
}),
self.input = new Element('input', {
'type': 'text',
'name': self.postName(),
'value': self.getSettingValue()
})
@@ -291,15 +342,19 @@ Option.Checkbox = new Class({
create: function(){
var self = this;
var randomId = 'option-'+Math.floor(Math.random()*1000000)
new Element('label', {
'text': self.options.label
}).adopt(
self.input = new Element('input', {
'type': 'checkbox',
'value': self.getSettingValue(),
'checked': self.getSettingValue() !== undefined
})
).inject(self.el);
'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);
}
});
+1 -1
View File
@@ -30,7 +30,7 @@ Page.Wanted = new Class({
var self = this
if(self.movies.length == 0)
self.api().request('movie', {
Api.request('movie', {
'data': {},
'onComplete': function(json){
self.store(json.movies);