2011-10-05 10:24:32 +02:00
#!/usr/bin/env python
2011-10-03 01:56:18 +02:00
# -*- coding: UTF-8 -*-
import sys , os , re
import cssmin
import jsmin
import codecs
import shutil
import StringIO
import urllib
import main
2013-01-08 16:12:19 +01:00
#===============================================================================
2012-02-12 00:34:25 +01:00
class FrontendBuilder ( object ) :
2011-10-03 01:56:18 +02:00
2012-02-12 00:34:25 +01:00
def __init__ ( self , frontend , settings , repositoryVersion ) :
2011-10-03 01:56:18 +02:00
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 ( )
2012-02-12 00:34:25 +01:00
# self.repository = repository.repositoryWithPath(self.projectDir)
self . repositoryVersion = repositoryVersion
2011-10-03 01:56:18 +02:00
self . processedFiles = { }
2013-01-08 16:12:19 +01:00
#---------------------------------------------------------------------------
def name ( self ) :
raise NotImplementedError ( )
2013-08-30 17:56:53 +02:00
def projectResourceTypes ( self ) :
raise NotImplementedError ( )
2013-01-08 16:12:19 +01:00
2014-07-28 18:07:48 +02:00
# def copyStaticResources (self, targetFolder):
# raise NotImplementedError()
def copyResourcesToFolder ( self , targetFolder ) :
2013-01-08 16:12:19 +01:00
raise NotImplementedError ( )
2014-07-28 18:07:48 +02:00
def preprocessCSS ( self , targetFile ) :
pass
2013-01-08 16:12:19 +01:00
#---------------------------------------------------------------------------
2011-10-03 01:56:18 +02:00
def log ( self , message ) :
2013-01-08 16:12:19 +01:00
module = self . module
if ( self . module != self . submodule ) :
module = module + " . " + self . submodule
print " frontend [ " + module + " ]: " + message
2011-10-03 01:56:18 +02:00
2012-02-12 00:34:25 +01:00
def absolutePathForSources ( self ) :
return os . path . join ( self . projectDir , ' frontend ' , self . module )
def absolutePathForSourceFile ( self , basePath , file ) :
return os . path . join ( self . absolutePathForSources ( ) , basePath , file )
2011-10-03 01:56:18 +02:00
def absolutePathForTargetFile ( self , folder , basePath , file ) :
2012-02-12 00:34:25 +01:00
return os . path . join ( folder , self . module , basePath , file )
2011-10-03 01:56:18 +02:00
def filterFiles ( self , files ) :
result = [ ]
for file in files :
if file . startswith ( ' -- ' ) :
pass
else :
result . append ( file )
return result
2014-07-28 18:07:48 +02:00
2011-10-03 01:56:18 +02:00
def copyResources ( self , sourceFolder , destinationFolder , fileType ) :
2013-01-08 16:12:19 +01:00
if fileType in self . settings :
for file in self . filterFiles ( self . settings [ fileType ] ) :
src = self . absolutePathForSourceFile ( fileType , file )
dst = self . absolutePathForTargetFile ( destinationFolder , fileType , file )
main . createFolder ( os . path . dirname ( dst ) )
shutil . copy2 ( src , dst )
else :
srcFolder = os . path . join ( self . absolutePathForSources ( ) , fileType )
dstFolder = os . path . join ( destinationFolder , self . module , fileType )
if not ( os . path . exists ( dstFolder ) ) :
shutil . copytree ( srcFolder , dstFolder )
# try:
# shutil.copytree(srcFolder, dstFolder)
# except:
# pass
2011-10-03 01:56:18 +02:00
2014-07-28 18:07:48 +02:00
# def copyResourcesToFolder (self, targetFolder):
# for resoureceType in self.projectResourceTypes():
# self.copyResources(self.projectDir, targetFolder, resoureceType)
# self.copyStaticResources(targetFolder)
def copyDebugResourcesToFolder ( self , targetFolder ) :
2013-08-30 17:56:53 +02:00
for resoureceType in self . projectResourceTypes ( ) :
self . copyResources ( self . projectDir , targetFolder , resoureceType )
2011-10-03 01:56:18 +02:00
2014-07-28 18:07:48 +02:00
def loadIndividualFilesContent ( self , basePath , files ) :
result = { }
2011-10-03 01:56:18 +02:00
for file in self . filterFiles ( files ) :
try :
2012-02-12 00:34:25 +01:00
fileHandler = codecs . open ( self . absolutePathForSourceFile ( basePath , file ) , ' r ' , ' utf-8 ' )
2011-10-03 01:56:18 +02:00
except :
print " FILE: " + file
2014-07-28 18:07:48 +02:00
result [ file ] = fileHandler . read ( )
2011-10-03 01:56:18 +02:00
fileHandler . close ( )
2014-07-28 18:07:48 +02:00
2011-10-03 01:56:18 +02:00
return result
2014-07-28 18:07:48 +02:00
def loadFilesContent ( self , basePath , files ) :
result = " "
2014-10-09 13:55:45 +02:00
fileContent = self . loadIndividualFilesContent ( basePath , files )
for file in self . filterFiles ( files ) :
result + = fileContent [ file ] + ' \n '
2014-07-28 18:07:48 +02:00
return result
# def packFilesContent (self, filesContent):
# result = ""
#
# for name, content in filesContent:
# result += content + '\n'
#
# return result
2011-10-03 01:56:18 +02:00
def template ( self ) :
processedFile = ' html_template '
if not self . processedFiles . has_key ( processedFile ) :
2012-03-17 15:26:08 +01:00
# self.processedFiles[processedFile] = self.loadFilesContent('html', ['index_template.html'])
self . processedFiles [ processedFile ] = self . loadFilesContent ( ' html ' , [ self . settings [ ' html.template ' ] ] )
2011-10-03 01:56:18 +02:00
return self . processedFiles [ processedFile ]
2014-07-28 18:07:48 +02:00
#==========================================================================
2011-10-03 01:56:18 +02:00
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
2014-07-28 18:07:48 +02:00
2011-10-03 01:56:18 +02:00
def compressCSS ( self , css ) :
self . log ( " compressing CSS " )
#return self.regexCssCompressor(css)
return self . cssminCompressor ( css )
2014-07-28 18:07:48 +02:00
#--------------------------------------------------------------------------
2011-10-03 01:56:18 +02:00
#==========================================================================
2012-02-12 00:34:25 +01:00
def compressJS_jsmin ( self , js , description ) :
self . log ( " compressing " + description + " code " )
2011-10-03 01:56:18 +02:00
original = StringIO . StringIO ( js )
output = StringIO . StringIO ( )
jsMinifier = jsmin . JavascriptMinify ( )
jsMinifier . minify ( original , output )
result = output . getvalue ( )
original . close ( )
output . close ( )
return result
2012-02-12 00:34:25 +01:00
def compressJS_closureCompiler ( self , js , description ) :
2011-10-03 01:56:18 +02:00
# Googles Closure compiler
# java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js
result = js
return result
2012-02-12 00:34:25 +01:00
def compressJS ( self , js , description ) :
return self . compressJS_jsmin ( js , description )
#return self.compressJS_closureCompiler(js, description)
2011-10-03 01:56:18 +02:00
#==========================================================================
2012-02-12 00:34:25 +01:00
def packBookmarklet ( self , bookmakeletCode , version ) :
2011-10-03 01:56:18 +02:00
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 ' ) ,
]
2012-02-12 00:34:25 +01:00
result = self . compressJS ( bookmakeletCode , version + " bookmarklet " )
2011-10-03 01:56:18 +02:00
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
return result
def bookmarklet ( self ) :
cacheKey = ' bookmarklet '
if not self . processedFiles . has_key ( cacheKey ) :
2012-02-12 00:34:25 +01:00
result = ' bookmarklet= " ' + self . packBookmarklet ( self . loadFilesContent ( ' js ' , [ ' Bookmarklet.js ' ] ) , " regular " ) + ' " ;bookmarklet_ie= " ' + self . packBookmarklet ( self . loadFilesContent ( ' js ' , [ ' Bookmarklet_IE.js ' ] ) , " IE " ) + ' " ; '
2011-10-03 01:56:18 +02:00
self . processedFiles [ cacheKey ] = result
else :
result = self . processedFiles [ cacheKey ]
return result
2014-07-28 18:07:48 +02:00
#==========================================================================
2012-02-12 00:34:25 +01:00
def replaceTemplatePlaceholders ( self , pageTitle , copyright , css , code , jsLoadMode , version , versionType ) :
2011-10-03 01:56:18 +02:00
result = self . template ( )
2013-01-09 11:37:39 +01:00
result = result . replace ( ' @page.title@ ' , pageTitle )
result = result . replace ( ' @copyright@ ' , copyright )
result = result . replace ( ' @css@ ' , css )
#result = result.replace('@bookmarklet@', bookmarklet)
result = result . replace ( ' @application.version@ ' , version )
result = result . replace ( ' @application.version.type@ ' , versionType )
result = result . replace ( ' @js_ ' + jsLoadMode + ' @ ' , code )
2011-10-03 01:56:18 +02:00
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" />
2012-02-12 00:34:25 +01:00
return ' \n ' . join ( map ( lambda file : ' <link rel= " stylesheet " type= " text/css " href= " ' + basePath + ' / ' + file + ' " /> ' , files ) )
2011-10-03 01:56:18 +02:00
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>
2012-03-17 15:26:08 +01:00
return ' \n ' . join ( map ( lambda file : ' <script type= " text/javascript " src= " ' + basePath + ' / ' + file + ' " charset= " utf-8 " ></script> ' , files ) )
2011-10-03 01:56:18 +02:00
def scriptTagForContent ( self , content ) :
return ' <script> ' + content + ' </script> '
2012-02-12 00:34:25 +01:00
def assembleVersion ( self , pageTitle , copyright , css , js , jsLoadMode , version , versionType ) :
2011-10-03 01:56:18 +02:00
cacheKey = version + " - " + versionType
if not self . processedFiles . has_key ( cacheKey ) :
2012-02-12 00:34:25 +01:00
result = self . replaceTemplatePlaceholders ( pageTitle , copyright , css , js , jsLoadMode , version , versionType )
2011-10-03 01:56:18 +02:00
self . processedFiles [ cacheKey ] = result
else :
result = self . processedFiles [ cacheKey ]
#self.log("# cacheKey:\n" + result)
return result
def assemble ( self , assemblyMode = ' INSTALL ' , versionType = ' LIVE ' ) :
2012-02-12 00:34:25 +01:00
if versionType == ' LIVE ' :
pageTitle = " Clipperz - " + self . module
else :
pageTitle = " Clipperz - " + self . module + " [ " + versionType + " - " + assemblyMode + " ] "
2014-07-28 18:07:48 +02:00
for cssFile in self . settings [ ' css ' ] :
2014-08-22 08:38:53 +02:00
# self.preprocessCSS(self.absolutePathForSourceFile('css', cssFile))
pass
2014-07-28 18:07:48 +02:00
2011-10-03 01:56:18 +02:00
if assemblyMode == ' INSTALL ' :
2012-02-12 00:34:25 +01:00
copyright = self . assembleCopyrightHeader ( )
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 ' ] ) , " application " )
)
jsLoadMode = ' EMBEDDED '
elif assemblyMode == ' DEBUG ' :
copyright = self . assembleCopyrightHeader ( )
css = self . cssTagsForFiles ( ' ./css ' , self . filterFiles ( self . settings [ ' css ' ] ) )
2013-08-30 17:56:53 +02:00
js = self . scriptTagForContent (
self . bookmarklet ( ) ) + \
' \n ' + \
self . scriptTagsForFiles ( ' ./js ' , self . filterFiles ( self . settings [ ' js ' ] )
)
2012-02-12 00:34:25 +01:00
jsLoadMode = ' LINKED '
elif assemblyMode == ' DEVELOPMENT ' :
copyright = " "
css = self . cssTagsForFiles ( ' file:// ' + str ( os . path . join ( self . absolutePathForSources ( ) , ' css ' ) ) , self . filterFiles ( self . settings [ ' css ' ] ) )
2013-08-30 17:56:53 +02:00
js = self . scriptTagForContent (
self . bookmarklet ( ) ) + \
' \n ' + \
self . scriptTagsForFiles ( ' file:// ' + str ( os . path . join ( self . absolutePathForSources ( ) , ' js ' ) ) , self . filterFiles ( self . settings [ ' js ' ] )
)
2012-02-12 00:34:25 +01:00
jsLoadMode = ' LINKED '
2013-01-08 16:12:19 +01:00
versionType = ' development '
2012-02-12 00:34:25 +01:00
2011-10-03 01:56:18 +02:00
else :
2012-02-12 00:34:25 +01:00
raise NotImplementedError ( )
2011-10-03 01:56:18 +02:00
return self . assembleVersion (
pageTitle = pageTitle ,
2012-02-12 00:34:25 +01:00
copyright = copyright ,
2011-10-03 01:56:18 +02:00
css = css ,
js = js ,
2012-02-12 00:34:25 +01:00
jsLoadMode = jsLoadMode ,
version = self . repositoryVersion ,
2011-10-03 01:56:18 +02:00
versionType = versionType
)