Merge branch 'refs/heads/develop'

This commit is contained in:
Ruud committed 2014-06-11 23:51:05 +02:00
commit b45307e493
50 files changed
+651 -334

No files matched your search

+1 -1
View File
@@ -45,7 +45,7 @@ class WebHandler(BaseHandler):
self.write({'success': False, 'error': 'Failed returning results'}) self.write({'success': False, 'error': 'Failed returning results'})
def addView(route, func, static = False): def addView(route, func):
views[route] = func views[route] = func
@@ -72,6 +72,9 @@ class DownloaderBase(Provider):
return return
return self.download(data = data, media = media, filedata = filedata) return self.download(data = data, media = media, filedata = filedata)
def download(self, *args, **kwargs):
return False
def _getAllDownloadStatus(self, download_ids): def _getAllDownloadStatus(self, download_ids):
if self.isDisabled(manual = True, data = {}): if self.isDisabled(manual = True, data = {}):
return return
@@ -40,15 +40,16 @@ var DownloadersBase = new Class({
button.set('text', button_name); button.set('text', button_name);
var message;
if(json.success){ if(json.success){
var message = new Element('span.success', { message = new Element('span.success', {
'text': 'Connection successful' 'text': 'Connection successful'
}).inject(button, 'after') }).inject(button, 'after')
} }
else { else {
var msg_text = 'Connection failed. Check logs for details.'; var msg_text = 'Connection failed. Check logs for details.';
if(json.hasOwnProperty('msg')) msg_text = json.msg; if(json.hasOwnProperty('msg')) msg_text = json.msg;
var message = new Element('span.failed', { message = new Element('span.failed', {
'text': msg_text 'text': msg_text
}).inject(button, 'after') }).inject(button, 'after')
} }
+1 -1
View File
@@ -10,7 +10,7 @@ from threading import RLock
from couchpotato.api import addApiView from couchpotato.api import addApiView
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
from couchpotato.core.helpers.encoding import ss, sp from couchpotato.core.helpers.encoding import sp
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
from couchpotato.core.plugins.base import Plugin from couchpotato.core.plugins.base import Plugin
from couchpotato.environment import Env from couchpotato.environment import Env
+2 -2
View File
@@ -130,7 +130,7 @@ class NZBVortex(DownloaderBase):
url = cleanHost(self.conf('host'), ssl = self.conf('ssl')) + 'api/' + call url = cleanHost(self.conf('host'), ssl = self.conf('ssl')) + 'api/' + call
try: try:
data = self.urlopen('%s?%s' % (url, params), *args, verify_ssl = False, **kwargs) data = self.urlopen('%s?%s' % (url, params), *args, **kwargs)
if data: if data:
return json.loads(data) return json.loads(data)
@@ -154,7 +154,7 @@ class NZBVortex(DownloaderBase):
url = cleanHost(self.conf('host')) + 'api/app/apilevel' url = cleanHost(self.conf('host')) + 'api/app/apilevel'
try: try:
data = self.urlopen(url, show_error = False, verify_ssl = False) data = self.urlopen(url, show_error = False)
self.api_level = float(json.loads(data).get('apilevel')) self.api_level = float(json.loads(data).get('apilevel'))
except URLError as e: except URLError as e:
if hasattr(e, 'code') and e.code == 403: if hasattr(e, 'code') and e.code == 403:
+1 -1
View File
@@ -194,7 +194,7 @@ class Sabnzbd(DownloaderBase):
'output': 'json' 'output': 'json'
})) }))
data = self.urlopen(url, timeout = 60, show_error = False, verify_ssl = False, headers = {'User-Agent': Env.getIdentifier()}, **kwargs) data = self.urlopen(url, timeout = 60, show_error = False, headers = {'User-Agent': Env.getIdentifier()}, **kwargs)
if use_json: if use_json:
d = json.loads(data) d = json.loads(data)
if d.get('error'): if d.get('error'):
+1
View File
@@ -90,6 +90,7 @@ class SynologyRPC(object):
self.download_url = 'http://%s:%s/webapi/DownloadStation/task.cgi' % (host, port) self.download_url = 'http://%s:%s/webapi/DownloadStation/task.cgi' % (host, port)
self.auth_url = 'http://%s:%s/webapi/auth.cgi' % (host, port) self.auth_url = 'http://%s:%s/webapi/auth.cgi' % (host, port)
self.sid = None
self.username = username self.username = username
self.password = password self.password = password
self.destination = destination self.destination = destination
+1 -1
View File
@@ -168,7 +168,7 @@ class uTorrent(DownloaderBase):
status = 'busy' status = 'busy'
if (torrent[1] & self.status_flags['STARTED'] or torrent[1] & self.status_flags['QUEUED']) and torrent[4] == 1000: if (torrent[1] & self.status_flags['STARTED'] or torrent[1] & self.status_flags['QUEUED']) and torrent[4] == 1000:
status = 'seeding' status = 'seeding'
elif (torrent[1] & self.status_flags['ERROR']): elif torrent[1] & self.status_flags['ERROR']:
status = 'failed' status = 'failed'
elif torrent[4] == 1000: elif torrent[4] == 1000:
status = 'completed' status = 'completed'
+2 -2
View File
@@ -1,7 +1,7 @@
import os import os
import traceback import traceback
from couchpotato import get_db, CPLog from couchpotato import CPLog
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
from couchpotato.core.helpers.encoding import toUnicode from couchpotato.core.helpers.encoding import toUnicode
from couchpotato.core.plugins.base import Plugin from couchpotato.core.plugins.base import Plugin
@@ -28,7 +28,7 @@ class MediaBase(Plugin):
media = fireEvent('media.get', media_id, single = True) media = fireEvent('media.get', media_id, single = True)
event_name = '%s.searcher.single' % media.get('type') event_name = '%s.searcher.single' % media.get('type')
fireEventAsync(event_name, media, on_complete = self.createNotifyFront(media_id)) fireEventAsync(event_name, media, on_complete = self.createNotifyFront(media_id), manual = True)
except: except:
log.error('Failed creating onComplete: %s', traceback.format_exc()) log.error('Failed creating onComplete: %s', traceback.format_exc())
+2 -2
View File
@@ -99,7 +99,7 @@ from couchpotato.core.helpers.encoding import simplifyString"""
class TitleIndex(TreeBasedIndex): class TitleIndex(TreeBasedIndex):
_version = 3 _version = 4
custom_header = """from CodernityDB.tree_index import TreeBasedIndex custom_header = """from CodernityDB.tree_index import TreeBasedIndex
from string import ascii_letters from string import ascii_letters
@@ -128,7 +128,7 @@ from couchpotato.core.helpers.encoding import toUnicode, simplifyString"""
title = title[len(prefix):] title = title[len(prefix):]
break break
return str(nr_prefix + title).ljust(32, '_')[:32] return str(nr_prefix + title).ljust(32, ' ')[:32]
class StartsWithIndex(TreeBasedIndex): class StartsWithIndex(TreeBasedIndex):
@@ -125,7 +125,6 @@ class MediaPlugin(MediaBase):
imdb_id = getImdb(str(media_id)) imdb_id = getImdb(str(media_id))
media = None
if imdb_id: if imdb_id:
media = db.get('media', 'imdb-%s' % imdb_id, with_doc = True)['doc'] media = db.get('media', 'imdb-%s' % imdb_id, with_doc = True)['doc']
else: else:
@@ -129,6 +129,9 @@ class YarrProvider(Provider):
else: else:
return [] return []
def buildUrl(self, *args, **kwargs):
pass
def login(self): def login(self):
# Check if we are still logged in every hour # Check if we are still logged in every hour
@@ -91,7 +91,7 @@ class Base(NZBProvider, RSS):
# Extract a password from the description # Extract a password from the description
password = re.search('(?:' + self.passwords_regex + ')(?: *)(?:\:|\=)(?: *)(.*?)\<br\>|\n|$', description, flags = re.I).group(1) password = re.search('(?:' + self.passwords_regex + ')(?: *)(?:\:|\=)(?: *)(.*?)\<br\>|\n|$', description, flags = re.I).group(1)
if password: if password:
name = name + ' {{%s}}' % password.strip() name += ' {{%s}}' % password.strip()
except: except:
log.debug('Error getting details of "%s": %s', (name, traceback.format_exc())) log.debug('Error getting details of "%s": %s', (name, traceback.format_exc()))
@@ -51,6 +51,7 @@ class Base(TorrentProvider):
results_table = None results_table = None
data_split = splitString(data, '<table') data_split = splitString(data, '<table')
soup = None
for x in data_split: for x in data_split:
soup = BeautifulSoup(x) soup = BeautifulSoup(x)
results_table = soup.find('table', attrs = {'class': 'koptekst'}) results_table = soup.find('table', attrs = {'class': 'koptekst'})
@@ -1,4 +1,3 @@
from couchpotato.core.helpers.encoding import tryUrlencode
from couchpotato.core.helpers.variable import tryInt from couchpotato.core.helpers.variable import tryInt
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
from couchpotato.core.media._base.providers.torrent.base import TorrentProvider from couchpotato.core.media._base.providers.torrent.base import TorrentProvider
@@ -7,7 +7,6 @@
text-align: right; text-align: right;
height: 100%; height: 100%;
transition: all .4s cubic-bezier(0.9,0,0.1,1); transition: all .4s cubic-bezier(0.9,0,0.1,1);
position: absolute;
z-index: 20; z-index: 20;
border: 0 solid transparent; border: 0 solid transparent;
border-bottom-width: 4px; border-bottom-width: 4px;
@@ -357,12 +357,8 @@
top: 30px; top: 30px;
clear: both; clear: both;
bottom: 30px; bottom: 30px;
overflow: hidden;
position: absolute; position: absolute;
} }
.movies .data:hover .description {
overflow: auto;
}
.movies.list_list .movie:not(.details_view) .info .description, .movies.list_list .movie:not(.details_view) .info .description,
.movies.mass_edit_list .info .description, .movies.mass_edit_list .info .description,
.movies.thumbs_list .info .description { .movies.thumbs_list .info .description {
@@ -158,7 +158,7 @@ var Movie = new Class({
'text': self.data.info.year || 'n/a' 'text': self.data.info.year || 'n/a'
}) })
), ),
self.description = new Element('div.description', { self.description = new Element('div.description.tiny_scroll', {
'text': self.data.info.plot 'text': self.data.info.plot
}), }),
self.quality = new Element('div.quality', { self.quality = new Element('div.quality', {
@@ -45,6 +45,7 @@ class Charts(Plugin):
if catched_charts: if catched_charts:
return catched_charts return catched_charts
charts = []
try: try:
self.update_in_progress = True self.update_in_progress = True
charts = fireEvent('automation.get_chart_list', merge = True) charts = fireEvent('automation.get_chart_list', merge = True)
@@ -3,26 +3,21 @@
margin-bottom: 30px; margin-bottom: 30px;
} }
.charts > h2 { .charts > h2 {
height: 40px; height: 40px;
} }
.charts .chart { .charts .chart {
display: inline-block; display: inline-block;
width: 50%; width: 50%;
vertical-align: top; vertical-align: top;
max-height: 510px; max-height: 510px;
overflow: hidden; scrollbar-base-color: #4e5969;
scrollbar-base-color: #4e5969; }
}
.charts .chart:hover { .charts .chart .media_result.hidden {
overflow-y: auto; display: none;
} }
.charts .chart .media_result.hidden {
display: none;
}
.charts .refresh { .charts .refresh {
clear:both; clear:both;
@@ -36,30 +31,30 @@
text-align:center; text-align:center;
} }
.charts .refresh a { .charts .refresh a {
text-align: center; text-align: center;
padding: 0; padding: 0;
display: none; display: none;
width: 30px; width: 30px;
height: 30px; height: 30px;
position: absolute; position: absolute;
right: 10px; right: 10px;
top: -40px; top: -40px;
opacity: .7; opacity: .7;
} }
.charts .refresh a:hover { .charts .refresh a:hover {
opacity: 1; opacity: 1;
} }
.charts p.no_charts_enabled { .charts p.no_charts_enabled {
padding: 0.7em 1em; padding: 0.7em 1em;
display: none; display: none;
} }
.charts .chart h3 a { .charts .chart h3 a {
color: #fff; color: #fff;
} }
.charts .chart .media_result { .charts .chart .media_result {
@@ -148,7 +143,6 @@
padding: 0 3px 10px 0; padding: 0 3px 10px 0;
} }
.charts .chart .media_result .data:before { .charts .chart .media_result .data:before {
bottom: 0;
content: ''; content: '';
display: block; display: block;
height: 10px; height: 10px;
@@ -22,9 +22,11 @@ var Charts = new Class({
'events': { 'events': {
'click': function(e) { 'click': function(e) {
e.preventDefault(); e.preventDefault();
self.el.getChildren('div.chart').destroy();
self.el.getElements('.chart').destroy();
self.el_refreshing_text.show(); self.el_refreshing_text.show();
self.el_refresh_link.hide(); self.el_refresh_link.hide();
self.api_request = Api.request('charts.view', { self.api_request = Api.request('charts.view', {
'data': { 'force_update': 1 }, 'data': { 'force_update': 1 },
'onComplete': self.fill.bind(self) 'onComplete': self.fill.bind(self)
@@ -72,7 +74,7 @@ var Charts = new Class({
Object.each(json.charts, function(chart){ Object.each(json.charts, function(chart){
var c = new Element('div.chart').grab( var c = new Element('div.chart.tiny_scroll').grab(
new Element('h3').grab( new Element('a', { new Element('h3').grab( new Element('a', {
'text': chart.name, 'text': chart.name,
'href': chart.url 'href': chart.url
@@ -51,19 +51,14 @@ class FanartTV(MovieProvider):
def _parseMovie(self, movie): def _parseMovie(self, movie):
images = { images = {
'landscape': [], 'landscape': self._getMultImages(movie.get('moviethumb', []), 1),
'logo': [], 'logo': [],
'disc_art': [], 'disc_art': self._getMultImages(self._trimDiscs(movie.get('moviedisc', [])), 1),
'clear_art': [], 'clear_art': self._getMultImages(movie.get('hdmovieart', []), 1),
'banner': [], 'banner': self._getMultImages(movie.get('moviebanner', []), 1),
'extra_fanart': [], 'extra_fanart': [],
} }
images['landscape'] = self._getMultImages(movie.get('moviethumb', []), 1)
images['banner'] = self._getMultImages(movie.get('moviebanner', []), 1)
images['disc_art'] = self._getMultImages(self._trimDiscs(movie.get('moviedisc', [])), 1)
images['clear_art'] = self._getMultImages(movie.get('hdmovieart', []), 1)
if len(images['clear_art']) == 0: if len(images['clear_art']) == 0:
images['clear_art'] = self._getMultImages(movie.get('movieart', []), 1) images['clear_art'] = self._getMultImages(movie.get('movieart', []), 1)
@@ -105,10 +100,10 @@ class FanartTV(MovieProvider):
return image_url return image_url
def _getMultImages(self, images, n): def _getMultImages(self, images, n):
''' """
Chooses the best n images and returns them as a list. Chooses the best n images and returns them as a list.
If n<0, all images will be returned. If n<0, all images will be returned.
''' """
image_urls = [] image_urls = []
pool = [] pool = []
for image in images: for image in images:
@@ -1,6 +1,6 @@
import traceback import traceback
from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.event import addEvent
from couchpotato.core.helpers.encoding import simplifyString, toUnicode, ss from couchpotato.core.helpers.encoding import simplifyString, toUnicode, ss
from couchpotato.core.helpers.variable import tryInt from couchpotato.core.helpers.variable import tryInt
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
@@ -0,0 +1,36 @@
import os
from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData
autoload = 'MediaBrowser'
class MediaBrowser(MovieMetaData):
def getThumbnailName(self, name, root, i):
return os.path.join(root, 'folder.jpg')
def getFanartName(self, name, root, i):
return os.path.join(root, 'backdrop.jpg')
config = [{
'name': 'mediabrowser',
'groups': [
{
'tab': 'renamer',
'subtab': 'metadata',
'name': 'mediabrowser_metadata',
'label': 'MediaBrowser',
'description': 'Generate folder.jpg and backdrop.jpg',
'options': [
{
'name': 'meta_enabled',
'default': False,
'type': 'enabler',
},
],
},
],
}]
@@ -0,0 +1,33 @@
import os
from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData
autoload = 'SonyPS3'
class SonyPS3(MovieMetaData):
def getThumbnailName(self, name, root, i):
return os.path.join(root, 'cover.jpg')
config = [{
'name': 'sonyps3',
'groups': [
{
'tab': 'renamer',
'subtab': 'metadata',
'name': 'sonyps3_metadata',
'label': 'Sony PS3',
'description': 'Generate cover.jpg',
'options': [
{
'name': 'meta_enabled',
'default': False,
'type': 'enabler',
},
],
},
],
}]
@@ -1,5 +1,4 @@
from couchpotato.core.helpers.encoding import tryUrlencode from couchpotato.core.helpers.encoding import tryUrlencode
from couchpotato.core.event import fireEvent
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
from couchpotato.core.media._base.providers.torrent.sceneaccess import Base from couchpotato.core.media._base.providers.torrent.sceneaccess import Base
from couchpotato.core.media.movie.providers.base import MovieProvider from couchpotato.core.media.movie.providers.base import MovieProvider
@@ -11,3 +11,6 @@ class TrailerProvider(Provider):
def __init__(self): def __init__(self):
addEvent('trailer.search', self.search) addEvent('trailer.search', self.search)
def search(self, *args, **kwargs):
pass
+43 -12
View File
@@ -58,13 +58,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
def searchAllView(self, **kwargs): def searchAllView(self, **kwargs):
fireEventAsync('movie.searcher.all') fireEventAsync('movie.searcher.all', manual = True)
return { return {
'success': not self.in_progress 'success': not self.in_progress
} }
def searchAll(self): def searchAll(self, manual = False):
if self.in_progress: if self.in_progress:
log.info('Search already in progress') log.info('Search already in progress')
@@ -91,7 +91,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
media = fireEvent('media.get', media_id, single = True) media = fireEvent('media.get', media_id, single = True)
try: try:
self.single(media, search_protocols) self.single(media, search_protocols, manual = manual)
except IndexError: except IndexError:
log.error('Forcing library update for %s, if you see this often, please report: %s', (getIdentifier(media), traceback.format_exc())) log.error('Forcing library update for %s, if you see this often, please report: %s', (getIdentifier(media), traceback.format_exc()))
fireEvent('movie.update_info', media_id) fireEvent('movie.update_info', media_id)
@@ -109,7 +109,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
self.in_progress = False self.in_progress = False
def single(self, movie, search_protocols = None, manual = False): def single(self, movie, search_protocols = None, manual = False, force_download = False):
# Find out search type # Find out search type
try: try:
@@ -128,6 +128,9 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
found_releases = [] found_releases = []
previous_releases = movie.get('releases', []) previous_releases = movie.get('releases', [])
too_early_to_search = [] too_early_to_search = []
outside_eta_results = 0
alway_search = self.conf('always_search')
ignore_eta = manual
default_title = getTitle(movie) default_title = getTitle(movie)
if not default_title: if not default_title:
@@ -137,6 +140,14 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title) fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title)
# Ignore eta once every 7 days
if not alway_search:
prop_name = 'last_ignored_eta.%s' % movie['_id']
last_ignored_eta = float(Env.prop(prop_name, default = 0))
if last_ignored_eta > time.time() - 604800:
ignore_eta = True
Env.prop(prop_name, value = time.time())
db = get_db() db = get_db()
profile = db.get('id', movie['profile_id']) profile = db.get('id', movie['profile_id'])
@@ -154,9 +165,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
index += 1 index += 1
if not self.conf('always_search') and not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year']): could_not_be_released = not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year'])
if not alway_search and could_not_be_released:
too_early_to_search.append(q_identifier) too_early_to_search.append(q_identifier)
continue
# Skip release, if ETA isn't ignored
if not ignore_eta:
continue
has_better_quality = 0 has_better_quality = 0
@@ -177,15 +192,19 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
break break
quality = fireEvent('quality.single', identifier = q_identifier, single = True) quality = fireEvent('quality.single', identifier = q_identifier, single = True)
log.info('Search for %s in %s', (default_title, quality['label'])) log.info('Search for %s in %s%s', (default_title, quality['label'], ' ignoring ETA' if alway_search or ignore_eta else ''))
# Extend quality with profile customs # Extend quality with profile customs
quality['custom'] = quality_custom quality['custom'] = quality_custom
results = fireEvent('searcher.search', search_protocols, movie, quality, single = True) or [] results = fireEvent('searcher.search', search_protocols, movie, quality, single = True) or []
if len(results) == 0: results_count = len(results)
if results_count == 0:
log.debug('Nothing found for %s in %s', (default_title, quality['label'])) log.debug('Nothing found for %s in %s', (default_title, quality['label']))
# Keep track of releases found outside ETA window
outside_eta_results += results_count if could_not_be_released else 0
# Check if movie isn't deleted while searching # Check if movie isn't deleted while searching
if not fireEvent('media.get', movie.get('_id'), single = True): if not fireEvent('media.get', movie.get('_id'), single = True):
break break
@@ -193,8 +212,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
# Add them to this movie releases list # Add them to this movie releases list
found_releases += fireEvent('release.create_from_search', results, movie, quality, single = True) found_releases += fireEvent('release.create_from_search', results, movie, quality, single = True)
# Don't trigger download, but notify user of available releases
if could_not_be_released:
if results_count > 0:
log.debug('Found %s releases for "%s", but ETA isn\'t correct yet.', (results_count, default_title))
# Try find a valid result and download it # Try find a valid result and download it
if fireEvent('release.try_download_result', results, movie, quality_custom, manual, single = True): if (force_download or not could_not_be_released) and fireEvent('release.try_download_result', results, movie, quality_custom, single = True):
ret = True ret = True
# Remove releases that aren't found anymore # Remove releases that aren't found anymore
@@ -214,6 +238,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
if len(too_early_to_search) > 0: if len(too_early_to_search) > 0:
log.info2('Too early to search for %s, %s', (too_early_to_search, default_title)) log.info2('Too early to search for %s, %s', (too_early_to_search, default_title))
if outside_eta_results > 0:
message = 'Found %s releases for "%s" before ETA. Select and download via the dashboard.' % (outside_eta_results, default_title)
log.info(message)
if not manual:
fireEvent('media.available', message = message, data = {})
fireEvent('notify.frontend', type = 'movie.searcher.ended', data = {'_id': movie['_id']}) fireEvent('notify.frontend', type = 'movie.searcher.ended', data = {'_id': movie['_id']})
return ret return ret
@@ -334,13 +365,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
def tryNextReleaseView(self, media_id = None, **kwargs): def tryNextReleaseView(self, media_id = None, **kwargs):
trynext = self.tryNextRelease(media_id, manual = True) trynext = self.tryNextRelease(media_id, manual = True, force_download = True)
return { return {
'success': trynext 'success': trynext
} }
def tryNextRelease(self, media_id, manual = False): def tryNextRelease(self, media_id, manual = False, force_download = False):
try: try:
db = get_db() db = get_db()
@@ -352,7 +383,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
movie_dict = fireEvent('media.get', media_id, single = True) movie_dict = fireEvent('media.get', media_id, single = True)
log.info('Trying next release for: %s', getTitle(movie_dict)) log.info('Trying next release for: %s', getTitle(movie_dict))
self.single(movie_dict, manual = manual) self.single(movie_dict, manual = manual, force_download = force_download)
return True return True
@@ -84,7 +84,6 @@ class Suggestion(Plugin):
# Get new results and add them # Get new results and add them
if len(new_suggestions) - 1 < limit: if len(new_suggestions) - 1 < limit:
db = get_db()
active_movies = fireEvent('media.with_status', ['active', 'done'], single = True) active_movies = fireEvent('media.with_status', ['active', 'done'], single = True)
movies = [getIdentifier(x) for x in active_movies] movies = [getIdentifier(x) for x in active_movies]
movies.extend(seen) movies.extend(seen)
+1
View File
@@ -15,6 +15,7 @@ class Notification(Provider):
test_message = 'ZOMG Lazors Pewpewpew!' test_message = 'ZOMG Lazors Pewpewpew!'
listen_to = [ listen_to = [
'media.available',
'renamer.after', 'movie.snatched', 'renamer.after', 'movie.snatched',
'updater.available', 'updater.updated', 'updater.available', 'updater.updated',
'core.message.important', 'core.message.important',
@@ -28,6 +28,7 @@ class CoreNotifier(Notification):
m_lock = None m_lock = None
listen_to = [ listen_to = [
'media.available',
'renamer.after', 'movie.snatched', 'renamer.after', 'movie.snatched',
'updater.available', 'updater.updated', 'updater.available', 'updater.updated',
'core.message', 'core.message.important', 'core.message', 'core.message.important',
+2 -1
View File
@@ -22,10 +22,11 @@ class NMJ(Notification):
# noinspection PyMissingConstructor # noinspection PyMissingConstructor
def __init__(self): def __init__(self):
addEvent('renamer.after', self.addToLibrary)
addApiView(self.testNotifyName(), self.test) addApiView(self.testNotifyName(), self.test)
addApiView('notify.nmj.auto_config', self.autoConfig) addApiView('notify.nmj.auto_config', self.autoConfig)
addEvent('renamer.after', self.addToLibrary)
def autoConfig(self, host = 'localhost', **kwargs): def autoConfig(self, host = 'localhost', **kwargs):
mount = '' mount = ''
+3 -1
View File
@@ -1,6 +1,7 @@
import os import os
import subprocess import subprocess
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent from couchpotato.core.event import addEvent
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
from couchpotato.core.notifications.base import Notification from couchpotato.core.notifications.base import Notification
@@ -16,7 +17,8 @@ class Synoindex(Notification):
index_path = '/usr/syno/bin/synoindex' index_path = '/usr/syno/bin/synoindex'
def __init__(self): def __init__(self):
super(Synoindex, self).__init__() addApiView(self.testNotifyName(), self.test)
addEvent('renamer.after', self.addToLibrary) addEvent('renamer.after', self.addToLibrary)
def addToLibrary(self, message = None, group = None): def addToLibrary(self, message = None, group = None):
+1 -1
View File
@@ -166,7 +166,7 @@ class Plugin(object):
log.error('Couldn\'t remove empty directory %s: %s', (folder, traceback.format_exc())) log.error('Couldn\'t remove empty directory %s: %s', (folder, traceback.format_exc()))
# http request # http request
def urlopen(self, url, timeout = 30, data = None, headers = None, files = None, show_error = True, verify_ssl = True): def urlopen(self, url, timeout = 30, data = None, headers = None, files = None, show_error = True):
url = quote(ss(url), safe = "%/:=&?~#+!$,;'@()*[]") url = quote(ss(url), safe = "%/:=&?~#+!$,;'@()*[]")
if not headers: headers = {} if not headers: headers = {}
+2 -2
View File
@@ -49,8 +49,8 @@ class FileManager(Plugin):
files.extend(file_dict[x]) files.extend(file_dict[x])
for f in os.listdir(cache_dir): for f in os.listdir(cache_dir):
if os.path.splitext(f.name)[1] in ['.png', '.jpg', '.jpeg']: if os.path.splitext(f)[1] in ['.png', '.jpg', '.jpeg']:
file_path = os.path.join(cache_dir, f.name) file_path = os.path.join(cache_dir, f)
if toUnicode(file_path) not in files: if toUnicode(file_path) not in files:
os.remove(file_path) os.remove(file_path)
except: except:
+2 -2
View File
@@ -134,8 +134,8 @@ class Logging(Plugin):
logs_raw = toUnicode(log_content).split('[0m\n') logs_raw = toUnicode(log_content).split('[0m\n')
logs = [] logs = []
for log in logs_raw: for log_line in logs_raw:
split = splitString(log, '\x1b') split = splitString(log_line, '\x1b')
if split: if split:
try: try:
date, time, log_type = splitString(split[0], ' ') date, time, log_type = splitString(split[0], ' ')
+1 -1
View File
@@ -23,7 +23,7 @@
cursor: pointer; cursor: pointer;
} }
.page.log .nav li:hover:not(.active, .filter) { .page.log .nav li:hover:not(.active):not(.filter) {
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
} }
+11 -8
View File
@@ -32,7 +32,7 @@ class Manage(Plugin):
# Add files after renaming # Add files after renaming
def after_rename(message = None, group = None): def after_rename(message = None, group = None):
if not group: group = {} if not group: group = {}
return self.scanFilesToLibrary(folder = group['destination_dir'], files = group['renamed_files']) return self.scanFilesToLibrary(folder = group['destination_dir'], files = group['renamed_files'], release_download = group['release_download'])
addEvent('renamer.after', after_rename, priority = 110) addEvent('renamer.after', after_rename, priority = 110)
addApiView('manage.update', self.updateLibraryView, docs = { addApiView('manage.update', self.updateLibraryView, docs = {
@@ -210,14 +210,14 @@ class Manage(Plugin):
'to_go': total_found, 'to_go': total_found,
}) })
self.updateProgress(folder, to_go)
if group['media'] and group['identifier']: if group['media'] and group['identifier']:
added_identifiers.append(group['identifier']) added_identifiers.append(group['identifier'])
# Add it to release and update the info # Add it to release and update the info
fireEvent('release.add', group = group, update_info = False) fireEvent('release.add', group = group, update_info = False)
fireEvent('movie.update_info', identifier = group['identifier'], on_complete = self.createAfterUpdate(folder, group['identifier'])) fireEvent('movie.update_info', identifier = group['identifier'], on_complete = self.createAfterUpdate(folder, group['identifier']))
else:
self.updateProgress(folder)
return addToLibrary return addToLibrary
@@ -228,7 +228,6 @@ class Manage(Plugin):
if not self.in_progress or self.shuttingDown(): if not self.in_progress or self.shuttingDown():
return return
self.updateProgress(folder)
total = self.in_progress[folder]['total'] total = self.in_progress[folder]['total']
movie_dict = fireEvent('media.get', identifier, single = True) movie_dict = fireEvent('media.get', identifier, single = True)
@@ -236,10 +235,11 @@ class Manage(Plugin):
return afterUpdate return afterUpdate
def updateProgress(self, folder): def updateProgress(self, folder, to_go):
pr = self.in_progress[folder] pr = self.in_progress[folder]
pr['to_go'] -= 1 if to_go < pr['to_go']:
pr['to_go'] = to_go
avg = (time.time() - pr['started']) / (pr['total'] - pr['to_go']) avg = (time.time() - pr['started']) / (pr['total'] - pr['to_go'])
pr['eta'] = tryInt(avg * pr['to_go']) pr['eta'] = tryInt(avg * pr['to_go'])
@@ -254,7 +254,7 @@ class Manage(Plugin):
return [] return []
def scanFilesToLibrary(self, folder = None, files = None): def scanFilesToLibrary(self, folder = None, files = None, release_download = None):
folder = os.path.normpath(folder) folder = os.path.normpath(folder)
@@ -263,7 +263,10 @@ class Manage(Plugin):
if groups: if groups:
for group in groups.values(): for group in groups.values():
if group.get('media'): if group.get('media'):
fireEvent('release.add', group = group) if release_download and release_download.get('release_id'):
fireEvent('release.add', group = group, update_id = release_download.get('release_id'))
else:
fireEvent('release.add', group = group)
def getDiskSpace(self): def getDiskSpace(self):
+39 -23
View File
@@ -104,7 +104,7 @@ class Release(Plugin):
elif rel['status'] in ['snatched', 'downloaded']: elif rel['status'] in ['snatched', 'downloaded']:
self.updateStatus(rel['_id'], status = 'ignore') self.updateStatus(rel['_id'], status = 'ignore')
def add(self, group, update_info = True): def add(self, group, update_info = True, update_id = None):
try: try:
db = get_db() db = get_db()
@@ -120,27 +120,43 @@ class Release(Plugin):
'profile_id': None, 'profile_id': None,
}, search_after = False, update_after = update_info, notify_after = False, status = 'done', single = True) }, search_after = False, update_after = update_info, notify_after = False, status = 'done', single = True)
# Add Release release = None
release = { if update_id:
'_t': 'release', try:
'media_id': media['_id'], release = db.get('id', update_id)
'identifier': release_identifier, release.update({
'quality': group['meta_data']['quality'].get('identifier'), 'identifier': release_identifier,
'is_3d': group['meta_data']['quality'].get('is_3d', 0), 'last_edit': int(time.time()),
'last_edit': int(time.time()), 'status': 'done',
'status': 'done' })
} except:
try: log.error('Failed updating existing release: %s', traceback.format_exc())
r = db.get('release_identifier', release_identifier, with_doc = True)['doc'] else:
r['media_id'] = media['_id']
except:
r = db.insert(release)
# Update with ref and _id # Add Release
release.update({ if not release:
'_id': r['_id'], release = {
'_rev': r['_rev'], '_t': 'release',
}) 'media_id': media['_id'],
'identifier': release_identifier,
'quality': group['meta_data']['quality'].get('identifier'),
'is_3d': group['meta_data']['quality'].get('is_3d', 0),
'last_edit': int(time.time()),
'status': 'done'
}
try:
r = db.get('release_identifier', release_identifier, with_doc = True)['doc']
r['media_id'] = media['_id']
except:
log.error('Failed updating release by identifier: %s', traceback.format_exc())
r = db.insert(release)
# Update with ref and _id
release.update({
'_id': r['_id'],
'_rev': r['_rev'],
})
# Empty out empty file groups # Empty out empty file groups
release['files'] = dict((k, [toUnicode(x) for x in v]) for k, v in group['files'].items() if v) release['files'] = dict((k, [toUnicode(x) for x in v]) for k, v in group['files'].items() if v)
@@ -341,7 +357,7 @@ class Release(Plugin):
return True return True
def tryDownloadResult(self, results, media, quality_custom, manual = False): def tryDownloadResult(self, results, media, quality_custom):
wait_for = False wait_for = False
let_through = False let_through = False
@@ -375,7 +391,7 @@ class Release(Plugin):
wait_for = True wait_for = True
continue continue
downloaded = fireEvent('release.download', data = rel, media = media, manual = manual, single = True) downloaded = fireEvent('release.download', data = rel, media = media, single = True)
if downloaded is True: if downloaded is True:
return True return True
elif downloaded != 'try_next': elif downloaded != 'try_next':
+6 -3
View File
@@ -227,6 +227,7 @@ class Renamer(Plugin):
for group_identifier in groups: for group_identifier in groups:
group = groups[group_identifier] group = groups[group_identifier]
group['release_download'] = None
rename_files = {} rename_files = {}
remove_files = [] remove_files = []
remove_releases = [] remove_releases = []
@@ -456,7 +457,7 @@ class Renamer(Plugin):
mdia['last_edit'] = int(time.time()) mdia['last_edit'] = int(time.time())
db.update(mdia) db.update(mdia)
except Exception as e: except:
log.error('Failed marking movie finished: %s', (traceback.format_exc())) log.error('Failed marking movie finished: %s', (traceback.format_exc()))
# Go over current movie releases # Go over current movie releases
@@ -504,13 +505,15 @@ class Renamer(Plugin):
if release_download['status'] == 'completed': if release_download['status'] == 'completed':
# Set the release to downloaded # Set the release to downloaded
fireEvent('release.update_status', release['_id'], status = 'downloaded', single = True) fireEvent('release.update_status', release['_id'], status = 'downloaded', single = True)
group['release_download'] = release_download
elif release_download['status'] == 'seeding': elif release_download['status'] == 'seeding':
# Set the release to seeding # Set the release to seeding
fireEvent('release.update_status', release['_id'], status = 'seeding', single = True) fireEvent('release.update_status', release['_id'], status = 'seeding', single = True)
elif release.get('identifier') == group['meta_data']['quality']['identifier']: elif release.get('identifier') == group['meta_data']['quality']['identifier']:
# Set the release to downloaded # Set the release to downloaded
fireEvent('release.update_status', release['_id'], status = 'downloaded', single = True) fireEvent('release.update_status', release['_id'], status = 'downloaded', single = True)
group['release_download'] = release_download
# Remove leftover files # Remove leftover files
if not remove_leftovers: # Don't remove anything if not remove_leftovers: # Don't remove anything
+3 -1
View File
@@ -364,6 +364,7 @@ class Scanner(Plugin):
if return_ignored is False and identifier in ignored_identifiers: if return_ignored is False and identifier in ignored_identifiers:
log.debug('Ignore file found, ignoring release: %s', identifier) log.debug('Ignore file found, ignoring release: %s', identifier)
total_found -= 1
continue continue
# Group extra (and easy) files first # Group extra (and easy) files first
@@ -384,6 +385,7 @@ class Scanner(Plugin):
if len(group['files']['movie']) == 0: if len(group['files']['movie']) == 0:
log.error('Couldn\'t find any movie files for %s', identifier) log.error('Couldn\'t find any movie files for %s', identifier)
total_found -= 1
continue continue
log.debug('Getting metadata for %s', identifier) log.debug('Getting metadata for %s', identifier)
@@ -429,7 +431,7 @@ class Scanner(Plugin):
# Notify parent & progress on something found # Notify parent & progress on something found
if on_found: if on_found:
on_found(group, total_found, total_found - len(processed_movies)) on_found(group, total_found, len(valid_files))
# Wait for all the async events calm down a bit # Wait for all the async events calm down a bit
while threading.activeCount() > 100 and not self.shuttingDown(): while threading.activeCount() > 100 and not self.shuttingDown():
@@ -90,7 +90,7 @@ Page.Wizard = new Class({
self.parent(action, params); self.parent(action, params);
self.addEvent('create', function(){ self.addEvent('create', function(){
self.order(); self.orderGroups();
}); });
self.initialized = true; self.initialized = true;
@@ -106,7 +106,7 @@ Page.Wizard = new Class({
}).delay(1) }).delay(1)
}, },
order: function(){ orderGroups: function(){
var self = this; var self = this;
var form = self.el.getElement('.uniForm'); var form = self.el.getElement('.uniForm');
-1
View File
@@ -1,5 +1,4 @@
from __future__ import with_statement from __future__ import with_statement
import traceback
import ConfigParser import ConfigParser
from hashlib import md5 from hashlib import md5
+2 -2
View File
@@ -7,9 +7,9 @@ var ApiClass = new Class({
}, },
request: function(type, options){ request: function(type, options){
var self = this; var self = this,
r_type = self.options.is_remote ? 'JSONP' : 'JSON';
var r_type = self.options.is_remote ? 'JSONP' : 'JSON';
return new Request[r_type](Object.merge({ return new Request[r_type](Object.merge({
'callbackKey': 'callback_func', 'callbackKey': 'callback_func',
'method': 'get', 'method': 'get',
+3 -3
View File
@@ -54,7 +54,7 @@
}, },
pushState: function(e){ pushState: function(e){
if((!e.meta && Browser.Platform.mac) || (!e.control && !Browser.Platform.mac)){ if((!e.meta && Browser.platform.mac) || (!e.control && !Browser.platform.mac)){
(e).preventDefault(); (e).preventDefault();
var url = e.target.get('href'); var url = e.target.get('href');
if(History.getPath() != url) if(History.getPath() != url)
@@ -63,7 +63,7 @@
}, },
isMac: function(){ isMac: function(){
return Browser.Platform.mac return Browser.platform.mac
}, },
createLayout: function(){ createLayout: function(){
@@ -322,7 +322,7 @@
var url = 'http://www.dereferer.org/?' + el.get('href'); var url = 'http://www.dereferer.org/?' + el.get('href');
if(el.get('target') == '_blank' || (e.meta && Browser.Platform.mac) || (e.control && !Browser.Platform.mac)) if(el.get('target') == '_blank' || (e.meta && Browser.platform.mac) || (e.control && !Browser.platform.mac))
window.open(url); window.open(url);
else else
window.location = url; window.location = url;
+252 -137
View File
@@ -20,7 +20,7 @@ description: The heart of MooTools.
license: MIT-style license. license: MIT-style license.
copyright: Copyright (c) 2006-2012 [Valerio Proietti](http://mad4milk.net/). copyright: Copyright (c) 2006-2014 [Valerio Proietti](http://mad4milk.net/).
authors: The MooTools production team (http://mootools.net/developers/) authors: The MooTools production team (http://mootools.net/developers/)
@@ -36,8 +36,8 @@ provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
(function(){ (function(){
this.MooTools = { this.MooTools = {
version: '1.4.5', version: '1.5.0',
build: 'ab8ea8824dc3b24b6666867a2c4ed58ebb762cf0' build: '0f7b690afee9349b15909f33016a25d2e4d9f4e3'
}; };
// typeOf, instanceOf // typeOf, instanceOf
@@ -50,7 +50,7 @@ var typeOf = this.typeOf = function(item){
if (item.nodeType == 1) return 'element'; if (item.nodeType == 1) return 'element';
if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace'; if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
} else if (typeof item.length == 'number'){ } else if (typeof item.length == 'number'){
if (item.callee) return 'arguments'; if ('callee' in item) return 'arguments';
if ('item' in item) return 'collection'; if ('item' in item) return 'collection';
} }
@@ -267,7 +267,7 @@ var force = function(name, object, methods){
if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
fn.call(prototype, prototype[methods[i]], methods[i]); fn.call(prototype, prototype[methods[i]], methods[i]);
} }
for (var key in prototype) fn.call(prototype, prototype[key], key) for (var key in prototype) fn.call(prototype, prototype[key], key);
}; };
} }
@@ -275,7 +275,7 @@ var force = function(name, object, methods){
}; };
force('String', String, [ force('String', String, [
'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search', 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase' 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
])('Array', Array, [ ])('Array', Array, [
'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
@@ -325,11 +325,13 @@ Object.each = Object.forEach;
Array.implement({ Array.implement({
/*<!ES5>*/
forEach: function(fn, bind){ forEach: function(fn, bind){
for (var i = 0, l = this.length; i < l; i++){ for (var i = 0, l = this.length; i < l; i++){
if (i in this) fn.call(bind, this[i], i, this); if (i in this) fn.call(bind, this[i], i, this);
} }
}, },
/*</!ES5>*/
each: function(fn, bind){ each: function(fn, bind){
Array.forEach(this, fn, bind); Array.forEach(this, fn, bind);
@@ -421,7 +423,7 @@ description: Contains Array Prototypes like each, contains, and erase.
license: MIT-style license. license: MIT-style license.
requires: Type requires: [Type]
provides: Array provides: Array
@@ -564,7 +566,7 @@ Array.implement({
if (this.length != 3) return null; if (this.length != 3) return null;
var rgb = this.map(function(value){ var rgb = this.map(function(value){
if (value.length == 1) value += value; if (value.length == 1) value += value;
return value.toInt(16); return parseInt(value, 16);
}); });
return (array) ? rgb : 'rgb(' + rgb + ')'; return (array) ? rgb : 'rgb(' + rgb + ')';
}, },
@@ -594,7 +596,7 @@ description: Contains String Prototypes like camelCase, capitalize, test, and to
license: MIT-style license. license: MIT-style license.
requires: Type requires: [Type, Array]
provides: String provides: String
@@ -603,14 +605,16 @@ provides: String
String.implement({ String.implement({
//<!ES6>
contains: function(string, index){
return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1;
},
//</!ES6>
test: function(regex, params){ test: function(regex, params){
return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this); return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
}, },
contains: function(string, separator){
return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1;
},
trim: function(){ trim: function(){
return String(this).replace(/^\s+|\s+$/g, ''); return String(this).replace(/^\s+|\s+$/g, '');
}, },
@@ -669,6 +673,8 @@ String.implement({
}); });
/* /*
--- ---
@@ -1063,37 +1069,47 @@ provides: [Browser, Window, Document]
var document = this.document; var document = this.document;
var window = document.window = this; var window = document.window = this;
var ua = navigator.userAgent.toLowerCase(), var parse = function(ua, platform){
platform = navigator.platform.toLowerCase(), ua = ua.toLowerCase();
UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0], platform = (platform ? platform.toLowerCase() : '');
mode = UA[1] == 'ie' && document.documentMode;
var Browser = this.Browser = { var UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/) || [null, 'unknown', 0];
extend: Function.prototype.extend, if (UA[1] == 'trident'){
UA[1] = 'ie';
if (UA[4]) UA[2] = UA[4];
} else if (UA[1] == 'crios') {
UA[1] = 'chrome';
}
name: (UA[1] == 'version') ? UA[3] : UA[1], var platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0];
if (platform == 'win') platform = 'windows';
version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]), return {
extend: Function.prototype.extend,
name: (UA[1] == 'version') ? UA[3] : UA[1],
version: parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
platform: platform
};
};
Platform: { var Browser = this.Browser = parse(navigator.userAgent, navigator.platform);
name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]
},
if (Browser.ie){
Browser.version = document.documentMode;
}
Browser.extend({
Features: { Features: {
xpath: !!(document.evaluate), xpath: !!(document.evaluate),
air: !!(window.runtime), air: !!(window.runtime),
query: !!(document.querySelector), query: !!(document.querySelector),
json: !!(window.JSON) json: !!(window.JSON)
}, },
parseUA: parse
});
Plugins: {}
};
Browser[Browser.name] = true;
Browser[Browser.name + parseInt(Browser.version, 10)] = true;
Browser.Platform[Browser.Platform.name] = true;
// Request // Request
@@ -1126,18 +1142,7 @@ Browser.Request = (function(){
Browser.Features.xhr = !!(Browser.Request); Browser.Features.xhr = !!(Browser.Request);
// Flash detection
var version = (Function.attempt(function(){
return navigator.plugins['Shockwave Flash'].description;
}, function(){
return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
}) || '0 r0').match(/\d+/g);
Browser.Plugins.Flash = {
version: Number(version[0] || '0.' + version[1]) || 0,
build: Number(version[2]) || 0
};
// String scripts // String scripts
@@ -1756,7 +1761,7 @@ local.setDocument = function(document){
// native matchesSelector function // native matchesSelector function
features.nativeMatchesSelector = root.matchesSelector || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector; features.nativeMatchesSelector = root.matches || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
if (features.nativeMatchesSelector) try { if (features.nativeMatchesSelector) try {
// if matchesSelector trows errors on incorrect sintaxes we can use it // if matchesSelector trows errors on incorrect sintaxes we can use it
features.nativeMatchesSelector.call(root, ':slick'); features.nativeMatchesSelector.call(root, ':slick');
@@ -2590,12 +2595,12 @@ license: MIT-style license.
requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder] requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
provides: [Element, Elements, $, $$, Iframe, Selectors] provides: [Element, Elements, $, $$, IFrame, Selectors]
... ...
*/ */
var Element = function(tag, props){ var Element = this.Element = function(tag, props){
var konstructor = Element.Constructors[tag]; var konstructor = Element.Constructors[tag];
if (konstructor) return konstructor(props); if (konstructor) return konstructor(props);
if (typeof tag != 'string') return document.id(tag).set(props); if (typeof tag != 'string') return document.id(tag).set(props);
@@ -2779,7 +2784,7 @@ Array.mirror(Elements);
/*<ltIE8>*/ /*<ltIE8>*/
var createElementAcceptsHTML; var createElementAcceptsHTML;
try { try {
createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x'); createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
} catch (e){} } catch (e){}
var escapeQuotes = function(html){ var escapeQuotes = function(html){
@@ -3112,7 +3117,28 @@ var pollutesGetAttribute = (function(div){
return (div.getAttribute('random') == 'attribute'); return (div.getAttribute('random') == 'attribute');
})(document.createElement('div')); })(document.createElement('div'));
/* <ltIE9> */ var hasCloneBug = (function(test){
test.innerHTML = '<object><param name="should_fix" value="the unknown"></object>';
return test.cloneNode(true).firstChild.childNodes.length != 1;
})(document.createElement('div'));
/* </ltIE9> */
var hasClassList = !!document.createElement('div').classList;
var classes = function(className){
var classNames = (className || '').clean().split(" "), uniques = {};
return classNames.filter(function(className){
if (className !== "" && !uniques[className]) return uniques[className] = className;
});
};
var addToClassList = function(name){
this.classList.add(name);
};
var removeFromClassList = function(name){
this.classList.remove(name);
};
Element.implement({ Element.implement({
@@ -3122,7 +3148,8 @@ Element.implement({
setter(this, value); setter(this, value);
} else { } else {
/* <ltIE9> */ /* <ltIE9> */
if (pollutesGetAttribute) var attributeWhiteList = this.retrieve('$attributeWhiteList', {}); var attributeWhiteList;
if (pollutesGetAttribute) attributeWhiteList = this.retrieve('$attributeWhiteList', {});
/* </ltIE9> */ /* </ltIE9> */
if (value == null){ if (value == null){
@@ -3194,17 +3221,27 @@ Element.implement({
return this; return this;
}, },
hasClass: function(className){ hasClass: hasClassList ? function(className){
return this.classList.contains(className);
} : function(className){
return this.className.clean().contains(className, ' '); return this.className.clean().contains(className, ' ');
}, },
addClass: function(className){ addClass: hasClassList ? function(className){
if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean(); classes(className).forEach(addToClassList, this);
return this;
} : function(className){
this.className = classes(className + ' ' + this.className).join(' ');
return this; return this;
}, },
removeClass: function(className){ removeClass: hasClassList ? function(className){
this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1'); classes(className).forEach(removeFromClassList, this);
return this;
} : function(className){
var classNames = classes(this.className);
classes(className).forEach(classNames.erase, classNames);
this.className = classNames.join(' ');
return this; return this;
}, },
@@ -3279,6 +3316,37 @@ Element.implement({
}); });
// appendHTML
var appendInserters = {
before: 'beforeBegin',
after: 'afterEnd',
bottom: 'beforeEnd',
top: 'afterBegin',
inside: 'beforeEnd'
};
Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){
this.insertAdjacentHTML(appendInserters[where || 'bottom'], html);
return this;
} : function(html, where){
var temp = new Element('div', {html: html}),
children = temp.childNodes,
fragment = temp.firstChild;
if (!fragment) return this;
if (children.length > 1){
fragment = document.createDocumentFragment();
for (var i = 0, l = children.length; i < l; i++){
fragment.appendChild(children[i]);
}
}
inserters[where || 'bottom'](fragment, this);
return this;
});
var collected = {}, storage = {}; var collected = {}, storage = {};
var get = function(uid){ var get = function(uid){
@@ -3344,7 +3412,7 @@ Element.implement({
} }
/*<ltIE9>*/ /*<ltIE9>*/
if (Browser.ie){ if (hasCloneBug){
var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object'); var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML; for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
} }
@@ -3357,13 +3425,7 @@ Element.implement({
[Element, Window, Document].invoke('implement', { [Element, Window, Document].invoke('implement', {
addListener: function(type, fn){ addListener: function(type, fn){
if (type == 'unload'){ if (window.attachEvent && !window.addEventListener){
var old = fn, self = this;
fn = function(){
self.removeListener('unload', fn);
old();
};
} else {
collected[Slick.uidOf(this)] = this; collected[Slick.uidOf(this)] = this;
} }
if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]); if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
@@ -3398,10 +3460,14 @@ Element.implement({
}); });
/*<ltIE9>*/ /*<ltIE9>*/
if (window.attachEvent && !window.addEventListener) window.addListener('unload', function(){ if (window.attachEvent && !window.addEventListener){
Object.each(collected, clean); var gc = function(){
if (window.CollectGarbage) CollectGarbage(); Object.each(collected, clean);
}); if (window.CollectGarbage) CollectGarbage();
window.removeListener('unload', gc);
}
window.addListener('unload', gc);
}
/*</ltIE9>*/ /*</ltIE9>*/
Element.Properties = {}; Element.Properties = {};
@@ -3446,11 +3512,13 @@ Element.Properties.html = {
}; };
var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true;
/*<ltIE9>*/ /*<ltIE9>*/
// technique by jdbarlett - http://jdbartlett.com/innershiv/ // technique by jdbarlett - http://jdbartlett.com/innershiv/
var div = document.createElement('div'); var div = document.createElement('div');
div.innerHTML = '<nav></nav>'; div.innerHTML = '<nav></nav>';
var supportsHTML5Elements = (div.childNodes.length == 1); supportsHTML5Elements = (div.childNodes.length == 1);
if (!supportsHTML5Elements){ if (!supportsHTML5Elements){
var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '), var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '),
fragment = document.createDocumentFragment(), l = tags.length; fragment = document.createDocumentFragment(), l = tags.length;
@@ -3460,7 +3528,7 @@ div = null;
/*</ltIE9>*/ /*</ltIE9>*/
/*<IE>*/ /*<IE>*/
var supportsTableInnerHTML = Function.attempt(function(){ supportsTableInnerHTML = Function.attempt(function(){
var table = document.createElement('table'); var table = document.createElement('table');
table.innerHTML = '<tr><td></td></tr>'; table.innerHTML = '<tr><td></td></tr>';
return true; return true;
@@ -3469,7 +3537,7 @@ var supportsTableInnerHTML = Function.attempt(function(){
/*<ltFF4>*/ /*<ltFF4>*/
var tr = document.createElement('tr'), html = '<td></td>'; var tr = document.createElement('tr'), html = '<td></td>';
tr.innerHTML = html; tr.innerHTML = html;
var supportsTRInnerHTML = (tr.innerHTML == html); supportsTRInnerHTML = (tr.innerHTML == html);
tr = null; tr = null;
/*</ltFF4>*/ /*</ltFF4>*/
@@ -3514,11 +3582,12 @@ if (testForm.firstChild.value != 's') Element.Properties.value = {
var tag = this.get('tag'); var tag = this.get('tag');
if (tag != 'select') return this.setProperty('value', value); if (tag != 'select') return this.setProperty('value', value);
var options = this.getElements('option'); var options = this.getElements('option');
value = String(value);
for (var i = 0; i < options.length; i++){ for (var i = 0; i < options.length; i++){
var option = options[i], var option = options[i],
attr = option.getAttributeNode('value'), attr = option.getAttributeNode('value'),
optionValue = (attr && attr.specified) ? option.value : option.get('text'); optionValue = (attr && attr.specified) ? option.value : option.get('text');
if (optionValue == value) return option.selected = true; if (optionValue === value) return option.selected = true;
} }
}, },
@@ -3572,17 +3641,24 @@ provides: Element.Style
(function(){ (function(){
var html = document.html; var html = document.html, el;
//<ltIE9> //<ltIE9>
// Check for oldIE, which does not remove styles when they're set to null // Check for oldIE, which does not remove styles when they're set to null
var el = document.createElement('div'); el = document.createElement('div');
el.style.color = 'red'; el.style.color = 'red';
el.style.color = null; el.style.color = null;
var doesNotRemoveStyles = el.style.color == 'red'; var doesNotRemoveStyles = el.style.color == 'red';
// check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color)
var border = '1px solid #123abc';
el.style.border = border;
var returnsBordersInWrongOrder = el.style.border != border;
el = null; el = null;
//</ltIE9> //</ltIE9>
var hasGetComputedStyle = !!window.getComputedStyle;
Element.Properties.styles = {set: function(styles){ Element.Properties.styles = {set: function(styles){
this.setStyles(styles); this.setStyles(styles);
}}; }};
@@ -3596,16 +3672,25 @@ var setVisibility = function(element, opacity){
element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden'; element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
}; };
//<ltIE9>
var setFilter = function(element, regexp, value){
var style = element.style,
filter = style.filter || element.getComputedStyle('filter') || '';
style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim();
if (!style.filter) style.removeAttribute('filter');
};
//</ltIE9>
var setOpacity = (hasOpacity ? function(element, opacity){ var setOpacity = (hasOpacity ? function(element, opacity){
element.style.opacity = opacity; element.style.opacity = opacity;
} : (hasFilter ? function(element, opacity){ } : (hasFilter ? function(element, opacity){
var style = element.style; if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
if (!element.currentStyle || !element.currentStyle.hasLayout) style.zoom = 1; if (opacity == null || opacity == 1){
if (opacity == null || opacity == 1) opacity = ''; setFilter(element, reAlpha, '');
else opacity = 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')'; if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)');
var filter = style.filter || element.getComputedStyle('filter') || ''; } else {
style.filter = reAlpha.test(filter) ? filter.replace(reAlpha, opacity) : filter + opacity; setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')');
if (!style.filter) style.removeAttribute('filter'); }
} : setVisibility)); } : setVisibility));
var getOpacity = (hasOpacity ? function(element){ var getOpacity = (hasOpacity ? function(element){
@@ -3622,15 +3707,27 @@ var getOpacity = (hasOpacity ? function(element){
return opacity; return opacity;
})); }));
var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat'; var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat',
namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'},
hasBackgroundPositionXY = (html.style.backgroundPositionX != null);
//<ltIE9>
var removeStyle = function(style, property){
if (property == 'backgroundPosition'){
style.removeAttribute(property + 'X');
property += 'Y';
}
style.removeAttribute(property);
};
//</ltIE9>
Element.implement({ Element.implement({
getComputedStyle: function(property){ getComputedStyle: function(property){
if (this.currentStyle) return this.currentStyle[property.camelCase()]; if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[property.camelCase()];
var defaultView = Element.getDocument(this).defaultView, var defaultView = Element.getDocument(this).defaultView,
computed = defaultView ? defaultView.getComputedStyle(this, null) : null; computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : null; return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : '';
}, },
setStyle: function(property, value){ setStyle: function(property, value){
@@ -3652,7 +3749,7 @@ Element.implement({
this.style[property] = value; this.style[property] = value;
//<ltIE9> //<ltIE9>
if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){ if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
this.style.removeAttribute(property); removeStyle(this.style, property);
} }
//</ltIE9> //</ltIE9>
return this; return this;
@@ -3663,20 +3760,25 @@ Element.implement({
property = (property == 'float' ? floatName : property).camelCase(); property = (property == 'float' ? floatName : property).camelCase();
var result = this.style[property]; var result = this.style[property];
if (!result || property == 'zIndex'){ if (!result || property == 'zIndex'){
result = []; if (Element.ShortStyles.hasOwnProperty(property)){
for (var style in Element.ShortStyles){ result = [];
if (property != style) continue; for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s));
for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
return result.join(' '); return result.join(' ');
} }
result = this.getComputedStyle(property); result = this.getComputedStyle(property);
} }
if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){
return result.replace(/(top|right|bottom|left)/g, function(position){
return namedPositions[position];
}) || '0px';
}
if (!result && property == 'backgroundPosition') return '0px 0px';
if (result){ if (result){
result = String(result); result = String(result);
var color = result.match(/rgba?\([\d\s,]+\)/); var color = result.match(/rgba?\([\d\s,]+\)/);
if (color) result = result.replace(color[0], color[0].rgbToHex()); if (color) result = result.replace(color[0], color[0].rgbToHex());
} }
if (Browser.opera || Browser.ie){ if (!hasGetComputedStyle && !this.style[property]){
if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){ if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0; var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
values.each(function(value){ values.each(function(value){
@@ -3684,10 +3786,15 @@ Element.implement({
}, this); }, this);
return this['offset' + property.capitalize()] - size + 'px'; return this['offset' + property.capitalize()] - size + 'px';
} }
if (Browser.ie && (/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){ if ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
return '0px'; return '0px';
} }
} }
//<ltIE9>
if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){
return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1');
}
//</ltIE9>
return result; return result;
}, },
@@ -3709,7 +3816,7 @@ Element.implement({
Element.Styles = { Element.Styles = {
left: '@px', top: '@px', bottom: '@px', right: '@px', left: '@px', top: '@px', bottom: '@px', right: '@px',
width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px', width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)', backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)', 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(@, @, @)', 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(@, @, @)', borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
@@ -3738,6 +3845,7 @@ Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, bor
Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)'; Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
}); });
if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'};
})(); })();
@@ -3779,7 +3887,7 @@ var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
if (type.indexOf('key') == 0){ if (type.indexOf('key') == 0){
var code = this.code = (event.which || event.keyCode); var code = this.code = (event.which || event.keyCode);
this.key = _keys[code]; this.key = _keys[code];
if (type == 'keydown'){ if (type == 'keydown' || type == 'keyup'){
if (code > 111 && code < 124) this.key = 'f' + (code - 111); if (code > 111 && code < 124) this.key = 'f' + (code - 111);
else if (code > 95 && code < 106) this.key = code - 96; else if (code > 95 && code < 106) this.key = code - 96;
} }
@@ -4001,23 +4109,27 @@ Element.NativeEvents = {
gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
hashchange: 1, popstate: 2, // history
error: 1, abort: 1, scroll: 1 //misc error: 1, abort: 1, scroll: 1 //misc
}; };
Element.Events = {mousewheel: { Element.Events = {
base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel' mousewheel: {
}}; base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll'
}
};
var check = function(event){
var related = event.relatedTarget;
if (related == null) return true;
if (!related) return false;
return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
};
if ('onmouseenter' in document.documentElement){ if ('onmouseenter' in document.documentElement){
Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2; Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2;
Element.MouseenterCheck = check;
} else { } else {
var check = function(event){
var related = event.relatedTarget;
if (related == null) return true;
if (!related) return false;
return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
};
Element.Events.mouseenter = { Element.Events.mouseenter = {
base: 'mouseover', base: 'mouseover',
condition: check condition: check
@@ -4035,12 +4147,12 @@ if (!window.addEventListener){
Element.Events.change = { Element.Events.change = {
base: function(){ base: function(){
var type = this.type; var type = this.type;
return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change' return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change';
}, },
condition: function(event){ condition: function(event){
return this.type != 'radio' || (event.event.propertyName == 'checked' && this.checked); return event.type != 'propertychange' || event.event.propertyName == 'checked';
} }
} };
} }
/*</ltIE9>*/ /*</ltIE9>*/
@@ -4080,10 +4192,12 @@ var bubbleUp = function(self, match, fn, event, target){
var map = { var map = {
mouseenter: { mouseenter: {
base: 'mouseover' base: 'mouseover',
condition: Element.MouseenterCheck
}, },
mouseleave: { mouseleave: {
base: 'mouseout' base: 'mouseout',
condition: Element.MouseenterCheck
}, },
focus: { focus: {
base: 'focus' + (eventListenerSupport ? '' : 'in'), base: 'focus' + (eventListenerSupport ? '' : 'in'),
@@ -4190,8 +4304,8 @@ var delegation = {
}; };
var elementEvent = Element.Events[_type]; var elementEvent = Element.Events[_type];
if (elementEvent && elementEvent.condition){ if (_map.condition || elementEvent && elementEvent.condition){
var __match = match, condition = elementEvent.condition; var __match = match, condition = _map.condition || elementEvent.condition;
match = function(target, event){ match = function(target, event){
return __match(target, event) && condition.call(target, event, type); return __match(target, event) && condition.call(target, event, type);
}; };
@@ -4226,7 +4340,7 @@ var delegation = {
if (_map.remove) _map.remove(this, _uid); if (_map.remove) _map.remove(this, _uid);
delete stored[_uid]; delete stored[_uid];
storage[_type] = stored; storage[_type] = stored;
return removeEvent.call(this, type, delegator); return removeEvent.call(this, type, delegator, _map.capture);
} }
var __uid, s; var __uid, s;
@@ -4344,7 +4458,9 @@ Element.implement({
}, },
getOffsets: function(){ getOffsets: function(){
if (this.getBoundingClientRect && !Browser.Platform.ios){ var hasGetBoundingClientRect = this.getBoundingClientRect;
if (hasGetBoundingClientRect){
var bound = this.getBoundingClientRect(), var bound = this.getBoundingClientRect(),
html = document.id(this.getDocument().documentElement), html = document.id(this.getDocument().documentElement),
htmlScroll = html.getScroll(), htmlScroll = html.getScroll(),
@@ -4364,27 +4480,9 @@ Element.implement({
position.x += element.offsetLeft; position.x += element.offsetLeft;
position.y += element.offsetTop; position.y += element.offsetTop;
if (Browser.firefox){
if (!borderBox(element)){
position.x += leftBorder(element);
position.y += topBorder(element);
}
var parent = element.parentNode;
if (parent && styleString(parent, 'overflow') != 'visible'){
position.x += leftBorder(parent);
position.y += topBorder(parent);
}
} else if (element != this && Browser.safari){
position.x += leftBorder(element);
position.y += topBorder(element);
}
element = element.offsetParent; element = element.offsetParent;
} }
if (Browser.firefox && !borderBox(this)){
position.x -= leftBorder(this);
position.y -= topBorder(this);
}
return position; return position;
}, },
@@ -4666,13 +4764,17 @@ var Fx = this.Fx = new Class({
}, },
resume: function(){ resume: function(){
if ((this.frame < this.frames) && !this.isRunning()) pushInstance.call(this, this.options.fps); if (this.isPaused()) pushInstance.call(this, this.options.fps);
return this; return this;
}, },
isRunning: function(){ isRunning: function(){
var list = instances[this.options.fps]; var list = instances[this.options.fps];
return list && list.contains(this); return list && list.contains(this);
},
isPaused: function(){
return (this.frame < this.frames) && !this.isRunning();
} }
}); });
@@ -4745,7 +4847,7 @@ Fx.CSS = new Class({
from = element.getStyle(property); from = element.getStyle(property);
var unit = this.options.unit; var unit = this.options.unit;
// adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299 // adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299
if (unit && from.slice(-unit.length) != unit && parseFloat(from) != 0){ if (unit && from && typeof from == 'string' && from.slice(-unit.length) != unit && parseFloat(from) != 0){
element.setStyle(property, to + unit); element.setStyle(property, to + unit);
var value = element.getComputedStyle(property); var value = element.getComputedStyle(property);
// IE and Opera support pixelLeft or pixelWidth // IE and Opera support pixelLeft or pixelWidth
@@ -4817,11 +4919,13 @@ Fx.CSS = new Class({
search: function(selector){ search: function(selector){
if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector]; if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$'); var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
Array.each(document.styleSheets, function(sheet, j){
var href = sheet.href; var searchStyles = function(rules){
if (href && href.contains('://') && !href.contains(document.domain)) return;
var rules = sheet.rules || sheet.cssRules;
Array.each(rules, function(rule, i){ Array.each(rules, function(rule, i){
if (rule.media){
searchStyles(rule.rules || rule.cssRules);
return;
}
if (!rule.style) return; if (!rule.style) return;
var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){ var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
return m.toLowerCase(); return m.toLowerCase();
@@ -4833,6 +4937,13 @@ Fx.CSS = new Class({
to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value; to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
}); });
}); });
};
Array.each(document.styleSheets, function(sheet, j){
var href = sheet.href;
if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return;
var rules = sheet.rules || sheet.cssRules;
searchStyles(rules);
}); });
return Fx.CSS.Cache[selector] = to; return Fx.CSS.Cache[selector] = to;
} }
@@ -5369,10 +5480,10 @@ var Request = this.Request = new Class({
if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition); if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
if (this.options.noCache) if (this.options.noCache)
url += (url.contains('?') ? '&' : '?') + String.uniqueID(); url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID();
if (data && method == 'get'){ if (data && (method == 'get' || method == 'delete')){
url += (url.contains('?') ? '&' : '?') + data; url += (url.indexOf('?') > -1 ? '&' : '?') + data;
data = null; data = null;
} }
@@ -5526,10 +5637,14 @@ JSON.encode = JSON.stringify ? function(obj){
return null; return null;
}; };
JSON.secure = true;
JSON.decode = function(string, secure){ JSON.decode = function(string, secure){
if (!string || typeOf(string) != 'string') return null; if (!string || typeOf(string) != 'string') return null;
if (secure || JSON.secure){ if (secure == null) secure = JSON.secure;
if (secure){
if (JSON.parse) return JSON.parse(string); if (JSON.parse) return JSON.parse(string);
if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.'); if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
} }
@@ -1,6 +1,16 @@
// MooTools: the javascript framework. /*
// Load this file's selection again by visiting: http://mootools.net/more/0f75cfbac1aabbedaba7630beef8d10c ---
// Or build this file again with packager using: packager build More/Events.Pseudos More/Date More/Date.Extras More/Element.Forms More/Element.Position More/Element.Shortcuts More/Fx.Scroll More/Fx.Slide More/Sortables More/Request.JSONP More/Request.Periodical MooTools: the javascript framework
web build:
- http://mootools.net/more/0f75cfbac1aabbedaba7630beef8d10c
packager build:
- packager build More/Events.Pseudos More/Date More/Date.Extras More/Element.Forms More/Element.Position More/Element.Shortcuts More/Fx.Scroll More/Fx.Slide More/Sortables More/Request.JSONP More/Request.Periodical
...
*/
/* /*
--- ---
@@ -31,8 +41,8 @@ provides: [MooTools.More]
*/ */
MooTools.More = { MooTools.More = {
'version': '1.4.0.1', version: '1.5.0',
'build': 'a4244edf2aa97ac8a196fc96082dd35af1abab87' build: '73db5e24e6e9c5c87b3a27aebef2248053f7db37'
}; };
@@ -48,7 +58,7 @@ license: MIT-style license
authors: authors:
- Arian Stolwijk - Arian Stolwijk
requires: [Core/Class.Extras, Core/Slick.Parser, More/MooTools.More] requires: [Core/Class.Extras, Core/Slick.Parser, MooTools.More]
provides: [Events.Pseudos] provides: [Events.Pseudos]
@@ -211,7 +221,7 @@ authors:
requires: requires:
- Core/Object - Core/Object
- /MooTools.More - MooTools.More
provides: [Object.Extras] provides: [Object.Extras]
@@ -280,8 +290,8 @@ authors:
requires: requires:
- Core/Events - Core/Events
- /Object.Extras - Object.Extras
- /MooTools.More - MooTools.More
provides: [Locale, Lang] provides: [Locale, Lang]
@@ -444,7 +454,7 @@ authors:
- Aaron Newton - Aaron Newton
requires: requires:
- /Locale - Locale
provides: [Locale.en-US.Date] provides: [Locale.en-US.Date]
@@ -1079,7 +1089,7 @@ authors:
- Scott Kyle - Scott Kyle
requires: requires:
- /Date - Date
provides: [Date.Extras] provides: [Date.Extras]
@@ -1235,10 +1245,8 @@ var special = {
'S': /[ŠŞŚ]/g, 'S': /[ŠŞŚ]/g,
't': /[ťţ]/g, 't': /[ťţ]/g,
'T': /[ŤŢ]/g, 'T': /[ŤŢ]/g,
'ue': /[ü]/g, 'u': /[ùúûůüµ]/g,
'UE': /[Ü]/g, 'U': /[ÙÚÛŮÜ]/g,
'u': /[ùúûůµ]/g,
'U': /[ÙÚÛŮ]/g,
'y': /[ÿý]/g, 'y': /[ÿý]/g,
'Y': /[ŸÝ]/g, 'Y': /[ŸÝ]/g,
'z': /[žźż]/g, 'z': /[žźż]/g,
@@ -1263,7 +1271,16 @@ tidy = {
'-': /[\u2013]/g, '-': /[\u2013]/g,
// '--': /[\u2014]/g, // '--': /[\u2014]/g,
'&raquo;': /[\uFFFD]/g '&raquo;': /[\uFFFD]/g
}; },
conversions = {
ms: 1,
s: 1000,
m: 6e4,
h: 36e5
},
findUnits = /(\d*.?\d+)([msh]+)/;
var walk = function(string, replacements){ var walk = function(string, replacements){
var result = string, key; var result = string, key;
@@ -1325,6 +1342,13 @@ String.implement({
if (trail) string += trail; if (trail) string += trail;
} }
return string; return string;
},
ms: function(){
// "Borrowed" from https://gist.github.com/1503944
var units = findUnits.exec(this);
if (units == null) return Number(this);
return Number(units[1]) * conversions[units[2]];
} }
}); });
@@ -1348,8 +1372,8 @@ authors:
requires: requires:
- Core/Element - Core/Element
- /String.Extras - String.Extras
- /MooTools.More - MooTools.More
provides: [Element.Forms] provides: [Element.Forms]
@@ -1493,7 +1517,7 @@ authors:
requires: requires:
- Core/Element.Style - Core/Element.Style
- Core/Element.Dimensions - Core/Element.Dimensions
- /MooTools.More - MooTools.More
provides: [Element.Measure] provides: [Element.Measure]
@@ -1710,13 +1734,15 @@ var local = Element.Position = {
}, },
setOffsetOption: function(element, options){ setOffsetOption: function(element, options){
var parentOffset = {x: 0, y: 0}, var parentOffset = {x: 0, y: 0};
offsetParent = element.measure(function(){ var parentScroll = {x: 0, y: 0};
return document.id(this.getOffsetParent()); var offsetParent = element.measure(function(){
}), return document.id(this.getOffsetParent());
parentScroll = offsetParent.getScroll(); });
if (!offsetParent || offsetParent == element.getDocument().body) return; if (!offsetParent || offsetParent == element.getDocument().body) return;
parentScroll = offsetParent.getScroll();
parentOffset = offsetParent.measure(function(){ parentOffset = offsetParent.measure(function(){
var position = this.getPosition(); var position = this.getPosition();
if (this.getStyle('position') == 'fixed'){ if (this.getStyle('position') == 'fixed'){
@@ -1896,7 +1922,7 @@ authors:
requires: requires:
- Core/Element.Style - Core/Element.Style
- /MooTools.More - MooTools.More
provides: [Element.Shortcuts] provides: [Element.Shortcuts]
@@ -1976,7 +2002,7 @@ requires:
- Core/Fx - Core/Fx
- Core/Element.Event - Core/Element.Event
- Core/Element.Dimensions - Core/Element.Dimensions
- /MooTools.More - MooTools.More
provides: [Fx.Scroll] provides: [Fx.Scroll]
@@ -2014,7 +2040,6 @@ Fx.Scroll = new Class({
set: function(){ set: function(){
var now = Array.flatten(arguments); var now = Array.flatten(arguments);
if (Browser.firefox) now = [Math.round(now[0]), Math.round(now[1])]; // not needed anymore in newer firefox versions
this.element.scrollTo(now[0], now[1]); this.element.scrollTo(now[0], now[1]);
return this; return this;
}, },
@@ -2148,7 +2173,7 @@ authors:
requires: requires:
- Core/Fx - Core/Fx
- Core/Element.Style - Core/Element.Style
- /MooTools.More - MooTools.More
provides: [Fx.Slide] provides: [Fx.Slide]
@@ -2325,7 +2350,7 @@ requires:
- Core/Element.Event - Core/Element.Event
- Core/Element.Style - Core/Element.Style
- Core/Element.Dimensions - Core/Element.Dimensions
- /MooTools.More - MooTools.More
provides: [Drag] provides: [Drag]
... ...
@@ -2371,10 +2396,10 @@ var Drag = new Class({
this.mouse = {'now': {}, 'pos': {}}; this.mouse = {'now': {}, 'pos': {}};
this.value = {'start': {}, 'now': {}}; this.value = {'start': {}, 'now': {}};
this.selection = (Browser.ie) ? 'selectstart' : 'mousedown'; this.selection = 'selectstart' in document ? 'selectstart' : 'mousedown';
if (Browser.ie && !Drag.ondragstartFixed){ if ('ondragstart' in document && !('FileReader' in window) && !Drag.ondragstartFixed){
document.ondragstart = Function.from(false); document.ondragstart = Function.from(false);
Drag.ondragstartFixed = true; Drag.ondragstartFixed = true;
} }
@@ -2559,7 +2584,7 @@ authors:
requires: requires:
- Core/Element.Dimensions - Core/Element.Dimensions
- /Drag - Drag
provides: [Drag.Move] provides: [Drag.Move]
@@ -2586,10 +2611,7 @@ Drag.Move = new Class({
element = this.element; element = this.element;
this.droppables = $$(this.options.droppables); this.droppables = $$(this.options.droppables);
this.container = document.id(this.options.container); this.setContainer(this.options.container);
if (this.container && typeOf(this.container) != 'element')
this.container = document.id(this.container.getDocument().body);
if (this.options.style){ if (this.options.style){
if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){ if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){
@@ -2607,6 +2629,13 @@ Drag.Move = new Class({
this.overed = null; this.overed = null;
}, },
setContainer: function(container) {
this.container = document.id(container);
if (this.container && typeOf(this.container) != 'element'){
this.container = document.id(this.container.getDocument().body);
}
},
start: function(event){ start: function(event){
if (this.container) this.options.limit = this.calculateLimit(); if (this.container) this.options.limit = this.calculateLimit();
@@ -2670,7 +2699,9 @@ Drag.Move = new Class({
if (container != offsetParent){ if (container != offsetParent){
left += containerMargin.left + offsetParentPadding.left; left += containerMargin.left + offsetParentPadding.left;
top += ((Browser.ie6 || Browser.ie7) ? 0 : containerMargin.top) + offsetParentPadding.top; if (!offsetParentPadding.left && left < 0) left = 0;
top += offsetParent == document.body ? 0 : containerMargin.top + offsetParentPadding.top;
if (!offsetParentPadding.top && top < 0) top = 0;
} }
} else { } else {
left -= elementMargin.left; left -= elementMargin.left;
@@ -2754,7 +2785,7 @@ authors:
requires: requires:
- Core/Fx.Morph - Core/Fx.Morph
- /Drag.Move - Drag.Move
provides: [Sortables] provides: [Sortables]
@@ -2773,7 +2804,8 @@ var Sortables = new Class({
clone: false, clone: false,
revert: false, revert: false,
handle: false, handle: false,
dragOptions: {} dragOptions: {},
unDraggableTags: ['button', 'input', 'a', 'textarea', 'select', 'option']
}, },
initialize: function(lists, options){ initialize: function(lists, options){
@@ -2840,6 +2872,24 @@ var Sortables = new Class({
}, this)); }, this));
}, },
getDroppableCoordinates: function (element){
var offsetParent = element.getOffsetParent();
var position = element.getPosition(offsetParent);
var scroll = {
w: window.getScroll(),
offsetParent: offsetParent.getScroll()
};
position.x += scroll.offsetParent.x;
position.y += scroll.offsetParent.y;
if (offsetParent.getStyle('position') == 'fixed'){
position.x -= scroll.w.x;
position.y -= scroll.w.y;
}
return position;
},
getClone: function(event, element){ getClone: function(event, element){
if (!this.options.clone) return new Element(element.tagName).inject(document.body); if (!this.options.clone) return new Element(element.tagName).inject(document.body);
if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list); if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
@@ -2859,7 +2909,7 @@ var Sortables = new Class({
}); });
} }
return clone.inject(this.list).setPosition(element.getPosition(element.getOffsetParent())); return clone.inject(this.list).setPosition(this.getDroppableCoordinates(this.element));
}, },
getDroppables: function(){ getDroppables: function(){
@@ -2884,7 +2934,7 @@ var Sortables = new Class({
if ( if (
!this.idle || !this.idle ||
event.rightClick || event.rightClick ||
['button', 'input', 'a', 'textarea'].contains(event.target.get('tag')) (!this.options.handle && this.options.unDraggableTags.contains(event.target.get('tag')))
) return; ) return;
this.idle = false; this.idle = false;
@@ -2915,14 +2965,16 @@ var Sortables = new Class({
end: function(){ end: function(){
this.drag.detach(); this.drag.detach();
this.element.setStyle('opacity', this.opacity); this.element.setStyle('opacity', this.opacity);
var self = this;
if (this.effect){ if (this.effect){
var dim = this.element.getStyles('width', 'height'), var dim = this.element.getStyles('width', 'height'),
clone = this.clone, clone = this.clone,
pos = clone.computePosition(this.element.getPosition(this.clone.getOffsetParent())); pos = clone.computePosition(this.getDroppableCoordinates(clone));
var destroy = function(){ var destroy = function(){
this.removeEvent('cancel', destroy); this.removeEvent('cancel', destroy);
clone.destroy(); clone.destroy();
self.reset();
}; };
this.effect.element = clone; this.effect.element = clone;
@@ -2935,8 +2987,9 @@ var Sortables = new Class({
}).addEvent('cancel', destroy).chain(destroy); }).addEvent('cancel', destroy).chain(destroy);
} else { } else {
this.clone.destroy(); this.clone.destroy();
self.reset();
} }
this.reset();
}, },
reset: function(){ reset: function(){
@@ -3125,7 +3178,7 @@ authors:
requires: requires:
- Core/Request - Core/Request
- /MooTools.More - MooTools.More
provides: [Request.Periodical] provides: [Request.Periodical]
+11 -11
View File
@@ -1,15 +1,15 @@
var Question = new Class( { var Question = new Class( {
initialize : function(question, hint, answers) { initialize : function(question, hint, answers) {
var self = this var self = this;
self.question = question self.question = question;
self.hint = hint self.hint = hint;
self.answers = answers self.answers = answers;
self.createQuestion(); self.createQuestion();
self.answers.each(function(answer) { self.answers.each(function(answer) {
self.createAnswer(answer) self.createAnswer(answer);
}) })
}, },
@@ -29,14 +29,14 @@ var Question = new Class( {
}, },
createAnswer : function(options) { createAnswer : function(options) {
var self = this var self = this;
var answer = new Element('a', Object.merge(options, { var answer = new Element('a', Object.merge(options, {
'class' : 'answer button '+(options['class'] || '')+(options['cancel'] ? ' cancel' : '') 'class' : 'answer button '+(options['class'] || '')+(options['cancel'] ? ' cancel' : '')
})).inject(this.container) })).inject(this.container);
if (options.cancel) { if (options.cancel) {
answer.addEvent('click', self.close.bind(self)) answer.addEvent('click', self.close.bind(self));
} }
else if (options.request) { else if (options.request) {
answer.addEvent('click', function(e){ answer.addEvent('click', function(e){
@@ -44,7 +44,7 @@ var Question = new Class( {
new Request(Object.merge(options, { new Request(Object.merge(options, {
'url': options.href, 'url': options.href,
'onComplete': function() { 'onComplete': function() {
(options.onComplete || function(){})() (options.onComplete || function(){})();
self.close(); self.close();
} }
})).send(); })).send();
@@ -59,7 +59,7 @@ var Question = new Class( {
}, },
toElement : function() { toElement : function() {
return this.container return this.container;
} }
}) });
+2 -2
View File
@@ -27,8 +27,8 @@ Page.Settings = new Class({
}, },
openTab: function(action){ openTab: function(action){
var self = this; var self = this,
var action = (action == 'index' ? 'about' : action) || self.action; action = (action == 'index' ? 'about' : action) || self.action;
if(self.current) if(self.current)
self.toggleTab(self.current, true); self.toggleTab(self.current, true);
+26
View File
@@ -8,6 +8,7 @@ body, html {
padding: 0; padding: 0;
background: #4e5969; background: #4e5969;
-webkit-font-smoothing: subpixel-antialiased; -webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: grayscale;
} }
body { overflow-y: scroll; } body { overflow-y: scroll; }
body.noscroll { overflow: hidden; } body.noscroll { overflow: hidden; }
@@ -52,6 +53,30 @@ input:-moz-placeholder {
font-style: italic; font-style: italic;
} }
.tiny_scroll {
overflow: hidden;
}
.tiny_scroll:hover {
overflow-y: auto;
}
.tiny_scroll::-webkit-scrollbar {
width: 5px;
}
.tiny_scroll::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.4);
-webkit-border-radius: 5px;
border-radius: 5px;
}
.tiny_scroll::-webkit-scrollbar-thumb {
-webkit-border-radius: 5px;
border-radius: 5px;
background: rgba(255,255,255,0.3);
}
a img { a img {
border:none; border:none;
} }
@@ -166,6 +191,7 @@ body > .spinner, .mask{
text-transform: none; text-transform: none;
line-height: 1; line-height: 1;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 15px; font-size: 15px;
color: #FFF; color: #FFF;
} }