├── example ├── lib │ ├── sibling.js │ └── index.js ├── package.json └── nodules ├── cli.js ├── test └── test-load.js ├── package.json ├── lib ├── nodules-utils │ ├── process.js │ ├── system.js │ ├── node-delay.js │ ├── http-client.js │ ├── rhino-fs.js │ ├── rhino-delay.js │ ├── fs-promise.js │ ├── rhino-http-client.js │ ├── node-http-client.js │ ├── node-fs.js │ ├── lazy-array.js │ ├── unzip.js │ ├── promise.js │ └── inflate.js └── nodules.js └── README.md /example/lib/sibling.js: -------------------------------------------------------------------------------- 1 | exports.foo = 444; -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // just starts nodules 4 | require("./lib/nodules").useLocal().runAsMain(process.argv[2]); -------------------------------------------------------------------------------- /test/test-load.js: -------------------------------------------------------------------------------- 1 | require("../lib/nodules").ensure("http://github.com/kriszyp/nodules/raw/master/lib/nodules-utils/fs-promise.js", function(require){ 2 | var validate = require("./fs-promise"); 3 | require("sys").puts("loaded fs-promise: "+ validate); 4 | }); 5 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-modules-example", 3 | "mappings": { 4 | "commonjs-utils": "jar:http://github.com/kriszyp/commonjs-utils/zipball/master!/lib/" 5 | }, 6 | "version": "0.1", 7 | "description": "An example package for web-modules", 8 | "author": "John Doe" 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodules", 3 | "author": "Kris Zyp", 4 | "dependencies": [], 5 | "contributors": [], 6 | "keywords": [ 7 | "module", 8 | "URI", 9 | "async", 10 | "reload" 11 | ], 12 | "engines": {"node":">=0.1.90"}, 13 | "githubName": "nodules", 14 | "type": "zip", 15 | "main":"./lib/nodules", 16 | "version":"0.2.4", 17 | "bin" : { 18 | "nodules" : "./cli.js" 19 | }, 20 | "location": "http://github.com/kriszyp/nodules/zipball/master" 21 | } -------------------------------------------------------------------------------- /lib/nodules-utils/process.js: -------------------------------------------------------------------------------- 1 | if(typeof console !== "undefined"){ 2 | exports.print = function(){ 3 | console.log.apply(console, arguments); 4 | } 5 | } 6 | else{ 7 | try{ 8 | exports.print = require("sys").puts; 9 | }catch(e){ 10 | exports.print = print; 11 | } 12 | } 13 | if(typeof process !== "undefined"){ 14 | exports.args = process.argv; 15 | exports.env = process.env; 16 | } 17 | else{ 18 | exports.args = require("system").args; 19 | exports.env = require("system").env; 20 | } 21 | -------------------------------------------------------------------------------- /lib/nodules-utils/system.js: -------------------------------------------------------------------------------- 1 | if(typeof console !== "undefined"){ 2 | exports.print = function(){ 3 | console.log.apply(console, arguments); 4 | } 5 | } 6 | else{ 7 | try{ 8 | exports.print = require("sys").puts; 9 | }catch(e){ 10 | exports.print = print; 11 | } 12 | } 13 | if(typeof process !== "undefined"){ 14 | exports.args = process.argv; 15 | exports.env = process.env; 16 | } 17 | else{ 18 | exports.args = require("system").args; 19 | exports.env = require("system").env; 20 | } 21 | -------------------------------------------------------------------------------- /example/nodules: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is an example startup script. Simply edit the line below to provide 3 | # the correct path to the nodules.js file and put this script on your path. You 4 | # can then start nodules from anywhere. 5 | 6 | # set the NODULES_PATH where downloaded modules are stored first 7 | export NODULES_PATH=/path/to/downloaded-modules 8 | 9 | node /path/to/lib/nodules.js $1 10 | 11 | # I also like to include --debug in the arguments for automatically running with listening for a debugger enabled. -------------------------------------------------------------------------------- /example/lib/index.js: -------------------------------------------------------------------------------- 1 | var validate = require("commonjs-utils/json-schema").validate, 2 | sys = require("sys"); 3 | validate({},{}); 4 | 5 | sys.puts("We can access modules within the local package using top level ids, and from " + 6 | "here we can use a relative reference: " + (require("./sibling") == require("sibling"))); 7 | 8 | require.reloadable(function(){ 9 | var foo = require("./sibling").foo; 10 | sys.puts("The latest value from sibling is " + foo); 11 | setTimeout(function(){ 12 | // wait around for a bit to see if sibling changes 13 | }, 10000); 14 | }); 15 | 16 | sys.puts(require("promise")); 17 | if(require.main == module){ 18 | sys.puts("This indicates that we are the main/entry module"); 19 | } 20 | -------------------------------------------------------------------------------- /lib/nodules-utils/node-delay.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Provides time-based promise-returning delay and schedule functions 3 | */ 4 | var defer = require("./promise").defer, 5 | LazyArray = require("./lazy-array").LazyArray; 6 | // returns a promise that is fulfilled after the given number of milliseconds 7 | exports.delay = function(ms){ 8 | var deferred = defer(); 9 | setTimeout(deferred.resolve, ms); 10 | return deferred.promise; 11 | }; 12 | // returns a lazy array that iterates one every given number of milliseconds 13 | exports.schedule = function(ms){ 14 | var callbacks = []; 15 | setInterval(function(){ 16 | callbacks.forEach(function(callback){ 17 | if(callback()){ 18 | callbacks.splice(callbacks.indexOf(callback), 1); 19 | } 20 | }); 21 | }, ms); 22 | return LazyArray({ 23 | some: function(callback){ 24 | callbacks.push(callback); 25 | } 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /lib/nodules-utils/http-client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * HTTP Client using the JSGI standard objects 3 | */ 4 | var defer = require("./promise").defer; 5 | 6 | exports.request = function(request){ 7 | var xhr = new XMLHttpRequest(); 8 | xhr.open(request.method || "GET", 9 | request.uri || // allow request.uri to shortcut creating a URL from all the various parts 10 | (request.scheme + "://" + request.serverName + ":" + request.serverPort + request.pathInfo + (request.queryString ? '?' + request.queryString : '')), true); 11 | for(var i in request.headers){ 12 | xhr.setRequestHeader(i, request.headers[i]); 13 | } 14 | var deferred = defer(); 15 | var response; 16 | var lastUpdate; 17 | xhr.onreadystatechange = function(){ 18 | if(xhr.readyState == 4 || xhr.readyState == 3){ 19 | if(!response){ 20 | response = { 21 | body: [xhr.responseText], 22 | status: xhr.status, 23 | headers: {} 24 | }; 25 | lastUpdate = xhr.responseText.length; 26 | var headers = xhr.getAllResponseHeaders(); 27 | headers = headers.split(/\n/); 28 | for(var i = 0; i < headers.length; i++){ 29 | var nameValue = headers[i].split(": ", 2); 30 | if(nameValue){ 31 | var name = nameValue[0]; 32 | response.headers[name.toLowerCase()] = xhr.getResponseHeader(name); 33 | } 34 | } 35 | } 36 | else{ 37 | response.body = [xhr.responseText]; 38 | lastUpdate = xhr.responseText.length; 39 | } 40 | if(xhr.readyState == 4){ 41 | deferred.resolve(response); 42 | } 43 | else{ 44 | deferred.progress(response); 45 | } 46 | } 47 | } 48 | xhr.send(request.body && request.body.toString()); 49 | return deferred.promise; 50 | } 51 | -------------------------------------------------------------------------------- /lib/nodules-utils/rhino-fs.js: -------------------------------------------------------------------------------- 1 | var File = require("file"), 2 | LazyArray = require("./lazy-array").LazyArray, 3 | defer = require("./promise").defer; 4 | for(var i in File){ 5 | exports[i] = File[i]; 6 | } 7 | exports.readFileSync = File.read; 8 | exports.writeFileSync = function(path, source, encoding){ 9 | return File.write(path, source, encoding == "binary" ? "b" : ""); 10 | }; 11 | exports.mkdirSync = File.mkdir; 12 | exports.readdir = exports.list; 13 | exports.stat = exports.statSync = function(path) { 14 | try{ 15 | var stat = File.stat.apply(null, arguments); 16 | }catch(e){ 17 | var deferred = defer(); 18 | deferred.reject(e); 19 | return deferred.promise; 20 | } 21 | stat.isFile = function() { 22 | return File.isFile(path); 23 | } 24 | if(!stat.mtime){ 25 | var deferred = defer(); 26 | deferred.reject("File not found"); 27 | return deferred.promise; 28 | } 29 | return stat; 30 | } 31 | 32 | exports.makeTree = File.mkdirs; 33 | exports.makeDirectory = File.mkdir; 34 | 35 | exports.open = function(){ 36 | var file = File.open.apply(this, arguments); 37 | var array = LazyArray({ 38 | some: function(callback){ 39 | while(true){ 40 | var buffer = file.read(4096); 41 | if(buffer.length <= 0){ 42 | return; 43 | } 44 | if(callback(buffer)){ 45 | return; 46 | } 47 | } 48 | } 49 | }); 50 | for(var i in array){ 51 | file[i] = array[i]; 52 | } 53 | return file; 54 | } 55 | exports.createWriteStream = function(path, options) { 56 | options = options || {}; 57 | options.flags = options.flags || "w"; 58 | var flags = options.flags || "w", 59 | f = File.open(path, flags); 60 | return { 61 | writable: true, 62 | write: function() { 63 | var deferred = defer(); 64 | try { 65 | f.write.apply(this, arguments); 66 | f.flush(); 67 | } 68 | catch (e) { 69 | return stream.writable = false; 70 | } 71 | deferred.resolve(); 72 | return deferred.promise; 73 | }, 74 | end: f.close, 75 | destroy: f.close 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/nodules-utils/rhino-delay.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Provides time-based promise-returning delay and schedule functions for Rhino 3 | */ 4 | var defer = require("./promise").defer, 5 | LazyArray = require("./lazy-array").LazyArray; 6 | // returns a promise that is fulfilled after the given number of milliseconds 7 | exports.delay = function(ms){ 8 | var deferred = defer(); 9 | _scheduleTimeout(deferred.resolve, ms, false); 10 | return deferred.promise; 11 | }; 12 | // returns a lazy array that iterates one every given number of milliseconds 13 | exports.schedule = function(ms){ 14 | var callbacks = []; 15 | _scheduleTimeout(function(){ 16 | callbacks.forEach(function(callback){ 17 | if(callback()){ 18 | callbacks.splice(callbacks.indexOf(callback), 1); 19 | } 20 | }); 21 | }, ms, true); 22 | return LazyArray({ 23 | some: function(callback){ 24 | callbacks.push(callback); 25 | } 26 | }); 27 | }; 28 | 29 | var nextId = 1, 30 | timeouts = {}, 31 | timer, 32 | queue; 33 | 34 | var _scheduleTimeout = function(callback, delay, repeat) 35 | { 36 | if (typeof callback == "function") 37 | var func = callback; 38 | else if (typeof callback == "string") 39 | var func = new Function(callback); 40 | else 41 | return; 42 | 43 | var timeout = { 44 | }; 45 | var id = nextId++; 46 | timeouts[id] = timeout; 47 | 48 | timer = timer || new java.util.Timer("JavaScript timer thread", true); 49 | queue = queue || require("event-loop"); 50 | var lastFinished = true; 51 | var task = timeout.task = new java.util.TimerTask({ 52 | run: function(){ 53 | if(lastFinished){ 54 | lastFinished = false; 55 | queue.enqueue(function(){ 56 | if(!timeout.cancelled){ // check to make sure it wasn't enqueued and then later cancelled 57 | try{ 58 | func(); 59 | }finally{ 60 | lastFinished = true; 61 | } 62 | } 63 | }); 64 | } 65 | } 66 | }); 67 | delay = Math.floor(delay); 68 | 69 | if(repeat){ 70 | timer.schedule(task, delay, delay); 71 | } 72 | else{ 73 | timer.schedule(task, delay); 74 | } 75 | 76 | return id; 77 | } 78 | 79 | 80 | -------------------------------------------------------------------------------- /lib/nodules-utils/fs-promise.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Node fs module that returns promises 3 | */ 4 | 5 | var fs = require("fs"), 6 | LazyArray = require("./lazy-array").LazyArray, 7 | Buffer = require("buffer").Buffer, 8 | defer = require("./promise").defer; 9 | convertNodeAsyncFunction = require("./promise").convertNodeAsyncFunction; 10 | 11 | // convert all the non-sync functions 12 | for (var i in fs) { 13 | if (i.match(/Sync$/) || i.match(/watch/)) { 14 | exports[i] = fs[i]; 15 | } 16 | else{ 17 | exports[i] = convertNodeAsyncFunction(fs[i]); 18 | } 19 | } 20 | function File(fd){ 21 | var file = new LazyArray({ 22 | some: function(callback){ 23 | var deferred = defer(); 24 | function readAndSend(){ 25 | var buffer = new Buffer(4096); 26 | fs.read(fd, buffer, 0, 4096, null, function(err, bytesRead){ 27 | if(err){ 28 | deferred.reject(err); 29 | return; 30 | } 31 | if (bytesRead === 0){ 32 | fs.close(fd); 33 | deferred.resolve(); 34 | } 35 | else { 36 | var result; 37 | if(bytesRead < 4096){ 38 | result = callback(buffer.slice(0, bytesRead)); 39 | }else{ 40 | result = callback(buffer); 41 | } 42 | if(result){ 43 | deferred.resolve(); 44 | }else{ 45 | readAndSend(fd); 46 | } 47 | } 48 | }); 49 | } 50 | readAndSend(); 51 | return deferred.promise; 52 | }, 53 | length: 0 54 | }); 55 | file.fd = fd; 56 | file.write = function(contents){ 57 | 58 | } 59 | return file; 60 | } 61 | File.prototype = LazyArray.prototype; 62 | 63 | var nodeRead = exports.read; 64 | exports.read = function(path, options){ 65 | if(path instanceof File){ 66 | arguments[0] = path.fd; 67 | return nodeRead.apply(this, arguments); 68 | }else{ 69 | return exports.readFileSync(path, options).toString((options && options.charset) || "utf8"); 70 | } 71 | }; 72 | 73 | var nodeWrite = exports.write; 74 | exports.write = function(path, contents, options){ 75 | if(path instanceof File){ 76 | arguments[0] = path.fd; 77 | return nodeWrite.apply(this, arguments); 78 | }else{ 79 | return exports.writeFileSync(path, contents, options); 80 | } 81 | }; 82 | var nodeClose = exports.close; 83 | exports.close = function(path, contents, options){ 84 | if(path instanceof File){ 85 | arguments[0] = path.fd; 86 | } 87 | return nodeClose.apply(this, arguments); 88 | }; 89 | 90 | 91 | nodeOpen = exports.open; 92 | exports.open = function(){ 93 | return nodeOpen.apply(this, arguments).then(File); 94 | }; -------------------------------------------------------------------------------- /lib/nodules-utils/rhino-http-client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * HTTP Client using the JSGI standard objects 3 | */ 4 | 5 | var defer = require("./promise").defer, 6 | IO = require("io").IO, 7 | LazyArray = require("./lazy-array").LazyArray; 8 | 9 | // configurable proxy server setting, defaults to http_proxy env var 10 | exports.proxyServer = require("./process").env.http_proxy; 11 | 12 | exports.request = function(request){ 13 | var url = new java.net.URL(request.url), 14 | connection = url.openConnection(), 15 | method = request.method || "GET", 16 | is = null, 17 | promised = true; 18 | 19 | if (request.jsgi && "async" in request.jsgi) promised = request.jsgi.async; 20 | 21 | for (var header in this.headers) { 22 | var value = this.headers[header]; 23 | connection.addRequestProperty(String(header), String(value)); 24 | } 25 | connection.setDoInput(true); 26 | connection.setRequestMethod(method); 27 | if (request.body && typeof request.body.forEach === "function") { 28 | connection.setDoOutput(true); 29 | var writer = new java.io.OutputStreamWriter(connection.getOutputStream()); 30 | request.body.forEach(function(chunk) { 31 | writer.write(chunk); 32 | writer.flush(); 33 | }); 34 | } 35 | if (typeof writer !== "undefined") writer.close(); 36 | 37 | try { 38 | connection.connect(); 39 | is = connection.getInputStream(); 40 | } 41 | catch (e) { 42 | is = connection.getErrorStream(); 43 | } 44 | 45 | var status = Number(connection.getResponseCode()), 46 | headers = {}; 47 | for (var i = 0;; i++) { 48 | var key = connection.getHeaderFieldKey(i), 49 | value = connection.getHeaderField(i); 50 | if (!key && !value) 51 | break; 52 | // returns the HTTP status code with no key, ignore it. 53 | if (key) { 54 | key = String(key).toLowerCase(); 55 | value = String(value); 56 | if (headers[key]) { 57 | if (!Array.isArray(headers[key])) headers[key] = [headers[key]]; 58 | headers[key].push(value); 59 | } 60 | else { 61 | headers[key] = value; 62 | } 63 | } 64 | } 65 | 66 | // TODO bytestrings? 67 | var reader = new IO(is), 68 | deferred = defer(), 69 | bodyDeferred = defer(), 70 | response = { 71 | status: status, 72 | headers: headers 73 | } 74 | 75 | response.body = LazyArray({ 76 | some: function(callback) { 77 | try { 78 | var data; 79 | while((data = reader.read()).length > 0){ 80 | callback(data); 81 | } 82 | reader.close(); 83 | bodyDeferred.resolve(); 84 | } 85 | catch (e) { 86 | bodyDeferred.reject(e); 87 | reader.close(); 88 | } 89 | // FIXME why doesn't this work?! 90 | if (promised) return bodyDeferred.promise; 91 | } 92 | }); 93 | 94 | deferred.resolve(response); 95 | if (promised) return deferred.promise; 96 | return response; 97 | }; -------------------------------------------------------------------------------- /lib/nodules-utils/node-http-client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * HTTP Client using the JSGI standard objects 3 | */ 4 | var defer = require("./promise").defer, 5 | when = require("./promise").when, 6 | LazyArray = require("./lazy-array").LazyArray, 7 | http = require("http"), 8 | https = require("https"), 9 | parse = require("url").parse; 10 | 11 | // configurable proxy server setting, defaults to http_proxy env var 12 | exports.proxyServer = require("./system").env.http_proxy; 13 | 14 | exports.request = function(request){ 15 | if(request.url){ 16 | var parsed = parse(request.url); 17 | for(var i in parsed){ 18 | request[i] = parsed[i]; 19 | } 20 | } 21 | var deferred = defer(); 22 | if(exports.proxyServer){ 23 | request.pathname = request.url; 24 | var proxySettings = parse(exports.proxyServer); 25 | request.port = proxySettings.port; 26 | request.protocol = proxySettings.protocol; 27 | request.hostname = proxySettings.hostname; 28 | } 29 | var secure = request.protocol.indexOf("s") > -1; 30 | request.port = request.port || (secure ? 443 : 80); 31 | request.headers = request.headers || {host: request.host}; 32 | request.host = request.hostname; 33 | request.method = request.method || "GET"; 34 | request.path = request.pathname || request.pathInfo || ""; 35 | if (request.queryString) { 36 | request.path += "?"+request.queryString; 37 | } 38 | var timedOut; 39 | 40 | var req = (secure ? https : http).request(request); 41 | req.addListener("response", function (response){ 42 | if(timedOut){ 43 | return; 44 | } 45 | response.status = response.statusCode; 46 | var sendData = function(block){ 47 | buffer.push(block); 48 | }; 49 | var buffer = []; 50 | var bodyDeferred = defer(); 51 | var body = response.body = LazyArray({ 52 | some: function(callback){ 53 | buffer.forEach(callback); 54 | sendData = callback; 55 | return bodyDeferred.promise; 56 | } 57 | }); 58 | if(request.encoding){ 59 | response.setEncoding(request.encoding); 60 | } 61 | 62 | response.addListener("data", function (chunk) { 63 | sendData(chunk); 64 | }); 65 | response.addListener("end", function(){ 66 | bodyDeferred.resolve(); 67 | }); 68 | response.addListener("error", function(error){ 69 | bodyDeferred.reject(error); 70 | }); 71 | deferred.resolve(response); 72 | clearTimeout(timeout); 73 | }); 74 | var timeout = setTimeout(function(){ 75 | timedOut = true; 76 | deferred.reject(new Error("Timeout")); 77 | }, 20000); 78 | req.addListener("error", function(error){ 79 | deferred.reject(error); 80 | }); 81 | req.addListener("timeout", function(error){ 82 | deferred.reject(error); 83 | }); 84 | req.addListener("close", function(error){ 85 | deferred.reject(error); 86 | }); 87 | if(request.body){ 88 | return when(request.body.forEach(function(block){ 89 | req.write(block); 90 | }), function(){ 91 | req.end(); 92 | return deferred.promise; 93 | }); 94 | } 95 | req.end(); 96 | return deferred.promise; 97 | }; 98 | -------------------------------------------------------------------------------- /lib/nodules-utils/node-fs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Node fs module that returns promises 3 | */ 4 | 5 | var fs = require("fs"), 6 | LazyArray = require("./lazy-array").LazyArray, 7 | Buffer = require("buffer").Buffer, 8 | defer = require("./promise").defer; 9 | convertNodeAsyncFunction = require("./promise").convertNodeAsyncFunction; 10 | 11 | // convert all the non-sync functions 12 | for (var i in fs) { 13 | if (i.match(/Sync$/) || i.match(/watch/)) { 14 | exports[i] = fs[i]; 15 | } 16 | else{ 17 | exports[i] = convertNodeAsyncFunction(fs[i]); 18 | } 19 | } 20 | function File(fd){ 21 | var file = new LazyArray({ 22 | some: function(callback){ 23 | var deferred = defer(); 24 | function readAndSend(){ 25 | var buffer = new Buffer(4096); 26 | fs.read(fd, buffer, 0, 4096, null, function(err, bytesRead){ 27 | if(err){ 28 | deferred.reject(err); 29 | return; 30 | } 31 | if (bytesRead === 0){ 32 | fs.close(fd); 33 | deferred.resolve(); 34 | } 35 | else { 36 | var result; 37 | if(bytesRead < 4096){ 38 | result = callback(buffer.slice(0, bytesRead)); 39 | }else{ 40 | result = callback(buffer); 41 | } 42 | if(result){ 43 | deferred.resolve(); 44 | }else{ 45 | readAndSend(fd); 46 | } 47 | } 48 | }); 49 | } 50 | readAndSend(); 51 | return deferred.promise; 52 | }, 53 | length: 0 54 | }); 55 | file.fd = fd; 56 | file.write = function(contents){ 57 | 58 | } 59 | return file; 60 | } 61 | File.prototype = LazyArray.prototype; 62 | 63 | var nodeRead = exports.read; 64 | exports.read = function(path, options){ 65 | if(path instanceof File){ 66 | arguments[0] = path.fd; 67 | return nodeRead.apply(this, arguments); 68 | }else{ 69 | return exports.readFileSync(path, options).toString((options && options.charset) || "utf8"); 70 | } 71 | }; 72 | 73 | var nodeWrite = exports.write; 74 | exports.write = function(path, contents, options){ 75 | if(path instanceof File){ 76 | arguments[0] = path.fd; 77 | return nodeWrite.apply(this, arguments); 78 | }else{ 79 | return exports.writeFileSync(path, contents, options); 80 | } 81 | }; 82 | var nodeClose = exports.close; 83 | exports.close = function(path, contents, options){ 84 | if(path instanceof File){ 85 | arguments[0] = path.fd; 86 | } 87 | return nodeClose.apply(this, arguments); 88 | }; 89 | 90 | 91 | nodeOpen = exports.open; 92 | exports.open = function(){ 93 | return nodeOpen.apply(this, arguments).then(File); 94 | }; 95 | 96 | exports.makeDirectory = exports.mkdirSync; 97 | 98 | exports.makeTree = function(path){ 99 | var index = path.lastIndexOf('/'); 100 | if(index === -1){ 101 | return; 102 | } 103 | var path = path.substring(0, index); 104 | try{ 105 | fs.statSync(path); 106 | }catch(e){ 107 | exports.makeTree(path); 108 | fs.mkdirSync(path, 0777); 109 | } 110 | }; 111 | exports.absolute = exports.realpathSync; -------------------------------------------------------------------------------- /lib/nodules-utils/lazy-array.js: -------------------------------------------------------------------------------- 1 | try{ 2 | var when = require("./promise").when; 3 | }catch(e){ 4 | when = function(value, callback){ 5 | return callback(value); 6 | } 7 | } 8 | exports.LazyArray = function(hasSomeAndLength){ 9 | return new SomeWrapper(hasSomeAndLength); 10 | }; 11 | exports.first = function(array){ 12 | return exports.get(array, 0); 13 | }; 14 | exports.last = function(array){ 15 | return exports.get(array, array.length-1); 16 | }; 17 | exports.get = function(array, index){ 18 | var result, i = 0; 19 | return when(array.some(function(item){ 20 | if(i == index){ 21 | result = item; 22 | return true; 23 | } 24 | i++; 25 | }), 26 | function(){ 27 | return result; 28 | }); 29 | }; 30 | 31 | 32 | var testProto = {}; 33 | var testProto2 = testProto.__proto__ = testProto2; 34 | var mutableProto = testProto.__proto__ === testProto2; 35 | function SomeWrapper(hasSomeAndLength){ 36 | if(mutableProto){ 37 | hasSomeAndLength.source = hasSomeAndLength; 38 | hasSomeAndLength.__proto__ = SomeWrapper.prototype; 39 | return hasSomeAndLength; 40 | } 41 | this.source = hasSomeAndLength; 42 | if(hasSomeAndLength.length){ 43 | this.length = hasSomeAndLength.length; 44 | } 45 | this.totalCount = hasSomeAndLength.totalCount; 46 | } 47 | exports.LazyArray.prototype = SomeWrapper.prototype = []; 48 | SomeWrapper.prototype.some = function(callback){ 49 | this.source.some(callback); 50 | } 51 | SomeWrapper.prototype.filter = function(fn, thisObj){ 52 | var result = []; 53 | return when(this.source.some(function(item){ 54 | if(fn.call(thisObj, item)){ 55 | results.push(item); 56 | } 57 | }), function(){ 58 | return results; 59 | }); 60 | }; 61 | 62 | SomeWrapper.prototype.every = function(fn, thisObj){ 63 | return when(this.source.some(function(item){ 64 | if(!fn.call(thisObj, item)){ 65 | return true; 66 | } 67 | }), function(result){return !result;}); 68 | }; 69 | SomeWrapper.prototype.forEach= function(fn, thisObj){ 70 | return this.source.some(function(item){ 71 | fn.call(thisObj, item); 72 | }); 73 | }; 74 | SomeWrapper.prototype.concat = function(someOther){ 75 | var source = this.source; 76 | return new SomeWrapper({ 77 | length : source.length + someOther.length, 78 | some : function(fn,thisObj){ 79 | return when(source.some(fn,thisObj), function(result){ 80 | return result || someOther.some(fn,thisObj); 81 | }); 82 | } 83 | }); 84 | }; 85 | SomeWrapper.prototype.map = function(mapFn, mapThisObj){ 86 | var source = this.source; 87 | return new SomeWrapper({ 88 | length : source.length, 89 | some : function(fn,thisObj){ 90 | return source.some(function(item){ 91 | return fn.call(thisObj, mapFn.call(mapThisObj, item)); 92 | }); 93 | } 94 | }); 95 | }; 96 | SomeWrapper.prototype.toRealArray= function(mapFn, mapThisObj){ 97 | var array = []; 98 | return when(this.source.some(function(item){ 99 | array.push(item); 100 | }), function(){ 101 | return array; 102 | }); 103 | }; 104 | SomeWrapper.prototype.join = function(){ 105 | var args = arguments; 106 | return when(this.toRealArray(), function(realArray){ 107 | return Array.prototype.join.apply(realArray, args); 108 | }); 109 | }; 110 | SomeWrapper.prototype.sort = function(){ 111 | var args = arguments; 112 | return when(this.toRealArray(), function(realArray){ 113 | return Array.prototype.sort.apply(realArray, args); 114 | }); 115 | }; 116 | SomeWrapper.prototype.reverse = function(){ 117 | var args = arguments; 118 | return when(this.toRealArray(), function(realArray){ 119 | return Array.prototype.reverse.apply(realArray, args); 120 | }); 121 | }; 122 | SomeWrapper.prototype.get = SomeWrapper.prototype.item = function(index){ 123 | var result, i = 0; 124 | return when(this.source.some(function(item){ 125 | if(i == index){ 126 | result = item; 127 | return true; 128 | } 129 | i++; 130 | }), function(){ 131 | return result; 132 | }); 133 | }; 134 | 135 | 136 | SomeWrapper.prototype.toSource = function(){ 137 | var serializedParts = []; 138 | return when(this.source.some(function(item){ 139 | serializedParts.push(item && item.toSource()); 140 | }), function(){ 141 | return '[' + serializedParts.join(",") + ']'; 142 | }); 143 | }; 144 | SomeWrapper.prototype.toJSON = function(){ 145 | var loadedParts = []; 146 | return when(this.source.some(function(item){ 147 | loadedParts.push(item); 148 | }), function(){ 149 | return loadedParts; 150 | }); 151 | }; 152 | -------------------------------------------------------------------------------- /lib/nodules-utils/unzip.js: -------------------------------------------------------------------------------- 1 | if(typeof Packages !== "undefined"){ 2 | var ByteString = require("binary").ByteString; 3 | exports.Unzip = function(fileContents){ 4 | var zipStream = new Packages.java.util.zip.ZipInputStream(new Packages.java.io.ByteArrayInputStream(fileContents._bytes)); 5 | return { 6 | readEntries: function(){ 7 | var entry; 8 | this.entries = []; 9 | var bufferSize = 4096; 10 | while((entry = zipStream.getNextEntry())){ 11 | var size = 0, buffer = Packages.java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 0); 12 | do{ 13 | var nextBuffer = Packages.java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, bufferSize); 14 | var bytesRead = zipStream.read(nextBuffer, 0, nextBuffer.length); 15 | if(bytesRead > 0){ 16 | var newBuffer = Packages.java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, bytesRead + size); 17 | Packages.java.lang.System.arraycopy(buffer, 0, newBuffer, 0, size); 18 | Packages.java.lang.System.arraycopy(nextBuffer, 0, newBuffer, size, bytesRead); 19 | buffer = newBuffer; 20 | size += bytesRead; 21 | } 22 | }while(bytesRead > 0); 23 | this.entries.push({ 24 | compressionMethod: 0, 25 | fileName: entry.getName(), 26 | data: new ByteString(buffer, 0, buffer.length) 27 | }); 28 | } 29 | }, 30 | isZipFile: function(){ 31 | return true; 32 | } 33 | } 34 | } 35 | } 36 | else{ 37 | var JSUnzip = function (fileContents) { 38 | this.fileContents = new JSUnzip.BigEndianBinaryStream(fileContents); 39 | } 40 | exports.Unzip = JSUnzip; 41 | JSUnzip.MAGIC_NUMBER = 0x04034b50; 42 | 43 | JSUnzip.prototype = { 44 | readEntries: function () { 45 | if (!this.isZipFile()) { 46 | throw new Error("File is not a Zip file."); 47 | } 48 | 49 | this.entries = []; 50 | var e = new JSUnzip.ZipEntry(this.fileContents); 51 | while (typeof(e.data) === "string") { 52 | this.entries.push(e); 53 | e = new JSUnzip.ZipEntry(this.fileContents); 54 | } 55 | }, 56 | 57 | isZipFile: function () { 58 | return this.fileContents.getByteRangeAsNumber(0, 4) === JSUnzip.MAGIC_NUMBER; 59 | } 60 | } 61 | 62 | JSUnzip.ZipEntry = function (binaryStream) { 63 | this.signature = binaryStream.getNextBytesAsNumber(4); 64 | if (this.signature !== JSUnzip.MAGIC_NUMBER) { 65 | return; 66 | } 67 | 68 | this.versionNeeded = binaryStream.getNextBytesAsNumber(2); 69 | this.bitFlag = binaryStream.getNextBytesAsNumber(2); 70 | this.compressionMethod = binaryStream.getNextBytesAsNumber(2); 71 | this.timeBlob = binaryStream.getNextBytesAsNumber(4); 72 | 73 | if (this.isEncrypted()) { 74 | throw "File contains encrypted entry. Not supported."; 75 | } 76 | 77 | if (this.isUsingUtf8()) { 78 | throw "File is using UTF8. Not supported."; 79 | } 80 | 81 | if (this.isUsingBit3TrailingDataDescriptor()) { 82 | throw "File is using bit 3 trailing data descriptor. Not supported."; 83 | } 84 | 85 | this.crc32 = binaryStream.getNextBytesAsNumber(4); 86 | this.compressedSize = binaryStream.getNextBytesAsNumber(4); 87 | this.uncompressedSize = binaryStream.getNextBytesAsNumber(4); 88 | 89 | if (this.isUsingZip64()) { 90 | throw "File is using Zip64 (4gb+ file size). Not supported"; 91 | } 92 | 93 | this.fileNameLength = binaryStream.getNextBytesAsNumber(2); 94 | this.extraFieldLength = binaryStream.getNextBytesAsNumber(2); 95 | 96 | this.fileName = binaryStream.getNextBytesAsString(this.fileNameLength); 97 | this.extra = binaryStream.getNextBytesAsString(this.extraFieldLength); 98 | this.data = binaryStream.getNextBytesAsString(this.compressedSize); 99 | } 100 | 101 | JSUnzip.ZipEntry.prototype = { 102 | isEncrypted: function () { 103 | return (this.bitFlag & 0x01) === 0x01; 104 | }, 105 | 106 | isUsingUtf8: function () { 107 | return (this.bitFlag & 0x0800) === 0x0800; 108 | }, 109 | 110 | isUsingBit3TrailingDataDescriptor: function () { 111 | return (this.bitFlag & 0x0008) === 0x0008; 112 | }, 113 | 114 | isUsingZip64: function () { 115 | this.compressedSize === 0xFFFFFFFF || 116 | this.uncompressedSize === 0xFFFFFFFF; 117 | } 118 | } 119 | 120 | JSUnzip.BigEndianBinaryStream = function (stream) { 121 | this.stream = stream; 122 | this.resetByteIndex(); 123 | } 124 | 125 | JSUnzip.BigEndianBinaryStream.prototype = { 126 | // The index of the current byte, used when we step through the byte 127 | // with getNextBytesAs*. 128 | resetByteIndex: function () { 129 | this.currentByteIndex = 0; 130 | }, 131 | 132 | // TODO: Other similar JS libs does charCodeAt(index) & 0xff. Grok 133 | // why, and do that here if neccesary. So far, I've never gotten a 134 | // char code higher than 255. 135 | getByteAt: function (index) { 136 | return this.stream.charCodeAt(index); 137 | }, 138 | 139 | getNextBytesAsNumber: function (steps) { 140 | var res = this.getByteRangeAsNumber(this.currentByteIndex, steps); 141 | this.currentByteIndex += steps; 142 | return res; 143 | }, 144 | 145 | getNextBytesAsString: function (steps) { 146 | var res = this.getByteRangeAsString(this.currentByteIndex, steps); 147 | this.currentByteIndex += steps; 148 | return res; 149 | }, 150 | 151 | // Big endian, so we're going backwards. 152 | getByteRangeAsNumber: function (index, steps) { 153 | var result = 0; 154 | var i = index + steps - 1; 155 | while (i >= index) { 156 | result = (result << 8) + this.getByteAt(i); 157 | i--; 158 | } 159 | return result; 160 | }, 161 | 162 | getByteRangeAsString: function (index, steps) { 163 | var result = ""; 164 | var max = index + steps; 165 | var i = index; 166 | while (i < max) { 167 | result += String.fromCharCode(this.getByteAt(i)); 168 | i++; 169 | } 170 | return result; 171 | } 172 | } 173 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nodules is an asynchronous module loader for Node that provides URL/HTTP-based module 2 | ids, module hot-reloading, and package based module mapping. Nodules 3 | implements the [CommonJS package.json mappings proposal](http://wiki.commonjs.org/wiki/Packages/Mappings) 4 | and automatically 5 | analyzes modules references and downloads any dependencies on first access prior to 6 | executing modules. Remotely downloaded modules are retained so they only need 7 | to be downloaded once. Nodules supports standard CommonJS modules, 8 | and CommonJS module transport format via require, require.ensure, [define](http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition), and [require.define](http://wiki.commonjs.org/wiki/Modules/Transport/D). 9 | 10 | To see Nodules in action right away, go into the "example" directory, and you can start 11 | the example "package" with: 12 | 13 | node ../lib/nodules.js 14 | 15 | The first time you run this, Nodules should automatically download the dependency, and 16 | then start the application. You can test the module hot-reloading by making a change to 17 | sibling.js while it is running. 18 | 19 | The most direct way to load a module with nodules is simply to load the module 20 | from the command prompt: 21 | 22 | node /path/to/nodules.js http://somesite.com/my-module.js 23 | 24 | The require provided by nodules is transitive, so all dependencies of my-module will also 25 | be able to utilize full URLs. Nodules asynchronously downloads all the deep 26 | dependencies of a module prior to execution so that all requires can execute 27 | synchronously even though all modules are fetched asynchronously. The modules are 28 | all saved/cached locally so that future require calls (in future program executions) 29 | can always run locally. 30 | 31 | Naturally, it is easier to start nodules with a script, create a script with 32 | using the script from example/nodules as a template, and you can simply do: 33 | 34 | nodules module-to-load 35 | 36 | Packages 37 | ======== 38 | 39 | For any type of real development, it is recommended that you use packages rather 40 | than unorganized individual modules. Nodules provides an elegant mechanism for 41 | working with packages based on package.json mappings. A package is a directory/file 42 | structure for a set of modules, module configuration, and other resources. An example 43 | can be found in the nodules's "example" directory. If you run nodules from within 44 | a package, it will automatically read the "package.json" from the current working directory 45 | for module configuration and id mapping, use the "lib" as one of the default paths for looking modules, 46 | and execute the "lib/index.js" file if it exists. The package.json's mappings can contain 47 | mappings to URIs so that you don't have to use URIs directly in your require calls in 48 | your modules. For example, your package.json could define this mapping to map the 49 | foo namespace to the modules from a package archive available from somesite.com: 50 | 51 | package.json 52 | { 53 | "name":"my-project", 54 | "mappings": { 55 | "foo": "http://somesite.com/foo.zip" 56 | } 57 | } 58 | 59 | We could then define our index.js file using that mapping: 60 | 61 | lib/index.js: 62 | var Something = require("foo/bar").Something; // The module from http://somesite.com/foo/lib/bar.js 63 | Something(); 64 | 65 | Now we can run our package by simply starting nodules from inside this directory 66 | (with package.json): 67 | 68 | nodules 69 | 70 | Mappings 71 | -------- 72 | 73 | Nodules supports referening package zip file which is the recommended mechanism for referencing packages: 74 | For example: 75 | 76 | "mappings": { 77 | "commonjs-utils": "http://github.com/kriszyp/commonjs-utils/zipball/master" 78 | } 79 | 80 | When the target ends with a slash, the mapping will only match module ids in require statements 81 | where the mapping is the first term in a path, so this would match something of the form: 82 | 83 | require(""commonjs-utils/lazy-array"); 84 | 85 | You can also map directly to individual modules by specifying the full URL with an extension 86 | (and Nodules support the jar: scheme). For example: 87 | 88 | "lazy-array": "jar:http://github.com/kriszyp/commonjs-utils/zipball/master!/lib/lazy-array.js" 89 | 90 | This will only match the exact module id of require("lazy-array") (not require("lazy-array/...")). 91 | 92 | Module Reloading 93 | ================ 94 | 95 | Another critical aspect of productive development is module reloading so you don't 96 | have to constantly restart your VM. To use reloading, you can wrap your reloadable code 97 | in a require.reloadable function. This function takes a callback 98 | that is called whenever any loaded modules change (including when it is first called). 99 | For example: 100 | 101 | require.reloadable(function(){ 102 | // Load the app and assign to "app" when started and for each reload 103 | app = require("./my-app.js"); 104 | }); 105 | // Don't re-execute http server initiation for each reload, should only load once 106 | http.createServer(function(request, response){ 107 | app(request, response); 108 | }).listen(80); 109 | 110 | Nodules does intelligent dependency tracking so that when a file changes, the appropriate 111 | modules are reloaded. All the modules that depend on the modified module are reloaded to 112 | ensure correct references, but modules without depedency on the modified module are not 113 | reloaded. This enabled optimal reloading performance while ensuring the proper references 114 | to objects are flushed for consistent behavior. 115 | 116 | Module return values 117 | ==================== 118 | 119 | Nodules also supports modules that return a value or switch the exports. This 120 | very useful for situations where it is desirable for a module to provide a single 121 | function or constructor. One can create a module that returns a function like this: 122 | return function(){ 123 | ... 124 | }; 125 | 126 | Or 127 | exports = function(){ 128 | ... 129 | }; 130 | 131 | 132 | 133 | Using Nodules Programmatically 134 | ============================== 135 | 136 | You can still use nodules programmatically without having to start Node with 137 | nodules as the starting script. You can use Nodules's ensure function to 138 | asynchronously load an Nodules's entry module: 139 | 140 | require("nodules").ensure("http://somesite.com/foo", function(require){ 141 | require("./foo"); 142 | }); 143 | 144 | 145 | Where foo.js could have: 146 | 147 | var someModule = require("http://othersite.com/path/to/module"); 148 | 149 | For "normal" top-level ids, require will default to the system's require implementation, 150 | so modules can still do: 151 | 152 | var fs = require("fs"); 153 | 154 | Nodules Local Cache 155 | =================== 156 | 157 | Nodules downloads any necessary dependencies and stores them locally. By 158 | default these files will be downloaded to a directory structure rooted in the current 159 | working directory (under "downloaded-modules"). However, the location of the local directory of downloaded packages/modules 160 | can be defined with the NODULES_PATH environment variable. It is generally 161 | recommended that you define the NODULES_PATH variable (to an absolute path) 162 | so that the same set of cached/downloaded packages/modules can be reused from 163 | different working directories. 164 | 165 | It is perfectly valid and reasonable to edit files from the locally 166 | downloaded file set within this path. By default URLs are mapped to the file 167 | system by converting each part of the URL to a path, but this makes heavily 168 | nested paths. To make it easier to edit and work with 169 | your own packages, you can define a paths.json file in the NODULES_PATH 170 | directory that defines how URLs are mapped to the local file system. For example, 171 | this makes a good paths.json for directing your git projects to your own 172 | projects directory: 173 | 174 | { 175 | "(jar:)?http://github.com/my-name/([^\/]+)/zipball/[^\/]+!?" : "/projects/$2" 176 | } 177 | 178 | (URLs that don't match will be saved using the default mapping mechanism.) 179 | 180 | More package.json Configurations 181 | ================================ 182 | Engine Specific Overlay 183 | ----------------------- 184 | 185 | CommonJS packages provides a means for creating engine-specific overlays to define 186 | alternate configurations for other engines. For example, one could define an overlay in 187 | package.json: 188 | { 189 | "overlay":{ 190 | "node": { 191 | "file": "fs" 192 | } 193 | } 194 | } 195 | 196 | Compiler 197 | -------- 198 | 199 | We can also define a compiler to be used on sources prior to execution. This is 200 | more efficient than using a global extension matching like registerExtension since 201 | it only applies to the package that defines the compiler rather than being global. In 202 | your package.json you can define a compiler to use (this is how you would use CoffeeScript): 203 | 204 | { 205 | "compiler": { 206 | "module": "jar:http://github.com/jashkenas/coffee-script/zipball/master!/lib/coffee-script.js", 207 | "function": "compile" 208 | }, 209 | "extension": ".coffee", 210 | ... 211 | } 212 | 213 | The "module" property is required, and the "function" property is optional and defaults to "compile". 214 | 215 | Proxy Settings 216 | -------------- 217 | 218 | If your machine is behind a proxy, Nodules will need to go through the proxy for HTTP downloads. Nodules will 219 | read the "http_proxy" environmental variable to determine what proxy it needs to route requests through. 220 | 221 | Nodules provided top level modules 222 | ---------------------------------- 223 | 224 | Nodules provides several top level modules for modules loaded with Nodules, including "promise" (promise library), 225 | "system" (based on CommonJS module), "fs-promise" (promise based fs module), and 226 | "nodules" (the nodules module itself). 227 | 228 | Larger Example 229 | -------------- 230 | 231 | You can download and run the [Persevere example wiki application](http://github.com/kriszyp/persevere-example-wiki/) with Nodules to see a more complex use of dependencies. 232 | 233 | License 234 | ======= 235 | 236 | Copyright (c) 2010, The Dojo Foundation All Rights Reserved. 237 | Nodules is a sub-project of Persevere (www.persvr.org) and is thus available 238 | under either the terms of the modified BSD license or the Academic Free License 239 | version 2.1. As a recipient of nodules, you may choose which license to receive 240 | this code under. 241 | 242 | Dev Progress 243 | ============ 244 | 245 | Currently alpha level, issues: 246 | URI redirection needs to properly change module ids 247 | More unit tests -------------------------------------------------------------------------------- /lib/nodules-utils/promise.js: -------------------------------------------------------------------------------- 1 | 2 | // Kris Zyp 3 | 4 | // this is based on the CommonJS spec for promises: 5 | // http://wiki.commonjs.org/wiki/Promises 6 | // Includes convenience functions for promises, much of this is taken from Tyler Close's ref_send 7 | // and Kris Kowal's work on promises. 8 | // // MIT License 9 | 10 | // A typical usage: 11 | // A default Promise constructor can be used to create a self-resolving deferred/promise: 12 | // var Promise = require("promise").Promise; 13 | // var promise = new Promise(); 14 | // asyncOperation(function(){ 15 | // Promise.resolve("succesful result"); 16 | // }); 17 | // promise -> given to the consumer 18 | // 19 | // A consumer can use the promise 20 | // promise.then(function(result){ 21 | // ... when the action is complete this is executed ... 22 | // }, 23 | // function(error){ 24 | // ... executed when the promise fails 25 | // }); 26 | // 27 | // Alternately, a provider can create a deferred and resolve it when it completes an action. 28 | // The deferred object a promise object that provides a separation of consumer and producer to protect 29 | // promises from being fulfilled by untrusted code. 30 | // var defer = require("promise").defer; 31 | // var deferred = defer(); 32 | // asyncOperation(function(){ 33 | // deferred.resolve("succesful result"); 34 | // }); 35 | // deferred.promise -> given to the consumer 36 | // 37 | // Another way that a consumer can use the promise (using promise.then is also allowed) 38 | // var when = require("promise").when; 39 | // when(promise,function(result){ 40 | // ... when the action is complete this is executed ... 41 | // }, 42 | // function(error){ 43 | // ... executed when the promise fails 44 | // }); 45 | 46 | exports.errorTimeout = 100; 47 | var freeze = Object.freeze || function(){}; 48 | 49 | /** 50 | * Default constructor that creates a self-resolving Promise. Not all promise implementations 51 | * need to use this constructor. 52 | */ 53 | var Promise = function(canceller){ 54 | }; 55 | 56 | /** 57 | * Promise implementations must provide a "then" function. 58 | */ 59 | Promise.prototype.then = function(resolvedCallback, errorCallback, progressCallback){ 60 | throw new TypeError("The Promise base class is abstract, this function must be implemented by the Promise implementation"); 61 | }; 62 | 63 | /** 64 | * If an implementation of a promise supports a concurrency model that allows 65 | * execution to block until the promise is resolved, the wait function may be 66 | * added. 67 | */ 68 | /** 69 | * If an implementation of a promise can be cancelled, it may add this function 70 | */ 71 | // Promise.prototype.cancel = function(){ 72 | // }; 73 | 74 | Promise.prototype.get = function(propertyName){ 75 | return this.then(function(value){ 76 | return value[propertyName]; 77 | }); 78 | }; 79 | 80 | Promise.prototype.put = function(propertyName, value){ 81 | return this.then(function(object){ 82 | return object[propertyName] = value; 83 | }); 84 | }; 85 | 86 | Promise.prototype.call = function(functionName /*, args */){ 87 | return this.then(function(value){ 88 | return value[propertyName].apply(value, Array.prototype.slice.call(arguments, 1)); 89 | }); 90 | }; 91 | 92 | /** Dojo/NodeJS methods*/ 93 | Promise.prototype.addCallback = function(callback){ 94 | return this.then(callback); 95 | }; 96 | 97 | Promise.prototype.addErrback = function(errback){ 98 | return this.then(function(){}, errback); 99 | }; 100 | 101 | /*Dojo methods*/ 102 | Promise.prototype.addBoth = function(callback){ 103 | return this.then(callback, callback); 104 | }; 105 | 106 | Promise.prototype.addCallbacks = function(callback, errback){ 107 | return this.then(callback, errback); 108 | }; 109 | 110 | /*NodeJS method*/ 111 | Promise.prototype.wait = function(){ 112 | return exports.wait(this); 113 | }; 114 | 115 | Deferred.prototype = Promise.prototype; 116 | // A deferred provides an API for creating and resolving a promise. 117 | exports.Promise = exports.Deferred = exports.defer = defer; 118 | function defer(){ 119 | return new Deferred(); 120 | } 121 | 122 | var contextHandler = exports.contextHandler = {}; 123 | 124 | function Deferred(canceller){ 125 | var result, finished, isError, waiting = [], handled; 126 | var promise = this.promise = new Promise(); 127 | var currentContextHandler = contextHandler.getHandler && contextHandler.getHandler(); 128 | 129 | function notifyAll(value){ 130 | if(finished){ 131 | throw new Error("This deferred has already been resolved"); 132 | } 133 | result = value; 134 | finished = true; 135 | for(var i = 0; i < waiting.length; i++){ 136 | notify(waiting[i]); 137 | } 138 | } 139 | function notify(listener){ 140 | var func = (isError ? listener.error : listener.resolved); 141 | if(func){ 142 | handled = true; 143 | if(currentContextHandler){ 144 | currentContextHandler.resume(); 145 | } 146 | try{ 147 | var newResult = func(result); 148 | if(newResult && typeof newResult.then === "function"){ 149 | newResult.then(listener.deferred.resolve, listener.deferred.reject); 150 | return; 151 | } 152 | listener.deferred.resolve(newResult); 153 | } 154 | catch(e){ 155 | listener.deferred.reject(e); 156 | } 157 | finally{ 158 | if(currentContextHandler){ 159 | currentContextHandler.suspend(); 160 | } 161 | } 162 | } 163 | else{ 164 | if(isError){ 165 | if (listener.deferred.reject(result, true)) { 166 | handled = true; 167 | } 168 | } 169 | else{ 170 | listener.deferred.resolve.apply(listener.deferred, result); 171 | } 172 | } 173 | } 174 | // calling resolve will resolve the promise 175 | this.resolve = this.callback = this.emitSuccess = function(value){ 176 | notifyAll(value); 177 | }; 178 | 179 | // calling error will indicate that the promise failed 180 | var reject = this.reject = this.errback = this.emitError = function(error, dontThrow){ 181 | isError = true; 182 | notifyAll(error); 183 | if (!dontThrow && typeof setTimeout !== "undefined") { 184 | setTimeout(function () { 185 | if (!handled) { 186 | throw error; 187 | } 188 | }, exports.errorTimeout); 189 | } 190 | return handled; 191 | }; 192 | 193 | // call progress to provide updates on the progress on the completion of the promise 194 | this.progress = function(update){ 195 | for(var i = 0; i < waiting.length; i++){ 196 | var progress = waiting[i].progress; 197 | progress && progress(update); 198 | } 199 | } 200 | // provide the implementation of the promise 201 | this.then = promise.then = function(resolvedCallback, errorCallback, progressCallback){ 202 | var returnDeferred = new Deferred(promise.cancel); 203 | var listener = {resolved: resolvedCallback, error: errorCallback, progress: progressCallback, deferred: returnDeferred}; 204 | if(finished){ 205 | notify(listener); 206 | } 207 | else{ 208 | waiting.push(listener); 209 | } 210 | return returnDeferred.promise; 211 | }; 212 | var timeout; 213 | if(typeof setTimeout !== "undefined") { 214 | this.timeout = function (ms) { 215 | if (ms === undefined) { 216 | return timeout; 217 | } 218 | timeout = ms; 219 | setTimeout(function () { 220 | if (!finished) { 221 | if (promise.cancel) { 222 | promise.cancel(new Error("timeout")); 223 | } 224 | else { 225 | reject(new Error("timeout")); 226 | } 227 | } 228 | }, ms); 229 | return promise; 230 | }; 231 | } 232 | 233 | if(canceller){ 234 | this.cancel = promise.cancel = function(){ 235 | var error = canceller(); 236 | if(!(error instanceof Error)){ 237 | error = new Error(error); 238 | } 239 | reject(error); 240 | } 241 | } 242 | freeze(promise); 243 | }; 244 | 245 | function perform(value, async, sync){ 246 | try{ 247 | if(value && typeof value.then === "function"){ 248 | value = async(value); 249 | } 250 | else{ 251 | value = sync(value); 252 | } 253 | if(value && typeof value.then === "function"){ 254 | return value; 255 | } 256 | var deferred = new Deferred(); 257 | deferred.resolve(value); 258 | return deferred.promise; 259 | }catch(e){ 260 | var deferred = new Deferred(); 261 | deferred.reject(e); 262 | return deferred.promise; 263 | } 264 | 265 | } 266 | /** 267 | * Promise manager to make it easier to consume promises 268 | */ 269 | 270 | /** 271 | * Registers an observer on a promise. 272 | * @param value promise or value to observe 273 | * @param resolvedCallback function to be called with the resolved value 274 | * @param rejectCallback function to be called with the rejection reason 275 | * @param progressCallback function to be called when progress is made 276 | * @return promise for the return value from the invoked callback 277 | */ 278 | exports.whenPromise = function(value, resolvedCallback, rejectCallback, progressCallback){ 279 | return perform(value, function(value){ 280 | return value.then(resolvedCallback, rejectCallback, progressCallback); 281 | }, 282 | function(value){ 283 | return resolvedCallback(value); 284 | }); 285 | }; 286 | /** 287 | * Registers an observer on a promise. 288 | * @param value promise or value to observe 289 | * @param resolvedCallback function to be called with the resolved value 290 | * @param rejectCallback function to be called with the rejection reason 291 | * @param progressCallback function to be called when progress is made 292 | * @return promise for the return value from the invoked callback or the value if it 293 | * is a non-promise value 294 | */ 295 | exports.when = function(value, resolvedCallback, rejectCallback, progressCallback){ 296 | if(value && typeof value.then === "function"){ 297 | return exports.whenPromise(value, resolvedCallback, rejectCallback, progressCallback); 298 | } 299 | return resolvedCallback(value); 300 | }; 301 | 302 | /** 303 | * Gets the value of a property in a future turn. 304 | * @param target promise or value for target object 305 | * @param property name of property to get 306 | * @return promise for the property value 307 | */ 308 | exports.get = function(target, property){ 309 | return perform(target, function(target){ 310 | return target.get(property); 311 | }, 312 | function(target){ 313 | return target[property] 314 | }); 315 | }; 316 | 317 | /** 318 | * Invokes a method in a future turn. 319 | * @param target promise or value for target object 320 | * @param methodName name of method to invoke 321 | * @param args array of invocation arguments 322 | * @return promise for the return value 323 | */ 324 | exports.post = function(target, methodName, args){ 325 | return perform(target, function(target){ 326 | return target.call(property, args); 327 | }, 328 | function(target){ 329 | return target[methodName].apply(target, args); 330 | }); 331 | }; 332 | 333 | /** 334 | * Sets the value of a property in a future turn. 335 | * @param target promise or value for target object 336 | * @param property name of property to set 337 | * @param value new value of property 338 | * @return promise for the return value 339 | */ 340 | exports.put = function(target, property, value){ 341 | return perform(target, function(target){ 342 | return target.put(property, value); 343 | }, 344 | function(target){ 345 | return target[property] = value; 346 | }); 347 | }; 348 | 349 | 350 | /** 351 | * Waits for the given promise to finish, blocking (and executing other events) 352 | * if necessary to wait for the promise to finish. If target is not a promise 353 | * it will return the target immediately. If the promise results in an reject, 354 | * that reject will be thrown. 355 | * @param target promise or value to wait for. 356 | * @return the value of the promise; 357 | */ 358 | exports.wait = function(target){ 359 | if(!queue){ 360 | throw new Error("Can not wait, the event-queue module is not available"); 361 | } 362 | if(target && typeof target.then === "function"){ 363 | var isFinished, isError, result; 364 | target.then(function(value){ 365 | isFinished = true; 366 | result = value; 367 | }, 368 | function(error){ 369 | isFinished = true; 370 | isError = true; 371 | result = error; 372 | }); 373 | while(!isFinished){ 374 | queue.processNextEvent(true); 375 | } 376 | if(isError){ 377 | throw result; 378 | } 379 | return result; 380 | } 381 | else{ 382 | return target; 383 | } 384 | }; 385 | 386 | 387 | 388 | /** 389 | * Takes an array of promises and returns a promise that is fulfilled once all 390 | * the promises in the array are fulfilled 391 | * @param array The array of promises 392 | * @return the promise that is fulfilled when all the array is fulfilled, resolved to the array of results 393 | */ 394 | exports.all = function(array){ 395 | var deferred = new Deferred(); 396 | if(!(array instanceof Array)){ 397 | array = Array.prototype.slice.call(arguments); 398 | } 399 | var fulfilled = 0, length = array.length; 400 | var results = []; 401 | if (length === 0) deferred.resolve(results); 402 | else { 403 | array.forEach(function(promise, index){ 404 | exports.when(promise, each, each); 405 | function each(value){ 406 | results[index] = value; 407 | fulfilled++; 408 | if(fulfilled === length){ 409 | deferred.resolve(results); 410 | } 411 | } 412 | }); 413 | } 414 | return deferred.promise; 415 | }; 416 | 417 | /** 418 | * Takes an array of promises and returns a promise that is fulfilled when the first 419 | * promise in the array of promises is fulfilled 420 | * @param array The array of promises 421 | * @return a promise that is fulfilled with the value of the value of first promise to be fulfilled 422 | */ 423 | exports.first = function(array){ 424 | var deferred = new Deferred(); 425 | if(!(array instanceof Array)){ 426 | array = Array.prototype.slice.call(arguments); 427 | } 428 | var fulfilled; 429 | array.forEach(function(promise, index){ 430 | exports.when(promise, function(value){ 431 | if (!fulfilled) { 432 | fulfilled = true; 433 | deferred.resolve(value); 434 | } 435 | }, 436 | function(error){ 437 | if (!fulfilled) { 438 | fulfilled = true; 439 | deferred.resolve(error); 440 | } 441 | }); 442 | }); 443 | return deferred.promise; 444 | }; 445 | 446 | /** 447 | * Takes an array of asynchronous functions (that return promises) and 448 | * executes them sequentially. Each funtion is called with the return value of the last function 449 | * @param array The array of function 450 | * @param startingValue The value to pass to the first function 451 | * @return the value returned from the last function 452 | */ 453 | exports.seq = function(array, startingValue){ 454 | array = array.concat(); // make a copy 455 | var deferred = new Deferred(); 456 | function next(value){ 457 | var nextAction = array.shift(); 458 | if(nextAction){ 459 | exports.when(nextAction(value), next, deferred.reject); 460 | } 461 | else { 462 | deferred.resolve(value); 463 | } 464 | } 465 | next(startingValue); 466 | return deferred.promise; 467 | }; 468 | 469 | 470 | /** 471 | * Delays for a given amount of time and then fulfills the returned promise. 472 | * @param milliseconds The number of milliseconds to delay 473 | * @return A promise that will be fulfilled after the delay 474 | */ 475 | if(typeof setTimeout !== "undefined") { 476 | exports.delay = function(milliseconds) { 477 | var deferred = new Deferred(); 478 | setTimeout(function(){ 479 | deferred.resolve(); 480 | }, milliseconds); 481 | return deferred.promise; 482 | }; 483 | } 484 | 485 | 486 | 487 | /** 488 | * Runs a function that takes a callback, but returns a Promise instead. 489 | * @param func node compatible async function which takes a callback as its last argument 490 | * @return promise for the return value from the callback from the function 491 | */ 492 | exports.execute = function(asyncFunction){ 493 | var args = Array.prototype.slice.call(arguments, 1); 494 | 495 | var deferred = new Deferred(); 496 | args.push(function(error, result){ 497 | if(error) { 498 | deferred.emitError(error); 499 | } 500 | else { 501 | if(arguments.length > 2){ 502 | // if there are multiple success values, we return an array 503 | Array.prototype.shift.call(arguments, 1); 504 | deferred.emitSuccess(arguments); 505 | } 506 | else{ 507 | deferred.emitSuccess(result); 508 | } 509 | } 510 | }); 511 | asyncFunction.apply(this, args); 512 | return deferred.promise; 513 | }; 514 | 515 | /** 516 | * Converts a Node async function to a promise returning function 517 | * @param func node compatible async function which takes a callback as its last argument 518 | * @return A function that returns a promise 519 | */ 520 | exports.convertNodeAsyncFunction = function(asyncFunction, callbackNotDeclared){ 521 | var arity = asyncFunction.length; 522 | return function(){ 523 | var deferred = new Deferred(); 524 | if(callbackNotDeclared){ 525 | arity = arguments.length + 1; 526 | } 527 | arguments.length = arity; 528 | arguments[arity - 1] = function(error, result){ 529 | if(error) { 530 | deferred.emitError(error); 531 | } 532 | else { 533 | if(arguments.length > 2){ 534 | // if there are multiple success values, we return an array 535 | Array.prototype.shift.call(arguments, 1); 536 | deferred.emitSuccess(arguments); 537 | } 538 | else{ 539 | deferred.emitSuccess(result); 540 | } 541 | } 542 | }; 543 | asyncFunction.apply(this, arguments); 544 | return deferred.promise; 545 | }; 546 | }; 547 | -------------------------------------------------------------------------------- /lib/nodules-utils/inflate.js: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 1999 Masanao Izumo 2 | * Version: 1.0.0.1 3 | * LastModified: Dec 25 1999 4 | */ 5 | 6 | 7 | exports.zipInflate = zip_inflate; 8 | /* constant parameters */ 9 | var zip_WSIZE = 32768; // Sliding Window size 10 | var zip_STORED_BLOCK = 0; 11 | var zip_STATIC_TREES = 1; 12 | var zip_DYN_TREES = 2; 13 | 14 | /* for inflate */ 15 | var zip_lbits = 9; // bits in base literal/length lookup table 16 | var zip_dbits = 6; // bits in base distance lookup table 17 | var zip_INBUFSIZ = 32768; // Input buffer size 18 | var zip_INBUF_EXTRA = 64; // Extra buffer 19 | 20 | /* variables (inflate) */ 21 | var zip_slide; 22 | var zip_wp; // current position in slide 23 | var zip_fixed_tl = null; // inflate static 24 | var zip_fixed_td; // inflate static 25 | var zip_fixed_bl, fixed_bd; // inflate static 26 | var zip_bit_buf; // bit buffer 27 | var zip_bit_len; // bits in bit buffer 28 | var zip_method; 29 | var zip_eof; 30 | var zip_copy_leng; 31 | var zip_copy_dist; 32 | var zip_tl, zip_td; // literal/length and distance decoder tables 33 | var zip_bl, zip_bd; // number of bits decoded by tl and td 34 | 35 | var zip_inflate_data; 36 | var zip_inflate_pos; 37 | 38 | 39 | /* constant tables (inflate) */ 40 | var zip_MASK_BITS = new Array( 41 | 0x0000, 42 | 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 43 | 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff); 44 | // Tables for deflate from PKZIP's appnote.txt. 45 | var zip_cplens = new Array( // Copy lengths for literal codes 257..285 46 | 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 47 | 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0); 48 | /* note: see note #13 above about the 258 in this list. */ 49 | var zip_cplext = new Array( // Extra bits for literal codes 257..285 50 | 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 51 | 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99); // 99==invalid 52 | var zip_cpdist = new Array( // Copy offsets for distance codes 0..29 53 | 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 54 | 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 55 | 8193, 12289, 16385, 24577); 56 | var zip_cpdext = new Array( // Extra bits for distance codes 57 | 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 58 | 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 59 | 12, 12, 13, 13); 60 | var zip_border = new Array( // Order of the bit length code lengths 61 | 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15); 62 | /* objects (inflate) */ 63 | 64 | function zip_HuftList() { 65 | this.next = null; 66 | this.list = null; 67 | } 68 | 69 | function zip_HuftNode() { 70 | this.e = 0; // number of extra bits or operation 71 | this.b = 0; // number of bits in this code or subcode 72 | 73 | // union 74 | this.n = 0; // literal, length base, or distance base 75 | this.t = null; // (zip_HuftNode) pointer to next level of table 76 | } 77 | 78 | function zip_HuftBuild(b, // code lengths in bits (all assumed <= BMAX) 79 | n, // number of codes (assumed <= N_MAX) 80 | s, // number of simple-valued codes (0..s-1) 81 | d, // list of base values for non-simple codes 82 | e, // list of extra bits for non-simple codes 83 | mm // maximum lookup bits 84 | ) { 85 | this.BMAX = 16; // maximum bit length of any code 86 | this.N_MAX = 288; // maximum number of codes in any set 87 | this.status = 0; // 0: success, 1: incomplete table, 2: bad input 88 | this.root = null; // (zip_HuftList) starting table 89 | this.m = 0; // maximum lookup bits, returns actual 90 | 91 | /* Given a list of code lengths and a maximum table size, make a set of 92 | tables to decode that set of codes. Return zero on success, one if 93 | the given code set is incomplete (the tables are still built in this 94 | case), two if the input is invalid (all zero length codes or an 95 | oversubscribed set of lengths), and three if not enough memory. 96 | The code with value 256 is special, and the tables are constructed 97 | so that no bits beyond that code are fetched when that code is 98 | decoded. */ 99 | { 100 | var a; // counter for codes of length k 101 | var c = new Array(this.BMAX+1); // bit length count table 102 | var el; // length of EOB code (value 256) 103 | var f; // i repeats in table every f entries 104 | var g; // maximum code length 105 | var h; // table level 106 | var i; // counter, current code 107 | var j; // counter 108 | var k; // number of bits in current code 109 | var lx = new Array(this.BMAX+1); // stack of bits per table 110 | var p; // pointer into c[], b[], or v[] 111 | var pidx; // index of p 112 | var q; // (zip_HuftNode) points to current table 113 | var r = new zip_HuftNode(); // table entry for structure assignment 114 | var u = new Array(this.BMAX); // zip_HuftNode[BMAX][] table stack 115 | var v = new Array(this.N_MAX); // values in order of bit length 116 | var w; 117 | var x = new Array(this.BMAX+1);// bit offsets, then code stack 118 | var xp; // pointer into x or c 119 | var y; // number of dummy codes added 120 | var z; // number of entries in current table 121 | var o; 122 | var tail; // (zip_HuftList) 123 | 124 | tail = this.root = null; 125 | for(i = 0; i < c.length; i++) 126 | c[i] = 0; 127 | for(i = 0; i < lx.length; i++) 128 | lx[i] = 0; 129 | for(i = 0; i < u.length; i++) 130 | u[i] = null; 131 | for(i = 0; i < v.length; i++) 132 | v[i] = 0; 133 | for(i = 0; i < x.length; i++) 134 | x[i] = 0; 135 | 136 | // Generate counts for each bit length 137 | el = n > 256 ? b[256] : this.BMAX; // set length of EOB code, if any 138 | p = b; pidx = 0; 139 | i = n; 140 | do { 141 | c[p[pidx]]++; // assume all entries <= BMAX 142 | pidx++; 143 | } while(--i > 0); 144 | if(c[0] == n) { // null input--all zero length codes 145 | this.root = null; 146 | this.m = 0; 147 | this.status = 0; 148 | return; 149 | } 150 | 151 | // Find minimum and maximum length, bound *m by those 152 | for(j = 1; j <= this.BMAX; j++) 153 | if(c[j] != 0) 154 | break; 155 | k = j; // minimum code length 156 | if(mm < j) 157 | mm = j; 158 | for(i = this.BMAX; i != 0; i--) 159 | if(c[i] != 0) 160 | break; 161 | g = i; // maximum code length 162 | if(mm > i) 163 | mm = i; 164 | 165 | // Adjust last length count to fill out codes, if needed 166 | for(y = 1 << j; j < i; j++, y <<= 1) 167 | if((y -= c[j]) < 0) { 168 | this.status = 2; // bad input: more codes than bits 169 | this.m = mm; 170 | return; 171 | } 172 | if((y -= c[i]) < 0) { 173 | this.status = 2; 174 | this.m = mm; 175 | return; 176 | } 177 | c[i] += y; 178 | 179 | // Generate starting offsets into the value table for each length 180 | x[1] = j = 0; 181 | p = c; 182 | pidx = 1; 183 | xp = 2; 184 | while(--i > 0) // note that i == g from above 185 | x[xp++] = (j += p[pidx++]); 186 | 187 | // Make a table of values in order of bit lengths 188 | p = b; pidx = 0; 189 | i = 0; 190 | do { 191 | if((j = p[pidx++]) != 0) 192 | v[x[j]++] = i; 193 | } while(++i < n); 194 | n = x[g]; // set n to length of v 195 | 196 | // Generate the Huffman codes and for each, make the table entries 197 | x[0] = i = 0; // first Huffman code is zero 198 | p = v; pidx = 0; // grab values in bit order 199 | h = -1; // no tables yet--level -1 200 | w = lx[0] = 0; // no bits decoded yet 201 | q = null; // ditto 202 | z = 0; // ditto 203 | 204 | // go through the bit lengths (k already is bits in shortest code) 205 | for(; k <= g; k++) { 206 | a = c[k]; 207 | while(a-- > 0) { 208 | // here i is the Huffman code of length k bits for value p[pidx] 209 | // make tables up to required level 210 | while(k > w + lx[1 + h]) { 211 | w += lx[1 + h]; // add bits already decoded 212 | h++; 213 | 214 | // compute minimum size table less than or equal to *m bits 215 | z = (z = g - w) > mm ? mm : z; // upper limit 216 | if((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table 217 | // too few codes for k-w bit table 218 | f -= a + 1; // deduct codes from patterns left 219 | xp = k; 220 | while(++j < z) { // try smaller tables up to z bits 221 | if((f <<= 1) <= c[++xp]) 222 | break; // enough codes to use up j bits 223 | f -= c[xp]; // else deduct codes from patterns 224 | } 225 | } 226 | if(w + j > el && w < el) 227 | j = el - w; // make EOB code end at table 228 | z = 1 << j; // table entries for j-bit table 229 | lx[1 + h] = j; // set table size in stack 230 | 231 | // allocate and link in new table 232 | q = new Array(z); 233 | for(o = 0; o < z; o++) { 234 | q[o] = new zip_HuftNode(); 235 | } 236 | 237 | if(tail == null) 238 | tail = this.root = new zip_HuftList(); 239 | else 240 | tail = tail.next = new zip_HuftList(); 241 | tail.next = null; 242 | tail.list = q; 243 | u[h] = q; // table starts after link 244 | 245 | /* connect to last table, if there is one */ 246 | if(h > 0) { 247 | x[h] = i; // save pattern for backing up 248 | r.b = lx[h]; // bits to dump before this table 249 | r.e = 16 + j; // bits in this table 250 | r.t = q; // pointer to this table 251 | j = (i & ((1 << w) - 1)) >> (w - lx[h]); 252 | u[h-1][j].e = r.e; 253 | u[h-1][j].b = r.b; 254 | u[h-1][j].n = r.n; 255 | u[h-1][j].t = r.t; 256 | } 257 | } 258 | 259 | // set up table entry in r 260 | r.b = k - w; 261 | if(pidx >= n) 262 | r.e = 99; // out of values--invalid code 263 | else if(p[pidx] < s) { 264 | r.e = (p[pidx] < 256 ? 16 : 15); // 256 is end-of-block code 265 | r.n = p[pidx++]; // simple code is just the value 266 | } else { 267 | r.e = e[p[pidx] - s]; // non-simple--look up in lists 268 | r.n = d[p[pidx++] - s]; 269 | } 270 | 271 | // fill code-like entries with r // 272 | f = 1 << (k - w); 273 | for(j = i >> w; j < z; j += f) { 274 | q[j].e = r.e; 275 | q[j].b = r.b; 276 | q[j].n = r.n; 277 | q[j].t = r.t; 278 | } 279 | 280 | // backwards increment the k-bit code i 281 | for(j = 1 << (k - 1); (i & j) != 0; j >>= 1) 282 | i ^= j; 283 | i ^= j; 284 | 285 | // backup over finished tables 286 | while((i & ((1 << w) - 1)) != x[h]) { 287 | w -= lx[h]; // don't need to update q 288 | h--; 289 | } 290 | } 291 | } 292 | 293 | /* return actual size of base table */ 294 | this.m = lx[1]; 295 | 296 | /* Return true (1) if we were given an incomplete table */ 297 | this.status = ((y != 0 && g != 1) ? 1 : 0); 298 | } /* end of constructor */ 299 | } 300 | 301 | 302 | /* routines (inflate) */ 303 | 304 | function zip_GET_BYTE() { 305 | if(zip_inflate_data.length == zip_inflate_pos) 306 | return -1; 307 | return zip_inflate_data.charCodeAt(zip_inflate_pos++) & 0xff; 308 | } 309 | 310 | function zip_NEEDBITS(n) { 311 | while(zip_bit_len < n) { 312 | zip_bit_buf |= zip_GET_BYTE() << zip_bit_len; 313 | zip_bit_len += 8; 314 | } 315 | } 316 | 317 | function zip_GETBITS(n) { 318 | return zip_bit_buf & zip_MASK_BITS[n]; 319 | } 320 | 321 | function zip_DUMPBITS(n) { 322 | zip_bit_buf >>= n; 323 | zip_bit_len -= n; 324 | } 325 | 326 | function zip_inflate_codes(buff, off, size) { 327 | /* inflate (decompress) the codes in a deflated (compressed) block. 328 | Return an error code or zero if it all goes ok. */ 329 | var e; // table entry flag/number of extra bits 330 | var t; // (zip_HuftNode) pointer to table entry 331 | var n; 332 | 333 | if(size == 0) 334 | return 0; 335 | 336 | // inflate the coded data 337 | n = 0; 338 | for(;;) { // do until end of block 339 | zip_NEEDBITS(zip_bl); 340 | t = zip_tl.list[zip_GETBITS(zip_bl)]; 341 | e = t.e; 342 | while(e > 16) { 343 | if(e == 99) 344 | return -1; 345 | zip_DUMPBITS(t.b); 346 | e -= 16; 347 | zip_NEEDBITS(e); 348 | t = t.t[zip_GETBITS(e)]; 349 | e = t.e; 350 | } 351 | zip_DUMPBITS(t.b); 352 | 353 | if(e == 16) { // then it's a literal 354 | zip_wp &= zip_WSIZE - 1; 355 | buff[off + n++] = zip_slide[zip_wp++] = t.n; 356 | if(n == size) 357 | return size; 358 | continue; 359 | } 360 | 361 | // exit if end of block 362 | if(e == 15) 363 | break; 364 | 365 | // it's an EOB or a length 366 | 367 | // get length of block to copy 368 | zip_NEEDBITS(e); 369 | zip_copy_leng = t.n + zip_GETBITS(e); 370 | zip_DUMPBITS(e); 371 | 372 | // decode distance of block to copy 373 | zip_NEEDBITS(zip_bd); 374 | t = zip_td.list[zip_GETBITS(zip_bd)]; 375 | e = t.e; 376 | 377 | while(e > 16) { 378 | if(e == 99) 379 | return -1; 380 | zip_DUMPBITS(t.b); 381 | e -= 16; 382 | zip_NEEDBITS(e); 383 | t = t.t[zip_GETBITS(e)]; 384 | e = t.e; 385 | } 386 | zip_DUMPBITS(t.b); 387 | zip_NEEDBITS(e); 388 | zip_copy_dist = zip_wp - t.n - zip_GETBITS(e); 389 | zip_DUMPBITS(e); 390 | 391 | // do the copy 392 | while(zip_copy_leng > 0 && n < size) { 393 | zip_copy_leng--; 394 | zip_copy_dist &= zip_WSIZE - 1; 395 | zip_wp &= zip_WSIZE - 1; 396 | buff[off + n++] = zip_slide[zip_wp++] 397 | = zip_slide[zip_copy_dist++]; 398 | } 399 | 400 | if(n == size) 401 | return size; 402 | } 403 | 404 | zip_method = -1; // done 405 | return n; 406 | } 407 | 408 | function zip_inflate_stored(buff, off, size) { 409 | /* "decompress" an inflated type 0 (stored) block. */ 410 | var n; 411 | 412 | // go to byte boundary 413 | n = zip_bit_len & 7; 414 | zip_DUMPBITS(n); 415 | 416 | // get the length and its complement 417 | zip_NEEDBITS(16); 418 | n = zip_GETBITS(16); 419 | zip_DUMPBITS(16); 420 | zip_NEEDBITS(16); 421 | if(n != ((~zip_bit_buf) & 0xffff)) 422 | return -1; // error in compressed data 423 | zip_DUMPBITS(16); 424 | 425 | // read and output the compressed data 426 | zip_copy_leng = n; 427 | 428 | n = 0; 429 | while(zip_copy_leng > 0 && n < size) { 430 | zip_copy_leng--; 431 | zip_wp &= zip_WSIZE - 1; 432 | zip_NEEDBITS(8); 433 | buff[off + n++] = zip_slide[zip_wp++] = 434 | zip_GETBITS(8); 435 | zip_DUMPBITS(8); 436 | } 437 | 438 | if(zip_copy_leng == 0) 439 | zip_method = -1; // done 440 | return n; 441 | } 442 | 443 | function zip_inflate_fixed(buff, off, size) { 444 | /* decompress an inflated type 1 (fixed Huffman codes) block. We should 445 | either replace this with a custom decoder, or at least precompute the 446 | Huffman tables. */ 447 | 448 | // if first time, set up tables for fixed blocks 449 | if(zip_fixed_tl == null) { 450 | var i; // temporary variable 451 | var l = new Array(288); // length list for huft_build 452 | var h; // zip_HuftBuild 453 | 454 | // literal table 455 | for(i = 0; i < 144; i++) 456 | l[i] = 8; 457 | for(; i < 256; i++) 458 | l[i] = 9; 459 | for(; i < 280; i++) 460 | l[i] = 7; 461 | for(; i < 288; i++) // make a complete, but wrong code set 462 | l[i] = 8; 463 | zip_fixed_bl = 7; 464 | 465 | h = new zip_HuftBuild(l, 288, 257, zip_cplens, zip_cplext, 466 | zip_fixed_bl); 467 | if(h.status != 0) { 468 | alert("HufBuild error: "+h.status); 469 | return -1; 470 | } 471 | zip_fixed_tl = h.root; 472 | zip_fixed_bl = h.m; 473 | 474 | // distance table 475 | for(i = 0; i < 30; i++) // make an incomplete code set 476 | l[i] = 5; 477 | zip_fixed_bd = 5; 478 | 479 | h = new zip_HuftBuild(l, 30, 0, zip_cpdist, zip_cpdext, zip_fixed_bd); 480 | if(h.status > 1) { 481 | zip_fixed_tl = null; 482 | alert("HufBuild error: "+h.status); 483 | return -1; 484 | } 485 | zip_fixed_td = h.root; 486 | zip_fixed_bd = h.m; 487 | } 488 | 489 | zip_tl = zip_fixed_tl; 490 | zip_td = zip_fixed_td; 491 | zip_bl = zip_fixed_bl; 492 | zip_bd = zip_fixed_bd; 493 | return zip_inflate_codes(buff, off, size); 494 | } 495 | 496 | function zip_inflate_dynamic(buff, off, size) { 497 | // decompress an inflated type 2 (dynamic Huffman codes) block. 498 | var i; // temporary variables 499 | var j; 500 | var l; // last length 501 | var n; // number of lengths to get 502 | var t; // (zip_HuftNode) literal/length code table 503 | var nb; // number of bit length codes 504 | var nl; // number of literal/length codes 505 | var nd; // number of distance codes 506 | var ll = new Array(286+30); // literal/length and distance code lengths 507 | var h; // (zip_HuftBuild) 508 | 509 | for(i = 0; i < ll.length; i++) 510 | ll[i] = 0; 511 | 512 | // read in table lengths 513 | zip_NEEDBITS(5); 514 | nl = 257 + zip_GETBITS(5); // number of literal/length codes 515 | zip_DUMPBITS(5); 516 | zip_NEEDBITS(5); 517 | nd = 1 + zip_GETBITS(5); // number of distance codes 518 | zip_DUMPBITS(5); 519 | zip_NEEDBITS(4); 520 | nb = 4 + zip_GETBITS(4); // number of bit length codes 521 | zip_DUMPBITS(4); 522 | if(nl > 286 || nd > 30) 523 | return -1; // bad lengths 524 | 525 | // read in bit-length-code lengths 526 | for(j = 0; j < nb; j++) 527 | { 528 | zip_NEEDBITS(3); 529 | ll[zip_border[j]] = zip_GETBITS(3); 530 | zip_DUMPBITS(3); 531 | } 532 | for(; j < 19; j++) 533 | ll[zip_border[j]] = 0; 534 | 535 | // build decoding table for trees--single level, 7 bit lookup 536 | zip_bl = 7; 537 | h = new zip_HuftBuild(ll, 19, 19, null, null, zip_bl); 538 | if(h.status != 0) 539 | return -1; // incomplete code set 540 | 541 | zip_tl = h.root; 542 | zip_bl = h.m; 543 | 544 | // read in literal and distance code lengths 545 | n = nl + nd; 546 | i = l = 0; 547 | while(i < n) { 548 | zip_NEEDBITS(zip_bl); 549 | t = zip_tl.list[zip_GETBITS(zip_bl)]; 550 | j = t.b; 551 | zip_DUMPBITS(j); 552 | j = t.n; 553 | if(j < 16) // length of code in bits (0..15) 554 | ll[i++] = l = j; // save last length in l 555 | else if(j == 16) { // repeat last length 3 to 6 times 556 | zip_NEEDBITS(2); 557 | j = 3 + zip_GETBITS(2); 558 | zip_DUMPBITS(2); 559 | if(i + j > n) 560 | return -1; 561 | while(j-- > 0) 562 | ll[i++] = l; 563 | } else if(j == 17) { // 3 to 10 zero length codes 564 | zip_NEEDBITS(3); 565 | j = 3 + zip_GETBITS(3); 566 | zip_DUMPBITS(3); 567 | if(i + j > n) 568 | return -1; 569 | while(j-- > 0) 570 | ll[i++] = 0; 571 | l = 0; 572 | } else { // j == 18: 11 to 138 zero length codes 573 | zip_NEEDBITS(7); 574 | j = 11 + zip_GETBITS(7); 575 | zip_DUMPBITS(7); 576 | if(i + j > n) 577 | return -1; 578 | while(j-- > 0) 579 | ll[i++] = 0; 580 | l = 0; 581 | } 582 | } 583 | 584 | // build the decoding tables for literal/length and distance codes 585 | zip_bl = zip_lbits; 586 | h = new zip_HuftBuild(ll, nl, 257, zip_cplens, zip_cplext, zip_bl); 587 | if(zip_bl == 0) // no literals or lengths 588 | h.status = 1; 589 | if(h.status != 0) { 590 | if(h.status == 1) 591 | ;// **incomplete literal tree** 592 | return -1; // incomplete code set 593 | } 594 | zip_tl = h.root; 595 | zip_bl = h.m; 596 | 597 | for(i = 0; i < nd; i++) 598 | ll[i] = ll[i + nl]; 599 | zip_bd = zip_dbits; 600 | h = new zip_HuftBuild(ll, nd, 0, zip_cpdist, zip_cpdext, zip_bd); 601 | zip_td = h.root; 602 | zip_bd = h.m; 603 | 604 | if(zip_bd == 0 && nl > 257) { // lengths but no distances 605 | // **incomplete distance tree** 606 | return -1; 607 | } 608 | 609 | if(h.status == 1) { 610 | ;// **incomplete distance tree** 611 | } 612 | if(h.status != 0) 613 | return -1; 614 | 615 | // decompress until an end-of-block code 616 | return zip_inflate_codes(buff, off, size); 617 | } 618 | 619 | function zip_inflate_start() { 620 | var i; 621 | 622 | if(zip_slide == null) 623 | zip_slide = new Array(2 * zip_WSIZE); 624 | zip_wp = 0; 625 | zip_bit_buf = 0; 626 | zip_bit_len = 0; 627 | zip_method = -1; 628 | zip_eof = false; 629 | zip_copy_leng = zip_copy_dist = 0; 630 | zip_tl = null; 631 | } 632 | 633 | function zip_inflate_internal(buff, off, size) { 634 | // decompress an inflated entry 635 | var n, i; 636 | 637 | n = 0; 638 | while(n < size) { 639 | if(zip_eof && zip_method == -1) 640 | return n; 641 | 642 | if(zip_copy_leng > 0) { 643 | if(zip_method != zip_STORED_BLOCK) { 644 | // STATIC_TREES or DYN_TREES 645 | while(zip_copy_leng > 0 && n < size) { 646 | zip_copy_leng--; 647 | zip_copy_dist &= zip_WSIZE - 1; 648 | zip_wp &= zip_WSIZE - 1; 649 | buff[off + n++] = zip_slide[zip_wp++] = 650 | zip_slide[zip_copy_dist++]; 651 | } 652 | } else { 653 | while(zip_copy_leng > 0 && n < size) { 654 | zip_copy_leng--; 655 | zip_wp &= zip_WSIZE - 1; 656 | zip_NEEDBITS(8); 657 | buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8); 658 | zip_DUMPBITS(8); 659 | } 660 | if(zip_copy_leng == 0) 661 | zip_method = -1; // done 662 | } 663 | if(n == size) 664 | return n; 665 | } 666 | 667 | if(zip_method == -1) { 668 | if(zip_eof) 669 | break; 670 | 671 | // read in last block bit 672 | zip_NEEDBITS(1); 673 | if(zip_GETBITS(1) != 0) 674 | zip_eof = true; 675 | zip_DUMPBITS(1); 676 | 677 | // read in block type 678 | zip_NEEDBITS(2); 679 | zip_method = zip_GETBITS(2); 680 | zip_DUMPBITS(2); 681 | zip_tl = null; 682 | zip_copy_leng = 0; 683 | } 684 | 685 | switch(zip_method) { 686 | case 0: // zip_STORED_BLOCK 687 | i = zip_inflate_stored(buff, off + n, size - n); 688 | break; 689 | 690 | case 1: // zip_STATIC_TREES 691 | if(zip_tl != null) 692 | i = zip_inflate_codes(buff, off + n, size - n); 693 | else 694 | i = zip_inflate_fixed(buff, off + n, size - n); 695 | break; 696 | 697 | case 2: // zip_DYN_TREES 698 | if(zip_tl != null) 699 | i = zip_inflate_codes(buff, off + n, size - n); 700 | else 701 | i = zip_inflate_dynamic(buff, off + n, size - n); 702 | break; 703 | 704 | default: // error 705 | i = -1; 706 | break; 707 | } 708 | 709 | if(i == -1) { 710 | if(zip_eof) 711 | return 0; 712 | return -1; 713 | } 714 | n += i; 715 | } 716 | return n; 717 | } 718 | 719 | function zip_inflate(str) { 720 | var out, buff; 721 | var i, j; 722 | 723 | zip_inflate_start(); 724 | zip_inflate_data = str; 725 | zip_inflate_pos = 0; 726 | 727 | buff = new Array(1024); 728 | out = ""; 729 | while((i = zip_inflate_internal(buff, 0, buff.length)) > 0) { 730 | for(j = 0; j < i; j++) 731 | out += String.fromCharCode(buff[j]); 732 | } 733 | zip_inflate_data = null; // G.C. 734 | return out; 735 | } -------------------------------------------------------------------------------- /lib/nodules.js: -------------------------------------------------------------------------------- 1 | (function(global){ 2 | // create compile function for different platforms 3 | var compile = typeof process === "object" ? 4 | function(source, name){ 5 | return require('vm').runInThisContext("(" + source + ")", name); 6 | } : 7 | typeof Packages === "object" ? 8 | function(source, name){ 9 | return Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, source, name, 1, null); 10 | } : eval; 11 | 12 | 13 | if(typeof require == "undefined"){ 14 | // presumably this would only happen from a direct start in rhino 15 | var args = global.arguments; 16 | // bootstrap require 17 | require = makeRequire("file://" + args[0]); 18 | require.paths = []; 19 | } 20 | var modules = {}, 21 | factories = {}, 22 | waitingOn = 0, 23 | inFlight = {}, 24 | monitored = [], 25 | overlays = {}, 26 | callbacks = [], 27 | currentModule, 28 | currentRequire, 29 | useSetInterval = false, 30 | monitorModules = true, 31 | packages = {}, 32 | filePathMappings = [], 33 | defaultPath = "", 34 | main = null, 35 | Unzip = require("./nodules-utils/unzip").Unzip, 36 | promiseModule = require("./nodules-utils/promise"), 37 | when = promiseModule.when, 38 | system = require("./nodules-utils/process"), 39 | print = system.print, 40 | zipInflate = require("./nodules-utils/inflate").zipInflate, 41 | paths = require.paths, 42 | defaultRequire = require, 43 | allKnownOverlays = {npm: true, narwhal: true, rhino: true, node: true}; 44 | 45 | if(typeof process === "undefined"){ 46 | var request = require("./nodules-utils/rhino-http-client").request, 47 | schedule = require("./nodules-utils/rhino-delay").schedule, 48 | enqueue = require("event-loop").enqueue, 49 | fs = require("./nodules-utils/rhino-fs"); 50 | }else{ 51 | var request = require("./nodules-utils/node-http-client").request, 52 | schedule = require("./nodules-utils/node-delay").schedule, 53 | enqueue = process.nextTick, 54 | fs = require("./nodules-utils/node-fs"); 55 | } 56 | var moduleExports = { 57 | promise: promiseModule, 58 | "fs-promise": fs, 59 | "nodules": exports, 60 | system: system 61 | }; 62 | 63 | 64 | function EnginePackage(engine){ 65 | var enginePackage = this; 66 | this.useLocal= function(){ 67 | var packageJson = "{}", 68 | path = fs.absolute("."); 69 | function findPackage(path){ 70 | try{ 71 | packageJson = fs.read(path + "/package.json"); 72 | }catch(e){ 73 | if(path.lastIndexOf('/') < 1 && path.lastIndexOf('\\') < 1){ 74 | throw new Error("Couldn't find package.json"); 75 | } 76 | return findPackage(path.substring(0, Math.max(path.lastIndexOf('/'),path.lastIndexOf('\\')))); 77 | } 78 | return path; 79 | } 80 | try{ 81 | path = findPackage(path); 82 | }catch(e){} 83 | try{ 84 | var parsed = JSON.parse(packageJson); 85 | }catch(e){ 86 | e.message += " trying to parse local package.json"; 87 | throw e; 88 | } 89 | if(path.charAt(path.length - 1) == '\\' || path.charAt(path.length - 1) == '/'){ 90 | path = path.substring(0, path.length - 1); 91 | } 92 | return enginePackage.usePackage(parsed, "file://" + path); 93 | }; 94 | this.usePackage= function(packageData, path){ 95 | processPackage(path, packageData, engine); 96 | if(path){ 97 | packageData.mappings.defaultPath = path + "/lib/"; 98 | } 99 | for(var i in packageData){ 100 | enginePackage[i] = packageData[i]; 101 | } 102 | return enginePackage; 103 | }; 104 | 105 | this.getModuleSource = function(id){ 106 | try{ 107 | return fs.read(enginePackage.getCachePath(id)); 108 | }catch(e){ 109 | if(id.indexOf(":") === -1 && moduleExports[id.substring(0, id.length - 3)]){ 110 | try{ 111 | return fs.read(__dirname+ "/nodules-utils/" + id); 112 | } 113 | catch(e){} 114 | } 115 | } 116 | }; 117 | this.getCachePath= function(id){ 118 | if(id.substring(id.length - 3) == ".js"){ 119 | id = id.substring(0, id.length - 3); 120 | } 121 | var uri = resolveUri("", id, enginePackage.mappings); 122 | if(id.charAt(id.length -1) == "/"){ 123 | uri = uri.substring(0, uri.lastIndexOf(".")); 124 | } 125 | if(uri.substring(0,7) == "file://"){ 126 | return uri.substring(7); 127 | } 128 | return cachePath(uri); 129 | }; 130 | } 131 | 132 | var define = function (id, injects, factory) { 133 | if (currentModule == null) { 134 | throw new Error("define() may only be called during module factory instantiation"); 135 | } 136 | var module = currentModule; 137 | var require = currentRequire; 138 | if (!factory) { 139 | // two or less arguments 140 | factory = injects; 141 | if (factory) { 142 | // two args 143 | if (typeof id === "string") { 144 | if (id !== module.id) { 145 | throw new Error("Can not assign module to a different id than the current file"); 146 | } 147 | // default injects 148 | injects = ["require", "exports", "module"]; 149 | } 150 | else{ 151 | // anonymous, deps included 152 | injects = id; 153 | } 154 | } 155 | else { 156 | // only one arg, just the factory 157 | factory = id; 158 | injects = ["require", "exports", "module"]; 159 | } 160 | } 161 | if (typeof factory !== "function"){ 162 | // we can just provide a plain object 163 | return module.exports = factory; 164 | } 165 | var returned = factory.apply(module.exports, injects.map(function (injection) { 166 | switch (injection) { 167 | // check for CommonJS injection variables 168 | case "require": return require; 169 | case "exports": return module.exports; 170 | case "module": return module; 171 | default: 172 | // a module dependency 173 | return require(injection); 174 | } 175 | })); 176 | if(returned){ 177 | // since AMD encapsulates a function/callback, it can allow the factory to return the exports. 178 | module.exports = returned; 179 | } 180 | }; 181 | 182 | packages[""] = exports; 183 | exports.mappings = []; 184 | exports.mappings.defaultPath = ""; 185 | 186 | exports.forBrowser = function(){ 187 | return new EnginePackage("browser"); 188 | }; 189 | exports.forEngine = function(engine){ 190 | return new EnginePackage(engine); 191 | }; 192 | 193 | exports.ensure = makeRequire("").ensure; 194 | exports.runAsMain = function(uri){ 195 | if(!uri || uri.indexOf(":") === -1){ 196 | uri = "file://" + fs.absolute(uri || "lib/index.js"); 197 | } 198 | main = modules[uri] = modules[uri] || new Module(uri); 199 | return exports.ensure(uri, function(require){ 200 | require(uri); 201 | }); 202 | }; 203 | 204 | EnginePackage.call(exports, exports.usingEngine = typeof process !== "undefined" ? "node" : "narwhal"); 205 | 206 | function Module(uri){ 207 | this.id = uri; 208 | this.dependents = {}; 209 | } 210 | 211 | Module.prototype.supportsUri = true; 212 | Module.prototype.setExports = function(exports){ 213 | this.exports = exports; 214 | } 215 | 216 | exports.baseFilePath = system.env.NODULES_PATH || "downloaded-modules"; 217 | try{ 218 | var filePathMappingsJson = fs.read(exports.baseFilePath + "/paths.json"); 219 | }catch(e){ 220 | 221 | } 222 | if(filePathMappingsJson){ 223 | var filePathMappingsObject = JSON.parse(filePathMappingsJson); 224 | useSetInterval = filePathMappingsObject.useSetInterval; 225 | monitorModules = filePathMappingsObject.monitorModules !== false; 226 | for(var i in filePathMappingsObject){ 227 | filePathMappings.push({ 228 | from: RegExp(i), 229 | to: filePathMappingsObject[i] 230 | }); 231 | } 232 | } 233 | 234 | function reloadable(onload){ 235 | var onChange = function(){ 236 | monitored.push(onChange); 237 | onload(); 238 | } 239 | onChange(); 240 | } 241 | function resolveUri(currentId, uri, mappings){ 242 | if(uri.charAt(0) === '.'){ 243 | var extension = currentId.match(/\.[\w]+$/); 244 | extension = extension ? extension[0] : ""; 245 | uri = currentId.substring(0, currentId.lastIndexOf('/') + 1) + uri; 246 | while(lastUri !== uri){ 247 | var lastUri = uri; 248 | uri = uri.replace(/\/[^\/]*\/\.\.\//,'/'); 249 | } 250 | return [uri.replace(/\/\.\//g,'/')] + extension; 251 | } 252 | else if(uri.indexOf(":") > -1){ 253 | return uri; 254 | }else{ 255 | if(mappings){ 256 | for(var i = 0; i < mappings.length; i++){ 257 | var mapping = mappings[i]; 258 | var from = mapping.from; 259 | if(mapping.exact ? uri === from : uri.substring(0, from.length) === from){ 260 | uri = mapping.to + uri.substring(from.length); 261 | return uri.match(/\.\w+$/) ? uri : uri + (getPackage(uri).extension || ".js"); 262 | } 263 | } 264 | var packageData = getPackage(""); 265 | if(!uri.match(/\.\w+$/) && !(packageData.usesSystemModules && packageData.usesSystemModules.indexOf(uri) > -1)){ 266 | uri = mappings.defaultPath +uri + (packageData.extension || ".js"); 267 | } 268 | } 269 | return uri; 270 | } 271 | } 272 | function getPackageUri(uri){ 273 | if(uri.substring(0,4) == "jar:"){ 274 | // if it is an archive, the root should be the package URI 275 | return uri.substring(0, uri.lastIndexOf('!') + 2); 276 | } 277 | else{ 278 | // else try to base it on the path 279 | return uri.substring(0, uri.lastIndexOf('/lib/') + 1); 280 | } 281 | } 282 | function getPackage(uri){ 283 | return packages[getPackageUri(uri)] || {mappings: packages[""].mappings}; 284 | } 285 | function makeWorker(Constructor, currentId){ 286 | return Constructor && function(script, name){ 287 | var worker = Constructor("nodules-worker.js", name); 288 | var mappings = getPackage(currentId).mappings; 289 | worker.postMessage(resolveUri(currentId, script, mappings)); 290 | return worker; 291 | } 292 | } 293 | function makeRequire(currentId){ 294 | var require = function(id){ 295 | var uri = resolveUri(currentId, id, getPackage(currentId).mappings); 296 | if(moduleExports[uri]){ 297 | modules[uri].dependents[currentId] = true; 298 | return moduleExports[uri]; 299 | } 300 | if(factories[uri]){ 301 | try{ 302 | var exports = moduleExports[uri] = {}, 303 | module = currentModule = modules[uri] = modules[uri] || new Module(uri), 304 | currentFile = cachePath(uri), 305 | factory = factories[uri], 306 | originalExports = module.exports = exports, 307 | nextRequire = currentRequire = makeRequire(uri); 308 | module.dependents[currentId] = true; 309 | exports = factory.call(exports, nextRequire, exports, module, define, 310 | currentFile, currentFile.replace(/\/[^\/]*$/,'')) 311 | || exports; 312 | if(factory != factories[uri]){ 313 | // if a module was wrapped with the transport/D than the factory will get replaced 314 | exports = factories[uri].call(exports, nextRequire, exports, module, define, 315 | currentFile, currentFile.replace(/\/[^\/]*$/,'')) 316 | || exports; 317 | } 318 | if(originalExports != module.exports){ 319 | exports = module.exports; 320 | } 321 | Object.defineProperty(module, "exports",{value:exports}); 322 | moduleExports[uri] = exports; 323 | var successful = true; 324 | } 325 | finally{ 326 | currentRequire = null; 327 | currentModule = null; 328 | if(!successful){ 329 | delete moduleExports[uri]; 330 | } 331 | } 332 | return exports; 333 | } 334 | if(uri.indexOf(":") === -1){ 335 | id = uri; 336 | if(id.substring(id.length - 3) == ".js"){ 337 | id = id.substring(0, id.length - 3); 338 | } 339 | } 340 | try{ 341 | return moduleExports[id] || defaultRequire(id); 342 | }catch(e){ 343 | if(e.message.substring(0, 19) == "Can not find module"){ 344 | throw new Error("Can not find module " + uri); 345 | } 346 | if(e.message.substring(0, 28) == "require error: couldn't find"){ 347 | throw new Error("Can not find module " + uri); 348 | } 349 | throw e; 350 | } 351 | }; 352 | require.main = main; 353 | require.define = function(moduleSet, dependencies){ 354 | if(dependencies){ 355 | require.ensure(dependencies); 356 | } 357 | var context = getPackageUri(currentId) + "lib/"; 358 | for(var i in moduleSet){ 359 | var moduleDef = moduleSet[i]; 360 | factories[context + i + ".js"] = moduleDef.factory || moduleDef; 361 | } 362 | }; 363 | require.def = define; 364 | /* require.def = function(id, dependencies, factory){ 365 | if(dependencies){ 366 | require.ensure(dependencies); 367 | }else{ 368 | factory = dependencies; 369 | } 370 | factories[getPackageUri(currentId) + "lib/" + id + ".js"] = function(require, exports, module){ 371 | return factory.apply(exports, dependencies ? dependencies.map(function(id){ 372 | switch(id){ 373 | case "require": return require; 374 | case "exports" : return exports; 375 | case "module" : return module; 376 | default: return require(id); 377 | } 378 | }) : []); 379 | }; 380 | };*/ 381 | require.paths = paths; 382 | require.reloadable = reloadable; 383 | require.resource = function(uri){ 384 | uri = resolveUri(currentId, uri, getPackage(currentId).mappings); 385 | return factories[uri]; 386 | } 387 | var ensure = require.ensure = function(id, callback){ 388 | var require = makeRequire(uri); 389 | if(id instanceof Array){ 390 | if(!id.length){ 391 | return callback && callback(); 392 | } 393 | var uri = resolveUri(currentId, id[0], getPackage(currentId).mappings), 394 | require = makeRequire(uri); 395 | waitingOn++; 396 | if(callback){ 397 | callbacks.unshift(callback); 398 | } 399 | try{ 400 | var results = id.map(ensure); 401 | }finally{ 402 | decrementWaiting(); 403 | } 404 | return results; 405 | } 406 | var uri = resolveUri(currentId, id, getPackage(currentId).mappings), 407 | require = makeRequire(uri), 408 | i = 0; 409 | if(factories[uri]){ 410 | if(typeof callback == "function"){ 411 | callback(require); 412 | } 413 | return; 414 | } 415 | if(typeof callback == "function"){ 416 | callbacks.unshift(callback); 417 | } 418 | if(uri.indexOf(':') < 0 || inFlight[uri]){ 419 | return; 420 | } 421 | function onError(error){ 422 | if(uri.indexOf(":") === -1){ 423 | id = uri; 424 | if(id.substring(id.length - 3) == ".js"){ 425 | id = id.substring(0, id.length - 3); 426 | } 427 | } 428 | try{ 429 | //check to see if it is a system module 430 | moduleExports[id] || defaultRequire(id); 431 | }catch(e){ 432 | factories[uri] = function(){ 433 | throw new Error(error.stack + " failed to load " + uri); 434 | }; 435 | } 436 | decrementWaiting(); 437 | } 438 | function decrementWaiting(){ 439 | waitingOn--; 440 | if(waitingOn === 0){ 441 | var calling = callbacks; 442 | callbacks = []; 443 | inFlight = {}; 444 | calling.forEach(function(callback){ 445 | enqueue(function(){ 446 | callback(require); 447 | }); 448 | }); 449 | } 450 | } 451 | waitingOn++; 452 | inFlight[uri] = true; 453 | try{ 454 | var source = exports.load(uri, require); 455 | return when(source, function(source){ 456 | try{ 457 | if(source !== undefined){ 458 | var packageData = getPackage(uri); 459 | if(packageData && packageData.compiler){ 460 | var deferred = promiseModule.defer(); 461 | require.ensure(packageData.compiler.module, function(){ 462 | try{ 463 | var rewrittenSource = require(packageData.compiler.module)[packageData.compiler["function"] || "compile"](source); 464 | createFactory(uri, rewrittenSource); 465 | deferred.resolve(); 466 | }catch(e){ 467 | e.message += " compiling " + uri; 468 | deferred.reject(e); 469 | } 470 | }); 471 | return deferred.promise; 472 | } 473 | createFactory(uri, source); 474 | return exports; 475 | } 476 | }finally{ 477 | decrementWaiting(); 478 | } 479 | }, onError); 480 | } 481 | catch(e){ 482 | onError(e); 483 | } 484 | }; 485 | return require; 486 | } 487 | function processPackage(packageUri, packageData, engine){ 488 | engine = engine || exports.usingEngine; 489 | var mappings = packageData.mappings || {}; 490 | var mappingsArray = packages[""].mappings; 491 | var defaultPath = mappingsArray.defaultPath; 492 | function addMappings(mappings){ 493 | if(mappings){ 494 | mappingsArray = mappingsArray.concat(Object.keys(mappings).map(function(key){ 495 | var to = mappings[key]; 496 | if(typeof to == "string"){ 497 | if(to.substring(0,5) == "http:"){ 498 | to = "jar:" + to + "!/lib/"; 499 | } 500 | if(to.substring(0,6) == "https:"){ 501 | to = "jar:" + to + "!/lib/"; 502 | } 503 | // if it ends with a slash, only match paths 504 | if(to.charAt(to.length - 1) === '/' && key.charAt(key.length - 1) !== '/'){ 505 | key += '/'; 506 | } 507 | // for backwards compatibility with regex exact matches 508 | else if(key.charAt(0) === "^" && key.charAt(key.length - 1) === "$"){ 509 | to += packageData.extension || ".js"; 510 | key = key.substring(1, key.length - 1); 511 | } 512 | }else if(to.archive){ 513 | var libDir = to.descriptor && to.descriptor.directories && to.descriptor.directories.lib; 514 | if(typeof libDir != "string"){ 515 | libDir = "lib"; 516 | } 517 | key += '/'; 518 | to = to.archive ? "jar:" + to.archive + "!/" + libDir + "/" : to.location; 519 | } 520 | 521 | return { 522 | from: key, 523 | exact: to.match(/\.\w+$/), 524 | to: resolveUri(packageUri, typeof to == "string" ? to : to.to) 525 | }; 526 | }).sort(function(a, b){ 527 | return a.from.toString().length < b.from.toString().length ? 1 : -1; 528 | })); 529 | } 530 | } 531 | if(packageData.overlay){ 532 | Object.keys(packageData.overlay).forEach(function(condition){ 533 | try{ 534 | var matches = (engine == condition) || !(condition in allKnownOverlays) && eval(condition); 535 | }catch(e){} 536 | if(matches){ 537 | addMappings(packageData.overlay[condition].mappings); 538 | } 539 | }); 540 | } 541 | addMappings(packageData.mappings); 542 | mappingsArray.defaultPath = defaultPath; 543 | packageData.mappings = mappingsArray; 544 | return packageData; 545 | } 546 | 547 | 548 | 549 | exports.load = function(uri, require){ 550 | var protocolLoader = exports.protocols[uri.substring(0, uri.indexOf(":"))]; 551 | // do this so that we don't timeout on adding the error handler for the source 552 | if(!protocolLoader){ 553 | throw new Error("Protocol " + uri.substring(0, uri.indexOf(":")) + " not implemented for accessing " + uri); 554 | } 555 | var source = protocolLoader(uri); 556 | return when(source, function(source){ 557 | if(!source){ 558 | throw new Error("Not found " + uri); 559 | } 560 | // check for source defined package URI 561 | var packageUri = source.match(/package root: (\w+:.*)/); 562 | if(packageUri){ 563 | packageUri = packageUri[1]; 564 | } 565 | else if(uri.substring(0,4) == "jar:"){ 566 | // if it is an archive, the root should be the package URI 567 | var packageUri = uri.substring(0, uri.lastIndexOf('!') + 2); 568 | } 569 | else{ 570 | // else try to base it on the path 571 | var packageUri = uri.substring(0, uri.lastIndexOf('/lib/') + 1); 572 | } 573 | var packageData = packages[packageUri]; 574 | if(!packageData){ 575 | // idPart = uri; 576 | // function tryNext(){ 577 | // idPart = idPart.substring(0, idPart.lastIndexOf('/') + 1); 578 | // don't watch json files or changes will create a new factory 579 | dontWatch[packageUri + "package.json"] = true; 580 | packageData = when(protocolLoader(packageUri + "package.json"), function(packageJson){ 581 | if(!packageJson){ 582 | return packages[packageUri] = processPackage(packageUri, {}); 583 | } 584 | try{ 585 | var parsed = JSON.parse(packageJson); 586 | }catch(e){ 587 | e.message += " trying to parse " + packageUri + "package.json"; 588 | throw e; 589 | } 590 | return packages[packageUri] = processPackage(packageUri, parsed); 591 | }, function(error){ 592 | return packages[packageUri] = processPackage(packageUri, {}); 593 | }); 594 | if(!packages[packageUri]){ 595 | packages[packageUri] = packageData; 596 | } 597 | } 598 | return when(packageData, function(packageData){ 599 | if(source){ 600 | source.replace(/require\s*\(\s*['"]([^'"]*)['"]\s*\)/g, function(t, moduleId){ 601 | if(require){ 602 | require.ensure(moduleId); 603 | } 604 | }); 605 | source.replace(/define\s*\(\s*(\[(?:['"][^'"]*['"],?)+\])\s*\)/, function(t, deps){ 606 | deps = JSON.parse(deps); 607 | if(require){ 608 | deps.forEach(function(moduleId){ 609 | require.ensure(moduleId); 610 | }); 611 | } 612 | }); 613 | 614 | if(packageData.compiler){ 615 | require.ensure(packageData.compiler.module); 616 | } 617 | } 618 | return source; 619 | }); 620 | }); 621 | }; 622 | function createFactory(uri, source){ 623 | try{ 624 | factories[uri] = compile("function(require, exports, module, define, __filename, __dirname, Worker, SharedWorker){" + source + "\n;return exports;}", uri); 625 | /* var indexOfExport, indexOfRequireDef = source.indexOf("define"); 626 | if(indexOfRequireDef > -1 && ((indexOfExport = source.indexOf("exports.")) == -1 || indexOfExport > indexOfRequireDef)){ 627 | // looks like it is an Aynchronous module definition module 628 | factories[uri]({def: function(id, dependencies, factory){ 629 | if(!factory){ 630 | factory = dependencies; 631 | if(!factory){ 632 | factory = id; 633 | id = null; 634 | } 635 | dependencies = null; 636 | 637 | } 638 | if(typeof id == "object"){ 639 | dependencies = id; 640 | id = null; 641 | } 642 | if(typeof id == "string"){ 643 | if(uri.indexOf(id) == -1){ 644 | throw new Error("Can't set another module"); 645 | } 646 | } 647 | if(dependencies){ 648 | makeRequire(uri).ensure(dependencies.filter(function(dep){ 649 | return !(dep in {require:true, exports:true, module: true}); 650 | })); 651 | } 652 | factories[uri] = function(require, exports, module){ 653 | return factory.apply(exports, dependencies ? dependencies.map(function(id){ 654 | switch(id){ 655 | case "require": return require; 656 | case "exports" : return exports; 657 | case "module" : return module; 658 | default: return require(id); 659 | } 660 | }) : arguments); 661 | }; 662 | }}); 663 | }*/ 664 | }catch(e){ 665 | factories[uri] = function(){ 666 | throw new Error(e.stack + " in " + uri); 667 | } 668 | } 669 | } 670 | exports.protocols = { 671 | http: cache(function(uri){ 672 | return getUri(uri); 673 | }, true), 674 | https: cache(function(uri){ 675 | return getUri(uri); 676 | }, true), 677 | jar: cache(function(uri){ 678 | uri = uri.substring(4); 679 | var exclamationIndex = uri.indexOf("!"); 680 | var target = uri.substring(exclamationIndex + 2); 681 | var targetContents; 682 | uri = uri.substring(0, exclamationIndex); 683 | return when(fs.stat(cachePath(uri)), function(stat){ 684 | if(!stat.mtime){ 685 | return onError(); 686 | } 687 | // archive has already been downloaded, but the file was not found 688 | return null; 689 | }, onError); 690 | function onError(){ 691 | return when(getUri(uri), function(source){ 692 | if(source === null){ 693 | throw new Error("Archive not found " + uri); 694 | } 695 | var unzip = new Unzip(source); 696 | unzip.readEntries(); 697 | var rootPath = unzip.entries[0].fileName; 698 | unzip.entries.some(function(entry){ 699 | if(target == entry.fileName){ 700 | rootPath = ""; 701 | return true; 702 | } 703 | if(entry.fileName.substring(0, rootPath.length) !== rootPath){ 704 | rootPath = ""; 705 | } 706 | }); 707 | unzip.entries.forEach(function(entry){ 708 | var fileName = entry.fileName.substring(rootPath.length); 709 | var path = cachePath(uri + "!/" + fileName); 710 | if (entry.compressionMethod <= 1) { 711 | // Uncompressed 712 | var contents = entry.data; 713 | } else if (entry.compressionMethod === 8) { 714 | // Deflated 715 | var contents = zipInflate(entry.data); 716 | }else{ 717 | throw new Error("Unknown compression format"); 718 | } 719 | ensurePath(path); 720 | if(path.charAt(path.length-1) != '/'){ 721 | // its a file 722 | try{ 723 | fs.writeFileSync(path, contents, "binary"); 724 | }catch(e){ 725 | // make sure we immediately let the user know if a write fails 726 | throw e; 727 | } 728 | } 729 | if(target == fileName){ 730 | targetContents = contents; 731 | } 732 | }); 733 | if(!targetContents){ 734 | throw new Error("Target path " + target + " not found in archive " + uri); 735 | } 736 | return targetContents; 737 | }); 738 | } 739 | }), 740 | file: function(uri){ 741 | return readModuleFile(uri.substring(7), uri); 742 | }, 743 | data: function(uri){ 744 | return uri.substring(uri.indexOf(",")); 745 | } 746 | }; 747 | exports.protocols.zip = exports.protocols.jar; 748 | 749 | var requestedUris = {}; 750 | function getUri(uri, tries){ 751 | tries = tries || 1; 752 | if(requestedUris[uri]){ 753 | return requestedUris[uri]; 754 | } 755 | print("Downloading " + uri + (tries > 1 ? " attempt #" + tries : "")); 756 | return requestedUris[uri] = request({url:uri, encoding:"binary"}).then(function(response){ 757 | if(response.status == 302 || response.status == 301){ 758 | return getUri(response.headers.location); 759 | } 760 | if(response.status < 300){ 761 | var body = ""; 762 | return when(response.body.forEach(function(part){ 763 | if(!body){ 764 | body = part; 765 | }else{ 766 | body += part; 767 | } 768 | }), function(){ 769 | return body; 770 | }); 771 | } 772 | if(response.status == 404){ 773 | return null; 774 | } 775 | throw new Error(response.status); 776 | }, function(error){ 777 | tries++; 778 | if(tries > 3){ 779 | throw error; 780 | } 781 | // try again 782 | delete requestedUris[uri]; 783 | return getUri(uri, tries); 784 | }); 785 | } 786 | 787 | function onFileChange(uri){ 788 | // we delete all module entries and dependents to ensure proper referencing 789 | function removeDependents(module){ 790 | if(module){ 791 | delete moduleExports[module.id]; 792 | var dependents = module.dependents; 793 | module.dependents = {}; 794 | for(var i in dependents){ 795 | removeDependents(modules[i]); 796 | } 797 | } 798 | } 799 | removeDependents(modules[uri]); 800 | var calling = monitored; 801 | monitored = []; 802 | calling.forEach(function(callback){ 803 | callback(); 804 | }); 805 | } 806 | function ensurePath(path){ 807 | var index = path.lastIndexOf('/'); 808 | if(index === -1){ 809 | return; 810 | } 811 | var path = path.substring(0, index); 812 | try{ 813 | var test = fs.statSync(path).mtime.time; 814 | }catch(e){ 815 | ensurePath(path); 816 | fs.mkdirSync(path, 0777); 817 | } 818 | } 819 | var watching = {}; 820 | var dontWatch = {}; 821 | 822 | var watchedFiles; 823 | function readModuleFile(path, uri){ 824 | try{ 825 | var source = fs.read(path); 826 | if(monitorModules && !watching[path] && !dontWatch[uri]){ 827 | watching[path] = true; 828 | if(fs.watchFile && !useSetInterval){ 829 | fs.watchFile(path, {persistent: false, interval: process.platform == "darwin" ? 300 : 0}, possibleChange); 830 | }else{ 831 | if(!watchedFiles){ 832 | watchedFiles = []; 833 | schedule(1000).forEach(function(){ 834 | watchedFiles.forEach(function(watched){ 835 | if(!watched.pending){ 836 | watched.pending = true; 837 | // a hack to get the OS to reread from the network paths 838 | if(fs.closeSync){ 839 | fs.closeSync(fs.openSync(watched.path, "r")); 840 | } 841 | when(fs.stat(watched.path), function(stat){ 842 | watched.pending = false; 843 | watched.callback(watched.oldstat, stat); 844 | watched.oldstat = stat; 845 | }, print); 846 | } 847 | }); 848 | }); 849 | } 850 | watchedFiles.push({ 851 | oldstat: fs.statSync(path), 852 | path: path, 853 | callback: possibleChange 854 | }); 855 | } 856 | } 857 | return source; 858 | } 859 | catch(e){ 860 | if(path.match(/\.js$/) && typeof process != "undefined"){ 861 | path = path.replace(/\.js$/,".node"); 862 | try{ 863 | fs.read(path); 864 | return 'process.dlopen("' + path + '", exports);'; 865 | } 866 | catch(nodeE){ 867 | } 868 | } 869 | throw e; 870 | } 871 | function possibleChange(oldstat, newstat){ 872 | if(oldstat.mtime.getTime() !== newstat.mtime.getTime() && waitingOn === 0){ 873 | if(typeof process == "undefined" || !process.env._CHILD_ID_){ 874 | print("Reloading " + uri); 875 | } 876 | delete factories[uri]; 877 | exports.ensure(uri, function(){ 878 | onFileChange(uri); 879 | }); 880 | } 881 | } 882 | } 883 | function cachePath(uri){ 884 | var path = uri; 885 | if(path.indexOf(exports.defaultUri) === 0){ 886 | path = path.substring(exports.defaultUri.length); 887 | } 888 | filePathMappings.forEach(function(pathMapping){ 889 | path = path.replace(pathMapping.from, pathMapping.to); 890 | }); 891 | return ((path.charAt(0) == '/' || path.charAt(1) == ':' || path.substring(0,5) == "file:") ? '' : exports.baseFilePath + '/') + path.replace(/^\w*:(\w*:)?\/\//,'').replace(/!\/?/g,'/'); // remove protocol and replace colons and add base file path 892 | } 893 | function cache(handler, writeBack){ 894 | return function(uri){ 895 | try{ 896 | return when(readModuleFile(cachePath(uri), uri), function(source){ 897 | if(source === "Not Found"){ 898 | return null; 899 | } 900 | return source; 901 | }, onError); 902 | } 903 | catch(e){ 904 | return onError(e); 905 | } 906 | function onError(error){ 907 | var source = handler(uri); 908 | if(writeBack){ 909 | when(source, function(source){ 910 | var path = cachePath(uri); 911 | ensurePath(path); 912 | fs.writeFileSync(path, source === null ? "Not Found" : source, "binary"); 913 | }); 914 | } 915 | return source; 916 | } 917 | }; 918 | }; 919 | 920 | 921 | if(typeof process == "undefined"){ 922 | system.args.unshift(null); 923 | } 924 | if(require.main == module){ 925 | if (system.args[2] === "-refresh") { 926 | print("deleting " + exports.baseFilePath); 927 | require("child_process").exec("rm -r " + exports.baseFilePath, function(err, stdout, stderr) { 928 | if (err !== null) { 929 | system.print("error deleting directory: " + err); 930 | } else { 931 | exports.useLocal().runAsMain(system.args[3]); 932 | } 933 | }); 934 | } else { 935 | exports.useLocal().runAsMain(system.args[2]); 936 | } 937 | if(typeof process === "undefined"){ 938 | require("event-loop").enterEventLoop(); 939 | } 940 | } 941 | })(this); --------------------------------------------------------------------------------