#!/usr/bin/env python import sys, re, os, optparse # reconfigure path to import own modules from modules subfolder sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "modules")) import config, tokenizer, loader, api, tree, treegenerator, settings, resources, filetool, stringoptimizer, optparseext, variableoptimizer, obfuscator, compiler, migrator, textutil def getparser(): parser = optparse.OptionParser("usage: %prog [options]", option_class=optparseext.ExtendAction) ################################################################################# # GENERAL ################################################################################# # From/To File parser.add_option("--from-file", dest="fromFile", metavar="FILENAME", help="Read options from FILENAME.") parser.add_option("--export-to-file", dest="exportToFile", metavar="FILENAME", help="Store options to FILENAME.") # Directories (Lists, Match using index) parser.add_option("--script-input", action="extend", dest="scriptInput", metavar="DIRECTORY", type="string", default=[], help="Define a script input directory.") parser.add_option("--script-encoding", action="extend", dest="scriptEncoding", metavar="ENCODING", type="string", default=[], help="Define the encoding for a script input directory.") parser.add_option("--source-script-path", action="extend", dest="sourceScriptPath", metavar="PATH", type="string", default=[], help="Define a script path for the source version.") parser.add_option("--resource-input", action="extend", dest="resourceInput", metavar="DIRECTORY", type="string", default=[], help="Define a resource input directory.") parser.add_option("--resource-output", action="extend", dest="resourceOutput", metavar="DIRECTORY", type="string", default=[], help="Define a resource output directory.") # Available Actions parser.add_option("--generate-compiled-script", action="store_true", dest="generateCompiledScript", default=False, help="Compile source files.") parser.add_option("--generate-source-script", action="store_true", dest="generateSourceScript", default=False, help="Generate source version.") parser.add_option("--generate-api-documentation", action="store_true", dest="generateApiDocumentation", default=False, help="Generate API documentation.") parser.add_option("--copy-resources", action="store_true", dest="copyResources", default=False, help="Copy resource files.") parser.add_option("--fix-source", action="store_true", dest="fixSource", default=False, help="Fix source files") parser.add_option("--pretty-print", action="store_true", dest="prettyPrint", default=False, help="Pretty print source code.") parser.add_option("--migrate-source", action="store_true", dest="migrateSource", default=False, help="Migrate existing code to new version.") # Debug Actions parser.add_option("--store-tokens", action="store_true", dest="storeTokens", default=False, help="Store tokenized content of source files. (Debugging)") parser.add_option("--store-tree", action="store_true", dest="storeTree", default=False, help="Store tree content of source files. (Debugging)") parser.add_option("--print-files", action="store_true", dest="printFiles", default=False, help="Output known files. (Debugging)") parser.add_option("--print-modules", action="store_true", dest="printModules", default=False, help="Output known modules. (Debugging)") parser.add_option("--print-files-without-modules", action="store_true", dest="printFilesWithoutModules", default=False, help="Output files which have no module connection. (Debugging)") parser.add_option("--print-includes", action="store_true", dest="printIncludes", default=False, help="Output sorted file list. (Debugging)") parser.add_option("--print-dependencies", action="store_true", dest="printDeps", default=False, help="Output dependencies of files. (Debugging)") # Output files parser.add_option("--source-script-file", dest="sourceScriptFile", metavar="FILENAME", help="Name of output file from source build process.") parser.add_option("--compiled-script-file", dest="compiledScriptFile", metavar="FILENAME", help="Name of output file from compiler.") parser.add_option("--api-documentation-json-file", dest="apiDocumentationJsonFile", metavar="FILENAME", help="Name of JSON API file.") parser.add_option("--api-documentation-xml-file", dest="apiDocumentationXmlFile", metavar="FILENAME", help="Name of XML API file.") parser.add_option("--settings-script-file", dest="settingsScriptFile", metavar="FILENAME", help="Name of settings script file.") # Encoding parser.add_option("--script-output-encoding", dest="scriptOutputEncoding", default="utf-8", metavar="ENCODING", help="Defines the encoding used for script output files.") parser.add_option("--xml-output-encoding", dest="xmlOutputEncoding", default="utf-8", metavar="ENCODING", help="Defines the encoding used for XML output files.") ################################################################################# # OPTIONS ################################################################################# # General options parser.add_option("-q", "--quiet", action="store_false", dest="verbose", default=False, help="Quiet output mode.") parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="Verbose output mode.") parser.add_option("-d", "--debug", action="store_true", dest="enableDebug", help="Enable debug mode.") parser.add_option("--package-id", dest="packageId", default="", metavar="ID", help="Defines a package ID (required for string optimization etc.)") parser.add_option("--disable-internal-check", action="store_true", dest="disableInternalCheck", default=False, help="Disable check of modifications to internal files.") # Options for source and compiled version parser.add_option("--define-runtime-setting", action="append", dest="defineRuntimeSetting", metavar="NAMESPACE.KEY:VALUE", default=[], help="Define a setting.") parser.add_option("--add-new-lines", action="store_true", dest="addNewLines", default=False, help="Keep newlines in compiled files.") # Options for source version parser.add_option("--source-loader-type", action="store", dest="sourceLoaderType", metavar="TYPE", choices=("auto", "docwrite", "domappend"), default="auto", help="Generated source loader type: auto, docwrite, domappend [default: %default].") # Options for compiled version parser.add_option("--add-file-ids", action="store_true", dest="addFileIds", default=False, help="Add file IDs to compiled output.") parser.add_option("--optimize-strings", action="store_true", dest="optimizeStrings", default=False, help="Optimize strings. Increase mshtml performance.") parser.add_option("--optimize-variables", action="store_true", dest="optimizeVariables", default=False, help="Optimize variables. Reducing size.") parser.add_option("--obfuscate-identifiers", action="store_true", dest="obfuscateIdentifiers", default=False, help="Obfuscate public names like function names. (ALPHA!)") # Options for resource copying parser.add_option("--override-resource-output", action="append", dest="overrideResourceOutput", metavar="CLASSNAME.ID:DIRECTORY", default=[], help="Define a resource input directory.") # Options for token/tree storage parser.add_option("--token-output-directory", dest="tokenOutputDirectory", metavar="DIRECTORY", help="Define output directory for tokenizer result of the incoming JavaScript files. (Debugging)") parser.add_option("--tree-output-directory", dest="treeOutputDirectory", metavar="DIRECTORY", help="Define output directory for generated tree of the incoming JavaScript files. (Debugging)") # Cache Directory parser.add_option("--cache-directory", dest="cacheDirectory", metavar="DIRECTORY", help="If this is defined the loader trys to use cache to optimize the performance.") # Options for migration support parser.add_option("--migration-target", dest="migrationTarget", metavar="VERSION", help="Define the target for migration of source code.") parser.add_option("--migration-input", action="extend", dest="migrationInput", metavar="DIRECTORY", type="string", default=[], help="Define additional directories for to directories to migrate e.g. HTML files, ...") ################################################################################# # INCLUDE/EXCLUDE ################################################################################# # Include/Exclude parser.add_option("-i", "--include", action="extend", dest="includeWithDeps", metavar="ID", type="string", default=[], help="Include ID") parser.add_option("-e", "--exclude", action="extend", dest="excludeWithDeps", metavar="ID", type="string", default=[], help="Exclude ID") parser.add_option("--include-without-dependencies", action="extend", dest="includeWithoutDeps", metavar="ID", type="string", default=[], help="Include ID") parser.add_option("--exclude-without-dependencies", action="extend", dest="excludeWithoutDeps", metavar="ID", type="string", default=[], help="Exclude ID") # Include/Exclude options parser.add_option("--disable-auto-dependencies", action="store_false", dest="enableAutoDependencies", default=True, help="Disable detection of dependencies.") return parser def argparser(cmdlineargs): # Parse arguments (options, args) = getparser().parse_args(cmdlineargs) # Export to file if options.exportToFile != None: print print " EXPORTING:" print "----------------------------------------------------------------------------" print " * Translating options..." optionString = "# Exported configuration from build.py\n\n" ignoreValue = True lastWasKey = False for arg in cmdlineargs: if arg == "--export-to-file": ignoreValue = True elif arg.startswith("--"): if lastWasKey: optionString += "\n" optionString += arg[2:] ignoreValue = False lastWasKey = True elif arg.startswith("-"): print " * Couldn't export short argument: %s" % arg optionString += "\n# Ignored short argument %s\n" % arg ignoreValue = True elif not ignoreValue: optionString += " = %s\n" % arg ignoreValue = True lastWasKey = False print " * Export to file: %s" % options.exportToFile filetool.save(options.exportToFile, optionString) sys.exit(0) # Read from file elif options.fromFile != None: print print " INITIALIZATION:" print "----------------------------------------------------------------------------" print " * Reading configuration..." # Convert file content into arguments fileargs = {} fileargpos = 0 fileargid = "default" currentfileargs = [] fileargs[fileargid] = currentfileargs alternativeFormatBegin = re.compile("\s*\[\s*") alternativeFormatEnd = re.compile("\s*\]\s*=\s*") emptyLine = re.compile("^\s*$") for line in file(options.fromFile).read().split("\n"): line = line.strip() if emptyLine.match(line) or line.startswith("#") or line.startswith("//"): continue # Translating... line = alternativeFormatBegin.sub(" = ", line) line = alternativeFormatEnd.sub(":", line) # Splitting line line = line.split("=") # Extract key element key = line.pop(0).strip() # Separate packages if key == "package": fileargpos += 1 fileargid = line[0].strip() print " - Found package: %s" % fileargid currentfileargs = [] fileargs[fileargid] = currentfileargs continue currentfileargs.append("--%s" % key) if len(line) > 0: value = line[0].strip() currentfileargs.append(value) # Parse defaultargs = fileargs["default"] if len(fileargs) > 1: (fileDb, moduleDb) = load(getparser().parse_args(defaultargs)[0]) if options.obfuscateIdentifiers: sharednames = {} for filearg in fileargs: if filearg == "default": continue combinedargs = [] combinedargs.extend(defaultargs) combinedargs.extend(fileargs[filearg]) options = getparser().parse_args(defaultargs)[0] findnames(fileDb, moduleDb, options, sharednames) names = obfuscator.sort(sharednames) for filearg in fileargs: if filearg == "default": continue print print print print print " PACKAGE: %s" % filearg print "----------------------------------------------------------------------------" combinedargs = [] combinedargs.extend(defaultargs) combinedargs.extend(fileargs[filearg]) options = getparser().parse_args(combinedargs)[0] execute(fileDb, moduleDb, options, filearg, names) else: options = getparser().parse_args(defaultargs)[0] (fileDb, moduleDb) = load(options) if options.obfuscateIdentifiers: execute(fileDb, moduleDb, options, "", obfuscator.sort(findnames(fileDb, moduleDb, options))) else: execute(fileDb, moduleDb, options, "", names) else: print print " INITIALIZATION:" print "----------------------------------------------------------------------------" print " * Processing arguments..." (fileDb, moduleDb) = load(options) if options.obfuscateIdentifiers: execute(fileDb, moduleDb, options, options.packageId, obfuscator.sort(findnames(fileDb, moduleDb, options))) else: execute(fileDb, moduleDb, options, options.packageId) def main(): if len(sys.argv[1:]) == 0: basename = os.path.basename(sys.argv[0]) print "usage: %s [options]" % basename print "Try '%s -h' or '%s --help' to show the help message." % (basename, basename) sys.exit(1) argparser(sys.argv[1:]) def load(options): ###################################################################### # SOURCE LOADER ###################################################################### print print " SOURCE LOADER:" print "----------------------------------------------------------------------------" if options.scriptInput == None or len(options.scriptInput) == 0: if len(options.migrationInput) == 0: basename = os.path.basename(sys.argv[0]) print "You must define at least one script input directory!" print "usage: %s [options]" % basename print "Try '%s -h' or '%s --help' to show the help message." % (basename, basename) sys.exit(1) (fileDb, moduleDb) = loader.indexScriptInput(options) ###################################################################### # DEBUG OUTPUT JOBS ###################################################################### if options.printFiles: print print " OUTPUT OF KNOWN FILES:" print "----------------------------------------------------------------------------" print " * These are all known files:" for fileEntry in fileDb: print " - %s (%s)" % (fileEntry, fileDb[fileEntry]["path"]) if options.printModules: print print " OUTPUT OF KNOWN MODULES:" print "----------------------------------------------------------------------------" print " * These are all known modules:" for moduleEntry in moduleDb: print " * %s" % moduleEntry for fileEntry in moduleDb[moduleEntry]: print " - %s" % fileEntry if options.printFilesWithoutModules: print print " OUTPUT OF FILES WHICH HAVE NO MODULE CONNECTION:" print "----------------------------------------------------------------------------" print " * These are all files without a module connection:" for fileEntry in fileDb: fileFound = False for moduleEntry in moduleDb: for moduleFile in moduleDb[moduleEntry]: if moduleFile == fileEntry: fileFound = True break if not fileFound: print " - %s" % fileEntry return fileDb, moduleDb def findnames(fileDb, moduleDb, options, names={}): print print " SEARCHING FOR IDENTIFIERS:" print "----------------------------------------------------------------------------" if options.verbose: print " * Searching..." else: print " * Searching: ", sortedIncludeList = loader.getSortedList(options, fileDb, moduleDb) for fileId in sortedIncludeList: if options.verbose: print " - %s" % fileId else: sys.stdout.write(".") sys.stdout.flush() obfuscator.search(loader.getTree(fileDb, fileId, options), names) if not options.verbose: print return names def execute(fileDb, moduleDb, options, pkgid="", names=[]): additionalOutput = [] ###################################################################### # SORT OF INCLUDE LIST ###################################################################### print print " SORT OF INCLUDE LIST:" print "----------------------------------------------------------------------------" if options.verbose: print " * Include (with dependencies): %s" % options.includeWithDeps print " * Include (without dependencies): %s" % options.includeWithoutDeps print " * Exclude (with dependencies): %s" % options.excludeWithDeps print " * Exclude (without dependencies): %s" % options.excludeWithoutDeps print " * Sorting classes..." sortedIncludeList = loader.getSortedList(options, fileDb, moduleDb) if len(sortedIncludeList) == len(fileDb): print " * Including all classes" print " * Arranged %s classes" % len(sortedIncludeList) if options.printIncludes: print print " PRINT OF INCLUDE ORDER:" print "----------------------------------------------------------------------------" print " * The files will be included in this order:" for fileId in sortedIncludeList: print " - %s" % fileId if options.printDeps: print print " OUTPUT OF DEPENDENCIES:" print "----------------------------------------------------------------------------" print " * These are all included files with their dependencies:" for fileId in sortedIncludeList: print " - %s" % fileId if len(fileDb[fileId]["loadtimeDeps"]) > 0: print " - Loadtime: " for depEntry in fileDb[fileId]["loadtimeDeps"]: print " - %s" % depEntry if len(fileDb[fileId]["afterDeps"]) > 0: print " - After: " for depEntry in fileDb[fileId]["afterDeps"]: print " - %s" % depEntry if len(fileDb[fileId]["runtimeDeps"]) > 0: print " - Runtime: " for depEntry in fileDb[fileId]["runtimeDeps"]: print " - %s" % depEntry if len(fileDb[fileId]["beforeDeps"]) > 0: print " - Before: " for depEntry in fileDb[fileId]["beforeDeps"]: print " - %s" % depEntry if len(fileDb[fileId]["optionalDeps"]) > 0: print " - Optional: " for depEntry in fileDb[fileId]["optionalDeps"]: print " - %s" % depEntry ###################################################################### # SOURCE MIGRATION ###################################################################### if options.migrateSource: print print " SOURCE MIGRATION:" print "----------------------------------------------------------------------------" print " * Migrate Source Code..." migrator.handle(sortedIncludeList, fileDb, options) # Return after migration: Ignore other jobs return ###################################################################### # GENERATION OF PRETTY PRINTED CODE ###################################################################### if options.fixSource: print print " FIX SOURCE CODE:" print "----------------------------------------------------------------------------" if options.verbose: print " * Fixing code..." else: print " * Fixing code: ", for fileId in sortedIncludeList: if options.verbose: print " - Reading %s" % fileId fileEntry = fileDb[fileId] filePath = fileEntry["path"] fileEncoding = fileEntry["encoding"] fileContent = filetool.read(filePath, fileEncoding) fixedContent = textutil.removeTrailingSpaces(textutil.tab2Space(textutil.any2Unix(fileContent), 2)) if fixedContent != fileContent: if options.verbose: print " - Storing modifications..." else: sys.stdout.write("!") sys.stdout.flush() filetool.save(filePath, fixedContent, fileEncoding) elif not options.verbose: sys.stdout.write(".") sys.stdout.flush() if not options.verbose: print # Return after fixing: Ignore other jobs return ###################################################################### # GENERATION OF PRETTY PRINTED CODE ###################################################################### if options.prettyPrint: print print " GENERATION OF PRETTY PRINTED CODE:" print "----------------------------------------------------------------------------" if options.verbose: print " * Pretty printing..." else: print " * Pretty printing: ", for fileId in sortedIncludeList: if options.verbose: print " - Compiling %s" % fileId else: sys.stdout.write(".") sys.stdout.flush() prettyFileContent = compiler.compile(loader.getTree(fileDb, fileId, options), True) if not prettyFileContent.endswith("\n"): prettyFileContent += "\n" filetool.save(fileDb[fileId]["path"], prettyFileContent) if not options.verbose: print # Return after pretty print: Ignore other jobs return ###################################################################### # STRING OPTIMIZATION ###################################################################### if options.optimizeStrings: print print " STRING OPTIMIZATION:" print "----------------------------------------------------------------------------" if options.verbose: print " * Searching strings..." else: print " * Searching strings: ", stringMap = {} for fileId in sortedIncludeList: if options.verbose: print " - %s" % fileId else: sys.stdout.write(".") sys.stdout.flush() localMap = loader.getStrings(fileDb, fileId, options) for value in localMap: if value in stringMap: stringMap[value] += localMap[value] else: stringMap[value] = localMap[value] if not options.verbose: print counter = 0 for value in stringMap: counter += stringMap[value] stringList = stringoptimizer.sort(stringMap) print " * Found %s strings (used %s times)" % (len(stringMap), counter) if options.verbose: print " * Replacing strings..." else: print " * Replacing strings: ", for fileId in sortedIncludeList: if options.verbose: print " - %s" % fileId else: sys.stdout.write(".") sys.stdout.flush() stringoptimizer.replace(loader.getTree(fileDb, fileId, options), stringList, "$" + pkgid, options.verbose) if not options.verbose: print print " * Generating replacement..." additionalOutput.append(stringoptimizer.replacement(stringList, "$" + pkgid)) ###################################################################### # LOCAL VARIABLE OPTIMIZATION ###################################################################### if options.optimizeVariables: print print " LOCAL VARIABLE OPTIMIZATION:" print "----------------------------------------------------------------------------" if options.verbose: print " * Optimizing variables..." else: print " * Optimizing variables: ", for fileId in sortedIncludeList: if options.verbose: print " - %s" % fileId else: sys.stdout.write(".") sys.stdout.flush() variableoptimizer.search(loader.getTree(fileDb, fileId, options), [], 0, "$") if not options.verbose: print ###################################################################### # NAME OBFUSCATION ###################################################################### if options.obfuscateIdentifiers: print print " OBFUSCATE IDENTIFIERS:" print "----------------------------------------------------------------------------" if options.verbose: print " * Obfuscating identifiers..." else: print " * Obfuscating identifiers: ", counter = 0 for fileId in sortedIncludeList: if options.verbose: print " - %s" % fileId else: sys.stdout.write(".") sys.stdout.flush() counter += obfuscator.update(loader.getTree(fileDb, fileId, options), names, "$$") if not options.verbose: print print " * Updated %s names" % counter ###################################################################### # TOKEN STORAGE ###################################################################### if options.storeTokens: print print " TOKEN STORAGE:" print "----------------------------------------------------------------------------" if options.tokenOutputDirectory == None: print " * You must define the token output directory!" sys.exit(1) if options.verbose: print " * Storing tokens..." else: print " * Storing tokens: ", for fileId in sortedIncludeList: tokenString = tokenizer.convertTokensToString(loader.getTokens(fileDb, fileId, options)) if options.verbose: print " * writing tokens for %s (%s KB)..." % (fileIdm, len(tokenString) / 1000.0) else: sys.stdout.write(".") sys.stdout.flush() filetool.save(os.path.join(filetool.normalize(options.tokenOutputDirectory), fileId + config.TOKENEXT), tokenString) if not options.verbose: print ###################################################################### # TREE STORAGE ###################################################################### if options.storeTree: print print " TREE STORAGE:" print "----------------------------------------------------------------------------" if options.treeOutputDirectory == None: print " * You must define the tree output directory!" sys.exit(1) if options.verbose: print " * Storing tree..." else: print " * Storing tree: ", for fileId in sortedIncludeList: treeString = "\n" + tree.nodeToXmlString(loader.getTree(fileDb, fileId, options)) if options.verbose: print " * writing tree for %s (%s KB)..." % (fileId, len(treeString) / 1000.0) else: sys.stdout.write(".") sys.stdout.flush() filetool.save(os.path.join(filetool.normalize(options.treeOutputDirectory), fileId + config.XMLEXT), treeString) if not options.verbose: print ###################################################################### # GENERATION OF API ###################################################################### if options.generateApiDocumentation: print print " GENERATION OF API:" print "----------------------------------------------------------------------------" if options.apiDocumentationJsonFile == None and options.apiDocumentationXmlFile == None: print " * You must define one of JSON or XML API documentation file!" docTree = None if options.verbose: print " * Generating API tree..." else: print " * Generating API tree: ", for fileId in sortedIncludeList: if options.verbose: print " - %s" % fileId else: sys.stdout.write(".") sys.stdout.flush() docTree = api.createDoc(loader.getTree(fileDb, fileId, options), docTree) if not options.verbose: print if docTree: print " * Finalising tree..." api.postWorkPackage(docTree, docTree) if options.apiDocumentationXmlFile != None: print " * Writing XML API file to %s" % options.apiDocumentationXmlFile xmlContent = "\n" if options.addNewLines: xmlContent += "\n" + tree.nodeToXmlString(docTree) else: xmlContent += tree.nodeToXmlString(docTree, "", "", "") filetool.save(options.apiDocumentationXmlFile, xmlContent, options.xmlOutputEncoding) if options.apiDocumentationJsonFile != None: print " * Writing JSON API file to %s" % options.apiDocumentationJsonFile if options.addNewLines: jsonContent = tree.nodeToJsonString(docTree) else: jsonContent = tree.nodeToJsonString(docTree, "", "", "") filetool.save(options.apiDocumentationJsonFile, jsonContent, options.scriptOutputEncoding) ###################################################################### # CREATE COPY OF RESOURCES ###################################################################### if options.copyResources: print print " CREATE COPY OF RESOURCES:" print "----------------------------------------------------------------------------" resources.copy(options, sortedIncludeList, fileDb) ###################################################################### # GENERATION OF SETTINGS ###################################################################### if options.generateSourceScript or options.generateCompiledScript: settingsStr = "" if len(options.defineRuntimeSetting) != 0: print print " GENERATION OF SETTINGS:" print "----------------------------------------------------------------------------" print " * Processing input data..." settingsStr = settings.generate(options) if options.settingsScriptFile: print " * Storing result to %s" % options.settingsScriptFile filetool.save(options.settingsScriptFile, settingsStr) # clear settings for build and source settingsStr = "" ###################################################################### # GENERATION OF SOURCE VERSION ###################################################################### if options.generateSourceScript: print print " GENERATION OF SOURCE SCRIPT:" print "----------------------------------------------------------------------------" if options.sourceScriptFile == None: print " * You must define the source script file!" sys.exit(1) else: options.sourceScriptFile = os.path.normpath(options.sourceScriptFile) print " * Generating includer..." sourceOutput = settingsStr srcEol = ""; if options.addNewLines: srcEol = "\n"; if sourceOutput != "": settingsStr += srcEol # Define javascript loaders jsLoaders = {} # HTML-only: create ')};""" # XHTML-compatible: create and append DOM script nodes jsLoaders["domappend"] = """if(document.createElementNS&&parentNode.namespaceURI)""" + srcEol + """var includeJs=function(src){var js=document.createElementNS(parentNode.namespaceURI,"script");js.type="text/javascript";js.src=src;parentNode.appendChild(js)};""" + srcEol + """else """ + srcEol + """var includeJs=function(src){var js=document.createElement("script");js.type="text/javascript";js.src=src;parentNode.appendChild(js)};""" # Source loader closure sourceOutput += """(function(sources){""" + srcEol # Detect the node we are being called from sourceOutput += """var parentNode=document.getElementsByTagName('body')[0]||document.getElementsByTagName('head')[0];""" + srcEol # Autoselect a loader based on client engine if options.sourceLoaderType == "auto": sourceOutput += """var clientEngine=null;""" + srcEol # Opera sourceOutput += """if(window.opera&&/Opera[\s\/]([0-9\.]*)/.test(navigator.userAgent))clientEngine="opera";else """ + srcEol # Khtml sourceOutput += """if(typeof navigator.vendor==="string"&&navigator.vendor==="KDE"&&/KHTML\/([0-9-\.]*)/.test(navigator.userAgent))clientEngine="khtml";else """ + srcEol # Webkit sourceOutput += """if(navigator.userAgent.indexOf("AppleWebKit")!=-1&&/AppleWebKit\/([0-9-\.]*)/.test(navigator.userAgent))clientEngine="webkit";else """ + srcEol # Gecko sourceOutput += """if(window.controllers&&typeof navigator.product==="string"&&navigator.product==="Gecko"&&/rv\:([^\);]+)(\)|;)/.test(navigator.userAgent))clientEngine="gecko";else """ + srcEol # MShtml sourceOutput += """if(/MSIE\s+([^\);]+)(\)|;)/.test(navigator.userAgent))clientEngine="mshtml";""" + srcEol # Select the loader based on the engine sourceOutput += """switch(clientEngine){""" + srcEol # Use DOM sourceOutput += """case "opera": case "gecko":""" + srcEol sourceOutput += jsLoaders["domappend"] + srcEol sourceOutput += """break;""" + srcEol # Use document.write() sourceOutput += """case "webkit": case "khtml": case "mshtml": default:""" + srcEol sourceOutput += jsLoaders["docwrite"] + srcEol sourceOutput += """break;""" + srcEol sourceOutput += """}""" + srcEol # Use a fixed loader else: sourceOutput += jsLoaders[options.sourceLoaderType] + srcEol # Loading loop sourceOutput += """for(var i=0;i