Browse Source

Movie add

JSONP
pull/1/merge
Ruud 14 years ago
parent
commit
e39de40d2d
  1. 11
      couchpotato/__init__.py
  2. 32
      couchpotato/api/__init__.py
  3. 18
      couchpotato/core/event.py
  4. 37
      couchpotato/core/helpers.py
  5. 1
      couchpotato/core/helpers/__init__.py
  6. 26
      couchpotato/core/helpers/encoding.py
  7. 22
      couchpotato/core/helpers/request.py
  8. 33
      couchpotato/core/helpers/rss.py
  9. 7
      couchpotato/core/plugins/file_browser/main.py
  10. 28
      couchpotato/core/plugins/movie_add/main.py
  11. 6
      couchpotato/core/plugins/movie_list/__init__.py
  12. 41
      couchpotato/core/plugins/movie_list/main.py
  13. 45
      couchpotato/core/providers/base.py
  14. 67
      couchpotato/core/providers/tmdb/main.py
  15. 8
      couchpotato/core/settings/__init__.py
  16. 98
      couchpotato/core/settings/model.py
  17. 9
      couchpotato/environment.py
  18. 113
      couchpotato/static/scripts/block/search.js
  19. 7
      couchpotato/static/scripts/couchpotato.js
  20. 4555
      couchpotato/static/scripts/mootools.js
  21. 667
      couchpotato/static/scripts/mootools_more.js
  22. 16
      couchpotato/static/scripts/page/settings.js
  23. 28
      couchpotato/static/scripts/uniform.js
  24. 7
      couchpotato/templates/_desktop.html

11
couchpotato/__init__.py

@ -12,19 +12,21 @@ from sqlalchemy.orm.session import sessionmaker
from werkzeug.utils import redirect from werkzeug.utils import redirect
import os import os
app = Flask(__name__)
log = CPLog(__name__) log = CPLog(__name__)
app = Flask(__name__)
web = Module(__name__, 'web') web = Module(__name__, 'web')
def get_session(engine): def get_session(engine = None):
engine = engine if engine else get_engine() engine = engine if engine else get_engine()
return scoped_session(sessionmaker(transactional = True, bind = engine)) return scoped_session(sessionmaker(bind = engine))
def get_engine(): def get_engine():
return create_engine(Env.get('db_path'), echo = False) return create_engine(Env.get('db_path'), echo = False)
""" Web view """
@web.route('/') @web.route('/')
@requires_auth @requires_auth
def index(): def index():
@ -33,5 +35,6 @@ def index():
@app.errorhandler(404) @app.errorhandler(404)
def page_not_found(error): def page_not_found(error):
index_url = url_for('web.index') index_url = url_for('web.index')
url = request.path[len(index_url):] url = getattr(request, 'path')[len(index_url):]
return redirect(index_url + '#' + url) return redirect(index_url + '#' + url)

32
couchpotato/api/__init__.py

@ -1,30 +1,20 @@
from couchpotato.core.settings.model import Resource from couchpotato.core.helpers.request import jsonified
from flask import Module from flask import Module
from flask.helpers import jsonify
api = Module(__name__) api = Module(__name__)
def addApiView(route, func): def addApiView(route, func):
api.add_url_rule(route + '/', route, func) api.add_url_rule(route + '/', endpoint = route if route else 'index', view_func = func)
""" Api view """
@api.route('')
def index(): def index():
return jsonify({'test': 'bla'}) from couchpotato import app
routes = []
for route, x in sorted(app.view_functions.iteritems()):
if route[0:4] == 'api.':
routes += [route[4:]]
return jsonified({'routes': routes})
@api.route('movie/') addApiView('', index)
def movie():
return jsonify({
'success': True,
'movies': [
{
'name': 'Movie 1',
'description': 'Description 1',
},
{
'name': 'Movie 2',
'description': 'Description 2',
}
]
})

18
couchpotato/core/event.py

@ -1,5 +1,6 @@
from couchpotato.core.logger import CPLog
from axl.axel import Event from axl.axel import Event
from couchpotato.core.logger import CPLog
import traceback
log = CPLog(__name__) log = CPLog(__name__)
events = {} events = {}
@ -21,7 +22,17 @@ def fireEvent(name, *args, **kwargs):
try: try:
e = events[name] e = events[name]
e.asynchronous = False e.asynchronous = False
return e(*args, **kwargs) result = e(*args, **kwargs)
results = []
for r in result:
if r[0] == True:
results.append(r[1])
else:
etype, value, tb = r[1]
log.debug(''.join(traceback.format_exception(etype, value, tb)))
return results
except Exception, e: except Exception, e:
log.debug(e) log.debug(e)
@ -29,7 +40,8 @@ def fireEventAsync(name, *args, **kwargs):
try: try:
e = events[name] e = events[name]
e.asynchronous = True e.asynchronous = True
return e(*args, **kwargs) e(*args, **kwargs)
return True
except Exception, e: except Exception, e:
log.debug(e) log.debug(e)

37
couchpotato/core/helpers.py

@ -1,37 +0,0 @@
def latinToAscii(unicrap):
xlate = {0xc0:'A', 0xc1:'A', 0xc2:'A', 0xc3:'A', 0xc4:'A', 0xc5:'A',
0xc6:'Ae', 0xc7:'C',
0xc8:'E', 0xc9:'E', 0xca:'E', 0xcb:'E', 0x86:'e',
0xcc:'I', 0xcd:'I', 0xce:'I', 0xcf:'I',
0xd0:'Th', 0xd1:'N',
0xd2:'O', 0xd3:'O', 0xd4:'O', 0xd5:'O', 0xd6:'O', 0xd8:'O',
0xd9:'U', 0xda:'U', 0xdb:'U', 0xdc:'U',
0xdd:'Y', 0xde:'th', 0xdf:'ss',
0xe0:'a', 0xe1:'a', 0xe2:'a', 0xe3:'a', 0xe4:'a', 0xe5:'a',
0xe6:'ae', 0xe7:'c',
0xe8:'e', 0xe9:'e', 0xea:'e', 0xeb:'e',
0xec:'i', 0xed:'i', 0xee:'i', 0xef:'i',
0xf0:'th', 0xf1:'n',
0xf2:'o', 0xf3:'o', 0xf4:'o', 0xf5:'o', 0xf6:'o', 0xf8:'o',
0xf9:'u', 0xfa:'u', 0xfb:'u', 0xfc:'u',
0xfd:'y', 0xfe:'th', 0xff:'y',
0xa1:'!', 0xa2:'{cent}', 0xa3:'{pound}', 0xa4:'{currency}',
0xa5:'{yen}', 0xa6:'|', 0xa7:'{section}', 0xa8:'{umlaut}',
0xa9:'{C}', 0xaa:'{^a}', 0xab:'<<', 0xac:'{not}',
0xad:'-', 0xae:'{R}', 0xaf:'_', 0xb0:'{degrees}',
0xb1:'{+/-}', 0xb2:'{^2}', 0xb3:'{^3}', 0xb4:"'",
0xb5:'{micro}', 0xb6:'{paragraph}', 0xb7:'*', 0xb8:'{cedilla}',
0xb9:'{^1}', 0xba:'{^o}', 0xbb:'>>',
0xbc:'{1/4}', 0xbd:'{1/2}', 0xbe:'{3/4}', 0xbf:'?',
0xd7:'*', 0xf7:'/'
}
r = ''
for i in unicrap:
if xlate.has_key(ord(i)):
r += xlate[ord(i)]
elif ord(i) >= 0x80:
pass
else:
r += str(i)
return r

1
couchpotato/core/helpers/__init__.py

@ -0,0 +1 @@

26
couchpotato/core/helpers/encoding.py

@ -0,0 +1,26 @@
from couchpotato.core.logger import CPLog
from string import ascii_letters, digits
import re
import unicodedata
log = CPLog(__name__)
def toSafeString(original):
valid_chars = "-_.() %s%s" % (ascii_letters, digits)
cleanedFilename = unicodedata.normalize('NFKD', toUnicode(original)).encode('ASCII', 'ignore')
return ''.join(c for c in cleanedFilename if c in valid_chars)
def simplifyString(original):
string = toSafeString(original)
split = re.split('\W+', string.lower())
return toUnicode(' '.join(split))
def toUnicode(original, *args):
try:
if type(original) is unicode:
return original
else:
return unicode(original, *args)
except UnicodeDecodeError:
ascii_text = str(original).encode('string_escape')
return unicode(ascii_text)

22
couchpotato/core/helpers/request.py

@ -0,0 +1,22 @@
from flask.globals import current_app
from flask.helpers import json, jsonify
import flask
def getParams():
return getattr(flask.request, 'args')
def getParam(attr, default = None):
return getattr(flask.request, 'args').get(attr, default)
def padded_jsonify(callback, *args, **kwargs):
content = str(callback) + '(' + json.dumps(dict(*args, **kwargs)) + ')'
return current_app.response_class(content, mimetype = 'text/javascript')
def jsonified(*args, **kwargs):
from couchpotato.environment import Env
callback = getParam('json_callback', None)
if callback:
return padded_jsonify(callback, *args, **kwargs)
else:
return jsonify(*args, **kwargs)

33
couchpotato/core/helpers/rss.py

@ -0,0 +1,33 @@
from couchpotato.core.logger import CPLog
import xml.etree.ElementTree as XMLTree
log = CPLog(__name__)
class RSS():
def gettextelements(self, xml, path):
''' Find elements and return tree'''
textelements = []
try:
elements = xml.findall(path)
except:
return
for element in elements:
textelements.append(element.text)
return textelements
def gettextelement(self, xml, path):
''' Find element and return text'''
try:
return xml.find(path).text
except:
return
def getItems(self, data, path = 'channel/item'):
try:
return XMLTree.parse(data).findall(path)
except Exception, e:
log.error('Error parsing RSS. %s' % e)
return []

7
couchpotato/core/plugins/file_browser/main.py

@ -1,6 +1,5 @@
from couchpotato.api import addApiView from couchpotato.api import addApiView
from couchpotato.environment import Env from couchpotato.core.helpers.request import getParam, jsonified
from flask.helpers import jsonify
import os import os
import string import string
@ -45,12 +44,12 @@ class FileBrowser():
def view(self): def view(self):
try: try:
fb = FileBrowser(Env.getParam('path', '/')) fb = FileBrowser(getParam('path', '/'))
dirs = fb.getDirectories() dirs = fb.getDirectories()
except: except:
dirs = [] dirs = []
return jsonify({ return jsonified({
'empty': len(dirs) == 0, 'empty': len(dirs) == 0,
'dirs': dirs, 'dirs': dirs,
}) })

28
couchpotato/core/plugins/movie_add/main.py

@ -1,30 +1,36 @@
from couchpotato.api import addApiView from couchpotato.api import addApiView
from couchpotato.core.event import getEvent, fireEvent from couchpotato.core.event import fireEvent
from couchpotato.core.helpers.request import getParams, jsonified
from couchpotato.core.plugins.base import Plugin from couchpotato.core.plugins.base import Plugin
from couchpotato.environment import Env
from flask.helpers import jsonify
class MovieAdd(Plugin): class MovieAdd(Plugin):
def __init__(self): def __init__(self):
addApiView('movie.add.search', self.search) addApiView('movie.add.search', self.search)
addApiView('movie.add.select', self.select)
def search(self): def search(self):
a = Env.getParams() a = getParams()
print fireEvent('provider.movie.search', q = a.get('q')) results = fireEvent('provider.movie.search', q = a.get('q'))
movies = [ # Combine movie results
{'id': 1, 'name': 'test'} movies = []
] for r in results:
movies += r
return jsonify({ return jsonified({
'success': True, 'success': True,
'empty': len(movies) == 0, 'empty': len(movies) == 0,
'movies': movies, 'movies': movies,
}) })
def select(self): def select(self):
pass
a = getParams()
return jsonified({
'success': True,
'added': True,
})

6
couchpotato/core/plugins/movie_list/__init__.py

@ -0,0 +1,6 @@
from couchpotato.core.plugins.movie_list.main import MovieList
def start():
return MovieList()
config = []

41
couchpotato/core/plugins/movie_list/main.py

@ -0,0 +1,41 @@
from couchpotato import get_session
from couchpotato.api import addApiView
from couchpotato.core.helpers.request import getParams, jsonified
from couchpotato.core.plugins.base import Plugin
from couchpotato.core.settings.model import Movie, Release
class MovieList(Plugin):
def __init__(self):
addApiView('movie.list', self.list)
def list(self):
a = getParams()
results = get_session().query(Movie).filter(
Movie.releases.any(
Release.status.has(identifier = 'wanted')
)
).all()
movies = []
for movie in results:
temp = {
'id': movie.id,
'name': movie.id,
'releases': [],
}
for release in movie.releases:
temp['releases'].append({
'status': release.status.label,
'quality': release.quality.label
})
movies.append(temp)
return jsonified({
'success': True,
'empty': len(movies) == 0,
'movies': movies,
})

45
couchpotato/core/providers/base.py

@ -1,9 +1,4 @@
from couchpotato.core.helpers import latinToAscii
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
from string import ascii_letters, digits
import re
import unicodedata
import xml.etree.ElementTree as XMLTree
log = CPLog(__name__) log = CPLog(__name__)
@ -15,43 +10,3 @@ class Provider():
def __init__(self): def __init__(self):
pass pass
def toSaveString(self, string):
string = latinToAscii(string)
string = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn'))
safe_chars = ascii_letters + digits + '_ -.,\':!?'
r = ''.join([char if char in safe_chars else ' ' for char in string])
return re.sub('\s+' , ' ', r)
def toSearchString(self, string):
string = latinToAscii(string)
string = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn'))
safe_chars = ascii_letters + digits + ' \''
r = ''.join([char if char in safe_chars else ' ' for char in string])
return re.sub('\s+' , ' ', r).replace('\'s', 's').replace('\'', ' ')
def gettextelements(self, xml, path):
''' Find elements and return tree'''
textelements = []
try:
elements = xml.findall(path)
except:
return
for element in elements:
textelements.append(element.text)
return textelements
def gettextelement(self, xml, path):
''' Find element and return text'''
try:
return xml.find(path).text
except:
return
def getItems(self, data, path = 'channel/item'):
try:
return XMLTree.parse(data).findall(path)
except Exception, e:
log.error('Error parsing RSS. %s' % e)
return []

67
couchpotato/core/providers/tmdb/main.py

@ -1,10 +1,12 @@
from __future__ import with_statement from __future__ import with_statement
from couchpotato.core.event import addEvent from couchpotato.core.event import addEvent
from couchpotato.core.helpers.encoding import simplifyString, toUnicode
from couchpotato.core.logger import CPLog 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 urllib2
import simplejson as json
log = CPLog(__name__) log = CPLog(__name__)
@ -21,7 +23,7 @@ class TMDB(Provider):
def conf(self, attr): def conf(self, attr):
return Env.setting(attr, 'TheMovieDB') return Env.setting(attr, 'TheMovieDB')
def search(self, q, limit = 8, alternative = True): def search(self, q, limit = 12, alternative = True):
''' Find movie by name ''' ''' Find movie by name '''
if self.isDisabled(): if self.isDisabled():
@ -29,58 +31,61 @@ class TMDB(Provider):
log.debug('TheMovieDB - Searching for movie: %s' % q) log.debug('TheMovieDB - Searching for movie: %s' % q)
url = "%s/%s/%s/xml/%s/%s" % (self.apiUrl, 'Movie.search', 'en', self.conf('api_key'), quote_plus(self.toSearchString(q))) url = "%s/%s/%s/json/%s/%s" % (self.apiUrl, 'Movie.search', 'en', self.conf('api_key'), quote_plus(simplifyString(q)))
log.info('Searching: %s' % url) log.info('Searching: %s' % url)
data = urllib2.urlopen(url) data = urllib2.urlopen(url)
jsn = json.load(data)
return self.parseXML(data, limit, alternative = alternative) return self.parse(jsn, limit, alternative = alternative)
def parseXML(self, data, limit, alternative = True): def parse(self, data, limit, alternative = True):
if data: if data:
log.debug('TheMovieDB - Parsing RSS') log.debug('TheMovieDB - Parsing RSS')
try: try:
xml = self.getItems(data, 'movies/movie')
results = [] results = []
nr = 0 nr = 0
for movie in xml: for movie in data:
id = int(self.gettextelement(movie, "id"))
year = movie['released'][:4]
name = self.gettextelement(movie, "name") # Poster url
imdb = self.gettextelement(movie, "imdb_id") poster = ''
year = str(self.gettextelement(movie, "released"))[:4] for p in movie['posters']:
p = p['image']
if(p['size'] == 'thumb'):
poster = p['url']
break
# 1900 is the same as None # 1900 is the same as None
if year == '1900': if year == '1900':
year = 'None' year = None
results.append({ movie_data = {
'id': id, 'id': int(movie['id']),
'name': self.toSaveString(name), 'name': toUnicode(movie['name']),
'imdb': imdb, 'poster': poster,
'year': year 'imdb': movie['imdb_id'],
}) 'year': year,
'tagline': 'This is the tagline of the movie',
alternativeName = self.gettextelement(movie, "alternative_name") }
results.append(movie_data)
alternativeName = movie['alternative_name']
if alternativeName and alternative: if alternativeName and alternative:
if alternativeName.lower() != name.lower() and alternativeName.lower() != 'none' and alternativeName != None: if alternativeName.lower() != movie['name'].lower() and alternativeName.lower() != 'none' and alternativeName != None:
results.append({ movie_data['name'] = toUnicode(alternativeName)
'id': id, results.append(movie_data)
'name': self.toSaveString(alternativeName),
'imdb': imdb,
'year': year
})
nr += 1 nr += 1
if nr == limit: if nr == limit:
break break
#log.info('TheMovieDB - Found: %s' % results) log.info('TheMovieDB - Found: %s' % [result['name'] + u' (' + str(result['year']) + ')' for result in results])
return results return results
except SyntaxError: except SyntaxError, e:
log.error('TheMovieDB - Failed to parse XML response from TheMovieDb') log.error('TheMovieDB - Failed to parse XML response from TheMovieDb: %s' % e)
return False return False

8
couchpotato/core/settings/__init__.py

@ -1,7 +1,7 @@
from __future__ import with_statement from __future__ import with_statement
from couchpotato.api import addApiView from couchpotato.api import addApiView
from couchpotato.core.event import addEvent from couchpotato.core.event import addEvent
from flask.helpers import jsonify from couchpotato.core.helpers.request import getParams, jsonified
import ConfigParser import ConfigParser
import os.path import os.path
import time import time
@ -107,14 +107,14 @@ class Settings():
def view(self): def view(self):
return jsonify({ return jsonified({
'options': self.getOptions(), 'options': self.getOptions(),
'values': self.getValues() 'values': self.getValues()
}) })
def saveView(self): def saveView(self):
a = Env.getParams() a = getParams()
section = a.get('section') section = a.get('section')
option = a.get('name') option = a.get('name')
@ -123,6 +123,6 @@ class Settings():
self.set(option, section, value) self.set(option, section, value)
self.save() self.save()
return jsonify({ return jsonified({
'success': True, 'success': True,
}) })

98
couchpotato/core/settings/model.py

@ -2,7 +2,7 @@ from elixir.entity import Entity
from elixir.fields import Field from elixir.fields import Field
from elixir.options import options_defaults from elixir.options import options_defaults
from elixir.relationships import OneToMany, ManyToOne from elixir.relationships import OneToMany, ManyToOne
from sqlalchemy.types import Integer, String, Unicode from sqlalchemy.types import Integer, String, Unicode, UnicodeText, Boolean
options_defaults["shortnames"] = True options_defaults["shortnames"] = True
@ -13,43 +13,111 @@ options_defaults["shortnames"] = True
# http://elixir.ematia.de/trac/wiki/Recipes/MultipleDatabasesOneMetadata # http://elixir.ematia.de/trac/wiki/Recipes/MultipleDatabasesOneMetadata
__session__ = None __session__ = None
class Resource(Entity):
"""Represents a resource of movies. class Movie(Entity):
This resources can be online or offline.""" """Movie Resource a movie could have multiple releases
name = Field(Unicode(255)) The files belonging to the movie object are global for the whole movie
path = Field(Unicode(255)) such as trailers, nfo, thumbnails"""
mooli_id = Field(Integer)
profile = ManyToOne('Profile')
releases = OneToMany('Release') releases = OneToMany('Release')
files = OneToMany('File')
class Release(Entity): class Release(Entity):
"""Logically groups all files that belong to a certain release, such as """Logically groups all files that belong to a certain release, such as
parts of a movie, subtitles, nfo, trailers etc.""" parts of a movie, subtitles."""
movie = ManyToOne('Movie')
status = ManyToOne('Status')
quality = ManyToOne('Quality')
files = OneToMany('File') files = OneToMany('File')
mooli_id = Field(Integer) history = OneToMany('History')
resource = ManyToOne('Resource')
class Status(Entity):
"""The status of a release, such as Downloaded, Deleted, Wanted etc"""
identifier = Field(String(20), unique = True)
label = Field(String(20))
releases = OneToMany('Release')
class Quality(Entity):
"""Quality name of a release, DVD, 720P, DVD-Rip etc"""
identifier = Field(String(20), unique = True)
label = Field(String(20))
releases = OneToMany('Release')
profile_types = ManyToOne('ProfileType')
class Profile(Entity):
""""""
identifier = Field(String(20), unique = True)
label = Field(Unicode(50))
order = Field(Integer)
wait_for = Field(Integer)
movie = OneToMany('Movie')
profile_type = OneToMany('ProfileType')
class ProfileType(Entity):
""""""
order = Field(Integer)
mark_completed = Field(Boolean)
wait_for = Field(Integer)
type = OneToMany('Quality')
profile = ManyToOne('Profile')
class File(Entity): class File(Entity):
"""File that belongs to a release.""" """File that belongs to a release."""
history = OneToMany('RenameHistory')
path = Field(Unicode(255), nullable = False, unique = True) path = Field(Unicode(255), nullable = False, unique = True)
# Subtitles can have multiple parts, too
part = Field(Integer) part = Field(Integer)
history = OneToMany('RenameHistory')
movie = ManyToOne('Movie')
release = ManyToOne('Release') release = ManyToOne('Release')
# Let's remember the size so we know about offline media.
size = Field(Integer, nullable = False)
type = ManyToOne('FileType') type = ManyToOne('FileType')
properties = OneToMany('FileProperty')
class FileType(Entity): class FileType(Entity):
"""Types could be trailer, subtitle, movie, partial movie etc.""" """Types could be trailer, subtitle, movie, partial movie etc."""
identifier = Field(String(20), unique = True) identifier = Field(String(20), unique = True)
name = Field(Unicode(255), nullable = False) name = Field(Unicode(50), nullable = False)
files = OneToMany('File') files = OneToMany('File')
class FileProperty(Entity):
"""Properties that can be bound to a file for off-line usage"""
identifier = Field(String(20))
value = Field(Unicode(255), nullable = False)
file = ManyToOne('File')
class History(Entity):
"""History of actions that are connected to a certain release,
such as, renamed to, downloaded, deleted, download subtitles etc"""
message = Field(UnicodeText())
release = ManyToOne('Release')
class RenameHistory(Entity): class RenameHistory(Entity):
"""Remembers from where to where files have been moved.""" """Remembers from where to where files have been moved."""
file = ManyToOne('File')
old = Field(String(255)) old = Field(String(255))
new = Field(String(255)) new = Field(String(255))
file = ManyToOne('File')

9
couchpotato/environment.py

@ -1,6 +1,5 @@
from couchpotato.core.loader import Loader from couchpotato.core.loader import Loader
from couchpotato.core.settings import Settings from couchpotato.core.settings import Settings
import flask
class Env: class Env:
@ -42,11 +41,3 @@ class Env:
s = Env.get('settings') s = Env.get('settings')
s.set(section, attr, value) s.set(section, attr, value)
return s return s
@staticmethod
def getParams():
return getattr(flask.request, 'args')
@staticmethod
def getParam(attr, default = None):
return getattr(flask.request, 'args').get(attr, default)

113
couchpotato/static/scripts/block/search.js

@ -2,40 +2,131 @@ Block.Search = new Class({
Extends: BlockBase, Extends: BlockBase,
cache: {},
create: function(){ create: function(){
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', { self.input = new Element('input', {
'events': { 'events': {
'keyup': self.autocomplete.bind(self) 'keyup': self.keyup.bind(self)
} }
}) }),
self.results = new Element('div.results')
); );
// Debug
self.input.set('value', 'iron man');
self.autocomplete(0)
}, },
autocomplete: function(){ keyup: function(e){
var self = this;
if(['up', 'down'].indexOf(e.key) > -1){
p('select item')
}
else if(self.q() != self.last_q) {
self.autocomplete()
}
},
autocomplete: function(delay){
var self = this; var self = this;
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer) if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
self.autocomplete_timer = self.list.delay(300, self) self.autocomplete_timer = self.list.delay((delay || 300), self)
}, },
list: function(){ list: function(){
var self = this; var self = this;
if(self.api_request) self.api_request.cancel(); if(self.api_request) self.api_request.cancel();
self.api_request = self.api().request('movie.add.search', {
'data': { var q = self.q();
'q': self.input.get('value') var cache = self.cache[q];
},
'onComplete': self.fill.bind(self) if(!cache)
}) self.api_request = self.api().request('movie.add.search', {
'data': {
'q': q
},
'onComplete': self.fill.bind(self, q)
})
else
self.fill(q, cache)
self.last_q = q;
}, },
fill: function(){ fill: function(q, json){
var self = this;
self.cache[q] = json
self.movies = []
self.results.empty()
Object.each(json.movies, function(movie){
var m = new Block.Search.Item(movie);
$(m).inject(self.results)
self.movies.include(m)
});
},
q: function(){
return this.input.get('value').trim();
} }
}); });
Block.Search.Item = new Class({
initialize: function(info){
var self = this;
self.info = info;
self.create();
},
create: function(){
var self = this;
self.el = new Element('div.movie').adopt(
self.name = new Element('h2', {
'text': self.info.name
}),
self.tagline = new Element('span', {
'text': self.info.tagline
}),
self.year = self.info.year ? new Element('span', {
'text': self.info.year
}) : null,
self.director = self.info.director ? new Element('span', {
'text': 'Director:' + self.info.director
}) : null,
self.starring = self.info.actors ? new Element('span', {
'text': 'Starring:'
}) : null
)
if(self.info.actors){
Object.each(self.info.actors, function(actor){
new Element('span', {
'text': actor.name
}).inject(self.starring)
})
}
},
toElement: function(){
return this.el
}
})

7
couchpotato/static/scripts/couchpotato.js

@ -100,16 +100,15 @@ var Api = new Class({
var self = this var self = this
self.options = options; self.options = options;
self.req = new Request.JSON({
'method': 'get'
})
}, },
request: function(type, options){ request: function(type, options){
var self = this; var self = this;
return new Request.JSON(Object.merge({ var r_type = self.options.is_remote ? 'JSONP' : 'JSON';
return new Request[r_type](Object.merge({
'callbackKey': 'json_callback',
'method': 'get', 'method': 'get',
'url': self.createUrl(type), 'url': self.createUrl(type),
}, options)).send() }, options)).send()

4555
couchpotato/static/scripts/mootools.js

File diff suppressed because it is too large

667
couchpotato/static/scripts/mootools_more.js

@ -1,609 +1,70 @@
// MooTools: the javascript framework. // MooTools: the javascript framework.
// Load this file's selection again by visiting: http://mootools.net/more/4da9e3082bf0d4194b76d7e5b3874113 // Load this file's selection again by visiting: http://mootools.net/more/9c0d9e00884b8329757da16ea8f1f4e1
// Or build this file again with packager using: packager build More/Element.Forms More/Element.Delegation More/Element.Shortcuts // Or build this file again with packager using: packager build More/Element.Forms More/Element.Delegation More/Element.Shortcuts More/Request.JSONP
/* /*
--- ---
copyrights:
- [MooTools](http://mootools.net)
script: String.Extras.js licenses:
- [MIT License](http://mootools.net/license.txt)
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,
'&raquo;': /[\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);
}
});
})();
/*
---
script: More.js
name: More
description: MooTools More
license: MIT-style license
authors:
- Guillermo Rauch
- Thomas Aylott
- Scott Kyle
- Arian Stolwijk
- Tim Wienk
- Christoph Pojer
- Aaron Newton
requires:
- Core/MooTools
provides: [MooTools.More]
...
*/
MooTools.More = {
'version': '1.3.0.1',
'build': '6dce99bed2792dffcbbbb4ddc15a1fb9a41994b5'
};
/*
---
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]
... ...
*/ */
MooTools.More={version:"1.3.1.1",build:"0292a3af1eea242b817fecf9daa127417d10d4ce"};(function(){var c={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},b={" ":/[\xa0\u2002\u2003\u2009]/g,"*":/[\xb7]/g,"'":/[\u2018\u2019]/g,'"':/[\u201c\u201d]/g,"...":/[\u2026]/g,"-":/[\u2013]/g,"&raquo;":/[\uFFFD]/g};
Element.implement({ var a=function(f,h){var e=f,g;for(g in h){e=e.replace(h[g],g);}return e;};var d=function(e,g){e=e||"";var h=g?"<"+e+"(?!\\w)[^>]*>([\\s\\S]*?)</"+e+"(?!\\w)>":"</?"+e+"([^>]+)?>",f=new RegExp(h,"gi");
return f;};String.implement({standardize:function(){return a(this,c);},repeat:function(e){return new Array(e+1).join(this);},pad:function(e,h,g){if(this.length>=e){return this;
tidy: function(){ }var f=(h==null?" ":""+h).repeat(e-this.length).substr(0,e-this.length);if(!g||g=="right"){return this+f;}if(g=="left"){return f+this;}return f.substr(0,(f.length/2).floor())+this+f.substr(0,(f.length/2).ceil());
this.set('value', this.get('value').tidy()); },getTags:function(e,f){return this.match(d(e,f))||[];},stripTags:function(e,f){return this.replace(d(e,f),"");},tidy:function(){return a(this,b);},truncate:function(e,f,i){var h=this;
}, if(f==null&&arguments.length==1){f="…";}if(h.length>e){h=h.substring(0,e);if(i){var g=h.lastIndexOf(i);if(g!=-1){h=h.substr(0,g);}}if(f){h+=f;}}return h;
}});}).call(this);Element.implement({tidy:function(){this.set("value",this.get("value").tidy());},getTextInRange:function(b,a){return this.get("value").substring(b,a);
getTextInRange: function(start, end){ },getSelectedText:function(){if(this.setSelectionRange){return this.getTextInRange(this.getSelectionStart(),this.getSelectionEnd());}return document.selection.createRange().text;
return this.get('value').substring(start, end); },getSelectedRange:function(){if(this.selectionStart!=null){return{start:this.selectionStart,end:this.selectionEnd};}var e={start:0,end:0};var a=this.getDocument().selection.createRange();
}, if(!a||a.parentElement()!=this){return e;}var c=a.duplicate();if(this.type=="text"){e.start=0-c.moveStart("character",-100000);e.end=e.start+a.text.length;
}else{var b=this.get("value");var d=b.length;c.moveToElementText(this);c.setEndPoint("StartToEnd",a);if(c.text.length){d-=b.match(/[\n\r]*$/)[0].length;
getSelectedText: function(){ }e.end=d-c.text.length;c.setEndPoint("StartToStart",a);e.start=d-c.text.length;}return e;},getSelectionStart:function(){return this.getSelectedRange().start;
if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd()); },getSelectionEnd:function(){return this.getSelectedRange().end;},setCaretPosition:function(a){if(a=="end"){a=this.get("value").length;}this.selectRange(a,a);
return document.selection.createRange().text; return this;},getCaretPosition:function(){return this.getSelectedRange().start;},selectRange:function(e,a){if(this.setSelectionRange){this.focus();this.setSelectionRange(e,a);
}, }else{var c=this.get("value");var d=c.substr(e,a-e).replace(/\r/g,"").length;e=c.substr(0,e).replace(/\r/g,"").length;var b=this.createTextRange();b.collapse(true);
b.moveEnd("character",e+d);b.moveStart("character",e);b.select();}return this;},insertAtCursor:function(b,a){var d=this.getSelectedRange();var c=this.get("value");
getSelectedRange: function(){ this.set("value",c.substring(0,d.start)+b+c.substring(d.end,c.length));if(a!==false){this.selectRange(d.start,d.start+b.length);}else{this.setCaretPosition(d.start+b.length);
if (this.selectionStart != null){ }return this;},insertAroundCursor:function(b,a){b=Object.append({before:"",defaultMiddle:"",after:""},b);var c=this.getSelectedText()||b.defaultMiddle;
return { var g=this.getSelectedRange();var f=this.get("value");if(g.start==g.end){this.set("value",f.substring(0,g.start)+b.before+c+b.after+f.substring(g.end,f.length));
start: this.selectionStart, this.selectRange(g.start+b.before.length,g.end+b.before.length+c.length);}else{var d=f.substring(g.start,g.end);this.set("value",f.substring(0,g.start)+b.before+d+b.after+f.substring(g.end,f.length));
end: this.selectionEnd var e=g.start+b.before.length;if(a!==false){this.selectRange(e,e+d.length);}else{this.setCaretPosition(e+f.length);}}return this;}});Events.Pseudos=function(g,c,e){var b="monitorEvents:";
}; var a=function(h){return{store:h.store?function(i,j){h.store(b+i,j);}:function(i,j){(h.$monitorEvents||(h.$monitorEvents={}))[i]=j;},retrieve:h.retrieve?function(i,j){return h.retrieve(b+i,j);
} }:function(i,j){if(!h.$monitorEvents){return j;}return h.$monitorEvents[i]||j;}};};var f=function(j){if(j.indexOf(":")==-1||!g){return null;}var i=Slick.parse(j).expressions[0][0],m=i.pseudos,h=m.length,k=[];
while(h--){if(g[m[h].key]){k.push({event:i.tag,value:m[h].value,pseudo:m[h].key,original:j});}}return k.length?k:null;};var d=function(h){return Object.merge.apply(this,h.map(function(i){return g[i.pseudo].options||{};
var pos = { }));};return{addEvent:function(m,p,j){var n=f(m);if(!n){return c.call(this,m,p,j);}var k=a(this),s=k.retrieve(m,[]),h=n[0].event,t=d(n),o=p,i=t[h]||{},l=Array.slice(arguments,2),r=this,q;
start: 0, if(i.args){l.append(Array.from(i.args));}if(i.base){h=i.base;}if(i.onAdd){i.onAdd(this);}n.each(function(u){var v=o;o=function(){(i.listener||g[u.pseudo].listener).call(r,u,v,arguments,q,t);
end: 0 };});q=o.bind(this);s.include({event:p,monitor:q});k.store(m,s);c.apply(this,[m,p].concat(l));return c.apply(this,[h,q].concat(l));},removeEvent:function(l,n){var m=f(l);
}; if(!m){return e.call(this,l,n);}var j=a(this),o=j.retrieve(l);if(!o){return this;}var h=m[0].event,p=d(m),i=p[h]||{},k=Array.slice(arguments,2);if(i.args){k.append(Array.from(i.args));
var range = this.getDocument().selection.createRange(); }if(i.base){h=i.base;}if(i.onRemove){i.onRemove(this);}e.apply(this,[l,n].concat(k));o.each(function(q,r){if(!n||q.event==n){e.apply(this,[h,q.monitor].concat(k));
if (!range || range.parentElement() != this) return pos; }delete o[r];},this);j.store(l,o);return this;}};};(function(){var b={once:{listener:function(e,f,d,c){f.apply(this,d);this.removeEvent(e.event,c).removeEvent(e.original,f);
var duplicate = range.duplicate(); }},throttle:{listener:function(d,e,c){if(!e._throttled){e.apply(this,c);e._throttled=setTimeout(function(){e._throttled=false;},d.value||250);}}},pause:{listener:function(d,e,c){clearTimeout(e._pause);
e._pause=e.delay(d.value||250,this,c);}}};Events.definePseudo=function(c,d){b[c]=Type.isFunction(d)?{listener:d}:d;return this;};Events.lookupPseudo=function(c){return b[c];
if (this.type == 'text'){ };var a=Events.prototype;Events.implement(Events.Pseudos(b,a.addEvent,a.removeEvent));["Request","Fx"].each(function(c){if(this[c]){this[c].implement(Events.prototype);
pos.start = 0 - duplicate.moveStart('character', -100000); }});}).call(this);(function(){var d={},c=["once","throttle","pause"],b=c.length;while(b--){d[c[b]]=Events.lookupPseudo(c[b]);}Event.definePseudo=function(e,f){d[e]=Type.isFunction(f)?{listener:f}:f;
pos.end = pos.start + range.text.length; return this;};var a=Element.prototype;[Element,Window,Document].invoke("implement",Events.Pseudos(d,a.addEvent,a.removeEvent));}).call(this);(function(){var b=!(window.attachEvent&&!window.addEventListener),e=Element.NativeEvents;
} else { e.focusin=2;e.focusout=2;var c=function(g,j,h){var i=Element.Events[g.event],k;if(i){k=i.condition;}return Slick.match(j,g.value)&&(!k||k.call(j,h));};
var value = this.get('value'); var f=function(g){var h="$delegation:";return{base:"focusin",onRemove:function(i){i.retrieve(h+"forms",[]).each(function(j){j.retrieve(h+"listeners",[]).each(function(k){j.removeEvent(g,k);
var offset = value.length; });j.eliminate(h+g+"listeners").eliminate(h+g+"originalFn");});},listener:function(q,r,p,s,t){var j=p[0],i=this.retrieve(h+"forms",[]),o=j.target,l=(o.get("tag")=="form")?o:j.target.getParent("form"),n=l.retrieve(h+"originalFn",[]),k=l.retrieve(h+"listeners",[]);
duplicate.moveToElementText(this); i.include(l);this.store(h+"forms",i);if(!n.contains(r)){var m=function(u){if(c(q,this,u)){r.call(this,u);}};l.addEvent(g,m);n.push(r);k.push(m);l.store(h+g+"originalFn",n).store(h+g+"listeners",k);
duplicate.setEndPoint('StartToEnd', range); }}};};var a=function(g){return{base:"focusin",listener:function(j,k,h){var i={blur:function(){this.removeEvents(i);}};i[g]=function(l){if(c(j,this,l)){k.call(this,l);
if (duplicate.text.length) offset -= value.match(/[\n\r]*$/)[0].length; }};h[0].target.addEvents(i);}};};var d={mouseenter:{base:"mouseover"},mouseleave:{base:"mouseout"},focus:{base:"focus"+(b?"":"in"),args:[true]},blur:{base:b?"blur":"focusout",args:[true]}};
pos.end = offset - duplicate.text.length; if(!b){Object.append(d,{submit:f("submit"),reset:f("reset"),change:a("change"),select:a("select")});}Event.definePseudo("relay",{listener:function(j,k,i,g,h){var l=i[0];
duplicate.setEndPoint('StartToStart', range); for(var n=l.target;n&&n!=this;n=n.parentNode){var m=document.id(n);if(c(j,m,l)){if(m){k.call(m,l,m);}return;}}},options:d});}).call(this);Element.implement({isDisplayed:function(){return this.getStyle("display")!="none";
pos.start = offset - duplicate.text.length; },isVisible:function(){var a=this.offsetWidth,b=this.offsetHeight;return(a==0&&b==0)?false:(a>0&&b>0)?true:this.style.display!="none";},toggle:function(){return this[this.isDisplayed()?"hide":"show"]();
} },hide:function(){var b;try{b=this.getStyle("display");}catch(a){}if(b=="none"){return this;}return this.store("element:_originalDisplay",b||"").setStyle("display","none");
return pos; },show:function(a){if(!a&&this.isDisplayed()){return this;}a=a||this.retrieve("element:_originalDisplay")||"block";return this.setStyle("display",(a=="none")?"block":a);
}, },swapClass:function(a,b){return this.removeClass(a).addClass(b);}});Document.implement({clearSelection:function(){if(window.getSelection){var a=window.getSelection();
if(a&&a.removeAllRanges){a.removeAllRanges();}}else{if(document.selection&&document.selection.empty){try{document.selection.empty();}catch(b){}}}}});Request.JSONP=new Class({Implements:[Chain,Events,Options],options:{onRequest:function(a){if(this.options.log&&window.console&&console.log){console.log("JSONP retrieving script with url:"+a);
getSelectionStart: function(){ }},onError:function(a){if(this.options.log&&window.console&&console.warn){console.warn("JSONP "+a+" will fail in Internet Explorer, which enforces a 2083 bytes length limit on URIs");
return this.getSelectedRange().start; }},url:"",callbackKey:"callback",injectScript:document.head,data:"",link:"ignore",timeout:0,log:false},initialize:function(a){this.setOptions(a);},send:function(c){if(!Request.prototype.check.call(this,c)){return this;
}, }this.running=true;var d=typeOf(c);if(d=="string"||d=="element"){c={data:c};}c=Object.merge(this.options,c||{});var e=c.data;switch(typeOf(e)){case"element":e=document.id(e).toQueryString();
break;case"object":case"hash":e=Object.toQueryString(e);}var b=this.index=Request.JSONP.counter++;var f=c.url+(c.url.test("\\?")?"&":"?")+(c.callbackKey)+"=Request.JSONP.request_map.request_"+b+(e?"&"+e:"");
getSelectionEnd: function(){ if(f.length>2083){this.fireEvent("error",f);}Request.JSONP.request_map["request_"+b]=function(){this.success(arguments,b);}.bind(this);var a=this.getScript(f).inject(c.injectScript);
return this.getSelectedRange().end; this.fireEvent("request",[f,a]);if(c.timeout){this.timeout.delay(c.timeout,this);}return this;},getScript:function(a){if(!this.script){this.script=new Element("script[type=text/javascript]",{async:true,src:a});
}, }return this.script;},success:function(b,a){if(!this.running){return false;}this.clear().fireEvent("complete",b).fireEvent("success",b).callChain();},cancel:function(){if(this.running){this.clear().fireEvent("cancel");
}return this;},isRunning:function(){return !!this.running;},clear:function(){this.running=false;if(this.script){this.script.destroy();this.script=null;
setCaretPosition: function(pos){ }return this;},timeout:function(){if(this.running){this.running=false;this.fireEvent("timeout",[this.script.get("src"),this.script]).fireEvent("failure").cancel();
if (pos == 'end') pos = this.get('value').length; }return this;}});Request.JSONP.counter=0;Request.JSONP.request_map={};
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;
}
});
/*
---
name: Events.Pseudos
description: Adds the functionallity to add pseudo events
license: MIT-style license
authors:
- Arian Stolwijk
requires: [Core/Class.Extras, Core/Slick.Parser, More/MooTools.More]
provides: [Events.Pseudos]
...
*/
Events.Pseudos = function(pseudos, addEvent, removeEvent){
var storeKey = 'monitorEvents:';
var storageOf = function(object){
return {
store: object.store ? function(key, value){
object.store(storeKey + key, value);
} : function(key, value){
(object.$monitorEvents || (object.$monitorEvents = {}))[key] = value;
},
retrieve: object.retrieve ? function(key, dflt){
return object.retrieve(storeKey + key, dflt);
} : function(key, dflt){
if (!object.$monitorEvents) return dflt;
return object.$monitorEvents[key] || dflt;
}
};
};
var splitType = function(type){
if (type.indexOf(':') == -1) return null;
var parsed = Slick.parse(type).expressions[0][0],
parsedPseudos = parsed.pseudos;
return (pseudos && pseudos[parsedPseudos[0].key]) ? {
event: parsed.tag,
value: parsedPseudos[0].value,
pseudo: parsedPseudos[0].key,
original: type
} : null;
};
return {
addEvent: function(type, fn, internal){
var split = splitType(type);
if (!split) return addEvent.call(this, type, fn, internal);
var storage = storageOf(this),
events = storage.retrieve(type, []),
pseudoArgs = Array.from(pseudos[split.pseudo]),
proxy = pseudoArgs[1];
var self = this;
var monitor = function(){
pseudoArgs[0].call(self, split, fn, arguments, proxy);
};
events.include({event: fn, monitor: monitor});
storage.store(type, events);
var eventType = split.event;
if (proxy && proxy[eventType]) eventType = proxy[eventType].base;
addEvent.call(this, type, fn, internal);
return addEvent.call(this, eventType, monitor, internal);
},
removeEvent: function(type, fn){
var split = splitType(type);
if (!split) return removeEvent.call(this, type, fn);
var storage = storageOf(this),
events = storage.retrieve(type),
pseudoArgs = Array.from(pseudos[split.pseudo]),
proxy = pseudoArgs[1];
if (!events) return this;
var eventType = split.event;
if (proxy && proxy[eventType]) eventType = proxy[eventType].base;
removeEvent.call(this, type, fn);
events.each(function(monitor, i){
if (!fn || monitor.event == fn) removeEvent.call(this, eventType, monitor.monitor);
delete events[i];
}, this);
storage.store(type, events);
return this;
}
};
};
(function(){
var pseudos = {
once: function(split, fn, args){
fn.apply(this, args);
this.removeEvent(split.original, fn);
}
};
Events.definePseudo = function(key, fn){
pseudos[key] = fn;
};
var proto = Events.prototype;
Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
})();
/*
---
name: Element.Event.Pseudos
description: Adds the functionality to add pseudo events for Elements
license: MIT-style license
authors:
- Arian Stolwijk
requires: [Core/Element.Event, Events.Pseudos]
provides: [Element.Event.Pseudos]
...
*/
(function(){
var pseudos = {
once: function(split, fn, args){
fn.apply(this, args);
this.removeEvent(split.original, fn);
}
};
Event.definePseudo = function(key, fn, proxy){
pseudos[key] = [fn, proxy];
};
var proto = Element.prototype;
[Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
})();
/*
---
script: Element.Delegation.js
name: Element.Delegation
description: Extends the Element native object to include the delegate method for more efficient event management.
credits:
- "Event checking based on the work of Daniel Steigerwald. License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
license: MIT-style license
authors:
- Aaron Newton
- Daniel Steigerwald
requires: [/MooTools.More, Element.Event.Pseudos]
provides: [Element.Delegation]
...
*/
Event.definePseudo('relay', function(split, fn, args, proxy){
var event = args[0];
var check = proxy ? proxy.condition : null;
for (var target = event.target; target && target != this; target = target.parentNode){
var finalTarget = document.id(target);
if (Slick.match(target, split.value) && (!check || check.call(finalTarget, event))){
if (finalTarget) fn.call(finalTarget, event, finalTarget);
return;
}
}
}, {
mouseenter: {
base: 'mouseover',
condition: Element.Events.mouseenter.condition
},
mouseleave: {
base: 'mouseout',
condition: Element.Events.mouseleave.condition
}
});
/*
---
script: Element.Shortcuts.js
name: Element.Shortcuts
description: Extends the Element native object to include some shortcut methods.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Element.Style
- /MooTools.More
provides: [Element.Shortcuts]
...
*/
Element.implement({
isDisplayed: function(){
return this.getStyle('display') != 'none';
},
isVisible: function(){
var w = this.offsetWidth,
h = this.offsetHeight;
return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none';
},
toggle: function(){
return this[this.isDisplayed() ? 'hide' : 'show']();
},
hide: function(){
var d;
try {
//IE fails here if the element is not in the dom
d = this.getStyle('display');
} catch(e){}
if (d == 'none') return this;
return this.store('element:_originalDisplay', d || '').setStyle('display', 'none');
},
show: function(display){
if (!display && this.isDisplayed()) return this;
display = display || this.retrieve('element:_originalDisplay') || 'block';
return this.setStyle('display', (display == 'none') ? 'block' : display);
},
swapClass: function(remove, add){
return this.removeClass(remove).addClass(add);
}
});
Document.implement({
clearSelection: function(){
if (document.selection && document.selection.empty){
document.selection.empty();
} else if (window.getSelection){
var selection = window.getSelection();
if (selection && selection.removeAllRanges) selection.removeAllRanges();
}
}
});

16
couchpotato/static/scripts/page/settings.js

@ -34,9 +34,9 @@ Page.Settings = new Class({
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 g = self.groups[tab] || self.groups.general;
g.tab[a](c); g.tab[a](c);
g.group[a](c); g.group[a](c);
}, },
@ -76,7 +76,7 @@ Page.Settings = new Class({
self.containers = new Element('form.uniForm.containers') self.containers = new Element('form.uniForm.containers')
); );
Object.each(json.sections, function(section, section_name){ Object.each(json.options, function(section, section_name){
// Create tab // Create tab
var tab = new Element('li').adopt( var tab = new Element('li').adopt(
@ -132,13 +132,7 @@ var OptionBase = new Class({
// Add focus events // Add focus events
self.input.addEvents({ self.input.addEvents({
'change': self.changed.bind(self), 'change': self.changed.bind(self),
'keyup': 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);
}
}); });
}, },

28
couchpotato/static/scripts/uniform.js

@ -0,0 +1,28 @@
var Uniform = new Class({
Implements : [Options],
options : {
focusedClass : 'focused',
holderClass : 'ctrlHolder'
},
initialize : function(options) {
this.setOptions(options);
var focused = this.options.focusedClass;
var holder = '.' + this.options.holderClass;
$(document.body).addEvents( {
'focus:relay(input, select, textarea)' : function() {
var parent = this.getParent(holder);
if (parent)
parent.addClass(focused);
},
'blur:relay(input, select, textarea)' : function() {
var parent = this.getParent(holder);
if (parent)
parent.removeClass(focused);
}
});
}
});

7
couchpotato/templates/_desktop.html

@ -4,8 +4,11 @@
<link rel="stylesheet" href="{{ url_for('.static', filename='style/main.css') }}"> <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.generic.css') }}">
<link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.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.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/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>
@ -25,11 +28,13 @@
<script type="text/javascript"> <script type="text/javascript">
window.addEvent('domready', function() { window.addEvent('domready', function() {
new Uniform();
var cp = new CouchPotato({ var cp = new CouchPotato({
'base_url': '{{ request.path }}', 'base_url': '{{ request.path }}',
'api': { 'api': {
'url': '{{ url_for('api.index') }}', 'url': '{{ url_for('api.index') }}',
'path_sep': '{{ sep }}' 'path_sep': '{{ sep }}',
'is_remote': false
} }
}); });
}) })

Loading…
Cancel
Save