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

View File

@@ -2,11 +2,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
'''Wrapper for the command line interface.''' '''Wrapper for the command line interface.'''
from os.path import dirname, isfile from os.path import dirname
import os import os
import subprocess
import sys import sys
import traceback
# Root path # Root path
base_path = dirname(os.path.abspath(__file__)) base_path = dirname(os.path.abspath(__file__))
@@ -17,37 +15,8 @@ sys.path.insert(0, os.path.join(base_path, 'libs'))
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
log = CPLog(__name__) log = CPLog(__name__)
try:
from couchpotato import cli
except ImportError, e:
log.info('Checking local dependencies...')
if isfile(__file__):
cwd = dirname(__file__)
log.info('Updating libraries...')
stdout, stderr = subprocess.Popen(['git', 'submodule', 'init'],
stderr = subprocess.PIPE,
stdout = subprocess.PIPE).communicate()
if stderr:
log.info('[WARNING] Git is complaining:')
log.info(stderr)
stdout, stderr = subprocess.Popen(['git', 'submodule', 'update'],
stderr = subprocess.PIPE,
stdout = subprocess.PIPE).communicate()
if stderr:
log.info('[WARNING] Git is complaining:')
log.info(stderr)
log.info('Passing execution to couchpotato...')
try:
from couchpotato import cli
except ImportError:
log.error("[ERROR]: Something's seriously wrong.")
log.error(traceback.print_exc())
sys.exit(1)
else:
# Running from Titanium
raise NotImplementedError("Don't know how to do that.")
from couchpotato import cli
if __name__ == '__main__': if __name__ == '__main__':
try: try:
cli.cmd_couchpotato(base_path, sys.argv[1:]) cli.cmd_couchpotato(base_path, sys.argv[1:])

View File

@@ -1,7 +1,6 @@
from argparse import ArgumentParser from argparse import ArgumentParser
from couchpotato import get_engine, web from couchpotato import web
from couchpotato.api import api from couchpotato.api import api
from couchpotato.core.settings.model import *
from libs.daemon import createDaemon from libs.daemon import createDaemon
from logging import handlers from logging import handlers
import logging import logging
@@ -110,9 +109,8 @@ def cmd_couchpotato(base_path, args):
upgrade(db, repo) upgrade(db, repo)
# Configure Database # Configure Database
from elixir import setup_all, create_all from couchpotato.core.settings.model import setup
setup_all() setup()
create_all(get_engine())
# Create app # Create app

View File

@@ -4,59 +4,81 @@ def start():
pass pass
config = [{ config = [{
'name': 'global', 'name': 'core',
'tab': 'general', 'groups': [
'options': { {
'debug': { 'tab': 'general',
'name': 'basics',
'label': 'Basics',
'description': 'Needs restart before changes take effect.',
'options': [
{
'name': 'username',
'default': '',
'type': 'string',
'label': 'Username',
},
{
'name': 'password',
'default': '',
'password': True,
'type': 'string',
'label': 'Password',
},
{
'name': 'host',
'advanced': True,
'default': '0.0.0.0',
'type': 'string',
'label': 'Host',
'description': 'Host that I should listen to. "0.0.0.0" listens to all ips.',
},
{
'name': 'port',
'default': 5000,
'type': 'int',
'label': 'Port',
'description': 'The port I should listen to.',
},
{
'name': 'launch_browser',
'default': True,
'type': 'bool',
'label': 'Launch Browser',
'description': 'Launch the browser when I start.',
},
],
},
{
'tab': 'general',
'name': 'advanced',
'label': 'Advanced',
'description': "For those who know what the're doing",
'advanced': True, 'advanced': True,
'default': False, 'options': [
'type': 'bool', {
'label': 'Debug', 'name': 'api_key',
'description': 'Enable debugging.', 'default': uuid4().hex,
'type': 'string',
'readonly': True,
'label': 'Api Key',
'description': "This is top-secret! Don't share this!",
},
{
'name': 'debug',
'default': False,
'type': 'bool',
'label': 'Debug',
'description': 'Enable debugging.',
},
{
'name': 'url_base',
'default': '',
'type': 'string',
'label': 'Url Base',
'description': 'When using mod_proxy use this to prepend the url with this.',
},
],
}, },
'host': { ],
'advanced': True,
'default': '0.0.0.0',
'type': 'string',
'label': 'Host',
'description': 'Host that I should listen to 0.0.0.0 listens to everything.',
},
'port': {
'default': 5000,
'type': 'int',
'label': 'Port',
'description': 'The port I should listen to.',
},
'username': {
'default': '',
'type': 'string',
'label': 'Username',
},
'password': {
'default': '',
'password': True,
'type': 'string',
'label': 'Password',
},
'launch_browser': {
'default': True,
'type': 'bool',
'label': 'Launch Browser',
'description': 'Launch the browser when I start.',
},
'url_base': {
'advanced': True,
'default': '',
'type': 'string',
'label': 'Url Base',
'description': 'When using mod_proxy use this to prepend the url with this.',
},
'api_key': {
'default': uuid4().hex,
'type': 'string',
'readonly': True,
'label': 'Api Key',
'description': 'This is top-secret! Don\'t share this!',
}
}
}] }]

View File

@@ -53,8 +53,9 @@ class Loader:
for section in module.config: for section in module.config:
fireEventAsync('settings.options', section['name'], section) fireEventAsync('settings.options', section['name'], section)
options = {} options = {}
for key, option in section['options'].iteritems(): for group in section['groups']:
options[key] = option['default'] for option in group['options']:
options[option['name']] = option['default']
fireEventAsync('settings.register', section_name = section['name'], options = options, save = save) fireEventAsync('settings.register', section_name = section['name'], options = options, save = save)
return True return True
except Exception, e: except Exception, e:

View File

@@ -1,33 +1,33 @@
def start(): def start():
pass pass
config = [{ #config = [{
'name': 'Renamer', # 'name': 'Renamer',
'tab': 'renaming', # 'tab': 'renaming',
'options': { # 'options': {
'enabled': { # 'enabled': {
'default': False, # 'default': False,
'type': 'bool', # 'type': 'bool',
'description': 'Enable renaming', # 'description': 'Enable renaming',
}, # },
'from': { # 'from': {
'default': '', # 'default': '',
'type': 'directory', # 'type': 'directory',
'label': 'From', # 'label': 'From',
'description': 'Folder where the movies are downloaded to.', # 'description': 'Folder where the movies are downloaded to.',
}, # },
'to': { # 'to': {
'default': '', # 'default': '',
'type': 'directory', # 'type': 'directory',
'label': 'To', # 'label': 'To',
'description': 'Folder where the movies will be moved to.', # 'description': 'Folder where the movies will be moved to.',
}, # },
'run_every': { # 'run_every': {
'default': 1, # 'default': 1,
'type': 'int', # 'type': 'int',
'unit': 'min(s)', # 'unit': 'min(s)',
'description': 'Search for new movies inside the folder every X minutes.', # 'description': 'Search for new movies inside the folder every X minutes.',
} # }
} # }
}] #}]
config = []

View File

@@ -4,14 +4,22 @@ def start():
return TMDB() return TMDB()
config = [{ config = [{
'name': 'TheMovieDB', 'name': 'themoviedb',
'tab': 'providers', 'groups': [
'options': { {
'api_key': { 'tab': 'providers',
'name': 'tmdb',
'label': 'TheMovieDB',
'advanced': True, 'advanced': True,
'default': '9b939aee0aaafc12a65bf448e4af9543', 'options': [
'type': 'string', {
'description': 'Api key to use for calls to TheMovieDB.', 'name': 'api_key',
} 'default': '9b939aee0aaafc12a65bf448e4af9543',
} 'type': 'string',
'label': 'Api Key',
'description': 'Used for all calls to TheMovieDB.',
},
],
},
],
}] }]

View File

@@ -5,8 +5,9 @@ from couchpotato.core.logger import CPLog
from couchpotato.core.providers.base import Provider from couchpotato.core.providers.base import Provider
from couchpotato.environment import Env from couchpotato.environment import Env
from urllib import quote_plus from urllib import quote_plus
import urllib2 import copy
import simplejson as json import simplejson as json
import urllib2
log = CPLog(__name__) log = CPLog(__name__)
@@ -21,7 +22,7 @@ class TMDB(Provider):
addEvent('provider.movie.search', self.search) addEvent('provider.movie.search', self.search)
def conf(self, attr): def conf(self, attr):
return Env.setting(attr, 'TheMovieDB') return Env.setting(attr, 'themoviedb')
def search(self, q, limit = 12, alternative = True): def search(self, q, limit = 12, alternative = True):
''' Find movie by name ''' ''' Find movie by name '''
@@ -48,7 +49,7 @@ class TMDB(Provider):
nr = 0 nr = 0
for movie in data: for movie in data:
year = movie['released'][:4] year = str(movie['released'])[:4]
# Poster url # Poster url
poster = '' poster = ''
@@ -59,7 +60,7 @@ class TMDB(Provider):
break break
# 1900 is the same as None # 1900 is the same as None
if year == '1900': if year == '1900' or year.lower() == 'none':
year = None year = None
movie_data = { movie_data = {
@@ -70,14 +71,13 @@ class TMDB(Provider):
'year': year, 'year': year,
'tagline': 'This is the tagline of the movie', 'tagline': 'This is the tagline of the movie',
} }
results.append(movie_data) results.append(copy.deepcopy(movie_data))
alternativeName = movie['alternative_name'] alternativeName = movie['alternative_name']
if alternativeName and alternative: if alternativeName and alternative:
if alternativeName.lower() != movie['name'].lower() and alternativeName.lower() != 'none' and alternativeName != None: if alternativeName.lower() != movie['name'].lower() and alternativeName.lower() != 'none' and alternativeName != None:
movie_data['name'] = toUnicode(alternativeName) movie_data['name'] = toUnicode(alternativeName)
results.append(movie_data) results.append(copy.deepcopy(movie_data))
nr += 1 nr += 1
if nr == limit: if nr == limit:
break break

View File

@@ -52,7 +52,7 @@ class Settings():
def set(self, section, option, value): def set(self, section, option, value):
return self.p.set(section, option, self.cleanValue(value)) return self.p.set(section, option, self.cleanValue(value))
def get(self, option = '', section = 'global', default = ''): def get(self, option = '', section = 'core', default = ''):
try: try:
value = self.p.get(section, option) value = self.p.get(section, option)
return self.cleanValue(value) return self.cleanValue(value)

View File

@@ -121,3 +121,12 @@ class RenameHistory(Entity):
new = Field(String(255)) new = Field(String(255))
file = ManyToOne('File') file = ManyToOne('File')
def setup():
""" Setup the database and create the tables that don't exists yet """
from elixir import setup_all, create_all
from couchpotato import get_engine
setup_all()
create_all(get_engine())

View File

@@ -31,7 +31,7 @@ class Env:
return setattr(Env, '_' + attr, value) return setattr(Env, '_' + attr, value)
@staticmethod @staticmethod
def setting(attr, section = 'global', value = None, default = ''): def setting(attr, section = 'core', value = None, default = ''):
# Return setting # Return setting
if value == None: if value == None:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -17,10 +17,6 @@ var BlockBase = new Class({
this.el = new Element('div.block'); this.el = new Element('div.block');
}, },
api: function(){
return this.getParent().getApi()
},
getParent: function(){ getParent: function(){
return this.page return this.page
}, },

View File

@@ -8,17 +8,57 @@ Block.Search = new Class({
var self = this; var self = this;
self.el = new Element('div.search_form').adopt( self.el = new Element('div.search_form').adopt(
self.input = new Element('input', { new Element('div.input').adopt(
'events': { self.input = new Element('input', {
'keyup': self.keyup.bind(self) '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
} }
}), }).adopt(
self.results = new Element('div.results') new Element('div.pointer'),
self.results = new Element('div.results')
).fade('hide')
); );
// Debug self.spinner = new Spinner(self.result_container);
self.input.set('value', 'iron man');
self.autocomplete(0) 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){ keyup: function(e){
@@ -36,6 +76,13 @@ Block.Search = new Class({
autocomplete: function(delay){ autocomplete: function(delay){
var self = this; var self = this;
if(!self.q()){
self.hideResults(true)
return
}
self.spinner.show()
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer) if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
self.autocomplete_timer = self.list.delay((delay || 300), self) self.autocomplete_timer = self.list.delay((delay || 300), self)
}, },
@@ -48,13 +95,16 @@ Block.Search = new Class({
var q = self.q(); var q = self.q();
var cache = self.cache[q]; var cache = self.cache[q];
if(!cache) self.hideResults(false)
self.api_request = self.api().request('movie.add.search', {
if(!cache){
self.api_request = Api.request('movie.add.search', {
'data': { 'data': {
'q': q 'q': q
}, },
'onComplete': self.fill.bind(self, q) 'onComplete': self.fill.bind(self, q)
}) })
}
else else
self.fill(q, cache) self.fill(q, cache)
@@ -65,20 +115,28 @@ Block.Search = new Class({
fill: function(q, json){ fill: function(q, json){
var self = this; var self = this;
self.spinner.hide();
self.cache[q] = json self.cache[q] = json
self.movies = [] self.movies = []
self.results.empty() self.results.empty()
Object.each(json.movies, function(movie){ 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) self.movies.include(m)
}); });
}, },
loading: function(bool){
this.el[bool ? 'addClass' : 'removeClass']('loading')
},
q: function(){ q: function(){
return this.input.get('value').trim(); return this.input.get('value').trim();
} }
@@ -93,31 +151,58 @@ Block.Search.Item = new Class({
self.info = info; self.info = info;
self.create(); self.create();
self.OuterClickStack = new EventStack.OuterClick();
}, },
create: function(){ create: function(){
var self = this; var self = this;
self.el = new Element('div.movie').adopt( var info = self.info
self.name = new Element('h2', {
'text': self.info.name self.el = new Element('div.movie', {
}), 'id': info.imdb
self.tagline = new Element('span', { }).adopt(
'text': self.info.tagline new Element('div.add').adopt(
}), new Element('span', {
self.year = self.info.year ? new Element('span', { 'text': 'test'
'text': self.info.year })
}) : null, ),
self.director = self.info.director ? new Element('span', { self.data_container = new Element('div.data', {
'text': 'Director:' + self.info.director 'tween': {
}) : null, duration: 400,
self.starring = self.info.actors ? new Element('span', { transition: 'quint:in:out'
'text': 'Starring:' },
}) : null '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){ if(info.actors){
Object.each(self.info.actors, function(actor){ Object.each(info.actors, function(actor){
new Element('span', { new Element('span', {
'text': actor.name 'text': actor.name
}).inject(self.starring) }).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(){ toElement: function(){
return this.el return this.el
} }

View File

@@ -18,7 +18,6 @@ var CouchPotato = new Class({
self.c = $(document.body) self.c = $(document.body)
self.route = new Route(self.defaults); self.route = new Route(self.defaults);
self.api = new Api(self.options.api)
self.createLayout(); self.createLayout();
self.createPages(); self.createPages();
@@ -43,8 +42,10 @@ var CouchPotato = new Class({
self.c.adopt( self.c.adopt(
self.header = new Element('div.header').adopt( self.header = new Element('div.header').adopt(
self.block.navigation = new Block.Navigation(self, {}), new Element('div').adopt(
self.block.search = new Block.Search(self, {}) self.block.navigation = new Block.Navigation(self, {}),
self.block.search = new Block.Search(self, {})
)
), ),
self.content = new Element('div.content'), self.content = new Element('div.content'),
self.block.footer = new Block.Footer(self, {}) self.block.footer = new Block.Footer(self, {})
@@ -71,36 +72,29 @@ var CouchPotato = new Class({
var action = self.route.getAction(); var action = self.route.getAction();
var params = self.route.getParams(); var params = self.route.getParams();
if(self.current_page)
self.current_page.hide()
var page = self.pages[page_name]; var page = self.pages[page_name];
page.open(action, params); page.open(action, params);
page.show(); page.show();
if(self.current_page)
self.current_page.hide()
self.current_page = page; self.current_page = page;
}, },
getBlock: function(block_name){ getBlock: function(block_name){
return this.block[block_name] return this.block[block_name]
},
getApi: function(){
return this.api
} }
}); });
var Api = new Class({ var ApiClass = new Class({
url: '', setup: function(options){
initialize: function(options){
var self = this var self = this
self.options = options; self.options = options;
}, },
request: function(type, options){ request: function(type, options){
@@ -123,6 +117,7 @@ var Api = new Class({
} }
}); });
window.Api = new ApiClass()
var Route = new Class({ var Route = new Class({
@@ -183,4 +178,44 @@ var Route = new Class({
var p = function(){ var p = function(){
if(typeof(console) !== 'undefined' && console != null) if(typeof(console) !== 'undefined' && console != null)
console.log(arguments) 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);
});
})();

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);

View File

@@ -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

View File

@@ -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;
}
});

View File

@@ -57,10 +57,6 @@ var PageBase = new Class({
getParent: function(){ getParent: function(){
return this.app return this.app
}, },
api: function(){
return this.getParent().getApi()
},
show: function(){ show: function(){
this.el.addClass('active'); this.el.addClass('active');

View File

@@ -5,11 +5,19 @@ Page.Settings = new Class({
name: 'settings', name: 'settings',
title: 'Change settings.', title: 'Change settings.',
groups: {}, tabs: {
'general': {
'label': 'General'
},
'providers': {
'label': 'Providers'
}
},
open: function(action, params){ open: function(action, params){
var self = this var self = this
//p('open config', action, params) self.action = action;
self.params = params;
if(!self.data) if(!self.data)
self.getData(self.create.bind(self)) self.getData(self.create.bind(self))
@@ -19,6 +27,7 @@ Page.Settings = new Class({
openTab: function(action){ openTab: function(action){
var self = this; var self = this;
action = action || self.action
if(self.current) if(self.current)
self.toggleTab(self.current, true); self.toggleTab(self.current, true);
@@ -27,16 +36,16 @@ Page.Settings = new Class({
self.current = action; self.current = action;
}, },
toggleTab: function(tab, hide){ toggleTab: function(tab_name, hide){
var self = this; var self = this;
var a = hide ? 'removeClass' : 'addClass'; var a = hide ? 'removeClass' : 'addClass';
var c = 'active'; var c = 'active';
var g = self.groups[tab] || self.groups.general; var t = self.tabs[tab_name] || self.tabs[self.action] || self.tabs.general;
g.tab[a](c); t.tab[a](c);
g.group[a](c); t.content[a](c);
}, },
@@ -44,7 +53,7 @@ Page.Settings = new Class({
var self = this; var self = this;
if(onComplete) if(onComplete)
self.api().request('settings', { Api.request('settings', {
'useSpinner': true, 'useSpinner': true,
'spinnerOptions': { 'spinnerOptions': {
'target': self.el '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){ create: function(json){
var self = this var self = this
self.el.adopt( self.el.adopt(
self.tabs = new Element('ul.tabs'), self.tabs_container = new Element('ul.tabs'),
self.containers = new Element('form.uniForm.containers') 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){ Object.each(json.options, function(section, section_name){
// Create tab // Add groups to content
var tab = new Element('li').adopt( section.groups.sortBy('order').each(function(group){
new Element('a', {
'href': '/'+self.name+'/'+section.tab+'/',
'text': section.tab.capitalize()
})
).inject(self.tabs);
var group = new Element('div.group').inject(self.containers);
self.groups[section.tab] = { // Create the group
'tab': tab, var group_el = new Element('fieldset', {
'group': group '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(); self.openTab();
@@ -251,6 +301,7 @@ Option.String = new Class({
'text': self.options.label 'text': self.options.label
}), }),
self.input = new Element('input', { self.input = new Element('input', {
'type': 'text',
'name': self.postName(), 'name': self.postName(),
'value': self.getSettingValue() 'value': self.getSettingValue()
}) })
@@ -291,15 +342,19 @@ Option.Checkbox = new Class({
create: function(){ create: function(){
var self = this; var self = this;
var randomId = 'option-'+Math.floor(Math.random()*1000000)
new Element('label', { new Element('label', {
'text': self.options.label 'text': self.options.label,
}).adopt( 'for': randomId
self.input = new Element('input', { }).inject(self.el);
'type': 'checkbox',
'value': self.getSettingValue(), self.input = new Element('input', {
'checked': self.getSettingValue() !== undefined 'type': 'checkbox',
}) 'value': self.getSettingValue(),
).inject(self.el); 'checked': self.getSettingValue() !== undefined,
'id': randomId
}).inject(self.el);
} }
}); });

View File

@@ -30,7 +30,7 @@ Page.Wanted = new Class({
var self = this var self = this
if(self.movies.length == 0) if(self.movies.length == 0)
self.api().request('movie', { Api.request('movie', {
'data': {}, 'data': {},
'onComplete': function(json){ 'onComplete': function(json){
self.store(json.movies); self.store(json.movies);

View File

@@ -1,8 +1,11 @@
/* @override http://localhost:5000/static/style/main.css */
html { html {
color: #343434; color: #343434;
font-size: 12px; font-size: 12px;
line-height: 1.5; line-height: 1.5;
font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; font-family: Helvetica, Arial, Geneva, sans-serif;
height: 100%;
} }
body { body {
@@ -10,6 +13,7 @@ body {
padding: 0; padding: 0;
background: #fff; background: #fff;
overflow-y: scroll; overflow-y: scroll;
height: 100%;
} }
body.noscroll { overflow: hidden; } body.noscroll { overflow: hidden; }
@@ -42,11 +46,13 @@ a {
a:hover { color: #4d66c4; } a:hover { color: #4d66c4; }
.page { .page {
display: none;
width: 960px; width: 960px;
margin: 0 auto; margin: 0 auto;
line-height: 24px; line-height: 24px;
padding: 0 0 20px; padding: 0 0 20px;
} }
.page.active { display: block; }
.page .noticeMe { .page .noticeMe {
background-color: lightgoldenrodyellow; background-color: lightgoldenrodyellow;
@@ -59,7 +65,7 @@ a:hover { color: #4d66c4; }
.content { .content {
clear:both; clear:both;
padding: 130px 10px 10px; padding: 80px 10px 10px;
} }
.footer { .footer {
@@ -99,7 +105,6 @@ form {
.header { .header {
background: #f7f7f7; background: #f7f7f7;
padding:10px; padding:10px;
margin-bottom: 20px;
border-bottom: 1px solid #f1f1f1; border-bottom: 1px solid #f1f1f1;
height: 60px; height: 60px;
-moz-box-shadow: 0 0 30px rgba(0,0,0,0.1); -moz-box-shadow: 0 0 30px rgba(0,0,0,0.1);
@@ -109,20 +114,24 @@ form {
z-index: 9999; z-index: 9999;
} }
.header .navigation { .header > div {
width: 960px; width: 960px;
margin: 0 auto; margin: 0 auto;
overflow: hidden; overflow: hidden;
} }
.header .navigation {
display: inline-block;
width: 75%;
margin: 0;
padding: 0;
}
.header .navigation li { .header .navigation li {
color: #8b8b8b; color: #8b8b8b;
display: block; display: inline-block;
font-size:20px; font-size:20px;
font-weight: bold; font-weight: bold;
margin: 0; margin: 0;
text-align: center; text-align: center;
float: left;
} }
.header .navigation li a { .header .navigation li a {
@@ -153,3 +162,4 @@ form {
.header .navigation li a:hover, .header .navigation li a:active { .header .navigation li a:hover, .header .navigation li a:active {
color: #8b8b8b; color: #8b8b8b;
} }

View File

@@ -0,0 +1,119 @@
/* @override http://localhost:5000/static/style/movie_add.css */
.search_form {
display: inline-block;
width: 25%;
}
.search_form input {
padding-right: 25px;
border: 1px solid #aaa;
padding: 4px;
margin: 0;
font-size: 14px;
width: 90%;
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
}
.search_form .input a {
width: 12px;
height: 20px;
display: inline-block;
margin: 0 0 -5px -20px;
top: 4px;
right: 5px;
background: url('../images/close_button.png') 0 center no-repeat;
cursor: pointer;
}
.search_form .input a:hover { background-position: -12px center; }
.search_form .results_container {
padding: 10px 0;
position: absolute;
background: #fff;
margin: 11px 0 0 -243px;
width: 470px;
min-height: 140px;
box-shadow: 0 0 30px rgba(0,0,0,0.2);
-moz-box-shadow: 0 0 30px rgba(0,0,0,0.2);
-webkit-box-shadow: 0 0 30px rgba(0,0,0,0.2);
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
}
.search_form .spinner {
background: #fff url('../images/spinner.gif') no-repeat center 70px;
}
.search_form .pointer {
border-right: 10px solid transparent;
border-left: 10px solid transparent;
border-bottom: 10px solid #fff;
display: block;
position: absolute;
width: 0px;
left: 50%;
margin: -19px 0 0 110px;
}
.search_form .results .movie {
overflow: hidden;
background: #666;
min-height: 140px;
}
.search_form .results .movie .add {
min-height: 140px;
}
.search_form .results .movie .data {
padding: 0 15px;
width: 440px;
position: relative;
min-height: 100px;
top: 0;
margin: -140px 0 0 0;
background: #fff;
min-height: 140px;
}
.search_form .results .movie .thumbnail {
width: 17%;
display: inline-block;
margin: 15px 3% 15px 0;
vertical-align: top;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
box-shadow: 0 0 3px rgba(0,0,0,0.35);
-moz-box-shadow: 0 0 3px rgba(0,0,0,0.35);
-webkit-box-shadow: 0 0 3px rgba(0,0,0,0.35);
}
.search_form .results .movie .info {
width: 74%;
display: inline-block;
vertical-align: top;
padding: 15px 0;
background: #fff;
}
.search_form .results .movie .add +.info {
margin-left: 20%;
}
.search_form .results .movie .info h2 {
margin: 0;
}
.search_form .results .movie .info h2 span {
padding: 0 5px;
content: ")";
}
.search_form .results .movie .info h2 span:before { content: "("; }
.search_form .results .movie .info h2 span:after { content: ")"; }

View File

@@ -0,0 +1,126 @@
/* @override http://localhost:5000/static/style/page/settings.css */
.page.settings {
overflow: hidden;
}
.page.settings .tabs {
float: left;
width: 20%;
font-size: 25px;
text-align: right;
list-style: none;
padding: 40px 0;
margin: 0;
min-height: 300px;
}
.page.settings .tabs a {
display: block;
padding: 10px 15px;
border: 1px solid transparent;
position: relative;
z-index: 200;
margin-right: -1px;
}
.page.settings .tabs .active a {
color: black;
background: #fbfbfb;
border-color: #f1f1f1;
border-right-color: transparent;
}
.page.settings .containers {
width: 75.8%;
float: left;
padding: 20px 2%;
min-height: 300px;
background: #fbfbfb;
border-left: 1px solid #f1f1f1;s
}
.page.settings .advanced {
display: none;
color: #ce3b19;
}
.page.settings.show_advanced .advanced { display: block; }
.page.settings .tab_content {
display: none;
}
.page.settings .tab_content.active { display: block; }
.page.settings fieldset {
padding: 10px 0;
}
.page.settings fieldset h2 {
font-weight: normal;
font-size: 25px;
padding: 0 9px 10px;
margin: 0;
border-bottom: 1px solid #f3f3f3;
}
.page.settings fieldset h2 .hint {
color: #888;
font-size: 12px;
margin-left: 10px;
}
.page.settings .ctrlHolder {
line-height: 25px;
padding: 10px;
border-bottom: 1px solid #f3f3f3;
font-size: 14px;
}
.page.settings .ctrlHolder:last-child { border: none; }
.page.settings .ctrlHolder:hover { background: rgba(211,234,254,0.1); }
.page.settings .ctrlHolder.focused:hover { background: rgba(251,246,48,0.29); }
.page.settings .ctrlHolder .formHint {
float: right;
width: 47%;
margin: -18px 0;
padding: 0;
}
.page.settings .ctrlHolder input[type=checkbox] + .formHint {
float: none;
width: auto;
display: inline-block;
margin-left: 1% !important;
color: #222;
}
.page.settings .ctrlHolder label {
font-weight: bold;
width: 20%;
margin: 0;
padding: 6px 0 0;
}
.page.settings input[type=text] {
border: 1px solid #aaa;
padding: 3px;
margin: 0;
width: 30%;
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
}
.page.settings .input.xsmall { width: 5% }
.page.settings .input.small { width: 10% }
.page.settings .input.medium { width: 15% }
.page.settings .input.large { width: 25% }
.page.settings .input.xlarge { width: 30% }
.page.settings .advanced_toggle {
clear: both;
display: block;
text-align: right;
height: 20px;
margin: 0;
}
.page.settings .advanced_toggle span { padding: 0 5px; }
.page.settings.show_advanced .advanced_toggle {
color: #ce3b19;
}

View File

@@ -5,12 +5,17 @@
<link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.generic.css') }}"> <link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.generic.css') }}">
<link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.css') }}"> <link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.css') }}">
<link rel="stylesheet" href="{{ url_for('.static', filename='style/page/settings.css') }}">
<link rel="stylesheet" href="{{ url_for('.static', filename='style/movie_add.css') }}">
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/mootools.js') }}"></script> <script type="text/javascript" src="{{ url_for('.static', filename='scripts/mootools.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/mootools_more.js') }}"></script> <script type="text/javascript" src="{{ url_for('.static', filename='scripts/mootools_more.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/uniform.js') }}"></script> <script type="text/javascript" src="{{ url_for('.static', filename='scripts/uniform.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/couchpotato.js') }}"></script> <script type="text/javascript" src="{{ url_for('.static', filename='scripts/couchpotato.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/history.js') }}"></script> <script type="text/javascript" src="{{ url_for('.static', filename='scripts/history.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/eventstack.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/eventstack_outerclick.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/block.js') }}"></script> <script type="text/javascript" src="{{ url_for('.static', filename='scripts/block.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/block/navigation.js') }}"></script> <script type="text/javascript" src="{{ url_for('.static', filename='scripts/block/navigation.js') }}"></script>
@@ -29,13 +34,15 @@
<script type="text/javascript"> <script type="text/javascript">
window.addEvent('domready', function() { window.addEvent('domready', function() {
new Uniform(); new Uniform();
Api.setup({
'url': '{{ url_for('api.index') }}',
'path_sep': '{{ sep }}',
'is_remote': false
})
var cp = new CouchPotato({ var cp = new CouchPotato({
'base_url': '{{ request.path }}', 'base_url': '{{ request.path }}'
'api': {
'url': '{{ url_for('api.index') }}',
'path_sep': '{{ sep }}',
'is_remote': false
}
}); });
}) })
</script> </script>