Commit 396e0cec by andreas.pelme

git-svn-id: https://django-compress.googlecode.com/svn/trunk@2 98d35234-f74b-0410-9e22-51d878bdf110

parent d6be6ecd
from django.conf import settings
from compress.utils import needs_update, compress_css, compress_js
if settings.DEBUG:
for css in settings.COMPRESS_CSS.values():
if needs_update(css['compressed_filename'], css['source_filenames']):
compress_css(css)
for js in settings.COMPRESS_JS.values():
if needs_update(js['compressed_filename'], css['source_filenames']):
compress_js(js)
\ No newline at end of file
import os
from django.conf import settings
from compress.utils import media_root
DEFAULT_ARGUMENTS = '--template=highest'
DEFAULT_BINARY = 'csstidy'
class CSSTidyCompressor:
def compress_css(self, css):
try:
binary = settings.COMPRESS_CSS_CSSTIDY_BINARY
except AttributeError:
binary = DEFAULT_BINARY
try:
arguments = settings.COMPRESS_CSS_CSSTIDY_ARGUMENTS
except AttributeError:
arguments = DEFAULT_ARGUMENTS
# try to create a temporary concatenated source
tmp_filename = os.tmpnam()
fd_source = open(tmp_filename, 'w+')
for source_filename in css['source_filenames']:
fd = open(media_root(source_filename), 'r')
fd_source.write(fd.read())
fd.close()
fd_source.close()
command = '%s %s %s %s' % (binary, tmp_filename, arguments, media_root(css['compressed_filename']))
print os.popen(command).readlines()
os.unlink(tmp_filename)
def compress_js(self, js):
raise NotImplementedError
\ No newline at end of file
from compress.utils import media_root
from compress.compressors.jsmin.jsmin import jsmin
class JSMinCompressor:
def compress_css(self, css):
raise NotImplementedError
def compress_js(self, js):
source = ''
for source_filename in js['source_filenames']:
fd = open(media_root(source_filename), 'r')
source += fd.read()
fd.close()
compressed = jsmin(source)
fd = open(media_root(js['compressed_filename']), 'w+')
fd.write(compressed)
fd.close()
\ No newline at end of file
#!/usr/bin/python
# This code is original from jsmin by Douglas Crockford, it was translated to
# Python by Baruch Even. The original code had the following copyright and
# license.
#
# /* jsmin.c
# 2007-05-22
#
# Copyright (c) 2002 Douglas Crockford (www.crockford.com)
#
# 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 shall be used for Good, not Evil.
#
# 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.
# */
from StringIO import StringIO
def jsmin(js):
ins = StringIO(js)
outs = StringIO()
JavascriptMinify().minify(ins, outs)
str = outs.getvalue()
if len(str) > 0 and str[0] == '\n':
str = str[1:]
return str
def isAlphanum(c):
"""return true if the character is a letter, digit, underscore,
dollar sign, or non-ASCII character.
"""
return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
(c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126));
class UnterminatedComment(Exception):
pass
class UnterminatedStringLiteral(Exception):
pass
class UnterminatedRegularExpression(Exception):
pass
class JavascriptMinify(object):
def _outA(self):
self.outstream.write(self.theA)
def _outB(self):
self.outstream.write(self.theB)
def _get(self):
"""return the next character from stdin. Watch out for lookahead. If
the character is a control character, translate it to a space or
linefeed.
"""
c = self.theLookahead
self.theLookahead = None
if c == None:
c = self.instream.read(1)
if c >= ' ' or c == '\n':
return c
if c == '': # EOF
return '\000'
if c == '\r':
return '\n'
return ' '
def _peek(self):
self.theLookahead = self._get()
return self.theLookahead
def _next(self):
"""get the next character, excluding comments. peek() is used to see
if a '/' is followed by a '/' or '*'.
"""
c = self._get()
if c == '/':
p = self._peek()
if p == '/':
c = self._get()
while c > '\n':
c = self._get()
return c
if p == '*':
c = self._get()
while 1:
c = self._get()
if c == '*':
if self._peek() == '/':
self._get()
return ' '
if c == '\000':
raise UnterminatedComment()
return c
def _action(self, action):
"""do something! What you do is determined by the argument:
1 Output A. Copy B to A. Get the next B.
2 Copy B to A. Get the next B. (Delete A).
3 Get the next B. (Delete B).
action treats a string as a single character. Wow!
action recognizes a regular expression if it is preceded by ( or , or =.
"""
if action <= 1:
self._outA()
if action <= 2:
self.theA = self.theB
if self.theA == "'" or self.theA == '"':
while 1:
self._outA()
self.theA = self._get()
if self.theA == self.theB:
break
if self.theA <= '\n':
raise UnterminatedStringLiteral()
if self.theA == '\\':
self._outA()
self.theA = self._get()
if action <= 3:
self.theB = self._next()
if self.theB == '/' and (self.theA == '(' or self.theA == ',' or
self.theA == '=' or self.theA == ':' or
self.theA == '[' or self.theA == '?' or
self.theA == '!' or self.theA == '&' or
self.theA == '|' or self.theA == ';' or
self.theA == '{' or self.theA == '}' or
self.theA == '\n'):
self._outA()
self._outB()
while 1:
self.theA = self._get()
if self.theA == '/':
break
elif self.theA == '\\':
self._outA()
self.theA = self._get()
elif self.theA <= '\n':
raise UnterminatedRegularExpression()
self._outA()
self.theB = self._next()
def _jsmin(self):
"""Copy the input to the output, deleting the characters which are
insignificant to JavaScript. Comments will be removed. Tabs will be
replaced with spaces. Carriage returns will be replaced with linefeeds.
Most spaces and linefeeds will be removed.
"""
self.theA = '\n'
self._action(3)
while self.theA != '\000':
if self.theA == ' ':
if isAlphanum(self.theB):
self._action(1)
else:
self._action(2)
elif self.theA == '\n':
if self.theB in ['{', '[', '(', '+', '-']:
self._action(1)
elif self.theB == ' ':
self._action(3)
else:
if isAlphanum(self.theB):
self._action(1)
else:
self._action(2)
else:
if self.theB == ' ':
if isAlphanum(self.theA):
self._action(1)
else:
self._action(3)
elif self.theB == '\n':
if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
self._action(1)
else:
if isAlphanum(self.theA):
self._action(1)
else:
self._action(3)
else:
self._action(1)
def minify(self, instream, outstream):
self.instream = instream
self.outstream = outstream
self.theA = '\n'
self.theB = None
self.theLookahead = None
self._jsmin()
self.instream.close()
if __name__ == '__main__':
import sys
jsm = JavascriptMinify()
jsm.minify(sys.stdin, sys.stdout)
\ No newline at end of file
<link href="{{ url }}" rel="stylesheet" type="text/css" media="{{ css.media }}" />
\ No newline at end of file
<script type="text/javascript" src="{{ url }}"></script>
\ No newline at end of file
# {% %}
from django import template
from django.conf import settings
register = template.Library()
def render_css(css, filename):
return template.loader.render_to_string('compress/css.html', {
'url': settings.MEDIA_URL + filename,
'css': css,
})
def render_js(js, filename):
return template.loader.render_to_string('compress/js.html', {
'url': settings.MEDIA_URL + filename,
'js': js,
})
class CompressedCSSNode(template.Node):
def __init__(self, name):
self.name = name
def render(self, context):
css_name = template.Variable(self.name).resolve(context)
css = settings.COMPRESS_CSS[css_name]
if settings.COMPRESS:
return render_css(css, css['compressed_filename'])
else:
# output source files
r = ''
for source_file in css['source_filenames']:
r += render_css(css, source_file)
return r
class CompressedJSNode(template.Node):
def __init__(self, name):
self.name = name
def render(self, context):
js_name = template.Variable(self.name).resolve(context)
js = settings.COMPRESS_JS[js_name]
if settings.COMPRESS:
return render_js(js, js['compressed_filename'])
else:
# output source files
r = ''
for source_file in js['source_filenames']:
r += render_js(js, source_file)
return r
# @register.tag
def compressed_css(parser, token):
try:
tag_name, name = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError, '%r requires exactly one argument, being the name of the COMPRESS_CSS setting'
return CompressedCSSNode(name)
compressed_css = register.tag(compressed_css)
# @register.tag
def compressed_js(parser, token):
try:
tag_name, name = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError, '%r requires exactly one argument, being the name of the COMPRESS_JS setting'
return CompressedJSNode(name)
compressed_js = register.tag(compressed_js)
\ No newline at end of file
import os
from django.conf import settings
def get_compressor(compressor_class):
"""
Convert a string version of a function name to the callable object.
If the lookup_view is not an import path, it is assumed to be a URL pattern
label and the original string is returned.
If can_fail is True, lookup_view might be a URL pattern label, so errors
during the import fail and the string is returned.
"""
if not hasattr(compressor_class, '__bases__'):
try:
# Bail early for non-ASCII strings (they can't be functions).
compressor_class = compressor_class.encode('ascii')
mod_name, class_name = get_mod_func(compressor_class)
print mod_name
print class_name
if class_name != '':
compressor_class = getattr(__import__(mod_name, {}, {}, ['']), class_name)
except (ImportError, AttributeError):
pass
return compressor_class
# since this function is not part of any offical API,
# it is duplicated here to avoid regressions with future/different versions
# of django
def get_mod_func(callback):
# Converts 'django.views.news.stories.story_detail' to
# ['django.views.news.stories', 'story_detail']
try:
dot = callback.rindex('.')
except ValueError:
return callback, ''
return callback[:dot], callback[dot+1:]
def needs_update(compressed_file, source_files):
"""
Scan the source files for changes and returns True if the compressed_file needs to be updated.
compressed_file and source_files should be given with full paths
"""
compressed_file_full = media_root(compressed_file)
if not os.path.exists(compressed_file_full):
return True
compressed_file_mtime = os.stat(compressed_file_full).st_mtime
for source_file in source_files:
if compressed_file_mtime < os.stat(media_root(source_file)).st_mtime:
return True
return False
def media_root(filename):
return os.path.join(settings.MEDIA_ROOT, filename)
def media_url(filename):
return settings.MEDIA_URL + filename
def compress_css(css):
get_compressor(settings.COMPRESS_CSS_COMPRESSOR)().compress_css(css)
def compress_js(js):
get_compressor(settings.COMPRESS_JS_COMPRESSOR)().compress_js(js)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment