diff --git a/AppKit/Platform/DOM/CPDOMWindowBridge.j b/AppKit/Platform/DOM/CPDOMWindowBridge.j index ad5a3e51c..2d201690f 100644 --- a/AppKit/Platform/DOM/CPDOMWindowBridge.j +++ b/AppKit/Platform/DOM/CPDOMWindowBridge.j @@ -861,7 +861,7 @@ var CTRL_KEY_CODE = 17; @end var CLICK_SPACE_DELTA = 5.0, - CLICK_TIME_DELTA = document.addEventListener ? 350.0 : 1000.0; + CLICK_TIME_DELTA = (typeof document != "undefined" && document.addEventListener) ? 350.0 : 1000.0; var CPDOMEventGetClickCount = function(aComparisonEvent, aTimestamp, aLocation) { diff --git a/Tools/Utilities/js.jar b/Tools/Utilities/js.jar index 2b926505d..2f4fad90c 100644 Binary files a/Tools/Utilities/js.jar and b/Tools/Utilities/js.jar differ diff --git a/Tools/press/env.js b/Tools/press/env.js new file mode 100644 index 000000000..0466201b4 --- /dev/null +++ b/Tools/press/env.js @@ -0,0 +1,767 @@ +/* + * Pure JavaScript Browser Environment + * By John Resig + * Copyright 2008 John Resig, under the MIT License + */ + +// The window Object +var window = this; + +(function(){ + + // Browser Navigator + + window.navigator = { + get userAgent(){ + return "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3"; + } + }; + + var curLocation = (new java.io.File("./")).toURL(); + + window.__defineSetter__("location", function(url){ + var xhr = new XMLHttpRequest(); + xhr.open("GET", url); + xhr.onreadystatechange = function(){ + curLocation = new java.net.URL( curLocation, url ); + window.document = xhr.responseXML; + + var event = document.createEvent(); + event.initEvent("load"); + window.dispatchEvent( event ); + }; + xhr.send(); + }); + + window.__defineGetter__("location", function(url){ + return { + get protocol(){ + return curLocation.getProtocol() + ":"; + }, + get href(){ + return curLocation.toString(); + }, + toString: function(){ + return this.href; + } + }; + }); + + // Timers + + var timers = []; + + window.setTimeout = function(fn, time){ + var num; + return num = setInterval(function(){ + fn(); + clearInterval(num); + }, time); + }; + + window.setInterval = function(fn, time){ + var num = timers.length; + + timers[num] = new java.lang.Thread(new java.lang.Runnable({ + run: function(){ + while (true){ + java.lang.Thread.currentThread().sleep(time); + fn(); + } + } + })); + + timers[num].start(); + + return num; + }; + + window.clearInterval = function(num){ + if ( timers[num] ) { + timers[num].stop(); + delete timers[num]; + } + }; + + // Window Events + + var events = [{}]; + + window.addEventListener = function(type, fn){ + if ( !this.uuid || this == window ) { + this.uuid = events.length; + events[this.uuid] = {}; + } + + if ( !events[this.uuid][type] ) + events[this.uuid][type] = []; + + if ( events[this.uuid][type].indexOf( fn ) < 0 ) + events[this.uuid][type].push( fn ); + }; + + window.removeEventListener = function(type, fn){ + if ( !this.uuid || this == window ) { + this.uuid = events.length; + events[this.uuid] = {}; + } + + if ( !events[this.uuid][type] ) + events[this.uuid][type] = []; + + events[this.uuid][type] = + events[this.uuid][type].filter(function(f){ + return f != fn; + }); + }; + + window.dispatchEvent = function(event){ + if ( event.type ) { + if ( this.uuid && events[this.uuid][event.type] ) { + var self = this; + + events[this.uuid][event.type].forEach(function(fn){ + fn.call( self, event ); + }); + } + + if ( this["on" + event.type] ) + this["on" + event.type].call( self, event ); + } + }; + + // DOM Document + + window.DOMDocument = function(file){ + this._file = file; + this._dom = Packages.javax.xml.parsers. + DocumentBuilderFactory.newInstance() + .newDocumentBuilder().parse(file); + + if ( !obj_nodes.containsKey( this._dom ) ) + obj_nodes.put( this._dom, this ); + }; + + DOMDocument.prototype = { + get nodeType(){ + return 9; + }, + createTextNode: function(text){ + return makeNode( this._dom.createTextNode( + text.replace(/&/g, "&").replace(//g, ">")) ); + }, + createElement: function(name){ + return makeNode( this._dom.createElement(name.toLowerCase()) ); + }, + getElementsByTagName: function(name){ + return new DOMNodeList( this._dom.getElementsByTagName( + name.toLowerCase()) ); + }, + getElementsByName: function(name){ + var elems = this._dom.getElementsByTagName("*"), ret = []; + ret.item = function(i){ return this[i]; }; + ret.getLength = function(){ return this.length; }; + + for ( var i = 0; i < elems.length; i++ ) { + var elem = elems.item(i); + if ( elem.getAttribute("name") == name ) + ret.push( elem ); + } + + return new DOMNodeList( ret ); + }, + getElementById: function(id){ + var elems = this._dom.getElementsByTagName("*"); + + for ( var i = 0; i < elems.length; i++ ) { + var elem = elems.item(i); + if ( elem.getAttribute("id") == id ) + return makeNode(elem); + } + + return null; + }, + get body(){ + return this.getElementsByTagName("body")[0]; + }, + get documentElement(){ + return makeNode( this._dom.getDocumentElement() ); + }, + get ownerDocument(){ + return null; + }, + addEventListener: window.addEventListener, + removeEventListener: window.removeEventListener, + dispatchEvent: window.dispatchEvent, + get nodeName() { + return "#document"; + }, + importNode: function(node, deep){ + return makeNode( this._dom.importNode(node._dom, deep) ); + }, + toString: function(){ + return "Document" + (typeof this._file == "string" ? + ": " + this._file : ""); + }, + get innerHTML(){ + return this.documentElement.outerHTML; + }, + + get defaultView(){ + return { + getComputedStyle: function(elem){ + return { + getPropertyValue: function(prop){ + prop = prop.replace(/\-(\w)/g,function(m,c){ + return c.toUpperCase(); + }); + var val = elem.style[prop]; + + if ( prop == "opacity" && val == "" ) + val = "1"; + + return val; + } + }; + } + }; + }, + + createEvent: function(){ + return { + type: "", + initEvent: function(type){ + this.type = type; + } + }; + } + }; + + function getDocument(node){ + return obj_nodes.get(node); + } + + // DOM NodeList + + window.DOMNodeList = function(list){ + this._dom = list; + this.length = list.getLength(); + + for ( var i = 0; i < this.length; i++ ) { + var node = list.item(i); + this[i] = makeNode( node ); + } + }; + + DOMNodeList.prototype = { + toString: function(){ + return "[ " + + Array.prototype.join.call( this, ", " ) + " ]"; + }, + get outerHTML(){ + return Array.prototype.map.call( + this, function(node){return node.outerHTML;}).join(''); + } + }; + + // DOM Node + + window.DOMNode = function(node){ + this._dom = node; + }; + + DOMNode.prototype = { + get nodeType(){ + return this._dom.getNodeType(); + }, + get nodeValue(){ + return this._dom.getNodeValue(); + }, + get nodeName() { + return this._dom.getNodeName(); + }, + get childNodes(){ + return new DOMNodeList( this._dom.getChildNodes() ); + }, + cloneNode: function(deep){ + return makeNode( this._dom.cloneNode(deep) ); + }, + get ownerDocument(){ + return getDocument( this._dom.ownerDocument ); + }, + get documentElement(){ + return makeNode( this._dom.documentElement ); + }, + get parentNode() { + return makeNode( this._dom.getParentNode() ); + }, + get nextSibling() { + return makeNode( this._dom.getNextSibling() ); + }, + get previousSibling() { + return makeNode( this._dom.getPreviousSibling() ); + }, + toString: function(){ + return '"' + this.nodeValue + '"'; + }, + get outerHTML(){ + return this.nodeValue; + } + }; + + window.DOMComment = function(node){ + this._dom = node; + }; + + DOMComment.prototype = extend(new DOMNode(), { + get nodeType(){ + return 8; + }, + get outerHTML(){ + return ""; + } + }); + + // DOM Element + + window.DOMElement = function(elem){ + this._dom = elem; + this.style = { + get opacity(){ return this._opacity; }, + set opacity(val){ this._opacity = val + ""; } + }; + + // Load CSS info + var styles = (this.getAttribute("style") || "").split(/\s*;\s*/); + + for ( var i = 0; i < styles.length; i++ ) { + var style = styles[i].split(/\s*:\s*/); + if ( style.length == 2 ) + this.style[ style[0] ] = style[1]; + } + + if ( this.nodeName == "FORM" ) { + this.__defineGetter__("elements", function(){ + return this.getElementsByTagName("*"); + }); + + this.__defineGetter__("length", function(){ + var elems = this.elements; + for ( var i = 0; i < elems.length; i++ ) { + this[i] = elems[i]; + } + + return elems.length; + }); + } + + if ( this.nodeName == "SELECT" ) { + this.__defineGetter__("options", function(){ + return this.getElementsByTagName("option"); + }); + } + + this.defaultValue = this.value; + }; + + DOMElement.prototype = extend( new DOMNode(), { + get nodeName(){ + return this.tagName; + }, + get tagName(){ + return this._dom.getTagName().toUpperCase(); + }, + toString: function(){ + return "<" + this.tagName + (this.id ? "#" + this.id : "" ) + ">"; + }, + get outerHTML(){ + var ret = "<" + this.tagName, attr = this.attributes; + + for ( var i in attr ) + ret += " " + i + "='" + attr[i] + "'"; + + if ( this.childNodes.length || this.nodeName == "SCRIPT" ) + ret += ">" + this.childNodes.outerHTML + + ""; + else + ret += "/>"; + + return ret; + }, + + get attributes(){ + var attr = {}, attrs = this._dom.getAttributes(); + + for ( var i = 0; i < attrs.getLength(); i++ ) + attr[ attrs.item(i).nodeName ] = attrs.item(i).nodeValue; + + return attr; + }, + + get innerHTML(){ + return this.childNodes.outerHTML; + }, + set innerHTML(html){ + html = html.replace(/<\/?([A-Z]+)/g, function(m){ + return m.toLowerCase(); + }).replace(/ /g, " "); + + var nodes = this.ownerDocument.importNode( + new DOMDocument( new java.io.ByteArrayInputStream( + (new java.lang.String("" + html + "")) + .getBytes("UTF8"))).documentElement, true).childNodes; + + while (this.firstChild) + this.removeChild( this.firstChild ); + + for ( var i = 0; i < nodes.length; i++ ) + this.appendChild( nodes[i] ); + }, + + get textContent(){ + return nav(this.childNodes); + + function nav(nodes){ + var str = ""; + for ( var i = 0; i < nodes.length; i++ ) + if ( nodes[i].nodeType == 3 ) + str += nodes[i].nodeValue; + else if ( nodes[i].nodeType == 1 ) + str += nav(nodes[i].childNodes); + return str; + } + }, + set textContent(text){ + while (this.firstChild) + this.removeChild( this.firstChild ); + this.appendChild( this.ownerDocument.createTextNode(text)); + }, + + style: {}, + clientHeight: 0, + clientWidth: 0, + offsetHeight: 0, + offsetWidth: 0, + + get disabled() { + var val = this.getAttribute("disabled"); + return val != "false" && !!val; + }, + set disabled(val) { return this.setAttribute("disabled",val); }, + + get checked() { + var val = this.getAttribute("checked"); + return val != "false" && !!val; + }, + set checked(val) { return this.setAttribute("checked",val); }, + + get selected() { + if ( !this._selectDone ) { + this._selectDone = true; + + if ( this.nodeName == "OPTION" && !this.parentNode.getAttribute("multiple") ) { + var opt = this.parentNode.getElementsByTagName("option"); + + if ( this == opt[0] ) { + var select = true; + + for ( var i = 1; i < opt.length; i++ ) + if ( opt[i].selected ) { + select = false; + break; + } + + if ( select ) + this.selected = true; + } + } + } + + var val = this.getAttribute("selected"); + return val != "false" && !!val; + }, + set selected(val) { return this.setAttribute("selected",val); }, + + get className() { return this.getAttribute("class") || ""; }, + set className(val) { + return this.setAttribute("class", + val.replace(/(^\s*|\s*$)/g,"")); + }, + + get type() { return this.getAttribute("type") || ""; }, + set type(val) { return this.setAttribute("type",val); }, + + get defaultValue() { return this.getAttribute("defaultValue") || ""; }, + set defaultValue(val) { return this.setAttribute("defaultValue",val); }, + + get value() { return this.getAttribute("value") || ""; }, + set value(val) { return this.setAttribute("value",val); }, + + get src() { return this.getAttribute("src") || ""; }, + set src(val) { return this.setAttribute("src",val); }, + + get id() { return this.getAttribute("id") || ""; }, + set id(val) { return this.setAttribute("id",val); }, + + getAttribute: function(name){ + return this._dom.hasAttribute(name) ? + new String( this._dom.getAttribute(name) ) : + null; + }, + setAttribute: function(name,value){ + this._dom.setAttribute(name,value); + }, + removeAttribute: function(name){ + this._dom.removeAttribute(name); + }, + + get childNodes(){ + return new DOMNodeList( this._dom.getChildNodes() ); + }, + get firstChild(){ + return makeNode( this._dom.getFirstChild() ); + }, + get lastChild(){ + return makeNode( this._dom.getLastChild() ); + }, + appendChild: function(node){ + this._dom.appendChild( node._dom ); + }, + insertBefore: function(node,before){ + this._dom.insertBefore( node._dom, before ? before._dom : before ); + + execScripts( node ); + + function execScripts( node ) { + if ( node.nodeName == "SCRIPT" ) { + if ( !node.getAttribute("src") ) { + eval.call( window, node.textContent ); + } + } else { + var scripts = node.getElementsByTagName("script"); + for ( var i = 0; i < scripts.length; i++ ) { + execScripts( node ); + } + } + } + }, + removeChild: function(node){ + this._dom.removeChild( node._dom ); + }, + + getElementsByTagName: DOMDocument.prototype.getElementsByTagName, + + addEventListener: window.addEventListener, + removeEventListener: window.removeEventListener, + dispatchEvent: window.dispatchEvent, + + click: function(){ + var event = document.createEvent(); + event.initEvent("click"); + this.dispatchEvent(event); + }, + submit: function(){ + var event = document.createEvent(); + event.initEvent("submit"); + this.dispatchEvent(event); + }, + focus: function(){ + var event = document.createEvent(); + event.initEvent("focus"); + this.dispatchEvent(event); + }, + blur: function(){ + var event = document.createEvent(); + event.initEvent("blur"); + this.dispatchEvent(event); + }, + get contentWindow(){ + return this.nodeName == "IFRAME" ? { + document: this.contentDocument + } : null; + }, + get contentDocument(){ + if ( this.nodeName == "IFRAME" ) { + if ( !this._doc ) + this._doc = new DOMDocument( + new java.io.ByteArrayInputStream((new java.lang.String( + "")) + .getBytes("UTF8"))); + return this._doc; + } else + return null; + } + }); + + // Helper method for extending one object with another + + function extend(a,b) { + for ( var i in b ) { + var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i); + + if ( g || s ) { + if ( g ) + a.__defineGetter__(i, g); + if ( s ) + a.__defineSetter__(i, s); + } else + a[i] = b[i]; + } + return a; + } + + // Helper method for generating the right + // DOM objects based upon the type + + var obj_nodes = new java.util.HashMap(); + + function makeNode(node){ + if ( node ) { + if ( !obj_nodes.containsKey( node ) ) + obj_nodes.put( node, node.getNodeType() == 1? + new DOMElement( node ) : + node.getNodeType() == 8 ? + new DOMComment( node ) : + new DOMNode( node ) ); + + return obj_nodes.get(node); + } else + return null; + } + + // XMLHttpRequest + // Originally implemented by Yehuda Katz + + window.XMLHttpRequest = function(){ + this.headers = {}; + this.responseHeaders = {}; + }; + + XMLHttpRequest.prototype = { + open: function(method, url, async, user, password){ + this.readyState = 1; + if (async) + this.async = true; + this.method = method || "GET"; + this.url = url; + this.onreadystatechange(); + }, + setRequestHeader: function(header, value){ + this.headers[header] = value; + }, + getResponseHeader: function(header){ }, + send: function(data){ + var self = this; + + function makeRequest(){ + var url = new java.net.URL(curLocation, self.url); + + if ( url.getProtocol() == "file" ) { + if ( self.method == "PUT" ) { + var out = new java.io.FileWriter( + new java.io.File( new java.net.URI( url.toString() ) ) ), + text = new java.lang.String( data || "" ); + + out.write( text, 0, text.length() ); + out.flush(); + out.close(); + } else if ( self.method == "DELETE" ) { + var file = new java.io.File( new java.net.URI( url.toString() ) ); + file["delete"](); + } else { + var connection = url.openConnection(); + connection.connect(); + handleResponse(); + } + } else { + var connection = url.openConnection(); + + connection.setRequestMethod( self.method ); + + // Add headers to Java connection + for (var header in self.headers) + connection.addRequestProperty(header, self.headers[header]); + + connection.connect(); + + // Stick the response headers into responseHeaders + for (var i = 0; ; i++) { + var headerName = connection.getHeaderFieldKey(i); + var headerValue = connection.getHeaderField(i); + if (!headerName && !headerValue) break; + if (headerName) + self.responseHeaders[headerName] = headerValue; + } + + handleResponse(); + } + + function handleResponse(){ + self.readyState = 4; + self.status = parseInt(connection.responseCode) || undefined; + self.statusText = connection.responseMessage || ""; + + var stream = new java.io.InputStreamReader(connection.getInputStream()), + buffer = new java.io.BufferedReader(stream), line; + + while ((line = buffer.readLine()) != null) + self.responseText += line; + + self.responseXML = null; + + if ( self.responseText.match(/^\s* " + scope.objj_included_files[i]); + + 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(value); }; + + // 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, "", 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(); +} \ No newline at end of file diff --git a/Tools/press/press.j b/Tools/press/press.j new file mode 100644 index 000000000..c4fad198d --- /dev/null +++ b/Tools/press/press.j @@ -0,0 +1,264 @@ +import + +import "objj-analysis-tools.j" + +CPLogRegister(CPLogPrint); + + +function main() +{ + if (args.length < 2) + { + print("Usage: press input_base_file.j output_directory"); + return; + } + + var rootPath = args[0], + sourceDirectory = dirname(rootPath) || ".", + outputDirectory = args[1]; + + var cx = Packages.org.mozilla.javascript.Context.enter(), + scope = makeObjjScope(cx); + + var frameworks = rootPath.substring(0, rootPath.lastIndexOf("/")+1) + "Frameworks/"; + scope.OBJJ_INCLUDE_PATHS = [frameworks]; + + CPLog.info("OBJJ_INCLUDE_PATHS="+scope.OBJJ_INCLUDE_PATHS); + + // phase 1: get global defines + + var globals = findGlobalDefines(cx, scope, rootPath); + + // coalesce the results + var dependencies = coalesceGlobalDefines(globals); + + // phase 2: walk the import tree to determine exactly which files need to be included + + var requiredFiles = {}; + + if (scope.objj_files[rootPath]) + { + var context = { + scope : scope, + dependencies : dependencies, + processedFiles : {}, + ignoreFrameworkImports : true, + importCallback : function(importing, imported) { + requiredFiles[imported] = true; + }, + referenceCallback : function(referencing, referenced) { + requiredFiles[referenced] = true; + } + } + + traverseDependencies(context, scope.objj_files[rootPath]); + + 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_IMPORT) + { + 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 if (file.fragments[i].type & FRAGMENT_FILE) + { + 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 sourceDirectoryFile = new Packages.java.io.File(sourceDirectory), + outputDirectoryFile = new Packages.java.io.File(outputDirectory); + + copyDirectory(sourceDirectoryFile, outputDirectoryFile); + + for (var path in outputFiles) + { + var file = new java.io.File(outputDirectoryFile, path); + + 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) +{ + 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())); + else if (files[i].isDirectory()) + copyDirectory(files[i], new Packages.java.io.File(dst, files[i].getName())); + } +} + +function copyFile(src, dst) +{ + 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 pathRelativeTo(target, relativeTo) +{ + var components = [], + targetParts = target.split("/"), + relativeParts = 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]); + + return components.join("/"); +} + +main();