Merge branch 'refs/heads/develop'
@@ -79,7 +79,7 @@ class Core(Plugin):
|
||||
|
||||
def shutdown():
|
||||
self.initShutdown()
|
||||
IOLoop.instance().add_callback(shutdown)
|
||||
IOLoop.current().add_callback(shutdown)
|
||||
|
||||
return 'shutdown'
|
||||
|
||||
@@ -89,7 +89,7 @@ class Core(Plugin):
|
||||
|
||||
def restart():
|
||||
self.initShutdown(restart = True)
|
||||
IOLoop.instance().add_callback(restart)
|
||||
IOLoop.current().add_callback(restart)
|
||||
|
||||
return 'restarting'
|
||||
|
||||
@@ -128,7 +128,7 @@ class Core(Plugin):
|
||||
log.debug('Save to shutdown/restart')
|
||||
|
||||
try:
|
||||
IOLoop.instance().stop()
|
||||
IOLoop.current().stop()
|
||||
except RuntimeError:
|
||||
pass
|
||||
except:
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import ss
|
||||
from couchpotato.core.helpers.variable import tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
from minify.cssmin import cssmin
|
||||
from minify.jsmin import jsmin
|
||||
import cssprefixer
|
||||
import os
|
||||
import traceback
|
||||
|
||||
@@ -23,7 +24,6 @@ class ClientScript(Plugin):
|
||||
'script': [
|
||||
'scripts/library/mootools.js',
|
||||
'scripts/library/mootools_more.js',
|
||||
'scripts/library/prefix_free.js',
|
||||
'scripts/library/uniform.js',
|
||||
'scripts/library/form_replacement/form_check.js',
|
||||
'scripts/library/form_replacement/form_radio.js',
|
||||
@@ -69,7 +69,8 @@ class ClientScript(Plugin):
|
||||
addEvent('clientscript.get_styles', self.getStyles)
|
||||
addEvent('clientscript.get_scripts', self.getScripts)
|
||||
|
||||
addEvent('app.load', self.minify)
|
||||
if not Env.get('dev'):
|
||||
addEvent('app.load', self.minify)
|
||||
|
||||
self.addCore()
|
||||
|
||||
@@ -108,8 +109,10 @@ class ClientScript(Plugin):
|
||||
if file_type == 'script':
|
||||
data = jsmin(f)
|
||||
else:
|
||||
data = cssmin(f)
|
||||
data = cssprefixer.process(f, debug = False, minify = True)
|
||||
data = data.replace('../images/', '../static/images/')
|
||||
data = data.replace('../fonts/', '../static/fonts/')
|
||||
data = data.replace('../../static/', '../static/') # Replace inside plugins
|
||||
|
||||
raw.append({'file': file_path, 'date': int(os.path.getmtime(file_path)), 'data': data})
|
||||
|
||||
@@ -119,7 +122,7 @@ class ClientScript(Plugin):
|
||||
data += self.comment.get(file_type) % (r.get('file'), r.get('date'))
|
||||
data += r.get('data') + '\n\n'
|
||||
|
||||
self.createFile(out, data.strip())
|
||||
self.createFile(out, ss(data.strip()))
|
||||
|
||||
if not self.minified.get(file_type):
|
||||
self.minified[file_type] = {}
|
||||
|
||||
@@ -15,6 +15,7 @@ import tarfile
|
||||
import time
|
||||
import traceback
|
||||
import version
|
||||
import zipfile
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -263,11 +264,11 @@ class SourceUpdater(BaseUpdater):
|
||||
def doUpdate(self):
|
||||
|
||||
try:
|
||||
url = 'https://github.com/%s/%s/tarball/%s' % (self.repo_user, self.repo_name, self.branch)
|
||||
destination = os.path.join(Env.get('cache_dir'), self.update_version.get('hash') + '.tar.gz')
|
||||
extracted_path = os.path.join(Env.get('cache_dir'), 'temp_updater')
|
||||
download_data = fireEvent('cp.source_url', repo = self.repo_user, repo_name = self.repo_name, branch = self.branch, single = True)
|
||||
destination = os.path.join(Env.get('cache_dir'), self.update_version.get('hash')) + '.' + download_data.get('type')
|
||||
|
||||
destination = fireEvent('file.download', url = url, dest = destination, single = True)
|
||||
extracted_path = os.path.join(Env.get('cache_dir'), 'temp_updater')
|
||||
destination = fireEvent('file.download', url = download_data.get('url'), dest = destination, single = True)
|
||||
|
||||
# Cleanup leftover from last time
|
||||
if os.path.isdir(extracted_path):
|
||||
@@ -275,9 +276,14 @@ class SourceUpdater(BaseUpdater):
|
||||
self.makeDir(extracted_path)
|
||||
|
||||
# Extract
|
||||
tar = tarfile.open(destination)
|
||||
tar.extractall(path = extracted_path)
|
||||
tar.close()
|
||||
if download_data.get('type') == 'zip':
|
||||
zip = zipfile.ZipFile(destination)
|
||||
zip.extractall(extracted_path)
|
||||
else:
|
||||
tar = tarfile.open(destination)
|
||||
tar.extractall(path = extracted_path)
|
||||
tar.close()
|
||||
|
||||
os.remove(destination)
|
||||
|
||||
if self.replaceWith(os.path.join(extracted_path, os.listdir(extracted_path)[0])):
|
||||
|
||||
@@ -5,7 +5,7 @@ var UpdaterBase = new Class({
|
||||
initialize: function(){
|
||||
var self = this;
|
||||
|
||||
App.addEvent('load', self.info.bind(self, 1000))
|
||||
App.addEvent('load', self.info.bind(self, 2000))
|
||||
App.addEvent('unload', function(){
|
||||
if(self.timer)
|
||||
clearTimeout(self.timer);
|
||||
@@ -84,7 +84,7 @@ var UpdaterBase = new Class({
|
||||
'click': self.doUpdate.bind(self)
|
||||
}
|
||||
})
|
||||
).inject($(document.body).getElement('.header'))
|
||||
).inject(document.body)
|
||||
},
|
||||
|
||||
doUpdate: function(){
|
||||
|
||||
@@ -24,6 +24,12 @@ config = [{
|
||||
'default': 'localhost:6789',
|
||||
'description': 'Hostname with port. Usually <strong>localhost:6789</strong>',
|
||||
},
|
||||
{
|
||||
'name': 'username',
|
||||
'default': 'nzbget',
|
||||
'advanced': True,
|
||||
'description': 'Set a different username to connect. Default: nzbget',
|
||||
},
|
||||
{
|
||||
'name': 'password',
|
||||
'type': 'password',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from base64 import standard_b64encode
|
||||
from couchpotato.core.downloaders.base import Downloader, StatusList
|
||||
from couchpotato.core.helpers.encoding import ss
|
||||
from couchpotato.core.helpers.variable import tryInt
|
||||
from couchpotato.core.helpers.variable import tryInt, md5
|
||||
from couchpotato.core.logger import CPLog
|
||||
from datetime import timedelta
|
||||
import re
|
||||
@@ -16,7 +16,7 @@ class NZBGet(Downloader):
|
||||
|
||||
type = ['nzb']
|
||||
|
||||
url = 'http://nzbget:%(password)s@%(host)s/xmlrpc'
|
||||
url = 'http://%(username)s:%(password)s@%(host)s/xmlrpc'
|
||||
|
||||
def download(self, data = {}, movie = {}, filedata = None):
|
||||
|
||||
@@ -26,7 +26,7 @@ class NZBGet(Downloader):
|
||||
|
||||
log.info('Sending "%s" to NZBGet.', data.get('name'))
|
||||
|
||||
url = self.url % {'host': self.conf('host'), 'password': self.conf('password')}
|
||||
url = self.url % {'host': self.conf('host'), 'username': self.conf('username'), 'password': self.conf('password')}
|
||||
nzb_name = ss('%s.nzb' % self.createNzbName(data, movie))
|
||||
|
||||
rpc = xmlrpclib.ServerProxy(url)
|
||||
@@ -52,7 +52,14 @@ class NZBGet(Downloader):
|
||||
|
||||
if xml_response:
|
||||
log.info('NZB sent successfully to NZBGet')
|
||||
return True
|
||||
nzb_id = md5(data['url']) # about as unique as they come ;)
|
||||
couchpotato_id = "couchpotato=" + nzb_id
|
||||
groups = rpc.listgroups()
|
||||
file_id = [item['LastID'] for item in groups if item['NZBFilename'] == nzb_name]
|
||||
confirmed = rpc.editqueue("GroupSetParameter", 0, couchpotato_id, file_id)
|
||||
if confirmed:
|
||||
log.debug('couchpotato parameter set in nzbget download')
|
||||
return self.downloadReturnId(nzb_id)
|
||||
else:
|
||||
log.error('NZBGet could not add %s to the queue.', nzb_name)
|
||||
return False
|
||||
@@ -61,7 +68,7 @@ class NZBGet(Downloader):
|
||||
|
||||
log.debug('Checking NZBGet download status.')
|
||||
|
||||
url = self.url % {'host': self.conf('host'), 'password': self.conf('password')}
|
||||
url = self.url % {'host': self.conf('host'), 'username': self.conf('username'), 'password': self.conf('password')}
|
||||
|
||||
rpc = xmlrpclib.ServerProxy(url)
|
||||
try:
|
||||
@@ -93,15 +100,19 @@ class NZBGet(Downloader):
|
||||
|
||||
for item in groups:
|
||||
log.debug('Found %s in NZBGet download queue', item['NZBFilename'])
|
||||
try:
|
||||
nzb_id = [param['Value'] for param in item['Parameters'] if param['Name'] == 'couchpotato'][0]
|
||||
except:
|
||||
nzb_id = item['NZBID']
|
||||
statuses.append({
|
||||
'id': item['NZBID'],
|
||||
'id': nzb_id,
|
||||
'name': item['NZBFilename'],
|
||||
'original_status': 'DOWNLOADING' if item['ActiveDownloads'] > 0 else 'QUEUED',
|
||||
# Seems to have no native API function for time left. This will return the time left after NZBGet started downloading this item
|
||||
'timeleft': str(timedelta(seconds = item['RemainingSizeMB'] / status['DownloadRate'] * 2 ^ 20)) if item['ActiveDownloads'] > 0 and not (status['DownloadPaused'] or status['Download2Paused']) else -1,
|
||||
})
|
||||
|
||||
for item in queue:
|
||||
for item in queue: # 'Parameters' is not passed in rpc.postqueue
|
||||
log.debug('Found %s in NZBGet postprocessing queue', item['NZBFilename'])
|
||||
statuses.append({
|
||||
'id': item['NZBID'],
|
||||
@@ -112,8 +123,12 @@ class NZBGet(Downloader):
|
||||
|
||||
for item in history:
|
||||
log.debug('Found %s in NZBGet history. ParStatus: %s, ScriptStatus: %s, Log: %s', (item['NZBFilename'] , item['ParStatus'], item['ScriptStatus'] , item['Log']))
|
||||
try:
|
||||
nzb_id = [param['Value'] for param in item['Parameters'] if param['Name'] == 'couchpotato'][0]
|
||||
except:
|
||||
nzb_id = item['NZBID']
|
||||
statuses.append({
|
||||
'id': item['NZBID'],
|
||||
'id': nzb_id,
|
||||
'name': item['NZBFilename'],
|
||||
'status': 'completed' if item['ParStatus'] == 'SUCCESS' and item['ScriptStatus'] == 'SUCCESS' else 'failed',
|
||||
'original_status': item['ParStatus'] + ', ' + item['ScriptStatus'],
|
||||
@@ -147,8 +162,11 @@ class NZBGet(Downloader):
|
||||
|
||||
try:
|
||||
history = rpc.history()
|
||||
if rpc.editqueue('HistoryDelete', 0, "", [tryInt(item['id'])]):
|
||||
path = [hist['DestDir'] for hist in history if hist['NZBID'] == item['id']][0]
|
||||
for hist in history:
|
||||
if hist['Parameters'] and hist['Parameters']['couchpotato'] and hist['Parameters']['couchpotato'] == item['id']:
|
||||
nzb_id = hist['ID']
|
||||
path = hist['DestDir']
|
||||
if rpc.editqueue('HistoryDelete', 0, "", [tryInt(nzb_id)]):
|
||||
shutil.rmtree(path, True)
|
||||
except:
|
||||
log.error('Failed deleting: %s', traceback.format_exc(0))
|
||||
|
||||
@@ -16,10 +16,8 @@ def runHandler(name, handler, *args, **kwargs):
|
||||
|
||||
def addEvent(name, handler, priority = 100):
|
||||
|
||||
if events.get(name):
|
||||
e = events[name]
|
||||
else:
|
||||
e = events[name] = Event(name = name, threads = 10, exc_info = True, traceback = True, lock = threading.RLock())
|
||||
if not events.get(name):
|
||||
events[name] = []
|
||||
|
||||
def createHandle(*args, **kwargs):
|
||||
|
||||
@@ -35,7 +33,10 @@ def addEvent(name, handler, priority = 100):
|
||||
|
||||
return h
|
||||
|
||||
e.handle(createHandle, priority = priority)
|
||||
events[name].append({
|
||||
'handler': createHandle,
|
||||
'priority': priority,
|
||||
})
|
||||
|
||||
def removeEvent(name, handler):
|
||||
e = events[name]
|
||||
@@ -43,6 +44,12 @@ def removeEvent(name, handler):
|
||||
|
||||
def fireEvent(name, *args, **kwargs):
|
||||
if not events.get(name): return
|
||||
|
||||
e = Event(name = name, threads = 10, asynch = kwargs.get('async', False), exc_info = True, traceback = True, lock = threading.RLock())
|
||||
|
||||
for event in events[name]:
|
||||
e.handle(event['handler'], priority = event['priority'])
|
||||
|
||||
#log.debug('Firing event %s', name)
|
||||
try:
|
||||
|
||||
@@ -52,6 +59,7 @@ def fireEvent(name, *args, **kwargs):
|
||||
'single': False, # Return single handler
|
||||
'merge': False, # Merge items
|
||||
'in_order': False, # Fire them in specific order, waits for the other to finish
|
||||
'async': False
|
||||
}
|
||||
|
||||
# Do options
|
||||
@@ -62,13 +70,6 @@ def fireEvent(name, *args, **kwargs):
|
||||
options[x] = val
|
||||
except: pass
|
||||
|
||||
e = events[name]
|
||||
|
||||
# Lock this event
|
||||
e.lock.acquire()
|
||||
|
||||
e.asynchronous = False
|
||||
|
||||
# Make sure only 1 event is fired at a time when order is wanted
|
||||
kwargs['event_order_lock'] = threading.RLock() if options['in_order'] or options['single'] else None
|
||||
kwargs['event_return_on_result'] = options['single']
|
||||
@@ -76,9 +77,6 @@ def fireEvent(name, *args, **kwargs):
|
||||
# Fire
|
||||
result = e(*args, **kwargs)
|
||||
|
||||
# Release lock for this event
|
||||
e.lock.release()
|
||||
|
||||
if options['single'] and not options['merge']:
|
||||
results = None
|
||||
|
||||
@@ -104,10 +102,11 @@ def fireEvent(name, *args, **kwargs):
|
||||
|
||||
# Merge
|
||||
if options['merge'] and len(results) > 0:
|
||||
results.reverse() # Priority 1 is higher then 100
|
||||
|
||||
# Dict
|
||||
if isinstance(results[0], dict):
|
||||
results.reverse()
|
||||
|
||||
merged = {}
|
||||
for result in results:
|
||||
merged = mergeDicts(merged, result, prepend_list = True)
|
||||
@@ -140,13 +139,8 @@ def fireEvent(name, *args, **kwargs):
|
||||
log.error('%s: %s', (name, traceback.format_exc()))
|
||||
|
||||
def fireEventAsync(*args, **kwargs):
|
||||
try:
|
||||
my_thread = threading.Thread(target = fireEvent, args = args, kwargs = kwargs)
|
||||
my_thread.setDaemon(True)
|
||||
my_thread.start()
|
||||
return True
|
||||
except Exception, e:
|
||||
log.error('%s: %s', (args[0], e))
|
||||
kwargs['async'] = True
|
||||
fireEvent(*args, **kwargs)
|
||||
|
||||
def errorHandler(error):
|
||||
etype, value, tb = error
|
||||
|
||||
@@ -10,6 +10,20 @@ import sys
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
def link(src, dst):
|
||||
if os.name == 'nt':
|
||||
import ctypes
|
||||
if ctypes.windll.kernel32.CreateHardLinkW(unicode(dst), unicode(src), 0) == 0: raise ctypes.WinError()
|
||||
else:
|
||||
os.link(src, dst)
|
||||
|
||||
def symlink(src, dst):
|
||||
if os.name == 'nt':
|
||||
import ctypes
|
||||
if ctypes.windll.kernel32.CreateSymbolicLinkW(unicode(dst), unicode(src), 1 if os.path.isdir(src) else 0) in [0, 1280]: raise ctypes.WinError()
|
||||
else:
|
||||
os.symlink(src, dst)
|
||||
|
||||
def getUserDir():
|
||||
try:
|
||||
import pwd
|
||||
|
||||
@@ -18,6 +18,7 @@ class Notification(Provider):
|
||||
listen_to = [
|
||||
'renamer.after', 'movie.snatched',
|
||||
'updater.available', 'updater.updated',
|
||||
'core.message',
|
||||
]
|
||||
dont_listen_to = []
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from couchpotato import get_session
|
||||
from couchpotato.api import addApiView, addNonBlockApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.request import jsonified, getParam
|
||||
from couchpotato.core.helpers.variable import tryInt, splitString
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.notifications.base import Notification
|
||||
from couchpotato.core.settings.model import Notification as Notif
|
||||
from couchpotato.environment import Env
|
||||
from sqlalchemy.sql.expression import or_
|
||||
import threading
|
||||
import time
|
||||
@@ -21,11 +22,6 @@ class CoreNotifier(Notification):
|
||||
messages = []
|
||||
listeners = []
|
||||
|
||||
listen_to = [
|
||||
'renamer.after', 'movie.snatched',
|
||||
'updater.available', 'updater.updated',
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
super(CoreNotifier, self).__init__()
|
||||
|
||||
@@ -54,7 +50,10 @@ class CoreNotifier(Notification):
|
||||
addNonBlockApiView('notification.listener', (self.addListener, self.removeListener))
|
||||
addApiView('notification.listener', self.listener)
|
||||
|
||||
fireEvent('schedule.interval', 'core.check_messages', self.checkMessages, hours = 12, single = True)
|
||||
|
||||
addEvent('app.load', self.clean)
|
||||
addEvent('app.load', self.checkMessages)
|
||||
|
||||
def clean(self):
|
||||
|
||||
@@ -112,6 +111,23 @@ class CoreNotifier(Notification):
|
||||
'notifications': notifications
|
||||
})
|
||||
|
||||
def checkMessages(self):
|
||||
|
||||
prop_name = 'messages.last_check'
|
||||
last_check = tryInt(Env.prop(prop_name, default = 0))
|
||||
|
||||
messages = fireEvent('cp.messages', last_check = last_check, single = True)
|
||||
|
||||
last_time = 0
|
||||
for message in messages:
|
||||
if message.get('time') > last_check:
|
||||
fireEvent('core.message', message = message.get('message'), data = message)
|
||||
|
||||
if last_time < message.get('time'):
|
||||
last_time = message.get('time')
|
||||
|
||||
Env.prop(prop_name, value = last_time)
|
||||
|
||||
def notify(self, message = '', data = {}, listener = None):
|
||||
|
||||
db = get_session()
|
||||
|
||||
@@ -21,20 +21,17 @@ var NotificationBase = new Class({
|
||||
App.addEvent('load', function(){
|
||||
|
||||
App.block.notification = new Block.Menu(self, {
|
||||
'button_class': 'icon2.eye-open',
|
||||
'class': 'notification_menu',
|
||||
'onOpen': self.markAsRead.bind(self)
|
||||
})
|
||||
$(App.block.notification).inject(App.getBlock('search'), 'after');
|
||||
self.badge = new Element('div.badge').inject(App.block.notification, 'top').hide();
|
||||
|
||||
/* App.getBlock('notification').addLink(new Element('a.more', {
|
||||
'href': App.createUrl('notifications'),
|
||||
'text': 'Show older notifications'
|
||||
})); */
|
||||
});
|
||||
|
||||
window.addEvent('load', function(){
|
||||
self.startInterval.delay(Browser.safari ? 100 : 0, self)
|
||||
self.startInterval.delay(2000, self)
|
||||
});
|
||||
|
||||
},
|
||||
@@ -47,14 +44,19 @@ var NotificationBase = new Class({
|
||||
|
||||
result.el = App.getBlock('notification').addLink(
|
||||
new Element('span.'+(result.read ? 'read' : '' )).adopt(
|
||||
new Element('span.message', {'text': result.message}),
|
||||
new Element('span.message', {'html': result.message}),
|
||||
new Element('span.added', {'text': added.timeDiffInWords(), 'title': added})
|
||||
)
|
||||
, 'top');
|
||||
self.notifications.include(result);
|
||||
|
||||
if(!result.read)
|
||||
if(result.data.important !== undefined && !result.read){
|
||||
var sticky = true
|
||||
App.fireEvent('message', [result.message, sticky, result])
|
||||
}
|
||||
else if(!result.read){
|
||||
self.setBadge(self.notifications.filter(function(n){ return !n.read}).length)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@@ -64,20 +66,26 @@ var NotificationBase = new Class({
|
||||
self.badge[value ? 'show' : 'hide']()
|
||||
},
|
||||
|
||||
markAsRead: function(){
|
||||
var self = this;
|
||||
markAsRead: function(force_ids){
|
||||
var self = this,
|
||||
ids = force_ids;
|
||||
|
||||
var rn = self.notifications.filter(function(n){
|
||||
return !n.read
|
||||
})
|
||||
if(!force_ids) {
|
||||
var rn = self.notifications.filter(function(n){
|
||||
return !n.read && n.data.important === undefined
|
||||
})
|
||||
|
||||
var ids = []
|
||||
rn.each(function(n){
|
||||
ids.include(n.id)
|
||||
})
|
||||
var ids = []
|
||||
rn.each(function(n){
|
||||
ids.include(n.id)
|
||||
})
|
||||
}
|
||||
|
||||
if(ids.length > 0)
|
||||
Api.request('notification.markread', {
|
||||
'data': {
|
||||
'ids': ids.join(',')
|
||||
},
|
||||
'onSuccess': function(){
|
||||
self.setBadge('')
|
||||
}
|
||||
@@ -143,26 +151,41 @@ var NotificationBase = new Class({
|
||||
self.startPoll()
|
||||
},
|
||||
|
||||
showMessage: function(message){
|
||||
showMessage: function(message, sticky, data){
|
||||
var self = this;
|
||||
|
||||
if(!self.message_container)
|
||||
self.message_container = new Element('div.messages').inject(document.body);
|
||||
|
||||
var new_message = new Element('div.message', {
|
||||
'text': message
|
||||
}).inject(self.message_container);
|
||||
var new_message = new Element('div', {
|
||||
'class': 'message' + (sticky ? ' sticky' : ''),
|
||||
'html': message
|
||||
}).inject(self.message_container, 'top');
|
||||
|
||||
setTimeout(function(){
|
||||
new_message.addClass('show')
|
||||
}, 10);
|
||||
|
||||
setTimeout(function(){
|
||||
var hide_message = function(){
|
||||
new_message.addClass('hide')
|
||||
setTimeout(function(){
|
||||
new_message.destroy();
|
||||
}, 1000);
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
if(sticky)
|
||||
new_message.grab(
|
||||
new Element('a.close.icon2', {
|
||||
'events': {
|
||||
'click': function(){
|
||||
self.markAsRead([data.id]);
|
||||
hide_message();
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
else
|
||||
setTimeout(hide_message, 4000);
|
||||
|
||||
},
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ class FileManager(Plugin):
|
||||
db = get_session()
|
||||
for root, dirs, walk_files in os.walk(Env.get('cache_dir')):
|
||||
for filename in walk_files:
|
||||
if root == python_cache or 'minified' in filename: continue
|
||||
if root == python_cache or 'minified' in filename or 'version' in filename: continue
|
||||
file_path = os.path.join(root, filename)
|
||||
f = db.query(File).filter(File.path == toUnicode(file_path)).first()
|
||||
if not f:
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
.page.log .nav {
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
padding: 0 0 30px;
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
position: fixed;
|
||||
width: 960px;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #4E5969;
|
||||
}
|
||||
|
||||
.page.log .nav li {
|
||||
display: inline;
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
@@ -24,7 +25,17 @@
|
||||
.page.log .nav li.active {
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
font-size: 30px;
|
||||
background: rgba(255,255,255,.1);
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.page.log .nav {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.page.log .nav li {
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.page.log .loading {
|
||||
|
||||
@@ -185,6 +185,8 @@ class Manage(Plugin):
|
||||
# Add it to release and update the info
|
||||
fireEvent('release.add', group = group)
|
||||
fireEventAsync('library.update', identifier = identifier, on_complete = self.createAfterUpdate(folder, identifier))
|
||||
else:
|
||||
self.in_progress[folder]['to_go'] = self.in_progress[folder]['to_go'] - 1
|
||||
|
||||
return addToLibrary
|
||||
|
||||
|
||||
@@ -108,9 +108,8 @@ class MoviePlugin(Plugin):
|
||||
now = time.time()
|
||||
week = 262080
|
||||
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
available_status = fireEvent('status.get', 'available', single = True)
|
||||
snatched_status = fireEvent('status.get', 'snatched', single = True)
|
||||
done_status, available_status, snatched_status = \
|
||||
fireEvent('status.get', ['done', 'available', 'snatched'], single = True)
|
||||
|
||||
db = get_session()
|
||||
|
||||
@@ -367,11 +366,8 @@ class MoviePlugin(Plugin):
|
||||
library = fireEvent('library.add', single = True, attrs = params, update_after = update_library)
|
||||
|
||||
# Status
|
||||
status_active = fireEvent('status.add', 'active', single = True)
|
||||
snatched_status = fireEvent('status.add', 'snatched', single = True)
|
||||
ignored_status = fireEvent('status.add', 'ignored', single = True)
|
||||
done_status = fireEvent('status.add', 'done', single = True)
|
||||
downloaded_status = fireEvent('status.add', 'downloaded', single = True)
|
||||
status_active, snatched_status, ignored_status, done_status, downloaded_status = \
|
||||
fireEvent('status.get', ['active', 'snatched', 'ignored', 'done', 'downloaded'], single = True)
|
||||
|
||||
default_profile = fireEvent('profile.default', single = True)
|
||||
|
||||
@@ -549,8 +545,7 @@ class MoviePlugin(Plugin):
|
||||
|
||||
def restatus(self, movie_id):
|
||||
|
||||
active_status = fireEvent('status.get', 'active', single = True)
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
active_status, done_status = fireEvent('status.get', ['active', 'done'], single = True)
|
||||
|
||||
db = get_session()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var MovieList = new Class({
|
||||
|
||||
Implements: [Options],
|
||||
Implements: [Events, Options],
|
||||
|
||||
options: {
|
||||
navigation: true,
|
||||
@@ -8,7 +8,8 @@ var MovieList = new Class({
|
||||
load_more: true,
|
||||
loader: true,
|
||||
menu: [],
|
||||
add_new: false
|
||||
add_new: false,
|
||||
force_view: false
|
||||
},
|
||||
|
||||
movies: [],
|
||||
@@ -43,8 +44,11 @@ var MovieList = new Class({
|
||||
}) : null
|
||||
);
|
||||
|
||||
self.changeView(self.getSavedView() || self.options.view || 'details');
|
||||
|
||||
if($(window).getSize().x <= 480 && !self.options.force_view)
|
||||
self.changeView('list');
|
||||
else
|
||||
self.changeView(self.getSavedView() || self.options.view || 'details');
|
||||
|
||||
self.getMovies();
|
||||
|
||||
App.addEvent('movie.added', self.movieAdded.bind(self))
|
||||
@@ -121,7 +125,7 @@ var MovieList = new Class({
|
||||
|
||||
if(!self.navigation_counter) return;
|
||||
|
||||
self.navigation_counter.set('text', (count || 0));
|
||||
self.navigation_counter.set('text', (count || 0) + ' movies');
|
||||
|
||||
},
|
||||
|
||||
@@ -145,65 +149,67 @@ var MovieList = new Class({
|
||||
var self = this;
|
||||
var chars = '#ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
self.current_view = self.getSavedView() || 'details';
|
||||
self.el.addClass(self.current_view+'_list')
|
||||
self.el.addClass('with_navigation')
|
||||
|
||||
self.navigation = new Element('div.alph_nav').adopt(
|
||||
self.navigation_actions = new Element('ul.inlay.actions.reversed'),
|
||||
self.navigation_counter = new Element('span.counter[title=Total]'),
|
||||
self.navigation_alpha = new Element('ul.numbers', {
|
||||
'events': {
|
||||
'click:relay(li)': function(e, el){
|
||||
self.movie_list.empty()
|
||||
self.activateLetter(el.get('data-letter'))
|
||||
self.getMovies()
|
||||
self.navigation = new Element('div.alph_nav').grab(
|
||||
new Element('div').adopt(
|
||||
self.navigation_alpha = new Element('ul.numbers', {
|
||||
'events': {
|
||||
'click:relay(li)': function(e, el){
|
||||
self.movie_list.empty()
|
||||
self.activateLetter(el.get('data-letter'))
|
||||
self.getMovies()
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
self.navigation_search_input = new Element('input.inlay', {
|
||||
'placeholder': 'Search',
|
||||
'events': {
|
||||
'keyup': self.search.bind(self),
|
||||
'change': self.search.bind(self)
|
||||
}
|
||||
}),
|
||||
self.navigation_menu = new Block.Menu(self),
|
||||
self.mass_edit_form = new Element('div.mass_edit_form').adopt(
|
||||
new Element('span.select').adopt(
|
||||
self.mass_edit_select = new Element('input[type=checkbox].inlay', {
|
||||
'events': {
|
||||
'change': self.massEditToggleAll.bind(self)
|
||||
}
|
||||
}),
|
||||
self.mass_edit_selected = new Element('span.count', {'text': 0}),
|
||||
self.mass_edit_selected_label = new Element('span', {'text': 'selected'})
|
||||
),
|
||||
new Element('div.quality').adopt(
|
||||
self.mass_edit_quality = new Element('select'),
|
||||
new Element('a.button.orange', {
|
||||
'text': 'Change quality',
|
||||
'events': {
|
||||
'click': self.changeQualitySelected.bind(self)
|
||||
}
|
||||
})
|
||||
),
|
||||
new Element('div.delete').adopt(
|
||||
new Element('span[text=or]'),
|
||||
new Element('a.button.red', {
|
||||
'text': 'Delete',
|
||||
'events': {
|
||||
'click': self.deleteSelected.bind(self)
|
||||
}
|
||||
})
|
||||
),
|
||||
new Element('div.refresh').adopt(
|
||||
new Element('span[text=or]'),
|
||||
new Element('a.button.green', {
|
||||
'text': 'Refresh',
|
||||
'events': {
|
||||
'click': self.refreshSelected.bind(self)
|
||||
}
|
||||
})
|
||||
}),
|
||||
self.navigation_counter = new Element('span.counter[title=Total]'),
|
||||
self.navigation_actions = new Element('ul.inlay.actions.reversed'),
|
||||
self.navigation_search_input = new Element('input.search.inlay', {
|
||||
'title': 'Search through ' + self.options.identifier,
|
||||
'placeholder': 'Search through ' + self.options.identifier,
|
||||
'events': {
|
||||
'keyup': self.search.bind(self),
|
||||
'change': self.search.bind(self)
|
||||
}
|
||||
}),
|
||||
self.navigation_menu = new Block.Menu(self),
|
||||
self.mass_edit_form = new Element('div.mass_edit_form').adopt(
|
||||
new Element('span.select').adopt(
|
||||
self.mass_edit_select = new Element('input[type=checkbox].inlay', {
|
||||
'events': {
|
||||
'change': self.massEditToggleAll.bind(self)
|
||||
}
|
||||
}),
|
||||
self.mass_edit_selected = new Element('span.count', {'text': 0}),
|
||||
self.mass_edit_selected_label = new Element('span', {'text': 'selected'})
|
||||
),
|
||||
new Element('div.quality').adopt(
|
||||
self.mass_edit_quality = new Element('select'),
|
||||
new Element('a.button.orange', {
|
||||
'text': 'Change quality',
|
||||
'events': {
|
||||
'click': self.changeQualitySelected.bind(self)
|
||||
}
|
||||
})
|
||||
),
|
||||
new Element('div.delete').adopt(
|
||||
new Element('span[text=or]'),
|
||||
new Element('a.button.red', {
|
||||
'text': 'Delete',
|
||||
'events': {
|
||||
'click': self.deleteSelected.bind(self)
|
||||
}
|
||||
})
|
||||
),
|
||||
new Element('div.refresh').adopt(
|
||||
new Element('span[text=or]'),
|
||||
new Element('a.button.green', {
|
||||
'text': 'Refresh',
|
||||
'events': {
|
||||
'click': self.refreshSelected.bind(self)
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
).inject(self.el, 'top');
|
||||
@@ -248,18 +254,19 @@ var MovieList = new Class({
|
||||
});
|
||||
|
||||
// Get available chars and highlight
|
||||
Api.request('movie.available_chars', {
|
||||
'data': Object.merge({
|
||||
'status': self.options.status
|
||||
}, self.filter),
|
||||
'onSuccess': function(json){
|
||||
|
||||
json.chars.split('').each(function(c){
|
||||
self.letters[c.capitalize()].addClass('available')
|
||||
})
|
||||
|
||||
}
|
||||
});
|
||||
if(self.navigation.isDisplayed() || self.navigation.isVisible())
|
||||
Api.request('movie.available_chars', {
|
||||
'data': Object.merge({
|
||||
'status': self.options.status
|
||||
}, self.filter),
|
||||
'onSuccess': function(json){
|
||||
|
||||
json.chars.split('').each(function(c){
|
||||
self.letters[c.capitalize()].addClass('available')
|
||||
})
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Add menu or hide
|
||||
if (self.options.menu.length > 0)
|
||||
@@ -267,17 +274,7 @@ var MovieList = new Class({
|
||||
self.navigation_menu.addLink(menu_item);
|
||||
})
|
||||
else
|
||||
self.navigation_menu.hide()
|
||||
|
||||
self.nav_scrollspy = new ScrollSpy({
|
||||
min: 10,
|
||||
onEnter: function(){
|
||||
self.navigation.addClass('float')
|
||||
},
|
||||
onLeave: function(){
|
||||
self.navigation.removeClass('float')
|
||||
}
|
||||
});
|
||||
self.navigation_menu.hide();
|
||||
|
||||
},
|
||||
|
||||
@@ -517,6 +514,7 @@ var MovieList = new Class({
|
||||
}
|
||||
|
||||
self.checkIfEmpty();
|
||||
self.fireEvent('loaded');
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -543,7 +541,7 @@ var MovieList = new Class({
|
||||
self.title[is_empty ? 'hide' : 'show']()
|
||||
|
||||
if(self.description)
|
||||
self.description[is_empty ? 'hide' : 'show']()
|
||||
self.description.setStyle('display', [is_empty ? 'none' : ''])
|
||||
|
||||
if(is_empty && self.options.on_empty_element){
|
||||
self.options.on_empty_element.inject(self.loader_first || self.title || self.movie_list, 'after');
|
||||
|
||||
@@ -1,50 +1,73 @@
|
||||
.movies {
|
||||
padding: 60px 0 20px;
|
||||
padding: 10px 0 20px;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.movies .loading {
|
||||
display: block;
|
||||
padding: 20px 0 0 0;
|
||||
width: 100%;
|
||||
z-index: 3;
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1);
|
||||
height: 40px;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
.movies > div {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.movies.thumbs_list > div:not(.description) {
|
||||
margin-right: -4px;
|
||||
text-align: center;
|
||||
}
|
||||
.movies .loading.hide {
|
||||
height: 0;
|
||||
|
||||
.movies .loading {
|
||||
display: block;
|
||||
padding: 20px 0 0 0;
|
||||
opacity: 0;
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
.movies .loading .spinner {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.movies .loading .message {
|
||||
margin: 0 20px;
|
||||
width: 100%;
|
||||
z-index: 3;
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1);
|
||||
height: 40px;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
.movies .loading.hide {
|
||||
height: 0;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
margin-top: -20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.movies .loading .spinner {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.movies .loading .message {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.movies > h2 {
|
||||
.movies h2 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.movies h2 {
|
||||
font-size: 25px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.movies > .description {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 0;
|
||||
font-style: italic;
|
||||
text-shadow: none;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.movies:hover > .description {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media all and (max-width: 860px) {
|
||||
.movies > .description {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.movies.thumbs_list {
|
||||
padding: 20px 0 20px;
|
||||
@@ -53,21 +76,21 @@
|
||||
.home .movies {
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
|
||||
.movies.mass_edit_list {
|
||||
padding-top: 90px;
|
||||
}
|
||||
|
||||
.movies .movie {
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
transition: all 0.6s cubic-bezier(0.9,0,0.1,1);
|
||||
}
|
||||
|
||||
.movies.details_list .movie {
|
||||
padding-left: 120px;
|
||||
}
|
||||
|
||||
.movies.list_list .movie:not(.details_view),
|
||||
.movies.mass_edit_list .movie {
|
||||
@@ -75,13 +98,21 @@
|
||||
}
|
||||
|
||||
.movies.thumbs_list .movie {
|
||||
width: 153px;
|
||||
height: 230px;
|
||||
width: 16.66667%;
|
||||
height: auto;
|
||||
display: inline-block;
|
||||
margin: 0 8px 0 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
border: 0;
|
||||
}
|
||||
.movies.thumbs_list .movie:nth-child(6n+6) {
|
||||
margin: 0;
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
.movies.thumbs_list .movie {
|
||||
width: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
.movies .movie .mask {
|
||||
@@ -109,8 +140,8 @@
|
||||
.movies .data {
|
||||
padding: 20px;
|
||||
height: 100%;
|
||||
width: 840px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
right: 0;
|
||||
border-radius: 0;
|
||||
transition: all .6s cubic-bezier(0.9,0,0.1,1);
|
||||
@@ -119,14 +150,15 @@
|
||||
.movies.mass_edit_list .movie .data {
|
||||
height: 30px;
|
||||
padding: 3px 0 3px 10px;
|
||||
width: 938px;
|
||||
box-shadow: none;
|
||||
border: 0;
|
||||
background: #4e5969;
|
||||
}
|
||||
|
||||
.movies.thumbs_list .data {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
height: 100%;
|
||||
@@ -171,8 +203,11 @@
|
||||
height: 100%;
|
||||
border-radius: 4px 0 0 4px;
|
||||
transition: all .6s cubic-bezier(0.9,0,0.1,1);
|
||||
|
||||
}
|
||||
.movies.thumbs_list .poster {
|
||||
position: relative;
|
||||
border-radius: 0;
|
||||
}
|
||||
.movies.list_list .movie:not(.details_view) .poster,
|
||||
.movies.mass_edit_list .poster {
|
||||
width: 20px;
|
||||
@@ -186,38 +221,72 @@
|
||||
.movies.thumbs_list .poster {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.movies .poster img,
|
||||
.options .poster img {
|
||||
width: 101%;
|
||||
height: 101%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.movies.thumbs_list .poster img {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.movies .info {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.movies .info .title {
|
||||
display: inline;
|
||||
position: absolute;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 2px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 90%;
|
||||
width: 100%;
|
||||
padding-right: 80px;
|
||||
transition: all 0.2s linear;
|
||||
}
|
||||
.movies .info .title span {
|
||||
display: block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
top: -5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.movies.thumbs_list .info .title span {
|
||||
white-space: normal;
|
||||
overflow: auto;
|
||||
height: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.movies.thumbs_list .movie .info .title span,
|
||||
.movies.thumbs_list .movie .info .year {
|
||||
font-size: 15px;
|
||||
line-height: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.movies.list_list .movie:not(.details_view) .info .title,
|
||||
.movies.mass_edit_list .info .title {
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
text-overflow: ellipsis;
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
|
||||
.movies.thumbs_list .movie:not(.no_thumbnail) .info {
|
||||
@@ -229,25 +298,22 @@
|
||||
|
||||
.movies.thumbs_list .info .title {
|
||||
font-size: 21px;
|
||||
text-shadow: 0 0 10px #000;
|
||||
word-wrap: break-word;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.movies .info .year {
|
||||
position: absolute;
|
||||
font-size: 30px;
|
||||
margin-bottom: 10px;
|
||||
color: #bbb;
|
||||
width: 10%;
|
||||
right: 0;
|
||||
top: 0;
|
||||
top: 1px;
|
||||
text-align: right;
|
||||
transition: all 0.2s linear;
|
||||
font-weight: normal;
|
||||
}
|
||||
.movies.list_list .movie:not(.details_view) .info .year,
|
||||
.movies.mass_edit_list .info .year {
|
||||
font-size: 16px;
|
||||
width: 6%;
|
||||
font-size: 1.25em;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
@@ -259,16 +325,14 @@
|
||||
top: auto;
|
||||
right: auto;
|
||||
color: #FFF;
|
||||
text-shadow: none;
|
||||
text-shadow: 0 0 6px #000;
|
||||
}
|
||||
|
||||
.movies .info .description {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
clear: both;
|
||||
height: 80px;
|
||||
bottom: 30px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
.movies .data:hover .description {
|
||||
overflow: auto;
|
||||
@@ -281,12 +345,17 @@
|
||||
|
||||
.movies .data .quality {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
bottom: 2px;
|
||||
display: block;
|
||||
min-height: 20px;
|
||||
vertical-align: mid;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.movies .data .quality {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.movies .status_suggest .data .quality,
|
||||
.movies.thumbs_list .data .quality {
|
||||
display: none;
|
||||
@@ -302,9 +371,8 @@
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
text-shadow: none;
|
||||
font-weight: normal;
|
||||
margin: 0 2px;
|
||||
margin: 0 4px 0 0;
|
||||
border-radius: 2px;
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
@@ -312,7 +380,7 @@
|
||||
.movies.mass_edit_list .data .quality {
|
||||
text-align: right;
|
||||
right: 0;
|
||||
margin-right: 50px;
|
||||
margin-right: 60px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -342,18 +410,40 @@
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
line-height: 0;
|
||||
margin-top: -25px;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
display: none;
|
||||
width: 0;
|
||||
}
|
||||
@media all and (max-width: 480px) {
|
||||
.movies .data .actions {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.movies .movie:hover .data .actions {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.movies.details_list .data .actions {
|
||||
top: auto;
|
||||
bottom: 18px;
|
||||
}
|
||||
|
||||
.movies .movie:hover .actions {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
.movies.thumbs_list .data .actions {
|
||||
bottom: 8px;
|
||||
bottom: 2px;
|
||||
right: 10px;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
.movies .movie:hover .action { opacity: 0.6; }
|
||||
.movies .movie:hover .action:hover { opacity: 1; }
|
||||
.movies.mass_edit_list .data .actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.movies .data .action {
|
||||
background-repeat: no-repeat;
|
||||
@@ -362,7 +452,10 @@
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
padding: 3px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.movies.mass_edit_list .movie .data .actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.movies.list_list .movie:not(.details_view):hover .actions,
|
||||
@@ -381,7 +474,8 @@
|
||||
font-size: 20px;
|
||||
position: absolute;
|
||||
padding: 70px 0 0;
|
||||
width: 100%;
|
||||
left: 120px;
|
||||
right: 0;
|
||||
}
|
||||
.movies .delete_container .cancel {
|
||||
}
|
||||
@@ -399,8 +493,8 @@
|
||||
|
||||
.movies .options {
|
||||
position: absolute;
|
||||
margin-left: 120px;
|
||||
width: 840px;
|
||||
right: 0;
|
||||
left: 120px;
|
||||
}
|
||||
|
||||
.movies .options .form {
|
||||
@@ -423,7 +517,6 @@
|
||||
.movies .options .table .item.ignored span {
|
||||
text-decoration: line-through;
|
||||
color: rgba(255,255,255,0.4);
|
||||
text-shadow: none;
|
||||
}
|
||||
.movies .options .table .item.ignored .delete {
|
||||
background-image: url('../images/icon.undo.png');
|
||||
@@ -457,7 +550,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
.movies .options .table .name {
|
||||
width: 350px;
|
||||
width: 340px;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
padding: 0 10px;
|
||||
@@ -491,6 +584,9 @@
|
||||
text-align: center;
|
||||
transition: all .6s cubic-bezier(0.9,0,0.1,1);
|
||||
overflow: hidden;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
.movies .movie .trailer_container.hide {
|
||||
height: 0 !important;
|
||||
@@ -507,6 +603,7 @@
|
||||
background: #4e5969;
|
||||
border-radius: 0 0 2px 2px;
|
||||
transition: all .2s cubic-bezier(0.9,0,0.1,1) .2s;
|
||||
z-index: 11;
|
||||
}
|
||||
.movies .movie .hide_trailer.hide {
|
||||
top: -30px;
|
||||
@@ -543,11 +640,20 @@
|
||||
right: 135px;
|
||||
z-index: 2;
|
||||
opacity: 0;
|
||||
text-shadow: none;
|
||||
background: #4e5969;
|
||||
min-width: 300px;
|
||||
text-align: right;
|
||||
height: 100%;
|
||||
padding: 3px 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.movies .movie .trynext {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.movies.mass_edit_list .trynext { display: none; }
|
||||
.wanted .movies .movie .trynext {
|
||||
padding-right: 50px;
|
||||
}
|
||||
@@ -555,6 +661,14 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.movies.details_list .movie .trynext {
|
||||
background: #47515f;
|
||||
padding: 0;
|
||||
right: 0;
|
||||
bottom: 35px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.movies .movie .trynext a {
|
||||
background-position: 5px center;
|
||||
padding: 0 5px 0 25px;
|
||||
@@ -562,6 +676,9 @@
|
||||
color: #FFF;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.movies .movie .trynext a:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
.movies .movie .trynext a:hover {
|
||||
background-color: #369545;
|
||||
}
|
||||
@@ -578,24 +695,32 @@
|
||||
|
||||
.movies .alph_nav {
|
||||
transition: box-shadow .4s linear;
|
||||
position: fixed;
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
top: 0;
|
||||
padding: 100px 60px 7px;
|
||||
width: 1080px;
|
||||
margin: 0 -60px;
|
||||
box-shadow: 0 20px 20px -22px rgba(0,0,0,0.1);
|
||||
background: #4e5969;
|
||||
top: 0px;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.movies .alph_nav.float {
|
||||
box-shadow: 0 30px 30px -32px rgba(0,0,0,0.5);
|
||||
border-radius: 0;
|
||||
@media all and (max-width: 480px) {
|
||||
.movies .alph_nav {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.movies .alph_nav ul.numbers,
|
||||
.movies .alph_nav > div {
|
||||
position: relative;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.movies .alph_nav .numbers,
|
||||
.movies .alph_nav .counter,
|
||||
.movies .alph_nav ul.actions {
|
||||
.movies .alph_nav .actions {
|
||||
list-style: none;
|
||||
padding: 0 0 1px;
|
||||
margin: 0;
|
||||
@@ -604,49 +729,62 @@
|
||||
}
|
||||
|
||||
.movies .alph_nav .counter {
|
||||
width: 60px;
|
||||
text-align: center;
|
||||
text-align: right;
|
||||
position: absolute;
|
||||
right: 270px;
|
||||
background: #4e5969;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
.movies .alph_nav .numbers li,
|
||||
.movies .alph_nav .actions li {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 20px;
|
||||
height: 24px;
|
||||
line-height: 26px;
|
||||
line-height: 23px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
color: rgba(255,255,255,0.2);
|
||||
border: 1px solid transparent;
|
||||
transition: all 0.1s ease-in-out;
|
||||
text-shadow: none;
|
||||
}
|
||||
.movies .alph_nav .numbers li:first-child {
|
||||
width: 43px;
|
||||
}
|
||||
|
||||
@media all and (max-width: 900px) {
|
||||
.movies .alph_nav .numbers {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.movies .alph_nav .numbers li {
|
||||
width: auto;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.movies .alph_nav li.available {
|
||||
color: rgba(255,255,255,0.8);
|
||||
color: #FFF;
|
||||
font-weight: bolder;
|
||||
|
||||
}
|
||||
.movies .alph_nav li.active.available, .movies .alph_nav li.available:hover {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
.movies .alph_nav li.active.available,
|
||||
.movies .alph_nav li.available:hover {
|
||||
background: rgba(255,255,255,.1);
|
||||
}
|
||||
|
||||
.movies .alph_nav input {
|
||||
.movies .alph_nav .search {
|
||||
padding: 6px 5px;
|
||||
margin: 0 0 0 6px;
|
||||
float: left;
|
||||
width: 155px;
|
||||
margin: 0 0 0 20px;
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
width: 154px;
|
||||
height: 25px;
|
||||
transition: all 0.6s cubic-bezier(0.9,0,0.1,1);
|
||||
}
|
||||
|
||||
.movies .alph_nav .actions {
|
||||
margin: 0 6px 0 0;
|
||||
-moz-user-select: none;
|
||||
position: absolute;
|
||||
right: 183px;
|
||||
}
|
||||
.movies .alph_nav .actions li {
|
||||
border-radius: 1px;
|
||||
@@ -730,10 +868,12 @@
|
||||
}
|
||||
|
||||
.movies .alph_nav .more_menu {
|
||||
margin-left: 48px;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.movies .alph_nav .more_menu > a {
|
||||
background-color: #4e5969;
|
||||
background-position: center -158px;
|
||||
}
|
||||
|
||||
@@ -790,7 +930,6 @@
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
text-shadow: none;
|
||||
font-weight: normal;
|
||||
font-size: 20px;
|
||||
border-left: 1px solid rgba(255, 255, 255, .2);
|
||||
|
||||
@@ -126,12 +126,14 @@ var Movie = new Class({
|
||||
self.thumbnail = File.Select.single('poster', self.data.library.files),
|
||||
self.data_container = new Element('div.data.inlay.light').adopt(
|
||||
self.info_container = new Element('div.info').adopt(
|
||||
self.title = new Element('div.title', {
|
||||
'text': self.getTitle() || 'n/a'
|
||||
}),
|
||||
self.year = new Element('div.year', {
|
||||
'text': self.data.library.year || 'n/a'
|
||||
}),
|
||||
new Element('div.title').adopt(
|
||||
self.title = new Element('span', {
|
||||
'text': self.getTitle() || 'n/a'
|
||||
}),
|
||||
self.year = new Element('div.year', {
|
||||
'text': self.data.library.year || 'n/a'
|
||||
})
|
||||
),
|
||||
self.rating = new Element('div.rating.icon', {
|
||||
'text': self.data.library.rating
|
||||
}),
|
||||
|
||||
@@ -1,105 +1,143 @@
|
||||
.search_form {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 25%;
|
||||
position: absolute;
|
||||
right: 105px;
|
||||
top: 0;
|
||||
text-align: right;
|
||||
height: 100%;
|
||||
border-bottom: 4px solid transparent;
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1);
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
border: 1px solid transparent;
|
||||
border-width: 0 0 4px;
|
||||
}
|
||||
|
||||
.search_form input {
|
||||
padding: 4px 20px 4px 4px;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
.search_form:hover {
|
||||
border-color: #047792;
|
||||
}
|
||||
.search_form input:focus {
|
||||
padding-right: 83px;
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.search_form {
|
||||
right: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.search_form.focused,
|
||||
.search_form.shown {
|
||||
border-color: #04bce6;
|
||||
}
|
||||
|
||||
.search_form .input {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
width: 45px;
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1);
|
||||
}
|
||||
|
||||
.search_form.focused .input,
|
||||
.search_form.shown .input {
|
||||
width: 380px;
|
||||
background: #4e5969;
|
||||
}
|
||||
|
||||
.search_form .input .enter {
|
||||
background: #369545 url('../images/sprite.png') right -188px no-repeat;
|
||||
padding: 0 20px 0 4px;
|
||||
border-radius: 2px;
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
margin-left: -78px;
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
visibility: hidden;
|
||||
}
|
||||
.search_form.focused .input .enter {
|
||||
visibility: visible;
|
||||
.search_form .input input {
|
||||
border-radius: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background: rgba(255,255,255,.08);
|
||||
color: #FFF;
|
||||
font-size: 25px;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
opacity: 0;
|
||||
padding: 0 40px 0 10px;
|
||||
transition: all .4s ease-in-out .2s;
|
||||
}
|
||||
.search_form.focused.filled .input .enter {
|
||||
opacity: 1;
|
||||
.search_form.focused .input input,
|
||||
.search_form.shown .input input {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.search_form .input input {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.search_form.focused .input,
|
||||
.search_form.shown .input {
|
||||
width: 277px;
|
||||
}
|
||||
}
|
||||
|
||||
.search_form .input a {
|
||||
width: 17px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
margin: -2px 0 0 2px;
|
||||
top: 4px;
|
||||
right: 5px;
|
||||
background: url('../images/sprite.png') left -37px no-repeat;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: all 0.2s ease-in-out;
|
||||
vertical-align: middle;
|
||||
|
||||
.search_form .input a {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 44px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
line-height: 66px;
|
||||
font-size: 15px;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.search_form .input a:after {
|
||||
content: "\e03e";
|
||||
}
|
||||
|
||||
.search_form.filled .input a {
|
||||
opacity: 1;
|
||||
.search_form.shown.filled .input a:after {
|
||||
content: "\e04e";
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.search_form .input a {
|
||||
line-height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.search_form .results_container {
|
||||
text-align: left;
|
||||
position: absolute;
|
||||
background: #5c697b;
|
||||
margin: 6px 0 0 -230px;
|
||||
margin: 4px 0 0;
|
||||
width: 470px;
|
||||
min-height: 140px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 20px 20px -10px rgba(0,0,0,0.55);
|
||||
display: none;
|
||||
}
|
||||
@media all and (max-width: 480px) {
|
||||
.search_form .results_container {
|
||||
width: 320px;
|
||||
}
|
||||
}
|
||||
.search_form.focused.filled .results_container,
|
||||
.search_form.shown.filled .results_container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search_form .results_container:before {
|
||||
content: ' ';
|
||||
height: 0;
|
||||
position: relative;
|
||||
width: 0;
|
||||
border: 10px solid transparent;
|
||||
border-bottom-color: #5c697b;
|
||||
display: block;
|
||||
top: -20px;
|
||||
left: 346px;
|
||||
}
|
||||
|
||||
.search_form .results {
|
||||
max-height: 570px;
|
||||
overflow-x: hidden;
|
||||
padding: 10px 0;
|
||||
margin-top: -18px;
|
||||
}
|
||||
|
||||
.movie_result {
|
||||
overflow: hidden;
|
||||
height: 140px;
|
||||
height: 50px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.movie_result .options {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
left: 30px;
|
||||
right: 0;
|
||||
padding: 13px;
|
||||
border: 1px solid transparent;
|
||||
border-width: 1px 0;
|
||||
border-radius: 0;
|
||||
@@ -107,7 +145,6 @@
|
||||
}
|
||||
|
||||
.movie_result .options > div {
|
||||
padding: 0 15px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@@ -122,6 +159,13 @@
|
||||
}
|
||||
.movie_result .options select[name=title] { width: 180px; }
|
||||
.movie_result .options select[name=profile] { width: 90px; }
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
|
||||
.movie_result .options select[name=title] { width: 90px; }
|
||||
.movie_result .options select[name=profile] { width: 60px; }
|
||||
|
||||
}
|
||||
|
||||
.movie_result .options .button {
|
||||
vertical-align: middle;
|
||||
@@ -130,25 +174,21 @@
|
||||
|
||||
.movie_result .options .message {
|
||||
height: 100%;
|
||||
line-height: 140px;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.movie_result .data {
|
||||
padding: 0 15px;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
left: 30px;
|
||||
right: 0;
|
||||
background: #5c697b;
|
||||
cursor: pointer;
|
||||
|
||||
border-bottom: 1px solid #333;
|
||||
border-top: 1px solid rgba(255,255,255, 0.15);
|
||||
transition: all .6s cubic-bezier(0.9,0,0.1,1);
|
||||
border-top: 1px solid rgba(255,255,255, 0.08);
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1);
|
||||
}
|
||||
.movie_result .data.open {
|
||||
left: 100%;
|
||||
@@ -162,49 +202,40 @@
|
||||
}
|
||||
|
||||
.movie_result .thumbnail {
|
||||
width: 17%;
|
||||
display: inline-block;
|
||||
margin: 15px 3% 15px 0;
|
||||
width: 34px;
|
||||
min-height: 100%;
|
||||
display: block;
|
||||
margin: 0;
|
||||
vertical-align: top;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 3px rgba(0,0,0,0.35);
|
||||
}
|
||||
|
||||
.movie_result .info {
|
||||
width: 80%;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding: 15px 0;
|
||||
height: 120px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.movie_result .info .tagline {
|
||||
max-height: 70px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.movie_result .add +.info {
|
||||
margin-left: 20%;
|
||||
position: absolute;
|
||||
top: 20%;
|
||||
left: 15px;
|
||||
right: 60px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.movie_result .info h2 {
|
||||
font-weight: normal;
|
||||
font-size: 20px;
|
||||
display: block;
|
||||
margin: 0;
|
||||
font-size: 17px;
|
||||
line-height: 20px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.movie_result .info h2 span {
|
||||
padding: 0 5px;
|
||||
position: absolute;
|
||||
right: -60px;
|
||||
}
|
||||
|
||||
.movie_result .info h2 span:before { content: "("; }
|
||||
.movie_result .info h2 span:after { content: ")"; }
|
||||
|
||||
.search_form .mask,
|
||||
.movie_result .mask {
|
||||
border-radius: 3px;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
@@ -7,33 +7,30 @@ Block.Search = new Class({
|
||||
create: function(){
|
||||
var self = this;
|
||||
|
||||
var focus_timer = 0;
|
||||
self.el = new Element('div.search_form').adopt(
|
||||
new Element('div.input').adopt(
|
||||
self.input = new Element('input.inlay', {
|
||||
self.input = new Element('input', {
|
||||
'placeholder': 'Search & add a new movie',
|
||||
'events': {
|
||||
'keyup': self.keyup.bind(self),
|
||||
'focus': function(){
|
||||
if(focus_timer) clearTimeout(focus_timer);
|
||||
self.el.addClass('focused')
|
||||
if(this.get('value'))
|
||||
self.hideResults(false)
|
||||
},
|
||||
'blur': function(){
|
||||
(function(){
|
||||
focus_timer = (function(){
|
||||
self.el.removeClass('focused')
|
||||
}).delay(2000);
|
||||
}).delay(100);
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Element('span.enter', {
|
||||
new Element('a.icon2', {
|
||||
'events': {
|
||||
'click': self.keyup.bind(self)
|
||||
},
|
||||
'text':'Enter'
|
||||
}),
|
||||
new Element('a', {
|
||||
'events': {
|
||||
'click': self.clear.bind(self)
|
||||
'click': self.clear.bind(self),
|
||||
'touchend': self.clear.bind(self)
|
||||
}
|
||||
})
|
||||
),
|
||||
@@ -59,13 +56,21 @@ Block.Search = new Class({
|
||||
var self = this;
|
||||
(e).preventDefault();
|
||||
|
||||
self.last_q = '';
|
||||
self.input.set('value', '');
|
||||
self.input.focus()
|
||||
if(self.last_q === ''){
|
||||
self.input.blur()
|
||||
self.last_q = null;
|
||||
}
|
||||
else {
|
||||
|
||||
self.movies = []
|
||||
self.results.empty()
|
||||
self.el.removeClass('filled')
|
||||
self.last_q = '';
|
||||
self.input.set('value', '');
|
||||
self.input.focus()
|
||||
|
||||
self.movies = []
|
||||
self.results.empty()
|
||||
self.el.removeClass('filled')
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
hideResults: function(bool){
|
||||
@@ -92,8 +97,10 @@ Block.Search = new Class({
|
||||
|
||||
self.el[self.q() ? 'addClass' : 'removeClass']('filled')
|
||||
|
||||
if(self.q() != self.last_q && (['enter'].indexOf(e.key) > -1 || e.type == 'click'))
|
||||
self.autocomplete()
|
||||
if(self.q() != self.last_q){
|
||||
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
|
||||
self.autocomplete_timer = self.autocomplete.delay(300, self)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@@ -197,6 +204,11 @@ Block.Search.Item = new Class({
|
||||
self.el = new Element('div.movie_result', {
|
||||
'id': info.imdb
|
||||
}).adopt(
|
||||
self.thumbnail = info.images && info.images.poster.length > 0 ? new Element('img.thumbnail', {
|
||||
'src': info.images.poster[0],
|
||||
'height': null,
|
||||
'width': null
|
||||
}) : null,
|
||||
self.options_el = new Element('div.options.inlay'),
|
||||
self.data_container = new Element('div.data', {
|
||||
'tween': {
|
||||
@@ -207,11 +219,6 @@ Block.Search.Item = new Class({
|
||||
'click': self.showOptions.bind(self)
|
||||
}
|
||||
}).adopt(
|
||||
self.thumbnail = info.images && info.images.poster.length > 0 ? new Element('img.thumbnail', {
|
||||
'src': info.images.poster[0],
|
||||
'height': null,
|
||||
'width': null
|
||||
}) : null,
|
||||
new Element('div.info').adopt(
|
||||
self.title = new Element('h2', {
|
||||
'text': info.titles[0]
|
||||
@@ -219,28 +226,11 @@ Block.Search.Item = new Class({
|
||||
self.year = info.year ? new Element('span.year', {
|
||||
'text': info.year
|
||||
}) : null
|
||||
),
|
||||
self.tagline = new Element('span.tagline', {
|
||||
'text': info.tagline ? info.tagline : info.plot,
|
||||
'title': info.tagline ? info.tagline : info.plot
|
||||
}),
|
||||
self.director = self.info.director ? new Element('span.director', {
|
||||
'text': 'Director:' + info.director
|
||||
}) : null,
|
||||
self.starring = info.actors ? new Element('span.actors', {
|
||||
'text': 'Starring:'
|
||||
}) : null
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if(info.actors){
|
||||
Object.each(info.actors, function(actor){
|
||||
new Element('span', {
|
||||
'text': actor
|
||||
}).inject(self.starring)
|
||||
})
|
||||
}
|
||||
|
||||
info.titles.each(function(title){
|
||||
self.alternativeTitle({
|
||||
@@ -320,11 +310,6 @@ Block.Search.Item = new Class({
|
||||
|
||||
self.options_el.grab(
|
||||
new Element('div').adopt(
|
||||
self.thumbnail = (info.images && info.images.poster.length > 0) ? new Element('img.thumbnail', {
|
||||
'src': info.images.poster[0],
|
||||
'height': null,
|
||||
'width': null
|
||||
}) : null,
|
||||
self.info.in_wanted && self.info.in_wanted.profile ? new Element('span.in_wanted', {
|
||||
'text': 'Already in wanted list: ' + self.info.in_wanted.profile.label
|
||||
}) : (in_library ? new Element('span.in_library', {
|
||||
|
||||
@@ -61,6 +61,10 @@
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.profile .type .check {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.profile .quality_type select {
|
||||
width: 186px;
|
||||
margin-left: -1px;
|
||||
@@ -71,13 +75,13 @@
|
||||
}
|
||||
|
||||
.profile .types .type .handle {
|
||||
background: url('./handle.png') center;
|
||||
background: url('../../static/profile_plugin/handle.png') center;
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: grab;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -105,9 +109,9 @@
|
||||
}
|
||||
|
||||
#profile_ordering li {
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: grab;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.2);
|
||||
padding: 0 5px;
|
||||
}
|
||||
@@ -126,7 +130,7 @@
|
||||
}
|
||||
|
||||
#profile_ordering li .handle {
|
||||
background: url('./handle.png') center;
|
||||
background: url('../../static/profile_plugin/handle.png') center;
|
||||
width: 20px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
@@ -41,12 +41,15 @@ class Release(Plugin):
|
||||
addEvent('release.clean', self.clean)
|
||||
|
||||
def add(self, group):
|
||||
|
||||
db = get_session()
|
||||
|
||||
identifier = '%s.%s.%s' % (group['library']['identifier'], group['meta_data'].get('audio', 'unknown'), group['meta_data']['quality']['identifier'])
|
||||
|
||||
|
||||
done_status, snatched_status = fireEvent('status.get', ['done', 'snatched'], single = True)
|
||||
|
||||
# Add movie
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
movie = db.query(Movie).filter_by(library_id = group['library'].get('id')).first()
|
||||
if not movie:
|
||||
movie = Movie(
|
||||
@@ -58,7 +61,6 @@ class Release(Plugin):
|
||||
db.commit()
|
||||
|
||||
# Add Release
|
||||
snatched_status = fireEvent('status.get', 'snatched', single = True)
|
||||
rel = db.query(Relea).filter(
|
||||
or_(
|
||||
Relea.identifier == identifier,
|
||||
@@ -76,15 +78,19 @@ class Release(Plugin):
|
||||
db.commit()
|
||||
|
||||
# Add each file type
|
||||
added_files = []
|
||||
for type in group['files']:
|
||||
for cur_file in group['files'][type]:
|
||||
added_file = self.saveFile(cur_file, type = type, include_media_info = type is 'movie')
|
||||
try:
|
||||
added_file = db.query(File).filter_by(id = added_file.get('id')).one()
|
||||
rel.files.append(added_file)
|
||||
db.commit()
|
||||
except Exception, e:
|
||||
log.debug('Failed to attach "%s" to release: %s', (cur_file, e))
|
||||
added_files.append(added_file.get('id'))
|
||||
|
||||
# Add the release files in batch
|
||||
try:
|
||||
added_files = db.query(File).filter(or_(*[File.id == x for x in added_files])).all()
|
||||
rel.files.append(added_files)
|
||||
db.commit()
|
||||
except Exception, e:
|
||||
log.debug('Failed to attach "%s" to release: %s', (cur_file, e))
|
||||
|
||||
fireEvent('movie.restatus', movie.id)
|
||||
|
||||
@@ -147,8 +153,7 @@ class Release(Plugin):
|
||||
|
||||
rel = db.query(Relea).filter_by(id = id).first()
|
||||
if rel:
|
||||
ignored_status = fireEvent('status.get', 'ignored', single = True)
|
||||
available_status = fireEvent('status.get', 'available', single = True)
|
||||
ignored_status, available_status = fireEvent('status.get', ['ignored', 'available'], single = True)
|
||||
rel.status_id = available_status.get('id') if rel.status_id is ignored_status.get('id') else ignored_status.get('id')
|
||||
db.commit()
|
||||
|
||||
@@ -161,8 +166,7 @@ class Release(Plugin):
|
||||
db = get_session()
|
||||
id = getParam('id')
|
||||
|
||||
snatched_status = fireEvent('status.add', 'snatched', single = True)
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
snatched_status, done_status = fireEvent('status.get', ['snatched', 'done'], single = True)
|
||||
|
||||
rel = db.query(Relea).filter_by(id = id).first()
|
||||
if rel:
|
||||
|
||||
@@ -114,11 +114,11 @@ config = [{
|
||||
},
|
||||
{
|
||||
'name': 'file_action',
|
||||
'label': 'File Action',
|
||||
'label': 'Torrent File Action',
|
||||
'default': 'move',
|
||||
'type': 'dropdown',
|
||||
'values': [('Move', 'move'), ('Copy', 'copy'), ('Hard link', 'hardlink'), ('Sym link', 'symlink'), ('Move & Sym link', 'move_symlink')],
|
||||
'description': 'Define which kind of file operation you want to use. Before you start using <a href="http://en.wikipedia.org/wiki/Hard_link">hard links</a> or <a href="http://en.wikipedia.org/wiki/Sym_link">sym links</a>, PLEASE read about their possible drawbacks.',
|
||||
'description': 'Define which kind of file operation you want to use for torrents. Before you start using <a href="http://en.wikipedia.org/wiki/Hard_link">hard links</a> or <a href="http://en.wikipedia.org/wiki/Sym_link">sym links</a>, PLEASE read about their possible drawbacks.',
|
||||
'advanced': True,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4,13 +4,12 @@ from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import toUnicode, ss
|
||||
from couchpotato.core.helpers.request import getParams, jsonified
|
||||
from couchpotato.core.helpers.variable import getExt, mergeDicts, getTitle, \
|
||||
getImdb
|
||||
getImdb, link, symlink
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.settings.model import Library, File, Profile, Release, \
|
||||
ReleaseInfo
|
||||
from couchpotato.environment import Env
|
||||
from linktastic.linktastic import link, symlink
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
@@ -20,7 +19,6 @@ import traceback
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Renamer(Plugin):
|
||||
|
||||
renaming_started = False
|
||||
@@ -70,15 +68,14 @@ class Renamer(Plugin):
|
||||
|
||||
fireEventAsync('renamer.scan',
|
||||
movie_folder = movie_folder,
|
||||
downloader = downloader,
|
||||
download_id = download_id
|
||||
download_info = {'id': download_id, 'downloader': downloader} if download_id else None
|
||||
)
|
||||
|
||||
return jsonified({
|
||||
'success': True
|
||||
})
|
||||
|
||||
def scan(self, movie_folder = None, downloader = None, download_id = None):
|
||||
def scan(self, movie_folder = None, download_info = None):
|
||||
|
||||
if self.isDisabled():
|
||||
return
|
||||
@@ -103,7 +100,7 @@ class Renamer(Plugin):
|
||||
|
||||
# make sure the movie folder name is included in the search
|
||||
folder = None
|
||||
movie_files = []
|
||||
files = []
|
||||
if movie_folder:
|
||||
log.info('Scanning movie folder %s...', movie_folder)
|
||||
movie_folder = movie_folder.rstrip(os.path.sep)
|
||||
@@ -112,37 +109,17 @@ class Renamer(Plugin):
|
||||
# Get all files from the specified folder
|
||||
try:
|
||||
for root, folders, names in os.walk(movie_folder):
|
||||
movie_files.extend([os.path.join(root, name) for name in names])
|
||||
files.extend([os.path.join(root, name) for name in names])
|
||||
except:
|
||||
log.error('Failed getting files from %s: %s', (movie_folder, traceback.format_exc()))
|
||||
|
||||
db = get_session()
|
||||
|
||||
# Get the release with the downloader ID that was downloded by the downloader
|
||||
download_info = None
|
||||
if download_id and downloader:
|
||||
rls = None
|
||||
|
||||
rlsnfo_dwnlds = db.query(ReleaseInfo).filter_by(identifier = 'download_downloader', value = downloader).all()
|
||||
rlsnfo_ids = db.query(ReleaseInfo).filter_by(identifier = 'download_id', value = download_id).all()
|
||||
|
||||
for rlsnfo_dwnld in rlsnfo_dwnlds:
|
||||
for rlsnfo_id in rlsnfo_ids:
|
||||
if rlsnfo_id.release == rlsnfo_dwnld.release:
|
||||
rls = rlsnfo_id.release
|
||||
break
|
||||
if rls: break
|
||||
|
||||
if rls:
|
||||
download_info = {
|
||||
'imdb_id': rls.movie.library.identifier,
|
||||
'quality': rls.quality.identifier,
|
||||
}
|
||||
else:
|
||||
log.error('Download ID %s from downloader %s not found in releases', (download_id, downloader))
|
||||
# Extend the download info with info stored in the downloaded release
|
||||
download_info = self.extendDownloadInfo(download_info)
|
||||
|
||||
groups = fireEvent('scanner.scan', folder = folder if folder else self.conf('from'),
|
||||
files = movie_files, download_info = download_info, return_ignored = False, single = True)
|
||||
files = files, download_info = download_info, return_ignored = False, single = True)
|
||||
|
||||
destination = self.conf('to')
|
||||
folder_name = self.conf('folder_name')
|
||||
@@ -152,10 +129,8 @@ class Renamer(Plugin):
|
||||
separator = self.conf('separator')
|
||||
|
||||
# Statusses
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
active_status = fireEvent('status.get', 'active', single = True)
|
||||
downloaded_status = fireEvent('status.get', 'downloaded', single = True)
|
||||
snatched_status = fireEvent('status.get', 'snatched', single = True)
|
||||
done_status, active_status, downloaded_status, snatched_status = \
|
||||
fireEvent('status.get', ['done', 'active', 'downloaded', 'snatched'], single = True)
|
||||
|
||||
for group_identifier in groups:
|
||||
|
||||
@@ -375,7 +350,7 @@ class Renamer(Plugin):
|
||||
else:
|
||||
log.info('Better quality release already exists for %s, with quality %s', (movie.library.titles[0].title, release.quality.label))
|
||||
|
||||
# Add _EXISTS_ to the parent dir
|
||||
# Add exists tag to the .ignore file
|
||||
self.tagDir(group, 'exists')
|
||||
|
||||
# Notify on rename fail
|
||||
@@ -396,7 +371,8 @@ class Renamer(Plugin):
|
||||
db.commit()
|
||||
|
||||
# Remove leftover files
|
||||
if self.conf('cleanup') and not self.conf('move_leftover') and remove_leftovers:
|
||||
if self.conf('cleanup') and not self.conf('move_leftover') and remove_leftovers and \
|
||||
not (self.conf('file_action') != 'move' and self.downloadIsTorrent(download_info)):
|
||||
log.debug('Removing leftover files')
|
||||
for current_file in group['files']['leftover']:
|
||||
remove_files.append(current_file)
|
||||
@@ -421,7 +397,7 @@ class Renamer(Plugin):
|
||||
os.remove(src)
|
||||
|
||||
parent_dir = os.path.normpath(os.path.dirname(src))
|
||||
if delete_folders.count(parent_dir) == 0 and os.path.isdir(parent_dir) and destination != parent_dir:
|
||||
if delete_folders.count(parent_dir) == 0 and os.path.isdir(parent_dir) and not parent_dir in [destination, movie_folder] and not self.conf('from') in parent_dir:
|
||||
delete_folders.append(parent_dir)
|
||||
|
||||
except:
|
||||
@@ -446,13 +422,13 @@ class Renamer(Plugin):
|
||||
self.makeDir(os.path.dirname(dst))
|
||||
|
||||
try:
|
||||
self.moveFile(src, dst)
|
||||
self.moveFile(src, dst, forcemove = not self.downloadIsTorrent(download_info))
|
||||
group['renamed_files'].append(dst)
|
||||
except:
|
||||
log.error('Failed moving the file "%s" : %s', (os.path.basename(src), traceback.format_exc()))
|
||||
self.tagDir(group, 'failed_rename')
|
||||
|
||||
if self.conf('file_action') != 'move':
|
||||
if self.conf('file_action') != 'move' and self.downloadIsTorrent(download_info):
|
||||
self.tagDir(group, 'renamed already')
|
||||
|
||||
# Remove matching releases
|
||||
@@ -518,10 +494,12 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
self.createFile(ignore_file, text)
|
||||
|
||||
|
||||
def moveFile(self, old, dest):
|
||||
def moveFile(self, old, dest, forcemove = False):
|
||||
dest = ss(dest)
|
||||
try:
|
||||
if self.conf('file_action') == 'hardlink':
|
||||
if forcemove:
|
||||
shutil.move(old, dest)
|
||||
elif self.conf('file_action') == 'hardlink':
|
||||
link(old, dest)
|
||||
elif self.conf('file_action') == 'symlink':
|
||||
symlink(old, dest)
|
||||
@@ -606,11 +584,8 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
|
||||
self.checking_snatched = True
|
||||
|
||||
snatched_status = fireEvent('status.get', 'snatched', single = True)
|
||||
ignored_status = fireEvent('status.get', 'ignored', single = True)
|
||||
failed_status = fireEvent('status.get', 'failed', single = True)
|
||||
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
snatched_status, ignored_status, failed_status, done_status = \
|
||||
fireEvent('status.get', ['snatched', 'ignored', 'failed', 'done'], single = True)
|
||||
|
||||
db = get_session()
|
||||
rels = db.query(Release).filter_by(status_id = snatched_status.get('id')).all()
|
||||
@@ -665,17 +640,16 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
pass
|
||||
elif item['status'] == 'failed':
|
||||
fireEvent('download.remove_failed', item, single = True)
|
||||
rel.status_id = failed_status.get('id')
|
||||
rel.last_edit = int(time.time())
|
||||
db.commit()
|
||||
|
||||
if self.conf('next_on_failed'):
|
||||
fireEvent('searcher.try_next_release', movie_id = rel.movie_id)
|
||||
else:
|
||||
rel.status_id = failed_status.get('id')
|
||||
rel.last_edit = int(time.time())
|
||||
db.commit()
|
||||
elif item['status'] == 'completed':
|
||||
log.info('Download of %s completed!', item['name'])
|
||||
if item['id'] and item['downloader'] and item['folder']:
|
||||
fireEventAsync('renamer.scan', movie_folder = item['folder'], downloader = item['downloader'], download_id = item['id'])
|
||||
fireEventAsync('renamer.scan', movie_folder = item['folder'], download_info = item)
|
||||
else:
|
||||
scan_required = True
|
||||
|
||||
@@ -694,3 +668,38 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
self.checking_snatched = False
|
||||
|
||||
return True
|
||||
|
||||
def extendDownloadInfo(self, download_info):
|
||||
|
||||
rls = None
|
||||
|
||||
if download_info and download_info.get('id') and download_info.get('downloader'):
|
||||
|
||||
db = get_session()
|
||||
|
||||
rlsnfo_dwnlds = db.query(ReleaseInfo).filter_by(identifier = 'download_downloader', value = download_info.get('downloader')).all()
|
||||
rlsnfo_ids = db.query(ReleaseInfo).filter_by(identifier = 'download_id', value = download_info.get('id')).all()
|
||||
|
||||
for rlsnfo_dwnld in rlsnfo_dwnlds:
|
||||
for rlsnfo_id in rlsnfo_ids:
|
||||
if rlsnfo_id.release == rlsnfo_dwnld.release:
|
||||
rls = rlsnfo_id.release
|
||||
break
|
||||
if rls: break
|
||||
|
||||
if not rls:
|
||||
log.error('Download ID %s from downloader %s not found in releases', (download_info.get('id'), download_info.get('downloader')))
|
||||
|
||||
if rls:
|
||||
|
||||
rls_dict = rls.to_dict({'info':{}})
|
||||
download_info.update({
|
||||
'imdb_id': rls.movie.library.identifier,
|
||||
'quality': rls.quality.identifier,
|
||||
'type': rls_dict.get('info', {}).get('type')
|
||||
})
|
||||
|
||||
return download_info
|
||||
|
||||
def downloadIsTorrent(self, download_info):
|
||||
return download_info and download_info.get('type') in ['torrent', 'torrent_magnet']
|
||||
|
||||
@@ -25,12 +25,13 @@ config = [{
|
||||
'label': 'Required words',
|
||||
'default': '',
|
||||
'placeholder': 'Example: DTS, AC3 & English',
|
||||
'description': 'Ignore releases that don\'t contain at least one set of words. Sets are separated by "," and each word within a set must be separated with "&"'
|
||||
'description': 'A release should contain at least one set of words. Sets are separated by "," and each word within a set must be separated with "&"'
|
||||
},
|
||||
{
|
||||
'name': 'ignored_words',
|
||||
'label': 'Ignored words',
|
||||
'default': 'german, dutch, french, truefrench, danish, swedish, spanish, italian, korean, dubbed, swesub, korsub, dksubs',
|
||||
'description': 'Ignores releases that match any of these sets. (Works like explained above)'
|
||||
},
|
||||
{
|
||||
'name': 'preferred_method',
|
||||
|
||||
@@ -146,8 +146,7 @@ class Searcher(Plugin):
|
||||
|
||||
pre_releases = fireEvent('quality.pre_releases', single = True)
|
||||
release_dates = fireEvent('library.update_release_date', identifier = movie['library']['identifier'], merge = True)
|
||||
available_status = fireEvent('status.get', 'available', single = True)
|
||||
ignored_status = fireEvent('status.get', 'ignored', single = True)
|
||||
available_status, ignored_status = fireEvent('status.get', ['available', 'ignored'], single = True)
|
||||
|
||||
found_releases = []
|
||||
|
||||
@@ -384,8 +383,9 @@ class Searcher(Plugin):
|
||||
movie_words = re.split('\W+', simplifyString(movie_name))
|
||||
nzb_name = simplifyString(nzb['name'])
|
||||
nzb_words = re.split('\W+', nzb_name)
|
||||
required_words = splitString(self.conf('required_words').lower())
|
||||
|
||||
# Make sure it has required words
|
||||
required_words = splitString(self.conf('required_words').lower())
|
||||
req_match = 0
|
||||
for req_set in required_words:
|
||||
req = splitString(req_set, '&')
|
||||
@@ -395,19 +395,24 @@ class Searcher(Plugin):
|
||||
log.info2("Wrong: Required word missing: %s" % nzb['name'])
|
||||
return False
|
||||
|
||||
# Ignore releases
|
||||
ignored_words = splitString(self.conf('ignored_words').lower())
|
||||
blacklisted = list(set(nzb_words) & set(ignored_words) - set(movie_words))
|
||||
if self.conf('ignored_words') and blacklisted:
|
||||
log.info2("Wrong: '%s' blacklisted words: %s" % (nzb['name'], ", ".join(blacklisted)))
|
||||
ignored_match = 0
|
||||
for ignored_set in ignored_words:
|
||||
ignored = splitString(ignored_set, '&')
|
||||
ignored_match += len(list(set(nzb_words) & set(ignored))) == len(ignored)
|
||||
|
||||
if self.conf('ignored_words') and ignored_match:
|
||||
log.info2("Wrong: '%s' contains 'ignored words'" % (nzb['name']))
|
||||
return False
|
||||
|
||||
# Ignore porn stuff
|
||||
pron_tags = ['xxx', 'sex', 'anal', 'tits', 'fuck', 'porn', 'orgy', 'milf', 'boobs', 'erotica', 'erotic']
|
||||
pron_words = list(set(nzb_words) & set(pron_tags) - set(movie_words))
|
||||
if pron_words:
|
||||
log.info('Wrong: %s, probably pr0n', (nzb['name']))
|
||||
return False
|
||||
|
||||
#qualities = fireEvent('quality.all', single = True)
|
||||
preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True)
|
||||
|
||||
# Contains lower quality string
|
||||
|
||||
@@ -25,13 +25,14 @@ class StatusPlugin(Plugin):
|
||||
'available': 'Available',
|
||||
'suggest': 'Suggest',
|
||||
}
|
||||
status_cached = {}
|
||||
|
||||
def __init__(self):
|
||||
addEvent('status.add', self.add)
|
||||
addEvent('status.get', self.add) # Alias for .add
|
||||
addEvent('status.get', self.get)
|
||||
addEvent('status.get_by_id', self.getById)
|
||||
addEvent('status.all', self.all)
|
||||
addEvent('app.initialize', self.fill)
|
||||
addEvent('app.load', self.all) # Cache all statuses
|
||||
|
||||
addApiView('status.list', self.list, docs = {
|
||||
'desc': 'Check for available update',
|
||||
@@ -67,26 +68,40 @@ class StatusPlugin(Plugin):
|
||||
s = status.to_dict()
|
||||
temp.append(s)
|
||||
|
||||
#db.close()
|
||||
# Update cache
|
||||
self.status_cached[status.identifier] = s
|
||||
|
||||
return temp
|
||||
|
||||
def add(self, identifier):
|
||||
def get(self, identifiers):
|
||||
|
||||
if not isinstance(identifiers, (list)):
|
||||
identifiers = [identifiers]
|
||||
|
||||
db = get_session()
|
||||
return_list = []
|
||||
|
||||
s = db.query(Status).filter_by(identifier = identifier).first()
|
||||
if not s:
|
||||
s = Status(
|
||||
identifier = identifier,
|
||||
label = toUnicode(identifier.capitalize())
|
||||
)
|
||||
db.add(s)
|
||||
db.commit()
|
||||
for identifier in identifiers:
|
||||
|
||||
status_dict = s.to_dict()
|
||||
if self.status_cached.get(identifier):
|
||||
return_list.append(self.status_cached.get(identifier))
|
||||
continue
|
||||
|
||||
#db.close()
|
||||
return status_dict
|
||||
s = db.query(Status).filter_by(identifier = identifier).first()
|
||||
if not s:
|
||||
s = Status(
|
||||
identifier = identifier,
|
||||
label = toUnicode(identifier.capitalize())
|
||||
)
|
||||
db.add(s)
|
||||
db.commit()
|
||||
|
||||
status_dict = s.to_dict()
|
||||
|
||||
self.status_cached[identifier] = status_dict
|
||||
return_list.append(status_dict)
|
||||
|
||||
return return_list if len(identifiers) > 1 else return_list[0]
|
||||
|
||||
def fill(self):
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ config = [{
|
||||
'label': 'Naming',
|
||||
'default': '<filename>-trailer',
|
||||
'advanced': True,
|
||||
'description': 'Use <filename> to use above settings.'
|
||||
'description': 'Use <strong><filename></strong> to use above settings.'
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -22,10 +22,15 @@ class Trailer(Plugin):
|
||||
return False
|
||||
|
||||
for trailer in trailers.get(self.conf('quality'), []):
|
||||
filename = self.conf('name').replace('<filename>', group['filename']) + ('.%s' % getExt(trailer))
|
||||
|
||||
ext = getExt(trailer)
|
||||
filename = self.conf('name').replace('<filename>', group['filename']) + ('.%s' % ('mp4' if len(ext) > 5 else ext))
|
||||
destination = os.path.join(group['destination_dir'], filename)
|
||||
if not os.path.isfile(destination):
|
||||
fireEvent('file.download', url = trailer, dest = destination, urlopen_kwargs = {'headers': {'User-Agent': 'Quicktime'}}, single = True)
|
||||
trailer_file = fireEvent('file.download', url = trailer, dest = destination, urlopen_kwargs = {'headers': {'User-Agent': 'Quicktime'}}, single = True)
|
||||
if os.path.getsize(trailer_file) < (1024 * 1024): # Don't trust small trailers (1MB), try next one
|
||||
os.unlink(trailer_file)
|
||||
continue
|
||||
else:
|
||||
log.debug('Trailer already exists: %s', destination)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.page.userscript .frame.loading {
|
||||
@@ -12,3 +13,26 @@
|
||||
font-size: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page.userscript .movie_result {
|
||||
height: 140px;
|
||||
}
|
||||
.page.userscript .movie_result .thumbnail {
|
||||
width: 90px;
|
||||
}
|
||||
.page.userscript .movie_result .options {
|
||||
left: 90px;
|
||||
padding: 54px 15px;
|
||||
}
|
||||
|
||||
.page.userscript .movie_result .year {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.page.userscript .movie_result .options select[name="title"] {
|
||||
width: 190px;
|
||||
}
|
||||
|
||||
.page.userscript .movie_result .options select[name="profile"] {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
@@ -63,28 +63,19 @@ var UserscriptSettingTab = new Class({
|
||||
self.settings = App.getPage('Settings')
|
||||
self.settings.addEvent('create', function(){
|
||||
|
||||
// See if userscript can be installed
|
||||
var userscript = false;
|
||||
try {
|
||||
if(Components.interfaces.gmIGreasemonkeyService)
|
||||
userscript = true
|
||||
}
|
||||
catch(e){
|
||||
userscript = Browser.chrome === true;
|
||||
}
|
||||
|
||||
var host_url = window.location.protocol + '//' + window.location.host;
|
||||
|
||||
self.settings.createGroup({
|
||||
'name': 'userscript',
|
||||
'label': 'Install the bookmarklet' + (userscript ? ' or userscript' : ''),
|
||||
'label': 'Install the bookmarklet or userscript',
|
||||
'description': 'Easily add movies via imdb.com, appletrailers and more'
|
||||
}).inject(self.settings.tabs.automation.content, 'top').adopt(
|
||||
(userscript ? [new Element('a.userscript.button', {
|
||||
new Element('a.userscript.button', {
|
||||
'text': 'Install userscript',
|
||||
'href': Api.createUrl('userscript.get')+randomString()+'/couchpotato.user.js',
|
||||
'target': '_self'
|
||||
}), new Element('span.or[text=or]')] : null),
|
||||
'target': '_blank'
|
||||
}),
|
||||
new Element('span.or[text=or]'),
|
||||
new Element('span.bookmarklet').adopt(
|
||||
new Element('a.button.green', {
|
||||
'text': '+CouchPotato',
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// ==UserScript==
|
||||
//
|
||||
// If you can read this, you need to enable or install the Greasemonkey add-on for firefox
|
||||
// If you are using Chrome, download this file and drag it to the extensions tab
|
||||
// Other browsers, use the bookmarklet
|
||||
//
|
||||
// @name CouchPotato UserScript
|
||||
// @description Add movies like a real CouchPotato
|
||||
// @grant none
|
||||
|
||||
@@ -1,49 +1,60 @@
|
||||
.page.wizard .uniForm {
|
||||
width: 80%;
|
||||
margin: 0 auto 30px;
|
||||
margin: 0 0 30px;
|
||||
width: 83%;
|
||||
}
|
||||
|
||||
.page.wizard h1 {
|
||||
padding: 10px 30px;
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
margin: 0 5px;
|
||||
display: block;
|
||||
font-size: 30px;
|
||||
margin-top: 80px;
|
||||
}
|
||||
|
||||
.page.wizard .description {
|
||||
padding: 10px 30px;
|
||||
font-size: 18px;
|
||||
padding: 10px 5px;
|
||||
font-size: 1.45em;
|
||||
line-height: 1.4em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.page.wizard .tab_wrapper {
|
||||
background: #5c697b;
|
||||
padding: 10px 0;
|
||||
font-size: 18px;
|
||||
height: 65px;
|
||||
font-size: 1.75em;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
min-width: 960px;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
box-shadow: 0 0 50px rgba(0,0,0,0.55);
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.page.wizard .tab_wrapper .tabs {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-width: 960px;
|
||||
}
|
||||
|
||||
.page.wizard .tabs li {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
}
|
||||
.page.wizard .tabs li a {
|
||||
padding: 20px 10px;
|
||||
height: 100%;
|
||||
display: block;
|
||||
color: #FFF;
|
||||
font-weight: normal;
|
||||
border-bottom: 4px solid transparent;
|
||||
}
|
||||
|
||||
.page.wizard .tabs li:hover a { border-color: #047792; }
|
||||
.page.wizard .tabs li.done a { border-color: #04bce6; }
|
||||
|
||||
.page.wizard .tab_wrapper .pointer {
|
||||
border-right: 10px solid transparent;
|
||||
@@ -61,27 +72,13 @@
|
||||
.page.wizard form > div {
|
||||
min-height: 300px;
|
||||
}
|
||||
.page.wizard .wgroup_finish {
|
||||
height: 300px;
|
||||
}
|
||||
.page.wizard .wgroup_finish h1 {
|
||||
text-align: center;
|
||||
}
|
||||
.page.wizard .wgroup_finish .wizard_support,
|
||||
.page.wizard .wgroup_finish .description {
|
||||
font-size: 25px;
|
||||
line-height: 120%;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page.wizard .button.green {
|
||||
padding: 20px;
|
||||
font-size: 25px;
|
||||
margin: 10px 30px 80px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
.page.wizard .button.green {
|
||||
padding: 20px;
|
||||
font-size: 25px;
|
||||
margin: 10px 0 80px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.page.wizard .tab_nzb_providers {
|
||||
margin: 20px 0 0 0;
|
||||
|
||||
@@ -9,27 +9,12 @@ Page.Wizard = new Class({
|
||||
headers: {
|
||||
'welcome': {
|
||||
'title': 'Welcome to the new CouchPotato',
|
||||
'description': 'To get started, fill in each of the following settings as much as you can. <br />Maybe first start with importing your movies from the previous CouchPotato',
|
||||
'description': 'To get started, fill in each of the following settings as much as you can.',
|
||||
'content': new Element('div', {
|
||||
'styles': {
|
||||
'margin': '0 0 0 30px'
|
||||
}
|
||||
}).adopt(
|
||||
new Element('div', {
|
||||
'html': 'Select the <strong>data.db</strong>. It should be in your CouchPotato root directory.'
|
||||
}),
|
||||
self.import_iframe = new Element('iframe', {
|
||||
'styles': {
|
||||
'height': 40,
|
||||
'width': 300,
|
||||
'border': 0,
|
||||
'overflow': 'hidden'
|
||||
}
|
||||
})
|
||||
),
|
||||
'event': function(){
|
||||
self.import_iframe.set('src', Api.createUrl('v1.import'))
|
||||
}
|
||||
})
|
||||
},
|
||||
'general': {
|
||||
'title': 'General',
|
||||
@@ -178,7 +163,7 @@ Page.Wizard = new Class({
|
||||
'href': App.createUrl('wizard/'+group),
|
||||
'text': (self.headers[group].label || group).capitalize()
|
||||
})
|
||||
).inject(tabs);
|
||||
).inject(tabs)
|
||||
|
||||
}
|
||||
else
|
||||
@@ -214,13 +199,7 @@ Page.Wizard = new Class({
|
||||
self.el.getElement('.t_searcher').hide();
|
||||
|
||||
// Add pointer
|
||||
new Element('.tab_wrapper').wraps(tabs).adopt(
|
||||
self.pointer = new Element('.pointer', {
|
||||
'tween': {
|
||||
'transition': 'quint:in:out'
|
||||
}
|
||||
})
|
||||
);
|
||||
new Element('.tab_wrapper').wraps(tabs);
|
||||
|
||||
// Add nav
|
||||
var minimum = self.el.getSize().y-window.getSize().y;
|
||||
@@ -232,16 +211,18 @@ Page.Wizard = new Class({
|
||||
if(!t) return;
|
||||
|
||||
var func = function(){
|
||||
var ct = t.getCoordinates();
|
||||
self.pointer.tween('left', ct.left+(ct.width/2)-(self.pointer.getWidth()/2));
|
||||
// Activate all previous ones
|
||||
self.groups.each(function(groups2, nr2){
|
||||
var t2 = self.el.getElement('.t_'+groups2);
|
||||
t2[nr2 > nr ? 'removeClass' : 'addClass' ]('done');
|
||||
})
|
||||
g.tween('opacity', 1);
|
||||
}
|
||||
|
||||
if(nr == 0)
|
||||
func();
|
||||
|
||||
|
||||
var ss = new ScrollSpy( {
|
||||
new ScrollSpy( {
|
||||
min: function(){
|
||||
var c = g.getCoordinates();
|
||||
var top = c.top-(window.getSize().y/2);
|
||||
|
||||
@@ -52,8 +52,7 @@ class MovieResultModifier(Plugin):
|
||||
if l:
|
||||
|
||||
# Statuses
|
||||
active_status = fireEvent('status.get', 'active', single = True)
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
active_status, done_status = fireEvent('status.get', ['active', 'done'], single = True)
|
||||
|
||||
for movie in l.movies:
|
||||
if movie.status_id == active_status['id']:
|
||||
|
||||
@@ -2,6 +2,7 @@ from couchpotato import get_session
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.request import jsonified, getParams
|
||||
from couchpotato.core.helpers.variable import tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.movie.base import MovieProvider
|
||||
from couchpotato.core.settings.model import Movie
|
||||
@@ -19,19 +20,37 @@ class CouchPotatoApi(MovieProvider):
|
||||
'is_movie': 'https://couchpota.to/api/ismovie/%s/',
|
||||
'eta': 'https://couchpota.to/api/eta/%s/',
|
||||
'suggest': 'https://couchpota.to/api/suggest/',
|
||||
'updater': 'https://couchpota.to/api/updater/?%s',
|
||||
'messages': 'https://couchpota.to/api/messages/?%s',
|
||||
}
|
||||
http_time_between_calls = 0
|
||||
api_version = 1
|
||||
|
||||
def __init__(self):
|
||||
#addApiView('movie.suggest', self.suggestView)
|
||||
|
||||
addEvent('movie.info', self.getInfo, priority = 1)
|
||||
addEvent('movie.search', self.search, priority = 1)
|
||||
addEvent('movie.release_date', self.getReleaseDate)
|
||||
addEvent('movie.suggest', self.suggest)
|
||||
addEvent('movie.is_movie', self.isMovie)
|
||||
|
||||
addEvent('cp.source_url', self.getSourceUrl)
|
||||
addEvent('cp.messages', self.getMessages)
|
||||
|
||||
def getMessages(self, last_check = 0):
|
||||
|
||||
data = self.getJsonData(self.urls['messages'] % tryUrlencode({
|
||||
'last_check': last_check,
|
||||
}), headers = self.getRequestHeaders(), cache_timeout = 10)
|
||||
|
||||
return data
|
||||
|
||||
def getSourceUrl(self, repo = None, repo_name = None, branch = None):
|
||||
return self.getJsonData(self.urls['updater'] % tryUrlencode({
|
||||
'repo': repo,
|
||||
'name': repo_name,
|
||||
'branch': branch,
|
||||
}), headers = self.getRequestHeaders())
|
||||
|
||||
def search(self, q, limit = 12):
|
||||
return self.getJsonData(self.urls['search'] % tryUrlencode(q), headers = self.getRequestHeaders())
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ config = [{
|
||||
},
|
||||
{
|
||||
'name': 'api_key',
|
||||
'default': ',,,',
|
||||
'default': ',,,,,',
|
||||
'label': 'Api Key',
|
||||
'description': 'Can be found on your profile page',
|
||||
'type': 'combined',
|
||||
|
||||
@@ -74,3 +74,6 @@ class TorrentLeech(TorrentProvider):
|
||||
'remember_me': 'on',
|
||||
'login': 'submit',
|
||||
})
|
||||
|
||||
def loginSuccess(self, output):
|
||||
return '/user/account/logout' in output.lower() or 'welcome back' in output.lower()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from .main import Criticker
|
||||
|
||||
def start():
|
||||
return Criticker()
|
||||
|
||||
config = []
|
||||
6
couchpotato/core/providers/userscript/criticker/main.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from couchpotato.core.providers.userscript.base import UserscriptBase
|
||||
|
||||
|
||||
class Criticker(UserscriptBase):
|
||||
|
||||
includes = ['http://www.criticker.com/film/*']
|
||||
@@ -241,7 +241,8 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En
|
||||
from tornado.ioloop import IOLoop
|
||||
web_container = WSGIContainer(app)
|
||||
web_container._log = _log
|
||||
loop = IOLoop.instance()
|
||||
loop = IOLoop.current()
|
||||
|
||||
|
||||
application = Application([
|
||||
(r'%s/api/%s/nonblock/(.*)/' % (url_base, api_key), NonBlockHandler),
|
||||
|
||||
BIN
couchpotato/static/fonts/Elusive-Icons.eot
Executable file
298
couchpotato/static/fonts/Elusive-Icons.svg
Executable file
|
After Width: | Height: | Size: 213 KiB |
BIN
couchpotato/static/fonts/Elusive-Icons.ttf
Executable file
BIN
couchpotato/static/fonts/Elusive-Icons.woff
Executable file
BIN
couchpotato/static/fonts/OpenSans-Bold-webfont.eot
Executable file
146
couchpotato/static/fonts/OpenSans-Bold-webfont.svg
Executable file
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
This is a custom SVG webfont generated by Font Squirrel.
|
||||
Copyright : Digitized data copyright 20102011 Google Corporation
|
||||
Foundry : Ascender Corporation
|
||||
Foundry URL : httpwwwascendercorpcom
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="OpenSansBold" horiz-adv-x="1169" >
|
||||
<font-face units-per-em="2048" ascent="1638" descent="-410" />
|
||||
<missing-glyph horiz-adv-x="532" />
|
||||
<glyph unicode=" " horiz-adv-x="532" />
|
||||
<glyph unicode="	" horiz-adv-x="532" />
|
||||
<glyph unicode=" " horiz-adv-x="532" />
|
||||
<glyph unicode="!" horiz-adv-x="586" d="M117 143q0 84 45 127t131 43q83 0 128.5 -44t45.5 -126q0 -79 -46 -124.5t-128 -45.5q-84 0 -130 44.5t-46 125.5zM121 1462h346l-51 -977h-244z" />
|
||||
<glyph unicode=""" horiz-adv-x="967" d="M133 1462h279l-41 -528h-197zM555 1462h279l-41 -528h-197z" />
|
||||
<glyph unicode="#" horiz-adv-x="1323" d="M45 406v206h277l47 232h-252v209h289l77 407h219l-77 -407h198l78 407h215l-78 -407h240v-209h-279l-47 -232h258v-206h-297l-77 -406h-220l78 406h-194l-76 -406h-215l74 406h-238zM539 612h196l47 232h-196z" />
|
||||
<glyph unicode="$" d="M88 1049q0 145 113.5 238.5t316.5 113.5v153h137v-149q229 -10 414 -92l-94 -234q-156 64 -320 78v-295q195 -75 277.5 -130t121 -121t38.5 -154q0 -159 -115 -255.5t-322 -115.5v-205h-137v201q-244 5 -428 86v264q87 -43 209.5 -76t218.5 -39v310l-67 26 q-198 78 -280.5 169.5t-82.5 226.5zM389 1049q0 -44 30.5 -72.5t98.5 -58.5v235q-129 -19 -129 -104zM655 324q136 23 136 118q0 42 -34 71t-102 60v-249z" />
|
||||
<glyph unicode="%" horiz-adv-x="1845" d="M63 1026q0 457 345 457q169 0 259.5 -118.5t90.5 -338.5q0 -230 -89 -345.5t-261 -115.5q-165 0 -255 118.5t-90 342.5zM315 1024q0 -127 22.5 -189.5t72.5 -62.5q96 0 96 252q0 250 -96 250q-50 0 -72.5 -61.5t-22.5 -188.5zM395 0l811 1462h240l-811 -1462h-240z M1087 442q0 457 345 457q169 0 259.5 -118.5t90.5 -338.5q0 -229 -89 -344.5t-261 -115.5q-165 0 -255 118.5t-90 341.5zM1339 440q0 -127 22.5 -189.5t72.5 -62.5q96 0 96 252q0 250 -96 250q-50 0 -72.5 -61.5t-22.5 -188.5z" />
|
||||
<glyph unicode="&" horiz-adv-x="1536" d="M82 395q0 137 60.5 233.5t207.5 180.5q-75 86 -109 164.5t-34 171.5q0 152 116.5 245t311.5 93q186 0 297.5 -86.5t111.5 -231.5q0 -119 -69 -217.5t-223 -187.5l284 -277q71 117 123 301h318q-36 -135 -99 -263.5t-143 -227.5l301 -293h-377l-115 113 q-191 -133 -432 -133q-244 0 -387 112t-143 303zM403 424q0 -86 64.5 -137t165.5 -51q126 0 227 61l-332 330q-58 -44 -91.5 -92t-33.5 -111zM489 1124q0 -88 95 -194q86 48 132 94.5t46 108.5q0 53 -36 83.5t-93 30.5q-67 0 -105.5 -32t-38.5 -91z" />
|
||||
<glyph unicode="'" horiz-adv-x="545" d="M133 1462h279l-41 -528h-197z" />
|
||||
<glyph unicode="(" horiz-adv-x="694" d="M82 561q0 265 77.5 496t223.5 405h250q-141 -193 -213 -424t-72 -475q0 -245 73.5 -473.5t209.5 -413.5h-248q-147 170 -224 397t-77 488z" />
|
||||
<glyph unicode=")" horiz-adv-x="694" d="M61 1462h250q147 -175 224 -406.5t77 -494.5t-77.5 -490t-223.5 -395h-248q135 184 209 412.5t74 474.5q0 244 -72 475t-213 424z" />
|
||||
<glyph unicode="*" horiz-adv-x="1116" d="M63 1042l39 250l365 -104l-41 368h262l-41 -368l373 104l33 -252l-340 -24l223 -297l-227 -121l-156 313l-137 -311l-236 119l221 297z" />
|
||||
<glyph unicode="+" d="M88 612v219h387v390h219v-390h387v-219h-387v-385h-219v385h-387z" />
|
||||
<glyph unicode="," horiz-adv-x="594" d="M63 -264q65 266 101 502h280l15 -23q-52 -202 -176 -479h-220z" />
|
||||
<glyph unicode="-" horiz-adv-x="659" d="M61 424v250h537v-250h-537z" />
|
||||
<glyph unicode="." horiz-adv-x="584" d="M117 143q0 84 45 127t131 43q83 0 128.5 -44t45.5 -126q0 -79 -46 -124.5t-128 -45.5q-84 0 -130 44.5t-46 125.5z" />
|
||||
<glyph unicode="/" horiz-adv-x="846" d="M14 0l545 1462h277l-545 -1462h-277z" />
|
||||
<glyph unicode="0" d="M74 731q0 387 125 570.5t385 183.5q253 0 382.5 -192t129.5 -562q0 -383 -125.5 -567t-386.5 -184q-253 0 -381.5 190t-128.5 561zM381 731q0 -269 46.5 -385.5t156.5 -116.5q108 0 156 118t48 384q0 269 -48.5 386.5t-155.5 117.5q-109 0 -156 -117.5t-47 -386.5z" />
|
||||
<glyph unicode="1" d="M121 1087l471 375h254v-1462h-309v846l3 139l5 152q-77 -77 -107 -101l-168 -135z" />
|
||||
<glyph unicode="2" d="M78 1274q108 92 179 130t155 58.5t188 20.5q137 0 242 -50t163 -140t58 -206q0 -101 -35.5 -189.5t-110 -181.5t-262.5 -265l-188 -177v-14h637v-260h-1022v215l367 371q163 167 213 231.5t72 119.5t22 114q0 88 -48.5 131t-129.5 43q-85 0 -165 -39t-167 -111z" />
|
||||
<glyph unicode="3" d="M78 59v263q85 -43 187 -70t202 -27q153 0 226 52t73 167q0 103 -84 146t-268 43h-111v237h113q170 0 248.5 44.5t78.5 152.5q0 166 -208 166q-72 0 -146.5 -24t-165.5 -83l-143 213q200 144 477 144q227 0 358.5 -92t131.5 -256q0 -137 -83 -233t-233 -132v-6 q177 -22 268 -107.5t91 -230.5q0 -211 -153 -328.5t-437 -117.5q-238 0 -422 79z" />
|
||||
<glyph unicode="4" d="M35 303v215l641 944h285v-919h176v-240h-176v-303h-302v303h-624zM307 543h352v248q0 62 5 180t8 137h-8q-37 -82 -89 -160z" />
|
||||
<glyph unicode="5" d="M100 59v267q79 -42 184 -68.5t199 -26.5q283 0 283 232q0 221 -293 221q-53 0 -117 -10.5t-104 -22.5l-123 66l55 745h793v-262h-522l-27 -287l35 7q61 14 151 14q212 0 337.5 -119t125.5 -326q0 -245 -151 -377t-432 -132q-244 0 -394 79z" />
|
||||
<glyph unicode="6" d="M72 621q0 434 183.5 646t549.5 212q125 0 196 -15v-247q-89 20 -176 20q-159 0 -259.5 -48t-150.5 -142t-59 -267h13q99 170 317 170q196 0 307 -123t111 -340q0 -234 -132 -370.5t-366 -136.5q-162 0 -282.5 75t-186 219t-65.5 347zM379 510q0 -119 62.5 -201t158.5 -82 q99 0 152 66.5t53 189.5q0 107 -49.5 168.5t-149.5 61.5q-94 0 -160.5 -61t-66.5 -142z" />
|
||||
<glyph unicode="7" d="M55 1200v260h1049v-194l-553 -1266h-324l549 1200h-721z" />
|
||||
<glyph unicode="8" d="M72 371q0 125 66.5 222t213.5 171q-125 79 -180 169t-55 197q0 157 130 254t339 97q210 0 338.5 -95.5t128.5 -257.5q0 -112 -62 -199.5t-200 -156.5q164 -88 235.5 -183.5t71.5 -209.5q0 -180 -141 -289.5t-371 -109.5q-240 0 -377 102t-137 289zM358 389q0 -86 60 -134 t164 -48q115 0 172 49.5t57 130.5q0 67 -56.5 125.5t-183.5 124.5q-213 -98 -213 -248zM408 1106q0 -60 38.5 -107.5t139.5 -97.5q98 46 137 94t39 111q0 69 -50 109t-128 40q-79 0 -127.5 -40.5t-48.5 -108.5z" />
|
||||
<glyph unicode="9" d="M66 971q0 235 133.5 371.5t363.5 136.5q162 0 283.5 -76t186.5 -220.5t65 -344.5q0 -432 -182 -645t-551 -213q-130 0 -197 14v248q84 -21 176 -21q155 0 255 45.5t153 143t61 268.5h-12q-58 -94 -134 -132t-190 -38q-191 0 -301 122.5t-110 340.5zM365 975 q0 -106 49 -168t149 -62q94 0 161 61.5t67 141.5q0 119 -62.5 201t-159.5 82q-96 0 -150 -66t-54 -190z" />
|
||||
<glyph unicode=":" horiz-adv-x="584" d="M117 143q0 84 45 127t131 43q83 0 128.5 -44t45.5 -126q0 -79 -46 -124.5t-128 -45.5q-84 0 -130 44.5t-46 125.5zM117 969q0 84 45 127t131 43q83 0 128.5 -44t45.5 -126q0 -81 -46.5 -125.5t-127.5 -44.5q-84 0 -130 44t-46 126z" />
|
||||
<glyph unicode=";" horiz-adv-x="594" d="M63 -264q65 266 101 502h280l15 -23q-52 -202 -176 -479h-220zM117 969q0 84 45 127t131 43q83 0 128.5 -44t45.5 -126q0 -81 -46.5 -125.5t-127.5 -44.5q-84 0 -130 44t-46 126z" />
|
||||
<glyph unicode="<" d="M88 641v143l993 496v-240l-684 -317l684 -281v-239z" />
|
||||
<glyph unicode="=" d="M88 418v219h993v-219h-993zM88 805v219h993v-219h-993z" />
|
||||
<glyph unicode=">" d="M88 203v239l684 281l-684 317v240l993 -496v-143z" />
|
||||
<glyph unicode="?" horiz-adv-x="977" d="M6 1358q223 125 473 125q206 0 327.5 -99t121.5 -264q0 -110 -50 -190t-190 -180q-96 -71 -121.5 -108t-25.5 -97v-60h-265v74q0 96 41 167t150 151q105 75 138.5 122t33.5 105q0 65 -48 99t-134 34q-150 0 -342 -98zM244 143q0 84 45 127t131 43q83 0 128.5 -44 t45.5 -126q0 -79 -46 -124.5t-128 -45.5q-84 0 -130 44.5t-46 125.5z" />
|
||||
<glyph unicode="@" horiz-adv-x="1837" d="M102 602q0 247 108.5 448.5t309 316t461.5 114.5q220 0 393 -90t267 -256t94 -383q0 -144 -46 -263.5t-130 -187.5t-195 -68q-74 0 -131 35.5t-82 93.5h-16q-108 -129 -275 -129q-177 0 -279 106.5t-102 291.5q0 211 134 340t350 129q86 0 189.5 -16.5t170.5 -39.5 l-23 -489q0 -139 76 -139q64 0 102 93.5t38 244.5q0 161 -67 284.5t-188.5 188.5t-277.5 65q-202 0 -351 -83t-228.5 -239.5t-79.5 -361.5q0 -276 147.5 -423.5t427.5 -147.5q106 0 233 23.5t250 68.5v-192q-214 -91 -475 -91q-380 0 -592.5 200t-212.5 556zM711 627 q0 -211 172 -211q90 0 137 63.5t57 206.5l13 221q-51 11 -115 11q-125 0 -194.5 -78t-69.5 -213z" />
|
||||
<glyph unicode="A" horiz-adv-x="1413" d="M0 0l516 1468h379l518 -1468h-334l-106 348h-533l-106 -348h-334zM518 608h381q-147 473 -165.5 535t-26.5 98q-33 -128 -189 -633z" />
|
||||
<glyph unicode="B" horiz-adv-x="1376" d="M184 0v1462h455q311 0 451.5 -88.5t140.5 -281.5q0 -131 -61.5 -215t-163.5 -101v-10q139 -31 200.5 -116t61.5 -226q0 -200 -144.5 -312t-392.5 -112h-547zM494 256h202q128 0 189 49t61 150q0 182 -260 182h-192v-381zM494 883h180q126 0 182.5 39t56.5 129 q0 84 -61.5 120.5t-194.5 36.5h-163v-325z" />
|
||||
<glyph unicode="C" horiz-adv-x="1305" d="M119 729q0 228 83 399.5t238.5 263t364.5 91.5q213 0 428 -103l-100 -252q-82 39 -165 68t-163 29q-175 0 -271 -131.5t-96 -366.5q0 -489 367 -489q154 0 373 77v-260q-180 -75 -402 -75q-319 0 -488 193.5t-169 555.5z" />
|
||||
<glyph unicode="D" horiz-adv-x="1516" d="M184 0v1462h459q358 0 556 -189t198 -528q0 -361 -205.5 -553t-593.5 -192h-414zM494 256h133q448 0 448 481q0 471 -416 471h-165v-952z" />
|
||||
<glyph unicode="E" horiz-adv-x="1147" d="M184 0v1462h842v-254h-532v-321h495v-254h-495v-377h532v-256h-842z" />
|
||||
<glyph unicode="F" horiz-adv-x="1124" d="M184 0v1462h838v-254h-533v-377h496v-253h-496v-578h-305z" />
|
||||
<glyph unicode="G" horiz-adv-x="1483" d="M119 733q0 354 202.5 552t561.5 198q225 0 434 -90l-103 -248q-160 80 -333 80q-201 0 -322 -135t-121 -363q0 -238 97.5 -363.5t283.5 -125.5q97 0 197 20v305h-277v258h580v-758q-141 -46 -265.5 -64.5t-254.5 -18.5q-331 0 -505.5 194.5t-174.5 558.5z" />
|
||||
<glyph unicode="H" horiz-adv-x="1567" d="M184 0v1462h310v-573h579v573h309v-1462h-309v631h-579v-631h-310z" />
|
||||
<glyph unicode="I" horiz-adv-x="678" d="M184 0v1462h310v-1462h-310z" />
|
||||
<glyph unicode="J" horiz-adv-x="678" d="M-152 -150q80 -20 146 -20q102 0 146 63.5t44 198.5v1370h310v-1368q0 -256 -117 -390t-346 -134q-105 0 -183 22v258z" />
|
||||
<glyph unicode="K" horiz-adv-x="1360" d="M184 0v1462h310v-669l122 172l396 497h344l-510 -647l514 -815h-352l-383 616l-131 -94v-522h-310z" />
|
||||
<glyph unicode="L" horiz-adv-x="1157" d="M184 0v1462h310v-1206h593v-256h-903z" />
|
||||
<glyph unicode="M" horiz-adv-x="1931" d="M184 0v1462h422l346 -1118h6l367 1118h422v-1462h-289v692q0 49 1.5 113t13.5 340h-9l-377 -1145h-284l-352 1147h-9q19 -350 19 -467v-680h-277z" />
|
||||
<glyph unicode="N" horiz-adv-x="1665" d="M184 0v1462h391l635 -1095h7q-15 285 -15 403v692h279v-1462h-394l-636 1106h-9q19 -293 19 -418v-688h-277z" />
|
||||
<glyph unicode="O" horiz-adv-x="1630" d="M119 735q0 365 180.5 557.5t517.5 192.5t515.5 -194t178.5 -558q0 -363 -180 -558t-516 -195t-516 195t-180 560zM444 733q0 -245 93 -369t278 -124q371 0 371 493q0 494 -369 494q-185 0 -279 -124.5t-94 -369.5z" />
|
||||
<glyph unicode="P" horiz-adv-x="1286" d="M184 0v1462h467q266 0 404.5 -114.5t138.5 -341.5q0 -236 -147.5 -361t-419.5 -125h-133v-520h-310zM494 774h102q143 0 214 56.5t71 164.5q0 109 -59.5 161t-186.5 52h-141v-434z" />
|
||||
<glyph unicode="Q" horiz-adv-x="1630" d="M119 735q0 365 180.5 557.5t517.5 192.5t515.5 -194t178.5 -558q0 -258 -91.5 -432.5t-268.5 -255.5l352 -393h-397l-268 328h-23q-336 0 -516 195t-180 560zM444 733q0 -245 93 -369t278 -124q371 0 371 493q0 494 -369 494q-185 0 -279 -124.5t-94 -369.5z" />
|
||||
<glyph unicode="R" horiz-adv-x="1352" d="M184 0v1462h426q298 0 441 -108.5t143 -329.5q0 -129 -71 -229.5t-201 -157.5q330 -493 430 -637h-344l-349 561h-165v-561h-310zM494 813h100q147 0 217 49t70 154q0 104 -71.5 148t-221.5 44h-94v-395z" />
|
||||
<glyph unicode="S" horiz-adv-x="1128" d="M94 68v288q148 -66 250.5 -93t187.5 -27q102 0 156.5 39t54.5 116q0 43 -24 76.5t-70.5 64.5t-189.5 99q-134 63 -201 121t-107 135t-40 180q0 194 131.5 305t363.5 111q114 0 217.5 -27t216.5 -76l-100 -241q-117 48 -193.5 67t-150.5 19q-88 0 -135 -41t-47 -107 q0 -41 19 -71.5t60.5 -59t196.5 -102.5q205 -98 281 -196.5t76 -241.5q0 -198 -142.5 -312t-396.5 -114q-234 0 -414 88z" />
|
||||
<glyph unicode="T" horiz-adv-x="1186" d="M41 1204v258h1104v-258h-397v-1204h-310v1204h-397z" />
|
||||
<glyph unicode="U" horiz-adv-x="1548" d="M174 520v942h309v-895q0 -169 68 -248t225 -79q152 0 220.5 79.5t68.5 249.5v893h309v-946q0 -162 -72.5 -284t-209.5 -187t-324 -65q-282 0 -438 144.5t-156 395.5z" />
|
||||
<glyph unicode="V" horiz-adv-x="1331" d="M0 1462h313l275 -870q23 -77 47.5 -179.5t30.5 -142.5q11 92 75 322l277 870h313l-497 -1462h-338z" />
|
||||
<glyph unicode="W" horiz-adv-x="1980" d="M0 1462h305l187 -798q49 -221 71 -383q6 57 27.5 176.5t40.5 185.5l213 819h293l213 -819q14 -55 35 -168t32 -194q10 78 32 194.5t40 188.5l186 798h305l-372 -1462h-353l-198 768q-11 41 -37.5 169.5t-30.5 172.5q-6 -54 -30 -173.5t-37 -170.5l-197 -766h-352z" />
|
||||
<glyph unicode="X" horiz-adv-x="1366" d="M0 0l485 754l-454 708h342l315 -526l309 526h334l-459 -725l494 -737h-354l-340 553l-340 -553h-332z" />
|
||||
<glyph unicode="Y" horiz-adv-x="1278" d="M0 1462h336l303 -602l305 602h334l-485 -893v-569h-308v559z" />
|
||||
<glyph unicode="Z" horiz-adv-x="1186" d="M49 0v201l701 1005h-682v256h1050v-200l-700 -1006h719v-256h-1088z" />
|
||||
<glyph unicode="[" horiz-adv-x="678" d="M143 -324v1786h484v-211h-224v-1364h224v-211h-484z" />
|
||||
<glyph unicode="\" horiz-adv-x="846" d="M12 1462h277l545 -1462h-277z" />
|
||||
<glyph unicode="]" horiz-adv-x="678" d="M51 -113h223v1364h-223v211h484v-1786h-484v211z" />
|
||||
<glyph unicode="^" horiz-adv-x="1090" d="M8 520l438 950h144l495 -950h-239l-322 643l-280 -643h-236z" />
|
||||
<glyph unicode="_" horiz-adv-x="842" d="M-4 -184h850v-140h-850v140z" />
|
||||
<glyph unicode="`" horiz-adv-x="1243" d="M332 1548v21h342q63 -101 235 -301v-27h-202q-63 44 -185 142.5t-190 164.5z" />
|
||||
<glyph unicode="a" horiz-adv-x="1237" d="M86 334q0 178 124.5 262.5t375.5 93.5l194 6v49q0 170 -174 170q-134 0 -315 -81l-101 206q193 101 428 101q225 0 345 -98t120 -298v-745h-213l-59 152h-8q-77 -97 -158.5 -134.5t-212.5 -37.5q-161 0 -253.5 92t-92.5 262zM399 332q0 -129 148 -129q106 0 169.5 61 t63.5 162v92l-118 -4q-133 -4 -198 -48t-65 -134z" />
|
||||
<glyph unicode="b" horiz-adv-x="1296" d="M160 0v1556h305v-362q0 -69 -12 -221h12q107 166 317 166q198 0 310 -154.5t112 -423.5q0 -277 -115.5 -429t-314.5 -152q-197 0 -309 143h-21l-51 -123h-233zM465 563q0 -180 53.5 -258t169.5 -78q94 0 149.5 86.5t55.5 251.5t-56 247.5t-153 82.5q-113 0 -165 -69.5 t-54 -229.5v-33z" />
|
||||
<glyph unicode="c" horiz-adv-x="1053" d="M92 553q0 285 142 435.5t407 150.5q194 0 348 -76l-90 -236q-72 29 -134 47.5t-124 18.5q-238 0 -238 -338q0 -328 238 -328q88 0 163 23.5t150 73.5v-261q-74 -47 -149.5 -65t-190.5 -18q-522 0 -522 573z" />
|
||||
<glyph unicode="d" horiz-adv-x="1296" d="M92 557q0 275 114.5 428.5t315.5 153.5q211 0 322 -164h10q-23 125 -23 223v358h306v-1556h-234l-59 145h-13q-104 -165 -317 -165q-197 0 -309.5 153t-112.5 424zM401 553q0 -165 57 -247.5t163 -82.5q117 0 171.5 68t59.5 231v33q0 180 -55.5 258t-180.5 78 q-102 0 -158.5 -86.5t-56.5 -251.5z" />
|
||||
<glyph unicode="e" horiz-adv-x="1210" d="M92 551q0 281 140.5 434.5t388.5 153.5q237 0 369 -135t132 -373v-148h-721q5 -130 77 -203t202 -73q101 0 191 21t188 67v-236q-80 -40 -171 -59.5t-222 -19.5q-270 0 -422 149t-152 422zM408 686h428q-2 113 -59 174.5t-154 61.5t-152 -61.5t-63 -174.5z" />
|
||||
<glyph unicode="f" horiz-adv-x="793" d="M41 889v147l168 82v82q0 191 94 279t301 88q158 0 281 -47l-78 -224q-92 29 -170 29q-65 0 -94 -38.5t-29 -98.5v-70h264v-229h-264v-889h-305v889h-168z" />
|
||||
<glyph unicode="g" horiz-adv-x="1157" d="M6 -182q0 101 63 169t185 97q-47 20 -82 65.5t-35 96.5q0 64 37 106.5t107 83.5q-88 38 -139.5 122t-51.5 198q0 183 119 283t340 100q47 0 111.5 -8.5t82.5 -12.5h390v-155l-175 -45q48 -75 48 -168q0 -180 -125.5 -280.5t-348.5 -100.5l-55 3l-45 5q-47 -36 -47 -80 q0 -66 168 -66h190q184 0 280.5 -79t96.5 -232q0 -196 -163.5 -304t-469.5 -108q-234 0 -357.5 81.5t-123.5 228.5zM270 -158q0 -63 60.5 -99t169.5 -36q164 0 257 45t93 123q0 63 -55 87t-170 24h-158q-84 0 -140.5 -39.5t-56.5 -104.5zM381 752q0 -91 41.5 -144t126.5 -53 q86 0 126 53t40 144q0 202 -166 202q-168 0 -168 -202z" />
|
||||
<glyph unicode="h" horiz-adv-x="1346" d="M160 0v1556h305v-317q0 -37 -7 -174l-7 -90h16q102 164 324 164q197 0 299 -106t102 -304v-729h-305v653q0 242 -180 242q-128 0 -185 -87t-57 -282v-526h-305z" />
|
||||
<glyph unicode="i" horiz-adv-x="625" d="M147 1407q0 149 166 149t166 -149q0 -71 -41.5 -110.5t-124.5 -39.5q-166 0 -166 150zM160 0v1118h305v-1118h-305z" />
|
||||
<glyph unicode="j" horiz-adv-x="625" d="M-131 -227q70 -19 143 -19q77 0 112.5 43t35.5 127v1194h305v-1239q0 -178 -103 -274.5t-292 -96.5q-117 0 -201 25v240zM147 1407q0 149 166 149t166 -149q0 -71 -41.5 -110.5t-124.5 -39.5q-166 0 -166 150z" />
|
||||
<glyph unicode="k" horiz-adv-x="1270" d="M160 0v1556h305v-694l-16 -254h4l133 170l313 340h344l-444 -485l471 -633h-352l-322 453l-131 -105v-348h-305z" />
|
||||
<glyph unicode="l" horiz-adv-x="625" d="M160 0v1556h305v-1556h-305z" />
|
||||
<glyph unicode="m" horiz-adv-x="2011" d="M160 0v1118h233l41 -143h17q45 77 130 120.5t195 43.5q251 0 340 -164h27q45 78 132.5 121t197.5 43q190 0 287.5 -97.5t97.5 -312.5v-729h-306v653q0 121 -40.5 181.5t-127.5 60.5q-112 0 -167.5 -80t-55.5 -254v-561h-305v653q0 121 -40.5 181.5t-127.5 60.5 q-117 0 -170 -86t-53 -283v-526h-305z" />
|
||||
<glyph unicode="n" horiz-adv-x="1346" d="M160 0v1118h233l41 -143h17q51 81 140.5 122.5t203.5 41.5q195 0 296 -105.5t101 -304.5v-729h-305v653q0 121 -43 181.5t-137 60.5q-128 0 -185 -85.5t-57 -283.5v-526h-305z" />
|
||||
<glyph unicode="o" horiz-adv-x="1268" d="M92 561q0 274 143 426t402 152q161 0 284 -70t189 -201t66 -307q0 -273 -144 -427t-401 -154q-161 0 -284 70.5t-189 202.5t-66 308zM403 561q0 -166 54.5 -251t177.5 -85q122 0 175.5 84.5t53.5 251.5q0 166 -54 249t-177 83q-122 0 -176 -82.5t-54 -249.5z" />
|
||||
<glyph unicode="p" horiz-adv-x="1296" d="M160 -492v1610h248l43 -145h14q107 166 317 166q198 0 310 -153t112 -425q0 -179 -52.5 -311t-149.5 -201t-228 -69q-197 0 -309 143h-16q16 -140 16 -162v-453h-305zM465 563q0 -180 53.5 -258t169.5 -78q205 0 205 338q0 165 -50.5 247.5t-158.5 82.5 q-113 0 -165 -69.5t-54 -229.5v-33z" />
|
||||
<glyph unicode="q" horiz-adv-x="1296" d="M92 557q0 274 114.5 428t313.5 154q106 0 185 -40t139 -124h8l27 143h258v-1610h-306v469q0 61 13 168h-13q-49 -81 -130 -123t-187 -42q-198 0 -310 152.5t-112 424.5zM403 553q0 -168 53.5 -251t166.5 -83q116 0 170 66.5t59 232.5v37q0 180 -55.5 258t-178.5 78 q-215 0 -215 -338z" />
|
||||
<glyph unicode="r" horiz-adv-x="930" d="M160 0v1118h231l45 -188h15q52 94 140.5 151.5t192.5 57.5q62 0 103 -9l-23 -286q-37 10 -90 10q-146 0 -227.5 -75t-81.5 -210v-569h-305z" />
|
||||
<glyph unicode="s" horiz-adv-x="1018" d="M92 827q0 149 115.5 230.5t327.5 81.5q202 0 393 -88l-92 -220q-84 36 -157 59t-149 23q-135 0 -135 -73q0 -41 43.5 -71t190.5 -89q131 -53 192 -99t90 -106t29 -143q0 -172 -119.5 -262t-357.5 -90q-122 0 -208 16.5t-161 48.5v252q85 -40 191.5 -67t187.5 -27 q166 0 166 96q0 36 -22 58.5t-76 51t-144 66.5q-129 54 -189.5 100t-88 105.5t-27.5 146.5z" />
|
||||
<glyph unicode="t" horiz-adv-x="889" d="M47 889v129l168 102l88 236h195v-238h313v-229h-313v-539q0 -65 36.5 -96t96.5 -31q80 0 192 35v-227q-114 -51 -280 -51q-183 0 -266.5 92.5t-83.5 277.5v539h-146z" />
|
||||
<glyph unicode="u" horiz-adv-x="1346" d="M154 389v729h305v-653q0 -121 43 -181.5t137 -60.5q128 0 185 85.5t57 283.5v526h305v-1118h-234l-41 143h-16q-49 -78 -139 -120.5t-205 -42.5q-197 0 -297 105.5t-100 303.5z" />
|
||||
<glyph unicode="v" horiz-adv-x="1165" d="M0 1118h319l216 -637q36 -121 45 -229h6q5 96 45 229l215 637h319l-426 -1118h-313z" />
|
||||
<glyph unicode="w" horiz-adv-x="1753" d="M20 1118h304l129 -495q31 -133 63 -367h6q4 76 35 241l16 85l138 536h336l131 -536q4 -22 12.5 -65t16.5 -91.5t14.5 -95t7.5 -74.5h6q9 72 32 197.5t33 169.5l134 495h299l-322 -1118h-332l-86 391l-116 494h-7l-204 -885h-328z" />
|
||||
<glyph unicode="x" horiz-adv-x="1184" d="M10 0l379 571l-360 547h346l217 -356l219 356h346l-364 -547l381 -571h-347l-235 383l-236 -383h-346z" />
|
||||
<glyph unicode="y" horiz-adv-x="1165" d="M0 1118h334l211 -629q27 -82 37 -194h6q11 103 43 194l207 629h327l-473 -1261q-65 -175 -185.5 -262t-281.5 -87q-79 0 -155 17v242q55 -13 120 -13q81 0 141.5 49.5t94.5 149.5l18 55z" />
|
||||
<glyph unicode="z" horiz-adv-x="999" d="M55 0v180l518 705h-487v233h834v-198l-504 -687h522v-233h-883z" />
|
||||
<glyph unicode="{" horiz-adv-x="807" d="M31 449v239q126 0 191 44t65 126v8v318q0 153 97 215.5t341 62.5v-225q-99 -3 -136.5 -38t-37.5 -103v-299q-6 -188 -234 -222v-12q234 -35 234 -212v-9v-299q0 -68 37 -103t137 -38v-226q-244 0 -341 62.5t-97 216.5v315q0 87 -65.5 133t-190.5 46z" />
|
||||
<glyph unicode="|" horiz-adv-x="1128" d="M455 -465v2015h219v-2015h-219z" />
|
||||
<glyph unicode="}" horiz-adv-x="807" d="M82 -98q99 2 136.5 36t37.5 105v299v11q0 86 59 139.5t174 70.5v12q-227 34 -233 222v299q0 70 -37 104t-137 37v225q167 0 262 -26.5t135.5 -84t40.5 -167.5v-318v-10q0 -84 61.5 -126t194.5 -42v-239q-125 0 -190.5 -41t-65.5 -138v-315q0 -112 -41 -169t-135.5 -83.5 t-261.5 -26.5v226z" />
|
||||
<glyph unicode="~" d="M88 551v231q103 109 256 109q73 0 137.5 -16t139.5 -48q129 -55 227 -55q53 0 116 32t117 89v-231q-101 -109 -256 -109q-66 0 -126 13t-150 50q-131 56 -227 56q-55 0 -117.5 -33.5t-116.5 -87.5z" />
|
||||
<glyph unicode="¢" d="M143 741q0 261 104.5 403t315.5 173v166h178v-158q166 -9 299 -74l-90 -235q-72 29 -134 47t-124 18q-121 0 -179 -83.5t-58 -254.5q0 -327 237 -327q82 0 148 15.5t166 60.5v-254q-127 -61 -265 -70v-188h-178v196q-420 59 -420 565z" />
|
||||
<glyph unicode="£" d="M82 0v248q103 44 141.5 101t38.5 157v145h-178v219h178v195q0 201 114.5 309.5t323.5 108.5q195 0 390 -82l-93 -230q-157 64 -272 64q-78 0 -120 -44.5t-42 -127.5v-193h375v-219h-375v-143q0 -170 -151 -248h718v-260h-1048z" />
|
||||
<glyph unicode="¥" d="M6 1462h316l262 -602l264 602h313l-383 -747h195v-178h-246v-138h246v-178h-246v-221h-287v221h-247v178h247v138h-247v178h190z" />
|
||||
<glyph unicode="©" horiz-adv-x="1704" d="M100 731q0 200 100 375t275 276t377 101q200 0 375 -100t276 -275t101 -377q0 -197 -97 -370t-272 -277t-383 -104q-207 0 -382 103.5t-272.5 276.5t-97.5 371zM242 731q0 -164 82 -305.5t224 -223t304 -81.5q164 0 305.5 82t223 224t81.5 304q0 164 -82 305.5t-224 223 t-304 81.5q-164 0 -305.5 -82t-223 -224t-81.5 -304zM461 733q0 220 110.5 342.5t309.5 122.5q149 0 305 -78l-74 -168q-113 58 -217 58q-97 0 -150 -74t-53 -205q0 -280 203 -280q57 0 123 15t123 44v-191q-120 -57 -252 -57q-204 0 -316 125t-112 346z" />
|
||||
<glyph unicode="­" horiz-adv-x="659" d="M61 424v250h537v-250h-537z" />
|
||||
<glyph unicode="®" horiz-adv-x="1704" d="M100 731q0 200 100 375t275 276t377 101q200 0 375 -100t276 -275t101 -377q0 -197 -97 -370t-272 -277t-383 -104q-207 0 -382 103.5t-272.5 276.5t-97.5 371zM242 731q0 -164 82 -305.5t224 -223t304 -81.5q164 0 305.5 82t223 224t81.5 304q0 164 -82 305.5t-224 223 t-304 81.5q-164 0 -305.5 -82t-223 -224t-81.5 -304zM543 272v916h264q181 0 265.5 -70t84.5 -213q0 -170 -143 -233l237 -400h-254l-178 338h-47v-338h-229zM772 778h31q66 0 94.5 28.5t28.5 94.5q0 65 -28 92t-97 27h-29v-242z" />
|
||||
<glyph unicode="´" horiz-adv-x="1243" d="M332 1241v27q172 200 235 301h342v-21q-52 -52 -177.5 -154.5t-196.5 -152.5h-203z" />
|
||||
<glyph unicode=" " horiz-adv-x="784" />
|
||||
<glyph unicode=" " horiz-adv-x="1569" />
|
||||
<glyph unicode=" " horiz-adv-x="784" />
|
||||
<glyph unicode=" " horiz-adv-x="1569" />
|
||||
<glyph unicode=" " horiz-adv-x="523" />
|
||||
<glyph unicode=" " horiz-adv-x="392" />
|
||||
<glyph unicode=" " horiz-adv-x="261" />
|
||||
<glyph unicode=" " horiz-adv-x="261" />
|
||||
<glyph unicode=" " horiz-adv-x="196" />
|
||||
<glyph unicode=" " horiz-adv-x="313" />
|
||||
<glyph unicode=" " horiz-adv-x="87" />
|
||||
<glyph unicode="‐" horiz-adv-x="659" d="M61 424v250h537v-250h-537z" />
|
||||
<glyph unicode="‑" horiz-adv-x="659" d="M61 424v250h537v-250h-537z" />
|
||||
<glyph unicode="‒" horiz-adv-x="659" d="M61 424v250h537v-250h-537z" />
|
||||
<glyph unicode="–" horiz-adv-x="1024" d="M82 436v230h860v-230h-860z" />
|
||||
<glyph unicode="—" horiz-adv-x="2048" d="M82 436v230h1884v-230h-1884z" />
|
||||
<glyph unicode="‘" horiz-adv-x="444" d="M25 983q22 91 72.5 228.5t103.5 250.5h219q-66 -267 -101 -501h-280z" />
|
||||
<glyph unicode="’" horiz-adv-x="444" d="M25 961q69 296 100 501h281l14 -22q-50 -197 -176 -479h-219z" />
|
||||
<glyph unicode="“" horiz-adv-x="911" d="M25 983q22 91 72.5 228.5t103.5 250.5h219q-66 -267 -101 -501h-280zM492 983q22 91 72.5 228.5t103.5 250.5h219q-66 -267 -101 -501h-280z" />
|
||||
<glyph unicode="”" horiz-adv-x="911" d="M25 961q69 296 100 501h281l14 -22q-50 -197 -176 -479h-219zM492 961q69 296 100 501h280l15 -22q-50 -197 -176 -479h-219z" />
|
||||
<glyph unicode="•" horiz-adv-x="770" d="M98 748q0 154 74 235.5t213 81.5q137 0 212 -82t75 -235q0 -152 -75.5 -235t-211.5 -83q-138 0 -212.5 83t-74.5 235z" />
|
||||
<glyph unicode="…" horiz-adv-x="1751" d="M117 143q0 84 45 127t131 43q83 0 128.5 -44t45.5 -126q0 -79 -46 -124.5t-128 -45.5q-84 0 -130 44.5t-46 125.5zM700 143q0 84 45 127t132 43q83 0 128.5 -44t45.5 -126q0 -79 -46 -124.5t-128 -45.5q-85 0 -131 44.5t-46 125.5zM1284 143q0 84 45 127t131 43 q83 0 128.5 -44t45.5 -126q0 -79 -46 -124.5t-128 -45.5q-84 0 -130 44.5t-46 125.5z" />
|
||||
<glyph unicode=" " horiz-adv-x="313" />
|
||||
<glyph unicode=" " horiz-adv-x="392" />
|
||||
<glyph unicode="€" d="M66 481v178h118q-4 23 -4 62l2 53h-116v176h133q37 242 199 382.5t405 140.5q188 0 352 -82l-98 -232q-69 31 -129 48.5t-125 17.5q-122 0 -201 -70.5t-102 -204.5h403v-176h-418l-2 -35v-47l2 -33h355v-178h-338q51 -243 321 -243q143 0 275 57v-256q-116 -59 -293 -59 q-245 0 -403 133t-199 368h-137z" />
|
||||
<glyph unicode="™" horiz-adv-x="1534" d="M16 1313v149h564v-149h-199v-572h-168v572h-197zM625 741v721h247l160 -510l170 510h240v-721h-168v408l4 121h-6l-174 -529h-142l-165 529h-7l4 -111v-418h-163z" />
|
||||
<glyph unicode="" horiz-adv-x="1120" d="M0 1120h1120v-1120h-1120v1120z" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 25 KiB |
BIN
couchpotato/static/fonts/OpenSans-Bold-webfont.ttf
Executable file
BIN
couchpotato/static/fonts/OpenSans-Bold-webfont.woff
Executable file
BIN
couchpotato/static/fonts/OpenSans-BoldItalic-webfont.eot
Executable file
146
couchpotato/static/fonts/OpenSans-BoldItalic-webfont.svg
Executable file
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
This is a custom SVG webfont generated by Font Squirrel.
|
||||
Copyright : Digitized data copyright 20102011 Google Corporation
|
||||
Foundry : Ascender Corporation
|
||||
Foundry URL : httpwwwascendercorpcom
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="OpenSansBoldItalic" horiz-adv-x="1128" >
|
||||
<font-face units-per-em="2048" ascent="1638" descent="-410" />
|
||||
<missing-glyph horiz-adv-x="532" />
|
||||
<glyph unicode=" " horiz-adv-x="532" />
|
||||
<glyph unicode="	" horiz-adv-x="532" />
|
||||
<glyph unicode=" " horiz-adv-x="532" />
|
||||
<glyph unicode="!" horiz-adv-x="586" d="M25 115q0 90 53.5 144t150.5 54q68 0 109 -38t41 -107q0 -87 -55 -141t-144 -54q-73 0 -114 37.5t-41 104.5zM150 485l157 977h340l-256 -977h-241z" />
|
||||
<glyph unicode=""" horiz-adv-x="928" d="M201 934l71 528h277l-152 -528h-196zM604 934l74 528h276l-151 -528h-199z" />
|
||||
<glyph unicode="#" horiz-adv-x="1323" d="M41 408l18 206h277l70 232h-252l18 209h289l119 407h217l-117 -407h199l116 407h215l-116 -407h239l-18 -209h-279l-69 -232h258l-19 -206h-297l-116 -408h-220l117 408h-194l-115 -408h-215l113 408h-238zM553 614h197l69 232h-196z" />
|
||||
<glyph unicode="$" d="M51 168v266q198 -107 404 -117l71 322q-163 61 -241 151t-78 214q0 173 127 279.5t350 121.5l35 151h139l-33 -151q166 -22 295 -90l-106 -232q-132 65 -242 74l-63 -299q131 -51 195 -99.5t97 -113t33 -149.5q0 -184 -125.5 -291.5t-367.5 -124.5l-39 -199h-140l44 201 q-209 12 -355 86zM502 1022q0 -79 80 -111l51 246q-62 -7 -96.5 -41t-34.5 -94zM594 322q63 9 102 45t39 98q0 46 -24.5 75.5t-59.5 43.5z" />
|
||||
<glyph unicode="%" horiz-adv-x="1753" d="M115 885q0 169 55.5 311.5t148.5 214.5t216 72q137 0 211.5 -80t74.5 -238q0 -166 -56 -310t-151 -217t-217 -73q-139 0 -210.5 83.5t-71.5 236.5zM231 0l1088 1462h235l-1083 -1462h-240zM360 868q0 -96 56 -96q65 0 112 131t47 275q0 96 -57 96q-63 0 -110.5 -128.5 t-47.5 -277.5zM973 283q0 177 53 322.5t148 219.5t219 74q137 0 211.5 -78.5t74.5 -230.5q0 -167 -54 -313.5t-148 -220.5t-215 -74q-144 0 -216.5 78.5t-72.5 222.5zM1219 285q0 -97 55 -97q41 0 77 55t59.5 154.5t23.5 196.5q0 96 -58 96q-39 0 -75 -56t-59 -154t-23 -195 z" />
|
||||
<glyph unicode="&" horiz-adv-x="1450" d="M68 358q0 145 78.5 248.5t273.5 200.5q-76 130 -76 258q0 195 117.5 307.5t316.5 112.5q169 0 266 -82.5t97 -224.5q0 -280 -365 -426l195 -263q44 57 80.5 121.5t78.5 173.5h300q-133 -313 -310 -497l205 -287h-350l-72 98q-175 -118 -403 -118q-209 0 -320.5 97.5 t-111.5 280.5zM383 387q0 -65 45.5 -108t116.5 -43q115 0 221 59l-225 328q-88 -51 -123 -104.5t-35 -131.5zM621 1085q0 -46 12 -92t29 -73q113 59 155.5 111t42.5 112q0 57 -30 82.5t-70 25.5q-66 0 -102.5 -46.5t-36.5 -119.5z" />
|
||||
<glyph unicode="'" horiz-adv-x="522" d="M201 934l71 528h277l-152 -528h-196z" />
|
||||
<glyph unicode="(" horiz-adv-x="694" d="M74 281q0 339 122.5 626.5t381.5 554.5h262q-255 -278 -377.5 -573.5t-122.5 -618.5q0 -308 117 -594h-234q-149 266 -149 605z" />
|
||||
<glyph unicode=")" horiz-adv-x="694" d="M-147 -324q499 545 499 1192q0 307 -116 594h233q149 -264 149 -604q0 -342 -124 -630.5t-379 -551.5h-262z" />
|
||||
<glyph unicode="*" horiz-adv-x="1116" d="M172 1141l86 237l338 -174l33 369l256 -51l-113 -353l387 29l-18 -254l-338 43l160 -336l-246 -73l-90 337l-197 -278l-207 164l275 248z" />
|
||||
<glyph unicode="+" d="M109 612v219h366v369h219v-369h367v-219h-367v-364h-219v364h-366z" />
|
||||
<glyph unicode="," horiz-adv-x="569" d="M-102 -264q74 167 194 502h285l8 -23q-118 -255 -262 -479h-225z" />
|
||||
<glyph unicode="-" horiz-adv-x="659" d="M41 424l53 250h524l-53 -250h-524z" />
|
||||
<glyph unicode="." horiz-adv-x="584" d="M25 115q0 90 53.5 144t150.5 54q68 0 109 -38t41 -107q0 -87 -55 -141t-144 -54q-73 0 -114 37.5t-41 104.5z" />
|
||||
<glyph unicode="/" horiz-adv-x="862" d="M-90 0l809 1462h295l-809 -1462h-295z" />
|
||||
<glyph unicode="0" d="M66 467q0 297 84 537t228 360.5t333 120.5q399 0 399 -473q0 -470 -168.5 -751t-472.5 -281q-198 0 -300.5 122t-102.5 365zM369 461q0 -115 27.5 -173.5t97.5 -58.5q81 0 150.5 106t116 301t46.5 386q0 111 -30.5 162t-92.5 51q-80 0 -149.5 -104t-117.5 -302t-48 -368z " />
|
||||
<glyph unicode="1" d="M182 1114l566 348h249l-309 -1462h-305l180 829q35 152 76 287q-9 -8 -61.5 -47t-262.5 -170z" />
|
||||
<glyph unicode="2" d="M-49 0l43 213l477 424q180 159 248.5 254.5t68.5 179.5q0 75 -41 114.5t-110 39.5q-66 0 -135.5 -33.5t-171.5 -118.5l-146 203q132 112 252 159.5t250 47.5q190 0 301 -98t111 -259q0 -107 -41 -201t-122.5 -188t-266.5 -245l-269 -222v-10h568l-54 -260h-962z" />
|
||||
<glyph unicode="3" d="M14 59v267q84 -50 182 -75.5t191 -25.5q158 0 243 63.5t85 176.5q0 172 -258 172h-138l46 221h73q167 0 263 62t96 172q0 67 -43 104t-121 37q-134 0 -287 -100l-127 204q124 81 232.5 113.5t246.5 32.5q190 0 298 -90.5t108 -243.5q0 -156 -94.5 -262t-261.5 -135v-4 q131 -26 198.5 -106.5t67.5 -201.5q0 -133 -74 -238t-212 -163.5t-327 -58.5q-239 0 -387 79z" />
|
||||
<glyph unicode="4" d="M-25 303l48 234l770 925h311l-195 -919h170l-51 -240h-170l-63 -303h-293l63 303h-590zM305 543h311l58 248q12 58 40 164t42 141h-6q-35 -63 -132 -181z" />
|
||||
<glyph unicode="5" d="M27 61v269q174 -99 352 -99q154 0 241 71t87 194q0 94 -57.5 141t-166.5 47q-102 0 -213 -33l-104 78l207 733h755l-55 -262h-489l-88 -293q72 15 127 15q183 0 289 -103t106 -287q0 -167 -71.5 -292t-208.5 -192.5t-330 -67.5q-117 0 -218.5 23t-162.5 58z" />
|
||||
<glyph unicode="6" d="M88 469q0 202 61 395.5t167.5 335t256.5 213.5t357 72q125 0 223 -27l-51 -246q-84 25 -191 25q-194 0 -313.5 -108t-185.5 -345h4q115 166 311 166q157 0 242.5 -97t85.5 -273q0 -169 -71 -313.5t-190.5 -215.5t-277.5 -71q-212 0 -320 127t-108 362zM383 422 q0 -91 40 -143t107 -52q99 0 161.5 94t62.5 236q0 71 -33.5 113.5t-102.5 42.5q-60 0 -114.5 -35.5t-87.5 -95.5t-33 -160z" />
|
||||
<glyph unicode="7" d="M78 0l737 1202h-629l56 260h975l-41 -194l-752 -1268h-346z" />
|
||||
<glyph unicode="8" d="M55 350q0 298 348 426q-165 132 -165 299q0 119 58 212.5t168 145.5t257 52q123 0 215.5 -42t141 -118t48.5 -174q0 -134 -80.5 -233.5t-230.5 -151.5q217 -141 217 -365q0 -122 -63.5 -218.5t-181 -149.5t-273.5 -53q-214 0 -336.5 100t-122.5 270zM352 383 q0 -81 50 -128.5t135 -47.5q93 0 147.5 53.5t54.5 138.5q0 73 -36.5 131.5t-120.5 112.5q-116 -45 -173 -107t-57 -153zM528 1094q0 -132 123 -201q185 72 185 221q0 68 -39.5 107t-102.5 39q-76 0 -121 -46.5t-45 -119.5z" />
|
||||
<glyph unicode="9" d="M86 12v256q111 -41 227 -41q121 0 207.5 49t144 138.5t99.5 257.5h-4q-111 -158 -295 -158q-163 0 -252.5 103.5t-89.5 285.5q0 166 73 305.5t196 208t286 68.5q203 0 308.5 -123t105.5 -361q0 -280 -99 -533t-264 -370.5t-403 -117.5q-128 0 -240 32zM424 928 q0 -87 37.5 -131.5t105.5 -44.5q60 0 111.5 36.5t82 100t30.5 158.5q0 84 -35.5 137t-110.5 53q-65 0 -115.5 -42t-78 -114t-27.5 -153z" />
|
||||
<glyph unicode=":" horiz-adv-x="584" d="M25 115q0 90 53.5 144t150.5 54q68 0 109 -38t41 -107q0 -87 -55 -141t-144 -54q-73 0 -114 37.5t-41 104.5zM207 940q0 92 55.5 145.5t149.5 53.5q68 0 108.5 -38.5t40.5 -107.5q0 -86 -54.5 -140t-144.5 -54q-72 0 -113.5 36.5t-41.5 104.5z" />
|
||||
<glyph unicode=";" horiz-adv-x="584" d="M-102 -264q74 167 194 502h285l8 -23q-118 -255 -262 -479h-225zM207 940q0 92 55.5 145.5t149.5 53.5q68 0 108.5 -38.5t40.5 -107.5q0 -86 -54.5 -140t-144.5 -54q-72 0 -113.5 36.5t-41.5 104.5z" />
|
||||
<glyph unicode="<" d="M109 641v143l952 496v-240l-643 -317l643 -281v-239z" />
|
||||
<glyph unicode="=" d="M109 418v219h952v-219h-952zM109 807v217h952v-217h-952z" />
|
||||
<glyph unicode=">" d="M109 203v239l643 281l-643 317v240l952 -496v-143z" />
|
||||
<glyph unicode="?" horiz-adv-x="940" d="M166 115q0 91 55 144.5t150 53.5q68 0 108.5 -38t40.5 -107q0 -87 -55 -141t-143 -54q-74 0 -115 38t-41 104zM178 1358q230 125 445 125q177 0 280 -87.5t103 -244.5q0 -83 -28.5 -149.5t-82.5 -123t-190 -147.5q-64 -43 -96.5 -73t-52.5 -64.5t-38 -108.5h-258l14 78 q19 103 73.5 177t172.5 155q124 84 157.5 127t33.5 96q0 119 -133 119q-50 0 -106.5 -16t-201.5 -84z" />
|
||||
<glyph unicode="@" horiz-adv-x="1753" d="M92 500q0 279 120.5 497t343 341.5t497.5 123.5q318 0 499 -163.5t181 -458.5q0 -173 -64 -321t-177.5 -231t-254.5 -83q-88 0 -144.5 38.5t-72.5 108.5h-6q-50 -77 -113 -112t-147 -35q-127 0 -198 79.5t-71 229.5q0 147 67.5 276.5t187.5 205t268 75.5q185 0 327 -55 l-106 -420q-11 -44 -19 -76.5t-8 -64.5q0 -68 58 -68q66 0 124 64t92.5 171t34.5 214q0 213 -123.5 325.5t-359.5 112.5q-203 0 -366.5 -94t-255 -266t-91.5 -392q0 -243 134 -380.5t376 -137.5q117 0 219.5 20t221.5 66v-186q-230 -90 -465 -90q-217 0 -378 85.5 t-246 241.5t-85 359zM713 526q0 -65 24.5 -102t69.5 -37q141 0 213 270l57 222q-36 10 -82 10q-82 0 -145.5 -51.5t-100 -137t-36.5 -174.5z" />
|
||||
<glyph unicode="A" horiz-adv-x="1286" d="M-123 0l766 1468h373l147 -1468h-297l-24 348h-473l-172 -348h-320zM494 608h333l-26 350q-10 131 -10 253v36q-44 -120 -109 -254z" />
|
||||
<glyph unicode="B" horiz-adv-x="1270" d="M53 0l309 1462h426q229 0 346 -81.5t117 -243.5q0 -150 -83 -247.5t-236 -129.5v-6q100 -26 159.5 -96.5t59.5 -180.5q0 -229 -153 -353t-423 -124h-522zM412 256h180q117 0 183.5 58t66.5 161q0 162 -183 162h-165zM545 883h149q121 0 181.5 48.5t60.5 139.5 q0 137 -170 137h-152z" />
|
||||
<glyph unicode="C" horiz-adv-x="1253" d="M123 553q0 262 104 482.5t278 335t400 114.5q125 0 222 -22.5t208 -82.5l-118 -250q-106 59 -175 78t-137 19q-132 0 -237.5 -81t-169.5 -238.5t-64 -338.5q0 -167 68.5 -248t218.5 -81q146 0 338 77v-260q-199 -77 -400 -77q-254 0 -395 149.5t-141 423.5z" />
|
||||
<glyph unicode="D" horiz-adv-x="1386" d="M53 0l309 1462h396q270 0 417.5 -143t147.5 -410q0 -280 -98 -486.5t-283.5 -314.5t-437.5 -108h-451zM412 256h106q148 0 258 76t172 223.5t62 337.5q0 154 -72.5 234.5t-208.5 80.5h-115z" />
|
||||
<glyph unicode="E" horiz-adv-x="1110" d="M53 0l309 1462h818l-54 -254h-512l-67 -321h477l-55 -254h-477l-80 -377h512l-54 -256h-817z" />
|
||||
<glyph unicode="F" horiz-adv-x="1087" d="M53 0l309 1462h814l-54 -254h-508l-79 -377h473l-56 -253h-473l-121 -578h-305z" />
|
||||
<glyph unicode="G" horiz-adv-x="1413" d="M123 549q0 268 107 484.5t301 334t448 117.5q218 0 410 -99l-115 -251q-74 40 -148 64t-161 24q-153 0 -273.5 -83t-189 -236.5t-68.5 -330.5q0 -172 72.5 -252.5t222.5 -80.5q76 0 170 24l66 299h-267l56 258h563l-162 -762q-134 -46 -248.5 -62.5t-242.5 -16.5 q-259 0 -400 147t-141 422z" />
|
||||
<glyph unicode="H" horiz-adv-x="1434" d="M53 0l309 1462h306l-121 -573h471l121 573h305l-309 -1462h-306l134 631h-471l-134 -631h-305z" />
|
||||
<glyph unicode="I" horiz-adv-x="659" d="M53 0l312 1462h305l-312 -1462h-305z" />
|
||||
<glyph unicode="J" horiz-adv-x="678" d="M-322 -150q88 -20 164 -20q99 0 160.5 60.5t89.5 191.5l293 1380h305l-303 -1423q-52 -245 -175.5 -357t-346.5 -112q-94 0 -187 27v253z" />
|
||||
<glyph unicode="K" horiz-adv-x="1255" d="M53 0l309 1462h306l-152 -702l158 205l409 497h361l-594 -700l291 -762h-338l-211 592l-125 -70l-109 -522h-305z" />
|
||||
<glyph unicode="L" horiz-adv-x="1061" d="M53 0l309 1462h306l-256 -1206h512l-54 -256h-817z" />
|
||||
<glyph unicode="M" horiz-adv-x="1802" d="M53 0l309 1462h404l68 -1093h4l551 1093h423l-309 -1462h-280l145 692q53 247 105 441h-5l-569 -1133h-281l-61 1133h-4q-11 -88 -38 -231t-187 -902h-275z" />
|
||||
<glyph unicode="N" horiz-adv-x="1546" d="M53 0l309 1462h357l340 -1077h4q12 76 39 217t180 860h274l-309 -1462h-342l-356 1106h-6l-4 -32q-32 -216 -66 -386l-145 -688h-275z" />
|
||||
<glyph unicode="O" horiz-adv-x="1495" d="M123 537q0 265 99 487.5t273 341.5t402 119q255 0 395 -144t140 -403q0 -283 -99 -506.5t-271 -337.5t-396 -114q-256 0 -399.5 147.5t-143.5 409.5zM434 537q0 -147 66.5 -222t187.5 -75t220.5 87t155.5 246t56 357q0 142 -65 219.5t-183 77.5q-121 0 -222 -91.5 t-158.5 -251.5t-57.5 -347z" />
|
||||
<glyph unicode="P" horiz-adv-x="1188" d="M53 0l309 1462h338q242 0 366 -106.5t124 -319.5q0 -241 -169.5 -378.5t-467.5 -137.5h-86l-109 -520h-305zM522 774h56q142 0 223.5 69t81.5 185q0 180 -195 180h-74z" />
|
||||
<glyph unicode="Q" horiz-adv-x="1495" d="M123 537q0 265 99 487.5t273 341.5t402 119q255 0 395 -144t140 -403q0 -316 -122.5 -555.5t-334.5 -337.5l254 -393h-359l-178 328h-26q-256 0 -399.5 147.5t-143.5 409.5zM434 537q0 -147 66.5 -222t187.5 -75t220.5 87t155.5 246t56 357q0 142 -65 219.5t-183 77.5 q-121 0 -222 -91.5t-158.5 -251.5t-57.5 -347z" />
|
||||
<glyph unicode="R" horiz-adv-x="1247" d="M53 0l309 1462h359q237 0 356 -102t119 -299q0 -158 -83 -271.5t-239 -168.5l261 -621h-332l-207 561h-119l-119 -561h-305zM530 813h78q131 0 204 57t73 174q0 82 -47.5 123t-149.5 41h-74z" />
|
||||
<glyph unicode="S" horiz-adv-x="1085" d="M41 70v274q193 -108 358 -108q112 0 175 42.5t63 116.5q0 43 -13.5 75.5t-38.5 60.5t-124 102q-138 99 -194 196t-56 209q0 129 62 230.5t176.5 158t263.5 56.5q217 0 397 -99l-109 -233q-156 74 -288 74q-83 0 -136 -45t-53 -119q0 -61 33 -106.5t148 -120.5 q121 -80 181 -176.5t60 -225.5q0 -209 -148 -330.5t-401 -121.5q-221 0 -356 90z" />
|
||||
<glyph unicode="T" horiz-adv-x="1087" d="M168 1204l55 258h1010l-55 -258h-353l-254 -1204h-305l254 1204h-352z" />
|
||||
<glyph unicode="U" horiz-adv-x="1415" d="M141 401q0 72 15 138l196 923h305l-194 -919q-17 -74 -17 -125q0 -178 189 -178q123 0 195 76.5t104 228.5l194 917h306l-201 -946q-57 -266 -218 -401t-419 -135q-212 0 -333.5 113.5t-121.5 307.5z" />
|
||||
<glyph unicode="V" horiz-adv-x="1208" d="M184 1462h295l51 -880q4 -45 4 -133q-2 -103 -6 -150h7q78 221 110 283l432 880h316l-748 -1462h-334z" />
|
||||
<glyph unicode="W" horiz-adv-x="1831" d="M184 1462h287l6 -798q0 -52 -4 -173t-10 -174h6q22 64 67 180.5t60 145.5l369 819h270l21 -873q0 -146 -9 -272h6q43 129 131 349l330 796h309l-647 -1462h-346l-22 721l-2 139q0 88 4 158h-4q-46 -146 -115 -299l-324 -719h-338z" />
|
||||
<glyph unicode="X" horiz-adv-x="1241" d="M-117 0l576 764l-238 698h320l153 -518l363 518h344l-545 -725l268 -737h-331l-172 543l-396 -543h-342z" />
|
||||
<glyph unicode="Y" horiz-adv-x="1155" d="M186 1462h312l129 -592l374 592h342l-618 -903l-119 -559h-303l119 559z" />
|
||||
<glyph unicode="Z" horiz-adv-x="1098" d="M-61 0l38 201l777 1005h-543l53 256h936l-41 -202l-782 -1004h596l-53 -256h-981z" />
|
||||
<glyph unicode="[" horiz-adv-x="678" d="M-37 -324l381 1786h473l-45 -211h-215l-291 -1364h215l-45 -211h-473z" />
|
||||
<glyph unicode="\" horiz-adv-x="862" d="M221 1462h260l224 -1462h-267z" />
|
||||
<glyph unicode="]" horiz-adv-x="678" d="M-137 -324l45 211h213l291 1364h-215l45 211h473l-381 -1786h-471z" />
|
||||
<glyph unicode="^" horiz-adv-x="1081" d="M20 520l619 950h147l277 -950h-223l-174 633l-402 -633h-244z" />
|
||||
<glyph unicode="_" horiz-adv-x="819" d="M-186 -324l30 140h822l-31 -140h-821z" />
|
||||
<glyph unicode="`" horiz-adv-x="1135" d="M508 1548v21h311q36 -148 115 -303v-25h-184q-71 69 -138.5 153.5t-103.5 153.5z" />
|
||||
<glyph unicode="a" horiz-adv-x="1217" d="M90 385q0 198 72 377.5t189 278t257 98.5q97 0 167.5 -42t109.5 -122h8l57 143h232l-238 -1118h-229l14 145h-4q-134 -165 -319 -165q-147 0 -231.5 106.5t-84.5 298.5zM395 399q0 -88 33.5 -132t95.5 -44q69 0 133 67t103 181.5t39 259.5q0 71 -38.5 117.5t-101.5 46.5 q-68 0 -129.5 -72t-98 -190t-36.5 -234z" />
|
||||
<glyph unicode="b" horiz-adv-x="1219" d="M37 0l330 1556h301l-62 -288q-41 -182 -84 -299h8q78 98 142.5 134t140.5 36q146 0 230.5 -108t84.5 -298t-68 -367.5t-187 -281.5t-263 -104q-194 0 -276 163h-8l-58 -143h-231zM420 399q0 -80 37 -128t102 -48q67 0 128 69t98.5 189.5t37.5 237.5q0 176 -131 176 q-68 0 -130 -65t-102 -180.5t-40 -250.5z" />
|
||||
<glyph unicode="c" horiz-adv-x="989" d="M90 391q0 212 74.5 385.5t209.5 268t308 94.5q182 0 328 -72l-92 -229q-54 23 -106 40t-118 17q-85 0 -153.5 -64t-107 -175.5t-38.5 -239.5q0 -96 45.5 -144.5t126.5 -48.5q76 0 141 23.5t134 58.5v-246q-152 -79 -336 -79q-201 0 -308.5 107.5t-107.5 303.5z" />
|
||||
<glyph unicode="d" horiz-adv-x="1217" d="M90 387q0 196 71.5 374.5t188.5 278t258 99.5q82 0 141.5 -37t112.5 -127h8l2 28q6 110 25 195l76 358h301l-330 -1556h-229l14 145h-4q-71 -87 -148.5 -126t-170.5 -39q-147 0 -231.5 107t-84.5 300zM395 399q0 -176 137 -176q66 0 128.5 68.5t100.5 182.5t38 245 q0 80 -37.5 128t-102.5 48q-68 0 -129.5 -72t-98 -190t-36.5 -234z" />
|
||||
<glyph unicode="e" horiz-adv-x="1141" d="M90 412q0 207 82.5 377.5t223.5 260t319 89.5q177 0 276 -81.5t99 -223.5q0 -187 -167 -288.5t-477 -101.5h-51l-2 -21v-20q0 -91 51.5 -143.5t147.5 -52.5q87 0 158 19t172 67v-227q-172 -86 -390 -86q-210 0 -326 113t-116 319zM428 647h45q155 0 241.5 48.5 t86.5 131.5q0 95 -105 95q-88 0 -166 -80t-102 -195z" />
|
||||
<glyph unicode="f" horiz-adv-x="764" d="M-219 -225q61 -21 115 -21q61 0 107 40t65 130l204 965h-163l30 145l183 84l18 84q41 190 138.5 277.5t273.5 87.5q131 0 235 -49l-80 -224q-69 31 -133 31q-57 0 -92 -40t-47 -105l-12 -62h219l-49 -229h-220l-215 -1010q-77 -371 -403 -371q-104 0 -174 25v242z" />
|
||||
<glyph unicode="g" horiz-adv-x="1108" d="M-115 -209q0 102 68.5 175.5t214.5 121.5q-74 47 -74 133q0 71 44.5 122.5t146.5 98.5q-65 49 -96 112t-31 153q0 199 125.5 315.5t341.5 116.5q83 0 166 -23h395l-35 -166l-174 -41q16 -52 16 -118q0 -195 -121 -308.5t-329 -113.5q-59 0 -99 10q-84 -27 -84 -78 q0 -34 30 -49t89 -23l137 -18q163 -21 237.5 -84.5t74.5 -183.5q0 -211 -156 -323t-446 -112q-208 0 -324.5 75.5t-116.5 207.5zM150 -172q0 -115 194 -115q151 0 228 45t77 127q0 39 -32.5 60t-137.5 35l-114 14q-106 -14 -160.5 -57t-54.5 -109zM442 680q0 -119 103 -119 q75 0 121.5 76.5t46.5 193.5t-99 117q-77 0 -124.5 -76.5t-47.5 -191.5z" />
|
||||
<glyph unicode="h" horiz-adv-x="1237" d="M37 0l330 1556h301q-39 -181 -60 -278t-86 -309h8q62 77 138 123.5t176 46.5q138 0 213.5 -83.5t75.5 -238.5q0 -73 -23 -180l-133 -637h-301l137 653q16 68 16 119q0 123 -108 123q-92 0 -167 -114t-118 -318l-98 -463h-301z" />
|
||||
<glyph unicode="i" horiz-adv-x="608" d="M37 0l237 1118h301l-237 -1118h-301zM322 1380q0 87 47.5 131.5t134.5 44.5q73 0 111 -31t38 -89q0 -80 -44 -129.5t-136 -49.5q-151 0 -151 123z" />
|
||||
<glyph unicode="j" horiz-adv-x="608" d="M-264 -225q61 -21 114 -21q137 0 173 170l253 1194h302l-265 -1239q-77 -371 -403 -371q-104 0 -174 25v242zM324 1380q0 87 47.5 131.5t134.5 44.5q73 0 111 -31t38 -89q0 -80 -44 -129.5t-136 -49.5q-151 0 -151 123z" />
|
||||
<glyph unicode="k" horiz-adv-x="1163" d="M37 0l330 1556h301l-148 -694q-8 -41 -29 -117l-28 -102h4l453 475h344l-498 -504l285 -614h-336l-183 420l-120 -72l-74 -348h-301z" />
|
||||
<glyph unicode="l" horiz-adv-x="608" d="M37 0l330 1556h301l-330 -1556h-301z" />
|
||||
<glyph unicode="m" horiz-adv-x="1853" d="M37 0l237 1118h230l-21 -207h6q146 228 355 228q219 0 262 -228h6q68 110 160.5 169t197.5 59q136 0 207.5 -85t71.5 -237q0 -76 -23 -180l-133 -637h-301l138 653q16 68 16 119q0 123 -98 123q-92 0 -166.5 -112t-118.5 -318l-96 -465h-301l137 653q16 68 16 119 q0 123 -98 123q-92 0 -167 -114t-118 -318l-98 -463h-301z" />
|
||||
<glyph unicode="n" horiz-adv-x="1237" d="M37 0l237 1118h230l-21 -207h6q146 228 355 228q138 0 213.5 -83.5t75.5 -238.5q0 -73 -23 -180l-133 -637h-301l137 653q16 68 16 119q0 123 -108 123q-92 0 -167 -114t-118 -318l-98 -463h-301z" />
|
||||
<glyph unicode="o" horiz-adv-x="1198" d="M90 410q0 213 71.5 379.5t206.5 258t316 91.5q196 0 310 -118t114 -325q0 -211 -70.5 -374t-203.5 -252.5t-316 -89.5q-195 0 -311.5 117.5t-116.5 312.5zM393 410q0 -185 150 -185q75 0 135 61.5t93.5 171t33.5 238.5q0 197 -143 197q-75 0 -134.5 -61t-97 -179 t-37.5 -243z" />
|
||||
<glyph unicode="p" horiz-adv-x="1219" d="M-68 -492l342 1610h230l-17 -170h9q138 191 317 191q146 0 230.5 -107.5t84.5 -300.5q0 -191 -68.5 -367.5t-187.5 -280t-262 -103.5q-83 0 -143 37t-111 126h-8q-12 -159 -43 -295l-72 -340h-301zM420 399q0 -80 37 -128t102 -48q67 0 128 69t98.5 189.5t37.5 237.5 q0 176 -131 176q-68 0 -131.5 -67.5t-102 -180t-38.5 -248.5z" />
|
||||
<glyph unicode="q" horiz-adv-x="1217" d="M90 385q0 198 72 377.5t189 278t257 98.5q86 0 152.5 -37.5t124.5 -126.5h8l57 143h232l-342 -1610h-301q47 218 73 337.5t84 304.5h-8q-72 -94 -143 -132t-154 -38q-88 0 -156 47.5t-106.5 138.5t-38.5 219zM395 399q0 -88 36.5 -132t103.5 -44q64 0 127.5 70t100 181 t36.5 245q0 80 -37.5 128t-102.5 48q-68 0 -129.5 -72t-98 -190t-36.5 -234z" />
|
||||
<glyph unicode="r" horiz-adv-x="862" d="M37 0l237 1118h230l-21 -207h6q147 228 353 228q59 0 96 -11l-66 -290q-45 16 -100 16q-116 0 -203.5 -91.5t-124.5 -262.5l-106 -500h-301z" />
|
||||
<glyph unicode="s" horiz-adv-x="969" d="M23 45v248q157 -90 319 -90q80 0 131 32.5t51 88.5q0 43 -37 77t-131 86q-121 68 -169 135.5t-48 159.5q0 170 110.5 263.5t315.5 93.5q201 0 363 -95l-99 -215q-140 84 -258 84q-57 0 -92 -25.5t-35 -68.5q0 -39 32 -68.5t120 -74.5q123 -63 178 -137t55 -170 q0 -188 -124.5 -288.5t-346.5 -100.5q-107 0 -186.5 15t-148.5 50z" />
|
||||
<glyph unicode="t" horiz-adv-x="840" d="M94 889l29 147l196 84l132 236h194l-49 -238h283l-50 -229h-282l-115 -539q-6 -30 -6 -53q0 -74 88 -74q65 0 162 35v-225q-111 -53 -266 -53q-150 0 -220.5 63t-70.5 195q0 50 12 112l115 539h-152z" />
|
||||
<glyph unicode="u" horiz-adv-x="1237" d="M111 301q0 93 24 213l127 604h301l-137 -653q-16 -68 -16 -119q0 -123 108 -123q92 0 167 114t118 318l98 463h301l-237 -1118h-230l21 207h-6q-145 -227 -355 -227q-138 0 -211 82.5t-73 238.5z" />
|
||||
<glyph unicode="v" horiz-adv-x="1049" d="M102 1118h295l45 -586q7 -133 7 -231h6q55 153 92 223l297 594h323l-604 -1118h-323z" />
|
||||
<glyph unicode="w" horiz-adv-x="1614" d="M125 1118h281l4 -495l-4 -167l-7 -171h4q6 20 14 41.5t51 136.5t46 119l231 536h328v-536q0 -142 -10 -297h6l28 80q73 208 95 258l219 495h307l-530 -1118h-330l-6 520q0 155 10 340h-6q-62 -178 -123 -319l-233 -541h-324z" />
|
||||
<glyph unicode="x" horiz-adv-x="1087" d="M-100 0l479 573l-225 545h321l115 -334l244 334h354l-467 -561l244 -557h-326l-125 342l-264 -342h-350z" />
|
||||
<glyph unicode="y" horiz-adv-x="1063" d="M-141 -233q68 -13 116 -13q84 0 147.5 48t117.5 149l26 49l-164 1118h295l56 -518q14 -122 14 -293h6q20 51 44 119.5t65 153.5l260 538h327l-680 -1278q-177 -332 -483 -332q-90 0 -147 19v240z" />
|
||||
<glyph unicode="z" horiz-adv-x="932" d="M-47 0l35 180l575 705h-397l51 233h750l-43 -200l-566 -685h439l-49 -233h-795z" />
|
||||
<glyph unicode="{" horiz-adv-x="727" d="M-8 459l45 229q122 0 192.5 41.5t92.5 138.5l61 285q38 170 131 239.5t270 69.5h84l-49 -225q-90 -2 -130.5 -34.5t-55.5 -106.5l-66 -297q-45 -207 -276 -236v-8q85 -26 126.5 -82.5t41.5 -134.5q0 -44 -15 -113l-36 -178q-7 -28 -7 -51q0 -54 33.5 -74t91.5 -20v-226 h-53q-167 0 -253.5 63.5t-86.5 184.5q0 57 14 125l39 184q15 69 15 86q0 140 -209 140z" />
|
||||
<glyph unicode="|" d="M455 -465v2015h219v-2015h-219z" />
|
||||
<glyph unicode="}" horiz-adv-x="727" d="M-100 -98q93 3 137 35.5t59 105.5l66 297q25 111 95 166t181 69v9q-168 51 -168 217q0 43 15 112l37 179q6 30 6 51q0 54 -36.5 74t-109.5 20l41 225h33q340 0 340 -248q0 -56 -14 -124l-39 -185q-15 -69 -15 -86q0 -139 209 -139l-45 -229q-122 0 -192.5 -42t-91.5 -139 l-62 -284q-37 -170 -130.5 -240t-270.5 -70h-45v226z" />
|
||||
<glyph unicode="~" d="M109 551v231q101 109 256 109q64 0 117 -14t139 -50q64 -27 111 -41t95 -14q51 0 112 30.5t122 90.5v-231q-103 -109 -256 -109q-59 0 -109 11.5t-147 51.5q-89 38 -127 47t-80 9q-54 0 -116.5 -33t-116.5 -88z" />
|
||||
<glyph unicode="¢" d="M164 584q0 193 62.5 355t178 262.5t267.5 123.5l33 158h188l-35 -158q118 -14 225 -65l-92 -230q-53 23 -105 40t-118 17q-133 0 -216 -143t-83 -336q0 -96 45 -144t127 -48q75 0 140 23.5t134 58.5v-246q-136 -71 -299 -80l-41 -192h-188l49 210q-134 36 -203 136 t-69 258z" />
|
||||
<glyph unicode="£" d="M-12 0l49 246q196 48 244 264l22 104h-192l45 220h192l49 247q41 197 162 300.5t313 103.5q195 0 369 -86l-113 -232q-141 68 -237 68q-75 0 -123 -39.5t-68 -132.5l-47 -229h299l-45 -220h-299l-18 -84q-42 -195 -209 -270h655l-55 -260h-993z" />
|
||||
<glyph unicode="¥" d="M88 221l37 178h252l29 138h-252l39 178h196l-192 747h297l114 -590l371 590h311l-506 -747h203l-39 -178h-252l-28 -138h252l-37 -178h-252l-47 -221h-291l47 221h-252z" />
|
||||
<glyph unicode="©" horiz-adv-x="1704" d="M125 731q0 200 100 375t275 276t377 101q199 0 373.5 -99t276 -275.5t101.5 -377.5q0 -199 -98.5 -373t-272.5 -276t-380 -102q-207 0 -382 103.5t-272.5 276.5t-97.5 371zM266 731q0 -164 81.5 -305t224 -223t305.5 -82q167 0 308 83t221.5 223.5t80.5 303.5 t-80.5 303.5t-222 223.5t-307.5 83q-164 0 -306.5 -82.5t-223.5 -223.5t-81 -304zM485 721q0 225 117.5 351t325.5 126q142 0 284 -72l-75 -174q-114 58 -205 58q-111 0 -163 -73t-52 -214q0 -134 55.5 -203t159.5 -69q43 0 108.5 15.5t124.5 43.5v-191q-131 -57 -262 -57 q-196 0 -307 122.5t-111 336.5z" />
|
||||
<glyph unicode="­" horiz-adv-x="659" d="M41 424l53 250h524l-53 -250h-524z" />
|
||||
<glyph unicode="®" horiz-adv-x="1704" d="M125 731q0 200 100 375t275 276t377 101q199 0 373.5 -99t276 -275.5t101.5 -377.5q0 -199 -98.5 -373t-272.5 -276t-380 -102q-207 0 -382 103.5t-272.5 276.5t-97.5 371zM266 731q0 -164 81.5 -305t224 -223t305.5 -82q167 0 308 83t221.5 223.5t80.5 303.5 t-80.5 303.5t-222 223.5t-307.5 83q-164 0 -306.5 -82.5t-223.5 -223.5t-81 -304zM571 293v874h308q173 0 265.5 -67.5t92.5 -200.5q0 -86 -44 -149.5t-130 -96.5l197 -360h-254l-138 297h-67v-297h-230zM801 758h51q72 0 113 31t41 92q0 59 -35.5 88.5t-116.5 29.5h-53 v-241z" />
|
||||
<glyph unicode="´" horiz-adv-x="1135" d="M483 1241v25q79 88 222 303h335v-17q-46 -56 -154 -152.5t-194 -158.5h-209z" />
|
||||
<glyph unicode=" " horiz-adv-x="786" />
|
||||
<glyph unicode=" " horiz-adv-x="1573" />
|
||||
<glyph unicode=" " horiz-adv-x="786" />
|
||||
<glyph unicode=" " horiz-adv-x="1573" />
|
||||
<glyph unicode=" " horiz-adv-x="524" />
|
||||
<glyph unicode=" " horiz-adv-x="393" />
|
||||
<glyph unicode=" " horiz-adv-x="262" />
|
||||
<glyph unicode=" " horiz-adv-x="262" />
|
||||
<glyph unicode=" " horiz-adv-x="196" />
|
||||
<glyph unicode=" " horiz-adv-x="314" />
|
||||
<glyph unicode=" " horiz-adv-x="87" />
|
||||
<glyph unicode="‐" horiz-adv-x="659" d="M41 424l53 250h524l-53 -250h-524z" />
|
||||
<glyph unicode="‑" horiz-adv-x="659" d="M41 424l53 250h524l-53 -250h-524z" />
|
||||
<glyph unicode="‒" horiz-adv-x="659" d="M41 424l53 250h524l-53 -250h-524z" />
|
||||
<glyph unicode="–" horiz-adv-x="983" d="M41 436l49 230h852l-49 -230h-852z" />
|
||||
<glyph unicode="—" horiz-adv-x="1966" d="M41 436l49 230h1835l-49 -230h-1835z" />
|
||||
<glyph unicode="‘" horiz-adv-x="440" d="M115 983q103 227 262 479h225q-91 -213 -194 -501h-285z" />
|
||||
<glyph unicode="’" horiz-adv-x="440" d="M106 961q89 206 195 501h285l8 -22q-103 -227 -262 -479h-226z" />
|
||||
<glyph unicode="“" horiz-adv-x="887" d="M115 983q103 227 262 479h225q-91 -213 -194 -501h-285zM561 983q103 227 262 479h226q-97 -227 -195 -501h-285z" />
|
||||
<glyph unicode="”" horiz-adv-x="887" d="M106 961q89 206 195 501h285l8 -22q-103 -227 -262 -479h-226zM553 961q23 53 46.5 111t148.5 390h284l8 -22q-103 -227 -262 -479h-225z" />
|
||||
<glyph unicode="•" horiz-adv-x="739" d="M104 686q0 106 42.5 194t120 136.5t182.5 48.5q120 0 182.5 -67t62.5 -191q0 -177 -91.5 -277t-248.5 -100q-117 0 -183.5 67t-66.5 189z" />
|
||||
<glyph unicode="…" horiz-adv-x="1706" d="M25 115q0 90 53.5 144t150.5 54q68 0 109 -38t41 -107q0 -87 -55 -141t-144 -54q-73 0 -114 37.5t-41 104.5zM586 115q0 90 53.5 144t150.5 54q68 0 109 -38t41 -107q0 -87 -55 -141t-144 -54q-73 0 -114 37.5t-41 104.5zM1147 115q0 90 53.5 144t150.5 54q68 0 109 -38 t41 -107q0 -87 -55 -141t-144 -54q-73 0 -114 37.5t-41 104.5z" />
|
||||
<glyph unicode=" " horiz-adv-x="314" />
|
||||
<glyph unicode=" " horiz-adv-x="393" />
|
||||
<glyph unicode="€" d="M41 481l37 178h127q9 67 22 115h-125l39 176h135q87 252 250.5 393.5t374.5 141.5q100 0 179 -23t165 -80l-125 -223q-87 49 -131 63.5t-90 14.5q-97 0 -176 -74.5t-135 -212.5h348l-39 -176h-360q-11 -34 -25 -115h299l-37 -178h-280q0 -120 44.5 -181.5t147.5 -61.5 q133 0 283 63v-258q-126 -63 -330 -63q-446 0 -446 501h-152z" />
|
||||
<glyph unicode="™" horiz-adv-x="1534" d="M106 1313v149h564v-149h-199v-572h-168v572h-197zM715 741v721h248l159 -510l170 510h240v-721h-168v408l4 121h-6l-174 -529h-141l-166 529h-7l5 -111v-418h-164z" />
|
||||
<glyph unicode="" horiz-adv-x="1120" d="M0 1120h1120v-1120h-1120v1120z" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 26 KiB |
BIN
couchpotato/static/fonts/OpenSans-BoldItalic-webfont.ttf
Executable file
BIN
couchpotato/static/fonts/OpenSans-BoldItalic-webfont.woff
Executable file
BIN
couchpotato/static/fonts/OpenSans-Italic-webfont.eot
Executable file
146
couchpotato/static/fonts/OpenSans-Italic-webfont.svg
Executable file
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
This is a custom SVG webfont generated by Font Squirrel.
|
||||
Copyright : Digitized data copyright 20102011 Google Corporation
|
||||
Foundry : Ascender Corporation
|
||||
Foundry URL : httpwwwascendercorpcom
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="OpenSansItalic" horiz-adv-x="1128" >
|
||||
<font-face units-per-em="2048" ascent="1638" descent="-410" />
|
||||
<missing-glyph horiz-adv-x="532" />
|
||||
<glyph unicode=" " horiz-adv-x="532" />
|
||||
<glyph unicode="	" horiz-adv-x="532" />
|
||||
<glyph unicode=" " horiz-adv-x="532" />
|
||||
<glyph unicode="!" horiz-adv-x="530" d="M43 78q0 76 39.5 120t107.5 44q45 0 73 -27.5t28 -81.5q0 -68 -39 -115t-105 -47q-49 0 -76.5 28t-27.5 79zM172 403q49 307 176 1059h207l-274 -1059h-109z" />
|
||||
<glyph unicode=""" horiz-adv-x="791" d="M225 934l72 528h188l-153 -528h-107zM573 934l72 528h189l-154 -528h-107z" />
|
||||
<glyph unicode="#" horiz-adv-x="1323" d="M63 430l13 129h284l101 340h-277l13 127h301l123 436h139l-125 -436h305l127 436h133l-125 -436h264l-12 -127h-291l-98 -340h285l-13 -129h-309l-125 -430h-139l129 430h-303l-127 -430h-133l121 430h-261zM500 559h303l96 340h-303z" />
|
||||
<glyph unicode="$" d="M72 176v154q82 -41 175.5 -63.5t166.5 -22.5l98 452q-139 49 -201.5 123.5t-62.5 188.5q0 159 108 255t299 113l39 176h133l-39 -178q159 -12 283 -76l-63 -135q-121 63 -248 72l-94 -440q149 -55 212.5 -125t63.5 -178q0 -162 -112.5 -263t-309.5 -123l-49 -225h-133 l49 223q-195 14 -315 72zM401 1010q0 -53 34.5 -97.5t107.5 -70.5l84 393q-108 -11 -167 -69t-59 -156zM549 250q107 13 170 75t63 154q0 54 -33 96t-114 74z" />
|
||||
<glyph unicode="%" horiz-adv-x="1624" d="M168 860q0 166 50.5 318.5t136.5 228.5t200 76q116 0 176 -72t60 -205q0 -108 -32 -237.5t-82.5 -217.5t-120.5 -137t-157 -49q-109 0 -170 75t-61 220zM231 0l1086 1462h151l-1085 -1462h-152zM307 864q0 -172 107 -172q52 0 94 39.5t73.5 114t50.5 175t19 171.5 q0 166 -108 166q-66 0 -119 -63t-85 -187.5t-32 -243.5zM909 274q0 166 50.5 318.5t136.5 228.5t200 76q116 0 176 -71.5t60 -204.5q0 -107 -31.5 -236t-82 -217.5t-121 -138t-156.5 -49.5q-110 0 -171 74.5t-61 219.5zM1049 279q0 -173 106 -173q65 0 117 65t86.5 198.5 t34.5 236.5q0 166 -109 166q-67 0 -119.5 -64.5t-84 -188.5t-31.5 -240z" />
|
||||
<glyph unicode="&" horiz-adv-x="1372" d="M66 342q0 148 90 257.5t303 211.5q-103 165 -103 309q0 164 106 264.5t281 100.5q149 0 236.5 -79t87.5 -212q0 -78 -32.5 -137t-87.5 -108t-127.5 -90t-153.5 -83l278 -389q127 110 199 295h168q-101 -236 -283 -412l203 -270h-201l-117 166q-120 -100 -230 -143 t-247 -43q-168 0 -269 96t-101 266zM229 354q0 -106 66.5 -170.5t175.5 -64.5q87 0 168 33t195 124l-306 433q-128 -67 -184 -116t-85.5 -107.5t-29.5 -131.5zM516 1118q0 -120 82 -235q139 71 191 110t83 85t31 104q0 77 -42.5 121.5t-123.5 44.5q-105 0 -163 -60t-58 -170 z" />
|
||||
<glyph unicode="'" horiz-adv-x="444" d="M225 934l72 528h188l-153 -528h-107z" />
|
||||
<glyph unicode="(" horiz-adv-x="584" d="M82 272q0 339 120 627t384 563h157q-246 -270 -371.5 -570t-125.5 -618q0 -339 114 -598h-131q-147 266 -147 596z" />
|
||||
<glyph unicode=")" horiz-adv-x="584" d="M-160 -324q496 551 496 1188q0 341 -113 598h131q146 -269 146 -598q0 -341 -121.5 -629.5t-382.5 -558.5h-156z" />
|
||||
<glyph unicode="*" horiz-adv-x="1130" d="M215 1194l55 154l371 -185l41 400l172 -35l-123 -383l422 18l-8 -157l-393 47l180 -383l-166 -52l-113 406l-258 -344l-116 121l309 284z" />
|
||||
<glyph unicode="+" d="M127 651v142h389v391h141v-391h390v-142h-390v-387h-141v387h-389z" />
|
||||
<glyph unicode="," horiz-adv-x="492" d="M-100 -264q126 286 204 502h187l8 -23q-113 -235 -270 -479h-129z" />
|
||||
<glyph unicode="-" horiz-adv-x="639" d="M55 469l35 158h479l-34 -158h-480z" />
|
||||
<glyph unicode="." horiz-adv-x="518" d="M43 74q0 77 40.5 122.5t111.5 45.5q43 0 69.5 -26t26.5 -79q0 -71 -40 -118.5t-108 -47.5q-46 0 -73 26t-27 77z" />
|
||||
<glyph unicode="/" horiz-adv-x="717" d="M-94 0l813 1462h174l-813 -1462h-174z" />
|
||||
<glyph unicode="0" d="M121 477q0 270 82 514.5t216.5 369t307.5 124.5q365 0 365 -471q0 -295 -78.5 -539t-214 -369.5t-314.5 -125.5q-176 0 -270 127.5t-94 369.5zM293 479q0 -172 50 -264t161 -92q115 0 209 114t150.5 328t56.5 453q0 323 -203 323q-113 0 -209 -115.5t-155.5 -323 t-59.5 -423.5z" />
|
||||
<glyph unicode="1" d="M303 1178l449 284h149l-313 -1462h-172l196 913q59 261 88 359q-50 -53 -139 -111l-178 -110z" />
|
||||
<glyph unicode="2" d="M12 0l31 147l465 420q102 93 176.5 163.5t123 133t72 124t23.5 136.5q0 99 -60 157t-163 58q-77 0 -150.5 -28.5t-162.5 -96.5l-82 115q191 154 413 154q176 0 278.5 -88.5t102.5 -243.5q0 -111 -39.5 -204t-131 -197t-294.5 -281l-352 -307v-8h678l-29 -154h-899z" />
|
||||
<glyph unicode="3" d="M47 59v164q94 -49 199 -75.5t190 -26.5q162 0 252 79.5t90 217.5q0 131 -79 198.5t-220 67.5h-131l31 143h139q165 0 274 87t109 227q0 92 -58 146t-157 54q-80 0 -157 -27t-175 -93l-80 118q195 144 424 144q179 0 277 -87t98 -237q0 -156 -101 -264.5t-280 -140.5v-9 q124 -23 195 -106.5t71 -208.5q0 -133 -62 -234.5t-181 -158.5t-283 -57q-210 0 -385 79z" />
|
||||
<glyph unicode="4" d="M16 334l29 158l834 978h196l-207 -983h232l-33 -153h-233l-72 -334h-164l74 334h-656zM219 487h486q46 220 78 373t116 445h-8q-17 -29 -66.5 -96.5t-72.5 -96.5z" />
|
||||
<glyph unicode="5" d="M80 59v164q164 -102 334 -102q191 0 298 96t107 268q0 126 -73.5 199.5t-204.5 73.5q-48 0 -97 -6.5t-139 -30.5l-74 57l197 684h668l-33 -153h-522l-127 -439q87 23 184 23q182 0 289.5 -104.5t107.5 -282.5q0 -161 -73 -283t-204 -182.5t-308 -60.5q-193 0 -330 79z " />
|
||||
<glyph unicode="6" d="M133 424q0 209 60.5 415t163.5 351.5t246 219t327 73.5q111 0 184 -23l-35 -145q-68 22 -170 22q-212 0 -356.5 -149t-212.5 -443h8q59 79 146.5 126t193.5 47q154 0 244 -98.5t90 -270.5q0 -161 -66.5 -294.5t-180.5 -204t-261 -70.5q-182 0 -281.5 115t-99.5 329z M299 416q0 -137 60.5 -216t172.5 -79q94 0 167.5 54t114 149t40.5 208q0 248 -221 248q-66 0 -128 -28.5t-110 -76t-72 -104.5t-24 -155z" />
|
||||
<glyph unicode="7" d="M174 0l768 1313h-719l31 149h891l-27 -139l-764 -1323h-180z" />
|
||||
<glyph unicode="8" d="M96 346q0 148 95 256t296 184q-95 69 -135.5 144.5t-40.5 171.5q0 111 54.5 198.5t153.5 136t222 48.5q174 0 271.5 -86.5t97.5 -235.5q0 -129 -78 -225t-266 -176q127 -78 180 -165t53 -202q0 -122 -60 -217.5t-172.5 -146.5t-264.5 -51q-190 0 -298 98.5t-108 267.5z M270 354q0 -107 69 -170t181 -63q139 0 222 74t83 196q0 99 -52 174t-165 135q-185 -60 -261.5 -143.5t-76.5 -202.5zM479 1100q0 -82 39 -144t127 -116q161 60 228 131.5t67 173.5q0 90 -57.5 143t-153.5 53q-114 0 -182 -65.5t-68 -175.5z" />
|
||||
<glyph unicode="9" d="M98 14v158q134 -47 246 -47q202 0 327 141t189 441h-10q-51 -75 -132.5 -118.5t-180.5 -43.5q-169 0 -261 98.5t-92 288.5q0 153 64.5 280.5t180 199t259.5 71.5q180 0 279.5 -114.5t99.5 -334.5q0 -194 -56 -406.5t-147.5 -360t-221.5 -217.5t-302 -70q-136 0 -242 34z M350 938q0 -124 54.5 -190t162.5 -66q76 0 140 28.5t108.5 81.5t65 114t20.5 151q0 131 -59 207.5t-160 76.5q-150 0 -241 -113t-91 -290z" />
|
||||
<glyph unicode=":" horiz-adv-x="518" d="M43 74q0 77 40.5 122.5t111.5 45.5q43 0 69.5 -26t26.5 -79q0 -71 -40 -118.5t-108 -47.5q-46 0 -73 26t-27 77zM203 956q0 77 40 122.5t111 45.5q97 0 97 -104q0 -73 -41.5 -119.5t-106.5 -46.5q-46 0 -73 26.5t-27 75.5z" />
|
||||
<glyph unicode=";" horiz-adv-x="518" d="M-100 -264q126 286 204 502h187l8 -23q-113 -235 -270 -479h-129zM203 956q0 77 40 122.5t111 45.5q97 0 97 -104q0 -73 -41.5 -119.5t-106.5 -46.5q-46 0 -73 26.5t-27 75.5z" />
|
||||
<glyph unicode="<" d="M121 664v98l919 479v-149l-747 -371l747 -328v-151z" />
|
||||
<glyph unicode="=" d="M127 444v142h920v-142h-920zM127 858v139h920v-139h-920z" />
|
||||
<glyph unicode=">" d="M121 242v151l745 328l-745 371v149l919 -479v-98z" />
|
||||
<glyph unicode="?" horiz-adv-x="874" d="M158 74q0 77 40 122.5t111 45.5q44 0 70.5 -26t26.5 -79q0 -73 -41.5 -119.5t-106.5 -46.5q-46 0 -73 26t-27 77zM197 1382q92 51 192 76t182 25q167 0 259 -84t92 -238q0 -123 -65.5 -226.5t-225.5 -223.5q-125 -91 -169 -147.5t-67 -160.5h-135q22 130 72.5 213.5 t165.5 174.5q128 100 168 144t63 94t23 112q0 93 -51.5 143.5t-147.5 50.5q-81 0 -155 -25.5t-140 -56.5z" />
|
||||
<glyph unicode="@" horiz-adv-x="1735" d="M111 504q0 261 126.5 485.5t343.5 347.5t486 123q191 0 329 -75.5t210.5 -213.5t72.5 -319q0 -179 -55 -324t-155 -227t-222 -82q-197 0 -213 184h-8q-111 -184 -291 -184q-115 0 -180.5 75.5t-65.5 209.5q0 157 68 284t188.5 199t260.5 72q65 0 127.5 -12t150.5 -48 q-64 -242 -98 -368t-31 -172q0 -117 102 -117q78 0 141.5 67t100.5 183.5t37 243.5q0 239 -128 367t-370 128q-228 0 -406.5 -107t-277 -295.5t-98.5 -416.5q0 -270 143.5 -418.5t409.5 -148.5q197 0 420 86v-127q-219 -90 -443 -90q-314 0 -494.5 184.5t-180.5 505.5z M639 518q0 -93 33 -134.5t98 -41.5q187 0 272 315l70 258q-63 23 -127 23q-94 0 -174 -55t-126 -153t-46 -212z" />
|
||||
<glyph unicode="A" horiz-adv-x="1137" d="M-117 0l799 1462h174l184 -1462h-170l-57 465h-496l-245 -465h-189zM401 621h394l-35 299q-24 179 -29 350q-37 -88 -80.5 -175t-249.5 -474z" />
|
||||
<glyph unicode="B" horiz-adv-x="1225" d="M86 0l309 1462h375q432 0 432 -336q0 -141 -87 -238t-245 -126v-10q115 -32 176.5 -110.5t61.5 -188.5q0 -212 -152 -332.5t-407 -120.5h-463zM287 145h266q181 0 278 80.5t97 227.5q0 116 -74.5 177.5t-214.5 61.5h-236zM434 836h248q156 0 249 73t93 199 q0 104 -66.5 155.5t-209.5 51.5h-211z" />
|
||||
<glyph unicode="C" horiz-adv-x="1198" d="M150 537q0 261 105.5 485.5t283.5 342.5t403 118q197 0 348 -80l-69 -141q-138 69 -279 69q-174 0 -311.5 -97t-218 -284.5t-80.5 -408.5q0 -187 97.5 -298.5t268.5 -111.5q139 0 322 57v-149q-86 -31 -164 -45t-188 -14q-242 0 -380 149.5t-138 407.5z" />
|
||||
<glyph unicode="D" horiz-adv-x="1364" d="M86 0l309 1462h342q276 0 419.5 -149.5t143.5 -435.5q0 -261 -105 -461t-300 -308t-457 -108h-352zM287 147h162q202 0 355 91.5t234.5 258.5t81.5 382t-103 325.5t-302 110.5h-178z" />
|
||||
<glyph unicode="E" horiz-adv-x="1047" d="M86 0l309 1462h735l-32 -153h-566l-98 -469h527l-29 -152h-529l-114 -536h565l-33 -152h-735z" />
|
||||
<glyph unicode="F" horiz-adv-x="967" d="M86 0l309 1462h735l-30 -153h-568l-110 -533h528l-32 -153h-529l-131 -623h-172z" />
|
||||
<glyph unicode="G" horiz-adv-x="1386" d="M150 528q0 269 101.5 489.5t281.5 343t399 122.5q117 0 219.5 -20t206.5 -64l-66 -152q-77 34 -165.5 59t-194.5 25q-169 0 -307.5 -101.5t-215.5 -283.5t-77 -407q0 -190 102.5 -299t286.5 -109q154 0 260 39l96 444h-289l33 152h459l-154 -711q-216 -75 -419 -75 q-264 0 -410.5 144.5t-146.5 403.5z" />
|
||||
<glyph unicode="H" horiz-adv-x="1389" d="M86 0l309 1462h170l-131 -622h660l133 622h168l-310 -1462h-167l143 688h-660l-145 -688h-170z" />
|
||||
<glyph unicode="I" horiz-adv-x="559" d="M86 0l311 1462h168l-311 -1462h-168z" />
|
||||
<glyph unicode="J" horiz-adv-x="547" d="M-319 -360l6 147q69 -20 145 -20q100 0 165.5 62.5t90.5 182.5l307 1450h170l-309 -1468q-79 -379 -422 -379q-105 0 -153 25z" />
|
||||
<glyph unicode="K" horiz-adv-x="1141" d="M86 0l309 1462h170l-151 -710l700 710h209l-639 -637l350 -825h-186q-72 181 -146.5 359.5t-146.5 361.5l-174 -131l-125 -590h-170z" />
|
||||
<glyph unicode="L" horiz-adv-x="971" d="M86 0l309 1462h170l-276 -1308h565l-33 -154h-735z" />
|
||||
<glyph unicode="M" horiz-adv-x="1714" d="M84 0l309 1462h244l149 -1204h9l659 1204h266l-303 -1462h-174q126 590 193 905.5t94 392.5h-6l-717 -1298h-131l-166 1296h-8q-7 -72 -28.5 -197.5t-37.5 -199.5l-190 -899h-162z" />
|
||||
<glyph unicode="N" horiz-adv-x="1438" d="M84 0l309 1462h180l459 -1220h6q30 224 72 405l174 815h164l-309 -1462h-181l-460 1223h-6q-32 -221 -74 -418l-172 -805h-162z" />
|
||||
<glyph unicode="O" horiz-adv-x="1475" d="M150 549q0 264 96 482t263.5 336t377.5 118q244 0 384 -154t140 -424q0 -269 -88 -481.5t-252 -329t-379 -116.5q-256 0 -399 149.5t-143 419.5zM332 553q0 -199 98 -310.5t266 -111.5q152 0 272.5 97.5t190.5 279.5t70 403q0 199 -94 310.5t-261 111.5q-157 0 -281 -101 t-192.5 -281t-68.5 -398z" />
|
||||
<glyph unicode="P" horiz-adv-x="1159" d="M86 0l309 1462h330q214 0 324 -94.5t110 -282.5q0 -248 -164 -379t-481 -131h-135l-123 -575h-170zM410 721h133q216 0 328 91t112 267q0 125 -69.5 180.5t-213.5 55.5h-163z" />
|
||||
<glyph unicode="Q" horiz-adv-x="1475" d="M150 549q0 264 96 482t263.5 336t377.5 118q244 0 384 -154t140 -424q0 -333 -139 -576t-375 -321l274 -358h-219l-227 330l-17 -2h-16q-256 0 -399 149.5t-143 419.5zM332 553q0 -199 98 -310.5t266 -111.5q158 0 279 100t187.5 280.5t66.5 399.5q0 199 -94 310.5 t-261 111.5q-157 0 -281 -101t-192.5 -281t-68.5 -398z" />
|
||||
<glyph unicode="R" horiz-adv-x="1165" d="M86 0l309 1462h320q446 0 446 -366q0 -348 -368 -449l239 -647h-186l-209 608h-252l-129 -608h-170zM416 754h168q193 0 297 85t104 244q0 121 -67.5 175.5t-219.5 54.5h-166q-102 -494 -116 -559z" />
|
||||
<glyph unicode="S" horiz-adv-x="1028" d="M39 43v170q162 -84 340 -84q162 0 257 75.5t95 207.5q0 78 -52.5 137.5t-195.5 140.5q-151 85 -209.5 170t-58.5 201q0 187 132 304.5t347 117.5q99 0 184.5 -19t180.5 -65l-66 -150q-66 38 -148 60t-151 22q-134 0 -215.5 -69.5t-81.5 -188.5q0 -54 17 -92.5t54 -72.5 t142 -95q147 -88 198.5 -138t78 -110.5t26.5 -140.5q0 -211 -140.5 -327.5t-395.5 -116.5q-106 0 -186.5 14.5t-151.5 48.5z" />
|
||||
<glyph unicode="T" horiz-adv-x="1020" d="M186 1311l33 151h985l-30 -151h-408l-279 -1311h-172l277 1311h-406z" />
|
||||
<glyph unicode="U" horiz-adv-x="1384" d="M164 383q0 81 24 201l189 878h170l-191 -891q-22 -106 -22 -188q0 -117 73 -184.5t218 -67.5q172 0 267.5 87.5t139.5 289.5l205 954h170l-205 -966q-55 -263 -197.5 -389.5t-388.5 -126.5q-230 0 -341 104t-111 299z" />
|
||||
<glyph unicode="V" horiz-adv-x="1122" d="M188 1462h170l97 -930q20 -196 20 -335h4q61 144 162 338l479 927h191l-781 -1462h-180z" />
|
||||
<glyph unicode="W" horiz-adv-x="1745" d="M223 1462h170l31 -901l2 -88q0 -98 -10 -258h6q89 243 156 383l405 864h178l43 -860q9 -153 9 -304l-1 -83h9q75 224 131 354l387 893h182l-664 -1462h-170l-49 965q-8 136 -8 282h-6q-25 -72 -61 -154.5t-504 -1092.5h-174z" />
|
||||
<glyph unicode="X" horiz-adv-x="1063" d="M-104 0l596 776l-263 686h172l203 -563l443 563h186l-555 -694l278 -768h-180l-213 641l-481 -641h-186z" />
|
||||
<glyph unicode="Y" horiz-adv-x="1030" d="M188 1462h170l179 -747l489 747h193l-627 -921l-113 -541h-172l119 549z" />
|
||||
<glyph unicode="Z" horiz-adv-x="1087" d="M-16 0l28 137l924 1170h-655l32 155h858l-26 -139l-924 -1169h697l-33 -154h-901z" />
|
||||
<glyph unicode="[" horiz-adv-x="586" d="M-16 -324l381 1786h387l-31 -141h-227l-318 -1503h227l-32 -142h-387z" />
|
||||
<glyph unicode="\" horiz-adv-x="717" d="M221 1462h154l217 -1462h-154z" />
|
||||
<glyph unicode="]" horiz-adv-x="586" d="M-150 -324l31 142h225l320 1503h-227l30 141h389l-380 -1786h-388z" />
|
||||
<glyph unicode="^" horiz-adv-x="1059" d="M53 553l598 920h109l266 -920h-145l-201 747l-467 -747h-160z" />
|
||||
<glyph unicode="_" horiz-adv-x="807" d="M-188 -324l30 140h811l-30 -140h-811z" />
|
||||
<glyph unicode="`" horiz-adv-x="1135" d="M575 1548v21h181q43 -136 147 -303v-25h-104q-61 61 -128.5 154t-95.5 153z" />
|
||||
<glyph unicode="a" horiz-adv-x="1157" d="M98 350q0 208 71 386t196 279t274 101q92 0 164 -49.5t112 -142.5h11l67 172h127l-233 -1096h-133l26 209h-8q-179 -229 -377 -229q-139 0 -218 99t-79 271zM270 346q0 -114 47 -170.5t132 -56.5q97 0 193 92.5t156 241t60 297.5q0 103 -56 164t-147 61 q-104 0 -193.5 -86t-140.5 -233t-51 -310z" />
|
||||
<glyph unicode="b" horiz-adv-x="1182" d="M59 0l330 1556h168q-51 -242 -78.5 -370.5t-75.5 -300.5h9q93 118 183.5 173.5t186.5 55.5q141 0 220 -99t79 -272q0 -209 -68.5 -386.5t-191 -277t-276.5 -99.5q-97 0 -170.5 51t-110.5 139h-10l-70 -170h-125zM319 346q0 -110 55.5 -168.5t160.5 -58.5q99 0 184.5 81 t137.5 230.5t52 317.5q0 227 -178 227q-96 0 -195.5 -95t-158 -239t-58.5 -295z" />
|
||||
<glyph unicode="c" horiz-adv-x="922" d="M98 389q0 200 74 369t204.5 263.5t293.5 94.5q137 0 268 -51l-47 -141q-120 51 -219 51q-112 0 -204.5 -76.5t-145 -213t-52.5 -296.5q0 -128 66.5 -199t183.5 -71q72 0 136 20t126 47v-143q-124 -63 -276 -63q-194 0 -301 107t-107 302z" />
|
||||
<glyph unicode="d" horiz-adv-x="1182" d="M98 350q0 214 72 392t194.5 275t274.5 97q194 0 281 -190h10q17 155 45 274l78 358h166l-330 -1556h-139l22 209h-8q-101 -125 -189 -177t-182 -52q-139 0 -217 98t-78 272zM270 346q0 -227 179 -227q94 0 194 93.5t158.5 239t58.5 296.5q0 111 -54 169t-157 58 q-101 0 -187.5 -82.5t-139 -232t-52.5 -314.5z" />
|
||||
<glyph unicode="e" horiz-adv-x="1010" d="M98 391q0 188 74.5 360.5t197.5 268.5t271 96q153 0 230 -66.5t77 -185.5q0 -180 -166 -282.5t-475 -102.5h-33l-4 -80q0 -131 61.5 -204.5t190.5 -73.5q63 0 129.5 18t165.5 66v-146q-94 -44 -166 -61.5t-159 -17.5q-184 0 -289 109t-105 302zM299 618h12 q228 0 349.5 59.5t121.5 172.5q0 53 -36.5 88t-114.5 35q-103 0 -193.5 -94t-138.5 -261z" />
|
||||
<glyph unicode="f" horiz-adv-x="641" d="M-229 -330q64 -22 112 -22q76 0 117 62t66 177l227 1082h-193l13 67l206 66l23 100q46 200 127.5 282.5t241.5 82.5q40 0 98 -11.5t90 -25.5l-43 -129q-76 29 -137 29q-87 0 -133.5 -48.5t-75.5 -177.5l-25 -108h238l-25 -127h-237l-232 -1098q-39 -189 -120 -276 t-213 -87q-69 0 -125 21v141z" />
|
||||
<glyph unicode="g" horiz-adv-x="1026" d="M-127 -211q0 105 72 182t233 131q-78 41 -78 121q0 69 51 118.5t142 92.5q-63 32 -103 94.5t-40 145.5q0 194 119.5 318t305.5 124q78 0 154 -20h371l-25 -107l-211 -24q41 -62 41 -158q0 -191 -116.5 -304.5t-311.5 -113.5q-55 0 -84 8q-139 -53 -139 -131 q0 -41 33 -54.5t96 -21.5l117 -14q181 -22 262.5 -88t81.5 -194q0 -184 -146 -285t-411 -101q-194 0 -304 73.5t-110 207.5zM35 -195q0 -77 65 -122t193 -45q182 0 284.5 63.5t102.5 179.5q0 62 -54 98t-184 50l-159 16q-120 -25 -184 -88t-64 -152zM313 680 q0 -85 45 -129.5t125 -44.5q79 0 138 42t90.5 115.5t31.5 159.5q0 82 -44 125t-126 43q-78 0 -136.5 -40.5t-91 -113t-32.5 -157.5z" />
|
||||
<glyph unicode="h" horiz-adv-x="1182" d="M59 0l330 1556h168q-18 -82 -34.5 -159t-34 -156.5t-38 -166.5t-47.5 -189h11q94 123 185.5 176t191.5 53q131 0 202.5 -72t71.5 -204q0 -62 -23 -166q-39 -193 -145 -672h-168l148 692q18 94 18 135q0 148 -147 148q-89 0 -173.5 -59t-149 -171.5t-97.5 -271.5 l-101 -473h-168z" />
|
||||
<glyph unicode="i" horiz-adv-x="520" d="M59 0l234 1096h168l-234 -1096h-168zM340 1376q0 56 32 91.5t83 35.5q88 0 88 -90q0 -55 -33.5 -93t-77.5 -38q-40 0 -66 24.5t-26 69.5z" />
|
||||
<glyph unicode="j" horiz-adv-x="520" d="M-258 -330q61 -22 119 -22q125 0 168 205l264 1243h166l-266 -1258q-36 -171 -114.5 -250.5t-213.5 -79.5q-69 0 -123 21v141zM340 1376q0 56 32 91.5t83 35.5q86 0 86 -90q0 -55 -33.5 -93t-77.5 -38q-38 0 -64 24.5t-26 69.5z" />
|
||||
<glyph unicode="k" horiz-adv-x="999" d="M57 0l330 1556h170l-129 -602q-57 -266 -102 -395h4l526 537h201l-469 -467l295 -629h-187l-235 524l-152 -123l-82 -401h-170z" />
|
||||
<glyph unicode="l" horiz-adv-x="520" d="M57 0l332 1556h168l-332 -1556h-168z" />
|
||||
<glyph unicode="m" horiz-adv-x="1786" d="M59 0l234 1096h139l-22 -203h10q87 119 173.5 171t178.5 52q113 0 174 -65t72 -181h8q86 125 183 185.5t196 60.5q127 0 196.5 -68t69.5 -198q0 -68 -22 -178l-144 -672h-170l148 692q20 104 20 146q0 62 -34.5 99.5t-108.5 37.5q-81 0 -160 -58t-138.5 -164.5 t-90.5 -252.5l-107 -500h-168l148 692q18 94 18 135q0 70 -31 109t-106 39q-84 0 -163.5 -60t-140 -171.5t-93.5 -268.5l-101 -475h-168z" />
|
||||
<glyph unicode="n" horiz-adv-x="1182" d="M59 0l234 1096h139l-22 -203h10q96 122 185.5 172.5t185.5 50.5q127 0 200.5 -69.5t73.5 -194.5q0 -79 -23 -180l-143 -672h-170l148 692q20 104 20 144q0 63 -35.5 101t-113.5 38q-89 0 -173.5 -60t-149 -171t-97.5 -269l-101 -475h-168z" />
|
||||
<glyph unicode="o" horiz-adv-x="1149" d="M98 406q0 190 73 357.5t197 257t275 89.5q190 0 300 -112.5t110 -309.5q0 -188 -72 -355t-195 -258t-278 -91q-192 0 -301 113t-109 309zM270 397q0 -131 63.5 -202.5t182.5 -71.5q104 0 187 73t129.5 207.5t46.5 307.5q0 115 -62.5 186.5t-169.5 71.5q-109 0 -195.5 -74 t-134 -205.5t-47.5 -292.5z" />
|
||||
<glyph unicode="p" horiz-adv-x="1182" d="M-43 -492l336 1588h139l-26 -209h8q179 227 372 227q137 0 216 -97.5t79 -273.5q0 -212 -69 -389t-191 -275.5t-276 -98.5q-97 0 -170 50t-113 140h-10l-4 -38q-3 -25 -10.5 -70t-114.5 -554h-166zM319 346q0 -110 55.5 -168.5t160.5 -58.5q99 0 184.5 81t137.5 230.5 t52 317.5q0 227 -178 227q-96 0 -195.5 -95t-158 -239t-58.5 -295z" />
|
||||
<glyph unicode="q" horiz-adv-x="1182" d="M98 350q0 212 72.5 392t196 277t274.5 97q94 0 165.5 -50.5t108.5 -141.5h13l67 172h125l-336 -1588h-166l101 480q9 45 57 221h-8q-95 -121 -185 -175t-186 -54q-140 0 -219.5 97.5t-79.5 272.5zM270 346q0 -227 179 -227q92 0 190 92t158.5 237t60.5 300 q0 105 -54.5 166t-152.5 61q-101 0 -189 -84.5t-140 -233t-52 -311.5z" />
|
||||
<glyph unicode="r" horiz-adv-x="811" d="M59 0l234 1096h139l-22 -203h10q72 95 119 136.5t98.5 64t114.5 22.5q69 0 120 -14l-36 -150q-53 13 -105 13q-91 0 -170.5 -60t-139 -166.5t-87.5 -236.5l-107 -502h-168z" />
|
||||
<glyph unicode="s" horiz-adv-x="877" d="M8 49v158q70 -42 151 -65t150 -23q126 0 190 50t64 128q0 57 -35 96t-151 107q-130 73 -184 143t-54 166q0 138 101 222.5t266 84.5q171 0 330 -74l-54 -137l-56 25q-101 43 -220 43q-93 0 -146 -43.5t-53 -112.5q0 -56 35.5 -96t146.5 -103q107 -60 153.5 -103 t69.5 -92.5t23 -111.5q0 -156 -110.5 -243.5t-311.5 -87.5q-169 0 -305 69z" />
|
||||
<glyph unicode="t" horiz-adv-x="664" d="M90 969l14 73l185 78l125 228h98l-55 -252h274l-26 -127h-273l-129 -604q-18 -87 -18 -132q0 -56 29 -86t81 -30q55 0 144 26v-129q-34 -14 -84 -24t-80 -10q-125 0 -191.5 59.5t-66.5 177.5q0 66 18 150l127 602h-172z" />
|
||||
<glyph unicode="u" horiz-adv-x="1182" d="M113 248q0 62 22 172l146 676h170l-150 -695q-18 -89 -18 -139q0 -143 147 -143q88 0 173 60t150 172t99 270l100 475h166l-231 -1096h-139l22 203h-12q-98 -125 -187 -174t-184 -49q-128 0 -201 69.5t-73 198.5z" />
|
||||
<glyph unicode="v" horiz-adv-x="946" d="M98 1096h168l64 -613q24 -258 24 -362h6q127 275 179 371l325 604h178l-591 -1096h-228z" />
|
||||
<glyph unicode="w" horiz-adv-x="1468" d="M117 1096h164l18 -594v-88q0 -147 -8 -269h6q47 124 137 322l295 629h182l37 -594q6 -168 6 -262v-53l-2 -42h6q28 86 83 218.5t323 732.5h178l-506 -1096h-205l-32 602q-4 94 -4 172v156h-9l-50 -118l-83 -189l-291 -623h-202z" />
|
||||
<glyph unicode="x" horiz-adv-x="979" d="M-74 0l475 565l-239 531h170l174 -412l330 412h194l-455 -539l252 -557h-168l-192 434l-346 -434h-195z" />
|
||||
<glyph unicode="y" horiz-adv-x="946" d="M-197 -336q63 -18 131 -18q82 0 140.5 50.5t113.5 149.5l76 136l-166 1114h168l74 -545q10 -69 19.5 -203.5t9.5 -216.5h6q35 87 87 200t77 156l325 609h178l-696 -1282q-93 -172 -184 -239t-219 -67q-72 0 -140 21v135z" />
|
||||
<glyph unicode="z" horiz-adv-x="909" d="M-29 0l23 117l694 854h-479l27 125h657l-29 -140l-680 -831h531l-25 -125h-719z" />
|
||||
<glyph unicode="{" horiz-adv-x="715" d="M27 514l32 143q118 0 189.5 43.5t93.5 147.5l68 326q34 160 117.5 224t254.5 64h33l-31 -141q-105 0 -151 -36.5t-66 -123.5l-71 -321q-28 -123 -91 -184t-167 -78v-5q151 -41 151 -213q0 -59 -18 -131l-47 -211q-15 -58 -15 -98q0 -53 36.5 -77.5t119.5 -24.5v-142h-23 q-141 0 -216.5 52.5t-75.5 171.5q0 52 20 141q33 146 51.5 227.5t14.5 102.5q0 143 -209 143z" />
|
||||
<glyph unicode="|" d="M541 -496v2052h139v-2052h-139z" />
|
||||
<glyph unicode="}" horiz-adv-x="715" d="M-74 -182q115 0 167 36t71 123l72 322q25 117 88 179.5t170 80.5v6q-150 42 -150 211q0 59 18 131l50 213q14 65 14 99q0 53 -40.5 77.5t-139.5 24.5l28 141h11q144 0 220.5 -52.5t76.5 -170.5q0 -48 -21 -141l-49 -219q-16 -68 -16 -111q0 -143 209 -143l-33 -144 q-119 0 -190 -43t-93 -147l-67 -326q-36 -164 -119 -226.5t-264 -62.5h-13v142z" />
|
||||
<glyph unicode="~" d="M115 592v151q98 109 243 109q69 0 127 -14.5t144 -51.5q64 -27 112.5 -41t98.5 -14q55 0 119.5 33t115.5 88v-150q-100 -110 -244 -110q-72 0 -135 16.5t-135 48.5q-75 32 -120 44t-93 12q-54 0 -118.5 -34.5t-114.5 -86.5z" />
|
||||
<glyph unicode="¢" d="M225 590q0 185 63.5 344t178.5 258.5t260 120.5l35 170h123l-37 -168q119 -9 217 -49l-47 -142q-109 52 -219 52q-112 0 -204.5 -76.5t-145 -213t-52.5 -296.5q0 -125 66 -198t184 -73q72 0 136 20t126 48v-143q-123 -62 -286 -66l-41 -198h-125l43 215 q-132 34 -203.5 137.5t-71.5 257.5z" />
|
||||
<glyph unicode="£" d="M-23 0l27 141q205 46 258 289l47 221h-200l26 127h201l76 350q75 353 430 353q184 0 336 -86l-66 -133q-146 79 -278 79q-213 0 -263 -237l-69 -326h370l-26 -127h-371l-47 -219q-22 -98 -66 -166.5t-124 -111.5h725l-33 -154h-953z" />
|
||||
<glyph unicode="¥" d="M127 266l29 133h290l33 160h-291l29 133h225l-202 770h163l179 -747l491 747h187l-533 -770h231l-28 -133h-297l-33 -160h297l-29 -133h-295l-57 -266h-154l56 266h-291z" />
|
||||
<glyph unicode="©" horiz-adv-x="1704" d="M139 731q0 200 100 375t275 276t377 101q197 0 370 -97t277 -272t104 -383q0 -204 -100.5 -376.5t-273 -273.5t-377.5 -101q-207 0 -382 103.5t-272.5 276.5t-97.5 371zM244 731q0 -173 87 -323.5t237.5 -237t322.5 -86.5q174 0 323 87t236.5 235.5t87.5 324.5 q0 174 -87 323t-235.5 236.5t-324.5 87.5q-174 0 -323 -87t-236.5 -235.5t-87.5 -324.5zM520 733q0 208 110 330.5t300 122.5q130 0 248 -60l-60 -120q-106 53 -190 53q-125 0 -191.5 -87t-66.5 -241q0 -169 65 -249.5t193 -80.5q82 0 211 43v-122q-66 -28 -113 -38 t-104 -10q-192 0 -297 119.5t-105 339.5z" />
|
||||
<glyph unicode="­" horiz-adv-x="639" d="M55 469l35 158h479l-34 -158h-480z" />
|
||||
<glyph unicode="®" horiz-adv-x="1704" d="M139 731q0 200 100 375t275 276t377 101q197 0 370 -97t277 -272t104 -383q0 -204 -100.5 -376.5t-273 -273.5t-377.5 -101q-207 0 -382 103.5t-272.5 276.5t-97.5 371zM244 731q0 -173 87 -323.5t237.5 -237t322.5 -86.5q174 0 323 87t236.5 235.5t87.5 324.5 q0 174 -87 323t-235.5 236.5t-324.5 87.5q-174 0 -323 -87t-236.5 -235.5t-87.5 -324.5zM645 291v880h229q163 0 241.5 -63t78.5 -193q0 -78 -47.5 -141t-132.5 -98l227 -385h-149l-207 352h-113v-352h-127zM772 762h92q195 0 195 149q0 76 -47.5 107t-149.5 31h-90v-287z " />
|
||||
<glyph unicode="´" horiz-adv-x="1135" d="M532 1241v27q56 60 125.5 151.5t106.5 149.5h190v-21q-38 -49 -140 -151t-177 -156h-105z" />
|
||||
<glyph unicode=" " horiz-adv-x="784" />
|
||||
<glyph unicode=" " horiz-adv-x="1569" />
|
||||
<glyph unicode=" " horiz-adv-x="784" />
|
||||
<glyph unicode=" " horiz-adv-x="1569" />
|
||||
<glyph unicode=" " horiz-adv-x="523" />
|
||||
<glyph unicode=" " horiz-adv-x="392" />
|
||||
<glyph unicode=" " horiz-adv-x="261" />
|
||||
<glyph unicode=" " horiz-adv-x="261" />
|
||||
<glyph unicode=" " horiz-adv-x="196" />
|
||||
<glyph unicode=" " horiz-adv-x="313" />
|
||||
<glyph unicode=" " horiz-adv-x="87" />
|
||||
<glyph unicode="‐" horiz-adv-x="639" d="M55 469l35 158h479l-34 -158h-480z" />
|
||||
<glyph unicode="‑" horiz-adv-x="639" d="M55 469l35 158h479l-34 -158h-480z" />
|
||||
<glyph unicode="‒" horiz-adv-x="639" d="M55 469l35 158h479l-34 -158h-480z" />
|
||||
<glyph unicode="–" horiz-adv-x="983" d="M55 469l35 160h823l-34 -160h-824z" />
|
||||
<glyph unicode="—" horiz-adv-x="1966" d="M55 469l35 160h1806l-34 -160h-1807z" />
|
||||
<glyph unicode="‘" horiz-adv-x="348" d="M123 983q98 211 270 479h127q-147 -345 -203 -501h-188z" />
|
||||
<glyph unicode="’" horiz-adv-x="348" d="M125 961q134 298 203 501h188l8 -22q-40 -91 -111 -218.5t-159 -260.5h-129z" />
|
||||
<glyph unicode="“" horiz-adv-x="719" d="M123 983q98 211 270 479h127q-147 -345 -203 -501h-188zM492 983q80 181 272 479h127q-162 -379 -203 -501h-188z" />
|
||||
<glyph unicode="”" horiz-adv-x="719" d="M125 961q134 298 203 501h188l8 -22q-40 -91 -111 -218.5t-159 -260.5h-129zM494 961q57 126 115.5 272.5t86.5 228.5h189l10 -22q-94 -206 -274 -479h-127z" />
|
||||
<glyph unicode="•" horiz-adv-x="774" d="M199 684q0 145 73.5 231t198.5 86q92 0 139 -49t47 -141q0 -141 -74 -230t-202 -89q-89 0 -135.5 49.5t-46.5 142.5z" />
|
||||
<glyph unicode="…" horiz-adv-x="1563" d="M563 74q0 77 40.5 122.5t111.5 45.5q43 0 69.5 -26t26.5 -79q0 -71 -40 -118.5t-108 -47.5q-46 0 -73 26t-27 77zM1085 74q0 77 40.5 122.5t111.5 45.5q43 0 69.5 -26t26.5 -79q0 -71 -40 -118.5t-108 -47.5q-46 0 -73 26t-27 77zM43 74q0 77 40.5 122.5t111.5 45.5 q43 0 69.5 -26t26.5 -79q0 -71 -40 -118.5t-108 -47.5q-46 0 -73 26t-27 77z" />
|
||||
<glyph unicode=" " horiz-adv-x="313" />
|
||||
<glyph unicode=" " horiz-adv-x="392" />
|
||||
<glyph unicode="€" d="M63 504l27 131h154q8 80 30 164h-151l27 133h159q97 267 259.5 408t369.5 141q89 0 160 -21.5t141 -70.5l-80 -138q-113 78 -231 78q-140 0 -254 -99t-189 -298h426l-26 -133h-441q-21 -65 -32 -164h381l-29 -131h-361q0 -373 297 -373q123 0 256 55v-147 q-127 -59 -278 -59q-212 0 -328.5 133.5t-116.5 378.5v12h-170z" />
|
||||
<glyph unicode="™" horiz-adv-x="1534" d="M121 1358v104h516v-104h-199v-617h-121v617h-196zM705 741v721h180l182 -557l193 557h170v-721h-121v430q0 73 4 121h-6l-197 -551h-96l-189 551h-6q4 -52 4 -121v-430h-118z" />
|
||||
<glyph unicode="" horiz-adv-x="1095" d="M0 1095h1095v-1095h-1095v1095z" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 27 KiB |
BIN
couchpotato/static/fonts/OpenSans-Italic-webfont.ttf
Executable file
BIN
couchpotato/static/fonts/OpenSans-Italic-webfont.woff
Executable file
BIN
couchpotato/static/fonts/OpenSans-Regular-webfont.eot
Executable file
146
couchpotato/static/fonts/OpenSans-Regular-webfont.svg
Executable file
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
This is a custom SVG webfont generated by Font Squirrel.
|
||||
Copyright : Digitized data copyright 20102011 Google Corporation
|
||||
Foundry : Ascender Corporation
|
||||
Foundry URL : httpwwwascendercorpcom
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="OpenSansRegular" horiz-adv-x="1171" >
|
||||
<font-face units-per-em="2048" ascent="1638" descent="-410" />
|
||||
<missing-glyph horiz-adv-x="532" />
|
||||
<glyph unicode=" " horiz-adv-x="532" />
|
||||
<glyph unicode="	" horiz-adv-x="532" />
|
||||
<glyph unicode=" " horiz-adv-x="532" />
|
||||
<glyph unicode="!" horiz-adv-x="547" d="M152 106q0 136 120 136q58 0 89.5 -35t31.5 -101q0 -64 -32 -99.5t-89 -35.5q-52 0 -86 31.5t-34 103.5zM170 1462h207l-51 -1059h-105z" />
|
||||
<glyph unicode=""" horiz-adv-x="821" d="M133 1462h186l-40 -528h-105zM502 1462h186l-41 -528h-104z" />
|
||||
<glyph unicode="#" horiz-adv-x="1323" d="M51 430v129h287l68 340h-277v127h299l82 436h139l-82 -436h305l84 436h134l-84 -436h264v-127h-289l-66 -340h283v-129h-307l-84 -430h-137l84 430h-303l-82 -430h-136l80 430h-262zM475 559h303l66 340h-303z" />
|
||||
<glyph unicode="$" d="M131 170v156q83 -37 191.5 -60.5t197.5 -23.5v440q-205 65 -287.5 151t-82.5 222q0 131 101.5 215t268.5 102v182h129v-180q184 -5 355 -74l-52 -131q-149 59 -303 70v-434q157 -50 235 -97.5t115 -109t37 -149.5q0 -136 -102 -224.5t-285 -111.5v-232h-129v223 q-112 0 -217 17.5t-172 48.5zM319 1057q0 -76 45 -122t156 -87v387q-99 -16 -150 -62.5t-51 -115.5zM649 252q217 30 217 184q0 72 -44.5 116.5t-172.5 88.5v-389z" />
|
||||
<glyph unicode="%" horiz-adv-x="1686" d="M104 1026q0 227 74.5 342t220.5 115q145 0 223 -119t78 -338q0 -228 -76.5 -344.5t-224.5 -116.5q-140 0 -217.5 119t-77.5 342zM242 1026q0 -170 37 -255t120 -85q164 0 164 340q0 338 -164 338q-83 0 -120 -84t-37 -254zM365 0l811 1462h147l-811 -1462h-147zM985 440 q0 227 74.5 342t220.5 115q142 0 221.5 -117.5t79.5 -339.5q0 -227 -76.5 -343.5t-224.5 -116.5q-142 0 -218.5 119t-76.5 341zM1122 440q0 -171 37 -255.5t121 -84.5t124 83.5t40 256.5q0 171 -40 253.5t-124 82.5t-121 -82.5t-37 -253.5z" />
|
||||
<glyph unicode="&" horiz-adv-x="1495" d="M113 379q0 130 69.5 230t249.5 202q-85 95 -115.5 144t-48.5 102t-18 110q0 150 98 234t273 84q162 0 255 -83.5t93 -232.5q0 -107 -68 -197.5t-225 -183.5l407 -391q56 62 89.5 145.5t56.5 182.5h168q-68 -286 -205 -434l299 -291h-229l-185 178q-118 -106 -240 -152 t-272 -46q-215 0 -333.5 106t-118.5 293zM285 383q0 -117 77.5 -185.5t206.5 -68.5q241 0 400 154l-437 424q-111 -68 -157 -112.5t-68 -95.5t-22 -116zM414 1171q0 -69 36 -131.5t123 -150.5q129 75 179.5 138.5t50.5 146.5q0 77 -51.5 125.5t-137.5 48.5q-89 0 -144.5 -48 t-55.5 -129z" />
|
||||
<glyph unicode="'" horiz-adv-x="453" d="M133 1462h186l-40 -528h-105z" />
|
||||
<glyph unicode="(" horiz-adv-x="606" d="M82 561q0 265 77.5 496t223.5 405h162q-144 -193 -216.5 -424t-72.5 -475q0 -240 74 -469t213 -418h-160q-147 170 -224 397t-77 488z" />
|
||||
<glyph unicode=")" horiz-adv-x="606" d="M61 1462h162q147 -175 224 -406.5t77 -494.5t-77.5 -490t-223.5 -395h-160q139 188 213 417.5t74 469.5q0 244 -72.5 475t-216.5 424z" />
|
||||
<glyph unicode="*" horiz-adv-x="1130" d="M86 1090l29 182l391 -111l-43 395h194l-43 -395l398 111l26 -182l-381 -31l248 -326l-172 -94l-176 362l-160 -362l-176 94l242 326z" />
|
||||
<glyph unicode="+" d="M104 653v138h410v428h139v-428h412v-138h-412v-426h-139v426h-410z" />
|
||||
<glyph unicode="," horiz-adv-x="502" d="M63 -264q27 104 59.5 257t45.5 245h182l15 -23q-26 -100 -75 -232.5t-102 -246.5h-125z" />
|
||||
<glyph unicode="-" horiz-adv-x="659" d="M84 473v152h491v-152h-491z" />
|
||||
<glyph unicode="." horiz-adv-x="545" d="M152 106q0 67 30.5 101.5t87.5 34.5q58 0 90.5 -34.5t32.5 -101.5q0 -65 -33 -100t-90 -35q-51 0 -84.5 31.5t-33.5 103.5z" />
|
||||
<glyph unicode="/" horiz-adv-x="752" d="M20 0l545 1462h166l-545 -1462h-166z" />
|
||||
<glyph unicode="0" d="M102 733q0 382 119 567t363 185q238 0 361.5 -193t123.5 -559q0 -379 -119.5 -566t-365.5 -187q-236 0 -359 191.5t-123 561.5zM270 733q0 -319 75 -464.5t239 -145.5q166 0 240.5 147.5t74.5 462.5t-74.5 461.5t-240.5 146.5q-164 0 -239 -144.5t-75 -463.5z" />
|
||||
<glyph unicode="1" d="M188 1163l387 299h140v-1462h-162v1042q0 130 8 246q-21 -21 -47 -44t-238 -195z" />
|
||||
<glyph unicode="2" d="M100 0v143l385 387q176 178 232 254t84 148t28 155q0 117 -71 185.5t-197 68.5q-91 0 -172.5 -30t-181.5 -109l-88 113q202 168 440 168q206 0 323 -105.5t117 -283.5q0 -139 -78 -275t-292 -344l-320 -313v-8h752v-154h-961z" />
|
||||
<glyph unicode="3" d="M94 59v158q95 -47 202.5 -71.5t203.5 -24.5q379 0 379 297q0 266 -418 266h-144v143h146q171 0 271 75.5t100 209.5q0 107 -73.5 168t-199.5 61q-96 0 -181 -26t-194 -96l-84 112q90 71 207.5 111.5t247.5 40.5q213 0 331 -97.5t118 -267.5q0 -140 -78.5 -229 t-222.5 -119v-8q176 -22 261 -112t85 -236q0 -209 -145 -321.5t-412 -112.5q-116 0 -212.5 17.5t-187.5 61.5z" />
|
||||
<glyph unicode="4" d="M43 336v145l694 989h176v-983h217v-151h-217v-336h-159v336h-711zM209 487h545v486q0 143 10 323h-8q-48 -96 -90 -159z" />
|
||||
<glyph unicode="5" d="M133 59v160q70 -45 174 -70.5t205 -25.5q176 0 273.5 83t97.5 240q0 306 -375 306q-95 0 -254 -29l-86 55l55 684h727v-153h-585l-37 -439q115 23 229 23q231 0 363.5 -114.5t132.5 -313.5q0 -227 -144.5 -356t-398.5 -129q-247 0 -377 79z" />
|
||||
<glyph unicode="6" d="M117 625q0 431 167.5 644.5t495.5 213.5q113 0 178 -19v-143q-77 25 -176 25q-235 0 -359 -146.5t-136 -460.5h12q110 172 348 172q197 0 310.5 -119t113.5 -323q0 -228 -124.5 -358.5t-336.5 -130.5q-227 0 -360 170.5t-133 474.5zM287 506q0 -103 40 -192t113.5 -141 t167.5 -52q142 0 220.5 89.5t78.5 258.5q0 145 -73 228t-218 83q-90 0 -165 -37t-119.5 -102t-44.5 -135z" />
|
||||
<glyph unicode="7" d="M94 1309v153h973v-133l-598 -1329h-184l606 1309h-797z" />
|
||||
<glyph unicode="8" d="M104 373q0 251 306 391q-138 78 -198 168.5t-60 202.5q0 159 117.5 253.5t314.5 94.5q200 0 317 -93t117 -257q0 -108 -67 -197t-214 -162q178 -85 253 -178.5t75 -216.5q0 -182 -127 -290.5t-348 -108.5q-234 0 -360 102.5t-126 290.5zM268 369q0 -120 83.5 -187 t234.5 -67q149 0 232 70t83 192q0 97 -78 172.5t-272 146.5q-149 -64 -216 -141.5t-67 -185.5zM315 1128q0 -92 59 -158t218 -132q143 60 202.5 129t59.5 161q0 101 -72.5 160.5t-199.5 59.5q-125 0 -196 -60t-71 -160z" />
|
||||
<glyph unicode="9" d="M106 991q0 228 127.5 360t335.5 132q149 0 260.5 -76.5t171.5 -223t60 -345.5q0 -858 -664 -858q-116 0 -184 20v143q80 -26 182 -26q240 0 362.5 148.5t133.5 455.5h-12q-55 -83 -146 -126.5t-205 -43.5q-194 0 -308 116t-114 324zM270 993q0 -144 72 -226.5t219 -82.5 q91 0 167.5 37t120.5 101t44 134q0 105 -41 194t-114.5 140t-168.5 51q-143 0 -221 -92t-78 -256z" />
|
||||
<glyph unicode=":" horiz-adv-x="545" d="M152 106q0 67 30.5 101.5t87.5 34.5q58 0 90.5 -34.5t32.5 -101.5q0 -65 -33 -100t-90 -35q-51 0 -84.5 31.5t-33.5 103.5zM152 989q0 135 118 135q123 0 123 -135q0 -65 -33 -100t-90 -35q-51 0 -84.5 31.5t-33.5 103.5z" />
|
||||
<glyph unicode=";" horiz-adv-x="545" d="M63 -264q27 104 59.5 257t45.5 245h182l15 -23q-26 -100 -75 -232.5t-102 -246.5h-125zM147 989q0 135 119 135q123 0 123 -135q0 -65 -33 -100t-90 -35q-58 0 -88.5 35t-30.5 100z" />
|
||||
<glyph unicode="<" d="M104 664v98l961 479v-149l-782 -371l782 -328v-151z" />
|
||||
<glyph unicode="=" d="M119 449v137h930v-137h-930zM119 858v137h930v-137h-930z" />
|
||||
<glyph unicode=">" d="M104 242v151l783 326l-783 373v149l961 -479v-98z" />
|
||||
<glyph unicode="?" horiz-adv-x="879" d="M27 1384q189 99 395 99q191 0 297 -94t106 -265q0 -73 -19.5 -128.5t-57.5 -105t-164 -159.5q-101 -86 -133.5 -143t-32.5 -152v-33h-129v54q0 117 36 192.5t134 159.5q136 115 171.5 173t35.5 140q0 102 -65.5 157.5t-188.5 55.5q-79 0 -154 -18.5t-172 -67.5zM240 106 q0 136 120 136q58 0 89.5 -35t31.5 -101q0 -64 -32 -99.5t-89 -35.5q-52 0 -86 31.5t-34 103.5z" />
|
||||
<glyph unicode="@" horiz-adv-x="1841" d="M121 571q0 260 107 463t305 314.5t454 111.5q215 0 382.5 -90.5t259 -257t91.5 -383.5q0 -142 -44 -260t-124 -183t-184 -65q-86 0 -145 52t-70 133h-8q-40 -87 -114.5 -136t-176.5 -49q-150 0 -234.5 102.5t-84.5 278.5q0 204 118 331.5t310 127.5q68 0 154 -12.5 t155 -34.5l-25 -470v-22q0 -178 133 -178q91 0 148 107.5t57 279.5q0 181 -74 317t-210.5 209.5t-313.5 73.5q-223 0 -388 -92.5t-252 -264t-87 -396.5q0 -305 161 -469t464 -164q210 0 436 86v-133q-192 -84 -436 -84q-363 0 -563.5 199.5t-200.5 557.5zM686 598 q0 -254 195 -254q207 0 225 313l14 261q-72 20 -157 20q-130 0 -203.5 -90t-73.5 -250z" />
|
||||
<glyph unicode="A" horiz-adv-x="1296" d="M0 0l578 1468h143l575 -1468h-176l-182 465h-586l-180 -465h-172zM412 618h473l-170 453q-33 86 -68 211q-22 -96 -63 -211z" />
|
||||
<glyph unicode="B" horiz-adv-x="1327" d="M201 0v1462h413q291 0 421 -87t130 -275q0 -130 -72.5 -214.5t-211.5 -109.5v-10q333 -57 333 -350q0 -196 -132.5 -306t-370.5 -110h-510zM371 145h305q177 0 266.5 68.5t89.5 214.5q0 136 -91.5 200t-278.5 64h-291v-547zM371 836h280q180 0 259 56.5t79 190.5 q0 123 -88 177.5t-280 54.5h-250v-479z" />
|
||||
<glyph unicode="C" horiz-adv-x="1292" d="M125 733q0 226 84.5 396t244 262t375.5 92q230 0 402 -84l-72 -146q-166 78 -332 78q-241 0 -380.5 -160.5t-139.5 -439.5q0 -287 134.5 -443.5t383.5 -156.5q153 0 349 55v-149q-152 -57 -375 -57q-323 0 -498.5 196t-175.5 557z" />
|
||||
<glyph unicode="D" horiz-adv-x="1493" d="M201 0v1462h448q341 0 530 -189t189 -528q0 -362 -196.5 -553.5t-565.5 -191.5h-405zM371 147h207q304 0 457 149.5t153 442.5q0 286 -143.5 431t-426.5 145h-247v-1168z" />
|
||||
<glyph unicode="E" horiz-adv-x="1139" d="M201 0v1462h815v-151h-645v-471h606v-150h-606v-538h645v-152h-815z" />
|
||||
<glyph unicode="F" horiz-adv-x="1057" d="M201 0v1462h815v-151h-645v-535h606v-151h-606v-625h-170z" />
|
||||
<glyph unicode="G" horiz-adv-x="1491" d="M125 731q0 228 91.5 399.5t263.5 262t403 90.5q234 0 436 -86l-66 -150q-198 84 -381 84q-267 0 -417 -159t-150 -441q0 -296 144.5 -449t424.5 -153q152 0 297 35v450h-327v152h497v-711q-116 -37 -236 -56t-278 -19q-332 0 -517 197.5t-185 553.5z" />
|
||||
<glyph unicode="H" horiz-adv-x="1511" d="M201 0v1462h170v-622h770v622h170v-1462h-170v688h-770v-688h-170z" />
|
||||
<glyph unicode="I" horiz-adv-x="571" d="M201 0v1462h170v-1462h-170z" />
|
||||
<glyph unicode="J" horiz-adv-x="547" d="M-160 -213q71 -20 148 -20q99 0 150.5 60t51.5 173v1462h170v-1448q0 -190 -96 -294.5t-276 -104.5q-94 0 -148 27v145z" />
|
||||
<glyph unicode="K" horiz-adv-x="1257" d="M201 0v1462h170v-725l663 725h201l-588 -635l610 -827h-200l-533 709l-153 -136v-573h-170z" />
|
||||
<glyph unicode="L" horiz-adv-x="1063" d="M201 0v1462h170v-1308h645v-154h-815z" />
|
||||
<glyph unicode="M" horiz-adv-x="1849" d="M201 0v1462h256l463 -1206h8l467 1206h254v-1462h-170v942q0 162 14 352h-8l-500 -1294h-137l-496 1296h-8q14 -154 14 -366v-930h-157z" />
|
||||
<glyph unicode="N" horiz-adv-x="1544" d="M201 0v1462h192l797 -1222h8q-2 28 -9 174q-5 114 -5 177v32v839h159v-1462h-194l-799 1227h-8q16 -216 16 -396v-831h-157z" />
|
||||
<glyph unicode="O" horiz-adv-x="1595" d="M125 735q0 357 176 553.5t500 196.5q315 0 492 -200t177 -552q0 -351 -177.5 -552t-493.5 -201q-323 0 -498.5 197.5t-175.5 557.5zM305 733q0 -297 126.5 -450.5t367.5 -153.5q243 0 367 153t124 451q0 295 -123.5 447.5t-365.5 152.5q-243 0 -369.5 -153.5 t-126.5 -446.5z" />
|
||||
<glyph unicode="P" horiz-adv-x="1233" d="M201 0v1462h379q548 0 548 -426q0 -222 -151.5 -341.5t-433.5 -119.5h-172v-575h-170zM371 721h153q226 0 327 73t101 234q0 145 -95 216t-296 71h-190v-594z" />
|
||||
<glyph unicode="Q" horiz-adv-x="1595" d="M125 735q0 357 176 553.5t500 196.5q315 0 492 -200t177 -552q0 -281 -113 -467t-319 -252l348 -362h-247l-285 330l-55 -2q-323 0 -498.5 197.5t-175.5 557.5zM305 733q0 -297 126.5 -450.5t367.5 -153.5q243 0 367 153t124 451q0 295 -123.5 447.5t-365.5 152.5 q-243 0 -369.5 -153.5t-126.5 -446.5z" />
|
||||
<glyph unicode="R" horiz-adv-x="1266" d="M201 0v1462h401q269 0 397.5 -103t128.5 -310q0 -290 -294 -392l397 -657h-201l-354 608h-305v-608h-170zM371 754h233q180 0 264 71.5t84 214.5q0 145 -85.5 209t-274.5 64h-221v-559z" />
|
||||
<glyph unicode="S" horiz-adv-x="1124" d="M106 47v164q90 -38 196 -60t210 -22q170 0 256 64.5t86 179.5q0 76 -30.5 124.5t-102 89.5t-217.5 93q-204 73 -291.5 173t-87.5 261q0 169 127 269t336 100q218 0 401 -80l-53 -148q-181 76 -352 76q-135 0 -211 -58t-76 -161q0 -76 28 -124.5t94.5 -89t203.5 -89.5 q230 -82 316.5 -176t86.5 -244q0 -193 -140 -301t-380 -108q-260 0 -400 67z" />
|
||||
<glyph unicode="T" horiz-adv-x="1133" d="M18 1311v151h1096v-151h-463v-1311h-170v1311h-463z" />
|
||||
<glyph unicode="U" horiz-adv-x="1491" d="M186 520v942h170v-954q0 -183 100 -281t294 -98q185 0 285 98.5t100 282.5v952h170v-946q0 -250 -151 -393t-415 -143t-408.5 144t-144.5 396z" />
|
||||
<glyph unicode="V" horiz-adv-x="1219" d="M0 1462h180l336 -946q58 -163 92 -317q36 162 94 323l334 940h183l-527 -1462h-168z" />
|
||||
<glyph unicode="W" horiz-adv-x="1896" d="M27 1462h180l231 -903q48 -190 70 -344q27 183 80 358l262 889h180l275 -897q48 -155 81 -350q19 142 72 346l230 901h180l-391 -1462h-168l-295 979q-21 65 -47 164t-27 119q-22 -132 -70 -289l-286 -973h-168z" />
|
||||
<glyph unicode="X" horiz-adv-x="1182" d="M8 0l486 764l-453 698h188l363 -579l366 579h181l-453 -692l488 -770h-193l-393 643l-400 -643h-180z" />
|
||||
<glyph unicode="Y" horiz-adv-x="1147" d="M0 1462h186l387 -731l390 731h184l-488 -895v-567h-172v559z" />
|
||||
<glyph unicode="Z" horiz-adv-x="1169" d="M82 0v133l776 1176h-752v153h959v-133l-776 -1175h798v-154h-1005z" />
|
||||
<glyph unicode="[" horiz-adv-x="674" d="M166 -324v1786h457v-141h-289v-1503h289v-142h-457z" />
|
||||
<glyph unicode="\" horiz-adv-x="752" d="M23 1462h163l547 -1462h-166z" />
|
||||
<glyph unicode="]" horiz-adv-x="674" d="M51 -182h289v1503h-289v141h457v-1786h-457v142z" />
|
||||
<glyph unicode="^" horiz-adv-x="1110" d="M49 551l434 922h99l477 -922h-152l-372 745l-334 -745h-152z" />
|
||||
<glyph unicode="_" horiz-adv-x="918" d="M-4 -184h926v-131h-926v131z" />
|
||||
<glyph unicode="`" horiz-adv-x="1182" d="M393 1548v21h203q32 -69 89 -159.5t101 -143.5v-25h-110q-65 52 -154 148t-129 159z" />
|
||||
<glyph unicode="a" horiz-adv-x="1139" d="M94 303q0 332 531 348l186 6v68q0 129 -55.5 190.5t-177.5 61.5q-137 0 -310 -84l-51 127q81 44 177.5 69t193.5 25q196 0 290.5 -87t94.5 -279v-748h-123l-33 156h-8q-82 -103 -163.5 -139.5t-203.5 -36.5q-163 0 -255.5 84t-92.5 239zM268 301q0 -90 54.5 -137 t152.5 -47q155 0 243.5 85t88.5 238v99l-166 -7q-198 -7 -285.5 -61.5t-87.5 -169.5z" />
|
||||
<glyph unicode="b" horiz-adv-x="1255" d="M176 0v1556h166v-378q0 -127 -8 -228h8q116 164 344 164q216 0 335.5 -147.5t119.5 -417.5t-120.5 -419.5t-334.5 -149.5q-107 0 -195.5 39.5t-148.5 121.5h-12l-35 -141h-119zM342 549q0 -231 77 -330.5t247 -99.5q153 0 228 111.5t75 320.5q0 214 -75 319t-232 105 q-170 0 -245 -97.5t-75 -328.5z" />
|
||||
<glyph unicode="c" horiz-adv-x="975" d="M115 541q0 275 132.5 425t377.5 150q79 0 158 -17t124 -40l-51 -141q-55 22 -120 36.5t-115 14.5q-334 0 -334 -426q0 -202 81.5 -310t241.5 -108q137 0 281 59v-147q-110 -57 -277 -57q-238 0 -368.5 146.5t-130.5 414.5z" />
|
||||
<glyph unicode="d" horiz-adv-x="1255" d="M115 545q0 271 120 421t334 150q223 0 342 -162h13l-7 79l-4 77v446h166v-1556h-135l-22 147h-9q-115 -167 -344 -167q-215 0 -334.5 147t-119.5 418zM287 543q0 -210 77 -317t226 -107q170 0 246.5 92.5t76.5 298.5v35q0 233 -77.5 332.5t-247.5 99.5 q-146 0 -223.5 -113.5t-77.5 -320.5z" />
|
||||
<glyph unicode="e" horiz-adv-x="1149" d="M115 539q0 265 130.5 421t350.5 156q206 0 326 -135.5t120 -357.5v-105h-755q5 -193 97.5 -293t260.5 -100q177 0 350 74v-148q-88 -38 -166.5 -54.5t-189.5 -16.5q-243 0 -383.5 148t-140.5 411zM291 653h573q0 157 -70 240.5t-200 83.5q-132 0 -210.5 -86t-92.5 -238z " />
|
||||
<glyph unicode="f" horiz-adv-x="694" d="M29 967v75l196 60v61q0 404 353 404q87 0 204 -35l-43 -133q-96 31 -164 31q-94 0 -139 -62.5t-45 -200.5v-71h279v-129h-279v-967h-166v967h-196z" />
|
||||
<glyph unicode="g" horiz-adv-x="1122" d="M39 -186q0 100 64 173t180 99q-42 19 -70.5 59t-28.5 93q0 60 32 105t101 87q-85 35 -138.5 119t-53.5 192q0 180 108 277.5t306 97.5q86 0 155 -20h379v-105l-203 -24q28 -35 50 -91.5t22 -127.5q0 -161 -110 -257t-302 -96q-49 0 -92 8q-106 -56 -106 -141 q0 -45 37 -66.5t127 -21.5h194q178 0 273.5 -75t95.5 -218q0 -182 -146 -277.5t-426 -95.5q-215 0 -331.5 80t-116.5 226zM199 -184q0 -89 75 -135t215 -46q209 0 309.5 62.5t100.5 169.5q0 89 -55 123.5t-207 34.5h-199q-113 0 -176 -54t-63 -155zM289 745q0 -115 65 -174 t181 -59q243 0 243 236q0 247 -246 247q-117 0 -180 -63t-63 -187z" />
|
||||
<glyph unicode="h" horiz-adv-x="1257" d="M176 0v1556h166v-471q0 -85 -8 -141h10q49 79 139.5 124.5t206.5 45.5q201 0 301.5 -95.5t100.5 -303.5v-715h-166v709q0 134 -61 200t-191 66q-173 0 -252.5 -94t-79.5 -308v-573h-166z" />
|
||||
<glyph unicode="i" horiz-adv-x="518" d="M162 1393q0 57 28 83.5t70 26.5q40 0 69 -27t29 -83t-29 -83.5t-69 -27.5q-42 0 -70 27.5t-28 83.5zM176 0v1096h166v-1096h-166z" />
|
||||
<glyph unicode="j" horiz-adv-x="518" d="M-111 -332q69 -20 136 -20q78 0 114.5 42.5t36.5 129.5v1276h166v-1264q0 -324 -299 -324q-95 0 -154 25v135zM162 1393q0 57 28 83.5t70 26.5q40 0 69 -27t29 -83t-29 -83.5t-69 -27.5q-42 0 -70 27.5t-28 83.5z" />
|
||||
<glyph unicode="k" horiz-adv-x="1075" d="M176 0v1556h164v-825q0 -55 -8 -170h8q43 61 131 160l354 375h197l-444 -467l475 -629h-201l-387 518l-125 -108v-410h-164z" />
|
||||
<glyph unicode="l" horiz-adv-x="518" d="M176 0v1556h166v-1556h-166z" />
|
||||
<glyph unicode="m" horiz-adv-x="1905" d="M176 0v1096h135l27 -150h8q47 80 132.5 125t191.5 45q257 0 336 -186h8q49 86 142 136t212 50q186 0 278.5 -95.5t92.5 -305.5v-715h-166v713q0 131 -56 196.5t-174 65.5q-155 0 -229 -89t-74 -274v-612h-166v713q0 131 -56 196.5t-175 65.5q-156 0 -228.5 -93.5 t-72.5 -306.5v-575h-166z" />
|
||||
<glyph unicode="n" horiz-adv-x="1257" d="M176 0v1096h135l27 -150h8q51 81 143 125.5t205 44.5q198 0 298 -95.5t100 -305.5v-715h-166v709q0 134 -61 200t-191 66q-172 0 -252 -93t-80 -307v-575h-166z" />
|
||||
<glyph unicode="o" horiz-adv-x="1237" d="M115 549q0 268 134 417.5t372 149.5q230 0 365.5 -153t135.5 -414q0 -268 -135 -418.5t-373 -150.5q-147 0 -261 69t-176 198t-62 302zM287 549q0 -210 84 -320t247 -110t247.5 109.5t84.5 320.5q0 209 -84.5 317.5t-249.5 108.5q-163 0 -246 -107t-83 -319z" />
|
||||
<glyph unicode="p" horiz-adv-x="1255" d="M176 -492v1588h135l23 -150h8q64 90 149 130t195 40q218 0 336.5 -149t118.5 -418q0 -270 -120.5 -419.5t-334.5 -149.5q-107 0 -195.5 39.5t-148.5 121.5h-12q12 -96 12 -182v-451h-166zM342 549q0 -231 77 -330.5t247 -99.5q142 0 222.5 115t80.5 317 q0 205 -80.5 314.5t-226.5 109.5q-168 0 -243 -93t-77 -296v-37z" />
|
||||
<glyph unicode="q" horiz-adv-x="1255" d="M115 545q0 269 120 420t334 151q225 0 346 -170h9l24 150h131v-1588h-166v469q0 100 11 170h-13q-115 -167 -346 -167q-212 0 -331 149t-119 416zM287 543q0 -207 76.5 -315.5t226.5 -108.5q166 0 242 89t81 300v37q0 230 -78 331t-247 101q-146 0 -223.5 -113.5 t-77.5 -320.5z" />
|
||||
<glyph unicode="r" horiz-adv-x="836" d="M176 0v1096h137l19 -203h8q61 107 147 165t189 58q73 0 131 -12l-23 -154q-68 15 -120 15q-133 0 -227.5 -108t-94.5 -269v-588h-166z" />
|
||||
<glyph unicode="s" horiz-adv-x="977" d="M106 827q0 134 109 211.5t299 77.5q177 0 346 -72l-59 -135q-165 68 -299 68q-118 0 -178 -37t-60 -102q0 -44 22.5 -75t72.5 -59t192 -81q195 -71 263.5 -143t68.5 -181q0 -153 -114 -236t-320 -83q-218 0 -340 69v154q79 -40 169.5 -63t174.5 -23q130 0 200 41.5 t70 126.5q0 64 -55.5 109.5t-216.5 107.5q-153 57 -217.5 99.5t-96 96.5t-31.5 129z" />
|
||||
<glyph unicode="t" horiz-adv-x="723" d="M31 967v80l157 69l70 234h96v-254h318v-129h-318v-645q0 -99 47 -152t129 -53q44 0 85 6.5t65 13.5v-127q-27 -13 -79.5 -21.5t-94.5 -8.5q-318 0 -318 335v652h-157z" />
|
||||
<glyph unicode="u" horiz-adv-x="1257" d="M164 379v717h168v-711q0 -134 61 -200t191 -66q172 0 251.5 94t79.5 307v576h166v-1096h-137l-24 147h-9q-51 -81 -141.5 -124t-206.5 -43q-200 0 -299.5 95t-99.5 304z" />
|
||||
<glyph unicode="v" horiz-adv-x="1026" d="M0 1096h178l236 -650q80 -228 94 -296h8q11 53 69.5 219.5t262.5 726.5h178l-416 -1096h-194z" />
|
||||
<glyph unicode="w" horiz-adv-x="1593" d="M23 1096h174q106 -413 161.5 -629t63.5 -291h8q11 57 35.5 147.5t42.5 143.5l201 629h180l196 -629q56 -172 76 -289h8q4 36 21.5 111t208.5 807h172l-303 -1096h-197l-201 643q-19 59 -71 268h-8q-40 -175 -70 -270l-207 -641h-192z" />
|
||||
<glyph unicode="x" horiz-adv-x="1073" d="M39 0l401 561l-381 535h189l289 -420l288 420h187l-381 -535l401 -561h-188l-307 444l-310 -444h-188z" />
|
||||
<glyph unicode="y" horiz-adv-x="1032" d="M2 1096h178l240 -625q79 -214 98 -309h8q13 51 54.5 174.5t271.5 759.5h178l-471 -1248q-70 -185 -163.5 -262.5t-229.5 -77.5q-76 0 -150 17v133q55 -12 123 -12q171 0 244 192l61 156z" />
|
||||
<glyph unicode="z" horiz-adv-x="958" d="M82 0v113l598 854h-561v129h743v-129l-590 -838h605v-129h-795z" />
|
||||
<glyph unicode="{" horiz-adv-x="776" d="M61 498v141q130 2 188 48t58 142v306q0 155 108 241t290 86v-139q-230 -6 -230 -199v-295q0 -215 -223 -254v-12q223 -39 223 -254v-297q0 -102 58.5 -148t171.5 -48v-140q-190 2 -294 87t-104 239v303q0 104 -63 148.5t-183 44.5z" />
|
||||
<glyph unicode="|" horiz-adv-x="1128" d="M494 -496v2052h141v-2052h-141z" />
|
||||
<glyph unicode="}" horiz-adv-x="776" d="M72 -184q111 2 169 48t58 148v297q0 114 55 174t168 80v12q-223 39 -223 254v295q0 193 -227 199v139q184 0 289.5 -87t105.5 -240v-306q0 -97 59 -142.5t189 -47.5v-141q-122 0 -185 -44.5t-63 -148.5v-303q0 -153 -102.5 -238.5t-292.5 -87.5v140z" />
|
||||
<glyph unicode="~" d="M104 592v151q100 109 244 109q68 0 124.5 -14t145.5 -52q66 -28 115 -41.5t96 -13.5q54 0 118 32t118 89v-150q-102 -110 -244 -110q-72 0 -135 16.5t-135 48.5q-75 32 -120 44t-93 12q-53 0 -116.5 -33.5t-117.5 -87.5z" />
|
||||
<glyph unicode="¢" d="M190 741q0 508 396 570v172h135v-164q75 -3 146 -19.5t120 -39.5l-49 -140q-133 51 -242 51q-172 0 -253 -105.5t-81 -322.5q0 -212 79.5 -313.5t246.5 -101.5q141 0 283 59v-147q-105 -54 -252 -60v-200h-133v206q-203 32 -299.5 168.5t-96.5 386.5z" />
|
||||
<glyph unicode="£" d="M63 0v141q205 47 205 291v223h-198v127h198v316q0 178 112 280.5t302 102.5t360 -84l-61 -133q-154 77 -297 77q-123 0 -185.5 -62t-62.5 -202v-295h422v-127h-422v-221q0 -100 -32.5 -168t-106.5 -112h795v-154h-1029z" />
|
||||
<glyph unicode="¥" d="M31 1462h178l375 -727l379 727h174l-416 -770h262v-127h-317v-170h317v-127h-317v-268h-164v268h-316v127h316v170h-316v127h256z" />
|
||||
<glyph unicode="©" horiz-adv-x="1704" d="M100 731q0 200 100 375t275 276t377 101q200 0 375 -100t276 -275t101 -377q0 -197 -97 -370t-272 -277t-383 -104q-207 0 -382 103.5t-272.5 276.5t-97.5 371zM205 731q0 -173 87 -323.5t237.5 -237t322.5 -86.5q174 0 323 87t236.5 235.5t87.5 324.5q0 174 -87 323 t-235.5 236.5t-324.5 87.5q-174 0 -323 -87t-236.5 -235.5t-87.5 -324.5zM481 731q0 209 110.5 332t301.5 123q128 0 246 -60l-58 -118q-108 51 -188 51q-125 0 -192.5 -87t-67.5 -241q0 -168 63.5 -249t194.5 -81q86 0 211 45v-124q-48 -20 -98.5 -34t-120.5 -14 q-194 0 -298 120.5t-104 336.5z" />
|
||||
<glyph unicode="­" horiz-adv-x="659" d="M84 473v152h491v-152h-491z" />
|
||||
<glyph unicode="®" horiz-adv-x="1704" d="M100 731q0 200 100 375t275 276t377 101q200 0 375 -100t276 -275t101 -377q0 -197 -97 -370t-272 -277t-383 -104q-207 0 -382 103.5t-272.5 276.5t-97.5 371zM205 731q0 -173 87 -323.5t237.5 -237t322.5 -86.5q174 0 323 87t236.5 235.5t87.5 324.5q0 174 -87 323 t-235.5 236.5t-324.5 87.5q-174 0 -323 -87t-236.5 -235.5t-87.5 -324.5zM575 285v891h261q166 0 243.5 -65t77.5 -198q0 -80 -42.5 -141.5t-119.5 -91.5l238 -395h-168l-207 354h-135v-354h-148zM723 762h108q80 0 128.5 41.5t48.5 105.5q0 75 -43 107.5t-136 32.5h-106 v-287z" />
|
||||
<glyph unicode="´" horiz-adv-x="1182" d="M393 1241v25q48 62 103.5 150t87.5 153h202v-21q-44 -65 -131 -160t-151 -147h-111z" />
|
||||
<glyph unicode=" " horiz-adv-x="784" />
|
||||
<glyph unicode=" " horiz-adv-x="1569" />
|
||||
<glyph unicode=" " horiz-adv-x="784" />
|
||||
<glyph unicode=" " horiz-adv-x="1569" />
|
||||
<glyph unicode=" " horiz-adv-x="523" />
|
||||
<glyph unicode=" " horiz-adv-x="392" />
|
||||
<glyph unicode=" " horiz-adv-x="261" />
|
||||
<glyph unicode=" " horiz-adv-x="261" />
|
||||
<glyph unicode=" " horiz-adv-x="196" />
|
||||
<glyph unicode=" " horiz-adv-x="313" />
|
||||
<glyph unicode=" " horiz-adv-x="87" />
|
||||
<glyph unicode="‐" horiz-adv-x="659" d="M84 473v152h491v-152h-491z" />
|
||||
<glyph unicode="‑" horiz-adv-x="659" d="M84 473v152h491v-152h-491z" />
|
||||
<glyph unicode="‒" horiz-adv-x="659" d="M84 473v152h491v-152h-491z" />
|
||||
<glyph unicode="–" horiz-adv-x="1024" d="M82 473v152h860v-152h-860z" />
|
||||
<glyph unicode="—" horiz-adv-x="2048" d="M82 473v152h1884v-152h-1884z" />
|
||||
<glyph unicode="‘" horiz-adv-x="348" d="M25 983q22 90 71 224t105 255h123q-66 -254 -103 -501h-184z" />
|
||||
<glyph unicode="’" horiz-adv-x="348" d="M25 961q70 285 102 501h182l15 -22q-26 -100 -75 -232.5t-102 -246.5h-122z" />
|
||||
<glyph unicode="“" horiz-adv-x="717" d="M25 983q22 90 71 224t105 255h123q-66 -254 -103 -501h-184zM391 983q56 215 178 479h123q-30 -115 -59.5 -259.5t-42.5 -241.5h-184z" />
|
||||
<glyph unicode="”" horiz-adv-x="717" d="M25 961q70 285 102 501h182l15 -22q-26 -100 -75 -232.5t-102 -246.5h-122zM391 961q26 100 59 254t46 247h182l14 -22q-24 -91 -72 -224t-104 -255h-125z" />
|
||||
<glyph unicode="•" horiz-adv-x="770" d="M164 748q0 121 56.5 184t164.5 63q105 0 163 -62t58 -185q0 -119 -57.5 -183.5t-163.5 -64.5q-107 0 -164 65.5t-57 182.5z" />
|
||||
<glyph unicode="…" horiz-adv-x="1606" d="M152 106q0 67 30.5 101.5t87.5 34.5q58 0 90.5 -34.5t32.5 -101.5q0 -65 -33 -100t-90 -35q-51 0 -84.5 31.5t-33.5 103.5zM682 106q0 67 30.5 101.5t87.5 34.5q58 0 90.5 -34.5t32.5 -101.5q0 -65 -33 -100t-90 -35q-51 0 -84.5 31.5t-33.5 103.5zM1213 106 q0 67 30.5 101.5t87.5 34.5q58 0 90.5 -34.5t32.5 -101.5q0 -65 -33 -100t-90 -35q-51 0 -84.5 31.5t-33.5 103.5z" />
|
||||
<glyph unicode=" " horiz-adv-x="313" />
|
||||
<glyph unicode=" " horiz-adv-x="392" />
|
||||
<glyph unicode="€" horiz-adv-x="1208" d="M63 506v129h152l-2 42v44l2 80h-152v129h164q39 261 185 407t383 146q201 0 366 -97l-71 -139q-166 86 -295 86q-319 0 -398 -403h510v-129h-524l-2 -57v-64l2 -45h463v-129h-447q37 -180 138.5 -278.5t271.5 -98.5q156 0 309 66v-150q-146 -65 -317 -65 q-237 0 -381.5 134.5t-190.5 391.5h-166z" />
|
||||
<glyph unicode="™" horiz-adv-x="1589" d="M37 1356v106h543v-106h-211v-615h-123v615h-209zM647 741v721h187l196 -559l203 559h180v-721h-127v420l6 137h-8l-211 -557h-104l-201 559h-8l6 -129v-430h-119z" />
|
||||
<glyph unicode="" horiz-adv-x="1095" d="M0 1095h1095v-1095h-1095v1095z" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 25 KiB |
BIN
couchpotato/static/fonts/OpenSans-Regular-webfont.ttf
Executable file
BIN
couchpotato/static/fonts/OpenSans-Regular-webfont.woff
Executable file
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -15,7 +15,7 @@ Block.Menu = new Class({
|
||||
self.wrapper = new Element('div.wrapper').adopt(
|
||||
self.more_option_ul = new Element('ul')
|
||||
),
|
||||
new Element('a.button.onlay', {
|
||||
new Element('a.button.onlay' + (self.options.button_class ? '.' + self.options.button_class : ''), {
|
||||
'events': {
|
||||
'click': function(){
|
||||
self.el.toggleClass('show')
|
||||
|
||||
@@ -5,7 +5,17 @@ Block.Navigation = new Class({
|
||||
create: function(){
|
||||
var self = this;
|
||||
|
||||
var settings_added = false;
|
||||
self.el = new Element('div.navigation').adopt(
|
||||
self.foldout = new Element('a.foldout.icon2.menu', {
|
||||
'events': {
|
||||
'click': self.toggleMenu.bind(self)
|
||||
}
|
||||
}).grab(new Element('span.overlay')),
|
||||
self.logo = new Element('a.logo', {
|
||||
'text': 'CouchPotato',
|
||||
'href': App.createUrl('')
|
||||
}),
|
||||
self.nav = new Element('ul'),
|
||||
self.backtotop = new Element('a.backtotop', {
|
||||
'text': 'back to top',
|
||||
@@ -28,19 +38,50 @@ Block.Navigation = new Class({
|
||||
onEnter: function(){
|
||||
self.backtotop.fade('in')
|
||||
}
|
||||
});
|
||||
|
||||
self.nav.addEvents({
|
||||
'click:relay(a)': function(){
|
||||
if($(document.body).getParent().hasClass('menu_shown'))
|
||||
self.toggleMenu();
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
addTab: function(name, tab){
|
||||
var self = this
|
||||
var self = this;
|
||||
|
||||
return new Element('li.tab_'+(name || 'unknown')).adopt(
|
||||
return new Element('li.tab_'+(name || 'unknown')).grab(
|
||||
new Element('a', tab)
|
||||
).inject(self.nav)
|
||||
|
||||
},
|
||||
|
||||
toggleMenu: function(e){
|
||||
var self = this,
|
||||
body = $(document.body),
|
||||
html = body.getParent();
|
||||
|
||||
// Copy over settings menu
|
||||
if(!self.added){
|
||||
|
||||
new Element('li.separator').inject(self.nav);
|
||||
body.getElements('.header .more_menu.menu li a').each(function(el, nr){
|
||||
if([0, 1, 2, 5].indexOf(nr) > -1){
|
||||
self.nav.grab(
|
||||
new Element('li').grab(el.clone().cloneEvents(el))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
self.added = true;
|
||||
}
|
||||
|
||||
html.toggleClass('menu_shown');
|
||||
|
||||
},
|
||||
|
||||
activate: function(name){
|
||||
var self = this;
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ var CouchPotato = new Class({
|
||||
new Element('div').adopt(
|
||||
self.block.navigation = new Block.Navigation(self, {}),
|
||||
self.block.search = new Block.Search(self, {}),
|
||||
self.block.more = new Block.Menu(self, {})
|
||||
self.block.more = new Block.Menu(self, {'button_class': 'icon2.cog'})
|
||||
)
|
||||
),
|
||||
self.content = new Element('div.content'),
|
||||
@@ -285,23 +285,15 @@ var CouchPotato = new Class({
|
||||
|
||||
createUserscriptButtons: function(){
|
||||
|
||||
var userscript = false;
|
||||
try {
|
||||
if(Components.interfaces.gmIGreasemonkeyService)
|
||||
userscript = true
|
||||
}
|
||||
catch(e){
|
||||
userscript = Browser.chrome === true;
|
||||
}
|
||||
|
||||
var host_url = window.location.protocol + '//' + window.location.host;
|
||||
|
||||
return new Element('div.group_userscript').adopt(
|
||||
(userscript ? [new Element('a.userscript.button', {
|
||||
new Element('a.userscript.button', {
|
||||
'text': 'Install userscript',
|
||||
'href': Api.createUrl('userscript.get')+randomString()+'/couchpotato.user.js',
|
||||
'target': '_self'
|
||||
}), new Element('span.or[text=or]')] : null),
|
||||
'target': '_blank'
|
||||
}),
|
||||
new Element('span.or[text=or]'),
|
||||
new Element('span.bookmarklet').adopt(
|
||||
new Element('a.button.orange', {
|
||||
'text': '+CouchPotato',
|
||||
|
||||
@@ -1,487 +0,0 @@
|
||||
/**
|
||||
* StyleFix 1.0.3 & PrefixFree 1.0.7
|
||||
* @author Lea Verou
|
||||
* MIT license
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
if(!window.addEventListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = window.StyleFix = {
|
||||
link: function(link) {
|
||||
try {
|
||||
// Ignore stylesheets with data-noprefix attribute as well as alternate stylesheets
|
||||
if(link.rel !== 'stylesheet' || link.hasAttribute('data-noprefix')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = link.href || link.getAttribute('data-href'),
|
||||
base = url.replace(/[^\/]+$/, ''),
|
||||
base_scheme = (/^[a-z]{3,10}:/.exec(base) || [''])[0],
|
||||
base_domain = (/^[a-z]{3,10}:\/\/[^\/]+/.exec(base) || [''])[0],
|
||||
base_query = /^([^?]*)\??/.exec(url)[1],
|
||||
parent = link.parentNode,
|
||||
xhr = new XMLHttpRequest(),
|
||||
process;
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
if(xhr.readyState === 4) {
|
||||
process();
|
||||
}
|
||||
};
|
||||
|
||||
process = function() {
|
||||
var css = xhr.responseText;
|
||||
|
||||
if(css && link.parentNode && (!xhr.status || xhr.status < 400 || xhr.status > 600)) {
|
||||
css = self.fix(css, true, link);
|
||||
|
||||
// Convert relative URLs to absolute, if needed
|
||||
if(base) {
|
||||
css = css.replace(/url\(\s*?((?:"|')?)(.+?)\1\s*?\)/gi, function($0, quote, url) {
|
||||
if(/^([a-z]{3,10}:|#)/i.test(url)) { // Absolute & or hash-relative
|
||||
return $0;
|
||||
}
|
||||
else if(/^\/\//.test(url)) { // Scheme-relative
|
||||
// May contain sequences like /../ and /./ but those DO work
|
||||
return 'url("' + base_scheme + url + '")';
|
||||
}
|
||||
else if(/^\//.test(url)) { // Domain-relative
|
||||
return 'url("' + base_domain + url + '")';
|
||||
}
|
||||
else if(/^\?/.test(url)) { // Query-relative
|
||||
return 'url("' + base_query + url + '")';
|
||||
}
|
||||
else {
|
||||
// Path-relative
|
||||
return 'url("' + base + url + '")';
|
||||
}
|
||||
});
|
||||
|
||||
// behavior URLs shoudn’t be converted (Issue #19)
|
||||
// base should be escaped before added to RegExp (Issue #81)
|
||||
var escaped_base = base.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g,"\\$1");
|
||||
css = css.replace(RegExp('\\b(behavior:\\s*?url\\(\'?"?)' + escaped_base, 'gi'), '$1');
|
||||
}
|
||||
|
||||
var style = document.createElement('style');
|
||||
style.textContent = css;
|
||||
style.media = link.media;
|
||||
style.disabled = link.disabled;
|
||||
style.setAttribute('data-href', link.getAttribute('href'));
|
||||
|
||||
parent.insertBefore(style, link);
|
||||
parent.removeChild(link);
|
||||
|
||||
style.media = link.media; // Duplicate is intentional. See issue #31
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
xhr.open('GET', url);
|
||||
xhr.send(null);
|
||||
} catch (e) {
|
||||
// Fallback to XDomainRequest if available
|
||||
if (typeof XDomainRequest != "undefined") {
|
||||
xhr = new XDomainRequest();
|
||||
xhr.onerror = xhr.onprogress = function() {};
|
||||
xhr.onload = process;
|
||||
xhr.open("GET", url);
|
||||
xhr.send(null);
|
||||
}
|
||||
}
|
||||
|
||||
link.setAttribute('data-inprogress', '');
|
||||
},
|
||||
|
||||
styleElement: function(style) {
|
||||
if (style.hasAttribute('data-noprefix')) {
|
||||
return;
|
||||
}
|
||||
var disabled = style.disabled;
|
||||
|
||||
style.textContent = self.fix(style.textContent, true, style);
|
||||
|
||||
style.disabled = disabled;
|
||||
},
|
||||
|
||||
styleAttribute: function(element) {
|
||||
var css = element.getAttribute('style');
|
||||
|
||||
css = self.fix(css, false, element);
|
||||
|
||||
element.setAttribute('style', css);
|
||||
},
|
||||
|
||||
process: function() {
|
||||
// Linked stylesheets
|
||||
$('link[rel="stylesheet"]:not([data-inprogress])').forEach(StyleFix.link);
|
||||
|
||||
// Inline stylesheets
|
||||
$('style').forEach(StyleFix.styleElement);
|
||||
|
||||
// Inline styles
|
||||
$('[style]').forEach(StyleFix.styleAttribute);
|
||||
},
|
||||
|
||||
register: function(fixer, index) {
|
||||
(self.fixers = self.fixers || [])
|
||||
.splice(index === undefined? self.fixers.length : index, 0, fixer);
|
||||
},
|
||||
|
||||
fix: function(css, raw, element) {
|
||||
for(var i=0; i<self.fixers.length; i++) {
|
||||
css = self.fixers[i](css, raw, element) || css;
|
||||
}
|
||||
|
||||
return css;
|
||||
},
|
||||
|
||||
camelCase: function(str) {
|
||||
return str.replace(/-([a-z])/g, function($0, $1) { return $1.toUpperCase(); }).replace('-','');
|
||||
},
|
||||
|
||||
deCamelCase: function(str) {
|
||||
return str.replace(/[A-Z]/g, function($0) { return '-' + $0.toLowerCase() });
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************
|
||||
* Process styles
|
||||
**************************************/
|
||||
(function(){
|
||||
setTimeout(function(){
|
||||
$('link[rel="stylesheet"]').forEach(StyleFix.link);
|
||||
}, 10);
|
||||
|
||||
document.addEventListener('DOMContentLoaded', StyleFix.process, false);
|
||||
})();
|
||||
|
||||
function $(expr, con) {
|
||||
return [].slice.call((con || document).querySelectorAll(expr));
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
/**
|
||||
* PrefixFree
|
||||
*/
|
||||
(function(root){
|
||||
|
||||
if(!window.StyleFix || !window.getComputedStyle) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Private helper
|
||||
function fix(what, before, after, replacement, css) {
|
||||
what = self[what];
|
||||
|
||||
if(what.length) {
|
||||
var regex = RegExp(before + '(' + what.join('|') + ')' + after, 'gi');
|
||||
|
||||
css = css.replace(regex, replacement);
|
||||
}
|
||||
|
||||
return css;
|
||||
}
|
||||
|
||||
var self = window.PrefixFree = {
|
||||
prefixCSS: function(css, raw, element) {
|
||||
var prefix = self.prefix;
|
||||
|
||||
// Gradient angles hotfix
|
||||
if(self.functions.indexOf('linear-gradient') > -1) {
|
||||
// Gradients are supported with a prefix, convert angles to legacy
|
||||
css = css.replace(/(\s|:|,)(repeating-)?linear-gradient\(\s*(-?\d*\.?\d*)deg/ig, function ($0, delim, repeating, deg) {
|
||||
return delim + (repeating || '') + 'linear-gradient(' + (90-deg) + 'deg';
|
||||
});
|
||||
}
|
||||
|
||||
css = fix('functions', '(\\s|:|,)', '\\s*\\(', '$1' + prefix + '$2(', css);
|
||||
css = fix('keywords', '(\\s|:)', '(\\s|;|\\}|$)', '$1' + prefix + '$2$3', css);
|
||||
css = fix('properties', '(^|\\{|\\s|;)', '\\s*:', '$1' + prefix + '$2:', css);
|
||||
|
||||
// Prefix properties *inside* values (issue #8)
|
||||
if (self.properties.length) {
|
||||
var regex = RegExp('\\b(' + self.properties.join('|') + ')(?!:)', 'gi');
|
||||
|
||||
css = fix('valueProperties', '\\b', ':(.+?);', function($0) {
|
||||
return $0.replace(regex, prefix + "$1")
|
||||
}, css);
|
||||
}
|
||||
|
||||
if(raw) {
|
||||
css = fix('selectors', '', '\\b', self.prefixSelector, css);
|
||||
css = fix('atrules', '@', '\\b', '@' + prefix + '$1', css);
|
||||
}
|
||||
|
||||
// Fix double prefixing
|
||||
css = css.replace(RegExp('-' + prefix, 'g'), '-');
|
||||
|
||||
// Prefix wildcard
|
||||
css = css.replace(/-\*-(?=[a-z]+)/gi, self.prefix);
|
||||
|
||||
return css;
|
||||
},
|
||||
|
||||
property: function(property) {
|
||||
return (self.properties.indexOf(property)? self.prefix : '') + property;
|
||||
},
|
||||
|
||||
value: function(value, property) {
|
||||
value = fix('functions', '(^|\\s|,)', '\\s*\\(', '$1' + self.prefix + '$2(', value);
|
||||
value = fix('keywords', '(^|\\s)', '(\\s|$)', '$1' + self.prefix + '$2$3', value);
|
||||
|
||||
// TODO properties inside values
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
// Warning: Prefixes no matter what, even if the selector is supported prefix-less
|
||||
prefixSelector: function(selector) {
|
||||
return selector.replace(/^:{1,2}/, function($0) { return $0 + self.prefix })
|
||||
},
|
||||
|
||||
// Warning: Prefixes no matter what, even if the property is supported prefix-less
|
||||
prefixProperty: function(property, camelCase) {
|
||||
var prefixed = self.prefix + property;
|
||||
|
||||
return camelCase? StyleFix.camelCase(prefixed) : prefixed;
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************
|
||||
* Properties
|
||||
**************************************/
|
||||
(function() {
|
||||
var prefixes = {},
|
||||
properties = [],
|
||||
shorthands = {},
|
||||
style = getComputedStyle(document.documentElement, null),
|
||||
dummy = document.createElement('div').style;
|
||||
|
||||
// Why are we doing this instead of iterating over properties in a .style object? Cause Webkit won't iterate over those.
|
||||
var iterate = function(property) {
|
||||
if(property.charAt(0) === '-') {
|
||||
properties.push(property);
|
||||
|
||||
var parts = property.split('-'),
|
||||
prefix = parts[1];
|
||||
|
||||
// Count prefix uses
|
||||
prefixes[prefix] = ++prefixes[prefix] || 1;
|
||||
|
||||
// This helps determining shorthands
|
||||
while(parts.length > 3) {
|
||||
parts.pop();
|
||||
|
||||
var shorthand = parts.join('-');
|
||||
|
||||
if(supported(shorthand) && properties.indexOf(shorthand) === -1) {
|
||||
properties.push(shorthand);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
supported = function(property) {
|
||||
return StyleFix.camelCase(property) in dummy;
|
||||
}
|
||||
|
||||
// Some browsers have numerical indices for the properties, some don't
|
||||
if(style.length > 0) {
|
||||
for(var i=0; i<style.length; i++) {
|
||||
iterate(style[i])
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(var property in style) {
|
||||
iterate(StyleFix.deCamelCase(property));
|
||||
}
|
||||
}
|
||||
|
||||
// Find most frequently used prefix
|
||||
var highest = {uses:0};
|
||||
for(var prefix in prefixes) {
|
||||
var uses = prefixes[prefix];
|
||||
|
||||
if(highest.uses < uses) {
|
||||
highest = {prefix: prefix, uses: uses};
|
||||
}
|
||||
}
|
||||
|
||||
self.prefix = '-' + highest.prefix + '-';
|
||||
self.Prefix = StyleFix.camelCase(self.prefix);
|
||||
|
||||
self.properties = [];
|
||||
|
||||
// Get properties ONLY supported with a prefix
|
||||
for(var i=0; i<properties.length; i++) {
|
||||
var property = properties[i];
|
||||
|
||||
if(property.indexOf(self.prefix) === 0) { // we might have multiple prefixes, like Opera
|
||||
var unprefixed = property.slice(self.prefix.length);
|
||||
|
||||
if(!supported(unprefixed)) {
|
||||
self.properties.push(unprefixed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IE fix
|
||||
if(self.Prefix == 'Ms'
|
||||
&& !('transform' in dummy)
|
||||
&& !('MsTransform' in dummy)
|
||||
&& ('msTransform' in dummy)) {
|
||||
self.properties.push('transform', 'transform-origin');
|
||||
}
|
||||
|
||||
self.properties.sort();
|
||||
})();
|
||||
|
||||
/**************************************
|
||||
* Values
|
||||
**************************************/
|
||||
(function() {
|
||||
// Values that might need prefixing
|
||||
var functions = {
|
||||
'linear-gradient': {
|
||||
property: 'backgroundImage',
|
||||
params: 'red, teal'
|
||||
},
|
||||
'calc': {
|
||||
property: 'width',
|
||||
params: '1px + 5%'
|
||||
},
|
||||
'element': {
|
||||
property: 'backgroundImage',
|
||||
params: '#foo'
|
||||
},
|
||||
'cross-fade': {
|
||||
property: 'backgroundImage',
|
||||
params: 'url(a.png), url(b.png), 50%'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
functions['repeating-linear-gradient'] =
|
||||
functions['repeating-radial-gradient'] =
|
||||
functions['radial-gradient'] =
|
||||
functions['linear-gradient'];
|
||||
|
||||
var keywords = {
|
||||
'initial': 'color',
|
||||
'zoom-in': 'cursor',
|
||||
'zoom-out': 'cursor',
|
||||
'box': 'display',
|
||||
'flexbox': 'display',
|
||||
'inline-flexbox': 'display',
|
||||
'flex': 'display',
|
||||
'inline-flex': 'display'
|
||||
};
|
||||
|
||||
self.functions = [];
|
||||
self.keywords = [];
|
||||
|
||||
var style = document.createElement('div').style;
|
||||
|
||||
function supported(value, property) {
|
||||
style[property] = '';
|
||||
style[property] = value;
|
||||
|
||||
return !!style[property];
|
||||
}
|
||||
|
||||
for (var func in functions) {
|
||||
var test = functions[func],
|
||||
property = test.property,
|
||||
value = func + '(' + test.params + ')';
|
||||
|
||||
if (!supported(value, property)
|
||||
&& supported(self.prefix + value, property)) {
|
||||
// It's supported, but with a prefix
|
||||
self.functions.push(func);
|
||||
}
|
||||
}
|
||||
|
||||
for (var keyword in keywords) {
|
||||
var property = keywords[keyword];
|
||||
|
||||
if (!supported(keyword, property)
|
||||
&& supported(self.prefix + keyword, property)) {
|
||||
// It's supported, but with a prefix
|
||||
self.keywords.push(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
/**************************************
|
||||
* Selectors and @-rules
|
||||
**************************************/
|
||||
(function() {
|
||||
|
||||
var
|
||||
selectors = {
|
||||
':read-only': null,
|
||||
':read-write': null,
|
||||
':any-link': null,
|
||||
'::selection': null
|
||||
},
|
||||
|
||||
atrules = {
|
||||
'keyframes': 'name',
|
||||
'viewport': null,
|
||||
'document': 'regexp(".")'
|
||||
};
|
||||
|
||||
self.selectors = [];
|
||||
self.atrules = [];
|
||||
|
||||
var style = root.appendChild(document.createElement('style'));
|
||||
|
||||
function supported(selector) {
|
||||
style.textContent = selector + '{}'; // Safari 4 has issues with style.innerHTML
|
||||
|
||||
return !!style.sheet.cssRules.length;
|
||||
}
|
||||
|
||||
for(var selector in selectors) {
|
||||
var test = selector + (selectors[selector]? '(' + selectors[selector] + ')' : '');
|
||||
|
||||
if(!supported(test) && supported(self.prefixSelector(test))) {
|
||||
self.selectors.push(selector);
|
||||
}
|
||||
}
|
||||
|
||||
for(var atrule in atrules) {
|
||||
var test = atrule + ' ' + (atrules[atrule] || '');
|
||||
|
||||
if(!supported('@' + test) && supported('@' + self.prefix + test)) {
|
||||
self.atrules.push(atrule);
|
||||
}
|
||||
}
|
||||
|
||||
root.removeChild(style);
|
||||
|
||||
})();
|
||||
|
||||
// Properties that accept properties as their value
|
||||
self.valueProperties = [
|
||||
'transition',
|
||||
'transition-property'
|
||||
]
|
||||
|
||||
// Add class for current prefix
|
||||
root.className += ' ' + self.prefix;
|
||||
|
||||
StyleFix.register(self.prefixCSS);
|
||||
|
||||
|
||||
})(document.documentElement);
|
||||
@@ -7,36 +7,24 @@ var Question = new Class( {
|
||||
self.hint = hint
|
||||
self.answers = answers
|
||||
|
||||
self.createQuestion()
|
||||
self.createQuestion();
|
||||
self.answers.each(function(answer) {
|
||||
self.createAnswer(answer)
|
||||
})
|
||||
self.createMask()
|
||||
|
||||
},
|
||||
|
||||
createMask : function() {
|
||||
var self = this
|
||||
|
||||
self.mask = new Element('div.mask').fade('hide').inject(document.body).fade('in');
|
||||
},
|
||||
|
||||
createQuestion : function() {
|
||||
var self = this;
|
||||
|
||||
this.container = new Element('div', {
|
||||
'class' : 'question'
|
||||
}).adopt(
|
||||
self.container = new Element('div.mask.question').adopt(
|
||||
new Element('h3', {
|
||||
'html': this.question
|
||||
}),
|
||||
new Element('div.hint', {
|
||||
'html': this.hint
|
||||
})
|
||||
).inject(document.body)
|
||||
|
||||
this.container.position( {
|
||||
'position' : 'center'
|
||||
});
|
||||
).fade('hide').inject(document.body).fade('in')
|
||||
|
||||
},
|
||||
|
||||
@@ -59,17 +47,15 @@ var Question = new Class( {
|
||||
(options.onComplete || function(){})()
|
||||
self.close();
|
||||
}
|
||||
})).send();
|
||||
})).send();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
close : function() {
|
||||
var self = this;
|
||||
self.mask.fade('out');
|
||||
(function(){self.mask.destroy()}).delay(1000);
|
||||
|
||||
this.container.destroy();
|
||||
self.container.fade('out');
|
||||
(function(){self.container.destroy()}).delay(1000);
|
||||
},
|
||||
|
||||
toElement : function() {
|
||||
|
||||
@@ -46,7 +46,7 @@ Page.Home = new Class({
|
||||
self.soon_list = new MovieList({
|
||||
'navigation': false,
|
||||
'identifier': 'soon',
|
||||
'limit': 18,
|
||||
'limit': 12,
|
||||
'title': 'Available soon',
|
||||
'description': 'These are being searched for and should be available soon as they will be released on DVD in the next few weeks.',
|
||||
'on_empty_element': new Element('div').adopt(
|
||||
@@ -59,9 +59,41 @@ Page.Home = new Class({
|
||||
'actions': [MA.IMDB, MA.Refresh],
|
||||
'load_more': false,
|
||||
'view': 'thumbs',
|
||||
'force_view': true,
|
||||
'api_call': 'dashboard.soon'
|
||||
});
|
||||
|
||||
// Make all thumbnails the same size
|
||||
self.soon_list.addEvent('loaded', function(){
|
||||
var images = $(self.soon_list).getElements('img'),
|
||||
timer,
|
||||
lowest = null;
|
||||
|
||||
images.addEvent('load', function(){
|
||||
var height = this.getSize().y;
|
||||
if(!lowest || lowest > height){
|
||||
lowest = height;
|
||||
if(timer) clearTimeout(timer);
|
||||
timer = (function(){
|
||||
images.getParent().setStyle('height', lowest);
|
||||
}).delay(300)
|
||||
}
|
||||
});
|
||||
|
||||
$(window).addEvent('resize', function(){
|
||||
if(timer) clearTimeout(timer);
|
||||
timer = (function(){
|
||||
var lowest;
|
||||
images.each(function(img){
|
||||
var height = img.getSize().y;
|
||||
if(!lowest || lowest > height)
|
||||
lowest = height;
|
||||
});
|
||||
images.getParent().setStyle('height', lowest);
|
||||
}).delay(300);
|
||||
});
|
||||
});
|
||||
|
||||
// Still not available
|
||||
self.late_list = new MovieList({
|
||||
'navigation': false,
|
||||
|
||||
@@ -30,7 +30,7 @@ Page.Wanted = new Class({
|
||||
$(self.wanted).inject(self.el);
|
||||
|
||||
// Check if search is in progress
|
||||
self.startProgressInterval();
|
||||
self.startProgressInterval.delay(4000, self);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
html {
|
||||
body, html {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
|
||||
font-family: OpenSans, "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
|
||||
height: 100%;
|
||||
text-shadow: 0 1px 0 #000;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #4e5969;
|
||||
overflow-y: scroll;
|
||||
height: 100%;
|
||||
}
|
||||
body { overflow-y: scroll; }
|
||||
body.noscroll { overflow: hidden; }
|
||||
|
||||
#clean {
|
||||
@@ -32,14 +27,16 @@ pre {
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
font-size: 12px;
|
||||
font-size: 1em;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
|
||||
}
|
||||
input:focus, textarea:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input:-moz-placeholder, textarea:-moz-placeholder {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
::-webkit-input-placeholder, ::-webkit-textarea-placeholder {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
@@ -59,53 +56,30 @@ a:hover { color: #f3f3f3; }
|
||||
|
||||
.page {
|
||||
display: none;
|
||||
width: 960px;
|
||||
width: 100%;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
line-height: 24px;
|
||||
padding: 0 0 20px;
|
||||
line-height: 1.5em;
|
||||
padding: 0 15px 20px;
|
||||
}
|
||||
.page.active { display: block; }
|
||||
|
||||
.page .noticeMe {
|
||||
background-color: lightgoldenrodyellow;
|
||||
display: block;
|
||||
padding: 20px 10px;
|
||||
margin: 0 -10px 40px;
|
||||
font-size: 19px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
clear:both;
|
||||
padding: 80px 0 10px;
|
||||
padding: 65px 0 10px;
|
||||
background: #4e5969;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 30px;
|
||||
padding: 0;
|
||||
margin: 20px 0 0 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align:center;
|
||||
padding: 50px 0 0 0;
|
||||
color: #999;
|
||||
font-size: 10px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.footer .check {
|
||||
color: #333;
|
||||
@media all and (max-width: 480px) {
|
||||
.content {
|
||||
padding-top: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
#toTop {
|
||||
background: black;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 10px 10px 10px 40px;
|
||||
background: #f7f7f7 url('../images/toTop.gif') no-repeat 10px center;
|
||||
border-radius: 5px 0 0 0;
|
||||
h2 {
|
||||
font-size: 2.5em;
|
||||
padding: 0;
|
||||
margin: 20px 0 0 0;
|
||||
}
|
||||
|
||||
form {
|
||||
@@ -126,6 +100,12 @@ body > .spinner, .mask{
|
||||
width: 100%;
|
||||
padding: 200px;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
body > .mask {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
background: #5082bc url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAyCAYAAACd+7GKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAClJREFUeNpi/v//vwMTAwPDfzjBgMpFI/7hFSOT9Y8qRuF3JLoHAQIMAHYtMmRA+CugAAAAAElFTkSuQmCC") repeat-x;
|
||||
@@ -135,8 +115,6 @@ body > .spinner, .mask{
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||||
text-shadow: 0 -1px 1px rgba(0,0,0,0.25);
|
||||
cursor: pointer;
|
||||
}
|
||||
.button.red { background-color: #ff0000; }
|
||||
@@ -164,132 +142,302 @@ body > .spinner, .mask{
|
||||
.icon.spinner { background-image: url('../images/icon.spinner.gif'); }
|
||||
.icon.attention { background-image: url('../images/icon.attention.png'); }
|
||||
|
||||
.icon2 {
|
||||
display: inline-block;
|
||||
background: center no-repeat;
|
||||
font-family: 'Elusive-Icons';
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.icon2.cog:before { content: "\e109"; }
|
||||
.icon2.eye-open:before { content: "\e09d"; }
|
||||
.icon2.search:before { content: "\e03e"; }
|
||||
.icon2.return-key:before { content: "\e111"; }
|
||||
.icon2.close:before { content: "\e04e"; }
|
||||
.icon2.menu:before {
|
||||
content: "\e076 \e076 \e076";
|
||||
line-height: 6px;
|
||||
transform: scaleX(2);
|
||||
width: 20px;
|
||||
font-size: 10px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/*** Navigation ***/
|
||||
.header {
|
||||
background: #4e5969;
|
||||
padding: 10px 0;
|
||||
height: 80px;
|
||||
height: 66px;
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
z-index: 5;
|
||||
box-shadow: 0 20px 30px -30px rgba(0,0,0,0.05);
|
||||
transition: box-shadow .4s cubic-bezier(0.9,0,0.1,1);
|
||||
background: #4e5969;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,.1);
|
||||
transition: all .4s ease-in-out;
|
||||
}
|
||||
.header.with_shadow {
|
||||
box-shadow: 0 20px 30px -30px rgba(0,0,0,0.3);
|
||||
background-color: #46505e;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.header {
|
||||
height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.header > div {
|
||||
width: 960px;
|
||||
width: 100%;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.header .navigation {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 67.2%;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.header .navigation ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.header .navigation li {
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
font-size:20px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header .navigation li a {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
}
|
||||
.header .navigation li:first-child a { padding-left: 10px; }
|
||||
.header .navigation li span {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.header .navigation li a:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 2px;
|
||||
width: 76%;
|
||||
left: 12%;
|
||||
position: absolute;
|
||||
top: 46px;
|
||||
background-color: #46505e;
|
||||
outline: none;
|
||||
box-shadow: inset 0 1px 8px rgba(0,0,0,0.05), 0 1px 0px rgba(255,255,255,0.15);
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1);
|
||||
}
|
||||
|
||||
.header .navigation li:hover a:after { background-color: #047792; }
|
||||
.header .navigation li.active a:after { background-color: #04bce6; }
|
||||
|
||||
.header .navigation li.disabled { color: #e5e5e5; }
|
||||
.header .navigation li a { color: #fff; }
|
||||
|
||||
.header .navigation .backtotop {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
width: 80px;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
|
||||
.header .foldout {
|
||||
width: 44px;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
margin: -10px 0 0 -40px;
|
||||
background: #4e5969;
|
||||
padding: 5px 0;
|
||||
border-radius: 0 0 5px 5px;
|
||||
color: rgba(255,255,255,.4);
|
||||
text-shadow: none;
|
||||
font-weight: normal;
|
||||
border-right: 1px solid rgba(255,255,255,.07);
|
||||
display: none;
|
||||
vertical-align: top;
|
||||
line-height: 42px;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.header .logo {
|
||||
display: inline-block;
|
||||
font-size: 1.75em;
|
||||
padding: 15px 30px 0 15px;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
border-right: 1px solid rgba(255,255,255,.07);
|
||||
color: #FFF;
|
||||
font-weight: normal;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.header .foldout {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.header .logo {
|
||||
padding-top: 7px;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 481px) and (max-width: 640px) {
|
||||
|
||||
.header .logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.header .navigation ul {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header .navigation li {
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
font-size: 1.75em;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
border: 1px solid rgba(255,255,255,.07);
|
||||
border-width: 0 0 0 1px;
|
||||
}
|
||||
.header .navigation li:first-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.header .navigation li a {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
border: 1px solid transparent;
|
||||
border-width: 0 0 4px 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.header .navigation li:hover a { border-color: #047792; }
|
||||
.header .navigation li.active a { border-color: #04bce6; }
|
||||
|
||||
.header .navigation li.disabled { color: #e5e5e5; }
|
||||
.header .navigation li a { color: #fff; }
|
||||
|
||||
.header .navigation .backtotop {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
width: 80px;
|
||||
left: 50%;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
text-align: center;
|
||||
margin: -10px 0 0 -40px;
|
||||
background: #4e5969;
|
||||
padding: 5px 0;
|
||||
color: rgba(255,255,255,.4);
|
||||
font-weight: normal;
|
||||
}
|
||||
.header:hover .navigation .backtotop { color: #fff; }
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
|
||||
body {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
transition: all .5s cubic-bezier(0.9,0,0.1,1);
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.menu_shown body {
|
||||
left: 160px;
|
||||
}
|
||||
|
||||
.header .navigation {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.menu_shown .header .navigation .overlay {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 160px;
|
||||
}
|
||||
|
||||
.header .navigation ul {
|
||||
width: 160px;
|
||||
position: fixed;
|
||||
left: -160px;
|
||||
background: rgba(0,0,0,.5);
|
||||
transition: all .5s cubic-bezier(0.9,0,0.1,1);
|
||||
}
|
||||
|
||||
.menu_shown .header .navigation ul {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.header .navigation ul li {
|
||||
display: block;
|
||||
text-align: left;
|
||||
border-width: 1px 0 0 0;
|
||||
height: 44px;
|
||||
}
|
||||
.header .navigation ul li a {
|
||||
border-width: 0 4px 0 0;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.header .navigation ul li.separator {
|
||||
background-color: rgba(255,255,255, .07);
|
||||
height: 5px;
|
||||
}
|
||||
}
|
||||
.header:hover .navigation .backtotop { color: #fff; }
|
||||
|
||||
.header .more_menu {
|
||||
margin-left: 12px;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
height: 100%;
|
||||
border-left: 1px solid rgba(255,255,255,.07);
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.header .more_menu {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.header .more_menu .button {
|
||||
height: 100%;
|
||||
width: 44px;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
background: none;
|
||||
line-height: 66px;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
border: 1px solid transparent;
|
||||
border-width: 0 0 4px;
|
||||
}
|
||||
.header .more_menu .button:hover {
|
||||
background: none;
|
||||
border-color: #047792;
|
||||
}
|
||||
|
||||
.header .more_menu .wrapper {
|
||||
width: 150px;
|
||||
margin-left: -110px;
|
||||
}
|
||||
.header .more_menu .wrapper:before {
|
||||
margin-left: -34px;
|
||||
margin-left: -106px;
|
||||
margin-top: 66px;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.header .more_menu .button {
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
.header .more_menu .wrapper {
|
||||
margin-top: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.header .more_menu .red { color: red; }
|
||||
.header .more_menu .orange { color: orange; }
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
line-height: 14px;
|
||||
border-radius: 50%;
|
||||
font-size: 8px;
|
||||
margin: -5px 0 0 15px;
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,.6), 0 0 3px rgba(0,0,0,.7);
|
||||
line-height: 20px;
|
||||
margin: 0;
|
||||
background-color: #1b79b8;
|
||||
text-shadow: none;
|
||||
background-image: -*-linear-gradient(0deg, rgba(255,255,255,.3) 0%, rgba(255,255,255,.1) 100%);
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.header .notification_menu {
|
||||
right: 60px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.header .notification_menu {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.header .notification_menu .wrapper {
|
||||
width: 300px;
|
||||
margin-left: -260px;
|
||||
margin-left: -255px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.header .notification_menu .wrapper:before {
|
||||
left: 296px;
|
||||
}
|
||||
|
||||
.header .notification_menu ul {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
@@ -309,7 +457,7 @@ body > .spinner, .mask{
|
||||
.header .notification_menu li:last-child > span { border: 0; }
|
||||
.header .notification_menu li .added {
|
||||
display: block;
|
||||
font-size: 10px;
|
||||
font-size: .85em;
|
||||
color: #aaa;
|
||||
text-align: ;
|
||||
}
|
||||
@@ -318,25 +466,27 @@ body > .spinner, .mask{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header .message.update {
|
||||
.message.update {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
top: -70px;
|
||||
padding: 2px 0;
|
||||
position: fixed;
|
||||
padding: 10px;
|
||||
background: #ff6134;
|
||||
font-size: 12px;
|
||||
border-radius: 0 0 5px 5px;
|
||||
box-shadow: 0 2px 1px rgba(0,0,0, 0.3);
|
||||
font-size: 15px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 19;
|
||||
}
|
||||
|
||||
.header .message a {
|
||||
padding: 0 10px;
|
||||
.message.update a {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
/*** Global Styles ***/
|
||||
.check {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
vertical-align: top;
|
||||
margin-top: 4px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
@@ -361,8 +511,8 @@ body > .spinner, .mask{
|
||||
border-radius:30px;
|
||||
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.35), inset 0 1px 0px rgba(255,255,255,0.20);
|
||||
background: url('../images/sprite.png') no-repeat 94% -53px, -*-linear-gradient(
|
||||
270deg,
|
||||
background: url('../images/sprite.png') no-repeat 94% -53px, linear-gradient(
|
||||
180deg,
|
||||
#5b9bd1 0%,
|
||||
#406db8 100%
|
||||
);
|
||||
@@ -437,8 +587,8 @@ body > .spinner, .mask{
|
||||
border: 1px solid #252930;
|
||||
box-shadow: inset 0 1px 0px rgba(255,255,255,0.20), 0 0 3px rgba(0,0,0, 0.2);
|
||||
background: rgb(55,62,74);
|
||||
background-image: -*-linear-gradient(
|
||||
90deg,
|
||||
background-image: linear-gradient(
|
||||
0,
|
||||
rgb(55,62,74) 0%,
|
||||
rgb(73,83,98) 100%
|
||||
);
|
||||
@@ -454,13 +604,9 @@ body > .spinner, .mask{
|
||||
display: block;
|
||||
width: 600px;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
position:fixed;
|
||||
z-index:101;
|
||||
z-index: 101;
|
||||
text-align: center;
|
||||
background: #5c697b;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 50px rgba(0,0,0,0.55);
|
||||
}
|
||||
|
||||
.question h3 {
|
||||
@@ -472,7 +618,6 @@ body > .spinner, .mask{
|
||||
.question .hint {
|
||||
font-size: 14px;
|
||||
color: #ccc;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.question .answer {
|
||||
@@ -495,10 +640,11 @@ body > .spinner, .mask{
|
||||
background-color: #4c5766;
|
||||
}
|
||||
|
||||
.more_menu {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.more_menu {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.more_menu > a {
|
||||
display: block;
|
||||
@@ -508,57 +654,45 @@ body > .spinner, .mask{
|
||||
border: 1px solid rgba(0,0,0,0.3);
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
.more_menu.show > a:not(:active), .more_menu > a:hover:not(:active) {
|
||||
background-color: #406db8;
|
||||
}
|
||||
|
||||
.more_menu.show > a:not(:active), .more_menu > a:hover:not(:active) {
|
||||
background-color: #406db8;
|
||||
}
|
||||
|
||||
.more_menu .wrapper {
|
||||
display: none;
|
||||
border: 1px solid #333;
|
||||
background: rgba(255,255,255,0.98);
|
||||
border-radius: 3px;
|
||||
padding: 4px !important;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 4px;
|
||||
margin: 26px 0 0 0;
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
margin: 32px 0 0 -145px;
|
||||
z-index: 90;
|
||||
width: 185px;
|
||||
box-shadow: 0 10px 10px -5px rgba(0,0,0,0.4);
|
||||
box-shadow: 0 20px 20px -5px rgba(0,0,0,0.1);
|
||||
text-align: center;
|
||||
color: #000;
|
||||
text-shadow: none;
|
||||
background-image: -*-linear-gradient(
|
||||
45deg,
|
||||
background-image: linear-gradient(
|
||||
-45deg,
|
||||
rgb(200,200,200) 0%,
|
||||
rgb(255,255,255) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.more_menu .wrapper:before {
|
||||
content: ' ';
|
||||
height: 0;
|
||||
position: relative;
|
||||
width: 0;
|
||||
border: 6px solid transparent;
|
||||
border-bottom-color: #fff;
|
||||
display: block;
|
||||
top: -16px;
|
||||
left: 146px;
|
||||
}
|
||||
|
||||
.more_menu.show .wrapper {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.more_menu ul {
|
||||
padding: 0;
|
||||
margin: -12px 0 0 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
|
||||
.more_menu .wrapper li {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
.more_menu .wrapper li a {
|
||||
display: block;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.2);
|
||||
@@ -570,7 +704,7 @@ body > .spinner, .mask{
|
||||
padding: 3px 0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
|
||||
.more_menu .wrapper li:last-child a {
|
||||
border: none;
|
||||
}
|
||||
@@ -582,41 +716,109 @@ body > .spinner, .mask{
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 2px;
|
||||
width: 240px;
|
||||
width: 320px;
|
||||
z-index: 20;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
@media all and (max-width: 480px) {
|
||||
.messages {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.messages .message {
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
margin: 2px 0 0 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
transition: all .6s cubic-bezier(0.9,0,0.1,1);
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.35), inset 0 1px 0px rgba(255,255,255,0.20);
|
||||
background-image: -*-linear-gradient(
|
||||
270deg,
|
||||
#5b9bd1 0%,
|
||||
#406db8 100%
|
||||
);
|
||||
background: #5b9bd1;
|
||||
width: 100%;
|
||||
padding: 0 5px;
|
||||
visibility: hidden;
|
||||
position: relative;
|
||||
margin: 1px 0 0;
|
||||
max-height: 0;
|
||||
padding: 0 30px 0 20px;
|
||||
font-size: 1.1em;
|
||||
font-weight: normal;
|
||||
transform: scale(0);
|
||||
}
|
||||
.messages .message.sticky {
|
||||
background-color: #c84040;
|
||||
}
|
||||
.messages .message.show {
|
||||
visibility: visible;
|
||||
height: auto;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
min-height: 1px;
|
||||
max-height: 400px;
|
||||
max-height: 100px;
|
||||
padding: 15px 30px 15px 20px;
|
||||
transform: scale(1);
|
||||
}
|
||||
.messages .message.hide {
|
||||
margin-left: 240px;
|
||||
opacity: 0;
|
||||
}
|
||||
max-height: 0;
|
||||
padding: 0 20px;
|
||||
margin: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
.messages .close {
|
||||
position: absolute;
|
||||
padding: 10px 8px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
/* Fonts */
|
||||
@font-face {
|
||||
font-family: 'Elusive-Icons';
|
||||
src:url('../fonts/Elusive-Icons.eot');
|
||||
src:url('../fonts/Elusive-Icons.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/Elusive-Icons.woff') format('woff'),
|
||||
url('../fonts/Elusive-Icons.ttf') format('truetype'),
|
||||
url('../fonts/Elusive-Icons.svg#Elusive-Icons') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'OpenSans';
|
||||
src: url('../fonts/OpenSans-Regular-webfont.eot');
|
||||
src: url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/OpenSans-Regular-webfont.woff') format('woff'),
|
||||
url('../fonts/OpenSans-Regular-webfont.ttf') format('truetype'),
|
||||
url('../fonts/OpenSans-Regular-webfont.svg#OpenSansRegular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'OpenSans';
|
||||
src: url('../fonts/OpenSans-Italic-webfont.eot');
|
||||
src: url('../fonts/OpenSans-Italic-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/OpenSans-Italic-webfont.woff') format('woff'),
|
||||
url('../fonts/OpenSans-Italic-webfont.ttf') format('truetype'),
|
||||
url('../fonts/OpenSans-Italic-webfont.svg#OpenSansItalic') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'OpenSans';
|
||||
src: url('../fonts/OpenSans-Bold-webfont.eot');
|
||||
src: url('../fonts/OpenSans-Bold-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/OpenSans-Bold-webfont.woff') format('woff'),
|
||||
url('../fonts/OpenSans-Bold-webfont.ttf') format('truetype'),
|
||||
url('../fonts/OpenSans-Bold-webfont.svg#OpenSansBold') format('svg');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'OpenSans';
|
||||
src: url('../fonts/OpenSans-BoldItalic-webfont.eot');
|
||||
src: url('../fonts/OpenSans-BoldItalic-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/OpenSans-BoldItalic-webfont.woff') format('woff'),
|
||||
url('../fonts/OpenSans-BoldItalic-webfont.ttf') format('truetype'),
|
||||
url('../fonts/OpenSans-BoldItalic-webfont.svg#OpenSansBoldItalic') format('svg');
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
.page.settings {
|
||||
min-width: 960px;
|
||||
}
|
||||
|
||||
.page.settings:after {
|
||||
content: ".";
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
@@ -9,15 +13,15 @@
|
||||
|
||||
.page.settings .tabs {
|
||||
float: left;
|
||||
width: 20%;
|
||||
font-size: 20px;
|
||||
width: 14.7%;
|
||||
font-size: 17px;
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
padding: 40px 0;
|
||||
padding: 35px 0;
|
||||
margin: 0;
|
||||
min-height: 470px;
|
||||
background-image: -*-linear-gradient(
|
||||
20deg,
|
||||
background-image: linear-gradient(
|
||||
76deg,
|
||||
rgba(0,0,0,0) 50%,
|
||||
rgba(0,0,0,0.3) 100%
|
||||
);
|
||||
@@ -60,9 +64,9 @@
|
||||
|
||||
|
||||
.page.settings .containers {
|
||||
width: 80%;
|
||||
width: 84%;
|
||||
float: left;
|
||||
padding: 20px 2%;
|
||||
padding: 40px 2%;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
@@ -135,6 +139,10 @@
|
||||
padding-left: 2%;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.page .check {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.page .check + .formHint {
|
||||
float: none;
|
||||
@@ -163,6 +171,10 @@
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page .option_list .check {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.page .option_list .enabler {
|
||||
padding: 0;
|
||||
margin-left: 5px !important;
|
||||
@@ -240,8 +252,11 @@
|
||||
display: block;
|
||||
text-align: right;
|
||||
height: 20px;
|
||||
margin: 0;
|
||||
margin: 0 0 -37px;
|
||||
}
|
||||
.page .advanced_toggle .check {
|
||||
margin: 0;
|
||||
}
|
||||
.page .advanced_toggle span { padding: 0 5px; }
|
||||
.page.show_advanced .advanced_toggle {
|
||||
color: #edc07f;
|
||||
@@ -439,16 +454,16 @@
|
||||
border-radius: 2px;
|
||||
}
|
||||
.page .tag_input > ul:hover > li.choice {
|
||||
background: -*-linear-gradient(
|
||||
270deg,
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255,255,255,0.3) 0%,
|
||||
rgba(255,255,255,0.1) 100%
|
||||
);
|
||||
}
|
||||
.page .tag_input > ul > li.choice:hover,
|
||||
.page .tag_input > ul > li.choice.selected {
|
||||
background: -*-linear-gradient(
|
||||
270deg,
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
#5b9bd1 0%,
|
||||
#406db8 100%
|
||||
);
|
||||
@@ -486,8 +501,8 @@
|
||||
margin: -9px 0 0 -16px;
|
||||
border-radius: 30px 30px 0 0;
|
||||
cursor: pointer;
|
||||
background: url('../images/icon.delete.png') no-repeat center 2px, -*-linear-gradient(
|
||||
270deg,
|
||||
background: url('../images/icon.delete.png') no-repeat center 2px, linear-gradient(
|
||||
180deg,
|
||||
#5b9bd1 0%,
|
||||
#5b9bd1 100%
|
||||
);
|
||||
@@ -540,6 +555,9 @@
|
||||
.page .combined_table .ctrlHolder > * {
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
.page .combined_table .ctrlHolder > .check {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.page .combined_table .ctrlHolder .delete {
|
||||
display: none;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
{% for url in fireEvent('clientscript.get_styles', as_html = True, location = 'front', single = True) %}
|
||||
<link rel="stylesheet" href="{{ url_for('web.index') }}{{ url }}" type="text/css">{% endfor %}
|
||||
{% for url in fireEvent('clientscript.get_scripts', as_html = True, location = 'front', single = True) %}
|
||||
@@ -17,6 +20,17 @@
|
||||
<script type="text/javascript" src="https://www.youtube.com/player_api" defer="defer"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
if($(window).getSize().x <= 480)
|
||||
window.addEvent('load', function() {
|
||||
|
||||
setTimeout(function(){
|
||||
window.scrollTo(0, 1);
|
||||
window.scrollTo(0, 0);
|
||||
}, 100);
|
||||
|
||||
});
|
||||
|
||||
window.addEvent('domready', function() {
|
||||
new Uniform();
|
||||
|
||||
|
||||
20
libs/cssprefixer/__init__.py
Executable file
@@ -0,0 +1,20 @@
|
||||
# CSSPrefixer
|
||||
# Copyright 2010-2012 Greg V. <floatboth@me.com>
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import engine
|
||||
import rules
|
||||
from engine import process
|
||||
|
||||
__all__ = ('process', 'engine', 'rules')
|
||||
117
libs/cssprefixer/engine.py
Executable file
@@ -0,0 +1,117 @@
|
||||
# CSSPrefixer
|
||||
# Copyright 2010-2012 Greg V. <floatboth@me.com>
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import cssutils
|
||||
import re
|
||||
from rules import rules as tr_rules
|
||||
from rules import prefixRegex
|
||||
|
||||
|
||||
keyframesRegex = re.compile(r'@keyframes\s?\w+\s?{(.*)}')
|
||||
blockRegex = re.compile(r'\w+\s?\{(.*)\}')
|
||||
|
||||
|
||||
def magic(ruleset, debug, minify, filt, parser):
|
||||
if isinstance(ruleset, cssutils.css.CSSUnknownRule):
|
||||
if ruleset.cssText.startswith('@keyframes'):
|
||||
inner = parser.parseString(keyframesRegex.split(ruleset.cssText.replace('\n', ''))[1])
|
||||
# BUG: doesn't work when minified
|
||||
s = '' if minify else '\n'
|
||||
return '@-webkit-keyframes {' + s + \
|
||||
''.join([magic(rs, debug, minify, ['webkit'], parser) for rs in inner]) \
|
||||
+ '}' + s + '@-moz-keyframes {' + s + \
|
||||
''.join([magic(rs, debug, minify, ['moz'], parser) for rs in inner]) \
|
||||
+ '}' + s + ruleset.cssText
|
||||
elif ruleset.cssText.startswith('from') or ruleset.cssText.startswith('to'):
|
||||
return ''.join([magic(rs, debug, minify, filt, parser)
|
||||
for rs in parser.parseString(blockRegex.sub(r'\1', ruleset.cssText.replace('\n', ''))[1])])
|
||||
else:
|
||||
return
|
||||
elif hasattr(ruleset, 'style'): # Comments don't
|
||||
ruleSet = set()
|
||||
rules = list()
|
||||
children = list(ruleset.style.children())
|
||||
ruleset.style = cssutils.css.CSSStyleDeclaration() # clear out the styles that were there
|
||||
for rule in children:
|
||||
if not hasattr(rule, 'name'): # comments don't have name
|
||||
rules.append(rule)
|
||||
continue
|
||||
name = prefixRegex.sub('', rule.name)
|
||||
if name in tr_rules:
|
||||
rule.name = name
|
||||
if rule.cssText in ruleSet:
|
||||
continue
|
||||
ruleSet.add(rule.cssText)
|
||||
rules.append(rule)
|
||||
|
||||
ruleset.style.seq._readonly = False
|
||||
for rule in rules:
|
||||
if not hasattr(rule, 'name'):
|
||||
ruleset.style.seq.append(rule, 'Comment')
|
||||
continue
|
||||
processor = None
|
||||
try: # try except so if anything goes wrong we don't lose the original property
|
||||
if rule.name in tr_rules:
|
||||
processor = tr_rules[rule.name](rule)
|
||||
[ruleset.style.seq.append(prop, 'Property') for prop in processor.get_prefixed_props(filt) if prop]
|
||||
# always add the original rule
|
||||
if processor and hasattr(processor, 'get_base_prop'):
|
||||
ruleset.style.seq.append(processor.get_base_prop(), 'Property')
|
||||
else:
|
||||
ruleset.style.seq.append(rule, 'Property')
|
||||
except:
|
||||
if debug:
|
||||
print 'warning with ' + str(rule)
|
||||
ruleset.style.seq.append(rule, 'Property')
|
||||
ruleset.style.seq._readonly = True
|
||||
elif hasattr(ruleset, 'cssRules'):
|
||||
for subruleset in ruleset:
|
||||
magic(subruleset, debug, minify, filt, parser)
|
||||
cssText = ruleset.cssText
|
||||
if not cssText: # blank rules return None so return an empty string
|
||||
return
|
||||
if minify or not hasattr(ruleset, 'style'):
|
||||
return unicode(cssText)
|
||||
return unicode(cssText) + '\n'
|
||||
|
||||
|
||||
def process(string, debug = False, minify = False, filt = ['webkit', 'moz', 'o', 'ms'], **prefs):
|
||||
loglevel = 'DEBUG' if debug else 'ERROR'
|
||||
parser = cssutils.CSSParser(loglevel = 'CRITICAL')
|
||||
if minify:
|
||||
cssutils.ser.prefs.useMinified()
|
||||
else:
|
||||
cssutils.ser.prefs.useDefaults()
|
||||
|
||||
# use the passed in prefs
|
||||
for key, value in prefs.iteritems():
|
||||
if hasattr(cssutils.ser.prefs, key):
|
||||
cssutils.ser.prefs.__dict__[key] = value
|
||||
|
||||
results = []
|
||||
sheet = parser.parseString(string)
|
||||
for ruleset in sheet.cssRules:
|
||||
cssText = magic(ruleset, debug, minify, filt, parser)
|
||||
if cssText:
|
||||
results.append(cssText)
|
||||
|
||||
# format with newlines based on minify
|
||||
joinStr = '' if minify else '\n'
|
||||
|
||||
# Not using sheet.cssText - it's buggy:
|
||||
# it skips some prefixed properties.
|
||||
return joinStr.join(results).rstrip()
|
||||
|
||||
__all__ = ['process']
|
||||
271
libs/cssprefixer/rules.py
Executable file
@@ -0,0 +1,271 @@
|
||||
# CSSPrefixer
|
||||
# Copyright 2010-2012 Greg V. <floatboth@me.com>
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import re
|
||||
import cssutils
|
||||
|
||||
prefixRegex = re.compile('^(-o-|-ms-|-moz-|-webkit-)')
|
||||
|
||||
|
||||
class BaseReplacementRule(object):
|
||||
vendor_prefixes = ['moz', 'webkit']
|
||||
|
||||
def __init__(self, prop):
|
||||
self.prop = prop
|
||||
|
||||
def get_prefixed_props(self, filt):
|
||||
for prefix in [p for p in self.vendor_prefixes if p in filt]:
|
||||
yield cssutils.css.Property(
|
||||
name='-%s-%s' % (prefix, self.prop.name),
|
||||
value=self.prop.value,
|
||||
priority=self.prop.priority
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def should_prefix():
|
||||
return True
|
||||
|
||||
|
||||
class FullReplacementRule(BaseReplacementRule):
|
||||
vendor_prefixes = sorted(BaseReplacementRule.vendor_prefixes + ['o', 'ms'])
|
||||
|
||||
|
||||
class BaseAndIEReplacementRule(BaseReplacementRule):
|
||||
vendor_prefixes = sorted(BaseReplacementRule.vendor_prefixes + ['ms'])
|
||||
|
||||
|
||||
class BaseAndOperaReplacementRule(BaseReplacementRule):
|
||||
vendor_prefixes = sorted(BaseReplacementRule.vendor_prefixes + ['o'])
|
||||
|
||||
|
||||
class WebkitReplacementRule(BaseReplacementRule):
|
||||
vendor_prefixes = ['webkit']
|
||||
|
||||
|
||||
class OperaAndIEReplacementRule(BaseReplacementRule):
|
||||
vendor_prefixes = ['ms', 'o']
|
||||
|
||||
|
||||
class MozReplacementRule(BaseReplacementRule):
|
||||
vendor_prefixes = ['moz']
|
||||
|
||||
|
||||
class BorderRadiusReplacementRule(BaseReplacementRule):
|
||||
"""
|
||||
Mozilla's Gecko engine uses different syntax for rounded corners.
|
||||
"""
|
||||
vendor_prefixes = ['webkit']
|
||||
|
||||
def get_prefixed_props(self, filt):
|
||||
for prop in BaseReplacementRule.get_prefixed_props(self, filt):
|
||||
yield prop
|
||||
if 'moz' in filt:
|
||||
name = '-moz-' + self.prop.name.replace('top-left-radius', 'radius-topleft') \
|
||||
.replace('top-right-radius', 'radius-topright') \
|
||||
.replace('bottom-right-radius', 'radius-bottomright') \
|
||||
.replace('bottom-left-radius', 'radius-bottomleft')
|
||||
yield cssutils.css.Property(
|
||||
name=name,
|
||||
value=self.prop.value,
|
||||
priority=self.prop.priority
|
||||
)
|
||||
|
||||
|
||||
class DisplayReplacementRule(BaseReplacementRule):
|
||||
"""
|
||||
Flexible Box Model stuff.
|
||||
CSSUtils parser doesn't support duplicate properties, so that's dirty.
|
||||
"""
|
||||
def get_prefixed_props(self, filt):
|
||||
if self.prop.value == 'box': # only add prefixes if the value is box
|
||||
for prefix in [p for p in self.vendor_prefixes if p in filt]:
|
||||
yield cssutils.css.Property(
|
||||
name='display',
|
||||
value='-%s-box' % prefix,
|
||||
priority=self.prop.priority
|
||||
)
|
||||
|
||||
|
||||
class TransitionReplacementRule(BaseReplacementRule):
|
||||
vendor_prefixes = ['moz', 'o', 'webkit']
|
||||
|
||||
def __get_prefixed_prop(self, prefix=None):
|
||||
name = self.prop.name
|
||||
if prefix:
|
||||
name = '-%s-%s' % (prefix, self.prop.name)
|
||||
newValues = []
|
||||
for value in self.prop.value.split(','):
|
||||
parts = value.strip().split(' ')
|
||||
parts[0] = prefixRegex.sub('', parts[0])
|
||||
if parts[0] in rules and prefix and rules[parts[0]].should_prefix():
|
||||
parts[0] = '-%s-%s' % (prefix, parts[0])
|
||||
newValues.append(' '.join(parts))
|
||||
return cssutils.css.Property(
|
||||
name=name,
|
||||
value=', '.join(newValues),
|
||||
priority=self.prop.priority
|
||||
)
|
||||
|
||||
def get_prefixed_props(self, filt):
|
||||
for prefix in [p for p in self.vendor_prefixes if p in filt]:
|
||||
yield self.__get_prefixed_prop(prefix)
|
||||
|
||||
def get_base_prop(self):
|
||||
return self.__get_prefixed_prop()
|
||||
|
||||
|
||||
class GradientReplacementRule(BaseReplacementRule):
|
||||
vendor_prefixes = ['moz', 'o', 'webkit']
|
||||
|
||||
def __iter_values(self):
|
||||
valueSplit = self.prop.value.split(',')
|
||||
index = 0
|
||||
# currentString = ''
|
||||
while(True):
|
||||
if index >= len(valueSplit):
|
||||
break
|
||||
rawValue = valueSplit[index].strip()
|
||||
snip = prefixRegex.sub('', rawValue)
|
||||
if snip.startswith('linear-gradient'):
|
||||
values = [re.sub('^linear-gradient\(', '', snip)]
|
||||
if valueSplit[index + 1].strip().endswith(')'):
|
||||
values.append(re.sub('\)+$', '', valueSplit[index + 1].strip()))
|
||||
else:
|
||||
values.append(valueSplit[index + 1].strip())
|
||||
values.append(re.sub('\)+$', '', valueSplit[index + 2].strip()))
|
||||
if len(values) == 2:
|
||||
yield {
|
||||
'start': values[0],
|
||||
'end': values[1]
|
||||
}
|
||||
else:
|
||||
yield {
|
||||
'pos': values[0],
|
||||
'start': values[1],
|
||||
'end': values[2]
|
||||
}
|
||||
index += len(values)
|
||||
elif snip.startswith('gradient'):
|
||||
yield {
|
||||
'start': re.sub('\)+$', '', valueSplit[index + 4].strip()),
|
||||
'end': re.sub('\)+$', '', valueSplit[index + 6].strip()),
|
||||
}
|
||||
index += 7
|
||||
else:
|
||||
# not a gradient so just yield the raw string
|
||||
yield rawValue
|
||||
index += 1
|
||||
|
||||
def __get_prefixed_prop(self, values, prefix=None):
|
||||
gradientName = 'linear-gradient'
|
||||
if prefix:
|
||||
gradientName = '-%s-%s' % (prefix, gradientName)
|
||||
newValues = []
|
||||
for value in values:
|
||||
if isinstance(value, dict):
|
||||
if 'pos' in value:
|
||||
newValues.append(gradientName + '(%(pos)s, %(start)s, %(end)s)' % value)
|
||||
else:
|
||||
newValues.append(gradientName + '(%(start)s, %(end)s)' % value)
|
||||
else:
|
||||
newValues.append(value)
|
||||
return cssutils.css.Property(
|
||||
name=self.prop.name,
|
||||
value=', '.join(newValues),
|
||||
priority=self.prop.priority
|
||||
)
|
||||
|
||||
def get_prefixed_props(self, filt):
|
||||
values = list(self.__iter_values())
|
||||
needPrefix = False
|
||||
for value in values: # check if there are any gradients
|
||||
if isinstance(value, dict):
|
||||
needPrefix = True
|
||||
break
|
||||
if needPrefix:
|
||||
for prefix in [p for p in self.vendor_prefixes if p in filt]:
|
||||
yield self.__get_prefixed_prop(values, prefix)
|
||||
if prefix == 'webkit':
|
||||
newValues = []
|
||||
for value in values:
|
||||
if isinstance(value, dict):
|
||||
newValues.append('-webkit-gradient(linear, left top, left bottom, color-stop(0, %(start)s), color-stop(1, %(end)s))' % value)
|
||||
else:
|
||||
newValues.append(value)
|
||||
yield cssutils.css.Property(
|
||||
name=self.prop.name,
|
||||
value=', '.join(newValues),
|
||||
priority=self.prop.priority
|
||||
)
|
||||
else:
|
||||
yield None
|
||||
|
||||
def get_base_prop(self):
|
||||
values = self.__iter_values()
|
||||
return self.__get_prefixed_prop(values)
|
||||
|
||||
rules = {
|
||||
'border-radius': BaseReplacementRule,
|
||||
'border-top-left-radius': BorderRadiusReplacementRule,
|
||||
'border-top-right-radius': BorderRadiusReplacementRule,
|
||||
'border-bottom-right-radius': BorderRadiusReplacementRule,
|
||||
'border-bottom-left-radius': BorderRadiusReplacementRule,
|
||||
'border-image': FullReplacementRule,
|
||||
'box-shadow': BaseReplacementRule,
|
||||
'box-sizing': MozReplacementRule,
|
||||
'box-orient': BaseAndIEReplacementRule,
|
||||
'box-direction': BaseAndIEReplacementRule,
|
||||
'box-ordinal-group': BaseAndIEReplacementRule,
|
||||
'box-align': BaseAndIEReplacementRule,
|
||||
'box-flex': BaseAndIEReplacementRule,
|
||||
'box-flex-group': BaseReplacementRule,
|
||||
'box-pack': BaseAndIEReplacementRule,
|
||||
'box-lines': BaseAndIEReplacementRule,
|
||||
'user-select': BaseReplacementRule,
|
||||
'user-modify': BaseReplacementRule,
|
||||
'margin-start': BaseReplacementRule,
|
||||
'margin-end': BaseReplacementRule,
|
||||
'padding-start': BaseReplacementRule,
|
||||
'padding-end': BaseReplacementRule,
|
||||
'column-count': BaseReplacementRule,
|
||||
'column-gap': BaseReplacementRule,
|
||||
'column-rule': BaseReplacementRule,
|
||||
'column-rule-color': BaseReplacementRule,
|
||||
'column-rule-style': BaseReplacementRule,
|
||||
'column-rule-width': BaseReplacementRule,
|
||||
'column-span': WebkitReplacementRule,
|
||||
'column-width': BaseReplacementRule,
|
||||
'columns': WebkitReplacementRule,
|
||||
|
||||
'background-clip': WebkitReplacementRule,
|
||||
'background-origin': WebkitReplacementRule,
|
||||
'background-size': WebkitReplacementRule,
|
||||
'background-image': GradientReplacementRule,
|
||||
'background': GradientReplacementRule,
|
||||
|
||||
'text-overflow': OperaAndIEReplacementRule,
|
||||
|
||||
'transition': TransitionReplacementRule,
|
||||
'transition-delay': BaseAndOperaReplacementRule,
|
||||
'transition-duration': BaseAndOperaReplacementRule,
|
||||
'transition-property': TransitionReplacementRule,
|
||||
'transition-timing-function': BaseAndOperaReplacementRule,
|
||||
'transform': FullReplacementRule,
|
||||
'transform-origin': FullReplacementRule,
|
||||
|
||||
'display': DisplayReplacementRule,
|
||||
'appearance': WebkitReplacementRule,
|
||||
'hyphens': BaseReplacementRule,
|
||||
}
|
||||
385
libs/cssutils/__init__.py
Executable file
@@ -0,0 +1,385 @@
|
||||
#!/usr/bin/env python
|
||||
"""cssutils - CSS Cascading Style Sheets library for Python
|
||||
|
||||
Copyright (C) 2004-2013 Christof Hoeke
|
||||
|
||||
cssutils is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
A Python package to parse and build CSS Cascading Style Sheets. DOM only, not
|
||||
any rendering facilities!
|
||||
|
||||
Based upon and partly implementing the following specifications :
|
||||
|
||||
`CSS 2.1 <http://www.w3.org/TR/CSS2/>`__
|
||||
General CSS rules and properties are defined here
|
||||
`CSS 2.1 Errata <http://www.w3.org/Style/css2-updates/CR-CSS21-20070719-errata.html>`__
|
||||
A few errata, mainly the definition of CHARSET_SYM tokens
|
||||
`CSS3 Module: Syntax <http://www.w3.org/TR/css3-syntax/>`__
|
||||
Used in parts since cssutils 0.9.4. cssutils tries to use the features from
|
||||
CSS 2.1 and CSS 3 with preference to CSS3 but as this is not final yet some
|
||||
parts are from CSS 2.1
|
||||
`MediaQueries <http://www.w3.org/TR/css3-mediaqueries/>`__
|
||||
MediaQueries are part of ``stylesheets.MediaList`` since v0.9.4, used in
|
||||
@import and @media rules.
|
||||
`Namespaces <http://dev.w3.org/csswg/css3-namespace/>`__
|
||||
Added in v0.9.1, updated to definition in CSSOM in v0.9.4, updated in 0.9.5
|
||||
for dev version
|
||||
`CSS3 Module: Pages Media <http://www.w3.org/TR/css3-page/>`__
|
||||
Most properties of this spec are implemented including MarginRules
|
||||
`Selectors <http://www.w3.org/TR/css3-selectors/>`__
|
||||
The selector syntax defined here (and not in CSS 2.1) should be parsable
|
||||
with cssutils (*should* mind though ;) )
|
||||
|
||||
`DOM Level 2 Style CSS <http://www.w3.org/TR/DOM-Level-2-Style/css.html>`__
|
||||
DOM for package css. 0.9.8 removes support for CSSValue and related API,
|
||||
see PropertyValue and Value API for now
|
||||
`DOM Level 2 Style Stylesheets <http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html>`__
|
||||
DOM for package stylesheets
|
||||
`CSSOM <http://dev.w3.org/csswg/cssom/>`__
|
||||
A few details (mainly the NamespaceRule DOM) is taken from here. Plan is
|
||||
to move implementation to the stuff defined here which is newer but still
|
||||
no REC so might change anytime...
|
||||
|
||||
|
||||
The cssutils tokenizer is a customized implementation of `CSS3 Module: Syntax
|
||||
(W3C Working Draft 13 August 2003) <http://www.w3.org/TR/css3-syntax/>`__ which
|
||||
itself is based on the CSS 2.1 tokenizer. It tries to be as compliant as
|
||||
possible but uses some (helpful) parts of the CSS 2.1 tokenizer.
|
||||
|
||||
I guess cssutils is neither CSS 2.1 nor CSS 3 compliant but tries to at least
|
||||
be able to parse both grammars including some more real world cases (some CSS
|
||||
hacks are actually parsed and serialized). Both official grammars are not final
|
||||
nor bugfree but still feasible. cssutils aim is not to be fully compliant to
|
||||
any CSS specification (the specifications seem to be in a constant flow anyway)
|
||||
but cssutils *should* be able to read and write as many as possible CSS
|
||||
stylesheets "in the wild" while at the same time implement the official APIs
|
||||
which are well documented. Some minor extensions are provided as well.
|
||||
|
||||
Please visit http://cthedot.de/cssutils/ for more details.
|
||||
|
||||
|
||||
Tested with Python 2.7.3 and 3.3 on Windows 8 64bit.
|
||||
|
||||
|
||||
This library may be used ``from cssutils import *`` which
|
||||
import subpackages ``css`` and ``stylesheets``, CSSParser and
|
||||
CSSSerializer classes only.
|
||||
|
||||
Usage may be::
|
||||
|
||||
>>> from cssutils import *
|
||||
>>> parser = CSSParser()
|
||||
>>> sheet = parser.parseString(u'a { color: red}')
|
||||
>>> print sheet.cssText
|
||||
a {
|
||||
color: red
|
||||
}
|
||||
|
||||
"""
|
||||
__all__ = ['css', 'stylesheets', 'CSSParser', 'CSSSerializer']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__author__ = 'Christof Hoeke with contributions by Walter Doerwald'
|
||||
__date__ = '$LastChangedDate:: $:'
|
||||
|
||||
VERSION = '0.9.10'
|
||||
|
||||
__version__ = '%s $Id$' % VERSION
|
||||
|
||||
import sys
|
||||
if sys.version_info < (2,6):
|
||||
bytes = str
|
||||
|
||||
import codec
|
||||
import os.path
|
||||
import urllib
|
||||
import urlparse
|
||||
import xml.dom
|
||||
|
||||
# order of imports is important (partly circular)
|
||||
from . import util
|
||||
import errorhandler
|
||||
log = errorhandler.ErrorHandler()
|
||||
|
||||
import css
|
||||
import stylesheets
|
||||
from parse import CSSParser
|
||||
|
||||
from serialize import CSSSerializer
|
||||
ser = CSSSerializer()
|
||||
|
||||
from profiles import Profiles
|
||||
profile = Profiles(log=log)
|
||||
|
||||
# used by Selector defining namespace prefix '*'
|
||||
_ANYNS = -1
|
||||
|
||||
class DOMImplementationCSS(object):
|
||||
"""This interface allows the DOM user to create a CSSStyleSheet
|
||||
outside the context of a document. There is no way to associate
|
||||
the new CSSStyleSheet with a document in DOM Level 2.
|
||||
|
||||
This class is its *own factory*, as it is given to
|
||||
xml.dom.registerDOMImplementation which simply calls it and receives
|
||||
an instance of this class then.
|
||||
"""
|
||||
_features = [
|
||||
('css', '1.0'),
|
||||
('css', '2.0'),
|
||||
('stylesheets', '1.0'),
|
||||
('stylesheets', '2.0')
|
||||
]
|
||||
|
||||
def createCSSStyleSheet(self, title, media):
|
||||
"""
|
||||
Creates a new CSSStyleSheet.
|
||||
|
||||
title of type DOMString
|
||||
The advisory title. See also the Style Sheet Interfaces
|
||||
section.
|
||||
media of type DOMString
|
||||
The comma-separated list of media associated with the new style
|
||||
sheet. See also the Style Sheet Interfaces section.
|
||||
|
||||
returns
|
||||
CSSStyleSheet: A new CSS style sheet.
|
||||
|
||||
TODO: DOMException
|
||||
SYNTAX_ERR: Raised if the specified media string value has a
|
||||
syntax error and is unparsable.
|
||||
"""
|
||||
return css.CSSStyleSheet(title=title, media=media)
|
||||
|
||||
def createDocument(self, *args):
|
||||
# not needed to HTML, also not for CSS?
|
||||
raise NotImplementedError
|
||||
|
||||
def createDocumentType(self, *args):
|
||||
# not needed to HTML, also not for CSS?
|
||||
raise NotImplementedError
|
||||
|
||||
def hasFeature(self, feature, version):
|
||||
return (feature.lower(), unicode(version)) in self._features
|
||||
|
||||
xml.dom.registerDOMImplementation('cssutils', DOMImplementationCSS)
|
||||
|
||||
|
||||
def parseString(*a, **k):
|
||||
return CSSParser().parseString(*a, **k)
|
||||
parseString.__doc__ = CSSParser.parseString.__doc__
|
||||
|
||||
def parseFile(*a, **k):
|
||||
return CSSParser().parseFile(*a, **k)
|
||||
parseFile.__doc__ = CSSParser.parseFile.__doc__
|
||||
|
||||
def parseUrl(*a, **k):
|
||||
return CSSParser().parseUrl(*a, **k)
|
||||
parseUrl.__doc__ = CSSParser.parseUrl.__doc__
|
||||
|
||||
def parseStyle(*a, **k):
|
||||
return CSSParser().parseStyle(*a, **k)
|
||||
parseStyle.__doc__ = CSSParser.parseStyle.__doc__
|
||||
|
||||
# set "ser", default serializer
|
||||
def setSerializer(serializer):
|
||||
"""Set the global serializer used by all class in cssutils."""
|
||||
global ser
|
||||
ser = serializer
|
||||
|
||||
def getUrls(sheet):
|
||||
"""Retrieve all ``url(urlstring)`` values (in e.g.
|
||||
:class:`cssutils.css.CSSImportRule` or :class:`cssutils.css.CSSValue`
|
||||
objects of given `sheet`.
|
||||
|
||||
:param sheet:
|
||||
:class:`cssutils.css.CSSStyleSheet` object whose URLs are yielded
|
||||
|
||||
This function is a generator. The generated URL values exclude ``url(`` and
|
||||
``)`` and surrounding single or double quotes.
|
||||
"""
|
||||
for importrule in (r for r in sheet if r.type == r.IMPORT_RULE):
|
||||
yield importrule.href
|
||||
|
||||
def styleDeclarations(base):
|
||||
"recursive generator to find all CSSStyleDeclarations"
|
||||
if hasattr(base, 'cssRules'):
|
||||
for rule in base.cssRules:
|
||||
for s in styleDeclarations(rule):
|
||||
yield s
|
||||
elif hasattr(base, 'style'):
|
||||
yield base.style
|
||||
|
||||
for style in styleDeclarations(sheet):
|
||||
for p in style.getProperties(all=True):
|
||||
for v in p.propertyValue:
|
||||
if v.type == 'URI':
|
||||
yield v.uri
|
||||
|
||||
def replaceUrls(sheetOrStyle, replacer, ignoreImportRules=False):
|
||||
"""Replace all URLs in :class:`cssutils.css.CSSImportRule` or
|
||||
:class:`cssutils.css.CSSValue` objects of given `sheetOrStyle`.
|
||||
|
||||
:param sheetOrStyle:
|
||||
a :class:`cssutils.css.CSSStyleSheet` or a
|
||||
:class:`cssutils.css.CSSStyleDeclaration` which is changed in place
|
||||
:param replacer:
|
||||
a function which is called with a single argument `url` which
|
||||
is the current value of each url() excluding ``url(``, ``)`` and
|
||||
surrounding (single or double) quotes.
|
||||
:param ignoreImportRules:
|
||||
if ``True`` does not call `replacer` with URLs from @import rules.
|
||||
"""
|
||||
if not ignoreImportRules and not isinstance(sheetOrStyle,
|
||||
css.CSSStyleDeclaration):
|
||||
for importrule in (r for r in sheetOrStyle if r.type == r.IMPORT_RULE):
|
||||
importrule.href = replacer(importrule.href)
|
||||
|
||||
def styleDeclarations(base):
|
||||
"recursive generator to find all CSSStyleDeclarations"
|
||||
if hasattr(base, 'cssRules'):
|
||||
for rule in base.cssRules:
|
||||
for s in styleDeclarations(rule):
|
||||
yield s
|
||||
elif hasattr(base, 'style'):
|
||||
yield base.style
|
||||
elif isinstance(sheetOrStyle, css.CSSStyleDeclaration):
|
||||
# base is a style already
|
||||
yield base
|
||||
|
||||
for style in styleDeclarations(sheetOrStyle):
|
||||
for p in style.getProperties(all=True):
|
||||
for v in p.propertyValue:
|
||||
if v.type == v.URI:
|
||||
v.uri = replacer(v.uri)
|
||||
|
||||
def resolveImports(sheet, target=None):
|
||||
"""Recurcively combine all rules in given `sheet` into a `target` sheet.
|
||||
@import rules which use media information are tried to be wrapped into
|
||||
@media rules so keeping the media information. This may not work in
|
||||
all instances (if e.g. an @import rule itself contains an @import rule
|
||||
with different media infos or if it contains rules which may not be
|
||||
used inside an @media block like @namespace rules.). In these cases
|
||||
the @import rule is kept as in the original sheet and a WARNING is issued.
|
||||
|
||||
:param sheet:
|
||||
in this given :class:`cssutils.css.CSSStyleSheet` all import rules are
|
||||
resolved and added to a resulting *flat* sheet.
|
||||
:param target:
|
||||
A :class:`cssutils.css.CSSStyleSheet` object which will be the
|
||||
resulting *flat* sheet if given
|
||||
:returns: given `target` or a new :class:`cssutils.css.CSSStyleSheet`
|
||||
object
|
||||
"""
|
||||
if not target:
|
||||
target = css.CSSStyleSheet(href=sheet.href,
|
||||
media=sheet.media,
|
||||
title=sheet.title)
|
||||
|
||||
def getReplacer(targetbase):
|
||||
"Return a replacer which uses base to return adjusted URLs"
|
||||
basesch, baseloc, basepath, basequery, basefrag = urlparse.urlsplit(targetbase)
|
||||
basepath, basepathfilename = os.path.split(basepath)
|
||||
|
||||
def replacer(uri):
|
||||
scheme, location, path, query, fragment = urlparse.urlsplit(uri)
|
||||
if not scheme and not location and not path.startswith(u'/'):
|
||||
# relative
|
||||
path, filename = os.path.split(path)
|
||||
combined = os.path.normpath(os.path.join(basepath, path, filename))
|
||||
return urllib.pathname2url(combined)
|
||||
else:
|
||||
# keep anything absolute
|
||||
return uri
|
||||
|
||||
return replacer
|
||||
|
||||
for rule in sheet.cssRules:
|
||||
if rule.type == rule.CHARSET_RULE:
|
||||
pass
|
||||
elif rule.type == rule.IMPORT_RULE:
|
||||
log.info(u'Processing @import %r' % rule.href, neverraise=True)
|
||||
|
||||
if rule.hrefFound:
|
||||
# add all rules of @import to current sheet
|
||||
target.add(css.CSSComment(cssText=u'/* START @import "%s" */'
|
||||
% rule.href))
|
||||
|
||||
try:
|
||||
# nested imports
|
||||
importedSheet = resolveImports(rule.styleSheet)
|
||||
except xml.dom.HierarchyRequestErr, e:
|
||||
log.warn(u'@import: Cannot resolve target, keeping rule: %s'
|
||||
% e, neverraise=True)
|
||||
target.add(rule)
|
||||
else:
|
||||
# adjust relative URI references
|
||||
log.info(u'@import: Adjusting paths for %r' % rule.href,
|
||||
neverraise=True)
|
||||
replaceUrls(importedSheet,
|
||||
getReplacer(rule.href),
|
||||
ignoreImportRules=True)
|
||||
|
||||
# might have to wrap rules in @media if media given
|
||||
if rule.media.mediaText == u'all':
|
||||
mediaproxy = None
|
||||
else:
|
||||
keepimport = False
|
||||
for r in importedSheet:
|
||||
# check if rules present which may not be
|
||||
# combined with media
|
||||
if r.type not in (r.COMMENT,
|
||||
r.STYLE_RULE,
|
||||
r.IMPORT_RULE):
|
||||
keepimport = True
|
||||
break
|
||||
if keepimport:
|
||||
log.warn(u'Cannot combine imported sheet with'
|
||||
u' given media as other rules then'
|
||||
u' comments or stylerules found %r,'
|
||||
u' keeping %r' % (r,
|
||||
rule.cssText),
|
||||
neverraise=True)
|
||||
target.add(rule)
|
||||
continue
|
||||
|
||||
# wrap in @media if media is not `all`
|
||||
log.info(u'@import: Wrapping some rules in @media '
|
||||
u' to keep media: %s'
|
||||
% rule.media.mediaText, neverraise=True)
|
||||
mediaproxy = css.CSSMediaRule(rule.media.mediaText)
|
||||
|
||||
for r in importedSheet:
|
||||
if mediaproxy:
|
||||
mediaproxy.add(r)
|
||||
else:
|
||||
# add to top sheet directly but are difficult anyway
|
||||
target.add(r)
|
||||
|
||||
if mediaproxy:
|
||||
target.add(mediaproxy)
|
||||
|
||||
else:
|
||||
# keep @import as it is
|
||||
log.error(u'Cannot get referenced stylesheet %r, keeping rule'
|
||||
% rule.href, neverraise=True)
|
||||
target.add(rule)
|
||||
|
||||
else:
|
||||
target.add(rule)
|
||||
|
||||
return target
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print __doc__
|
||||
584
libs/cssutils/_codec2.py
Executable file
@@ -0,0 +1,584 @@
|
||||
#!/usr/bin/env python
|
||||
"""Python codec for CSS."""
|
||||
__docformat__ = 'restructuredtext'
|
||||
__author__ = 'Walter Doerwald'
|
||||
__version__ = '$Id: util.py 1114 2008-03-05 13:22:59Z cthedot $'
|
||||
|
||||
import codecs
|
||||
import marshal
|
||||
|
||||
# We're using bits to store all possible candidate encodings (or variants, i.e.
|
||||
# we have two bits for the variants of UTF-16 and two for the
|
||||
# variants of UTF-32).
|
||||
#
|
||||
# Prefixes for various CSS encodings
|
||||
# UTF-8-SIG xEF xBB xBF
|
||||
# UTF-16 (LE) xFF xFE ~x00|~x00
|
||||
# UTF-16 (BE) xFE xFF
|
||||
# UTF-16-LE @ x00 @ x00
|
||||
# UTF-16-BE x00 @
|
||||
# UTF-32 (LE) xFF xFE x00 x00
|
||||
# UTF-32 (BE) x00 x00 xFE xFF
|
||||
# UTF-32-LE @ x00 x00 x00
|
||||
# UTF-32-BE x00 x00 x00 @
|
||||
# CHARSET @ c h a ...
|
||||
|
||||
|
||||
def detectencoding_str(input, final=False):
|
||||
"""
|
||||
Detect the encoding of the byte string ``input``, which contains the
|
||||
beginning of a CSS file. This function returns the detected encoding (or
|
||||
``None`` if it hasn't got enough data), and a flag that indicates whether
|
||||
that encoding has been detected explicitely or implicitely. To detect the
|
||||
encoding the first few bytes are used (or if ``input`` is ASCII compatible
|
||||
and starts with a charset rule the encoding name from the rule). "Explicit"
|
||||
detection means that the bytes start with a BOM or a charset rule.
|
||||
|
||||
If the encoding can't be detected yet, ``None`` is returned as the encoding.
|
||||
``final`` specifies whether more data will be available in later calls or
|
||||
not. If ``final`` is true, ``detectencoding_str()`` will never return
|
||||
``None`` as the encoding.
|
||||
"""
|
||||
|
||||
# A bit for every candidate
|
||||
CANDIDATE_UTF_8_SIG = 1
|
||||
CANDIDATE_UTF_16_AS_LE = 2
|
||||
CANDIDATE_UTF_16_AS_BE = 4
|
||||
CANDIDATE_UTF_16_LE = 8
|
||||
CANDIDATE_UTF_16_BE = 16
|
||||
CANDIDATE_UTF_32_AS_LE = 32
|
||||
CANDIDATE_UTF_32_AS_BE = 64
|
||||
CANDIDATE_UTF_32_LE = 128
|
||||
CANDIDATE_UTF_32_BE = 256
|
||||
CANDIDATE_CHARSET = 512
|
||||
|
||||
candidates = 1023 # all candidates
|
||||
|
||||
li = len(input)
|
||||
if li>=1:
|
||||
# Check first byte
|
||||
c = input[0]
|
||||
if c != "\xef":
|
||||
candidates &= ~CANDIDATE_UTF_8_SIG
|
||||
if c != "\xff":
|
||||
candidates &= ~(CANDIDATE_UTF_32_AS_LE|CANDIDATE_UTF_16_AS_LE)
|
||||
if c != "\xfe":
|
||||
candidates &= ~CANDIDATE_UTF_16_AS_BE
|
||||
if c != "@":
|
||||
candidates &= ~(CANDIDATE_UTF_32_LE|CANDIDATE_UTF_16_LE|CANDIDATE_CHARSET)
|
||||
if c != "\x00":
|
||||
candidates &= ~(CANDIDATE_UTF_32_AS_BE|CANDIDATE_UTF_32_BE|CANDIDATE_UTF_16_BE)
|
||||
if li>=2:
|
||||
# Check second byte
|
||||
c = input[1]
|
||||
if c != "\xbb":
|
||||
candidates &= ~CANDIDATE_UTF_8_SIG
|
||||
if c != "\xfe":
|
||||
candidates &= ~(CANDIDATE_UTF_16_AS_LE|CANDIDATE_UTF_32_AS_LE)
|
||||
if c != "\xff":
|
||||
candidates &= ~CANDIDATE_UTF_16_AS_BE
|
||||
if c != "\x00":
|
||||
candidates &= ~(CANDIDATE_UTF_16_LE|CANDIDATE_UTF_32_AS_BE|CANDIDATE_UTF_32_LE|CANDIDATE_UTF_32_BE)
|
||||
if c != "@":
|
||||
candidates &= ~CANDIDATE_UTF_16_BE
|
||||
if c != "c":
|
||||
candidates &= ~CANDIDATE_CHARSET
|
||||
if li>=3:
|
||||
# Check third byte
|
||||
c = input[2]
|
||||
if c != "\xbf":
|
||||
candidates &= ~CANDIDATE_UTF_8_SIG
|
||||
if c != "c":
|
||||
candidates &= ~CANDIDATE_UTF_16_LE
|
||||
if c != "\x00":
|
||||
candidates &= ~(CANDIDATE_UTF_32_AS_LE|CANDIDATE_UTF_32_LE|CANDIDATE_UTF_32_BE)
|
||||
if c != "\xfe":
|
||||
candidates &= ~CANDIDATE_UTF_32_AS_BE
|
||||
if c != "h":
|
||||
candidates &= ~CANDIDATE_CHARSET
|
||||
if li>=4:
|
||||
# Check fourth byte
|
||||
c = input[3]
|
||||
if input[2:4] == "\x00\x00":
|
||||
candidates &= ~CANDIDATE_UTF_16_AS_LE
|
||||
if c != "\x00":
|
||||
candidates &= ~(CANDIDATE_UTF_16_LE|CANDIDATE_UTF_32_AS_LE|CANDIDATE_UTF_32_LE)
|
||||
if c != "\xff":
|
||||
candidates &= ~CANDIDATE_UTF_32_AS_BE
|
||||
if c != "@":
|
||||
candidates &= ~CANDIDATE_UTF_32_BE
|
||||
if c != "a":
|
||||
candidates &= ~CANDIDATE_CHARSET
|
||||
if candidates == 0:
|
||||
return ("utf-8", False)
|
||||
if not (candidates & (candidates-1)): # only one candidate remaining
|
||||
if candidates == CANDIDATE_UTF_8_SIG and li >= 3:
|
||||
return ("utf-8-sig", True)
|
||||
elif candidates == CANDIDATE_UTF_16_AS_LE and li >= 2:
|
||||
return ("utf-16", True)
|
||||
elif candidates == CANDIDATE_UTF_16_AS_BE and li >= 2:
|
||||
return ("utf-16", True)
|
||||
elif candidates == CANDIDATE_UTF_16_LE and li >= 4:
|
||||
return ("utf-16-le", False)
|
||||
elif candidates == CANDIDATE_UTF_16_BE and li >= 2:
|
||||
return ("utf-16-be", False)
|
||||
elif candidates == CANDIDATE_UTF_32_AS_LE and li >= 4:
|
||||
return ("utf-32", True)
|
||||
elif candidates == CANDIDATE_UTF_32_AS_BE and li >= 4:
|
||||
return ("utf-32", True)
|
||||
elif candidates == CANDIDATE_UTF_32_LE and li >= 4:
|
||||
return ("utf-32-le", False)
|
||||
elif candidates == CANDIDATE_UTF_32_BE and li >= 4:
|
||||
return ("utf-32-be", False)
|
||||
elif candidates == CANDIDATE_CHARSET and li >= 4:
|
||||
prefix = '@charset "'
|
||||
if input[:len(prefix)] == prefix:
|
||||
pos = input.find('"', len(prefix))
|
||||
if pos >= 0:
|
||||
return (input[len(prefix):pos], True)
|
||||
# if this is the last call, and we haven't determined an encoding yet,
|
||||
# we default to UTF-8
|
||||
if final:
|
||||
return ("utf-8", False)
|
||||
return (None, False) # dont' know yet
|
||||
|
||||
|
||||
def detectencoding_unicode(input, final=False):
|
||||
"""
|
||||
Detect the encoding of the unicode string ``input``, which contains the
|
||||
beginning of a CSS file. The encoding is detected from the charset rule
|
||||
at the beginning of ``input``. If there is no charset rule, ``"utf-8"``
|
||||
will be returned.
|
||||
|
||||
If the encoding can't be detected yet, ``None`` is returned. ``final``
|
||||
specifies whether more data will be available in later calls or not. If
|
||||
``final`` is true, ``detectencoding_unicode()`` will never return ``None``.
|
||||
"""
|
||||
prefix = u'@charset "'
|
||||
if input.startswith(prefix):
|
||||
pos = input.find(u'"', len(prefix))
|
||||
if pos >= 0:
|
||||
return (input[len(prefix):pos], True)
|
||||
elif final or not prefix.startswith(input):
|
||||
# if this is the last call, and we haven't determined an encoding yet,
|
||||
# (or the string definitely doesn't start with prefix) we default to UTF-8
|
||||
return ("utf-8", False)
|
||||
return (None, False) # don't know yet
|
||||
|
||||
|
||||
def _fixencoding(input, encoding, final=False):
|
||||
"""
|
||||
Replace the name of the encoding in the charset rule at the beginning of
|
||||
``input`` with ``encoding``. If ``input`` doesn't starts with a charset
|
||||
rule, ``input`` will be returned unmodified.
|
||||
|
||||
If the encoding can't be found yet, ``None`` is returned. ``final``
|
||||
specifies whether more data will be available in later calls or not.
|
||||
If ``final`` is true, ``_fixencoding()`` will never return ``None``.
|
||||
"""
|
||||
prefix = u'@charset "'
|
||||
if len(input) > len(prefix):
|
||||
if input.startswith(prefix):
|
||||
pos = input.find(u'"', len(prefix))
|
||||
if pos >= 0:
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
encoding = u"utf-8"
|
||||
return prefix + encoding + input[pos:]
|
||||
# we haven't seen the end of the encoding name yet => fall through
|
||||
else:
|
||||
return input # doesn't start with prefix, so nothing to fix
|
||||
elif not prefix.startswith(input) or final:
|
||||
# can't turn out to be a @charset rule later (or there is no "later")
|
||||
return input
|
||||
if final:
|
||||
return input
|
||||
return None # don't know yet
|
||||
|
||||
|
||||
def decode(input, errors="strict", encoding=None, force=True):
|
||||
if encoding is None or not force:
|
||||
(_encoding, explicit) = detectencoding_str(input, True)
|
||||
if _encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
if (explicit and not force) or encoding is None: # Take the encoding from the input
|
||||
encoding = _encoding
|
||||
(input, consumed) = codecs.getdecoder(encoding)(input, errors)
|
||||
return (_fixencoding(input, unicode(encoding), True), consumed)
|
||||
|
||||
|
||||
def encode(input, errors="strict", encoding=None):
|
||||
consumed = len(input)
|
||||
if encoding is None:
|
||||
encoding = detectencoding_unicode(input, True)[0]
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
input = _fixencoding(input, u"utf-8", True)
|
||||
else:
|
||||
input = _fixencoding(input, unicode(encoding), True)
|
||||
if encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
encoder = codecs.getencoder(encoding)
|
||||
return (encoder(input, errors)[0], consumed)
|
||||
|
||||
|
||||
def _bytes2int(bytes):
|
||||
# Helper: convert an 8 bit string into an ``int``.
|
||||
i = 0
|
||||
for byte in bytes:
|
||||
i = (i<<8) + ord(byte)
|
||||
return i
|
||||
|
||||
|
||||
def _int2bytes(i):
|
||||
# Helper: convert an ``int`` into an 8-bit string.
|
||||
v = []
|
||||
while i:
|
||||
v.insert(0, chr(i&0xff))
|
||||
i >>= 8
|
||||
return "".join(v)
|
||||
|
||||
|
||||
if hasattr(codecs, "IncrementalDecoder"):
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def __init__(self, errors="strict", encoding=None, force=True):
|
||||
self.decoder = None
|
||||
self.encoding = encoding
|
||||
self.force = force
|
||||
codecs.IncrementalDecoder.__init__(self, errors)
|
||||
# Store ``errors`` somewhere else,
|
||||
# because we have to hide it in a property
|
||||
self._errors = errors
|
||||
self.buffer = u"".encode()
|
||||
self.headerfixed = False
|
||||
|
||||
def iterdecode(self, input):
|
||||
for part in input:
|
||||
result = self.decode(part, False)
|
||||
if result:
|
||||
yield result
|
||||
result = self.decode("", True)
|
||||
if result:
|
||||
yield result
|
||||
|
||||
def decode(self, input, final=False):
|
||||
# We're doing basically the same as a ``BufferedIncrementalDecoder``,
|
||||
# but since the buffer is only relevant until the encoding has been
|
||||
# detected (in which case the buffer of the underlying codec might
|
||||
# kick in), we're implementing buffering ourselves to avoid some
|
||||
# overhead.
|
||||
if self.decoder is None:
|
||||
input = self.buffer + input
|
||||
# Do we have to detect the encoding from the input?
|
||||
if self.encoding is None or not self.force:
|
||||
(encoding, explicit) = detectencoding_str(input, final)
|
||||
if encoding is None: # no encoding determined yet
|
||||
self.buffer = input # retry the complete input on the next call
|
||||
return u"" # no encoding determined yet, so no output
|
||||
elif encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
if (explicit and not self.force) or self.encoding is None: # Take the encoding from the input
|
||||
self.encoding = encoding
|
||||
self.buffer = "" # drop buffer, as the decoder might keep its own
|
||||
decoder = codecs.getincrementaldecoder(self.encoding)
|
||||
self.decoder = decoder(self._errors)
|
||||
if self.headerfixed:
|
||||
return self.decoder.decode(input, final)
|
||||
# If we haven't fixed the header yet,
|
||||
# the content of ``self.buffer`` is a ``unicode`` object
|
||||
output = self.buffer + self.decoder.decode(input, final)
|
||||
encoding = self.encoding
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
encoding = "utf-8"
|
||||
newoutput = _fixencoding(output, unicode(encoding), final)
|
||||
if newoutput is None:
|
||||
# retry fixing the @charset rule (but keep the decoded stuff)
|
||||
self.buffer = output
|
||||
return u""
|
||||
self.headerfixed = True
|
||||
return newoutput
|
||||
|
||||
def reset(self):
|
||||
codecs.IncrementalDecoder.reset(self)
|
||||
self.decoder = None
|
||||
self.buffer = u"".encode()
|
||||
self.headerfixed = False
|
||||
|
||||
def _geterrors(self):
|
||||
return self._errors
|
||||
|
||||
def _seterrors(self, errors):
|
||||
# Setting ``errors`` must be done on the real decoder too
|
||||
if self.decoder is not None:
|
||||
self.decoder.errors = errors
|
||||
self._errors = errors
|
||||
errors = property(_geterrors, _seterrors)
|
||||
|
||||
def getstate(self):
|
||||
if self.decoder is not None:
|
||||
state = (self.encoding, self.buffer, self.headerfixed, True, self.decoder.getstate())
|
||||
else:
|
||||
state = (self.encoding, self.buffer, self.headerfixed, False, None)
|
||||
return ("", _bytes2int(marshal.dumps(state)))
|
||||
|
||||
def setstate(self, state):
|
||||
state = _int2bytes(marshal.loads(state[1])) # ignore buffered input
|
||||
self.encoding = state[0]
|
||||
self.buffer = state[1]
|
||||
self.headerfixed = state[2]
|
||||
if state[3] is not None:
|
||||
self.decoder = codecs.getincrementaldecoder(self.encoding)(self._errors)
|
||||
self.decoder.setstate(state[4])
|
||||
else:
|
||||
self.decoder = None
|
||||
|
||||
|
||||
if hasattr(codecs, "IncrementalEncoder"):
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def __init__(self, errors="strict", encoding=None):
|
||||
self.encoder = None
|
||||
self.encoding = encoding
|
||||
codecs.IncrementalEncoder.__init__(self, errors)
|
||||
# Store ``errors`` somewhere else,
|
||||
# because we have to hide it in a property
|
||||
self._errors = errors
|
||||
self.buffer = u""
|
||||
|
||||
def iterencode(self, input):
|
||||
for part in input:
|
||||
result = self.encode(part, False)
|
||||
if result:
|
||||
yield result
|
||||
result = self.encode(u"", True)
|
||||
if result:
|
||||
yield result
|
||||
|
||||
def encode(self, input, final=False):
|
||||
if self.encoder is None:
|
||||
input = self.buffer + input
|
||||
if self.encoding is not None:
|
||||
# Replace encoding in the @charset rule with the specified one
|
||||
encoding = self.encoding
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
encoding = "utf-8"
|
||||
newinput = _fixencoding(input, unicode(encoding), final)
|
||||
if newinput is None: # @charset rule incomplete => Retry next time
|
||||
self.buffer = input
|
||||
return ""
|
||||
input = newinput
|
||||
else:
|
||||
# Use encoding from the @charset declaration
|
||||
self.encoding = detectencoding_unicode(input, final)[0]
|
||||
if self.encoding is not None:
|
||||
if self.encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
info = codecs.lookup(self.encoding)
|
||||
encoding = self.encoding
|
||||
if self.encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
input = _fixencoding(input, u"utf-8", True)
|
||||
self.encoder = info.incrementalencoder(self._errors)
|
||||
self.buffer = u""
|
||||
else:
|
||||
self.buffer = input
|
||||
return ""
|
||||
return self.encoder.encode(input, final)
|
||||
|
||||
def reset(self):
|
||||
codecs.IncrementalEncoder.reset(self)
|
||||
self.encoder = None
|
||||
self.buffer = u""
|
||||
|
||||
def _geterrors(self):
|
||||
return self._errors
|
||||
|
||||
def _seterrors(self, errors):
|
||||
# Setting ``errors ``must be done on the real encoder too
|
||||
if self.encoder is not None:
|
||||
self.encoder.errors = errors
|
||||
self._errors = errors
|
||||
errors = property(_geterrors, _seterrors)
|
||||
|
||||
def getstate(self):
|
||||
if self.encoder is not None:
|
||||
state = (self.encoding, self.buffer, True, self.encoder.getstate())
|
||||
else:
|
||||
state = (self.encoding, self.buffer, False, None)
|
||||
return _bytes2int(marshal.dumps(state))
|
||||
|
||||
def setstate(self, state):
|
||||
state = _int2bytes(marshal.loads(state))
|
||||
self.encoding = state[0]
|
||||
self.buffer = state[1]
|
||||
if state[2] is not None:
|
||||
self.encoder = codecs.getincrementalencoder(self.encoding)(self._errors)
|
||||
self.encoder.setstate(state[4])
|
||||
else:
|
||||
self.encoder = None
|
||||
|
||||
|
||||
class StreamWriter(codecs.StreamWriter):
|
||||
def __init__(self, stream, errors="strict", encoding=None, header=False):
|
||||
codecs.StreamWriter.__init__(self, stream, errors)
|
||||
self.streamwriter = None
|
||||
self.encoding = encoding
|
||||
self._errors = errors
|
||||
self.buffer = u""
|
||||
|
||||
def encode(self, input, errors='strict'):
|
||||
li = len(input)
|
||||
if self.streamwriter is None:
|
||||
input = self.buffer + input
|
||||
li = len(input)
|
||||
if self.encoding is not None:
|
||||
# Replace encoding in the @charset rule with the specified one
|
||||
encoding = self.encoding
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
encoding = "utf-8"
|
||||
newinput = _fixencoding(input, unicode(encoding), False)
|
||||
if newinput is None: # @charset rule incomplete => Retry next time
|
||||
self.buffer = input
|
||||
return ("", 0)
|
||||
input = newinput
|
||||
else:
|
||||
# Use encoding from the @charset declaration
|
||||
self.encoding = detectencoding_unicode(input, False)[0]
|
||||
if self.encoding is not None:
|
||||
if self.encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
self.streamwriter = codecs.getwriter(self.encoding)(self.stream, self._errors)
|
||||
encoding = self.encoding
|
||||
if self.encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
input = _fixencoding(input, u"utf-8", True)
|
||||
self.buffer = u""
|
||||
else:
|
||||
self.buffer = input
|
||||
return ("", 0)
|
||||
return (self.streamwriter.encode(input, errors)[0], li)
|
||||
|
||||
def _geterrors(self):
|
||||
return self._errors
|
||||
|
||||
def _seterrors(self, errors):
|
||||
# Setting ``errors`` must be done on the streamwriter too
|
||||
if self.streamwriter is not None:
|
||||
self.streamwriter.errors = errors
|
||||
self._errors = errors
|
||||
|
||||
errors = property(_geterrors, _seterrors)
|
||||
|
||||
|
||||
class StreamReader(codecs.StreamReader):
|
||||
def __init__(self, stream, errors="strict", encoding=None, force=True):
|
||||
codecs.StreamReader.__init__(self, stream, errors)
|
||||
self.streamreader = None
|
||||
self.encoding = encoding
|
||||
self.force = force
|
||||
self._errors = errors
|
||||
|
||||
def decode(self, input, errors='strict'):
|
||||
if self.streamreader is None:
|
||||
if self.encoding is None or not self.force:
|
||||
(encoding, explicit) = detectencoding_str(input, False)
|
||||
if encoding is None: # no encoding determined yet
|
||||
return (u"", 0) # no encoding determined yet, so no output
|
||||
elif encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
if (explicit and not self.force) or self.encoding is None: # Take the encoding from the input
|
||||
self.encoding = encoding
|
||||
streamreader = codecs.getreader(self.encoding)
|
||||
streamreader = streamreader(self.stream, self._errors)
|
||||
(output, consumed) = streamreader.decode(input, errors)
|
||||
encoding = self.encoding
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
encoding = "utf-8"
|
||||
newoutput = _fixencoding(output, unicode(encoding), False)
|
||||
if newoutput is not None:
|
||||
self.streamreader = streamreader
|
||||
return (newoutput, consumed)
|
||||
return (u"", 0) # we will create a new streamreader on the next call
|
||||
return self.streamreader.decode(input, errors)
|
||||
|
||||
def _geterrors(self):
|
||||
return self._errors
|
||||
|
||||
def _seterrors(self, errors):
|
||||
# Setting ``errors`` must be done on the streamreader too
|
||||
if self.streamreader is not None:
|
||||
self.streamreader.errors = errors
|
||||
self._errors = errors
|
||||
|
||||
errors = property(_geterrors, _seterrors)
|
||||
|
||||
|
||||
if hasattr(codecs, "CodecInfo"):
|
||||
# We're running on Python 2.5 or better
|
||||
def search_function(name):
|
||||
if name == "css":
|
||||
return codecs.CodecInfo(
|
||||
name="css",
|
||||
encode=encode,
|
||||
decode=decode,
|
||||
incrementalencoder=IncrementalEncoder,
|
||||
incrementaldecoder=IncrementalDecoder,
|
||||
streamwriter=StreamWriter,
|
||||
streamreader=StreamReader,
|
||||
)
|
||||
else:
|
||||
# If we're running on Python 2.4, define the utf-8-sig codec here
|
||||
def utf8sig_encode(input, errors='strict'):
|
||||
return (codecs.BOM_UTF8 + codecs.utf_8_encode(input, errors)[0], len(input))
|
||||
|
||||
def utf8sig_decode(input, errors='strict'):
|
||||
prefix = 0
|
||||
if input[:3] == codecs.BOM_UTF8:
|
||||
input = input[3:]
|
||||
prefix = 3
|
||||
(output, consumed) = codecs.utf_8_decode(input, errors, True)
|
||||
return (output, consumed+prefix)
|
||||
|
||||
class UTF8SigStreamWriter(codecs.StreamWriter):
|
||||
def reset(self):
|
||||
codecs.StreamWriter.reset(self)
|
||||
try:
|
||||
del self.encode
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def encode(self, input, errors='strict'):
|
||||
self.encode = codecs.utf_8_encode
|
||||
return utf8sig_encode(input, errors)
|
||||
|
||||
class UTF8SigStreamReader(codecs.StreamReader):
|
||||
def reset(self):
|
||||
codecs.StreamReader.reset(self)
|
||||
try:
|
||||
del self.decode
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def decode(self, input, errors='strict'):
|
||||
if len(input) < 3 and codecs.BOM_UTF8.startswith(input):
|
||||
# not enough data to decide if this is a BOM
|
||||
# => try again on the next call
|
||||
return (u"", 0)
|
||||
self.decode = codecs.utf_8_decode
|
||||
return utf8sig_decode(input, errors)
|
||||
|
||||
def search_function(name):
|
||||
import encodings
|
||||
name = encodings.normalize_encoding(name)
|
||||
if name == "css":
|
||||
return (encode, decode, StreamReader, StreamWriter)
|
||||
elif name == "utf_8_sig":
|
||||
return (utf8sig_encode, utf8sig_decode, UTF8SigStreamReader, UTF8SigStreamWriter)
|
||||
|
||||
|
||||
codecs.register(search_function)
|
||||
|
||||
|
||||
# Error handler for CSS escaping
|
||||
|
||||
def cssescape(exc):
|
||||
if not isinstance(exc, UnicodeEncodeError):
|
||||
raise TypeError("don't know how to handle %r" % exc)
|
||||
return (u"".join(u"\\%06x" % ord(c) for c in exc.object[exc.start:exc.end]), exc.end)
|
||||
|
||||
codecs.register_error("cssescape", cssescape)
|
||||
608
libs/cssutils/_codec3.py
Executable file
@@ -0,0 +1,608 @@
|
||||
#!/usr/bin/env python
|
||||
"""Python codec for CSS."""
|
||||
__docformat__ = 'restructuredtext'
|
||||
__author__ = 'Walter Doerwald'
|
||||
__version__ = '$Id: util.py 1114 2008-03-05 13:22:59Z cthedot $'
|
||||
|
||||
import sys
|
||||
import codecs
|
||||
import marshal
|
||||
|
||||
# We're using bits to store all possible candidate encodings (or variants, i.e.
|
||||
# we have two bits for the variants of UTF-16 and two for the
|
||||
# variants of UTF-32).
|
||||
#
|
||||
# Prefixes for various CSS encodings
|
||||
# UTF-8-SIG xEF xBB xBF
|
||||
# UTF-16 (LE) xFF xFE ~x00|~x00
|
||||
# UTF-16 (BE) xFE xFF
|
||||
# UTF-16-LE @ x00 @ x00
|
||||
# UTF-16-BE x00 @
|
||||
# UTF-32 (LE) xFF xFE x00 x00
|
||||
# UTF-32 (BE) x00 x00 xFE xFF
|
||||
# UTF-32-LE @ x00 x00 x00
|
||||
# UTF-32-BE x00 x00 x00 @
|
||||
# CHARSET @ c h a ...
|
||||
|
||||
|
||||
def chars(bytestring):
|
||||
return ''.join(chr(byte) for byte in bytestring)
|
||||
|
||||
|
||||
def detectencoding_str(input, final=False):
|
||||
"""
|
||||
Detect the encoding of the byte string ``input``, which contains the
|
||||
beginning of a CSS file. This function returns the detected encoding (or
|
||||
``None`` if it hasn't got enough data), and a flag that indicates whether
|
||||
that encoding has been detected explicitely or implicitely. To detect the
|
||||
encoding the first few bytes are used (or if ``input`` is ASCII compatible
|
||||
and starts with a charset rule the encoding name from the rule). "Explicit"
|
||||
detection means that the bytes start with a BOM or a charset rule.
|
||||
|
||||
If the encoding can't be detected yet, ``None`` is returned as the encoding.
|
||||
``final`` specifies whether more data will be available in later calls or
|
||||
not. If ``final`` is true, ``detectencoding_str()`` will never return
|
||||
``None`` as the encoding.
|
||||
"""
|
||||
|
||||
# A bit for every candidate
|
||||
CANDIDATE_UTF_8_SIG = 1
|
||||
CANDIDATE_UTF_16_AS_LE = 2
|
||||
CANDIDATE_UTF_16_AS_BE = 4
|
||||
CANDIDATE_UTF_16_LE = 8
|
||||
CANDIDATE_UTF_16_BE = 16
|
||||
CANDIDATE_UTF_32_AS_LE = 32
|
||||
CANDIDATE_UTF_32_AS_BE = 64
|
||||
CANDIDATE_UTF_32_LE = 128
|
||||
CANDIDATE_UTF_32_BE = 256
|
||||
CANDIDATE_CHARSET = 512
|
||||
|
||||
candidates = 1023 # all candidates
|
||||
|
||||
#input = chars(input)
|
||||
li = len(input)
|
||||
if li>=1:
|
||||
# Check first byte
|
||||
c = input[0]
|
||||
if c != b"\xef"[0]:
|
||||
candidates &= ~CANDIDATE_UTF_8_SIG
|
||||
if c != b"\xff"[0]:
|
||||
candidates &= ~(CANDIDATE_UTF_32_AS_LE|CANDIDATE_UTF_16_AS_LE)
|
||||
if c != b"\xfe"[0]:
|
||||
candidates &= ~CANDIDATE_UTF_16_AS_BE
|
||||
if c != b"@"[0]:
|
||||
candidates &= ~(CANDIDATE_UTF_32_LE|CANDIDATE_UTF_16_LE|CANDIDATE_CHARSET)
|
||||
if c != b"\x00"[0]:
|
||||
candidates &= ~(CANDIDATE_UTF_32_AS_BE|CANDIDATE_UTF_32_BE|CANDIDATE_UTF_16_BE)
|
||||
if li>=2:
|
||||
# Check second byte
|
||||
c = input[1]
|
||||
if c != b"\xbb"[0]:
|
||||
candidates &= ~CANDIDATE_UTF_8_SIG
|
||||
if c != b"\xfe"[0]:
|
||||
candidates &= ~(CANDIDATE_UTF_16_AS_LE|CANDIDATE_UTF_32_AS_LE)
|
||||
if c != b"\xff"[0]:
|
||||
candidates &= ~CANDIDATE_UTF_16_AS_BE
|
||||
if c != b"\x00"[0]:
|
||||
candidates &= ~(CANDIDATE_UTF_16_LE|CANDIDATE_UTF_32_AS_BE|CANDIDATE_UTF_32_LE|CANDIDATE_UTF_32_BE)
|
||||
if c != b"@"[0]:
|
||||
candidates &= ~CANDIDATE_UTF_16_BE
|
||||
if c != b"c"[0]:
|
||||
candidates &= ~CANDIDATE_CHARSET
|
||||
if li>=3:
|
||||
# Check third byte
|
||||
c = input[2]
|
||||
if c != b"\xbf"[0]:
|
||||
candidates &= ~CANDIDATE_UTF_8_SIG
|
||||
if c != b"c"[0]:
|
||||
candidates &= ~CANDIDATE_UTF_16_LE
|
||||
if c != b"\x00"[0]:
|
||||
candidates &= ~(CANDIDATE_UTF_32_AS_LE|CANDIDATE_UTF_32_LE|CANDIDATE_UTF_32_BE)
|
||||
if c != b"\xfe"[0]:
|
||||
candidates &= ~CANDIDATE_UTF_32_AS_BE
|
||||
if c != b"h"[0]:
|
||||
candidates &= ~CANDIDATE_CHARSET
|
||||
if li>=4:
|
||||
# Check fourth byte
|
||||
c = input[3]
|
||||
if input[2:4] == b"\x00\x00"[0:2]:
|
||||
candidates &= ~CANDIDATE_UTF_16_AS_LE
|
||||
if c != b"\x00"[0]:
|
||||
candidates &= ~(CANDIDATE_UTF_16_LE|CANDIDATE_UTF_32_AS_LE|CANDIDATE_UTF_32_LE)
|
||||
if c != b"\xff"[0]:
|
||||
candidates &= ~CANDIDATE_UTF_32_AS_BE
|
||||
if c != b"@"[0]:
|
||||
candidates &= ~CANDIDATE_UTF_32_BE
|
||||
if c != b"a"[0]:
|
||||
candidates &= ~CANDIDATE_CHARSET
|
||||
if candidates == 0:
|
||||
return ("utf-8", False)
|
||||
if not (candidates & (candidates-1)): # only one candidate remaining
|
||||
if candidates == CANDIDATE_UTF_8_SIG and li >= 3:
|
||||
return ("utf-8-sig", True)
|
||||
elif candidates == CANDIDATE_UTF_16_AS_LE and li >= 2:
|
||||
return ("utf-16", True)
|
||||
elif candidates == CANDIDATE_UTF_16_AS_BE and li >= 2:
|
||||
return ("utf-16", True)
|
||||
elif candidates == CANDIDATE_UTF_16_LE and li >= 4:
|
||||
return ("utf-16-le", False)
|
||||
elif candidates == CANDIDATE_UTF_16_BE and li >= 2:
|
||||
return ("utf-16-be", False)
|
||||
elif candidates == CANDIDATE_UTF_32_AS_LE and li >= 4:
|
||||
return ("utf-32", True)
|
||||
elif candidates == CANDIDATE_UTF_32_AS_BE and li >= 4:
|
||||
return ("utf-32", True)
|
||||
elif candidates == CANDIDATE_UTF_32_LE and li >= 4:
|
||||
return ("utf-32-le", False)
|
||||
elif candidates == CANDIDATE_UTF_32_BE and li >= 4:
|
||||
return ("utf-32-be", False)
|
||||
elif candidates == CANDIDATE_CHARSET and li >= 4:
|
||||
prefix = '@charset "'
|
||||
charsinput = chars(input)
|
||||
if charsinput[:len(prefix)] == prefix:
|
||||
pos = charsinput.find('"', len(prefix))
|
||||
if pos >= 0:
|
||||
# TODO: return str and not bytes!
|
||||
return (charsinput[len(prefix):pos], True)
|
||||
# if this is the last call, and we haven't determined an encoding yet,
|
||||
# we default to UTF-8
|
||||
if final:
|
||||
return ("utf-8", False)
|
||||
return (None, False) # dont' know yet
|
||||
|
||||
|
||||
def detectencoding_unicode(input, final=False):
|
||||
"""
|
||||
Detect the encoding of the unicode string ``input``, which contains the
|
||||
beginning of a CSS file. The encoding is detected from the charset rule
|
||||
at the beginning of ``input``. If there is no charset rule, ``"utf-8"``
|
||||
will be returned.
|
||||
|
||||
If the encoding can't be detected yet, ``None`` is returned. ``final``
|
||||
specifies whether more data will be available in later calls or not. If
|
||||
``final`` is true, ``detectencoding_unicode()`` will never return ``None``.
|
||||
"""
|
||||
prefix = '@charset "'
|
||||
if input.startswith(prefix):
|
||||
pos = input.find('"', len(prefix))
|
||||
if pos >= 0:
|
||||
return (input[len(prefix):pos], True)
|
||||
elif final or not prefix.startswith(input):
|
||||
# if this is the last call, and we haven't determined an encoding yet,
|
||||
# (or the string definitely doesn't start with prefix) we default to UTF-8
|
||||
return ("utf-8", False)
|
||||
return (None, False) # don't know yet
|
||||
|
||||
|
||||
def _fixencoding(input, encoding, final=False):
|
||||
"""
|
||||
Replace the name of the encoding in the charset rule at the beginning of
|
||||
``input`` with ``encoding``. If ``input`` doesn't starts with a charset
|
||||
rule, ``input`` will be returned unmodified.
|
||||
|
||||
If the encoding can't be found yet, ``None`` is returned. ``final``
|
||||
specifies whether more data will be available in later calls or not.
|
||||
If ``final`` is true, ``_fixencoding()`` will never return ``None``.
|
||||
"""
|
||||
prefix = '@charset "'
|
||||
if len(input) > len(prefix):
|
||||
if input.startswith(prefix):
|
||||
pos = input.find('"', len(prefix))
|
||||
if pos >= 0:
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
encoding = "utf-8"
|
||||
return prefix + encoding + input[pos:]
|
||||
# we haven't seen the end of the encoding name yet => fall through
|
||||
else:
|
||||
return input # doesn't start with prefix, so nothing to fix
|
||||
elif not prefix.startswith(input) or final:
|
||||
# can't turn out to be a @charset rule later (or there is no "later")
|
||||
return input
|
||||
if final:
|
||||
return input
|
||||
return None # don't know yet
|
||||
|
||||
|
||||
def decode(input, errors="strict", encoding=None, force=True):
|
||||
try:
|
||||
# py 3 only, memory?! object to bytes
|
||||
input = input.tobytes()
|
||||
except AttributeError as e:
|
||||
pass
|
||||
|
||||
if encoding is None or not force:
|
||||
(_encoding, explicit) = detectencoding_str(input, True)
|
||||
if _encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
if (explicit and not force) or encoding is None: # Take the encoding from the input
|
||||
encoding = _encoding
|
||||
|
||||
# NEEDS: change in parse.py (str to bytes!)
|
||||
(input, consumed) = codecs.getdecoder(encoding)(input, errors)
|
||||
return (_fixencoding(input, str(encoding), True), consumed)
|
||||
|
||||
|
||||
def encode(input, errors="strict", encoding=None):
|
||||
consumed = len(input)
|
||||
if encoding is None:
|
||||
encoding = detectencoding_unicode(input, True)[0]
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
input = _fixencoding(input, "utf-8", True)
|
||||
else:
|
||||
input = _fixencoding(input, str(encoding), True)
|
||||
if encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
encoder = codecs.getencoder(encoding)
|
||||
return (encoder(input, errors)[0], consumed)
|
||||
|
||||
|
||||
def _bytes2int(bytes):
|
||||
# Helper: convert an 8 bit string into an ``int``.
|
||||
i = 0
|
||||
for byte in bytes:
|
||||
i = (i<<8) + ord(byte)
|
||||
return i
|
||||
|
||||
|
||||
def _int2bytes(i):
|
||||
# Helper: convert an ``int`` into an 8-bit string.
|
||||
v = []
|
||||
while i:
|
||||
v.insert(0, chr(i&0xff))
|
||||
i >>= 8
|
||||
return "".join(v)
|
||||
|
||||
|
||||
if hasattr(codecs, "IncrementalDecoder"):
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def __init__(self, errors="strict", encoding=None, force=True):
|
||||
self.decoder = None
|
||||
self.encoding = encoding
|
||||
self.force = force
|
||||
codecs.IncrementalDecoder.__init__(self, errors)
|
||||
# Store ``errors`` somewhere else,
|
||||
# because we have to hide it in a property
|
||||
self._errors = errors
|
||||
self.buffer = b""
|
||||
self.headerfixed = False
|
||||
|
||||
def iterdecode(self, input):
|
||||
for part in input:
|
||||
result = self.decode(part, False)
|
||||
if result:
|
||||
yield result
|
||||
result = self.decode("", True)
|
||||
if result:
|
||||
yield result
|
||||
|
||||
def decode(self, input, final=False):
|
||||
# We're doing basically the same as a ``BufferedIncrementalDecoder``,
|
||||
# but since the buffer is only relevant until the encoding has been
|
||||
# detected (in which case the buffer of the underlying codec might
|
||||
# kick in), we're implementing buffering ourselves to avoid some
|
||||
# overhead.
|
||||
if self.decoder is None:
|
||||
input = self.buffer + input
|
||||
# Do we have to detect the encoding from the input?
|
||||
if self.encoding is None or not self.force:
|
||||
(encoding, explicit) = detectencoding_str(input, final)
|
||||
if encoding is None: # no encoding determined yet
|
||||
self.buffer = input # retry the complete input on the next call
|
||||
return "" # no encoding determined yet, so no output
|
||||
elif encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
if (explicit and not self.force) or self.encoding is None: # Take the encoding from the input
|
||||
self.encoding = encoding
|
||||
self.buffer = "" # drop buffer, as the decoder might keep its own
|
||||
decoder = codecs.getincrementaldecoder(self.encoding)
|
||||
self.decoder = decoder(self._errors)
|
||||
if self.headerfixed:
|
||||
return self.decoder.decode(input, final)
|
||||
# If we haven't fixed the header yet,
|
||||
# the content of ``self.buffer`` is a ``unicode`` object
|
||||
output = self.buffer + self.decoder.decode(input, final)
|
||||
encoding = self.encoding
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
encoding = "utf-8"
|
||||
newoutput = _fixencoding(output, str(encoding), final)
|
||||
if newoutput is None:
|
||||
# retry fixing the @charset rule (but keep the decoded stuff)
|
||||
self.buffer = output
|
||||
return ""
|
||||
self.headerfixed = True
|
||||
return newoutput
|
||||
|
||||
def reset(self):
|
||||
codecs.IncrementalDecoder.reset(self)
|
||||
self.decoder = None
|
||||
self.buffer = b""
|
||||
self.headerfixed = False
|
||||
|
||||
def _geterrors(self):
|
||||
return self._errors
|
||||
|
||||
def _seterrors(self, errors):
|
||||
# Setting ``errors`` must be done on the real decoder too
|
||||
if self.decoder is not None:
|
||||
self.decoder.errors = errors
|
||||
self._errors = errors
|
||||
errors = property(_geterrors, _seterrors)
|
||||
|
||||
def getstate(self):
|
||||
if self.decoder is not None:
|
||||
state = (self.encoding, self.buffer, self.headerfixed, True, self.decoder.getstate())
|
||||
else:
|
||||
state = (self.encoding, self.buffer, self.headerfixed, False, None)
|
||||
return ("", _bytes2int(marshal.dumps(state)))
|
||||
|
||||
def setstate(self, state):
|
||||
state = _int2bytes(marshal.loads(state[1])) # ignore buffered input
|
||||
self.encoding = state[0]
|
||||
self.buffer = state[1]
|
||||
self.headerfixed = state[2]
|
||||
if state[3] is not None:
|
||||
self.decoder = codecs.getincrementaldecoder(self.encoding)(self._errors)
|
||||
self.decoder.setstate(state[4])
|
||||
else:
|
||||
self.decoder = None
|
||||
|
||||
|
||||
if hasattr(codecs, "IncrementalEncoder"):
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def __init__(self, errors="strict", encoding=None):
|
||||
self.encoder = None
|
||||
self.encoding = encoding
|
||||
codecs.IncrementalEncoder.__init__(self, errors)
|
||||
# Store ``errors`` somewhere else,
|
||||
# because we have to hide it in a property
|
||||
self._errors = errors
|
||||
self.buffer = ""
|
||||
|
||||
def iterencode(self, input):
|
||||
for part in input:
|
||||
result = self.encode(part, False)
|
||||
if result:
|
||||
yield result
|
||||
result = self.encode("", True)
|
||||
if result:
|
||||
yield result
|
||||
|
||||
def encode(self, input, final=False):
|
||||
if self.encoder is None:
|
||||
input = self.buffer + input
|
||||
if self.encoding is not None:
|
||||
# Replace encoding in the @charset rule with the specified one
|
||||
encoding = self.encoding
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
encoding = "utf-8"
|
||||
newinput = _fixencoding(input, str(encoding), final)
|
||||
if newinput is None: # @charset rule incomplete => Retry next time
|
||||
self.buffer = input
|
||||
return ""
|
||||
input = newinput
|
||||
else:
|
||||
# Use encoding from the @charset declaration
|
||||
self.encoding = detectencoding_unicode(input, final)[0]
|
||||
if self.encoding is not None:
|
||||
if self.encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
info = codecs.lookup(self.encoding)
|
||||
encoding = self.encoding
|
||||
if self.encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
input = _fixencoding(input, "utf-8", True)
|
||||
self.encoder = info.incrementalencoder(self._errors)
|
||||
self.buffer = ""
|
||||
else:
|
||||
self.buffer = input
|
||||
return ""
|
||||
return self.encoder.encode(input, final)
|
||||
|
||||
def reset(self):
|
||||
codecs.IncrementalEncoder.reset(self)
|
||||
self.encoder = None
|
||||
self.buffer = ""
|
||||
|
||||
def _geterrors(self):
|
||||
return self._errors
|
||||
|
||||
def _seterrors(self, errors):
|
||||
# Setting ``errors ``must be done on the real encoder too
|
||||
if self.encoder is not None:
|
||||
self.encoder.errors = errors
|
||||
self._errors = errors
|
||||
errors = property(_geterrors, _seterrors)
|
||||
|
||||
def getstate(self):
|
||||
if self.encoder is not None:
|
||||
state = (self.encoding, self.buffer, True, self.encoder.getstate())
|
||||
else:
|
||||
state = (self.encoding, self.buffer, False, None)
|
||||
return _bytes2int(marshal.dumps(state))
|
||||
|
||||
def setstate(self, state):
|
||||
state = _int2bytes(marshal.loads(state))
|
||||
self.encoding = state[0]
|
||||
self.buffer = state[1]
|
||||
if state[2] is not None:
|
||||
self.encoder = codecs.getincrementalencoder(self.encoding)(self._errors)
|
||||
self.encoder.setstate(state[4])
|
||||
else:
|
||||
self.encoder = None
|
||||
|
||||
|
||||
class StreamWriter(codecs.StreamWriter):
|
||||
def __init__(self, stream, errors="strict", encoding=None, header=False):
|
||||
codecs.StreamWriter.__init__(self, stream, errors)
|
||||
self.streamwriter = None
|
||||
self.encoding = encoding
|
||||
self._errors = errors
|
||||
self.buffer = ""
|
||||
|
||||
def encode(self, input, errors='strict'):
|
||||
li = len(input)
|
||||
if self.streamwriter is None:
|
||||
input = self.buffer + input
|
||||
li = len(input)
|
||||
if self.encoding is not None:
|
||||
# Replace encoding in the @charset rule with the specified one
|
||||
encoding = self.encoding
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
encoding = "utf-8"
|
||||
newinput = _fixencoding(input, str(encoding), False)
|
||||
if newinput is None: # @charset rule incomplete => Retry next time
|
||||
self.buffer = input
|
||||
return ("", 0)
|
||||
input = newinput
|
||||
else:
|
||||
# Use encoding from the @charset declaration
|
||||
self.encoding = detectencoding_unicode(input, False)[0]
|
||||
if self.encoding is not None:
|
||||
if self.encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
self.streamwriter = codecs.getwriter(self.encoding)(self.stream, self._errors)
|
||||
encoding = self.encoding
|
||||
if self.encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
input = _fixencoding(input, "utf-8", True)
|
||||
self.buffer = ""
|
||||
else:
|
||||
self.buffer = input
|
||||
return ("", 0)
|
||||
return (self.streamwriter.encode(input, errors)[0], li)
|
||||
|
||||
def _geterrors(self):
|
||||
return self._errors
|
||||
|
||||
def _seterrors(self, errors):
|
||||
# Setting ``errors`` must be done on the streamwriter too
|
||||
try:
|
||||
if self.streamwriter is not None:
|
||||
self.streamwriter.errors = errors
|
||||
except AttributeError as e:
|
||||
# TODO: py3 only exception?
|
||||
pass
|
||||
|
||||
self._errors = errors
|
||||
errors = property(_geterrors, _seterrors)
|
||||
|
||||
|
||||
class StreamReader(codecs.StreamReader):
|
||||
def __init__(self, stream, errors="strict", encoding=None, force=True):
|
||||
codecs.StreamReader.__init__(self, stream, errors)
|
||||
self.streamreader = None
|
||||
self.encoding = encoding
|
||||
self.force = force
|
||||
self._errors = errors
|
||||
|
||||
def decode(self, input, errors='strict'):
|
||||
if self.streamreader is None:
|
||||
if self.encoding is None or not self.force:
|
||||
(encoding, explicit) = detectencoding_str(input, False)
|
||||
if encoding is None: # no encoding determined yet
|
||||
return ("", 0) # no encoding determined yet, so no output
|
||||
elif encoding == "css":
|
||||
raise ValueError("css not allowed as encoding name")
|
||||
if (explicit and not self.force) or self.encoding is None: # Take the encoding from the input
|
||||
self.encoding = encoding
|
||||
streamreader = codecs.getreader(self.encoding)
|
||||
streamreader = streamreader(self.stream, self._errors)
|
||||
(output, consumed) = streamreader.decode(input, errors)
|
||||
encoding = self.encoding
|
||||
if encoding.replace("_", "-").lower() == "utf-8-sig":
|
||||
encoding = "utf-8"
|
||||
newoutput = _fixencoding(output, str(encoding), False)
|
||||
if newoutput is not None:
|
||||
self.streamreader = streamreader
|
||||
return (newoutput, consumed)
|
||||
return ("", 0) # we will create a new streamreader on the next call
|
||||
return self.streamreader.decode(input, errors)
|
||||
|
||||
def _geterrors(self):
|
||||
return self._errors
|
||||
|
||||
def _seterrors(self, errors):
|
||||
# Setting ``errors`` must be done on the streamreader too
|
||||
try:
|
||||
if self.streamreader is not None:
|
||||
self.streamreader.errors = errors
|
||||
except AttributeError as e:
|
||||
# TODO: py3 only exception?
|
||||
pass
|
||||
|
||||
self._errors = errors
|
||||
errors = property(_geterrors, _seterrors)
|
||||
|
||||
|
||||
if hasattr(codecs, "CodecInfo"):
|
||||
# We're running on Python 2.5 or better
|
||||
def search_function(name):
|
||||
if name == "css":
|
||||
return codecs.CodecInfo(
|
||||
name="css",
|
||||
encode=encode,
|
||||
decode=decode,
|
||||
incrementalencoder=IncrementalEncoder,
|
||||
incrementaldecoder=IncrementalDecoder,
|
||||
streamwriter=StreamWriter,
|
||||
streamreader=StreamReader,
|
||||
)
|
||||
else:
|
||||
# If we're running on Python 2.4, define the utf-8-sig codec here
|
||||
def utf8sig_encode(input, errors='strict'):
|
||||
return (codecs.BOM_UTF8 + codecs.utf_8_encode(input, errors)[0], len(input))
|
||||
|
||||
def utf8sig_decode(input, errors='strict'):
|
||||
prefix = 0
|
||||
if input[:3] == codecs.BOM_UTF8:
|
||||
input = input[3:]
|
||||
prefix = 3
|
||||
(output, consumed) = codecs.utf_8_decode(input, errors, True)
|
||||
return (output, consumed+prefix)
|
||||
|
||||
class UTF8SigStreamWriter(codecs.StreamWriter):
|
||||
def reset(self):
|
||||
codecs.StreamWriter.reset(self)
|
||||
try:
|
||||
del self.encode
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def encode(self, input, errors='strict'):
|
||||
self.encode = codecs.utf_8_encode
|
||||
return utf8sig_encode(input, errors)
|
||||
|
||||
class UTF8SigStreamReader(codecs.StreamReader):
|
||||
def reset(self):
|
||||
codecs.StreamReader.reset(self)
|
||||
try:
|
||||
del self.decode
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def decode(self, input, errors='strict'):
|
||||
if len(input) < 3 and codecs.BOM_UTF8.startswith(input):
|
||||
# not enough data to decide if this is a BOM
|
||||
# => try again on the next call
|
||||
return ("", 0)
|
||||
self.decode = codecs.utf_8_decode
|
||||
return utf8sig_decode(input, errors)
|
||||
|
||||
def search_function(name):
|
||||
import encodings
|
||||
name = encodings.normalize_encoding(name)
|
||||
if name == "css":
|
||||
return (encode, decode, StreamReader, StreamWriter)
|
||||
elif name == "utf_8_sig":
|
||||
return (utf8sig_encode, utf8sig_decode, UTF8SigStreamReader, UTF8SigStreamWriter)
|
||||
|
||||
|
||||
codecs.register(search_function)
|
||||
|
||||
|
||||
# Error handler for CSS escaping
|
||||
|
||||
def cssescape(exc):
|
||||
if not isinstance(exc, UnicodeEncodeError):
|
||||
raise TypeError("don't know how to handle %r" % exc)
|
||||
return ("".join("\\%06x" % ord(c) for c in exc.object[exc.start:exc.end]), exc.end)
|
||||
|
||||
codecs.register_error("cssescape", cssescape)
|
||||
44
libs/cssutils/_fetch.py
Executable file
@@ -0,0 +1,44 @@
|
||||
"""Default URL reading functions"""
|
||||
__all__ = ['_defaultFetcher']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: tokenize2.py 1547 2008-12-10 20:42:26Z cthedot $'
|
||||
|
||||
import cssutils
|
||||
from cssutils import VERSION
|
||||
import encutils
|
||||
import errorhandler
|
||||
import urllib2
|
||||
|
||||
log = errorhandler.ErrorHandler()
|
||||
|
||||
def _defaultFetcher(url):
|
||||
"""Retrieve data from ``url``. cssutils default implementation of fetch
|
||||
URL function.
|
||||
|
||||
Returns ``(encoding, string)`` or ``None``
|
||||
"""
|
||||
try:
|
||||
request = urllib2.Request(url)
|
||||
request.add_header('User-agent',
|
||||
'cssutils %s (http://www.cthedot.de/cssutils/)' % VERSION)
|
||||
res = urllib2.urlopen(request)
|
||||
except OSError, e:
|
||||
# e.g if file URL and not found
|
||||
log.warn(e, error=OSError)
|
||||
except (OSError, ValueError), e:
|
||||
# invalid url, e.g. "1"
|
||||
log.warn(u'ValueError, %s' % e.args[0], error=ValueError)
|
||||
except urllib2.HTTPError, e:
|
||||
# http error, e.g. 404, e can be raised
|
||||
log.warn(u'HTTPError opening url=%s: %s %s' %
|
||||
(url, e.code, e.msg), error=e)
|
||||
except urllib2.URLError, e:
|
||||
# URLError like mailto: or other IO errors, e can be raised
|
||||
log.warn(u'URLError, %s' % e.reason, error=e)
|
||||
else:
|
||||
if res:
|
||||
mimeType, encoding = encutils.getHTTPInfo(res)
|
||||
if mimeType != u'text/css':
|
||||
log.error(u'Expected "text/css" mime type for url=%r but found: %r' %
|
||||
(url, mimeType), error=ValueError)
|
||||
return encoding, res.read()
|
||||
68
libs/cssutils/_fetchgae.py
Executable file
@@ -0,0 +1,68 @@
|
||||
"""GAE specific URL reading functions"""
|
||||
__all__ = ['_defaultFetcher']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: tokenize2.py 1547 2008-12-10 20:42:26Z cthedot $'
|
||||
|
||||
# raises ImportError of not on GAE
|
||||
from google.appengine.api import urlfetch
|
||||
import cgi
|
||||
import errorhandler
|
||||
import util
|
||||
|
||||
log = errorhandler.ErrorHandler()
|
||||
|
||||
def _defaultFetcher(url):
|
||||
"""
|
||||
uses GoogleAppEngine (GAE)
|
||||
fetch(url, payload=None, method=GET, headers={}, allow_truncated=False)
|
||||
|
||||
Response
|
||||
content
|
||||
The body content of the response.
|
||||
content_was_truncated
|
||||
True if the allow_truncated parameter to fetch() was True and
|
||||
the response exceeded the maximum response size. In this case,
|
||||
the content attribute contains the truncated response.
|
||||
status_code
|
||||
The HTTP status code.
|
||||
headers
|
||||
The HTTP response headers, as a mapping of names to values.
|
||||
|
||||
Exceptions
|
||||
exception InvalidURLError()
|
||||
The URL of the request was not a valid URL, or it used an
|
||||
unsupported method. Only http and https URLs are supported.
|
||||
exception DownloadError()
|
||||
There was an error retrieving the data.
|
||||
|
||||
This exception is not raised if the server returns an HTTP
|
||||
error code: In that case, the response data comes back intact,
|
||||
including the error code.
|
||||
|
||||
exception ResponseTooLargeError()
|
||||
The response data exceeded the maximum allowed size, and the
|
||||
allow_truncated parameter passed to fetch() was False.
|
||||
"""
|
||||
#from google.appengine.api import urlfetch
|
||||
try:
|
||||
r = urlfetch.fetch(url, method=urlfetch.GET)
|
||||
except urlfetch.Error, e:
|
||||
log.warn(u'Error opening url=%r: %s' % (url, e),
|
||||
error=IOError)
|
||||
else:
|
||||
if r.status_code == 200:
|
||||
# find mimetype and encoding
|
||||
mimetype = 'application/octet-stream'
|
||||
try:
|
||||
mimetype, params = cgi.parse_header(r.headers['content-type'])
|
||||
encoding = params['charset']
|
||||
except KeyError:
|
||||
encoding = None
|
||||
if mimetype != u'text/css':
|
||||
log.error(u'Expected "text/css" mime type for url %r but found: %r' %
|
||||
(url, mimetype), error=ValueError)
|
||||
return encoding, r.content
|
||||
else:
|
||||
# TODO: 301 etc
|
||||
log.warn(u'Error opening url=%r: HTTP status %s' %
|
||||
(url, r.status_code), error=IOError)
|
||||
16
libs/cssutils/codec.py
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
"""Python codec for CSS."""
|
||||
__docformat__ = 'restructuredtext'
|
||||
__author__ = 'Walter Doerwald'
|
||||
__version__ = '$Id: util.py 1114 2008-03-05 13:22:59Z cthedot $'
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3,):
|
||||
from _codec2 import *
|
||||
# for tests
|
||||
from _codec2 import _fixencoding
|
||||
else:
|
||||
from _codec3 import *
|
||||
# for tests
|
||||
from _codec3 import _fixencoding
|
||||
80
libs/cssutils/css/__init__.py
Executable file
@@ -0,0 +1,80 @@
|
||||
"""Implements Document Object Model Level 2 CSS
|
||||
http://www.w3.org/TR/2000/PR-DOM-Level-2-Style-20000927/css.html
|
||||
|
||||
currently implemented
|
||||
- CSSStyleSheet
|
||||
- CSSRuleList
|
||||
- CSSRule
|
||||
- CSSComment (cssutils addon)
|
||||
- CSSCharsetRule
|
||||
- CSSFontFaceRule
|
||||
- CSSImportRule
|
||||
- CSSMediaRule
|
||||
- CSSNamespaceRule (WD)
|
||||
- CSSPageRule
|
||||
- CSSStyleRule
|
||||
- CSSUnkownRule
|
||||
- Selector and SelectorList
|
||||
- CSSStyleDeclaration
|
||||
- CSS2Properties
|
||||
- CSSValue
|
||||
- CSSPrimitiveValue
|
||||
- CSSValueList
|
||||
- CSSVariablesRule
|
||||
- CSSVariablesDeclaration
|
||||
|
||||
todo
|
||||
- RGBColor, Rect, Counter
|
||||
"""
|
||||
__all__ = [
|
||||
'CSSStyleSheet',
|
||||
'CSSRuleList',
|
||||
'CSSRule',
|
||||
'CSSComment',
|
||||
'CSSCharsetRule',
|
||||
'CSSFontFaceRule'
|
||||
'CSSImportRule',
|
||||
'CSSMediaRule',
|
||||
'CSSNamespaceRule',
|
||||
'CSSPageRule',
|
||||
'MarginRule',
|
||||
'CSSStyleRule',
|
||||
'CSSUnknownRule',
|
||||
'CSSVariablesRule',
|
||||
'CSSVariablesDeclaration',
|
||||
'Selector', 'SelectorList',
|
||||
'CSSStyleDeclaration', 'Property',
|
||||
#'CSSValue', 'CSSPrimitiveValue', 'CSSValueList'
|
||||
'PropertyValue',
|
||||
'Value',
|
||||
'ColorValue',
|
||||
'DimensionValue',
|
||||
'URIValue',
|
||||
'CSSFunction',
|
||||
'CSSVariable',
|
||||
'MSValue'
|
||||
]
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
from cssstylesheet import *
|
||||
from cssrulelist import *
|
||||
from cssrule import *
|
||||
from csscomment import *
|
||||
from csscharsetrule import *
|
||||
from cssfontfacerule import *
|
||||
from cssimportrule import *
|
||||
from cssmediarule import *
|
||||
from cssnamespacerule import *
|
||||
from csspagerule import *
|
||||
from marginrule import *
|
||||
from cssstylerule import *
|
||||
from cssvariablesrule import *
|
||||
from cssunknownrule import *
|
||||
from selector import *
|
||||
from selectorlist import *
|
||||
from cssstyledeclaration import *
|
||||
from cssvariablesdeclaration import *
|
||||
from property import *
|
||||
#from cssvalue import *
|
||||
from value import *
|
||||
184
libs/cssutils/css/colors.py
Executable file
@@ -0,0 +1,184 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Built from something like this:
|
||||
|
||||
print [
|
||||
(
|
||||
row[2].text_content().strip(),
|
||||
eval(row[4].text_content().strip())
|
||||
)
|
||||
for row in lxml.html.parse('http://www.w3.org/TR/css3-color/')
|
||||
.xpath("//*[@class='colortable']//tr[position()>1]")
|
||||
]
|
||||
|
||||
by Simon Sapin
|
||||
"""
|
||||
|
||||
COLORS = {
|
||||
'transparent': (0, 0, 0, 0.0),
|
||||
|
||||
'black': (0, 0, 0, 1.0),
|
||||
'silver': (192, 192, 192, 1.0),
|
||||
'gray': (128, 128, 128, 1.0),
|
||||
'white': (255, 255, 255, 1.0),
|
||||
'maroon': (128, 0, 0, 1.0),
|
||||
'red': (255, 0, 0, 1.0),
|
||||
'purple': (128, 0, 128, 1.0),
|
||||
'fuchsia': (255, 0, 255, 1.0),
|
||||
'green': (0, 128, 0, 1.0),
|
||||
'lime': (0, 255, 0, 1.0),
|
||||
'olive': (128, 128, 0, 1.0),
|
||||
'yellow': (255, 255, 0, 1.0),
|
||||
'navy': (0, 0, 128, 1.0),
|
||||
'blue': (0, 0, 255, 1.0),
|
||||
'teal': (0, 128, 128, 1.0),
|
||||
|
||||
'aqua': (0, 255, 255, 1.0),
|
||||
'aliceblue': (240, 248, 255, 1.0),
|
||||
'antiquewhite': (250, 235, 215, 1.0),
|
||||
'aqua': (0, 255, 255, 1.0),
|
||||
'aquamarine': (127, 255, 212, 1.0),
|
||||
'azure': (240, 255, 255, 1.0),
|
||||
'beige': (245, 245, 220, 1.0),
|
||||
'bisque': (255, 228, 196, 1.0),
|
||||
'black': (0, 0, 0, 1.0),
|
||||
'blanchedalmond': (255, 235, 205, 1.0),
|
||||
'blue': (0, 0, 255, 1.0),
|
||||
'blueviolet': (138, 43, 226, 1.0),
|
||||
'brown': (165, 42, 42, 1.0),
|
||||
'burlywood': (222, 184, 135, 1.0),
|
||||
'cadetblue': (95, 158, 160, 1.0),
|
||||
'chartreuse': (127, 255, 0, 1.0),
|
||||
'chocolate': (210, 105, 30, 1.0),
|
||||
'coral': (255, 127, 80, 1.0),
|
||||
'cornflowerblue': (100, 149, 237, 1.0),
|
||||
'cornsilk': (255, 248, 220, 1.0),
|
||||
'crimson': (220, 20, 60, 1.0),
|
||||
'cyan': (0, 255, 255, 1.0),
|
||||
'darkblue': (0, 0, 139, 1.0),
|
||||
'darkcyan': (0, 139, 139, 1.0),
|
||||
'darkgoldenrod': (184, 134, 11, 1.0),
|
||||
'darkgray': (169, 169, 169, 1.0),
|
||||
'darkgreen': (0, 100, 0, 1.0),
|
||||
'darkgrey': (169, 169, 169, 1.0),
|
||||
'darkkhaki': (189, 183, 107, 1.0),
|
||||
'darkmagenta': (139, 0, 139, 1.0),
|
||||
'darkolivegreen': (85, 107, 47, 1.0),
|
||||
'darkorange': (255, 140, 0, 1.0),
|
||||
'darkorchid': (153, 50, 204, 1.0),
|
||||
'darkred': (139, 0, 0, 1.0),
|
||||
'darksalmon': (233, 150, 122, 1.0),
|
||||
'darkseagreen': (143, 188, 143, 1.0),
|
||||
'darkslateblue': (72, 61, 139, 1.0),
|
||||
'darkslategray': (47, 79, 79, 1.0),
|
||||
'darkslategrey': (47, 79, 79, 1.0),
|
||||
'darkturquoise': (0, 206, 209, 1.0),
|
||||
'darkviolet': (148, 0, 211, 1.0),
|
||||
'deeppink': (255, 20, 147, 1.0),
|
||||
'deepskyblue': (0, 191, 255, 1.0),
|
||||
'dimgray': (105, 105, 105, 1.0),
|
||||
'dimgrey': (105, 105, 105, 1.0),
|
||||
'dodgerblue': (30, 144, 255, 1.0),
|
||||
'firebrick': (178, 34, 34, 1.0),
|
||||
'floralwhite': (255, 250, 240, 1.0),
|
||||
'forestgreen': (34, 139, 34, 1.0),
|
||||
'fuchsia': (255, 0, 255, 1.0),
|
||||
'gainsboro': (220, 220, 220, 1.0),
|
||||
'ghostwhite': (248, 248, 255, 1.0),
|
||||
'gold': (255, 215, 0, 1.0),
|
||||
'goldenrod': (218, 165, 32, 1.0),
|
||||
'gray': (128, 128, 128, 1.0),
|
||||
'green': (0, 128, 0, 1.0),
|
||||
'greenyellow': (173, 255, 47, 1.0),
|
||||
'grey': (128, 128, 128, 1.0),
|
||||
'honeydew': (240, 255, 240, 1.0),
|
||||
'hotpink': (255, 105, 180, 1.0),
|
||||
'indianred': (205, 92, 92, 1.0),
|
||||
'indigo': (75, 0, 130, 1.0),
|
||||
'ivory': (255, 255, 240, 1.0),
|
||||
'khaki': (240, 230, 140, 1.0),
|
||||
'lavender': (230, 230, 250, 1.0),
|
||||
'lavenderblush': (255, 240, 245, 1.0),
|
||||
'lawngreen': (124, 252, 0, 1.0),
|
||||
'lemonchiffon': (255, 250, 205, 1.0),
|
||||
'lightblue': (173, 216, 230, 1.0),
|
||||
'lightcoral': (240, 128, 128, 1.0),
|
||||
'lightcyan': (224, 255, 255, 1.0),
|
||||
'lightgoldenrodyellow': (250, 250, 210, 1.0),
|
||||
'lightgray': (211, 211, 211, 1.0),
|
||||
'lightgreen': (144, 238, 144, 1.0),
|
||||
'lightgrey': (211, 211, 211, 1.0),
|
||||
'lightpink': (255, 182, 193, 1.0),
|
||||
'lightsalmon': (255, 160, 122, 1.0),
|
||||
'lightseagreen': (32, 178, 170, 1.0),
|
||||
'lightskyblue': (135, 206, 250, 1.0),
|
||||
'lightslategray': (119, 136, 153, 1.0),
|
||||
'lightslategrey': (119, 136, 153, 1.0),
|
||||
'lightsteelblue': (176, 196, 222, 1.0),
|
||||
'lightyellow': (255, 255, 224, 1.0),
|
||||
'lime': (0, 255, 0, 1.0),
|
||||
'limegreen': (50, 205, 50, 1.0),
|
||||
'linen': (250, 240, 230, 1.0),
|
||||
'magenta': (255, 0, 255, 1.0),
|
||||
'maroon': (128, 0, 0, 1.0),
|
||||
'mediumaquamarine': (102, 205, 170, 1.0),
|
||||
'mediumblue': (0, 0, 205, 1.0),
|
||||
'mediumorchid': (186, 85, 211, 1.0),
|
||||
'mediumpurple': (147, 112, 219, 1.0),
|
||||
'mediumseagreen': (60, 179, 113, 1.0),
|
||||
'mediumslateblue': (123, 104, 238, 1.0),
|
||||
'mediumspringgreen': (0, 250, 154, 1.0),
|
||||
'mediumturquoise': (72, 209, 204, 1.0),
|
||||
'mediumvioletred': (199, 21, 133, 1.0),
|
||||
'midnightblue': (25, 25, 112, 1.0),
|
||||
'mintcream': (245, 255, 250, 1.0),
|
||||
'mistyrose': (255, 228, 225, 1.0),
|
||||
'moccasin': (255, 228, 181, 1.0),
|
||||
'navajowhite': (255, 222, 173, 1.0),
|
||||
'navy': (0, 0, 128, 1.0),
|
||||
'oldlace': (253, 245, 230, 1.0),
|
||||
'olive': (128, 128, 0, 1.0),
|
||||
'olivedrab': (107, 142, 35, 1.0),
|
||||
'orange': (255, 165, 0, 1.0),
|
||||
'orangered': (255, 69, 0, 1.0),
|
||||
'orchid': (218, 112, 214, 1.0),
|
||||
'palegoldenrod': (238, 232, 170, 1.0),
|
||||
'palegreen': (152, 251, 152, 1.0),
|
||||
'paleturquoise': (175, 238, 238, 1.0),
|
||||
'palevioletred': (219, 112, 147, 1.0),
|
||||
'papayawhip': (255, 239, 213, 1.0),
|
||||
'peachpuff': (255, 218, 185, 1.0),
|
||||
'peru': (205, 133, 63, 1.0),
|
||||
'pink': (255, 192, 203, 1.0),
|
||||
'plum': (221, 160, 221, 1.0),
|
||||
'powderblue': (176, 224, 230, 1.0),
|
||||
'purple': (128, 0, 128, 1.0),
|
||||
'red': (255, 0, 0, 1.0),
|
||||
'rosybrown': (188, 143, 143, 1.0),
|
||||
'royalblue': (65, 105, 225, 1.0),
|
||||
'saddlebrown': (139, 69, 19, 1.0),
|
||||
'salmon': (250, 128, 114, 1.0),
|
||||
'sandybrown': (244, 164, 96, 1.0),
|
||||
'seagreen': (46, 139, 87, 1.0),
|
||||
'seashell': (255, 245, 238, 1.0),
|
||||
'sienna': (160, 82, 45, 1.0),
|
||||
'silver': (192, 192, 192, 1.0),
|
||||
'skyblue': (135, 206, 235, 1.0),
|
||||
'slateblue': (106, 90, 205, 1.0),
|
||||
'slategray': (112, 128, 144, 1.0),
|
||||
'slategrey': (112, 128, 144, 1.0),
|
||||
'snow': (255, 250, 250, 1.0),
|
||||
'springgreen': (0, 255, 127, 1.0),
|
||||
'steelblue': (70, 130, 180, 1.0),
|
||||
'tan': (210, 180, 140, 1.0),
|
||||
'teal': (0, 128, 128, 1.0),
|
||||
'thistle': (216, 191, 216, 1.0),
|
||||
'tomato': (255, 99, 71, 1.0),
|
||||
'turquoise': (64, 224, 208, 1.0),
|
||||
'violet': (238, 130, 238, 1.0),
|
||||
'wheat': (245, 222, 179, 1.0),
|
||||
'white': (255, 255, 255, 1.0),
|
||||
'whitesmoke': (245, 245, 245, 1.0),
|
||||
'yellow': (255, 255, 0, 1.0),
|
||||
'yellowgreen': (154, 205, 50, 1.0),
|
||||
}
|
||||
159
libs/cssutils/css/csscharsetrule.py
Executable file
@@ -0,0 +1,159 @@
|
||||
"""CSSCharsetRule implements DOM Level 2 CSS CSSCharsetRule."""
|
||||
__all__ = ['CSSCharsetRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
import codecs
|
||||
import cssrule
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSCharsetRule(cssrule.CSSRule):
|
||||
"""
|
||||
The CSSCharsetRule interface represents an @charset rule in a CSS style
|
||||
sheet. The value of the encoding attribute does not affect the encoding
|
||||
of text data in the DOM objects; this encoding is always UTF-16
|
||||
(also in Python?). After a stylesheet is loaded, the value of the
|
||||
encoding attribute is the value found in the @charset rule. If there
|
||||
was no @charset in the original document, then no CSSCharsetRule is
|
||||
created. The value of the encoding attribute may also be used as a hint
|
||||
for the encoding used on serialization of the style sheet.
|
||||
|
||||
The value of the @charset rule (and therefore of the CSSCharsetRule)
|
||||
may not correspond to the encoding the document actually came in;
|
||||
character encoding information e.g. in an HTTP header, has priority
|
||||
(see CSS document representation) but this is not reflected in the
|
||||
CSSCharsetRule.
|
||||
|
||||
This rule is not really needed anymore as setting
|
||||
:attr:`CSSStyleSheet.encoding` is much easier.
|
||||
|
||||
Format::
|
||||
|
||||
charsetrule:
|
||||
CHARSET_SYM S* STRING S* ';'
|
||||
|
||||
BUT: Only valid format is (single space, double quotes!)::
|
||||
|
||||
@charset "ENCODING";
|
||||
"""
|
||||
def __init__(self, encoding=None, parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
:param encoding:
|
||||
a valid character encoding
|
||||
:param readonly:
|
||||
defaults to False, not used yet
|
||||
"""
|
||||
super(CSSCharsetRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = '@charset'
|
||||
|
||||
if encoding:
|
||||
self.encoding = encoding
|
||||
else:
|
||||
self._encoding = None
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
return u"cssutils.css.%s(encoding=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.encoding)
|
||||
|
||||
def __str__(self):
|
||||
return u"<cssutils.css.%s object encoding=%r at 0x%x>" % (
|
||||
self.__class__.__name__,
|
||||
self.encoding,
|
||||
id(self))
|
||||
|
||||
def _getCssText(self):
|
||||
"""The parsable textual representation."""
|
||||
return cssutils.ser.do_CSSCharsetRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:param cssText:
|
||||
A parsable DOMString.
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
super(CSSCharsetRule, self)._setCssText(cssText)
|
||||
|
||||
wellformed = True
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
|
||||
if self._type(self._nexttoken(tokenizer)) != self._prods.CHARSET_SYM:
|
||||
wellformed = False
|
||||
self._log.error(u'CSSCharsetRule must start with "@charset "',
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
|
||||
encodingtoken = self._nexttoken(tokenizer)
|
||||
encodingtype = self._type(encodingtoken)
|
||||
encoding = self._stringtokenvalue(encodingtoken)
|
||||
if self._prods.STRING != encodingtype or not encoding:
|
||||
wellformed = False
|
||||
self._log.error(u'CSSCharsetRule: no encoding found; %r.' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
semicolon = self._tokenvalue(self._nexttoken(tokenizer))
|
||||
EOFtype = self._type(self._nexttoken(tokenizer))
|
||||
if u';' != semicolon or EOFtype not in ('EOF', None):
|
||||
wellformed = False
|
||||
self._log.error(u'CSSCharsetRule: Syntax Error: %r.' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
if wellformed:
|
||||
self.encoding = encoding
|
||||
|
||||
cssText = property(fget=_getCssText, fset=_setCssText,
|
||||
doc=u"(DOM) The parsable textual representation.")
|
||||
|
||||
def _setEncoding(self, encoding):
|
||||
"""
|
||||
:param encoding:
|
||||
a valid encoding to be used. Currently only valid Python encodings
|
||||
are allowed.
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this encoding rule is readonly.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified encoding value has a syntax error and
|
||||
is unparsable.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
tokenizer = self._tokenize2(encoding)
|
||||
encodingtoken = self._nexttoken(tokenizer)
|
||||
unexpected = self._nexttoken(tokenizer)
|
||||
|
||||
if not encodingtoken or unexpected or\
|
||||
self._prods.IDENT != self._type(encodingtoken):
|
||||
self._log.error(u'CSSCharsetRule: Syntax Error in encoding value '
|
||||
u'%r.' % encoding)
|
||||
else:
|
||||
try:
|
||||
codecs.lookup(encoding)
|
||||
except LookupError:
|
||||
self._log.error(u'CSSCharsetRule: Unknown (Python) encoding %r.'
|
||||
% encoding)
|
||||
else:
|
||||
self._encoding = encoding.lower()
|
||||
|
||||
encoding = property(lambda self: self._encoding, _setEncoding,
|
||||
doc=u"(DOM)The encoding information used in this @charset rule.")
|
||||
|
||||
type = property(lambda self: self.CHARSET_RULE,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
u"type constant.")
|
||||
|
||||
wellformed = property(lambda self: bool(self.encoding))
|
||||
87
libs/cssutils/css/csscomment.py
Executable file
@@ -0,0 +1,87 @@
|
||||
"""CSSComment is not defined in DOM Level 2 at all but a cssutils defined
|
||||
class only.
|
||||
|
||||
Implements CSSRule which is also extended for a CSSComment rule type.
|
||||
"""
|
||||
__all__ = ['CSSComment']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
import cssrule
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSComment(cssrule.CSSRule):
|
||||
"""
|
||||
Represents a CSS comment (cssutils only).
|
||||
|
||||
Format::
|
||||
|
||||
/*...*/
|
||||
"""
|
||||
def __init__(self, cssText=None, parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
super(CSSComment, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
|
||||
self._cssText = None
|
||||
if cssText:
|
||||
self._setCssText(cssText)
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
return u"cssutils.css.%s(cssText=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return u"<cssutils.css.%s object cssText=%r at 0x%x>" % (
|
||||
self.__class__.__name__,
|
||||
self.cssText,
|
||||
id(self))
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText."""
|
||||
return cssutils.ser.do_CSSComment(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:param cssText:
|
||||
textual text to set or tokenlist which is not tokenized
|
||||
anymore. May also be a single token for this rule
|
||||
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
super(CSSComment, self)._setCssText(cssText)
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
|
||||
commenttoken = self._nexttoken(tokenizer)
|
||||
unexpected = self._nexttoken(tokenizer)
|
||||
|
||||
if not commenttoken or\
|
||||
self._type(commenttoken) != self._prods.COMMENT or\
|
||||
unexpected:
|
||||
self._log.error(u'CSSComment: Not a CSSComment: %r' %
|
||||
self._valuestr(cssText),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
else:
|
||||
self._cssText = self._tokenvalue(commenttoken)
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc=u"The parsable textual representation of this rule.")
|
||||
|
||||
type = property(lambda self: self.COMMENT,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
u"type constant.")
|
||||
|
||||
# constant but needed:
|
||||
wellformed = property(lambda self: True)
|
||||
184
libs/cssutils/css/cssfontfacerule.py
Executable file
@@ -0,0 +1,184 @@
|
||||
"""CSSFontFaceRule implements DOM Level 2 CSS CSSFontFaceRule.
|
||||
|
||||
From cssutils 0.9.6 additions from CSS Fonts Module Level 3 are
|
||||
added http://www.w3.org/TR/css3-fonts/.
|
||||
"""
|
||||
__all__ = ['CSSFontFaceRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
from cssstyledeclaration import CSSStyleDeclaration
|
||||
import cssrule
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSFontFaceRule(cssrule.CSSRule):
|
||||
"""
|
||||
The CSSFontFaceRule interface represents a @font-face rule in a CSS
|
||||
style sheet. The @font-face rule is used to hold a set of font
|
||||
descriptions.
|
||||
|
||||
Format::
|
||||
|
||||
font_face
|
||||
: FONT_FACE_SYM S*
|
||||
'{' S* declaration [ ';' S* declaration ]* '}' S*
|
||||
;
|
||||
|
||||
cssutils uses a :class:`~cssutils.css.CSSStyleDeclaration` to
|
||||
represent the font descriptions. For validation a specific profile
|
||||
is used though were some properties have other valid values than
|
||||
when used in e.g. a :class:`~cssutils.css.CSSStyleRule`.
|
||||
"""
|
||||
def __init__(self, style=None, parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
If readonly allows setting of properties in constructor only.
|
||||
|
||||
:param style:
|
||||
CSSStyleDeclaration used to hold any font descriptions
|
||||
for this CSSFontFaceRule
|
||||
"""
|
||||
super(CSSFontFaceRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = u'@font-face'
|
||||
|
||||
if style:
|
||||
self.style = style
|
||||
else:
|
||||
self.style = CSSStyleDeclaration()
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
return u"cssutils.css.%s(style=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.style.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return u"<cssutils.css.%s object style=%r valid=%r at 0x%x>" % (
|
||||
self.__class__.__name__,
|
||||
self.style.cssText,
|
||||
self.valid,
|
||||
id(self))
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText."""
|
||||
return cssutils.ser.do_CSSFontFaceRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
super(CSSFontFaceRule, self)._setCssText(cssText)
|
||||
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
attoken = self._nexttoken(tokenizer, None)
|
||||
if self._type(attoken) != self._prods.FONT_FACE_SYM:
|
||||
self._log.error(u'CSSFontFaceRule: No CSSFontFaceRule found: %s' %
|
||||
self._valuestr(cssText),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
else:
|
||||
newStyle = CSSStyleDeclaration(parentRule=self)
|
||||
ok = True
|
||||
|
||||
beforetokens, brace = self._tokensupto2(tokenizer,
|
||||
blockstartonly=True,
|
||||
separateEnd=True)
|
||||
if self._tokenvalue(brace) != u'{':
|
||||
ok = False
|
||||
self._log.error(u'CSSFontFaceRule: No start { of style '
|
||||
u'declaration found: %r'
|
||||
% self._valuestr(cssText), brace)
|
||||
|
||||
# parse stuff before { which should be comments and S only
|
||||
new = {'wellformed': True}
|
||||
newseq = self._tempSeq()
|
||||
|
||||
beforewellformed, expected = self._parse(expected=':',
|
||||
seq=newseq, tokenizer=self._tokenize2(beforetokens),
|
||||
productions={})
|
||||
ok = ok and beforewellformed and new['wellformed']
|
||||
|
||||
styletokens, braceorEOFtoken = self._tokensupto2(tokenizer,
|
||||
blockendonly=True,
|
||||
separateEnd=True)
|
||||
|
||||
val, type_ = self._tokenvalue(braceorEOFtoken),\
|
||||
self._type(braceorEOFtoken)
|
||||
if val != u'}' and type_ != 'EOF':
|
||||
ok = False
|
||||
self._log.error(u'CSSFontFaceRule: No "}" after style '
|
||||
u'declaration found: %r'
|
||||
% self._valuestr(cssText))
|
||||
|
||||
nonetoken = self._nexttoken(tokenizer)
|
||||
if nonetoken:
|
||||
ok = False
|
||||
self._log.error(u'CSSFontFaceRule: Trailing content found.',
|
||||
token=nonetoken)
|
||||
|
||||
if 'EOF' == type_:
|
||||
# add again as style needs it
|
||||
styletokens.append(braceorEOFtoken)
|
||||
|
||||
# SET, may raise:
|
||||
newStyle.cssText = styletokens
|
||||
|
||||
if ok:
|
||||
# contains probably comments only (upto ``{``)
|
||||
self._setSeq(newseq)
|
||||
self.style = newStyle
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc=u"(DOM) The parsable textual representation of this "
|
||||
u"rule.")
|
||||
|
||||
def _setStyle(self, style):
|
||||
"""
|
||||
:param style:
|
||||
a CSSStyleDeclaration or string
|
||||
"""
|
||||
self._checkReadonly()
|
||||
if isinstance(style, basestring):
|
||||
self._style = CSSStyleDeclaration(cssText=style, parentRule=self)
|
||||
else:
|
||||
style._parentRule = self
|
||||
self._style = style
|
||||
|
||||
style = property(lambda self: self._style, _setStyle,
|
||||
doc=u"(DOM) The declaration-block of this rule set, "
|
||||
u"a :class:`~cssutils.css.CSSStyleDeclaration`.")
|
||||
|
||||
type = property(lambda self: self.FONT_FACE_RULE,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
u"type constant.")
|
||||
|
||||
def _getValid(self):
|
||||
needed = ['font-family', 'src']
|
||||
for p in self.style.getProperties(all=True):
|
||||
if not p.valid:
|
||||
return False
|
||||
try:
|
||||
needed.remove(p.name)
|
||||
except ValueError:
|
||||
pass
|
||||
return not bool(needed)
|
||||
|
||||
valid = property(_getValid,
|
||||
doc=u"CSSFontFace is valid if properties `font-family` "
|
||||
u"and `src` are set and all properties are valid.")
|
||||
|
||||
# constant but needed:
|
||||
wellformed = property(lambda self: True)
|
||||
396
libs/cssutils/css/cssimportrule.py
Executable file
@@ -0,0 +1,396 @@
|
||||
"""CSSImportRule implements DOM Level 2 CSS CSSImportRule plus the
|
||||
``name`` property from http://www.w3.org/TR/css3-cascade/#cascading."""
|
||||
__all__ = ['CSSImportRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
import cssrule
|
||||
import cssutils
|
||||
import os
|
||||
import urlparse
|
||||
import xml.dom
|
||||
|
||||
class CSSImportRule(cssrule.CSSRule):
|
||||
"""
|
||||
Represents an @import rule within a CSS style sheet. The @import rule
|
||||
is used to import style rules from other style sheets.
|
||||
|
||||
Format::
|
||||
|
||||
import
|
||||
: IMPORT_SYM S*
|
||||
[STRING|URI] S* [ medium [ COMMA S* medium]* ]? S* STRING? S* ';' S*
|
||||
;
|
||||
"""
|
||||
def __init__(self, href=None, mediaText=None, name=None,
|
||||
parentRule=None, parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
If readonly allows setting of properties in constructor only
|
||||
|
||||
:param href:
|
||||
location of the style sheet to be imported.
|
||||
:param mediaText:
|
||||
A list of media types for which this style sheet may be used
|
||||
as a string
|
||||
:param name:
|
||||
Additional name of imported style sheet
|
||||
"""
|
||||
super(CSSImportRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = u'@import'
|
||||
self._styleSheet = None
|
||||
|
||||
# string or uri used for reserialization
|
||||
self.hreftype = None
|
||||
|
||||
# prepare seq
|
||||
seq = self._tempSeq()
|
||||
seq.append(None, 'href')
|
||||
#seq.append(None, 'media')
|
||||
seq.append(None, 'name')
|
||||
self._setSeq(seq)
|
||||
|
||||
# 1. media
|
||||
if mediaText:
|
||||
self.media = mediaText
|
||||
else:
|
||||
# must be all for @import
|
||||
self.media = cssutils.stylesheets.MediaList(mediaText=u'all')
|
||||
# 2. name
|
||||
self.name = name
|
||||
# 3. href and styleSheet
|
||||
self.href = href
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
if self._usemedia:
|
||||
mediaText = self.media.mediaText
|
||||
else:
|
||||
mediaText = None
|
||||
return u"cssutils.css.%s(href=%r, mediaText=%r, name=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.href,
|
||||
self.media.mediaText,
|
||||
self.name)
|
||||
|
||||
def __str__(self):
|
||||
if self._usemedia:
|
||||
mediaText = self.media.mediaText
|
||||
else:
|
||||
mediaText = None
|
||||
return u"<cssutils.css.%s object href=%r mediaText=%r name=%r at 0x%x>"\
|
||||
% (self.__class__.__name__,
|
||||
self.href,
|
||||
mediaText,
|
||||
self.name,
|
||||
id(self))
|
||||
|
||||
_usemedia = property(lambda self: self.media.mediaText not in (u'', u'all'),
|
||||
doc="if self.media is used (or simply empty)")
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText."""
|
||||
return cssutils.ser.do_CSSImportRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
"""
|
||||
super(CSSImportRule, self)._setCssText(cssText)
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
attoken = self._nexttoken(tokenizer, None)
|
||||
if self._type(attoken) != self._prods.IMPORT_SYM:
|
||||
self._log.error(u'CSSImportRule: No CSSImportRule found: %s' %
|
||||
self._valuestr(cssText),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
else:
|
||||
# for closures: must be a mutable
|
||||
new = {'keyword': self._tokenvalue(attoken),
|
||||
'href': None,
|
||||
'hreftype': None,
|
||||
'media': None,
|
||||
'name': None,
|
||||
'wellformed': True
|
||||
}
|
||||
|
||||
def __doname(seq, token):
|
||||
# called by _string or _ident
|
||||
new['name'] = self._stringtokenvalue(token)
|
||||
seq.append(new['name'], 'name')
|
||||
return ';'
|
||||
|
||||
def _string(expected, seq, token, tokenizer=None):
|
||||
if 'href' == expected:
|
||||
# href
|
||||
new['href'] = self._stringtokenvalue(token)
|
||||
new['hreftype'] = 'string'
|
||||
seq.append(new['href'], 'href')
|
||||
return 'media name ;'
|
||||
elif 'name' in expected:
|
||||
# name
|
||||
return __doname(seq, token)
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(
|
||||
u'CSSImportRule: Unexpected string.', token)
|
||||
return expected
|
||||
|
||||
def _uri(expected, seq, token, tokenizer=None):
|
||||
# href
|
||||
if 'href' == expected:
|
||||
uri = self._uritokenvalue(token)
|
||||
new['hreftype'] = 'uri'
|
||||
new['href'] = uri
|
||||
seq.append(new['href'], 'href')
|
||||
return 'media name ;'
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(
|
||||
u'CSSImportRule: Unexpected URI.', token)
|
||||
return expected
|
||||
|
||||
def _ident(expected, seq, token, tokenizer=None):
|
||||
# medialist ending with ; which is checked upon too
|
||||
if expected.startswith('media'):
|
||||
mediatokens = self._tokensupto2(
|
||||
tokenizer, importmediaqueryendonly=True)
|
||||
mediatokens.insert(0, token) # push found token
|
||||
|
||||
last = mediatokens.pop() # retrieve ;
|
||||
lastval, lasttyp = self._tokenvalue(last), self._type(last)
|
||||
if lastval != u';' and lasttyp not in ('EOF',
|
||||
self._prods.STRING):
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSImportRule: No ";" found: %s' %
|
||||
self._valuestr(cssText), token=token)
|
||||
|
||||
newMedia = cssutils.stylesheets.MediaList(parentRule=self)
|
||||
newMedia.mediaText = mediatokens
|
||||
if newMedia.wellformed:
|
||||
new['media'] = newMedia
|
||||
seq.append(newMedia, 'media')
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSImportRule: Invalid MediaList: %s' %
|
||||
self._valuestr(cssText), token=token)
|
||||
|
||||
if lasttyp == self._prods.STRING:
|
||||
# name
|
||||
return __doname(seq, last)
|
||||
else:
|
||||
return 'EOF' # ';' is token "last"
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSImportRule: Unexpected ident.', token)
|
||||
return expected
|
||||
|
||||
def _char(expected, seq, token, tokenizer=None):
|
||||
# final ;
|
||||
val = self._tokenvalue(token)
|
||||
if expected.endswith(';') and u';' == val:
|
||||
return 'EOF'
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(
|
||||
u'CSSImportRule: Unexpected char.', token)
|
||||
return expected
|
||||
|
||||
# import : IMPORT_SYM S* [STRING|URI]
|
||||
# S* [ medium [ ',' S* medium]* ]? ';' S*
|
||||
# STRING? # see http://www.w3.org/TR/css3-cascade/#cascading
|
||||
# ;
|
||||
newseq = self._tempSeq()
|
||||
wellformed, expected = self._parse(expected='href',
|
||||
seq=newseq, tokenizer=tokenizer,
|
||||
productions={'STRING': _string,
|
||||
'URI': _uri,
|
||||
'IDENT': _ident,
|
||||
'CHAR': _char},
|
||||
new=new)
|
||||
|
||||
# wellformed set by parse
|
||||
ok = wellformed and new['wellformed']
|
||||
|
||||
# post conditions
|
||||
if not new['href']:
|
||||
ok = False
|
||||
self._log.error(u'CSSImportRule: No href found: %s' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
if expected != 'EOF':
|
||||
ok = False
|
||||
self._log.error(u'CSSImportRule: No ";" found: %s' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
# set all
|
||||
if ok:
|
||||
self._setSeq(newseq)
|
||||
|
||||
self.atkeyword = new['keyword']
|
||||
self.hreftype = new['hreftype']
|
||||
self.name = new['name']
|
||||
|
||||
if new['media']:
|
||||
self.media = new['media']
|
||||
else:
|
||||
# must be all for @import
|
||||
self.media = cssutils.stylesheets.MediaList(mediaText=u'all')
|
||||
|
||||
# needs new self.media
|
||||
self.href = new['href']
|
||||
|
||||
cssText = property(fget=_getCssText, fset=_setCssText,
|
||||
doc="(DOM) The parsable textual representation of this rule.")
|
||||
|
||||
def _setHref(self, href):
|
||||
# set new href
|
||||
self._href = href
|
||||
# update seq
|
||||
for i, item in enumerate(self.seq):
|
||||
val, type_ = item.value, item.type
|
||||
if 'href' == type_:
|
||||
self._seq[i] = (href, type_, item.line, item.col)
|
||||
break
|
||||
|
||||
importedSheet = cssutils.css.CSSStyleSheet(media=self.media,
|
||||
ownerRule=self,
|
||||
title=self.name)
|
||||
self.hrefFound = False
|
||||
# set styleSheet
|
||||
if href and self.parentStyleSheet:
|
||||
# loading errors are all catched!
|
||||
|
||||
# relative href
|
||||
parentHref = self.parentStyleSheet.href
|
||||
if parentHref is None:
|
||||
# use cwd instead
|
||||
parentHref = cssutils.helper.path2url(os.getcwd()) + '/'
|
||||
|
||||
fullhref = urlparse.urljoin(parentHref, self.href)
|
||||
|
||||
# all possible exceptions are ignored
|
||||
try:
|
||||
usedEncoding, enctype, cssText = \
|
||||
self.parentStyleSheet._resolveImport(fullhref)
|
||||
|
||||
if cssText is None:
|
||||
# catched in next except below!
|
||||
raise IOError('Cannot read Stylesheet.')
|
||||
|
||||
# contentEncoding with parentStyleSheet.overrideEncoding,
|
||||
# HTTP or parent
|
||||
encodingOverride, encoding = None, None
|
||||
|
||||
if enctype == 0:
|
||||
encodingOverride = usedEncoding
|
||||
elif 0 < enctype < 5:
|
||||
encoding = usedEncoding
|
||||
|
||||
# inherit fetcher for @imports in styleSheet
|
||||
importedSheet._href = fullhref
|
||||
importedSheet._setFetcher(self.parentStyleSheet._fetcher)
|
||||
importedSheet._setCssTextWithEncodingOverride(
|
||||
cssText,
|
||||
encodingOverride=encodingOverride,
|
||||
encoding=encoding)
|
||||
|
||||
except (OSError, IOError, ValueError), e:
|
||||
self._log.warn(u'CSSImportRule: While processing imported '
|
||||
u'style sheet href=%s: %r'
|
||||
% (self.href, e), neverraise=True)
|
||||
|
||||
else:
|
||||
# used by resolveImports if to keep unprocessed href
|
||||
self.hrefFound = True
|
||||
|
||||
self._styleSheet = importedSheet
|
||||
|
||||
_href = None # needs to be set
|
||||
href = property(lambda self: self._href, _setHref,
|
||||
doc=u"Location of the style sheet to be imported.")
|
||||
|
||||
def _setMedia(self, media):
|
||||
"""
|
||||
:param media:
|
||||
a :class:`~cssutils.stylesheets.MediaList` or string
|
||||
"""
|
||||
self._checkReadonly()
|
||||
if isinstance(media, basestring):
|
||||
self._media = cssutils.stylesheets.MediaList(mediaText=media,
|
||||
parentRule=self)
|
||||
else:
|
||||
media._parentRule = self
|
||||
self._media = media
|
||||
|
||||
# update seq
|
||||
ihref = 0
|
||||
for i, item in enumerate(self.seq):
|
||||
if item.type == 'href':
|
||||
ihref = i
|
||||
elif item.type == 'media':
|
||||
self.seq[i] = (self._media, 'media', None, None)
|
||||
break
|
||||
else:
|
||||
# if no media until now add after href
|
||||
self.seq.insert(ihref+1,
|
||||
self._media, 'media', None, None)
|
||||
|
||||
media = property(lambda self: self._media, _setMedia,
|
||||
doc=u"(DOM) A list of media types for this rule "
|
||||
u"of type :class:`~cssutils.stylesheets.MediaList`.")
|
||||
|
||||
def _setName(self, name=u''):
|
||||
"""Raises xml.dom.SyntaxErr if name is not a string."""
|
||||
if name is None or isinstance(name, basestring):
|
||||
# "" or '' handled as None
|
||||
if not name:
|
||||
name = None
|
||||
|
||||
# save name
|
||||
self._name = name
|
||||
|
||||
# update seq
|
||||
for i, item in enumerate(self.seq):
|
||||
val, typ = item.value, item.type
|
||||
if 'name' == typ:
|
||||
self._seq[i] = (name, typ, item.line, item.col)
|
||||
break
|
||||
|
||||
# set title of imported sheet
|
||||
if self.styleSheet:
|
||||
self.styleSheet.title = name
|
||||
|
||||
else:
|
||||
self._log.error(u'CSSImportRule: Not a valid name: %s' % name)
|
||||
|
||||
name = property(lambda self: self._name, _setName,
|
||||
doc=u"An optional name for the imported sheet.")
|
||||
|
||||
styleSheet = property(lambda self: self._styleSheet,
|
||||
doc=u"(readonly) The style sheet referred to by this "
|
||||
u"rule.")
|
||||
|
||||
type = property(lambda self: self.IMPORT_RULE,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
u"type constant.")
|
||||
|
||||
def _getWellformed(self):
|
||||
"Depending on if media is used at all."
|
||||
if self._usemedia:
|
||||
return bool(self.href and self.media.wellformed)
|
||||
else:
|
||||
return bool(self.href)
|
||||
|
||||
wellformed = property(_getWellformed)
|
||||
302
libs/cssutils/css/cssmediarule.py
Executable file
@@ -0,0 +1,302 @@
|
||||
"""CSSMediaRule implements DOM Level 2 CSS CSSMediaRule."""
|
||||
__all__ = ['CSSMediaRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
import cssrule
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSMediaRule(cssrule.CSSRuleRules):
|
||||
"""
|
||||
Objects implementing the CSSMediaRule interface can be identified by the
|
||||
MEDIA_RULE constant. On these objects the type attribute must return the
|
||||
value of that constant.
|
||||
|
||||
Format::
|
||||
|
||||
: MEDIA_SYM S* medium [ COMMA S* medium ]*
|
||||
|
||||
STRING? # the name
|
||||
|
||||
LBRACE S* ruleset* '}' S*;
|
||||
|
||||
``cssRules``
|
||||
All Rules in this media rule, a :class:`~cssutils.css.CSSRuleList`.
|
||||
"""
|
||||
def __init__(self, mediaText='all', name=None,
|
||||
parentRule=None, parentStyleSheet=None, readonly=False):
|
||||
"""constructor"""
|
||||
super(CSSMediaRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = u'@media'
|
||||
|
||||
# 1. media
|
||||
if mediaText:
|
||||
self.media = mediaText
|
||||
else:
|
||||
self.media = cssutils.stylesheets.MediaList()
|
||||
|
||||
self.name = name
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
return u"cssutils.css.%s(mediaText=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.media.mediaText)
|
||||
|
||||
def __str__(self):
|
||||
return u"<cssutils.css.%s object mediaText=%r at 0x%x>" % (
|
||||
self.__class__.__name__,
|
||||
self.media.mediaText,
|
||||
id(self))
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText."""
|
||||
return cssutils.ser.do_CSSMediaRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:param cssText:
|
||||
a parseable string or a tuple of (cssText, dict-of-namespaces)
|
||||
:Exceptions:
|
||||
- :exc:`~xml.dom.NamespaceErr`:
|
||||
Raised if a specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
# media "name"? { cssRules }
|
||||
super(CSSMediaRule, self)._setCssText(cssText)
|
||||
|
||||
# might be (cssText, namespaces)
|
||||
cssText, namespaces = self._splitNamespacesOff(cssText)
|
||||
|
||||
try:
|
||||
# use parent style sheet ones if available
|
||||
namespaces = self.parentStyleSheet.namespaces
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
attoken = self._nexttoken(tokenizer, None)
|
||||
if self._type(attoken) != self._prods.MEDIA_SYM:
|
||||
self._log.error(u'CSSMediaRule: No CSSMediaRule found: %s' %
|
||||
self._valuestr(cssText),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
|
||||
else:
|
||||
# save if parse goes wrong
|
||||
oldMedia = self._media
|
||||
oldName = self._name
|
||||
oldCssRules = self._cssRules
|
||||
|
||||
ok = True
|
||||
|
||||
# media
|
||||
mediatokens, end = self._tokensupto2(tokenizer,
|
||||
mediaqueryendonly=True,
|
||||
separateEnd=True)
|
||||
if u'{' == self._tokenvalue(end)\
|
||||
or self._prods.STRING == self._type(end):
|
||||
self.media = cssutils.stylesheets.MediaList(parentRule=self)
|
||||
# TODO: remove special case
|
||||
self.media.mediaText = mediatokens
|
||||
ok = ok and self.media.wellformed
|
||||
else:
|
||||
ok = False
|
||||
|
||||
# name (optional)
|
||||
name = None
|
||||
nameseq = self._tempSeq()
|
||||
if self._prods.STRING == self._type(end):
|
||||
name = self._stringtokenvalue(end)
|
||||
# TODO: for now comments are lost after name
|
||||
nametokens, end = self._tokensupto2(tokenizer,
|
||||
blockstartonly=True,
|
||||
separateEnd=True)
|
||||
wellformed, expected = self._parse(None,
|
||||
nameseq,
|
||||
nametokens,
|
||||
{})
|
||||
if not wellformed:
|
||||
ok = False
|
||||
self._log.error(u'CSSMediaRule: Syntax Error: %s' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
|
||||
# check for {
|
||||
if u'{' != self._tokenvalue(end):
|
||||
self._log.error(u'CSSMediaRule: No "{" found: %s' %
|
||||
self._valuestr(cssText))
|
||||
return
|
||||
|
||||
# cssRules
|
||||
cssrulestokens, braceOrEOF = self._tokensupto2(tokenizer,
|
||||
mediaendonly=True,
|
||||
separateEnd=True)
|
||||
nonetoken = self._nexttoken(tokenizer, None)
|
||||
if 'EOF' == self._type(braceOrEOF):
|
||||
# HACK!!!
|
||||
# TODO: Not complete, add EOF to rule and } to @media
|
||||
cssrulestokens.append(braceOrEOF)
|
||||
braceOrEOF = ('CHAR', '}', 0, 0)
|
||||
self._log.debug(u'CSSMediaRule: Incomplete, adding "}".',
|
||||
token=braceOrEOF, neverraise=True)
|
||||
|
||||
if u'}' != self._tokenvalue(braceOrEOF):
|
||||
self._log.error(u'CSSMediaRule: No "}" found.',
|
||||
token=braceOrEOF)
|
||||
elif nonetoken:
|
||||
self._log.error(u'CSSMediaRule: Trailing content found.',
|
||||
token=nonetoken)
|
||||
else:
|
||||
# for closures: must be a mutable
|
||||
new = {'wellformed': True }
|
||||
|
||||
def COMMENT(expected, seq, token, tokenizer=None):
|
||||
self.insertRule(cssutils.css.CSSComment([token],
|
||||
parentRule=self,
|
||||
parentStyleSheet=self.parentStyleSheet))
|
||||
return expected
|
||||
|
||||
def ruleset(expected, seq, token, tokenizer):
|
||||
rule = cssutils.css.CSSStyleRule(parentRule=self,
|
||||
parentStyleSheet=self.parentStyleSheet)
|
||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||
if rule.wellformed:
|
||||
self.insertRule(rule)
|
||||
return expected
|
||||
|
||||
def atrule(expected, seq, token, tokenizer):
|
||||
# TODO: get complete rule!
|
||||
tokens = self._tokensupto2(tokenizer, token)
|
||||
atval = self._tokenvalue(token)
|
||||
if atval in ('@charset ', '@font-face', '@import',
|
||||
'@namespace', '@page', '@media', '@variables'):
|
||||
self._log.error(u'CSSMediaRule: This rule is not '
|
||||
u'allowed in CSSMediaRule - ignored: '
|
||||
u'%s.' % self._valuestr(tokens),
|
||||
token = token,
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
else:
|
||||
rule = cssutils.css.CSSUnknownRule(tokens,
|
||||
parentRule=self,
|
||||
parentStyleSheet=self.parentStyleSheet)
|
||||
if rule.wellformed:
|
||||
self.insertRule(rule)
|
||||
return expected
|
||||
|
||||
# save for possible reset
|
||||
oldCssRules = self.cssRules
|
||||
|
||||
self.cssRules = cssutils.css.CSSRuleList()
|
||||
seq = [] # not used really
|
||||
|
||||
tokenizer = iter(cssrulestokens)
|
||||
wellformed, expected = self._parse(braceOrEOF,
|
||||
seq,
|
||||
tokenizer, {
|
||||
'COMMENT': COMMENT,
|
||||
'CHARSET_SYM': atrule,
|
||||
'FONT_FACE_SYM': atrule,
|
||||
'IMPORT_SYM': atrule,
|
||||
'NAMESPACE_SYM': atrule,
|
||||
'PAGE_SYM': atrule,
|
||||
'MEDIA_SYM': atrule,
|
||||
'ATKEYWORD': atrule
|
||||
},
|
||||
default=ruleset,
|
||||
new=new)
|
||||
ok = ok and wellformed
|
||||
|
||||
if ok:
|
||||
self.name = name
|
||||
self._setSeq(nameseq)
|
||||
else:
|
||||
self._media = oldMedia
|
||||
self._cssRules = oldCssRules
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc=u"(DOM) The parsable textual representation of this "
|
||||
u"rule.")
|
||||
|
||||
def _setName(self, name):
|
||||
if isinstance(name, basestring) or name is None:
|
||||
# "" or ''
|
||||
if not name:
|
||||
name = None
|
||||
|
||||
self._name = name
|
||||
else:
|
||||
self._log.error(u'CSSImportRule: Not a valid name: %s' % name)
|
||||
|
||||
name = property(lambda self: self._name, _setName,
|
||||
doc=u"An optional name for this media rule.")
|
||||
|
||||
def _setMedia(self, media):
|
||||
"""
|
||||
:param media:
|
||||
a :class:`~cssutils.stylesheets.MediaList` or string
|
||||
"""
|
||||
self._checkReadonly()
|
||||
if isinstance(media, basestring):
|
||||
self._media = cssutils.stylesheets.MediaList(mediaText=media,
|
||||
parentRule=self)
|
||||
else:
|
||||
media._parentRule = self
|
||||
self._media = media
|
||||
|
||||
# NOT IN @media seq at all?!
|
||||
# # update seq
|
||||
# for i, item in enumerate(self.seq):
|
||||
# if item.type == 'media':
|
||||
# self._seq[i] = (self._media, 'media', None, None)
|
||||
# break
|
||||
# else:
|
||||
# # insert after @media if not in seq at all
|
||||
# self.seq.insert(0,
|
||||
# self._media, 'media', None, None)
|
||||
|
||||
media = property(lambda self: self._media, _setMedia,
|
||||
doc=u"(DOM) A list of media types for this rule "
|
||||
u"of type :class:`~cssutils.stylesheets.MediaList`.")
|
||||
|
||||
|
||||
def insertRule(self, rule, index=None):
|
||||
"""Implements base ``insertRule``."""
|
||||
rule, index = self._prepareInsertRule(rule, index)
|
||||
|
||||
if rule is False or rule is True:
|
||||
# done or error
|
||||
return
|
||||
|
||||
# check hierarchy
|
||||
if isinstance(rule, cssutils.css.CSSCharsetRule) or \
|
||||
isinstance(rule, cssutils.css.CSSFontFaceRule) or \
|
||||
isinstance(rule, cssutils.css.CSSImportRule) or \
|
||||
isinstance(rule, cssutils.css.CSSNamespaceRule) or \
|
||||
isinstance(rule, cssutils.css.CSSPageRule) or \
|
||||
isinstance(rule, cssutils.css.MarginRule) or \
|
||||
isinstance(rule, CSSMediaRule):
|
||||
self._log.error(u'%s: This type of rule is not allowed here: %s'
|
||||
% (self.__class__.__name__, rule.cssText),
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
|
||||
return self._finishInsertRule(rule, index)
|
||||
|
||||
type = property(lambda self: self.MEDIA_RULE,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
u"type constant.")
|
||||
|
||||
wellformed = property(lambda self: self.media.wellformed)
|
||||
295
libs/cssutils/css/cssnamespacerule.py
Executable file
@@ -0,0 +1,295 @@
|
||||
"""CSSNamespaceRule currently implements http://dev.w3.org/csswg/css3-namespace/
|
||||
"""
|
||||
__all__ = ['CSSNamespaceRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
import cssrule
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSNamespaceRule(cssrule.CSSRule):
|
||||
"""
|
||||
Represents an @namespace rule within a CSS style sheet.
|
||||
|
||||
The @namespace at-rule declares a namespace prefix and associates
|
||||
it with a given namespace (a string). This namespace prefix can then be
|
||||
used in namespace-qualified names such as those described in the
|
||||
Selectors Module [SELECT] or the Values and Units module [CSS3VAL].
|
||||
|
||||
Dealing with these rules directly is not needed anymore, easier is
|
||||
the use of :attr:`cssutils.css.CSSStyleSheet.namespaces`.
|
||||
|
||||
Format::
|
||||
|
||||
namespace
|
||||
: NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
|
||||
;
|
||||
namespace_prefix
|
||||
: IDENT
|
||||
;
|
||||
"""
|
||||
def __init__(self, namespaceURI=None, prefix=None, cssText=None,
|
||||
parentRule=None, parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
:Parameters:
|
||||
namespaceURI
|
||||
The namespace URI (a simple string!) which is bound to the
|
||||
given prefix. If no prefix is set
|
||||
(``CSSNamespaceRule.prefix==''``) the namespace defined by
|
||||
namespaceURI is set as the default namespace
|
||||
prefix
|
||||
The prefix used in the stylesheet for the given
|
||||
``CSSNamespaceRule.uri``.
|
||||
cssText
|
||||
if no namespaceURI is given cssText must be given to set
|
||||
a namespaceURI as this is readonly later on
|
||||
parentStyleSheet
|
||||
sheet where this rule belongs to
|
||||
|
||||
Do not use as positional but as keyword parameters only!
|
||||
|
||||
If readonly allows setting of properties in constructor only
|
||||
|
||||
format namespace::
|
||||
|
||||
namespace
|
||||
: NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
|
||||
;
|
||||
namespace_prefix
|
||||
: IDENT
|
||||
;
|
||||
"""
|
||||
super(CSSNamespaceRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = u'@namespace'
|
||||
self._prefix = u''
|
||||
self._namespaceURI = None
|
||||
|
||||
if namespaceURI:
|
||||
self.namespaceURI = namespaceURI
|
||||
self.prefix = prefix
|
||||
tempseq = self._tempSeq()
|
||||
tempseq.append(self.prefix, 'prefix')
|
||||
tempseq.append(self.namespaceURI, 'namespaceURI')
|
||||
self._setSeq(tempseq)
|
||||
|
||||
elif cssText is not None:
|
||||
self.cssText = cssText
|
||||
|
||||
if parentStyleSheet:
|
||||
self._parentStyleSheet = parentStyleSheet
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
return u"cssutils.css.%s(namespaceURI=%r, prefix=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.namespaceURI,
|
||||
self.prefix)
|
||||
|
||||
def __str__(self):
|
||||
return u"<cssutils.css.%s object namespaceURI=%r prefix=%r at 0x%x>" % (
|
||||
self.__class__.__name__,
|
||||
self.namespaceURI,
|
||||
self.prefix,
|
||||
id(self))
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText"""
|
||||
return cssutils.ser.do_CSSNamespaceRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:param cssText: initial value for this rules cssText which is parsed
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
"""
|
||||
super(CSSNamespaceRule, self)._setCssText(cssText)
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
attoken = self._nexttoken(tokenizer, None)
|
||||
if self._type(attoken) != self._prods.NAMESPACE_SYM:
|
||||
self._log.error(u'CSSNamespaceRule: No CSSNamespaceRule found: %s' %
|
||||
self._valuestr(cssText),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
else:
|
||||
# for closures: must be a mutable
|
||||
new = {'keyword': self._tokenvalue(attoken),
|
||||
'prefix': u'',
|
||||
'uri': None,
|
||||
'wellformed': True
|
||||
}
|
||||
|
||||
def _ident(expected, seq, token, tokenizer=None):
|
||||
# the namespace prefix, optional
|
||||
if 'prefix or uri' == expected:
|
||||
new['prefix'] = self._tokenvalue(token)
|
||||
seq.append(new['prefix'], 'prefix')
|
||||
return 'uri'
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(
|
||||
u'CSSNamespaceRule: Unexpected ident.', token)
|
||||
return expected
|
||||
|
||||
def _string(expected, seq, token, tokenizer=None):
|
||||
# the namespace URI as a STRING
|
||||
if expected.endswith('uri'):
|
||||
new['uri'] = self._stringtokenvalue(token)
|
||||
seq.append(new['uri'], 'namespaceURI')
|
||||
return ';'
|
||||
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(
|
||||
u'CSSNamespaceRule: Unexpected string.', token)
|
||||
return expected
|
||||
|
||||
def _uri(expected, seq, token, tokenizer=None):
|
||||
# the namespace URI as URI which is DEPRECATED
|
||||
if expected.endswith('uri'):
|
||||
uri = self._uritokenvalue(token)
|
||||
new['uri'] = uri
|
||||
seq.append(new['uri'], 'namespaceURI')
|
||||
return ';'
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(
|
||||
u'CSSNamespaceRule: Unexpected URI.', token)
|
||||
return expected
|
||||
|
||||
def _char(expected, seq, token, tokenizer=None):
|
||||
# final ;
|
||||
val = self._tokenvalue(token)
|
||||
if ';' == expected and u';' == val:
|
||||
return 'EOF'
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(
|
||||
u'CSSNamespaceRule: Unexpected char.', token)
|
||||
return expected
|
||||
|
||||
# "NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*"
|
||||
newseq = self._tempSeq()
|
||||
wellformed, expected = self._parse(expected='prefix or uri',
|
||||
seq=newseq, tokenizer=tokenizer,
|
||||
productions={'IDENT': _ident,
|
||||
'STRING': _string,
|
||||
'URI': _uri,
|
||||
'CHAR': _char},
|
||||
new=new)
|
||||
|
||||
# wellformed set by parse
|
||||
wellformed = wellformed and new['wellformed']
|
||||
|
||||
# post conditions
|
||||
if new['uri'] is None:
|
||||
wellformed = False
|
||||
self._log.error(u'CSSNamespaceRule: No namespace URI found: %s'
|
||||
% self._valuestr(cssText))
|
||||
|
||||
if expected != 'EOF':
|
||||
wellformed = False
|
||||
self._log.error(u'CSSNamespaceRule: No ";" found: %s' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
# set all
|
||||
if wellformed:
|
||||
self.atkeyword = new['keyword']
|
||||
self._prefix = new['prefix']
|
||||
self.namespaceURI = new['uri']
|
||||
self._setSeq(newseq)
|
||||
|
||||
cssText = property(fget=_getCssText, fset=_setCssText,
|
||||
doc=u"(DOM) The parsable textual representation of this "
|
||||
u"rule.")
|
||||
|
||||
def _setNamespaceURI(self, namespaceURI):
|
||||
"""
|
||||
:param namespaceURI: the initial value for this rules namespaceURI
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
(CSSRule) Raised if this rule is readonly or a namespaceURI is
|
||||
already set in this rule.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
if not self._namespaceURI:
|
||||
# initial setting
|
||||
self._namespaceURI = namespaceURI
|
||||
tempseq = self._tempSeq()
|
||||
tempseq.append(namespaceURI, 'namespaceURI')
|
||||
self._setSeq(tempseq) # makes seq readonly!
|
||||
elif self._namespaceURI != namespaceURI:
|
||||
self._log.error(u'CSSNamespaceRule: namespaceURI is readonly.',
|
||||
error=xml.dom.NoModificationAllowedErr)
|
||||
|
||||
namespaceURI = property(lambda self: self._namespaceURI, _setNamespaceURI,
|
||||
doc="URI (handled as simple string) of the defined namespace.")
|
||||
|
||||
def _replaceNamespaceURI(self, namespaceURI):
|
||||
"""Used during parse of new sheet only!
|
||||
|
||||
:param namespaceURI: the new value for this rules namespaceURI
|
||||
"""
|
||||
self._namespaceURI = namespaceURI
|
||||
for i, x in enumerate(self._seq):
|
||||
if 'namespaceURI' == x.type:
|
||||
self._seq._readonly = False
|
||||
self._seq.replace(i, namespaceURI, 'namespaceURI')
|
||||
self._seq._readonly = True
|
||||
break
|
||||
|
||||
def _setPrefix(self, prefix=None):
|
||||
"""
|
||||
:param prefix: the new prefix
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
if not prefix:
|
||||
prefix = u''
|
||||
else:
|
||||
tokenizer = self._tokenize2(prefix)
|
||||
prefixtoken = self._nexttoken(tokenizer, None)
|
||||
if not prefixtoken or self._type(prefixtoken) != self._prods.IDENT:
|
||||
self._log.error(u'CSSNamespaceRule: No valid prefix "%s".' %
|
||||
self._valuestr(prefix),
|
||||
error=xml.dom.SyntaxErr)
|
||||
return
|
||||
else:
|
||||
prefix = self._tokenvalue(prefixtoken)
|
||||
# update seq
|
||||
for i, x in enumerate(self._seq):
|
||||
if x == self._prefix:
|
||||
self._seq[i] = (prefix, 'prefix', None, None)
|
||||
break
|
||||
else:
|
||||
# put prefix at the beginning!
|
||||
self._seq[0] = (prefix, 'prefix', None, None)
|
||||
|
||||
# set new prefix
|
||||
self._prefix = prefix
|
||||
|
||||
prefix = property(lambda self: self._prefix, _setPrefix,
|
||||
doc=u"Prefix used for the defined namespace.")
|
||||
|
||||
type = property(lambda self: self.NAMESPACE_RULE,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
u"type constant.")
|
||||
|
||||
wellformed = property(lambda self: self.namespaceURI is not None)
|
||||
|
||||
436
libs/cssutils/css/csspagerule.py
Executable file
@@ -0,0 +1,436 @@
|
||||
"""CSSPageRule implements DOM Level 2 CSS CSSPageRule."""
|
||||
__all__ = ['CSSPageRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
from itertools import chain
|
||||
from cssstyledeclaration import CSSStyleDeclaration
|
||||
from marginrule import MarginRule
|
||||
import cssrule
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSPageRule(cssrule.CSSRuleRules):
|
||||
"""
|
||||
The CSSPageRule interface represents a @page rule within a CSS style
|
||||
sheet. The @page rule is used to specify the dimensions, orientation,
|
||||
margins, etc. of a page box for paged media.
|
||||
|
||||
Format::
|
||||
|
||||
page :
|
||||
PAGE_SYM S* IDENT? pseudo_page? S*
|
||||
'{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
|
||||
;
|
||||
|
||||
pseudo_page :
|
||||
':' [ "left" | "right" | "first" ]
|
||||
;
|
||||
|
||||
margin :
|
||||
margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
|
||||
;
|
||||
|
||||
margin_sym :
|
||||
TOPLEFTCORNER_SYM |
|
||||
TOPLEFT_SYM |
|
||||
TOPCENTER_SYM |
|
||||
TOPRIGHT_SYM |
|
||||
TOPRIGHTCORNER_SYM |
|
||||
BOTTOMLEFTCORNER_SYM |
|
||||
BOTTOMLEFT_SYM |
|
||||
BOTTOMCENTER_SYM |
|
||||
BOTTOMRIGHT_SYM |
|
||||
BOTTOMRIGHTCORNER_SYM |
|
||||
LEFTTOP_SYM |
|
||||
LEFTMIDDLE_SYM |
|
||||
LEFTBOTTOM_SYM |
|
||||
RIGHTTOP_SYM |
|
||||
RIGHTMIDDLE_SYM |
|
||||
RIGHTBOTTOM_SYM
|
||||
;
|
||||
|
||||
`cssRules` contains a list of `MarginRule` objects.
|
||||
"""
|
||||
def __init__(self, selectorText=None, style=None, parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
If readonly allows setting of properties in constructor only.
|
||||
|
||||
:param selectorText:
|
||||
type string
|
||||
:param style:
|
||||
CSSStyleDeclaration for this CSSStyleRule
|
||||
"""
|
||||
super(CSSPageRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = u'@page'
|
||||
self._specificity = (0, 0, 0)
|
||||
|
||||
tempseq = self._tempSeq()
|
||||
|
||||
if selectorText:
|
||||
self.selectorText = selectorText
|
||||
tempseq.append(self.selectorText, 'selectorText')
|
||||
else:
|
||||
self._selectorText = self._tempSeq()
|
||||
|
||||
if style:
|
||||
self.style = style
|
||||
else:
|
||||
self.style = CSSStyleDeclaration()
|
||||
|
||||
tempseq.append(self.style, 'style')
|
||||
|
||||
self._setSeq(tempseq)
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
return u"cssutils.css.%s(selectorText=%r, style=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.selectorText,
|
||||
self.style.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return (u"<cssutils.css.%s object selectorText=%r specificity=%r "+
|
||||
u"style=%r cssRules=%r at 0x%x>") % (
|
||||
self.__class__.__name__,
|
||||
self.selectorText,
|
||||
self.specificity,
|
||||
self.style.cssText,
|
||||
len(self.cssRules),
|
||||
id(self))
|
||||
|
||||
def __contains__(self, margin):
|
||||
"""Check if margin is set in the rule."""
|
||||
return margin in self.keys()
|
||||
|
||||
def keys(self):
|
||||
"Return list of all set margins (MarginRule)."
|
||||
return list(r.margin for r in self.cssRules)
|
||||
|
||||
def __getitem__(self, margin):
|
||||
"""Retrieve the style (of MarginRule)
|
||||
for `margin` (which must be normalized).
|
||||
"""
|
||||
for r in self.cssRules:
|
||||
if r.margin == margin:
|
||||
return r.style
|
||||
|
||||
def __setitem__(self, margin, style):
|
||||
"""Set the style (of MarginRule)
|
||||
for `margin` (which must be normalized).
|
||||
"""
|
||||
for i, r in enumerate(self.cssRules):
|
||||
if r.margin == margin:
|
||||
r.style = style
|
||||
return i
|
||||
else:
|
||||
return self.add(MarginRule(margin, style))
|
||||
|
||||
def __delitem__(self, margin):
|
||||
"""Delete the style (the MarginRule)
|
||||
for `margin` (which must be normalized).
|
||||
"""
|
||||
for r in self.cssRules:
|
||||
if r.margin == margin:
|
||||
self.deleteRule(r)
|
||||
|
||||
def __parseSelectorText(self, selectorText):
|
||||
"""
|
||||
Parse `selectorText` which may also be a list of tokens
|
||||
and returns (selectorText, seq).
|
||||
|
||||
see _setSelectorText for details
|
||||
"""
|
||||
# for closures: must be a mutable
|
||||
new = {'wellformed': True, 'last-S': False,
|
||||
'name': 0, 'first': 0, 'lr': 0}
|
||||
specificity = (0, 0, 0)
|
||||
|
||||
def _char(expected, seq, token, tokenizer=None):
|
||||
# pseudo_page, :left, :right or :first
|
||||
val = self._tokenvalue(token)
|
||||
if not new['last-S'] and expected in ['page', ': or EOF']\
|
||||
and u':' == val:
|
||||
try:
|
||||
identtoken = tokenizer.next()
|
||||
except StopIteration:
|
||||
self._log.error(
|
||||
u'CSSPageRule selectorText: No IDENT found.', token)
|
||||
else:
|
||||
ival, ityp = self._tokenvalue(identtoken),\
|
||||
self._type(identtoken)
|
||||
if self._prods.IDENT != ityp:
|
||||
self._log.error(u'CSSPageRule selectorText: Expected '
|
||||
u'IDENT but found: %r' % ival, token)
|
||||
else:
|
||||
if not ival in (u'first', u'left', u'right'):
|
||||
self._log.warn(u'CSSPageRule: Unknown @page '
|
||||
u'selector: %r'
|
||||
% (u':'+ival,), neverraise=True)
|
||||
if ival == u'first':
|
||||
new['first'] = 1
|
||||
else:
|
||||
new['lr'] = 1
|
||||
seq.append(val + ival, 'pseudo')
|
||||
return 'EOF'
|
||||
return expected
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSPageRule selectorText: Unexpected CHAR: %r'
|
||||
% val, token)
|
||||
return expected
|
||||
|
||||
def S(expected, seq, token, tokenizer=None):
|
||||
"Does not raise if EOF is found."
|
||||
if expected == ': or EOF':
|
||||
# pseudo must directly follow IDENT if given
|
||||
new['last-S'] = True
|
||||
return expected
|
||||
|
||||
def IDENT(expected, seq, token, tokenizer=None):
|
||||
""
|
||||
val = self._tokenvalue(token)
|
||||
if 'page' == expected:
|
||||
if self._normalize(val) == u'auto':
|
||||
self._log.error(u'CSSPageRule selectorText: Invalid pagename.',
|
||||
token)
|
||||
else:
|
||||
new['name'] = 1
|
||||
seq.append(val, 'IDENT')
|
||||
|
||||
return ': or EOF'
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSPageRule selectorText: Unexpected IDENT: '
|
||||
u'%r' % val, token)
|
||||
return expected
|
||||
|
||||
def COMMENT(expected, seq, token, tokenizer=None):
|
||||
"Does not raise if EOF is found."
|
||||
seq.append(cssutils.css.CSSComment([token]), 'COMMENT')
|
||||
return expected
|
||||
|
||||
newseq = self._tempSeq()
|
||||
wellformed, expected = self._parse(expected='page',
|
||||
seq=newseq, tokenizer=self._tokenize2(selectorText),
|
||||
productions={'CHAR': _char,
|
||||
'IDENT': IDENT,
|
||||
'COMMENT': COMMENT,
|
||||
'S': S},
|
||||
new=new)
|
||||
wellformed = wellformed and new['wellformed']
|
||||
|
||||
# post conditions
|
||||
if expected == 'ident':
|
||||
self._log.error(
|
||||
u'CSSPageRule selectorText: No valid selector: %r' %
|
||||
self._valuestr(selectorText))
|
||||
|
||||
return wellformed, newseq, (new['name'], new['first'], new['lr'])
|
||||
|
||||
|
||||
def __parseMarginAndStyle(self, tokens):
|
||||
"tokens is a list, no generator (yet)"
|
||||
g = iter(tokens)
|
||||
styletokens = []
|
||||
|
||||
# new rules until parse done
|
||||
cssRules = []
|
||||
|
||||
for token in g:
|
||||
if token[0] == 'ATKEYWORD' and \
|
||||
self._normalize(token[1]) in MarginRule.margins:
|
||||
|
||||
# MarginRule
|
||||
m = MarginRule(parentRule=self,
|
||||
parentStyleSheet=self.parentStyleSheet)
|
||||
m.cssText = chain([token], g)
|
||||
|
||||
# merge if margin set more than once
|
||||
for r in cssRules:
|
||||
if r.margin == m.margin:
|
||||
for p in m.style:
|
||||
r.style.setProperty(p, replace=False)
|
||||
break
|
||||
else:
|
||||
cssRules.append(m)
|
||||
|
||||
continue
|
||||
|
||||
# TODO: Properties?
|
||||
styletokens.append(token)
|
||||
|
||||
return cssRules, styletokens
|
||||
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText."""
|
||||
return cssutils.ser.do_CSSPageRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
super(CSSPageRule, self)._setCssText(cssText)
|
||||
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
if self._type(self._nexttoken(tokenizer)) != self._prods.PAGE_SYM:
|
||||
self._log.error(u'CSSPageRule: No CSSPageRule found: %s' %
|
||||
self._valuestr(cssText),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
else:
|
||||
newStyle = CSSStyleDeclaration(parentRule=self)
|
||||
ok = True
|
||||
|
||||
selectortokens, startbrace = self._tokensupto2(tokenizer,
|
||||
blockstartonly=True,
|
||||
separateEnd=True)
|
||||
styletokens, braceorEOFtoken = self._tokensupto2(tokenizer,
|
||||
blockendonly=True,
|
||||
separateEnd=True)
|
||||
nonetoken = self._nexttoken(tokenizer)
|
||||
if self._tokenvalue(startbrace) != u'{':
|
||||
ok = False
|
||||
self._log.error(u'CSSPageRule: No start { of style declaration '
|
||||
u'found: %r' %
|
||||
self._valuestr(cssText), startbrace)
|
||||
elif nonetoken:
|
||||
ok = False
|
||||
self._log.error(u'CSSPageRule: Trailing content found.',
|
||||
token=nonetoken)
|
||||
|
||||
selok, newselseq, specificity = self.__parseSelectorText(selectortokens)
|
||||
ok = ok and selok
|
||||
|
||||
val, type_ = self._tokenvalue(braceorEOFtoken),\
|
||||
self._type(braceorEOFtoken)
|
||||
|
||||
if val != u'}' and type_ != 'EOF':
|
||||
ok = False
|
||||
self._log.error(
|
||||
u'CSSPageRule: No "}" after style declaration found: %r' %
|
||||
self._valuestr(cssText))
|
||||
else:
|
||||
if 'EOF' == type_:
|
||||
# add again as style needs it
|
||||
styletokens.append(braceorEOFtoken)
|
||||
|
||||
# filter pagemargin rules out first
|
||||
cssRules, styletokens = self.__parseMarginAndStyle(styletokens)
|
||||
|
||||
# SET, may raise:
|
||||
newStyle.cssText = styletokens
|
||||
|
||||
if ok:
|
||||
self._selectorText = newselseq
|
||||
self._specificity = specificity
|
||||
self.style = newStyle
|
||||
self.cssRules = cssutils.css.CSSRuleList()
|
||||
for r in cssRules:
|
||||
self.cssRules.append(r)
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc=u"(DOM) The parsable textual representation of this rule.")
|
||||
|
||||
|
||||
def _getSelectorText(self):
|
||||
"""Wrapper for cssutils Selector object."""
|
||||
return cssutils.ser.do_CSSPageRuleSelector(self._selectorText)
|
||||
|
||||
def _setSelectorText(self, selectorText):
|
||||
"""Wrapper for cssutils Selector object.
|
||||
|
||||
:param selectorText:
|
||||
DOM String, in CSS 2.1 one of
|
||||
|
||||
- :first
|
||||
- :left
|
||||
- :right
|
||||
- empty
|
||||
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
and is unparsable.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
# may raise SYNTAX_ERR
|
||||
wellformed, newseq, specificity = self.__parseSelectorText(selectorText)
|
||||
if wellformed:
|
||||
self._selectorText = newseq
|
||||
self._specificity = specificity
|
||||
|
||||
selectorText = property(_getSelectorText, _setSelectorText,
|
||||
doc=u"(DOM) The parsable textual representation of "
|
||||
u"the page selector for the rule.")
|
||||
|
||||
def _setStyle(self, style):
|
||||
"""
|
||||
:param style:
|
||||
a CSSStyleDeclaration or string
|
||||
"""
|
||||
self._checkReadonly()
|
||||
if isinstance(style, basestring):
|
||||
self._style = CSSStyleDeclaration(cssText=style, parentRule=self)
|
||||
else:
|
||||
style._parentRule = self
|
||||
self._style = style
|
||||
|
||||
style = property(lambda self: self._style, _setStyle,
|
||||
doc=u"(DOM) The declaration-block of this rule set, "
|
||||
u"a :class:`~cssutils.css.CSSStyleDeclaration`.")
|
||||
|
||||
|
||||
def insertRule(self, rule, index=None):
|
||||
"""Implements base ``insertRule``."""
|
||||
rule, index = self._prepareInsertRule(rule, index)
|
||||
|
||||
if rule is False or rule is True:
|
||||
# done or error
|
||||
return
|
||||
|
||||
# check hierarchy
|
||||
if isinstance(rule, cssutils.css.CSSCharsetRule) or \
|
||||
isinstance(rule, cssutils.css.CSSFontFaceRule) or \
|
||||
isinstance(rule, cssutils.css.CSSImportRule) or \
|
||||
isinstance(rule, cssutils.css.CSSNamespaceRule) or \
|
||||
isinstance(rule, CSSPageRule) or \
|
||||
isinstance(rule, cssutils.css.CSSMediaRule):
|
||||
self._log.error(u'%s: This type of rule is not allowed here: %s'
|
||||
% (self.__class__.__name__, rule.cssText),
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
|
||||
return self._finishInsertRule(rule, index)
|
||||
|
||||
specificity = property(lambda self: self._specificity,
|
||||
doc=u"""Specificity of this page rule (READONLY).
|
||||
Tuple of (f, g, h) where:
|
||||
|
||||
- if the page selector has a named page, f=1; else f=0
|
||||
- if the page selector has a ':first' pseudo-class, g=1; else g=0
|
||||
- if the page selector has a ':left' or ':right' pseudo-class, h=1; else h=0
|
||||
""")
|
||||
|
||||
type = property(lambda self: self.PAGE_RULE,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
u"type constant.")
|
||||
|
||||
# constant but needed:
|
||||
wellformed = property(lambda self: True)
|
||||
122
libs/cssutils/css/cssproperties.py
Executable file
@@ -0,0 +1,122 @@
|
||||
"""CSS2Properties (partly!) implements DOM Level 2 CSS CSS2Properties used
|
||||
by CSSStyleDeclaration
|
||||
|
||||
TODO: CSS2Properties
|
||||
If an implementation does implement this interface, it is expected to
|
||||
understand the specific syntax of the shorthand properties, and apply
|
||||
their semantics; when the margin property is set, for example, the
|
||||
marginTop, marginRight, marginBottom and marginLeft properties are
|
||||
actually being set by the underlying implementation.
|
||||
|
||||
When dealing with CSS "shorthand" properties, the shorthand properties
|
||||
should be decomposed into their component longhand properties as
|
||||
appropriate, and when querying for their value, the form returned
|
||||
should be the shortest form exactly equivalent to the declarations made
|
||||
in the ruleset. However, if there is no shorthand declaration that
|
||||
could be added to the ruleset without changing in any way the rules
|
||||
already declared in the ruleset (i.e., by adding longhand rules that
|
||||
were previously not declared in the ruleset), then the empty string
|
||||
should be returned for the shorthand property.
|
||||
|
||||
For example, querying for the font property should not return
|
||||
"normal normal normal 14pt/normal Arial, sans-serif", when
|
||||
"14pt Arial, sans-serif" suffices. (The normals are initial values, and
|
||||
are implied by use of the longhand property.)
|
||||
|
||||
If the values for all the longhand properties that compose a particular
|
||||
string are the initial values, then a string consisting of all the
|
||||
initial values should be returned (e.g. a border-width value of
|
||||
"medium" should be returned as such, not as "").
|
||||
|
||||
For some shorthand properties that take missing values from other
|
||||
sides, such as the margin, padding, and border-[width|style|color]
|
||||
properties, the minimum number of sides possible should be used; i.e.,
|
||||
"0px 10px" will be returned instead of "0px 10px 0px 10px".
|
||||
|
||||
If the value of a shorthand property can not be decomposed into its
|
||||
component longhand properties, as is the case for the font property
|
||||
with a value of "menu", querying for the values of the component
|
||||
longhand properties should return the empty string.
|
||||
|
||||
TODO: CSS2Properties DOMImplementation
|
||||
The interface found within this section are not mandatory. A DOM
|
||||
application can use the hasFeature method of the DOMImplementation
|
||||
interface to determine whether it is supported or not. The feature
|
||||
string for this extended interface listed in this section is "CSS2"
|
||||
and the version is "2.0".
|
||||
|
||||
"""
|
||||
__all__ = ['CSS2Properties']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
import cssutils.profiles
|
||||
import re
|
||||
|
||||
class CSS2Properties(object):
|
||||
"""The CSS2Properties interface represents a convenience mechanism
|
||||
for retrieving and setting properties within a CSSStyleDeclaration.
|
||||
The attributes of this interface correspond to all the properties
|
||||
specified in CSS2. Getting an attribute of this interface is
|
||||
equivalent to calling the getPropertyValue method of the
|
||||
CSSStyleDeclaration interface. Setting an attribute of this
|
||||
interface is equivalent to calling the setProperty method of the
|
||||
CSSStyleDeclaration interface.
|
||||
|
||||
cssutils actually also allows usage of ``del`` to remove a CSS property
|
||||
from a CSSStyleDeclaration.
|
||||
|
||||
This is an abstract class, the following functions need to be present
|
||||
in inheriting class:
|
||||
|
||||
- ``_getP``
|
||||
- ``_setP``
|
||||
- ``_delP``
|
||||
"""
|
||||
# actual properties are set after the class definition!
|
||||
def _getP(self, CSSname): pass
|
||||
def _setP(self, CSSname, value): pass
|
||||
def _delP(self, CSSname): pass
|
||||
|
||||
|
||||
_reCSStoDOMname = re.compile('-[a-z]', re.I)
|
||||
def _toDOMname(CSSname):
|
||||
"""Returns DOMname for given CSSname e.g. for CSSname 'font-style' returns
|
||||
'fontStyle'.
|
||||
"""
|
||||
def _doCSStoDOMname2(m): return m.group(0)[1].capitalize()
|
||||
return _reCSStoDOMname.sub(_doCSStoDOMname2, CSSname)
|
||||
|
||||
_reDOMtoCSSname = re.compile('([A-Z])[a-z]+')
|
||||
def _toCSSname(DOMname):
|
||||
"""Return CSSname for given DOMname e.g. for DOMname 'fontStyle' returns
|
||||
'font-style'.
|
||||
"""
|
||||
def _doDOMtoCSSname2(m): return '-' + m.group(0).lower()
|
||||
return _reDOMtoCSSname.sub(_doDOMtoCSSname2, DOMname)
|
||||
|
||||
# add list of DOMname properties to CSS2Properties
|
||||
# used for CSSStyleDeclaration to check if allowed properties
|
||||
# but somehow doubled, any better way?
|
||||
CSS2Properties._properties = []
|
||||
for group in cssutils.profiles.properties:
|
||||
for name in cssutils.profiles.properties[group]:
|
||||
CSS2Properties._properties.append(_toDOMname(name))
|
||||
|
||||
|
||||
# add CSS2Properties to CSSStyleDeclaration:
|
||||
def __named_property_def(DOMname):
|
||||
"""
|
||||
Closure to keep name known in each properties accessor function
|
||||
DOMname is converted to CSSname here, so actual calls use CSSname.
|
||||
"""
|
||||
CSSname = _toCSSname(DOMname)
|
||||
def _get(self): return self._getP(CSSname)
|
||||
def _set(self, value): self._setP(CSSname, value)
|
||||
def _del(self): self._delP(CSSname)
|
||||
return _get, _set, _del
|
||||
|
||||
# add all CSS2Properties to CSSStyleDeclaration
|
||||
for DOMname in CSS2Properties._properties:
|
||||
setattr(CSS2Properties, DOMname,
|
||||
property(*__named_property_def(DOMname)))
|
||||
304
libs/cssutils/css/cssrule.py
Executable file
@@ -0,0 +1,304 @@
|
||||
"""CSSRule implements DOM Level 2 CSS CSSRule."""
|
||||
__all__ = ['CSSRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSRule(cssutils.util.Base2):
|
||||
"""Abstract base interface for any type of CSS statement. This includes
|
||||
both rule sets and at-rules. An implementation is expected to preserve
|
||||
all rules specified in a CSS style sheet, even if the rule is not
|
||||
recognized by the parser. Unrecognized rules are represented using the
|
||||
:class:`CSSUnknownRule` interface.
|
||||
"""
|
||||
|
||||
"""
|
||||
CSSRule type constants.
|
||||
An integer indicating which type of rule this is.
|
||||
"""
|
||||
UNKNOWN_RULE = 0
|
||||
":class:`cssutils.css.CSSUnknownRule` (not used in CSSOM anymore)"
|
||||
STYLE_RULE = 1
|
||||
":class:`cssutils.css.CSSStyleRule`"
|
||||
CHARSET_RULE = 2
|
||||
":class:`cssutils.css.CSSCharsetRule` (not used in CSSOM anymore)"
|
||||
IMPORT_RULE = 3
|
||||
":class:`cssutils.css.CSSImportRule`"
|
||||
MEDIA_RULE = 4
|
||||
":class:`cssutils.css.CSSMediaRule`"
|
||||
FONT_FACE_RULE = 5
|
||||
":class:`cssutils.css.CSSFontFaceRule`"
|
||||
PAGE_RULE = 6
|
||||
":class:`cssutils.css.CSSPageRule`"
|
||||
NAMESPACE_RULE = 10
|
||||
""":class:`cssutils.css.CSSNamespaceRule`,
|
||||
Value has changed in 0.9.7a3 due to a change in the CSSOM spec."""
|
||||
COMMENT = 1001 # was -1, cssutils only
|
||||
""":class:`cssutils.css.CSSComment` - not in the offical spec,
|
||||
Value has changed in 0.9.7a3"""
|
||||
VARIABLES_RULE = 1008
|
||||
""":class:`cssutils.css.CSSVariablesRule` - experimental rule
|
||||
not in the offical spec"""
|
||||
|
||||
MARGIN_RULE = 1006
|
||||
""":class:`cssutils.css.MarginRule` - experimental rule
|
||||
not in the offical spec"""
|
||||
|
||||
_typestrings = {UNKNOWN_RULE: u'UNKNOWN_RULE',
|
||||
STYLE_RULE: u'STYLE_RULE',
|
||||
CHARSET_RULE: u'CHARSET_RULE',
|
||||
IMPORT_RULE: u'IMPORT_RULE',
|
||||
MEDIA_RULE: u'MEDIA_RULE',
|
||||
FONT_FACE_RULE: u'FONT_FACE_RULE',
|
||||
PAGE_RULE: u'PAGE_RULE',
|
||||
NAMESPACE_RULE: u'NAMESPACE_RULE',
|
||||
COMMENT: u'COMMENT',
|
||||
VARIABLES_RULE: u'VARIABLES_RULE',
|
||||
MARGIN_RULE: u'MARGIN_RULE'
|
||||
}
|
||||
|
||||
def __init__(self, parentRule=None, parentStyleSheet=None, readonly=False):
|
||||
"""Set common attributes for all rules."""
|
||||
super(CSSRule, self).__init__()
|
||||
self._parent = parentRule
|
||||
self._parentRule = parentRule
|
||||
self._parentStyleSheet = parentStyleSheet
|
||||
self._setSeq(self._tempSeq())
|
||||
#self._atkeyword = None
|
||||
# must be set after initialization of #inheriting rule is done
|
||||
self._readonly = False
|
||||
|
||||
def _setAtkeyword(self, keyword):
|
||||
"""Check if new keyword fits the rule it is used for."""
|
||||
atkeyword = self._normalize(keyword)
|
||||
if not self.atkeyword or (self.atkeyword == atkeyword):
|
||||
self._atkeyword = atkeyword
|
||||
self._keyword = keyword
|
||||
else:
|
||||
self._log.error(u'%s: Invalid atkeyword for this rule: %r' %
|
||||
(self.atkeyword, keyword),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
|
||||
atkeyword = property(lambda self: self._atkeyword, _setAtkeyword,
|
||||
doc=u"Normalized keyword of an @rule (e.g. ``@import``).")
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:param cssText:
|
||||
A parsable DOMString.
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
cssText = property(lambda self: u'', _setCssText,
|
||||
doc=u"(DOM) The parsable textual representation of the "
|
||||
u"rule. This reflects the current state of the rule "
|
||||
u"and not its initial value.")
|
||||
|
||||
parent = property(lambda self: self._parent,
|
||||
doc=u"The Parent Node of this CSSRule or None.")
|
||||
|
||||
parentRule = property(lambda self: self._parentRule,
|
||||
doc=u"If this rule is contained inside another rule "
|
||||
u"(e.g. a style rule inside an @media block), this "
|
||||
u"is the containing rule. If this rule is not nested "
|
||||
u"inside any other rules, this returns None.")
|
||||
|
||||
def _getParentStyleSheet(self):
|
||||
# rules contained in other rules (@media) use that rules parent
|
||||
if (self.parentRule):
|
||||
return self.parentRule._parentStyleSheet
|
||||
else:
|
||||
return self._parentStyleSheet
|
||||
|
||||
parentStyleSheet = property(_getParentStyleSheet,
|
||||
doc=u"The style sheet that contains this rule.")
|
||||
|
||||
type = property(lambda self: self.UNKNOWN_RULE,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
u"type constant.")
|
||||
|
||||
typeString = property(lambda self: CSSRule._typestrings[self.type],
|
||||
doc=u"Descriptive name of this rule's type.")
|
||||
|
||||
wellformed = property(lambda self: False,
|
||||
doc=u"If the rule is wellformed.")
|
||||
|
||||
|
||||
|
||||
class CSSRuleRules(CSSRule):
|
||||
"""Abstract base interface for rules that contain other rules
|
||||
like @media or @page. Methods may be overwritten if a rule
|
||||
has specific stuff to do like checking the order of insertion like
|
||||
@media does.
|
||||
"""
|
||||
|
||||
def __init__(self, parentRule=None, parentStyleSheet=None):
|
||||
|
||||
super(CSSRuleRules, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
|
||||
self.cssRules = cssutils.css.CSSRuleList()
|
||||
|
||||
def __iter__(self):
|
||||
"""Generator iterating over these rule's cssRules."""
|
||||
for rule in self._cssRules:
|
||||
yield rule
|
||||
|
||||
def _setCssRules(self, cssRules):
|
||||
"Set new cssRules and update contained rules refs."
|
||||
cssRules.append = self.insertRule
|
||||
cssRules.extend = self.insertRule
|
||||
cssRules.__delitem__ == self.deleteRule
|
||||
|
||||
for rule in cssRules:
|
||||
rule._parentRule = self
|
||||
rule._parentStyleSheet = None
|
||||
|
||||
self._cssRules = cssRules
|
||||
|
||||
cssRules = property(lambda self: self._cssRules, _setCssRules,
|
||||
"All Rules in this style sheet, a "
|
||||
":class:`~cssutils.css.CSSRuleList`.")
|
||||
|
||||
def deleteRule(self, index):
|
||||
"""
|
||||
Delete the rule at `index` from rules ``cssRules``.
|
||||
|
||||
:param index:
|
||||
The `index` of the rule to be removed from the rules cssRules
|
||||
list. For an `index` < 0 **no** :exc:`~xml.dom.IndexSizeErr` is
|
||||
raised but rules for normal Python lists are used. E.g.
|
||||
``deleteRule(-1)`` removes the last rule in cssRules.
|
||||
|
||||
`index` may also be a CSSRule object which will then be removed.
|
||||
|
||||
:Exceptions:
|
||||
- :exc:`~xml.dom.IndexSizeErr`:
|
||||
Raised if the specified index does not correspond to a rule in
|
||||
the media rule list.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this media rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
if isinstance(index, CSSRule):
|
||||
for i, r in enumerate(self.cssRules):
|
||||
if index == r:
|
||||
index = i
|
||||
break
|
||||
else:
|
||||
raise xml.dom.IndexSizeErr(u"%s: Not a rule in "
|
||||
u"this rule'a cssRules list: %s"
|
||||
% (self.__class__.__name__, index))
|
||||
|
||||
try:
|
||||
# detach
|
||||
self._cssRules[index]._parentRule = None
|
||||
del self._cssRules[index]
|
||||
|
||||
except IndexError:
|
||||
raise xml.dom.IndexSizeErr(u'%s: %s is not a valid index '
|
||||
u'in the rulelist of length %i'
|
||||
% (self.__class__.__name__,
|
||||
index, self._cssRules.length))
|
||||
|
||||
def _prepareInsertRule(self, rule, index=None):
|
||||
"return checked `index` and optional parsed `rule`"
|
||||
self._checkReadonly()
|
||||
|
||||
# check index
|
||||
if index is None:
|
||||
index = len(self._cssRules)
|
||||
|
||||
elif index < 0 or index > self._cssRules.length:
|
||||
raise xml.dom.IndexSizeErr(u'%s: Invalid index %s for '
|
||||
u'CSSRuleList with a length of %s.'
|
||||
% (self.__class__.__name__,
|
||||
index, self._cssRules.length))
|
||||
|
||||
# check and optionally parse rule
|
||||
if isinstance(rule, basestring):
|
||||
tempsheet = cssutils.css.CSSStyleSheet()
|
||||
tempsheet.cssText = rule
|
||||
if len(tempsheet.cssRules) != 1 or (tempsheet.cssRules and
|
||||
not isinstance(tempsheet.cssRules[0], cssutils.css.CSSRule)):
|
||||
self._log.error(u'%s: Invalid Rule: %s' % (self.__class__.__name__,
|
||||
rule))
|
||||
return False, False
|
||||
rule = tempsheet.cssRules[0]
|
||||
|
||||
elif isinstance(rule, cssutils.css.CSSRuleList):
|
||||
# insert all rules
|
||||
for i, r in enumerate(rule):
|
||||
self.insertRule(r, index + i)
|
||||
return True, True
|
||||
|
||||
elif not isinstance(rule, cssutils.css.CSSRule):
|
||||
self._log.error(u'%s: Not a CSSRule: %s' % (rule,
|
||||
self.__class__.__name__))
|
||||
return False, False
|
||||
|
||||
return rule, index
|
||||
|
||||
def _finishInsertRule(self, rule, index):
|
||||
"add `rule` at `index`"
|
||||
rule._parentRule = self
|
||||
rule._parentStyleSheet = None
|
||||
self._cssRules.insert(index, rule)
|
||||
return index
|
||||
|
||||
def add(self, rule):
|
||||
"""Add `rule` to page rule. Same as ``insertRule(rule)``."""
|
||||
return self.insertRule(rule)
|
||||
|
||||
def insertRule(self, rule, index=None):
|
||||
"""
|
||||
Insert `rule` into the rules ``cssRules``.
|
||||
|
||||
:param rule:
|
||||
the parsable text representing the `rule` to be inserted. For rule
|
||||
sets this contains both the selector and the style declaration.
|
||||
For at-rules, this specifies both the at-identifier and the rule
|
||||
content.
|
||||
|
||||
cssutils also allows rule to be a valid
|
||||
:class:`~cssutils.css.CSSRule` object.
|
||||
|
||||
:param index:
|
||||
before the `index` the specified `rule` will be inserted.
|
||||
If the specified `index` is equal to the length of the rules
|
||||
rule collection, the rule will be added to the end of the rule.
|
||||
If index is not given or None rule will be appended to rule
|
||||
list.
|
||||
|
||||
:returns:
|
||||
the index of the newly inserted rule.
|
||||
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the `rule` cannot be inserted at the specified `index`,
|
||||
e.g., if an @import rule is inserted after a standard rule set
|
||||
or other at-rule.
|
||||
- :exc:`~xml.dom.IndexSizeErr`:
|
||||
Raised if the specified `index` is not a valid insertion point.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this rule is readonly.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified `rule` has a syntax error and is
|
||||
unparsable.
|
||||
"""
|
||||
return self._prepareInsertRule(rule, index)
|
||||
53
libs/cssutils/css/cssrulelist.py
Executable file
@@ -0,0 +1,53 @@
|
||||
"""CSSRuleList implements DOM Level 2 CSS CSSRuleList.
|
||||
Partly also http://dev.w3.org/csswg/cssom/#the-cssrulelist."""
|
||||
__all__ = ['CSSRuleList']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
class CSSRuleList(list):
|
||||
"""The CSSRuleList object represents an (ordered) list of statements.
|
||||
|
||||
The items in the CSSRuleList are accessible via an integral index,
|
||||
starting from 0.
|
||||
|
||||
Subclasses a standard Python list so theoretically all standard list
|
||||
methods are available. Setting methods like ``__init__``, ``append``,
|
||||
``extend`` or ``__setslice__`` are added later on instances of this
|
||||
class if so desired.
|
||||
E.g. CSSStyleSheet adds ``append`` which is not available in a simple
|
||||
instance of this class!
|
||||
"""
|
||||
def __init__(self, *ignored):
|
||||
"Nothing is set as this must also be defined later."
|
||||
pass
|
||||
|
||||
def __notimplemented(self, *ignored):
|
||||
"Implemented in class using a CSSRuleList only."
|
||||
raise NotImplementedError(
|
||||
'Must be implemented by class using an instance of this class.')
|
||||
|
||||
append = extend = __setitem__ = __setslice__ = __notimplemented
|
||||
|
||||
def item(self, index):
|
||||
"""(DOM) Retrieve a CSS rule by ordinal `index`. The order in this
|
||||
collection represents the order of the rules in the CSS style
|
||||
sheet. If index is greater than or equal to the number of rules in
|
||||
the list, this returns None.
|
||||
|
||||
Returns CSSRule, the style rule at the index position in the
|
||||
CSSRuleList, or None if that is not a valid index.
|
||||
"""
|
||||
try:
|
||||
return self[index]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
length = property(lambda self: len(self),
|
||||
doc=u"(DOM) The number of CSSRules in the list.")
|
||||
|
||||
def rulesOfType(self, type):
|
||||
"""Yield the rules which have the given `type` only, one of the
|
||||
constants defined in :class:`cssutils.css.CSSRule`."""
|
||||
for r in self:
|
||||
if r.type == type:
|
||||
yield r
|
||||
697
libs/cssutils/css/cssstyledeclaration.py
Executable file
@@ -0,0 +1,697 @@
|
||||
"""CSSStyleDeclaration implements DOM Level 2 CSS CSSStyleDeclaration and
|
||||
extends CSS2Properties
|
||||
|
||||
see
|
||||
http://www.w3.org/TR/1998/REC-CSS2-19980512/syndata.html#parsing-errors
|
||||
|
||||
Unknown properties
|
||||
------------------
|
||||
User agents must ignore a declaration with an unknown property.
|
||||
For example, if the style sheet is::
|
||||
|
||||
H1 { color: red; rotation: 70minutes }
|
||||
|
||||
the user agent will treat this as if the style sheet had been::
|
||||
|
||||
H1 { color: red }
|
||||
|
||||
Cssutils gives a message about any unknown properties but
|
||||
keeps any property (if syntactically correct).
|
||||
|
||||
Illegal values
|
||||
--------------
|
||||
User agents must ignore a declaration with an illegal value. For example::
|
||||
|
||||
IMG { float: left } /* correct CSS2 */
|
||||
IMG { float: left here } /* "here" is not a value of 'float' */
|
||||
IMG { background: "red" } /* keywords cannot be quoted in CSS2 */
|
||||
IMG { border-width: 3 } /* a unit must be specified for length values */
|
||||
|
||||
A CSS2 parser would honor the first rule and ignore the rest, as if the
|
||||
style sheet had been::
|
||||
|
||||
IMG { float: left }
|
||||
IMG { }
|
||||
IMG { }
|
||||
IMG { }
|
||||
|
||||
Cssutils again will issue a message (WARNING in this case) about invalid
|
||||
CSS2 property values.
|
||||
|
||||
TODO:
|
||||
This interface is also used to provide a read-only access to the
|
||||
computed values of an element. See also the ViewCSS interface.
|
||||
|
||||
- return computed values and not literal values
|
||||
- simplify unit pairs/triples/quadruples
|
||||
2px 2px 2px 2px -> 2px for border/padding...
|
||||
- normalize compound properties like:
|
||||
background: no-repeat left url() #fff
|
||||
-> background: #fff url() no-repeat left
|
||||
"""
|
||||
__all__ = ['CSSStyleDeclaration', 'Property']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
from cssproperties import CSS2Properties
|
||||
from property import Property
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSStyleDeclaration(CSS2Properties, cssutils.util.Base2):
|
||||
"""The CSSStyleDeclaration class represents a single CSS declaration
|
||||
block. This class may be used to determine the style properties
|
||||
currently set in a block or to set style properties explicitly
|
||||
within the block.
|
||||
|
||||
While an implementation may not recognize all CSS properties within
|
||||
a CSS declaration block, it is expected to provide access to all
|
||||
specified properties in the style sheet through the
|
||||
CSSStyleDeclaration interface.
|
||||
Furthermore, implementations that support a specific level of CSS
|
||||
should correctly handle CSS shorthand properties for that level. For
|
||||
a further discussion of shorthand properties, see the CSS2Properties
|
||||
interface.
|
||||
|
||||
Additionally the CSS2Properties interface is implemented.
|
||||
|
||||
$css2propertyname
|
||||
All properties defined in the CSS2Properties class are available
|
||||
as direct properties of CSSStyleDeclaration with their respective
|
||||
DOM name, so e.g. ``fontStyle`` for property 'font-style'.
|
||||
|
||||
These may be used as::
|
||||
|
||||
>>> style = CSSStyleDeclaration(cssText='color: red')
|
||||
>>> style.color = 'green'
|
||||
>>> print style.color
|
||||
green
|
||||
>>> del style.color
|
||||
>>> print style.color
|
||||
<BLANKLINE>
|
||||
|
||||
Format::
|
||||
|
||||
[Property: Value Priority?;]* [Property: Value Priority?]?
|
||||
"""
|
||||
def __init__(self, cssText=u'', parentRule=None, readonly=False,
|
||||
validating=None):
|
||||
"""
|
||||
:param cssText:
|
||||
Shortcut, sets CSSStyleDeclaration.cssText
|
||||
:param parentRule:
|
||||
The CSS rule that contains this declaration block or
|
||||
None if this CSSStyleDeclaration is not attached to a CSSRule.
|
||||
:param readonly:
|
||||
defaults to False
|
||||
:param validating:
|
||||
a flag defining if this sheet should be validated on change.
|
||||
Defaults to None, which means defer to the parent stylesheet.
|
||||
"""
|
||||
super(CSSStyleDeclaration, self).__init__()
|
||||
self._parentRule = parentRule
|
||||
self.validating = validating
|
||||
self.cssText = cssText
|
||||
self._readonly = readonly
|
||||
|
||||
def __contains__(self, nameOrProperty):
|
||||
"""Check if a property (or a property with given name) is in style.
|
||||
|
||||
:param name:
|
||||
a string or Property, uses normalized name and not literalname
|
||||
"""
|
||||
if isinstance(nameOrProperty, Property):
|
||||
name = nameOrProperty.name
|
||||
else:
|
||||
name = self._normalize(nameOrProperty)
|
||||
return name in self.__nnames()
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterator of set Property objects with different normalized names."""
|
||||
def properties():
|
||||
for name in self.__nnames():
|
||||
yield self.getProperty(name)
|
||||
return properties()
|
||||
|
||||
def keys(self):
|
||||
"""Analoguous to standard dict returns property names which are set in
|
||||
this declaration."""
|
||||
return list(self.__nnames())
|
||||
|
||||
def __getitem__(self, CSSName):
|
||||
"""Retrieve the value of property ``CSSName`` from this declaration.
|
||||
|
||||
``CSSName`` will be always normalized.
|
||||
"""
|
||||
return self.getPropertyValue(CSSName)
|
||||
|
||||
def __setitem__(self, CSSName, value):
|
||||
"""Set value of property ``CSSName``. ``value`` may also be a tuple of
|
||||
(value, priority), e.g. style['color'] = ('red', 'important')
|
||||
|
||||
``CSSName`` will be always normalized.
|
||||
"""
|
||||
priority = None
|
||||
if isinstance(value, tuple):
|
||||
value, priority = value
|
||||
|
||||
return self.setProperty(CSSName, value, priority)
|
||||
|
||||
def __delitem__(self, CSSName):
|
||||
"""Delete property ``CSSName`` from this declaration.
|
||||
If property is not in this declaration return u'' just like
|
||||
removeProperty.
|
||||
|
||||
``CSSName`` will be always normalized.
|
||||
"""
|
||||
return self.removeProperty(CSSName)
|
||||
|
||||
def __setattr__(self, n, v):
|
||||
"""Prevent setting of unknown properties on CSSStyleDeclaration
|
||||
which would not work anyway. For these
|
||||
``CSSStyleDeclaration.setProperty`` MUST be called explicitly!
|
||||
|
||||
TODO:
|
||||
implementation of known is not really nice, any alternative?
|
||||
"""
|
||||
known = ['_tokenizer', '_log', '_ttypes',
|
||||
'_seq', 'seq', 'parentRule', '_parentRule', 'cssText',
|
||||
'valid', 'wellformed', 'validating',
|
||||
'_readonly', '_profiles', '_validating']
|
||||
known.extend(CSS2Properties._properties)
|
||||
if n in known:
|
||||
super(CSSStyleDeclaration, self).__setattr__(n, v)
|
||||
else:
|
||||
raise AttributeError(u'Unknown CSS Property, '
|
||||
u'``CSSStyleDeclaration.setProperty("%s", '
|
||||
u'...)`` MUST be used.' % n)
|
||||
|
||||
def __repr__(self):
|
||||
return u"cssutils.css.%s(cssText=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.getCssText(separator=u' '))
|
||||
|
||||
def __str__(self):
|
||||
return u"<cssutils.css.%s object length=%r (all: %r) at 0x%x>" % (
|
||||
self.__class__.__name__,
|
||||
self.length,
|
||||
len(self.getProperties(all=True)),
|
||||
id(self))
|
||||
|
||||
def __nnames(self):
|
||||
"""Return iterator for all different names in order as set
|
||||
if names are set twice the last one is used (double reverse!)
|
||||
"""
|
||||
names = []
|
||||
for item in reversed(self.seq):
|
||||
val = item.value
|
||||
if isinstance(val, Property) and not val.name in names:
|
||||
names.append(val.name)
|
||||
return reversed(names)
|
||||
|
||||
# overwritten accessor functions for CSS2Properties' properties
|
||||
def _getP(self, CSSName):
|
||||
"""(DOM CSS2Properties) Overwritten here and effectively the same as
|
||||
``self.getPropertyValue(CSSname)``.
|
||||
|
||||
Parameter is in CSSname format ('font-style'), see CSS2Properties.
|
||||
|
||||
Example::
|
||||
|
||||
>>> style = CSSStyleDeclaration(cssText='font-style:italic;')
|
||||
>>> print style.fontStyle
|
||||
italic
|
||||
"""
|
||||
return self.getPropertyValue(CSSName)
|
||||
|
||||
def _setP(self, CSSName, value):
|
||||
"""(DOM CSS2Properties) Overwritten here and effectively the same as
|
||||
``self.setProperty(CSSname, value)``.
|
||||
|
||||
Only known CSS2Properties may be set this way, otherwise an
|
||||
AttributeError is raised.
|
||||
For these unknown properties ``setPropertyValue(CSSname, value)``
|
||||
has to be called explicitly.
|
||||
Also setting the priority of properties needs to be done with a
|
||||
call like ``setPropertyValue(CSSname, value, priority)``.
|
||||
|
||||
Example::
|
||||
|
||||
>>> style = CSSStyleDeclaration()
|
||||
>>> style.fontStyle = 'italic'
|
||||
>>> # or
|
||||
>>> style.setProperty('font-style', 'italic', '!important')
|
||||
|
||||
"""
|
||||
self.setProperty(CSSName, value)
|
||||
# TODO: Shorthand ones
|
||||
|
||||
def _delP(self, CSSName):
|
||||
"""(cssutils only) Overwritten here and effectively the same as
|
||||
``self.removeProperty(CSSname)``.
|
||||
|
||||
Example::
|
||||
|
||||
>>> style = CSSStyleDeclaration(cssText='font-style:italic;')
|
||||
>>> del style.fontStyle
|
||||
>>> print style.fontStyle
|
||||
<BLANKLINE>
|
||||
|
||||
"""
|
||||
self.removeProperty(CSSName)
|
||||
|
||||
def children(self):
|
||||
"""Generator yielding any known child in this declaration including
|
||||
*all* properties, comments or CSSUnknownrules.
|
||||
"""
|
||||
for item in self._seq:
|
||||
yield item.value
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText."""
|
||||
return cssutils.ser.do_css_CSSStyleDeclaration(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""Setting this attribute will result in the parsing of the new value
|
||||
and resetting of all the properties in the declaration block
|
||||
including the removal or addition of properties.
|
||||
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this declaration is readonly or a property is readonly.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
|
||||
# for closures: must be a mutable
|
||||
new = {'wellformed': True}
|
||||
def ident(expected, seq, token, tokenizer=None):
|
||||
# a property
|
||||
|
||||
tokens = self._tokensupto2(tokenizer, starttoken=token,
|
||||
semicolon=True)
|
||||
if self._tokenvalue(tokens[-1]) == u';':
|
||||
tokens.pop()
|
||||
property = Property(parent=self)
|
||||
property.cssText = tokens
|
||||
if property.wellformed:
|
||||
seq.append(property, 'Property')
|
||||
else:
|
||||
self._log.error(u'CSSStyleDeclaration: Syntax Error in '
|
||||
u'Property: %s' % self._valuestr(tokens))
|
||||
# does not matter in this case
|
||||
return expected
|
||||
|
||||
def unexpected(expected, seq, token, tokenizer=None):
|
||||
# error, find next ; or } to omit upto next property
|
||||
ignored = self._tokenvalue(token) + self._valuestr(
|
||||
self._tokensupto2(tokenizer,
|
||||
propertyvalueendonly=True))
|
||||
self._log.error(u'CSSStyleDeclaration: Unexpected token, ignoring '
|
||||
'upto %r.' % ignored,token)
|
||||
# does not matter in this case
|
||||
return expected
|
||||
|
||||
def char(expected, seq, token, tokenizer=None):
|
||||
# a standalone ; or error...
|
||||
if self._tokenvalue(token) == u';':
|
||||
self._log.info(u'CSSStyleDeclaration: Stripped standalone semicolon'
|
||||
u': %s' % self._valuestr([token]), neverraise=True)
|
||||
return expected
|
||||
else:
|
||||
return unexpected(expected, seq, token, tokenizer)
|
||||
|
||||
# [Property: Value;]* Property: Value?
|
||||
newseq = self._tempSeq()
|
||||
wellformed, expected = self._parse(expected=None,
|
||||
seq=newseq, tokenizer=tokenizer,
|
||||
productions={'IDENT': ident, 'CHAR': char},
|
||||
default=unexpected)
|
||||
# wellformed set by parse
|
||||
|
||||
for item in newseq:
|
||||
item.value._parent = self
|
||||
|
||||
# do not check wellformed as invalid things are removed anyway
|
||||
self._setSeq(newseq)
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc=u"(DOM) A parsable textual representation of the "
|
||||
u"declaration block excluding the surrounding curly "
|
||||
u"braces.")
|
||||
|
||||
def getCssText(self, separator=None):
|
||||
"""
|
||||
:returns:
|
||||
serialized property cssText, each property separated by
|
||||
given `separator` which may e.g. be ``u''`` to be able to use
|
||||
cssText directly in an HTML style attribute. ``;`` is part of
|
||||
each property (except the last one) and **cannot** be set with
|
||||
separator!
|
||||
"""
|
||||
return cssutils.ser.do_css_CSSStyleDeclaration(self, separator)
|
||||
|
||||
def _setParentRule(self, parentRule):
|
||||
self._parentRule = parentRule
|
||||
# for x in self.children():
|
||||
# x.parent = self
|
||||
|
||||
parentRule = property(lambda self: self._parentRule, _setParentRule,
|
||||
doc="(DOM) The CSS rule that contains this declaration block or "
|
||||
"None if this CSSStyleDeclaration is not attached to a CSSRule.")
|
||||
|
||||
def getProperties(self, name=None, all=False):
|
||||
"""
|
||||
:param name:
|
||||
optional `name` of properties which are requested.
|
||||
Only properties with this **always normalized** `name` are returned.
|
||||
If `name` is ``None`` all properties are returned (at least one for
|
||||
each set name depending on parameter `all`).
|
||||
:param all:
|
||||
if ``False`` (DEFAULT) only the effective properties are returned.
|
||||
If name is given a list with only one property is returned.
|
||||
|
||||
if ``True`` all properties including properties set multiple times
|
||||
with different values or priorities for different UAs are returned.
|
||||
The order of the properties is fully kept as in the original
|
||||
stylesheet.
|
||||
:returns:
|
||||
a list of :class:`~cssutils.css.Property` objects set in
|
||||
this declaration.
|
||||
"""
|
||||
if name and not all:
|
||||
# single prop but list
|
||||
p = self.getProperty(name)
|
||||
if p:
|
||||
return [p]
|
||||
else:
|
||||
return []
|
||||
elif not all:
|
||||
# effective Properties in name order
|
||||
return [self.getProperty(name) for name in self.__nnames()]
|
||||
else:
|
||||
# all properties or all with this name
|
||||
nname = self._normalize(name)
|
||||
properties = []
|
||||
for item in self.seq:
|
||||
val = item.value
|
||||
if isinstance(val, Property) and (
|
||||
(bool(nname) == False) or (val.name == nname)):
|
||||
properties.append(val)
|
||||
return properties
|
||||
|
||||
def getProperty(self, name, normalize=True):
|
||||
"""
|
||||
:param name:
|
||||
of the CSS property, always lowercase (even if not normalized)
|
||||
:param normalize:
|
||||
if ``True`` (DEFAULT) name will be normalized (lowercase, no simple
|
||||
escapes) so "color", "COLOR" or "C\olor" will all be equivalent
|
||||
|
||||
If ``False`` may return **NOT** the effective value but the
|
||||
effective for the unnormalized name.
|
||||
:returns:
|
||||
the effective :class:`~cssutils.css.Property` object.
|
||||
"""
|
||||
nname = self._normalize(name)
|
||||
found = None
|
||||
for item in reversed(self.seq):
|
||||
val = item.value
|
||||
if isinstance(val, Property):
|
||||
if (normalize and nname == val.name) or name == val.literalname:
|
||||
if val.priority:
|
||||
return val
|
||||
elif not found:
|
||||
found = val
|
||||
return found
|
||||
|
||||
def getPropertyCSSValue(self, name, normalize=True):
|
||||
"""
|
||||
:param name:
|
||||
of the CSS property, always lowercase (even if not normalized)
|
||||
:param normalize:
|
||||
if ``True`` (DEFAULT) name will be normalized (lowercase, no simple
|
||||
escapes) so "color", "COLOR" or "C\olor" will all be equivalent
|
||||
|
||||
If ``False`` may return **NOT** the effective value but the
|
||||
effective for the unnormalized name.
|
||||
:returns:
|
||||
:class:`~cssutils.css.CSSValue`, the value of the effective
|
||||
property if it has been explicitly set for this declaration block.
|
||||
|
||||
(DOM)
|
||||
Used to retrieve the object representation of the value of a CSS
|
||||
property if it has been explicitly set within this declaration
|
||||
block. Returns None if the property has not been set.
|
||||
|
||||
(This method returns None if the property is a shorthand
|
||||
property. Shorthand property values can only be accessed and
|
||||
modified as strings, using the getPropertyValue and setProperty
|
||||
methods.)
|
||||
|
||||
**cssutils currently always returns a CSSValue if the property is
|
||||
set.**
|
||||
|
||||
for more on shorthand properties see
|
||||
http://www.dustindiaz.com/css-shorthand/
|
||||
"""
|
||||
nname = self._normalize(name)
|
||||
if nname in self._SHORTHANDPROPERTIES:
|
||||
self._log.info(u'CSSValue for shorthand property "%s" should be '
|
||||
u'None, this may be implemented later.' %
|
||||
nname, neverraise=True)
|
||||
|
||||
p = self.getProperty(name, normalize)
|
||||
if p:
|
||||
return p.cssValue
|
||||
else:
|
||||
return None
|
||||
|
||||
def getPropertyValue(self, name, normalize=True):
|
||||
"""
|
||||
:param name:
|
||||
of the CSS property, always lowercase (even if not normalized)
|
||||
:param normalize:
|
||||
if ``True`` (DEFAULT) name will be normalized (lowercase, no simple
|
||||
escapes) so "color", "COLOR" or "C\olor" will all be equivalent
|
||||
|
||||
If ``False`` may return **NOT** the effective value but the
|
||||
effective for the unnormalized name.
|
||||
:returns:
|
||||
the value of the effective property if it has been explicitly set
|
||||
for this declaration block. Returns the empty string if the
|
||||
property has not been set.
|
||||
"""
|
||||
p = self.getProperty(name, normalize)
|
||||
if p:
|
||||
return p.value
|
||||
else:
|
||||
return u''
|
||||
|
||||
def getPropertyPriority(self, name, normalize=True):
|
||||
"""
|
||||
:param name:
|
||||
of the CSS property, always lowercase (even if not normalized)
|
||||
:param normalize:
|
||||
if ``True`` (DEFAULT) name will be normalized (lowercase, no simple
|
||||
escapes) so "color", "COLOR" or "C\olor" will all be equivalent
|
||||
|
||||
If ``False`` may return **NOT** the effective value but the
|
||||
effective for the unnormalized name.
|
||||
:returns:
|
||||
the priority of the effective CSS property (e.g. the
|
||||
"important" qualifier) if the property has been explicitly set in
|
||||
this declaration block. The empty string if none exists.
|
||||
"""
|
||||
p = self.getProperty(name, normalize)
|
||||
if p:
|
||||
return p.priority
|
||||
else:
|
||||
return u''
|
||||
|
||||
def removeProperty(self, name, normalize=True):
|
||||
"""
|
||||
(DOM)
|
||||
Used to remove a CSS property if it has been explicitly set within
|
||||
this declaration block.
|
||||
|
||||
:param name:
|
||||
of the CSS property
|
||||
:param normalize:
|
||||
if ``True`` (DEFAULT) name will be normalized (lowercase, no simple
|
||||
escapes) so "color", "COLOR" or "C\olor" will all be equivalent.
|
||||
The effective Property value is returned and *all* Properties
|
||||
with ``Property.name == name`` are removed.
|
||||
|
||||
If ``False`` may return **NOT** the effective value but the
|
||||
effective for the unnormalized `name` only. Also only the
|
||||
Properties with the literal name `name` are removed.
|
||||
:returns:
|
||||
the value of the property if it has been explicitly set for
|
||||
this declaration block. Returns the empty string if the property
|
||||
has not been set or the property name does not correspond to a
|
||||
known CSS property
|
||||
|
||||
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this declaration is readonly or the property is
|
||||
readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
r = self.getPropertyValue(name, normalize=normalize)
|
||||
newseq = self._tempSeq()
|
||||
if normalize:
|
||||
# remove all properties with name == nname
|
||||
nname = self._normalize(name)
|
||||
for item in self.seq:
|
||||
if not (isinstance(item.value, Property)
|
||||
and item.value.name == nname):
|
||||
newseq.appendItem(item)
|
||||
else:
|
||||
# remove all properties with literalname == name
|
||||
for item in self.seq:
|
||||
if not (isinstance(item.value, Property)
|
||||
and item.value.literalname == name):
|
||||
newseq.appendItem(item)
|
||||
self._setSeq(newseq)
|
||||
return r
|
||||
|
||||
def setProperty(self, name, value=None, priority=u'',
|
||||
normalize=True, replace=True):
|
||||
"""(DOM) Set a property value and priority within this declaration
|
||||
block.
|
||||
|
||||
:param name:
|
||||
of the CSS property to set (in W3C DOM the parameter is called
|
||||
"propertyName"), always lowercase (even if not normalized)
|
||||
|
||||
If a property with this `name` is present it will be reset.
|
||||
|
||||
cssutils also allowed `name` to be a
|
||||
:class:`~cssutils.css.Property` object, all other
|
||||
parameter are ignored in this case
|
||||
|
||||
:param value:
|
||||
the new value of the property, ignored if `name` is a Property.
|
||||
:param priority:
|
||||
the optional priority of the property (e.g. "important"),
|
||||
ignored if `name` is a Property.
|
||||
:param normalize:
|
||||
if True (DEFAULT) `name` will be normalized (lowercase, no simple
|
||||
escapes) so "color", "COLOR" or "C\olor" will all be equivalent
|
||||
:param replace:
|
||||
if True (DEFAULT) the given property will replace a present
|
||||
property. If False a new property will be added always.
|
||||
The difference to `normalize` is that two or more properties with
|
||||
the same name may be set, useful for e.g. stuff like::
|
||||
|
||||
background: red;
|
||||
background: rgba(255, 0, 0, 0.5);
|
||||
|
||||
which defines the same property but only capable UAs use the last
|
||||
property value, older ones use the first value.
|
||||
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified value has a syntax error and is
|
||||
unparsable.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this declaration is readonly or the property is
|
||||
readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
if isinstance(name, Property):
|
||||
newp = name
|
||||
name = newp.literalname
|
||||
elif not value:
|
||||
# empty string or None effectively removed property
|
||||
return self.removeProperty(name)
|
||||
else:
|
||||
newp = Property(name, value, priority)
|
||||
|
||||
if newp.wellformed:
|
||||
if replace:
|
||||
# check if update
|
||||
nname = self._normalize(name)
|
||||
properties = self.getProperties(name, all=(not normalize))
|
||||
for property in reversed(properties):
|
||||
if normalize and property.name == nname:
|
||||
property.cssValue = newp.cssValue.cssText
|
||||
property.priority = newp.priority
|
||||
return
|
||||
elif property.literalname == name:
|
||||
property.cssValue = newp.cssValue.cssText
|
||||
property.priority = newp.priority
|
||||
return
|
||||
|
||||
# not yet set or forced omit replace
|
||||
newp.parent = self
|
||||
self.seq._readonly = False
|
||||
self.seq.append(newp, 'Property')
|
||||
self.seq._readonly = True
|
||||
|
||||
else:
|
||||
self._log.warn(u'Invalid Property: %s: %s %s'
|
||||
% (name, value, priority))
|
||||
|
||||
def item(self, index):
|
||||
"""(DOM) Retrieve the properties that have been explicitly set in
|
||||
this declaration block. The order of the properties retrieved using
|
||||
this method does not have to be the order in which they were set.
|
||||
This method can be used to iterate over all properties in this
|
||||
declaration block.
|
||||
|
||||
:param index:
|
||||
of the property to retrieve, negative values behave like
|
||||
negative indexes on Python lists, so -1 is the last element
|
||||
|
||||
:returns:
|
||||
the name of the property at this ordinal position. The
|
||||
empty string if no property exists at this position.
|
||||
|
||||
**ATTENTION:**
|
||||
Only properties with different names are counted. If two
|
||||
properties with the same name are present in this declaration
|
||||
only the effective one is included.
|
||||
|
||||
:meth:`item` and :attr:`length` work on the same set here.
|
||||
"""
|
||||
names = list(self.__nnames())
|
||||
try:
|
||||
return names[index]
|
||||
except IndexError:
|
||||
return u''
|
||||
|
||||
length = property(lambda self: len(list(self.__nnames())),
|
||||
doc=u"(DOM) The number of distinct properties that have "
|
||||
u"been explicitly in this declaration block. The "
|
||||
u"range of valid indices is 0 to length-1 inclusive. "
|
||||
u"These are properties with a different ``name`` "
|
||||
u"only. :meth:`item` and :attr:`length` work on the "
|
||||
u"same set here.")
|
||||
|
||||
def _getValidating(self):
|
||||
try:
|
||||
# CSSParser.parseX() sets validating of stylesheet
|
||||
return self.parentRule.parentStyleSheet.validating
|
||||
except AttributeError:
|
||||
# CSSParser.parseStyle() sets validating of declaration
|
||||
if self._validating is not None:
|
||||
return self._validating
|
||||
# default
|
||||
return True
|
||||
|
||||
def _setValidating(self, validating):
|
||||
self._validating = validating
|
||||
|
||||
validating = property(_getValidating, _setValidating,
|
||||
doc=u"If ``True`` this declaration validates "
|
||||
u"contained properties. The parent StyleSheet "
|
||||
u"validation setting does *always* win though so "
|
||||
u"even if validating is True it may not validate "
|
||||
u"if the StyleSheet defines else!")
|
||||
234
libs/cssutils/css/cssstylerule.py
Executable file
@@ -0,0 +1,234 @@
|
||||
"""CSSStyleRule implements DOM Level 2 CSS CSSStyleRule."""
|
||||
__all__ = ['CSSStyleRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
from cssstyledeclaration import CSSStyleDeclaration
|
||||
from selectorlist import SelectorList
|
||||
import cssrule
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSStyleRule(cssrule.CSSRule):
|
||||
"""The CSSStyleRule object represents a ruleset specified (if any) in a CSS
|
||||
style sheet. It provides access to a declaration block as well as to the
|
||||
associated group of selectors.
|
||||
|
||||
Format::
|
||||
|
||||
: selector [ COMMA S* selector ]*
|
||||
LBRACE S* declaration [ ';' S* declaration ]* '}' S*
|
||||
;
|
||||
"""
|
||||
def __init__(self, selectorText=None, style=None, parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
:Parameters:
|
||||
selectorText
|
||||
string parsed into selectorList
|
||||
style
|
||||
string parsed into CSSStyleDeclaration for this CSSStyleRule
|
||||
readonly
|
||||
if True allows setting of properties in constructor only
|
||||
"""
|
||||
super(CSSStyleRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
|
||||
self.selectorList = SelectorList()
|
||||
if selectorText:
|
||||
self.selectorText = selectorText
|
||||
|
||||
if style:
|
||||
self.style = style
|
||||
else:
|
||||
self.style = CSSStyleDeclaration()
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
if self._namespaces:
|
||||
st = (self.selectorText, self._namespaces)
|
||||
else:
|
||||
st = self.selectorText
|
||||
return u"cssutils.css.%s(selectorText=%r, style=%r)" % (
|
||||
self.__class__.__name__, st, self.style.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return u"<cssutils.css.%s object selectorText=%r style=%r _namespaces=%r "\
|
||||
u"at 0x%x>" % (self.__class__.__name__,
|
||||
self.selectorText,
|
||||
self.style.cssText,
|
||||
self._namespaces,
|
||||
id(self))
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText."""
|
||||
return cssutils.ser.do_CSSStyleRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:param cssText:
|
||||
a parseable string or a tuple of (cssText, dict-of-namespaces)
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.NamespaceErr`:
|
||||
Raised if the specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
super(CSSStyleRule, self)._setCssText(cssText)
|
||||
|
||||
# might be (cssText, namespaces)
|
||||
cssText, namespaces = self._splitNamespacesOff(cssText)
|
||||
try:
|
||||
# use parent style sheet ones if available
|
||||
namespaces = self.parentStyleSheet.namespaces
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
selectortokens = self._tokensupto2(tokenizer, blockstartonly=True)
|
||||
styletokens = self._tokensupto2(tokenizer, blockendonly=True)
|
||||
trail = self._nexttoken(tokenizer)
|
||||
if trail:
|
||||
self._log.error(u'CSSStyleRule: Trailing content: %s' %
|
||||
self._valuestr(cssText), token=trail)
|
||||
elif not selectortokens:
|
||||
self._log.error(u'CSSStyleRule: No selector found: %r' %
|
||||
self._valuestr(cssText))
|
||||
elif self._tokenvalue(selectortokens[0]).startswith(u'@'):
|
||||
self._log.error(u'CSSStyleRule: No style rule: %r' %
|
||||
self._valuestr(cssText),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
else:
|
||||
newSelectorList = SelectorList(parentRule=self)
|
||||
newStyle = CSSStyleDeclaration(parentRule=self)
|
||||
ok = True
|
||||
|
||||
bracetoken = selectortokens.pop()
|
||||
if self._tokenvalue(bracetoken) != u'{':
|
||||
ok = False
|
||||
self._log.error(
|
||||
u'CSSStyleRule: No start { of style declaration found: %r' %
|
||||
self._valuestr(cssText), bracetoken)
|
||||
elif not selectortokens:
|
||||
ok = False
|
||||
self._log.error(u'CSSStyleRule: No selector found: %r.' %
|
||||
self._valuestr(cssText), bracetoken)
|
||||
# SET
|
||||
newSelectorList.selectorText = (selectortokens,
|
||||
namespaces)
|
||||
|
||||
if not styletokens:
|
||||
ok = False
|
||||
self._log.error(
|
||||
u'CSSStyleRule: No style declaration or "}" found: %r' %
|
||||
self._valuestr(cssText))
|
||||
else:
|
||||
braceorEOFtoken = styletokens.pop()
|
||||
val, typ = self._tokenvalue(braceorEOFtoken),\
|
||||
self._type(braceorEOFtoken)
|
||||
if val != u'}' and typ != 'EOF':
|
||||
ok = False
|
||||
self._log.error(u'CSSStyleRule: No "}" after style '
|
||||
u'declaration found: %r'
|
||||
% self._valuestr(cssText))
|
||||
else:
|
||||
if 'EOF' == typ:
|
||||
# add again as style needs it
|
||||
styletokens.append(braceorEOFtoken)
|
||||
# SET, may raise:
|
||||
newStyle.cssText = styletokens
|
||||
|
||||
if ok:
|
||||
self.selectorList = newSelectorList
|
||||
self.style = newStyle
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc=u"(DOM) The parsable textual representation of this "
|
||||
u"rule.")
|
||||
|
||||
def __getNamespaces(self):
|
||||
"""Uses children namespaces if not attached to a sheet, else the sheet's
|
||||
ones."""
|
||||
try:
|
||||
return self.parentStyleSheet.namespaces
|
||||
except AttributeError:
|
||||
return self.selectorList._namespaces
|
||||
|
||||
_namespaces = property(__getNamespaces,
|
||||
doc=u"If this Rule is attached to a CSSStyleSheet "
|
||||
u"the namespaces of that sheet are mirrored "
|
||||
u"here. While the Rule is not attached the "
|
||||
u"namespaces of selectorList are used.""")
|
||||
|
||||
def _setSelectorList(self, selectorList):
|
||||
"""
|
||||
:param selectorList: A SelectorList which replaces the current
|
||||
selectorList object
|
||||
"""
|
||||
self._checkReadonly()
|
||||
selectorList._parentRule = self
|
||||
self._selectorList = selectorList
|
||||
|
||||
_selectorList = None
|
||||
selectorList = property(lambda self: self._selectorList, _setSelectorList,
|
||||
doc=u"The SelectorList of this rule.")
|
||||
|
||||
def _setSelectorText(self, selectorText):
|
||||
"""
|
||||
wrapper for cssutils SelectorList object
|
||||
|
||||
:param selectorText:
|
||||
of type string, might also be a comma separated list
|
||||
of selectors
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.NamespaceErr`:
|
||||
Raised if the specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
and is unparsable.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
sl = SelectorList(selectorText=selectorText, parentRule=self)
|
||||
if sl.wellformed:
|
||||
self._selectorList = sl
|
||||
|
||||
selectorText = property(lambda self: self._selectorList.selectorText,
|
||||
_setSelectorText,
|
||||
doc=u"(DOM) The textual representation of the "
|
||||
u"selector for the rule set.")
|
||||
|
||||
def _setStyle(self, style):
|
||||
"""
|
||||
:param style: A string or CSSStyleDeclaration which replaces the
|
||||
current style object.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
if isinstance(style, basestring):
|
||||
self._style = CSSStyleDeclaration(cssText=style, parentRule=self)
|
||||
else:
|
||||
style._parentRule = self
|
||||
self._style = style
|
||||
|
||||
style = property(lambda self: self._style, _setStyle,
|
||||
doc=u"(DOM) The declaration-block of this rule set.")
|
||||
|
||||
type = property(lambda self: self.STYLE_RULE,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
"type constant.")
|
||||
|
||||
wellformed = property(lambda self: self.selectorList.wellformed)
|
||||
804
libs/cssutils/css/cssstylesheet.py
Executable file
@@ -0,0 +1,804 @@
|
||||
"""CSSStyleSheet implements DOM Level 2 CSS CSSStyleSheet.
|
||||
|
||||
Partly also:
|
||||
- http://dev.w3.org/csswg/cssom/#the-cssstylesheet
|
||||
- http://www.w3.org/TR/2006/WD-css3-namespace-20060828/
|
||||
|
||||
TODO:
|
||||
- ownerRule and ownerNode
|
||||
"""
|
||||
__all__ = ['CSSStyleSheet']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
from cssutils.helper import Deprecated
|
||||
from cssutils.util import _Namespaces, _SimpleNamespaces, _readUrl
|
||||
from cssrule import CSSRule
|
||||
from cssvariablesdeclaration import CSSVariablesDeclaration
|
||||
import cssutils.stylesheets
|
||||
import xml.dom
|
||||
|
||||
class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
||||
"""CSSStyleSheet represents a CSS style sheet.
|
||||
|
||||
Format::
|
||||
|
||||
stylesheet
|
||||
: [ CHARSET_SYM S* STRING S* ';' ]?
|
||||
[S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
|
||||
[ namespace [S|CDO|CDC]* ]* # according to @namespace WD
|
||||
[ [ ruleset | media | page ] [S|CDO|CDC]* ]*
|
||||
|
||||
``cssRules``
|
||||
All Rules in this style sheet, a :class:`~cssutils.css.CSSRuleList`.
|
||||
"""
|
||||
def __init__(self, href=None, media=None, title=u'', disabled=None,
|
||||
ownerNode=None, parentStyleSheet=None, readonly=False,
|
||||
ownerRule=None,
|
||||
validating=True):
|
||||
"""
|
||||
For parameters see :class:`~cssutils.stylesheets.StyleSheet`
|
||||
"""
|
||||
super(CSSStyleSheet, self).__init__(
|
||||
'text/css', href, media, title, disabled,
|
||||
ownerNode, parentStyleSheet,
|
||||
validating=validating)
|
||||
|
||||
self._ownerRule = ownerRule
|
||||
self.cssRules = cssutils.css.CSSRuleList()
|
||||
self._namespaces = _Namespaces(parentStyleSheet=self, log=self._log)
|
||||
self._variables = CSSVariablesDeclaration()
|
||||
self._readonly = readonly
|
||||
|
||||
# used only during setting cssText by parse*()
|
||||
self.__encodingOverride = None
|
||||
self._fetcher = None
|
||||
|
||||
def __iter__(self):
|
||||
"Generator which iterates over cssRules."
|
||||
for rule in self._cssRules:
|
||||
yield rule
|
||||
|
||||
def __repr__(self):
|
||||
if self.media:
|
||||
mediaText = self.media.mediaText
|
||||
else:
|
||||
mediaText = None
|
||||
return "cssutils.css.%s(href=%r, media=%r, title=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.href, mediaText, self.title)
|
||||
|
||||
def __str__(self):
|
||||
if self.media:
|
||||
mediaText = self.media.mediaText
|
||||
else:
|
||||
mediaText = None
|
||||
return "<cssutils.css.%s object encoding=%r href=%r "\
|
||||
"media=%r title=%r namespaces=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.encoding, self.href,
|
||||
mediaText, self.title, self.namespaces.namespaces,
|
||||
id(self))
|
||||
|
||||
def _cleanNamespaces(self):
|
||||
"Remove all namespace rules with same namespaceURI but last."
|
||||
rules = self.cssRules
|
||||
namespaceitems = self.namespaces.items()
|
||||
i = 0
|
||||
while i < len(rules):
|
||||
rule = rules[i]
|
||||
if rule.type == rule.NAMESPACE_RULE and \
|
||||
(rule.prefix, rule.namespaceURI) not in namespaceitems:
|
||||
self.deleteRule(i)
|
||||
else:
|
||||
i += 1
|
||||
|
||||
def _getUsedURIs(self):
|
||||
"Return set of URIs used in the sheet."
|
||||
useduris = set()
|
||||
for r1 in self:
|
||||
if r1.STYLE_RULE == r1.type:
|
||||
useduris.update(r1.selectorList._getUsedUris())
|
||||
elif r1.MEDIA_RULE == r1.type:
|
||||
for r2 in r1:
|
||||
if r2.type == r2.STYLE_RULE:
|
||||
useduris.update(r2.selectorList._getUsedUris())
|
||||
return useduris
|
||||
|
||||
def _setCssRules(self, cssRules):
|
||||
"Set new cssRules and update contained rules refs."
|
||||
cssRules.append = self.insertRule
|
||||
cssRules.extend = self.insertRule
|
||||
cssRules.__delitem__ = self.deleteRule
|
||||
|
||||
for rule in cssRules:
|
||||
rule._parentStyleSheet = self
|
||||
|
||||
self._cssRules = cssRules
|
||||
|
||||
cssRules = property(lambda self: self._cssRules, _setCssRules,
|
||||
u"All Rules in this style sheet, a "
|
||||
u":class:`~cssutils.css.CSSRuleList`.")
|
||||
|
||||
def _getCssText(self):
|
||||
"Textual representation of the stylesheet (a byte string)."
|
||||
return cssutils.ser.do_CSSStyleSheet(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""Parse `cssText` and overwrites the whole stylesheet.
|
||||
|
||||
:param cssText:
|
||||
a parseable string or a tuple of (cssText, dict-of-namespaces)
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.NamespaceErr`:
|
||||
If a namespace prefix is found which is not declared.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
cssText, namespaces = self._splitNamespacesOff(cssText)
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
|
||||
def S(expected, seq, token, tokenizer=None):
|
||||
# @charset must be at absolute beginning of style sheet
|
||||
# or 0 for py3
|
||||
return max(1, expected or 0)
|
||||
|
||||
def COMMENT(expected, seq, token, tokenizer=None):
|
||||
"special: sets parent*"
|
||||
self.insertRule(cssutils.css.CSSComment([token],
|
||||
parentStyleSheet=self))
|
||||
# or 0 for py3
|
||||
return max(1, expected or 0)
|
||||
|
||||
def charsetrule(expected, seq, token, tokenizer):
|
||||
# parse and consume tokens in any case
|
||||
rule = cssutils.css.CSSCharsetRule(parentStyleSheet=self)
|
||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||
|
||||
if expected > 0:
|
||||
self._log.error(u'CSSStylesheet: CSSCharsetRule only allowed '
|
||||
u'at beginning of stylesheet.',
|
||||
token, xml.dom.HierarchyRequestErr)
|
||||
return expected
|
||||
elif rule.wellformed:
|
||||
self.insertRule(rule)
|
||||
|
||||
return 1
|
||||
|
||||
def importrule(expected, seq, token, tokenizer):
|
||||
# parse and consume tokens in any case
|
||||
rule = cssutils.css.CSSImportRule(parentStyleSheet=self)
|
||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||
|
||||
if expected > 1:
|
||||
self._log.error(u'CSSStylesheet: CSSImportRule not allowed '
|
||||
u'here.', token, xml.dom.HierarchyRequestErr)
|
||||
return expected
|
||||
elif rule.wellformed:
|
||||
self.insertRule(rule)
|
||||
|
||||
return 1
|
||||
|
||||
def namespacerule(expected, seq, token, tokenizer):
|
||||
# parse and consume tokens in any case
|
||||
rule = cssutils.css.CSSNamespaceRule(cssText=self._tokensupto2(tokenizer,
|
||||
token),
|
||||
parentStyleSheet=self)
|
||||
|
||||
if expected > 2:
|
||||
self._log.error(u'CSSStylesheet: CSSNamespaceRule not allowed '
|
||||
u'here.', token, xml.dom.HierarchyRequestErr)
|
||||
return expected
|
||||
elif rule.wellformed:
|
||||
if rule.prefix not in self.namespaces:
|
||||
# add new if not same prefix
|
||||
self.insertRule(rule, _clean=False)
|
||||
else:
|
||||
# same prefix => replace namespaceURI
|
||||
for r in self.cssRules.rulesOfType(rule.NAMESPACE_RULE):
|
||||
if r.prefix == rule.prefix:
|
||||
r._replaceNamespaceURI(rule.namespaceURI)
|
||||
|
||||
self._namespaces[rule.prefix] = rule.namespaceURI
|
||||
|
||||
return 2
|
||||
|
||||
def variablesrule(expected, seq, token, tokenizer):
|
||||
# parse and consume tokens in any case
|
||||
rule = cssutils.css.CSSVariablesRule(parentStyleSheet=self)
|
||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||
|
||||
if expected > 2:
|
||||
self._log.error(u'CSSStylesheet: CSSVariablesRule not allowed '
|
||||
u'here.', token, xml.dom.HierarchyRequestErr)
|
||||
return expected
|
||||
elif rule.wellformed:
|
||||
self.insertRule(rule)
|
||||
self._updateVariables()
|
||||
|
||||
return 2
|
||||
|
||||
def fontfacerule(expected, seq, token, tokenizer):
|
||||
# parse and consume tokens in any case
|
||||
rule = cssutils.css.CSSFontFaceRule(parentStyleSheet=self)
|
||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||
if rule.wellformed:
|
||||
self.insertRule(rule)
|
||||
return 3
|
||||
|
||||
def mediarule(expected, seq, token, tokenizer):
|
||||
# parse and consume tokens in any case
|
||||
rule = cssutils.css.CSSMediaRule(parentStyleSheet=self)
|
||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||
if rule.wellformed:
|
||||
self.insertRule(rule)
|
||||
return 3
|
||||
|
||||
def pagerule(expected, seq, token, tokenizer):
|
||||
# parse and consume tokens in any case
|
||||
rule = cssutils.css.CSSPageRule(parentStyleSheet=self)
|
||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||
if rule.wellformed:
|
||||
self.insertRule(rule)
|
||||
return 3
|
||||
|
||||
def unknownrule(expected, seq, token, tokenizer):
|
||||
# parse and consume tokens in any case
|
||||
if token[1] in cssutils.css.MarginRule.margins:
|
||||
self._log.error(u'CSSStylesheet: MarginRule out CSSPageRule.',
|
||||
token, neverraise=True)
|
||||
rule = cssutils.css.MarginRule(parentStyleSheet=self)
|
||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||
else:
|
||||
self._log.warn(u'CSSStylesheet: Unknown @rule found.',
|
||||
token, neverraise=True)
|
||||
rule = cssutils.css.CSSUnknownRule(parentStyleSheet=self)
|
||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||
|
||||
if rule.wellformed:
|
||||
self.insertRule(rule)
|
||||
|
||||
# or 0 for py3
|
||||
return max(1, expected or 0)
|
||||
|
||||
def ruleset(expected, seq, token, tokenizer):
|
||||
# parse and consume tokens in any case
|
||||
rule = cssutils.css.CSSStyleRule(parentStyleSheet=self)
|
||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||
if rule.wellformed:
|
||||
self.insertRule(rule)
|
||||
return 3
|
||||
|
||||
# save for possible reset
|
||||
oldCssRules = self.cssRules
|
||||
oldNamespaces = self._namespaces
|
||||
|
||||
self.cssRules = cssutils.css.CSSRuleList()
|
||||
# simple during parse
|
||||
self._namespaces = namespaces
|
||||
self._variables = CSSVariablesDeclaration()
|
||||
|
||||
# not used?!
|
||||
newseq = []
|
||||
|
||||
# ['CHARSET', 'IMPORT', ('VAR', NAMESPACE'), ('PAGE', 'MEDIA', ruleset)]
|
||||
wellformed, expected = self._parse(0, newseq, tokenizer,
|
||||
{'S': S,
|
||||
'COMMENT': COMMENT,
|
||||
'CDO': lambda *ignored: None,
|
||||
'CDC': lambda *ignored: None,
|
||||
'CHARSET_SYM': charsetrule,
|
||||
'FONT_FACE_SYM': fontfacerule,
|
||||
'IMPORT_SYM': importrule,
|
||||
'NAMESPACE_SYM': namespacerule,
|
||||
'PAGE_SYM': pagerule,
|
||||
'MEDIA_SYM': mediarule,
|
||||
'VARIABLES_SYM': variablesrule,
|
||||
'ATKEYWORD': unknownrule
|
||||
},
|
||||
default=ruleset)
|
||||
|
||||
if wellformed:
|
||||
# use proper namespace object
|
||||
self._namespaces = _Namespaces(parentStyleSheet=self, log=self._log)
|
||||
self._cleanNamespaces()
|
||||
|
||||
else:
|
||||
# reset
|
||||
self._cssRules = oldCssRules
|
||||
self._namespaces = oldNamespaces
|
||||
self._updateVariables()
|
||||
self._cleanNamespaces()
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
"Textual representation of the stylesheet (a byte string)")
|
||||
|
||||
def _resolveImport(self, url):
|
||||
"""Read (encoding, enctype, decodedContent) from `url` for @import
|
||||
sheets."""
|
||||
try:
|
||||
# only available during parsing of a complete sheet
|
||||
parentEncoding = self.__newEncoding
|
||||
|
||||
except AttributeError:
|
||||
try:
|
||||
# explicit @charset
|
||||
parentEncoding = self._cssRules[0].encoding
|
||||
except (IndexError, AttributeError):
|
||||
# default not UTF-8 but None!
|
||||
parentEncoding = None
|
||||
|
||||
|
||||
return _readUrl(url, fetcher=self._fetcher,
|
||||
overrideEncoding=self.__encodingOverride,
|
||||
parentEncoding=parentEncoding)
|
||||
|
||||
def _setCssTextWithEncodingOverride(self, cssText, encodingOverride=None,
|
||||
encoding=None):
|
||||
"""Set `cssText` but use `encodingOverride` to overwrite detected
|
||||
encoding. This is used by parse and @import during setting of cssText.
|
||||
|
||||
If `encoding` is given use this but do not save as `encodingOverride`.
|
||||
"""
|
||||
if encodingOverride:
|
||||
# encoding during resolving of @import
|
||||
self.__encodingOverride = encodingOverride
|
||||
|
||||
if encoding:
|
||||
# save for nested @import
|
||||
self.__newEncoding = encoding
|
||||
|
||||
self.cssText = cssText
|
||||
|
||||
if encodingOverride:
|
||||
# set encodingOverride explicit again!
|
||||
self.encoding = self.__encodingOverride
|
||||
# del?
|
||||
self.__encodingOverride = None
|
||||
elif encoding:
|
||||
# may e.g. be httpEncoding
|
||||
self.encoding = encoding
|
||||
try:
|
||||
del self.__newEncoding
|
||||
except AttributeError, e:
|
||||
pass
|
||||
|
||||
def _setFetcher(self, fetcher=None):
|
||||
"""Set @import URL loader, if None the default is used."""
|
||||
self._fetcher = fetcher
|
||||
|
||||
def _getEncoding(self):
|
||||
"""Encoding set in :class:`~cssutils.css.CSSCharsetRule` or if ``None``
|
||||
resulting in default ``utf-8`` encoding being used."""
|
||||
try:
|
||||
return self._cssRules[0].encoding
|
||||
except (IndexError, AttributeError):
|
||||
return 'utf-8'
|
||||
|
||||
def _setEncoding(self, encoding):
|
||||
"""Set `encoding` of charset rule if present in sheet or insert a new
|
||||
:class:`~cssutils.css.CSSCharsetRule` with given `encoding`.
|
||||
If `encoding` is None removes charsetrule if present resulting in
|
||||
default encoding of utf-8.
|
||||
"""
|
||||
try:
|
||||
rule = self._cssRules[0]
|
||||
except IndexError:
|
||||
rule = None
|
||||
if rule and rule.CHARSET_RULE == rule.type:
|
||||
if encoding:
|
||||
rule.encoding = encoding
|
||||
else:
|
||||
self.deleteRule(0)
|
||||
elif encoding:
|
||||
self.insertRule(cssutils.css.CSSCharsetRule(encoding=encoding), 0)
|
||||
|
||||
encoding = property(_getEncoding, _setEncoding,
|
||||
"(cssutils) Reflect encoding of an @charset rule or 'utf-8' "
|
||||
"(default) if set to ``None``")
|
||||
|
||||
namespaces = property(lambda self: self._namespaces,
|
||||
doc="All Namespaces used in this CSSStyleSheet.")
|
||||
|
||||
def _updateVariables(self):
|
||||
"""Updates self._variables, called when @import or @variables rules
|
||||
is added to sheet.
|
||||
"""
|
||||
for r in self.cssRules.rulesOfType(CSSRule.IMPORT_RULE):
|
||||
s = r.styleSheet
|
||||
if s:
|
||||
for var in s.variables:
|
||||
self._variables.setVariable(var, s.variables[var])
|
||||
# for r in self.cssRules.rulesOfType(CSSRule.IMPORT_RULE):
|
||||
# for vr in r.styleSheet.cssRules.rulesOfType(CSSRule.VARIABLES_RULE):
|
||||
# for var in vr.variables:
|
||||
# self._variables.setVariable(var, vr.variables[var])
|
||||
for vr in self.cssRules.rulesOfType(CSSRule.VARIABLES_RULE):
|
||||
for var in vr.variables:
|
||||
self._variables.setVariable(var, vr.variables[var])
|
||||
|
||||
variables = property(lambda self: self._variables,
|
||||
doc=u"A :class:`cssutils.css.CSSVariablesDeclaration` "
|
||||
u"containing all available variables in this "
|
||||
u"CSSStyleSheet including the ones defined in "
|
||||
u"imported sheets.")
|
||||
|
||||
def add(self, rule):
|
||||
"""Add `rule` to style sheet at appropriate position.
|
||||
Same as ``insertRule(rule, inOrder=True)``.
|
||||
"""
|
||||
return self.insertRule(rule, index=None, inOrder=True)
|
||||
|
||||
def deleteRule(self, index):
|
||||
"""Delete rule at `index` from the style sheet.
|
||||
|
||||
:param index:
|
||||
The `index` of the rule to be removed from the StyleSheet's rule
|
||||
list. For an `index` < 0 **no** :exc:`~xml.dom.IndexSizeErr` is
|
||||
raised but rules for normal Python lists are used. E.g.
|
||||
``deleteRule(-1)`` removes the last rule in cssRules.
|
||||
|
||||
`index` may also be a CSSRule object which will then be removed
|
||||
from the StyleSheet.
|
||||
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.IndexSizeErr`:
|
||||
Raised if the specified index does not correspond to a rule in
|
||||
the style sheet's rule list.
|
||||
- :exc:`~xml.dom.NamespaceErr`:
|
||||
Raised if removing this rule would result in an invalid StyleSheet
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this style sheet is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
if isinstance(index, CSSRule):
|
||||
for i, r in enumerate(self.cssRules):
|
||||
if index == r:
|
||||
index = i
|
||||
break
|
||||
else:
|
||||
raise xml.dom.IndexSizeErr(u"CSSStyleSheet: Not a rule in"
|
||||
" this sheets'a cssRules list: %s"
|
||||
% index)
|
||||
|
||||
try:
|
||||
rule = self._cssRules[index]
|
||||
except IndexError:
|
||||
raise xml.dom.IndexSizeErr(
|
||||
u'CSSStyleSheet: %s is not a valid index in the rulelist of '
|
||||
u'length %i' % (index, self._cssRules.length))
|
||||
else:
|
||||
if rule.type == rule.NAMESPACE_RULE:
|
||||
# check all namespacerules if used
|
||||
uris = [r.namespaceURI for r in self
|
||||
if r.type == r.NAMESPACE_RULE]
|
||||
useduris = self._getUsedURIs()
|
||||
if rule.namespaceURI in useduris and\
|
||||
uris.count(rule.namespaceURI) == 1:
|
||||
raise xml.dom.NoModificationAllowedErr(
|
||||
u'CSSStyleSheet: NamespaceURI defined in this rule is '
|
||||
u'used, cannot remove.')
|
||||
return
|
||||
|
||||
rule._parentStyleSheet = None # detach
|
||||
del self._cssRules[index] # delete from StyleSheet
|
||||
|
||||
def insertRule(self, rule, index=None, inOrder=False, _clean=True):
|
||||
"""
|
||||
Used to insert a new rule into the style sheet. The new rule now
|
||||
becomes part of the cascade.
|
||||
|
||||
:param rule:
|
||||
a parsable DOMString, in cssutils also a
|
||||
:class:`~cssutils.css.CSSRule` or :class:`~cssutils.css.CSSRuleList`
|
||||
:param index:
|
||||
of the rule before the new rule will be inserted.
|
||||
If the specified `index` is equal to the length of the
|
||||
StyleSheet's rule collection, the rule will be added to the end
|
||||
of the style sheet.
|
||||
If `index` is not given or ``None`` rule will be appended to rule
|
||||
list.
|
||||
:param inOrder:
|
||||
if ``True`` the rule will be put to a proper location while
|
||||
ignoring `index` and without raising
|
||||
:exc:`~xml.dom.HierarchyRequestErr`.
|
||||
The resulting index is returned nevertheless.
|
||||
:returns: The index within the style sheet's rule collection
|
||||
:Exceptions:
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at the specified `index`
|
||||
e.g. if an @import rule is inserted after a standard rule set
|
||||
or other at-rule.
|
||||
- :exc:`~xml.dom.IndexSizeErr`:
|
||||
Raised if the specified `index` is not a valid insertion point.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this style sheet is readonly.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified rule has a syntax error and is
|
||||
unparsable.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
# check position
|
||||
if index is None:
|
||||
index = len(self._cssRules)
|
||||
elif index < 0 or index > self._cssRules.length:
|
||||
raise xml.dom.IndexSizeErr(
|
||||
u'CSSStyleSheet: Invalid index %s for CSSRuleList with a '
|
||||
u'length of %s.' % (index, self._cssRules.length))
|
||||
return
|
||||
|
||||
if isinstance(rule, basestring):
|
||||
# init a temp sheet which has the same properties as self
|
||||
tempsheet = CSSStyleSheet(href=self.href,
|
||||
media=self.media,
|
||||
title=self.title,
|
||||
parentStyleSheet=self.parentStyleSheet,
|
||||
ownerRule=self.ownerRule)
|
||||
tempsheet._ownerNode = self.ownerNode
|
||||
tempsheet._fetcher = self._fetcher
|
||||
# prepend encoding if in this sheet to be able to use it in
|
||||
# @import rules encoding resolution
|
||||
# do not add if new rule startswith "@charset" (which is exact!)
|
||||
if not rule.startswith(u'@charset') and (self._cssRules and
|
||||
self._cssRules[0].type == self._cssRules[0].CHARSET_RULE):
|
||||
# rule 0 is @charset!
|
||||
newrulescount, newruleindex = 2, 1
|
||||
rule = self._cssRules[0].cssText + rule
|
||||
else:
|
||||
newrulescount, newruleindex = 1, 0
|
||||
|
||||
# parse the new rule(s)
|
||||
tempsheet.cssText = (rule, self._namespaces)
|
||||
|
||||
if len(tempsheet.cssRules) != newrulescount or (not isinstance(
|
||||
tempsheet.cssRules[newruleindex], cssutils.css.CSSRule)):
|
||||
self._log.error(u'CSSStyleSheet: Not a CSSRule: %s' % rule)
|
||||
return
|
||||
rule = tempsheet.cssRules[newruleindex]
|
||||
rule._parentStyleSheet = None # done later?
|
||||
|
||||
# TODO:
|
||||
#tempsheet._namespaces = self._namespaces
|
||||
#variables?
|
||||
|
||||
elif isinstance(rule, cssutils.css.CSSRuleList):
|
||||
# insert all rules
|
||||
for i, r in enumerate(rule):
|
||||
self.insertRule(r, index + i)
|
||||
return index
|
||||
|
||||
if not rule.wellformed:
|
||||
self._log.error(u'CSSStyleSheet: Invalid rules cannot be added.')
|
||||
return
|
||||
|
||||
# CHECK HIERARCHY
|
||||
# @charset
|
||||
if rule.type == rule.CHARSET_RULE:
|
||||
if inOrder:
|
||||
index = 0
|
||||
# always first and only
|
||||
if (self._cssRules
|
||||
and self._cssRules[0].type == rule.CHARSET_RULE):
|
||||
self._cssRules[0].encoding = rule.encoding
|
||||
else:
|
||||
self._cssRules.insert(0, rule)
|
||||
elif index != 0 or (self._cssRules and
|
||||
self._cssRules[0].type == rule.CHARSET_RULE):
|
||||
self._log.error(
|
||||
u'CSSStylesheet: @charset only allowed once at the'
|
||||
' beginning of a stylesheet.',
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
else:
|
||||
self._cssRules.insert(index, rule)
|
||||
|
||||
# @unknown or comment
|
||||
elif rule.type in (rule.UNKNOWN_RULE, rule.COMMENT) and not inOrder:
|
||||
if index == 0 and self._cssRules and\
|
||||
self._cssRules[0].type == rule.CHARSET_RULE:
|
||||
self._log.error(
|
||||
u'CSSStylesheet: @charset must be the first rule.',
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
else:
|
||||
self._cssRules.insert(index, rule)
|
||||
|
||||
# @import
|
||||
elif rule.type == rule.IMPORT_RULE:
|
||||
if inOrder:
|
||||
# automatic order
|
||||
if rule.type in (r.type for r in self):
|
||||
# find last of this type
|
||||
for i, r in enumerate(reversed(self._cssRules)):
|
||||
if r.type == rule.type:
|
||||
index = len(self._cssRules) - i
|
||||
break
|
||||
else:
|
||||
# find first point to insert
|
||||
if self._cssRules and\
|
||||
self._cssRules[0].type in (rule.CHARSET_RULE,
|
||||
rule.COMMENT):
|
||||
index = 1
|
||||
else:
|
||||
index = 0
|
||||
else:
|
||||
# after @charset
|
||||
if index == 0 and self._cssRules and\
|
||||
self._cssRules[0].type == rule.CHARSET_RULE:
|
||||
self._log.error(
|
||||
u'CSSStylesheet: Found @charset at index 0.',
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
# before @namespace @variables @page @font-face @media stylerule
|
||||
for r in self._cssRules[:index]:
|
||||
if r.type in (r.NAMESPACE_RULE,
|
||||
r.VARIABLES_RULE,
|
||||
r.MEDIA_RULE,
|
||||
r.PAGE_RULE,
|
||||
r.STYLE_RULE,
|
||||
r.FONT_FACE_RULE):
|
||||
self._log.error(
|
||||
u'CSSStylesheet: Cannot insert @import here,'
|
||||
' found @namespace, @variables, @media, @page or'
|
||||
' CSSStyleRule before index %s.' %
|
||||
index,
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
self._cssRules.insert(index, rule)
|
||||
self._updateVariables()
|
||||
|
||||
# @namespace
|
||||
elif rule.type == rule.NAMESPACE_RULE:
|
||||
if inOrder:
|
||||
if rule.type in (r.type for r in self):
|
||||
# find last of this type
|
||||
for i, r in enumerate(reversed(self._cssRules)):
|
||||
if r.type == rule.type:
|
||||
index = len(self._cssRules) - i
|
||||
break
|
||||
else:
|
||||
# find first point to insert
|
||||
for i, r in enumerate(self._cssRules):
|
||||
if r.type in (r.VARIABLES_RULE, r.MEDIA_RULE,
|
||||
r.PAGE_RULE, r.STYLE_RULE,
|
||||
r.FONT_FACE_RULE, r.UNKNOWN_RULE,
|
||||
r.COMMENT):
|
||||
index = i # before these
|
||||
break
|
||||
else:
|
||||
# after @charset and @import
|
||||
for r in self._cssRules[index:]:
|
||||
if r.type in (r.CHARSET_RULE, r.IMPORT_RULE):
|
||||
self._log.error(
|
||||
u'CSSStylesheet: Cannot insert @namespace here,'
|
||||
' found @charset or @import after index %s.' %
|
||||
index,
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
# before @variables @media @page @font-face and stylerule
|
||||
for r in self._cssRules[:index]:
|
||||
if r.type in (r.VARIABLES_RULE,
|
||||
r.MEDIA_RULE,
|
||||
r.PAGE_RULE,
|
||||
r.STYLE_RULE,
|
||||
r.FONT_FACE_RULE):
|
||||
self._log.error(
|
||||
u'CSSStylesheet: Cannot insert @namespace here,'
|
||||
' found @variables, @media, @page or CSSStyleRule'
|
||||
' before index %s.' %
|
||||
index,
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
|
||||
if not (rule.prefix in self.namespaces and
|
||||
self.namespaces[rule.prefix] == rule.namespaceURI):
|
||||
# no doublettes
|
||||
self._cssRules.insert(index, rule)
|
||||
if _clean:
|
||||
self._cleanNamespaces()
|
||||
|
||||
|
||||
# @variables
|
||||
elif rule.type == rule.VARIABLES_RULE:
|
||||
if inOrder:
|
||||
if rule.type in (r.type for r in self):
|
||||
# find last of this type
|
||||
for i, r in enumerate(reversed(self._cssRules)):
|
||||
if r.type == rule.type:
|
||||
index = len(self._cssRules) - i
|
||||
break
|
||||
else:
|
||||
# find first point to insert
|
||||
for i, r in enumerate(self._cssRules):
|
||||
if r.type in (r.MEDIA_RULE,
|
||||
r.PAGE_RULE,
|
||||
r.STYLE_RULE,
|
||||
r.FONT_FACE_RULE,
|
||||
r.UNKNOWN_RULE,
|
||||
r.COMMENT):
|
||||
index = i # before these
|
||||
break
|
||||
else:
|
||||
# after @charset @import @namespace
|
||||
for r in self._cssRules[index:]:
|
||||
if r.type in (r.CHARSET_RULE,
|
||||
r.IMPORT_RULE,
|
||||
r.NAMESPACE_RULE):
|
||||
self._log.error(
|
||||
u'CSSStylesheet: Cannot insert @variables here,'
|
||||
' found @charset, @import or @namespace after'
|
||||
' index %s.' %
|
||||
index,
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
# before @media @page @font-face and stylerule
|
||||
for r in self._cssRules[:index]:
|
||||
if r.type in (r.MEDIA_RULE,
|
||||
r.PAGE_RULE,
|
||||
r.STYLE_RULE,
|
||||
r.FONT_FACE_RULE):
|
||||
self._log.error(
|
||||
u'CSSStylesheet: Cannot insert @variables here,'
|
||||
' found @media, @page or CSSStyleRule'
|
||||
' before index %s.' %
|
||||
index,
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
|
||||
self._cssRules.insert(index, rule)
|
||||
self._updateVariables()
|
||||
|
||||
# all other where order is not important
|
||||
else:
|
||||
if inOrder:
|
||||
# simply add to end as no specific order
|
||||
self._cssRules.append(rule)
|
||||
index = len(self._cssRules) - 1
|
||||
else:
|
||||
for r in self._cssRules[index:]:
|
||||
if r.type in (r.CHARSET_RULE,
|
||||
r.IMPORT_RULE,
|
||||
r.NAMESPACE_RULE):
|
||||
self._log.error(
|
||||
u'CSSStylesheet: Cannot insert rule here, found '
|
||||
u'@charset, @import or @namespace before index %s.'
|
||||
% index, error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
self._cssRules.insert(index, rule)
|
||||
|
||||
# post settings
|
||||
rule._parentStyleSheet = self
|
||||
|
||||
if rule.IMPORT_RULE == rule.type and not rule.hrefFound:
|
||||
# try loading the imported sheet which has new relative href now
|
||||
rule.href = rule.href
|
||||
|
||||
return index
|
||||
|
||||
ownerRule = property(lambda self: self._ownerRule,
|
||||
doc=u'A ref to an @import rule if it is imported, '
|
||||
u'else ``None``.')
|
||||
|
||||
|
||||
@Deprecated(u'Use ``cssutils.setSerializer(serializer)`` instead.')
|
||||
def setSerializer(self, cssserializer):
|
||||
"""Set the cssutils global Serializer used for all output."""
|
||||
if isinstance(cssserializer, cssutils.CSSSerializer):
|
||||
cssutils.ser = cssserializer
|
||||
else:
|
||||
raise ValueError(u'Serializer must be an instance of '
|
||||
u'cssutils.CSSSerializer.')
|
||||
|
||||
@Deprecated(u'Set pref in ``cssutils.ser.prefs`` instead.')
|
||||
def setSerializerPref(self, pref, value):
|
||||
"""Set a Preference of CSSSerializer used for output.
|
||||
See :class:`cssutils.serialize.Preferences` for possible
|
||||
preferences to be set.
|
||||
"""
|
||||
cssutils.ser.prefs.__setattr__(pref, value)
|
||||
209
libs/cssutils/css/cssunknownrule.py
Executable file
@@ -0,0 +1,209 @@
|
||||
"""CSSUnknownRule implements DOM Level 2 CSS CSSUnknownRule."""
|
||||
__all__ = ['CSSUnknownRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id$'
|
||||
|
||||
import cssrule
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSUnknownRule(cssrule.CSSRule):
|
||||
"""
|
||||
Represents an at-rule not supported by this user agent, so in
|
||||
effect all other at-rules not defined in cssutils.
|
||||
|
||||
Format::
|
||||
|
||||
@xxx until ';' or block {...}
|
||||
"""
|
||||
def __init__(self, cssText=u'', parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
:param cssText:
|
||||
of type string
|
||||
"""
|
||||
super(CSSUnknownRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = None
|
||||
if cssText:
|
||||
self.cssText = cssText
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
return u"cssutils.css.%s(cssText=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return u"<cssutils.css.%s object cssText=%r at 0x%x>" % (
|
||||
self.__class__.__name__,
|
||||
self.cssText,
|
||||
id(self))
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText."""
|
||||
return cssutils.ser.do_CSSUnknownRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
super(CSSUnknownRule, self)._setCssText(cssText)
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
attoken = self._nexttoken(tokenizer, None)
|
||||
if not attoken or self._type(attoken) != self._prods.ATKEYWORD:
|
||||
self._log.error(u'CSSUnknownRule: No CSSUnknownRule found: %s' %
|
||||
self._valuestr(cssText),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
else:
|
||||
# for closures: must be a mutable
|
||||
new = {'nesting': [], # {} [] or ()
|
||||
'wellformed': True
|
||||
}
|
||||
|
||||
def CHAR(expected, seq, token, tokenizer=None):
|
||||
type_, val, line, col = token
|
||||
if expected != 'EOF':
|
||||
if val in u'{[(':
|
||||
new['nesting'].append(val)
|
||||
elif val in u'}])':
|
||||
opening = {u'}': u'{', u']': u'[', u')': u'('}[val]
|
||||
try:
|
||||
if new['nesting'][-1] == opening:
|
||||
new['nesting'].pop()
|
||||
else:
|
||||
raise IndexError()
|
||||
except IndexError:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSUnknownRule: Wrong nesting of '
|
||||
u'{, [ or (.', token=token)
|
||||
|
||||
if val in u'};' and not new['nesting']:
|
||||
expected = 'EOF'
|
||||
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return expected
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSUnknownRule: Expected end of rule.',
|
||||
token=token)
|
||||
return expected
|
||||
|
||||
def FUNCTION(expected, seq, token, tokenizer=None):
|
||||
# handled as opening (
|
||||
type_, val, line, col = token
|
||||
val = self._tokenvalue(token)
|
||||
if expected != 'EOF':
|
||||
new['nesting'].append(u'(')
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return expected
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSUnknownRule: Expected end of rule.',
|
||||
token=token)
|
||||
return expected
|
||||
|
||||
def EOF(expected, seq, token, tokenizer=None):
|
||||
"close all blocks and return 'EOF'"
|
||||
for x in reversed(new['nesting']):
|
||||
closing = {u'{': u'}', u'[': u']', u'(': u')'}[x]
|
||||
seq.append(closing, closing)
|
||||
new['nesting'] = []
|
||||
return 'EOF'
|
||||
|
||||
def INVALID(expected, seq, token, tokenizer=None):
|
||||
# makes rule invalid
|
||||
self._log.error(u'CSSUnknownRule: Bad syntax.',
|
||||
token=token, error=xml.dom.SyntaxErr)
|
||||
new['wellformed'] = False
|
||||
return expected
|
||||
|
||||
def STRING(expected, seq, token, tokenizer=None):
|
||||
type_, val, line, col = token
|
||||
val = self._stringtokenvalue(token)
|
||||
if expected != 'EOF':
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return expected
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSUnknownRule: Expected end of rule.',
|
||||
token=token)
|
||||
return expected
|
||||
|
||||
def URI(expected, seq, token, tokenizer=None):
|
||||
type_, val, line, col = token
|
||||
val = self._uritokenvalue(token)
|
||||
if expected != 'EOF':
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return expected
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSUnknownRule: Expected end of rule.',
|
||||
token=token)
|
||||
return expected
|
||||
|
||||
def default(expected, seq, token, tokenizer=None):
|
||||
type_, val, line, col = token
|
||||
if expected != 'EOF':
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return expected
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSUnknownRule: Expected end of rule.',
|
||||
token=token)
|
||||
return expected
|
||||
|
||||
# unknown : ATKEYWORD S* ... ; | }
|
||||
newseq = self._tempSeq()
|
||||
wellformed, expected = self._parse(expected=None,
|
||||
seq=newseq, tokenizer=tokenizer,
|
||||
productions={'CHAR': CHAR,
|
||||
'EOF': EOF,
|
||||
'FUNCTION': FUNCTION,
|
||||
'INVALID': INVALID,
|
||||
'STRING': STRING,
|
||||
'URI': URI,
|
||||
'S': default # overwrite default default!
|
||||
},
|
||||
default=default,
|
||||
new=new)
|
||||
|
||||
# wellformed set by parse
|
||||
wellformed = wellformed and new['wellformed']
|
||||
|
||||
# post conditions
|
||||
if expected != 'EOF':
|
||||
wellformed = False
|
||||
self._log.error(u'CSSUnknownRule: No ending ";" or "}" found: '
|
||||
u'%r' % self._valuestr(cssText))
|
||||
elif new['nesting']:
|
||||
wellformed = False
|
||||
self._log.error(u'CSSUnknownRule: Unclosed "{", "[" or "(": %r'
|
||||
% self._valuestr(cssText))
|
||||
|
||||
# set all
|
||||
if wellformed:
|
||||
self.atkeyword = self._tokenvalue(attoken)
|
||||
self._setSeq(newseq)
|
||||
|
||||
cssText = property(fget=_getCssText, fset=_setCssText,
|
||||
doc=u"(DOM) The parsable textual representation.")
|
||||
|
||||
type = property(lambda self: self.UNKNOWN_RULE,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
u"type constant.")
|
||||
|
||||
wellformed = property(lambda self: bool(self.atkeyword))
|
||||
|
||||
1251
libs/cssutils/css/cssvalue.py
Executable file
330
libs/cssutils/css/cssvariablesdeclaration.py
Executable file
@@ -0,0 +1,330 @@
|
||||
"""CSSVariablesDeclaration
|
||||
http://disruptive-innovations.com/zoo/cssvariables/#mozTocId496530
|
||||
"""
|
||||
__all__ = ['CSSVariablesDeclaration']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssstyledeclaration.py 1819 2009-08-01 20:52:43Z cthedot $'
|
||||
|
||||
from cssutils.prodparser import *
|
||||
from cssutils.helper import normalize
|
||||
from value import PropertyValue
|
||||
import cssutils
|
||||
import itertools
|
||||
import xml.dom
|
||||
|
||||
class CSSVariablesDeclaration(cssutils.util._NewBase):
|
||||
"""The CSSVariablesDeclaration interface represents a single block of
|
||||
variable declarations.
|
||||
"""
|
||||
def __init__(self, cssText=u'', parentRule=None, readonly=False):
|
||||
"""
|
||||
:param cssText:
|
||||
Shortcut, sets CSSVariablesDeclaration.cssText
|
||||
:param parentRule:
|
||||
The CSS rule that contains this declaration block or
|
||||
None if this CSSVariablesDeclaration is not attached to a CSSRule.
|
||||
:param readonly:
|
||||
defaults to False
|
||||
|
||||
Format::
|
||||
|
||||
variableset
|
||||
: vardeclaration [ ';' S* vardeclaration ]* S*
|
||||
;
|
||||
|
||||
vardeclaration
|
||||
: varname ':' S* term
|
||||
;
|
||||
|
||||
varname
|
||||
: IDENT S*
|
||||
;
|
||||
"""
|
||||
super(CSSVariablesDeclaration, self).__init__()
|
||||
self._parentRule = parentRule
|
||||
self._vars = {}
|
||||
if cssText:
|
||||
self.cssText = cssText
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
return u"cssutils.css.%s(cssText=%r)" % (self.__class__.__name__,
|
||||
self.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return u"<cssutils.css.%s object length=%r at 0x%x>" % (
|
||||
self.__class__.__name__,
|
||||
self.length,
|
||||
id(self))
|
||||
|
||||
def __contains__(self, variableName):
|
||||
"""Check if a variable is in variable declaration block.
|
||||
|
||||
:param variableName:
|
||||
a string
|
||||
"""
|
||||
return normalize(variableName) in self.keys()
|
||||
|
||||
def __getitem__(self, variableName):
|
||||
"""Retrieve the value of variable ``variableName`` from this
|
||||
declaration.
|
||||
"""
|
||||
return self.getVariableValue(variableName)
|
||||
|
||||
def __setitem__(self, variableName, value):
|
||||
self.setVariable(variableName, value)
|
||||
|
||||
def __delitem__(self, variableName):
|
||||
return self.removeVariable(variableName)
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterator of names of set variables."""
|
||||
for name in self.keys():
|
||||
yield name
|
||||
|
||||
def keys(self):
|
||||
"""Analoguous to standard dict returns variable names which are set in
|
||||
this declaration."""
|
||||
return self._vars.keys()
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText."""
|
||||
return cssutils.ser.do_css_CSSVariablesDeclaration(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""Setting this attribute will result in the parsing of the new value
|
||||
and resetting of all the properties in the declaration block
|
||||
including the removal or addition of properties.
|
||||
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this declaration is readonly or a property is readonly.
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
|
||||
Format::
|
||||
|
||||
variableset
|
||||
: vardeclaration [ ';' S* vardeclaration ]*
|
||||
;
|
||||
|
||||
vardeclaration
|
||||
: varname ':' S* term
|
||||
;
|
||||
|
||||
varname
|
||||
: IDENT S*
|
||||
;
|
||||
|
||||
expr
|
||||
: [ VARCALL | term ] [ operator [ VARCALL | term ] ]*
|
||||
;
|
||||
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
vardeclaration = Sequence(
|
||||
PreDef.ident(),
|
||||
PreDef.char(u':', u':', toSeq=False),
|
||||
#PreDef.S(toSeq=False, optional=True),
|
||||
Prod(name=u'term', match=lambda t, v: True,
|
||||
toSeq=lambda t, tokens: (u'value',
|
||||
PropertyValue(itertools.chain([t],
|
||||
tokens),
|
||||
parent=self)
|
||||
)
|
||||
)
|
||||
)
|
||||
prods = Sequence(vardeclaration,
|
||||
Sequence(PreDef.S(optional=True),
|
||||
PreDef.char(u';', u';', toSeq=False),
|
||||
PreDef.S(optional=True),
|
||||
vardeclaration,
|
||||
minmax=lambda: (0, None)),
|
||||
PreDef.S(optional=True),
|
||||
PreDef.char(u';', u';', toSeq=False, optional=True)
|
||||
)
|
||||
# parse
|
||||
wellformed, seq, store, notused = \
|
||||
ProdParser().parse(cssText,
|
||||
u'CSSVariableDeclaration',
|
||||
prods)
|
||||
if wellformed:
|
||||
newseq = self._tempSeq()
|
||||
newvars = {}
|
||||
|
||||
# seq contains only name: value pairs plus comments etc
|
||||
nameitem = None
|
||||
for item in seq:
|
||||
if u'IDENT' == item.type:
|
||||
nameitem = item
|
||||
elif u'value' == item.type:
|
||||
nname = normalize(nameitem.value)
|
||||
if nname in newvars:
|
||||
# replace var with same name
|
||||
for i, it in enumerate(newseq):
|
||||
if normalize(it.value[0]) == nname:
|
||||
newseq.replace(i,
|
||||
(nameitem.value, item.value),
|
||||
'var',
|
||||
nameitem.line, nameitem.col)
|
||||
else:
|
||||
# saved non normalized name for reserialization
|
||||
newseq.append((nameitem.value, item.value),
|
||||
'var',
|
||||
nameitem.line, nameitem.col)
|
||||
|
||||
# newseq.append((nameitem.value, item.value),
|
||||
# 'var',
|
||||
# nameitem.line, nameitem.col)
|
||||
|
||||
newvars[nname] = item.value
|
||||
|
||||
else:
|
||||
newseq.appendItem(item)
|
||||
|
||||
self._setSeq(newseq)
|
||||
self._vars = newvars
|
||||
self.wellformed = True
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc=u"(DOM) A parsable textual representation of the declaration "
|
||||
u"block excluding the surrounding curly braces.")
|
||||
|
||||
def _setParentRule(self, parentRule):
|
||||
self._parentRule = parentRule
|
||||
|
||||
parentRule = property(lambda self: self._parentRule, _setParentRule,
|
||||
doc=u"(DOM) The CSS rule that contains this"
|
||||
u" declaration block or None if this block"
|
||||
u" is not attached to a CSSRule.")
|
||||
|
||||
def getVariableValue(self, variableName):
|
||||
"""Used to retrieve the value of a variable if it has been explicitly
|
||||
set within this variable declaration block.
|
||||
|
||||
:param variableName:
|
||||
The name of the variable.
|
||||
:returns:
|
||||
the value of the variable if it has been explicitly set in this
|
||||
variable declaration block. Returns the empty string if the
|
||||
variable has not been set.
|
||||
"""
|
||||
try:
|
||||
return self._vars[normalize(variableName)].cssText
|
||||
except KeyError, e:
|
||||
return u''
|
||||
|
||||
def removeVariable(self, variableName):
|
||||
"""Used to remove a variable if it has been explicitly set within this
|
||||
variable declaration block.
|
||||
|
||||
:param variableName:
|
||||
The name of the variable.
|
||||
:returns:
|
||||
the value of the variable if it has been explicitly set for this
|
||||
variable declaration block. Returns the empty string if the
|
||||
variable has not been set.
|
||||
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this declaration is readonly is readonly.
|
||||
"""
|
||||
normalname = variableName
|
||||
try:
|
||||
r = self._vars[normalname]
|
||||
except KeyError, e:
|
||||
return u''
|
||||
else:
|
||||
self.seq._readonly = False
|
||||
if normalname in self._vars:
|
||||
for i, x in enumerate(self.seq):
|
||||
if x.value[0] == variableName:
|
||||
del self.seq[i]
|
||||
self.seq._readonly = True
|
||||
del self._vars[normalname]
|
||||
|
||||
return r.cssText
|
||||
|
||||
def setVariable(self, variableName, value):
|
||||
"""Used to set a variable value within this variable declaration block.
|
||||
|
||||
:param variableName:
|
||||
The name of the CSS variable.
|
||||
:param value:
|
||||
The new value of the variable, may also be a PropertyValue object.
|
||||
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified value has a syntax error and is
|
||||
unparsable.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if this declaration is readonly or the property is
|
||||
readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
# check name
|
||||
wellformed, seq, store, unused = \
|
||||
ProdParser().parse(normalize(variableName),
|
||||
u'variableName',
|
||||
Sequence(PreDef.ident()))
|
||||
if not wellformed:
|
||||
self._log.error(u'Invalid variableName: %r: %r'
|
||||
% (variableName, value))
|
||||
else:
|
||||
# check value
|
||||
if isinstance(value, PropertyValue):
|
||||
v = value
|
||||
else:
|
||||
v = PropertyValue(cssText=value, parent=self)
|
||||
|
||||
if not v.wellformed:
|
||||
self._log.error(u'Invalid variable value: %r: %r'
|
||||
% (variableName, value))
|
||||
else:
|
||||
# update seq
|
||||
self.seq._readonly = False
|
||||
|
||||
variableName = normalize(variableName)
|
||||
|
||||
if variableName in self._vars:
|
||||
for i, x in enumerate(self.seq):
|
||||
if x.value[0] == variableName:
|
||||
self.seq.replace(i,
|
||||
[variableName, v],
|
||||
x.type,
|
||||
x.line,
|
||||
x.col)
|
||||
break
|
||||
else:
|
||||
self.seq.append([variableName, v], 'var')
|
||||
self.seq._readonly = True
|
||||
self._vars[variableName] = v
|
||||
|
||||
def item(self, index):
|
||||
"""Used to retrieve the variables that have been explicitly set in
|
||||
this variable declaration block. The order of the variables
|
||||
retrieved using this method does not have to be the order in which
|
||||
they were set. This method can be used to iterate over all variables
|
||||
in this variable declaration block.
|
||||
|
||||
:param index:
|
||||
of the variable name to retrieve, negative values behave like
|
||||
negative indexes on Python lists, so -1 is the last element
|
||||
|
||||
:returns:
|
||||
The name of the variable at this ordinal position. The empty
|
||||
string if no variable exists at this position.
|
||||
"""
|
||||
try:
|
||||
return self.keys()[index]
|
||||
except IndexError:
|
||||
return u''
|
||||
|
||||
length = property(lambda self: len(self._vars),
|
||||
doc=u"The number of variables that have been explicitly set in this"
|
||||
u" variable declaration block. The range of valid indices is 0"
|
||||
u" to length-1 inclusive.")
|
||||
198
libs/cssutils/css/cssvariablesrule.py
Executable file
@@ -0,0 +1,198 @@
|
||||
"""CSSVariables implements (and only partly) experimental
|
||||
`CSS Variables <http://disruptive-innovations.com/zoo/cssvariables/>`_
|
||||
"""
|
||||
__all__ = ['CSSVariablesRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssfontfacerule.py 1818 2009-07-30 21:39:00Z cthedot $'
|
||||
|
||||
from cssvariablesdeclaration import CSSVariablesDeclaration
|
||||
import cssrule
|
||||
import cssutils
|
||||
import xml.dom
|
||||
|
||||
class CSSVariablesRule(cssrule.CSSRule):
|
||||
"""
|
||||
The CSSVariablesRule interface represents a @variables rule within a CSS
|
||||
style sheet. The @variables rule is used to specify variables.
|
||||
|
||||
cssutils uses a :class:`~cssutils.css.CSSVariablesDeclaration` to
|
||||
represent the variables.
|
||||
|
||||
Format::
|
||||
|
||||
variables
|
||||
VARIABLES_SYM S* medium [ COMMA S* medium ]* LBRACE S*
|
||||
variableset* '}' S*
|
||||
;
|
||||
|
||||
for variableset see :class:`cssutils.css.CSSVariablesDeclaration`
|
||||
|
||||
**Media are not implemented. Reason is that cssutils is using CSS
|
||||
variables in a kind of preprocessing and therefor no media information
|
||||
is available at this stage. For now do not use media!**
|
||||
|
||||
Example::
|
||||
|
||||
@variables {
|
||||
CorporateLogoBGColor: #fe8d12;
|
||||
}
|
||||
|
||||
div.logoContainer {
|
||||
background-color: var(CorporateLogoBGColor);
|
||||
}
|
||||
"""
|
||||
def __init__(self, mediaText=None, variables=None, parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
If readonly allows setting of properties in constructor only.
|
||||
"""
|
||||
super(CSSVariablesRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = u'@variables'
|
||||
|
||||
# dummy
|
||||
self._media = cssutils.stylesheets.MediaList(mediaText,
|
||||
readonly=readonly)
|
||||
|
||||
if variables:
|
||||
self.variables = variables
|
||||
else:
|
||||
self.variables = CSSVariablesDeclaration(parentRule=self)
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __repr__(self):
|
||||
return u"cssutils.css.%s(mediaText=%r, variables=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self._media.mediaText,
|
||||
self.variables.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return u"<cssutils.css.%s object mediaText=%r variables=%r valid=%r " \
|
||||
u"at 0x%x>" % (self.__class__.__name__,
|
||||
self._media.mediaText,
|
||||
self.variables.cssText,
|
||||
self.valid,
|
||||
id(self))
|
||||
|
||||
def _getCssText(self):
|
||||
"""Return serialized property cssText."""
|
||||
return cssutils.ser.do_CSSVariablesRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
:exceptions:
|
||||
- :exc:`~xml.dom.SyntaxErr`:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||
Raised if the rule is readonly.
|
||||
|
||||
Format::
|
||||
|
||||
variables
|
||||
: VARIABLES_SYM S* medium [ COMMA S* medium ]* LBRACE S*
|
||||
variableset* '}' S*
|
||||
;
|
||||
|
||||
variableset
|
||||
: LBRACE S* vardeclaration [ ';' S* vardeclaration ]* '}' S*
|
||||
;
|
||||
"""
|
||||
super(CSSVariablesRule, self)._setCssText(cssText)
|
||||
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
attoken = self._nexttoken(tokenizer, None)
|
||||
if self._type(attoken) != self._prods.VARIABLES_SYM:
|
||||
self._log.error(u'CSSVariablesRule: No CSSVariablesRule found: %s' %
|
||||
self._valuestr(cssText),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
else:
|
||||
newVariables = CSSVariablesDeclaration(parentRule=self)
|
||||
ok = True
|
||||
|
||||
beforetokens, brace = self._tokensupto2(tokenizer,
|
||||
blockstartonly=True,
|
||||
separateEnd=True)
|
||||
if self._tokenvalue(brace) != u'{':
|
||||
ok = False
|
||||
self._log.error(u'CSSVariablesRule: No start { of variable '
|
||||
u'declaration found: %r'
|
||||
% self._valuestr(cssText), brace)
|
||||
|
||||
# parse stuff before { which should be comments and S only
|
||||
new = {'wellformed': True}
|
||||
newseq = self._tempSeq()#[]
|
||||
|
||||
beforewellformed, expected = self._parse(expected=':',
|
||||
seq=newseq, tokenizer=self._tokenize2(beforetokens),
|
||||
productions={})
|
||||
ok = ok and beforewellformed and new['wellformed']
|
||||
|
||||
variablestokens, braceorEOFtoken = self._tokensupto2(tokenizer,
|
||||
blockendonly=True,
|
||||
separateEnd=True)
|
||||
|
||||
val, type_ = self._tokenvalue(braceorEOFtoken), \
|
||||
self._type(braceorEOFtoken)
|
||||
if val != u'}' and type_ != 'EOF':
|
||||
ok = False
|
||||
self._log.error(u'CSSVariablesRule: No "}" after variables '
|
||||
u'declaration found: %r'
|
||||
% self._valuestr(cssText))
|
||||
|
||||
nonetoken = self._nexttoken(tokenizer)
|
||||
if nonetoken:
|
||||
ok = False
|
||||
self._log.error(u'CSSVariablesRule: Trailing content found.',
|
||||
token=nonetoken)
|
||||
|
||||
if 'EOF' == type_:
|
||||
# add again as variables needs it
|
||||
variablestokens.append(braceorEOFtoken)
|
||||
# SET but may raise:
|
||||
newVariables.cssText = variablestokens
|
||||
|
||||
if ok:
|
||||
# contains probably comments only upto {
|
||||
self._setSeq(newseq)
|
||||
self.variables = newVariables
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc=u"(DOM) The parsable textual representation of this "
|
||||
u"rule.")
|
||||
|
||||
media = property(doc=u"NOT IMPLEMENTED! As cssutils resolves variables "\
|
||||
u"during serializing media information is lost.")
|
||||
|
||||
def _setVariables(self, variables):
|
||||
"""
|
||||
:param variables:
|
||||
a CSSVariablesDeclaration or string
|
||||
"""
|
||||
self._checkReadonly()
|
||||
if isinstance(variables, basestring):
|
||||
self._variables = CSSVariablesDeclaration(cssText=variables,
|
||||
parentRule=self)
|
||||
else:
|
||||
variables._parentRule = self
|
||||
self._variables = variables
|
||||
|
||||
variables = property(lambda self: self._variables, _setVariables,
|
||||
doc=u"(DOM) The variables of this rule set, a "
|
||||
u":class:`cssutils.css.CSSVariablesDeclaration`.")
|
||||
|
||||
type = property(lambda self: self.VARIABLES_RULE,
|
||||
doc=u"The type of this rule, as defined by a CSSRule "
|
||||
u"type constant.")
|
||||
|
||||
valid = property(lambda self: True, doc='NOT IMPLEMTED REALLY (TODO)')
|
||||
|
||||
# constant but needed:
|
||||
wellformed = property(lambda self: True)
|
||||