diff --git a/couchpotato/core/_base/clientscript/main.py b/couchpotato/core/_base/clientscript/main.py
index 9536463e..323c2e4a 100644
--- a/couchpotato/core/_base/clientscript/main.py
+++ b/couchpotato/core/_base/clientscript/main.py
@@ -4,9 +4,11 @@ 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 re
+import time
import traceback
log = CPLog(__name__)
@@ -109,7 +111,8 @@ class ClientScript(Plugin):
if file_type == 'script':
data = jsmin(f)
else:
- data = cssprefixer.process(f, debug = False, minify = True)
+ data = self.prefix(f)
+ data = cssmin(f)
data = data.replace('../images/', '../static/images/')
data = data.replace('../fonts/', '../static/fonts/')
data = data.replace('../../static/', '../static/') # Replace inside plugins
@@ -122,7 +125,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, ss(data.strip()))
+ self.createFile(out, data.strip())
if not self.minified.get(file_type):
self.minified[file_type] = {}
@@ -170,3 +173,28 @@ class ClientScript(Plugin):
if not self.paths[type].get(location):
self.paths[type][location] = []
self.paths[type][location].append(file_path)
+
+ prefix_properties = ['border-radius', 'transform', 'transition', 'box-shadow']
+ prefix_tags = ['ms', 'moz', 'webkit']
+ def prefix(self, data):
+
+ trimmed_data = re.sub('(\t|\n|\r)+', '', data)
+
+ new_data = ''
+ colon_split = trimmed_data.split(';')
+ for splt in colon_split:
+ curl_split = splt.strip().split('{')
+ for curly in curl_split:
+ curly = curly.strip()
+ for prop in self.prefix_properties:
+ if curly[:len(prop) + 1] == prop + ':':
+ for tag in self.prefix_tags:
+ new_data += ' -%s-%s; ' % (tag, curly)
+
+ new_data += curly + (' { ' if len(curl_split) > 1 else ' ')
+
+ new_data += '; '
+
+ new_data = new_data.replace('{ ;', '; ').replace('} ;', '} ')
+
+ return new_data
diff --git a/couchpotato/core/_base/updater/main.py b/couchpotato/core/_base/updater/main.py
index 44ceebdd..e4e936ce 100644
--- a/couchpotato/core/_base/updater/main.py
+++ b/couchpotato/core/_base/updater/main.py
@@ -33,7 +33,7 @@ class Updater(Plugin):
else:
self.updater = SourceUpdater()
- addEvent('app.load', self.autoUpdate)
+ addEvent('app.load', self.setCrons)
addEvent('updater.info', self.info)
addApiView('updater.info', self.getInfo, docs = {
@@ -62,7 +62,7 @@ class Updater(Plugin):
self.autoUpdate() # Check after enabling
def autoUpdate(self):
- if self.check() and self.conf('automatic') and not self.updater.update_failed:
+ if self.isEnabled() and self.check() and self.conf('automatic') and not self.updater.update_failed:
if self.updater.doUpdate():
# Notify before restarting
@@ -80,8 +80,8 @@ class Updater(Plugin):
return False
- def check(self):
- if self.isDisabled():
+ def check(self, force = False):
+ if not force and self.isDisabled():
return
if self.updater.check():
@@ -100,7 +100,7 @@ class Updater(Plugin):
def checkView(self):
return jsonified({
- 'update_available': self.check(),
+ 'update_available': self.check(force = True),
'info': self.updater.info()
})
diff --git a/couchpotato/core/downloaders/transmission/main.py b/couchpotato/core/downloaders/transmission/main.py
index 8cf47380..6094e89b 100644
--- a/couchpotato/core/downloaders/transmission/main.py
+++ b/couchpotato/core/downloaders/transmission/main.py
@@ -8,7 +8,6 @@ import httplib
import json
import os.path
import re
-import shutil
import traceback
import urllib2
@@ -37,12 +36,7 @@ class Transmission(Downloader):
if len(self.conf('directory', default = '')) > 0:
folder_name = self.createFileName(data, filedata, movie)[:-len(data.get('type')) - 1]
- folder_path = os.path.join(self.conf('directory', default = ''), folder_name).rstrip(os.path.sep)
-
- # Create the empty folder to download too
- self.makeDir(folder_path)
-
- params['download-dir'] = folder_path
+ params['download-dir'] = os.path.join(self.conf('directory', default = ''), folder_name).rstrip(os.path.sep)
torrent_params = {}
if self.conf('ratio'):
@@ -94,11 +88,13 @@ class Transmission(Downloader):
'fields': ['id', 'name', 'hashString', 'percentDone', 'status', 'eta', 'isFinished', 'downloadDir', 'uploadRatio']
}
queue = trpc.get_alltorrents(return_params)
-
except Exception, err:
log.error('Failed getting queue: %s', err)
return False
+ if not queue:
+ return []
+
statuses = StatusList(self)
# Get torrents status
diff --git a/couchpotato/core/downloaders/utorrent/main.py b/couchpotato/core/downloaders/utorrent/main.py
index bca57e71..f49859ab 100644
--- a/couchpotato/core/downloaders/utorrent/main.py
+++ b/couchpotato/core/downloaders/utorrent/main.py
@@ -3,10 +3,9 @@ from bencode import bencode, bdecode
from couchpotato.core.downloaders.base import Downloader, StatusList
from couchpotato.core.helpers.encoding import isInt, ss
from couchpotato.core.logger import CPLog
+from datetime import timedelta
from hashlib import sha1
from multipartpost import MultipartPostHandler
-from datetime import timedelta
-import os
import cookielib
import httplib
import json
@@ -106,35 +105,6 @@ class uTorrent(Downloader):
return False
statuses = StatusList(self)
- download_folder = ''
- settings_dict = {}
-
- try:
- data = self.utorrent_api.get_settings()
- utorrent_settings = json.loads(data)
-
- # Create settings dict
- for item in utorrent_settings['settings']:
- if item[1] == 0: # int
- settings_dict[item[0]] = int(item[2] if not item[2].strip() == '' else '0')
- elif item[1] == 1: # bool
- settings_dict[item[0]] = True if item[2] == 'true' else False
- elif item[1] == 2: # string
- settings_dict[item[0]] = item[2]
- log.debug('uTorrent settings: %s', settings_dict)
-
- # Get the download path from the uTorrent settings
- if settings_dict['dir_completed_download_flag']:
- download_folder = settings_dict['dir_completed_download']
- elif settings_dict['dir_active_download_flag']:
- download_folder = settings_dict['dir_active_download']
- else:
- log.info('No download folder set in uTorrent. Please set a download folder')
- return False
-
- except Exception, err:
- log.error('Failed to get settings from uTorrent: %s', err)
- return False
# Get torrents
for item in queue.get('torrents', []):
@@ -144,18 +114,13 @@ class uTorrent(Downloader):
if item[21] == 'Finished' or item[21] == 'Seeding':
status = 'completed'
- if settings_dict['dir_add_label']:
- release_folder = os.path.join(download_folder, item[11], item[2])
- else:
- release_folder = os.path.join(download_folder, item[2])
-
statuses.append({
'id': item[0],
'name': item[2],
'status': status,
'original_status': item[1],
'timeleft': str(timedelta(seconds = item[10])),
- 'folder': release_folder,
+ 'folder': item[26],
})
return statuses
@@ -235,4 +200,22 @@ class uTorrentAPI(object):
def get_settings(self):
action = "action=getsettings"
- return self._request(action)
+ settings_dict = {}
+ try:
+ utorrent_settings = json.loads(self._request(action))
+
+ # Create settings dict
+ for item in utorrent_settings['settings']:
+ if item[1] == 0: # int
+ settings_dict[item[0]] = int(item[2] if not item[2].strip() == '' else '0')
+ elif item[1] == 1: # bool
+ settings_dict[item[0]] = True if item[2] == 'true' else False
+ elif item[1] == 2: # string
+ settings_dict[item[0]] = item[2]
+
+ #log.debug('uTorrent settings: %s', settings_dict)
+
+ except Exception, err:
+ log.error('Failed to get settings from uTorrent: %s', err)
+
+ return settings_dict
diff --git a/couchpotato/core/event.py b/couchpotato/core/event.py
index 2f872a6a..1fd6ab25 100644
--- a/couchpotato/core/event.py
+++ b/couchpotato/core/event.py
@@ -139,8 +139,13 @@ def fireEvent(name, *args, **kwargs):
log.error('%s: %s', (name, traceback.format_exc()))
def fireEventAsync(*args, **kwargs):
- kwargs['async'] = True
- fireEvent(*args, **kwargs)
+ try:
+ t = threading.Thread(target = fireEvent, args = args, kwargs = kwargs)
+ t.setDaemon(True)
+ t.start()
+ return True
+ except Exception, e:
+ log.error('%s: %s', (args[0], e))
def errorHandler(error):
etype, value, tb = error
diff --git a/couchpotato/core/logger.py b/couchpotato/core/logger.py
index 3ad33aa0..68a6c3f0 100644
--- a/couchpotato/core/logger.py
+++ b/couchpotato/core/logger.py
@@ -1,6 +1,5 @@
import logging
import re
-import traceback
class CPLog(object):
@@ -50,8 +49,8 @@ class CPLog(object):
msg = msg % tuple([ss(x) for x in list(replace_tuple)])
else:
msg = msg % ss(replace_tuple)
- except:
- self.logger.error(u'Failed encoding stuff to log: %s' % traceback.format_exc())
+ except Exception, e:
+ self.logger.error(u'Failed encoding stuff to log "%s": %s' % (msg, e))
if not Env.get('dev'):
diff --git a/couchpotato/core/notifications/core/static/notification.js b/couchpotato/core/notifications/core/static/notification.js
index 2b74364a..b05eb1a3 100644
--- a/couchpotato/core/notifications/core/static/notification.js
+++ b/couchpotato/core/notifications/core/static/notification.js
@@ -31,8 +31,8 @@ var NotificationBase = new Class({
});
window.addEvent('load', function(){
- self.startInterval.delay($(window).getSize().x <= 480 ? 2000 : 300, self)
- });
+ self.startInterval.delay($(window).getSize().x <= 480 ? 2000 : 100, self);
+ })
},
diff --git a/couchpotato/core/notifications/plex/__init__.py b/couchpotato/core/notifications/plex/__init__.py
index 8d89a40f..c00ea6d4 100644
--- a/couchpotato/core/notifications/plex/__init__.py
+++ b/couchpotato/core/notifications/plex/__init__.py
@@ -22,6 +22,13 @@ config = [{
'description': 'Default should be on localhost',
'advanced': True,
},
+ {
+ 'name': 'on_snatch',
+ 'default': 0,
+ 'type': 'bool',
+ 'advanced': True,
+ 'description': 'Also send message when movie is snatched.',
+ },
],
}
],
diff --git a/couchpotato/core/notifications/pushover/main.py b/couchpotato/core/notifications/pushover/main.py
index ba7f426e..ea5e7748 100644
--- a/couchpotato/core/notifications/pushover/main.py
+++ b/couchpotato/core/notifications/pushover/main.py
@@ -23,7 +23,7 @@ class Pushover(Notification):
}
if data and data.get('library'):
- api_data.extend({
+ api_data.update({
'url': toUnicode('http://www.imdb.com/title/%s/' % data['library']['identifier']),
'url_title': toUnicode('%s on IMDb' % getTitle(data['library'])),
})
diff --git a/couchpotato/core/notifications/xbmc/__init__.py b/couchpotato/core/notifications/xbmc/__init__.py
index 0753c82a..a4808458 100644
--- a/couchpotato/core/notifications/xbmc/__init__.py
+++ b/couchpotato/core/notifications/xbmc/__init__.py
@@ -31,6 +31,13 @@ config = [{
'default': '',
'type': 'password',
},
+ {
+ 'name': 'on_snatch',
+ 'default': 0,
+ 'type': 'bool',
+ 'advanced': True,
+ 'description': 'Also send message when movie is snatched.',
+ },
],
}
],
diff --git a/couchpotato/core/notifications/xbmc/main.py b/couchpotato/core/notifications/xbmc/main.py
index 84b9f7e4..f60b917f 100755
--- a/couchpotato/core/notifications/xbmc/main.py
+++ b/couchpotato/core/notifications/xbmc/main.py
@@ -13,6 +13,7 @@ class XBMC(Notification):
listen_to = ['renamer.after']
use_json_notifications = {}
+ http_time_between_calls = 0
def notify(self, message = '', data = {}, listener = None):
diff --git a/couchpotato/core/plugins/base.py b/couchpotato/core/plugins/base.py
index 9330631d..2d0d1efa 100644
--- a/couchpotato/core/plugins/base.py
+++ b/couchpotato/core/plugins/base.py
@@ -28,6 +28,7 @@ class Plugin(object):
_needs_shutdown = False
+ user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20130519 Firefox/24.0'
http_last_use = {}
http_time_between_calls = 0
http_failed_request = {}
@@ -104,12 +105,13 @@ class Plugin(object):
if not params: params = {}
# Fill in some headers
- headers['Referer'] = headers.get('Referer', urlparse(url).hostname)
- headers['Host'] = headers.get('Host', urlparse(url).hostname)
- headers['User-Agent'] = headers.get('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0.2) Gecko/20100101 Firefox/10.0.2')
- headers['Accept-encoding'] = headers.get('Accept-encoding', 'gzip')
+ parsed_url = urlparse(url)
+ host = parsed_url.hostname
- host = urlparse(url).hostname
+ headers['Referer'] = headers.get('Referer', '%s://%s' % (parsed_url.scheme, host))
+ headers['Host'] = headers.get('Host', host)
+ headers['User-Agent'] = headers.get('User-Agent', self.user_agent)
+ headers['Accept-encoding'] = headers.get('Accept-encoding', 'gzip')
# Don't try for failed requests
if self.http_failed_disabled.get(host, 0) > 0:
@@ -138,7 +140,7 @@ class Plugin(object):
response = opener.open(request, timeout = timeout)
else:
- log.info('Opening url: %s, params: %s', (url, [x for x in params.iterkeys()]))
+ log.info('Opening url: %s, params: %s', (url, [x for x in params.iterkeys()] if isinstance(params, dict) else 'with data'))
data = tryUrlencode(params) if len(params) > 0 else None
request = urllib2.Request(url, data, headers)
diff --git a/couchpotato/core/plugins/dashboard/main.py b/couchpotato/core/plugins/dashboard/main.py
index d14eb5fb..aabe856f 100644
--- a/couchpotato/core/plugins/dashboard/main.py
+++ b/couchpotato/core/plugins/dashboard/main.py
@@ -73,7 +73,7 @@ class Dashboard(Plugin):
profile_pre[profile.get('id')] = contains
# Get all active movies
- active_status = fireEvent('status.get', 'active', single = True)
+ active_status, snatched_status, downloaded_status, available_status = fireEvent('status.get', ['active', 'snatched', 'downloaded', 'available'], single = True)
subq = db.query(Movie).filter(Movie.status_id == active_status.get('id')).subquery()
q = db.query(Movie).join((subq, subq.c.id == Movie.id)) \
@@ -108,6 +108,14 @@ class Dashboard(Plugin):
if pp.get('dvd') and fireEvent('searcher.could_be_released', False, eta, single = True):
coming_soon = True
+ # Skip if movie is snatched/downloaded/available
+ skip = False
+ for release in movie.releases:
+ if release.status_id in [snatched_status.get('id'), downloaded_status.get('id'), available_status.get('id')]:
+ skip = True
+ break
+ if skip:
+ continue
if coming_soon:
temp = movie.to_dict({
@@ -125,6 +133,7 @@ class Dashboard(Plugin):
if len(movies) >= limit:
break
+ db.expire_all()
return jsonified({
'success': True,
'empty': len(movies) == 0,
diff --git a/couchpotato/core/plugins/file/main.py b/couchpotato/core/plugins/file/main.py
index 5239f1e5..43f36e16 100644
--- a/couchpotato/core/plugins/file/main.py
+++ b/couchpotato/core/plugins/file/main.py
@@ -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 or 'version' in filename: continue
+ if root == python_cache or 'minified' in filename or 'version' in filename or 'temp_updater' in root: continue
file_path = os.path.join(root, filename)
f = db.query(File).filter(File.path == toUnicode(file_path)).first()
if not f:
diff --git a/couchpotato/core/plugins/file/static/file.js b/couchpotato/core/plugins/file/static/file.js
index 7b893e88..6659c882 100644
--- a/couchpotato/core/plugins/file/static/file.js
+++ b/couchpotato/core/plugins/file/static/file.js
@@ -1,11 +1,11 @@
var File = new Class({
- initialize: function(file){
+ initialize: function(type, file){
var self = this;
if(!file){
self.empty = true;
- self.el = new Element('div');
+ self.el = new Element('div.empty_file.'+type);
return
}
@@ -22,7 +22,10 @@ var File = new Class({
var file_name = self.data.path.replace(/^.*[\\\/]/, '');
self.el = new Element('div', {
- 'class': 'type_image ' + self.type.identifier
+ 'class': 'type_image ' + self.type.identifier,
+ 'styles': {
+ 'background-image': 'url('+Api.createUrl('file.cache') + file_name+')'
+ }
}).adopt(
new Element('img', {
'src': Api.createUrl('file.cache') + file_name
@@ -45,7 +48,7 @@ var FileSelect = new Class({
});
if(single)
- return new File(results.pop());
+ return new File(type, results.pop());
return results;
diff --git a/couchpotato/core/plugins/library/main.py b/couchpotato/core/plugins/library/main.py
index b91ebe1e..168aead1 100644
--- a/couchpotato/core/plugins/library/main.py
+++ b/couchpotato/core/plugins/library/main.py
@@ -20,7 +20,6 @@ class LibraryPlugin(Plugin):
addEvent('library.update', self.update)
addEvent('library.update_release_date', self.updateReleaseDate)
-
def add(self, attrs = {}, update_after = True):
db = get_session()
@@ -53,6 +52,7 @@ class LibraryPlugin(Plugin):
library_dict = l.to_dict(self.default_dict)
+ db.expire_all()
return library_dict
def update(self, identifier, default_title = '', force = False):
@@ -132,6 +132,7 @@ class LibraryPlugin(Plugin):
library_dict = library.to_dict(self.default_dict)
+ db.expire_all()
return library_dict
def updateReleaseDate(self, identifier):
@@ -150,6 +151,7 @@ class LibraryPlugin(Plugin):
library.info = mergeDicts(library.info, {'release_date': dates })
db.commit()
+ db.expire_all()
return dates
diff --git a/couchpotato/core/plugins/log/static/log.js b/couchpotato/core/plugins/log/static/log.js
index 0e276b58..e9e4af05 100644
--- a/couchpotato/core/plugins/log/static/log.js
+++ b/couchpotato/core/plugins/log/static/log.js
@@ -6,19 +6,6 @@ Page.Log = new Class({
title: 'Show recent logs.',
has_tab: false,
- initialize: function(options){
- var self = this;
- self.parent(options)
-
-
- App.getBlock('more').addLink(new Element('a', {
- 'href': App.createUrl(self.name),
- 'text': self.name.capitalize(),
- 'title': self.title
- }))
-
- },
-
indexAction: function(){
var self = this;
diff --git a/couchpotato/core/plugins/movie/main.py b/couchpotato/core/plugins/movie/main.py
index 1ce68f08..cb7281fa 100644
--- a/couchpotato/core/plugins/movie/main.py
+++ b/couchpotato/core/plugins/movie/main.py
@@ -118,12 +118,13 @@ class MoviePlugin(Plugin):
.filter(Movie.status_id == done_status.get('id'), Movie.last_edit < (now - week)) \
.all()
- #
for movie in movies:
for rel in movie.releases:
if rel.status_id in [available_status.get('id'), snatched_status.get('id')]:
fireEvent('release.delete', id = rel.id, single = True)
+ db.expire_all()
+
def getView(self):
movie_id = getParam('id')
@@ -149,6 +150,7 @@ class MoviePlugin(Plugin):
if m:
results = m.to_dict(self.default_dict)
+ db.expire_all()
return results
def list(self, status = None, release_status = None, limit_offset = None, starts_with = None, search = None, order = None):
@@ -174,8 +176,6 @@ class MoviePlugin(Plugin):
if release_status and len(release_status) > 0:
q = q.filter(or_(*[Release.status.has(identifier = s) for s in release_status]))
- total_count = q.count()
-
filter_or = []
if starts_with:
starts_with = toUnicode(starts_with.lower())
@@ -193,6 +193,8 @@ class MoviePlugin(Plugin):
if filter_or:
q = q.filter(or_(*filter_or))
+ total_count = q.count()
+
if order == 'release_order':
q = q.order_by(desc(Release.last_edit))
else:
@@ -216,15 +218,14 @@ class MoviePlugin(Plugin):
results = q2.all()
movies = []
for movie in results:
- temp = movie.to_dict({
+ movies.append(movie.to_dict({
'profile': {'types': {}},
'releases': {'files':{}, 'info': {}},
'library': {'titles': {}, 'files':{}},
'files': {},
- })
- movies.append(temp)
+ }))
- #db.close()
+ db.expire_all()
return (total_count, movies)
def availableChars(self, status = None, release_status = None):
@@ -259,7 +260,7 @@ class MoviePlugin(Plugin):
if char not in chars:
chars += str(char)
- #db.close()
+ db.expire_all()
return ''.join(sorted(chars, key = str.lower))
def listView(self):
@@ -318,8 +319,7 @@ class MoviePlugin(Plugin):
fireEvent('notify.frontend', type = 'movie.busy.%s' % id, data = True)
fireEventAsync('library.update', identifier = movie.library.identifier, default_title = default_title, force = True, on_complete = self.createOnComplete(id))
-
- #db.close()
+ db.expire_all()
return jsonified({
'success': True,
})
@@ -428,7 +428,7 @@ class MoviePlugin(Plugin):
if added:
fireEvent('notify.frontend', type = 'movie.added', data = movie_dict, message = 'Successfully added "%s" to your wanted list.' % params.get('title', ''))
- #db.close()
+ db.expire_all()
return movie_dict
@@ -478,7 +478,7 @@ class MoviePlugin(Plugin):
movie_dict = m.to_dict(self.default_dict)
fireEventAsync('searcher.single', movie_dict, on_complete = self.createNotifyFront(movie_id))
- #db.close()
+ db.expire_all()
return jsonified({
'success': True,
})
@@ -540,7 +540,7 @@ class MoviePlugin(Plugin):
if deleted:
fireEvent('notify.frontend', type = 'movie.deleted', data = movie.to_dict())
- #db.close()
+ db.expire_all()
return True
def restatus(self, movie_id):
@@ -568,7 +568,6 @@ class MoviePlugin(Plugin):
m.status_id = active_status.get('id') if move_to_wanted else done_status.get('id')
db.commit()
- #db.close()
return True
@@ -578,6 +577,7 @@ class MoviePlugin(Plugin):
db = get_session()
movie = db.query(Movie).filter_by(id = movie_id).first()
fireEventAsync('searcher.single', movie.to_dict(self.default_dict), on_complete = self.createNotifyFront(movie_id))
+ db.expire_all()
return onComplete
@@ -588,5 +588,6 @@ class MoviePlugin(Plugin):
db = get_session()
movie = db.query(Movie).filter_by(id = movie_id).first()
fireEvent('notify.frontend', type = 'movie.update.%s' % movie.id, data = movie.to_dict(self.default_dict))
+ db.expire_all()
return notifyFront
diff --git a/couchpotato/core/plugins/movie/static/list.js b/couchpotato/core/plugins/movie/static/list.js
index 6f577012..1b11fab5 100644
--- a/couchpotato/core/plugins/movie/static/list.js
+++ b/couchpotato/core/plugins/movie/static/list.js
@@ -14,6 +14,7 @@ var MovieList = new Class({
movies: [],
movies_added: {},
+ total_movies: 0,
letters: {},
filter: null,
@@ -23,7 +24,7 @@ var MovieList = new Class({
self.offset = 0;
self.filter = self.options.filter || {
- 'startswith': null,
+ 'starts_with': null,
'search': null
}
@@ -48,7 +49,7 @@ var MovieList = new Class({
self.changeView('list');
else
self.changeView(self.getSavedView() || self.options.view || 'details');
-
+
self.getMovies();
App.addEvent('movie.added', self.movieAdded.bind(self))
@@ -62,7 +63,8 @@ var MovieList = new Class({
self.movies.each(function(movie){
if(movie.get('id') == notification.data.id){
movie.destroy();
- delete self.movies_added[notification.data.id]
+ delete self.movies_added[notification.data.id];
+ self.setCounter(self.counter_count-1);
}
})
}
@@ -76,6 +78,7 @@ var MovieList = new Class({
if(self.options.add_new && !self.movies_added[notification.data.id] && notification.data.status.identifier == self.options.status){
window.scroll(0,0);
self.createMovie(notification.data, 'top');
+ self.setCounter(self.counter_count+1);
self.checkIfEmpty();
}
@@ -115,7 +118,7 @@ var MovieList = new Class({
self.createMovie(movie);
});
- self.total_movies = total;
+ self.total_movies += total;
self.setCounter(total);
},
@@ -125,8 +128,41 @@ var MovieList = new Class({
if(!self.navigation_counter) return;
+ self.counter_count = count;
self.navigation_counter.set('text', (count || 0) + ' movies');
+ if (self.empty_message) {
+ self.empty_message.destroy();
+ self.empty_message = null;
+ }
+
+ if(self.total_movies && count == 0 && !self.empty_message){
+ var message = (self.filter.search ? 'for "'+self.filter.search+'"' : '') +
+ (self.filter.starts_with ? ' in '+self.filter.starts_with+'' : '');
+
+ self.empty_message = new Element('.message', {
+ 'html': 'No movies found ' + message + '.
'
+ }).grab(
+ new Element('a', {
+ 'text': 'Reset filter',
+ 'events': {
+ 'click': function(){
+ self.filter = {
+ 'starts_with': null,
+ 'search': null
+ };
+ self.navigation_search_input.set('value', '');
+ self.reset();
+ self.activateLetter();
+ self.getMovies(true);
+ self.last_search_value = '';
+ }
+ }
+ })
+ ).inject(self.movie_list);
+
+ }
+
},
createMovie: function(movie, inject_at){
@@ -151,66 +187,69 @@ var MovieList = new Class({
self.el.addClass('with_navigation')
- self.navigation = new Element('div.alph_nav').grab(
- new Element('div').adopt(
- self.navigation_alpha = new Element('ul.numbers', {
+ self.navigation = new Element('div.alph_nav').adopt(
+ 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)
+ }
+ })
+ )
+ ),
+ new Element('div.menus').adopt(
+ self.navigation_counter = new Element('span.counter[title=Total]'),
+ self.filter_menu = new Block.Menu(self, {
+ 'class': 'filter'
+ }),
+ self.navigation_actions = new Element('ul.actions', {
'events': {
'click:relay(li)': function(e, el){
- self.movie_list.empty()
- self.activateLetter(el.get('data-letter'))
- self.getMovies()
+ var a = 'active';
+ self.navigation_actions.getElements('.'+a).removeClass(a);
+ self.changeView(el.get('data-view'));
+ this.addClass(a);
+
+ el.inject(el.getParent(), 'top');
+ el.getSiblings().hide()
+ setTimeout(function(){
+ el.getSiblings().setStyle('display', null);
+ }, 100)
}
}
}),
- 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)
- }
- })
- )
- )
+ self.navigation_menu = new Block.Menu(self, {
+ 'class': 'extra'
+ })
)
).inject(self.el, 'top');
@@ -223,20 +262,39 @@ var MovieList = new Class({
}).inject(self.mass_edit_quality)
});
+ self.filter_menu.addLink(
+ self.navigation_search_input = new Element('input', {
+ 'title': 'Search through ' + self.options.identifier,
+ 'placeholder': 'Search through ' + self.options.identifier,
+ 'events': {
+ 'keyup': self.search.bind(self),
+ 'change': self.search.bind(self)
+ }
+ })
+ ).addClass('search');
+
+ self.filter_menu.addEvent('open', function(){
+ self.navigation_search_input.focus();
+ });
+
+ self.filter_menu.addLink(
+ self.navigation_alpha = new Element('ul.numbers', {
+ 'events': {
+ 'click:relay(li.available)': function(e, el){
+ self.activateLetter(el.get('data-letter'))
+ self.getMovies(true)
+ }
+ }
+ })
+ );
+
// Actions
['mass_edit', 'details', 'list'].each(function(view){
- self.navigation_actions.adopt(
- new Element('li.'+view+(self.current_view == view ? '.active' : '')+'[data-view='+view+']', {
- 'events': {
- 'click': function(e){
- var a = 'active';
- self.navigation_actions.getElements('.'+a).removeClass(a);
- self.changeView(this.get('data-view'));
- this.addClass(a);
- }
- }
- }).adopt(new Element('span'))
- )
+ var current = self.current_view == view;
+ new Element('li', {
+ 'class': 'icon2 ' + view + (current ? ' active ' : ''),
+ 'data-view': view
+ }).inject(self.navigation_actions, current ? 'top' : 'bottom');
});
// All
@@ -260,11 +318,11 @@ var MovieList = new Class({
'status': self.options.status
}, self.filter),
'onSuccess': function(json){
-
+
json.chars.split('').each(function(c){
self.letters[c.capitalize()].addClass('available')
})
-
+
}
});
@@ -322,14 +380,14 @@ var MovieList = new Class({
self.movies.each(function(movie){
if (movie.isSelected()){
$(movie).destroy()
- erase_movies.include(movie)
+ erase_movies.include(movie);
}
});
erase_movies.each(function(movie){
self.movies.erase(movie);
-
- movie.destroy()
+ movie.destroy();
+ self.setCounter(self.counter_count-1);
});
self.calculateSelected();
@@ -448,8 +506,7 @@ var MovieList = new Class({
self.activateLetter();
self.filter.search = search_value;
- self.movie_list.empty();
- self.getMovies();
+ self.getMovies(true);
self.last_search_value = search_value;
@@ -461,11 +518,10 @@ var MovieList = new Class({
var self = this;
self.reset();
- self.movie_list.empty();
- self.getMovies();
+ self.getMovies(true);
},
- getMovies: function(){
+ getMovies: function(reset){
var self = this;
if(self.scrollspy){
@@ -492,10 +548,13 @@ var MovieList = new Class({
Api.request(self.options.api_call || 'movie.list', {
'data': Object.merge({
'status': self.options.status,
- 'limit_offset': self.options.limit + ',' + self.offset
+ 'limit_offset': self.options.limit ? self.options.limit + ',' + self.offset : null
}, self.filter),
'onSuccess': function(json){
+ if(reset)
+ self.movie_list.empty();
+
if(self.loader_first){
var lf = self.loader_first;
self.loader_first.addClass('hide')
diff --git a/couchpotato/core/plugins/movie/static/movie.actions.js b/couchpotato/core/plugins/movie/static/movie.actions.js
index cf30e8a5..f091e7da 100644
--- a/couchpotato/core/plugins/movie/static/movie.actions.js
+++ b/couchpotato/core/plugins/movie/static/movie.actions.js
@@ -1,6 +1,6 @@
var MovieAction = new Class({
- class_name: 'action icon',
+ class_name: 'action icon2',
initialize: function(movie){
var self = this;
@@ -82,7 +82,7 @@ MA.Release = new Class({
create: function(){
var self = this;
- self.el = new Element('a.releases.icon.download', {
+ self.el = new Element('a.releases.download', {
'title': 'Show the releases that are available for ' + self.movie.getTitle(),
'events': {
'click': self.show.bind(self)
@@ -100,10 +100,8 @@ MA.Release = new Class({
var self = this;
if(!self.options_container){
- self.options_container = new Element('div.options').adopt(
- self.release_container = new Element('div.releases.table').adopt(
- self.trynext_container = new Element('div.buttons.try_container')
- )
+ self.options_container = new Element('div.options').grab(
+ self.release_container = new Element('div.releases.table')
);
// Header
@@ -149,11 +147,11 @@ MA.Release = new Class({
new Element('span.age', {'text': self.get(release, 'age')}),
new Element('span.score', {'text': self.get(release, 'score')}),
new Element('span.provider', { 'text': provider, 'title': provider }),
- release.info['detail_url'] ? new Element('a.info.icon', {
+ release.info['detail_url'] ? new Element('a.info.icon2', {
'href': release.info['detail_url'],
'target': '_blank'
- }) : null,
- new Element('a.download.icon', {
+ }) : new Element('a'),
+ new Element('a.download.icon2', {
'events': {
'click': function(e){
(e).preventDefault();
@@ -162,7 +160,7 @@ MA.Release = new Class({
}
}
}),
- new Element('a.delete.icon', {
+ new Element('a.delete.icon2', {
'events': {
'click': function(e){
(e).preventDefault();
@@ -190,7 +188,9 @@ MA.Release = new Class({
self.release_container.getElement('#release_'+self.next_release.id).addClass('next_release');
}
- if(self.next_release || self.last_release){
+ if(self.next_release || (self.last_release && ['ignored', 'failed'].indexOf(self.last_release.status.identifier) === false)){
+
+ self.trynext_container = new Element('div.buttons.try_container').inject(self.release_container, 'top');
self.trynext_container.adopt(
new Element('span.or', {
@@ -237,18 +237,19 @@ MA.Release = new Class({
(e).preventDefault();
self.createReleases();
- self.trynext_container = new Element('div.buttons.trynext').inject(self.movie.info_container);
- if(self.next_release || self.last_release){
+ if(self.next_release || (self.last_release && ['ignored', 'failed'].indexOf(self.last_release.status.identifier) === false)){
+
+ self.trynext_container = new Element('div.buttons.trynext').inject(self.movie.info_container);
self.trynext_container.adopt(
- self.next_release ? [new Element('a.icon.readd', {
+ self.next_release ? [new Element('a.icon2.readd', {
'text': self.last_release ? 'Download another release' : 'Download the best release',
'events': {
'click': self.tryNextRelease.bind(self)
}
}),
- new Element('a.icon.download', {
+ new Element('a.icon2.download', {
'text': 'pick one yourself',
'events': {
'click': function(){
@@ -256,7 +257,7 @@ MA.Release = new Class({
}
}
})] : null,
- new Element('a.icon.completed', {
+ new Element('a.icon2.completed', {
'text': 'mark this movie done',
'events': {
'click': function(){
@@ -292,7 +293,7 @@ MA.Release = new Class({
var self = this;
var release_el = self.release_container.getElement('#release_'+release.id),
- icon = release_el.getElement('.download.icon');
+ icon = release_el.getElement('.download.icon2');
self.movie.busy(true);
diff --git a/couchpotato/core/plugins/movie/static/movie.css b/couchpotato/core/plugins/movie/static/movie.css
index 66c0ef00..8327d294 100644
--- a/couchpotato/core/plugins/movie/static/movie.css
+++ b/couchpotato/core/plugins/movie/static/movie.css
@@ -9,9 +9,20 @@
clear: both;
}
+ .movies > div .message {
+ display: block;
+ padding: 20px;
+ font-size: 20px;
+ color: white;
+ text-align: center;
+ }
+ .movies > div .message a {
+ padding: 20px;
+ display: block;
+ }
+
.movies.thumbs_list > div:not(.description) {
margin-right: -4px;
- text-align: center;
}
.movies .loading {
@@ -79,24 +90,36 @@
.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);
+ transition-property: width, height;
+ background: rgba(0,0,0,.2);
}
+ .movies.mass_edit_list .movie {
+ padding-left: 22px;
+ background: none;
+ }
+
.movies.details_list .movie {
padding-left: 120px;
}
-
+
.movies.list_list .movie:not(.details_view),
.movies.mass_edit_list .movie {
- height: 32px;
+ height: 30px;
+ border-bottom: 1px solid rgba(255,255,255,.15);
}
+ .movies.list_list .movie:last-child,
+ .movies.mass_edit_list .movie:last-child {
+ border: none;
+ }
+
.movies.thumbs_list .movie {
width: 16.66667%;
height: auto;
@@ -104,9 +127,6 @@
margin: 0;
padding: 0;
vertical-align: top;
- border-radius: 0;
- box-shadow: none;
- border: 0;
}
@media all and (max-width: 800px) {
@@ -125,16 +145,7 @@
.movies.list_list .movie:not(.details_view),
.movies.mass_edit_list .movie {
- margin: 1px 0;
- border-radius: 0;
- background: no-repeat;
- box-shadow: none;
- border-bottom: 1px solid rgba(255,255,255,0.05);
- }
-
- .movies.list_list .movie:hover:not(.details_view),
- .movies.mass_edit_list .movie {
- background: rgba(255,255,255,0.03);
+ margin: 0;
}
.movies .data {
@@ -142,18 +153,18 @@
height: 100%;
width: 100%;
position: relative;
- right: 0;
- border-radius: 0;
transition: all .6s cubic-bezier(0.9,0,0.1,1);
+ right: 0;
}
.movies.list_list .movie:not(.details_view) .data,
.movies.mass_edit_list .movie .data {
- height: 30px;
- padding: 3px 0 3px 10px;
- box-shadow: none;
+ padding: 0 0 0 10px;
border: 0;
background: #4e5969;
}
+ .movies.mass_edit_list .movie .data {
+ padding-left: 8px;
+ }
.movies.thumbs_list .data {
position: absolute;
@@ -165,14 +176,6 @@
background: none;
transition: none;
}
-
- .movies.thumbs_list .movie.no_thumbnail .data { background-image: linear-gradient(-30deg, rgba(255, 0, 85, .2) 0,rgba(125, 185, 235, .2) 100%);
- }
- .movies.thumbs_list .movie.no_thumbnail:nth-child(2n+6) .data { background-image: linear-gradient(-20deg, rgba(125, 0, 215, .2) 0, rgba(4, 55, 5, .7) 100%); }
- .movies.thumbs_list .movie.no_thumbnail:nth-child(3n+6) .data { background-image: linear-gradient(-30deg, rgba(155, 0, 85, .2) 0,rgba(25, 185, 235, .7) 100%); }
- .movies.thumbs_list .movie.no_thumbnail:nth-child(4n+6) .data { background-image: linear-gradient(-30deg, rgba(115, 5, 235, .2) 0, rgba(55, 180, 5, .7) 100%); }
- .movies.thumbs_list .movie.no_thumbnail:nth-child(5n+6) .data { background-image: linear-gradient(-30deg, rgba(35, 15, 215, .2) 0, rgba(135, 215, 115, .7) 100%); }
- .movies.thumbs_list .movie.no_thumbnail:nth-child(6n+6) .data { background-image: linear-gradient(-30deg, rgba(35, 15, 215, .2) 0, rgba(135, 15, 115, .7) 100%); }
.movies.thumbs_list .movie:hover .data {
background: rgba(0,0,0,0.9);
@@ -201,18 +204,16 @@
line-height: 0;
overflow: hidden;
height: 100%;
- border-radius: 4px 0 0 4px;
transition: all .6s cubic-bezier(0.9,0,0.1,1);
+ background: rgba(0,0,0,.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;
height: 30px;
- border-radius: 1px 0 0 1px;
}
.movies.mass_edit_list .poster {
display: none;
@@ -222,7 +223,13 @@
width: 100%;
height: 100%;
transition: none;
+ background: no-repeat center;
+ background-size: cover;
}
+ .movies.thumbs_list .no_thumbnail .empty_file {
+ width: 100%;
+ height: 100%;
+ }
.movies .poster img,
.options .poster img {
@@ -234,7 +241,7 @@
width: 100%;
top: 0;
bottom: 0;
- border-radius: 0;
+ opacity: 0;
}
.movies .info {
@@ -248,22 +255,32 @@
font-weight: bold;
margin-bottom: 10px;
margin-top: 2px;
- left: 0;
- top: 0;
width: 100%;
padding-right: 80px;
transition: all 0.2s linear;
+ height: 35px;
+ top: -5px;
+ position: relative;
}
+ .movies.list_list .info .title,
+ .movies.mass_edit_list .info .title {
+ height: 100%;
+ top: 0;
+ margin: 0;
+ }
+ .touch_enabled .movies.list_list .info .title {
+ display: inline-block;
+ padding-right: 55px;
+ }
+
.movies .info .title span {
- display: block;
+ display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 100%;
- height: 30px;
+ height: 100%;
line-height: 30px;
- top: -5px;
- position: relative;
}
.movies.thumbs_list .info .title span {
@@ -300,13 +317,14 @@
font-size: 21px;
word-wrap: break-word;
padding: 0;
+ height: 100%;
}
.movies .info .year {
position: absolute;
color: #bbb;
right: 0;
- top: 1px;
+ top: 6px;
text-align: right;
transition: all 0.2s linear;
font-weight: normal;
@@ -326,6 +344,10 @@
right: auto;
color: #FFF;
}
+
+ .touch_enabled .movies.list_list .movie .info .year {
+ font-size: 1em;
+ }
.movies .info .description {
top: 30px;
@@ -350,6 +372,17 @@
min-height: 20px;
}
+ .movies.list_list .movie:hover .data .quality {
+ display: none;
+ }
+
+ .touch_enabled .movies.list_list .movie .data .quality {
+ position: relative;
+ display: inline-block;
+ margin: 0;
+ top: -4px;
+ }
+
@media all and (max-width: 480px) {
.movies .data .quality {
display: none;
@@ -382,12 +415,12 @@
right: 0;
margin-right: 60px;
z-index: 1;
+ top: 5px;
}
.movies .data .quality .available,
.movies .data .quality .snatched {
opacity: 1;
- box-shadow: 1px 1px 0 rgba(0,0,0,0.2);
cursor: pointer;
}
@@ -407,13 +440,14 @@
.movies .data .actions {
position: absolute;
- bottom: 20px;
+ bottom: 17px;
right: 20px;
line-height: 0;
top: 0;
+ display: block;
+ width: auto;
opacity: 0;
display: none;
- width: 0;
}
@media all and (max-width: 480px) {
.movies .data .actions {
@@ -421,10 +455,10 @@
}
}
- .movies .movie:hover .data .actions {
+ .movies .movie:hover .data .actions,
+ .touch_enabled .movies .movie .data .actions {
opacity: 1;
display: inline-block;
- width: auto;
}
.movies.details_list .data .actions {
@@ -437,7 +471,7 @@
display: inline-block;
}
.movies.thumbs_list .data .actions {
- bottom: 2px;
+ bottom: 12px;
right: 10px;
top: auto;
}
@@ -446,20 +480,31 @@
.movies .movie:hover .action:hover { opacity: 1; }
.movies .data .action {
- background-repeat: no-repeat;
- background-position: center;
display: inline-block;
- width: 26px;
- height: 26px;
- padding: 3px;
+ height: 22px;
+ min-width: 33px;
+ padding: 0 5px;
+ line-height: 26px;
+ text-align: center;
+ font-size: 13px;
+ color: #FFF;
+ margin-left: 1px;
}
-
+ .movies .data .action.trailer { color: #FFF; }
+ .movies .data .action.download { color: #b9dec0; }
+ .movies .data .action.edit { color: #c6b589; }
+ .movies .data .action.refresh { color: #cbeecc; }
+ .movies .data .action.delete { color: #e9b0b0; }
+ .movies .data .action.directory { color: #ffed92; }
+ .movies .data .action.readd { color: #c2fac5; }
+
.movies.mass_edit_list .movie .data .actions {
display: none;
}
.movies.list_list .movie:not(.details_view):hover .actions,
- .movies.mass_edit_list .movie:hover .actions {
+ .movies.mass_edit_list .movie:hover .actions,
+ .touch_enabled .movies.list_list .movie:not(.details_view) .actions {
margin: 0;
background: #4e5969;
top: 2px;
@@ -473,12 +518,10 @@
text-align: center;
font-size: 20px;
position: absolute;
- padding: 70px 0 0;
+ padding: 80px 0 0;
left: 120px;
right: 0;
}
- .movies .delete_container .cancel {
- }
.movies .delete_container .or {
padding: 10px;
}
@@ -498,9 +541,9 @@
}
.movies .options .form {
- margin: 70px 20px 0;
- float: left;
+ margin: 80px 0 0;
font-size: 20px;
+ text-align: center;
}
.movies .options .form select {
@@ -510,6 +553,7 @@
.movies .options .table {
height: 180px;
overflow: auto;
+ line-height: 2em;
}
.movies .options .table .item {
border-bottom: 1px solid rgba(255,255,255,0.1);
@@ -518,8 +562,10 @@
text-decoration: line-through;
color: rgba(255,255,255,0.4);
}
- .movies .options .table .item.ignored .delete {
- background-image: url('../images/icon.undo.png');
+ .movies .options .table .item.ignored .delete:before {
+ display: inline-block;
+ content: "\e04b";
+ transform: scale(-1, 1);
}
.movies .options .table .item:last-child { border: 0; }
@@ -565,10 +611,12 @@
width: 30px !important;
height: 20px;
opacity: 0.8;
+ line-height: 25px;
}
- .movies .options .table a:hover {
- opacity: 1;
- }
+ .movies .options .table a:hover { opacity: 1; }
+ .movies .options .table a.download { color: #a7fbaf; }
+ .movies .options .table a.delete { color: #fda3a3; }
+ .movies .options .table .ignored a.delete { color: #b5fda3; }
.movies .options .table .head > * {
font-weight: bold;
@@ -601,7 +649,6 @@
text-align: center;
padding: 3px 10px;
background: #4e5969;
- border-radius: 0 0 2px 2px;
transition: all .2s cubic-bezier(0.9,0,0.1,1) .2s;
z-index: 11;
}
@@ -637,16 +684,17 @@
.movies .movie .trynext {
display: inline;
position: absolute;
- right: 135px;
+ right: 180px;
z-index: 2;
opacity: 0;
background: #4e5969;
- min-width: 300px;
text-align: right;
height: 100%;
- padding: 3px 0;
top: 0;
}
+ .touch_enabled .movies .movie .trynext {
+ display: none;
+ }
@media all and (max-width: 480px) {
.movies .movie .trynext {
@@ -655,9 +703,10 @@
}
.movies.mass_edit_list .trynext { display: none; }
.wanted .movies .movie .trynext {
- padding-right: 50px;
+ padding-right: 30px;
}
- .movies .movie:hover .trynext {
+ .movies .movie:hover .trynext,
+ .touch_enabled .movies.details_list .movie .trynext {
opacity: 1;
}
@@ -665,8 +714,7 @@
background: #47515f;
padding: 0;
right: 0;
- bottom: 35px;
- height: auto;
+ height: 25px;
}
.movies .movie .trynext a {
@@ -674,12 +722,23 @@
padding: 0 5px 0 25px;
margin-right: 10px;
color: #FFF;
- border-radius: 2px;
+ height: 100%;
+ line-height: 27px;
+ font-family: OpenSans, "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
}
- .movies .movie .trynext a:last-child {
- margin: 0;
+ .movies .movie .trynext a:before {
+ margin: 2px 0 0 -20px;
+ position: absolute;
+ font-family: 'Elusive-Icons';
}
- .movies .movie .trynext a:hover {
+ .movies.details_list .movie .trynext a {
+ line-height: 23px;
+ }
+ .movies .movie .trynext a:last-child {
+ margin: 0;
+ }
+ .movies .movie .trynext a:hover,
+ .touch_enabled .movies .movie .trynext a {
background-color: #369545;
}
@@ -694,14 +753,7 @@
}
.movies .alph_nav {
- transition: box-shadow .4s linear;
- position: relative;
- z-index: 4;
- top: 0px;
- right: 0;
- margin: 0 auto;
- width: 100%;
- padding: 10px 0;
+ height: 44px;
}
@media all and (max-width: 480px) {
@@ -709,13 +761,10 @@
display: none;
}
}
-
- .movies .alph_nav > div {
- position: relative;
- max-width: 980px;
- margin: 0 auto;
- padding: 0;
- min-height: 24px;
+
+ .movies .alph_nav .menus {
+ display: inline-block;
+ float: right;
}
.movies .alph_nav .numbers,
@@ -724,118 +773,152 @@
list-style: none;
padding: 0 0 1px;
margin: 0;
- float: left;
user-select: none;
}
.movies .alph_nav .counter {
+ display: inline-block;
text-align: right;
- position: absolute;
- right: 270px;
- background: #4e5969;
- padding: 4px 10px;
+ padding: 0 10px;
+ height: 100%;
+ line-height: 43px;
+ border-right: 1px solid rgba(255,255,255,.07);
}
.movies .alph_nav .numbers li,
.movies .alph_nav .actions li {
display: inline-block;
vertical-align: top;
- height: 24px;
- line-height: 23px;
+ height: 100%;
+ line-height: 30px;
text-align: center;
- cursor: pointer;
- color: rgba(255,255,255,0.2);
border: 1px solid transparent;
transition: all 0.1s ease-in-out;
}
-
- @media all and (max-width: 900px) {
- .movies .alph_nav .numbers {
- display: none;
- }
- }
.movies .alph_nav .numbers li {
- width: auto;
- padding: 0 4px;
+ width: 30px;
+ height: 30px;
+ opacity: 0.3;
}
+ .movies .alph_nav .numbers li.letter_all {
+ width: 60px;
+ }
.movies .alph_nav li.available {
- color: #FFF;
- font-weight: bolder;
+ font-weight: bold;
+ cursor: pointer;
+ opacity: 1;
}
.movies .alph_nav li.active.available,
.movies .alph_nav li.available:hover {
- background: rgba(255,255,255,.1);
+ background: rgba(0,0,0,.1);
}
- .movies .alph_nav .search {
+ .movies .alph_nav .search input {
padding: 6px 5px;
- 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);
+ width: 100%;
+ height: 44px;
+ display: inline-block;
+ border: 0;
+ background: none;
+ color: #444;
+ font-size: 14px;
+ padding: 10px;
+ padding: 0 10px 0 30px;
+ border-bottom: 1px solid rgba(0,0,0,.08);
}
+ .movies .alph_nav .search input:focus {
+ background: rgba(0,0,0,.08);
+ }
+
+ .movies .alph_nav .search input::-webkit-input-placeholder {
+ color: #444;
+ opacity: .6;
+ }
+
+ .movies .alph_nav .search:before {
+ font-family: 'Elusive-Icons';
+ content: "\e03e";
+ position: absolute;
+ height: 100%;
+ line-height: 45px;
+ font-size: 12px;
+ margin: 0 0 0 10px;
+ opacity: .6;
+ color: #444;
+ }
.movies .alph_nav .actions {
- margin: 0 6px 0 0;
-moz-user-select: none;
- position: absolute;
- right: 183px;
+ width: 44px;
+ height: 44px;
+ display: inline-block;
+ vertical-align: top;
+ z-index: 200;
+ position: relative;
+ border: 1px solid rgba(255,255,255,.07);
+ border-width: 0 1px;
}
+ .movies .alph_nav .actions:hover {
+ box-shadow: 0 100px 20px -10px rgba(0,0,0,0.55);
+ }
.movies .alph_nav .actions li {
- border-radius: 1px;
- width: auto;
+ width: 100%;
+ height: 45px;
+ line-height: 40px;
+ position: relative;
+ z-index: 20;
+ display: none;
+ cursor: pointer;
}
- .movies .alph_nav .actions li.active {
- background: none;
- border: 1px solid transparent;
- box-shadow: none;
- }
- .movies .alph_nav .actions li span {
- display: block;
- background: url('../images/sprite.png') no-repeat;
- width: 25px;
- height: 100%;
- }
-
- .movies .alph_nav .actions li.mass_edit span {
- background-position: 3px 3px;
+ .movies .alph_nav .actions:hover li:not(.active) {
+ display: block;
+ background: #FFF;
+ color: #444;
+ }
+ .movies .alph_nav .actions li:hover:not(.active) {
+ background: #ccc;
+ }
+ .movies .alph_nav .actions li.active {
+ display: block;
}
- .movies .alph_nav .actions li.list span {
- background-position: 3px -95px;
+ .movies .alph_nav .actions li.mass_edit:before {
+ content: "\e070";
}
- .movies .alph_nav .actions li.details span {
- background-position: 3px -74px;
+ .movies .alph_nav .actions li.list:before {
+ content: "\e0d8";
}
- .movies .alph_nav .actions li:first-child {
- border-radius: 3px 0 0 3px;
- }
- .movies .alph_nav .actions li:last-child {
- border-radius: 0 3px 3px 0;
- }
+ .movies .alph_nav .actions li.details:before {
+ content: "\e022";
+ }
.movies .alph_nav .mass_edit_form {
clear: both;
text-align: center;
display: none;
+ overflow: hidden;
+ float: left;
+ height: 44px;
+ line-height: 44px;
}
.movies.mass_edit_list .mass_edit_form {
- display: block;
+ display: inline-block;
}
.movies.mass_edit_list .mass_edit_form .select {
- float: left;
- margin: 5px 0 0 5px;
font-size: 14px;
+ display: inline-block;
}
- .movies.mass_edit_list .mass_edit_form .select span {
+ .movies.mass_edit_list .mass_edit_form .select .check {
+ display: inline-block;
vertical-align: middle;
+ margin: -4px 0 0 5px;
+ }
+ .movies.mass_edit_list .mass_edit_form .select span {
opacity: 0.7;
}
.movies.mass_edit_list .mass_edit_form .select .count {
@@ -844,8 +927,7 @@
}
.movies .alph_nav .mass_edit_form .quality {
- float: left;
- padding: 8px 0 0;
+ display: inline-block;
margin: 0 0 0 16px;
}
.movies .alph_nav .mass_edit_form .quality select {
@@ -858,8 +940,8 @@
.movies .alph_nav .mass_edit_form .refresh,
.movies .alph_nav .mass_edit_form .delete {
- float: left;
- padding: 8px 0 0 8px;
+ display: inline-block;
+ margin-left: 8px;
}
.movies .alph_nav .mass_edit_form .refresh span,
@@ -867,28 +949,48 @@
margin: 0 10px 0 0;
}
- .movies .alph_nav .more_menu {
- right: 0;
- position: absolute;
- }
-
.movies .alph_nav .more_menu > a {
- background-color: #4e5969;
- background-position: center -158px;
+ background: none;
}
+ .movies .alph_nav .more_menu.extra > a:before {
+ content: '...';
+ font-size: 1.7em;
+ line-height: 23px;
+ text-align: center;
+ display: block;
+ }
+
+ .movies .alph_nav .more_menu.filter {
+ }
+
+ .movies .alph_nav .more_menu.filter > a:before {
+ content: "\e0e8";
+ font-family: 'Elusive-Icons';
+ line-height: 33px;
+ display: block;
+ text-align: center;
+ }
+
+ .movies .alph_nav .more_menu.filter .wrapper {
+ right: 88px;
+ width: 300px;
+ }
+
.movies .empty_wanted {
background-image: url('../images/emptylist.png');
+ background-position: 80% 0;
height: 750px;
- width: 800px;
+ width: 100%;
+ max-width: 900px;
padding-top: 260px;
- margin-top: -50px;
}
.movies .empty_manage {
text-align: center;
font-size: 25px;
line-height: 150%;
+ padding: 40px 0;
}
.movies .empty_manage .after_manage {
@@ -897,7 +999,6 @@
}
.movies .progress {
- border-radius: 2px;
padding: 10px;
margin: 5px 0;
text-align: left;
@@ -912,7 +1013,6 @@
width: 49%;
background: rgba(255, 255, 255, 0.05);
margin: 2px 0.5%;
- border-radius: 3px;
}
.movies .progress > div .folder {
diff --git a/couchpotato/core/plugins/movie/static/movie.js b/couchpotato/core/plugins/movie/static/movie.js
index 95fd1de5..5ca36c9d 100644
--- a/couchpotato/core/plugins/movie/static/movie.js
+++ b/couchpotato/core/plugins/movie/static/movie.js
@@ -11,7 +11,7 @@ var Movie = new Class({
self.view = options.view || 'details';
self.list = list;
- self.el = new Element('div.movie.inlay');
+ self.el = new Element('div.movie');
self.profile = Quality.getProfile(data.profile_id) || {};
self.parent(self, options);
@@ -23,7 +23,8 @@ var Movie = new Class({
var self = this;
App.addEvent('movie.update.'+self.data.id, function(notification){
- self.busy(false)
+ self.busy(false);
+ self.removeView();
self.update.delay(2000, self, notification);
});
@@ -107,6 +108,7 @@ var Movie = new Class({
self.data = notification.data;
self.el.empty();
+ self.removeView();
self.profile = Quality.getProfile(self.data.profile_id) || {};
self.create();
@@ -139,9 +141,6 @@ var Movie = new Class({
'text': self.data.library.year || 'n/a'
})
),
- self.rating = new Element('div.rating.icon', {
- 'text': self.data.library.rating
- }),
self.description = new Element('div.description', {
'text': self.data.library.plot
}),
@@ -149,8 +148,8 @@ var Movie = new Class({
'events': {
'click': function(e){
var releases = self.el.getElement('.actions .releases');
- if(releases)
- releases.fireEvent('click', [e])
+ if(releases.isVisible())
+ releases.fireEvent('click', [e])
}
}
})
@@ -199,9 +198,6 @@ var Movie = new Class({
self.actions.adopt(action)
});
- if(!self.data.library.rating)
- self.rating.hide();
-
},
addQuality: function(quality_id){
@@ -244,10 +240,10 @@ var Movie = new Class({
if(direction == 'in'){
self.temp_view = self.view;
- self.changeView('details')
+ self.changeView('details');
self.el.addEvent('outerClick', function(){
- self.removeView()
+ self.removeView();
self.slide('out')
})
el.show();
diff --git a/couchpotato/core/plugins/movie/static/search.css b/couchpotato/core/plugins/movie/static/search.css
index fb0cadd2..82fd13cc 100644
--- a/couchpotato/core/plugins/movie/static/search.css
+++ b/couchpotato/core/plugins/movie/static/search.css
@@ -44,9 +44,8 @@
.search_form .input input {
border-radius: 0;
display: block;
- width: 100%;
border: 0;
- background: rgba(255,255,255,.08);
+ background: none;
color: #FFF;
font-size: 25px;
height: 100%;
@@ -106,7 +105,7 @@
background: #5c697b;
margin: 4px 0 0;
width: 470px;
- min-height: 140px;
+ min-height: 50px;
box-shadow: 0 20px 20px -10px rgba(0,0,0,0.55);
display: none;
}
diff --git a/couchpotato/core/plugins/movie/static/search.js b/couchpotato/core/plugins/movie/static/search.js
index c61b1624..8fa4fb0c 100644
--- a/couchpotato/core/plugins/movie/static/search.js
+++ b/couchpotato/core/plugins/movie/static/search.js
@@ -98,6 +98,9 @@ Block.Search = new Class({
self.el[self.q() ? 'addClass' : 'removeClass']('filled')
if(self.q() != self.last_q){
+ if(self.api_request && self.api_request.isRunning())
+ self.api_request.cancel();
+
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
self.autocomplete_timer = self.autocomplete.delay(300, self)
}
@@ -116,12 +119,9 @@ Block.Search = new Class({
},
list: function(){
- var self = this;
-
- if(self.api_request && self.api_request.running) return
-
- var q = self.q();
- var cache = self.cache[q];
+ var self = this,
+ q = self.q(),
+ cache = self.cache[q];
self.hideResults(false);
@@ -164,9 +164,6 @@ Block.Search = new Class({
});
- if(q != self.q())
- self.list()
-
// Calculate result heights
var w = window.getSize(),
rc = self.result_container.getCoordinates();
diff --git a/couchpotato/core/plugins/profile/main.py b/couchpotato/core/plugins/profile/main.py
index 3dd89d9f..e9259b6d 100644
--- a/couchpotato/core/plugins/profile/main.py
+++ b/couchpotato/core/plugins/profile/main.py
@@ -62,6 +62,7 @@ class ProfilePlugin(Plugin):
for profile in profiles:
temp.append(profile.to_dict(self.to_dict))
+ db.expire_all()
return temp
def save(self):
@@ -109,6 +110,7 @@ class ProfilePlugin(Plugin):
default = db.query(Profile).first()
default_dict = default.to_dict(self.to_dict)
+ db.expire_all()
return default_dict
def saveOrder(self):
@@ -151,6 +153,7 @@ class ProfilePlugin(Plugin):
except Exception, e:
message = log.error('Failed deleting Profile: %s', e)
+ db.expire_all()
return jsonified({
'success': success,
'message': message
diff --git a/couchpotato/core/plugins/profile/static/profile.css b/couchpotato/core/plugins/profile/static/profile.css
index cdb33945..d94576b9 100644
--- a/couchpotato/core/plugins/profile/static/profile.css
+++ b/couchpotato/core/plugins/profile/static/profile.css
@@ -13,11 +13,11 @@
.profile > .delete {
position: absolute;
- padding: 25px 20px;
- background-position: center;
+ padding: 16px;
right: 0;
cursor: pointer;
opacity: 0.6;
+ color: #fd5353;
}
.profile > .delete:hover {
opacity: 1;
@@ -29,6 +29,7 @@
.profile .qualities {
min-height: 80px;
+ padding-top: 0;
}
.profile .formHint {
@@ -97,11 +98,13 @@
}
.profile .types .type .delete {
- background-position: left center;
height: 20px;
width: 20px;
+ line-height: 20px;
visibility: hidden;
cursor: pointer;
+ font-size: 13px;
+ color: #fd5353;
}
.profile .types .type:hover:not(.is_empty) .delete {
diff --git a/couchpotato/core/plugins/profile/static/profile.js b/couchpotato/core/plugins/profile/static/profile.js
index 3bb44989..a93ca1bc 100644
--- a/couchpotato/core/plugins/profile/static/profile.js
+++ b/couchpotato/core/plugins/profile/static/profile.js
@@ -24,7 +24,7 @@ var Profile = new Class({
var data = self.data;
self.el = new Element('div.profile').adopt(
- self.delete_button = new Element('span.delete.icon', {
+ self.delete_button = new Element('span.delete.icon2', {
'events': {
'click': self.del.bind(self)
}
@@ -256,7 +256,7 @@ Profile.Type = new Class({
}
})
),
- new Element('span.delete.icon', {
+ new Element('span.delete.icon2', {
'events': {
'click': self.del.bind(self)
}
diff --git a/couchpotato/core/plugins/quality/main.py b/couchpotato/core/plugins/quality/main.py
index cf0b046c..22afe475 100644
--- a/couchpotato/core/plugins/quality/main.py
+++ b/couchpotato/core/plugins/quality/main.py
@@ -8,7 +8,6 @@ from couchpotato.core.logger import CPLog
from couchpotato.core.plugins.base import Plugin
from couchpotato.core.settings.model import Quality, Profile, ProfileType
from sqlalchemy.sql.expression import or_
-import os.path
import re
import time
@@ -25,7 +24,7 @@ class QualityPlugin(Plugin):
{'identifier': 'dvdr', 'size': (3000, 10000), 'label': 'DVD-R', 'alternative': [], 'allow': [], 'ext':['iso', 'img'], 'tags': ['pal', 'ntsc', 'video_ts', 'audio_ts']},
{'identifier': 'dvdrip', 'size': (600, 2400), 'label': 'DVD-Rip', 'width': 720, 'alternative': ['dvdrip'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg'], 'tags': [('dvd', 'rip'), ('dvd', 'xvid'), ('dvd', 'divx')]},
{'identifier': 'scr', 'size': (600, 1600), 'label': 'Screener', 'alternative': ['screener', 'dvdscr', 'ppvrip', 'dvdscreener'], 'allow': ['dvdr', 'dvd'], 'ext':['avi', 'mpg', 'mpeg']},
- {'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': [], 'allow': ['dvdr'], 'ext':['avi', 'mpg', 'mpeg']},
+ {'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': ['r6'], 'allow': ['dvdr'], 'ext':['avi', 'mpg', 'mpeg']},
{'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']},
{'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']},
{'identifier': 'cam', 'size': (600, 1000), 'label': 'Cam', 'alternative': ['camrip', 'hdcam'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']}
@@ -164,7 +163,6 @@ class QualityPlugin(Plugin):
if cached and extra is {}: return cached
for cur_file in files:
- size = (os.path.getsize(cur_file) / 1024 / 1024) if os.path.isfile(cur_file) else 0
words = re.split('\W+', cur_file.lower())
for quality in self.all():
@@ -188,29 +186,30 @@ class QualityPlugin(Plugin):
return self.setCache(hash, quality)
# Try again with loose testing
- quality = self.guessLoose(hash, extra = extra)
+ quality = self.guessLoose(hash, files = files, extra = extra)
if quality:
return self.setCache(hash, quality)
log.debug('Could not identify quality for: %s', files)
return None
- def guessLoose(self, hash, extra):
+ def guessLoose(self, hash, files = None, extra = None):
- for quality in self.all():
+ if extra:
+ for quality in self.all():
- # Check width resolution, range 20
- if (quality.get('width', 720) - 20) <= extra.get('resolution_width', 0) <= (quality.get('width', 720) + 20):
- log.debug('Found %s via resolution_width: %s == %s', (quality['identifier'], quality.get('width', 720), extra.get('resolution_width', 0)))
- return self.setCache(hash, quality)
+ # Check width resolution, range 20
+ if quality.get('width') and (quality.get('width') - 20) <= extra.get('resolution_width', 0) <= (quality.get('width') + 20):
+ log.debug('Found %s via resolution_width: %s == %s', (quality['identifier'], quality.get('width'), extra.get('resolution_width', 0)))
+ return self.setCache(hash, quality)
- # Check height resolution, range 20
- if (quality.get('height', 480) - 20) <= extra.get('resolution_height', 0) <= (quality.get('height', 480) + 20):
- log.debug('Found %s via resolution_height: %s == %s', (quality['identifier'], quality.get('height', 480), extra.get('resolution_height', 0)))
- return self.setCache(hash, quality)
+ # Check height resolution, range 20
+ if quality.get('height') and (quality.get('height') - 20) <= extra.get('resolution_height', 0) <= (quality.get('height') + 20):
+ log.debug('Found %s via resolution_height: %s == %s', (quality['identifier'], quality.get('height'), extra.get('resolution_height', 0)))
+ return self.setCache(hash, quality)
- if 480 <= extra.get('resolution_width', 0) <= 720:
- log.debug('Found as dvdrip')
- return self.setCache(hash, self.single('dvdrip'))
+ if 480 <= extra.get('resolution_width', 0) <= 720:
+ log.debug('Found as dvdrip')
+ return self.setCache(hash, self.single('dvdrip'))
return None
diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py
index 6618a8be..c409740a 100644
--- a/couchpotato/core/plugins/release/main.py
+++ b/couchpotato/core/plugins/release/main.py
@@ -180,7 +180,7 @@ class Release(Plugin):
provider = fireEvent('provider.belongs_to', item['url'], provider = item.get('provider'), single = True)
if item['type'] != 'torrent_magnet':
- item['download'] = provider.download
+ item['download'] = provider.loginDownload if provider.urls.get('login') else provider.download
success = fireEvent('searcher.download', data = item, movie = rel.movie.to_dict({
'profile': {'types': {'quality': {}}},
diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py
index 30a57a7b..7ab9f450 100644
--- a/couchpotato/core/plugins/renamer/main.py
+++ b/couchpotato/core/plugins/renamer/main.py
@@ -4,7 +4,7 @@ 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, link, symlink
+ getImdb, link, symlink, tryInt
from couchpotato.core.logger import CPLog
from couchpotato.core.plugins.base import Plugin
from couchpotato.core.settings.model import Library, File, Profile, Release, \
@@ -25,10 +25,10 @@ class Renamer(Plugin):
checking_snatched = False
def __init__(self):
-
addApiView('renamer.scan', self.scanView, docs = {
'desc': 'For the renamer to check for new files to rename in a folder',
'params': {
+ 'async': {'desc': 'Optional: Set to 1 if you dont want to fire the renamer.scan asynchronous.'},
'movie_folder': {'desc': 'Optional: The folder of the movie to scan. Keep empty for default renamer folder.'},
'downloader' : {'desc': 'Optional: The downloader this movie has been downloaded with'},
'download_id': {'desc': 'Optional: The downloader\'s nzb/torrent ID'},
@@ -62,11 +62,14 @@ class Renamer(Plugin):
def scanView(self):
params = getParams()
+ async = tryInt(params.get('async', None))
movie_folder = params.get('movie_folder', None)
downloader = params.get('downloader', None)
download_id = params.get('download_id', None)
- fireEventAsync('renamer.scan',
+ fire_handle = fireEvent if not async else fireEventAsync
+
+ fire_handle('renamer.scan',
movie_folder = movie_folder,
download_info = {'id': download_id, 'downloader': downloader} if download_id else None
)
@@ -204,6 +207,7 @@ class Renamer(Plugin):
cd = 1 if multiple else 0
for current_file in sorted(list(group['files'][file_type])):
+ current_file = toUnicode(current_file)
# Original filename
replacements['original'] = os.path.splitext(os.path.basename(current_file))[0]
diff --git a/couchpotato/core/plugins/scanner/main.py b/couchpotato/core/plugins/scanner/main.py
index 79fe0018..56e57158 100644
--- a/couchpotato/core/plugins/scanner/main.py
+++ b/couchpotato/core/plugins/scanner/main.py
@@ -336,7 +336,7 @@ class Scanner(Plugin):
break
if return_ignored is False and identifier in ignored_identifiers:
- log.debug('Ignore file found, ignoring release: %s' % identifier)
+ log.debug('Ignore file found, ignoring release: %s', identifier)
continue
# Group extra (and easy) files first
@@ -385,6 +385,8 @@ class Scanner(Plugin):
for file_type in group['files']:
if not file_type is 'leftover':
group['files']['leftover'] -= set(group['files'][file_type])
+ group['files'][file_type] = list(group['files'][file_type])
+ group['files']['leftover'] = list(group['files']['leftover'])
# Delete the unsorted list
del group['unsorted_files']
@@ -438,11 +440,14 @@ class Scanner(Plugin):
if data.get('audio'): break
# Use the quality guess first, if that failes use the quality we wanted to download
- data['quality'] = fireEvent('quality.guess', files = files, extra = data, single = True)
+ data['quality'] = None
+ if download_info and download_info.get('quality'):
+ data['quality'] = fireEvent('quality.single', download_info.get('quality'), single = True)
+
if not data['quality']:
- if download_info and download_info.get('quality'):
- data['quality'] = fireEvent('quality.single', download_info.get('quality'), single = True)
- else:
+ data['quality'] = fireEvent('quality.guess', files = files, extra = data, single = True)
+
+ if not data['quality']:
data['quality'] = fireEvent('quality.single', 'dvdr' if group['is_dvd'] else 'dvdrip', single = True)
data['quality_type'] = 'HD' if data.get('resolution_width', 0) >= 1280 or data['quality'].get('hd') else 'SD'
diff --git a/couchpotato/core/plugins/searcher/main.py b/couchpotato/core/plugins/searcher/main.py
index dba0ea7e..23e8ea58 100644
--- a/couchpotato/core/plugins/searcher/main.py
+++ b/couchpotato/core/plugins/searcher/main.py
@@ -392,7 +392,7 @@ class Searcher(Plugin):
req_match += len(list(set(nzb_words) & set(req))) == len(req)
if self.conf('required_words') and req_match == 0:
- log.info2("Wrong: Required word missing: %s" % nzb['name'])
+ log.info2('Wrong: Required word missing: %s', nzb['name'])
return False
# Ignore releases
@@ -403,7 +403,7 @@ class Searcher(Plugin):
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']))
+ log.info2("Wrong: '%s' contains 'ignored words'", (nzb['name']))
return False
# Ignore porn stuff
@@ -462,7 +462,7 @@ class Searcher(Plugin):
if len(movie_words) <= 2 and self.correctYear([nzb['name']], movie['library']['year'], 0):
return True
- log.info("Wrong: %s, undetermined naming. Looking for '%s (%s)'" % (nzb['name'], movie_name, movie['library']['year']))
+ log.info("Wrong: %s, undetermined naming. Looking for '%s (%s)'", (nzb['name'], movie_name, movie['library']['year']))
return False
def containsOtherQuality(self, nzb, movie_year = None, preferred_quality = {}):
diff --git a/couchpotato/core/plugins/subtitle/main.py b/couchpotato/core/plugins/subtitle/main.py
index 73ead087..c6bef6ae 100644
--- a/couchpotato/core/plugins/subtitle/main.py
+++ b/couchpotato/core/plugins/subtitle/main.py
@@ -59,7 +59,7 @@ class Subtitle(Plugin):
for d_sub in downloaded:
log.info('Found subtitle (%s): %s', (d_sub.language.alpha2, files))
- group['files']['subtitle'].add(d_sub.path)
+ group['files']['subtitle'].append(d_sub.path)
group['subtitle_language'][d_sub.path] = [d_sub.language.alpha2]
return True
diff --git a/couchpotato/core/providers/automation/base.py b/couchpotato/core/providers/automation/base.py
index 6aef58a1..4655c1ad 100644
--- a/couchpotato/core/providers/automation/base.py
+++ b/couchpotato/core/providers/automation/base.py
@@ -11,6 +11,7 @@ log = CPLog(__name__)
class Automation(Provider):
enabled_option = 'automation_enabled'
+ http_time_between_calls = 2
interval = 86400
last_checked = 0
@@ -73,13 +74,13 @@ class Automation(Provider):
req_match += len(list(set(movie_genres) & set(req))) == len(req)
if self.getMinimal('required_genres') and req_match == 0:
- log.info2("Required genre(s) missing for %s" % movie['original_title'])
+ log.info2('Required genre(s) missing for %s', movie['original_title'])
return False
for ign_set in ignored_genres:
ign = splitString(ign_set, '&')
if len(list(set(movie_genres) & set(ign))) == len(ign):
- log.info2("%s has blacklisted genre(s): %s" % (movie['original_title'], ign))
+ log.info2('%s has blacklisted genre(s): %s', (movie['original_title'], ign))
return False
return True
diff --git a/couchpotato/core/providers/automation/goodfilms/main.py b/couchpotato/core/providers/automation/goodfilms/main.py
index dd4b1aef..266a284c 100644
--- a/couchpotato/core/providers/automation/goodfilms/main.py
+++ b/couchpotato/core/providers/automation/goodfilms/main.py
@@ -1,13 +1,13 @@
+from bs4 import BeautifulSoup
from couchpotato.core.logger import CPLog
from couchpotato.core.providers.automation.base import Automation
-from bs4 import BeautifulSoup
log = CPLog(__name__)
class Goodfilms(Automation):
- url = 'http://goodfil.ms/%s/queue'
+ url = 'http://goodfil.ms/%s/queue?page=%d&without_layout=1'
def getIMDBids(self):
@@ -25,12 +25,25 @@ class Goodfilms(Automation):
def getWatchlist(self):
- url = self.url % self.conf('automation_username')
- soup = BeautifulSoup(self.getHTMLData(url))
-
movies = []
+ page = 1
- for movie in soup.find_all('div', attrs = { 'class': 'movie', 'data-film-title': True }):
- movies.append({ 'title': movie['data-film-title'], 'year': movie['data-film-year'] })
+ while True:
+ url = self.url % (self.conf('automation_username'), page)
+ data = self.getHTMLData(url)
+ soup = BeautifulSoup(data)
+
+ this_watch_list = soup.find_all('div', attrs = { 'class': 'movie', 'data-film-title': True })
+
+ if not this_watch_list: # No Movies
+ break
+
+ for movie in this_watch_list:
+ movies.append({ 'title': movie['data-film-title'], 'year': movie['data-film-year'] })
+
+ if not 'next page' in data.lower():
+ break
+
+ page += 1
return movies
diff --git a/couchpotato/core/providers/automation/rottentomatoes/main.py b/couchpotato/core/providers/automation/rottentomatoes/main.py
index 053b79e8..b4482023 100644
--- a/couchpotato/core/providers/automation/rottentomatoes/main.py
+++ b/couchpotato/core/providers/automation/rottentomatoes/main.py
@@ -35,10 +35,10 @@ class Rottentomatoes(Automation, RSS):
name = result.group(0)
if rating < tryInt(self.conf('tomatometer_percent')):
- log.info2('%s seems to be rotten...' % name)
+ log.info2('%s seems to be rotten...', name)
else:
- log.info2('Found %s fresh enough movies, enqueuing: %s' % (rating, name))
+ log.info2('Found %s fresh enough movies, enqueuing: %s', (rating, name))
year = datetime.datetime.now().strftime("%Y")
imdb = self.search(name, year)
diff --git a/couchpotato/core/providers/base.py b/couchpotato/core/providers/base.py
index 1f0df964..e3709aa9 100644
--- a/couchpotato/core/providers/base.py
+++ b/couchpotato/core/providers/base.py
@@ -62,7 +62,7 @@ class Provider(Plugin):
cache_key = '%s%s' % (md5(url), md5('%s' % kwargs.get('params', {})))
data = self.getCache(cache_key, url, **kwargs)
- if data:
+ if data and len(data) > 0:
try:
data = XMLTree.fromstring(data)
return self.getElements(data, item_path)
@@ -104,7 +104,7 @@ class YarrProvider(Provider):
try:
cookiejar = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
- opener.addheaders = []
+ opener.addheaders = [('User-Agent', self.user_agent)]
urllib2.install_opener(opener)
log.info2('Logging into %s', self.urls['login'])
f = opener.open(self.urls['login'], self.getLoginParams())
@@ -114,9 +114,12 @@ class YarrProvider(Provider):
if self.loginSuccess(output):
self.login_opener = opener
return True
- except:
- log.error('Failed to login %s: %s', (self.getName(), traceback.format_exc()))
+ error = 'unknown'
+ except:
+ error = traceback.format_exc()
+
+ log.error('Failed to login %s: %s', (self.getName(), error))
return False
def loginSuccess(self, output):
@@ -179,7 +182,7 @@ class YarrProvider(Provider):
if hostname in download_url:
return self
except:
- log.debug('Url % s doesn\'t belong to %s', (url, self.getName()))
+ log.debug('Url %s doesn\'t belong to %s', (url, self.getName()))
return
diff --git a/couchpotato/core/providers/movie/_modifier/main.py b/couchpotato/core/providers/movie/_modifier/main.py
index 12d1e325..148d9035 100644
--- a/couchpotato/core/providers/movie/_modifier/main.py
+++ b/couchpotato/core/providers/movie/_modifier/main.py
@@ -4,6 +4,7 @@ from couchpotato.core.helpers.variable import mergeDicts, randomString
from couchpotato.core.logger import CPLog
from couchpotato.core.plugins.base import Plugin
from couchpotato.core.settings.model import Library
+import copy
import traceback
log = CPLog(__name__)
@@ -11,6 +12,25 @@ log = CPLog(__name__)
class MovieResultModifier(Plugin):
+ default_info = {
+ 'tmdb_id': 0,
+ 'titles': [],
+ 'original_title': '',
+ 'year': 0,
+ 'images': {
+ 'poster': [],
+ 'backdrop': [],
+ 'poster_original': [],
+ 'backdrop_original': []
+ },
+ 'runtime': 0,
+ 'plot': '',
+ 'tagline': '',
+ 'imdb': '',
+ 'genres': [],
+ 'release_date': {}
+ }
+
def __init__(self):
addEvent('result.modify.movie.search', self.combineOnIMDB)
addEvent('result.modify.movie.info', self.checkLibrary)
@@ -67,6 +87,9 @@ class MovieResultModifier(Plugin):
return temp
def checkLibrary(self, result):
+
+ result = mergeDicts(copy.deepcopy(self.default_info), copy.deepcopy(result))
+
if result and result.get('imdb'):
return mergeDicts(result, self.getLibraryTags(result['imdb']))
return result
diff --git a/couchpotato/core/providers/movie/couchpotatoapi/main.py b/couchpotato/core/providers/movie/couchpotatoapi/main.py
index 2beb7e28..4aba28ca 100644
--- a/couchpotato/core/providers/movie/couchpotatoapi/main.py
+++ b/couchpotato/core/providers/movie/couchpotatoapi/main.py
@@ -2,7 +2,6 @@ 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
@@ -15,13 +14,13 @@ log = CPLog(__name__)
class CouchPotatoApi(MovieProvider):
urls = {
- 'search': 'https://couchpota.to/api/search/%s/',
- 'info': 'https://couchpota.to/api/info/%s/',
- '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',
+ 'search': 'https://api.couchpota.to/search/%s/',
+ 'info': 'https://api.couchpota.to/info/%s/',
+ 'is_movie': 'https://api.couchpota.to/ismovie/%s/',
+ 'eta': 'https://api.couchpota.to/eta/%s/',
+ 'suggest': 'https://api.couchpota.to/suggest/',
+ 'updater': 'https://api.couchpota.to/updater/?%s',
+ 'messages': 'https://api.couchpota.to/messages/?%s',
}
http_time_between_calls = 0
api_version = 1
@@ -71,7 +70,8 @@ class CouchPotatoApi(MovieProvider):
return
result = self.getJsonData(self.urls['info'] % identifier, headers = self.getRequestHeaders())
- if result: return result
+ if result:
+ return dict((k, v) for k, v in result.iteritems() if v)
return {}
diff --git a/couchpotato/core/providers/movie/omdbapi/main.py b/couchpotato/core/providers/movie/omdbapi/main.py
index cdfece0a..89990747 100644
--- a/couchpotato/core/providers/movie/omdbapi/main.py
+++ b/couchpotato/core/providers/movie/omdbapi/main.py
@@ -86,7 +86,7 @@ class OMDBAPI(MovieProvider):
movie_data = {
'via_imdb': True,
'titles': [movie.get('Title')] if movie.get('Title') else [],
- 'original_title': movie.get('Title', ''),
+ 'original_title': movie.get('Title'),
'images': {
'poster': [movie.get('Poster', '')] if movie.get('Poster') and len(movie.get('Poster', '')) > 4 else [],
},
@@ -96,14 +96,15 @@ class OMDBAPI(MovieProvider):
},
'imdb': str(movie.get('imdbID', '')),
'runtime': self.runtimeToMinutes(movie.get('Runtime', '')),
- 'released': movie.get('Released', ''),
+ 'released': movie.get('Released'),
'year': year if isinstance(year, (int)) else None,
- 'plot': movie.get('Plot', ''),
+ 'plot': movie.get('Plot'),
'genres': splitString(movie.get('Genre', '')),
'directors': splitString(movie.get('Director', '')),
'writers': splitString(movie.get('Writer', '')),
'actors': splitString(movie.get('Actors', '')),
}
+ movie_data = dict((k, v) for k, v in movie_data.iteritems() if v)
except:
log.error('Failed parsing IMDB API json: %s', traceback.format_exc())
diff --git a/couchpotato/core/providers/movie/themoviedb/main.py b/couchpotato/core/providers/movie/themoviedb/main.py
index 0554a901..f5fb8ef4 100644
--- a/couchpotato/core/providers/movie/themoviedb/main.py
+++ b/couchpotato/core/providers/movie/themoviedb/main.py
@@ -37,7 +37,7 @@ class TheMovieDb(MovieProvider):
if raw:
try:
results = self.parseMovie(raw)
- log.info('Found: %s', results['titles'][0] + ' (' + str(results['year']) + ')')
+ log.info('Found: %s', results['titles'][0] + ' (' + str(results.get('year', 0)) + ')')
self.setCache(cache_key, results)
return results
@@ -81,7 +81,7 @@ class TheMovieDb(MovieProvider):
if nr == limit:
break
- log.info('Found: %s', [result['titles'][0] + ' (' + str(result['year']) + ')' for result in results])
+ log.info('Found: %s', [result['titles'][0] + ' (' + str(result.get('year', 0)) + ')' for result in results])
self.setCache(cache_key, results)
return results
@@ -170,11 +170,12 @@ class TheMovieDb(MovieProvider):
'runtime': movie.get('runtime'),
'released': movie.get('released'),
'year': year,
- 'plot': movie.get('overview', ''),
- 'tagline': '',
+ 'plot': movie.get('overview'),
'genres': genres,
}
+ movie_data = dict((k, v) for k, v in movie_data.iteritems() if v)
+
# Add alternative names
for alt in ['original_name', 'alternative_name']:
alt_name = toUnicode(movie.get(alt))
diff --git a/couchpotato/core/providers/nzb/ftdworld/main.py b/couchpotato/core/providers/nzb/ftdworld/main.py
index bd2a3ee2..caecadad 100644
--- a/couchpotato/core/providers/nzb/ftdworld/main.py
+++ b/couchpotato/core/providers/nzb/ftdworld/main.py
@@ -3,7 +3,6 @@ from couchpotato.core.helpers.variable import tryInt
from couchpotato.core.logger import CPLog
from couchpotato.core.providers.nzb.base import NZBProvider
from couchpotato.environment import Env
-from dateutil.parser import parse
import json
import traceback
diff --git a/couchpotato/core/providers/torrent/hdbits/__init__.py b/couchpotato/core/providers/torrent/hdbits/__init__.py
new file mode 100644
index 00000000..8a9fc80e
--- /dev/null
+++ b/couchpotato/core/providers/torrent/hdbits/__init__.py
@@ -0,0 +1,45 @@
+from .main import HDBits
+
+def start():
+ return HDBits()
+
+config = [{
+ 'name': 'hdbits',
+ 'groups': [
+ {
+ 'tab': 'searcher',
+ 'subtab': 'providers',
+ 'list': 'torrent_providers',
+ 'name': 'HDBits',
+ 'description': 'See HDBits',
+ 'options': [
+ {
+ 'name': 'enabled',
+ 'type': 'enabler',
+ 'default': False,
+ },
+ {
+ 'name': 'username',
+ 'default': '',
+ },
+ {
+ 'name': 'password',
+ 'default': '',
+ 'type': 'password',
+ },
+ {
+ 'name': 'passkey',
+ 'default': '',
+ },
+ {
+ 'name': 'extra_score',
+ 'advanced': True,
+ 'label': 'Extra Score',
+ 'type': 'int',
+ 'default': 0,
+ 'description': 'Starting score for each release found via this provider.',
+ },
+ ],
+ },
+ ],
+}]
diff --git a/couchpotato/core/providers/torrent/hdbits/main.py b/couchpotato/core/providers/torrent/hdbits/main.py
new file mode 100644
index 00000000..218c3691
--- /dev/null
+++ b/couchpotato/core/providers/torrent/hdbits/main.py
@@ -0,0 +1,55 @@
+from bs4 import BeautifulSoup
+from couchpotato.core.helpers.encoding import tryUrlencode
+from couchpotato.core.helpers.variable import tryInt
+from couchpotato.core.logger import CPLog
+from couchpotato.core.providers.torrent.base import TorrentProvider
+import traceback
+
+log = CPLog(__name__)
+
+
+class HDBits(TorrentProvider):
+
+ urls = {
+ 'test' : 'https://hdbits.org/',
+ 'login' : 'https://hdbits.org/login/doLogin/',
+ 'detail' : 'https://hdbits.org/details.php?id=%s&source=browse',
+ 'search' : 'https://hdbits.org/json_search.php?imdb=%s',
+ 'download' : 'https://hdbits.org/download.php/%s.torrent?id=%s&passkey=%s&source=details.browse',
+ }
+
+ http_time_between_calls = 1 #seconds
+
+ def _search(self, movie, quality, results):
+
+ data = self.getJsonData(self.urls['search'] % movie['library']['identifier'], opener = self.login_opener)
+
+ if data:
+ try:
+ for result in data:
+ results.append({
+ 'id': result['id'],
+ 'name': result['title'],
+ 'url': self.urls['download'] % (result['title'], result['id'], self.conf('passkey')),
+ 'detail_url': self.urls['detail'] % result['id'],
+ 'size': self.parseSize(result['size']),
+ 'seeders': tryInt(result['seeder']),
+ 'leechers': tryInt(result['leecher'])
+ })
+
+ except:
+ log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc()))
+
+ def getLoginParams(self):
+ data = self.getHTMLData('https://hdbits.org/login')
+ bs = BeautifulSoup(data)
+ secret = bs.find('input', attrs = {'name': 'lol'})['value']
+
+ return tryUrlencode({
+ 'uname': self.conf('username'),
+ 'password': self.conf('password'),
+ 'lol': secret
+ })
+
+ def loginSuccess(self, output):
+ return '/logout.php' in output.lower()
diff --git a/couchpotato/core/providers/torrent/passthepopcorn/__init__.py b/couchpotato/core/providers/torrent/passthepopcorn/__init__.py
index a9f687c3..a535034a 100644
--- a/couchpotato/core/providers/torrent/passthepopcorn/__init__.py
+++ b/couchpotato/core/providers/torrent/passthepopcorn/__init__.py
@@ -38,6 +38,30 @@ config = [{
'name': 'passkey',
'default': '',
},
+ {
+ 'name': 'prefer_golden',
+ 'advanced': True,
+ 'type': 'bool',
+ 'label': 'Prefer golden',
+ 'default': 1,
+ 'description': 'Favors Golden Popcorn-releases over all other releases.'
+ },
+ {
+ 'name': 'prefer_scene',
+ 'advanced': True,
+ 'type': 'bool',
+ 'label': 'Prefer scene',
+ 'default': 0,
+ 'description': 'Favors scene-releases over non-scene releases.'
+ },
+ {
+ 'name': 'require_approval',
+ 'advanced': True,
+ 'type': 'bool',
+ 'label': 'Require approval',
+ 'default': 0,
+ 'description': 'Require staff-approval for releases to be accepted.'
+ },
{
'name': 'extra_score',
'advanced': True,
diff --git a/couchpotato/core/providers/torrent/passthepopcorn/main.py b/couchpotato/core/providers/torrent/passthepopcorn/main.py
index 9abe51fb..a4bb0429 100644
--- a/couchpotato/core/providers/torrent/passthepopcorn/main.py
+++ b/couchpotato/core/providers/torrent/passthepopcorn/main.py
@@ -58,7 +58,7 @@ class PassThePopcorn(TorrentProvider):
class PTPHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
def http_error_302(self, req, fp, code, msg, headers):
- log.debug("302 detected; redirected to %s" % headers['Location'])
+ log.debug("302 detected; redirected to %s", headers['Location'])
if (headers['Location'] != 'login.php'):
return urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
else:
@@ -84,7 +84,7 @@ class PassThePopcorn(TorrentProvider):
txt = self.urlopen(url, opener = self.login_opener)
res = json.loads(txt)
except:
- log.error('Search on PassThePopcorn.me (%s) failed (could not decode JSON)' % params)
+ log.error('Search on PassThePopcorn.me (%s) failed (could not decode JSON)', params)
return
try:
@@ -96,18 +96,23 @@ class PassThePopcorn(TorrentProvider):
for ptpmovie in res['Movies']:
if not 'Torrents' in ptpmovie:
- log.debug('Movie %s (%s) has NO torrents' % (ptpmovie['Title'], ptpmovie['Year']))
+ log.debug('Movie %s (%s) has NO torrents', (ptpmovie['Title'], ptpmovie['Year']))
continue
- log.debug('Movie %s (%s) has %d torrents' % (ptpmovie['Title'], ptpmovie['Year'], len(ptpmovie['Torrents'])))
+ log.debug('Movie %s (%s) has %d torrents', (ptpmovie['Title'], ptpmovie['Year'], len(ptpmovie['Torrents'])))
for torrent in ptpmovie['Torrents']:
torrent_id = tryInt(torrent['Id'])
torrentdesc = '%s %s %s' % (torrent['Resolution'], torrent['Source'], torrent['Codec'])
+ torrentscore = 0
if 'GoldenPopcorn' in torrent and torrent['GoldenPopcorn']:
torrentdesc += ' HQ'
+ if self.conf('prefer_golden'):
+ torrentscore += 200
if 'Scene' in torrent and torrent['Scene']:
torrentdesc += ' Scene'
+ if self.conf('prefer_scene'):
+ torrentscore += 50
if 'RemasterTitle' in torrent and torrent['RemasterTitle']:
torrentdesc += self.htmlToASCII(' %s' % torrent['RemasterTitle'])
@@ -115,18 +120,21 @@ class PassThePopcorn(TorrentProvider):
torrent_name = re.sub('[^A-Za-z0-9\-_ \(\).]+', '', '%s (%s) - %s' % (movie_title, ptpmovie['Year'], torrentdesc))
def extra_check(item):
- return self.torrentMeetsQualitySpec(item, type)
+ return self.torrentMeetsQualitySpec(item, quality_id)
results.append({
'id': torrent_id,
'name': torrent_name,
+ 'Source': torrent['Source'],
+ 'Checked': 'true' if torrent['Checked'] else 'false',
+ 'Resolution': torrent['Resolution'],
'url': '%s?action=download&id=%d&authkey=%s&torrent_pass=%s' % (self.urls['torrent'], torrent_id, authkey, passkey),
'detail_url': self.urls['detail'] % torrent_id,
'date': tryInt(time.mktime(parse(torrent['UploadTime']).timetuple())),
'size': tryInt(torrent['Size']) / (1024 * 1024),
'seeders': tryInt(torrent['Seeders']),
'leechers': tryInt(torrent['Leechers']),
- 'score': 50 if torrent['GoldenPopcorn'] else 0,
+ 'score': torrentscore,
'extra_check': extra_check,
'download': self.loginDownload,
})
@@ -151,7 +159,7 @@ class PassThePopcorn(TorrentProvider):
try:
response = opener.open(self.urls['login'], self.getLoginParams())
except urllib2.URLError as e:
- log.error('Login to PassThePopcorn failed: %s' % e)
+ log.error('Login to PassThePopcorn failed: %s', e)
return False
if response.getcode() == 200:
@@ -159,7 +167,7 @@ class PassThePopcorn(TorrentProvider):
self.login_opener = opener
return True
else:
- log.error('Login to PassThePopcorn failed: returned code %d' % response.getcode())
+ log.error('Login to PassThePopcorn failed: returned code %d', response.getcode())
return False
def torrentMeetsQualitySpec(self, torrent, quality):
@@ -167,12 +175,18 @@ class PassThePopcorn(TorrentProvider):
if not quality in self.post_search_filters:
return True
- for field, specs in self.post_search_filters[quality].items():
+ reqs = self.post_search_filters[quality].copy()
+
+ if self.conf('require_approval'):
+ log.debug('Config: Require staff-approval activated')
+ reqs['Checked'] = ['true']
+
+ for field, specs in reqs.items():
matches_one = False
seen_one = False
if not field in torrent:
- log.debug('Torrent with ID %s has no field "%s"; cannot apply post-search-filter for quality "%s"' % (torrent['Id'], field, quality))
+ log.debug('Torrent with ID %s has no field "%s"; cannot apply post-search-filter for quality "%s"', (torrent['Id'], field, quality))
continue
for spec in specs:
@@ -182,11 +196,14 @@ class PassThePopcorn(TorrentProvider):
return False
else:
# a positive rule; if any of the possible positive values match the field, return True
+ log.debug('Checking if torrents field %s equals %s' % (field, spec))
seen_one = True
if torrent[field] == spec:
+ log.debug('Torrent satisfied %s == %s' % (field, spec))
matches_one = True
if seen_one and not matches_one:
+ log.debug('Torrent did not satisfy requirements, ignoring')
return False
return True
diff --git a/couchpotato/core/providers/torrent/thepiratebay/main.py b/couchpotato/core/providers/torrent/thepiratebay/main.py
index 1e47a710..10608157 100644
--- a/couchpotato/core/providers/torrent/thepiratebay/main.py
+++ b/couchpotato/core/providers/torrent/thepiratebay/main.py
@@ -35,8 +35,11 @@ class ThePirateBay(TorrentMagnetProvider):
'https://piratereverse.info',
'https://tpb.pirateparty.org.uk',
'https://argumentomteemigreren.nl',
- 'https://livepirate.com/',
- 'https://www.getpirate.com/',
+ 'https://livepirate.com',
+ 'https://www.getpirate.com',
+ 'https://tpb.partipirate.org',
+ 'https://tpb.piraten.lu',
+ 'https://kuiken.co',
]
def __init__(self):
@@ -50,7 +53,7 @@ class ThePirateBay(TorrentMagnetProvider):
while page < total_pages:
- search_url = self.urls['search'] % (self.getDomain(), tryUrlencode('"%s %s"' % (title, movie['library']['year'])), page, self.getCatId(quality['identifier'])[0])
+ search_url = self.urls['search'] % (self.getDomain(), tryUrlencode('"%s" %s' % (title, movie['library']['year'])), page, self.getCatId(quality['identifier'])[0])
page += 1
data = self.getHTMLData(search_url)
diff --git a/couchpotato/core/providers/torrent/torrentshack/__init__.py b/couchpotato/core/providers/torrent/torrentshack/__init__.py
new file mode 100644
index 00000000..203e0996
--- /dev/null
+++ b/couchpotato/core/providers/torrent/torrentshack/__init__.py
@@ -0,0 +1,47 @@
+from .main import TorrentShack
+
+def start():
+ return TorrentShack()
+
+config = [{
+ 'name': 'torrentshack',
+ 'groups': [
+ {
+ 'tab': 'searcher',
+ 'subtab': 'providers',
+ 'list': 'torrent_providers',
+ 'name': 'TorrentShack',
+ 'description': 'See TorrentShack',
+ 'options': [
+ {
+ 'name': 'enabled',
+ 'type': 'enabler',
+ 'default': False,
+ },
+ {
+ 'name': 'username',
+ 'default': '',
+ },
+ {
+ 'name': 'password',
+ 'default': '',
+ 'type': 'password',
+ },
+ {
+ 'name': 'scene_only',
+ 'type': 'bool',
+ 'default': False,
+ 'description': 'Only allow scene releases.'
+ },
+ {
+ 'name': 'extra_score',
+ 'advanced': True,
+ 'label': 'Extra Score',
+ 'type': 'int',
+ 'default': 0,
+ 'description': 'Starting score for each release found via this provider.',
+ }
+ ],
+ },
+ ],
+}]
diff --git a/couchpotato/core/providers/torrent/torrentshack/main.py b/couchpotato/core/providers/torrent/torrentshack/main.py
new file mode 100644
index 00000000..df2ea291
--- /dev/null
+++ b/couchpotato/core/providers/torrent/torrentshack/main.py
@@ -0,0 +1,81 @@
+from bs4 import BeautifulSoup
+from couchpotato.core.helpers.encoding import tryUrlencode
+from couchpotato.core.helpers.variable import tryInt
+from couchpotato.core.logger import CPLog
+from couchpotato.core.providers.torrent.base import TorrentProvider
+import traceback
+
+log = CPLog(__name__)
+
+
+class TorrentShack(TorrentProvider):
+
+ urls = {
+ 'test' : 'http://www.torrentshack.net/',
+ 'login' : 'http://www.torrentshack.net/login.php',
+ 'detail' : 'http://www.torrentshack.net/torrent/%s',
+ 'search' : 'http://www.torrentshack.net/torrents.php?searchstr=%s&filter_cat[%d]=1',
+ 'download' : 'http://www.torrentshack.net/%s',
+ }
+
+ cat_ids = [
+ ([970], ['bd50']),
+ ([300], ['720p', '1080p']),
+ ([350], ['dvdr']),
+ ([400], ['brrip', 'dvdrip']),
+ ]
+
+ http_time_between_calls = 1 #seconds
+ cat_backup_id = None
+
+ def _searchOnTitle(self, title, movie, quality, results):
+
+ url = self.urls['search'] % (tryUrlencode('"%s" %s' % (title.replace(':', ''), movie['library']['year'])), self.getCatId(quality['identifier'])[0])
+ data = self.getHTMLData(url, opener = self.login_opener)
+
+ if data:
+ html = BeautifulSoup(data)
+
+ try:
+ result_table = html.find('table', attrs = {'id' : 'torrent_table'})
+ if not result_table:
+ return
+
+ entries = result_table.find_all('tr', attrs = {'class' : 'torrent'})
+
+ for result in entries:
+
+ link = result.find('span', attrs = {'class' : 'torrent_name_link'}).parent
+ url = result.find('td', attrs = {'class' : 'torrent_td'}).find('a')
+
+ extra_info = ''
+ if result.find('span', attrs = {'class' : 'torrent_extra_info'}):
+ extra_info = result.find('span', attrs = {'class' : 'torrent_extra_info'}).text
+
+ if not self.conf('scene_only') or extra_info != '[NotScene]':
+ results.append({
+ 'id': link['href'].replace('torrents.php?torrentid=', ''),
+ 'name': unicode(link.span.string).translate({ord(u'\xad'): None}),
+ 'url': self.urls['download'] % url['href'],
+ 'detail_url': self.urls['download'] % link['href'],
+ 'download': self.loginDownload,
+ 'size': self.parseSize(result.find_all('td')[4].string),
+ 'seeders': tryInt(result.find_all('td')[6].string),
+ 'leechers': tryInt(result.find_all('td')[7].string),
+ })
+ else:
+ log.info('Not adding release %s [NotScene]' % unicode(link.span.string).translate({ord(u'\xad'): None}))
+
+ except:
+ log.error('Failed to parsing %s: %s', (self.getName(), traceback.format_exc()))
+
+ def getLoginParams(self):
+ return tryUrlencode({
+ 'username': self.conf('username'),
+ 'password': self.conf('password'),
+ 'keeplogged': '1',
+ 'login': 'Login',
+ })
+
+ def loginSuccess(self, output):
+ return 'logout.php' in output.lower()
diff --git a/couchpotato/core/providers/userscript/filmweb/main.py b/couchpotato/core/providers/userscript/filmweb/main.py
index 5e6adbe1..66c5fcdb 100644
--- a/couchpotato/core/providers/userscript/filmweb/main.py
+++ b/couchpotato/core/providers/userscript/filmweb/main.py
@@ -4,7 +4,9 @@ import re
class Filmweb(UserscriptBase):
- includes = ['http://www.filmweb.pl/*']
+ version = 2
+
+ includes = ['http://www.filmweb.pl/film/*']
def getMovie(self, url):
diff --git a/couchpotato/static/scripts/block/menu.js b/couchpotato/static/scripts/block/menu.js
index cdd7875b..8d315f59 100644
--- a/couchpotato/static/scripts/block/menu.js
+++ b/couchpotato/static/scripts/block/menu.js
@@ -15,19 +15,21 @@ Block.Menu = new Class({
self.wrapper = new Element('div.wrapper').adopt(
self.more_option_ul = new Element('ul')
),
- new Element('a.button.onlay' + (self.options.button_class ? '.' + self.options.button_class : ''), {
+ self.button = new Element('a.button' + (self.options.button_class ? '.' + self.options.button_class : ''), {
'events': {
'click': function(){
self.el.toggleClass('show')
self.fireEvent(self.el.hasClass('show') ? 'open' : 'close')
- if(self.el.hasClass('show'))
- this.addEvent('outerClick', function(){
- self.el.removeClass('show')
- this.removeEvents('outerClick');
+ if(self.el.hasClass('show')){
+ self.el.addEvent('outerClick', self.removeOuterClick.bind(self))
+ this.addEvent('outerClick', function(e){
+ if(e.target.get('tag') != 'input')
+ self.removeOuterClick()
})
+ }
else
- this.removeEvents('outerClick');
+ self.removeOuterClick()
}
}
@@ -36,6 +38,15 @@ Block.Menu = new Class({
},
+ removeOuterClick: function(){
+ var self = this;
+
+ self.el.removeClass('show')
+ self.el.removeEvents('outerClick');
+
+ self.button.removeEvents('outerClick');
+ },
+
addLink: function(tab, position){
var self = this;
var el = new Element('li').adopt(tab).inject(self.more_option_ul, position || 'bottom');
diff --git a/couchpotato/static/scripts/block/navigation.js b/couchpotato/static/scripts/block/navigation.js
index feda3795..8389ff9f 100644
--- a/couchpotato/static/scripts/block/navigation.js
+++ b/couchpotato/static/scripts/block/navigation.js
@@ -67,12 +67,12 @@ Block.Navigation = new Class({
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))
- );
- }
+ body.getElements('.header .more_menu.menu li a, .header .more_menu.menu li span.separator').each(function(el, nr){
+ if(nr <= 2) return;
+ if(el.get('tag') == 'a')
+ self.nav.grab(new Element('li').grab(el.clone().cloneEvents(el)));
+ else
+ self.nav.grab(new Element('li.separator'));
});
self.added = true;
diff --git a/couchpotato/static/scripts/couchpotato.js b/couchpotato/static/scripts/couchpotato.js
index 673323b6..8e6ccede 100644
--- a/couchpotato/static/scripts/couchpotato.js
+++ b/couchpotato/static/scripts/couchpotato.js
@@ -30,6 +30,12 @@ var CouchPotato = new Class({
History.addEvent('change', self.openPage.bind(self));
self.c.addEvent('click:relay(a[href^=/]:not([target]))', self.pushState.bind(self));
self.c.addEvent('click:relay(a[href^=http])', self.openDerefered.bind(self));
+
+ // Check if device is touchenabled
+ self.touch_device = 'ontouchstart' in document.documentElement;
+ if(self.touch_device)
+ self.c.addClass('touch_enabled');
+
},
getOption: function(name){
@@ -68,28 +74,42 @@ var CouchPotato = new Class({
self.block.footer = new Block.Footer(self, {})
);
- [new Element('a.orange', {
- 'text': 'Restart',
- 'events': {
- 'click': self.restartQA.bind(self)
- }
- }),
- new Element('a.red', {
- 'text': 'Shutdown',
- 'events': {
- 'click': self.shutdownQA.bind(self)
- }
- }),
- new Element('a', {
- 'text': 'Update to latest',
- 'events': {
- 'click': self.checkForUpdate.bind(self, null)
- }
- }),
- new Element('a', {
- 'text': 'Run install wizard',
- 'href': App.createUrl('wizard')
- })].each(function(a){
+ var setting_links = [
+ new Element('a', {
+ 'text': 'About CouchPotato',
+ 'href': App.createUrl('settings/about')
+ }),
+ new Element('a', {
+ 'text': 'Check for Updates',
+ 'events': {
+ 'click': self.checkForUpdate.bind(self, null)
+ }
+ }),
+ new Element('span.separator'),
+ new Element('a', {
+ 'text': 'Settings',
+ 'href': App.createUrl('settings/general')
+ }),
+ new Element('a', {
+ 'text': 'Logs',
+ 'href': App.createUrl('log')
+ }),
+ new Element('span.separator'),
+ new Element('a', {
+ 'text': 'Restart',
+ 'events': {
+ 'click': self.restartQA.bind(self)
+ }
+ }),
+ new Element('a', {
+ 'text': 'Shutdown',
+ 'events': {
+ 'click': self.shutdownQA.bind(self)
+ }
+ })
+ ]
+
+ setting_links.each(function(a){
self.block.more.addLink(a)
})
diff --git a/couchpotato/static/scripts/page/home.js b/couchpotato/static/scripts/page/home.js
index f874acdb..04fc7a8e 100644
--- a/couchpotato/static/scripts/page/home.js
+++ b/couchpotato/static/scripts/page/home.js
@@ -39,7 +39,8 @@ Page.Home = new Class({
),
'filter': {
'release_status': 'snatched,available'
- }
+ },
+ 'limit': null
});
// Coming Soon
@@ -65,31 +66,37 @@ Page.Home = new Class({
// Make all thumbnails the same size
self.soon_list.addEvent('loaded', function(){
- var images = $(self.soon_list).getElements('img'),
+ var images = $(self.soon_list).getElements('.poster'),
timer,
- lowest = null;
+ highest = 100;
- 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)
- }
+ images.each(function(img_container){
+ img_container.getElements('img').addEvent('load', function(){
+ var img = this,
+ height = img.getSize().y;
+ if(!highest || highest < height){
+ highest = height;
+ if(timer) clearTimeout(timer);
+ timer = (function(){
+ images.setStyle('height', highest);
+ }).delay(50);
+ }
+ });
});
$(window).addEvent('resize', function(){
if(timer) clearTimeout(timer);
timer = (function(){
- var lowest;
- images.each(function(img){
+ var highest = 100;
+ images.each(function(img_container){
+ var img = img_container.getElement('img');
+ if(!img) return
+
var height = img.getSize().y;
- if(!lowest || lowest > height)
- lowest = height;
+ if(!highest || highest < height)
+ highest = height;
});
- images.getParent().setStyle('height', lowest);
+ images.setStyle('height', highest);
}).delay(300);
});
});
diff --git a/couchpotato/static/scripts/page/settings.js b/couchpotato/static/scripts/page/settings.js
index 6e563bff..0d7ebe7f 100644
--- a/couchpotato/static/scripts/page/settings.js
+++ b/couchpotato/static/scripts/page/settings.js
@@ -11,20 +11,6 @@ Page.Settings = new Class({
current: 'about',
has_tab: false,
- initialize: function(options){
- var self = this;
- self.parent(options);
-
- // Add to more menu
- if(self.name == 'settings')
- App.getBlock('more').addLink(new Element('a', {
- 'href': App.createUrl(self.name),
- 'text': self.name.capitalize(),
- 'title': self.title
- }), 'top')
-
- },
-
open: function(action, params){
var self = this;
self.action = action == 'index' ? self.default_action : action;
@@ -876,7 +862,7 @@ Option.Directories = new Class({
$(dir).addClass('is_empty');
// Add remove button
- new Element('a.icon.delete', {
+ new Element('a.icon2.delete', {
'events': {
'click': self.delItem.bind(self, dir)
}
@@ -1385,7 +1371,7 @@ Option.Combined = new Class({
item[value_empty == value_count ? 'addClass' : 'removeClass']('is_empty');
- new Element('a.icon.delete', {
+ new Element('a.icon2.delete', {
'events': {
'click': self.deleteCombinedItem.bind(self)
}
diff --git a/couchpotato/static/style/main.css b/couchpotato/static/style/main.css
index 02dda3d1..97983130 100644
--- a/couchpotato/static/style/main.css
+++ b/couchpotato/static/style/main.css
@@ -7,6 +7,7 @@ body, html {
margin: 0;
padding: 0;
background: #4e5969;
+ -webkit-font-smoothing: subpixel-antialiased;
}
body { overflow-y: scroll; }
body.noscroll { overflow: hidden; }
@@ -34,11 +35,21 @@ input:focus, textarea:focus {
outline: none;
}
-input:-moz-placeholder, textarea:-moz-placeholder {
- color: rgba(255, 255, 255, 0.6);
+::-moz-placeholder {
+ color: rgba(255, 255, 255, 0.5);
+ font-style: italic;
}
-::-webkit-input-placeholder, ::-webkit-textarea-placeholder {
- color: rgba(255, 255, 255, 0.6);
+input:-moz-placeholder {
+ color: rgba(255, 255, 255, 0.5);
+ font-style: italic;
+}
+::-webkit-input-placeholder {
+ color: rgba(255, 255, 255, 0.5);
+ font-style: italic;
+}
+:-ms-input-placeholder {
+ color: rgba(255, 255, 255, 0.5) !important;
+ font-style: italic;
}
a img {
@@ -108,7 +119,7 @@ body > .spinner, .mask{
}
.button {
- background: #5082bc url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAyCAYAAACd+7GKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAClJREFUeNpi/v//vwMTAwPDfzjBgMpFI/7hFSOT9Y8qRuF3JLoHAQIMAHYtMmRA+CugAAAAAElFTkSuQmCC") repeat-x;
+ background: #5082bc;
padding: 5px 10px 6px;
color: #fff;
text-decoration: none;
@@ -154,6 +165,7 @@ body > .spinner, .mask{
line-height: 1;
-webkit-font-smoothing: antialiased;
font-size: 15px;
+ color: #FFF;
}
.icon2.cog:before { content: "\e109"; }
@@ -161,6 +173,30 @@ body > .spinner, .mask{
.icon2.search:before { content: "\e03e"; }
.icon2.return-key:before { content: "\e111"; }
.icon2.close:before { content: "\e04e"; }
+.icon2.trailer:before { content: "\e0e9"; }
+.icon2.download:before { content: "\e0c3"; }
+.icon2.edit:before { content: "\e068"; }
+.icon2.refresh:before { content: "\e04f"; font-weight: bold; }
+.icon2.delete:before { content: "\e04e"; }
+.icon2.directory:before { content: "\e097"; }
+.icon2.completed:before { content: "\e070"; }
+.icon2.info:before { content: "\e089"; }
+.icon2.attention:before { content: "\e009"; }
+.icon2.readd:before {
+ display: inline-block;
+ content: "\e04b";
+ transform: scale(-1, 1);
+}
+.icon2.imdb:before {
+ content: "IMDb";
+ color: #444;
+ padding: 2px;
+ border-radius: 3px;
+ background-color: #fbec98;
+ font: bold 8px Arial;
+ position: relative;
+ top: -3px;
+}
.icon2.menu:before {
content: "\e076 \e076 \e076";
line-height: 6px;
@@ -178,13 +214,10 @@ body > .spinner, .mask{
margin: 0;
width: 100%;
z-index: 5;
- background: #4e5969;
+ background: #5c697b;
box-shadow: 0 0 10px rgba(0,0,0,.1);
transition: all .4s ease-in-out;
}
- .header.with_shadow {
- background-color: #46505e;
- }
@media all and (max-width: 480px) {
.header {
@@ -315,7 +348,7 @@ body > .spinner, .mask{
}
.menu_shown body {
- left: 160px;
+ left: 240px;
}
.header .navigation {
@@ -327,13 +360,13 @@ body > .spinner, .mask{
right: 0;
top: 0;
bottom: 0;
- left: 160px;
+ left: 240px;
}
.header .navigation ul {
- width: 160px;
+ width: 240px;
position: fixed;
- left: -160px;
+ left: -240px;
background: rgba(0,0,0,.5);
transition: all .5s cubic-bezier(0.9,0,0.1,1);
}
@@ -374,26 +407,15 @@ body > .spinner, .mask{
.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;
+ width: 200px;
margin-left: -106px;
- margin-top: 66px;
+ margin-top: 22px;
}
@media all and (max-width: 480px) {
@@ -402,7 +424,7 @@ body > .spinner, .mask{
}
.header .more_menu .wrapper {
- margin-top: 44px;
+ margin-top: 0;
}
}
@@ -439,13 +461,20 @@ body > .spinner, .mask{
}
.header .notification_menu ul {
+ min-height: 60px;
max-height: 300px;
overflow: auto;
}
-
- .header .notification_menu > a {
- background-position: center -209px;
- }
+ .header .notification_menu ul:empty:after {
+ content: 'No notifications (yet)';
+ text-align: center;
+ width: 100%;
+ position: absolute;
+ padding: 18px 0;
+ font-size: 15px;
+ font-style: italic;
+ opacity: .4;
+ }
.header .notification_menu li > span {
padding: 5px;
@@ -491,6 +520,7 @@ body > .spinner, .mask{
width: 16px;
cursor: pointer;
background: url('../images/sprite.png') no-repeat -200px;
+ border-radius: 3px;
}
.check.highlighted { background-color: #424c59; }
.check.checked { background-position: -2px 0; }
@@ -508,14 +538,7 @@ body > .spinner, .mask{
.select .selection {
display: inline-block;
padding: 0 30px 0 20px;
- 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(
- 180deg,
- #5b9bd1 0%,
- #406db8 100%
- );
+ background: #5b9bd1 url('../images/sprite.png') no-repeat 94% -53px;
}
.select .selection .selectionDisplay {
@@ -535,11 +558,9 @@ body > .spinner, .mask{
.select .list {
display: none;
background: #282d34;
- border: 1px solid #1f242b;
position: absolute;
- margin: 28px 0 0 0;
+ margin: 25px 0 0 0;
box-shadow: 0 20px 20px -10px rgba(0,0,0,0.4);
- border-radius:3px;
z-index: 3;
}
.select.active .list {
@@ -566,15 +587,14 @@ body > .spinner, .mask{
.inlay {
color: #fff;
border: 0;
- border-radius:3px;
background-color: #282d34;
- box-shadow: inset 0 1px 8px rgba(0,0,0,0.25), 0 1px 0px rgba(255,255,255,0.25);
+ box-shadow: inset 0 1px 8px rgba(0,0,0,0.25);
}
.inlay.light {
background-color: #47515f;
outline: none;
- box-shadow: inset 0 1px 8px rgba(0,0,0,0.05), 0 1px 0px rgba(255,255,255,0.15);
+ box-shadow: none;
}
.inlay:focus {
@@ -585,7 +605,7 @@ body > .spinner, .mask{
.onlay, .inlay .selected, .inlay:not(.reversed) > li:hover, .inlay > li.active, .inlay.reversed > li {
border-radius:3px;
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);
+ box-shadow: inset 0 1px 0px rgba(255,255,255,0.20);
background: rgb(55,62,74);
background-image: linear-gradient(
0,
@@ -597,7 +617,7 @@ body > .spinner, .mask{
color: #fff;
border: 1px solid transparent;
background-color: #282d34;
- box-shadow: inset 0 1px 8px rgba(0,0,0,0.25), 0 1px 0px rgba(255,255,255,0.25);
+ box-shadow: inset 0 1px 8px rgba(0,0,0,0.25);
}
.question {
@@ -648,38 +668,37 @@ body > .spinner, .mask{
.more_menu > a {
display: block;
- background: url('../images/sprite.png') no-repeat center -137px;
- height: 25px;
- width: 25px;
- border: 1px solid rgba(0,0,0,0.3);
+ height: 44px;
+ width: 44px;
transition: all 0.3s ease-in-out;
+ border-bottom: 4px solid transparent;
+ border-radius: 0;
}
- .more_menu.show > a:not(:active), .more_menu > a:hover:not(:active) {
- background-color: #406db8;
+
+ .more_menu .button:hover {
+ border-color: #047792;
+ }
+
+ .more_menu.show .button {
+ border-color: #04bce6;
}
.more_menu .wrapper {
display: none;
- background: rgba(255,255,255,0.98);
top: 0;
right: 0;
- padding: 4px;
- margin: 26px 0 0 0;
+ margin: 11px 0 0 0;
position: absolute;
z-index: 90;
width: 185px;
- box-shadow: 0 20px 20px -5px rgba(0,0,0,0.1);
- text-align: center;
- color: #000;
- background-image: linear-gradient(
- -45deg,
- rgb(200,200,200) 0%,
- rgb(255,255,255) 100%
- );
+ box-shadow: 0 20px 20px -10px rgba(0,0,0,0.55);
+ color: #444;
+ background: #fff;
}
.more_menu.show .wrapper {
display: block;
+ top: 44px;
}
.more_menu ul {
@@ -698,11 +717,19 @@ body > .spinner, .mask{
border-bottom: 1px solid rgba(255,255,255,0.2);
box-shadow: none;
font-weight: normal;
- font-size: 11px;
- text-transform: uppercase;
+ font-size: 1.2em;
letter-spacing: 1px;
- padding: 3px 0;
- color: #000;
+ padding: 2px 10px;
+ color: #444;
+ }
+ .more_menu .wrapper li:first-child a { padding-top: 5px; }
+ .more_menu .wrapper li:last-child a { padding-bottom: 5px; }
+
+ .more_menu .wrapper li .separator {
+ border-bottom: 1px solid rgba(0,0,0,.1);
+ display: block;
+ height: 1;
+ margin: 5px 0;
}
.more_menu .wrapper li:last-child a {
diff --git a/couchpotato/static/style/settings.css b/couchpotato/static/style/settings.css
index 7bfeb3aa..132d9c53 100644
--- a/couchpotato/static/style/settings.css
+++ b/couchpotato/static/style/settings.css
@@ -252,7 +252,7 @@
display: block;
text-align: right;
height: 20px;
- margin: 0 0 -37px;
+ margin: 0 0 -38px;
}
.page .advanced_toggle .check {
margin: 0;
@@ -262,18 +262,24 @@
color: #edc07f;
}
- .page .directory {
+ .page form .directory {
display: inline-block;
padding: 0 4% 0 4px;
font-size: 13px;
width: 30%;
- background-image: url('../images/icon.folder.gif');
- background-repeat: no-repeat;
- background-position: 97% center;
overflow: hidden;
vertical-align: top;
+ position: relative;
}
- .page .directory > span {
+ .page form .directory:after {
+ content: "\e097";
+ position: absolute;
+ right: 7px;
+ top: 2px;
+ font-family: 'Elusive-Icons';
+ color: #f5e39c;
+ }
+ .page form .directory > span {
height: 25px;
display: inline-block;
float: right;
@@ -281,15 +287,19 @@
white-space: nowrap;
cursor: pointer;
}
+ .page form .directory span:empty:before {
+ content: 'No folder selected';
+ font-style: italic;
+ opacity: .3;
+ }
.page .directory_list {
z-index: 2;
position: absolute;
width: 450px;
- margin: 28px 0 20px 18%;
+ margin: 28px 0 20px 18.4%;
background: #5c697b;
- border-radius: 3px;
- box-shadow: 0 0 50px rgba(0,0,0,0.55);
+ box-shadow: 0 20px 40px -20px rgba(0,0,0,0.55);
}
.page .directory_list .pointer {
@@ -299,7 +309,7 @@
display: block;
position: absolute;
width: 0px;
- margin: -6px 0 0 22%;
+ margin: -6px 0 0 45%;
}
.page .directory_list ul {
@@ -311,11 +321,14 @@
}
.page .directory_list li {
- padding: 4px 10px;
+ padding: 4px 30px 4px 10px;
cursor: pointer;
margin: 0 !important;
border-top: 1px solid rgba(255,255,255,0.1);
background: url('../images/right.arrow.png') no-repeat 98% center;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
}
.page .directory_list li:last-child {
border-bottom: 1px solid rgba(255,255,255,0.1);
@@ -341,7 +354,10 @@
.page .directory_list .actions {
clear: both;
padding: 4% 4% 2%;
- min-height: 25px;
+ min-height: 45px;
+ position: relative;
+ width: 100%;
+ text-align: right;
}
.page .directory_list .actions label {
@@ -350,7 +366,7 @@
padding: 0;
}
.page .directory_list .actions .inlay {
- margin: -2px 0 0 7px;
+ margin: 0 0 0 7px;
}
.page .directory_list .actions .back {
@@ -360,6 +376,9 @@
padding: 0;
line-height: 120%;
vertical-align: top;
+ position: absolute;
+ text-align: left;
+ left: 4%;
}
.page .directory_list .actions:last-child {
@@ -373,10 +392,10 @@
}
.page .directory_list .actions:last-child > .clear {
- left: -90%;
- position: relative;
- background-color: #af3128;
-}
+ left: 4%;
+ position: absolute;
+ background-color: #af3128;
+ }
.page .directory_list .actions:last-child > .cancel {
font-weight: bold;
@@ -563,8 +582,10 @@
display: none;
width: 22px;
height: 22px;
+ line-height: 22px;
+ text-align: center;
vertical-align: middle;
- background-position: left center;
+ color: #fe3d3d;
}
.page .combined_table .ctrlHolder:hover .delete {
display: inline-block;
@@ -576,7 +597,8 @@
.page .tab_about .usenet {
padding: 20px 30px 0;
- font-size: 17px;
+ font-size: 1.5em;
+ line-height: 1.3em;
}
.page .tab_about .usenet a {
diff --git a/libs/cssprefixer/__init__.py b/libs/cssprefixer/__init__.py
deleted file mode 100755
index 05674329..00000000
--- a/libs/cssprefixer/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# CSSPrefixer
-# Copyright 2010-2012 Greg V.
-
-# 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')
diff --git a/libs/cssprefixer/engine.py b/libs/cssprefixer/engine.py
deleted file mode 100755
index 894f6e7f..00000000
--- a/libs/cssprefixer/engine.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# CSSPrefixer
-# Copyright 2010-2012 Greg V.
-
-# 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']
diff --git a/libs/cssprefixer/rules.py b/libs/cssprefixer/rules.py
deleted file mode 100755
index 156d4f18..00000000
--- a/libs/cssprefixer/rules.py
+++ /dev/null
@@ -1,271 +0,0 @@
-# CSSPrefixer
-# Copyright 2010-2012 Greg V.
-
-# 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,
-}
diff --git a/libs/cssutils/__init__.py b/libs/cssutils/__init__.py
deleted file mode 100755
index 8157ce57..00000000
--- a/libs/cssutils/__init__.py
+++ /dev/null
@@ -1,385 +0,0 @@
-#!/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 .
-
-
-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 `__
- General CSS rules and properties are defined here
-`CSS 2.1 Errata `__
- A few errata, mainly the definition of CHARSET_SYM tokens
-`CSS3 Module: 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 `__
- MediaQueries are part of ``stylesheets.MediaList`` since v0.9.4, used in
- @import and @media rules.
-`Namespaces `__
- 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 `__
- Most properties of this spec are implemented including MarginRules
-`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 `__
- 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 `__
- DOM for package stylesheets
-`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) `__ 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__
diff --git a/libs/cssutils/_codec2.py b/libs/cssutils/_codec2.py
deleted file mode 100755
index d0ae617f..00000000
--- a/libs/cssutils/_codec2.py
+++ /dev/null
@@ -1,584 +0,0 @@
-#!/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)
diff --git a/libs/cssutils/_codec3.py b/libs/cssutils/_codec3.py
deleted file mode 100755
index 878a7b29..00000000
--- a/libs/cssutils/_codec3.py
+++ /dev/null
@@ -1,608 +0,0 @@
-#!/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)
diff --git a/libs/cssutils/_fetch.py b/libs/cssutils/_fetch.py
deleted file mode 100755
index a9138fe3..00000000
--- a/libs/cssutils/_fetch.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""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()
diff --git a/libs/cssutils/_fetchgae.py b/libs/cssutils/_fetchgae.py
deleted file mode 100755
index ef91d412..00000000
--- a/libs/cssutils/_fetchgae.py
+++ /dev/null
@@ -1,68 +0,0 @@
-"""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)
diff --git a/libs/cssutils/codec.py b/libs/cssutils/codec.py
deleted file mode 100755
index c694e1fc..00000000
--- a/libs/cssutils/codec.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/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
diff --git a/libs/cssutils/css/__init__.py b/libs/cssutils/css/__init__.py
deleted file mode 100755
index 4cc4a904..00000000
--- a/libs/cssutils/css/__init__.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""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 *
diff --git a/libs/cssutils/css/colors.py b/libs/cssutils/css/colors.py
deleted file mode 100755
index 5f8dd899..00000000
--- a/libs/cssutils/css/colors.py
+++ /dev/null
@@ -1,184 +0,0 @@
-# -*- 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),
-}
diff --git a/libs/cssutils/css/csscharsetrule.py b/libs/cssutils/css/csscharsetrule.py
deleted file mode 100755
index fa6b7c4d..00000000
--- a/libs/cssutils/css/csscharsetrule.py
+++ /dev/null
@@ -1,159 +0,0 @@
-"""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"" % (
- 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))
diff --git a/libs/cssutils/css/csscomment.py b/libs/cssutils/css/csscomment.py
deleted file mode 100755
index 311df0fd..00000000
--- a/libs/cssutils/css/csscomment.py
+++ /dev/null
@@ -1,87 +0,0 @@
-"""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"" % (
- 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)
diff --git a/libs/cssutils/css/cssfontfacerule.py b/libs/cssutils/css/cssfontfacerule.py
deleted file mode 100755
index 1e936066..00000000
--- a/libs/cssutils/css/cssfontfacerule.py
+++ /dev/null
@@ -1,184 +0,0 @@
-"""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"" % (
- 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)
diff --git a/libs/cssutils/css/cssimportrule.py b/libs/cssutils/css/cssimportrule.py
deleted file mode 100755
index 59fe2564..00000000
--- a/libs/cssutils/css/cssimportrule.py
+++ /dev/null
@@ -1,396 +0,0 @@
-"""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""\
- % (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)
diff --git a/libs/cssutils/css/cssmediarule.py b/libs/cssutils/css/cssmediarule.py
deleted file mode 100755
index a7ebf463..00000000
--- a/libs/cssutils/css/cssmediarule.py
+++ /dev/null
@@ -1,302 +0,0 @@
-"""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"" % (
- 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)
diff --git a/libs/cssutils/css/cssnamespacerule.py b/libs/cssutils/css/cssnamespacerule.py
deleted file mode 100755
index a2610fc2..00000000
--- a/libs/cssutils/css/cssnamespacerule.py
+++ /dev/null
@@ -1,295 +0,0 @@
-"""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"" % (
- 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)
-
\ No newline at end of file
diff --git a/libs/cssutils/css/csspagerule.py b/libs/cssutils/css/csspagerule.py
deleted file mode 100755
index cc2f4516..00000000
--- a/libs/cssutils/css/csspagerule.py
+++ /dev/null
@@ -1,436 +0,0 @@
-"""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"") % (
- 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)
\ No newline at end of file
diff --git a/libs/cssutils/css/cssproperties.py b/libs/cssutils/css/cssproperties.py
deleted file mode 100755
index 7454daff..00000000
--- a/libs/cssutils/css/cssproperties.py
+++ /dev/null
@@ -1,122 +0,0 @@
-"""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)))
diff --git a/libs/cssutils/css/cssrule.py b/libs/cssutils/css/cssrule.py
deleted file mode 100755
index 1d3ab6fb..00000000
--- a/libs/cssutils/css/cssrule.py
+++ /dev/null
@@ -1,304 +0,0 @@
-"""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)
diff --git a/libs/cssutils/css/cssrulelist.py b/libs/cssutils/css/cssrulelist.py
deleted file mode 100755
index f9daff49..00000000
--- a/libs/cssutils/css/cssrulelist.py
+++ /dev/null
@@ -1,53 +0,0 @@
-"""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
diff --git a/libs/cssutils/css/cssstyledeclaration.py b/libs/cssutils/css/cssstyledeclaration.py
deleted file mode 100755
index 7573487a..00000000
--- a/libs/cssutils/css/cssstyledeclaration.py
+++ /dev/null
@@ -1,697 +0,0 @@
-"""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
-
-
- 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"" % (
- 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
-
-
- """
- 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!")
diff --git a/libs/cssutils/css/cssstylerule.py b/libs/cssutils/css/cssstylerule.py
deleted file mode 100755
index db85b8b5..00000000
--- a/libs/cssutils/css/cssstylerule.py
+++ /dev/null
@@ -1,234 +0,0 @@
-"""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"" % (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)
diff --git a/libs/cssutils/css/cssstylesheet.py b/libs/cssutils/css/cssstylesheet.py
deleted file mode 100755
index 88c46b8c..00000000
--- a/libs/cssutils/css/cssstylesheet.py
+++ /dev/null
@@ -1,804 +0,0 @@
-"""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 "" % (
- 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)
diff --git a/libs/cssutils/css/cssunknownrule.py b/libs/cssutils/css/cssunknownrule.py
deleted file mode 100755
index c9e4361f..00000000
--- a/libs/cssutils/css/cssunknownrule.py
+++ /dev/null
@@ -1,209 +0,0 @@
-"""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"" % (
- 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))
-
\ No newline at end of file
diff --git a/libs/cssutils/css/cssvalue.py b/libs/cssutils/css/cssvalue.py
deleted file mode 100755
index 2f643453..00000000
--- a/libs/cssutils/css/cssvalue.py
+++ /dev/null
@@ -1,1251 +0,0 @@
-"""CSSValue related classes
-
-- CSSValue implements DOM Level 2 CSS CSSValue
-- CSSPrimitiveValue implements DOM Level 2 CSS CSSPrimitiveValue
-- CSSValueList implements DOM Level 2 CSS CSSValueList
-
-"""
-__all__ = ['CSSValue', 'CSSPrimitiveValue', 'CSSValueList', 'RGBColor',
- 'CSSVariable']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-
-from cssutils.prodparser import *
-import cssutils
-import cssutils.helper
-import math
-import re
-import xml.dom
-
-
-class CSSValue(cssutils.util._NewBase):
- """The CSSValue interface represents a simple or a complex value.
- A CSSValue object only occurs in a context of a CSS property.
- """
-
- # The value is inherited and the cssText contains "inherit".
- CSS_INHERIT = 0
- # The value is a CSSPrimitiveValue.
- CSS_PRIMITIVE_VALUE = 1
- # The value is a CSSValueList.
- CSS_VALUE_LIST = 2
- # The value is a custom value.
- CSS_CUSTOM = 3
- # The value is a CSSVariable.
- CSS_VARIABLE = 4
-
- _typestrings = {0: 'CSS_INHERIT' ,
- 1: 'CSS_PRIMITIVE_VALUE',
- 2: 'CSS_VALUE_LIST',
- 3: 'CSS_CUSTOM',
- 4: 'CSS_VARIABLE'}
-
- def __init__(self, cssText=None, parent=None, readonly=False):
- """
- :param cssText:
- the parsable cssText of the value
- :param readonly:
- defaults to False
- """
- super(CSSValue, self).__init__()
-
- self._cssValueType = None
- self.wellformed = False
- self.parent = parent
- if cssText is not None: # may be 0
- if isinstance(cssText, int):
- cssText = unicode(cssText) # if it is an integer
- elif isinstance(cssText, float):
- cssText = u'%f' % cssText # if it is a floating point number
-
- self.cssText = cssText
-
- self._readonly = readonly
-
- def __repr__(self):
- return u"cssutils.css.%s(%r)" % (self.__class__.__name__,
- self.cssText)
-
- def __str__(self):
- return u"" % (self.__class__.__name__,
- self.cssValueTypeString,
- self.cssText,
- id(self))
-
- def _setCssText(self, cssText):
- """
- Format::
-
- unary_operator
- : '-' | '+'
- ;
- operator
- : '/' S* | ',' S* | /* empty */
- ;
- expr
- : term [ operator term ]*
- ;
- term
- : unary_operator?
- [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* |
- ANGLE S* | TIME S* | FREQ S* ]
- | STRING S* | IDENT S* | URI S* | hexcolor | function
- | UNICODE-RANGE S*
- ;
- function
- : FUNCTION S* expr ')' S*
- ;
- /*
- * There is a constraint on the color that it must
- * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
- * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
- */
- hexcolor
- : HASH S*
- ;
-
- :exceptions:
- - :exc:`~xml.dom.SyntaxErr`:
- Raised if the specified CSS string value has a syntax error
- (according to the attached property) or is unparsable.
- - :exc:`~xml.dom.InvalidModificationErr`:
- TODO: Raised if the specified CSS string value represents a
- different type of values than the values allowed by the CSS
- property.
- - :exc:`~xml.dom.NoModificationAllowedErr`:
- Raised if this value is readonly.
- """
- self._checkReadonly()
-
- # used as operator is , / or S
- nextSor = u',/'
-
- term = Choice(Sequence(PreDef.unary(),
- Choice(PreDef.number(nextSor=nextSor),
- PreDef.percentage(nextSor=nextSor),
- PreDef.dimension(nextSor=nextSor))),
- PreDef.string(nextSor=nextSor),
- PreDef.ident(nextSor=nextSor),
- PreDef.uri(nextSor=nextSor),
- PreDef.hexcolor(nextSor=nextSor),
- PreDef.unicode_range(nextSor=nextSor),
- # special case IE only expression
- Prod(name='expression',
- match=lambda t, v: t == self._prods.FUNCTION and (
- cssutils.helper.normalize(v) in (u'expression(',
- u'alpha(',
- u'blur(',
- u'chroma(',
- u'dropshadow(',
- u'fliph(',
- u'flipv(',
- u'glow(',
- u'gray(',
- u'invert(',
- u'mask(',
- u'shadow(',
- u'wave(',
- u'xray(') or
- v.startswith(u'progid:DXImageTransform.Microsoft.')
- ),
- nextSor=nextSor,
- toSeq=lambda t, tokens: (ExpressionValue._functionName,
- ExpressionValue(
- cssutils.helper.pushtoken(t, tokens),
- parent=self)
- )
- ),
- # CSS Variable var(
- PreDef.variable(nextSor=nextSor,
- toSeq=lambda t, tokens: ('CSSVariable',
- CSSVariable(
- cssutils.helper.pushtoken(t, tokens),
- parent=self)
- )
- ),
- # calc(
- PreDef.calc(nextSor=nextSor,
- toSeq=lambda t, tokens: (CalcValue._functionName,
- CalcValue(
- cssutils.helper.pushtoken(t, tokens),
- parent=self)
- )
- ),
-# TODO:
-# # rgb/rgba(
-# Prod(name='RGBColor',
-# match=lambda t, v: t == self._prods.FUNCTION and (
-# cssutils.helper.normalize(v) in (u'rgb(',
-# u'rgba('
-# )
-# ),
-# nextSor=nextSor,
-# toSeq=lambda t, tokens: (RGBColor._functionName,
-# RGBColor(
-# cssutils.helper.pushtoken(t, tokens),
-# parent=self)
-# )
-# ),
- # other functions like rgb( etc
- PreDef.function(nextSor=nextSor,
- toSeq=lambda t, tokens: ('FUNCTION',
- CSSFunction(
- cssutils.helper.pushtoken(t, tokens),
- parent=self)
- )
- )
- )
- operator = Choice(PreDef.S(),
- PreDef.char('comma', ',',
- toSeq=lambda t, tokens: ('operator', t[1])),
- PreDef.char('slash', '/',
- toSeq=lambda t, tokens: ('operator', t[1])),
- optional=True)
- # CSSValue PRODUCTIONS
- valueprods = Sequence(term,
- Sequence(operator, # mayEnd this Sequence if whitespace
-
- # TODO: only when setting via other class
- # used by variabledeclaration currently
- PreDef.char('END', ';',
- stopAndKeep=True,
- optional=True),
-
- term,
- minmax=lambda: (0, None)))
- # parse
- wellformed, seq, store, notused = ProdParser().parse(cssText,
- u'CSSValue',
- valueprods,
- keepS=True)
- if wellformed:
- # - count actual values and set firstvalue which is used later on
- # - combine comma separated list, e.g. font-family to a single item
- # - remove S which should be an operator but is no needed
- count, firstvalue = 0, ()
- newseq = self._tempSeq()
- i, end = 0, len(seq)
- while i < end:
- item = seq[i]
- if item.type == self._prods.S:
- pass
-
- elif (item.value, item.type) == (u',', 'operator'):
- # , separared counts as a single STRING for now
- # URI or STRING value might be a single CHAR too!
- newseq.appendItem(item)
- count -= 1
- if firstvalue:
- # list of IDENTs is handled as STRING!
- if firstvalue[1] == self._prods.IDENT:
- firstvalue = firstvalue[0], 'STRING'
-
- elif item.value == u'/':
- # / separated items count as one
- newseq.appendItem(item)
-
- elif item.value == u'-' or item.value == u'+':
- # combine +- and following number or other
- i += 1
- try:
- next = seq[i]
- except IndexError:
- firstvalue = () # raised later
- break
-
- newval = item.value + next.value
- newseq.append(newval, next.type,
- item.line, item.col)
- if not firstvalue:
- firstvalue = (newval, next.type)
- count += 1
-
- elif item.type != cssutils.css.CSSComment:
- newseq.appendItem(item)
- if not firstvalue:
- firstvalue = (item.value, item.type)
- count += 1
-
- else:
- newseq.appendItem(item)
-
- i += 1
-
- if not firstvalue:
- self._log.error(
- u'CSSValue: Unknown syntax or no value: %r.' %
- self._valuestr(cssText))
- else:
- # ok and set
- self._setSeq(newseq)
- self.wellformed = wellformed
-
- if hasattr(self, '_value'):
- # only in case of CSSPrimitiveValue, else remove!
- del self._value
-
- if count == 1:
- # inherit, primitive or variable
- if isinstance(firstvalue[0], basestring) and\
- u'inherit' == cssutils.helper.normalize(firstvalue[0]):
- self.__class__ = CSSValue
- self._cssValueType = CSSValue.CSS_INHERIT
- elif 'CSSVariable' == firstvalue[1]:
- self.__class__ = CSSVariable
- self._value = firstvalue
- # TODO: remove major hack!
- self._name = firstvalue[0]._name
- else:
- self.__class__ = CSSPrimitiveValue
- self._value = firstvalue
-
- elif count > 1:
- # valuelist
- self.__class__ = CSSValueList
-
- # change items in list to specific type (primitive etc)
- newseq = self._tempSeq()
- commalist = []
- nexttocommalist = False
-
- def itemValue(item):
- "Reserialized simple item.value"
- if self._prods.STRING == item.type:
- return cssutils.helper.string(item.value)
- elif self._prods.URI == item.type:
- return cssutils.helper.uri(item.value)
- elif self._prods.FUNCTION == item.type or\
- 'CSSVariable' == item.type:
- return item.value.cssText
- else:
- return item.value
-
- def saveifcommalist(commalist, newseq):
- """
- saves items in commalist to seq and items
- if anything in there
- """
- if commalist:
- newseq.replace(-1,
- CSSPrimitiveValue(cssText=u''.join(
- commalist)),
- CSSPrimitiveValue,
- newseq[-1].line,
- newseq[-1].col)
- del commalist[:]
-
- for i, item in enumerate(self._seq):
- if issubclass(type(item.value), CSSValue):
- # set parent of CSSValueList items to the lists
- # parent
- item.value.parent = self.parent
-
- if item.type in (self._prods.DIMENSION,
- self._prods.FUNCTION,
- self._prods.HASH,
- self._prods.IDENT,
- self._prods.NUMBER,
- self._prods.PERCENTAGE,
- self._prods.STRING,
- self._prods.URI,
- self._prods.UNICODE_RANGE,
- 'CSSVariable'):
- if nexttocommalist:
- # wait until complete
- commalist.append(itemValue(item))
- else:
- saveifcommalist(commalist, newseq)
- # append new item
- if hasattr(item.value, 'cssText'):
- newseq.append(item.value,
- item.value.__class__,
- item.line, item.col)
-
- else:
- newseq.append(CSSPrimitiveValue(
- itemValue(item)),
- CSSPrimitiveValue,
- item.line, item.col)
-
- nexttocommalist = False
-
- elif u',' == item.value:
- if not commalist:
- # save last item to commalist
- commalist.append(itemValue(self._seq[i - 1]))
- commalist.append(u',')
- nexttocommalist = True
-
- else:
- if nexttocommalist:
- commalist.append(item.value.cssText)
- else:
- newseq.appendItem(item)
-
- saveifcommalist(commalist, newseq)
- self._setSeq(newseq)
-
- else:
- # should not happen...
- self.__class__ = CSSValue
- self._cssValueType = CSSValue.CSS_CUSTOM
-
- cssText = property(lambda self: cssutils.ser.do_css_CSSValue(self),
- _setCssText,
- doc="A string representation of the current value.")
-
- cssValueType = property(lambda self: self._cssValueType,
- doc="A (readonly) code defining the type of the value.")
-
- cssValueTypeString = property(
- lambda self: CSSValue._typestrings.get(self.cssValueType, None),
- doc="(readonly) Name of cssValueType.")
-
-
-class CSSPrimitiveValue(CSSValue):
- """Represents a single CSS Value. May be used to determine the value of a
- specific style property currently set in a block or to set a specific
- style property explicitly within the block. Might be obtained from the
- getPropertyCSSValue method of CSSStyleDeclaration.
-
- Conversions are allowed between absolute values (from millimeters to
- centimeters, from degrees to radians, and so on) but not between
- relative values. (For example, a pixel value cannot be converted to a
- centimeter value.) Percentage values can't be converted since they are
- relative to the parent value (or another property value). There is one
- exception for color percentage values: since a color percentage value
- is relative to the range 0-255, a color percentage value can be
- converted to a number; (see also the RGBColor interface).
- """
- # constant: type of this CSSValue class
- cssValueType = CSSValue.CSS_PRIMITIVE_VALUE
-
- __types = cssutils.cssproductions.CSSProductions
-
- # An integer indicating which type of unit applies to the value.
- CSS_UNKNOWN = 0 # only obtainable via cssText
- CSS_NUMBER = 1
- CSS_PERCENTAGE = 2
- CSS_EMS = 3
- CSS_EXS = 4
- CSS_PX = 5
- CSS_CM = 6
- CSS_MM = 7
- CSS_IN = 8
- CSS_PT = 9
- CSS_PC = 10
- CSS_DEG = 11
- CSS_RAD = 12
- CSS_GRAD = 13
- CSS_MS = 14
- CSS_S = 15
- CSS_HZ = 16
- CSS_KHZ = 17
- CSS_DIMENSION = 18
- CSS_STRING = 19
- CSS_URI = 20
- CSS_IDENT = 21
- CSS_ATTR = 22
- CSS_COUNTER = 23
- CSS_RECT = 24
- CSS_RGBCOLOR = 25
- # NOT OFFICIAL:
- CSS_RGBACOLOR = 26
- CSS_UNICODE_RANGE = 27
-
- _floattypes = (CSS_NUMBER, CSS_PERCENTAGE, CSS_EMS, CSS_EXS,
- CSS_PX, CSS_CM, CSS_MM, CSS_IN, CSS_PT, CSS_PC,
- CSS_DEG, CSS_RAD, CSS_GRAD, CSS_MS, CSS_S,
- CSS_HZ, CSS_KHZ, CSS_DIMENSION)
- _stringtypes = (CSS_ATTR, CSS_IDENT, CSS_STRING, CSS_URI)
- _countertypes = (CSS_COUNTER,)
- _recttypes = (CSS_RECT,)
- _rbgtypes = (CSS_RGBCOLOR, CSS_RGBACOLOR)
- _lengthtypes = (CSS_NUMBER, CSS_EMS, CSS_EXS,
- CSS_PX, CSS_CM, CSS_MM, CSS_IN, CSS_PT, CSS_PC)
-
- # oldtype: newType: converterfunc
- _converter = {
- # cm <-> mm <-> in, 1 inch is equal to 2.54 centimeters.
- # pt <-> pc, the points used by CSS 2.1 are equal to 1/72nd of an inch.
- # pc: picas - 1 pica is equal to 12 points
- (CSS_CM, CSS_MM): lambda x: x * 10,
- (CSS_MM, CSS_CM): lambda x: x / 10,
-
- (CSS_PT, CSS_PC): lambda x: x * 12,
- (CSS_PC, CSS_PT): lambda x: x / 12,
-
- (CSS_CM, CSS_IN): lambda x: x / 2.54,
- (CSS_IN, CSS_CM): lambda x: x * 2.54,
- (CSS_MM, CSS_IN): lambda x: x / 25.4,
- (CSS_IN, CSS_MM): lambda x: x * 25.4,
-
- (CSS_IN, CSS_PT): lambda x: x / 72,
- (CSS_PT, CSS_IN): lambda x: x * 72,
- (CSS_CM, CSS_PT): lambda x: x / 2.54 / 72,
- (CSS_PT, CSS_CM): lambda x: x * 72 * 2.54,
- (CSS_MM, CSS_PT): lambda x: x / 25.4 / 72,
- (CSS_PT, CSS_MM): lambda x: x * 72 * 25.4,
-
- (CSS_IN, CSS_PC): lambda x: x / 72 / 12,
- (CSS_PC, CSS_IN): lambda x: x * 12 * 72,
- (CSS_CM, CSS_PC): lambda x: x / 2.54 / 72 / 12,
- (CSS_PC, CSS_CM): lambda x: x * 12 * 72 * 2.54,
- (CSS_MM, CSS_PC): lambda x: x / 25.4 / 72 / 12,
- (CSS_PC, CSS_MM): lambda x: x * 12 * 72 * 25.4,
-
- # hz <-> khz
- (CSS_KHZ, CSS_HZ): lambda x: x * 1000,
- (CSS_HZ, CSS_KHZ): lambda x: x / 1000,
- # s <-> ms
- (CSS_S, CSS_MS): lambda x: x * 1000,
- (CSS_MS, CSS_S): lambda x: x / 1000,
-
- (CSS_RAD, CSS_DEG): lambda x: math.degrees(x),
- (CSS_DEG, CSS_RAD): lambda x: math.radians(x),
- # TODO: convert grad <-> deg or rad
- #(CSS_RAD, CSS_GRAD): lambda x: math.degrees(x),
- #(CSS_DEG, CSS_GRAD): lambda x: math.radians(x),
- #(CSS_GRAD, CSS_RAD): lambda x: math.radians(x),
- #(CSS_GRAD, CSS_DEG): lambda x: math.radians(x)
- }
-
- def __init__(self, cssText=None, parent=None, readonly=False):
- """See CSSPrimitiveValue.__init__()"""
- super(CSSPrimitiveValue, self).__init__(cssText=cssText,
- parent=parent,
- readonly=readonly)
-
- def __str__(self):
- return u""\
- % (self.__class__.__name__,
- self.primitiveTypeString,
- self.cssText,
- id(self))
-
- _unitnames = ['CSS_UNKNOWN',
- 'CSS_NUMBER', 'CSS_PERCENTAGE',
- 'CSS_EMS', 'CSS_EXS',
- 'CSS_PX',
- 'CSS_CM', 'CSS_MM',
- 'CSS_IN',
- 'CSS_PT', 'CSS_PC',
- 'CSS_DEG', 'CSS_RAD', 'CSS_GRAD',
- 'CSS_MS', 'CSS_S',
- 'CSS_HZ', 'CSS_KHZ',
- 'CSS_DIMENSION',
- 'CSS_STRING', 'CSS_URI', 'CSS_IDENT',
- 'CSS_ATTR', 'CSS_COUNTER', 'CSS_RECT',
- 'CSS_RGBCOLOR', 'CSS_RGBACOLOR',
- 'CSS_UNICODE_RANGE'
- ]
-
- _reNumDim = re.compile(ur'([+-]?\d*\.\d+|[+-]?\d+)(.*)$', re.I | re.U | re.X)
-
- def _unitDIMENSION(value):
- """Check val for dimension name."""
- units = {'em': 'CSS_EMS', 'ex': 'CSS_EXS',
- 'px': 'CSS_PX',
- 'cm': 'CSS_CM', 'mm': 'CSS_MM',
- 'in': 'CSS_IN',
- 'pt': 'CSS_PT', 'pc': 'CSS_PC',
- 'deg': 'CSS_DEG', 'rad': 'CSS_RAD', 'grad': 'CSS_GRAD',
- 'ms': 'CSS_MS', 's': 'CSS_S',
- 'hz': 'CSS_HZ', 'khz': 'CSS_KHZ'
- }
- val, dim = CSSPrimitiveValue._reNumDim.findall(cssutils.helper.normalize(value))[0]
- return units.get(dim, 'CSS_DIMENSION')
-
- def _unitFUNCTION(value):
- """Check val for function name."""
- units = {'attr(': 'CSS_ATTR',
- 'counter(': 'CSS_COUNTER',
- 'rect(': 'CSS_RECT',
- 'rgb(': 'CSS_RGBCOLOR',
- 'rgba(': 'CSS_RGBACOLOR',
- }
- return units.get(re.findall(ur'^(.*?\()',
- cssutils.helper.normalize(value.cssText),
- re.U)[0],
- 'CSS_UNKNOWN')
-
- __unitbytype = {
- __types.NUMBER: 'CSS_NUMBER',
- __types.PERCENTAGE: 'CSS_PERCENTAGE',
- __types.STRING: 'CSS_STRING',
- __types.UNICODE_RANGE: 'CSS_UNICODE_RANGE',
- __types.URI: 'CSS_URI',
- __types.IDENT: 'CSS_IDENT',
- __types.HASH: 'CSS_RGBCOLOR',
- __types.DIMENSION: _unitDIMENSION,
- __types.FUNCTION: _unitFUNCTION
- }
-
- def __set_primitiveType(self):
- """primitiveType is readonly but is set lazy if accessed"""
- # TODO: check unary and font-family STRING a, b, "c"
- val, type_ = self._value
- # try get by type_
- pt = self.__unitbytype.get(type_, 'CSS_UNKNOWN')
- if callable(pt):
- # multiple options, check value too
- pt = pt(val)
- self._primitiveType = getattr(self, pt)
-
- def _getPrimitiveType(self):
- if not hasattr(self, '_primitivetype'):
- self.__set_primitiveType()
- return self._primitiveType
-
- primitiveType = property(_getPrimitiveType,
- doc="(readonly) The type of the value as defined "
- "by the constants in this class.")
-
- def _getPrimitiveTypeString(self):
- return self._unitnames[self.primitiveType]
-
- primitiveTypeString = property(_getPrimitiveTypeString,
- doc="Name of primitive type of this value.")
-
- def _getCSSPrimitiveTypeString(self, type):
- "get TypeString by given type which may be unknown, used by setters"
- try:
- return self._unitnames[type]
- except (IndexError, TypeError):
- return u'%r (UNKNOWN TYPE)' % type
-
- def _getNumDim(self, value=None):
- "Split self._value in numerical and dimension part."
- if value is None:
- value = cssutils.helper.normalize(self._value[0])
-
- try:
- val, dim = CSSPrimitiveValue._reNumDim.findall(value)[0]
- except IndexError:
- val, dim = value, u''
- try:
- val = float(val)
- if val == int(val):
- val = int(val)
- except ValueError:
- raise xml.dom.InvalidAccessErr(
- u'CSSPrimitiveValue: No float value %r' % self._value[0])
-
- return val, dim
-
- def getFloatValue(self, unitType=None):
- """(DOM) This method is used to get a float value in a
- specified unit. If this CSS value doesn't contain a float value
- or can't be converted into the specified unit, a DOMException
- is raised.
-
- :param unitType:
- to get the float value. The unit code can only be a float unit type
- (i.e. CSS_NUMBER, CSS_PERCENTAGE, CSS_EMS, CSS_EXS, CSS_PX, CSS_CM,
- CSS_MM, CSS_IN, CSS_PT, CSS_PC, CSS_DEG, CSS_RAD, CSS_GRAD, CSS_MS,
- CSS_S, CSS_HZ, CSS_KHZ, CSS_DIMENSION) or None in which case
- the current dimension is used.
-
- :returns:
- not necessarily a float but some cases just an integer
- e.g. if the value is ``1px`` it return ``1`` and **not** ``1.0``
-
- Conversions might return strange values like 1.000000000001
- """
- if unitType is not None and unitType not in self._floattypes:
- raise xml.dom.InvalidAccessErr(
- u'unitType Parameter is not a float type')
-
- val, dim = self._getNumDim()
-
- if unitType is not None and self.primitiveType != unitType:
- # convert if needed
- try:
- val = self._converter[self.primitiveType, unitType](val)
- except KeyError:
- raise xml.dom.InvalidAccessErr(
- u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r'
- % (self.primitiveTypeString,
- self._getCSSPrimitiveTypeString(unitType)))
-
- if val == int(val):
- val = int(val)
-
- return val
-
- def setFloatValue(self, unitType, floatValue):
- """(DOM) A method to set the float value with a specified unit.
- If the property attached with this value can not accept the
- specified unit or the float value, the value will be unchanged and
- a DOMException will be raised.
-
- :param unitType:
- a unit code as defined above. The unit code can only be a float
- unit type
- :param floatValue:
- the new float value which does not have to be a float value but
- may simple be an int e.g. if setting::
-
- setFloatValue(CSS_PX, 1)
-
- :exceptions:
- - :exc:`~xml.dom.InvalidAccessErr`:
- Raised if the attached property doesn't
- support the float value or the unit type.
- - :exc:`~xml.dom.NoModificationAllowedErr`:
- Raised if this property is readonly.
- """
- self._checkReadonly()
- if unitType not in self._floattypes:
- raise xml.dom.InvalidAccessErr(
- u'CSSPrimitiveValue: unitType %r is not a float type' %
- self._getCSSPrimitiveTypeString(unitType))
- try:
- val = float(floatValue)
- except ValueError, e:
- raise xml.dom.InvalidAccessErr(
- u'CSSPrimitiveValue: floatValue %r is not a float' %
- floatValue)
-
- oldval, dim = self._getNumDim()
- if self.primitiveType != unitType:
- # convert if possible
- try:
- val = self._converter[unitType, self.primitiveType](val)
- except KeyError:
- raise xml.dom.InvalidAccessErr(
- u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r'
- % (self.primitiveTypeString,
- self._getCSSPrimitiveTypeString(unitType)))
-
- if val == int(val):
- val = int(val)
-
- self.cssText = '%s%s' % (val, dim)
-
- def getStringValue(self):
- """(DOM) This method is used to get the string value. If the
- CSS value doesn't contain a string value, a DOMException is raised.
-
- Some properties (like 'font-family' or 'voice-family')
- convert a whitespace separated list of idents to a string.
-
- Only the actual value is returned so e.g. all the following return the
- actual value ``a``: url(a), attr(a), "a", 'a'
- """
- if self.primitiveType not in self._stringtypes:
- raise xml.dom.InvalidAccessErr(
- u'CSSPrimitiveValue %r is not a string type'
- % self.primitiveTypeString)
-
- if CSSPrimitiveValue.CSS_ATTR == self.primitiveType:
- return self._value[0].cssText[5:-1]
- else:
- return self._value[0]
-
- def setStringValue(self, stringType, stringValue):
- """(DOM) A method to set the string value with the specified
- unit. If the property attached to this value can't accept the
- specified unit or the string value, the value will be unchanged and
- a DOMException will be raised.
-
- :param stringType:
- a string code as defined above. The string code can only be a
- string unit type (i.e. CSS_STRING, CSS_URI, CSS_IDENT, and
- CSS_ATTR).
- :param stringValue:
- the new string value
- Only the actual value is expected so for (CSS_URI, "a") the
- new value will be ``url(a)``. For (CSS_STRING, "'a'")
- the new value will be ``"\\'a\\'"`` as the surrounding ``'`` are
- not part of the string value
-
- :exceptions:
- - :exc:`~xml.dom.InvalidAccessErr`:
- Raised if the CSS value doesn't contain a
- string value or if the string value can't be converted into
- the specified unit.
-
- - :exc:`~xml.dom.NoModificationAllowedErr`:
- Raised if this property is readonly.
- """
- self._checkReadonly()
- # self not stringType
- if self.primitiveType not in self._stringtypes:
- raise xml.dom.InvalidAccessErr(
- u'CSSPrimitiveValue %r is not a string type'
- % self.primitiveTypeString)
- # given stringType is no StringType
- if stringType not in self._stringtypes:
- raise xml.dom.InvalidAccessErr(
- u'CSSPrimitiveValue: stringType %s is not a string type'
- % self._getCSSPrimitiveTypeString(stringType))
-
- if self._primitiveType != stringType:
- raise xml.dom.InvalidAccessErr(
- u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r'
- % (self.primitiveTypeString,
- self._getCSSPrimitiveTypeString(stringType)))
-
- if CSSPrimitiveValue.CSS_STRING == self._primitiveType:
- self.cssText = cssutils.helper.string(stringValue)
- elif CSSPrimitiveValue.CSS_URI == self._primitiveType:
- self.cssText = cssutils.helper.uri(stringValue)
- elif CSSPrimitiveValue.CSS_ATTR == self._primitiveType:
- self.cssText = u'attr(%s)' % stringValue
- else:
- self.cssText = stringValue
- self._primitiveType = stringType
-
- def getCounterValue(self):
- """(DOM) This method is used to get the Counter value. If
- this CSS value doesn't contain a counter value, a DOMException
- is raised. Modification to the corresponding style property
- can be achieved using the Counter interface.
-
- **Not implemented.**
- """
- if not self.CSS_COUNTER == self.primitiveType:
- raise xml.dom.InvalidAccessErr(u'Value is not a counter type')
- # TODO: use Counter class
- raise NotImplementedError()
-
- def getRGBColorValue(self):
- """(DOM) This method is used to get the RGB color. If this
- CSS value doesn't contain a RGB color value, a DOMException
- is raised. Modification to the corresponding style property
- can be achieved using the RGBColor interface.
- """
- if self.primitiveType not in self._rbgtypes:
- raise xml.dom.InvalidAccessErr(u'Value is not a RGBColor value')
- return RGBColor(self._value[0])
-
- def getRectValue(self):
- """(DOM) This method is used to get the Rect value. If this CSS
- value doesn't contain a rect value, a DOMException is raised.
- Modification to the corresponding style property can be achieved
- using the Rect interface.
-
- **Not implemented.**
- """
- if self.primitiveType not in self._recttypes:
- raise xml.dom.InvalidAccessErr(u'value is not a Rect value')
- # TODO: use Rect class
- raise NotImplementedError()
-
- def _getCssText(self):
- """Overwrites CSSValue."""
- return cssutils.ser.do_css_CSSPrimitiveValue(self)
-
- def _setCssText(self, cssText):
- """Use CSSValue."""
- return super(CSSPrimitiveValue, self)._setCssText(cssText)
-
- cssText = property(_getCssText, _setCssText,
- doc="A string representation of the current value.")
-
-
-class CSSValueList(CSSValue):
- """The CSSValueList interface provides the abstraction of an ordered
- collection of CSS values.
-
- Some properties allow an empty list into their syntax. In that case,
- these properties take the none identifier. So, an empty list means
- that the property has the value none.
-
- The items in the CSSValueList are accessible via an integral index,
- starting from 0.
- """
- cssValueType = CSSValue.CSS_VALUE_LIST
-
- def __init__(self, cssText=None, parent=None, readonly=False):
- """Init a new CSSValueList"""
- super(CSSValueList, self).__init__(cssText=cssText,
- parent=parent,
- readonly=readonly)
- self._items = []
-
- def __iter__(self):
- "CSSValueList is iterable."
- for item in self.__items():
- yield item.value
-
- def __str__(self):
- return u"" % (self.__class__.__name__,
- self.cssValueTypeString,
- self.cssText,
- self.length,
- id(self))
-
- def __items(self):
- return [item for item in self._seq
- if isinstance(item.value, CSSValue)]
-
- def item(self, index):
- """(DOM) Retrieve a CSSValue by ordinal `index`. The
- order in this collection represents the order of the values in the
- CSS style property. If `index` is greater than or equal to the number
- of values in the list, this returns ``None``.
- """
- try:
- return self.__items()[index].value
- except IndexError:
- return None
-
- length = property(lambda self: len(self.__items()),
- doc=u"(DOM attribute) The number of CSSValues in the "
- u"list.")
-
-
-class CSSFunction(CSSPrimitiveValue):
- """A CSS function value like rect() etc."""
- _functionName = u'CSSFunction'
- primitiveType = CSSPrimitiveValue.CSS_UNKNOWN
-
- def __init__(self, cssText=None, parent=None, readonly=False):
- """
- Init a new CSSFunction
-
- :param cssText:
- the parsable cssText of the value
- :param readonly:
- defaults to False
- """
- super(CSSFunction, self).__init__(parent=parent)
- self._funcType = None
- self.valid = False
- self.wellformed = False
- if cssText is not None:
- self.cssText = cssText
- self._readonly = readonly
-
- def _productiondefinition(self):
- """Return definition used for parsing."""
- types = self._prods # rename!
-
- value = Sequence(PreDef.unary(),
- Prod(name='PrimitiveValue',
- match=lambda t, v: t in (types.DIMENSION,
- types.HASH,
- types.IDENT,
- types.NUMBER,
- types.PERCENTAGE,
- types.STRING),
- toSeq=lambda t, tokens: (t[0],
- CSSPrimitiveValue(t[1]))
- )
- )
- valueOrFunc = Choice(value,
- # FUNC is actually not in spec but used in e.g. Prince
- PreDef.function(toSeq=lambda t,
- tokens: ('FUNCTION',
- CSSFunction(
- cssutils.helper.pushtoken(t, tokens))
- )
- )
- )
- funcProds = Sequence(Prod(name='FUNC',
- match=lambda t, v: t == types.FUNCTION,
- toSeq=lambda t, tokens: (t[0], cssutils.helper.normalize(t[1]))),
- Choice(Sequence(valueOrFunc,
- # more values starting with Comma
- # should use store where colorType is saved to
- # define min and may, closure?
- Sequence(PreDef.comma(),
- valueOrFunc,
- minmax=lambda: (0, None)),
- PreDef.funcEnd(stop=True)),
- PreDef.funcEnd(stop=True))
- )
- return funcProds
-
- def _setCssText(self, cssText):
- self._checkReadonly()
- # store: colorType, parts
- wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
- self._functionName,
- self._productiondefinition(),
- keepS=True)
- if wellformed:
- # combine +/- and following CSSPrimitiveValue, remove S
- newseq = self._tempSeq()
- i, end = 0, len(seq)
- while i < end:
- item = seq[i]
- if item.type == self._prods.S:
- pass
- elif item.value == u'+' or item.value == u'-':
- i += 1
- next = seq[i]
- newval = next.value
- if isinstance(newval, CSSPrimitiveValue):
- newval.setFloatValue(newval.primitiveType,
- float(item.value + str(newval.getFloatValue())))
- newseq.append(newval, next.type,
- item.line, item.col)
- else:
- # expressions only?
- newseq.appendItem(item)
- newseq.appendItem(next)
- else:
- newseq.appendItem(item)
-
- i += 1
-
- self.wellformed = True
- self._setSeq(newseq)
- self._funcType = newseq[0].value
-
- cssText = property(lambda self: cssutils.ser.do_css_FunctionValue(self),
- _setCssText)
-
- funcType = property(lambda self: self._funcType)
-
-
-class RGBColor(CSSFunction):
- """A CSS color like RGB, RGBA or a simple value like `#000` or `red`."""
-
- _functionName = u'Function rgb()'
-
- def __init__(self, cssText=None, parent=None, readonly=False):
- """
- Init a new RGBColor
-
- :param cssText:
- the parsable cssText of the value
- :param readonly:
- defaults to False
- """
- super(CSSFunction, self).__init__(parent=parent)
- self._colorType = None
- self.valid = False
- self.wellformed = False
- if cssText is not None:
- try:
- # if it is a Function object
- cssText = cssText.cssText
- except AttributeError:
- pass
- self.cssText = cssText
-
- self._readonly = readonly
-
- def __repr__(self):
- return u"cssutils.css.%s(%r)" % (self.__class__.__name__,
- self.cssText)
-
- def __str__(self):
- return u"" % (
- self.__class__.__name__,
- self.colorType,
- self.cssText,
- id(self))
-
- def _setCssText(self, cssText):
- self._checkReadonly()
- types = self._prods # rename!
- valueProd = Prod(name='value',
- match=lambda t, v: t in (types.NUMBER, types.PERCENTAGE),
- toSeq=lambda t, v: (CSSPrimitiveValue, CSSPrimitiveValue(v)),
- toStore='parts'
- )
- # COLOR PRODUCTION
- funccolor = Sequence(Prod(name='FUNC',
- match=lambda t, v: t == types.FUNCTION and cssutils.helper.normalize(v) in ('rgb(', 'rgba(', 'hsl(', 'hsla('),
- toSeq=lambda t, v: (t, v),#cssutils.helper.normalize(v)),
- toStore='colorType'),
- PreDef.unary(),
- valueProd,
- # 2 or 3 more values starting with Comma
- Sequence(PreDef.comma(),
- PreDef.unary(),
- valueProd,
- minmax=lambda: (2, 3)),
- PreDef.funcEnd()
- )
- colorprods = Choice(funccolor,
- PreDef.hexcolor('colorType'),
- Prod(name='named color',
- match=lambda t, v: t == types.IDENT,
- toStore='colorType'
- )
- )
- # store: colorType, parts
- wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
- u'RGBColor',
- colorprods,
- keepS=True,
- store={'parts': []})
-
- if wellformed:
- self.wellformed = True
- if store['colorType'].type == self._prods.HASH:
- self._colorType = 'HEX'
- elif store['colorType'].type == self._prods.IDENT:
- self._colorType = 'Named Color'
- else:
- self._colorType = store['colorType'].value[:-1]
- #self._colorType = cssutils.helper.normalize(store['colorType'].value)[:-1]
-
- self._setSeq(seq)
-
- cssText = property(lambda self: cssutils.ser.do_css_RGBColor(self),
- _setCssText)
-
- colorType = property(lambda self: self._colorType)
-
-
-class CalcValue(CSSFunction):
- """Calc Function"""
- _functionName = u'Function calc()'
-
- def _productiondefinition(self):
- """Return defintion used for parsing."""
- types = self._prods # rename!
-
- def toSeq(t, tokens):
- "Do not normalize function name!"
- return t[0], t[1]
-
- funcProds = Sequence(Prod(name='calc',
- match=lambda t, v: t == types.FUNCTION,
- toSeq=toSeq
- ),
- Sequence(Choice(Prod(name='nested function',
- match=lambda t, v: t == self._prods.FUNCTION,
- toSeq=lambda t, tokens: (CSSFunction._functionName,
- CSSFunction(cssutils.helper.pushtoken(t,
- tokens)))
- ),
- Prod(name='part',
- match=lambda t, v: v != u')',
- toSeq=lambda t, tokens: (t[0], t[1])),
- ),
- minmax=lambda: (0, None)),
- PreDef.funcEnd(stop=True))
- return funcProds
-
- def _getCssText(self):
- return cssutils.ser.do_css_CalcValue(self)
-
- def _setCssText(self, cssText):
- return super(CalcValue, self)._setCssText(cssText)
-
- cssText = property(_getCssText, _setCssText,
- doc=u"A string representation of the current value.")
-
-
-class ExpressionValue(CSSFunction):
- """Special IE only CSSFunction which may contain *anything*.
- Used for expressions and ``alpha(opacity=100)`` currently."""
- _functionName = u'Expression (IE only)'
-
- def _productiondefinition(self):
- """Return defintion used for parsing."""
- types = self._prods # rename!
-
- def toSeq(t, tokens):
- "Do not normalize function name!"
- return t[0], t[1]
-
- funcProds = Sequence(Prod(name='expression',
- match=lambda t, v: t == types.FUNCTION,
- toSeq=toSeq
- ),
- Sequence(Choice(Prod(name='nested function',
- match=lambda t, v: t == self._prods.FUNCTION,
- toSeq=lambda t, tokens: (ExpressionValue._functionName,
- ExpressionValue(cssutils.helper.pushtoken(t,
- tokens)))
- ),
- Prod(name='part',
- match=lambda t, v: v != u')',
- toSeq=lambda t, tokens: (t[0], t[1])),
- ),
- minmax=lambda: (0, None)),
- PreDef.funcEnd(stop=True))
- return funcProds
-
- def _getCssText(self):
- return cssutils.ser.do_css_ExpressionValue(self)
-
- def _setCssText(self, cssText):
- #self._log.warn(u'CSSValue: Unoffial and probably invalid MS value used!')
- return super(ExpressionValue, self)._setCssText(cssText)
-
- cssText = property(_getCssText, _setCssText,
- doc=u"A string representation of the current value.")
-
-
-class CSSVariable(CSSValue):
- """The CSSVariable represents a call to CSS Variable."""
-
- def __init__(self, cssText=None, parent=None, readonly=False):
- """Init a new CSSVariable.
-
- :param cssText:
- the parsable cssText of the value, e.g. ``var(x)``
- :param readonly:
- defaults to False
- """
- self._name = None
- super(CSSVariable, self).__init__(cssText=cssText,
- parent=parent,
- readonly=readonly)
-
- def __repr__(self):
- return u"cssutils.css.%s(%r)" % (self.__class__.__name__, self.cssText)
-
- def __str__(self):
- return u"" % (
- self.__class__.__name__,
- self.name,
- self.value,
- id(self))
-
- def _setCssText(self, cssText):
- self._checkReadonly()
-
- types = self._prods # rename!
-
- funcProds = Sequence(Prod(name='var',
- match=lambda t, v: t == types.FUNCTION
- ),
- PreDef.ident(toStore='ident'),
- PreDef.funcEnd(stop=True))
-
- # store: name of variable
- store = {'ident': None}
- wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
- u'CSSVariable',
- funcProds,
- keepS=True)
- if wellformed:
- self._name = store['ident'].value
- self._setSeq(seq)
- self.wellformed = True
-
- cssText = property(lambda self: cssutils.ser.do_css_CSSVariable(self),
- _setCssText,
- doc=u"A string representation of the current variable.")
-
- cssValueType = CSSValue.CSS_VARIABLE
-
- # TODO: writable? check if var (value) available?
- name = property(lambda self: self._name)
-
- def _getValue(self):
- "Find contained sheet and @variables there"
- try:
- variables = self.parent.parent.parentRule.parentStyleSheet.variables
- except AttributeError:
- return None
- else:
- try:
- return variables[self.name]
- except KeyError:
- return None
-
- value = property(_getValue)
diff --git a/libs/cssutils/css/cssvariablesdeclaration.py b/libs/cssutils/css/cssvariablesdeclaration.py
deleted file mode 100755
index 2b33f670..00000000
--- a/libs/cssutils/css/cssvariablesdeclaration.py
+++ /dev/null
@@ -1,330 +0,0 @@
-"""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"" % (
- 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.")
diff --git a/libs/cssutils/css/cssvariablesrule.py b/libs/cssutils/css/cssvariablesrule.py
deleted file mode 100755
index ce5335bf..00000000
--- a/libs/cssutils/css/cssvariablesrule.py
+++ /dev/null
@@ -1,198 +0,0 @@
-"""CSSVariables implements (and only partly) experimental
-`CSS Variables `_
-"""
-__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"" % (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)
diff --git a/libs/cssutils/css/marginrule.py b/libs/cssutils/css/marginrule.py
deleted file mode 100755
index 0c789fad..00000000
--- a/libs/cssutils/css/marginrule.py
+++ /dev/null
@@ -1,215 +0,0 @@
-"""MarginRule implements DOM Level 2 CSS MarginRule."""
-__all__ = ['MarginRule']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-
-from cssutils.prodparser import *
-from cssstyledeclaration import CSSStyleDeclaration
-import cssrule
-import cssutils
-import xml.dom
-
-class MarginRule(cssrule.CSSRule):
- """
- A margin at-rule consists of an ATKEYWORD that identifies the margin box
- (e.g. '@top-left') and a block of declarations (said to be in the margin
- context).
-
- Format::
-
- 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
- ;
-
- e.g.::
-
- @top-left {
- content: "123";
- }
- """
- margins = ['@top-left-corner',
- '@top-left',
- '@top-center',
- '@top-right',
- '@top-right-corner',
- '@bottom-left-corner',
- '@bottom-left',
- '@bottom-center',
- '@bottom-right',
- '@bottom-right-corner',
- '@left-top',
- '@left-middle',
- '@left-bottom',
- '@right-top',
- '@right-middle',
- '@right-bottom'
- ]
-
- def __init__(self, margin=None, style=None, parentRule=None,
- parentStyleSheet=None, readonly=False):
- """
- :param atkeyword:
- The margin area, e.g. '@top-left' for this rule
- :param style:
- CSSStyleDeclaration for this MarginRule
- """
- super(MarginRule, self).__init__(parentRule=parentRule,
- parentStyleSheet=parentStyleSheet)
-
- self._atkeyword = self._keyword = None
-
- if margin:
- self.margin = margin
-
- if style:
- self.style = style
- else:
- self.style = CSSStyleDeclaration(parentRule=self)
-
- self._readonly = readonly
-
- def _setMargin(self, margin):
- """Check if new keyword fits the rule it is used for."""
- n = self._normalize(margin)
-
- if n not in MarginRule.margins:
- self._log.error(u'Invalid margin @keyword for this %s rule: %r' %
- (self.margin, margin),
- error=xml.dom.InvalidModificationErr)
-
- else:
- self._atkeyword = n
- self._keyword = margin
-
- margin = property(lambda self: self._atkeyword, _setMargin,
- doc=u"Margin area of parent CSSPageRule. "
- u"`margin` and `atkeyword` are both normalized "
- u"@keyword of the @rule.")
-
- atkeyword = margin
-
- def __repr__(self):
- return u"cssutils.css.%s(margin=%r, style=%r)" % (self.__class__.__name__,
- self.margin,
- self.style.cssText)
-
- def __str__(self):
- return u"" % (self.__class__.__name__,
- self.margin,
- self.style.cssText,
- id(self))
-
- def _getCssText(self):
- """Return serialized property cssText."""
- return cssutils.ser.do_MarginRule(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(MarginRule, self)._setCssText(cssText)
-
- # TEMP: all style tokens are saved in store to fill styledeclaration
- # TODO: resolve when all generators
- styletokens = Prod(name='styletokens',
- match=lambda t, v: v != u'}',
- #toSeq=False,
- toStore='styletokens',
- storeToken=True
- )
-
- prods = Sequence(Prod(name='@ margin',
- match=lambda t, v:
- t == 'ATKEYWORD' and
- self._normalize(v) in MarginRule.margins,
- toStore='margin'
- # TODO?
- #, exception=xml.dom.InvalidModificationErr
- ),
- PreDef.char('OPEN', u'{'),
- Sequence(Choice(PreDef.unknownrule(toStore='@'),
- styletokens),
- minmax=lambda: (0, None)
- ),
- PreDef.char('CLOSE', u'}', stopAndKeep=True)
- )
- # parse
- ok, seq, store, unused = ProdParser().parse(cssText,
- u'MarginRule',
- prods)
-
- if ok:
- # TODO: use seq for serializing instead of fixed stuff?
- self._setSeq(seq)
-
- if 'margin' in store:
- # may raise:
- self.margin = store['margin'].value
- else:
- self._log.error(u'No margin @keyword for this %s rule' %
- self.margin,
- error=xml.dom.InvalidModificationErr)
-
- # new empty style
- self.style = CSSStyleDeclaration(parentRule=self)
-
- if 'styletokens' in store:
- # may raise:
- self.style.cssText = store['styletokens']
-
-
- cssText = property(fget=_getCssText, fset=_setCssText,
- doc=u"(DOM) The parsable textual representation.")
-
- 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.MARGIN_RULE,
- doc=u"The type of this rule, as defined by a CSSRule "
- u"type constant.")
-
- wellformed = property(lambda self: bool(self.atkeyword))
-
\ No newline at end of file
diff --git a/libs/cssutils/css/property.py b/libs/cssutils/css/property.py
deleted file mode 100755
index fad240c0..00000000
--- a/libs/cssutils/css/property.py
+++ /dev/null
@@ -1,510 +0,0 @@
-"""Property is a single CSS property in a CSSStyleDeclaration."""
-__all__ = ['Property']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-
-from cssutils.helper import Deprecated
-from value import PropertyValue
-import cssutils
-import xml.dom
-
-class Property(cssutils.util.Base):
- """A CSS property in a StyleDeclaration of a CSSStyleRule (cssutils).
-
- Format::
-
- property = name
- : IDENT S*
- ;
-
- expr = value
- : term [ operator term ]*
- ;
- term
- : unary_operator?
- [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* |
- ANGLE S* | TIME S* | FREQ S* | function ]
- | STRING S* | IDENT S* | URI S* | hexcolor
- ;
- function
- : FUNCTION S* expr ')' S*
- ;
- /*
- * There is a constraint on the color that it must
- * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
- * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
- */
- hexcolor
- : HASH S*
- ;
-
- prio
- : IMPORTANT_SYM S*
- ;
-
- """
- def __init__(self, name=None, value=None, priority=u'',
- _mediaQuery=False, parent=None):
- """
- :param name:
- a property name string (will be normalized)
- :param value:
- a property value string
- :param priority:
- an optional priority string which currently must be u'',
- u'!important' or u'important'
- :param _mediaQuery:
- if ``True`` value is optional (used by MediaQuery)
- :param parent:
- the parent object, normally a
- :class:`cssutils.css.CSSStyleDeclaration`
- """
- super(Property, self).__init__()
- self.seqs = [[], None, []]
- self.wellformed = False
- self._mediaQuery = _mediaQuery
- self.parent = parent
-
- self.__nametoken = None
- self._name = u''
- self._literalname = u''
- self.seqs[1] = PropertyValue(parent=self)
- if name:
- self.name = name
- self.propertyValue = value
-
- self._priority = u''
- self._literalpriority = u''
- if priority:
- self.priority = priority
-
- def __repr__(self):
- return u"cssutils.css.%s(name=%r, value=%r, priority=%r)" % (
- self.__class__.__name__,
- self.literalname,
- self.propertyValue.cssText,
- self.priority)
-
- def __str__(self):
- return u"<%s.%s object name=%r value=%r priority=%r valid=%r at 0x%x>" \
- % (self.__class__.__module__,
- self.__class__.__name__,
- self.name,
- self.propertyValue.cssText,
- self.priority,
- self.valid,
- id(self))
-
- def _isValidating(self):
- """Return True if validation is enabled."""
- try:
- return self.parent.validating
- except AttributeError:
- # default (no parent)
- return True
-
- def _getCssText(self):
- """Return serialized property cssText."""
- return cssutils.ser.do_Property(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.NoModificationAllowedErr`:
- Raised if the rule is readonly.
- """
- # check and prepare tokenlists for setting
- tokenizer = self._tokenize2(cssText)
- nametokens = self._tokensupto2(tokenizer, propertynameendonly=True)
- if nametokens:
- wellformed = True
-
- valuetokens = self._tokensupto2(tokenizer,
- propertyvalueendonly=True)
- prioritytokens = self._tokensupto2(tokenizer,
- propertypriorityendonly=True)
-
- if self._mediaQuery and not valuetokens:
- # MediaQuery may consist of name only
- self.name = nametokens
- self.propertyValue = None
- self.priority = None
- return
-
- # remove colon from nametokens
- colontoken = nametokens.pop()
- if self._tokenvalue(colontoken) != u':':
- wellformed = False
- self._log.error(u'Property: No ":" after name found: %s' %
- self._valuestr(cssText), colontoken)
- elif not nametokens:
- wellformed = False
- self._log.error(u'Property: No property name found: %s' %
- self._valuestr(cssText), colontoken)
-
- if valuetokens:
- if self._tokenvalue(valuetokens[-1]) == u'!':
- # priority given, move "!" to prioritytokens
- prioritytokens.insert(0, valuetokens.pop(-1))
- else:
- wellformed = False
- self._log.error(u'Property: No property value found: %s' %
- self._valuestr(cssText), colontoken)
-
- if wellformed:
- self.wellformed = True
- self.name = nametokens
- self.propertyValue = valuetokens
- self.priority = prioritytokens
-
- # also invalid values are set!
-
- if self._isValidating():
- self.validate()
-
- else:
- self._log.error(u'Property: No property name found: %s' %
- self._valuestr(cssText))
-
- cssText = property(fget=_getCssText, fset=_setCssText,
- doc="A parsable textual representation.")
-
- def _setName(self, name):
- """
- :exceptions:
- - :exc:`~xml.dom.SyntaxErr`:
- Raised if the specified name has a syntax error and is
- unparsable.
- """
- # for closures: must be a mutable
- new = {'literalname': None,
- 'wellformed': True}
-
- def _ident(expected, seq, token, tokenizer=None):
- # name
- if 'name' == expected:
- new['literalname'] = self._tokenvalue(token).lower()
- seq.append(new['literalname'])
- return 'EOF'
- else:
- new['wellformed'] = False
- self._log.error(u'Property: Unexpected ident.', token)
- return expected
-
- newseq = []
- wellformed, expected = self._parse(expected='name',
- seq=newseq,
- tokenizer=self._tokenize2(name),
- productions={'IDENT': _ident})
- wellformed = wellformed and new['wellformed']
-
- # post conditions
- # define a token for error logging
- if isinstance(name, list):
- token = name[0]
- self.__nametoken = token
- else:
- token = None
-
- if not new['literalname']:
- wellformed = False
- self._log.error(u'Property: No name found: %s' %
- self._valuestr(name), token=token)
-
- if wellformed:
- self.wellformed = True
- self._literalname = new['literalname']
- self._name = self._normalize(self._literalname)
- self.seqs[0] = newseq
-
- # validate
- if self._isValidating() and self._name not in cssutils.profile.knownNames:
- # self.valid = False
- self._log.warn(u'Property: Unknown Property name.',
- token=token, neverraise=True)
- else:
- pass
-# self.valid = True
-# if self.propertyValue:
-# self.propertyValue._propertyName = self._name
-# #self.valid = self.propertyValue.valid
- else:
- self.wellformed = False
-
- name = property(lambda self: self._name, _setName,
- doc="Name of this property.")
-
- literalname = property(lambda self: self._literalname,
- doc="Readonly literal (not normalized) name "
- "of this property")
-
- def _setPropertyValue(self, cssText):
- """
- See css.PropertyValue
-
- :exceptions:
- - :exc:`~xml.dom.SyntaxErr`:
- Raised if the specified CSS string value has a syntax error
- (according to the attached property) or is unparsable.
- - :exc:`~xml.dom.InvalidModificationErr`:
- TODO: Raised if the specified CSS string value represents a different
- type of values than the values allowed by the CSS property.
- """
- if self._mediaQuery and not cssText:
- self.seqs[1] = PropertyValue(parent=self)
- else:
- self.seqs[1].cssText = cssText
- self.wellformed = self.wellformed and self.seqs[1].wellformed
-
- propertyValue = property(lambda self: self.seqs[1],
- _setPropertyValue,
- doc=u"(cssutils) PropertyValue object of property")
-
-
- def _getValue(self):
- if self.propertyValue:
- # value without comments
- return self.propertyValue.value
- else:
- return u''
-
- def _setValue(self, value):
- self._setPropertyValue(value)
-
- value = property(_getValue, _setValue,
- doc="The textual value of this Properties propertyValue.")
-
- def _setPriority(self, priority):
- """
- priority
- a string, currently either u'', u'!important' or u'important'
-
- Format::
-
- prio
- : IMPORTANT_SYM S*
- ;
-
- "!"{w}"important" {return IMPORTANT_SYM;}
-
- :exceptions:
- - :exc:`~xml.dom.SyntaxErr`:
- Raised if the specified priority has a syntax error and is
- unparsable.
- In this case a priority not equal to None, "" or "!{w}important".
- As CSSOM defines CSSStyleDeclaration.getPropertyPriority resulting
- in u'important' this value is also allowed to set a Properties
- priority
- """
- if self._mediaQuery:
- self._priority = u''
- self._literalpriority = u''
- if priority:
- self._log.error(u'Property: No priority in a MediaQuery - '
- u'ignored.')
- return
-
- if isinstance(priority, basestring) and\
- u'important' == self._normalize(priority):
- priority = u'!%s' % priority
-
- # for closures: must be a mutable
- new = {'literalpriority': u'',
- 'wellformed': True}
-
- def _char(expected, seq, token, tokenizer=None):
- # "!"
- val = self._tokenvalue(token)
- if u'!' == expected == val:
- seq.append(val)
- return 'important'
- else:
- new['wellformed'] = False
- self._log.error(u'Property: Unexpected char.', token)
- return expected
-
- def _ident(expected, seq, token, tokenizer=None):
- # "important"
- val = self._tokenvalue(token)
- if 'important' == expected:
- new['literalpriority'] = val
- seq.append(val)
- return 'EOF'
- else:
- new['wellformed'] = False
- self._log.error(u'Property: Unexpected ident.', token)
- return expected
-
- newseq = []
- wellformed, expected = self._parse(expected='!',
- seq=newseq,
- tokenizer=self._tokenize2(priority),
- productions={'CHAR': _char,
- 'IDENT': _ident})
- wellformed = wellformed and new['wellformed']
-
- # post conditions
- if priority and not new['literalpriority']:
- wellformed = False
- self._log.info(u'Property: Invalid priority: %s' %
- self._valuestr(priority))
-
- if wellformed:
- self.wellformed = self.wellformed and wellformed
- self._literalpriority = new['literalpriority']
- self._priority = self._normalize(self.literalpriority)
- self.seqs[2] = newseq
- # validate priority
- if self._priority not in (u'', u'important'):
- self._log.error(u'Property: No CSS priority value: %s' %
- self._priority)
-
- priority = property(lambda self: self._priority, _setPriority,
- doc="Priority of this property.")
-
- literalpriority = property(lambda self: self._literalpriority,
- doc="Readonly literal (not normalized) priority of this property")
-
- def _setParent(self, parent):
- self._parent = parent
-
- parent = property(lambda self: self._parent, _setParent,
- doc="The Parent Node (normally a CSSStyledeclaration) of this "
- "Property")
-
- def validate(self):
- """Validate value against `profiles` which are checked dynamically.
- properties in e.g. @font-face rules are checked against
- ``cssutils.profile.CSS3_FONT_FACE`` only.
-
- For each of the following cases a message is reported:
-
- - INVALID (so the property is known but not valid)
- ``ERROR Property: Invalid value for "{PROFILE-1[/PROFILE-2...]"
- property: ...``
-
- - VALID but not in given profiles or defaultProfiles
- ``WARNING Property: Not valid for profile "{PROFILE-X}" but valid
- "{PROFILE-Y}" property: ...``
-
- - VALID in current profile
- ``DEBUG Found valid "{PROFILE-1[/PROFILE-2...]" property...``
-
- - UNKNOWN property
- ``WARNING Unknown Property name...`` is issued
-
- so for example::
-
- cssutils.log.setLevel(logging.DEBUG)
- parser = cssutils.CSSParser()
- s = parser.parseString('''body {
- unknown-property: x;
- color: 4;
- color: rgba(1,2,3,4);
- color: red
- }''')
-
- # Log output:
-
- WARNING Property: Unknown Property name. [2:9: unknown-property]
- ERROR Property: Invalid value for "CSS Color Module Level 3/CSS Level 2.1" property: 4 [3:9: color]
- DEBUG Property: Found valid "CSS Color Module Level 3" value: rgba(1, 2, 3, 4) [4:9: color]
- DEBUG Property: Found valid "CSS Level 2.1" value: red [5:9: color]
-
-
- and when setting an explicit default profile::
-
- cssutils.profile.defaultProfiles = cssutils.profile.CSS_LEVEL_2
- s = parser.parseString('''body {
- unknown-property: x;
- color: 4;
- color: rgba(1,2,3,4);
- color: red
- }''')
-
- # Log output:
-
- WARNING Property: Unknown Property name. [2:9: unknown-property]
- ERROR Property: Invalid value for "CSS Color Module Level 3/CSS Level 2.1" property: 4 [3:9: color]
- WARNING Property: Not valid for profile "CSS Level 2.1" but valid "CSS Color Module Level 3" value: rgba(1, 2, 3, 4) [4:9: color]
- DEBUG Property: Found valid "CSS Level 2.1" value: red [5:9: color]
- """
- valid = False
-
- profiles = None
- try:
- # if @font-face use that profile
- rule = self.parent.parentRule
- except AttributeError:
- pass
- else:
- if rule is not None:
- if rule.type == rule.FONT_FACE_RULE:
- profiles = [cssutils.profile.CSS3_FONT_FACE]
- #TODO: same for @page
-
- if self.name and self.value:
-
- cv = self.propertyValue
- # TODO
-# if cv.cssValueType == cv.CSS_VARIABLE and not cv.value:
-# # TODO: false alarms too!
-# cssutils.log.warn(u'No value for variable "%s" found, keeping '
-# u'variable.' % cv.name, neverraise=True)
-
- if self.name in cssutils.profile.knownNames:
- # add valid, matching, validprofiles...
- valid, matching, validprofiles = \
- cssutils.profile.validateWithProfile(self.name,
- self.value,
- profiles)
-
- if not valid:
- self._log.error(u'Property: Invalid value for '
- u'"%s" property: %s'
- % (u'/'.join(validprofiles), self.value),
- token=self.__nametoken,
- neverraise=True)
-
- # TODO: remove logic to profiles!
- elif valid and not matching:#(profiles and profiles not in validprofiles):
- if not profiles:
- notvalidprofiles = u'/'.join(cssutils.profile.defaultProfiles)
- else:
- notvalidprofiles = profiles
- self._log.warn(u'Property: Not valid for profile "%s" '
- u'but valid "%s" value: %s '
- % (notvalidprofiles, u'/'.join(validprofiles),
- self.value),
- token = self.__nametoken,
- neverraise=True)
- valid = False
-
- elif valid:
- self._log.debug(u'Property: Found valid "%s" value: %s'
- % (u'/'.join(validprofiles), self.value),
- token = self.__nametoken,
- neverraise=True)
-
- if self._priority not in (u'', u'important'):
- valid = False
-
- return valid
-
- valid = property(validate, doc=u"Check if value of this property is valid "
- u"in the properties context.")
-
-
- @Deprecated(u'Use ``property.propertyValue`` instead.')
- def _getCSSValue(self):
- return self.propertyValue
-
- @Deprecated(u'Use ``property.propertyValue`` instead.')
- def _setCSSValue(self, cssText):
- self._setPropertyValue(cssText)
-
- cssValue = property(_getCSSValue, _setCSSValue,
- doc="(DEPRECATED) Use ``property.propertyValue`` instead.")
diff --git a/libs/cssutils/css/selector.py b/libs/cssutils/css/selector.py
deleted file mode 100755
index 87840a0d..00000000
--- a/libs/cssutils/css/selector.py
+++ /dev/null
@@ -1,813 +0,0 @@
-"""Selector is a single Selector of a CSSStyleRule SelectorList.
-Partly implements http://www.w3.org/TR/css3-selectors/.
-
-TODO
- - .contains(selector)
- - .isSubselector(selector)
-"""
-__all__ = ['Selector']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-
-from cssutils.helper import Deprecated
-from cssutils.util import _SimpleNamespaces
-import cssutils
-import xml.dom
-
-class Selector(cssutils.util.Base2):
- """
- (cssutils) a single selector in a :class:`~cssutils.css.SelectorList`
- of a :class:`~cssutils.css.CSSStyleRule`.
-
- Format::
-
- # implemented in SelectorList
- selectors_group
- : selector [ COMMA S* selector ]*
- ;
-
- selector
- : simple_selector_sequence [ combinator simple_selector_sequence ]*
- ;
-
- combinator
- /* combinators can be surrounded by white space */
- : PLUS S* | GREATER S* | TILDE S* | S+
- ;
-
- simple_selector_sequence
- : [ type_selector | universal ]
- [ HASH | class | attrib | pseudo | negation ]*
- | [ HASH | class | attrib | pseudo | negation ]+
- ;
-
- type_selector
- : [ namespace_prefix ]? element_name
- ;
-
- namespace_prefix
- : [ IDENT | '*' ]? '|'
- ;
-
- element_name
- : IDENT
- ;
-
- universal
- : [ namespace_prefix ]? '*'
- ;
-
- class
- : '.' IDENT
- ;
-
- attrib
- : '[' S* [ namespace_prefix ]? IDENT S*
- [ [ PREFIXMATCH |
- SUFFIXMATCH |
- SUBSTRINGMATCH |
- '=' |
- INCLUDES |
- DASHMATCH ] S* [ IDENT | STRING ] S*
- ]? ']'
- ;
-
- pseudo
- /* '::' starts a pseudo-element, ':' a pseudo-class */
- /* Exceptions: :first-line, :first-letter, :before and :after. */
- /* Note that pseudo-elements are restricted to one per selector and */
- /* occur only in the last simple_selector_sequence. */
- : ':' ':'? [ IDENT | functional_pseudo ]
- ;
-
- functional_pseudo
- : FUNCTION S* expression ')'
- ;
-
- expression
- /* In CSS3, the expressions are identifiers, strings, */
- /* or of the form "an+b" */
- : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
- ;
-
- negation
- : NOT S* negation_arg S* ')'
- ;
-
- negation_arg
- : type_selector | universal | HASH | class | attrib | pseudo
- ;
-
- """
- def __init__(self, selectorText=None, parent=None,
- readonly=False):
- """
- :Parameters:
- selectorText
- initial value of this selector
- parent
- a SelectorList
- readonly
- default to False
- """
- super(Selector, self).__init__()
-
- self.__namespaces = _SimpleNamespaces(log=self._log)
- self._element = None
- self._parent = parent
- self._specificity = (0, 0, 0, 0)
-
- if selectorText:
- self.selectorText = selectorText
-
- self._readonly = readonly
-
- def __repr__(self):
- if self.__getNamespaces():
- st = (self.selectorText, self._getUsedNamespaces())
- else:
- st = self.selectorText
- return u"cssutils.css.%s(selectorText=%r)" % (self.__class__.__name__,
- st)
-
- def __str__(self):
- return u"" % (self.__class__.__name__,
- self.selectorText,
- self.specificity,
- self._getUsedNamespaces(),
- id(self))
-
- def _getUsedUris(self):
- "Return list of actually used URIs in this Selector."
- uris = set()
- for item in self.seq:
- type_, val = item.type, item.value
- if type_.endswith(u'-selector') or type_ == u'universal' and \
- isinstance(val, tuple) and val[0] not in (None, u'*'):
- uris.add(val[0])
- return uris
-
- def _getUsedNamespaces(self):
- "Return actually used namespaces only."
- useduris = self._getUsedUris()
- namespaces = _SimpleNamespaces(log=self._log)
- for p, uri in self._namespaces.items():
- if uri in useduris:
- namespaces[p] = uri
- return namespaces
-
- def __getNamespaces(self):
- "Use own namespaces if not attached to a sheet, else the sheet's ones."
- try:
- return self._parent.parentRule.parentStyleSheet.namespaces
- except AttributeError:
- return self.__namespaces
-
- _namespaces = property(__getNamespaces,
- doc=u"If this Selector is attached to a "
- u"CSSStyleSheet the namespaces of that sheet "
- u"are mirrored here. While the Selector (or "
- u"parent SelectorList or parentRule(s) of that "
- u"are not attached a own dict of {prefix: "
- u"namespaceURI} is used.")
-
-
- element = property(lambda self: self._element,
- doc=u"Effective element target of this selector.")
-
- parent = property(lambda self: self._parent,
- doc=u"(DOM) The SelectorList that contains this Selector "
- u"or None if this Selector is not attached to a "
- u"SelectorList.")
-
- def _getSelectorText(self):
- """Return serialized format."""
- return cssutils.ser.do_css_Selector(self)
-
- def _setSelectorText(self, selectorText):
- """
- :param selectorText:
- parsable string or a tuple of (selectorText, dict-of-namespaces).
- Given namespaces are ignored if this object is attached to a
- CSSStyleSheet!
-
- :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()
-
- # might be (selectorText, namespaces)
- selectorText, namespaces = self._splitNamespacesOff(selectorText)
-
- try:
- # uses parent stylesheets namespaces if available,
- # otherwise given ones
- namespaces = self.parent.parentRule.parentStyleSheet.namespaces
- except AttributeError:
- pass
- tokenizer = self._tokenize2(selectorText)
- if not tokenizer:
- self._log.error(u'Selector: No selectorText given.')
- else:
- # prepare tokenlist:
- # "*" -> type "universal"
- # "*"|IDENT + "|" -> combined to "namespace_prefix"
- # "|" -> type "namespace_prefix"
- # "." + IDENT -> combined to "class"
- # ":" + IDENT, ":" + FUNCTION -> pseudo-class
- # FUNCTION "not(" -> negation
- # "::" + IDENT, "::" + FUNCTION -> pseudo-element
- tokens = []
- for t in tokenizer:
- typ, val, lin, col = t
- if val == u':' and tokens and\
- self._tokenvalue(tokens[-1]) == ':':
- # combine ":" and ":"
- tokens[-1] = (typ, u'::', lin, col)
-
- elif typ == 'IDENT' and tokens\
- and self._tokenvalue(tokens[-1]) == u'.':
- # class: combine to .IDENT
- tokens[-1] = ('class', u'.'+val, lin, col)
- elif typ == 'IDENT' and tokens and \
- self._tokenvalue(tokens[-1]).startswith(u':') and\
- not self._tokenvalue(tokens[-1]).endswith(u'('):
- # pseudo-X: combine to :IDENT or ::IDENT but not ":a(" + "b"
- if self._tokenvalue(tokens[-1]).startswith(u'::'):
- t = 'pseudo-element'
- else:
- t = 'pseudo-class'
- tokens[-1] = (t, self._tokenvalue(tokens[-1])+val, lin, col)
-
- elif typ == 'FUNCTION' and val == u'not(' and tokens and \
- u':' == self._tokenvalue(tokens[-1]):
- tokens[-1] = ('negation', u':' + val, lin, tokens[-1][3])
- elif typ == 'FUNCTION' and tokens\
- and self._tokenvalue(tokens[-1]).startswith(u':'):
- # pseudo-X: combine to :FUNCTION( or ::FUNCTION(
- if self._tokenvalue(tokens[-1]).startswith(u'::'):
- t = 'pseudo-element'
- else:
- t = 'pseudo-class'
- tokens[-1] = (t, self._tokenvalue(tokens[-1])+val, lin, col)
-
- elif val == u'*' and tokens and\
- self._type(tokens[-1]) == 'namespace_prefix' and\
- self._tokenvalue(tokens[-1]).endswith(u'|'):
- # combine prefix|*
- tokens[-1] = ('universal', self._tokenvalue(tokens[-1])+val,
- lin, col)
- elif val == u'*':
- # universal: "*"
- tokens.append(('universal', val, lin, col))
-
- elif val == u'|' and tokens and\
- self._type(tokens[-1]) in (self._prods.IDENT, 'universal')\
- and self._tokenvalue(tokens[-1]).find(u'|') == -1:
- # namespace_prefix: "IDENT|" or "*|"
- tokens[-1] = ('namespace_prefix',
- self._tokenvalue(tokens[-1])+u'|', lin, col)
- elif val == u'|':
- # namespace_prefix: "|"
- tokens.append(('namespace_prefix', val, lin, col))
-
- else:
- tokens.append(t)
-
- tokenizer = iter(tokens)
-
- # for closures: must be a mutable
- new = {'context': [''], # stack of: 'attrib', 'negation', 'pseudo'
- 'element': None,
- '_PREFIX': None,
- 'specificity': [0, 0, 0, 0], # mutable, finally a tuple!
- 'wellformed': True
- }
- # used for equality checks and setting of a space combinator
- S = u' '
-
- def append(seq, val, typ=None, token=None):
- """
- appends to seq
-
- namespace_prefix, IDENT will be combined to a tuple
- (prefix, name) where prefix might be None, the empty string
- or a prefix.
-
- Saved are also:
- - specificity definition: style, id, class/att, type
- - element: the element this Selector is for
- """
- context = new['context'][-1]
- if token:
- line, col = token[2], token[3]
- else:
- line, col = None, None
-
- if typ == '_PREFIX':
- # SPECIAL TYPE: save prefix for combination with next
- new['_PREFIX'] = val[:-1]
- # handle next time
- return
-
- if new['_PREFIX'] is not None:
- # as saved from before and reset to None
- prefix, new['_PREFIX'] = new['_PREFIX'], None
- elif typ == 'universal' and '|' in val:
- # val == *|* or prefix|*
- prefix, val = val.split('|')
- else:
- prefix = None
-
- # namespace
- if (typ.endswith('-selector') or typ == 'universal') and not (
- 'attribute-selector' == typ and not prefix):
- # att **IS NOT** in default ns
- if prefix == u'*':
- # *|name: in ANY_NS
- namespaceURI = cssutils._ANYNS
- elif prefix is None:
- # e or *: default namespace with prefix u''
- # or local-name()
- namespaceURI = namespaces.get(u'', None)
- elif prefix == u'':
- # |name or |*: in no (or the empty) namespace
- namespaceURI = u''
- else:
- # explicit namespace prefix
- # does not raise KeyError, see _SimpleNamespaces
- namespaceURI = namespaces[prefix]
-
- if namespaceURI is None:
- new['wellformed'] = False
- self._log.error(u'Selector: No namespaceURI found '
- u'for prefix %r' % prefix,
- token=token,
- error=xml.dom.NamespaceErr)
- return
-
- # val is now (namespaceprefix, name) tuple
- val = (namespaceURI, val)
-
- # specificity
- if not context or context == 'negation':
- if 'id' == typ:
- new['specificity'][1] += 1
- elif 'class' == typ or '[' == val:
- new['specificity'][2] += 1
- elif typ in ('type-selector', 'negation-type-selector',
- 'pseudo-element'):
- new['specificity'][3] += 1
- if not context and typ in ('type-selector', 'universal'):
- # define element
- new['element'] = val
-
- seq.append(val, typ, line=line, col=col)
-
- # expected constants
- simple_selector_sequence = 'type_selector universal HASH class ' \
- 'attrib pseudo negation '
- simple_selector_sequence2 = 'HASH class attrib pseudo negation '
-
- element_name = 'element_name'
-
- negation_arg = 'type_selector universal HASH class attrib pseudo'
- negationend = ')'
-
- attname = 'prefix attribute'
- attname2 = 'attribute'
- attcombinator = 'combinator ]' # optional
- attvalue = 'value' # optional
- attend = ']'
-
- expressionstart = 'PLUS - DIMENSION NUMBER STRING IDENT'
- expression = expressionstart + ' )'
-
- combinator = ' combinator'
-
- def _COMMENT(expected, seq, token, tokenizer=None):
- "special implementation for comment token"
- append(seq, cssutils.css.CSSComment([token]), 'COMMENT',
- token=token)
- return expected
-
- def _S(expected, seq, token, tokenizer=None):
- # S
- context = new['context'][-1]
- if context.startswith('pseudo-'):
- if seq and seq[-1].value not in u'+-':
- # e.g. x:func(a + b)
- append(seq, S, 'S', token=token)
- return expected
-
- elif context != 'attrib' and 'combinator' in expected:
- append(seq, S, 'descendant', token=token)
- return simple_selector_sequence + combinator
-
- else:
- return expected
-
- def _universal(expected, seq, token, tokenizer=None):
- # *|* or prefix|*
- context = new['context'][-1]
- val = self._tokenvalue(token)
- if 'universal' in expected:
- append(seq, val, 'universal', token=token)
-
- if 'negation' == context:
- return negationend
- else:
- return simple_selector_sequence2 + combinator
-
- else:
- new['wellformed'] = False
- self._log.error(
- u'Selector: Unexpected universal.', token=token)
- return expected
-
- def _namespace_prefix(expected, seq, token, tokenizer=None):
- # prefix| => element_name
- # or prefix| => attribute_name if attrib
- context = new['context'][-1]
- val = self._tokenvalue(token)
- if 'attrib' == context and 'prefix' in expected:
- # [PREFIX|att]
- append(seq, val, '_PREFIX', token=token)
- return attname2
- elif 'type_selector' in expected:
- # PREFIX|*
- append(seq, val, '_PREFIX', token=token)
- return element_name
- else:
- new['wellformed'] = False
- self._log.error(
- u'Selector: Unexpected namespace prefix.', token=token)
- return expected
-
- def _pseudo(expected, seq, token, tokenizer=None):
- # pseudo-class or pseudo-element :a ::a :a( ::a(
- """
- /* '::' starts a pseudo-element, ':' a pseudo-class */
- /* Exceptions: :first-line, :first-letter, :before and
- :after. */
- /* Note that pseudo-elements are restricted to one per selector
- and */
- /* occur only in the last simple_selector_sequence. */
- """
- context = new['context'][-1]
- val, typ = self._tokenvalue(token, normalize=True),\
- self._type(token)
- if 'pseudo' in expected:
- if val in (':first-line',
- ':first-letter',
- ':before',
- ':after'):
- # always pseudo-element ???
- typ = 'pseudo-element'
- append(seq, val, typ, token=token)
-
- if val.endswith(u'('):
- # function
- # "pseudo-" "class" or "element"
- new['context'].append(typ)
- return expressionstart
- elif 'negation' == context:
- return negationend
- elif 'pseudo-element' == typ:
- # only one per element, check at ) also!
- return combinator
- else:
- return simple_selector_sequence2 + combinator
-
- else:
- new['wellformed'] = False
- self._log.error(
- u'Selector: Unexpected start of pseudo.', token=token)
- return expected
-
- def _expression(expected, seq, token, tokenizer=None):
- # [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
- context = new['context'][-1]
- val, typ = self._tokenvalue(token), self._type(token)
- if context.startswith('pseudo-'):
- append(seq, val, typ, token=token)
- return expression
- else:
- new['wellformed'] = False
- self._log.error(
- u'Selector: Unexpected %s.' % typ, token=token)
- return expected
-
- def _attcombinator(expected, seq, token, tokenizer=None):
- # context: attrib
- # PREFIXMATCH | SUFFIXMATCH | SUBSTRINGMATCH | INCLUDES |
- # DASHMATCH
- context = new['context'][-1]
- val, typ = self._tokenvalue(token), self._type(token)
- if 'attrib' == context and 'combinator' in expected:
- # combinator in attrib
- append(seq, val, typ.lower(), token=token)
- return attvalue
- else:
- new['wellformed'] = False
- self._log.error(
- u'Selector: Unexpected %s.' % typ, token=token)
- return expected
-
- def _string(expected, seq, token, tokenizer=None):
- # identifier
- context = new['context'][-1]
- typ, val = self._type(token), self._stringtokenvalue(token)
-
- # context: attrib
- if 'attrib' == context and 'value' in expected:
- # attrib: [...=VALUE]
- append(seq, val, typ, token=token)
- return attend
-
- # context: pseudo
- elif context.startswith('pseudo-'):
- # :func(...)
- append(seq, val, typ, token=token)
- return expression
-
- else:
- new['wellformed'] = False
- self._log.error(
- u'Selector: Unexpected STRING.', token=token)
- return expected
-
- def _ident(expected, seq, token, tokenizer=None):
- # identifier
- context = new['context'][-1]
- val, typ = self._tokenvalue(token), self._type(token)
-
- # context: attrib
- if 'attrib' == context and 'attribute' in expected:
- # attrib: [...|ATT...]
- append(seq, val, 'attribute-selector', token=token)
- return attcombinator
-
- elif 'attrib' == context and 'value' in expected:
- # attrib: [...=VALUE]
- append(seq, val, 'attribute-value', token=token)
- return attend
-
- # context: negation
- elif 'negation' == context:
- # negation: (prefix|IDENT)
- append(seq, val, 'negation-type-selector', token=token)
- return negationend
-
- # context: pseudo
- elif context.startswith('pseudo-'):
- # :func(...)
- append(seq, val, typ, token=token)
- return expression
-
- elif 'type_selector' in expected or element_name == expected:
- # element name after ns or complete type_selector
- append(seq, val, 'type-selector', token=token)
- return simple_selector_sequence2 + combinator
-
- else:
- new['wellformed'] = False
- self._log.error(u'Selector: Unexpected IDENT.', token=token)
- return expected
-
- def _class(expected, seq, token, tokenizer=None):
- # .IDENT
- context = new['context'][-1]
- val = self._tokenvalue(token)
- if 'class' in expected:
- append(seq, val, 'class', token=token)
-
- if 'negation' == context:
- return negationend
- else:
- return simple_selector_sequence2 + combinator
-
- else:
- new['wellformed'] = False
- self._log.error(u'Selector: Unexpected class.', token=token)
- return expected
-
- def _hash(expected, seq, token, tokenizer=None):
- # #IDENT
- context = new['context'][-1]
- val = self._tokenvalue(token)
- if 'HASH' in expected:
- append(seq, val, 'id', token=token)
-
- if 'negation' == context:
- return negationend
- else:
- return simple_selector_sequence2 + combinator
-
- else:
- new['wellformed'] = False
- self._log.error(u'Selector: Unexpected HASH.', token=token)
- return expected
-
- def _char(expected, seq, token, tokenizer=None):
- # + > ~ ) [ ] + -
- context = new['context'][-1]
- val = self._tokenvalue(token)
-
- # context: attrib
- if u']' == val and 'attrib' == context and ']' in expected:
- # end of attrib
- append(seq, val, 'attribute-end', token=token)
- context = new['context'].pop() # attrib is done
- context = new['context'][-1]
- if 'negation' == context:
- return negationend
- else:
- return simple_selector_sequence2 + combinator
-
- elif u'=' == val and 'attrib' == context\
- and 'combinator' in expected:
- # combinator in attrib
- append(seq, val, 'equals', token=token)
- return attvalue
-
- # context: negation
- elif u')' == val and 'negation' == context and u')' in expected:
- # not(negation_arg)"
- append(seq, val, 'negation-end', token=token)
- new['context'].pop() # negation is done
- context = new['context'][-1]
- return simple_selector_sequence + combinator
-
- # context: pseudo (at least one expression)
- elif val in u'+-' and context.startswith('pseudo-'):
- # :func(+ -)"
- _names = {'+': 'plus', '-': 'minus'}
- if val == u'+' and seq and seq[-1].value == S:
- seq.replace(-1, val, _names[val])
- else:
- append(seq, val, _names[val],
- token=token)
- return expression
-
- elif u')' == val and context.startswith('pseudo-') and\
- expression == expected:
- # :func(expression)"
- append(seq, val, 'function-end', token=token)
- new['context'].pop() # pseudo is done
- if 'pseudo-element' == context:
- return combinator
- else:
- return simple_selector_sequence + combinator
-
- # context: ROOT
- elif u'[' == val and 'attrib' in expected:
- # start of [attrib]
- append(seq, val, 'attribute-start', token=token)
- new['context'].append('attrib')
- return attname
-
- elif val in u'+>~' and 'combinator' in expected:
- # no other combinator except S may be following
- _names = {
- '>': 'child',
- '+': 'adjacent-sibling',
- '~': 'following-sibling'}
- if seq and seq[-1].value == S:
- seq.replace(-1, val, _names[val])
- else:
- append(seq, val, _names[val], token=token)
- return simple_selector_sequence
-
- elif u',' == val:
- # not a selectorlist
- new['wellformed'] = False
- self._log.error(
- u'Selector: Single selector only.',
- error=xml.dom.InvalidModificationErr,
- token=token)
- return expected
-
- else:
- new['wellformed'] = False
- self._log.error(
- u'Selector: Unexpected CHAR.', token=token)
- return expected
-
- def _negation(expected, seq, token, tokenizer=None):
- # not(
- context = new['context'][-1]
- val = self._tokenvalue(token, normalize=True)
- if 'negation' in expected:
- new['context'].append('negation')
- append(seq, val, 'negation-start', token=token)
- return negation_arg
- else:
- new['wellformed'] = False
- self._log.error(
- u'Selector: Unexpected negation.', token=token)
- return expected
-
- def _atkeyword(expected, seq, token, tokenizer=None):
- "invalidates selector"
- new['wellformed'] = False
- self._log.error(
- u'Selector: Unexpected ATKEYWORD.', token=token)
- return expected
-
-
- # expected: only|not or mediatype, mediatype, feature, and
- newseq = self._tempSeq()
-
- wellformed, expected = self._parse(
- expected=simple_selector_sequence,
- seq=newseq, tokenizer=tokenizer,
- productions={'CHAR': _char,
- 'class': _class,
- 'HASH': _hash,
- 'STRING': _string,
- 'IDENT': _ident,
- 'namespace_prefix': _namespace_prefix,
- 'negation': _negation,
- 'pseudo-class': _pseudo,
- 'pseudo-element': _pseudo,
- 'universal': _universal,
- # pseudo
- 'NUMBER': _expression,
- 'DIMENSION': _expression,
- # attribute
- 'PREFIXMATCH': _attcombinator,
- 'SUFFIXMATCH': _attcombinator,
- 'SUBSTRINGMATCH': _attcombinator,
- 'DASHMATCH': _attcombinator,
- 'INCLUDES': _attcombinator,
-
- 'S': _S,
- 'COMMENT': _COMMENT,
- 'ATKEYWORD': _atkeyword})
- wellformed = wellformed and new['wellformed']
-
- # post condition
- if len(new['context']) > 1 or not newseq:
- wellformed = False
- self._log.error(u'Selector: Invalid or incomplete selector: %s'
- % self._valuestr(selectorText))
-
- if expected == 'element_name':
- wellformed = False
- self._log.error(u'Selector: No element name found: %s'
- % self._valuestr(selectorText))
-
- if expected == simple_selector_sequence and newseq:
- wellformed = False
- self._log.error(u'Selector: Cannot end with combinator: %s'
- % self._valuestr(selectorText))
-
- if newseq and hasattr(newseq[-1].value, 'strip') \
- and newseq[-1].value.strip() == u'':
- del newseq[-1]
-
- # set
- if wellformed:
- self.__namespaces = namespaces
- self._element = new['element']
- self._specificity = tuple(new['specificity'])
- self._setSeq(newseq)
- # filter that only used ones are kept
- self.__namespaces = self._getUsedNamespaces()
-
- selectorText = property(_getSelectorText, _setSelectorText,
- doc=u"(DOM) The parsable textual representation of "
- u"the selector.")
-
- specificity = property(lambda self: self._specificity,
- doc="""Specificity of this selector (READONLY).
- Tuple of (a, b, c, d) where:
-
- a
- presence of style in document, always 0 if not used on a
- document
- b
- number of ID selectors
- c
- number of .class selectors
- d
- number of Element (type) selectors""")
-
- wellformed = property(lambda self: bool(len(self.seq)))
-
-
- @Deprecated('Use property parent instead')
- def _getParentList(self):
- return self.parent
-
- parentList = property(_getParentList,
- doc="DEPRECATED, see property parent instead")
diff --git a/libs/cssutils/css/selectorlist.py b/libs/cssutils/css/selectorlist.py
deleted file mode 100755
index 2072cd53..00000000
--- a/libs/cssutils/css/selectorlist.py
+++ /dev/null
@@ -1,234 +0,0 @@
-"""SelectorList is a list of CSS Selector objects.
-
-TODO
- - remove duplicate Selectors. -> CSSOM canonicalize
-
- - ??? CSS2 gives a special meaning to the comma (,) in selectors.
- However, since it is not known if the comma may acquire other
- meanings in future versions of CSS, the whole statement should be
- ignored if there is an error anywhere in the selector, even though
- the rest of the selector may look reasonable in CSS2.
-
- Illegal example(s):
-
- For example, since the "&" is not a valid token in a CSS2 selector,
- a CSS2 user agent must ignore the whole second line, and not set
- the color of H3 to red:
-"""
-__all__ = ['SelectorList']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-
-from selector import Selector
-import cssutils
-import xml.dom
-
-class SelectorList(cssutils.util.Base, cssutils.util.ListSeq):
- """A list of :class:`~cssutils.css.Selector` objects
- of a :class:`~cssutils.css.CSSStyleRule`."""
- def __init__(self, selectorText=None, parentRule=None,
- readonly=False):
- """
- :Parameters:
- selectorText
- parsable list of Selectors
- parentRule
- the parent CSSRule if available
- """
- super(SelectorList, self).__init__()
-
- self._parentRule = parentRule
-
- if selectorText:
- self.selectorText = selectorText
-
- 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)" % (self.__class__.__name__,
- st)
-
- def __str__(self):
- return u"" % (self.__class__.__name__,
- self.selectorText,
- self._namespaces,
- id(self))
-
- def __setitem__(self, index, newSelector):
- """Overwrite ListSeq.__setitem__
-
- Any duplicate Selectors are **not** removed.
- """
- newSelector = self.__prepareset(newSelector)
- if newSelector:
- self.seq[index] = newSelector
-
- def __prepareset(self, newSelector, namespaces=None):
- "Used by appendSelector and __setitem__"
- if not namespaces:
- namespaces = {}
- self._checkReadonly()
- if not isinstance(newSelector, Selector):
- newSelector = Selector((newSelector, namespaces),
- parent=self)
- if newSelector.wellformed:
- newSelector._parent = self # maybe set twice but must be!
- return newSelector
-
- def __getNamespaces(self):
- """Use children namespaces if not attached to a sheet, else the sheet's
- ones.
- """
- try:
- return self.parentRule.parentStyleSheet.namespaces
- except AttributeError:
- namespaces = {}
- for selector in self.seq:
- namespaces.update(selector._namespaces)
- return namespaces
-
- def _getUsedUris(self):
- "Used by CSSStyleSheet to check if @namespace rules are needed"
- uris = set()
- for s in self:
- uris.update(s._getUsedUris())
- return uris
-
- _namespaces = property(__getNamespaces, doc="""If this SelectorList is
- attached to a CSSStyleSheet the namespaces of that sheet are mirrored
- here. While the SelectorList (or parentRule(s) are
- not attached the namespaces of all children Selectors are used.""")
-
- def append(self, newSelector):
- "Same as :meth:`appendSelector`."
- self.appendSelector(newSelector)
-
- def appendSelector(self, newSelector):
- """
- Append `newSelector` to this list (a string will be converted to a
- :class:`~cssutils.css.Selector`).
-
- :param newSelector:
- comma-separated list of selectors (as a single string) or a tuple of
- `(newSelector, dict-of-namespaces)`
- :returns: New :class:`~cssutils.css.Selector` or ``None`` if
- `newSelector` is not wellformed.
- :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()
-
- # might be (selectorText, namespaces)
- newSelector, namespaces = self._splitNamespacesOff(newSelector)
- try:
- # use parent's only if available
- namespaces = self.parentRule.parentStyleSheet.namespaces
- except AttributeError:
- # use already present namespaces plus new given ones
- _namespaces = self._namespaces
- _namespaces.update(namespaces)
- namespaces = _namespaces
-
- newSelector = self.__prepareset(newSelector, namespaces)
- if newSelector:
- seq = self.seq[:]
- del self.seq[:]
- for s in seq:
- if s.selectorText != newSelector.selectorText:
- self.seq.append(s)
- self.seq.append(newSelector)
- return newSelector
-
- def _getSelectorText(self):
- "Return serialized format."
- return cssutils.ser.do_css_SelectorList(self)
-
- def _setSelectorText(self, selectorText):
- """
- :param selectorText:
- comma-separated list of selectors or a tuple of
- (selectorText, 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.NoModificationAllowedErr`:
- Raised if this rule is readonly.
- """
- self._checkReadonly()
-
- # might be (selectorText, namespaces)
- selectorText, namespaces = self._splitNamespacesOff(selectorText)
- try:
- # use parent's only if available
- namespaces = self.parentRule.parentStyleSheet.namespaces
- except AttributeError:
- pass
-
- wellformed = True
- tokenizer = self._tokenize2(selectorText)
- newseq = []
-
- expected = True
- while True:
- # find all upto and including next ",", EOF or nothing
- selectortokens = self._tokensupto2(tokenizer, listseponly=True)
- if selectortokens:
- if self._tokenvalue(selectortokens[-1]) == ',':
- expected = selectortokens.pop()
- else:
- expected = None
-
- selector = Selector((selectortokens, namespaces),
- parent=self)
- if selector.wellformed:
- newseq.append(selector)
- else:
- wellformed = False
- self._log.error(u'SelectorList: Invalid Selector: %s' %
- self._valuestr(selectortokens))
- else:
- break
-
- # post condition
- if u',' == expected:
- wellformed = False
- self._log.error(u'SelectorList: Cannot end with ",": %r' %
- self._valuestr(selectorText))
- elif expected:
- wellformed = False
- self._log.error(u'SelectorList: Unknown Syntax: %r' %
- self._valuestr(selectorText))
- if wellformed:
- self.seq = newseq
-
- selectorText = property(_getSelectorText, _setSelectorText,
- doc=u"(cssutils) The textual representation of the "
- u"selector for a rule set.")
-
- length = property(lambda self: len(self),
- doc=u"The number of :class:`~cssutils.css.Selector` "
- u"objects in the list.")
-
- parentRule = property(lambda self: self._parentRule,
- doc=u"(DOM) The CSS rule that contains this "
- u"SelectorList or ``None`` if this SelectorList "
- u"is not attached to a CSSRule.")
-
- wellformed = property(lambda self: bool(len(self.seq)))
-
diff --git a/libs/cssutils/css/value.py b/libs/cssutils/css/value.py
deleted file mode 100755
index f9ab050a..00000000
--- a/libs/cssutils/css/value.py
+++ /dev/null
@@ -1,871 +0,0 @@
-"""Value related classes.
-
-DOM Level 2 CSS CSSValue, CSSPrimitiveValue and CSSValueList are **no longer**
-supported and are replaced by these new classes.
-"""
-__all__ = ['PropertyValue',
- 'Value',
- 'ColorValue',
- 'DimensionValue',
- 'URIValue',
- 'CSSFunction',
- 'CSSVariable',
- 'MSValue'
- ]
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-
-from cssutils.prodparser import *
-import cssutils
-from cssutils.helper import normalize, pushtoken
-import colorsys
-import math
-import re
-import xml.dom
-import urlparse
-
-class PropertyValue(cssutils.util._NewBase):
- """
- An unstructured list like holder for all values defined for a
- :class:`~cssutils.css.Property`. Contains :class:`~cssutils.css.Value`
- or subclass objects. Currently there is no access to the combinators of
- the defined values which might simply be space or comma or slash.
-
- You may:
-
- - iterate over all contained Value objects (not the separators like ``,``,
- ``/`` or `` `` though!)
- - get a Value item by index or use ``PropertyValue[index]``
- - find out the number of values defined (unstructured)
- """
- def __init__(self, cssText=None, parent=None, readonly=False):
- """
- :param cssText:
- the parsable cssText of the value
- :param readonly:
- defaults to False
- """
- super(PropertyValue, self).__init__()
-
- self.parent = parent
- self.wellformed = False
-
- if cssText is not None: # may be 0
- if isinstance(cssText, (int, float)):
- cssText = unicode(cssText) # if it is a number
- self.cssText = cssText
-
- self._readonly = readonly
-
- def __len__(self):
- return len(list(self.__items()))
-
- def __getitem__(self, index):
- try:
- return list(self.__items())[index]
- except IndexError:
- return None
-
- def __iter__(self):
- "Generator which iterates over values."
- for item in self.__items():
- yield item
-
- def __repr__(self):
- return u"cssutils.css.%s(%r)" % (self.__class__.__name__,
- self.cssText)
-
- def __str__(self):
- return u"" % (self.__class__.__name__,
- self.length, self.cssText, id(self))
-
- def __items(self, seq=None):
- "a generator of Value obects only, no , / or ' '"
- if seq is None:
- seq = self.seq
- return (x.value for x in seq if isinstance(x.value, Value))
-
- def _setCssText(self, cssText):
- if isinstance(cssText, (int, float)):
- cssText = unicode(cssText) # if it is a number
- """
- Format::
-
- unary_operator
- : '-' | '+'
- ;
- operator
- : '/' S* | ',' S* | /* empty */
- ;
- expr
- : term [ operator term ]*
- ;
- term
- : unary_operator?
- [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* |
- ANGLE S* | TIME S* | FREQ S* ]
- | STRING S* | IDENT S* | URI S* | hexcolor | function
- | UNICODE-RANGE S*
- ;
- function
- : FUNCTION S* expr ')' S*
- ;
- /*
- * There is a constraint on the color that it must
- * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
- * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
- */
- hexcolor
- : HASH S*
- ;
-
- :exceptions:
- - :exc:`~xml.dom.SyntaxErr`:
- Raised if the specified CSS string value has a syntax error
- (according to the attached property) or is unparsable.
- - :exc:`~xml.dom.InvalidModificationErr`:
- TODO: Raised if the specified CSS string value represents a
- different type of values than the values allowed by the CSS
- property.
- - :exc:`~xml.dom.NoModificationAllowedErr`:
- Raised if this value is readonly.
- """
- self._checkReadonly()
-
- # used as operator is , / or S
- nextSor = u',/'
- term = Choice(_ColorProd(self, nextSor),
- _DimensionProd(self, nextSor),
- _URIProd(self, nextSor),
- _ValueProd(self, nextSor),
-# _CalcValueProd(self, nextSor),
-# _Rect(self, nextSor),
- # all other functions
- _CSSVariableProd(self, nextSor),
- _MSValueProd(self, nextSor),
- _CSSFunctionProd(self, nextSor)
- )
- operator = Choice(PreDef.S(toSeq=False),
- PreDef.char('comma', ',',
- toSeq=lambda t, tokens: ('operator', t[1])),
- PreDef.char('slash', '/',
- toSeq=lambda t, tokens: ('operator', t[1])),
- optional=True)
- prods = Sequence(term,
- Sequence(# mayEnd this Sequence if whitespace
- operator,
- # TODO: only when setting via other class
- # used by variabledeclaration currently
- PreDef.char('END', ';',
- stopAndKeep=True,
- optional=True),
- # TODO: } and !important ends too!
- term,
- minmax=lambda: (0, None)))
- # parse
- ok, seq, store, unused = ProdParser().parse(cssText,
- u'PropertyValue',
- prods)
- # must be at least one value!
- ok = ok and len(list(self.__items(seq))) > 0
- if ok:
- self._setSeq(seq)
- self.wellformed = True
- else:
- self._log.error(u'PropertyValue: Unknown syntax or no value: %s' %
- self._valuestr(cssText))
-
- cssText = property(lambda self: cssutils.ser.do_css_PropertyValue(self),
- _setCssText,
- doc="A string representation of the current value.")
-
- def item(self, index):
- """
- The value at position `index`. Alternatively simple use
- ``PropertyValue[index]``.
-
- :param index:
- the parsable cssText of the value
- :exceptions:
- - :exc:`~IndexError`:
- Raised if index if out of bounds
- """
- return self[index]
-
- length = property(lambda self: len(self),
- doc=u"Number of values set.")
-
- value = property(lambda self: cssutils.ser.do_css_PropertyValue(self,
- valuesOnly=True),
- doc=u"A string representation of the current value "
- u"without any comments used for validation.")
-
-
-class Value(cssutils.util._NewBase):
- """
- Represents a single CSS value. For now simple values of
- IDENT, STRING, or UNICODE-RANGE values are represented directly
- as Value objects. Other values like e.g. FUNCTIONs are represented by
- subclasses with an extended API.
- """
- IDENT = u'IDENT'
- STRING = u'STRING'
- UNICODE_RANGE = u'UNICODE-RANGE'
- URI = u'URI'
-
- DIMENSION = u'DIMENSION'
- NUMBER = u'NUMBER'
- PERCENTAGE = u'PERCENTAGE'
-
- COLOR_VALUE = u'COLOR_VALUE'
- HASH = u'HASH'
-
- FUNCTION = u'FUNCTION'
- VARIABLE = u'VARIABLE'
-
- _type = None
- _value = u''
-
- def __init__(self, cssText=None, parent=None, readonly=False):
- super(Value, self).__init__()
-
- self.parent = parent
-
- if cssText:
- self.cssText = cssText
-
- def __repr__(self):
- return u"cssutils.css.%s(%r)" % (self.__class__.__name__,
- self.cssText)
-
- def __str__(self):
- return u""\
- % (self.__class__.__name__,
- self.type, self.value, self.cssText,
- id(self))
-
- def _setCssText(self, cssText):
- self._checkReadonly()
-
- prods = Choice(PreDef.hexcolor(stop=True),
- PreDef.ident(stop=True),
- PreDef.string(stop=True),
- PreDef.unicode_range(stop=True),
- )
- ok, seq, store, unused = ProdParser().parse(cssText, u'Value', prods)
- if ok:
- # only 1 value anyway!
- self._type = seq[0].type
- self._value = seq[0].value
-
- self._setSeq(seq)
- self.wellformed = ok
-
- cssText = property(lambda self: cssutils.ser.do_css_Value(self),
- _setCssText,
- doc=u'String value of this value.')
-
- type = property(lambda self: self._type, #_setType,
- doc=u"Type of this value, for now the production type "
- u"like e.g. `DIMENSION` or `STRING`. All types are "
- u"defined as constants in :class:`~cssutils.css.Value`.")
-
- def _setValue(self, value):
- # TODO: check!
- self._value = value
-
- value = property(lambda self: self._value, _setValue,
- doc=u"Actual value if possible: An int or float or else "
- u" a string")
-
-
-class ColorValue(Value):
- """
- A color value like rgb(), rgba(), hsl(), hsla() or #rgb, #rrggbb
-
- TODO: Color Keywords
- """
- from colors import COLORS
-
- type = Value.COLOR_VALUE
- # hexcolor, FUNCTION?
- _colorType = None
- _red = 0
- _green = 0
- _blue = 0
- _alpha = 0
-
- def __str__(self):
- return u""\
- % (self.__class__.__name__,
- self.type, self.value,
- self.colorType, self.red, self.green, self.blue, self.alpha,
- id(self))
-
- def _setCssText(self, cssText):
- self._checkReadonly()
- types = self._prods # rename!
-
- component = Choice(PreDef.unary(toSeq=lambda t, tokens: (t[0],
- DimensionValue(pushtoken(t, tokens),
- parent=self)
- )),
- PreDef.number(toSeq=lambda t, tokens: (t[0],
- DimensionValue(pushtoken(t, tokens),
- parent=self)
- )),
- PreDef.percentage(toSeq=lambda t, tokens: (t[0],
- DimensionValue(pushtoken(t, tokens),
- parent=self)
- ))
- )
- noalp = Sequence(Prod(name='FUNCTION',
- match=lambda t, v: t == types.FUNCTION and
- v in (u'rgb(', u'hsl('),
- toSeq=lambda t, tokens: (t[0], normalize(t[1]))),
- component,
- Sequence(PreDef.comma(),
- component,
- minmax=lambda: (2, 2)
- ),
- PreDef.funcEnd(stop=True)
- )
- witha = Sequence(Prod(name='FUNCTION',
- match=lambda t, v: t == types.FUNCTION and
- v in (u'rgba(', u'hsla('),
- toSeq=lambda t, tokens: (t[0],
- normalize(t[1]))
- ),
- component,
- Sequence(PreDef.comma(),
- component,
- minmax=lambda: (3, 3)
- ),
- PreDef.funcEnd(stop=True)
- )
- namedcolor = Prod(name='Named Color',
- match=lambda t, v: t == 'IDENT' and (
- normalize(v) in self.COLORS.keys()
- ),
- stop=True)
-
- prods = Choice(PreDef.hexcolor(stop=True),
- namedcolor,
- noalp,
- witha)
-
- ok, seq, store, unused = ProdParser().parse(cssText,
- self.type,
- prods)
- if ok:
- t, v = seq[0].type, seq[0].value
- if u'IDENT' == t:
- rgba = self.COLORS[normalize(v)]
- if u'HASH' == t:
- if len(v) == 4:
- # HASH #rgb
- rgba = (int(2*v[1], 16),
- int(2*v[2], 16),
- int(2*v[3], 16),
- 1.0)
- else:
- # HASH #rrggbb
- rgba = (int(v[1:3], 16),
- int(v[3:5], 16),
- int(v[5:7], 16),
- 1.0)
-
- elif u'FUNCTION' == t:
- functiontype, raw, check = None, [], u''
- HSL = False
-
- for item in seq:
- try:
- type_ = item.value.type
- except AttributeError, e:
- # type of function, e.g. rgb(
- if item.type == 'FUNCTION':
- functiontype = item.value
- HSL = functiontype in (u'hsl(', u'hsla(')
- continue
-
- # save components
- if type_ == Value.NUMBER:
- raw.append(item.value.value)
- check += u'N'
- elif type_ == Value.PERCENTAGE:
- if HSL:
- # save as percentage fraction
- raw.append(item.value.value / 100.0)
- else:
- # save as real value of percentage of 255
- raw.append(int(255 * item.value.value / 100))
- check += u'P'
-
- if HSL:
- # convert to rgb
- # h is 360 based (circle)
- h, s, l = raw[0] / 360.0, raw[1], raw[2]
- # ORDER h l s !!!
- r, g, b = colorsys.hls_to_rgb(h, l, s)
- # back to 255 based
- rgba = [int(round(r*255)),
- int(round(g*255)),
- int(round(b*255))]
-
- if len(raw) > 3:
- rgba.append(raw[3])
-
- else:
- # rgb, rgba
- rgba = raw
-
- if len(rgba) < 4:
- rgba.append(1.0)
-
- # validate
- checks = {u'rgb(': ('NNN', 'PPP'),
- u'rgba(': ('NNNN', 'PPPN'),
- u'hsl(': ('NPP',),
- u'hsla(': ('NPPN',)
- }
- if check not in checks[functiontype]:
- self._log.error(u'ColorValue has invalid %s) parameters: '
- u'%s (N=Number, P=Percentage)' %
- (functiontype, check))
-
- self._colorType = t
- self._red, self._green, self._blue, self._alpha = tuple(rgba)
- self._setSeq(seq)
- self.wellformed = ok
-
- cssText = property(lambda self: cssutils.ser.do_css_ColorValue(self),
- _setCssText,
- doc=u"String value of this value.")
-
- value = property(lambda self: cssutils.ser.do_css_CSSFunction(self, True),
- doc=u'Same as cssText but without comments.')
-
- type = property(lambda self: Value.COLOR_VALUE,
- doc=u"Type is fixed to Value.COLOR_VALUE.")
-
- def _getName(self):
- for n, v in self.COLORS.items():
- if v == (self.red, self.green, self.blue, self.alpha):
- return n
-
- colorType = property(lambda self: self._colorType,
- doc=u"IDENT (red), HASH (#f00) or FUNCTION (rgb(255, 0, 0).")
-
- name = property(_getName,
- doc=u'Name of the color if known (in ColorValue.COLORS) '
- u'else None')
-
- red = property(lambda self: self._red,
- doc=u'red part as integer between 0 and 255')
- green = property(lambda self: self._green,
- doc=u'green part as integer between 0 and 255')
- blue = property(lambda self: self._blue,
- doc=u'blue part as integer between 0 and 255')
- alpha = property(lambda self: self._alpha,
- doc=u'alpha part as float between 0.0 and 1.0')
-
-class DimensionValue(Value):
- """
- A numerical value with an optional dimenstion like e.g. "px" or "%".
-
- Covers DIMENSION, PERCENTAGE or NUMBER values.
- """
- __reNumDim = re.compile(ur'^(\d*\.\d+|\d+)(.*)$', re.I | re.U | re.X)
- _dimension = None
- _sign = None
-
- def __str__(self):
- return u""\
- % (self.__class__.__name__,
- self.type, self.value, self.dimension, self.cssText,
- id(self))
-
- def _setCssText(self, cssText):
- self._checkReadonly()
-
- prods = Sequence(PreDef.unary(),
- Choice(PreDef.dimension(stop=True),
- PreDef.number(stop=True),
- PreDef.percentage(stop=True)
- )
- )
- ok, seq, store, unused = ProdParser().parse(cssText,
- u'DimensionValue',
- prods)
- if ok:
- sign = val = u''
- dim = type_ = None
-
- # find
- for item in seq:
- if item.value in u'+-':
- sign = item.value
- else:
- type_ = item.type
-
- # number + optional dim
- v, d = self.__reNumDim.findall(
- normalize(item.value))[0]
- if u'.' in v:
- val = float(sign + v)
- else:
- val = int(sign + v)
- if d:
- dim = d
-
- self._sign = sign
- self._value = val
- self._dimension = dim
- self._type = type_
-
- self._setSeq(seq)
- self.wellformed = ok
-
- cssText = property(lambda self: cssutils.ser.do_css_Value(self),
- _setCssText,
- doc=u"String value of this value including dimension.")
-
- dimension = property(lambda self: self._dimension, #_setValue,
- doc=u"Dimension if a DIMENSION or PERCENTAGE value, "
- u"else None")
-class URIValue(Value):
- """
- An URI value like ``url(example.png)``.
- """
- _type = Value.URI
- _uri = Value._value
-
- def __str__(self):
- return u""\
- % (self.__class__.__name__,
- self.type, self.value, self.uri, self.cssText,
- id(self))
-
- def _setCssText(self, cssText):
- self._checkReadonly()
-
- prods = Sequence(PreDef.uri(stop=True))
-
- ok, seq, store, unused = ProdParser().parse(cssText, u'URIValue', prods)
- if ok:
- # only 1 value only anyway
- self._type = seq[0].type
- self._value = seq[0].value
-
- self._setSeq(seq)
- self.wellformed = ok
-
- cssText = property(lambda self: cssutils.ser.do_css_Value(self),
- _setCssText,
- doc=u'String value of this value.')
-
- def _setUri(self, uri):
- # TODO: check?
- self._value = uri
-
- uri = property(lambda self: self._value, _setUri,
- doc=u"Actual URL without delimiters or the empty string")
-
- def absoluteUri(self):
- """Actual URL, made absolute if possible, else same as `uri`."""
- # Ancestry: PropertyValue, Property, CSSStyleDeclaration, CSSStyleRule,
- # CSSStyleSheet
- try:
- # TODO: better way?
- styleSheet = self.parent.parent.parent.parentRule.parentStyleSheet
- except AttributeError, e:
- return self.uri
- else:
- return urlparse.urljoin(styleSheet.href, self.uri)
-
- absoluteUri = property(absoluteUri, doc=absoluteUri.__doc__)
-
-
-class CSSFunction(Value):
- """
- A function value.
- """
- _functionName = 'Function'
-
- def _productions(self):
- """Return definition used for parsing."""
- types = self._prods # rename!
-
- itemProd = Choice(_ColorProd(self),
- _DimensionProd(self),
- _URIProd(self),
- _ValueProd(self),
- #_CalcValueProd(self),
- _CSSVariableProd(self),
- _CSSFunctionProd(self)
- )
- funcProds = Sequence(Prod(name='FUNCTION',
- match=lambda t, v: t == types.FUNCTION,
- toSeq=lambda t, tokens: (t[0],
- normalize(t[1]))),
- Choice(Sequence(itemProd,
- Sequence(PreDef.comma(),
- itemProd,
- minmax=lambda: (0, None)),
- PreDef.funcEnd(stop=True)),
- PreDef.funcEnd(stop=True))
- )
- return funcProds
-
- def _setCssText(self, cssText):
- self._checkReadonly()
- ok, seq, store, unused = ProdParser().parse(cssText,
- self.type,
- self._productions())
- if ok:
- self._setSeq(seq)
- self.wellformed = ok
-
- cssText = property(lambda self: cssutils.ser.do_css_CSSFunction(self),
- _setCssText,
- doc=u"String value of this value.")
-
- value = property(lambda self: cssutils.ser.do_css_CSSFunction(self, True),
- doc=u'Same as cssText but without comments.')
-
- type = property(lambda self: Value.FUNCTION,
- doc=u"Type is fixed to Value.FUNCTION.")
-
-class MSValue(CSSFunction):
- """An IE specific Microsoft only function value which is much looser
- in what is syntactically allowed."""
- _functionName = 'MSValue'
-
- def _productions(self):
- """Return definition used for parsing."""
- types = self._prods # rename!
-
- func = Prod(name='MSValue-Sub',
- match=lambda t, v: t == self._prods.FUNCTION,
- toSeq=lambda t, tokens: (MSValue._functionName,
- MSValue(pushtoken(t,
- tokens
- ),
- parent=self
- )
- )
- )
-
-
- funcProds = Sequence(Prod(name='FUNCTION',
- match=lambda t, v: t == types.FUNCTION,
- toSeq=lambda t, tokens: (t[0], t[1])
- ),
- Sequence(Choice(_ColorProd(self),
- _DimensionProd(self),
- _URIProd(self),
- _ValueProd(self),
- _MSValueProd(self),
- #_CalcValueProd(self),
- _CSSVariableProd(self),
- func,
- #_CSSFunctionProd(self),
- Prod(name='MSValuePart',
- match=lambda t, v: v != u')',
- toSeq=lambda t, tokens: (t[0], t[1])
- )
- ),
- minmax=lambda: (0, None)
- ),
- PreDef.funcEnd(stop=True)
- )
- return funcProds
-
- def _setCssText(self, cssText):
- super(MSValue, self)._setCssText(cssText)
-
- cssText = property(lambda self: cssutils.ser.do_css_MSValue(self),
- _setCssText,
- doc=u"String value of this value.")
-
-
-class CSSVariable(CSSFunction):
- """The CSSVariable represents a CSS variables like ``var(varname)``.
-
- A variable has a (nonnormalized!) `name` and a `value` which is
- tried to be resolved from any available CSSVariablesRule definition.
- """
- _functionName = 'CSSVariable'
- _name = None
-
- def __str__(self):
- return u"" % (
- self.__class__.__name__, self.name, self.value, id(self))
-
- def _setCssText(self, cssText):
- self._checkReadonly()
-
- types = self._prods # rename!
- prods = Sequence(Prod(name='var',
- match=lambda t, v: t == types.FUNCTION and
- normalize(v) == u'var('
- ),
- PreDef.ident(toStore='ident'),
- PreDef.funcEnd(stop=True))
-
- # store: name of variable
- store = {'ident': None}
- ok, seq, store, unused = ProdParser().parse(cssText,
- u'CSSVariable',
- prods)
- if ok:
- self._name = store['ident'].value
- self._setSeq(seq)
- self.wellformed = ok
-
- cssText = property(lambda self: cssutils.ser.do_css_CSSVariable(self),
- _setCssText, doc=u"String representation of variable.")
-
- # TODO: writable? check if var (value) available?
- name = property(lambda self: self._name,
- doc=u"The name identifier of this variable referring to "
- u"a value in a "
- u":class:`cssutils.css.CSSVariablesDeclaration`.")
-
- type = property(lambda self: Value.VARIABLE,
- doc=u"Type is fixed to Value.VARIABLE.")
-
- def _getValue(self):
- "Find contained sheet and @variables there"
- rel = self
- while True:
- # find node which has parentRule to get to StyleSheet
- if hasattr(rel, 'parent'):
- rel = rel.parent
- else:
- break
- try:
- variables = rel.parentRule.parentStyleSheet.variables
- except AttributeError:
- return None
- else:
- try:
- return variables[self.name]
- except KeyError:
- return None
-
- value = property(_getValue,
- doc=u'The resolved actual value or None.')
-
-
-# helper for productions
-def _ValueProd(parent, nextSor=False):
- return Prod(name='Value',
- match=lambda t, v: t in ('IDENT', 'STRING', 'UNICODE-RANGE'),
- nextSor = nextSor,
- toSeq=lambda t, tokens: ('Value', Value(
- pushtoken(t,
- tokens),
- parent=parent)
- )
- )
-
-
-def _DimensionProd(parent, nextSor=False):
- return Prod(name='Dimension',
- match=lambda t, v: t in (u'DIMENSION',
- u'NUMBER',
- u'PERCENTAGE') or v in u'+-',
- nextSor = nextSor,
- toSeq=lambda t, tokens: (t[0], DimensionValue(
- pushtoken(t,
- tokens),
- parent=parent)
- )
- )
-
-def _URIProd(parent, nextSor=False):
- return Prod(name='URIValue',
- match=lambda t, v: t == 'URI',
- nextSor = nextSor,
- toSeq=lambda t, tokens: ('URIValue', URIValue(
- pushtoken(t,
- tokens),
- parent=parent)
- )
- )
-
-reHexcolor = re.compile(r'^\#(?:[0-9abcdefABCDEF]{3}|[0-9abcdefABCDEF]{6})$')
-
-def _ColorProd(parent, nextSor=False):
- return Prod(name='ColorValue',
- match=lambda t, v:
- (t == 'HASH' and
- reHexcolor.match(v)
- ) or
- (t == 'FUNCTION' and
- normalize(v) in (u'rgb(',
- u'rgba(',
- u'hsl(',
- u'hsla(')
- ) or
- (t == 'IDENT' and
- normalize(v) in ColorValue.COLORS.keys()
- ),
- nextSor = nextSor,
- toSeq=lambda t, tokens: ('ColorValue', ColorValue(
- pushtoken(t,
- tokens),
- parent=parent)
- )
- )
-
-def _CSSFunctionProd(parent, nextSor=False):
- return PreDef.function(nextSor=nextSor,
- toSeq=lambda t, tokens: (CSSFunction._functionName,
- CSSFunction(
- pushtoken(t, tokens),
- parent=parent)
- )
- )
-
-def _CSSVariableProd(parent, nextSor=False):
- return PreDef.variable(nextSor=nextSor,
- toSeq=lambda t, tokens: (CSSVariable._functionName,
- CSSVariable(
- pushtoken(t, tokens),
- parent=parent)
- )
- )
-
-def _MSValueProd(parent, nextSor=False):
- return Prod(name=MSValue._functionName,
- match=lambda t, v: (#t == self._prods.FUNCTION and (
- normalize(v) in (u'expression(',
- u'alpha(',
- u'blur(',
- u'chroma(',
- u'dropshadow(',
- u'fliph(',
- u'flipv(',
- u'glow(',
- u'gray(',
- u'invert(',
- u'mask(',
- u'shadow(',
- u'wave(',
- u'xray(') or
- v.startswith(u'progid:DXImageTransform.Microsoft.')
- ),
- nextSor=nextSor,
- toSeq=lambda t, tokens: (MSValue._functionName,
- MSValue(pushtoken(t,
- tokens
- ),
- parent=parent
- )
- )
- )
diff --git a/libs/cssutils/css2productions.py b/libs/cssutils/css2productions.py
deleted file mode 100755
index a435e71e..00000000
--- a/libs/cssutils/css2productions.py
+++ /dev/null
@@ -1,131 +0,0 @@
-"""productions for CSS 2.1
-
-CSS2_1_MACROS and CSS2_1_PRODUCTIONS are from both
-http://www.w3.org/TR/CSS21/grammar.html and
-http://www.w3.org/TR/css3-syntax/#grammar0
-
-
-"""
-__all__ = ['CSSProductions', 'MACROS', 'PRODUCTIONS']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-
-# option case-insensitive
-MACROS = {
- 'h': r'[0-9a-f]',
- #'nonascii': r'[\200-\377]',
- 'nonascii': r'[^\0-\177]', # CSS3
- 'unicode': r'\\{h}{1,6}(\r\n|[ \t\r\n\f])?',
-
- 'escape': r'{unicode}|\\[^\r\n\f0-9a-f]',
- 'nmstart': r'[_a-zA-Z]|{nonascii}|{escape}',
- 'nmchar': r'[_a-zA-Z0-9-]|{nonascii}|{escape}',
- 'string1': r'\"([^\n\r\f\\"]|\\{nl}|{escape})*\"',
- 'string2': r"\'([^\n\r\f\\']|\\{nl}|{escape})*\'",
- 'invalid1': r'\"([^\n\r\f\\"]|\\{nl}|{escape})*',
- 'invalid2': r"\'([^\n\r\f\\']|\\{nl}|{escape})*",
- 'comment': r'\/\*[^*]*\*+([^/*][^*]*\*+)*\/',
- # CSS list 080725 19:43
- # \/\*([^*\\]|{escape})*\*+(([^/*\\]|{escape})[^*]*\*+)*\/
-
- 'ident': r'[-]?{nmstart}{nmchar}*',
- 'name': r'{nmchar}+',
- # CHANGED TO SPEC: added "-?"
- 'num': r'-?[0-9]*\.[0-9]+|[0-9]+',
- 'string': r'{string1}|{string2}',
- 'invalid': r'{invalid1}|{invalid2}',
- 'url': r'([!#$%&*-~]|{nonascii}|{escape})*',
- 's': r'[ \t\r\n\f]+',
- 'w': r'{s}?',
- 'nl': r'\n|\r\n|\r|\f',
- 'range': r'\?{1,6}|{h}(\?{0,5}|{h}(\?{0,4}|{h}(\?{0,3}|{h}(\?{0,2}|{h}(\??|{h})))))',
-
- 'A': r'a|\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])?',
- 'C': r'c|\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])?',
- 'D': r'd|\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])?',
- 'E': r'e|\\0{0,4}(45|65)(\r\n|[ \t\r\n\f])?',
- 'F': r'f|\\0{0,4}(46|66)(\r\n|[ \t\r\n\f])?',
- 'G': r'g|\\0{0,4}(47|67)(\r\n|[ \t\r\n\f])?|\\g',
- 'H': r'h|\\0{0,4}(48|68)(\r\n|[ \t\r\n\f])?|\\h',
- 'I': r'i|\\0{0,4}(49|69)(\r\n|[ \t\r\n\f])?|\\i',
- 'K': r'k|\\0{0,4}(4b|6b)(\r\n|[ \t\r\n\f])?|\\k',
- 'M': r'm|\\0{0,4}(4d|6d)(\r\n|[ \t\r\n\f])?|\\m',
- 'N': r'n|\\0{0,4}(4e|6e)(\r\n|[ \t\r\n\f])?|\\n',
- 'O': r'o|\\0{0,4}(51|71)(\r\n|[ \t\r\n\f])?|\\o',
- 'P': r'p|\\0{0,4}(50|70)(\r\n|[ \t\r\n\f])?|\\p',
- 'R': r'r|\\0{0,4}(52|72)(\r\n|[ \t\r\n\f])?|\\r',
- 'S': r's|\\0{0,4}(53|73)(\r\n|[ \t\r\n\f])?|\\s',
- 'T': r't|\\0{0,4}(54|74)(\r\n|[ \t\r\n\f])?|\\t',
- 'X': r'x|\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\x',
- 'Z': r'z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z',
- }
-
-PRODUCTIONS = [
- ('URI', r'url\({w}{string}{w}\)'), #"url("{w}{string}{w}")" {return URI;}
- ('URI', r'url\({w}{url}{w}\)'), #"url("{w}{url}{w}")" {return URI;}
- ('FUNCTION', r'{ident}\('), #{ident}"(" {return FUNCTION;}
-
- ('IMPORT_SYM', r'@{I}{M}{P}{O}{R}{T}'), #"@import" {return IMPORT_SYM;}
- ('PAGE_SYM', r'@{P}{A}{G}{E}'), #"@page" {return PAGE_SYM;}
- ('MEDIA_SYM', r'@{M}{E}{D}{I}{A}'), #"@media" {return MEDIA_SYM;}
- ('FONT_FACE_SYM', r'@{F}{O}{N}{T}\-{F}{A}{C}{E}'), #"@font-face" {return FONT_FACE_SYM;}
-
- # CHANGED TO SPEC: only @charset
- ('CHARSET_SYM', r'@charset '), #"@charset " {return CHARSET_SYM;}
-
- ('NAMESPACE_SYM', r'@{N}{A}{M}{E}{S}{P}{A}{C}{E}'), #"@namespace" {return NAMESPACE_SYM;}
-
- # CHANGED TO SPEC: ATKEYWORD
- ('ATKEYWORD', r'\@{ident}'),
-
- ('IDENT', r'{ident}'), #{ident} {return IDENT;}
- ('STRING', r'{string}'), #{string} {return STRING;}
- ('INVALID', r'{invalid}'), # {return INVALID; /* unclosed string */}
- ('HASH', r'\#{name}'), #"#"{name} {return HASH;}
- ('PERCENTAGE', r'{num}%'), #{num}% {return PERCENTAGE;}
- ('LENGTH', r'{num}{E}{M}'), #{num}em {return EMS;}
- ('LENGTH', r'{num}{E}{X}'), #{num}ex {return EXS;}
- ('LENGTH', r'{num}{P}{X}'), #{num}px {return LENGTH;}
- ('LENGTH', r'{num}{C}{M}'), #{num}cm {return LENGTH;}
- ('LENGTH', r'{num}{M}{M}'), #{num}mm {return LENGTH;}
- ('LENGTH', r'{num}{I}{N}'), #{num}in {return LENGTH;}
- ('LENGTH', r'{num}{P}{T}'), #{num}pt {return LENGTH;}
- ('LENGTH', r'{num}{P}{C}'), #{num}pc {return LENGTH;}
- ('ANGLE', r'{num}{D}{E}{G}'), #{num}deg {return ANGLE;}
- ('ANGLE', r'{num}{R}{A}{D}'), #{num}rad {return ANGLE;}
- ('ANGLE', r'{num}{G}{R}{A}{D}'), #{num}grad {return ANGLE;}
- ('TIME', r'{num}{M}{S}'), #{num}ms {return TIME;}
- ('TIME', r'{num}{S}'), #{num}s {return TIME;}
- ('FREQ', r'{num}{H}{Z}'), #{num}Hz {return FREQ;}
- ('FREQ', r'{num}{K}{H}{Z}'), #{num}kHz {return FREQ;}
- ('DIMEN', r'{num}{ident}'), #{num}{ident} {return DIMEN;}
- ('NUMBER', r'{num}'), #{num} {return NUMBER;}
- #('UNICODERANGE', r'U\+{range}'), #U\+{range} {return UNICODERANGE;}
- #('UNICODERANGE', r'U\+{h}{1,6}-{h}{1,6}'), #U\+{h}{1,6}-{h}{1,6} {return UNICODERANGE;}
- # --- CSS3 ---
- ('UNICODE-RANGE', r'[0-9A-F?]{1,6}(\-[0-9A-F]{1,6})?'),
- ('CDO', r'\<\!\-\-'), #"" {return CDC;}
- ('S', r'{s}'),# {return S;}
-
- # \/\*[^*]*\*+([^/*][^*]*\*+)*\/ /* ignore comments */
- # {s}+\/\*[^*]*\*+([^/*][^*]*\*+)*\/ {unput(' '); /*replace by space*/}
-
- ('INCLUDES', r'\~\='), #"~=" {return INCLUDES;}
- ('DASHMATCH', r'\|\='), #"|=" {return DASHMATCH;}
- ('LBRACE', r'\{'), #{w}"{" {return LBRACE;}
- ('PLUS', r'\+'), #{w}"+" {return PLUS;}
- ('GREATER', r'\>'), #{w}">" {return GREATER;}
- ('COMMA', r'\,'), #{w}"," {return COMMA;}
- ('IMPORTANT_SYM', r'\!({w}|{comment})*{I}{M}{P}{O}{R}{T}{A}{N}{T}'), #"!{w}important" {return IMPORTANT_SYM;}
- ('COMMENT', '\/\*[^*]*\*+([^/][^*]*\*+)*\/'), # /* ignore comments */
- ('CLASS', r'\.'), #. {return *yytext;}
-
- # --- CSS3! ---
- ('CHAR', r'[^"\']'),
- ]
-
-class CSSProductions(object):
- pass
-for i, t in enumerate(PRODUCTIONS):
- setattr(CSSProductions, t[0].replace('-', '_'), t[0])
\ No newline at end of file
diff --git a/libs/cssutils/cssproductions.py b/libs/cssutils/cssproductions.py
deleted file mode 100755
index 9f7b3d80..00000000
--- a/libs/cssutils/cssproductions.py
+++ /dev/null
@@ -1,124 +0,0 @@
-"""productions for cssutils based on a mix of CSS 2.1 and CSS 3 Syntax
-productions
-
-- http://www.w3.org/TR/css3-syntax
-- http://www.w3.org/TR/css3-syntax/#grammar0
-
-open issues
- - numbers contain "-" if present
- - HASH: #aaa is, #000 is not anymore,
- CSS2.1: 'nmchar': r'[_a-z0-9-]|{nonascii}|{escape}',
- CSS3: 'nmchar': r'[_a-z-]|{nonascii}|{escape}',
-"""
-__all__ = ['CSSProductions', 'MACROS', 'PRODUCTIONS']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-
-# a complete list of css3 macros
-MACROS = {
- 'nonascii': r'[^\0-\177]',
- 'unicode': r'\\[0-9A-Fa-f]{1,6}(?:{nl}|{s})?',
- #'escape': r'{unicode}|\\[ -~\200-\777]',
- 'escape': r'{unicode}|\\[^\n\r\f0-9a-f]',
- 'nmstart': r'[_a-zA-Z]|{nonascii}|{escape}',
- 'nmchar': r'[-_a-zA-Z0-9]|{nonascii}|{escape}',
- 'string1': r'"([^\n\r\f\\"]|\\{nl}|{escape})*"',
- 'string2': r"'([^\n\r\f\\']|\\{nl}|{escape})*'",
- 'invalid1': r'\"([^\n\r\f\\"]|\\{nl}|{escape})*',
- 'invalid2': r"\'([^\n\r\f\\']|\\{nl}|{escape})*",
-
- 'comment': r'\/\*[^*]*\*+([^/][^*]*\*+)*\/',
- 'ident': r'[-]?{nmstart}{nmchar}*',
- 'name': r'{nmchar}+',
- 'num': r'[0-9]*\.[0-9]+|[0-9]+', #r'[-]?\d+|[-]?\d*\.\d+',
- 'string': r'{string1}|{string2}',
- # from CSS2.1
- 'invalid': r'{invalid1}|{invalid2}',
- 'url': r'[\x09\x21\x23-\x26\x28\x2a-\x7E]|{nonascii}|{escape}',
-
- 's': r'\t|\r|\n|\f|\x20',
- 'w': r'{s}*',
- 'nl': r'\n|\r\n|\r|\f',
-
- 'A': r'A|a|\\0{0,4}(?:41|61)(?:\r\n|[ \t\r\n\f])?',
- 'B': r'B|b|\\0{0,4}(?:42|62)(?:\r\n|[ \t\r\n\f])?',
- 'C': r'C|c|\\0{0,4}(?:43|63)(?:\r\n|[ \t\r\n\f])?',
- 'D': r'D|d|\\0{0,4}(?:44|64)(?:\r\n|[ \t\r\n\f])?',
- 'E': r'E|e|\\0{0,4}(?:45|65)(?:\r\n|[ \t\r\n\f])?',
- 'F': r'F|f|\\0{0,4}(?:46|66)(?:\r\n|[ \t\r\n\f])?',
- 'G': r'G|g|\\0{0,4}(?:47|67)(?:\r\n|[ \t\r\n\f])?|\\G|\\g',
- 'H': r'H|h|\\0{0,4}(?:48|68)(?:\r\n|[ \t\r\n\f])?|\\H|\\h',
- 'I': r'I|i|\\0{0,4}(?:49|69)(?:\r\n|[ \t\r\n\f])?|\\I|\\i',
- 'K': r'K|k|\\0{0,4}(?:4b|6b)(?:\r\n|[ \t\r\n\f])?|\\K|\\k',
- 'L': r'L|l|\\0{0,4}(?:4c|6c)(?:\r\n|[ \t\r\n\f])?|\\L|\\l',
- 'M': r'M|m|\\0{0,4}(?:4d|6d)(?:\r\n|[ \t\r\n\f])?|\\M|\\m',
- 'N': r'N|n|\\0{0,4}(?:4e|6e)(?:\r\n|[ \t\r\n\f])?|\\N|\\n',
- 'O': r'O|o|\\0{0,4}(?:4f|6f)(?:\r\n|[ \t\r\n\f])?|\\O|\\o',
- 'P': r'P|p|\\0{0,4}(?:50|70)(?:\r\n|[ \t\r\n\f])?|\\P|\\p',
- 'R': r'R|r|\\0{0,4}(?:52|72)(?:\r\n|[ \t\r\n\f])?|\\R|\\r',
- 'S': r'S|s|\\0{0,4}(?:53|73)(?:\r\n|[ \t\r\n\f])?|\\S|\\s',
- 'T': r'T|t|\\0{0,4}(?:54|74)(?:\r\n|[ \t\r\n\f])?|\\T|\\t',
- 'U': r'U|u|\\0{0,4}(?:55|75)(?:\r\n|[ \t\r\n\f])?|\\U|\\u',
- 'V': r'V|v|\\0{0,4}(?:56|76)(?:\r\n|[ \t\r\n\f])?|\\V|\\v',
- 'X': r'X|x|\\0{0,4}(?:58|78)(?:\r\n|[ \t\r\n\f])?|\\X|\\x',
- 'Z': r'Z|z|\\0{0,4}(?:5a|7a)(?:\r\n|[ \t\r\n\f])?|\\Z|\\z',
- }
-
-# The following productions are the complete list of tokens
-# used by cssutils, a mix of CSS3 and some CSS2.1 productions.
-# The productions are **ordered**:
-PRODUCTIONS = [
- # UTF8_BOM or UTF8_BOM_SIG will only be checked at beginning of CSS
- ('BOM', '\xfe\xff|\xef\xbb\xbf'),
-
- ('S', r'{s}+'), # 1st in list of general productions
- ('URI', r'{U}{R}{L}\({w}({string}|{url}*){w}\)'),
- ('FUNCTION', r'{ident}\('),
- ('UNICODE-RANGE', r'{U}\+[0-9A-Fa-f?]{1,6}(\-[0-9A-Fa-f]{1,6})?'),
- ('IDENT', r'{ident}'),
- ('DIMENSION', r'{num}{ident}'),
- ('PERCENTAGE', r'{num}\%'),
- ('NUMBER', r'{num}'),
- ('HASH', r'\#{name}'),
- ('COMMENT', r'{comment}'), #r'\/\*[^*]*\*+([^/][^*]*\*+)*\/'),
- ('STRING', r'{string}'),
- ('INVALID', r'{invalid}'), # from CSS2.1
- ('ATKEYWORD', r'@{ident}'), # other keywords are done in the tokenizer
- ('INCLUDES', '\~\='),
- ('DASHMATCH', r'\|\='),
- ('PREFIXMATCH', r'\^\='),
- ('SUFFIXMATCH', r'\$\='),
- ('SUBSTRINGMATCH', r'\*\='),
- ('CDO', r'\<\!\-\-'),
- ('CDC', r'\-\-\>'),
- ('CHAR', r'[^"\']') # MUST always be last
- # valid ony at start so not checked everytime
- #('CHARSET_SYM', r'@charset '), # from Errata includes ending space!
- # checked specially if fullsheet is parsed
- ]
-
-
-
-class CSSProductions(object):
- """
- most attributes are set later
- """
- EOF = True
- # removed from productions as they simply are ATKEYWORD until
- # tokenizing
- CHARSET_SYM = u'CHARSET_SYM'
- FONT_FACE_SYM = u'FONT_FACE_SYM'
- MEDIA_SYM = u'MEDIA_SYM'
- IMPORT_SYM = u'IMPORT_SYM'
- NAMESPACE_SYM = u'NAMESPACE_SYM'
- PAGE_SYM = u'PAGE_SYM'
- VARIABLES_SYM = u'VARIABLES_SYM'
-
-for i, t in enumerate(PRODUCTIONS):
- setattr(CSSProductions, t[0].replace('-', '_'), t[0])
-
-
-# may be enabled by settings.set
-_DXImageTransform = (u'FUNCTION',
- ur'progid\:DXImageTransform\.Microsoft\..+\('
- )
diff --git a/libs/cssutils/errorhandler.py b/libs/cssutils/errorhandler.py
deleted file mode 100755
index 916adff5..00000000
--- a/libs/cssutils/errorhandler.py
+++ /dev/null
@@ -1,118 +0,0 @@
-#!/usr/bin/env python
-"""cssutils ErrorHandler
-
-ErrorHandler
- used as log with usual levels (debug, info, warn, error)
-
- if instanciated with ``raiseExceptions=True`` raises exeptions instead
- of logging
-
-log
- defaults to instance of ErrorHandler for any kind of log message from
- lexerm, parser etc.
-
- - raiseExceptions = [False, True]
- - setloglevel(loglevel)
-"""
-__all__ = ['ErrorHandler']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-
-import logging
-import urllib2
-import xml.dom
-
-class _ErrorHandler(object):
- """
- handles all errors and log messages
- """
- def __init__(self, log, defaultloglevel=logging.INFO,
- raiseExceptions=True):
- """
- inits log if none given
-
- log
- for parse messages, default logs to sys.stderr
- defaultloglevel
- if none give this is logging.DEBUG
- raiseExceptions
- - True: Errors will be raised e.g. during building
- - False: Errors will be written to the log, this is the
- default behaviour when parsing
- """
- # may be disabled during setting of known valid items
- self.enabled = True
-
- if log:
- self._log = log
- else:
- import sys
- self._log = logging.getLogger('CSSUTILS')
- hdlr = logging.StreamHandler(sys.stderr)
- formatter = logging.Formatter('%(levelname)s\t%(message)s')
- hdlr.setFormatter(formatter)
- self._log.addHandler(hdlr)
- self._log.setLevel(defaultloglevel)
-
- self.raiseExceptions = raiseExceptions
-
- def __getattr__(self, name):
- "use self._log items"
- calls = ('debug', 'info', 'warn', 'error', 'critical', 'fatal')
- other = ('setLevel', 'getEffectiveLevel', 'addHandler', 'removeHandler')
-
- if name in calls:
- self._logcall = getattr(self._log, name)
- return self.__handle
- elif name in other:
- return getattr(self._log, name)
- else:
- raise AttributeError(
- '(errorhandler) No Attribute %r found' % name)
-
- def __handle(self, msg=u'', token=None, error=xml.dom.SyntaxErr,
- neverraise=False, args=None):
- """
- handles all calls
- logs or raises exception
- """
- if self.enabled:
- if error is None:
- error = xml.dom.SyntaxErr
-
- line, col = None, None
- if token:
- if isinstance(token, tuple):
- value, line, col = token[1], token[2], token[3]
- else:
- value, line, col = token.value, token.line, token.col
- msg = u'%s [%s:%s: %s]' % (
- msg, line, col, value)
-
- if error and self.raiseExceptions and not neverraise:
- if isinstance(error, urllib2.HTTPError) or isinstance(error, urllib2.URLError):
- raise
- elif issubclass(error, xml.dom.DOMException):
- error.line = line
- error.col = col
- raise error(msg)
- else:
- self._logcall(msg)
-
- def setLog(self, log):
- """set log of errorhandler's log"""
- self._log = log
-
-
-class ErrorHandler(_ErrorHandler):
- "Singleton, see _ErrorHandler"
- instance = None
-
- def __init__(self,
- log=None, defaultloglevel=logging.INFO, raiseExceptions=True):
-
- if ErrorHandler.instance is None:
- ErrorHandler.instance = _ErrorHandler(log=log,
- defaultloglevel=defaultloglevel,
- raiseExceptions=raiseExceptions)
- self.__dict__ = ErrorHandler.instance.__dict__
diff --git a/libs/cssutils/helper.py b/libs/cssutils/helper.py
deleted file mode 100755
index b3c55ff3..00000000
--- a/libs/cssutils/helper.py
+++ /dev/null
@@ -1,137 +0,0 @@
-"""cssutils helper
-"""
-__docformat__ = 'restructuredtext'
-__version__ = '$Id: errorhandler.py 1234 2008-05-22 20:26:12Z cthedot $'
-
-import os
-import re
-import sys
-import urllib
-
-class Deprecated(object):
- """This is a decorator which can be used to mark functions
- as deprecated. It will result in a warning being emitted
- when the function is used.
-
- It accepts a single paramter ``msg`` which is shown with the warning.
- It should contain information which function or method to use instead.
- """
- def __init__(self, msg):
- self.msg = msg
-
- def __call__(self, func):
- def newFunc(*args, **kwargs):
- import warnings
- warnings.warn("Call to deprecated method %r. %s" %
- (func.__name__, self.msg),
- category=DeprecationWarning,
- stacklevel=2)
- return func(*args, **kwargs)
- newFunc.__name__ = func.__name__
- newFunc.__doc__ = func.__doc__
- newFunc.__dict__.update(func.__dict__)
- return newFunc
-
-# simple escapes, all non unicodes
-_simpleescapes = re.compile(ur'(\\[^0-9a-fA-F])').sub
-def normalize(x):
- """
- normalizes x, namely:
-
- - remove any \ before non unicode sequences (0-9a-zA-Z) so for
- x=="c\olor\" return "color" (unicode escape sequences should have
- been resolved by the tokenizer already)
- - lowercase
- """
- if x:
- def removeescape(matchobj):
- return matchobj.group(0)[1:]
- x = _simpleescapes(removeescape, x)
- return x.lower()
- else:
- return x
-
-def path2url(path):
- """Return file URL of `path`"""
- return u'file:' + urllib.pathname2url(os.path.abspath(path))
-
-def pushtoken(token, tokens):
- """Return new generator starting with token followed by all tokens in
- ``tokens``"""
- # TODO: may use itertools.chain?
- yield token
- for t in tokens:
- yield t
-
-def string(value):
- """
- Serialize value with quotes e.g.::
-
- ``a \'string`` => ``'a \'string'``
- """
- # \n = 0xa, \r = 0xd, \f = 0xc
- value = value.replace(u'\n', u'\\a ').replace(
- u'\r', u'\\d ').replace(
- u'\f', u'\\c ').replace(
- u'"', u'\\"')
-
- if value.endswith(u'\\'):
- value = value[:-1] + u'\\\\'
-
- return u'"%s"' % value
-
-def stringvalue(string):
- """
- Retrieve actual value of string without quotes. Escaped
- quotes inside the value are resolved, e.g.::
-
- ``'a \'string'`` => ``a 'string``
- """
- return string.replace(u'\\'+string[0], string[0])[1:-1]
-
-_match_forbidden_in_uri = re.compile(ur'''.*?[\(\)\s\;,'"]''', re.U).match
-def uri(value):
- """
- Serialize value by adding ``url()`` and with quotes if needed e.g.::
-
- ``"`` => ``url("\"")``
- """
- if _match_forbidden_in_uri(value):
- value = string(value)
- return u'url(%s)' % value
-
-def urivalue(uri):
- """
- Return actual content without surrounding "url(" and ")"
- and removed surrounding quotes too including contained
- escapes of quotes, e.g.::
-
- ``url("\"")`` => ``"``
- """
- uri = uri[uri.find('(')+1:-1].strip()
- if uri and (uri[0] in '\'"') and (uri[0] == uri[-1]):
- return stringvalue(uri)
- else:
- return uri
-
-#def normalnumber(num):
-# """
-# Return normalized number as string.
-# """
-# sign = ''
-# if num.startswith('-'):
-# sign = '-'
-# num = num[1:]
-# elif num.startswith('+'):
-# num = num[1:]
-#
-# if float(num) == 0.0:
-# return '0'
-# else:
-# if num.find('.') == -1:
-# return sign + str(int(num))
-# else:
-# a, b = num.split('.')
-# if not a:
-# a = '0'
-# return '%s%s.%s' % (sign, int(a), b)
diff --git a/libs/cssutils/parse.py b/libs/cssutils/parse.py
deleted file mode 100755
index 715a320a..00000000
--- a/libs/cssutils/parse.py
+++ /dev/null
@@ -1,232 +0,0 @@
-#!/usr/bin/env python
-"""A validating CSSParser"""
-__all__ = ['CSSParser']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id$'
-
-from helper import path2url
-import codecs
-import cssutils
-import os
-import sys
-import tokenize2
-import urllib
-
-from cssutils import css
-
-if sys.version_info < (2,6):
- bytes = str
-
-class CSSParser(object):
- """Parse a CSS StyleSheet from URL, string or file and return a DOM Level 2
- CSS StyleSheet object.
-
- Usage::
-
- parser = CSSParser()
- # optionally
- parser.setFetcher(fetcher)
- sheet = parser.parseFile('test1.css', 'ascii')
- print sheet.cssText
- """
- def __init__(self, log=None, loglevel=None, raiseExceptions=None,
- fetcher=None, parseComments=True,
- validate=True):
- """
- :param log:
- logging object
- :param loglevel:
- logging loglevel
- :param raiseExceptions:
- if log should simply log (default) or raise errors during
- parsing. Later while working with the resulting sheets
- the setting used in cssutils.log.raiseExeptions is used
- :param fetcher:
- see ``setFetcher(fetcher)``
- :param parseComments:
- if comments should be added to CSS DOM or simply omitted
- :param validate:
- if parsing should validate, may be overwritten in parse methods
- """
- if log is not None:
- cssutils.log.setLog(log)
- if loglevel is not None:
- cssutils.log.setLevel(loglevel)
-
- # remember global setting
- self.__globalRaising = cssutils.log.raiseExceptions
- if raiseExceptions:
- self.__parseRaising = raiseExceptions
- else:
- # DEFAULT during parse
- self.__parseRaising = False
-
- self.__tokenizer = tokenize2.Tokenizer(doComments=parseComments)
- self.setFetcher(fetcher)
-
- self._validate = validate
-
- def __parseSetting(self, parse):
- """during parse exceptions may be handled differently depending on
- init parameter ``raiseExceptions``
- """
- if parse:
- cssutils.log.raiseExceptions = self.__parseRaising
- else:
- cssutils.log.raiseExceptions = self.__globalRaising
-
- def parseStyle(self, cssText, encoding='utf-8', validate=None):
- """Parse given `cssText` which is assumed to be the content of
- a HTML style attribute.
-
- :param cssText:
- CSS string to parse
- :param encoding:
- It will be used to decode `cssText` if given as a (byte)
- string.
- :param validate:
- If given defines if validation is used. Uses CSSParser settings as
- fallback
- :returns:
- :class:`~cssutils.css.CSSStyleDeclaration`
- """
- self.__parseSetting(True)
- if isinstance(cssText, bytes):
- # TODO: use codecs.getdecoder('css') here?
- cssText = cssText.decode(encoding)
- if validate is None:
- validate = self._validate
- style = css.CSSStyleDeclaration(cssText, validating=validate)
- self.__parseSetting(False)
- return style
-
- def parseString(self, cssText, encoding=None, href=None, media=None,
- title=None,
- validate=None):
- """Parse `cssText` as :class:`~cssutils.css.CSSStyleSheet`.
- Errors may be raised (e.g. UnicodeDecodeError).
-
- :param cssText:
- CSS string to parse
- :param encoding:
- If ``None`` the encoding will be read from BOM or an @charset
- rule or defaults to UTF-8.
- If given overrides any found encoding including the ones for
- imported sheets.
- It also will be used to decode `cssText` if given as a (byte)
- string.
- :param href:
- The ``href`` attribute to assign to the parsed style sheet.
- Used to resolve other urls in the parsed sheet like @import hrefs.
- :param media:
- The ``media`` attribute to assign to the parsed style sheet
- (may be a MediaList, list or a string).
- :param title:
- The ``title`` attribute to assign to the parsed style sheet.
- :param validate:
- If given defines if validation is used. Uses CSSParser settings as
- fallback
- :returns:
- :class:`~cssutils.css.CSSStyleSheet`.
- """
- self.__parseSetting(True)
- # TODO: py3 needs bytes here!
- if isinstance(cssText, bytes):
- cssText = codecs.getdecoder('css')(cssText, encoding=encoding)[0]
-
- if validate is None:
- validate = self._validate
-
- sheet = cssutils.css.CSSStyleSheet(href=href,
- media=cssutils.stylesheets.MediaList(media),
- title=title,
- validating=validate)
- sheet._setFetcher(self.__fetcher)
- # tokenizing this ways closes open constructs and adds EOF
- sheet._setCssTextWithEncodingOverride(self.__tokenizer.tokenize(cssText,
- fullsheet=True),
- encodingOverride=encoding)
- self.__parseSetting(False)
- return sheet
-
- def parseFile(self, filename, encoding=None,
- href=None, media=None, title=None,
- validate=None):
- """Retrieve content from `filename` and parse it. Errors may be raised
- (e.g. IOError).
-
- :param filename:
- of the CSS file to parse, if no `href` is given filename is
- converted to a (file:) URL and set as ``href`` of resulting
- stylesheet.
- If `href` is given it is set as ``sheet.href``. Either way
- ``sheet.href`` is used to resolve e.g. stylesheet imports via
- @import rules.
- :param encoding:
- Value ``None`` defaults to encoding detection via BOM or an
- @charset rule.
- Other values override detected encoding for the sheet at
- `filename` including any imported sheets.
- :returns:
- :class:`~cssutils.css.CSSStyleSheet`.
- """
- if not href:
- # prepend // for file URL, urllib does not do this?
- #href = u'file:' + urllib.pathname2url(os.path.abspath(filename))
- href = path2url(filename)
-
- return self.parseString(open(filename, 'rb').read(),
- encoding=encoding, # read returns a str
- href=href, media=media, title=title,
- validate=validate)
-
- def parseUrl(self, href, encoding=None, media=None, title=None,
- validate=None):
- """Retrieve content from URL `href` and parse it. Errors may be raised
- (e.g. URLError).
-
- :param href:
- URL of the CSS file to parse, will also be set as ``href`` of
- resulting stylesheet
- :param encoding:
- Value ``None`` defaults to encoding detection via HTTP, BOM or an
- @charset rule.
- A value overrides detected encoding for the sheet at ``href``
- including any imported sheets.
- :returns:
- :class:`~cssutils.css.CSSStyleSheet`.
- """
- encoding, enctype, text = cssutils.util._readUrl(href,
- fetcher=self.__fetcher,
- overrideEncoding=encoding)
- if enctype == 5:
- # do not use if defaulting to UTF-8
- encoding = None
-
- if text is not None:
- return self.parseString(text, encoding=encoding,
- href=href, media=media, title=title,
- validate=validate)
-
- def setFetcher(self, fetcher=None):
- """Replace the default URL fetch function with a custom one.
-
- :param fetcher:
- A function which gets a single parameter
-
- ``url``
- the URL to read
-
- and must return ``(encoding, content)`` where ``encoding`` is the
- HTTP charset normally given via the Content-Type header (which may
- simply omit the charset in which case ``encoding`` would be
- ``None``) and ``content`` being the string (or unicode) content.
-
- The Mimetype should be 'text/css' but this has to be checked by the
- fetcher itself (the default fetcher emits a warning if encountering
- a different mimetype).
-
- Calling ``setFetcher`` with ``fetcher=None`` resets cssutils
- to use its default function.
- """
- self.__fetcher = fetcher
diff --git a/libs/cssutils/prodparser.py b/libs/cssutils/prodparser.py
deleted file mode 100755
index ac7825cc..00000000
--- a/libs/cssutils/prodparser.py
+++ /dev/null
@@ -1,733 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Productions parser used by css and stylesheets classes to parse
-test into a cssutils.util.Seq and at the same time retrieving
-additional specific cssutils.util.Item objects for later use.
-
-TODO:
- - ProdsParser
- - handle EOF or STOP?
- - handle unknown @rules
- - handle S: maybe save to Seq? parameterized?
- - store['_raw']: always?
-
- - Sequence:
- - opt first(), naive impl for now
-
-"""
-__all__ = ['ProdParser', 'Sequence', 'Choice', 'Prod', 'PreDef']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id: parse.py 1418 2008-08-09 19:27:50Z cthedot $'
-
-from helper import pushtoken
-import cssutils
-import re
-import string
-import sys
-
-
-class ParseError(Exception):
- """Base Exception class for ProdParser (used internally)."""
- pass
-
-class Done(ParseError):
- """Raised if Sequence or Choice is finished and no more Prods left."""
- pass
-
-class Exhausted(ParseError):
- """Raised if Sequence or Choice is finished but token is given."""
- pass
-
-class Missing(ParseError):
- """Raised if Sequence or Choice is not finished but no matching token given."""
- pass
-
-class NoMatch(ParseError):
- """Raised if nothing in Sequence or Choice does match."""
- pass
-
-
-class Choice(object):
- """A Choice of productions (Sequence or single Prod)."""
-
- def __init__(self, *prods, **options):
- """
- *prods
- Prod or Sequence objects
- options:
- optional=False
- """
- self._prods = prods
-
- try:
- self.optional = options['optional']
- except KeyError, e:
- for p in self._prods:
- if p.optional:
- self.optional = True
- break
- else:
- self.optional = False
-
- self.reset()
-
- def reset(self):
- """Start Choice from zero"""
- self._exhausted = False
-
- def matches(self, token):
- """Check if token matches"""
- for prod in self._prods:
- if prod.matches(token):
- return True
- return False
-
- def nextProd(self, token):
- """
- Return:
-
- - next matching Prod or Sequence
- - ``None`` if any Prod or Sequence is optional and no token matched
- - raise ParseError if nothing matches and all are mandatory
- - raise Exhausted if choice already done
-
- ``token`` may be None but this occurs when no tokens left."""
- if not self._exhausted:
- optional = False
- for x in self._prods:
- if x.matches(token):
- self._exhausted = True
- x.reset()
- return x
- elif x.optional:
- optional = True
- else:
- if not optional:
- # None matched but also None is optional
- raise ParseError(u'No match in %s' % self)
- elif token:
- raise Exhausted(u'Extra token')
-
- def __str__(self):
- return u'Choice(%s)' % u', '.join([str(x) for x in self._prods])
-
-
-class Sequence(object):
- """A Sequence of productions (Choice or single Prod)."""
- def __init__(self, *prods, **options):
- """
- *prods
- Prod or Sequence objects
- **options:
- minmax = lambda: (1, 1)
- callback returning number of times this sequence may run
- """
- self._prods = prods
- try:
- minmax = options['minmax']
- except KeyError:
- minmax = lambda: (1, 1)
-
- self._min, self._max = minmax()
- if self._max is None:
- # unlimited
- try:
- # py2.6/3
- self._max = sys.maxsize
- except AttributeError:
- # py<2.6
- self._max = sys.maxint
-
- self._prodcount = len(self._prods)
- self.reset()
-
- def matches(self, token):
- """Called by Choice to try to find if Sequence matches."""
- for prod in self._prods:
- if prod.matches(token):
- return True
- try:
- if not prod.optional:
- break
- except AttributeError:
- pass
- return False
-
- def reset(self):
- """Reset this Sequence if it is nested."""
- self._roundstarted = False
- self._i = 0
- self._round = 0
-
- def _currentName(self):
- """Return current element of Sequence, used by name"""
- # TODO: current impl first only if 1st if an prod!
- for prod in self._prods[self._i:]:
- if not prod.optional:
- return str(prod)
- else:
- return 'Sequence'
-
- optional = property(lambda self: self._min == 0)
-
- def nextProd(self, token):
- """Return
-
- - next matching Prod or Choice
- - raises ParseError if nothing matches
- - raises Exhausted if sequence already done
- """
- while self._round < self._max:
- # for this round
- i = self._i
- round = self._round
- p = self._prods[i]
- if i == 0:
- self._roundstarted = False
-
- # for next round
- self._i += 1
- if self._i == self._prodcount:
- self._round += 1
- self._i = 0
-
- if p.matches(token):
- self._roundstarted = True
- # reset nested Choice or Prod to use from start
- p.reset()
- return p
-
- elif p.optional:
- continue
-
- elif round < self._min:
- raise Missing(u'Missing token for production %s' % p)
-
- elif not token:
- if self._roundstarted:
- raise Missing(u'Missing token for production %s' % p)
- else:
- raise Done()
-
- else:
- raise NoMatch(u'No matching production for token')
-
- if token:
- raise Exhausted(u'Extra token')
-
- def __str__(self):
- return u'Sequence(%s)' % u', '.join([str(x) for x in self._prods])
-
-
-class Prod(object):
- """Single Prod in Sequence or Choice."""
- def __init__(self, name, match, optional=False,
- toSeq=None, toStore=None,
- stop=False, stopAndKeep=False,
- nextSor=False, mayEnd=False,
- storeToken=None,
- exception=None):
- """
- name
- name used for error reporting
- match callback
- function called with parameters tokentype and tokenvalue
- returning True, False or raising ParseError
- toSeq callback (optional) or False
- calling toSeq(token, tokens) returns (type_, val) == (token[0], token[1])
- to be appended to seq else simply unaltered (type_, val)
-
- if False nothing is added
-
- toStore (optional)
- key to save util.Item to store or callback(store, util.Item)
- optional = False
- wether Prod is optional or not
- stop = False
- if True stop parsing of tokens here
- stopAndKeep
- if True stop parsing of tokens here but return stopping
- token in unused tokens
- nextSor=False
- next is S or other like , or / (CSSValue)
- mayEnd = False
- no token must follow even defined by Sequence.
- Used for operator ',/ ' currently only
-
- storeToken = None
- if True toStore saves simple token tuple and not and Item object
- to store. Old style processing, TODO: resolve
-
- exception = None
- exception to be raised in case of error, normaly SyntaxErr
- """
- self._name = name
- self.match = match
- self.optional = optional
- self.stop = stop
- self.stopAndKeep = stopAndKeep
- self.nextSor = nextSor
- self.mayEnd = mayEnd
- self.storeToken = storeToken
- self.exception = exception
-
- def makeToStore(key):
- "Return a function used by toStore."
- def toStore(store, item):
- "Set or append store item."
- if key in store:
- _v = store[key]
- if not isinstance(_v, list):
- store[key] = [_v]
- store[key].append(item)
- else:
- store[key] = item
- return toStore
-
- if toSeq or toSeq is False:
- # called: seq.append(toSeq(value))
- self.toSeq = toSeq
- else:
- self.toSeq = lambda t, tokens: (t[0], t[1])
-
- if hasattr(toStore, '__call__'):
- self.toStore = toStore
- elif toStore:
- self.toStore = makeToStore(toStore)
- else:
- # always set!
- self.toStore = None
-
- def matches(self, token):
- """Return if token matches."""
- if not token:
- return False
- type_, val, line, col = token
- return self.match(type_, val)
-
- def reset(self):
- pass
-
- def __str__(self):
- return self._name
-
- def __repr__(self):
- return "" % (
- self.__class__.__name__, self._name, id(self))
-
-
-# global tokenizer as there is only one!
-tokenizer = cssutils.tokenize2.Tokenizer()
-
-class ProdParser(object):
- """Productions parser."""
- def __init__(self, clear=True):
- self.types = cssutils.cssproductions.CSSProductions
- self._log = cssutils.log
- if clear:
- tokenizer.clear()
-
- def _texttotokens(self, text):
- """Build a generator which is the only thing that is parsed!
- old classes may use lists etc
- """
- if isinstance(text, basestring):
- # DEFAULT, to tokenize strip space
- return tokenizer.tokenize(text.strip())
-
- elif isinstance(text, tuple):
- # OLD: (token, tokens) or a single token
- if len(text) == 2:
- # (token, tokens)
- chain([token], tokens)
- else:
- # single token
- return iter([text])
-
- elif isinstance(text, list):
- # OLD: generator from list
- return iter(text)
-
- else:
- # DEFAULT, already tokenized, assume generator
- return text
-
- def _SorTokens(self, tokens, until=',/'):
- """New tokens generator which has S tokens removed,
- if followed by anything in ``until``, normally a ``,``."""
- for token in tokens:
- if token[0] == self.types.S:
- try:
- next_ = tokens.next()
- except StopIteration:
- yield token
- else:
- if next_[1] in until:
- # omit S as e.g. ``,`` has been found
- yield next_
- elif next_[0] == self.types.COMMENT:
- # pass COMMENT
- yield next_
- else:
- yield token
- yield next_
-
- elif token[0] == self.types.COMMENT:
- # pass COMMENT
- yield token
- else:
- yield token
- break
- # normal mode again
- for token in tokens:
- yield token
-
-
- def parse(self, text, name, productions, keepS=False, store=None):
- """
- text (or token generator)
- to parse, will be tokenized if not a generator yet
-
- may be:
- - a string to be tokenized
- - a single token, a tuple
- - a tuple of (token, tokensGenerator)
- - already tokenized so a tokens generator
-
- name
- used for logging
- productions
- used to parse tokens
- keepS
- if WS should be added to Seq or just be ignored
- store UPDATED
- If a Prod defines ``toStore`` the key defined there
- is a key in store to be set or if store[key] is a list
- the next Item is appended here.
-
- TODO: NEEDED? :
- Key ``raw`` is always added and holds all unprocessed
- values found
-
- returns
- :wellformed: True or False
- :seq: a filled cssutils.util.Seq object which is NOT readonly yet
- :store: filled keys defined by Prod.toStore
- :unusedtokens: token generator containing tokens not used yet
- """
- tokens = self._texttotokens(text)
- if not tokens:
- self._log.error(u'No content to parse.')
- # TODO: return???
-
- seq = cssutils.util.Seq(readonly=False)
- if not store: # store for specific values
- store = {}
- prods = [productions] # stack of productions
- wellformed = True
-
- # while no real token is found any S are ignored
- started = False
- stopall = False
- prod = None
- # flag if default S handling should be done
- defaultS = True
- while True:
- try:
- token = tokens.next()
- except StopIteration:
- break
- type_, val, line, col = token
-
- # default productions
- if type_ == self.types.COMMENT:
- # always append COMMENT
- seq.append(cssutils.css.CSSComment(val),
- cssutils.css.CSSComment, line, col)
- elif defaultS and type_ == self.types.S:
- # append S (but ignore starting ones)
- if not keepS or not started:
- continue
- else:
- seq.append(val, type_, line, col)
-# elif type_ == self.types.ATKEYWORD:
-# # @rule
-# r = cssutils.css.CSSUnknownRule(cssText=val)
-# seq.append(r, type(r), line, col)
- elif type_ == self.types.INVALID:
- # invalidate parse
- wellformed = False
- self._log.error(u'Invalid token: %r' % (token,))
- break
- elif type_ == 'EOF':
- # do nothing? (self.types.EOF == True!)
- pass
- else:
- started = True # check S now
- nextSor = False # reset
-
- try:
- while True:
- # find next matching production
- try:
- prod = prods[-1].nextProd(token)
- except (Exhausted, NoMatch), e:
- # try next
- prod = None
- if isinstance(prod, Prod):
- # found actual Prod, not a Choice or Sequence
- break
- elif prod:
- # nested Sequence, Choice
- prods.append(prod)
- else:
- # nested exhausted, try in parent
- if len(prods) > 1:
- prods.pop()
- else:
- raise ParseError('No match')
- except ParseError, e:
- wellformed = False
- self._log.error(u'%s: %s: %r' % (name, e, token))
- break
- else:
- # process prod
- if prod.toSeq and not prod.stopAndKeep:
- type_, val = prod.toSeq(token, tokens)
- if val is not None:
- seq.append(val, type_, line, col)
- if prod.toStore:
- if not prod.storeToken:
- prod.toStore(store, seq[-1])
- else:
- # workaround for now for old style token
- # parsing!
- # TODO: remove when all new style
- prod.toStore(store, token)
-
- if prod.stop: # EOF?
- # stop here and ignore following tokens
- break
-
- if prod.stopAndKeep: # e.g. ;
- # stop here and ignore following tokens
- # but keep this token for next run
- tokenizer.push(token)
- stopall = True
- break
-
- if prod.nextSor:
- # following is S or other token (e.g. ",")?
- # remove S if
- tokens = self._SorTokens(tokens, ',/')
- defaultS = False
- else:
- defaultS = True
-
- lastprod = prod
-
- if not stopall:
- # stop immediately
- while True:
- # all productions exhausted?
- try:
- prod = prods[-1].nextProd(token=None)
- except Done, e:
- # ok
- prod = None
-
- except Missing, e:
- prod = None
- # last was a S operator which may End a Sequence, then ok
- if hasattr(lastprod, 'mayEnd') and not lastprod.mayEnd:
- wellformed = False
- self._log.error(u'%s: %s' % (name, e))
-
- except ParseError, e:
- prod = None
- wellformed = False
- self._log.error(u'%s: %s' % (name, e))
-
- else:
- if prods[-1].optional:
- prod = None
- elif prod and prod.optional:
- # ignore optional
- continue
-
- if prod and not prod.optional:
- wellformed = False
- self._log.error(u'%s: Missing token for production %r'
- % (name, str(prod)))
- break
- elif len(prods) > 1:
- # nested exhausted, next in parent
- prods.pop()
- else:
- break
-
- # trim S from end
- seq.rstrip()
- return wellformed, seq, store, tokens
-
-
-class PreDef(object):
- """Predefined Prod definition for use in productions definition
- for ProdParser instances.
- """
- types = cssutils.cssproductions.CSSProductions
- reHexcolor = re.compile(r'^\#(?:[0-9abcdefABCDEF]{3}|[0-9abcdefABCDEF]{6})$')
-
- @staticmethod
- def calc(toSeq=None, nextSor=False):
- return Prod(name=u'calcfunction',
- match=lambda t, v: u'calc(' == cssutils.helper.normalize(v),
- toSeq=toSeq,
- nextSor=nextSor)
-
- @staticmethod
- def char(name='char', char=u',', toSeq=None,
- stop=False, stopAndKeep=False,
- optional=True, nextSor=False):
- "any CHAR"
- return Prod(name=name, match=lambda t, v: v == char, toSeq=toSeq,
- stop=stop, stopAndKeep=stopAndKeep, optional=optional,
- nextSor=nextSor)
-
- @staticmethod
- def comma():
- return PreDef.char(u'comma', u',')
-
- @staticmethod
- def dimension(nextSor=False, stop=False):
- return Prod(name=u'dimension',
- match=lambda t, v: t == PreDef.types.DIMENSION,
- toSeq=lambda t, tokens: (t[0], cssutils.helper.normalize(t[1])),
- stop=stop,
- nextSor=nextSor)
-
- @staticmethod
- def function(toSeq=None, nextSor=False):
- return Prod(name=u'function',
- match=lambda t, v: t == PreDef.types.FUNCTION,
- toSeq=toSeq,
- nextSor=nextSor)
-
- @staticmethod
- def funcEnd(stop=False):
- ")"
- return PreDef.char(u'end FUNC ")"', u')',
- stop=stop)
-
- @staticmethod
- def hexcolor(stop=False, nextSor=False):
- "#123 or #123456"
- return Prod(name='HEX color',
- match=lambda t, v: (
- t == PreDef.types.HASH and
- PreDef.reHexcolor.match(v)
- ),
- stop=stop,
- nextSor=nextSor)
-
- @staticmethod
- def ident(stop=False, toStore=None, nextSor=False):
- return Prod(name=u'ident',
- match=lambda t, v: t == PreDef.types.IDENT,
- stop=stop,
- toStore=toStore,
- nextSor=nextSor)
-
- @staticmethod
- def number(stop=False, toSeq=None, nextSor=False):
- return Prod(name=u'number',
- match=lambda t, v: t == PreDef.types.NUMBER,
- stop=stop,
- toSeq=toSeq,
- nextSor=nextSor)
-
- @staticmethod
- def percentage(stop=False, toSeq=None, nextSor=False):
- return Prod(name=u'percentage',
- match=lambda t, v: t == PreDef.types.PERCENTAGE,
- stop=stop,
- toSeq=toSeq,
- nextSor=nextSor)
-
- @staticmethod
- def string(stop=False, nextSor=False):
- "string delimiters are removed by default"
- return Prod(name=u'string',
- match=lambda t, v: t == PreDef.types.STRING,
- toSeq=lambda t, tokens: (t[0], cssutils.helper.stringvalue(t[1])),
- stop=stop,
- nextSor=nextSor)
-
- @staticmethod
- def S(name=u'whitespace', toSeq=None, optional=False):
- return Prod(name=name,
- match=lambda t, v: t == PreDef.types.S,
- toSeq=toSeq,
- optional=optional,
- mayEnd=True)
-
- @staticmethod
- def unary(stop=False, toSeq=None, nextSor=False):
- "+ or -"
- return Prod(name=u'unary +-', match=lambda t, v: v in (u'+', u'-'),
- optional=True,
- stop=stop,
- toSeq=toSeq,
- nextSor=nextSor)
-
- @staticmethod
- def uri(stop=False, nextSor=False):
- "'url(' and ')' are removed and URI is stripped"
- return Prod(name=u'URI',
- match=lambda t, v: t == PreDef.types.URI,
- toSeq=lambda t, tokens: (t[0], cssutils.helper.urivalue(t[1])),
- stop=stop,
- nextSor=nextSor)
-
- @staticmethod
- def unicode_range(stop=False, nextSor=False):
- "u+123456-abc normalized to lower `u`"
- return Prod(name='unicode-range',
- match=lambda t, v: t == PreDef.types.UNICODE_RANGE,
- toSeq=lambda t, tokens: (t[0], t[1].lower()),
- stop=stop,
- nextSor=nextSor
- )
-
- @staticmethod
- def variable(toSeq=None, stop=False, nextSor=False):
- return Prod(name=u'variable',
- match=lambda t, v: u'var(' == cssutils.helper.normalize(v),
- toSeq=toSeq,
- stop=stop,
- nextSor=nextSor)
-
- # used for MarginRule for now:
- @staticmethod
- def unknownrule(name=u'@', toStore=None):
- """@rule dummy (matches ATKEYWORD to remove unknown rule tokens from
- stream::
-
- @x;
- @x {...}
-
- no nested yet!
- """
- def rule(tokens):
- saved = []
- for t in tokens:
- saved.append(t)
- if (t[1] == u'}' or t[1] == u';'):
- return cssutils.css.CSSUnknownRule(saved)
-
- return Prod(name=name,
- match=lambda t, v: t == u'ATKEYWORD',
- toSeq=lambda t, tokens: (u'CSSUnknownRule',
- rule(pushtoken(t, tokens))
- ),
- toStore=toStore
- )
diff --git a/libs/cssutils/profiles.py b/libs/cssutils/profiles.py
deleted file mode 100755
index 57c63bd4..00000000
--- a/libs/cssutils/profiles.py
+++ /dev/null
@@ -1,791 +0,0 @@
-"""CSS profiles.
-
-Profiles is based on code by Kevin D. Smith, orginally used as cssvalues,
-thanks!
-"""
-__all__ = ['Profiles']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id: cssproperties.py 1116 2008-03-05 13:52:23Z cthedot $'
-
-import re
-import types
-
-class NoSuchProfileException(Exception):
- """Raised if no profile with given name is found"""
- pass
-
-
-# dummies, replaced in Profiles.addProfile
-_fontRegexReplacements = {
- '__FONT_FAMILY_SINGLE': lambda f: False,
- '__FONT_WITH_1_FAMILY': lambda f: False
- }
-
-def _fontFamilyValidator(families):
- """Check if ``font-family`` value is valid, regex is too slow.
-
- Splits on ``,`` and checks each family separately.
- Somehow naive as font-family name could contain a "," but this is unlikely.
- Still should be a TODO.
- """
- match = _fontRegexReplacements['__FONT_FAMILY_SINGLE']
-
- for f in families.split(u','):
- if not match(f.strip()):
- return False
- return True
-
-def _fontValidator(font):
- """Check if font value is valid, regex is too slow.
-
- Checks everything before ``,`` on basic font value. Everything after should
- be a valid font-family value.
- """
- if u',' in font:
- # split off until 1st family
- font1, families2 = font.split(u',', 1)
- else:
- font1, families2 = font, None
-
- if not _fontRegexReplacements['__FONT_WITH_1_FAMILY'](font1.strip()):
- return False
-
- if families2 and not _fontFamilyValidator(families2):
- return False
-
- return True
-
-
-class Profiles(object):
- """
- All profiles used for validation. ``cssutils.profile`` is a
- preset object of this class and used by all properties for validation.
-
- Predefined profiles are (use
- :meth:`~cssutils.profiles.Profiles.propertiesByProfile` to
- get a list of defined properties):
-
- :attr:`~cssutils.profiles.Profiles.CSS_LEVEL_2`
- Properties defined by CSS2.1
- :attr:`~cssutils.profiles.Profiles.CSS3_BASIC_USER_INTERFACE`
- Currently resize and outline properties only
- :attr:`~cssutils.profiles.Profiles.CSS3_BOX`
- Currently overflow related properties only
- :attr:`~cssutils.profiles.Profiles.CSS3_COLOR`
- CSS 3 color properties
- :attr:`~cssutils.profiles.Profiles.CSS3_PAGED_MEDIA`
- As defined at http://www.w3.org/TR/css3-page/ (at 090307)
-
- Predefined macros are:
-
- :attr:`~cssutils.profiles.Profiles._TOKEN_MACROS`
- Macros containing the token values as defined to CSS2
- :attr:`~cssutils.profiles.Profiles._MACROS`
- Additional general macros.
-
- If you want to redefine any of these macros do this in your custom
- macros.
- """
- CSS_LEVEL_2 = u'CSS Level 2.1'
- CSS3_BACKGROUNDS_AND_BORDERS = u'CSS Backgrounds and Borders Module Level 3'
- CSS3_BASIC_USER_INTERFACE = u'CSS3 Basic User Interface Module'
- CSS3_BOX = CSS_BOX_LEVEL_3 = u'CSS Box Module Level 3'
- CSS3_COLOR = CSS_COLOR_LEVEL_3 = u'CSS Color Module Level 3'
- CSS3_FONTS = u'CSS Fonts Module Level 3'
- CSS3_FONT_FACE = u'CSS Fonts Module Level 3 @font-face properties'
- CSS3_PAGED_MEDIA = u'CSS3 Paged Media Module'
- CSS3_TEXT = u'CSS Text Level 3'
-
- _TOKEN_MACROS = {
- 'ident': r'[-]?{nmstart}{nmchar}*',
- 'name': r'{nmchar}+',
- 'nmstart': r'[_a-z]|{nonascii}|{escape}',
- 'nonascii': r'[^\0-\177]',
- 'unicode': r'\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?',
- 'escape': r'{unicode}|\\[ -~\200-\777]',
- # 'escape': r'{unicode}|\\[ -~\200-\4177777]',
- 'int': r'[-]?\d+',
- 'nmchar': r'[\w-]|{nonascii}|{escape}',
- 'num': r'[-]?\d+|[-]?\d*\.\d+',
- 'positivenum': r'\d+|\d*\.\d+',
- 'number': r'{num}',
- 'string': r'{string1}|{string2}',
- 'string1': r'"(\\\"|[^\"])*"',
- 'uri': r'url\({w}({string}|(\\\)|[^\)])+){w}\)',
- 'string2': r"'(\\\'|[^\'])*'",
- 'nl': r'\n|\r\n|\r|\f',
- 'w': r'\s*',
- }
- _MACROS = {
- 'hexcolor': r'#[0-9a-f]{3}|#[0-9a-f]{6}',
- 'rgbcolor': r'rgb\({w}{int}{w}\,{w}{int}{w}\,{w}{int}{w}\)|rgb\({w}{num}%{w}\,{w}{num}%{w}\,{w}{num}%{w}\)',
- 'namedcolor': r'(transparent|orange|maroon|red|orange|yellow|olive|purple|fuchsia|white|lime|green|navy|blue|aqua|teal|black|silver|gray)',
- 'uicolor': r'(ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText)',
- 'color': r'{namedcolor}|{hexcolor}|{rgbcolor}|{uicolor}',
- #'color': r'(maroon|red|orange|yellow|olive|purple|fuchsia|white|lime|green|navy|blue|aqua|teal|black|silver|gray|ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText)|#[0-9a-f]{3}|#[0-9a-f]{6}|rgb\({w}{int}{w},{w}{int}{w},{w}{int}{w}\)|rgb\({w}{num}%{w},{w}{num}%{w},{w}{num}%{w}\)',
- 'integer': r'{int}',
- 'length': r'0|{num}(em|ex|px|in|cm|mm|pt|pc)',
- 'positivelength': r'0|{positivenum}(em|ex|px|in|cm|mm|pt|pc)',
- 'angle': r'0|{num}(deg|grad|rad)',
- 'time': r'0|{num}m?s',
- 'frequency': r'0|{num}k?Hz',
- 'percentage': r'{num}%',
- 'shadow': '(inset)?{w}{length}{w}{length}{w}{length}?{w}{length}?{w}{color}?'
- }
-
- def __init__(self, log=None):
- """A few profiles are predefined."""
- self._log = log
-
- # macro cache
- self._usedMacros = Profiles._TOKEN_MACROS.copy()
- self._usedMacros.update(Profiles._MACROS.copy())
-
- # to keep order, REFACTOR!
- self._profileNames = []
- # for reset if macro changes
- self._rawProfiles = {}
- # already compiled profiles: {profile: {property: checkfunc, ...}, ...}
- self._profilesProperties = {}
-
- self._defaultProfiles = None
-
- self.addProfiles([(self.CSS_LEVEL_2,
- properties[self.CSS_LEVEL_2],
- macros[self.CSS_LEVEL_2]
- ),
- (self.CSS3_BACKGROUNDS_AND_BORDERS,
- properties[self.CSS3_BACKGROUNDS_AND_BORDERS],
- macros[self.CSS3_BACKGROUNDS_AND_BORDERS]
- ),
- (self.CSS3_BASIC_USER_INTERFACE,
- properties[self.CSS3_BASIC_USER_INTERFACE],
- macros[self.CSS3_BASIC_USER_INTERFACE]
- ),
- (self.CSS3_BOX,
- properties[self.CSS3_BOX],
- macros[self.CSS3_BOX]
- ),
- (self.CSS3_COLOR,
- properties[self.CSS3_COLOR],
- macros[self.CSS3_COLOR]
- ),
- (self.CSS3_FONTS,
- properties[self.CSS3_FONTS],
- macros[self.CSS3_FONTS]
- ),
- # new object for font-face only?
- (self.CSS3_FONT_FACE,
- properties[self.CSS3_FONT_FACE],
- macros[self.CSS3_FONTS]
- ),
- (self.CSS3_PAGED_MEDIA,
- properties[self.CSS3_PAGED_MEDIA],
- macros[self.CSS3_PAGED_MEDIA]
- ),
- (self.CSS3_TEXT,
- properties[self.CSS3_TEXT],
- macros[self.CSS3_TEXT]
- )
- ])
-
- self.__update_knownNames()
-
- def _expand_macros(self, dictionary, macros):
- """Expand macros in token dictionary"""
- def macro_value(m):
- return '(?:%s)' % macros[m.groupdict()['macro']]
-
- for key, value in dictionary.items():
- if not hasattr(value, '__call__'):
- while re.search(r'{[a-z][a-z0-9-]*}', value):
- value = re.sub(r'{(?P[a-z][a-z0-9-]*)}',
- macro_value, value)
- dictionary[key] = value
-
- return dictionary
-
- def _compile_regexes(self, dictionary):
- """Compile all regular expressions into callable objects"""
- for key, value in dictionary.items():
- # might be a function (font-family) as regex is too slow
- if not hasattr(value, '__call__') and not isinstance(value,
- types.FunctionType):
- value = re.compile('^(?:%s)$' % value, re.I).match
- dictionary[key] = value
-
- return dictionary
-
- def __update_knownNames(self):
- self._knownNames = []
- for properties in self._profilesProperties.values():
- self._knownNames.extend(properties.keys())
-
- def _getDefaultProfiles(self):
- "If not explicitly set same as Profiles.profiles but in reverse order."
- if not self._defaultProfiles:
- return self.profiles
- else:
- return self._defaultProfiles
-
- def _setDefaultProfiles(self, profiles):
- "profiles may be a single or a list of profile names"
- if isinstance(profiles, basestring):
- self._defaultProfiles = (profiles,)
- else:
- self._defaultProfiles = profiles
-
- defaultProfiles = property(_getDefaultProfiles,
- _setDefaultProfiles,
- doc=u"Names of profiles to use for validation."
- u"To use e.g. the CSS2 profile set "
- u"``cssutils.profile.defaultProfiles = "
- u"cssutils.profile.CSS_LEVEL_2``")
-
- profiles = property(lambda self: self._profileNames,
- doc=u'Names of all profiles in order as defined.')
-
- knownNames = property(lambda self: self._knownNames,
- doc="All known property names of all profiles.")
-
- def _resetProperties(self, newMacros=None):
- "reset all props from raw values as changes in macros happened"
- # base
- macros = Profiles._TOKEN_MACROS.copy()
- macros.update(Profiles._MACROS.copy())
-
- # former
- for profile in self._profileNames:
- macros.update(self._rawProfiles[profile]['macros'])
-
- # new
- if newMacros:
- macros.update(newMacros)
-
- # reset properties
- self._profilesProperties.clear()
- for profile in self._profileNames:
- properties = self._expand_macros(
- # keep raw
- self._rawProfiles[profile]['properties'].copy(),
- macros)
- self._profilesProperties[profile] = self._compile_regexes(properties)
-
- # save
- self._usedMacros = macros
-
-
- def addProfiles(self, profiles):
- """Add a list of profiles at once. Useful as if profiles define custom
- macros these are used in one go. Using `addProfile` instead my be
- **very** slow instead.
- """
- # add macros
- for profile, properties, macros in profiles:
- if macros:
- self._usedMacros.update(macros)
- self._rawProfiles[profile] = {'macros': macros.copy()}
-
- # only add new properties
- for profile, properties, macros in profiles:
- self.addProfile(profile, properties.copy(), None)
-
-
- def addProfile(self, profile, properties, macros=None):
- """Add a new profile with name `profile` (e.g. 'CSS level 2')
- and the given `properties`.
-
- :param profile:
- the new `profile`'s name
- :param properties:
- a dictionary of ``{ property-name: propery-value }`` items where
- property-value is a regex which may use macros defined in given
- ``macros`` or the standard macros Profiles.tokens and
- Profiles.generalvalues.
-
- ``propery-value`` may also be a function which takes a single
- argument which is the value to validate and which should return
- True or False.
- Any exceptions which may be raised during this custom validation
- are reported or raised as all other cssutils exceptions depending
- on cssutils.log.raiseExceptions which e.g during parsing normally
- is False so the exceptions would be logged only.
- :param macros:
- may be used in the given properties definitions. There are some
- predefined basic macros which may always be used in
- :attr:`Profiles._TOKEN_MACROS` and :attr:`Profiles._MACROS`.
- """
- if macros:
- # check if known macros would change and if yes reset properties
- if len(set(macros.keys()).intersection(self._usedMacros.keys())):
- self._resetProperties(newMacros=macros)
-
- else:
- # no replacement, simply continue
- self._usedMacros.update(macros)
-
- else:
- # might have been set by addProfiles before
- try:
- macros = self._rawProfiles[profile]['macros']
- except KeyError, e:
- macros = {}
-
- # save name and raw props/macros if macros change to completely reset
- self._profileNames.append(profile)
- self._rawProfiles[profile] = {'properties': properties.copy(),
- 'macros': macros.copy()}
- # prepare and save properties
- properties = self._expand_macros(properties, self._usedMacros)
- self._profilesProperties[profile] = self._compile_regexes(properties)
-
- self.__update_knownNames()
-
- # hack for font and font-family which are too slow with regexes
- if '__FONT_WITH_1_FAMILY' in properties:
- _fontRegexReplacements['__FONT_WITH_1_FAMILY'] = properties['__FONT_WITH_1_FAMILY']
- if '__FONT_FAMILY_SINGLE' in properties:
- _fontRegexReplacements['__FONT_FAMILY_SINGLE'] = properties['__FONT_FAMILY_SINGLE']
-
-
- def removeProfile(self, profile=None, all=False):
- """Remove `profile` or remove `all` profiles.
-
- If the removed profile used custom macros all remaining profiles
- are reset to reflect the macro changes. This may be quite an expensive
- operation!
-
- :param profile:
- profile name to remove
- :param all:
- if ``True`` removes all profiles to start with a clean state
- :exceptions:
- - :exc:`cssutils.profiles.NoSuchProfileException`:
- If given `profile` cannot be found.
- """
- if all:
- self._profilesProperties.clear()
- self._rawProfiles.clear()
- del self._profileNames[:]
- else:
- reset = False
-
- try:
- if (self._rawProfiles[profile]['macros']):
- reset = True
-
- del self._profilesProperties[profile]
- del self._rawProfiles[profile]
- del self._profileNames[self._profileNames.index(profile)]
- except KeyError:
- raise NoSuchProfileException(u'No profile %r.' % profile)
-
- else:
- if reset:
- # reset properties as macros were removed
- self._resetProperties()
-
- self.__update_knownNames()
-
- def propertiesByProfile(self, profiles=None):
- """Generator: Yield property names, if no `profiles` is given all
- profile's properties are used.
-
- :param profiles:
- a single profile name or a list of names.
- """
- if not profiles:
- profiles = self.profiles
- elif isinstance(profiles, basestring):
- profiles = (profiles, )
- try:
- for profile in sorted(profiles):
- for name in sorted(self._profilesProperties[profile].keys()):
- yield name
- except KeyError, e:
- raise NoSuchProfileException(e)
-
- def validate(self, name, value):
- """Check if `value` is valid for given property `name` using **any**
- profile.
-
- :param name:
- a property name
- :param value:
- a CSS value (string)
- :returns:
- if the `value` is valid for the given property `name` in any
- profile
- """
- for profile in self.profiles:
- if name in self._profilesProperties[profile]:
- try:
- # custom validation errors are caught
- r = bool(self._profilesProperties[profile][name](value))
- except Exception, e:
- # TODO: more specific exception?
- # Validate should not be fatal though!
- self._log.error(e, error=Exception)
- r = False
- if r:
- return r
- return False
-
- def validateWithProfile(self, name, value, profiles=None):
- """Check if `value` is valid for given property `name` returning
- ``(valid, profile)``.
-
- :param name:
- a property name
- :param value:
- a CSS value (string)
- :param profiles:
- internal parameter used by Property.validate only
- :returns:
- ``valid, matching, profiles`` where ``valid`` is if the `value`
- is valid for the given property `name` in any profile,
- ``matching==True`` if it is valid in the given `profiles`
- and ``profiles`` the profile names for which the value is valid
- (or ``[]`` if not valid at all)
-
- Example::
-
- >>> cssutils.profile.defaultProfiles = cssutils.profile.CSS_LEVEL_2
- >>> print cssutils.profile.validateWithProfile('color', 'rgba(1,1,1,1)')
- (True, False, Profiles.CSS3_COLOR)
- """
- if name not in self.knownNames:
- return False, False, []
- else:
- if not profiles:
- profiles = self.defaultProfiles
- elif isinstance(profiles, basestring):
- profiles = (profiles, )
- for profilename in reversed(profiles):
- # check given profiles
- if name in self._profilesProperties[profilename]:
- validate = self._profilesProperties[profilename][name]
- try:
- if validate(value):
- return True, True, [profilename]
- except Exception, e:
- self._log.error(e, error=Exception)
-
- for profilename in (p for p in self._profileNames
- if p not in profiles):
- # check remaining profiles as well
- if name in self._profilesProperties[profilename]:
- validate = self._profilesProperties[profilename][name]
- try:
- if validate(value):
- return True, False, [profilename]
- except Exception, e:
- self._log.error(e, error=Exception)
-
- names = []
- for profilename, properties in self._profilesProperties.items():
- # return profile to which name belongs
- if name in properties.keys():
- names.append(profilename)
- names.sort()
- return False, False, names
-
-
-properties = {}
-macros = {}
-
-
-"""
-Define some regular expression fragments that will be used as
-macros within the CSS property value regular expressions.
-"""
-macros[Profiles.CSS_LEVEL_2] = {
- 'background-color': r'{color}|transparent|inherit',
- 'background-image': r'{uri}|none|inherit',
- #'background-position': r'({percentage}|{length})(\s*({percentage}|{length}))?|((top|center|bottom)\s*(left|center|right)?)|((left|center|right)\s*(top|center|bottom)?)|inherit',
- 'background-position': r'({percentage}|{length}|left|center|right)(\s*({percentage}|{length}|top|center|bottom))?|((top|center|bottom)\s*(left|center|right)?)|((left|center|right)\s*(top|center|bottom)?)|inherit',
- 'background-repeat': r'repeat|repeat-x|repeat-y|no-repeat|inherit',
- 'background-attachment': r'scroll|fixed|inherit',
- 'shape': r'rect\(({w}({length}|auto}){w},){3}{w}({length}|auto){w}\)',
- 'counter': r'counter\({w}{ident}{w}(?:,{w}{list-style-type}{w})?\)',
- 'identifier': r'{ident}',
- 'family-name': r'{string}|{ident}({w}{ident})*',
- 'generic-family': r'serif|sans-serif|cursive|fantasy|monospace',
- 'absolute-size': r'(x?x-)?(small|large)|medium',
- 'relative-size': r'smaller|larger',
-
- #[[ | ] [, | ]* ] | inherit
- #'font-family': r'(({family-name}|{generic-family})({w},{w}({family-name}|{generic-family}))*)|inherit',
- # EXTREMELY SLOW REGEX
- #'font-family': r'({family-name}({w},{w}{family-name})*)|inherit',
-
- 'font-size': r'{absolute-size}|{relative-size}|{positivelength}|{percentage}|inherit',
- 'font-style': r'normal|italic|oblique|inherit',
- 'font-variant': r'normal|small-caps|inherit',
- 'font-weight': r'normal|bold|bolder|lighter|[1-9]00|inherit',
- 'line-height': r'normal|{number}|{length}|{percentage}|inherit',
- 'list-style-image': r'{uri}|none|inherit',
- 'list-style-position': r'inside|outside|inherit',
- 'list-style-type': r'disc|circle|square|decimal|decimal-leading-zero|lower-roman|upper-roman|lower-greek|lower-(latin|alpha)|upper-(latin|alpha)|armenian|georgian|none|inherit',
- 'margin-width': r'{length}|{percentage}|auto',
- 'padding-width': r'{length}|{percentage}',
- 'specific-voice': r'{ident}',
- 'generic-voice': r'male|female|child',
- 'content': r'{string}|{uri}|{counter}|attr\({w}{ident}{w}\)|open-quote|close-quote|no-open-quote|no-close-quote',
- 'background-attrs': r'{background-color}|{background-image}|{background-repeat}|{background-attachment}|{background-position}',
- 'list-attrs': r'{list-style-type}|{list-style-position}|{list-style-image}',
- 'font-attrs': r'{font-style}|{font-variant}|{font-weight}',
- 'text-attrs': r'underline|overline|line-through|blink',
- 'overflow': r'visible|hidden|scroll|auto|inherit',
-}
-
-"""
-Define the regular expressions for validation all CSS values
-"""
-properties[Profiles.CSS_LEVEL_2] = {
- 'azimuth': r'{angle}|(behind\s+)?(left-side|far-left|left|center-left|center|center-right|right|far-right|right-side)(\s+behind)?|behind|leftwards|rightwards|inherit',
- 'background-attachment': r'{background-attachment}',
- 'background-color': r'{background-color}',
- 'background-image': r'{background-image}',
- 'background-position': r'{background-position}',
- 'background-repeat': r'{background-repeat}',
- # Each piece should only be allowed one time
- 'background': r'{background-attrs}(\s+{background-attrs})*|inherit',
- 'border-collapse': r'collapse|separate|inherit',
- 'border-spacing': r'{length}(\s+{length})?|inherit',
- 'bottom': r'{length}|{percentage}|auto|inherit',
- 'caption-side': r'top|bottom|inherit',
- 'clear': r'none|left|right|both|inherit',
- 'clip': r'{shape}|auto|inherit',
- 'color': r'{color}|inherit',
- 'content': r'none|normal|{content}(\s+{content})*|inherit',
- 'counter-increment': r'({ident}(\s+{integer})?)(\s+({ident}(\s+{integer})?))*|none|inherit',
- 'counter-reset': r'({ident}(\s+{integer})?)(\s+({ident}(\s+{integer})?))*|none|inherit',
- 'cue-after': r'{uri}|none|inherit',
- 'cue-before': r'{uri}|none|inherit',
- 'cue': r'({uri}|none|inherit){1,2}|inherit',
- #'cursor': r'((({uri}{w},{w})*)?(auto|crosshair|default|pointer|move|(e|ne|nw|n|se|sw|s|w)-resize|text|wait|help|progress))|inherit',
- 'direction': r'ltr|rtl|inherit',
- 'display': r'inline|block|list-item|run-in|inline-block|table|inline-table|table-row-group|table-header-group|table-footer-group|table-row|table-column-group|table-column|table-cell|table-caption|none|inherit',
- 'elevation': r'{angle}|below|level|above|higher|lower|inherit',
- 'empty-cells': r'show|hide|inherit',
- 'float': r'left|right|none|inherit',
-
- # regex too slow:
- # 'font-family': r'{font-family}',
- 'font-family': _fontFamilyValidator,
- '__FONT_FAMILY_SINGLE': r'{family-name}',
-
- 'font-size': r'{font-size}',
- 'font-style': r'{font-style}',
- 'font-variant': r'{font-variant}',
- 'font-weight': r'{font-weight}',
-
- # regex too slow and wrong too:
- # 'font': r'({font-attrs}\s+)*{font-size}({w}/{w}{line-height})?\s+{font-family}|caption|icon|menu|message-box|small-caption|status-bar|inherit',
- 'font': _fontValidator,
- '__FONT_WITH_1_FAMILY': r'(({font-attrs}\s+)*{font-size}({w}/{w}{line-height})?\s+{family-name})|caption|icon|menu|message-box|small-caption|status-bar|inherit',
-
- 'height': r'{length}|{percentage}|auto|inherit',
- 'left': r'{length}|{percentage}|auto|inherit',
- 'letter-spacing': r'normal|{length}|inherit',
- 'line-height': r'{line-height}',
- 'list-style-image': r'{list-style-image}',
- 'list-style-position': r'{list-style-position}',
- 'list-style-type': r'{list-style-type}',
- 'list-style': r'{list-attrs}(\s+{list-attrs})*|inherit',
- 'margin-right': r'{margin-width}|inherit',
- 'margin-left': r'{margin-width}|inherit',
- 'margin-top': r'{margin-width}|inherit',
- 'margin-bottom': r'{margin-width}|inherit',
- 'margin': r'{margin-width}(\s+{margin-width}){0,3}|inherit',
- 'max-height': r'{length}|{percentage}|none|inherit',
- 'max-width': r'{length}|{percentage}|none|inherit',
- 'min-height': r'{length}|{percentage}|none|inherit',
- 'min-width': r'{length}|{percentage}|none|inherit',
- 'orphans': r'{integer}|inherit',
- 'overflow': r'{overflow}',
- 'padding-top': r'{padding-width}|inherit',
- 'padding-right': r'{padding-width}|inherit',
- 'padding-bottom': r'{padding-width}|inherit',
- 'padding-left': r'{padding-width}|inherit',
- 'padding': r'{padding-width}(\s+{padding-width}){0,3}|inherit',
- 'page-break-after': r'auto|always|avoid|left|right|inherit',
- 'page-break-before': r'auto|always|avoid|left|right|inherit',
- 'page-break-inside': r'avoid|auto|inherit',
- 'pause-after': r'{time}|{percentage}|inherit',
- 'pause-before': r'{time}|{percentage}|inherit',
- 'pause': r'({time}|{percentage}){1,2}|inherit',
- 'pitch-range': r'{number}|inherit',
- 'pitch': r'{frequency}|x-low|low|medium|high|x-high|inherit',
- 'play-during': r'{uri}(\s+(mix|repeat))*|auto|none|inherit',
- 'position': r'static|relative|absolute|fixed|inherit',
- 'quotes': r'({string}\s+{string})(\s+{string}\s+{string})*|none|inherit',
- 'richness': r'{number}|inherit',
- 'right': r'{length}|{percentage}|auto|inherit',
- 'speak-header': r'once|always|inherit',
- 'speak-numeral': r'digits|continuous|inherit',
- 'speak-punctuation': r'code|none|inherit',
- 'speak': r'normal|none|spell-out|inherit',
- 'speech-rate': r'{number}|x-slow|slow|medium|fast|x-fast|faster|slower|inherit',
- 'stress': r'{number}|inherit',
- 'table-layout': r'auto|fixed|inherit',
- 'text-align': r'left|right|center|justify|inherit',
- 'text-decoration': r'none|{text-attrs}(\s+{text-attrs})*|inherit',
- 'text-indent': r'{length}|{percentage}|inherit',
- 'text-transform': r'capitalize|uppercase|lowercase|none|inherit',
- 'top': r'{length}|{percentage}|auto|inherit',
- 'unicode-bidi': r'normal|embed|bidi-override|inherit',
- 'vertical-align': r'baseline|sub|super|top|text-top|middle|bottom|text-bottom|{percentage}|{length}|inherit',
- 'visibility': r'visible|hidden|collapse|inherit',
- 'voice-family': r'({specific-voice}|{generic-voice}{w},{w})*({specific-voice}|{generic-voice})|inherit',
- 'volume': r'{number}|{percentage}|silent|x-soft|soft|medium|loud|x-loud|inherit',
- 'white-space': r'normal|pre|nowrap|pre-wrap|pre-line|inherit',
- 'widows': r'{integer}|inherit',
- 'width': r'{length}|{percentage}|auto|inherit',
- 'word-spacing': r'normal|{length}|inherit',
- 'z-index': r'auto|{integer}|inherit',
-}
-
-
-macros[Profiles.CSS3_BACKGROUNDS_AND_BORDERS] = {
- 'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset',
- 'border-width': '{length}|thin|medium|thick',
- 'b1': r'{border-width}?({w}{border-style})?({w}{color})?',
- 'b2': r'{border-width}?({w}{color})?({w}{border-style})?',
- 'b3': r'{border-style}?({w}{border-width})?({w}{color})?',
- 'b4': r'{border-style}?({w}{color})?({w}{border-width})?',
- 'b5': r'{color}?({w}{border-style})?({w}{border-width})?',
- 'b6': r'{color}?({w}{border-width})?({w}{border-style})?',
- 'border-attrs': r'{b1}|{b2}|{b3}|{b4}|{b5}|{b6}',
- 'border-radius-part': '({length}|{percentage})(\s+({length}|{percentage}))?'
- }
-properties[Profiles.CSS3_BACKGROUNDS_AND_BORDERS] = {
- 'border-color': r'({color}|transparent)(\s+({color}|transparent)){0,3}|inherit',
- 'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit',
- 'border-top': r'{border-attrs}|inherit',
- 'border-right': r'{border-attrs}|inherit',
- 'border-bottom': r'{border-attrs}|inherit',
- 'border-left': r'{border-attrs}|inherit',
- 'border-top-color': r'{color}|transparent|inherit',
- 'border-right-color': r'{color}|transparent|inherit',
- 'border-bottom-color': r'{color}|transparent|inherit',
- 'border-left-color': r'{color}|transparent|inherit',
- 'border-top-style': r'{border-style}|inherit',
- 'border-right-style': r'{border-style}|inherit',
- 'border-bottom-style': r'{border-style}|inherit',
- 'border-left-style': r'{border-style}|inherit',
- 'border-top-width': r'{border-width}|inherit',
- 'border-right-width': r'{border-width}|inherit',
- 'border-bottom-width': r'{border-width}|inherit',
- 'border-left-width': r'{border-width}|inherit',
- 'border-width': r'{border-width}(\s+{border-width}){0,3}|inherit',
- 'border': r'{border-attrs}|inherit',
- 'border-top-right-radius': '{border-radius-part}',
- 'border-bottom-right-radius': '{border-radius-part}',
- 'border-bottom-left-radius': '{border-radius-part}',
- 'border-top-left-radius': '{border-radius-part}',
- 'border-radius': '({length}{w}|{percentage}{w}){1,4}(/{w}({length}{w}|{percentage}{w}){1,4})?',
- 'box-shadow': 'none|{shadow}({w},{w}{shadow})*',
- }
-
-# CSS3 Basic User Interface Module
-macros[Profiles.CSS3_BASIC_USER_INTERFACE] = {
- 'border-style': macros[Profiles.CSS3_BACKGROUNDS_AND_BORDERS]['border-style'],
- 'border-width': macros[Profiles.CSS3_BACKGROUNDS_AND_BORDERS]['border-width'],
- 'outline-1': r'{outline-color}(\s+{outline-style})?(\s+{outline-width})?',
- 'outline-2': r'{outline-color}(\s+{outline-width})?(\s+{outline-style})?',
- 'outline-3': r'{outline-style}(\s+{outline-color})?(\s+{outline-width})?',
- 'outline-4': r'{outline-style}(\s+{outline-width})?(\s+{outline-color})?',
- 'outline-5': r'{outline-width}(\s+{outline-color})?(\s+{outline-style})?',
- 'outline-6': r'{outline-width}(\s+{outline-style})?(\s+{outline-color})?',
- 'outline-color': r'{color}|invert|inherit',
- 'outline-style': r'auto|{border-style}|inherit',
- 'outline-width': r'{border-width}|inherit',
- }
-properties[Profiles.CSS3_BASIC_USER_INTERFACE] = {
- 'box-sizing': r'content-box|border-box',
- 'cursor': r'((({uri}{w}({number}{w}{number}{w})?,{w})*)?(auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|(e|n|ne|nw|s|se|sw|w|ew|ns|nesw|nwse|col|row)-resize|all-scroll))|inherit',
- 'nav-index': r'auto|{number}|inherit',
- 'outline-color': r'{outline-color}',
- 'outline-style': r'{outline-style}',
- 'outline-width': r'{outline-width}',
- 'outline-offset': r'{length}|inherit',
- #'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit',
- 'outline': r'{outline-1}|{outline-2}|{outline-3}|{outline-4}|{outline-5}|{outline-6}|inherit',
- 'resize': 'none|both|horizontal|vertical|inherit',
- }
-
-# CSS Box Module Level 3
-macros[Profiles.CSS3_BOX] = {
- 'overflow': macros[Profiles.CSS_LEVEL_2]['overflow']
- }
-properties[Profiles.CSS3_BOX] = {
- 'overflow': '{overflow}{w}{overflow}?|inherit',
- 'overflow-x': '{overflow}|inherit',
- 'overflow-y': '{overflow}|inherit'
- }
-
-# CSS Color Module Level 3
-macros[Profiles.CSS3_COLOR] = {
- # orange and transparent in CSS 2.1
- 'namedcolor': r'(currentcolor|transparent|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)',
- # orange?
- 'rgbacolor': r'rgba\({w}{int}{w}\,{w}{int}{w}\,{w}{int}{w}\,{w}{num}{w}\)|rgba\({w}{num}%{w}\,{w}{num}%{w}\,{w}{num}%{w}\,{w}{num}{w}\)',
- 'hslcolor': r'hsl\({w}{int}{w}\,{w}{num}%{w}\,{w}{num}%{w}\)|hsla\({w}{int}{w}\,{w}{num}%{w}\,{w}{num}%{w}\,{w}{num}{w}\)',
- 'x11color': r'aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen',
- 'uicolor': r'(ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText)',
- 'color': r'{namedcolor}|{hexcolor}|{rgbcolor}|{rgbacolor}|{hslcolor}|{x11color}|inherit',
- }
-properties[Profiles.CSS3_COLOR] = {
- 'opacity': r'{num}|inherit',
- }
-
-# CSS Fonts Module Level 3 http://www.w3.org/TR/css3-fonts/
-macros[Profiles.CSS3_FONTS] = {
- #'family-name': r'{string}|{ident}',
- 'family-name': r'{string}|{ident}({w}{ident})*',
- 'font-face-name': 'local\({w}{family-name}{w}\)',
- 'font-stretch-names': r'(ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded)',
- 'unicode-range': r'[uU]\+[0-9A-Fa-f?]{1,6}(\-[0-9A-Fa-f]{1,6})?'
- }
-properties[Profiles.CSS3_FONTS] = {
- 'font-size-adjust': r'{number}|none|inherit',
- 'font-stretch': r'normal|wider|narrower|{font-stretch-names}|inherit'
- }
-properties[Profiles.CSS3_FONT_FACE] = {
- 'font-family': '{family-name}',
- 'font-stretch': r'{font-stretch-names}',
- 'font-style': r'normal|italic|oblique',
- 'font-weight': r'normal|bold|[1-9]00',
- 'src': r'({uri}{w}(format\({w}{string}{w}(\,{w}{string}{w})*\))?|{font-face-name})({w},{w}({uri}{w}(format\({w}{string}{w}(\,{w}{string}{w})*\))?|{font-face-name}))*',
- 'unicode-range': '{unicode-range}({w},{w}{unicode-range})*'
- }
-
-# CSS3 Paged Media
-macros[Profiles.CSS3_PAGED_MEDIA] = {
- 'page-size': 'a5|a4|a3|b5|b4|letter|legal|ledger',
- 'page-orientation': 'portrait|landscape',
- 'page-1': '{page-size}(?:{w}{page-orientation})?',
- 'page-2': '{page-orientation}(?:{w}{page-size})?',
- 'page-size-orientation': '{page-1}|{page-2}',
- 'pagebreak': 'auto|always|avoid|left|right'
- }
-properties[Profiles.CSS3_PAGED_MEDIA] = {
- 'fit': 'fill|hidden|meet|slice',
- 'fit-position': r'auto|(({percentage}|{length})(\s*({percentage}|{length}))?|((top|center|bottom)\s*(left|center|right)?)|((left|center|right)\s*(top|center|bottom)?))',
- 'image-orientation': 'auto|{angle}',
- 'orphans': r'{integer}|inherit',
- 'page': 'auto|{ident}',
- 'page-break-before': '{pagebreak}|inherit',
- 'page-break-after': '{pagebreak}|inherit',
- 'page-break-inside': 'auto|avoid|inherit',
- 'size': '({length}{w}){1,2}|auto|{page-size-orientation}',
- 'widows': r'{integer}|inherit'
- }
-
-macros[Profiles.CSS3_TEXT] = {
- }
-properties[Profiles.CSS3_TEXT] = {
- 'text-shadow': 'none|{shadow}({w},{w}{shadow})*',
- }
diff --git a/libs/cssutils/sac.py b/libs/cssutils/sac.py
deleted file mode 100755
index d46a0b70..00000000
--- a/libs/cssutils/sac.py
+++ /dev/null
@@ -1,428 +0,0 @@
-#!/usr/bin/env python
-"""A validating CSSParser"""
-__all__ = ['CSSParser']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id: parse.py 1754 2009-05-30 14:50:13Z cthedot $'
-
-import helper
-import codecs
-import errorhandler
-import os
-import tokenize2
-import urllib
-import sys
-
-
-class ErrorHandler(object):
- """Basic class for CSS error handlers.
-
- This class class provides a default implementation ignoring warnings and
- recoverable errors and throwing a SAXParseException for fatal errors.
-
- If a CSS application needs to implement customized error handling, it must
- extend this class and then register an instance with the CSS parser
- using the parser's setErrorHandler method. The parser will then report all
- errors and warnings through this interface.
-
- The parser shall use this class instead of throwing an exception: it is
- up to the application whether to throw an exception for different types of
- errors and warnings. Note, however, that there is no requirement that the
- parser continue to provide useful information after a call to fatalError
- (in other words, a CSS driver class could catch an exception and report a
- fatalError).
- """
- def __init__(self):
- self._log = errorhandler.ErrorHandler()
-
- def error(self, exception, token=None):
- self._log.error(exception, token, neverraise=True)
-
- def fatal(self, exception, token=None):
- self._log.fatal(exception, token)
-
- def warn(self, exception, token=None):
- self._log.warn(exception, token, neverraise=True)
-
-
-class DocumentHandler(object):
- """
- void endFontFace()
- Receive notification of the end of a font face statement.
- void endMedia(SACMediaList media)
- Receive notification of the end of a media statement.
- void endPage(java.lang.String name, java.lang.String pseudo_page)
- Receive notification of the end of a media statement.
- void importStyle(java.lang.String uri, SACMediaList media, java.lang.String defaultNamespaceURI)
- Receive notification of a import statement in the style sheet.
- void startFontFace()
- Receive notification of the beginning of a font face statement.
- void startMedia(SACMediaList media)
- Receive notification of the beginning of a media statement.
- void startPage(java.lang.String name, java.lang.String pseudo_page)
- Receive notification of the beginning of a page statement.
- """
- def __init__(self):
- def log(msg):
- sys.stderr.write('INFO\t%s\n' % msg)
- self._log = log
-
- def comment(self, text, line=None, col=None):
- "Receive notification of a comment."
- self._log("comment %r at [%s, %s]" % (text, line, col))
-
- def startDocument(self, encoding):
- "Receive notification of the beginning of a style sheet."
- # source
- self._log("startDocument encoding=%s" % encoding)
-
- def endDocument(self, source=None, line=None, col=None):
- "Receive notification of the end of a document."
- self._log("endDocument EOF")
-
- def importStyle(self, uri, media, name, line=None, col=None):
- "Receive notification of a import statement in the style sheet."
- # defaultNamespaceURI???
- self._log("importStyle at [%s, %s]" % (line, col))
-
- def namespaceDeclaration(self, prefix, uri, line=None, col=None):
- "Receive notification of an unknown rule t-rule not supported by this parser."
- # prefix might be None!
- self._log("namespaceDeclaration at [%s, %s]" % (line, col))
-
- def startSelector(self, selectors=None, line=None, col=None):
- "Receive notification of the beginning of a rule statement."
- # TODO selectorList!
- self._log("startSelector at [%s, %s]" % (line, col))
-
- def endSelector(self, selectors=None, line=None, col=None):
- "Receive notification of the end of a rule statement."
- self._log("endSelector at [%s, %s]" % (line, col))
-
- def property(self, name, value='TODO', important=False, line=None, col=None):
- "Receive notification of a declaration."
- # TODO: value is LexicalValue?
- self._log("property %r at [%s, %s]" % (name, line, col))
-
- def ignorableAtRule(self, atRule, line=None, col=None):
- "Receive notification of an unknown rule t-rule not supported by this parser."
- self._log("ignorableAtRule %r at [%s, %s]" % (atRule, line, col))
-
-
-
-class EchoHandler(DocumentHandler):
- "Echos all input to property `out`"
- def __init__(self):
- super(EchoHandler, self).__init__()
- self._out = []
-
- out = property(lambda self: u''.join(self._out))
-
- def startDocument(self, encoding):
- super(EchoHandler, self).startDocument(encoding)
- if u'utf-8' != encoding:
- self._out.append(u'@charset "%s";\n' % encoding)
-
-# def comment(self, text, line=None, col=None):
-# self._out.append(u'/*%s*/' % text)
-
- def importStyle(self, uri, media, name, line=None, col=None):
- "Receive notification of a import statement in the style sheet."
- # defaultNamespaceURI???
- super(EchoHandler, self).importStyle(uri, media, name, line, col)
- self._out.append(u'@import %s%s%s;\n' % (helper.string(uri),
- u'%s ' % media if media else u'',
- u'%s ' % name if name else u'')
- )
-
-
- def namespaceDeclaration(self, prefix, uri, line=None, col=None):
- super(EchoHandler, self).namespaceDeclaration(prefix, uri, line, col)
- self._out.append(u'@namespace %s%s;\n' % (u'%s ' % prefix if prefix else u'',
- helper.string(uri)))
-
- def startSelector(self, selectors=None, line=None, col=None):
- super(EchoHandler, self).startSelector(selectors, line, col)
- if selectors:
- self._out.append(u', '.join(selectors))
- self._out.append(u' {\n')
-
- def endSelector(self, selectors=None, line=None, col=None):
- self._out.append(u' }')
-
- def property(self, name, value, important=False, line=None, col=None):
- super(EchoHandler, self).property(name, value, line, col)
- self._out.append(u' %s: %s%s;\n' % (name, value,
- u' !important' if important else u''))
-
-
-class Parser(object):
- """
- java.lang.String getParserVersion()
- Returns a string about which CSS language is supported by this parser.
- boolean parsePriority(InputSource source)
- Parse a CSS priority value (e.g.
- LexicalUnit parsePropertyValue(InputSource source)
- Parse a CSS property value.
- void parseRule(InputSource source)
- Parse a CSS rule.
- SelectorList parseSelectors(InputSource source)
- Parse a comma separated list of selectors.
- void parseStyleDeclaration(InputSource source)
- Parse a CSS style declaration (without '{' and '}').
- void parseStyleSheet(InputSource source)
- Parse a CSS document.
- void parseStyleSheet(java.lang.String uri)
- Parse a CSS document from a URI.
- void setConditionFactory(ConditionFactory conditionFactory)
-
- void setDocumentHandler(DocumentHandler handler)
- Allow an application to register a document event handler.
- void setErrorHandler(ErrorHandler handler)
- Allow an application to register an error event handler.
- void setLocale(java.util.Locale locale)
- Allow an application to request a locale for errors and warnings.
- void setSelectorFactory(SelectorFactory selectorFactory)
- """
- def __init__(self, documentHandler=None, errorHandler=None):
- self._tokenizer = tokenize2.Tokenizer()
- if documentHandler:
- self.setDocumentHandler(documentHandler)
- else:
- self.setDocumentHandler(DocumentHandler())
-
- if errorHandler:
- self.setErrorHandler(errorHandler)
- else:
- self.setErrorHandler(ErrorHandler())
-
- def parseString(self, cssText, encoding=None):
- if isinstance(cssText, str):
- cssText = codecs.getdecoder('css')(cssText, encoding=encoding)[0]
-
- tokens = self._tokenizer.tokenize(cssText, fullsheet=True)
-
- def COMMENT(val, line, col):
- self._handler.comment(val[2:-2], line, col)
-
- def EOF(val, line, col):
- self._handler.endDocument(val, line, col)
-
- def simple(t):
- map = {'COMMENT': COMMENT,
- 'S': lambda val, line, col: None,
- 'EOF': EOF}
- type_, val, line, col = t
- if type_ in map:
- map[type_](val, line, col)
- return True
- else:
- return False
-
- # START PARSING
- t = tokens.next()
- type_, val, line, col = t
-
- encoding = 'utf-8'
- if 'CHARSET_SYM' == type_:
- # @charset "encoding";
- # S
- encodingtoken = tokens.next()
- semicolontoken = tokens.next()
- if 'STRING' == type_:
- encoding = helper.stringvalue(val)
- # ;
- if 'STRING' == encodingtoken[0] and semicolontoken:
- encoding = helper.stringvalue(encodingtoken[1])
- else:
- self._errorHandler.fatal(u'Invalid @charset')
-
- t = tokens.next()
- type_, val, line, col = t
-
- self._handler.startDocument(encoding)
-
- while True:
- start = (line, col)
- try:
- if simple(t):
- pass
-
- elif 'ATKEYWORD' == type_ or type_ in ('PAGE_SYM', 'MEDIA_SYM', 'FONT_FACE_SYM'):
- atRule = [val]
- braces = 0
- while True:
- # read till end ;
- # TODO: or {}
- t = tokens.next()
- type_, val, line, col = t
- atRule.append(val)
- if u';' == val and not braces:
- break
- elif u'{' == val:
- braces += 1
- elif u'}' == val:
- braces -= 1
- if braces == 0:
- break
-
- self._handler.ignorableAtRule(u''.join(atRule), *start)
-
- elif 'IMPORT_SYM' == type_:
- # import URI or STRING media? name?
- uri, media, name = None, None, None
- while True:
- t = tokens.next()
- type_, val, line, col = t
- if 'STRING' == type_:
- uri = helper.stringvalue(val)
- elif 'URI' == type_:
- uri = helper.urivalue(val)
- elif u';' == val:
- break
-
- if uri:
- self._handler.importStyle(uri, media, name)
- else:
- self._errorHandler.error(u'Invalid @import'
- u' declaration at %r'
- % (start,))
-
- elif 'NAMESPACE_SYM' == type_:
- prefix, uri = None, None
- while True:
- t = tokens.next()
- type_, val, line, col = t
- if 'IDENT' == type_:
- prefix = val
- elif 'STRING' == type_:
- uri = helper.stringvalue(val)
- elif 'URI' == type_:
- uri = helper.urivalue(val)
- elif u';' == val:
- break
- if uri:
- self._handler.namespaceDeclaration(prefix, uri, *start)
- else:
- self._errorHandler.error(u'Invalid @namespace'
- u' declaration at %r'
- % (start,))
-
- else:
- # CSSSTYLERULE
- selector = []
- selectors = []
- while True:
- # selectors[, selector]* {
- if 'S' == type_:
- selector.append(u' ')
- elif simple(t):
- pass
- elif u',' == val:
- selectors.append(u''.join(selector).strip())
- selector = []
- elif u'{' == val:
- selectors.append(u''.join(selector).strip())
- self._handler.startSelector(selectors, *start)
- break
- else:
- selector.append(val)
-
- t = tokens.next()
- type_, val, line, col = t
-
- end = None
- while True:
- # name: value [!important][;name: value [!important]]*;?
- name, value, important = None, [], False
-
- while True:
- # name:
- t = tokens.next()
- type_, val, line, col = t
- if 'S' == type_:
- pass
- elif simple(t):
- pass
- elif 'IDENT' == type_:
- if name:
- self._errorHandler.error('more than one property name', t)
- else:
- name = val
- elif u':' == val:
- if not name:
- self._errorHandler.error('no property name', t)
- break
- elif u';' == val:
- self._errorHandler.error('premature end of property', t)
- end = val
- break
- elif u'}' == val:
- if name:
- self._errorHandler.error('premature end of property', t)
- end = val
- break
- else:
- self._errorHandler.error('unexpected property name token %r' % val, t)
-
- while not u';' == end and not u'}' == end:
- # value !;}
- t = tokens.next()
- type_, val, line, col = t
-
- if 'S' == type_:
- value.append(u' ')
- elif simple(t):
- pass
- elif u'!' == val or u';' == val or u'}' == val:
- value = ''.join(value).strip()
- if not value:
- self._errorHandler.error('premature end of property (no value)', t)
- end = val
- break
- else:
- value.append(val)
-
- while u'!' == end:
- # !important
- t = tokens.next()
- type_, val, line, col = t
-
- if simple(t):
- pass
- elif u'IDENT' == type_ and not important:
- important = True
- elif u';' == val or u'}' == val:
- end = val
- break
- else:
- self._errorHandler.error('unexpected priority token %r' % val)
-
- if name and value:
- self._handler.property(name, value, important)
-
- if u'}' == end:
- self._handler.endSelector(selectors, line=line, col=col)
- break
- else:
- # reset
- end = None
-
- else:
- self._handler.endSelector(selectors, line=line, col=col)
-
- t = tokens.next()
- type_, val, line, col = t
-
- except StopIteration:
- break
-
-
-
- def setDocumentHandler(self, handler):
- "Allow an application to register a document event `handler`."
- self._handler = handler
-
- def setErrorHandler(self, handler):
- "TODO"
- self._errorHandler = handler
-
\ No newline at end of file
diff --git a/libs/cssutils/script.py b/libs/cssutils/script.py
deleted file mode 100755
index 56c1c6e2..00000000
--- a/libs/cssutils/script.py
+++ /dev/null
@@ -1,362 +0,0 @@
-"""classes and functions used by cssutils scripts
-"""
-__all__ = ['CSSCapture', 'csscombine']
-__docformat__ = 'restructuredtext'
-__version__ = '$Id: parse.py 1323 2008-07-06 18:13:57Z cthedot $'
-
-import HTMLParser
-import codecs
-import cssutils
-import errno
-import logging
-import os
-import sys
-import urllib2
-import urlparse
-
-try:
- import cssutils.encutils as encutils
-except ImportError:
- try:
- import encutils
- except ImportError:
- sys.exit("You need encutils from http://cthedot.de/encutils/")
-
-# types of sheets in HTML
-LINK = 0 #
-STYLE = 1 #
-
-class CSSCaptureHTMLParser(HTMLParser.HTMLParser):
- """CSSCapture helper: Parse given data for link and style elements"""
- curtag = u''
- sheets = [] # (type, [atts, cssText])
-
- def _loweratts(self, atts):
- return dict([(a.lower(), v.lower()) for a, v in atts])
-
- def handle_starttag(self, tag, atts):
- if tag == u'link':
- atts = self._loweratts(atts)
- if u'text/css' == atts.get(u'type', u''):
- self.sheets.append((LINK, atts))
- elif tag == u'style':
- # also get content of style
- atts = self._loweratts(atts)
- if u'text/css' == atts.get(u'type', u''):
- self.sheets.append((STYLE, [atts, u'']))
- self.curtag = tag
- else:
- # close as only intersting