neue Verzeichnissstruktur
This commit is contained in:
1
utils/management/__init__.py
Normal file
1
utils/management/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#!/usr/bin/python
|
||||
1
utils/management/commands/__init__.py
Normal file
1
utils/management/commands/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#!/usr/bin/python
|
||||
243
utils/management/commands/compresscss.py
Normal file
243
utils/management/commands/compresscss.py
Normal file
@@ -0,0 +1,243 @@
|
||||
'''
|
||||
Created on 06.06.2011
|
||||
|
||||
@author: christian
|
||||
'''
|
||||
import fnmatch
|
||||
from optparse import make_option
|
||||
import os
|
||||
import re
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
'''
|
||||
classdocs
|
||||
'''
|
||||
can_import_settings = True
|
||||
help = _("Reads raw CSS from stdin, and writes compressed CSS to stdout.")
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option('-w', '--wrap',
|
||||
type='int',
|
||||
default=None,
|
||||
metavar='N',
|
||||
help="Wrap output to approximately N chars per line."),
|
||||
)
|
||||
|
||||
def remove_comments(self, 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(self, 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(self, css):
|
||||
"""Remove unnecessary semicolons."""
|
||||
|
||||
return re.sub(r";+\}", "}", css)
|
||||
|
||||
|
||||
def remove_empty_rules(self, css):
|
||||
"""Remove empty rules."""
|
||||
|
||||
return re.sub(r"[^\}\{]+\{\}", "", css)
|
||||
|
||||
|
||||
def normalize_rgb_colors_to_hex(self, 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 = match.group(1).split(",")
|
||||
hexcolor = '#%.2x%.2x%.2x' % map(int, colors)
|
||||
css = css.replace(match.group(), hexcolor)
|
||||
match = regex.search(css)
|
||||
return css
|
||||
|
||||
def condense_zero_units(self, 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(self, 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(self, css):
|
||||
"""Replace `0.6` with `.6` where possible."""
|
||||
|
||||
return re.sub(r"(:|\s)0+\.(\d+)", r"\1.\2", css)
|
||||
|
||||
|
||||
def condense_hex_colors(self, 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(self, css):
|
||||
"""Condense multiple adjacent whitespace characters into one."""
|
||||
|
||||
return re.sub(r"\s+", " ", css)
|
||||
|
||||
def condense_semicolons(self, css):
|
||||
"""Condense multiple adjacent semicolon characters into one."""
|
||||
|
||||
return re.sub(r";;+", ";", css)
|
||||
|
||||
def wrap_css_lines(self, 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 compress(self, css, wrap=None):
|
||||
css = self.remove_comments(css)
|
||||
css = self.condense_whitespace(css)
|
||||
# A pseudo class for the Box Model Hack
|
||||
# (see http://tantek.com/CSS/Examples/boxmodelhack.html)
|
||||
css = css.replace('"\\"}\\""', "___PSEUDOCLASSBMH___")
|
||||
css = self.remove_unnecessary_whitespace(css)
|
||||
css = self.remove_unnecessary_semicolons(css)
|
||||
css = self.condense_zero_units(css)
|
||||
css = self.condense_multidimensional_zeros(css)
|
||||
css = self.condense_floating_points(css)
|
||||
css = self.normalize_rgb_colors_to_hex(css)
|
||||
css = self.condense_hex_colors(css)
|
||||
if wrap is not None:
|
||||
css = self.wrap_css_lines(css, wrap)
|
||||
css = css.replace("___PSEUDOCLASSBMH___", '"\\"}\\""')
|
||||
css = self.condense_semicolons(css)
|
||||
# A Hack for IE compatiblity
|
||||
# These crappy browser has issues with AND Statments in @MEDIA Blocks
|
||||
css = css.replace('and(', 'and (')
|
||||
return css.strip()
|
||||
|
||||
def handle(self, *args, **options):
|
||||
CSS_ROOT = os.path.join(settings.STATICFILES_DIRS[0], 'css')
|
||||
for site in Site.objects.all():
|
||||
css_input = os.path.join(CSS_ROOT, site.domain)
|
||||
css_output = '%s/%s.css' % (CSS_ROOT, site.domain.replace('.', '_'))
|
||||
|
||||
print _("Compressing CSS for %s") % site.name
|
||||
print "Input Dir: %s" % css_input
|
||||
print "Output File: %s" % css_output
|
||||
|
||||
try:
|
||||
os.makedirs(css_input)
|
||||
except OSError:
|
||||
pass
|
||||
css = ''
|
||||
"""Read each .css file in the css_input directory,
|
||||
and append their content"""
|
||||
for file_name in fnmatch.filter(sorted(os.listdir(css_input)), '*.css'):
|
||||
print ' Adding: %s' % file_name
|
||||
with open(os.path.join(css_input, file_name), 'r') as css_file:
|
||||
css += css_file.read()
|
||||
with open(css_output, 'w') as css_file:
|
||||
css_file.write(self.compress(css))
|
||||
47
utils/management/commands/compressjs.py
Normal file
47
utils/management/commands/compressjs.py
Normal file
@@ -0,0 +1,47 @@
|
||||
'''
|
||||
Created on 06.06.2011
|
||||
|
||||
@author: christian
|
||||
'''
|
||||
import fnmatch
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils.translation import ugettext as _
|
||||
import jsmin
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
'''
|
||||
classdocs
|
||||
'''
|
||||
can_import_settings = True
|
||||
help = _("Reads raw CSS from stdin, and writes compressed CSS to stdout.")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
JS_ROOT = os.path.join(settings.STATICFILES_DIRS[0], 'js')
|
||||
for site in Site.objects.all():
|
||||
js_input = os.path.join(JS_ROOT, site.domain)
|
||||
js_output = '%s/%s.js' % (JS_ROOT, site.domain.replace('.', '_'))
|
||||
|
||||
print _("Compressing JavaScript for %s") % site.name
|
||||
print "Input Dir: %s" % js_input
|
||||
print "Output File: %s" % js_output
|
||||
|
||||
try:
|
||||
os.makedirs(js_input)
|
||||
except OSError:
|
||||
pass
|
||||
output = ''
|
||||
# Read each .js file in the js_input directory, and append their
|
||||
# content
|
||||
js_files = fnmatch.filter(sorted(os.listdir(js_input)), '*.js')
|
||||
for file_name in js_files:
|
||||
print " Adding: %s..." % file_name
|
||||
input_file_name = os.path.join(js_input, file_name)
|
||||
with open(input_file_name, 'r') as input_file:
|
||||
output += input_file.read()
|
||||
with open(js_output, 'w') as output_file:
|
||||
output_file.write(jsmin.jsmin(output))
|
||||
46
utils/management/commands/scss-compiler.py
Normal file
46
utils/management/commands/scss-compiler.py
Normal file
@@ -0,0 +1,46 @@
|
||||
'''
|
||||
Created on 06.06.2011
|
||||
|
||||
@author: christian
|
||||
'''
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils.translation import ugettext as _
|
||||
from optparse import make_option
|
||||
import os, re, fnmatch
|
||||
from scss import parser
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
'''
|
||||
classdocs
|
||||
'''
|
||||
can_import_settings = True
|
||||
help = _("Compile SCSS rules.")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
CSS_ROOT = os.path.join(settings.STATICFILES_DIRS[0], 'css')
|
||||
for site in Site.objects.all():
|
||||
css_input = os.path.join(CSS_ROOT, site.domain)
|
||||
css_output = '%s/%s.css' % (CSS_ROOT, site.domain.replace('.', '_'))
|
||||
|
||||
print _("Compressing CSS for %s") % site.name
|
||||
print "Input Dir: %s" % css_input
|
||||
print "Output File: %s" % css_output
|
||||
|
||||
try:
|
||||
os.makedirs(css_input)
|
||||
except OSError:
|
||||
pass
|
||||
css = ''
|
||||
"""Read each .css file in the css_input directory,
|
||||
and append their content"""
|
||||
for file_name in fnmatch.filter(sorted(os.listdir(css_input)), '*.css'):
|
||||
print ' Adding: %s' % file_name
|
||||
with open(os.path.join(css_input, file_name), 'r') as css_file:
|
||||
css += css_file.read()
|
||||
with open(css_output, 'w') as css_file:
|
||||
css_file.write(self.compress(css))
|
||||
#file.write(css)
|
||||
|
||||
Reference in New Issue
Block a user