1
0
mirror of http://git.whoc.org.uk/git/password-manager.git synced 2025-10-24 17:27:35 +02:00

First version of the newly restructured repository

This commit is contained in:
Giulio Cesare Solaroli
2011-10-03 00:56:18 +01:00
parent 597ecfbc02
commit ef68436ac0
729 changed files with 232898 additions and 0 deletions

5
scripts/build Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
readonly CURR_DIR=$(cd "$(dirname "$0")"; pwd -P)
${CURR_DIR}/builder/main.py $@

View File

@@ -0,0 +1,89 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys, os, json
import shutil
import main
import hashlib
class BackendBuilder:
def __init__ (self, projectTargetDir, frontends, versions, settings):
self.projectTargetDir = projectTargetDir
self.frontends = frontends
self.versions = versions
self.settings = settings
def name (self):
raise NotImplementedError()
def relativePath (self):
raise NotImplementedError()
def compileCode (self):
pass
def copyCompiledCodeToTargetDir (self):
src = self.sourceFolder()
dst = self.targetFolder()
main.createFolder(os.path.dirname(dst))
shutil.copytree(src, dst)
def sourceFolder (self):
return main.projectBaseDir() + '/backend/' + self.relativePath() + '/src'
def targetFolder (self):
return self.projectTargetDir + self.relativePath()
def createTargetFolder (self):
main.createFolder(self.targetFolder())
# def copyFrontendResources (self, frontend):
# print "copying resources for frontend: " + frontend
# print "SETTINGS: " + str(self.settings)
def writeToTargetFolder (self, filename, content):
file = open(self.targetFolder() + '/' + filename, 'w')
file.write(content.encode('utf-8'))
file.close()
def configureIndexContent (self, indexContent):
result = indexContent
result = result.replace( '@request.path@', self.settings['request.path'] )
result = result.replace( '@should.pay.toll@', self.settings['should.pay.toll'] )
return result
def logChecksums (self, content, message):
md5Digest = hashlib.md5(content.encode('utf-8')).hexdigest()
shaDigest = hashlib.sha1(content.encode('utf-8')).hexdigest()
sha256Digest = hashlib.sha256(content.encode('utf-8')).hexdigest()
print message + ": " + md5Digest + " (md5)"
print message + ": " + shaDigest + " (sha1)"
print message + ": " + sha256Digest + " (sha256)"
def run (self):
print self.name() + " - RUN"
self.compileCode()
self.copyCompiledCodeToTargetDir()
for frontend in self.frontends:
frontendPath = frontend.module + '/'
if 'debug' in self.versions:
frontend.copyResourcesToTargetFolder(self.targetFolder())
#self.writeToTargetFolder(frontendPath + 'index_debug.html', self.configureIndexContent(frontend.assembleDebugVersion()))
self.writeToTargetFolder(frontendPath + 'index_debug.html', self.configureIndexContent(frontend.assemble(assemblyMode='DEBUG', versionType='DEBUG')))
if 'install' in self.versions:
index = self.configureIndexContent(frontend.assemble())
self.writeToTargetFolder(frontendPath + 'index.html', index)
self.logChecksums(index, "[" + self.name() + " - " + frontend.module + "] index.html checksum")

223
scripts/builder/cssmin.py Normal file
View File

@@ -0,0 +1,223 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""`cssmin` - A Python port of the YUI CSS compressor."""
from StringIO import StringIO # The pure-Python StringIO supports unicode.
import re
__version__ = '0.1.4'
def remove_comments(css):
"""Remove all CSS comment blocks."""
iemac = False
preserve = False
comment_start = css.find("/*")
while comment_start >= 0:
# Preserve comments that look like `/*!...*/`.
# Slicing is used to make sure we don"t get an IndexError.
preserve = css[comment_start + 2:comment_start + 3] == "!"
comment_end = css.find("*/", comment_start + 2)
if comment_end < 0:
if not preserve:
css = css[:comment_start]
break
elif comment_end >= (comment_start + 2):
if css[comment_end - 1] == "\\":
# This is an IE Mac-specific comment; leave this one and the
# following one alone.
comment_start = comment_end + 2
iemac = True
elif iemac:
comment_start = comment_end + 2
iemac = False
elif not preserve:
css = css[:comment_start] + css[comment_end + 2:]
else:
comment_start = comment_end + 2
comment_start = css.find("/*", comment_start)
return css
def remove_unnecessary_whitespace(css):
"""Remove unnecessary whitespace characters."""
def pseudoclasscolon(css):
"""
Prevents 'p :link' from becoming 'p:link'.
Translates 'p :link' into 'p ___PSEUDOCLASSCOLON___link'; this is
translated back again later.
"""
regex = re.compile(r"(^|\})(([^\{\:])+\:)+([^\{]*\{)")
match = regex.search(css)
while match:
css = ''.join([
css[:match.start()],
match.group().replace(":", "___PSEUDOCLASSCOLON___"),
css[match.end():]])
match = regex.search(css)
return css
css = pseudoclasscolon(css)
# Remove spaces from before things.
css = re.sub(r"\s+([!{};:>+\(\)\],])", r"\1", css)
# If there is a `@charset`, then only allow one, and move to the beginning.
css = re.sub(r"^(.*)(@charset \"[^\"]*\";)", r"\2\1", css)
css = re.sub(r"^(\s*@charset [^;]+;\s*)+", r"\1", css)
# Put the space back in for a few cases, such as `@media screen` and
# `(-webkit-min-device-pixel-ratio:0)`.
css = re.sub(r"\band\(", "and (", css)
# Put the colons back.
css = css.replace('___PSEUDOCLASSCOLON___', ':')
# Remove spaces from after things.
css = re.sub(r"([!{}:;>+\(\[,])\s+", r"\1", css)
return css
def remove_unnecessary_semicolons(css):
"""Remove unnecessary semicolons."""
return re.sub(r";+\}", "}", css)
def remove_empty_rules(css):
"""Remove empty rules."""
return re.sub(r"[^\}\{]+\{\}", "", css)
def normalize_rgb_colors_to_hex(css):
"""Convert `rgb(51,102,153)` to `#336699`."""
regex = re.compile(r"rgb\s*\(\s*([0-9,\s]+)\s*\)")
match = regex.search(css)
while match:
colors = map(lambda s: s.strip(), match.group(1).split(","))
hexcolor = '#%.2x%.2x%.2x' % tuple(map(int, colors))
css = css.replace(match.group(), hexcolor)
match = regex.search(css)
return css
def condense_zero_units(css):
"""Replace `0(px, em, %, etc)` with `0`."""
return re.sub(r"([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", r"\1\2", css)
def condense_multidimensional_zeros(css):
"""Replace `:0 0 0 0;`, `:0 0 0;` etc. with `:0;`."""
css = css.replace(":0 0 0 0;", ":0;")
css = css.replace(":0 0 0;", ":0;")
css = css.replace(":0 0;", ":0;")
# Revert `background-position:0;` to the valid `background-position:0 0;`.
css = css.replace("background-position:0;", "background-position:0 0;")
return css
def condense_floating_points(css):
"""Replace `0.6` with `.6` where possible."""
return re.sub(r"(:|\s)0+\.(\d+)", r"\1.\2", css)
def condense_hex_colors(css):
"""Shorten colors from #AABBCC to #ABC where possible."""
regex = re.compile(r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])")
match = regex.search(css)
while match:
first = match.group(3) + match.group(5) + match.group(7)
second = match.group(4) + match.group(6) + match.group(8)
if first.lower() == second.lower():
css = css.replace(match.group(), match.group(1) + match.group(2) + '#' + first)
match = regex.search(css, match.end() - 3)
else:
match = regex.search(css, match.end())
return css
def condense_whitespace(css):
"""Condense multiple adjacent whitespace characters into one."""
return re.sub(r"\s+", " ", css)
def condense_semicolons(css):
"""Condense multiple adjacent semicolon characters into one."""
return re.sub(r";;+", ";", css)
def wrap_css_lines(css, line_length):
"""Wrap the lines of the given CSS to an approximate length."""
lines = []
line_start = 0
for i, char in enumerate(css):
# It's safe to break after `}` characters.
if char == '}' and (i - line_start >= line_length):
lines.append(css[line_start:i + 1])
line_start = i + 1
if line_start < len(css):
lines.append(css[line_start:])
return '\n'.join(lines)
def cssmin(css, wrap=None):
css = remove_comments(css)
css = condense_whitespace(css)
# A pseudo class for the Box Model Hack
# (see http://tantek.com/CSS/Examples/boxmodelhack.html)
css = css.replace('"\\"}\\""', "___PSEUDOCLASSBMH___")
css = remove_unnecessary_whitespace(css)
css = remove_unnecessary_semicolons(css)
css = condense_zero_units(css)
css = condense_multidimensional_zeros(css)
css = condense_floating_points(css)
css = normalize_rgb_colors_to_hex(css)
css = condense_hex_colors(css)
if wrap is not None:
css = wrap_css_lines(css, wrap)
css = css.replace("___PSEUDOCLASSBMH___", '"\\"}\\""')
css = condense_semicolons(css)
return css.strip()
def main():
import optparse
import sys
p = optparse.OptionParser(
prog="cssmin", version=__version__,
usage="%prog [--wrap N]",
description="""Reads raw CSS from stdin, and writes compressed CSS to stdout.""")
p.add_option(
'-w', '--wrap', type='int', default=None, metavar='N',
help="Wrap output to approximately N chars per line.")
options, args = p.parse_args()
sys.stdout.write(cssmin(sys.stdin.read(), wrap=options.wrap))
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,398 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys, os, re
import cssmin
import jsmin
import codecs
import shutil
import StringIO
import urllib
#from mercurial import ui, hg
#from mercurial.node import hex
from dulwich.repo import Repo
import main
class FrontendBuilder:
def __init__ (self, frontend, settings):
if '.' in frontend:
moduleComponents = frontend.split('.')
self.module = moduleComponents[0]
self.submodule = moduleComponents[1]
else:
self.module = frontend
self.submodule = frontend
self.settings = settings
self.projectDir = main.projectBaseDir()
self.processedFiles = {}
def mercurialRepositoryVersion (self):
repo = hg.repository(ui.ui(), self.projectDir)
context = repo['tip']
result = str(context)
return result
def gitRepositoryVersion (self):
repo = Repo(self.projectDir)
#if repo.is_dirty():
# print "WARNING: build run with dirty repository"
result = repo.refs['HEAD']
return result
def repositoryVersion (self):
cacheKey = 'repositoryVersion'
if not self.processedFiles.has_key(cacheKey):
#result = self.mercurialRepositoryVersion()
result = self.gitRepositoryVersion()
self.processedFiles[cacheKey] = result
else:
result = self.processedFiles[cacheKey]
return result
#def relativePath (self):
# return self.module
#
def log (self, message):
print "frontend [" + self.module + "]: " + message
def absolutePathForSourceFile (self, folder, basePath, file):
return folder + '/frontend/' + self.module + '/' + basePath + '/' + file
def absolutePathForTargetFile (self, folder, basePath, file):
return folder + '/' + self.module + '/' + basePath + '/' + file
def filterFiles (self, files):
result = []
for file in files:
if file.startswith('--'):
pass
else:
result.append(file)
return result
def copyResources (self, sourceFolder, destinationFolder, fileType):
for file in self.filterFiles(self.settings[fileType]):
src = self.absolutePathForSourceFile(sourceFolder, fileType, file)
dst = self.absolutePathForTargetFile(destinationFolder, fileType, file)
main.createFolder(os.path.dirname(dst))
shutil.copy2(src, dst)
def copyResourcesToTargetFolder (self, targetFolder):
self.copyResources(self.projectDir, targetFolder, 'css')
self.copyResources(self.projectDir, targetFolder, 'js')
def loadFilesContent (self, basePath, files):
result = ""
for file in self.filterFiles(files):
try:
fileHandler = codecs.open(self.absolutePathForSourceFile(self.projectDir, basePath, file), 'r', 'utf-8')
except:
print "FILE: " + file
result += fileHandler.read() + '\n'
fileHandler.close()
return result
def template (self):
processedFile = 'html_template'
if not self.processedFiles.has_key(processedFile):
self.processedFiles[processedFile] = self.loadFilesContent('html', ['index_template.html'])
return self.processedFiles[processedFile]
def cssminCompressor (self, css):
# package found here:
# - http://stackoverflow.com/questions/222581/python-script-for-minifying-css/2396777#2396777
# actual downloaded version: http://pypi.python.org/pypi/cssmin/0.1.4
return cssmin.cssmin(css)
def regexCssCompressor (self, css):
# http://stackoverflow.com/questions/222581/python-script-for-minifying-css/223689#223689
# remove comments - this will break a lot of hacks :-P
css = re.sub( r'\s*/\*\s*\*/', "$$HACK1$$", css ) # preserve IE<6 comment hack
css = re.sub( r'/\*[\s\S]*?\*/', "", css )
css = css.replace( "$$HACK1$$", '/**/' ) # preserve IE<6 comment hack
# url() doesn't need quotes
css = re.sub( r'url\((["\'])([^)]*)\1\)', r'url(\2)', css )
# spaces may be safely collapsed as generated content will collapse them anyway
css = re.sub( r'\s+', ' ', css )
# shorten collapsable colors: #aabbcc to #abc
css = re.sub( r'#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3(\s|;)', r'#\1\2\3\4', css )
# fragment values can loose zeros
css = re.sub( r':\s*0(\.\d+([cm]m|e[mx]|in|p[ctx]))\s*;', r':\1;', css )
for rule in re.findall( r'([^{]+){([^}]*)}', css ):
# we don't need spaces around operators
selectors = [re.sub( r'(?<=[\[\(>+=])\s+|\s+(?=[=~^$*|>+\]\)])', r'', selector.strip() ) for selector in rule[0].split( ',' )]
# order is important, but we still want to discard repetitions
properties = {}
porder = []
for prop in re.findall( '(.*?):(.*?)(;|$)', rule[1] ):
key = prop[0].strip().lower()
if key not in porder: porder.append( key )
properties[ key ] = prop[1].strip()
# output rule if it contains any declarations
if properties:
print "%s{%s}" % ( ','.join( selectors ), ''.join(['%s:%s;' % (key, properties[key]) for key in porder])[:-1] )
return css
def compressCSS (self, css):
self.log("compressing CSS")
#return self.regexCssCompressor(css)
return self.cssminCompressor(css)
#==========================================================================
def compressJS_jsmin (self, js):
self.log("compressing JS code")
original = StringIO.StringIO(js)
output = StringIO.StringIO()
jsMinifier = jsmin.JavascriptMinify()
jsMinifier.minify(original, output)
result = output.getvalue()
original.close()
output.close()
return result
def compressJS_closureCompiler (self, js):
# Googles Closure compiler
# java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js
result = js
return result
def compressJS (self, js):
return self.compressJS_jsmin(js)
#return self.compressJS_closureCompiler(js)
#==========================================================================
def packBookmarklet (self, bookmakeletCode):
replacers = [
('isLoginForm', 'ilf'),
('findLoginForm', 'flf'),
('findLoginForm', 'flf'),
('formParameters', 'fp' ),
('pageParameters', 'pp' ),
('serializeJSON', 'sj' ),
('reprString', 'rs' ),
('logFormParameters', 'lfp'),
('loadClipperzBookmarklet', 'lcb'),
('loginForm', 'lf' ),
('parameters', 'p' ),
('inputElementValues', 'iev'),
]
result = self.compressJS(bookmakeletCode)
result = re.sub('\n', ' ', result) # Fit all in a single line
# result = re.sub('\s+', ' ', result) # Collapse "redundant" spaces. WARNING: this could have some evil side effects on constant strings used inside to code!!
# result = re.sub('\s?([,\+=\(\)\{\};])\s?', '\\1', result)
for replacer in replacers:
result = re.sub(replacer[0], replacer[1], result)
# <!-- escaping required to handle the bookmarklet code within the javascript code -->
result = re.sub('\://', '%3a%2f%2f', result)
result = re.sub('/', '%2f', result)
# result = re.sub('"', '%22', result)
result = re.sub('"', '\\"', result)
result = re.sub('\"', '%22', result)
result = re.sub('\'', '%22', result)
result = re.sub('\\\\', '%5c', result)
result = result.strip()
result = 'javascript:' + result
# replacers = [
# ('aForm', '_1' ),
# ('inputFields', '_2' ),
# ('passwordFieldsFound', '_3' ),
# ('aDocument', '_6' ),
# ('aLevel', '_7' ),
# # ('result', '_8' ),
# ('documentForms', '_9' ),
# ('iFrames', '_c' ),
# ('anInputElement', '_d' ),
# ('options', '_f' ),
# ('option', '_12'),
# ('aLoginForm', '_13'),
# # ('action', '_17'),
# ('radioValues', '_18'),
# ('radioValueName', '_19'),
# ('inputElement', '_1a'),
# ('elementValues', '_1b'),
# ('radioValue', '_1c'),
# ('values', '_1d'),
# ('objtype', '_21'),
# ('useKey', '_27'),
# ('bookmarkletDiv', '_28'),
# ('someParameters', '_29'),
# ('anException', '_2a'),
# ('newDiv', '_2b'),
# ('base_url', '_2c'),
# ('help_url', '_2d'),
# ('logo_image_url', '_2e'),
# ('background_image_url','_2f'),
# ('close_image_url', '_30'),
# # ('bookmarklet_textarea','_31'),
# ('innerHTML', '_32'),
# ]
# for replacer in replacers:
# result = re.sub('([^\.])' + replacer[0], '\\1' + replacer[1], result)
# replacers = [
# ('headNode', '_1' ),
# ('clipperzScriptNode', '_2' ),
# ]
# for replacer in replacers:
# result = re.sub('([^\.])' + replacer[0], '\\1' + replacer[1], result)
# result = re.sub(';', ';\n', result)
return result
def bookmarklet (self):
cacheKey = 'bookmarklet'
if not self.processedFiles.has_key(cacheKey):
result = 'bookmarklet="' + self.packBookmarklet(self.loadFilesContent('js', ['Bookmarklet.js'])) + '";bookmarklet_ie="' + self.packBookmarklet(self.loadFilesContent('js', ['Bookmarklet_IE.js'])) + '";'
self.processedFiles[cacheKey] = result
else:
result = self.processedFiles[cacheKey]
return result
def replaceTemplatePlaceholders (self, assemblyMode, pageTitle, copyright, css, code, version, versionType):
result = self.template()
result = result.replace('@page.title@', pageTitle, 1)
result = result.replace('@copyright@', copyright, 1)
result = result.replace('@css@', css, 1)
#result = result.replace('@bookmarklet@', bookmarklet, 1)
result = result.replace('@application.version@', version, 1)
result = result.replace('@application.version.type@', versionType, 1)
result = result.replace('@js_' + assemblyMode + '@', code, 1)
result = re.sub('@js_[^@]+@', '', result)
return result
def assembleCopyrightHeader (self):
processedFile = 'copyright'
if not self.processedFiles.has_key(processedFile):
#self.log("assembling copyright header")
copyrightValues = self.settings['copyright.values']
license = self.loadFilesContent('../../properties', ['license.txt'])
result = self.loadFilesContent('properties', ['creditsAndCopyrights.txt'])
result = re.sub('@clipperz.license@', license, result)
for key in copyrightValues:
result = re.sub('@'+key+'@', copyrightValues[key], result)
self.processedFiles[processedFile] = result
return self.processedFiles[processedFile]
def cssTagsForFiles (self, basePath, files):
#<link rel="stylesheet" type="text/css" href="./css/reset-min.css" />
return '\n'.join(map(lambda file: '<link rel="stylesheet" type="text/css" href="./' + basePath + '/' + file + '" />', files))
def cssTagForContent (self, content):
return '<style type="text/css">' + content + '</style>'
def scriptTagsForFiles (self, basePath, files):
#<script type='text/javascript' src='./js/src/bookmarklet.js'></script>
return '\n'.join(map(lambda file: '<script type="text/javascript" src="./' + basePath + '/' + file + '"></script>', files))
def scriptTagForContent (self, content):
return '<script>' + content + '</script>'
def assembleVersion (self, assemblyMode, pageTitle, copyright, css, js, version, versionType):
cacheKey = version + "-" + versionType
if not self.processedFiles.has_key(cacheKey):
result = self.replaceTemplatePlaceholders(assemblyMode, pageTitle, copyright, css, js, version, versionType)
self.processedFiles[cacheKey] = result
else:
result = self.processedFiles[cacheKey]
#self.log("# cacheKey:\n" + result)
return result
def assemble (self, assemblyMode='INSTALL', versionType='LIVE'):
pageTitle = "Clipperz - " + self.module
if versionType != 'LIVE':
pageTitle += " [" + versionType + " - " + assemblyMode +"]"
if assemblyMode == 'INSTALL':
css = self.cssTagForContent(self.compressCSS(self.loadFilesContent('css', self.settings['css'])))
js = self.scriptTagForContent(self.bookmarklet() + '\n' + self.compressJS(self.loadFilesContent('js', self.settings['js'])))
else:
css = self.cssTagsForFiles('css', self.filterFiles(self.settings['css']))
js = self.scriptTagForContent(self.bookmarklet()) + '\n' + self.scriptTagsForFiles('js', self.filterFiles(self.settings['js']))
return self.assembleVersion(
assemblyMode = assemblyMode,
pageTitle = pageTitle,
copyright = self.assembleCopyrightHeader(),
css = css,
js = js,
version = self.repositoryVersion(),
versionType = versionType
)

246
scripts/builder/jsmin.py Normal file
View File

@@ -0,0 +1,246 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os, os.path, shutil
# 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 an unescaped '/' is followed by a '/' or '*'.
"""
c = self._get()
if c == '/' and self.theA != '\\':
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()
def compress(in_files, out_file, in_type='js', verbose=False, temp_file='.temp'):
temp = open(temp_file, 'w')
for f in in_files:
fh = open(f)
data = fh.read() + '\n'
fh.close()
temp.write(data)
print ' + %s' % f
temp.close()
out = open(out_file, 'w')
jsm = JavascriptMinify()
jsm.minify(open(temp_file,'r'), out)
out.close()
org_size = os.path.getsize(temp_file)
new_size = os.path.getsize(out_file)
print '=> %s' % out_file
print 'Original: %.2f kB' % (org_size / 1024.0)
print 'Compressed: %.2f kB' % (new_size / 1024.0)
print 'Reduction: %.1f%%' % (float(org_size - new_size) / org_size * 100)
print ''
os.remove(temp_file)

166
scripts/builder/main.py Executable file
View File

@@ -0,0 +1,166 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys, os, json
import shutil
import pprint
import frontendBuilder
import codecs
import itertools
from collections import deque
from phpBuilder import PhpBuilder
from pythonBuilder import PythonBuilder
pp = pprint.PrettyPrinter(indent=4, depth=4)
#--------------------------------------------------------------------
def scriptDir ():
return os.path.dirname(sys.argv[0])
def projectBaseDir ():
return os.path.abspath(scriptDir() + '/../..')
def projectTargetDir():
return projectBaseDir() + '/target/'
#--------------------------------------------------------------------
def createFolder (path):
if not os.path.exists(path):
os.makedirs(path)
#--------------------------------------------------------------------
def loadSettings (component, module):
print "MODULE: " + module
if '.' in module:
moduleComponents = module.split('.')
module = moduleComponents[0]
submodule = moduleComponents[1]
else:
submodule = module
settings = codecs.open(projectBaseDir() + '/' + component + '/' + module + '/properties/' + submodule + '.properties.json', 'r', 'utf-8')
result = json.load(settings)
settings.close
return result
#====================================================================
#
# def assembleFrontend (frontend, versions):
# result = {}
# settings = loadSettings('frontend', frontend)
# builder = frontendBuilder.FrontendBuilder(frontend, settings, projectBaseDir())
#
# for version in versions:
# if version == 'install':
# result[version] = builder.assembleInstallVersion()
# elif version == 'debug':
# result[version] = builder.assembleDebugVersion()
# else:
# raise Exception('unrecognized version: ' + version)
#
# return result
#
#====================================================================
def assembleBackend (backend, frontends, versions):
settings = loadSettings('backend', backend)
if backend == 'php':
backendBuilder = PhpBuilder(projectTargetDir(), frontends, versions, settings)
elif backend == 'python':
backendBuilder = PythonBuilder(projectTargetDir(), frontends, versions, settings)
#elif backend == 'java':
# buildJavaBackend (frontends, versions, settings)
else:
raise Exception('unrecognized backend: ' + backend)
backendBuilder.run()
#====================================================================
def build (settings):
frontends = []
for frontend in settings['frontends']:
frontends.append(frontendBuilder.FrontendBuilder(frontend, loadSettings('frontend', frontend)))
for backend in settings['backends']:
assembleBackend(backend, frontends, settings['versions'])
#--------------------------------------------------------------------
def clean ():
print "cleaning up …"
if os.path.exists(projectTargetDir()):
shutil.rmtree(projectTargetDir())
#--------------------------------------------------------------------
def usage (message):
if message != None:
print "ERROR: " + message
print
print "build.py clean"
print "build.py clean install"
print "build.py install --ALL"
print "build.py install debug --ALL"
print "build.py clean install debug --ALL"
print "build.ph install, debug --backends php java --frontends beta gamma"
print "build.ph install, debug --backends php java --frontends beta gamma gamma.mobile"
exit(1)
#--------------------------------------------------------------------
def main ():
settings = {}
parameters = list(itertools.islice(sys.argv, 1, None))
shouldClean = len(filter(lambda x: x == 'clean', parameters)) > 0
if (shouldClean):
clean ()
parameters = filter(lambda x: x != 'clean', parameters)
versions = list(itertools.takewhile(lambda x: not x.startswith('--'), parameters))
settings['versions'] = versions; #['debug', 'install']
parameters = deque(itertools.dropwhile(lambda x: not x.startswith('--'), parameters))
if len(parameters) > 0:
parameter = parameters.popleft()
if parameter == "--ALL":
settings['frontends'] = ['beta', 'gamma', 'mobile']
settings['backends'] = ['php', 'python', 'java']
else:
while parameter != None:
values = list(itertools.takewhile(lambda x: not x.startswith('--'), parameters))
if parameter == "--backends":
settings['backends'] = values
elif parameter == "--frontends":
settings['frontends'] = values
parameters = deque(itertools.dropwhile(lambda x: not x.startswith('--'), parameters))
if parameters:
parameter = parameters.popleft()
else:
parameter = None
if (not settings.has_key('versions')):
usage("missing 'versions'")
if (not settings.has_key('frontends')):
usage("missing 'frontends'")
if (not settings.has_key('backends')):
usage("missing 'backends'")
build (settings)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,14 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from backendBuilder import BackendBuilder
class PhpBuilder(BackendBuilder):
def name(self):
return "PHP builder"
def relativePath(self):
return 'php'

View File

@@ -0,0 +1,14 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from backendBuilder import BackendBuilder
class PythonBuilder(BackendBuilder):
def name(self):
return "Python builder"
def relativePath(self):
return 'python'