diff --git a/.gitignore b/.gitignore
index e90fa9c..5479997 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,12 @@
# Touch SDK
-/example/touch/
+/examples/touch/
# Generated directories
.ux-ant-cache/
.sass-cache/
.sencha_backup/
-/example/archive
-/example/build
+/examples/archive
+/examples/build
# Generated files
/application/resources/css/
diff --git a/.sencha/package/build-impl.xml b/.sencha/package/build-impl.xml
new file mode 100644
index 0000000..e242abd
--- /dev/null
+++ b/.sencha/package/build-impl.xml
@@ -0,0 +1,358 @@
+
+
+
jasmine.undefined
instead of undefined
, since undefined
is just
- * a plain old variable and may be redefined by somebody else.
- *
- * @private
- */
-jasmine.undefined = jasmine.___undefined___;
-
-/**
- * Show diagnostic messages in the console if set to true
- *
- */
-jasmine.VERBOSE = false;
-
-/**
- * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
- *
- */
-jasmine.DEFAULT_UPDATE_INTERVAL = 250;
-
-/**
- * Maximum levels of nesting that will be included when an object is pretty-printed
- */
-jasmine.MAX_PRETTY_PRINT_DEPTH = 40;
-
-/**
- * Default timeout interval in milliseconds for waitsFor() blocks.
- */
-jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
-
-/**
- * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite.
- * Set to false to let the exception bubble up in the browser.
- *
- */
-jasmine.CATCH_EXCEPTIONS = true;
-
-jasmine.getGlobal = function() {
- function getGlobal() {
- return this;
- }
-
- return getGlobal();
-};
-
-/**
- * Allows for bound functions to be compared. Internal use only.
- *
- * @ignore
- * @private
- * @param base {Object} bound 'this' for the function
- * @param name {Function} function to find
- */
-jasmine.bindOriginal_ = function(base, name) {
- var original = base[name];
- if (original.apply) {
- return function() {
- return original.apply(base, arguments);
- };
- } else {
- // IE support
- return jasmine.getGlobal()[name];
- }
-};
-
-jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
-jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
-jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
-jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
-
-jasmine.MessageResult = function(values) {
- this.type = 'log';
- this.values = values;
- this.trace = new Error(); // todo: test better
-};
-
-jasmine.MessageResult.prototype.toString = function() {
- var text = "";
- for (var i = 0; i < this.values.length; i++) {
- if (i > 0) text += " ";
- if (jasmine.isString_(this.values[i])) {
- text += this.values[i];
- } else {
- text += jasmine.pp(this.values[i]);
- }
- }
- return text;
-};
-
-jasmine.ExpectationResult = function(params) {
- this.type = 'expect';
- this.matcherName = params.matcherName;
- this.passed_ = params.passed;
- this.expected = params.expected;
- this.actual = params.actual;
- this.message = this.passed_ ? 'Passed.' : params.message;
-
- var trace = (params.trace || new Error(this.message));
- this.trace = this.passed_ ? '' : trace;
-};
-
-jasmine.ExpectationResult.prototype.toString = function () {
- return this.message;
-};
-
-jasmine.ExpectationResult.prototype.passed = function () {
- return this.passed_;
-};
-
-/**
- * Getter for the Jasmine environment. Ensures one gets created
- */
-jasmine.getEnv = function() {
- var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
- return env;
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isArray_ = function(value) {
- return jasmine.isA_("Array", value);
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isString_ = function(value) {
- return jasmine.isA_("String", value);
-};
-
-/**
- * @ignore
- * @private
- * @param value
- * @returns {Boolean}
- */
-jasmine.isNumber_ = function(value) {
- return jasmine.isA_("Number", value);
-};
-
-/**
- * @ignore
- * @private
- * @param {String} typeName
- * @param value
- * @returns {Boolean}
- */
-jasmine.isA_ = function(typeName, value) {
- return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
-};
-
-/**
- * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
- *
- * @param value {Object} an object to be outputted
- * @returns {String}
- */
-jasmine.pp = function(value) {
- var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
- stringPrettyPrinter.format(value);
- return stringPrettyPrinter.string;
-};
-
-/**
- * Returns true if the object is a DOM Node.
- *
- * @param {Object} obj object to check
- * @returns {Boolean}
- */
-jasmine.isDomNode = function(obj) {
- return obj.nodeType > 0;
-};
-
-/**
- * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
- *
- * @example
- * // don't care about which function is passed in, as long as it's a function
- * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
- *
- * @param {Class} clazz
- * @returns matchable object of the type clazz
- */
-jasmine.any = function(clazz) {
- return new jasmine.Matchers.Any(clazz);
-};
-
-/**
- * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
- * attributes on the object.
- *
- * @example
- * // don't care about any other attributes than foo.
- * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
- *
- * @param sample {Object} sample
- * @returns matchable object for the sample
- */
-jasmine.objectContaining = function (sample) {
- return new jasmine.Matchers.ObjectContaining(sample);
-};
-
-/**
- * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
- *
- * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
- * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
- *
- * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
- *
- * Spies are torn down at the end of every spec.
- *
- * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
- *
- * @example
- * // a stub
- * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
- *
- * // spy example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- *
- * // actual foo.not will not be called, execution stops
- * spyOn(foo, 'not');
-
- // foo.not spied upon, execution will continue to implementation
- * spyOn(foo, 'not').andCallThrough();
- *
- * // fake example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- *
- * // foo.not(val) will return val
- * spyOn(foo, 'not').andCallFake(function(value) {return value;});
- *
- * // mock example
- * foo.not(7 == 7);
- * expect(foo.not).toHaveBeenCalled();
- * expect(foo.not).toHaveBeenCalledWith(true);
- *
- * @constructor
- * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
- * @param {String} name
- */
-jasmine.Spy = function(name) {
- /**
- * The name of the spy, if provided.
- */
- this.identity = name || 'unknown';
- /**
- * Is this Object a spy?
- */
- this.isSpy = true;
- /**
- * The actual function this spy stubs.
- */
- this.plan = function() {
- };
- /**
- * Tracking of the most recent call to the spy.
- * @example
- * var mySpy = jasmine.createSpy('foo');
- * mySpy(1, 2);
- * mySpy.mostRecentCall.args = [1, 2];
- */
- this.mostRecentCall = {};
-
- /**
- * Holds arguments for each call to the spy, indexed by call count
- * @example
- * var mySpy = jasmine.createSpy('foo');
- * mySpy(1, 2);
- * mySpy(7, 8);
- * mySpy.mostRecentCall.args = [7, 8];
- * mySpy.argsForCall[0] = [1, 2];
- * mySpy.argsForCall[1] = [7, 8];
- */
- this.argsForCall = [];
- this.calls = [];
-};
-
-/**
- * Tells a spy to call through to the actual implemenatation.
- *
- * @example
- * var foo = {
- * bar: function() { // do some stuff }
- * }
- *
- * // defining a spy on an existing property: foo.bar
- * spyOn(foo, 'bar').andCallThrough();
- */
-jasmine.Spy.prototype.andCallThrough = function() {
- this.plan = this.originalValue;
- return this;
-};
-
-/**
- * For setting the return value of a spy.
- *
- * @example
- * // defining a spy from scratch: foo() returns 'baz'
- * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
- *
- * // defining a spy on an existing property: foo.bar() returns 'baz'
- * spyOn(foo, 'bar').andReturn('baz');
- *
- * @param {Object} value
- */
-jasmine.Spy.prototype.andReturn = function(value) {
- this.plan = function() {
- return value;
- };
- return this;
-};
-
-/**
- * For throwing an exception when a spy is called.
- *
- * @example
- * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
- * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
- *
- * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
- * spyOn(foo, 'bar').andThrow('baz');
- *
- * @param {String} exceptionMsg
- */
-jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
- this.plan = function() {
- throw exceptionMsg;
- };
- return this;
-};
-
-/**
- * Calls an alternate implementation when a spy is called.
- *
- * @example
- * var baz = function() {
- * // do some stuff, return something
- * }
- * // defining a spy from scratch: foo() calls the function baz
- * var foo = jasmine.createSpy('spy on foo').andCall(baz);
- *
- * // defining a spy on an existing property: foo.bar() calls an anonymnous function
- * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
- *
- * @param {Function} fakeFunc
- */
-jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
- this.plan = fakeFunc;
- return this;
-};
-
-/**
- * Resets all of a spy's the tracking variables so that it can be used again.
- *
- * @example
- * spyOn(foo, 'bar');
- *
- * foo.bar();
- *
- * expect(foo.bar.callCount).toEqual(1);
- *
- * foo.bar.reset();
- *
- * expect(foo.bar.callCount).toEqual(0);
- */
-jasmine.Spy.prototype.reset = function() {
- this.wasCalled = false;
- this.callCount = 0;
- this.argsForCall = [];
- this.calls = [];
- this.mostRecentCall = {};
-};
-
-jasmine.createSpy = function(name) {
-
- var spyObj = function() {
- spyObj.wasCalled = true;
- spyObj.callCount++;
- var args = jasmine.util.argsToArray(arguments);
- spyObj.mostRecentCall.object = this;
- spyObj.mostRecentCall.args = args;
- spyObj.argsForCall.push(args);
- spyObj.calls.push({object: this, args: args});
- return spyObj.plan.apply(this, arguments);
- };
-
- var spy = new jasmine.Spy(name);
-
- for (var prop in spy) {
- spyObj[prop] = spy[prop];
- }
-
- spyObj.reset();
-
- return spyObj;
-};
-
-/**
- * Determines whether an object is a spy.
- *
- * @param {jasmine.Spy|Object} putativeSpy
- * @returns {Boolean}
- */
-jasmine.isSpy = function(putativeSpy) {
- return putativeSpy && putativeSpy.isSpy;
-};
-
-/**
- * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
- * large in one call.
- *
- * @param {String} baseName name of spy class
- * @param {Array} methodNames array of names of methods to make spies
- */
-jasmine.createSpyObj = function(baseName, methodNames) {
- if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
- throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
- }
- var obj = {};
- for (var i = 0; i < methodNames.length; i++) {
- obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
- }
- return obj;
-};
-
-/**
- * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
- *
- * Be careful not to leave calls to jasmine.log
in production code.
- */
-jasmine.log = function() {
- var spec = jasmine.getEnv().currentSpec;
- spec.log.apply(spec, arguments);
-};
-
-/**
- * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
- *
- * @example
- * // spy example
- * var foo = {
- * not: function(bool) { return !bool; }
- * }
- * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
- *
- * @see jasmine.createSpy
- * @param obj
- * @param methodName
- * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods
- */
-var spyOn = function(obj, methodName) {
- return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
-};
-if (isCommonJS) exports.spyOn = spyOn;
-
-/**
- * Creates a Jasmine spec that will be added to the current suite.
- *
- * // TODO: pending tests
- *
- * @example
- * it('should be true', function() {
- * expect(true).toEqual(true);
- * });
- *
- * @param {String} desc description of this specification
- * @param {Function} func defines the preconditions and expectations of the spec
- */
-var it = function(desc, func) {
- return jasmine.getEnv().it(desc, func);
-};
-if (isCommonJS) exports.it = it;
-
-/**
- * Creates a disabled Jasmine spec.
- *
- * A convenience method that allows existing specs to be disabled temporarily during development.
- *
- * @param {String} desc description of this specification
- * @param {Function} func defines the preconditions and expectations of the spec
- */
-var xit = function(desc, func) {
- return jasmine.getEnv().xit(desc, func);
-};
-if (isCommonJS) exports.xit = xit;
-
-/**
- * Starts a chain for a Jasmine expectation.
- *
- * It is passed an Object that is the actual value and should chain to one of the many
- * jasmine.Matchers functions.
- *
- * @param {Object} actual Actual value to test against and expected value
- * @return {jasmine.Matchers}
- */
-var expect = function(actual) {
- return jasmine.getEnv().currentSpec.expect(actual);
-};
-if (isCommonJS) exports.expect = expect;
-
-/**
- * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
- *
- * @param {Function} func Function that defines part of a jasmine spec.
- */
-var runs = function(func) {
- jasmine.getEnv().currentSpec.runs(func);
-};
-if (isCommonJS) exports.runs = runs;
-
-/**
- * Waits a fixed time period before moving to the next block.
- *
- * @deprecated Use waitsFor() instead
- * @param {Number} timeout milliseconds to wait
- */
-var waits = function(timeout) {
- jasmine.getEnv().currentSpec.waits(timeout);
-};
-if (isCommonJS) exports.waits = waits;
-
-/**
- * Waits for the latchFunction to return true before proceeding to the next block.
- *
- * @param {Function} latchFunction
- * @param {String} optional_timeoutMessage
- * @param {Number} optional_timeout
- */
-var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
- jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
-};
-if (isCommonJS) exports.waitsFor = waitsFor;
-
-/**
- * A function that is called before each spec in a suite.
- *
- * Used for spec setup, including validating assumptions.
- *
- * @param {Function} beforeEachFunction
- */
-var beforeEach = function(beforeEachFunction) {
- jasmine.getEnv().beforeEach(beforeEachFunction);
-};
-if (isCommonJS) exports.beforeEach = beforeEach;
-
-/**
- * A function that is called after each spec in a suite.
- *
- * Used for restoring any state that is hijacked during spec execution.
- *
- * @param {Function} afterEachFunction
- */
-var afterEach = function(afterEachFunction) {
- jasmine.getEnv().afterEach(afterEachFunction);
-};
-if (isCommonJS) exports.afterEach = afterEach;
-
-/**
- * Defines a suite of specifications.
- *
- * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
- * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
- * of setup in some tests.
- *
- * @example
- * // TODO: a simple suite
- *
- * // TODO: a simple suite with a nested describe block
- *
- * @param {String} description A string, usually the class under test.
- * @param {Function} specDefinitions function that defines several specs.
- */
-var describe = function(description, specDefinitions) {
- return jasmine.getEnv().describe(description, specDefinitions);
-};
-if (isCommonJS) exports.describe = describe;
-
-/**
- * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
- *
- * @param {String} description A string, usually the class under test.
- * @param {Function} specDefinitions function that defines several specs.
- */
-var xdescribe = function(description, specDefinitions) {
- return jasmine.getEnv().xdescribe(description, specDefinitions);
-};
-if (isCommonJS) exports.xdescribe = xdescribe;
-
-
-// Provide the XMLHttpRequest class for IE 5.x-6.x:
-jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
- function tryIt(f) {
- try {
- return f();
- } catch(e) {
- }
- return null;
- }
-
- var xhr = tryIt(function() {
- return new ActiveXObject("Msxml2.XMLHTTP.6.0");
- }) ||
- tryIt(function() {
- return new ActiveXObject("Msxml2.XMLHTTP.3.0");
- }) ||
- tryIt(function() {
- return new ActiveXObject("Msxml2.XMLHTTP");
- }) ||
- tryIt(function() {
- return new ActiveXObject("Microsoft.XMLHTTP");
- });
-
- if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
-
- return xhr;
-} : XMLHttpRequest;
-/**
- * @namespace
- */
-jasmine.util = {};
-
-/**
- * Declare that a child class inherit it's prototype from the parent class.
- *
- * @private
- * @param {Function} childClass
- * @param {Function} parentClass
- */
-jasmine.util.inherit = function(childClass, parentClass) {
- /**
- * @private
- */
- var subclass = function() {
- };
- subclass.prototype = parentClass.prototype;
- childClass.prototype = new subclass();
-};
-
-jasmine.util.formatException = function(e) {
- var lineNumber;
- if (e.line) {
- lineNumber = e.line;
- }
- else if (e.lineNumber) {
- lineNumber = e.lineNumber;
- }
-
- var file;
-
- if (e.sourceURL) {
- file = e.sourceURL;
- }
- else if (e.fileName) {
- file = e.fileName;
- }
-
- var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
-
- if (file && lineNumber) {
- message += ' in ' + file + ' (line ' + lineNumber + ')';
- }
-
- return message;
-};
-
-jasmine.util.htmlEscape = function(str) {
- if (!str) return str;
- return str.replace(/&/g, '&')
- .replace(//g, '>');
-};
-
-jasmine.util.argsToArray = function(args) {
- var arrayOfArgs = [];
- for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
- return arrayOfArgs;
-};
-
-jasmine.util.extend = function(destination, source) {
- for (var property in source) destination[property] = source[property];
- return destination;
-};
-
-/**
- * Environment for Jasmine
- *
- * @constructor
- */
-jasmine.Env = function() {
- this.currentSpec = null;
- this.currentSuite = null;
- this.currentRunner_ = new jasmine.Runner(this);
-
- this.reporter = new jasmine.MultiReporter();
-
- this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
- this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
- this.lastUpdate = 0;
- this.specFilter = function() {
- return true;
- };
-
- this.nextSpecId_ = 0;
- this.nextSuiteId_ = 0;
- this.equalityTesters_ = [];
-
- // wrap matchers
- this.matchersClass = function() {
- jasmine.Matchers.apply(this, arguments);
- };
- jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
-
- jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
-};
-
-
-jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
-jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
-jasmine.Env.prototype.setInterval = jasmine.setInterval;
-jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
-
-/**
- * @returns an object containing jasmine version build info, if set.
- */
-jasmine.Env.prototype.version = function () {
- if (jasmine.version_) {
- return jasmine.version_;
- } else {
- throw new Error('Version not set');
- }
-};
-
-/**
- * @returns string containing jasmine version build info, if set.
- */
-jasmine.Env.prototype.versionString = function() {
- if (!jasmine.version_) {
- return "version unknown";
- }
-
- var version = this.version();
- var versionString = version.major + "." + version.minor + "." + version.build;
- if (version.release_candidate) {
- versionString += ".rc" + version.release_candidate;
- }
- versionString += " revision " + version.revision;
- return versionString;
-};
-
-/**
- * @returns a sequential integer starting at 0
- */
-jasmine.Env.prototype.nextSpecId = function () {
- return this.nextSpecId_++;
-};
-
-/**
- * @returns a sequential integer starting at 0
- */
-jasmine.Env.prototype.nextSuiteId = function () {
- return this.nextSuiteId_++;
-};
-
-/**
- * Register a reporter to receive status updates from Jasmine.
- * @param {jasmine.Reporter} reporter An object which will receive status updates.
- */
-jasmine.Env.prototype.addReporter = function(reporter) {
- this.reporter.addReporter(reporter);
-};
-
-jasmine.Env.prototype.execute = function() {
- this.currentRunner_.execute();
-};
-
-jasmine.Env.prototype.describe = function(description, specDefinitions) {
- var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
-
- var parentSuite = this.currentSuite;
- if (parentSuite) {
- parentSuite.add(suite);
- } else {
- this.currentRunner_.add(suite);
- }
-
- this.currentSuite = suite;
-
- var declarationError = null;
- try {
- specDefinitions.call(suite);
- } catch(e) {
- declarationError = e;
- }
-
- if (declarationError) {
- this.it("encountered a declaration exception", function() {
- throw declarationError;
- });
- }
-
- this.currentSuite = parentSuite;
-
- return suite;
-};
-
-jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
- if (this.currentSuite) {
- this.currentSuite.beforeEach(beforeEachFunction);
- } else {
- this.currentRunner_.beforeEach(beforeEachFunction);
- }
-};
-
-jasmine.Env.prototype.currentRunner = function () {
- return this.currentRunner_;
-};
-
-jasmine.Env.prototype.afterEach = function(afterEachFunction) {
- if (this.currentSuite) {
- this.currentSuite.afterEach(afterEachFunction);
- } else {
- this.currentRunner_.afterEach(afterEachFunction);
- }
-
-};
-
-jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
- return {
- execute: function() {
- }
- };
-};
-
-jasmine.Env.prototype.it = function(description, func) {
- var spec = new jasmine.Spec(this, this.currentSuite, description);
- this.currentSuite.add(spec);
- this.currentSpec = spec;
-
- if (func) {
- spec.runs(func);
- }
-
- return spec;
-};
-
-jasmine.Env.prototype.xit = function(desc, func) {
- return {
- id: this.nextSpecId(),
- runs: function() {
- }
- };
-};
-
-jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) {
- if (a.source != b.source)
- mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/");
-
- if (a.ignoreCase != b.ignoreCase)
- mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier");
-
- if (a.global != b.global)
- mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier");
-
- if (a.multiline != b.multiline)
- mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier");
-
- if (a.sticky != b.sticky)
- mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier");
-
- return (mismatchValues.length === 0);
-};
-
-jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
- if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
- return true;
- }
-
- a.__Jasmine_been_here_before__ = b;
- b.__Jasmine_been_here_before__ = a;
-
- var hasKey = function(obj, keyName) {
- return obj !== null && obj[keyName] !== jasmine.undefined;
- };
-
- for (var property in b) {
- if (!hasKey(a, property) && hasKey(b, property)) {
- mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
- }
- }
- for (property in a) {
- if (!hasKey(b, property) && hasKey(a, property)) {
- mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
- }
- }
- for (property in b) {
- if (property == '__Jasmine_been_here_before__') continue;
- if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
- mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
- }
- }
-
- if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
- mismatchValues.push("arrays were not the same length");
- }
-
- delete a.__Jasmine_been_here_before__;
- delete b.__Jasmine_been_here_before__;
- return (mismatchKeys.length === 0 && mismatchValues.length === 0);
-};
-
-jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
- mismatchKeys = mismatchKeys || [];
- mismatchValues = mismatchValues || [];
-
- for (var i = 0; i < this.equalityTesters_.length; i++) {
- var equalityTester = this.equalityTesters_[i];
- var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
- if (result !== jasmine.undefined) return result;
- }
-
- if (a === b) return true;
-
- if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
- return (a == jasmine.undefined && b == jasmine.undefined);
- }
-
- if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
- return a === b;
- }
-
- if (a instanceof Date && b instanceof Date) {
- return a.getTime() == b.getTime();
- }
-
- if (a.jasmineMatches) {
- return a.jasmineMatches(b);
- }
-
- if (b.jasmineMatches) {
- return b.jasmineMatches(a);
- }
-
- if (a instanceof jasmine.Matchers.ObjectContaining) {
- return a.matches(b);
- }
-
- if (b instanceof jasmine.Matchers.ObjectContaining) {
- return b.matches(a);
- }
-
- if (jasmine.isString_(a) && jasmine.isString_(b)) {
- return (a == b);
- }
-
- if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
- return (a == b);
- }
-
- if (a instanceof RegExp && b instanceof RegExp) {
- return this.compareRegExps_(a, b, mismatchKeys, mismatchValues);
- }
-
- if (typeof a === "object" && typeof b === "object") {
- return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
- }
-
- //Straight check
- return (a === b);
-};
-
-jasmine.Env.prototype.contains_ = function(haystack, needle) {
- if (jasmine.isArray_(haystack)) {
- for (var i = 0; i < haystack.length; i++) {
- if (this.equals_(haystack[i], needle)) return true;
- }
- return false;
- }
- return haystack.indexOf(needle) >= 0;
-};
-
-jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
- this.equalityTesters_.push(equalityTester);
-};
-/** No-op base class for Jasmine reporters.
- *
- * @constructor
- */
-jasmine.Reporter = function() {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.reportSpecResults = function(spec) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.Reporter.prototype.log = function(str) {
-};
-
-/**
- * Blocks are functions with executable code that make up a spec.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {Function} func
- * @param {jasmine.Spec} spec
- */
-jasmine.Block = function(env, func, spec) {
- this.env = env;
- this.func = func;
- this.spec = spec;
-};
-
-jasmine.Block.prototype.execute = function(onComplete) {
- if (!jasmine.CATCH_EXCEPTIONS) {
- this.func.apply(this.spec);
- }
- else {
- try {
- this.func.apply(this.spec);
- } catch (e) {
- this.spec.fail(e);
- }
- }
- onComplete();
-};
-/** JavaScript API reporter.
- *
- * @constructor
- */
-jasmine.JsApiReporter = function() {
- this.started = false;
- this.finished = false;
- this.suites_ = [];
- this.results_ = {};
-};
-
-jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
- this.started = true;
- var suites = runner.topLevelSuites();
- for (var i = 0; i < suites.length; i++) {
- var suite = suites[i];
- this.suites_.push(this.summarize_(suite));
- }
-};
-
-jasmine.JsApiReporter.prototype.suites = function() {
- return this.suites_;
-};
-
-jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
- var isSuite = suiteOrSpec instanceof jasmine.Suite;
- var summary = {
- id: suiteOrSpec.id,
- name: suiteOrSpec.description,
- type: isSuite ? 'suite' : 'spec',
- children: []
- };
-
- if (isSuite) {
- var children = suiteOrSpec.children();
- for (var i = 0; i < children.length; i++) {
- summary.children.push(this.summarize_(children[i]));
- }
- }
- return summary;
-};
-
-jasmine.JsApiReporter.prototype.results = function() {
- return this.results_;
-};
-
-jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
- return this.results_[specId];
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
- this.finished = true;
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
- this.results_[spec.id] = {
- messages: spec.results().getItems(),
- result: spec.results().failedCount > 0 ? "failed" : "passed"
- };
-};
-
-//noinspection JSUnusedLocalSymbols
-jasmine.JsApiReporter.prototype.log = function(str) {
-};
-
-jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
- var results = {};
- for (var i = 0; i < specIds.length; i++) {
- var specId = specIds[i];
- results[specId] = this.summarizeResult_(this.results_[specId]);
- }
- return results;
-};
-
-jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
- var summaryMessages = [];
- var messagesLength = result.messages.length;
- for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
- var resultMessage = result.messages[messageIndex];
- summaryMessages.push({
- text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
- passed: resultMessage.passed ? resultMessage.passed() : true,
- type: resultMessage.type,
- message: resultMessage.message,
- trace: {
- stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
- }
- });
- }
-
- return {
- result : result.result,
- messages : summaryMessages
- };
-};
-
-/**
- * @constructor
- * @param {jasmine.Env} env
- * @param actual
- * @param {jasmine.Spec} spec
- */
-jasmine.Matchers = function(env, actual, spec, opt_isNot) {
- this.env = env;
- this.actual = actual;
- this.spec = spec;
- this.isNot = opt_isNot || false;
- this.reportWasCalled_ = false;
-};
-
-// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
-jasmine.Matchers.pp = function(str) {
- throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
-};
-
-// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
-jasmine.Matchers.prototype.report = function(result, failing_message, details) {
- throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
-};
-
-jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
- for (var methodName in prototype) {
- if (methodName == 'report') continue;
- var orig = prototype[methodName];
- matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
- }
-};
-
-jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
- return function() {
- var matcherArgs = jasmine.util.argsToArray(arguments);
- var result = matcherFunction.apply(this, arguments);
-
- if (this.isNot) {
- result = !result;
- }
-
- if (this.reportWasCalled_) return result;
-
- var message;
- if (!result) {
- if (this.message) {
- message = this.message.apply(this, arguments);
- if (jasmine.isArray_(message)) {
- message = message[this.isNot ? 1 : 0];
- }
- } else {
- var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
- message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
- if (matcherArgs.length > 0) {
- for (var i = 0; i < matcherArgs.length; i++) {
- if (i > 0) message += ",";
- message += " " + jasmine.pp(matcherArgs[i]);
- }
- }
- message += ".";
- }
- }
- var expectationResult = new jasmine.ExpectationResult({
- matcherName: matcherName,
- passed: result,
- expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
- actual: this.actual,
- message: message
- });
- this.spec.addMatcherResult(expectationResult);
- return jasmine.undefined;
- };
-};
-
-
-
-
-/**
- * toBe: compares the actual to the expected using ===
- * @param expected
- */
-jasmine.Matchers.prototype.toBe = function(expected) {
- return this.actual === expected;
-};
-
-/**
- * toNotBe: compares the actual to the expected using !==
- * @param expected
- * @deprecated as of 1.0. Use not.toBe() instead.
- */
-jasmine.Matchers.prototype.toNotBe = function(expected) {
- return this.actual !== expected;
-};
-
-/**
- * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
- *
- * @param expected
- */
-jasmine.Matchers.prototype.toEqual = function(expected) {
- return this.env.equals_(this.actual, expected);
-};
-
-/**
- * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
- * @param expected
- * @deprecated as of 1.0. Use not.toEqual() instead.
- */
-jasmine.Matchers.prototype.toNotEqual = function(expected) {
- return !this.env.equals_(this.actual, expected);
-};
-
-/**
- * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
- * a pattern or a String.
- *
- * @param expected
- */
-jasmine.Matchers.prototype.toMatch = function(expected) {
- return new RegExp(expected).test(this.actual);
-};
-
-/**
- * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
- * @param expected
- * @deprecated as of 1.0. Use not.toMatch() instead.
- */
-jasmine.Matchers.prototype.toNotMatch = function(expected) {
- return !(new RegExp(expected).test(this.actual));
-};
-
-/**
- * Matcher that compares the actual to jasmine.undefined.
- */
-jasmine.Matchers.prototype.toBeDefined = function() {
- return (this.actual !== jasmine.undefined);
-};
-
-/**
- * Matcher that compares the actual to jasmine.undefined.
- */
-jasmine.Matchers.prototype.toBeUndefined = function() {
- return (this.actual === jasmine.undefined);
-};
-
-/**
- * Matcher that compares the actual to null.
- */
-jasmine.Matchers.prototype.toBeNull = function() {
- return (this.actual === null);
-};
-
-/**
- * Matcher that compares the actual to NaN.
- */
-jasmine.Matchers.prototype.toBeNaN = function() {
- this.message = function() {
- return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ];
- };
-
- return (this.actual !== this.actual);
-};
-
-/**
- * Matcher that boolean not-nots the actual.
- */
-jasmine.Matchers.prototype.toBeTruthy = function() {
- return !!this.actual;
-};
-
-
-/**
- * Matcher that boolean nots the actual.
- */
-jasmine.Matchers.prototype.toBeFalsy = function() {
- return !this.actual;
-};
-
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was called.
- */
-jasmine.Matchers.prototype.toHaveBeenCalled = function() {
- if (arguments.length > 0) {
- throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
- }
-
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
-
- this.message = function() {
- return [
- "Expected spy " + this.actual.identity + " to have been called.",
- "Expected spy " + this.actual.identity + " not to have been called."
- ];
- };
-
- return this.actual.wasCalled;
-};
-
-/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
-jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was not called.
- *
- * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
- */
-jasmine.Matchers.prototype.wasNotCalled = function() {
- if (arguments.length > 0) {
- throw new Error('wasNotCalled does not take arguments');
- }
-
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
-
- this.message = function() {
- return [
- "Expected spy " + this.actual.identity + " to not have been called.",
- "Expected spy " + this.actual.identity + " to have been called."
- ];
- };
-
- return !this.actual.wasCalled;
-};
-
-/**
- * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
- *
- * @example
- *
- */
-jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
- var expectedArgs = jasmine.util.argsToArray(arguments);
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
- this.message = function() {
- var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was.";
- var positiveMessage = "";
- if (this.actual.callCount === 0) {
- positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.";
- } else {
- positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '')
- }
- return [positiveMessage, invertedMessage];
- };
-
- return this.env.contains_(this.actual.argsForCall, expectedArgs);
-};
-
-/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
-jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
-
-/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
-jasmine.Matchers.prototype.wasNotCalledWith = function() {
- var expectedArgs = jasmine.util.argsToArray(arguments);
- if (!jasmine.isSpy(this.actual)) {
- throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
- }
-
- this.message = function() {
- return [
- "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
- "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
- ];
- };
-
- return !this.env.contains_(this.actual.argsForCall, expectedArgs);
-};
-
-/**
- * Matcher that checks that the expected item is an element in the actual Array.
- *
- * @param {Object} expected
- */
-jasmine.Matchers.prototype.toContain = function(expected) {
- return this.env.contains_(this.actual, expected);
-};
-
-/**
- * Matcher that checks that the expected item is NOT an element in the actual Array.
- *
- * @param {Object} expected
- * @deprecated as of 1.0. Use not.toContain() instead.
- */
-jasmine.Matchers.prototype.toNotContain = function(expected) {
- return !this.env.contains_(this.actual, expected);
-};
-
-jasmine.Matchers.prototype.toBeLessThan = function(expected) {
- return this.actual < expected;
-};
-
-jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
- return this.actual > expected;
-};
-
-/**
- * Matcher that checks that the expected item is equal to the actual item
- * up to a given level of decimal precision (default 2).
- *
- * @param {Number} expected
- * @param {Number} precision, as number of decimal places
- */
-jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
- if (!(precision === 0)) {
- precision = precision || 2;
- }
- return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2);
-};
-
-/**
- * Matcher that checks that the expected exception was thrown by the actual.
- *
- * @param {String} [expected]
- */
-jasmine.Matchers.prototype.toThrow = function(expected) {
- var result = false;
- var exception;
- if (typeof this.actual != 'function') {
- throw new Error('Actual is not a function');
- }
- try {
- this.actual();
- } catch (e) {
- exception = e;
- }
- if (exception) {
- result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
- }
-
- var not = this.isNot ? "not " : "";
-
- this.message = function() {
- if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
- return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
- } else {
- return "Expected function to throw an exception.";
- }
- };
-
- return result;
-};
-
-jasmine.Matchers.Any = function(expectedClass) {
- this.expectedClass = expectedClass;
-};
-
-jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
- if (this.expectedClass == String) {
- return typeof other == 'string' || other instanceof String;
- }
-
- if (this.expectedClass == Number) {
- return typeof other == 'number' || other instanceof Number;
- }
-
- if (this.expectedClass == Function) {
- return typeof other == 'function' || other instanceof Function;
- }
-
- if (this.expectedClass == Object) {
- return typeof other == 'object';
- }
-
- return other instanceof this.expectedClass;
-};
-
-jasmine.Matchers.Any.prototype.jasmineToString = function() {
- return 'jasmine.log
in production code.
- */
-jasmine.Spec.prototype.log = function() {
- return this.results_.log(arguments);
-};
-
-jasmine.Spec.prototype.runs = function (func) {
- var block = new jasmine.Block(this.env, func, this);
- this.addToQueue(block);
- return this;
-};
-
-jasmine.Spec.prototype.addToQueue = function (block) {
- if (this.queue.isRunning()) {
- this.queue.insertNext(block);
- } else {
- this.queue.add(block);
- }
-};
-
-/**
- * @param {jasmine.ExpectationResult} result
- */
-jasmine.Spec.prototype.addMatcherResult = function(result) {
- this.results_.addResult(result);
-};
-
-jasmine.Spec.prototype.expect = function(actual) {
- var positive = new (this.getMatchersClass_())(this.env, actual, this);
- positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
- return positive;
-};
-
-/**
- * Waits a fixed time period before moving to the next block.
- *
- * @deprecated Use waitsFor() instead
- * @param {Number} timeout milliseconds to wait
- */
-jasmine.Spec.prototype.waits = function(timeout) {
- var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
- this.addToQueue(waitsFunc);
- return this;
-};
-
-/**
- * Waits for the latchFunction to return true before proceeding to the next block.
- *
- * @param {Function} latchFunction
- * @param {String} optional_timeoutMessage
- * @param {Number} optional_timeout
- */
-jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
- var latchFunction_ = null;
- var optional_timeoutMessage_ = null;
- var optional_timeout_ = null;
-
- for (var i = 0; i < arguments.length; i++) {
- var arg = arguments[i];
- switch (typeof arg) {
- case 'function':
- latchFunction_ = arg;
- break;
- case 'string':
- optional_timeoutMessage_ = arg;
- break;
- case 'number':
- optional_timeout_ = arg;
- break;
- }
- }
-
- var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
- this.addToQueue(waitsForFunc);
- return this;
-};
-
-jasmine.Spec.prototype.fail = function (e) {
- var expectationResult = new jasmine.ExpectationResult({
- passed: false,
- message: e ? jasmine.util.formatException(e) : 'Exception',
- trace: { stack: e.stack }
- });
- this.results_.addResult(expectationResult);
-};
-
-jasmine.Spec.prototype.getMatchersClass_ = function() {
- return this.matchersClass || this.env.matchersClass;
-};
-
-jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
- var parent = this.getMatchersClass_();
- var newMatchersClass = function() {
- parent.apply(this, arguments);
- };
- jasmine.util.inherit(newMatchersClass, parent);
- jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
- this.matchersClass = newMatchersClass;
-};
-
-jasmine.Spec.prototype.finishCallback = function() {
- this.env.reporter.reportSpecResults(this);
-};
-
-jasmine.Spec.prototype.finish = function(onComplete) {
- this.removeAllSpies();
- this.finishCallback();
- if (onComplete) {
- onComplete();
- }
-};
-
-jasmine.Spec.prototype.after = function(doAfter) {
- if (this.queue.isRunning()) {
- this.queue.add(new jasmine.Block(this.env, doAfter, this), true);
- } else {
- this.afterCallbacks.unshift(doAfter);
- }
-};
-
-jasmine.Spec.prototype.execute = function(onComplete) {
- var spec = this;
- if (!spec.env.specFilter(spec)) {
- spec.results_.skipped = true;
- spec.finish(onComplete);
- return;
- }
-
- this.env.reporter.reportSpecStarting(this);
-
- spec.env.currentSpec = spec;
-
- spec.addBeforesAndAftersToQueue();
-
- spec.queue.start(function () {
- spec.finish(onComplete);
- });
-};
-
-jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
- var runner = this.env.currentRunner();
- var i;
-
- for (var suite = this.suite; suite; suite = suite.parentSuite) {
- for (i = 0; i < suite.before_.length; i++) {
- this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
- }
- }
- for (i = 0; i < runner.before_.length; i++) {
- this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
- }
- for (i = 0; i < this.afterCallbacks.length; i++) {
- this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true);
- }
- for (suite = this.suite; suite; suite = suite.parentSuite) {
- for (i = 0; i < suite.after_.length; i++) {
- this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true);
- }
- }
- for (i = 0; i < runner.after_.length; i++) {
- this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true);
- }
-};
-
-jasmine.Spec.prototype.explodes = function() {
- throw 'explodes function should not have been called';
-};
-
-jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
- if (obj == jasmine.undefined) {
- throw "spyOn could not find an object to spy upon for " + methodName + "()";
- }
-
- if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
- throw methodName + '() method does not exist';
- }
-
- if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
- throw new Error(methodName + ' has already been spied upon');
- }
-
- var spyObj = jasmine.createSpy(methodName);
-
- this.spies_.push(spyObj);
- spyObj.baseObj = obj;
- spyObj.methodName = methodName;
- spyObj.originalValue = obj[methodName];
-
- obj[methodName] = spyObj;
-
- return spyObj;
-};
-
-jasmine.Spec.prototype.removeAllSpies = function() {
- for (var i = 0; i < this.spies_.length; i++) {
- var spy = this.spies_[i];
- spy.baseObj[spy.methodName] = spy.originalValue;
- }
- this.spies_ = [];
-};
-
-/**
- * Internal representation of a Jasmine suite.
- *
- * @constructor
- * @param {jasmine.Env} env
- * @param {String} description
- * @param {Function} specDefinitions
- * @param {jasmine.Suite} parentSuite
- */
-jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
- var self = this;
- self.id = env.nextSuiteId ? env.nextSuiteId() : null;
- self.description = description;
- self.queue = new jasmine.Queue(env);
- self.parentSuite = parentSuite;
- self.env = env;
- self.before_ = [];
- self.after_ = [];
- self.children_ = [];
- self.suites_ = [];
- self.specs_ = [];
-};
-
-jasmine.Suite.prototype.getFullName = function() {
- var fullName = this.description;
- for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
- fullName = parentSuite.description + ' ' + fullName;
- }
- return fullName;
-};
-
-jasmine.Suite.prototype.finish = function(onComplete) {
- this.env.reporter.reportSuiteResults(this);
- this.finished = true;
- if (typeof(onComplete) == 'function') {
- onComplete();
- }
-};
-
-jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
- beforeEachFunction.typeName = 'beforeEach';
- this.before_.unshift(beforeEachFunction);
-};
-
-jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
- afterEachFunction.typeName = 'afterEach';
- this.after_.unshift(afterEachFunction);
-};
-
-jasmine.Suite.prototype.results = function() {
- return this.queue.results();
-};
-
-jasmine.Suite.prototype.add = function(suiteOrSpec) {
- this.children_.push(suiteOrSpec);
- if (suiteOrSpec instanceof jasmine.Suite) {
- this.suites_.push(suiteOrSpec);
- this.env.currentRunner().addSuite(suiteOrSpec);
- } else {
- this.specs_.push(suiteOrSpec);
- }
- this.queue.add(suiteOrSpec);
-};
-
-jasmine.Suite.prototype.specs = function() {
- return this.specs_;
-};
-
-jasmine.Suite.prototype.suites = function() {
- return this.suites_;
-};
-
-jasmine.Suite.prototype.children = function() {
- return this.children_;
-};
-
-jasmine.Suite.prototype.execute = function(onComplete) {
- var self = this;
- this.queue.start(function () {
- self.finish(onComplete);
- });
-};
-jasmine.WaitsBlock = function(env, timeout, spec) {
- this.timeout = timeout;
- jasmine.Block.call(this, env, null, spec);
-};
-
-jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
-
-jasmine.WaitsBlock.prototype.execute = function (onComplete) {
- if (jasmine.VERBOSE) {
- this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
- }
- this.env.setTimeout(function () {
- onComplete();
- }, this.timeout);
-};
-/**
- * A block which waits for some condition to become true, with timeout.
- *
- * @constructor
- * @extends jasmine.Block
- * @param {jasmine.Env} env The Jasmine environment.
- * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
- * @param {Function} latchFunction A function which returns true when the desired condition has been met.
- * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
- * @param {jasmine.Spec} spec The Jasmine spec.
- */
-jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
- this.timeout = timeout || env.defaultTimeoutInterval;
- this.latchFunction = latchFunction;
- this.message = message;
- this.totalTimeSpentWaitingForLatch = 0;
- jasmine.Block.call(this, env, null, spec);
-};
-jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
-
-jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
-
-jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
- if (jasmine.VERBOSE) {
- this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
- }
- var latchFunctionResult;
- try {
- latchFunctionResult = this.latchFunction.apply(this.spec);
- } catch (e) {
- this.spec.fail(e);
- onComplete();
- return;
- }
-
- if (latchFunctionResult) {
- onComplete();
- } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
- var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
- this.spec.fail({
- name: 'timeout',
- message: message
- });
-
- this.abort = true;
- onComplete();
- } else {
- this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
- var self = this;
- this.env.setTimeout(function() {
- self.execute(onComplete);
- }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
- }
-};
-
-jasmine.version_= {
- "major": 1,
- "minor": 3,
- "build": 1,
- "revision": 1354556913
-};
diff --git a/test/resources/data/plData.json b/test/resources/data/plData.json
deleted file mode 100644
index bee772c..0000000
--- a/test/resources/data/plData.json
+++ /dev/null
@@ -1,210 +0,0 @@
-{
- "items": [
- {
- "text": "2012",
- "year": true,
- "items": [
- {
- "text": "Jan",
- "items": [
- {
- "sales": 120,
- "expenses": 90,
- "profits": 30,
- "leaf": true
- },
- {
- "sales": 150,
- "expenses": 70,
- "profits": 80,
- "leaf": true
- }
- ]
- },
- {
- "text": "Feb",
- "items": [
- {
- "sales": 100,
- "expenses": 80,
- "profits": 20,
- "leaf": true
- },
- {
- "sales": 130,
- "expenses": 80,
- "profits": 50,
- "leaf": true
- }
- ]
- },
- {
- "text": "Mar",
- "items": [
- {
- "sales": 180,
- "expenses": 110,
- "profits": 70,
- "leaf": true
- },
- {
- "sales": 130,
- "expenses": 80,
- "profits": 50,
- "leaf": true
- }
- ]
- },
- {
- "text": "Apr",
- "items": [
- {
- "sales": 110,
- "expenses": 70,
- "profits": 40,
- "leaf": true
- }
- ]
- },
- {
- "text": "May",
- "items": [
- {
- "sales": 60,
- "expenses": 50,
- "profits": 10,
- "leaf": true
- }
- ]
- },
- {
- "text": "Jun",
- "items": [
- {
- "sales": 210,
- "expenses": 180,
- "profits": 30,
- "leaf": true
- }
- ]
- },
- {
- "text": "Jul",
- "items": [
- {
- "sales": 250,
- "expenses": 140,
- "profits": 110,
- "leaf": true
- }
- ]
- },
- {
- "text": "Aug",
- "items": [
- {
- "sales": 130,
- "expenses": 80,
- "profits": 50,
- "leaf": true
- }
- ]
- },
- {
- "text": "Sep",
- "items": [
- {
- "sales": 110,
- "expenses": 80,
- "profits": 30,
- "leaf": true
- }
- ]
- },
- {
- "text": "Oct",
- "items": [
- {
- "sales": 180,
- "expenses": 110,
- "profits": 70,
- "leaf": true
- }
- ]
- },
- {
- "text": "Nov",
- "items": [
- {
- "sales": 220,
- "expenses": 130,
- "profits": 90,
- "leaf": true
- }
- ]
- },
- {
- "text": "Dec",
- "items": [
- {
- "sales": 150,
- "expenses": 130,
- "profits": 30,
- "leaf": true
- }
- ]
- }
- ]
- },
- {
- "text": "2013",
- "year": true,
- "items": [
- {
- "text": "Jan",
- "items": [
- {
- "sales": 120,
- "expenses": 90,
- "profits": 30,
- "leaf": true
- }
- ]
- },
- {
- "text": "Feb",
- "items": [
- {
- "sales": 100,
- "expenses": 80,
- "profits": 20,
- "leaf": true
- }
- ]
- },
- {
- "text": "Mar",
- "items": [
- {
- "sales": 180,
- "expenses": 110,
- "profits": 70,
- "leaf": true
- }
- ]
- },
- {
- "text": "Apr",
- "items": [
- {
- "sales": 110,
- "expenses": 70,
- "profits": 40,
- "leaf": true
- }
- ]
- }
- ]
- }
- ]
-}
diff --git a/test/resources/data/testData.json b/test/resources/data/testData.json
deleted file mode 100644
index a8f5adb..0000000
--- a/test/resources/data/testData.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "items" : [{
- "text" : "Today",
- "items" : [{
- "text" : "Eat",
- "leaf" : true
- }, {
- "text" : "Sleep",
- "leaf" : true
- }, {
- "text" : "Drinking",
- "leaf" : true
- }]
- }, {
- "text" : "Tomorrow",
- "items" : [{
- "text" : "Watch TV",
- "leaf" : true
- }, {
- "text" : "Watch Video",
- "leaf" : true
- }]
- }, {
- "text" : "This week",
- "items" : [{
- "text" : "Shopping",
- "leaf" : true
- }]
- }, {
- "text" : "Later",
- "items" : [{
- "text" : "Eat",
- "leaf" : true
- }, {
- "text" : "Sleep",
- "leaf" : true
- }, {
- "text" : "Drinking",
- "leaf" : true
- }]
- }]
-}
\ No newline at end of file
diff --git a/test/run-jasmine.js b/test/run-jasmine.js
deleted file mode 100644
index 5e5fe60..0000000
--- a/test/run-jasmine.js
+++ /dev/null
@@ -1,86 +0,0 @@
-var system = require('system');
-
-/**
- * Wait until the test condition is true or a timeout occurs. Useful for waiting
- * on a server response or for a ui change (fadeIn, etc.) to occur.
- *
- * @param testFx javascript condition that evaluates to a boolean,
- * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
- * as a callback function.
- * @param onReady what to do when testFx condition is fulfilled,
- * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
- * as a callback function.
- * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
- */
-function waitFor(testFx, onReady, timeOutMillis) {
- var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timeout is 3s
- start = new Date().getTime(),
- condition = false,
- interval = setInterval(function() {
- if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
- // If not time-out yet and condition not yet fulfilled
- condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
- } else {
- if(!condition) {
- // If condition still not fulfilled (timeout but condition is 'false')
- console.log("'waitFor()' timeout");
- phantom.exit(1);
- } else {
- // Condition fulfilled (timeout and/or condition is 'true')
- console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
- typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
- clearInterval(interval); //< Stop this interval
- }
- }
- }, 100); //< repeat check every 100ms
-};
-
-
-if (system.args.length !== 2) {
- console.log('Usage: run-jasmine.js URL');
- phantom.exit(1);
-}
-
-var page = require('webpage').create();
-
-// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
-page.onConsoleMessage = function(msg) {
- console.log(msg);
-};
-
-page.open(system.args[1], function(status){
- if (status !== "success") {
- console.log("Unable to access network");
- phantom.exit();
- } else {
- waitFor(function(){
- return page.evaluate(function(){
- return document.body.querySelector('.symbolSummary .pending') === null;
- });
- }, function(){
- var exitCode = page.evaluate(function(){
- console.log('');
- console.log(document.body.querySelector('.description').innerText);
- var list = document.body.querySelectorAll('.results > #details > .specDetail.failed');
- if (list && list.length > 0) {
- console.log('');
- console.log(list.length + ' test(s) FAILED:');
- for (i = 0; i < list.length; ++i) {
- var el = list[i],
- desc = el.querySelector('.description'),
- msg = el.querySelector('.resultMessage.fail');
- console.log('');
- console.log(desc.innerText);
- console.log(msg.innerText);
- console.log('');
- }
- return 1;
- } else {
- console.log(document.body.querySelector('.alert > .passingAlert.bar').innerText);
- return 0;
- }
- });
- phantom.exit(exitCode);
- });
- }
-});
diff --git a/test/spec/AccordionListSpec.js b/test/spec/AccordionListSpec.js
deleted file mode 100644
index d931a45..0000000
--- a/test/spec/AccordionListSpec.js
+++ /dev/null
@@ -1,132 +0,0 @@
-describe('Ext.ux.AccordionList', function() {
- var accordionList;
-
- beforeEach(function() {
- accordionList = Ext.create('Ext.ux.AccordionList');
- });
-
- it('says Hello!', function() {
- expect(accordionList).toBeDefined();
- });
-
- it('loads nested json data', function() {
- accordionList.setStore(Ext.create('AccordionListExample.store.Task'));
- accordionList.load();
-
- waitsFor(function() {
- return accordionList.getCount() > 0;
- }, "Now, loading data...", 10000);
-
- runs(function () {
- expect(accordionList.getCount()).toEqual(4);
- expect(accordionList.getAllCount()).toEqual(13);
- });
- });
-
- it('should remove all record', function() {
- accordionList.setStore(Ext.create('AccordionListExample.store.Task'));
- accordionList.load();
-
- waitsFor(function() {
- return accordionList.getCount() > 0;
- }, "Now, loading data...", 10000);
-
- runs(function () {
- accordionList.removeAll();
- expect(accordionList.getCount()).toEqual(0);
- expect(accordionList.getAllCount()).toEqual(0);
- });
- });
-
- isAllExpanded = function() {
- var nodes = accordionList.getList().getStore().getNode().childNodes;
- return nodes.every(function expanded (v) {
- var hasChild = v.childNodes > 0;
- return v.isExpanded() &&
- (hasChild ? v.childNodes.every(expanded) : true);
- });
- };
-
- isAllCollapsed = function() {
- var nodes = accordionList.getList().getStore().getNode().childNodes;
- return nodes.every(function collapsed (v) {
- var hasChild = v.childNodes > 0;
- return v.isExpanded() === false &&
- (hasChild ? v.childNodes.every(collapsed) : true);
- });
- };
-
-
- it('should expand all item when configured defaultExpanded', function() {
- accordionList.setDefaultExpanded(true);
- accordionList.setStore(Ext.create('AccordionListExample.store.Task'));
- accordionList.load();
-
- waitsFor(function() {
- return accordionList.getCount() > 0;
- }, "Now, loading data...", 10000);
-
- runs(function () {
- accordionList.doInitialize();
- expect(isAllExpanded(accordionList)).toBeTruthy();
- });
- });
-
- it('should expand all item when invoked doAllExpand', function() {
- accordionList.setStore(Ext.create('AccordionListExample.store.Task'));
- accordionList.load();
-
- waitsFor(function() {
- return accordionList.getCount() > 0;
- }, "Now, loading data...", 10000);
-
- runs(function () {
- accordionList.doAllExpand();
- expect(isAllExpanded(accordionList)).toBeTruthy();
- });
- });
-
- it('should collapse all item when invoked doAllCollapse', function() {
- accordionList.setStore(Ext.create('AccordionListExample.store.Task'));
- accordionList.load();
-
- waitsFor(function() {
- return accordionList.getCount() > 0;
- }, "Now, loading data...", 10000);
-
- runs(function () {
- accordionList.doAllCollapse();
- expect(isAllCollapsed(accordionList)).toBeTruthy();
- });
- });
-
- it('header item should have headerItemCls', function() {
- accordionList.setStore(Ext.create('AccordionListExample.store.Task'));
- accordionList.load();
-
- waitsFor(function() {
- return accordionList.getCount() > 0;
- }, "Now, loading data...", 10000);
-
- runs(function () {
- var elems = accordionList.element.query('.' + accordionList.getHeaderItemCls());
- expect(elems.length).toEqual(4);
- });
- });
-
- it('content item should have contentItemCls', function() {
- accordionList.setStore(Ext.create('AccordionListExample.store.Task'));
- accordionList.load();
-
- waitsFor(function() {
- return accordionList.getCount() > 0;
- }, "Now, loading data...", 10000);
-
- runs(function () {
- accordionList.doAllExpand();
- var elems = accordionList.element.query('.' + accordionList.getContentItemCls());
- expect(elems.length).toEqual(9);
- });
- });
-
-});
diff --git a/test/src/app.js b/test/src/app.js
deleted file mode 100644
index 1923534..0000000
--- a/test/src/app.js
+++ /dev/null
@@ -1 +0,0 @@
-/*9e08fba34a37e3b141f9519c233414ba0ddd666a*/(function(){var global=this,objectPrototype=Object.prototype,toString=objectPrototype.toString,enumerables=true,enumerablesTest={toString:1},emptyFn=function(){},i;if(typeof Ext==="undefined"){global.Ext={}}Ext.global=global;for(i in enumerablesTest){enumerables=null}if(enumerables){enumerables=["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"]}Ext.enumerables=enumerables;Ext.apply=function(object,config,defaults){if(defaults){Ext.apply(object,defaults)}if(object&&config&&typeof config==="object"){var i,j,k;for(i in config){object[i]=config[i]}if(enumerables){for(j=enumerables.length;j--;){k=enumerables[j];if(config.hasOwnProperty(k)){object[k]=config[k]}}}}return object};Ext.buildSettings=Ext.apply({baseCSSPrefix:"x-",scopeResetCSS:false},Ext.buildSettings||{});Ext.apply(Ext,{emptyFn:emptyFn,baseCSSPrefix:Ext.buildSettings.baseCSSPrefix,applyIf:function(object,config){var property;if(object){for(property in config){if(object[property]===undefined){object[property]=config[property]}}}return object},iterate:function(object,fn,scope){if(Ext.isEmpty(object)){return}if(scope===undefined){scope=object}if(Ext.isIterable(object)){Ext.Array.each.call(Ext.Array,object,fn,scope)}else{Ext.Object.each.call(Ext.Object,object,fn,scope)}}});Ext.apply(Ext,{extend:function(){var objectConstructor=objectPrototype.constructor,inlineOverrides=function(o){for(var m in o){if(!o.hasOwnProperty(m)){continue}this[m]=o[m]}};return function(subclass,superclass,overrides){if(Ext.isObject(superclass)){overrides=superclass;superclass=subclass;subclass=overrides.constructor!==objectConstructor?overrides.constructor:function(){superclass.apply(this,arguments)}}var F=function(){},subclassProto,superclassProto=superclass.prototype;F.prototype=superclassProto;subclassProto=subclass.prototype=new F();subclassProto.constructor=subclass;subclass.superclass=superclassProto;if(superclassProto.constructor===objectConstructor){superclassProto.constructor=superclass}subclass.override=function(overrides){Ext.override(subclass,overrides)};subclassProto.override=inlineOverrides;subclassProto.proto=subclassProto;subclass.override(overrides);subclass.extend=function(o){return Ext.extend(subclass,o)};return subclass}}(),override:function(cls,overrides){if(cls.$isClass){return cls.override(overrides)}else{Ext.apply(cls.prototype,overrides)}}});Ext.apply(Ext,{valueFrom:function(value,defaultValue,allowBlank){return Ext.isEmpty(value,allowBlank)?defaultValue:value},typeOf:function(value){if(value===null){return"null"}var type=typeof value;if(type==="undefined"||type==="string"||type==="number"||type==="boolean"){return type}var typeToString=toString.call(value);switch(typeToString){case"[object Array]":return"array";case"[object Date]":return"date";case"[object Boolean]":return"boolean";case"[object Number]":return"number";case"[object RegExp]":return"regexp"}if(type==="function"){return"function"}if(type==="object"){if(value.nodeType!==undefined){if(value.nodeType===3){return(/\S/).test(value.nodeValue)?"textnode":"whitespace"}else{return"element"}}return"object"}},isEmpty:function(value,allowEmptyString){return(value===null)||(value===undefined)||(!allowEmptyString?value==="":false)||(Ext.isArray(value)&&value.length===0)},isArray:("isArray" in Array)?Array.isArray:function(value){return toString.call(value)==="[object Array]"},isDate:function(value){return toString.call(value)==="[object Date]"},isMSDate:function(value){if(!Ext.isString(value)){return false}else{return value.match("\\\\?/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\\\?/")!==null}},isObject:(toString.call(null)==="[object Object]")?function(value){return value!==null&&value!==undefined&&toString.call(value)==="[object Object]"&&value.ownerDocument===undefined}:function(value){return toString.call(value)==="[object Object]"},isSimpleObject:function(value){return value instanceof Object&&value.constructor===Object},isPrimitive:function(value){var type=typeof value;return type==="string"||type==="number"||type==="boolean"},isFunction:(typeof document!=="undefined"&&typeof document.getElementsByTagName("body")==="function")?function(value){return toString.call(value)==="[object Function]"}:function(value){return typeof value==="function"},isNumber:function(value){return typeof value==="number"&&isFinite(value)},isNumeric:function(value){return !isNaN(parseFloat(value))&&isFinite(value)},isString:function(value){return typeof value==="string"},isBoolean:function(value){return typeof value==="boolean"},isElement:function(value){return value?value.nodeType===1:false},isTextNode:function(value){return value?value.nodeName==="#text":false},isDefined:function(value){return typeof value!=="undefined"},isIterable:function(value){return(value&&typeof value!=="string")?value.length!==undefined:false}});Ext.apply(Ext,{clone:function(item){if(item===null||item===undefined){return item}if(item.nodeType&&item.cloneNode){return item.cloneNode(true)}var type=toString.call(item);if(type==="[object Date]"){return new Date(item.getTime())}var i,j,k,clone,key;if(type==="[object Array]"){i=item.length;clone=[];while(i--){clone[i]=Ext.clone(item[i])}}else{if(type==="[object Object]"&&item.constructor===Object){clone={};for(key in item){clone[key]=Ext.clone(item[key])}if(enumerables){for(j=enumerables.length;j--;){k=enumerables[j];clone[k]=item[k]}}}}return clone||item},getUniqueGlobalNamespace:function(){var uniqueGlobalNamespace=this.uniqueGlobalNamespace;if(uniqueGlobalNamespace===undefined){var i=0;do{uniqueGlobalNamespace="ExtBox"+(++i)}while(Ext.global[uniqueGlobalNamespace]!==undefined);Ext.global[uniqueGlobalNamespace]=Ext;this.uniqueGlobalNamespace=uniqueGlobalNamespace}return uniqueGlobalNamespace},functionFactory:function(){var args=Array.prototype.slice.call(arguments),ln=args.length;if(ln>0){args[ln-1]="var Ext=window."+this.getUniqueGlobalNamespace()+";"+args[ln-1]}return Function.prototype.constructor.apply(Function.prototype,args)},globalEval:("execScript" in global)?function(code){global.execScript(code)}:function(code){(function(){eval(code)})()}});Ext.type=Ext.typeOf})();(function(){var a="4.1.0",b;Ext.Version=b=Ext.extend(Object,{constructor:function(d){var c=this.toNumber,f,e;if(d instanceof b){return d}this.version=this.shortVersion=String(d).toLowerCase().replace(/_/g,".").replace(/[\-+]/g,"");e=this.version.search(/([^\d\.])/);if(e!==-1){this.release=this.version.substr(e,d.length);this.shortVersion=this.version.substr(0,e)}this.shortVersion=this.shortVersion.replace(/[^\d]/g,"");f=this.version.split(".");this.major=c(f.shift());this.minor=c(f.shift());this.patch=c(f.shift());this.build=c(f.shift());return this},toNumber:function(c){c=parseInt(c||0,10);if(isNaN(c)){c=0}return c},toString:function(){return this.version},valueOf:function(){return this.version},getMajor:function(){return this.major||0},getMinor:function(){return this.minor||0},getPatch:function(){return this.patch||0},getBuild:function(){return this.build||0},getRelease:function(){return this.release||""},isGreaterThan:function(c){return b.compare(this.version,c)===1},isGreaterThanOrEqual:function(c){return b.compare(this.version,c)>=0},isLessThan:function(c){return b.compare(this.version,c)===-1},isLessThanOrEqual:function(c){return b.compare(this.version,c)<=0},equals:function(c){return b.compare(this.version,c)===0},match:function(c){c=String(c);return this.version.substr(0,c.length)===c},toArray:function(){return[this.getMajor(),this.getMinor(),this.getPatch(),this.getBuild(),this.getRelease()]},getShortVersion:function(){return this.shortVersion},gt:function(){return this.isGreaterThan.apply(this,arguments)},lt:function(){return this.isLessThan.apply(this,arguments)},gtEq:function(){return this.isGreaterThanOrEqual.apply(this,arguments)},ltEq:function(){return this.isLessThanOrEqual.apply(this,arguments)}});Ext.apply(b,{releaseValueMap:{dev:-6,alpha:-5,a:-5,beta:-4,b:-4,rc:-3,"#":-2,p:-1,pl:-1},getComponentValue:function(c){return !c?0:(isNaN(c)?this.releaseValueMap[c]||c:parseInt(c,10))},compare:function(g,f){var d,e,c;g=new b(g).toArray();f=new b(f).toArray();for(c=0;c0){s=function(){var L=[],K,M,J;for(K=0,M=m.length;K 0){if(!this.config.enabled){throw new Error("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. Missing required class"+((F.length>1)?"es":"")+": "+F.join(", "))}}else{s.call(o);return this}H=this.syncModeEnabled;if(!H){z.push({requires:F.slice(),callback:s,scope:o})}v=F.length;for(C=0;C f){h[i]=g;l[i]=b}j[i]=n},onDrag:function(b){if(!this.isDragging){return}var a=this.lastDragPosition;this.onAxisDrag("x",b.deltaX);this.onAxisDrag("y",b.deltaY);this.scrollTo(a.x,a.y)},onDragEnd:function(c){var b,a;if(!this.isDragging){return}this.dragEndTime=Ext.Date.now();this.onDrag(c);this.isDragging=false;b=this.getAnimationEasing("x",c);a=this.getAnimationEasing("y",c);if(b||a){this.getTranslatable().animate(b,a)}else{this.onScrollEnd()}},getAnimationEasing:function(g,j){if(!this.isAxisEnabled(g)){return null}var f=this.position[g],c=this.getMinPosition()[g],i=this.getMaxPosition()[g],a=this.getMaxAbsoluteVelocity(),d=null,b=this.dragEndTime,h=j.flick.velocity[g],k;if(f\^])\s?|\s|$)/,c=/^(#)?([\w\-]+|\*)(?:\((true|false)\))?/,b=[{re:/^\.([\w\-]+)(?:\((true|false)\))?/,method:l},{re:/^(?:[\[](?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]])/,method:m},{re:/^#([\w\-]+)/,method:d},{re:/^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,method:k},{re:/^(?:\{([^\}]+)\})/,method:j}];g.Query=Ext.extend(Object,{constructor:function(n){n=n||{};Ext.apply(this,n)},execute:function(o){var q=this.operations,r=0,s=q.length,p,n;if(!o){n=Ext.ComponentManager.all.getArray()}else{if(Ext.isArray(o)){n=o}}for(;r1){for(q=0,r=s.length;q
0){this.refreshRunningAnimationsData(d,Ext.Array.merge(J,w),true,H.replacePrevious)}c=a.nameMap;E=a.nameList;u={};for(A=0;A The generated SVG font can be imported back to IcoMoon for modification.Your font contains the following glyphs
+ Class Names
+ Hello SyntaxHighlighter
+
+function helloSyntaxHighlighter()
+{
+ return "hi!";
+}
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/scripts/shAutoloader.js b/tests/siesta/resources/syntaxhighlighter_3.0.83/scripts/shAutoloader.js
new file mode 100644
index 0000000..4e29bdd
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/scripts/shAutoloader.js
@@ -0,0 +1,17 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('(2(){1 h=5;h.I=2(){2 n(c,a){4(1 d=0;d\\\\w+)\\\\]$","U"),i=1,p=0;p
keep development active!{ target: DOMElement, params: Object }
objects.
+ */
+ findElements: function(globalParams, element)
+ {
+ var elements = element ? [element] : toArray(document.getElementsByTagName(sh.config.tagName)),
+ conf = sh.config,
+ result = []
+ ;
+
+ // support for feature
+ if (conf.useScriptTags)
+ elements = elements.concat(getSyntaxHighlighterScriptTags());
+
+ if (elements.length === 0)
+ return result;
+
+ for (var i = 0; i < elements.length; i++)
+ {
+ var item = {
+ target: elements[i],
+ // local params take precedence over globals
+ params: merge(globalParams, parseParams(elements[i].className))
+ };
+
+ if (item.params['brush'] == null)
+ continue;
+
+ result.push(item);
+ }
+
+ return result;
+ },
+
+ /**
+ * Shorthand to highlight all elements on the page that are marked as
+ * SyntaxHighlighter source code.
+ *
+ * @param {Object} globalParams Optional parameters which override element's
+ * parameters. Only used if element is specified.
+ *
+ * @param {Object} element Optional element to highlight. If none is
+ * provided, all elements in the current document
+ * are highlighted.
+ */
+ highlight: function(globalParams, element)
+ {
+ var elements = this.findElements(globalParams, element),
+ propertyName = 'innerHTML',
+ highlighter = null,
+ conf = sh.config
+ ;
+
+ if (elements.length === 0)
+ return;
+
+ for (var i = 0; i < elements.length; i++)
+ {
+ var element = elements[i],
+ target = element.target,
+ params = element.params,
+ brushName = params.brush,
+ code
+ ;
+
+ if (brushName == null)
+ continue;
+
+ // Instantiate a brush
+ if (params['html-script'] == 'true' || sh.defaults['html-script'] == true)
+ {
+ highlighter = new sh.HtmlScript(brushName);
+ brushName = 'htmlscript';
+ }
+ else
+ {
+ var brush = findBrush(brushName);
+
+ if (brush)
+ highlighter = new brush();
+ else
+ continue;
+ }
+
+ code = target[propertyName];
+
+ // remove CDATA from tags if it's present
+ if (conf.useScriptTags)
+ code = stripCData(code);
+
+ // Inject title if the attribute is present
+ if ((target.title || '') != '')
+ params.title = target.title;
+
+ params['brush'] = brushName;
+ highlighter.init(params);
+ element = highlighter.getDiv(code);
+
+ // carry over ID
+ if ((target.id || '') != '')
+ element.id = target.id;
+
+ target.parentNode.replaceChild(element, target);
+ }
+ },
+
+ /**
+ * Main entry point for the SyntaxHighlighter.
+ * @param {Object} params Optional params to apply to all highlighted elements.
+ */
+ all: function(params)
+ {
+ attachEvent(
+ window,
+ 'load',
+ function() { sh.highlight(params); }
+ );
+ }
+}; // end of sh
+
+sh['all'] = sh.all;
+sh['highlight'] = sh.highlight;
+
+/**
+ * Checks if target DOM elements has specified CSS class.
+ * @param {DOMElement} target Target DOM element to check.
+ * @param {String} className Name of the CSS class to check for.
+ * @return {Boolean} Returns true if class name is present, false otherwise.
+ */
+function hasClass(target, className)
+{
+ return target.className.indexOf(className) != -1;
+};
+
+/**
+ * Adds CSS class name to the target DOM element.
+ * @param {DOMElement} target Target DOM element.
+ * @param {String} className New CSS class to add.
+ */
+function addClass(target, className)
+{
+ if (!hasClass(target, className))
+ target.className += ' ' + className;
+};
+
+/**
+ * Removes CSS class name from the target DOM element.
+ * @param {DOMElement} target Target DOM element.
+ * @param {String} className CSS class to remove.
+ */
+function removeClass(target, className)
+{
+ target.className = target.className.replace(className, '');
+};
+
+/**
+ * Converts the source to array object. Mostly used for function arguments and
+ * lists returned by getElementsByTagName() which aren't Array objects.
+ * @param {List} source Source list.
+ * @return {Array} Returns array.
+ */
+function toArray(source)
+{
+ var result = [];
+
+ for (var i = 0; i < source.length; i++)
+ result.push(source[i]);
+
+ return result;
+};
+
+/**
+ * Splits block of text into lines.
+ * @param {String} block Block of text.
+ * @return {Array} Returns array of lines.
+ */
+function splitLines(block)
+{
+ return block.split('\n');
+}
+
+/**
+ * Generates HTML ID for the highlighter.
+ * @param {String} highlighterId Highlighter ID.
+ * @return {String} Returns HTML ID.
+ */
+function getHighlighterId(id)
+{
+ var prefix = 'highlighter_';
+ return id.indexOf(prefix) == 0 ? id : prefix + id;
+};
+
+/**
+ * Finds Highlighter instance by ID.
+ * @param {String} highlighterId Highlighter ID.
+ * @return {Highlighter} Returns instance of the highlighter.
+ */
+function getHighlighterById(id)
+{
+ return sh.vars.highlighters[getHighlighterId(id)];
+};
+
+/**
+ * Finds highlighter's DIV container.
+ * @param {String} highlighterId Highlighter ID.
+ * @return {Element} Returns highlighter's DIV element.
+ */
+function getHighlighterDivById(id)
+{
+ return document.getElementById(getHighlighterId(id));
+};
+
+/**
+ * Stores highlighter so that getHighlighterById() can do its thing. Each
+ * highlighter must call this method to preserve itself.
+ * @param {Highilghter} highlighter Highlighter instance.
+ */
+function storeHighlighter(highlighter)
+{
+ sh.vars.highlighters[getHighlighterId(highlighter.id)] = highlighter;
+};
+
+/**
+ * Looks for a child or parent node which has specified classname.
+ * Equivalent to jQuery's $(container).find(".className")
+ * @param {Element} target Target element.
+ * @param {String} search Class name or node name to look for.
+ * @param {Boolean} reverse If set to true, will go up the node tree instead of down.
+ * @return {Element} Returns found child or parent element on null.
+ */
+function findElement(target, search, reverse /* optional */)
+{
+ if (target == null)
+ return null;
+
+ var nodes = reverse != true ? target.childNodes : [ target.parentNode ],
+ propertyToFind = { '#' : 'id', '.' : 'className' }[search.substr(0, 1)] || 'nodeName',
+ expectedValue,
+ found
+ ;
+
+ expectedValue = propertyToFind != 'nodeName'
+ ? search.substr(1)
+ : search.toUpperCase()
+ ;
+
+ // main return of the found node
+ if ((target[propertyToFind] || '').indexOf(expectedValue) != -1)
+ return target;
+
+ for (var i = 0; nodes && i < nodes.length && found == null; i++)
+ found = findElement(nodes[i], search, reverse);
+
+ return found;
+};
+
+/**
+ * Looks for a parent node which has specified classname.
+ * This is an alias to findElement(container, className, true)
.
+ * @param {Element} target Target element.
+ * @param {String} className Class name to look for.
+ * @return {Element} Returns found parent element on null.
+ */
+function findParentElement(target, className)
+{
+ return findElement(target, className, true);
+};
+
+/**
+ * Finds an index of element in the array.
+ * @ignore
+ * @param {Object} searchElement
+ * @param {Number} fromIndex
+ * @return {Number} Returns index of element if found; -1 otherwise.
+ */
+function indexOf(array, searchElement, fromIndex)
+{
+ fromIndex = Math.max(fromIndex || 0, 0);
+
+ for (var i = fromIndex; i < array.length; i++)
+ if(array[i] == searchElement)
+ return i;
+
+ return -1;
+};
+
+/**
+ * Generates a unique element ID.
+ */
+function guid(prefix)
+{
+ return (prefix || '') + Math.round(Math.random() * 1000000).toString();
+};
+
+/**
+ * Merges two objects. Values from obj2 override values in obj1.
+ * Function is NOT recursive and works only for one dimensional objects.
+ * @param {Object} obj1 First object.
+ * @param {Object} obj2 Second object.
+ * @return {Object} Returns combination of both objects.
+ */
+function merge(obj1, obj2)
+{
+ var result = {}, name;
+
+ for (name in obj1)
+ result[name] = obj1[name];
+
+ for (name in obj2)
+ result[name] = obj2[name];
+
+ return result;
+};
+
+/**
+ * Attempts to convert string to boolean.
+ * @param {String} value Input string.
+ * @return {Boolean} Returns true if input was "true", false if input was "false" and value otherwise.
+ */
+function toBoolean(value)
+{
+ var result = { "true" : true, "false" : false }[value];
+ return result == null ? value : result;
+};
+
+/**
+ * Opens up a centered popup window.
+ * @param {String} url URL to open in the window.
+ * @param {String} name Popup name.
+ * @param {int} width Popup width.
+ * @param {int} height Popup height.
+ * @param {String} options window.open() options.
+ * @return {Window} Returns window instance.
+ */
+function popup(url, name, width, height, options)
+{
+ var x = (screen.width - width) / 2,
+ y = (screen.height - height) / 2
+ ;
+
+ options += ', left=' + x +
+ ', top=' + y +
+ ', width=' + width +
+ ', height=' + height
+ ;
+ options = options.replace(/^,/, '');
+
+ var win = window.open(url, name, options);
+ win.focus();
+ return win;
+};
+
+/**
+ * Adds event handler to the target object.
+ * @param {Object} obj Target object.
+ * @param {String} type Name of the event.
+ * @param {Function} func Handling function.
+ */
+function attachEvent(obj, type, func, scope)
+{
+ function handler(e)
+ {
+ e = e || window.event;
+
+ if (!e.target)
+ {
+ e.target = e.srcElement;
+ e.preventDefault = function()
+ {
+ this.returnValue = false;
+ };
+ }
+
+ func.call(scope || window, e);
+ };
+
+ if (obj.attachEvent)
+ {
+ obj.attachEvent('on' + type, handler);
+ }
+ else
+ {
+ obj.addEventListener(type, handler, false);
+ }
+};
+
+/**
+ * Displays an alert.
+ * @param {String} str String to display.
+ */
+function alert(str)
+{
+ window.alert(sh.config.strings.alert + str);
+};
+
+/**
+ * Finds a brush by its alias.
+ *
+ * @param {String} alias Brush alias.
+ * @param {Boolean} showAlert Suppresses the alert if false.
+ * @return {Brush} Returns bursh constructor if found, null otherwise.
+ */
+function findBrush(alias, showAlert)
+{
+ var brushes = sh.vars.discoveredBrushes,
+ result = null
+ ;
+
+ if (brushes == null)
+ {
+ brushes = {};
+
+ // Find all brushes
+ for (var brush in sh.brushes)
+ {
+ var info = sh.brushes[brush],
+ aliases = info.aliases
+ ;
+
+ if (aliases == null)
+ continue;
+
+ // keep the brush name
+ info.brushName = brush.toLowerCase();
+
+ for (var i = 0; i < aliases.length; i++)
+ brushes[aliases[i]] = brush;
+ }
+
+ sh.vars.discoveredBrushes = brushes;
+ }
+
+ result = sh.brushes[brushes[alias]];
+
+ if (result == null && showAlert != false)
+ alert(sh.config.strings.noBrush + alias);
+
+ return result;
+};
+
+/**
+ * Executes a callback on each line and replaces each line with result from the callback.
+ * @param {Object} str Input string.
+ * @param {Object} callback Callback function taking one string argument and returning a string.
+ */
+function eachLine(str, callback)
+{
+ var lines = splitLines(str);
+
+ for (var i = 0; i < lines.length; i++)
+ lines[i] = callback(lines[i], i);
+
+ return lines.join('\n');
+};
+
+/**
+ * This is a special trim which only removes first and last empty lines
+ * and doesn't affect valid leading space on the first line.
+ *
+ * @param {String} str Input string
+ * @return {String} Returns string without empty first and last lines.
+ */
+function trimFirstAndLastLines(str)
+{
+ return str.replace(/^[ ]*[\n]+|[\n]*[ ]*$/g, '');
+};
+
+/**
+ * Parses key/value pairs into hash object.
+ *
+ * Understands the following formats:
+ * - name: word;
+ * - name: [word, word];
+ * - name: "string";
+ * - name: 'string';
+ *
+ * For example:
+ * name1: value; name2: [value, value]; name3: 'value'
+ *
+ * @param {String} str Input string.
+ * @return {Object} Returns deserialized object.
+ */
+function parseParams(str)
+{
+ var match,
+ result = {},
+ arrayRegex = new XRegExp("^\\[(? tag with given style applied to it.
+ *
+ * @param {String} str Input string.
+ * @param {String} css Style name to apply to the string.
+ * @return {String} Returns input string with each line surrounded by tag.
+ */
+function wrapLinesWithCode(str, css)
+{
+ if (str == null || str.length == 0 || str == '\n')
+ return str;
+
+ str = str.replace(/
' + line + '
';
+ });
+
+ return str;
+};
+
+/**
+ * Pads number with zeros until it's length is the same as given length.
+ *
+ * @param {Number} number Number to pad.
+ * @param {Number} length Max string length with.
+ * @return {String} Returns a string padded with proper amount of '0'.
+ */
+function padNumber(number, length)
+{
+ var result = number.toString();
+
+ while (result.length < length)
+ result = '0' + result;
+
+ return result;
+};
+
+/**
+ * Replaces tabs with spaces.
+ *
+ * @param {String} code Source code.
+ * @param {Number} tabSize Size of the tab.
+ * @return {String} Returns code with all tabs replaces by spaces.
+ */
+function processTabs(code, tabSize)
+{
+ var tab = '';
+
+ for (var i = 0; i < tabSize; i++)
+ tab += ' ';
+
+ return code.replace(/\t/g, tab);
+};
+
+/**
+ * Replaces tabs with smart spaces.
+ *
+ * @param {String} code Code to fix the tabs in.
+ * @param {Number} tabSize Number of spaces in a column.
+ * @return {String} Returns code with all tabs replaces with roper amount of spaces.
+ */
+function processSmartTabs(code, tabSize)
+{
+ var lines = splitLines(code),
+ tab = '\t',
+ spaces = ''
+ ;
+
+ // Create a string with 1000 spaces to copy spaces from...
+ // It's assumed that there would be no indentation longer than that.
+ for (var i = 0; i < 50; i++)
+ spaces += ' '; // 20 spaces * 50
+
+ // This function inserts specified amount of spaces in the string
+ // where a tab is while removing that given tab.
+ function insertSpaces(line, pos, count)
+ {
+ return line.substr(0, pos)
+ + spaces.substr(0, count)
+ + line.substr(pos + 1, line.length) // pos + 1 will get rid of the tab
+ ;
+ };
+
+ // Go through all the lines and do the 'smart tabs' magic.
+ code = eachLine(code, function(line)
+ {
+ if (line.indexOf(tab) == -1)
+ return line;
+
+ var pos = 0;
+
+ while ((pos = line.indexOf(tab)) != -1)
+ {
+ // This is pretty much all there is to the 'smart tabs' logic.
+ // Based on the position within the line and size of a tab,
+ // calculate the amount of spaces we need to insert.
+ var spaces = tabSize - pos % tabSize;
+ line = insertSpaces(line, pos, spaces);
+ }
+
+ return line;
+ });
+
+ return code;
+};
+
+/**
+ * Performs various string fixes based on configuration.
+ */
+function fixInputString(str)
+{
+ var br = /
|<br\s*\/?>/gi;
+
+ if (sh.config.bloggerMode == true)
+ str = str.replace(br, '\n');
+
+ if (sh.config.stripBrs == true)
+ str = str.replace(br, '');
+
+ return str;
+};
+
+/**
+ * Removes all white space at the begining and end of a string.
+ *
+ * @param {String} str String to trim.
+ * @return {String} Returns string without leading and following white space characters.
+ */
+function trim(str)
+{
+ return str.replace(/^\s+|\s+$/g, '');
+};
+
+/**
+ * Unindents a block of text by the lowest common indent amount.
+ * @param {String} str Text to unindent.
+ * @return {String} Returns unindented text block.
+ */
+function unindent(str)
+{
+ var lines = splitLines(fixInputString(str)),
+ indents = new Array(),
+ regex = /^\s*/,
+ min = 1000
+ ;
+
+ // go through every line and check for common number of indents
+ for (var i = 0; i < lines.length && min > 0; i++)
+ {
+ var line = lines[i];
+
+ if (trim(line).length == 0)
+ continue;
+
+ var matches = regex.exec(line);
+
+ // In the event that just one line doesn't have leading white space
+ // we can't unindent anything, so bail completely.
+ if (matches == null)
+ return str;
+
+ min = Math.min(matches[0].length, min);
+ }
+
+ // trim minimum common number of white space from the begining of every line
+ if (min > 0)
+ for (var i = 0; i < lines.length; i++)
+ lines[i] = lines[i].substr(min);
+
+ return lines.join('\n');
+};
+
+/**
+ * Callback method for Array.sort() which sorts matches by
+ * index position and then by length.
+ *
+ * @param {Match} m1 Left object.
+ * @param {Match} m2 Right object.
+ * @return {Number} Returns -1, 0 or -1 as a comparison result.
+ */
+function matchesSortCallback(m1, m2)
+{
+ // sort matches by index first
+ if(m1.index < m2.index)
+ return -1;
+ else if(m1.index > m2.index)
+ return 1;
+ else
+ {
+ // if index is the same, sort by length
+ if(m1.length < m2.length)
+ return -1;
+ else if(m1.length > m2.length)
+ return 1;
+ }
+
+ return 0;
+};
+
+/**
+ * Executes given regular expression on provided code and returns all
+ * matches that are found.
+ *
+ * @param {String} code Code to execute regular expression on.
+ * @param {Object} regex Regular expression item info from regexList
collection.
+ * @return {Array} Returns a list of Match objects.
+ */
+function getMatches(code, regexInfo)
+{
+ function defaultAdd(match, regexInfo)
+ {
+ return match[0];
+ };
+
+ var index = 0,
+ match = null,
+ matches = [],
+ func = regexInfo.func ? regexInfo.func : defaultAdd
+ ;
+
+ while((match = regexInfo.regex.exec(code)) != null)
+ {
+ var resultMatch = func(match, regexInfo);
+
+ if (typeof(resultMatch) == 'string')
+ resultMatch = [new sh.Match(resultMatch, match.index, regexInfo.css)];
+
+ matches = matches.concat(resultMatch);
+ }
+
+ return matches;
+};
+
+/**
+ * Turns all URLs in the code into tags.
+ * @param {String} code Input code.
+ * @return {String} Returns code with
' + spaces + '
' : '') + line
+ );
+ }
+
+ return html;
+ },
+
+ /**
+ * Returns HTML for the table title or empty string if title is null.
+ */
+ getTitleHtml: function(title)
+ {
+ return title ? '' + this.getLineNumbersHtml(code) + ' | ' : '') + + ''
+ + ' '
+ + html
+ + ' '
+ + ' | '
+ + '
.*?)" +
+ "(?" + regexGroup.right.source + ")",
+ "sgi"
+ )
+ };
+ }
+}; // end of Highlighter
+
+return sh;
+}(); // end of anonymous function
+
+// CommonJS
+typeof(exports) != 'undefined' ? exports['SyntaxHighlighter'] = SyntaxHighlighter : null;
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/src/shLegacy.js b/tests/siesta/resources/syntaxhighlighter_3.0.83/src/shLegacy.js
new file mode 100644
index 0000000..36951c9
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/src/shLegacy.js
@@ -0,0 +1,157 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+var dp = {
+ SyntaxHighlighter : {}
+};
+
+dp.SyntaxHighlighter = {
+ parseParams: function(
+ input,
+ showGutter,
+ showControls,
+ collapseAll,
+ firstLine,
+ showColumns
+ )
+ {
+ function getValue(list, name)
+ {
+ var regex = new XRegExp('^' + name + '\\[(?\\w+)\\]$', 'gi'),
+ match = null
+ ;
+
+ for (var i = 0; i < list.length; i++)
+ if ((match = regex.exec(list[i])) != null)
+ return match.value;
+
+ return null;
+ };
+
+ function defaultValue(value, def)
+ {
+ return value != null ? value : def;
+ };
+
+ function asString(value)
+ {
+ return value != null ? value.toString() : null;
+ };
+
+ var parts = input.split(':'),
+ brushName = parts[0],
+ options = {},
+ straight = { 'true' : true }
+ reverse = { 'true' : false },
+ result = null,
+ defaults = SyntaxHighlighter.defaults
+ ;
+
+ for (var i in parts)
+ options[parts[i]] = 'true';
+
+ showGutter = asString(defaultValue(showGutter, defaults.gutter));
+ showControls = asString(defaultValue(showControls, defaults.toolbar));
+ collapseAll = asString(defaultValue(collapseAll, defaults.collapse));
+ showColumns = asString(defaultValue(showColumns, defaults.ruler));
+ firstLine = asString(defaultValue(firstLine, defaults['first-line']));
+
+ return {
+ brush : brushName,
+ gutter : defaultValue(reverse[options.nogutter], showGutter),
+ toolbar : defaultValue(reverse[options.nocontrols], showControls),
+ collapse : defaultValue(straight[options.collapse], collapseAll),
+ // ruler : defaultValue(straight[options.showcolumns], showColumns),
+ 'first-line' : defaultValue(getValue(parts, 'firstline'), firstLine)
+ };
+ },
+
+ HighlightAll: function(
+ name,
+ showGutter /* optional */,
+ showControls /* optional */,
+ collapseAll /* optional */,
+ firstLine /* optional */,
+ showColumns /* optional */
+ )
+ {
+ function findValue()
+ {
+ var a = arguments;
+
+ for (var i = 0; i < a.length; i++)
+ {
+ if (a[i] === null)
+ continue;
+
+ if (typeof(a[i]) == 'string' && a[i] != '')
+ return a[i] + '';
+
+ if (typeof(a[i]) == 'object' && a[i].value != '')
+ return a[i].value + '';
+ }
+
+ return null;
+ };
+
+ function findTagsByName(list, name, tagName)
+ {
+ var tags = document.getElementsByTagName(tagName);
+
+ for (var i = 0; i < tags.length; i++)
+ if (tags[i].getAttribute('name') == name)
+ list.push(tags[i]);
+ }
+
+ var elements = [],
+ highlighter = null,
+ registered = {},
+ propertyName = 'innerHTML'
+ ;
+
+ // for some reason IE doesn't find by name, however it does see them just fine by tag name...
+ findTagsByName(elements, name, 'pre');
+ findTagsByName(elements, name, 'textarea');
+
+ if (elements.length === 0)
+ return;
+
+ for (var i = 0; i < elements.length; i++)
+ {
+ var element = elements[i],
+ params = findValue(
+ element.attributes['class'], element.className,
+ element.attributes['language'], element.language
+ ),
+ language = ''
+ ;
+
+ if (params === null)
+ continue;
+
+ params = dp.SyntaxHighlighter.parseParams(
+ params,
+ showGutter,
+ showControls,
+ collapseAll,
+ firstLine,
+ showColumns
+ );
+
+ SyntaxHighlighter.highlight(params, element);
+ }
+ }
+};
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCore.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCore.css
new file mode 100644
index 0000000..34f6864
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCore.css
@@ -0,0 +1,226 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+ -moz-border-radius: 0 0 0 0 !important;
+ -webkit-border-radius: 0 0 0 0 !important;
+ background: none !important;
+ border: 0 !important;
+ bottom: auto !important;
+ float: none !important;
+ height: auto !important;
+ left: auto !important;
+ line-height: 1.1em !important;
+ margin: 0 !important;
+ outline: 0 !important;
+ overflow: visible !important;
+ padding: 0 !important;
+ position: static !important;
+ right: auto !important;
+ text-align: left !important;
+ top: auto !important;
+ vertical-align: baseline !important;
+ width: auto !important;
+ box-sizing: content-box !important;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+ font-size: 1em !important;
+ min-height: inherit !important;
+ min-height: auto !important;
+}
+
+.syntaxhighlighter {
+ width: 100% !important;
+ margin: 1em 0 1em 0 !important;
+ position: relative !important;
+ overflow: auto !important;
+ font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+ overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+ font-style: italic !important;
+}
+.syntaxhighlighter .line {
+ white-space: pre !important;
+}
+.syntaxhighlighter table {
+ width: 100% !important;
+}
+.syntaxhighlighter table caption {
+ text-align: left !important;
+ padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+ width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+ position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+ box-sizing: border-box !important;
+ position: absolute !important;
+ left: 0 !important;
+ top: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+ border: none !important;
+ background: white !important;
+ padding-left: 1em !important;
+ overflow: hidden !important;
+ white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+ text-align: right !important;
+ padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+ padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+ padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+ display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ padding: 0.1em 0.8em 0em 0.8em !important;
+ font-size: 1em !important;
+ position: static !important;
+ width: auto !important;
+ height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+ display: inline !important;
+ margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+ padding: 0 !important;
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+ position: absolute !important;
+ right: 1px !important;
+ top: 1px !important;
+ width: 11px !important;
+ height: 11px !important;
+ font-size: 10px !important;
+ z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+ display: block !important;
+ text-align: center !important;
+ text-decoration: none !important;
+ padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+ display: none !important;
+}
+.syntaxhighlighter.ie {
+ font-size: .9em !important;
+ padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+ line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+ padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+ background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+ color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+ color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+ display: none !important;
+}
+.syntaxhighlighter.printing a {
+ text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+ color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+ color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+ color: #006699 !important;
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+ color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+ color: black !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreDefault.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreDefault.css
new file mode 100644
index 0000000..08f9e10
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreDefault.css
@@ -0,0 +1,328 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+ -moz-border-radius: 0 0 0 0 !important;
+ -webkit-border-radius: 0 0 0 0 !important;
+ background: none !important;
+ border: 0 !important;
+ bottom: auto !important;
+ float: none !important;
+ height: auto !important;
+ left: auto !important;
+ line-height: 1.1em !important;
+ margin: 0 !important;
+ outline: 0 !important;
+ overflow: visible !important;
+ padding: 0 !important;
+ position: static !important;
+ right: auto !important;
+ text-align: left !important;
+ top: auto !important;
+ vertical-align: baseline !important;
+ width: auto !important;
+ box-sizing: content-box !important;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+ font-size: 1em !important;
+ min-height: inherit !important;
+ min-height: auto !important;
+}
+
+.syntaxhighlighter {
+ width: 100% !important;
+ margin: 1em 0 1em 0 !important;
+ position: relative !important;
+ overflow: auto !important;
+ font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+ overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+ font-style: italic !important;
+}
+.syntaxhighlighter .line {
+ white-space: pre !important;
+}
+.syntaxhighlighter table {
+ width: 100% !important;
+}
+.syntaxhighlighter table caption {
+ text-align: left !important;
+ padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+ width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+ position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+ box-sizing: border-box !important;
+ position: absolute !important;
+ left: 0 !important;
+ top: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+ border: none !important;
+ background: white !important;
+ padding-left: 1em !important;
+ overflow: hidden !important;
+ white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+ text-align: right !important;
+ padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+ padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+ padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+ display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ padding: 0.1em 0.8em 0em 0.8em !important;
+ font-size: 1em !important;
+ position: static !important;
+ width: auto !important;
+ height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+ display: inline !important;
+ margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+ padding: 0 !important;
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+ position: absolute !important;
+ right: 1px !important;
+ top: 1px !important;
+ width: 11px !important;
+ height: 11px !important;
+ font-size: 10px !important;
+ z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+ display: block !important;
+ text-align: center !important;
+ text-decoration: none !important;
+ padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+ display: none !important;
+}
+.syntaxhighlighter.ie {
+ font-size: .9em !important;
+ padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+ line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+ padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+ background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+ color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+ color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+ display: none !important;
+}
+.syntaxhighlighter.printing a {
+ text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+ color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+ color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+ color: #006699 !important;
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+ color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+ color: black !important;
+}
+
+.syntaxhighlighter {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #e0e0e0 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: black !important;
+}
+.syntaxhighlighter table caption {
+ color: black !important;
+}
+.syntaxhighlighter .gutter {
+ color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #6ce26c !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #6ce26c !important;
+ color: white !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: blue !important;
+ background: white !important;
+ border: 1px solid #6ce26c !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: blue !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: red !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #6ce26c !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: black !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: black !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: blue !important;
+}
+.syntaxhighlighter .keyword {
+ color: #006699 !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #006699 !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: red !important;
+}
+
+.syntaxhighlighter .keyword {
+ font-weight: bold !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreDjango.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreDjango.css
new file mode 100644
index 0000000..1db1f70
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreDjango.css
@@ -0,0 +1,331 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+ -moz-border-radius: 0 0 0 0 !important;
+ -webkit-border-radius: 0 0 0 0 !important;
+ background: none !important;
+ border: 0 !important;
+ bottom: auto !important;
+ float: none !important;
+ height: auto !important;
+ left: auto !important;
+ line-height: 1.1em !important;
+ margin: 0 !important;
+ outline: 0 !important;
+ overflow: visible !important;
+ padding: 0 !important;
+ position: static !important;
+ right: auto !important;
+ text-align: left !important;
+ top: auto !important;
+ vertical-align: baseline !important;
+ width: auto !important;
+ box-sizing: content-box !important;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+ font-size: 1em !important;
+ min-height: inherit !important;
+ min-height: auto !important;
+}
+
+.syntaxhighlighter {
+ width: 100% !important;
+ margin: 1em 0 1em 0 !important;
+ position: relative !important;
+ overflow: auto !important;
+ font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+ overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+ font-style: italic !important;
+}
+.syntaxhighlighter .line {
+ white-space: pre !important;
+}
+.syntaxhighlighter table {
+ width: 100% !important;
+}
+.syntaxhighlighter table caption {
+ text-align: left !important;
+ padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+ width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+ position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+ box-sizing: border-box !important;
+ position: absolute !important;
+ left: 0 !important;
+ top: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+ border: none !important;
+ background: white !important;
+ padding-left: 1em !important;
+ overflow: hidden !important;
+ white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+ text-align: right !important;
+ padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+ padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+ padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+ display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ padding: 0.1em 0.8em 0em 0.8em !important;
+ font-size: 1em !important;
+ position: static !important;
+ width: auto !important;
+ height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+ display: inline !important;
+ margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+ padding: 0 !important;
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+ position: absolute !important;
+ right: 1px !important;
+ top: 1px !important;
+ width: 11px !important;
+ height: 11px !important;
+ font-size: 10px !important;
+ z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+ display: block !important;
+ text-align: center !important;
+ text-decoration: none !important;
+ padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+ display: none !important;
+}
+.syntaxhighlighter.ie {
+ font-size: .9em !important;
+ padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+ line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+ padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+ background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+ color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+ color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+ display: none !important;
+}
+.syntaxhighlighter.printing a {
+ text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+ color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+ color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+ color: #006699 !important;
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+ color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+ color: black !important;
+}
+
+.syntaxhighlighter {
+ background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #233729 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: white !important;
+}
+.syntaxhighlighter table caption {
+ color: #f8f8f8 !important;
+}
+.syntaxhighlighter .gutter {
+ color: #497958 !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #41a83e !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #41a83e !important;
+ color: #0a2b1d !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #96dd3b !important;
+ background: black !important;
+ border: 1px solid #41a83e !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #96dd3b !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #41a83e !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #ffe862 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: #f8f8f8 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #336442 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #9df39f !important;
+}
+.syntaxhighlighter .keyword {
+ color: #96dd3b !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #91bb9e !important;
+}
+.syntaxhighlighter .variable {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+ color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+ color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #96dd3b !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: #eb939a !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #91bb9e !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #edef7d !important;
+}
+
+.syntaxhighlighter .comments {
+ font-style: italic !important;
+}
+.syntaxhighlighter .keyword {
+ font-weight: bold !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreEclipse.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreEclipse.css
new file mode 100644
index 0000000..a45de9f
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreEclipse.css
@@ -0,0 +1,339 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+ -moz-border-radius: 0 0 0 0 !important;
+ -webkit-border-radius: 0 0 0 0 !important;
+ background: none !important;
+ border: 0 !important;
+ bottom: auto !important;
+ float: none !important;
+ height: auto !important;
+ left: auto !important;
+ line-height: 1.1em !important;
+ margin: 0 !important;
+ outline: 0 !important;
+ overflow: visible !important;
+ padding: 0 !important;
+ position: static !important;
+ right: auto !important;
+ text-align: left !important;
+ top: auto !important;
+ vertical-align: baseline !important;
+ width: auto !important;
+ box-sizing: content-box !important;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+ font-size: 1em !important;
+ min-height: inherit !important;
+ min-height: auto !important;
+}
+
+.syntaxhighlighter {
+ width: 100% !important;
+ margin: 1em 0 1em 0 !important;
+ position: relative !important;
+ overflow: auto !important;
+ font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+ overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+ font-style: italic !important;
+}
+.syntaxhighlighter .line {
+ white-space: pre !important;
+}
+.syntaxhighlighter table {
+ width: 100% !important;
+}
+.syntaxhighlighter table caption {
+ text-align: left !important;
+ padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+ width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+ position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+ box-sizing: border-box !important;
+ position: absolute !important;
+ left: 0 !important;
+ top: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+ border: none !important;
+ background: white !important;
+ padding-left: 1em !important;
+ overflow: hidden !important;
+ white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+ text-align: right !important;
+ padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+ padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+ padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+ display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ padding: 0.1em 0.8em 0em 0.8em !important;
+ font-size: 1em !important;
+ position: static !important;
+ width: auto !important;
+ height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+ display: inline !important;
+ margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+ padding: 0 !important;
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+ position: absolute !important;
+ right: 1px !important;
+ top: 1px !important;
+ width: 11px !important;
+ height: 11px !important;
+ font-size: 10px !important;
+ z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+ display: block !important;
+ text-align: center !important;
+ text-decoration: none !important;
+ padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+ display: none !important;
+}
+.syntaxhighlighter.ie {
+ font-size: .9em !important;
+ padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+ line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+ padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+ background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+ color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+ color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+ display: none !important;
+}
+.syntaxhighlighter.printing a {
+ text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+ color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+ color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+ color: #006699 !important;
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+ color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+ color: black !important;
+}
+
+.syntaxhighlighter {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #c3defe !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: white !important;
+}
+.syntaxhighlighter table caption {
+ color: black !important;
+}
+.syntaxhighlighter .gutter {
+ color: #787878 !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #d4d0c8 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #d4d0c8 !important;
+ color: white !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #3f5fbf !important;
+ background: white !important;
+ border: 1px solid #d4d0c8 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #3f5fbf !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter .toolbar {
+ color: #a0a0a0 !important;
+ background: #d4d0c8 !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: #a0a0a0 !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: red !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: black !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #3f5fbf !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #2a00ff !important;
+}
+.syntaxhighlighter .keyword {
+ color: #7f0055 !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #646464 !important;
+}
+.syntaxhighlighter .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #7f0055 !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: red !important;
+}
+
+.syntaxhighlighter .keyword {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .xml .keyword {
+ color: #3f7f7f !important;
+ font-weight: normal !important;
+}
+.syntaxhighlighter .xml .color1, .syntaxhighlighter .xml .color1 a {
+ color: #7f007f !important;
+}
+.syntaxhighlighter .xml .string {
+ font-style: italic !important;
+ color: #2a00ff !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreEmacs.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreEmacs.css
new file mode 100644
index 0000000..706c77a
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreEmacs.css
@@ -0,0 +1,324 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+ -moz-border-radius: 0 0 0 0 !important;
+ -webkit-border-radius: 0 0 0 0 !important;
+ background: none !important;
+ border: 0 !important;
+ bottom: auto !important;
+ float: none !important;
+ height: auto !important;
+ left: auto !important;
+ line-height: 1.1em !important;
+ margin: 0 !important;
+ outline: 0 !important;
+ overflow: visible !important;
+ padding: 0 !important;
+ position: static !important;
+ right: auto !important;
+ text-align: left !important;
+ top: auto !important;
+ vertical-align: baseline !important;
+ width: auto !important;
+ box-sizing: content-box !important;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+ font-size: 1em !important;
+ min-height: inherit !important;
+ min-height: auto !important;
+}
+
+.syntaxhighlighter {
+ width: 100% !important;
+ margin: 1em 0 1em 0 !important;
+ position: relative !important;
+ overflow: auto !important;
+ font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+ overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+ font-style: italic !important;
+}
+.syntaxhighlighter .line {
+ white-space: pre !important;
+}
+.syntaxhighlighter table {
+ width: 100% !important;
+}
+.syntaxhighlighter table caption {
+ text-align: left !important;
+ padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+ width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+ position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+ box-sizing: border-box !important;
+ position: absolute !important;
+ left: 0 !important;
+ top: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+ border: none !important;
+ background: white !important;
+ padding-left: 1em !important;
+ overflow: hidden !important;
+ white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+ text-align: right !important;
+ padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+ padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+ padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+ display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ padding: 0.1em 0.8em 0em 0.8em !important;
+ font-size: 1em !important;
+ position: static !important;
+ width: auto !important;
+ height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+ display: inline !important;
+ margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+ padding: 0 !important;
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+ position: absolute !important;
+ right: 1px !important;
+ top: 1px !important;
+ width: 11px !important;
+ height: 11px !important;
+ font-size: 10px !important;
+ z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+ display: block !important;
+ text-align: center !important;
+ text-decoration: none !important;
+ padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+ display: none !important;
+}
+.syntaxhighlighter.ie {
+ font-size: .9em !important;
+ padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+ line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+ padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+ background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+ color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+ color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+ display: none !important;
+}
+.syntaxhighlighter.printing a {
+ text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+ color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+ color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+ color: #006699 !important;
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+ color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+ color: black !important;
+}
+
+.syntaxhighlighter {
+ background-color: black !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: black !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: black !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #2a3133 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: white !important;
+}
+.syntaxhighlighter table caption {
+ color: #d3d3d3 !important;
+}
+.syntaxhighlighter .gutter {
+ color: #d3d3d3 !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #990000 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #990000 !important;
+ color: black !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #ebdb8d !important;
+ background: black !important;
+ border: 1px solid #990000 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #ebdb8d !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: #ff7d27 !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #990000 !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #9ccff4 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: #d3d3d3 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #ff7d27 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #ff9e7b !important;
+}
+.syntaxhighlighter .keyword {
+ color: aqua !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #aec4de !important;
+}
+.syntaxhighlighter .variable {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #81cef9 !important;
+}
+.syntaxhighlighter .constants {
+ color: #ff9e7b !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: aqua !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: #ebdb8d !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #ff7d27 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #aec4de !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreFadeToGrey.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreFadeToGrey.css
new file mode 100644
index 0000000..6101eba
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreFadeToGrey.css
@@ -0,0 +1,328 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+ -moz-border-radius: 0 0 0 0 !important;
+ -webkit-border-radius: 0 0 0 0 !important;
+ background: none !important;
+ border: 0 !important;
+ bottom: auto !important;
+ float: none !important;
+ height: auto !important;
+ left: auto !important;
+ line-height: 1.1em !important;
+ margin: 0 !important;
+ outline: 0 !important;
+ overflow: visible !important;
+ padding: 0 !important;
+ position: static !important;
+ right: auto !important;
+ text-align: left !important;
+ top: auto !important;
+ vertical-align: baseline !important;
+ width: auto !important;
+ box-sizing: content-box !important;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+ font-size: 1em !important;
+ min-height: inherit !important;
+ min-height: auto !important;
+}
+
+.syntaxhighlighter {
+ width: 100% !important;
+ margin: 1em 0 1em 0 !important;
+ position: relative !important;
+ overflow: auto !important;
+ font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+ overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+ font-style: italic !important;
+}
+.syntaxhighlighter .line {
+ white-space: pre !important;
+}
+.syntaxhighlighter table {
+ width: 100% !important;
+}
+.syntaxhighlighter table caption {
+ text-align: left !important;
+ padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+ width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+ position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+ box-sizing: border-box !important;
+ position: absolute !important;
+ left: 0 !important;
+ top: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+ border: none !important;
+ background: white !important;
+ padding-left: 1em !important;
+ overflow: hidden !important;
+ white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+ text-align: right !important;
+ padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+ padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+ padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+ display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ padding: 0.1em 0.8em 0em 0.8em !important;
+ font-size: 1em !important;
+ position: static !important;
+ width: auto !important;
+ height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+ display: inline !important;
+ margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+ padding: 0 !important;
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+ position: absolute !important;
+ right: 1px !important;
+ top: 1px !important;
+ width: 11px !important;
+ height: 11px !important;
+ font-size: 10px !important;
+ z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+ display: block !important;
+ text-align: center !important;
+ text-decoration: none !important;
+ padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+ display: none !important;
+}
+.syntaxhighlighter.ie {
+ font-size: .9em !important;
+ padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+ line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+ padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+ background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+ color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+ color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+ display: none !important;
+}
+.syntaxhighlighter.printing a {
+ text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+ color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+ color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+ color: #006699 !important;
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+ color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+ color: black !important;
+}
+
+.syntaxhighlighter {
+ background-color: #121212 !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: #121212 !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: #121212 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #2c2c29 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: white !important;
+}
+.syntaxhighlighter table caption {
+ color: white !important;
+}
+.syntaxhighlighter .gutter {
+ color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #3185b9 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #3185b9 !important;
+ color: #121212 !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #3185b9 !important;
+ background: black !important;
+ border: 1px solid #3185b9 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #3185b9 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: #d01d33 !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #3185b9 !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #96daff !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: white !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #696854 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #e3e658 !important;
+}
+.syntaxhighlighter .keyword {
+ color: #d01d33 !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #435a5f !important;
+}
+.syntaxhighlighter .variable {
+ color: #898989 !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #aaaaaa !important;
+}
+.syntaxhighlighter .constants {
+ color: #96daff !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #d01d33 !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: #ffc074 !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #4a8cdb !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #96daff !important;
+}
+
+.syntaxhighlighter .functions {
+ font-weight: bold !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreMDUltra.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreMDUltra.css
new file mode 100644
index 0000000..2923ce7
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreMDUltra.css
@@ -0,0 +1,324 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+ -moz-border-radius: 0 0 0 0 !important;
+ -webkit-border-radius: 0 0 0 0 !important;
+ background: none !important;
+ border: 0 !important;
+ bottom: auto !important;
+ float: none !important;
+ height: auto !important;
+ left: auto !important;
+ line-height: 1.1em !important;
+ margin: 0 !important;
+ outline: 0 !important;
+ overflow: visible !important;
+ padding: 0 !important;
+ position: static !important;
+ right: auto !important;
+ text-align: left !important;
+ top: auto !important;
+ vertical-align: baseline !important;
+ width: auto !important;
+ box-sizing: content-box !important;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+ font-size: 1em !important;
+ min-height: inherit !important;
+ min-height: auto !important;
+}
+
+.syntaxhighlighter {
+ width: 100% !important;
+ margin: 1em 0 1em 0 !important;
+ position: relative !important;
+ overflow: auto !important;
+ font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+ overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+ font-style: italic !important;
+}
+.syntaxhighlighter .line {
+ white-space: pre !important;
+}
+.syntaxhighlighter table {
+ width: 100% !important;
+}
+.syntaxhighlighter table caption {
+ text-align: left !important;
+ padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+ width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+ position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+ box-sizing: border-box !important;
+ position: absolute !important;
+ left: 0 !important;
+ top: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+ border: none !important;
+ background: white !important;
+ padding-left: 1em !important;
+ overflow: hidden !important;
+ white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+ text-align: right !important;
+ padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+ padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+ padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+ display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ padding: 0.1em 0.8em 0em 0.8em !important;
+ font-size: 1em !important;
+ position: static !important;
+ width: auto !important;
+ height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+ display: inline !important;
+ margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+ padding: 0 !important;
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+ position: absolute !important;
+ right: 1px !important;
+ top: 1px !important;
+ width: 11px !important;
+ height: 11px !important;
+ font-size: 10px !important;
+ z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+ display: block !important;
+ text-align: center !important;
+ text-decoration: none !important;
+ padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+ display: none !important;
+}
+.syntaxhighlighter.ie {
+ font-size: .9em !important;
+ padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+ line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+ padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+ background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+ color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+ color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+ display: none !important;
+}
+.syntaxhighlighter.printing a {
+ text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+ color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+ color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+ color: #006699 !important;
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+ color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+ color: black !important;
+}
+
+.syntaxhighlighter {
+ background-color: #222222 !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: #222222 !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: #222222 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #253e5a !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: white !important;
+}
+.syntaxhighlighter table caption {
+ color: lime !important;
+}
+.syntaxhighlighter .gutter {
+ color: #38566f !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #435a5f !important;
+ color: #222222 !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #428bdd !important;
+ background: black !important;
+ border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #428bdd !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: lime !important;
+}
+.syntaxhighlighter .toolbar {
+ color: #aaaaff !important;
+ background: #435a5f !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: #aaaaff !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #9ccff4 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: lime !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #428bdd !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: lime !important;
+}
+.syntaxhighlighter .keyword {
+ color: #aaaaff !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #8aa6c1 !important;
+}
+.syntaxhighlighter .variable {
+ color: aqua !important;
+}
+.syntaxhighlighter .value {
+ color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ff8000 !important;
+}
+.syntaxhighlighter .constants {
+ color: yellow !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #aaaaff !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: red !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: yellow !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #ffaa3e !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreMidnight.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreMidnight.css
new file mode 100644
index 0000000..e3733ee
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreMidnight.css
@@ -0,0 +1,324 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+ -moz-border-radius: 0 0 0 0 !important;
+ -webkit-border-radius: 0 0 0 0 !important;
+ background: none !important;
+ border: 0 !important;
+ bottom: auto !important;
+ float: none !important;
+ height: auto !important;
+ left: auto !important;
+ line-height: 1.1em !important;
+ margin: 0 !important;
+ outline: 0 !important;
+ overflow: visible !important;
+ padding: 0 !important;
+ position: static !important;
+ right: auto !important;
+ text-align: left !important;
+ top: auto !important;
+ vertical-align: baseline !important;
+ width: auto !important;
+ box-sizing: content-box !important;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+ font-size: 1em !important;
+ min-height: inherit !important;
+ min-height: auto !important;
+}
+
+.syntaxhighlighter {
+ width: 100% !important;
+ margin: 1em 0 1em 0 !important;
+ position: relative !important;
+ overflow: auto !important;
+ font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+ overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+ font-style: italic !important;
+}
+.syntaxhighlighter .line {
+ white-space: pre !important;
+}
+.syntaxhighlighter table {
+ width: 100% !important;
+}
+.syntaxhighlighter table caption {
+ text-align: left !important;
+ padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+ width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+ position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+ box-sizing: border-box !important;
+ position: absolute !important;
+ left: 0 !important;
+ top: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+ border: none !important;
+ background: white !important;
+ padding-left: 1em !important;
+ overflow: hidden !important;
+ white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+ text-align: right !important;
+ padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+ padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+ padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+ display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ padding: 0.1em 0.8em 0em 0.8em !important;
+ font-size: 1em !important;
+ position: static !important;
+ width: auto !important;
+ height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+ display: inline !important;
+ margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+ padding: 0 !important;
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+ position: absolute !important;
+ right: 1px !important;
+ top: 1px !important;
+ width: 11px !important;
+ height: 11px !important;
+ font-size: 10px !important;
+ z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+ display: block !important;
+ text-align: center !important;
+ text-decoration: none !important;
+ padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+ display: none !important;
+}
+.syntaxhighlighter.ie {
+ font-size: .9em !important;
+ padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+ line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+ padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+ background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+ color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+ color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+ display: none !important;
+}
+.syntaxhighlighter.printing a {
+ text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+ color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+ color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+ color: #006699 !important;
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+ color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+ color: black !important;
+}
+
+.syntaxhighlighter {
+ background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #253e5a !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: #38566f !important;
+}
+.syntaxhighlighter table caption {
+ color: #d1edff !important;
+}
+.syntaxhighlighter .gutter {
+ color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #435a5f !important;
+ color: #0f192a !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #428bdd !important;
+ background: black !important;
+ border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #428bdd !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: #1dc116 !important;
+}
+.syntaxhighlighter .toolbar {
+ color: #d1edff !important;
+ background: #435a5f !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: #d1edff !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #8aa6c1 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: #d1edff !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #428bdd !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #1dc116 !important;
+}
+.syntaxhighlighter .keyword {
+ color: #b43d3d !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #8aa6c1 !important;
+}
+.syntaxhighlighter .variable {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+ color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+ color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #b43d3d !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: #f8bb00 !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: white !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #ffaa3e !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreRDark.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreRDark.css
new file mode 100644
index 0000000..d093683
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shCoreRDark.css
@@ -0,0 +1,324 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+ -moz-border-radius: 0 0 0 0 !important;
+ -webkit-border-radius: 0 0 0 0 !important;
+ background: none !important;
+ border: 0 !important;
+ bottom: auto !important;
+ float: none !important;
+ height: auto !important;
+ left: auto !important;
+ line-height: 1.1em !important;
+ margin: 0 !important;
+ outline: 0 !important;
+ overflow: visible !important;
+ padding: 0 !important;
+ position: static !important;
+ right: auto !important;
+ text-align: left !important;
+ top: auto !important;
+ vertical-align: baseline !important;
+ width: auto !important;
+ box-sizing: content-box !important;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+ font-size: 1em !important;
+ min-height: inherit !important;
+ min-height: auto !important;
+}
+
+.syntaxhighlighter {
+ width: 100% !important;
+ margin: 1em 0 1em 0 !important;
+ position: relative !important;
+ overflow: auto !important;
+ font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+ overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+ font-style: italic !important;
+}
+.syntaxhighlighter .line {
+ white-space: pre !important;
+}
+.syntaxhighlighter table {
+ width: 100% !important;
+}
+.syntaxhighlighter table caption {
+ text-align: left !important;
+ padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+ width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+ position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+ box-sizing: border-box !important;
+ position: absolute !important;
+ left: 0 !important;
+ top: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+ border: none !important;
+ background: white !important;
+ padding-left: 1em !important;
+ overflow: hidden !important;
+ white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+ text-align: right !important;
+ padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+ padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+ padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+ display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ padding: 0.1em 0.8em 0em 0.8em !important;
+ font-size: 1em !important;
+ position: static !important;
+ width: auto !important;
+ height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+ display: inline !important;
+ margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+ padding: 0 !important;
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+ position: absolute !important;
+ right: 1px !important;
+ top: 1px !important;
+ width: 11px !important;
+ height: 11px !important;
+ font-size: 10px !important;
+ z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+ display: block !important;
+ text-align: center !important;
+ text-decoration: none !important;
+ padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+ display: none !important;
+}
+.syntaxhighlighter.ie {
+ font-size: .9em !important;
+ padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+ line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+ padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+ background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+ color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+ color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+ display: none !important;
+}
+.syntaxhighlighter.printing a {
+ text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+ color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+ color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+ color: #006699 !important;
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+ color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+ color: black !important;
+}
+
+.syntaxhighlighter {
+ background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #323e41 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: #b9bdb6 !important;
+}
+.syntaxhighlighter table caption {
+ color: #b9bdb6 !important;
+}
+.syntaxhighlighter .gutter {
+ color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #435a5f !important;
+ color: #1b2426 !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #5ba1cf !important;
+ background: black !important;
+ border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #5ba1cf !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: #5ce638 !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #435a5f !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #e0e8ff !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: #b9bdb6 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #878a85 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #5ce638 !important;
+}
+.syntaxhighlighter .keyword {
+ color: #5ba1cf !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #435a5f !important;
+}
+.syntaxhighlighter .variable {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+ color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #5ba1cf !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: #e0e8ff !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: white !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #ffaa3e !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeDefault.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeDefault.css
new file mode 100644
index 0000000..1365411
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeDefault.css
@@ -0,0 +1,117 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #e0e0e0 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: black !important;
+}
+.syntaxhighlighter table caption {
+ color: black !important;
+}
+.syntaxhighlighter .gutter {
+ color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #6ce26c !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #6ce26c !important;
+ color: white !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: blue !important;
+ background: white !important;
+ border: 1px solid #6ce26c !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: blue !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: red !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #6ce26c !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: black !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: black !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: blue !important;
+}
+.syntaxhighlighter .keyword {
+ color: #006699 !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #006699 !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: red !important;
+}
+
+.syntaxhighlighter .keyword {
+ font-weight: bold !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeDjango.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeDjango.css
new file mode 100644
index 0000000..d8b4313
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeDjango.css
@@ -0,0 +1,120 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+ background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #233729 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: white !important;
+}
+.syntaxhighlighter table caption {
+ color: #f8f8f8 !important;
+}
+.syntaxhighlighter .gutter {
+ color: #497958 !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #41a83e !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #41a83e !important;
+ color: #0a2b1d !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #96dd3b !important;
+ background: black !important;
+ border: 1px solid #41a83e !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #96dd3b !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #41a83e !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #ffe862 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: #f8f8f8 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #336442 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #9df39f !important;
+}
+.syntaxhighlighter .keyword {
+ color: #96dd3b !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #91bb9e !important;
+}
+.syntaxhighlighter .variable {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+ color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+ color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #96dd3b !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: #eb939a !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #91bb9e !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #edef7d !important;
+}
+
+.syntaxhighlighter .comments {
+ font-style: italic !important;
+}
+.syntaxhighlighter .keyword {
+ font-weight: bold !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeEclipse.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeEclipse.css
new file mode 100644
index 0000000..77377d9
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeEclipse.css
@@ -0,0 +1,128 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #c3defe !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: white !important;
+}
+.syntaxhighlighter table caption {
+ color: black !important;
+}
+.syntaxhighlighter .gutter {
+ color: #787878 !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #d4d0c8 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #d4d0c8 !important;
+ color: white !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #3f5fbf !important;
+ background: white !important;
+ border: 1px solid #d4d0c8 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #3f5fbf !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter .toolbar {
+ color: #a0a0a0 !important;
+ background: #d4d0c8 !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: #a0a0a0 !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: red !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: black !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #3f5fbf !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #2a00ff !important;
+}
+.syntaxhighlighter .keyword {
+ color: #7f0055 !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #646464 !important;
+}
+.syntaxhighlighter .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #7f0055 !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: red !important;
+}
+
+.syntaxhighlighter .keyword {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .xml .keyword {
+ color: #3f7f7f !important;
+ font-weight: normal !important;
+}
+.syntaxhighlighter .xml .color1, .syntaxhighlighter .xml .color1 a {
+ color: #7f007f !important;
+}
+.syntaxhighlighter .xml .string {
+ font-style: italic !important;
+ color: #2a00ff !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeEmacs.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeEmacs.css
new file mode 100644
index 0000000..dae5053
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeEmacs.css
@@ -0,0 +1,113 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+ background-color: black !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: black !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: black !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #2a3133 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: white !important;
+}
+.syntaxhighlighter table caption {
+ color: #d3d3d3 !important;
+}
+.syntaxhighlighter .gutter {
+ color: #d3d3d3 !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #990000 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #990000 !important;
+ color: black !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #ebdb8d !important;
+ background: black !important;
+ border: 1px solid #990000 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #ebdb8d !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: #ff7d27 !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #990000 !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #9ccff4 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: #d3d3d3 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #ff7d27 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #ff9e7b !important;
+}
+.syntaxhighlighter .keyword {
+ color: aqua !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #aec4de !important;
+}
+.syntaxhighlighter .variable {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #81cef9 !important;
+}
+.syntaxhighlighter .constants {
+ color: #ff9e7b !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: aqua !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: #ebdb8d !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #ff7d27 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #aec4de !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeFadeToGrey.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeFadeToGrey.css
new file mode 100644
index 0000000..8fbd871
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeFadeToGrey.css
@@ -0,0 +1,117 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+ background-color: #121212 !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: #121212 !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: #121212 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #2c2c29 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: white !important;
+}
+.syntaxhighlighter table caption {
+ color: white !important;
+}
+.syntaxhighlighter .gutter {
+ color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #3185b9 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #3185b9 !important;
+ color: #121212 !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #3185b9 !important;
+ background: black !important;
+ border: 1px solid #3185b9 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #3185b9 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: #d01d33 !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #3185b9 !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #96daff !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: white !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #696854 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #e3e658 !important;
+}
+.syntaxhighlighter .keyword {
+ color: #d01d33 !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #435a5f !important;
+}
+.syntaxhighlighter .variable {
+ color: #898989 !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #aaaaaa !important;
+}
+.syntaxhighlighter .constants {
+ color: #96daff !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #d01d33 !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: #ffc074 !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #4a8cdb !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #96daff !important;
+}
+
+.syntaxhighlighter .functions {
+ font-weight: bold !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeMDUltra.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeMDUltra.css
new file mode 100644
index 0000000..f4db39c
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeMDUltra.css
@@ -0,0 +1,113 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+ background-color: #222222 !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: #222222 !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: #222222 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #253e5a !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: white !important;
+}
+.syntaxhighlighter table caption {
+ color: lime !important;
+}
+.syntaxhighlighter .gutter {
+ color: #38566f !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #435a5f !important;
+ color: #222222 !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #428bdd !important;
+ background: black !important;
+ border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #428bdd !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: lime !important;
+}
+.syntaxhighlighter .toolbar {
+ color: #aaaaff !important;
+ background: #435a5f !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: #aaaaff !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #9ccff4 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: lime !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #428bdd !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: lime !important;
+}
+.syntaxhighlighter .keyword {
+ color: #aaaaff !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #8aa6c1 !important;
+}
+.syntaxhighlighter .variable {
+ color: aqua !important;
+}
+.syntaxhighlighter .value {
+ color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ff8000 !important;
+}
+.syntaxhighlighter .constants {
+ color: yellow !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #aaaaff !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: red !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: yellow !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #ffaa3e !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeMidnight.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeMidnight.css
new file mode 100644
index 0000000..c49563c
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeMidnight.css
@@ -0,0 +1,113 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+ background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #253e5a !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: #38566f !important;
+}
+.syntaxhighlighter table caption {
+ color: #d1edff !important;
+}
+.syntaxhighlighter .gutter {
+ color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #435a5f !important;
+ color: #0f192a !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #428bdd !important;
+ background: black !important;
+ border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #428bdd !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: #1dc116 !important;
+}
+.syntaxhighlighter .toolbar {
+ color: #d1edff !important;
+ background: #435a5f !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: #d1edff !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #8aa6c1 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: #d1edff !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #428bdd !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #1dc116 !important;
+}
+.syntaxhighlighter .keyword {
+ color: #b43d3d !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #8aa6c1 !important;
+}
+.syntaxhighlighter .variable {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+ color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+ color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #b43d3d !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: #f8bb00 !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: white !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #ffaa3e !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeRDark.css b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeRDark.css
new file mode 100644
index 0000000..6305a10
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/styles/shThemeRDark.css
@@ -0,0 +1,113 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+ background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #323e41 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: #b9bdb6 !important;
+}
+.syntaxhighlighter table caption {
+ color: #b9bdb6 !important;
+}
+.syntaxhighlighter .gutter {
+ color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #435a5f !important;
+ color: #1b2426 !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: #5ba1cf !important;
+ background: black !important;
+ border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: #5ba1cf !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: #5ce638 !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #435a5f !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: #e0e8ff !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: #b9bdb6 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #878a85 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: #5ce638 !important;
+}
+.syntaxhighlighter .keyword {
+ color: #5ba1cf !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: #435a5f !important;
+}
+.syntaxhighlighter .variable {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+ color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #5ba1cf !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: #e0e8ff !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: white !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: #ffaa3e !important;
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/brushes/sass.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/brushes/sass.html
new file mode 100644
index 0000000..f415684
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/brushes/sass.html
@@ -0,0 +1,204 @@
+
\ No newline at end of file
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/brushes_tests.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/brushes_tests.html
new file mode 100644
index 0000000..e1e2068
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/brushes_tests.html
@@ -0,0 +1,136 @@
+
+
+
+
+ SyntaxHighlighter Brushes Tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/001_basic.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/001_basic.html
new file mode 100644
index 0000000..1d57334
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/001_basic.html
@@ -0,0 +1,42 @@
+
+ /**
+ * multiline comment
+ */
+
+ text
+
+ // single line comment
+
+ text
+
+ "string" text 'string' text "string"
+ "string with \" escape" text 'string with \' escape' text "string with \" escape"
+
+ var code = '\
+ function helloWorld()\
+ {\
+ // this is great!\
+ for(var i = 0; i <= 1; i++)\
+ alert("yay");\
+ }\
+ ';
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/002_brushes.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/002_brushes.html
new file mode 100644
index 0000000..e9c64e2
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/002_brushes.html
@@ -0,0 +1,50 @@
+
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+ test
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/003_script_tag.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/003_script_tag.html
new file mode 100644
index 0000000..9b44292
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/003_script_tag.html
@@ -0,0 +1,42 @@
+
+
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/004_url_parsing.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/004_url_parsing.html
new file mode 100644
index 0000000..d489a66
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/004_url_parsing.html
@@ -0,0 +1,43 @@
+
+/**
+ * Please see <http://www.alexgorbatchev.come/?test=1&y=2>
+ */
+var home = "http://www.alexgorbatchev.come/?test=1&y=2;test/1/2/3;";
+// < http://www.gnu.org/licenses/?test=1&y=2 >.
+
+// Test embedded URLs that terminate at a left angle bracket.
+// See bug #28: http://bitbucket.org/alexg/syntaxhighlighter/issue/28/
+"http://www.example.com/song2.mp3 ";
+
+
+
\ No newline at end of file
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/005_no_gutter.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/005_no_gutter.html
new file mode 100644
index 0000000..6e6b1be
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/005_no_gutter.html
@@ -0,0 +1,33 @@
+
+ public Image getImage(URL url, String name) {
+ try {
+ /*
+ Regular multiline comment.
+ */
+ return getImage(new URL(url, name));
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/006_pad_line_numbers.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/006_pad_line_numbers.html
new file mode 100644
index 0000000..8ebdd55
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/006_pad_line_numbers.html
@@ -0,0 +1,39 @@
+
+/**
+ * Returns an Image object that can then be painted on the screen.
+ * The url argument must specify an absolute {@link URL}. The name
+ * argument is a specifier that is relative to the url argument.
+ *
+ * @param url an absolute URL giving the base location of the image
+ * @param name the location of the image, relative to the url argument
+ * @return the image at the specified URL
+ * @see Image
+ */
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/007_collapse.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/007_collapse.html
new file mode 100644
index 0000000..2643bb5
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/007_collapse.html
@@ -0,0 +1,60 @@
+
+ /**
+ * Returns an Image object that can then be painted on the screen.
+ * The url argument must specify an absolute {@link URL}. The name
+ * argument is a specifier that is relative to the url argument.
+ *
+ * @param url an absolute URL giving the base location of the image
+ * @param name the location of the image, relative to the url argument
+ * @return the image at the specified URL
+ * @see Image
+ */
+
+
+
+ /**
+ * Returns an Image object that can then be painted on the screen.
+ * The url argument must specify an absolute {@link URL}. The name
+ * argument is a specifier that is relative to the url argument.
+ *
+ * @param url an absolute URL giving the base location of the image
+ * @param name the location of the image, relative to the url argument
+ * @return the image at the specified URL
+ * @see Image
+ */
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/007_collapse_interaction.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/007_collapse_interaction.html
new file mode 100644
index 0000000..ea72046
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/007_collapse_interaction.html
@@ -0,0 +1,44 @@
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/008_first_line.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/008_first_line.html
new file mode 100644
index 0000000..169dc38
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/008_first_line.html
@@ -0,0 +1,29 @@
+
+ partial class Foo
+ {
+ function test()
+ {
+ yield return;
+ }
+ }
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/009_class_name.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/009_class_name.html
new file mode 100644
index 0000000..f2437d5
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/009_class_name.html
@@ -0,0 +1,32 @@
+
+ public Image getImage(URL url, String name) {
+ try {
+ /*
+ Regular multiline comment.
+ */
+ return getImage(new URL(url, name));
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/010_highlight.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/010_highlight.html
new file mode 100644
index 0000000..64b6613
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/010_highlight.html
@@ -0,0 +1,70 @@
+
+ public function validateStrongPassword(password:String):Boolean
+ {
+ if (password == null || password.length <= 0)
+ {
+ return false;
+ }
+
+ return STRONG_PASSWORD_PATTERN.test(password);
+ }
+
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/011_smart_tabs.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/011_smart_tabs.html
new file mode 100644
index 0000000..d6d62fc
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/011_smart_tabs.html
@@ -0,0 +1,98 @@
+
+ the words in this paragraph
+ should look like they are
+ evenly spaced between columns
+
+
+
+ the words in this paragraph
+ should look like they are
+ evenly spaced between columns
+
+
+
+ the words in this paragraph
+ should look out of whack
+ because smart tabs are disabled
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/012_server_side.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/012_server_side.html
new file mode 100644
index 0000000..1bb4217
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/012_server_side.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/013_html_script.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/013_html_script.html
new file mode 100644
index 0000000..c0a1201
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/013_html_script.html
@@ -0,0 +1,34 @@
+
+<hello>
+ <%
+ package free.cafekiwi.gotapi;
+ %>
+</hello>
+
+
+<%= print(); %>
+
+
+
\ No newline at end of file
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/014_legacy.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/014_legacy.html
new file mode 100644
index 0000000..eb4343e
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/cases/014_legacy.html
@@ -0,0 +1,70 @@
+basic check
+no toolbar
+no gutter
+collapsed
+first line
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/commonjs_tests.js b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/commonjs_tests.js
new file mode 100644
index 0000000..cda8162
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/commonjs_tests.js
@@ -0,0 +1,52 @@
+/**
+ * This is a CommonJS compatibility test. You can run this file with node.
+ */
+require.paths.unshift(__dirname + '/../scripts');
+
+var sys = require('sys'),
+ shSyntaxHighlighter = require('shCore').SyntaxHighlighter,
+ code = 'test',
+ brushes = [
+ 'AS3',
+ 'AppleScript',
+ 'Bash',
+ 'CSharp',
+ 'ColdFusion',
+ 'Cpp',
+ 'Css',
+ 'Delphi',
+ 'Diff',
+ 'Erlang',
+ 'Groovy',
+ 'JScript',
+ 'Java',
+ 'JavaFX',
+ 'Perl',
+ 'Php',
+ 'Plain',
+ 'PowerShell',
+ 'Python',
+ 'Ruby',
+ 'Sass',
+ 'Scala',
+ 'Sql',
+ 'Vb',
+ 'Xml'
+ ]
+ ;
+
+brushes.sort();
+
+for (var i = 0; i < brushes.length; i++)
+{
+ var name = brushes[i],
+ brush = require('shBrush' + name).Brush
+ ;
+
+ brush = new brush();
+ brush.init({ toolbar: false });
+
+ var result = brush.getHtml(code);
+
+ sys.puts(name + (result != null ? ': ok' : ': NOT OK'));
+}
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/js/jquery-1.4.2.js b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/js/jquery-1.4.2.js
new file mode 100644
index 0000000..e414a7e
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/js/jquery-1.4.2.js
@@ -0,0 +1,6240 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function( window, undefined ) {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // Use the correct document accordingly with window argument (sandbox)
+ document = window.document,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // (both of which we optimize for)
+ quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
+
+ // Is it a simple selector
+ isSimple = /^.[^:#\[\.,]*$/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+
+ // Used for trimming whitespace
+ rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // Has the ready events already been bound?
+ readyBound = false,
+
+ // The functions to execute on DOM ready
+ readyList = [],
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwnProperty = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ indexOf = Array.prototype.indexOf;
+
+jQuery.fn = jQuery.prototype = {
+ init: function( selector, context ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = "body";
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ match = quickExpr.exec( selector );
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ doc = (context ? context.ownerDocument || context : document);
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = buildFragment( [ match[1] ], [ doc ] );
+ selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ if ( elem ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $("TAG")
+ } else if ( !context && /^\w+$/.test( selector ) ) {
+ this.selector = selector;
+ this.context = document;
+ selector = document.getElementsByTagName( selector );
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return (context || rootjQuery).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return jQuery( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if (selector.selector !== undefined) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.4.2",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = jQuery();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // If the DOM is already ready
+ if ( jQuery.isReady ) {
+ // Execute the function immediately
+ fn.call( document, jQuery );
+
+ // Otherwise, remember the function for later
+ } else if ( readyList ) {
+ // Add the function to the wait list
+ readyList.push( fn );
+ }
+
+ return this;
+ },
+
+ eq: function( i ) {
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, +i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || jQuery(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ // copy reference to target object
+ var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging object literal values or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
+ var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
+ : jQuery.isArray(copy) ? [] : {};
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ window.$ = _$;
+
+ if ( deep ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // Handle when the DOM is ready
+ ready: function() {
+ // Make sure that the DOM is not already loaded
+ if ( !jQuery.isReady ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 13 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If there are functions bound, to execute
+ if ( readyList ) {
+ // Execute all of them
+ var fn, i = 0;
+ while ( (fn = readyList[ i++ ]) ) {
+ fn.call( document, jQuery );
+ }
+
+ // Reset the list of functions
+ readyList = null;
+ }
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.triggerHandler ) {
+ jQuery( document ).triggerHandler( "ready" );
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyBound ) {
+ return;
+ }
+
+ readyBound = true;
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ return jQuery.ready();
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent("onreadystatechange", DOMContentLoaded);
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return toString.call(obj) === "[object Function]";
+ },
+
+ isArray: function( obj ) {
+ return toString.call(obj) === "[object Array]";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
+ return false;
+ }
+
+ // Not own constructor property must be Object
+ if ( obj.constructor
+ && !hasOwnProperty.call(obj, "constructor")
+ && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwnProperty.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw msg;
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {
+
+ // Try to use the native JSON parser first
+ return window.JSON && window.JSON.parse ?
+ window.JSON.parse( data ) :
+ (new Function("return " + data))();
+
+ } else {
+ jQuery.error( "Invalid JSON: " + data );
+ }
+ },
+
+ noop: function() {},
+
+ // Evalulates a script in a global context
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test(data) ) {
+ // Inspired by code by Andrea Giammarchi
+ // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+ var head = document.getElementsByTagName("head")[0] || document.documentElement,
+ script = document.createElement("script");
+
+ script.type = "text/javascript";
+
+ if ( jQuery.support.scriptEval ) {
+ script.appendChild( document.createTextNode( data ) );
+ } else {
+ script.text = data;
+ }
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709).
+ head.insertBefore( script, head.firstChild );
+ head.removeChild( script );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction(object);
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( var value = object[0];
+ i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
+ }
+ }
+
+ return object;
+ },
+
+ trim: function( text ) {
+ return (text || "").replace( rtrim, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // The extra typeof function check is to prevent crashes
+ // in Safari 2 (See: #3039)
+ if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length, j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [];
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ if ( !inv !== !callback( elems[ i ], i ) ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var ret = [], value;
+
+ // Go through the array, translating each of the items to their
+ // new value (or values).
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ proxy: function( fn, proxy, thisObject ) {
+ if ( arguments.length === 2 ) {
+ if ( typeof proxy === "string" ) {
+ thisObject = fn;
+ fn = thisObject[ proxy ];
+ proxy = undefined;
+
+ } else if ( proxy && !jQuery.isFunction( proxy ) ) {
+ thisObject = proxy;
+ proxy = undefined;
+ }
+ }
+
+ if ( !proxy && fn ) {
+ proxy = function() {
+ return fn.apply( thisObject || this, arguments );
+ };
+ }
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ if ( fn ) {
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+ }
+
+ // So proxy can be declared as an argument
+ return proxy;
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+ /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) ||
+ /(msie) ([\w.]+)/.exec( ua ) ||
+ !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ browser: {}
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+if ( indexOf ) {
+ jQuery.inArray = function( elem, array ) {
+ return indexOf.call( array, elem );
+ };
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch( error ) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+function evalScript( i, elem ) {
+ if ( elem.src ) {
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+ } else {
+ jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+}
+
+// Mutifunctional method to get and set values to a collection
+// The value/s can be optionally by executed if its a function
+function access( elems, key, value, exec, fn, pass ) {
+ var length = elems.length;
+
+ // Setting many attributes
+ if ( typeof key === "object" ) {
+ for ( var k in key ) {
+ access( elems, k, key[k], exec, fn, value );
+ }
+ return elems;
+ }
+
+ // Setting one attribute
+ if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = !pass && exec && jQuery.isFunction(value);
+
+ for ( var i = 0; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+
+ return elems;
+ }
+
+ // Getting an attribute
+ return length ? fn( elems[0], key ) : undefined;
+}
+
+function now() {
+ return (new Date).getTime();
+}
+(function() {
+
+ jQuery.support = {};
+
+ var root = document.documentElement,
+ script = document.createElement("script"),
+ div = document.createElement("div"),
+ id = "script" + now();
+
+ div.style.display = "none";
+ div.innerHTML = "
a";
+
+ var all = div.getElementsByTagName("*"),
+ a = div.getElementsByTagName("a")[0];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return;
+ }
+
+ jQuery.support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: div.firstChild.nodeType === 3,
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText insted)
+ style: /red/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: a.getAttribute("href") === "/a",
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.55$/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: div.getElementsByTagName("input")[0].value === "on",
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,
+
+ parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,
+
+ // Will be defined later
+ deleteExpando: true,
+ checkClone: false,
+ scriptEval: false,
+ noCloneEvent: true,
+ boxModel: null
+ };
+
+ script.type = "text/javascript";
+ try {
+ script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
+ } catch(e) {}
+
+ root.insertBefore( script, root.firstChild );
+
+ // Make sure that the execution of code works by injecting a script
+ // tag with appendChild/createTextNode
+ // (IE doesn't support this, fails, and uses .text instead)
+ if ( window[ id ] ) {
+ jQuery.support.scriptEval = true;
+ delete window[ id ];
+ }
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete script.test;
+
+ } catch(e) {
+ jQuery.support.deleteExpando = false;
+ }
+
+ root.removeChild( script );
+
+ if ( div.attachEvent && div.fireEvent ) {
+ div.attachEvent("onclick", function click() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ jQuery.support.noCloneEvent = false;
+ div.detachEvent("onclick", click);
+ });
+ div.cloneNode(true).fireEvent("onclick");
+ }
+
+ div = document.createElement("div");
+ div.innerHTML = "";
+
+ var fragment = document.createDocumentFragment();
+ fragment.appendChild( div.firstChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
+
+ // Figure out if the W3C box model works as expected
+ // document.body must exist before we can do this
+ jQuery(function() {
+ var div = document.createElement("div");
+ div.style.width = div.style.paddingLeft = "1px";
+
+ document.body.appendChild( div );
+ jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
+ document.body.removeChild( div ).style.display = 'none';
+
+ div = null;
+ });
+
+ // Technique from Juriy Zaytsev
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+ var eventSupported = function( eventName ) {
+ var el = document.createElement("div");
+ eventName = "on" + eventName;
+
+ var isSupported = (eventName in el);
+ if ( !isSupported ) {
+ el.setAttribute(eventName, "return;");
+ isSupported = typeof el[eventName] === "function";
+ }
+ el = null;
+
+ return isSupported;
+ };
+
+ jQuery.support.submitBubbles = eventSupported("submit");
+ jQuery.support.changeBubbles = eventSupported("change");
+
+ // release memory in IE
+ root = script = div = all = a = null;
+})();
+
+jQuery.props = {
+ "for": "htmlFor",
+ "class": "className",
+ readonly: "readOnly",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ tabindex: "tabIndex",
+ usemap: "useMap",
+ frameborder: "frameBorder"
+};
+var expando = "jQuery" + now(), uuid = 0, windowData = {};
+
+jQuery.extend({
+ cache: {},
+
+ expando:expando,
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ "object": true,
+ "applet": true
+ },
+
+ data: function( elem, name, data ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ return;
+ }
+
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ], cache = jQuery.cache, thisCache;
+
+ if ( !id && typeof name === "string" && data === undefined ) {
+ return null;
+ }
+
+ // Compute a unique ID for the element
+ if ( !id ) {
+ id = ++uuid;
+ }
+
+ // Avoid generating a new cache unless none exists and we
+ // want to manipulate it.
+ if ( typeof name === "object" ) {
+ elem[ expando ] = id;
+ thisCache = cache[ id ] = jQuery.extend(true, {}, name);
+
+ } else if ( !cache[ id ] ) {
+ elem[ expando ] = id;
+ cache[ id ] = {};
+ }
+
+ thisCache = cache[ id ];
+
+ // Prevent overriding the named cache with undefined values
+ if ( data !== undefined ) {
+ thisCache[ name ] = data;
+ }
+
+ return typeof name === "string" ? thisCache[ name ] : thisCache;
+ },
+
+ removeData: function( elem, name ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ return;
+ }
+
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];
+
+ // If we want to remove a specific section of the element's data
+ if ( name ) {
+ if ( thisCache ) {
+ // Remove the section of cache data
+ delete thisCache[ name ];
+
+ // If we've removed all the data, remove the element's cache
+ if ( jQuery.isEmptyObject(thisCache) ) {
+ jQuery.removeData( elem );
+ }
+ }
+
+ // Otherwise, we want to remove all of the element's data
+ } else {
+ if ( jQuery.support.deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ // Completely remove the data cache
+ delete cache[ id ];
+ }
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ if ( typeof key === "undefined" && this.length ) {
+ return jQuery.data( this[0] );
+
+ } else if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ if ( data === undefined && this.length ) {
+ data = jQuery.data( this[0], key );
+ }
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ } else {
+ return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() {
+ jQuery.data( this, key, value );
+ });
+ }
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+jQuery.extend({
+ queue: function( elem, type, data ) {
+ if ( !elem ) {
+ return;
+ }
+
+ type = (type || "fx") + "queue";
+ var q = jQuery.data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( !data ) {
+ return q || [];
+ }
+
+ if ( !q || jQuery.isArray(data) ) {
+ q = jQuery.data( elem, type, jQuery.makeArray(data) );
+
+ } else {
+ q.push( data );
+ }
+
+ return q;
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ), fn = queue.shift();
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ }
+
+ if ( fn ) {
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift("inprogress");
+ }
+
+ fn.call(elem, function() {
+ jQuery.dequeue(elem, type);
+ });
+ }
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ }
+
+ if ( data === undefined ) {
+ return jQuery.queue( this[0], type );
+ }
+ return this.each(function( i, elem ) {
+ var queue = jQuery.queue( this, type, data );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function() {
+ var elem = this;
+ setTimeout(function() {
+ jQuery.dequeue( elem, type );
+ }, time );
+ });
+ },
+
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ }
+});
+var rclass = /[\n\t]/g,
+ rspace = /\s+/,
+ rreturn = /\r/g,
+ rspecialurl = /href|src|style/,
+ rtype = /(button|input)/i,
+ rfocusable = /(button|input|object|select|textarea)/i,
+ rclickable = /^(a|area)$/i,
+ rradiocheck = /radio|checkbox/;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return access( this, name, value, true, jQuery.attr );
+ },
+
+ removeAttr: function( name, fn ) {
+ return this.each(function(){
+ jQuery.attr( this, name, "" );
+ if ( this.nodeType === 1 ) {
+ this.removeAttribute( name );
+ }
+ });
+ },
+
+ addClass: function( value ) {
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.addClass( value.call(this, i, self.attr("class")) );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ var classNames = (value || "").split( rspace );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var elem = this[i];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className ) {
+ elem.className = value;
+
+ } else {
+ var className = " " + elem.className + " ", setClass = elem.className;
+ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
+ setClass += " " + classNames[c];
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.removeClass( value.call(this, i, self.attr("class")) );
+ });
+ }
+
+ if ( (value && typeof value === "string") || value === undefined ) {
+ var classNames = (value || "").split(rspace);
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var elem = this[i];
+
+ if ( elem.nodeType === 1 && elem.className ) {
+ if ( value ) {
+ var className = (" " + elem.className + " ").replace(rclass, " ");
+ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+ className = className.replace(" " + classNames[c] + " ", " ");
+ }
+ elem.className = jQuery.trim( className );
+
+ } else {
+ elem.className = "";
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value, isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className, i = 0, self = jQuery(this),
+ state = stateVal,
+ classNames = value.split( rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space seperated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery.data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ";
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ if ( value === undefined ) {
+ var elem = this[0];
+
+ if ( elem ) {
+ if ( jQuery.nodeName( elem, "option" ) ) {
+ return (elem.attributes.value || {}).specified ? elem.value : elem.text;
+ }
+
+ // We need to handle select boxes special
+ if ( jQuery.nodeName( elem, "select" ) ) {
+ var index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type === "select-one";
+
+ // Nothing was selected
+ if ( index < 0 ) {
+ return null;
+ }
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ if ( option.selected ) {
+ // Get the specifc value for the option
+ value = jQuery(option).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+ }
+
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+
+
+ // Everything else, we just grab the value
+ return (elem.value || "").replace(rreturn, "");
+
+ }
+
+ return undefined;
+ }
+
+ var isFunction = jQuery.isFunction(value);
+
+ return this.each(function(i) {
+ var self = jQuery(this), val = value;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call(this, i, self.val());
+ }
+
+ // Typecast each time if the value is a Function and the appended
+ // value is therefore different each time.
+ if ( typeof val === "number" ) {
+ val += "";
+ }
+
+ if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
+ this.checked = jQuery.inArray( self.val(), val ) >= 0;
+
+ } else if ( jQuery.nodeName( this, "select" ) ) {
+ var values = jQuery.makeArray(val);
+
+ jQuery( "option", this ).each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ this.selectedIndex = -1;
+ }
+
+ } else {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ attrFn: {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true
+ },
+
+ attr: function( elem, name, value, pass ) {
+ // don't set attributes on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return undefined;
+ }
+
+ if ( pass && name in jQuery.attrFn ) {
+ return jQuery(elem)[name](value);
+ }
+
+ var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
+ // Whether we are setting (or getting)
+ set = value !== undefined;
+
+ // Try to normalize/fix the name
+ name = notxml && jQuery.props[ name ] || name;
+
+ // Only do all the following if this is a node (faster for style)
+ if ( elem.nodeType === 1 ) {
+ // These attributes require special treatment
+ var special = rspecialurl.test( name );
+
+ // Safari mis-reports the default selected property of an option
+ // Accessing the parent's selectedIndex property fixes it
+ if ( name === "selected" && !jQuery.support.optSelected ) {
+ var parent = elem.parentNode;
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ }
+
+ // If applicable, access the attribute via the DOM 0 way
+ if ( name in elem && notxml && !special ) {
+ if ( set ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ }
+
+ elem[ name ] = value;
+ }
+
+ // browsers index elements by id/name on forms, give priority to attributes.
+ if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
+ return elem.getAttributeNode( name ).nodeValue;
+ }
+
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ if ( name === "tabIndex" ) {
+ var attributeNode = elem.getAttributeNode( "tabIndex" );
+
+ return attributeNode && attributeNode.specified ?
+ attributeNode.value :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+
+ return elem[ name ];
+ }
+
+ if ( !jQuery.support.style && notxml && name === "style" ) {
+ if ( set ) {
+ elem.style.cssText = "" + value;
+ }
+
+ return elem.style.cssText;
+ }
+
+ if ( set ) {
+ // convert the value to a string (all browsers do this but IE) see #1070
+ elem.setAttribute( name, "" + value );
+ }
+
+ var attr = !jQuery.support.hrefNormalized && notxml && special ?
+ // Some attributes require a special call on IE
+ elem.getAttribute( name, 2 ) :
+ elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return attr === null ? undefined : attr;
+ }
+
+ // elem is actually elem.style ... set the style
+ // Using attr for specific style information is now deprecated. Use style instead.
+ return jQuery.style( elem, name, value );
+ }
+});
+var rnamespaces = /\.(.*)$/,
+ fcleanup = function( nm ) {
+ return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
+ return "\\" + ch;
+ });
+ };
+
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function( elem, types, handler, data ) {
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // For whatever reason, IE has trouble passing the window object
+ // around, causing it to be cloned in the process
+ if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
+ elem = window;
+ }
+
+ var handleObjIn, handleObj;
+
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ }
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure
+ var elemData = jQuery.data( elem );
+
+ // If no elemData is found then we must be trying to bind to one of the
+ // banned noData elements
+ if ( !elemData ) {
+ return;
+ }
+
+ var events = elemData.events = elemData.events || {},
+ eventHandle = elemData.handle, eventHandle;
+
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function() {
+ // Handle the second event of a trigger and when
+ // an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+ jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ }
+
+ // Add elem as a property of the handle function
+ // This is to prevent a memory leak with non-native events in IE.
+ eventHandle.elem = elem;
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ var type, i = 0, namespaces;
+
+ while ( (type = types[ i++ ]) ) {
+ handleObj = handleObjIn ?
+ jQuery.extend({}, handleObjIn) :
+ { handler: handler, data: data };
+
+ // Namespaced event handlers
+ if ( type.indexOf(".") > -1 ) {
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ handleObj.namespace = namespaces.slice(0).sort().join(".");
+
+ } else {
+ namespaces = [];
+ handleObj.namespace = "";
+ }
+
+ handleObj.type = type;
+ handleObj.guid = handler.guid;
+
+ // Get the current list of functions bound to this event
+ var handlers = events[ type ],
+ special = jQuery.event.special[ type ] || {};
+
+ // Init the event handler queue
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+
+ // Check for a special event handler
+ // Only use addEventListener/attachEvent if the special
+ // events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add the function to the element's handler list
+ handlers.push( handleObj );
+
+ // Keep track of which events have been used, for global triggering
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, pos ) {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+ elemData = jQuery.data( elem ),
+ events = elemData && elemData.events;
+
+ if ( !elemData || !events ) {
+ return;
+ }
+
+ // types is actually an event object here
+ if ( types && types.type ) {
+ handler = types.handler;
+ types = types.type;
+ }
+
+ // Unbind all events for the element
+ if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+ types = types || "";
+
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types );
+ }
+
+ return;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).unbind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ while ( (type = types[ i++ ]) ) {
+ origType = type;
+ handleObj = null;
+ all = type.indexOf(".") < 0;
+ namespaces = [];
+
+ if ( !all ) {
+ // Namespaced event handlers
+ namespaces = type.split(".");
+ type = namespaces.shift();
+
+ namespace = new RegExp("(^|\\.)" +
+ jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
+ }
+
+ eventType = events[ type ];
+
+ if ( !eventType ) {
+ continue;
+ }
+
+ if ( !handler ) {
+ for ( var j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ jQuery.event.remove( elem, origType, handleObj.handler, j );
+ eventType.splice( j--, 1 );
+ }
+ }
+
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+
+ for ( var j = pos || 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( handler.guid === handleObj.guid ) {
+ // remove the given handler for the given type
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ if ( pos == null ) {
+ eventType.splice( j--, 1 );
+ }
+
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+
+ if ( pos != null ) {
+ break;
+ }
+ }
+ }
+
+ // remove generic event handler if no more handlers exist
+ if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ removeEvent( elem, type, elemData.handle );
+ }
+
+ ret = null;
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ var handle = elemData.handle;
+ if ( handle ) {
+ handle.elem = null;
+ }
+
+ delete elemData.events;
+ delete elemData.handle;
+
+ if ( jQuery.isEmptyObject( elemData ) ) {
+ jQuery.removeData( elem );
+ }
+ }
+ },
+
+ // bubbling is internal
+ trigger: function( event, data, elem /*, bubbling */ ) {
+ // Event object or event type
+ var type = event.type || event,
+ bubbling = arguments[3];
+
+ if ( !bubbling ) {
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[expando] ? event :
+ // Object literal
+ jQuery.extend( jQuery.Event(type), event ) :
+ // Just the event type (string)
+ jQuery.Event(type);
+
+ if ( type.indexOf("!") >= 0 ) {
+ event.type = type = type.slice(0, -1);
+ event.exclusive = true;
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // Don't bubble custom events when global (to avoid too much overhead)
+ event.stopPropagation();
+
+ // Only trigger if we've ever bound an event for it
+ if ( jQuery.event.global[ type ] ) {
+ jQuery.each( jQuery.cache, function() {
+ if ( this.events && this.events[type] ) {
+ jQuery.event.trigger( event, data, this.handle.elem );
+ }
+ });
+ }
+ }
+
+ // Handle triggering a single element
+
+ // don't do events on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return undefined;
+ }
+
+ // Clean up in case it is reused
+ event.result = undefined;
+ event.target = elem;
+
+ // Clone the incoming data, if any
+ data = jQuery.makeArray( data );
+ data.unshift( event );
+ }
+
+ event.currentTarget = elem;
+
+ // Trigger the event, it is assumed that "handle" is a function
+ var handle = jQuery.data( elem, "handle" );
+ if ( handle ) {
+ handle.apply( elem, data );
+ }
+
+ var parent = elem.parentNode || elem.ownerDocument;
+
+ // Trigger an inline bound script
+ try {
+ if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
+ if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
+ event.result = false;
+ }
+ }
+
+ // prevent IE from throwing an error for some elements with some event types, see #3533
+ } catch (e) {}
+
+ if ( !event.isPropagationStopped() && parent ) {
+ jQuery.event.trigger( event, data, parent, true );
+
+ } else if ( !event.isDefaultPrevented() ) {
+ var target = event.target, old,
+ isClick = jQuery.nodeName(target, "a") && type === "click",
+ special = jQuery.event.special[ type ] || {};
+
+ if ( (!special._default || special._default.call( elem, event ) === false) &&
+ !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
+
+ try {
+ if ( target[ type ] ) {
+ // Make sure that we don't accidentally re-trigger the onFOO events
+ old = target[ "on" + type ];
+
+ if ( old ) {
+ target[ "on" + type ] = null;
+ }
+
+ jQuery.event.triggered = true;
+ target[ type ]();
+ }
+
+ // prevent IE from throwing an error for some elements with some event types, see #3533
+ } catch (e) {}
+
+ if ( old ) {
+ target[ "on" + type ] = old;
+ }
+
+ jQuery.event.triggered = false;
+ }
+ }
+ },
+
+ handle: function( event ) {
+ var all, handlers, namespaces, namespace, events;
+
+ event = arguments[0] = jQuery.event.fix( event || window.event );
+ event.currentTarget = this;
+
+ // Namespaced event handlers
+ all = event.type.indexOf(".") < 0 && !event.exclusive;
+
+ if ( !all ) {
+ namespaces = event.type.split(".");
+ event.type = namespaces.shift();
+ namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ var events = jQuery.data(this, "events"), handlers = events[ event.type ];
+
+ if ( events && handlers ) {
+ // Clone the handlers to prevent manipulation
+ handlers = handlers.slice(0);
+
+ for ( var j = 0, l = handlers.length; j < l; j++ ) {
+ var handleObj = handlers[ j ];
+
+ // Filter the functions by class
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ event.handler = handleObj.handler;
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ var ret = handleObj.handler.apply( this, arguments );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ if ( event.isImmediatePropagationStopped() ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+ fix: function( event ) {
+ if ( event[ expando ] ) {
+ return event;
+ }
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = event;
+ event = jQuery.Event( originalEvent );
+
+ for ( var i = this.props.length, prop; i; ) {
+ prop = this.props[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !event.target ) {
+ event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+ }
+
+ // check if target is a textnode (safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && event.fromElement ) {
+ event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
+ }
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && event.clientX != null ) {
+ var doc = document.documentElement, body = document.body;
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
+ event.which = event.charCode || event.keyCode;
+ }
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !event.metaKey && event.ctrlKey ) {
+ event.metaKey = event.ctrlKey;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && event.button !== undefined ) {
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+ }
+
+ return event;
+ },
+
+ // Deprecated, use jQuery.guid instead
+ guid: 1E8,
+
+ // Deprecated, use jQuery.proxy instead
+ proxy: jQuery.proxy,
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: jQuery.bindReady,
+ teardown: jQuery.noop
+ },
+
+ live: {
+ add: function( handleObj ) {
+ jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) );
+ },
+
+ remove: function( handleObj ) {
+ var remove = true,
+ type = handleObj.origType.replace(rnamespaces, "");
+
+ jQuery.each( jQuery.data(this, "events").live || [], function() {
+ if ( type === this.origType.replace(rnamespaces, "") ) {
+ remove = false;
+ return false;
+ }
+ });
+
+ if ( remove ) {
+ jQuery.event.remove( this, handleObj.origType, liveHandler );
+ }
+ }
+
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( this.setInterval ) {
+ this.onbeforeunload = eventHandle;
+ }
+
+ return false;
+ },
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ }
+};
+
+var removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ elem.removeEventListener( type, handle, false );
+ } :
+ function( elem, type, handle ) {
+ elem.detachEvent( "on" + type, handle );
+ };
+
+jQuery.Event = function( src ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !this.preventDefault ) {
+ return new jQuery.Event( src );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = now();
+
+ // Mark it as fixed
+ this[ expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+ }
+ // otherwise set the returnValue property of the original event to false (IE)
+ e.returnValue = false;
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function( event ) {
+ // Check if mouse(over|out) are still within the same parent element
+ var parent = event.relatedTarget;
+
+ // Firefox sometimes assigns relatedTarget a XUL element
+ // which we cannot access the parentNode property of
+ try {
+ // Traverse up the tree
+ while ( parent && parent !== this ) {
+ parent = parent.parentNode;
+ }
+
+ if ( parent !== this ) {
+ // set the correct event type
+ event.type = event.data;
+
+ // handle event if we actually just moused on to a non sub-element
+ jQuery.event.handle.apply( this, arguments );
+ }
+
+ // assuming we've left the element since we most likely mousedover a xul element
+ } catch(e) { }
+},
+
+// In case of event delegation, we only need to rename the event.type,
+// liveHandler will take care of the rest.
+delegate = function( event ) {
+ event.type = event.data;
+ jQuery.event.handle.apply( this, arguments );
+};
+
+// Create mouseenter and mouseleave events
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ setup: function( data ) {
+ jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
+ },
+ teardown: function( data ) {
+ jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
+ }
+ };
+});
+
+// submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function( data, namespaces ) {
+ if ( this.nodeName.toLowerCase() !== "form" ) {
+ jQuery.event.add(this, "click.specialSubmit", function( e ) {
+ var elem = e.target, type = elem.type;
+
+ if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+ return trigger( "submit", this, arguments );
+ }
+ });
+
+ jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+ var elem = e.target, type = elem.type;
+
+ if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+ return trigger( "submit", this, arguments );
+ }
+ });
+
+ } else {
+ return false;
+ }
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialSubmit" );
+ }
+ };
+
+}
+
+// change delegation, happens here so we have bind.
+if ( !jQuery.support.changeBubbles ) {
+
+ var formElems = /textarea|input|select/i,
+
+ changeFilters,
+
+ getVal = function( elem ) {
+ var type = elem.type, val = elem.value;
+
+ if ( type === "radio" || type === "checkbox" ) {
+ val = elem.checked;
+
+ } else if ( type === "select-multiple" ) {
+ val = elem.selectedIndex > -1 ?
+ jQuery.map( elem.options, function( elem ) {
+ return elem.selected;
+ }).join("-") :
+ "";
+
+ } else if ( elem.nodeName.toLowerCase() === "select" ) {
+ val = elem.selectedIndex;
+ }
+
+ return val;
+ },
+
+ testChange = function testChange( e ) {
+ var elem = e.target, data, val;
+
+ if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
+ return;
+ }
+
+ data = jQuery.data( elem, "_change_data" );
+ val = getVal(elem);
+
+ // the current data will be also retrieved by beforeactivate
+ if ( e.type !== "focusout" || elem.type !== "radio" ) {
+ jQuery.data( elem, "_change_data", val );
+ }
+
+ if ( data === undefined || val === data ) {
+ return;
+ }
+
+ if ( data != null || val ) {
+ e.type = "change";
+ return jQuery.event.trigger( e, arguments[1], elem );
+ }
+ };
+
+ jQuery.event.special.change = {
+ filters: {
+ focusout: testChange,
+
+ click: function( e ) {
+ var elem = e.target, type = elem.type;
+
+ if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
+ return testChange.call( this, e );
+ }
+ },
+
+ // Change has to be called before submit
+ // Keydown will be called before keypress, which is used in submit-event delegation
+ keydown: function( e ) {
+ var elem = e.target, type = elem.type;
+
+ if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
+ (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+ type === "select-multiple" ) {
+ return testChange.call( this, e );
+ }
+ },
+
+ // Beforeactivate happens also before the previous element is blurred
+ // with this event you can't trigger a change event, but you can store
+ // information/focus[in] is not needed anymore
+ beforeactivate: function( e ) {
+ var elem = e.target;
+ jQuery.data( elem, "_change_data", getVal(elem) );
+ }
+ },
+
+ setup: function( data, namespaces ) {
+ if ( this.type === "file" ) {
+ return false;
+ }
+
+ for ( var type in changeFilters ) {
+ jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
+ }
+
+ return formElems.test( this.nodeName );
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialChange" );
+
+ return formElems.test( this.nodeName );
+ }
+ };
+
+ changeFilters = jQuery.event.special.change.filters;
+}
+
+function trigger( type, elem, args ) {
+ args[0].type = type;
+ return jQuery.event.handle.apply( elem, args );
+}
+
+// Create "bubbling" focus and blur events
+if ( document.addEventListener ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ this.addEventListener( orig, handler, true );
+ },
+ teardown: function() {
+ this.removeEventListener( orig, handler, true );
+ }
+ };
+
+ function handler( e ) {
+ e = jQuery.event.fix( e );
+ e.type = fix;
+ return jQuery.event.handle.call( this, e );
+ }
+ });
+}
+
+jQuery.each(["bind", "one"], function( i, name ) {
+ jQuery.fn[ name ] = function( type, data, fn ) {
+ // Handle object literals
+ if ( typeof type === "object" ) {
+ for ( var key in type ) {
+ this[ name ](key, data, type[key], fn);
+ }
+ return this;
+ }
+
+ if ( jQuery.isFunction( data ) ) {
+ fn = data;
+ data = undefined;
+ }
+
+ var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
+ jQuery( this ).unbind( event, handler );
+ return fn.apply( this, arguments );
+ }) : fn;
+
+ if ( type === "unload" && name !== "one" ) {
+ this.one( type, data, fn );
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.add( this[i], type, handler, data );
+ }
+ }
+
+ return this;
+ };
+});
+
+jQuery.fn.extend({
+ unbind: function( type, fn ) {
+ // Handle object literals
+ if ( typeof type === "object" && !type.preventDefault ) {
+ for ( var key in type ) {
+ this.unbind(key, type[key]);
+ }
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.remove( this[i], type, fn );
+ }
+ }
+
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.live( types, data, fn, selector );
+ },
+
+ undelegate: function( selector, types, fn ) {
+ if ( arguments.length === 0 ) {
+ return this.unbind( "live" );
+
+ } else {
+ return this.die( types, null, fn, selector );
+ }
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ var event = jQuery.Event( type );
+ event.preventDefault();
+ event.stopPropagation();
+ jQuery.event.trigger( event, data, this[0] );
+ return event.result;
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments, i = 1;
+
+ // link all the functions, so any of them can unbind this click handler
+ while ( i < args.length ) {
+ jQuery.proxy( fn, args[ i++ ] );
+ }
+
+ return this.click( jQuery.proxy( fn, function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ }));
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+var liveMap = {
+ focus: "focusin",
+ blur: "focusout",
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+};
+
+jQuery.each(["live", "die"], function( i, name ) {
+ jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
+ var type, i = 0, match, namespaces, preType,
+ selector = origSelector || this.selector,
+ context = origSelector ? this : jQuery( this.context );
+
+ if ( jQuery.isFunction( data ) ) {
+ fn = data;
+ data = undefined;
+ }
+
+ types = (types || "").split(" ");
+
+ while ( (type = types[ i++ ]) != null ) {
+ match = rnamespaces.exec( type );
+ namespaces = "";
+
+ if ( match ) {
+ namespaces = match[0];
+ type = type.replace( rnamespaces, "" );
+ }
+
+ if ( type === "hover" ) {
+ types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+ continue;
+ }
+
+ preType = type;
+
+ if ( type === "focus" || type === "blur" ) {
+ types.push( liveMap[ type ] + namespaces );
+ type = type + namespaces;
+
+ } else {
+ type = (liveMap[ type ] || type) + namespaces;
+ }
+
+ if ( name === "live" ) {
+ // bind live handler
+ context.each(function(){
+ jQuery.event.add( this, liveConvert( type, selector ),
+ { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+ });
+
+ } else {
+ // unbind live handler
+ context.unbind( liveConvert( type, selector ), fn );
+ }
+ }
+
+ return this;
+ }
+});
+
+function liveHandler( event ) {
+ var stop, elems = [], selectors = [], args = arguments,
+ related, match, handleObj, elem, j, i, l, data,
+ events = jQuery.data( this, "events" );
+
+ // Make sure we avoid non-left-click bubbling in Firefox (#3861)
+ if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
+ return;
+ }
+
+ event.liveFired = this;
+
+ var live = events.live.slice(0);
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+ selectors.push( handleObj.selector );
+
+ } else {
+ live.splice( j--, 1 );
+ }
+ }
+
+ match = jQuery( event.target ).closest( selectors, event.currentTarget );
+
+ for ( i = 0, l = match.length; i < l; i++ ) {
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( match[i].selector === handleObj.selector ) {
+ elem = match[i].elem;
+ related = null;
+
+ // Those two events require additional checking
+ if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+ related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
+ }
+
+ if ( !related || related !== elem ) {
+ elems.push({ elem: elem, handleObj: handleObj });
+ }
+ }
+ }
+ }
+
+ for ( i = 0, l = elems.length; i < l; i++ ) {
+ match = elems[i];
+ event.currentTarget = match.elem;
+ event.data = match.handleObj.data;
+ event.handleObj = match.handleObj;
+
+ if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
+ stop = false;
+ break;
+ }
+ }
+
+ return stop;
+}
+
+function liveConvert( type, selector ) {
+ return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
+}
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( fn ) {
+ return fn ? this.bind( name, fn ) : this.trigger( name );
+ };
+
+ if ( jQuery.attrFn ) {
+ jQuery.attrFn[ name ] = true;
+ }
+});
+
+// Prevent memory leaks in IE
+// Window isn't included so as not to unbind existing unload events
+// More info:
+// - http://isaacschlueter.com/2006/10/msie-memory-leaks/
+if ( window.attachEvent && !window.addEventListener ) {
+ window.attachEvent("onunload", function() {
+ for ( var id in jQuery.cache ) {
+ if ( jQuery.cache[ id ].handle ) {
+ // Try/Catch is to handle iframes being unloaded, see #4280
+ try {
+ jQuery.event.remove( jQuery.cache[ id ].handle.elem );
+ } catch(e) {}
+ }
+ }
+ });
+}
+/*!
+ * Sizzle CSS Selector Engine - v1.0
+ * Copyright 2009, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ done = 0,
+ toString = Object.prototype.toString,
+ hasDuplicate = false,
+ baseHasDuplicate = true;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function(){
+ baseHasDuplicate = false;
+ return 0;
+});
+
+var Sizzle = function(selector, context, results, seed) {
+ results = results || [];
+ var origContext = context = context || document;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
+ soFar = selector;
+
+ // Reset the position of the chunker regexp (start from head)
+ while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
+ soFar = m[3];
+
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
+ }
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context );
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] ) {
+ selector += parts.shift();
+ }
+
+ set = posProcess( selector, set );
+ }
+ }
+ } else {
+ // Take a shortcut and set the context if the root selector is an ID
+ // (but not if it'll be faster if the inner selector is an ID)
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+ var ret = Sizzle.find( parts.shift(), context, contextXML );
+ context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
+ }
+
+ if ( context ) {
+ var ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+ set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray(set);
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ var cur = parts.pop(), pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
+ }
+ } else {
+ checkSet = parts = [];
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ Sizzle.error( cur || selector );
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+ } else if ( context && context.nodeType === 1 ) {
+ for ( var i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+ } else {
+ for ( var i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, origContext, results, seed );
+ Sizzle.uniqueSort( results );
+ }
+
+ return results;
+};
+
+Sizzle.uniqueSort = function(results){
+ if ( sortOrder ) {
+ hasDuplicate = baseHasDuplicate;
+ results.sort(sortOrder);
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[i-1] ) {
+ results.splice(i--, 1);
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function(expr, set){
+ return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+ var set, match;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+ var type = Expr.order[i], match;
+
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ var left = match[1];
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace(/\\/g, "");
+ set = Expr.find[ type ]( match, context, isXML );
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = context.getElementsByTagName("*");
+ }
+
+ return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+ var old = expr, result = [], curLoop = set, match, anyFound,
+ isXMLFilter = set && set[0] && isXML(set[0]);
+
+ while ( expr && set.length ) {
+ for ( var type in Expr.filter ) {
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ var filter = Expr.filter[ type ], found, item, left = match[1];
+ anyFound = false;
+
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ if ( curLoop === result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ var pass = not ^ !!found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+ } else {
+ curLoop[i] = false;
+ }
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr === old ) {
+ if ( anyFound == null ) {
+ Sizzle.error( expr );
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+ throw "Syntax error, unrecognized expression: " + msg;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+ },
+ leftMatch: {},
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+ attrHandle: {
+ href: function(elem){
+ return elem.getAttribute("href");
+ }
+ },
+ relative: {
+ "+": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !/\W/.test(part),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag ) {
+ part = part.toLowerCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+ ">": function(checkSet, part){
+ var isPartStr = typeof part === "string";
+
+ if ( isPartStr && !/\W/.test(part) ) {
+ part = part.toLowerCase();
+
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+ }
+ }
+ } else {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+ "": function(checkSet, part, isXML){
+ var doneName = done++, checkFn = dirCheck;
+
+ if ( typeof part === "string" && !/\W/.test(part) ) {
+ var nodeCheck = part = part.toLowerCase();
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+ },
+ "~": function(checkSet, part, isXML){
+ var doneName = done++, checkFn = dirCheck;
+
+ if ( typeof part === "string" && !/\W/.test(part) ) {
+ var nodeCheck = part = part.toLowerCase();
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+ }
+ },
+ find: {
+ ID: function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ return m ? [m] : [];
+ }
+ },
+ NAME: function(match, context){
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [], results = context.getElementsByName(match[1]);
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+ TAG: function(match, context){
+ return context.getElementsByTagName(match[1]);
+ }
+ },
+ preFilter: {
+ CLASS: function(match, curLoop, inplace, result, not, isXML){
+ match = " " + match[1].replace(/\\/g, "") + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
+ result.push( elem );
+ }
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+ ID: function(match){
+ return match[1].replace(/\\/g, "");
+ },
+ TAG: function(match, curLoop){
+ return match[1].toLowerCase();
+ },
+ CHILD: function(match){
+ if ( match[1] === "nth" ) {
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+ ATTR: function(match, curLoop, inplace, result, not, isXML){
+ var name = match[1].replace(/\\/g, "");
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+ PSEUDO: function(match, curLoop, inplace, result, not){
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+ return false;
+ }
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+ POS: function(match){
+ match.unshift( true );
+ return match;
+ }
+ },
+ filters: {
+ enabled: function(elem){
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+ disabled: function(elem){
+ return elem.disabled === true;
+ },
+ checked: function(elem){
+ return elem.checked === true;
+ },
+ selected: function(elem){
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ elem.parentNode.selectedIndex;
+ return elem.selected === true;
+ },
+ parent: function(elem){
+ return !!elem.firstChild;
+ },
+ empty: function(elem){
+ return !elem.firstChild;
+ },
+ has: function(elem, i, match){
+ return !!Sizzle( match[3], elem ).length;
+ },
+ header: function(elem){
+ return /h\d/i.test( elem.nodeName );
+ },
+ text: function(elem){
+ return "text" === elem.type;
+ },
+ radio: function(elem){
+ return "radio" === elem.type;
+ },
+ checkbox: function(elem){
+ return "checkbox" === elem.type;
+ },
+ file: function(elem){
+ return "file" === elem.type;
+ },
+ password: function(elem){
+ return "password" === elem.type;
+ },
+ submit: function(elem){
+ return "submit" === elem.type;
+ },
+ image: function(elem){
+ return "image" === elem.type;
+ },
+ reset: function(elem){
+ return "reset" === elem.type;
+ },
+ button: function(elem){
+ return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
+ },
+ input: function(elem){
+ return /input|select|textarea|button/i.test(elem.nodeName);
+ }
+ },
+ setFilters: {
+ first: function(elem, i){
+ return i === 0;
+ },
+ last: function(elem, i, match, array){
+ return i === array.length - 1;
+ },
+ even: function(elem, i){
+ return i % 2 === 0;
+ },
+ odd: function(elem, i){
+ return i % 2 === 1;
+ },
+ lt: function(elem, i, match){
+ return i < match[3] - 0;
+ },
+ gt: function(elem, i, match){
+ return i > match[3] - 0;
+ },
+ nth: function(elem, i, match){
+ return match[3] - 0 === i;
+ },
+ eq: function(elem, i, match){
+ return match[3] - 0 === i;
+ }
+ },
+ filter: {
+ PSEUDO: function(elem, match, i, array){
+ var name = match[1], filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var i = 0, l = not.length; i < l; i++ ) {
+ if ( not[i] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+ } else {
+ Sizzle.error( "Syntax error, unrecognized expression: " + name );
+ }
+ },
+ CHILD: function(elem, match){
+ var type = match[1], node = elem;
+ switch (type) {
+ case 'only':
+ case 'first':
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+ if ( type === "first" ) {
+ return true;
+ }
+ node = elem;
+ case 'last':
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+ return true;
+ case 'nth':
+ var first = match[2], last = match[3];
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ var doneName = match[0],
+ parent = elem.parentNode;
+
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+ var count = 0;
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+ parent.sizcache = doneName;
+ }
+
+ var diff = elem.nodeIndex - last;
+ if ( first === 0 ) {
+ return diff === 0;
+ } else {
+ return ( diff % first === 0 && diff / first >= 0 );
+ }
+ }
+ },
+ ID: function(elem, match){
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+ TAG: function(elem, match){
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+ },
+ CLASS: function(elem, match){
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+ ATTR: function(elem, match){
+ var name = match[1],
+ result = Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value !== check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+ POS: function(elem, match, i, array){
+ var name = match[2], filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){
+ return "\\" + (num - 0 + 1);
+ }));
+}
+
+var makeArray = function(array, results) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch(e){
+ makeArray = function(array, results) {
+ var ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var i = 0, l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+ } else {
+ for ( var i = 0; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ if ( a == b ) {
+ hasDuplicate = true;
+ }
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
+ var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( "sourceIndex" in document.documentElement ) {
+ sortOrder = function( a, b ) {
+ if ( !a.sourceIndex || !b.sourceIndex ) {
+ if ( a == b ) {
+ hasDuplicate = true;
+ }
+ return a.sourceIndex ? -1 : 1;
+ }
+
+ var ret = a.sourceIndex - b.sourceIndex;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( document.createRange ) {
+ sortOrder = function( a, b ) {
+ if ( !a.ownerDocument || !b.ownerDocument ) {
+ if ( a == b ) {
+ hasDuplicate = true;
+ }
+ return a.ownerDocument ? -1 : 1;
+ }
+
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.setStart(a, 0);
+ aRange.setEnd(a, 0);
+ bRange.setStart(b, 0);
+ bRange.setEnd(b, 0);
+ var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+}
+
+// Utility function for retreiving the text value of an array of DOM nodes
+function getText( elems ) {
+ var ret = "", elem;
+
+ for ( var i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ ret += elem.nodeValue;
+
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ ret += getText( elem.childNodes );
+ }
+ }
+
+ return ret;
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("div"),
+ id = "script" + (new Date).getTime();
+ form.innerHTML = "";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ var root = document.documentElement;
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( document.getElementById( id ) ) {
+ Expr.find.ID = function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+ }
+ };
+
+ Expr.filter.ID = function(elem, match){
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+ root = form = null; // release memory in IE
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function(match, context){
+ var results = context.getElementsByTagName(match[1]);
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "";
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+ Expr.attrHandle.href = function(elem){
+ return elem.getAttribute("href", 2);
+ };
+ }
+
+ div = null; // release memory in IE
+})();
+
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle, div = document.createElement("div");
+ div.innerHTML = "";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function(query, context, extra, seed){
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(e){}
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ div = null; // release memory in IE
+ })();
+}
+
+(function(){
+ var div = document.createElement("div");
+
+ div.innerHTML = "";
+
+ // Opera can't find a second classname (in 9.6)
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+ return;
+ }
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 ) {
+ return;
+ }
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function(match, context, isXML) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+
+ div = null; // release memory in IE
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ elem = elem[dir];
+ var match = false;
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName.toLowerCase() === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ elem = elem[dir];
+ var match = false;
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+var contains = document.compareDocumentPosition ? function(a, b){
+ return !!(a.compareDocumentPosition(b) & 16);
+} : function(a, b){
+ return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function(selector, context){
+ var tmpSet = [], later = "", match,
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = getText;
+jQuery.isXMLDoc = isXML;
+jQuery.contains = contains;
+
+return;
+
+window.Sizzle = Sizzle;
+
+})();
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+ // Note: This RegExp should be improved, or likely pulled from Sizzle
+ rmultiselector = /,/,
+ slice = Array.prototype.slice;
+
+// Implement the identical functionality for filter and not
+var winnow = function( elements, qualifier, keep ) {
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return !!qualifier.call( elem, i, elem ) === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return (elem === qualifier) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
+ });
+};
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var ret = this.pushStack( "", "find", selector ), length = 0;
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( var n = length; n < ret.length; n++ ) {
+ for ( var r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var targets = jQuery( target );
+ return this.filter(function() {
+ for ( var i = 0, l = targets.length; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && jQuery.filter( selector, this ).length > 0;
+ },
+
+ closest: function( selectors, context ) {
+ if ( jQuery.isArray( selectors ) ) {
+ var ret = [], cur = this[0], match, matches = {}, selector;
+
+ if ( cur && selectors.length ) {
+ for ( var i = 0, l = selectors.length; i < l; i++ ) {
+ selector = selectors[i];
+
+ if ( !matches[selector] ) {
+ matches[selector] = jQuery.expr.match.POS.test( selector ) ?
+ jQuery( selector, context || this.context ) :
+ selector;
+ }
+ }
+
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( selector in matches ) {
+ match = matches[selector];
+
+ if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
+ ret.push({ selector: selector, elem: cur });
+ delete matches[selector];
+ }
+ }
+ cur = cur.parentNode;
+ }
+ }
+
+ return ret;
+ }
+
+ var pos = jQuery.expr.match.POS.test( selectors ) ?
+ jQuery( selectors, context || this.context ) : null;
+
+ return this.map(function( i, cur ) {
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
+ return cur;
+ }
+ cur = cur.parentNode;
+ }
+ return null;
+ });
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ if ( !elem || typeof elem === "string" ) {
+ return jQuery.inArray( this[0],
+ // If it receives a string, the selector is used
+ // If it receives nothing, the siblings are used
+ elem ? jQuery( elem ) : this.parent().children() );
+ }
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context || this.context ) :
+ jQuery.makeArray( selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return jQuery.nth( elem, 2, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return jQuery.nth( elem, 2, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( elem.parentNode.firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.makeArray( elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 ? jQuery.unique( ret ) : ret;
+
+ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, slice.call(arguments).join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [], cur = elem[dir];
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function( cur, result, dir, elem ) {
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType === 1 && ++num === result ) {
+ break;
+ }
+ }
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,
+ rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
+ rtagName = /<([\w:]+)/,
+ rtbody = /" + tag + ">";
+ },
+ wrapMap = {
+ option: [ 1, "" ],
+ legend: [ 1, "" ],
+ thead: [ 1, "", "
" ],
+ tr: [ 2, "", "
" ],
+ td: [ 3, "", "
" ],
+ col: [ 2, "", "
" ],
+ area: [ 1, "" ],
+ _default: [ 0, "", "" ]
+ };
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize and
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+SyntaxHighlighter Highlight Lines Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/theme_tests.html b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/theme_tests.html
new file mode 100644
index 0000000..da07ecf
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/theme_tests.html
@@ -0,0 +1,134 @@
+
+
+
+
+ SyntaxHighlighter Theme Tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/webrick.rb b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/webrick.rb
new file mode 100644
index 0000000..0b3c93c
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/webrick.rb
@@ -0,0 +1,11 @@
+require 'webrick'
+include WEBrick
+
+s = HTTPServer.new(
+ :Port => 2010,
+ :DocumentRoot => Dir::pwd
+)
+s.mount('/sh/scripts', WEBrick::HTTPServlet::FileHandler, '../scripts')
+s.mount('/sh/styles', WEBrick::HTTPServlet::FileHandler, '../styles')
+trap('INT') { s.stop }
+s.start
diff --git a/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/webrick.sh b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/webrick.sh
new file mode 100644
index 0000000..7e249f8
--- /dev/null
+++ b/tests/siesta/resources/syntaxhighlighter_3.0.83/tests/webrick.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+ruby webrick.rb
diff --git a/tests/siesta/siesta-all.js b/tests/siesta/siesta-all.js
new file mode 100644
index 0000000..5ed7439
--- /dev/null
+++ b/tests/siesta/siesta-all.js
@@ -0,0 +1,34766 @@
+/*
+
+Siesta 2.0.4
+Copyright(c) 2009-2013 Bryntum AB
+http://bryntum.com/contact
+http://bryntum.com/products/siesta/license
+
+*/
+;!function () {;
+var Joose = {}
+
+// configuration hash
+
+Joose.C = typeof JOOSE_CFG != 'undefined' ? JOOSE_CFG : {}
+
+Joose.is_IE = '\v' == 'v'
+Joose.is_NodeJS = Boolean(typeof process != 'undefined' && process.pid)
+
+
+Joose.top = Joose.is_NodeJS && global || this
+
+Joose.stub = function () {
+ return function () { throw new Error("Modules can not be instantiated") }
+}
+
+
+Joose.VERSION = ({ /*PKGVERSION*/VERSION : '3.50.1' }).VERSION
+
+
+if (typeof module != 'undefined') module.exports = Joose
+/*if (!Joose.is_NodeJS) */
+this.Joose = Joose
+
+
+// Static helpers for Arrays
+Joose.A = {
+
+ each : function (array, func, scope) {
+ scope = scope || this
+
+ for (var i = 0, len = array.length; i < len; i++)
+ if (func.call(scope, array[i], i) === false) return false
+ },
+
+
+ eachR : function (array, func, scope) {
+ scope = scope || this
+
+ for (var i = array.length - 1; i >= 0; i--)
+ if (func.call(scope, array[i], i) === false) return false
+ },
+
+
+ exists : function (array, value) {
+ for (var i = 0, len = array.length; i < len; i++) if (array[i] == value) return true
+
+ return false
+ },
+
+
+ map : function (array, func, scope) {
+ scope = scope || this
+
+ var res = []
+
+ for (var i = 0, len = array.length; i < len; i++)
+ res.push( func.call(scope, array[i], i) )
+
+ return res
+ },
+
+
+ grep : function (array, func) {
+ var a = []
+
+ Joose.A.each(array, function (t) {
+ if (func(t)) a.push(t)
+ })
+
+ return a
+ },
+
+
+ remove : function (array, removeEle) {
+ var a = []
+
+ Joose.A.each(array, function (t) {
+ if (t !== removeEle) a.push(t)
+ })
+
+ return a
+ }
+
+}
+
+// Static helpers for Strings
+Joose.S = {
+
+ saneSplit : function (str, delimeter) {
+ var res = (str || '').split(delimeter)
+
+ if (res.length == 1 && !res[0]) res.shift()
+
+ return res
+ },
+
+
+ uppercaseFirst : function (string) {
+ return string.substr(0, 1).toUpperCase() + string.substr(1, string.length - 1)
+ },
+
+
+ strToClass : function (name, top) {
+ var current = top || Joose.top
+
+ Joose.A.each(name.split('.'), function (segment) {
+ if (current)
+ current = current[ segment ]
+ else
+ return false
+ })
+
+ return current
+ }
+}
+
+var baseFunc = function () {}
+
+var enumProps = [ 'hasOwnProperty', 'valueOf', 'toString', 'constructor' ]
+
+var manualEnum = true
+
+for (var i in { toString : 1 }) manualEnum = false
+
+
+// Static helpers for objects
+Joose.O = {
+
+ each : function (object, func, scope) {
+ scope = scope || this
+
+ for (var i in object)
+ if (func.call(scope, object[i], i) === false) return false
+
+ if (manualEnum)
+ return Joose.A.each(enumProps, function (el) {
+
+ if (object.hasOwnProperty(el)) return func.call(scope, object[el], el)
+ })
+ },
+
+
+ eachOwn : function (object, func, scope) {
+ scope = scope || this
+
+ return Joose.O.each(object, function (value, name) {
+ if (object.hasOwnProperty(name)) return func.call(scope, value, name)
+ }, scope)
+ },
+
+
+ copy : function (source, target) {
+ target = target || {}
+
+ Joose.O.each(source, function (value, name) { target[name] = value })
+
+ return target
+ },
+
+
+ copyOwn : function (source, target) {
+ target = target || {}
+
+ Joose.O.eachOwn(source, function (value, name) { target[name] = value })
+
+ return target
+ },
+
+
+ getMutableCopy : function (object) {
+ baseFunc.prototype = object
+
+ return new baseFunc()
+ },
+
+
+ extend : function (target, source) {
+ return Joose.O.copy(source, target)
+ },
+
+
+ isEmpty : function (object) {
+ for (var i in object) if (object.hasOwnProperty(i)) return false
+
+ return true
+ },
+
+
+ isInstance: function (obj) {
+ return obj && obj.meta && obj.constructor == obj.meta.c
+ },
+
+
+ isClass : function (obj) {
+ return obj && obj.meta && obj.meta.c == obj
+ },
+
+
+ wantArray : function (obj) {
+ if (obj instanceof Array) return obj
+
+ return [ obj ]
+ },
+
+
+ // this was a bug in WebKit, which gives typeof / / == 'function'
+ // should be monitored and removed at some point in the future
+ isFunction : function (obj) {
+ return typeof obj == 'function' && obj.constructor != / /.constructor
+ }
+}
+
+
+//initializers
+
+Joose.I = {
+ Array : function () { return [] },
+ Object : function () { return {} },
+ Function : function () { return arguments.callee },
+ Now : function () { return new Date() }
+};
+Joose.Proto = Joose.stub()
+
+Joose.Proto.Empty = Joose.stub()
+
+Joose.Proto.Empty.meta = {};
+;(function () {
+
+ Joose.Proto.Object = Joose.stub()
+
+
+ var SUPER = function () {
+ var self = SUPER.caller
+
+ if (self == SUPERARG) self = self.caller
+
+ if (!self.SUPER) throw "Invalid call to SUPER"
+
+ return self.SUPER[self.methodName].apply(this, arguments)
+ }
+
+
+ var SUPERARG = function () {
+ return this.SUPER.apply(this, arguments[0])
+ }
+
+
+
+ Joose.Proto.Object.prototype = {
+
+ SUPERARG : SUPERARG,
+ SUPER : SUPER,
+
+ INNER : function () {
+ throw "Invalid call to INNER"
+ },
+
+
+ BUILD : function (config) {
+ return arguments.length == 1 && typeof config == 'object' && config || {}
+ },
+
+
+ initialize: function () {
+ },
+
+
+ toString: function () {
+ return "a " + this.meta.name
+ }
+
+ }
+
+ Joose.Proto.Object.meta = {
+ constructor : Joose.Proto.Object,
+
+ methods : Joose.O.copy(Joose.Proto.Object.prototype),
+ attributes : {}
+ }
+
+ Joose.Proto.Object.prototype.meta = Joose.Proto.Object.meta
+
+})();
+;(function () {
+
+ Joose.Proto.Class = function () {
+ return this.initialize(this.BUILD.apply(this, arguments)) || this
+ }
+
+ var bootstrap = {
+
+ VERSION : null,
+ AUTHORITY : null,
+
+ constructor : Joose.Proto.Class,
+ superClass : null,
+
+ name : null,
+
+ attributes : null,
+ methods : null,
+
+ meta : null,
+ c : null,
+
+ defaultSuperClass : Joose.Proto.Object,
+
+
+ BUILD : function (name, extend) {
+ this.name = name
+
+ return { __extend__ : extend || {} }
+ },
+
+
+ initialize: function (props) {
+ var extend = props.__extend__
+
+ this.VERSION = extend.VERSION
+ this.AUTHORITY = extend.AUTHORITY
+
+ delete extend.VERSION
+ delete extend.AUTHORITY
+
+ this.c = this.extractConstructor(extend)
+
+ this.adaptConstructor(this.c)
+
+ if (extend.constructorOnly) {
+ delete extend.constructorOnly
+ return
+ }
+
+ this.construct(extend)
+ },
+
+
+ construct : function (extend) {
+ if (!this.prepareProps(extend)) return
+
+ var superClass = this.superClass = this.extractSuperClass(extend)
+
+ this.processSuperClass(superClass)
+
+ this.adaptPrototype(this.c.prototype)
+
+ this.finalize(extend)
+ },
+
+
+ finalize : function (extend) {
+ this.processStem(extend)
+
+ this.extend(extend)
+ },
+
+
+ //if the extension returns false from this method it should re-enter 'construct'
+ prepareProps : function (extend) {
+ return true
+ },
+
+
+ extractConstructor : function (extend) {
+ var res = extend.hasOwnProperty('constructor') ? extend.constructor : this.defaultConstructor()
+
+ delete extend.constructor
+
+ return res
+ },
+
+
+ extractSuperClass : function (extend) {
+ if (extend.hasOwnProperty('isa') && !extend.isa) throw new Error("Attempt to inherit from undefined superclass [" + this.name + "]")
+
+ var res = extend.isa || this.defaultSuperClass
+
+ delete extend.isa
+
+ return res
+ },
+
+
+ processStem : function () {
+ var superMeta = this.superClass.meta
+
+ this.methods = Joose.O.getMutableCopy(superMeta.methods || {})
+ this.attributes = Joose.O.getMutableCopy(superMeta.attributes || {})
+ },
+
+
+ initInstance : function (instance, props) {
+ Joose.O.copyOwn(props, instance)
+ },
+
+
+ defaultConstructor: function () {
+ return function (arg) {
+ var BUILD = this.BUILD
+
+ var args = BUILD && BUILD.apply(this, arguments) || arg || {}
+
+ var thisMeta = this.meta
+
+ thisMeta.initInstance(this, args)
+
+ return thisMeta.hasMethod('initialize') && this.initialize(args) || this
+ }
+ },
+
+
+ processSuperClass: function (superClass) {
+ var superProto = superClass.prototype
+
+ //non-Joose superclasses
+ if (!superClass.meta) {
+
+ var extend = Joose.O.copy(superProto)
+
+ extend.isa = Joose.Proto.Empty
+ // clear potential value in the `extend.constructor` to prevent it from being modified
+ delete extend.constructor
+
+ var meta = new this.defaultSuperClass.meta.constructor(null, extend)
+
+ superClass.meta = superProto.meta = meta
+
+ meta.c = superClass
+ }
+
+ this.c.prototype = Joose.O.getMutableCopy(superProto)
+ this.c.superClass = superProto
+ },
+
+
+ adaptConstructor: function (c) {
+ c.meta = this
+
+ if (!c.hasOwnProperty('toString')) c.toString = function () { return this.meta.name }
+ },
+
+
+ adaptPrototype: function (proto) {
+ //this will fix weird semantic of native "constructor" property to more intuitive (idea borrowed from Ext)
+ proto.constructor = this.c
+ proto.meta = this
+ },
+
+
+ addMethod: function (name, func) {
+ func.SUPER = this.superClass.prototype
+
+ //chrome don't allow to redefine the "name" property
+ func.methodName = name
+
+ this.methods[name] = func
+ this.c.prototype[name] = func
+ },
+
+
+ addAttribute: function (name, init) {
+ this.attributes[name] = init
+ this.c.prototype[name] = init
+ },
+
+
+ removeMethod : function (name) {
+ delete this.methods[name]
+ delete this.c.prototype[name]
+ },
+
+
+ removeAttribute: function (name) {
+ delete this.attributes[name]
+ delete this.c.prototype[name]
+ },
+
+
+ hasMethod: function (name) {
+ return Boolean(this.methods[name])
+ },
+
+
+ hasAttribute: function (name) {
+ return this.attributes[name] !== undefined
+ },
+
+
+ hasOwnMethod: function (name) {
+ return this.hasMethod(name) && this.methods.hasOwnProperty(name)
+ },
+
+
+ hasOwnAttribute: function (name) {
+ return this.hasAttribute(name) && this.attributes.hasOwnProperty(name)
+ },
+
+
+ extend : function (props) {
+ Joose.O.eachOwn(props, function (value, name) {
+ if (name != 'meta' && name != 'constructor')
+ if (Joose.O.isFunction(value) && !value.meta)
+ this.addMethod(name, value)
+ else
+ this.addAttribute(name, value)
+ }, this)
+ },
+
+
+ subClassOf : function (classObject, extend) {
+ return this.subClass(extend, null, classObject)
+ },
+
+
+ subClass : function (extend, name, classObject) {
+ extend = extend || {}
+ extend.isa = classObject || this.c
+
+ return new this.constructor(name, extend).c
+ },
+
+
+ instantiate : function () {
+ var f = function () {}
+
+ f.prototype = this.c.prototype
+
+ var obj = new f()
+
+ return this.c.apply(obj, arguments) || obj
+ }
+ }
+
+ //micro bootstraping
+
+ Joose.Proto.Class.prototype = Joose.O.getMutableCopy(Joose.Proto.Object.prototype)
+
+ Joose.O.extend(Joose.Proto.Class.prototype, bootstrap)
+
+ Joose.Proto.Class.prototype.meta = new Joose.Proto.Class('Joose.Proto.Class', bootstrap)
+
+
+
+ Joose.Proto.Class.meta.addMethod('isa', function (someClass) {
+ var f = function () {}
+
+ f.prototype = this.c.prototype
+
+ return new f() instanceof someClass
+ })
+})();
+Joose.Managed = Joose.stub()
+
+Joose.Managed.Property = new Joose.Proto.Class('Joose.Managed.Property', {
+
+ name : null,
+
+ init : null,
+ value : null,
+
+ definedIn : null,
+
+
+ initialize : function (props) {
+ Joose.Managed.Property.superClass.initialize.call(this, props)
+
+ this.computeValue()
+ },
+
+
+ computeValue : function () {
+ this.value = this.init
+ },
+
+
+ //targetClass is still open at this stage
+ preApply : function (targetClass) {
+ },
+
+
+ //targetClass is already open at this stage
+ postUnApply : function (targetClass) {
+ },
+
+
+ apply : function (target) {
+ target[this.name] = this.value
+ },
+
+
+ isAppliedTo : function (target) {
+ return target[this.name] == this.value
+ },
+
+
+ unapply : function (from) {
+ if (!this.isAppliedTo(from)) throw "Unapply of property [" + this.name + "] from [" + from + "] failed"
+
+ delete from[this.name]
+ },
+
+
+ cloneProps : function () {
+ return {
+ name : this.name,
+ init : this.init,
+ definedIn : this.definedIn
+ }
+ },
+
+
+ clone : function (name) {
+ var props = this.cloneProps()
+
+ props.name = name || props.name
+
+ return new this.constructor(props)
+ }
+
+
+}).c;
+Joose.Managed.Property.ConflictMarker = new Joose.Proto.Class('Joose.Managed.Property.ConflictMarker', {
+
+ isa : Joose.Managed.Property,
+
+ apply : function (target) {
+ throw new Error("Attempt to apply ConflictMarker [" + this.name + "] to [" + target + "]")
+ }
+
+}).c;
+Joose.Managed.Property.Requirement = new Joose.Proto.Class('Joose.Managed.Property.Requirement', {
+
+ isa : Joose.Managed.Property,
+
+
+ apply : function (target) {
+ if (!target.meta.hasMethod(this.name))
+ throw new Error("Requirement [" + this.name + "], defined in [" + this.definedIn.definedIn.name + "] is not satisfied for class [" + target + "]")
+ },
+
+
+ unapply : function (from) {
+ }
+
+}).c;
+Joose.Managed.Property.Attribute = new Joose.Proto.Class('Joose.Managed.Property.Attribute', {
+
+ isa : Joose.Managed.Property,
+
+ slot : null,
+
+
+ initialize : function () {
+ Joose.Managed.Property.Attribute.superClass.initialize.apply(this, arguments)
+
+ this.slot = this.name
+ },
+
+
+ apply : function (target) {
+ target.prototype[ this.slot ] = this.value
+ },
+
+
+ isAppliedTo : function (target) {
+ return target.prototype[ this.slot ] == this.value
+ },
+
+
+ unapply : function (from) {
+ if (!this.isAppliedTo(from)) throw "Unapply of property [" + this.name + "] from [" + from + "] failed"
+
+ delete from.prototype[this.slot]
+ },
+
+
+ clearValue : function (instance) {
+ delete instance[ this.slot ]
+ },
+
+
+ hasValue : function (instance) {
+ return instance.hasOwnProperty(this.slot)
+ },
+
+
+ getRawValueFrom : function (instance) {
+ return instance[ this.slot ]
+ },
+
+
+ setRawValueTo : function (instance, value) {
+ instance[ this.slot ] = value
+
+ return this
+ }
+
+}).c;
+Joose.Managed.Property.MethodModifier = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier', {
+
+ isa : Joose.Managed.Property,
+
+
+ prepareWrapper : function () {
+ throw "Abstract method [prepareWrapper] of " + this + " was called"
+ },
+
+
+ apply : function (target) {
+ var name = this.name
+ var targetProto = target.prototype
+ var isOwn = targetProto.hasOwnProperty(name)
+ var original = targetProto[name]
+ var superProto = target.meta.superClass.prototype
+
+
+ var originalCall = isOwn ? original : function () {
+ return superProto[name].apply(this, arguments)
+ }
+
+ var methodWrapper = this.prepareWrapper({
+ name : name,
+ modifier : this.value,
+
+ isOwn : isOwn,
+ originalCall : originalCall,
+
+ superProto : superProto,
+
+ target : target
+ })
+
+ if (isOwn) methodWrapper.__ORIGINAL__ = original
+
+ methodWrapper.__CONTAIN__ = this.value
+ methodWrapper.__METHOD__ = this
+ this.value.displayName = this.getDisplayName(target)
+ methodWrapper.displayName = 'internal wrapper'
+
+ targetProto[name] = methodWrapper
+ },
+
+
+ getDisplayName : function (target) {
+ return target.meta.name + '[' + this.name + ']'
+ },
+
+
+ isAppliedTo : function (target) {
+ var targetCont = target.prototype[this.name]
+
+ return targetCont && targetCont.__CONTAIN__ == this.value
+ },
+
+
+ unapply : function (from) {
+ var name = this.name
+ var fromProto = from.prototype
+ var original = fromProto[name].__ORIGINAL__
+
+ if (!this.isAppliedTo(from)) throw "Unapply of method [" + name + "] from class [" + from + "] failed"
+
+ //if modifier was applied to own method - restore it
+ if (original)
+ fromProto[name] = original
+ //otherwise - just delete it, to reveal the inherited method
+ else
+ delete fromProto[name]
+ }
+
+}).c;
+Joose.Managed.Property.MethodModifier.Override = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.Override', {
+
+ isa : Joose.Managed.Property.MethodModifier,
+
+
+ prepareWrapper : function (params) {
+
+ var modifier = params.modifier
+ var originalCall = params.originalCall
+ var superProto = params.superProto
+ var superMetaConst = superProto.meta.constructor
+
+ //call to Joose.Proto level, require some additional processing
+ var isCallToProto = (superMetaConst == Joose.Proto.Class || superMetaConst == Joose.Proto.Object) && !(params.isOwn && originalCall.IS_OVERRIDE)
+
+ var original = originalCall
+
+ if (isCallToProto) original = function () {
+ var beforeSUPER = this.SUPER
+
+ this.SUPER = superProto.SUPER
+
+ var res = originalCall.apply(this, arguments)
+
+ this.SUPER = beforeSUPER
+
+ return res
+ }
+
+ var override = function () {
+
+ var beforeSUPER = this.SUPER
+
+ this.SUPER = original
+
+ var res = modifier.apply(this, arguments)
+
+ this.SUPER = beforeSUPER
+
+ return res
+ }
+
+ override.IS_OVERRIDE = true
+
+ return override
+ },
+
+ getDisplayName : function (target) {
+ return target.meta.name + '[override ' + this.name + ']'
+ }
+
+
+}).c;
+Joose.Managed.Property.MethodModifier.Put = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.Put', {
+
+ isa : Joose.Managed.Property.MethodModifier.Override,
+
+
+ prepareWrapper : function (params) {
+
+ if (params.isOwn) throw "Method [" + params.name + "] is applying over something [" + params.originalCall + "] in class [" + params.target + "]"
+
+ return Joose.Managed.Property.MethodModifier.Put.superClass.prepareWrapper.call(this, params)
+ },
+
+ getDisplayName : function (target) {
+ return target.meta.name + '[' + this.name + ']'
+ }
+
+
+}).c;
+Joose.Managed.Property.MethodModifier.After = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.After', {
+
+ isa : Joose.Managed.Property.MethodModifier,
+
+
+ prepareWrapper : function (params) {
+
+ var modifier = params.modifier
+ var originalCall = params.originalCall
+
+ return function () {
+ var res = originalCall.apply(this, arguments)
+ modifier.apply(this, arguments)
+ return res
+ }
+ },
+
+ getDisplayName : function (target) {
+ return target.meta.name + '[after ' + this.name + ']'
+ }
+
+}).c;
+Joose.Managed.Property.MethodModifier.Before = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.Before', {
+
+ isa : Joose.Managed.Property.MethodModifier,
+
+
+ prepareWrapper : function (params) {
+
+ var modifier = params.modifier
+ var originalCall = params.originalCall
+
+ return function () {
+ modifier.apply(this, arguments)
+ return originalCall.apply(this, arguments)
+ }
+ },
+
+ getDisplayName : function (target) {
+ return target.meta.name + '[before ' + this.name + ']'
+ }
+
+}).c;
+Joose.Managed.Property.MethodModifier.Around = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.Around', {
+
+ isa : Joose.Managed.Property.MethodModifier,
+
+ prepareWrapper : function (params) {
+
+ var modifier = params.modifier
+ var originalCall = params.originalCall
+
+ var me
+
+ var bound = function () {
+ return originalCall.apply(me, arguments)
+ }
+
+ return function () {
+ me = this
+
+ var boundArr = [ bound ]
+ boundArr.push.apply(boundArr, arguments)
+
+ return modifier.apply(this, boundArr)
+ }
+ },
+
+ getDisplayName : function (target) {
+ return target.meta.name + '[around ' + this.name + ']'
+ }
+
+}).c;
+Joose.Managed.Property.MethodModifier.Augment = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.Augment', {
+
+ isa : Joose.Managed.Property.MethodModifier,
+
+
+ prepareWrapper : function (params) {
+
+ var AUGMENT = function () {
+
+ //populate callstack to the most deep non-augment method
+ var callstack = []
+
+ var self = AUGMENT
+
+ do {
+ callstack.push(self.IS_AUGMENT ? self.__CONTAIN__ : self)
+
+ self = self.IS_AUGMENT && (self.__ORIGINAL__ || self.SUPER[self.methodName])
+ } while (self)
+
+
+ //save previous INNER
+ var beforeINNER = this.INNER
+
+ //create new INNER
+ this.INNER = function () {
+ var innerCall = callstack.pop()
+
+ return innerCall ? innerCall.apply(this, arguments) : undefined
+ }
+
+ //augment modifier results in hypotetical INNER call of the same method in subclass
+ var res = this.INNER.apply(this, arguments)
+
+ //restore previous INNER chain
+ this.INNER = beforeINNER
+
+ return res
+ }
+
+ AUGMENT.methodName = params.name
+ AUGMENT.SUPER = params.superProto
+ AUGMENT.IS_AUGMENT = true
+
+ return AUGMENT
+ },
+
+ getDisplayName : function (target) {
+ return target.meta.name + '[augment ' + this.name + ']'
+ }
+
+}).c;
+Joose.Managed.PropertySet = new Joose.Proto.Class('Joose.Managed.PropertySet', {
+
+ isa : Joose.Managed.Property,
+
+ properties : null,
+
+ propertyMetaClass : Joose.Managed.Property,
+
+
+ initialize : function (props) {
+ Joose.Managed.PropertySet.superClass.initialize.call(this, props)
+
+ //XXX this guards the meta roles :)
+ this.properties = props.properties || {}
+ },
+
+
+ addProperty : function (name, props) {
+ var metaClass = props.meta || this.propertyMetaClass
+ delete props.meta
+
+ props.definedIn = this
+ props.name = name
+
+ return this.properties[name] = new metaClass(props)
+ },
+
+
+ addPropertyObject : function (object) {
+ return this.properties[object.name] = object
+ },
+
+
+ removeProperty : function (name) {
+ var prop = this.properties[name]
+
+ delete this.properties[name]
+
+ return prop
+ },
+
+
+ haveProperty : function (name) {
+ return this.properties[name] != null
+ },
+
+
+ haveOwnProperty : function (name) {
+ return this.haveProperty(name) && this.properties.hasOwnProperty(name)
+ },
+
+
+ getProperty : function (name) {
+ return this.properties[name]
+ },
+
+
+ //includes inherited properties (probably you wants 'eachOwn', which process only "own" (including consumed from Roles) properties)
+ each : function (func, scope) {
+ Joose.O.each(this.properties, func, scope || this)
+ },
+
+
+ eachOwn : function (func, scope) {
+ Joose.O.eachOwn(this.properties, func, scope || this)
+ },
+
+
+ //synonym for each
+ eachAll : function (func, scope) {
+ this.each(func, scope)
+ },
+
+
+ cloneProps : function () {
+ var props = Joose.Managed.PropertySet.superClass.cloneProps.call(this)
+
+ props.propertyMetaClass = this.propertyMetaClass
+
+ return props
+ },
+
+
+ clone : function (name) {
+ var clone = this.cleanClone(name)
+
+ clone.properties = Joose.O.copyOwn(this.properties)
+
+ return clone
+ },
+
+
+ cleanClone : function (name) {
+ var props = this.cloneProps()
+
+ props.name = name || props.name
+
+ return new this.constructor(props)
+ },
+
+
+ alias : function (what) {
+ var props = this.properties
+
+ Joose.O.each(what, function (aliasName, originalName) {
+ var original = props[originalName]
+
+ if (original) this.addPropertyObject(original.clone(aliasName))
+ }, this)
+ },
+
+
+ exclude : function (what) {
+ var props = this.properties
+
+ Joose.A.each(what, function (name) {
+ delete props[name]
+ })
+ },
+
+
+ beforeConsumedBy : function () {
+ },
+
+
+ flattenTo : function (target) {
+ var targetProps = target.properties
+
+ this.eachOwn(function (property, name) {
+ var targetProperty = targetProps[name]
+
+ if (targetProperty instanceof Joose.Managed.Property.ConflictMarker) return
+
+ if (!targetProps.hasOwnProperty(name) || targetProperty == null) {
+ target.addPropertyObject(property)
+ return
+ }
+
+ if (targetProperty == property) return
+
+ target.removeProperty(name)
+ target.addProperty(name, {
+ meta : Joose.Managed.Property.ConflictMarker
+ })
+ }, this)
+ },
+
+
+ composeTo : function (target) {
+ this.eachOwn(function (property, name) {
+ if (!target.haveOwnProperty(name)) target.addPropertyObject(property)
+ })
+ },
+
+
+ composeFrom : function () {
+ if (!arguments.length) return
+
+ var flattening = this.cleanClone()
+
+ Joose.A.each(arguments, function (arg) {
+ var isDescriptor = !(arg instanceof Joose.Managed.PropertySet)
+ var propSet = isDescriptor ? arg.propertySet : arg
+
+ propSet.beforeConsumedBy(this, flattening)
+
+ if (isDescriptor) {
+ if (arg.alias || arg.exclude) propSet = propSet.clone()
+ if (arg.alias) propSet.alias(arg.alias)
+ if (arg.exclude) propSet.exclude(arg.exclude)
+ }
+
+ propSet.flattenTo(flattening)
+ }, this)
+
+ flattening.composeTo(this)
+ },
+
+
+ preApply : function (target) {
+ this.eachOwn(function (property) {
+ property.preApply(target)
+ })
+ },
+
+
+ apply : function (target) {
+ this.eachOwn(function (property) {
+ property.apply(target)
+ })
+ },
+
+
+ unapply : function (from) {
+ this.eachOwn(function (property) {
+ property.unapply(from)
+ })
+ },
+
+
+ postUnApply : function (target) {
+ this.eachOwn(function (property) {
+ property.postUnApply(target)
+ })
+ }
+
+}).c
+;
+var __ID__ = 1
+
+
+Joose.Managed.PropertySet.Mutable = new Joose.Proto.Class('Joose.Managed.PropertySet.Mutable', {
+
+ isa : Joose.Managed.PropertySet,
+
+ ID : null,
+
+ derivatives : null,
+
+ opened : null,
+
+ composedFrom : null,
+
+
+ initialize : function (props) {
+ Joose.Managed.PropertySet.Mutable.superClass.initialize.call(this, props)
+
+ //initially opened
+ this.opened = 1
+ this.derivatives = {}
+ this.ID = __ID__++
+ this.composedFrom = []
+ },
+
+
+ addComposeInfo : function () {
+ this.ensureOpen()
+
+ Joose.A.each(arguments, function (arg) {
+ this.composedFrom.push(arg)
+
+ var propSet = arg instanceof Joose.Managed.PropertySet ? arg : arg.propertySet
+
+ propSet.derivatives[this.ID] = this
+ }, this)
+ },
+
+
+ removeComposeInfo : function () {
+ this.ensureOpen()
+
+ Joose.A.each(arguments, function (arg) {
+
+ var i = 0
+
+ while (i < this.composedFrom.length) {
+ var propSet = this.composedFrom[i]
+ propSet = propSet instanceof Joose.Managed.PropertySet ? propSet : propSet.propertySet
+
+ if (arg == propSet) {
+ delete propSet.derivatives[this.ID]
+ this.composedFrom.splice(i, 1)
+ } else i++
+ }
+
+ }, this)
+ },
+
+
+ ensureOpen : function () {
+ if (!this.opened) throw "Mutation of closed property set: [" + this.name + "]"
+ },
+
+
+ addProperty : function (name, props) {
+ this.ensureOpen()
+
+ return Joose.Managed.PropertySet.Mutable.superClass.addProperty.call(this, name, props)
+ },
+
+
+ addPropertyObject : function (object) {
+ this.ensureOpen()
+
+ return Joose.Managed.PropertySet.Mutable.superClass.addPropertyObject.call(this, object)
+ },
+
+
+ removeProperty : function (name) {
+ this.ensureOpen()
+
+ return Joose.Managed.PropertySet.Mutable.superClass.removeProperty.call(this, name)
+ },
+
+
+ composeFrom : function () {
+ this.ensureOpen()
+
+ return Joose.Managed.PropertySet.Mutable.superClass.composeFrom.apply(this, this.composedFrom)
+ },
+
+
+ open : function () {
+ this.opened++
+
+ if (this.opened == 1) {
+
+ Joose.O.each(this.derivatives, function (propSet) {
+ propSet.open()
+ })
+
+ this.deCompose()
+ }
+ },
+
+
+ close : function () {
+ if (!this.opened) throw "Unmatched 'close' operation on property set: [" + this.name + "]"
+
+ if (this.opened == 1) {
+ this.reCompose()
+
+ Joose.O.each(this.derivatives, function (propSet) {
+ propSet.close()
+ })
+ }
+ this.opened--
+ },
+
+
+ reCompose : function () {
+ this.composeFrom()
+ },
+
+
+ deCompose : function () {
+ this.eachOwn(function (property, name) {
+ if (property.definedIn != this) this.removeProperty(name)
+ }, this)
+ }
+
+}).c;
+Joose.Managed.StemElement = function () { throw "Modules may not be instantiated." }
+
+Joose.Managed.StemElement.Attributes = new Joose.Proto.Class('Joose.Managed.StemElement.Attributes', {
+
+ isa : Joose.Managed.PropertySet.Mutable,
+
+ propertyMetaClass : Joose.Managed.Property.Attribute
+
+}).c
+;
+Joose.Managed.StemElement.Methods = new Joose.Proto.Class('Joose.Managed.StemElement.Methods', {
+
+ isa : Joose.Managed.PropertySet.Mutable,
+
+ propertyMetaClass : Joose.Managed.Property.MethodModifier.Put,
+
+
+ preApply : function () {
+ },
+
+
+ postUnApply : function () {
+ }
+
+}).c;
+Joose.Managed.StemElement.Requirements = new Joose.Proto.Class('Joose.Managed.StemElement.Requirements', {
+
+ isa : Joose.Managed.PropertySet.Mutable,
+
+ propertyMetaClass : Joose.Managed.Property.Requirement,
+
+
+
+ alias : function () {
+ },
+
+
+ exclude : function () {
+ },
+
+
+ flattenTo : function (target) {
+ this.each(function (property, name) {
+ if (!target.haveProperty(name)) target.addPropertyObject(property)
+ })
+ },
+
+
+ composeTo : function (target) {
+ this.flattenTo(target)
+ },
+
+
+ preApply : function () {
+ },
+
+
+ postUnApply : function () {
+ }
+
+}).c;
+Joose.Managed.StemElement.MethodModifiers = new Joose.Proto.Class('Joose.Managed.StemElement.MethodModifiers', {
+
+ isa : Joose.Managed.PropertySet.Mutable,
+
+ propertyMetaClass : null,
+
+
+ addProperty : function (name, props) {
+ var metaClass = props.meta
+ delete props.meta
+
+ props.definedIn = this
+ props.name = name
+
+ var modifier = new metaClass(props)
+ var properties = this.properties
+
+ if (!properties[name]) properties[ name ] = []
+
+ properties[name].push(modifier)
+
+ return modifier
+ },
+
+
+ addPropertyObject : function (object) {
+ var name = object.name
+ var properties = this.properties
+
+ if (!properties[name]) properties[name] = []
+
+ properties[name].push(object)
+
+ return object
+ },
+
+
+ //remove only the last modifier
+ removeProperty : function (name) {
+ if (!this.haveProperty(name)) return undefined
+
+ var properties = this.properties
+ var modifier = properties[ name ].pop()
+
+ //if all modifiers were removed - clearing the properties
+ if (!properties[name].length) Joose.Managed.StemElement.MethodModifiers.superClass.removeProperty.call(this, name)
+
+ return modifier
+ },
+
+
+ alias : function () {
+ },
+
+
+ exclude : function () {
+ },
+
+
+ flattenTo : function (target) {
+ var targetProps = target.properties
+
+ this.each(function (modifiersArr, name) {
+ var targetModifiersArr = targetProps[name]
+
+ if (targetModifiersArr == null) targetModifiersArr = targetProps[name] = []
+
+ Joose.A.each(modifiersArr, function (modifier) {
+ if (!Joose.A.exists(targetModifiersArr, modifier)) targetModifiersArr.push(modifier)
+ })
+
+ })
+ },
+
+
+ composeTo : function (target) {
+ this.flattenTo(target)
+ },
+
+
+ deCompose : function () {
+ this.each(function (modifiersArr, name) {
+ var i = 0
+
+ while (i < modifiersArr.length)
+ if (modifiersArr[i].definedIn != this)
+ modifiersArr.splice(i, 1)
+ else
+ i++
+ })
+ },
+
+
+ preApply : function (target) {
+ },
+
+
+ postUnApply : function (target) {
+ },
+
+
+ apply : function (target) {
+ this.each(function (modifiersArr, name) {
+ Joose.A.each(modifiersArr, function (modifier) {
+ modifier.apply(target)
+ })
+ })
+ },
+
+
+ unapply : function (from) {
+ this.each(function (modifiersArr, name) {
+ for (var i = modifiersArr.length - 1; i >=0 ; i--) modifiersArr[i].unapply(from)
+ })
+ }
+
+
+
+}).c;
+Joose.Managed.PropertySet.Composition = new Joose.Proto.Class('Joose.Managed.PropertySet.Composition', {
+
+ isa : Joose.Managed.PropertySet.Mutable,
+
+ propertyMetaClass : Joose.Managed.PropertySet.Mutable,
+
+ processOrder : null,
+
+
+ each : function (func, scope) {
+ var props = this.properties
+ var scope = scope || this
+
+ Joose.A.each(this.processOrder, function (name) {
+ func.call(scope, props[name], name)
+ })
+ },
+
+
+ eachR : function (func, scope) {
+ var props = this.properties
+ var scope = scope || this
+
+ Joose.A.eachR(this.processOrder, function (name) {
+ func.call(scope, props[name], name)
+ })
+
+
+// var props = this.properties
+// var processOrder = this.processOrder
+//
+// for(var i = processOrder.length - 1; i >= 0; i--)
+// func.call(scope || this, props[ processOrder[i] ], processOrder[i])
+ },
+
+
+ clone : function (name) {
+ var clone = this.cleanClone(name)
+
+ this.each(function (property) {
+ clone.addPropertyObject(property.clone())
+ })
+
+ return clone
+ },
+
+
+ alias : function (what) {
+ this.each(function (property) {
+ property.alias(what)
+ })
+ },
+
+
+ exclude : function (what) {
+ this.each(function (property) {
+ property.exclude(what)
+ })
+ },
+
+
+ flattenTo : function (target) {
+ var targetProps = target.properties
+
+ this.each(function (property, name) {
+ var subTarget = targetProps[name] || target.addProperty(name, {
+ meta : property.constructor
+ })
+
+ property.flattenTo(subTarget)
+ })
+ },
+
+
+ composeTo : function (target) {
+ var targetProps = target.properties
+
+ this.each(function (property, name) {
+ var subTarget = targetProps[name] || target.addProperty(name, {
+ meta : property.constructor
+ })
+
+ property.composeTo(subTarget)
+ })
+ },
+
+
+
+ deCompose : function () {
+ this.eachR(function (property) {
+ property.open()
+ })
+
+ Joose.Managed.PropertySet.Composition.superClass.deCompose.call(this)
+ },
+
+
+ reCompose : function () {
+ Joose.Managed.PropertySet.Composition.superClass.reCompose.call(this)
+
+ this.each(function (property) {
+ property.close()
+ })
+ },
+
+
+ unapply : function (from) {
+ this.eachR(function (property) {
+ property.unapply(from)
+ })
+ }
+
+}).c
+;
+Joose.Managed.Stem = new Joose.Proto.Class('Joose.Managed.Stem', {
+
+ isa : Joose.Managed.PropertySet.Composition,
+
+ targetMeta : null,
+
+ attributesMC : Joose.Managed.StemElement.Attributes,
+ methodsMC : Joose.Managed.StemElement.Methods,
+ requirementsMC : Joose.Managed.StemElement.Requirements,
+ methodsModifiersMC : Joose.Managed.StemElement.MethodModifiers,
+
+ processOrder : [ 'attributes', 'methods', 'requirements', 'methodsModifiers' ],
+
+
+ initialize : function (props) {
+ Joose.Managed.Stem.superClass.initialize.call(this, props)
+
+ var targetMeta = this.targetMeta
+
+ this.addProperty('attributes', {
+ meta : this.attributesMC,
+
+ //it can be no 'targetMeta' in clones
+ properties : targetMeta ? targetMeta.attributes : {}
+ })
+
+
+ this.addProperty('methods', {
+ meta : this.methodsMC,
+
+ properties : targetMeta ? targetMeta.methods : {}
+ })
+
+
+ this.addProperty('requirements', {
+ meta : this.requirementsMC
+ })
+
+
+ this.addProperty('methodsModifiers', {
+ meta : this.methodsModifiersMC
+ })
+ },
+
+
+ reCompose : function () {
+ var c = this.targetMeta.c
+
+ this.preApply(c)
+
+ Joose.Managed.Stem.superClass.reCompose.call(this)
+
+ this.apply(c)
+ },
+
+
+ deCompose : function () {
+ var c = this.targetMeta.c
+
+ this.unapply(c)
+
+ Joose.Managed.Stem.superClass.deCompose.call(this)
+
+ this.postUnApply(c)
+ }
+
+
+}).c
+;
+Joose.Managed.Builder = new Joose.Proto.Class('Joose.Managed.Builder', {
+
+ targetMeta : null,
+
+
+ _buildStart : function (targetMeta, props) {
+ targetMeta.stem.open()
+
+ Joose.A.each([ 'trait', 'traits', 'removeTrait', 'removeTraits', 'does', 'doesnot', 'doesnt' ], function (builder) {
+ if (props[builder]) {
+ this[builder](targetMeta, props[builder])
+ delete props[builder]
+ }
+ }, this)
+ },
+
+
+ _extend : function (props) {
+ if (Joose.O.isEmpty(props)) return
+
+ var targetMeta = this.targetMeta
+
+ this._buildStart(targetMeta, props)
+
+ Joose.O.eachOwn(props, function (value, name) {
+ var handler = this[name]
+
+ if (!handler) throw new Error("Unknown builder [" + name + "] was used during extending of [" + targetMeta.c + "]")
+
+ handler.call(this, targetMeta, value)
+ }, this)
+
+ this._buildComplete(targetMeta, props)
+ },
+
+
+ _buildComplete : function (targetMeta, props) {
+ targetMeta.stem.close()
+ },
+
+
+ methods : function (targetMeta, info) {
+ Joose.O.eachOwn(info, function (value, name) {
+ targetMeta.addMethod(name, value)
+ })
+ },
+
+
+ removeMethods : function (targetMeta, info) {
+ Joose.A.each(info, function (name) {
+ targetMeta.removeMethod(name)
+ })
+ },
+
+
+ have : function (targetMeta, info) {
+ Joose.O.eachOwn(info, function (value, name) {
+ targetMeta.addAttribute(name, value)
+ })
+ },
+
+
+ havenot : function (targetMeta, info) {
+ Joose.A.each(info, function (name) {
+ targetMeta.removeAttribute(name)
+ })
+ },
+
+
+ havent : function (targetMeta, info) {
+ this.havenot(targetMeta, info)
+ },
+
+
+ after : function (targetMeta, info) {
+ Joose.O.each(info, function (value, name) {
+ targetMeta.addMethodModifier(name, value, Joose.Managed.Property.MethodModifier.After)
+ })
+ },
+
+
+ before : function (targetMeta, info) {
+ Joose.O.each(info, function (value, name) {
+ targetMeta.addMethodModifier(name, value, Joose.Managed.Property.MethodModifier.Before)
+ })
+ },
+
+
+ override : function (targetMeta, info) {
+ Joose.O.each(info, function (value, name) {
+ targetMeta.addMethodModifier(name, value, Joose.Managed.Property.MethodModifier.Override)
+ })
+ },
+
+
+ around : function (targetMeta, info) {
+ Joose.O.each(info, function (value, name) {
+ targetMeta.addMethodModifier(name, value, Joose.Managed.Property.MethodModifier.Around)
+ })
+ },
+
+
+ augment : function (targetMeta, info) {
+ Joose.O.each(info, function (value, name) {
+ targetMeta.addMethodModifier(name, value, Joose.Managed.Property.MethodModifier.Augment)
+ })
+ },
+
+
+ removeModifier : function (targetMeta, info) {
+ Joose.A.each(info, function (name) {
+ targetMeta.removeMethodModifier(name)
+ })
+ },
+
+
+ does : function (targetMeta, info) {
+ Joose.A.each(Joose.O.wantArray(info), function (desc) {
+ targetMeta.addRole(desc)
+ })
+ },
+
+
+ doesnot : function (targetMeta, info) {
+ Joose.A.each(Joose.O.wantArray(info), function (desc) {
+ targetMeta.removeRole(desc)
+ })
+ },
+
+
+ doesnt : function (targetMeta, info) {
+ this.doesnot(targetMeta, info)
+ },
+
+
+ trait : function () {
+ this.traits.apply(this, arguments)
+ },
+
+
+ traits : function (targetMeta, info) {
+ if (targetMeta.firstPass) return
+
+ if (!targetMeta.meta.isDetached) throw "Can't apply trait to not detached class"
+
+ targetMeta.meta.extend({
+ does : info
+ })
+ },
+
+
+ removeTrait : function () {
+ this.removeTraits.apply(this, arguments)
+ },
+
+
+ removeTraits : function (targetMeta, info) {
+ if (!targetMeta.meta.isDetached) throw "Can't remove trait from not detached class"
+
+ targetMeta.meta.extend({
+ doesnot : info
+ })
+ },
+
+ name : function (targetMeta, name) {
+ targetMeta.name = name
+ }
+
+}).c;
+Joose.Managed.Class = new Joose.Proto.Class('Joose.Managed.Class', {
+
+ isa : Joose.Proto.Class,
+
+ stem : null,
+ stemClass : Joose.Managed.Stem,
+ stemClassCreated : false,
+
+ builder : null,
+ builderClass : Joose.Managed.Builder,
+ builderClassCreated : false,
+
+ isDetached : false,
+ firstPass : true,
+
+ // a special instance, which, when passed as 1st argument to constructor, signifies that constructor should
+ // skips traits processing for this instance
+ skipTraitsAnchor : {},
+
+
+ //build for metaclasses - collects traits from roles
+ BUILD : function () {
+ var sup = Joose.Managed.Class.superClass.BUILD.apply(this, arguments)
+
+ var props = sup.__extend__
+
+ var traits = Joose.O.wantArray(props.trait || props.traits || [])
+ delete props.trait
+ delete props.traits
+
+ Joose.A.each(Joose.O.wantArray(props.does || []), function (arg) {
+ var role = (arg.meta instanceof Joose.Managed.Class) ? arg : arg.role
+
+ if (role.meta.meta.isDetached) traits.push(role.meta.constructor)
+ })
+
+ if (traits.length) props.traits = traits
+
+ return sup
+ },
+
+
+ initInstance : function (instance, props) {
+ Joose.O.each(this.attributes, function (attribute, name) {
+
+ if (attribute instanceof Joose.Managed.Attribute)
+ attribute.initFromConfig(instance, props)
+ else
+ if (props.hasOwnProperty(name)) instance[name] = props[name]
+ })
+ },
+
+
+ // we are using the same constructor for usual and meta- classes
+ defaultConstructor: function () {
+ return function (skipTraitsAnchor, params) {
+
+ var thisMeta = this.meta
+ var skipTraits = skipTraitsAnchor == thisMeta.skipTraitsAnchor
+
+ var BUILD = this.BUILD
+
+ var props = BUILD && BUILD.apply(this, skipTraits ? params : arguments) || (skipTraits ? params[0] : skipTraitsAnchor) || {}
+
+
+ // either looking for traits in __extend__ (meta-class) or in usual props (usual class)
+ var extend = props.__extend__ || props
+
+ var traits = extend.trait || extend.traits
+
+ if (traits || extend.detached) {
+ delete extend.trait
+ delete extend.traits
+ delete extend.detached
+
+ if (!skipTraits) {
+ var classWithTrait = thisMeta.subClass({ does : traits || [] }, thisMeta.name)
+ var meta = classWithTrait.meta
+ meta.isDetached = true
+
+ return meta.instantiate(thisMeta.skipTraitsAnchor, arguments)
+ }
+ }
+
+ thisMeta.initInstance(this, props)
+
+ return thisMeta.hasMethod('initialize') && this.initialize(props) || this
+ }
+ },
+
+
+ finalize: function (extend) {
+ Joose.Managed.Class.superClass.finalize.call(this, extend)
+
+ this.stem.close()
+
+ this.afterMutate()
+ },
+
+
+ processStem : function () {
+ Joose.Managed.Class.superClass.processStem.call(this)
+
+ this.builder = new this.builderClass({ targetMeta : this })
+ this.stem = new this.stemClass({ name : this.name, targetMeta : this })
+
+ var builderClass = this.getClassInAttribute('builderClass')
+
+ if (builderClass) {
+ this.builderClassCreated = true
+ this.addAttribute('builderClass', this.subClassOf(builderClass))
+ }
+
+
+ var stemClass = this.getClassInAttribute('stemClass')
+
+ if (stemClass) {
+ this.stemClassCreated = true
+ this.addAttribute('stemClass', this.subClassOf(stemClass))
+ }
+ },
+
+
+ extend : function (props) {
+ if (props.builder) {
+ this.getBuilderTarget().meta.extend(props.builder)
+ delete props.builder
+ }
+
+ if (props.stem) {
+ this.getStemTarget().meta.extend(props.stem)
+ delete props.stem
+ }
+
+ this.builder._extend(props)
+
+ this.firstPass = false
+
+ if (!this.stem.opened) this.afterMutate()
+ },
+
+
+ getBuilderTarget : function () {
+ var builderClass = this.getClassInAttribute('builderClass')
+ if (!builderClass) throw "Attempt to extend a builder on non-meta class"
+
+ return builderClass
+ },
+
+
+ getStemTarget : function () {
+ var stemClass = this.getClassInAttribute('stemClass')
+ if (!stemClass) throw "Attempt to extend a stem on non-meta class"
+
+ return stemClass
+ },
+
+
+ getClassInAttribute : function (attributeName) {
+ var attrClass = this.getAttribute(attributeName)
+ if (attrClass instanceof Joose.Managed.Property.Attribute) attrClass = attrClass.value
+
+ return attrClass
+ },
+
+
+ addMethodModifier: function (name, func, type) {
+ var props = {}
+
+ props.init = func
+ props.meta = type
+
+ return this.stem.properties.methodsModifiers.addProperty(name, props)
+ },
+
+
+ removeMethodModifier: function (name) {
+ return this.stem.properties.methodsModifiers.removeProperty(name)
+ },
+
+
+ addMethod: function (name, func, props) {
+ props = props || {}
+ props.init = func
+
+ return this.stem.properties.methods.addProperty(name, props)
+ },
+
+
+ addAttribute: function (name, init, props) {
+ props = props || {}
+ props.init = init
+
+ return this.stem.properties.attributes.addProperty(name, props)
+ },
+
+
+ removeMethod : function (name) {
+ return this.stem.properties.methods.removeProperty(name)
+ },
+
+
+ removeAttribute: function (name) {
+ return this.stem.properties.attributes.removeProperty(name)
+ },
+
+
+ hasMethod: function (name) {
+ return this.stem.properties.methods.haveProperty(name)
+ },
+
+
+ hasAttribute: function (name) {
+ return this.stem.properties.attributes.haveProperty(name)
+ },
+
+
+ hasMethodModifiersFor : function (name) {
+ return this.stem.properties.methodsModifiers.haveProperty(name)
+ },
+
+
+ hasOwnMethod: function (name) {
+ return this.stem.properties.methods.haveOwnProperty(name)
+ },
+
+
+ hasOwnAttribute: function (name) {
+ return this.stem.properties.attributes.haveOwnProperty(name)
+ },
+
+
+ getMethod : function (name) {
+ return this.stem.properties.methods.getProperty(name)
+ },
+
+
+ getAttribute : function (name) {
+ return this.stem.properties.attributes.getProperty(name)
+ },
+
+
+ eachRole : function (roles, func, scope) {
+ Joose.A.each(roles, function (arg, index) {
+ var role = (arg.meta instanceof Joose.Managed.Class) ? arg : arg.role
+
+ func.call(scope || this, arg, role, index)
+ }, this)
+ },
+
+
+ addRole : function () {
+
+ this.eachRole(arguments, function (arg, role) {
+
+ this.beforeRoleAdd(role)
+
+ var desc = arg
+
+ //compose descriptor can contain 'alias' and 'exclude' fields, in this case actual reference should be stored
+ //into 'propertySet' field
+ if (role != arg) {
+ desc.propertySet = role.meta.stem
+ delete desc.role
+ } else
+ desc = desc.meta.stem
+
+ this.stem.addComposeInfo(desc)
+
+ }, this)
+ },
+
+
+ beforeRoleAdd : function (role) {
+ var roleMeta = role.meta
+
+ if (roleMeta.builderClassCreated) this.getBuilderTarget().meta.extend({
+ does : [ roleMeta.getBuilderTarget() ]
+ })
+
+ if (roleMeta.stemClassCreated) this.getStemTarget().meta.extend({
+ does : [ roleMeta.getStemTarget() ]
+ })
+
+ if (roleMeta.meta.isDetached && !this.firstPass) this.builder.traits(this, roleMeta.constructor)
+ },
+
+
+ beforeRoleRemove : function (role) {
+ var roleMeta = role.meta
+
+ if (roleMeta.builderClassCreated) this.getBuilderTarget().meta.extend({
+ doesnt : [ roleMeta.getBuilderTarget() ]
+ })
+
+ if (roleMeta.stemClassCreated) this.getStemTarget().meta.extend({
+ doesnt : [ roleMeta.getStemTarget() ]
+ })
+
+ if (roleMeta.meta.isDetached && !this.firstPass) this.builder.removeTraits(this, roleMeta.constructor)
+ },
+
+
+ removeRole : function () {
+ this.eachRole(arguments, function (arg, role) {
+ this.beforeRoleRemove(role)
+
+ this.stem.removeComposeInfo(role.meta.stem)
+ }, this)
+ },
+
+
+ getRoles : function () {
+
+ return Joose.A.map(this.stem.composedFrom, function (composeDesc) {
+ //compose descriptor can contain 'alias' and 'exclude' fields, in this case actual reference is stored
+ //into 'propertySet' field
+ if (!(composeDesc instanceof Joose.Managed.PropertySet)) return composeDesc.propertySet
+
+ return composeDesc.targetMeta.c
+ })
+ },
+
+
+ does : function (role) {
+ var myRoles = this.getRoles()
+
+ for (var i = 0; i < myRoles.length; i++) if (role == myRoles[i]) return true
+ for (var i = 0; i < myRoles.length; i++) if (myRoles[i].meta.does(role)) return true
+
+ var superMeta = this.superClass.meta
+
+ // considering the case of inheriting from non-Joose classes
+ if (this.superClass != Joose.Proto.Empty && superMeta && superMeta.meta && superMeta.meta.hasMethod('does')) return superMeta.does(role)
+
+ return false
+ },
+
+
+ getMethods : function () {
+ return this.stem.properties.methods
+ },
+
+
+ getAttributes : function () {
+ return this.stem.properties.attributes
+ },
+
+
+ afterMutate : function () {
+ },
+
+
+ getCurrentMethod : function () {
+ for (var wrapper = arguments.callee.caller, count = 0; wrapper && count < 5; wrapper = wrapper.caller, count++)
+ if (wrapper.__METHOD__) return wrapper.__METHOD__
+
+ return null
+ }
+
+
+}).c;
+Joose.Managed.Role = new Joose.Managed.Class('Joose.Managed.Role', {
+
+ isa : Joose.Managed.Class,
+
+ have : {
+ defaultSuperClass : Joose.Proto.Empty,
+
+ builderRole : null,
+ stemRole : null
+ },
+
+
+ methods : {
+
+ defaultConstructor : function () {
+ return function () {
+ throw new Error("Roles cant be instantiated")
+ }
+ },
+
+
+ processSuperClass : function () {
+ if (this.superClass != this.defaultSuperClass) throw new Error("Roles can't inherit from anything")
+ },
+
+
+ getBuilderTarget : function () {
+ if (!this.builderRole) {
+ this.builderRole = new this.constructor().c
+ this.builderClassCreated = true
+ }
+
+ return this.builderRole
+ },
+
+
+ getStemTarget : function () {
+ if (!this.stemRole) {
+ this.stemRole = new this.constructor().c
+ this.stemClassCreated = true
+ }
+
+ return this.stemRole
+ },
+
+
+ addRequirement : function (methodName) {
+ this.stem.properties.requirements.addProperty(methodName, {})
+ }
+
+ },
+
+
+ stem : {
+ methods : {
+
+ apply : function () {
+ },
+
+
+ unapply : function () {
+ }
+ }
+ },
+
+
+ builder : {
+ methods : {
+ requires : function (targetClassMeta, info) {
+ Joose.A.each(Joose.O.wantArray(info), function (methodName) {
+ targetClassMeta.addRequirement(methodName)
+ }, this)
+ }
+ }
+ }
+
+}).c;
+Joose.Managed.Attribute = new Joose.Managed.Class('Joose.Managed.Attribute', {
+
+ isa : Joose.Managed.Property.Attribute,
+
+ have : {
+ is : null,
+
+ builder : null,
+
+ isPrivate : false,
+
+ role : null,
+
+ publicName : null,
+ setterName : null,
+ getterName : null,
+
+ //indicates the logical readableness/writeableness of the attribute
+ readable : false,
+ writeable : false,
+
+ //indicates the physical presense of the accessor (may be absent for "combined" accessors for example)
+ hasGetter : false,
+ hasSetter : false,
+
+ required : false,
+
+ canInlineSetRaw : true,
+ canInlineGetRaw : true
+ },
+
+
+ after : {
+ initialize : function () {
+ var name = this.name
+
+ this.publicName = name.replace(/^_+/, '')
+
+ this.slot = this.isPrivate ? '$' + name : name
+
+ this.setterName = this.setterName || this.getSetterName()
+ this.getterName = this.getterName || this.getGetterName()
+
+ this.readable = this.hasGetter = /^r/i.test(this.is)
+ this.writeable = this.hasSetter = /^.w/i.test(this.is)
+ }
+ },
+
+
+ override : {
+
+ computeValue : function () {
+ var init = this.init
+
+ if (Joose.O.isClass(init) || !Joose.O.isFunction(init)) this.SUPER()
+ },
+
+
+ preApply : function (targetClass) {
+ targetClass.meta.extend({
+ methods : this.getAccessorsFor(targetClass)
+ })
+ },
+
+
+ postUnApply : function (from) {
+ from.meta.extend({
+ removeMethods : this.getAccessorsFrom(from)
+ })
+ }
+
+ },
+
+
+ methods : {
+
+ getAccessorsFor : function (targetClass) {
+ var targetMeta = targetClass.meta
+ var setterName = this.setterName
+ var getterName = this.getterName
+
+ var methods = {}
+
+ if (this.hasSetter && !targetMeta.hasMethod(setterName)) {
+ methods[setterName] = this.getSetter()
+ methods[setterName].ACCESSOR_FROM = this
+ }
+
+ if (this.hasGetter && !targetMeta.hasMethod(getterName)) {
+ methods[getterName] = this.getGetter()
+ methods[getterName].ACCESSOR_FROM = this
+ }
+
+ return methods
+ },
+
+
+ getAccessorsFrom : function (from) {
+ var targetMeta = from.meta
+ var setterName = this.setterName
+ var getterName = this.getterName
+
+ var setter = this.hasSetter && targetMeta.getMethod(setterName)
+ var getter = this.hasGetter && targetMeta.getMethod(getterName)
+
+ var removeMethods = []
+
+ if (setter && setter.value.ACCESSOR_FROM == this) removeMethods.push(setterName)
+ if (getter && getter.value.ACCESSOR_FROM == this) removeMethods.push(getterName)
+
+ return removeMethods
+ },
+
+
+ getGetterName : function () {
+ return 'get' + Joose.S.uppercaseFirst(this.publicName)
+ },
+
+
+ getSetterName : function () {
+ return 'set' + Joose.S.uppercaseFirst(this.publicName)
+ },
+
+
+ getSetter : function () {
+ var me = this
+ var slot = me.slot
+
+ if (me.canInlineSetRaw)
+ return function (value) {
+ this[ slot ] = value
+
+ return this
+ }
+ else
+ return function () {
+ return me.setRawValueTo.apply(this, arguments)
+ }
+ },
+
+
+ getGetter : function () {
+ var me = this
+ var slot = me.slot
+
+ if (me.canInlineGetRaw)
+ return function (value) {
+ return this[ slot ]
+ }
+ else
+ return function () {
+ return me.getRawValueFrom.apply(this, arguments)
+ }
+ },
+
+
+ getValueFrom : function (instance) {
+ var getterName = this.getterName
+
+ if (this.readable && instance.meta.hasMethod(getterName)) return instance[ getterName ]()
+
+ return this.getRawValueFrom(instance)
+ },
+
+
+ setValueTo : function (instance, value) {
+ var setterName = this.setterName
+
+ if (this.writeable && instance.meta.hasMethod(setterName))
+ instance[ setterName ](value)
+ else
+ this.setRawValueTo(instance, value)
+ },
+
+
+ initFromConfig : function (instance, config) {
+ var name = this.name
+
+ var value, isSet = false
+
+ if (config.hasOwnProperty(name)) {
+ value = config[name]
+ isSet = true
+ } else {
+ var init = this.init
+
+ // simple function (not class) has been used as "init" value
+ if (Joose.O.isFunction(init) && !Joose.O.isClass(init)) {
+
+ value = init.call(instance, config, name)
+
+ isSet = true
+
+ } else if (this.builder) {
+
+ value = instance[ this.builder.replace(/^this\./, '') ](config, name)
+ isSet = true
+ }
+ }
+
+ if (isSet)
+ this.setRawValueTo(instance, value)
+ else
+ if (this.required) throw new Error("Required attribute [" + name + "] is missed during initialization of " + instance)
+ }
+ }
+
+}).c
+;
+Joose.Managed.Attribute.Builder = new Joose.Managed.Role('Joose.Managed.Attribute.Builder', {
+
+
+ have : {
+ defaultAttributeClass : Joose.Managed.Attribute
+ },
+
+ builder : {
+
+ methods : {
+
+ has : function (targetClassMeta, info) {
+ Joose.O.eachOwn(info, function (props, name) {
+ if (typeof props != 'object' || props == null || props.constructor == / /.constructor) props = { init : props }
+
+ props.meta = props.meta || targetClassMeta.defaultAttributeClass
+
+ if (/^__/.test(name)) {
+ name = name.replace(/^_+/, '')
+
+ props.isPrivate = true
+ }
+
+ targetClassMeta.addAttribute(name, props.init, props)
+ }, this)
+ },
+
+
+ hasnot : function (targetClassMeta, info) {
+ this.havenot(targetClassMeta, info)
+ },
+
+
+ hasnt : function (targetClassMeta, info) {
+ this.hasnot(targetClassMeta, info)
+ }
+ }
+
+ }
+
+}).c
+;
+Joose.Managed.My = new Joose.Managed.Role('Joose.Managed.My', {
+
+ have : {
+ myClass : null,
+
+ needToReAlias : false
+ },
+
+
+ methods : {
+ createMy : function (extend) {
+ var thisMeta = this.meta
+ var isRole = this instanceof Joose.Managed.Role
+
+ var myExtend = extend.my || {}
+ delete extend.my
+
+ // Symbiont will generally have the same meta class as its hoster, excepting the cases, when the superclass also have the symbiont.
+ // In such cases, the meta class for symbiont will be inherited (unless explicitly specified)
+ var superClassMy = this.superClass.meta.myClass
+
+ if (!isRole && !myExtend.isa && superClassMy) myExtend.isa = superClassMy
+
+
+ if (!myExtend.meta && !myExtend.isa) myExtend.meta = this.constructor
+
+ myExtend.name = this.name + '.my'
+
+ var createdClass = this.myClass = Class(myExtend)
+
+ var c = this.c
+
+ c.prototype.my = c.my = isRole ? createdClass : new createdClass({ HOST : c })
+
+ this.needToReAlias = true
+ },
+
+
+ aliasStaticMethods : function () {
+ this.needToReAlias = false
+
+ var c = this.c
+ var myProto = this.myClass.prototype
+
+ Joose.O.eachOwn(c, function (property, name) {
+ if (property.IS_ALIAS) delete c[ name ]
+ })
+
+ this.myClass.meta.stem.properties.methods.each(function (method, name) {
+
+ if (!c[ name ])
+ (c[ name ] = function () {
+ return myProto[ name ].apply(c.my, arguments)
+ }).IS_ALIAS = true
+ })
+ }
+ },
+
+
+ override : {
+
+ extend : function (props) {
+ var myClass = this.myClass
+
+ if (!myClass && this.superClass.meta.myClass) this.createMy(props)
+
+ if (props.my) {
+ if (!myClass)
+ this.createMy(props)
+ else {
+ this.needToReAlias = true
+
+ myClass.meta.extend(props.my)
+ delete props.my
+ }
+ }
+
+ this.SUPER(props)
+
+ if (this.needToReAlias && !(this instanceof Joose.Managed.Role)) this.aliasStaticMethods()
+ }
+ },
+
+
+ before : {
+
+ addRole : function () {
+ var myStem
+
+ Joose.A.each(arguments, function (arg) {
+
+ if (!arg) throw new Error("Attempt to consume an undefined Role into [" + this.name + "]")
+
+ //instanceof Class to allow treat classes as roles
+ var role = (arg.meta instanceof Joose.Managed.Class) ? arg : arg.role
+
+ if (role.meta.meta.hasAttribute('myClass') && role.meta.myClass) {
+
+ if (!this.myClass) {
+ this.createMy({
+ my : {
+ does : role.meta.myClass
+ }
+ })
+ return
+ }
+
+ myStem = this.myClass.meta.stem
+ if (!myStem.opened) myStem.open()
+
+ myStem.addComposeInfo(role.my.meta.stem)
+ }
+ }, this)
+
+ if (myStem) {
+ myStem.close()
+
+ this.needToReAlias = true
+ }
+ },
+
+
+ removeRole : function () {
+ if (!this.myClass) return
+
+ var myStem = this.myClass.meta.stem
+ myStem.open()
+
+ Joose.A.each(arguments, function (role) {
+ if (role.meta.meta.hasAttribute('myClass') && role.meta.myClass) {
+ myStem.removeComposeInfo(role.my.meta.stem)
+
+ this.needToReAlias = true
+ }
+ }, this)
+
+ myStem.close()
+ }
+
+ }
+
+}).c;
+Joose.Namespace = Joose.stub()
+
+Joose.Namespace.Able = new Joose.Managed.Role('Joose.Namespace.Able', {
+
+ have : {
+ bodyFunc : null
+ },
+
+
+ before : {
+ extend : function (extend) {
+ if (extend.body) {
+ this.bodyFunc = extend.body
+ delete extend.body
+ }
+ }
+ },
+
+
+ after: {
+
+ afterMutate : function () {
+ var bodyFunc = this.bodyFunc
+ delete this.bodyFunc
+
+ if (bodyFunc) Joose.Namespace.Manager.my.executeIn(this.c, bodyFunc)
+ }
+ }
+
+}).c;
+Joose.Managed.Bootstrap = new Joose.Managed.Role('Joose.Managed.Bootstrap', {
+
+ does : [ Joose.Namespace.Able, Joose.Managed.My, Joose.Managed.Attribute.Builder ]
+
+}).c
+;
+Joose.Meta = Joose.stub()
+
+
+Joose.Meta.Object = new Joose.Proto.Class('Joose.Meta.Object', {
+
+ isa : Joose.Proto.Object
+
+}).c
+
+
+;
+Joose.Meta.Class = new Joose.Managed.Class('Joose.Meta.Class', {
+
+ isa : Joose.Managed.Class,
+
+ does : Joose.Managed.Bootstrap,
+
+ have : {
+ defaultSuperClass : Joose.Meta.Object
+ }
+
+}).c
+
+;
+Joose.Meta.Role = new Joose.Meta.Class('Joose.Meta.Role', {
+
+ isa : Joose.Managed.Role,
+
+ does : Joose.Managed.Bootstrap
+
+}).c;
+Joose.Namespace.Keeper = new Joose.Meta.Class('Joose.Namespace.Keeper', {
+
+ isa : Joose.Meta.Class,
+
+ have : {
+ externalConstructor : null
+ },
+
+
+ methods: {
+
+ defaultConstructor: function () {
+
+ return function () {
+ //constructors should assume that meta is attached to 'arguments.callee' (not to 'this')
+ var thisMeta = arguments.callee.meta
+
+ if (thisMeta instanceof Joose.Namespace.Keeper) throw new Error("Module [" + thisMeta.c + "] may not be instantiated. Forgot to 'use' the class with the same name?")
+
+ var externalConstructor = thisMeta.externalConstructor
+
+ if (typeof externalConstructor == 'function') {
+
+ externalConstructor.meta = thisMeta
+
+ return externalConstructor.apply(this, arguments)
+ }
+
+ throw "NamespaceKeeper of [" + thisMeta.name + "] was planted incorrectly."
+ }
+ },
+
+
+ //withClass should be not constructed yet on this stage (see Joose.Proto.Class.construct)
+ //it should be on the 'constructorOnly' life stage (should already have constructor)
+ plant: function (withClass) {
+ var keeper = this.c
+
+ keeper.meta = withClass.meta
+
+ keeper.meta.c = keeper
+ keeper.meta.externalConstructor = withClass
+ }
+ }
+
+}).c
+
+
+;
+Joose.Namespace.Manager = new Joose.Managed.Class('Joose.Namespace.Manager', {
+
+ have : {
+ current : null
+ },
+
+
+ methods : {
+
+ initialize : function () {
+ this.current = [ Joose.top ]
+ },
+
+
+ getCurrent: function () {
+ return this.current[0]
+ },
+
+
+ executeIn : function (ns, func) {
+ var current = this.current
+
+ current.unshift(ns)
+ var res = func.call(ns, ns)
+ current.shift()
+
+ return res
+ },
+
+
+ earlyCreate : function (name, metaClass, props) {
+ props.constructorOnly = true
+
+ return new metaClass(name, props).c
+ },
+
+
+ //this function establishing the full "namespace chain" (including the last element)
+ create : function (nsName, metaClass, extend) {
+
+ //if no name provided, then we creating an anonymous class, so just skip all the namespace manipulations
+ if (!nsName) return new metaClass(nsName, extend).c
+
+ var me = this
+
+ if (/^\./.test(nsName)) return this.executeIn(Joose.top, function () {
+ return me.create(nsName.replace(/^\./, ''), metaClass, extend)
+ })
+
+ var props = extend || {}
+
+ var parts = Joose.S.saneSplit(nsName, '.')
+ var object = this.getCurrent()
+ var soFar = object == Joose.top ? [] : Joose.S.saneSplit(object.meta.name, '.')
+
+ for (var i = 0; i < parts.length; i++) {
+ var part = parts[i]
+ var isLast = i == parts.length - 1
+
+ if (part == "meta" || part == "my" || !part) throw "Module name [" + nsName + "] may not include a part called 'meta' or 'my' or empty part."
+
+ var cur = object[part]
+
+ soFar.push(part)
+
+ var soFarName = soFar.join(".")
+ var needFinalize = false
+ var nsKeeper
+
+ // if the namespace segment is empty
+ if (typeof cur == "undefined") {
+ if (isLast) {
+ // perform "early create" which just fills the namespace segment with right constructor
+ // this allows us to have a right constructor in the namespace segment when the `body` will be called
+ nsKeeper = this.earlyCreate(soFarName, metaClass, props)
+ needFinalize = true
+ } else
+ nsKeeper = new Joose.Namespace.Keeper(soFarName).c
+
+ object[part] = nsKeeper
+
+ cur = nsKeeper
+
+ } else if (isLast && cur && cur.meta) {
+
+ var currentMeta = cur.meta
+
+ if (metaClass == Joose.Namespace.Keeper)
+ //`Module` over something case - extend the original
+ currentMeta.extend(props)
+ else {
+
+ if (currentMeta instanceof Joose.Namespace.Keeper) {
+
+ currentMeta.plant(this.earlyCreate(soFarName, metaClass, props))
+
+ needFinalize = true
+ } else
+ throw new Error("Double declaration of [" + soFarName + "]")
+ }
+
+ } else
+ if (isLast && !(cur && cur.meta && cur.meta.meta)) throw "Trying to setup module " + soFarName + " failed. There is already something: " + cur
+
+ // hook to allow embedd resource into meta
+ if (isLast) this.prepareMeta(cur.meta)
+
+ if (needFinalize) cur.meta.construct(props)
+
+ object = cur
+ }
+
+ return object
+ },
+
+
+ prepareMeta : function () {
+ },
+
+
+ prepareProperties : function (name, props, defaultMeta, callback) {
+ if (name && typeof name != 'string') {
+ props = name
+ name = null
+ }
+
+ var meta
+
+ if (props && props.meta) {
+ meta = props.meta
+ delete props.meta
+ }
+
+ if (!meta)
+ if (props && typeof props.isa == 'function' && props.isa.meta)
+ meta = props.isa.meta.constructor
+ else
+ meta = defaultMeta
+
+ return callback.call(this, name, meta, props)
+ },
+
+
+ getDefaultHelperFor : function (metaClass) {
+ var me = this
+
+ return function (name, props) {
+ return me.prepareProperties(name, props, metaClass, function (name, meta, props) {
+ return me.create(name, meta, props)
+ })
+ }
+ },
+
+
+ register : function (helperName, metaClass, func) {
+ var me = this
+
+ if (this.meta.hasMethod(helperName)) {
+
+ var helper = function () {
+ return me[ helperName ].apply(me, arguments)
+ }
+
+ if (!Joose.top[ helperName ]) Joose.top[ helperName ] = helper
+ if (!Joose[ helperName ]) Joose[ helperName ] = helper
+
+ if (Joose.is_NodeJS && typeof exports != 'undefined') exports[ helperName ] = helper
+
+ } else {
+ var methods = {}
+
+ methods[ helperName ] = func || this.getDefaultHelperFor(metaClass)
+
+ this.meta.extend({
+ methods : methods
+ })
+
+ this.register(helperName)
+ }
+ },
+
+
+ Module : function (name, props) {
+ return this.prepareProperties(name, props, Joose.Namespace.Keeper, function (name, meta, props) {
+ if (typeof props == 'function') props = { body : props }
+
+ return this.create(name, meta, props)
+ })
+ }
+ }
+
+}).c
+
+Joose.Namespace.Manager.my = new Joose.Namespace.Manager()
+
+Joose.Namespace.Manager.my.register('Class', Joose.Meta.Class)
+Joose.Namespace.Manager.my.register('Role', Joose.Meta.Role)
+Joose.Namespace.Manager.my.register('Module')
+
+
+// for the rest of the package
+var Class = Joose.Class
+var Role = Joose.Role
+;
+Role('Joose.Attribute.Delegate', {
+
+ have : {
+ handles : null
+ },
+
+
+ override : {
+
+ eachDelegate : function (handles, func, scope) {
+ if (typeof handles == 'string') return func.call(scope, handles, handles)
+
+ if (handles instanceof Array)
+ return Joose.A.each(handles, function (delegateTo) {
+
+ func.call(scope, delegateTo, delegateTo)
+ })
+
+ if (handles === Object(handles))
+ Joose.O.eachOwn(handles, function (delegateTo, handleAs) {
+
+ func.call(scope, handleAs, delegateTo)
+ })
+ },
+
+
+ getAccessorsFor : function (targetClass) {
+ var targetMeta = targetClass.meta
+ var methods = this.SUPER(targetClass)
+
+ var me = this
+
+ this.eachDelegate(this.handles, function (handleAs, delegateTo) {
+
+ if (!targetMeta.hasMethod(handleAs)) {
+ var handler = methods[ handleAs ] = function () {
+ var attrValue = me.getValueFrom(this)
+
+ return attrValue[ delegateTo ].apply(attrValue, arguments)
+ }
+
+ handler.ACCESSOR_FROM = me
+ }
+ })
+
+ return methods
+ },
+
+
+ getAccessorsFrom : function (from) {
+ var methods = this.SUPER(from)
+
+ var me = this
+ var targetMeta = from.meta
+
+ this.eachDelegate(this.handles, function (handleAs) {
+
+ var handler = targetMeta.getMethod(handleAs)
+
+ if (handler && handler.value.ACCESSOR_FROM == me) methods.push(handleAs)
+ })
+
+ return methods
+ }
+ }
+})
+
+;
+Role('Joose.Attribute.Trigger', {
+
+ have : {
+ trigger : null
+ },
+
+
+ after : {
+ initialize : function() {
+ if (this.trigger) {
+ if (!this.writeable) throw new Error("Can't use `trigger` for read-only attributes")
+
+ this.hasSetter = true
+ }
+ }
+ },
+
+
+ override : {
+
+ getSetter : function() {
+ var original = this.SUPER()
+ var trigger = this.trigger
+
+ if (!trigger) return original
+
+ var me = this
+ var init = Joose.O.isFunction(me.init) ? null : me.init
+
+ return function () {
+ var oldValue = me.hasValue(this) ? me.getValueFrom(this) : init
+
+ var res = original.apply(this, arguments)
+
+ trigger.call(this, me.getValueFrom(this), oldValue)
+
+ return res
+ }
+ }
+ }
+})
+
+;
+Role('Joose.Attribute.Lazy', {
+
+
+ have : {
+ lazy : null
+ },
+
+
+ before : {
+ computeValue : function () {
+ if (typeof this.init == 'function' && this.lazy) {
+ this.lazy = this.init
+ delete this.init
+ }
+ }
+ },
+
+
+ after : {
+ initialize : function () {
+ if (this.lazy) this.readable = this.hasGetter = true
+ }
+ },
+
+
+ override : {
+
+ getGetter : function () {
+ var original = this.SUPER()
+ var lazy = this.lazy
+
+ if (!lazy) return original
+
+ var me = this
+
+ return function () {
+ if (!me.hasValue(this)) {
+ var initializer = typeof lazy == 'function' ? lazy : this[ lazy.replace(/^this\./, '') ]
+
+ me.setValueTo(this, initializer.apply(this, arguments))
+ }
+
+ return original.call(this)
+ }
+ }
+ }
+})
+
+;
+Role('Joose.Attribute.Accessor.Combined', {
+
+
+ have : {
+ isCombined : false
+ },
+
+
+ after : {
+ initialize : function() {
+ this.isCombined = this.isCombined || /..c/i.test(this.is)
+
+ if (this.isCombined) {
+ this.slot = '$$' + this.name
+
+ this.hasGetter = true
+ this.hasSetter = false
+
+ this.setterName = this.getterName = this.publicName
+ }
+ }
+ },
+
+
+ override : {
+
+ getGetter : function() {
+ var getter = this.SUPER()
+
+ if (!this.isCombined) return getter
+
+ var setter = this.getSetter()
+
+ var me = this
+
+ return function () {
+
+ if (!arguments.length) {
+ if (me.readable) return getter.call(this)
+ throw new Error("Call to getter of unreadable attribute: [" + me.name + "]")
+ }
+
+ if (me.writeable) return setter.apply(this, arguments)
+
+ throw new Error("Call to setter of read-only attribute: [" + me.name + "]")
+ }
+ }
+ }
+
+})
+
+;
+Joose.Managed.Attribute.meta.extend({
+ does : [ Joose.Attribute.Delegate, Joose.Attribute.Trigger, Joose.Attribute.Lazy, Joose.Attribute.Accessor.Combined ]
+})
+
+;
+Role('Joose.Meta.Singleton', {
+
+ has : {
+ forceInstance : Joose.I.Object,
+ instance : null
+ },
+
+
+
+ override : {
+
+ defaultConstructor : function () {
+ var meta = this
+ var previous = this.SUPER()
+
+ this.adaptConstructor(previous)
+
+ return function (forceInstance, params) {
+ if (forceInstance == meta.forceInstance) return previous.apply(this, params) || this
+
+ var instance = meta.instance
+
+ if (instance) {
+ if (meta.hasMethod('configure')) instance.configure.apply(instance, arguments)
+ } else
+ meta.instance = new meta.c(meta.forceInstance, arguments)
+
+ return meta.instance
+ }
+ }
+ }
+
+
+})
+
+
+Joose.Namespace.Manager.my.register('Singleton', Class({
+ isa : Joose.Meta.Class,
+ meta : Joose.Meta.Class,
+
+ does : Joose.Meta.Singleton
+}))
+;
+;
+}();;
+;
+Class('Scope.Provider', {
+
+ /*PKGVERSION*/VERSION : 0.12,
+
+ has : {
+ scope : null,
+
+ seedingCode : null,
+ seedingScript : null,
+
+ preload : {
+ is : 'ro',
+ init : Joose.I.Array
+ },
+
+ cleanupCallback : null,
+ beforeCleanupCallback : null
+ },
+
+
+ methods : {
+
+ isCSS : function (url) {
+ return /\.css(\?.*)?$/i.test(url)
+ },
+
+
+ isAlreadySetUp : function () {
+ return Boolean(this.scope)
+ },
+
+
+ addPreload : function (preloadDesc) {
+ if (this.isAlreadySetUp()) throw new Error("Can't use `addPreload` - scope is already setup. Use `runCode/runScript` instead")
+
+ if (typeof preloadDesc == 'string')
+
+ if (this.isCSS(preloadDesc))
+ preloadDesc = {
+ type : 'css',
+ url : preloadDesc
+ }
+ else
+ preloadDesc = {
+ type : 'js',
+ url : preloadDesc
+ }
+ else
+
+ if (preloadDesc.text)
+ preloadDesc = {
+ type : 'js',
+ content : preloadDesc.text
+ }
+
+ if (!preloadDesc.type) throw new Error("Preload descriptor must have the `type` property")
+
+ this.preload.push(preloadDesc)
+ },
+
+
+ addOnErrorHandler : function (handler, callback) {
+ throw "Abstract method `addOnErrorHandler` of Scope.Provider called"
+ },
+
+
+ create : function () {
+ throw "Abstract method `create` of Scope.Provider called"
+ },
+
+
+ setup : function (callback) {
+ throw "Abstract method `setup` of Scope.Provider called"
+ },
+
+
+ cleanup : function (callback) {
+ throw "Abstract method `cleanup` of Scope.Provider called"
+ },
+
+
+ runCode : function (text, callback) {
+ throw "Abstract method `runCode` of Scope.Provider called"
+ },
+
+
+ runScript : function (url, callback) {
+ throw "Abstract method `runScript` of Scope.Provider called"
+ }
+ }
+})
+
+
+Scope.Provider.__ONLOAD__ = {}
+Scope.Provider.__ONERROR__ = {};
+Role('Scope.Provider.Role.WithDOM', {
+
+ requires : [ 'getDocument', 'create', 'getPreload', 'isAlreadySetUp' ],
+
+ has : {
+ useStrictMode : true,
+ sourceURL : null,
+
+ minViewportSize : null,
+
+ parentWindow : function () { return window },
+ scopeId : function () { return Math.round(Math.random() * 1e10) },
+
+ // init function
+ attachToOnError : function () {
+
+ // returns the value of the attribute
+ return function (window, scopeId, handler, preventDefault) {
+
+ var prevHandler = window.onerror
+ if (prevHandler && prevHandler.__SP_MANAGED__) return
+
+ window.onerror = function (message, url, lineNumber) {
+ // prevent recursive calls if other authors politely did not overwrite the handler and will call it
+ if (handler.__CALLING__) return
+
+ handler.__CALLING__ = true
+
+ prevHandler && prevHandler.apply(this, arguments)
+
+ handler.apply(this, arguments)
+
+ handler.__CALLING__ = false
+
+ // in FF/IE need to return `true` to prevent default action
+ if (preventDefault !== false) return window.WebKitPoint ? false : true
+ }
+
+ window.onerror.__SP_MANAGED__ = true
+ }
+ },
+
+ // this is a "cached" onerror handler - a handler which was provided before the scope
+ // has started the creation process - should be installed ASAP in the creation process
+ // to allow catching of the exceptions in the scope with `sourceURL`
+ cachedOnError : null
+ },
+
+
+ override : {
+
+ cleanup : function () {
+ this.scope.onerror = null
+
+ this.SUPERARG(arguments)
+
+ this.scope = null
+ }
+ },
+
+
+ methods : {
+
+ cleanupHanlders : function () {
+ var scopeProvider = this.parentWindow.Scope.Provider
+
+ delete scopeProvider.__ONLOAD__[ this.scopeId ]
+ delete scopeProvider.__ONERROR__[ this.scopeId ]
+ },
+
+
+ getHead : function () {
+ return this.getDocument().getElementsByTagName('head')[ 0 ]
+ },
+
+
+ installOnErrorHandler : function (handler) {
+ if (!this.isAlreadySetUp()) throw "Scope should be already set up"
+
+ this.attachToOnError(this.scope, this.scopeId, handler)
+ },
+
+
+ addOnErrorHandler : function (handler, preventDefault) {
+ handler.__SP_MANAGED__ = true
+
+ if (this.cachedOnError && this.cachedOnError != handler) throw "Can only install one on error handler"
+ this.cachedOnError = handler
+
+ var scopeId = this.scopeId
+
+ this.parentWindow.Scope.Provider.__ONERROR__[ scopeId ] = handler
+
+ var attachToOnError = ';(' + this.attachToOnError.toString() + ')(window, ' + scopeId + ', (window.opener || window.parent).Scope.Provider.__ONERROR__[ ' + scopeId + ' ], ' + preventDefault + ');'
+
+ if (this.isAlreadySetUp())
+ this.runCode(attachToOnError)
+ else {
+ // this is a fallback - run the "attachToOnError" from inside of scope
+ this.getPreload().unshift({
+ type : 'js',
+ content : attachToOnError,
+ unordered : true
+ })
+ }
+ },
+
+
+ addSeedingToPreload : function () {
+ var preload = this.getPreload()
+
+ if (this.seedingCode) preload.unshift({
+ type : 'js',
+ content : this.seedingCode
+ })
+
+ if (this.seedingScript) preload.push({
+ type : 'js',
+ url : this.seedingScript
+ })
+ },
+
+
+ setup : function (callback) {
+ var isIE = 'v' == '\v' || Boolean(this.parentWindow.msWriteProfilerMark)
+// var isOpera = Object.prototype.toString.call(this.parentWindow.opera) == '[object Opera]'
+ var hasInlineScript = false
+
+ Joose.A.each(this.getPreload(), function (preloadDesc) {
+ // IE will execute the inline scripts ASAP, this might be not what we want (inline script might be need executed only after some url script)
+ // its however ok in some cases (like adding `onerror` handler
+ // such inline scripts should be marked with `unordered` - true
+ if (preloadDesc.type == 'js' && preloadDesc.content && !preloadDesc.unordered) {
+ hasInlineScript = true
+
+ return false
+ }
+ })
+
+ if (this.sourceURL || isIE && hasInlineScript) {
+ this.addSeedingToPreload()
+
+ this.setupIncrementally(callback)
+
+ } else {
+ // for sane browsers just add the seeding code and seeding script to preloads
+ if (!isIE) this.addSeedingToPreload()
+
+ // seeding scripts are included only for sane browsers (not IE)
+ this.setupWithDocWrite(callback, isIE)
+ }
+ },
+
+
+ setupWithDocWrite : function (callback, needToSeed) {
+ var html = []
+ var me = this
+
+ Joose.A.each(this.getPreload(), function (preloadDesc) {
+
+ if (preloadDesc.type == 'js')
+ html.push(me.getScriptTagString(preloadDesc.url, preloadDesc.content))
+
+ else if (preloadDesc.type == 'css')
+ html.push(me.getLinkTagString(preloadDesc.url, preloadDesc.content))
+
+ else throw "Incorrect preload descriptor " + preloadDesc
+ })
+
+ // no need to wait for DOM ready - we'll overwrite it anyway
+ this.create()
+
+ var scopeId = this.scopeId
+
+ this.parentWindow.Scope.Provider.__ONLOAD__[ scopeId ] = function () {
+
+ var cont = function () { callback && callback(me) }
+
+ // sane browsers - seeding code and script has been already added
+ if (!needToSeed) { cont(); return }
+
+ // our beloved IE - manually seeding the scope
+
+ if (me.seedingCode) me.runCode(me.seedingCode)
+
+ if (me.seedingScript)
+ me.runScript(me.seedingScript, cont)
+ else
+ cont()
+ }
+
+ var doc = this.getDocument()
+
+ doc.open()
+
+ doc.write([
+ this.useStrictMode ? '' : '',
+ '',
+ '',
+ html.join(''),
+ '',
+
+ '',
+ '',
+ ''
+ ].join(''))
+
+ doc.close()
+
+ // Chrome (Webkit?) will clear the `onerror` after "doc.open()/.close()" so need to re-install it
+ if (me.cachedOnError) me.installOnErrorHandler(me.cachedOnError)
+ },
+
+
+ setupIncrementally : function (callback) {
+ var me = this
+
+ // here the "onerror" should be included early in the "preloads"
+ this.create(function () {
+
+ var loadScripts = function (preloads, callback) {
+
+ var cont = function () { loadScripts(preloads, callback) }
+
+ if (!preloads.length)
+ callback && callback()
+ else {
+ var preloadDesc = preloads.shift()
+
+ if (preloadDesc.url)
+ me.runScript(preloadDesc.url, cont)
+ else
+ if (preloadDesc.type == 'js')
+ me.runCode(preloadDesc.content, cont)
+ else {
+ me.addStyleTag(preloadDesc.content)
+
+ cont()
+ }
+ }
+ }
+
+ loadScripts(me.getPreload().slice(), callback)
+ })
+ },
+
+
+ getScriptTagString : function (url, text) {
+ var res = ''
+ else
+ res += '>' + text.replace(/<\/script>/gi, '\\x3C/script>') + ''
+
+ return res
+ },
+
+
+ getLinkTagString : function (url, text) {
+ if (url) return ''
+
+ if (text) return ''
+ },
+
+
+
+ loadCSS : function (url, callback) {
+ var doc = this.getDocument()
+ var link = doc.createElement('link')
+
+ link.type = 'text/css'
+ link.rel = 'stylesheet'
+ link.href = url
+
+ this.getHead().appendChild(link)
+
+ var hasContinued = false
+
+ var cont = function () {
+ // just in case some crazy JS engine calls `onerror` even after node removal
+ if (hasContinued) return
+ hasContinued = true
+ clearTimeout(forcedTimeout)
+
+ if (callback) callback()
+
+ doc.body.removeChild(img)
+ }
+
+ var forcedTimeout = setTimeout(cont, 3000)
+
+ var img = doc.createElement('img')
+
+ img.onerror = cont
+
+ doc.body.appendChild(img)
+
+ img.src = url
+ },
+
+
+ runCode : function (text, callback) {
+ this.getHead().appendChild(this.createScriptTag(text))
+
+ callback && callback()
+ },
+
+
+ runScript : function (url, callback) {
+ if (this.isCSS(url))
+ this.loadCSS(url, callback)
+ else
+ this.getHead().appendChild(this.createScriptTag(null, url, callback))
+ },
+
+
+ createScriptTag : function (text, url, callback) {
+ var node = this.getDocument().createElement("script")
+
+ node.setAttribute("type", "text/javascript")
+
+ if (url) node.setAttribute("src", url)
+
+ if (text) node.text = text
+
+ if (callback) node.onload = node.onreadystatechange = function() {
+ if (!node.readyState || node.readyState == "loaded" || node.readyState == "complete" || node.readyState == 4 && node.status == 200) {
+ node.onload = node.onreadystatechange = null
+
+ //surely for IE6..
+ if ('v' == '\v')
+ setTimeout(callback, 0)
+ else
+ callback()
+ }
+ }
+
+ return node
+ },
+
+
+ addStyleTag : function (text) {
+ var document = this.getDocument()
+ var node = document.createElement('style')
+
+ node.setAttribute("type", "text/css")
+
+ var head = document.getElementsByTagName('head')[0]
+ head.appendChild(node)
+
+ if (node.styleSheet) { // IE
+ node.styleSheet.cssText = text
+ } else { // the world
+ node.appendChild(document.createTextNode(text))
+ }
+ }
+ }
+})
+
+
+/**
+
+Name
+====
+
+Scope.Provider.Role.WithDOM - role for scope provider, which uses `script` tag for running the code.
+
+
+SYNOPSIS
+========
+
+ Class('Scope.Provider.IFrame', {
+
+ isa : Scope.Provider,
+
+ does : Scope.Provider.Role.WithDOM,
+
+ ...
+ })
+
+DESCRIPTION
+===========
+
+`Scope.Provider.Role.WithDOM` requires the implementation of the `getDocument` method, which should return the
+document into which the `script` tags will be created.
+
+In return, this role provides the implementation of `runCode` and `runScript`.
+
+
+
+
+GETTING HELP
+============
+
+This extension is supported via github issues tracker:
+
+For general Joose questions you can also visit [#joose](http://webchat.freenode.net/?randomnick=1&channels=joose&prompt=1)
+on irc.freenode.org or the forum at:
+
+
+
+SEE ALSO
+========
+
+Web page of this module:
+
+General documentation for Joose:
+
+
+BUGS
+====
+
+All complex software has bugs lurking in it, and this module is no exception.
+
+Please report any bugs through the web interface at
+
+
+
+AUTHORS
+=======
+
+Nickolay Platonov
+
+
+
+
+
+COPYRIGHT AND LICENSE
+=====================
+
+This software is Copyright (c) 2010 by Nickolay Platonov .
+
+This is free software, licensed under:
+
+ The GNU Lesser General Public License, Version 3, June 2007
+
+*/;
+Class('Scope.Provider.IFrame', {
+
+ isa : Scope.Provider,
+
+ does : Scope.Provider.Role.WithDOM,
+
+
+ have : {
+ iframe : null,
+ cls : null,
+
+ performWrap : false,
+ wrapCls : null,
+ wrapper : null,
+
+ parentEl : null,
+
+ cleanupDelay : 1000
+ },
+
+
+ methods : {
+
+ getDocument : function () {
+ return this.iframe.contentWindow.document
+ },
+
+
+ create : function (onLoadCallback) {
+ var me = this
+ var doc = this.parentWindow.document
+ var iframe = this.iframe = doc.createElement('iframe')
+
+ var minViewportSize = this.minViewportSize
+
+ iframe.className = this.cls || ''
+ iframe.style.width = (minViewportSize && minViewportSize.width || 1024) + 'px'
+ iframe.style.height = (minViewportSize && minViewportSize.height || 768) + 'px'
+ iframe.setAttribute('frameborder', 0)
+
+ var ignoreOnLoad = false
+
+ var callback = function () {
+ if (ignoreOnLoad) return
+
+ if (iframe.detachEvent)
+ iframe.detachEvent('onload', callback)
+ else
+ iframe.onload = null
+
+ onLoadCallback && onLoadCallback(me)
+ }
+
+ if (iframe.attachEvent)
+ iframe.attachEvent('onload', callback)
+ else
+ iframe.onload = callback
+
+ iframe.src = this.sourceURL || 'about:blank'
+
+ if (this.performWrap) {
+ var wrapper = this.wrapper = this.wrapper || doc.createElement('div')
+
+ wrapper.className = this.wrapCls || ''
+
+ wrapper.appendChild(iframe)
+ }
+
+ ;(this.parentEl || doc.body).appendChild(wrapper || iframe)
+
+ var scope = this.scope = iframe.contentWindow
+ var doc = this.getDocument()
+
+ // dances with tambourine around the IE, somehow fixes the cross-domain limits
+ if ('v' == '\v' || Boolean(this.parentWindow.msWriteProfilerMark)) {
+ // only ignore the 1st call to callback when there is a `sourceURL` config
+ // which will later be assigned to `iframe.src` and will trigger a new iframe loading
+ if (this.sourceURL) ignoreOnLoad = true
+
+ doc.open()
+ doc.write('')
+ doc.close()
+
+ ignoreOnLoad = false
+
+ iframe.onreadystatechange = function () {
+ if (iframe.readyState == 'complete') iframe.onreadystatechange = null
+
+ // trying to add the "early" onerror handler on each "readyState" change
+ // for some mystical reasons can't use `me.installOnErrorHandler` need to inline the call
+ if (me.cachedOnError) me.attachToOnError(scope, me.scopeId, me.cachedOnError)
+ }
+
+ if (this.sourceURL) iframe.src = this.sourceURL
+ }
+
+ // trying to add the "early" onerror handler - installing it in this stage will only work in FF
+ // (other browsers will clear on varios stages)
+ if (me.cachedOnError) me.installOnErrorHandler(me.cachedOnError)
+ },
+
+
+ cleanup : function () {
+ var wrapper = this.wrapper || this.iframe
+ var iframe = this.iframe
+ var me = this
+
+ wrapper.style.display = 'none'
+
+ var onUnloadChecker = function () {
+ if (!window.onunload) window.onunload = function () { return 'something' }
+ }
+
+ // add the `onunload` handler if there's no any - attempting to prevent browser from caching the iframe
+ // trying to create the handler from inside of the scope
+ this.runCode(';(' + onUnloadChecker.toString() + ')();')
+
+ this.iframe = null
+ this.scope = null
+ this.wrapper = null
+
+ // wait for 1000ms to allow time for possible `setTimeout` in the scope of iframe
+ setTimeout(function () {
+
+ if (me.beforeCleanupCallback) me.beforeCleanupCallback()
+
+ // chaging the page, triggering `onunload` and hopefully preventing browser from caching the content of iframe
+ iframe.src = 'javascript:false'
+
+ // wait again before removing iframe from the DOM, as recommended by some online sources
+ setTimeout(function () {
+ ;(me.parentEl || me.parentWindow.document.body).removeChild(wrapper)
+
+ wrapper = null
+ iframe = null
+
+ me.parentEl = null
+
+ me.cleanupHanlders()
+
+ if (me.cleanupCallback) me.cleanupCallback()
+
+ }, me.cleanupDelay)
+ }, me.cleanupDelay)
+ }
+ }
+})
+
+/**
+
+Name
+====
+
+Scope.Provider.IFrame - scope provider, which uses the iframe.
+
+
+SYNOPSIS
+========
+
+ var provider = new Scope.Provider.IFrame()
+
+ provider.setup(function () {
+
+ if (provider.scope.SOME_GLOBAL == 'some_value') {
+ ...
+ }
+
+ provider.runCode(text, callback)
+
+ ...
+
+ provider.runScript(url, callback)
+
+ ...
+
+ provider.cleanup()
+ })
+
+
+DESCRIPTION
+===========
+
+`Scope.Provider.IFrame` is an implementation of the scope provider, which uses the iframe,
+to create a new scope.
+
+
+ISA
+===
+
+[Scope.Provider](../Provider.html)
+
+
+DOES
+====
+
+[Scope.Provider.Role.WithDOM](Role/WithDOM.html)
+
+
+
+GETTING HELP
+============
+
+This extension is supported via github issues tracker:
+
+You can also ask questions at IRC channel : [#joose](http://webchat.freenode.net/?randomnick=1&channels=joose&prompt=1)
+
+Or the mailing list:
+
+
+
+SEE ALSO
+========
+
+Web page of this module:
+
+General documentation for Joose:
+
+
+BUGS
+====
+
+All complex software has bugs lurking in it, and this module is no exception.
+
+Please report any bugs through the web interface at
+
+
+
+AUTHORS
+=======
+
+Nickolay Platonov
+
+
+
+
+
+COPYRIGHT AND LICENSE
+=====================
+
+This software is Copyright (c) 2010 by Nickolay Platonov .
+
+This is free software, licensed under:
+
+ The GNU Lesser General Public License, Version 3, June 2007
+
+*/;
+Class('Scope.Provider.Window', {
+
+ isa : Scope.Provider,
+
+ does : Scope.Provider.Role.WithDOM,
+
+
+ has : {
+ popupWindow : null
+ },
+
+
+ methods : {
+
+ create : function (onLoadCallback) {
+ var popup = this.scope = this.popupWindow = this.parentWindow.open(this.sourceURL || 'about:blank', '_blank', "width=800,height=600")
+
+ if (!popup) {
+ alert('Please enable popups for the host with this test suite running: ' + this.parentWindow.location.host)
+ throw 'Please enable popups for the host with this test suite running: ' + this.parentWindow.location.host
+ }
+
+ var isIE = 'v' == '\v' || Boolean(this.parentWindow.msWriteProfilerMark)
+
+ // dances with tambourine around the IE
+ if (isIE && !this.sourceURL) {
+ var doc = this.getDocument()
+
+ doc.open()
+ doc.write('')
+ doc.close()
+ }
+
+ // trying to add the "early" onerror handler - will probably only work in FF
+ if (this.cachedOnError) this.installOnErrorHandler(this.cachedOnError)
+
+ /*!
+ * contentloaded.js
+ *
+ * Author: Diego Perini (diego.perini at gmail.com)
+ * Summary: cross-browser wrapper for DOMContentLoaded
+ * Updated: 20101020
+ * License: MIT
+ * Version: 1.2
+ *
+ * URL:
+ * http://javascript.nwbox.com/ContentLoaded/
+ * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
+ *
+ */
+
+ // @win window reference
+ // @fn function reference
+ var contentLoaded = function (win, fn) {
+
+ var done = false, top = true,
+
+ doc = win.document, root = doc.documentElement,
+
+ add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
+ rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
+ pre = doc.addEventListener ? '' : 'on',
+
+ init = function(e) {
+ if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
+
+ (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
+
+ if (!done && (done = true)) fn.call(win, e.type || e);
+ },
+
+ poll = function() {
+ try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; }
+
+ init('poll');
+ };
+
+ if (doc.readyState == 'complete')
+ fn.call(win, 'lazy');
+ else {
+ if (doc.createEventObject && root.doScroll) {
+ try { top = !win.frameElement; } catch(e) { }
+ if (top) poll();
+ }
+ doc[add](pre + 'DOMContentLoaded', init, false);
+ doc[add](pre + 'readystatechange', init, false);
+ win[add](pre + 'load', init, false);
+ }
+ }
+
+ contentLoaded(popup, onLoadCallback || function () {})
+ },
+
+
+ getDocument : function () {
+ return this.popupWindow.document
+ },
+
+
+ cleanup : function () {
+ if (this.beforeCleanupCallback) this.beforeCleanupCallback()
+
+ this.popupWindow.close()
+
+ this.popupWindow = null
+
+ this.cleanupHanlders()
+
+ if (this.cleanupCallback) this.cleanupCallback()
+ }
+ }
+})
+
+/**
+
+Name
+====
+
+Scope.Provider.Window - scope provider, which uses the popup browser window.
+
+
+SYNOPSIS
+========
+
+ var provider = new Scope.Provider.Window()
+
+ provider.setup(function () {
+
+ if (provider.scope.SOME_GLOBAL == 'some_value') {
+ ...
+ }
+
+ provider.runCode(text, callback)
+
+ ...
+
+ provider.runScript(url, callback)
+
+ ...
+
+ provider.cleanup()
+ })
+
+
+DESCRIPTION
+===========
+
+`Scope.Provider.Window` is an implementation of the scope provider, which uses the popup browser window,
+to create a new scope.
+
+
+ISA
+===
+
+[Scope.Provider](../Provider.html)
+
+
+DOES
+====
+
+[Scope.Provider.Role.WithDOM](Role/WithDOM.html)
+
+
+
+GETTING HELP
+============
+
+This extension is supported via github issues tracker:
+
+You can also ask questions at IRC channel : [#joose](http://webchat.freenode.net/?randomnick=1&channels=joose&prompt=1)
+
+Or the mailing list:
+
+
+
+SEE ALSO
+========
+
+Web page of this module:
+
+General documentation for Joose:
+
+
+BUGS
+====
+
+All complex software has bugs lurking in it, and this module is no exception.
+
+Please report any bugs through the web interface at
+
+
+
+AUTHORS
+=======
+
+Nickolay Platonov
+
+
+
+
+
+COPYRIGHT AND LICENSE
+=====================
+
+This software is Copyright (c) 2010 by Nickolay Platonov .
+
+This is free software, licensed under:
+
+ The GNU Lesser General Public License, Version 3, June 2007
+
+*/;
+Class('Scope.Provider.NodeJS', {
+
+ isa : Scope.Provider,
+
+
+ has : {
+ sourceURL : null
+ },
+
+
+ methods : {
+
+ compile : function (module, content, filename) {
+ var Module = require('module')
+ var path = require('path')
+
+ var self = module;
+ // remove shebang
+ content = content.replace(/^\#\!.*/, '');
+
+ var modRequire = function (path) {
+ return self.require(path);
+ }
+
+ modRequire.resolve = function(request) {
+ return Module._resolveFilename(request, self)[1];
+ };
+
+ Object.defineProperty(modRequire, 'paths', { get: function() {
+ throw new Error('modRequire.paths is removed. Use ' +
+ 'node_modules folders, or the NODE_PATH '+
+ 'environment variable instead.');
+ }});
+
+ modRequire.main = process.mainModule;
+
+ // Enable support to add extra extension types
+ modRequire.extensions = Module._extensions;
+ modRequire.registerExtension = function() {
+ throw new Error('modRequire.registerExtension() removed. Use ' +
+ 'modRequire.extensions instead.');
+ };
+
+ modRequire.cache = Module._cache;
+
+ var dirname = path.dirname(filename);
+
+ // create wrapper function
+ var wrapper = Module.wrap(content);
+
+ var compiledWrapper = require('vm').runInContext(wrapper, this.scope, filename);
+
+ return compiledWrapper.apply(self.exports, [self.exports, modRequire, self, filename, dirname]);
+ },
+
+
+ addOnErrorHandler : function (handler, callback) {
+ },
+
+
+ create : function (callback) {
+ var vm = require('vm')
+ var sandbox = {}
+
+ Joose.O.extend(sandbox, {
+// __PROVIDER__ : true,
+
+ process : process,
+
+ global : sandbox,
+ root : root,
+
+ setTimeout : setTimeout,
+ clearTimeout : clearTimeout,
+ setInterval : setInterval,
+ clearInterval : clearInterval
+// ,
+//
+// __filename : __filename,
+// __dirname : __dirname,
+// module : module
+ })
+
+ var scope = this.scope = vm.createContext(sandbox)
+
+ callback && callback()
+ },
+
+
+ setup : function (callback) {
+ this.create()
+
+ var me = this
+
+ if (this.seedingCode) require('vm').runInContext(this.seedingCode, this.scope)
+
+ Joose.A.each(this.getPreload(), function (preloadDesc) {
+
+ if (preloadDesc.type == 'js')
+ if (preloadDesc.url)
+ me.runScript(preloadDesc.url)
+ else
+ me.runCode(preloadDesc.content)
+ })
+
+ if (this.seedingScript) {
+ var Module = require('module')
+ var path = require('path')
+
+ var module = new Module('./' + this.sourceURL, require.main)
+
+ var filename = module.filename = path.join(path.dirname(require.main.filename), this.sourceURL)
+
+ var content = require('fs').readFileSync(filename, 'utf8')
+ // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
+ // because the buffer-to-string conversion in `fs.readFileSync()`
+ // translates it to FEFF, the UTF-16 BOM.
+ if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1)
+
+ this.compile(module, content, filename)
+ }
+
+ callback && callback()
+ },
+
+
+ runCode : function (text, callback) {
+ var res = require('vm').runInContext(text, this.scope)
+
+ callback && callback(res)
+
+ return res
+ },
+
+
+ runScript : function (url, callback) {
+ var content = require('fs').readFileSync(url, 'utf8')
+
+ var res = require('vm').runInContext(content, this.scope, url)
+
+ callback && callback(res)
+
+ return res
+ },
+
+
+ cleanup : function () {
+ if (this.beforeCleanupCallback) this.beforeCleanupCallback()
+ if (this.cleanupCallback) this.cleanupCallback()
+ }
+ }
+})
+
+
+/**
+
+Name
+====
+
+Scope.Provider.NodeJS - scope provider, which uses the `Script.runInNewContext` call of the NodeJS.
+
+
+SYNOPSIS
+========
+
+ var provider = new Scope.Provider.NodeJS()
+
+ provider.setup(function () {
+
+ if (provider.scope.SOME_GLOBAL == 'some_value') {
+ ...
+ }
+
+ provider.runCode(text, callback)
+
+ ...
+
+ provider.runScript(url, callback)
+
+ ...
+
+ provider.cleanup()
+ })
+
+
+DESCRIPTION
+===========
+
+`Scope.Provider.NodeJS` is an implementation of the scope provider,
+which uses the `Script.runInNewContext` call of the NodeJS platform.
+
+
+ISA
+===
+
+[Scope.Provider](../Provider.html)
+
+
+
+GETTING HELP
+============
+
+This extension is supported via github issues tracker:
+
+You can also ask questions at IRC channel : [#joose](http://webchat.freenode.net/?randomnick=1&channels=joose&prompt=1)
+
+Or the mailing list:
+
+
+
+SEE ALSO
+========
+
+Web page of this module:
+
+General documentation for Joose:
+
+
+BUGS
+====
+
+All complex software has bugs lurking in it, and this module is no exception.
+
+Please report any bugs through the web interface at
+
+
+
+AUTHORS
+=======
+
+Nickolay Platonov
+
+
+
+
+
+COPYRIGHT AND LICENSE
+=====================
+
+This software is Copyright (c) 2010 by Nickolay Platonov .
+
+This is free software, licensed under:
+
+ The GNU Lesser General Public License, Version 3, June 2007
+
+*/;
+;
+Class('JooseX.Observable.Event', {
+
+ has : {
+ name : { required : true },
+ args : { required : true },
+
+ source : { required : true },
+
+ splat : null,
+ current : null,
+
+ bubbling : true
+ },
+
+
+ methods : {
+
+ stopPropagation : function () {
+ this.bubbling = false
+ }
+ }
+})
+
+
+;
+Class('JooseX.Observable.Listener', {
+
+ has : {
+ channel : { required : true },
+ eventName : { required : true },
+
+ func : { required : true },
+ scope : null,
+
+ single : false,
+
+ buffer : null,
+ bufferMax : null,
+
+ bufferStartedAt : null,
+ bufferTimeout : null,
+
+ delayTimeout : null,
+
+ delay : null
+ },
+
+
+ methods : {
+
+ activate : function (event, args) {
+ var me = this
+
+ if (me.buffer != null) {
+
+ if (me.bufferMax != null)
+ if (!me.bufferStartedAt)
+ me.bufferStartedAt = new Date()
+ else
+ if (new Date - me.bufferStartedAt > me.bufferMax) return
+
+
+ if (me.bufferTimeout) clearTimeout(me.bufferTimeout)
+
+ me.bufferTimeout = setTimeout(function () {
+
+ delete me.bufferStartedAt
+ delete me.bufferTimeout
+
+ me.doActivate(event, args)
+
+ }, me.buffer)
+
+ return
+ }
+
+ if (me.delay != null) {
+
+ me.delayTimeout = setTimeout(function () {
+
+ delete me.delayTimeout
+
+ me.doActivate(event, args)
+
+ }, me.delay)
+
+ return
+ }
+
+ return me.doActivate(event, args)
+ },
+
+
+ doActivate : function (event, args) {
+ if (this.single) this.remove()
+
+ return this.func.apply(this.scope || event.source, [ event ].concat(args) ) !== false
+ },
+
+
+ cancel : function () {
+ if (this.buffer) {
+ clearTimeout(this.bufferTimeout)
+
+ delete this.bufferTimeout
+ delete this.bufferStartedAt
+ }
+
+ if (this.delay) clearTimeout(this.delayTimeout)
+ },
+
+
+ remove : function () {
+ this.channel.removeListener(this)
+ }
+ }
+})
+
+
+;
+Class('JooseX.Observable.Channel', {
+
+ has : {
+ channels : Joose.I.Object,
+
+ listeners : Joose.I.Object
+ },
+
+
+ methods : {
+
+ // (!) segments array will be destroyed in this method
+ getListenersFor : function (segments, name, activators) {
+ var listeners = this.listeners
+
+ if (listeners[ '**' ]) {
+
+ var splat = segments.concat(name)
+
+ Joose.A.each(listeners[ '**' ], function (listener) {
+ activators.push({
+ listener : listener,
+ splat : splat
+ })
+ })
+ }
+
+ if (segments.length) {
+ var next = this.getSingleChannel(segments.shift(), true)
+
+ if (next) next.getListenersFor(segments, name, activators)
+ } else {
+
+ if (listeners[ '*' ])
+ Joose.A.each(listeners[ '*' ], function (listener) {
+
+ activators.push({
+ listener : listener,
+ splat : name
+ })
+ })
+
+ if (listeners[ name ])
+ Joose.A.each(listeners[ name ], function (listener) {
+
+ activators.push({
+ listener : listener
+ })
+ })
+ }
+ },
+
+
+ hasListenerFor : function (segments, name) {
+ var listeners = this.listeners
+
+ if (listeners[ '**' ] && listeners[ '**' ].length) return true
+
+ if (segments.length) {
+ var next = this.getSingleChannel(segments.shift(), true)
+
+ if (next) return next.hasListenerFor(segments, name)
+
+ } else {
+
+ if (listeners[ '*' ] && listeners[ '*' ].length) return true
+
+ if (listeners[ name ] && listeners[ name ].length) return true
+ }
+
+ return false
+ },
+
+
+ addListener : function (listener) {
+ var eventName = listener.eventName
+ var listeners = this.listeners
+
+ listeners[ eventName ] = listeners[ eventName ] || []
+
+ listeners[ eventName ].push(listener)
+ },
+
+
+ removeListener : function (listenerToRemove) {
+ var eventListeners = this.listeners[ listenerToRemove.eventName ]
+
+ eventListeners && Joose.A.each(eventListeners, function (listener, index) {
+
+ if (listener == listenerToRemove) {
+
+ eventListeners.splice(index, 1)
+
+ return false
+ }
+ })
+ },
+
+
+ removeListenerByHandler : function (eventName, func, scope) {
+ var eventListeners = this.listeners[ eventName ]
+
+ eventListeners && Joose.A.each(eventListeners, function (listener, index) {
+
+ if (listener.func == func && listener.scope == scope) {
+
+ eventListeners.splice(index, 1)
+
+ return false
+ }
+ })
+ },
+
+
+ getSingleChannel : function (name, doNotCreate) {
+ var channels = this.channels
+
+ if (channels[ name ]) return channels[ name ]
+
+ if (doNotCreate) return null
+
+ return channels[ name ] = new JooseX.Observable.Channel()
+ },
+
+
+ // (!) segments array will be destroyed in this method
+ getChannel : function (segments, doNotCreate) {
+ if (!segments.length) return this
+
+ var next = this.getSingleChannel(segments.shift(), doNotCreate)
+
+ if (doNotCreate && !next) return null
+
+ return next.getChannel(segments, doNotCreate)
+ }
+ }
+})
+
+
+;
+Role('JooseX.Observable', {
+
+ /*PKGVERSION*/VERSION : 0.04,
+
+// use : [
+// 'JooseX.Observable.Channel',
+// 'JooseX.Observable.Listener',
+// 'JooseX.Observable.Event'
+// ],
+
+
+// trait : 'JooseX.Observable.Meta',
+
+
+ has : {
+ rootChannel : {
+ is : 'rw',
+ init : function () { return new JooseX.Observable.Channel() }
+ },
+
+ suspendCounter : 0
+ },
+
+
+ methods : {
+
+ getBubbleTarget : function () {
+ },
+
+
+ parseEventPath : function (path) {
+ var channels = path.split('/')
+ var eventName = channels.pop()
+
+ if (channels.length && !channels[ 0 ]) channels.shift()
+
+ return {
+ channels : channels,
+ eventName : eventName
+ }
+ },
+
+
+ on : function (path, func, scope, options) {
+ if (!func) throw "Not valid listener function provided when subsribing on event: " + path
+
+ var parsed = this.parseEventPath(path)
+ var channel = this.getRootChannel().getChannel(parsed.channels)
+
+ var listener = new JooseX.Observable.Listener(Joose.O.extend(options || {}, {
+ channel : channel,
+ eventName : parsed.eventName,
+
+ func : func,
+ scope : scope
+ }))
+
+ channel.addListener(listener)
+
+ return listener
+ },
+
+
+ un : function (path, func, scope) {
+
+ if (path instanceof JooseX.Observable.Listener) {
+
+ path.remove()
+
+ return
+ }
+
+ var parsed = this.parseEventPath(path)
+ var channel = this.getRootChannel().getChannel(parsed.channels, true)
+
+ if (channel) channel.removeListenerByHandler(parsed.eventName, func, scope)
+ },
+
+
+ emit : function () {
+ return this.fireEvent.apply(this, arguments)
+ },
+
+
+ fireEvent : function (path) {
+ if (this.suspendCounter) return
+
+ var args = Array.prototype.slice.call(arguments, 1)
+
+ var event = new JooseX.Observable.Event({
+ name : path,
+ args : args,
+
+ source : this
+ })
+
+ return this.propagateEvent(event, path, args)
+ },
+
+
+ propagateEvent : function (event, path, args) {
+ if (this.suspendCounter) return
+
+ var parsed = this.parseEventPath(path)
+ var eventName = parsed.eventName
+
+ if (!eventName == '*' || eventName == '**') throw new Error("Can't fire an empty event or event with `*`, `**` names ")
+
+ var activators = []
+
+ this.getRootChannel().getListenersFor(parsed.channels, eventName, activators)
+
+ var res = true
+
+ event.current = this
+
+ if (activators.length) Joose.A.each(activators, function (activator) {
+ event.splat = activator.splat
+
+ res = activator.listener.activate(event, args) !== false && res
+ })
+
+ if (event.bubbling) {
+
+ var further = this.getBubbleTarget()
+
+ if (further) res = further.propagateEvent(event, path, args) !== false && res
+ }
+
+ return res
+ },
+
+
+ hasListenerFor : function (path) {
+ var parsed = this.parseEventPath(path)
+
+ return this.getRootChannel().hasListenerFor(parsed.channels, parsed.eventName)
+ },
+
+
+ purgeListeners : function () {
+ this.rootChannel = new JooseX.Observable.Channel()
+ },
+
+
+ suspendEvents : function () {
+ this.suspendCounter++
+ },
+
+
+ resumeEvents : function () {
+ this.suspendCounter--
+
+ if (this.suspendCounter < 0) this.suspendCounter = 0
+ }
+ }
+});
+;
+/*
+ http://www.JSON.org/json2.js
+ 2011-02-23
+
+ Public Domain.
+
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+ See http://www.JSON.org/js.html
+
+
+ This code should be minified before deployment.
+ See http://javascript.crockford.com/jsmin.html
+
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+ NOT CONTROL.
+
+
+ This file creates a global JSON object containing two methods: stringify
+ and parse.
+
+ JSON.stringify(value, replacer, space)
+ value any JavaScript value, usually an object or array.
+
+ replacer an optional parameter that determines how object
+ values are stringified for objects. It can be a
+ function or an array of strings.
+
+ space an optional parameter that specifies the indentation
+ of nested structures. If it is omitted, the text will
+ be packed without extra whitespace. If it is a number,
+ it will specify the number of spaces to indent at each
+ level. If it is a string (such as '\t' or ' '),
+ it contains the characters used to indent at each level.
+
+ This method produces a JSON text from a JavaScript value.
+
+ When an object value is found, if the object contains a toJSON
+ method, its toJSON method will be called and the result will be
+ stringified. A toJSON method does not serialize: it returns the
+ value represented by the name/value pair that should be serialized,
+ or undefined if nothing should be serialized. The toJSON method
+ will be passed the key associated with the value, and this will be
+ bound to the value
+
+ For example, this would serialize Dates as ISO strings.
+
+ Date.prototype.toJSON = function (key) {
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ return this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z';
+ };
+
+ You can provide an optional replacer method. It will be passed the
+ key and value of each member, with this bound to the containing
+ object. The value that is returned from your method will be
+ serialized. If your method returns undefined, then the member will
+ be excluded from the serialization.
+
+ If the replacer parameter is an array of strings, then it will be
+ used to select the members to be serialized. It filters the results
+ such that only members with keys listed in the replacer array are
+ stringified.
+
+ Values that do not have JSON representations, such as undefined or
+ functions, will not be serialized. Such values in objects will be
+ dropped; in arrays they will be replaced with null. You can use
+ a replacer function to replace those with JSON values.
+ JSON.stringify(undefined) returns undefined.
+
+ The optional space parameter produces a stringification of the
+ value that is filled with line breaks and indentation to make it
+ easier to read.
+
+ If the space parameter is a non-empty string, then that string will
+ be used for indentation. If the space parameter is a number, then
+ the indentation will be that many spaces.
+
+ Example:
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
+ // text is '["e",{"pluribus":"unum"}]'
+
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+ text = JSON.stringify([new Date()], function (key, value) {
+ return this[key] instanceof Date ?
+ 'Date(' + this[key] + ')' : value;
+ });
+ // text is '["Date(---current time---)"]'
+
+
+ JSON.parse(text, reviver)
+ This method parses a JSON text to produce an object or array.
+ It can throw a SyntaxError exception.
+
+ The optional reviver parameter is a function that can filter and
+ transform the results. It receives each of the keys and values,
+ and its return value is used instead of the original value.
+ If it returns what it received, then the structure is not modified.
+ If it returns undefined then the member is deleted.
+
+ Example:
+
+ // Parse the text. Values that look like ISO date strings will
+ // be converted to Date objects.
+
+ myData = JSON.parse(text, function (key, value) {
+ var a;
+ if (typeof value === 'string') {
+ a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+ if (a) {
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+ +a[5], +a[6]));
+ }
+ }
+ return value;
+ });
+
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+ var d;
+ if (typeof value === 'string' &&
+ value.slice(0, 5) === 'Date(' &&
+ value.slice(-1) === ')') {
+ d = new Date(value.slice(5, -1));
+ if (d) {
+ return d;
+ }
+ }
+ return value;
+ });
+
+
+ This is a reference implementation. You are free to copy, modify, or
+ redistribute.
+*/
+
+/*jslint evil: true, strict: false, regexp: false */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
+ test, toJSON, toString, valueOf
+*/
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+var JSON;
+if (!JSON) {
+ JSON = {};
+}
+
+(function () {
+ "use strict";
+
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ if (typeof Date.prototype.toJSON !== 'function') {
+
+ Date.prototype.toJSON = function (key) {
+
+ return isFinite(this.valueOf()) ?
+ this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z' : null;
+ };
+
+ String.prototype.toJSON =
+ Number.prototype.toJSON =
+ Boolean.prototype.toJSON = function (key) {
+ return this.valueOf();
+ };
+ }
+
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ gap,
+ indent,
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ },
+ rep;
+
+
+ function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 'string' ? c :
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' : '"' + string + '"';
+ }
+
+
+ function str(key, holder) {
+
+// Produce a string from holder[key].
+
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ mind = gap,
+ partial,
+ value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+ if (value && typeof value === 'object' &&
+ typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
+ }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+// What happens next depends on the value's type.
+
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+ return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+ case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+ if (!value) {
+ return 'null';
+ }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+ gap += indent;
+ partial = [];
+
+// Is the value an array?
+
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+ v = partial.length === 0 ? '[]' : gap ?
+ '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
+ '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+ if (rep && typeof rep === 'object') {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ if (typeof rep[i] === 'string') {
+ k = rep[i];
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+ v = partial.length === 0 ? '{}' : gap ?
+ '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
+ '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+ }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+ if (typeof JSON.stringify !== 'function') {
+ JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+ var i;
+ gap = '';
+ indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+ } else if (typeof space === 'string') {
+ indent = space;
+ }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function' &&
+ (typeof replacer !== 'object' ||
+ typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+ return str('', {'': value});
+ };
+ }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+ if (typeof JSON.parse !== 'function') {
+ JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+ var j;
+
+ function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+ var k, v, value = holder[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = walk(value, k);
+ if (v !== undefined) {
+ value[k] = v;
+ } else {
+ delete value[k];
+ }
+ }
+ }
+ }
+ return reviver.call(holder, key, value);
+ }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+ text = String(text);
+ cx.lastIndex = 0;
+ if (cx.test(text)) {
+ text = text.replace(cx, function (a) {
+ return '\\u' +
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+ if (/^[\],:{}\s]*$/
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+ j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+ return typeof reviver === 'function' ?
+ walk({'': j}, '') : j;
+ }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+ throw new SyntaxError('JSON.parse');
+ };
+ }
+}());
+;
+!function () {
+
+ var REF = 1
+
+ Class('Data.Visitor2', {
+
+ has : {
+ seenPlaceholder : {
+ init : {}
+ },
+
+ outOfDepthPlaceholder : {
+ init : {}
+ },
+
+ seen : Joose.I.Object,
+
+ maxDepth : null
+ },
+
+ methods : {
+
+ getClassNameFor : function (object) {
+ if (Joose.O.isInstance(object)) return object.meta.name
+
+ return Object.prototype.toString.call(object).replace(/^\[object /, '').replace(/\]$/, '')
+ },
+
+
+ getRefAdr : function () {
+ return REF++
+ },
+
+
+ assignRefAdrTo : function (object) {
+ if (!object.__REFADR__)
+ if (Object.defineProperty)
+ Object.defineProperty(object, '__REFADR__', { value : REF++ })
+ else
+ object.__REFADR__ = REF++
+
+ return object.__REFADR__
+ },
+
+
+ isSeen : function (object) {
+ return object.__REFADR__ && this.seen.hasOwnProperty(object.__REFADR__)
+ },
+
+
+ markSeenAs : function (object, result) {
+ return this.seen[ object.__REFADR__ ] = result
+ },
+
+
+ hasSeenResultFor : function (object) {
+ var ref = object.__REFADR__
+
+ return this.seen.hasOwnProperty(ref) && this.seen[ ref ] != this.seenPlaceholder
+ },
+
+
+ visit : function (value, depth) {
+ // will be false for NaN values
+ if (depth > this.maxDepth)
+ return this.visitOutOfDepthValue(value, depth + 1)
+ else
+ if (Object(value) === value)
+ if (this.isSeen(value))
+ return this.visitSeen(value, depth + 1)
+ else
+ return this.visitNotSeen(value, depth + 1)
+ else
+ return this.visitValue(value, depth + 1)
+ },
+
+
+ visitOutOfDepthValue : function (value, depth) {
+ return this.outOfDepthPlaceholder
+ },
+
+
+ visitValue : function (value, depth) {
+ return value
+ },
+
+
+ visitSeen : function (value, depth) {
+ return this.seen[ value.__REFADR__ ]
+ },
+
+
+ getInitialSeenMarker : function (object, depth) {
+ return this.seenPlaceholder
+ },
+
+
+ visitNotSeen : function (object, depth) {
+ this.assignRefAdrTo(object)
+
+ this.markSeenAs(object, this.getInitialSeenMarker(object, depth))
+
+
+ if (Joose.O.isInstance(object)) return this.markSeenAs(object, this.visitJooseInstance(object, depth))
+
+
+ var methodName = 'visit' + this.getClassNameFor(object)
+
+ if (!this.meta.hasMethod(methodName)) methodName = 'visitObject'
+
+ return this.markSeenAs(object, this[ methodName ](object, depth))
+ },
+
+
+ visitArray : function (array, depth) {
+ Joose.A.each(array, function (value, index) {
+
+ this.visitArrayEntry(value, index, array, depth)
+
+ }, this)
+
+ return array
+ },
+
+
+ visitArrayEntry : function (entry, index, array, depth) {
+ return this.visit(entry, depth)
+ },
+
+
+ visitObject : function (object, depth) {
+
+ Joose.O.eachOwn(object, function (value, key) {
+
+ if (key != '__REFADR__') {
+ this.visitObjectKey(key, value, object, depth)
+ this.visitObjectValue(value, key, object, depth)
+ }
+
+ }, this)
+
+ return object
+ },
+
+
+ visitJooseInstance : function (value, depth) {
+ return this.visitObject(value, depth)
+ },
+
+
+ visitObjectKey : function (key, value, object, depth) {
+ return this.visitValue(key, depth)
+ },
+
+
+ visitObjectValue : function (value, key, object, depth) {
+ return this.visit(value, depth)
+ }
+ },
+
+
+ my : {
+
+ has : {
+ HOST : null
+ },
+
+
+ methods : {
+
+ visit : function (value, maxDepth) {
+ var visitor = new this.HOST({
+ maxDepth : maxDepth || Infinity
+ })
+
+ return visitor.visit(value, 0)
+ }
+ }
+ }
+ })
+
+}()
+
+
+;
+;
+;
+Class('Siesta.Util.Serializer', {
+
+ isa : Data.Visitor2,
+
+ has : {
+ result : Joose.I.Array,
+ manualEnum : function () {
+ for (var i in { toString : 1 }) return false
+
+ return true
+ }
+ },
+
+
+ methods : {
+
+ assignRefAdrTo : function (object) {
+ try {
+ return this.SUPER(object)
+ } catch (e) {
+ if (!object.__REFADR__) object.__REFADR__ = this.getRefAdr()
+ }
+
+ return object.__REFADR__
+ },
+
+
+ write : function (str) {
+ this.result.push(str)
+ },
+
+
+ visitOutOfDepthValue : function (value, depth) {
+ this.write('...')
+ },
+
+
+ visitValue : function (value) {
+ if (value == null)
+ // `null` and `undefined`
+ this.write(value + '')
+ else
+ this.write(typeof value == 'string' ? '"' + value.replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"' : value + '')
+ },
+
+
+ visitObjectKey : function (key, value, object) {
+ this.write('"' + key + '": ')
+ },
+
+
+ getClassNameFor : function (object) {
+ if (object.nodeType != null && object.nodeName != null && object.tagName) return 'DOMElement'
+
+ // trying to detect and not dive into global window
+ if (object.document != null && object.location != null && object.location.href != null) return 'Window'
+
+ return this.SUPER(object)
+ },
+
+
+ visitSeen : function (value, depth) {
+ this.write('[Circular]')
+ },
+
+
+ visitRegExp : function (value, depth) {
+ this.write(value + '')
+ },
+
+
+ visitFunction : function (value, depth) {
+ this.write('function ' + (value.name || '') + '() { ... }')
+ },
+
+
+ visitDate : function (value, depth) {
+ this.write('"' + value + '"')
+ },
+
+
+ // safer alternative to parent's implementation of `visitObject` - some host objects has no "hasOwnProperty" method
+ visitObject : function (object, depth) {
+ for (var key in object) {
+ if (key != '__REFADR__' && (!object.hasOwnProperty || object.hasOwnProperty(key))) {
+ var value = object[ key ]
+
+ this.visitObjectKey(key, value, object, depth)
+ this.visitObjectValue(value, key, object, depth)
+ }
+ }
+
+ var me = this
+
+ if (this.manualEnum)
+ Joose.A.each([ 'hasOwnProperty', 'valueOf', 'toString', 'constructor' ], function (key) {
+ if (object.hasOwnProperty && object.hasOwnProperty(key)) {
+ var value = object[ key ]
+
+ me.visitObjectKey(key, value, object, depth)
+ me.visitObjectValue(value, key, object, depth)
+ }
+ })
+
+ return object
+ },
+
+
+ visitJooseInstance : function (value, depth) {
+ if (value.meta.hasMethod('toString')) {
+ this.write(value.toString())
+
+ return value
+ }
+
+ return this.SUPERARG(arguments)
+ },
+
+
+ visitDOMElement : function (object, depth) {
+ var output = '<' + object.tagName
+
+ if (object.id) output += ' id="' + object.id + '"'
+ if (object.className) output += ' class="' + object.className + '"'
+
+ this.write(output + '>')
+ },
+
+
+ visitDOMStringMap : function () {
+ this.write('[DOMStringMap]')
+ },
+
+
+ // the Object.prototype.toString.call(window) for FF
+ visitWindow : function () {
+ this.write('[window]')
+ },
+
+
+ // window.location type in FF
+ visitLocation : function () {
+ this.write('[window.location]')
+ }
+ },
+
+
+ before : {
+ visitObject : function () {
+ this.write('{')
+ },
+
+
+ visitArray : function () {
+ this.write('[')
+ }
+ },
+
+
+ after : {
+ visitObject : function () {
+ var result = this.result
+
+ if (result[ result.length - 1 ] == ', ') result.pop()
+
+ this.write('}')
+ },
+
+
+ visitArray : function () {
+ var result = this.result
+
+ if (result[ result.length - 1 ] == ', ') result.pop()
+
+ this.write(']')
+ },
+
+
+ visitObjectValue : function () {
+ this.write(', ')
+ },
+
+
+ visitArrayEntry : function () {
+ this.write(', ')
+ }
+ },
+
+
+ my : {
+
+ has : {
+ HOST : null
+ },
+
+
+ methods : {
+
+ stringify : function (value, maxDepth) {
+ var visitor = new this.HOST({
+ maxDepth : maxDepth || 4
+ })
+
+ visitor.visit(value, 0)
+
+ return visitor.result.join('')
+ }
+ }
+ }
+})
+;
+Role('Siesta.Util.Role.CanFormatStrings', {
+
+ methods : {
+
+ formatString: function (string, data) {
+ var match
+ var variables = []
+ var isRaw = []
+ var regexp = /\{(\!)?((?:\w|-|_)+?)\}/g
+
+ while (match = regexp.exec(string)) {
+ isRaw.push(match[ 1 ])
+ variables.push(match[ 2 ])
+ }
+
+ var result = string
+
+ Joose.A.each(variables, function (variable, index) {
+ var varIsRaw = isRaw[ index ]
+
+ result = result.replace(
+ new RegExp('\\{' + (varIsRaw ? '!' : '') + variable + '\\}', 'g'),
+ data.hasOwnProperty(variable) ?
+ varIsRaw ? data[ variable ] + '' : Siesta.Util.Serializer.stringify(data[ variable ])
+ :
+ ''
+ )
+ })
+
+ return result
+ }
+ }
+})
+;
+Class('Siesta.Util.Queue', {
+
+ has : {
+ // array of Objects, each containing arbitrary data about queue step. Possibly keys:
+ // `processor` - an individual processor function for this step
+ // can also be provided for whole queue
+ // will receive the: (stepData, index, queue)
+ // `isAsync` - when provided, the `next` function will be also embedded,
+ // which should be called manually
+ // `interval` - the delay after step (except for asynchronous)
+ steps : Joose.I.Array,
+
+ interval : 100,
+ callbackDelay : 0,
+ // setTimeout
+ deferer : { required : true },
+ // clearTimeout - only required when "abort" is planned / possible
+ deferClearer : null,
+
+ processor : null,
+ processorScope : null,
+
+ currentTimeout : null,
+ callback : null,
+ scope : null,
+ isAborted : false,
+
+ observeTest : null,
+
+ currentDelayStepId : null
+ },
+
+
+ methods : {
+
+ // step is an object with
+ // {
+ // processor : func,
+ // processorScope : obj,
+ // next : func (in case of async step, will be populated by queue)
+ // }
+
+ addStep : function (stepData) {
+ this.addSyncStep(stepData)
+ },
+
+
+ addSyncStep : function (stepData) {
+ this.steps.push(stepData)
+ },
+
+
+ addAsyncStep : function (stepData) {
+ stepData.isAsync = true
+
+ this.steps.push(stepData)
+ },
+
+ addDelayStep : function (delayMs) {
+ var origSetTimeout = this.deferer;
+ var me = this;
+
+ this.addAsyncStep({
+ processor : function(data) {
+ me.currentDelayStepId = origSetTimeout(data.next, delayMs || 500);
+ }
+ });
+ },
+
+
+ run : function (callback, scope) {
+ this.callback = callback
+ this.scope = scope
+
+ // abort the queue, if the provided test instance has finalized (probably because of exception)
+ this.observeTest && this.observeTest.on('testfinalize', function () { this.abort(true) }, this, { single : true })
+
+ this.doSteps(this.steps.slice(), callback, scope)
+ },
+
+
+ abort : function (ignoreCallback) {
+ this.isAborted = true
+
+ var deferClearer = this.deferClearer
+
+ if (!deferClearer) throw "Need `deferClearer` to be able to `abort` the queue"
+
+ deferClearer(this.currentDelayStepId);
+ deferClearer(this.currentTimeout)
+
+ if (!ignoreCallback) this.callback.call(this.scope || this)
+ },
+
+
+ doSteps : function (steps, callback, scope) {
+ this.currentTimeout = null
+
+ var me = this
+ var deferer = this.deferer
+ var step = steps.shift()
+
+ if (this.isAborted) return
+
+ if (step) {
+ var processor = step.processor || this.processor
+ var processorScope = step.processorScope || this.processorScope
+
+ var index = this.steps.length - steps.length - 1
+
+ if (!processor) throw new Error("No process function found for step: " + index)
+
+ if (step.isAsync) {
+ var next = step.next = function () {
+ me.doSteps(steps, callback, scope)
+ }
+
+ // processor should call `next` to continue
+ processor.call(processorScope || me, step, index, this, next)
+ } else {
+
+ processor.call(processorScope || me, step, index, this)
+
+ if (this.isAborted) return
+
+ var interval = step.interval || me.interval
+
+ if (interval)
+ this.currentTimeout = deferer(function () {
+ me.doSteps(steps, callback, scope)
+ }, interval)
+ else
+ me.doSteps(steps, callback, scope)
+ }
+
+
+ } else {
+ if (callback)
+ if (this.callbackDelay)
+ deferer(function () {
+ if (!me.isAborted) callback.call(scope || this)
+ }, this.callbackDelay)
+ else
+ callback.call(scope || this)
+ }
+ }
+ }
+})
+;
+Class('Siesta.Util.XMLNode', {
+
+ has : {
+ children : Joose.I.Array,
+
+ tag : { required : true },
+ attributes : Joose.I.Object,
+
+ textContent : null,
+
+ escapeTable : {
+
+ init : {
+ '&' : '&',
+ '<' : '<',
+ '>' : '>',
+ '"' : '"'
+ }
+ }
+
+ },
+
+
+ methods : {
+
+ escapeXml : function (s) {
+ var me = this
+
+ return typeof s != 'string' ? s : s.replace(/[&<>"]/g, function (match) {
+ return me.escapeTable[ match ]
+ })
+ },
+
+
+ toString : function () {
+ var me = this
+ var childrenContent = []
+
+ Joose.A.each(this.children, function (child) {
+ childrenContent.push(child.toString())
+ })
+
+ var attributesContent = []
+
+ Joose.O.each(this.attributes, function (value, name) {
+ attributesContent.push(name + '="' + me.escapeXml(value) + '"')
+ })
+
+ // to have predictable order of attributes in tests
+ attributesContent.sort()
+
+ attributesContent.unshift(this.tag)
+
+
+ return '<' + attributesContent.join(' ') + '>' + (this.textContent != null ? this.escapeXml(this.textContent) : '') + childrenContent.join('') + '' + this.tag + '>'
+ },
+
+
+ appendChild : function (child) {
+ if (child instanceof Siesta.Util.XMLNode)
+ child.parent = this
+ else
+ child = new Siesta.Util.XMLNode(Joose.O.extend(child, { parent : this }))
+
+ this.children.push(child)
+
+ return child
+ },
+
+
+ setAttribute : function (name, value) {
+ this.attributes[ name ] = value
+ }
+ }
+})
+;
+Class('Siesta.Util.Rect', {
+
+ has : {
+ left : null,
+ top : null,
+ width : null,
+ height : null,
+
+ right : null,
+ bottom : null
+ },
+
+
+ methods : {
+
+ initialize : function () {
+ var left = this.left
+ var width = this.width
+ var right = this.right
+
+ if (right == null && left != null && width != null) this.right = left + width - 1
+
+ if (width == null && left != null && right != null) this.width = right - left + 1
+
+ var top = this.top
+ var height = this.height
+ var bottom = this.bottom
+
+ if (bottom == null && top != null && height != null) this.bottom = top + height - 1
+
+ if (height == null && top != null && bottom != null) this.height = bottom - top + 1
+ },
+
+
+ isEmpty : function () {
+ return this.left == null
+ },
+
+
+ intersect : function (rect) {
+ if (
+ rect.isEmpty() || this.isEmpty()
+ ||
+ rect.left > this.right || rect.right < this.left
+ ||
+ rect.top > this.bottom || rect.bottom < this.top
+ ) return this.my.getEmpty()
+
+ return new this.constructor({
+ left : Math.max(this.left, rect.left),
+ right : Math.min(this.right, rect.right),
+ top : Math.max(this.top, rect.top),
+ bottom : Math.min(this.bottom, rect.bottom)
+ })
+ },
+
+
+ contains : function (left, top) {
+ return this.left <= left && left <= this.right
+ &&
+ this.top <= top && top <= this.bottom
+ },
+
+
+ cropLeftRight : function (rect) {
+ return this.intersect(new this.constructor({
+ left : rect.left,
+ right : rect.right,
+ top : this.top,
+ bottom : this.bottom
+ }))
+ },
+
+
+ cropTopBottom : function (rect) {
+ return this.intersect(new this.constructor({
+ left : this.left,
+ right : this.right,
+ top : rect.top,
+ bottom : rect.bottom
+ }))
+ },
+
+
+ equalsTo : function (rect) {
+ return this.left == rect.left && this.right == rect.right && this.top == rect.top && this.bottom == rect.bottom
+ }
+ },
+
+
+ // static methods/props
+ my : {
+ has : {
+ HOST : null
+ },
+
+ methods : {
+
+ getEmpty : function () {
+ return new this.HOST()
+ }
+ }
+ }
+})
+;
+Class('Siesta.Content.Resource', {
+
+ has : {
+ url : null,
+
+ content : null
+ },
+
+
+ methods : {
+
+ asHTML : function () {
+ throw "Abstract method called"
+ },
+
+
+ asDescriptor : function () {
+ throw "Abstract method called"
+ },
+
+
+ // todo should check same-origin
+ canCache : function () {
+ }
+
+ }
+
+})
+//eof Siesta.Result
+
+;
+Class('Siesta.Content.Resource.CSS', {
+
+ isa : Siesta.Content.Resource,
+
+ has : {
+ },
+
+
+ methods : {
+
+ asHTML : function () {
+ },
+
+
+ asDescriptor : function () {
+ var res = {
+ type : 'css'
+ }
+
+ if (this.url) res.url = this.url
+ if (this.content) res.content = this.content
+
+ return res
+ }
+ }
+
+})
+//eof Siesta.Result
+
+;
+Class('Siesta.Content.Resource.JavaScript', {
+
+ isa : Siesta.Content.Resource,
+
+ has : {
+ instrument : false
+ },
+
+
+ methods : {
+
+ asHTML : function () {
+ },
+
+
+ asDescriptor : function () {
+ var res = {
+ type : 'js'
+ }
+
+ if (this.url) res.url = this.url
+ if (this.content) res.content = this.content
+
+ return res
+ }
+ }
+
+})
+//eof Siesta.Result
+
+;
+Class('Siesta.Content.Preset', {
+
+ has : {
+ preload : Joose.I.Array,
+
+ resources : Joose.I.Array
+ },
+
+
+ methods : {
+
+ initialize : function () {
+ var me = this
+
+ Joose.A.each(this.preload, function (preloadDesc) {
+
+ me.addResource(preloadDesc)
+ })
+ },
+
+
+ isCSS : function (url) {
+ return /\.css(\?.*)?$/i.test(url)
+ },
+
+
+ getResourceFromDescriptor : function (desc) {
+ var constructor, config
+
+ var CSS
+
+ if (typeof desc == 'string') {
+ constructor = this.isCSS(desc) ? Siesta.Content.Resource.CSS : Siesta.Content.Resource.JavaScript
+
+ config = { url : desc }
+ } else if (desc.text) {
+ constructor = Siesta.Content.Resource.JavaScript
+ config = { content : desc.text }
+
+ } else {
+ if (!desc.url && !desc.content) throw "Incorrect preload descriptor:" + desc
+
+ constructor = desc.type && desc.type == 'css' || this.isCSS(desc.url) ? Siesta.Content.Resource.CSS : Siesta.Content.Resource.JavaScript
+
+ config = {}
+
+ if (desc.url) config.url = desc.url
+ if (desc.content) config.content = desc.content
+ if (desc.instrument) config.instrument = desc.instrument
+ }
+
+ return new constructor(config)
+ },
+
+
+ addResource : function (desc) {
+ var resource = (desc instanceof Siesta.Content.Resource) && desc || this.getResourceFromDescriptor(desc)
+
+ this.resources.push(resource)
+
+ return resource
+ },
+
+
+ eachResource : function (func, scope) {
+ return Joose.A.each(this.resources, func, scope || this)
+ },
+
+
+ // deprecated - seems preset doesn't need to know about scope providers
+ prepareScope : function (scopeProvider, contentManager) {
+
+ this.eachResource(function (resource) {
+
+ if (contentManager.hasContentOf(resource))
+ scopeProvider.addPreload({
+ type : (resource instanceof Siesta.Content.Resource.CSS) ? 'css' : 'js',
+ content : contentManager.getContentOf(resource)
+ })
+ else
+ scopeProvider.addPreload(resource.asDescriptor())
+ })
+ }
+ }
+
+})
+
+;
+Class('Siesta.Content.Manager', {
+
+ has : {
+ disabled : false,
+
+ presets : {
+ required : true
+ },
+
+ urls : Joose.I.Object,
+
+ maxLoads : 5,
+
+ harness : null
+ },
+
+
+ methods : {
+
+ cache : function (callback, errback, ignoreErrors) {
+ if (this.disabled) {
+ callback && callback()
+
+ return
+ }
+
+ var urls = this.urls
+ var me = this
+
+ Joose.A.each(this.presets, function (preset) {
+ preset.eachResource(function (resource) {
+ if (resource.url) urls[ resource.url ] = null
+ })
+ })
+
+ var loadCount = 0
+ var errorCount = 0
+
+ var urlsArray = []
+
+ Joose.O.each(urls, function (value, url) {
+ // if some content has been already provided - skip it from caching
+ if (!me.hasContentOf(url)) urlsArray.push(url)
+ })
+
+ var total = urlsArray.length
+
+ if (total) {
+
+ var loadSingle = function () {
+ if (!urlsArray.length) return
+
+ var url = urlsArray.shift()
+
+ me.load(url, function (content) {
+ if (errorCount) return
+
+ urls[ url ] = content
+
+ if (++loadCount == total)
+ callback && callback()
+ else
+ loadSingle()
+
+ }, ignoreErrors ? function () {
+
+ if (++loadCount == total)
+ callback && callback()
+ else
+ loadSingle()
+
+ } : function () {
+ errorCount++
+
+ errback && errback(url)
+ })
+ }
+
+ // running only `maxLoads` "loading threads" at the same time
+ for (var i = 0; i < this.maxLoads; i++) loadSingle()
+
+ } else
+ callback && callback()
+ },
+
+
+ load : function (url, callback, errback) {
+ throw "abstract method `load` called"
+ },
+
+
+ addContent : function (url, content) {
+ this.urls[ url ] = content
+ },
+
+
+ hasContentOf : function (url) {
+ if (url instanceof Siesta.Content.Resource) url = url.url
+
+ return typeof this.urls[ url ] == 'string'
+ },
+
+
+ getContentOf : function (url) {
+ if (url instanceof Siesta.Content.Resource) url = url.url
+
+ return this.urls[ url ]
+ }
+ }
+})
+
+;
+;
+Class('Siesta', {
+ /*PKGVERSION*/VERSION : '2.0.4',
+
+ // "my" should been named "static"
+ my : {
+
+ has : {
+ config : null,
+ activeHarness : null
+ },
+
+ methods : {
+
+ getConfigForTestScript : function (text) {
+ try {
+ eval(text)
+
+ return this.config
+ } catch (e) {
+ return null
+ }
+ },
+
+
+ StartTest : function (arg1, arg2) {
+ if (typeof arg1 == 'object')
+ this.config = arg1
+ else if (typeof arg2 == 'object')
+ this.config = arg2
+ else
+ this.config = null
+ }
+ }
+ }
+})
+
+// fake StartTest function to extract test configs
+if (typeof StartTest == 'undefined') StartTest = Siesta.StartTest
+if (typeof startTest == 'undefined') startTest = Siesta.StartTest
+if (typeof describe == 'undefined') describe = Siesta.StartTest;
+;(function () {
+
+var ID = 0
+
+Class('Siesta.Result', {
+
+ has : {
+ description : null,
+
+ children : Joose.I.Array,
+
+ length : 0,
+
+ id : function () {
+ return ++ID
+ },
+
+ parent : null
+ },
+
+
+ methods : {
+
+ itemAt : function (i) {
+ return this.children[ i ]
+ },
+
+
+ push : function (result) {
+ this.children.push(result)
+
+ result.parent = this
+
+ this.length = this.children.length
+ },
+
+
+ each : function (func, scope) {
+ var children = this.children
+
+ if (func.call(scope || this, this) === false) return false
+
+ for (var i = 0; i < children.length; i++)
+ if (children[ i ].each(func, scope) === false) return false
+ },
+
+
+ eachChild : function (func, scope) {
+ var children = this.children
+
+ for (var i = 0; i < children.length; i++)
+ if (func.call(scope, children[ i ]) === false) return false
+ },
+
+
+ toString : function () {
+ return this.description
+ },
+
+
+ toJSON : function () {
+ return {
+ type : this.meta.name,
+ description : this.description
+ }
+ },
+
+
+ findChildById : function (id) {
+ var child
+
+ this.each(function (node) {
+ if (node.id == id) { child = node; return false }
+ })
+
+ return child
+ }
+ },
+
+ // used for self-testing when we need different ids for outer context and context being tested
+ my : {
+ methods : {
+ seedID : function (value) {
+ ID = value
+ }
+ }
+ }
+
+})
+
+
+})();
+Class('Siesta.Result.Diagnostic', {
+
+ isa : Siesta.Result,
+
+ has : {
+ isWarning : false
+ },
+
+ methods : {
+
+ toString : function () {
+ return '# ' + this.description
+ },
+
+
+ toJSON : function () {
+ var info = {
+ type : this.meta.name,
+ description : this.description
+ }
+
+ if (this.isWarning) info.isWarning = true
+
+ return info
+ }
+ }
+});
+
+;
+Class('Siesta.Result.Summary', {
+
+ isa : Siesta.Result,
+
+ has : {
+ isFailed : false
+ },
+
+ methods : {
+
+ // summary should belong only to the top level Siesta.Result.SubTest instance
+ getTest : function () {
+ return this.parent.test
+ },
+
+
+ toString : function () {
+
+ }
+ }
+});
+
+;
+Class('Siesta.Result.Assertion', {
+
+ isa : Siesta.Result,
+
+
+ has : {
+ name : null,
+
+ passed : null,
+
+ annotation : null,
+
+ index : null,
+ // stored as string
+ sourceLine : null,
+
+ isSkipped : false,
+ isTodo : false,
+
+ isException : false,
+ exceptionType : null,
+
+ isWaitFor : false,
+ completed : false // for waitFor assertions
+ },
+
+
+ methods : {
+
+ isPassed : function (raw) {
+ if (raw) return this.passed
+
+ if (this.isTodo) return true
+
+ if (this.isWaitFor && !this.completed) return true
+
+ return this.passed
+ },
+
+
+ toString : function () {
+ var text = (this.isTodo ? 'TODO: ' : '') + (this.passed ? 'ok ' : 'fail ') + this.index + ' - ' + this.description
+
+ if (this.annotation) text += '\n' + this.annotation
+
+ return text
+ },
+
+
+ toJSON : function () {
+ var me = this
+
+ var info = {
+ type : this.meta.name,
+ passed : this.passed,
+ description : this.description || 'No description'
+ }
+
+ // copy if true
+ Joose.A.each([ 'isTodo', 'annotation', 'isWaitFor', 'isException', 'sourceLine', 'name' ], function (name) {
+ if (me[ name ]) info[ name ] = me[ name ]
+ })
+
+ if (this.isException) {
+ info.exceptionType = this.exceptionType
+ }
+
+ return info
+ }
+ }
+})
+
+;
+Class('Siesta.Result.SubTest', {
+
+ isa : Siesta.Result,
+
+
+ has : {
+ // reference to a test it belongs to
+ // SubTests result instances will be set as `results` for sub tests instances
+ test : null
+ },
+
+
+ methods : {
+
+ isWorking : function () {
+ return !this.test.isFinished()
+ },
+
+
+ toJSON : function () {
+ var test = this.test
+
+ // a flag that test instance does not belongs to the current context
+ // this only happens during self-testing
+ // if this is the case, in IE, calling any method from the test context will throw exception
+ // "can't execute script from freed context", so we avoid calling any methods on the test in such case
+ // accessing properties is ok though
+ var isCrossContext = !(test instanceof Object)
+
+ var report = {
+ type : this.meta.name,
+ name : test.name,
+
+ startDate : test.startDate,
+ endDate : test.endDate || (new Date() - 0),
+
+ passed : isCrossContext ? null : test.isPassed()
+ }
+
+ if (!test.parent) report.url = test.url
+ if (test.specType) report.bddSpecType = test.specType
+
+ var isFailed = false
+ var assertions = []
+
+ Joose.A.each(this.children, function (result) {
+ if ((result instanceof Siesta.Result.Assertion) || (result instanceof Siesta.Result.Diagnostic) || (result instanceof Siesta.Result.SubTest)) {
+ var assertion = result.toJSON()
+
+ if (!assertion.passed && !assertion.isTodo) isFailed = true
+
+ assertions.push(assertion)
+ }
+ })
+
+ report.assertions = assertions
+
+ // see a comment above
+ if (isCrossContext) {
+ report.passed = !(isFailed || test.failed || !test.endDate)
+ }
+
+ return report
+ }
+
+ }
+})
+
+;
+/**
+@class Siesta.Test.Function
+
+This is a mixin, with helper methods for testing functionality relating to DOM elements. This mixin is consumed by {@link Siesta.Test}
+
+*/
+Role('Siesta.Test.Function', {
+
+ methods : {
+ /**
+ * This assertion passes if the function is called at least one time during the test life span.
+ *
+ * @param {Function/String} fn The function itself or the name of the function on the host object (2nd argument)
+ * @param {Object} host The "owner" of the method
+ * @param {String} desc The description of the assertion.
+ */
+ isCalled : function(fn, obj, desc) {
+ this.isCalledNTimes(fn, obj, 1, desc, true);
+ },
+
+ /**
+ * This assertion passes if the function is called exactly one time during the test life span.
+ *
+ * @param {Function/String} fn The function itself or the name of the function on the host object (2nd argument)
+ * @param {Object} host The "owner" of the method
+ * @param {String} desc The description of the assertion.
+ */
+ isCalledOnce : function(fn, obj, desc) {
+ this.isCalledNTimes(fn, obj, 1, desc, false);
+ },
+
+ /**
+ * This assertion passes if the function is called exactly (n) times during the test life span.
+ *
+ * @param {Function/String} fn The function itself or the name of the function on the host object (2nd argument)
+ * @param {Object} host The "owner" of the method
+ * @param {Number} n The expected number of calls
+ * @param {String} desc The description of the assertion.
+ */
+ isCalledNTimes : function(fn, obj, n, desc, isGreaterEqual) {
+ var me = this,
+ prop = typeof fn === "string" ? fn : me.getPropertyName(obj, fn);
+
+ desc = desc ? (desc + ' ') : '';
+
+ this.on('beforetestfinalizeearly', function () {
+ if (counter === n || (isGreaterEqual && counter > n)) {
+ me.pass(desc || (prop + ' method was called exactly ' + n + ' times'));
+ } else {
+
+ me.fail(desc || prop, {
+ assertionName : 'isCalledNTimes ' + prop,
+ got : counter,
+ need : n ,
+ needDesc : ("Need " + (isGreaterEqual ? 'at least ' : 'exactly '))
+ });
+ }
+ });
+
+ var counter = 0;
+ fn = obj[prop];
+ obj[prop] = function () { counter++; return fn.apply(this, arguments); };
+ },
+
+ /**
+ * This assertion passes if the function is not called during the test life span.
+ *
+ * @param {Function/String} fn The function itself or the name of the function on the host object (2nd argument)
+ * @param {Object} host The "owner" of the method
+ * @param {Number} n The expected number of calls
+ * @param {String} desc The description of the assertion.
+ */
+ isntCalled : function(fn, obj, desc) {
+ this.isCalledNTimes(fn, obj, 0, desc);
+ },
+
+ getPropertyName : function(host, obj) {
+ for (var o in host) {
+ if (host[o] === obj) return o;
+ }
+ },
+
+ /**
+ * This assertion passes when the supplied class method is called exactly (n) times during the test life span.
+ * Under "class method" here we mean the function in the prototype. Note, that this assertion counts calls to the method in *any* class instance.
+ *
+ * The `className` parameter can be supplied as a class constructor function or as a string, representing the class
+ * name. In the latter case the `class` will be eval'ed to get a reference to the class constructor.
+ *
+ * For example:
+
+ StartTest(function (t) {
+
+ function machine(type, version) {
+ this.machineInfo = {
+ type : type,
+ version : version
+ };
+ };
+
+ machine.prototype.update = function (type, version) {
+ this.setVersion(type);
+ this.setType(version);
+ };
+
+ machine.prototype.setVersion = function (data) {
+ this.machineInfo.version = data;
+ };
+
+ machine.prototype.setType = function (data) {
+ this.machineInfo.type = data;
+ };
+
+ t.methodIsCalled("setVersion", machine, "Checking if method 'setVersion' has been called");
+ t.methodIsCalled("setType", machine, "Checking if method 'setType' has been called");
+
+ var m = new machine('rover', '0.1.2');
+
+ m.update('3.2.1', 'New Rover');
+ });
+
+ *
+ * This assertion is useful when testing for example an Ext JS class where event listeners are added during
+ * class instantiation time, which means you need to observe the prototype method before instantiation.
+ *
+ * @param {Function/String} fn The function itself or the name of the method on the class (2nd argument)
+ * @param {Function/String} className The constructor function or the name of the class that contains the method
+ * @param {Number} n The expected number of calls
+ * @param {String} desc The description of the assertion
+ */
+ methodIsCalledNTimes: function(fn, className, n, desc, isGreaterEqual){
+ var me = this,
+ counter = 0;
+
+ desc = desc ? (desc + ' ') : '';
+
+ try {
+ if (me.typeOf(className) == 'String') className = me.global.eval(className)
+ } catch (e) {
+ me.fail(desc, {
+ assertionName : 'isMethodCalled',
+ annotation : "Exception [" + e + "] caught, while evaluating the class name [" + className + "]"
+ })
+
+ return
+ }
+
+ var prototype = className.prototype;
+ var prop = typeof fn === "string" ? fn : me.getPropertyName(prototype, fn);
+
+ me.on('beforetestfinalizeearly', function () {
+ if (counter === n || (isGreaterEqual && counter > n)) {
+ me.pass(desc || (prop + ' method was called exactly ' + n + ' times'));
+ } else {
+ me.fail(desc || prop, {
+ assertionName : 'isCalledNTimes ' + prop,
+ got : counter,
+ need : n ,
+ needDesc : ("Need " + (isGreaterEqual ? 'at least ' : 'exactly '))
+ });
+ }
+ });
+
+ fn = prototype[ prop ];
+ prototype[ prop ] = function () { counter++; return fn.apply(this, arguments); };
+ },
+
+ /**
+ * This assertion passes if the class method is called at least one time during the test life span.
+ *
+ * See {@link #methodIsCalledNTimes} for more details.
+ *
+ * @param {Function/String} fn The function itself or the name of the method on the class (2nd argument)
+ * @param {Function/String} className The class constructor function or name of the class that contains the method
+ * @param {String} desc The description of the assertion.
+ */
+ methodIsCalled : function(fn, className, desc) {
+ this.methodIsCalledNTimes(fn, className, 1, desc, true);
+ },
+
+ /**
+ * This assertion passes if the class method is not called during the test life span.
+ *
+ * See {@link #methodIsCalledNTimes} for more details.
+ *
+ * @param {Function/String} fn The function itself or the name of the method on the class (2nd argument)
+ * @param {Function/String} className The class constructor function or name of the class that contains the method
+ * @param {String} desc The description of the assertion.
+ */
+ methodIsntCalled : function(fn, className, desc) {
+ this.methodIsCalledNTimes(fn, className, 0, desc);
+ }
+ }
+});
+;
+/**
+@class Siesta.Test.Date
+
+A mixin with the additinal assertions for dates. Being consumed by {@link Siesta.Test}
+
+*/
+Role('Siesta.Test.Date', {
+
+ methods : {
+
+ isDateEq: function (got, expectedDate, description) {
+ this.isDateEqual.apply(this, arguments);
+ },
+
+
+ /**
+ * This assertion passes when the 2 provided dates are equal and fails otherwise.
+ *
+ * It has a synonym: `isDateEq`
+ *
+ * @param {Date} got The 1st date to compare
+ * @param {Date} expectedDate The 2nd date to compare
+ * @param {String} [description] The description of the assertion
+ */
+ isDateEqual: function (got, expectedDate, description) {
+ if (got - expectedDate === 0) {
+ this.pass(description, {
+ descTpl : '{got} date is equal to {expectedDate}',
+ got : got,
+ expectedDate : expectedDate
+ });
+ } else {
+ this.fail(description, {
+ assertionName : 'isDateEqual',
+
+ got : got ? got.toString() : '',
+ gotDesc : 'Got',
+
+ need : expectedDate.toString()
+ });
+ }
+ }
+ }
+});
+;
+/**
+@class Siesta.Test.More
+
+A mixin with additional generic assertion methods, which can work cross-platform between browsers and NodeJS.
+Is being consumed by {@link Siesta.Test}, so all of them are available in all tests.
+
+*/
+Role('Siesta.Test.More', {
+
+ requires : [ 'isFailed', 'typeOf', 'on' ],
+
+
+ has : {
+ autoCheckGlobals : false,
+ expectedGlobals : Joose.I.Array,
+
+ disableGlobalsCheck : false,
+
+ browserGlobals : {
+ init : [
+ 'console',
+ 'getInterface',
+ 'ExtBox1',
+ '__IE_DEVTOOLBAR_CONSOLE_COMMAND_LINE',
+ 'seleniumAlert',
+ 'onload',
+ 'onerror',
+ 'StartTest',
+ 'startTest',
+ 'describe',
+ // will be reported in IE8 after overriding
+ 'setTimeout',
+ 'clearTimeout',
+ 'requestAnimationFrame',
+ 'cancelAnimationFrame',
+ '__coverage__',
+ /__cov_\d+/
+ ]
+ },
+
+ /**
+ * @cfg {Number} waitForTimeout Default timeout for `waitFor` (in milliseconds). Default value is 10000.
+ */
+ waitForTimeout : 10000,
+
+ waitForPollInterval : 100
+ },
+
+
+ methods : {
+
+ /**
+ * This assertion passes, when the comparison of 1st with 2nd, using `>` operator will return `true` and fails otherwise.
+ *
+ * @param {Number/Date} value1 The 1st value to compare
+ * @param {Number/Date} value2 The 2nd value to compare
+ * @param {String} desc The description of the assertion
+ */
+ isGreater : function (value1, value2, desc) {
+ if (value1 > value2)
+ this.pass(desc, {
+ descTpl : '`{value1}` is greater than `{value2}`',
+ value1 : value1,
+ value2 : value2
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isGreater',
+
+ got : value1,
+ need : value2,
+
+ needDesc : "Need, greater than"
+ })
+ },
+
+
+ /**
+ * This assertion passes, when the comparison of 1st with 2nd, using `<` operator will return `true` and fails otherwise.
+ *
+ * @param {Number/Date} value1 The 1st value to compare
+ * @param {Number/Date} value2 The 2nd value to compare
+ * @param {String} desc The description of the assertion
+ */
+ isLess : function (value1, value2, desc) {
+ if (value1 < value2)
+ this.pass(desc, {
+ descTpl : '`{value1}` is less than `{value2}`',
+ value1 : value1,
+ value2 : value2
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isLess',
+
+ got : value1,
+ need : value2,
+
+ needDesc : "Need, less than"
+ })
+ },
+
+
+ isGE : function () {
+ this.isGreaterOrEqual.apply(this, arguments)
+ },
+
+ /**
+ * This assertion passes, when the comparison of 1st with 2nd, using `>=` operator will return `true` and fails otherwise.
+ *
+ * It has a synonym - `isGE`.
+ *
+ * @param {Number/Date} value1 The 1st value to compare
+ * @param {Number/Date} value2 The 2nd value to compare
+ * @param {String} desc The description of the assertion
+ */
+ isGreaterOrEqual : function (value1, value2, desc) {
+ if (value1 >= value2)
+ this.pass(desc, {
+ descTpl : '`{value1}` is greater or equal to `{value2}`',
+ value1 : value1,
+ value2 : value2
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isGreaterOrEqual',
+
+ got : value1,
+ need : value2,
+
+ needDesc : "Need, greater or equal to"
+ })
+ },
+
+
+
+ isLE : function () {
+ this.isLessOrEqual.apply(this, arguments)
+ },
+
+ /**
+ * This assertion passes, when the comparison of 1st with 2nd, using `<=` operator will return `true` and fails otherwise.
+ *
+ * It has a synonym - `isLE`.
+ *
+ * @param {Number/Date} value1 The 1st value to compare
+ * @param {Number/Date} value2 The 2nd value to compare
+ * @param {String} desc The description of the assertion
+ */
+ isLessOrEqual : function (value1, value2, desc) {
+ if (value1 <= value2)
+ this.pass(desc, {
+ descTpl : '`{value1}` is less or equal to `{value2}`',
+ value1 : value1,
+ value2 : value2
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isLessOrEqual',
+
+ got : value1,
+ need : value2,
+
+ needDesc : "Need, less or equal to"
+ })
+ },
+
+
+ /**
+ * This assertion suppose to compare the numeric values. It passes when the passed values are approximately the same (the difference
+ * is withing a threshold). A threshold can be provided explicitly (when assertion is called with 4 arguments),
+ * or it will be set to 5% from the 1st value (when calling assertion with 3 arguments).
+ *
+ * @param {Number} value1 The 1st value to compare
+ * @param {Number} value2 The 2nd value to compare
+ * @param {Number} threshHold The maximum allowed difference between values. This argument can be omited.
+ * @param {String} desc The description of the assertion
+ */
+ isApprox : function (value1, value2, threshHold, desc) {
+ if (arguments.length == 2) threshHold = Math.abs(value1 * 0.05)
+
+ if (arguments.length == 3) {
+ if (this.typeOf(threshHold) == 'String') {
+ desc = threshHold
+ threshHold = Math.abs(value1 * 0.05)
+ }
+ }
+
+ // this function normalizes the fractional numbers to fixed point presentation
+ // for example in JS: 1.05 - 1 = 0.050000000000000044
+ // so what we do is: (1.05 * 10^2 - 1 * 10^2) / 10^2 = (105 - 100) / 100 = 0.05
+ var subtract = function (value1, value2) {
+ var fractionalLength = function (v) {
+ var afterPointPart = (v + '').split('.')[ 1 ]
+
+ return afterPointPart && afterPointPart.length || 0
+ }
+
+ var maxLength = Math.max(fractionalLength(value1), fractionalLength(value2))
+ var k = Math.pow(10, maxLength);
+
+ return (value1 * k - value2 * k) / k;
+ };
+
+ if (Math.abs(subtract(value2, value1)) <= threshHold)
+ this.pass(desc, {
+ descTpl : '`{value1}` is approximately equal to `{value2}`',
+ value1 : value1,
+ value2 : value2,
+ annotation : value2 == value1 ? 'Exact match' : 'Match within treshhold: ' + threshHold
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isApprox',
+ got : value1,
+ need : value2,
+ needDesc : 'Need approx',
+ annotation : 'Threshold is: ' + threshHold
+ })
+ },
+
+
+ /**
+ * This assertion passes when the passed `string` matches to a regular expression `regex`. When `regex` is a string,
+ * assertion will check that it is a substring of `string`
+ *
+ * @param {String} string The string to check for "likeness"
+ * @param {String/RegExp} regex The regex against which to test the string, can be also a plain string
+ * @param {String} desc The description of the assertion
+ */
+ like : function (string, regex, desc) {
+ if (this.typeOf(regex) == "RegExp")
+
+ if (string.match(regex))
+ this.pass(desc, {
+ descTpl : '`{string}` matches regexp {regex}',
+ string : string,
+ regex : regex
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'like',
+ got : string,
+ need : regex,
+ needDesc : 'Need string matching'
+ })
+ else
+
+ if (string.indexOf(regex) != -1)
+ this.pass(desc, {
+ descTpl : '`{string}` has a substring: `{regex}`',
+ string : string,
+ regex : regex
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'like',
+ got : string,
+ need : regex,
+ needDesc : 'Need string containing'
+ })
+ },
+
+ /**
+ * This method is the opposite of 'like', it adds failed assertion, when the string matches the passed regex.
+ *
+ * @param {String} string The string to check for "unlikeness"
+ * @param {String/RegExp} regex The regex against which to test the string, can be also a plain string
+ * @param {String} desc The description of the assertion
+ */
+ unlike : function(string, regex, desc) {
+ if (this.typeOf(regex) == "RegExp")
+
+ if (!string.match(regex))
+ this.pass(desc, {
+ descTpl : '`{string}` does not match regexp {regex}',
+ string : string,
+ regex : regex
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'unlike',
+ got : string,
+ need : regex,
+ needDesc : 'Need string not matching'
+ })
+ else
+
+ if (string.indexOf(regex) == -1)
+ this.pass(desc, {
+ descTpl : '`{string}` does not have a substring: `{regex}`',
+ string : string,
+ regex : regex
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'unlike',
+ got : string,
+ need : regex,
+ needDesc : 'Need string not containing'
+ })
+ },
+
+
+ "throws" : function () {
+ this.throwsOk.apply(this, arguments)
+ },
+
+ throws_ok : function () {
+ this.throwsOk.apply(this, arguments)
+ },
+
+ /**
+ * This assertion passes, when the `func` function throws the exception during executing, and the
+ * stringified exception passes the 'like' assertion (with 'expected' parameter).
+ *
+ * It has synonyms - `throws_ok` and `throws`.
+ *
+ * @param {Function} func The function which supposed to throw an exception
+ * @param {String/RegExp} expected The regex against which to test the stringified exception, can be also a plain string
+ * @param {String} desc The description of the assertion
+ */
+ throwsOk : function (func, expected, desc) {
+ if (this.typeOf(func) != 'Function') throw new Error('throws_ok accepts a function as 1st argument')
+
+ var e = this.getExceptionCatcher()(func)
+
+ // assuming no one will throw undefined exception..
+ if (e === undefined) {
+ this.fail(desc, {
+ assertionName : 'throws_ok',
+ annotation : 'Function did not throw an exception'
+ })
+
+ return
+ }
+
+ if (e instanceof this.getTestErrorClass())
+ //IE uses non-standard 'description' property for error msg
+ e = e.message || e.description
+
+ e = '' + e
+
+ if (this.typeOf(expected) == "RegExp")
+
+ if (e.match(expected))
+ this.pass(desc, {
+ descTpl : 'Function throws exception matching to {expected}',
+ expected : expected
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'throws_ok',
+ got : e,
+ gotDesc : 'Exception stringifies to',
+ need : expected,
+ needDesc : 'Need string matching'
+ })
+ else
+
+ if (e.indexOf(expected) != -1)
+ this.pass(desc, {
+ descTpl : 'Function throws exception containing a substring: {expected}',
+ expected : expected
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'throws_ok',
+ got : e,
+ gotDesc : 'Exception stringifies to',
+ need : expected,
+ needDesc : 'Need string containing'
+ })
+ },
+
+
+
+ lives_ok : function () {
+ this.livesOk.apply(this, arguments)
+ },
+
+ lives : function () {
+ this.livesOk.apply(this, arguments)
+ },
+
+ /**
+ * This assertion passes, when the supplied `func` function doesn't throw an exception during execution.
+ *
+ * This method has two synonyms: `lives_ok` and `lives`
+ *
+ * @param {Function} func The function which is not supposed to throw an exception
+ * @param {String} desc The description of the assertion
+ */
+ livesOk : function (func, desc) {
+ if (this.typeOf(func) != 'Function') {
+ func = [ desc, desc = func ][ 0 ]
+ }
+
+ var e = this.getExceptionCatcher()(func)
+
+ if (e === undefined)
+ this.pass(desc, {
+ descTpl : 'Function does not throw any exceptions'
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'lives_ok',
+ annotation : 'Function threw an exception: ' + e
+ })
+ },
+
+
+ isa_ok : function (value, className, desc) {
+ this.isInstanceOf(value, className, desc)
+ },
+
+
+ isaOk : function (value, className, desc) {
+ this.isInstanceOf(value, className, desc)
+ },
+
+ /**
+ * This assertion passes, when the supplied `value` is the instance of the `className`. The check is performed with
+ * `instanceof` operator. The `className` parameter can be supplied as class constructor or as string, representing the class
+ * name. In the latter case the `class` will eval'ed to receive the class constructor.
+ *
+ * This method has synonyms: `isaOk`, `isa_ok`
+ *
+ * @param {Mixed} value The value to check for 'isa' relationship
+ * @param {Class/String} className The class to check for 'isa' relationship with `value`
+ * @param {String} desc The description of the assertion
+ */
+ isInstanceOf : function (value, className, desc) {
+ try {
+ if (this.typeOf(className) == 'String') className = this.global.eval(className)
+ } catch (e) {
+ this.fail(desc, {
+ assertionName : 'isa_ok',
+ annotation : "Exception [" + e + "] caught, while evaluating the class name [" + className + "]"
+ })
+
+ return
+ }
+
+ if (value instanceof className)
+ this.pass(desc, {
+ descTpl : '{value} is an instance of correct class',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isa_ok',
+ got : value,
+ need : String(className),
+ needDesc : 'Need, instance of'
+ })
+ },
+
+
+ /**
+ * This assertion passes, if supplied value is a String.
+ *
+ * @param {Mixed} value The value to check.
+ * @param {String} desc The description of the assertion
+ */
+ isString : function (value, desc) {
+ if (this.typeOf(value) == 'String')
+ this.pass(desc, {
+ descTpl : '{value} is a string',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ got : value,
+ need : "A string value"
+ })
+ },
+
+
+ /**
+ * This assertion passes, if supplied value is an Object
+ *
+ * @param {Mixed} value The value to check.
+ * @param {String} desc The description of the assertion
+ */
+ isObject : function (value, desc) {
+ if (this.typeOf(value) == 'Object')
+ this.pass(desc, {
+ descTpl : '{value} is an object',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ got : value,
+ need : "An object value"
+ })
+ },
+
+
+ /**
+ * This assertion passes, if supplied value is an Array
+ *
+ * @param {Mixed} value The value to check.
+ * @param {String} desc The description of the assertion
+ */
+ isArray : function (value, desc) {
+ if (this.typeOf(value) == 'Array')
+ this.pass(desc, {
+ descTpl : '{value} is an array',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ got : value,
+ need : "An array value"
+ })
+ },
+
+
+ /**
+ * This assertion passes, if supplied value is a Number.
+ *
+ * @param {Mixed} value The value to check.
+ * @param {String} desc The description of the assertion
+ */
+ isNumber : function (value, desc) {
+ if (this.typeOf(value) == 'Number')
+ this.pass(desc, {
+ descTpl : '{value} is a number',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ got : value,
+ need : "A number value"
+ })
+ },
+
+
+ /**
+ * This assertion passes, if supplied value is a Boolean.
+ *
+ * @param {Mixed} value The value to check.
+ * @param {String} desc The description of the assertion
+ */
+ isBoolean : function (value, desc) {
+ if (this.typeOf(value) == 'Boolean')
+ this.pass(desc, {
+ descTpl : '{value} is a boolean',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ got : value,
+ need : "A boolean value"
+ })
+ },
+
+
+ /**
+ * This assertion passes, if supplied value is a Date.
+ *
+ * @param {Mixed} value The value to check.
+ * @param {String} desc The description of the assertion
+ */
+ isDate : function (value, desc) {
+ if (this.typeOf(value) == 'Date')
+ this.pass(desc, {
+ descTpl : '{value} is a date',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ got : value,
+ need : "A date value"
+ })
+ },
+
+
+ /**
+ * This assertion passes, if supplied value is a RegExp.
+ *
+ * @param {Mixed} value The value to check.
+ * @param {String} desc The description of the assertion
+ */
+ isRegExp : function (value, desc) {
+ if (this.typeOf(value) == 'RegExp')
+ this.pass(desc, {
+ descTpl : '{value} is a regular expression',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ got : value,
+ need : "A regular expression value"
+ })
+ },
+
+
+ /**
+ * This assertion passes, if supplied value is a Function.
+ *
+ * @param {Mixed} value The value to check.
+ * @param {String} desc The description of the assertion
+ */
+ isFunction : function (value, desc) {
+ if (this.typeOf(value) == 'Function')
+ this.pass(desc, {
+ descTpl : '{value} is a function',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ got : value,
+ need : "A function value"
+ })
+ },
+
+
+ is_deeply : function (obj1, obj2, desc) {
+ this.isDeeply.apply(this, arguments)
+ },
+
+ /**
+ * This assertion passes when in-depth comparison of 1st and 2nd arguments (which are assumed to be JSON objects) shows that they are equal.
+ * Comparison is performed with '==' operator, so `[ 1 ]` and `[ "1" ] objects will be equal. The objects should not contain cyclic references.
+ *
+ * This method works correctly with the *placeholders* generated with method {@link #any}.
+ *
+ * This method has a synonym: `is_deeply`
+ *
+ * @param {Object} obj1 The 1st object to compare
+ * @param {Object} obj2 The 2nd object to compare
+ * @param {String} desc The description of the assertion
+ */
+ isDeeply : function (obj1, obj2, desc) {
+ if (this.typeOf(obj1) === this.typeOf(obj2) && this.compareObjects(obj1, obj2))
+ this.pass(desc, {
+ descTpl : '{obj1} is deeply equal to {obj2}',
+ obj1 : obj1,
+ obj2 : obj2
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isDeeply',
+ got : obj1,
+ need : obj2
+ })
+ },
+
+
+ /**
+ * This assertion passes when in-depth comparison of 1st and 2nd arguments (which are assumed to be JSON objects) shows that they are equal.
+ * Comparison is performed with '===' operator, so `[ 1 ]` and `[ "1" ] objects will be different. The objects should not contain cyclic references.
+ *
+ * This method works correctly with the *placeholders* generated with method {@link #any}.
+ *
+ * @param {Object} obj1 The 1st object to compare
+ * @param {Object} obj2 The 2nd object to compare
+ * @param {String} desc The description of the assertion
+ */
+ isDeeplyStrict : function (obj1, obj2, desc) {
+ if (this.typeOf(obj1) === this.typeOf(obj2) && this.compareObjects(obj1, obj2, true))
+ this.pass(desc, {
+ descTpl : '{obj1} is strictly deeply equal to {obj2}',
+ obj1 : obj1,
+ obj2 : obj2
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isDeeplyStrict',
+ got : obj1,
+ need : obj2
+ })
+ },
+
+ expectGlobal : function () {
+ this.expectGlobals.apply(this, arguments)
+ },
+
+
+ /**
+ * This method accepts a variable number of names of expected properties in the global scope. When verifying the globals with {@link #verifyGlobals}
+ * assertions, the expected gloabls will not be counted as failed assertions.
+ *
+ * This method has a synonym with singular name: `expectGlobal`
+ *
+ * @param {String/RegExp} name1 The name of global property or the regular expression to match several properties
+ * @param {String/RegExp} name2 The name of global property or the regular expression to match several properties
+ * @param {String/RegExp} nameN The name of global property or the regular expression to match several properties
+ */
+ expectGlobals : function () {
+ this.expectedGlobals.push.apply(this.expectedGlobals, arguments)
+ },
+
+
+ /**
+ * This method accepts a variable number of names of expected properties in the global scope and then performs a globals check.
+ *
+ * It will scan all globals properties in the scope of test and compare them with the list of expected globals. Expected globals can be provided with:
+ * {@link #expectGlobals} method or {@link Siesta.Harness#expectedGlobals expectedGlobals} configuration option of harness.
+ *
+ * You can enable this assertion to automatically happen at the end of each test, using {@link Siesta.Harness#autoCheckGlobals autoCheckGlobals} option of the harness.
+ *
+ * @param {String/RegExp} name1 The name of global property or the regular expression to match several properties
+ * @param {String/RegExp} name2 The name of global property or the regular expression to match several properties
+ * @param {String/RegExp} nameN The name of global property or the regular expression to match several properties
+ */
+ verifyGlobals : function () {
+ if (this.disableGlobalsCheck) {
+ this.diag('Testing leakage of global variables is not supported on this platform')
+
+ return
+ }
+
+ this.expectGlobals.apply(this, arguments)
+
+ var me = this
+ var expectedStrings = {}
+ var expectedRegExps = []
+
+ Joose.A.each(this.expectedGlobals.concat(this.browserGlobals), function (value) {
+ if (me.typeOf(value) == 'RegExp')
+ expectedRegExps.push(value)
+ else
+ expectedStrings[ value ] = true
+ })
+
+ this.diag('Global variables')
+
+ var failed = false
+
+ for (var name in this.global) {
+ if (expectedStrings[ name ]) continue
+
+ var isExpected = false
+
+ for (var i = 0; i < expectedRegExps.length; i++) {
+ if (expectedRegExps[ i ].test(name)) {
+ isExpected = true
+ break
+ }
+ }
+
+ if (!isExpected) {
+ me.fail('Unexpected global found', 'Global name: ' + name + ', value: ' + Siesta.Util.Serializer.stringify(this.global[name]))
+
+ failed = true
+ }
+ }
+
+ if (!failed) this.pass('No unexpected global variables found')
+ },
+
+
+ // will create a half-realized, "phantom", "isWaitFor" assertion, which is only purposed
+ // for user to get the instant feedback about "waitFor" actions
+ // this assertion will be "finalized" and added to the test results in the "finalizeWaiting"
+ startWaiting : function (description, sourceLine) {
+ var result = new Siesta.Result.Assertion({
+ description : description,
+ isWaitFor : true,
+ sourceLine : sourceLine
+ });
+
+ this.fireEvent('testupdate', this, result, this.getResults())
+
+ return result;
+ },
+
+
+ finalizeWaiting : function (result, passed, desc, annotation, errback) {
+ // Treat this is an ordinary assertion from now on
+ result.completed = true;
+
+ if (passed)
+ this.pass(desc, annotation, result)
+ else {
+ this.fail(desc, annotation, result);
+
+ errback && errback()
+ }
+ },
+
+
+ /**
+ * Waits for passed checker method to return true (or any non-false value, like for example DOM element or array), and calls the callback when this happens.
+ * As an additional feature, the callback will receive the result from the checker method as the 1st argument.
+ *
+
+ t.waitFor(
+ function () { return document.getElementById('someEl') },
+ function (el) {
+ // waited for element #someEl to appear
+ // element will be available in the callback as 1st argument "el"
+ }
+ })
+
+ * You can also call this method with a single Object having the following properties: `method`, `callback`, `scope`, `timeout`, `interval`:
+
+ t.waitFor({
+ method : function () { return document.getElementById('someEl') },
+ callback : function (el) {
+ // waited for element #someEl to appear
+ // element will be available in the callback as 1st argument "el"
+ }
+ })
+
+ *
+ * @param {Object/Function/Number} method Either a function which should return true when a certain condition has been fulfilled, or a number of ms to wait before calling the callback.
+ * @param {Function} callback A function to call when the condition has been met. Will receive a result from checker function.
+ * @param {Object} scope The scope for the callback
+ * @param {Int} timeout The maximum amount of time (in milliseconds) to wait for the condition to be fulfilled.
+ * Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value. If condition is not fullfilled within this time, a failed assertion will be added to the test.
+ * @param {Int} [interval=100] The polling interval (in milliseconds)
+ *
+ * @return {Object} An object with the following properties:
+ * @return {Function} return.force A function, that will force this wait operation to immediately complete (and call the callback).
+ * No call to checker will be performed and callback will not receive a result from it.
+ */
+ waitFor : function (method, callback, scope, timeout, interval) {
+ var description = ' condition to be fullfilled';
+ var assertionName = 'waitFor';
+ var errback
+
+ if (arguments.length === 1 && this.typeOf(method) == 'Object') {
+ var options = method;
+
+ method = options.method;
+ callback = options.callback;
+ scope = options.scope;
+ timeout = options.timeout;
+ interval = options.interval
+
+ description = options.description || description;
+ assertionName = options.assertionName || assertionName;
+
+ // errback is called in case "waitFor" has failed
+ errback = options.errback
+ }
+
+ var isWaitingForTime = this.typeOf(method) == 'Number'
+
+ var description = isWaitingForTime ? (method + ' ms') : description;
+ var me = this;
+
+ callback = callback || function () {}
+
+ var sourceLine = me.getSourceLine();
+ var originalSetTimeout = me.originalSetTimeout;
+ var originalClearTimeout = me.originalClearTimeout;
+ var pollTimeout
+
+ // early notification about the started "waitFor" operation
+ var waitAssertion = me.startWaiting('Waiting for ' + description, sourceLine);
+
+ interval = interval || this.waitForPollInterval
+ timeout = timeout || this.waitForTimeout
+
+ // this async frame not supposed to fail, because its delayed to `timeout + 3 * interval`
+ // failure supposed to be generated in the "pollFunc" and this async frame to be closed
+ // however, in IE it happens that async frame may end earlier than failure from "pollFunc"
+ // in such case we report same error as in "pollFunc"
+ var async = this.beginAsync((isWaitingForTime ? method : timeout) + 3 * interval, function () {
+ originalClearTimeout(pollTimeout)
+
+ me.finalizeWaiting(waitAssertion, false, 'Waited too long for: ' + description, {
+ assertionName : assertionName,
+ annotation : 'Condition was not fullfilled during ' + timeout + 'ms'
+ }, errback)
+
+ return true
+ })
+
+ var isDone = false
+
+ // stop polling, if this test instance has finalized (probably because of exception)
+ this.on('beforetestfinalize', function () {
+ if (!isDone) {
+ isDone = true
+
+ me.finalizeWaiting(waitAssertion, false, 'Waiting aborted');
+ me.endAsync(async)
+
+ originalClearTimeout(pollTimeout)
+ }
+ }, null, { single : true })
+
+ if (isWaitingForTime) {
+ pollTimeout = originalSetTimeout(function() {
+ isDone = true
+
+ me.finalizeWaiting(waitAssertion, true, 'Waited ' + method + ' ms');
+ me.endAsync(async);
+ me.processCallbackFromTest(callback, [], scope || me)
+ }, method);
+
+ } else {
+
+ var startDate = new Date()
+
+ var pollFunc = function () {
+ var time = new Date() - startDate;
+
+ if (time > timeout) {
+ me.endAsync(async);
+
+ me.finalizeWaiting(waitAssertion, false, 'Waited too long for: ' + description, {
+ assertionName : assertionName,
+ annotation : 'Condition was not fullfilled during ' + timeout + 'ms'
+ }, errback)
+
+ isDone = true
+
+ return
+ }
+
+ try {
+ var result = method.call(scope || me);
+ } catch (e) {
+ me.endAsync(async);
+
+ me.finalizeWaiting(waitAssertion, false, assertionName + ' checker threw an exception', {
+ assertionName : assertionName,
+ got : e.toString(),
+ gotDesc : "Exception"
+ }, errback)
+
+ isDone = true
+
+ return
+ }
+
+ if (result != null && result !== false) {
+ me.endAsync(async);
+
+ isDone = true
+
+ me.finalizeWaiting(waitAssertion, true, 'Waited ' + time + ' ms for ' + description);
+
+ me.processCallbackFromTest(callback, [ result ], scope || me)
+ } else
+ pollTimeout = originalSetTimeout(pollFunc, interval)
+ }
+
+ pollFunc()
+ }
+
+ return {
+ force : function () {
+ // wait operation already completed
+ if (isDone) return
+
+ isDone = true
+
+ originalClearTimeout(pollTimeout)
+
+ me.endAsync(async);
+
+ me.finalizeWaiting(waitAssertion, true, 'Forced finalization of waiting for ' + description);
+
+ me.processCallbackFromTest(callback, [], scope || me)
+ }
+ }
+ },
+
+ /**
+ * Waits for the number of a number millseconds and calls the callback when after waiting. This is just a convenience synonym for the {@link #waitFor} method.
+
+ t.waitForMs(1500, callback)
+
+ *
+ * @param {Number} method The number of ms to wait before calling the callback.
+ * @param {Function} callback A function to call when the condition has been met. Will receive a result from checker function.
+ * @param {Object} scope The scope for the callback
+ * @param {Int} timeout The maximum amount of time (in milliseconds) to wait for the condition to be fulfilled.
+ * Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value. If condition is not fullfilled within this time, a failed assertion will be added to the test.
+ * @param {Int} [interval=100] The polling interval (in milliseconds)
+ *
+ * @return {Object} An object with the following properties:
+ * @return {Function} return.force A function, that will force this wait operation to immediately complete (and call the callback).
+ * No call to checker will be performed and callback will not receive a result from it.
+ */
+ waitForMs : function() {
+ return this.waitFor.apply(this, arguments);
+ },
+
+
+ /**
+ * Waits for the passed checker method to return true (or any non-false value, like for example DOM element or array), and calls the callback when this happens.
+ * This is just a convenience synonym for the {@link #waitFor} method.
+ *
+
+ t.waitForFn(function() { return true; }, callback)
+
+ *
+ * @param {Function} fn The checker function.
+ * @param {Function} callback A function to call when the condition has been met. Will receive a result from checker function.
+ * @param {Object} scope The scope for the callback
+ * @param {Int} timeout The maximum amount of time (in milliseconds) to wait for the condition to be fulfilled.
+ * Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value. If condition is not fullfilled within this time, a failed assertion will be added to the test.
+ * @param {Int} [interval=100] The polling interval (in milliseconds)
+ *
+ * @return {Object} An object with the following properties:
+ * @return {Function} return.force A function, that will force this wait operation to immediately complete (and call the callback).
+ * No call to checker will be performed and callback will not receive a result from it.
+ */
+ waitForFn : function() {
+ return this.waitFor.apply(this, arguments);
+ },
+
+ // takes the step function and tries to analyze if it is missing the call to "next"
+ // returns "true" if "next" is used,
+ analyzeChainStep : function (func) {
+ var sources = func.toString()
+ var firstArg = sources.match(/function\s*[^(]*\(\s*(.*?)\s*(?:,|\))/)[ 1 ]
+
+ if (!firstArg) return false
+
+ var body = sources.match(/\{([\s\S]*)\}/)[ 1 ]
+
+ return body.indexOf(firstArg) != -1
+ },
+
+
+ /**
+ * This method accept either variable number of arguments (steps) or the array of them.
+ * Each step should be either a function or configuration object for test actions.
+ * These functions / actions will be executed in order.
+ *
+ * 1) If step is a function, as the 1st argument, it will receive a callback to call when the step is completed.
+ * As the 2nd and further arguments, the step function will receive the arguments passed to the previous callback.
+ *
+ * The last step will receive a no-op callback, which can be ignored or still called. **Note**, that last step is assumed to
+ * complete synchronously! If you need to launch some asynchronous process in the last step, you may need to add another empty function step
+ * to the end of the chain.
+ *
+ * 2) If a step is presented with action configuration object, then the callback will be called by the action class automatically,
+ * there's no need to provide any. Configuration object should contain the "action" property, specifying the action class
+ * and some other config options (depending from the action class). For brevity, instead of "action" property, configuration
+ * object may contain the property corresponding to the action name itself, with the action's target. See the following examples and also
+ * refer to the documentation of the action classes.
+ *
+ * If configuration object will contain a "desc" property, a passing assertion with it value will be added to the test, after this step completion.
+ *
+ * 3) If step is a sub test instance, created with {@link #getSubTest} method, then step will launch it.
+ *
+ * Its better to see how it works on the example. For example, when using using only functions:
+
+ t.chain(
+ // function receives a callback as 1st argument
+ function (next) {
+ // we pass that callback to the "click" method
+ t.click(buttonEl, next)
+ },
+ function (next) {
+ t.type(fieldEl, 'Something', next)
+ },
+ function (next) {
+ t.is(fieldEl.value == 'Something', 'Correct value in the field')
+
+ // call the callback with some arguments
+ next('foo', 'bar')
+ },
+ // those arguments are now available as arguments of next step
+ function (next, value1, value2) {
+ t.is(value1, 'foo', 'The arguments for the callback are translated to the arguments of the step')
+ t.is(value2, 'bar', 'The arguments for the callback are translated to the arguments of the step')
+ }
+ )
+
+ *
+ * The same example, using action configuration objects for first 2 steps. For the list of available actions please refer to the classes in the Siesta.Test.Action namespace.
+
+ t.chain(
+ {
+ action : 'click',
+ target : buttonEl,
+ desc : "Clicked on the button"
+ },
+ // or
+ {
+ click : buttonEl,
+ desc : "Clicked on the button"
+ },
+
+ {
+ action : 'type',
+ target : fieldEl,
+ text : 'Something',
+ desc : "Typed in the field"
+ },
+ // or
+ {
+ type : 'Something',
+ target : fieldEl,
+ desc : "Typed in the field"
+ },
+
+
+ function (next) {
+ t.is(fieldEl.value == 'Something', 'Correct value in the field')
+
+ next('foo', 'bar')
+ },
+ ...
+ )
+
+ * Please note, that by default, each step is expected to complete within the {@link Siesta.Harness#defaultTimeout} time.
+ * You can change this with the `timeout` property of the step configuration object, allowing some steps to last longer.
+ * Steps with sub-tests are expected to complete within {@link Siesta.Harness#subTestTimeout}.
+ *
+ * In a special case, `action` property of the step configuration object can be a function. In this case you can also
+ * provide a `timeout` property, otherwise this case is identical to using functions:
+ *
+
+ t.chain(
+ {
+ action : function (next) { ... },
+ // allow 50s for the function to call "next" before step will be considered timed-out
+ timeout : 50000
+ },
+ ...
+ )
+
+ * **Tip**:
+ *
+ * If step is presented with a `null` or `undefined` value it will be ignored. Additionally, the step may be presented
+ * with an array of steps - all arrays in the input will be flattened.
+ *
+ * These tips allows us to implement conditional steps processing, like this:
+ *
+
+ var el1IsInDom = t.$('.some-class1')[ 0 ]
+ var el2IsInDom = t.$('.some-class2')[ 0 ]
+
+ t.chain(
+ { click : '.some-other-el' },
+
+ el1IsInDom ? [
+ { click : el1IsInDom },
+
+ el2IsInDom ? [
+ { click : el1IsInDom },
+ ] : null,
+ ] : null,
+
+ ...
+ )
+
+ *
+ * @param {Function/Object/Array} step1 The function to execute or action configuration, or the array of such
+ * @param {Function/Object} step2 The function to execute or action configuration
+ * @param {Function/Object} stepN The function to execute or action configuration
+ */
+ chain : function () {
+ // inline any arrays in the arguments into one array
+ var steps = this.flattenArray(arguments)
+
+ var nonEmpty = []
+ Joose.A.each(steps, function (step) { if (step) nonEmpty.push(step) })
+
+ steps = nonEmpty
+
+ var len = steps.length
+
+ // do nothing
+ if (!len) return;
+
+ var me = this
+ var self = arguments.callee
+
+ var queue = new Siesta.Util.Queue({
+ deferer : this.originalSetTimeout,
+ deferClearer : this.originalClearTimeout,
+
+ interval : self.hasOwnProperty('actionDelay') ? self.actionDelay : this.actionDelay,
+
+ observeTest : this
+ })
+
+ // hack to allow configuration of `actionDelay`...
+ delete self.actionDelay
+
+ var sourceLine = me.getSourceLine();
+
+ var args = []
+
+ Joose.A.each(steps, function (step, index) {
+
+ var isLast = index == len - 1
+
+ queue.addAsyncStep({
+ processor : function (data) {
+ var isStepWithOwnAsyncFrame = step.action == 'wait' || step.waitFor || (step instanceof Siesta.Test)
+
+ if (!isStepWithOwnAsyncFrame) {
+ var timeout = step.timeout || me.defaultTimeout
+
+ // + 100 to allow `waitFor` steps (which will be waiting the `timeout` time) to
+ // generate their own failures
+ var async = me.beginAsync(timeout + 100, function () {
+ me.fail(
+ 'The step in `t.chain()` call did not complete within required timeframe, chain can not proceed',
+ {
+ sourceLine : sourceLine,
+ annotation : 'Step number: ' + (index + 1) + ' (1-based)' + (sourceLine ? '\nAt line : ' + sourceLine : ''),
+ ownTextOnly : true
+ }
+ )
+
+ return true
+ })
+ }
+
+ var nextFunc = function () {
+ if (!isStepWithOwnAsyncFrame) me.endAsync(async)
+
+ args = Array.prototype.slice.call(arguments);
+
+ if (step.desc) {
+ me.pass(step.desc);
+ }
+ data.next()
+ }
+
+ if (step instanceof Siesta.Test) {
+ me.launchSubTest(step, nextFunc)
+ } else if (me.typeOf(step) == 'Function' || me.typeOf(step.action) == 'Function') {
+ var func = me.typeOf(step) == 'Function' ? step : step.action
+
+ // if the last step is a function - then provide "null" as the "next" callback for it
+ args.unshift(isLast ? function () {} : nextFunc)
+
+ if (!isLast && !me.analyzeChainStep(func)) me.fail('Step function [' + func.toString() + '] does not use provided "next" function anywhere')
+
+ if (me.transparentEx)
+ func.apply(me, args)
+ else {
+ var e = me.getExceptionCatcher()(function () {
+ func.apply(me, args)
+ })
+
+ if (e !== undefined) {
+ me.fail("Chain step threw an exception", { annotation : e + '' })
+ }
+ }
+
+ // and finalize the async frame manually, as the "nextFunc" for last step will never be called
+ isLast && me.endAsync(async)
+
+ } else if (me.typeOf(step) == 'String') {
+ var action = new Siesta.Test.Action.Eval({
+ actionString : step,
+ next : nextFunc,
+ test : me
+ })
+
+ action.process()
+
+ } else {
+ if (!step.args) step.args = args
+
+ // Don't pass target to next step if it is a waitFor action, it does not make sense and messes up the arguments
+ if (!isLast && (steps[ index + 1 ].waitFor || steps[ index + 1 ].action == 'wait')) {
+ step.passTargetToNext = false;
+ }
+
+ step.next = nextFunc
+ step.test = me
+
+ var action = Siesta.Test.ActionRegistry().create(step)
+
+ action.process()
+ }
+ }
+ })
+ })
+
+ queue.run()
+ }
+ },
+
+
+ after : {
+
+ initialize : function () {
+
+ this.on('beforetestfinalize', function () {
+
+ if (this.autoCheckGlobals && !this.isFailed() && !this.parent) this.verifyGlobals()
+
+ }, this)
+ }
+ }
+
+})
+//eof Siesta.Test.More
+;
+Class('Siesta.Test.BDD.Placeholder', {
+
+ has : {
+ clsConstructor : { required : true },
+ t : null,
+ context : null,
+
+ globals : {
+ init : [
+ 'String',
+ 'Boolean',
+ 'Number',
+ 'Date',
+ 'RegExp',
+ 'Function',
+ 'Array',
+ 'Object'
+ ]
+ }
+ },
+
+
+ methods : {
+
+ getClassName : function (onlyGlobals) {
+ var clsConstructor = this.getClassConstructor()
+ var context = this.context
+
+ var clsName
+
+ Joose.A.each(this.globals, function (property) {
+ if (clsConstructor == context[ property ]) { clsName = property; return false }
+ })
+
+ return onlyGlobals ? clsName : clsName || clsConstructor + ''
+ },
+
+
+ getClassConstructor : function () {
+ return this.clsConstructor
+ },
+
+
+ equalsTo : function (value) {
+ var clsConstructor = this.getClassConstructor()
+
+ if (!clsConstructor) return true
+
+ if (value instanceof Siesta.Test.BDD.Placeholder) {
+ var ownClassName = this.getClassName(true)
+
+ if (
+ value.getClassName(true) == 'Object' && (
+ ownClassName == 'Date' ||
+ ownClassName == 'RegExp' ||
+ ownClassName == 'Function'||
+ ownClassName == 'Array'
+ )
+ ) {
+ return true
+ }
+
+ return clsConstructor == value.getClassConstructor()
+ }
+
+ var isEqual = false
+
+ var globalCls = this.getClassName(true)
+
+ if (globalCls)
+ isEqual = this.t.typeOf(value) == globalCls || (value instanceof this.context[ globalCls ])
+
+ return isEqual || (value instanceof clsConstructor)
+ },
+
+
+ toString : function () {
+ return 'any ' + this.getClassName()
+ }
+ }
+})
+;
+/**
+@class Siesta.Test.BDD.Expectation
+
+This class is a central point for writing assertions in the BDD style. Instances of this class can be generated with the {@link Siesta.Test#expect expect}
+method. Then, calling some method on the instance will create a new assertion in the test.
+
+* **Note**, that to negate any assertion, you can use a special property {@link #not}, that also contains an expectation instance, but with the opposite meaning.
+
+For example:
+
+ t.expect(1).toBe(1)
+ t.expect(1).not.toBe(2)
+
+ t.expect('Foo').toContain('oo')
+ t.expect('Foo').not.toContain('bar')
+
+
+*/
+Class('Siesta.Test.BDD.Expectation', {
+
+ has : {
+ value : null,
+
+ isNot : false,
+
+ /**
+ * @property {Siesta.Test.BDD.Expectation} not Another expectation instance with the negated meaning.
+ */
+ not : null,
+
+ t : null
+ },
+
+
+ methods : {
+
+ initialize : function () {
+ if (!this.isNot) this.not = new this.constructor({
+ isNot : true,
+ t : this.t,
+
+ value : this.value
+ })
+ },
+
+
+ process : function (passed, config) {
+ var isNot = this.isNot
+ config = config || {}
+
+ config.not = config.not || isNot ? 'not ' : ''
+ config.got = config.hasOwnProperty('got') ? config.got : this.value
+
+ if (config.noGot) delete config.got
+
+ var assertionName = config.assertionName
+
+ if (assertionName && isNot) config.assertionName = assertionName.replace(/^(expect\(.+?\)\.)/, '$1not.')
+
+ passed = isNot ? !passed : passed
+
+ this.t[ passed ? 'pass' : 'fail' ](null, config)
+ },
+
+
+ /**
+ * This assertion compares the value provided to the {@link Siesta.Test#expect expect} method with the `expectedValue` argument.
+ * Comparison is done with `===` operator, so it should be used only with the primitivies - numbers, strings, booleans etc.
+ * To deeply compare JSON objects and arrays, use {@link #toEqual} method.
+ *
+ * This method works correctly with the placeholders generated with {@link Siesta.Test#any any} method
+ *
+ * @param {Primitive} expectedValue An expected value
+ */
+ toBe : function (expectedValue) {
+ this.process(this.t.compareObjects(this.value, expectedValue, true, true), {
+ descTpl : 'Expect {got} {!not}to be {need}',
+ assertionName : 'expect(got).toBe(need)',
+ need : expectedValue,
+ needDesc : this.isNot ? 'Need not' : 'Need'
+ })
+ },
+
+
+ /**
+ * This assertion compares the value provided to the {@link Siesta.Test#expect expect} method with the `expectedValue` argument.
+ * Comparison works for JSON objects and/or arrays, it is performed "deeply". Right now the values should not contain cyclic references.
+ *
+ * This method works correctly with the placeholders generated with {@link Siesta.Test#any any} method
+ *
+ * @param {Mixed} expectedValue An expected value
+ */
+ toEqual : function (expectedValue) {
+ this.process(this.t.compareObjects(this.value, expectedValue, true), {
+ descTpl : 'Expect {got} {!not}to be equal to {need}',
+ assertionName : 'expect(got).toEqual(need)',
+ need : expectedValue,
+ needDesc : this.isNot ? 'Need not' : 'Need'
+ })
+ },
+
+
+ /**
+ * This assertion passes, when value provided to the {@link Siesta.Test#expect expect} method is `null`.
+ */
+ toBeNull : function () {
+ this.process(this.t.compareObjects(this.value, null, true, true), {
+ descTpl : 'Expect {got} {!not}to be null',
+ assertionName : 'expect(got).toBeNull()',
+ need : null,
+ needDesc : this.isNot ? 'Need not' : 'Need'
+ })
+ },
+
+
+ /**
+ * This assertion passes, when value provided to the {@link Siesta.Test#expect expect} method is `NaN`.
+ */
+ toBeNaN : function () {
+ var value = this.value
+
+ this.process(this.t.typeOf(value) == 'Number' && value != value, {
+ descTpl : 'Expect {got} {!not}to be NaN',
+ assertionName : 'expect(got).toBeNaN()',
+ need : NaN,
+ needDesc : this.isNot ? 'Need not' : 'Need'
+ })
+ },
+
+
+ /**
+ * This assertion passes, when value provided to the {@link Siesta.Test#expect expect} method is not the `undefined` value.
+ */
+ toBeDefined : function () {
+ this.process(this.value !== undefined, {
+ descTpl : 'Expect {got} {!not}to be defined',
+ assertionName : 'expect(got).toBeDefined()'
+ })
+ },
+
+
+ /**
+ * This assertion passes, when value provided to the {@link Siesta.Test#expect expect} method is the `undefined` value.
+ */
+ toBeUndefined : function (value) {
+ this.process(this.value === undefined, {
+ descTpl : 'Expect {got} {!not}to be undefined',
+ assertionName : 'expect(got).toBeUndefined()'
+ })
+ },
+
+
+ /**
+ * This assertion passes, when value provided to the {@link Siesta.Test#expect expect} method is "truthy" - evaluates to `true`.
+ * For example - non empty strings, numbers except the 0, objects, arrays etc.
+ */
+ toBeTruthy : function () {
+ this.process(this.value, {
+ descTpl : 'Expect {got} {!not}to be truthy',
+ assertionName : 'expect(got).toBeTruthy()'
+ })
+ },
+
+
+ /**
+ * This assertion passes, when value provided to the {@link Siesta.Test#expect expect} method is "falsy" - evaluates to `false`.
+ * For example - empty strings, number 0, `null`, `undefined`, etc.
+ */
+ toBeFalsy : function () {
+ this.process(!this.value, {
+ descTpl : 'Expect {got} {!not}to be falsy',
+ assertionName : 'expect(got).toBeFalsy()'
+ })
+ },
+
+
+ /**
+ * This assertion passes, when the string provided to the {@link Siesta.Test#expect expect} method matches the regular expression.
+ *
+ * @param {RegExp} regexp The regular expression to match the string against
+ */
+ toMatch : function (regexp) {
+ if (this.t.typeOf(regexp) != 'RegExp') throw new Error("`expect().toMatch()` matcher expects a regular expression")
+
+ this.process(new RegExp(regexp).test(this.value), {
+ descTpl : 'Expect {got} {!not}to match {need}',
+ assertionName : 'expect(got).toMatch(need)',
+ need : regexp,
+ needDesc : this.isNot ? 'Need not matching' : 'Need matching'
+ })
+ },
+
+
+ /**
+ * This assertion passes in 2 cases:
+ *
+ * 1) When the value provided to the {@link Siesta.Test#expect expect} method is a string, and it contains a passed substring.
+ * 2) When the value provided to the {@link Siesta.Test#expect expect} method is an array (or array-like), and it contains a passed element.
+ *
+ * @param {String/Mixed} element The element of the array or a sub-string
+ */
+ toContain : function (element) {
+ var value = this.value
+ var t = this.t
+
+ var passed = false
+
+ if (t.typeOf(value) == 'String') {
+ this.process(value.indexOf(element) >= 0, {
+ descTpl : 'Expect {got} {!not}to contain {need}',
+ assertionName : 'expect(got).toContain(need)',
+ need : element,
+ needDesc : this.isNot ? 'Need string not containing' : 'Need string containing'
+ })
+ } else {
+ // Normalize to allow NodeList, Arguments etc.
+ value = Array.prototype.slice.call(value);
+
+ for (var i = 0; i < value.length; i++)
+ if (t.compareObjects(element, value[ i ], true)) {
+ passed = true
+ break
+ }
+
+ this.process(passed, {
+ descTpl : 'Expect {got} {!not}to contain {need}',
+ assertionName : 'expect(got).toContain(need)',
+ need : element,
+ needDesc : this.isNot ? 'Need array not containing' : 'Need array containing'
+ })
+
+ }
+ },
+
+
+ /**
+ * This assertion passes, when the number provided to the {@link Siesta.Test#expect expect} method is less than the
+ * expected number.
+ *
+ * @param {Number} expectedValue The number to compare with
+ */
+ toBeLessThan : function (expectedValue) {
+ this.process(this.value < expectedValue, {
+ descTpl : 'Expect {got} {!not}to be less than {need}',
+ assertionName : 'expect(got).toBeLessThan(need)',
+ need : expectedValue,
+ needDesc : this.isNot ? 'Need value bigger or equal than' : 'Need value less than'
+ })
+ },
+
+
+ /**
+ * This assertion passes, when the number provided to the {@link Siesta.Test#expect expect} method is greater than the
+ * expected number.
+ *
+ * @param {Number} expectedValue The number to compare with
+ */
+ toBeGreaterThan : function (expectedValue) {
+ this.process(this.value > expectedValue, {
+ descTpl : 'Expect {got} {!not}to be greater than {need}',
+ assertionName : 'expect(got).toBeGreaterThan(need)',
+ need : expectedValue,
+ needDesc : this.isNot ? 'Need value less or equal than' : 'Need value greater than'
+ })
+ },
+
+
+ /**
+ * This assertion passes, when the number provided to the {@link Siesta.Test#expect expect} method is approximately equal
+ * the given number. The proximity can be defined as the `precision` argument
+ *
+ * @param {Number} expectedValue The number to compare with
+ * @param {Number} [precision=2] The number of digits after dot (comma) that should be same in both numbers.
+ */
+ toBeCloseTo : function (expectedValue, precision) {
+ precision = precision != null ? precision : 2
+
+ // not sure why we divide the precision by 2, but jasmine does that for some reason
+ var threshold = Math.pow(10, -precision) / 2
+ var delta = Math.abs(this.value - expectedValue)
+
+ this.process(delta < threshold, {
+ descTpl : 'Expect {got} {!not}to be close to {need}',
+ assertionName : 'expect(got).toBeCloseTo(need)',
+ need : expectedValue,
+ needDesc : this.isNot ? 'Need value not close to' : 'Need value close to',
+ annotation : delta ? "Threshold is " + threshold : 'Exact match'
+ })
+ },
+
+
+ /**
+ * This assertion passes when the function, provided to the {@link Siesta.Test#expect expect} method, throws any exception
+ * during its execution.
+ */
+ toThrow : function () {
+ var func = this.value
+ var t = this.t
+
+ if (t.typeOf(func) != 'Function') throw new Error("`expect().toMatch()` matcher expects a function")
+
+ var e = t.getExceptionCatcher()(func)
+
+ if (e instanceof t.getTestErrorClass())
+ //IE uses non-standard 'description' property for error msg
+ e = e.message || e.description
+
+ this.process(e !== undefined, {
+ descTpl : 'Expect function {!not}to throw exception',
+ assertionName : 'expect(func).toThrow()',
+ annotation : e ? "Thrown exception: " + Siesta.Util.Serializer.stringify(e) : 'No exception thrown',
+
+ noGot : true
+ })
+ },
+
+
+ // TODO
+ toHaveBeenCalled : function () {
+ },
+
+
+ // TODO
+ toHaveBeenCalledWith : function () {
+ }
+ }
+})
+;
+/**
+@class Siesta.Test.BDD
+
+A mixin providing a BDD style layer for most of the assertion methods.
+It is consumed by {@link Siesta.Test}, so all of its methods are available in all tests.
+
+*/
+Role('Siesta.Test.BDD', {
+
+ requires : [
+ 'getSubTest', 'chain'
+ ],
+
+ has : {
+ specType : null, // `describe` or `it`
+
+ sequentialSubTests : Joose.I.Array,
+
+ // flag, whether the "run" function of the test (containing actual test code) have been already run
+ codeProcessed : false,
+
+ launchTimeout : null,
+
+ // Siesta.Test.BDD.Expectation should already present on the page
+ expectationClass : Siesta.Test.BDD.Expectation
+ },
+
+
+ methods : {
+
+ checkSpecFunction : function (func, type, name) {
+ if (!func) throw new Error("Code body is not provided for " + (type == 'describe' ? 'suite' : 'spec') + ' [' + name + ']')
+ if (!func.length) throw new Error("Code body of " + (type == 'describe' ? 'suite' : 'spec') + ' [' + name + '] does not declare a test instance as 1st argument')
+ },
+
+
+ /**
+ * This is an "exclusive" version of the regular {@link #describe} suite. When such suites presents in some test file,
+ * the other regular suites at the same level will not be executed, only "exclusive" ones.
+ *
+ * @param {String} name The name or description of the suite
+ * @param {Function} code The code function for this suite. It will receive a test instance as the first argument which should be used for all assertion methods.
+ * @param {Number} [timeout] A maximum duration for this suite. If not provided {@link Siesta.Harness#subTestTimeout} value is used.
+ */
+ ddescribe : function (name, code, timeout) {
+ this.describe(name, code, timeout, true)
+ },
+
+
+ /**
+ * This is a no-op method, allowing you to quickly ignore some suites.
+ */
+ xdescribe : function () {
+ },
+
+
+ /**
+ * This method starts a sub test with *suite* (in BDD terms). Such suite consists from one or more *specs* (see method {@link #it}} or other suites.
+ * The number of nesting levels is not limited. All suites of the same nesting level are executed sequentially.
+ *
+ * For example:
+ *
+ t.describe('A product', function (t) {
+
+ t.it('should have feature X', function (t) {
+ ...
+ })
+
+ t.describe('feature X', function (t) {
+ t.it('should be cool', function (t) {
+ ...
+ })
+ })
+ })
+ *
+ * See also {@link #xdescribe}, {@link #ddescribe}
+ *
+ * @param {String} name The name or description of the suite
+ * @param {Function} code The code function for this suite. It will receive a test instance as the first argument which should be used for all assertion methods.
+ * @param {Number} [timeout] A maximum duration for this suite. If not provided {@link Siesta.Harness#subTestTimeout} value is used.
+ */
+ describe : function (name, code, timeout, isExclusive) {
+ this.checkSpecFunction(code, 'describe', name)
+
+ var subTest = this.getSubTest({
+ name : name,
+ run : code,
+
+ isExclusive : isExclusive,
+
+ specType : 'describe',
+ timeout : timeout
+ })
+
+ if (this.codeProcessed) this.scheduleSpecsLaunch()
+
+ this.sequentialSubTests.push(subTest)
+ },
+
+
+ /**
+ * This is an "exclusive" version of the regular {@link #it} spec. When such specs presents in some suite,
+ * the other regular specs at the same level will not be executed, only "exclusive" ones. Note, that even "regular" suites (`t.describe`) sections
+ * will be ignored, if they are on the same level with the exclusive `iit` section.
+ *
+ * @param {String} name The name or description of the spec
+ * @param {Function} code The code function for this spec. It will receive a test instance as the first argument which should be used for all assertion methods.
+ * @param {Number} [timeout] A maximum duration for this spec. If not provided {@link Siesta.Harness#subTestTimeout} value is used.
+ */
+ iit : function (name, code, timeout) {
+ this.it(name, code, timeout, true)
+ },
+
+
+ /**
+ * This is a no-op method, allowing you to quickly ignore some specs.
+ */
+ xit : function () {
+ },
+
+
+ /**
+ * This method starts a sub test with *spec* (in BDD terms). Such spec consists from one or more assertions (or *expectations*, *matchers*, etc) or other nested specs
+ * and/or suites. See the {@link #expect} method. The number of nesting levels is not limited. All specs of the same nesting level are executed sequentially.
+ *
+ * For example:
+ *
+ t.describe('A product', function (t) {
+
+ t.it('should have feature X', function (t) {
+ ...
+ })
+
+ t.it('should have feature Y', function (t) {
+ ...
+ })
+ })
+ *
+ * See also {@link #xit}, {@link #iit}
+ *
+ * @param {String} name The name or description of the spec
+ * @param {Function} code The code function for this spec. It will receive a test instance as the first argument which should be used for all assertion methods.
+ * @param {Number} [timeout] A maximum duration for this spec. If not provided {@link Siesta.Harness#subTestTimeout} value is used.
+ */
+ it : function (name, code, timeout, isExclusive) {
+ this.checkSpecFunction(code, 'it', name)
+
+ var subTest = this.getSubTest({
+ name : name,
+ run : code,
+
+ isExclusive : isExclusive,
+
+ specType : 'it',
+ timeout : timeout
+ })
+
+ if (this.codeProcessed) this.scheduleSpecsLaunch()
+
+ this.sequentialSubTests.push(subTest)
+ },
+
+
+ /**
+ * This method returns an "expectation" instance, which can be used to check various assertions about the passed value.
+ *
+ * **Note**, that every expectation has a special property `not`, that contains another expectation, but with the negated meaning.
+ *
+ * For example:
+ *
+
+ t.expect(1).toBe(1)
+ t.expect(1).not.toBe(2)
+
+ t.expect('Foo').toContain('oo')
+ t.expect('Foo').not.toContain('bar')
+
+
+ * Please refer to the documentation of the {@link Siesta.Test.BDD.Expectation} class for the list of available methods.
+ *
+ * @param {Mixed} value Any value, that will be assert about
+ * @return {Siesta.Test.BDD.Expectation} Expectation instance
+ */
+ expect : function (value) {
+ return new this.expectationClass({
+ t : this,
+ value : value
+ })
+ },
+
+
+ /**
+ * This method returns a *placeholder*, denoting any instance of the provided class constructor. Such placeholder can be used in various
+ * comparison assertions, like {@link #is}, {@link #isDeeply}, {@link Siesta.Test.BDD.Expectation#toBe expect().toBe()},
+ * {@link Siesta.Test.BDD.Expectation#toBe expect().toEqual()} and so on.
+ *
+ * For example:
+
+ t.is(1, t.any(Number))
+
+ t.expect(1).toBe(t.any(Number))
+
+ t.isDeeply({ name : 'John', age : 45 }, { name : 'John', age : t.any(Number))
+
+ t.expect({ name : 'John', age : 45 }).toEqual({ name : 'John', age : t.any(Number))
+
+ t.is(NaN, t.any(), 'When class constructor is not provided `t.any()` should match anything')
+
+ *
+ * @param {Function} clsConstructor A class constructor instances of which are denoted with this placeholder. As a special case if this argument
+ * is not provided, a placeholder will match any value.
+ *
+ * @return {Object} A placeholder object
+ */
+ any : function (clsConstructor) {
+ return new Siesta.Test.BDD.Placeholder({
+ clsConstructor : clsConstructor,
+ t : this,
+ context : this.global
+ })
+ },
+
+
+ scheduleSpecsLaunch : function () {
+ if (this.launchTimeout) return
+
+ var async = this.beginAsync()
+ var originalSetTimeout = this.originalSetTimeout
+ var me = this
+
+ this.launchTimeout = originalSetTimeout(function () {
+ me.endAsync(async)
+ me.launchTimeout = null
+
+ me.launchSpecs()
+ }, 0)
+ },
+
+
+ launchSpecs : function () {
+ var me = this
+ var sequentialSubTests = this.sequentialSubTests
+
+ this.sequentialSubTests = []
+
+ // hackish way to pass a config to `t.chain`
+ this.chain.actionDelay = 0
+
+ var exclusiveSubTests = []
+
+ Joose.A.each(sequentialSubTests, function (subTest) {
+ if (subTest.isExclusive) exclusiveSubTests.push(subTest)
+ })
+
+ this.chain(exclusiveSubTests.length ? exclusiveSubTests : sequentialSubTests)
+ }
+ },
+
+
+ override : {
+ afterLaunch : function () {
+ this.codeProcessed = true
+
+ this.launchSpecs()
+
+ this.SUPERARG(arguments)
+ }
+ }
+
+})
+//eof Siesta.Test.BDD
+;
+Role('Siesta.Test.Sub', {
+
+ has : {
+ isExclusive : false,
+ parent : { required : true }
+ },
+
+
+ methods : {
+
+ getExceptionCatcher : function () {
+ return this.parent.getExceptionCatcher()
+ },
+
+
+ getTestErrorClass : function () {
+ return this.parent.getTestErrorClass()
+ },
+
+
+ getStartTestAnchor : function () {
+ return this.parent.getStartTestAnchor()
+ },
+
+
+ expectGlobals : function () {
+ return this.parent.expectGlobals.apply(this.parent, arguments)
+ }
+ }
+
+})
+//eof Siesta.Test
+;
+/**
+@class Siesta.Test
+@mixin Siesta.Test.More
+@mixin Siesta.Test.Date
+@mixin Siesta.Test.Function
+@mixin Siesta.Test.BDD
+
+`Siesta.Test` is a base testing class in Siesta hierarchy. Its not supposed to be created manually, instead, the harness will create it for you.
+
+This file is a reference only, for a getting start guide and manual, please refer to Getting Started Guide.
+
+Please note: Each test will be run in **its own**, completely **isolated** and **clean** global scope. **There is no need to cleanup anything**.
+
+SYNOPSIS
+========
+
+ StartTest(function(t) {
+ t.diag("Sanity")
+
+ t.ok($, 'jQuery is here')
+
+ t.ok(Your.Project, 'My project is here')
+ t.ok(Your.Project.Util, '.. indeed')
+
+ setTimeout(function () {
+
+ t.ok(true, "True is ok")
+
+ }, 500)
+ })
+
+
+*/
+
+Class('Siesta.Test', {
+
+ does : [
+ Siesta.Util.Role.CanFormatStrings,
+ Siesta.Test.More,
+ Siesta.Test.Date,
+ Siesta.Test.Function,
+ Siesta.Test.BDD,
+ JooseX.Observable
+ ],
+
+
+ has : {
+ name : null,
+
+ url : { required : true },
+ urlExtractRegex : {
+ is : 'rwc',
+ lazy : function () {
+ return new RegExp(this.url.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1") + ':(\\d+)')
+ }
+ },
+
+ assertPlanned : null,
+ assertCount : 0,
+
+ // whether this test contains only "todo" assertions
+ isTodo : false,
+
+ results : {
+ lazy : function () {
+ return new Siesta.Result.SubTest({ description : this.name || 'Root', test : this })
+ }
+ },
+
+ run : { required : true },
+ startTestAnchor : null,
+ exceptionCatcher : null,
+ testErrorClass : null,
+
+ generation : function () {
+ return Math.random()
+ },
+
+ parent : null,
+ harness : { required : true },
+
+ /**
+ * @cfg {Number} isReadyTimeout
+ *
+ * Timeout in milliseconds to wait for test start. Default value is 10000. See also {@link #isReady}
+ */
+ isReadyTimeout : 10000,
+
+ // indicates that a test has thrown an exception (not related to failed assertions)
+ failed : false,
+ failedException : null, // stringified exception
+ failedExceptionType : null, // type of exception
+
+ // start and end date are stored as numbers (new Date() - 0)
+ // this is to allow sharing date instances between different contexts
+ startDate : null,
+ endDate : null,
+ lastActivityDate : null,
+ contentManager : null,
+
+ // the scope provider for the context of the test page
+ scopeProvider : null,
+ // the context of the test page
+ global : { required : true },
+
+ // the scope provider for the context of the test script
+ // usually the same as the `scopeProvider`, but may be different in case of using `separateContext` option
+ scriptScopeProvider : null,
+
+ transparentEx : false,
+
+ needDone : false,
+ isDone : false,
+
+ defaultTimeout : 15000,
+ // a default timeout for sub tests
+ subTestTimeout : null,
+ // a timeout of this particular test
+ timeout : null,
+
+ timeoutsCount : 1,
+ timeoutIds : Joose.I.Object,
+ idsToIndex : Joose.I.Object,
+ waitTitles : Joose.I.Object,
+
+
+ // indicates that test function has completed the execution (test may be still running due to async)
+ processed : false,
+
+ callback : null,
+
+ // Nbr of exceptions detected while running the test
+ nbrExceptions : 0,
+ testEndReported : false,
+
+ // only used for testing itself, otherwise should be always `true`
+ needToCleanup : true,
+
+ overrideSetTimeout : false,
+
+ originalSetTimeout : { required : true },
+ originalClearTimeout : { required : true },
+
+ sourceLineForAllAssertions : false,
+
+ $passCount : null,
+ $failCount : null
+ },
+
+
+ methods : {
+
+ initialize : function () {
+ // suppress bubblings of some events (JooseX.Observable does not provide better mechanism for that, yet)
+ this.on('teststart', function (event) {
+ if (this.parent) event.stopPropagation()
+ })
+
+ this.on('testfinalize', function (event) {
+ if (this.parent) event.stopPropagation()
+ })
+
+ this.on('beforetestfinalize', function (event) {
+ if (this.parent) event.stopPropagation()
+ })
+
+ this.on('beforetestfinalizeearly', function (event) {
+ if (this.parent) event.stopPropagation()
+ })
+
+ this.subTestTimeout = 2 * this.defaultTimeout
+ },
+
+ /**
+ * This method allows you to delay the start of the test, for example for performing some asynchronous setup code (like login into application).
+ *
+ * It is supposed to be overriden in the subclass of Siesta.Test and should return object with properties "ready" and "reason"
+ * ("reason" is only meaningful for "ready : false"). Test instance will poll this method and will only launch
+ * the test, when this method will return "ready : true". If waiting for this will take longer than {@link #isReadyTimeout} then, test
+ * will be launched anyway, but a failing assertion will be added to it.
+ *
+ * **Important** This method should always check the value returned by `this.SUPER` call.
+ *
+ * Typical example of using this method will be:
+ *
+
+ Class('My.Test.Class', {
+
+ isa : Siesta.Test.Browser,
+
+ has : {
+ isCustomSetupDone : false
+ },
+
+ override : {
+
+ isReady : function () {
+ var result = this.SUPERARG(arguments);
+
+ if (!result.ready) return result;
+
+ if (!this.isCustomSetupDone) return {
+ ready : false,
+ reason : "Waiting for `isCustomSetupDone` took too long - something wrong?"
+ }
+
+ return {
+ ready : true
+ }
+ },
+
+
+ start : function () {
+ var me = this;
+
+ Ext.Ajax.request({
+ url : 'do_login.php',
+
+ params : { ... },
+
+ success : function () {
+ me.isCustomSetupDone = true
+ }
+ })
+
+ this.SUPERARG(arguments)
+ }
+ },
+
+ ....
+ })
+
+ *
+ * @return {Object} Object with properties `{ ready : true/false, reason : 'description' }`
+ */
+ isReady: function() {
+ // this should allow us to wait until the presense of "run" function
+ // it will become available after call to StartTest method
+ // which some users may call asynchronously, after some delay
+ // see https://www.assembla.com/spaces/bryntum/tickets/379
+ // in this case test can not be configured using object as 1st argument for StartTest
+ this.run = this.run || this.getStartTestAnchor().args && this.getStartTestAnchor().args[ 0 ]
+
+ return {
+ ready : this.typeOf(this.run) == 'Function',
+ reason : 'No code provided to test'
+ }
+ },
+
+
+ isFromTheSameGeneration : function (test2) {
+ return this.generation == test2.generation
+ },
+
+
+ toString : function() {
+ return this.url
+ },
+
+
+ // deprecated
+ plan : function (value) {
+ if (this.assertPlanned != null) throw new Error("Test plan can't be changed")
+
+ this.assertPlanned = value
+ },
+
+
+ addResult : function (result) {
+ var isAssertion = result instanceof Siesta.Result.Assertion
+
+ if (isAssertion) result.isTodo = this.isTodo
+
+ // only allow to add diagnostic results and todo results after the end of test
+ // and only if "needDone" is enabled
+ if (isAssertion && (this.isDone || this.isFinished()) && !result.isTodo) {
+ if (!this.testEndReported) {
+ this.testEndReported = true
+
+ this.fail("Adding assertions after the test has finished.")
+ }
+ }
+
+ if (isAssertion && !result.index) {
+ result.index = ++this.assertCount
+ }
+
+ this.getResults().push(result)
+
+ // clear the cache
+ this.$passCount = this.$failCount = null
+
+ /**
+ * This event is fired when an individual test case receives a new result (assertion or diagnostic message).
+ *
+ * This event bubbles up to the {@link Siesta.Harness harness}, so you can observe it on the harness as well.
+ *
+ * @event testupdate
+ * @member Siesta.Test
+ * @param {JooseX.Observable.Event} event The event instance
+ * @param {Siesta.Test} test The test instance that just has started
+ * @param {Siesta.Result} result The new result. Instance of Siesta.Result.Assertion or Siesta.Result.Diagnostic classes
+ */
+ this.fireEvent('testupdate', this, result, this.getResults())
+
+ this.lastActivityDate = new Date();
+ },
+
+
+ /**
+ * This method output the diagnostic message.
+ * @param {String} desc The text of diagnostic message
+ */
+ diag : function (desc) {
+ this.addResult(new Siesta.Result.Diagnostic({
+ description : desc
+ }))
+ },
+
+
+ /**
+ * This method add the passed assertion to this test.
+ *
+ * @param {String} desc The description of the assertion
+ * @param {String/Object} annotation The string with additional description how exactly this assertion passes. Will be shown with monospace font.
+ * Can be also an object with the following properties:
+ * @param {String} annotation.annotation The actual annotation text
+ * @param {String} annotation.descTpl The template for the default description text. Will be used if user did not provide any description for
+ * assertion. Template can contain variables in braces. The values for variables are taken as properties of `annotation` parameters with the same name:
+ *
+
+ this.pass(desc, {
+ descTpl : '{value1} sounds like {value2}',
+ value1 : '1',
+ value2 : 'one
+ })
+
+ *
+ */
+ pass : function (desc, annotation, result) {
+ if (annotation && this.typeOf(annotation) != 'String') {
+ // create a default assertion description
+ if (!desc && annotation.descTpl) desc = this.formatString(annotation.descTpl, annotation)
+
+ // actual annotation
+ annotation = annotation.annotation
+ }
+
+ if (result) {
+ result.passed = true
+ result.description = desc || ''
+ result.annotation = annotation
+ }
+
+ this.addResult(result || new Siesta.Result.Assertion({
+ passed : true,
+
+ annotation : annotation,
+ description : desc || '',
+ sourceLine : (result && result.sourceLine) || (annotation && annotation.sourceLine) || this.sourceLineForAllAssertions && this.getSourceLine() || null
+ }))
+ },
+
+
+ /**
+ * This method returns a result of `Object.prototype.toString` applied to the passed argument. The `[object` and trailing `]` are trimmed.
+ *
+ * @param {Mixed} object
+ * @return {String} The name of the "type" for this object.
+ */
+ typeOf : function (object) {
+ return Object.prototype.toString.call(object).replace(/^\[object /, '').replace(/\]$/, '')
+ },
+
+ /**
+ * This method add the failed assertion to this test.
+ *
+ * @param {String} desc The description of the assertion
+ * @param {String/Object} annotation The additional description how exactly this assertion fails. Will be shown with monospace font.
+ *
+ * Can be either string or an object with the following properties. In the latter case a string will be constructed from the properties of the object.
+ *
+ * - `assertionName` - the name of assertion, will be shown in the 1st line, along with originating source line (in FF and Chrome only)
+ * - `got` - an arbitrary JavaScript object, when provided will be shown on the next line
+ * - `need` - an arbitrary JavaScript object, when provided will be shown on the next line
+ * - `gotDesc` - a prompt for "got", default value is "Got", but can be for example: "We have"
+ * - `needDesc` - a prompt for "need", default value is "Need", but can be for example: "We need"
+ * - `annotation` - A text to append on the last line, can contain some additional explanations
+ *
+ * The "got" and "need" values will be stringified to the "not quite JSON" notation. Notably the points of circular references will be
+ * marked with `[Circular]` marks and the values at 4th (and following) level of depth will be marked with triple points: `[ [ [ ... ] ] ]`
+ */
+ fail : function (desc, annotation, result) {
+ var sourceLine = (result && result.sourceLine) || (annotation && annotation.sourceLine) || this.getSourceLine()
+
+ if (annotation && this.typeOf(annotation) != 'String') {
+ if (!desc && annotation.descTpl) desc = this.formatString(annotation.descTpl, annotation)
+
+ var strings = []
+
+ var params = annotation
+ var annotation = params.annotation
+ var assertionName = params.assertionName
+ var hasGot = params.hasOwnProperty('got')
+ var hasNeed = params.hasOwnProperty('need')
+ var gotDesc = params.gotDesc || 'Got'
+ var needDesc = params.needDesc || 'Need'
+
+ if (!params.ownTextOnly && (assertionName || sourceLine)) strings.push(
+ 'Failed assertion ' + (assertionName ? '`' + assertionName + '` ' : '') + this.formatSourceLine(sourceLine)
+ )
+
+ if (hasGot && hasNeed) {
+ var max = Math.max(gotDesc.length, needDesc.length)
+
+ gotDesc = this.appendSpaces(gotDesc, max - gotDesc.length + 1)
+ needDesc = this.appendSpaces(needDesc, max - needDesc.length + 1)
+ }
+
+ if (hasGot) strings.push(gotDesc + ': ' + Siesta.Util.Serializer.stringify(params.got))
+ if (hasNeed) strings.push(needDesc + ': ' + Siesta.Util.Serializer.stringify(params.need))
+
+ if (annotation) strings.push(annotation)
+
+ annotation = strings.join('\n')
+ }
+
+ if (result) {
+ // Failing a pending waitFor operation
+ result.name = assertionName;
+ result.passed = false;
+ result.annotation = annotation;
+ result.description = desc;
+ }
+
+ this.addResult(result || new Siesta.Result.Assertion({
+ name : assertionName,
+ passed : false,
+ sourceLine : sourceLine,
+
+ annotation : annotation,
+ description : desc
+ }))
+
+ if (this.harness.activateDebuggerOnFail) {
+ eval("debugger");
+ }
+
+ if (this.harness.breakOnFail) {
+ this.finalize(true);
+ throw 'Assertion failed, test execution aborted';
+ }
+ },
+
+
+ getSource : function () {
+ return this.contentManager.getContentOf(this.url)
+ },
+
+
+ getSourceLine : function () {
+ try {
+ throw new Error()
+ } catch (e) {
+ if (e.stack) {
+ var match = e.stack.match(this.urlExtractRegex())
+
+ if (match) return match[ 1 ]
+ }
+
+ return null
+ }
+ },
+
+
+ getStartTestAnchor : function () {
+ return this.startTestAnchor
+ },
+
+
+ getExceptionCatcher : function () {
+ return this.exceptionCatcher
+ },
+
+
+ getTestErrorClass : function () {
+ return this.testErrorClass
+ },
+
+
+ processCallbackFromTest : function (callback, args, scope) {
+ var me = this
+
+ if (!callback) return true;
+
+ if (this.transparentEx) {
+ callback.apply(scope || this.global, args || [])
+ } else {
+ var e = this.getExceptionCatcher()(function(){
+ callback.apply(scope || me.global, args || [])
+ })
+
+ if (e) {
+ this.failWithException(e)
+
+ // flow should be interrupted - exception detected
+ return false
+ }
+ }
+
+ // flow can be continued
+ return true
+ },
+
+
+ getStackTrace : function (e) {
+ if (Object(e) !== e) return null
+ if (!e.stack) return null
+
+ var text = e.stack + '';
+ var isFirefox = /^@/.test(text)
+ var lines = text.split('\n')
+
+ var result = []
+ var match
+
+ for (var i = 0; i < lines.length; i++) {
+ if (!lines[ i ]) continue
+
+ if (!i) {
+ if (isFirefox)
+ result.push(e + '')
+ else {
+ result.push(lines[ i ])
+ continue;
+ }
+ }
+
+ if (isFirefox) {
+ match = /@(.*?):(\d+)/.exec(lines[ i ]);
+
+ // the format of stack trace in Firefox has changed, 080_exception_parsing should fail
+ if (!match) return null
+
+ result.push(' at line ' + match[ 2 ] + ' of ' + match[ 1 ])
+ } else {
+ match = /\s*at\s(.*?):(\d+):(\d+)/.exec(lines[ i ]);
+
+ // the format of stack trace in Chrome has changed, 080_exception_parsing should fail
+ if (!match) return null
+
+ result.push(' at line ' + match[ 2 ] + ', character ' + match[ 3 ] + ', of ' + match[ 1 ])
+ }
+ }
+
+ if (!result.length) return null
+
+ return result
+ },
+
+
+ formatSourceLine : function (sourceLine) {
+ return sourceLine ? 'at line ' + sourceLine + ' of ' + this.url : ''
+ },
+
+
+ appendSpaces : function (str, num) {
+ var spaces = ''
+
+ while (num--) spaces += ' '
+
+ return str + spaces
+ },
+
+
+ eachAssertion : function (func, scope) {
+ scope = scope || this
+
+ this.getResults().each(function (result) {
+ if (result instanceof Siesta.Result.Assertion) func.call(scope, result)
+ })
+ },
+
+
+ eachSubTest : function (func, scope) {
+ scope = scope || this
+
+ this.getResults().each(function (result) {
+ if (result instanceof Siesta.Result.SubTest) func.call(scope, result.test)
+ })
+ },
+
+
+ eachChildTest : function (func, scope) {
+ scope = scope || this
+
+ this.getResults().eachChild(function (result) {
+ if (result instanceof Siesta.Result.SubTest) func.call(scope, result.test)
+ })
+ },
+
+
+ countKeys : function (object) {
+ var counter = 0
+
+ Joose.O.eachOwn(object, function () {
+ counter++
+ })
+
+ return counter
+ },
+
+
+ /**
+ * This method performs a deep comparison of the passed JSON objects. Objects must not contain cyclic references.
+ * You can use this method in your own assertions.
+ *
+ * @param {Mixed} obj1 The 1st object to compare
+ * @param {Mixed} obj2 The 2nd object to compare
+ * @param {Boolean} strict When passed the `true` value, the comparison of the primitive values will be performed with the
+ * `===` operator (so [ 1 ] and [ "1" ] object will be different). Additionally, when this flag is set to `true`, then
+ * when comparing Function, RegExp and Date instances, additional check that objects contains the same set of own properties ("hasOwnProperty")
+ * will be performed.
+ * @param {Boolean} onlyPrimitives When set to `true`, the function will not recurse into composite objects (like [] or {}) and will just report that
+ * objects are different. Use this mode when you are only interesetd in comparison of primitive values (numbers, strings, etc).
+ * @param {Boolean} asObjects When set to `true`, the function will compare various special Object instances, like Functions, RegExp etc,
+ * by comparison of there properties only and not taking the anything else into account.
+ * @return {Boolean} `true` if the passed objects are equal
+ */
+ compareObjects : function (obj1, obj2, strict, onlyPrimitives, asObjects) {
+ var obj1IsPlaceholder = obj1 instanceof Siesta.Test.BDD.Placeholder
+ var obj2IsPlaceholder = obj2 instanceof Siesta.Test.BDD.Placeholder
+
+ if (strict) {
+ if (obj1 === obj2) return true
+ } else
+ if (obj1 == obj2) return true
+
+ if (obj1IsPlaceholder && obj2IsPlaceholder)
+ return obj1.equalsTo(obj2)
+ else if (obj2IsPlaceholder)
+ return obj2.equalsTo(obj1)
+ else if (obj1IsPlaceholder)
+ return obj1.equalsTo(obj2)
+
+ if (onlyPrimitives) return false
+
+ var type1 = this.typeOf(obj1)
+ var type2 = this.typeOf(obj2)
+
+ if (type1 != type2) return false
+
+ var me = this
+
+ if (type1 == 'Object' || asObjects)
+ if (this.countKeys(obj1) != this.countKeys(obj2))
+ return false
+ else {
+ var res = Joose.O.eachOwn(obj1, function (value, name) {
+
+ if (!me.compareObjects(value, obj2[ name ], strict)) return false
+ })
+
+ return res === false ? false : true
+ }
+
+ if (type1 == 'Array')
+ if (obj1.length != obj2.length)
+ return false
+ else {
+ for (var i = 0; i < obj1.length; i++)
+ if (!this.compareObjects(obj1[ i ], obj2[ i ], strict)) return false
+
+ return true
+ }
+
+ if (type1 == 'Function')
+ return obj1.toString() == obj2.toString() && (!strict || this.compareObjects(obj1, obj2, strict, false, true))
+
+ if (type1 == 'RegExp')
+ return obj1.source == obj2.source && obj1.global == obj2.global && obj1.ignoreCase == obj2.ignoreCase
+ && obj1.multiline == obj2.multiline && (!strict || this.compareObjects(obj1, obj2, strict, false, true))
+
+ if (type1 == 'Date') return !Boolean(obj1 - obj2) && (!strict || this.compareObjects(obj1, obj2, strict, false, true))
+
+ return false
+ },
+
+
+ /**
+ * This assertion passes when the supplied `value` evalutes to `true` and fails otherwise.
+ *
+ * @param {Mixed} value The value, indicating wheter assertions passes or fails
+ * @param {String} desc The description of the assertion
+ */
+ ok : function (value, desc) {
+ if (value)
+ this.pass(desc, {
+ descTpl : '`{value}` is a "truthy" value',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'ok',
+ got : value,
+ annotation : 'Need "truthy" value'
+ })
+ },
+
+
+ notok : function () {
+ this.notOk.apply(this, arguments)
+ },
+
+ /**
+ * This assertion passes when the supplied `value` evalutes to `false` and fails otherwise.
+ *
+ * It has a synonym - `notok`.
+ *
+ * @param {Mixed} value The value, indicating wheter assertions passes or fails
+ * @param {String} desc The description of the assertion
+ */
+ notOk : function (value, desc) {
+ if (!value)
+ this.pass(desc, {
+ descTpl : '`{value}` is not a "truthy" value',
+ value : value
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'notOk',
+ got : value,
+ annotation : 'Need "falsy" value'
+ })
+ },
+
+
+ /**
+ * This assertion passes when the comparison of 1st and 2nd arguments with `==` operator returns true and fails otherwise.
+ *
+ * As a special case, one or both arguments can be *placeholders*, generated with method {@link #any}.
+ *
+ * @param {Mixed} got The value "we have" - will be shown as "Got:" in case of failure
+ * @param {Mixed} expected The value "we expect" - will be shown as "Need:" in case of failure
+ * @param {String} desc The description of the assertion
+ */
+ is : function (got, expected, desc) {
+ if (expected && got instanceof this.global.Date) {
+ this.isDateEqual(got, expected, desc);
+ } else if (this.compareObjects(got, expected, false, true))
+ this.pass(desc, {
+ descTpl : '`{got}` is equal to `{expected}`',
+ got : got,
+ expected : expected
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'is',
+ got : got,
+ need : expected
+ })
+ },
+
+
+
+ isnot : function () {
+ this.isNot.apply(this, arguments)
+ },
+
+ isnt : function () {
+ this.isNot.apply(this, arguments)
+ },
+
+
+ /**
+ * This assertion passes when the comparison of 1st and 2nd arguments with `!=` operator returns true and fails otherwise.
+ * It has synonyms - `isnot` and `isnt`.
+ *
+ * As a special case, one or both arguments can be instance of {@link Siesta.Test.BDD.Placeholder} class, generated with method {@link #any}.
+ *
+ * @param {Mixed} got The value "we have" - will be shown as "Got:" in case of failure
+ * @param {Mixed} expected The value "we expect" - will be shown as "Need:" in case of failure
+ * @param {String} desc The description of the assertion
+ */
+ isNot : function (got, expected, desc) {
+ if (!this.compareObjects(got, expected, false, true))
+ this.pass(desc, {
+ descTpl : '`{got}` is not equal to `{expected}`',
+ got : got,
+ expected : expected
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isnt',
+ got : got,
+ need : expected,
+ needDesc : 'Need, not'
+ })
+ },
+
+
+ /**
+ * This assertion passes when the comparison of 1st and 2nd arguments with `===` operator returns true and fails otherwise.
+ *
+ * As a special case, one or both arguments can be instance of {@link Siesta.Test.BDD.Placeholder} class, generated with method {@link #any}.
+ *
+ * @param {Mixed} got The value "we have" - will be shown as "Got:" in case of failure
+ * @param {Mixed} expected The value "we expect" - will be shown as "Need:" in case of failure
+ * @param {String} desc The description of the assertion
+ */
+ isStrict : function (got, expected, desc) {
+ if (this.compareObjects(got, expected, true, true))
+ this.pass(desc, {
+ descTpl : '`{got}` is strictly equal to `{expected}`',
+ got : got,
+ expected : expected
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isStrict',
+ got : got,
+ need : expected,
+ needDesc : 'Need strictly'
+ })
+ },
+
+
+ isntStrict : function () {
+ this.isNotStrict.apply(this, arguments)
+ },
+
+ /**
+ * This assertion passes when the comparison of 1st and 2nd arguments with `!==` operator returns true and fails otherwise.
+ * It has synonyms - `isntStrict`.
+ *
+ * As a special case, one or both arguments can be instance of {@link Siesta.Test.BDD.Placeholder} class, generated with method {@link #any}.
+ *
+ * @param {Mixed} got The value "we have" - will be shown as "Got:" in case of failure
+ * @param {Mixed} expected The value "we expect" - will be shown as "Need:" in case of failure
+ * @param {String} desc The description of the assertion
+ */
+ isNotStrict : function (got, expected, desc) {
+ if (!this.compareObjects(got, expected, true, true))
+ this.pass(desc, {
+ descTpl : '`{got}` is not strictly equal to `{expected}`',
+ got : got,
+ expected : expected
+ })
+ else
+ this.fail(desc, {
+ assertionName : 'isntStrict',
+ got : got,
+ need : expected,
+ needDesc : 'Need, strictly not'
+ })
+ },
+
+
+ /**
+ * This method starts the "asynchronous frame". The test will wait for all asynchronous frames to complete before it will finalize.
+ * The frame can be finished with the {@link #endWait} call. Unlike the {@link #beginAsync}, this method requires you to provide
+ * the unique id for the asynchronous frame.
+ *
+ * For example:
+ *
+ * t.wait("require")
+ *
+ * Ext.require('Some.Class', function () {
+ *
+ * t.ok(Some.Class, 'Some class was loaded')
+ *
+ * t.endWait("require")
+ * })
+ *
+ *
+ * @param {String} title The unique id for the asynchronous frame.
+ * @param {String} howLong The maximum time (in ms) to wait until force the finalization of this async frame. Optional. Default time is 15000 ms.
+ */
+ wait : function (title, howLong) {
+ if (this.waitTitles.hasOwnProperty(title)) throw new Error("Already doing a `wait` with title [" + title + "]")
+
+ return this.waitTitles[ title ] = this.beginAsync(howLong)
+ },
+
+
+ /**
+ * This method finalize the "asynchronous frame" started with {@link #wait}.
+ *
+ * @param {String} title The id of frame to finalize, which was previously passed to {@link #wait} method
+ */
+ endWait : function (title) {
+ if (!this.waitTitles.hasOwnProperty(title)) throw new Error("There were no call to `wait` with title [" + title + "]")
+
+ this.endAsync(this.waitTitles[ title ])
+
+ delete this.waitTitles[ title ]
+ },
+
+
+
+ /**
+ * This method starts the "asynchronous frame". The test will wait for all asynchronous frames to complete before it will finalize.
+ * The frame should be finished with the {@link #endAsync} call within the provided `time`, otherwise a failure will be reported.
+ *
+ * For example:
+ *
+ * var async = t.beginAsync()
+ *
+ * Ext.require('Some.Class', function () {
+ *
+ * t.ok(Some.Class, 'Some class was loaded')
+ *
+ * t.endAsync(async)
+ * })
+ *
+ *
+ * @param {Number} time The maximum time (in ms) to wait until force the finalization of this async frame. Optional. Default time is 15000 ms.
+ * @param {Function} errback Optional. The function to call in case the call to {@link #endAsync} was not detected withing `time`. If function
+ * will return any "truthy" value, the failure will not be reported (you can report own failure with this errback).
+ *
+ * @return {Object} The frame object, which can be used in {@link #endAsync} call
+ */
+ beginAsync : function (time, errback) {
+ time = time || this.defaultTimeout
+
+ var me = this
+ var originalSetTimeout = this.originalSetTimeout
+
+ var index = this.timeoutsCount++
+
+ // in NodeJS `setTimeout` returns an object and not a simple ID, so we try hard to store that object under unique index
+ // also using `setTimeout` from the scope of test - as timeouts in different scopes in browsers are mis-synchronized
+ // can't just use `this.originalSetTimeout` because of scoping issues
+ var timeoutId = originalSetTimeout(function () {
+
+ if (me.hasAsyncFrame(index)) {
+ if (!errback || !errback.call(me, me)) me.fail('No matching `endAsync` call within ' + time + 'ms')
+
+ me.endAsync(index)
+ }
+ }, time)
+
+ this.timeoutIds[ index ] = timeoutId
+
+ return index
+ },
+
+
+ hasAsyncFrame : function (index) {
+ return this.timeoutIds.hasOwnProperty(index)
+ },
+
+
+ /**
+ * This method finalize the "asynchronous frame" started with {@link #beginAsync}.
+ *
+ * @param {Object} frame The frame to finalize (returned by {@link #beginAsync} method
+ */
+ endAsync : function (index) {
+ var originalSetTimeout = this.originalSetTimeout
+ var originalClearTimeout = this.originalClearTimeout || this.global.clearTimeout
+ var counter = 0
+
+ if (index == null) Joose.O.each(this.timeoutIds, function (timeoutId, indx) {
+ index = indx
+ if (counter++) throw new Error("Calls to endAsync without argument should only be performed if you have single beginAsync statement")
+ })
+
+ var timeoutId = this.timeoutIds[ index ]
+
+ // need to call in this way for IE < 9
+ originalClearTimeout(timeoutId)
+ delete this.timeoutIds[ index ]
+
+ var me = this
+
+ if (this.processed && !this.isFinished())
+ // to allow potential call to `done` after `endAsync`
+ originalSetTimeout(function () {
+ me.finalize()
+ }, 1)
+ },
+
+
+ clearTimeouts : function () {
+ var originalClearTimeout = this.originalClearTimeout
+
+ Joose.O.each(this.timeoutIds, function (value, id) {
+ originalClearTimeout(value)
+ })
+
+ this.timeoutIds = {}
+ },
+
+
+ // deprecated
+ skipIf : function (condition, why, code, howMany) {
+ howMany = howMany || 1
+
+ if (condition) {
+
+ for (var i = 1; i <= howMany; i++) this.addResult(new Siesta.Result.Assertion({
+ passed : true,
+ isSkipped : true,
+
+ description : 'SKIPPED: ' + why
+ }))
+
+ } else
+ code()
+ },
+
+
+ // deprecated
+ skip : function (why, code, howMany) {
+ this.skipIf(true, why, code, howMany)
+ },
+
+
+ processSubTestConfig : function (config) {
+ return Joose.O.extend({
+ trait : Siesta.Test.Sub,
+
+ parent : this,
+
+ isTodo : this.isTodo,
+ transparentEx : this.transparentEx,
+
+ waitForTimeout : this.waitForTimeout,
+ waitForPollInterval : this.waitForPollInterval,
+ defaultTimeout : this.defaultTimeout,
+ timeout : this.subTestTimeout,
+
+ global : this.global,
+ url : this.url,
+ scopeProvider : this.scopeProvider,
+ harness : this.harness,
+ generation : this.generation,
+
+ overrideSetTimeout : false,
+ originalSetTimeout : this.originalSetTimeout,
+ originalClearTimeout : this.originalClearTimeout,
+
+ autoCheckGlobals : false,
+ needToCleanup : false
+ }, config)
+ },
+
+
+ /**
+ * Returns a new instance of the test class, configured as being a "sub test" of the current test.
+ *
+ * The number of nesting levels is not limited - ie sub-tests may have own sub-tests.
+ *
+ * Note, that this method does not starts the sub test, but only instatiate it. To start the sub test, use {@link #launchSubTest} method.
+ *
+ * @param {String} name The name of the test. Will be used in the UI, as the parent node name in the assertions tree
+ * @param {Function} code A function with test code. Will receive a test instance as the 1st argument.
+ * @param {Number} [timeout] A maximum duration (in ms) for this sub test. If test will not complete within this time,
+ * it will be considered failed. If not provided, the {@link Siesta.Harness#subTestTimeout} value is used.
+ *
+ * @return {Siesta.Test} A sub test instance
+ */
+ getSubTest : function (arg1, arg2, arg3) {
+ var config
+
+ if (arguments.length == 2 || arguments.length == 3)
+ config = {
+ name : arg1,
+ run : arg2,
+ timeout : arg3
+ }
+ else if (arguments.length == 1 && this.typeOf(arg1) == 'Function')
+ config = {
+ name : 'Sub test',
+ run : arg1
+ }
+
+ config = config || arg1 || {}
+
+ // pass-through only valid timeout values
+ if (config.timeout == null) delete config.timeout
+
+ var name = config.name
+
+ if (!config.run) {
+ this.failWithException("Code body is not provided for sub test [" + name + "]")
+ throw new Error("Code body is not provided for sub test [" + name + "]")
+ }
+ if (!config.run.length) {
+ this.failWithException('Code body of sub test [' + name + '] does not declare a test instance as 1st argument')
+ throw new Error('Code body of sub test [' + name + '] does not declare a test instance as 1st argument')
+ }
+
+ return new (config.meta || this.constructor)(this.processSubTestConfig(config))
+ },
+
+
+ /**
+ * This method launch the provided sub test instance.
+ *
+ * @param {Siesta.Test} subTest A test instance to launch
+ * @param {Function} callback A function to call, after the test is completed. This function is called regardless from the test execution result.
+ */
+ launchSubTest : function (subTest, callback) {
+ var me = this
+
+ var async = this.beginAsync(subTest.timeout || this.subTestTimeout, function () {
+ me.fail('Subtest ' + (subTest.name ? '[' + subTest.name + ']' : '') +' failed to finish within ' + this.subTestTimeout + 'ms')
+
+ testEndListener.remove()
+
+ subTest.finalize(true)
+
+ callback && callback(subTest)
+
+ return true
+ })
+
+ var testEndListener = subTest.on('testfinalize', function () {
+ me.endAsync(async)
+
+ callback && callback(subTest)
+ })
+
+ this.addResult(subTest.getResults())
+
+ subTest.start()
+ },
+
+
+ /**
+ * With this method you can mark a group of assertions as "todo", assuming they most likely will fail,
+ * but it's still worth to try to run them.
+ * The supplied `code` function will be run, it will receive a new test instance as the 1st argument,
+ * which should be used for assertion checks (and not the primary test instance, received from `StartTest`).
+ *
+ * Assertions, failed inside of the `code` block will be still treated by harness as "green".
+ * Assertions, passed inside of the `code` block will be treated by harness as bonus ones and highlighted.
+ *
+ * See also {@link Siesta.Test.ExtJS#knownBugIn} method. Note, that this method will start a new {@link #subTest sub test}.
+ *
+ * For example:
+
+ t.todo('Scheduled for 4.1.x release', function (todo) {
+
+ var treePanel = new Ext.tree.Panel()
+
+ todo.is(treePanel.getView().store, treePanel.store, 'NodeStore and TreeStore have been merged and there is only 1 store now');
+ })
+
+ * @param {String} why The reason/description for the todo
+ * @param {Function} code A function, wrapping the "todo" assertions. This function will receive a special test class instance
+ * which should be used for assertion checks
+ */
+ todo : function (why, code, callback) {
+ if (this.typeOf(why) == 'Function') why = [ code, code = why ][ 0 ]
+
+ var todo = this.getSubTest({
+ name : why,
+
+ run : code,
+
+ isTodo : true,
+ transparentEx : false
+ })
+
+ this.launchSubTest(todo, callback)
+ },
+
+
+ /**
+ * This method starts a new sub test. Sub tests have separate order of assertions. In the browser UI,
+ * sub tests are presented with the "parent" node of the assertions tree. Sub tests are useful if you want to test
+ * several asynchronous processes in parallel, and would like to see assertions from every process separated.
+ *
+ * Sub tests may have their own sub tests, the number of nesting levels is not limited.
+ *
+ * Sub test can contain asynchronous methods as any other tests. Sub tests are considered completed
+ * only when all of its asynchronous methods have completed *and* all of its sub-tests are completed too.
+ *
+ * For example:
+ *
+
+ t.subTest('Load 1st store', function (t) {
+ var async = t.beginAsync()
+
+ store1.load({
+ callback : function () {
+ t.endAsync(async);
+ t.isGreater(store1.getCount(), 0, "Store1 has been loaded")
+ }
+ })
+ })
+
+ t.subTest('Load 2nd store', function (t) {
+ var async = t.beginAsync()
+
+ store2.load({
+ callback : function () {
+ t.endAsync(async);
+ t.isGreater(store2.getCount(), 0, "Store2 has been loaded")
+ }
+ })
+ })
+
+ * Note, that sub test starts right away, w/o waiting for any previous sub tests to complete. If you'd like to run several sub-tests
+ * sequentially, use {@link #chain} method in combination with {@link #getSubTest} method.
+ *
+ * @param {String} desc The name of the sub test. Will be shown as the name of the parent node in assertion tree.
+ * @param {Function} code The test function to execute. It will receive a test instance as 1st argument. This test instance *must* be
+ * used for assertions inside of the test function
+ * @param {Function} callback The callback to execute after the sub test completes (either successfully or not)
+ * @param {Number} [timeout] A maximum duration (in ms) for this sub test. If test will not complete within this time,
+ * it will be considered failed. If not provided, the {@link Siesta.Harness#subTestTimeout} value is used.
+ */
+ subTest : function (desc, code, callback, timeout) {
+ var subTest = this.getSubTest({
+ name : desc || "Sub test",
+
+ timeout : timeout,
+
+ run : code
+ })
+
+ this.launchSubTest(subTest, callback)
+ },
+
+
+ failWithException : function (e) {
+ this.failed = true
+
+ var stackTrace = this.getStackTrace(e)
+ var stringified = e + ''
+ var annotation = (stackTrace || []).join('\n')
+
+ this.failedException = stringified
+ this.failedExceptionType = this.typeOf(e)
+
+ // prepend the exception message to the stack trace if its not already there
+ if (annotation.indexOf(stringified) == -1) annotation = stringified + annotation
+
+ this.addResult(new Siesta.Result.Assertion({
+ isException : true,
+ exceptionType : this.failedExceptionType,
+ passed : false,
+ description : (this.parent ? "Sub-test `" + this.name + "`" : 'Test ') + ' threw an exception',
+ annotation : annotation
+ }))
+
+
+ /**
+ * This event is fired when an individual test case has thrown an exception.
+ *
+ * This event bubbles up to the {@link Siesta.Harness harness}, so you can observe it on the harness as well.
+ *
+ * @event testfailedwithexception
+ * @member Siesta.Test
+ * @param {JooseX.Observable.Event} event The event instance
+ * @param {Siesta.Test} test The test instance that just threw an exception
+ * @param {Object} exception The exception thrown
+ */
+ this.fireEvent('testfailedwithexception', this, e, stackTrace);
+
+ this.finalize(true)
+ },
+
+
+ start : function (alreadyFailedWithException) {
+ if (this.startDate) {
+ throw 'Test has already been started';
+ }
+
+ this.startDate = new Date() - 0
+
+ /**
+ * This event is fired when an individual test case starts. When *started*, the test may still be waiting for the {@link #isReady} condition
+ * to be fullfilled. Once all conditions are fullfilled the test will be *launched*.
+ *
+ * This event bubbles up to the {@link Siesta.Harness harness}, you can observe it on harness as well.
+ *
+ * @event teststart
+ * @member Siesta.Test
+ * @param {JooseX.Observable.Event} event The event instance
+ * @param {Siesta.Test} test The test instance that just has started
+ */
+ this.fireEvent('teststart', this);
+
+ if (alreadyFailedWithException) {
+ this.failWithException(alreadyFailedWithException)
+
+ return
+ }
+
+ var me = this;
+ var errorMessage;
+ var readyRes = me.isReady();
+
+ if (readyRes.ready) {
+ // We're ready to go
+ me.launch();
+ } else {
+ // Need to wait for isReady to give green light
+ var timeout = setTimeout(function () {
+ clearInterval(interval)
+ me.launch(errorMessage)
+
+ }, me.isReadyTimeout)
+
+ var interval = setInterval(function(){
+ readyRes = me.isReady();
+
+ if (readyRes.ready) {
+ clearInterval(interval)
+ clearTimeout(timeout)
+ me.launch();
+ } else {
+ errorMessage = readyRes.reason || errorMessage;
+ }
+ }, 100);
+ }
+ },
+
+
+ launch : function (errorMessage) {
+ if (errorMessage) {
+ this.fail('Wait for test start condition took too long', {
+ annotation : errorMessage
+ })
+ }
+
+ var me = this
+ var global = this.global
+
+ var scopeProvider = this.scopeProvider
+
+ var originalSetTimeout = this.originalSetTimeout
+ var originalClearTimeout = this.originalClearTimeout
+
+ // this.overrideSetTimeout
+ if (this.overrideSetTimeout) {
+ // see http://www.adequatelygood.com/2011/4/Replacing-setTimeout-Globally
+ scopeProvider.runCode('var setTimeout, clearTimeout;')
+
+ global.setTimeout = function (func, delay) {
+
+ var index = me.timeoutsCount++
+
+ // in NodeJS `setTimeout` returns an object and not a simple ID, so we try hard to store that object under unique index
+ // also using `setTimeout` from the scope of test - as timeouts in different scopes in browsers are mis-synchronized
+ var timeoutId = originalSetTimeout(function () {
+ originalClearTimeout(timeoutId)
+ delete me.timeoutIds[ index ]
+
+ // if the test func has been executed, but the test was not finalized yet - then we should try to finalize it
+ if (me.processed && !me.isFinished())
+ // we are doing that after slight delay, potentially allowing to setup some other async frames in the "func" below
+ originalSetTimeout(function () {
+ me.finalize()
+ }, 1)
+
+ func()
+
+ }, delay)
+
+ // in NodeJS saves the index of the timeout descriptor to the descriptor
+ if (typeof timeoutId == 'object')
+ timeoutId.__index = index
+ else
+ // in browser (where `timeoutId` is a number) - to the `idsToIndex` hash
+ me.idsToIndex[ timeoutId ] = index
+
+ return me.timeoutIds[ index ] = timeoutId
+ }
+
+ global.clearTimeout = function (id) {
+ if (id == null) return
+
+ var index
+
+ // in NodeJS `setTimeout` returns an object and not a simple ID
+ if (typeof id == 'object') {
+ index = id.__index
+ if (me.timeoutIds[ index ] != id) throw "Incorrect state"
+ } else {
+ index = me.idsToIndex[ id ]
+
+ delete me.idsToIndex[ id ]
+ }
+
+ originalClearTimeout(id)
+
+ if (index != null) delete me.timeoutIds[ index ]
+
+ // if the test func has been executed, but the test was not finalized yet - then we should try to finalize it
+ if (me.processed && !me.isFinished())
+ // we are doing that after slight delay, potentially allowing to setup some other async frames after the "clearTimeout" will complete
+ originalSetTimeout(function () {
+ me.finalize()
+ }, 1)
+ }
+ }
+ // eof this.overrideSetTimeout
+
+ // we only don't need to cleanup up when doing a self-testing or for sub-tests
+ if (this.needToCleanup) {
+ scopeProvider.beforeCleanupCallback = function () {
+ // if scope cleanup happens most probably user has restarted the test and is not interested in the results
+ // of previous launch
+ // finalizing the previous test in such case
+ if (!me.isFinished()) me.finalize(true)
+
+ if (me.overrideSetTimeout) {
+ global.setTimeout = originalSetTimeout
+ global.clearTimeout = originalClearTimeout
+ }
+
+ // cleanup the closures just in case (probably useful for IE)
+ originalSetTimeout = originalClearTimeout = null
+ global = run = null
+
+ me.eachSubTest(function (subTest) {
+ subTest.originalSetTimeout = subTest.originalClearTimeout = null
+ subTest.global = subTest.run = null
+ subTest.exceptionCatcher = subTest.testErrorClass = null
+ subTest.startTestAnchor = null
+ })
+ }
+ }
+
+ var run = this.run
+
+ if (this.transparentEx)
+ run(me)
+ else
+ var e = this.getExceptionCatcher()(function(){
+ run(me)
+ })
+
+ this.afterLaunch(e)
+ },
+
+ // a method executed after the "run" function has been ran - used in BDD role for example
+ afterLaunch : function (e) {
+ if (e)
+ this.failWithException(e)
+ else
+ this.finalize()
+ },
+
+
+ finalize : function (force) {
+ if (this.isFinished()) return
+
+ this.processed = true
+
+ if (force) {
+ this.clearTimeouts()
+
+// this.eachChildTest(function (childTest) { childTest.finalize(true) })
+ }
+
+ if (!Joose.O.isEmpty(this.timeoutIds)) {
+ if (
+ !this.__timeoutWarning && this.overrideSetTimeout && this.lastActivityDate &&
+ new Date() - this.lastActivityDate > this.defaultTimeout * 2
+ ) {
+ this.diag('Your test is still considered to be running, if this is unexpected please see console for more information');
+ this.warn('Your test [' + this.url + '] has not finalized, most likely since a timer (setTimeout) is still active. ' +
+ 'If this is the expected behavior, try setting "overrideSetTimeout : false" on your Harness configuration.');
+ this.__timeoutWarning = true;
+ }
+
+ return
+ }
+
+ if (!this.needDone && !this.isDone) {
+ // this is early "testfinalize" hook, we need "early" and "usual" hooks, since we want globals check to be the last assertion
+ this.fireEvent('beforetestfinalizeearly')
+
+ /**
+ * This event is fired before the individual test case ends (no any corresponded harness actions will be run yet).
+ *
+ * This event bubbles up to the {@link Siesta.Harness harness}, you can observe it on harness as well.
+ *
+ * @event beforetestfinalize
+ * @member Siesta.Test
+ * @param {JooseX.Observable.Event} event The event instance
+ * @param {Siesta.Test} test The test instance that is about to finalize
+ */
+ this.fireEvent('beforetestfinalize');
+ }
+
+ this.endDate = new Date() - 0
+
+ if (!this.parent) this.addResult(new Siesta.Result.Summary({
+ isFailed : this.isFailed(),
+ description : this.getSummaryMessage()
+ }))
+
+ /**
+ * This event is fired when an individual test case ends (either because it has completed correctly or thrown an exception).
+ *
+ * This event bubbles up to the {@link Siesta.Harness harness}, you can observe it on the harness as well.
+ *
+ * @event testfinalize
+ * @member Siesta.Test
+ * @param {JooseX.Observable.Event} event The event instance
+ * @param {Siesta.Test} test The test instance that just has completed
+ */
+ this.fireEvent('testfinalize', this);
+
+ // a test end event that bubbles
+ this.fireEvent('testendbubbling', this);
+
+ this.callback && this.callback()
+ },
+
+
+ getSummaryMessage : function (lineBreaks) {
+ var res = []
+
+ var passCount = this.getPassCount()
+ var failCount = this.getFailCount()
+ var assertPlanned = this.assertPlanned
+ var total = failCount + passCount
+
+ res.push('Passed: ' + passCount)
+ res.push('Failed: ' + failCount)
+
+ if (!this.failed) {
+ // there was a t.plan() call
+ if (assertPlanned != null) {
+ if (total < assertPlanned)
+ res.push('Looks like you planned ' + assertPlanned + ' tests, but ran only ' + total)
+
+ if (total > assertPlanned)
+ res.push('Looks like you planned ' + assertPlanned + ' tests, but ran ' + (total - assertPlanned) + ' extra tests, ' + total + ' total.')
+
+ if (total == assertPlanned && !failCount) res.push('All tests passed')
+ } else {
+ if (!this.isDoneCorrectly()) res.push('Test has completed, but there were no `t.done()` call. Add it at the bottom, or use `t.beginAsync()` for asynchronous code')
+
+ if (this.isDoneCorrectly() && !failCount) res.push('All tests passed')
+ }
+ }
+
+ return lineBreaks ? res.join(lineBreaks) : res
+ },
+
+
+ /**
+ * This method indicates that test has completed at the expected point and no more assertions are planned. Adding assertions after the call to `done`
+ * will add a failing assertion "Adding assertion after test completion".
+ *
+ * @param {Number} delay Optional. When provided, the test will not complete right away, but will wait for `delay` milliseconds for additional assertions.
+ */
+ done : function (delay) {
+ var me = this
+
+ if (delay) {
+ var async = this.beginAsync()
+
+ var originalSetTimeout = this.originalSetTimeout
+
+ originalSetTimeout(function () {
+
+ me.endAsync(async)
+ me.done()
+
+ }, delay)
+
+ } else {
+ this.fireEvent('beforetestfinalizeearly')
+ this.fireEvent('beforetestfinalize');
+
+ this.isDone = true
+
+ if (this.processed) this.finalize()
+ }
+ },
+
+ // `isDoneCorrectly` means that either test does not need the call to `done`
+ // or the call to `done` has been already made
+ isDoneCorrectly : function () {
+ return !this.needDone || this.isDone
+ },
+
+
+ getAssertionCount : function (excludeTodo) {
+ var count = 0
+
+ this.eachAssertion(function (assertion) {
+ if (!excludeTodo || !assertion.isTodo) count++
+ })
+
+ return count
+ },
+
+
+ // cached method except the "includeTodo" case
+ getPassCount : function (includeTodo) {
+ if (this.$passCount != null && !includeTodo) return this.$passCount
+
+ var passCount = 0
+
+ this.eachAssertion(function (assertion) {
+ if (assertion.passed && (includeTodo || !assertion.isTodo)) passCount++
+ })
+
+ return includeTodo ? passCount : this.$passCount = passCount
+ },
+
+ getTodoPassCount : function () {
+ var todoCount = 0;
+
+ this.eachAssertion(function (assertion) {
+ if (assertion.isTodo && assertion.passed) todoCount++;
+ });
+
+ return todoCount;
+ },
+
+ getTodoFailCount : function () {
+ var todoCount = 0;
+
+ this.eachAssertion(function (assertion) {
+ if (assertion.isTodo && !assertion.passed) todoCount++;
+ });
+
+ return todoCount;
+ },
+
+
+ // cached method except the "includeTodo" case
+ getFailCount : function (includeTodo) {
+ if (this.$failCount != null && !includeTodo) return this.$failCount
+
+ var failCount = 0
+
+ this.eachAssertion(function (assertion) {
+ if (!assertion.passed && (includeTodo || !assertion.isTodo)) failCount++
+ })
+
+ return includeTodo ? failCount : this.$failCount = failCount
+ },
+
+
+ getFailedAssertions : function () {
+ var failed = [];
+
+ this.eachAssertion(function (assertion) {
+ if (!assertion.isPassed()) failed.push(assertion)
+ })
+
+ return failed
+ },
+
+
+ isPassed : function () {
+ var passCount = this.getPassCount()
+ var failCount = this.getFailCount()
+ var assertPlanned = this.assertPlanned
+
+ return this.isFinished() && !this.failed && !failCount && (
+ assertPlanned != null && passCount == assertPlanned
+ ||
+ assertPlanned == null && this.isDoneCorrectly()
+ )
+ },
+
+
+ isFailed : function () {
+ var passCount = this.getPassCount()
+ var failCount = this.getFailCount()
+ var assertPlanned = this.assertPlanned
+
+ return this.failed || failCount || (
+
+ this.isFinished() && (
+ assertPlanned != null && passCount != assertPlanned
+ ||
+ assertPlanned == null && !this.isDoneCorrectly()
+ )
+ )
+ },
+
+
+ isFailedWithException : function () {
+ return this.failed
+ },
+
+
+ isStarted : function () {
+ return this.startDate != null
+ },
+
+
+ isFinished : function () {
+ return this.endDate != null
+ },
+
+
+ getDuration : function () {
+ return this.endDate - this.startDate
+ },
+
+
+ getBubbleTarget : function () {
+ return this.parent || this.harness;
+ },
+
+
+ warn : function (message) {
+ this.addResult(new Siesta.Result.Diagnostic({
+ description : message,
+ isWarning : true
+ }))
+ },
+
+
+ flattenArray : function (array) {
+ var me = this
+ var result = []
+
+ Joose.A.each(array, function (el) {
+ if (me.typeOf(el) == 'Array')
+ result.push.apply(result, me.flattenArray(el))
+ else
+ result.push(el)
+ })
+
+ return result
+ }
+ }
+ // eof methods
+
+})
+//eof Siesta.Test;
+Singleton('Siesta.Test.ActionRegistry', {
+
+ has : {
+ actionClasses : Joose.I.Object,
+
+ targetShortcuts : function () {
+ return [
+ 'waitFor',
+ 'click',
+ 'rightClick',
+ 'doubleClick',
+ 'dblClick',
+ 'doubleTap',
+ 'drag',
+ 'longpress',
+ 'mouseDown',
+ 'mouseUp',
+ 'moveCursorTo',
+ 'swipe',
+ 'tap',
+ 'type'
+ ]
+ }
+ },
+
+
+ methods : {
+
+ registerAction : function (name, constructor) {
+ this.actionClasses[ name.toLowerCase() ] = constructor
+ },
+
+
+ getActionClass : function (name) {
+ return this.actionClasses[ name.toLowerCase() ]
+ },
+
+
+ create : function (obj) {
+ if (obj !== Object(obj)) throw "Action configuration should be an Object instance"
+
+ if (!obj.action) {
+ var lowerCasedKeys = {}
+
+ Joose.O.eachOwn(obj, function (value, key) {
+ lowerCasedKeys[ key.toLowerCase() ] = key
+ })
+
+ var me = this
+
+ Joose.A.each(this.targetShortcuts, function (shortcut) {
+ shortcut = shortcut.toLowerCase()
+
+ if (lowerCasedKeys.hasOwnProperty(shortcut)) {
+ obj.action = shortcut
+
+ switch (shortcut) {
+ case 'waitfor' :
+ // do nothing
+ break
+
+ case 'type' :
+ obj.text = obj[ lowerCasedKeys[ shortcut ] ]
+ break
+
+ default :
+ obj.target = obj[ lowerCasedKeys[ shortcut ] ]
+ }
+
+ return false
+ }
+ })
+ }
+
+ if (!obj.action) throw "Need to include `action` property or shortuct property in the step config"
+
+ var actionClass = this.getActionClass(obj.action)
+
+ return new actionClass(obj)
+ }
+ }
+});
+;
+/**
+@class Siesta.Test.Action
+
+Base class for {@link Siesta.Test#chain} actions.
+
+*/
+Class('Siesta.Test.Action', {
+
+ has : {
+ args : null,
+
+ /**
+ * @cfg {String} desc When provided, once step is completed, a passing assertion with this text will be added to a test.
+ * This configuration option can be useful to indicate the progress of "wait" steps
+ */
+ desc : null,
+ test : { required : true },
+ next : { required : true },
+
+ requiredTestMethod : null
+ },
+
+
+ methods : {
+
+ initialize : function () {
+ var requiredTestMethod = this.requiredTestMethod
+
+ // additional sanity check
+ if (requiredTestMethod && !this.test[ requiredTestMethod ])
+ throw new Error("Action [" + this + "] requires `" + requiredTestMethod + "` method in your test class")
+ },
+
+
+ process : function () {
+ this.next()
+ }
+ }
+});
+;
+/**
+
+@class Siesta.Test.Action.Done
+@extends Siesta.Test.Action
+
+This action can be included in the `t.chain` call with "done" shortcut:
+
+ t.chain(
+ {
+ action : 'done'
+ }
+ )
+
+This action will just call the {@link Siesta.Test#done done} method of the test.
+
+*/
+Class('Siesta.Test.Action.Done', {
+
+ isa : Siesta.Test.Action,
+
+ has : {
+ /**
+ * @cfg {Number} delay
+ *
+ * An optional `delay` argument for {@link Siesta.Test#done done} call.
+ */
+ delay : null
+ },
+
+
+ methods : {
+
+ process : function () {
+ this.test.done(this.delay)
+
+ this.next()
+ }
+ }
+});
+
+
+Siesta.Test.ActionRegistry().registerAction('done', Siesta.Test.Action.Done);
+/**
+
+@class Siesta.Test.Action.Wait
+@extends Siesta.Test.Action
+
+This action can be included in the `t.chain` call with "wait" or "delay" shortcuts:
+
+ t.chain(
+ {
+ action : 'wait', // or "delay"
+ delay : 1000 // 1 second
+ }
+ )
+
+Alternatively, for convenience, this action can be included in the chain using "waitFor" config (the "action" property can be omitted):
+
+ t.chain(
+ {
+ waitFor : 'selector', // or any other waitFor* method name
+ args : [ '.x-grid-row' ] // an array of arguments for the specified method
+ }
+ )
+
+ t.chain(
+ {
+ waitFor : 'rowsVisible', // or any other waitFor* method name
+ args : [ grid ] // an array of arguments for the specified method
+ }
+ )
+
+ t.chain(
+ {
+ waitFor : 'waitForRowsVisible', // full method name is also ok
+ args : grid // a single value will be converted to array automatically
+ }
+ )
+
+In the latter case, this action will perform a call to the one of the `waitFor*` methods of the test instance.
+The name of the method is computed by prepending the uppercased value of `waitFor` config with the string "waitFor"
+(unless it doesn't already start with "waitFor").
+The arguments for method call can be provided as the "args" array. Any non-array value for "args" will be converted to an array with one element.
+* **Note**, that this action will provide a `callback`, `scope`, and `timeout` arguments for `waitFor*` methods - you should not specify them.
+
+
+As a special case, the value of `waitFor` config can be a Number or Function - that will trigger the call to {@link Siesta.Test#waitFor} method with provided value:
+
+ t.chain(
+ {
+ waitFor : 500
+ },
+ // same as
+ {
+ waitFor : '',
+ args : [ 500 ]
+ },
+ {
+ waitFor : function () { return document.body.className.match(/someClass/) }
+ }
+ )
+
+*/
+Class('Siesta.Test.Action.Wait', {
+
+ isa : Siesta.Test.Action,
+
+ has : {
+ /**
+ * @cfg {Number} delay
+ *
+ * A number of milliseconds to wait before continuing.
+ */
+ delay : 1000,
+
+ /**
+ * @cfg {Number} timeout
+ *
+ * The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value.
+ */
+ timeout : null,
+
+ /**
+ * @cfg {Array/Function} args
+ *
+ * The array of arguments to pass to waitForXXX method. You should omit the 3 last parameters: callback, scope, timeout. Any non-array value will be converted to
+ * a single-value array. Can be also a function, returning either an array of a single value, which will be converted to array.
+ * Function will be called using test instance as a "this" scope.
+ * If you need to pass a function, as an argument, wrap in the array. Compare:
+ {
+ waitFor : 'SomeCondition',
+ // will be called when processing the action, should return an array of arguments
+ args : function () {}
+ }
+
+ {
+ waitFor : 'SomeCondition',
+ // won't be called, instead will be passed as 1st argument
+ args : [ function () {} ]
+ }
+ *
+ */
+ args : null,
+
+ /**
+ * @cfg {String} waitFor
+ *
+ * The name of the `waitFor` method to call. You can omit the leading "waitFor":
+ *
+
+ t.chain(
+ {
+ waitFor : 'selector',
+ ...
+ },
+ // same as
+ {
+ waitFor : 'waitForSelector',
+ ...
+ }
+ )
+ *
+ */
+ waitFor : null
+ },
+
+
+ methods : {
+
+ process : function () {
+ var waitFor = this.waitFor;
+ var test = this.test
+
+ if (test.typeOf(waitFor) === 'Number' || test.typeOf(waitFor) === 'Function') {
+ // Caller supplied a function returning true when done waiting or
+ // a number of milliseconds to wait for.
+ this.args = [ waitFor ];
+ waitFor = '';
+ }
+
+ if (waitFor == null) {
+ this.args = [ this.delay ];
+ waitFor = '';
+ }
+
+ if (test.typeOf(this.args) === "Function") {
+ this.args = this.args.call(test, this);
+ }
+
+ if (test.typeOf(this.args) !== "Array") {
+ this.args = [ this.args ];
+ }
+
+ // also allow full method names
+ waitFor = waitFor.replace(/^waitFor/, '')
+ var methodName = 'waitFor' + Joose.S.uppercaseFirst(waitFor);
+
+ if (!test[methodName]){
+ throw 'Could not find a waitFor method named ' + methodName;
+ }
+
+ // If using simple waitFor statement, use the object notation to be able to pass a description
+ // which gives better debugging help than "Waited too long for condition to be fulfilled".
+ if (methodName === 'waitFor') {
+ test[methodName]({
+ method : this.args[ 0 ],
+ callback : this.next,
+ scope : test,
+ timeout : this.timeout || test.waitForTimeout,
+ description : this.desc || ''
+ });
+ } else {
+ test[methodName].apply(test, this.args.concat(this.next, test, this.timeout || test.waitForTimeout));
+ }
+ }
+ }
+});
+
+Joose.A.each([ 'wait', 'waitFor', 'delay' ], function(name) {
+ Siesta.Test.ActionRegistry().registerAction(name, Siesta.Test.Action.Wait);
+});;
+/**
+
+@class Siesta.Test.Action.Eval
+@extends Siesta.Test.Action
+
+This action can be included in the `t.chain` steps only with a plain string. Siesta will examine the passed string,
+and call an apropriate method of the test class. String should have the following format:
+
+ methodName(params)
+
+Method name is anything until the first parenthes. Method name may have an optional prefix `t.`.
+Everything in between of outermost parentheses will be treated as parameters for method call. For example:
+
+ t.chain(
+ // string should look like a usual method call,
+ // but arguments can't reference any variables
+ // strings should be quoted, to include quoting symbol in string use double slash: \\
+ 't.click("combo[type=some\\"Type] => .x-form-trigger")',
+
+ // leading "t." is optional, but quoting is not
+ 'waitForComponent("combo[type=someType]")',
+
+ // JSON objects are ok, but they should be a valid JSON - ie object properties should be quoted
+ 'myClick([ 10, 10 ], { "foo" : "bar" })',
+ )
+
+* **Note** You can pass the JSON objects as arguments, but they should be serialized as valid JSON - ie object properties should be quoted.
+
+* **Note** A callback for next step in chain will be always appended to provided parameters. Make sure it is placed in a correct spot!
+For example if method signature is `t.someMethod(param1, param2, callback)` and you are calling this method as:
+
+ t.chain(
+ `t.someMethod("text")`
+ )
+it will fail - callback will be provided in place of `param2`. Instead call it as:
+
+ t.chain(
+ `t.someMethod("text", null)`
+ )
+
+This action may save you few keystrokes, when you need to perform some action with static arguments (known prior the action).
+
+*/
+Class('Siesta.Test.Action.Eval', {
+
+ isa : Siesta.Test.Action,
+
+ has : {
+ /**
+ * @cfg {Object} options
+ *
+ * Any options that will be used when simulating the event. For information about possible
+ * config options, please see:
+ */
+ actionString : null
+ },
+
+
+ methods : {
+
+ process : function () {
+ var test = this.test
+ var parsed = this.parseActionString(this.actionString)
+
+ if (parsed.error) {
+ test.fail(parsed.error)
+ this.next()
+ return
+ }
+
+ var methodName = parsed.methodName
+
+ if (!methodName || test.typeOf(test[ methodName ]) != 'Function') {
+ test.fail("Invalid method name: " + methodName)
+ this.next()
+ return
+ }
+
+ parsed.params.push(this.next)
+
+ test[ methodName ].apply(test, parsed.params)
+ },
+
+
+ parseActionString : function (actionString) {
+ var match = /^\s*(.+?)\(\s*(.*)\s*\)\s*$/.exec(actionString)
+
+ if (!match) return {
+ error : "Wrong format of the action string: " + actionString
+ }
+
+ var methodName = match[ 1 ].replace(/^t\./, '')
+
+ try {
+ var params = JSON.parse('[' + match[ 2 ] + ']')
+ } catch (e) {
+ return {
+ error : "Can't parse arguments: " + match[ 2 ]
+ }
+ }
+
+ return {
+ methodName : methodName,
+ params : params
+ }
+ }
+ }
+});
+;
+/**
+
+@class Siesta.Harness
+@mixin Siesta.Role.CanStyleOutput
+
+`Siesta.Harness` is an abstract base harness class in Siesta hierarchy. This class provides no UI,
+you should use one of it subclasses, for example {@link Siesta.Harness.Browser}
+
+This file is a reference only, for a getting start guide and manual, please refer to Getting Started Guide.
+
+
+Synopsys
+========
+
+ var Harness,
+ isNode = typeof process != 'undefined' && process.pid
+
+ if (isNode) {
+ Harness = require('siesta');
+ } else {
+ Harness = Siesta.Harness.Browser;
+ }
+
+
+ Harness.configure({
+ title : 'Awesome Test Suite',
+
+ transparentEx : true,
+
+ autoCheckGlobals : true,
+ expectedGlobals : [
+ 'Ext',
+ 'Sch'
+ ],
+
+ preload : [
+ "http://cdn.sencha.io/ext-4.0.2a/ext-all-debug.js",
+ "../awesome-project-all.js",
+ {
+ text : "console.log('preload completed')"
+ }
+ ]
+ })
+
+
+ Harness.start(
+ // simple string - url relative to harness file
+ 'sanity.t.js',
+
+ // test file descriptor with own configuration options
+ {
+ url : 'basic.t.js',
+
+ // replace `preload` option of harness
+ preload : [
+ "http://cdn.sencha.io/ext-4.0.6/ext-all-debug.js",
+ "../awesome-project-all.js"
+ ]
+ },
+
+ // groups ("folders") of test files (possibly with own options)
+ {
+ group : 'Sanity',
+
+ autoCheckGlobals : false,
+
+ items : [
+ 'data/crud.t.js',
+ ...
+ ]
+ },
+ ...
+ )
+
+
+*/
+
+
+Class('Siesta.Harness', {
+
+ does : [
+ JooseX.Observable
+ ],
+
+ has : {
+ /**
+ * @cfg {String} title The title of the test suite. Can contain HTML. When provided in the test file descriptor - will change the name of test in the harness UI.
+ */
+ title : null,
+
+ /**
+ * @cfg {Class} testClass The test class which will be used for creating test instances, defaults to {@link Siesta.Test}.
+ * You can subclass {@link Siesta.Test} and provide a new class.
+ *
+ * This option can be also specified in the test file descriptor.
+ */
+ testClass : Siesta.Test,
+ contentManagerClass : Siesta.Content.Manager,
+
+ // fields of test descriptor:
+ // - id - either `url` or wbs + group - computed
+ // - url
+ // - isMissing - true if test file is missing
+ // - testCode - a test code source (can be provided by user)
+ // - testConfig - config object provided to the StartTest
+ // - index - (in the group) computed
+ // - scopeProvider
+ // - scopeProviderConfig
+ // - preload
+ // - alsoPreload
+ // - parent - parent descriptor (or harness for top-most ones) - computed
+ // - preset - computed by harness - instance of Siesta.Content.Preset
+ // - forceDOMVisible - true to show the