mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
IGN:...
This commit is contained in:
parent
308b128089
commit
fc934f1f66
@ -23,3 +23,8 @@ installer/windows/calibre/build.log
|
||||
src/calibre/translations/.errors
|
||||
src/calibre/plugins/*
|
||||
src/calibre/gui2/pictureflow/.build
|
||||
src/cssutils/.svn/
|
||||
src/cssutils/_todo/
|
||||
src/cssutils/scripts/
|
||||
src/cssutils/css/.svn/
|
||||
src/cssutils/stylesheets/.svn/
|
||||
|
@ -11,6 +11,20 @@ from calibre.utils.config import Config, StringConfig
|
||||
from calibre.utils.zipfile import ZipFile, ZIP_STORED
|
||||
from calibre.ebooks.html import config as common_config
|
||||
|
||||
class DefaultProfile(object):
|
||||
|
||||
flow_size = sys.maxint
|
||||
|
||||
class PRS505(DefaultProfile):
|
||||
|
||||
flow_size = 300000
|
||||
|
||||
|
||||
PROFILES = {
|
||||
'PRS505' : PRS505,
|
||||
'None' : DefaultProfile,
|
||||
}
|
||||
|
||||
def initialize_container(path_to_container, opf_name='metadata.opf'):
|
||||
'''
|
||||
Create an empty EPUB document, with a default skeleton.
|
||||
@ -43,7 +57,8 @@ def config(defaults=None):
|
||||
|
||||
c.add_opt('output', ['-o', '--output'], default=None,
|
||||
help=_('The output EPUB file. If not specified, it is derived from the input file name.'))
|
||||
|
||||
c.add_opt('profile', ['--profile'], default='PRS505', choices=list(PROFILES.keys()),
|
||||
help=_('Profile of the target device this EPUB is meant for. Set to None to create a device independent EPUB. The profile is used for device specific restrictions on the EPUB. Choices are: ')+str(list(PROFILES.keys())))
|
||||
|
||||
structure = c.add_group('structure detection', _('Control auto-detection of document structure.'))
|
||||
structure('chapter', ['--chapter'], default="//*[re:match(name(), 'h[1-2]') and re:test(., 'chapter|book|section', 'i')]",
|
||||
|
@ -17,7 +17,7 @@ from calibre.ebooks.epub import config as common_config
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.ebooks.metadata.toc import TOC
|
||||
from calibre.ebooks.epub import initialize_container
|
||||
from calibre.ebooks.epub import initialize_container, PROFILES
|
||||
|
||||
|
||||
class HTMLProcessor(Processor):
|
||||
@ -38,7 +38,14 @@ class HTMLProcessor(Processor):
|
||||
|
||||
self.split()
|
||||
|
||||
|
||||
def save(self):
|
||||
file = Processor.save(self)
|
||||
with open(file, 'rb') as f:
|
||||
f.seek(0, 2)
|
||||
size = f.tell()
|
||||
if size > self.opts.profile.flow_size:
|
||||
self.split()
|
||||
|
||||
|
||||
def collect_font_statistics(self):
|
||||
'''
|
||||
@ -87,6 +94,7 @@ def convert(htmlfile, opts, notification=None):
|
||||
htmlfile = os.path.abspath(htmlfile)
|
||||
if opts.output is None:
|
||||
opts.output = os.path.splitext(os.path.basename(htmlfile))[0] + '.epub'
|
||||
opts.profile = PROFILES[opts.profile]
|
||||
opts.output = os.path.abspath(opts.output)
|
||||
if htmlfile.lower().endswith('.opf'):
|
||||
opf = OPFReader(htmlfile, os.path.dirname(os.path.abspath(htmlfile)))
|
||||
|
@ -438,7 +438,7 @@ class Processor(Parser):
|
||||
style = etree.SubElement(head, 'style', attrib={'type':'text/css'})
|
||||
style.text='\n'+self.css
|
||||
style.tail = '\n\n'
|
||||
Parser.save(self)
|
||||
return Parser.save(self)
|
||||
|
||||
def populate_toc(self, toc):
|
||||
if self.level >= self.opts.max_toc_recursion:
|
||||
|
@ -1,4 +0,0 @@
|
||||
from csscombine import csscombine
|
||||
__all__ = ["csscapture", "csscombine", "cssparse"]
|
||||
|
||||
|
@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Retrieve all CSS stylesheets including embedded for a given URL.
|
||||
Retrieve as StyleSheetList or save to disk - raw, parsed or minified version.
|
||||
|
||||
TODO:
|
||||
- maybe use DOM 3 load/save?
|
||||
- logger class which handles all cases when no log is given...
|
||||
- saveto: why does urllib2 hang?
|
||||
"""
|
||||
__all__ = ['CSSCapture']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: csscapture.py 1332 2008-07-09 13:12:56Z cthedot $'
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import sys
|
||||
from cssutils.script import CSSCapture
|
||||
|
||||
def main(args=None):
|
||||
usage = "usage: %prog [options] URL"
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option('-d', '--debug', action='store_true', dest='debug',
|
||||
help='show debug messages during capturing')
|
||||
parser.add_option('-m', '--minified', action='store_true', dest='minified',
|
||||
help='saves minified version of captured files')
|
||||
parser.add_option('-n', '--notsave', action='store_true', dest='notsave',
|
||||
help='if given files are NOT saved, only log is written')
|
||||
# parser.add_option('-r', '--saveraw', action='store_true', dest='saveraw',
|
||||
# help='if given saves raw css otherwise cssutils\' parsed files')
|
||||
parser.add_option('-s', '--saveto', action='store', dest='saveto',
|
||||
help='saving retrieved files to "saveto", defaults to "_CSSCapture_SAVED"')
|
||||
parser.add_option('-u', '--useragent', action='store', dest='ua',
|
||||
help='useragent to use for request of URL, default is urllib2s default')
|
||||
options, url = parser.parse_args()
|
||||
|
||||
# TODO:
|
||||
options.saveraw = False
|
||||
|
||||
if not url:
|
||||
parser.error('no URL given')
|
||||
else:
|
||||
url = url[0]
|
||||
|
||||
if options.debug:
|
||||
level = logging.DEBUG
|
||||
else:
|
||||
level = logging.INFO
|
||||
|
||||
# START
|
||||
c = CSSCapture(ua=options.ua, defaultloglevel=level)
|
||||
|
||||
stylesheetlist = c.capture(url)
|
||||
|
||||
if options.notsave is None or not options.notsave:
|
||||
if options.saveto:
|
||||
saveto = options.saveto
|
||||
else:
|
||||
saveto = u'_CSSCapture_SAVED'
|
||||
c.saveto(saveto, saveraw=options.saveraw, minified=options.minified)
|
||||
else:
|
||||
for i, s in enumerate(stylesheetlist):
|
||||
print u'''%s.
|
||||
encoding: %r
|
||||
title: %r
|
||||
href: %r''' % (i + 1, s.encoding, s.title, s.href)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,90 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Combine sheets referred to by @import rules in a given CSS proxy sheet
|
||||
into a single new sheet.
|
||||
|
||||
- proxy currently is a path (no URI!)
|
||||
- in @import rules only relative paths do work for now but should be used
|
||||
anyway
|
||||
- currently no nested @imports are resolved
|
||||
- messages are send to stderr
|
||||
- output to stdout.
|
||||
|
||||
Example::
|
||||
|
||||
csscombine sheets\csscombine-proxy.css -m -t ascii -s utf-8
|
||||
1>combined.css 2>log.txt
|
||||
|
||||
results in log.txt::
|
||||
|
||||
COMBINING sheets/csscombine-proxy.css
|
||||
USING SOURCE ENCODING: css
|
||||
* PROCESSING @import sheets\csscombine-1.css
|
||||
* PROCESSING @import sheets\csscombine-2.css
|
||||
INFO Nested @imports are not combined: @import "1.css";
|
||||
SETTING TARGET ENCODING: ascii
|
||||
|
||||
and combined.css::
|
||||
|
||||
@charset "ascii";@import"1.css";@namespaces2"uri";s2|sheet-1{top:1px}s2|sheet-2{top:2px}proxy{top:3px}
|
||||
|
||||
or without option -m::
|
||||
|
||||
@charset "ascii";
|
||||
@import "1.css";
|
||||
@namespace s2 "uri";
|
||||
@namespace other "other";
|
||||
/* proxy sheet were imported sheets should be combined */
|
||||
/* non-ascii chars: \F6 \E4 \FC */
|
||||
/* @import "csscombine-1.css"; */
|
||||
/* combined sheet 1 */
|
||||
s2|sheet-1 {
|
||||
top: 1px
|
||||
}
|
||||
/* @import url(csscombine-2.css); */
|
||||
/* combined sheet 2 */
|
||||
s2|sheet-2 {
|
||||
top: 2px
|
||||
}
|
||||
proxy {
|
||||
top: 3px
|
||||
}
|
||||
|
||||
TODO
|
||||
- URL or file hrefs? URI should be default
|
||||
- no nested @imports are resolved yet
|
||||
- maybe add a config file which is used?
|
||||
|
||||
"""
|
||||
__all__ = ['csscombine']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: csscombine.py 1332 2008-07-09 13:12:56Z cthedot $'
|
||||
|
||||
import optparse
|
||||
import sys
|
||||
from cssutils.script import csscombine
|
||||
|
||||
def main(args=None):
|
||||
usage = "usage: %prog [options] path"
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option('-s', '--sourceencoding', action='store',
|
||||
dest='sourceencoding',
|
||||
help='encoding of input, defaulting to "css". If given overwrites other encoding information like @charset declarations')
|
||||
parser.add_option('-t', '--targetencoding', action='store',
|
||||
dest='targetencoding',
|
||||
help='encoding of output, defaulting to "UTF-8"', default='utf-8')
|
||||
parser.add_option('-m', '--minify', action='store_true', dest='minify',
|
||||
default=False,
|
||||
help='saves minified version of combined files, defaults to False')
|
||||
options, path = parser.parse_args()
|
||||
|
||||
if not path:
|
||||
parser.error('no path given')
|
||||
else:
|
||||
path = path[0]
|
||||
|
||||
print csscombine(path, options.sourceencoding, options.targetencoding,
|
||||
options.minify)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""utility script to parse given filenames or string
|
||||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssparse.py 1327 2008-07-08 21:17:12Z cthedot $'
|
||||
|
||||
import cssutils
|
||||
import logging
|
||||
import optparse
|
||||
import sys
|
||||
|
||||
def main(args=None):
|
||||
"""
|
||||
Parses given filename(s) or string (using optional encoding) and prints
|
||||
the parsed style sheet to stdout.
|
||||
|
||||
Redirect stdout to save CSS. Redirect stderr to save parser log infos.
|
||||
"""
|
||||
usage = """usage: %prog [options] filename1.css [filename2.css ...]
|
||||
[>filename_combined.css] [2>parserinfo.log] """
|
||||
p = optparse.OptionParser(usage=usage)
|
||||
p.add_option('-e', '--encoding', action='store', dest='encoding',
|
||||
help='encoding of the file')
|
||||
p.add_option('-d', '--debug', action='store_true', dest='debug',
|
||||
help='activate debugging output')
|
||||
p.add_option('-m', '--minify', action='store_true', dest='minify',
|
||||
help='minify parsed CSS', default=False)
|
||||
p.add_option('-s', '--string', action='store_true', dest='string',
|
||||
help='parse given string')
|
||||
|
||||
(options, params) = p.parse_args(args)
|
||||
|
||||
if not params:
|
||||
p.error("no filename given")
|
||||
|
||||
if options.debug:
|
||||
p = cssutils.CSSParser(loglevel=logging.DEBUG)
|
||||
else:
|
||||
p = cssutils.CSSParser()
|
||||
|
||||
if options.minify:
|
||||
cssutils.ser.prefs.useMinified()
|
||||
|
||||
if options.string:
|
||||
sheet = p.parseString(u''.join(params), encoding=options.encoding)
|
||||
print sheet.cssText
|
||||
print
|
||||
sys.stderr.write('\n')
|
||||
else:
|
||||
for filename in params:
|
||||
sys.stderr.write('=== CSS FILE: "%s" ===\n' % filename)
|
||||
sheet = p.parseFile(filename, encoding=options.encoding)
|
||||
print sheet.cssText
|
||||
print
|
||||
sys.stderr.write('\n')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,254 +1,254 @@
|
||||
#!/usr/bin/env python
|
||||
"""cssutils - CSS Cascading Style Sheets library for Python
|
||||
|
||||
Copyright (C) 2004-2008 Christof Hoeke
|
||||
|
||||
cssutils is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
A Python package to parse and build CSS Cascading Style Sheets. DOM only, not any rendering facilities!
|
||||
|
||||
Based upon and partly implementing the following specifications :
|
||||
|
||||
`CSS 2.1 <http://www.w3.org/TR/CSS21/>`__
|
||||
General CSS rules and properties are defined here
|
||||
`CSS 2.1 Errata <http://www.w3.org/Style/css2-updates/CR-CSS21-20070719-errata.html>`__
|
||||
A few errata, mainly the definition of CHARSET_SYM tokens
|
||||
`CSS3 Module: Syntax <http://www.w3.org/TR/css3-syntax/>`__
|
||||
Used in parts since cssutils 0.9.4. cssutils tries to use the features from CSS 2.1 and CSS 3 with preference to CSS3 but as this is not final yet some parts are from CSS 2.1
|
||||
`MediaQueries <http://www.w3.org/TR/css3-mediaqueries/>`__
|
||||
MediaQueries are part of ``stylesheets.MediaList`` since v0.9.4, used in @import and @media rules.
|
||||
`Namespaces <http://dev.w3.org/csswg/css3-namespace/>`__
|
||||
Added in v0.9.1, updated to definition in CSSOM in v0.9.4, updated in 0.9.5 for dev version
|
||||
`Selectors <http://www.w3.org/TR/css3-selectors/>`__
|
||||
The selector syntax defined here (and not in CSS 2.1) should be parsable with cssutils (*should* mind though ;) )
|
||||
|
||||
`DOM Level 2 Style CSS <http://www.w3.org/TR/DOM-Level-2-Style/css.html>`__
|
||||
DOM for package css
|
||||
`DOM Level 2 Style Stylesheets <http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html>`__
|
||||
DOM for package stylesheets
|
||||
`CSSOM <http://dev.w3.org/csswg/cssom/>`__
|
||||
A few details (mainly the NamespaceRule DOM) is taken from here. Plan is to move implementation to the stuff defined here which is newer but still no REC so might change anytime...
|
||||
|
||||
|
||||
The cssutils tokenizer is a customized implementation of `CSS3 Module: Syntax (W3C Working Draft 13 August 2003) <http://www.w3.org/TR/css3-syntax/>`__ which itself is based on the CSS 2.1 tokenizer. It tries to be as compliant as possible but uses some (helpful) parts of the CSS 2.1 tokenizer.
|
||||
|
||||
I guess cssutils is neither CSS 2.1 nor CSS 3 compliant but tries to at least be able to parse both grammars including some more real world cases (some CSS hacks are actually parsed and serialized). Both official grammars are not final nor bugfree but still feasible. cssutils aim is not to be fully compliant to any CSS specification (the specifications seem to be in a constant flow anyway) but cssutils *should* be able to read and write as many as possible CSS stylesheets "in the wild" while at the same time implement the official APIs which are well documented. Some minor extensions are provided as well.
|
||||
|
||||
Please visit http://cthedot.de/cssutils/ for more details.
|
||||
|
||||
|
||||
Tested with Python 2.5 on Windows Vista mainly.
|
||||
|
||||
|
||||
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:: 2008-08-11 20:11:23 +0200 #$:'
|
||||
|
||||
VERSION = '0.9.5.1'
|
||||
|
||||
__version__ = '%s $Id: __init__.py 1426 2008-08-11 18:11:23Z cthedot $' % VERSION
|
||||
|
||||
import codec
|
||||
import xml.dom
|
||||
|
||||
# order of imports is important (partly circular)
|
||||
from helper import Deprecated
|
||||
import errorhandler
|
||||
log = errorhandler.ErrorHandler()
|
||||
|
||||
import util
|
||||
import css
|
||||
import stylesheets
|
||||
from parse import CSSParser
|
||||
|
||||
from serialize import CSSSerializer
|
||||
ser = CSSSerializer()
|
||||
|
||||
# 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__
|
||||
|
||||
@Deprecated('Use cssutils.parseFile() instead.')
|
||||
def parse(*a, **k):
|
||||
return parseFile(*a, **k)
|
||||
parse.__doc__ = CSSParser.parse.__doc__
|
||||
|
||||
|
||||
# set "ser", default serializer
|
||||
def setSerializer(serializer):
|
||||
"""
|
||||
sets the global serializer used by all class in cssutils
|
||||
"""
|
||||
global ser
|
||||
ser = serializer
|
||||
|
||||
|
||||
def getUrls(sheet):
|
||||
"""
|
||||
Utility function to get all ``url(urlstring)`` values in
|
||||
``CSSImportRules`` and ``CSSStyleDeclaration`` objects (properties)
|
||||
of given CSSStyleSheet ``sheet``.
|
||||
|
||||
This function is a generator. The 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 getUrl(v):
|
||||
if v.CSS_PRIMITIVE_VALUE == v.cssValueType and\
|
||||
v.CSS_URI == v.primitiveType:
|
||||
return v.getStringValue()
|
||||
|
||||
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):
|
||||
v = p.cssValue
|
||||
if v.CSS_VALUE_LIST == v.cssValueType:
|
||||
for item in v:
|
||||
u = getUrl(item)
|
||||
if u is not None:
|
||||
yield u
|
||||
elif v.CSS_PRIMITIVE_VALUE == v.cssValueType:
|
||||
u = getUrl(v)
|
||||
if u is not None:
|
||||
yield u
|
||||
|
||||
def replaceUrls(sheet, replacer):
|
||||
"""
|
||||
Utility function to replace all ``url(urlstring)`` values in
|
||||
``CSSImportRules`` and ``CSSStyleDeclaration`` objects (properties)
|
||||
of given CSSStyleSheet ``sheet``.
|
||||
|
||||
``replacer`` must be a function which is called with a single
|
||||
argument ``urlstring`` which is the current value of url()
|
||||
excluding ``url(`` and ``)`` and surrounding single or double quotes.
|
||||
"""
|
||||
for importrule in (r for r in sheet if r.type == r.IMPORT_RULE):
|
||||
importrule.href = replacer(importrule.href)
|
||||
|
||||
def setProperty(v):
|
||||
if v.CSS_PRIMITIVE_VALUE == v.cssValueType and\
|
||||
v.CSS_URI == v.primitiveType:
|
||||
v.setStringValue(v.CSS_URI,
|
||||
replacer(v.getStringValue()))
|
||||
|
||||
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):
|
||||
v = p.cssValue
|
||||
if v.CSS_VALUE_LIST == v.cssValueType:
|
||||
for item in v:
|
||||
setProperty(item)
|
||||
elif v.CSS_PRIMITIVE_VALUE == v.cssValueType:
|
||||
setProperty(v)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print __doc__
|
||||
#!/usr/bin/env python
|
||||
"""cssutils - CSS Cascading Style Sheets library for Python
|
||||
|
||||
Copyright (C) 2004-2008 Christof Hoeke
|
||||
|
||||
cssutils is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
A Python package to parse and build CSS Cascading Style Sheets. DOM only, not any rendering facilities!
|
||||
|
||||
Based upon and partly implementing the following specifications :
|
||||
|
||||
`CSS 2.1 <http://www.w3.org/TR/CSS21/>`__
|
||||
General CSS rules and properties are defined here
|
||||
`CSS 2.1 Errata <http://www.w3.org/Style/css2-updates/CR-CSS21-20070719-errata.html>`__
|
||||
A few errata, mainly the definition of CHARSET_SYM tokens
|
||||
`CSS3 Module: Syntax <http://www.w3.org/TR/css3-syntax/>`__
|
||||
Used in parts since cssutils 0.9.4. cssutils tries to use the features from CSS 2.1 and CSS 3 with preference to CSS3 but as this is not final yet some parts are from CSS 2.1
|
||||
`MediaQueries <http://www.w3.org/TR/css3-mediaqueries/>`__
|
||||
MediaQueries are part of ``stylesheets.MediaList`` since v0.9.4, used in @import and @media rules.
|
||||
`Namespaces <http://dev.w3.org/csswg/css3-namespace/>`__
|
||||
Added in v0.9.1, updated to definition in CSSOM in v0.9.4, updated in 0.9.5 for dev version
|
||||
`Selectors <http://www.w3.org/TR/css3-selectors/>`__
|
||||
The selector syntax defined here (and not in CSS 2.1) should be parsable with cssutils (*should* mind though ;) )
|
||||
|
||||
`DOM Level 2 Style CSS <http://www.w3.org/TR/DOM-Level-2-Style/css.html>`__
|
||||
DOM for package css
|
||||
`DOM Level 2 Style Stylesheets <http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html>`__
|
||||
DOM for package stylesheets
|
||||
`CSSOM <http://dev.w3.org/csswg/cssom/>`__
|
||||
A few details (mainly the NamespaceRule DOM) is taken from here. Plan is to move implementation to the stuff defined here which is newer but still no REC so might change anytime...
|
||||
|
||||
|
||||
The cssutils tokenizer is a customized implementation of `CSS3 Module: Syntax (W3C Working Draft 13 August 2003) <http://www.w3.org/TR/css3-syntax/>`__ which itself is based on the CSS 2.1 tokenizer. It tries to be as compliant as possible but uses some (helpful) parts of the CSS 2.1 tokenizer.
|
||||
|
||||
I guess cssutils is neither CSS 2.1 nor CSS 3 compliant but tries to at least be able to parse both grammars including some more real world cases (some CSS hacks are actually parsed and serialized). Both official grammars are not final nor bugfree but still feasible. cssutils aim is not to be fully compliant to any CSS specification (the specifications seem to be in a constant flow anyway) but cssutils *should* be able to read and write as many as possible CSS stylesheets "in the wild" while at the same time implement the official APIs which are well documented. Some minor extensions are provided as well.
|
||||
|
||||
Please visit http://cthedot.de/cssutils/ for more details.
|
||||
|
||||
|
||||
Tested with Python 2.5 on Windows Vista mainly.
|
||||
|
||||
|
||||
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:: 2008-08-11 11:11:23 -0700 #$:'
|
||||
|
||||
VERSION = '0.9.5.1'
|
||||
|
||||
__version__ = '%s $Id: __init__.py 1426 2008-08-11 18:11:23Z cthedot $' % VERSION
|
||||
|
||||
import codec
|
||||
import xml.dom
|
||||
|
||||
# order of imports is important (partly circular)
|
||||
from helper import Deprecated
|
||||
import errorhandler
|
||||
log = errorhandler.ErrorHandler()
|
||||
|
||||
import util
|
||||
import css
|
||||
import stylesheets
|
||||
from parse import CSSParser
|
||||
|
||||
from serialize import CSSSerializer
|
||||
ser = CSSSerializer()
|
||||
|
||||
# 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__
|
||||
|
||||
@Deprecated('Use cssutils.parseFile() instead.')
|
||||
def parse(*a, **k):
|
||||
return parseFile(*a, **k)
|
||||
parse.__doc__ = CSSParser.parse.__doc__
|
||||
|
||||
|
||||
# set "ser", default serializer
|
||||
def setSerializer(serializer):
|
||||
"""
|
||||
sets the global serializer used by all class in cssutils
|
||||
"""
|
||||
global ser
|
||||
ser = serializer
|
||||
|
||||
|
||||
def getUrls(sheet):
|
||||
"""
|
||||
Utility function to get all ``url(urlstring)`` values in
|
||||
``CSSImportRules`` and ``CSSStyleDeclaration`` objects (properties)
|
||||
of given CSSStyleSheet ``sheet``.
|
||||
|
||||
This function is a generator. The 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 getUrl(v):
|
||||
if v.CSS_PRIMITIVE_VALUE == v.cssValueType and\
|
||||
v.CSS_URI == v.primitiveType:
|
||||
return v.getStringValue()
|
||||
|
||||
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):
|
||||
v = p.cssValue
|
||||
if v.CSS_VALUE_LIST == v.cssValueType:
|
||||
for item in v:
|
||||
u = getUrl(item)
|
||||
if u is not None:
|
||||
yield u
|
||||
elif v.CSS_PRIMITIVE_VALUE == v.cssValueType:
|
||||
u = getUrl(v)
|
||||
if u is not None:
|
||||
yield u
|
||||
|
||||
def replaceUrls(sheet, replacer):
|
||||
"""
|
||||
Utility function to replace all ``url(urlstring)`` values in
|
||||
``CSSImportRules`` and ``CSSStyleDeclaration`` objects (properties)
|
||||
of given CSSStyleSheet ``sheet``.
|
||||
|
||||
``replacer`` must be a function which is called with a single
|
||||
argument ``urlstring`` which is the current value of url()
|
||||
excluding ``url(`` and ``)`` and surrounding single or double quotes.
|
||||
"""
|
||||
for importrule in (r for r in sheet if r.type == r.IMPORT_RULE):
|
||||
importrule.href = replacer(importrule.href)
|
||||
|
||||
def setProperty(v):
|
||||
if v.CSS_PRIMITIVE_VALUE == v.cssValueType and\
|
||||
v.CSS_URI == v.primitiveType:
|
||||
v.setStringValue(v.CSS_URI,
|
||||
replacer(v.getStringValue()))
|
||||
|
||||
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):
|
||||
v = p.cssValue
|
||||
if v.CSS_VALUE_LIST == v.cssValueType:
|
||||
for item in v:
|
||||
setProperty(item)
|
||||
elif v.CSS_PRIMITIVE_VALUE == v.cssValueType:
|
||||
setProperty(v)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print __doc__
|
File diff suppressed because it is too large
Load Diff
@ -1,63 +1,63 @@
|
||||
"""
|
||||
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
|
||||
|
||||
todo
|
||||
- RGBColor, Rect, Counter
|
||||
"""
|
||||
__all__ = [
|
||||
'CSSStyleSheet',
|
||||
'CSSRuleList',
|
||||
'CSSRule',
|
||||
'CSSComment',
|
||||
'CSSCharsetRule',
|
||||
'CSSFontFaceRule'
|
||||
'CSSImportRule',
|
||||
'CSSMediaRule',
|
||||
'CSSNamespaceRule',
|
||||
'CSSPageRule',
|
||||
'CSSStyleRule',
|
||||
'CSSUnknownRule',
|
||||
'Selector', 'SelectorList',
|
||||
'CSSStyleDeclaration', 'Property',
|
||||
'CSSValue', 'CSSPrimitiveValue', 'CSSValueList'
|
||||
]
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: __init__.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
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 cssstylerule import *
|
||||
from cssunknownrule import *
|
||||
from selector import *
|
||||
from selectorlist import *
|
||||
from cssstyledeclaration import *
|
||||
from cssvalue import *
|
||||
"""
|
||||
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
|
||||
|
||||
todo
|
||||
- RGBColor, Rect, Counter
|
||||
"""
|
||||
__all__ = [
|
||||
'CSSStyleSheet',
|
||||
'CSSRuleList',
|
||||
'CSSRule',
|
||||
'CSSComment',
|
||||
'CSSCharsetRule',
|
||||
'CSSFontFaceRule'
|
||||
'CSSImportRule',
|
||||
'CSSMediaRule',
|
||||
'CSSNamespaceRule',
|
||||
'CSSPageRule',
|
||||
'CSSStyleRule',
|
||||
'CSSUnknownRule',
|
||||
'Selector', 'SelectorList',
|
||||
'CSSStyleDeclaration', 'Property',
|
||||
'CSSValue', 'CSSPrimitiveValue', 'CSSValueList'
|
||||
]
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: __init__.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
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 cssstylerule import *
|
||||
from cssunknownrule import *
|
||||
from selector import *
|
||||
from selectorlist import *
|
||||
from cssstyledeclaration import *
|
||||
from cssvalue import *
|
@ -1,165 +1,165 @@
|
||||
"""CSSCharsetRule implements DOM Level 2 CSS CSSCharsetRule.
|
||||
|
||||
TODO:
|
||||
- check encoding syntax and not codecs.lookup?
|
||||
"""
|
||||
__all__ = ['CSSCharsetRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: csscharsetrule.py 1170 2008-03-20 17:42:07Z cthedot $'
|
||||
|
||||
import codecs
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
|
||||
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.
|
||||
|
||||
Properties
|
||||
==========
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of this rule
|
||||
encoding: of type DOMString
|
||||
The encoding information used in this @charset rule.
|
||||
|
||||
Inherits properties from CSSRule
|
||||
|
||||
Format
|
||||
======
|
||||
charsetrule:
|
||||
CHARSET_SYM S* STRING S* ';'
|
||||
|
||||
BUT: Only valid format is:
|
||||
@charset "ENCODING";
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.CHARSET_RULE)
|
||||
|
||||
def __init__(self, encoding=None, parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
encoding:
|
||||
a valid character encoding
|
||||
readonly:
|
||||
defaults to False, not used yet
|
||||
|
||||
if readonly allows setting of properties in constructor only
|
||||
"""
|
||||
super(CSSCharsetRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = '@charset'
|
||||
self._encoding = None
|
||||
if encoding:
|
||||
self.encoding = encoding
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def _getCssText(self):
|
||||
"""returns serialized property cssText"""
|
||||
return cssutils.ser.do_CSSCharsetRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR: (self)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- INVALID_MODIFICATION_ERR: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- HIERARCHY_REQUEST_ERR: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
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="(DOM) The parsable textual representation.")
|
||||
|
||||
def _setEncoding(self, encoding):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
Raised if this encoding rule is readonly.
|
||||
- SYNTAX_ERR: (self)
|
||||
Raised if the specified encoding value has a syntax error and
|
||||
is unparsable.
|
||||
Currently only valid Python encodings are allowed.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
tokenizer = self._tokenize2(encoding)
|
||||
encodingtoken = self._nexttoken(tokenizer)
|
||||
unexpected = self._nexttoken(tokenizer)
|
||||
|
||||
valid = True
|
||||
if not encodingtoken or unexpected or\
|
||||
self._prods.IDENT != self._type(encodingtoken):
|
||||
valid = False
|
||||
self._log.error(
|
||||
'CSSCharsetRule: Syntax Error in encoding value %r.' %
|
||||
encoding)
|
||||
else:
|
||||
try:
|
||||
codecs.lookup(encoding)
|
||||
except LookupError:
|
||||
valid = False
|
||||
self._log.error('CSSCharsetRule: Unknown (Python) encoding %r.' %
|
||||
encoding)
|
||||
else:
|
||||
self._encoding = encoding.lower()
|
||||
|
||||
encoding = property(lambda self: self._encoding, _setEncoding,
|
||||
doc="(DOM)The encoding information used in this @charset rule.")
|
||||
|
||||
wellformed = property(lambda self: bool(self.encoding))
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(encoding=%r)" % (
|
||||
self.__class__.__name__, self.encoding)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object encoding=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.encoding, id(self))
|
||||
"""CSSCharsetRule implements DOM Level 2 CSS CSSCharsetRule.
|
||||
|
||||
TODO:
|
||||
- check encoding syntax and not codecs.lookup?
|
||||
"""
|
||||
__all__ = ['CSSCharsetRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: csscharsetrule.py 1170 2008-03-20 17:42:07Z cthedot $'
|
||||
|
||||
import codecs
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
|
||||
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.
|
||||
|
||||
Properties
|
||||
==========
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of this rule
|
||||
encoding: of type DOMString
|
||||
The encoding information used in this @charset rule.
|
||||
|
||||
Inherits properties from CSSRule
|
||||
|
||||
Format
|
||||
======
|
||||
charsetrule:
|
||||
CHARSET_SYM S* STRING S* ';'
|
||||
|
||||
BUT: Only valid format is:
|
||||
@charset "ENCODING";
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.CHARSET_RULE)
|
||||
|
||||
def __init__(self, encoding=None, parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
encoding:
|
||||
a valid character encoding
|
||||
readonly:
|
||||
defaults to False, not used yet
|
||||
|
||||
if readonly allows setting of properties in constructor only
|
||||
"""
|
||||
super(CSSCharsetRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = '@charset'
|
||||
self._encoding = None
|
||||
if encoding:
|
||||
self.encoding = encoding
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def _getCssText(self):
|
||||
"""returns serialized property cssText"""
|
||||
return cssutils.ser.do_CSSCharsetRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR: (self)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- INVALID_MODIFICATION_ERR: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- HIERARCHY_REQUEST_ERR: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
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="(DOM) The parsable textual representation.")
|
||||
|
||||
def _setEncoding(self, encoding):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
Raised if this encoding rule is readonly.
|
||||
- SYNTAX_ERR: (self)
|
||||
Raised if the specified encoding value has a syntax error and
|
||||
is unparsable.
|
||||
Currently only valid Python encodings are allowed.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
tokenizer = self._tokenize2(encoding)
|
||||
encodingtoken = self._nexttoken(tokenizer)
|
||||
unexpected = self._nexttoken(tokenizer)
|
||||
|
||||
valid = True
|
||||
if not encodingtoken or unexpected or\
|
||||
self._prods.IDENT != self._type(encodingtoken):
|
||||
valid = False
|
||||
self._log.error(
|
||||
'CSSCharsetRule: Syntax Error in encoding value %r.' %
|
||||
encoding)
|
||||
else:
|
||||
try:
|
||||
codecs.lookup(encoding)
|
||||
except LookupError:
|
||||
valid = False
|
||||
self._log.error('CSSCharsetRule: Unknown (Python) encoding %r.' %
|
||||
encoding)
|
||||
else:
|
||||
self._encoding = encoding.lower()
|
||||
|
||||
encoding = property(lambda self: self._encoding, _setEncoding,
|
||||
doc="(DOM)The encoding information used in this @charset rule.")
|
||||
|
||||
wellformed = property(lambda self: bool(self.encoding))
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(encoding=%r)" % (
|
||||
self.__class__.__name__, self.encoding)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object encoding=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.encoding, id(self))
|
@ -1,92 +1,92 @@
|
||||
"""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: csscomment.py 1170 2008-03-20 17:42:07Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
|
||||
class CSSComment(cssrule.CSSRule):
|
||||
"""
|
||||
(cssutils) a CSS comment
|
||||
|
||||
Properties
|
||||
==========
|
||||
cssText: of type DOMString
|
||||
The comment text including comment delimiters
|
||||
|
||||
Inherits properties from CSSRule
|
||||
|
||||
Format
|
||||
======
|
||||
::
|
||||
|
||||
/*...*/
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.COMMENT) # value = -1
|
||||
# constant but needed:
|
||||
wellformed = True
|
||||
|
||||
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 _getCssText(self):
|
||||
"""returns serialized property cssText"""
|
||||
return cssutils.ser.do_CSSComment(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
cssText
|
||||
textual text to set or tokenlist which is not tokenized
|
||||
anymore. May also be a single token for this rule
|
||||
parser
|
||||
if called from cssparser directly this is Parser instance
|
||||
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR: (self)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- INVALID_MODIFICATION_ERR: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
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"(cssutils) Textual representation of this comment")
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(cssText=%r)" % (
|
||||
self.__class__.__name__, self.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object cssText=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.cssText, id(self))
|
||||
"""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: csscomment.py 1170 2008-03-20 17:42:07Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
|
||||
class CSSComment(cssrule.CSSRule):
|
||||
"""
|
||||
(cssutils) a CSS comment
|
||||
|
||||
Properties
|
||||
==========
|
||||
cssText: of type DOMString
|
||||
The comment text including comment delimiters
|
||||
|
||||
Inherits properties from CSSRule
|
||||
|
||||
Format
|
||||
======
|
||||
::
|
||||
|
||||
/*...*/
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.COMMENT) # value = -1
|
||||
# constant but needed:
|
||||
wellformed = True
|
||||
|
||||
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 _getCssText(self):
|
||||
"""returns serialized property cssText"""
|
||||
return cssutils.ser.do_CSSComment(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
cssText
|
||||
textual text to set or tokenlist which is not tokenized
|
||||
anymore. May also be a single token for this rule
|
||||
parser
|
||||
if called from cssparser directly this is Parser instance
|
||||
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR: (self)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- INVALID_MODIFICATION_ERR: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
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"(cssutils) Textual representation of this comment")
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(cssText=%r)" % (
|
||||
self.__class__.__name__, self.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object cssText=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.cssText, id(self))
|
@ -1,399 +1,399 @@
|
||||
"""CSSImportRule implements DOM Level 2 CSS CSSImportRule.
|
||||
|
||||
plus:
|
||||
|
||||
``name`` property
|
||||
http://www.w3.org/TR/css3-cascade/#cascading
|
||||
|
||||
"""
|
||||
__all__ = ['CSSImportRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssimportrule.py 1401 2008-07-29 21:07:54Z cthedot $'
|
||||
|
||||
import os
|
||||
import urllib
|
||||
import urlparse
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
|
||||
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.
|
||||
|
||||
Properties
|
||||
==========
|
||||
atkeyword: (cssutils only)
|
||||
the literal keyword used
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of this rule
|
||||
href: of type DOMString, (DOM readonly, cssutils also writable)
|
||||
The location of the style sheet to be imported. The attribute will
|
||||
not contain the url(...) specifier around the URI.
|
||||
hreftype: 'uri' (serializer default) or 'string' (cssutils only)
|
||||
The original type of href, not really relevant as it may be
|
||||
reconfigured in the serializer but it is kept anyway
|
||||
media: of type stylesheets::MediaList (DOM readonly)
|
||||
A list of media types for this rule of type MediaList.
|
||||
name:
|
||||
An optional name used for cascading
|
||||
styleSheet: of type CSSStyleSheet (DOM readonly)
|
||||
The style sheet referred to by this rule. The value of this
|
||||
attribute is None if the style sheet has not yet been loaded or if
|
||||
it will not be loaded (e.g. if the stylesheet is for a media type
|
||||
not supported by the user agent).
|
||||
|
||||
Inherits properties from CSSRule
|
||||
|
||||
Format
|
||||
======
|
||||
import
|
||||
: IMPORT_SYM S*
|
||||
[STRING|URI] S* [ medium [ COMMA S* medium]* ]? S* STRING? S* ';' S*
|
||||
;
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.IMPORT_RULE)
|
||||
|
||||
def __init__(self, href=None, mediaText=u'all', name=None,
|
||||
parentRule=None, parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
if readonly allows setting of properties in constructor only
|
||||
|
||||
Do not use as positional but as keyword attributes only!
|
||||
|
||||
href
|
||||
location of the style sheet to be imported.
|
||||
mediaText
|
||||
A list of media types for which this style sheet may be used
|
||||
as a string
|
||||
"""
|
||||
super(CSSImportRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = u'@import'
|
||||
self.hreftype = None
|
||||
self._styleSheet = None
|
||||
|
||||
self._href = None
|
||||
self.href = href
|
||||
|
||||
self._media = cssutils.stylesheets.MediaList()
|
||||
if mediaText:
|
||||
self._media.mediaText = mediaText
|
||||
|
||||
self._name = name
|
||||
|
||||
seq = self._tempSeq()
|
||||
seq.append(self.href, 'href')
|
||||
seq.append(self.media, 'media')
|
||||
seq.append(self.name, 'name')
|
||||
self._setSeq(seq)
|
||||
self._readonly = readonly
|
||||
|
||||
_usemedia = property(lambda self: self.media.mediaText not in (u'', u'all'),
|
||||
doc="if self._media is used (or simply empty)")
|
||||
|
||||
def _getCssText(self):
|
||||
"""
|
||||
returns serialized property cssText
|
||||
"""
|
||||
return cssutils.ser.do_CSSImportRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- HIERARCHY_REQUEST_ERR: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- INVALID_MODIFICATION_ERR: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
Raised if the rule is readonly.
|
||||
- SYNTAX_ERR: (self)
|
||||
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)
|
||||
|
||||
media = cssutils.stylesheets.MediaList()
|
||||
media.mediaText = mediatokens
|
||||
if media.wellformed:
|
||||
new['media'] = media
|
||||
seq.append(media, '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
|
||||
wellformed = wellformed and new['wellformed']
|
||||
|
||||
# post conditions
|
||||
if not new['href']:
|
||||
wellformed = False
|
||||
self._log.error(u'CSSImportRule: No href found: %s' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
if expected != 'EOF':
|
||||
wellformed = False
|
||||
self._log.error(u'CSSImportRule: No ";" found: %s' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
# set all
|
||||
if wellformed:
|
||||
self.atkeyword = new['keyword']
|
||||
self.hreftype = new['hreftype']
|
||||
if new['media']:
|
||||
# use same object
|
||||
self.media.mediaText = new['media'].mediaText
|
||||
# put it in newseq too
|
||||
for index, x in enumerate(newseq):
|
||||
if x.type == 'media':
|
||||
newseq.replace(index, self.media,
|
||||
x.type, x.line, x.col)
|
||||
break
|
||||
else:
|
||||
# reset media
|
||||
self.media.mediaText = u'all'
|
||||
newseq.append(self.media, 'media')
|
||||
self.name = new['name']
|
||||
self._setSeq(newseq)
|
||||
self.href = new['href']
|
||||
|
||||
if self.styleSheet:
|
||||
# title is set by href
|
||||
#self.styleSheet._href = self.href
|
||||
self.styleSheet._parentStyleSheet = self.parentStyleSheet
|
||||
|
||||
cssText = property(fget=_getCssText, fset=_setCssText,
|
||||
doc="(DOM attribute) The parsable textual representation.")
|
||||
|
||||
def _setHref(self, href):
|
||||
# update seq
|
||||
for i, item in enumerate(self.seq):
|
||||
val, typ = item.value, item.type
|
||||
if 'href' == typ:
|
||||
self._seq[i] = (href, typ, item.line, item.col)
|
||||
break
|
||||
else:
|
||||
seq = self._tempSeq()
|
||||
seq.append(self.href, 'href')
|
||||
self._setSeq(seq)
|
||||
# set new href
|
||||
self._href = href
|
||||
if not self.styleSheet:
|
||||
# set only if not set before
|
||||
self.__setStyleSheet()
|
||||
|
||||
href = property(lambda self: self._href, _setHref,
|
||||
doc="Location of the style sheet to be imported.")
|
||||
|
||||
media = property(lambda self: self._media,
|
||||
doc=u"(DOM readonly) A list of media types for this rule"
|
||||
" of type MediaList")
|
||||
|
||||
def _setName(self, name):
|
||||
"""raises xml.dom.SyntaxErr if name is not a string"""
|
||||
if isinstance(name, basestring) or name is None:
|
||||
# "" or ''
|
||||
if not name:
|
||||
name = None
|
||||
# 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
|
||||
else:
|
||||
# append
|
||||
seq = self._tempSeq()
|
||||
for item in self.seq:
|
||||
# copy current seq
|
||||
seq.append(item.value, item.type, item.line, item.col)
|
||||
seq.append(name, 'name')
|
||||
self._setSeq(seq)
|
||||
self._name = name
|
||||
# set title of referred 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")
|
||||
|
||||
def __setStyleSheet(self):
|
||||
"""Read new CSSStyleSheet cssText from href using parentStyleSheet.href
|
||||
|
||||
Indirectly called if setting ``href``. In case of any error styleSheet
|
||||
is set to ``None``.
|
||||
"""
|
||||
# should simply fail so all errors are catched!
|
||||
if self.parentStyleSheet and self.href:
|
||||
# relative href
|
||||
parentHref = self.parentStyleSheet.href
|
||||
if parentHref is None:
|
||||
# use cwd instead
|
||||
parentHref = u'file:' + urllib.pathname2url(os.getcwd()) + '/'
|
||||
href = urlparse.urljoin(parentHref, self.href)
|
||||
|
||||
# all possible exceptions are ignored (styleSheet is None then)
|
||||
try:
|
||||
usedEncoding, enctype, cssText = self.parentStyleSheet._resolveImport(href)
|
||||
if cssText is None:
|
||||
# catched in next except below!
|
||||
raise IOError('Cannot read Stylesheet.')
|
||||
styleSheet = cssutils.css.CSSStyleSheet(href=href,
|
||||
media=self.media,
|
||||
ownerRule=self,
|
||||
title=self.name)
|
||||
# inherit fetcher for @imports in styleSheet
|
||||
styleSheet._setFetcher(self.parentStyleSheet._fetcher)
|
||||
# contentEncoding with parentStyleSheet.overrideEncoding,
|
||||
# HTTP or parent
|
||||
encodingOverride, encoding = None, None
|
||||
if enctype == 0:
|
||||
encodingOverride = usedEncoding
|
||||
elif 5 > enctype > 0:
|
||||
encoding = usedEncoding
|
||||
|
||||
styleSheet._setCssTextWithEncodingOverride(cssText,
|
||||
encodingOverride=encodingOverride,
|
||||
encoding=encoding)
|
||||
|
||||
except (OSError, IOError, ValueError), e:
|
||||
self._log.warn(u'CSSImportRule: While processing imported style sheet href=%r: %r'
|
||||
% (self.href, e), neverraise=True)
|
||||
else:
|
||||
self._styleSheet = styleSheet
|
||||
|
||||
styleSheet = property(lambda self: self._styleSheet,
|
||||
doc="(readonly) The style sheet referred to by this rule.")
|
||||
|
||||
def _getWellformed(self):
|
||||
"depending 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)
|
||||
|
||||
def __repr__(self):
|
||||
if self._usemedia:
|
||||
mediaText = self.media.mediaText
|
||||
else:
|
||||
mediaText = None
|
||||
return "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 "<cssutils.css.%s object href=%r mediaText=%r name=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.href, mediaText, self.name, id(self))
|
||||
"""CSSImportRule implements DOM Level 2 CSS CSSImportRule.
|
||||
|
||||
plus:
|
||||
|
||||
``name`` property
|
||||
http://www.w3.org/TR/css3-cascade/#cascading
|
||||
|
||||
"""
|
||||
__all__ = ['CSSImportRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssimportrule.py 1401 2008-07-29 21:07:54Z cthedot $'
|
||||
|
||||
import os
|
||||
import urllib
|
||||
import urlparse
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
|
||||
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.
|
||||
|
||||
Properties
|
||||
==========
|
||||
atkeyword: (cssutils only)
|
||||
the literal keyword used
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of this rule
|
||||
href: of type DOMString, (DOM readonly, cssutils also writable)
|
||||
The location of the style sheet to be imported. The attribute will
|
||||
not contain the url(...) specifier around the URI.
|
||||
hreftype: 'uri' (serializer default) or 'string' (cssutils only)
|
||||
The original type of href, not really relevant as it may be
|
||||
reconfigured in the serializer but it is kept anyway
|
||||
media: of type stylesheets::MediaList (DOM readonly)
|
||||
A list of media types for this rule of type MediaList.
|
||||
name:
|
||||
An optional name used for cascading
|
||||
styleSheet: of type CSSStyleSheet (DOM readonly)
|
||||
The style sheet referred to by this rule. The value of this
|
||||
attribute is None if the style sheet has not yet been loaded or if
|
||||
it will not be loaded (e.g. if the stylesheet is for a media type
|
||||
not supported by the user agent).
|
||||
|
||||
Inherits properties from CSSRule
|
||||
|
||||
Format
|
||||
======
|
||||
import
|
||||
: IMPORT_SYM S*
|
||||
[STRING|URI] S* [ medium [ COMMA S* medium]* ]? S* STRING? S* ';' S*
|
||||
;
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.IMPORT_RULE)
|
||||
|
||||
def __init__(self, href=None, mediaText=u'all', name=None,
|
||||
parentRule=None, parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
if readonly allows setting of properties in constructor only
|
||||
|
||||
Do not use as positional but as keyword attributes only!
|
||||
|
||||
href
|
||||
location of the style sheet to be imported.
|
||||
mediaText
|
||||
A list of media types for which this style sheet may be used
|
||||
as a string
|
||||
"""
|
||||
super(CSSImportRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = u'@import'
|
||||
self.hreftype = None
|
||||
self._styleSheet = None
|
||||
|
||||
self._href = None
|
||||
self.href = href
|
||||
|
||||
self._media = cssutils.stylesheets.MediaList()
|
||||
if mediaText:
|
||||
self._media.mediaText = mediaText
|
||||
|
||||
self._name = name
|
||||
|
||||
seq = self._tempSeq()
|
||||
seq.append(self.href, 'href')
|
||||
seq.append(self.media, 'media')
|
||||
seq.append(self.name, 'name')
|
||||
self._setSeq(seq)
|
||||
self._readonly = readonly
|
||||
|
||||
_usemedia = property(lambda self: self.media.mediaText not in (u'', u'all'),
|
||||
doc="if self._media is used (or simply empty)")
|
||||
|
||||
def _getCssText(self):
|
||||
"""
|
||||
returns serialized property cssText
|
||||
"""
|
||||
return cssutils.ser.do_CSSImportRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- HIERARCHY_REQUEST_ERR: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- INVALID_MODIFICATION_ERR: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
Raised if the rule is readonly.
|
||||
- SYNTAX_ERR: (self)
|
||||
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)
|
||||
|
||||
media = cssutils.stylesheets.MediaList()
|
||||
media.mediaText = mediatokens
|
||||
if media.wellformed:
|
||||
new['media'] = media
|
||||
seq.append(media, '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
|
||||
wellformed = wellformed and new['wellformed']
|
||||
|
||||
# post conditions
|
||||
if not new['href']:
|
||||
wellformed = False
|
||||
self._log.error(u'CSSImportRule: No href found: %s' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
if expected != 'EOF':
|
||||
wellformed = False
|
||||
self._log.error(u'CSSImportRule: No ";" found: %s' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
# set all
|
||||
if wellformed:
|
||||
self.atkeyword = new['keyword']
|
||||
self.hreftype = new['hreftype']
|
||||
if new['media']:
|
||||
# use same object
|
||||
self.media.mediaText = new['media'].mediaText
|
||||
# put it in newseq too
|
||||
for index, x in enumerate(newseq):
|
||||
if x.type == 'media':
|
||||
newseq.replace(index, self.media,
|
||||
x.type, x.line, x.col)
|
||||
break
|
||||
else:
|
||||
# reset media
|
||||
self.media.mediaText = u'all'
|
||||
newseq.append(self.media, 'media')
|
||||
self.name = new['name']
|
||||
self._setSeq(newseq)
|
||||
self.href = new['href']
|
||||
|
||||
if self.styleSheet:
|
||||
# title is set by href
|
||||
#self.styleSheet._href = self.href
|
||||
self.styleSheet._parentStyleSheet = self.parentStyleSheet
|
||||
|
||||
cssText = property(fget=_getCssText, fset=_setCssText,
|
||||
doc="(DOM attribute) The parsable textual representation.")
|
||||
|
||||
def _setHref(self, href):
|
||||
# update seq
|
||||
for i, item in enumerate(self.seq):
|
||||
val, typ = item.value, item.type
|
||||
if 'href' == typ:
|
||||
self._seq[i] = (href, typ, item.line, item.col)
|
||||
break
|
||||
else:
|
||||
seq = self._tempSeq()
|
||||
seq.append(self.href, 'href')
|
||||
self._setSeq(seq)
|
||||
# set new href
|
||||
self._href = href
|
||||
if not self.styleSheet:
|
||||
# set only if not set before
|
||||
self.__setStyleSheet()
|
||||
|
||||
href = property(lambda self: self._href, _setHref,
|
||||
doc="Location of the style sheet to be imported.")
|
||||
|
||||
media = property(lambda self: self._media,
|
||||
doc=u"(DOM readonly) A list of media types for this rule"
|
||||
" of type MediaList")
|
||||
|
||||
def _setName(self, name):
|
||||
"""raises xml.dom.SyntaxErr if name is not a string"""
|
||||
if isinstance(name, basestring) or name is None:
|
||||
# "" or ''
|
||||
if not name:
|
||||
name = None
|
||||
# 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
|
||||
else:
|
||||
# append
|
||||
seq = self._tempSeq()
|
||||
for item in self.seq:
|
||||
# copy current seq
|
||||
seq.append(item.value, item.type, item.line, item.col)
|
||||
seq.append(name, 'name')
|
||||
self._setSeq(seq)
|
||||
self._name = name
|
||||
# set title of referred 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")
|
||||
|
||||
def __setStyleSheet(self):
|
||||
"""Read new CSSStyleSheet cssText from href using parentStyleSheet.href
|
||||
|
||||
Indirectly called if setting ``href``. In case of any error styleSheet
|
||||
is set to ``None``.
|
||||
"""
|
||||
# should simply fail so all errors are catched!
|
||||
if self.parentStyleSheet and self.href:
|
||||
# relative href
|
||||
parentHref = self.parentStyleSheet.href
|
||||
if parentHref is None:
|
||||
# use cwd instead
|
||||
parentHref = u'file:' + urllib.pathname2url(os.getcwd()) + '/'
|
||||
href = urlparse.urljoin(parentHref, self.href)
|
||||
|
||||
# all possible exceptions are ignored (styleSheet is None then)
|
||||
try:
|
||||
usedEncoding, enctype, cssText = self.parentStyleSheet._resolveImport(href)
|
||||
if cssText is None:
|
||||
# catched in next except below!
|
||||
raise IOError('Cannot read Stylesheet.')
|
||||
styleSheet = cssutils.css.CSSStyleSheet(href=href,
|
||||
media=self.media,
|
||||
ownerRule=self,
|
||||
title=self.name)
|
||||
# inherit fetcher for @imports in styleSheet
|
||||
styleSheet._setFetcher(self.parentStyleSheet._fetcher)
|
||||
# contentEncoding with parentStyleSheet.overrideEncoding,
|
||||
# HTTP or parent
|
||||
encodingOverride, encoding = None, None
|
||||
if enctype == 0:
|
||||
encodingOverride = usedEncoding
|
||||
elif 5 > enctype > 0:
|
||||
encoding = usedEncoding
|
||||
|
||||
styleSheet._setCssTextWithEncodingOverride(cssText,
|
||||
encodingOverride=encodingOverride,
|
||||
encoding=encoding)
|
||||
|
||||
except (OSError, IOError, ValueError), e:
|
||||
self._log.warn(u'CSSImportRule: While processing imported style sheet href=%r: %r'
|
||||
% (self.href, e), neverraise=True)
|
||||
else:
|
||||
self._styleSheet = styleSheet
|
||||
|
||||
styleSheet = property(lambda self: self._styleSheet,
|
||||
doc="(readonly) The style sheet referred to by this rule.")
|
||||
|
||||
def _getWellformed(self):
|
||||
"depending 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)
|
||||
|
||||
def __repr__(self):
|
||||
if self._usemedia:
|
||||
mediaText = self.media.mediaText
|
||||
else:
|
||||
mediaText = None
|
||||
return "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 "<cssutils.css.%s object href=%r mediaText=%r name=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.href, mediaText, self.name, id(self))
|
@ -1,349 +1,349 @@
|
||||
"""CSSMediaRule implements DOM Level 2 CSS CSSMediaRule.
|
||||
"""
|
||||
__all__ = ['CSSMediaRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssmediarule.py 1370 2008-07-14 20:15:03Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
|
||||
class CSSMediaRule(cssrule.CSSRule):
|
||||
"""
|
||||
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.
|
||||
|
||||
Properties
|
||||
==========
|
||||
atkeyword: (cssutils only)
|
||||
the literal keyword used
|
||||
cssRules: A css::CSSRuleList of all CSS rules contained within the
|
||||
media block.
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of this rule
|
||||
media: of type stylesheets::MediaList, (DOM readonly)
|
||||
A list of media types for this rule of type MediaList.
|
||||
name:
|
||||
An optional name used for cascading
|
||||
|
||||
Format
|
||||
======
|
||||
media
|
||||
: MEDIA_SYM S* medium [ COMMA S* medium ]*
|
||||
|
||||
STRING? # the name
|
||||
|
||||
LBRACE S* ruleset* '}' S*;
|
||||
"""
|
||||
# CONSTANT
|
||||
type = property(lambda self: cssrule.CSSRule.MEDIA_RULE)
|
||||
|
||||
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'
|
||||
self._media = cssutils.stylesheets.MediaList(mediaText,
|
||||
readonly=readonly)
|
||||
self.name = name
|
||||
self.cssRules = cssutils.css.cssrulelist.CSSRuleList()
|
||||
self.cssRules.append = self.insertRule
|
||||
self.cssRules.extend = self.insertRule
|
||||
self.cssRules.__delitem__ == self.deleteRule
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __iter__(self):
|
||||
"""generator which iterates over cssRules."""
|
||||
for rule in self.cssRules:
|
||||
yield rule
|
||||
|
||||
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:
|
||||
- `NAMESPACE_ERR`: (Selector)
|
||||
Raised if a specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- `SYNTAX_ERR`: (self, StyleDeclaration, etc)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- `INVALID_MODIFICATION_ERR`: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- `HIERARCHY_REQUEST_ERR`: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule)
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
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:
|
||||
# media "name"? { cssRules }
|
||||
|
||||
# media
|
||||
wellformed = True
|
||||
mediatokens, end = self._tokensupto2(tokenizer,
|
||||
mediaqueryendonly=True,
|
||||
separateEnd=True)
|
||||
if u'{' == self._tokenvalue(end) or self._prods.STRING == self._type(end):
|
||||
newmedia = cssutils.stylesheets.MediaList()
|
||||
newmedia.mediaText = mediatokens
|
||||
|
||||
# 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:
|
||||
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 (u'}' != self._tokenvalue(braceOrEOF) and
|
||||
'EOF' != self._type(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
|
||||
newcssrules = [] #cssutils.css.CSSRuleList()
|
||||
new = {'wellformed': True }
|
||||
|
||||
def ruleset(expected, seq, token, tokenizer):
|
||||
rule = cssutils.css.CSSStyleRule(parentRule=self)
|
||||
rule.cssText = (self._tokensupto2(tokenizer, token),
|
||||
namespaces)
|
||||
if rule.wellformed:
|
||||
rule._parentStyleSheet=self.parentStyleSheet
|
||||
seq.append(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'):
|
||||
self._log.error(
|
||||
u'CSSMediaRule: This rule is not allowed in CSSMediaRule - ignored: %s.'
|
||||
% self._valuestr(tokens),
|
||||
token = token,
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
else:
|
||||
rule = cssutils.css.CSSUnknownRule(parentRule=self,
|
||||
parentStyleSheet=self.parentStyleSheet)
|
||||
rule.cssText = tokens
|
||||
if rule.wellformed:
|
||||
seq.append(rule)
|
||||
return expected
|
||||
|
||||
def COMMENT(expected, seq, token, tokenizer=None):
|
||||
seq.append(cssutils.css.CSSComment([token]))
|
||||
return expected
|
||||
|
||||
tokenizer = (t for t in cssrulestokens) # TODO: not elegant!
|
||||
wellformed, expected = self._parse(braceOrEOF,
|
||||
newcssrules,
|
||||
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)
|
||||
|
||||
# no post condition
|
||||
if newmedia.wellformed and wellformed:
|
||||
# keep reference
|
||||
self._media.mediaText = newmedia.mediaText
|
||||
self.name = name
|
||||
self._setSeq(nameseq)
|
||||
del self.cssRules[:]
|
||||
for r in newcssrules:
|
||||
self.cssRules.append(r)
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc="(DOM attribute) The parsable textual representation.")
|
||||
|
||||
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 the media rules")
|
||||
|
||||
media = property(lambda self: self._media,
|
||||
doc=u"(DOM readonly) A list of media types for this rule of type\
|
||||
MediaList")
|
||||
|
||||
wellformed = property(lambda self: self.media.wellformed)
|
||||
|
||||
def deleteRule(self, index):
|
||||
"""
|
||||
index
|
||||
within the media block's rule collection of the rule to remove.
|
||||
|
||||
Used to delete a rule from the media block.
|
||||
|
||||
DOMExceptions
|
||||
|
||||
- INDEX_SIZE_ERR: (self)
|
||||
Raised if the specified index does not correspond to a rule in
|
||||
the media rule list.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this media rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
try:
|
||||
self.cssRules[index]._parentRule = None # detach
|
||||
del self.cssRules[index] # remove from @media
|
||||
except IndexError:
|
||||
raise xml.dom.IndexSizeErr(
|
||||
u'CSSMediaRule: %s is not a valid index in the rulelist of length %i' % (
|
||||
index, self.cssRules.length))
|
||||
|
||||
def add(self, rule):
|
||||
"""Add rule to end of this mediarule. Same as ``.insertRule(rule)``."""
|
||||
self.insertRule(rule, index=None)
|
||||
|
||||
def insertRule(self, rule, index=None):
|
||||
"""
|
||||
rule
|
||||
The parsable text representing the rule. 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 **CSSRule** object
|
||||
|
||||
index
|
||||
within the media block's rule collection of the rule before
|
||||
which to insert the specified rule. If the specified index is
|
||||
equal to the length of the media blocks's rule collection, the
|
||||
rule will be added to the end of the media block.
|
||||
If index is not given or None rule will be appended to rule
|
||||
list.
|
||||
|
||||
Used to insert a new rule into the media block.
|
||||
|
||||
DOMException on setting
|
||||
|
||||
- HIERARCHY_REQUEST_ERR:
|
||||
(no use case yet as no @charset or @import allowed))
|
||||
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.
|
||||
- INDEX_SIZE_ERR: (self)
|
||||
Raised if the specified index is not a valid insertion point.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this media rule is readonly.
|
||||
- SYNTAX_ERR: (CSSStyleRule)
|
||||
Raised if the specified rule has a syntax error and is
|
||||
unparsable.
|
||||
|
||||
returns the index within the media block's rule collection of the
|
||||
newly inserted rule.
|
||||
|
||||
"""
|
||||
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'CSSMediaRule: Invalid index %s for CSSRuleList with a length of %s.' % (
|
||||
index, self.cssRules.length))
|
||||
|
||||
# parse
|
||||
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'CSSMediaRule: Invalid Rule: %s' % rule)
|
||||
return
|
||||
rule = tempsheet.cssRules[0]
|
||||
elif not isinstance(rule, cssutils.css.CSSRule):
|
||||
self._log.error(u'CSSMediaRule: Not a CSSRule: %s' % rule)
|
||||
return
|
||||
|
||||
# CHECK HIERARCHY
|
||||
# @charset @import @page @namespace @media
|
||||
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, CSSMediaRule):
|
||||
self._log.error(u'CSSMediaRule: This type of rule is not allowed here: %s' %
|
||||
rule.cssText,
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
|
||||
self.cssRules.insert(index, rule)
|
||||
rule._parentRule = self
|
||||
rule._parentStyleSheet = self.parentStyleSheet
|
||||
return index
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(mediaText=%r)" % (
|
||||
self.__class__.__name__, self.media.mediaText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object mediaText=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.media.mediaText, id(self))
|
||||
"""CSSMediaRule implements DOM Level 2 CSS CSSMediaRule.
|
||||
"""
|
||||
__all__ = ['CSSMediaRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssmediarule.py 1370 2008-07-14 20:15:03Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
|
||||
class CSSMediaRule(cssrule.CSSRule):
|
||||
"""
|
||||
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.
|
||||
|
||||
Properties
|
||||
==========
|
||||
atkeyword: (cssutils only)
|
||||
the literal keyword used
|
||||
cssRules: A css::CSSRuleList of all CSS rules contained within the
|
||||
media block.
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of this rule
|
||||
media: of type stylesheets::MediaList, (DOM readonly)
|
||||
A list of media types for this rule of type MediaList.
|
||||
name:
|
||||
An optional name used for cascading
|
||||
|
||||
Format
|
||||
======
|
||||
media
|
||||
: MEDIA_SYM S* medium [ COMMA S* medium ]*
|
||||
|
||||
STRING? # the name
|
||||
|
||||
LBRACE S* ruleset* '}' S*;
|
||||
"""
|
||||
# CONSTANT
|
||||
type = property(lambda self: cssrule.CSSRule.MEDIA_RULE)
|
||||
|
||||
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'
|
||||
self._media = cssutils.stylesheets.MediaList(mediaText,
|
||||
readonly=readonly)
|
||||
self.name = name
|
||||
self.cssRules = cssutils.css.cssrulelist.CSSRuleList()
|
||||
self.cssRules.append = self.insertRule
|
||||
self.cssRules.extend = self.insertRule
|
||||
self.cssRules.__delitem__ == self.deleteRule
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __iter__(self):
|
||||
"""generator which iterates over cssRules."""
|
||||
for rule in self.cssRules:
|
||||
yield rule
|
||||
|
||||
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:
|
||||
- `NAMESPACE_ERR`: (Selector)
|
||||
Raised if a specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- `SYNTAX_ERR`: (self, StyleDeclaration, etc)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- `INVALID_MODIFICATION_ERR`: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- `HIERARCHY_REQUEST_ERR`: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule)
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
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:
|
||||
# media "name"? { cssRules }
|
||||
|
||||
# media
|
||||
wellformed = True
|
||||
mediatokens, end = self._tokensupto2(tokenizer,
|
||||
mediaqueryendonly=True,
|
||||
separateEnd=True)
|
||||
if u'{' == self._tokenvalue(end) or self._prods.STRING == self._type(end):
|
||||
newmedia = cssutils.stylesheets.MediaList()
|
||||
newmedia.mediaText = mediatokens
|
||||
|
||||
# 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:
|
||||
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 (u'}' != self._tokenvalue(braceOrEOF) and
|
||||
'EOF' != self._type(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
|
||||
newcssrules = [] #cssutils.css.CSSRuleList()
|
||||
new = {'wellformed': True }
|
||||
|
||||
def ruleset(expected, seq, token, tokenizer):
|
||||
rule = cssutils.css.CSSStyleRule(parentRule=self)
|
||||
rule.cssText = (self._tokensupto2(tokenizer, token),
|
||||
namespaces)
|
||||
if rule.wellformed:
|
||||
rule._parentStyleSheet=self.parentStyleSheet
|
||||
seq.append(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'):
|
||||
self._log.error(
|
||||
u'CSSMediaRule: This rule is not allowed in CSSMediaRule - ignored: %s.'
|
||||
% self._valuestr(tokens),
|
||||
token = token,
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
else:
|
||||
rule = cssutils.css.CSSUnknownRule(parentRule=self,
|
||||
parentStyleSheet=self.parentStyleSheet)
|
||||
rule.cssText = tokens
|
||||
if rule.wellformed:
|
||||
seq.append(rule)
|
||||
return expected
|
||||
|
||||
def COMMENT(expected, seq, token, tokenizer=None):
|
||||
seq.append(cssutils.css.CSSComment([token]))
|
||||
return expected
|
||||
|
||||
tokenizer = (t for t in cssrulestokens) # TODO: not elegant!
|
||||
wellformed, expected = self._parse(braceOrEOF,
|
||||
newcssrules,
|
||||
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)
|
||||
|
||||
# no post condition
|
||||
if newmedia.wellformed and wellformed:
|
||||
# keep reference
|
||||
self._media.mediaText = newmedia.mediaText
|
||||
self.name = name
|
||||
self._setSeq(nameseq)
|
||||
del self.cssRules[:]
|
||||
for r in newcssrules:
|
||||
self.cssRules.append(r)
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc="(DOM attribute) The parsable textual representation.")
|
||||
|
||||
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 the media rules")
|
||||
|
||||
media = property(lambda self: self._media,
|
||||
doc=u"(DOM readonly) A list of media types for this rule of type\
|
||||
MediaList")
|
||||
|
||||
wellformed = property(lambda self: self.media.wellformed)
|
||||
|
||||
def deleteRule(self, index):
|
||||
"""
|
||||
index
|
||||
within the media block's rule collection of the rule to remove.
|
||||
|
||||
Used to delete a rule from the media block.
|
||||
|
||||
DOMExceptions
|
||||
|
||||
- INDEX_SIZE_ERR: (self)
|
||||
Raised if the specified index does not correspond to a rule in
|
||||
the media rule list.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this media rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
try:
|
||||
self.cssRules[index]._parentRule = None # detach
|
||||
del self.cssRules[index] # remove from @media
|
||||
except IndexError:
|
||||
raise xml.dom.IndexSizeErr(
|
||||
u'CSSMediaRule: %s is not a valid index in the rulelist of length %i' % (
|
||||
index, self.cssRules.length))
|
||||
|
||||
def add(self, rule):
|
||||
"""Add rule to end of this mediarule. Same as ``.insertRule(rule)``."""
|
||||
self.insertRule(rule, index=None)
|
||||
|
||||
def insertRule(self, rule, index=None):
|
||||
"""
|
||||
rule
|
||||
The parsable text representing the rule. 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 **CSSRule** object
|
||||
|
||||
index
|
||||
within the media block's rule collection of the rule before
|
||||
which to insert the specified rule. If the specified index is
|
||||
equal to the length of the media blocks's rule collection, the
|
||||
rule will be added to the end of the media block.
|
||||
If index is not given or None rule will be appended to rule
|
||||
list.
|
||||
|
||||
Used to insert a new rule into the media block.
|
||||
|
||||
DOMException on setting
|
||||
|
||||
- HIERARCHY_REQUEST_ERR:
|
||||
(no use case yet as no @charset or @import allowed))
|
||||
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.
|
||||
- INDEX_SIZE_ERR: (self)
|
||||
Raised if the specified index is not a valid insertion point.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this media rule is readonly.
|
||||
- SYNTAX_ERR: (CSSStyleRule)
|
||||
Raised if the specified rule has a syntax error and is
|
||||
unparsable.
|
||||
|
||||
returns the index within the media block's rule collection of the
|
||||
newly inserted rule.
|
||||
|
||||
"""
|
||||
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'CSSMediaRule: Invalid index %s for CSSRuleList with a length of %s.' % (
|
||||
index, self.cssRules.length))
|
||||
|
||||
# parse
|
||||
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'CSSMediaRule: Invalid Rule: %s' % rule)
|
||||
return
|
||||
rule = tempsheet.cssRules[0]
|
||||
elif not isinstance(rule, cssutils.css.CSSRule):
|
||||
self._log.error(u'CSSMediaRule: Not a CSSRule: %s' % rule)
|
||||
return
|
||||
|
||||
# CHECK HIERARCHY
|
||||
# @charset @import @page @namespace @media
|
||||
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, CSSMediaRule):
|
||||
self._log.error(u'CSSMediaRule: This type of rule is not allowed here: %s' %
|
||||
rule.cssText,
|
||||
error=xml.dom.HierarchyRequestErr)
|
||||
return
|
||||
|
||||
self.cssRules.insert(index, rule)
|
||||
rule._parentRule = self
|
||||
rule._parentStyleSheet = self.parentStyleSheet
|
||||
return index
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(mediaText=%r)" % (
|
||||
self.__class__.__name__, self.media.mediaText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object mediaText=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.media.mediaText, id(self))
|
@ -1,306 +1,306 @@
|
||||
"""CSSNamespaceRule currently implements
|
||||
http://dev.w3.org/csswg/css3-namespace/
|
||||
|
||||
(until 0.9.5a2: http://www.w3.org/TR/2006/WD-css3-namespace-20060828/)
|
||||
"""
|
||||
__all__ = ['CSSNamespaceRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssnamespacerule.py 1305 2008-06-22 18:42:51Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
from cssutils.helper import Deprecated
|
||||
|
||||
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].
|
||||
|
||||
Properties
|
||||
==========
|
||||
atkeyword (cssutils only)
|
||||
the literal keyword used
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of this rule
|
||||
namespaceURI: of type DOMString
|
||||
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: of type DOMString
|
||||
The prefix used in the stylesheet for the given
|
||||
``CSSNamespaceRule.nsuri``. If prefix is empty namespaceURI sets a
|
||||
default namespace for the stylesheet.
|
||||
|
||||
Inherits properties from CSSRule
|
||||
|
||||
Format
|
||||
======
|
||||
namespace
|
||||
: NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
|
||||
;
|
||||
namespace_prefix
|
||||
: IDENT
|
||||
;
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.NAMESPACE_RULE)
|
||||
|
||||
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 _getCssText(self):
|
||||
"""
|
||||
returns serialized property cssText
|
||||
"""
|
||||
return cssutils.ser.do_CSSNamespaceRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
:param cssText: initial value for this rules cssText which is parsed
|
||||
:Exceptions:
|
||||
- `HIERARCHY_REQUEST_ERR`: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- `INVALID_MODIFICATION_ERR`: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule)
|
||||
Raised if the rule is readonly.
|
||||
- `SYNTAX_ERR`: (self)
|
||||
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="(DOM attribute) The parsable textual representation.")
|
||||
|
||||
def _setNamespaceURI(self, namespaceURI):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
:param namespaceURI: the initial value for this rules namespaceURI
|
||||
:Exceptions:
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`:
|
||||
(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 (string!) of the defined namespace.")
|
||||
|
||||
def _setPrefix(self, prefix=None):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
:param prefix: the new prefix
|
||||
:Exceptions:
|
||||
- `SYNTAX_ERR`: (TODO)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: CSSRule)
|
||||
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 seg
|
||||
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="Prefix used for the defined namespace.")
|
||||
|
||||
# def _setParentStyleSheet(self, parentStyleSheet):
|
||||
# self._parentStyleSheet = parentStyleSheet
|
||||
#
|
||||
# parentStyleSheet = property(lambda self: self._parentStyleSheet,
|
||||
# _setParentStyleSheet,
|
||||
# doc=u"Containing CSSStyleSheet.")
|
||||
|
||||
wellformed = property(lambda self: self.namespaceURI is not None)
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(namespaceURI=%r, prefix=%r)" % (
|
||||
self.__class__.__name__, self.namespaceURI, self.prefix)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object namespaceURI=%r prefix=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.namespaceURI, self.prefix, id(self))
|
||||
"""CSSNamespaceRule currently implements
|
||||
http://dev.w3.org/csswg/css3-namespace/
|
||||
|
||||
(until 0.9.5a2: http://www.w3.org/TR/2006/WD-css3-namespace-20060828/)
|
||||
"""
|
||||
__all__ = ['CSSNamespaceRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssnamespacerule.py 1305 2008-06-22 18:42:51Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
from cssutils.helper import Deprecated
|
||||
|
||||
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].
|
||||
|
||||
Properties
|
||||
==========
|
||||
atkeyword (cssutils only)
|
||||
the literal keyword used
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of this rule
|
||||
namespaceURI: of type DOMString
|
||||
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: of type DOMString
|
||||
The prefix used in the stylesheet for the given
|
||||
``CSSNamespaceRule.nsuri``. If prefix is empty namespaceURI sets a
|
||||
default namespace for the stylesheet.
|
||||
|
||||
Inherits properties from CSSRule
|
||||
|
||||
Format
|
||||
======
|
||||
namespace
|
||||
: NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
|
||||
;
|
||||
namespace_prefix
|
||||
: IDENT
|
||||
;
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.NAMESPACE_RULE)
|
||||
|
||||
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 _getCssText(self):
|
||||
"""
|
||||
returns serialized property cssText
|
||||
"""
|
||||
return cssutils.ser.do_CSSNamespaceRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
:param cssText: initial value for this rules cssText which is parsed
|
||||
:Exceptions:
|
||||
- `HIERARCHY_REQUEST_ERR`: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- `INVALID_MODIFICATION_ERR`: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule)
|
||||
Raised if the rule is readonly.
|
||||
- `SYNTAX_ERR`: (self)
|
||||
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="(DOM attribute) The parsable textual representation.")
|
||||
|
||||
def _setNamespaceURI(self, namespaceURI):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
:param namespaceURI: the initial value for this rules namespaceURI
|
||||
:Exceptions:
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`:
|
||||
(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 (string!) of the defined namespace.")
|
||||
|
||||
def _setPrefix(self, prefix=None):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
:param prefix: the new prefix
|
||||
:Exceptions:
|
||||
- `SYNTAX_ERR`: (TODO)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: CSSRule)
|
||||
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 seg
|
||||
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="Prefix used for the defined namespace.")
|
||||
|
||||
# def _setParentStyleSheet(self, parentStyleSheet):
|
||||
# self._parentStyleSheet = parentStyleSheet
|
||||
#
|
||||
# parentStyleSheet = property(lambda self: self._parentStyleSheet,
|
||||
# _setParentStyleSheet,
|
||||
# doc=u"Containing CSSStyleSheet.")
|
||||
|
||||
wellformed = property(lambda self: self.namespaceURI is not None)
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(namespaceURI=%r, prefix=%r)" % (
|
||||
self.__class__.__name__, self.namespaceURI, self.prefix)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object namespaceURI=%r prefix=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.namespaceURI, self.prefix, id(self))
|
@ -1,286 +1,286 @@
|
||||
"""CSSPageRule implements DOM Level 2 CSS CSSPageRule.
|
||||
"""
|
||||
__all__ = ['CSSPageRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: csspagerule.py 1284 2008-06-05 16:29:17Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
from selectorlist import SelectorList
|
||||
from cssstyledeclaration import CSSStyleDeclaration
|
||||
|
||||
class CSSPageRule(cssrule.CSSRule):
|
||||
"""
|
||||
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.
|
||||
|
||||
Properties
|
||||
==========
|
||||
atkeyword (cssutils only)
|
||||
the literal keyword used
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of this rule
|
||||
selectorText: of type DOMString
|
||||
The parsable textual representation of the page selector for the rule.
|
||||
style: of type CSSStyleDeclaration
|
||||
The declaration-block of this rule.
|
||||
|
||||
Inherits properties from CSSRule
|
||||
|
||||
Format
|
||||
======
|
||||
::
|
||||
|
||||
page
|
||||
: PAGE_SYM S* pseudo_page? S*
|
||||
LBRACE S* declaration [ ';' S* declaration ]* '}' S*
|
||||
;
|
||||
pseudo_page
|
||||
: ':' IDENT # :first, :left, :right in CSS 2.1
|
||||
;
|
||||
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.PAGE_RULE)
|
||||
# constant but needed:
|
||||
wellformed = True
|
||||
|
||||
def __init__(self, selectorText=None, style=None, parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
if readonly allows setting of properties in constructor only
|
||||
|
||||
selectorText
|
||||
type string
|
||||
style
|
||||
CSSStyleDeclaration for this CSSStyleRule
|
||||
"""
|
||||
super(CSSPageRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = u'@page'
|
||||
tempseq = self._tempSeq()
|
||||
if selectorText:
|
||||
self.selectorText = selectorText
|
||||
tempseq.append(self.selectorText, 'selectorText')
|
||||
else:
|
||||
self._selectorText = u''
|
||||
if style:
|
||||
self.style = style
|
||||
tempseq.append(self.style, 'style')
|
||||
else:
|
||||
self._style = CSSStyleDeclaration(parentRule=self)
|
||||
self._setSeq(tempseq)
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __parseSelectorText(self, selectorText):
|
||||
"""
|
||||
parses selectorText which may also be a list of tokens
|
||||
and returns (selectorText, seq)
|
||||
|
||||
see _setSelectorText for details
|
||||
"""
|
||||
# for closures: must be a mutable
|
||||
new = {'selector': None, 'wellformed': True}
|
||||
|
||||
def _char(expected, seq, token, tokenizer=None):
|
||||
# pseudo_page, :left, :right or :first
|
||||
val = self._tokenvalue(token)
|
||||
if ':' == expected 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 IDENT but found: %r' %
|
||||
ival, token)
|
||||
else:
|
||||
new['selector'] = val + ival
|
||||
seq.append(new['selector'], 'selector')
|
||||
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."
|
||||
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=':',
|
||||
seq=newseq, tokenizer=self._tokenize2(selectorText),
|
||||
productions={'CHAR': _char,
|
||||
'COMMENT': COMMENT,
|
||||
'S': S},
|
||||
new=new)
|
||||
wellformed = wellformed and new['wellformed']
|
||||
newselector = new['selector']
|
||||
|
||||
# post conditions
|
||||
if expected == 'ident':
|
||||
self._log.error(
|
||||
u'CSSPageRule selectorText: No valid selector: %r' %
|
||||
self._valuestr(selectorText))
|
||||
|
||||
if not newselector in (None, u':first', u':left', u':right'):
|
||||
self._log.warn(u'CSSPageRule: Unknown CSS 2.1 @page selector: %r' %
|
||||
newselector, neverraise=True)
|
||||
|
||||
return newselector, newseq
|
||||
|
||||
def _getCssText(self):
|
||||
"""
|
||||
returns serialized property cssText
|
||||
"""
|
||||
return cssutils.ser.do_CSSPageRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR: (self, StyleDeclaration)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- INVALID_MODIFICATION_ERR: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- HIERARCHY_REQUEST_ERR: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
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:
|
||||
wellformed = 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'{':
|
||||
wellformed = False
|
||||
self._log.error(
|
||||
u'CSSPageRule: No start { of style declaration found: %r' %
|
||||
self._valuestr(cssText), startbrace)
|
||||
elif nonetoken:
|
||||
wellformed = False
|
||||
self._log.error(
|
||||
u'CSSPageRule: Trailing content found.', token=nonetoken)
|
||||
|
||||
|
||||
newselector, newselectorseq = self.__parseSelectorText(selectortokens)
|
||||
|
||||
newstyle = CSSStyleDeclaration()
|
||||
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
|
||||
if val != u'}' and typ != 'EOF':
|
||||
wellformed = False
|
||||
self._log.error(
|
||||
u'CSSPageRule: No "}" after style declaration found: %r' %
|
||||
self._valuestr(cssText))
|
||||
else:
|
||||
if 'EOF' == typ:
|
||||
# add again as style needs it
|
||||
styletokens.append(braceorEOFtoken)
|
||||
newstyle.cssText = styletokens
|
||||
|
||||
if wellformed:
|
||||
self._selectorText = newselector # already parsed
|
||||
self.style = newstyle
|
||||
self._setSeq(newselectorseq) # contains upto style only
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc="(DOM) The parsable textual representation of the rule.")
|
||||
|
||||
def _getSelectorText(self):
|
||||
"""
|
||||
wrapper for cssutils Selector object
|
||||
"""
|
||||
return self._selectorText
|
||||
|
||||
def _setSelectorText(self, selectorText):
|
||||
"""
|
||||
wrapper for cssutils Selector object
|
||||
|
||||
selector: DOM String
|
||||
in CSS 2.1 one of
|
||||
- :first
|
||||
- :left
|
||||
- :right
|
||||
- empty
|
||||
|
||||
If WS or Comments are included they are ignored here! Only
|
||||
way to add a comment is via setting ``cssText``
|
||||
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR:
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
and is unparsable.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
# may raise SYNTAX_ERR
|
||||
newselectortext, newseq = self.__parseSelectorText(selectorText)
|
||||
|
||||
if newselectortext:
|
||||
for i, x in enumerate(self.seq):
|
||||
if x == self._selectorText:
|
||||
self.seq[i] = newselectortext
|
||||
self._selectorText = newselectortext
|
||||
|
||||
selectorText = property(_getSelectorText, _setSelectorText,
|
||||
doc="""(DOM) The parsable textual representation of the page selector for the rule.""")
|
||||
|
||||
def _getStyle(self):
|
||||
|
||||
return self._style
|
||||
|
||||
def _setStyle(self, style):
|
||||
"""
|
||||
style
|
||||
StyleDeclaration or string
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
if isinstance(style, basestring):
|
||||
self._style.cssText = style
|
||||
else:
|
||||
# cssText would be serialized with optional preferences
|
||||
# so use seq!
|
||||
self._style._seq = style.seq
|
||||
|
||||
style = property(_getStyle, _setStyle,
|
||||
doc="(DOM) The declaration-block of this rule set.")
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(selectorText=%r, style=%r)" % (
|
||||
self.__class__.__name__, self.selectorText, self.style.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object selectorText=%r style=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.selectorText, self.style.cssText,
|
||||
id(self))
|
||||
"""CSSPageRule implements DOM Level 2 CSS CSSPageRule.
|
||||
"""
|
||||
__all__ = ['CSSPageRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: csspagerule.py 1284 2008-06-05 16:29:17Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
from selectorlist import SelectorList
|
||||
from cssstyledeclaration import CSSStyleDeclaration
|
||||
|
||||
class CSSPageRule(cssrule.CSSRule):
|
||||
"""
|
||||
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.
|
||||
|
||||
Properties
|
||||
==========
|
||||
atkeyword (cssutils only)
|
||||
the literal keyword used
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of this rule
|
||||
selectorText: of type DOMString
|
||||
The parsable textual representation of the page selector for the rule.
|
||||
style: of type CSSStyleDeclaration
|
||||
The declaration-block of this rule.
|
||||
|
||||
Inherits properties from CSSRule
|
||||
|
||||
Format
|
||||
======
|
||||
::
|
||||
|
||||
page
|
||||
: PAGE_SYM S* pseudo_page? S*
|
||||
LBRACE S* declaration [ ';' S* declaration ]* '}' S*
|
||||
;
|
||||
pseudo_page
|
||||
: ':' IDENT # :first, :left, :right in CSS 2.1
|
||||
;
|
||||
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.PAGE_RULE)
|
||||
# constant but needed:
|
||||
wellformed = True
|
||||
|
||||
def __init__(self, selectorText=None, style=None, parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
if readonly allows setting of properties in constructor only
|
||||
|
||||
selectorText
|
||||
type string
|
||||
style
|
||||
CSSStyleDeclaration for this CSSStyleRule
|
||||
"""
|
||||
super(CSSPageRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = u'@page'
|
||||
tempseq = self._tempSeq()
|
||||
if selectorText:
|
||||
self.selectorText = selectorText
|
||||
tempseq.append(self.selectorText, 'selectorText')
|
||||
else:
|
||||
self._selectorText = u''
|
||||
if style:
|
||||
self.style = style
|
||||
tempseq.append(self.style, 'style')
|
||||
else:
|
||||
self._style = CSSStyleDeclaration(parentRule=self)
|
||||
self._setSeq(tempseq)
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def __parseSelectorText(self, selectorText):
|
||||
"""
|
||||
parses selectorText which may also be a list of tokens
|
||||
and returns (selectorText, seq)
|
||||
|
||||
see _setSelectorText for details
|
||||
"""
|
||||
# for closures: must be a mutable
|
||||
new = {'selector': None, 'wellformed': True}
|
||||
|
||||
def _char(expected, seq, token, tokenizer=None):
|
||||
# pseudo_page, :left, :right or :first
|
||||
val = self._tokenvalue(token)
|
||||
if ':' == expected 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 IDENT but found: %r' %
|
||||
ival, token)
|
||||
else:
|
||||
new['selector'] = val + ival
|
||||
seq.append(new['selector'], 'selector')
|
||||
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."
|
||||
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=':',
|
||||
seq=newseq, tokenizer=self._tokenize2(selectorText),
|
||||
productions={'CHAR': _char,
|
||||
'COMMENT': COMMENT,
|
||||
'S': S},
|
||||
new=new)
|
||||
wellformed = wellformed and new['wellformed']
|
||||
newselector = new['selector']
|
||||
|
||||
# post conditions
|
||||
if expected == 'ident':
|
||||
self._log.error(
|
||||
u'CSSPageRule selectorText: No valid selector: %r' %
|
||||
self._valuestr(selectorText))
|
||||
|
||||
if not newselector in (None, u':first', u':left', u':right'):
|
||||
self._log.warn(u'CSSPageRule: Unknown CSS 2.1 @page selector: %r' %
|
||||
newselector, neverraise=True)
|
||||
|
||||
return newselector, newseq
|
||||
|
||||
def _getCssText(self):
|
||||
"""
|
||||
returns serialized property cssText
|
||||
"""
|
||||
return cssutils.ser.do_CSSPageRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR: (self, StyleDeclaration)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- INVALID_MODIFICATION_ERR: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- HIERARCHY_REQUEST_ERR: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
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:
|
||||
wellformed = 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'{':
|
||||
wellformed = False
|
||||
self._log.error(
|
||||
u'CSSPageRule: No start { of style declaration found: %r' %
|
||||
self._valuestr(cssText), startbrace)
|
||||
elif nonetoken:
|
||||
wellformed = False
|
||||
self._log.error(
|
||||
u'CSSPageRule: Trailing content found.', token=nonetoken)
|
||||
|
||||
|
||||
newselector, newselectorseq = self.__parseSelectorText(selectortokens)
|
||||
|
||||
newstyle = CSSStyleDeclaration()
|
||||
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
|
||||
if val != u'}' and typ != 'EOF':
|
||||
wellformed = False
|
||||
self._log.error(
|
||||
u'CSSPageRule: No "}" after style declaration found: %r' %
|
||||
self._valuestr(cssText))
|
||||
else:
|
||||
if 'EOF' == typ:
|
||||
# add again as style needs it
|
||||
styletokens.append(braceorEOFtoken)
|
||||
newstyle.cssText = styletokens
|
||||
|
||||
if wellformed:
|
||||
self._selectorText = newselector # already parsed
|
||||
self.style = newstyle
|
||||
self._setSeq(newselectorseq) # contains upto style only
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc="(DOM) The parsable textual representation of the rule.")
|
||||
|
||||
def _getSelectorText(self):
|
||||
"""
|
||||
wrapper for cssutils Selector object
|
||||
"""
|
||||
return self._selectorText
|
||||
|
||||
def _setSelectorText(self, selectorText):
|
||||
"""
|
||||
wrapper for cssutils Selector object
|
||||
|
||||
selector: DOM String
|
||||
in CSS 2.1 one of
|
||||
- :first
|
||||
- :left
|
||||
- :right
|
||||
- empty
|
||||
|
||||
If WS or Comments are included they are ignored here! Only
|
||||
way to add a comment is via setting ``cssText``
|
||||
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR:
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
and is unparsable.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
# may raise SYNTAX_ERR
|
||||
newselectortext, newseq = self.__parseSelectorText(selectorText)
|
||||
|
||||
if newselectortext:
|
||||
for i, x in enumerate(self.seq):
|
||||
if x == self._selectorText:
|
||||
self.seq[i] = newselectortext
|
||||
self._selectorText = newselectortext
|
||||
|
||||
selectorText = property(_getSelectorText, _setSelectorText,
|
||||
doc="""(DOM) The parsable textual representation of the page selector for the rule.""")
|
||||
|
||||
def _getStyle(self):
|
||||
|
||||
return self._style
|
||||
|
||||
def _setStyle(self, style):
|
||||
"""
|
||||
style
|
||||
StyleDeclaration or string
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
if isinstance(style, basestring):
|
||||
self._style.cssText = style
|
||||
else:
|
||||
# cssText would be serialized with optional preferences
|
||||
# so use seq!
|
||||
self._style._seq = style.seq
|
||||
|
||||
style = property(_getStyle, _setStyle,
|
||||
doc="(DOM) The declaration-block of this rule set.")
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(selectorText=%r, style=%r)" % (
|
||||
self.__class__.__name__, self.selectorText, self.style.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object selectorText=%r style=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.selectorText, self.style.cssText,
|
||||
id(self))
|
@ -1,349 +1,349 @@
|
||||
"""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".
|
||||
|
||||
|
||||
cssvalues
|
||||
=========
|
||||
contributed by Kevin D. Smith, thanks!
|
||||
|
||||
"cssvalues" is used as a property validator.
|
||||
it is an importable object that contains a dictionary of compiled regular
|
||||
expressions. The keys of this dictionary are all of the valid CSS property
|
||||
names. The values are compiled regular expressions that can be used to
|
||||
validate the values for that property. (Actually, the values are references
|
||||
to the 'match' method of a compiled regular expression, so that they are
|
||||
simply called like functions.)
|
||||
|
||||
"""
|
||||
__all__ = ['CSS2Properties', 'cssvalues']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssproperties.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
import re
|
||||
|
||||
"""
|
||||
Define some regular expression fragments that will be used as
|
||||
macros within the CSS property value regular expressions.
|
||||
"""
|
||||
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+',
|
||||
'number': r'{num}',
|
||||
'string': r'{string1}|{string2}',
|
||||
'string1': r'"(\\\"|[^\"])*"',
|
||||
'string2': r"'(\\\'|[^\'])*'",
|
||||
'nl': r'\n|\r\n|\r|\f',
|
||||
'w': r'\s*',
|
||||
|
||||
'integer': r'{int}',
|
||||
'length': r'0|{num}(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',
|
||||
'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}\)',
|
||||
'uri': r'url\({w}({string}|(\\\)|[^\)])+){w}\)',
|
||||
'percentage': r'{num}%',
|
||||
'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset',
|
||||
'border-color': '{color}',
|
||||
'border-width': '{length}|thin|medium|thick',
|
||||
|
||||
'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-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}{identifier}{w}(?:,{w}{list-style-type}{w})?\)',
|
||||
'identifier': r'{ident}',
|
||||
'family-name': r'{string}|{identifier}',
|
||||
'generic-family': r'serif|sans-serif|cursive|fantasy|monospace',
|
||||
'absolute-size': r'(x?x-)?(small|large)|medium',
|
||||
'relative-size': r'smaller|larger',
|
||||
'font-family': r'(({family-name}|{generic-family}){w},{w})*({family-name}|{generic-family})|inherit',
|
||||
'font-size': r'{absolute-size}|{relative-size}|{length}|{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',
|
||||
'outline-color': r'{color}|invert|inherit',
|
||||
'outline-style': r'{border-style}|inherit',
|
||||
'outline-width': r'{border-width}|inherit',
|
||||
'padding-width': r'{length}|{percentage}',
|
||||
'specific-voice': r'{identifier}',
|
||||
'generic-voice': r'male|female|child',
|
||||
'content': r'{string}|{uri}|{counter}|attr\({w}{identifier}{w}\)|open-quote|close-quote|no-open-quote|no-close-quote',
|
||||
'border-attrs': r'{border-width}|{border-style}|{border-color}',
|
||||
'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}',
|
||||
'outline-attrs': r'{outline-color}|{outline-style}|{outline-width}',
|
||||
'text-attrs': r'underline|overline|line-through|blink',
|
||||
}
|
||||
|
||||
"""
|
||||
Define the regular expressions for validation all CSS values
|
||||
"""
|
||||
cssvalues = {
|
||||
'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-color': r'({border-color}|transparent)(\s+({border-color}|transparent)){0,3}|inherit',
|
||||
'border-spacing': r'{length}(\s+{length})?|inherit',
|
||||
'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit',
|
||||
'border-top': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-right': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-bottom': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-left': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-top-color': r'{border-color}|transparent|inherit',
|
||||
'border-right-color': r'{border-color}|transparent|inherit',
|
||||
'border-bottom-color': r'{border-color}|transparent|inherit',
|
||||
'border-left-color': r'{border-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-right-width': r'{border-width}|inherit',
|
||||
'border-width': r'{border-width}(\s+{border-width}){0,3}|inherit',
|
||||
'border': r'{border-attrs}(\s+{border-attrs})*|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'normal|{content}(\s+{content})*|inherit',
|
||||
'counter-increment': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
|
||||
'counter-reset': r'({identifier}(\s+{integer})?)(\s+({identifier}(\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',
|
||||
'font-family': r'{font-family}',
|
||||
'font-size': r'{font-size}',
|
||||
'font-style': r'{font-style}',
|
||||
'font-variant': r'{font-variant}',
|
||||
'font-weight': r'{font-weight}',
|
||||
'font': r'({font-attrs}\s+)*{font-size}({w}/{w}{line-height})?\s+{font-family}|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',
|
||||
'outline-color': r'{outline-color}',
|
||||
'outline-style': r'{outline-style}',
|
||||
'outline-width': r'{outline-width}',
|
||||
'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit',
|
||||
'overflow': r'visible|hidden|scroll|auto|inherit',
|
||||
'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',
|
||||
}
|
||||
|
||||
def _expand_macros(tokdict):
|
||||
""" Expand macros in token dictionary """
|
||||
def macro_value(m):
|
||||
return '(?:%s)' % MACROS[m.groupdict()['macro']]
|
||||
for key, value in tokdict.items():
|
||||
while re.search(r'{[a-z][a-z0-9-]*}', value):
|
||||
value = re.sub(r'{(?P<macro>[a-z][a-z0-9-]*)}',
|
||||
macro_value, value)
|
||||
tokdict[key] = value
|
||||
return tokdict
|
||||
|
||||
def _compile_regexes(tokdict):
|
||||
""" Compile all regular expressions into callable objects """
|
||||
for key, value in tokdict.items():
|
||||
tokdict[key] = re.compile('^(?:%s)$' % value, re.I).match
|
||||
return tokdict
|
||||
|
||||
_compile_regexes(_expand_macros(cssvalues))
|
||||
|
||||
|
||||
# functions to convert between CSS and DOM name
|
||||
|
||||
_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):
|
||||
"""
|
||||
returns 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)
|
||||
|
||||
|
||||
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
|
||||
|
||||
# add list of DOMname properties to CSS2Properties
|
||||
# used for CSSStyleDeclaration to check if allowed properties
|
||||
# but somehow doubled, any better way?
|
||||
CSS2Properties._properties = [_toDOMname(p) for p in cssvalues.keys()]
|
||||
|
||||
# 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)))
|
||||
"""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".
|
||||
|
||||
|
||||
cssvalues
|
||||
=========
|
||||
contributed by Kevin D. Smith, thanks!
|
||||
|
||||
"cssvalues" is used as a property validator.
|
||||
it is an importable object that contains a dictionary of compiled regular
|
||||
expressions. The keys of this dictionary are all of the valid CSS property
|
||||
names. The values are compiled regular expressions that can be used to
|
||||
validate the values for that property. (Actually, the values are references
|
||||
to the 'match' method of a compiled regular expression, so that they are
|
||||
simply called like functions.)
|
||||
|
||||
"""
|
||||
__all__ = ['CSS2Properties', 'cssvalues']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssproperties.py 1469 2008-09-15 19:06:00Z cthedot $'
|
||||
|
||||
import re
|
||||
|
||||
"""
|
||||
Define some regular expression fragments that will be used as
|
||||
macros within the CSS property value regular expressions.
|
||||
"""
|
||||
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+',
|
||||
'number': r'{num}',
|
||||
'string': r'{string1}|{string2}',
|
||||
'string1': r'"(\\\"|[^\"])*"',
|
||||
'string2': r"'(\\\'|[^\'])*'",
|
||||
'nl': r'\n|\r\n|\r|\f',
|
||||
'w': r'\s*',
|
||||
|
||||
'integer': r'{int}',
|
||||
'length': r'0|{num}(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',
|
||||
'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}\)',
|
||||
'uri': r'url\({w}({string}|(\\\)|[^\)])+){w}\)',
|
||||
'percentage': r'{num}%',
|
||||
'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset',
|
||||
'border-color': '{color}',
|
||||
'border-width': '{length}|thin|medium|thick',
|
||||
|
||||
'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-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}{identifier}{w}(?:,{w}{list-style-type}{w})?\)',
|
||||
'identifier': r'{ident}',
|
||||
'family-name': r'{string}|{identifier}',
|
||||
'generic-family': r'serif|sans-serif|cursive|fantasy|monospace',
|
||||
'absolute-size': r'(x?x-)?(small|large)|medium',
|
||||
'relative-size': r'smaller|larger',
|
||||
'font-family': r'(({family-name}|{generic-family}){w},{w})*({family-name}|{generic-family})|inherit',
|
||||
'font-size': r'{absolute-size}|{relative-size}|{length}|{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',
|
||||
'outline-color': r'{color}|invert|inherit',
|
||||
'outline-style': r'{border-style}|inherit',
|
||||
'outline-width': r'{border-width}|inherit',
|
||||
'padding-width': r'{length}|{percentage}',
|
||||
'specific-voice': r'{identifier}',
|
||||
'generic-voice': r'male|female|child',
|
||||
'content': r'{string}|{uri}|{counter}|attr\({w}{identifier}{w}\)|open-quote|close-quote|no-open-quote|no-close-quote',
|
||||
'border-attrs': r'{border-width}|{border-style}|{border-color}',
|
||||
'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}',
|
||||
'outline-attrs': r'{outline-color}|{outline-style}|{outline-width}',
|
||||
'text-attrs': r'underline|overline|line-through|blink',
|
||||
}
|
||||
|
||||
"""
|
||||
Define the regular expressions for validation all CSS values
|
||||
"""
|
||||
cssvalues = {
|
||||
'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-color': r'({border-color}|transparent)(\s+({border-color}|transparent)){0,3}|inherit',
|
||||
'border-spacing': r'{length}(\s+{length})?|inherit',
|
||||
'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit',
|
||||
'border-top': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-right': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-bottom': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-left': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-top-color': r'{border-color}|transparent|inherit',
|
||||
'border-right-color': r'{border-color}|transparent|inherit',
|
||||
'border-bottom-color': r'{border-color}|transparent|inherit',
|
||||
'border-left-color': r'{border-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}(\s+{border-attrs})*|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'normal|{content}(\s+{content})*|inherit',
|
||||
'counter-increment': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
|
||||
'counter-reset': r'({identifier}(\s+{integer})?)(\s+({identifier}(\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',
|
||||
'font-family': r'{font-family}',
|
||||
'font-size': r'{font-size}',
|
||||
'font-style': r'{font-style}',
|
||||
'font-variant': r'{font-variant}',
|
||||
'font-weight': r'{font-weight}',
|
||||
'font': r'({font-attrs}\s+)*{font-size}({w}/{w}{line-height})?\s+{font-family}|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',
|
||||
'outline-color': r'{outline-color}',
|
||||
'outline-style': r'{outline-style}',
|
||||
'outline-width': r'{outline-width}',
|
||||
'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit',
|
||||
'overflow': r'visible|hidden|scroll|auto|inherit',
|
||||
'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',
|
||||
}
|
||||
|
||||
def _expand_macros(tokdict):
|
||||
""" Expand macros in token dictionary """
|
||||
def macro_value(m):
|
||||
return '(?:%s)' % MACROS[m.groupdict()['macro']]
|
||||
for key, value in tokdict.items():
|
||||
while re.search(r'{[a-z][a-z0-9-]*}', value):
|
||||
value = re.sub(r'{(?P<macro>[a-z][a-z0-9-]*)}',
|
||||
macro_value, value)
|
||||
tokdict[key] = value
|
||||
return tokdict
|
||||
|
||||
def _compile_regexes(tokdict):
|
||||
""" Compile all regular expressions into callable objects """
|
||||
for key, value in tokdict.items():
|
||||
tokdict[key] = re.compile('^(?:%s)$' % value, re.I).match
|
||||
return tokdict
|
||||
|
||||
_compile_regexes(_expand_macros(cssvalues))
|
||||
|
||||
|
||||
# functions to convert between CSS and DOM name
|
||||
|
||||
_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):
|
||||
"""
|
||||
returns 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)
|
||||
|
||||
|
||||
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
|
||||
|
||||
# add list of DOMname properties to CSS2Properties
|
||||
# used for CSSStyleDeclaration to check if allowed properties
|
||||
# but somehow doubled, any better way?
|
||||
CSS2Properties._properties = [_toDOMname(p) for p in cssvalues.keys()]
|
||||
|
||||
# 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)))
|
@ -1,134 +1,134 @@
|
||||
"""CSSRule implements DOM Level 2 CSS CSSRule."""
|
||||
__all__ = ['CSSRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssrule.py 1177 2008-03-20 17:47:23Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssutils
|
||||
|
||||
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
|
||||
CSSUnknownRule interface.
|
||||
|
||||
Properties
|
||||
==========
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of the rule. This reflects the
|
||||
current state of the rule and not its initial value.
|
||||
parentRule: of type CSSRule, readonly
|
||||
If this rule is contained inside another rule (e.g. a style rule
|
||||
inside an @media block), this is the containing rule. If this rule
|
||||
is not nested inside any other rules, this returns None.
|
||||
parentStyleSheet: of type CSSStyleSheet, readonly
|
||||
The style sheet that contains this rule.
|
||||
type: of type unsigned short, readonly
|
||||
The type of the rule, as defined above. The expectation is that
|
||||
binding-specific casting methods can be used to cast down from an
|
||||
instance of the CSSRule interface to the specific derived interface
|
||||
implied by the type.
|
||||
|
||||
cssutils only
|
||||
-------------
|
||||
seq (READONLY):
|
||||
contains sequence of parts of the rule including comments but
|
||||
excluding @KEYWORD and braces
|
||||
typeString: string
|
||||
A string name of the type of this rule, e.g. 'STYLE_RULE'. Mainly
|
||||
useful for debugging
|
||||
wellformed:
|
||||
if a rule is valid
|
||||
"""
|
||||
|
||||
"""
|
||||
CSSRule type constants.
|
||||
An integer indicating which type of rule this is.
|
||||
"""
|
||||
COMMENT = -1 # cssutils only
|
||||
UNKNOWN_RULE = 0 #u
|
||||
STYLE_RULE = 1 #s
|
||||
CHARSET_RULE = 2 #c
|
||||
IMPORT_RULE = 3 #i
|
||||
MEDIA_RULE = 4 #m
|
||||
FONT_FACE_RULE = 5 #f
|
||||
PAGE_RULE = 6 #p
|
||||
NAMESPACE_RULE = 7 # CSSOM
|
||||
|
||||
_typestrings = ['UNKNOWN_RULE', 'STYLE_RULE', 'CHARSET_RULE', 'IMPORT_RULE',
|
||||
'MEDIA_RULE', 'FONT_FACE_RULE', 'PAGE_RULE', 'NAMESPACE_RULE',
|
||||
'COMMENT']
|
||||
|
||||
type = UNKNOWN_RULE
|
||||
"""
|
||||
The type of this rule, as defined by a CSSRule type constant.
|
||||
Overwritten in derived classes.
|
||||
|
||||
The expectation is that binding-specific casting methods can be used to
|
||||
cast down from an instance of the CSSRule interface to the specific
|
||||
derived interface implied by the type.
|
||||
(Casting not for this Python implementation I guess...)
|
||||
"""
|
||||
|
||||
def __init__(self, parentRule=None, parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
set common attributes for all rules
|
||||
"""
|
||||
super(CSSRule, self).__init__()
|
||||
self._parentRule = parentRule
|
||||
self._parentStyleSheet = parentStyleSheet
|
||||
self._setSeq(self._tempSeq())
|
||||
# must be set after initialization of #inheriting rule is done
|
||||
self._readonly = False
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- INVALID_MODIFICATION_ERR:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- HIERARCHY_REQUEST_ERR:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
cssText = property(lambda self: u'', _setCssText,
|
||||
doc="""(DOM) The parsable textual representation of the rule. This
|
||||
reflects the current state of the rule and not its initial value.
|
||||
The initial value is saved, but this may be removed in a future
|
||||
version!
|
||||
MUST BE OVERWRITTEN IN SUBCLASS TO WORK!""")
|
||||
|
||||
def _setAtkeyword(self, akw):
|
||||
"""checks if new keyword is normalized same as old"""
|
||||
if not self.atkeyword or (self._normalize(akw) ==
|
||||
self._normalize(self.atkeyword)):
|
||||
self._atkeyword = akw
|
||||
else:
|
||||
self._log.error(u'%s: Invalid atkeyword for this rule: %r' %
|
||||
(self._normalize(self.atkeyword), akw),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
|
||||
atkeyword = property(lambda self: self._atkeyword, _setAtkeyword,
|
||||
doc=u"@keyword for @rules")
|
||||
|
||||
parentRule = property(lambda self: self._parentRule,
|
||||
doc=u"READONLY")
|
||||
|
||||
parentStyleSheet = property(lambda self: self._parentStyleSheet,
|
||||
doc=u"READONLY")
|
||||
|
||||
wellformed = property(lambda self: False,
|
||||
doc=u"READONLY")
|
||||
|
||||
typeString = property(lambda self: CSSRule._typestrings[self.type],
|
||||
doc="Name of this rules type.")
|
||||
"""CSSRule implements DOM Level 2 CSS CSSRule."""
|
||||
__all__ = ['CSSRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssrule.py 1177 2008-03-20 17:47:23Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssutils
|
||||
|
||||
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
|
||||
CSSUnknownRule interface.
|
||||
|
||||
Properties
|
||||
==========
|
||||
cssText: of type DOMString
|
||||
The parsable textual representation of the rule. This reflects the
|
||||
current state of the rule and not its initial value.
|
||||
parentRule: of type CSSRule, readonly
|
||||
If this rule is contained inside another rule (e.g. a style rule
|
||||
inside an @media block), this is the containing rule. If this rule
|
||||
is not nested inside any other rules, this returns None.
|
||||
parentStyleSheet: of type CSSStyleSheet, readonly
|
||||
The style sheet that contains this rule.
|
||||
type: of type unsigned short, readonly
|
||||
The type of the rule, as defined above. The expectation is that
|
||||
binding-specific casting methods can be used to cast down from an
|
||||
instance of the CSSRule interface to the specific derived interface
|
||||
implied by the type.
|
||||
|
||||
cssutils only
|
||||
-------------
|
||||
seq (READONLY):
|
||||
contains sequence of parts of the rule including comments but
|
||||
excluding @KEYWORD and braces
|
||||
typeString: string
|
||||
A string name of the type of this rule, e.g. 'STYLE_RULE'. Mainly
|
||||
useful for debugging
|
||||
wellformed:
|
||||
if a rule is valid
|
||||
"""
|
||||
|
||||
"""
|
||||
CSSRule type constants.
|
||||
An integer indicating which type of rule this is.
|
||||
"""
|
||||
COMMENT = -1 # cssutils only
|
||||
UNKNOWN_RULE = 0 #u
|
||||
STYLE_RULE = 1 #s
|
||||
CHARSET_RULE = 2 #c
|
||||
IMPORT_RULE = 3 #i
|
||||
MEDIA_RULE = 4 #m
|
||||
FONT_FACE_RULE = 5 #f
|
||||
PAGE_RULE = 6 #p
|
||||
NAMESPACE_RULE = 7 # CSSOM
|
||||
|
||||
_typestrings = ['UNKNOWN_RULE', 'STYLE_RULE', 'CHARSET_RULE', 'IMPORT_RULE',
|
||||
'MEDIA_RULE', 'FONT_FACE_RULE', 'PAGE_RULE', 'NAMESPACE_RULE',
|
||||
'COMMENT']
|
||||
|
||||
type = UNKNOWN_RULE
|
||||
"""
|
||||
The type of this rule, as defined by a CSSRule type constant.
|
||||
Overwritten in derived classes.
|
||||
|
||||
The expectation is that binding-specific casting methods can be used to
|
||||
cast down from an instance of the CSSRule interface to the specific
|
||||
derived interface implied by the type.
|
||||
(Casting not for this Python implementation I guess...)
|
||||
"""
|
||||
|
||||
def __init__(self, parentRule=None, parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
set common attributes for all rules
|
||||
"""
|
||||
super(CSSRule, self).__init__()
|
||||
self._parentRule = parentRule
|
||||
self._parentStyleSheet = parentStyleSheet
|
||||
self._setSeq(self._tempSeq())
|
||||
# must be set after initialization of #inheriting rule is done
|
||||
self._readonly = False
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- INVALID_MODIFICATION_ERR:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- HIERARCHY_REQUEST_ERR:
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if the rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
|
||||
cssText = property(lambda self: u'', _setCssText,
|
||||
doc="""(DOM) The parsable textual representation of the rule. This
|
||||
reflects the current state of the rule and not its initial value.
|
||||
The initial value is saved, but this may be removed in a future
|
||||
version!
|
||||
MUST BE OVERWRITTEN IN SUBCLASS TO WORK!""")
|
||||
|
||||
def _setAtkeyword(self, akw):
|
||||
"""checks if new keyword is normalized same as old"""
|
||||
if not self.atkeyword or (self._normalize(akw) ==
|
||||
self._normalize(self.atkeyword)):
|
||||
self._atkeyword = akw
|
||||
else:
|
||||
self._log.error(u'%s: Invalid atkeyword for this rule: %r' %
|
||||
(self._normalize(self.atkeyword), akw),
|
||||
error=xml.dom.InvalidModificationErr)
|
||||
|
||||
atkeyword = property(lambda self: self._atkeyword, _setAtkeyword,
|
||||
doc=u"@keyword for @rules")
|
||||
|
||||
parentRule = property(lambda self: self._parentRule,
|
||||
doc=u"READONLY")
|
||||
|
||||
parentStyleSheet = property(lambda self: self._parentStyleSheet,
|
||||
doc=u"READONLY")
|
||||
|
||||
wellformed = property(lambda self: False,
|
||||
doc=u"READONLY")
|
||||
|
||||
typeString = property(lambda self: CSSRule._typestrings[self.type],
|
||||
doc="Name of this rules type.")
|
@ -1,60 +1,60 @@
|
||||
"""
|
||||
CSSRuleList implements DOM Level 2 CSS CSSRuleList.
|
||||
|
||||
Partly also
|
||||
* http://dev.w3.org/csswg/cssom/#the-cssrulelist
|
||||
"""
|
||||
__all__ = ['CSSRuleList']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssrulelist.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
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!
|
||||
|
||||
Properties
|
||||
==========
|
||||
length: of type unsigned long, readonly
|
||||
The number of CSSRules in the list. The range of valid child rule
|
||||
indices is 0 to length-1 inclusive.
|
||||
"""
|
||||
def __init__(self, *ignored):
|
||||
"nothing is set as this must also be defined later"
|
||||
pass
|
||||
|
||||
def __notimplemented(self, *ignored):
|
||||
"no direct setting possible"
|
||||
raise NotImplementedError(
|
||||
'Must be implemented by class using an instance of this class.')
|
||||
|
||||
append = extend = __setitem__ = __setslice__ = __notimplemented
|
||||
|
||||
def item(self, index):
|
||||
"""
|
||||
(DOM)
|
||||
Used to 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="(DOM) The number of CSSRules in the list.")
|
||||
|
||||
"""
|
||||
CSSRuleList implements DOM Level 2 CSS CSSRuleList.
|
||||
|
||||
Partly also
|
||||
* http://dev.w3.org/csswg/cssom/#the-cssrulelist
|
||||
"""
|
||||
__all__ = ['CSSRuleList']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssrulelist.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
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!
|
||||
|
||||
Properties
|
||||
==========
|
||||
length: of type unsigned long, readonly
|
||||
The number of CSSRules in the list. The range of valid child rule
|
||||
indices is 0 to length-1 inclusive.
|
||||
"""
|
||||
def __init__(self, *ignored):
|
||||
"nothing is set as this must also be defined later"
|
||||
pass
|
||||
|
||||
def __notimplemented(self, *ignored):
|
||||
"no direct setting possible"
|
||||
raise NotImplementedError(
|
||||
'Must be implemented by class using an instance of this class.')
|
||||
|
||||
append = extend = __setitem__ = __setslice__ = __notimplemented
|
||||
|
||||
def item(self, index):
|
||||
"""
|
||||
(DOM)
|
||||
Used to 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="(DOM) The number of CSSRules in the list.")
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,242 +1,242 @@
|
||||
"""CSSStyleRule implements DOM Level 2 CSS CSSStyleRule.
|
||||
"""
|
||||
__all__ = ['CSSStyleRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssstylerule.py 1284 2008-06-05 16:29:17Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
from selectorlist import SelectorList
|
||||
from cssstyledeclaration import CSSStyleDeclaration
|
||||
|
||||
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.
|
||||
|
||||
Properties
|
||||
==========
|
||||
selectorList: of type SelectorList (cssutils only)
|
||||
A list of all Selector elements for the rule set.
|
||||
selectorText: of type DOMString
|
||||
The textual representation of the selector for the rule set. The
|
||||
implementation may have stripped out insignificant whitespace while
|
||||
parsing the selector.
|
||||
style: of type CSSStyleDeclaration, (DOM)
|
||||
The declaration-block of this rule set.
|
||||
type
|
||||
the type of this rule, constant cssutils.CSSRule.STYLE_RULE
|
||||
|
||||
inherited properties:
|
||||
- cssText
|
||||
- parentRule
|
||||
- parentStyleSheet
|
||||
|
||||
Format
|
||||
======
|
||||
ruleset::
|
||||
|
||||
: selector [ COMMA S* selector ]*
|
||||
LBRACE S* declaration [ ';' S* declaration ]* '}' S*
|
||||
;
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.STYLE_RULE)
|
||||
|
||||
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(parentRule=self)
|
||||
self._style = CSSStyleDeclaration(parentRule=self)
|
||||
if selectorText:
|
||||
self.selectorText = selectorText
|
||||
if style:
|
||||
self.style = style
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
|
||||
def _getCssText(self):
|
||||
"""
|
||||
returns 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:
|
||||
- `NAMESPACE_ERR`: (Selector)
|
||||
Raised if the specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- `SYNTAX_ERR`: (self, StyleDeclaration, etc)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- `INVALID_MODIFICATION_ERR`: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- `HIERARCHY_REQUEST_ERR`: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule)
|
||||
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:
|
||||
wellformed = True
|
||||
|
||||
bracetoken = selectortokens.pop()
|
||||
if self._tokenvalue(bracetoken) != u'{':
|
||||
wellformed = False
|
||||
self._log.error(
|
||||
u'CSSStyleRule: No start { of style declaration found: %r' %
|
||||
self._valuestr(cssText), bracetoken)
|
||||
elif not selectortokens:
|
||||
wellformed = False
|
||||
self._log.error(u'CSSStyleRule: No selector found: %r.' %
|
||||
self._valuestr(cssText), bracetoken)
|
||||
newselectorlist = SelectorList(selectorText=(selectortokens,
|
||||
namespaces),
|
||||
parentRule=self)
|
||||
|
||||
newstyle = CSSStyleDeclaration()
|
||||
if not styletokens:
|
||||
wellformed = 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':
|
||||
wellformed = False
|
||||
self._log.error(
|
||||
u'CSSStyleRule: No "}" after style declaration found: %r' %
|
||||
self._valuestr(cssText))
|
||||
else:
|
||||
if 'EOF' == typ:
|
||||
# add again as style needs it
|
||||
styletokens.append(braceorEOFtoken)
|
||||
newstyle.cssText = styletokens
|
||||
|
||||
if wellformed:
|
||||
self._selectorList = newselectorlist
|
||||
self.style = newstyle
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc="(DOM) The parsable textual representation of the 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 the namespaces of that sheet are mirrored
|
||||
here. While the Rule is not attached the namespaces of selectorList
|
||||
are used.""")
|
||||
|
||||
def _setSelectorList(self, selectorList):
|
||||
"""
|
||||
:param selectorList: selectorList, only content is used, not the actual
|
||||
object
|
||||
"""
|
||||
self._checkReadonly()
|
||||
self.selectorText = selectorList.selectorText
|
||||
|
||||
selectorList = property(lambda self: self._selectorList, _setSelectorList,
|
||||
doc="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:
|
||||
- `NAMESPACE_ERR`: (Selector)
|
||||
Raised if the specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- `SYNTAX_ERR`: (SelectorList, Selector)
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
and is unparsable.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (self)
|
||||
Raised if this rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
self._selectorList.selectorText = selectorText
|
||||
|
||||
selectorText = property(lambda self: self._selectorList.selectorText,
|
||||
_setSelectorText,
|
||||
doc="""(DOM) The textual representation of the selector for the
|
||||
rule set.""")
|
||||
|
||||
def _setStyle(self, style):
|
||||
"""
|
||||
:param style: CSSStyleDeclaration or string, only the cssText of a
|
||||
declaration is used, not the actual object
|
||||
"""
|
||||
self._checkReadonly()
|
||||
if isinstance(style, basestring):
|
||||
self._style.cssText = style
|
||||
else:
|
||||
# cssText would be serialized with optional preferences
|
||||
# so use _seq!
|
||||
self._style._seq = style._seq
|
||||
|
||||
style = property(lambda self: self._style, _setStyle,
|
||||
doc="(DOM) The declaration-block of this rule set.")
|
||||
|
||||
wellformed = property(lambda self: self.selectorList.wellformed)
|
||||
|
||||
def __repr__(self):
|
||||
if self._namespaces:
|
||||
st = (self.selectorText, self._namespaces)
|
||||
else:
|
||||
st = self.selectorText
|
||||
return "cssutils.css.%s(selectorText=%r, style=%r)" % (
|
||||
self.__class__.__name__, st, self.style.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object selector=%r style=%r _namespaces=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.selectorText, self.style.cssText,
|
||||
self._namespaces, id(self))
|
||||
"""CSSStyleRule implements DOM Level 2 CSS CSSStyleRule.
|
||||
"""
|
||||
__all__ = ['CSSStyleRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssstylerule.py 1284 2008-06-05 16:29:17Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
from selectorlist import SelectorList
|
||||
from cssstyledeclaration import CSSStyleDeclaration
|
||||
|
||||
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.
|
||||
|
||||
Properties
|
||||
==========
|
||||
selectorList: of type SelectorList (cssutils only)
|
||||
A list of all Selector elements for the rule set.
|
||||
selectorText: of type DOMString
|
||||
The textual representation of the selector for the rule set. The
|
||||
implementation may have stripped out insignificant whitespace while
|
||||
parsing the selector.
|
||||
style: of type CSSStyleDeclaration, (DOM)
|
||||
The declaration-block of this rule set.
|
||||
type
|
||||
the type of this rule, constant cssutils.CSSRule.STYLE_RULE
|
||||
|
||||
inherited properties:
|
||||
- cssText
|
||||
- parentRule
|
||||
- parentStyleSheet
|
||||
|
||||
Format
|
||||
======
|
||||
ruleset::
|
||||
|
||||
: selector [ COMMA S* selector ]*
|
||||
LBRACE S* declaration [ ';' S* declaration ]* '}' S*
|
||||
;
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.STYLE_RULE)
|
||||
|
||||
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(parentRule=self)
|
||||
self._style = CSSStyleDeclaration(parentRule=self)
|
||||
if selectorText:
|
||||
self.selectorText = selectorText
|
||||
if style:
|
||||
self.style = style
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
|
||||
def _getCssText(self):
|
||||
"""
|
||||
returns 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:
|
||||
- `NAMESPACE_ERR`: (Selector)
|
||||
Raised if the specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- `SYNTAX_ERR`: (self, StyleDeclaration, etc)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- `INVALID_MODIFICATION_ERR`: (self)
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- `HIERARCHY_REQUEST_ERR`: (CSSStylesheet)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule)
|
||||
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:
|
||||
wellformed = True
|
||||
|
||||
bracetoken = selectortokens.pop()
|
||||
if self._tokenvalue(bracetoken) != u'{':
|
||||
wellformed = False
|
||||
self._log.error(
|
||||
u'CSSStyleRule: No start { of style declaration found: %r' %
|
||||
self._valuestr(cssText), bracetoken)
|
||||
elif not selectortokens:
|
||||
wellformed = False
|
||||
self._log.error(u'CSSStyleRule: No selector found: %r.' %
|
||||
self._valuestr(cssText), bracetoken)
|
||||
newselectorlist = SelectorList(selectorText=(selectortokens,
|
||||
namespaces),
|
||||
parentRule=self)
|
||||
|
||||
newstyle = CSSStyleDeclaration()
|
||||
if not styletokens:
|
||||
wellformed = 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':
|
||||
wellformed = False
|
||||
self._log.error(
|
||||
u'CSSStyleRule: No "}" after style declaration found: %r' %
|
||||
self._valuestr(cssText))
|
||||
else:
|
||||
if 'EOF' == typ:
|
||||
# add again as style needs it
|
||||
styletokens.append(braceorEOFtoken)
|
||||
newstyle.cssText = styletokens
|
||||
|
||||
if wellformed:
|
||||
self._selectorList = newselectorlist
|
||||
self.style = newstyle
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc="(DOM) The parsable textual representation of the 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 the namespaces of that sheet are mirrored
|
||||
here. While the Rule is not attached the namespaces of selectorList
|
||||
are used.""")
|
||||
|
||||
def _setSelectorList(self, selectorList):
|
||||
"""
|
||||
:param selectorList: selectorList, only content is used, not the actual
|
||||
object
|
||||
"""
|
||||
self._checkReadonly()
|
||||
self.selectorText = selectorList.selectorText
|
||||
|
||||
selectorList = property(lambda self: self._selectorList, _setSelectorList,
|
||||
doc="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:
|
||||
- `NAMESPACE_ERR`: (Selector)
|
||||
Raised if the specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- `SYNTAX_ERR`: (SelectorList, Selector)
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
and is unparsable.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (self)
|
||||
Raised if this rule is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
self._selectorList.selectorText = selectorText
|
||||
|
||||
selectorText = property(lambda self: self._selectorList.selectorText,
|
||||
_setSelectorText,
|
||||
doc="""(DOM) The textual representation of the selector for the
|
||||
rule set.""")
|
||||
|
||||
def _setStyle(self, style):
|
||||
"""
|
||||
:param style: CSSStyleDeclaration or string, only the cssText of a
|
||||
declaration is used, not the actual object
|
||||
"""
|
||||
self._checkReadonly()
|
||||
if isinstance(style, basestring):
|
||||
self._style.cssText = style
|
||||
else:
|
||||
# cssText would be serialized with optional preferences
|
||||
# so use _seq!
|
||||
self._style._seq = style._seq
|
||||
|
||||
style = property(lambda self: self._style, _setStyle,
|
||||
doc="(DOM) The declaration-block of this rule set.")
|
||||
|
||||
wellformed = property(lambda self: self.selectorList.wellformed)
|
||||
|
||||
def __repr__(self):
|
||||
if self._namespaces:
|
||||
st = (self.selectorText, self._namespaces)
|
||||
else:
|
||||
st = self.selectorText
|
||||
return "cssutils.css.%s(selectorText=%r, style=%r)" % (
|
||||
self.__class__.__name__, st, self.style.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object selector=%r style=%r _namespaces=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.selectorText, self.style.cssText,
|
||||
self._namespaces, id(self))
|
File diff suppressed because it is too large
Load Diff
@ -1,208 +1,208 @@
|
||||
"""CSSUnknownRule implements DOM Level 2 CSS CSSUnknownRule.
|
||||
"""
|
||||
__all__ = ['CSSUnknownRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssunknownrule.py 1170 2008-03-20 17:42:07Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
|
||||
class CSSUnknownRule(cssrule.CSSRule):
|
||||
"""
|
||||
represents an at-rule not supported by this user agent.
|
||||
|
||||
Properties
|
||||
==========
|
||||
inherited from CSSRule
|
||||
- cssText
|
||||
- type
|
||||
|
||||
cssutils only
|
||||
-------------
|
||||
atkeyword
|
||||
the literal keyword used
|
||||
seq
|
||||
All parts of this rule excluding @KEYWORD but including CSSComments
|
||||
wellformed
|
||||
if this Rule is wellformed, for Unknown rules if an atkeyword is set
|
||||
at all
|
||||
|
||||
Format
|
||||
======
|
||||
unknownrule:
|
||||
@xxx until ';' or block {...}
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.UNKNOWN_RULE)
|
||||
|
||||
def __init__(self, cssText=u'', parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
cssText
|
||||
of type string
|
||||
"""
|
||||
super(CSSUnknownRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = None
|
||||
if cssText:
|
||||
self.cssText = cssText
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def _getCssText(self):
|
||||
""" returns serialized property cssText """
|
||||
return cssutils.ser.do_CSSUnknownRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- INVALID_MODIFICATION_ERR:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- HIERARCHY_REQUEST_ERR: (never raised)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
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 {, [ 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 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,
|
||||
'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: %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="(DOM) The parsable textual representation.")
|
||||
|
||||
wellformed = property(lambda self: bool(self.atkeyword))
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(cssText=%r)" % (
|
||||
self.__class__.__name__, self.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object cssText=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.cssText, id(self))
|
||||
"""CSSUnknownRule implements DOM Level 2 CSS CSSUnknownRule.
|
||||
"""
|
||||
__all__ = ['CSSUnknownRule']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssunknownrule.py 1170 2008-03-20 17:42:07Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssrule
|
||||
import cssutils
|
||||
|
||||
class CSSUnknownRule(cssrule.CSSRule):
|
||||
"""
|
||||
represents an at-rule not supported by this user agent.
|
||||
|
||||
Properties
|
||||
==========
|
||||
inherited from CSSRule
|
||||
- cssText
|
||||
- type
|
||||
|
||||
cssutils only
|
||||
-------------
|
||||
atkeyword
|
||||
the literal keyword used
|
||||
seq
|
||||
All parts of this rule excluding @KEYWORD but including CSSComments
|
||||
wellformed
|
||||
if this Rule is wellformed, for Unknown rules if an atkeyword is set
|
||||
at all
|
||||
|
||||
Format
|
||||
======
|
||||
unknownrule:
|
||||
@xxx until ';' or block {...}
|
||||
"""
|
||||
type = property(lambda self: cssrule.CSSRule.UNKNOWN_RULE)
|
||||
|
||||
def __init__(self, cssText=u'', parentRule=None,
|
||||
parentStyleSheet=None, readonly=False):
|
||||
"""
|
||||
cssText
|
||||
of type string
|
||||
"""
|
||||
super(CSSUnknownRule, self).__init__(parentRule=parentRule,
|
||||
parentStyleSheet=parentStyleSheet)
|
||||
self._atkeyword = None
|
||||
if cssText:
|
||||
self.cssText = cssText
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def _getCssText(self):
|
||||
""" returns serialized property cssText """
|
||||
return cssutils.ser.do_CSSUnknownRule(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR:
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
- INVALID_MODIFICATION_ERR:
|
||||
Raised if the specified CSS string value represents a different
|
||||
type of rule than the current one.
|
||||
- HIERARCHY_REQUEST_ERR: (never raised)
|
||||
Raised if the rule cannot be inserted at this point in the
|
||||
style sheet.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
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 {, [ 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 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,
|
||||
'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: %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="(DOM) The parsable textual representation.")
|
||||
|
||||
wellformed = property(lambda self: bool(self.atkeyword))
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(cssText=%r)" % (
|
||||
self.__class__.__name__, self.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object cssText=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.cssText, id(self))
|
File diff suppressed because it is too large
Load Diff
@ -1,414 +1,415 @@
|
||||
"""Property is a single CSS property in a CSSStyleDeclaration
|
||||
|
||||
Internal use only, may be removed in the future!
|
||||
"""
|
||||
__all__ = ['Property']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: property.py 1305 2008-06-22 18:42:51Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssutils
|
||||
import cssproperties
|
||||
from cssvalue import CSSValue
|
||||
from cssutils.helper import Deprecated
|
||||
|
||||
class Property(cssutils.util.Base):
|
||||
"""
|
||||
(cssutils) a CSS property in a StyleDeclaration of a CSSStyleRule
|
||||
|
||||
Properties
|
||||
==========
|
||||
cssText
|
||||
a parsable textual representation of this property
|
||||
name
|
||||
normalized name of the property, e.g. "color" when name is "c\olor"
|
||||
(since 0.9.5)
|
||||
literalname (since 0.9.5)
|
||||
original name of the property in the source CSS which is not normalized
|
||||
e.g. "C\\OLor"
|
||||
cssValue
|
||||
the relevant CSSValue instance for this property
|
||||
value
|
||||
the string value of the property, same as cssValue.cssText
|
||||
priority
|
||||
of the property (currently only u"important" or None)
|
||||
literalpriority
|
||||
original priority of the property in the source CSS which is not
|
||||
normalized e.g. "IM\portant"
|
||||
seqs
|
||||
combination of a list for seq of name, a CSSValue object, and
|
||||
a list for seq of priority (empty or [!important] currently)
|
||||
valid
|
||||
if this Property is valid
|
||||
wellformed
|
||||
if this Property is syntactically ok
|
||||
|
||||
DEPRECATED normalname (since 0.9.5)
|
||||
normalized name of the property, e.g. "color" when name is "c\olor"
|
||||
|
||||
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):
|
||||
"""
|
||||
inits property
|
||||
|
||||
name
|
||||
a property name string (will be normalized)
|
||||
value
|
||||
a property value string
|
||||
priority
|
||||
an optional priority string which currently must be u'',
|
||||
u'!important' or u'important'
|
||||
_mediaQuery boolean
|
||||
if True value is optional as used by MediaQuery objects
|
||||
"""
|
||||
super(Property, self).__init__()
|
||||
|
||||
self.seqs = [[], None, []]
|
||||
self.valid = False
|
||||
self.wellformed = False
|
||||
self._mediaQuery = _mediaQuery
|
||||
|
||||
if name:
|
||||
self.name = name
|
||||
else:
|
||||
self._name = u''
|
||||
self._literalname = u''
|
||||
self.__normalname = u'' # DEPRECATED
|
||||
|
||||
if value:
|
||||
self.cssValue = value
|
||||
else:
|
||||
self.seqs[1] = CSSValue()
|
||||
|
||||
if priority:
|
||||
self.priority = priority
|
||||
else:
|
||||
self._priority = u''
|
||||
self._literalpriority = u''
|
||||
|
||||
def _getCssText(self):
|
||||
"""
|
||||
returns serialized property cssText
|
||||
"""
|
||||
return cssutils.ser.do_Property(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
Raised if the rule is readonly.
|
||||
- SYNTAX_ERR: (self)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
"""
|
||||
# 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.cssValue = 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: %r' %
|
||||
self._valuestr(cssText), colontoken)
|
||||
elif not nametokens:
|
||||
wellformed = False
|
||||
self._log.error(u'Property: No property name found: %r.' %
|
||||
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: %r.' %
|
||||
self._valuestr(cssText), colontoken)
|
||||
|
||||
if wellformed:
|
||||
self.wellformed = True
|
||||
self.name = nametokens
|
||||
self.cssValue = valuetokens
|
||||
self.priority = prioritytokens
|
||||
|
||||
else:
|
||||
self._log.error(u'Property: No property name found: %r.' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
cssText = property(fget=_getCssText, fset=_setCssText,
|
||||
doc="A parsable textual representation.")
|
||||
|
||||
def _setName(self, name):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR: (self)
|
||||
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]
|
||||
else:
|
||||
token = None
|
||||
|
||||
if not new['literalname']:
|
||||
wellformed = False
|
||||
self._log.error(u'Property: No name found: %r' %
|
||||
self._valuestr(name), token=token)
|
||||
|
||||
if wellformed:
|
||||
self.wellformed = True
|
||||
self._literalname = new['literalname']
|
||||
self._name = self._normalize(self._literalname)
|
||||
self.__normalname = self._name # DEPRECATED
|
||||
self.seqs[0] = newseq
|
||||
|
||||
# validate
|
||||
if self._name not in cssproperties.cssvalues:
|
||||
self.valid = False
|
||||
tokenizer=self._tokenize2(name)
|
||||
self._log.info(u'Property: No CSS2 Property: %r.' %
|
||||
new['literalname'], token=token, neverraise=True)
|
||||
else:
|
||||
self.valid = True
|
||||
if self.cssValue:
|
||||
self.cssValue._propertyName = self._name
|
||||
self.valid = self.cssValue.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 _getCSSValue(self):
|
||||
return self.seqs[1]
|
||||
|
||||
def _setCSSValue(self, cssText):
|
||||
"""
|
||||
see css.CSSValue
|
||||
|
||||
DOMException on setting?
|
||||
|
||||
- SYNTAX_ERR: (self)
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
(according to the attached property) or is unparsable.
|
||||
- TODO: INVALID_MODIFICATION_ERR:
|
||||
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] = CSSValue()
|
||||
else:
|
||||
if not self.seqs[1]:
|
||||
self.seqs[1] = CSSValue()
|
||||
|
||||
cssvalue = self.seqs[1]
|
||||
cssvalue._propertyName = self.name
|
||||
cssvalue.cssText = cssText
|
||||
if cssvalue._value and cssvalue.wellformed:
|
||||
self.seqs[1] = cssvalue
|
||||
self.valid = self.valid and cssvalue.valid
|
||||
self.wellformed = self.wellformed and cssvalue.wellformed
|
||||
|
||||
cssValue = property(_getCSSValue, _setCSSValue,
|
||||
doc="(cssutils) CSSValue object of this property")
|
||||
|
||||
def _getValue(self):
|
||||
if self.cssValue:
|
||||
return self.cssValue._value
|
||||
else:
|
||||
return u''
|
||||
|
||||
def _setValue(self, value):
|
||||
self.cssValue.cssText = value
|
||||
self.valid = self.valid and self.cssValue.valid
|
||||
self.wellformed = self.wellformed and self.cssValue.wellformed
|
||||
|
||||
value = property(_getValue, _setValue,
|
||||
doc="The textual value of this Properties cssValue.")
|
||||
|
||||
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;}
|
||||
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR: (self)
|
||||
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 - 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)
|
||||
normalval = self._tokenvalue(token, normalize=True)
|
||||
if 'important' == expected == normalval:
|
||||
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: %r.' %
|
||||
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
|
||||
if self._priority not in (u'', u'important'):
|
||||
self.valid = False
|
||||
self._log.info(u'Property: No CSS2 priority value: %r.' %
|
||||
self._priority, neverraise=True)
|
||||
|
||||
priority = property(lambda self: self._priority, _setPriority,
|
||||
doc="(cssutils) Priority of this property")
|
||||
|
||||
literalpriority = property(lambda self: self._literalpriority,
|
||||
doc="Readonly literal (not normalized) priority of this property")
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(name=%r, value=%r, priority=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.literalname, self.cssValue.cssText, self.priority)
|
||||
|
||||
def __str__(self):
|
||||
return "<%s.%s object name=%r value=%r priority=%r at 0x%x>" % (
|
||||
self.__class__.__module__, self.__class__.__name__,
|
||||
self.name, self.cssValue.cssText, self.priority, id(self))
|
||||
|
||||
@Deprecated(u'Use property ``name`` instead (since cssutils 0.9.5).')
|
||||
def _getNormalname(self):
|
||||
return self.__normalname
|
||||
normalname = property(_getNormalname,
|
||||
"""Property is a single CSS property in a CSSStyleDeclaration
|
||||
|
||||
Internal use only, may be removed in the future!
|
||||
"""
|
||||
__all__ = ['Property']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: property.py 1444 2008-08-31 18:45:35Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssutils
|
||||
#import cssproperties
|
||||
from cssutils.profiles import profiles
|
||||
from cssvalue import CSSValue
|
||||
from cssutils.helper import Deprecated
|
||||
|
||||
class Property(cssutils.util.Base):
|
||||
"""
|
||||
(cssutils) a CSS property in a StyleDeclaration of a CSSStyleRule
|
||||
|
||||
Properties
|
||||
==========
|
||||
cssText
|
||||
a parsable textual representation of this property
|
||||
name
|
||||
normalized name of the property, e.g. "color" when name is "c\olor"
|
||||
(since 0.9.5)
|
||||
literalname (since 0.9.5)
|
||||
original name of the property in the source CSS which is not normalized
|
||||
e.g. "C\\OLor"
|
||||
cssValue
|
||||
the relevant CSSValue instance for this property
|
||||
value
|
||||
the string value of the property, same as cssValue.cssText
|
||||
priority
|
||||
of the property (currently only u"important" or None)
|
||||
literalpriority
|
||||
original priority of the property in the source CSS which is not
|
||||
normalized e.g. "IM\portant"
|
||||
seqs
|
||||
combination of a list for seq of name, a CSSValue object, and
|
||||
a list for seq of priority (empty or [!important] currently)
|
||||
valid
|
||||
if this Property is valid
|
||||
wellformed
|
||||
if this Property is syntactically ok
|
||||
|
||||
DEPRECATED normalname (since 0.9.5)
|
||||
normalized name of the property, e.g. "color" when name is "c\olor"
|
||||
|
||||
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):
|
||||
"""
|
||||
inits property
|
||||
|
||||
name
|
||||
a property name string (will be normalized)
|
||||
value
|
||||
a property value string
|
||||
priority
|
||||
an optional priority string which currently must be u'',
|
||||
u'!important' or u'important'
|
||||
_mediaQuery boolean
|
||||
if True value is optional as used by MediaQuery objects
|
||||
"""
|
||||
super(Property, self).__init__()
|
||||
|
||||
self.seqs = [[], None, []]
|
||||
self.valid = False
|
||||
self.wellformed = False
|
||||
self._mediaQuery = _mediaQuery
|
||||
|
||||
if name:
|
||||
self.name = name
|
||||
else:
|
||||
self._name = u''
|
||||
self._literalname = u''
|
||||
self.__normalname = u'' # DEPRECATED
|
||||
|
||||
if value:
|
||||
self.cssValue = value
|
||||
else:
|
||||
self.seqs[1] = CSSValue()
|
||||
|
||||
if priority:
|
||||
self.priority = priority
|
||||
else:
|
||||
self._priority = u''
|
||||
self._literalpriority = u''
|
||||
|
||||
def _getCssText(self):
|
||||
"""
|
||||
returns serialized property cssText
|
||||
"""
|
||||
return cssutils.ser.do_Property(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
|
||||
Raised if the rule is readonly.
|
||||
- SYNTAX_ERR: (self)
|
||||
Raised if the specified CSS string value has a syntax error and
|
||||
is unparsable.
|
||||
"""
|
||||
# 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.cssValue = 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: %r' %
|
||||
self._valuestr(cssText), colontoken)
|
||||
elif not nametokens:
|
||||
wellformed = False
|
||||
self._log.error(u'Property: No property name found: %r.' %
|
||||
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: %r.' %
|
||||
self._valuestr(cssText), colontoken)
|
||||
|
||||
if wellformed:
|
||||
self.wellformed = True
|
||||
self.name = nametokens
|
||||
self.cssValue = valuetokens
|
||||
self.priority = prioritytokens
|
||||
|
||||
else:
|
||||
self._log.error(u'Property: No property name found: %r.' %
|
||||
self._valuestr(cssText))
|
||||
|
||||
cssText = property(fget=_getCssText, fset=_setCssText,
|
||||
doc="A parsable textual representation.")
|
||||
|
||||
def _setName(self, name):
|
||||
"""
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR: (self)
|
||||
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]
|
||||
else:
|
||||
token = None
|
||||
|
||||
if not new['literalname']:
|
||||
wellformed = False
|
||||
self._log.error(u'Property: No name found: %r' %
|
||||
self._valuestr(name), token=token)
|
||||
|
||||
if wellformed:
|
||||
self.wellformed = True
|
||||
self._literalname = new['literalname']
|
||||
self._name = self._normalize(self._literalname)
|
||||
self.__normalname = self._name # DEPRECATED
|
||||
self.seqs[0] = newseq
|
||||
|
||||
# validate
|
||||
if self._name not in profiles.propertiesByProfile():
|
||||
self.valid = False
|
||||
tokenizer=self._tokenize2(name)
|
||||
self._log.warn(u'Property: Unknown Property: %r.' %
|
||||
new['literalname'], token=token, neverraise=True)
|
||||
else:
|
||||
self.valid = True
|
||||
if self.cssValue:
|
||||
self.cssValue._propertyName = self._name
|
||||
self.valid = self.cssValue.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 _getCSSValue(self):
|
||||
return self.seqs[1]
|
||||
|
||||
def _setCSSValue(self, cssText):
|
||||
"""
|
||||
see css.CSSValue
|
||||
|
||||
DOMException on setting?
|
||||
|
||||
- SYNTAX_ERR: (self)
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
(according to the attached property) or is unparsable.
|
||||
- TODO: INVALID_MODIFICATION_ERR:
|
||||
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] = CSSValue()
|
||||
else:
|
||||
if not self.seqs[1]:
|
||||
self.seqs[1] = CSSValue()
|
||||
|
||||
cssvalue = self.seqs[1]
|
||||
cssvalue._propertyName = self.name
|
||||
cssvalue.cssText = cssText
|
||||
if cssvalue._value and cssvalue.wellformed:
|
||||
self.seqs[1] = cssvalue
|
||||
self.valid = self.valid and cssvalue.valid
|
||||
self.wellformed = self.wellformed and cssvalue.wellformed
|
||||
|
||||
cssValue = property(_getCSSValue, _setCSSValue,
|
||||
doc="(cssutils) CSSValue object of this property")
|
||||
|
||||
def _getValue(self):
|
||||
if self.cssValue:
|
||||
return self.cssValue._value
|
||||
else:
|
||||
return u''
|
||||
|
||||
def _setValue(self, value):
|
||||
self.cssValue.cssText = value
|
||||
self.valid = self.valid and self.cssValue.valid
|
||||
self.wellformed = self.wellformed and self.cssValue.wellformed
|
||||
|
||||
value = property(_getValue, _setValue,
|
||||
doc="The textual value of this Properties cssValue.")
|
||||
|
||||
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;}
|
||||
|
||||
DOMException on setting
|
||||
|
||||
- SYNTAX_ERR: (self)
|
||||
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 - 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)
|
||||
normalval = self._tokenvalue(token, normalize=True)
|
||||
if 'important' == expected == normalval:
|
||||
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: %r.' %
|
||||
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
|
||||
if self._priority not in (u'', u'important'):
|
||||
self.valid = False
|
||||
self._log.info(u'Property: No CSS2 priority value: %r.' %
|
||||
self._priority, neverraise=True)
|
||||
|
||||
priority = property(lambda self: self._priority, _setPriority,
|
||||
doc="(cssutils) Priority of this property")
|
||||
|
||||
literalpriority = property(lambda self: self._literalpriority,
|
||||
doc="Readonly literal (not normalized) priority of this property")
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(name=%r, value=%r, priority=%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.literalname, self.cssValue.cssText, self.priority)
|
||||
|
||||
def __str__(self):
|
||||
return "<%s.%s object name=%r value=%r priority=%r at 0x%x>" % (
|
||||
self.__class__.__module__, self.__class__.__name__,
|
||||
self.name, self.cssValue.cssText, self.priority, id(self))
|
||||
|
||||
@Deprecated(u'Use property ``name`` instead (since cssutils 0.9.5).')
|
||||
def _getNormalname(self):
|
||||
return self.__normalname
|
||||
normalname = property(_getNormalname,
|
||||
doc="DEPRECATED since 0.9.5, use name instead")
|
File diff suppressed because it is too large
Load Diff
@ -1,249 +1,249 @@
|
||||
"""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: selectorlist.py 1174 2008-03-20 17:43:07Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssutils
|
||||
from selector import Selector
|
||||
|
||||
class SelectorList(cssutils.util.Base, cssutils.util.ListSeq):
|
||||
"""
|
||||
(cssutils) a list of Selectors of a CSSStyleRule
|
||||
|
||||
Properties
|
||||
==========
|
||||
length: of type unsigned long, readonly
|
||||
The number of Selector elements in the list.
|
||||
parentRule: of type CSSRule, readonly
|
||||
The CSS rule that contains this selector list or None if this
|
||||
list is not attached to a CSSRule.
|
||||
selectorText: of type DOMString
|
||||
The textual representation of the selector for the rule set. The
|
||||
implementation may have stripped out insignificant whitespace while
|
||||
parsing the selector.
|
||||
seq: (internal use!)
|
||||
A list of Selector objects
|
||||
wellformed
|
||||
if this selectorlist is wellformed regarding the Selector spec
|
||||
"""
|
||||
def __init__(self, selectorText=None, parentRule=None,
|
||||
readonly=False):
|
||||
"""
|
||||
initializes SelectorList with optional selectorText
|
||||
|
||||
: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 __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),
|
||||
parentList=self)
|
||||
if newSelector.wellformed:
|
||||
newSelector._parent = self # maybe set twice but must be!
|
||||
return newSelector
|
||||
|
||||
def __setitem__(self, index, newSelector):
|
||||
"""
|
||||
overwrites ListSeq.__setitem__
|
||||
|
||||
Any duplicate Selectors are **not** removed.
|
||||
"""
|
||||
newSelector = self.__prepareset(newSelector)
|
||||
if newSelector:
|
||||
self.seq[index] = newSelector
|
||||
|
||||
def append(self, newSelector):
|
||||
"same as appendSelector(newSelector)"
|
||||
self.appendSelector(newSelector)
|
||||
|
||||
length = property(lambda self: len(self),
|
||||
doc="The number of Selector elements in the list.")
|
||||
|
||||
|
||||
def __getNamespaces(self):
|
||||
"uses 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
|
||||
|
||||
_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.""")
|
||||
|
||||
parentRule = property(lambda self: self._parentRule,
|
||||
doc="(DOM) The CSS rule that contains this SelectorList or\
|
||||
None if this SelectorList is not attached to a CSSRule.")
|
||||
|
||||
def _getSelectorText(self):
|
||||
"returns 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:
|
||||
- `NAMESPACE_ERR`: (Selector)
|
||||
Raised if the specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- `SYNTAX_ERR`: (self)
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
and is unparsable.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (self)
|
||||
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),
|
||||
parentList=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
|
||||
# for selector in newseq:
|
||||
# self.appendSelector(selector)
|
||||
|
||||
selectorText = property(_getSelectorText, _setSelectorText,
|
||||
doc="""(cssutils) The textual representation of the selector for
|
||||
a rule set.""")
|
||||
|
||||
wellformed = property(lambda self: bool(len(self.seq)))
|
||||
|
||||
def appendSelector(self, newSelector):
|
||||
"""
|
||||
Append newSelector (a string will be converted to a new
|
||||
Selector).
|
||||
|
||||
:param newSelector:
|
||||
comma-separated list of selectors or a tuple of
|
||||
(selectorText, dict-of-namespaces)
|
||||
:returns: New Selector or None if newSelector is not wellformed.
|
||||
:Exceptions:
|
||||
- `NAMESPACE_ERR`: (self)
|
||||
Raised if the specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- `SYNTAX_ERR`: (self)
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
and is unparsable.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (self)
|
||||
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 __repr__(self):
|
||||
if self._namespaces:
|
||||
st = (self.selectorText, self._namespaces)
|
||||
else:
|
||||
st = self.selectorText
|
||||
return "cssutils.css.%s(selectorText=%r)" % (
|
||||
self.__class__.__name__, st)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object selectorText=%r _namespaces=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.selectorText, self._namespaces,
|
||||
id(self))
|
||||
|
||||
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
|
||||
"""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: selectorlist.py 1174 2008-03-20 17:43:07Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssutils
|
||||
from selector import Selector
|
||||
|
||||
class SelectorList(cssutils.util.Base, cssutils.util.ListSeq):
|
||||
"""
|
||||
(cssutils) a list of Selectors of a CSSStyleRule
|
||||
|
||||
Properties
|
||||
==========
|
||||
length: of type unsigned long, readonly
|
||||
The number of Selector elements in the list.
|
||||
parentRule: of type CSSRule, readonly
|
||||
The CSS rule that contains this selector list or None if this
|
||||
list is not attached to a CSSRule.
|
||||
selectorText: of type DOMString
|
||||
The textual representation of the selector for the rule set. The
|
||||
implementation may have stripped out insignificant whitespace while
|
||||
parsing the selector.
|
||||
seq: (internal use!)
|
||||
A list of Selector objects
|
||||
wellformed
|
||||
if this selectorlist is wellformed regarding the Selector spec
|
||||
"""
|
||||
def __init__(self, selectorText=None, parentRule=None,
|
||||
readonly=False):
|
||||
"""
|
||||
initializes SelectorList with optional selectorText
|
||||
|
||||
: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 __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),
|
||||
parentList=self)
|
||||
if newSelector.wellformed:
|
||||
newSelector._parent = self # maybe set twice but must be!
|
||||
return newSelector
|
||||
|
||||
def __setitem__(self, index, newSelector):
|
||||
"""
|
||||
overwrites ListSeq.__setitem__
|
||||
|
||||
Any duplicate Selectors are **not** removed.
|
||||
"""
|
||||
newSelector = self.__prepareset(newSelector)
|
||||
if newSelector:
|
||||
self.seq[index] = newSelector
|
||||
|
||||
def append(self, newSelector):
|
||||
"same as appendSelector(newSelector)"
|
||||
self.appendSelector(newSelector)
|
||||
|
||||
length = property(lambda self: len(self),
|
||||
doc="The number of Selector elements in the list.")
|
||||
|
||||
|
||||
def __getNamespaces(self):
|
||||
"uses 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
|
||||
|
||||
_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.""")
|
||||
|
||||
parentRule = property(lambda self: self._parentRule,
|
||||
doc="(DOM) The CSS rule that contains this SelectorList or\
|
||||
None if this SelectorList is not attached to a CSSRule.")
|
||||
|
||||
def _getSelectorText(self):
|
||||
"returns 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:
|
||||
- `NAMESPACE_ERR`: (Selector)
|
||||
Raised if the specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- `SYNTAX_ERR`: (self)
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
and is unparsable.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (self)
|
||||
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),
|
||||
parentList=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
|
||||
# for selector in newseq:
|
||||
# self.appendSelector(selector)
|
||||
|
||||
selectorText = property(_getSelectorText, _setSelectorText,
|
||||
doc="""(cssutils) The textual representation of the selector for
|
||||
a rule set.""")
|
||||
|
||||
wellformed = property(lambda self: bool(len(self.seq)))
|
||||
|
||||
def appendSelector(self, newSelector):
|
||||
"""
|
||||
Append newSelector (a string will be converted to a new
|
||||
Selector).
|
||||
|
||||
:param newSelector:
|
||||
comma-separated list of selectors or a tuple of
|
||||
(selectorText, dict-of-namespaces)
|
||||
:returns: New Selector or None if newSelector is not wellformed.
|
||||
:Exceptions:
|
||||
- `NAMESPACE_ERR`: (self)
|
||||
Raised if the specified selector uses an unknown namespace
|
||||
prefix.
|
||||
- `SYNTAX_ERR`: (self)
|
||||
Raised if the specified CSS string value has a syntax error
|
||||
and is unparsable.
|
||||
- `NO_MODIFICATION_ALLOWED_ERR`: (self)
|
||||
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 __repr__(self):
|
||||
if self._namespaces:
|
||||
st = (self.selectorText, self._namespaces)
|
||||
else:
|
||||
st = self.selectorText
|
||||
return "cssutils.css.%s(selectorText=%r)" % (
|
||||
self.__class__.__name__, st)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object selectorText=%r _namespaces=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.selectorText, self._namespaces,
|
||||
id(self))
|
||||
|
||||
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
|
@ -1,117 +1,117 @@
|
||||
#!/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: errorhandler.py 1361 2008-07-13 18:12:40Z cthedot $'
|
||||
|
||||
import logging
|
||||
import urllib2
|
||||
import xml.dom
|
||||
from helper import Deprecated
|
||||
|
||||
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
|
||||
"""
|
||||
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 token:
|
||||
if isinstance(token, tuple):
|
||||
msg = u'%s [%s:%s: %s]' % (
|
||||
msg, token[2], token[3], token[1])
|
||||
else:
|
||||
msg = u'%s [%s:%s: %s]' % (
|
||||
msg, token.line, token.col, token.value)
|
||||
|
||||
if error and self.raiseExceptions and not neverraise:
|
||||
if isinstance(error, urllib2.HTTPError) or isinstance(error, urllib2.URLError):
|
||||
raise error
|
||||
else:
|
||||
raise error(msg)
|
||||
else:
|
||||
self._logcall(msg)
|
||||
|
||||
def setLog(self, log):
|
||||
"""set log of errorhandler's log"""
|
||||
self._log = log
|
||||
|
||||
@Deprecated('Use setLog() instead.')
|
||||
def setlog(self, log):
|
||||
self.setLog(log)
|
||||
|
||||
@Deprecated('Use setLevel() instead.')
|
||||
def setloglevel(self, level):
|
||||
self.setLevel(level)
|
||||
|
||||
|
||||
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__
|
||||
#!/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: errorhandler.py 1361 2008-07-13 18:12:40Z cthedot $'
|
||||
|
||||
import logging
|
||||
import urllib2
|
||||
import xml.dom
|
||||
from helper import Deprecated
|
||||
|
||||
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
|
||||
"""
|
||||
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 token:
|
||||
if isinstance(token, tuple):
|
||||
msg = u'%s [%s:%s: %s]' % (
|
||||
msg, token[2], token[3], token[1])
|
||||
else:
|
||||
msg = u'%s [%s:%s: %s]' % (
|
||||
msg, token.line, token.col, token.value)
|
||||
|
||||
if error and self.raiseExceptions and not neverraise:
|
||||
if isinstance(error, urllib2.HTTPError) or isinstance(error, urllib2.URLError):
|
||||
raise error
|
||||
else:
|
||||
raise error(msg)
|
||||
else:
|
||||
self._logcall(msg)
|
||||
|
||||
def setLog(self, log):
|
||||
"""set log of errorhandler's log"""
|
||||
self._log = log
|
||||
|
||||
@Deprecated('Use setLog() instead.')
|
||||
def setlog(self, log):
|
||||
self.setLog(log)
|
||||
|
||||
@Deprecated('Use setLevel() instead.')
|
||||
def setloglevel(self, level):
|
||||
self.setLevel(level)
|
||||
|
||||
|
||||
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__
|
@ -1,183 +1,183 @@
|
||||
#!/usr/bin/env python
|
||||
"""a validating CSSParser
|
||||
"""
|
||||
__all__ = ['CSSParser']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: parse.py 1418 2008-08-09 19:27:50Z cthedot $'
|
||||
|
||||
import codecs
|
||||
import os
|
||||
import urllib
|
||||
from helper import Deprecated
|
||||
import tokenize2
|
||||
import cssutils
|
||||
|
||||
class CSSParser(object):
|
||||
"""
|
||||
parses a CSS StyleSheet string or file and
|
||||
returns 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):
|
||||
"""
|
||||
log
|
||||
logging object
|
||||
loglevel
|
||||
logging loglevel
|
||||
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
|
||||
fetcher
|
||||
see ``setFetchUrl(fetcher)``
|
||||
"""
|
||||
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()
|
||||
self.setFetcher(fetcher)
|
||||
|
||||
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 parseString(self, cssText, encoding=None, href=None, media=None,
|
||||
title=None):
|
||||
"""Return parsed CSSStyleSheet from given string cssText.
|
||||
Raises errors during retrieving (e.g. UnicodeDecodeError).
|
||||
|
||||
cssText
|
||||
CSS string to parse
|
||||
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.
|
||||
href
|
||||
The href attribute to assign to the parsed style sheet.
|
||||
Used to resolve other urls in the parsed sheet like @import hrefs
|
||||
media
|
||||
The media attribute to assign to the parsed style sheet
|
||||
(may be a MediaList, list or a string)
|
||||
title
|
||||
The title attribute to assign to the parsed style sheet
|
||||
"""
|
||||
self.__parseSetting(True)
|
||||
if isinstance(cssText, str):
|
||||
cssText = codecs.getdecoder('css')(cssText, encoding=encoding)[0]
|
||||
|
||||
sheet = cssutils.css.CSSStyleSheet(href=href,
|
||||
media=cssutils.stylesheets.MediaList(media),
|
||||
title=title)
|
||||
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):
|
||||
"""Retrieve and return a CSSStyleSheet from given filename.
|
||||
Raises errors during retrieving (e.g. IOError).
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
for other parameters see ``parseString``
|
||||
"""
|
||||
if not href:
|
||||
# prepend // for file URL, urllib does not do this?
|
||||
href = u'file:' + urllib.pathname2url(os.path.abspath(filename))
|
||||
|
||||
return self.parseString(open(filename, 'rb').read(),
|
||||
encoding=encoding, # read returns a str
|
||||
href=href, media=media, title=title)
|
||||
|
||||
def parseUrl(self, href, encoding=None, media=None, title=None):
|
||||
"""Retrieve and return a CSSStyleSheet from given href (an URL).
|
||||
In case of any errors while reading the URL returns None.
|
||||
|
||||
href
|
||||
URL of the CSS file to parse, will also be set as ``href`` of
|
||||
resulting stylesheet
|
||||
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.
|
||||
|
||||
for other parameters see ``parseString``
|
||||
"""
|
||||
encoding, enctype, text = cssutils.util._readUrl(href,
|
||||
overrideEncoding=encoding)
|
||||
if enctype == 5:
|
||||
# do not used if defaulting to UTF-8
|
||||
encoding = None
|
||||
|
||||
if text is not None:
|
||||
return self.parseString(text, encoding=encoding,
|
||||
href=href, media=media, title=title)
|
||||
|
||||
def setFetcher(self, fetcher=None):
|
||||
"""Replace the default URL fetch function with a custom one.
|
||||
The fetcher function gets a single parameter
|
||||
|
||||
``url``
|
||||
the URL to read
|
||||
|
||||
and returns ``(encoding, content)`` where ``encoding`` is the HTTP
|
||||
charset normally given via the Content-Type header (which may simply
|
||||
omit the charset) and ``content`` being the (byte) string 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
|
||||
|
||||
@Deprecated('Use cssutils.CSSParser().parseFile() instead.')
|
||||
def parse(self, filename, encoding=None,
|
||||
href=None, media=None, title=None):
|
||||
self.parseFile(filename, encoding, href, media, title)
|
||||
#!/usr/bin/env python
|
||||
"""a validating CSSParser
|
||||
"""
|
||||
__all__ = ['CSSParser']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: parse.py 1418 2008-08-09 19:27:50Z cthedot $'
|
||||
|
||||
import codecs
|
||||
import os
|
||||
import urllib
|
||||
from helper import Deprecated
|
||||
import tokenize2
|
||||
import cssutils
|
||||
|
||||
class CSSParser(object):
|
||||
"""
|
||||
parses a CSS StyleSheet string or file and
|
||||
returns 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):
|
||||
"""
|
||||
log
|
||||
logging object
|
||||
loglevel
|
||||
logging loglevel
|
||||
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
|
||||
fetcher
|
||||
see ``setFetchUrl(fetcher)``
|
||||
"""
|
||||
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()
|
||||
self.setFetcher(fetcher)
|
||||
|
||||
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 parseString(self, cssText, encoding=None, href=None, media=None,
|
||||
title=None):
|
||||
"""Return parsed CSSStyleSheet from given string cssText.
|
||||
Raises errors during retrieving (e.g. UnicodeDecodeError).
|
||||
|
||||
cssText
|
||||
CSS string to parse
|
||||
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.
|
||||
href
|
||||
The href attribute to assign to the parsed style sheet.
|
||||
Used to resolve other urls in the parsed sheet like @import hrefs
|
||||
media
|
||||
The media attribute to assign to the parsed style sheet
|
||||
(may be a MediaList, list or a string)
|
||||
title
|
||||
The title attribute to assign to the parsed style sheet
|
||||
"""
|
||||
self.__parseSetting(True)
|
||||
if isinstance(cssText, str):
|
||||
cssText = codecs.getdecoder('css')(cssText, encoding=encoding)[0]
|
||||
|
||||
sheet = cssutils.css.CSSStyleSheet(href=href,
|
||||
media=cssutils.stylesheets.MediaList(media),
|
||||
title=title)
|
||||
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):
|
||||
"""Retrieve and return a CSSStyleSheet from given filename.
|
||||
Raises errors during retrieving (e.g. IOError).
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
for other parameters see ``parseString``
|
||||
"""
|
||||
if not href:
|
||||
# prepend // for file URL, urllib does not do this?
|
||||
href = u'file:' + urllib.pathname2url(os.path.abspath(filename))
|
||||
|
||||
return self.parseString(open(filename, 'rb').read(),
|
||||
encoding=encoding, # read returns a str
|
||||
href=href, media=media, title=title)
|
||||
|
||||
def parseUrl(self, href, encoding=None, media=None, title=None):
|
||||
"""Retrieve and return a CSSStyleSheet from given href (an URL).
|
||||
In case of any errors while reading the URL returns None.
|
||||
|
||||
href
|
||||
URL of the CSS file to parse, will also be set as ``href`` of
|
||||
resulting stylesheet
|
||||
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.
|
||||
|
||||
for other parameters see ``parseString``
|
||||
"""
|
||||
encoding, enctype, text = cssutils.util._readUrl(href,
|
||||
overrideEncoding=encoding)
|
||||
if enctype == 5:
|
||||
# do not used if defaulting to UTF-8
|
||||
encoding = None
|
||||
|
||||
if text is not None:
|
||||
return self.parseString(text, encoding=encoding,
|
||||
href=href, media=media, title=title)
|
||||
|
||||
def setFetcher(self, fetcher=None):
|
||||
"""Replace the default URL fetch function with a custom one.
|
||||
The fetcher function gets a single parameter
|
||||
|
||||
``url``
|
||||
the URL to read
|
||||
|
||||
and returns ``(encoding, content)`` where ``encoding`` is the HTTP
|
||||
charset normally given via the Content-Type header (which may simply
|
||||
omit the charset) and ``content`` being the (byte) string 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
|
||||
|
||||
@Deprecated('Use cssutils.CSSParser().parseFile() instead.')
|
||||
def parse(self, filename, encoding=None,
|
||||
href=None, media=None, title=None):
|
||||
self.parseFile(filename, encoding, href, media, title)
|
402
src/cssutils/prodparser.py
Normal file
402
src/cssutils/prodparser.py
Normal file
@ -0,0 +1,402 @@
|
||||
# -*- 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 $'
|
||||
|
||||
import cssutils
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
"""Base Exception class for ProdParser (used internally)."""
|
||||
pass
|
||||
|
||||
class Exhausted(ParseError):
|
||||
"""Raised if Sequence or Choice is done."""
|
||||
pass
|
||||
|
||||
class NoMatch(ParseError):
|
||||
"""Raised if Sequence or Choice do not match."""
|
||||
pass
|
||||
|
||||
class MissingToken(ParseError):
|
||||
"""Raised if Sequence or Choice are not exhausted."""
|
||||
pass
|
||||
|
||||
|
||||
class Choice(object):
|
||||
"""A Choice of productions (Sequence or single Prod)."""
|
||||
def __init__(self, prods):
|
||||
"""
|
||||
prods
|
||||
Prod or Sequence objects
|
||||
"""
|
||||
self._prods = prods
|
||||
self._exhausted = False
|
||||
|
||||
def nextProd(self, token):
|
||||
"""
|
||||
Return:
|
||||
|
||||
- next matching Prod or Sequence
|
||||
- raises ParseError if nothing matches
|
||||
- raises Exhausted if choice already done
|
||||
|
||||
``token`` may be None but this occurs when no tokens left."""
|
||||
if not self._exhausted:
|
||||
for x in self._prods:
|
||||
if isinstance(x, Prod):
|
||||
test = x
|
||||
else:
|
||||
# nested Sequence matches if 1st prod matches
|
||||
test = x.first()
|
||||
try:
|
||||
if test.matches(token):
|
||||
self._exhausted = True
|
||||
return x
|
||||
except ParseError, e:
|
||||
# do not raise if other my match
|
||||
continue
|
||||
else:
|
||||
# None matched
|
||||
raise ParseError(u'No match in choice')
|
||||
else:
|
||||
raise Exhausted(u'Extra token')
|
||||
|
||||
|
||||
class Sequence(object):
|
||||
"""A Sequence of productions (Choice or single Prod)."""
|
||||
def __init__(self, prods, minmax=None):
|
||||
"""
|
||||
prods
|
||||
Prod or Sequence objects
|
||||
minmax = lambda: (1, 1)
|
||||
callback returning number of times this sequence may run
|
||||
"""
|
||||
self._prods = prods
|
||||
if not minmax:
|
||||
minmax = lambda: (1, 1)
|
||||
self._min, self._max = minmax()
|
||||
|
||||
self._number = len(self._prods)
|
||||
self._round = 1 # 1 based!
|
||||
self._pos = 0
|
||||
|
||||
def first(self):
|
||||
"""Return 1st element of Sequence, used by Choice"""
|
||||
# TODO: current impl first only if 1st if an prod!
|
||||
for prod in self._prods:
|
||||
if not prod.optional:
|
||||
return prod
|
||||
|
||||
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._pos:]:
|
||||
if not prod.optional:
|
||||
return prod.name
|
||||
else:
|
||||
return 'Unknown'
|
||||
|
||||
name = property(_currentName, doc='Used for Error reporting')
|
||||
|
||||
def nextProd(self, token):
|
||||
"""Return
|
||||
|
||||
- next matching Prod or Choice
|
||||
- raises ParseError if nothing matches
|
||||
- raises Exhausted if sequence already done
|
||||
"""
|
||||
while self._pos < self._number:
|
||||
x = self._prods[self._pos]
|
||||
thisround = self._round
|
||||
|
||||
self._pos += 1
|
||||
if self._pos == self._number:
|
||||
if self._round < self._max:
|
||||
# new round?
|
||||
self._pos = 0
|
||||
self._round += 1
|
||||
|
||||
if isinstance(x, Prod):
|
||||
if not token and (x.optional or thisround > self._min):
|
||||
# token is None if nothing expected
|
||||
raise Exhausted()
|
||||
elif not token and not x.optional:
|
||||
raise MissingToken(u'Missing token for production %s'
|
||||
% x.name)
|
||||
elif x.matches(token):
|
||||
return x
|
||||
elif x.optional:
|
||||
# try next
|
||||
continue
|
||||
# elif thisround > self._min:
|
||||
# # minimum done
|
||||
# self._round = self._max
|
||||
# self._pos = self._number
|
||||
# return None
|
||||
else:
|
||||
# should have matched
|
||||
raise NoMatch(u'No matching production for token')
|
||||
|
||||
else:
|
||||
# nested Sequence or Choice
|
||||
return x
|
||||
|
||||
# Sequence is exhausted
|
||||
if self._round >= self._max:
|
||||
raise Exhausted(u'Extra token')
|
||||
|
||||
|
||||
class Prod(object):
|
||||
"""Single Prod in Sequence or Choice."""
|
||||
def __init__(self, name, match, toSeq=None, toStore=None,
|
||||
optional=False):
|
||||
"""
|
||||
name
|
||||
name used for error reporting
|
||||
match callback
|
||||
function called with parameters tokentype and tokenvalue
|
||||
returning True, False or raising ParseError
|
||||
toSeq callback (optional)
|
||||
if given calling toSeq(token) will be appended to seq
|
||||
else simply seq
|
||||
toStore (optional)
|
||||
key to save util.Item to store or callback(store, util.Item)
|
||||
optional = False
|
||||
wether Prod is optional or not
|
||||
"""
|
||||
self.name = name
|
||||
self.match = match
|
||||
self.optional=optional
|
||||
|
||||
def makeToStore(key):
|
||||
"Return a function used by toStore."
|
||||
def toStore(store, item):
|
||||
"Set or append store item."
|
||||
if key in store:
|
||||
store[key].append(item)
|
||||
else:
|
||||
store[key] = item
|
||||
return toStore
|
||||
|
||||
if toSeq:
|
||||
# called: seq.append(toSeq(value))
|
||||
self.toSeq = toSeq
|
||||
else:
|
||||
self.toSeq = lambda val: val
|
||||
|
||||
if callable(toStore):
|
||||
self.toStore = toStore
|
||||
elif toStore:
|
||||
self.toStore = makeToStore(toStore)
|
||||
else:
|
||||
# always set!
|
||||
self.toStore = None
|
||||
|
||||
def matches(self, token):
|
||||
"""Return if token matches."""
|
||||
type_, val, line, col = token
|
||||
return self.match(type_, val)
|
||||
|
||||
def __repr__(self):
|
||||
return "<cssutils.prodsparser.%s object name=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.name, id(self))
|
||||
|
||||
|
||||
class ProdParser(object):
|
||||
"""Productions parser."""
|
||||
def __init__(self):
|
||||
self.types = cssutils.cssproductions.CSSProductions
|
||||
self._log = cssutils.log
|
||||
self._tokenizer = cssutils.tokenize2.Tokenizer()
|
||||
|
||||
def parse(self, text, name, productions, 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
|
||||
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
|
||||
"""
|
||||
if isinstance(text, basestring):
|
||||
# to tokenize
|
||||
tokens = self._tokenizer.tokenize(text)
|
||||
elif isinstance(text, tuple):
|
||||
# (token, tokens) or a single token
|
||||
if len(text) == 2:
|
||||
# (token, tokens)
|
||||
def gen(token, tokens):
|
||||
"new generator appending token and tokens"
|
||||
yield token
|
||||
for t in tokens:
|
||||
yield t
|
||||
|
||||
tokens = (t for t in gen(*text))
|
||||
|
||||
else:
|
||||
# single token
|
||||
tokens = [text]
|
||||
else:
|
||||
# already tokenized, assume generator
|
||||
tokens = text
|
||||
|
||||
# a new seq to append all Items to
|
||||
seq = cssutils.util.Seq(readonly=False)
|
||||
|
||||
# store for specific values
|
||||
if not store:
|
||||
store = {}
|
||||
# store['_raw'] = []
|
||||
|
||||
# stack of productions
|
||||
prods = [productions]
|
||||
|
||||
wellformed = True
|
||||
for token in tokens:
|
||||
type_, val, line, col = token
|
||||
# store['_raw'].append(val)
|
||||
|
||||
# default productions
|
||||
if type_ == self.types.S:
|
||||
# always append S?
|
||||
seq.append(val, type_, line, col)
|
||||
elif type_ == self.types.COMMENT:
|
||||
# always append COMMENT
|
||||
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.EOF:
|
||||
# do nothing
|
||||
pass
|
||||
# next = 'EOF'
|
||||
else:
|
||||
# check prods
|
||||
try:
|
||||
while True:
|
||||
# find next matching production
|
||||
try:
|
||||
prod = prods[-1].nextProd(token)
|
||||
except (NoMatch, Exhausted), e:
|
||||
# try next
|
||||
prod = None
|
||||
|
||||
if isinstance(prod, Prod):
|
||||
break
|
||||
elif not prod:
|
||||
if len(prods) > 1:
|
||||
# nested exhausted, next in parent
|
||||
prods.pop()
|
||||
else:
|
||||
raise Exhausted('Extra token')
|
||||
else:
|
||||
# nested Sequence, Choice
|
||||
prods.append(prod)
|
||||
|
||||
except ParseError, e:
|
||||
wellformed = False
|
||||
self._log.error(u'%s: %s: %r' % (name, e, token))
|
||||
|
||||
else:
|
||||
# process prod
|
||||
if prod.toSeq:
|
||||
seq.append(prod.toSeq(val), type_, line, col)
|
||||
else:
|
||||
seq.append(val, type_, line, col)
|
||||
|
||||
if prod.toStore:
|
||||
prod.toStore(store, seq[-1])
|
||||
|
||||
# if 'STOP' == next: # EOF?
|
||||
# # stop here and ignore following tokens
|
||||
# break
|
||||
|
||||
while True:
|
||||
# all productions exhausted?
|
||||
try:
|
||||
prod = prods[-1].nextProd(token=None)
|
||||
except Exhausted, e:
|
||||
prod = None # ok
|
||||
except (MissingToken, NoMatch), e:
|
||||
wellformed = False
|
||||
self._log.error(u'%s: %s'
|
||||
% (name, e))
|
||||
else:
|
||||
try:
|
||||
if prod.optional:
|
||||
# ignore optional ones
|
||||
continue
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if prod:
|
||||
wellformed = False
|
||||
self._log.error(u'%s: Missing token for production %r'
|
||||
% (name, prod.name))
|
||||
elif len(prods) > 1:
|
||||
# nested exhausted, next in parent
|
||||
prods.pop()
|
||||
else:
|
||||
break
|
||||
|
||||
# bool, Seq, None or generator
|
||||
return wellformed, seq, store, tokens
|
||||
|
||||
|
||||
class PreDef(object):
|
||||
"""Predefined Prod definition for use in productions definition
|
||||
for ProdParser instances.
|
||||
"""
|
||||
@staticmethod
|
||||
def comma():
|
||||
","
|
||||
return Prod(name=u'comma', match=lambda t, v: v == u',')
|
||||
|
||||
@staticmethod
|
||||
def funcEnd():
|
||||
")"
|
||||
return Prod(name=u'end FUNC ")"', match=lambda t, v: v == u')')
|
||||
|
||||
@staticmethod
|
||||
def unary():
|
||||
"+ or -"
|
||||
return Prod(name=u'unary +-', match=lambda t, v: v in u'+-',
|
||||
optional=True)
|
367
src/cssutils/profiles.py
Normal file
367
src/cssutils/profiles.py
Normal file
@ -0,0 +1,367 @@
|
||||
"""CSS profiles. Predefined are:
|
||||
|
||||
- 'CSS level 2'
|
||||
|
||||
"""
|
||||
__all__ = ['profiles']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssproperties.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
import cssutils
|
||||
import re
|
||||
|
||||
"""
|
||||
Define some regular expression fragments that will be used as
|
||||
macros within the CSS property value regular expressions.
|
||||
"""
|
||||
css2macros = {
|
||||
'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset',
|
||||
'border-color': '{color}',
|
||||
'border-width': '{length}|thin|medium|thick',
|
||||
|
||||
'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-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}{identifier}{w}(?:,{w}{list-style-type}{w})?\)',
|
||||
'identifier': r'{ident}',
|
||||
'family-name': r'{string}|{identifier}',
|
||||
'generic-family': r'serif|sans-serif|cursive|fantasy|monospace',
|
||||
'absolute-size': r'(x?x-)?(small|large)|medium',
|
||||
'relative-size': r'smaller|larger',
|
||||
'font-family': r'(({family-name}|{generic-family}){w},{w})*({family-name}|{generic-family})|inherit',
|
||||
'font-size': r'{absolute-size}|{relative-size}|{length}|{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',
|
||||
'outline-color': r'{color}|invert|inherit',
|
||||
'outline-style': r'{border-style}|inherit',
|
||||
'outline-width': r'{border-width}|inherit',
|
||||
'padding-width': r'{length}|{percentage}',
|
||||
'specific-voice': r'{identifier}',
|
||||
'generic-voice': r'male|female|child',
|
||||
'content': r'{string}|{uri}|{counter}|attr\({w}{identifier}{w}\)|open-quote|close-quote|no-open-quote|no-close-quote',
|
||||
'border-attrs': r'{border-width}|{border-style}|{border-color}',
|
||||
'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}',
|
||||
'outline-attrs': r'{outline-color}|{outline-style}|{outline-width}',
|
||||
'text-attrs': r'underline|overline|line-through|blink',
|
||||
}
|
||||
|
||||
"""
|
||||
Define the regular expressions for validation all CSS values
|
||||
"""
|
||||
css2 = {
|
||||
'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-color': r'({border-color}|transparent)(\s+({border-color}|transparent)){0,3}|inherit',
|
||||
'border-spacing': r'{length}(\s+{length})?|inherit',
|
||||
'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit',
|
||||
'border-top': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-right': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-bottom': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-left': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-top-color': r'{border-color}|transparent|inherit',
|
||||
'border-right-color': r'{border-color}|transparent|inherit',
|
||||
'border-bottom-color': r'{border-color}|transparent|inherit',
|
||||
'border-left-color': r'{border-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}(\s+{border-attrs})*|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'normal|{content}(\s+{content})*|inherit',
|
||||
'counter-increment': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
|
||||
'counter-reset': r'({identifier}(\s+{integer})?)(\s+({identifier}(\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',
|
||||
'font-family': r'{font-family}',
|
||||
'font-size': r'{font-size}',
|
||||
'font-style': r'{font-style}',
|
||||
'font-variant': r'{font-variant}',
|
||||
'font-weight': r'{font-weight}',
|
||||
'font': r'({font-attrs}\s+)*{font-size}({w}/{w}{line-height})?\s+{font-family}|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',
|
||||
'outline-color': r'{outline-color}',
|
||||
'outline-style': r'{outline-style}',
|
||||
'outline-width': r'{outline-width}',
|
||||
'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit',
|
||||
'overflow': r'visible|hidden|scroll|auto|inherit',
|
||||
'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',
|
||||
}
|
||||
|
||||
# CSS Color Module Level 3
|
||||
css3colormacros = {
|
||||
# orange and transparent in CSS 2.1
|
||||
'namedcolor': r'(currentcolor|transparent|orange|black|green|silver|lime|gray|olive|white|yellow|maroon|navy|red|blue|purple|teal|fuchsia|aqua)',
|
||||
# orange?
|
||||
'rgbacolor': r'rgba\({w}{int}{w},{w}{int}{w},{w}{int}{w},{w}{int}{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}\)',
|
||||
}
|
||||
|
||||
|
||||
css3color = {
|
||||
'color': r'{namedcolor}|{hexcolor}|{rgbcolor}|{rgbacolor}|{hslcolor}|inherit',
|
||||
'opacity': r'{num}|inherit'
|
||||
}
|
||||
|
||||
class NoSuchProfileException(Exception):
|
||||
"""Raised if no profile with given name is found"""
|
||||
pass
|
||||
|
||||
|
||||
class Profiles(object):
|
||||
"""
|
||||
A dictionary of::
|
||||
|
||||
profilename: {
|
||||
propname: propvalue_regex*
|
||||
}
|
||||
|
||||
Predefined profiles are:
|
||||
|
||||
- 'CSS level 2': Properties defined by CSS2
|
||||
|
||||
"""
|
||||
basicmacros = {
|
||||
'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+',
|
||||
'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*',
|
||||
}
|
||||
generalmacros = {
|
||||
'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)',
|
||||
'angle': r'0|{num}(deg|grad|rad)',
|
||||
'time': r'0|{num}m?s',
|
||||
'frequency': r'0|{num}k?Hz',
|
||||
'percentage': r'{num}%',
|
||||
}
|
||||
|
||||
CSS_LEVEL_2 = 'CSS Level 2.1'
|
||||
CSS_COLOR_LEVEL_3 = 'CSS Color Module Level 3'
|
||||
|
||||
def __init__(self):
|
||||
self._log = cssutils.log
|
||||
self._profilenames = [] # to keep order, REFACTOR!
|
||||
self._profiles = {}
|
||||
self.addProfile(self.CSS_LEVEL_2, css2, css2macros)
|
||||
self.addProfile(self.CSS_COLOR_LEVEL_3, css3color, css3colormacros)
|
||||
|
||||
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 callable(value):
|
||||
while re.search(r'{[a-z][a-z0-9-]*}', value):
|
||||
value = re.sub(r'{(?P<macro>[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():
|
||||
if not callable(value):
|
||||
value = re.compile('^(?:%s)$' % value, re.I).match
|
||||
dictionary[key] = value
|
||||
|
||||
return dictionary
|
||||
|
||||
profiles = property(lambda self: sorted(self._profiles.keys()),
|
||||
doc=u'Names of all profiles.')
|
||||
|
||||
def addProfile(self, profile, properties, macros=None):
|
||||
"""Add a new profile with name ``profile`` (e.g. 'CSS level 2')
|
||||
and the given ``properties``. ``macros`` are
|
||||
|
||||
``profile``
|
||||
The new profile's name
|
||||
``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.
|
||||
"""
|
||||
if not macros:
|
||||
macros = {}
|
||||
m = self.basicmacros
|
||||
m.update(self.generalmacros)
|
||||
m.update(macros)
|
||||
properties = self._expand_macros(properties, m)
|
||||
self._profilenames.append(profile)
|
||||
self._profiles[profile] = self._compile_regexes(properties)
|
||||
|
||||
def propertiesByProfile(self, profiles=None):
|
||||
"""Generator: Yield property names, if no profile(s) is given all
|
||||
profile's properties are used."""
|
||||
if not profiles:
|
||||
profiles = self.profiles
|
||||
elif isinstance(profiles, basestring):
|
||||
profiles = (profiles, )
|
||||
|
||||
try:
|
||||
for profile in sorted(profiles):
|
||||
for name in sorted(self._profiles[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."""
|
||||
for profile in self.profiles:
|
||||
if name in self._profiles[profile]:
|
||||
try:
|
||||
# custom validation errors are caught
|
||||
r = bool(self._profiles[profile][name](value))
|
||||
except Exception, e:
|
||||
self._log.error(e, error=Exception)
|
||||
return False
|
||||
if r:
|
||||
return r
|
||||
return False
|
||||
|
||||
def validateWithProfile(self, name, value):
|
||||
"""Check if value is valid for given property name returning
|
||||
(valid, valid_in_profile).
|
||||
|
||||
You may want to check if valid_in_profile is what you expected.
|
||||
|
||||
Example: You might expect a valid Profiles.CSS_LEVEL_2 value but
|
||||
e.g. ``validateWithProfile('color', 'rgba(1,1,1,1)')`` returns
|
||||
(True, Profiles.CSS_COLOR_LEVEL_3)
|
||||
"""
|
||||
for profilename in self._profilenames:
|
||||
if name in self._profiles[profilename]:
|
||||
try:
|
||||
# custom validation errors are caught
|
||||
r = (bool(self._profiles[profilename][name](value)),
|
||||
profilename)
|
||||
except Exception, e:
|
||||
self._log.error(e, error=Exception)
|
||||
r = False, None
|
||||
if r[0]:
|
||||
return r
|
||||
return False, None
|
||||
|
||||
|
||||
profiles = Profiles()
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,18 @@
|
||||
"""
|
||||
Document Object Model Level 2 Style Sheets
|
||||
http://www.w3.org/TR/2000/PR-DOM-Level-2-Style-20000927/stylesheets.html
|
||||
|
||||
currently implemented:
|
||||
- MediaList
|
||||
- MediaQuery (http://www.w3.org/TR/css3-mediaqueries/)
|
||||
- StyleSheet
|
||||
- StyleSheetList
|
||||
"""
|
||||
__all__ = ['MediaList', 'MediaQuery', 'StyleSheet', 'StyleSheetList']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: __init__.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
from medialist import *
|
||||
from mediaquery import *
|
||||
from stylesheet import *
|
||||
from stylesheetlist import *
|
||||
"""
|
||||
Document Object Model Level 2 Style Sheets
|
||||
http://www.w3.org/TR/2000/PR-DOM-Level-2-Style-20000927/stylesheets.html
|
||||
|
||||
currently implemented:
|
||||
- MediaList
|
||||
- MediaQuery (http://www.w3.org/TR/css3-mediaqueries/)
|
||||
- StyleSheet
|
||||
- StyleSheetList
|
||||
"""
|
||||
__all__ = ['MediaList', 'MediaQuery', 'StyleSheet', 'StyleSheetList']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: __init__.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
from medialist import *
|
||||
from mediaquery import *
|
||||
from stylesheet import *
|
||||
from stylesheetlist import *
|
@ -1,256 +1,256 @@
|
||||
"""
|
||||
MediaList implements DOM Level 2 Style Sheets MediaList.
|
||||
|
||||
TODO:
|
||||
- delete: maybe if deleting from all, replace *all* with all others?
|
||||
- is unknown media an exception?
|
||||
"""
|
||||
__all__ = ['MediaList']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: medialist.py 1423 2008-08-11 12:43:22Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssutils
|
||||
from cssutils.css import csscomment
|
||||
from mediaquery import MediaQuery
|
||||
|
||||
class MediaList(cssutils.util.Base, cssutils.util.ListSeq):
|
||||
"""
|
||||
Provides the abstraction of an ordered collection of media,
|
||||
without defining or constraining how this collection is
|
||||
implemented.
|
||||
|
||||
A media is always an instance of MediaQuery.
|
||||
|
||||
An empty list is the same as a list that contains the medium "all".
|
||||
|
||||
Properties
|
||||
==========
|
||||
length:
|
||||
The number of MediaQuery objects in the list.
|
||||
mediaText: of type DOMString
|
||||
The parsable textual representation of this MediaList
|
||||
self: a list (cssutils)
|
||||
All MediaQueries in this MediaList
|
||||
wellformed:
|
||||
if this list is wellformed
|
||||
|
||||
Format
|
||||
======
|
||||
::
|
||||
|
||||
medium [ COMMA S* medium ]*
|
||||
|
||||
New::
|
||||
|
||||
<media_query> [, <media_query> ]*
|
||||
"""
|
||||
def __init__(self, mediaText=None, readonly=False):
|
||||
"""
|
||||
mediaText
|
||||
unicodestring of parsable comma separared media
|
||||
or a list of media
|
||||
"""
|
||||
super(MediaList, self).__init__()
|
||||
self._wellformed = False
|
||||
|
||||
if isinstance(mediaText, list):
|
||||
mediaText = u','.join(mediaText)
|
||||
|
||||
if mediaText:
|
||||
self.mediaText = mediaText
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
length = property(lambda self: len(self),
|
||||
doc="(DOM readonly) The number of media in the list.")
|
||||
|
||||
def _getMediaText(self):
|
||||
"""
|
||||
returns serialized property mediaText
|
||||
"""
|
||||
return cssutils.ser.do_stylesheets_medialist(self)
|
||||
|
||||
def _setMediaText(self, mediaText):
|
||||
"""
|
||||
mediaText
|
||||
simple value or comma-separated list of media
|
||||
|
||||
DOMException
|
||||
|
||||
- SYNTAX_ERR: (MediaQuery)
|
||||
Raised if the specified string value has a syntax error and is
|
||||
unparsable.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this media list is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
wellformed = True
|
||||
tokenizer = self._tokenize2(mediaText)
|
||||
newseq = []
|
||||
|
||||
expected = None
|
||||
while True:
|
||||
# find all upto and including next ",", EOF or nothing
|
||||
mqtokens = self._tokensupto2(tokenizer, listseponly=True)
|
||||
if mqtokens:
|
||||
if self._tokenvalue(mqtokens[-1]) == ',':
|
||||
expected = mqtokens.pop()
|
||||
else:
|
||||
expected = None
|
||||
|
||||
mq = MediaQuery(mqtokens)
|
||||
if mq.wellformed:
|
||||
newseq.append(mq)
|
||||
else:
|
||||
wellformed = False
|
||||
self._log.error(u'MediaList: Invalid MediaQuery: %s' %
|
||||
self._valuestr(mqtokens))
|
||||
else:
|
||||
break
|
||||
|
||||
# post condition
|
||||
if expected:
|
||||
wellformed = False
|
||||
self._log.error(u'MediaList: Cannot end with ",".')
|
||||
|
||||
if wellformed:
|
||||
del self[:]
|
||||
for mq in newseq:
|
||||
self.appendMedium(mq)
|
||||
self._wellformed = True
|
||||
|
||||
mediaText = property(_getMediaText, _setMediaText,
|
||||
doc="""(DOM) The parsable textual representation of the media list.
|
||||
This is a comma-separated list of media.""")
|
||||
|
||||
wellformed = property(lambda self: self._wellformed)
|
||||
|
||||
def __prepareset(self, newMedium):
|
||||
# used by appendSelector and __setitem__
|
||||
self._checkReadonly()
|
||||
|
||||
if not isinstance(newMedium, MediaQuery):
|
||||
newMedium = MediaQuery(newMedium)
|
||||
|
||||
if newMedium.wellformed:
|
||||
return newMedium
|
||||
|
||||
def __setitem__(self, index, newMedium):
|
||||
"""
|
||||
overwrites ListSeq.__setitem__
|
||||
|
||||
Any duplicate items are **not** removed.
|
||||
"""
|
||||
newMedium = self.__prepareset(newMedium)
|
||||
if newMedium:
|
||||
self.seq[index] = newMedium
|
||||
# TODO: remove duplicates?
|
||||
|
||||
def appendMedium(self, newMedium):
|
||||
"""
|
||||
(DOM)
|
||||
Adds the medium newMedium to the end of the list. If the newMedium
|
||||
is already used, it is first removed.
|
||||
|
||||
newMedium
|
||||
a string or a MediaQuery object
|
||||
|
||||
returns if newMedium is wellformed
|
||||
|
||||
DOMException
|
||||
|
||||
- INVALID_CHARACTER_ERR: (self)
|
||||
If the medium contains characters that are invalid in the
|
||||
underlying style language.
|
||||
- INVALID_MODIFICATION_ERR (self)
|
||||
If mediaText is "all" and a new medium is tried to be added.
|
||||
Exception is "handheld" which is set in any case (Opera does handle
|
||||
"all, handheld" special, this special case might be removed in the
|
||||
future).
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this list is readonly.
|
||||
"""
|
||||
newMedium = self.__prepareset(newMedium)
|
||||
|
||||
if newMedium:
|
||||
mts = [self._normalize(mq.mediaType) for mq in self]
|
||||
newmt = self._normalize(newMedium.mediaType)
|
||||
|
||||
if newmt in mts:
|
||||
self.deleteMedium(newmt)
|
||||
self.seq.append(newMedium)
|
||||
elif u'all' == newmt:
|
||||
# remove all except handheld (Opera)
|
||||
h = None
|
||||
for mq in self:
|
||||
if mq.mediaType == u'handheld':
|
||||
h = mq
|
||||
del self[:]
|
||||
self.seq.append(newMedium)
|
||||
if h:
|
||||
self.append(h)
|
||||
elif u'all' in mts:
|
||||
if u'handheld' == newmt:
|
||||
self.seq.append(newMedium)
|
||||
self._log.warn(u'MediaList: Already specified "all" but still setting new medium: %r' %
|
||||
newMedium, error=xml.dom.InvalidModificationErr, neverraise=True)
|
||||
else:
|
||||
self._log.warn(u'MediaList: Ignoring new medium %r as already specified "all" (set ``mediaText`` instead).' %
|
||||
newMedium, error=xml.dom.InvalidModificationErr)
|
||||
else:
|
||||
self.seq.append(newMedium)
|
||||
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
def append(self, newMedium):
|
||||
"overwrites ListSeq.append"
|
||||
self.appendMedium(newMedium)
|
||||
|
||||
def deleteMedium(self, oldMedium):
|
||||
"""
|
||||
(DOM)
|
||||
Deletes the medium indicated by oldMedium from the list.
|
||||
|
||||
DOMException
|
||||
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this list is readonly.
|
||||
- NOT_FOUND_ERR: (self)
|
||||
Raised if oldMedium is not in the list.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
oldMedium = self._normalize(oldMedium)
|
||||
|
||||
for i, mq in enumerate(self):
|
||||
if self._normalize(mq.mediaType) == oldMedium:
|
||||
del self[i]
|
||||
break
|
||||
else:
|
||||
self._log.error(u'"%s" not in this MediaList' % oldMedium,
|
||||
error=xml.dom.NotFoundErr)
|
||||
# raise xml.dom.NotFoundErr(
|
||||
# u'"%s" not in this MediaList' % oldMedium)
|
||||
|
||||
def item(self, index):
|
||||
"""
|
||||
(DOM)
|
||||
Returns the mediaType of the index'th element in the list.
|
||||
If index is greater than or equal to the number of media in the
|
||||
list, returns None.
|
||||
"""
|
||||
try:
|
||||
return self[index].mediaType
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.stylesheets.%s(mediaText=%r)" % (
|
||||
self.__class__.__name__, self.mediaText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.stylesheets.%s object mediaText=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.mediaText, id(self))
|
||||
"""
|
||||
MediaList implements DOM Level 2 Style Sheets MediaList.
|
||||
|
||||
TODO:
|
||||
- delete: maybe if deleting from all, replace *all* with all others?
|
||||
- is unknown media an exception?
|
||||
"""
|
||||
__all__ = ['MediaList']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: medialist.py 1423 2008-08-11 12:43:22Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssutils
|
||||
from cssutils.css import csscomment
|
||||
from mediaquery import MediaQuery
|
||||
|
||||
class MediaList(cssutils.util.Base, cssutils.util.ListSeq):
|
||||
"""
|
||||
Provides the abstraction of an ordered collection of media,
|
||||
without defining or constraining how this collection is
|
||||
implemented.
|
||||
|
||||
A media is always an instance of MediaQuery.
|
||||
|
||||
An empty list is the same as a list that contains the medium "all".
|
||||
|
||||
Properties
|
||||
==========
|
||||
length:
|
||||
The number of MediaQuery objects in the list.
|
||||
mediaText: of type DOMString
|
||||
The parsable textual representation of this MediaList
|
||||
self: a list (cssutils)
|
||||
All MediaQueries in this MediaList
|
||||
wellformed:
|
||||
if this list is wellformed
|
||||
|
||||
Format
|
||||
======
|
||||
::
|
||||
|
||||
medium [ COMMA S* medium ]*
|
||||
|
||||
New::
|
||||
|
||||
<media_query> [, <media_query> ]*
|
||||
"""
|
||||
def __init__(self, mediaText=None, readonly=False):
|
||||
"""
|
||||
mediaText
|
||||
unicodestring of parsable comma separared media
|
||||
or a list of media
|
||||
"""
|
||||
super(MediaList, self).__init__()
|
||||
self._wellformed = False
|
||||
|
||||
if isinstance(mediaText, list):
|
||||
mediaText = u','.join(mediaText)
|
||||
|
||||
if mediaText:
|
||||
self.mediaText = mediaText
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
length = property(lambda self: len(self),
|
||||
doc="(DOM readonly) The number of media in the list.")
|
||||
|
||||
def _getMediaText(self):
|
||||
"""
|
||||
returns serialized property mediaText
|
||||
"""
|
||||
return cssutils.ser.do_stylesheets_medialist(self)
|
||||
|
||||
def _setMediaText(self, mediaText):
|
||||
"""
|
||||
mediaText
|
||||
simple value or comma-separated list of media
|
||||
|
||||
DOMException
|
||||
|
||||
- SYNTAX_ERR: (MediaQuery)
|
||||
Raised if the specified string value has a syntax error and is
|
||||
unparsable.
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this media list is readonly.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
wellformed = True
|
||||
tokenizer = self._tokenize2(mediaText)
|
||||
newseq = []
|
||||
|
||||
expected = None
|
||||
while True:
|
||||
# find all upto and including next ",", EOF or nothing
|
||||
mqtokens = self._tokensupto2(tokenizer, listseponly=True)
|
||||
if mqtokens:
|
||||
if self._tokenvalue(mqtokens[-1]) == ',':
|
||||
expected = mqtokens.pop()
|
||||
else:
|
||||
expected = None
|
||||
|
||||
mq = MediaQuery(mqtokens)
|
||||
if mq.wellformed:
|
||||
newseq.append(mq)
|
||||
else:
|
||||
wellformed = False
|
||||
self._log.error(u'MediaList: Invalid MediaQuery: %s' %
|
||||
self._valuestr(mqtokens))
|
||||
else:
|
||||
break
|
||||
|
||||
# post condition
|
||||
if expected:
|
||||
wellformed = False
|
||||
self._log.error(u'MediaList: Cannot end with ",".')
|
||||
|
||||
if wellformed:
|
||||
del self[:]
|
||||
for mq in newseq:
|
||||
self.appendMedium(mq)
|
||||
self._wellformed = True
|
||||
|
||||
mediaText = property(_getMediaText, _setMediaText,
|
||||
doc="""(DOM) The parsable textual representation of the media list.
|
||||
This is a comma-separated list of media.""")
|
||||
|
||||
wellformed = property(lambda self: self._wellformed)
|
||||
|
||||
def __prepareset(self, newMedium):
|
||||
# used by appendSelector and __setitem__
|
||||
self._checkReadonly()
|
||||
|
||||
if not isinstance(newMedium, MediaQuery):
|
||||
newMedium = MediaQuery(newMedium)
|
||||
|
||||
if newMedium.wellformed:
|
||||
return newMedium
|
||||
|
||||
def __setitem__(self, index, newMedium):
|
||||
"""
|
||||
overwrites ListSeq.__setitem__
|
||||
|
||||
Any duplicate items are **not** removed.
|
||||
"""
|
||||
newMedium = self.__prepareset(newMedium)
|
||||
if newMedium:
|
||||
self.seq[index] = newMedium
|
||||
# TODO: remove duplicates?
|
||||
|
||||
def appendMedium(self, newMedium):
|
||||
"""
|
||||
(DOM)
|
||||
Adds the medium newMedium to the end of the list. If the newMedium
|
||||
is already used, it is first removed.
|
||||
|
||||
newMedium
|
||||
a string or a MediaQuery object
|
||||
|
||||
returns if newMedium is wellformed
|
||||
|
||||
DOMException
|
||||
|
||||
- INVALID_CHARACTER_ERR: (self)
|
||||
If the medium contains characters that are invalid in the
|
||||
underlying style language.
|
||||
- INVALID_MODIFICATION_ERR (self)
|
||||
If mediaText is "all" and a new medium is tried to be added.
|
||||
Exception is "handheld" which is set in any case (Opera does handle
|
||||
"all, handheld" special, this special case might be removed in the
|
||||
future).
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this list is readonly.
|
||||
"""
|
||||
newMedium = self.__prepareset(newMedium)
|
||||
|
||||
if newMedium:
|
||||
mts = [self._normalize(mq.mediaType) for mq in self]
|
||||
newmt = self._normalize(newMedium.mediaType)
|
||||
|
||||
if newmt in mts:
|
||||
self.deleteMedium(newmt)
|
||||
self.seq.append(newMedium)
|
||||
elif u'all' == newmt:
|
||||
# remove all except handheld (Opera)
|
||||
h = None
|
||||
for mq in self:
|
||||
if mq.mediaType == u'handheld':
|
||||
h = mq
|
||||
del self[:]
|
||||
self.seq.append(newMedium)
|
||||
if h:
|
||||
self.append(h)
|
||||
elif u'all' in mts:
|
||||
if u'handheld' == newmt:
|
||||
self.seq.append(newMedium)
|
||||
self._log.warn(u'MediaList: Already specified "all" but still setting new medium: %r' %
|
||||
newMedium, error=xml.dom.InvalidModificationErr, neverraise=True)
|
||||
else:
|
||||
self._log.warn(u'MediaList: Ignoring new medium %r as already specified "all" (set ``mediaText`` instead).' %
|
||||
newMedium, error=xml.dom.InvalidModificationErr)
|
||||
else:
|
||||
self.seq.append(newMedium)
|
||||
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
def append(self, newMedium):
|
||||
"overwrites ListSeq.append"
|
||||
self.appendMedium(newMedium)
|
||||
|
||||
def deleteMedium(self, oldMedium):
|
||||
"""
|
||||
(DOM)
|
||||
Deletes the medium indicated by oldMedium from the list.
|
||||
|
||||
DOMException
|
||||
|
||||
- NO_MODIFICATION_ALLOWED_ERR: (self)
|
||||
Raised if this list is readonly.
|
||||
- NOT_FOUND_ERR: (self)
|
||||
Raised if oldMedium is not in the list.
|
||||
"""
|
||||
self._checkReadonly()
|
||||
oldMedium = self._normalize(oldMedium)
|
||||
|
||||
for i, mq in enumerate(self):
|
||||
if self._normalize(mq.mediaType) == oldMedium:
|
||||
del self[i]
|
||||
break
|
||||
else:
|
||||
self._log.error(u'"%s" not in this MediaList' % oldMedium,
|
||||
error=xml.dom.NotFoundErr)
|
||||
# raise xml.dom.NotFoundErr(
|
||||
# u'"%s" not in this MediaList' % oldMedium)
|
||||
|
||||
def item(self, index):
|
||||
"""
|
||||
(DOM)
|
||||
Returns the mediaType of the index'th element in the list.
|
||||
If index is greater than or equal to the number of media in the
|
||||
list, returns None.
|
||||
"""
|
||||
try:
|
||||
return self[index].mediaType
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.stylesheets.%s(mediaText=%r)" % (
|
||||
self.__class__.__name__, self.mediaText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.stylesheets.%s object mediaText=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.mediaText, id(self))
|
@ -1,101 +1,101 @@
|
||||
"""
|
||||
StyleSheet implements DOM Level 2 Style Sheets StyleSheet.
|
||||
"""
|
||||
__all__ = ['StyleSheet']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: stylesheet.py 1284 2008-06-05 16:29:17Z cthedot $'
|
||||
|
||||
import urlparse
|
||||
import cssutils
|
||||
|
||||
class StyleSheet(cssutils.util.Base2):
|
||||
"""
|
||||
The StyleSheet interface is the abstract base interface
|
||||
for any type of style sheet. It represents a single style
|
||||
sheet associated with a structured document.
|
||||
|
||||
In HTML, the StyleSheet interface represents either an
|
||||
external style sheet, included via the HTML LINK element,
|
||||
or an inline STYLE element (-ch: also an @import stylesheet?).
|
||||
|
||||
In XML, this interface represents
|
||||
an external style sheet, included via a style sheet
|
||||
processing instruction.
|
||||
"""
|
||||
def __init__(self, type='text/css',
|
||||
href=None,
|
||||
media=None,
|
||||
title=u'',
|
||||
disabled=None,
|
||||
ownerNode=None,
|
||||
parentStyleSheet=None):
|
||||
"""
|
||||
type: readonly
|
||||
This specifies the style sheet language for this
|
||||
style sheet. The style sheet language is specified
|
||||
as a content type (e.g. "text/css"). The content
|
||||
type is often specified in the ownerNode. Also see
|
||||
the type attribute definition for the LINK element
|
||||
in HTML 4.0, and the type pseudo-attribute for the
|
||||
XML style sheet processing instruction.
|
||||
href: readonly
|
||||
If the style sheet is a linked style sheet, the value
|
||||
of this attribute is its location. For inline style
|
||||
sheets, the value of this attribute is None. See the
|
||||
href attribute definition for the LINK element in HTML
|
||||
4.0, and the href pseudo-attribute for the XML style
|
||||
sheet processing instruction.
|
||||
media: of type MediaList, readonly
|
||||
The intended destination media for style information.
|
||||
The media is often specified in the ownerNode. If no
|
||||
media has been specified, the MediaList will be empty.
|
||||
See the media attribute definition for the LINK element
|
||||
in HTML 4.0, and the media pseudo-attribute for the XML
|
||||
style sheet processing instruction. Modifying the media
|
||||
list may cause a change to the attribute disabled.
|
||||
title: readonly
|
||||
The advisory title. The title is often specified in
|
||||
the ownerNode. See the title attribute definition for
|
||||
the LINK element in HTML 4.0, and the title
|
||||
pseudo-attribute for the XML style sheet processing
|
||||
instruction.
|
||||
disabled: False if the style sheet is applied to the
|
||||
document. True if it is not. Modifying this attribute
|
||||
may cause a new resolution of style for the document.
|
||||
A stylesheet only applies if both an appropriate medium
|
||||
definition is present and the disabled attribute is False.
|
||||
So, if the media doesn't apply to the current user agent,
|
||||
the disabled attribute is ignored.
|
||||
ownerNode: of type Node, readonly
|
||||
The node that associates this style sheet with the
|
||||
document. For HTML, this may be the corresponding LINK
|
||||
or STYLE element. For XML, it may be the linking
|
||||
processing instruction. For style sheets that are
|
||||
included by other style sheets, the value of this
|
||||
attribute is None.
|
||||
parentStyleSheet: of type StyleSheet, readonly
|
||||
For style sheet languages that support the concept
|
||||
of style sheet inclusion, this attribute represents
|
||||
the including style sheet, if one exists. If the style
|
||||
sheet is a top-level style sheet, or the style sheet
|
||||
language does not support inclusion, the value of this
|
||||
attribute is None.
|
||||
"""
|
||||
super(StyleSheet, self).__init__()
|
||||
|
||||
self._href = href
|
||||
self._ownerNode = ownerNode
|
||||
self._parentStyleSheet = parentStyleSheet
|
||||
self._type = type
|
||||
|
||||
self.disabled = bool(disabled)
|
||||
self.media = media
|
||||
self.title = title
|
||||
|
||||
href = property(lambda self: self._href)
|
||||
|
||||
ownerNode = property(lambda self: self._ownerNode)
|
||||
|
||||
parentStyleSheet = property(lambda self: self._parentStyleSheet)
|
||||
|
||||
type = property(lambda self: self._type, doc=u'Default: "ext/css"')
|
||||
"""
|
||||
StyleSheet implements DOM Level 2 Style Sheets StyleSheet.
|
||||
"""
|
||||
__all__ = ['StyleSheet']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: stylesheet.py 1284 2008-06-05 16:29:17Z cthedot $'
|
||||
|
||||
import urlparse
|
||||
import cssutils
|
||||
|
||||
class StyleSheet(cssutils.util.Base2):
|
||||
"""
|
||||
The StyleSheet interface is the abstract base interface
|
||||
for any type of style sheet. It represents a single style
|
||||
sheet associated with a structured document.
|
||||
|
||||
In HTML, the StyleSheet interface represents either an
|
||||
external style sheet, included via the HTML LINK element,
|
||||
or an inline STYLE element (-ch: also an @import stylesheet?).
|
||||
|
||||
In XML, this interface represents
|
||||
an external style sheet, included via a style sheet
|
||||
processing instruction.
|
||||
"""
|
||||
def __init__(self, type='text/css',
|
||||
href=None,
|
||||
media=None,
|
||||
title=u'',
|
||||
disabled=None,
|
||||
ownerNode=None,
|
||||
parentStyleSheet=None):
|
||||
"""
|
||||
type: readonly
|
||||
This specifies the style sheet language for this
|
||||
style sheet. The style sheet language is specified
|
||||
as a content type (e.g. "text/css"). The content
|
||||
type is often specified in the ownerNode. Also see
|
||||
the type attribute definition for the LINK element
|
||||
in HTML 4.0, and the type pseudo-attribute for the
|
||||
XML style sheet processing instruction.
|
||||
href: readonly
|
||||
If the style sheet is a linked style sheet, the value
|
||||
of this attribute is its location. For inline style
|
||||
sheets, the value of this attribute is None. See the
|
||||
href attribute definition for the LINK element in HTML
|
||||
4.0, and the href pseudo-attribute for the XML style
|
||||
sheet processing instruction.
|
||||
media: of type MediaList, readonly
|
||||
The intended destination media for style information.
|
||||
The media is often specified in the ownerNode. If no
|
||||
media has been specified, the MediaList will be empty.
|
||||
See the media attribute definition for the LINK element
|
||||
in HTML 4.0, and the media pseudo-attribute for the XML
|
||||
style sheet processing instruction. Modifying the media
|
||||
list may cause a change to the attribute disabled.
|
||||
title: readonly
|
||||
The advisory title. The title is often specified in
|
||||
the ownerNode. See the title attribute definition for
|
||||
the LINK element in HTML 4.0, and the title
|
||||
pseudo-attribute for the XML style sheet processing
|
||||
instruction.
|
||||
disabled: False if the style sheet is applied to the
|
||||
document. True if it is not. Modifying this attribute
|
||||
may cause a new resolution of style for the document.
|
||||
A stylesheet only applies if both an appropriate medium
|
||||
definition is present and the disabled attribute is False.
|
||||
So, if the media doesn't apply to the current user agent,
|
||||
the disabled attribute is ignored.
|
||||
ownerNode: of type Node, readonly
|
||||
The node that associates this style sheet with the
|
||||
document. For HTML, this may be the corresponding LINK
|
||||
or STYLE element. For XML, it may be the linking
|
||||
processing instruction. For style sheets that are
|
||||
included by other style sheets, the value of this
|
||||
attribute is None.
|
||||
parentStyleSheet: of type StyleSheet, readonly
|
||||
For style sheet languages that support the concept
|
||||
of style sheet inclusion, this attribute represents
|
||||
the including style sheet, if one exists. If the style
|
||||
sheet is a top-level style sheet, or the style sheet
|
||||
language does not support inclusion, the value of this
|
||||
attribute is None.
|
||||
"""
|
||||
super(StyleSheet, self).__init__()
|
||||
|
||||
self._href = href
|
||||
self._ownerNode = ownerNode
|
||||
self._parentStyleSheet = parentStyleSheet
|
||||
self._type = type
|
||||
|
||||
self.disabled = bool(disabled)
|
||||
self.media = media
|
||||
self.title = title
|
||||
|
||||
href = property(lambda self: self._href)
|
||||
|
||||
ownerNode = property(lambda self: self._ownerNode)
|
||||
|
||||
parentStyleSheet = property(lambda self: self._parentStyleSheet)
|
||||
|
||||
type = property(lambda self: self._type, doc=u'Default: "ext/css"')
|
@ -1,35 +1,35 @@
|
||||
"""
|
||||
StyleSheetList implements DOM Level 2 Style Sheets StyleSheetList.
|
||||
"""
|
||||
__all__ = ['StyleSheetList']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: stylesheetlist.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
class StyleSheetList(list):
|
||||
"""
|
||||
Interface StyleSheetList (introduced in DOM Level 2)
|
||||
|
||||
The StyleSheetList interface provides the abstraction of an ordered
|
||||
collection of style sheets.
|
||||
|
||||
The items in the StyleSheetList are accessible via an integral index,
|
||||
starting from 0.
|
||||
|
||||
This Python implementation is based on a standard Python list so e.g.
|
||||
allows ``examplelist[index]`` usage.
|
||||
"""
|
||||
def item(self, index):
|
||||
"""
|
||||
Used to retrieve a style sheet by ordinal index. If index is
|
||||
greater than or equal to the number of style sheets in the list,
|
||||
this returns None.
|
||||
"""
|
||||
try:
|
||||
return self[index]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
length = property(lambda self: len(self),
|
||||
doc="""The number of StyleSheets in the list. The range of valid
|
||||
child stylesheet indices is 0 to length-1 inclusive.""")
|
||||
|
||||
"""
|
||||
StyleSheetList implements DOM Level 2 Style Sheets StyleSheetList.
|
||||
"""
|
||||
__all__ = ['StyleSheetList']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: stylesheetlist.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
class StyleSheetList(list):
|
||||
"""
|
||||
Interface StyleSheetList (introduced in DOM Level 2)
|
||||
|
||||
The StyleSheetList interface provides the abstraction of an ordered
|
||||
collection of style sheets.
|
||||
|
||||
The items in the StyleSheetList are accessible via an integral index,
|
||||
starting from 0.
|
||||
|
||||
This Python implementation is based on a standard Python list so e.g.
|
||||
allows ``examplelist[index]`` usage.
|
||||
"""
|
||||
def item(self, index):
|
||||
"""
|
||||
Used to retrieve a style sheet by ordinal index. If index is
|
||||
greater than or equal to the number of style sheets in the list,
|
||||
this returns None.
|
||||
"""
|
||||
try:
|
||||
return self[index]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
length = property(lambda self: len(self),
|
||||
doc="""The number of StyleSheets in the list. The range of valid
|
||||
child stylesheet indices is 0 to length-1 inclusive.""")
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user