mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-02 23:26:16 +00:00
"press" support for flattening to a single JavaScript file, and foundation of intelligent image sprites
This commit is contained in:
Executable → Regular
+411
-360
@@ -1,361 +1,412 @@
|
||||
var objjPath = OBJJ_LIB+'/Frameworks/Objective-J/Objective-J.js',
|
||||
bridgePath = OBJJ_LIB+'/bridge.js',
|
||||
envPath = "/Users/tlrobinson/280North/git/cappuccino/Tools/press/env.js";
|
||||
|
||||
/*
|
||||
param context includes
|
||||
scope: a global variable containing objj_files hash
|
||||
processedFiles: hash containing file paths which have already been analyzed
|
||||
dependencies: hash mapping from paths to an array of global variables defined by that file
|
||||
[importCallback]: callback function that is called for each imported file (takes importing file path, and imported file path parameters)
|
||||
[referencedCallback]: callback function that is called for each referenced file (takes referencing file path, referenced file path parameters, and list of tokens)
|
||||
[importedFiles]: hash that will contain a mapping of file names to a hash of imported files
|
||||
[referencedFiles]: hash that will contain a mapping of file names to a hash of referenced files (which contains a hash of tokens referenced)
|
||||
|
||||
param file is an objj_file object containing path, fragments, content, bundle, etc
|
||||
*/
|
||||
function traverseDependencies(context, file)
|
||||
{
|
||||
if (context.processedFiles[file.path])
|
||||
return;
|
||||
context.processedFiles[file.path] = true;
|
||||
|
||||
var ignoreImports = false;
|
||||
if (context.ignoreAllImports)
|
||||
{
|
||||
CPLog.warn("Ignoring all import fragments. ("+file.path+")");
|
||||
ignoreImports = true;
|
||||
}
|
||||
else if (context.ignoreFrameworkImports)
|
||||
{
|
||||
var matches = file.path.match(/([^\/]+)\/([^\/]+)\.j$/); // Matches "ZZZ/ZZZ.j" (e.x. AppKit/AppKit.j and Foundation/Foundation.j)
|
||||
if (matches && matches[1] === matches[2])
|
||||
{
|
||||
CPLog.warn("Framework import file! Ignoring all import fragments. ("+file.path+")");
|
||||
ignoreImports = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.fragments)
|
||||
{
|
||||
if (file.included)
|
||||
CPLog.warn(file.path + " is included but missing fragments");
|
||||
else
|
||||
CPLog.info("Preprocessing " + file.path);
|
||||
|
||||
file.fragments = objj_preprocess(file.contents, file.bundle, file);
|
||||
}
|
||||
|
||||
var referencedFiles = {},
|
||||
importedFiles = {};
|
||||
|
||||
CPLog.trace("Processing " + file.path + " fragments ("+file.fragments.length+")");
|
||||
for (var i = 0; i < file.fragments.length; i++)
|
||||
{
|
||||
var fragment = file.fragments[i];
|
||||
|
||||
if (fragment.type & FRAGMENT_CODE)
|
||||
{
|
||||
var lexer = new objj_lexer(fragment.info, NULL);
|
||||
|
||||
var token;
|
||||
while (token = lexer.skip_whitespace())
|
||||
{
|
||||
if (context.dependencies[token])
|
||||
{
|
||||
var files = context.dependencies[token]
|
||||
for (var j = 0; j < files.length; j++)
|
||||
{
|
||||
// don't record references to self
|
||||
if (files[j] != file.path)
|
||||
{
|
||||
if (!referencedFiles[files[j]])
|
||||
referencedFiles[files[j]] = {};
|
||||
|
||||
referencedFiles[files[j]][token] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fragment.type & FRAGMENT_FILE)
|
||||
{
|
||||
if (ignoreImports)
|
||||
{
|
||||
fragment.conditionallyIgnore = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var importedFile = findImportInObjjFiles(context.scope, fragment);
|
||||
if (importedFile)
|
||||
{
|
||||
// should never import self, but just in case?
|
||||
if (importedFile != file.path)
|
||||
importedFiles[importedFile] = true;
|
||||
else
|
||||
CPLog.error("Ignoring self import (why are you importing yourself!?): " + file.path);
|
||||
}
|
||||
else
|
||||
CPLog.error("Couldn't find file for import " + fragment.info + "("+fragment.type+")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check each imported file
|
||||
for (var importedFile in importedFiles)
|
||||
{
|
||||
if (importedFile != file.path)
|
||||
{
|
||||
if (context.importCallback)
|
||||
context.importCallback(file.path, importedFile);
|
||||
|
||||
if (context.scope.objj_files[importedFile])
|
||||
traverseDependencies(context, context.scope.objj_files[importedFile]);
|
||||
else
|
||||
CPLog.error("Missing imported file: " + importedFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.importedFiles)
|
||||
context.importedFiles[file.path] = importedFiles;
|
||||
|
||||
// check each referenced file
|
||||
for (var referencedFile in referencedFiles)
|
||||
{
|
||||
if (referencedFile != file.path)
|
||||
{
|
||||
if (context.referenceCallback)
|
||||
context.referenceCallback(file.path, referencedFile, referencedFiles[referencedFile]);
|
||||
|
||||
if (context.scope.objj_files[referencedFile])
|
||||
traverseDependencies(context, context.scope.objj_files[referencedFile]);
|
||||
else
|
||||
CPLog.error("Missing referenced file: " + referencedFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.referencedFiles)
|
||||
context.referencedFiles[file.path] = referencedFiles;
|
||||
}
|
||||
|
||||
function findImportInObjjFiles(scope, fragment)
|
||||
{
|
||||
var importPath = null;
|
||||
|
||||
if (fragment.type & FRAGMENT_LOCAL)
|
||||
{
|
||||
var searchPath = fragment.info;
|
||||
if (scope.objj_files[searchPath])
|
||||
{
|
||||
importPath = searchPath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var count = scope.OBJJ_INCLUDE_PATHS.length;
|
||||
while (count--)
|
||||
{
|
||||
var searchPath = scope.OBJJ_INCLUDE_PATHS[count].replace(/\/$/, "") + "/" + fragment.info;
|
||||
if (scope.objj_files[searchPath])
|
||||
{
|
||||
importPath = searchPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return importPath;
|
||||
}
|
||||
|
||||
// given a fresh scope and the path to a root source file, determine which files define each global variable
|
||||
function findGlobalDefines(context, scope, rootPath)
|
||||
{
|
||||
addMockBrowserEnvironment(scope);
|
||||
|
||||
var ignore = cloneProperties(scope, true);
|
||||
ignore['bundle'] = true;
|
||||
|
||||
var dependencies = {};
|
||||
|
||||
//scope.fragment_evaluate_file_original = scope.fragment_evaluate_file;
|
||||
//scope.fragment_evaluate_file = function(aFragment)
|
||||
//{
|
||||
// //CPLog.trace("Loading "+aFragment.info);
|
||||
//
|
||||
// var result = scope.fragment_evaluate_file_original(aFragment);
|
||||
//
|
||||
// return result;
|
||||
//}
|
||||
|
||||
var needsPatch = true;
|
||||
|
||||
var fragment_evaluate_file_original = scope.fragment_evaluate_file;
|
||||
scope.fragment_evaluate_file = function(aFragment)
|
||||
{
|
||||
// patch Foundation.j (HACK for Rhino bug #374918)
|
||||
if (needsPatch && aFragment.file && (/Foundation\.j$/).test(aFragment.file.path))
|
||||
{
|
||||
CPLog.error("Patching Foundation.j");
|
||||
|
||||
var patchFragment = new objj_fragment();
|
||||
patchFragment.info = "__RHINO_FIRST_SCOPE.String.prototype.isa=CPString;__RHINO_FIRST_SCOPE.Number.prototype.isa=CPNumber;__RHINO_FIRST_SCOPE.Boolean.prototype.isa=CPNumber;print('PATCHED!');";
|
||||
patchFragment.type = FRAGMENT_CODE;
|
||||
patchFragment.file = aFragment.file;
|
||||
patchFragment.bundle = aFragment.bundle;
|
||||
patchFragment.context = aFragment.context;
|
||||
|
||||
for (var i = 0; i < aFragment.context.fragments.length; i++)
|
||||
{
|
||||
if ((aFragment.context.fragments[i].type & FRAGMENT_FILE) && (/Foundation\//).test(aFragment.context.fragments[i].info))
|
||||
{
|
||||
CPLog.error("Inserting patch");
|
||||
aFragment.context.fragments.splice(i, 0, patchFragment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
needsPatch = false;
|
||||
}
|
||||
|
||||
return fragment_evaluate_file_original(aFragment);
|
||||
}
|
||||
|
||||
var fragment_evaluate_code_original = scope.fragment_evaluate_code;
|
||||
scope.fragment_evaluate_code = function(aFragment)
|
||||
{
|
||||
CPLog.debug("Evaling "+aFragment.file.path + " / " + aFragment.bundle.path);
|
||||
|
||||
var before = cloneProperties(scope);
|
||||
|
||||
var result = fragment_evaluate_code_original(aFragment);
|
||||
|
||||
var definedGlobals = {};
|
||||
diff(before, scope, ignore, definedGlobals, definedGlobals, null);
|
||||
dependencies[aFragment.file.path] = definedGlobals;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
runWithScope(context, scope, function(importName)
|
||||
{
|
||||
objj_import(importName, true, NULL);
|
||||
}, [rootPath]);
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
function coalesceGlobalDefines(globals)
|
||||
{
|
||||
var dependencies = {};
|
||||
for (var fileName in globals)
|
||||
{
|
||||
var fileGlobals = globals[fileName];
|
||||
|
||||
for (var globalName in fileGlobals)
|
||||
{
|
||||
if (!dependencies[globalName])
|
||||
dependencies[globalName] = [];
|
||||
dependencies[globalName].push(fileName);
|
||||
}
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
// create a new scope loaded with Objective-J
|
||||
function makeObjjScope(context, debug)
|
||||
{
|
||||
// init standard js scope objects
|
||||
var scope = context.initStandardObjects();
|
||||
|
||||
if (debug)
|
||||
{
|
||||
scope.objj_alert = print;
|
||||
scope.debug = true;
|
||||
}
|
||||
|
||||
// give the scope "print"
|
||||
scope.print = function(value) { Packages.java.lang.System.out.println(String(value)); };
|
||||
|
||||
// HACK for Rhino bug #374918 (fix toll free bridging)
|
||||
scope.__RHINO_FIRST_SCOPE = this;
|
||||
CPLog.info("__RHINO_FIRST_SCOPE="+scope.__RHINO_FIRST_SCOPE);
|
||||
|
||||
// load and eval fake browser environment
|
||||
//var envSource = readFile(envPath);
|
||||
//if (envSource)
|
||||
// context.evaluateString(scope, envSource, "env.js", 1, null);
|
||||
//else
|
||||
// CPLog.warn("Missing env.js");
|
||||
|
||||
// load and eval the bridge
|
||||
var bridgeSource = readFile(bridgePath);
|
||||
if (bridgeSource)
|
||||
context.evaluateString(scope, bridgeSource, "bridge.js", 1, null);
|
||||
else
|
||||
CPLog.warn("Missing bridge.js");
|
||||
|
||||
// load and eval obj-j
|
||||
var objjSource = readFile(objjPath);
|
||||
if (objjSource)
|
||||
context.evaluateString(scope, objjSource, "Objective-J.js", 1, null);
|
||||
else
|
||||
CPLog.warn("Missing Objective-J.js");
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
// run a function within the given scope (func can be a function object if the source of the function is returned by toString() as it is by default)
|
||||
function runWithScope(context, scope, func, arguments)
|
||||
{
|
||||
scope.__runWithScopeArgs = arguments || [];
|
||||
|
||||
var code = "("+func+").apply(this, this.__runWithScopeArgs); serviceTimeouts();";
|
||||
|
||||
return context.evaluateString(scope, code, "<cmd>", 1, null);
|
||||
}
|
||||
|
||||
// add a mock browser environment to the provided scope
|
||||
function addMockBrowserEnvironment(scope)
|
||||
{
|
||||
// TODO: complete this. or use env.js?
|
||||
|
||||
scope.Element = function() {
|
||||
this.style = {}
|
||||
}
|
||||
|
||||
scope.document = {
|
||||
createElement : function() {
|
||||
return new scope.Element();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// does a shallow copy of an object. if onlyList is true, it sets each property to "true" instead of the actual value
|
||||
function cloneProperties(object, onlyList)
|
||||
{
|
||||
var results = {}
|
||||
for (var memeber in object)
|
||||
results[memeber] = onlyList ? true : object[memeber];
|
||||
return results;
|
||||
}
|
||||
|
||||
function diff(objectA, objectB, ignore, added, changed, deleted)
|
||||
{
|
||||
for (var i in objectB)
|
||||
if (added && !ignore[i] && typeof objectA[i] == "undefined")
|
||||
added[i] = true;
|
||||
for (var i in objectB)
|
||||
if (changed && !ignore[i] && typeof objectA[i] != "undefined" && typeof objectB[i] != "undefined" && objectA[i] !== objectB[i])
|
||||
changed[i] = true;
|
||||
for (var i in objectA)
|
||||
if (deleted && !ignore[i] && typeof objectB[i] == "undefined")
|
||||
deleted[i] = true;
|
||||
}
|
||||
|
||||
function allKeys(object)
|
||||
{
|
||||
var result = [];
|
||||
for (var i in object)
|
||||
result.push(i)
|
||||
return result.sort();
|
||||
var objjPath = OBJJ_LIB+'/Frameworks-Rhino/Objective-J/Objective-J.js',
|
||||
bridgePath = OBJJ_LIB+'/bridge.js',
|
||||
envPath = "/Users/tlrobinson/280North/git/cappuccino/Tools/press/env.js";
|
||||
|
||||
/*
|
||||
param context includes
|
||||
scope: a global variable containing objj_files hash
|
||||
processedFiles: hash containing file paths which have already been analyzed
|
||||
dependencies: hash mapping from paths to an array of global variables defined by that file
|
||||
[importCallback]: callback function that is called for each imported file (takes importing file path, and imported file path parameters)
|
||||
[referencedCallback]: callback function that is called for each referenced file (takes referencing file path, referenced file path parameters, and list of tokens)
|
||||
[importedFiles]: hash that will contain a mapping of file names to a hash of imported files
|
||||
[referencedFiles]: hash that will contain a mapping of file names to a hash of referenced files (which contains a hash of tokens referenced)
|
||||
|
||||
param file is an objj_file object containing path, fragments, content, bundle, etc
|
||||
*/
|
||||
function traverseDependencies(context, file)
|
||||
{
|
||||
if (context.processedFiles[file.path])
|
||||
return;
|
||||
context.processedFiles[file.path] = true;
|
||||
|
||||
var ignoreImports = false;
|
||||
if (context.ignoreAllImports)
|
||||
{
|
||||
CPLog.warn("Ignoring all import fragments. ("+file.path+")");
|
||||
ignoreImports = true;
|
||||
}
|
||||
else if (context.ignoreFrameworkImports)
|
||||
{
|
||||
var matches = file.path.match(/([^\/]+)\/([^\/]+)\.j$/); // Matches "ZZZ/ZZZ.j" (e.x. AppKit/AppKit.j and Foundation/Foundation.j)
|
||||
if (matches && matches[1] === matches[2])
|
||||
{
|
||||
CPLog.warn("Framework import file! Ignoring all import fragments. ("+file.path+")");
|
||||
ignoreImports = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if fragments are missing, preprocess the contents
|
||||
if (!file.fragments)
|
||||
{
|
||||
if (file.included)
|
||||
CPLog.warn(file.path + " is included but missing fragments");
|
||||
else
|
||||
CPLog.warn("Preprocessing " + file.path);
|
||||
|
||||
file.fragments = objj_preprocess(file.contents, file.bundle, file);
|
||||
}
|
||||
|
||||
// sprite: look for pngs in the Resources directory
|
||||
if (!context.bundleImages)
|
||||
context.bundleImages = {};
|
||||
|
||||
if (!context.bundleImages[file.bundle.path])
|
||||
{
|
||||
var resourcesFile = new java.io.File(dirname(file.bundle.path) + "/Resources");
|
||||
if (resourcesFile.exists())
|
||||
{
|
||||
context.bundleImages[file.bundle.path] = {};
|
||||
|
||||
var pngFiles = find(resourcesFile, (/\.png$/));
|
||||
for (var i = 0; i < pngFiles.length; i++)
|
||||
{
|
||||
var path = pathRelativeTo(pngFiles[i].getCanonicalPath(), resourcesFile.getCanonicalPath());
|
||||
context.bundleImages[file.bundle.path][path] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
var images = context.bundleImages[file.bundle.path];
|
||||
|
||||
var referencedFiles = {},
|
||||
importedFiles = {};
|
||||
|
||||
CPLog.debug("Processing " + file.path + " fragments ("+file.fragments.length+")");
|
||||
for (var i = 0; i < file.fragments.length; i++)
|
||||
{
|
||||
var fragment = file.fragments[i];
|
||||
|
||||
if (fragment.type & FRAGMENT_CODE)
|
||||
{
|
||||
var lexer = new objj_lexer(fragment.info, NULL);
|
||||
|
||||
var token;
|
||||
while (token = lexer.skip_whitespace())
|
||||
{
|
||||
if (context.dependencies[token])
|
||||
{
|
||||
var files = context.dependencies[token]
|
||||
for (var j = 0; j < files.length; j++)
|
||||
{
|
||||
// don't record references to self
|
||||
if (files[j] != file.path)
|
||||
{
|
||||
if (!referencedFiles[files[j]])
|
||||
referencedFiles[files[j]] = {};
|
||||
|
||||
referencedFiles[files[j]][token] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var matches = token.match(new RegExp("^['\"](.*)['\"]$"));
|
||||
if (matches && images && images[matches[1]])
|
||||
images[matches[1]] = (images[matches[1]] | 2);
|
||||
}
|
||||
}
|
||||
else if (fragment.type & FRAGMENT_FILE)
|
||||
{
|
||||
if (ignoreImports)
|
||||
{
|
||||
fragment.conditionallyIgnore = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var importedFile = findImportInObjjFiles(context.scope, fragment);
|
||||
if (importedFile)
|
||||
{
|
||||
// should never import self, but just in case?
|
||||
if (importedFile != file.path)
|
||||
importedFiles[importedFile] = true;
|
||||
else
|
||||
CPLog.error("Ignoring self import (why are you importing yourself!?): " + file.path);
|
||||
}
|
||||
else
|
||||
CPLog.error("Couldn't find file for import " + fragment.info + "("+fragment.type+")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check each imported file
|
||||
for (var importedFile in importedFiles)
|
||||
{
|
||||
if (importedFile != file.path)
|
||||
{
|
||||
if (context.importCallback)
|
||||
context.importCallback(file.path, importedFile);
|
||||
|
||||
if (context.scope.objj_files[importedFile])
|
||||
traverseDependencies(context, context.scope.objj_files[importedFile]);
|
||||
else
|
||||
CPLog.error("Missing imported file: " + importedFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.importedFiles)
|
||||
context.importedFiles[file.path] = importedFiles;
|
||||
|
||||
// check each referenced file
|
||||
for (var referencedFile in referencedFiles)
|
||||
{
|
||||
if (referencedFile != file.path)
|
||||
{
|
||||
if (context.referenceCallback)
|
||||
context.referenceCallback(file.path, referencedFile, referencedFiles[referencedFile]);
|
||||
|
||||
if (context.scope.objj_files[referencedFile])
|
||||
traverseDependencies(context, context.scope.objj_files[referencedFile]);
|
||||
else
|
||||
CPLog.error("Missing referenced file: " + referencedFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.referencedFiles)
|
||||
context.referencedFiles[file.path] = referencedFiles;
|
||||
}
|
||||
|
||||
function findImportInObjjFiles(scope, fragment)
|
||||
{
|
||||
var importPath = null;
|
||||
|
||||
if (fragment.type & FRAGMENT_LOCAL)
|
||||
{
|
||||
var searchPath = fragment.info;
|
||||
//CPLog.trace("Looking for " + searchPath);
|
||||
//for (var i in scope.objj_files) CPLog.debug(" " + i);
|
||||
|
||||
if (scope.objj_files[searchPath])
|
||||
{
|
||||
importPath = searchPath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var count = scope.OBJJ_INCLUDE_PATHS.length;
|
||||
while (count--)
|
||||
{
|
||||
var searchPath = scope.OBJJ_INCLUDE_PATHS[count].replace(/\/$/, "") + "/" + fragment.info;
|
||||
if (scope.objj_files[searchPath])
|
||||
{
|
||||
importPath = searchPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return importPath;
|
||||
}
|
||||
|
||||
// given a fresh scope and the path to a root source file, determine which files define each global variable
|
||||
function findGlobalDefines(context, scope, rootPath, evaledFragments)
|
||||
{
|
||||
addMockBrowserEnvironment(scope);
|
||||
|
||||
var ignore = cloneProperties(scope, true);
|
||||
ignore['bundle'] = true;
|
||||
|
||||
var dependencies = {};
|
||||
|
||||
//scope.fragment_evaluate_file_original = scope.fragment_evaluate_file;
|
||||
//scope.fragment_evaluate_file = function(aFragment)
|
||||
//{
|
||||
// //CPLog.trace("Loading "+aFragment.info);
|
||||
//
|
||||
// var result = scope.fragment_evaluate_file_original(aFragment);
|
||||
//
|
||||
// return result;
|
||||
//}
|
||||
|
||||
var needsPatch = true,
|
||||
patchString = "__RHINO_FIRST_SCOPE.String.prototype.isa=CPString;__RHINO_FIRST_SCOPE.Number.prototype.isa=CPNumber;__RHINO_FIRST_SCOPE.Boolean.prototype.isa=CPNumber;print('PATCHED!');";
|
||||
|
||||
// OVERRIDE fragment_evaluate_file
|
||||
var fragment_evaluate_file_original = scope.fragment_evaluate_file;
|
||||
scope.fragment_evaluate_file = function(aFragment) {
|
||||
|
||||
// patch Foundation.j (HACK for Rhino bug #374918)
|
||||
if (needsPatch && aFragment.file && (/Foundation\.j$/).test(aFragment.file.path))
|
||||
{
|
||||
CPLog.warn("Patching Foundation.j");
|
||||
|
||||
var patchFragment = new objj_fragment();
|
||||
patchFragment.info = patchString;
|
||||
patchFragment.type = FRAGMENT_CODE;
|
||||
patchFragment.file = aFragment.file;
|
||||
patchFragment.bundle = aFragment.bundle;
|
||||
patchFragment.context = aFragment.context;
|
||||
|
||||
for (var i = 0; i < aFragment.context.fragments.length; i++)
|
||||
{
|
||||
if ((aFragment.context.fragments[i].type & FRAGMENT_FILE) && (/Foundation\//).test(aFragment.context.fragments[i].info))
|
||||
{
|
||||
CPLog.warn("Inserting patch");
|
||||
aFragment.context.fragments.splice(i, 0, patchFragment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
needsPatch = false;
|
||||
}
|
||||
|
||||
return fragment_evaluate_file_original(aFragment);
|
||||
}
|
||||
|
||||
// OVERRIDE fragment_evaluate_code
|
||||
var fragment_evaluate_code_original = scope.fragment_evaluate_code;
|
||||
scope.fragment_evaluate_code = function(aFragment) {
|
||||
|
||||
CPLog.debug("Evaling "+aFragment.file.path + " / " + aFragment.bundle.path);
|
||||
|
||||
var before = cloneProperties(scope);
|
||||
|
||||
if (evaledFragments)
|
||||
{
|
||||
if (aFragment.info != patchString)
|
||||
evaledFragments.push(aFragment);
|
||||
}
|
||||
|
||||
var result = fragment_evaluate_code_original(aFragment);
|
||||
|
||||
var definedGlobals = {};
|
||||
diff(before, scope, ignore, definedGlobals, definedGlobals, null);
|
||||
dependencies[aFragment.file.path] = definedGlobals;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
runWithScope(context, scope, function(importName) {
|
||||
objj_import(importName, true, NULL);
|
||||
}, [rootPath]);
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
function coalesceGlobalDefines(globals)
|
||||
{
|
||||
var dependencies = {};
|
||||
for (var fileName in globals)
|
||||
{
|
||||
var fileGlobals = globals[fileName];
|
||||
|
||||
for (var globalName in fileGlobals)
|
||||
{
|
||||
if (!dependencies[globalName])
|
||||
dependencies[globalName] = [];
|
||||
dependencies[globalName].push(fileName);
|
||||
}
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
// create a new scope loaded with Objective-J
|
||||
function makeObjjScope(context, debug)
|
||||
{
|
||||
// init standard js scope objects
|
||||
var scope = context.initStandardObjects();
|
||||
|
||||
if (debug)
|
||||
{
|
||||
scope.objj_alert = print;
|
||||
scope.debug = true;
|
||||
}
|
||||
|
||||
// give the scope "print"
|
||||
scope.print = function(value) { Packages.java.lang.System.out.println(String(value)); };
|
||||
|
||||
// HACK for Rhino bug #374918 (fix toll free bridging)
|
||||
scope.__RHINO_FIRST_SCOPE = this;
|
||||
|
||||
// load and eval fake browser environment
|
||||
//var envSource = readFile(envPath);
|
||||
//if (envSource)
|
||||
// context.evaluateString(scope, envSource, "env.js", 1, null);
|
||||
//else
|
||||
// CPLog.warn("Missing env.js");
|
||||
|
||||
// load and eval the bridge
|
||||
var bridgeSource = readFile(bridgePath);
|
||||
if (bridgeSource)
|
||||
context.evaluateString(scope, bridgeSource, "bridge.js", 1, null);
|
||||
else
|
||||
CPLog.warn("Missing bridge.js");
|
||||
|
||||
// load and eval obj-j
|
||||
var objjSource = readFile(objjPath);
|
||||
if (objjSource)
|
||||
context.evaluateString(scope, objjSource, "Objective-J.js", 1, null);
|
||||
else
|
||||
CPLog.warn("Missing Objective-J.js");
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
// run a function within the given scope (func can be a function object if the source of the function is returned by toString() as it is by default)
|
||||
function runWithScope(context, scope, func, arguments)
|
||||
{
|
||||
scope.__runWithScopeArgs = arguments || [];
|
||||
|
||||
var code = "("+func+").apply(this, this.__runWithScopeArgs); serviceTimeouts();";
|
||||
|
||||
return context.evaluateString(scope, code, "<cmd>", 1, null);
|
||||
}
|
||||
|
||||
// add a mock browser environment to the provided scope
|
||||
function addMockBrowserEnvironment(scope)
|
||||
{
|
||||
// TODO: complete this. or use env.js?
|
||||
|
||||
scope.Element = function() {
|
||||
this.style = {}
|
||||
}
|
||||
|
||||
scope.document = {
|
||||
createElement : function() {
|
||||
return new scope.Element();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// does a shallow copy of an object. if onlyList is true, it sets each property to "true" instead of the actual value
|
||||
function cloneProperties(object, onlyList)
|
||||
{
|
||||
var results = {}
|
||||
for (var memeber in object)
|
||||
results[memeber] = onlyList ? true : object[memeber];
|
||||
return results;
|
||||
}
|
||||
|
||||
function diff(objectA, objectB, ignore, added, changed, deleted)
|
||||
{
|
||||
for (var i in objectB)
|
||||
if (added && !ignore[i] && typeof objectA[i] == "undefined")
|
||||
added[i] = true;
|
||||
for (var i in objectB)
|
||||
if (changed && !ignore[i] && typeof objectA[i] != "undefined" && typeof objectB[i] != "undefined" && objectA[i] !== objectB[i])
|
||||
changed[i] = true;
|
||||
for (var i in objectA)
|
||||
if (deleted && !ignore[i] && typeof objectB[i] == "undefined")
|
||||
deleted[i] = true;
|
||||
}
|
||||
|
||||
function allKeys(object)
|
||||
{
|
||||
var result = [];
|
||||
for (var i in object)
|
||||
result.push(i)
|
||||
return result.sort();
|
||||
}
|
||||
|
||||
function find(src, regex)
|
||||
{
|
||||
var results = [];
|
||||
var files = src.listFiles();
|
||||
for (var i = 0; i < files.length; i++)
|
||||
{
|
||||
if (files[i].isFile() && regex.test(files[i].getAbsolutePath()))
|
||||
results.push(files[i]);
|
||||
else if (files[i].isDirectory())
|
||||
results = Array.prototype.concat.apply(results, find(files[i], regex));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
+545
-362
@@ -1,362 +1,545 @@
|
||||
import <Foundation/CPLog.j>
|
||||
|
||||
import "objj-analysis-tools.j"
|
||||
|
||||
CPLogRegister(CPLogPrint);
|
||||
|
||||
var defaultMain = "main.j",
|
||||
defaultFrameworks = "Frameworks";
|
||||
|
||||
function main()
|
||||
{
|
||||
var rootDirectory = null,
|
||||
outputDirectory = null,
|
||||
mainFilename = null,
|
||||
frameworksDirectory = null,
|
||||
optimizePNG = false;
|
||||
|
||||
var usageError = false;
|
||||
while (args.length && !usageError)
|
||||
{
|
||||
var arg = args.shift();
|
||||
switch(arg)
|
||||
{
|
||||
case "--png":
|
||||
optimizePNG = true;
|
||||
break;
|
||||
case "--main":
|
||||
if (args.length)
|
||||
mainFile = args.shift();
|
||||
else
|
||||
usageError = true;
|
||||
break;
|
||||
case "--frameworks":
|
||||
if (args.length)
|
||||
frameworksDirectory = args.shift().replace(/\/$/, "");
|
||||
else
|
||||
usageError = true;
|
||||
break;
|
||||
default:
|
||||
if (rootDirectory == null)
|
||||
rootDirectory = arg.replace(/\/$/, "");
|
||||
else if (outputDirectory == null)
|
||||
outputDirectory = arg.replace(/\/$/, "");
|
||||
else
|
||||
usageError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (rootDirectory == null || outputDirectory == null)
|
||||
{
|
||||
print("Usage: press root_directory output_directory [--main override_main.j] [--frameworks override_frameworks] [--png]");
|
||||
return;
|
||||
}
|
||||
|
||||
rootDirectory = absolutePath(rootDirectory);
|
||||
|
||||
// determine main and frameworks paths
|
||||
var mainPath = rootDirectory + "/" + (mainFilename || defaultMain),
|
||||
frameworksPath = rootDirectory + "/" + (frameworksDirectory || defaultFrameworks);
|
||||
|
||||
CPLog.info("root=" + rootDirectory);
|
||||
CPLog.info("output=" + outputDirectory);
|
||||
CPLog.info("main=" + mainPath)
|
||||
CPLog.info("frameworks=" + frameworksPath);
|
||||
|
||||
// get Rhino context
|
||||
var cx = Packages.org.mozilla.javascript.Context.getCurrentContext(), //Packages.org.mozilla.javascript.Context.enter(),
|
||||
scope = makeObjjScope(cx);
|
||||
|
||||
// set OBJJ_INCLUDE_PATHS to include the frameworks path
|
||||
scope.OBJJ_INCLUDE_PATHS = [frameworksPath];
|
||||
CPLog.info("OBJJ_INCLUDE_PATHS="+scope.OBJJ_INCLUDE_PATHS);
|
||||
|
||||
// phase 1: get global defines
|
||||
var globals = findGlobalDefines(cx, scope, mainPath);
|
||||
|
||||
// coalesce the results
|
||||
var dependencies = coalesceGlobalDefines(globals);
|
||||
|
||||
// phase 2: walk the dependency tree (both imports and references) to determine exactly which files need to be included
|
||||
|
||||
var requiredFiles = {};
|
||||
|
||||
if (scope.objj_files[mainPath])
|
||||
{
|
||||
var context = {
|
||||
scope : scope,
|
||||
dependencies : dependencies,
|
||||
processedFiles : {},
|
||||
ignoreFrameworkImports : true,
|
||||
importCallback : function(importing, imported) {
|
||||
requiredFiles[imported] = true;
|
||||
},
|
||||
referenceCallback : function(referencing, referenced) {
|
||||
requiredFiles[referenced] = true;
|
||||
}
|
||||
}
|
||||
|
||||
requiredFiles[mainPath] = true;
|
||||
traverseDependencies(context, scope.objj_files[mainPath]);
|
||||
|
||||
var count = 0,
|
||||
total = 0;
|
||||
for (var path in scope.objj_files)
|
||||
{
|
||||
if (requiredFiles[path])
|
||||
{
|
||||
CPLog.info("Included: " + path);
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLog.warn("Excluded: " + path);
|
||||
}
|
||||
total++;
|
||||
}
|
||||
CPLog.error("Total required files: " + count + " out of " + total);
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLog.error("Root file not loaded!");
|
||||
return;
|
||||
}
|
||||
|
||||
// phase 3: rebuild .sj files with correct imports, copy .j files
|
||||
|
||||
var outputFiles = {};
|
||||
for (var path in requiredFiles)
|
||||
{
|
||||
var file = scope.objj_files[path],
|
||||
filename = basename(path),
|
||||
directory = dirname(path);
|
||||
|
||||
if (file.path != path)
|
||||
CPLog.warn("Sanity check (file path): " + file.path + " vs. " + path);
|
||||
|
||||
if (file.bundle)
|
||||
{
|
||||
var bundleDirectory = dirname(file.bundle.path);
|
||||
|
||||
if (bundleDirectory != directory)
|
||||
CPLog.warn("Sanity check (directory path): " + directory + " vs. " + bundleDirectory);
|
||||
|
||||
// if it's in a .sj
|
||||
var dict = file.bundle.info,
|
||||
replacedFiles = [dict objectForKey:"CPBundleReplacedFiles"];
|
||||
if (replacedFiles && [replacedFiles containsObject:filename])
|
||||
{
|
||||
var staticPath = bundleDirectory + "/" + [dict objectForKey:"CPBundleExecutable"];
|
||||
if (!outputFiles[staticPath])
|
||||
{
|
||||
outputFiles[staticPath] = [];
|
||||
outputFiles[staticPath].push("@STATIC;1.0;");
|
||||
}
|
||||
outputFiles[staticPath].push("p;");
|
||||
outputFiles[staticPath].push(filename.length+";");
|
||||
outputFiles[staticPath].push(filename);
|
||||
|
||||
for (var i = 0; i < file.fragments.length; i++)
|
||||
{
|
||||
if (file.fragments[i].type & FRAGMENT_CODE)
|
||||
{
|
||||
outputFiles[staticPath].push("c;");
|
||||
outputFiles[staticPath].push(file.fragments[i].info.length+";");
|
||||
outputFiles[staticPath].push(file.fragments[i].info);
|
||||
}
|
||||
else if (file.fragments[i].type & FRAGMENT_FILE)
|
||||
{
|
||||
var ignoreFragment = false;
|
||||
if (file.fragments[i].conditionallyIgnore)
|
||||
{
|
||||
var importPath = findImportInObjjFiles(scope, file.fragments[i]);
|
||||
if (!importPath || !requiredFiles[importPath])
|
||||
{
|
||||
ignoreFragment = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignoreFragment)
|
||||
{
|
||||
if (file.fragments[i].type & FRAGMENT_LOCAL)
|
||||
{
|
||||
var relativePath = pathRelativeTo(file.fragments[i].info, directory)
|
||||
|
||||
outputFiles[staticPath].push("i;");
|
||||
outputFiles[staticPath].push(relativePath.length+";");
|
||||
outputFiles[staticPath].push(relativePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputFiles[staticPath].push("I;");
|
||||
outputFiles[staticPath].push(file.fragments[i].info.length+";");
|
||||
outputFiles[staticPath].push(file.fragments[i].info);
|
||||
}
|
||||
}
|
||||
else
|
||||
CPLog.warn("Ignoring import fragment " + file.fragments[i].info + " in " + path);
|
||||
}
|
||||
else
|
||||
CPLog.error("Unknown fragment type");
|
||||
}
|
||||
}
|
||||
// always output individual .j files
|
||||
else
|
||||
{
|
||||
outputFiles[path] = file.contents;
|
||||
}
|
||||
}
|
||||
else
|
||||
CPLog.warn("No bundle for " + path)
|
||||
}
|
||||
|
||||
// phase 4: copy everything and write out the new files
|
||||
|
||||
var rootDirectoryFile = new Packages.java.io.File(rootDirectory),
|
||||
outputDirectoryFile = new Packages.java.io.File(outputDirectory);
|
||||
|
||||
copyDirectory(rootDirectoryFile, outputDirectoryFile, optimizePNG);
|
||||
|
||||
for (var path in outputFiles)
|
||||
{
|
||||
var file = new java.io.File(outputDirectoryFile, pathRelativeTo(path, rootDirectory));
|
||||
|
||||
var parent = file.getParentFile();
|
||||
if (!parent.exists())
|
||||
{
|
||||
CPLog.warn(parent + " doesn't exist, creating directories.");
|
||||
parent.mkdirs();
|
||||
}
|
||||
|
||||
CPLog.info("Writing out " + file);
|
||||
|
||||
var writer = new java.io.BufferedWriter(new java.io.FileWriter(file));
|
||||
|
||||
if (typeof outputFiles[path] == "string")
|
||||
writer.write(outputFiles[path]);
|
||||
else
|
||||
writer.write(outputFiles[path].join(""));
|
||||
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper Utilities
|
||||
|
||||
// TODO: moved elsewhere?
|
||||
|
||||
function copyDirectory(src, dst, optimizePNG)
|
||||
{
|
||||
CPLog.trace("Copying directory " + src);
|
||||
|
||||
dst.mkdirs();
|
||||
|
||||
var files = src.listFiles();
|
||||
for (var i = 0; i < files.length; i++)
|
||||
{
|
||||
if (files[i].isFile())
|
||||
copyFile(files[i], new Packages.java.io.File(dst, files[i].getName()), optimizePNG);
|
||||
else if (files[i].isDirectory())
|
||||
copyDirectory(files[i], new Packages.java.io.File(dst, files[i].getName()), optimizePNG);
|
||||
}
|
||||
}
|
||||
|
||||
function copyFile(src, dst, optimizePNG)
|
||||
{
|
||||
if (optimizePNG && (/.png$/).test(src.getName()))
|
||||
{
|
||||
CPLog.warn("Optimizing .png " + src);
|
||||
exec(["pngcrush", "-rem", "alla", "-reduce", /*"-brute",*/ src.getAbsolutePath(), dst.getAbsolutePath()]);
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLog.trace("Copying file " + src);
|
||||
|
||||
var input = (new Packages.java.io.FileInputStream(src)).getChannel(),
|
||||
output = (new Packages.java.io.FileOutputStream(dst)).getChannel();
|
||||
|
||||
input.transferTo(0, input.size(), output);
|
||||
|
||||
input.close();
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
|
||||
function dirname(path)
|
||||
{
|
||||
return path.substring(0, path.lastIndexOf("/"));
|
||||
}
|
||||
|
||||
function basename(path)
|
||||
{
|
||||
return path.substring(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
function absolutePath(path)
|
||||
{
|
||||
return String((new Packages.java.io.File(path)).getCanonicalPath());
|
||||
}
|
||||
|
||||
function pathRelativeTo(target, relativeTo)
|
||||
{
|
||||
var components = [],
|
||||
targetParts = target.split("/"),
|
||||
relativeParts = relativeTo ? relativeTo.split("/") : [];
|
||||
|
||||
var i = 0;
|
||||
while (i < targetParts.length)
|
||||
{
|
||||
if (targetParts[i] != relativeParts[i])
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
for (var j = i; j < relativeParts.length; j++)
|
||||
components.push("..");
|
||||
|
||||
for (var j = i; j < targetParts.length; j++)
|
||||
components.push(targetParts[j]);
|
||||
|
||||
var result = components.join("/");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function exec()
|
||||
{
|
||||
var printOutput = false;
|
||||
|
||||
var runtime = Packages.java.lang.Runtime.getRuntime()
|
||||
var p = runtime.exec.apply(runtime, arguments);
|
||||
|
||||
var stdout = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getInputStream())),
|
||||
stdoutString = "",
|
||||
stderr = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getErrorStream())),
|
||||
stderrString = "";
|
||||
|
||||
var done = false;
|
||||
while (!done)
|
||||
{
|
||||
done = true;
|
||||
if (s = stdout.readLine())
|
||||
{
|
||||
stdoutString += s;
|
||||
if (printOutput)
|
||||
CPLog.info("exec: " + s);
|
||||
done = false;
|
||||
}
|
||||
if (s = stderr.readLine())
|
||||
{
|
||||
stderrString += s;
|
||||
//if (printOutput)
|
||||
CPLog.warn("exec: " + s);
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
|
||||
var code = p.waitFor();
|
||||
|
||||
return { code : code, stdout : stdoutString, stderr : stderrString };
|
||||
}
|
||||
|
||||
main();
|
||||
import <Foundation/Foundation.j>
|
||||
|
||||
import "objj-analysis-tools.j"
|
||||
|
||||
var defaultMain = "main.j",
|
||||
defaultFrameworks = "Frameworks";
|
||||
|
||||
var usageMessage =
|
||||
"Usage: press root_directory output_directory [options]\n\
|
||||
--main path The relative path (from root_directory) to the main file (default: 'main.j')\n\
|
||||
--frameworks path The relative path (from root_directory) to the frameworks directory (default: 'Frameworks')\n\
|
||||
--png Run pngcrush on all PNGs (pngcrush must be installed!)\n\
|
||||
--flatten Flatten all code into a single Application.js file and attempt add script tag to index.html (useful for Adobe AIR and CDN deployment)\n\
|
||||
--nostrip Don't strip any files";
|
||||
|
||||
function main()
|
||||
{
|
||||
var rootDirectory = null,
|
||||
outputDirectory = null,
|
||||
mainFilename = null,
|
||||
frameworksDirectory = null,
|
||||
optimizePNG = false,
|
||||
flatten = false,
|
||||
noStrip = false,
|
||||
verbose = true;
|
||||
|
||||
var usageError = false;
|
||||
while (args.length && !usageError)
|
||||
{
|
||||
var arg = args.shift();
|
||||
switch(arg)
|
||||
{
|
||||
case "--main":
|
||||
if (args.length)
|
||||
mainFile = args.shift();
|
||||
else
|
||||
usageError = true;
|
||||
break;
|
||||
case "--frameworks":
|
||||
if (args.length)
|
||||
frameworksDirectory = args.shift().replace(/\/$/, "");
|
||||
else
|
||||
usageError = true;
|
||||
break;
|
||||
case "--png":
|
||||
optimizePNG = true;
|
||||
break;
|
||||
case "--flatten":
|
||||
flatten = true;
|
||||
break;
|
||||
case "--nostrip":
|
||||
noStrip = true;
|
||||
break;
|
||||
default:
|
||||
if (rootDirectory == null)
|
||||
rootDirectory = arg.replace(/\/$/, "");
|
||||
else if (outputDirectory == null)
|
||||
outputDirectory = arg.replace(/\/$/, "");
|
||||
else
|
||||
usageError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
CPLogRegister(CPLogPrint);
|
||||
else
|
||||
CPLogRegisterRange(CPLogPrint, "fatal", "info");
|
||||
|
||||
if (usageError || rootDirectory == null || outputDirectory == null)
|
||||
{
|
||||
print(usageMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
rootDirectory = absolutePath(rootDirectory);
|
||||
|
||||
// determine main and frameworks paths
|
||||
var mainPath = rootDirectory + "/" + (mainFilename || defaultMain),
|
||||
frameworksPath = rootDirectory + "/" + (frameworksDirectory || defaultFrameworks);
|
||||
|
||||
CPLog.info("Application root: " + rootDirectory);
|
||||
CPLog.info("Output directory: " + outputDirectory);
|
||||
CPLog.info("Main file: " + mainPath)
|
||||
CPLog.info("Frameworks: " + frameworksPath);
|
||||
|
||||
// get a Rhino context
|
||||
var cx = Packages.org.mozilla.javascript.Context.getCurrentContext(),
|
||||
scope = makeObjjScope(cx);
|
||||
|
||||
// set OBJJ_INCLUDE_PATHS to include the frameworks path
|
||||
scope.OBJJ_INCLUDE_PATHS = [frameworksPath];
|
||||
|
||||
// flattening bookkeeping. keep track of the bundles and evaled code (in the correct order!)
|
||||
var bundleArchives = [],
|
||||
evaledFragments = [];
|
||||
|
||||
scope.objj_search.prototype.didReceiveBundleResponseOriginal = scope.objj_search.prototype.didReceiveBundleResponse;
|
||||
scope.objj_search.prototype.didReceiveBundleResponse = function(aResponse) {
|
||||
//CPLog.trace("RESPONSE: " + aResponse);
|
||||
|
||||
var __fakeResponse = {
|
||||
success : aResponse.success,
|
||||
filePath : pathRelativeTo(aResponse.filePath, rootDirectory)
|
||||
};
|
||||
|
||||
if (aResponse.success)
|
||||
{
|
||||
var xmlOutput = new Packages.java.io.ByteArrayOutputStream();
|
||||
outputTransformer(xmlOutput, aResponse.xml, "UTF-8");
|
||||
__fakeResponse.xml = String(xmlOutput.toString());
|
||||
//CPLog.trace("SERIALIZED: " + __fakeResponse.xml.substring(0,100));
|
||||
}
|
||||
|
||||
bundleArchives.push(__fakeResponse);
|
||||
|
||||
this.didReceiveBundleResponseOriginal.apply(this, arguments);
|
||||
}
|
||||
|
||||
// phase 1: get global defines
|
||||
CPLog.info("Loading application...");
|
||||
var globals = findGlobalDefines(cx, scope, mainPath, evaledFragments);
|
||||
|
||||
// coalesce the results
|
||||
var dependencies = coalesceGlobalDefines(globals);
|
||||
|
||||
// phase 2: walk the dependency tree (both imports and references) to determine exactly which files need to be included
|
||||
|
||||
var requiredFiles = {};
|
||||
|
||||
if (noStrip)
|
||||
{
|
||||
// all files are required. no need for analysis
|
||||
requiredFiles = scope.objj_files;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!scope.objj_files[mainPath])
|
||||
{
|
||||
CPLog.error("Root file not loaded!");
|
||||
return;
|
||||
}
|
||||
|
||||
CPLog.info("Analyzing dependencies...");
|
||||
|
||||
var context = {
|
||||
scope : scope,
|
||||
dependencies : dependencies,
|
||||
processedFiles : {},
|
||||
ignoreFrameworkImports : true,
|
||||
importCallback : function(importing, imported) {
|
||||
requiredFiles[imported] = true;
|
||||
},
|
||||
referenceCallback : function(referencing, referenced) {
|
||||
requiredFiles[referenced] = true;
|
||||
}
|
||||
}
|
||||
|
||||
requiredFiles[mainPath] = true;
|
||||
traverseDependencies(context, scope.objj_files[mainPath]);
|
||||
|
||||
var count = 0,
|
||||
total = 0;
|
||||
for (var path in scope.objj_files)
|
||||
{
|
||||
if (requiredFiles[path])
|
||||
{
|
||||
CPLog.debug("Included: " + path);
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLog.warn("Excluded: " + path);
|
||||
}
|
||||
total++;
|
||||
}
|
||||
CPLog.info("Total required files: " + count + " out of " + total);
|
||||
|
||||
// FIXME: sprite images
|
||||
for (var i in context.bundleImages)
|
||||
{
|
||||
var images = context.bundleImages[i];
|
||||
|
||||
//CPLog.info("Bundle images for " + i);
|
||||
//for (var j in images)
|
||||
// CPLog.debug(j + " = " + images[j]);
|
||||
}
|
||||
}
|
||||
|
||||
var outputFiles = {};
|
||||
|
||||
if (flatten)
|
||||
{
|
||||
// phase 3a: build single Application.js file (and modified index.html)
|
||||
CPLog.info("Flattening...");
|
||||
|
||||
var applicationJS = [],
|
||||
indexHTML = readFile(rootDirectory + "/index.html");
|
||||
|
||||
// shim for faking bundle stuff. kind of a giant hack.
|
||||
var fakeDidReceiveBundleResponse = function(aResponse)
|
||||
{
|
||||
var bundle = new objj_bundle();
|
||||
|
||||
bundle.path = aResponse.filePath;
|
||||
|
||||
if (aResponse.success)
|
||||
bundle.info = CPPropertyListCreateFromXMLData({ string : aResponse.xml });
|
||||
else
|
||||
bundle.info = new objj_dictionary();
|
||||
|
||||
objj_bundles[aResponse.filePath] = bundle;
|
||||
}
|
||||
|
||||
// add fake bundle response bookkeeping
|
||||
applicationJS.push("var __fakeDidReceiveBundleResponse = " + String(fakeDidReceiveBundleResponse));
|
||||
applicationJS.push("var __fakeBundleArchives = " + CPJSObjectCreateJSON(bundleArchives) + ";");
|
||||
applicationJS.push("for (var i = 0; i < __fakeBundleArchives.length; i++) __fakeDidReceiveBundleResponse(__fakeBundleArchives[i]);")
|
||||
|
||||
// add each fragment, wrapped in a function, along with OBJJ_CURRENT_BUNDLE bookkeeping
|
||||
for (var i = 0; i < evaledFragments.length; i++)
|
||||
{
|
||||
if (requiredFiles[evaledFragments[i].file.path])
|
||||
{
|
||||
applicationJS.push("(function() {");
|
||||
applicationJS.push("var OBJJ_CURRENT_BUNDLE = objj_bundles['"+pathRelativeTo(evaledFragments[i].bundle.path, rootDirectory)+"'];");
|
||||
applicationJS.push(evaledFragments[i].info);
|
||||
applicationJS.push("})();");
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLog.warn("Stripping " + evaledFragments[i].file.path);
|
||||
}
|
||||
}
|
||||
|
||||
// call main once the page has loaded
|
||||
applicationJS.push(
|
||||
"if (window.addEventListener) \
|
||||
window.addEventListener('load', main, false); \
|
||||
else if (window.attachEvent) \
|
||||
window.attachEvent('onload', main);"
|
||||
);
|
||||
|
||||
|
||||
// do a little regex magic to remove the objj_import and put in a script tag for Application.js
|
||||
var matches = indexHTML.match(/<script[^>]*>(?:.|\n)*?<\/script>/g);
|
||||
for (var i = 0; i < matches.length; i++)
|
||||
{
|
||||
if (/\bobjj_import\s*\(/.test(matches[i]))
|
||||
{
|
||||
CPLog.warn("Replacing objj_import with script tag.");
|
||||
var replacement = '<script src = "Application.js" type = "text/javascript"></script>\n' + matches[i].replace("objj_import", "//objj_import");
|
||||
indexHTML = indexHTML.replace(matches[i], replacement);
|
||||
}
|
||||
}
|
||||
|
||||
// output Application.js and index.html
|
||||
outputFiles[rootDirectory + "/Application.js"] = applicationJS.join("\n");
|
||||
outputFiles[rootDirectory + "/index.html"] = indexHTML;
|
||||
}
|
||||
else
|
||||
{
|
||||
// phase 3b: rebuild .sj files with correct imports, copy .j files
|
||||
|
||||
var bundles = {};
|
||||
|
||||
for (var path in requiredFiles)
|
||||
{
|
||||
var file = scope.objj_files[path],
|
||||
filename = basename(path),
|
||||
directory = dirname(path);
|
||||
|
||||
if (file.path != path)
|
||||
CPLog.warn("Sanity check (file path): " + file.path + " vs. " + path);
|
||||
|
||||
if (file.bundle)
|
||||
{
|
||||
var bundleDirectory = dirname(file.bundle.path);
|
||||
|
||||
if (!bundles[file.bundle.path])
|
||||
bundles[file.bundle.path] = file.bundle;
|
||||
|
||||
if (bundleDirectory != directory)
|
||||
CPLog.warn("Sanity check (directory path): " + directory + " vs. " + bundleDirectory);
|
||||
|
||||
// if it's in a .sj
|
||||
var dict = file.bundle.info,
|
||||
replacedFiles = [dict objectForKey:"CPBundleReplacedFiles"];
|
||||
if (replacedFiles && [replacedFiles containsObject:filename])
|
||||
{
|
||||
var staticPath = bundleDirectory + "/" + [dict objectForKey:"CPBundleExecutable"];
|
||||
if (!outputFiles[staticPath])
|
||||
{
|
||||
outputFiles[staticPath] = [];
|
||||
outputFiles[staticPath].push("@STATIC;1.0;");
|
||||
}
|
||||
outputFiles[staticPath].push("p;");
|
||||
outputFiles[staticPath].push(filename.length+";");
|
||||
outputFiles[staticPath].push(filename);
|
||||
|
||||
for (var i = 0; i < file.fragments.length; i++)
|
||||
{
|
||||
if (file.fragments[i].type & FRAGMENT_CODE)
|
||||
{
|
||||
outputFiles[staticPath].push("c;");
|
||||
outputFiles[staticPath].push(file.fragments[i].info.length+";");
|
||||
outputFiles[staticPath].push(file.fragments[i].info);
|
||||
}
|
||||
else if (file.fragments[i].type & FRAGMENT_FILE)
|
||||
{
|
||||
var ignoreFragment = false;
|
||||
if (file.fragments[i].conditionallyIgnore)
|
||||
{
|
||||
var importPath = findImportInObjjFiles(scope, file.fragments[i]);
|
||||
if (!importPath || !requiredFiles[importPath])
|
||||
{
|
||||
ignoreFragment = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignoreFragment)
|
||||
{
|
||||
if (file.fragments[i].type & FRAGMENT_LOCAL)
|
||||
{
|
||||
var relativePath = pathRelativeTo(file.fragments[i].info, directory)
|
||||
|
||||
outputFiles[staticPath].push("i;");
|
||||
outputFiles[staticPath].push(relativePath.length+";");
|
||||
outputFiles[staticPath].push(relativePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputFiles[staticPath].push("I;");
|
||||
outputFiles[staticPath].push(file.fragments[i].info.length+";");
|
||||
outputFiles[staticPath].push(file.fragments[i].info);
|
||||
}
|
||||
}
|
||||
else
|
||||
CPLog.warn("Ignoring import fragment " + file.fragments[i].info + " in " + path);
|
||||
}
|
||||
else
|
||||
CPLog.error("Unknown fragment type");
|
||||
}
|
||||
}
|
||||
// always output individual .j files
|
||||
else
|
||||
{
|
||||
outputFiles[path] = file.contents;
|
||||
}
|
||||
}
|
||||
else
|
||||
CPLog.warn("No bundle for " + path)
|
||||
}
|
||||
|
||||
// phase 3.5: fix bundle plists
|
||||
for (var path in bundles)
|
||||
{
|
||||
var bundle = bundles[path];
|
||||
CPLog.info("Bundle: " + path);
|
||||
|
||||
var dict = file.bundle.info,
|
||||
replacedFiles = [dict objectForKey:"CPBundleReplacedFiles"];
|
||||
if (replacedFiles)
|
||||
{
|
||||
for (var i = 0; i < replacedFiles.length; i++)
|
||||
{
|
||||
if (!requiredFiles[replacedFiles[i]])
|
||||
{
|
||||
CPLog.info("Removing: " + replacedFiles[i]);
|
||||
replacedFiles.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputFiles[path] = CPPropertyListCreateXMLData(bundle.info).string;
|
||||
}
|
||||
}
|
||||
|
||||
// phase 4: copy everything and write out the new files
|
||||
|
||||
var rootDirectoryFile = new Packages.java.io.File(rootDirectory),
|
||||
outputDirectoryFile = new Packages.java.io.File(outputDirectory);
|
||||
|
||||
// FIXME: intelligently copy only what we need (Resources directories?)
|
||||
copyDirectory(rootDirectoryFile, outputDirectoryFile, optimizePNG);
|
||||
|
||||
for (var path in outputFiles)
|
||||
{
|
||||
var file = new java.io.File(outputDirectoryFile, pathRelativeTo(path, rootDirectory));
|
||||
|
||||
var parent = file.getParentFile();
|
||||
if (!parent.exists())
|
||||
{
|
||||
CPLog.warn(parent + " doesn't exist, creating directories.");
|
||||
parent.mkdirs();
|
||||
}
|
||||
|
||||
CPLog.info("Writing out " + file);
|
||||
|
||||
var writer = new java.io.BufferedWriter(new java.io.FileWriter(file));
|
||||
|
||||
if (typeof outputFiles[path] == "string")
|
||||
writer.write(outputFiles[path]);
|
||||
else
|
||||
writer.write(outputFiles[path].join(""));
|
||||
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper Utilities
|
||||
|
||||
// TODO: moved elsewhere?
|
||||
|
||||
function copyDirectory(src, dst, optimizePNG)
|
||||
{
|
||||
CPLog.trace("Copying directory " + src);
|
||||
|
||||
dst.mkdirs();
|
||||
|
||||
var files = src.listFiles();
|
||||
for (var i = 0; i < files.length; i++)
|
||||
{
|
||||
if (files[i].isFile())
|
||||
copyFile(files[i], new Packages.java.io.File(dst, files[i].getName()), optimizePNG);
|
||||
else if (files[i].isDirectory())
|
||||
copyDirectory(files[i], new Packages.java.io.File(dst, files[i].getName()), optimizePNG);
|
||||
}
|
||||
}
|
||||
|
||||
function copyFile(src, dst, optimizePNG)
|
||||
{
|
||||
if (optimizePNG && (/.png$/).test(src.getName()))
|
||||
{
|
||||
CPLog.warn("Optimizing .png " + src);
|
||||
exec(["pngcrush", "-rem", "alla", "-reduce", /*"-brute",*/ src.getAbsolutePath(), dst.getAbsolutePath()]);
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLog.trace("Copying file " + src);
|
||||
|
||||
var input = (new Packages.java.io.FileInputStream(src)).getChannel(),
|
||||
output = (new Packages.java.io.FileOutputStream(dst)).getChannel();
|
||||
|
||||
input.transferTo(0, input.size(), output);
|
||||
|
||||
input.close();
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
|
||||
function dirname(path)
|
||||
{
|
||||
return path.substring(0, path.lastIndexOf("/"));
|
||||
}
|
||||
|
||||
function basename(path)
|
||||
{
|
||||
return path.substring(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
function absolutePath(path)
|
||||
{
|
||||
return String((new Packages.java.io.File(path)).getCanonicalPath());
|
||||
}
|
||||
|
||||
function pathRelativeTo(target, relativeTo)
|
||||
{
|
||||
var components = [],
|
||||
targetParts = target.split("/"),
|
||||
relativeParts = relativeTo ? relativeTo.split("/") : [];
|
||||
|
||||
var i = 0;
|
||||
while (i < targetParts.length)
|
||||
{
|
||||
if (targetParts[i] != relativeParts[i])
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
for (var j = i; j < relativeParts.length; j++)
|
||||
components.push("..");
|
||||
|
||||
for (var j = i; j < targetParts.length; j++)
|
||||
components.push(targetParts[j]);
|
||||
|
||||
var result = components.join("/");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function exec()
|
||||
{
|
||||
var printOutput = false;
|
||||
|
||||
var runtime = Packages.java.lang.Runtime.getRuntime()
|
||||
var p = runtime.exec.apply(runtime, arguments);
|
||||
|
||||
var stdout = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getInputStream())),
|
||||
stdoutString = "",
|
||||
stderr = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getErrorStream())),
|
||||
stderrString = "";
|
||||
|
||||
var done = false;
|
||||
while (!done)
|
||||
{
|
||||
done = true;
|
||||
if (s = stdout.readLine())
|
||||
{
|
||||
stdoutString += s;
|
||||
if (printOutput)
|
||||
CPLog.info("exec: " + s);
|
||||
done = false;
|
||||
}
|
||||
if (s = stderr.readLine())
|
||||
{
|
||||
stderrString += s;
|
||||
//if (printOutput)
|
||||
CPLog.warn("exec: " + s);
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
|
||||
var code = p.waitFor();
|
||||
|
||||
return { code : code, stdout : stdoutString, stderr : stderrString };
|
||||
}
|
||||
|
||||
function outputTransformer(os, document, encoding, standalone)
|
||||
{
|
||||
var domSource = new Packages.javax.xml.transform.dom.DOMSource(document);
|
||||
var streamResult = new Packages.javax.xml.transform.stream.StreamResult(os);
|
||||
var tf = Packages.javax.xml.transform.TransformerFactory.newInstance();
|
||||
|
||||
var serializer = tf.newTransformer();
|
||||
serializer.setOutputProperty(Packages.javax.xml.transform.OutputKeys.VERSION, "1.0");
|
||||
serializer.setOutputProperty(Packages.javax.xml.transform.OutputKeys.INDENT, "yes");
|
||||
if (encoding)
|
||||
serializer.setOutputProperty(Packages.javax.xml.transform.OutputKeys.ENCODING, encoding);
|
||||
if (standalone)
|
||||
serializer.setOutputProperty(Packages.javax.xml.transform.OutputKeys.STANDALONE, (standalone ? "yes" : "no"));
|
||||
|
||||
serializer.transform(domSource, streamResult);
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
Reference in New Issue
Block a user