From: Ɓukasz Rekucki Date: Fri, 12 Mar 2010 23:33:26 +0000 (+0100) Subject: Added django-compress. X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/61c3753c6e0c567ccbb11115c787da22218ab5f6 Added django-compress. --- diff --git a/apps/compress/__init__.py b/apps/compress/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/compress/conf/__init__.py b/apps/compress/conf/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/compress/conf/settings.py b/apps/compress/conf/settings.py new file mode 100644 index 00000000..4c71561e --- /dev/null +++ b/apps/compress/conf/settings.py @@ -0,0 +1,20 @@ +from django.core.exceptions import ImproperlyConfigured +from django.conf import settings + +COMPRESS = getattr(settings, 'COMPRESS', not settings.DEBUG) +COMPRESS_AUTO = getattr(settings, 'COMPRESS_AUTO', True) +COMPRESS_VERSION = getattr(settings, 'COMPRESS_VERSION', False) +COMPRESS_VERSION_PLACEHOLDER = getattr(settings, 'COMPRESS_VERSION_PLACEHOLDER', '?') +COMPRESS_VERSION_DEFAULT = getattr(settings, 'COMPRESS_VERSION_DEFAULT', '0') +COMPRESS_VERSIONING = getattr(settings, 'COMPRESS_VERSIONING', 'compress.versioning.mtime.MTimeVersioning') + +COMPRESS_CSS_FILTERS = getattr(settings, 'COMPRESS_CSS_FILTERS', ['compress.filters.csstidy.CSSTidyFilter']) +COMPRESS_JS_FILTERS = getattr(settings, 'COMPRESS_JS_FILTERS', ['compress.filters.jsmin.JSMinFilter']) +COMPRESS_CSS = getattr(settings, 'COMPRESS_CSS', {}) +COMPRESS_JS = getattr(settings, 'COMPRESS_JS', {}) + +if COMPRESS_CSS_FILTERS is None: + COMPRESS_CSS_FILTERS = [] + +if COMPRESS_JS_FILTERS is None: + COMPRESS_JS_FILTERS = [] diff --git a/apps/compress/filter_base.py b/apps/compress/filter_base.py new file mode 100644 index 00000000..9b98531b --- /dev/null +++ b/apps/compress/filter_base.py @@ -0,0 +1,14 @@ +class FilterBase: + def __init__(self, verbose): + self.verbose = verbose + + def filter_css(self, css): + raise NotImplementedError + def filter_js(self, js): + raise NotImplementedError + +class FilterError(Exception): + """ + This exception is raised when a filter fails + """ + pass \ No newline at end of file diff --git a/apps/compress/filters/__init__.py b/apps/compress/filters/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/compress/filters/csstidy/__init__.py b/apps/compress/filters/csstidy/__init__.py new file mode 100644 index 00000000..d40e8eeb --- /dev/null +++ b/apps/compress/filters/csstidy/__init__.py @@ -0,0 +1,33 @@ +import os +import warnings +import tempfile + +from django.conf import settings + +from compress.filter_base import FilterBase + +BINARY = getattr(settings, 'CSSTIDY_BINARY', 'csstidy') +ARGUMENTS = getattr(settings, 'CSSTIDY_ARGUMENTS', '--template=highest') + +warnings.simplefilter('ignore', RuntimeWarning) + +class CSSTidyFilter(FilterBase): + def filter_css(self, css): + tmp_file = tempfile.NamedTemporaryFile(mode='w+b') + tmp_file.write(css) + tmp_file.flush() + + output_file = tempfile.NamedTemporaryFile(mode='w+b') + + command = '%s %s %s %s' % (BINARY, tmp_file.name, ARGUMENTS, output_file.name) + + command_output = os.popen(command).read() + + filtered_css = output_file.read() + output_file.close() + tmp_file.close() + + if self.verbose: + print command_output + + return filtered_css diff --git a/apps/compress/filters/csstidy_python/__init__.py b/apps/compress/filters/csstidy_python/__init__.py new file mode 100644 index 00000000..7d581ed4 --- /dev/null +++ b/apps/compress/filters/csstidy_python/__init__.py @@ -0,0 +1,19 @@ +from django.conf import settings + +from compress.filter_base import FilterBase +from compress.filters.csstidy_python.csstidy import CSSTidy + +COMPRESS_CSSTIDY_SETTINGS = getattr(settings, 'COMPRESS_CSSTIDY_SETTINGS', {}) + +class CSSTidyFilter(FilterBase): + def filter_css(self, css): + tidy = CSSTidy() + + for k, v in COMPRESS_CSSTIDY_SETTINGS.items(): + tidy.setSetting(k, v) + + tidy.parse(css) + + r = tidy.Output('string') + + return r diff --git a/apps/compress/filters/csstidy_python/csstidy.py b/apps/compress/filters/csstidy_python/csstidy.py new file mode 100644 index 00000000..6ae8dc73 --- /dev/null +++ b/apps/compress/filters/csstidy_python/csstidy.py @@ -0,0 +1,636 @@ +# CSSTidy - CSS Parse +# +# CSS Parser class +# +# This file is part of CSSTidy. +# +# CSSTidy is free software you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation either version 2 of the License, or +# (at your option) any later version. +# +# CSSTidy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with CSSTidy if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @package csstidy +# @author Dj Gilcrease (digitalxero at gmail dot com) 2005-2006 + +import re + +from optimizer import CSSOptimizer +from output import CSSPrinter +import data +from tools import SortedDict + +class CSSTidy(object): + #Saves the parsed CSS + _css = "" + _raw_css = SortedDict() + _optimized_css = SortedDict() + + #List of Tokens + _tokens = [] + + #Printer class + _output = None + + #Optimiser class + _optimizer = None + + #Saves the CSS charset (@charset) + _charset = '' + + #Saves all @import URLs + _import = [] + + #Saves the namespace + _namespace = '' + + #Contains the version of csstidy + _version = '1.3' + + #Stores the settings + _settings = {} + + # Saves the parser-status. + # + # Possible values: + # - is = in selector + # - ip = in property + # - iv = in value + # - instr = in string (started at " or ' or ( ) + # - ic = in comment (ignore everything) + # - at = in @-block + _status = 'is' + + #Saves the current at rule (@media) + _at = '' + + #Saves the current selector + _selector = '' + + #Saves the current property + _property = '' + + #Saves the position of , in selectors + _sel_separate = [] + + #Saves the current value + _value = '' + + #Saves the current sub-value + _sub_value = '' + + #Saves all subvalues for a property. + _sub_value_arr = [] + + #Saves the char which opened the last string + _str_char = '' + _cur_string = '' + + #Status from which the parser switched to ic or instr + _from = '' + + #Variable needed to manage string-in-strings, for example url("foo.png") + _str_in_str = False + + #=True if in invalid at-rule + _invalid_at = False + + #=True if something has been added to the current selector + _added = False + + #Saves the message log + _log = SortedDict() + + #Saves the line number + _line = 1 + + def __init__(self): + self._settings['remove_bslash'] = True + self._settings['compress_colors'] = True + self._settings['compress_font-weight'] = True + self._settings['lowercase_s'] = False + self._settings['optimise_shorthands'] = 2 + self._settings['remove_last_'] = False + self._settings['case_properties'] = 1 + self._settings['sort_properties'] = False + self._settings['sort_selectors'] = False + self._settings['merge_selectors'] = 2 + self._settings['discard_invalid_properties'] = False + self._settings['css_level'] = 'CSS2.1' + self._settings['preserve_css'] = False + self._settings['timestamp'] = False + self._settings['template'] = 'highest_compression' + + #Maps self._status to methods + self.__statusMethod = {'is':self.__parseStatus_is, 'ip': self.__parseStatus_ip, 'iv':self.__parseStatus_iv, 'instr':self.__parseStatus_instr, 'ic':self.__parseStatus_ic, 'at':self.__parseStatus_at} + + self._output = CSSPrinter(self) + self._optimizer = CSSOptimizer(self) + + #Public Methods + def getSetting(self, setting): + return self._settings.get(setting, False) + + #Set the value of a setting. + def setSetting(self, setting, value): + self._settings[setting] = value + return True + + def log(self, message, ttype, line = -1): + if line == -1: + line = self._line + + line = int(line) + + add = {'m': message, 't': ttype} + + if not self._log.has_key(line): + self._log[line] = [] + self._log[line].append(add) + elif add not in self._log[line]: + self._log[line].append(add) + + + #Checks if a character is escaped (and returns True if it is) + def escaped(self, string, pos): + return not (string[pos-1] != '\\' or self.escaped(string, pos-1)) + + #Adds CSS to an existing media/selector + def merge_css_blocks(self, media, selector, css_add): + for prop, value in css_add.iteritems(): + self.__css_add_property(media, selector, prop, value, False) + + #Checks if $value is !important. + def is_important(self, value): + return '!important' in value.lower() + + #Returns a value without !important + def gvw_important(self, value): + if self.is_important(value): + ret = value.strip() + ret = ret[0:-9] + ret = ret.strip() + ret = ret[0:-1] + ret = ret.strip() + return ret + + return value + + def parse(self, cssString): + #Switch from \r\n to \n + self._css = cssString.replace("\r\n", "\n") + ' ' + self._raw_css = {} + self._optimized_css = {} + self._curComment = '' + + #Start Parsing + i = 0 + while i < len(cssString): + if self._css[i] == "\n" or self._css[i] == "\r": + self._line += 1 + + i += self.__statusMethod[self._status](i) + + i += 1; + + self._optimized_css = self._optimizer.optimize(self._raw_css) + + def parseFile(self, filename): + try: + f = open(filename, "r") + self.parse(f.read()) + finally: + f.close() + + #Private Methods + def __parseStatus_is(self, idx): + """ + Parse in Selector + """ + ret = 0 + + if self.__is_token(self._css, idx): + if self._css[idx] == '/' and self._css[idx+1] == '*' and self._selector.strip() == '': + self._status = 'ic' + self._from = 'is' + return 1 + + elif self._css[idx] == '@' and self._selector.strip() == '': + #Check for at-rule + self._invalid_at = True + + for name, ttype in data.at_rules.iteritems(): + if self._css[idx+1:len(name)].lower() == name.lower(): + if ttype == 'at': + self._at = '@' + name + else: + self._selector = '@' + name + + self._status = ttype + self._invalid_at = False + ret += len(name) + + if self._invalid_at: + self._selector = '@' + invalid_at_name = '' + for j in xrange(idx+1, len(self._css)): + if not self._css[j].isalpha(): + break; + + invalid_at_name += self._css[j] + + self.log('Invalid @-rule: ' + invalid_at_name + ' (removed)', 'Warning') + + elif self._css[idx] == '"' or self._css[idx] == "'": + self._cur_string = self._css[idx] + self._status = 'instr' + self._str_char = self._css[idx] + self._from = 'is' + + elif self._invalid_at and self._css[idx] == ';': + self._invalid_at = False + self._status = 'is' + + elif self._css[idx] == '{': + self._status = 'ip' + self.__add_token(data.SEL_START, self._selector) + self._added = False; + + elif self._css[idx] == '}': + self.__add_token(data.AT_END, self._at) + self._at = '' + self._selector = '' + self._sel_separate = [] + + elif self._css[idx] == ',': + self._selector = self._selector.strip() + ',' + self._sel_separate.append(len(self._selector)) + + elif self._css[idx] == '\\': + self._selector += self.__unicode(idx) + + #remove unnecessary universal selector, FS#147 + elif not (self._css[idx] == '*' and self._css[idx+1] in ('.', '#', '[', ':')): + self._selector += self._css[idx] + + else: + lastpos = len(self._selector)-1 + + if lastpos == -1 or not ((self._selector[lastpos].isspace() or self.__is_token(self._selector, lastpos) and self._selector[lastpos] == ',') and self._css[idx].isspace()): + self._selector += self._css[idx] + + return ret + + def __parseStatus_ip(self, idx): + """ + Parse in property + """ + if self.__is_token(self._css, idx): + if (self._css[idx] == ':' or self._css[idx] == '=') and self._property != '': + self._status = 'iv' + + if not self.getSetting('discard_invalid_properties') or self.__property_is_valid(self._property): + self.__add_token(data.PROPERTY, self._property) + + elif self._css[idx] == '/' and self._css[idx+1] == '*' and self._property == '': + self._status = 'ic' + self._from = 'ip' + return 1 + + elif self._css[idx] == '}': + self.__explode_selectors() + self._status = 'is' + self._invalid_at = False + self.__add_token(data.SEL_END, self._selector) + self._selector = '' + self._property = '' + + elif self._css[idx] == ';': + self._property = '' + + elif self._css[idx] == '\\': + self._property += self.__unicode(idx) + + elif not self._css[idx].isspace(): + self._property += self._css[idx] + + return 0 + + def __parseStatus_iv(self, idx): + """ + Parse in value + """ + pn = (( self._css[idx] == "\n" or self._css[idx] == "\r") and self.__property_is_next(idx+1) or idx == len(self._css)) #CHECK# + if self.__is_token(self._css, idx) or pn: + if self._css[idx] == '/' and self._css[idx+1] == '*': + self._status = 'ic' + self._from = 'iv' + return 1 + + elif self._css[idx] == '"' or self._css[idx] == "'" or self._css[idx] == '(': + self._cur_string = self._css[idx] + self._str_char = ')' if self._css[idx] == '(' else self._css[idx] + self._status = 'instr' + self._from = 'iv' + + elif self._css[idx] == ',': + self._sub_value = self._sub_value.strip() + ',' + + elif self._css[idx] == '\\': + self._sub_value += self.__unicode(idx) + + elif self._css[idx] == ';' or pn: + if len(self._selector) > 0 and self._selector[0] == '@' and data.at_rules.has_key(self._selector[1:]) and data.at_rules[self._selector[1:]] == 'iv': + self._sub_value_arr.append(self._sub_value.strip()) + + self._status = 'is' + + if '@charset' in self._selector: + self._charset = self._sub_value_arr[0] + + elif '@namespace' in self._selector: + self._namespace = ' '.join(self._sub_value_arr) + + elif '@import' in self._selector: + self._import.append(' '.join(self._sub_value_arr)) + + + self._sub_value_arr = [] + self._sub_value = '' + self._selector = '' + self._sel_separate = [] + + else: + self._status = 'ip' + + elif self._css[idx] != '}': + self._sub_value += self._css[idx] + + if (self._css[idx] == '}' or self._css[idx] == ';' or pn) and self._selector != '': + if self._at == '': + self._at = data.DEFAULT_AT + + #case settings + if self.getSetting('lowercase_s'): + self._selector = self._selector.lower() + + self._property = self._property.lower() + + if self._sub_value != '': + self._sub_value_arr.append(self._sub_value) + self._sub_value = '' + + self._value = ' '.join(self._sub_value_arr) + + + self._selector = self._selector.strip() + + valid = self.__property_is_valid(self._property) + + if (not self._invalid_at or self.getSetting('preserve_css')) and (not self.getSetting('discard_invalid_properties') or valid): + self.__css_add_property(self._at, self._selector, self._property, self._value) + self.__add_token(data.VALUE, self._value) + + if not valid: + if self.getSetting('discard_invalid_properties'): + self.log('Removed invalid property: ' + self._property, 'Warning') + + else: + self.log('Invalid property in ' + self.getSetting('css_level').upper() + ': ' + self._property, 'Warning') + + self._property = ''; + self._sub_value_arr = [] + self._value = '' + + if self._css[idx] == '}': + self.__explode_selectors() + self.__add_token(data.SEL_END, self._selector) + self._status = 'is' + self._invalid_at = False + self._selector = '' + + elif not pn: + self._sub_value += self._css[idx] + + if self._css[idx].isspace(): + if self._sub_value != '': + self._sub_value_arr.append(self._sub_value) + self._sub_value = '' + + return 0 + + def __parseStatus_instr(self, idx): + """ + Parse in String + """ + if self._str_char == ')' and (self._css[idx] == '"' or self._css[idx] == "'") and not self.escaped(self._css, idx): + self._str_in_str = not self._str_in_str + + temp_add = self._css[idx] # ...and no not-escaped backslash at the previous position + if (self._css[idx] == "\n" or self._css[idx] == "\r") and not (self._css[idx-1] == '\\' and not self.escaped(self._css, idx-1)): + temp_add = "\\A " + self.log('Fixed incorrect newline in string', 'Warning') + + if not (self._str_char == ')' and self._css[idx].isspace() and not self._str_in_str): + self._cur_string += temp_add + + if self._css[idx] == self._str_char and not self.escaped(self._css, idx) and not self._str_in_str: + self._status = self._from + regex = re.compile(r'([\s]+)', re.I | re.U | re.S) + if regex.match(self._cur_string) is None and self._property != 'content': + if self._str_char == '"' or self._str_char == "'": + self._cur_string = self._cur_string[1:-1] + + elif len(self._cur_string) > 3 and (self._cur_string[1] == '"' or self._cur_string[1] == "'"): + self._cur_string = self._cur_string[0] + self._cur_string[2:-2] + self._cur_string[-1] + + if self._from == 'iv': + self._sub_value += self._cur_string + + elif self._from == 'is': + self._selector += self._cur_string + + return 0 + + def __parseStatus_ic(self, idx): + """ + Parse css In Comment + """ + if self._css[idx] == '*' and self._css[idx+1] == '/': + self._status = self._from + self.__add_token(data.COMMENT, self._curComment) + self._curComment = '' + return 1 + + else: + self._curComment += self._css[idx] + + return 0 + + def __parseStatus_at(self, idx): + """ + Parse in at-block + """ + if self.__is_token(string, idx): + if self._css[idx] == '/' and self._css[idx+1] == '*': + self._status = 'ic' + self._from = 'at' + return 1 + + elif self._css[i] == '{': + self._status = 'is' + self.__add_token(data.AT_START, self._at) + + elif self._css[i] == ',': + self._at = self._at.strip() + ',' + + elif self._css[i] == '\\': + self._at += self.__unicode(i) + else: + lastpos = len(self._at)-1 + if not (self._at[lastpos].isspace() or self.__is_token(self._at, lastpos) and self._at[lastpos] == ',') and self._css[i].isspace(): + self._at += self._css[i] + + return 0 + + def __explode_selectors(self): + #Explode multiple selectors + if self.getSetting('merge_selectors') == 1: + new_sels = [] + lastpos = 0; + self._sel_separate.append(len(self._selector)) + + for num in xrange(len(self._sel_separate)): + pos = self._sel_separate[num] + if num == (len(self._sel_separate)): #CHECK# + pos += 1 + + new_sels.append(self._selector[lastpos:(pos-lastpos-1)]) + lastpos = pos + + if len(new_sels) > 1: + for selector in new_sels: + self.merge_css_blocks(self._at, selector, self._raw_css[self._at][self._selector]) + + del self._raw_css[self._at][self._selector] + + self._sel_separate = [] + + #Adds a property with value to the existing CSS code + def __css_add_property(self, media, selector, prop, new_val): + if self.getSetting('preserve_css') or new_val.strip() == '': + return + + if not self._raw_css.has_key(media): + self._raw_css[media] = SortedDict() + + if not self._raw_css[media].has_key(selector): + self._raw_css[media][selector] = SortedDict() + + self._added = True + if self._raw_css[media][selector].has_key(prop): + if (self.is_important(self._raw_css[media][selector][prop]) and self.is_important(new_val)) or not self.is_important(self._raw_css[media][selector][prop]): + del self._raw_css[media][selector][prop] + self._raw_css[media][selector][prop] = new_val.strip() + + else: + self._raw_css[media][selector][prop] = new_val.strip() + + #Checks if the next word in a string from pos is a CSS property + def __property_is_next(self, pos): + istring = self._css[pos: len(self._css)] + pos = istring.find(':') + if pos == -1: + return False; + + istring = istring[:pos].strip().lower() + if data.all_properties.has_key(istring): + self.log('Added semicolon to the end of declaration', 'Warning') + return True + + return False; + + #Checks if a property is valid + def __property_is_valid(self, prop): + return (data.all_properties.has_key(prop) and data.all_properties[prop].find(self.getSetting('css_level').upper()) != -1) + + #Adds a token to self._tokens + def __add_token(self, ttype, cssdata, do=False): + if self.getSetting('preserve_css') or do: + if ttype == data.COMMENT: + token = [ttype, cssdata] + else: + token = [ttype, cssdata.strip()] + + self._tokens.append(token) + + #Parse unicode notations and find a replacement character + def __unicode(self, idx): + ##FIX## + return '' + + #Starts parsing from URL + ##USED? + def __parse_from_url(self, url): + try: + if "http" in url.lower() or "https" in url.lower(): + f = urllib.urlopen(url) + else: + f = open(url) + + data = f.read() + return self.parse(data) + finally: + f.close() + + #Checks if there is a token at the current position + def __is_token(self, string, idx): + return (string[idx] in data.tokens and not self.escaped(string, idx)) + + + #Property Methods + def _getOutput(self): + self._output.prepare(self._optimized_css) + return self._output.render + + def _getLog(self): + ret = "" + ks = self._log.keys() + ks.sort() + for line in ks: + for msg in self._log[line]: + ret += "Type: " + msg['t'] + "\n" + ret += "Message: " + msg['m'] + "\n" + ret += "\n" + + return ret + + def _getCSS(self): + return self._css + + + #Properties + Output = property(_getOutput, None) + Log = property(_getLog, None) + CSS = property(_getCSS, None) + + +if __name__ == '__main__': + import sys + tidy = CSSTidy() + f = open(sys.argv[1], "r") + css = f.read() + f.close() + tidy.parse(css) + tidy.Output('file', filename="Stylesheet.min.css") + print tidy.Output() + #print tidy._import \ No newline at end of file diff --git a/apps/compress/filters/csstidy_python/data.py b/apps/compress/filters/csstidy_python/data.py new file mode 100644 index 00000000..bd728cba --- /dev/null +++ b/apps/compress/filters/csstidy_python/data.py @@ -0,0 +1,421 @@ +# Various CSS Data for CSSTidy +# +# This file is part of CSSTidy. +# +# CSSTidy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# CSSTidy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with CSSTidy; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @package csstidy +# @author Florian Schmitz (floele at gmail dot com) 2005 + +AT_START = 1 +AT_END = 2 +SEL_START = 3 +SEL_END = 4 +PROPERTY = 5 +VALUE = 6 +COMMENT = 7 +DEFAULT_AT = 41 + +# All whitespace allowed in CSS +# +# @global array whitespace +# @version 1.0 +whitespace = frozenset([' ',"\n","\t","\r","\x0B"]) + +# All CSS tokens used by csstidy +# +# @global string tokens +# @version 1.0 +tokens = '/@}{;:=\'"(,\\!$%&)#+.<>?[]^`|~' + +# All CSS units (CSS 3 units included) +# +# @see compress_numbers() +# @global array units +# @version 1.0 +units = frozenset(['in','cm','mm','pt','pc','px','rem','em','%','ex','gd','vw','vh','vm','deg','grad','rad','ms','s','khz','hz']) + +# Available at-rules +# +# @global array at_rules +# @version 1.0 +at_rules = {'page':'is', 'font-face':'is', 'charset':'iv', 'import':'iv', 'namespace':'iv', 'media':'at'} + +# Properties that need a value with unit +# +# @todo CSS3 properties +# @see compress_numbers() +# @global array unit_values +# @version 1.2 +unit_values = frozenset(['background', 'background-position', 'border', 'border-top', 'border-right', 'border-bottom', + 'border-left', 'border-width', 'border-top-width', 'border-right-width', 'border-left-width', + 'border-bottom-width', 'bottom', 'border-spacing', 'font-size','height', 'left', 'margin', 'margin-top', + 'margin-right', 'margin-bottom', 'margin-left', 'max-height', 'max-width', 'min-height', 'min-width', + 'outline-width', 'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left','position', + 'right', 'top', 'text-indent', 'letter-spacing', 'word-spacing', 'width' + ]) + + +# Properties that allow as value +# +# @todo CSS3 properties +# @see compress_numbers() +# @global array color_values +# @version 1.0 +color_values = frozenset(['background-color', 'border-color', 'border-top-color', 'border-right-color', + 'border-bottom-color', 'border-left-color', 'color', 'outline-color']) + + +# Default values for the background properties +# +# @todo Possibly property names will change during CSS3 development +# @global array background_prop_default +# @see dissolve_short_bg() +# @see merge_bg() +# @version 1.0 +background_prop_default = {} +background_prop_default['background-image'] = 'none' +background_prop_default['background-size'] = 'auto' +background_prop_default['background-repeat'] = 'repeat' +background_prop_default['background-position'] = '0 0' +background_prop_default['background-attachment'] = 'scroll' +background_prop_default['background-clip'] = 'border' +background_prop_default['background-origin'] = 'padding' +background_prop_default['background-color'] = 'transparent' + +# A list of non-W3C color names which get replaced by their hex-codes +# +# @global array replace_colors +# @see cut_color() +# @version 1.0 +replace_colors = {} +replace_colors['aliceblue'] = '#F0F8FF' +replace_colors['antiquewhite'] = '#FAEBD7' +replace_colors['aquamarine'] = '#7FFFD4' +replace_colors['azure'] = '#F0FFFF' +replace_colors['beige'] = '#F5F5DC' +replace_colors['bisque'] = '#FFE4C4' +replace_colors['blanchedalmond'] = '#FFEBCD' +replace_colors['blueviolet'] = '#8A2BE2' +replace_colors['brown'] = '#A52A2A' +replace_colors['burlywood'] = '#DEB887' +replace_colors['cadetblue'] = '#5F9EA0' +replace_colors['chartreuse'] = '#7FFF00' +replace_colors['chocolate'] = '#D2691E' +replace_colors['coral'] = '#FF7F50' +replace_colors['cornflowerblue'] = '#6495ED' +replace_colors['cornsilk'] = '#FFF8DC' +replace_colors['crimson'] = '#DC143C' +replace_colors['cyan'] = '#00FFFF' +replace_colors['darkblue'] = '#00008B' +replace_colors['darkcyan'] = '#008B8B' +replace_colors['darkgoldenrod'] = '#B8860B' +replace_colors['darkgray'] = '#A9A9A9' +replace_colors['darkgreen'] = '#006400' +replace_colors['darkkhaki'] = '#BDB76B' +replace_colors['darkmagenta'] = '#8B008B' +replace_colors['darkolivegreen'] = '#556B2F' +replace_colors['darkorange'] = '#FF8C00' +replace_colors['darkorchid'] = '#9932CC' +replace_colors['darkred'] = '#8B0000' +replace_colors['darksalmon'] = '#E9967A' +replace_colors['darkseagreen'] = '#8FBC8F' +replace_colors['darkslateblue'] = '#483D8B' +replace_colors['darkslategray'] = '#2F4F4F' +replace_colors['darkturquoise'] = '#00CED1' +replace_colors['darkviolet'] = '#9400D3' +replace_colors['deeppink'] = '#FF1493' +replace_colors['deepskyblue'] = '#00BFFF' +replace_colors['dimgray'] = '#696969' +replace_colors['dodgerblue'] = '#1E90FF' +replace_colors['feldspar'] = '#D19275' +replace_colors['firebrick'] = '#B22222' +replace_colors['floralwhite'] = '#FFFAF0' +replace_colors['forestgreen'] = '#228B22' +replace_colors['gainsboro'] = '#DCDCDC' +replace_colors['ghostwhite'] = '#F8F8FF' +replace_colors['gold'] = '#FFD700' +replace_colors['goldenrod'] = '#DAA520' +replace_colors['greenyellow'] = '#ADFF2F' +replace_colors['honeydew'] = '#F0FFF0' +replace_colors['hotpink'] = '#FF69B4' +replace_colors['indianred'] = '#CD5C5C' +replace_colors['indigo'] = '#4B0082' +replace_colors['ivory'] = '#FFFFF0' +replace_colors['khaki'] = '#F0E68C' +replace_colors['lavender'] = '#E6E6FA' +replace_colors['lavenderblush'] = '#FFF0F5' +replace_colors['lawngreen'] = '#7CFC00' +replace_colors['lemonchiffon'] = '#FFFACD' +replace_colors['lightblue'] = '#ADD8E6' +replace_colors['lightcoral'] = '#F08080' +replace_colors['lightcyan'] = '#E0FFFF' +replace_colors['lightgoldenrodyellow'] = '#FAFAD2' +replace_colors['lightgrey'] = '#D3D3D3' +replace_colors['lightgreen'] = '#90EE90' +replace_colors['lightpink'] = '#FFB6C1' +replace_colors['lightsalmon'] = '#FFA07A' +replace_colors['lightseagreen'] = '#20B2AA' +replace_colors['lightskyblue'] = '#87CEFA' +replace_colors['lightslateblue'] = '#8470FF' +replace_colors['lightslategray'] = '#778899' +replace_colors['lightsteelblue'] = '#B0C4DE' +replace_colors['lightyellow'] = '#FFFFE0' +replace_colors['limegreen'] = '#32CD32' +replace_colors['linen'] = '#FAF0E6' +replace_colors['magenta'] = '#FF00FF' +replace_colors['mediumaquamarine'] = '#66CDAA' +replace_colors['mediumblue'] = '#0000CD' +replace_colors['mediumorchid'] = '#BA55D3' +replace_colors['mediumpurple'] = '#9370D8' +replace_colors['mediumseagreen'] = '#3CB371' +replace_colors['mediumslateblue'] = '#7B68EE' +replace_colors['mediumspringgreen'] = '#00FA9A' +replace_colors['mediumturquoise'] = '#48D1CC' +replace_colors['mediumvioletred'] = '#C71585' +replace_colors['midnightblue'] = '#191970' +replace_colors['mintcream'] = '#F5FFFA' +replace_colors['mistyrose'] = '#FFE4E1' +replace_colors['moccasin'] = '#FFE4B5' +replace_colors['navajowhite'] = '#FFDEAD' +replace_colors['oldlace'] = '#FDF5E6' +replace_colors['olivedrab'] = '#6B8E23' +replace_colors['orangered'] = '#FF4500' +replace_colors['orchid'] = '#DA70D6' +replace_colors['palegoldenrod'] = '#EEE8AA' +replace_colors['palegreen'] = '#98FB98' +replace_colors['paleturquoise'] = '#AFEEEE' +replace_colors['palevioletred'] = '#D87093' +replace_colors['papayawhip'] = '#FFEFD5' +replace_colors['peachpuff'] = '#FFDAB9' +replace_colors['peru'] = '#CD853F' +replace_colors['pink'] = '#FFC0CB' +replace_colors['plum'] = '#DDA0DD' +replace_colors['powderblue'] = '#B0E0E6' +replace_colors['rosybrown'] = '#BC8F8F' +replace_colors['royalblue'] = '#4169E1' +replace_colors['saddlebrown'] = '#8B4513' +replace_colors['salmon'] = '#FA8072' +replace_colors['sandybrown'] = '#F4A460' +replace_colors['seagreen'] = '#2E8B57' +replace_colors['seashell'] = '#FFF5EE' +replace_colors['sienna'] = '#A0522D' +replace_colors['skyblue'] = '#87CEEB' +replace_colors['slateblue'] = '#6A5ACD' +replace_colors['slategray'] = '#708090' +replace_colors['snow'] = '#FFFAFA' +replace_colors['springgreen'] = '#00FF7F' +replace_colors['steelblue'] = '#4682B4' +replace_colors['tan'] = '#D2B48C' +replace_colors['thistle'] = '#D8BFD8' +replace_colors['tomato'] = '#FF6347' +replace_colors['turquoise'] = '#40E0D0' +replace_colors['violet'] = '#EE82EE' +replace_colors['violetred'] = '#D02090' +replace_colors['wheat'] = '#F5DEB3' +replace_colors['whitesmoke'] = '#F5F5F5' +replace_colors['yellowgreen'] = '#9ACD32' + +#A list of optimized colors +optimize_colors = {} +optimize_colors['black'] = '#000' +optimize_colors['fuchsia'] = '#F0F' +optimize_colors['white'] = '#FFF' +optimize_colors['yellow'] = '#FF0' +optimize_colors['cyan'] = '#0FF' +optimize_colors['magenta'] = '#F0F' +optimize_colors['lightslategray'] = '#789' + +optimize_colors['#800000'] = 'maroon' +optimize_colors['#FFA500'] = 'orange' +optimize_colors['#808000'] = 'olive' +optimize_colors['#800080'] = 'purple' +optimize_colors['#008000'] = 'green' +optimize_colors['#000080'] = 'navy' +optimize_colors['#008080'] = 'teal' +optimize_colors['#C0C0C0'] = 'silver' +optimize_colors['#808080'] = 'gray' +optimize_colors['#4B0082'] = 'indigo' +optimize_colors['#FFD700'] = 'gold' +optimize_colors['#A52A2A'] = 'brown' +optimize_colors['#00FFFF'] = 'cyan' +optimize_colors['#EE82EE'] = 'violet' +optimize_colors['#DA70D6'] = 'orchid' +optimize_colors['#FFE4C4'] = 'bisque' +optimize_colors['#F0E68C'] = 'khaki' +optimize_colors['#F5DEB3'] = 'wheat' +optimize_colors['#FF7F50'] = 'coral' +optimize_colors['#F5F5DC'] = 'beige' +optimize_colors['#F0FFFF'] = 'azure' +optimize_colors['#A0522D'] = 'sienna' +optimize_colors['#CD853F'] = 'peru' +optimize_colors['#FFFFF0'] = 'ivory' +optimize_colors['#DDA0DD'] = 'plum' +optimize_colors['#D2B48C'] = 'tan' +optimize_colors['#FFC0CB'] = 'pink' +optimize_colors['#FFFAFA'] = 'snow' +optimize_colors['#FA8072'] = 'salmon' +optimize_colors['#FF6347'] = 'tomato' +optimize_colors['#FAF0E6'] = 'linen' +optimize_colors['#F00'] = 'red' + + +# A list of all shorthand properties that are devided into four properties and/or have four subvalues +# +# @global array shorthands +# @todo Are there new ones in CSS3? +# @see dissolve_4value_shorthands() +# @see merge_4value_shorthands() +# @version 1.0 +shorthands = {} +shorthands['border-color'] = ['border-top-color','border-right-color','border-bottom-color','border-left-color'] +shorthands['border-style'] = ['border-top-style','border-right-style','border-bottom-style','border-left-style'] +shorthands['border-width'] = ['border-top-width','border-right-width','border-bottom-width','border-left-width'] +shorthands['margin'] = ['margin-top','margin-right','margin-bottom','margin-left'] +shorthands['padding'] = ['padding-top','padding-right','padding-bottom','padding-left'] +shorthands['-moz-border-radius'] = 0 + +# All CSS Properties. Needed for csstidy::property_is_next() +# +# @global array all_properties +# @todo Add CSS3 properties +# @version 1.0 +# @see csstidy::property_is_next() +all_properties = {} +all_properties['background'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['background-color'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['background-image'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['background-repeat'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['background-attachment'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['background-position'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-top'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-right'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-bottom'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-left'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-color'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-top-color'] = 'CSS2.0,CSS2.1' +all_properties['border-bottom-color'] = 'CSS2.0,CSS2.1' +all_properties['border-left-color'] = 'CSS2.0,CSS2.1' +all_properties['border-right-color'] = 'CSS2.0,CSS2.1' +all_properties['border-style'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-top-style'] = 'CSS2.0,CSS2.1' +all_properties['border-right-style'] = 'CSS2.0,CSS2.1' +all_properties['border-left-style'] = 'CSS2.0,CSS2.1' +all_properties['border-bottom-style'] = 'CSS2.0,CSS2.1' +all_properties['border-width'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-top-width'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-right-width'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-left-width'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-bottom-width'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['border-collapse'] = 'CSS2.0,CSS2.1' +all_properties['border-spacing'] = 'CSS2.0,CSS2.1' +all_properties['bottom'] = 'CSS2.0,CSS2.1' +all_properties['caption-side'] = 'CSS2.0,CSS2.1' +all_properties['content'] = 'CSS2.0,CSS2.1' +all_properties['clear'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['clip'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['color'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['counter-reset'] = 'CSS2.0,CSS2.1' +all_properties['counter-increment'] = 'CSS2.0,CSS2.1' +all_properties['cursor'] = 'CSS2.0,CSS2.1' +all_properties['empty-cells'] = 'CSS2.0,CSS2.1' +all_properties['display'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['direction'] = 'CSS2.0,CSS2.1' +all_properties['float'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['font'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['font-family'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['font-style'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['font-variant'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['font-weight'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['font-stretch'] = 'CSS2.0' +all_properties['font-size-adjust'] = 'CSS2.0' +all_properties['font-size'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['height'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['left'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['line-height'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['list-style'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['list-style-type'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['list-style-image'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['list-style-position'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['margin'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['margin-top'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['margin-right'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['margin-bottom'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['margin-left'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['marks'] = 'CSS1.0,CSS2.0' +all_properties['marker-offset'] = 'CSS2.0' +all_properties['max-height'] = 'CSS2.0,CSS2.1' +all_properties['max-width'] = 'CSS2.0,CSS2.1' +all_properties['min-height'] = 'CSS2.0,CSS2.1' +all_properties['min-width'] = 'CSS2.0,CSS2.1' +all_properties['overflow'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['orphans'] = 'CSS2.0,CSS2.1' +all_properties['outline'] = 'CSS2.0,CSS2.1' +all_properties['outline-width'] = 'CSS2.0,CSS2.1' +all_properties['outline-style'] = 'CSS2.0,CSS2.1' +all_properties['outline-color'] = 'CSS2.0,CSS2.1' +all_properties['padding'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['padding-top'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['padding-right'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['padding-bottom'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['padding-left'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['page-break-before'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['page-break-after'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['page-break-inside'] = 'CSS2.0,CSS2.1' +all_properties['page'] = 'CSS2.0' +all_properties['position'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['quotes'] = 'CSS2.0,CSS2.1' +all_properties['right'] = 'CSS2.0,CSS2.1' +all_properties['size'] = 'CSS1.0,CSS2.0' +all_properties['speak-header'] = 'CSS2.0,CSS2.1' +all_properties['table-layout'] = 'CSS2.0,CSS2.1' +all_properties['top'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['text-indent'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['text-align'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['text-decoration'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['text-shadow'] = 'CSS2.0' +all_properties['letter-spacing'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['word-spacing'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['text-transform'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['white-space'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['unicode-bidi'] = 'CSS2.0,CSS2.1' +all_properties['vertical-align'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['visibility'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['width'] = 'CSS1.0,CSS2.0,CSS2.1' +all_properties['widows'] = 'CSS2.0,CSS2.1' +all_properties['z-index'] = 'CSS1.0,CSS2.0,CSS2.1' + +# Speech # +all_properties['volume'] = 'CSS2.0,CSS2.1' +all_properties['speak'] = 'CSS2.0,CSS2.1' +all_properties['pause'] = 'CSS2.0,CSS2.1' +all_properties['pause-before'] = 'CSS2.0,CSS2.1' +all_properties['pause-after'] = 'CSS2.0,CSS2.1' +all_properties['cue'] = 'CSS2.0,CSS2.1' +all_properties['cue-before'] = 'CSS2.0,CSS2.1' +all_properties['cue-after'] = 'CSS2.0,CSS2.1' +all_properties['play-during'] = 'CSS2.0,CSS2.1' +all_properties['azimuth'] = 'CSS2.0,CSS2.1' +all_properties['elevation'] = 'CSS2.0,CSS2.1' +all_properties['speech-rate'] = 'CSS2.0,CSS2.1' +all_properties['voice-family'] = 'CSS2.0,CSS2.1' +all_properties['pitch'] = 'CSS2.0,CSS2.1' +all_properties['pitch-range'] = 'CSS2.0,CSS2.1' +all_properties['stress'] = 'CSS2.0,CSS2.1' +all_properties['richness'] = 'CSS2.0,CSS2.1' +all_properties['speak-punctuation'] = 'CSS2.0,CSS2.1' +all_properties['speak-numeral'] = 'CSS2.0,CSS2.1' \ No newline at end of file diff --git a/apps/compress/filters/csstidy_python/optimizer.py b/apps/compress/filters/csstidy_python/optimizer.py new file mode 100644 index 00000000..7cd284cf --- /dev/null +++ b/apps/compress/filters/csstidy_python/optimizer.py @@ -0,0 +1,383 @@ +# CSSTidy - CSS Optimizer +# +# CSS Optimizer class +# +# This file is part of CSSTidy. +# +# CSSTidy is free software you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation either version 2 of the License, or +# (at your option) any later version. +# +# CSSTidy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with CSSTidy if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @package csstidy +# @author Dj Gilcrease (digitalxero at gmail dot com) 2005-2006 + +import data +from tools import SortedDict + + +class CSSOptimizer(object): + def __init__(self, parser): + #raw_css is a dict + self.parser = parser + self._optimized_css = SortedDict + + +#PUBLIC METHODS + def optimize(self, raw_css): + if self.parser.getSetting('preserve_css'): + return raw_css + + self._optimized_css = raw_css + + if self.parser.getSetting('merge_selectors') == 2: + self.__merge_selectors() + + ##OPTIMIZE## + for media, css in self._optimized_css.iteritems(): + for selector, cssdata in css.iteritems(): + if self.parser.getSetting('optimise_shorthands') >= 1: + cssdata = self.__merge_4value_shorthands(cssdata) + + if self.parser.getSetting('optimise_shorthands') >= 2: + cssdata = self.__merge_bg(cssdata) + + for item, value in cssdata.iteritems(): + value = self.__compress_numbers(item, value) + value = self.__compress_important(value) + + if item in data.color_values and self.parser.getSetting('compress_colors'): + old = value[:] + value = self.__compress_color(value) + if old != value: + self.parser.log('In "' + selector + '" Optimised ' + item + ': Changed ' + old + ' to ' + value, 'Information') + + if item == 'font-weight' and self.parser.getSetting('compress_font-weight'): + if value == 'bold': + value = '700' + self.parser.log('In "' + selector + '" Optimised font-weight: Changed "bold" to "700"', 'Information') + + elif value == 'normal': + value = '400' + self.parser.log('In "' + selector + '" Optimised font-weight: Changed "normal" to "400"', 'Information') + + self._optimized_css[media][selector][item] = value + + + return self._optimized_css + + +#PRIVATE METHODS + def __merge_bg(self, cssdata): + """ + Merges all background properties + @cssdata (dict) is a dictionary of the selector properties + """ + #Max number of background images. CSS3 not yet fully implemented + img = 1 + clr = 1 + bg_img_list = [] + if cssdata.has_key('background-image'): + img = len(cssdata['background-image'].split(',')) + bg_img_list = self.parser.gvw_important(cssdata['background-image']).split(',') + + elif cssdata.has_key('background-color'): + clr = len(cssdata['background-color'].split(',')) + + + number_of_values = max(img, clr, 1) + + new_bg_value = '' + important = '' + + for i in xrange(number_of_values): + for bg_property, default_value in data.background_prop_default.iteritems(): + #Skip if property does not exist + if not cssdata.has_key(bg_property): + continue + + cur_value = cssdata[bg_property] + + #Skip some properties if there is no background image + if (len(bg_img_list) > i and bg_img_list[i] == 'none') and bg_property in frozenset(['background-size', 'background-position', 'background-attachment', 'background-repeat']): + continue + + #Remove !important + if self.parser.is_important(cur_value): + important = ' !important' + cur_value = self.parser.gvw_important(cur_value) + + #Do not add default values + if cur_value == default_value: + continue + + temp = cur_value.split(',') + + if len(temp) > i: + if bg_property == 'background-size': + new_bg_value += '(' + temp[i] + ') ' + + else: + new_bg_value += temp[i] + ' ' + + new_bg_value = new_bg_value.strip() + if i != (number_of_values-1): + new_bg_value += ',' + + #Delete all background-properties + for bg_property, default_value in data.background_prop_default.iteritems(): + try: + del cssdata[bg_property] + except: + pass + + #Add new background property + if new_bg_value != '': + cssdata['background'] = new_bg_value + important + + return cssdata + + def __merge_4value_shorthands(self, cssdata): + """ + Merges Shorthand properties again, the opposite of dissolve_4value_shorthands() + @cssdata (dict) is a dictionary of the selector properties + """ + for key, value in data.shorthands.iteritems(): + important = '' + if value != 0 and cssdata.has_key(value[0]) and cssdata.has_key(value[1]) and cssdata.has_key(value[2]) and cssdata.has_key(value[3]): + cssdata[key] = '' + + for i in xrange(4): + val = cssdata[value[i]] + if self.parser.is_important(val): + important = '!important' + cssdata[key] += self.parser.gvw_important(val) + ' ' + + else: + cssdata[key] += val + ' ' + + del cssdata[value[i]] + if cssdata.has_key(key): + cssdata[key] = self.__shorthand(cssdata[key] + important.strip()) + + return cssdata + + + def __merge_selectors(self): + """ + Merges selectors with same properties. Example: a{color:red} b{color:red} . a,b{color:red} + Very basic and has at least one bug. Hopefully there is a replacement soon. + @selector_one (string) is the current selector + @value_one (dict) is a dictionary of the selector properties + Note: Currently is the elements of a selector are identical, but in a different order, they are not merged + """ + + ##OPTIMIZE## + ##FIX## + + raw_css = self._optimized_css.copy() + delete = [] + add = SortedDict() + for media, css in raw_css.iteritems(): + for selector_one, value_one in css.iteritems(): + newsel = selector_one + + for selector_two, value_two in css.iteritems(): + if selector_one == selector_two: + #We need to skip self + continue + + if value_one == value_two: + #Ok, we need to merge these two selectors + newsel += ', ' + selector_two + delete.append((media, selector_two)) + + + if not add.has_key(media): + add[media] = SortedDict() + + add[media][newsel] = value_one + delete.append((media, selector_one)) + + for item in delete: + try: + del self._optimized_css[item[0]][item[1]] + except: + #Must have already been deleted + continue + + for media, css in add.iteritems(): + self._optimized_css[media].update(css) + + + + def __shorthand(self, value): + """ + Compresses shorthand values. Example: margin:1px 1px 1px 1px . margin:1px + @value (string) + """ + + ##FIX## + + important = ''; + if self.parser.is_important(value): + value_list = self.parser.gvw_important(value) + important = '!important' + else: + value_list = value + + ret = value + value_list = value_list.split(' ') + + if len(value_list) == 4: + if value_list[0] == value_list[1] and value_list[0] == value_list[2] and value_list[0] == value_list[3]: + ret = value_list[0] + important + + elif value_list[1] == value_list[3] and value_list[0] == value_list[2]: + ret = value_list[0] + ' ' + value_list[1] + important + + elif value_list[1] == value_list[3]: + ret = value_list[0] + ' ' + value_list[1] + ' ' + value_list[2] + important + + elif len(value_list) == 3: + if value_list[0] == value_list[1] and value_list[0] == value_list[2]: + ret = value_list[0] + important + + elif value_list[0] == value_list[2]: + return value_list[0] + ' ' + value_list[1] + important + + elif len(value_list) == 2: + if value_list[0] == value_list[1]: + ret = value_list[0] + important + + if ret != value: + self.parser.log('Optimised shorthand notation: Changed "' + value + '" to "' + ret + '"', 'Information') + + return ret + + def __compress_important(self, value): + """ + Removes unnecessary whitespace in ! important + @value (string) + """ + if self.parser.is_important(value): + value = self.parser.gvw_important(value) + '!important' + + return value + + def __compress_numbers(self, prop, value): + """ + Compresses numbers (ie. 1.0 becomes 1 or 1.100 becomes 1.1 ) + @value (string) is the posible number to be compressed + """ + + ##FIX## + + value = value.split('/') + + for l in xrange(len(value)): + #continue if no numeric value + if not (len(value[l]) > 0 and (value[l][0].isdigit() or value[l][0] in ('+', '-') )): + continue + + #Fix bad colors + if prop in data.color_values: + value[l] = '#' + value[l] + + is_floatable = False + try: + float(value[l]) + is_floatable = True + except: + pass + + if is_floatable and float(value[l]) == 0: + value[l] = '0' + + elif value[l][0] != '#': + unit_found = False + for unit in data.units: + pos = value[l].lower().find(unit) + if pos != -1 and prop not in data.shorthands: + value[l] = self.__remove_leading_zeros(float(value[l][:pos])) + unit + unit_found = True + break; + + if not unit_found and prop in data.unit_values and prop not in data.shorthands: + value[l] = self.__remove_leading_zeros(float(value[l])) + 'px' + + elif not unit_found and prop not in data.shorthands: + value[l] = self.__remove_leading_zeros(float(value[l])) + + + if len(value) > 1: + return '/'.join(value) + + return value[0] + + def __remove_leading_zeros(self, float_val): + """ + Removes the leading zeros from a float value + @float_val (float) + @returns (string) + """ + #Remove leading zero + if abs(float_val) < 1: + if float_val < 0: + float_val = '-' . str(float_val)[2:] + else: + float_val = str(float_val)[1:] + + return str(float_val) + + def __compress_color(self, color): + """ + Color compression function. Converts all rgb() values to #-values and uses the short-form if possible. Also replaces 4 color names by #-values. + @color (string) the {posible} color to change + """ + + #rgb(0,0,0) . #000000 (or #000 in this case later) + if color[:4].lower() == 'rgb(': + color_tmp = color[4:(len(color)-5)] + color_tmp = color_tmp.split(',') + + for c in color_tmp: + c = c.strip() + if c[:-1] == '%': + c = round((255*color_tmp[i])/100) + + if color_tmp[i] > 255: + color_tmp[i] = 255 + + color = '#' + + for i in xrange(3): + if color_tmp[i] < 16: + color += '0' + str(hex(color_tmp[i])).replace('0x', '') + else: + color += str(hex(color_tmp[i])).replace('0x', '') + + #Fix bad color names + if data.replace_colors.has_key(color.lower()): + color = data.replace_colors[color.lower()] + + #aabbcc . #abc + if len(color) == 7: + color_temp = color.lower() + if color_temp[0] == '#' and color_temp[1] == color_temp[2] and color_temp[3] == color_temp[4] and color_temp[5] == color_temp[6]: + color = '#' + color[1] + color[3] + color[5] + + if data.optimize_colors.has_key(color.lower()): + color = data.optimize_colors[color.lower()] + + return color \ No newline at end of file diff --git a/apps/compress/filters/csstidy_python/output.py b/apps/compress/filters/csstidy_python/output.py new file mode 100644 index 00000000..795a0d05 --- /dev/null +++ b/apps/compress/filters/csstidy_python/output.py @@ -0,0 +1,101 @@ +# CSSTidy - CSS Printer +# +# CSS Printer class +# +# This file is part of CSSTidy. +# +# CSSTidy is free software you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation either version 2 of the License, or +# (at your option) any later version. +# +# CSSTidy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with CSSTidy if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @package csstidy +# @author Dj Gilcrease (digitalxero at gmail dot com) 2005-2006 + +import data + +class CSSPrinter(object): + def __init__(self, parser): + self.parser = parser + self._css = {} + self.__renderMethods = {'string': self.__renderString, 'file': self.__renderFile} + +#PUBLIC METHODS + def prepare(self, css): + self._css = css + + def render(self, output="string", *args, **kwargs): + return self.__renderMethods[output](*args, **kwargs) + +#PRIVATE METHODS + def __renderString(self, *args, **kwargs): + ##OPTIMIZE## + template = self.parser.getSetting('template') + ret = "" + + if template == 'highest_compression': + top_line_end = "" + iner_line_end = "" + bottom_line_end = "" + indent = "" + + elif template == 'high_compression': + top_line_end = "\n" + iner_line_end = "" + bottom_line_end = "\n" + indent = "" + + elif template == 'default': + top_line_end = "\n" + iner_line_end = "\n" + bottom_line_end = "\n\n" + indent = "" + + elif template == 'low_compression': + top_line_end = "\n" + iner_line_end = "\n" + bottom_line_end = "\n\n" + indent = " " + + if self.parser.getSetting('timestamp'): + ret += '/# CSSTidy ' + self.parser.version + ': ' + datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0000") + ' #/' + top_line_end + + for item in self.parser._import: + ret += '@import(' + item + ');' + top_line_end + + for item in self.parser._charset: + ret += '@charset(' + item + ');' + top_line_end + + for item in self.parser._namespace: + ret += '@namespace(' + item + ');' + top_line_end + + for media, css in self._css.iteritems(): + for selector, cssdata in css.iteritems(): + ret += selector + '{' + top_line_end + + for item, value in cssdata.iteritems(): + ret += indent + item + ':' + value + ';' + iner_line_end + + ret += '}' + bottom_line_end + + return ret + + def __renderFile(self, filename=None, *args, **kwargs): + if filename is None: + return self.__renderString() + + try: + f = open(filename, "w") + f.write(self.__renderString()) + finally: + f.close() \ No newline at end of file diff --git a/apps/compress/filters/csstidy_python/tools.py b/apps/compress/filters/csstidy_python/tools.py new file mode 100644 index 00000000..e62faef2 --- /dev/null +++ b/apps/compress/filters/csstidy_python/tools.py @@ -0,0 +1,109 @@ + +class SortedDict(dict): + """ + A dictionary that keeps its keys in the order in which they're inserted. + """ + def __init__(self, data=None): + if data is None: + data = {} + super(SortedDict, self).__init__(data) + if isinstance(data, dict): + self.keyOrder = data.keys() + else: + self.keyOrder = [] + for key, value in data: + if key not in self.keyOrder: + self.keyOrder.append(key) + + def __deepcopy__(self, memo): + from copy import deepcopy + return self.__class__([(key, deepcopy(value, memo)) + for key, value in self.iteritems()]) + + def __setitem__(self, key, value): + super(SortedDict, self).__setitem__(key, value) + if key not in self.keyOrder: + self.keyOrder.append(key) + + def __delitem__(self, key): + super(SortedDict, self).__delitem__(key) + self.keyOrder.remove(key) + + def __iter__(self): + for k in self.keyOrder: + yield k + + def pop(self, k, *args): + result = super(SortedDict, self).pop(k, *args) + try: + self.keyOrder.remove(k) + except ValueError: + # Key wasn't in the dictionary in the first place. No problem. + pass + return result + + def popitem(self): + result = super(SortedDict, self).popitem() + self.keyOrder.remove(result[0]) + return result + + def items(self): + return zip(self.keyOrder, self.values()) + + def iteritems(self): + for key in self.keyOrder: + yield key, super(SortedDict, self).__getitem__(key) + + def keys(self): + return self.keyOrder[:] + + def iterkeys(self): + return iter(self.keyOrder) + + def values(self): + return [super(SortedDict, self).__getitem__(k) for k in self.keyOrder] + + def itervalues(self): + for key in self.keyOrder: + yield super(SortedDict, self).__getitem__(key) + + def update(self, dict_): + for k, v in dict_.items(): + self.__setitem__(k, v) + + def setdefault(self, key, default): + if key not in self.keyOrder: + self.keyOrder.append(key) + return super(SortedDict, self).setdefault(key, default) + + def value_for_index(self, index): + """Returns the value of the item at the given zero-based index.""" + return self[self.keyOrder[index]] + + def insert(self, index, key, value): + """Inserts the key, value pair before the item with the given index.""" + if key in self.keyOrder: + n = self.keyOrder.index(key) + del self.keyOrder[n] + if n < index: + index -= 1 + self.keyOrder.insert(index, key) + super(SortedDict, self).__setitem__(key, value) + + def copy(self): + """Returns a copy of this object.""" + # This way of initializing the copy means it works for subclasses, too. + obj = self.__class__(self) + obj.keyOrder = self.keyOrder[:] + return obj + + def __repr__(self): + """ + Replaces the normal dict.__repr__ with a version that returns the keys + in their sorted order. + """ + return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()]) + + def clear(self): + super(SortedDict, self).clear() + self.keyOrder = [] \ No newline at end of file diff --git a/apps/compress/filters/jsmin/__init__.py b/apps/compress/filters/jsmin/__init__.py new file mode 100644 index 00000000..d2262008 --- /dev/null +++ b/apps/compress/filters/jsmin/__init__.py @@ -0,0 +1,6 @@ +from compress.filters.jsmin.jsmin import jsmin +from compress.filter_base import FilterBase + +class JSMinFilter(FilterBase): + def filter_js(self, js): + return jsmin(js) \ No newline at end of file diff --git a/apps/compress/filters/jsmin/jsmin.py b/apps/compress/filters/jsmin/jsmin.py new file mode 100644 index 00000000..4f9d384f --- /dev/null +++ b/apps/compress/filters/jsmin/jsmin.py @@ -0,0 +1,218 @@ +#!/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 diff --git a/apps/compress/filters/yui/__init__.py b/apps/compress/filters/yui/__init__.py new file mode 100644 index 00000000..1e2e711f --- /dev/null +++ b/apps/compress/filters/yui/__init__.py @@ -0,0 +1,44 @@ +import subprocess + +from django.conf import settings + +from compress.filter_base import FilterBase, FilterError + +BINARY = getattr(settings, 'COMPRESS_YUI_BINARY', 'java -jar yuicompressor.jar') +CSS_ARGUMENTS = getattr(settings, 'COMPRESS_YUI_CSS_ARGUMENTS', '') +JS_ARGUMENTS = getattr(settings, 'COMPRESS_YUI_JS_ARGUMENTS', '') + +class YUICompressorFilter(FilterBase): + + def filter_common(self, content, type_, arguments): + command = '%s --type=%s %s' % (BINARY, type_, arguments) + + if self.verbose: + command += ' --verbose' + + p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) + p.stdin.write(content) + p.stdin.close() + + filtered_css = p.stdout.read() + p.stdout.close() + + err = p.stderr.read() + p.stderr.close() + + if p.wait() != 0: + if not err: + err = 'Unable to apply YUI Compressor filter' + + raise FilterError(err) + + if self.verbose: + print err + + return filtered_css + + def filter_js(self, js): + return self.filter_common(js, 'js', JS_ARGUMENTS) + + def filter_css(self, css): + return self.filter_common(css, 'css', CSS_ARGUMENTS) \ No newline at end of file diff --git a/apps/compress/management/__init__.py b/apps/compress/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/compress/management/commands/__init__.py b/apps/compress/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/compress/management/commands/synccompress.py b/apps/compress/management/commands/synccompress.py new file mode 100644 index 00000000..8217979d --- /dev/null +++ b/apps/compress/management/commands/synccompress.py @@ -0,0 +1,58 @@ +from django.core.management.base import NoArgsCommand +from optparse import make_option + +from django.conf import settings + +class Command(NoArgsCommand): + option_list = NoArgsCommand.option_list + ( + make_option('--force', action='store_true', default=False, help='Force update of all files, even if the source files are older than the current compressed file.'), + ) + help = 'Updates and compresses CSS and JavsScript on-demand, without restarting Django' + args = '' + + def handle_noargs(self, **options): + + force = options.get('force', False) + verbosity = int(options.get('verbosity', 1)) + + from compress.utils import needs_update, filter_css, filter_js + + for name, css in settings.COMPRESS_CSS.items(): + u, version = needs_update(css['output_filename'], + css['source_filenames']) + + if (force or u) or verbosity >= 2: + msg = 'CSS Group \'%s\'' % name + print msg + print len(msg) * '-' + print "Version: %s" % version + + if force or u: + filter_css(css, verbosity) + + if (force or u) or verbosity >= 2: + print + + for name, js in settings.COMPRESS_JS.items(): + u, version = needs_update(js['output_filename'], + js['source_filenames']) + + if (force or u) or verbosity >= 2: + msg = 'JavaScript Group \'%s\'' % name + print msg + print len(msg) * '-' + print "Version: %s" % version + + if force or u: + filter_js(js, verbosity) + + if (force or u) or verbosity >= 2: + print + +# Backwards compatibility for Django r9110 +if not [opt for opt in Command.option_list if opt.dest=='verbosity']: + Command.option_list += ( + make_option('--verbosity', '-v', action="store", dest="verbosity", + default='1', type='choice', choices=['0', '1', '2'], + help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"), + ) diff --git a/apps/compress/models.py b/apps/compress/models.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/compress/signals.py b/apps/compress/signals.py new file mode 100644 index 00000000..bd76a76e --- /dev/null +++ b/apps/compress/signals.py @@ -0,0 +1,4 @@ +from django.dispatch import Signal + +css_filtered = Signal() +js_filtered = Signal() diff --git a/apps/compress/templates/compress/.tmp_css.html.32854~ b/apps/compress/templates/compress/.tmp_css.html.32854~ new file mode 100644 index 00000000..3751a4a0 --- /dev/null +++ b/apps/compress/templates/compress/.tmp_css.html.32854~ @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/compress/templates/compress/.tmp_js.html.60841~ b/apps/compress/templates/compress/.tmp_js.html.60841~ new file mode 100644 index 00000000..8419c20a --- /dev/null +++ b/apps/compress/templates/compress/.tmp_js.html.60841~ @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/compress/templates/compress/css.html b/apps/compress/templates/compress/css.html new file mode 100644 index 00000000..3751a4a0 --- /dev/null +++ b/apps/compress/templates/compress/css.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/compress/templates/compress/css_ie.html b/apps/compress/templates/compress/css_ie.html new file mode 100644 index 00000000..2e244ee9 --- /dev/null +++ b/apps/compress/templates/compress/css_ie.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/compress/templates/compress/js.html b/apps/compress/templates/compress/js.html new file mode 100644 index 00000000..8419c20a --- /dev/null +++ b/apps/compress/templates/compress/js.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/compress/templates/compress/js_ie.html b/apps/compress/templates/compress/js_ie.html new file mode 100644 index 00000000..ef1a37c7 --- /dev/null +++ b/apps/compress/templates/compress/js_ie.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/compress/templatetags/__init__.py b/apps/compress/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/compress/templatetags/compressed.py b/apps/compress/templatetags/compressed.py new file mode 100644 index 00000000..f9c55f92 --- /dev/null +++ b/apps/compress/templatetags/compressed.py @@ -0,0 +1,124 @@ +import os + +from django import template + +from django.conf import settings as django_settings + +from compress.conf import settings +from compress.utils import media_root, media_url, needs_update, filter_css, filter_js, get_output_filename, get_version, get_version_from_file + +register = template.Library() + +def render_common(template_name, obj, filename, version): + if settings.COMPRESS: + filename = get_output_filename(filename, version) + + context = obj.get('extra_context', {}) + prefix = context.get('prefix', None) + if filename.startswith('http://'): + context['url'] = filename + else: + context['url'] = media_url(filename, prefix) + + return template.loader.render_to_string(template_name, context) + +def render_css(css, filename, version=None): + return render_common(css.get('template_name', 'compress/css.html'), css, filename, version) + +def render_js(js, filename, version=None): + return render_common(js.get('template_name', 'compress/js.html'), js, filename, version) + +class CompressedCSSNode(template.Node): + def __init__(self, name): + self.name = name + + def render(self, context): + css_name = template.Variable(self.name).resolve(context) + + try: + css = settings.COMPRESS_CSS[css_name] + except KeyError: + return '' # fail silently, do not return anything if an invalid group is specified + + if settings.COMPRESS: + + version = None + + if settings.COMPRESS_AUTO: + u, version = needs_update(css['output_filename'], + css['source_filenames']) + if u: + filter_css(css) + else: + filename_base, filename = os.path.split(css['output_filename']) + path_name = media_root(filename_base) + version = get_version_from_file(path_name, filename) + + return render_css(css, css['output_filename'], version) + 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) + + try: + js = settings.COMPRESS_JS[js_name] + except KeyError: + return '' # fail silently, do not return anything if an invalid group is specified + + if 'external_urls' in js: + r = '' + for url in js['external_urls']: + r += render_js(js, url) + return r + + if settings.COMPRESS: + + version = None + + if settings.COMPRESS_AUTO: + u, version = needs_update(js['output_filename'], + js['source_filenames']) + if u: + filter_js(js) + else: + filename_base, filename = os.path.split(js['output_filename']) + path_name = media_root(filename_base) + version = get_version_from_file(path_name, filename) + + return render_js(js, js['output_filename'], version) + 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: the name of a group in the COMPRESS_CSS setting' % token.split_contents()[0] + + 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: the name of a group in the COMPRESS_JS setting' % token.split_contents()[0] + + return CompressedJSNode(name) +compressed_js = register.tag(compressed_js) diff --git a/apps/compress/utils.py b/apps/compress/utils.py new file mode 100644 index 00000000..5e13222f --- /dev/null +++ b/apps/compress/utils.py @@ -0,0 +1,139 @@ +import os +import re +import tempfile + +from django.conf import settings as django_settings +from django.utils.http import urlquote +from django.dispatch import dispatcher + +from compress.conf import settings +from compress.signals import css_filtered, js_filtered + +def get_class(class_string): + """ + Convert a string version of a function name to the callable object. + """ + + if not hasattr(class_string, '__bases__'): + + try: + class_string = class_string.encode('ascii') + mod_name, class_name = get_mod_func(class_string) + if class_name != '': + class_string = getattr(__import__(mod_name, {}, {}, ['']), class_name) + except (ImportError, AttributeError): + raise Exception('Failed to import filter %s' % class_string) + + return class_string + +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(output_file, source_files, verbosity=0): + """ + Scan the source files for changes and returns True if the output_file needs to be updated. + """ + + version = get_version(source_files) + + on = get_output_filename(output_file, version) + compressed_file_full = media_root(on) + + if not os.path.exists(compressed_file_full): + return True, version + + update_needed = getattr(get_class(settings.COMPRESS_VERSIONING)(), 'needs_update')(output_file, source_files, version) + return update_needed + +def media_root(filename): + """ + Return the full path to ``filename``. ``filename`` is a relative path name in MEDIA_ROOT + """ + return os.path.join(django_settings.STATIC_ROOT, filename) + +def media_url(url, prefix=None): + if prefix: + return prefix + urlquote(url) + return django_settings.STATIC_URL + urlquote(url) + +def concat(filenames, separator=''): + """ + Concatenate the files from the list of the ``filenames``, ouput separated with ``separator``. + """ + r = '' + for filename in filenames: + fd = open(media_root(filename), 'rb') + r += fd.read() + r += separator + fd.close() + return r + +def max_mtime(files): + return int(max([os.stat(media_root(f)).st_mtime for f in files])) + +def save_file(filename, contents): + dirname = os.path.dirname(media_root(filename)) + if not os.path.exists(dirname): + os.makedirs(dirname) + fd = open(media_root(filename), 'wb+') + fd.write(contents) + fd.close() + +def get_output_filename(filename, version): + if settings.COMPRESS_VERSION and version is not None: + return filename.replace(settings.COMPRESS_VERSION_PLACEHOLDER, version) + else: + return filename.replace(settings.COMPRESS_VERSION_PLACEHOLDER, settings.COMPRESS_VERSION_DEFAULT) + +def get_version(source_files, verbosity=0): + version = getattr(get_class(settings.COMPRESS_VERSIONING)(), 'get_version')(source_files) + return version + +def get_version_from_file(path, filename): + regex = re.compile(r'^%s$' % (get_output_filename(settings.COMPRESS_VERSION_PLACEHOLDER.join([re.escape(part) for part in filename.split(settings.COMPRESS_VERSION_PLACEHOLDER)]), r'([A-Za-z0-9]+)'))) + for f in os.listdir(path): + result = regex.match(f) + if result and result.groups(): + return result.groups()[0] + +def remove_files(path, filename, verbosity=0): + regex = re.compile(r'^%s$' % (os.path.basename(get_output_filename(settings.COMPRESS_VERSION_PLACEHOLDER.join([re.escape(part) for part in filename.split(settings.COMPRESS_VERSION_PLACEHOLDER)]), r'[A-Za-z0-9]+')))) + if os.path.exists(path): + for f in os.listdir(path): + if regex.match(f): + if verbosity >= 1: + print "Removing outdated file %s" % f + + os.unlink(os.path.join(path, f)) + +def filter_common(obj, verbosity, filters, attr, separator, signal): + output = concat(obj['source_filenames'], separator) + + filename = get_output_filename(obj['output_filename'], get_version(obj['source_filenames'])) + + if settings.COMPRESS_VERSION: + remove_files(os.path.dirname(media_root(filename)), obj['output_filename'], verbosity) + + if verbosity >= 1: + print "Saving %s" % filename + + for f in filters: + output = getattr(get_class(f)(verbose=(verbosity >= 2)), attr)(output) + + save_file(filename, output) + signal.send(None) + +def filter_css(css, verbosity=0): + return filter_common(css, verbosity, filters=settings.COMPRESS_CSS_FILTERS, attr='filter_css', separator='', signal=css_filtered) + +def filter_js(js, verbosity=0): + return filter_common(js, verbosity, filters=settings.COMPRESS_JS_FILTERS, attr='filter_js', separator='', signal=js_filtered) diff --git a/apps/compress/versioning/__init__.py b/apps/compress/versioning/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/compress/versioning/base.py b/apps/compress/versioning/base.py new file mode 100644 index 00000000..8b815196 --- /dev/null +++ b/apps/compress/versioning/base.py @@ -0,0 +1,13 @@ +class VersioningBase(object): + + def get_version(self, source_files): + raise NotImplementedError + + def needs_update(self, output_file, source_files, version): + raise NotImplementedError + +class VersioningError(Exception): + """ + This exception is raised when version creation fails + """ + pass \ No newline at end of file diff --git a/apps/compress/versioning/hash/__init__.py b/apps/compress/versioning/hash/__init__.py new file mode 100644 index 00000000..74721fe0 --- /dev/null +++ b/apps/compress/versioning/hash/__init__.py @@ -0,0 +1,47 @@ +import cStringIO +from hashlib import md5, sha1 +import os + +from compress.conf import settings +from compress.utils import concat, get_output_filename +from compress.versioning.base import VersioningBase + +class HashVersioningBase(VersioningBase): + def __init__(self, hash_method): + self.hash_method = hash_method + + def needs_update(self, output_file, source_files, version): + output_file_name = get_output_filename(output_file, version) + ph = settings.COMPRESS_VERSION_PLACEHOLDER + of = output_file + try: + phi = of.index(ph) + old_version = output_file_name[phi:phi+len(ph)-len(of)] + return (version != old_version), version + except ValueError: + # no placeholder found, do not update, manual update if needed + return False, version + + def get_version(self, source_files): + buf = concat(source_files) + s = cStringIO.StringIO(buf) + version = self.get_hash(s) + s.close() + return version + + def get_hash(self, f, CHUNK=2**16): + m = self.hash_method() + while 1: + chunk = f.read(CHUNK) + if not chunk: + break + m.update(chunk) + return m.hexdigest() + +class MD5Versioning(HashVersioningBase): + def __init__(self): + super(MD5Versioning, self).__init__(md5) + +class SHA1Versioning(HashVersioningBase): + def __init__(self): + super(SHA1Versioning, self).__init__(sha1) \ No newline at end of file diff --git a/apps/compress/versioning/mtime/__init__.py b/apps/compress/versioning/mtime/__init__.py new file mode 100644 index 00000000..31b9d0b9 --- /dev/null +++ b/apps/compress/versioning/mtime/__init__.py @@ -0,0 +1,19 @@ +import os + +from compress.utils import get_output_filename, media_root +from compress.versioning.base import VersioningBase + +class MTimeVersioning(VersioningBase): + + def get_version(self, source_files): + + # Return the modification time for the newest source file + return str(max([int(os.stat(media_root(f)).st_mtime) for f in source_files])) + + def needs_update(self, output_file, source_files, version): + + output_file_name = get_output_filename(output_file, version) + compressed_file_full = media_root(output_file_name) + + return (int(os.stat(compressed_file_full).st_mtime) < int(version)), version + diff --git a/apps/wiki/templates/wiki/document_details.html b/apps/wiki/templates/wiki/document_details.html index 5f8d791a..ac952458 100644 --- a/apps/wiki/templates/wiki/document_details.html +++ b/apps/wiki/templates/wiki/document_details.html @@ -2,21 +2,16 @@ {% load toolbar_tags %} {% block extrahead %} - - - - + {% load compressed %} + {% compressed_css 'detail' %} +{% endblock %} + +{% block extrabody %} - + - - - - - - + {% compressed_js 'detail' %} {% endblock %} {% block maincontent %} diff --git a/apps/wiki/templates/wiki/document_list.html b/apps/wiki/templates/wiki/document_list.html index 1f2c9b93..183d6128 100644 --- a/apps/wiki/templates/wiki/document_list.html +++ b/apps/wiki/templates/wiki/document_list.html @@ -1,8 +1,12 @@ {% extends "base.html" %} +{% load compressed %} {% block extrahead %} - - +{% compressed_css 'listing' %} +{% endblock extrahead %} + +{% block extrabody %} +{% compressed_js 'listing' %} -{% endblock extrahead %} +{% endblock %} {% block maincontent %}

Platforma Redakcyjna

diff --git a/fabfile.py b/fabfile.py index a936eb29..eba8f1f8 100644 --- a/fabfile.py +++ b/fabfile.py @@ -70,6 +70,7 @@ def deploy(): copy_localsettings() symlink_current_release() migrate() + django_compress() restart_webserver() def deploy_version(version): @@ -152,6 +153,14 @@ def migrate(): run('../../../bin/python manage.py syncdb --noinput' % env, pty = True) if env.use_south: run('../../../bin/python manage.py migrate' % env, pty = True) + +def django_compress(): + "Update static files" + print '>>> migrate' + require('project_name', provided_by = [staging, production]) + with cd('%(path)s/releases/current/%(project_name)s' % env): + run('../../../bin/python manage.py synccompress --force' % env, pty = True) + def restart_webserver(): "Restart the web server" diff --git a/platforma/settings.py b/platforma/settings.py index 634d6cbe..b4585455 100644 --- a/platforma/settings.py +++ b/platforma/settings.py @@ -109,22 +109,52 @@ CAS_ADMIN_PREFIX = "/admin/" CAS_LOGOUT_COMPLETELY = True # CSS and JS files to compress -# COMPRESS_CSS = { -# 'all': { -# 'source_filenames': ('css/master.css', 'css/jquery.date_input.css', 'css/jquery.countdown.css',), -# 'output_filename': 'css/all.min.css', -# } -# } -# -# COMPRESS_JS = { -# 'all': { -# 'source_filenames': ('js/jquery.js', 'js/jquery.date_input.js', 'js/jquery.date_input-pl.js', -# 'js/jquery.countdown.js', 'js/jquery.countdown-pl.js',), -# 'output_filename': 'js/all.min.js', -# } -# } -# -# COMPRESS_CSS_FILTERS = None +COMPRESS_CSS = { + 'detail': { + 'source_filenames': ( + 'css/master.css', + 'css/html.css', + 'css/jquery.autocomplete.css', + ), + 'output_filename': 'compressed/detail_styles_?.css', + }, + 'listing': { + 'source_filenames': ( + 'css/filelist.css', + ), + 'output_filename': 'compressed/listing_styles_?.js', + } +} + +COMPRESS_JS = { + # everything except codemirror + 'detail': { + 'source_filenames': ( + 'js/jquery-1.4.2.min.js', + 'js/jquery.autocomplete.js', + 'js/jquery.blockui.js', + 'js/jquery.elastic.js', + 'js/button_scripts.js', + 'js/slugify.js', + 'js/xslt.js', + 'js/main.js', + ), + 'output_filename': 'compressed/detail_scripts_?.js', + }, + 'listing': { + 'source_filenames': ( + 'js/jquery-1.4.2.min.js', + 'js/slugify.js', + ), + 'output_filename': 'compressed/listing_scripts_?.js', + } +} + +COMPRESS_CSS_FILTERS = None +COMPRESS_JS_FILTERS = None +COMPRESS_AUTO = False +COMPRESS_VERSION = True +COMPRESS_VERSIONING = 'compress.versioning.hash.MD5Versioning' INSTALLED_APPS = ( @@ -136,6 +166,7 @@ INSTALLED_APPS = ( 'django.contrib.admindocs', 'django_nose', + 'compress', 'wiki', 'sorl.thumbnail', diff --git a/platforma/static/css/autumn.css b/platforma/static/css/autumn.css deleted file mode 100644 index dddd8996..00000000 --- a/platforma/static/css/autumn.css +++ /dev/null @@ -1,49 +0,0 @@ -/* - Document : autumn - Created on : 2009-10-01, 00:23:51 - Author : lreqc - Description: - Autumn colors for PR. -*/ - -body { - background-color: #C28800; -} - -#header { - background-color: #E2CF62; - border-bottom-color: #860000; -} - -.panel-main-toolbar { - background-color: #412F1D; -} - -.splitview-splitbar { - /* border-color: #412F1D; */ - border: none; - background-color: #860000; -} - -.image-gallery-header { - background-color: #E2CF62; -} - -a:link, a:visited, a:active { - color: #860000; - text-decoration: none; - font-weight: bold; -} - -a:hover { - text-decoration: underline; -} - -.toolbar-buttons-container { - background-color: #E2CF62; -} - -.toolbar-tabs-container { - background-color: #860000; -} - diff --git a/platforma/static/css/awesome-overlay-sprite.png b/platforma/static/css/awesome-overlay-sprite.png deleted file mode 100644 index ef8dd2d5..00000000 Binary files a/platforma/static/css/awesome-overlay-sprite.png and /dev/null differ diff --git a/platforma/static/css/buttons.css b/platforma/static/css/buttons.css deleted file mode 100644 index 034c3b7a..00000000 --- a/platforma/static/css/buttons.css +++ /dev/null @@ -1,93 +0,0 @@ -/* set an awesome color for the buttons */ -button.awesome, -input.awesome, -a.button.awesome { - background-color: #111; -} -/* Touch the rest at your onw risk. */ -button.awesome, -input.awesome, -a.button.awesome { - - font: inherit; - background-repeat: repeat-x; - color: #fff; - text-decoration: none; - position: relative; - cursor: pointer; - border: 0; - font-style: normal; - font-weight: bold; - line-height: 1; - - padding: 5px 10px 6px; - font-size: 13px; - - /* IE only stuff */ - border-bottom: 1px solid transparent\9; - - /* not all browser support these, but who cares? */ - text-shadow: 0 -1px 1px rgba(0,0,0,0.25), -2px 0 1px rgba(0,0,0,0.25); - border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.5); - -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.5); - - /* one image for all states - see http://www.alistapart.com/articles/sprites */ - background-image: url(awesome-overlay-sprite.png); - background-position: 0 0; - - /* cross browser inline block hack - see http://blog.mozilla.com/webdev/2009/02/20/cross-browser-inline-block/ */ - display: -moz-inline-stack; - display: inline-block; - vertical-align: middle; - *display: inline !important; - position: relative; - - /* help IE to calm down a bit */ - zoom: 1; - - /*disable text selection (Firefox only)*/ - -moz-user-select: none; -} -/* hide selection background color */ -.awesome::selection { - background: transparent; -} - -button.awesome:hover, -input.awesome:hover, -a.button.awesome:hover { - background-position: 0 -50px; - color: #fff; -} -button.awesome:active, -input.awesome:active, -a.button.awesome:active { - background-position: 0 100%; - -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,0.7); - /* unfortunately, Safari seems not to support inset yet */ - -webkit-box-shadow: none; - - /* IE only stuff */ - border-bottom: 0\9; - border-top: 1px solid #666\9; -} - -/* Remove extra padding in Firefox */ -button::-moz-focus-inner { - border: 0; - padding: 0; -} - -button.awesome.small, input.awesome.small, a.button.awesome.small { padding: 4px 7px 5px; font-size: 10px; } -button.awesome.small:active, input.awesome.small:active, a.button.awesome.small:active { padding: 5px 7px 4px; } -button.awesome.medium, input.awesome.medium, a.button.awesome.medium { /* default */ } -button.awesome.medium:active, input.awesome.medium:active, a.button.awesome.medium:active { padding: 6px 10px 5px; } -button.awesome.large, input.awesome.large, a.button.awesome.large { padding: 8px 14px 9px; font-size: 14px; } -button.awesome.large:active, input.awesome.large:active, a.button.awesome.large:active { padding: 9px 14px 8px; } - -.awesome.green { background-color: #3ACC00 !important; } diff --git a/platforma/static/css/jquery.modal.css b/platforma/static/css/jquery.modal.css deleted file mode 100644 index 4dc8a258..00000000 --- a/platforma/static/css/jquery.modal.css +++ /dev/null @@ -1,29 +0,0 @@ -/* jqModal base Styling courtesy of; - Brice Burgess */ - -/* The Window's CSS z-index value is respected (takes priority). If none is supplied, - the Window's z-index value will be set to 3000 by default (via jqModal.js). */ - -.jqmWindow { - display: none; - - position: absolute; - top: 40px; - left: 25%; - bottom: auto; - height: auto; - width: auto; - - max-width: 80%; - max-height: 80%; - - overflow: auto; - - background-color: #EEE; - color: #333; - border: 1px solid black; - padding: 1em; - z-index: 10000; -} - -.jqmOverlay { background-color: #000; } diff --git a/platforma/static/css/managment.css b/platforma/static/css/managment.css deleted file mode 100644 index d1e2fc20..00000000 --- a/platforma/static/css/managment.css +++ /dev/null @@ -1,70 +0,0 @@ -table.request-report -{ - border: 2px groove black; - font-size: 12pt; - - margin-top: 3em; - margin-bottom: 2em; - margin-left: auto; - margin-right: auto; -} - -.pull-request { - padding: 20px 20px 10px 20px; - border-bottom: 1px dotted #CCC; -} - -.pull-request h2 { - padding: 0; - margin: 0; - font-size: 22px; -} - -.pull-request .request-info { - color: #999; - list-style: none; - margin: 0; - padding: 0; -} - -.pull-request .request-info li { - float: left; - padding-right: 20px; -} - -.pull-request p.comment { - clear: left; - padding: 20px 0; -} - -.pull-request p { - margin: 0; - padding: 0; -} - -.awesome.button { - margin-right: 10px; -} - -.pull-request.status-N { - background-color: #FFF; -} - -.pull-request.status-R { - background-color: red; -} - -.pull-request.status-A { - background-color: #DDD; -} - -.pull-request .diff { - white-space: pre; - height: 360px; - width: 960px; - overflow: auto; - border: 1px solid #CCC; - margin-top: 10px; - background-color: #F9F9F9; - padding: 2px; -} diff --git a/platforma/static/css/pygments.css b/platforma/static/css/pygments.css deleted file mode 100644 index 5cea4ab7..00000000 --- a/platforma/static/css/pygments.css +++ /dev/null @@ -1,60 +0,0 @@ -.hll { background-color: #ffffcc } -.c { color: #888888 } /* Comment */ -.err { color: #a61717; background-color: #e3d2d2 } /* Error */ -.k { color: #008800; font-weight: bold } /* Keyword */ -.cm { color: #888888 } /* Comment.Multiline */ -.cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ -.c1 { color: #888888 } /* Comment.Single */ -.cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ -.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ -.ge { font-style: italic } /* Generic.Emph */ -.gr { color: #aa0000 } /* Generic.Error */ -.gh { color: #303030 } /* Generic.Heading */ -.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ -.go { color: #888888 } /* Generic.Output */ -.gp { color: #555555 } /* Generic.Prompt */ -.gs { font-weight: bold } /* Generic.Strong */ -.gu { color: #606060 } /* Generic.Subheading */ -.gt { color: #aa0000 } /* Generic.Traceback */ -.kc { color: #008800; font-weight: bold } /* Keyword.Constant */ -.kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ -.kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ -.kp { color: #008800 } /* Keyword.Pseudo */ -.kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ -.kt { color: #888888; font-weight: bold } /* Keyword.Type */ -.m { color: #0000DD; font-weight: bold } /* Literal.Number */ -.s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ -.na { color: #336699 } /* Name.Attribute */ -.nb { color: #003388 } /* Name.Builtin */ -.nc { color: #bb0066; font-weight: bold } /* Name.Class */ -.no { color: #003366; font-weight: bold } /* Name.Constant */ -.nd { color: #555555 } /* Name.Decorator */ -.ne { color: #bb0066; font-weight: bold } /* Name.Exception */ -.nf { color: #0066bb; font-weight: bold } /* Name.Function */ -.nl { color: #336699; font-style: italic } /* Name.Label */ -.nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ -.py { color: #336699; font-weight: bold } /* Name.Property */ -.nt { color: #bb0066; font-weight: bold } /* Name.Tag */ -.nv { color: #336699 } /* Name.Variable */ -.ow { color: #008800 } /* Operator.Word */ -.w { color: #bbbbbb } /* Text.Whitespace */ -.mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ -.mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ -.mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ -.mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ -.sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ -.sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ -.sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ -.s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ -.se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ -.sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ -.si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ -.sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ -.sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ -.s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ -.ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ -.bp { color: #003388 } /* Name.Builtin.Pseudo */ -.vc { color: #336699 } /* Name.Variable.Class */ -.vg { color: #dd7700 } /* Name.Variable.Global */ -.vi { color: #3333bb } /* Name.Variable.Instance */ -.il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ diff --git a/platforma/static/css/toolbar.css b/platforma/static/css/toolbar.css deleted file mode 100644 index b5df66f3..00000000 --- a/platforma/static/css/toolbar.css +++ /dev/null @@ -1,5 +0,0 @@ -.toolbar-buttons-container { - padding-top: 2px; - padding-bottom: 2px; -} - diff --git a/platforma/static/js/jquery-1.4.2.min.js b/platforma/static/js/jquery-1.4.2.min.js new file mode 100644 index 00000000..7c243080 --- /dev/null +++ b/platforma/static/js/jquery-1.4.2.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/platforma/static/js/lib/jquery.cookie.js b/platforma/static/js/lib/jquery.cookie.js deleted file mode 100644 index 6df1faca..00000000 --- a/platforma/static/js/lib/jquery.cookie.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Cookie plugin - * - * Copyright (c) 2006 Klaus Hartl (stilbuero.de) - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - */ - -/** - * Create a cookie with the given name and value and other optional parameters. - * - * @example $.cookie('the_cookie', 'the_value'); - * @desc Set the value of a cookie. - * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); - * @desc Create a cookie with all available options. - * @example $.cookie('the_cookie', 'the_value'); - * @desc Create a session cookie. - * @example $.cookie('the_cookie', null); - * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain - * used when the cookie was set. - * - * @param String name The name of the cookie. - * @param String value The value of the cookie. - * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. - * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. - * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. - * If set to null or omitted, the cookie will be a session cookie and will not be retained - * when the the browser exits. - * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). - * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). - * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will - * require a secure protocol (like HTTPS). - * @type undefined - * - * @name $.cookie - * @cat Plugins/Cookie - * @author Klaus Hartl/klaus.hartl@stilbuero.de - */ - -/** - * Get the value of a cookie with the given name. - * - * @example $.cookie('the_cookie'); - * @desc Get the value of a cookie. - * - * @param String name The name of the cookie. - * @return The value of the cookie. - * @type String - * - * @name $.cookie - * @cat Plugins/Cookie - * @author Klaus Hartl/klaus.hartl@stilbuero.de - */ -jQuery.cookie = function(name, value, options) { - if (typeof value != 'undefined') { // name and value given, set cookie - options = options || {}; - if (value === null) { - value = ''; - options.expires = -1; - } - var expires = ''; - if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { - var date; - if (typeof options.expires == 'number') { - date = new Date(); - date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); - } else { - date = options.expires; - } - expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE - } - // CAUTION: Needed to parenthesize options.path and options.domain - // in the following expressions, otherwise they evaluate to undefined - // in the packed version for some reason... - var path = options.path ? '; path=' + (options.path) : ''; - var domain = options.domain ? '; domain=' + (options.domain) : ''; - var secure = options.secure ? '; secure' : ''; - document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); - } else { // only name given, get cookie - var cookieValue = null; - if (document.cookie && document.cookie != '') { - var cookies = document.cookie.split(';'); - for (var i = 0; i < cookies.length; i++) { - var cookie = jQuery.trim(cookies[i]); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) == (name + '=')) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } -}; \ No newline at end of file diff --git a/platforma/static/js/lib/jquery.hpanel.js b/platforma/static/js/lib/jquery.hpanel.js deleted file mode 100644 index 1ad0d17f..00000000 --- a/platforma/static/js/lib/jquery.hpanel.js +++ /dev/null @@ -1,94 +0,0 @@ -(function($){ - - /* behaviour */ - $.hpanel = { - settings: {}, - current_data: {}, - resize_start: function(event, mydata) { - $(document).bind('mousemove', mydata, $.hpanel.resize_changed). - bind('mouseup', mydata, $.hpanel.resize_stop); - - $('.panel-overlay', mydata.root).css('display', 'block'); - return false; - }, - resize_changed: function(event) { - var old_width = parseInt(event.data.overlay.css('width')); - var delta = event.pageX + event.data.hotspot_x - old_width; - event.data.overlay.css({'width': old_width + delta}); - - if(event.data.overlay.next) { - var left = parseInt(event.data.overlay.next.css('left')); - event.data.overlay.next.css('left', left+delta); - } - - return false; - }, - resize_stop: function(event) { - $(document).unbind('mousemove', $.hpanel.resize_changed).unbind('mouseup', $.hpanel.resize_stop); - // $('.panel-content', event.data.root).css('display', 'block'); - var overlays = $('.panel-content-overlay', event.data.root); - $('.panel-content-overlay', event.data.root).each(function(i) { - if( $(this).data('panel').hasClass('last-panel') ) - $(this).data('panel').css({ - 'left': $(this).css('left'), 'right': $(this).css('right')}); - else - $(this).data('panel').css({ - 'left': $(this).css('left'), 'width': $(this).css('width')}); - }); - $('.panel-overlay', event.data.root).css('display', 'none'); - $(event.data.root).trigger('stopResize'); - } - }; - - $.fn.makeHorizPanel = function(options) - { - var root = $(this) - - /* create an overlay */ - var overlay_root = $("
"); - root.append(overlay_root); - - var prev = null; - - $('*.panel-wrap', root).each( function() - { - var panel = $(this); - var handle = $('.panel-slider', panel); - var overlay = $("
 
"); - overlay_root.append(overlay); - overlay.data('panel', panel); - overlay.data('next', null); - - if (prev) prev.next = overlay; - - if( panel.hasClass('last-panel') ) - { - overlay.css({'left': panel.css('left'), 'right': panel.css('right')}); - } - else { - overlay.css({'left': panel.css('left'), 'width': panel.css('width')}); - $.log('Has handle: ' + panel.attr('id')); - overlay.append(handle.clone()); - /* attach the trigger */ - handle.mousedown(function(event) { - var touch_data = { - root: root, overlay: overlay, - hotspot_x: event.pageX - handle.position().left - }; - - $(this).trigger('hpanel:panel-resize-start', touch_data); - return false; - }); - $('.panel-content', panel).css('right', - (handle.outerWidth() || 10) + 'px'); - $('.panel-content-overlay', panel).css('right', - (handle.outerWidth() || 10) + 'px'); - }; - - prev = overlay; - }); - - root.bind('hpanel:panel-resize-start', $.hpanel.resize_start); - }; -})(jQuery); - diff --git a/platforma/static/js/lib/jquery.js b/platforma/static/js/lib/jquery.js deleted file mode 100644 index 92635743..00000000 --- a/platforma/static/js/lib/jquery.js +++ /dev/null @@ -1,4376 +0,0 @@ -/*! - * jQuery JavaScript Library v1.3.2 - * http://jquery.com/ - * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 - */ -(function(){ - -var - // Will speed up references to window, and allows munging its name. - window = this, - // Will speed up references to undefined, and allows munging its name. - undefined, - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - // Map over the $ in case of overwrite - _$ = window.$, - - jQuery = window.jQuery = window.$ = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context ); - }, - - // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, - // Is it a simple selector - isSimple = /^.[^:#\[\.,]*$/; - -jQuery.fn = jQuery.prototype = { - init: function( selector, context ) { - // Make sure that a selection was provided - selector = selector || document; - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this[0] = selector; - this.length = 1; - this.context = selector; - return this; - } - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - var match = quickExpr.exec( selector ); - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) - selector = jQuery.clean( [ match[1] ], context ); - - // HANDLE: $("#id") - else { - var elem = document.getElementById( match[3] ); - - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem && elem.id != match[3] ) - return jQuery().find( selector ); - - // Otherwise, we inject the element directly into the jQuery object - var ret = jQuery( elem || [] ); - ret.context = document; - ret.selector = selector; - return ret; - } - - // HANDLE: $(expr, [context]) - // (which is just equivalent to: $(content).find(expr) - } else - return jQuery( context ).find( selector ); - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) - return jQuery( document ).ready( selector ); - - // Make sure that old selector state is passed along - if ( selector.selector && selector.context ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return this.setArray(jQuery.isArray( selector ) ? - selector : - jQuery.makeArray(selector)); - }, - - // Start with an empty selector - selector: "", - - // The current version of jQuery being used - jquery: "1.3.2", - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num === undefined ? - - // Return a 'clean' array - Array.prototype.slice.call( this ) : - - // Return just the object - this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new jQuery matched element set - var ret = jQuery( elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) - ret.selector = this.selector + (this.selector ? " " : "") + selector; - else if ( name ) - ret.selector = this.selector + "." + name + "(" + selector + ")"; - - // Return the newly-formed element set - return ret; - }, - - // Force the current matched set of elements to become - // the specified array of elements (destroying the stack in the process) - // You should use pushStack() in order to do this, but maintain the stack - setArray: function( elems ) { - // Resetting the length to 0, then using the native Array push - // is a super-fast way to populate an object with array-like properties - this.length = 0; - Array.prototype.push.apply( this, elems ); - - return this; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem && elem.jquery ? elem[0] : elem - , this ); - }, - - attr: function( name, value, type ) { - var options = name; - - // Look for the case where we're accessing a style value - if ( typeof name === "string" ) - if ( value === undefined ) - return this[0] && jQuery[ type || "attr" ]( this[0], name ); - - else { - options = {}; - options[ name ] = value; - } - - // Check to see if we're setting style values - return this.each(function(i){ - // Set all the styles - for ( name in options ) - jQuery.attr( - type ? - this.style : - this, - name, jQuery.prop( this, options[ name ], type, i, name ) - ); - }); - }, - - css: function( key, value ) { - // ignore negative width and height values - if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) - value = undefined; - return this.attr( key, value, "curCSS" ); - }, - - text: function( text ) { - if ( typeof text !== "object" && text != null ) - return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); - - var ret = ""; - - jQuery.each( text || this, function(){ - jQuery.each( this.childNodes, function(){ - if ( this.nodeType != 8 ) - ret += this.nodeType != 1 ? - this.nodeValue : - jQuery.fn.text( [ this ] ); - }); - }); - - return ret; - }, - - wrapAll: function( html ) { - if ( this[0] ) { - // The elements to wrap the target around - var wrap = jQuery( html, this[0].ownerDocument ).clone(); - - if ( this[0].parentNode ) - wrap.insertBefore( this[0] ); - - wrap.map(function(){ - var elem = this; - - while ( elem.firstChild ) - elem = elem.firstChild; - - return elem; - }).append(this); - } - - return this; - }, - - wrapInner: function( html ) { - return this.each(function(){ - jQuery( this ).contents().wrapAll( html ); - }); - }, - - wrap: function( html ) { - return this.each(function(){ - jQuery( this ).wrapAll( html ); - }); - }, - - append: function() { - return this.domManip(arguments, true, function(elem){ - if (this.nodeType == 1) - this.appendChild( elem ); - }); - }, - - prepend: function() { - return this.domManip(arguments, true, function(elem){ - if (this.nodeType == 1) - this.insertBefore( elem, this.firstChild ); - }); - }, - - before: function() { - return this.domManip(arguments, false, function(elem){ - this.parentNode.insertBefore( elem, this ); - }); - }, - - after: function() { - return this.domManip(arguments, false, function(elem){ - this.parentNode.insertBefore( elem, this.nextSibling ); - }); - }, - - end: function() { - return this.prevObject || jQuery( [] ); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: [].push, - sort: [].sort, - splice: [].splice, - - find: function( selector ) { - if ( this.length === 1 ) { - var ret = this.pushStack( [], "find", selector ); - ret.length = 0; - jQuery.find( selector, this[0], ret ); - return ret; - } else { - return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ - return jQuery.find( selector, elem ); - })), "find", selector ); - } - }, - - clone: function( events ) { - // Do the clone - var ret = this.map(function(){ - if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { - // IE copies events bound via attachEvent when - // using cloneNode. Calling detachEvent on the - // clone will also remove the events from the orignal - // In order to get around this, we use innerHTML. - // Unfortunately, this means some modifications to - // attributes in IE that are actually only stored - // as properties will not be copied (such as the - // the name attribute on an input). - var html = this.outerHTML; - if ( !html ) { - var div = this.ownerDocument.createElement("div"); - div.appendChild( this.cloneNode(true) ); - html = div.innerHTML; - } - - return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; - } else - return this.cloneNode(true); - }); - - // Copy the events from the original to the clone - if ( events === true ) { - var orig = this.find("*").andSelf(), i = 0; - - ret.find("*").andSelf().each(function(){ - if ( this.nodeName !== orig[i].nodeName ) - return; - - var events = jQuery.data( orig[i], "events" ); - - for ( var type in events ) { - for ( var handler in events[ type ] ) { - jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); - } - } - - i++; - }); - } - - // Return the cloned set - return ret; - }, - - filter: function( selector ) { - return this.pushStack( - jQuery.isFunction( selector ) && - jQuery.grep(this, function(elem, i){ - return selector.call( elem, i ); - }) || - - jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ - return elem.nodeType === 1; - }) ), "filter", selector ); - }, - - closest: function( selector ) { - var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, - closer = 0; - - return this.map(function(){ - var cur = this; - while ( cur && cur.ownerDocument ) { - if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { - jQuery.data(cur, "closest", closer); - return cur; - } - cur = cur.parentNode; - closer++; - } - }); - }, - - not: function( selector ) { - if ( typeof selector === "string" ) - // test special case where just one selector is passed in - if ( isSimple.test( selector ) ) - return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); - else - selector = jQuery.multiFilter( selector, this ); - - var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; - return this.filter(function() { - return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; - }); - }, - - add: function( selector ) { - return this.pushStack( jQuery.unique( jQuery.merge( - this.get(), - typeof selector === "string" ? - jQuery( selector ) : - jQuery.makeArray( selector ) - ))); - }, - - is: function( selector ) { - return !!selector && jQuery.multiFilter( selector, this ).length > 0; - }, - - hasClass: function( selector ) { - return !!selector && this.is( "." + selector ); - }, - - val: function( value ) { - if ( value === undefined ) { - var elem = this[0]; - - if ( elem ) { - if( jQuery.nodeName( elem, 'option' ) ) - return (elem.attributes.value || {}).specified ? elem.value : elem.text; - - // We need to handle select boxes special - if ( jQuery.nodeName( elem, "select" ) ) { - var index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type == "select-one"; - - // Nothing was selected - if ( index < 0 ) - return null; - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; - - if ( option.selected ) { - // Get the specifc value for the option - value = jQuery(option).val(); - - // We don't need an array for one selects - if ( one ) - return value; - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - } - - // Everything else, we just grab the value - return (elem.value || "").replace(/\r/g, ""); - - } - - return undefined; - } - - if ( typeof value === "number" ) - value += ''; - - return this.each(function(){ - if ( this.nodeType != 1 ) - return; - - if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) - this.checked = (jQuery.inArray(this.value, value) >= 0 || - jQuery.inArray(this.name, value) >= 0); - - else if ( jQuery.nodeName( this, "select" ) ) { - var values = jQuery.makeArray(value); - - jQuery( "option", this ).each(function(){ - this.selected = (jQuery.inArray( this.value, values ) >= 0 || - jQuery.inArray( this.text, values ) >= 0); - }); - - if ( !values.length ) - this.selectedIndex = -1; - - } else - this.value = value; - }); - }, - - html: function( value ) { - return value === undefined ? - (this[0] ? - this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : - null) : - this.empty().append( value ); - }, - - replaceWith: function( value ) { - return this.after( value ).remove(); - }, - - eq: function( i ) { - return this.slice( i, +i + 1 ); - }, - - slice: function() { - return this.pushStack( Array.prototype.slice.apply( this, arguments ), - "slice", Array.prototype.slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function(elem, i){ - return callback.call( elem, i, elem ); - })); - }, - - andSelf: function() { - return this.add( this.prevObject ); - }, - - domManip: function( args, table, callback ) { - if ( this[0] ) { - var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), - scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), - first = fragment.firstChild; - - if ( first ) - for ( var i = 0, l = this.length; i < l; i++ ) - callback.call( root(this[i], first), this.length > 1 || i > 0 ? - fragment.cloneNode(true) : fragment ); - - if ( scripts ) - jQuery.each( scripts, evalScript ); - } - - return this; - - function root( elem, cur ) { - return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? - (elem.getElementsByTagName("tbody")[0] || - elem.appendChild(elem.ownerDocument.createElement("tbody"))) : - elem; - } - } -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -function evalScript( i, elem ) { - if ( elem.src ) - jQuery.ajax({ - url: elem.src, - async: false, - dataType: "script" - }); - - else - jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); - - if ( elem.parentNode ) - elem.parentNode.removeChild( elem ); -} - -function now(){ - return +new Date; -} - -jQuery.extend = jQuery.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) - target = {}; - - // extend jQuery itself if only one argument is passed - if ( length == i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) - // Extend the base object - for ( var name in options ) { - var src = target[ name ], copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) - continue; - - // Recurse if we're merging object values - if ( deep && copy && typeof copy === "object" && !copy.nodeType ) - target[ name ] = jQuery.extend( deep, - // Never move original objects, clone them - src || ( copy.length != null ? [ ] : { } ) - , copy ); - - // Don't bring in undefined values - else if ( copy !== undefined ) - target[ name ] = copy; - - } - - // Return the modified object - return target; -}; - -// exclude the following css properties to add px -var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, - // cache defaultView - defaultView = document.defaultView || {}, - toString = Object.prototype.toString; - -jQuery.extend({ - noConflict: function( deep ) { - window.$ = _$; - - if ( deep ) - window.jQuery = _jQuery; - - return jQuery; - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return toString.call(obj) === "[object Function]"; - }, - - isArray: function( obj ) { - return toString.call(obj) === "[object Array]"; - }, - - // check if an element is in a (or is an) XML document - isXMLDoc: function( elem ) { - return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || - !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); - }, - - // Evalulates a script in a global context - globalEval: function( data ) { - if ( data && /\S/.test(data) ) { - // Inspired by code by Andrea Giammarchi - // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html - var head = document.getElementsByTagName("head")[0] || document.documentElement, - script = document.createElement("script"); - - script.type = "text/javascript"; - if ( jQuery.support.scriptEval ) - script.appendChild( document.createTextNode( data ) ); - else - script.text = data; - - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709). - head.insertBefore( script, head.firstChild ); - head.removeChild( script ); - } - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); - }, - - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, length = object.length; - - if ( args ) { - if ( length === undefined ) { - for ( name in object ) - if ( callback.apply( object[ name ], args ) === false ) - break; - } else - for ( ; i < length; ) - if ( callback.apply( object[ i++ ], args ) === false ) - break; - - // A special, fast, case for the most common use of each - } else { - if ( length === undefined ) { - for ( name in object ) - if ( callback.call( object[ name ], name, object[ name ] ) === false ) - break; - } else - for ( var value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} - } - - return object; - }, - - prop: function( elem, value, type, i, name ) { - // Handle executable functions - if ( jQuery.isFunction( value ) ) - value = value.call( elem, i ); - - // Handle passing in a number to a CSS property - return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? - value + "px" : - value; - }, - - className: { - // internal only, use addClass("class") - add: function( elem, classNames ) { - jQuery.each((classNames || "").split(/\s+/), function(i, className){ - if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) - elem.className += (elem.className ? " " : "") + className; - }); - }, - - // internal only, use removeClass("class") - remove: function( elem, classNames ) { - if (elem.nodeType == 1) - elem.className = classNames !== undefined ? - jQuery.grep(elem.className.split(/\s+/), function(className){ - return !jQuery.className.has( classNames, className ); - }).join(" ") : - ""; - }, - - // internal only, use hasClass("class") - has: function( elem, className ) { - return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; - } - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback ) { - var old = {}; - // Remember the old values, and insert the new ones - for ( var name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - callback.call( elem ); - - // Revert the old values - for ( var name in options ) - elem.style[ name ] = old[ name ]; - }, - - css: function( elem, name, force, extra ) { - if ( name == "width" || name == "height" ) { - var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; - - function getWH() { - val = name == "width" ? elem.offsetWidth : elem.offsetHeight; - - if ( extra === "border" ) - return; - - jQuery.each( which, function() { - if ( !extra ) - val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; - if ( extra === "margin" ) - val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; - else - val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; - }); - } - - if ( elem.offsetWidth !== 0 ) - getWH(); - else - jQuery.swap( elem, props, getWH ); - - return Math.max(0, Math.round(val)); - } - - return jQuery.curCSS( elem, name, force ); - }, - - curCSS: function( elem, name, force ) { - var ret, style = elem.style; - - // We need to handle opacity special in IE - if ( name == "opacity" && !jQuery.support.opacity ) { - ret = jQuery.attr( style, "opacity" ); - - return ret == "" ? - "1" : - ret; - } - - // Make sure we're using the right name for getting the float value - if ( name.match( /float/i ) ) - name = styleFloat; - - if ( !force && style && style[ name ] ) - ret = style[ name ]; - - else if ( defaultView.getComputedStyle ) { - - // Only "float" is needed here - if ( name.match( /float/i ) ) - name = "float"; - - name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); - - var computedStyle = defaultView.getComputedStyle( elem, null ); - - if ( computedStyle ) - ret = computedStyle.getPropertyValue( name ); - - // We should always get a number back from opacity - if ( name == "opacity" && ret == "" ) - ret = "1"; - - } else if ( elem.currentStyle ) { - var camelCase = name.replace(/\-(\w)/g, function(all, letter){ - return letter.toUpperCase(); - }); - - ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { - // Remember the original values - var left = style.left, rsLeft = elem.runtimeStyle.left; - - // Put in the new values to get a computed value out - elem.runtimeStyle.left = elem.currentStyle.left; - style.left = ret || 0; - ret = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - elem.runtimeStyle.left = rsLeft; - } - } - - return ret; - }, - - clean: function( elems, context, fragment ) { - context = context || document; - - // !context.createElement fails in IE with an error but returns typeof 'object' - if ( typeof context.createElement === "undefined" ) - context = context.ownerDocument || context[0] && context[0].ownerDocument || document; - - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { - var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); - if ( match ) - return [ context.createElement( match[1] ) ]; - } - - var ret = [], scripts = [], div = context.createElement("div"); - - jQuery.each(elems, function(i, elem){ - if ( typeof elem === "number" ) - elem += ''; - - if ( !elem ) - return; - - // Convert html string into DOM nodes - if ( typeof elem === "string" ) { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? - all : - front + ">"; - }); - - // Trim whitespace, otherwise indexOf won't work as expected - var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); - - var wrap = - // option or optgroup - !tags.indexOf("", "" ] || - - !tags.indexOf("", "" ] || - - tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && - [ 1, "", "
" ] || - - !tags.indexOf("", "" ] || - - // matched above - (!tags.indexOf("", "" ] || - - !tags.indexOf("", "" ] || - - // IE can't serialize and {% block extrahead %} {% endblock %}