This commit is contained in:
2021-04-19 20:16:55 +02:00
commit a0ff94dca2
839 changed files with 198976 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
Name: py_vulcanize
URL: N/A
Version: N/A
Description:
Py-vulcanize, formerly known as TVCM (trace-viewer component model).
This code doesn't actually live anywhere else currently, but it may
be split out into a separate repository in the future.

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env python
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import sys
_CATAPULT = os.path.abspath(os.path.join(
os.path.dirname(__file__), os.path.pardir, os.path.pardir, os.path.pardir))
def _AddToPathIfNeeded(path):
if path not in sys.path:
sys.path.insert(0, path)
if __name__ == '__main__':
_AddToPathIfNeeded(_CATAPULT)
from hooks import install
if '--no-install-hooks' in sys.argv:
sys.argv.remove('--no-install-hooks')
else:
install.InstallHooks()
from catapult_build import run_with_typ
sys.exit(run_with_typ.Run(
os.path.join(_CATAPULT, 'common', 'py_vulcanize')))

View File

@@ -0,0 +1,11 @@
# Copyright (c) 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Trace-viewer component model.
This module implements trace-viewer's component model.
"""
from py_vulcanize.generate import * # pylint: disable=wildcard-import
from py_vulcanize.project import Project

View File

@@ -0,0 +1,151 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import codecs
import collections
import os
import sys
import six
class WithableStringIO(six.StringIO):
def __enter__(self, *args):
return self
def __exit__(self, *args):
pass
class FakeFS(object):
def __init__(self, initial_filenames_and_contents=None):
self._file_contents = {}
if initial_filenames_and_contents:
for k, v in six.iteritems(initial_filenames_and_contents):
self._file_contents[k] = v
self._bound = False
self._real_codecs_open = codecs.open
self._real_open = sys.modules['__builtin__'].open
self._real_abspath = os.path.abspath
self._real_exists = os.path.exists
self._real_walk = os.walk
self._real_listdir = os.listdir
def __enter__(self):
self.Bind()
return self
def __exit__(self, *args):
self.Unbind()
def Bind(self):
assert not self._bound
codecs.open = self._FakeCodecsOpen
sys.modules['__builtin__'].open = self._FakeOpen
os.path.abspath = self._FakeAbspath
os.path.exists = self._FakeExists
os.walk = self._FakeWalk
os.listdir = self._FakeListDir
self._bound = True
def Unbind(self):
assert self._bound
codecs.open = self._real_codecs_open
sys.modules['__builtin__'].open = self._real_open
os.path.abspath = self._real_abspath
os.path.exists = self._real_exists
os.walk = self._real_walk
os.listdir = self._real_listdir
self._bound = False
def AddFile(self, path, contents):
assert path not in self._file_contents
path = os.path.normpath(path)
self._file_contents[path] = contents
def _FakeOpen(self, path, mode=None):
if mode is None:
mode = 'r'
if mode == 'r' or mode == 'rU' or mode == 'rb':
if path not in self._file_contents:
return self._real_open(path, mode)
return WithableStringIO(self._file_contents[path])
raise NotImplementedError()
def _FakeCodecsOpen(self, path, mode=None,
encoding=None): # pylint: disable=unused-argument
if mode is None:
mode = 'r'
if mode == 'r' or mode == 'rU' or mode == 'rb':
if path not in self._file_contents:
return self._real_open(path, mode)
return WithableStringIO(self._file_contents[path])
raise NotImplementedError()
def _FakeAbspath(self, path):
"""Normalize the path and ensure it starts with os.path.sep.
The tests all assume paths start with things like '/my/project',
and this abspath implementaion makes that assumption work correctly
on Windows.
"""
normpath = os.path.normpath(path)
if not normpath.startswith(os.path.sep):
normpath = os.path.sep + normpath
return normpath
def _FakeExists(self, path):
if path in self._file_contents:
return True
return self._real_exists(path)
def _FakeWalk(self, top):
assert os.path.isabs(top)
all_filenames = list(self._file_contents.keys())
pending_prefixes = collections.deque()
pending_prefixes.append(top)
visited_prefixes = set()
while len(pending_prefixes):
prefix = pending_prefixes.popleft()
if prefix in visited_prefixes:
continue
visited_prefixes.add(prefix)
if prefix.endswith(os.path.sep):
prefix_with_trailing_sep = prefix
else:
prefix_with_trailing_sep = prefix + os.path.sep
dirs = set()
files = []
for filename in all_filenames:
if not filename.startswith(prefix_with_trailing_sep):
continue
relative_to_prefix = os.path.relpath(filename, prefix)
dirpart = os.path.dirname(relative_to_prefix)
if len(dirpart) == 0:
files.append(relative_to_prefix)
continue
parts = dirpart.split(os.sep)
if len(parts) == 0:
dirs.add(dirpart)
else:
pending = os.path.join(prefix, parts[0])
dirs.add(parts[0])
pending_prefixes.appendleft(pending)
dirs = sorted(dirs)
yield prefix, dirs, files
def _FakeListDir(self, dirname):
raise NotImplementedError()

View File

@@ -0,0 +1,52 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import unittest
from py_vulcanize import fake_fs
class FakeFSUnittest(unittest.TestCase):
def testBasic(self):
fs = fake_fs.FakeFS()
fs.AddFile('/blah/x', 'foobar')
with fs:
assert os.path.exists(os.path.normpath('/blah/x'))
self.assertEquals(
'foobar',
open(os.path.normpath('/blah/x'), 'r').read())
def testWithableOpen(self):
fs = fake_fs.FakeFS()
fs.AddFile('/blah/x', 'foobar')
with fs:
with open(os.path.normpath('/blah/x'), 'r') as f:
self.assertEquals('foobar', f.read())
def testWalk(self):
fs = fake_fs.FakeFS()
fs.AddFile('/x/w2/w3/z3.txt', '')
fs.AddFile('/x/w/z.txt', '')
fs.AddFile('/x/y.txt', '')
fs.AddFile('/a.txt', 'foobar')
with fs:
gen = os.walk(os.path.normpath('/'))
r = next(gen)
self.assertEquals((os.path.normpath('/'), ['x'], ['a.txt']), r)
r = next(gen)
self.assertEquals((os.path.normpath('/x'), ['w', 'w2'], ['y.txt']), r)
r = next(gen)
self.assertEquals((os.path.normpath('/x/w'), [], ['z.txt']), r)
r = next(gen)
self.assertEquals((os.path.normpath('/x/w2'), ['w3'], []), r)
r = next(gen)
self.assertEquals((os.path.normpath('/x/w2/w3'), [], ['z3.txt']), r)
self.assertRaises(StopIteration, gen.next)

View File

@@ -0,0 +1,279 @@
# Copyright (c) 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import subprocess
import sys
import tempfile
from py_vulcanize import html_generation_controller
try:
from six import StringIO
except ImportError:
from io import StringIO
html_warning_message = """
<!--
WARNING: This file is auto generated.
Do not edit directly.
-->
"""
js_warning_message = """
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/* WARNING: This file is auto generated.
*
* Do not edit directly.
*/
"""
css_warning_message = """
/* Copyright 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
/* WARNING: This file is auto-generated.
*
* Do not edit directly.
*/
"""
def _AssertIsUTF8(f):
if isinstance(f, StringIO):
return
assert f.encoding == 'utf-8'
def _MinifyJS(input_js):
py_vulcanize_path = os.path.abspath(os.path.join(
os.path.dirname(__file__), '..'))
rjsmin_path = os.path.abspath(
os.path.join(py_vulcanize_path, 'third_party', 'rjsmin', 'rjsmin.py'))
with tempfile.NamedTemporaryFile() as _:
args = [
'python',
rjsmin_path
]
p = subprocess.Popen(args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
res = p.communicate(input=input_js)
errorcode = p.wait()
if errorcode != 0:
sys.stderr.write('rJSmin exited with error code %d' % errorcode)
sys.stderr.write(res[1])
raise Exception('Failed to minify, omgah')
return res[0]
def GenerateJS(load_sequence,
use_include_tags_for_scripts=False,
dir_for_include_tag_root=None,
minify=False,
report_sizes=False):
f = StringIO()
GenerateJSToFile(f,
load_sequence,
use_include_tags_for_scripts,
dir_for_include_tag_root,
minify=minify,
report_sizes=report_sizes)
return f.getvalue()
def GenerateJSToFile(f,
load_sequence,
use_include_tags_for_scripts=False,
dir_for_include_tag_root=None,
minify=False,
report_sizes=False):
_AssertIsUTF8(f)
if use_include_tags_for_scripts and dir_for_include_tag_root is None:
raise Exception('Must provide dir_for_include_tag_root')
f.write(js_warning_message)
f.write('\n')
if not minify:
flatten_to_file = f
else:
flatten_to_file = StringIO()
for module in load_sequence:
module.AppendJSContentsToFile(flatten_to_file,
use_include_tags_for_scripts,
dir_for_include_tag_root)
if minify:
js = flatten_to_file.getvalue()
minified_js = _MinifyJS(js)
f.write(minified_js)
f.write('\n')
if report_sizes:
for module in load_sequence:
s = StringIO()
module.AppendJSContentsToFile(s,
use_include_tags_for_scripts,
dir_for_include_tag_root)
# Add minified size info.
js = s.getvalue()
min_js_size = str(len(_MinifyJS(js)))
# Print names for this module. Some domain-specific simplifications
# are included to make pivoting more obvious.
parts = module.name.split('.')
if parts[:2] == ['base', 'ui']:
parts = ['base_ui'] + parts[2:]
if parts[:2] == ['tracing', 'importer']:
parts = ['importer'] + parts[2:]
tln = parts[0]
sln = '.'.join(parts[:2])
# Output
print(('%i\t%s\t%s\t%s\t%s' %
(len(js), min_js_size, module.name, tln, sln)))
sys.stdout.flush()
class ExtraScript(object):
def __init__(self, script_id=None, text_content=None, content_type=None):
if script_id is not None:
assert script_id[0] != '#'
self.script_id = script_id
self.text_content = text_content
self.content_type = content_type
def WriteToFile(self, output_file):
_AssertIsUTF8(output_file)
attrs = []
if self.script_id:
attrs.append('id="%s"' % self.script_id)
if self.content_type:
attrs.append('content-type="%s"' % self.content_type)
if len(attrs) > 0:
output_file.write('<script %s>\n' % ' '.join(attrs))
else:
output_file.write('<script>\n')
if self.text_content:
output_file.write(self.text_content)
output_file.write('</script>\n')
def _MinifyCSS(css_text):
py_vulcanize_path = os.path.abspath(os.path.join(
os.path.dirname(__file__), '..'))
rcssmin_path = os.path.abspath(
os.path.join(py_vulcanize_path, 'third_party', 'rcssmin', 'rcssmin.py'))
with tempfile.NamedTemporaryFile() as _:
rcssmin_args = ['python', rcssmin_path]
p = subprocess.Popen(rcssmin_args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
res = p.communicate(input=css_text)
errorcode = p.wait()
if errorcode != 0:
sys.stderr.write('rCSSmin exited with error code %d' % errorcode)
sys.stderr.write(res[1])
raise Exception('Failed to generate css for %s.' % css_text)
return res[0]
def GenerateStandaloneHTMLAsString(*args, **kwargs):
f = StringIO()
GenerateStandaloneHTMLToFile(f, *args, **kwargs)
return f.getvalue()
def GenerateStandaloneHTMLToFile(output_file,
load_sequence,
title=None,
flattened_js_url=None,
extra_scripts=None,
minify=False,
report_sizes=False,
output_html_head_and_body=True):
"""Writes a HTML file with the content of all modules in a load sequence.
The load_sequence is a list of (HTML or JS) Module objects; the order that
they're inserted into the file depends on their type and position in the load
sequence.
"""
_AssertIsUTF8(output_file)
extra_scripts = extra_scripts or []
if output_html_head_and_body:
output_file.write(
'<!DOCTYPE html>\n'
'<html>\n'
' <head i18n-values="dir:textdirection;">\n'
' <meta http-equiv="Content-Type" content="text/html;'
'charset=utf-8">\n')
if title:
output_file.write(' <title>%s</title>\n ' % title)
else:
assert title is None
loader = load_sequence[0].loader
written_style_sheets = set()
class HTMLGenerationController(
html_generation_controller.HTMLGenerationController):
def __init__(self, module):
self.module = module
def GetHTMLForStylesheetHRef(self, href):
resource = self.module.HRefToResource(
href, '<link rel="stylesheet" href="%s">' % href)
style_sheet = loader.LoadStyleSheet(resource.name)
if style_sheet in written_style_sheets:
return None
written_style_sheets.add(style_sheet)
text = style_sheet.contents_with_inlined_images
if minify:
text = _MinifyCSS(text)
return '<style>\n%s\n</style>' % text
for module in load_sequence:
controller = HTMLGenerationController(module)
module.AppendHTMLContentsToFile(output_file, controller, minify=minify)
if flattened_js_url:
output_file.write('<script src="%s"></script>\n' % flattened_js_url)
else:
output_file.write('<script>\n')
js = GenerateJS(load_sequence, minify=minify, report_sizes=report_sizes)
output_file.write(js)
output_file.write('</script>\n')
for extra_script in extra_scripts:
extra_script.WriteToFile(output_file)
if output_html_head_and_body:
output_file.write('</head>\n <body>\n </body>\n</html>\n')

View File

@@ -0,0 +1,89 @@
# Copyright (c) 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import unittest
from py_vulcanize import generate
from py_vulcanize import fake_fs
from py_vulcanize import project as project_module
class GenerateTests(unittest.TestCase):
def setUp(self):
self.fs = fake_fs.FakeFS()
self.fs.AddFile(
'/x/foo/my_module.html',
('<!DOCTYPE html>\n'
'<link rel="import" href="/foo/other_module.html">\n'))
self.fs.AddFile(
'/x/foo/other_module.html',
('<!DOCTYPE html>\n'
'<script src="/foo/raw/raw_script.js"></script>\n'
'<script>\n'
' \'use strict\';\n'
' HelloWorld();\n'
'</script>\n'))
self.fs.AddFile('/x/foo/raw/raw_script.js', '\n/* raw script */\n')
self.fs.AddFile('/x/components/polymer/polymer.min.js', '\n')
self.fs.AddFile('/x/foo/external_script.js', 'External()')
self.fs.AddFile('/x/foo/inline_and_external_module.html',
('<!DOCTYPE html>\n'
'<script>Script1()</script>'
'<script src=/foo/external_script.js></script>'
'<script>Script2()</script>'))
self.project = project_module.Project([os.path.normpath('/x')])
def testJSGeneration(self):
with self.fs:
load_sequence = self.project.CalcLoadSequenceForModuleNames(
[os.path.normpath('foo.my_module')])
generate.GenerateJS(load_sequence)
def testHTMLGeneration(self):
with self.fs:
load_sequence = self.project.CalcLoadSequenceForModuleNames(
[os.path.normpath('foo.my_module')])
result = generate.GenerateStandaloneHTMLAsString(load_sequence)
self.assertIn('HelloWorld();', result)
def testExtraScriptWithWriteContentsFunc(self):
with self.fs:
load_sequence = self.project.CalcLoadSequenceForModuleNames(
[os.path.normpath('foo.my_module')])
class ExtraScript(generate.ExtraScript):
def WriteToFile(self, f):
f.write('<script>ExtraScript!</script>')
result = generate.GenerateStandaloneHTMLAsString(
load_sequence, title='Title', extra_scripts=[ExtraScript()])
self.assertIn('ExtraScript', result)
def testScriptOrdering(self):
with self.fs:
load_sequence = self.project.CalcLoadSequenceForModuleNames(
[os.path.normpath('foo.inline_and_external_module')])
result = generate.GenerateStandaloneHTMLAsString(load_sequence)
script1_pos = result.index('Script1()')
script2_pos = result.index('Script2()')
external_pos = result.index('External()')
self.assertTrue(script1_pos < external_pos < script2_pos)
def testScriptOrderingWithIncludeTag(self):
with self.fs:
load_sequence = self.project.CalcLoadSequenceForModuleNames(
[os.path.normpath('foo.inline_and_external_module')])
result = generate.GenerateJS(load_sequence,
use_include_tags_for_scripts = True,
dir_for_include_tag_root='/x/')
script1_pos = result.index('Script1()')
script2_pos = result.index('Script2()')
external_path = os.path.join('foo', 'external_script.js')
external_pos = result.index('<include src="{0}">'.format(external_path))
self.assertTrue(script1_pos < external_pos < script2_pos)

View File

@@ -0,0 +1,28 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import re
from py_vulcanize import style_sheet
class HTMLGenerationController(object):
def __init__(self):
self.current_module = None
def GetHTMLForStylesheetHRef(self, href): # pylint: disable=unused-argument
return None
def GetHTMLForInlineStylesheet(self, contents):
if self.current_module is None:
if re.search('url\(.+\)', contents):
raise Exception(
'Default HTMLGenerationController cannot handle inline style urls')
return contents
module_dirname = os.path.dirname(self.current_module.resource.absolute_path)
ss = style_sheet.ParsedStyleSheet(
self.current_module.loader, module_dirname, contents)
return ss.contents_with_inlined_images

View File

@@ -0,0 +1,154 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import re
from py_vulcanize import js_utils
from py_vulcanize import module
from py_vulcanize import parse_html_deps
from py_vulcanize import style_sheet
def IsHTMLResourceTheModuleGivenConflictingResourceNames(
js_resource, html_resource): # pylint: disable=unused-argument
return 'polymer-element' in html_resource.contents
class HTMLModule(module.Module):
@property
def _module_dir_name(self):
return os.path.dirname(self.resource.absolute_path)
def Parse(self, excluded_scripts):
try:
parser_results = parse_html_deps.HTMLModuleParser().Parse(self.contents)
except Exception as ex:
raise Exception('While parsing %s: %s' % (self.name, str(ex)))
self.dependency_metadata = Parse(self.loader,
self.name,
self._module_dir_name,
self.IsThirdPartyComponent(),
parser_results,
excluded_scripts)
self._parser_results = parser_results
self.scripts = parser_results.scripts
def Load(self, excluded_scripts):
super(HTMLModule, self).Load(excluded_scripts=excluded_scripts)
reachable_names = set([m.name
for m in self.all_dependent_modules_recursive])
if 'tr.exportTo' in self.contents:
if 'tracing.base.base' not in reachable_names:
raise Exception('%s: Does not have a dependency on base' %
os.path.relpath(self.resource.absolute_path))
for script in self.scripts:
if script.is_external:
if excluded_scripts and any(re.match(pattern, script.src) for
pattern in excluded_scripts):
continue
resource = _HRefToResource(self.loader, self.name, self._module_dir_name,
script.src,
tag_for_err_msg='<script src="%s">' % script.src)
path = resource.unix_style_relative_path
raw_script = self.loader.LoadRawScript(path)
self.dependent_raw_scripts.append(raw_script)
script.loaded_raw_script = raw_script
def GetTVCMDepsModuleType(self):
return 'py_vulcanize.HTML_MODULE_TYPE'
def AppendHTMLContentsToFile(self, f, ctl, minify=False):
super(HTMLModule, self).AppendHTMLContentsToFile(f, ctl)
ctl.current_module = self
try:
for piece in self._parser_results.YieldHTMLInPieces(ctl, minify=minify):
f.write(piece)
finally:
ctl.current_module = None
def HRefToResource(self, href, tag_for_err_msg):
return _HRefToResource(self.loader, self.name, self._module_dir_name,
href, tag_for_err_msg)
def AppendDirectlyDependentFilenamesTo(
self, dependent_filenames, include_raw_scripts=True):
super(HTMLModule, self).AppendDirectlyDependentFilenamesTo(
dependent_filenames, include_raw_scripts)
for contents in self._parser_results.inline_stylesheets:
module_dirname = os.path.dirname(self.resource.absolute_path)
ss = style_sheet.ParsedStyleSheet(
self.loader, module_dirname, contents)
ss.AppendDirectlyDependentFilenamesTo(dependent_filenames)
def _HRefToResource(
loader, module_name, module_dir_name, href, tag_for_err_msg):
if href[0] == '/':
resource = loader.FindResourceGivenRelativePath(
os.path.normpath(href[1:]))
else:
abspath = os.path.normpath(os.path.join(module_dir_name,
os.path.normpath(href)))
resource = loader.FindResourceGivenAbsolutePath(abspath)
if not resource:
raise module.DepsException(
'In %s, the %s cannot be loaded because '
'it is not in the search path' % (module_name, tag_for_err_msg))
try:
resource.contents
except:
raise module.DepsException('In %s, %s points at a nonexistent file ' % (
module_name, tag_for_err_msg))
return resource
def Parse(loader, module_name, module_dir_name, is_component, parser_results,
exclude_scripts=None):
res = module.ModuleDependencyMetadata()
if is_component:
return res
# External script references.
for href in parser_results.scripts_external:
if exclude_scripts and any(re.match(pattern, href) for
pattern in exclude_scripts):
continue
resource = _HRefToResource(loader, module_name, module_dir_name,
href,
tag_for_err_msg='<script src="%s">' % href)
res.dependent_raw_script_relative_paths.append(
resource.unix_style_relative_path)
# External imports. Mostly the same as <script>, but we know its a module.
for href in parser_results.imports:
if exclude_scripts and any(re.match(pattern, href) for
pattern in exclude_scripts):
continue
if not href.endswith('.html'):
raise Exception(
'In %s, the <link rel="import" href="%s"> must point at a '
'file with an html suffix' % (module_name, href))
resource = _HRefToResource(
loader, module_name, module_dir_name, href,
tag_for_err_msg='<link rel="import" href="%s">' % href)
res.dependent_module_names.append(resource.name)
# Style sheets.
for href in parser_results.stylesheets:
resource = _HRefToResource(
loader, module_name, module_dir_name, href,
tag_for_err_msg='<link rel="stylesheet" href="%s">' % href)
res.style_sheet_names.append(resource.name)
return res

View File

@@ -0,0 +1,325 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import unittest
from py_vulcanize import fake_fs
from py_vulcanize import generate
from py_vulcanize import html_generation_controller
from py_vulcanize import html_module
from py_vulcanize import parse_html_deps
from py_vulcanize import project as project_module
from py_vulcanize import resource
from py_vulcanize import resource_loader as resource_loader
import six
class ResourceWithFakeContents(resource.Resource):
def __init__(self, toplevel_dir, absolute_path, fake_contents):
"""A resource with explicitly provided contents.
If the resource does not exist, then pass fake_contents=None. This will
cause accessing the resource contents to raise an exception mimicking the
behavior of regular resources."""
super(ResourceWithFakeContents, self).__init__(toplevel_dir, absolute_path)
self._fake_contents = fake_contents
@property
def contents(self):
if self._fake_contents is None:
raise Exception('File not found')
return self._fake_contents
class FakeLoader(object):
def __init__(self, source_paths, initial_filenames_and_contents=None):
self._source_paths = source_paths
self._file_contents = {}
if initial_filenames_and_contents:
for k, v in six.iteritems(initial_filenames_and_contents):
self._file_contents[k] = v
def FindResourceGivenAbsolutePath(self, absolute_path):
candidate_paths = []
for source_path in self._source_paths:
if absolute_path.startswith(source_path):
candidate_paths.append(source_path)
if len(candidate_paths) == 0:
return None
# Sort by length. Longest match wins.
candidate_paths.sort(lambda x, y: len(x) - len(y))
longest_candidate = candidate_paths[-1]
return ResourceWithFakeContents(
longest_candidate, absolute_path,
self._file_contents.get(absolute_path, None))
def FindResourceGivenRelativePath(self, relative_path):
absolute_path = None
for script_path in self._source_paths:
absolute_path = os.path.join(script_path, relative_path)
if absolute_path in self._file_contents:
return ResourceWithFakeContents(script_path, absolute_path,
self._file_contents[absolute_path])
return None
class ParseTests(unittest.TestCase):
def testValidExternalScriptReferenceToRawScript(self):
parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html>
<script src="../foo.js">
""")
file_contents = {}
file_contents[os.path.normpath('/tmp/a/foo.js')] = """
'i am just some raw script';
"""
metadata = html_module.Parse(
FakeLoader([os.path.normpath('/tmp')], file_contents),
'a.b.start',
'/tmp/a/b/',
is_component=False,
parser_results=parse_results)
self.assertEquals([], metadata.dependent_module_names)
self.assertEquals(
['a/foo.js'], metadata.dependent_raw_script_relative_paths)
def testExternalScriptReferenceToModuleOutsideScriptPath(self):
parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html>
<script src="/foo.js">
""")
file_contents = {}
file_contents[os.path.normpath('/foo.js')] = ''
def DoIt():
html_module.Parse(FakeLoader([os.path.normpath('/tmp')], file_contents),
'a.b.start',
'/tmp/a/b/',
is_component=False,
parser_results=parse_results)
self.assertRaises(Exception, DoIt)
def testExternalScriptReferenceToFileThatDoesntExist(self):
parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html>
<script src="/foo.js">
""")
file_contents = {}
def DoIt():
html_module.Parse(FakeLoader([os.path.normpath('/tmp')], file_contents),
'a.b.start',
'/tmp/a/b/',
is_component=False,
parser_results=parse_results)
self.assertRaises(Exception, DoIt)
def testValidImportOfModule(self):
parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html>
<link rel="import" href="../foo.html">
""")
file_contents = {}
file_contents[os.path.normpath('/tmp/a/foo.html')] = """
"""
metadata = html_module.Parse(
FakeLoader([os.path.normpath('/tmp')], file_contents),
'a.b.start',
'/tmp/a/b/',
is_component=False,
parser_results=parse_results)
self.assertEquals(['a.foo'], metadata.dependent_module_names)
def testStyleSheetImport(self):
parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html>
<link rel="stylesheet" href="../foo.css">
""")
file_contents = {}
file_contents[os.path.normpath('/tmp/a/foo.css')] = """
"""
metadata = html_module.Parse(
FakeLoader([os.path.normpath('/tmp')], file_contents),
'a.b.start',
'/tmp/a/b/',
is_component=False,
parser_results=parse_results)
self.assertEquals([], metadata.dependent_module_names)
self.assertEquals(['a.foo'], metadata.style_sheet_names)
def testUsingAbsoluteHref(self):
parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html>
<script src="/foo.js">
""")
file_contents = {}
file_contents[os.path.normpath('/src/foo.js')] = ''
metadata = html_module.Parse(
FakeLoader([os.path.normpath("/tmp"), os.path.normpath("/src")],
file_contents),
"a.b.start",
"/tmp/a/b/",
is_component=False,
parser_results=parse_results)
self.assertEquals(['foo.js'], metadata.dependent_raw_script_relative_paths)
class HTMLModuleTests(unittest.TestCase):
def testBasicModuleGeneration(self):
file_contents = {}
file_contents[os.path.normpath('/tmp/a/b/start.html')] = """
<!DOCTYPE html>
<link rel="import" href="/widget.html">
<link rel="stylesheet" href="../common.css">
<script src="/raw_script.js"></script>
<script src="/excluded_script.js"></script>
<dom-module id="start">
<template>
</template>
<script>
'use strict';
console.log('inline script for start.html got written');
</script>
</dom-module>
"""
file_contents[os.path.normpath('/py_vulcanize/py_vulcanize.html')] = """<!DOCTYPE html>
"""
file_contents[os.path.normpath('/components/widget.html')] = """
<!DOCTYPE html>
<link rel="import" href="/py_vulcanize.html">
<widget name="widget.html"></widget>
<script>
'use strict';
console.log('inline script for widget.html');
</script>
"""
file_contents[os.path.normpath('/tmp/a/common.css')] = """
/* /tmp/a/common.css was written */
"""
file_contents[os.path.normpath('/raw/raw_script.js')] = """
console.log('/raw/raw_script.js was written');
"""
file_contents[os.path.normpath(
'/raw/components/polymer/polymer.min.js')] = """
"""
with fake_fs.FakeFS(file_contents):
project = project_module.Project(
[os.path.normpath('/py_vulcanize/'),
os.path.normpath('/tmp/'),
os.path.normpath('/components/'),
os.path.normpath('/raw/')])
loader = resource_loader.ResourceLoader(project)
a_b_start_module = loader.LoadModule(
module_name='a.b.start', excluded_scripts=['\/excluded_script.js'])
load_sequence = project.CalcLoadSequenceForModules([a_b_start_module])
# Check load sequence names.
load_sequence_names = [x.name for x in load_sequence]
self.assertEquals(['py_vulcanize',
'widget',
'a.b.start'], load_sequence_names)
# Check module_deps on a_b_start_module
def HasDependentModule(module, name):
return [x for x in module.dependent_modules
if x.name == name]
assert HasDependentModule(a_b_start_module, 'widget')
# Check JS generation.
js = generate.GenerateJS(load_sequence)
assert 'inline script for start.html' in js
assert 'inline script for widget.html' in js
assert '/raw/raw_script.js' in js
assert 'excluded_script.js' not in js
# Check HTML generation.
html = generate.GenerateStandaloneHTMLAsString(
load_sequence, title='', flattened_js_url='/blah.js')
assert '<dom-module id="start">' in html
assert 'inline script for widget.html' not in html
assert 'common.css' in html
def testPolymerConversion(self):
file_contents = {}
file_contents[os.path.normpath('/tmp/a/b/my_component.html')] = """
<!DOCTYPE html>
<dom-module id="my-component">
<template>
</template>
<script>
'use strict';
Polymer ( {
is: "my-component"
});
</script>
</dom-module>
"""
with fake_fs.FakeFS(file_contents):
project = project_module.Project([
os.path.normpath('/py_vulcanize/'), os.path.normpath('/tmp/')])
loader = resource_loader.ResourceLoader(project)
my_component = loader.LoadModule(module_name='a.b.my_component')
f = six.StringIO()
my_component.AppendJSContentsToFile(
f,
use_include_tags_for_scripts=False,
dir_for_include_tag_root=None)
js = f.getvalue().rstrip()
expected_js = """
'use strict';
Polymer ( {
is: "my-component"
});
""".rstrip()
self.assertEquals(expected_js, js)
def testInlineStylesheetURLs(self):
file_contents = {}
file_contents[os.path.normpath('/tmp/a/b/my_component.html')] = """
<!DOCTYPE html>
<style>
.some-rule {
background-image: url('../something.jpg');
}
</style>
"""
file_contents[os.path.normpath('/tmp/a/something.jpg')] = 'jpgdata'
with fake_fs.FakeFS(file_contents):
project = project_module.Project([
os.path.normpath('/py_vulcanize/'), os.path.normpath('/tmp/')])
loader = resource_loader.ResourceLoader(project)
my_component = loader.LoadModule(module_name='a.b.my_component')
computed_deps = []
my_component.AppendDirectlyDependentFilenamesTo(computed_deps)
self.assertEquals(set(computed_deps),
set([os.path.normpath('/tmp/a/b/my_component.html'),
os.path.normpath('/tmp/a/something.jpg')]))
f = six.StringIO()
ctl = html_generation_controller.HTMLGenerationController()
my_component.AppendHTMLContentsToFile(f, ctl)
html = f.getvalue().rstrip()
# FIXME: This is apparently not used.
expected_html = """
.some-rule {
background-image: url(data:image/jpg;base64,anBnZGF0YQ==);
}
""".rstrip()

View File

@@ -0,0 +1,7 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
def EscapeJSIfNeeded(js):
return js.replace('</script>', '<\/script>')

View File

@@ -0,0 +1,18 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import unittest
from py_vulcanize import js_utils
class ValidateStrictModeTests(unittest.TestCase):
def testEscapeJSIfNeeded(self):
self.assertEqual(
'<script>var foo;<\/script>',
js_utils.EscapeJSIfNeeded('<script>var foo;</script>'))
self.assertEqual(
'<script>var foo;<\/script>',
js_utils.EscapeJSIfNeeded('<script>var foo;<\/script>'))

View File

@@ -0,0 +1,267 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This module contains the Module class and other classes for resources.
The Module class represents a module in the trace viewer system. A module has
a name, and may require a variety of other resources, such as stylesheets,
template objects, raw JavaScript, or other modules.
Other resources include HTML templates, raw JavaScript files, and stylesheets.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import codecs
import inspect
import os
from py_vulcanize import js_utils
import six
class DepsException(Exception):
"""Exceptions related to module dependency resolution."""
def __init__(self, fmt, *args):
from py_vulcanize import style_sheet as style_sheet_module
context = []
frame = inspect.currentframe()
while frame:
frame_locals = frame.f_locals
module_name = None
if 'self' in frame_locals:
s = frame_locals['self']
if isinstance(s, Module):
module_name = s.name
if isinstance(s, style_sheet_module.StyleSheet):
module_name = s.name + '.css'
if not module_name:
if 'module' in frame_locals:
module = frame_locals['module']
if isinstance(s, Module):
module_name = module.name
elif 'm' in frame_locals:
module = frame_locals['m']
if isinstance(s, Module):
module_name = module.name
if module_name:
if len(context):
if context[-1] != module_name:
context.append(module_name)
else:
context.append(module_name)
frame = frame.f_back
context.reverse()
self.context = context
context_str = '\n'.join(' %s' % x for x in context)
Exception.__init__(
self, 'While loading:\n%s\nGot: %s' % (context_str, (fmt % args)))
class ModuleDependencyMetadata(object):
def __init__(self):
self.dependent_module_names = []
self.dependent_raw_script_relative_paths = []
self.style_sheet_names = []
def AppendMetdata(self, other):
self.dependent_module_names += other.dependent_module_names
self.dependent_raw_script_relative_paths += \
other.dependent_raw_script_relative_paths
self.style_sheet_names += other.style_sheet_names
_next_module_id = 1
class Module(object):
"""Represents a JavaScript module.
Interesting properties include:
name: Module name, may include a namespace, e.g. 'py_vulcanize.foo'.
filename: The filename of the actual module.
contents: The text contents of the module.
dependent_modules: Other modules that this module depends on.
In addition to these properties, a Module also contains lists of other
resources that it depends on.
"""
def __init__(self, loader, name, resource, load_resource=True):
assert isinstance(name, six.string_types), 'Got %s instead' % repr(name)
global _next_module_id
self._id = _next_module_id
_next_module_id += 1
self.loader = loader
self.name = name
self.resource = resource
if load_resource:
f = codecs.open(self.filename, mode='r', encoding='utf-8')
self.contents = f.read()
f.close()
else:
self.contents = None
# Dependency metadata, set up during Parse().
self.dependency_metadata = None
# Actual dependencies, set up during load().
self.dependent_modules = []
self.dependent_raw_scripts = []
self.scripts = []
self.style_sheets = []
# Caches.
self._all_dependent_modules_recursive = None
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self.name)
@property
def id(self):
return self._id
@property
def filename(self):
return self.resource.absolute_path
def IsThirdPartyComponent(self):
"""Checks whether this module is a third-party Polymer component."""
if os.path.join('third_party', 'components') in self.filename:
return True
if os.path.join('third_party', 'polymer', 'components') in self.filename:
return True
return False
def Parse(self, excluded_scripts):
"""Parses self.contents and fills in the module's dependency metadata."""
raise NotImplementedError()
def GetTVCMDepsModuleType(self):
"""Returns the py_vulcanize.setModuleInfo type for this module"""
raise NotImplementedError()
def AppendJSContentsToFile(self,
f,
use_include_tags_for_scripts,
dir_for_include_tag_root):
"""Appends the js for this module to the provided file."""
for script in self.scripts:
script.AppendJSContentsToFile(f, use_include_tags_for_scripts,
dir_for_include_tag_root)
def AppendHTMLContentsToFile(self, f, ctl, minify=False):
"""Appends the HTML for this module [without links] to the provided file."""
pass
def Load(self, excluded_scripts=None):
"""Loads the sub-resources that this module depends on from its dependency
metadata.
Raises:
DepsException: There was a problem finding one of the dependencies.
Exception: There was a problem parsing a module that this one depends on.
"""
assert self.name, 'Module name must be set before dep resolution.'
assert self.filename, 'Module filename must be set before dep resolution.'
assert self.name in self.loader.loaded_modules, (
'Module must be registered in resource loader before loading.')
metadata = self.dependency_metadata
for name in metadata.dependent_module_names:
module = self.loader.LoadModule(module_name=name,
excluded_scripts=excluded_scripts)
self.dependent_modules.append(module)
for name in metadata.style_sheet_names:
style_sheet = self.loader.LoadStyleSheet(name)
self.style_sheets.append(style_sheet)
@property
def all_dependent_modules_recursive(self):
if self._all_dependent_modules_recursive:
return self._all_dependent_modules_recursive
self._all_dependent_modules_recursive = set(self.dependent_modules)
for dependent_module in self.dependent_modules:
self._all_dependent_modules_recursive.update(
dependent_module.all_dependent_modules_recursive)
return self._all_dependent_modules_recursive
def ComputeLoadSequenceRecursive(self, load_sequence, already_loaded_set,
depth=0):
"""Recursively builds up a load sequence list.
Args:
load_sequence: A list which will be incrementally built up.
already_loaded_set: A set of modules that has already been added to the
load sequence list.
depth: The depth of recursion. If it too deep, that indicates a loop.
"""
if depth > 32:
raise Exception('Include loop detected on %s', self.name)
for dependent_module in self.dependent_modules:
if dependent_module.name in already_loaded_set:
continue
dependent_module.ComputeLoadSequenceRecursive(
load_sequence, already_loaded_set, depth + 1)
if self.name not in already_loaded_set:
already_loaded_set.add(self.name)
load_sequence.append(self)
def GetAllDependentFilenamesRecursive(self, include_raw_scripts=True):
dependent_filenames = []
visited_modules = set()
def Get(module):
module.AppendDirectlyDependentFilenamesTo(
dependent_filenames, include_raw_scripts)
visited_modules.add(module)
for m in module.dependent_modules:
if m in visited_modules:
continue
Get(m)
Get(self)
return dependent_filenames
def AppendDirectlyDependentFilenamesTo(
self, dependent_filenames, include_raw_scripts=True):
dependent_filenames.append(self.resource.absolute_path)
if include_raw_scripts:
for raw_script in self.dependent_raw_scripts:
dependent_filenames.append(raw_script.resource.absolute_path)
for style_sheet in self.style_sheets:
style_sheet.AppendDirectlyDependentFilenamesTo(dependent_filenames)
class RawScript(object):
"""Represents a raw script resource referenced by a module via the
py_vulcanize.requireRawScript(xxx) directive."""
def __init__(self, resource):
self.resource = resource
@property
def filename(self):
return self.resource.absolute_path
@property
def contents(self):
return self.resource.contents
def __repr__(self):
return 'RawScript(%s)' % self.filename

View File

@@ -0,0 +1,130 @@
#!/usr/bin/env python
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for the module module, which contains Module and related classes."""
import os
import unittest
from py_vulcanize import fake_fs
from py_vulcanize import module
from py_vulcanize import resource_loader
from py_vulcanize import project as project_module
class ModuleIntegrationTests(unittest.TestCase):
def test_module(self):
fs = fake_fs.FakeFS()
fs.AddFile('/src/x.html', """
<!DOCTYPE html>
<link rel="import" href="/y.html">
<link rel="import" href="/z.html">
<script>
'use strict';
</script>
""")
fs.AddFile('/src/y.html', """
<!DOCTYPE html>
<link rel="import" href="/z.html">
""")
fs.AddFile('/src/z.html', """
<!DOCTYPE html>
""")
fs.AddFile('/src/py_vulcanize.html', '<!DOCTYPE html>')
with fs:
project = project_module.Project([os.path.normpath('/src/')])
loader = resource_loader.ResourceLoader(project)
x_module = loader.LoadModule('x')
self.assertEquals([loader.loaded_modules['y'],
loader.loaded_modules['z']],
x_module.dependent_modules)
already_loaded_set = set()
load_sequence = []
x_module.ComputeLoadSequenceRecursive(load_sequence, already_loaded_set)
self.assertEquals([loader.loaded_modules['z'],
loader.loaded_modules['y'],
x_module],
load_sequence)
def testBasic(self):
fs = fake_fs.FakeFS()
fs.AddFile('/x/src/my_module.html', """
<!DOCTYPE html>
<link rel="import" href="/py_vulcanize/foo.html">
});
""")
fs.AddFile('/x/py_vulcanize/foo.html', """
<!DOCTYPE html>
});
""")
project = project_module.Project([os.path.normpath('/x')])
loader = resource_loader.ResourceLoader(project)
with fs:
my_module = loader.LoadModule(module_name='src.my_module')
dep_names = [x.name for x in my_module.dependent_modules]
self.assertEquals(['py_vulcanize.foo'], dep_names)
def testDepsExceptionContext(self):
fs = fake_fs.FakeFS()
fs.AddFile('/x/src/my_module.html', """
<!DOCTYPE html>
<link rel="import" href="/py_vulcanize/foo.html">
""")
fs.AddFile('/x/py_vulcanize/foo.html', """
<!DOCTYPE html>
<link rel="import" href="missing.html">
""")
project = project_module.Project([os.path.normpath('/x')])
loader = resource_loader.ResourceLoader(project)
with fs:
exc = None
try:
loader.LoadModule(module_name='src.my_module')
assert False, 'Expected an exception'
except module.DepsException as e:
exc = e
self.assertEquals(
['src.my_module', 'py_vulcanize.foo'],
exc.context)
def testGetAllDependentFilenamesRecursive(self):
fs = fake_fs.FakeFS()
fs.AddFile('/x/y/z/foo.html', """
<!DOCTYPE html>
<link rel="import" href="/z/foo2.html">
<link rel="stylesheet" href="/z/foo.css">
<script src="/bar.js"></script>
""")
fs.AddFile('/x/y/z/foo.css', """
.x .y {
background-image: url(foo.jpeg);
}
""")
fs.AddFile('/x/y/z/foo.jpeg', '')
fs.AddFile('/x/y/z/foo2.html', """
<!DOCTYPE html>
""")
fs.AddFile('/x/raw/bar.js', 'hello')
project = project_module.Project([
os.path.normpath('/x/y'), os.path.normpath('/x/raw/')])
loader = resource_loader.ResourceLoader(project)
with fs:
my_module = loader.LoadModule(module_name='z.foo')
self.assertEquals(1, len(my_module.dependent_raw_scripts))
dependent_filenames = my_module.GetAllDependentFilenamesRecursive()
self.assertEquals(
[
os.path.normpath('/x/y/z/foo.html'),
os.path.normpath('/x/raw/bar.js'),
os.path.normpath('/x/y/z/foo.css'),
os.path.normpath('/x/y/z/foo.jpeg'),
os.path.normpath('/x/y/z/foo2.html'),
],
dependent_filenames)

View File

@@ -0,0 +1,288 @@
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import sys
from py_vulcanize import html_generation_controller
from py_vulcanize import js_utils
from py_vulcanize import module
from py_vulcanize import strip_js_comments
import six
def _AddToPathIfNeeded(path):
if path not in sys.path:
sys.path.insert(0, path)
def _InitBeautifulSoup():
catapult_path = os.path.abspath(
os.path.join(os.path.dirname(__file__),
os.path.pardir, os.path.pardir, os.path.pardir))
bs_path = os.path.join(catapult_path, 'third_party', 'beautifulsoup4')
_AddToPathIfNeeded(bs_path)
html5lib_path = os.path.join(catapult_path, 'third_party', 'html5lib-python')
_AddToPathIfNeeded(html5lib_path)
six_path = os.path.join(catapult_path, 'third_party', 'six')
_AddToPathIfNeeded(six_path)
_InitBeautifulSoup()
import bs4
class Script(object):
def __init__(self, soup):
if not soup:
raise module.DepsException('Script object created without soup')
self._soup = soup
def AppendJSContentsToFile(self, f, *args, **kwargs):
raise NotImplementedError()
class InlineScript(Script):
def __init__(self, soup):
super(InlineScript, self).__init__(soup)
self._stripped_contents = None
self._open_tags = None
self.is_external = False
@property
def contents(self):
return six.text_type(self._soup.string)
@property
def stripped_contents(self):
if not self._stripped_contents:
self._stripped_contents = strip_js_comments.StripJSComments(
self.contents)
return self._stripped_contents
@property
def open_tags(self):
if self._open_tags:
return self._open_tags
open_tags = []
cur = self._soup.parent
while cur:
if isinstance(cur, bs4.BeautifulSoup):
break
open_tags.append(_Tag(cur.name, cur.attrs))
cur = cur.parent
open_tags.reverse()
assert open_tags[-1].tag == 'script'
del open_tags[-1]
self._open_tags = open_tags
return self._open_tags
def AppendJSContentsToFile(self, f, *args, **kwargs):
js = self.contents
escaped_js = js_utils.EscapeJSIfNeeded(js)
f.write(escaped_js)
f.write('\n')
class ExternalScript(Script):
def __init__(self, soup):
super(ExternalScript, self).__init__(soup)
if 'src' not in soup.attrs:
raise Exception("{0} is not an external script.".format(soup))
self.is_external = True
self._loaded_raw_script = None
@property
def loaded_raw_script(self):
if self._loaded_raw_script:
return self._loaded_raw_script
return None
@loaded_raw_script.setter
def loaded_raw_script(self, value):
self._loaded_raw_script = value
@property
def src(self):
return self._soup.attrs['src']
def AppendJSContentsToFile(self,
f,
use_include_tags_for_scripts,
dir_for_include_tag_root):
raw_script = self.loaded_raw_script
if not raw_script:
return
if use_include_tags_for_scripts:
rel_filename = os.path.relpath(raw_script.filename,
dir_for_include_tag_root)
f.write("""<include src="%s">\n""" % rel_filename)
else:
f.write(js_utils.EscapeJSIfNeeded(raw_script.contents))
f.write('\n')
def _CreateSoupWithoutHeadOrBody(html):
soupCopy = bs4.BeautifulSoup(html, 'html5lib')
soup = bs4.BeautifulSoup()
soup.reset()
if soupCopy.head:
for n in soupCopy.head.contents:
n.extract()
soup.append(n)
if soupCopy.body:
for n in soupCopy.body.contents:
n.extract()
soup.append(n)
return soup
class HTMLModuleParserResults(object):
def __init__(self, html):
self._soup = bs4.BeautifulSoup(html, 'html5lib')
self._inline_scripts = None
self._scripts = None
@property
def scripts_external(self):
tags = self._soup.findAll('script', src=True)
return [t['src'] for t in tags]
@property
def inline_scripts(self):
if not self._inline_scripts:
tags = self._soup.findAll('script', src=None)
self._inline_scripts = [InlineScript(t.string) for t in tags]
return self._inline_scripts
@property
def scripts(self):
if not self._scripts:
self._scripts = []
script_elements = self._soup.findAll('script')
for element in script_elements:
if 'src' in element.attrs:
self._scripts.append(ExternalScript(element))
else:
self._scripts.append(InlineScript(element))
return self._scripts
@property
def imports(self):
tags = self._soup.findAll('link', rel='import')
return [t['href'] for t in tags]
@property
def stylesheets(self):
tags = self._soup.findAll('link', rel='stylesheet')
return [t['href'] for t in tags]
@property
def inline_stylesheets(self):
tags = self._soup.findAll('style')
return [six.text_type(t.string) for t in tags]
def YieldHTMLInPieces(self, controller, minify=False):
yield self.GenerateHTML(controller, minify)
def GenerateHTML(self, controller, minify=False, prettify=False):
soup = _CreateSoupWithoutHeadOrBody(six.text_type(self._soup))
# Remove declaration.
for x in soup.contents:
if isinstance(x, bs4.Doctype):
x.extract()
# Remove declaration.
for x in soup.contents:
if isinstance(x, bs4.Declaration):
x.extract()
# Remove all imports.
imports = soup.findAll('link', rel='import')
for imp in imports:
imp.extract()
# Remove all script links.
scripts_external = soup.findAll('script', src=True)
for script in scripts_external:
script.extract()
# Remove all in-line scripts.
scripts_external = soup.findAll('script', src=None)
for script in scripts_external:
script.extract()
# Process all in-line styles.
inline_styles = soup.findAll('style')
for style in inline_styles:
html = controller.GetHTMLForInlineStylesheet(six.text_type(style.string))
if html:
ns = soup.new_tag('style')
ns.append(bs4.NavigableString(html))
style.replaceWith(ns)
else:
style.extract()
# Rewrite all external stylesheet hrefs or remove, as needed.
stylesheet_links = soup.findAll('link', rel='stylesheet')
for stylesheet_link in stylesheet_links:
html = controller.GetHTMLForStylesheetHRef(stylesheet_link['href'])
if html:
tmp = bs4.BeautifulSoup(html, 'html5lib').findAll('style')
assert len(tmp) == 1
stylesheet_link.replaceWith(tmp[0])
else:
stylesheet_link.extract()
# Remove comments if minifying.
if minify:
comments = soup.findAll(
text=lambda text: isinstance(text, bs4.Comment))
for comment in comments:
comment.extract()
if prettify:
return soup.prettify('utf-8').strip()
# We are done.
return six.text_type(soup).strip()
@property
def html_contents_without_links_and_script(self):
return self.GenerateHTML(
html_generation_controller.HTMLGenerationController())
class _Tag(object):
def __init__(self, tag, attrs):
self.tag = tag
self.attrs = attrs
def __repr__(self):
attr_string = ' '.join('%s="%s"' % (x[0], x[1]) for x in self.attrs)
return '<%s %s>' % (self.tag, attr_string)
class HTMLModuleParser():
def Parse(self, html):
if html is None:
html = ''
else:
if html.find('< /script>') != -1:
raise Exception('Escape script tags with <\/script>')
return HTMLModuleParserResults(html)

View File

@@ -0,0 +1,292 @@
#!/usr/bin/env python
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import re
import unittest
from py_vulcanize import parse_html_deps
from py_vulcanize import html_generation_controller
class ParseTests(unittest.TestCase):
def test_parse_empty(self):
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse('')
self.assertEquals([], module.scripts_external)
self.assertEquals([], module.inline_scripts)
self.assertEquals([], module.stylesheets)
self.assertEquals([], module.imports)
def test_parse_none(self):
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(None)
self.assertEquals([], module.scripts_external)
self.assertEquals([], module.inline_scripts)
self.assertEquals([], module.stylesheets)
self.assertEquals([], module.imports)
def test_parse_script_src_basic(self):
html = """<!DOCTYPE html>
<html>
<head>
<script src="polymer.min.js"></script>
<script src="foo.js"></script>
</head>
<body>
</body>
</html>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals(['polymer.min.js', 'foo.js'], module.scripts_external)
self.assertEquals([], module.inline_scripts)
self.assertEquals([], module.stylesheets)
self.assertEquals([], module.imports)
self.assertNotIn(
'DOCTYPE html',
module.html_contents_without_links_and_script)
def test_parse_link_rel_import(self):
html = """<!DOCTYPE html>
<html>
<head>
<link rel="import" href="x-foo.html">
</head>
<body>
</body>
</html>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals([], module.scripts_external)
self.assertEquals([], module.inline_scripts)
self.assertEquals([], module.stylesheets)
self.assertEquals(['x-foo.html'], module.imports)
def test_parse_script_inline(self):
html = """<polymer-element name="tk-element-proto">
<template>
</template>
<script>
py_vulcanize.require("foo");
py_vulcanize.require('bar');
</script>
</polymer-element>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals([], module.scripts_external)
self.assertEquals(1, len(module.inline_scripts))
self.assertEquals([], module.stylesheets)
self.assertEquals([], module.imports)
script0 = module.inline_scripts[0]
val = re.sub(r'\s+', '', script0.contents)
inner_script = """py_vulcanize.require("foo");py_vulcanize.require('bar');"""
self.assertEquals(inner_script, val)
self.assertEquals(3, len(script0.open_tags))
self.assertEquals('polymer-element', script0.open_tags[2].tag)
self.assertNotIn(
'py_vulcanize.require("foo");',
module.html_contents_without_links_and_script)
def test_parse_script_inline_and_external(self):
html = """<polymer-element name="tk-element-proto">
<template>
</template>
<script>window = {}</script>
<script src="foo.js"></script>
<script>window = undefined</script>
</polymer-element>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals(3, len(module.scripts))
self.assertEquals('window = {}', module.scripts[0].contents)
self.assertEquals("foo.js",module.scripts[1].src)
self.assertTrue(module.scripts[1].is_external)
self.assertEquals('window = undefined', module.scripts[2].contents)
self.assertEquals([], module.imports)
def test_parse_script_src_sripping(self):
html = """
<script src="blah.js"></script>
"""
module = parse_html_deps.HTMLModuleParser().Parse(html)
self.assertEquals('',
module.html_contents_without_links_and_script)
def test_parse_link_rel_stylesheet(self):
html = """<polymer-element name="hi">
<template>
<link rel="stylesheet" href="frameworkstyles.css">
</template>
</polymer-element>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals([], module.scripts_external)
self.assertEquals([], module.inline_scripts)
self.assertEquals(['frameworkstyles.css'], module.stylesheets)
self.assertEquals([], module.imports)
class Ctl(html_generation_controller.HTMLGenerationController):
def GetHTMLForStylesheetHRef(self, href):
if href == 'frameworkstyles.css':
return '<style>FRAMEWORK</style>'
return None
gen_html = module.GenerateHTML(Ctl())
ghtm = """<polymer-element name="hi">
<template>
<style>FRAMEWORK</style>
</template>
</polymer-element>"""
self.assertEquals(ghtm, gen_html)
def test_parse_inline_style(self):
html = """<style>
hello
</style>"""
module = parse_html_deps.HTMLModuleParser().Parse(html)
self.assertEquals(html, module.html_contents_without_links_and_script)
class Ctl(html_generation_controller.HTMLGenerationController):
def GetHTMLForInlineStylesheet(self, contents):
if contents == '\n hello\n':
return '\n HELLO\n'
return None
gen_html = module.GenerateHTML(Ctl())
ghtm = """<style>
HELLO
</style>"""
self.assertEquals(ghtm, gen_html)
def test_parse_style_import(self):
html = """<polymer-element name="x-blink">
<template>
<style>
@import url(awesome.css);
</style>
</template>
</polymer-element>"""
parser = parse_html_deps.HTMLModuleParser()
self.assertRaises(lambda: parser.Parse(html))
def test_nested_templates(self):
orig_html = """<template>
<template>
<div id="foo"></div>
</template>
</template>"""
parser = parse_html_deps.HTMLModuleParser()
res = parser.Parse(orig_html)
html = res.html_contents_without_links_and_script
self.assertEquals(html, orig_html)
def test_html_contents_basic(self):
html = """<a b="c">d</a>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals(html, module.html_contents_without_links_and_script)
def test_html_contents_with_entity(self):
html = """<a>&rarr;</a>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals(u'<a>\u2192</a>',
module.html_contents_without_links_and_script)
def test_html_content_with_charref(self):
html = """<a>&#62;</a>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals('<a>&gt;</a>',
module.html_contents_without_links_and_script)
def test_html_content_start_end_br(self):
html = """<a><br /></a>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals('<a><br/></a>',
module.html_contents_without_links_and_script)
def test_html_content_start_end_img(self):
html = """<a><img src="foo.png" id="bar" /></a>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals('<a><img id="bar" src="foo.png"/></a>',
module.html_contents_without_links_and_script)
def test_html_contents_with_link_stripping(self):
html = """<a b="c">d</a>
<link rel="import" href="x-foo.html">"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals("""<a b="c">d</a>""",
module.html_contents_without_links_and_script.strip())
def test_html_contents_with_style_link_stripping(self):
html = """<a b="c">d</a>
<link rel="stylesheet" href="frameworkstyles.css">"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals("""<a b="c">d</a>""",
module.html_contents_without_links_and_script.strip())
def test_br_does_not_raise(self):
html = """<div><br/></div>"""
parser = parse_html_deps.HTMLModuleParser()
parser.Parse(html)
def test_p_does_not_raises(self):
html = """<div></p></div>"""
parser = parse_html_deps.HTMLModuleParser()
parser.Parse(html)
def test_link_endlink_does_not_raise(self):
html = """<link rel="stylesheet" href="foo.css"></link>"""
parser = parse_html_deps.HTMLModuleParser()
parser.Parse(html)
def test_link_script_does_not_raise(self):
html = """<link rel="stylesheet" href="foo.css">
<script>
</script>"""
parser = parse_html_deps.HTMLModuleParser()
parser.Parse(html)
def test_script_with_script_inside_as_js(self):
html = """<script>
var html_lines = [
'<script>',
'<\/script>',
];
</script>"""
parser = parse_html_deps.HTMLModuleParser()
parser.Parse(html)
def test_invalid_script_escaping_raises(self):
html = """<script>
var html_lines = [
'<script>',
'< /script>',
];
</script>"""
parser = parse_html_deps.HTMLModuleParser()
def DoIt():
parser.Parse(html)
self.assertRaises(Exception, DoIt)
def test_script_with_cdata(self):
html = """<script></h2></script>"""
parser = parse_html_deps.HTMLModuleParser()
module = parser.Parse(html)
self.assertEquals(1, len(module.inline_scripts))
self.assertEquals('</h2>', module.inline_scripts[0].contents)

View File

@@ -0,0 +1,239 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import collections
import os
try:
from six import StringIO
except ImportError:
from io import StringIO
from py_vulcanize import resource_loader
import six
def _FindAllFilesRecursive(source_paths):
all_filenames = set()
for source_path in source_paths:
for dirpath, _, filenames in os.walk(source_path):
for f in filenames:
if f.startswith('.'):
continue
x = os.path.abspath(os.path.join(dirpath, f))
all_filenames.add(x)
return all_filenames
class AbsFilenameList(object):
def __init__(self, willDirtyCallback):
self._willDirtyCallback = willDirtyCallback
self._filenames = []
self._filenames_set = set()
def _WillBecomeDirty(self):
if self._willDirtyCallback:
self._willDirtyCallback()
def append(self, filename):
assert os.path.isabs(filename)
self._WillBecomeDirty()
self._filenames.append(filename)
self._filenames_set.add(filename)
def extend(self, iterable):
self._WillBecomeDirty()
for filename in iterable:
assert os.path.isabs(filename)
self._filenames.append(filename)
self._filenames_set.add(filename)
def appendRel(self, basedir, filename):
assert os.path.isabs(basedir)
self._WillBecomeDirty()
n = os.path.abspath(os.path.join(basedir, filename))
self._filenames.append(n)
self._filenames_set.add(n)
def extendRel(self, basedir, iterable):
self._WillBecomeDirty()
assert os.path.isabs(basedir)
for filename in iterable:
n = os.path.abspath(os.path.join(basedir, filename))
self._filenames.append(n)
self._filenames_set.add(n)
def __contains__(self, x):
return x in self._filenames_set
def __len__(self):
return self._filenames.__len__()
def __iter__(self):
return iter(self._filenames)
def __repr__(self):
return repr(self._filenames)
def __str__(self):
return str(self._filenames)
class Project(object):
py_vulcanize_path = os.path.abspath(os.path.join(
os.path.dirname(__file__), '..'))
def __init__(self, source_paths=None):
"""
source_paths: A list of top-level directories in which modules and raw
scripts can be found. Module paths are relative to these directories.
"""
self._loader = None
self._frozen = False
self.source_paths = AbsFilenameList(self._WillPartOfPathChange)
if source_paths is not None:
self.source_paths.extend(source_paths)
def Freeze(self):
self._frozen = True
def _WillPartOfPathChange(self):
if self._frozen:
raise Exception('The project is frozen. You cannot edit it now')
self._loader = None
@staticmethod
def FromDict(d):
return Project(d['source_paths'])
def AsDict(self):
return {
'source_paths': list(self.source_paths)
}
def __repr__(self):
return "Project(%s)" % repr(self.source_paths)
def AddSourcePath(self, path):
self.source_paths.append(path)
@property
def loader(self):
if self._loader is None:
self._loader = resource_loader.ResourceLoader(self)
return self._loader
def ResetLoader(self):
self._loader = None
def _Load(self, filenames):
return [self.loader.LoadModule(module_filename=filename) for
filename in filenames]
def LoadModule(self, module_name=None, module_filename=None):
return self.loader.LoadModule(module_name=module_name,
module_filename=module_filename)
def CalcLoadSequenceForModuleNames(self, module_names,
excluded_scripts=None):
modules = [self.loader.LoadModule(module_name=name,
excluded_scripts=excluded_scripts) for
name in module_names]
return self.CalcLoadSequenceForModules(modules)
def CalcLoadSequenceForModules(self, modules):
already_loaded_set = set()
load_sequence = []
for m in modules:
m.ComputeLoadSequenceRecursive(load_sequence, already_loaded_set)
return load_sequence
def GetDepsGraphFromModuleNames(self, module_names):
modules = [self.loader.LoadModule(module_name=name) for
name in module_names]
return self.GetDepsGraphFromModules(modules)
def GetDepsGraphFromModules(self, modules):
load_sequence = self.CalcLoadSequenceForModules(modules)
g = _Graph()
for m in load_sequence:
g.AddModule(m)
for dep in m.dependent_modules:
g.AddEdge(m, dep.id)
# FIXME: _GetGraph is not defined. Maybe `return g` is intended?
return _GetGraph(load_sequence)
def GetDominatorGraphForModulesNamed(self, module_names, load_sequence):
modules = [self.loader.LoadModule(module_name=name)
for name in module_names]
return self.GetDominatorGraphForModules(modules, load_sequence)
def GetDominatorGraphForModules(self, start_modules, load_sequence):
modules_by_id = {}
for m in load_sequence:
modules_by_id[m.id] = m
module_referrers = collections.defaultdict(list)
for m in load_sequence:
for dep in m.dependent_modules:
module_referrers[dep].append(m)
# Now start at the top module and reverse.
visited = set()
g = _Graph()
pending = collections.deque()
pending.extend(start_modules)
while len(pending):
cur = pending.pop()
g.AddModule(cur)
visited.add(cur)
for out_dep in module_referrers[cur]:
if out_dep in visited:
continue
g.AddEdge(out_dep, cur)
visited.add(out_dep)
pending.append(out_dep)
# Visited -> Dot
return g.GetDot()
class _Graph(object):
def __init__(self):
self.nodes = []
self.edges = []
def AddModule(self, m):
f = StringIO()
m.AppendJSContentsToFile(f, False, None)
attrs = {
'label': '%s (%i)' % (m.name, f.tell())
}
f.close()
attr_items = ['%s="%s"' % (x, y) for x, y in six.iteritems(attrs)]
node = 'M%i [%s];' % (m.id, ','.join(attr_items))
self.nodes.append(node)
def AddEdge(self, mFrom, mTo):
edge = 'M%i -> M%i;' % (mFrom.id, mTo.id)
self.edges.append(edge)
def GetDot(self):
return 'digraph deps {\n\n%s\n\n%s\n}\n' % (
'\n'.join(self.nodes), '\n'.join(self.edges))

View File

@@ -0,0 +1,57 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A Resource is a file and its various associated canonical names."""
import codecs
import os
class Resource(object):
"""Represents a file found via a path search."""
def __init__(self, toplevel_dir, absolute_path, binary=False):
self.toplevel_dir = toplevel_dir
self.absolute_path = absolute_path
self._contents = None
self._binary = binary
@property
def relative_path(self):
"""The path to the file from the top-level directory"""
return os.path.relpath(self.absolute_path, self.toplevel_dir)
@property
def unix_style_relative_path(self):
return self.relative_path.replace(os.sep, '/')
@property
def name(self):
"""The dotted name for this resource based on its relative path."""
return self.name_from_relative_path(self.relative_path)
@staticmethod
def name_from_relative_path(relative_path):
dirname = os.path.dirname(relative_path)
basename = os.path.basename(relative_path)
modname = os.path.splitext(basename)[0]
if len(dirname):
name = dirname.replace(os.path.sep, '.') + '.' + modname
else:
name = modname
return name
@property
def contents(self):
if self._contents:
return self._contents
if not os.path.exists(self.absolute_path):
raise Exception('%s not found.' % self.absolute_path)
if self._binary:
f = open(self.absolute_path, mode='rb')
else:
f = codecs.open(self.absolute_path, mode='r', encoding='utf-8')
self._contents = f.read()
f.close()
return self._contents

View File

@@ -0,0 +1,228 @@
# Copyright (c) 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""ResourceFinder is a helper class for finding resources given their name."""
import codecs
import os
from py_vulcanize import module
from py_vulcanize import style_sheet as style_sheet_module
from py_vulcanize import resource as resource_module
from py_vulcanize import html_module
from py_vulcanize import strip_js_comments
class ResourceLoader(object):
"""Manges loading modules and their dependencies from files.
Modules handle parsing and the construction of their individual dependency
pointers. The loader deals with bookkeeping of what has been loaded, and
mapping names to file resources.
"""
def __init__(self, project):
self.project = project
self.stripped_js_by_filename = {}
self.loaded_modules = {}
self.loaded_raw_scripts = {}
self.loaded_style_sheets = {}
self.loaded_images = {}
@property
def source_paths(self):
"""A list of base directories to search for modules under."""
return self.project.source_paths
def FindResource(self, some_path, binary=False):
"""Finds a Resource for the given path.
Args:
some_path: A relative or absolute path to a file.
Returns:
A Resource or None.
"""
if os.path.isabs(some_path):
return self.FindResourceGivenAbsolutePath(some_path, binary)
else:
return self.FindResourceGivenRelativePath(some_path, binary)
def FindResourceGivenAbsolutePath(self, absolute_path, binary=False):
"""Returns a Resource for the given absolute path."""
candidate_paths = []
for source_path in self.source_paths:
if absolute_path.startswith(source_path):
candidate_paths.append(source_path)
if len(candidate_paths) == 0:
return None
# Sort by length. Longest match wins.
candidate_paths.sort(lambda x, y: len(x) - len(y))
longest_candidate = candidate_paths[-1]
return resource_module.Resource(longest_candidate, absolute_path, binary)
def FindResourceGivenRelativePath(self, relative_path, binary=False):
"""Returns a Resource for the given relative path."""
absolute_path = None
for script_path in self.source_paths:
absolute_path = os.path.join(script_path, relative_path)
if os.path.exists(absolute_path):
return resource_module.Resource(script_path, absolute_path, binary)
return None
def _FindResourceGivenNameAndSuffix(
self, requested_name, extension, return_resource=False):
"""Searches for a file and reads its contents.
Args:
requested_name: The name of the resource that was requested.
extension: The extension for this requested resource.
Returns:
A (path, contents) pair.
"""
pathy_name = requested_name.replace('.', os.sep)
filename = pathy_name + extension
resource = self.FindResourceGivenRelativePath(filename)
if return_resource:
return resource
if not resource:
return None, None
return _read_file(resource.absolute_path)
def FindModuleResource(self, requested_module_name):
"""Finds a module javascript file and returns a Resource, or none."""
js_resource = self._FindResourceGivenNameAndSuffix(
requested_module_name, '.js', return_resource=True)
html_resource = self._FindResourceGivenNameAndSuffix(
requested_module_name, '.html', return_resource=True)
if js_resource and html_resource:
if html_module.IsHTMLResourceTheModuleGivenConflictingResourceNames(
js_resource, html_resource):
return html_resource
return js_resource
elif js_resource:
return js_resource
return html_resource
def LoadModule(self, module_name=None, module_filename=None,
excluded_scripts=None):
assert bool(module_name) ^ bool(module_filename), (
'Must provide either module_name or module_filename.')
if module_filename:
resource = self.FindResource(module_filename)
if not resource:
raise Exception('Could not find %s in %s' % (
module_filename, repr(self.source_paths)))
module_name = resource.name
else:
resource = None # Will be set if we end up needing to load.
if module_name in self.loaded_modules:
assert self.loaded_modules[module_name].contents
return self.loaded_modules[module_name]
if not resource: # happens when module_name was given
resource = self.FindModuleResource(module_name)
if not resource:
raise module.DepsException('No resource for module "%s"' % module_name)
m = html_module.HTMLModule(self, module_name, resource)
self.loaded_modules[module_name] = m
# Fake it, this is probably either polymer.min.js or platform.js which are
# actually .js files....
if resource.absolute_path.endswith('.js'):
return m
m.Parse(excluded_scripts)
m.Load(excluded_scripts)
return m
def LoadRawScript(self, relative_raw_script_path):
resource = None
for source_path in self.source_paths:
possible_absolute_path = os.path.join(
source_path, os.path.normpath(relative_raw_script_path))
if os.path.exists(possible_absolute_path):
resource = resource_module.Resource(
source_path, possible_absolute_path)
break
if not resource:
raise module.DepsException(
'Could not find a file for raw script %s in %s' %
(relative_raw_script_path, self.source_paths))
assert relative_raw_script_path == resource.unix_style_relative_path, (
'Expected %s == %s' % (relative_raw_script_path,
resource.unix_style_relative_path))
if resource.absolute_path in self.loaded_raw_scripts:
return self.loaded_raw_scripts[resource.absolute_path]
raw_script = module.RawScript(resource)
self.loaded_raw_scripts[resource.absolute_path] = raw_script
return raw_script
def LoadStyleSheet(self, name):
if name in self.loaded_style_sheets:
return self.loaded_style_sheets[name]
resource = self._FindResourceGivenNameAndSuffix(
name, '.css', return_resource=True)
if not resource:
raise module.DepsException(
'Could not find a file for stylesheet %s' % name)
style_sheet = style_sheet_module.StyleSheet(self, name, resource)
style_sheet.load()
self.loaded_style_sheets[name] = style_sheet
return style_sheet
def LoadImage(self, abs_path):
if abs_path in self.loaded_images:
return self.loaded_images[abs_path]
if not os.path.exists(abs_path):
raise module.DepsException("url('%s') did not exist" % abs_path)
res = self.FindResourceGivenAbsolutePath(abs_path, binary=True)
if res is None:
raise module.DepsException("url('%s') was not in search path" % abs_path)
image = style_sheet_module.Image(res)
self.loaded_images[abs_path] = image
return image
def GetStrippedJSForFilename(self, filename, early_out_if_no_py_vulcanize):
if filename in self.stripped_js_by_filename:
return self.stripped_js_by_filename[filename]
with open(filename, 'r') as f:
contents = f.read(4096)
if early_out_if_no_py_vulcanize and ('py_vulcanize' not in contents):
return None
s = strip_js_comments.StripJSComments(contents)
self.stripped_js_by_filename[filename] = s
return s
def _read_file(absolute_path):
"""Reads a file and returns a (path, contents) pair.
Args:
absolute_path: Absolute path to a file.
Raises:
Exception: The given file doesn't exist.
IOError: There was a problem opening or reading the file.
"""
if not os.path.exists(absolute_path):
raise Exception('%s not found.' % absolute_path)
f = codecs.open(absolute_path, mode='r', encoding='utf-8')
contents = f.read()
f.close()
return absolute_path, contents

View File

@@ -0,0 +1,17 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import unittest
from py_vulcanize import resource
class ResourceUnittest(unittest.TestCase):
def testBasic(self):
r = resource.Resource('/a', '/a/b/c.js')
self.assertEquals('b.c', r.name)
self.assertEquals(os.path.join('b', 'c.js'), r.relative_path)
self.assertEquals('b/c.js', r.unix_style_relative_path)

View File

@@ -0,0 +1,81 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utility function for stripping comments out of JavaScript source code."""
import re
def _TokenizeJS(text):
"""Splits source code text into segments in preparation for comment stripping.
Note that this doesn't tokenize for parsing. There is no notion of statements,
variables, etc. The only tokens of interest are comment-related tokens.
Args:
text: The contents of a JavaScript file.
Yields:
A succession of strings in the file, including all comment-related symbols.
"""
rest = text
tokens = ['//', '/*', '*/', '\n']
next_tok = re.compile('|'.join(re.escape(x) for x in tokens))
while len(rest):
m = next_tok.search(rest)
if not m:
# end of string
yield rest
return
min_index = m.start()
end_index = m.end()
if min_index > 0:
yield rest[:min_index]
yield rest[min_index:end_index]
rest = rest[end_index:]
def StripJSComments(text):
"""Strips comments out of JavaScript source code.
Args:
text: JavaScript source text.
Returns:
JavaScript source text with comments stripped out.
"""
result_tokens = []
token_stream = _TokenizeJS(text).__iter__()
while True:
try:
t = next(token_stream)
except StopIteration:
break
if t == '//':
while True:
try:
t2 = next(token_stream)
if t2 == '\n':
break
except StopIteration:
break
elif t == '/*':
nesting = 1
while True:
try:
t2 = next(token_stream)
if t2 == '/*':
nesting += 1
elif t2 == '*/':
nesting -= 1
if nesting == 0:
break
except StopIteration:
break
else:
result_tokens.append(t)
return ''.join(result_tokens)

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for strip_js_comments module."""
import unittest
from py_vulcanize import strip_js_comments
# This test case tests a protected method.
# pylint: disable=W0212
class JavaScriptStripCommentTests(unittest.TestCase):
"""Test case for _strip_js_comments and _TokenizeJS."""
def test_strip_comments(self):
self.assertEquals(
'A ', strip_js_comments.StripJSComments('A // foo'))
self.assertEquals(
'A bar', strip_js_comments.StripJSComments('A // foo\nbar'))
self.assertEquals(
'A b', strip_js_comments.StripJSComments('A /* foo */ b'))
self.assertEquals(
'A b', strip_js_comments.StripJSComments('A /* foo\n */ b'))
def test_tokenize_empty(self):
tokens = list(strip_js_comments._TokenizeJS(''))
self.assertEquals([], tokens)
def test_tokenize_nl(self):
tokens = list(strip_js_comments._TokenizeJS('\n'))
self.assertEquals(['\n'], tokens)
def test_tokenize_slashslash_comment(self):
tokens = list(strip_js_comments._TokenizeJS('A // foo'))
self.assertEquals(['A ', '//', ' foo'], tokens)
def test_tokenize_slashslash_comment_then_newline(self):
tokens = list(strip_js_comments._TokenizeJS('A // foo\nbar'))
self.assertEquals(['A ', '//', ' foo', '\n', 'bar'], tokens)
def test_tokenize_cstyle_comment_one_line(self):
tokens = list(strip_js_comments._TokenizeJS('A /* foo */'))
self.assertEquals(['A ', '/*', ' foo ', '*/'], tokens)
def test_tokenize_cstyle_comment_multi_line(self):
tokens = list(strip_js_comments._TokenizeJS('A /* foo\n*bar\n*/'))
self.assertEquals(['A ', '/*', ' foo', '\n', '*bar', '\n', '*/'], tokens)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,138 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import base64
import os
import re
class Image(object):
def __init__(self, resource):
self.resource = resource
self.aliases = []
@property
def relative_path(self):
return self.resource.relative_path
@property
def absolute_path(self):
return self.resource.absolute_path
@property
def contents(self):
return self.resource.contents
class ParsedStyleSheet(object):
def __init__(self, loader, containing_dirname, contents):
self.loader = loader
self.contents = contents
self._images = None
self._Load(containing_dirname)
@property
def images(self):
return self._images
def AppendDirectlyDependentFilenamesTo(self, dependent_filenames):
for i in self.images:
dependent_filenames.append(i.resource.absolute_path)
@property
def contents_with_inlined_images(self):
images_by_url = {}
for i in self.images:
for a in i.aliases:
images_by_url[a] = i
def InlineUrl(m):
url = m.group('url')
image = images_by_url[url]
ext = os.path.splitext(image.absolute_path)[1]
data = base64.standard_b64encode(image.contents)
return 'url(data:image/%s;base64,%s)' % (ext[1:], data)
# I'm assuming we only have url()'s associated with images
return re.sub('url\((?P<quote>"|\'|)(?P<url>[^"\'()]*)(?P=quote)\)',
InlineUrl, self.contents)
def AppendDirectlyDependentFilenamesTo(self, dependent_filenames):
for i in self.images:
dependent_filenames.append(i.resource.absolute_path)
def _Load(self, containing_dirname):
if self.contents.find('@import') != -1:
raise Exception('@imports are not supported')
matches = re.findall(
'url\((?:["|\']?)([^"\'()]*)(?:["|\']?)\)',
self.contents)
def resolve_url(url):
if os.path.isabs(url):
# FIXME: module is used here, but py_vulcanize.module is never imported.
# However, py_vulcanize.module cannot be imported since py_vulcanize.module may import
# style_sheet, leading to an import loop.
raise module.DepsException('URL references must be relative')
# URLS are relative to this module's directory
abs_path = os.path.abspath(os.path.join(containing_dirname, url))
image = self.loader.LoadImage(abs_path)
image.aliases.append(url)
return image
self._images = [resolve_url(x) for x in matches]
class StyleSheet(object):
"""Represents a stylesheet resource referenced by a module via the
base.requireStylesheet(xxx) directive."""
def __init__(self, loader, name, resource):
self.loader = loader
self.name = name
self.resource = resource
self._parsed_style_sheet = None
@property
def filename(self):
return self.resource.absolute_path
@property
def contents(self):
return self.resource.contents
def __repr__(self):
return 'StyleSheet(%s)' % self.name
@property
def images(self):
self._InitParsedStyleSheetIfNeeded()
return self._parsed_style_sheet.images
def AppendDirectlyDependentFilenamesTo(self, dependent_filenames):
self._InitParsedStyleSheetIfNeeded()
dependent_filenames.append(self.resource.absolute_path)
self._parsed_style_sheet.AppendDirectlyDependentFilenamesTo(
dependent_filenames)
@property
def contents_with_inlined_images(self):
self._InitParsedStyleSheetIfNeeded()
return self._parsed_style_sheet.contents_with_inlined_images
def load(self):
self._InitParsedStyleSheetIfNeeded()
def _InitParsedStyleSheetIfNeeded(self):
if self._parsed_style_sheet:
return
module_dirname = os.path.dirname(self.resource.absolute_path)
self._parsed_style_sheet = ParsedStyleSheet(
self.loader, module_dirname, self.contents)

View File

@@ -0,0 +1,67 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import base64
import os
import unittest
from py_vulcanize import project as project_module
from py_vulcanize import resource_loader
from py_vulcanize import fake_fs
from py_vulcanize import module
class StyleSheetUnittest(unittest.TestCase):
def testImages(self):
fs = fake_fs.FakeFS()
fs.AddFile('/src/foo/x.css', """
.x .y {
background-image: url(../images/bar.jpeg);
}
""")
fs.AddFile('/src/images/bar.jpeg', 'hello world')
with fs:
project = project_module.Project([os.path.normpath('/src/')])
loader = resource_loader.ResourceLoader(project)
foo_x = loader.LoadStyleSheet('foo.x')
self.assertEquals(1, len(foo_x.images))
r0 = foo_x.images[0]
self.assertEquals(os.path.normpath('/src/images/bar.jpeg'),
r0.absolute_path)
inlined = foo_x.contents_with_inlined_images
self.assertEquals("""
.x .y {
background-image: url(data:image/jpeg;base64,%s);
}
""" % base64.standard_b64encode('hello world'), inlined)
def testURLResolveFails(self):
fs = fake_fs.FakeFS()
fs.AddFile('/src/foo/x.css', """
.x .y {
background-image: url(../images/missing.jpeg);
}
""")
with fs:
project = project_module.Project([os.path.normpath('/src')])
loader = resource_loader.ResourceLoader(project)
self.assertRaises(module.DepsException,
lambda: loader.LoadStyleSheet('foo.x'))
def testImportsCauseFailure(self):
fs = fake_fs.FakeFS()
fs.AddFile('/src/foo/x.css', """
@import url(awesome.css);
""")
with fs:
project = project_module.Project([os.path.normpath('/src')])
loader = resource_loader.ResourceLoader(project)
self.assertRaises(Exception,
lambda: loader.LoadStyleSheet('foo.x'))

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,354 @@
LICENSE
MANIFEST
PKG-INFO
README.rst
_setup/__init__.py
_setup/include/cext.h
_setup/py2/__init__.py
_setup/py2/commands.py
_setup/py2/data.py
_setup/py2/dist.py
_setup/py2/ext.py
_setup/py2/setup.py
_setup/py2/shell.py
_setup/py2/term/__init__.py
_setup/py2/term/_term.py
_setup/py2/util.py
_setup/py3/__init__.py
_setup/py3/commands.py
_setup/py3/data.py
_setup/py3/dist.py
_setup/py3/ext.py
_setup/py3/setup.py
_setup/py3/shell.py
_setup/py3/term/__init__.py
_setup/py3/term/_term.py
_setup/py3/util.py
bench
bench.sh
bench/LICENSE.cssmin
bench/__init__.py
bench/cssmin.py
bench/main.py
bench/wikipedia.css
bench/wikipedia.min.css
bench/write.py
docs/BENCHMARKS
docs/CHANGES
docs/CLASSIFIERS
docs/DESCRIPTION
docs/PROVIDES
docs/SUMMARY
docs/apidoc/api-objects.txt
docs/apidoc/crarr.png
docs/apidoc/epydoc.css
docs/apidoc/epydoc.js
docs/apidoc/help.html
docs/apidoc/identifier-index.html
docs/apidoc/index.html
docs/apidoc/module-tree.html
docs/apidoc/rcssmin-module.html
docs/apidoc/rcssmin-pysrc.html
docs/apidoc/redirect.html
package.cfg
rcssmin.c
rcssmin.py
run_tests.py
setup.py
tests
tests/main/atgroup_00.css
tests/main/atgroup_01.css
tests/main/atgroup_02.css
tests/main/atgroup_03.css
tests/main/atgroup_04.css
tests/main/atgroup_05.css
tests/main/atgroup_06.css
tests/main/atgroup_07.css
tests/main/atgroup_08.css
tests/main/atgroup_09.css
tests/main/atgroup_10.css
tests/main/atgroup_11.css
tests/main/comment_00.css
tests/main/comment_01.css
tests/main/comment_02.css
tests/main/comment_03.css
tests/main/comment_04.css
tests/main/escape_00.css
tests/main/escape_01.css
tests/main/escape_02.css
tests/main/escape_03.css
tests/main/escape_04.css
tests/main/escape_05.css
tests/main/escape_06.css
tests/main/first_00.css
tests/main/first_01.css
tests/main/first_02.css
tests/main/out/atgroup_00.out
tests/main/out/atgroup_00.out.b
tests/main/out/atgroup_01.out
tests/main/out/atgroup_01.out.b
tests/main/out/atgroup_02.out
tests/main/out/atgroup_02.out.b
tests/main/out/atgroup_03.out
tests/main/out/atgroup_03.out.b
tests/main/out/atgroup_04.out
tests/main/out/atgroup_04.out.b
tests/main/out/atgroup_05.out
tests/main/out/atgroup_05.out.b
tests/main/out/atgroup_06.out
tests/main/out/atgroup_06.out.b
tests/main/out/atgroup_07.out
tests/main/out/atgroup_07.out.b
tests/main/out/atgroup_08.out
tests/main/out/atgroup_08.out.b
tests/main/out/atgroup_09.out
tests/main/out/atgroup_09.out.b
tests/main/out/atgroup_10.out
tests/main/out/atgroup_10.out.b
tests/main/out/atgroup_11.out
tests/main/out/atgroup_11.out.b
tests/main/out/comment_00.out
tests/main/out/comment_00.out.b
tests/main/out/comment_01.out
tests/main/out/comment_01.out.b
tests/main/out/comment_02.out
tests/main/out/comment_02.out.b
tests/main/out/comment_03.out
tests/main/out/comment_03.out.b
tests/main/out/comment_04.out
tests/main/out/comment_04.out.b
tests/main/out/escape_00.out
tests/main/out/escape_00.out.b
tests/main/out/escape_01.out
tests/main/out/escape_01.out.b
tests/main/out/escape_02.out
tests/main/out/escape_02.out.b
tests/main/out/escape_03.out
tests/main/out/escape_03.out.b
tests/main/out/escape_04.out
tests/main/out/escape_04.out.b
tests/main/out/escape_05.out
tests/main/out/escape_05.out.b
tests/main/out/escape_06.out
tests/main/out/escape_06.out.b
tests/main/out/first_00.out
tests/main/out/first_00.out.b
tests/main/out/first_01.out
tests/main/out/first_01.out.b
tests/main/out/first_02.out
tests/main/out/first_02.out.b
tests/main/out/url_00.out
tests/main/out/url_00.out.b
tests/main/out/url_01.out
tests/main/out/url_01.out.b
tests/main/out/url_02.out
tests/main/out/url_02.out.b
tests/main/out/url_03.out
tests/main/out/url_03.out.b
tests/main/out/url_04.out
tests/main/out/url_04.out.b
tests/main/out/url_05.out
tests/main/out/url_05.out.b
tests/main/out/url_06.out
tests/main/out/url_06.out.b
tests/main/out/url_07.out
tests/main/out/url_07.out.b
tests/main/out/url_08.out
tests/main/out/url_08.out.b
tests/main/out/url_09.out
tests/main/out/url_09.out.b
tests/main/url_00.css
tests/main/url_01.css
tests/main/url_02.css
tests/main/url_03.css
tests/main/url_04.css
tests/main/url_05.css
tests/main/url_06.css
tests/main/url_07.css
tests/main/url_08.css
tests/main/url_09.css
tests/yui/README
tests/yui/background-position.css
tests/yui/background-position.css.min
tests/yui/border-none.css
tests/yui/border-none.css.min
tests/yui/box-model-hack.css
tests/yui/box-model-hack.css.min
tests/yui/bug2527974.css
tests/yui/bug2527974.css.min
tests/yui/bug2527991.css
tests/yui/bug2527991.css.min
tests/yui/bug2527998.css
tests/yui/bug2527998.css.min
tests/yui/bug2528034.css
tests/yui/bug2528034.css.min
tests/yui/charset-media.css
tests/yui/charset-media.css.min
tests/yui/color-simple.css
tests/yui/color-simple.css.min
tests/yui/color.css
tests/yui/color.css.min
tests/yui/comment.css
tests/yui/comment.css.min
tests/yui/concat-charset.css
tests/yui/concat-charset.css.min
tests/yui/dataurl-base64-doublequotes.css
tests/yui/dataurl-base64-doublequotes.css.min
tests/yui/dataurl-base64-eof.css
tests/yui/dataurl-base64-eof.css.min
tests/yui/dataurl-base64-linebreakindata.css
tests/yui/dataurl-base64-linebreakindata.css.min
tests/yui/dataurl-base64-noquotes.css
tests/yui/dataurl-base64-noquotes.css.min
tests/yui/dataurl-base64-singlequotes.css
tests/yui/dataurl-base64-singlequotes.css.min
tests/yui/dataurl-base64-twourls.css
tests/yui/dataurl-base64-twourls.css.min
tests/yui/dataurl-dbquote-font.css
tests/yui/dataurl-dbquote-font.css.min
tests/yui/dataurl-nonbase64-doublequotes.css
tests/yui/dataurl-nonbase64-doublequotes.css.min
tests/yui/dataurl-nonbase64-noquotes.css
tests/yui/dataurl-nonbase64-noquotes.css.min
tests/yui/dataurl-nonbase64-singlequotes.css
tests/yui/dataurl-nonbase64-singlequotes.css.min
tests/yui/dataurl-noquote-multiline-font.css
tests/yui/dataurl-noquote-multiline-font.css.min
tests/yui/dataurl-realdata-doublequotes.css
tests/yui/dataurl-realdata-doublequotes.css.min
tests/yui/dataurl-realdata-noquotes.css
tests/yui/dataurl-realdata-noquotes.css.min
tests/yui/dataurl-realdata-singlequotes.css
tests/yui/dataurl-realdata-singlequotes.css.min
tests/yui/dataurl-realdata-yuiapp.css
tests/yui/dataurl-realdata-yuiapp.css.min
tests/yui/dataurl-singlequote-font.css
tests/yui/dataurl-singlequote-font.css.min
tests/yui/decimals.css
tests/yui/decimals.css.min
tests/yui/dollar-header.css
tests/yui/dollar-header.css.min
tests/yui/font-face.css
tests/yui/font-face.css.min
tests/yui/ie5mac.css
tests/yui/ie5mac.css.min
tests/yui/media-empty-class.css
tests/yui/media-empty-class.css.min
tests/yui/media-multi.css
tests/yui/media-multi.css.min
tests/yui/media-test.css
tests/yui/media-test.css.min
tests/yui/opacity-filter.css
tests/yui/opacity-filter.css.min
tests/yui/out/background-position.out
tests/yui/out/background-position.out.b
tests/yui/out/border-none.out
tests/yui/out/border-none.out.b
tests/yui/out/box-model-hack.out
tests/yui/out/box-model-hack.out.b
tests/yui/out/bug2527974.out
tests/yui/out/bug2527974.out.b
tests/yui/out/bug2527991.out
tests/yui/out/bug2527991.out.b
tests/yui/out/bug2527998.out
tests/yui/out/bug2527998.out.b
tests/yui/out/bug2528034.out
tests/yui/out/bug2528034.out.b
tests/yui/out/charset-media.out
tests/yui/out/charset-media.out.b
tests/yui/out/color-simple.out
tests/yui/out/color-simple.out.b
tests/yui/out/color.out
tests/yui/out/color.out.b
tests/yui/out/comment.out
tests/yui/out/comment.out.b
tests/yui/out/concat-charset.out
tests/yui/out/concat-charset.out.b
tests/yui/out/dataurl-base64-doublequotes.out
tests/yui/out/dataurl-base64-doublequotes.out.b
tests/yui/out/dataurl-base64-eof.out
tests/yui/out/dataurl-base64-eof.out.b
tests/yui/out/dataurl-base64-linebreakindata.out
tests/yui/out/dataurl-base64-linebreakindata.out.b
tests/yui/out/dataurl-base64-noquotes.out
tests/yui/out/dataurl-base64-noquotes.out.b
tests/yui/out/dataurl-base64-singlequotes.out
tests/yui/out/dataurl-base64-singlequotes.out.b
tests/yui/out/dataurl-base64-twourls.out
tests/yui/out/dataurl-base64-twourls.out.b
tests/yui/out/dataurl-dbquote-font.out
tests/yui/out/dataurl-dbquote-font.out.b
tests/yui/out/dataurl-nonbase64-doublequotes.out
tests/yui/out/dataurl-nonbase64-doublequotes.out.b
tests/yui/out/dataurl-nonbase64-noquotes.out
tests/yui/out/dataurl-nonbase64-noquotes.out.b
tests/yui/out/dataurl-nonbase64-singlequotes.out
tests/yui/out/dataurl-nonbase64-singlequotes.out.b
tests/yui/out/dataurl-noquote-multiline-font.out
tests/yui/out/dataurl-noquote-multiline-font.out.b
tests/yui/out/dataurl-realdata-doublequotes.out
tests/yui/out/dataurl-realdata-doublequotes.out.b
tests/yui/out/dataurl-realdata-noquotes.out
tests/yui/out/dataurl-realdata-noquotes.out.b
tests/yui/out/dataurl-realdata-singlequotes.out
tests/yui/out/dataurl-realdata-singlequotes.out.b
tests/yui/out/dataurl-realdata-yuiapp.out
tests/yui/out/dataurl-realdata-yuiapp.out.b
tests/yui/out/dataurl-singlequote-font.out
tests/yui/out/dataurl-singlequote-font.out.b
tests/yui/out/decimals.out
tests/yui/out/decimals.out.b
tests/yui/out/dollar-header.out
tests/yui/out/dollar-header.out.b
tests/yui/out/font-face.out
tests/yui/out/font-face.out.b
tests/yui/out/ie5mac.out
tests/yui/out/ie5mac.out.b
tests/yui/out/media-empty-class.out
tests/yui/out/media-empty-class.out.b
tests/yui/out/media-multi.out
tests/yui/out/media-multi.out.b
tests/yui/out/media-test.out
tests/yui/out/media-test.out.b
tests/yui/out/opacity-filter.out
tests/yui/out/opacity-filter.out.b
tests/yui/out/preserve-case.out
tests/yui/out/preserve-case.out.b
tests/yui/out/preserve-new-line.out
tests/yui/out/preserve-new-line.out.b
tests/yui/out/preserve-strings.out
tests/yui/out/preserve-strings.out.b
tests/yui/out/pseudo-first.out
tests/yui/out/pseudo-first.out.b
tests/yui/out/pseudo.out
tests/yui/out/pseudo.out.b
tests/yui/out/special-comments.out
tests/yui/out/special-comments.out.b
tests/yui/out/star-underscore-hacks.out
tests/yui/out/star-underscore-hacks.out.b
tests/yui/out/string-in-comment.out
tests/yui/out/string-in-comment.out.b
tests/yui/out/webkit-transform.out
tests/yui/out/webkit-transform.out.b
tests/yui/out/zeros.out
tests/yui/out/zeros.out.b
tests/yui/preserve-case.css
tests/yui/preserve-case.css.min
tests/yui/preserve-new-line.css
tests/yui/preserve-new-line.css.min
tests/yui/preserve-strings.css
tests/yui/preserve-strings.css.min
tests/yui/pseudo-first.css
tests/yui/pseudo-first.css.min
tests/yui/pseudo.css
tests/yui/pseudo.css.min
tests/yui/special-comments.css
tests/yui/special-comments.css.min
tests/yui/star-underscore-hacks.css
tests/yui/star-underscore-hacks.css.min
tests/yui/string-in-comment.css
tests/yui/string-in-comment.css.min
tests/yui/webkit-transform.css
tests/yui/webkit-transform.css.min
tests/yui/zeros.css
tests/yui/zeros.css.min

View File

@@ -0,0 +1,316 @@
Metadata-Version: 1.1
Name: rcssmin
Version: 1.0.5
Summary: CSS Minifier
Home-page: http://opensource.perlig.de/rcssmin/
Author: André Malo
Author-email: nd@perlig.de
License: Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Download-URL: http://storage.perlig.de/rcssmin/
Description: ==============
CSS Minifier
==============
RCSSmin is a CSS minifier.
The minifier is based on the semantics of the `YUI compressor`_\, which itself
is based on `the rule list by Isaac Schlueter`_\.
This module is a re-implementation aiming for speed instead of maximum
compression, so it can be used at runtime (rather than during a preprocessing
step). RCSSmin does syntactical compression only (removing spaces, comments
and possibly semicolons). It does not provide semantic compression (like
removing empty blocks, collapsing redundant properties etc). It does, however,
support various CSS hacks (by keeping them working as intended).
Here's a feature list:
- Strings are kept, except that escaped newlines are stripped
- Space/Comments before the very end or before various characters are
stripped: ``:{});=>+],!`` (The colon (``:``) is a special case, a single
space is kept if it's outside a ruleset.)
- Space/Comments at the very beginning or after various characters are
stripped: ``{}(=:>+[,!``
- Optional space after unicode escapes is kept, resp. replaced by a simple
space
- whitespaces inside ``url()`` definitions are stripped
- Comments starting with an exclamation mark (``!``) can be kept optionally.
- All other comments and/or whitespace characters are replaced by a single
space.
- Multiple consecutive semicolons are reduced to one
- The last semicolon within a ruleset is stripped
- CSS Hacks supported:
- IE7 hack (``>/**/``)
- Mac-IE5 hack (``/*\*/.../**/``)
- The boxmodelhack is supported naturally because it relies on valid CSS2
strings
- Between ``:first-line`` and the following comma or curly brace a space is
inserted. (apparently it's needed for IE6)
- Same for ``:first-letter``
rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
factor 100 or so (depending on the input). docs/BENCHMARKS in the source
distribution contains the details.
Both python 2 (>= 2.4) and python 3 are supported.
.. _YUI compressor: https://github.com/yui/yuicompressor/
.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/
Copyright and License
~~~~~~~~~~~~~~~~~~~~~
Copyright 2011 - 2014
André Malo or his licensors, as applicable.
The whole package (except for the files in the bench/ directory) is
distributed under the Apache License Version 2.0. You'll find a copy in the
root directory of the distribution or online at:
<http://www.apache.org/licenses/LICENSE-2.0>.
Bugs
~~~~
No bugs, of course. ;-)
But if you've found one or have an idea how to improve rcssmin, feel free
to send a pull request on `github <https://github.com/ndparker/rcssmin>`_
or send a mail to <rcssmin-bugs@perlig.de>.
Author Information
~~~~~~~~~~~~~~~~~~
André "nd" Malo <nd perlig.de>
GPG: 0x8103A37E
If God intended people to be naked, they would be born that way.
-- Oscar Wilde
.. vim:tw=72 syntax=rest
Keywords: CSS,Minimization
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved
Classifier: License :: OSI Approved :: Apache License, Version 2.0
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: C
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: Jython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing
Classifier: Topic :: Text Processing :: Filters
Classifier: Topic :: Utilities
Provides: rcssmin (1.0)

View File

@@ -0,0 +1,16 @@
Name: rCSSmin
Short Name: rcssmin
URL: http://opensource.perlig.de/rcssmin/
Version: 1.0.5
License: Apache 2.0
License File: NOT_SHIPPED
Security Critical: no
Description:
rCSSmin is a CSS minifier written in python.
The minifier is based on the semantics of the YUI compressor, which itself is
based on the rule list by Isaac Schlueter.
Modifications made:
- Removed the bench.sh since the file doesn't have the licensing info and
caused license checker to fail.

View File

@@ -0,0 +1,153 @@
.. -*- coding: utf-8 -*-
=====================================
rCSSmin - A CSS Minifier For Python
=====================================
TABLE OF CONTENTS
-----------------
1. Introduction
2. Copyright and License
3. System Requirements
4. Installation
5. Documentation
6. Bugs
7. Author Information
INTRODUCTION
------------
RCSSmin is a CSS minifier written in python.
The minifier is based on the semantics of the `YUI compressor`_\, which itself
is based on `the rule list by Isaac Schlueter`_\.
This module is a re-implementation aiming for speed instead of maximum
compression, so it can be used at runtime (rather than during a preprocessing
step). RCSSmin does syntactical compression only (removing spaces, comments
and possibly semicolons). It does not provide semantic compression (like
removing empty blocks, collapsing redundant properties etc). It does, however,
support various CSS hacks (by keeping them working as intended).
Here's a feature list:
- Strings are kept, except that escaped newlines are stripped
- Space/Comments before the very end or before various characters are
stripped: ``:{});=>+],!`` (The colon (``:``) is a special case, a single
space is kept if it's outside a ruleset.)
- Space/Comments at the very beginning or after various characters are
stripped: ``{}(=:>+[,!``
- Optional space after unicode escapes is kept, resp. replaced by a simple
space
- whitespaces inside ``url()`` definitions are stripped
- Comments starting with an exclamation mark (``!``) can be kept optionally.
- All other comments and/or whitespace characters are replaced by a single
space.
- Multiple consecutive semicolons are reduced to one
- The last semicolon within a ruleset is stripped
- CSS Hacks supported:
- IE7 hack (``>/**/``)
- Mac-IE5 hack (``/*\*/.../**/``)
- The boxmodelhack is supported naturally because it relies on valid CSS2
strings
- Between ``:first-line`` and the following comma or curly brace a space is
inserted. (apparently it's needed for IE6)
- Same for ``:first-letter``
rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
factor 100 or so (depending on the input). docs/BENCHMARKS in the source
distribution contains the details.
.. _YUI compressor: https://github.com/yui/yuicompressor/
.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/
COPYRIGHT AND LICENSE
---------------------
Copyright 2011 - 2014
André Malo or his licensors, as applicable.
The whole package (except for the files in the bench/ directory)
is distributed under the Apache License Version 2.0. You'll find a copy in the
root directory of the distribution or online at:
<http://www.apache.org/licenses/LICENSE-2.0>.
SYSTEM REQUIREMENTS
-------------------
Both python 2 (>=2.4) and python 3 are supported.
INSTALLATION
------------
Using pip
~~~~~~~~~
$ pip install rcssmin
Using distutils
~~~~~~~~~~~~~~~
$ python setup.py install
The following extra options to the install command may be of interest:
--without-c-extensions Don't install C extensions
--without-docs Do not install documentation files
Drop-in
~~~~~~~
rCSSmin effectively consists of two files: rcssmin.py and rcssmin.c, the
latter being entirely optional. So, for simple integration you can just
copy rcssmin.py into your project and use it.
DOCUMENTATION
-------------
A generated API documentation is available in the docs/apidoc/ directory.
But you can just look into the module. It provides a simple function,
called cssmin which takes the CSS as a string and returns the minified
CSS as a string.
The module additionally provides a "streamy" interface:
$ python -mrcssmin <css >minified
It takes two options:
-b Keep bang-comments (Comments starting with an exclamation mark)
-p Force using the python implementation (not the C implementation)
The latest documentation is also available online at
<http://opensource.perlig.de/rcssmin/>.
BUGS
----
No bugs, of course. ;-)
But if you've found one or have an idea how to improve rcssmin, feel free to
send a pull request on `github <https://github.com/ndparker/rcssmin>`_ or
send a mail to <rcssmin-bugs@perlig.de>.
AUTHOR INFORMATION
------------------
André "nd" Malo <nd@perlig.de>
GPG: 0x8103A37E
If God intended people to be naked, they would be born that way.
-- Oscar Wilde

View File

@@ -0,0 +1,64 @@
`cssmin.py` - A Python port of the YUI CSS compressor.
Copyright (c) 2010 Zachary Voase
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------------
This software contains portions of the YUI CSS Compressor, notably some regular
expressions for reducing the size of CSS. The YUI Compressor source code can be
found at <http://github.com/yui/yuicompressor>, and is licensed as follows:
> YUI Compressor Copyright License Agreement (BSD License)
>
> Copyright (c) 2009, Yahoo! Inc.
> All rights reserved.
>
> Redistribution and use of this software in source and binary forms,
> with or without modification, are permitted provided that the following
> conditions are met:
>
> * Redistributions of source code must retain the above
> copyright notice, this list of conditions and the
> following disclaimer.
>
> * Redistributions in binary form must reproduce the above
> copyright notice, this list of conditions and the
> following disclaimer in the documentation and/or other
> materials provided with the distribution.
>
> * Neither the name of Yahoo! Inc. nor the names of its
> contributors may be used to endorse or promote products
> derived from this software without specific prior
> written permission of Yahoo! Inc.
>
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
> AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
> FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,30 @@
# -*- coding: ascii -*-
r"""
=================================
Benchmark cssmin implementations
=================================
Benchmark cssmin implementations.
:Copyright:
Copyright 2011 - 2014
Andr\xe9 Malo or his licensors, as applicable
:License:
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
if __doc__:
__doc__ = __doc__.encode('ascii').decode('unicode_escape')

View File

@@ -0,0 +1,246 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""`cssmin` - A Python port of the YUI CSS compressor.
:Copyright:
Copyright 2011 - 2014
Andr\xe9 Malo or his licensors, as applicable
:License:
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
try:
from StringIO import StringIO # The pure-Python StringIO supports unicode.
except ImportError:
from io import StringIO
import re
__version__ = '0.2.0'
def remove_comments(css):
"""Remove all CSS comment blocks."""
iemac = False
preserve = False
comment_start = css.find("/*")
while comment_start >= 0:
# Preserve comments that look like `/*!...*/`.
# Slicing is used to make sure we don"t get an IndexError.
preserve = css[comment_start + 2:comment_start + 3] == "!"
comment_end = css.find("*/", comment_start + 2)
if comment_end < 0:
if not preserve:
css = css[:comment_start]
break
elif comment_end >= (comment_start + 2):
if css[comment_end - 1] == "\\":
# This is an IE Mac-specific comment; leave this one and the
# following one alone.
comment_start = comment_end + 2
iemac = True
elif iemac:
comment_start = comment_end + 2
iemac = False
elif not preserve:
css = css[:comment_start] + css[comment_end + 2:]
else:
comment_start = comment_end + 2
comment_start = css.find("/*", comment_start)
return css
def remove_unnecessary_whitespace(css):
"""Remove unnecessary whitespace characters."""
def pseudoclasscolon(css):
"""
Prevents 'p :link' from becoming 'p:link'.
Translates 'p :link' into 'p ___PSEUDOCLASSCOLON___link'; this is
translated back again later.
"""
regex = re.compile(r"(^|\})(([^\{\:])+\:)+([^\{]*\{)")
match = regex.search(css)
while match:
css = ''.join([
css[:match.start()],
match.group().replace(":", "___PSEUDOCLASSCOLON___"),
css[match.end():]])
match = regex.search(css)
return css
css = pseudoclasscolon(css)
# Remove spaces from before things.
css = re.sub(r"\s+([!{};:>+\(\)\],])", r"\1", css)
# If there is a `@charset`, then only allow one, and move to the beginning.
css = re.sub(r"^(.*)(@charset \"[^\"]*\";)", r"\2\1", css)
css = re.sub(r"^(\s*@charset [^;]+;\s*)+", r"\1", css)
# Put the space back in for a few cases, such as `@media screen` and
# `(-webkit-min-device-pixel-ratio:0)`.
css = re.sub(r"\band\(", "and (", css)
# Put the colons back.
css = css.replace('___PSEUDOCLASSCOLON___', ':')
# Remove spaces from after things.
css = re.sub(r"([!{}:;>+\(\[,])\s+", r"\1", css)
return css
def remove_unnecessary_semicolons(css):
"""Remove unnecessary semicolons."""
return re.sub(r";+\}", "}", css)
def remove_empty_rules(css):
"""Remove empty rules."""
return re.sub(r"[^\}\{]+\{\}", "", css)
def normalize_rgb_colors_to_hex(css):
"""Convert `rgb(51,102,153)` to `#336699`."""
regex = re.compile(r"rgb\s*\(\s*([0-9,\s]+)\s*\)")
match = regex.search(css)
while match:
colors = map(lambda s: s.strip(), match.group(1).split(","))
hexcolor = '#%.2x%.2x%.2x' % tuple(map(int, colors))
css = css.replace(match.group(), hexcolor)
match = regex.search(css)
return css
def condense_zero_units(css):
"""Replace `0(px, em, %, etc)` with `0`."""
return re.sub(r"([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", r"\1\2", css)
def condense_multidimensional_zeros(css):
"""Replace `:0 0 0 0;`, `:0 0 0;` etc. with `:0;`."""
css = css.replace(":0 0 0 0;", ":0;")
css = css.replace(":0 0 0;", ":0;")
css = css.replace(":0 0;", ":0;")
# Revert `background-position:0;` to the valid `background-position:0 0;`.
css = css.replace("background-position:0;", "background-position:0 0;")
return css
def condense_floating_points(css):
"""Replace `0.6` with `.6` where possible."""
return re.sub(r"(:|\s)0+\.(\d+)", r"\1.\2", css)
def condense_hex_colors(css):
"""Shorten colors from #AABBCC to #ABC where possible."""
regex = re.compile(r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])")
match = regex.search(css)
while match:
first = match.group(3) + match.group(5) + match.group(7)
second = match.group(4) + match.group(6) + match.group(8)
if first.lower() == second.lower():
css = css.replace(match.group(), match.group(1) + match.group(2) + '#' + first)
match = regex.search(css, match.end() - 3)
else:
match = regex.search(css, match.end())
return css
def condense_whitespace(css):
"""Condense multiple adjacent whitespace characters into one."""
return re.sub(r"\s+", " ", css)
def condense_semicolons(css):
"""Condense multiple adjacent semicolon characters into one."""
return re.sub(r";;+", ";", css)
def wrap_css_lines(css, line_length):
"""Wrap the lines of the given CSS to an approximate length."""
lines = []
line_start = 0
for i, char in enumerate(css):
# It's safe to break after `}` characters.
if char == '}' and (i - line_start >= line_length):
lines.append(css[line_start:i + 1])
line_start = i + 1
if line_start < len(css):
lines.append(css[line_start:])
return '\n'.join(lines)
def cssmin(css, wrap=None):
css = remove_comments(css)
css = condense_whitespace(css)
# A pseudo class for the Box Model Hack
# (see http://tantek.com/CSS/Examples/boxmodelhack.html)
css = css.replace('"\\"}\\""', "___PSEUDOCLASSBMH___")
css = remove_unnecessary_whitespace(css)
css = remove_unnecessary_semicolons(css)
css = condense_zero_units(css)
css = condense_multidimensional_zeros(css)
css = condense_floating_points(css)
css = normalize_rgb_colors_to_hex(css)
css = condense_hex_colors(css)
if wrap is not None:
css = wrap_css_lines(css, wrap)
css = css.replace("___PSEUDOCLASSBMH___", '"\\"}\\""')
css = condense_semicolons(css)
return css.strip()
def main():
import optparse
import sys
p = optparse.OptionParser(
prog="cssmin", version=__version__,
usage="%prog [--wrap N]",
description="""Reads raw CSS from stdin, and writes compressed CSS to stdout.""")
p.add_option(
'-w', '--wrap', type='int', default=None, metavar='N',
help="Wrap output to approximately N chars per line.")
options, args = p.parse_args()
sys.stdout.write(cssmin(sys.stdin.read(), wrap=options.wrap))
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,236 @@
#!/usr/bin/env python
# -*- coding: ascii -*-
r"""
==================================
Benchmark cssmin implementations
==================================
Benchmark cssmin implementations.
:Copyright:
Copyright 2011 - 2014
Andr\xe9 Malo or his licensors, as applicable
:License:
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Usage::
python -mbench.main [-c COUNT] [-p file] cssfile ...
-c COUNT number of runs per cssfile and minifier. Defaults to 10.
-p file File to write the benchmark results in (pickled)
"""
if __doc__:
__doc__ = __doc__.encode('ascii').decode('unicode_escape')
__author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
__docformat__ = "restructuredtext en"
__license__ = "Apache License, Version 2.0"
__version__ = "1.0.0"
import sys as _sys
import time as _time
import_notes = []
class _p_02__rcssmin(object):
def __init__(self):
import rcssmin
cssmin = rcssmin._make_cssmin(python_only=True)
self.cssmin = lambda x: cssmin(x, keep_bang_comments=True)
class _p_03__rcssmin(object):
def __init__(self):
import _rcssmin
cssmin = _rcssmin.cssmin
self.cssmin = lambda x: cssmin(x, keep_bang_comments=True)
class cssmins(object):
from bench import cssmin as p_01_cssmin
p_02_rcssmin = _p_02__rcssmin()
try:
p_03__rcssmin = _p_03__rcssmin()
except ImportError:
import_notes.append("_rcssmin (C-Port) not available")
print(import_notes[-1])
print("Python Release: %s" % ".".join(map(str, _sys.version_info[:3])))
print("")
def slurp(filename):
""" Load a file """
fp = open(filename)
try:
return fp.read()
finally:
fp.close()
def print_(*value, **kwargs):
""" Print stuff """
(kwargs.get('file') or _sys.stdout).write(
''.join(value) + kwargs.get('end', '\n')
)
def bench(filenames, count):
"""
Benchmark the minifiers with given css samples
:Parameters:
`filenames` : sequence
List of filenames
`count` : ``int``
Number of runs per css file and minifier
:Exceptions:
- `RuntimeError` : empty filenames sequence
"""
if not filenames:
raise RuntimeError("Missing files to benchmark")
try:
xrange
except NameError:
xrange = range
try:
cmp
except NameError:
cmp = lambda a, b: (a > b) - (a < b)
ports = [item for item in dir(cssmins) if item.startswith('p_')]
ports.sort()
space = max(map(len, ports)) - 4
ports = [(item[5:], getattr(cssmins, item).cssmin) for item in ports]
flush = _sys.stdout.flush
struct = []
inputs = [(filename, slurp(filename)) for filename in filenames]
for filename, style in inputs:
print_("Benchmarking %r..." % filename, end=" ")
flush()
outputs = []
for _, cssmin in ports:
try:
outputs.append(cssmin(style))
except (SystemExit, KeyboardInterrupt):
raise
except:
outputs.append(None)
struct.append(dict(
filename=filename,
sizes=[
(item is not None and len(item) or None) for item in outputs
],
size=len(style),
messages=[],
times=[],
))
print_("(%.1f KiB)" % (struct[-1]['size'] / 1024.0,))
flush()
times = []
for idx, (name, cssmin) in enumerate(ports):
if outputs[idx] is None:
print_(" FAILED %s" % (name,))
struct[-1]['times'].append((name, None))
else:
print_(" Timing %s%s... (%5.1f KiB %s)" % (
name,
" " * (space - len(name)),
len(outputs[idx]) / 1024.0,
idx == 0 and '*' or ['=', '>', '<'][
cmp(len(outputs[idx]), len(outputs[0]))
],
), end=" ")
flush()
xcount = count
while True:
counted = [None for _ in xrange(xcount)]
start = _time.time()
for _ in counted:
cssmin(style)
end = _time.time()
result = (end - start) * 1000
if result < 10: # avoid measuring within the error range
xcount *= 10
continue
times.append(result / xcount)
break
print_("%8.2f ms" % times[-1], end=" ")
flush()
if len(times) <= 1:
print_()
else:
print_("(factor: %s)" % (', '.join([
'%.2f' % (timed / times[-1]) for timed in times[:-1]
])))
struct[-1]['times'].append((name, times[-1]))
flush()
print_()
return struct
def main(argv=None):
""" Main """
import getopt as _getopt
import os as _os
import pickle as _pickle
if argv is None:
argv = _sys.argv[1:]
try:
opts, args = _getopt.getopt(argv, "hc:p:", ["help"])
except getopt.GetoptError:
e = _sys.exc_info()[0](_sys.exc_info()[1])
print >> _sys.stderr, "%s\nTry %s -mbench.main --help" % (
e,
_os.path.basename(_sys.executable),
)
_sys.exit(2)
count, pickle = 10, None
for key, value in opts:
if key in ("-h", "--help"):
print >> _sys.stderr, (
"%s -mbench.main [-c count] [-p file] cssfile ..." % (
_os.path.basename(_sys.executable),
)
)
_sys.exit(0)
elif key == '-c':
count = int(value)
elif key == '-p':
pickle = str(value)
struct = bench(args, count)
if pickle:
fp = open(pickle, 'wb')
try:
fp.write(_pickle.dumps((
".".join(map(str, _sys.version_info[:3])),
import_notes,
struct,
), 0))
finally:
fp.close()
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,344 @@
#!/usr/bin/env python
# -*- coding: ascii -*-
r"""
=========================
Write benchmark results
=========================
Write benchmark results.
:Copyright:
Copyright 2014
Andr\xe9 Malo or his licensors, as applicable
:License:
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Usage::
python -mbench.write [-p plain] [-t table] <pickled
-p plain Plain file to write to (like docs/BENCHMARKS).
-t table Table file to write to (like docs/_userdoc/benchmark.txt).
"""
if __doc__:
__doc__ = __doc__.encode('ascii').decode('unicode_escape')
__author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
__docformat__ = "restructuredtext en"
__license__ = "Apache License, Version 2.0"
__version__ = "1.0.0"
import os as _os
import re as _re
import sys as _sys
try:
unicode
except NameError:
def uni(v):
if hasattr(v, 'decode'):
return v.decode('latin-1')
return str(v)
else:
def uni(v):
if isinstance(v, unicode):
return v.encode('utf-8')
return str(v)
def write_table(filename, results):
"""
Output tabled benchmark results
:Parameters:
`filename` : ``str``
Filename to write to
`results` : ``list``
Results
"""
try:
next
except NameError:
next = lambda i: (getattr(i, 'next', None) or i.__next__)()
try:
cmp
except NameError:
cmp = lambda a, b: (a > b) - (a < b)
names = [
('cssmin', 'YUI Port'),
('rcssmin', '|rcssmin|'),
('_rcssmin', r'_\ |rcssmin|'),
]
benched_per_table = 2
results = sorted(results, reverse=True)
# First we transform our data into a table (list of lists)
pythons, widths = [], [0] * (benched_per_table + 1)
last_version = None
for version, _, result in results:
version = uni(version)
if not(last_version is None or version.startswith('2.')):
continue
last_version = version
namesub = _re.compile(r'(?:-\d+(?:\.\d+)*)?\.css$').sub
result = iter(result)
tables = []
# given our data it's easier to create the table transposed...
for benched in result:
rows = [['Name'] + [desc for _, desc in names]]
for _ in range(benched_per_table):
if _:
try:
benched = next(result)
except StopIteration:
rows.append([''] + ['' for _ in names])
continue
times = dict((
uni(port), (time, benched['sizes'][idx])
) for idx, (port, time) in enumerate(benched['times']))
columns = ['%s (%.1f)' % (
namesub('', _os.path.basename(uni(benched['filename']))),
benched['size'] / 1024.0,
)]
for idx, (port, _) in enumerate(names):
if port not in times:
columns.append('n/a')
continue
time, size = times[port]
if time is None:
columns.append('(failed)')
continue
columns.append('%s%.2f ms (%.1f %s)' % (
idx == 0 and ' ' or '',
time,
size / 1024.0,
idx == 0 and '\\*' or ['=', '>', '<'][
cmp(size, benched['sizes'][0])
],
))
rows.append(columns)
# calculate column widths (global for all tables)
for idx, row in enumerate(rows):
widths[idx] = max(widths[idx], max(map(len, row)))
# ... and transpose it back.
tables.append(zip(*rows))
pythons.append((version, tables))
if last_version.startswith('2.'):
break
# Second we create a rest table from it
lines = []
separator = lambda c='-': '+'.join([''] + [
c * (width + 2) for width in widths
] + [''])
for idx, (version, tables) in enumerate(pythons):
if idx:
lines.append('')
lines.append('')
line = 'Python %s' % (version,)
lines.append(line)
lines.append('~' * len(line))
for table in tables:
lines.append('')
lines.append('.. rst-class:: benchmark')
lines.append('')
for idx, row in enumerate(table):
if idx == 0:
# header
lines.append(separator())
lines.append('|'.join([''] + [
' %s%*s ' % (col, len(col) - width, '')
for width, col in zip(widths, row)
] + ['']))
lines.append(separator('='))
else: # data
lines.append('|'.join([''] + [
j == 0 and (
' %s%*s ' % (col, len(col) - widths[j], '')
) or (
['%*s ', ' %*s '][idx == 1] % (widths[j], col)
)
for j, col in enumerate(row)
] + ['']))
lines.append(separator())
fplines = []
fp = open(filename)
try:
fpiter = iter(fp)
for line in fpiter:
line = line.rstrip()
if line == '.. begin tables':
buf = []
for line in fpiter:
line = line.rstrip()
if line == '.. end tables':
fplines.append('.. begin tables')
fplines.append('')
fplines.extend(lines)
fplines.append('')
fplines.append('.. end tables')
buf = []
break
else:
buf.append(line)
else:
fplines.extend(buf)
_sys.stderr.write("Placeholder container not found!\n")
else:
fplines.append(line)
finally:
fp.close()
fp = open(filename, 'w')
try:
fp.write('\n'.join(fplines) + '\n')
finally:
fp.close()
def write_plain(filename, results):
"""
Output plain benchmark results
:Parameters:
`filename` : ``str``
Filename to write to
`results` : ``list``
Results
"""
lines = []
results = sorted(results, reverse=True)
for idx, (version, import_notes, result) in enumerate(results):
if idx:
lines.append('')
lines.append('')
lines.append('$ python%s -OO bench/main.py bench/*.css' % (
'.'.join(version.split('.')[:2])
))
lines.append('~' * 72)
for note in import_notes:
lines.append(uni(note))
lines.append('Python Release: %s' % (version,))
for single in result:
lines.append('')
lines.append('Benchmarking %r... (%.1f KiB)' % (
uni(single['filename']), single['size'] / 1024.0
))
for msg in single['messages']:
lines.append(msg)
times = []
space = max([len(uni(port)) for port, _ in single['times']])
for idx, (port, time) in enumerate(single['times']):
port = uni(port)
if time is None:
lines.append(" FAILED %s" % (port,))
else:
times.append(time)
lines.append(
" Timing %s%s ... (%5.1f KiB %s) %8.2f ms" % (
port,
" " * (space - len(port)),
single['sizes'][idx] / 1024.0,
idx == 0 and '*' or ['=', '>', '<'][
cmp(single['sizes'][idx], single['sizes'][0])
],
time
)
)
if len(times) > 1:
lines[-1] += " (factor: %s)" % (', '.join([
'%.2f' % (timed / time) for timed in times[:-1]
]))
lines.append('')
lines.append('')
lines.append('# vim: nowrap')
fp = open(filename, 'w')
try:
fp.write('\n'.join(lines) + '\n')
finally:
fp.close()
def main(argv=None):
""" Main """
import getopt as _getopt
import pickle as _pickle
if argv is None:
argv = _sys.argv[1:]
try:
opts, args = _getopt.getopt(argv, "hp:t:", ["help"])
except getopt.GetoptError:
e = _sys.exc_info()[0](_sys.exc_info()[1])
print >> _sys.stderr, "%s\nTry %s -mbench.write --help" % (
e,
_os.path.basename(_sys.executable),
)
_sys.exit(2)
plain, table = None, None
for key, value in opts:
if key in ("-h", "--help"):
print >> _sys.stderr, (
"%s -mbench.write [-p plain] [-t table] <pickled" % (
_os.path.basename(_sys.executable),
)
)
_sys.exit(0)
elif key == '-p':
plain = str(value)
elif key == '-t':
table = str(value)
struct = []
_sys.stdin = getattr(_sys.stdin, 'detach', lambda: _sys.stdin)()
try:
while True:
version, import_notes, result = _pickle.load(_sys.stdin)
if hasattr(version, 'decode'):
version = version.decode('latin-1')
struct.append((version, import_notes, result))
except EOFError:
pass
if plain:
write_plain(plain, struct)
if table:
write_table(table, struct)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,61 @@
$ python3.4 -OO bench/main.py bench/*.css
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Python Release: 3.4.2
Benchmarking 'bench/wikipedia.css'... (81.0 KiB)
Timing cssmin ... ( 49.4 KiB *) 163.11 ms
Timing rcssmin ... ( 49.6 KiB >) 45.48 ms (factor: 3.59)
Timing _rcssmin ... ( 49.6 KiB >) 0.43 ms (factor: 378.93, 105.66)
Benchmarking 'bench/wikipedia.min.css'... (49.4 KiB)
Timing cssmin ... ( 49.4 KiB *) 119.00 ms
Timing rcssmin ... ( 49.4 KiB =) 20.94 ms (factor: 5.68)
Timing _rcssmin ... ( 49.4 KiB =) 0.26 ms (factor: 454.45, 79.98)
$ python3.3 -OO bench/main.py bench/*.css
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Python Release: 3.3.5
Benchmarking 'bench/wikipedia.css'... (81.0 KiB)
Timing cssmin ... ( 49.4 KiB *) 185.01 ms
Timing rcssmin ... ( 49.6 KiB >) 59.30 ms (factor: 3.12)
Timing _rcssmin ... ( 49.6 KiB >) 0.52 ms (factor: 356.38, 114.23)
Benchmarking 'bench/wikipedia.min.css'... (49.4 KiB)
Timing cssmin ... ( 49.4 KiB *) 136.26 ms
Timing rcssmin ... ( 49.4 KiB =) 25.51 ms (factor: 5.34)
Timing _rcssmin ... ( 49.4 KiB =) 0.26 ms (factor: 515.24, 96.47)
$ python3.2 -OO bench/main.py bench/*.css
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Python Release: 3.2.5
Benchmarking 'bench/wikipedia.css'... (81.0 KiB)
Timing cssmin ... ( 49.4 KiB *) 225.32 ms
Timing rcssmin ... ( 49.6 KiB >) 57.51 ms (factor: 3.92)
Timing _rcssmin ... ( 49.6 KiB >) 0.43 ms (factor: 527.98, 134.77)
Benchmarking 'bench/wikipedia.min.css'... (49.4 KiB)
Timing cssmin ... ( 49.4 KiB *) 129.43 ms
Timing rcssmin ... ( 49.4 KiB =) 24.45 ms (factor: 5.29)
Timing _rcssmin ... ( 49.4 KiB =) 0.25 ms (factor: 526.94, 99.55)
$ python2.7 -OO bench/main.py bench/*.css
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Python Release: 2.7.7
Benchmarking 'bench/wikipedia.css'... (81.0 KiB)
Timing cssmin ... ( 49.4 KiB *) 175.98 ms
Timing rcssmin ... ( 49.6 KiB >) 46.22 ms (factor: 3.81)
Timing _rcssmin ... ( 49.6 KiB >) 0.45 ms (factor: 390.95, 102.68)
Benchmarking 'bench/wikipedia.min.css'... (49.4 KiB)
Timing cssmin ... ( 49.4 KiB *) 126.19 ms
Timing rcssmin ... ( 49.4 KiB =) 19.92 ms (factor: 6.33)
Timing _rcssmin ... ( 49.4 KiB =) 0.27 ms (factor: 469.78, 74.17)
# vim: nowrap

View File

@@ -0,0 +1,42 @@
Changes with version 1.0.5
*) Added support for pypy 2.2
*) Updated benchmarks
*) Relint with newer pylint
*) Fix locale problem with the setup script on python3.
Submitted by https://github.com/svenstaro
Changes with version 1.0.4
*) Documentation and benchmark updates
Changes with version 1.0.3
*) Added support for the following grouping @-rules:
@supports, @document, @keyframes
*) Added support for Python 3.4 and Jython 2.7
Changes with version 1.0.2
*) Added compat option to setup.py supporting the pip installer
*) Added support for pypy (1.9, 2.0)
*) Added support for jython (2.5)
Changes with version 1.0.1
*) Added support for Python 3.3
Changes with version 1.0.0
*) First stable release.

View File

@@ -0,0 +1,19 @@
Development Status :: 5 - Production/Stable
Environment :: Web Environment
Intended Audience :: Developers
License :: OSI Approved
License :: OSI Approved :: Apache License, Version 2.0
Operating System :: OS Independent
Programming Language :: C
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 3
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: Jython
Programming Language :: Python :: Implementation :: PyPy
Topic :: Internet :: WWW/HTTP :: Dynamic Content
Topic :: Software Development :: Libraries
Topic :: Software Development :: Libraries :: Python Modules
Topic :: Text Processing
Topic :: Text Processing :: Filters
Topic :: Utilities

View File

@@ -0,0 +1,85 @@
==============
CSS Minifier
==============
RCSSmin is a CSS minifier.
The minifier is based on the semantics of the `YUI compressor`_\, which itself
is based on `the rule list by Isaac Schlueter`_\.
This module is a re-implementation aiming for speed instead of maximum
compression, so it can be used at runtime (rather than during a preprocessing
step). RCSSmin does syntactical compression only (removing spaces, comments
and possibly semicolons). It does not provide semantic compression (like
removing empty blocks, collapsing redundant properties etc). It does, however,
support various CSS hacks (by keeping them working as intended).
Here's a feature list:
- Strings are kept, except that escaped newlines are stripped
- Space/Comments before the very end or before various characters are
stripped: ``:{});=>+],!`` (The colon (``:``) is a special case, a single
space is kept if it's outside a ruleset.)
- Space/Comments at the very beginning or after various characters are
stripped: ``{}(=:>+[,!``
- Optional space after unicode escapes is kept, resp. replaced by a simple
space
- whitespaces inside ``url()`` definitions are stripped
- Comments starting with an exclamation mark (``!``) can be kept optionally.
- All other comments and/or whitespace characters are replaced by a single
space.
- Multiple consecutive semicolons are reduced to one
- The last semicolon within a ruleset is stripped
- CSS Hacks supported:
- IE7 hack (``>/**/``)
- Mac-IE5 hack (``/*\*/.../**/``)
- The boxmodelhack is supported naturally because it relies on valid CSS2
strings
- Between ``:first-line`` and the following comma or curly brace a space is
inserted. (apparently it's needed for IE6)
- Same for ``:first-letter``
rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
factor 100 or so (depending on the input). docs/BENCHMARKS in the source
distribution contains the details.
Both python 2 (>= 2.4) and python 3 are supported.
.. _YUI compressor: https://github.com/yui/yuicompressor/
.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/
Copyright and License
~~~~~~~~~~~~~~~~~~~~~
Copyright 2011 - 2014
André Malo or his licensors, as applicable.
The whole package (except for the files in the bench/ directory) is
distributed under the Apache License Version 2.0. You'll find a copy in the
root directory of the distribution or online at:
<http://www.apache.org/licenses/LICENSE-2.0>.
Bugs
~~~~
No bugs, of course. ;-)
But if you've found one or have an idea how to improve rcssmin, feel free
to send a pull request on `github <https://github.com/ndparker/rcssmin>`_
or send a mail to <rcssmin-bugs@perlig.de>.
Author Information
~~~~~~~~~~~~~~~~~~
André "nd" Malo <nd perlig.de>
GPG: 0x8103A37E
If God intended people to be naked, they would be born that way.
-- Oscar Wilde
.. vim:tw=72 syntax=rest

View File

@@ -0,0 +1 @@
rcssmin (1.0)

View File

@@ -0,0 +1 @@
CSS Minifier

View File

@@ -0,0 +1,6 @@
rcssmin rcssmin-module.html
rcssmin.__license__ rcssmin-module.html#__license__
rcssmin._make_cssmin rcssmin-module.html#_make_cssmin
rcssmin.__doc__ rcssmin-module.html#__doc__
rcssmin.__package__ rcssmin-module.html#__package__
rcssmin.cssmin rcssmin-module.html#cssmin

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

View File

@@ -0,0 +1,322 @@
/* Epydoc CSS Stylesheet
*
* This stylesheet can be used to customize the appearance of epydoc's
* HTML output.
*
*/
/* Default Colors & Styles
* - Set the default foreground & background color with 'body'; and
* link colors with 'a:link' and 'a:visited'.
* - Use bold for decision list terms.
* - The heading styles defined here are used for headings *within*
* docstring descriptions. All headings used by epydoc itself use
* either class='epydoc' or class='toc' (CSS styles for both
* defined below).
*/
body { background: #ffffff; color: #000000; }
p { margin-top: 0.5em; margin-bottom: 0.5em; }
a:link { color: #0000ff; }
a:visited { color: #204080; }
dt { font-weight: bold; }
h1 { font-size: +140%; font-style: italic;
font-weight: bold; }
h2 { font-size: +125%; font-style: italic;
font-weight: bold; }
h3 { font-size: +110%; font-style: italic;
font-weight: normal; }
code { font-size: 100%; }
/* N.B.: class, not pseudoclass */
a.link { font-family: monospace; }
/* Page Header & Footer
* - The standard page header consists of a navigation bar (with
* pointers to standard pages such as 'home' and 'trees'); a
* breadcrumbs list, which can be used to navigate to containing
* classes or modules; options links, to show/hide private
* variables and to show/hide frames; and a page title (using
* <h1>). The page title may be followed by a link to the
* corresponding source code (using 'span.codelink').
* - The footer consists of a navigation bar, a timestamp, and a
* pointer to epydoc's homepage.
*/
h1.epydoc { margin: 0; font-size: +140%; font-weight: bold; }
h2.epydoc { font-size: +130%; font-weight: bold; }
h3.epydoc { font-size: +115%; font-weight: bold;
margin-top: 0.2em; }
td h3.epydoc { font-size: +115%; font-weight: bold;
margin-bottom: 0; }
table.navbar { background: #a0c0ff; color: #000000;
border: 2px groove #c0d0d0; }
table.navbar table { color: #000000; }
th.navbar-select { background: #70b0ff;
color: #000000; }
table.navbar a { text-decoration: none; }
table.navbar a:link { color: #0000ff; }
table.navbar a:visited { color: #204080; }
span.breadcrumbs { font-size: 85%; font-weight: bold; }
span.options { font-size: 70%; }
span.codelink { font-size: 85%; }
td.footer { font-size: 85%; }
/* Table Headers
* - Each summary table and details section begins with a 'header'
* row. This row contains a section title (marked by
* 'span.table-header') as well as a show/hide private link
* (marked by 'span.options', defined above).
* - Summary tables that contain user-defined groups mark those
* groups using 'group header' rows.
*/
td.table-header { background: #70b0ff; color: #000000;
border: 1px solid #608090; }
td.table-header table { color: #000000; }
td.table-header table a:link { color: #0000ff; }
td.table-header table a:visited { color: #204080; }
span.table-header { font-size: 120%; font-weight: bold; }
th.group-header { background: #c0e0f8; color: #000000;
text-align: left; font-style: italic;
font-size: 115%;
border: 1px solid #608090; }
/* Summary Tables (functions, variables, etc)
* - Each object is described by a single row of the table with
* two cells. The left cell gives the object's type, and is
* marked with 'code.summary-type'. The right cell gives the
* object's name and a summary description.
* - CSS styles for the table's header and group headers are
* defined above, under 'Table Headers'
*/
table.summary { border-collapse: collapse;
background: #e8f0f8; color: #000000;
border: 1px solid #608090;
margin-bottom: 0.5em; }
td.summary { border: 1px solid #608090; }
code.summary-type { font-size: 85%; }
table.summary a:link { color: #0000ff; }
table.summary a:visited { color: #204080; }
/* Details Tables (functions, variables, etc)
* - Each object is described in its own div.
* - A single-row summary table w/ table-header is used as
* a header for each details section (CSS style for table-header
* is defined above, under 'Table Headers').
*/
table.details { border-collapse: collapse;
background: #e8f0f8; color: #000000;
border: 1px solid #608090;
margin: .2em 0 0 0; }
table.details table { color: #000000; }
table.details a:link { color: #0000ff; }
table.details a:visited { color: #204080; }
/* Fields */
dl.fields { margin-left: 2em; margin-top: 1em;
margin-bottom: 1em; }
dl.fields dd ul { margin-left: 0em; padding-left: 0em; }
dl.fields dd ul li ul { margin-left: 2em; padding-left: 0em; }
div.fields { margin-left: 2em; }
div.fields p { margin-bottom: 0.5em; }
/* Index tables (identifier index, term index, etc)
* - link-index is used for indices containing lists of links
* (namely, the identifier index & term index).
* - index-where is used in link indices for the text indicating
* the container/source for each link.
* - metadata-index is used for indices containing metadata
* extracted from fields (namely, the bug index & todo index).
*/
table.link-index { border-collapse: collapse;
background: #e8f0f8; color: #000000;
border: 1px solid #608090; }
td.link-index { border-width: 0px; }
table.link-index a:link { color: #0000ff; }
table.link-index a:visited { color: #204080; }
span.index-where { font-size: 70%; }
table.metadata-index { border-collapse: collapse;
background: #e8f0f8; color: #000000;
border: 1px solid #608090;
margin: .2em 0 0 0; }
td.metadata-index { border-width: 1px; border-style: solid; }
table.metadata-index a:link { color: #0000ff; }
table.metadata-index a:visited { color: #204080; }
/* Function signatures
* - sig* is used for the signature in the details section.
* - .summary-sig* is used for the signature in the summary
* table, and when listing property accessor functions.
* */
.sig-name { color: #006080; }
.sig-arg { color: #008060; }
.sig-default { color: #602000; }
.summary-sig { font-family: monospace; }
.summary-sig-name { color: #006080; font-weight: bold; }
table.summary a.summary-sig-name:link
{ color: #006080; font-weight: bold; }
table.summary a.summary-sig-name:visited
{ color: #006080; font-weight: bold; }
.summary-sig-arg { color: #006040; }
.summary-sig-default { color: #501800; }
/* Subclass list
*/
ul.subclass-list { display: inline; }
ul.subclass-list li { display: inline; }
/* To render variables, classes etc. like functions */
table.summary .summary-name { color: #006080; font-weight: bold;
font-family: monospace; }
table.summary
a.summary-name:link { color: #006080; font-weight: bold;
font-family: monospace; }
table.summary
a.summary-name:visited { color: #006080; font-weight: bold;
font-family: monospace; }
/* Variable values
* - In the 'variable details' sections, each varaible's value is
* listed in a 'pre.variable' box. The width of this box is
* restricted to 80 chars; if the value's repr is longer than
* this it will be wrapped, using a backslash marked with
* class 'variable-linewrap'. If the value's repr is longer
* than 3 lines, the rest will be ellided; and an ellipsis
* marker ('...' marked with 'variable-ellipsis') will be used.
* - If the value is a string, its quote marks will be marked
* with 'variable-quote'.
* - If the variable is a regexp, it is syntax-highlighted using
* the re* CSS classes.
*/
pre.variable { padding: .5em; margin: 0;
background: #dce4ec; color: #000000;
border: 1px solid #708890; }
.variable-linewrap { color: #604000; font-weight: bold; }
.variable-ellipsis { color: #604000; font-weight: bold; }
.variable-quote { color: #604000; font-weight: bold; }
.variable-group { color: #008000; font-weight: bold; }
.variable-op { color: #604000; font-weight: bold; }
.variable-string { color: #006030; }
.variable-unknown { color: #a00000; font-weight: bold; }
.re { color: #000000; }
.re-char { color: #006030; }
.re-op { color: #600000; }
.re-group { color: #003060; }
.re-ref { color: #404040; }
/* Base tree
* - Used by class pages to display the base class hierarchy.
*/
pre.base-tree { font-size: 80%; margin: 0; }
/* Frames-based table of contents headers
* - Consists of two frames: one for selecting modules; and
* the other listing the contents of the selected module.
* - h1.toc is used for each frame's heading
* - h2.toc is used for subheadings within each frame.
*/
h1.toc { text-align: center; font-size: 105%;
margin: 0; font-weight: bold;
padding: 0; }
h2.toc { font-size: 100%; font-weight: bold;
margin: 0.5em 0 0 -0.3em; }
/* Syntax Highlighting for Source Code
* - doctest examples are displayed in a 'pre.py-doctest' block.
* If the example is in a details table entry, then it will use
* the colors specified by the 'table pre.py-doctest' line.
* - Source code listings are displayed in a 'pre.py-src' block.
* Each line is marked with 'span.py-line' (used to draw a line
* down the left margin, separating the code from the line
* numbers). Line numbers are displayed with 'span.py-lineno'.
* The expand/collapse block toggle button is displayed with
* 'a.py-toggle' (Note: the CSS style for 'a.py-toggle' should not
* modify the font size of the text.)
* - If a source code page is opened with an anchor, then the
* corresponding code block will be highlighted. The code
* block's header is highlighted with 'py-highlight-hdr'; and
* the code block's body is highlighted with 'py-highlight'.
* - The remaining py-* classes are used to perform syntax
* highlighting (py-string for string literals, py-name for names,
* etc.)
*/
pre.py-doctest { padding: .5em; margin: 1em;
background: #e8f0f8; color: #000000;
border: 1px solid #708890; }
table pre.py-doctest { background: #dce4ec;
color: #000000; }
pre.py-src { border: 2px solid #000000;
background: #f0f0f0; color: #000000; }
.py-line { border-left: 2px solid #000000;
margin-left: .2em; padding-left: .4em; }
.py-lineno { font-style: italic; font-size: 90%;
padding-left: .5em; }
a.py-toggle { text-decoration: none; }
div.py-highlight-hdr { border-top: 2px solid #000000;
border-bottom: 2px solid #000000;
background: #d8e8e8; }
div.py-highlight { border-bottom: 2px solid #000000;
background: #d0e0e0; }
.py-prompt { color: #005050; font-weight: bold;}
.py-more { color: #005050; font-weight: bold;}
.py-string { color: #006030; }
.py-comment { color: #003060; }
.py-keyword { color: #600000; }
.py-output { color: #404040; }
.py-name { color: #000050; }
.py-name:link { color: #000050 !important; }
.py-name:visited { color: #000050 !important; }
.py-number { color: #005000; }
.py-defname { color: #000060; font-weight: bold; }
.py-def-name { color: #000060; font-weight: bold; }
.py-base-class { color: #000060; }
.py-param { color: #000060; }
.py-docstring { color: #006030; }
.py-decorator { color: #804020; }
/* Use this if you don't want links to names underlined: */
/*a.py-name { text-decoration: none; }*/
/* Graphs & Diagrams
* - These CSS styles are used for graphs & diagrams generated using
* Graphviz dot. 'img.graph-without-title' is used for bare
* diagrams (to remove the border created by making the image
* clickable).
*/
img.graph-without-title { border: none; }
img.graph-with-title { border: 1px solid #000000; }
span.graph-title { font-weight: bold; }
span.graph-caption { }
/* General-purpose classes
* - 'p.indent-wrapped-lines' defines a paragraph whose first line
* is not indented, but whose subsequent lines are.
* - The 'nomargin-top' class is used to remove the top margin (e.g.
* from lists). The 'nomargin' class is used to remove both the
* top and bottom margin (but not the left or right margin --
* for lists, that would cause the bullets to disappear.)
*/
p.indent-wrapped-lines { padding: 0 0 0 7em; text-indent: -7em;
margin: 0; }
.nomargin-top { margin-top: 0; }
.nomargin { margin-top: 0; margin-bottom: 0; }
/* HTML Log */
div.log-block { padding: 0; margin: .5em 0 .5em 0;
background: #e8f0f8; color: #000000;
border: 1px solid #000000; }
div.log-error { padding: .1em .3em .1em .3em; margin: 4px;
background: #ffb0b0; color: #000000;
border: 1px solid #000000; }
div.log-warning { padding: .1em .3em .1em .3em; margin: 4px;
background: #ffffb0; color: #000000;
border: 1px solid #000000; }
div.log-info { padding: .1em .3em .1em .3em; margin: 4px;
background: #b0ffb0; color: #000000;
border: 1px solid #000000; }
h2.log-hdr { background: #70b0ff; color: #000000;
margin: 0; padding: 0em 0.5em 0em 0.5em;
border-bottom: 1px solid #000000; font-size: 110%; }
p.log { font-weight: bold; margin: .5em 0 .5em 0; }
tr.opt-changed { color: #000000; font-weight: bold; }
tr.opt-default { color: #606060; }
pre.log { margin: 0; padding: 0; padding-left: 1em; }

View File

@@ -0,0 +1,293 @@
function toggle_private() {
// Search for any private/public links on this page. Store
// their old text in "cmd," so we will know what action to
// take; and change their text to the opposite action.
var cmd = "?";
var elts = document.getElementsByTagName("a");
for(var i=0; i<elts.length; i++) {
if (elts[i].className == "privatelink") {
cmd = elts[i].innerHTML;
elts[i].innerHTML = ((cmd && cmd.substr(0,4)=="show")?
"hide&nbsp;private":"show&nbsp;private");
}
}
// Update all DIVs containing private objects.
var elts = document.getElementsByTagName("div");
for(var i=0; i<elts.length; i++) {
if (elts[i].className == "private") {
elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
}
else if (elts[i].className == "public") {
elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"block":"none");
}
}
// Update all table rows containing private objects. Note, we
// use "" instead of "block" becaue IE & firefox disagree on what
// this should be (block vs table-row), and "" just gives the
// default for both browsers.
var elts = document.getElementsByTagName("tr");
for(var i=0; i<elts.length; i++) {
if (elts[i].className == "private") {
elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"");
}
}
// Update all list items containing private objects.
var elts = document.getElementsByTagName("li");
for(var i=0; i<elts.length; i++) {
if (elts[i].className == "private") {
elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?
"none":"");
}
}
// Update all list items containing private objects.
var elts = document.getElementsByTagName("ul");
for(var i=0; i<elts.length; i++) {
if (elts[i].className == "private") {
elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
}
}
// Set a cookie to remember the current option.
document.cookie = "EpydocPrivate="+cmd;
}
function show_private() {
var elts = document.getElementsByTagName("a");
for(var i=0; i<elts.length; i++) {
if (elts[i].className == "privatelink") {
cmd = elts[i].innerHTML;
if (cmd && cmd.substr(0,4)=="show")
toggle_private();
}
}
}
function getCookie(name) {
var dc = document.cookie;
var prefix = name + "=";
var begin = dc.indexOf("; " + prefix);
if (begin == -1) {
begin = dc.indexOf(prefix);
if (begin != 0) return null;
} else
{ begin += 2; }
var end = document.cookie.indexOf(";", begin);
if (end == -1)
{ end = dc.length; }
return unescape(dc.substring(begin + prefix.length, end));
}
function setFrame(url1, url2) {
parent.frames[1].location.href = url1;
parent.frames[2].location.href = url2;
}
function checkCookie() {
var cmd=getCookie("EpydocPrivate");
if (cmd && cmd.substr(0,4)!="show" && location.href.indexOf("#_") < 0)
toggle_private();
}
function toggleCallGraph(id) {
var elt = document.getElementById(id);
if (elt.style.display == "none")
elt.style.display = "block";
else
elt.style.display = "none";
}
function expand(id) {
var elt = document.getElementById(id+"-expanded");
if (elt) elt.style.display = "block";
var elt = document.getElementById(id+"-expanded-linenums");
if (elt) elt.style.display = "block";
var elt = document.getElementById(id+"-collapsed");
if (elt) { elt.innerHTML = ""; elt.style.display = "none"; }
var elt = document.getElementById(id+"-collapsed-linenums");
if (elt) { elt.innerHTML = ""; elt.style.display = "none"; }
var elt = document.getElementById(id+"-toggle");
if (elt) { elt.innerHTML = "-"; }
}
function collapse(id) {
var elt = document.getElementById(id+"-expanded");
if (elt) elt.style.display = "none";
var elt = document.getElementById(id+"-expanded-linenums");
if (elt) elt.style.display = "none";
var elt = document.getElementById(id+"-collapsed-linenums");
if (elt) { elt.innerHTML = "<br />"; elt.style.display="block"; }
var elt = document.getElementById(id+"-toggle");
if (elt) { elt.innerHTML = "+"; }
var elt = document.getElementById(id+"-collapsed");
if (elt) {
elt.style.display = "block";
var indent = elt.getAttribute("indent");
var pad = elt.getAttribute("pad");
var s = "<tt class='py-lineno'>";
for (var i=0; i<pad.length; i++) { s += "&nbsp;" }
s += "</tt>";
s += "&nbsp;&nbsp;<tt class='py-line'>";
for (var i=0; i<indent.length; i++) { s += "&nbsp;" }
s += "<a href='#' onclick='expand(\"" + id;
s += "\");return false'>...</a></tt><br />";
elt.innerHTML = s;
}
}
function toggle(id) {
elt = document.getElementById(id+"-toggle");
if (elt.innerHTML == "-")
collapse(id);
else
expand(id);
return false;
}
function highlight(id) {
var elt = document.getElementById(id+"-def");
if (elt) elt.className = "py-highlight-hdr";
var elt = document.getElementById(id+"-expanded");
if (elt) elt.className = "py-highlight";
var elt = document.getElementById(id+"-collapsed");
if (elt) elt.className = "py-highlight";
}
function num_lines(s) {
var n = 1;
var pos = s.indexOf("\n");
while ( pos > 0) {
n += 1;
pos = s.indexOf("\n", pos+1);
}
return n;
}
// Collapse all blocks that mave more than `min_lines` lines.
function collapse_all(min_lines) {
var elts = document.getElementsByTagName("div");
for (var i=0; i<elts.length; i++) {
var elt = elts[i];
var split = elt.id.indexOf("-");
if (split > 0)
if (elt.id.substring(split, elt.id.length) == "-expanded")
if (num_lines(elt.innerHTML) > min_lines)
collapse(elt.id.substring(0, split));
}
}
function expandto(href) {
var start = href.indexOf("#")+1;
if (start != 0 && start != href.length) {
if (href.substring(start, href.length) != "-") {
collapse_all(4);
pos = href.indexOf(".", start);
while (pos != -1) {
var id = href.substring(start, pos);
expand(id);
pos = href.indexOf(".", pos+1);
}
var id = href.substring(start, href.length);
expand(id);
highlight(id);
}
}
}
function kill_doclink(id) {
var parent = document.getElementById(id);
parent.removeChild(parent.childNodes.item(0));
}
function auto_kill_doclink(ev) {
if (!ev) var ev = window.event;
if (!this.contains(ev.toElement)) {
var parent = document.getElementById(this.parentID);
parent.removeChild(parent.childNodes.item(0));
}
}
function doclink(id, name, targets_id) {
var elt = document.getElementById(id);
// If we already opened the box, then destroy it.
// (This case should never occur, but leave it in just in case.)
if (elt.childNodes.length > 1) {
elt.removeChild(elt.childNodes.item(0));
}
else {
// The outer box: relative + inline positioning.
var box1 = document.createElement("div");
box1.style.position = "relative";
box1.style.display = "inline";
box1.style.top = 0;
box1.style.left = 0;
// A shadow for fun
var shadow = document.createElement("div");
shadow.style.position = "absolute";
shadow.style.left = "-1.3em";
shadow.style.top = "-1.3em";
shadow.style.background = "#404040";
// The inner box: absolute positioning.
var box2 = document.createElement("div");
box2.style.position = "relative";
box2.style.border = "1px solid #a0a0a0";
box2.style.left = "-.2em";
box2.style.top = "-.2em";
box2.style.background = "white";
box2.style.padding = ".3em .4em .3em .4em";
box2.style.fontStyle = "normal";
box2.onmouseout=auto_kill_doclink;
box2.parentID = id;
// Get the targets
var targets_elt = document.getElementById(targets_id);
var targets = targets_elt.getAttribute("targets");
var links = "";
target_list = targets.split(",");
for (var i=0; i<target_list.length; i++) {
var target = target_list[i].split("=");
links += "<li><a href='" + target[1] +
"' style='text-decoration:none'>" +
target[0] + "</a></li>";
}
// Put it all together.
elt.insertBefore(box1, elt.childNodes.item(0));
//box1.appendChild(box2);
box1.appendChild(shadow);
shadow.appendChild(box2);
box2.innerHTML =
"Which <b>"+name+"</b> do you want to see documentation for?" +
"<ul style='margin-bottom: 0;'>" +
links +
"<li><a href='#' style='text-decoration:none' " +
"onclick='kill_doclink(\""+id+"\");return false;'>"+
"<i>None of the above</i></a></li></ul>";
}
return false;
}
function get_anchor() {
var href = location.href;
var start = href.indexOf("#")+1;
if ((start != 0) && (start != href.length))
return href.substring(start, href.length);
}
function redirect_url(dottedName) {
// Scan through each element of the "pages" list, and check
// if "name" matches with any of them.
for (var i=0; i<pages.length; i++) {
// Each page has the form "<pagename>-m" or "<pagename>-c";
// extract the <pagename> portion & compare it to dottedName.
var pagename = pages[i].substring(0, pages[i].length-2);
if (pagename == dottedName.substring(0,pagename.length)) {
// We've found a page that matches `dottedName`;
// construct its URL, using leftover `dottedName`
// content to form an anchor.
var pagetype = pages[i].charAt(pages[i].length-1);
var url = pagename + ((pagetype=="m")?"-module.html":
"-class.html");
if (dottedName.length > pagename.length)
url += "#" + dottedName.substring(pagename.length+1,
dottedName.length);
return url;
}
}
}

View File

@@ -0,0 +1,261 @@
<?xml version="1.0" encoding="ascii"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Help</title>
<link rel="stylesheet" href="epydoc.css" type="text/css" />
<script type="text/javascript" src="epydoc.js"></script>
</head>
<body bgcolor="white" text="black" link="blue" vlink="#204080"
alink="#204080">
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="rcssmin-module.html">Home</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="module-tree.html">Trees</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Help&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0">
<tr valign="top">
<td width="100%">&nbsp;</td>
<td>
<table cellpadding="0" cellspacing="0">
<!-- hide/show private -->
</table>
</td>
</tr>
</table>
<h1 class="epydoc"> API Documentation </h1>
<p> This document contains the API (Application Programming Interface)
documentation for this project. Documentation for the Python
objects defined by the project is divided into separate pages for each
package, module, and class. The API documentation also includes two
pages containing information about the project as a whole: a trees
page, and an index page. </p>
<h2> Object Documentation </h2>
<p>Each <strong>Package Documentation</strong> page contains: </p>
<ul>
<li> A description of the package. </li>
<li> A list of the modules and sub-packages contained by the
package. </li>
<li> A summary of the classes defined by the package. </li>
<li> A summary of the functions defined by the package. </li>
<li> A summary of the variables defined by the package. </li>
<li> A detailed description of each function defined by the
package. </li>
<li> A detailed description of each variable defined by the
package. </li>
</ul>
<p>Each <strong>Module Documentation</strong> page contains:</p>
<ul>
<li> A description of the module. </li>
<li> A summary of the classes defined by the module. </li>
<li> A summary of the functions defined by the module. </li>
<li> A summary of the variables defined by the module. </li>
<li> A detailed description of each function defined by the
module. </li>
<li> A detailed description of each variable defined by the
module. </li>
</ul>
<p>Each <strong>Class Documentation</strong> page contains: </p>
<ul>
<li> A class inheritance diagram. </li>
<li> A list of known subclasses. </li>
<li> A description of the class. </li>
<li> A summary of the methods defined by the class. </li>
<li> A summary of the instance variables defined by the class. </li>
<li> A summary of the class (static) variables defined by the
class. </li>
<li> A detailed description of each method defined by the
class. </li>
<li> A detailed description of each instance variable defined by the
class. </li>
<li> A detailed description of each class (static) variable defined
by the class. </li>
</ul>
<h2> Project Documentation </h2>
<p> The <strong>Trees</strong> page contains the module and class hierarchies: </p>
<ul>
<li> The <em>module hierarchy</em> lists every package and module, with
modules grouped into packages. At the top level, and within each
package, modules and sub-packages are listed alphabetically. </li>
<li> The <em>class hierarchy</em> lists every class, grouped by base
class. If a class has more than one base class, then it will be
listed under each base class. At the top level, and under each base
class, classes are listed alphabetically. </li>
</ul>
<p> The <strong>Index</strong> page contains indices of terms and
identifiers: </p>
<ul>
<li> The <em>term index</em> lists every term indexed by any object's
documentation. For each term, the index provides links to each
place where the term is indexed. </li>
<li> The <em>identifier index</em> lists the (short) name of every package,
module, class, method, function, variable, and parameter. For each
identifier, the index provides a short description, and a link to
its documentation. </li>
</ul>
<h2> The Table of Contents </h2>
<p> The table of contents occupies the two frames on the left side of
the window. The upper-left frame displays the <em>project
contents</em>, and the lower-left frame displays the <em>module
contents</em>: </p>
<table class="help summary" border="1" cellspacing="0" cellpadding="3">
<tr style="height: 30%">
<td align="center" style="font-size: small">
Project<br />Contents<hr />...</td>
<td align="center" style="font-size: small" rowspan="2" width="70%">
API<br />Documentation<br />Frame<br /><br /><br />
</td>
</tr>
<tr>
<td align="center" style="font-size: small">
Module<br />Contents<hr />&nbsp;<br />...<br />&nbsp;
</td>
</tr>
</table><br />
<p> The <strong>project contents frame</strong> contains a list of all packages
and modules that are defined by the project. Clicking on an entry
will display its contents in the module contents frame. Clicking on a
special entry, labeled "Everything," will display the contents of
the entire project. </p>
<p> The <strong>module contents frame</strong> contains a list of every
submodule, class, type, exception, function, and variable defined by a
module or package. Clicking on an entry will display its
documentation in the API documentation frame. Clicking on the name of
the module, at the top of the frame, will display the documentation
for the module itself. </p>
<p> The "<strong>frames</strong>" and "<strong>no frames</strong>" buttons below the top
navigation bar can be used to control whether the table of contents is
displayed or not. </p>
<h2> The Navigation Bar </h2>
<p> A navigation bar is located at the top and bottom of every page.
It indicates what type of page you are currently viewing, and allows
you to go to related pages. The following table describes the labels
on the navigation bar. Note that not some labels (such as
[Parent]) are not displayed on all pages. </p>
<table class="summary" border="1" cellspacing="0" cellpadding="3" width="100%">
<tr class="summary">
<th>Label</th>
<th>Highlighted when...</th>
<th>Links to...</th>
</tr>
<tr><td valign="top"><strong>[Parent]</strong></td>
<td valign="top"><em>(never highlighted)</em></td>
<td valign="top"> the parent of the current package </td></tr>
<tr><td valign="top"><strong>[Package]</strong></td>
<td valign="top">viewing a package</td>
<td valign="top">the package containing the current object
</td></tr>
<tr><td valign="top"><strong>[Module]</strong></td>
<td valign="top">viewing a module</td>
<td valign="top">the module containing the current object
</td></tr>
<tr><td valign="top"><strong>[Class]</strong></td>
<td valign="top">viewing a class </td>
<td valign="top">the class containing the current object</td></tr>
<tr><td valign="top"><strong>[Trees]</strong></td>
<td valign="top">viewing the trees page</td>
<td valign="top"> the trees page </td></tr>
<tr><td valign="top"><strong>[Index]</strong></td>
<td valign="top">viewing the index page</td>
<td valign="top"> the index page </td></tr>
<tr><td valign="top"><strong>[Help]</strong></td>
<td valign="top">viewing the help page</td>
<td valign="top"> the help page </td></tr>
</table>
<p> The "<strong>show private</strong>" and "<strong>hide private</strong>" buttons below
the top navigation bar can be used to control whether documentation
for private objects is displayed. Private objects are usually defined
as objects whose (short) names begin with a single underscore, but do
not end with an underscore. For example, "<code>_x</code>",
"<code>__pprint</code>", and "<code>epydoc.epytext._tokenize</code>"
are private objects; but "<code>re.sub</code>",
"<code>__init__</code>", and "<code>type_</code>" are not. However,
if a module defines the "<code>__all__</code>" variable, then its
contents are used to decide which objects are private. </p>
<p> A timestamp below the bottom navigation bar indicates when each
page was last updated. </p>
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="rcssmin-module.html">Home</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="module-tree.html">Trees</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Help&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<script type="text/javascript">
<!--
// Private objects are initially displayed (because if
// javascript is turned off then we want them to be
// visible); but by default, we want to hide them. So hide
// them unless we have a cookie that says to show them.
checkCookie();
// -->
</script>
</body>
</html>

View File

@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="ascii"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Identifier Index</title>
<link rel="stylesheet" href="epydoc.css" type="text/css" />
<script type="text/javascript" src="epydoc.js"></script>
</head>
<body bgcolor="white" text="black" link="blue" vlink="#204080"
alink="#204080">
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="rcssmin-module.html">Home</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="module-tree.html">Trees</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Indices&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0">
<tr valign="top">
<td width="100%">&nbsp;</td>
<td>
<table cellpadding="0" cellspacing="0">
<!-- hide/show private -->
</table>
</td>
</tr>
</table>
<table border="0" width="100%">
<tr valign="bottom"><td>
<h1 class="epydoc">Identifier Index</h1>
</td><td>
[
A
B
<a href="#C">C</a>
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
<a href="#R">R</a>
S
T
U
V
W
X
Y
Z
<a href="#_">_</a>
]
</td></table>
<table border="0" width="100%">
<tr valign="top"><td valign="top" width="1%"><h2 class="epydoc"><a name="C">C</a></h2></td>
<td valign="top">
<table class="link-index" width="100%" border="1">
<tr>
<td width="33%" class="link-index"><a href="rcssmin-module.html#cssmin">cssmin()</a><br />
<span class="index-where">(in&nbsp;<a href="rcssmin-module.html">rcssmin</a>)</span></td>
<td width="33%" class="link-index">&nbsp;</td>
<td width="33%" class="link-index">&nbsp;</td>
</tr>
<tr><td class="link-index">&nbsp;</td><td class="link-index">&nbsp;</td><td class="link-index">&nbsp;</td></tr>
</table>
</td></tr>
<tr valign="top"><td valign="top" width="1%"><h2 class="epydoc"><a name="R">R</a></h2></td>
<td valign="top">
<table class="link-index" width="100%" border="1">
<tr>
<td width="33%" class="link-index"><a href="rcssmin-module.html">rcssmin</a></td>
<td width="33%" class="link-index">&nbsp;</td>
<td width="33%" class="link-index">&nbsp;</td>
</tr>
<tr><td class="link-index">&nbsp;</td><td class="link-index">&nbsp;</td><td class="link-index">&nbsp;</td></tr>
</table>
</td></tr>
<tr valign="top"><td valign="top" width="1%"><h2 class="epydoc"><a name="_">_</a></h2></td>
<td valign="top">
<table class="link-index" width="100%" border="1">
<tr>
<td width="33%" class="link-index"><a href="rcssmin-module.html#__doc__">__doc__</a><br />
<span class="index-where">(in&nbsp;<a href="rcssmin-module.html">rcssmin</a>)</span></td>
<td width="33%" class="link-index"><a href="rcssmin-module.html#__license__">__license__</a><br />
<span class="index-where">(in&nbsp;<a href="rcssmin-module.html">rcssmin</a>)</span></td>
<td width="33%" class="link-index"><a href="rcssmin-module.html#__package__">__package__</a><br />
<span class="index-where">(in&nbsp;<a href="rcssmin-module.html">rcssmin</a>)</span></td>
</tr>
<tr><td class="link-index">&nbsp;</td><td class="link-index">&nbsp;</td><td class="link-index">&nbsp;</td></tr>
</table>
</td></tr>
</table>
<br /><br /><!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="rcssmin-module.html">Home</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="module-tree.html">Trees</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Indices&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<script type="text/javascript">
<!--
// Private objects are initially displayed (because if
// javascript is turned off then we want them to be
// visible); but by default, we want to hide them. So hide
// them unless we have a cookie that says to show them.
checkCookie();
// -->
</script>
</body>
</html>

View File

@@ -0,0 +1,224 @@
<?xml version="1.0" encoding="ascii"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>rcssmin</title>
<link rel="stylesheet" href="epydoc.css" type="text/css" />
<script type="text/javascript" src="epydoc.js"></script>
</head>
<body bgcolor="white" text="black" link="blue" vlink="#204080"
alink="#204080">
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Home&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="module-tree.html">Trees</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0">
<tr valign="top">
<td width="100%">
<span class="breadcrumbs">
Module&nbsp;rcssmin
</span>
</td>
<td>
<table cellpadding="0" cellspacing="0">
<!-- hide/show private -->
</table>
</td>
</tr>
</table>
<!-- ==================== MODULE DESCRIPTION ==================== -->
<h1 class="epydoc">Module rcssmin</h1><p class="nomargin-top"><span class="codelink"><a href="rcssmin-pysrc.html">source&nbsp;code</a></span></p>
<p>CSS Minifier.</p>
<p>The minifier is based on the semantics of the <a class="rst-reference external" href="https://github.com/yui/yuicompressor/" target="_top">YUI compressor</a>, which
itself is based on <a class="rst-reference external" href="https://github.com/isaacs/cssmin/" target="_top">the rule list by Isaac Schlueter</a>.</p>
<p>This module is a re-implementation aiming for speed instead of maximum
compression, so it can be used at runtime (rather than during a preprocessing
step). RCSSmin does syntactical compression only (removing spaces, comments
and possibly semicolons). It does not provide semantic compression (like
removing empty blocks, collapsing redundant properties etc). It does, however,
support various CSS hacks (by keeping them working as intended).</p>
<p>Here's a feature list:</p>
<ul class="rst-simple">
<li>Strings are kept, except that escaped newlines are stripped</li>
<li>Space/Comments before the very end or before various characters are
stripped: <tt class="rst-docutils literal"><span class="pre">:{});=&gt;+],!</span></tt> (The colon (<tt class="rst-docutils literal">:</tt>) is a special case, a single
space is kept if it's outside a ruleset.)</li>
<li>Space/Comments at the very beginning or after various characters are
stripped: <tt class="rst-docutils literal"><span class="pre">{}(=:&gt;+[,!</span></tt></li>
<li>Optional space after unicode escapes is kept, resp. replaced by a simple
space</li>
<li>whitespaces inside <tt class="rst-docutils literal">url()</tt> definitions are stripped</li>
<li>Comments starting with an exclamation mark (<tt class="rst-docutils literal">!</tt>) can be kept optionally.</li>
<li>All other comments and/or whitespace characters are replaced by a single
space.</li>
<li>Multiple consecutive semicolons are reduced to one</li>
<li>The last semicolon within a ruleset is stripped</li>
<li>CSS Hacks supported:<ul>
<li>IE7 hack (<tt class="rst-docutils literal"><span class="pre">&gt;/**/</span></tt>)</li>
<li>Mac-IE5 hack (<tt class="rst-docutils literal"><span class="pre">/*\*/.../**/</span></tt>)</li>
<li>The boxmodelhack is supported naturally because it relies on valid CSS2
strings</li>
<li>Between <tt class="rst-docutils literal"><span class="pre">:first-line</span></tt> and the following comma or curly brace a space is
inserted. (apparently it's needed for IE6)</li>
<li>Same for <tt class="rst-docutils literal"><span class="pre">:first-letter</span></tt></li>
</ul>
</li>
</ul>
<p>rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
factor 100 or so (depending on the input). docs/BENCHMARKS in the source
distribution contains the details.</p>
<p>Both python 2 (&gt;= 2.4) and python 3 are supported.</p>
<hr />
<div class="fields"> <p><strong>Copyright:</strong>
Copyright 2011 - 2014
Andr&#233; Malo or his licensors, as applicable
</p>
<p><strong>License:</strong>
<p>Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at</p>
<blockquote>
<a class="rst-reference external" href="http://www.apache.org/licenses/LICENSE-2.0" target="_top">http://www.apache.org/licenses/LICENSE-2.0</a></blockquote>
<p>Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.</p>
</p>
<p><strong>Version:</strong>
1.0.5
</p>
<p><strong>Author:</strong>
Andr&#233; Malo
</p>
</div><!-- ==================== FUNCTIONS ==================== -->
<a name="section-Functions"></a>
<table class="summary" border="1" cellpadding="3"
cellspacing="0" width="100%" bgcolor="white">
<tr bgcolor="#70b0f0" class="table-header">
<td align="left" colspan="2" class="table-header">
<span class="table-header">Functions</span></td>
</tr>
<tr>
<td width="15%" align="right" valign="top" class="summary">
<span class="summary-type"><tt class="rst-docutils literal">str</tt></span>
</td><td class="summary">
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td><span class="summary-sig"><a href="rcssmin-module.html#cssmin" class="summary-sig-name">cssmin</a>(<span class="summary-sig-arg">style</span>,
<span class="summary-sig-arg">keep_bang_comments</span>=<span class="summary-sig-default">False</span>)</span><br />
Minify CSS.</td>
<td align="right" valign="top">
<span class="codelink"><a href="rcssmin-pysrc.html#cssmin">source&nbsp;code</a></span>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!-- ==================== FUNCTION DETAILS ==================== -->
<a name="section-FunctionDetails"></a>
<table class="details" border="1" cellpadding="3"
cellspacing="0" width="100%" bgcolor="white">
<tr bgcolor="#70b0f0" class="table-header">
<td align="left" colspan="2" class="table-header">
<span class="table-header">Function Details</span></td>
</tr>
</table>
<a name="cssmin"></a>
<div>
<table class="details" border="1" cellpadding="3"
cellspacing="0" width="100%" bgcolor="white">
<tr><td>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr valign="top"><td>
<h3 class="epydoc"><span class="sig"><span class="sig-name">cssmin</span>(<span class="sig-arg">style</span>,
<span class="sig-arg">keep_bang_comments</span>=<span class="sig-default">False</span>)</span>
</h3>
</td><td align="right" valign="top"
><span class="codelink"><a href="rcssmin-pysrc.html#cssmin">source&nbsp;code</a></span>&nbsp;
</td>
</tr></table>
Minify CSS.
<dl class="fields">
<dt>Parameters:</dt>
<dd><ul class="nomargin-top">
<li><strong class="pname"><code>style</code></strong> (<tt class="rst-docutils literal">str</tt>) - CSS to minify</li>
<li><strong class="pname"><code>keep_bang_comments</code></strong> (<tt class="rst-docutils literal">bool</tt>) - Keep comments starting with an exclamation mark? (<tt class="rst-docutils literal"><span class="pre">/*!...*/</span></tt>)</li>
</ul></dd>
<dt>Returns: <tt class="rst-docutils literal">str</tt></dt>
<dd>Minified style</dd>
</dl>
</td></tr></table>
</div>
<br />
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Home&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="module-tree.html">Trees</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<script type="text/javascript">
<!--
// Private objects are initially displayed (because if
// javascript is turned off then we want them to be
// visible); but by default, we want to hide them. So hide
// them unless we have a cookie that says to show them.
checkCookie();
// -->
</script>
</body>
</html>

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="ascii"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Module Hierarchy</title>
<link rel="stylesheet" href="epydoc.css" type="text/css" />
<script type="text/javascript" src="epydoc.js"></script>
</head>
<body bgcolor="white" text="black" link="blue" vlink="#204080"
alink="#204080">
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="rcssmin-module.html">Home</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Trees&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0">
<tr valign="top">
<td width="100%">&nbsp;</td>
<td>
<table cellpadding="0" cellspacing="0">
<!-- hide/show private -->
</table>
</td>
</tr>
</table>
<h1 class="epydoc">Module Hierarchy</h1>
<ul class="nomargin-top">
<li> <strong class="uidlink"><a href="rcssmin-module.html">rcssmin</a></strong>: <em class="summary">CSS Minifier.</em> </li>
</ul>
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="rcssmin-module.html">Home</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Trees&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<script type="text/javascript">
<!--
// Private objects are initially displayed (because if
// javascript is turned off then we want them to be
// visible); but by default, we want to hide them. So hide
// them unless we have a cookie that says to show them.
checkCookie();
// -->
</script>
</body>
</html>

View File

@@ -0,0 +1,224 @@
<?xml version="1.0" encoding="ascii"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>rcssmin</title>
<link rel="stylesheet" href="epydoc.css" type="text/css" />
<script type="text/javascript" src="epydoc.js"></script>
</head>
<body bgcolor="white" text="black" link="blue" vlink="#204080"
alink="#204080">
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Home&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="module-tree.html">Trees</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0">
<tr valign="top">
<td width="100%">
<span class="breadcrumbs">
Module&nbsp;rcssmin
</span>
</td>
<td>
<table cellpadding="0" cellspacing="0">
<!-- hide/show private -->
</table>
</td>
</tr>
</table>
<!-- ==================== MODULE DESCRIPTION ==================== -->
<h1 class="epydoc">Module rcssmin</h1><p class="nomargin-top"><span class="codelink"><a href="rcssmin-pysrc.html">source&nbsp;code</a></span></p>
<p>CSS Minifier.</p>
<p>The minifier is based on the semantics of the <a class="rst-reference external" href="https://github.com/yui/yuicompressor/" target="_top">YUI compressor</a>, which
itself is based on <a class="rst-reference external" href="https://github.com/isaacs/cssmin/" target="_top">the rule list by Isaac Schlueter</a>.</p>
<p>This module is a re-implementation aiming for speed instead of maximum
compression, so it can be used at runtime (rather than during a preprocessing
step). RCSSmin does syntactical compression only (removing spaces, comments
and possibly semicolons). It does not provide semantic compression (like
removing empty blocks, collapsing redundant properties etc). It does, however,
support various CSS hacks (by keeping them working as intended).</p>
<p>Here's a feature list:</p>
<ul class="rst-simple">
<li>Strings are kept, except that escaped newlines are stripped</li>
<li>Space/Comments before the very end or before various characters are
stripped: <tt class="rst-docutils literal"><span class="pre">:{});=&gt;+],!</span></tt> (The colon (<tt class="rst-docutils literal">:</tt>) is a special case, a single
space is kept if it's outside a ruleset.)</li>
<li>Space/Comments at the very beginning or after various characters are
stripped: <tt class="rst-docutils literal"><span class="pre">{}(=:&gt;+[,!</span></tt></li>
<li>Optional space after unicode escapes is kept, resp. replaced by a simple
space</li>
<li>whitespaces inside <tt class="rst-docutils literal">url()</tt> definitions are stripped</li>
<li>Comments starting with an exclamation mark (<tt class="rst-docutils literal">!</tt>) can be kept optionally.</li>
<li>All other comments and/or whitespace characters are replaced by a single
space.</li>
<li>Multiple consecutive semicolons are reduced to one</li>
<li>The last semicolon within a ruleset is stripped</li>
<li>CSS Hacks supported:<ul>
<li>IE7 hack (<tt class="rst-docutils literal"><span class="pre">&gt;/**/</span></tt>)</li>
<li>Mac-IE5 hack (<tt class="rst-docutils literal"><span class="pre">/*\*/.../**/</span></tt>)</li>
<li>The boxmodelhack is supported naturally because it relies on valid CSS2
strings</li>
<li>Between <tt class="rst-docutils literal"><span class="pre">:first-line</span></tt> and the following comma or curly brace a space is
inserted. (apparently it's needed for IE6)</li>
<li>Same for <tt class="rst-docutils literal"><span class="pre">:first-letter</span></tt></li>
</ul>
</li>
</ul>
<p>rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
factor 100 or so (depending on the input). docs/BENCHMARKS in the source
distribution contains the details.</p>
<p>Both python 2 (&gt;= 2.4) and python 3 are supported.</p>
<hr />
<div class="fields"> <p><strong>Copyright:</strong>
Copyright 2011 - 2014
Andr&#233; Malo or his licensors, as applicable
</p>
<p><strong>License:</strong>
<p>Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at</p>
<blockquote>
<a class="rst-reference external" href="http://www.apache.org/licenses/LICENSE-2.0" target="_top">http://www.apache.org/licenses/LICENSE-2.0</a></blockquote>
<p>Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.</p>
</p>
<p><strong>Version:</strong>
1.0.5
</p>
<p><strong>Author:</strong>
Andr&#233; Malo
</p>
</div><!-- ==================== FUNCTIONS ==================== -->
<a name="section-Functions"></a>
<table class="summary" border="1" cellpadding="3"
cellspacing="0" width="100%" bgcolor="white">
<tr bgcolor="#70b0f0" class="table-header">
<td align="left" colspan="2" class="table-header">
<span class="table-header">Functions</span></td>
</tr>
<tr>
<td width="15%" align="right" valign="top" class="summary">
<span class="summary-type"><tt class="rst-docutils literal">str</tt></span>
</td><td class="summary">
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td><span class="summary-sig"><a href="rcssmin-module.html#cssmin" class="summary-sig-name">cssmin</a>(<span class="summary-sig-arg">style</span>,
<span class="summary-sig-arg">keep_bang_comments</span>=<span class="summary-sig-default">False</span>)</span><br />
Minify CSS.</td>
<td align="right" valign="top">
<span class="codelink"><a href="rcssmin-pysrc.html#cssmin">source&nbsp;code</a></span>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!-- ==================== FUNCTION DETAILS ==================== -->
<a name="section-FunctionDetails"></a>
<table class="details" border="1" cellpadding="3"
cellspacing="0" width="100%" bgcolor="white">
<tr bgcolor="#70b0f0" class="table-header">
<td align="left" colspan="2" class="table-header">
<span class="table-header">Function Details</span></td>
</tr>
</table>
<a name="cssmin"></a>
<div>
<table class="details" border="1" cellpadding="3"
cellspacing="0" width="100%" bgcolor="white">
<tr><td>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr valign="top"><td>
<h3 class="epydoc"><span class="sig"><span class="sig-name">cssmin</span>(<span class="sig-arg">style</span>,
<span class="sig-arg">keep_bang_comments</span>=<span class="sig-default">False</span>)</span>
</h3>
</td><td align="right" valign="top"
><span class="codelink"><a href="rcssmin-pysrc.html#cssmin">source&nbsp;code</a></span>&nbsp;
</td>
</tr></table>
Minify CSS.
<dl class="fields">
<dt>Parameters:</dt>
<dd><ul class="nomargin-top">
<li><strong class="pname"><code>style</code></strong> (<tt class="rst-docutils literal">str</tt>) - CSS to minify</li>
<li><strong class="pname"><code>keep_bang_comments</code></strong> (<tt class="rst-docutils literal">bool</tt>) - Keep comments starting with an exclamation mark? (<tt class="rst-docutils literal"><span class="pre">/*!...*/</span></tt>)</li>
</ul></dd>
<dt>Returns: <tt class="rst-docutils literal">str</tt></dt>
<dd>Minified style</dd>
</dl>
</td></tr></table>
</div>
<br />
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Home&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="module-tree.html">Trees</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<script type="text/javascript">
<!--
// Private objects are initially displayed (because if
// javascript is turned off then we want them to be
// visible); but by default, we want to hide them. So hide
// them unless we have a cookie that says to show them.
checkCookie();
// -->
</script>
</body>
</html>

View File

@@ -0,0 +1,477 @@
<?xml version="1.0" encoding="ascii"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>rcssmin</title>
<link rel="stylesheet" href="epydoc.css" type="text/css" />
<script type="text/javascript" src="epydoc.js"></script>
</head>
<body bgcolor="white" text="black" link="blue" vlink="#204080"
alink="#204080">
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Home&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="module-tree.html">Trees</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0">
<tr valign="top">
<td width="100%">
<span class="breadcrumbs">
Module&nbsp;rcssmin
</span>
</td>
<td>
<table cellpadding="0" cellspacing="0">
<!-- hide/show private -->
</table>
</td>
</tr>
</table>
<h1 class="epydoc">Source Code for <a href="rcssmin-module.html">Module rcssmin</a></h1>
<pre class="py-src">
<a name="L1"></a><tt class="py-lineno"> 1</tt> <tt class="py-line"><tt class="py-comment">#!/usr/bin/env python</tt> </tt>
<a name="L2"></a><tt class="py-lineno"> 2</tt> <tt class="py-line"><tt class="py-comment"># -*- coding: ascii -*-</tt> </tt>
<a name="L3"></a><tt class="py-lineno"> 3</tt> <tt class="py-line"><tt class="py-docstring">r"""</tt> </tt>
<a name="L4"></a><tt class="py-lineno"> 4</tt> <tt class="py-line"><tt class="py-docstring">==============</tt> </tt>
<a name="L5"></a><tt class="py-lineno"> 5</tt> <tt class="py-line"><tt class="py-docstring"> CSS Minifier</tt> </tt>
<a name="L6"></a><tt class="py-lineno"> 6</tt> <tt class="py-line"><tt class="py-docstring">==============</tt> </tt>
<a name="L7"></a><tt class="py-lineno"> 7</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L8"></a><tt class="py-lineno"> 8</tt> <tt class="py-line"><tt class="py-docstring">CSS Minifier.</tt> </tt>
<a name="L9"></a><tt class="py-lineno"> 9</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L10"></a><tt class="py-lineno"> 10</tt> <tt class="py-line"><tt class="py-docstring">The minifier is based on the semantics of the `YUI compressor`_\\, which</tt> </tt>
<a name="L11"></a><tt class="py-lineno"> 11</tt> <tt class="py-line"><tt class="py-docstring">itself is based on `the rule list by Isaac Schlueter`_\\.</tt> </tt>
<a name="L12"></a><tt class="py-lineno"> 12</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L13"></a><tt class="py-lineno"> 13</tt> <tt class="py-line"><tt class="py-docstring">:Copyright:</tt> </tt>
<a name="L14"></a><tt class="py-lineno"> 14</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L15"></a><tt class="py-lineno"> 15</tt> <tt class="py-line"><tt class="py-docstring"> Copyright 2011 - 2014</tt> </tt>
<a name="L16"></a><tt class="py-lineno"> 16</tt> <tt class="py-line"><tt class="py-docstring"> Andr\xe9 Malo or his licensors, as applicable</tt> </tt>
<a name="L17"></a><tt class="py-lineno"> 17</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L18"></a><tt class="py-lineno"> 18</tt> <tt class="py-line"><tt class="py-docstring">:License:</tt> </tt>
<a name="L19"></a><tt class="py-lineno"> 19</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L20"></a><tt class="py-lineno"> 20</tt> <tt class="py-line"><tt class="py-docstring"> Licensed under the Apache License, Version 2.0 (the "License");</tt> </tt>
<a name="L21"></a><tt class="py-lineno"> 21</tt> <tt class="py-line"><tt class="py-docstring"> you may not use this file except in compliance with the License.</tt> </tt>
<a name="L22"></a><tt class="py-lineno"> 22</tt> <tt class="py-line"><tt class="py-docstring"> You may obtain a copy of the License at</tt> </tt>
<a name="L23"></a><tt class="py-lineno"> 23</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L24"></a><tt class="py-lineno"> 24</tt> <tt class="py-line"><tt class="py-docstring"> http://www.apache.org/licenses/LICENSE-2.0</tt> </tt>
<a name="L25"></a><tt class="py-lineno"> 25</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L26"></a><tt class="py-lineno"> 26</tt> <tt class="py-line"><tt class="py-docstring"> Unless required by applicable law or agreed to in writing, software</tt> </tt>
<a name="L27"></a><tt class="py-lineno"> 27</tt> <tt class="py-line"><tt class="py-docstring"> distributed under the License is distributed on an "AS IS" BASIS,</tt> </tt>
<a name="L28"></a><tt class="py-lineno"> 28</tt> <tt class="py-line"><tt class="py-docstring"> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</tt> </tt>
<a name="L29"></a><tt class="py-lineno"> 29</tt> <tt class="py-line"><tt class="py-docstring"> See the License for the specific language governing permissions and</tt> </tt>
<a name="L30"></a><tt class="py-lineno"> 30</tt> <tt class="py-line"><tt class="py-docstring"> limitations under the License.</tt> </tt>
<a name="L31"></a><tt class="py-lineno"> 31</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L32"></a><tt class="py-lineno"> 32</tt> <tt class="py-line"><tt class="py-docstring">This module is a re-implementation aiming for speed instead of maximum</tt> </tt>
<a name="L33"></a><tt class="py-lineno"> 33</tt> <tt class="py-line"><tt class="py-docstring">compression, so it can be used at runtime (rather than during a preprocessing</tt> </tt>
<a name="L34"></a><tt class="py-lineno"> 34</tt> <tt class="py-line"><tt class="py-docstring">step). RCSSmin does syntactical compression only (removing spaces, comments</tt> </tt>
<a name="L35"></a><tt class="py-lineno"> 35</tt> <tt class="py-line"><tt class="py-docstring">and possibly semicolons). It does not provide semantic compression (like</tt> </tt>
<a name="L36"></a><tt class="py-lineno"> 36</tt> <tt class="py-line"><tt class="py-docstring">removing empty blocks, collapsing redundant properties etc). It does, however,</tt> </tt>
<a name="L37"></a><tt class="py-lineno"> 37</tt> <tt class="py-line"><tt class="py-docstring">support various CSS hacks (by keeping them working as intended).</tt> </tt>
<a name="L38"></a><tt class="py-lineno"> 38</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L39"></a><tt class="py-lineno"> 39</tt> <tt class="py-line"><tt class="py-docstring">Here's a feature list:</tt> </tt>
<a name="L40"></a><tt class="py-lineno"> 40</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L41"></a><tt class="py-lineno"> 41</tt> <tt class="py-line"><tt class="py-docstring">- Strings are kept, except that escaped newlines are stripped</tt> </tt>
<a name="L42"></a><tt class="py-lineno"> 42</tt> <tt class="py-line"><tt class="py-docstring">- Space/Comments before the very end or before various characters are</tt> </tt>
<a name="L43"></a><tt class="py-lineno"> 43</tt> <tt class="py-line"><tt class="py-docstring"> stripped: ``:{});=&gt;+],!`` (The colon (``:``) is a special case, a single</tt> </tt>
<a name="L44"></a><tt class="py-lineno"> 44</tt> <tt class="py-line"><tt class="py-docstring"> space is kept if it's outside a ruleset.)</tt> </tt>
<a name="L45"></a><tt class="py-lineno"> 45</tt> <tt class="py-line"><tt class="py-docstring">- Space/Comments at the very beginning or after various characters are</tt> </tt>
<a name="L46"></a><tt class="py-lineno"> 46</tt> <tt class="py-line"><tt class="py-docstring"> stripped: ``{}(=:&gt;+[,!``</tt> </tt>
<a name="L47"></a><tt class="py-lineno"> 47</tt> <tt class="py-line"><tt class="py-docstring">- Optional space after unicode escapes is kept, resp. replaced by a simple</tt> </tt>
<a name="L48"></a><tt class="py-lineno"> 48</tt> <tt class="py-line"><tt class="py-docstring"> space</tt> </tt>
<a name="L49"></a><tt class="py-lineno"> 49</tt> <tt class="py-line"><tt class="py-docstring">- whitespaces inside ``url()`` definitions are stripped</tt> </tt>
<a name="L50"></a><tt class="py-lineno"> 50</tt> <tt class="py-line"><tt class="py-docstring">- Comments starting with an exclamation mark (``!``) can be kept optionally.</tt> </tt>
<a name="L51"></a><tt class="py-lineno"> 51</tt> <tt class="py-line"><tt class="py-docstring">- All other comments and/or whitespace characters are replaced by a single</tt> </tt>
<a name="L52"></a><tt class="py-lineno"> 52</tt> <tt class="py-line"><tt class="py-docstring"> space.</tt> </tt>
<a name="L53"></a><tt class="py-lineno"> 53</tt> <tt class="py-line"><tt class="py-docstring">- Multiple consecutive semicolons are reduced to one</tt> </tt>
<a name="L54"></a><tt class="py-lineno"> 54</tt> <tt class="py-line"><tt class="py-docstring">- The last semicolon within a ruleset is stripped</tt> </tt>
<a name="L55"></a><tt class="py-lineno"> 55</tt> <tt class="py-line"><tt class="py-docstring">- CSS Hacks supported:</tt> </tt>
<a name="L56"></a><tt class="py-lineno"> 56</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L57"></a><tt class="py-lineno"> 57</tt> <tt class="py-line"><tt class="py-docstring"> - IE7 hack (``&gt;/**/``)</tt> </tt>
<a name="L58"></a><tt class="py-lineno"> 58</tt> <tt class="py-line"><tt class="py-docstring"> - Mac-IE5 hack (``/*\\*/.../**/``)</tt> </tt>
<a name="L59"></a><tt class="py-lineno"> 59</tt> <tt class="py-line"><tt class="py-docstring"> - The boxmodelhack is supported naturally because it relies on valid CSS2</tt> </tt>
<a name="L60"></a><tt class="py-lineno"> 60</tt> <tt class="py-line"><tt class="py-docstring"> strings</tt> </tt>
<a name="L61"></a><tt class="py-lineno"> 61</tt> <tt class="py-line"><tt class="py-docstring"> - Between ``:first-line`` and the following comma or curly brace a space is</tt> </tt>
<a name="L62"></a><tt class="py-lineno"> 62</tt> <tt class="py-line"><tt class="py-docstring"> inserted. (apparently it's needed for IE6)</tt> </tt>
<a name="L63"></a><tt class="py-lineno"> 63</tt> <tt class="py-line"><tt class="py-docstring"> - Same for ``:first-letter``</tt> </tt>
<a name="L64"></a><tt class="py-lineno"> 64</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L65"></a><tt class="py-lineno"> 65</tt> <tt class="py-line"><tt class="py-docstring">rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to</tt> </tt>
<a name="L66"></a><tt class="py-lineno"> 66</tt> <tt class="py-line"><tt class="py-docstring">factor 100 or so (depending on the input). docs/BENCHMARKS in the source</tt> </tt>
<a name="L67"></a><tt class="py-lineno"> 67</tt> <tt class="py-line"><tt class="py-docstring">distribution contains the details.</tt> </tt>
<a name="L68"></a><tt class="py-lineno"> 68</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L69"></a><tt class="py-lineno"> 69</tt> <tt class="py-line"><tt class="py-docstring">Both python 2 (&gt;= 2.4) and python 3 are supported.</tt> </tt>
<a name="L70"></a><tt class="py-lineno"> 70</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L71"></a><tt class="py-lineno"> 71</tt> <tt class="py-line"><tt class="py-docstring">.. _YUI compressor: https://github.com/yui/yuicompressor/</tt> </tt>
<a name="L72"></a><tt class="py-lineno"> 72</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L73"></a><tt class="py-lineno"> 73</tt> <tt class="py-line"><tt class="py-docstring">.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/</tt> </tt>
<a name="L74"></a><tt class="py-lineno"> 74</tt> <tt class="py-line"><tt class="py-docstring">"""</tt> </tt>
<a name="L75"></a><tt class="py-lineno"> 75</tt> <tt class="py-line"><tt class="py-keyword">if</tt> <tt id="link-0" class="py-name" targets="Variable rcssmin.__doc__=rcssmin-module.html#__doc__"><a title="rcssmin.__doc__" class="py-name" href="#" onclick="return doclink('link-0', '__doc__', 'link-0');">__doc__</a></tt><tt class="py-op">:</tt> </tt>
<a name="L76"></a><tt class="py-lineno"> 76</tt> <tt class="py-line"> <tt class="py-comment"># pylint: disable = W0622</tt> </tt>
<a name="L77"></a><tt class="py-lineno"> 77</tt> <tt class="py-line"> <tt id="link-1" class="py-name"><a title="rcssmin.__doc__" class="py-name" href="#" onclick="return doclink('link-1', '__doc__', 'link-0');">__doc__</a></tt> <tt class="py-op">=</tt> <tt id="link-2" class="py-name"><a title="rcssmin.__doc__" class="py-name" href="#" onclick="return doclink('link-2', '__doc__', 'link-0');">__doc__</a></tt><tt class="py-op">.</tt><tt class="py-name">encode</tt><tt class="py-op">(</tt><tt class="py-string">'ascii'</tt><tt class="py-op">)</tt><tt class="py-op">.</tt><tt class="py-name">decode</tt><tt class="py-op">(</tt><tt class="py-string">'unicode_escape'</tt><tt class="py-op">)</tt> </tt>
<a name="L78"></a><tt class="py-lineno"> 78</tt> <tt class="py-line"><tt class="py-name">__author__</tt> <tt class="py-op">=</tt> <tt class="py-string">r"Andr\xe9 Malo"</tt><tt class="py-op">.</tt><tt class="py-name">encode</tt><tt class="py-op">(</tt><tt class="py-string">'ascii'</tt><tt class="py-op">)</tt><tt class="py-op">.</tt><tt class="py-name">decode</tt><tt class="py-op">(</tt><tt class="py-string">'unicode_escape'</tt><tt class="py-op">)</tt> </tt>
<a name="L79"></a><tt class="py-lineno"> 79</tt> <tt class="py-line"><tt class="py-name">__docformat__</tt> <tt class="py-op">=</tt> <tt class="py-string">"restructuredtext en"</tt> </tt>
<a name="L80"></a><tt class="py-lineno"> 80</tt> <tt class="py-line"><tt id="link-3" class="py-name" targets="Variable rcssmin.__license__=rcssmin-module.html#__license__"><a title="rcssmin.__license__" class="py-name" href="#" onclick="return doclink('link-3', '__license__', 'link-3');">__license__</a></tt> <tt class="py-op">=</tt> <tt class="py-string">"Apache License, Version 2.0"</tt> </tt>
<a name="L81"></a><tt class="py-lineno"> 81</tt> <tt class="py-line"><tt class="py-name">__version__</tt> <tt class="py-op">=</tt> <tt class="py-string">'1.0.5'</tt> </tt>
<a name="L82"></a><tt class="py-lineno"> 82</tt> <tt class="py-line"><tt class="py-name">__all__</tt> <tt class="py-op">=</tt> <tt class="py-op">[</tt><tt class="py-string">'cssmin'</tt><tt class="py-op">]</tt> </tt>
<a name="L83"></a><tt class="py-lineno"> 83</tt> <tt class="py-line"> </tt>
<a name="L84"></a><tt class="py-lineno"> 84</tt> <tt class="py-line"><tt class="py-keyword">import</tt> <tt class="py-name">re</tt> <tt class="py-keyword">as</tt> <tt class="py-name">_re</tt> </tt>
<a name="L85"></a><tt class="py-lineno"> 85</tt> <tt class="py-line"> </tt>
<a name="L86"></a><tt class="py-lineno"> 86</tt> <tt class="py-line"> </tt>
<a name="_make_cssmin"></a><div id="_make_cssmin-def"><a name="L87"></a><tt class="py-lineno"> 87</tt> <a class="py-toggle" href="#" id="_make_cssmin-toggle" onclick="return toggle('_make_cssmin');">-</a><tt class="py-line"><tt class="py-keyword">def</tt> <a class="py-def-name" href="rcssmin-module.html#_make_cssmin">_make_cssmin</a><tt class="py-op">(</tt><tt class="py-param">python_only</tt><tt class="py-op">=</tt><tt class="py-name">False</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
</div><div id="_make_cssmin-collapsed" style="display:none;" pad="+++" indent="++++"></div><div id="_make_cssmin-expanded"><a name="L88"></a><tt class="py-lineno"> 88</tt> <tt class="py-line"> <tt class="py-docstring">"""</tt> </tt>
<a name="L89"></a><tt class="py-lineno"> 89</tt> <tt class="py-line"><tt class="py-docstring"> Generate CSS minifier.</tt> </tt>
<a name="L90"></a><tt class="py-lineno"> 90</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L91"></a><tt class="py-lineno"> 91</tt> <tt class="py-line"><tt class="py-docstring"> :Parameters:</tt> </tt>
<a name="L92"></a><tt class="py-lineno"> 92</tt> <tt class="py-line"><tt class="py-docstring"> `python_only` : ``bool``</tt> </tt>
<a name="L93"></a><tt class="py-lineno"> 93</tt> <tt class="py-line"><tt class="py-docstring"> Use only the python variant. If true, the c extension is not even</tt> </tt>
<a name="L94"></a><tt class="py-lineno"> 94</tt> <tt class="py-line"><tt class="py-docstring"> tried to be loaded.</tt> </tt>
<a name="L95"></a><tt class="py-lineno"> 95</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L96"></a><tt class="py-lineno"> 96</tt> <tt class="py-line"><tt class="py-docstring"> :Return: Minifier</tt> </tt>
<a name="L97"></a><tt class="py-lineno"> 97</tt> <tt class="py-line"><tt class="py-docstring"> :Rtype: ``callable``</tt> </tt>
<a name="L98"></a><tt class="py-lineno"> 98</tt> <tt class="py-line"><tt class="py-docstring"> """</tt> </tt>
<a name="L99"></a><tt class="py-lineno"> 99</tt> <tt class="py-line"> <tt class="py-comment"># pylint: disable = R0912, R0914, W0612</tt> </tt>
<a name="L100"></a><tt class="py-lineno">100</tt> <tt class="py-line"> </tt>
<a name="L101"></a><tt class="py-lineno">101</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-keyword">not</tt> <tt class="py-name">python_only</tt><tt class="py-op">:</tt> </tt>
<a name="L102"></a><tt class="py-lineno">102</tt> <tt class="py-line"> <tt class="py-keyword">try</tt><tt class="py-op">:</tt> </tt>
<a name="L103"></a><tt class="py-lineno">103</tt> <tt class="py-line"> <tt class="py-keyword">import</tt> <tt class="py-name">_rcssmin</tt> </tt>
<a name="L104"></a><tt class="py-lineno">104</tt> <tt class="py-line"> <tt class="py-keyword">except</tt> <tt class="py-name">ImportError</tt><tt class="py-op">:</tt> </tt>
<a name="L105"></a><tt class="py-lineno">105</tt> <tt class="py-line"> <tt class="py-keyword">pass</tt> </tt>
<a name="L106"></a><tt class="py-lineno">106</tt> <tt class="py-line"> <tt class="py-keyword">else</tt><tt class="py-op">:</tt> </tt>
<a name="L107"></a><tt class="py-lineno">107</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">_rcssmin</tt><tt class="py-op">.</tt><tt id="link-4" class="py-name" targets="Function rcssmin.cssmin()=rcssmin-module.html#cssmin"><a title="rcssmin.cssmin" class="py-name" href="#" onclick="return doclink('link-4', 'cssmin', 'link-4');">cssmin</a></tt> </tt>
<a name="L108"></a><tt class="py-lineno">108</tt> <tt class="py-line"> </tt>
<a name="L109"></a><tt class="py-lineno">109</tt> <tt class="py-line"> <tt class="py-name">nl</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:[\n\f]|\r\n?)'</tt> <tt class="py-comment"># pylint: disable = C0103</tt> </tt>
<a name="L110"></a><tt class="py-lineno">110</tt> <tt class="py-line"> <tt class="py-name">spacechar</tt> <tt class="py-op">=</tt> <tt class="py-string">r'[\r\n\f\040\t]'</tt> </tt>
<a name="L111"></a><tt class="py-lineno">111</tt> <tt class="py-line"> </tt>
<a name="L112"></a><tt class="py-lineno">112</tt> <tt class="py-line"> <tt class="py-name">unicoded</tt> <tt class="py-op">=</tt> <tt class="py-string">r'[0-9a-fA-F]{1,6}(?:[\040\n\t\f]|\r\n?)?'</tt> </tt>
<a name="L113"></a><tt class="py-lineno">113</tt> <tt class="py-line"> <tt class="py-name">escaped</tt> <tt class="py-op">=</tt> <tt class="py-string">r'[^\n\r\f0-9a-fA-F]'</tt> </tt>
<a name="L114"></a><tt class="py-lineno">114</tt> <tt class="py-line"> <tt class="py-name">escape</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:\\(?:%(unicoded)s|%(escaped)s))'</tt> <tt class="py-op">%</tt> <tt class="py-name">locals</tt><tt class="py-op">(</tt><tt class="py-op">)</tt> </tt>
<a name="L115"></a><tt class="py-lineno">115</tt> <tt class="py-line"> </tt>
<a name="L116"></a><tt class="py-lineno">116</tt> <tt class="py-line"> <tt class="py-name">nmchar</tt> <tt class="py-op">=</tt> <tt class="py-string">r'[^\000-\054\056\057\072-\100\133-\136\140\173-\177]'</tt> </tt>
<a name="L117"></a><tt class="py-lineno">117</tt> <tt class="py-line"> <tt class="py-comment">#nmstart = r'[^\000-\100\133-\136\140\173-\177]'</tt> </tt>
<a name="L118"></a><tt class="py-lineno">118</tt> <tt class="py-line"> <tt class="py-comment">#ident = (r'(?:'</tt> </tt>
<a name="L119"></a><tt class="py-lineno">119</tt> <tt class="py-line"> <tt class="py-comment"># r'-?(?:%(nmstart)s|%(escape)s)%(nmchar)s*(?:%(escape)s%(nmchar)s*)*'</tt> </tt>
<a name="L120"></a><tt class="py-lineno">120</tt> <tt class="py-line"> <tt class="py-comment">#r')') % locals()</tt> </tt>
<a name="L121"></a><tt class="py-lineno">121</tt> <tt class="py-line"> </tt>
<a name="L122"></a><tt class="py-lineno">122</tt> <tt class="py-line"> <tt class="py-name">comment</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'</tt> </tt>
<a name="L123"></a><tt class="py-lineno">123</tt> <tt class="py-line"> </tt>
<a name="L124"></a><tt class="py-lineno">124</tt> <tt class="py-line"> <tt class="py-comment"># only for specific purposes. The bang is grouped:</tt> </tt>
<a name="L125"></a><tt class="py-lineno">125</tt> <tt class="py-line"> <tt class="py-name">_bang_comment</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:/\*(!?)[^*]*\*+(?:[^/*][^*]*\*+)*/)'</tt> </tt>
<a name="L126"></a><tt class="py-lineno">126</tt> <tt class="py-line"> </tt>
<a name="L127"></a><tt class="py-lineno">127</tt> <tt class="py-line"> <tt class="py-name">string1</tt> <tt class="py-op">=</tt> \ </tt>
<a name="L128"></a><tt class="py-lineno">128</tt> <tt class="py-line"> <tt class="py-string">r'(?:\047[^\047\\\r\n\f]*(?:\\[^\r\n\f][^\047\\\r\n\f]*)*\047)'</tt> </tt>
<a name="L129"></a><tt class="py-lineno">129</tt> <tt class="py-line"> <tt class="py-name">string2</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:"[^"\\\r\n\f]*(?:\\[^\r\n\f][^"\\\r\n\f]*)*")'</tt> </tt>
<a name="L130"></a><tt class="py-lineno">130</tt> <tt class="py-line"> <tt class="py-name">strings</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:%s|%s)'</tt> <tt class="py-op">%</tt> <tt class="py-op">(</tt><tt class="py-name">string1</tt><tt class="py-op">,</tt> <tt class="py-name">string2</tt><tt class="py-op">)</tt> </tt>
<a name="L131"></a><tt class="py-lineno">131</tt> <tt class="py-line"> </tt>
<a name="L132"></a><tt class="py-lineno">132</tt> <tt class="py-line"> <tt class="py-name">nl_string1</tt> <tt class="py-op">=</tt> \ </tt>
<a name="L133"></a><tt class="py-lineno">133</tt> <tt class="py-line"> <tt class="py-string">r'(?:\047[^\047\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^\047\\\r\n\f]*)*\047)'</tt> </tt>
<a name="L134"></a><tt class="py-lineno">134</tt> <tt class="py-line"> <tt class="py-name">nl_string2</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:"[^"\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^"\\\r\n\f]*)*")'</tt> </tt>
<a name="L135"></a><tt class="py-lineno">135</tt> <tt class="py-line"> <tt class="py-name">nl_strings</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:%s|%s)'</tt> <tt class="py-op">%</tt> <tt class="py-op">(</tt><tt class="py-name">nl_string1</tt><tt class="py-op">,</tt> <tt class="py-name">nl_string2</tt><tt class="py-op">)</tt> </tt>
<a name="L136"></a><tt class="py-lineno">136</tt> <tt class="py-line"> </tt>
<a name="L137"></a><tt class="py-lineno">137</tt> <tt class="py-line"> <tt class="py-name">uri_nl_string1</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:\047[^\047\\]*(?:\\(?:[^\r]|\r\n?)[^\047\\]*)*\047)'</tt> </tt>
<a name="L138"></a><tt class="py-lineno">138</tt> <tt class="py-line"> <tt class="py-name">uri_nl_string2</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:"[^"\\]*(?:\\(?:[^\r]|\r\n?)[^"\\]*)*")'</tt> </tt>
<a name="L139"></a><tt class="py-lineno">139</tt> <tt class="py-line"> <tt class="py-name">uri_nl_strings</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:%s|%s)'</tt> <tt class="py-op">%</tt> <tt class="py-op">(</tt><tt class="py-name">uri_nl_string1</tt><tt class="py-op">,</tt> <tt class="py-name">uri_nl_string2</tt><tt class="py-op">)</tt> </tt>
<a name="L140"></a><tt class="py-lineno">140</tt> <tt class="py-line"> </tt>
<a name="L141"></a><tt class="py-lineno">141</tt> <tt class="py-line"> <tt class="py-name">nl_escaped</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:\\%(nl)s)'</tt> <tt class="py-op">%</tt> <tt class="py-name">locals</tt><tt class="py-op">(</tt><tt class="py-op">)</tt> </tt>
<a name="L142"></a><tt class="py-lineno">142</tt> <tt class="py-line"> </tt>
<a name="L143"></a><tt class="py-lineno">143</tt> <tt class="py-line"> <tt class="py-name">space</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:%(spacechar)s|%(comment)s)'</tt> <tt class="py-op">%</tt> <tt class="py-name">locals</tt><tt class="py-op">(</tt><tt class="py-op">)</tt> </tt>
<a name="L144"></a><tt class="py-lineno">144</tt> <tt class="py-line"> </tt>
<a name="L145"></a><tt class="py-lineno">145</tt> <tt class="py-line"> <tt class="py-name">ie7hack</tt> <tt class="py-op">=</tt> <tt class="py-string">r'(?:&gt;/\*\*/)'</tt> </tt>
<a name="L146"></a><tt class="py-lineno">146</tt> <tt class="py-line"> </tt>
<a name="L147"></a><tt class="py-lineno">147</tt> <tt class="py-line"> <tt class="py-name">uri</tt> <tt class="py-op">=</tt> <tt class="py-op">(</tt><tt class="py-string">r'(?:'</tt> </tt>
<a name="L148"></a><tt class="py-lineno">148</tt> <tt class="py-line"> <tt class="py-comment"># noqa pylint: disable = C0330</tt> </tt>
<a name="L149"></a><tt class="py-lineno">149</tt> <tt class="py-line"> <tt class="py-string">r'(?:[^\000-\040"\047()\\\177]*'</tt> </tt>
<a name="L150"></a><tt class="py-lineno">150</tt> <tt class="py-line"> <tt class="py-string">r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*)'</tt> </tt>
<a name="L151"></a><tt class="py-lineno">151</tt> <tt class="py-line"> <tt class="py-string">r'(?:'</tt> </tt>
<a name="L152"></a><tt class="py-lineno">152</tt> <tt class="py-line"> <tt class="py-string">r'(?:%(spacechar)s+|%(nl_escaped)s+)'</tt> </tt>
<a name="L153"></a><tt class="py-lineno">153</tt> <tt class="py-line"> <tt class="py-string">r'(?:'</tt> </tt>
<a name="L154"></a><tt class="py-lineno">154</tt> <tt class="py-line"> <tt class="py-string">r'(?:[^\000-\040"\047()\\\177]|%(escape)s|%(nl_escaped)s)'</tt> </tt>
<a name="L155"></a><tt class="py-lineno">155</tt> <tt class="py-line"> <tt class="py-string">r'[^\000-\040"\047()\\\177]*'</tt> </tt>
<a name="L156"></a><tt class="py-lineno">156</tt> <tt class="py-line"> <tt class="py-string">r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*'</tt> </tt>
<a name="L157"></a><tt class="py-lineno">157</tt> <tt class="py-line"> <tt class="py-string">r')+'</tt> </tt>
<a name="L158"></a><tt class="py-lineno">158</tt> <tt class="py-line"> <tt class="py-string">r')*'</tt> </tt>
<a name="L159"></a><tt class="py-lineno">159</tt> <tt class="py-line"> <tt class="py-string">r')'</tt><tt class="py-op">)</tt> <tt class="py-op">%</tt> <tt class="py-name">locals</tt><tt class="py-op">(</tt><tt class="py-op">)</tt> </tt>
<a name="L160"></a><tt class="py-lineno">160</tt> <tt class="py-line"> </tt>
<a name="L161"></a><tt class="py-lineno">161</tt> <tt class="py-line"> <tt class="py-name">nl_unesc_sub</tt> <tt class="py-op">=</tt> <tt class="py-name">_re</tt><tt class="py-op">.</tt><tt class="py-name">compile</tt><tt class="py-op">(</tt><tt class="py-name">nl_escaped</tt><tt class="py-op">)</tt><tt class="py-op">.</tt><tt class="py-name">sub</tt> </tt>
<a name="L162"></a><tt class="py-lineno">162</tt> <tt class="py-line"> </tt>
<a name="L163"></a><tt class="py-lineno">163</tt> <tt class="py-line"> <tt class="py-name">uri_space_sub</tt> <tt class="py-op">=</tt> <tt class="py-name">_re</tt><tt class="py-op">.</tt><tt class="py-name">compile</tt><tt class="py-op">(</tt><tt class="py-op">(</tt> </tt>
<a name="L164"></a><tt class="py-lineno">164</tt> <tt class="py-line"> <tt class="py-string">r'(%(escape)s+)|%(spacechar)s+|%(nl_escaped)s+'</tt> </tt>
<a name="L165"></a><tt class="py-lineno">165</tt> <tt class="py-line"> <tt class="py-op">)</tt> <tt class="py-op">%</tt> <tt class="py-name">locals</tt><tt class="py-op">(</tt><tt class="py-op">)</tt><tt class="py-op">)</tt><tt class="py-op">.</tt><tt class="py-name">sub</tt> </tt>
<a name="L166"></a><tt class="py-lineno">166</tt> <tt class="py-line"> <tt class="py-name">uri_space_subber</tt> <tt class="py-op">=</tt> <tt class="py-keyword">lambda</tt> <tt class="py-name">m</tt><tt class="py-op">:</tt> <tt class="py-name">m</tt><tt class="py-op">.</tt><tt class="py-name">groups</tt><tt class="py-op">(</tt><tt class="py-op">)</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-keyword">or</tt> <tt class="py-string">''</tt> </tt>
<a name="L167"></a><tt class="py-lineno">167</tt> <tt class="py-line"> </tt>
<a name="L168"></a><tt class="py-lineno">168</tt> <tt class="py-line"> <tt class="py-name">space_sub_simple</tt> <tt class="py-op">=</tt> <tt class="py-name">_re</tt><tt class="py-op">.</tt><tt class="py-name">compile</tt><tt class="py-op">(</tt><tt class="py-op">(</tt> </tt>
<a name="L169"></a><tt class="py-lineno">169</tt> <tt class="py-line"> <tt class="py-string">r'[\r\n\f\040\t;]+|(%(comment)s+)'</tt> </tt>
<a name="L170"></a><tt class="py-lineno">170</tt> <tt class="py-line"> <tt class="py-op">)</tt> <tt class="py-op">%</tt> <tt class="py-name">locals</tt><tt class="py-op">(</tt><tt class="py-op">)</tt><tt class="py-op">)</tt><tt class="py-op">.</tt><tt class="py-name">sub</tt> </tt>
<a name="L171"></a><tt class="py-lineno">171</tt> <tt class="py-line"> <tt class="py-name">space_sub_banged</tt> <tt class="py-op">=</tt> <tt class="py-name">_re</tt><tt class="py-op">.</tt><tt class="py-name">compile</tt><tt class="py-op">(</tt><tt class="py-op">(</tt> </tt>
<a name="L172"></a><tt class="py-lineno">172</tt> <tt class="py-line"> <tt class="py-string">r'[\r\n\f\040\t;]+|(%(_bang_comment)s+)'</tt> </tt>
<a name="L173"></a><tt class="py-lineno">173</tt> <tt class="py-line"> <tt class="py-op">)</tt> <tt class="py-op">%</tt> <tt class="py-name">locals</tt><tt class="py-op">(</tt><tt class="py-op">)</tt><tt class="py-op">)</tt><tt class="py-op">.</tt><tt class="py-name">sub</tt> </tt>
<a name="L174"></a><tt class="py-lineno">174</tt> <tt class="py-line"> </tt>
<a name="L175"></a><tt class="py-lineno">175</tt> <tt class="py-line"> <tt class="py-name">post_esc_sub</tt> <tt class="py-op">=</tt> <tt class="py-name">_re</tt><tt class="py-op">.</tt><tt class="py-name">compile</tt><tt class="py-op">(</tt><tt class="py-string">r'[\r\n\f\t]+'</tt><tt class="py-op">)</tt><tt class="py-op">.</tt><tt class="py-name">sub</tt> </tt>
<a name="L176"></a><tt class="py-lineno">176</tt> <tt class="py-line"> </tt>
<a name="L177"></a><tt class="py-lineno">177</tt> <tt class="py-line"> <tt class="py-name">main_sub</tt> <tt class="py-op">=</tt> <tt class="py-name">_re</tt><tt class="py-op">.</tt><tt class="py-name">compile</tt><tt class="py-op">(</tt><tt class="py-op">(</tt> </tt>
<a name="L178"></a><tt class="py-lineno">178</tt> <tt class="py-line"> <tt class="py-comment"># noqa pylint: disable = C0330</tt> </tt>
<a name="L179"></a><tt class="py-lineno">179</tt> <tt class="py-line"> <tt class="py-string">r'([^\\"\047u&gt;@\r\n\f\040\t/;:{}]+)'</tt> </tt>
<a name="L180"></a><tt class="py-lineno">180</tt> <tt class="py-line"> <tt class="py-string">r'|(?&lt;=[{}(=:&gt;+[,!])(%(space)s+)'</tt> </tt>
<a name="L181"></a><tt class="py-lineno">181</tt> <tt class="py-line"> <tt class="py-string">r'|^(%(space)s+)'</tt> </tt>
<a name="L182"></a><tt class="py-lineno">182</tt> <tt class="py-line"> <tt class="py-string">r'|(%(space)s+)(?=(([:{});=&gt;+\],!])|$)?)'</tt> </tt>
<a name="L183"></a><tt class="py-lineno">183</tt> <tt class="py-line"> <tt class="py-string">r'|;(%(space)s*(?:;%(space)s*)*)(?=(\})?)'</tt> </tt>
<a name="L184"></a><tt class="py-lineno">184</tt> <tt class="py-line"> <tt class="py-string">r'|(\{)'</tt> </tt>
<a name="L185"></a><tt class="py-lineno">185</tt> <tt class="py-line"> <tt class="py-string">r'|(\})'</tt> </tt>
<a name="L186"></a><tt class="py-lineno">186</tt> <tt class="py-line"> <tt class="py-string">r'|(%(strings)s)'</tt> </tt>
<a name="L187"></a><tt class="py-lineno">187</tt> <tt class="py-line"> <tt class="py-string">r'|(?&lt;!%(nmchar)s)url\(%(spacechar)s*('</tt> </tt>
<a name="L188"></a><tt class="py-lineno">188</tt> <tt class="py-line"> <tt class="py-string">r'%(uri_nl_strings)s'</tt> </tt>
<a name="L189"></a><tt class="py-lineno">189</tt> <tt class="py-line"> <tt class="py-string">r'|%(uri)s'</tt> </tt>
<a name="L190"></a><tt class="py-lineno">190</tt> <tt class="py-line"> <tt class="py-string">r')%(spacechar)s*\)'</tt> </tt>
<a name="L191"></a><tt class="py-lineno">191</tt> <tt class="py-line"> <tt class="py-string">r'|(@(?:'</tt> </tt>
<a name="L192"></a><tt class="py-lineno">192</tt> <tt class="py-line"> <tt class="py-string">r'[mM][eE][dD][iI][aA]'</tt> </tt>
<a name="L193"></a><tt class="py-lineno">193</tt> <tt class="py-line"> <tt class="py-string">r'|[sS][uU][pP][pP][oO][rR][tT][sS]'</tt> </tt>
<a name="L194"></a><tt class="py-lineno">194</tt> <tt class="py-line"> <tt class="py-string">r'|[dD][oO][cC][uU][mM][eE][nN][tT]'</tt> </tt>
<a name="L195"></a><tt class="py-lineno">195</tt> <tt class="py-line"> <tt class="py-string">r'|(?:-(?:'</tt> </tt>
<a name="L196"></a><tt class="py-lineno">196</tt> <tt class="py-line"> <tt class="py-string">r'[wW][eE][bB][kK][iI][tT]|[mM][oO][zZ]|[oO]|[mM][sS]'</tt> </tt>
<a name="L197"></a><tt class="py-lineno">197</tt> <tt class="py-line"> <tt class="py-string">r')-)?'</tt> </tt>
<a name="L198"></a><tt class="py-lineno">198</tt> <tt class="py-line"> <tt class="py-string">r'[kK][eE][yY][fF][rR][aA][mM][eE][sS]'</tt> </tt>
<a name="L199"></a><tt class="py-lineno">199</tt> <tt class="py-line"> <tt class="py-string">r'))(?!%(nmchar)s)'</tt> </tt>
<a name="L200"></a><tt class="py-lineno">200</tt> <tt class="py-line"> <tt class="py-string">r'|(%(ie7hack)s)(%(space)s*)'</tt> </tt>
<a name="L201"></a><tt class="py-lineno">201</tt> <tt class="py-line"> <tt class="py-string">r'|(:[fF][iI][rR][sS][tT]-[lL]'</tt> </tt>
<a name="L202"></a><tt class="py-lineno">202</tt> <tt class="py-line"> <tt class="py-string">r'(?:[iI][nN][eE]|[eE][tT][tT][eE][rR]))'</tt> </tt>
<a name="L203"></a><tt class="py-lineno">203</tt> <tt class="py-line"> <tt class="py-string">r'(%(space)s*)(?=[{,])'</tt> </tt>
<a name="L204"></a><tt class="py-lineno">204</tt> <tt class="py-line"> <tt class="py-string">r'|(%(nl_strings)s)'</tt> </tt>
<a name="L205"></a><tt class="py-lineno">205</tt> <tt class="py-line"> <tt class="py-string">r'|(%(escape)s[^\\"\047u&gt;@\r\n\f\040\t/;:{}]*)'</tt> </tt>
<a name="L206"></a><tt class="py-lineno">206</tt> <tt class="py-line"> <tt class="py-op">)</tt> <tt class="py-op">%</tt> <tt class="py-name">locals</tt><tt class="py-op">(</tt><tt class="py-op">)</tt><tt class="py-op">)</tt><tt class="py-op">.</tt><tt class="py-name">sub</tt> </tt>
<a name="L207"></a><tt class="py-lineno">207</tt> <tt class="py-line"> </tt>
<a name="L208"></a><tt class="py-lineno">208</tt> <tt class="py-line"> <tt class="py-comment">#print main_sub.__self__.pattern</tt> </tt>
<a name="L209"></a><tt class="py-lineno">209</tt> <tt class="py-line"> </tt>
<a name="L210"></a><tt class="py-lineno">210</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">main_subber</tt><tt class="py-op">(</tt><tt class="py-param">keep_bang_comments</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L211"></a><tt class="py-lineno">211</tt> <tt class="py-line"> <tt class="py-docstring">""" Make main subber """</tt> </tt>
<a name="L212"></a><tt class="py-lineno">212</tt> <tt class="py-line"> <tt class="py-name">in_macie5</tt><tt class="py-op">,</tt> <tt class="py-name">in_rule</tt><tt class="py-op">,</tt> <tt class="py-name">at_group</tt> <tt class="py-op">=</tt> <tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt><tt class="py-op">,</tt> <tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt><tt class="py-op">,</tt> <tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> </tt>
<a name="L213"></a><tt class="py-lineno">213</tt> <tt class="py-line"> </tt>
<a name="L214"></a><tt class="py-lineno">214</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">keep_bang_comments</tt><tt class="py-op">:</tt> </tt>
<a name="L215"></a><tt class="py-lineno">215</tt> <tt class="py-line"> <tt class="py-name">space_sub</tt> <tt class="py-op">=</tt> <tt class="py-name">space_sub_banged</tt> </tt>
<a name="L216"></a><tt class="py-lineno">216</tt> <tt class="py-line"> </tt>
<a name="L217"></a><tt class="py-lineno">217</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">space_subber</tt><tt class="py-op">(</tt><tt class="py-param">match</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L218"></a><tt class="py-lineno">218</tt> <tt class="py-line"> <tt class="py-docstring">""" Space|Comment subber """</tt> </tt>
<a name="L219"></a><tt class="py-lineno">219</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">match</tt><tt class="py-op">.</tt><tt class="py-name">lastindex</tt><tt class="py-op">:</tt> </tt>
<a name="L220"></a><tt class="py-lineno">220</tt> <tt class="py-line"> <tt class="py-name">group1</tt><tt class="py-op">,</tt> <tt class="py-name">group2</tt> <tt class="py-op">=</tt> <tt class="py-name">match</tt><tt class="py-op">.</tt><tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">1</tt><tt class="py-op">,</tt> <tt class="py-number">2</tt><tt class="py-op">)</tt> </tt>
<a name="L221"></a><tt class="py-lineno">221</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">group2</tt><tt class="py-op">:</tt> </tt>
<a name="L222"></a><tt class="py-lineno">222</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">group1</tt><tt class="py-op">.</tt><tt class="py-name">endswith</tt><tt class="py-op">(</tt><tt class="py-string">r'\*/'</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L223"></a><tt class="py-lineno">223</tt> <tt class="py-line"> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">=</tt> <tt class="py-number">1</tt> </tt>
<a name="L224"></a><tt class="py-lineno">224</tt> <tt class="py-line"> <tt class="py-keyword">else</tt><tt class="py-op">:</tt> </tt>
<a name="L225"></a><tt class="py-lineno">225</tt> <tt class="py-line"> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">=</tt> <tt class="py-number">0</tt> </tt>
<a name="L226"></a><tt class="py-lineno">226</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">group1</tt> </tt>
<a name="L227"></a><tt class="py-lineno">227</tt> <tt class="py-line"> <tt class="py-keyword">elif</tt> <tt class="py-name">group1</tt><tt class="py-op">:</tt> </tt>
<a name="L228"></a><tt class="py-lineno">228</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">group1</tt><tt class="py-op">.</tt><tt class="py-name">endswith</tt><tt class="py-op">(</tt><tt class="py-string">r'\*/'</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L229"></a><tt class="py-lineno">229</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt><tt class="py-op">:</tt> </tt>
<a name="L230"></a><tt class="py-lineno">230</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">''</tt> </tt>
<a name="L231"></a><tt class="py-lineno">231</tt> <tt class="py-line"> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">=</tt> <tt class="py-number">1</tt> </tt>
<a name="L232"></a><tt class="py-lineno">232</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">r'/*\*/'</tt> </tt>
<a name="L233"></a><tt class="py-lineno">233</tt> <tt class="py-line"> <tt class="py-keyword">elif</tt> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt><tt class="py-op">:</tt> </tt>
<a name="L234"></a><tt class="py-lineno">234</tt> <tt class="py-line"> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">=</tt> <tt class="py-number">0</tt> </tt>
<a name="L235"></a><tt class="py-lineno">235</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">'/**/'</tt> </tt>
<a name="L236"></a><tt class="py-lineno">236</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">''</tt> </tt>
</div><a name="L237"></a><tt class="py-lineno">237</tt> <tt class="py-line"> <tt class="py-keyword">else</tt><tt class="py-op">:</tt> </tt>
<a name="L238"></a><tt class="py-lineno">238</tt> <tt class="py-line"> <tt class="py-name">space_sub</tt> <tt class="py-op">=</tt> <tt class="py-name">space_sub_simple</tt> </tt>
<a name="L239"></a><tt class="py-lineno">239</tt> <tt class="py-line"> </tt>
<a name="L240"></a><tt class="py-lineno">240</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">space_subber</tt><tt class="py-op">(</tt><tt class="py-param">match</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L241"></a><tt class="py-lineno">241</tt> <tt class="py-line"> <tt class="py-docstring">""" Space|Comment subber """</tt> </tt>
<a name="L242"></a><tt class="py-lineno">242</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">match</tt><tt class="py-op">.</tt><tt class="py-name">lastindex</tt><tt class="py-op">:</tt> </tt>
<a name="L243"></a><tt class="py-lineno">243</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">match</tt><tt class="py-op">.</tt><tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">1</tt><tt class="py-op">)</tt><tt class="py-op">.</tt><tt class="py-name">endswith</tt><tt class="py-op">(</tt><tt class="py-string">r'\*/'</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L244"></a><tt class="py-lineno">244</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt><tt class="py-op">:</tt> </tt>
<a name="L245"></a><tt class="py-lineno">245</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">''</tt> </tt>
<a name="L246"></a><tt class="py-lineno">246</tt> <tt class="py-line"> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">=</tt> <tt class="py-number">1</tt> </tt>
<a name="L247"></a><tt class="py-lineno">247</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">r'/*\*/'</tt> </tt>
<a name="L248"></a><tt class="py-lineno">248</tt> <tt class="py-line"> <tt class="py-keyword">elif</tt> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt><tt class="py-op">:</tt> </tt>
<a name="L249"></a><tt class="py-lineno">249</tt> <tt class="py-line"> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">=</tt> <tt class="py-number">0</tt> </tt>
<a name="L250"></a><tt class="py-lineno">250</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">'/**/'</tt> </tt>
<a name="L251"></a><tt class="py-lineno">251</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">''</tt> </tt>
</div><a name="L252"></a><tt class="py-lineno">252</tt> <tt class="py-line"> </tt>
<a name="L253"></a><tt class="py-lineno">253</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">fn_space_post</tt><tt class="py-op">(</tt><tt class="py-param">group</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L254"></a><tt class="py-lineno">254</tt> <tt class="py-line"> <tt class="py-docstring">""" space with token after """</tt> </tt>
<a name="L255"></a><tt class="py-lineno">255</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">5</tt><tt class="py-op">)</tt> <tt class="py-keyword">is</tt> <tt class="py-name">None</tt> <tt class="py-keyword">or</tt> <tt class="py-op">(</tt> </tt>
<a name="L256"></a><tt class="py-lineno">256</tt> <tt class="py-line"> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">6</tt><tt class="py-op">)</tt> <tt class="py-op">==</tt> <tt class="py-string">':'</tt> <tt class="py-keyword">and</tt> <tt class="py-keyword">not</tt> <tt class="py-name">in_rule</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-keyword">and</tt> <tt class="py-keyword">not</tt> <tt class="py-name">at_group</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L257"></a><tt class="py-lineno">257</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">' '</tt> <tt class="py-op">+</tt> <tt class="py-name">space_sub</tt><tt class="py-op">(</tt><tt class="py-name">space_subber</tt><tt class="py-op">,</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">4</tt><tt class="py-op">)</tt><tt class="py-op">)</tt> </tt>
<a name="L258"></a><tt class="py-lineno">258</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">space_sub</tt><tt class="py-op">(</tt><tt class="py-name">space_subber</tt><tt class="py-op">,</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">4</tt><tt class="py-op">)</tt><tt class="py-op">)</tt> </tt>
</div><a name="L259"></a><tt class="py-lineno">259</tt> <tt class="py-line"> </tt>
<a name="L260"></a><tt class="py-lineno">260</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">fn_semicolon</tt><tt class="py-op">(</tt><tt class="py-param">group</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L261"></a><tt class="py-lineno">261</tt> <tt class="py-line"> <tt class="py-docstring">""" ; handler """</tt> </tt>
<a name="L262"></a><tt class="py-lineno">262</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">';'</tt> <tt class="py-op">+</tt> <tt class="py-name">space_sub</tt><tt class="py-op">(</tt><tt class="py-name">space_subber</tt><tt class="py-op">,</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">7</tt><tt class="py-op">)</tt><tt class="py-op">)</tt> </tt>
</div><a name="L263"></a><tt class="py-lineno">263</tt> <tt class="py-line"> </tt>
<a name="L264"></a><tt class="py-lineno">264</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">fn_semicolon2</tt><tt class="py-op">(</tt><tt class="py-param">group</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L265"></a><tt class="py-lineno">265</tt> <tt class="py-line"> <tt class="py-docstring">""" ; handler """</tt> </tt>
<a name="L266"></a><tt class="py-lineno">266</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">in_rule</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt><tt class="py-op">:</tt> </tt>
<a name="L267"></a><tt class="py-lineno">267</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">space_sub</tt><tt class="py-op">(</tt><tt class="py-name">space_subber</tt><tt class="py-op">,</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">7</tt><tt class="py-op">)</tt><tt class="py-op">)</tt> </tt>
<a name="L268"></a><tt class="py-lineno">268</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">';'</tt> <tt class="py-op">+</tt> <tt class="py-name">space_sub</tt><tt class="py-op">(</tt><tt class="py-name">space_subber</tt><tt class="py-op">,</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">7</tt><tt class="py-op">)</tt><tt class="py-op">)</tt> </tt>
</div><a name="L269"></a><tt class="py-lineno">269</tt> <tt class="py-line"> </tt>
<a name="L270"></a><tt class="py-lineno">270</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">fn_open</tt><tt class="py-op">(</tt><tt class="py-param">_</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L271"></a><tt class="py-lineno">271</tt> <tt class="py-line"> <tt class="py-docstring">""" { handler """</tt> </tt>
<a name="L272"></a><tt class="py-lineno">272</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">at_group</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt><tt class="py-op">:</tt> </tt>
<a name="L273"></a><tt class="py-lineno">273</tt> <tt class="py-line"> <tt class="py-name">at_group</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">-=</tt> <tt class="py-number">1</tt> </tt>
<a name="L274"></a><tt class="py-lineno">274</tt> <tt class="py-line"> <tt class="py-keyword">else</tt><tt class="py-op">:</tt> </tt>
<a name="L275"></a><tt class="py-lineno">275</tt> <tt class="py-line"> <tt class="py-name">in_rule</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">=</tt> <tt class="py-number">1</tt> </tt>
<a name="L276"></a><tt class="py-lineno">276</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">'{'</tt> </tt>
</div><a name="L277"></a><tt class="py-lineno">277</tt> <tt class="py-line"> </tt>
<a name="L278"></a><tt class="py-lineno">278</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">fn_close</tt><tt class="py-op">(</tt><tt class="py-param">_</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L279"></a><tt class="py-lineno">279</tt> <tt class="py-line"> <tt class="py-docstring">""" } handler """</tt> </tt>
<a name="L280"></a><tt class="py-lineno">280</tt> <tt class="py-line"> <tt class="py-name">in_rule</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">=</tt> <tt class="py-number">0</tt> </tt>
<a name="L281"></a><tt class="py-lineno">281</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">'}'</tt> </tt>
</div><a name="L282"></a><tt class="py-lineno">282</tt> <tt class="py-line"> </tt>
<a name="L283"></a><tt class="py-lineno">283</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">fn_at_group</tt><tt class="py-op">(</tt><tt class="py-param">group</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L284"></a><tt class="py-lineno">284</tt> <tt class="py-line"> <tt class="py-docstring">""" @xxx group handler """</tt> </tt>
<a name="L285"></a><tt class="py-lineno">285</tt> <tt class="py-line"> <tt class="py-name">at_group</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">+=</tt> <tt class="py-number">1</tt> </tt>
<a name="L286"></a><tt class="py-lineno">286</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">13</tt><tt class="py-op">)</tt> </tt>
</div><a name="L287"></a><tt class="py-lineno">287</tt> <tt class="py-line"> </tt>
<a name="L288"></a><tt class="py-lineno">288</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">fn_ie7hack</tt><tt class="py-op">(</tt><tt class="py-param">group</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L289"></a><tt class="py-lineno">289</tt> <tt class="py-line"> <tt class="py-docstring">""" IE7 Hack handler """</tt> </tt>
<a name="L290"></a><tt class="py-lineno">290</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-keyword">not</tt> <tt class="py-name">in_rule</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-keyword">and</tt> <tt class="py-keyword">not</tt> <tt class="py-name">at_group</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt><tt class="py-op">:</tt> </tt>
<a name="L291"></a><tt class="py-lineno">291</tt> <tt class="py-line"> <tt class="py-name">in_macie5</tt><tt class="py-op">[</tt><tt class="py-number">0</tt><tt class="py-op">]</tt> <tt class="py-op">=</tt> <tt class="py-number">0</tt> </tt>
<a name="L292"></a><tt class="py-lineno">292</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">14</tt><tt class="py-op">)</tt> <tt class="py-op">+</tt> <tt class="py-name">space_sub</tt><tt class="py-op">(</tt><tt class="py-name">space_subber</tt><tt class="py-op">,</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">15</tt><tt class="py-op">)</tt><tt class="py-op">)</tt> </tt>
<a name="L293"></a><tt class="py-lineno">293</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-string">'&gt;'</tt> <tt class="py-op">+</tt> <tt class="py-name">space_sub</tt><tt class="py-op">(</tt><tt class="py-name">space_subber</tt><tt class="py-op">,</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">15</tt><tt class="py-op">)</tt><tt class="py-op">)</tt> </tt>
</div><a name="L294"></a><tt class="py-lineno">294</tt> <tt class="py-line"> </tt>
<a name="L295"></a><tt class="py-lineno">295</tt> <tt class="py-line"> <tt class="py-name">table</tt> <tt class="py-op">=</tt> <tt class="py-op">(</tt> </tt>
<a name="L296"></a><tt class="py-lineno">296</tt> <tt class="py-line"> <tt class="py-comment"># noqa pylint: disable = C0330</tt> </tt>
<a name="L297"></a><tt class="py-lineno">297</tt> <tt class="py-line"> <tt class="py-name">None</tt><tt class="py-op">,</tt> </tt>
<a name="L298"></a><tt class="py-lineno">298</tt> <tt class="py-line"> <tt class="py-name">None</tt><tt class="py-op">,</tt> </tt>
<a name="L299"></a><tt class="py-lineno">299</tt> <tt class="py-line"> <tt class="py-name">None</tt><tt class="py-op">,</tt> </tt>
<a name="L300"></a><tt class="py-lineno">300</tt> <tt class="py-line"> <tt class="py-name">None</tt><tt class="py-op">,</tt> </tt>
<a name="L301"></a><tt class="py-lineno">301</tt> <tt class="py-line"> <tt class="py-name">fn_space_post</tt><tt class="py-op">,</tt> <tt class="py-comment"># space with token after</tt> </tt>
<a name="L302"></a><tt class="py-lineno">302</tt> <tt class="py-line"> <tt class="py-name">fn_space_post</tt><tt class="py-op">,</tt> <tt class="py-comment"># space with token after</tt> </tt>
<a name="L303"></a><tt class="py-lineno">303</tt> <tt class="py-line"> <tt class="py-name">fn_space_post</tt><tt class="py-op">,</tt> <tt class="py-comment"># space with token after</tt> </tt>
<a name="L304"></a><tt class="py-lineno">304</tt> <tt class="py-line"> <tt class="py-name">fn_semicolon</tt><tt class="py-op">,</tt> <tt class="py-comment"># semicolon</tt> </tt>
<a name="L305"></a><tt class="py-lineno">305</tt> <tt class="py-line"> <tt class="py-name">fn_semicolon2</tt><tt class="py-op">,</tt> <tt class="py-comment"># semicolon</tt> </tt>
<a name="L306"></a><tt class="py-lineno">306</tt> <tt class="py-line"> <tt class="py-name">fn_open</tt><tt class="py-op">,</tt> <tt class="py-comment"># {</tt> </tt>
<a name="L307"></a><tt class="py-lineno">307</tt> <tt class="py-line"> <tt class="py-name">fn_close</tt><tt class="py-op">,</tt> <tt class="py-comment"># }</tt> </tt>
<a name="L308"></a><tt class="py-lineno">308</tt> <tt class="py-line"> <tt class="py-keyword">lambda</tt> <tt class="py-name">g</tt><tt class="py-op">:</tt> <tt class="py-name">g</tt><tt class="py-op">(</tt><tt class="py-number">11</tt><tt class="py-op">)</tt><tt class="py-op">,</tt> <tt class="py-comment"># string</tt> </tt>
<a name="L309"></a><tt class="py-lineno">309</tt> <tt class="py-line"> <tt class="py-keyword">lambda</tt> <tt class="py-name">g</tt><tt class="py-op">:</tt> <tt class="py-string">'url(%s)'</tt> <tt class="py-op">%</tt> <tt class="py-name">uri_space_sub</tt><tt class="py-op">(</tt><tt class="py-name">uri_space_subber</tt><tt class="py-op">,</tt> <tt class="py-name">g</tt><tt class="py-op">(</tt><tt class="py-number">12</tt><tt class="py-op">)</tt><tt class="py-op">)</tt><tt class="py-op">,</tt> </tt>
<a name="L310"></a><tt class="py-lineno">310</tt> <tt class="py-line"> <tt class="py-comment"># url(...)</tt> </tt>
<a name="L311"></a><tt class="py-lineno">311</tt> <tt class="py-line"> <tt class="py-name">fn_at_group</tt><tt class="py-op">,</tt> <tt class="py-comment"># @xxx expecting {...}</tt> </tt>
<a name="L312"></a><tt class="py-lineno">312</tt> <tt class="py-line"> <tt class="py-name">None</tt><tt class="py-op">,</tt> </tt>
<a name="L313"></a><tt class="py-lineno">313</tt> <tt class="py-line"> <tt class="py-name">fn_ie7hack</tt><tt class="py-op">,</tt> <tt class="py-comment"># ie7hack</tt> </tt>
<a name="L314"></a><tt class="py-lineno">314</tt> <tt class="py-line"> <tt class="py-name">None</tt><tt class="py-op">,</tt> </tt>
<a name="L315"></a><tt class="py-lineno">315</tt> <tt class="py-line"> <tt class="py-keyword">lambda</tt> <tt class="py-name">g</tt><tt class="py-op">:</tt> <tt class="py-name">g</tt><tt class="py-op">(</tt><tt class="py-number">16</tt><tt class="py-op">)</tt> <tt class="py-op">+</tt> <tt class="py-string">' '</tt> <tt class="py-op">+</tt> <tt class="py-name">space_sub</tt><tt class="py-op">(</tt><tt class="py-name">space_subber</tt><tt class="py-op">,</tt> <tt class="py-name">g</tt><tt class="py-op">(</tt><tt class="py-number">17</tt><tt class="py-op">)</tt><tt class="py-op">)</tt><tt class="py-op">,</tt> </tt>
<a name="L316"></a><tt class="py-lineno">316</tt> <tt class="py-line"> <tt class="py-comment"># :first-line|letter followed</tt> </tt>
<a name="L317"></a><tt class="py-lineno">317</tt> <tt class="py-line"> <tt class="py-comment"># by [{,] (apparently space</tt> </tt>
<a name="L318"></a><tt class="py-lineno">318</tt> <tt class="py-line"> <tt class="py-comment"># needed for IE6)</tt> </tt>
<a name="L319"></a><tt class="py-lineno">319</tt> <tt class="py-line"> <tt class="py-keyword">lambda</tt> <tt class="py-name">g</tt><tt class="py-op">:</tt> <tt class="py-name">nl_unesc_sub</tt><tt class="py-op">(</tt><tt class="py-string">''</tt><tt class="py-op">,</tt> <tt class="py-name">g</tt><tt class="py-op">(</tt><tt class="py-number">18</tt><tt class="py-op">)</tt><tt class="py-op">)</tt><tt class="py-op">,</tt> <tt class="py-comment"># nl_string</tt> </tt>
<a name="L320"></a><tt class="py-lineno">320</tt> <tt class="py-line"> <tt class="py-keyword">lambda</tt> <tt class="py-name">g</tt><tt class="py-op">:</tt> <tt class="py-name">post_esc_sub</tt><tt class="py-op">(</tt><tt class="py-string">' '</tt><tt class="py-op">,</tt> <tt class="py-name">g</tt><tt class="py-op">(</tt><tt class="py-number">19</tt><tt class="py-op">)</tt><tt class="py-op">)</tt><tt class="py-op">,</tt> <tt class="py-comment"># escape</tt> </tt>
<a name="L321"></a><tt class="py-lineno">321</tt> <tt class="py-line"> <tt class="py-op">)</tt> </tt>
<a name="L322"></a><tt class="py-lineno">322</tt> <tt class="py-line"> </tt>
<a name="L323"></a><tt class="py-lineno">323</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">func</tt><tt class="py-op">(</tt><tt class="py-param">match</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
<a name="L324"></a><tt class="py-lineno">324</tt> <tt class="py-line"> <tt class="py-docstring">""" Main subber """</tt> </tt>
<a name="L325"></a><tt class="py-lineno">325</tt> <tt class="py-line"> <tt class="py-name">idx</tt><tt class="py-op">,</tt> <tt class="py-name">group</tt> <tt class="py-op">=</tt> <tt class="py-name">match</tt><tt class="py-op">.</tt><tt class="py-name">lastindex</tt><tt class="py-op">,</tt> <tt class="py-name">match</tt><tt class="py-op">.</tt><tt class="py-name">group</tt> </tt>
<a name="L326"></a><tt class="py-lineno">326</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-name">idx</tt> <tt class="py-op">&gt;</tt> <tt class="py-number">3</tt><tt class="py-op">:</tt> </tt>
<a name="L327"></a><tt class="py-lineno">327</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">table</tt><tt class="py-op">[</tt><tt class="py-name">idx</tt><tt class="py-op">]</tt><tt class="py-op">(</tt><tt class="py-name">group</tt><tt class="py-op">)</tt> </tt>
<a name="L328"></a><tt class="py-lineno">328</tt> <tt class="py-line"> </tt>
<a name="L329"></a><tt class="py-lineno">329</tt> <tt class="py-line"> <tt class="py-comment"># shortcuts for frequent operations below:</tt> </tt>
<a name="L330"></a><tt class="py-lineno">330</tt> <tt class="py-line"> <tt class="py-keyword">elif</tt> <tt class="py-name">idx</tt> <tt class="py-op">==</tt> <tt class="py-number">1</tt><tt class="py-op">:</tt> <tt class="py-comment"># not interesting</tt> </tt>
<a name="L331"></a><tt class="py-lineno">331</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-number">1</tt><tt class="py-op">)</tt> </tt>
<a name="L332"></a><tt class="py-lineno">332</tt> <tt class="py-line"> <tt class="py-comment">#else: # space with token before or at the beginning</tt> </tt>
<a name="L333"></a><tt class="py-lineno">333</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">space_sub</tt><tt class="py-op">(</tt><tt class="py-name">space_subber</tt><tt class="py-op">,</tt> <tt class="py-name">group</tt><tt class="py-op">(</tt><tt class="py-name">idx</tt><tt class="py-op">)</tt><tt class="py-op">)</tt> </tt>
</div><a name="L334"></a><tt class="py-lineno">334</tt> <tt class="py-line"> </tt>
<a name="L335"></a><tt class="py-lineno">335</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">func</tt> </tt>
</div><a name="L336"></a><tt class="py-lineno">336</tt> <tt class="py-line"> </tt>
<a name="L337"></a><tt class="py-lineno">337</tt> <tt class="py-line"> <tt class="py-keyword">def</tt> <tt class="py-def-name">cssmin</tt><tt class="py-op">(</tt><tt class="py-param">style</tt><tt class="py-op">,</tt> <tt class="py-param">keep_bang_comments</tt><tt class="py-op">=</tt><tt class="py-name">False</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> <tt class="py-comment"># pylint: disable = W0621</tt> </tt>
<a name="L338"></a><tt class="py-lineno">338</tt> <tt class="py-line"> <tt class="py-docstring">"""</tt> </tt>
<a name="L339"></a><tt class="py-lineno">339</tt> <tt class="py-line"><tt class="py-docstring"> Minify CSS.</tt> </tt>
<a name="L340"></a><tt class="py-lineno">340</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L341"></a><tt class="py-lineno">341</tt> <tt class="py-line"><tt class="py-docstring"> :Parameters:</tt> </tt>
<a name="L342"></a><tt class="py-lineno">342</tt> <tt class="py-line"><tt class="py-docstring"> `style` : ``str``</tt> </tt>
<a name="L343"></a><tt class="py-lineno">343</tt> <tt class="py-line"><tt class="py-docstring"> CSS to minify</tt> </tt>
<a name="L344"></a><tt class="py-lineno">344</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L345"></a><tt class="py-lineno">345</tt> <tt class="py-line"><tt class="py-docstring"> `keep_bang_comments` : ``bool``</tt> </tt>
<a name="L346"></a><tt class="py-lineno">346</tt> <tt class="py-line"><tt class="py-docstring"> Keep comments starting with an exclamation mark? (``/*!...*/``)</tt> </tt>
<a name="L347"></a><tt class="py-lineno">347</tt> <tt class="py-line"><tt class="py-docstring"></tt> </tt>
<a name="L348"></a><tt class="py-lineno">348</tt> <tt class="py-line"><tt class="py-docstring"> :Return: Minified style</tt> </tt>
<a name="L349"></a><tt class="py-lineno">349</tt> <tt class="py-line"><tt class="py-docstring"> :Rtype: ``str``</tt> </tt>
<a name="L350"></a><tt class="py-lineno">350</tt> <tt class="py-line"><tt class="py-docstring"> """</tt> </tt>
<a name="L351"></a><tt class="py-lineno">351</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt class="py-name">main_sub</tt><tt class="py-op">(</tt><tt class="py-name">main_subber</tt><tt class="py-op">(</tt><tt class="py-name">keep_bang_comments</tt><tt class="py-op">)</tt><tt class="py-op">,</tt> <tt class="py-name">style</tt><tt class="py-op">)</tt> </tt>
</div><a name="L352"></a><tt class="py-lineno">352</tt> <tt class="py-line"> </tt>
<a name="L353"></a><tt class="py-lineno">353</tt> <tt class="py-line"> <tt class="py-keyword">return</tt> <tt id="link-5" class="py-name"><a title="rcssmin.cssmin" class="py-name" href="#" onclick="return doclink('link-5', 'cssmin', 'link-4');">cssmin</a></tt> </tt>
</div><a name="L354"></a><tt class="py-lineno">354</tt> <tt class="py-line"> </tt>
<a name="L355"></a><tt class="py-lineno">355</tt> <tt class="py-line"><tt id="link-6" class="py-name"><a title="rcssmin.cssmin" class="py-name" href="#" onclick="return doclink('link-6', 'cssmin', 'link-4');">cssmin</a></tt> <tt class="py-op">=</tt> <tt class="py-name">_make_cssmin</tt><tt class="py-op">(</tt><tt class="py-op">)</tt> </tt>
<a name="L356"></a><tt class="py-lineno">356</tt> <tt class="py-line"> </tt>
<a name="L357"></a><tt class="py-lineno">357</tt> <tt class="py-line"> </tt>
<a name="L358"></a><tt class="py-lineno">358</tt> <tt class="py-line"><tt class="py-keyword">if</tt> <tt class="py-name">__name__</tt> <tt class="py-op">==</tt> <tt class="py-string">'__main__'</tt><tt class="py-op">:</tt> </tt>
<a name="main"></a><div id="main-def"><a name="L359"></a><tt class="py-lineno">359</tt> <a class="py-toggle" href="#" id="main-toggle" onclick="return toggle('main');">-</a><tt class="py-line"> <tt class="py-keyword">def</tt> <a class="py-def-name" href="rcssmin-module.html#main">main</a><tt class="py-op">(</tt><tt class="py-op">)</tt><tt class="py-op">:</tt> </tt>
</div><div id="main-collapsed" style="display:none;" pad="+++" indent="++++++++"></div><div id="main-expanded"><a name="L360"></a><tt class="py-lineno">360</tt> <tt class="py-line"> <tt class="py-docstring">""" Main """</tt> </tt>
<a name="L361"></a><tt class="py-lineno">361</tt> <tt class="py-line"> <tt class="py-keyword">import</tt> <tt class="py-name">sys</tt> <tt class="py-keyword">as</tt> <tt class="py-name">_sys</tt> </tt>
<a name="L362"></a><tt class="py-lineno">362</tt> <tt class="py-line"> <tt class="py-name">keep_bang_comments</tt> <tt class="py-op">=</tt> <tt class="py-op">(</tt> </tt>
<a name="L363"></a><tt class="py-lineno">363</tt> <tt class="py-line"> <tt class="py-string">'-b'</tt> <tt class="py-keyword">in</tt> <tt class="py-name">_sys</tt><tt class="py-op">.</tt><tt class="py-name">argv</tt><tt class="py-op">[</tt><tt class="py-number">1</tt><tt class="py-op">:</tt><tt class="py-op">]</tt> </tt>
<a name="L364"></a><tt class="py-lineno">364</tt> <tt class="py-line"> <tt class="py-keyword">or</tt> <tt class="py-string">'-bp'</tt> <tt class="py-keyword">in</tt> <tt class="py-name">_sys</tt><tt class="py-op">.</tt><tt class="py-name">argv</tt><tt class="py-op">[</tt><tt class="py-number">1</tt><tt class="py-op">:</tt><tt class="py-op">]</tt> </tt>
<a name="L365"></a><tt class="py-lineno">365</tt> <tt class="py-line"> <tt class="py-keyword">or</tt> <tt class="py-string">'-pb'</tt> <tt class="py-keyword">in</tt> <tt class="py-name">_sys</tt><tt class="py-op">.</tt><tt class="py-name">argv</tt><tt class="py-op">[</tt><tt class="py-number">1</tt><tt class="py-op">:</tt><tt class="py-op">]</tt> </tt>
<a name="L366"></a><tt class="py-lineno">366</tt> <tt class="py-line"> <tt class="py-op">)</tt> </tt>
<a name="L367"></a><tt class="py-lineno">367</tt> <tt class="py-line"> <tt class="py-keyword">if</tt> <tt class="py-string">'-p'</tt> <tt class="py-keyword">in</tt> <tt class="py-name">_sys</tt><tt class="py-op">.</tt><tt class="py-name">argv</tt><tt class="py-op">[</tt><tt class="py-number">1</tt><tt class="py-op">:</tt><tt class="py-op">]</tt> <tt class="py-keyword">or</tt> <tt class="py-string">'-bp'</tt> <tt class="py-keyword">in</tt> <tt class="py-name">_sys</tt><tt class="py-op">.</tt><tt class="py-name">argv</tt><tt class="py-op">[</tt><tt class="py-number">1</tt><tt class="py-op">:</tt><tt class="py-op">]</tt> \ </tt>
<a name="L368"></a><tt class="py-lineno">368</tt> <tt class="py-line"> <tt class="py-keyword">or</tt> <tt class="py-string">'-pb'</tt> <tt class="py-keyword">in</tt> <tt class="py-name">_sys</tt><tt class="py-op">.</tt><tt class="py-name">argv</tt><tt class="py-op">[</tt><tt class="py-number">1</tt><tt class="py-op">:</tt><tt class="py-op">]</tt><tt class="py-op">:</tt> </tt>
<a name="L369"></a><tt class="py-lineno">369</tt> <tt class="py-line"> <tt class="py-keyword">global</tt> <tt id="link-7" class="py-name"><a title="rcssmin.cssmin" class="py-name" href="#" onclick="return doclink('link-7', 'cssmin', 'link-4');">cssmin</a></tt> <tt class="py-comment"># pylint: disable = W0603</tt> </tt>
<a name="L370"></a><tt class="py-lineno">370</tt> <tt class="py-line"> <tt id="link-8" class="py-name"><a title="rcssmin.cssmin" class="py-name" href="#" onclick="return doclink('link-8', 'cssmin', 'link-4');">cssmin</a></tt> <tt class="py-op">=</tt> <tt class="py-name">_make_cssmin</tt><tt class="py-op">(</tt><tt class="py-name">python_only</tt><tt class="py-op">=</tt><tt class="py-name">True</tt><tt class="py-op">)</tt> </tt>
<a name="L371"></a><tt class="py-lineno">371</tt> <tt class="py-line"> <tt class="py-name">_sys</tt><tt class="py-op">.</tt><tt class="py-name">stdout</tt><tt class="py-op">.</tt><tt class="py-name">write</tt><tt class="py-op">(</tt><tt id="link-9" class="py-name"><a title="rcssmin.cssmin" class="py-name" href="#" onclick="return doclink('link-9', 'cssmin', 'link-4');">cssmin</a></tt><tt class="py-op">(</tt> </tt>
<a name="L372"></a><tt class="py-lineno">372</tt> <tt class="py-line"> <tt class="py-name">_sys</tt><tt class="py-op">.</tt><tt class="py-name">stdin</tt><tt class="py-op">.</tt><tt class="py-name">read</tt><tt class="py-op">(</tt><tt class="py-op">)</tt><tt class="py-op">,</tt> <tt class="py-name">keep_bang_comments</tt><tt class="py-op">=</tt><tt class="py-name">keep_bang_comments</tt> </tt>
<a name="L373"></a><tt class="py-lineno">373</tt> <tt class="py-line"> <tt class="py-op">)</tt><tt class="py-op">)</tt> </tt>
</div><a name="L374"></a><tt class="py-lineno">374</tt> <tt class="py-line"> <tt class="py-name">main</tt><tt class="py-op">(</tt><tt class="py-op">)</tt> </tt>
<a name="L375"></a><tt class="py-lineno">375</tt> <tt class="py-line"> </tt><script type="text/javascript">
<!--
expandto(location.href);
// -->
</script>
</pre>
<br />
<!-- ==================== NAVIGATION BAR ==================== -->
<table class="navbar" border="0" width="100%" cellpadding="0"
bgcolor="#a0c0ff" cellspacing="0">
<tr valign="middle">
<!-- Home link -->
<th bgcolor="#70b0f0" class="navbar-select"
>&nbsp;&nbsp;&nbsp;Home&nbsp;&nbsp;&nbsp;</th>
<!-- Tree link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="module-tree.html">Trees</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Index link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Help link -->
<th>&nbsp;&nbsp;&nbsp;<a
href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
<!-- Project homepage -->
<th class="navbar" align="right" width="100%">
<table border="0" cellpadding="0" cellspacing="0">
<tr><th class="navbar" align="center"
><a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a></th>
</tr></table></th>
</tr>
</table>
<script type="text/javascript">
<!--
// Private objects are initially displayed (because if
// javascript is turned off then we want them to be
// visible); but by default, we want to hide them. So hide
// them unless we have a cookie that says to show them.
checkCookie();
// -->
</script>
</body>
</html>

View File

@@ -0,0 +1,38 @@
<html><head><title>Epydoc Redirect Page</title>
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="pragma" content="no-cache" />
<script type="text/javascript" src="epydoc.js"></script>
</head>
<body>
<script type="text/javascript">
<!--
var pages = ["rcssmin-m"];
var dottedName = get_anchor();
if (dottedName) {
var target = redirect_url(dottedName);
if (target) window.location.replace(target);
}
// -->
</script>
<h3>Epydoc Auto-redirect page</h3>
<p>When javascript is enabled, this page will redirect URLs of
the form <tt>redirect.html#<i>dotted.name</i></tt> to the
documentation for the object with the given fully-qualified
dotted name.</p>
<p><a id="message"> &nbsp; </a></p>
<script type="text/javascript">
<!--
if (dottedName) {
var msg = document.getElementById("message");
msg.innerHTML = "No documentation found for <tt>"+
dottedName+"</tt>";
}
// -->
</script>
</body>
</html>

View File

@@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
#
# Copyright 2009 - 2014
# André Malo or his licensors, as applicable
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[package]
name = rcssmin
python.min = 2.3
python.max = 3.4
pypy.min = 1.9
pypy.max = 2.2
jython.min = 2.5
jython.max = 2.7
version.number = 1.0.5
author.name = André Malo
author.email = nd@perlig.de
#maintainer.name =
#maintainer.email =
url.homepage = http://opensource.perlig.de/rcssmin/
url.download = http://storage.perlig.de/rcssmin/
[docs]
meta.classifiers = docs/CLASSIFIERS
meta.description = docs/DESCRIPTION
meta.summary = docs/SUMMARY
meta.provides = docs/PROVIDES
meta.license = LICENSE
meta.keywords =
CSS
Minimization
apidoc.dir = docs/apidoc
apidoc.strip = 1
#apidoc.ignore =
#userdoc.dir = docs/userdoc
#userdoc.strip = 1
#userdoc.ignore =
# .buildinfo
#examples.dir = docs/examples
#examples.strip = 1
#examples.ignore =
#man =
extra =
README.rst
docs/CHANGES
docs/BENCHMARKS
[manifest]
#packages.lib = .
#packages.collect =
modules = rcssmin
packages.extra =
_setup.py2.term
_setup.py3.term
#scripts =
dist =
tests
run_tests.py
bench
bench.sh

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,374 @@
#!/usr/bin/env python
# -*- coding: ascii -*-
r"""
==============
CSS Minifier
==============
CSS Minifier.
The minifier is based on the semantics of the `YUI compressor`_\\, which
itself is based on `the rule list by Isaac Schlueter`_\\.
:Copyright:
Copyright 2011 - 2014
Andr\xe9 Malo or his licensors, as applicable
:License:
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This module is a re-implementation aiming for speed instead of maximum
compression, so it can be used at runtime (rather than during a preprocessing
step). RCSSmin does syntactical compression only (removing spaces, comments
and possibly semicolons). It does not provide semantic compression (like
removing empty blocks, collapsing redundant properties etc). It does, however,
support various CSS hacks (by keeping them working as intended).
Here's a feature list:
- Strings are kept, except that escaped newlines are stripped
- Space/Comments before the very end or before various characters are
stripped: ``:{});=>+],!`` (The colon (``:``) is a special case, a single
space is kept if it's outside a ruleset.)
- Space/Comments at the very beginning or after various characters are
stripped: ``{}(=:>+[,!``
- Optional space after unicode escapes is kept, resp. replaced by a simple
space
- whitespaces inside ``url()`` definitions are stripped
- Comments starting with an exclamation mark (``!``) can be kept optionally.
- All other comments and/or whitespace characters are replaced by a single
space.
- Multiple consecutive semicolons are reduced to one
- The last semicolon within a ruleset is stripped
- CSS Hacks supported:
- IE7 hack (``>/**/``)
- Mac-IE5 hack (``/*\\*/.../**/``)
- The boxmodelhack is supported naturally because it relies on valid CSS2
strings
- Between ``:first-line`` and the following comma or curly brace a space is
inserted. (apparently it's needed for IE6)
- Same for ``:first-letter``
rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
factor 100 or so (depending on the input). docs/BENCHMARKS in the source
distribution contains the details.
Both python 2 (>= 2.4) and python 3 are supported.
.. _YUI compressor: https://github.com/yui/yuicompressor/
.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/
"""
if __doc__:
# pylint: disable = W0622
__doc__ = __doc__.encode('ascii').decode('unicode_escape')
__author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
__docformat__ = "restructuredtext en"
__license__ = "Apache License, Version 2.0"
__version__ = '1.0.5'
__all__ = ['cssmin']
import re as _re
def _make_cssmin(python_only=False):
"""
Generate CSS minifier.
:Parameters:
`python_only` : ``bool``
Use only the python variant. If true, the c extension is not even
tried to be loaded.
:Return: Minifier
:Rtype: ``callable``
"""
# pylint: disable = R0912, R0914, W0612
if not python_only:
try:
import _rcssmin
except ImportError:
pass
else:
return _rcssmin.cssmin
nl = r'(?:[\n\f]|\r\n?)' # pylint: disable = C0103
spacechar = r'[\r\n\f\040\t]'
unicoded = r'[0-9a-fA-F]{1,6}(?:[\040\n\t\f]|\r\n?)?'
escaped = r'[^\n\r\f0-9a-fA-F]'
escape = r'(?:\\(?:%(unicoded)s|%(escaped)s))' % locals()
nmchar = r'[^\000-\054\056\057\072-\100\133-\136\140\173-\177]'
#nmstart = r'[^\000-\100\133-\136\140\173-\177]'
#ident = (r'(?:'
# r'-?(?:%(nmstart)s|%(escape)s)%(nmchar)s*(?:%(escape)s%(nmchar)s*)*'
#r')') % locals()
comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
# only for specific purposes. The bang is grouped:
_bang_comment = r'(?:/\*(!?)[^*]*\*+(?:[^/*][^*]*\*+)*/)'
string1 = \
r'(?:\047[^\047\\\r\n\f]*(?:\\[^\r\n\f][^\047\\\r\n\f]*)*\047)'
string2 = r'(?:"[^"\\\r\n\f]*(?:\\[^\r\n\f][^"\\\r\n\f]*)*")'
strings = r'(?:%s|%s)' % (string1, string2)
nl_string1 = \
r'(?:\047[^\047\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^\047\\\r\n\f]*)*\047)'
nl_string2 = r'(?:"[^"\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^"\\\r\n\f]*)*")'
nl_strings = r'(?:%s|%s)' % (nl_string1, nl_string2)
uri_nl_string1 = r'(?:\047[^\047\\]*(?:\\(?:[^\r]|\r\n?)[^\047\\]*)*\047)'
uri_nl_string2 = r'(?:"[^"\\]*(?:\\(?:[^\r]|\r\n?)[^"\\]*)*")'
uri_nl_strings = r'(?:%s|%s)' % (uri_nl_string1, uri_nl_string2)
nl_escaped = r'(?:\\%(nl)s)' % locals()
space = r'(?:%(spacechar)s|%(comment)s)' % locals()
ie7hack = r'(?:>/\*\*/)'
uri = (r'(?:'
# noqa pylint: disable = C0330
r'(?:[^\000-\040"\047()\\\177]*'
r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*)'
r'(?:'
r'(?:%(spacechar)s+|%(nl_escaped)s+)'
r'(?:'
r'(?:[^\000-\040"\047()\\\177]|%(escape)s|%(nl_escaped)s)'
r'[^\000-\040"\047()\\\177]*'
r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*'
r')+'
r')*'
r')') % locals()
nl_unesc_sub = _re.compile(nl_escaped).sub
uri_space_sub = _re.compile((
r'(%(escape)s+)|%(spacechar)s+|%(nl_escaped)s+'
) % locals()).sub
uri_space_subber = lambda m: m.groups()[0] or ''
space_sub_simple = _re.compile((
r'[\r\n\f\040\t;]+|(%(comment)s+)'
) % locals()).sub
space_sub_banged = _re.compile((
r'[\r\n\f\040\t;]+|(%(_bang_comment)s+)'
) % locals()).sub
post_esc_sub = _re.compile(r'[\r\n\f\t]+').sub
main_sub = _re.compile((
# noqa pylint: disable = C0330
r'([^\\"\047u>@\r\n\f\040\t/;:{}]+)'
r'|(?<=[{}(=:>+[,!])(%(space)s+)'
r'|^(%(space)s+)'
r'|(%(space)s+)(?=(([:{});=>+\],!])|$)?)'
r'|;(%(space)s*(?:;%(space)s*)*)(?=(\})?)'
r'|(\{)'
r'|(\})'
r'|(%(strings)s)'
r'|(?<!%(nmchar)s)url\(%(spacechar)s*('
r'%(uri_nl_strings)s'
r'|%(uri)s'
r')%(spacechar)s*\)'
r'|(@(?:'
r'[mM][eE][dD][iI][aA]'
r'|[sS][uU][pP][pP][oO][rR][tT][sS]'
r'|[dD][oO][cC][uU][mM][eE][nN][tT]'
r'|(?:-(?:'
r'[wW][eE][bB][kK][iI][tT]|[mM][oO][zZ]|[oO]|[mM][sS]'
r')-)?'
r'[kK][eE][yY][fF][rR][aA][mM][eE][sS]'
r'))(?!%(nmchar)s)'
r'|(%(ie7hack)s)(%(space)s*)'
r'|(:[fF][iI][rR][sS][tT]-[lL]'
r'(?:[iI][nN][eE]|[eE][tT][tT][eE][rR]))'
r'(%(space)s*)(?=[{,])'
r'|(%(nl_strings)s)'
r'|(%(escape)s[^\\"\047u>@\r\n\f\040\t/;:{}]*)'
) % locals()).sub
#print main_sub.__self__.pattern
def main_subber(keep_bang_comments):
""" Make main subber """
in_macie5, in_rule, at_group = [0], [0], [0]
if keep_bang_comments:
space_sub = space_sub_banged
def space_subber(match):
""" Space|Comment subber """
if match.lastindex:
group1, group2 = match.group(1, 2)
if group2:
if group1.endswith(r'\*/'):
in_macie5[0] = 1
else:
in_macie5[0] = 0
return group1
elif group1:
if group1.endswith(r'\*/'):
if in_macie5[0]:
return ''
in_macie5[0] = 1
return r'/*\*/'
elif in_macie5[0]:
in_macie5[0] = 0
return '/**/'
return ''
else:
space_sub = space_sub_simple
def space_subber(match):
""" Space|Comment subber """
if match.lastindex:
if match.group(1).endswith(r'\*/'):
if in_macie5[0]:
return ''
in_macie5[0] = 1
return r'/*\*/'
elif in_macie5[0]:
in_macie5[0] = 0
return '/**/'
return ''
def fn_space_post(group):
""" space with token after """
if group(5) is None or (
group(6) == ':' and not in_rule[0] and not at_group[0]):
return ' ' + space_sub(space_subber, group(4))
return space_sub(space_subber, group(4))
def fn_semicolon(group):
""" ; handler """
return ';' + space_sub(space_subber, group(7))
def fn_semicolon2(group):
""" ; handler """
if in_rule[0]:
return space_sub(space_subber, group(7))
return ';' + space_sub(space_subber, group(7))
def fn_open(_):
""" { handler """
if at_group[0]:
at_group[0] -= 1
else:
in_rule[0] = 1
return '{'
def fn_close(_):
""" } handler """
in_rule[0] = 0
return '}'
def fn_at_group(group):
""" @xxx group handler """
at_group[0] += 1
return group(13)
def fn_ie7hack(group):
""" IE7 Hack handler """
if not in_rule[0] and not at_group[0]:
in_macie5[0] = 0
return group(14) + space_sub(space_subber, group(15))
return '>' + space_sub(space_subber, group(15))
table = (
# noqa pylint: disable = C0330
None,
None,
None,
None,
fn_space_post, # space with token after
fn_space_post, # space with token after
fn_space_post, # space with token after
fn_semicolon, # semicolon
fn_semicolon2, # semicolon
fn_open, # {
fn_close, # }
lambda g: g(11), # string
lambda g: 'url(%s)' % uri_space_sub(uri_space_subber, g(12)),
# url(...)
fn_at_group, # @xxx expecting {...}
None,
fn_ie7hack, # ie7hack
None,
lambda g: g(16) + ' ' + space_sub(space_subber, g(17)),
# :first-line|letter followed
# by [{,] (apparently space
# needed for IE6)
lambda g: nl_unesc_sub('', g(18)), # nl_string
lambda g: post_esc_sub(' ', g(19)), # escape
)
def func(match):
""" Main subber """
idx, group = match.lastindex, match.group
if idx > 3:
return table[idx](group)
# shortcuts for frequent operations below:
elif idx == 1: # not interesting
return group(1)
#else: # space with token before or at the beginning
return space_sub(space_subber, group(idx))
return func
def cssmin(style, keep_bang_comments=False): # pylint: disable = W0621
"""
Minify CSS.
:Parameters:
`style` : ``str``
CSS to minify
`keep_bang_comments` : ``bool``
Keep comments starting with an exclamation mark? (``/*!...*/``)
:Return: Minified style
:Rtype: ``str``
"""
return main_sub(main_subber(keep_bang_comments), style)
return cssmin
cssmin = _make_cssmin()
if __name__ == '__main__':
def main():
""" Main """
import sys as _sys
keep_bang_comments = (
'-b' in _sys.argv[1:]
or '-bp' in _sys.argv[1:]
or '-pb' in _sys.argv[1:]
)
if '-p' in _sys.argv[1:] or '-bp' in _sys.argv[1:] \
or '-pb' in _sys.argv[1:]:
global cssmin # pylint: disable = W0603
cssmin = _make_cssmin(python_only=True)
_sys.stdout.write(cssmin(
_sys.stdin.read(), keep_bang_comments=keep_bang_comments
))
main()

View File

@@ -0,0 +1,166 @@
#!/usr/bin/env python
# -*- coding: ascii -*-
#
# Copyright 2014
# Andr\xe9 Malo or his licensors, as applicable
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
===========
Run tests
===========
Run tests.
"""
__author__ = "Andr\xe9 Malo"
__author__ = getattr(__author__, 'decode', lambda x: __author__)('latin-1')
__docformat__ = "restructuredtext en"
import os as _os
import re as _re
import sys as _sys
from _setup import shell
from _setup import term
def run_tests(basedir, libdir):
""" Run output based tests """
import rcssmin as _rcssmin
py_cssmin = _rcssmin._make_cssmin(python_only=True)
c_cssmin = _rcssmin._make_cssmin(python_only=False)
def run_test(example, output_file):
""" Run it """
try:
fp = open(example, 'r')
except IOError:
return
else:
try:
input = fp.read()
finally:
fp.close()
def load_output(filename):
try:
fp = open(filename, 'r')
except IOError:
return None
else:
try:
output = fp.read()
finally:
fp.close()
output = output.strip()
if _re.search(r'(?<!\\)(?:\\\\)*\\[0-9a-zA-Z]{1,6}$', output):
output += ' '
return output
output = load_output(output_file)
output_b = load_output(output_file + '.b')
def do_test(cssmin, output, **options):
try:
genout = cssmin(input, **options)
except (KeyboardInterrupt, SystemExit):
raise
except:
return 1, "%(RED)s exc%(NORMAL)s "
else:
if output is None:
return 1, "%(RED)smiss%(NORMAL)s "
elif genout == output or genout == output.rstrip():
return 0, "%(GREEN)sOK%(NORMAL)s "
else:
return 1, "%(RED)sfail%(NORMAL)s "
erred, out = do_test(py_cssmin, output)
erred, c_out = do_test(c_cssmin, output)
erred, out_b = do_test(py_cssmin, output_b, keep_bang_comments=True)
erred, c_out_b = do_test(c_cssmin, output_b, keep_bang_comments=True)
term.write(
"%(out)s %(out_b)s | %(c_out)s %(c_out_b)s - %%(example)s\n"
% locals(),
example=_os.path.basename(example),
)
return erred
# end
# begin main test code
erred = 0
basedir = shell.native(basedir)
strip = len(basedir) - len(_os.path.basename(basedir))
for dirname, dirs, files in shell.walk(basedir):
dirs[:] = [
item for item in dirs if item not in ('.svn', '.git', 'out')
]
dirs.sort()
files = [item for item in files if item.endswith('.css')]
if not files:
continue
if not _os.path.isdir(_os.path.join(basedir, dirname, 'out')):
continue
term.yellow("---> %s" % (dirname[strip:],))
files.sort()
for filename in files:
if run_test(
_os.path.join(dirname, filename),
_os.path.join(dirname, 'out', filename[:-4] + '.out'),
): erred = 1
term.yellow("<--- %s" % (dirname[strip:],))
return erred
def main():
""" Main """
basedir, libdir = None, None
accept_opts = True
args = []
for arg in _sys.argv[1:]:
if accept_opts:
if arg == '--':
accept_opts = False
continue
elif arg == '-q':
term.write = term.green = term.red = term.yellow = \
term.announce = \
lambda fmt, **kwargs: None
continue
elif arg == '-p':
info = {}
for key in term.terminfo():
info[key] = ''
info['ERASE'] = '\n'
term.terminfo.info = info
continue
elif arg.startswith('-'):
_sys.stderr.write("Unrecognized option %r\n" % (arg,))
return 2
args.append(arg)
if len(args) > 2:
_sys.stderr.write("Too many arguments\n")
return 2
elif len(args) < 1:
_sys.stderr.write("Missing arguments\n")
return 2
basedir = args[0]
if len(args) > 1:
libdir = args[1]
return run_tests(basedir, libdir)
if __name__ == '__main__':
_sys.exit(main())

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python
# -*- coding: ascii -*-
#
# Copyright 2006 - 2013
# Andr\xe9 Malo or his licensors, as applicable
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys as _sys
from _setup import run
def setup(args=None, _manifest=0):
""" Main setup function """
from _setup.ext import Extension
if 'java' in _sys.platform.lower():
# no c extension for jython
ext = None
else:
ext=[Extension('_rcssmin', sources=['rcssmin.c'])]
return run(script_args=args, ext=ext, manifest_only=_manifest)
def manifest():
""" Create List of packaged files """
return setup((), _manifest=1)
if __name__ == '__main__':
setup()

View File

@@ -0,0 +1,3 @@
@page :first {
margin-left: 1cm;
}

View File

@@ -0,0 +1,15 @@
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
/* CSS rules here apply to:
+ The page "http://www.w3.org/".
+ Any page whose URL begins with "http://www.w3.org/Style/"
+ Any page whose URL's host is "mozilla.org" or ends with
".mozilla.org"
+ Any page whose URL starts with "https:" */
/* make the above-mentioned pages really ugly */
body { color: purple; background: yellow; }
}

View File

@@ -0,0 +1,17 @@
@media all and (min-width:500px) {
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
/* CSS rules here apply to:
+ The page "http://www.w3.org/".
+ Any page whose URL begins with "http://www.w3.org/Style/"
+ Any page whose URL's host is "mozilla.org" or ends with
".mozilla.org"
+ Any page whose URL starts with "https:" */
/* make the above-mentioned pages really ugly */
body { color: purple; background: yellow; }
}
}

View File

@@ -0,0 +1,11 @@
@media all and (min-width : 500px) {
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
@page :last {
margin : 3in;
}
}
}

View File

@@ -0,0 +1,13 @@
@media all and (min-width : 500px) {
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
@supports ( (perspective: 10px) or (-moz-perspective: 10px) or (-webkit-perspective: 10px) or (-ms-perspective: 10px) or (-o-perspective: 10px) ) {
@page :last {
margin : 3in;
}
}
}
}

View File

@@ -0,0 +1,31 @@
@media all and (min-width : 500px) {
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
@supports ( (perspective: 10px) or (-moz-perspective: 10px) or (-webkit-perspective: 10px) or (-ms-perspective: 10px) or (-o-perspective: 10px) ) {
@keyframes slidein {
from {
margin-left : 100%;
width: 300%
}
75% {
font-size:300%;
margin-left:25%;
width:150%;
}
to {
margin-left:0%;
width:100%;
}
}
@page :last {
margin : 3in;
}
}
}
}

View File

@@ -0,0 +1,31 @@
@mEdia all and (min-width : 500px) {
@docuMent url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
@suPpoRts ( (perspective: 10px) or (-moz-perspective: 10px) or (-webkit-perspective: 10px) or (-ms-perspective: 10px) or (-o-perspective: 10px) ) {
@keyFRames slidein {
from {
margin-left : 100%;
width: 300%
}
75% {
font-size:300%;
margin-left:25%;
width:150%;
}
to {
margin-left:0%;
width:100%;
}
}
@pagE :last {
margin : 3in;
}
}
}
}

View File

@@ -0,0 +1,31 @@
@media all and (min-width : 500px) {
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
@supports ( (perspective: 10px) or (-moz-perspective: 10px) or (-webkit-perspective: 10px) or (-ms-perspective: 10px) or (-o-perspective: 10px) ) {
@keyframes slidein {
from :blub {
margin-left : 100%;
width: 300%
}
75% {
font-size:300%;
margin-left:25%;
width:150%;
}
to {
margin-left:0%;
width:100%;
}
}
@page :last {
margin : 3in;
}
}
}
}

View File

@@ -0,0 +1,31 @@
@media all and (min-width : 500px) {
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
@supports ( (perspective: 10px) or (-moz-perspective: 10px) or (-webkit-perspective: 10px) or (-ms-perspective: 10px) or (-o-perspective: 10px) ) {
@-o-keyframes slidein {
from :blub {
margin-left : 100%;
width: 300%
}
75% {
font-size:300%;
margin-left:25%;
width:150%;
}
to {
margin-left:0%;
width:100%;
}
}
@page :last {
margin : 3in;
}
}
}
}

View File

@@ -0,0 +1,31 @@
@media all and (min-width : 500px) {
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
@supports ( (perspective: 10px) or (-moz-perspective: 10px) or (-webkit-perspective: 10px) or (-ms-perspective: 10px) or (-o-perspective: 10px) ) {
@-moz-keyframes slidein {
from :blub {
margin-left : 100%;
width: 300%
}
75% {
font-size:300%;
margin-left:25%;
width:150%;
}
to {
margin-left:0%;
width:100%;
}
}
@page :last {
margin : 3in;
}
}
}
}

View File

@@ -0,0 +1,31 @@
@media all and (min-width : 500px) {
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
@supports ( (perspective: 10px) or (-moz-perspective: 10px) or (-webkit-perspective: 10px) or (-ms-perspective: 10px) or (-o-perspective: 10px) ) {
@-webkit-keyframes slidein {
from :blub {
margin-left : 100%;
width: 300%
}
75% {
font-size:300%;
margin-left:25%;
width:150%;
}
to {
margin-left:0%;
width:100%;
}
}
@page :last {
margin : 3in;
}
}
}
}

View File

@@ -0,0 +1,31 @@
@media all and (min-width : 500px) {
@document url(http://www.w3.org/),
url-prefix(http://www.w3.org/Style/),
domain(mozilla.org),
regexp("https:.*")
{
@supports ( (perspective: 10px) or (-moz-perspective: 10px) or (-webkit-perspective: 10px) or (-ms-perspective: 10px) or (-o-perspective: 10px) ) {
@-ms-keyframes slidein {
from :blub {
margin-left : 100%;
width: 300%
}
75% {
font-size:300%;
margin-left:25%;
width:150%;
}
to {
margin-left:0%;
width:100%;
}
}
@page :last {
margin : 3in;
}
}
}
}

View File

@@ -0,0 +1 @@
/* this is a comment */i {love: comments; /*! yes */; /*YES*/}

View File

@@ -0,0 +1,7 @@
#mainnav li.hover dl.subsearch select {
margin-top /*\**/:4px\9;
margin-bottom /*\**/:0px\9;
}
#mainnav li.hover dl.subsearch label {
margin-top /*\**/:4px\9;
}

View File

@@ -0,0 +1 @@
x:first-line{bla: blub;}

View File

@@ -0,0 +1 @@
x:first-letter{bla: blub;}

View File

@@ -0,0 +1 @@
x:first-letter{bla:blub}y:first-line{foo:bar}

View File

@@ -0,0 +1 @@
@page :first{margin-left:1cm}

View File

@@ -0,0 +1 @@
@page :first{margin-left:1cm}

View File

@@ -0,0 +1 @@
@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){body{color:purple;background:yellow}}

View File

@@ -0,0 +1 @@
@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){body{color:purple;background:yellow}}

View File

@@ -0,0 +1 @@
@media all and (min-width:500px){@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){body{color:purple;background:yellow}}}

View File

@@ -0,0 +1 @@
@media all and (min-width:500px){@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){body{color:purple;background:yellow}}}

View File

@@ -0,0 +1 @@
@media all and (min-width:500px){@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){@page :last{margin:3in}}}

View File

@@ -0,0 +1 @@
@media all and (min-width:500px){@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){@page :last{margin:3in}}}

View File

@@ -0,0 +1 @@
@media all and (min-width:500px){@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){@supports ((perspective:10px) or (-moz-perspective:10px) or (-webkit-perspective:10px) or (-ms-perspective:10px) or (-o-perspective:10px)){@page :last{margin:3in}}}}

View File

@@ -0,0 +1 @@
@media all and (min-width:500px){@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){@supports ((perspective:10px) or (-moz-perspective:10px) or (-webkit-perspective:10px) or (-ms-perspective:10px) or (-o-perspective:10px)){@page :last{margin:3in}}}}

View File

@@ -0,0 +1 @@
@media all and (min-width:500px){@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){@supports ((perspective:10px) or (-moz-perspective:10px) or (-webkit-perspective:10px) or (-ms-perspective:10px) or (-o-perspective:10px)){@keyframes slidein{from{margin-left:100%;width:300%}75%{font-size:300%;margin-left:25%;width:150%}to{margin-left:0%;width:100%}}@page :last{margin:3in}}}}

View File

@@ -0,0 +1 @@
@media all and (min-width:500px){@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){@supports ((perspective:10px) or (-moz-perspective:10px) or (-webkit-perspective:10px) or (-ms-perspective:10px) or (-o-perspective:10px)){@keyframes slidein{from{margin-left:100%;width:300%}75%{font-size:300%;margin-left:25%;width:150%}to{margin-left:0%;width:100%}}@page :last{margin:3in}}}}

View File

@@ -0,0 +1 @@
@mEdia all and (min-width:500px){@docuMent url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){@suPpoRts ((perspective:10px) or (-moz-perspective:10px) or (-webkit-perspective:10px) or (-ms-perspective:10px) or (-o-perspective:10px)){@keyFRames slidein{from{margin-left:100%;width:300%}75%{font-size:300%;margin-left:25%;width:150%}to{margin-left:0%;width:100%}}@pagE :last{margin:3in}}}}

View File

@@ -0,0 +1 @@
@mEdia all and (min-width:500px){@docuMent url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){@suPpoRts ((perspective:10px) or (-moz-perspective:10px) or (-webkit-perspective:10px) or (-ms-perspective:10px) or (-o-perspective:10px)){@keyFRames slidein{from{margin-left:100%;width:300%}75%{font-size:300%;margin-left:25%;width:150%}to{margin-left:0%;width:100%}}@pagE :last{margin:3in}}}}

View File

@@ -0,0 +1 @@
@media all and (min-width:500px){@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/),domain(mozilla.org),regexp("https:.*"){@supports ((perspective:10px) or (-moz-perspective:10px) or (-webkit-perspective:10px) or (-ms-perspective:10px) or (-o-perspective:10px)){@keyframes slidein{from :blub{margin-left:100%;width:300%}75%{font-size:300%;margin-left:25%;width:150%}to{margin-left:0%;width:100%}}@page :last{margin:3in}}}}

Some files were not shown because too many files have changed in this diff Show More