Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Updated README:: Set default css filter to cssmin:: Added support for…

… jsmin filter
  • Loading branch information...
commit 737f9de8f85c9842753144e85f8c4c7de87b00d6 1 parent 0c72221
@aidaeology aidaeology authored
View
15 README.md
@@ -190,6 +190,21 @@ like this:
return on_my_cdn.get(uri, uri)
+Compression Filters
+---------------------------
+
+Default (cssmin)
+----------------------------
+django-static uses cssmin by default if it is installed.
+Get the source here: https://github.com/zacharyvoase/cssmin
+
+Using jsmin
+----------------------------
+If you would like to use jsmin instead of default js_slimmer, you just need to set
+the variable in your settings.py file:
+
+ DJANGO_STATIC_JSMIN = True
+
Using Google Closure Compiler
-----------------------------
View
1  django_static/filters/__init__.py
@@ -0,0 +1 @@
+#
View
217 django_static/filters/jsmin.py
@@ -0,0 +1,217 @@
+#!/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)
View
27 django_static/templatetags/django_static.py
@@ -28,8 +28,15 @@
try:
import cssmin
+ __cssmin_installed__ = True
except ImportError:
- warnings.warn("cssmin is not installed. (easy_install cssmin)")
+ __cssmin_installed__ = False
+
+try:
+ from ..filters import jsmin
+ __jsmin_installed__ = True
+except ImportError:
+ __jsmin_installed__ = False
if sys.platform == "win32":
@@ -705,30 +712,32 @@ def has_optimizer(type_):
if type_ == CSS:
if getattr(settings, 'DJANGO_STATIC_YUI_COMPRESSOR', None):
return True
- if getattr(settings, 'DJANGO_STATIC_CSSMIN', None):
- return True
- return slimmer is not None
+ return slimmer is not None or __cssmin_installed__
elif type_ == JS:
if getattr(settings, 'DJANGO_STATIC_CLOSURE_COMPILER', None):
return True
if getattr(settings, 'DJANGO_STATIC_YUI_COMPRESSOR', None):
return True
+ if getattr(settings, 'DJANGO_STATIC_JSMIN', None):
+ return True
return slimmer is not None
else:
raise ValueError("Invalid type %r" % type_)
def optimize(content, type_):
if type_ == CSS:
- if getattr(settings, 'DJANGO_STATIC_YUI_COMPRESSOR', None):
+ if __cssmin_installed__:
+ return _run_cssmin(content)
+ elif getattr(settings, 'DJANGO_STATIC_YUI_COMPRESSOR', None):
return _run_yui_compressor(content, type_)
- if getattr(settings, 'DJANGO_STATIC_CSSMIN', None):
- return _run_cssmin(content)
return css_slimmer(content)
elif type_ == JS:
if getattr(settings, 'DJANGO_STATIC_CLOSURE_COMPILER', None):
return _run_closure_compiler(content)
if getattr(settings, 'DJANGO_STATIC_YUI_COMPRESSOR', None):
return _run_yui_compressor(content, type_)
+ if getattr(settings, 'DJANGO_STATIC_JSMIN', None):
+ return _run_jsmin(content)
return js_slimmer(content)
else:
raise ValueError("Invalid type %r" % type_)
@@ -771,3 +780,7 @@ def _run_yui_compressor(code, type_):
def _run_cssmin(code):
output = cssmin.cssmin(code)
return output
+
+def _run_jsmin(code):
+ output = jsmin.jsmin(code)
+ return output
View
2  settings.py
@@ -95,4 +95,4 @@
#DJANGO_STATIC_CLOSURE_COMPILER = '/home/peterbe/tmp/compiler-latest/compiler.jar'
#DJANGO_STATIC_YUI_COMPRESSOR = '/home/peterbe/tmp/yuicompressor-2.4.2/build/yuicompressor-2.4.2.jar'
-#DJANGO_STATIC_CSSMIN = True
+#DJANGO_STATIC_JSMIN = True
View
2  setup.py
@@ -23,6 +23,7 @@
license='BSD',
packages=[
'django_static',
+ 'django_static.filters',
'django_static.templatetags',
],
classifiers=[
@@ -35,5 +36,4 @@
'Topic :: Software Development :: Libraries :: Application Frameworks',
'Topic :: Software Development :: Libraries :: Python Modules',
],
- include_package_data=True,
)
Please sign in to comment.
Something went wrong with that request. Please try again.