diff --git a/src/www/Bongo.rules b/src/www/Bongo.rules index d32003f..6445619 100644 --- a/src/www/Bongo.rules +++ b/src/www/Bongo.rules @@ -208,34 +208,35 @@ dist_img_DATA := \ jsdir = $(htdocsdir)/js +# These *must* be in the order in which they get loaded in the browser dist_js_DATA := \ + src/www/js/Dragonfly.js \ + src/www/js/Format.js \ + src/www/js/Gettext.js \ + src/www/js/Cookie.js \ + src/www/js/Location.js \ + src/www/js/Login.js \ + src/www/js/Util.js \ + src/www/js/Widgets.js \ + src/www/js/ValidatingEntry.js \ + src/www/js/Preferences.js \ src/www/js/AddressBook.js \ - src/www/js/CalendarDrag.js \ - src/www/js/CalendarEditor.js \ - src/www/js/Calendar.js \ - src/www/js/CalendarLayout.js \ - src/www/js/CalendarView.js \ - src/www/js/CalendarWidgets.js \ - src/www/js/Composer.js \ - src/www/js/Cookie.js \ - src/www/js/Dragonfly.js \ - src/www/js/Format.js \ - src/www/js/Gettext.js \ - src/www/js/BongoCalendar.js \ src/www/js/JCal.js \ - src/www/js/Location.js \ - src/www/js/Login.js \ - src/www/js/MailConversationView.js \ - src/www/js/Mail.js \ - src/www/js/MailListView.js \ - src/www/js/MailWidgets.js \ + src/www/js/Calendar.js \ + src/www/js/BongoCalendar.js \ + src/www/js/CalendarEditor.js \ src/www/js/OccurrenceEditor.js \ - src/www/js/Preferences.js \ - src/www/js/Search.js \ + src/www/js/CalendarView.js \ + src/www/js/CalendarLayout.js \ + src/www/js/CalendarDrag.js \ + src/www/js/CalendarWidgets.js \ src/www/js/Summary.js \ - src/www/js/Util.js \ - src/www/js/ValidatingEntry.js \ - src/www/js/Widgets.js \ + src/www/js/Search.js \ + src/www/js/Mail.js \ + src/www/js/MailWidgets.js \ + src/www/js/Composer.js \ + src/www/js/MailConversationView.js \ + src/www/js/MailListView.js \ src/www/js/Browser.js \ src/www/js/BrowserCheck.js @@ -246,37 +247,38 @@ dist_prototype_DATA := \ mochidir = $(jsdir)/lib/MochiKit +# These *must* be in the order in which they get loaded in the browser/MochiKit dist_mochi_DATA := \ + src/www/js/lib/MochiKit/Base.js \ + src/www/js/lib/MochiKit/Iter.js \ + src/www/js/lib/MochiKit/Logging.js \ + src/www/js/lib/MochiKit/DateTime.js \ + src/www/js/lib/MochiKit/Format.js \ src/www/js/lib/MochiKit/Async.js \ - src/www/js/lib/MochiKit/Base.js \ - src/www/js/lib/MochiKit/Color.js \ - src/www/js/lib/MochiKit/DateTime.js \ src/www/js/lib/MochiKit/DOM.js \ - src/www/js/lib/MochiKit/Format.js \ - src/www/js/lib/MochiKit/Iter.js \ - src/www/js/lib/MochiKit/interpreter.js \ - src/www/js/lib/MochiKit/Logging.js \ src/www/js/lib/MochiKit/LoggingPane.js \ - src/www/js/lib/MochiKit/MochiKit.js \ - src/www/js/lib/MochiKit/MochiKit-packed.js \ - src/www/js/lib/MochiKit/MockDOM.js \ - src/www/js/lib/MochiKit/__package__.js \ + src/www/js/lib/MochiKit/Color.js \ src/www/js/lib/MochiKit/Signal.js \ - src/www/js/lib/MochiKit/SimpleTest.js \ - src/www/js/lib/MochiKit/Test.js \ - src/www/js/lib/MochiKit/TestRunner.js \ - src/www/js/lib/MochiKit/Visual.js + src/www/js/lib/MochiKit/Visual.js \ + src/www/js/lib/MochiKit/MochiKit.js \ + src/www/js/lib/MochiKit/interpreter.js scriptaculousdir = $(jsdir)/lib/script.aculo.us dist_scriptaculous_DATA := \ - src/www/js/lib/script.aculo.us/builder.js \ src/www/js/lib/script.aculo.us/controls.js \ - src/www/js/lib/script.aculo.us/dragdrop.js \ - src/www/js/lib/script.aculo.us/effects.js \ - src/www/js/lib/script.aculo.us/scriptaculous.js \ - src/www/js/lib/script.aculo.us/slider.js \ - src/www/js/lib/script.aculo.us/unittest.js + src/www/js/lib/script.aculo.us/effects.js + +finaljsdir = $(jsdir) + +dist_finaljs_DATA = \ + src/www/js/final.js + +compress_script := $(top_srcdir)/src/www/compress.py + +src/www/js/final.js: + $(compress_script) $(dist_js_DATA) -o src/www/js/compiled.js -m -l 1 && \ + $(compress_script) $(dist_prototype_DATA) $(dist_scriptaculous_DATA) $(dist_mochi_DATA) src/www/js/compiled.js -o src/www/js/final.js -q -l 0 install-data-local:: install-dist_htdocsDATA ln -sf dragonfly.html "$(DESTDIR)$(htdocsdir)/index.html" @@ -362,7 +364,8 @@ sbin_SCRIPTS += \ src/www/bongo-standalone dist_noinst_SCRIPTS += \ - src/www/l10n/props2json + src/www/l10n/props2json \ + src/www/compress.py templatesdir = $(pkgdatadir)/templates diff --git a/src/www/compress.py b/src/www/compress.py new file mode 100755 index 0000000..54a7d31 --- /dev/null +++ b/src/www/compress.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# +# Compresses Javascript/CSS. + +import re, sys, os +from optparse import OptionParser + +class JavascriptCompressor(object): + + def __init__(self, compressionLevel=2, removeLineBreaks=False): + self.compressionLevel = compressionLevel + self.removeLineBreaks = removeLineBreaks + + # a bunch of regexes used in compression + # first, exempt string literals from compression by transient substitution + findLiterals = re.compile(r'(\'.*?(?<=[^\\])\')|(\".*?(?<=[^\\])\")') + stringMarker = '@s@t@r@' + backSubst = re.compile(stringMarker) # put the string literals back in + + mlc = re.compile(r'/\*.*?\*\/', re.DOTALL) # remove multiline comments + slc = re.compile('\/\/.*') # remove single line comments + + collapseWs = re.compile('(?<=\S)[ \t]+') # collapse successive non-leading white space characters into one + squeeze = re.compile( + ''' + \s+(?=[\}\]\)]) | # remove whitespace including linebreaks preceding closing brackets or braces + (?<=[\{\;\,\(])\s+ | # ... or following such + [ \t]+(?=\W) | # remove spaces or tabs preceding non-word characters + (?<=\W)[ \t]+ # ... or following such. This also will remove leading whitespace + ''' + , re.DOTALL | re.VERBOSE) + + def compress(self, script): + ''' + perform compression and return compressed script + ''' + if not self.compressionLevel: + return script + + # first, substitute string literals by placeholders to prevent the regexes messing with them + literals = [] + + def insertMarker(mo): + literals.append(mo.group()) + return self.stringMarker + + script = self.findLiterals.sub(insertMarker, script) + + # now, to the literal-free carcass, apply some kludgy regexes for deflation... + script = self.slc.sub('', script) # remove single line comments + script = self.mlc.sub('\n', script) # replace multiline comments by newlines + script = self.collapseWs.sub(' ', script) # collapse multiple whitespace characters + + # remove empty lines and trailing whitespace + script = '\n'.join([l.rstrip() for l in script.splitlines() if l.strip()]) + + if self.compressionLevel > 1: + script = self.squeeze.sub('', script) # squeeze out any dispensible white space + + if self.removeLineBreaks: + script = script.replace('\n', '') # get rid of any new lines + + # now back-substitute the string literals + literals.reverse() + script = self.backSubst.sub(lambda mo: literals.pop(), script) + + return script + +if __name__ == "__main__": + parser = OptionParser(version="%prog 1.0"); + parser.add_option('-n', '--nolinebreaks', + dest='linebreaks', + action='store_true', + default=False, + help="remove all line breaks in the resulting file [default: %default]") + parser.add_option('-q', '--quiet', + dest='quiet', + action='store_true', + default=False, + help="be vewwy quiet!") + parser.add_option('-m', '--measure', + dest='measure', + default=False, + action='store_true', + help="print compression statistics") + + + parser.add_option('-o', '--output', + dest='output', + help="file to save resulting compressed javascript file(s) to") + parser.add_option('-l', '--level', + dest='compression', + default=1, + type='int', + help="level of compression (0 is none, 2 is maximum) [default: %default]") + + (options, args) = parser.parse_args() + + if options.compression > 2 or options.compression < 0: + print "Level of compression must be not be less than zero or more than two." + sys.exit(1) + + if options.output and os.path.exists(options.output): + if options.quiet: + print "Warning: output file already exists - overwriting." + os.remove(options.output) + + lengthBefore = 0 + lengthAfter = 0 + c = JavascriptCompressor(compressionLevel=options.compression, removeLineBreaks=options.linebreaks) + + for arg in args: + fn = arg + if not options.quiet: + print "Compressing " + fn + "..." + try: + f = open(fn) + destfn = fn + '.compressed' + writemode = 'w' + if options.output: + destfn = options.output + writemode = 'a' + r = open(destfn, writemode) + try: + data = f.read() + lengthBefore += float(len(data)) + cpr = c.compress(data) + lengthAfter += float(len(cpr)) + r.write(cpr) + r.close() + finally: + f.close() + r.close() + except: + if not options.quiet: + print "Warning: failed to compress file " + fn + "." + + if not options.quiet: + if options.measure: + # Print stats. + squeezeRate = int(100*(1-(lengthAfter/lengthBefore))) + kbsize = round((lengthBefore - lengthAfter) / 1024, 2) + print + print "Complete! (overall size reduction of %s" % squeezeRate + "% - " + str(kbsize) + "KB)" + elif lengthAfter != 0 and lengthBefore != 0: + print + print "Complete!" + else: + print "Error: no input files specified." diff --git a/src/www/dragonfly.html b/src/www/dragonfly.html index 15e349c..0102421 100644 --- a/src/www/dragonfly.html +++ b/src/www/dragonfly.html @@ -15,12 +15,8 @@ @import url(css/contacts.css); @import url(css/calendar.css); @import url(css/summary.css); - @import url(css/modal.css); - @import url(css/tabs.css); - - + @@ -37,50 +33,7 @@ document.write ('

Loading...

'); // --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +