Movie List
Directory selection
This commit is contained in:
Ruud
2011-02-21 22:42:24 +01:00
parent 272e0b8527
commit 8682be33fb
27 changed files with 1770 additions and 233 deletions
+2 -1
View File
@@ -6,6 +6,7 @@ from flask.helpers import url_for
from flask.module import Module
from flask.templating import render_template
from werkzeug.utils import redirect
import os
app = Flask(__name__)
log = CPLog(__name__)
@@ -15,7 +16,7 @@ web = Module(__name__, 'web')
@web.route('/')
@requires_auth
def index():
return render_template('index.html')
return render_template('index.html', sep = os.sep)
@app.errorhandler(404)
def page_not_found(error):
+59 -1
View File
@@ -1,8 +1,66 @@
from couchpotato.api.file_browser import FileBrowser
from couchpotato.core.settings import settings
from couchpotato.core.settings.loader import settings_loader
from flask import Module
from flask.helpers import jsonify
import flask
api = Module(__name__)
@api.route('/')
@api.route('')
def index():
return jsonify({'test': 'bla'})
@api.route('settings/')
def settings_view():
return jsonify({
'sections': settings_loader.sections,
'values': settings.getValues()
})
@api.route('setting.save/')
def setting_save_view():
a = flask.request.args
section = a.get('section')
option = a.get('name')
value = a.get('value')
settings.set(section, option, value)
settings.save()
return jsonify({
'success': True,
});
@api.route('movie/')
def movie():
return jsonify({
'success': True,
'movies': [
{
'name': 'Movie 1',
'description': 'Description 1',
},
{
'name': 'Movie 2',
'description': 'Description 2',
}
]
})
@api.route('directory.list/')
def director_list():
a = flask.request.args
try:
fb = FileBrowser(a.get('path', '/'))
dirs = fb.getDirectories()
except:
dirs = []
return jsonify({
'empty': len(dirs) == 0,
'dirs': dirs,
})
+38
View File
@@ -0,0 +1,38 @@
import os
import string
if os.name == 'nt':
import win32file
class FileBrowser():
def __init__(self, path = '/'):
self.path = path
def getDirectories(self):
# Return driveletters or root if path is empty
if self.path == '/' or not self.path:
if os.name == 'nt':
return self.getDriveLetters()
self.path = '/'
dirs = []
for f in os.listdir(self.path):
path = os.path.join(self.path, f)
if(os.path.isdir(path)):
dirs.append(path)
return dirs
def getFiles(self):
pass
def getDriveLetters(self):
driveletters = []
for drive in string.ascii_uppercase:
if win32file.GetDriveType(drive + ":") == win32file.DRIVE_FIXED:
driveletters.append(drive + ":")
return driveletters
+5 -5
View File
@@ -77,10 +77,10 @@ def cmd_couchpotato(base_path, args):
# Load configs
from couchpotato.core.settings.loader import SettingsLoader
sl = SettingsLoader(root = base_path)
sl.addConfig('couchpotato', 'core')
sl.run()
from couchpotato.core.settings.loader import settings_loader
settings_loader.load(root = base_path)
settings_loader.addConfig('couchpotato', 'core')
settings_loader.run()
# Create app
@@ -103,7 +103,7 @@ def cmd_couchpotato(base_path, args):
# Register modules
app.register_module(web, url_prefix = '%s/' % url_base)
app.register_module(api, url_prefix = '%s/%s/%s/' % (url_base, 'api', api_key))
app.register_module(api, url_prefix = '%s/%s/%s/' % (url_base, 'api', api_key if not debug else 'apikey'))
# Go go go!
app.run(use_reloader = reloader)
+57 -10
View File
@@ -1,12 +1,59 @@
from uuid import uuid4
config = ('global', {
'debug': False,
'host': '0.0.0.0',
'port': 5000,
'username': '',
'password': '',
'launch_browser': True,
'url_base': '',
'api_key': uuid4().hex,
})
config = [{
'name': 'global',
'tab': 'general',
'options': {
'debug': {
'advanced': True,
'default': False,
'type': 'bool',
'label': 'Debug',
'description': 'Enable debugging.',
},
'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!',
}
}
}]
+30 -4
View File
@@ -1,4 +1,30 @@
config = ('Renamer', {
'enabled': False,
'cleanup': False
})
config = [{
'name': 'Renamer',
'tab': 'renaming',
'options': {
'enabled': {
'default': False,
'type': 'bool',
'description': 'Enable renaming',
},
'from': {
'default': '',
'type': 'directory',
'label': 'From',
'description': 'Folder where the movies are downloaded to.',
},
'to': {
'default': '',
'type': 'directory',
'label': 'To',
'description': 'Folder where the movies will be moved to.',
},
'run_every': {
'default': 1,
'type': 'int',
'unit': 'min(s)',
'description': 'Search for new movies inside the folder every X minutes.',
}
}
}]
+12 -3
View File
@@ -1,3 +1,12 @@
config = ('TheMovieDB', {
'key': '9b939aee0aaafc12a65bf448e4af9543'
})
config = [{
'name': 'TheMovieDB',
'tab': 'providers',
'options': {
'api_key': {
'advanced': True,
'default': '9b939aee0aaafc12a65bf448e4af9543',
'type': 'string',
'description': 'Api key to use for calls to TheMovieDB.',
}
}
}]
+22 -12
View File
@@ -27,7 +27,7 @@ class Settings():
return self.p
def sections(self):
return self.s
return self.p.sections()
def connectSignals(self):
signal('settings.register').connect(self.registerDefaults)
@@ -46,24 +46,34 @@ class Settings():
self.save(self)
def set(self, section, option, value):
return self.p.set(section, option, value)
return self.p.set(section, option, self.cleanValue(value))
def get(self, option = '', section = 'global', default = ''):
try:
value = self.p.get(section, option)
if(self.is_int(value)):
return int(value)
if str(value).lower() in self.bool:
return self.bool.get(str(value).lower())
return value if type(value) != str else value.strip()
return self.cleanValue(value)
except:
return default
def save(self, caller):
def cleanValue(self, value):
if(self.is_int(value)):
return int(value)
if str(value).lower() in self.bool:
return self.bool.get(str(value).lower())
return value.strip()
def getValues(self):
values = {}
for section in self.sections():
values[section] = {}
for option in self.p.items(section):
(option_name, option_value) = option
values[section][option_name] = option_value
return values
def save(self, caller = ''):
with open(self.file, 'wb') as configfile:
self.p.write(configfile)
+19 -4
View File
@@ -8,12 +8,14 @@ log = CPLog(__name__)
class SettingsLoader:
configs = {}
sections = {}
def __init__(self, root = ''):
def __init__(self):
self.settings_register = signal('settings.register')
self.settings_save = signal('settings.save')
def load(self, root = ''):
self.paths = {
'plugins' : ('couchpotato.core.plugins', os.path.join(root, 'couchpotato', 'core', 'plugins')),
'providers' : ('couchpotato.core.providers', os.path.join(root, 'couchpotato', 'core', 'providers')),
@@ -42,8 +44,13 @@ class SettingsLoader:
module_name = '%s.%s' % (module, name)
try:
m = getattr(self.loadModule(module_name), name)
(section, options) = m.config
self.settings_register.send(section, options = options, save = save)
for section in m.config:
self.addSection(section['name'], section)
options = {}
for key, option in section['options'].iteritems():
options[key] = option['default']
self.settings_register.send(section['name'], options = options, save = save)
return True
except Exception, e:
@@ -62,3 +69,11 @@ class SettingsLoader:
return m
except:
raise
def addSection(self, section_name, options):
self.sections[section_name] = options
def getSections(self):
return self.sections
settings_loader = SettingsLoader()
+26
View File
@@ -0,0 +1,26 @@
var BlockBase = new Class({
Implements: [Options, Events],
options: {},
initialize: function(parent, options){
var self = this;
self.setOptions(options);
self.parent = parent;
self.create();
},
create: function(){
this.el = new Element('div.block');
},
toElement: function(){
return this.el
}
});
var Block = {}
@@ -0,0 +1,11 @@
Block.Footer = new Class({
Extends: BlockBase,
create: function(){
var self = this;
self.el = new Element('div.footer');
}
});
@@ -0,0 +1,20 @@
Block.Navigation = new Class({
Extends: BlockBase,
create: function(){
var self = this;
self.el = new Element('ul.navigation');
},
addTab: function(tab){
var self = this
return new Element('li').adopt(
new Element('a', tab)
).inject(self.el)
}
});
@@ -0,0 +1,11 @@
Block.Search = new Class({
Extends: BlockBase,
create: function(){
var self = this;
self.el = new Element('div.search_form');
}
});
+56 -96
View File
@@ -3,20 +3,13 @@ var CouchPotato = new Class({
Implements: [Options],
defaults: {
page: 'movie',
page: 'wanted',
action: 'index',
params: {}
},
pages: [],
tabs: [
{'href': '/movie/', 'title':'Gimmy gimmy gimmy!', 'label':'Wanted'},
{'href': '/manage/', 'title':'Do stuff to your existing movies!', 'label':'Manage'},
{'href': '/feed/', 'title':'Which wanted movies are released soon?', 'label':'Soon'},
{'href': '/log/', 'title':'Show recent logs.', 'class':'logLink', 'label':'Logs'},
{'href': '/config/', 'title':'Change settings.', 'id':'showConfig'}
],
block: [],
initialize: function(options) {
var self = this;
@@ -25,38 +18,24 @@ var CouchPotato = new Class({
self.c = $(document.body)
self.route = new Route(self.defaults);
self.api = new Api(self.options.api_url)
self.api = new Api(self.options.api)
History.addEvent('change', self.createPage.bind(self));
self.createLayout();
self.createPages();
History.addEvent('change', self.openPage.bind(self));
History.handleInitialState();
self.createLayout()
self.createNavigation()
self.c.addEvent('click:relay(a)', self.openPage.bind(self))
self.c.addEvent('click:relay(a)', self.pushState.bind(self));
},
openPage: function(e){
pushState: function(e){
var self = this;
(e).stop()
var url = e.target.get('href')
History.push(url)
},
createNavigation: function(){
var self = this
self.tabs.each(function(tab){
new Element('li').adopt(
new Element('a', {
'href': tab.href,
'title': tab.title,
'text': tab.label
})
).inject(self.navigation)
})
(e).stop();
var url = e.target.get('href');
if(History.getPath() != url)
History.push(url);
},
createLayout: function(){
@@ -64,15 +43,27 @@ var CouchPotato = new Class({
self.c.adopt(
self.header = new Element('div.header').adopt(
self.navigation = new Element('ul.navigation'),
self.add_form = new Element('div.add_form')
self.block.navigation = new Block.Navigation(self, {}),
self.block.search = new Block.Search(self, {})
),
self.content = new Element('div.content'),
self.footer = new Element('div.footer')
)
self.block.footer = new Block.Footer(self, {})
);
},
createPage: function(url) {
createPages: function(){
var self = this;
Object.each(Page, function(page_class, class_name){
pg = new Page[class_name](self, {});
self.pages[class_name] = pg;
$(pg).inject(self.content);
});
},
openPage: function(url) {
var self = this;
self.route.parse(url);
@@ -80,14 +71,19 @@ var CouchPotato = new Class({
var action = self.route.getAction();
var params = self.route.getParams();
var pg = self.pages[page_name]
if(!pg){
pg = new Page[page_name]();
pg.setParent(self)
self.pages[page_name] = pg;
}
pg.open(action, params)
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(){
@@ -95,77 +91,41 @@ var CouchPotato = new Class({
}
});
var PageBase = new Class({
Implements: [Options],
initialize: function(options) {
},
open: function(action, params){
var self = this;
p('Opening: ' +self.getName() + ', ' + action + ', ' + Object.toQueryString(params));
try {
self[action+'Action'](params)
}
catch (e){
self.errorAction(e)
}
},
errorAction: function(e){
p('Error, action not found', e);
},
getName: function(){
return this.name
},
setParent: function(parent){
this.parent = parent
},
getParent: function(){
return this.parent
},
api: function(){
return this.parent.getApi()
}
});
var Api = new Class({
url: '',
initialize: function(url){
initialize: function(options){
var self = this
self.url = url
self.options = options;
self.req = new Request.JSON({
'method': 'get'
})
},
request: function(type, params, data){
request: function(type, options){
var self = this;
self.req.setOptions({
'url': self.createUrl(type, params),
'data': data
})
self.req.send()
new Request.JSON(Object.merge({
'method': 'get',
'url': self.createUrl(type),
}, options)).send()
},
createUrl: function(action, params){
return this.url + (action || 'default') + '/?' + Object.toQueryString(params)
createUrl: function(action){
return this.options.url + (action || 'default') + '/'
},
getOption: function(name){
return this.options[name]
}
});
var Route = new Class({
defaults: {},
+183 -2
View File
@@ -3,10 +3,10 @@
MooTools: the javascript framework
web build:
- http://mootools.net/core/755c9fd7bb9342a1fa57db060c2cd141
- http://mootools.net/core/efcfcd2923b4129a00a22580c45b1d75
packager build:
- packager build Core/Class Core/Class.Extras Core/Element Core/Request.JSON Core/DOMReady
- packager build Core/Class Core/Class.Extras Core/Element Core/Element.Style Core/Request.JSON Core/DOMReady
/*
---
@@ -3120,6 +3120,187 @@ Element.Properties.html = (function(){
})();
/*
---
name: Element.Style
description: Contains methods for interacting with the styles of Elements in a fashionable way.
license: MIT-style license.
requires: Element
provides: Element.Style
...
*/
(function(){
var html = document.html;
Element.Properties.styles = {set: function(styles){
this.setStyles(styles);
}};
var hasOpacity = (html.style.opacity != null);
var reAlpha = /alpha\(opacity=([\d.]+)\)/i;
var setOpacity = function(element, opacity){
if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
if (hasOpacity){
element.style.opacity = opacity;
} else {
opacity = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
var filter = element.style.filter || element.getComputedStyle('filter') || '';
element.style.filter = filter.test(reAlpha) ? filter.replace(reAlpha, opacity) : filter + opacity;
}
};
Element.Properties.opacity = {
set: function(opacity){
var visibility = this.style.visibility;
if (opacity == 0 && visibility != 'hidden') this.style.visibility = 'hidden';
else if (opacity != 0 && visibility != 'visible') this.style.visibility = 'visible';
setOpacity(this, opacity);
},
get: (hasOpacity) ? function(){
var opacity = this.style.opacity || this.getComputedStyle('opacity');
return (opacity == '') ? 1 : opacity;
} : function(){
var opacity, filter = (this.style.filter || this.getComputedStyle('filter'));
if (filter) opacity = filter.match(reAlpha);
return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
}
};
var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat';
Element.implement({
getComputedStyle: function(property){
if (this.currentStyle) return this.currentStyle[property.camelCase()];
var defaultView = Element.getDocument(this).defaultView,
computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : null;
},
setOpacity: function(value){
setOpacity(this, value);
return this;
},
getOpacity: function(){
return this.get('opacity');
},
setStyle: function(property, value){
switch (property){
case 'opacity': return this.set('opacity', parseFloat(value));
case 'float': property = floatName;
}
property = property.camelCase();
if (typeOf(value) != 'string'){
var map = (Element.Styles[property] || '@').split(' ');
value = Array.from(value).map(function(val, i){
if (!map[i]) return '';
return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
}).join(' ');
} else if (value == String(Number(value))){
value = Math.round(value);
}
this.style[property] = value;
return this;
},
getStyle: function(property){
switch (property){
case 'opacity': return this.get('opacity');
case 'float': property = floatName;
}
property = property.camelCase();
var result = this.style[property];
if (!result || property == 'zIndex'){
result = [];
for (var style in Element.ShortStyles){
if (property != style) continue;
for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
return result.join(' ');
}
result = this.getComputedStyle(property);
}
if (result){
result = String(result);
var color = result.match(/rgba?\([\d\s,]+\)/);
if (color) result = result.replace(color[0], color[0].rgbToHex());
}
if (Browser.opera || (Browser.ie && isNaN(parseFloat(result)))){
if (property.test(/^(height|width)$/)){
var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
values.each(function(value){
size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
}, this);
return this['offset' + property.capitalize()] - size + 'px';
}
if (Browser.opera && String(result).indexOf('px') != -1) return result;
if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
}
return result;
},
setStyles: function(styles){
for (var style in styles) this.setStyle(style, styles[style]);
return this;
},
getStyles: function(){
var result = {};
Array.flatten(arguments).each(function(key){
result[key] = this.getStyle(key);
}, this);
return result;
}
});
Element.Styles = {
left: '@px', top: '@px', bottom: '@px', right: '@px',
width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
};
Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
var Short = Element.ShortStyles;
var All = Element.Styles;
['margin', 'padding'].each(function(style){
var sd = style + direction;
Short[style][sd] = All[sd] = '@px';
});
var bd = 'border' + direction;
Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
Short[bd] = {};
Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
});
})();
/*
---
+279 -2
View File
@@ -1,6 +1,141 @@
// MooTools: the javascript framework.
// Load this file's selection again by visiting: http://mootools.net/more/82d3c4f6ee721f808321f6bf6818d8fb
// Or build this file again with packager using: packager build More/Element.Delegation More/Element.Shortcuts
// Load this file's selection again by visiting: http://mootools.net/more/4da9e3082bf0d4194b76d7e5b3874113
// Or build this file again with packager using: packager build More/Element.Forms More/Element.Delegation More/Element.Shortcuts
/*
---
script: String.Extras.js
name: String.Extras
description: Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).
license: MIT-style license
authors:
- Aaron Newton
- Guillermo Rauch
- Christopher Pitt
requires:
- Core/String
- Core/Array
provides: [String.Extras]
...
*/
(function(){
var special = {
'a': /[àáâãäåăą]/g,
'A': /[ÀÁÂÃÄÅĂĄ]/g,
'c': /[ćčç]/g,
'C': /[ĆČÇ]/g,
'd': /[ďđ]/g,
'D': /[ĎÐ]/g,
'e': /[èéêëěę]/g,
'E': /[ÈÉÊËĚĘ]/g,
'g': /[ğ]/g,
'G': /[Ğ]/g,
'i': /[ìíîï]/g,
'I': /[ÌÍÎÏ]/g,
'l': /[ĺľł]/g,
'L': /[ĹĽŁ]/g,
'n': /[ñňń]/g,
'N': /[ÑŇŃ]/g,
'o': /[òóôõöøő]/g,
'O': /[ÒÓÔÕÖØ]/g,
'r': /[řŕ]/g,
'R': /[ŘŔ]/g,
's': /[ššş]/g,
'S': /[ŠŞŚ]/g,
't': /[ťţ]/g,
'T': /[ŤŢ]/g,
'ue': /[ü]/g,
'UE': /[Ü]/g,
'u': /[ùúûůµ]/g,
'U': /[ÙÚÛŮ]/g,
'y': /[ÿý]/g,
'Y': /[ŸÝ]/g,
'z': /[žźż]/g,
'Z': /[ŽŹŻ]/g,
'th': /[þ]/g,
'TH': /[Þ]/g,
'dh': /[ð]/g,
'DH': /[Ð]/g,
'ss': /[ß]/g,
'oe': /[œ]/g,
'OE': /[Œ]/g,
'ae': /[æ]/g,
'AE': /[Æ]/g
},
tidy = {
' ': /[\xa0\u2002\u2003\u2009]/g,
'*': /[\xb7]/g,
'\'': /[\u2018\u2019]/g,
'"': /[\u201c\u201d]/g,
'...': /[\u2026]/g,
'-': /[\u2013]/g,
// '--': /[\u2014]/g,
'»': /[\uFFFD]/g
};
var walk = function(string, replacements){
var result = string;
for (key in replacements) result = result.replace(replacements[key], key);
return result;
};
var getRegexForTag = function(tag, contents){
tag = tag || '';
var regstr = contents ? "<" + tag + "(?!\\w)[^>]*>([\\s\\S]*?)<\/" + tag + "(?!\\w)>" : "<\/?" + tag + "([^>]+)?>";
reg = new RegExp(regstr, "gi");
return reg;
};
String.implement({
standardize: function(){
return walk(this, special);
},
repeat: function(times){
return new Array(times + 1).join(this);
},
pad: function(length, str, direction){
if (this.length >= length) return this;
var pad = (str == null ? ' ' : '' + str)
.repeat(length - this.length)
.substr(0, length - this.length);
if (!direction || direction == 'right') return this + pad;
if (direction == 'left') return pad + this;
return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil());
},
getTags: function(tag, contents){
return this.match(getRegexForTag(tag, contents)) || [];
},
stripTags: function(tag, contents){
return this.replace(getRegexForTag(tag, contents), '');
},
tidy: function(){
return walk(this, tidy);
}
});
})();
/*
---
@@ -35,6 +170,148 @@ MooTools.More = {
};
/*
---
script: Element.Forms.js
name: Element.Forms
description: Extends the Element native object to include methods useful in managing inputs.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Element
- /String.Extras
- /MooTools.More
provides: [Element.Forms]
...
*/
Element.implement({
tidy: function(){
this.set('value', this.get('value').tidy());
},
getTextInRange: function(start, end){
return this.get('value').substring(start, end);
},
getSelectedText: function(){
if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd());
return document.selection.createRange().text;
},
getSelectedRange: function(){
if (this.selectionStart != null){
return {
start: this.selectionStart,
end: this.selectionEnd
};
}
var pos = {
start: 0,
end: 0
};
var range = this.getDocument().selection.createRange();
if (!range || range.parentElement() != this) return pos;
var duplicate = range.duplicate();
if (this.type == 'text'){
pos.start = 0 - duplicate.moveStart('character', -100000);
pos.end = pos.start + range.text.length;
} else {
var value = this.get('value');
var offset = value.length;
duplicate.moveToElementText(this);
duplicate.setEndPoint('StartToEnd', range);
if (duplicate.text.length) offset -= value.match(/[\n\r]*$/)[0].length;
pos.end = offset - duplicate.text.length;
duplicate.setEndPoint('StartToStart', range);
pos.start = offset - duplicate.text.length;
}
return pos;
},
getSelectionStart: function(){
return this.getSelectedRange().start;
},
getSelectionEnd: function(){
return this.getSelectedRange().end;
},
setCaretPosition: function(pos){
if (pos == 'end') pos = this.get('value').length;
this.selectRange(pos, pos);
return this;
},
getCaretPosition: function(){
return this.getSelectedRange().start;
},
selectRange: function(start, end){
if (this.setSelectionRange){
this.focus();
this.setSelectionRange(start, end);
} else {
var value = this.get('value');
var diff = value.substr(start, end - start).replace(/\r/g, '').length;
start = value.substr(0, start).replace(/\r/g, '').length;
var range = this.createTextRange();
range.collapse(true);
range.moveEnd('character', start + diff);
range.moveStart('character', start);
range.select();
}
return this;
},
insertAtCursor: function(value, select){
var pos = this.getSelectedRange();
var text = this.get('value');
this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length));
if (select !== false) this.selectRange(pos.start, pos.start + value.length);
else this.setCaretPosition(pos.start + value.length);
return this;
},
insertAroundCursor: function(options, select){
options = Object.append({
before: '',
defaultMiddle: '',
after: ''
}, options);
var value = this.getSelectedText() || options.defaultMiddle;
var pos = this.getSelectedRange();
var text = this.get('value');
if (pos.start == pos.end){
this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length));
this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length);
} else {
var current = text.substring(pos.start, pos.end);
this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length));
var selStart = pos.start + options.before.length;
if (select !== false) this.selectRange(selStart, selStart + current.length);
else this.setCaretPosition(selStart + text.length);
}
return this;
}
});
/*
---
+78
View File
@@ -0,0 +1,78 @@
var PageBase = new Class({
Implements: [Options, Events],
options: {
},
has_tab: true,
initialize: function(parent, options) {
var self = this;
self.setOptions(options)
self.setParent(parent)
// Create main page container
self.el = new Element('div.page.'+self.name);
// Create tab for page
if(self.has_tab){
var nav = self.getParent().getBlock('navigation');
self.tab = nav.addTab({
'href': '/'+self.name,
'title': self.title,
'text': self.name.capitalize()
});
}
},
open: function(action, params){
var self = this;
//p('Opening: ' +self.getName() + ', ' + action + ', ' + Object.toQueryString(params));
try {
self[action+'Action'](params);
self.fireEvent('opened');
}
catch (e){
self.errorAction(e);
self.fireEvent('error');
}
},
errorAction: function(e){
p('Error, action not found', e);
},
getName: function(){
return this.name
},
setParent: function(parent){
this.app = parent
},
getParent: function(){
return this.app
},
api: function(){
return this.getParent().getApi()
},
show: function(){
this.el.addClass('active');
},
hide: function(){
this.el.removeClass('active');
},
toElement: function(){
return this.el
}
});
var Page = {}
+8
View File
@@ -0,0 +1,8 @@
Page.Log = new Class({
Extends: PageBase,
name: 'log',
title: 'Show recent logs.'
})
@@ -0,0 +1,8 @@
Page.Manage = new Class({
Extends: PageBase,
name: 'manage',
title: 'Do stuff to your existing movies!'
})
+426
View File
@@ -0,0 +1,426 @@
Page.Settings = new Class({
Extends: PageBase,
name: 'settings',
title: 'Change settings.',
groups: {},
open: function(action, params){
var self = this
//p('open config', action, params)
if(!self.data)
self.getData(self.create.bind(self))
else
self.openTab(action);
},
openTab: function(action){
var self = this;
if(self.current)
self.toggleTab(self.current, true);
self.toggleTab(action)
self.current = action;
},
toggleTab: function(tab, 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);
},
getData: function(onComplete){
var self = this;
if(onComplete)
self.api().request('settings', {
'useSpinner': true,
'spinnerOptions': {
'target': self.el
},
'onComplete': function(json){
self.data = json;
onComplete(json);
}
})
return self.data;
},
getValue: function(section, name){
var self = this;
try {
return self.data.values[section][name] || '';
}
catch(e){
return ''
}
},
create: function(json){
var self = this
self.el.adopt(
self.tabs = new Element('ul.tabs'),
self.containers = new Element('form.uniForm.containers')
);
Object.each(json.sections, 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);
self.groups[section.tab] = {
'tab': tab,
'group': group
}
// 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();
}
});
var OptionBase = new Class({
Implements: [Options, Events],
klass: 'textInput',
focused_class : 'focused',
save_on_change: true,
initialize: function(parent, section, name, options){
var self = this
self.setOptions(options)
self.page = parent;
self.section = section;
self.name = name;
self.createBase();
self.create();
self.createHint();
self.setAdvanced();
// Add focus events
self.input.addEvents({
'change': self.changed.bind(self),
'keyup': self.changed.bind(self),
'focus': function() {
self.el.addClass(self.focused_class);
},
'blur' : function() {
self.el.removeClass(self.focused_class);
}
});
},
/**
* Create the element
*/
createBase: function(){
var self = this
self.el = new Element('div.ctrlHolder')
},
create: function(){},
setAdvanced: function(){
this.el.addClass(this.options.advanced ? 'advanced': '')
},
createHint: function(){
var self = this;
if(self.options.description)
new Element('p.formHint', {
'text': self.options.description
}).inject(self.el);
},
// Element has changed, do something
changed: function(){
var self = this;
if(self.getValue() != self.previous_value){
if(self.save_on_change){
if(self.changed_timer) clearTimeout(self.changed_timer);
self.changed_timer = self.save.delay(300, self);
}
self.fireEvent('change')
}
},
save: function(){
var self = this;
self.api().request('setting.save', {
'data': {
'section': self.section,
'name': self.name,
'value': self.getValue()
},
'useSpinner': true,
'spinnerOptions': {
'target': self.el
},
'onComplete': self.saveCompleted.bind(self)
});
},
saveCompleted: function(json){
var self = this;
var sc = json.success ? 'save_success' : 'save_failed';
self.previous_value = self.getValue();
self.el.addClass(sc);
(function(){
self.el.removeClass(sc);
}).delay(3000, self);
},
setName: function(name){
this.name = name;
},
postName: function(){
var self = this;
return self.section +'['+self.name+']';
},
getValue: function(){
var self = this;
return self.input.get('value');
},
getSettingValue: function(){
var self = this;
return self.page.getValue(self.section, self.name);
},
api: function(){
return this.page.api();
},
inject: function(el, position){
this.el.inject(el, position);
return this.el;
},
toElement: function(){
return this.el;
}
})
var Option = {}
Option.String = new Class({
Extends: OptionBase,
type: 'input',
create: function(){
var self = this
self.el.adopt(
new Element('label', {
'text': self.options.label
}),
self.input = new Element('input', {
'name': self.postName(),
'value': self.getSettingValue()
})
);
}
});
Option.Dropdown = new Class({
Extends: OptionBase,
create: function(){
var self = this
new Element('label', {
'text': self.options.label
}).adopt(
self.input = new Element('select', {
'name': self.postName()
})
).inject(self.el)
Object.each(self.options.values, function(label, value){
new Element('option', {
'text': label,
'value': value
}).inject(self.input)
})
self.input.set('value', self.getSettingValue());
}
});
Option.Checkbox = new Class({
Extends: OptionBase,
type: 'checkbox',
create: function(){
var self = this;
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);
}
});
Option.Bool = new Class({
Extends: Option.Checkbox
});
Option.Int = new Class({
Extends: Option.String
});
Option.Directory = new Class({
Extends: OptionBase,
type: 'span',
browser: '',
save_on_change: false,
create: function(){
var self = this;
self.el.adopt(
new Element('label', {
'text': self.options.label
}),
self.input = new Element('span', {
'text': self.getSettingValue(),
'events': {
'click': self.showBrowser.bind(self),
'outerClick': self.hideBrowser.bind(self)
}
})
);
self.cached = {};
},
showBrowser: function(){
var self = this;
if(!self.browser)
self.browser = new Element('div.directory_list').adopt(
self.dir_list = new Element('ul')
).inject(self.input, 'after')
self.getDirs()
self.browser.show()
},
hideBrowser: function(){
this.browser.hide()
},
fillBrowser: function(json){
var self = this;
var c = self.getCurrentDir();
var v = self.input.get('value');
var add = true
if(!json){
json = self.cached[c];
}
else {
self.cached[c] = json;
}
self.dir_list.empty();
json.dirs.each(function(dir){
if(dir.indexOf(v) != -1){
new Element('li', {
'text': dir
}).inject(self.dir_list)
if(add){
self.input.insertAtCursor(dir.substring(v.length), true);
add = false
}
}
})
},
getDirs: function(){
var self = this;
var c = self.getCurrentDir();
if(self.cached[c]){
self.fillBrowser()
}
else {
self.api().request('directory.list', {
'data': {
'path': c
},
'onComplete': self.fillBrowser.bind(self)
})
}
},
getCurrentDir: function(){
var self = this;
var v = self.input.get('value');
var sep = self.api().getOption('path_sep');
var dirs = v.split(sep);
dirs.pop();
return dirs.join(sep)
},
getValue: function(){
var self = this;
return self.input.get('text');
}
});
+8
View File
@@ -0,0 +1,8 @@
Page.Soon = new Class({
Extends: PageBase,
name: 'soon',
title: 'Which wanted movies are released soon?'
})
+73
View File
@@ -0,0 +1,73 @@
Page.Wanted = new Class({
Extends: PageBase,
name: 'wanted',
title: 'Gimmy gimmy gimmy!',
movies: [],
indexAction: function(param){
var self = this;
self.get()
},
list: function(){
var self = this;
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);
});
},
get: function(status, onComplete){
var self = this
if(self.movies.length == 0)
self.api().request('movie', {
'data': {},
'onComplete': function(json){
self.store(json.movies);
self.list();
}
})
else
self.list()
},
store: function(movies){
var self = this;
self.movies = movies;
}
});
var Movie = new Class({
Extends: BlockBase,
initialize: function(self, options, data){
var self = this;
self.data = data;
self.parent(self, options);
},
create: function(){
var self = this;
self.el = new Element('div.movie', {
'text': self.data.name
});
}
})
-72
View File
@@ -1,72 +0,0 @@
var Page = {}
Page.Movie = new Class({
Extends: PageBase,
name: 'movie',
initialize: function(options){
},
indexAction: function(param){
var self = this
self.getMovies()
},
getMovies: function(){
var self = this
this.api().request('movie', {'status': 'wanted'})
}
})
Page.Manage = new Class({
Extends: PageBase,
name: 'manage',
initialize: function(options){
}
})
Page.Soon = new Class({
Extends: PageBase,
name: 'soon',
initialize: function(options){
}
})
Page.Config = new Class({
Extends: PageBase,
name: 'config',
initialize: function(options){
}
})
Page.Log = new Class({
Extends: PageBase,
name: 'log',
initialize: function(options){
}
})
+13 -19
View File
@@ -96,7 +96,7 @@ form {
}
/*** Navigation ***/
#header {
.header {
background: #f7f7f7;
padding:10px;
margin-bottom: 20px;
@@ -106,22 +106,16 @@ form {
-webkit-box-shadow: 0 0 30px rgba(0,0,0,0.1);
position: fixed;
width: 99%;
z-index: 9999;
}
#header .navigation {
.header .navigation {
width: 960px;
margin: 0 auto;
overflow:hidden;
overflow: hidden;
}
#header .navigation ul {
float: left;
padding: 0;
margin: 0 0 0 6px;
width: 450px;
}
#header .navigation ul li {
.header .navigation li {
color: #8b8b8b;
display: block;
font-size:20px;
@@ -131,31 +125,31 @@ form {
float: left;
}
#header .navigation ul li a {
.header .navigation li a {
display: block;
padding: 15px;
}
#header .navigation ul li:first-child a { padding-left: 10px; }
#header .navigation ul li a.logLink { font-size: 13px; padding: 23px 20px 15px; }
#header .navigation ul li a#showConfig {
.header .navigation li:first-child a { padding-left: 10px; }
.header .navigation li a.logLink { font-size: 13px; padding: 23px 20px 15px; }
.header .navigation li a#showConfig {
background: url('../../media/images/gear.png') no-repeat center;
height: 35px;
width: 10px;
}
#header .navigation ul li span {
.header .navigation li span {
display: block;
margin-top: 5px;
}
#header .navigation ul li.disabled {
.header .navigation li.disabled {
color: #e5e5e5;
}
#header .navigation ul li a:link, #header .navigation ul li a:visited {
.header .navigation li a:link, .header .navigation li a:visited {
color: #2c2c2c;
}
#header .navigation ul li a:hover, #header .navigation ul li a:active {
.header .navigation li a:hover, .header .navigation li a:active {
color: #8b8b8b;
}
+154
View File
@@ -0,0 +1,154 @@
/* ------------------------------------------------------------------------------
Copyright (c) 2010, Dragan Babic
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------ */
/* ############################# GENERALS ################################### */
/* ------------------------------------------------------------------------------ */
.uniForm{ margin: 0; padding: 0; position: relative; z-index: 1; } /* reset stuff */
/* Some generals and more resets */
.uniForm fieldset{ border: none; margin: 0; padding: 0; }
.uniForm fieldset legend{ margin: 0; padding: 0; }
/* This are the main units that contain form elements */
.uniForm .ctrlHolder,
.uniForm .buttonHolder{ margin: 0; padding: 0; clear: both; }
/* Clear all floats */
.uniForm:after,
.uniForm .buttonHolder:after,
.uniForm .ctrlHolder:after,
.uniForm .ctrlHolder .multiField:after,
.uniForm .inlineLabel:after{ content: "."; display: block; height: 0; line-height: 0; font-size: 0; clear: both; min-height: 0; visibility: hidden; }
.uniForm label,
.uniForm button{ cursor: pointer; }
/* ------------------------------------------------------------------------------ */
/* ########################## DEFAULT LAYOUT ################################ */
/* ------------------------------------------------------------------------------ */
/* Styles for form controls where labels are above the input elements */
/* ------------------------------------------------------------------------------ */
.uniForm label,
.uniForm .label{ display: block; float: none; margin: 0 0 .5em 0; padding: 0; line-height: 100%; width: auto; }
/* Float the input elements */
.uniForm .textInput,
.uniForm .fileUpload,
.uniForm .selectInput,
.uniForm select,
.uniForm textarea{ float: left; width: 53%; margin: 0; }
/* Postition the hints */
.uniForm .formHint{ float: right; width: 43%; margin: 0; clear: none; }
/* Position the elements inside combo boxes (multiple inputs/selects/checkboxes/radio buttons per unit) */
.uniForm ul{ float: left; width: 53%; margin: 0; padding: 0; }
.uniForm ul li{ margin: 0 0 .5em 0; list-style: none; }
.uniForm ul li label{ margin: 0; float: none; display: block; overflow: visible; }
/* Alternate layout */
.uniForm ul.alternate li{ float: left; width: 30%; margin-right: 3%; }
.uniForm ul.alternate li label{ float: none; display: block; width: 98%; }
.uniForm ul .textInput,
.uniForm ul .selectInput,
.uniForm ul select,
.uniForm ul.alternate .textInput,
.uniForm ul.alternate .selectInput,
.uniForm ul.alternate select{ width: 98%; margin-top: .5em; display: block; float: none; }
/* Required fields asterisk styling */
.uniForm label em,
.uniForm .label em{ float: left; width: 1em; margin: 0 0 0 -1em; }
/* ------------------------------------------------------------------------------ */
/* ######################### ALTERNATE LAYOUT ############################### */
/* ------------------------------------------------------------------------------ */
/* Styles for form controls where labels are in line with the input elements */
/* Set the class of the parent (preferably to a fieldset) to .inlineLabels */
/* ------------------------------------------------------------------------------ */
.uniForm .inlineLabels label,
.uniForm .inlineLabels .label{ float: left; margin: .3em 2% 0 0; padding: 0; line-height: 1; position: relative; width: 32%; }
/* Float the input elements */
.uniForm .inlineLabels .textInput,
.uniForm .inlineLabels .fileUpload,
.uniForm .inlineLabels .selectInput,
.uniForm .inlineLabels select,
.uniForm .inlineLabels textarea{ float: left; width: 64%; }
/* Postition the hints */
.uniForm .inlineLabels .formHint{ clear: both; float: none; width: auto; margin-left: 34%; position: static; }
/* Position the elements inside combo boxes (multiple inputs/selects/checkboxes/radio buttons per unit) */
.uniForm .inlineLabels ul{ float: left; width: 66%; }
.uniForm .inlineLabels ul li{ margin: .5em 0; }
.uniForm .inlineLabels ul li label{ float: none; display: block; width: 100%; }
/* Alternate layout */
.uniForm .inlineLabels ul.alternate li{ margin-right: 3%; margin-top: .25em; }
.uniForm .inlineLabels ul li label .textInput,
.uniForm .inlineLabels ul li label textarea,
.uniForm .inlineLabels ul li label select{ float: none; display: block; width: 98%; }
/* Required fields asterisk styling */
.uniForm .inlineLabels label em,
.uniForm .inlineLabels .label em{ display: block; float: none; margin: 0; position: absolute; right: 0; }
/* ----------------------------------------------------------------------------- */
/* ########################### Additional Stuff ################################ */
/* ----------------------------------------------------------------------------- */
/* Generals */
.uniForm legend{ color: inherit; }
.uniForm .secondaryAction{ float: left; }
/* .inlineLabel is used for inputs within labels - checkboxes and radio buttons */
.uniForm .inlineLabel input,
.uniForm .inlineLabels .inlineLabel input,
.uniForm .blockLabels .inlineLabel input,
/* class .inlineLabel is depreciated */
.uniForm label input{ float: none; display: inline; margin: 0; padding: 0; border: none; }
.uniForm .buttonHolder .inlineLabel,
.uniForm .buttonHolder label{ float: left; margin: .5em 0 0 0; width: auto; max-width: 60%; text-align: left; }
/* When you don't want to use a label */
.uniForm .inlineLabels .noLabel ul{ margin-left: 34%; /* Match to width of label + gap to field */ }
/* Classes for control of the widths of the fields */
.uniForm .small { width: 30% !important; }
.uniForm .medium{ width: 45% !important; }
.uniForm .large { } /* Large is default and should match the value you set for .textInput, textarea or select */
.uniForm .auto { width: auto !important; }
.uniForm .small,
.uniForm .medium,
.uniForm .auto{ margin-right: 4px; }
/* Columns */
.uniForm .col{ float: left; }
.uniForm .col{ width: 50%; }
@@ -0,0 +1,153 @@
/* ------------------------------------------------------------------------------
UNI-FORM DEFAULT by DRAGAN BABIC (v2) | Wed, 31 Mar 10
------------------------------------------------------------------------------
Copyright (c) 2010, Dragan Babic
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------ */
.uniForm{}
.uniForm legend{ font-weight: bold; font-size: 100%; margin: 0; padding: 1.5em 0; }
.uniForm .ctrlHolder{ padding: 1em; border-bottom: 1px solid #efefef; }
.uniForm .ctrlHolder.focused{ background: #fffcdf; }
.uniForm .inlineLabels .noLabel{}
.uniForm .buttonHolder{ background: #efefef; text-align: right; margin: 1.5em 0 0 0; padding: 1.5em;
/* CSS3 */
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-o-border-radius: 4px;
-khtml-border-radius: 4px;
}
.uniForm .buttonHolder .primaryAction{ padding: 10px 22px; line-height: 1; background: #254a86; border: 1px solid #163362; font-size: 12px; font-weight: bold; color: #fff;
/* CSS3 */
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-o-border-radius: 4px;
-khtml-border-radius: 4px;
box-shadow: 1px 1px 0 #fff;
-webkit-box-shadow: 1px 1px 0 #fff;
-moz-box-shadow: 1px 1px 0 #fff;
text-shadow: -1px -1px 0 rgba(0,0,0,.25);
}
.uniForm .buttonHolder .primaryAction:active{ position: relative; top: 1px; }
.uniForm .secondaryAction { text-align: left; }
.uniForm button.secondaryAction { background: transparent; border: none; color: #777; margin: 1.25em 0 0 0; padding: 0; }
.uniForm .inlineLabels label em,
.uniForm .inlineLabels .label em{ font-style: normal; font-weight: bold; }
.uniForm label small{ font-size: .75em; color: #777; }
.uniForm .textInput,
.uniForm textarea { padding: 4px 2px; border: 1px solid #aaa; background: #fff; }
.uniForm textarea { height: 12em; }
.uniForm select {}
.uniForm .fileUpload {}
.uniForm ul{}
.uniForm li{}
.uniForm ul li label{ font-size: .85em; }
.uniForm .small {}
.uniForm .medium{}
.uniForm .large {} /* Large is default and should match the value you set for .textInput, textarea or select */
.uniForm .auto {}
.uniForm .small,
.uniForm .medium,
.uniForm .auto{}
/* Get rid of the 'glow' effect in WebKit, optional */
.uniForm .ctrlHolder .textInput:focus,
.uniForm .ctrlHolder textarea:focus{ outline: none; }
.uniForm .formHint { font-size: .85em; color: #777; }
.uniForm .inlineLabels .formHint { padding-top: .5em; }
.uniForm .ctrlHolder.focused .formHint{ color: #333; }
/* ----------------------------------------------------------------------------- */
/* ############################### Messages #################################### */
/* ----------------------------------------------------------------------------- */
/* Error message at the top of the form */
.uniForm #errorMsg{ background: #ffdfdf; border: 1px solid #f3afb5; margin: 0 0 1.5em 0; padding: 0 1.5em;
/* CSS3 */
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-o-border-radius: 4px;
-khtml-border-radius: 4px;
}
.uniForm #errorMsg h3{} /* Feel free to use a heading level suitable to your page structure */
.uniForm #errorMsg ol{ margin: 0 0 1.5em 0; padding: 0; }
.uniForm #errorMsg ol li{ margin: 0 0 3px 1.5em; padding: 7px; background: #f6bec1; position: relative; font-size: .85em;
/* CSS3 */
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-o-border-radius: 4px;
-khtml-border-radius: 4px;
}
.uniForm .ctrlHolder.error,
.uniForm .ctrlHolder.focused.error{ background: #ffdfdf; border: 1px solid #f3afb5;
/* CSS3 */
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-o-border-radius: 4px;
-khtml-border-radius: 4px;
}
.uniForm .ctrlHolder.error input.error,
.uniForm .ctrlHolder.error select.error,
.uniForm .ctrlHolder.error textarea.error{ color: #af4c4c; margin: 0 0 6px 0; padding: 4px; }
/* Success messages at the top of the form */
.uniForm #okMsg{ background: #c8ffbf; border: 1px solid #a2ef95; margin: 0 0 1.5em 0; padding: 0 1.5em; text-align: center;
/* CSS3 */
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-o-border-radius: 4px;
-khtml-border-radius: 4px;
}
.uniForm #OKMsg p{ margin: 0; }
/* ----------------------------------------------------------------------------- */
/* ############################### Columns ##################################### */
/* ----------------------------------------------------------------------------- */
.uniForm .col{}
.uniForm .col.first{}
.uniForm .col.last{}
.uniForm .col{ margin-bottom: 1.5em; }
/* Use .first and .last classes to control the layout/spacing of your columns */
.uniForm .col.first{ width: 49%; float: left; clear: none; }
.uniForm .col.last { width: 49%; float: right; clear: none; margin-right: 0; }
+19 -2
View File
@@ -2,18 +2,35 @@
<html>
<head>
<link rel="stylesheet" href="{{ url_for('.static', filename='style/main.css') }}">
<link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.generic.css') }}">
<link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.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_more.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/pages.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/footer.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/block/search.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/page.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/page/wanted.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/page/settings.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/page/log.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/page/soon.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/page/manage.js') }}"></script>
<link href="{{ url_for('.static', filename='images/favicon.ico') }}" rel="icon" type="image/x-icon" />
<script type="text/javascript">
window.addEvent('domready', function() {
var cp = new CouchPotato({
'base_url': '{{ request.path }}',
'api_url': '{{ url_for('api.index') }}'
'api': {
'url': '{{ url_for('api.index') }}',
'path_sep': '{{ sep }}'
}
});
})
</script>