This commit is contained in:
Kovid Goyal 2008-09-19 10:01:15 -07:00
parent 308b128089
commit fc934f1f66
46 changed files with 10981 additions and 9996 deletions

View File

@ -23,3 +23,8 @@ installer/windows/calibre/build.log
src/calibre/translations/.errors src/calibre/translations/.errors
src/calibre/plugins/* src/calibre/plugins/*
src/calibre/gui2/pictureflow/.build src/calibre/gui2/pictureflow/.build
src/cssutils/.svn/
src/cssutils/_todo/
src/cssutils/scripts/
src/cssutils/css/.svn/
src/cssutils/stylesheets/.svn/

View File

@ -11,6 +11,20 @@ from calibre.utils.config import Config, StringConfig
from calibre.utils.zipfile import ZipFile, ZIP_STORED from calibre.utils.zipfile import ZipFile, ZIP_STORED
from calibre.ebooks.html import config as common_config 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'): def initialize_container(path_to_container, opf_name='metadata.opf'):
''' '''
Create an empty EPUB document, with a default skeleton. 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, c.add_opt('output', ['-o', '--output'], default=None,
help=_('The output EPUB file. If not specified, it is derived from the input file name.')) 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 = 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')]", structure('chapter', ['--chapter'], default="//*[re:match(name(), 'h[1-2]') and re:test(., 'chapter|book|section', 'i')]",

View File

@ -17,7 +17,7 @@ from calibre.ebooks.epub import config as common_config
from calibre.ptempfile import TemporaryDirectory from calibre.ptempfile import TemporaryDirectory
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.ebooks.metadata.toc import TOC 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): class HTMLProcessor(Processor):
@ -38,7 +38,14 @@ class HTMLProcessor(Processor):
self.split() 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): def collect_font_statistics(self):
''' '''
@ -87,6 +94,7 @@ def convert(htmlfile, opts, notification=None):
htmlfile = os.path.abspath(htmlfile) htmlfile = os.path.abspath(htmlfile)
if opts.output is None: if opts.output is None:
opts.output = os.path.splitext(os.path.basename(htmlfile))[0] + '.epub' opts.output = os.path.splitext(os.path.basename(htmlfile))[0] + '.epub'
opts.profile = PROFILES[opts.profile]
opts.output = os.path.abspath(opts.output) opts.output = os.path.abspath(opts.output)
if htmlfile.lower().endswith('.opf'): if htmlfile.lower().endswith('.opf'):
opf = OPFReader(htmlfile, os.path.dirname(os.path.abspath(htmlfile))) opf = OPFReader(htmlfile, os.path.dirname(os.path.abspath(htmlfile)))

View File

@ -438,7 +438,7 @@ class Processor(Parser):
style = etree.SubElement(head, 'style', attrib={'type':'text/css'}) style = etree.SubElement(head, 'style', attrib={'type':'text/css'})
style.text='\n'+self.css style.text='\n'+self.css
style.tail = '\n\n' style.tail = '\n\n'
Parser.save(self) return Parser.save(self)
def populate_toc(self, toc): def populate_toc(self, toc):
if self.level >= self.opts.max_toc_recursion: if self.level >= self.opts.max_toc_recursion:

View File

@ -1,4 +0,0 @@
from csscombine import csscombine
__all__ = ["csscapture", "csscombine", "cssparse"]

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -1,254 +1,254 @@
#!/usr/bin/env python #!/usr/bin/env python
"""cssutils - CSS Cascading Style Sheets library for Python """cssutils - CSS Cascading Style Sheets library for Python
Copyright (C) 2004-2008 Christof Hoeke Copyright (C) 2004-2008 Christof Hoeke
cssutils is free software: you can redistribute it and/or modify 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 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 the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details. GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License 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/>. 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! 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 : Based upon and partly implementing the following specifications :
`CSS 2.1 <http://www.w3.org/TR/CSS21/>`__ `CSS 2.1 <http://www.w3.org/TR/CSS21/>`__
General CSS rules and properties are defined here General CSS rules and properties are defined here
`CSS 2.1 Errata <http://www.w3.org/Style/css2-updates/CR-CSS21-20070719-errata.html>`__ `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 A few errata, mainly the definition of CHARSET_SYM tokens
`CSS3 Module: Syntax <http://www.w3.org/TR/css3-syntax/>`__ `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 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 <http://www.w3.org/TR/css3-mediaqueries/>`__
MediaQueries are part of ``stylesheets.MediaList`` since v0.9.4, used in @import and @media rules. MediaQueries are part of ``stylesheets.MediaList`` since v0.9.4, used in @import and @media rules.
`Namespaces <http://dev.w3.org/csswg/css3-namespace/>`__ `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 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/>`__ `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 ;) ) 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 Level 2 Style CSS <http://www.w3.org/TR/DOM-Level-2-Style/css.html>`__
DOM for package css DOM for package css
`DOM Level 2 Style Stylesheets <http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html>`__ `DOM Level 2 Style Stylesheets <http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html>`__
DOM for package stylesheets DOM for package stylesheets
`CSSOM <http://dev.w3.org/csswg/cssom/>`__ `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... 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. 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. 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. Please visit http://cthedot.de/cssutils/ for more details.
Tested with Python 2.5 on Windows Vista mainly. Tested with Python 2.5 on Windows Vista mainly.
This library may be used ``from cssutils import *`` which This library may be used ``from cssutils import *`` which
import subpackages ``css`` and ``stylesheets``, CSSParser and import subpackages ``css`` and ``stylesheets``, CSSParser and
CSSSerializer classes only. CSSSerializer classes only.
Usage may be:: Usage may be::
>>> from cssutils import * >>> from cssutils import *
>>> parser = CSSParser() >>> parser = CSSParser()
>>> sheet = parser.parseString(u'a { color: red}') >>> sheet = parser.parseString(u'a { color: red}')
>>> print sheet.cssText >>> print sheet.cssText
a { a {
color: red color: red
} }
""" """
__all__ = ['css', 'stylesheets', 'CSSParser', 'CSSSerializer'] __all__ = ['css', 'stylesheets', 'CSSParser', 'CSSSerializer']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__author__ = 'Christof Hoeke with contributions by Walter Doerwald' __author__ = 'Christof Hoeke with contributions by Walter Doerwald'
__date__ = '$LastChangedDate:: 2008-08-11 20:11:23 +0200 #$:' __date__ = '$LastChangedDate:: 2008-08-11 11:11:23 -0700 #$:'
VERSION = '0.9.5.1' VERSION = '0.9.5.1'
__version__ = '%s $Id: __init__.py 1426 2008-08-11 18:11:23Z cthedot $' % VERSION __version__ = '%s $Id: __init__.py 1426 2008-08-11 18:11:23Z cthedot $' % VERSION
import codec import codec
import xml.dom import xml.dom
# order of imports is important (partly circular) # order of imports is important (partly circular)
from helper import Deprecated from helper import Deprecated
import errorhandler import errorhandler
log = errorhandler.ErrorHandler() log = errorhandler.ErrorHandler()
import util import util
import css import css
import stylesheets import stylesheets
from parse import CSSParser from parse import CSSParser
from serialize import CSSSerializer from serialize import CSSSerializer
ser = CSSSerializer() ser = CSSSerializer()
# used by Selector defining namespace prefix '*' # used by Selector defining namespace prefix '*'
_ANYNS = -1 _ANYNS = -1
class DOMImplementationCSS(object): class DOMImplementationCSS(object):
""" """
This interface allows the DOM user to create a CSSStyleSheet This interface allows the DOM user to create a CSSStyleSheet
outside the context of a document. There is no way to associate outside the context of a document. There is no way to associate
the new CSSStyleSheet with a document in DOM Level 2. the new CSSStyleSheet with a document in DOM Level 2.
This class is its *own factory*, as it is given to This class is its *own factory*, as it is given to
xml.dom.registerDOMImplementation which simply calls it and receives xml.dom.registerDOMImplementation which simply calls it and receives
an instance of this class then. an instance of this class then.
""" """
_features = [ _features = [
('css', '1.0'), ('css', '1.0'),
('css', '2.0'), ('css', '2.0'),
('stylesheets', '1.0'), ('stylesheets', '1.0'),
('stylesheets', '2.0') ('stylesheets', '2.0')
] ]
def createCSSStyleSheet(self, title, media): def createCSSStyleSheet(self, title, media):
""" """
Creates a new CSSStyleSheet. Creates a new CSSStyleSheet.
title of type DOMString title of type DOMString
The advisory title. See also the Style Sheet Interfaces The advisory title. See also the Style Sheet Interfaces
section. section.
media of type DOMString media of type DOMString
The comma-separated list of media associated with the new style The comma-separated list of media associated with the new style
sheet. See also the Style Sheet Interfaces section. sheet. See also the Style Sheet Interfaces section.
returns returns
CSSStyleSheet: A new CSS style sheet. CSSStyleSheet: A new CSS style sheet.
TODO: DOMException TODO: DOMException
SYNTAX_ERR: Raised if the specified media string value has a SYNTAX_ERR: Raised if the specified media string value has a
syntax error and is unparsable. syntax error and is unparsable.
""" """
return css.CSSStyleSheet(title=title, media=media) return css.CSSStyleSheet(title=title, media=media)
def createDocument(self, *args): def createDocument(self, *args):
# not needed to HTML, also not for CSS? # not needed to HTML, also not for CSS?
raise NotImplementedError raise NotImplementedError
def createDocumentType(self, *args): def createDocumentType(self, *args):
# not needed to HTML, also not for CSS? # not needed to HTML, also not for CSS?
raise NotImplementedError raise NotImplementedError
def hasFeature(self, feature, version): def hasFeature(self, feature, version):
return (feature.lower(), unicode(version)) in self._features return (feature.lower(), unicode(version)) in self._features
xml.dom.registerDOMImplementation('cssutils', DOMImplementationCSS) xml.dom.registerDOMImplementation('cssutils', DOMImplementationCSS)
def parseString(*a, **k): def parseString(*a, **k):
return CSSParser().parseString(*a, **k) return CSSParser().parseString(*a, **k)
parseString.__doc__ = CSSParser.parseString.__doc__ parseString.__doc__ = CSSParser.parseString.__doc__
def parseFile(*a, **k): def parseFile(*a, **k):
return CSSParser().parseFile(*a, **k) return CSSParser().parseFile(*a, **k)
parseFile.__doc__ = CSSParser.parseFile.__doc__ parseFile.__doc__ = CSSParser.parseFile.__doc__
def parseUrl(*a, **k): def parseUrl(*a, **k):
return CSSParser().parseUrl(*a, **k) return CSSParser().parseUrl(*a, **k)
parseUrl.__doc__ = CSSParser.parseUrl.__doc__ parseUrl.__doc__ = CSSParser.parseUrl.__doc__
@Deprecated('Use cssutils.parseFile() instead.') @Deprecated('Use cssutils.parseFile() instead.')
def parse(*a, **k): def parse(*a, **k):
return parseFile(*a, **k) return parseFile(*a, **k)
parse.__doc__ = CSSParser.parse.__doc__ parse.__doc__ = CSSParser.parse.__doc__
# set "ser", default serializer # set "ser", default serializer
def setSerializer(serializer): def setSerializer(serializer):
""" """
sets the global serializer used by all class in cssutils sets the global serializer used by all class in cssutils
""" """
global ser global ser
ser = serializer ser = serializer
def getUrls(sheet): def getUrls(sheet):
""" """
Utility function to get all ``url(urlstring)`` values in Utility function to get all ``url(urlstring)`` values in
``CSSImportRules`` and ``CSSStyleDeclaration`` objects (properties) ``CSSImportRules`` and ``CSSStyleDeclaration`` objects (properties)
of given CSSStyleSheet ``sheet``. of given CSSStyleSheet ``sheet``.
This function is a generator. The url values exclude ``url(`` and ``)`` This function is a generator. The url values exclude ``url(`` and ``)``
and surrounding single or double quotes. and surrounding single or double quotes.
""" """
for importrule in (r for r in sheet if r.type == r.IMPORT_RULE): for importrule in (r for r in sheet if r.type == r.IMPORT_RULE):
yield importrule.href yield importrule.href
def getUrl(v): def getUrl(v):
if v.CSS_PRIMITIVE_VALUE == v.cssValueType and\ if v.CSS_PRIMITIVE_VALUE == v.cssValueType and\
v.CSS_URI == v.primitiveType: v.CSS_URI == v.primitiveType:
return v.getStringValue() return v.getStringValue()
def styleDeclarations(base): def styleDeclarations(base):
"recursive generator to find all CSSStyleDeclarations" "recursive generator to find all CSSStyleDeclarations"
if hasattr(base, 'cssRules'): if hasattr(base, 'cssRules'):
for rule in base.cssRules: for rule in base.cssRules:
for s in styleDeclarations(rule): for s in styleDeclarations(rule):
yield s yield s
elif hasattr(base, 'style'): elif hasattr(base, 'style'):
yield base.style yield base.style
for style in styleDeclarations(sheet): for style in styleDeclarations(sheet):
for p in style.getProperties(all=True): for p in style.getProperties(all=True):
v = p.cssValue v = p.cssValue
if v.CSS_VALUE_LIST == v.cssValueType: if v.CSS_VALUE_LIST == v.cssValueType:
for item in v: for item in v:
u = getUrl(item) u = getUrl(item)
if u is not None: if u is not None:
yield u yield u
elif v.CSS_PRIMITIVE_VALUE == v.cssValueType: elif v.CSS_PRIMITIVE_VALUE == v.cssValueType:
u = getUrl(v) u = getUrl(v)
if u is not None: if u is not None:
yield u yield u
def replaceUrls(sheet, replacer): def replaceUrls(sheet, replacer):
""" """
Utility function to replace all ``url(urlstring)`` values in Utility function to replace all ``url(urlstring)`` values in
``CSSImportRules`` and ``CSSStyleDeclaration`` objects (properties) ``CSSImportRules`` and ``CSSStyleDeclaration`` objects (properties)
of given CSSStyleSheet ``sheet``. of given CSSStyleSheet ``sheet``.
``replacer`` must be a function which is called with a single ``replacer`` must be a function which is called with a single
argument ``urlstring`` which is the current value of url() argument ``urlstring`` which is the current value of url()
excluding ``url(`` and ``)`` and surrounding single or double quotes. excluding ``url(`` and ``)`` and surrounding single or double quotes.
""" """
for importrule in (r for r in sheet if r.type == r.IMPORT_RULE): for importrule in (r for r in sheet if r.type == r.IMPORT_RULE):
importrule.href = replacer(importrule.href) importrule.href = replacer(importrule.href)
def setProperty(v): def setProperty(v):
if v.CSS_PRIMITIVE_VALUE == v.cssValueType and\ if v.CSS_PRIMITIVE_VALUE == v.cssValueType and\
v.CSS_URI == v.primitiveType: v.CSS_URI == v.primitiveType:
v.setStringValue(v.CSS_URI, v.setStringValue(v.CSS_URI,
replacer(v.getStringValue())) replacer(v.getStringValue()))
def styleDeclarations(base): def styleDeclarations(base):
"recursive generator to find all CSSStyleDeclarations" "recursive generator to find all CSSStyleDeclarations"
if hasattr(base, 'cssRules'): if hasattr(base, 'cssRules'):
for rule in base.cssRules: for rule in base.cssRules:
for s in styleDeclarations(rule): for s in styleDeclarations(rule):
yield s yield s
elif hasattr(base, 'style'): elif hasattr(base, 'style'):
yield base.style yield base.style
for style in styleDeclarations(sheet): for style in styleDeclarations(sheet):
for p in style.getProperties(all=True): for p in style.getProperties(all=True):
v = p.cssValue v = p.cssValue
if v.CSS_VALUE_LIST == v.cssValueType: if v.CSS_VALUE_LIST == v.cssValueType:
for item in v: for item in v:
setProperty(item) setProperty(item)
elif v.CSS_PRIMITIVE_VALUE == v.cssValueType: elif v.CSS_PRIMITIVE_VALUE == v.cssValueType:
setProperty(v) setProperty(v)
if __name__ == '__main__': if __name__ == '__main__':
print __doc__ print __doc__

File diff suppressed because it is too large Load Diff

View File

@ -1,63 +1,63 @@
""" """
Document Object Model Level 2 CSS Document Object Model Level 2 CSS
http://www.w3.org/TR/2000/PR-DOM-Level-2-Style-20000927/css.html http://www.w3.org/TR/2000/PR-DOM-Level-2-Style-20000927/css.html
currently implemented currently implemented
- CSSStyleSheet - CSSStyleSheet
- CSSRuleList - CSSRuleList
- CSSRule - CSSRule
- CSSComment (cssutils addon) - CSSComment (cssutils addon)
- CSSCharsetRule - CSSCharsetRule
- CSSFontFaceRule - CSSFontFaceRule
- CSSImportRule - CSSImportRule
- CSSMediaRule - CSSMediaRule
- CSSNamespaceRule (WD) - CSSNamespaceRule (WD)
- CSSPageRule - CSSPageRule
- CSSStyleRule - CSSStyleRule
- CSSUnkownRule - CSSUnkownRule
- Selector and SelectorList - Selector and SelectorList
- CSSStyleDeclaration - CSSStyleDeclaration
- CSS2Properties - CSS2Properties
- CSSValue - CSSValue
- CSSPrimitiveValue - CSSPrimitiveValue
- CSSValueList - CSSValueList
todo todo
- RGBColor, Rect, Counter - RGBColor, Rect, Counter
""" """
__all__ = [ __all__ = [
'CSSStyleSheet', 'CSSStyleSheet',
'CSSRuleList', 'CSSRuleList',
'CSSRule', 'CSSRule',
'CSSComment', 'CSSComment',
'CSSCharsetRule', 'CSSCharsetRule',
'CSSFontFaceRule' 'CSSFontFaceRule'
'CSSImportRule', 'CSSImportRule',
'CSSMediaRule', 'CSSMediaRule',
'CSSNamespaceRule', 'CSSNamespaceRule',
'CSSPageRule', 'CSSPageRule',
'CSSStyleRule', 'CSSStyleRule',
'CSSUnknownRule', 'CSSUnknownRule',
'Selector', 'SelectorList', 'Selector', 'SelectorList',
'CSSStyleDeclaration', 'Property', 'CSSStyleDeclaration', 'Property',
'CSSValue', 'CSSPrimitiveValue', 'CSSValueList' 'CSSValue', 'CSSPrimitiveValue', 'CSSValueList'
] ]
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: __init__.py 1116 2008-03-05 13:52:23Z cthedot $' __version__ = '$Id: __init__.py 1116 2008-03-05 13:52:23Z cthedot $'
from cssstylesheet import * from cssstylesheet import *
from cssrulelist import * from cssrulelist import *
from cssrule import * from cssrule import *
from csscomment import * from csscomment import *
from csscharsetrule import * from csscharsetrule import *
from cssfontfacerule import * from cssfontfacerule import *
from cssimportrule import * from cssimportrule import *
from cssmediarule import * from cssmediarule import *
from cssnamespacerule import * from cssnamespacerule import *
from csspagerule import * from csspagerule import *
from cssstylerule import * from cssstylerule import *
from cssunknownrule import * from cssunknownrule import *
from selector import * from selector import *
from selectorlist import * from selectorlist import *
from cssstyledeclaration import * from cssstyledeclaration import *
from cssvalue import * from cssvalue import *

View File

@ -1,165 +1,165 @@
"""CSSCharsetRule implements DOM Level 2 CSS CSSCharsetRule. """CSSCharsetRule implements DOM Level 2 CSS CSSCharsetRule.
TODO: TODO:
- check encoding syntax and not codecs.lookup? - check encoding syntax and not codecs.lookup?
""" """
__all__ = ['CSSCharsetRule'] __all__ = ['CSSCharsetRule']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: csscharsetrule.py 1170 2008-03-20 17:42:07Z cthedot $' __version__ = '$Id: csscharsetrule.py 1170 2008-03-20 17:42:07Z cthedot $'
import codecs import codecs
import xml.dom import xml.dom
import cssrule import cssrule
import cssutils import cssutils
class CSSCharsetRule(cssrule.CSSRule): class CSSCharsetRule(cssrule.CSSRule):
""" """
The CSSCharsetRule interface represents an @charset rule in a CSS style The CSSCharsetRule interface represents an @charset rule in a CSS style
sheet. The value of the encoding attribute does not affect the encoding 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 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 (also in Python?). After a stylesheet is loaded, the value of the
encoding attribute is the value found in the @charset rule. If there encoding attribute is the value found in the @charset rule. If there
was no @charset in the original document, then no CSSCharsetRule is 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 created. The value of the encoding attribute may also be used as a hint
for the encoding used on serialization of the style sheet. for the encoding used on serialization of the style sheet.
The value of the @charset rule (and therefore of the CSSCharsetRule) The value of the @charset rule (and therefore of the CSSCharsetRule)
may not correspond to the encoding the document actually came in; may not correspond to the encoding the document actually came in;
character encoding information e.g. in an HTTP header, has priority character encoding information e.g. in an HTTP header, has priority
(see CSS document representation) but this is not reflected in the (see CSS document representation) but this is not reflected in the
CSSCharsetRule. CSSCharsetRule.
Properties Properties
========== ==========
cssText: of type DOMString cssText: of type DOMString
The parsable textual representation of this rule The parsable textual representation of this rule
encoding: of type DOMString encoding: of type DOMString
The encoding information used in this @charset rule. The encoding information used in this @charset rule.
Inherits properties from CSSRule Inherits properties from CSSRule
Format Format
====== ======
charsetrule: charsetrule:
CHARSET_SYM S* STRING S* ';' CHARSET_SYM S* STRING S* ';'
BUT: Only valid format is: BUT: Only valid format is:
@charset "ENCODING"; @charset "ENCODING";
""" """
type = property(lambda self: cssrule.CSSRule.CHARSET_RULE) type = property(lambda self: cssrule.CSSRule.CHARSET_RULE)
def __init__(self, encoding=None, parentRule=None, def __init__(self, encoding=None, parentRule=None,
parentStyleSheet=None, readonly=False): parentStyleSheet=None, readonly=False):
""" """
encoding: encoding:
a valid character encoding a valid character encoding
readonly: readonly:
defaults to False, not used yet defaults to False, not used yet
if readonly allows setting of properties in constructor only if readonly allows setting of properties in constructor only
""" """
super(CSSCharsetRule, self).__init__(parentRule=parentRule, super(CSSCharsetRule, self).__init__(parentRule=parentRule,
parentStyleSheet=parentStyleSheet) parentStyleSheet=parentStyleSheet)
self._atkeyword = '@charset' self._atkeyword = '@charset'
self._encoding = None self._encoding = None
if encoding: if encoding:
self.encoding = encoding self.encoding = encoding
self._readonly = readonly self._readonly = readonly
def _getCssText(self): def _getCssText(self):
"""returns serialized property cssText""" """returns serialized property cssText"""
return cssutils.ser.do_CSSCharsetRule(self) return cssutils.ser.do_CSSCharsetRule(self)
def _setCssText(self, cssText): def _setCssText(self, cssText):
""" """
DOMException on setting DOMException on setting
- SYNTAX_ERR: (self) - SYNTAX_ERR: (self)
Raised if the specified CSS string value has a syntax error and Raised if the specified CSS string value has a syntax error and
is unparsable. is unparsable.
- INVALID_MODIFICATION_ERR: (self) - INVALID_MODIFICATION_ERR: (self)
Raised if the specified CSS string value represents a different Raised if the specified CSS string value represents a different
type of rule than the current one. type of rule than the current one.
- HIERARCHY_REQUEST_ERR: (CSSStylesheet) - HIERARCHY_REQUEST_ERR: (CSSStylesheet)
Raised if the rule cannot be inserted at this point in the Raised if the rule cannot be inserted at this point in the
style sheet. style sheet.
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule) - NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
Raised if the rule is readonly. Raised if the rule is readonly.
""" """
super(CSSCharsetRule, self)._setCssText(cssText) super(CSSCharsetRule, self)._setCssText(cssText)
wellformed = True wellformed = True
tokenizer = self._tokenize2(cssText) tokenizer = self._tokenize2(cssText)
if self._type(self._nexttoken(tokenizer)) != self._prods.CHARSET_SYM: if self._type(self._nexttoken(tokenizer)) != self._prods.CHARSET_SYM:
wellformed = False wellformed = False
self._log.error(u'CSSCharsetRule must start with "@charset "', self._log.error(u'CSSCharsetRule must start with "@charset "',
error=xml.dom.InvalidModificationErr) error=xml.dom.InvalidModificationErr)
encodingtoken = self._nexttoken(tokenizer) encodingtoken = self._nexttoken(tokenizer)
encodingtype = self._type(encodingtoken) encodingtype = self._type(encodingtoken)
encoding = self._stringtokenvalue(encodingtoken) encoding = self._stringtokenvalue(encodingtoken)
if self._prods.STRING != encodingtype or not encoding: if self._prods.STRING != encodingtype or not encoding:
wellformed = False wellformed = False
self._log.error(u'CSSCharsetRule: no encoding found; %r.' % self._log.error(u'CSSCharsetRule: no encoding found; %r.' %
self._valuestr(cssText)) self._valuestr(cssText))
semicolon = self._tokenvalue(self._nexttoken(tokenizer)) semicolon = self._tokenvalue(self._nexttoken(tokenizer))
EOFtype = self._type(self._nexttoken(tokenizer)) EOFtype = self._type(self._nexttoken(tokenizer))
if u';' != semicolon or EOFtype not in ('EOF', None): if u';' != semicolon or EOFtype not in ('EOF', None):
wellformed = False wellformed = False
self._log.error(u'CSSCharsetRule: Syntax Error: %r.' % self._log.error(u'CSSCharsetRule: Syntax Error: %r.' %
self._valuestr(cssText)) self._valuestr(cssText))
if wellformed: if wellformed:
self.encoding = encoding self.encoding = encoding
cssText = property(fget=_getCssText, fset=_setCssText, cssText = property(fget=_getCssText, fset=_setCssText,
doc="(DOM) The parsable textual representation.") doc="(DOM) The parsable textual representation.")
def _setEncoding(self, encoding): def _setEncoding(self, encoding):
""" """
DOMException on setting DOMException on setting
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule) - NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
Raised if this encoding rule is readonly. Raised if this encoding rule is readonly.
- SYNTAX_ERR: (self) - SYNTAX_ERR: (self)
Raised if the specified encoding value has a syntax error and Raised if the specified encoding value has a syntax error and
is unparsable. is unparsable.
Currently only valid Python encodings are allowed. Currently only valid Python encodings are allowed.
""" """
self._checkReadonly() self._checkReadonly()
tokenizer = self._tokenize2(encoding) tokenizer = self._tokenize2(encoding)
encodingtoken = self._nexttoken(tokenizer) encodingtoken = self._nexttoken(tokenizer)
unexpected = self._nexttoken(tokenizer) unexpected = self._nexttoken(tokenizer)
valid = True valid = True
if not encodingtoken or unexpected or\ if not encodingtoken or unexpected or\
self._prods.IDENT != self._type(encodingtoken): self._prods.IDENT != self._type(encodingtoken):
valid = False valid = False
self._log.error( self._log.error(
'CSSCharsetRule: Syntax Error in encoding value %r.' % 'CSSCharsetRule: Syntax Error in encoding value %r.' %
encoding) encoding)
else: else:
try: try:
codecs.lookup(encoding) codecs.lookup(encoding)
except LookupError: except LookupError:
valid = False valid = False
self._log.error('CSSCharsetRule: Unknown (Python) encoding %r.' % self._log.error('CSSCharsetRule: Unknown (Python) encoding %r.' %
encoding) encoding)
else: else:
self._encoding = encoding.lower() self._encoding = encoding.lower()
encoding = property(lambda self: self._encoding, _setEncoding, encoding = property(lambda self: self._encoding, _setEncoding,
doc="(DOM)The encoding information used in this @charset rule.") doc="(DOM)The encoding information used in this @charset rule.")
wellformed = property(lambda self: bool(self.encoding)) wellformed = property(lambda self: bool(self.encoding))
def __repr__(self): def __repr__(self):
return "cssutils.css.%s(encoding=%r)" % ( return "cssutils.css.%s(encoding=%r)" % (
self.__class__.__name__, self.encoding) self.__class__.__name__, self.encoding)
def __str__(self): def __str__(self):
return "<cssutils.css.%s object encoding=%r at 0x%x>" % ( return "<cssutils.css.%s object encoding=%r at 0x%x>" % (
self.__class__.__name__, self.encoding, id(self)) self.__class__.__name__, self.encoding, id(self))

View File

@ -1,92 +1,92 @@
"""CSSComment is not defined in DOM Level 2 at all but a cssutils defined """CSSComment is not defined in DOM Level 2 at all but a cssutils defined
class only. class only.
Implements CSSRule which is also extended for a CSSComment rule type Implements CSSRule which is also extended for a CSSComment rule type
""" """
__all__ = ['CSSComment'] __all__ = ['CSSComment']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: csscomment.py 1170 2008-03-20 17:42:07Z cthedot $' __version__ = '$Id: csscomment.py 1170 2008-03-20 17:42:07Z cthedot $'
import xml.dom import xml.dom
import cssrule import cssrule
import cssutils import cssutils
class CSSComment(cssrule.CSSRule): class CSSComment(cssrule.CSSRule):
""" """
(cssutils) a CSS comment (cssutils) a CSS comment
Properties Properties
========== ==========
cssText: of type DOMString cssText: of type DOMString
The comment text including comment delimiters The comment text including comment delimiters
Inherits properties from CSSRule Inherits properties from CSSRule
Format Format
====== ======
:: ::
/*...*/ /*...*/
""" """
type = property(lambda self: cssrule.CSSRule.COMMENT) # value = -1 type = property(lambda self: cssrule.CSSRule.COMMENT) # value = -1
# constant but needed: # constant but needed:
wellformed = True wellformed = True
def __init__(self, cssText=None, parentRule=None, def __init__(self, cssText=None, parentRule=None,
parentStyleSheet=None, readonly=False): parentStyleSheet=None, readonly=False):
super(CSSComment, self).__init__(parentRule=parentRule, super(CSSComment, self).__init__(parentRule=parentRule,
parentStyleSheet=parentStyleSheet) parentStyleSheet=parentStyleSheet)
self._cssText = None self._cssText = None
if cssText: if cssText:
self._setCssText(cssText) self._setCssText(cssText)
self._readonly = readonly self._readonly = readonly
def _getCssText(self): def _getCssText(self):
"""returns serialized property cssText""" """returns serialized property cssText"""
return cssutils.ser.do_CSSComment(self) return cssutils.ser.do_CSSComment(self)
def _setCssText(self, cssText): def _setCssText(self, cssText):
""" """
cssText cssText
textual text to set or tokenlist which is not tokenized textual text to set or tokenlist which is not tokenized
anymore. May also be a single token for this rule anymore. May also be a single token for this rule
parser parser
if called from cssparser directly this is Parser instance if called from cssparser directly this is Parser instance
DOMException on setting DOMException on setting
- SYNTAX_ERR: (self) - SYNTAX_ERR: (self)
Raised if the specified CSS string value has a syntax error and Raised if the specified CSS string value has a syntax error and
is unparsable. is unparsable.
- INVALID_MODIFICATION_ERR: (self) - INVALID_MODIFICATION_ERR: (self)
Raised if the specified CSS string value represents a different Raised if the specified CSS string value represents a different
type of rule than the current one. type of rule than the current one.
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule) - NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
Raised if the rule is readonly. Raised if the rule is readonly.
""" """
super(CSSComment, self)._setCssText(cssText) super(CSSComment, self)._setCssText(cssText)
tokenizer = self._tokenize2(cssText) tokenizer = self._tokenize2(cssText)
commenttoken = self._nexttoken(tokenizer) commenttoken = self._nexttoken(tokenizer)
unexpected = self._nexttoken(tokenizer) unexpected = self._nexttoken(tokenizer)
if not commenttoken or\ if not commenttoken or\
self._type(commenttoken) != self._prods.COMMENT or\ self._type(commenttoken) != self._prods.COMMENT or\
unexpected: unexpected:
self._log.error(u'CSSComment: Not a CSSComment: %r' % self._log.error(u'CSSComment: Not a CSSComment: %r' %
self._valuestr(cssText), self._valuestr(cssText),
error=xml.dom.InvalidModificationErr) error=xml.dom.InvalidModificationErr)
else: else:
self._cssText = self._tokenvalue(commenttoken) self._cssText = self._tokenvalue(commenttoken)
cssText = property(_getCssText, _setCssText, cssText = property(_getCssText, _setCssText,
doc=u"(cssutils) Textual representation of this comment") doc=u"(cssutils) Textual representation of this comment")
def __repr__(self): def __repr__(self):
return "cssutils.css.%s(cssText=%r)" % ( return "cssutils.css.%s(cssText=%r)" % (
self.__class__.__name__, self.cssText) self.__class__.__name__, self.cssText)
def __str__(self): def __str__(self):
return "<cssutils.css.%s object cssText=%r at 0x%x>" % ( return "<cssutils.css.%s object cssText=%r at 0x%x>" % (
self.__class__.__name__, self.cssText, id(self)) self.__class__.__name__, self.cssText, id(self))

View File

@ -1,399 +1,399 @@
"""CSSImportRule implements DOM Level 2 CSS CSSImportRule. """CSSImportRule implements DOM Level 2 CSS CSSImportRule.
plus: plus:
``name`` property ``name`` property
http://www.w3.org/TR/css3-cascade/#cascading http://www.w3.org/TR/css3-cascade/#cascading
""" """
__all__ = ['CSSImportRule'] __all__ = ['CSSImportRule']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: cssimportrule.py 1401 2008-07-29 21:07:54Z cthedot $' __version__ = '$Id: cssimportrule.py 1401 2008-07-29 21:07:54Z cthedot $'
import os import os
import urllib import urllib
import urlparse import urlparse
import xml.dom import xml.dom
import cssrule import cssrule
import cssutils import cssutils
class CSSImportRule(cssrule.CSSRule): class CSSImportRule(cssrule.CSSRule):
""" """
Represents an @import rule within a CSS style sheet. The @import rule Represents an @import rule within a CSS style sheet. The @import rule
is used to import style rules from other style sheets. is used to import style rules from other style sheets.
Properties Properties
========== ==========
atkeyword: (cssutils only) atkeyword: (cssutils only)
the literal keyword used the literal keyword used
cssText: of type DOMString cssText: of type DOMString
The parsable textual representation of this rule The parsable textual representation of this rule
href: of type DOMString, (DOM readonly, cssutils also writable) href: of type DOMString, (DOM readonly, cssutils also writable)
The location of the style sheet to be imported. The attribute will The location of the style sheet to be imported. The attribute will
not contain the url(...) specifier around the URI. not contain the url(...) specifier around the URI.
hreftype: 'uri' (serializer default) or 'string' (cssutils only) hreftype: 'uri' (serializer default) or 'string' (cssutils only)
The original type of href, not really relevant as it may be The original type of href, not really relevant as it may be
reconfigured in the serializer but it is kept anyway reconfigured in the serializer but it is kept anyway
media: of type stylesheets::MediaList (DOM readonly) media: of type stylesheets::MediaList (DOM readonly)
A list of media types for this rule of type MediaList. A list of media types for this rule of type MediaList.
name: name:
An optional name used for cascading An optional name used for cascading
styleSheet: of type CSSStyleSheet (DOM readonly) styleSheet: of type CSSStyleSheet (DOM readonly)
The style sheet referred to by this rule. The value of this 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 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 it will not be loaded (e.g. if the stylesheet is for a media type
not supported by the user agent). not supported by the user agent).
Inherits properties from CSSRule Inherits properties from CSSRule
Format Format
====== ======
import import
: IMPORT_SYM S* : IMPORT_SYM S*
[STRING|URI] S* [ medium [ COMMA S* medium]* ]? S* STRING? S* ';' S* [STRING|URI] S* [ medium [ COMMA S* medium]* ]? S* STRING? S* ';' S*
; ;
""" """
type = property(lambda self: cssrule.CSSRule.IMPORT_RULE) type = property(lambda self: cssrule.CSSRule.IMPORT_RULE)
def __init__(self, href=None, mediaText=u'all', name=None, def __init__(self, href=None, mediaText=u'all', name=None,
parentRule=None, parentStyleSheet=None, readonly=False): parentRule=None, parentStyleSheet=None, readonly=False):
""" """
if readonly allows setting of properties in constructor only if readonly allows setting of properties in constructor only
Do not use as positional but as keyword attributes only! Do not use as positional but as keyword attributes only!
href href
location of the style sheet to be imported. location of the style sheet to be imported.
mediaText mediaText
A list of media types for which this style sheet may be used A list of media types for which this style sheet may be used
as a string as a string
""" """
super(CSSImportRule, self).__init__(parentRule=parentRule, super(CSSImportRule, self).__init__(parentRule=parentRule,
parentStyleSheet=parentStyleSheet) parentStyleSheet=parentStyleSheet)
self._atkeyword = u'@import' self._atkeyword = u'@import'
self.hreftype = None self.hreftype = None
self._styleSheet = None self._styleSheet = None
self._href = None self._href = None
self.href = href self.href = href
self._media = cssutils.stylesheets.MediaList() self._media = cssutils.stylesheets.MediaList()
if mediaText: if mediaText:
self._media.mediaText = mediaText self._media.mediaText = mediaText
self._name = name self._name = name
seq = self._tempSeq() seq = self._tempSeq()
seq.append(self.href, 'href') seq.append(self.href, 'href')
seq.append(self.media, 'media') seq.append(self.media, 'media')
seq.append(self.name, 'name') seq.append(self.name, 'name')
self._setSeq(seq) self._setSeq(seq)
self._readonly = readonly self._readonly = readonly
_usemedia = property(lambda self: self.media.mediaText not in (u'', u'all'), _usemedia = property(lambda self: self.media.mediaText not in (u'', u'all'),
doc="if self._media is used (or simply empty)") doc="if self._media is used (or simply empty)")
def _getCssText(self): def _getCssText(self):
""" """
returns serialized property cssText returns serialized property cssText
""" """
return cssutils.ser.do_CSSImportRule(self) return cssutils.ser.do_CSSImportRule(self)
def _setCssText(self, cssText): def _setCssText(self, cssText):
""" """
DOMException on setting DOMException on setting
- HIERARCHY_REQUEST_ERR: (CSSStylesheet) - HIERARCHY_REQUEST_ERR: (CSSStylesheet)
Raised if the rule cannot be inserted at this point in the Raised if the rule cannot be inserted at this point in the
style sheet. style sheet.
- INVALID_MODIFICATION_ERR: (self) - INVALID_MODIFICATION_ERR: (self)
Raised if the specified CSS string value represents a different Raised if the specified CSS string value represents a different
type of rule than the current one. type of rule than the current one.
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule) - NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
Raised if the rule is readonly. Raised if the rule is readonly.
- SYNTAX_ERR: (self) - SYNTAX_ERR: (self)
Raised if the specified CSS string value has a syntax error and Raised if the specified CSS string value has a syntax error and
is unparsable. is unparsable.
""" """
super(CSSImportRule, self)._setCssText(cssText) super(CSSImportRule, self)._setCssText(cssText)
tokenizer = self._tokenize2(cssText) tokenizer = self._tokenize2(cssText)
attoken = self._nexttoken(tokenizer, None) attoken = self._nexttoken(tokenizer, None)
if self._type(attoken) != self._prods.IMPORT_SYM: if self._type(attoken) != self._prods.IMPORT_SYM:
self._log.error(u'CSSImportRule: No CSSImportRule found: %s' % self._log.error(u'CSSImportRule: No CSSImportRule found: %s' %
self._valuestr(cssText), self._valuestr(cssText),
error=xml.dom.InvalidModificationErr) error=xml.dom.InvalidModificationErr)
else: else:
# for closures: must be a mutable # for closures: must be a mutable
new = {'keyword': self._tokenvalue(attoken), new = {'keyword': self._tokenvalue(attoken),
'href': None, 'href': None,
'hreftype': None, 'hreftype': None,
'media': None, 'media': None,
'name': None, 'name': None,
'wellformed': True 'wellformed': True
} }
def __doname(seq, token): def __doname(seq, token):
# called by _string or _ident # called by _string or _ident
new['name'] = self._stringtokenvalue(token) new['name'] = self._stringtokenvalue(token)
seq.append(new['name'], 'name') seq.append(new['name'], 'name')
return ';' return ';'
def _string(expected, seq, token, tokenizer=None): def _string(expected, seq, token, tokenizer=None):
if 'href' == expected: if 'href' == expected:
# href # href
new['href'] = self._stringtokenvalue(token) new['href'] = self._stringtokenvalue(token)
new['hreftype'] = 'string' new['hreftype'] = 'string'
seq.append(new['href'], 'href') seq.append(new['href'], 'href')
return 'media name ;' return 'media name ;'
elif 'name' in expected: elif 'name' in expected:
# name # name
return __doname(seq, token) return __doname(seq, token)
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error( self._log.error(
u'CSSImportRule: Unexpected string.', token) u'CSSImportRule: Unexpected string.', token)
return expected return expected
def _uri(expected, seq, token, tokenizer=None): def _uri(expected, seq, token, tokenizer=None):
# href # href
if 'href' == expected: if 'href' == expected:
uri = self._uritokenvalue(token) uri = self._uritokenvalue(token)
new['hreftype'] = 'uri' new['hreftype'] = 'uri'
new['href'] = uri new['href'] = uri
seq.append(new['href'], 'href') seq.append(new['href'], 'href')
return 'media name ;' return 'media name ;'
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error( self._log.error(
u'CSSImportRule: Unexpected URI.', token) u'CSSImportRule: Unexpected URI.', token)
return expected return expected
def _ident(expected, seq, token, tokenizer=None): def _ident(expected, seq, token, tokenizer=None):
# medialist ending with ; which is checked upon too # medialist ending with ; which is checked upon too
if expected.startswith('media'): if expected.startswith('media'):
mediatokens = self._tokensupto2( mediatokens = self._tokensupto2(
tokenizer, importmediaqueryendonly=True) tokenizer, importmediaqueryendonly=True)
mediatokens.insert(0, token) # push found token mediatokens.insert(0, token) # push found token
last = mediatokens.pop() # retrieve ; last = mediatokens.pop() # retrieve ;
lastval, lasttyp = self._tokenvalue(last), self._type(last) lastval, lasttyp = self._tokenvalue(last), self._type(last)
if lastval != u';' and lasttyp not in ('EOF', self._prods.STRING): if lastval != u';' and lasttyp not in ('EOF', self._prods.STRING):
new['wellformed'] = False new['wellformed'] = False
self._log.error(u'CSSImportRule: No ";" found: %s' % self._log.error(u'CSSImportRule: No ";" found: %s' %
self._valuestr(cssText), token=token) self._valuestr(cssText), token=token)
media = cssutils.stylesheets.MediaList() media = cssutils.stylesheets.MediaList()
media.mediaText = mediatokens media.mediaText = mediatokens
if media.wellformed: if media.wellformed:
new['media'] = media new['media'] = media
seq.append(media, 'media') seq.append(media, 'media')
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error(u'CSSImportRule: Invalid MediaList: %s' % self._log.error(u'CSSImportRule: Invalid MediaList: %s' %
self._valuestr(cssText), token=token) self._valuestr(cssText), token=token)
if lasttyp == self._prods.STRING: if lasttyp == self._prods.STRING:
# name # name
return __doname(seq, last) return __doname(seq, last)
else: else:
return 'EOF' # ';' is token "last" return 'EOF' # ';' is token "last"
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error( self._log.error(
u'CSSImportRule: Unexpected ident.', token) u'CSSImportRule: Unexpected ident.', token)
return expected return expected
def _char(expected, seq, token, tokenizer=None): def _char(expected, seq, token, tokenizer=None):
# final ; # final ;
val = self._tokenvalue(token) val = self._tokenvalue(token)
if expected.endswith(';') and u';' == val: if expected.endswith(';') and u';' == val:
return 'EOF' return 'EOF'
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error( self._log.error(
u'CSSImportRule: Unexpected char.', token) u'CSSImportRule: Unexpected char.', token)
return expected return expected
# import : IMPORT_SYM S* [STRING|URI] # import : IMPORT_SYM S* [STRING|URI]
# S* [ medium [ ',' S* medium]* ]? ';' S* # S* [ medium [ ',' S* medium]* ]? ';' S*
# STRING? # see http://www.w3.org/TR/css3-cascade/#cascading # STRING? # see http://www.w3.org/TR/css3-cascade/#cascading
# ; # ;
newseq = self._tempSeq() newseq = self._tempSeq()
wellformed, expected = self._parse(expected='href', wellformed, expected = self._parse(expected='href',
seq=newseq, tokenizer=tokenizer, seq=newseq, tokenizer=tokenizer,
productions={'STRING': _string, productions={'STRING': _string,
'URI': _uri, 'URI': _uri,
'IDENT': _ident, 'IDENT': _ident,
'CHAR': _char}, 'CHAR': _char},
new=new) new=new)
# wellformed set by parse # wellformed set by parse
wellformed = wellformed and new['wellformed'] wellformed = wellformed and new['wellformed']
# post conditions # post conditions
if not new['href']: if not new['href']:
wellformed = False wellformed = False
self._log.error(u'CSSImportRule: No href found: %s' % self._log.error(u'CSSImportRule: No href found: %s' %
self._valuestr(cssText)) self._valuestr(cssText))
if expected != 'EOF': if expected != 'EOF':
wellformed = False wellformed = False
self._log.error(u'CSSImportRule: No ";" found: %s' % self._log.error(u'CSSImportRule: No ";" found: %s' %
self._valuestr(cssText)) self._valuestr(cssText))
# set all # set all
if wellformed: if wellformed:
self.atkeyword = new['keyword'] self.atkeyword = new['keyword']
self.hreftype = new['hreftype'] self.hreftype = new['hreftype']
if new['media']: if new['media']:
# use same object # use same object
self.media.mediaText = new['media'].mediaText self.media.mediaText = new['media'].mediaText
# put it in newseq too # put it in newseq too
for index, x in enumerate(newseq): for index, x in enumerate(newseq):
if x.type == 'media': if x.type == 'media':
newseq.replace(index, self.media, newseq.replace(index, self.media,
x.type, x.line, x.col) x.type, x.line, x.col)
break break
else: else:
# reset media # reset media
self.media.mediaText = u'all' self.media.mediaText = u'all'
newseq.append(self.media, 'media') newseq.append(self.media, 'media')
self.name = new['name'] self.name = new['name']
self._setSeq(newseq) self._setSeq(newseq)
self.href = new['href'] self.href = new['href']
if self.styleSheet: if self.styleSheet:
# title is set by href # title is set by href
#self.styleSheet._href = self.href #self.styleSheet._href = self.href
self.styleSheet._parentStyleSheet = self.parentStyleSheet self.styleSheet._parentStyleSheet = self.parentStyleSheet
cssText = property(fget=_getCssText, fset=_setCssText, cssText = property(fget=_getCssText, fset=_setCssText,
doc="(DOM attribute) The parsable textual representation.") doc="(DOM attribute) The parsable textual representation.")
def _setHref(self, href): def _setHref(self, href):
# update seq # update seq
for i, item in enumerate(self.seq): for i, item in enumerate(self.seq):
val, typ = item.value, item.type val, typ = item.value, item.type
if 'href' == typ: if 'href' == typ:
self._seq[i] = (href, typ, item.line, item.col) self._seq[i] = (href, typ, item.line, item.col)
break break
else: else:
seq = self._tempSeq() seq = self._tempSeq()
seq.append(self.href, 'href') seq.append(self.href, 'href')
self._setSeq(seq) self._setSeq(seq)
# set new href # set new href
self._href = href self._href = href
if not self.styleSheet: if not self.styleSheet:
# set only if not set before # set only if not set before
self.__setStyleSheet() self.__setStyleSheet()
href = property(lambda self: self._href, _setHref, href = property(lambda self: self._href, _setHref,
doc="Location of the style sheet to be imported.") doc="Location of the style sheet to be imported.")
media = property(lambda self: self._media, media = property(lambda self: self._media,
doc=u"(DOM readonly) A list of media types for this rule" doc=u"(DOM readonly) A list of media types for this rule"
" of type MediaList") " of type MediaList")
def _setName(self, name): def _setName(self, name):
"""raises xml.dom.SyntaxErr if name is not a string""" """raises xml.dom.SyntaxErr if name is not a string"""
if isinstance(name, basestring) or name is None: if isinstance(name, basestring) or name is None:
# "" or '' # "" or ''
if not name: if not name:
name = None name = None
# update seq # update seq
for i, item in enumerate(self.seq): for i, item in enumerate(self.seq):
val, typ = item.value, item.type val, typ = item.value, item.type
if 'name' == typ: if 'name' == typ:
self._seq[i] = (name, typ, item.line, item.col) self._seq[i] = (name, typ, item.line, item.col)
break break
else: else:
# append # append
seq = self._tempSeq() seq = self._tempSeq()
for item in self.seq: for item in self.seq:
# copy current seq # copy current seq
seq.append(item.value, item.type, item.line, item.col) seq.append(item.value, item.type, item.line, item.col)
seq.append(name, 'name') seq.append(name, 'name')
self._setSeq(seq) self._setSeq(seq)
self._name = name self._name = name
# set title of referred sheet # set title of referred sheet
if self.styleSheet: if self.styleSheet:
self.styleSheet.title = name self.styleSheet.title = name
else: else:
self._log.error(u'CSSImportRule: Not a valid name: %s' % name) self._log.error(u'CSSImportRule: Not a valid name: %s' % name)
name = property(lambda self: self._name, _setName, name = property(lambda self: self._name, _setName,
doc=u"An optional name for the imported sheet") doc=u"An optional name for the imported sheet")
def __setStyleSheet(self): def __setStyleSheet(self):
"""Read new CSSStyleSheet cssText from href using parentStyleSheet.href """Read new CSSStyleSheet cssText from href using parentStyleSheet.href
Indirectly called if setting ``href``. In case of any error styleSheet Indirectly called if setting ``href``. In case of any error styleSheet
is set to ``None``. is set to ``None``.
""" """
# should simply fail so all errors are catched! # should simply fail so all errors are catched!
if self.parentStyleSheet and self.href: if self.parentStyleSheet and self.href:
# relative href # relative href
parentHref = self.parentStyleSheet.href parentHref = self.parentStyleSheet.href
if parentHref is None: if parentHref is None:
# use cwd instead # use cwd instead
parentHref = u'file:' + urllib.pathname2url(os.getcwd()) + '/' parentHref = u'file:' + urllib.pathname2url(os.getcwd()) + '/'
href = urlparse.urljoin(parentHref, self.href) href = urlparse.urljoin(parentHref, self.href)
# all possible exceptions are ignored (styleSheet is None then) # all possible exceptions are ignored (styleSheet is None then)
try: try:
usedEncoding, enctype, cssText = self.parentStyleSheet._resolveImport(href) usedEncoding, enctype, cssText = self.parentStyleSheet._resolveImport(href)
if cssText is None: if cssText is None:
# catched in next except below! # catched in next except below!
raise IOError('Cannot read Stylesheet.') raise IOError('Cannot read Stylesheet.')
styleSheet = cssutils.css.CSSStyleSheet(href=href, styleSheet = cssutils.css.CSSStyleSheet(href=href,
media=self.media, media=self.media,
ownerRule=self, ownerRule=self,
title=self.name) title=self.name)
# inherit fetcher for @imports in styleSheet # inherit fetcher for @imports in styleSheet
styleSheet._setFetcher(self.parentStyleSheet._fetcher) styleSheet._setFetcher(self.parentStyleSheet._fetcher)
# contentEncoding with parentStyleSheet.overrideEncoding, # contentEncoding with parentStyleSheet.overrideEncoding,
# HTTP or parent # HTTP or parent
encodingOverride, encoding = None, None encodingOverride, encoding = None, None
if enctype == 0: if enctype == 0:
encodingOverride = usedEncoding encodingOverride = usedEncoding
elif 5 > enctype > 0: elif 5 > enctype > 0:
encoding = usedEncoding encoding = usedEncoding
styleSheet._setCssTextWithEncodingOverride(cssText, styleSheet._setCssTextWithEncodingOverride(cssText,
encodingOverride=encodingOverride, encodingOverride=encodingOverride,
encoding=encoding) encoding=encoding)
except (OSError, IOError, ValueError), e: except (OSError, IOError, ValueError), e:
self._log.warn(u'CSSImportRule: While processing imported style sheet href=%r: %r' self._log.warn(u'CSSImportRule: While processing imported style sheet href=%r: %r'
% (self.href, e), neverraise=True) % (self.href, e), neverraise=True)
else: else:
self._styleSheet = styleSheet self._styleSheet = styleSheet
styleSheet = property(lambda self: self._styleSheet, styleSheet = property(lambda self: self._styleSheet,
doc="(readonly) The style sheet referred to by this rule.") doc="(readonly) The style sheet referred to by this rule.")
def _getWellformed(self): def _getWellformed(self):
"depending if media is used at all" "depending if media is used at all"
if self._usemedia: if self._usemedia:
return bool(self.href and self.media.wellformed) return bool(self.href and self.media.wellformed)
else: else:
return bool(self.href) return bool(self.href)
wellformed = property(_getWellformed) wellformed = property(_getWellformed)
def __repr__(self): def __repr__(self):
if self._usemedia: if self._usemedia:
mediaText = self.media.mediaText mediaText = self.media.mediaText
else: else:
mediaText = None mediaText = None
return "cssutils.css.%s(href=%r, mediaText=%r, name=%r)" % ( return "cssutils.css.%s(href=%r, mediaText=%r, name=%r)" % (
self.__class__.__name__, self.__class__.__name__,
self.href, self.media.mediaText, self.name) self.href, self.media.mediaText, self.name)
def __str__(self): def __str__(self):
if self._usemedia: if self._usemedia:
mediaText = self.media.mediaText mediaText = self.media.mediaText
else: else:
mediaText = None mediaText = None
return "<cssutils.css.%s object href=%r mediaText=%r name=%r at 0x%x>" % ( return "<cssutils.css.%s object href=%r mediaText=%r name=%r at 0x%x>" % (
self.__class__.__name__, self.href, mediaText, self.name, id(self)) self.__class__.__name__, self.href, mediaText, self.name, id(self))

View File

@ -1,349 +1,349 @@
"""CSSMediaRule implements DOM Level 2 CSS CSSMediaRule. """CSSMediaRule implements DOM Level 2 CSS CSSMediaRule.
""" """
__all__ = ['CSSMediaRule'] __all__ = ['CSSMediaRule']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: cssmediarule.py 1370 2008-07-14 20:15:03Z cthedot $' __version__ = '$Id: cssmediarule.py 1370 2008-07-14 20:15:03Z cthedot $'
import xml.dom import xml.dom
import cssrule import cssrule
import cssutils import cssutils
class CSSMediaRule(cssrule.CSSRule): class CSSMediaRule(cssrule.CSSRule):
""" """
Objects implementing the CSSMediaRule interface can be identified by the Objects implementing the CSSMediaRule interface can be identified by the
MEDIA_RULE constant. On these objects the type attribute must return the MEDIA_RULE constant. On these objects the type attribute must return the
value of that constant. value of that constant.
Properties Properties
========== ==========
atkeyword: (cssutils only) atkeyword: (cssutils only)
the literal keyword used the literal keyword used
cssRules: A css::CSSRuleList of all CSS rules contained within the cssRules: A css::CSSRuleList of all CSS rules contained within the
media block. media block.
cssText: of type DOMString cssText: of type DOMString
The parsable textual representation of this rule The parsable textual representation of this rule
media: of type stylesheets::MediaList, (DOM readonly) media: of type stylesheets::MediaList, (DOM readonly)
A list of media types for this rule of type MediaList. A list of media types for this rule of type MediaList.
name: name:
An optional name used for cascading An optional name used for cascading
Format Format
====== ======
media media
: MEDIA_SYM S* medium [ COMMA S* medium ]* : MEDIA_SYM S* medium [ COMMA S* medium ]*
STRING? # the name STRING? # the name
LBRACE S* ruleset* '}' S*; LBRACE S* ruleset* '}' S*;
""" """
# CONSTANT # CONSTANT
type = property(lambda self: cssrule.CSSRule.MEDIA_RULE) type = property(lambda self: cssrule.CSSRule.MEDIA_RULE)
def __init__(self, mediaText='all', name=None, def __init__(self, mediaText='all', name=None,
parentRule=None, parentStyleSheet=None, readonly=False): parentRule=None, parentStyleSheet=None, readonly=False):
"""constructor""" """constructor"""
super(CSSMediaRule, self).__init__(parentRule=parentRule, super(CSSMediaRule, self).__init__(parentRule=parentRule,
parentStyleSheet=parentStyleSheet) parentStyleSheet=parentStyleSheet)
self._atkeyword = u'@media' self._atkeyword = u'@media'
self._media = cssutils.stylesheets.MediaList(mediaText, self._media = cssutils.stylesheets.MediaList(mediaText,
readonly=readonly) readonly=readonly)
self.name = name self.name = name
self.cssRules = cssutils.css.cssrulelist.CSSRuleList() self.cssRules = cssutils.css.cssrulelist.CSSRuleList()
self.cssRules.append = self.insertRule self.cssRules.append = self.insertRule
self.cssRules.extend = self.insertRule self.cssRules.extend = self.insertRule
self.cssRules.__delitem__ == self.deleteRule self.cssRules.__delitem__ == self.deleteRule
self._readonly = readonly self._readonly = readonly
def __iter__(self): def __iter__(self):
"""generator which iterates over cssRules.""" """generator which iterates over cssRules."""
for rule in self.cssRules: for rule in self.cssRules:
yield rule yield rule
def _getCssText(self): def _getCssText(self):
"""return serialized property cssText""" """return serialized property cssText"""
return cssutils.ser.do_CSSMediaRule(self) return cssutils.ser.do_CSSMediaRule(self)
def _setCssText(self, cssText): def _setCssText(self, cssText):
""" """
:param cssText: :param cssText:
a parseable string or a tuple of (cssText, dict-of-namespaces) a parseable string or a tuple of (cssText, dict-of-namespaces)
:Exceptions: :Exceptions:
- `NAMESPACE_ERR`: (Selector) - `NAMESPACE_ERR`: (Selector)
Raised if a specified selector uses an unknown namespace Raised if a specified selector uses an unknown namespace
prefix. prefix.
- `SYNTAX_ERR`: (self, StyleDeclaration, etc) - `SYNTAX_ERR`: (self, StyleDeclaration, etc)
Raised if the specified CSS string value has a syntax error and Raised if the specified CSS string value has a syntax error and
is unparsable. is unparsable.
- `INVALID_MODIFICATION_ERR`: (self) - `INVALID_MODIFICATION_ERR`: (self)
Raised if the specified CSS string value represents a different Raised if the specified CSS string value represents a different
type of rule than the current one. type of rule than the current one.
- `HIERARCHY_REQUEST_ERR`: (CSSStylesheet) - `HIERARCHY_REQUEST_ERR`: (CSSStylesheet)
Raised if the rule cannot be inserted at this point in the Raised if the rule cannot be inserted at this point in the
style sheet. style sheet.
- `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule) - `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule)
Raised if the rule is readonly. Raised if the rule is readonly.
""" """
super(CSSMediaRule, self)._setCssText(cssText) super(CSSMediaRule, self)._setCssText(cssText)
# might be (cssText, namespaces) # might be (cssText, namespaces)
cssText, namespaces = self._splitNamespacesOff(cssText) cssText, namespaces = self._splitNamespacesOff(cssText)
try: try:
# use parent style sheet ones if available # use parent style sheet ones if available
namespaces = self.parentStyleSheet.namespaces namespaces = self.parentStyleSheet.namespaces
except AttributeError: except AttributeError:
pass pass
tokenizer = self._tokenize2(cssText) tokenizer = self._tokenize2(cssText)
attoken = self._nexttoken(tokenizer, None) attoken = self._nexttoken(tokenizer, None)
if self._type(attoken) != self._prods.MEDIA_SYM: if self._type(attoken) != self._prods.MEDIA_SYM:
self._log.error(u'CSSMediaRule: No CSSMediaRule found: %s' % self._log.error(u'CSSMediaRule: No CSSMediaRule found: %s' %
self._valuestr(cssText), self._valuestr(cssText),
error=xml.dom.InvalidModificationErr) error=xml.dom.InvalidModificationErr)
else: else:
# media "name"? { cssRules } # media "name"? { cssRules }
# media # media
wellformed = True wellformed = True
mediatokens, end = self._tokensupto2(tokenizer, mediatokens, end = self._tokensupto2(tokenizer,
mediaqueryendonly=True, mediaqueryendonly=True,
separateEnd=True) separateEnd=True)
if u'{' == self._tokenvalue(end) or self._prods.STRING == self._type(end): if u'{' == self._tokenvalue(end) or self._prods.STRING == self._type(end):
newmedia = cssutils.stylesheets.MediaList() newmedia = cssutils.stylesheets.MediaList()
newmedia.mediaText = mediatokens newmedia.mediaText = mediatokens
# name (optional) # name (optional)
name = None name = None
nameseq = self._tempSeq() nameseq = self._tempSeq()
if self._prods.STRING == self._type(end): if self._prods.STRING == self._type(end):
name = self._stringtokenvalue(end) name = self._stringtokenvalue(end)
# TODO: for now comments are lost after name # TODO: for now comments are lost after name
nametokens, end = self._tokensupto2(tokenizer, nametokens, end = self._tokensupto2(tokenizer,
blockstartonly=True, blockstartonly=True,
separateEnd=True) separateEnd=True)
wellformed, expected = self._parse(None, nameseq, nametokens, {}) wellformed, expected = self._parse(None, nameseq, nametokens, {})
if not wellformed: if not wellformed:
self._log.error(u'CSSMediaRule: Syntax Error: %s' % self._log.error(u'CSSMediaRule: Syntax Error: %s' %
self._valuestr(cssText)) self._valuestr(cssText))
# check for { # check for {
if u'{' != self._tokenvalue(end): if u'{' != self._tokenvalue(end):
self._log.error(u'CSSMediaRule: No "{" found: %s' % self._log.error(u'CSSMediaRule: No "{" found: %s' %
self._valuestr(cssText)) self._valuestr(cssText))
return return
# cssRules # cssRules
cssrulestokens, braceOrEOF = self._tokensupto2(tokenizer, cssrulestokens, braceOrEOF = self._tokensupto2(tokenizer,
mediaendonly=True, mediaendonly=True,
separateEnd=True) separateEnd=True)
nonetoken = self._nexttoken(tokenizer, None) nonetoken = self._nexttoken(tokenizer, None)
if (u'}' != self._tokenvalue(braceOrEOF) and if (u'}' != self._tokenvalue(braceOrEOF) and
'EOF' != self._type(braceOrEOF)): 'EOF' != self._type(braceOrEOF)):
self._log.error(u'CSSMediaRule: No "}" found.', self._log.error(u'CSSMediaRule: No "}" found.',
token=braceOrEOF) token=braceOrEOF)
elif nonetoken: elif nonetoken:
self._log.error(u'CSSMediaRule: Trailing content found.', self._log.error(u'CSSMediaRule: Trailing content found.',
token=nonetoken) token=nonetoken)
else: else:
# for closures: must be a mutable # for closures: must be a mutable
newcssrules = [] #cssutils.css.CSSRuleList() newcssrules = [] #cssutils.css.CSSRuleList()
new = {'wellformed': True } new = {'wellformed': True }
def ruleset(expected, seq, token, tokenizer): def ruleset(expected, seq, token, tokenizer):
rule = cssutils.css.CSSStyleRule(parentRule=self) rule = cssutils.css.CSSStyleRule(parentRule=self)
rule.cssText = (self._tokensupto2(tokenizer, token), rule.cssText = (self._tokensupto2(tokenizer, token),
namespaces) namespaces)
if rule.wellformed: if rule.wellformed:
rule._parentStyleSheet=self.parentStyleSheet rule._parentStyleSheet=self.parentStyleSheet
seq.append(rule) seq.append(rule)
return expected return expected
def atrule(expected, seq, token, tokenizer): def atrule(expected, seq, token, tokenizer):
# TODO: get complete rule! # TODO: get complete rule!
tokens = self._tokensupto2(tokenizer, token) tokens = self._tokensupto2(tokenizer, token)
atval = self._tokenvalue(token) atval = self._tokenvalue(token)
if atval in ('@charset ', '@font-face', '@import', '@namespace', if atval in ('@charset ', '@font-face', '@import', '@namespace',
'@page', '@media'): '@page', '@media'):
self._log.error( self._log.error(
u'CSSMediaRule: This rule is not allowed in CSSMediaRule - ignored: %s.' u'CSSMediaRule: This rule is not allowed in CSSMediaRule - ignored: %s.'
% self._valuestr(tokens), % self._valuestr(tokens),
token = token, token = token,
error=xml.dom.HierarchyRequestErr) error=xml.dom.HierarchyRequestErr)
else: else:
rule = cssutils.css.CSSUnknownRule(parentRule=self, rule = cssutils.css.CSSUnknownRule(parentRule=self,
parentStyleSheet=self.parentStyleSheet) parentStyleSheet=self.parentStyleSheet)
rule.cssText = tokens rule.cssText = tokens
if rule.wellformed: if rule.wellformed:
seq.append(rule) seq.append(rule)
return expected return expected
def COMMENT(expected, seq, token, tokenizer=None): def COMMENT(expected, seq, token, tokenizer=None):
seq.append(cssutils.css.CSSComment([token])) seq.append(cssutils.css.CSSComment([token]))
return expected return expected
tokenizer = (t for t in cssrulestokens) # TODO: not elegant! tokenizer = (t for t in cssrulestokens) # TODO: not elegant!
wellformed, expected = self._parse(braceOrEOF, wellformed, expected = self._parse(braceOrEOF,
newcssrules, newcssrules,
tokenizer, { tokenizer, {
'COMMENT': COMMENT, 'COMMENT': COMMENT,
'CHARSET_SYM': atrule, 'CHARSET_SYM': atrule,
'FONT_FACE_SYM': atrule, 'FONT_FACE_SYM': atrule,
'IMPORT_SYM': atrule, 'IMPORT_SYM': atrule,
'NAMESPACE_SYM': atrule, 'NAMESPACE_SYM': atrule,
'PAGE_SYM': atrule, 'PAGE_SYM': atrule,
'MEDIA_SYM': atrule, 'MEDIA_SYM': atrule,
'ATKEYWORD': atrule 'ATKEYWORD': atrule
}, },
default=ruleset, default=ruleset,
new=new) new=new)
# no post condition # no post condition
if newmedia.wellformed and wellformed: if newmedia.wellformed and wellformed:
# keep reference # keep reference
self._media.mediaText = newmedia.mediaText self._media.mediaText = newmedia.mediaText
self.name = name self.name = name
self._setSeq(nameseq) self._setSeq(nameseq)
del self.cssRules[:] del self.cssRules[:]
for r in newcssrules: for r in newcssrules:
self.cssRules.append(r) self.cssRules.append(r)
cssText = property(_getCssText, _setCssText, cssText = property(_getCssText, _setCssText,
doc="(DOM attribute) The parsable textual representation.") doc="(DOM attribute) The parsable textual representation.")
def _setName(self, name): def _setName(self, name):
if isinstance(name, basestring) or name is None: if isinstance(name, basestring) or name is None:
# "" or '' # "" or ''
if not name: if not name:
name = None name = None
self._name = name self._name = name
else: else:
self._log.error(u'CSSImportRule: Not a valid name: %s' % name) self._log.error(u'CSSImportRule: Not a valid name: %s' % name)
name = property(lambda self: self._name, _setName, name = property(lambda self: self._name, _setName,
doc=u"An optional name for the media rules") doc=u"An optional name for the media rules")
media = property(lambda self: self._media, media = property(lambda self: self._media,
doc=u"(DOM readonly) A list of media types for this rule of type\ doc=u"(DOM readonly) A list of media types for this rule of type\
MediaList") MediaList")
wellformed = property(lambda self: self.media.wellformed) wellformed = property(lambda self: self.media.wellformed)
def deleteRule(self, index): def deleteRule(self, index):
""" """
index index
within the media block's rule collection of the rule to remove. within the media block's rule collection of the rule to remove.
Used to delete a rule from the media block. Used to delete a rule from the media block.
DOMExceptions DOMExceptions
- INDEX_SIZE_ERR: (self) - INDEX_SIZE_ERR: (self)
Raised if the specified index does not correspond to a rule in Raised if the specified index does not correspond to a rule in
the media rule list. the media rule list.
- NO_MODIFICATION_ALLOWED_ERR: (self) - NO_MODIFICATION_ALLOWED_ERR: (self)
Raised if this media rule is readonly. Raised if this media rule is readonly.
""" """
self._checkReadonly() self._checkReadonly()
try: try:
self.cssRules[index]._parentRule = None # detach self.cssRules[index]._parentRule = None # detach
del self.cssRules[index] # remove from @media del self.cssRules[index] # remove from @media
except IndexError: except IndexError:
raise xml.dom.IndexSizeErr( raise xml.dom.IndexSizeErr(
u'CSSMediaRule: %s is not a valid index in the rulelist of length %i' % ( u'CSSMediaRule: %s is not a valid index in the rulelist of length %i' % (
index, self.cssRules.length)) index, self.cssRules.length))
def add(self, rule): def add(self, rule):
"""Add rule to end of this mediarule. Same as ``.insertRule(rule)``.""" """Add rule to end of this mediarule. Same as ``.insertRule(rule)``."""
self.insertRule(rule, index=None) self.insertRule(rule, index=None)
def insertRule(self, rule, index=None): def insertRule(self, rule, index=None):
""" """
rule rule
The parsable text representing the rule. For rule sets this The parsable text representing the rule. For rule sets this
contains both the selector and the style declaration. For contains both the selector and the style declaration. For
at-rules, this specifies both the at-identifier and the rule at-rules, this specifies both the at-identifier and the rule
content. content.
cssutils also allows rule to be a valid **CSSRule** object cssutils also allows rule to be a valid **CSSRule** object
index index
within the media block's rule collection of the rule before within the media block's rule collection of the rule before
which to insert the specified rule. If the specified index is which to insert the specified rule. If the specified index is
equal to the length of the media blocks's rule collection, the equal to the length of the media blocks's rule collection, the
rule will be added to the end of the media block. rule will be added to the end of the media block.
If index is not given or None rule will be appended to rule If index is not given or None rule will be appended to rule
list. list.
Used to insert a new rule into the media block. Used to insert a new rule into the media block.
DOMException on setting DOMException on setting
- HIERARCHY_REQUEST_ERR: - HIERARCHY_REQUEST_ERR:
(no use case yet as no @charset or @import allowed)) (no use case yet as no @charset or @import allowed))
Raised if the rule cannot be inserted at the specified index, Raised if the rule cannot be inserted at the specified index,
e.g., if an @import rule is inserted after a standard rule set e.g., if an @import rule is inserted after a standard rule set
or other at-rule. or other at-rule.
- INDEX_SIZE_ERR: (self) - INDEX_SIZE_ERR: (self)
Raised if the specified index is not a valid insertion point. Raised if the specified index is not a valid insertion point.
- NO_MODIFICATION_ALLOWED_ERR: (self) - NO_MODIFICATION_ALLOWED_ERR: (self)
Raised if this media rule is readonly. Raised if this media rule is readonly.
- SYNTAX_ERR: (CSSStyleRule) - SYNTAX_ERR: (CSSStyleRule)
Raised if the specified rule has a syntax error and is Raised if the specified rule has a syntax error and is
unparsable. unparsable.
returns the index within the media block's rule collection of the returns the index within the media block's rule collection of the
newly inserted rule. newly inserted rule.
""" """
self._checkReadonly() self._checkReadonly()
# check position # check position
if index is None: if index is None:
index = len(self.cssRules) index = len(self.cssRules)
elif index < 0 or index > self.cssRules.length: elif index < 0 or index > self.cssRules.length:
raise xml.dom.IndexSizeErr( raise xml.dom.IndexSizeErr(
u'CSSMediaRule: Invalid index %s for CSSRuleList with a length of %s.' % ( u'CSSMediaRule: Invalid index %s for CSSRuleList with a length of %s.' % (
index, self.cssRules.length)) index, self.cssRules.length))
# parse # parse
if isinstance(rule, basestring): if isinstance(rule, basestring):
tempsheet = cssutils.css.CSSStyleSheet() tempsheet = cssutils.css.CSSStyleSheet()
tempsheet.cssText = rule tempsheet.cssText = rule
if len(tempsheet.cssRules) != 1 or (tempsheet.cssRules and if len(tempsheet.cssRules) != 1 or (tempsheet.cssRules and
not isinstance(tempsheet.cssRules[0], cssutils.css.CSSRule)): not isinstance(tempsheet.cssRules[0], cssutils.css.CSSRule)):
self._log.error(u'CSSMediaRule: Invalid Rule: %s' % rule) self._log.error(u'CSSMediaRule: Invalid Rule: %s' % rule)
return return
rule = tempsheet.cssRules[0] rule = tempsheet.cssRules[0]
elif not isinstance(rule, cssutils.css.CSSRule): elif not isinstance(rule, cssutils.css.CSSRule):
self._log.error(u'CSSMediaRule: Not a CSSRule: %s' % rule) self._log.error(u'CSSMediaRule: Not a CSSRule: %s' % rule)
return return
# CHECK HIERARCHY # CHECK HIERARCHY
# @charset @import @page @namespace @media # @charset @import @page @namespace @media
if isinstance(rule, cssutils.css.CSSCharsetRule) or \ if isinstance(rule, cssutils.css.CSSCharsetRule) or \
isinstance(rule, cssutils.css.CSSFontFaceRule) or \ isinstance(rule, cssutils.css.CSSFontFaceRule) or \
isinstance(rule, cssutils.css.CSSImportRule) or \ isinstance(rule, cssutils.css.CSSImportRule) or \
isinstance(rule, cssutils.css.CSSNamespaceRule) or \ isinstance(rule, cssutils.css.CSSNamespaceRule) or \
isinstance(rule, cssutils.css.CSSPageRule) or \ isinstance(rule, cssutils.css.CSSPageRule) or \
isinstance(rule, CSSMediaRule): isinstance(rule, CSSMediaRule):
self._log.error(u'CSSMediaRule: This type of rule is not allowed here: %s' % self._log.error(u'CSSMediaRule: This type of rule is not allowed here: %s' %
rule.cssText, rule.cssText,
error=xml.dom.HierarchyRequestErr) error=xml.dom.HierarchyRequestErr)
return return
self.cssRules.insert(index, rule) self.cssRules.insert(index, rule)
rule._parentRule = self rule._parentRule = self
rule._parentStyleSheet = self.parentStyleSheet rule._parentStyleSheet = self.parentStyleSheet
return index return index
def __repr__(self): def __repr__(self):
return "cssutils.css.%s(mediaText=%r)" % ( return "cssutils.css.%s(mediaText=%r)" % (
self.__class__.__name__, self.media.mediaText) self.__class__.__name__, self.media.mediaText)
def __str__(self): def __str__(self):
return "<cssutils.css.%s object mediaText=%r at 0x%x>" % ( return "<cssutils.css.%s object mediaText=%r at 0x%x>" % (
self.__class__.__name__, self.media.mediaText, id(self)) self.__class__.__name__, self.media.mediaText, id(self))

View File

@ -1,306 +1,306 @@
"""CSSNamespaceRule currently implements """CSSNamespaceRule currently implements
http://dev.w3.org/csswg/css3-namespace/ http://dev.w3.org/csswg/css3-namespace/
(until 0.9.5a2: http://www.w3.org/TR/2006/WD-css3-namespace-20060828/) (until 0.9.5a2: http://www.w3.org/TR/2006/WD-css3-namespace-20060828/)
""" """
__all__ = ['CSSNamespaceRule'] __all__ = ['CSSNamespaceRule']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: cssnamespacerule.py 1305 2008-06-22 18:42:51Z cthedot $' __version__ = '$Id: cssnamespacerule.py 1305 2008-06-22 18:42:51Z cthedot $'
import xml.dom import xml.dom
import cssrule import cssrule
import cssutils import cssutils
from cssutils.helper import Deprecated from cssutils.helper import Deprecated
class CSSNamespaceRule(cssrule.CSSRule): class CSSNamespaceRule(cssrule.CSSRule):
""" """
Represents an @namespace rule within a CSS style sheet. Represents an @namespace rule within a CSS style sheet.
The @namespace at-rule declares a namespace prefix and associates The @namespace at-rule declares a namespace prefix and associates
it with a given namespace (a string). This namespace prefix can then be it with a given namespace (a string). This namespace prefix can then be
used in namespace-qualified names such as those described in the used in namespace-qualified names such as those described in the
Selectors Module [SELECT] or the Values and Units module [CSS3VAL]. Selectors Module [SELECT] or the Values and Units module [CSS3VAL].
Properties Properties
========== ==========
atkeyword (cssutils only) atkeyword (cssutils only)
the literal keyword used the literal keyword used
cssText: of type DOMString cssText: of type DOMString
The parsable textual representation of this rule The parsable textual representation of this rule
namespaceURI: of type DOMString namespaceURI: of type DOMString
The namespace URI (a simple string!) which is bound to the given The namespace URI (a simple string!) which is bound to the given
prefix. If no prefix is set (``CSSNamespaceRule.prefix==''``) prefix. If no prefix is set (``CSSNamespaceRule.prefix==''``)
the namespace defined by ``namespaceURI`` is set as the default the namespace defined by ``namespaceURI`` is set as the default
namespace. namespace.
prefix: of type DOMString prefix: of type DOMString
The prefix used in the stylesheet for the given The prefix used in the stylesheet for the given
``CSSNamespaceRule.nsuri``. If prefix is empty namespaceURI sets a ``CSSNamespaceRule.nsuri``. If prefix is empty namespaceURI sets a
default namespace for the stylesheet. default namespace for the stylesheet.
Inherits properties from CSSRule Inherits properties from CSSRule
Format Format
====== ======
namespace namespace
: NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S* : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
; ;
namespace_prefix namespace_prefix
: IDENT : IDENT
; ;
""" """
type = property(lambda self: cssrule.CSSRule.NAMESPACE_RULE) type = property(lambda self: cssrule.CSSRule.NAMESPACE_RULE)
def __init__(self, namespaceURI=None, prefix=None, cssText=None, def __init__(self, namespaceURI=None, prefix=None, cssText=None,
parentRule=None, parentStyleSheet=None, readonly=False): parentRule=None, parentStyleSheet=None, readonly=False):
""" """
:Parameters: :Parameters:
namespaceURI namespaceURI
The namespace URI (a simple string!) which is bound to the The namespace URI (a simple string!) which is bound to the
given prefix. If no prefix is set given prefix. If no prefix is set
(``CSSNamespaceRule.prefix==''``) the namespace defined by (``CSSNamespaceRule.prefix==''``) the namespace defined by
namespaceURI is set as the default namespace namespaceURI is set as the default namespace
prefix prefix
The prefix used in the stylesheet for the given The prefix used in the stylesheet for the given
``CSSNamespaceRule.uri``. ``CSSNamespaceRule.uri``.
cssText cssText
if no namespaceURI is given cssText must be given to set if no namespaceURI is given cssText must be given to set
a namespaceURI as this is readonly later on a namespaceURI as this is readonly later on
parentStyleSheet parentStyleSheet
sheet where this rule belongs to sheet where this rule belongs to
Do not use as positional but as keyword parameters only! Do not use as positional but as keyword parameters only!
If readonly allows setting of properties in constructor only If readonly allows setting of properties in constructor only
format namespace:: format namespace::
namespace namespace
: NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S* : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
; ;
namespace_prefix namespace_prefix
: IDENT : IDENT
; ;
""" """
super(CSSNamespaceRule, self).__init__(parentRule=parentRule, super(CSSNamespaceRule, self).__init__(parentRule=parentRule,
parentStyleSheet=parentStyleSheet) parentStyleSheet=parentStyleSheet)
self._atkeyword = u'@namespace' self._atkeyword = u'@namespace'
self._prefix = u'' self._prefix = u''
self._namespaceURI = None self._namespaceURI = None
if namespaceURI: if namespaceURI:
self.namespaceURI = namespaceURI self.namespaceURI = namespaceURI
self.prefix = prefix self.prefix = prefix
tempseq = self._tempSeq() tempseq = self._tempSeq()
tempseq.append(self.prefix, 'prefix') tempseq.append(self.prefix, 'prefix')
tempseq.append(self.namespaceURI, 'namespaceURI') tempseq.append(self.namespaceURI, 'namespaceURI')
self._setSeq(tempseq) self._setSeq(tempseq)
elif cssText is not None: elif cssText is not None:
self.cssText = cssText self.cssText = cssText
if parentStyleSheet: if parentStyleSheet:
self._parentStyleSheet = parentStyleSheet self._parentStyleSheet = parentStyleSheet
self._readonly = readonly self._readonly = readonly
def _getCssText(self): def _getCssText(self):
""" """
returns serialized property cssText returns serialized property cssText
""" """
return cssutils.ser.do_CSSNamespaceRule(self) return cssutils.ser.do_CSSNamespaceRule(self)
def _setCssText(self, cssText): def _setCssText(self, cssText):
""" """
DOMException on setting DOMException on setting
:param cssText: initial value for this rules cssText which is parsed :param cssText: initial value for this rules cssText which is parsed
:Exceptions: :Exceptions:
- `HIERARCHY_REQUEST_ERR`: (CSSStylesheet) - `HIERARCHY_REQUEST_ERR`: (CSSStylesheet)
Raised if the rule cannot be inserted at this point in the Raised if the rule cannot be inserted at this point in the
style sheet. style sheet.
- `INVALID_MODIFICATION_ERR`: (self) - `INVALID_MODIFICATION_ERR`: (self)
Raised if the specified CSS string value represents a different Raised if the specified CSS string value represents a different
type of rule than the current one. type of rule than the current one.
- `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule) - `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule)
Raised if the rule is readonly. Raised if the rule is readonly.
- `SYNTAX_ERR`: (self) - `SYNTAX_ERR`: (self)
Raised if the specified CSS string value has a syntax error and Raised if the specified CSS string value has a syntax error and
is unparsable. is unparsable.
""" """
super(CSSNamespaceRule, self)._setCssText(cssText) super(CSSNamespaceRule, self)._setCssText(cssText)
tokenizer = self._tokenize2(cssText) tokenizer = self._tokenize2(cssText)
attoken = self._nexttoken(tokenizer, None) attoken = self._nexttoken(tokenizer, None)
if self._type(attoken) != self._prods.NAMESPACE_SYM: if self._type(attoken) != self._prods.NAMESPACE_SYM:
self._log.error(u'CSSNamespaceRule: No CSSNamespaceRule found: %s' % self._log.error(u'CSSNamespaceRule: No CSSNamespaceRule found: %s' %
self._valuestr(cssText), self._valuestr(cssText),
error=xml.dom.InvalidModificationErr) error=xml.dom.InvalidModificationErr)
else: else:
# for closures: must be a mutable # for closures: must be a mutable
new = {'keyword': self._tokenvalue(attoken), new = {'keyword': self._tokenvalue(attoken),
'prefix': u'', 'prefix': u'',
'uri': None, 'uri': None,
'wellformed': True 'wellformed': True
} }
def _ident(expected, seq, token, tokenizer=None): def _ident(expected, seq, token, tokenizer=None):
# the namespace prefix, optional # the namespace prefix, optional
if 'prefix or uri' == expected: if 'prefix or uri' == expected:
new['prefix'] = self._tokenvalue(token) new['prefix'] = self._tokenvalue(token)
seq.append(new['prefix'], 'prefix') seq.append(new['prefix'], 'prefix')
return 'uri' return 'uri'
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error( self._log.error(
u'CSSNamespaceRule: Unexpected ident.', token) u'CSSNamespaceRule: Unexpected ident.', token)
return expected return expected
def _string(expected, seq, token, tokenizer=None): def _string(expected, seq, token, tokenizer=None):
# the namespace URI as a STRING # the namespace URI as a STRING
if expected.endswith('uri'): if expected.endswith('uri'):
new['uri'] = self._stringtokenvalue(token) new['uri'] = self._stringtokenvalue(token)
seq.append(new['uri'], 'namespaceURI') seq.append(new['uri'], 'namespaceURI')
return ';' return ';'
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error( self._log.error(
u'CSSNamespaceRule: Unexpected string.', token) u'CSSNamespaceRule: Unexpected string.', token)
return expected return expected
def _uri(expected, seq, token, tokenizer=None): def _uri(expected, seq, token, tokenizer=None):
# the namespace URI as URI which is DEPRECATED # the namespace URI as URI which is DEPRECATED
if expected.endswith('uri'): if expected.endswith('uri'):
uri = self._uritokenvalue(token) uri = self._uritokenvalue(token)
new['uri'] = uri new['uri'] = uri
seq.append(new['uri'], 'namespaceURI') seq.append(new['uri'], 'namespaceURI')
return ';' return ';'
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error( self._log.error(
u'CSSNamespaceRule: Unexpected URI.', token) u'CSSNamespaceRule: Unexpected URI.', token)
return expected return expected
def _char(expected, seq, token, tokenizer=None): def _char(expected, seq, token, tokenizer=None):
# final ; # final ;
val = self._tokenvalue(token) val = self._tokenvalue(token)
if ';' == expected and u';' == val: if ';' == expected and u';' == val:
return 'EOF' return 'EOF'
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error( self._log.error(
u'CSSNamespaceRule: Unexpected char.', token) u'CSSNamespaceRule: Unexpected char.', token)
return expected return expected
# "NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*" # "NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*"
newseq = self._tempSeq() newseq = self._tempSeq()
wellformed, expected = self._parse(expected='prefix or uri', wellformed, expected = self._parse(expected='prefix or uri',
seq=newseq, tokenizer=tokenizer, seq=newseq, tokenizer=tokenizer,
productions={'IDENT': _ident, productions={'IDENT': _ident,
'STRING': _string, 'STRING': _string,
'URI': _uri, 'URI': _uri,
'CHAR': _char}, 'CHAR': _char},
new=new) new=new)
# wellformed set by parse # wellformed set by parse
wellformed = wellformed and new['wellformed'] wellformed = wellformed and new['wellformed']
# post conditions # post conditions
if new['uri'] is None: if new['uri'] is None:
wellformed = False wellformed = False
self._log.error(u'CSSNamespaceRule: No namespace URI found: %s' % self._log.error(u'CSSNamespaceRule: No namespace URI found: %s' %
self._valuestr(cssText)) self._valuestr(cssText))
if expected != 'EOF': if expected != 'EOF':
wellformed = False wellformed = False
self._log.error(u'CSSNamespaceRule: No ";" found: %s' % self._log.error(u'CSSNamespaceRule: No ";" found: %s' %
self._valuestr(cssText)) self._valuestr(cssText))
# set all # set all
if wellformed: if wellformed:
self.atkeyword = new['keyword'] self.atkeyword = new['keyword']
self._prefix = new['prefix'] self._prefix = new['prefix']
self.namespaceURI = new['uri'] self.namespaceURI = new['uri']
self._setSeq(newseq) self._setSeq(newseq)
cssText = property(fget=_getCssText, fset=_setCssText, cssText = property(fget=_getCssText, fset=_setCssText,
doc="(DOM attribute) The parsable textual representation.") doc="(DOM attribute) The parsable textual representation.")
def _setNamespaceURI(self, namespaceURI): def _setNamespaceURI(self, namespaceURI):
""" """
DOMException on setting DOMException on setting
:param namespaceURI: the initial value for this rules namespaceURI :param namespaceURI: the initial value for this rules namespaceURI
:Exceptions: :Exceptions:
- `NO_MODIFICATION_ALLOWED_ERR`: - `NO_MODIFICATION_ALLOWED_ERR`:
(CSSRule) Raised if this rule is readonly or a namespaceURI is (CSSRule) Raised if this rule is readonly or a namespaceURI is
already set in this rule. already set in this rule.
""" """
self._checkReadonly() self._checkReadonly()
if not self._namespaceURI: if not self._namespaceURI:
# initial setting # initial setting
self._namespaceURI = namespaceURI self._namespaceURI = namespaceURI
tempseq = self._tempSeq() tempseq = self._tempSeq()
tempseq.append(namespaceURI, 'namespaceURI') tempseq.append(namespaceURI, 'namespaceURI')
self._setSeq(tempseq) # makes seq readonly! self._setSeq(tempseq) # makes seq readonly!
elif self._namespaceURI != namespaceURI: elif self._namespaceURI != namespaceURI:
self._log.error(u'CSSNamespaceRule: namespaceURI is readonly.', self._log.error(u'CSSNamespaceRule: namespaceURI is readonly.',
error=xml.dom.NoModificationAllowedErr) error=xml.dom.NoModificationAllowedErr)
namespaceURI = property(lambda self: self._namespaceURI, _setNamespaceURI, namespaceURI = property(lambda self: self._namespaceURI, _setNamespaceURI,
doc="URI (string!) of the defined namespace.") doc="URI (string!) of the defined namespace.")
def _setPrefix(self, prefix=None): def _setPrefix(self, prefix=None):
""" """
DOMException on setting DOMException on setting
:param prefix: the new prefix :param prefix: the new prefix
:Exceptions: :Exceptions:
- `SYNTAX_ERR`: (TODO) - `SYNTAX_ERR`: (TODO)
Raised if the specified CSS string value has a syntax error and Raised if the specified CSS string value has a syntax error and
is unparsable. is unparsable.
- `NO_MODIFICATION_ALLOWED_ERR`: CSSRule) - `NO_MODIFICATION_ALLOWED_ERR`: CSSRule)
Raised if this rule is readonly. Raised if this rule is readonly.
""" """
self._checkReadonly() self._checkReadonly()
if not prefix: if not prefix:
prefix = u'' prefix = u''
else: else:
tokenizer = self._tokenize2(prefix) tokenizer = self._tokenize2(prefix)
prefixtoken = self._nexttoken(tokenizer, None) prefixtoken = self._nexttoken(tokenizer, None)
if not prefixtoken or self._type(prefixtoken) != self._prods.IDENT: if not prefixtoken or self._type(prefixtoken) != self._prods.IDENT:
self._log.error(u'CSSNamespaceRule: No valid prefix "%s".' % self._log.error(u'CSSNamespaceRule: No valid prefix "%s".' %
self._valuestr(prefix), self._valuestr(prefix),
error=xml.dom.SyntaxErr) error=xml.dom.SyntaxErr)
return return
else: else:
prefix = self._tokenvalue(prefixtoken) prefix = self._tokenvalue(prefixtoken)
# update seg # update seg
for i, x in enumerate(self._seq): for i, x in enumerate(self._seq):
if x == self._prefix: if x == self._prefix:
self._seq[i] = (prefix, 'prefix', None, None) self._seq[i] = (prefix, 'prefix', None, None)
break break
else: else:
# put prefix at the beginning! # put prefix at the beginning!
self._seq[0] = (prefix, 'prefix', None, None) self._seq[0] = (prefix, 'prefix', None, None)
# set new prefix # set new prefix
self._prefix = prefix self._prefix = prefix
prefix = property(lambda self: self._prefix, _setPrefix, prefix = property(lambda self: self._prefix, _setPrefix,
doc="Prefix used for the defined namespace.") doc="Prefix used for the defined namespace.")
# def _setParentStyleSheet(self, parentStyleSheet): # def _setParentStyleSheet(self, parentStyleSheet):
# self._parentStyleSheet = parentStyleSheet # self._parentStyleSheet = parentStyleSheet
# #
# parentStyleSheet = property(lambda self: self._parentStyleSheet, # parentStyleSheet = property(lambda self: self._parentStyleSheet,
# _setParentStyleSheet, # _setParentStyleSheet,
# doc=u"Containing CSSStyleSheet.") # doc=u"Containing CSSStyleSheet.")
wellformed = property(lambda self: self.namespaceURI is not None) wellformed = property(lambda self: self.namespaceURI is not None)
def __repr__(self): def __repr__(self):
return "cssutils.css.%s(namespaceURI=%r, prefix=%r)" % ( return "cssutils.css.%s(namespaceURI=%r, prefix=%r)" % (
self.__class__.__name__, self.namespaceURI, self.prefix) self.__class__.__name__, self.namespaceURI, self.prefix)
def __str__(self): def __str__(self):
return "<cssutils.css.%s object namespaceURI=%r prefix=%r at 0x%x>" % ( return "<cssutils.css.%s object namespaceURI=%r prefix=%r at 0x%x>" % (
self.__class__.__name__, self.namespaceURI, self.prefix, id(self)) self.__class__.__name__, self.namespaceURI, self.prefix, id(self))

View File

@ -1,286 +1,286 @@
"""CSSPageRule implements DOM Level 2 CSS CSSPageRule. """CSSPageRule implements DOM Level 2 CSS CSSPageRule.
""" """
__all__ = ['CSSPageRule'] __all__ = ['CSSPageRule']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: csspagerule.py 1284 2008-06-05 16:29:17Z cthedot $' __version__ = '$Id: csspagerule.py 1284 2008-06-05 16:29:17Z cthedot $'
import xml.dom import xml.dom
import cssrule import cssrule
import cssutils import cssutils
from selectorlist import SelectorList from selectorlist import SelectorList
from cssstyledeclaration import CSSStyleDeclaration from cssstyledeclaration import CSSStyleDeclaration
class CSSPageRule(cssrule.CSSRule): class CSSPageRule(cssrule.CSSRule):
""" """
The CSSPageRule interface represents a @page rule within a CSS style The CSSPageRule interface represents a @page rule within a CSS style
sheet. The @page rule is used to specify the dimensions, orientation, sheet. The @page rule is used to specify the dimensions, orientation,
margins, etc. of a page box for paged media. margins, etc. of a page box for paged media.
Properties Properties
========== ==========
atkeyword (cssutils only) atkeyword (cssutils only)
the literal keyword used the literal keyword used
cssText: of type DOMString cssText: of type DOMString
The parsable textual representation of this rule The parsable textual representation of this rule
selectorText: of type DOMString selectorText: of type DOMString
The parsable textual representation of the page selector for the rule. The parsable textual representation of the page selector for the rule.
style: of type CSSStyleDeclaration style: of type CSSStyleDeclaration
The declaration-block of this rule. The declaration-block of this rule.
Inherits properties from CSSRule Inherits properties from CSSRule
Format Format
====== ======
:: ::
page page
: PAGE_SYM S* pseudo_page? S* : PAGE_SYM S* pseudo_page? S*
LBRACE S* declaration [ ';' S* declaration ]* '}' S* LBRACE S* declaration [ ';' S* declaration ]* '}' S*
; ;
pseudo_page pseudo_page
: ':' IDENT # :first, :left, :right in CSS 2.1 : ':' IDENT # :first, :left, :right in CSS 2.1
; ;
""" """
type = property(lambda self: cssrule.CSSRule.PAGE_RULE) type = property(lambda self: cssrule.CSSRule.PAGE_RULE)
# constant but needed: # constant but needed:
wellformed = True wellformed = True
def __init__(self, selectorText=None, style=None, parentRule=None, def __init__(self, selectorText=None, style=None, parentRule=None,
parentStyleSheet=None, readonly=False): parentStyleSheet=None, readonly=False):
""" """
if readonly allows setting of properties in constructor only if readonly allows setting of properties in constructor only
selectorText selectorText
type string type string
style style
CSSStyleDeclaration for this CSSStyleRule CSSStyleDeclaration for this CSSStyleRule
""" """
super(CSSPageRule, self).__init__(parentRule=parentRule, super(CSSPageRule, self).__init__(parentRule=parentRule,
parentStyleSheet=parentStyleSheet) parentStyleSheet=parentStyleSheet)
self._atkeyword = u'@page' self._atkeyword = u'@page'
tempseq = self._tempSeq() tempseq = self._tempSeq()
if selectorText: if selectorText:
self.selectorText = selectorText self.selectorText = selectorText
tempseq.append(self.selectorText, 'selectorText') tempseq.append(self.selectorText, 'selectorText')
else: else:
self._selectorText = u'' self._selectorText = u''
if style: if style:
self.style = style self.style = style
tempseq.append(self.style, 'style') tempseq.append(self.style, 'style')
else: else:
self._style = CSSStyleDeclaration(parentRule=self) self._style = CSSStyleDeclaration(parentRule=self)
self._setSeq(tempseq) self._setSeq(tempseq)
self._readonly = readonly self._readonly = readonly
def __parseSelectorText(self, selectorText): def __parseSelectorText(self, selectorText):
""" """
parses selectorText which may also be a list of tokens parses selectorText which may also be a list of tokens
and returns (selectorText, seq) and returns (selectorText, seq)
see _setSelectorText for details see _setSelectorText for details
""" """
# for closures: must be a mutable # for closures: must be a mutable
new = {'selector': None, 'wellformed': True} new = {'selector': None, 'wellformed': True}
def _char(expected, seq, token, tokenizer=None): def _char(expected, seq, token, tokenizer=None):
# pseudo_page, :left, :right or :first # pseudo_page, :left, :right or :first
val = self._tokenvalue(token) val = self._tokenvalue(token)
if ':' == expected and u':' == val: if ':' == expected and u':' == val:
try: try:
identtoken = tokenizer.next() identtoken = tokenizer.next()
except StopIteration: except StopIteration:
self._log.error( self._log.error(
u'CSSPageRule selectorText: No IDENT found.', token) u'CSSPageRule selectorText: No IDENT found.', token)
else: else:
ival, ityp = self._tokenvalue(identtoken), self._type(identtoken) ival, ityp = self._tokenvalue(identtoken), self._type(identtoken)
if self._prods.IDENT != ityp: if self._prods.IDENT != ityp:
self._log.error( self._log.error(
u'CSSPageRule selectorText: Expected IDENT but found: %r' % u'CSSPageRule selectorText: Expected IDENT but found: %r' %
ival, token) ival, token)
else: else:
new['selector'] = val + ival new['selector'] = val + ival
seq.append(new['selector'], 'selector') seq.append(new['selector'], 'selector')
return 'EOF' return 'EOF'
return expected return expected
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error( self._log.error(
u'CSSPageRule selectorText: Unexpected CHAR: %r' % val, token) u'CSSPageRule selectorText: Unexpected CHAR: %r' % val, token)
return expected return expected
def S(expected, seq, token, tokenizer=None): def S(expected, seq, token, tokenizer=None):
"Does not raise if EOF is found." "Does not raise if EOF is found."
return expected return expected
def COMMENT(expected, seq, token, tokenizer=None): def COMMENT(expected, seq, token, tokenizer=None):
"Does not raise if EOF is found." "Does not raise if EOF is found."
seq.append(cssutils.css.CSSComment([token]), 'COMMENT') seq.append(cssutils.css.CSSComment([token]), 'COMMENT')
return expected return expected
newseq = self._tempSeq() newseq = self._tempSeq()
wellformed, expected = self._parse(expected=':', wellformed, expected = self._parse(expected=':',
seq=newseq, tokenizer=self._tokenize2(selectorText), seq=newseq, tokenizer=self._tokenize2(selectorText),
productions={'CHAR': _char, productions={'CHAR': _char,
'COMMENT': COMMENT, 'COMMENT': COMMENT,
'S': S}, 'S': S},
new=new) new=new)
wellformed = wellformed and new['wellformed'] wellformed = wellformed and new['wellformed']
newselector = new['selector'] newselector = new['selector']
# post conditions # post conditions
if expected == 'ident': if expected == 'ident':
self._log.error( self._log.error(
u'CSSPageRule selectorText: No valid selector: %r' % u'CSSPageRule selectorText: No valid selector: %r' %
self._valuestr(selectorText)) self._valuestr(selectorText))
if not newselector in (None, u':first', u':left', u':right'): if not newselector in (None, u':first', u':left', u':right'):
self._log.warn(u'CSSPageRule: Unknown CSS 2.1 @page selector: %r' % self._log.warn(u'CSSPageRule: Unknown CSS 2.1 @page selector: %r' %
newselector, neverraise=True) newselector, neverraise=True)
return newselector, newseq return newselector, newseq
def _getCssText(self): def _getCssText(self):
""" """
returns serialized property cssText returns serialized property cssText
""" """
return cssutils.ser.do_CSSPageRule(self) return cssutils.ser.do_CSSPageRule(self)
def _setCssText(self, cssText): def _setCssText(self, cssText):
""" """
DOMException on setting DOMException on setting
- SYNTAX_ERR: (self, StyleDeclaration) - SYNTAX_ERR: (self, StyleDeclaration)
Raised if the specified CSS string value has a syntax error and Raised if the specified CSS string value has a syntax error and
is unparsable. is unparsable.
- INVALID_MODIFICATION_ERR: (self) - INVALID_MODIFICATION_ERR: (self)
Raised if the specified CSS string value represents a different Raised if the specified CSS string value represents a different
type of rule than the current one. type of rule than the current one.
- HIERARCHY_REQUEST_ERR: (CSSStylesheet) - HIERARCHY_REQUEST_ERR: (CSSStylesheet)
Raised if the rule cannot be inserted at this point in the Raised if the rule cannot be inserted at this point in the
style sheet. style sheet.
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule) - NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
Raised if the rule is readonly. Raised if the rule is readonly.
""" """
super(CSSPageRule, self)._setCssText(cssText) super(CSSPageRule, self)._setCssText(cssText)
tokenizer = self._tokenize2(cssText) tokenizer = self._tokenize2(cssText)
if self._type(self._nexttoken(tokenizer)) != self._prods.PAGE_SYM: if self._type(self._nexttoken(tokenizer)) != self._prods.PAGE_SYM:
self._log.error(u'CSSPageRule: No CSSPageRule found: %s' % self._log.error(u'CSSPageRule: No CSSPageRule found: %s' %
self._valuestr(cssText), self._valuestr(cssText),
error=xml.dom.InvalidModificationErr) error=xml.dom.InvalidModificationErr)
else: else:
wellformed = True wellformed = True
selectortokens, startbrace = self._tokensupto2(tokenizer, selectortokens, startbrace = self._tokensupto2(tokenizer,
blockstartonly=True, blockstartonly=True,
separateEnd=True) separateEnd=True)
styletokens, braceorEOFtoken = self._tokensupto2(tokenizer, styletokens, braceorEOFtoken = self._tokensupto2(tokenizer,
blockendonly=True, blockendonly=True,
separateEnd=True) separateEnd=True)
nonetoken = self._nexttoken(tokenizer) nonetoken = self._nexttoken(tokenizer)
if self._tokenvalue(startbrace) != u'{': if self._tokenvalue(startbrace) != u'{':
wellformed = False wellformed = False
self._log.error( self._log.error(
u'CSSPageRule: No start { of style declaration found: %r' % u'CSSPageRule: No start { of style declaration found: %r' %
self._valuestr(cssText), startbrace) self._valuestr(cssText), startbrace)
elif nonetoken: elif nonetoken:
wellformed = False wellformed = False
self._log.error( self._log.error(
u'CSSPageRule: Trailing content found.', token=nonetoken) u'CSSPageRule: Trailing content found.', token=nonetoken)
newselector, newselectorseq = self.__parseSelectorText(selectortokens) newselector, newselectorseq = self.__parseSelectorText(selectortokens)
newstyle = CSSStyleDeclaration() newstyle = CSSStyleDeclaration()
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken) val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
if val != u'}' and typ != 'EOF': if val != u'}' and typ != 'EOF':
wellformed = False wellformed = False
self._log.error( self._log.error(
u'CSSPageRule: No "}" after style declaration found: %r' % u'CSSPageRule: No "}" after style declaration found: %r' %
self._valuestr(cssText)) self._valuestr(cssText))
else: else:
if 'EOF' == typ: if 'EOF' == typ:
# add again as style needs it # add again as style needs it
styletokens.append(braceorEOFtoken) styletokens.append(braceorEOFtoken)
newstyle.cssText = styletokens newstyle.cssText = styletokens
if wellformed: if wellformed:
self._selectorText = newselector # already parsed self._selectorText = newselector # already parsed
self.style = newstyle self.style = newstyle
self._setSeq(newselectorseq) # contains upto style only self._setSeq(newselectorseq) # contains upto style only
cssText = property(_getCssText, _setCssText, cssText = property(_getCssText, _setCssText,
doc="(DOM) The parsable textual representation of the rule.") doc="(DOM) The parsable textual representation of the rule.")
def _getSelectorText(self): def _getSelectorText(self):
""" """
wrapper for cssutils Selector object wrapper for cssutils Selector object
""" """
return self._selectorText return self._selectorText
def _setSelectorText(self, selectorText): def _setSelectorText(self, selectorText):
""" """
wrapper for cssutils Selector object wrapper for cssutils Selector object
selector: DOM String selector: DOM String
in CSS 2.1 one of in CSS 2.1 one of
- :first - :first
- :left - :left
- :right - :right
- empty - empty
If WS or Comments are included they are ignored here! Only If WS or Comments are included they are ignored here! Only
way to add a comment is via setting ``cssText`` way to add a comment is via setting ``cssText``
DOMException on setting DOMException on setting
- SYNTAX_ERR: - SYNTAX_ERR:
Raised if the specified CSS string value has a syntax error Raised if the specified CSS string value has a syntax error
and is unparsable. and is unparsable.
- NO_MODIFICATION_ALLOWED_ERR: (self) - NO_MODIFICATION_ALLOWED_ERR: (self)
Raised if this rule is readonly. Raised if this rule is readonly.
""" """
self._checkReadonly() self._checkReadonly()
# may raise SYNTAX_ERR # may raise SYNTAX_ERR
newselectortext, newseq = self.__parseSelectorText(selectorText) newselectortext, newseq = self.__parseSelectorText(selectorText)
if newselectortext: if newselectortext:
for i, x in enumerate(self.seq): for i, x in enumerate(self.seq):
if x == self._selectorText: if x == self._selectorText:
self.seq[i] = newselectortext self.seq[i] = newselectortext
self._selectorText = newselectortext self._selectorText = newselectortext
selectorText = property(_getSelectorText, _setSelectorText, selectorText = property(_getSelectorText, _setSelectorText,
doc="""(DOM) The parsable textual representation of the page selector for the rule.""") doc="""(DOM) The parsable textual representation of the page selector for the rule.""")
def _getStyle(self): def _getStyle(self):
return self._style return self._style
def _setStyle(self, style): def _setStyle(self, style):
""" """
style style
StyleDeclaration or string StyleDeclaration or string
""" """
self._checkReadonly() self._checkReadonly()
if isinstance(style, basestring): if isinstance(style, basestring):
self._style.cssText = style self._style.cssText = style
else: else:
# cssText would be serialized with optional preferences # cssText would be serialized with optional preferences
# so use seq! # so use seq!
self._style._seq = style.seq self._style._seq = style.seq
style = property(_getStyle, _setStyle, style = property(_getStyle, _setStyle,
doc="(DOM) The declaration-block of this rule set.") doc="(DOM) The declaration-block of this rule set.")
def __repr__(self): def __repr__(self):
return "cssutils.css.%s(selectorText=%r, style=%r)" % ( return "cssutils.css.%s(selectorText=%r, style=%r)" % (
self.__class__.__name__, self.selectorText, self.style.cssText) self.__class__.__name__, self.selectorText, self.style.cssText)
def __str__(self): def __str__(self):
return "<cssutils.css.%s object selectorText=%r style=%r at 0x%x>" % ( return "<cssutils.css.%s object selectorText=%r style=%r at 0x%x>" % (
self.__class__.__name__, self.selectorText, self.style.cssText, self.__class__.__name__, self.selectorText, self.style.cssText,
id(self)) id(self))

View File

@ -1,349 +1,349 @@
"""CSS2Properties (partly!) implements DOM Level 2 CSS CSS2Properties used """CSS2Properties (partly!) implements DOM Level 2 CSS CSS2Properties used
by CSSStyleDeclaration by CSSStyleDeclaration
TODO: CSS2Properties TODO: CSS2Properties
If an implementation does implement this interface, it is expected to If an implementation does implement this interface, it is expected to
understand the specific syntax of the shorthand properties, and apply understand the specific syntax of the shorthand properties, and apply
their semantics; when the margin property is set, for example, the their semantics; when the margin property is set, for example, the
marginTop, marginRight, marginBottom and marginLeft properties are marginTop, marginRight, marginBottom and marginLeft properties are
actually being set by the underlying implementation. actually being set by the underlying implementation.
When dealing with CSS "shorthand" properties, the shorthand properties When dealing with CSS "shorthand" properties, the shorthand properties
should be decomposed into their component longhand properties as should be decomposed into their component longhand properties as
appropriate, and when querying for their value, the form returned appropriate, and when querying for their value, the form returned
should be the shortest form exactly equivalent to the declarations made should be the shortest form exactly equivalent to the declarations made
in the ruleset. However, if there is no shorthand declaration that in the ruleset. However, if there is no shorthand declaration that
could be added to the ruleset without changing in any way the rules 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 already declared in the ruleset (i.e., by adding longhand rules that
were previously not declared in the ruleset), then the empty string were previously not declared in the ruleset), then the empty string
should be returned for the shorthand property. should be returned for the shorthand property.
For example, querying for the font property should not return For example, querying for the font property should not return
"normal normal normal 14pt/normal Arial, sans-serif", when "normal normal normal 14pt/normal Arial, sans-serif", when
"14pt Arial, sans-serif" suffices. (The normals are initial values, and "14pt Arial, sans-serif" suffices. (The normals are initial values, and
are implied by use of the longhand property.) are implied by use of the longhand property.)
If the values for all the longhand properties that compose a particular If the values for all the longhand properties that compose a particular
string are the initial values, then a string consisting of all the string are the initial values, then a string consisting of all the
initial values should be returned (e.g. a border-width value of initial values should be returned (e.g. a border-width value of
"medium" should be returned as such, not as ""). "medium" should be returned as such, not as "").
For some shorthand properties that take missing values from other For some shorthand properties that take missing values from other
sides, such as the margin, padding, and border-[width|style|color] sides, such as the margin, padding, and border-[width|style|color]
properties, the minimum number of sides possible should be used; i.e., properties, the minimum number of sides possible should be used; i.e.,
"0px 10px" will be returned instead of "0px 10px 0px 10px". "0px 10px" will be returned instead of "0px 10px 0px 10px".
If the value of a shorthand property can not be decomposed into its If the value of a shorthand property can not be decomposed into its
component longhand properties, as is the case for the font property component longhand properties, as is the case for the font property
with a value of "menu", querying for the values of the component with a value of "menu", querying for the values of the component
longhand properties should return the empty string. longhand properties should return the empty string.
TODO: CSS2Properties DOMImplementation TODO: CSS2Properties DOMImplementation
The interface found within this section are not mandatory. A DOM The interface found within this section are not mandatory. A DOM
application can use the hasFeature method of the DOMImplementation application can use the hasFeature method of the DOMImplementation
interface to determine whether it is supported or not. The feature interface to determine whether it is supported or not. The feature
string for this extended interface listed in this section is "CSS2" string for this extended interface listed in this section is "CSS2"
and the version is "2.0". and the version is "2.0".
cssvalues cssvalues
========= =========
contributed by Kevin D. Smith, thanks! contributed by Kevin D. Smith, thanks!
"cssvalues" is used as a property validator. "cssvalues" is used as a property validator.
it is an importable object that contains a dictionary of compiled regular 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 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 names. The values are compiled regular expressions that can be used to
validate the values for that property. (Actually, the values are references validate the values for that property. (Actually, the values are references
to the 'match' method of a compiled regular expression, so that they are to the 'match' method of a compiled regular expression, so that they are
simply called like functions.) simply called like functions.)
""" """
__all__ = ['CSS2Properties', 'cssvalues'] __all__ = ['CSS2Properties', 'cssvalues']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: cssproperties.py 1116 2008-03-05 13:52:23Z cthedot $' __version__ = '$Id: cssproperties.py 1469 2008-09-15 19:06:00Z cthedot $'
import re import re
""" """
Define some regular expression fragments that will be used as Define some regular expression fragments that will be used as
macros within the CSS property value regular expressions. macros within the CSS property value regular expressions.
""" """
MACROS = { MACROS = {
'ident': r'[-]?{nmstart}{nmchar}*', 'ident': r'[-]?{nmstart}{nmchar}*',
'name': r'{nmchar}+', 'name': r'{nmchar}+',
'nmstart': r'[_a-z]|{nonascii}|{escape}', 'nmstart': r'[_a-z]|{nonascii}|{escape}',
'nonascii': r'[^\0-\177]', 'nonascii': r'[^\0-\177]',
'unicode': r'\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?', 'unicode': r'\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?',
'escape': r'{unicode}|\\[ -~\200-\777]', 'escape': r'{unicode}|\\[ -~\200-\777]',
# 'escape': r'{unicode}|\\[ -~\200-\4177777]', # 'escape': r'{unicode}|\\[ -~\200-\4177777]',
'int': r'[-]?\d+', 'int': r'[-]?\d+',
'nmchar': r'[\w-]|{nonascii}|{escape}', 'nmchar': r'[\w-]|{nonascii}|{escape}',
'num': r'[-]?\d+|[-]?\d*\.\d+', 'num': r'[-]?\d+|[-]?\d*\.\d+',
'number': r'{num}', 'number': r'{num}',
'string': r'{string1}|{string2}', 'string': r'{string1}|{string2}',
'string1': r'"(\\\"|[^\"])*"', 'string1': r'"(\\\"|[^\"])*"',
'string2': r"'(\\\'|[^\'])*'", 'string2': r"'(\\\'|[^\'])*'",
'nl': r'\n|\r\n|\r|\f', 'nl': r'\n|\r\n|\r|\f',
'w': r'\s*', 'w': r'\s*',
'integer': r'{int}', 'integer': r'{int}',
'length': r'0|{num}(em|ex|px|in|cm|mm|pt|pc)', 'length': r'0|{num}(em|ex|px|in|cm|mm|pt|pc)',
'angle': r'0|{num}(deg|grad|rad)', 'angle': r'0|{num}(deg|grad|rad)',
'time': r'0|{num}m?s', 'time': r'0|{num}m?s',
'frequency': r'0|{num}k?Hz', '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}\)', '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}\)', 'uri': r'url\({w}({string}|(\\\)|[^\)])+){w}\)',
'percentage': r'{num}%', 'percentage': r'{num}%',
'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset', 'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset',
'border-color': '{color}', 'border-color': '{color}',
'border-width': '{length}|thin|medium|thick', 'border-width': '{length}|thin|medium|thick',
'background-color': r'{color}|transparent|inherit', 'background-color': r'{color}|transparent|inherit',
'background-image': r'{uri}|none|inherit', 'background-image': r'{uri}|none|inherit',
'background-position': r'({percentage}|{length})(\s*({percentage}|{length}))?|((top|center|bottom)\s*(left|center|right))|((left|center|right)\s*(top|center|bottom))|inherit', 'background-position': r'({percentage}|{length})(\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-repeat': r'repeat|repeat-x|repeat-y|no-repeat|inherit',
'background-attachment': r'scroll|fixed|inherit', 'background-attachment': r'scroll|fixed|inherit',
'shape': r'rect\(({w}({length}|auto}){w},){3}{w}({length}|auto){w}\)', 'shape': r'rect\(({w}({length}|auto}){w},){3}{w}({length}|auto){w}\)',
'counter': r'counter\({w}{identifier}{w}(?:,{w}{list-style-type}{w})?\)', 'counter': r'counter\({w}{identifier}{w}(?:,{w}{list-style-type}{w})?\)',
'identifier': r'{ident}', 'identifier': r'{ident}',
'family-name': r'{string}|{identifier}', 'family-name': r'{string}|{identifier}',
'generic-family': r'serif|sans-serif|cursive|fantasy|monospace', 'generic-family': r'serif|sans-serif|cursive|fantasy|monospace',
'absolute-size': r'(x?x-)?(small|large)|medium', 'absolute-size': r'(x?x-)?(small|large)|medium',
'relative-size': r'smaller|larger', 'relative-size': r'smaller|larger',
'font-family': r'(({family-name}|{generic-family}){w},{w})*({family-name}|{generic-family})|inherit', '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-size': r'{absolute-size}|{relative-size}|{length}|{percentage}|inherit',
'font-style': r'normal|italic|oblique|inherit', 'font-style': r'normal|italic|oblique|inherit',
'font-variant': r'normal|small-caps|inherit', 'font-variant': r'normal|small-caps|inherit',
'font-weight': r'normal|bold|bolder|lighter|[1-9]00|inherit', 'font-weight': r'normal|bold|bolder|lighter|[1-9]00|inherit',
'line-height': r'normal|{number}|{length}|{percentage}|inherit', 'line-height': r'normal|{number}|{length}|{percentage}|inherit',
'list-style-image': r'{uri}|none|inherit', 'list-style-image': r'{uri}|none|inherit',
'list-style-position': r'inside|outside|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', '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', 'margin-width': r'{length}|{percentage}|auto',
'outline-color': r'{color}|invert|inherit', 'outline-color': r'{color}|invert|inherit',
'outline-style': r'{border-style}|inherit', 'outline-style': r'{border-style}|inherit',
'outline-width': r'{border-width}|inherit', 'outline-width': r'{border-width}|inherit',
'padding-width': r'{length}|{percentage}', 'padding-width': r'{length}|{percentage}',
'specific-voice': r'{identifier}', 'specific-voice': r'{identifier}',
'generic-voice': r'male|female|child', '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', '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}', 'border-attrs': r'{border-width}|{border-style}|{border-color}',
'background-attrs': r'{background-color}|{background-image}|{background-repeat}|{background-attachment}|{background-position}', '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}', 'list-attrs': r'{list-style-type}|{list-style-position}|{list-style-image}',
'font-attrs': r'{font-style}|{font-variant}|{font-weight}', 'font-attrs': r'{font-style}|{font-variant}|{font-weight}',
'outline-attrs': r'{outline-color}|{outline-style}|{outline-width}', 'outline-attrs': r'{outline-color}|{outline-style}|{outline-width}',
'text-attrs': r'underline|overline|line-through|blink', 'text-attrs': r'underline|overline|line-through|blink',
} }
""" """
Define the regular expressions for validation all CSS values Define the regular expressions for validation all CSS values
""" """
cssvalues = { 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', '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-attachment': r'{background-attachment}',
'background-color': r'{background-color}', 'background-color': r'{background-color}',
'background-image': r'{background-image}', 'background-image': r'{background-image}',
'background-position': r'{background-position}', 'background-position': r'{background-position}',
'background-repeat': r'{background-repeat}', 'background-repeat': r'{background-repeat}',
# Each piece should only be allowed one time # Each piece should only be allowed one time
'background': r'{background-attrs}(\s+{background-attrs})*|inherit', 'background': r'{background-attrs}(\s+{background-attrs})*|inherit',
'border-collapse': r'collapse|separate|inherit', 'border-collapse': r'collapse|separate|inherit',
'border-color': r'({border-color}|transparent)(\s+({border-color}|transparent)){0,3}|inherit', 'border-color': r'({border-color}|transparent)(\s+({border-color}|transparent)){0,3}|inherit',
'border-spacing': r'{length}(\s+{length})?|inherit', 'border-spacing': r'{length}(\s+{length})?|inherit',
'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit', 'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit',
'border-top': r'{border-attrs}(\s+{border-attrs})*|inherit', 'border-top': r'{border-attrs}(\s+{border-attrs})*|inherit',
'border-right': 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-bottom': r'{border-attrs}(\s+{border-attrs})*|inherit',
'border-left': 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-top-color': r'{border-color}|transparent|inherit',
'border-right-color': r'{border-color}|transparent|inherit', 'border-right-color': r'{border-color}|transparent|inherit',
'border-bottom-color': r'{border-color}|transparent|inherit', 'border-bottom-color': r'{border-color}|transparent|inherit',
'border-left-color': r'{border-color}|transparent|inherit', 'border-left-color': r'{border-color}|transparent|inherit',
'border-top-style': r'{border-style}|inherit', 'border-top-style': r'{border-style}|inherit',
'border-right-style': r'{border-style}|inherit', 'border-right-style': r'{border-style}|inherit',
'border-bottom-style': r'{border-style}|inherit', 'border-bottom-style': r'{border-style}|inherit',
'border-left-style': r'{border-style}|inherit', 'border-left-style': r'{border-style}|inherit',
'border-top-width': r'{border-width}|inherit', 'border-top-width': r'{border-width}|inherit',
'border-right-width': r'{border-width}|inherit', 'border-right-width': r'{border-width}|inherit',
'border-bottom-width': r'{border-width}|inherit', 'border-bottom-width': r'{border-width}|inherit',
'border-right-width': r'{border-width}|inherit', 'border-left-width': r'{border-width}|inherit',
'border-width': r'{border-width}(\s+{border-width}){0,3}|inherit', 'border-width': r'{border-width}(\s+{border-width}){0,3}|inherit',
'border': r'{border-attrs}(\s+{border-attrs})*|inherit', 'border': r'{border-attrs}(\s+{border-attrs})*|inherit',
'bottom': r'{length}|{percentage}|auto|inherit', 'bottom': r'{length}|{percentage}|auto|inherit',
'caption-side': r'top|bottom|inherit', 'caption-side': r'top|bottom|inherit',
'clear': r'none|left|right|both|inherit', 'clear': r'none|left|right|both|inherit',
'clip': r'{shape}|auto|inherit', 'clip': r'{shape}|auto|inherit',
'color': r'{color}|inherit', 'color': r'{color}|inherit',
'content': r'normal|{content}(\s+{content})*|inherit', 'content': r'normal|{content}(\s+{content})*|inherit',
'counter-increment': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|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', 'counter-reset': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
'cue-after': r'{uri}|none|inherit', 'cue-after': r'{uri}|none|inherit',
'cue-before': r'{uri}|none|inherit', 'cue-before': r'{uri}|none|inherit',
'cue': r'({uri}|none|inherit){1,2}|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', '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', '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', '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', 'elevation': r'{angle}|below|level|above|higher|lower|inherit',
'empty-cells': r'show|hide|inherit', 'empty-cells': r'show|hide|inherit',
'float': r'left|right|none|inherit', 'float': r'left|right|none|inherit',
'font-family': r'{font-family}', 'font-family': r'{font-family}',
'font-size': r'{font-size}', 'font-size': r'{font-size}',
'font-style': r'{font-style}', 'font-style': r'{font-style}',
'font-variant': r'{font-variant}', 'font-variant': r'{font-variant}',
'font-weight': r'{font-weight}', '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', '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', 'height': r'{length}|{percentage}|auto|inherit',
'left': r'{length}|{percentage}|auto|inherit', 'left': r'{length}|{percentage}|auto|inherit',
'letter-spacing': r'normal|{length}|inherit', 'letter-spacing': r'normal|{length}|inherit',
'line-height': r'{line-height}', 'line-height': r'{line-height}',
'list-style-image': r'{list-style-image}', 'list-style-image': r'{list-style-image}',
'list-style-position': r'{list-style-position}', 'list-style-position': r'{list-style-position}',
'list-style-type': r'{list-style-type}', 'list-style-type': r'{list-style-type}',
'list-style': r'{list-attrs}(\s+{list-attrs})*|inherit', 'list-style': r'{list-attrs}(\s+{list-attrs})*|inherit',
'margin-right': r'{margin-width}|inherit', 'margin-right': r'{margin-width}|inherit',
'margin-left': r'{margin-width}|inherit', 'margin-left': r'{margin-width}|inherit',
'margin-top': r'{margin-width}|inherit', 'margin-top': r'{margin-width}|inherit',
'margin-bottom': r'{margin-width}|inherit', 'margin-bottom': r'{margin-width}|inherit',
'margin': r'{margin-width}(\s+{margin-width}){0,3}|inherit', 'margin': r'{margin-width}(\s+{margin-width}){0,3}|inherit',
'max-height': r'{length}|{percentage}|none|inherit', 'max-height': r'{length}|{percentage}|none|inherit',
'max-width': r'{length}|{percentage}|none|inherit', 'max-width': r'{length}|{percentage}|none|inherit',
'min-height': r'{length}|{percentage}|none|inherit', 'min-height': r'{length}|{percentage}|none|inherit',
'min-width': r'{length}|{percentage}|none|inherit', 'min-width': r'{length}|{percentage}|none|inherit',
'orphans': r'{integer}|inherit', 'orphans': r'{integer}|inherit',
'outline-color': r'{outline-color}', 'outline-color': r'{outline-color}',
'outline-style': r'{outline-style}', 'outline-style': r'{outline-style}',
'outline-width': r'{outline-width}', 'outline-width': r'{outline-width}',
'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit', 'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit',
'overflow': r'visible|hidden|scroll|auto|inherit', 'overflow': r'visible|hidden|scroll|auto|inherit',
'padding-top': r'{padding-width}|inherit', 'padding-top': r'{padding-width}|inherit',
'padding-right': r'{padding-width}|inherit', 'padding-right': r'{padding-width}|inherit',
'padding-bottom': r'{padding-width}|inherit', 'padding-bottom': r'{padding-width}|inherit',
'padding-left': r'{padding-width}|inherit', 'padding-left': r'{padding-width}|inherit',
'padding': r'{padding-width}(\s+{padding-width}){0,3}|inherit', 'padding': r'{padding-width}(\s+{padding-width}){0,3}|inherit',
'page-break-after': r'auto|always|avoid|left|right|inherit', 'page-break-after': r'auto|always|avoid|left|right|inherit',
'page-break-before': r'auto|always|avoid|left|right|inherit', 'page-break-before': r'auto|always|avoid|left|right|inherit',
'page-break-inside': r'avoid|auto|inherit', 'page-break-inside': r'avoid|auto|inherit',
'pause-after': r'{time}|{percentage}|inherit', 'pause-after': r'{time}|{percentage}|inherit',
'pause-before': r'{time}|{percentage}|inherit', 'pause-before': r'{time}|{percentage}|inherit',
'pause': r'({time}|{percentage}){1,2}|inherit', 'pause': r'({time}|{percentage}){1,2}|inherit',
'pitch-range': r'{number}|inherit', 'pitch-range': r'{number}|inherit',
'pitch': r'{frequency}|x-low|low|medium|high|x-high|inherit', 'pitch': r'{frequency}|x-low|low|medium|high|x-high|inherit',
'play-during': r'{uri}(\s+(mix|repeat))*|auto|none|inherit', 'play-during': r'{uri}(\s+(mix|repeat))*|auto|none|inherit',
'position': r'static|relative|absolute|fixed|inherit', 'position': r'static|relative|absolute|fixed|inherit',
'quotes': r'({string}\s+{string})(\s+{string}\s+{string})*|none|inherit', 'quotes': r'({string}\s+{string})(\s+{string}\s+{string})*|none|inherit',
'richness': r'{number}|inherit', 'richness': r'{number}|inherit',
'right': r'{length}|{percentage}|auto|inherit', 'right': r'{length}|{percentage}|auto|inherit',
'speak-header': r'once|always|inherit', 'speak-header': r'once|always|inherit',
'speak-numeral': r'digits|continuous|inherit', 'speak-numeral': r'digits|continuous|inherit',
'speak-punctuation': r'code|none|inherit', 'speak-punctuation': r'code|none|inherit',
'speak': r'normal|none|spell-out|inherit', 'speak': r'normal|none|spell-out|inherit',
'speech-rate': r'{number}|x-slow|slow|medium|fast|x-fast|faster|slower|inherit', 'speech-rate': r'{number}|x-slow|slow|medium|fast|x-fast|faster|slower|inherit',
'stress': r'{number}|inherit', 'stress': r'{number}|inherit',
'table-layout': r'auto|fixed|inherit', 'table-layout': r'auto|fixed|inherit',
'text-align': r'left|right|center|justify|inherit', 'text-align': r'left|right|center|justify|inherit',
'text-decoration': r'none|{text-attrs}(\s+{text-attrs})*|inherit', 'text-decoration': r'none|{text-attrs}(\s+{text-attrs})*|inherit',
'text-indent': r'{length}|{percentage}|inherit', 'text-indent': r'{length}|{percentage}|inherit',
'text-transform': r'capitalize|uppercase|lowercase|none|inherit', 'text-transform': r'capitalize|uppercase|lowercase|none|inherit',
'top': r'{length}|{percentage}|auto|inherit', 'top': r'{length}|{percentage}|auto|inherit',
'unicode-bidi': r'normal|embed|bidi-override|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', 'vertical-align': r'baseline|sub|super|top|text-top|middle|bottom|text-bottom|{percentage}|{length}|inherit',
'visibility': r'visible|hidden|collapse|inherit', 'visibility': r'visible|hidden|collapse|inherit',
'voice-family': r'({specific-voice}|{generic-voice}{w},{w})*({specific-voice}|{generic-voice})|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', 'volume': r'{number}|{percentage}|silent|x-soft|soft|medium|loud|x-loud|inherit',
'white-space': r'normal|pre|nowrap|pre-wrap|pre-line|inherit', 'white-space': r'normal|pre|nowrap|pre-wrap|pre-line|inherit',
'widows': r'{integer}|inherit', 'widows': r'{integer}|inherit',
'width': r'{length}|{percentage}|auto|inherit', 'width': r'{length}|{percentage}|auto|inherit',
'word-spacing': r'normal|{length}|inherit', 'word-spacing': r'normal|{length}|inherit',
'z-index': r'auto|{integer}|inherit', 'z-index': r'auto|{integer}|inherit',
} }
def _expand_macros(tokdict): def _expand_macros(tokdict):
""" Expand macros in token dictionary """ """ Expand macros in token dictionary """
def macro_value(m): def macro_value(m):
return '(?:%s)' % MACROS[m.groupdict()['macro']] return '(?:%s)' % MACROS[m.groupdict()['macro']]
for key, value in tokdict.items(): for key, value in tokdict.items():
while re.search(r'{[a-z][a-z0-9-]*}', value): while re.search(r'{[a-z][a-z0-9-]*}', value):
value = re.sub(r'{(?P<macro>[a-z][a-z0-9-]*)}', value = re.sub(r'{(?P<macro>[a-z][a-z0-9-]*)}',
macro_value, value) macro_value, value)
tokdict[key] = value tokdict[key] = value
return tokdict return tokdict
def _compile_regexes(tokdict): def _compile_regexes(tokdict):
""" Compile all regular expressions into callable objects """ """ Compile all regular expressions into callable objects """
for key, value in tokdict.items(): for key, value in tokdict.items():
tokdict[key] = re.compile('^(?:%s)$' % value, re.I).match tokdict[key] = re.compile('^(?:%s)$' % value, re.I).match
return tokdict return tokdict
_compile_regexes(_expand_macros(cssvalues)) _compile_regexes(_expand_macros(cssvalues))
# functions to convert between CSS and DOM name # functions to convert between CSS and DOM name
_reCSStoDOMname = re.compile('-[a-z]', re.I) _reCSStoDOMname = re.compile('-[a-z]', re.I)
def _toDOMname(CSSname): def _toDOMname(CSSname):
""" """
returns DOMname for given CSSname e.g. for CSSname 'font-style' returns returns DOMname for given CSSname e.g. for CSSname 'font-style' returns
'fontStyle' 'fontStyle'
""" """
def _doCSStoDOMname2(m): return m.group(0)[1].capitalize() def _doCSStoDOMname2(m): return m.group(0)[1].capitalize()
return _reCSStoDOMname.sub(_doCSStoDOMname2, CSSname) return _reCSStoDOMname.sub(_doCSStoDOMname2, CSSname)
_reDOMtoCSSname = re.compile('([A-Z])[a-z]+') _reDOMtoCSSname = re.compile('([A-Z])[a-z]+')
def _toCSSname(DOMname): def _toCSSname(DOMname):
""" """
returns CSSname for given DOMname e.g. for DOMname 'fontStyle' returns returns CSSname for given DOMname e.g. for DOMname 'fontStyle' returns
'font-style' 'font-style'
""" """
def _doDOMtoCSSname2(m): return '-' + m.group(0).lower() def _doDOMtoCSSname2(m): return '-' + m.group(0).lower()
return _reDOMtoCSSname.sub(_doDOMtoCSSname2, DOMname) return _reDOMtoCSSname.sub(_doDOMtoCSSname2, DOMname)
class CSS2Properties(object): class CSS2Properties(object):
""" """
The CSS2Properties interface represents a convenience mechanism The CSS2Properties interface represents a convenience mechanism
for retrieving and setting properties within a CSSStyleDeclaration. for retrieving and setting properties within a CSSStyleDeclaration.
The attributes of this interface correspond to all the properties The attributes of this interface correspond to all the properties
specified in CSS2. Getting an attribute of this interface is specified in CSS2. Getting an attribute of this interface is
equivalent to calling the getPropertyValue method of the equivalent to calling the getPropertyValue method of the
CSSStyleDeclaration interface. Setting an attribute of this CSSStyleDeclaration interface. Setting an attribute of this
interface is equivalent to calling the setProperty method of the interface is equivalent to calling the setProperty method of the
CSSStyleDeclaration interface. CSSStyleDeclaration interface.
cssutils actually also allows usage of ``del`` to remove a CSS property cssutils actually also allows usage of ``del`` to remove a CSS property
from a CSSStyleDeclaration. from a CSSStyleDeclaration.
This is an abstract class, the following functions need to be present This is an abstract class, the following functions need to be present
in inheriting class: in inheriting class:
- ``_getP`` - ``_getP``
- ``_setP`` - ``_setP``
- ``_delP`` - ``_delP``
""" """
# actual properties are set after the class definition! # actual properties are set after the class definition!
def _getP(self, CSSname): pass def _getP(self, CSSname): pass
def _setP(self, CSSname, value): pass def _setP(self, CSSname, value): pass
def _delP(self, CSSname): pass def _delP(self, CSSname): pass
# add list of DOMname properties to CSS2Properties # add list of DOMname properties to CSS2Properties
# used for CSSStyleDeclaration to check if allowed properties # used for CSSStyleDeclaration to check if allowed properties
# but somehow doubled, any better way? # but somehow doubled, any better way?
CSS2Properties._properties = [_toDOMname(p) for p in cssvalues.keys()] CSS2Properties._properties = [_toDOMname(p) for p in cssvalues.keys()]
# add CSS2Properties to CSSStyleDeclaration: # add CSS2Properties to CSSStyleDeclaration:
def __named_property_def(DOMname): def __named_property_def(DOMname):
""" """
closure to keep name known in each properties accessor function closure to keep name known in each properties accessor function
DOMname is converted to CSSname here, so actual calls use CSSname DOMname is converted to CSSname here, so actual calls use CSSname
""" """
CSSname = _toCSSname(DOMname) CSSname = _toCSSname(DOMname)
def _get(self): return self._getP(CSSname) def _get(self): return self._getP(CSSname)
def _set(self, value): self._setP(CSSname, value) def _set(self, value): self._setP(CSSname, value)
def _del(self): self._delP(CSSname) def _del(self): self._delP(CSSname)
return _get, _set, _del return _get, _set, _del
# add all CSS2Properties to CSSStyleDeclaration # add all CSS2Properties to CSSStyleDeclaration
for DOMname in CSS2Properties._properties: for DOMname in CSS2Properties._properties:
setattr(CSS2Properties, DOMname, setattr(CSS2Properties, DOMname,
property(*__named_property_def(DOMname))) property(*__named_property_def(DOMname)))

View File

@ -1,134 +1,134 @@
"""CSSRule implements DOM Level 2 CSS CSSRule.""" """CSSRule implements DOM Level 2 CSS CSSRule."""
__all__ = ['CSSRule'] __all__ = ['CSSRule']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: cssrule.py 1177 2008-03-20 17:47:23Z cthedot $' __version__ = '$Id: cssrule.py 1177 2008-03-20 17:47:23Z cthedot $'
import xml.dom import xml.dom
import cssutils import cssutils
class CSSRule(cssutils.util.Base2): class CSSRule(cssutils.util.Base2):
""" """
Abstract base interface for any type of CSS statement. This includes Abstract base interface for any type of CSS statement. This includes
both rule sets and at-rules. An implementation is expected to preserve 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 all rules specified in a CSS style sheet, even if the rule is not
recognized by the parser. Unrecognized rules are represented using the recognized by the parser. Unrecognized rules are represented using the
CSSUnknownRule interface. CSSUnknownRule interface.
Properties Properties
========== ==========
cssText: of type DOMString cssText: of type DOMString
The parsable textual representation of the rule. This reflects the The parsable textual representation of the rule. This reflects the
current state of the rule and not its initial value. current state of the rule and not its initial value.
parentRule: of type CSSRule, readonly parentRule: of type CSSRule, readonly
If this rule is contained inside another rule (e.g. a style rule 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 inside an @media block), this is the containing rule. If this rule
is not nested inside any other rules, this returns None. is not nested inside any other rules, this returns None.
parentStyleSheet: of type CSSStyleSheet, readonly parentStyleSheet: of type CSSStyleSheet, readonly
The style sheet that contains this rule. The style sheet that contains this rule.
type: of type unsigned short, readonly type: of type unsigned short, readonly
The type of the rule, as defined above. The expectation is that The type of the rule, as defined above. The expectation is that
binding-specific casting methods can be used to cast down from an binding-specific casting methods can be used to cast down from an
instance of the CSSRule interface to the specific derived interface instance of the CSSRule interface to the specific derived interface
implied by the type. implied by the type.
cssutils only cssutils only
------------- -------------
seq (READONLY): seq (READONLY):
contains sequence of parts of the rule including comments but contains sequence of parts of the rule including comments but
excluding @KEYWORD and braces excluding @KEYWORD and braces
typeString: string typeString: string
A string name of the type of this rule, e.g. 'STYLE_RULE'. Mainly A string name of the type of this rule, e.g. 'STYLE_RULE'. Mainly
useful for debugging useful for debugging
wellformed: wellformed:
if a rule is valid if a rule is valid
""" """
""" """
CSSRule type constants. CSSRule type constants.
An integer indicating which type of rule this is. An integer indicating which type of rule this is.
""" """
COMMENT = -1 # cssutils only COMMENT = -1 # cssutils only
UNKNOWN_RULE = 0 #u UNKNOWN_RULE = 0 #u
STYLE_RULE = 1 #s STYLE_RULE = 1 #s
CHARSET_RULE = 2 #c CHARSET_RULE = 2 #c
IMPORT_RULE = 3 #i IMPORT_RULE = 3 #i
MEDIA_RULE = 4 #m MEDIA_RULE = 4 #m
FONT_FACE_RULE = 5 #f FONT_FACE_RULE = 5 #f
PAGE_RULE = 6 #p PAGE_RULE = 6 #p
NAMESPACE_RULE = 7 # CSSOM NAMESPACE_RULE = 7 # CSSOM
_typestrings = ['UNKNOWN_RULE', 'STYLE_RULE', 'CHARSET_RULE', 'IMPORT_RULE', _typestrings = ['UNKNOWN_RULE', 'STYLE_RULE', 'CHARSET_RULE', 'IMPORT_RULE',
'MEDIA_RULE', 'FONT_FACE_RULE', 'PAGE_RULE', 'NAMESPACE_RULE', 'MEDIA_RULE', 'FONT_FACE_RULE', 'PAGE_RULE', 'NAMESPACE_RULE',
'COMMENT'] 'COMMENT']
type = UNKNOWN_RULE type = UNKNOWN_RULE
""" """
The type of this rule, as defined by a CSSRule type constant. The type of this rule, as defined by a CSSRule type constant.
Overwritten in derived classes. Overwritten in derived classes.
The expectation is that binding-specific casting methods can be used to The expectation is that binding-specific casting methods can be used to
cast down from an instance of the CSSRule interface to the specific cast down from an instance of the CSSRule interface to the specific
derived interface implied by the type. derived interface implied by the type.
(Casting not for this Python implementation I guess...) (Casting not for this Python implementation I guess...)
""" """
def __init__(self, parentRule=None, parentStyleSheet=None, readonly=False): def __init__(self, parentRule=None, parentStyleSheet=None, readonly=False):
""" """
set common attributes for all rules set common attributes for all rules
""" """
super(CSSRule, self).__init__() super(CSSRule, self).__init__()
self._parentRule = parentRule self._parentRule = parentRule
self._parentStyleSheet = parentStyleSheet self._parentStyleSheet = parentStyleSheet
self._setSeq(self._tempSeq()) self._setSeq(self._tempSeq())
# must be set after initialization of #inheriting rule is done # must be set after initialization of #inheriting rule is done
self._readonly = False self._readonly = False
def _setCssText(self, cssText): def _setCssText(self, cssText):
""" """
DOMException on setting DOMException on setting
- SYNTAX_ERR: - SYNTAX_ERR:
Raised if the specified CSS string value has a syntax error and Raised if the specified CSS string value has a syntax error and
is unparsable. is unparsable.
- INVALID_MODIFICATION_ERR: - INVALID_MODIFICATION_ERR:
Raised if the specified CSS string value represents a different Raised if the specified CSS string value represents a different
type of rule than the current one. type of rule than the current one.
- HIERARCHY_REQUEST_ERR: - HIERARCHY_REQUEST_ERR:
Raised if the rule cannot be inserted at this point in the Raised if the rule cannot be inserted at this point in the
style sheet. style sheet.
- NO_MODIFICATION_ALLOWED_ERR: (self) - NO_MODIFICATION_ALLOWED_ERR: (self)
Raised if the rule is readonly. Raised if the rule is readonly.
""" """
self._checkReadonly() self._checkReadonly()
cssText = property(lambda self: u'', _setCssText, cssText = property(lambda self: u'', _setCssText,
doc="""(DOM) The parsable textual representation of the rule. This doc="""(DOM) The parsable textual representation of the rule. This
reflects the current state of the rule and not its initial value. 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 The initial value is saved, but this may be removed in a future
version! version!
MUST BE OVERWRITTEN IN SUBCLASS TO WORK!""") MUST BE OVERWRITTEN IN SUBCLASS TO WORK!""")
def _setAtkeyword(self, akw): def _setAtkeyword(self, akw):
"""checks if new keyword is normalized same as old""" """checks if new keyword is normalized same as old"""
if not self.atkeyword or (self._normalize(akw) == if not self.atkeyword or (self._normalize(akw) ==
self._normalize(self.atkeyword)): self._normalize(self.atkeyword)):
self._atkeyword = akw self._atkeyword = akw
else: else:
self._log.error(u'%s: Invalid atkeyword for this rule: %r' % self._log.error(u'%s: Invalid atkeyword for this rule: %r' %
(self._normalize(self.atkeyword), akw), (self._normalize(self.atkeyword), akw),
error=xml.dom.InvalidModificationErr) error=xml.dom.InvalidModificationErr)
atkeyword = property(lambda self: self._atkeyword, _setAtkeyword, atkeyword = property(lambda self: self._atkeyword, _setAtkeyword,
doc=u"@keyword for @rules") doc=u"@keyword for @rules")
parentRule = property(lambda self: self._parentRule, parentRule = property(lambda self: self._parentRule,
doc=u"READONLY") doc=u"READONLY")
parentStyleSheet = property(lambda self: self._parentStyleSheet, parentStyleSheet = property(lambda self: self._parentStyleSheet,
doc=u"READONLY") doc=u"READONLY")
wellformed = property(lambda self: False, wellformed = property(lambda self: False,
doc=u"READONLY") doc=u"READONLY")
typeString = property(lambda self: CSSRule._typestrings[self.type], typeString = property(lambda self: CSSRule._typestrings[self.type],
doc="Name of this rules type.") doc="Name of this rules type.")

View File

@ -1,60 +1,60 @@
""" """
CSSRuleList implements DOM Level 2 CSS CSSRuleList. CSSRuleList implements DOM Level 2 CSS CSSRuleList.
Partly also Partly also
* http://dev.w3.org/csswg/cssom/#the-cssrulelist * http://dev.w3.org/csswg/cssom/#the-cssrulelist
""" """
__all__ = ['CSSRuleList'] __all__ = ['CSSRuleList']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: cssrulelist.py 1116 2008-03-05 13:52:23Z cthedot $' __version__ = '$Id: cssrulelist.py 1116 2008-03-05 13:52:23Z cthedot $'
class CSSRuleList(list): class CSSRuleList(list):
""" """
The CSSRuleList object represents an (ordered) list of statements. The CSSRuleList object represents an (ordered) list of statements.
The items in the CSSRuleList are accessible via an integral index, The items in the CSSRuleList are accessible via an integral index,
starting from 0. starting from 0.
Subclasses a standard Python list so theoretically all standard list Subclasses a standard Python list so theoretically all standard list
methods are available. Setting methods like ``__init__``, ``append``, methods are available. Setting methods like ``__init__``, ``append``,
``extend`` or ``__setslice__`` are added later on instances of this ``extend`` or ``__setslice__`` are added later on instances of this
class if so desired. class if so desired.
E.g. CSSStyleSheet adds ``append`` which is not available in a simple E.g. CSSStyleSheet adds ``append`` which is not available in a simple
instance of this class! instance of this class!
Properties Properties
========== ==========
length: of type unsigned long, readonly length: of type unsigned long, readonly
The number of CSSRules in the list. The range of valid child rule The number of CSSRules in the list. The range of valid child rule
indices is 0 to length-1 inclusive. indices is 0 to length-1 inclusive.
""" """
def __init__(self, *ignored): def __init__(self, *ignored):
"nothing is set as this must also be defined later" "nothing is set as this must also be defined later"
pass pass
def __notimplemented(self, *ignored): def __notimplemented(self, *ignored):
"no direct setting possible" "no direct setting possible"
raise NotImplementedError( raise NotImplementedError(
'Must be implemented by class using an instance of this class.') 'Must be implemented by class using an instance of this class.')
append = extend = __setitem__ = __setslice__ = __notimplemented append = extend = __setitem__ = __setslice__ = __notimplemented
def item(self, index): def item(self, index):
""" """
(DOM) (DOM)
Used to retrieve a CSS rule by ordinal index. The order in this Used to retrieve a CSS rule by ordinal index. The order in this
collection represents the order of the rules in the CSS style 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 sheet. If index is greater than or equal to the number of rules in
the list, this returns None. the list, this returns None.
Returns CSSRule, the style rule at the index position in the Returns CSSRule, the style rule at the index position in the
CSSRuleList, or None if that is not a valid index. CSSRuleList, or None if that is not a valid index.
""" """
try: try:
return self[index] return self[index]
except IndexError: except IndexError:
return None return None
length = property(lambda self: len(self), length = property(lambda self: len(self),
doc="(DOM) The number of CSSRules in the list.") doc="(DOM) The number of CSSRules in the list.")

View File

@ -1,242 +1,242 @@
"""CSSStyleRule implements DOM Level 2 CSS CSSStyleRule. """CSSStyleRule implements DOM Level 2 CSS CSSStyleRule.
""" """
__all__ = ['CSSStyleRule'] __all__ = ['CSSStyleRule']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: cssstylerule.py 1284 2008-06-05 16:29:17Z cthedot $' __version__ = '$Id: cssstylerule.py 1284 2008-06-05 16:29:17Z cthedot $'
import xml.dom import xml.dom
import cssrule import cssrule
import cssutils import cssutils
from selectorlist import SelectorList from selectorlist import SelectorList
from cssstyledeclaration import CSSStyleDeclaration from cssstyledeclaration import CSSStyleDeclaration
class CSSStyleRule(cssrule.CSSRule): class CSSStyleRule(cssrule.CSSRule):
""" """
The CSSStyleRule object represents a ruleset specified (if any) in a CSS 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 style sheet. It provides access to a declaration block as well as to the
associated group of selectors. associated group of selectors.
Properties Properties
========== ==========
selectorList: of type SelectorList (cssutils only) selectorList: of type SelectorList (cssutils only)
A list of all Selector elements for the rule set. A list of all Selector elements for the rule set.
selectorText: of type DOMString selectorText: of type DOMString
The textual representation of the selector for the rule set. The The textual representation of the selector for the rule set. The
implementation may have stripped out insignificant whitespace while implementation may have stripped out insignificant whitespace while
parsing the selector. parsing the selector.
style: of type CSSStyleDeclaration, (DOM) style: of type CSSStyleDeclaration, (DOM)
The declaration-block of this rule set. The declaration-block of this rule set.
type type
the type of this rule, constant cssutils.CSSRule.STYLE_RULE the type of this rule, constant cssutils.CSSRule.STYLE_RULE
inherited properties: inherited properties:
- cssText - cssText
- parentRule - parentRule
- parentStyleSheet - parentStyleSheet
Format Format
====== ======
ruleset:: ruleset::
: selector [ COMMA S* selector ]* : selector [ COMMA S* selector ]*
LBRACE S* declaration [ ';' S* declaration ]* '}' S* LBRACE S* declaration [ ';' S* declaration ]* '}' S*
; ;
""" """
type = property(lambda self: cssrule.CSSRule.STYLE_RULE) type = property(lambda self: cssrule.CSSRule.STYLE_RULE)
def __init__(self, selectorText=None, style=None, parentRule=None, def __init__(self, selectorText=None, style=None, parentRule=None,
parentStyleSheet=None, readonly=False): parentStyleSheet=None, readonly=False):
""" """
:Parameters: :Parameters:
selectorText selectorText
string parsed into selectorList string parsed into selectorList
style style
string parsed into CSSStyleDeclaration for this CSSStyleRule string parsed into CSSStyleDeclaration for this CSSStyleRule
readonly readonly
if True allows setting of properties in constructor only if True allows setting of properties in constructor only
""" """
super(CSSStyleRule, self).__init__(parentRule=parentRule, super(CSSStyleRule, self).__init__(parentRule=parentRule,
parentStyleSheet=parentStyleSheet) parentStyleSheet=parentStyleSheet)
self._selectorList = SelectorList(parentRule=self) self._selectorList = SelectorList(parentRule=self)
self._style = CSSStyleDeclaration(parentRule=self) self._style = CSSStyleDeclaration(parentRule=self)
if selectorText: if selectorText:
self.selectorText = selectorText self.selectorText = selectorText
if style: if style:
self.style = style self.style = style
self._readonly = readonly self._readonly = readonly
def _getCssText(self): def _getCssText(self):
""" """
returns serialized property cssText returns serialized property cssText
""" """
return cssutils.ser.do_CSSStyleRule(self) return cssutils.ser.do_CSSStyleRule(self)
def _setCssText(self, cssText): def _setCssText(self, cssText):
""" """
:param cssText: :param cssText:
a parseable string or a tuple of (cssText, dict-of-namespaces) a parseable string or a tuple of (cssText, dict-of-namespaces)
:Exceptions: :Exceptions:
- `NAMESPACE_ERR`: (Selector) - `NAMESPACE_ERR`: (Selector)
Raised if the specified selector uses an unknown namespace Raised if the specified selector uses an unknown namespace
prefix. prefix.
- `SYNTAX_ERR`: (self, StyleDeclaration, etc) - `SYNTAX_ERR`: (self, StyleDeclaration, etc)
Raised if the specified CSS string value has a syntax error and Raised if the specified CSS string value has a syntax error and
is unparsable. is unparsable.
- `INVALID_MODIFICATION_ERR`: (self) - `INVALID_MODIFICATION_ERR`: (self)
Raised if the specified CSS string value represents a different Raised if the specified CSS string value represents a different
type of rule than the current one. type of rule than the current one.
- `HIERARCHY_REQUEST_ERR`: (CSSStylesheet) - `HIERARCHY_REQUEST_ERR`: (CSSStylesheet)
Raised if the rule cannot be inserted at this point in the Raised if the rule cannot be inserted at this point in the
style sheet. style sheet.
- `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule) - `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule)
Raised if the rule is readonly. Raised if the rule is readonly.
""" """
super(CSSStyleRule, self)._setCssText(cssText) super(CSSStyleRule, self)._setCssText(cssText)
# might be (cssText, namespaces) # might be (cssText, namespaces)
cssText, namespaces = self._splitNamespacesOff(cssText) cssText, namespaces = self._splitNamespacesOff(cssText)
try: try:
# use parent style sheet ones if available # use parent style sheet ones if available
namespaces = self.parentStyleSheet.namespaces namespaces = self.parentStyleSheet.namespaces
except AttributeError: except AttributeError:
pass pass
tokenizer = self._tokenize2(cssText) tokenizer = self._tokenize2(cssText)
selectortokens = self._tokensupto2(tokenizer, blockstartonly=True) selectortokens = self._tokensupto2(tokenizer, blockstartonly=True)
styletokens = self._tokensupto2(tokenizer, blockendonly=True) styletokens = self._tokensupto2(tokenizer, blockendonly=True)
trail = self._nexttoken(tokenizer) trail = self._nexttoken(tokenizer)
if trail: if trail:
self._log.error(u'CSSStyleRule: Trailing content: %s' % self._log.error(u'CSSStyleRule: Trailing content: %s' %
self._valuestr(cssText), token=trail) self._valuestr(cssText), token=trail)
elif not selectortokens: elif not selectortokens:
self._log.error(u'CSSStyleRule: No selector found: %r' % self._log.error(u'CSSStyleRule: No selector found: %r' %
self._valuestr(cssText)) self._valuestr(cssText))
elif self._tokenvalue(selectortokens[0]).startswith(u'@'): elif self._tokenvalue(selectortokens[0]).startswith(u'@'):
self._log.error(u'CSSStyleRule: No style rule: %r' % self._log.error(u'CSSStyleRule: No style rule: %r' %
self._valuestr(cssText), self._valuestr(cssText),
error=xml.dom.InvalidModificationErr) error=xml.dom.InvalidModificationErr)
else: else:
wellformed = True wellformed = True
bracetoken = selectortokens.pop() bracetoken = selectortokens.pop()
if self._tokenvalue(bracetoken) != u'{': if self._tokenvalue(bracetoken) != u'{':
wellformed = False wellformed = False
self._log.error( self._log.error(
u'CSSStyleRule: No start { of style declaration found: %r' % u'CSSStyleRule: No start { of style declaration found: %r' %
self._valuestr(cssText), bracetoken) self._valuestr(cssText), bracetoken)
elif not selectortokens: elif not selectortokens:
wellformed = False wellformed = False
self._log.error(u'CSSStyleRule: No selector found: %r.' % self._log.error(u'CSSStyleRule: No selector found: %r.' %
self._valuestr(cssText), bracetoken) self._valuestr(cssText), bracetoken)
newselectorlist = SelectorList(selectorText=(selectortokens, newselectorlist = SelectorList(selectorText=(selectortokens,
namespaces), namespaces),
parentRule=self) parentRule=self)
newstyle = CSSStyleDeclaration() newstyle = CSSStyleDeclaration()
if not styletokens: if not styletokens:
wellformed = False wellformed = False
self._log.error( self._log.error(
u'CSSStyleRule: No style declaration or "}" found: %r' % u'CSSStyleRule: No style declaration or "}" found: %r' %
self._valuestr(cssText)) self._valuestr(cssText))
else: else:
braceorEOFtoken = styletokens.pop() braceorEOFtoken = styletokens.pop()
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken) val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
if val != u'}' and typ != 'EOF': if val != u'}' and typ != 'EOF':
wellformed = False wellformed = False
self._log.error( self._log.error(
u'CSSStyleRule: No "}" after style declaration found: %r' % u'CSSStyleRule: No "}" after style declaration found: %r' %
self._valuestr(cssText)) self._valuestr(cssText))
else: else:
if 'EOF' == typ: if 'EOF' == typ:
# add again as style needs it # add again as style needs it
styletokens.append(braceorEOFtoken) styletokens.append(braceorEOFtoken)
newstyle.cssText = styletokens newstyle.cssText = styletokens
if wellformed: if wellformed:
self._selectorList = newselectorlist self._selectorList = newselectorlist
self.style = newstyle self.style = newstyle
cssText = property(_getCssText, _setCssText, cssText = property(_getCssText, _setCssText,
doc="(DOM) The parsable textual representation of the rule.") doc="(DOM) The parsable textual representation of the rule.")
def __getNamespaces(self): def __getNamespaces(self):
"uses children namespaces if not attached to a sheet, else the sheet's ones" "uses children namespaces if not attached to a sheet, else the sheet's ones"
try: try:
return self.parentStyleSheet.namespaces return self.parentStyleSheet.namespaces
except AttributeError: except AttributeError:
return self.selectorList._namespaces return self.selectorList._namespaces
_namespaces = property(__getNamespaces, doc=u"""if this Rule is _namespaces = property(__getNamespaces, doc=u"""if this Rule is
attached to a CSSStyleSheet the namespaces of that sheet are mirrored attached to a CSSStyleSheet the namespaces of that sheet are mirrored
here. While the Rule is not attached the namespaces of selectorList here. While the Rule is not attached the namespaces of selectorList
are used.""") are used.""")
def _setSelectorList(self, selectorList): def _setSelectorList(self, selectorList):
""" """
:param selectorList: selectorList, only content is used, not the actual :param selectorList: selectorList, only content is used, not the actual
object object
""" """
self._checkReadonly() self._checkReadonly()
self.selectorText = selectorList.selectorText self.selectorText = selectorList.selectorText
selectorList = property(lambda self: self._selectorList, _setSelectorList, selectorList = property(lambda self: self._selectorList, _setSelectorList,
doc="The SelectorList of this rule.") doc="The SelectorList of this rule.")
def _setSelectorText(self, selectorText): def _setSelectorText(self, selectorText):
""" """
wrapper for cssutils SelectorList object wrapper for cssutils SelectorList object
:param selectorText: of type string, might also be a comma separated list :param selectorText: of type string, might also be a comma separated list
of selectors of selectors
:Exceptions: :Exceptions:
- `NAMESPACE_ERR`: (Selector) - `NAMESPACE_ERR`: (Selector)
Raised if the specified selector uses an unknown namespace Raised if the specified selector uses an unknown namespace
prefix. prefix.
- `SYNTAX_ERR`: (SelectorList, Selector) - `SYNTAX_ERR`: (SelectorList, Selector)
Raised if the specified CSS string value has a syntax error Raised if the specified CSS string value has a syntax error
and is unparsable. and is unparsable.
- `NO_MODIFICATION_ALLOWED_ERR`: (self) - `NO_MODIFICATION_ALLOWED_ERR`: (self)
Raised if this rule is readonly. Raised if this rule is readonly.
""" """
self._checkReadonly() self._checkReadonly()
self._selectorList.selectorText = selectorText self._selectorList.selectorText = selectorText
selectorText = property(lambda self: self._selectorList.selectorText, selectorText = property(lambda self: self._selectorList.selectorText,
_setSelectorText, _setSelectorText,
doc="""(DOM) The textual representation of the selector for the doc="""(DOM) The textual representation of the selector for the
rule set.""") rule set.""")
def _setStyle(self, style): def _setStyle(self, style):
""" """
:param style: CSSStyleDeclaration or string, only the cssText of a :param style: CSSStyleDeclaration or string, only the cssText of a
declaration is used, not the actual object declaration is used, not the actual object
""" """
self._checkReadonly() self._checkReadonly()
if isinstance(style, basestring): if isinstance(style, basestring):
self._style.cssText = style self._style.cssText = style
else: else:
# cssText would be serialized with optional preferences # cssText would be serialized with optional preferences
# so use _seq! # so use _seq!
self._style._seq = style._seq self._style._seq = style._seq
style = property(lambda self: self._style, _setStyle, style = property(lambda self: self._style, _setStyle,
doc="(DOM) The declaration-block of this rule set.") doc="(DOM) The declaration-block of this rule set.")
wellformed = property(lambda self: self.selectorList.wellformed) wellformed = property(lambda self: self.selectorList.wellformed)
def __repr__(self): def __repr__(self):
if self._namespaces: if self._namespaces:
st = (self.selectorText, self._namespaces) st = (self.selectorText, self._namespaces)
else: else:
st = self.selectorText st = self.selectorText
return "cssutils.css.%s(selectorText=%r, style=%r)" % ( return "cssutils.css.%s(selectorText=%r, style=%r)" % (
self.__class__.__name__, st, self.style.cssText) self.__class__.__name__, st, self.style.cssText)
def __str__(self): def __str__(self):
return "<cssutils.css.%s object selector=%r style=%r _namespaces=%r at 0x%x>" % ( return "<cssutils.css.%s object selector=%r style=%r _namespaces=%r at 0x%x>" % (
self.__class__.__name__, self.selectorText, self.style.cssText, self.__class__.__name__, self.selectorText, self.style.cssText,
self._namespaces, id(self)) self._namespaces, id(self))

View File

@ -1,208 +1,208 @@
"""CSSUnknownRule implements DOM Level 2 CSS CSSUnknownRule. """CSSUnknownRule implements DOM Level 2 CSS CSSUnknownRule.
""" """
__all__ = ['CSSUnknownRule'] __all__ = ['CSSUnknownRule']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: cssunknownrule.py 1170 2008-03-20 17:42:07Z cthedot $' __version__ = '$Id: cssunknownrule.py 1170 2008-03-20 17:42:07Z cthedot $'
import xml.dom import xml.dom
import cssrule import cssrule
import cssutils import cssutils
class CSSUnknownRule(cssrule.CSSRule): class CSSUnknownRule(cssrule.CSSRule):
""" """
represents an at-rule not supported by this user agent. represents an at-rule not supported by this user agent.
Properties Properties
========== ==========
inherited from CSSRule inherited from CSSRule
- cssText - cssText
- type - type
cssutils only cssutils only
------------- -------------
atkeyword atkeyword
the literal keyword used the literal keyword used
seq seq
All parts of this rule excluding @KEYWORD but including CSSComments All parts of this rule excluding @KEYWORD but including CSSComments
wellformed wellformed
if this Rule is wellformed, for Unknown rules if an atkeyword is set if this Rule is wellformed, for Unknown rules if an atkeyword is set
at all at all
Format Format
====== ======
unknownrule: unknownrule:
@xxx until ';' or block {...} @xxx until ';' or block {...}
""" """
type = property(lambda self: cssrule.CSSRule.UNKNOWN_RULE) type = property(lambda self: cssrule.CSSRule.UNKNOWN_RULE)
def __init__(self, cssText=u'', parentRule=None, def __init__(self, cssText=u'', parentRule=None,
parentStyleSheet=None, readonly=False): parentStyleSheet=None, readonly=False):
""" """
cssText cssText
of type string of type string
""" """
super(CSSUnknownRule, self).__init__(parentRule=parentRule, super(CSSUnknownRule, self).__init__(parentRule=parentRule,
parentStyleSheet=parentStyleSheet) parentStyleSheet=parentStyleSheet)
self._atkeyword = None self._atkeyword = None
if cssText: if cssText:
self.cssText = cssText self.cssText = cssText
self._readonly = readonly self._readonly = readonly
def _getCssText(self): def _getCssText(self):
""" returns serialized property cssText """ """ returns serialized property cssText """
return cssutils.ser.do_CSSUnknownRule(self) return cssutils.ser.do_CSSUnknownRule(self)
def _setCssText(self, cssText): def _setCssText(self, cssText):
""" """
DOMException on setting DOMException on setting
- SYNTAX_ERR: - SYNTAX_ERR:
Raised if the specified CSS string value has a syntax error and Raised if the specified CSS string value has a syntax error and
is unparsable. is unparsable.
- INVALID_MODIFICATION_ERR: - INVALID_MODIFICATION_ERR:
Raised if the specified CSS string value represents a different Raised if the specified CSS string value represents a different
type of rule than the current one. type of rule than the current one.
- HIERARCHY_REQUEST_ERR: (never raised) - HIERARCHY_REQUEST_ERR: (never raised)
Raised if the rule cannot be inserted at this point in the Raised if the rule cannot be inserted at this point in the
style sheet. style sheet.
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule) - NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
Raised if the rule is readonly. Raised if the rule is readonly.
""" """
super(CSSUnknownRule, self)._setCssText(cssText) super(CSSUnknownRule, self)._setCssText(cssText)
tokenizer = self._tokenize2(cssText) tokenizer = self._tokenize2(cssText)
attoken = self._nexttoken(tokenizer, None) attoken = self._nexttoken(tokenizer, None)
if not attoken or self._type(attoken) != self._prods.ATKEYWORD: if not attoken or self._type(attoken) != self._prods.ATKEYWORD:
self._log.error(u'CSSUnknownRule: No CSSUnknownRule found: %s' % self._log.error(u'CSSUnknownRule: No CSSUnknownRule found: %s' %
self._valuestr(cssText), self._valuestr(cssText),
error=xml.dom.InvalidModificationErr) error=xml.dom.InvalidModificationErr)
else: else:
# for closures: must be a mutable # for closures: must be a mutable
new = {'nesting': [], # {} [] or () new = {'nesting': [], # {} [] or ()
'wellformed': True 'wellformed': True
} }
def CHAR(expected, seq, token, tokenizer=None): def CHAR(expected, seq, token, tokenizer=None):
type_, val, line, col = token type_, val, line, col = token
if expected != 'EOF': if expected != 'EOF':
if val in u'{[(': if val in u'{[(':
new['nesting'].append(val) new['nesting'].append(val)
elif val in u'}])': elif val in u'}])':
opening = {u'}': u'{', u']': u'[', u')': u'('}[val] opening = {u'}': u'{', u']': u'[', u')': u'('}[val]
try: try:
if new['nesting'][-1] == opening: if new['nesting'][-1] == opening:
new['nesting'].pop() new['nesting'].pop()
else: else:
raise IndexError() raise IndexError()
except IndexError: except IndexError:
new['wellformed'] = False new['wellformed'] = False
self._log.error(u'CSSUnknownRule: Wrong nesting of {, [ or (.', self._log.error(u'CSSUnknownRule: Wrong nesting of {, [ or (.',
token=token) token=token)
if val in u'};' and not new['nesting']: if val in u'};' and not new['nesting']:
expected = 'EOF' expected = 'EOF'
seq.append(val, type_, line=line, col=col) seq.append(val, type_, line=line, col=col)
return expected return expected
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error(u'CSSUnknownRule: Expected end of rule.', self._log.error(u'CSSUnknownRule: Expected end of rule.',
token=token) token=token)
return expected return expected
def EOF(expected, seq, token, tokenizer=None): def EOF(expected, seq, token, tokenizer=None):
"close all blocks and return 'EOF'" "close all blocks and return 'EOF'"
for x in reversed(new['nesting']): for x in reversed(new['nesting']):
closing = {u'{': u'}', u'[': u']', u'(': u')'}[x] closing = {u'{': u'}', u'[': u']', u'(': u')'}[x]
seq.append(closing, closing) seq.append(closing, closing)
new['nesting'] = [] new['nesting'] = []
return 'EOF' return 'EOF'
def INVALID(expected, seq, token, tokenizer=None): def INVALID(expected, seq, token, tokenizer=None):
# makes rule invalid # makes rule invalid
self._log.error(u'CSSUnknownRule: Bad syntax.', self._log.error(u'CSSUnknownRule: Bad syntax.',
token=token, error=xml.dom.SyntaxErr) token=token, error=xml.dom.SyntaxErr)
new['wellformed'] = False new['wellformed'] = False
return expected return expected
def STRING(expected, seq, token, tokenizer=None): def STRING(expected, seq, token, tokenizer=None):
type_, val, line, col = token type_, val, line, col = token
val = self._stringtokenvalue(token) val = self._stringtokenvalue(token)
if expected != 'EOF': if expected != 'EOF':
seq.append(val, type_, line=line, col=col) seq.append(val, type_, line=line, col=col)
return expected return expected
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error(u'CSSUnknownRule: Expected end of rule.', self._log.error(u'CSSUnknownRule: Expected end of rule.',
token=token) token=token)
return expected return expected
def URI(expected, seq, token, tokenizer=None): def URI(expected, seq, token, tokenizer=None):
type_, val, line, col = token type_, val, line, col = token
val = self._uritokenvalue(token) val = self._uritokenvalue(token)
if expected != 'EOF': if expected != 'EOF':
seq.append(val, type_, line=line, col=col) seq.append(val, type_, line=line, col=col)
return expected return expected
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error(u'CSSUnknownRule: Expected end of rule.', self._log.error(u'CSSUnknownRule: Expected end of rule.',
token=token) token=token)
return expected return expected
def default(expected, seq, token, tokenizer=None): def default(expected, seq, token, tokenizer=None):
type_, val, line, col = token type_, val, line, col = token
if expected != 'EOF': if expected != 'EOF':
seq.append(val, type_, line=line, col=col) seq.append(val, type_, line=line, col=col)
return expected return expected
else: else:
new['wellformed'] = False new['wellformed'] = False
self._log.error(u'CSSUnknownRule: Expected end of rule.', self._log.error(u'CSSUnknownRule: Expected end of rule.',
token=token) token=token)
return expected return expected
# unknown : ATKEYWORD S* ... ; | } # unknown : ATKEYWORD S* ... ; | }
newseq = self._tempSeq() newseq = self._tempSeq()
wellformed, expected = self._parse(expected=None, wellformed, expected = self._parse(expected=None,
seq=newseq, tokenizer=tokenizer, seq=newseq, tokenizer=tokenizer,
productions={'CHAR': CHAR, productions={'CHAR': CHAR,
'EOF': EOF, 'EOF': EOF,
'INVALID': INVALID, 'INVALID': INVALID,
'STRING': STRING, 'STRING': STRING,
'URI': URI, 'URI': URI,
'S': default # overwrite default default! 'S': default # overwrite default default!
}, },
default=default, default=default,
new=new) new=new)
# wellformed set by parse # wellformed set by parse
wellformed = wellformed and new['wellformed'] wellformed = wellformed and new['wellformed']
# post conditions # post conditions
if expected != 'EOF': if expected != 'EOF':
wellformed = False wellformed = False
self._log.error( self._log.error(
u'CSSUnknownRule: No ending ";" or "}" found: %r' % u'CSSUnknownRule: No ending ";" or "}" found: %r' %
self._valuestr(cssText)) self._valuestr(cssText))
elif new['nesting']: elif new['nesting']:
wellformed = False wellformed = False
self._log.error( self._log.error(
u'CSSUnknownRule: Unclosed "{", "[" or "(": %r' % u'CSSUnknownRule: Unclosed "{", "[" or "(": %r' %
self._valuestr(cssText)) self._valuestr(cssText))
# set all # set all
if wellformed: if wellformed:
self.atkeyword = self._tokenvalue(attoken) self.atkeyword = self._tokenvalue(attoken)
self._setSeq(newseq) self._setSeq(newseq)
cssText = property(fget=_getCssText, fset=_setCssText, cssText = property(fget=_getCssText, fset=_setCssText,
doc="(DOM) The parsable textual representation.") doc="(DOM) The parsable textual representation.")
wellformed = property(lambda self: bool(self.atkeyword)) wellformed = property(lambda self: bool(self.atkeyword))
def __repr__(self): def __repr__(self):
return "cssutils.css.%s(cssText=%r)" % ( return "cssutils.css.%s(cssText=%r)" % (
self.__class__.__name__, self.cssText) self.__class__.__name__, self.cssText)
def __str__(self): def __str__(self):
return "<cssutils.css.%s object cssText=%r at 0x%x>" % ( return "<cssutils.css.%s object cssText=%r at 0x%x>" % (
self.__class__.__name__, self.cssText, id(self)) self.__class__.__name__, self.cssText, id(self))

View File

@ -1,414 +1,415 @@
"""Property is a single CSS property in a CSSStyleDeclaration """Property is a single CSS property in a CSSStyleDeclaration
Internal use only, may be removed in the future! Internal use only, may be removed in the future!
""" """
__all__ = ['Property'] __all__ = ['Property']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: property.py 1305 2008-06-22 18:42:51Z cthedot $' __version__ = '$Id: property.py 1444 2008-08-31 18:45:35Z cthedot $'
import xml.dom import xml.dom
import cssutils import cssutils
import cssproperties #import cssproperties
from cssvalue import CSSValue from cssutils.profiles import profiles
from cssutils.helper import Deprecated from cssvalue import CSSValue
from cssutils.helper import Deprecated
class Property(cssutils.util.Base):
""" class Property(cssutils.util.Base):
(cssutils) a CSS property in a StyleDeclaration of a CSSStyleRule """
(cssutils) a CSS property in a StyleDeclaration of a CSSStyleRule
Properties
========== Properties
cssText ==========
a parsable textual representation of this property cssText
name a parsable textual representation of this property
normalized name of the property, e.g. "color" when name is "c\olor" name
(since 0.9.5) normalized name of the property, e.g. "color" when name is "c\olor"
literalname (since 0.9.5) (since 0.9.5)
original name of the property in the source CSS which is not normalized literalname (since 0.9.5)
e.g. "C\\OLor" original name of the property in the source CSS which is not normalized
cssValue e.g. "C\\OLor"
the relevant CSSValue instance for this property cssValue
value the relevant CSSValue instance for this property
the string value of the property, same as cssValue.cssText value
priority the string value of the property, same as cssValue.cssText
of the property (currently only u"important" or None) priority
literalpriority of the property (currently only u"important" or None)
original priority of the property in the source CSS which is not literalpriority
normalized e.g. "IM\portant" original priority of the property in the source CSS which is not
seqs normalized e.g. "IM\portant"
combination of a list for seq of name, a CSSValue object, and seqs
a list for seq of priority (empty or [!important] currently) combination of a list for seq of name, a CSSValue object, and
valid a list for seq of priority (empty or [!important] currently)
if this Property is valid valid
wellformed if this Property is valid
if this Property is syntactically ok 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" DEPRECATED normalname (since 0.9.5)
normalized name of the property, e.g. "color" when name is "c\olor"
Format
====== Format
:: ======
::
property = name
: IDENT S* property = name
; : IDENT S*
;
expr = value
: term [ operator term ]* expr = value
; : term [ operator term ]*
term ;
: unary_operator? term
[ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | : unary_operator?
TIME S* | FREQ S* | function ] [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* |
| STRING S* | IDENT S* | URI S* | hexcolor TIME S* | FREQ S* | function ]
; | STRING S* | IDENT S* | URI S* | hexcolor
function ;
: FUNCTION S* expr ')' S* function
; : FUNCTION S* expr ')' S*
/* ;
* There is a constraint on the color that it must /*
* have either 3 or 6 hex-digits (i.e., [0-9a-fA-F]) * There is a constraint on the color that it must
* after the "#"; e.g., "#000" is OK, but "#abcd" is not. * 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* hexcolor
; : HASH S*
;
prio
: IMPORTANT_SYM S* prio
; : IMPORTANT_SYM S*
;
"""
def __init__(self, name=None, value=None, priority=u'', _mediaQuery=False): """
""" def __init__(self, name=None, value=None, priority=u'', _mediaQuery=False):
inits property """
inits property
name
a property name string (will be normalized) name
value a property name string (will be normalized)
a property value string value
priority a property value string
an optional priority string which currently must be u'', priority
u'!important' or u'important' an optional priority string which currently must be u'',
_mediaQuery boolean u'!important' or u'important'
if True value is optional as used by MediaQuery objects _mediaQuery boolean
""" if True value is optional as used by MediaQuery objects
super(Property, self).__init__() """
super(Property, self).__init__()
self.seqs = [[], None, []]
self.valid = False self.seqs = [[], None, []]
self.wellformed = False self.valid = False
self._mediaQuery = _mediaQuery self.wellformed = False
self._mediaQuery = _mediaQuery
if name:
self.name = name if name:
else: self.name = name
self._name = u'' else:
self._literalname = u'' self._name = u''
self.__normalname = u'' # DEPRECATED self._literalname = u''
self.__normalname = u'' # DEPRECATED
if value:
self.cssValue = value if value:
else: self.cssValue = value
self.seqs[1] = CSSValue() else:
self.seqs[1] = CSSValue()
if priority:
self.priority = priority if priority:
else: self.priority = priority
self._priority = u'' else:
self._literalpriority = u'' self._priority = u''
self._literalpriority = u''
def _getCssText(self):
""" def _getCssText(self):
returns serialized property cssText """
""" returns serialized property cssText
return cssutils.ser.do_Property(self) """
return cssutils.ser.do_Property(self)
def _setCssText(self, cssText):
""" def _setCssText(self, cssText):
DOMException on setting """
DOMException on setting
- NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
Raised if the rule is readonly. - NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
- SYNTAX_ERR: (self) Raised if the rule is readonly.
Raised if the specified CSS string value has a syntax error and - SYNTAX_ERR: (self)
is unparsable. Raised if the specified CSS string value has a syntax error and
""" is unparsable.
# check and prepare tokenlists for setting """
tokenizer = self._tokenize2(cssText) # check and prepare tokenlists for setting
nametokens = self._tokensupto2(tokenizer, propertynameendonly=True) tokenizer = self._tokenize2(cssText)
if nametokens: nametokens = self._tokensupto2(tokenizer, propertynameendonly=True)
wellformed = True if nametokens:
wellformed = True
valuetokens = self._tokensupto2(tokenizer,
propertyvalueendonly=True) valuetokens = self._tokensupto2(tokenizer,
prioritytokens = self._tokensupto2(tokenizer, propertyvalueendonly=True)
propertypriorityendonly=True) prioritytokens = self._tokensupto2(tokenizer,
propertypriorityendonly=True)
if self._mediaQuery and not valuetokens:
# MediaQuery may consist of name only if self._mediaQuery and not valuetokens:
self.name = nametokens # MediaQuery may consist of name only
self.cssValue = None self.name = nametokens
self.priority = None self.cssValue = None
return self.priority = None
return
# remove colon from nametokens
colontoken = nametokens.pop() # remove colon from nametokens
if self._tokenvalue(colontoken) != u':': colontoken = nametokens.pop()
wellformed = False if self._tokenvalue(colontoken) != u':':
self._log.error(u'Property: No ":" after name found: %r' % wellformed = False
self._valuestr(cssText), colontoken) self._log.error(u'Property: No ":" after name found: %r' %
elif not nametokens: self._valuestr(cssText), colontoken)
wellformed = False elif not nametokens:
self._log.error(u'Property: No property name found: %r.' % wellformed = False
self._valuestr(cssText), colontoken) self._log.error(u'Property: No property name found: %r.' %
self._valuestr(cssText), colontoken)
if valuetokens:
if self._tokenvalue(valuetokens[-1]) == u'!': if valuetokens:
# priority given, move "!" to prioritytokens if self._tokenvalue(valuetokens[-1]) == u'!':
prioritytokens.insert(0, valuetokens.pop(-1)) # priority given, move "!" to prioritytokens
else: prioritytokens.insert(0, valuetokens.pop(-1))
wellformed = False else:
self._log.error(u'Property: No property value found: %r.' % wellformed = False
self._valuestr(cssText), colontoken) self._log.error(u'Property: No property value found: %r.' %
self._valuestr(cssText), colontoken)
if wellformed:
self.wellformed = True if wellformed:
self.name = nametokens self.wellformed = True
self.cssValue = valuetokens self.name = nametokens
self.priority = prioritytokens self.cssValue = valuetokens
self.priority = prioritytokens
else:
self._log.error(u'Property: No property name found: %r.' % else:
self._valuestr(cssText)) self._log.error(u'Property: No property name found: %r.' %
self._valuestr(cssText))
cssText = property(fget=_getCssText, fset=_setCssText,
doc="A parsable textual representation.") cssText = property(fget=_getCssText, fset=_setCssText,
doc="A parsable textual representation.")
def _setName(self, name):
""" def _setName(self, name):
DOMException on setting """
DOMException on setting
- SYNTAX_ERR: (self)
Raised if the specified name has a syntax error and is - SYNTAX_ERR: (self)
unparsable. Raised if the specified name has a syntax error and is
""" unparsable.
# for closures: must be a mutable """
new = {'literalname': None, # for closures: must be a mutable
'wellformed': True} new = {'literalname': None,
'wellformed': True}
def _ident(expected, seq, token, tokenizer=None):
# name def _ident(expected, seq, token, tokenizer=None):
if 'name' == expected: # name
new['literalname'] = self._tokenvalue(token).lower() if 'name' == expected:
seq.append(new['literalname']) new['literalname'] = self._tokenvalue(token).lower()
return 'EOF' seq.append(new['literalname'])
else: return 'EOF'
new['wellformed'] = False else:
self._log.error(u'Property: Unexpected ident.', token) new['wellformed'] = False
return expected self._log.error(u'Property: Unexpected ident.', token)
return expected
newseq = []
wellformed, expected = self._parse(expected='name', newseq = []
seq=newseq, wellformed, expected = self._parse(expected='name',
tokenizer=self._tokenize2(name), seq=newseq,
productions={'IDENT': _ident}) tokenizer=self._tokenize2(name),
wellformed = wellformed and new['wellformed'] productions={'IDENT': _ident})
wellformed = wellformed and new['wellformed']
# post conditions
# define a token for error logging # post conditions
if isinstance(name, list): # define a token for error logging
token = name[0] if isinstance(name, list):
else: token = name[0]
token = None else:
token = None
if not new['literalname']:
wellformed = False if not new['literalname']:
self._log.error(u'Property: No name found: %r' % wellformed = False
self._valuestr(name), token=token) self._log.error(u'Property: No name found: %r' %
self._valuestr(name), token=token)
if wellformed:
self.wellformed = True if wellformed:
self._literalname = new['literalname'] self.wellformed = True
self._name = self._normalize(self._literalname) self._literalname = new['literalname']
self.__normalname = self._name # DEPRECATED self._name = self._normalize(self._literalname)
self.seqs[0] = newseq self.__normalname = self._name # DEPRECATED
self.seqs[0] = newseq
# validate
if self._name not in cssproperties.cssvalues: # validate
self.valid = False if self._name not in profiles.propertiesByProfile():
tokenizer=self._tokenize2(name) self.valid = False
self._log.info(u'Property: No CSS2 Property: %r.' % tokenizer=self._tokenize2(name)
new['literalname'], token=token, neverraise=True) self._log.warn(u'Property: Unknown Property: %r.' %
else: new['literalname'], token=token, neverraise=True)
self.valid = True else:
if self.cssValue: self.valid = True
self.cssValue._propertyName = self._name if self.cssValue:
self.valid = self.cssValue.valid self.cssValue._propertyName = self._name
else: self.valid = self.cssValue.valid
self.wellformed = False else:
self.wellformed = False
name = property(lambda self: self._name, _setName,
doc="Name of this property") 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") literalname = property(lambda self: self._literalname,
doc="Readonly literal (not normalized) name of this property")
def _getCSSValue(self):
return self.seqs[1] def _getCSSValue(self):
return self.seqs[1]
def _setCSSValue(self, cssText):
""" def _setCSSValue(self, cssText):
see css.CSSValue """
see css.CSSValue
DOMException on setting?
DOMException on setting?
- SYNTAX_ERR: (self)
Raised if the specified CSS string value has a syntax error - SYNTAX_ERR: (self)
(according to the attached property) or is unparsable. Raised if the specified CSS string value has a syntax error
- TODO: INVALID_MODIFICATION_ERR: (according to the attached property) or is unparsable.
Raised if the specified CSS string value represents a different - TODO: INVALID_MODIFICATION_ERR:
type of values than the values allowed by the CSS property. 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() if self._mediaQuery and not cssText:
else: self.seqs[1] = CSSValue()
if not self.seqs[1]: else:
self.seqs[1] = CSSValue() if not self.seqs[1]:
self.seqs[1] = CSSValue()
cssvalue = self.seqs[1]
cssvalue._propertyName = self.name cssvalue = self.seqs[1]
cssvalue.cssText = cssText cssvalue._propertyName = self.name
if cssvalue._value and cssvalue.wellformed: cssvalue.cssText = cssText
self.seqs[1] = cssvalue if cssvalue._value and cssvalue.wellformed:
self.valid = self.valid and cssvalue.valid self.seqs[1] = cssvalue
self.wellformed = self.wellformed and cssvalue.wellformed 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") cssValue = property(_getCSSValue, _setCSSValue,
doc="(cssutils) CSSValue object of this property")
def _getValue(self):
if self.cssValue: def _getValue(self):
return self.cssValue._value if self.cssValue:
else: return self.cssValue._value
return u'' else:
return u''
def _setValue(self, value):
self.cssValue.cssText = value def _setValue(self, value):
self.valid = self.valid and self.cssValue.valid self.cssValue.cssText = value
self.wellformed = self.wellformed and self.cssValue.wellformed 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.") value = property(_getValue, _setValue,
doc="The textual value of this Properties cssValue.")
def _setPriority(self, priority):
""" def _setPriority(self, priority):
priority """
a string, currently either u'', u'!important' or u'important' priority
a string, currently either u'', u'!important' or u'important'
Format
====== Format
:: ======
::
prio
: IMPORTANT_SYM S* prio
; : IMPORTANT_SYM S*
;
"!"{w}"important" {return IMPORTANT_SYM;}
"!"{w}"important" {return IMPORTANT_SYM;}
DOMException on setting
DOMException on setting
- SYNTAX_ERR: (self)
Raised if the specified priority has a syntax error and is - SYNTAX_ERR: (self)
unparsable. Raised if the specified priority has a syntax error and is
In this case a priority not equal to None, "" or "!{w}important". unparsable.
As CSSOM defines CSSStyleDeclaration.getPropertyPriority resulting in In this case a priority not equal to None, "" or "!{w}important".
u'important' this value is also allowed to set a Properties priority 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'' if self._mediaQuery:
self._literalpriority = u'' self._priority = u''
if priority: self._literalpriority = u''
self._log.error(u'Property: No priority in a MediaQuery - ignored.') if priority:
return self._log.error(u'Property: No priority in a MediaQuery - ignored.')
return
if isinstance(priority, basestring) and\
u'important' == self._normalize(priority): if isinstance(priority, basestring) and\
priority = u'!%s' % priority u'important' == self._normalize(priority):
priority = u'!%s' % priority
# for closures: must be a mutable
new = {'literalpriority': u'', # for closures: must be a mutable
'wellformed': True} new = {'literalpriority': u'',
'wellformed': True}
def _char(expected, seq, token, tokenizer=None):
# "!" def _char(expected, seq, token, tokenizer=None):
val = self._tokenvalue(token) # "!"
if u'!' == expected == val: val = self._tokenvalue(token)
seq.append(val) if u'!' == expected == val:
return 'important' seq.append(val)
else: return 'important'
new['wellformed'] = False else:
self._log.error(u'Property: Unexpected char.', token) new['wellformed'] = False
return expected self._log.error(u'Property: Unexpected char.', token)
return expected
def _ident(expected, seq, token, tokenizer=None):
# "important" def _ident(expected, seq, token, tokenizer=None):
val = self._tokenvalue(token) # "important"
normalval = self._tokenvalue(token, normalize=True) val = self._tokenvalue(token)
if 'important' == expected == normalval: normalval = self._tokenvalue(token, normalize=True)
new['literalpriority'] = val if 'important' == expected == normalval:
seq.append(val) new['literalpriority'] = val
return 'EOF' seq.append(val)
else: return 'EOF'
new['wellformed'] = False else:
self._log.error(u'Property: Unexpected ident.', token) new['wellformed'] = False
return expected self._log.error(u'Property: Unexpected ident.', token)
return expected
newseq = []
wellformed, expected = self._parse(expected='!', newseq = []
seq=newseq, wellformed, expected = self._parse(expected='!',
tokenizer=self._tokenize2(priority), seq=newseq,
productions={'CHAR': _char, tokenizer=self._tokenize2(priority),
'IDENT': _ident}) productions={'CHAR': _char,
wellformed = wellformed and new['wellformed'] 'IDENT': _ident})
wellformed = wellformed and new['wellformed']
# post conditions
if priority and not new['literalpriority']: # post conditions
wellformed = False if priority and not new['literalpriority']:
self._log.info(u'Property: Invalid priority: %r.' % wellformed = False
self._valuestr(priority)) self._log.info(u'Property: Invalid priority: %r.' %
self._valuestr(priority))
if wellformed:
self.wellformed = self.wellformed and wellformed if wellformed:
self._literalpriority = new['literalpriority'] self.wellformed = self.wellformed and wellformed
self._priority = self._normalize(self.literalpriority) self._literalpriority = new['literalpriority']
self.seqs[2] = newseq self._priority = self._normalize(self.literalpriority)
self.seqs[2] = newseq
# validate
if self._priority not in (u'', u'important'): # validate
self.valid = False if self._priority not in (u'', u'important'):
self._log.info(u'Property: No CSS2 priority value: %r.' % self.valid = False
self._priority, neverraise=True) 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") 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") 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)" % ( def __repr__(self):
self.__class__.__name__, return "cssutils.css.%s(name=%r, value=%r, priority=%r)" % (
self.literalname, self.cssValue.cssText, self.priority) 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>" % ( def __str__(self):
self.__class__.__module__, self.__class__.__name__, return "<%s.%s object name=%r value=%r priority=%r at 0x%x>" % (
self.name, self.cssValue.cssText, self.priority, id(self)) 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): @Deprecated(u'Use property ``name`` instead (since cssutils 0.9.5).')
return self.__normalname def _getNormalname(self):
normalname = property(_getNormalname, return self.__normalname
normalname = property(_getNormalname,
doc="DEPRECATED since 0.9.5, use name instead") doc="DEPRECATED since 0.9.5, use name instead")

View File

@ -1,249 +1,249 @@
"""SelectorList is a list of CSS Selector objects. """SelectorList is a list of CSS Selector objects.
TODO TODO
- remove duplicate Selectors. -> CSSOM canonicalize - remove duplicate Selectors. -> CSSOM canonicalize
- ??? CSS2 gives a special meaning to the comma (,) in selectors. - ??? CSS2 gives a special meaning to the comma (,) in selectors.
However, since it is not known if the comma may acquire other However, since it is not known if the comma may acquire other
meanings in future versions of CSS, the whole statement should be meanings in future versions of CSS, the whole statement should be
ignored if there is an error anywhere in the selector, even though ignored if there is an error anywhere in the selector, even though
the rest of the selector may look reasonable in CSS2. the rest of the selector may look reasonable in CSS2.
Illegal example(s): Illegal example(s):
For example, since the "&" is not a valid token in a CSS2 selector, 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 a CSS2 user agent must ignore the whole second line, and not set
the color of H3 to red: the color of H3 to red:
""" """
__all__ = ['SelectorList'] __all__ = ['SelectorList']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: selectorlist.py 1174 2008-03-20 17:43:07Z cthedot $' __version__ = '$Id: selectorlist.py 1174 2008-03-20 17:43:07Z cthedot $'
import xml.dom import xml.dom
import cssutils import cssutils
from selector import Selector from selector import Selector
class SelectorList(cssutils.util.Base, cssutils.util.ListSeq): class SelectorList(cssutils.util.Base, cssutils.util.ListSeq):
""" """
(cssutils) a list of Selectors of a CSSStyleRule (cssutils) a list of Selectors of a CSSStyleRule
Properties Properties
========== ==========
length: of type unsigned long, readonly length: of type unsigned long, readonly
The number of Selector elements in the list. The number of Selector elements in the list.
parentRule: of type CSSRule, readonly parentRule: of type CSSRule, readonly
The CSS rule that contains this selector list or None if this The CSS rule that contains this selector list or None if this
list is not attached to a CSSRule. list is not attached to a CSSRule.
selectorText: of type DOMString selectorText: of type DOMString
The textual representation of the selector for the rule set. The The textual representation of the selector for the rule set. The
implementation may have stripped out insignificant whitespace while implementation may have stripped out insignificant whitespace while
parsing the selector. parsing the selector.
seq: (internal use!) seq: (internal use!)
A list of Selector objects A list of Selector objects
wellformed wellformed
if this selectorlist is wellformed regarding the Selector spec if this selectorlist is wellformed regarding the Selector spec
""" """
def __init__(self, selectorText=None, parentRule=None, def __init__(self, selectorText=None, parentRule=None,
readonly=False): readonly=False):
""" """
initializes SelectorList with optional selectorText initializes SelectorList with optional selectorText
:Parameters: :Parameters:
selectorText selectorText
parsable list of Selectors parsable list of Selectors
parentRule parentRule
the parent CSSRule if available the parent CSSRule if available
""" """
super(SelectorList, self).__init__() super(SelectorList, self).__init__()
self._parentRule = parentRule self._parentRule = parentRule
if selectorText: if selectorText:
self.selectorText = selectorText self.selectorText = selectorText
self._readonly = readonly self._readonly = readonly
def __prepareset(self, newSelector, namespaces=None): def __prepareset(self, newSelector, namespaces=None):
"used by appendSelector and __setitem__" "used by appendSelector and __setitem__"
if not namespaces: if not namespaces:
namespaces = {} namespaces = {}
self._checkReadonly() self._checkReadonly()
if not isinstance(newSelector, Selector): if not isinstance(newSelector, Selector):
newSelector = Selector((newSelector, namespaces), newSelector = Selector((newSelector, namespaces),
parentList=self) parentList=self)
if newSelector.wellformed: if newSelector.wellformed:
newSelector._parent = self # maybe set twice but must be! newSelector._parent = self # maybe set twice but must be!
return newSelector return newSelector
def __setitem__(self, index, newSelector): def __setitem__(self, index, newSelector):
""" """
overwrites ListSeq.__setitem__ overwrites ListSeq.__setitem__
Any duplicate Selectors are **not** removed. Any duplicate Selectors are **not** removed.
""" """
newSelector = self.__prepareset(newSelector) newSelector = self.__prepareset(newSelector)
if newSelector: if newSelector:
self.seq[index] = newSelector self.seq[index] = newSelector
def append(self, newSelector): def append(self, newSelector):
"same as appendSelector(newSelector)" "same as appendSelector(newSelector)"
self.appendSelector(newSelector) self.appendSelector(newSelector)
length = property(lambda self: len(self), length = property(lambda self: len(self),
doc="The number of Selector elements in the list.") doc="The number of Selector elements in the list.")
def __getNamespaces(self): def __getNamespaces(self):
"uses children namespaces if not attached to a sheet, else the sheet's ones" "uses children namespaces if not attached to a sheet, else the sheet's ones"
try: try:
return self.parentRule.parentStyleSheet.namespaces return self.parentRule.parentStyleSheet.namespaces
except AttributeError: except AttributeError:
namespaces = {} namespaces = {}
for selector in self.seq: for selector in self.seq:
namespaces.update(selector._namespaces) namespaces.update(selector._namespaces)
return namespaces return namespaces
_namespaces = property(__getNamespaces, doc="""if this SelectorList is _namespaces = property(__getNamespaces, doc="""if this SelectorList is
attached to a CSSStyleSheet the namespaces of that sheet are mirrored attached to a CSSStyleSheet the namespaces of that sheet are mirrored
here. While the SelectorList (or parentRule(s) are here. While the SelectorList (or parentRule(s) are
not attached the namespaces of all children Selectors are used.""") not attached the namespaces of all children Selectors are used.""")
parentRule = property(lambda self: self._parentRule, parentRule = property(lambda self: self._parentRule,
doc="(DOM) The CSS rule that contains this SelectorList or\ doc="(DOM) The CSS rule that contains this SelectorList or\
None if this SelectorList is not attached to a CSSRule.") None if this SelectorList is not attached to a CSSRule.")
def _getSelectorText(self): def _getSelectorText(self):
"returns serialized format" "returns serialized format"
return cssutils.ser.do_css_SelectorList(self) return cssutils.ser.do_css_SelectorList(self)
def _setSelectorText(self, selectorText): def _setSelectorText(self, selectorText):
""" """
:param selectorText: :param selectorText:
comma-separated list of selectors or a tuple of comma-separated list of selectors or a tuple of
(selectorText, dict-of-namespaces) (selectorText, dict-of-namespaces)
:Exceptions: :Exceptions:
- `NAMESPACE_ERR`: (Selector) - `NAMESPACE_ERR`: (Selector)
Raised if the specified selector uses an unknown namespace Raised if the specified selector uses an unknown namespace
prefix. prefix.
- `SYNTAX_ERR`: (self) - `SYNTAX_ERR`: (self)
Raised if the specified CSS string value has a syntax error Raised if the specified CSS string value has a syntax error
and is unparsable. and is unparsable.
- `NO_MODIFICATION_ALLOWED_ERR`: (self) - `NO_MODIFICATION_ALLOWED_ERR`: (self)
Raised if this rule is readonly. Raised if this rule is readonly.
""" """
self._checkReadonly() self._checkReadonly()
# might be (selectorText, namespaces) # might be (selectorText, namespaces)
selectorText, namespaces = self._splitNamespacesOff(selectorText) selectorText, namespaces = self._splitNamespacesOff(selectorText)
try: try:
# use parent's only if available # use parent's only if available
namespaces = self.parentRule.parentStyleSheet.namespaces namespaces = self.parentRule.parentStyleSheet.namespaces
except AttributeError: except AttributeError:
pass pass
wellformed = True wellformed = True
tokenizer = self._tokenize2(selectorText) tokenizer = self._tokenize2(selectorText)
newseq = [] newseq = []
expected = True expected = True
while True: while True:
# find all upto and including next ",", EOF or nothing # find all upto and including next ",", EOF or nothing
selectortokens = self._tokensupto2(tokenizer, listseponly=True) selectortokens = self._tokensupto2(tokenizer, listseponly=True)
if selectortokens: if selectortokens:
if self._tokenvalue(selectortokens[-1]) == ',': if self._tokenvalue(selectortokens[-1]) == ',':
expected = selectortokens.pop() expected = selectortokens.pop()
else: else:
expected = None expected = None
selector = Selector((selectortokens, namespaces), selector = Selector((selectortokens, namespaces),
parentList=self) parentList=self)
if selector.wellformed: if selector.wellformed:
newseq.append(selector) newseq.append(selector)
else: else:
wellformed = False wellformed = False
self._log.error(u'SelectorList: Invalid Selector: %s' % self._log.error(u'SelectorList: Invalid Selector: %s' %
self._valuestr(selectortokens)) self._valuestr(selectortokens))
else: else:
break break
# post condition # post condition
if u',' == expected: if u',' == expected:
wellformed = False wellformed = False
self._log.error(u'SelectorList: Cannot end with ",": %r' % self._log.error(u'SelectorList: Cannot end with ",": %r' %
self._valuestr(selectorText)) self._valuestr(selectorText))
elif expected: elif expected:
wellformed = False wellformed = False
self._log.error(u'SelectorList: Unknown Syntax: %r' % self._log.error(u'SelectorList: Unknown Syntax: %r' %
self._valuestr(selectorText)) self._valuestr(selectorText))
if wellformed: if wellformed:
self.seq = newseq self.seq = newseq
# for selector in newseq: # for selector in newseq:
# self.appendSelector(selector) # self.appendSelector(selector)
selectorText = property(_getSelectorText, _setSelectorText, selectorText = property(_getSelectorText, _setSelectorText,
doc="""(cssutils) The textual representation of the selector for doc="""(cssutils) The textual representation of the selector for
a rule set.""") a rule set.""")
wellformed = property(lambda self: bool(len(self.seq))) wellformed = property(lambda self: bool(len(self.seq)))
def appendSelector(self, newSelector): def appendSelector(self, newSelector):
""" """
Append newSelector (a string will be converted to a new Append newSelector (a string will be converted to a new
Selector). Selector).
:param newSelector: :param newSelector:
comma-separated list of selectors or a tuple of comma-separated list of selectors or a tuple of
(selectorText, dict-of-namespaces) (selectorText, dict-of-namespaces)
:returns: New Selector or None if newSelector is not wellformed. :returns: New Selector or None if newSelector is not wellformed.
:Exceptions: :Exceptions:
- `NAMESPACE_ERR`: (self) - `NAMESPACE_ERR`: (self)
Raised if the specified selector uses an unknown namespace Raised if the specified selector uses an unknown namespace
prefix. prefix.
- `SYNTAX_ERR`: (self) - `SYNTAX_ERR`: (self)
Raised if the specified CSS string value has a syntax error Raised if the specified CSS string value has a syntax error
and is unparsable. and is unparsable.
- `NO_MODIFICATION_ALLOWED_ERR`: (self) - `NO_MODIFICATION_ALLOWED_ERR`: (self)
Raised if this rule is readonly. Raised if this rule is readonly.
""" """
self._checkReadonly() self._checkReadonly()
# might be (selectorText, namespaces) # might be (selectorText, namespaces)
newSelector, namespaces = self._splitNamespacesOff(newSelector) newSelector, namespaces = self._splitNamespacesOff(newSelector)
try: try:
# use parent's only if available # use parent's only if available
namespaces = self.parentRule.parentStyleSheet.namespaces namespaces = self.parentRule.parentStyleSheet.namespaces
except AttributeError: except AttributeError:
# use already present namespaces plus new given ones # use already present namespaces plus new given ones
_namespaces = self._namespaces _namespaces = self._namespaces
_namespaces.update(namespaces) _namespaces.update(namespaces)
namespaces = _namespaces namespaces = _namespaces
newSelector = self.__prepareset(newSelector, namespaces) newSelector = self.__prepareset(newSelector, namespaces)
if newSelector: if newSelector:
seq = self.seq[:] seq = self.seq[:]
del self.seq[:] del self.seq[:]
for s in seq: for s in seq:
if s.selectorText != newSelector.selectorText: if s.selectorText != newSelector.selectorText:
self.seq.append(s) self.seq.append(s)
self.seq.append(newSelector) self.seq.append(newSelector)
return newSelector return newSelector
def __repr__(self): def __repr__(self):
if self._namespaces: if self._namespaces:
st = (self.selectorText, self._namespaces) st = (self.selectorText, self._namespaces)
else: else:
st = self.selectorText st = self.selectorText
return "cssutils.css.%s(selectorText=%r)" % ( return "cssutils.css.%s(selectorText=%r)" % (
self.__class__.__name__, st) self.__class__.__name__, st)
def __str__(self): def __str__(self):
return "<cssutils.css.%s object selectorText=%r _namespaces=%r at 0x%x>" % ( return "<cssutils.css.%s object selectorText=%r _namespaces=%r at 0x%x>" % (
self.__class__.__name__, self.selectorText, self._namespaces, self.__class__.__name__, self.selectorText, self._namespaces,
id(self)) id(self))
def _getUsedUris(self): def _getUsedUris(self):
"used by CSSStyleSheet to check if @namespace rules are needed" "used by CSSStyleSheet to check if @namespace rules are needed"
uris = set() uris = set()
for s in self: for s in self:
uris.update(s._getUsedUris()) uris.update(s._getUsedUris())
return uris return uris

View File

@ -1,117 +1,117 @@
#!/usr/bin/env python #!/usr/bin/env python
"""cssutils ErrorHandler """cssutils ErrorHandler
ErrorHandler ErrorHandler
used as log with usual levels (debug, info, warn, error) used as log with usual levels (debug, info, warn, error)
if instanciated with ``raiseExceptions=True`` raises exeptions instead if instanciated with ``raiseExceptions=True`` raises exeptions instead
of logging of logging
log log
defaults to instance of ErrorHandler for any kind of log message from defaults to instance of ErrorHandler for any kind of log message from
lexerm, parser etc. lexerm, parser etc.
- raiseExceptions = [False, True] - raiseExceptions = [False, True]
- setloglevel(loglevel) - setloglevel(loglevel)
""" """
__all__ = ['ErrorHandler'] __all__ = ['ErrorHandler']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: errorhandler.py 1361 2008-07-13 18:12:40Z cthedot $' __version__ = '$Id: errorhandler.py 1361 2008-07-13 18:12:40Z cthedot $'
import logging import logging
import urllib2 import urllib2
import xml.dom import xml.dom
from helper import Deprecated from helper import Deprecated
class _ErrorHandler(object): class _ErrorHandler(object):
""" """
handles all errors and log messages handles all errors and log messages
""" """
def __init__(self, log, defaultloglevel=logging.INFO, def __init__(self, log, defaultloglevel=logging.INFO,
raiseExceptions=True): raiseExceptions=True):
""" """
inits log if none given inits log if none given
log log
for parse messages, default logs to sys.stderr for parse messages, default logs to sys.stderr
defaultloglevel defaultloglevel
if none give this is logging.DEBUG if none give this is logging.DEBUG
raiseExceptions raiseExceptions
- True: Errors will be raised e.g. during building - True: Errors will be raised e.g. during building
- False: Errors will be written to the log, this is the - False: Errors will be written to the log, this is the
default behaviour when parsing default behaviour when parsing
""" """
if log: if log:
self._log = log self._log = log
else: else:
import sys import sys
self._log = logging.getLogger('CSSUTILS') self._log = logging.getLogger('CSSUTILS')
hdlr = logging.StreamHandler(sys.stderr) hdlr = logging.StreamHandler(sys.stderr)
formatter = logging.Formatter('%(levelname)s\t%(message)s') formatter = logging.Formatter('%(levelname)s\t%(message)s')
hdlr.setFormatter(formatter) hdlr.setFormatter(formatter)
self._log.addHandler(hdlr) self._log.addHandler(hdlr)
self._log.setLevel(defaultloglevel) self._log.setLevel(defaultloglevel)
self.raiseExceptions = raiseExceptions self.raiseExceptions = raiseExceptions
def __getattr__(self, name): def __getattr__(self, name):
"use self._log items" "use self._log items"
calls = ('debug', 'info', 'warn', 'error', 'critical', 'fatal') calls = ('debug', 'info', 'warn', 'error', 'critical', 'fatal')
other = ('setLevel', 'getEffectiveLevel', 'addHandler', 'removeHandler') other = ('setLevel', 'getEffectiveLevel', 'addHandler', 'removeHandler')
if name in calls: if name in calls:
self._logcall = getattr(self._log, name) self._logcall = getattr(self._log, name)
return self.__handle return self.__handle
elif name in other: elif name in other:
return getattr(self._log, name) return getattr(self._log, name)
else: else:
raise AttributeError( raise AttributeError(
'(errorhandler) No Attribute %r found' % name) '(errorhandler) No Attribute %r found' % name)
def __handle(self, msg=u'', token=None, error=xml.dom.SyntaxErr, def __handle(self, msg=u'', token=None, error=xml.dom.SyntaxErr,
neverraise=False, args=None): neverraise=False, args=None):
""" """
handles all calls handles all calls
logs or raises exception logs or raises exception
""" """
if token: if token:
if isinstance(token, tuple): if isinstance(token, tuple):
msg = u'%s [%s:%s: %s]' % ( msg = u'%s [%s:%s: %s]' % (
msg, token[2], token[3], token[1]) msg, token[2], token[3], token[1])
else: else:
msg = u'%s [%s:%s: %s]' % ( msg = u'%s [%s:%s: %s]' % (
msg, token.line, token.col, token.value) msg, token.line, token.col, token.value)
if error and self.raiseExceptions and not neverraise: if error and self.raiseExceptions and not neverraise:
if isinstance(error, urllib2.HTTPError) or isinstance(error, urllib2.URLError): if isinstance(error, urllib2.HTTPError) or isinstance(error, urllib2.URLError):
raise error raise error
else: else:
raise error(msg) raise error(msg)
else: else:
self._logcall(msg) self._logcall(msg)
def setLog(self, log): def setLog(self, log):
"""set log of errorhandler's log""" """set log of errorhandler's log"""
self._log = log self._log = log
@Deprecated('Use setLog() instead.') @Deprecated('Use setLog() instead.')
def setlog(self, log): def setlog(self, log):
self.setLog(log) self.setLog(log)
@Deprecated('Use setLevel() instead.') @Deprecated('Use setLevel() instead.')
def setloglevel(self, level): def setloglevel(self, level):
self.setLevel(level) self.setLevel(level)
class ErrorHandler(_ErrorHandler): class ErrorHandler(_ErrorHandler):
"Singleton, see _ErrorHandler" "Singleton, see _ErrorHandler"
instance = None instance = None
def __init__(self, def __init__(self,
log=None, defaultloglevel=logging.INFO, raiseExceptions=True): log=None, defaultloglevel=logging.INFO, raiseExceptions=True):
if ErrorHandler.instance is None: if ErrorHandler.instance is None:
ErrorHandler.instance = _ErrorHandler(log=log, ErrorHandler.instance = _ErrorHandler(log=log,
defaultloglevel=defaultloglevel, defaultloglevel=defaultloglevel,
raiseExceptions=raiseExceptions) raiseExceptions=raiseExceptions)
self.__dict__ = ErrorHandler.instance.__dict__ self.__dict__ = ErrorHandler.instance.__dict__

View File

@ -1,183 +1,183 @@
#!/usr/bin/env python #!/usr/bin/env python
"""a validating CSSParser """a validating CSSParser
""" """
__all__ = ['CSSParser'] __all__ = ['CSSParser']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: parse.py 1418 2008-08-09 19:27:50Z cthedot $' __version__ = '$Id: parse.py 1418 2008-08-09 19:27:50Z cthedot $'
import codecs import codecs
import os import os
import urllib import urllib
from helper import Deprecated from helper import Deprecated
import tokenize2 import tokenize2
import cssutils import cssutils
class CSSParser(object): class CSSParser(object):
""" """
parses a CSS StyleSheet string or file and parses a CSS StyleSheet string or file and
returns a DOM Level 2 CSS StyleSheet object returns a DOM Level 2 CSS StyleSheet object
Usage:: Usage::
parser = CSSParser() parser = CSSParser()
# optionally # optionally
parser.setFetcher(fetcher) parser.setFetcher(fetcher)
sheet = parser.parseFile('test1.css', 'ascii') sheet = parser.parseFile('test1.css', 'ascii')
print sheet.cssText print sheet.cssText
""" """
def __init__(self, log=None, loglevel=None, raiseExceptions=None, def __init__(self, log=None, loglevel=None, raiseExceptions=None,
fetcher=None): fetcher=None):
""" """
log log
logging object logging object
loglevel loglevel
logging loglevel logging loglevel
raiseExceptions raiseExceptions
if log should simply log (default) or raise errors during if log should simply log (default) or raise errors during
parsing. Later while working with the resulting sheets parsing. Later while working with the resulting sheets
the setting used in cssutils.log.raiseExeptions is used the setting used in cssutils.log.raiseExeptions is used
fetcher fetcher
see ``setFetchUrl(fetcher)`` see ``setFetchUrl(fetcher)``
""" """
if log is not None: if log is not None:
cssutils.log.setLog(log) cssutils.log.setLog(log)
if loglevel is not None: if loglevel is not None:
cssutils.log.setLevel(loglevel) cssutils.log.setLevel(loglevel)
# remember global setting # remember global setting
self.__globalRaising = cssutils.log.raiseExceptions self.__globalRaising = cssutils.log.raiseExceptions
if raiseExceptions: if raiseExceptions:
self.__parseRaising = raiseExceptions self.__parseRaising = raiseExceptions
else: else:
# DEFAULT during parse # DEFAULT during parse
self.__parseRaising = False self.__parseRaising = False
self.__tokenizer = tokenize2.Tokenizer() self.__tokenizer = tokenize2.Tokenizer()
self.setFetcher(fetcher) self.setFetcher(fetcher)
def __parseSetting(self, parse): def __parseSetting(self, parse):
"""during parse exceptions may be handled differently depending on """during parse exceptions may be handled differently depending on
init parameter ``raiseExceptions`` init parameter ``raiseExceptions``
""" """
if parse: if parse:
cssutils.log.raiseExceptions = self.__parseRaising cssutils.log.raiseExceptions = self.__parseRaising
else: else:
cssutils.log.raiseExceptions = self.__globalRaising cssutils.log.raiseExceptions = self.__globalRaising
def parseString(self, cssText, encoding=None, href=None, media=None, def parseString(self, cssText, encoding=None, href=None, media=None,
title=None): title=None):
"""Return parsed CSSStyleSheet from given string cssText. """Return parsed CSSStyleSheet from given string cssText.
Raises errors during retrieving (e.g. UnicodeDecodeError). Raises errors during retrieving (e.g. UnicodeDecodeError).
cssText cssText
CSS string to parse CSS string to parse
encoding encoding
If ``None`` the encoding will be read from BOM or an @charset If ``None`` the encoding will be read from BOM or an @charset
rule or defaults to UTF-8. rule or defaults to UTF-8.
If given overrides any found encoding including the ones for If given overrides any found encoding including the ones for
imported sheets. imported sheets.
It also will be used to decode ``cssText`` if given as a (byte) It also will be used to decode ``cssText`` if given as a (byte)
string. string.
href href
The href attribute to assign to the parsed style sheet. The href attribute to assign to the parsed style sheet.
Used to resolve other urls in the parsed sheet like @import hrefs Used to resolve other urls in the parsed sheet like @import hrefs
media media
The media attribute to assign to the parsed style sheet The media attribute to assign to the parsed style sheet
(may be a MediaList, list or a string) (may be a MediaList, list or a string)
title title
The title attribute to assign to the parsed style sheet The title attribute to assign to the parsed style sheet
""" """
self.__parseSetting(True) self.__parseSetting(True)
if isinstance(cssText, str): if isinstance(cssText, str):
cssText = codecs.getdecoder('css')(cssText, encoding=encoding)[0] cssText = codecs.getdecoder('css')(cssText, encoding=encoding)[0]
sheet = cssutils.css.CSSStyleSheet(href=href, sheet = cssutils.css.CSSStyleSheet(href=href,
media=cssutils.stylesheets.MediaList(media), media=cssutils.stylesheets.MediaList(media),
title=title) title=title)
sheet._setFetcher(self.__fetcher) sheet._setFetcher(self.__fetcher)
# tokenizing this ways closes open constructs and adds EOF # tokenizing this ways closes open constructs and adds EOF
sheet._setCssTextWithEncodingOverride(self.__tokenizer.tokenize(cssText, sheet._setCssTextWithEncodingOverride(self.__tokenizer.tokenize(cssText,
fullsheet=True), fullsheet=True),
encodingOverride=encoding) encodingOverride=encoding)
self.__parseSetting(False) self.__parseSetting(False)
return sheet return sheet
def parseFile(self, filename, encoding=None, def parseFile(self, filename, encoding=None,
href=None, media=None, title=None): href=None, media=None, title=None):
"""Retrieve and return a CSSStyleSheet from given filename. """Retrieve and return a CSSStyleSheet from given filename.
Raises errors during retrieving (e.g. IOError). Raises errors during retrieving (e.g. IOError).
filename filename
of the CSS file to parse, if no ``href`` is given filename is of the CSS file to parse, if no ``href`` is given filename is
converted to a (file:) URL and set as ``href`` of resulting converted to a (file:) URL and set as ``href`` of resulting
stylesheet. stylesheet.
If href is given it is set as ``sheet.href``. Either way If href is given it is set as ``sheet.href``. Either way
``sheet.href`` is used to resolve e.g. stylesheet imports via ``sheet.href`` is used to resolve e.g. stylesheet imports via
@import rules. @import rules.
encoding encoding
Value ``None`` defaults to encoding detection via BOM or an Value ``None`` defaults to encoding detection via BOM or an
@charset rule. @charset rule.
Other values override detected encoding for the sheet at Other values override detected encoding for the sheet at
``filename`` including any imported sheets. ``filename`` including any imported sheets.
for other parameters see ``parseString`` for other parameters see ``parseString``
""" """
if not href: if not href:
# prepend // for file URL, urllib does not do this? # prepend // for file URL, urllib does not do this?
href = u'file:' + urllib.pathname2url(os.path.abspath(filename)) href = u'file:' + urllib.pathname2url(os.path.abspath(filename))
return self.parseString(open(filename, 'rb').read(), return self.parseString(open(filename, 'rb').read(),
encoding=encoding, # read returns a str encoding=encoding, # read returns a str
href=href, media=media, title=title) href=href, media=media, title=title)
def parseUrl(self, href, encoding=None, media=None, title=None): def parseUrl(self, href, encoding=None, media=None, title=None):
"""Retrieve and return a CSSStyleSheet from given href (an URL). """Retrieve and return a CSSStyleSheet from given href (an URL).
In case of any errors while reading the URL returns None. In case of any errors while reading the URL returns None.
href href
URL of the CSS file to parse, will also be set as ``href`` of URL of the CSS file to parse, will also be set as ``href`` of
resulting stylesheet resulting stylesheet
encoding encoding
Value ``None`` defaults to encoding detection via HTTP, BOM or an Value ``None`` defaults to encoding detection via HTTP, BOM or an
@charset rule. @charset rule.
A value overrides detected encoding for the sheet at ``href`` A value overrides detected encoding for the sheet at ``href``
including any imported sheets. including any imported sheets.
for other parameters see ``parseString`` for other parameters see ``parseString``
""" """
encoding, enctype, text = cssutils.util._readUrl(href, encoding, enctype, text = cssutils.util._readUrl(href,
overrideEncoding=encoding) overrideEncoding=encoding)
if enctype == 5: if enctype == 5:
# do not used if defaulting to UTF-8 # do not used if defaulting to UTF-8
encoding = None encoding = None
if text is not None: if text is not None:
return self.parseString(text, encoding=encoding, return self.parseString(text, encoding=encoding,
href=href, media=media, title=title) href=href, media=media, title=title)
def setFetcher(self, fetcher=None): def setFetcher(self, fetcher=None):
"""Replace the default URL fetch function with a custom one. """Replace the default URL fetch function with a custom one.
The fetcher function gets a single parameter The fetcher function gets a single parameter
``url`` ``url``
the URL to read the URL to read
and returns ``(encoding, content)`` where ``encoding`` is the HTTP and returns ``(encoding, content)`` where ``encoding`` is the HTTP
charset normally given via the Content-Type header (which may simply charset normally given via the Content-Type header (which may simply
omit the charset) and ``content`` being the (byte) string content. omit the charset) and ``content`` being the (byte) string content.
The Mimetype should be 'text/css' but this has to be checked by the The Mimetype should be 'text/css' but this has to be checked by the
fetcher itself (the default fetcher emits a warning if encountering fetcher itself (the default fetcher emits a warning if encountering
a different mimetype). a different mimetype).
Calling ``setFetcher`` with ``fetcher=None`` resets cssutils Calling ``setFetcher`` with ``fetcher=None`` resets cssutils
to use its default function. to use its default function.
""" """
self.__fetcher = fetcher self.__fetcher = fetcher
@Deprecated('Use cssutils.CSSParser().parseFile() instead.') @Deprecated('Use cssutils.CSSParser().parseFile() instead.')
def parse(self, filename, encoding=None, def parse(self, filename, encoding=None,
href=None, media=None, title=None): href=None, media=None, title=None):
self.parseFile(filename, encoding, href, media, title) self.parseFile(filename, encoding, href, media, title)

402
src/cssutils/prodparser.py Normal file
View 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
View 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()

View File

@ -1,18 +1,18 @@
""" """
Document Object Model Level 2 Style Sheets Document Object Model Level 2 Style Sheets
http://www.w3.org/TR/2000/PR-DOM-Level-2-Style-20000927/stylesheets.html http://www.w3.org/TR/2000/PR-DOM-Level-2-Style-20000927/stylesheets.html
currently implemented: currently implemented:
- MediaList - MediaList
- MediaQuery (http://www.w3.org/TR/css3-mediaqueries/) - MediaQuery (http://www.w3.org/TR/css3-mediaqueries/)
- StyleSheet - StyleSheet
- StyleSheetList - StyleSheetList
""" """
__all__ = ['MediaList', 'MediaQuery', 'StyleSheet', 'StyleSheetList'] __all__ = ['MediaList', 'MediaQuery', 'StyleSheet', 'StyleSheetList']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: __init__.py 1116 2008-03-05 13:52:23Z cthedot $' __version__ = '$Id: __init__.py 1116 2008-03-05 13:52:23Z cthedot $'
from medialist import * from medialist import *
from mediaquery import * from mediaquery import *
from stylesheet import * from stylesheet import *
from stylesheetlist import * from stylesheetlist import *

View File

@ -1,256 +1,256 @@
""" """
MediaList implements DOM Level 2 Style Sheets MediaList. MediaList implements DOM Level 2 Style Sheets MediaList.
TODO: TODO:
- delete: maybe if deleting from all, replace *all* with all others? - delete: maybe if deleting from all, replace *all* with all others?
- is unknown media an exception? - is unknown media an exception?
""" """
__all__ = ['MediaList'] __all__ = ['MediaList']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: medialist.py 1423 2008-08-11 12:43:22Z cthedot $' __version__ = '$Id: medialist.py 1423 2008-08-11 12:43:22Z cthedot $'
import xml.dom import xml.dom
import cssutils import cssutils
from cssutils.css import csscomment from cssutils.css import csscomment
from mediaquery import MediaQuery from mediaquery import MediaQuery
class MediaList(cssutils.util.Base, cssutils.util.ListSeq): class MediaList(cssutils.util.Base, cssutils.util.ListSeq):
""" """
Provides the abstraction of an ordered collection of media, Provides the abstraction of an ordered collection of media,
without defining or constraining how this collection is without defining or constraining how this collection is
implemented. implemented.
A media is always an instance of MediaQuery. A media is always an instance of MediaQuery.
An empty list is the same as a list that contains the medium "all". An empty list is the same as a list that contains the medium "all".
Properties Properties
========== ==========
length: length:
The number of MediaQuery objects in the list. The number of MediaQuery objects in the list.
mediaText: of type DOMString mediaText: of type DOMString
The parsable textual representation of this MediaList The parsable textual representation of this MediaList
self: a list (cssutils) self: a list (cssutils)
All MediaQueries in this MediaList All MediaQueries in this MediaList
wellformed: wellformed:
if this list is wellformed if this list is wellformed
Format Format
====== ======
:: ::
medium [ COMMA S* medium ]* medium [ COMMA S* medium ]*
New:: New::
<media_query> [, <media_query> ]* <media_query> [, <media_query> ]*
""" """
def __init__(self, mediaText=None, readonly=False): def __init__(self, mediaText=None, readonly=False):
""" """
mediaText mediaText
unicodestring of parsable comma separared media unicodestring of parsable comma separared media
or a list of media or a list of media
""" """
super(MediaList, self).__init__() super(MediaList, self).__init__()
self._wellformed = False self._wellformed = False
if isinstance(mediaText, list): if isinstance(mediaText, list):
mediaText = u','.join(mediaText) mediaText = u','.join(mediaText)
if mediaText: if mediaText:
self.mediaText = mediaText self.mediaText = mediaText
self._readonly = readonly self._readonly = readonly
length = property(lambda self: len(self), length = property(lambda self: len(self),
doc="(DOM readonly) The number of media in the list.") doc="(DOM readonly) The number of media in the list.")
def _getMediaText(self): def _getMediaText(self):
""" """
returns serialized property mediaText returns serialized property mediaText
""" """
return cssutils.ser.do_stylesheets_medialist(self) return cssutils.ser.do_stylesheets_medialist(self)
def _setMediaText(self, mediaText): def _setMediaText(self, mediaText):
""" """
mediaText mediaText
simple value or comma-separated list of media simple value or comma-separated list of media
DOMException DOMException
- SYNTAX_ERR: (MediaQuery) - SYNTAX_ERR: (MediaQuery)
Raised if the specified string value has a syntax error and is Raised if the specified string value has a syntax error and is
unparsable. unparsable.
- NO_MODIFICATION_ALLOWED_ERR: (self) - NO_MODIFICATION_ALLOWED_ERR: (self)
Raised if this media list is readonly. Raised if this media list is readonly.
""" """
self._checkReadonly() self._checkReadonly()
wellformed = True wellformed = True
tokenizer = self._tokenize2(mediaText) tokenizer = self._tokenize2(mediaText)
newseq = [] newseq = []
expected = None expected = None
while True: while True:
# find all upto and including next ",", EOF or nothing # find all upto and including next ",", EOF or nothing
mqtokens = self._tokensupto2(tokenizer, listseponly=True) mqtokens = self._tokensupto2(tokenizer, listseponly=True)
if mqtokens: if mqtokens:
if self._tokenvalue(mqtokens[-1]) == ',': if self._tokenvalue(mqtokens[-1]) == ',':
expected = mqtokens.pop() expected = mqtokens.pop()
else: else:
expected = None expected = None
mq = MediaQuery(mqtokens) mq = MediaQuery(mqtokens)
if mq.wellformed: if mq.wellformed:
newseq.append(mq) newseq.append(mq)
else: else:
wellformed = False wellformed = False
self._log.error(u'MediaList: Invalid MediaQuery: %s' % self._log.error(u'MediaList: Invalid MediaQuery: %s' %
self._valuestr(mqtokens)) self._valuestr(mqtokens))
else: else:
break break
# post condition # post condition
if expected: if expected:
wellformed = False wellformed = False
self._log.error(u'MediaList: Cannot end with ",".') self._log.error(u'MediaList: Cannot end with ",".')
if wellformed: if wellformed:
del self[:] del self[:]
for mq in newseq: for mq in newseq:
self.appendMedium(mq) self.appendMedium(mq)
self._wellformed = True self._wellformed = True
mediaText = property(_getMediaText, _setMediaText, mediaText = property(_getMediaText, _setMediaText,
doc="""(DOM) The parsable textual representation of the media list. doc="""(DOM) The parsable textual representation of the media list.
This is a comma-separated list of media.""") This is a comma-separated list of media.""")
wellformed = property(lambda self: self._wellformed) wellformed = property(lambda self: self._wellformed)
def __prepareset(self, newMedium): def __prepareset(self, newMedium):
# used by appendSelector and __setitem__ # used by appendSelector and __setitem__
self._checkReadonly() self._checkReadonly()
if not isinstance(newMedium, MediaQuery): if not isinstance(newMedium, MediaQuery):
newMedium = MediaQuery(newMedium) newMedium = MediaQuery(newMedium)
if newMedium.wellformed: if newMedium.wellformed:
return newMedium return newMedium
def __setitem__(self, index, newMedium): def __setitem__(self, index, newMedium):
""" """
overwrites ListSeq.__setitem__ overwrites ListSeq.__setitem__
Any duplicate items are **not** removed. Any duplicate items are **not** removed.
""" """
newMedium = self.__prepareset(newMedium) newMedium = self.__prepareset(newMedium)
if newMedium: if newMedium:
self.seq[index] = newMedium self.seq[index] = newMedium
# TODO: remove duplicates? # TODO: remove duplicates?
def appendMedium(self, newMedium): def appendMedium(self, newMedium):
""" """
(DOM) (DOM)
Adds the medium newMedium to the end of the list. If the newMedium Adds the medium newMedium to the end of the list. If the newMedium
is already used, it is first removed. is already used, it is first removed.
newMedium newMedium
a string or a MediaQuery object a string or a MediaQuery object
returns if newMedium is wellformed returns if newMedium is wellformed
DOMException DOMException
- INVALID_CHARACTER_ERR: (self) - INVALID_CHARACTER_ERR: (self)
If the medium contains characters that are invalid in the If the medium contains characters that are invalid in the
underlying style language. underlying style language.
- INVALID_MODIFICATION_ERR (self) - INVALID_MODIFICATION_ERR (self)
If mediaText is "all" and a new medium is tried to be added. 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 Exception is "handheld" which is set in any case (Opera does handle
"all, handheld" special, this special case might be removed in the "all, handheld" special, this special case might be removed in the
future). future).
- NO_MODIFICATION_ALLOWED_ERR: (self) - NO_MODIFICATION_ALLOWED_ERR: (self)
Raised if this list is readonly. Raised if this list is readonly.
""" """
newMedium = self.__prepareset(newMedium) newMedium = self.__prepareset(newMedium)
if newMedium: if newMedium:
mts = [self._normalize(mq.mediaType) for mq in self] mts = [self._normalize(mq.mediaType) for mq in self]
newmt = self._normalize(newMedium.mediaType) newmt = self._normalize(newMedium.mediaType)
if newmt in mts: if newmt in mts:
self.deleteMedium(newmt) self.deleteMedium(newmt)
self.seq.append(newMedium) self.seq.append(newMedium)
elif u'all' == newmt: elif u'all' == newmt:
# remove all except handheld (Opera) # remove all except handheld (Opera)
h = None h = None
for mq in self: for mq in self:
if mq.mediaType == u'handheld': if mq.mediaType == u'handheld':
h = mq h = mq
del self[:] del self[:]
self.seq.append(newMedium) self.seq.append(newMedium)
if h: if h:
self.append(h) self.append(h)
elif u'all' in mts: elif u'all' in mts:
if u'handheld' == newmt: if u'handheld' == newmt:
self.seq.append(newMedium) self.seq.append(newMedium)
self._log.warn(u'MediaList: Already specified "all" but still setting new medium: %r' % self._log.warn(u'MediaList: Already specified "all" but still setting new medium: %r' %
newMedium, error=xml.dom.InvalidModificationErr, neverraise=True) newMedium, error=xml.dom.InvalidModificationErr, neverraise=True)
else: else:
self._log.warn(u'MediaList: Ignoring new medium %r as already specified "all" (set ``mediaText`` instead).' % self._log.warn(u'MediaList: Ignoring new medium %r as already specified "all" (set ``mediaText`` instead).' %
newMedium, error=xml.dom.InvalidModificationErr) newMedium, error=xml.dom.InvalidModificationErr)
else: else:
self.seq.append(newMedium) self.seq.append(newMedium)
return True return True
else: else:
return False return False
def append(self, newMedium): def append(self, newMedium):
"overwrites ListSeq.append" "overwrites ListSeq.append"
self.appendMedium(newMedium) self.appendMedium(newMedium)
def deleteMedium(self, oldMedium): def deleteMedium(self, oldMedium):
""" """
(DOM) (DOM)
Deletes the medium indicated by oldMedium from the list. Deletes the medium indicated by oldMedium from the list.
DOMException DOMException
- NO_MODIFICATION_ALLOWED_ERR: (self) - NO_MODIFICATION_ALLOWED_ERR: (self)
Raised if this list is readonly. Raised if this list is readonly.
- NOT_FOUND_ERR: (self) - NOT_FOUND_ERR: (self)
Raised if oldMedium is not in the list. Raised if oldMedium is not in the list.
""" """
self._checkReadonly() self._checkReadonly()
oldMedium = self._normalize(oldMedium) oldMedium = self._normalize(oldMedium)
for i, mq in enumerate(self): for i, mq in enumerate(self):
if self._normalize(mq.mediaType) == oldMedium: if self._normalize(mq.mediaType) == oldMedium:
del self[i] del self[i]
break break
else: else:
self._log.error(u'"%s" not in this MediaList' % oldMedium, self._log.error(u'"%s" not in this MediaList' % oldMedium,
error=xml.dom.NotFoundErr) error=xml.dom.NotFoundErr)
# raise xml.dom.NotFoundErr( # raise xml.dom.NotFoundErr(
# u'"%s" not in this MediaList' % oldMedium) # u'"%s" not in this MediaList' % oldMedium)
def item(self, index): def item(self, index):
""" """
(DOM) (DOM)
Returns the mediaType of the index'th element in the list. 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 If index is greater than or equal to the number of media in the
list, returns None. list, returns None.
""" """
try: try:
return self[index].mediaType return self[index].mediaType
except IndexError: except IndexError:
return None return None
def __repr__(self): def __repr__(self):
return "cssutils.stylesheets.%s(mediaText=%r)" % ( return "cssutils.stylesheets.%s(mediaText=%r)" % (
self.__class__.__name__, self.mediaText) self.__class__.__name__, self.mediaText)
def __str__(self): def __str__(self):
return "<cssutils.stylesheets.%s object mediaText=%r at 0x%x>" % ( return "<cssutils.stylesheets.%s object mediaText=%r at 0x%x>" % (
self.__class__.__name__, self.mediaText, id(self)) self.__class__.__name__, self.mediaText, id(self))

View File

@ -1,101 +1,101 @@
""" """
StyleSheet implements DOM Level 2 Style Sheets StyleSheet. StyleSheet implements DOM Level 2 Style Sheets StyleSheet.
""" """
__all__ = ['StyleSheet'] __all__ = ['StyleSheet']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: stylesheet.py 1284 2008-06-05 16:29:17Z cthedot $' __version__ = '$Id: stylesheet.py 1284 2008-06-05 16:29:17Z cthedot $'
import urlparse import urlparse
import cssutils import cssutils
class StyleSheet(cssutils.util.Base2): class StyleSheet(cssutils.util.Base2):
""" """
The StyleSheet interface is the abstract base interface The StyleSheet interface is the abstract base interface
for any type of style sheet. It represents a single style for any type of style sheet. It represents a single style
sheet associated with a structured document. sheet associated with a structured document.
In HTML, the StyleSheet interface represents either an In HTML, the StyleSheet interface represents either an
external style sheet, included via the HTML LINK element, external style sheet, included via the HTML LINK element,
or an inline STYLE element (-ch: also an @import stylesheet?). or an inline STYLE element (-ch: also an @import stylesheet?).
In XML, this interface represents In XML, this interface represents
an external style sheet, included via a style sheet an external style sheet, included via a style sheet
processing instruction. processing instruction.
""" """
def __init__(self, type='text/css', def __init__(self, type='text/css',
href=None, href=None,
media=None, media=None,
title=u'', title=u'',
disabled=None, disabled=None,
ownerNode=None, ownerNode=None,
parentStyleSheet=None): parentStyleSheet=None):
""" """
type: readonly type: readonly
This specifies the style sheet language for this This specifies the style sheet language for this
style sheet. The style sheet language is specified style sheet. The style sheet language is specified
as a content type (e.g. "text/css"). The content as a content type (e.g. "text/css"). The content
type is often specified in the ownerNode. Also see type is often specified in the ownerNode. Also see
the type attribute definition for the LINK element the type attribute definition for the LINK element
in HTML 4.0, and the type pseudo-attribute for the in HTML 4.0, and the type pseudo-attribute for the
XML style sheet processing instruction. XML style sheet processing instruction.
href: readonly href: readonly
If the style sheet is a linked style sheet, the value If the style sheet is a linked style sheet, the value
of this attribute is its location. For inline style of this attribute is its location. For inline style
sheets, the value of this attribute is None. See the sheets, the value of this attribute is None. See the
href attribute definition for the LINK element in HTML href attribute definition for the LINK element in HTML
4.0, and the href pseudo-attribute for the XML style 4.0, and the href pseudo-attribute for the XML style
sheet processing instruction. sheet processing instruction.
media: of type MediaList, readonly media: of type MediaList, readonly
The intended destination media for style information. The intended destination media for style information.
The media is often specified in the ownerNode. If no The media is often specified in the ownerNode. If no
media has been specified, the MediaList will be empty. media has been specified, the MediaList will be empty.
See the media attribute definition for the LINK element See the media attribute definition for the LINK element
in HTML 4.0, and the media pseudo-attribute for the XML in HTML 4.0, and the media pseudo-attribute for the XML
style sheet processing instruction. Modifying the media style sheet processing instruction. Modifying the media
list may cause a change to the attribute disabled. list may cause a change to the attribute disabled.
title: readonly title: readonly
The advisory title. The title is often specified in The advisory title. The title is often specified in
the ownerNode. See the title attribute definition for the ownerNode. See the title attribute definition for
the LINK element in HTML 4.0, and the title the LINK element in HTML 4.0, and the title
pseudo-attribute for the XML style sheet processing pseudo-attribute for the XML style sheet processing
instruction. instruction.
disabled: False if the style sheet is applied to the disabled: False if the style sheet is applied to the
document. True if it is not. Modifying this attribute document. True if it is not. Modifying this attribute
may cause a new resolution of style for the document. may cause a new resolution of style for the document.
A stylesheet only applies if both an appropriate medium A stylesheet only applies if both an appropriate medium
definition is present and the disabled attribute is False. definition is present and the disabled attribute is False.
So, if the media doesn't apply to the current user agent, So, if the media doesn't apply to the current user agent,
the disabled attribute is ignored. the disabled attribute is ignored.
ownerNode: of type Node, readonly ownerNode: of type Node, readonly
The node that associates this style sheet with the The node that associates this style sheet with the
document. For HTML, this may be the corresponding LINK document. For HTML, this may be the corresponding LINK
or STYLE element. For XML, it may be the linking or STYLE element. For XML, it may be the linking
processing instruction. For style sheets that are processing instruction. For style sheets that are
included by other style sheets, the value of this included by other style sheets, the value of this
attribute is None. attribute is None.
parentStyleSheet: of type StyleSheet, readonly parentStyleSheet: of type StyleSheet, readonly
For style sheet languages that support the concept For style sheet languages that support the concept
of style sheet inclusion, this attribute represents of style sheet inclusion, this attribute represents
the including style sheet, if one exists. If the style the including style sheet, if one exists. If the style
sheet is a top-level style sheet, or the style sheet sheet is a top-level style sheet, or the style sheet
language does not support inclusion, the value of this language does not support inclusion, the value of this
attribute is None. attribute is None.
""" """
super(StyleSheet, self).__init__() super(StyleSheet, self).__init__()
self._href = href self._href = href
self._ownerNode = ownerNode self._ownerNode = ownerNode
self._parentStyleSheet = parentStyleSheet self._parentStyleSheet = parentStyleSheet
self._type = type self._type = type
self.disabled = bool(disabled) self.disabled = bool(disabled)
self.media = media self.media = media
self.title = title self.title = title
href = property(lambda self: self._href) href = property(lambda self: self._href)
ownerNode = property(lambda self: self._ownerNode) ownerNode = property(lambda self: self._ownerNode)
parentStyleSheet = property(lambda self: self._parentStyleSheet) parentStyleSheet = property(lambda self: self._parentStyleSheet)
type = property(lambda self: self._type, doc=u'Default: "ext/css"') type = property(lambda self: self._type, doc=u'Default: "ext/css"')

View File

@ -1,35 +1,35 @@
""" """
StyleSheetList implements DOM Level 2 Style Sheets StyleSheetList. StyleSheetList implements DOM Level 2 Style Sheets StyleSheetList.
""" """
__all__ = ['StyleSheetList'] __all__ = ['StyleSheetList']
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
__version__ = '$Id: stylesheetlist.py 1116 2008-03-05 13:52:23Z cthedot $' __version__ = '$Id: stylesheetlist.py 1116 2008-03-05 13:52:23Z cthedot $'
class StyleSheetList(list): class StyleSheetList(list):
""" """
Interface StyleSheetList (introduced in DOM Level 2) Interface StyleSheetList (introduced in DOM Level 2)
The StyleSheetList interface provides the abstraction of an ordered The StyleSheetList interface provides the abstraction of an ordered
collection of style sheets. collection of style sheets.
The items in the StyleSheetList are accessible via an integral index, The items in the StyleSheetList are accessible via an integral index,
starting from 0. starting from 0.
This Python implementation is based on a standard Python list so e.g. This Python implementation is based on a standard Python list so e.g.
allows ``examplelist[index]`` usage. allows ``examplelist[index]`` usage.
""" """
def item(self, index): def item(self, index):
""" """
Used to retrieve a style sheet by ordinal index. If index is 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, greater than or equal to the number of style sheets in the list,
this returns None. this returns None.
""" """
try: try:
return self[index] return self[index]
except IndexError: except IndexError:
return None return None
length = property(lambda self: len(self), length = property(lambda self: len(self),
doc="""The number of StyleSheets in the list. The range of valid doc="""The number of StyleSheets in the list. The range of valid
child stylesheet indices is 0 to length-1 inclusive.""") child stylesheet indices is 0 to length-1 inclusive.""")

File diff suppressed because it is too large Load Diff