Browse files

Squashed commit of the following:

commit f6933ca174c10d0756eb0880bba12acb6bdd3ff9
Merge: be911b2 08f9c32
Author: Wei Kin Huang <weikin.huang04@gmail.com>
Date:   Wed Jun 13 08:40:46 2012 -0700

    Merge pull request #1 from weikinhuang/browserstack-module

    Browserstack module & Refactor

commit 08f9c32ef9cf4e1dd2e3a0a4f4dad57b1840bc71
Author: Wei Kin Huang <weikin.huang04@gmail.com>
Date:   Wed Jun 13 11:26:11 2012 -0400

    New configuration file format

commit 15f20d4eb15ea4ada86ab4cc698015e72d90217f
Author: Wei Kin Huang <weikin.huang04@gmail.com>
Date:   Tue Jun 12 14:51:28 2012 -0400

    Use build.getOption to get configuration options for modules

commit 9f343419bb834a65440ab9606ea80da9884b2a37
Author: Wei Kin Huang <weikin.huang04@gmail.com>
Date:   Mon Jun 11 18:32:03 2012 -0400

    Continue moving to a more modular system for code organization

commit e2add7ede012290d7fca49452ec6980e61ec3380
Author: Wei Kin Huang <weikin.huang04@gmail.com>
Date:   Mon Jun 11 18:22:05 2012 -0400

    Continue moving to a more modular system for code organization

commit 9d114bc9e4dbaf89932f1b56effca81e36b49ce5
Author: Wei Kin Huang <weikin.huang04@gmail.com>
Date:   Mon Jun 11 16:42:16 2012 -0400

    Start moving to a more modular system for code organization

commit 61a19a41099c17c3848b7f027437833a386f647e
Author: Wei Kin Huang <weikin.huang04@gmail.com>
Date:   Mon Jun 11 14:41:01 2012 -0400

    Fix context issue for node bridges

commit eb7fd99cabb84cc0ac7eed4e2afd5c8929874cd1
Author: Wei Kin Huang <weikin.huang04@gmail.com>
Date:   Sat Jun 9 21:45:21 2012 -0400

    Initial iteration of browserstack based unit testing
  • Loading branch information...
1 parent 717cea7 commit b625f1ecf1a83cd5907fbfbb3f2a5d86b69bd011 @weikinhuang committed Jun 13, 2012
View
10 .gitignore
@@ -12,3 +12,13 @@
test/dev.html
build/.*.json
coverage/*
+
+
+# remove the following lines in production repo
+/config.js
+dist/*
+docs/*
+perf/*
+src/*
+test/*
+vendor/*
View
37 build/bridge/benchmark-node-bridge.js
@@ -15,31 +15,24 @@ var sandbox = {
setInterval : setInterval,
clearTimeout : clearTimeout,
clearInterval : clearInterval,
- console : console,
- Object : Object,
- Function : Function,
- Boolean : Boolean,
- Number : Number,
- String : String,
- RegExp : RegExp,
- Array : Array,
- Date : Date,
- Error : Error
+ console : console
};
-// window is a circualr reference
+//window/global/root is a circualr reference
sandbox.window = sandbox;
+sandbox.global = sandbox;
+sandbox.root = sandbox;
-// load the benchmark library into the sandbox
-sandbox.Benchmark = require(path.join(__dirname, "..", "perf/benchmark.js"));
+//create a new context
+var context = vm.createContext(sandbox);
-// keep a reference to the "root" variable
-sandbox.root = sandbox.window;
+// load the benchmark library into the sandbox
+context.Benchmark = require(path.join(__dirname, "..", "perf/benchmark.js"));
// create a new global test suite
-sandbox.___benchmarks = new sandbox.Benchmark.Suite();
+context.___benchmarks = new context.Benchmark.Suite();
// bind the response handlers to the parent process
-sandbox.___benchmarks.on("add", function(e) {
+context.___benchmarks.on("add", function(e) {
var i = 0, test = e.target, testData = {
id : test.id,
name : test.name
@@ -94,17 +87,17 @@ sandbox.___benchmarks.on("add", function(e) {
});
// notify the parent process that tests are finished
-sandbox.___benchmarks.on("complete", function() {
+context.___benchmarks.on("complete", function() {
process.send({
event : "done",
data : {}
});
});
// wrapper function to add a test group
-sandbox.Benchmark.test = function(group, testGroup) {
+context.Benchmark.test = function(group, testGroup) {
testGroup(function(name, test) {
- sandbox.___benchmarks.add.call(sandbox.___benchmarks, group + "." + name, test);
+ context.___benchmarks.add.call(context.___benchmarks, group + "." + name, test);
});
};
@@ -123,7 +116,7 @@ function load(src, root) {
// run the source in the sandbox
try {
- vm.runInNewContext(files.join("\n"), sandbox);
+ vm.runInContext(files.join("\n"), context);
} catch (e) {
console.log(e.message);
process.exit(1);
@@ -140,7 +133,7 @@ load(options.source.src, options.dir.src);
load(options.source.perf, options.dir.perf);
// start the tests
-sandbox.___benchmarks.run({
+context.___benchmarks.run({
async : true,
queued : true
});
View
2 build/bridge/benchmark-phantom-bridge.html
@@ -13,7 +13,7 @@ <h1 id="qunit-header">Benchmark Test Suite</h1>
var ___prev_module = window.module;
window.module = {};
</script>
-<script type="text/javascript" src="../../config.js"></script>
+<script type="text/javascript" src="../.cache.options.json"></script>
<script type="text/javascript">
var ___opts = module.exports;
if(___prev_module == null) {
View
65 build/bridge/coverage-node-bridge.js
@@ -17,97 +17,83 @@ var sandbox = {
setInterval : setInterval,
clearTimeout : clearTimeout,
clearInterval : clearInterval,
- console : console,
- Object : Object,
- Function : Function,
- Boolean : Boolean,
- Number : Number,
- String : String,
- RegExp : RegExp,
- Array : Array,
- Date : Date,
- Error : Error
+ console : console
};
-// window is a circualr reference
+// window/global/root is a circualr reference
sandbox.window = sandbox;
+sandbox.global = sandbox;
+sandbox.root = sandbox;
+
+// create a new context
+var context = vm.createContext(sandbox);
try {
- vm.runInNewContext(fs.readFileSync(path.join(__dirname, "..", "qunit/qunit.js"), "utf8"), sandbox);
+ vm.runInContext(fs.readFileSync(path.join(__dirname, "..", "qunit/qunit.js"), "utf8"), context);
} catch (err) {
process.exit(1);
}
-// keep a reference to the "root" variable
-sandbox.root = sandbox.window;
-
// have a global reference to QUnit within the sandbox
-sandbox.QUnit = sandbox.exports;
-sandbox.exports = {};
+context.QUnit = context.exports;
+context.exports = {};
// don't have qunit reorder tests
-sandbox.QUnit.config.reorder = false;
+context.QUnit.config.reorder = false;
-// override the log function to output back to the parent process
-sandbox.QUnit.log(function(data) {
+// add event listeners to the qunit test events
+context.QUnit.log(function(data) {
data.test = this.config.current.testName;
data.module = currentmodule;
process.send({
event : "assertionDone",
data : data
});
});
-
-// start test
-sandbox.QUnit.testStart(function(data) {
+context.QUnit.testStart(function(data) {
// use last module name if no module name defined
currentmodule = data.module || currentmodule;
data.test = this.config.current.testName;
data.module = currentmodule;
-
process.send({
event : "testStart",
data : data
});
});
-
-// override the testDone function to signal back to the parent process
-sandbox.QUnit.testDone(function(data) {
+context.QUnit.testDone(function(data) {
// use last module name if no module name defined
data.module = data.module || currentmodule;
-
process.send({
event : "testDone",
data : data
});
});
-
-sandbox.QUnit.moduleStart = function(data) {
+context.QUnit.moduleStart(function(data) {
process.send({
event : "moduleStart",
data : data
});
-};
-
-sandbox.QUnit.moduleDone = function(data) {
+});
+context.QUnit.moduleDone(function(data) {
process.send({
event : "moduleDone",
data : data
});
-};
+});
// override the done function to signal back to the parent process that this unit test is done
-sandbox.QUnit.done((function() {
+context.QUnit.done((function() {
var timeout = null, later = function(data) {
timeout = null;
var coverage = {};
- Object.keys(sandbox._$jscoverage).forEach(function(key) {
+ Object.keys(context._$jscoverage).forEach(function(key) {
coverage[key] = {
- lines : Array.prototype.slice.call(sandbox._$jscoverage[key], 0),
- conditionals : sandbox._$jscoverage[key].conditionals || []
+ lines : Array.prototype.slice.call(context._$jscoverage[key], 0),
+ conditionals : context._$jscoverage[key].conditionals || []
};
});
data.coverage = coverage;
+
process.send({
event : "done",
data : data
@@ -136,12 +122,13 @@ function load(src, root) {
// run the source in the sandbox
try {
- vm.runInNewContext(files.join("\n"), sandbox);
+ vm.runInContext(files.join("\n"), context);
} catch (e) {
console.log(e.message);
process.exit(1);
}
}
+
// load dependencies
load(options.source.external, options.dir.vendor);
View
2 build/bridge/coverage-phantom-bridge.html
@@ -18,7 +18,7 @@ <h2 id="qunit-userAgent"></h2>
var ___prev_module = window.module;
window.module = {};
</script>
-<script type="text/javascript" src="../../config.js"></script>
+<script type="text/javascript" src="../.cache.options.json"></script>
<script type="text/javascript">
var ___opts = module.exports;
if(___prev_module == null) {
View
59 build/bridge/qunit-browserstack-bridge.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<title>QUnit Test Suite</title>
+</head>
+
+<body>
+<h1 id="qunit-header">QUnit Test Suite</h1>
+<h2 id="qunit-banner"></h2>
+<div id="qunit-testrunner-toolbar"></div>
+<h2 id="qunit-userAgent"></h2>
+<ol id="qunit-tests"></ol>
+
+<script type="text/javascript">
+var root = this;
+var ___prev_module = window.module;
+window.module = {};
+</script>
+<script type="text/javascript" src="../.cache.options.json"></script>
+<script type="text/javascript">
+var ___opts = module.exports;
+if(___prev_module == null) {
+ window.module = null;
+ try {
+ delete window.module;
+ } catch(e) {
+ window.module = undefined;
+ }
+} else {
+ window.module = ___prev_module;
+}
+</script>
+<script type="text/javascript">
+(function(files) {
+ for ( var i = 0, l = files.length; i < l; i++) {
+ document.write("\n" + '<scr' + 'ipt type="text/javascript" src="../../vendor/' + files[i] + '"></scr' + 'ipt>');
+ }
+})(this.___opts.external || []);
+</script>
+<script type="text/javascript">
+(function(files) {
+ for ( var i = 0, l = files.length; i < l; i++) {
+ document.write("\n" + '<scr' + 'ipt type="text/javascript" src="../../src/' + files[i] + '"></scr' + 'ipt>');
+ }
+})(this.___opts.src);
+</script>
+<script type="text/javascript" src="/socket.io/socket.io.js"></script>
+<script type="text/javascript" src="../qunit/qunit.js"></script>
+<script type="text/javascript" src="qunit-browserstack-hooks.js"></script>
+<script type="text/javascript">
+(function(files) {
+ for ( var i = 0, l = files.length; i < l; i++) {
+ document.write("\n" + '<scr' + 'ipt type="text/javascript" src="../../test/' + files[i] + '"></scr' + 'ipt>');
+ }
+})(this.___opts.unit);
+</script>
+</body>
+</html>
View
91 build/bridge/qunit-browserstack-hooks.js
@@ -0,0 +1,91 @@
+(function() {
+ var currentmodule = null, currenttest = null;
+
+ // Run tests in the order they were defined.
+ QUnit.config.reorder = false;
+ // Run tests in series.
+ QUnit.config.autorun = false;
+
+ // keep reference to original functions
+ var original_log = QUnit.log,
+ // original test hooks
+ original_testStart = QUnit.testStart, original_testDone = QUnit.testDone,
+ // original module hooks
+ original_moduleStart = QUnit.moduleStart, original_moduleDone = QUnit.moduleDone,
+ // original done hook
+ original_done = QUnit.done;
+
+ var socket = io.connect();
+ socket.emit("browserConnect", {
+ ua : navigator.userAgent
+ });
+
+ // pass data to the process
+ function send(data) {
+ data.ua = navigator.userAgent;
+ socket.emit(data.event, data.data);
+ if (data.event === "done") {
+ socket.disconnect();
+ }
+ }
+
+ QUnit.log = function(data) {
+ if (data.message === "[dataect Object], undefined:undefined") {
+ return;
+ }
+ data.module = currentmodule;
+ data.test = currenttest;
+ data.actual = QUnit.jsDump.parse(data.actual);
+ data.expected = QUnit.jsDump.parse(data.expected);
+ send({
+ event : "assertionDone",
+ data : data
+ });
+ original_log.apply(this, arguments);
+ };
+
+ QUnit.testStart = function(data) {
+ currentmodule = data.module || currentmodule;
+ currenttest = data.name || currenttest;
+
+ send({
+ event : "testStart",
+ data : data
+ });
+ original_testStart.apply(this, arguments);
+ };
+
+ QUnit.testDone = function(data) {
+ data.module = data.module || currentmodule;
+
+ send({
+ event : "testDone",
+ data : data
+ });
+ original_testDone.apply(this, arguments);
+ };
+
+ QUnit.moduleStart = function(data) {
+ send({
+ event : "moduleStart",
+ data : data
+ });
+ original_moduleStart.apply(this, arguments);
+ };
+
+ QUnit.moduleDone = function(data) {
+ send({
+ event : "moduleDone",
+ data : data
+ });
+ original_moduleDone.apply(this, arguments);
+ };
+
+ QUnit.done = function(data) {
+ send({
+ event : "done",
+ data : data
+ });
+ original_done.apply(this, arguments);
+ };
+})();
View
2 build/bridge/qunit-coverage-bridge.html
@@ -7,7 +7,7 @@
this.___prev_module = this.module;
this.module = {};
</script>
-<script type="text/javascript" src="../../config.js"></script>
+<script type="text/javascript" src="../.cache.options.json"></script>
<script type="text/javascript">
this.___opts = this.module.exports;
if(this.___prev_module === undefined) {
View
57 build/bridge/qunit-node-bridge.js
@@ -17,86 +17,71 @@ var sandbox = {
setInterval : setInterval,
clearTimeout : clearTimeout,
clearInterval : clearInterval,
- console : console,
- Object : Object,
- Function : Function,
- Boolean : Boolean,
- Number : Number,
- String : String,
- RegExp : RegExp,
- Array : Array,
- Date : Date,
- Error : Error
+ console : console
};
-// window is a circualr reference
+// window/global/root is a circualr reference
sandbox.window = sandbox;
+sandbox.global = sandbox;
+sandbox.root = sandbox;
+
+// create a new context
+var context = vm.createContext(sandbox);
try {
- vm.runInNewContext(fs.readFileSync(path.join(__dirname, "..", "qunit/qunit.js"), "utf8"), sandbox);
+ vm.runInContext(fs.readFileSync(path.join(__dirname, "..", "qunit/qunit.js"), "utf8"), context);
} catch (err) {
process.exit(1);
}
-// keep a reference to the "root" variable
-sandbox.root = sandbox.window;
-
// have a global reference to QUnit within the sandbox
-sandbox.QUnit = sandbox.exports;
-sandbox.exports = {};
+context.QUnit = context.exports;
+context.exports = {};
// don't have qunit reorder tests
-sandbox.QUnit.config.reorder = false;
+context.QUnit.config.reorder = false;
-// override the log function to output back to the parent process
-sandbox.QUnit.log(function(data) {
+// add event listeners to the qunit test events
+context.QUnit.log(function(data) {
data.test = this.config.current.testName;
data.module = currentmodule;
process.send({
event : "assertionDone",
data : data
});
});
-
-// start test
-sandbox.QUnit.testStart(function(data) {
+context.QUnit.testStart(function(data) {
// use last module name if no module name defined
currentmodule = data.module || currentmodule;
data.test = this.config.current.testName;
data.module = currentmodule;
-
process.send({
event : "testStart",
data : data
});
});
-
-// override the testDone function to signal back to the parent process
-sandbox.QUnit.testDone(function(data) {
+context.QUnit.testDone(function(data) {
// use last module name if no module name defined
data.module = data.module || currentmodule;
-
process.send({
event : "testDone",
data : data
});
});
-
-sandbox.QUnit.moduleStart = function(data) {
+context.QUnit.moduleStart(function(data) {
process.send({
event : "moduleStart",
data : data
});
-};
-
-sandbox.QUnit.moduleDone = function(data) {
+});
+context.QUnit.moduleDone(function(data) {
process.send({
event : "moduleDone",
data : data
});
-};
+});
// override the done function to signal back to the parent process that this unit test is done
-sandbox.QUnit.done((function() {
+context.QUnit.done((function() {
var timeout = null, later = function(data) {
timeout = null;
process.send({
@@ -127,7 +112,7 @@ function load(src, root) {
// run the source in the sandbox
try {
- vm.runInNewContext(files.join("\n"), sandbox);
+ vm.runInContext(files.join("\n"), context);
} catch (e) {
console.log(e.message);
process.exit(1);
View
2 build/bridge/qunit-phantom-bridge.html
@@ -18,7 +18,7 @@ <h2 id="qunit-userAgent"></h2>
var ___prev_module = window.module;
window.module = {};
</script>
-<script type="text/javascript" src="../../config.js"></script>
+<script type="text/javascript" src="../.cache.options.json"></script>
<script type="text/javascript">
var ___opts = module.exports;
if(___prev_module == null) {
View
349 build/build.js
@@ -40,61 +40,12 @@ function gzip(data, callback) {
var Build = Classify.create({
__static_ : {
- defaultOptions : {
- name : "build",
- pkg : "package.json",
- version : "0.0.0",
- wrap : {},
- src : [],
- unit : [],
- perf : [],
- docs : [],
- external : [],
- env : {},
- lint : {},
- min : {},
- doc : {},
- build : "clean lint unit size concat min"
- },
- parseParams : function(params) {
- var opts = {
- build : [],
- subtract : []
- };
- params.forEach(function(arg) {
- // push which build options we want
- if (/^\w+$/.test(arg)) {
- opts.build.push(arg);
- return;
- }
- // push which build options we want to remove from the default build
- if (/^-\w+$/.test(arg)) {
- opts.subtract.push(arg);
- return;
- }
- // which options we want to parse for src, unit, perf, external
- if (/^-?\w+=/.test(arg)) {
- var parts = arg.split("="), name = parts.shift();
- opts[name] = parts.join("=").split(",");
- return;
- }
- });
- return opts;
- },
- build : function(options) {
- return Build(options, Array.prototype.slice.call(process.argv, 2)).build();
+ build : function(config) {
+ return Build(config || require(__DIR__ + "/config.js")).build();
}
},
- init : function(options, params) {
- // merge the default options into the options array
- Object.keys(Build.defaultOptions).forEach(function(key) {
- if (options[key] == null) {
- options[key] = Build.defaultOptions[key];
- }
- });
- this.options = options;
- this.params = Build.parseParams(params);
- this.modifyOptionsFromParams();
+ init : function(config) {
+ this.sourceCache = {};
this.dir = {
base : __DIR__,
build : __DIR__ + "/build",
@@ -106,44 +57,201 @@ var Build = Classify.create({
doc : __DIR__ + "/docs",
vendor : __DIR__ + "/vendor"
};
- this.sourceCache = {};
+
+ // default values for file sources
+ this.wrap = {};
+ this.src = [];
+ this.unit = [];
+ this.perf = [];
+ this.external = [];
+ this.env = {};
+ this.name = "";
+ this.version = "0.0.0";
+ this.repoUrl = "";
+
+ this.optStore = null;
+ this.options = {};
+ this.taskOptions = {};
+ this.replaceTokens = [];
+ this.currentStep = null;
+ this.defaultTasks = [];
+
+ // call the config function to populate the internal options
+ config(this);
+
+ this.writeParsedOptions();
},
- modifyOptionsFromParams : function() {
- var build = cArray().getNewObject(this.options.build ? this.options.build.split(" ") : []);
- if (this.params.build.length > 0) {
- build = cArray().getNewObject(this.params.build);
- } else if (this.params.subtract.length > 0) {
- build = build.diff(this.params.subtract);
+ setNameVersion : function(name, version) {
+ this.name = name;
+ this.version = version || "0.0.0";
+ return this;
+ },
+ setRepoName : function(repo) {
+ this.repoUrl = repo;
+ return this;
+ },
+ addSourceFile : function() {
+ this.src.push.apply(this.src, Array.isArray(arguments[0]) ? arguments[0] : arguments);
+ return this;
+ },
+ addUnitTestFile : function() {
+ this.unit.push.apply(this.unit, Array.isArray(arguments[0]) ? arguments[0] : arguments);
+ return this;
+ },
+ addBenchmarkFile : function() {
+ this.perf.push.apply(this.perf, Array.isArray(arguments[0]) ? arguments[0] : arguments);
+ return this;
+ },
+ addExternalFile : function() {
+ this.external.push.apply(this.external, Array.isArray(arguments[0]) ? arguments[0] : arguments);
+ return this;
+ },
+ addCopyright : function() {
+ if (!this.wrap.copy) {
+ this.wrap.copy = [];
+ }
+ this.wrap.copy.push.apply(this.wrap.copy, Array.isArray(arguments[0]) ? arguments[0] : arguments);
+ return this;
+ },
+ addIntro : function() {
+ if (!this.wrap.intro) {
+ this.wrap.intro = [];
+ }
+ this.wrap.intro.push.apply(this.wrap.intro, Array.isArray(arguments[0]) ? arguments[0] : arguments);
+ return this;
+ },
+ addOutro : function() {
+ if (!this.wrap.outro) {
+ this.wrap.outro = [];
}
+ this.wrap.outro.push.apply(this.wrap.outro, Array.isArray(arguments[0]) ? arguments[0] : arguments);
+ return this;
+ },
+ addReplaceToken : function(name, value) {
+ this.replaceTokens.push({
+ name : name,
+ value : value
+ });
+ return this;
+ },
+ enableEnvironment : function() {
+ var self = this;
+ Array.prototype.slice.call(Array.isArray(arguments[0]) ? arguments[0] : arguments, 0).forEach(function(env) {
+ self.env[env] = true;
+ });
+ return this;
+ },
+ addTaskOptions : function(name, options) {
+ this.taskOptions[name] = options || {};
+ return this;
+ },
+ addOption : function(name, options) {
+ this.options[name] = options || {};
+ return this;
+ },
+ setDefaultTasks : function() {
+ this.defaultTasks.push.apply(this.defaultTasks, Array.isArray(arguments[0]) ? arguments[0] : arguments);
+ return this;
+ },
+ writeParsedOptions : function() {
+ var data = {};
+ data.name = this.name;
+ data.src = this.src;
+ data.unit = this.unit;
+ data.perf = this.perf;
+ data.external = this.external;
+ this.writeCacheFile("options", data, true, "module.exports = %j;");
+ },
+
+ getOption : function(name) {
+ var self = this, opt = null, temp;
- this.steps = build;
+ // try to pull from the task options first
+ if (this.currentStep !== null && this.taskOptions[this.currentStep]) {
+ // do a nested loop to check the options object
+ temp = this.taskOptions[this.currentStep];
+ name.split(".").some(function(part, i) {
+ // skip the first step if it's name is the same as the step
+ if (i === 0 && part === self.currentStep) {
+ return;
+ }
+ if (temp == null || !temp.hasOwnProperty(part)) {
+ temp = null;
+ return false;
+ }
+ temp = temp[part];
+ });
+ if (temp != null) {
+ return temp;
+ }
+ }
+
+ // do a nested loop to check the options object
+ temp = this.options;
+ name.split(".").some(function(part) {
+ if (temp == null || !temp.hasOwnProperty(part)) {
+ temp = null;
+ return false;
+ }
+ temp = temp[part];
+ });
+ if (temp != null) {
+ return temp;
+ }
+ // check params array
+ Array.prototype.slice.call(process.argv, 2).some(function(param) {
+ if (param.indexOf("--" + name + "=") === 0) {
+ opt = param.replace(new RegExp("^--" + name.replace(/\./g, "\\.") + "="), "").replace(/^["']+|["']+$/g, "");
+ return false;
+ }
+ });
+ if (opt != null) {
+ return opt;
+ }
+ // then check the local config file
+ if (this.optStore === null) {
+ this.optStore = this.readCacheFile("config", true) || {};
+ }
+
+ // do a nested loop to check the options object
+ temp = this.optStore;
+ name.split(".").some(function(part) {
+ if (temp == null || !temp.hasOwnProperty(part)) {
+ temp = null;
+ return false;
+ }
+ temp = temp[part];
+ });
+ if (temp != null) {
+ return temp;
+ }
+ return null;
},
+
getSource : function(callback) {
if (this.sourceCache.full != null) {
callback(this.sourceCache.full);
return;
}
- var self = this, intro = "", outro = "", src = "", data, options = this.options;
- (options.wrap && options.wrap.intro || []).forEach(function(file) {
- intro += fs.readFileSync(self.dir.src + "/" + file, "utf-8");
+ var self = this, intro = "", outro = "", src = "", data;
+ (this.wrap && this.wrap.intro || []).forEach(function(file) {
+ intro += fs.readFileSync(self.dir.src + "/" + file, "utf8");
});
- (options.wrap && options.wrap.outro || []).forEach(function(file) {
- outro += fs.readFileSync(self.dir.src + "/" + file, "utf-8");
+ (this.wrap && this.wrap.outro || []).forEach(function(file) {
+ outro += fs.readFileSync(self.dir.src + "/" + file, "utf8");
});
- (options.src || []).forEach(function(file) {
- src += fs.readFileSync(self.dir.src + "/" + file, "utf-8");
+ this.src.forEach(function(file) {
+ src += fs.readFileSync(self.dir.src + "/" + file, "utf8");
});
data = intro + src + outro;
- data = data.replace(/@VERSION\b/g, options.version);
+ data = data.replace(/@VERSION\b/g, this.version);
data.replace(/@DATE\b/g, (new Date()).toUTCString());
- if (this.options.sourceReplace) {
- var replacer = this.options.sourceReplace;
- Object.keys(replacer).forEach(function(key) {
- data = data.replace(new RegExp("@" + key + "\\b", "g"), replacer[key]);
- });
- }
+
+ this.replaceTokens.forEach(function(token) {
+ data = data.replace(new RegExp("@" + token.name + "\\b", "g"), token.value);
+ });
this.sourceCache.full = data;
callback(data);
},
@@ -155,18 +263,16 @@ var Build = Classify.create({
var parser = require(this.dir.build + "/vendor/uglify/parse-js");
var uglify = require(this.dir.build + "/vendor/uglify/process");
var consolidator = require(this.dir.build + "/vendor/uglify/consolidator");
- var options = this.options;
+ var options = this.getOption("min") || {};
var self = this;
this.getSource(function(src) {
- options.min = options.min || {};
-
- if (options.min.preparse) {
- src = options.min.preparse(src);
+ if (options.preparse) {
+ src = options.preparse(src);
}
// parse code and get the initial AST
- var ast = parser.parse(src, options.min.strict_semicolons || false);
+ var ast = parser.parse(src, options.strict_semicolons || false);
if (options.consolidate) {
ast = consolidator.ast_consolidate(ast);
@@ -176,23 +282,23 @@ var Build = Classify.create({
}
// get a new AST with mangled names
- if (options.min.mangle) {
- ast = uglify.ast_mangle(ast, options.min.mangle);
+ if (options.mangle) {
+ ast = uglify.ast_mangle(ast, options.mangle);
}
// get an AST with compression optimizations
- if (options.min.squeeze) {
- options.min.squeeze.keep_comps = !(options.min.unsafe || false);
- ast = uglify.ast_squeeze(ast, options.min.squeeze);
+ if (options.squeeze) {
+ options.squeeze.keep_comps = !(options.unsafe || false);
+ ast = uglify.ast_squeeze(ast, options.squeeze);
// unsafe optimizations
- if (options.min.unsafe) {
+ if (options.unsafe) {
ast = uglify.ast_squeeze_more(ast);
}
}
// compressed code here
- var data = uglify.gen_code(ast, options.min.generate);
+ var data = uglify.gen_code(ast, options.generate);
self.sourceCache.min = data;
callback(data);
});
@@ -215,48 +321,68 @@ var Build = Classify.create({
callback(this.sourceCache.copyright);
return;
}
- var self = this, copy = "", options = this.options;
- (options.wrap.copy || []).forEach(function(file) {
- copy += fs.readFileSync(self.dir.src + "/" + file, "utf-8");
+ var self = this, copy = "";
+ (this.wrap.copy || []).forEach(function(file) {
+ copy += fs.readFileSync(self.dir.src + "/" + file, "utf8");
});
- copy = copy.replace(/@VERSION\b/g, options.version);
+ copy = copy.replace(/@VERSION\b/g, this.version);
copy = copy.replace(/@DATE\b/g, (new Date()).toUTCString());
- if (this.options.sourceReplace) {
- var replacer = this.options.sourceReplace;
- Object.keys(replacer).forEach(function(key) {
- copy = copy.replace(new RegExp("@" + key + "\\b", "g"), replacer[key]);
- });
- }
+ this.replaceTokens.forEach(function(token) {
+ copy = copy.replace(new RegExp("@" + token.name + "\\b", "g"), token.value);
+ });
this.sourceCache.copyright = copy;
callback(copy);
},
readCacheFile : function(name, callback) {
- fs.readFile(this.dir.build + "/.cache." + name + ".json", "utf8", function(error, data) {
- if (error) {
- callback(null);
- return;
- }
+ var filename = this.dir.build + "/.cache." + name + ".json";
+ if (callback === true) {
try {
- callback(JSON.parse(data));
+ return JSON.parse(fs.readFileSync(filename, "utf8"));
} catch (e) {
- callback(null);
+ return null;
}
- });
+ } else {
+ fs.readFile(filename, "utf8", function(error, data) {
+ if (error) {
+ callback(null);
+ return;
+ }
+ try {
+ callback(JSON.parse(data));
+ } catch (e) {
+ callback(null);
+ }
+ });
+ }
},
- writeCacheFile : function(name, data, callback) {
- fs.writeFile(this.dir.build + "/.cache." + name + ".json", JSON.stringify(data, true), "utf8", function() {
- callback();
- });
+ writeCacheFile : function(name, data, callback, format) {
+ var filename = this.dir.build + "/.cache." + name + ".json";
+ if (callback === true) {
+ fs.writeFileSync(filename, util.format(format || "%j", data), "utf8");
+ } else {
+ fs.writeFile(filename, util.format(format || "%j", data), "utf8", function() {
+ callback();
+ });
+ }
},
build : function() {
+ var steps = [];
+ Array.prototype.slice.call(process.argv, 2).forEach(function(arg) {
+ // push which build options we want
+ if (/^\w+$/.test(arg)) {
+ steps.push(arg);
+ }
+ });
+ this.steps = cArray().getNewObject(steps.length === 0 ? (this.defaultTasks || []) : steps);
this.startTime = new Date();
this.steps.serialEach(this.processStep, this.onComplete, this);
},
processStep : function(next, step, index) {
var self = this;
this.time = (+new Date());
+ this.currentStep = step.toLowerCase();
try {
- require(this.dir.build + "/module/" + step.toLowerCase())(this, function(data) {
+ require(this.dir.build + "/module/" + this.currentStep + ".js")(this, function(data) {
data = data || {};
data.name = step;
if (!data.time) {
@@ -268,6 +394,7 @@ var Build = Classify.create({
return;
}
self.printLine("Finished in " + self.color((data.time / 1000).toFixed(3), 171) + " seconds.\n");
+ self.currentStep = null;
next();
});
} catch (e) {
View
38 build/lib/benchmark-phantom-hooks.js
@@ -1,38 +0,0 @@
-(function(window, tests) {
- tests.on("add", function(e) {
- // bind events
- e.target.on("complete", function(e) {
- var data = {
- id : this.id,
- name : this.name,
- stats : this.stats,
- times : this.times,
- count : this.count,
- cycles : this.cycles,
- hz : this.hz
- };
- if (this.error) {
- data.error = this.error.message;
- }
-
- alert(JSON.stringify({
- event : "testDone",
- data : data
- }));
- });
- });
-
- tests.on("complete", function() {
- alert(JSON.stringify({
- event : "done",
- data : {}
- }));
- });
-
- // wrapper function to add a test group
- window.Benchmark.test = function(group, testGroup) {
- testGroup(function(name, test) {
- tests.add.call(tests, group + "." + name, test);
- });
- };
-})(window, ___benchmarks);
View
58 build/lib/highlight.javascript.js
@@ -1,58 +0,0 @@
-/*
-Language: JavaScript
-*/
-
-module.exports = function(hljs){
-return {
- defaultMode: {
- keywords: {
- keyword:
- 'in if for while finally var new function do return void else break catch ' +
- 'instanceof with throw case default try this switch continue typeof delete',
- literal:
- 'true false null undefined NaN Infinity'
- },
- contains: [
- hljs.APOS_STRING_MODE,
- hljs.QUOTE_STRING_MODE,
- hljs.C_LINE_COMMENT_MODE,
- hljs.C_BLOCK_COMMENT_MODE,
- hljs.C_NUMBER_MODE,
- { // regexp container
- begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*',
- keywords: 'return throw case',
- contains: [
- hljs.C_LINE_COMMENT_MODE,
- hljs.C_BLOCK_COMMENT_MODE,
- {
- className: 'regexp',
- begin: '/', end: '/[gim]*',
- contains: [{begin: '\\\\/'}]
- }
- ],
- relevance: 0
- },
- {
- className: 'function',
- beginWithKeyword: true, end: '{',
- keywords: 'function',
- contains: [
- {
- className: 'title', begin: '[A-Za-z$_][0-9A-Za-z$_]*'
- },
- {
- className: 'params',
- begin: '\\(', end: '\\)',
- contains: [
- hljs.C_LINE_COMMENT_MODE,
- hljs.C_BLOCK_COMMENT_MODE
- ],
- illegal: '["\'\\(]'
- }
- ],
- illegal: '\\[|%'
- }
- ]
- }
-};
-};
View
663 build/lib/highlight.js
@@ -1,663 +0,0 @@
-/*
-Syntax highlighting with language autodetection.
-http://softwaremaniacs.org/soft/highlight/
-*/
-
-var hljs = module.exports.hljs = new function() {
-
- /* Utility functions */
-
- function escape(value) {
- return value.replace(/&/gm, '&amp;').replace(/</gm, '&lt;');
- }
-
- function langRe(language, value, global) {
- return RegExp(
- value,
- 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '')
- );
- }
-
- function findCode(pre) {
- for (var i = 0; i < pre.childNodes.length; i++) {
- var node = pre.childNodes[i];
- if (node.nodeName == 'CODE') {
- return node;
- }
- if (!(node.nodeType == 3 && node.nodeValue.match(/\s+/))) {
- break;
- }
- }
- }
-
- function blockText(block, ignoreNewLines) {
- var result = '';
- for (var i = 0; i < block.childNodes.length; i++) {
- if (block.childNodes[i].nodeType == 3) {
- var chunk = block.childNodes[i].nodeValue;
- if (ignoreNewLines){chunk = chunk.replace(/\n/g, '');}
- result += chunk;
- } else if (block.childNodes[i].nodeName == 'BR'){result += '\n';} else {result += blockText(block.childNodes[i]);}
- }
- // Thank you, MSIE...
- if (/MSIE [678]/.test(navigator.userAgent)){result = result.replace(/\r/g, '\n');}
- return result;
- }
-
- function blockLanguage(block) {
- var classes = block.className.split(/\s+/);
- classes = classes.concat(block.parentNode.className.split(/\s+/));
- for (var i = 0; i < classes.length; i++) {
- var class_ = classes[i].replace(/^language-/, '');
- if (languages[class_] || class_ == 'no-highlight') {
- return class_;
- }
- }
- }
-
- /* Stream merging */
-
- function nodeStream(node) {
- var result = [];
- (function _nodeStream(node, offset) {
- for (var i = 0; i < node.childNodes.length; i++) {
- if (node.childNodes[i].nodeType == 3){offset += node.childNodes[i].nodeValue.length;}else if (node.childNodes[i].nodeName == 'BR'){offset += 1;}else if (node.childNodes[i].nodeType == 1) {
- result.push({
- event: 'start',
- offset: offset,
- node: node.childNodes[i]
- });
- offset = _nodeStream(node.childNodes[i], offset);
- result.push({
- event: 'stop',
- offset: offset,
- node: node.childNodes[i]
- });
- }
- }
- return offset;
- })(node, 0);
- return result;
- }
-
- function mergeStreams(stream1, stream2, value) {
- var processed = 0;
- var result = '';
- var nodeStack = [];
-
- function selectStream() {
- if (stream1.length && stream2.length) {
- if (stream1[0].offset != stream2[0].offset) {
- return (stream1[0].offset < stream2[0].offset) ? stream1 : stream2;
- } else {
- /*
- To avoid starting the stream just before it should stop the order is
- ensured that stream1 always starts first and closes last:
-
- if (event1 == 'start' && event2 == 'start')
- return stream1;
- if (event1 == 'start' && event2 == 'stop')
- return stream2;
- if (event1 == 'stop' && event2 == 'start')
- return stream1;
- if (event1 == 'stop' && event2 == 'stop')
- return stream2;
-
- ... which is collapsed to:
- */
- return stream2[0].event == 'start' ? stream1 : stream2;
- }
- } else {
- return stream1.length ? stream1 : stream2;
- }
- }
-
- function open(node) {
- var result = '<' + node.nodeName.toLowerCase();
- for (var i = 0; i < node.attributes.length; i++) {
- var attribute = node.attributes[i];
- result += ' ' + attribute.nodeName.toLowerCase();
- if (attribute.value !== undefined && attribute.value !== false && attribute.value !== null) {
- result += '="' + escape(attribute.value) + '"';
- }
- }
- return result + '>';
- }
-
- while (stream1.length || stream2.length) {
- var current = selectStream().splice(0, 1)[0];
- result += escape(value.substr(processed, current.offset - processed));
- processed = current.offset;
- if ( current.event == 'start') {
- result += open(current.node);
- nodeStack.push(current.node);
- } else if (current.event == 'stop') {
- var node, i = nodeStack.length;
- do {
- i--;
- node = nodeStack[i];
- result += ('</' + node.nodeName.toLowerCase() + '>');
- } while (node != current.node);
- nodeStack.splice(i, 1);
- while (i < nodeStack.length) {
- result += open(nodeStack[i]);
- i++;
- }
- }
- }
- return result + escape(value.substr(processed));
- }
-
- /* Initialization */
-
- function compileModes() {
-
- function compileMode(mode, language, is_default) {
- if (mode.compiled) {
- return;
- }
-
- var keywords = []; // used later with beginWithKeyword but filled as a side-effect of keywords compilation
- if (mode.keywords) {
- var compiled_keywords = {};
-
- function flatten(className, str) {
- var group = str.split(' ');
- for (var i = 0; i < group.length; i++) {
- var pair = group[i].split('|');
- compiled_keywords[pair[0]] = [className, pair[1] ? Number(pair[1]) : 1];
- keywords.push(pair[0]);
- }
- }
-
- mode.lexemsRe = langRe(language, mode.lexems || hljs.IDENT_RE, true);
- if (typeof mode.keywords == 'string') { // string
- flatten('keyword', mode.keywords)
- } else {
- for (var className in mode.keywords) {
- if (!mode.keywords.hasOwnProperty(className)) {
- continue;
- }
- flatten(className, mode.keywords[className]);
- }
- }
- mode.keywords = compiled_keywords;
- }
- if (!is_default) {
- if (mode.beginWithKeyword) {
- mode.begin = '\\b(' + keywords.join('|') + ')\\s';
- }
- mode.beginRe = langRe(language, mode.begin ? mode.begin : '\\B|\\b');
- if (!mode.end && !mode.endsWithParent){mode.end = '\\B|\\b';}
- if (mode.end){mode.endRe = langRe(language, mode.end);}
- }
- if (mode.illegal){mode.illegalRe = langRe(language, mode.illegal);}
- if (mode.relevance === undefined){mode.relevance = 1;}
- if (!mode.contains) {
- mode.contains = [];
- }
- // compiled flag is set before compiling submodes to avoid self-recursion
- // (see lisp where quoted_list contains quoted_list)
- mode.compiled = true;
- for (var i = 0; i < mode.contains.length; i++) {
- if (mode.contains[i] == 'self') {
- mode.contains[i] = mode;
- }
- compileMode(mode.contains[i], language, false);
- }
- if (mode.starts) {
- compileMode(mode.starts, language, false);
- }
- }
-
- for (var i in languages) {
- if (!languages.hasOwnProperty(i)) {
- continue;
- }
- compileMode(languages[i].defaultMode, languages[i], true);
- }
- }
-
- /*
- Core highlighting function. Accepts a language name and a string with the
- code to highlight. Returns an object with the following properties:
-
- - relevance (int)
- - keyword_count (int)
- - value (an HTML string with highlighting markup)
-
- */
- function highlight(language_name, value) {
- if (!compileModes.called) {
- compileModes();
- compileModes.called = true;
- }
-
- function subMode(lexem, mode) {
- for (var i = 0; i < mode.contains.length; i++) {
- var match = mode.contains[i].beginRe.exec(lexem);
- if (match && match.index == 0) {
- return mode.contains[i];
- }
- }
- }
-
- function endOfMode(mode_index, lexem) {
- if (modes[mode_index].end && modes[mode_index].endRe.test(lexem)) {
- return 1;
- }
- if (modes[mode_index].endsWithParent) {
- var level = endOfMode(mode_index - 1, lexem);
- return level ? level + 1 : 0;
- }
- return 0;
- }
-
- function isIllegal(lexem, mode) {
- return mode.illegal && mode.illegalRe.test(lexem);
- }
-
- function compileTerminators(mode, language) {
- var terminators = [];
-
- for (var i = 0; i < mode.contains.length; i++) {
- terminators.push(mode.contains[i].begin);
- }
-
- var index = modes.length - 1;
- do {
- if (modes[index].end) {
- terminators.push(modes[index].end);
- }
- index--;
- } while (modes[index + 1].endsWithParent);
-
- if (mode.illegal) {
- terminators.push(mode.illegal);
- }
-
- return terminators.length ? langRe(language, terminators.join('|'), true) : null;
- }
-
- function eatModeChunk(value, index) {
- var mode = modes[modes.length - 1];
- if (mode.terminators === undefined) {
- mode.terminators = compileTerminators(mode, language);
- }
- var match;
- if (mode.terminators) {
- mode.terminators.lastIndex = index;
- match = mode.terminators.exec(value);
- }
- return match ? [value.substr(index, match.index - index), match[0], false] : [value.substr(index), '', true];
- }
-
- function keywordMatch(mode, match) {
- var match_str = language.case_insensitive ? match[0].toLowerCase() : match[0];
- var value = mode.keywords[match_str];
- if (value && value instanceof Array) {
- return value;
- }
- return false;
- }
-
- function processKeywords(buffer, mode) {
- buffer = escape(buffer);
- if (!mode.keywords) {
- return buffer;
- }
- var result = '';
- var last_index = 0;
- mode.lexemsRe.lastIndex = 0;
- var match = mode.lexemsRe.exec(buffer);
- while (match) {
- result += buffer.substr(last_index, match.index - last_index);
- var keyword_match = keywordMatch(mode, match);
- if (keyword_match) {
- keyword_count += keyword_match[1];
- result += '<span class="'+ keyword_match[0] +'">' + match[0] + '</span>';
- } else {
- result += match[0];
- }
- last_index = mode.lexemsRe.lastIndex;
- match = mode.lexemsRe.exec(buffer);
- }
- return result + buffer.substr(last_index, buffer.length - last_index);
- }
-
- function processSubLanguage(buffer, mode) {
- var result;
- if (mode.subLanguage == '') {
- result = highlightAuto(buffer);
- } else {
- result = highlight(mode.subLanguage, buffer);
- }
- // Counting embedded language score towards the host language may be disabled
- // with zeroing the containing mode relevance. Usecase in point is Markdown that
- // allows XML everywhere and makes every XML snippet to have a much larger Markdown
- // score.
- if (mode.relevance > 0) {
- keyword_count += result.keyword_count;
- relevance += result.relevance;
- }
- return '<span class="' + result.language + '">' + result.value + '</span>';
- }
-
- function processBuffer(buffer, mode) {
- if (mode.subLanguage && languages[mode.subLanguage] || mode.subLanguage == '') {
- return processSubLanguage(buffer, mode);
- } else {
- return processKeywords(buffer, mode);
- }
- }
-
- function startNewMode(mode, lexem) {
- var markup = mode.className?'<span class="' + mode.className + '">':'';
- if (mode.returnBegin) {
- result += markup;
- mode.buffer = '';
- } else if (mode.excludeBegin) {
- result += escape(lexem) + markup;
- mode.buffer = '';
- } else {
- result += markup;
- mode.buffer = lexem;
- }
- modes.push(mode);
- relevance += mode.relevance;
- }
-
- function processModeInfo(buffer, lexem, end) {
- var current_mode = modes[modes.length - 1];
- if (end) {
- result += processBuffer(current_mode.buffer + buffer, current_mode);
- return false;
- }
-
- var new_mode = subMode(lexem, current_mode);
- if (new_mode) {
- result += processBuffer(current_mode.buffer + buffer, current_mode);
- startNewMode(new_mode, lexem);
- return new_mode.returnBegin;
- }
-
- var end_level = endOfMode(modes.length - 1, lexem);
- if (end_level) {
- var markup = current_mode.className?'</span>':'';
- if (current_mode.returnEnd) {
- result += processBuffer(current_mode.buffer + buffer, current_mode) + markup;
- } else if (current_mode.excludeEnd) {
- result += processBuffer(current_mode.buffer + buffer, current_mode) + markup + escape(lexem);
- } else {
- result += processBuffer(current_mode.buffer + buffer + lexem, current_mode) + markup;
- }
- while (end_level > 1) {
- markup = modes[modes.length - 2].className?'</span>':'';
- result += markup;
- end_level--;
- modes.length--;
- }
- var last_ended_mode = modes[modes.length - 1];
- modes.length--;
- modes[modes.length - 1].buffer = '';
- if (last_ended_mode.starts) {
- startNewMode(last_ended_mode.starts, '');
- }
- return current_mode.returnEnd;
- }
-
- if (isIllegal(lexem, current_mode)) {
- throw 'Illegal';
- }
- }
-
- var language = languages[language_name];
- var modes = [language.defaultMode];
- var relevance = 0;
- var keyword_count = 0;
- var result = '';
- try {
- var mode_info, index = 0;
- language.defaultMode.buffer = '';
- do {
- mode_info = eatModeChunk(value, index);
- var return_lexem = processModeInfo(mode_info[0], mode_info[1], mode_info[2]);
- index += mode_info[0].length;
- if (!return_lexem) {
- index += mode_info[1].length;
- }
- } while (!mode_info[2]);
- return {
- relevance: relevance,
- keyword_count: keyword_count,
- value: result,
- language: language_name
- };
- } catch (e) {
- if (e == 'Illegal') {
- return {
- relevance: 0,
- keyword_count: 0,
- value: escape(value)
- };
- } else {
- throw e;
- }
- }
- }
-
- /*
- Highlighting with language detection. Accepts a string with the code to
- highlight. Returns an object with the following properties:
-
- - language (detected language)
- - relevance (int)
- - keyword_count (int)
- - value (an HTML string with highlighting markup)
- - second_best (object with the same structure for second-best heuristically
- detected language, may be absent)
-
- */
- function highlightAuto(text) {
- var result = {
- keyword_count: 0,
- relevance: 0,
- value: escape(text)
- };
- var second_best = result;
- for (var key in languages) {
- if (!languages.hasOwnProperty(key)) {
- continue;
- }
- var current = highlight(key, text);
- current.language = key;
- if (current.keyword_count + current.relevance > second_best.keyword_count + second_best.relevance) {
- second_best = current;
- }
- if (current.keyword_count + current.relevance > result.keyword_count + result.relevance) {
- second_best = result;
- result = current;
- }
- }
- if (second_best.language) {
- result.second_best = second_best;
- }
- return result;
- }
-
- /*
- Post-processing of the highlighted markup:
-
- - replace TABs with something more useful
- - replace real line-breaks with '<br>' for non-pre containers
-
- */
- function fixMarkup(value, tabReplace, useBR) {
- if (tabReplace) {
- value = value.replace(/^((<[^>]+>|\t)+)/gm, function(match, p1, offset, s) {
- return p1.replace(/\t/g, tabReplace);
- });
- }
- if (useBR) {
- value = value.replace(/\n/g, '<br>');
- }
- return value;
- }
-
- /*
- Applies highlighting to a DOM node containing code. Accepts a DOM node and
- two optional parameters for fixMarkup.
- */
- function highlightBlock(block, tabReplace, useBR) {
- var text = blockText(block, useBR);
- var language = blockLanguage(block);
- var result, pre;
- if (language == 'no-highlight') {
- return;
- }
- if (language) {
- result = highlight(language, text);
- } else {
- result = highlightAuto(text);
- language = result.language;
- }
- var original = nodeStream(block);
- if (original.length) {
- pre = document.createElement('pre');
- pre.innerHTML = result.value;
- result.value = mergeStreams(original, nodeStream(pre), text);
- }
- result.value = fixMarkup(result.value, tabReplace, useBR);
-
- var class_name = block.className;
- if (!class_name.match('(\\s|^)(language-)?' + language + '(\\s|$)')) {
- class_name = class_name ? (class_name + ' ' + language) : language;
- }
- if (/MSIE [678]/.test(navigator.userAgent) && block.tagName == 'CODE' && block.parentNode.tagName == 'PRE') {
- // This is for backwards compatibility only. IE needs this strange
- // hack becasue it cannot just cleanly replace <code> block contents.
- pre = block.parentNode;
- var container = document.createElement('div');
- container.innerHTML = '<pre><code>' + result.value + '</code></pre>';
- block = container.firstChild.firstChild;
- container.firstChild.className = pre.className;
- pre.parentNode.replaceChild(container.firstChild, pre);
- } else {
- block.innerHTML = result.value;
- }
- block.className = class_name;
- block.result = {
- language: language,
- kw: result.keyword_count,
- re: result.relevance
- };
- if (result.second_best) {
- block.second_best = {
- language: result.second_best.language,
- kw: result.second_best.keyword_count,
- re: result.second_best.relevance
- };
- }
- }
-
- /*
- Applies highlighting to all <pre><code>..</code></pre> blocks on a page.
- */
- function initHighlighting() {
- if (initHighlighting.called) {
- return;
- }
- initHighlighting.called = true;
- var pres = document.getElementsByTagName('pre');
- for (var i = 0; i < pres.length; i++) {
- var code = findCode(pres[i]);
- if (code){highlightBlock(code, hljs.tabReplace);}
- }
- }
-
- /*
- Attaches highlighting to the page load event.
- */
- function initHighlightingOnLoad() {
- if (window.addEventListener) {
- window.addEventListener('DOMContentLoaded', initHighlighting, false);
- window.addEventListener('load', initHighlighting, false);
- } else if (window.attachEvent){window.attachEvent('onload', initHighlighting);} else {window.onload = initHighlighting;}
- }
-
- var languages = {}; // a shortcut to avoid writing "this." everywhere
-
- /* Interface definition */
-
- this.LANGUAGES = languages;
- this.highlight = highlight;
- this.highlightAuto = highlightAuto;
- this.fixMarkup = fixMarkup;
- this.highlightBlock = highlightBlock;
- this.initHighlighting = initHighlighting;
- this.initHighlightingOnLoad = initHighlightingOnLoad;
-
- // Common regexps
- this.IDENT_RE = '[a-zA-Z][a-zA-Z0-9_]*';
- this.UNDERSCORE_IDENT_RE = '[a-zA-Z_][a-zA-Z0-9_]*';
- this.NUMBER_RE = '\\b\\d+(\\.\\d+)?';
- this.C_NUMBER_RE = '\\b(0[xX][a-fA-F0-9]+|(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float
- this.BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b...
- this.RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';
-
- // Common modes
- this.BACKSLASH_ESCAPE = {
- begin: '\\\\.', relevance: 0
- };
- this.APOS_STRING_MODE = {
- className: 'string',
- begin: '\'', end: '\'',
- illegal: '\\n',
- contains: [this.BACKSLASH_ESCAPE],
- relevance: 0
- };
- this.QUOTE_STRING_MODE = {
- className: 'string',
- begin: '"', end: '"',
- illegal: '\\n',
- contains: [this.BACKSLASH_ESCAPE],
- relevance: 0
- };
- this.C_LINE_COMMENT_MODE = {
- className: 'comment',
- begin: '//', end: '$'
- };
- this.C_BLOCK_COMMENT_MODE = {
- className: 'comment',
- begin: '/\\*', end: '\\*/'
- };
- this.HASH_COMMENT_MODE = {
- className: 'comment',
- begin: '#', end: '$'
- };
- this.NUMBER_MODE = {
- className: 'number',
- begin: this.NUMBER_RE,
- relevance: 0
- };
- this.C_NUMBER_MODE = {
- className: 'number',
- begin: this.C_NUMBER_RE,
- relevance: 0
- };
- this.BINARY_NUMBER_MODE = {
- className: 'number',
- begin: this.BINARY_NUMBER_RE,
- relevance: 0
- };
-
- // Utility functions
- this.inherit = function(parent, obj) {
- var result = {}
- for (var key in parent)
- result[key] = parent[key];
- if (obj){for (var key in obj)
- result[key] = obj[key];}
- return result;
- }
-}();
View
376 build/module/browserstack.js
@@ -0,0 +1,376 @@
+// include the necessary module
+var fs = require("fs");
+var http = require("http");
+var url = require("url");
+var path = require("path");
+var BrowserStack = require("../vendor/browserstack/browserstack.js");
+var io = require("socket.io");
+// classify library
+var Classify = require("../vendor/classify/classify.min.js");
+// require the special array library
+require("../vendor/classify/classify-array.min.js")(Classify);
+var cArray = Classify("/Array");
+
+var Browser = Classify.create({
+ __static_ : {
+ uaBrowserMatch : {
+ chrome : /chrome\/(\d+(\.\d+)?)/,
+ safari : /version\/(\d+(\.\d+)?)/,
+ opera : /version\/(\d+(\.\d+)?)/,
+ firefox : /firefox\/(\d+(\.\d+)?)/,
+ msie : /msie (\d+(\.\d+)?)/
+ },
+ uaToBrowser : function(ua) {
+ var browser = {
+ browser : "none",
+ version : 0,
+ os : "other"
+ };
+ ua = ua.toLowerCase();
+
+ var osMatches = /(windows|mac|linux)/.exec(ua);
+ if (osMatches == null) {
+ return;
+ }
+ browser.os = osMatches[1];
+
+ var browserMatches = /(chrome|safari|opera|firefox|msie)/.exec(ua);
+ if (browserMatches == null) {
+ return;
+ }
+ browser.browser = browserMatches[1];
+ var versionMatches = this.uaBrowserMatch[browser.browser].exec(ua);
+ if (versionMatches == null) {
+ return;
+ }
+ // clean up
+ if (browser.browser === "msie") {
+ browser.browser = "ie";
+ }
+ if (browser.os === "windows") {
+ browser.os = "win";
+ }
+
+ browser.version = versionMatches[1];
+ return browser.browser + " " + browser.version + " " + browser.os;
+ }
+ },
+ init : function(build, config) {
+ this.os = config.os;
+ this.device = config.device;
+ this.browser = config.browser;
+ this.version = config.version;
+ this.name = (this.browser || this.device) + " " + this.version + " " + this.os;
+ this.build = build;
+ this.id = 0;
+
+ this.runtime = 0;
+ this.failed = 0;
+ this.passed = 0;
+ this.total = 0;
+ this.results = {};
+ },
+ setBrowserStackClient : function(client) {
+ this.browserstack = client;
+ },
+ setSocket : function(socket) {
+ var self = this, index = 0;
+ this.socket = socket;
+ this.build.printLine(this.build.color("= ", 34) + "Browser " + this.build.color(this.name, "bold") + " connected");
+ socket.on("assertionDone", function(data) {
+ data.index = ++index;
+ self.logEvent("assertionDone", data);
+ });
+ socket.on("testStart", function(data) {
+ self.logEvent("testStart", data);
+ });
+ socket.on("testDone", function(data) {
+ self.logEvent("testDone", data);
+ });
+ socket.on("moduleStart", function(data) {
+ self.logEvent("moduleStart", data);
+ });
+ socket.on("moduleDone", function(data) {
+ self.logEvent("moduleDone", data);
+ });
+ socket.on("done", function(data) {
+ self.logEvent("done", data);
+ });
+ },
+ setCallback : function(callback) {
+ this.callback = callback;
+ },
+ start : function() {
+ var self = this;
+ this.browserstack.createWorker({
+ version : this.version,
+ browser : this.browser,
+ os : this.os,
+ url : "http://" + (self.build.getOption("browserstack.ip") || "127.0.0.1") + ":" + parseInt(self.build.getOption("browserstack.port") || 80, 10) + "/build/bridge/qunit-browserstack-bridge.html",
+ timeout : 60
+ }, function(error, worker) {
+ if (error) {
+ self.callback();
+ return;
+ }
+ self.build.printLine(self.build.color("* ", 34) + "Browser " + self.build.color(self.name, "bold") + " initialized");
+ self.id = worker.id;
+ self.onWait();
+ });
+ },
+ stopWorker : function(callback) {
+ var self = this;
+ if (this.id) {
+ this.browserstack.terminateWorker(this.id, function() {
+ self.id = 0;
+ if (callback) {
+ callback();
+ }
+ });
+ } else {
+ if (callback) {
+ callback();
+ }
+ }
+ return this;
+ },
+ onWait : function() {
+ var self = this, i = 0, pattern = [ "|", "/", "-", "\\" ];
+ var runner = function() {
+ self.browserstack.getWorker(self.id, function(error, worker) {
+ if (worker.status !== "running") {
+ self.build.printTemp(pattern[i++ % 4] + " Waiting for: " + self.build.color(self.name, "bold"));
+ setTimeout(runner, 1000);
+ }
+ });
+ };
+ runner();
+ },
+ logEvent : function(type, data) {
+ switch (type) {
+ case "assertionDone":
+ if (data.result === false) {
+ if (!this.results[data.module]) {
+ this.results[data.module] = [];
+ }
+ this.results[data.module].push(data);
+ }
+ break;
+ case "testStart":
+ this.build.printTemp("Running: " + this.name + " " + this.build.color(data.module, "bold") + " " + data.name);
+ break;
+ case "testDone":
+ break;
+ case "moduleStart":
+ break;
+ case "moduleDone":
+ break;
+ case "done":
+ this.build.printLine(this.build.color("\u2714 ", 34) + "Browser " + this.build.color(this.name, "bold") + " completed tests");
+ this.failed = data.failed;
+ this.passed = data.passed;
+ this.total = data.total;
+ this.runtime = data.runtime;
+ this.stopWorker();
+ this.callback();
+ break;
+ }
+ },
+ process : function() {
+ var self = this, build = this.build;
+ build.printLine("Tests results for: " + build.color(this.name, "bold"));
+ Object.keys(this.results).forEach(function(test) {
+ build.printLine(build.color("Module: ", "bold") + test);
+ self.results[test].forEach(function(assertion) {
+ build.printLine(" " + (assertion.result ? build.color("\u2714 ", 34) : build.color("\u2716 ", 160)) + build.color("Test # ", "bold") + assertion.index + "/" + self.total);
+ build.printLine(" " + assertion.message + " [" + build.color(assertion.test, 248) + "]");
+ if (typeof assertion.expected !== "undefined") {
+ build.printLine(" -> " + build.color("Expected: " + assertion.expected, 34));
+ // if test failed, then we need to output the result
+ if (!assertion.result) {
+ build.printLine(" -> " + build.color("Result: " + assertion.actual, 160));
+ }
+ }
+ });
+ });
+
+ if (this.failed > 0) {
+ build.printLine(build.color("\u2716 ", 160) + this.failed + " / " + this.total + " Failed");
+ } else {
+ build.printLine(build.color("\u2714 ", 34) + "All tests [" + this.passed + " / " + this.total + "] passed!");
+ }
+ build.printLine();
+ }
+});
+
+var BrowserList = Classify.create({
+ init : function(build, server) {
+ this.build = build;
+ this.browsers = {};
+ this.validbrowsers = null;
+ this.client = BrowserStack.createClient({
+ username : this.build.getOption("browserstack.username"),
+ password : this.build.getOption("browserstack.password")
+ });
+ },
+ setCallback : function(callback) {
+ this.callback = callback;
+ },
+ setServer : function(server) {
+ this.server = server;
+ },
+ terminateOldWorkers : function() {
+ // kill existing workers
+ var self = this;
+ this.client.getWorkers(function(error, workers) {
+ workers.forEach(function(worker) {
+ self.client.terminateWorker(worker.id, function() {
+ self.build.printTemp("Terminated worker: " + worker.id);
+ });
+ });
+ });
+ },
+ listBrowsers : function(callback) {
+ var self = this;
+ if (this.validbrowsers !== null) {
+ callback(this.validbrowsers);
+ return this;
+ }
+ this.client.getBrowsers(function(error, browsers) {
+ if (error) {
+ throw error;
+ }
+ self.validbrowsers = browsers;
+ callback(browsers);
+ });
+ return this;
+ },
+ addBrowser : function(config) {
+ var browser = new Browser(this.build, config);
+ browser.setBrowserStackClient(this.client);
+ this.browsers[browser.name] = browser;
+ return browser;
+ },
+ getBrowser : function(name) {
+ return this.browsers[name];
+ },
+ start : function() {
+ var self = this, list = cArray().getNewObject(Object.keys(this.browsers));
+ list.threadEach(function(next, name) {
+ var browser = this.getBrowser(name);
+ browser.setCallback(next);
+ browser.start();
+ }, function() {
+ this.server.stop();
+ // output results && kill all the running workers before stopping
+ self.build.printLine();
+ list.threadEach(function(next, name) {
+ var browser = this.getBrowser(name);
+ browser.process();
+ browser.stopWorker(next);
+ }, function() {
+ self.callback();
+ }, this);
+ }, this);
+ }
+});
+
+var Server = Classify.create({
+ __static_ : {
+ contentType : {
+ js : "text/javascript",
+ css : "text/css",
+ html : "text/html"
+ }
+ },
+ init : function(build) {
+ this.build = build;
+ this.server = null;
+ },
+ setList : function(list) {
+ this.list = list;
+ },
+ start : function(callback) {
+ if (this.server) {
+ callback();
+ return this;
+ }
+ var self = this;
+ this.server = http.createServer(function(request, response) {
+ var uri = url.parse(request.url).pathname, filename = path.join(self.build.dir.base, uri);
+ self.build.printTemp(filename);
+ path.exists(filename, function(exists) {
+ if (!exists) {
+ response.writeHead(404, {
+ "Content-Type" : "text/plain"
+ });
+ response.write("404 Not Found\n");
+ response.end();
+ return;
+ }
+
+ if (fs.statSync(filename).isDirectory()) {
+ filename = filename.replace(/\/$/, "") + "/index.html";
+ }
+
+ fs.readFile(filename, "binary", function(err, file) {
+ if (err) {
+ response.writeHead(500, {
+ "Content-Type" : "text/plain"
+ });
+ response.write(err + "