diff --git a/Gruntfile.js b/Gruntfile.js index 8be591d..53d63a2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,8 +1,58 @@ module.exports = function( grunt ) { 'use strict'; + function getHandleModuleBundleComplete(start, end) { + return function handleModuleBundleComplete (data) { + var fs = require('fs'), + amdclean = require('amdclean'), + outputFile = data.path; + + fs.writeFileSync(outputFile, amdclean.clean({ + filePath: outputFile, + prefixMode: 'camelCase', + 'wrap': { + 'start': start, + 'end': end + } + })); + }; + } + grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), + requirejs : { + dist : { + options : { + baseUrl: '.', + include: 'lib/basket.js', + out: 'dist/basket.js', + optimize : 'none', + onModuleBundleComplete: getHandleModuleBundleComplete(';(function(){','return libBasketjs})();') + } + }, + full: { + options : { + paths: { + 'lib/RSVP.wrapper': 'bower_components/compact-promise/src/Defer' + }, + baseUrl: '.', + include: 'lib/basket.js', + out: 'dist/basket.full.js', + optimize : 'none', + onModuleBundleComplete: getHandleModuleBundleComplete('', '') + } + } + }, + umd: { + full: { + options: { + src: 'dist/basket.full.js', + dest: 'dist/basket.full.js', + globalAlias: 'basket', + objectToExport: 'libBasketjs' + } + } + }, concat: { options: { banner: '/*!\n' + @@ -14,13 +64,17 @@ module.exports = function( grunt ) { ' <%= _.pluck(pkg.licenses, "type").join(", ") %> License\n' + '* Created by: <%= _.pluck(pkg.maintainers, "name").join(", ") %>\n' + '* Contributors: <%= _.pluck(pkg.contributors, "name").join(", ") %>\n' + - '* Uses rsvp.js, https://github.com/tildeio/rsvp.js\n' + + '* Uses rsvp.js, https://github.com/tildeio/rsvp.js or compact-promise\n' + '*/', stripBanners: true }, dist: { - src: ['lib/basket.js'], + src: ['dist/basket.js'], dest: 'dist/basket.js' + }, + full: { + src: ['dist/basket.full.js'], + dest: 'dist/basket.full.js' } }, uglify: { @@ -41,14 +95,19 @@ module.exports = function( grunt ) { sourceMap: 'dist/basket.full.map' }, files: { - 'dist/basket.full.min.js': ['bower_components/rsvp/rsvp.min.js', 'dist/basket.js'] + 'dist/basket.full.min.js': ['dist/basket.full.js'] } } }, qunit: { - all: { + modular: { options: { - urls: ['http://localhost:8080/test/index.html'] + urls: ['http://localhost:8080/test/modular.html'] + } + }, + bundled: { + options: { + urls: ['http://localhost:8080/test/bundled.html'] } } }, @@ -59,7 +118,7 @@ module.exports = function( grunt ) { } }, connect: { - server: { + all: { options: { base: '.', port: 8080 @@ -80,13 +139,15 @@ module.exports = function( grunt ) { grunt.loadNpmTasks('grunt-contrib-qunit'); grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-requirejs'); + grunt.loadNpmTasks('grunt-umd'); // Dev - default grunt.registerTask('default', ['test']); // Release - grunt.registerTask('release', ['test', 'concat', 'uglify']); + grunt.registerTask('release', ['test', 'requirejs', 'umd', 'concat', 'uglify', 'qunit:bundled']); //Tests - grunt.registerTask('test', ['jshint', 'connect', 'qunit']); + grunt.registerTask('test', ['jshint', 'connect', 'qunit:modular']); }; diff --git a/bower.json b/bower.json index 2b82d7c..9f29e8a 100644 --- a/bower.json +++ b/bower.json @@ -14,5 +14,10 @@ ], "dependencies": { "rsvp": "~3.0.16" + }, + "devDependencies": { + "compact-promise": "~1.1.7", + "requirejs": "~2.2.0", + "qunit": "^1.20.0" } } diff --git a/dist/basket.full.js b/dist/basket.full.js new file mode 100644 index 0000000..27d063f --- /dev/null +++ b/dist/basket.full.js @@ -0,0 +1,375 @@ +/*! +* basket.js +* v0.5.2 - 2016-05-06 +* http://addyosmani.github.com/basket.js +* (c) Addy Osmani; License +* Created by: Addy Osmani, Sindre Sorhus, Andrée Hansson, Mat Scales +* Contributors: Ironsjp, Mathias Bynens, Rick Waldron, Felipe Morais +* Uses rsvp.js, https://github.com/tildeio/rsvp.js or compact-promise +*/(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define([], function () { + return (root['libBasketjs'] = factory()); + }); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + root['basket'] = factory(); + } +}(this, function () { + +var libRSVPwrapper, libBasketjs; +libRSVPwrapper = function () { + var PROTOTYPE = 'prototype', FUNCTION = 'function', RESOLVED = 'resolved', REJECTED = 'rejected'; + function resolve() { + var me = this; + me.promise.result = me.promise.result || arguments[0]; + if (me.promise[RESOLVED] || me.promise[REJECTED]) { + return; + } + me.promise[RESOLVED] = true; + for (var i = 0; i < me.promise._s.length; i++) { + me.promise._s[i].call(null, me.promise.result); + } + me.promise._s = []; + } + function reject() { + var me = this; + me.promise.error = me.promise.error || arguments[0]; + if (me.promise[RESOLVED] || me.promise[REJECTED]) { + return; + } + me.promise[REJECTED] = true; + for (var i = 0; i < me.promise._f.length; i++) { + me.promise._f[i].call(null, me.promise.error); + } + me.promise._f = []; + } + function Defer(promise) { + if (!(this instanceof Defer)) { + return new Defer(promise); + } + var me = this; + me.promise = promise && 'then' in promise ? promise : new Promise(me); + me.resolve = function () { + return resolve.apply(me, arguments); + }; + me.reject = function () { + return reject.apply(me, arguments); + }; + } + function Promise(arg) { + this._s = []; + this._f = []; + this._defer = arg && arg instanceof Defer ? arg : new Defer(this); + this.result = null; + this.error = null; + if (typeof arg === FUNCTION) { + try { + arg.call(this, this._defer.resolve, this._defer.reject); + } catch (ex) { + this._defer.reject(ex); + } + } + } + function createResultHandlerWrapper(handler, defer) { + var me = this; + return function () { + var res = handler.apply(me, arguments); + if (res && typeof res.then === FUNCTION) { + res.then(function () { + defer.resolve.apply(defer, arguments); + }, function () { + defer.reject.apply(defer, arguments); + }); + } else { + defer.resolve.apply(defer, res == null ? [] : [res]); + } + }; + } + Promise[PROTOTYPE].then = function (onSuccess, onFailure) { + var defer = new Defer(); + var me = this; + var handleSuccess, handleFail; + if (typeof onSuccess == FUNCTION) { + handleSuccess = createResultHandlerWrapper.call(me, onSuccess, defer); + } else { + handleSuccess = defer.resolve; + } + if (me[RESOLVED]) { + handleSuccess.call(null, me.result); + } else { + me._s.push(handleSuccess); + } + if (typeof onFailure == FUNCTION) { + handleFail = createResultHandlerWrapper.call(me, onFailure, defer); + } else { + handleFail = defer.reject; + } + if (me[REJECTED]) { + handleFail.call(null, me.error); + } else { + me._f.push(handleFail); + } + return defer.promise; + }; + Defer.Promise = Promise; + Defer.resolve = function (v) { + var result = new Defer(); + result.resolve(v); + return result.promise; + }; + Defer.reject = function (v) { + var result = new Defer(); + result.reject(v); + return result.promise; + }; + function getResultChecker(results, index, resolve, length, count) { + return function check(result) { + results[index] = result; + count.value++; + if (length.value === count.value) { + resolve(results); + } + }; + } + Defer.all = function (promises) { + return new Promise(function (rs, rj) { + var length = { value: promises.length }; + var count = { value: 0 }; + var results = []; + for (var l = promises.length; l--;) { + if (!('then' in promises[l])) { + results[l] = promises[l]; + length.value--; + } else { + promises[l].then(getResultChecker(results, l, rs, length, count), rj); + } + } + if (length.value <= 0 || length.value === count.value) { + rs(results); + return; + } + }); + }; + return Defer; +}(); +libBasketjs = function (RSVP) { + var Fn = Function, window = new Fn('return this')(); + var document = window.document; + var head = document.head || document.getElementsByTagName('head')[0]; + var storagePrefix = 'basket-'; + var defaultExpiration = 5000; + var inBasket = []; + var basket; + var addLocalStorage = function (key, storeObj) { + try { + localStorage.setItem(storagePrefix + key, JSON.stringify(storeObj)); + return true; + } catch (e) { + if (e.name.toUpperCase().indexOf('QUOTA') >= 0) { + var item; + var tempScripts = []; + for (item in localStorage) { + if (item.indexOf(storagePrefix) === 0) { + tempScripts.push(JSON.parse(localStorage[item])); + } + } + if (tempScripts.length) { + tempScripts.sort(function (a, b) { + return a.stamp - b.stamp; + }); + basket.remove(tempScripts[0].key); + return addLocalStorage(key, storeObj); + } else { + // no files to remove. Larger than available quota + return; + } + } else { + // some other error + return; + } + } + }; + var getUrl = function (url) { + var promise = new RSVP.Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200 || xhr.status === 0 && xhr.responseText) { + resolve({ + content: xhr.responseText, + type: xhr.getResponseHeader('content-type') + }); + } else { + reject(new Error(xhr.statusText)); + } + } + }; + // By default XHRs never timeout, and even Chrome doesn't implement the + // spec for xhr.timeout. So we do it ourselves. + setTimeout(function () { + if (xhr.readyState < 4) { + xhr.abort(); + } + }, basket.timeout); + xhr.send(); + }); + return promise; + }; + var wrapStoreData = function (obj, data) { + var now = +new Date(); + obj.data = data.content; + obj.originalType = data.type; + obj.type = obj.type || data.type; + obj.skipCache = obj.skipCache || false; + obj.stamp = now; + obj.expire = now + (obj.expire || defaultExpiration) * 60 * 60 * 1000; + return obj; + }; + var saveUrl = function (obj) { + return getUrl(obj.url).then(function (result) { + var storeObj = wrapStoreData(obj, result); + if (!obj.skipCache) { + addLocalStorage(obj.key, storeObj); + } + return storeObj; + }); + }; + var isCacheValid = function (source, obj) { + return !source || source.expire - +new Date() < 0 || obj.unique !== source.unique || basket.isValidItem && !basket.isValidItem(source, obj); + }; + var handleStackObject = function (obj) { + var source, promise, shouldFetch; + if (!obj.url) { + return; + } + obj.key = obj.key || obj.url; + source = basket.get(obj.key); + obj.execute = obj.execute !== false; + shouldFetch = isCacheValid(source, obj); + if (obj.live || shouldFetch) { + if (obj.unique) { + // set parameter to prevent browser cache + obj.url += (obj.url.indexOf('?') > 0 ? '&' : '?') + 'basket-unique=' + obj.unique; + } + promise = saveUrl(obj); + if (obj.live && !shouldFetch) { + promise = promise.then(function (result) { + // If we succeed, just return the value + // RSVP doesn't have a .fail convenience method + return result; + }, function () { + return source; + }); + } + } else { + source.type = obj.type || source.originalType; + source.execute = obj.execute; + promise = new RSVP.resolve(source); + } + return promise; + }; + var injectScript = function (obj) { + var script = document.createElement('script'); + script.defer = true; + // Have to use .text, since we support IE8, + // which won't allow appending to a script + script.text = obj.data; + head.appendChild(script); + }; + var handlers = { 'default': injectScript }; + var execute = function (obj) { + if (obj.type && handlers[obj.type]) { + return handlers[obj.type](obj); + } + return handlers['default'](obj); // 'default' is a reserved word + }; + var performActions = function (resources) { + var obj; + for (var i = 0; i < resources.length; i++) { + obj = resources[i]; + if (obj.execute) { + execute(obj); + } + } + return RSVP.resolve(resources); + }; + var fetch = function () { + var i, l, promises = []; + for (i = 0, l = arguments.length; i < l; i++) { + promises.push(handleStackObject(arguments[i])); + } + return RSVP.all(promises); + }; + var thenRequire = function () { + var resources = fetch.apply(null, arguments); + var promise = this.then(function () { + return resources; + }).then(performActions); + promise.thenRequire = thenRequire; + return promise; + }; + basket = { + require: function () { + for (var a = 0, l = arguments.length; a < l; a++) { + arguments[a].execute = arguments[a].execute !== false; + if (arguments[a].once && inBasket.indexOf(arguments[a].url) >= 0) { + arguments[a].execute = false; + } else if (arguments[a].execute !== false && inBasket.indexOf(arguments[a].url) < 0) { + inBasket.push(arguments[a].url); + } + } + var promise = fetch.apply(null, arguments).then(performActions); + promise.thenRequire = thenRequire; + return promise; + }, + remove: function (key) { + localStorage.removeItem(storagePrefix + key); + return this; + }, + get: function (key) { + var item = localStorage.getItem(storagePrefix + key); + try { + return JSON.parse(item || 'false'); + } catch (e) { + return false; + } + }, + clear: function (expired) { + var item, key; + var now = +new Date(); + for (item in localStorage) { + key = item.split(storagePrefix)[1]; + if (key && (!expired || this.get(key).expire <= now)) { + this.remove(key); + } + } + return this; + }, + isValidItem: null, + timeout: 5000, + addHandler: function (types, handler) { + if (!Array.isArray(types)) { + types = [types]; + } + types.forEach(function (type) { + handlers[type] = handler; + }); + }, + removeHandler: function (types) { + basket.addHandler(types, undefined); + } + }; + // delete expired keys + basket.clear(true); + return basket; +}(libRSVPwrapper); +return libBasketjs; + +})); diff --git a/dist/basket.full.min.js b/dist/basket.full.min.js index 14a7d9b..4d1df15 100644 --- a/dist/basket.full.min.js +++ b/dist/basket.full.min.js @@ -1,11 +1,11 @@ /*! * basket.js -* v0.5.2 - 2015-02-07 +* v0.5.2 - 2016-05-06 * http://addyosmani.github.com/basket.js * (c) Addy Osmani; License * Created by: Addy Osmani, Sindre Sorhus, Andrée Hansson, Mat Scales * Contributors: Ironsjp, Mathias Bynens, Rick Waldron, Felipe Morais -* Uses rsvp.js, https://github.com/tildeio/rsvp.js +* Uses rsvp.js, https://github.com/tildeio/rsvp.js or compact-promise */ -(function(){"use strict";function a(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1}function b(a){var b=a._promiseCallbacks;return b||(b=a._promiseCallbacks={}),b}function c(a,b){return"onerror"===a?void rb.on("error",b):2!==arguments.length?rb[a]:void(rb[a]=b)}function d(a){return"function"==typeof a||"object"==typeof a&&null!==a}function e(a){return"function"==typeof a}function f(a){return"object"==typeof a&&null!==a}function g(){}function h(){setTimeout(function(){for(var a,b=0;bh;h++)u(e.resolve(a[h]),void 0,c,d);return f}function E(a,b){var c=this;if(a&&"object"==typeof a&&a.constructor===c)return a;var d=new c(k,b);return q(d,a),d}function F(a,b){var c=this,d=new c(k,b);return t(d,a),d}function G(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function H(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function I(a,b){this._id=Jb++,this._label=b,this._state=void 0,this._result=void 0,this._subscribers=[],rb.instrument&&xb("created",this),k!==a&&(e(a)||G(),this instanceof I||H(),z(this,a))}function J(){this.value=void 0}function K(a){try{return a.then}catch(b){return Lb.value=b,Lb}}function L(a,b,c){try{a.apply(b,c)}catch(d){return Lb.value=d,Lb}}function M(a,b){for(var c,d,e={},f=a.length,g=new Array(f),h=0;f>h;h++)g[h]=a[h];for(d=0;dd;d++)c[d-1]=a[d];return c}function O(a,b){return{then:function(c,d){return a.call(b,c,d)}}}function P(a,b){var c=function(){for(var c,d=this,e=arguments.length,f=new Array(e+1),g=!1,h=0;e>h;++h){if(c=arguments[h],!g){if(g=S(c),g===Mb){var i=new Kb(k);return t(i,Mb.value),i}g&&g!==!0&&(c=O(g,c))}f[h]=c}var j=new Kb(k);return f[e]=function(a,c){a?t(j,a):void 0===b?q(j,c):b===!0?q(j,N(arguments)):tb(b)?q(j,M(arguments,b)):q(j,c)},g?R(j,f,a,d):Q(j,f,a,d)};return c.__proto__=a,c}function Q(a,b,c,d){var e=L(c,d,b);return e===Lb&&t(a,e.value),a}function R(a,b,c,d){return Kb.all(b).then(function(b){var e=L(c,d,b);return e===Lb&&t(a,e.value),a})}function S(a){return a&&"object"==typeof a?a.constructor===Kb?!0:K(a):!1}function T(a,b){return Kb.all(a,b)}function U(a,b,c){this._superConstructor(a,b,!1,c)}function V(a,b){return new U(Kb,a,b).promise}function W(a,b){return Kb.race(a,b)}function X(a,b,c){this._superConstructor(a,b,!0,c)}function Y(a,b){return new Rb(Kb,a,b).promise}function Z(a,b,c){this._superConstructor(a,b,!1,c)}function $(a,b){return new Z(Kb,a,b).promise}function _(a){throw setTimeout(function(){throw a}),a}function ab(a){var b={};return b.promise=new Kb(function(a,c){b.resolve=a,b.reject=c},a),b}function bb(a,b,c){return Kb.all(a,c).then(function(a){if(!e(b))throw new TypeError("You must pass a function as map's second argument.");for(var d=a.length,f=new Array(d),g=0;d>g;g++)f[g]=b(a[g]);return Kb.all(f,c)})}function cb(a,b){return Kb.resolve(a,b)}function db(a,b){return Kb.reject(a,b)}function eb(a,b,c){return Kb.all(a,c).then(function(a){if(!e(b))throw new TypeError("You must pass a function as filter's second argument.");for(var d=a.length,f=new Array(d),g=0;d>g;g++)f[g]=b(a[g]);return Kb.all(f,c).then(function(b){for(var c=new Array(d),e=0,f=0;d>f;f++)b[f]&&(c[e]=a[f],e++);return c.length=e,c})})}function fb(a,b){gc[_b]=a,gc[_b+1]=b,_b+=2,2===_b&&Tb()}function gb(){var a=process.nextTick,b=process.versions.node.match(/^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/);return Array.isArray(b)&&"0"===b[1]&&"10"===b[2]&&(a=setImmediate),function(){a(lb)}}function hb(){return function(){vertxNext(lb)}}function ib(){var a=0,b=new dc(lb),c=document.createTextNode("");return b.observe(c,{characterData:!0}),function(){c.data=a=++a%2}}function jb(){var a=new MessageChannel;return a.port1.onmessage=lb,function(){a.port2.postMessage(0)}}function kb(){return function(){setTimeout(lb,1)}}function lb(){for(var a=0;_b>a;a+=2){var b=gc[a],c=gc[a+1];b(c),gc[a]=void 0,gc[a+1]=void 0}_b=0}function mb(){try{var a=require("vertx");return a.runOnLoop||a.runOnContext,hb()}catch(b){return kb()}}function nb(a,b){rb.async(a,b)}function ob(){rb.on.apply(rb,arguments)}function pb(){rb.off.apply(rb,arguments)}var qb={mixin:function(a){return a.on=this.on,a.off=this.off,a.trigger=this.trigger,a._promiseCallbacks=void 0,a},on:function(c,d){var e,f=b(this);e=f[c],e||(e=f[c]=[]),-1===a(e,d)&&e.push(d)},off:function(c,d){var e,f,g=b(this);return d?(e=g[c],f=a(e,d),void(-1!==f&&e.splice(f,1))):void(g[c]=[])},trigger:function(a,c){var d,e,f=b(this);if(d=f[a])for(var g=0;g1)throw new Error("Second argument not supported");if("object"!=typeof a)throw new TypeError("Argument must be an object");return g.prototype=a,new g},wb=[],xb=i,yb=void 0,zb=1,Ab=2,Bb=new w,Cb=new w,Db=B;B.prototype._validateInput=function(a){return tb(a)},B.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},B.prototype._init=function(){this._result=new Array(this.length)},B.prototype._enumerate=function(){for(var a=this.length,b=this.promise,c=this._input,d=0;b._state===yb&&a>d;d++)this._eachEntry(c[d],d)},B.prototype._eachEntry=function(a,b){var c=this._instanceConstructor;f(a)?a.constructor===c&&a._state!==yb?(a._onError=null,this._settledAt(a._state,b,a._result)):this._willSettleAt(c.resolve(a),b):(this._remaining--,this._result[b]=this._makeResult(zb,b,a))},B.prototype._settledAt=function(a,b,c){var d=this.promise;d._state===yb&&(this._remaining--,this._abortOnReject&&a===Ab?t(d,c):this._result[b]=this._makeResult(a,b,c)),0===this._remaining&&s(d,this._result)},B.prototype._makeResult=function(a,b,c){return c},B.prototype._willSettleAt=function(a,b){var c=this;u(a,void 0,function(a){c._settledAt(zb,b,a)},function(a){c._settledAt(Ab,b,a)})};var Eb=C,Fb=D,Gb=E,Hb=F,Ib="rsvp_"+ub()+"-",Jb=0,Kb=I;I.cast=Gb,I.all=Eb,I.race=Fb,I.resolve=Gb,I.reject=Hb,I.prototype={constructor:I,_guidKey:Ib,_onError:function(a){rb.async(function(b){setTimeout(function(){b._onError&&rb.trigger("error",a)},0)},this)},then:function(a,b,c){var d=this,e=d._state;if(e===zb&&!a||e===Ab&&!b)return rb.instrument&&xb("chained",this,this),this;d._onError=null;var f=new this.constructor(k,c),g=d._result;if(rb.instrument&&xb("chained",d,f),e){var h=arguments[e-1];rb.async(function(){y(e,f,h,g)})}else u(d,f,a,b);return f},"catch":function(a,b){return this.then(null,a,b)},"finally":function(a,b){var c=this.constructor;return this.then(function(b){return c.resolve(a()).then(function(){return b})},function(b){return c.resolve(a()).then(function(){throw b})},b)}};var Lb=new J,Mb=new J,Nb=P,Ob=T;U.prototype=vb(Db.prototype),U.prototype._superConstructor=Db,U.prototype._makeResult=A,U.prototype._validationError=function(){return new Error("allSettled must be called with an array")};var Pb=V,Qb=W,Rb=X;X.prototype=vb(Db.prototype),X.prototype._superConstructor=Db,X.prototype._init=function(){this._result={}},X.prototype._validateInput=function(a){return a&&"object"==typeof a},X.prototype._validationError=function(){return new Error("Promise.hash must be called with an object")},X.prototype._enumerate=function(){var a=this.promise,b=this._input,c=[];for(var d in b)a._state===yb&&b.hasOwnProperty(d)&&c.push({position:d,entry:b[d]});var e=c.length;this._remaining=e;for(var f,g=0;a._state===yb&&e>g;g++)f=c[g],this._eachEntry(f.entry,f.position)};var Sb=Y;Z.prototype=vb(Rb.prototype),Z.prototype._superConstructor=Db,Z.prototype._makeResult=A,Z.prototype._validationError=function(){return new Error("hashSettled must be called with an object")};var Tb,Ub=$,Vb=_,Wb=ab,Xb=bb,Yb=cb,Zb=db,$b=eb,_b=0,ac=fb,bc="undefined"!=typeof window?window:void 0,cc=bc||{},dc=cc.MutationObserver||cc.WebKitMutationObserver,ec="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),fc="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,gc=new Array(1e3);if(Tb=ec?gb():dc?ib():fc?jb():void 0===bc&&"function"==typeof require?mb():kb(),rb.async=ac,"undefined"!=typeof window&&"object"==typeof window.__PROMISE_INSTRUMENTATION__){var hc=window.__PROMISE_INSTRUMENTATION__;c("instrument",!0);for(var ic in hc)hc.hasOwnProperty(ic)&&ob(ic,hc[ic])}var jc={race:Qb,Promise:Kb,allSettled:Pb,hash:Sb,hashSettled:Ub,denodeify:Nb,on:ob,off:pb,map:Xb,filter:$b,resolve:Yb,reject:Zb,all:Ob,rethrow:Vb,defer:Wb,EventTarget:qb,configure:c,async:nb};"function"==typeof define&&define.amd?define(function(){return jc}):"undefined"!=typeof module&&module.exports?module.exports=jc:"undefined"!=typeof this&&(this.RSVP=jc)}).call(this),function(a,b){"use strict";var c=b.head||b.getElementsByTagName("head")[0],d="basket-",e=5e3,f=[],g=function(a,b){try{return localStorage.setItem(d+a,JSON.stringify(b)),!0}catch(c){if(c.name.toUpperCase().indexOf("QUOTA")>=0){var e,f=[];for(e in localStorage)0===e.indexOf(d)&&f.push(JSON.parse(localStorage[e]));return f.length?(f.sort(function(a,b){return a.stamp-b.stamp}),basket.remove(f[0].key),g(a,b)):void 0}return}},h=function(a){var b=new RSVP.Promise(function(b,c){var d=new XMLHttpRequest;d.open("GET",a),d.onreadystatechange=function(){4===d.readyState&&(200===d.status||0===d.status&&d.responseText?b({content:d.responseText,type:d.getResponseHeader("content-type")}):c(new Error(d.statusText)))},setTimeout(function(){d.readyState<4&&d.abort()},basket.timeout),d.send()});return b},i=function(a){return h(a.url).then(function(b){var c=j(a,b);return a.skipCache||g(a.key,c),c})},j=function(a,b){var c=+new Date;return a.data=b.content,a.originalType=b.type,a.type=a.type||b.type,a.skipCache=a.skipCache||!1,a.stamp=c,a.expire=c+60*(a.expire||e)*60*1e3,a},k=function(a,b){return!a||a.expire-+new Date<0||b.unique!==a.unique||basket.isValidItem&&!basket.isValidItem(a,b)},l=function(a){var b,c,d;if(a.url)return a.key=a.key||a.url,b=basket.get(a.key),a.execute=a.execute!==!1,d=k(b,a),a.live||d?(a.unique&&(a.url+=(a.url.indexOf("?")>0?"&":"?")+"basket-unique="+a.unique),c=i(a),a.live&&!d&&(c=c.then(function(a){return a},function(){return b}))):(b.type=a.type||b.originalType,b.execute=a.execute,c=new RSVP.Promise(function(a){a(b)})),c},m=function(a){var d=b.createElement("script");d.defer=!0,d.text=a.data,c.appendChild(d)},n={"default":m},o=function(a){return a.type&&n[a.type]?n[a.type](a):n["default"](a)},p=function(a){return a.map(function(a){return a.execute&&o(a),a})},q=function(){var a,b,c=[];for(a=0,b=arguments.length;b>a;a++)c.push(l(arguments[a]));return RSVP.all(c)},r=function(){var a=q.apply(null,arguments),b=this.then(function(){return a}).then(p);return b.thenRequire=r,b};a.basket={require:function(){for(var a=0,b=arguments.length;b>a;a++)arguments[a].execute=arguments[a].execute!==!1,arguments[a].once&&f.indexOf(arguments[a].url)>=0?arguments[a].execute=!1:arguments[a].execute!==!1&&f.indexOf(arguments[a].url)<0&&f.push(arguments[a].url);var c=q.apply(null,arguments).then(p);return c.thenRequire=r,c},remove:function(a){return localStorage.removeItem(d+a),this},get:function(a){var b=localStorage.getItem(d+a);try{return JSON.parse(b||"false")}catch(c){return!1}},clear:function(a){var b,c,e=+new Date;for(b in localStorage)c=b.split(d)[1],c&&(!a||this.get(c).expire<=e)&&this.remove(c);return this},isValidItem:null,timeout:5e3,addHandler:function(a,b){Array.isArray(a)||(a=[a]),a.forEach(function(a){n[a]=b})},removeHandler:function(a){basket.addHandler(a,void 0)}},basket.clear(!0)}(this,document); +!function(a,b){"function"==typeof define&&define.amd?define([],function(){return a.libBasketjs=b()}):"object"==typeof exports?module.exports=b():a.basket=b()}(this,function(){var a,b;return a=function(){function a(){var a=this;if(a.promise.result=a.promise.result||arguments[0],!a.promise[i]&&!a.promise[j]){a.promise[i]=!0;for(var b=0;b=0){var e,f=[];for(e in localStorage)0===e.indexOf(g)&&f.push(JSON.parse(localStorage[e]));return f.length?(f.sort(function(a,b){return a.stamp-b.stamp}),b.remove(f[0].key),j(a,c)):void 0}return}},k=function(c){var d=new a.Promise(function(a,d){var e=new XMLHttpRequest;e.open("GET",c),e.onreadystatechange=function(){4===e.readyState&&(200===e.status||0===e.status&&e.responseText?a({content:e.responseText,type:e.getResponseHeader("content-type")}):d(new Error(e.statusText)))},setTimeout(function(){e.readyState<4&&e.abort()},b.timeout),e.send()});return d},l=function(a,b){var c=+new Date;return a.data=b.content,a.originalType=b.type,a.type=a.type||b.type,a.skipCache=a.skipCache||!1,a.stamp=c,a.expire=c+60*(a.expire||h)*60*1e3,a},m=function(a){return k(a.url).then(function(b){var c=l(a,b);return a.skipCache||j(a.key,c),c})},n=function(a,c){return!a||a.expire-+new Date<0||c.unique!==a.unique||b.isValidItem&&!b.isValidItem(a,c)},o=function(c){var d,e,f;if(c.url)return c.key=c.key||c.url,d=b.get(c.key),c.execute=c.execute!==!1,f=n(d,c),c.live||f?(c.unique&&(c.url+=(c.url.indexOf("?")>0?"&":"?")+"basket-unique="+c.unique),e=m(c),c.live&&!f&&(e=e.then(function(a){return a},function(){return d}))):(d.type=c.type||d.originalType,d.execute=c.execute,e=new a.resolve(d)),e},p=function(a){var b=e.createElement("script");b.defer=!0,b.text=a.data,f.appendChild(b)},q={"default":p},r=function(a){return a.type&&q[a.type]?q[a.type](a):q["default"](a)},s=function(b){for(var c,d=0;db;b++)d.push(o(arguments[b]));return a.all(d)},u=function(){var a=t.apply(null,arguments),b=this.then(function(){return a}).then(s);return b.thenRequire=u,b};return b={require:function(){for(var a=0,b=arguments.length;b>a;a++)arguments[a].execute=arguments[a].execute!==!1,arguments[a].once&&i.indexOf(arguments[a].url)>=0?arguments[a].execute=!1:arguments[a].execute!==!1&&i.indexOf(arguments[a].url)<0&&i.push(arguments[a].url);var c=t.apply(null,arguments).then(s);return c.thenRequire=u,c},remove:function(a){return localStorage.removeItem(g+a),this},get:function(a){var b=localStorage.getItem(g+a);try{return JSON.parse(b||"false")}catch(c){return!1}},clear:function(a){var b,c,d=+new Date;for(b in localStorage)c=b.split(g)[1],c&&(!a||this.get(c).expire<=d)&&this.remove(c);return this},isValidItem:null,timeout:5e3,addHandler:function(a,b){Array.isArray(a)||(a=[a]),a.forEach(function(a){q[a]=b})},removeHandler:function(a){b.addHandler(a,void 0)}},b.clear(!0),b}(a)}); //# sourceMappingURL=basket.full.min.js.map \ No newline at end of file diff --git a/dist/basket.full.min.js.map b/dist/basket.full.min.js.map index cfcd0ac..1f7e113 100644 --- a/dist/basket.full.min.js.map +++ b/dist/basket.full.min.js.map @@ -1 +1 @@ -{"version":3,"file":"basket.full.min.js","sources":["../bower_components/rsvp/rsvp.min.js","basket.js"],"names":["t","n","r","e","length","_promiseCallbacks","mn","on","arguments","o","i","u","s","setTimeout","An","payload","guid","key","id","childGuid","childId","error","stack","trigger","name","a","push","_guidKey","_id","eventName","detail","_result","label","_label","timeStamp","bn","Error","c","TypeError","f","l","then","Cn","h","call","p","async","d","m","w","_","_state","kn","Sn","_onError","g","v","constructor","y","b","Tn","_subscribers","instrument","jn","j","E","this","A","On","T","k","state","value","reason","S","_instanceConstructor","promise","_abortOnReject","_validateInput","_input","_remaining","_init","_enumerate","_validationError","C","In","O","gn","resolve","I","R","x","M","N","Yn","P","Y","Kn","D","apply","K","Array","U","q","F","V","Un","Dn","L","G","__proto__","all","W","$","_superConstructor","z","B","race","H","J","Vn","Q","X","Z","tn","reject","nn","rn","en","un","sr","tr","$n","sn","process","nextTick","versions","node","match","isArray","setImmediate","hn","an","vertxNext","cn","or","document","createTextNode","observe","characterData","data","fn","MessageChannel","port1","onmessage","port2","postMessage","ln","pn","require","runOnLoop","runOnContext","_n","vn","dn","off","yn","mixin","splice","wn","Object","prototype","toString","Date","now","getTime","En","create","_eachEntry","_settledAt","_willSettleAt","_makeResult","Rn","xn","Mn","Nn","Pn","cast","catch","finally","qn","Fn","Gn","Ln","hasOwnProperty","position","entry","Wn","zn","Bn","Hn","Jn","Qn","Xn","Zn","nr","rr","window","er","MutationObserver","WebKitMutationObserver","ir","ur","Uint8ClampedArray","importScripts","__PROMISE_INSTRUMENTATION__","ar","cr","fr","Promise","allSettled","hash","hashSettled","denodeify","map","filter","rethrow","defer","EventTarget","configure","define","amd","module","exports","RSVP","head","getElementsByTagName","storagePrefix","defaultExpiration","inBasket","addLocalStorage","storeObj","localStorage","setItem","JSON","stringify","toUpperCase","indexOf","item","tempScripts","parse","sort","stamp","basket","remove","getUrl","url","xhr","XMLHttpRequest","open","onreadystatechange","readyState","status","responseText","content","type","getResponseHeader","statusText","abort","timeout","send","saveUrl","obj","result","wrapStoreData","skipCache","originalType","expire","isCacheValid","source","unique","isValidItem","handleStackObject","shouldFetch","get","execute","live","injectScript","script","createElement","text","appendChild","handlers","default","performActions","resources","fetch","promises","thenRequire","once","removeItem","getItem","clear","expired","split","addHandler","types","handler","forEach","removeHandler","undefined"],"mappings":"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"} \ No newline at end of file +{"version":3,"sources":["basket.full.js"],"names":["root","factory","define","amd","exports","module","this","libRSVPwrapper","libBasketjs","resolve","me","promise","result","arguments","RESOLVED","REJECTED","i","_s","length","call","reject","error","_f","Defer","Promise","apply","arg","_defer","FUNCTION","ex","createResultHandlerWrapper","handler","defer","res","then","getResultChecker","results","index","count","value","PROTOTYPE","onSuccess","onFailure","handleSuccess","handleFail","push","v","all","promises","rs","rj","l","RSVP","basket","Fn","Function","window","document","head","getElementsByTagName","storagePrefix","defaultExpiration","inBasket","addLocalStorage","key","storeObj","localStorage","setItem","JSON","stringify","e","name","toUpperCase","indexOf","item","tempScripts","parse","sort","a","b","stamp","remove","getUrl","url","xhr","XMLHttpRequest","open","onreadystatechange","readyState","status","responseText","content","type","getResponseHeader","Error","statusText","setTimeout","abort","timeout","send","wrapStoreData","obj","data","now","Date","originalType","skipCache","expire","saveUrl","isCacheValid","source","unique","isValidItem","handleStackObject","shouldFetch","get","execute","live","injectScript","script","createElement","text","appendChild","handlers","default","performActions","resources","fetch","thenRequire","require","once","removeItem","getItem","clear","expired","split","addHandler","types","Array","isArray","forEach","removeHandler","undefined"],"mappings":";;;;;;;;;CAQG,SAAUA,EAAMC,GACK,kBAAXC,SAAyBA,OAAOC,IAEzCD,UAAW,WACT,MAAQF,GAAkB,YAAIC,MAEJ,gBAAZG,SAIhBC,OAAOD,QAAUH,IAEjBD,EAAa,OAAIC,KAEnBK,KAAM,WAER,GAAIC,GAAgBC,CA4VpB,OA3VAD,GAAiB,WAEf,QAASE,KACP,GAAIC,GAAKJ,IAET,IADAI,EAAGC,QAAQC,OAASF,EAAGC,QAAQC,QAAUC,UAAU,IAC/CH,EAAGC,QAAQG,KAAaJ,EAAGC,QAAQI,GAAvC,CAGAL,EAAGC,QAAQG,IAAY,CACvB,KAAK,GAAIE,GAAI,EAAGA,EAAIN,EAAGC,QAAQM,GAAGC,OAAQF,IACxCN,EAAGC,QAAQM,GAAGD,GAAGG,KAAK,KAAMT,EAAGC,QAAQC,OAEzCF,GAAGC,QAAQM,OAEb,QAASG,KACP,GAAIV,GAAKJ,IAET,IADAI,EAAGC,QAAQU,MAAQX,EAAGC,QAAQU,OAASR,UAAU,IAC7CH,EAAGC,QAAQG,KAAaJ,EAAGC,QAAQI,GAAvC,CAGAL,EAAGC,QAAQI,IAAY,CACvB,KAAK,GAAIC,GAAI,EAAGA,EAAIN,EAAGC,QAAQW,GAAGJ,OAAQF,IACxCN,EAAGC,QAAQW,GAAGN,GAAGG,KAAK,KAAMT,EAAGC,QAAQU,MAEzCX,GAAGC,QAAQW,OAEb,QAASC,GAAMZ,GACb,KAAML,eAAgBiB,IACpB,MAAO,IAAIA,GAAMZ,EAEnB,IAAID,GAAKJ,IACTI,GAAGC,QAAUA,GAAW,QAAUA,GAAUA,EAAU,GAAIa,GAAQd,GAClEA,EAAGD,QAAU,WACX,MAAOA,GAAQgB,MAAMf,EAAIG,YAE3BH,EAAGU,OAAS,WACV,MAAOA,GAAOK,MAAMf,EAAIG,YAG5B,QAASW,GAAQE,GAMf,GALApB,KAAKW,MACLX,KAAKgB,MACLhB,KAAKqB,OAASD,GAAOA,YAAeH,GAAQG,EAAM,GAAIH,GAAMjB,MAC5DA,KAAKM,OAAS,KACdN,KAAKe,MAAQ,WACFK,KAAQE,EACjB,IACEF,EAAIP,KAAKb,KAAMA,KAAKqB,OAAOlB,QAASH,KAAKqB,OAAOP,QAChD,MAAOS,GACPvB,KAAKqB,OAAOP,OAAOS,IAIzB,QAASC,GAA2BC,EAASC,GAC3C,GAAItB,GAAKJ,IACT,OAAO,YACL,GAAI2B,GAAMF,EAAQN,MAAMf,EAAIG,UACxBoB,UAAcA,GAAIC,OAASN,EAC7BK,EAAIC,KAAK,WACPF,EAAMvB,QAAQgB,MAAMO,EAAOnB,YAC1B,WACDmB,EAAMZ,OAAOK,MAAMO,EAAOnB,aAG5BmB,EAAMvB,QAAQgB,MAAMO,EAAc,MAAPC,MAAoBA,KAyCrD,QAASE,GAAiBC,EAASC,EAAO5B,EAASS,EAAQoB,GACzD,MAAO,UAAe1B,GACpBwB,EAAQC,GAASzB,EACjB0B,EAAMC,QACFrB,EAAOqB,QAAUD,EAAMC,OACzB9B,EAAQ2B,IA7Gd,GAAII,GAAY,YAAaZ,EAAW,WAAYd,EAAW,WAAYC,EAAW,UAoItF,OAjEAS,GAAQgB,GAAWN,KAAO,SAAUO,EAAWC,GAC7C,GAEIC,GAAeC,EAFfZ,EAAQ,GAAIT,GACZb,EAAKJ,IAsBT,OAnBEqC,SADSF,IAAab,EACNE,EAA2BX,KAAKT,EAAI+B,EAAWT,GAE/CA,EAAMvB,QAEpBC,EAAGI,GACL6B,EAAcxB,KAAK,KAAMT,EAAGE,QAE5BF,EAAGO,GAAG4B,KAAKF,GAGXC,QADSF,IAAad,EACTE,EAA2BX,KAAKT,EAAIgC,EAAWV,GAE/CA,EAAMZ,OAEjBV,EAAGK,GACL6B,EAAWzB,KAAK,KAAMT,EAAGW,OAEzBX,EAAGY,GAAGuB,KAAKD,GAENZ,EAAMrB,SAEfY,EAAMC,QAAUA,EAChBD,EAAMd,QAAU,SAAUqC,GACxB,GAAIlC,GAAS,GAAIW,EAEjB,OADAX,GAAOH,QAAQqC,GACRlC,EAAOD,SAEhBY,EAAMH,OAAS,SAAU0B,GACvB,GAAIlC,GAAS,GAAIW,EAEjB,OADAX,GAAOQ,OAAO0B,GACPlC,EAAOD,SAWhBY,EAAMwB,IAAM,SAAUC,GACpB,MAAO,IAAIxB,GAAQ,SAAUyB,EAAIC,GAI/B,IAAK,GAHDhC,IAAWqB,MAAOS,EAAS9B,QAC3BoB,GAAUC,MAAO,GACjBH,KACKe,EAAIH,EAAS9B,OAAQiC,KACtB,QAAUH,GAASG,GAIvBH,EAASG,GAAGjB,KAAKC,EAAiBC,EAASe,EAAGF,EAAI/B,EAAQoB,GAAQY,IAHlEd,EAAQe,GAAKH,EAASG,GACtBjC,EAAOqB,QAKX,OAAIrB,GAAOqB,OAAS,GAAKrB,EAAOqB,QAAUD,EAAMC,UAC9CU,GAAGb,GADL,UAMGb,KAETf,EAAc,SAAU4C,GACtB,GAMIC,GANAC,EAAKC,SAAUC,EAAS,GAAIF,GAAG,iBAC/BG,EAAWD,EAAOC,SAClBC,EAAOD,EAASC,MAAQD,EAASE,qBAAqB,QAAQ,GAC9DC,EAAgB,UAChBC,EAAoB,IACpBC,KAEAC,EAAkB,SAAUC,EAAKC,GACnC,IAEE,MADAC,cAAaC,QAAQP,EAAgBI,EAAKI,KAAKC,UAAUJ,KAClD,EACP,MAAOK,GACP,GAAIA,EAAEC,KAAKC,cAAcC,QAAQ,UAAY,EAAG,CAC9C,GAAIC,GACAC,IACJ,KAAKD,IAAQR,cACyB,IAAhCQ,EAAKD,QAAQb,IACfe,EAAY9B,KAAKuB,KAAKQ,MAAMV,aAAaQ,IAG7C,OAAIC,GAAYzD,QACdyD,EAAYE,KAAK,SAAUC,EAAGC,GAC5B,MAAOD,GAAEE,MAAQD,EAAEC,QAErB3B,EAAO4B,OAAON,EAAY,GAAGX,KACtBD,EAAgBC,EAAKC,IAG5B,OAIF,SAIFiB,EAAS,SAAUC,GACrB,GAAIxE,GAAU,GAAIyC,GAAK5B,QAAQ,SAAUf,EAASW,GAChD,GAAIgE,GAAM,GAAIC,eACdD,GAAIE,KAAK,MAAOH,GAChBC,EAAIG,mBAAqB,WACA,IAAnBH,EAAII,aACa,MAAfJ,EAAIK,QAAiC,IAAfL,EAAIK,QAAgBL,EAAIM,aAChDjF,GACEkF,QAASP,EAAIM,aACbE,KAAMR,EAAIS,kBAAkB,kBAG9BzE,EAAO,GAAI0E,OAAMV,EAAIW,eAM3BC,WAAW,WACLZ,EAAII,WAAa,GACnBJ,EAAIa,SAEL5C,EAAO6C,SACVd,EAAIe,QAEN,OAAOxF,IAELyF,EAAgB,SAAUC,EAAKC,GACjC,GAAIC,IAAO,GAAIC,KAOf,OANAH,GAAIC,KAAOA,EAAKX,QAChBU,EAAII,aAAeH,EAAKV,KACxBS,EAAIT,KAAOS,EAAIT,MAAQU,EAAKV,KAC5BS,EAAIK,UAAYL,EAAIK,YAAa,EACjCL,EAAIrB,MAAQuB,EACZF,EAAIM,OAASJ,EAA0C,IAAnCF,EAAIM,QAAU9C,GAA0B,GAAK,IAC1DwC,GAELO,EAAU,SAAUP,GACtB,MAAOnB,GAAOmB,EAAIlB,KAAKjD,KAAK,SAAUtB,GACpC,GAAIqD,GAAWmC,EAAcC,EAAKzF,EAIlC,OAHKyF,GAAIK,WACP3C,EAAgBsC,EAAIrC,IAAKC,GAEpBA,KAGP4C,EAAe,SAAUC,EAAQT,GACnC,OAAQS,GAAUA,EAAOH,QAAU,GAAIH,MAAS,GAAKH,EAAIU,SAAWD,EAAOC,QAAU1D,EAAO2D,cAAgB3D,EAAO2D,YAAYF,EAAQT,IAErIY,EAAoB,SAAUZ,GAChC,GAAIS,GAAQnG,EAASuG,CACrB,IAAKb,EAAIlB,IA2BT,MAxBAkB,GAAIrC,IAAMqC,EAAIrC,KAAOqC,EAAIlB,IACzB2B,EAASzD,EAAO8D,IAAId,EAAIrC,KACxBqC,EAAIe,QAAUf,EAAIe,WAAY,EAC9BF,EAAcL,EAAaC,EAAQT,GAC/BA,EAAIgB,MAAQH,GACVb,EAAIU,SAENV,EAAIlB,MAAQkB,EAAIlB,IAAIV,QAAQ,KAAO,EAAI,IAAM,KAAO,iBAAmB4B,EAAIU,QAE7EpG,EAAUiG,EAAQP,GACdA,EAAIgB,OAASH,IACfvG,EAAUA,EAAQuB,KAAK,SAAUtB,GAG/B,MAAOA,IACN,WACD,MAAOkG,QAIXA,EAAOlB,KAAOS,EAAIT,MAAQkB,EAAOL,aACjCK,EAAOM,QAAUf,EAAIe,QACrBzG,EAAU,GAAIyC,GAAK3C,QAAQqG,IAEtBnG,GAEL2G,EAAe,SAAUjB,GAC3B,GAAIkB,GAAS9D,EAAS+D,cAAc,SACpCD,GAAOvF,OAAQ,EAGfuF,EAAOE,KAAOpB,EAAIC,KAClB5C,EAAKgE,YAAYH,IAEfI,GAAaC,UAAWN,GACxBF,EAAU,SAAUf,GACtB,MAAIA,GAAIT,MAAQ+B,EAAStB,EAAIT,MACpB+B,EAAStB,EAAIT,MAAMS,GAErBsB,EAAS,WAAWtB,IAEzBwB,EAAiB,SAAUC,GAE7B,IAAK,GADDzB,GACKrF,EAAI,EAAGA,EAAI8G,EAAU5G,OAAQF,IACpCqF,EAAMyB,EAAU9G,GACZqF,EAAIe,SACNA,EAAQf,EAGZ,OAAOjD,GAAK3C,QAAQqH,IAElBC,EAAQ,WACV,GAAI/G,GAAGmC,EAAGH,IACV,KAAKhC,EAAI,EAAGmC,EAAItC,UAAUK,OAAYiC,EAAJnC,EAAOA,IACvCgC,EAASH,KAAKoE,EAAkBpG,UAAUG,IAE5C,OAAOoC,GAAKL,IAAIC,IAEdgF,EAAc,WAChB,GAAIF,GAAYC,EAAMtG,MAAM,KAAMZ,WAC9BF,EAAUL,KAAK4B,KAAK,WACtB,MAAO4F,KACN5F,KAAK2F,EAER,OADAlH,GAAQqH,YAAcA,EACfrH,EAuDT,OArDA0C,IACE4E,QAAS,WACP,IAAK,GAAInD,GAAI,EAAG3B,EAAItC,UAAUK,OAAYiC,EAAJ2B,EAAOA,IAC3CjE,UAAUiE,GAAGsC,QAAUvG,UAAUiE,GAAGsC,WAAY,EAC5CvG,UAAUiE,GAAGoD,MAAQpE,EAASW,QAAQ5D,UAAUiE,GAAGK,MAAQ,EAC7DtE,UAAUiE,GAAGsC,SAAU,EACdvG,UAAUiE,GAAGsC,WAAY,GAAStD,EAASW,QAAQ5D,UAAUiE,GAAGK,KAAO,GAChFrB,EAASjB,KAAKhC,UAAUiE,GAAGK,IAG/B,IAAIxE,GAAUoH,EAAMtG,MAAM,KAAMZ,WAAWqB,KAAK2F,EAEhD,OADAlH,GAAQqH,YAAcA,EACfrH,GAETsE,OAAQ,SAAUjB,GAEhB,MADAE,cAAaiE,WAAWvE,EAAgBI,GACjC1D,MAET6G,IAAK,SAAUnD,GACb,GAAIU,GAAOR,aAAakE,QAAQxE,EAAgBI,EAChD,KACE,MAAOI,MAAKQ,MAAMF,GAAQ,SAC1B,MAAOJ,GACP,OAAO,IAGX+D,MAAO,SAAUC,GACf,GAAI5D,GAAMV,EACNuC,GAAO,GAAIC,KACf,KAAK9B,IAAQR,cACXF,EAAMU,EAAK6D,MAAM3E,GAAe,GAC5BI,KAASsE,GAAWhI,KAAK6G,IAAInD,GAAK2C,QAAUJ,IAC9CjG,KAAK2E,OAAOjB,EAGhB,OAAO1D,OAET0G,YAAa,KACbd,QAAS,IACTsC,WAAY,SAAUC,EAAO1G,GACtB2G,MAAMC,QAAQF,KACjBA,GAASA,IAEXA,EAAMG,QAAQ,SAAUhD,GACtB+B,EAAS/B,GAAQ7D,KAGrB8G,cAAe,SAAUJ,GACvBpF,EAAOmF,WAAWC,EAAOK,UAI7BzF,EAAOgF,OAAM,GACNhF,GACP9C","file":"basket.full.min.js"} \ No newline at end of file diff --git a/dist/basket.js b/dist/basket.js index aba094d..e2c8f1c 100644 --- a/dist/basket.js +++ b/dist/basket.js @@ -1,278 +1,223 @@ /*! * basket.js -* v0.5.2 - 2015-02-07 +* v0.5.2 - 2016-05-06 * http://addyosmani.github.com/basket.js * (c) Addy Osmani; License * Created by: Addy Osmani, Sindre Sorhus, Andrée Hansson, Mat Scales * Contributors: Ironsjp, Mathias Bynens, Rick Waldron, Felipe Morais -* Uses rsvp.js, https://github.com/tildeio/rsvp.js -*/(function( window, document ) { - 'use strict'; - - var head = document.head || document.getElementsByTagName('head')[0]; - var storagePrefix = 'basket-'; - var defaultExpiration = 5000; - var inBasket = []; - - var addLocalStorage = function( key, storeObj ) { - try { - localStorage.setItem( storagePrefix + key, JSON.stringify( storeObj ) ); - return true; - } catch( e ) { - if ( e.name.toUpperCase().indexOf('QUOTA') >= 0 ) { - var item; - var tempScripts = []; - - for ( item in localStorage ) { - if ( item.indexOf( storagePrefix ) === 0 ) { - tempScripts.push( JSON.parse( localStorage[ item ] ) ); - } - } - - if ( tempScripts.length ) { - tempScripts.sort(function( a, b ) { - return a.stamp - b.stamp; - }); - - basket.remove( tempScripts[ 0 ].key ); - - return addLocalStorage( key, storeObj ); - - } else { - // no files to remove. Larger than available quota - return; - } - - } else { - // some other error - return; - } - } - - }; - - var getUrl = function( url ) { - var promise = new RSVP.Promise( function( resolve, reject ){ - - var xhr = new XMLHttpRequest(); - xhr.open( 'GET', url ); - - xhr.onreadystatechange = function() { - if ( xhr.readyState === 4 ) { - if ( ( xhr.status === 200 ) || - ( ( xhr.status === 0 ) && xhr.responseText ) ) { - resolve( { - content: xhr.responseText, - type: xhr.getResponseHeader('content-type') - } ); - } else { - reject( new Error( xhr.statusText ) ); - } - } - }; - - // By default XHRs never timeout, and even Chrome doesn't implement the - // spec for xhr.timeout. So we do it ourselves. - setTimeout( function () { - if( xhr.readyState < 4 ) { - xhr.abort(); - } - }, basket.timeout ); - - xhr.send(); - }); - - return promise; - }; - - var saveUrl = function( obj ) { - return getUrl( obj.url ).then( function( result ) { - var storeObj = wrapStoreData( obj, result ); - - if (!obj.skipCache) { - addLocalStorage( obj.key , storeObj ); - } - - return storeObj; - }); - }; - - var wrapStoreData = function( obj, data ) { - var now = +new Date(); - obj.data = data.content; - obj.originalType = data.type; - obj.type = obj.type || data.type; - obj.skipCache = obj.skipCache || false; - obj.stamp = now; - obj.expire = now + ( ( obj.expire || defaultExpiration ) * 60 * 60 * 1000 ); - - return obj; - }; - - var isCacheValid = function(source, obj) { - return !source || - source.expire - +new Date() < 0 || - obj.unique !== source.unique || - (basket.isValidItem && !basket.isValidItem(source, obj)); - }; - - var handleStackObject = function( obj ) { - var source, promise, shouldFetch; - - if ( !obj.url ) { - return; - } - - obj.key = ( obj.key || obj.url ); - source = basket.get( obj.key ); - - obj.execute = obj.execute !== false; - - shouldFetch = isCacheValid(source, obj); - - if( obj.live || shouldFetch ) { - if ( obj.unique ) { - // set parameter to prevent browser cache - obj.url += ( ( obj.url.indexOf('?') > 0 ) ? '&' : '?' ) + 'basket-unique=' + obj.unique; - } - promise = saveUrl( obj ); - - if( obj.live && !shouldFetch ) { - promise = promise - .then( function( result ) { - // If we succeed, just return the value - // RSVP doesn't have a .fail convenience method - return result; - }, function() { - return source; - }); - } - } else { - source.type = obj.type || source.originalType; - source.execute = obj.execute; - promise = new RSVP.Promise( function( resolve ){ - resolve( source ); - }); - } - - return promise; - }; - - var injectScript = function( obj ) { - var script = document.createElement('script'); - script.defer = true; - // Have to use .text, since we support IE8, - // which won't allow appending to a script - script.text = obj.data; - head.appendChild( script ); - }; - - var handlers = { - 'default': injectScript - }; - - var execute = function( obj ) { - if( obj.type && handlers[ obj.type ] ) { - return handlers[ obj.type ]( obj ); - } - - return handlers['default']( obj ); // 'default' is a reserved word - }; - - var performActions = function( resources ) { - return resources.map( function( obj ) { - if( obj.execute ) { - execute( obj ); - } - - return obj; - } ); - }; - - var fetch = function() { - var i, l, promises = []; - - for ( i = 0, l = arguments.length; i < l; i++ ) { - promises.push( handleStackObject( arguments[ i ] ) ); - } - - return RSVP.all( promises ); - }; - - var thenRequire = function() { - var resources = fetch.apply( null, arguments ); - var promise = this.then( function() { - return resources; - }).then( performActions ); - promise.thenRequire = thenRequire; - return promise; - }; - - window.basket = { - require: function() { - for ( var a = 0, l = arguments.length; a < l; a++ ) { - arguments[a].execute = arguments[a].execute !== false; - - if ( arguments[a].once && inBasket.indexOf(arguments[a].url) >= 0 ) { - arguments[a].execute = false; - } else if ( arguments[a].execute !== false && inBasket.indexOf(arguments[a].url) < 0 ) { - inBasket.push(arguments[a].url); - } - } - - var promise = fetch.apply( null, arguments ).then( performActions ); - - promise.thenRequire = thenRequire; - return promise; - }, - - remove: function( key ) { - localStorage.removeItem( storagePrefix + key ); - return this; - }, - - get: function( key ) { - var item = localStorage.getItem( storagePrefix + key ); - try { - return JSON.parse( item || 'false' ); - } catch( e ) { - return false; - } - }, - - clear: function( expired ) { - var item, key; - var now = +new Date(); - - for ( item in localStorage ) { - key = item.split( storagePrefix )[ 1 ]; - if ( key && ( !expired || this.get( key ).expire <= now ) ) { - this.remove( key ); - } - } - - return this; - }, - - isValidItem: null, - - timeout: 5000, - - addHandler: function( types, handler ) { - if( !Array.isArray( types ) ) { - types = [ types ]; - } - types.forEach( function( type ) { - handlers[ type ] = handler; - }); - }, - - removeHandler: function( types ) { - basket.addHandler( types, undefined ); - } - }; - - // delete expired keys - basket.clear( true ); - -})( this, document ); +* Uses rsvp.js, https://github.com/tildeio/rsvp.js or compact-promise +*/;(function(){/*global define, window*/ +var libRSVPwrapper, libBasketjs; +libRSVPwrapper = window.RSVP; +libBasketjs = function (RSVP) { + var Fn = Function, window = new Fn('return this')(); + var document = window.document; + var head = document.head || document.getElementsByTagName('head')[0]; + var storagePrefix = 'basket-'; + var defaultExpiration = 5000; + var inBasket = []; + var basket; + var addLocalStorage = function (key, storeObj) { + try { + localStorage.setItem(storagePrefix + key, JSON.stringify(storeObj)); + return true; + } catch (e) { + if (e.name.toUpperCase().indexOf('QUOTA') >= 0) { + var item; + var tempScripts = []; + for (item in localStorage) { + if (item.indexOf(storagePrefix) === 0) { + tempScripts.push(JSON.parse(localStorage[item])); + } + } + if (tempScripts.length) { + tempScripts.sort(function (a, b) { + return a.stamp - b.stamp; + }); + basket.remove(tempScripts[0].key); + return addLocalStorage(key, storeObj); + } else { + // no files to remove. Larger than available quota + return; + } + } else { + // some other error + return; + } + } + }; + var getUrl = function (url) { + var promise = new RSVP.Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200 || xhr.status === 0 && xhr.responseText) { + resolve({ + content: xhr.responseText, + type: xhr.getResponseHeader('content-type') + }); + } else { + reject(new Error(xhr.statusText)); + } + } + }; + // By default XHRs never timeout, and even Chrome doesn't implement the + // spec for xhr.timeout. So we do it ourselves. + setTimeout(function () { + if (xhr.readyState < 4) { + xhr.abort(); + } + }, basket.timeout); + xhr.send(); + }); + return promise; + }; + var wrapStoreData = function (obj, data) { + var now = +new Date(); + obj.data = data.content; + obj.originalType = data.type; + obj.type = obj.type || data.type; + obj.skipCache = obj.skipCache || false; + obj.stamp = now; + obj.expire = now + (obj.expire || defaultExpiration) * 60 * 60 * 1000; + return obj; + }; + var saveUrl = function (obj) { + return getUrl(obj.url).then(function (result) { + var storeObj = wrapStoreData(obj, result); + if (!obj.skipCache) { + addLocalStorage(obj.key, storeObj); + } + return storeObj; + }); + }; + var isCacheValid = function (source, obj) { + return !source || source.expire - +new Date() < 0 || obj.unique !== source.unique || basket.isValidItem && !basket.isValidItem(source, obj); + }; + var handleStackObject = function (obj) { + var source, promise, shouldFetch; + if (!obj.url) { + return; + } + obj.key = obj.key || obj.url; + source = basket.get(obj.key); + obj.execute = obj.execute !== false; + shouldFetch = isCacheValid(source, obj); + if (obj.live || shouldFetch) { + if (obj.unique) { + // set parameter to prevent browser cache + obj.url += (obj.url.indexOf('?') > 0 ? '&' : '?') + 'basket-unique=' + obj.unique; + } + promise = saveUrl(obj); + if (obj.live && !shouldFetch) { + promise = promise.then(function (result) { + // If we succeed, just return the value + // RSVP doesn't have a .fail convenience method + return result; + }, function () { + return source; + }); + } + } else { + source.type = obj.type || source.originalType; + source.execute = obj.execute; + promise = new RSVP.resolve(source); + } + return promise; + }; + var injectScript = function (obj) { + var script = document.createElement('script'); + script.defer = true; + // Have to use .text, since we support IE8, + // which won't allow appending to a script + script.text = obj.data; + head.appendChild(script); + }; + var handlers = { 'default': injectScript }; + var execute = function (obj) { + if (obj.type && handlers[obj.type]) { + return handlers[obj.type](obj); + } + return handlers['default'](obj); // 'default' is a reserved word + }; + var performActions = function (resources) { + var obj; + for (var i = 0; i < resources.length; i++) { + obj = resources[i]; + if (obj.execute) { + execute(obj); + } + } + return RSVP.resolve(resources); + }; + var fetch = function () { + var i, l, promises = []; + for (i = 0, l = arguments.length; i < l; i++) { + promises.push(handleStackObject(arguments[i])); + } + return RSVP.all(promises); + }; + var thenRequire = function () { + var resources = fetch.apply(null, arguments); + var promise = this.then(function () { + return resources; + }).then(performActions); + promise.thenRequire = thenRequire; + return promise; + }; + basket = { + require: function () { + for (var a = 0, l = arguments.length; a < l; a++) { + arguments[a].execute = arguments[a].execute !== false; + if (arguments[a].once && inBasket.indexOf(arguments[a].url) >= 0) { + arguments[a].execute = false; + } else if (arguments[a].execute !== false && inBasket.indexOf(arguments[a].url) < 0) { + inBasket.push(arguments[a].url); + } + } + var promise = fetch.apply(null, arguments).then(performActions); + promise.thenRequire = thenRequire; + return promise; + }, + remove: function (key) { + localStorage.removeItem(storagePrefix + key); + return this; + }, + get: function (key) { + var item = localStorage.getItem(storagePrefix + key); + try { + return JSON.parse(item || 'false'); + } catch (e) { + return false; + } + }, + clear: function (expired) { + var item, key; + var now = +new Date(); + for (item in localStorage) { + key = item.split(storagePrefix)[1]; + if (key && (!expired || this.get(key).expire <= now)) { + this.remove(key); + } + } + return this; + }, + isValidItem: null, + timeout: 5000, + addHandler: function (types, handler) { + if (!Array.isArray(types)) { + types = [types]; + } + types.forEach(function (type) { + handlers[type] = handler; + }); + }, + removeHandler: function (types) { + basket.addHandler(types, undefined); + } + }; + // delete expired keys + basket.clear(true); + return basket; +}(libRSVPwrapper);return libBasketjs})(); \ No newline at end of file diff --git a/dist/basket.min.js b/dist/basket.min.js index 4cd6f41..ba0ad8d 100644 --- a/dist/basket.min.js +++ b/dist/basket.min.js @@ -1,11 +1,11 @@ /*! * basket.js -* v0.5.2 - 2015-02-07 +* v0.5.2 - 2016-05-06 * http://addyosmani.github.com/basket.js * (c) Addy Osmani; License * Created by: Addy Osmani, Sindre Sorhus, Andrée Hansson, Mat Scales * Contributors: Ironsjp, Mathias Bynens, Rick Waldron, Felipe Morais -* Uses rsvp.js, https://github.com/tildeio/rsvp.js +* Uses rsvp.js, https://github.com/tildeio/rsvp.js or compact-promise */ -!function(a,b){"use strict";var c=b.head||b.getElementsByTagName("head")[0],d="basket-",e=5e3,f=[],g=function(a,b){try{return localStorage.setItem(d+a,JSON.stringify(b)),!0}catch(c){if(c.name.toUpperCase().indexOf("QUOTA")>=0){var e,f=[];for(e in localStorage)0===e.indexOf(d)&&f.push(JSON.parse(localStorage[e]));return f.length?(f.sort(function(a,b){return a.stamp-b.stamp}),basket.remove(f[0].key),g(a,b)):void 0}return}},h=function(a){var b=new RSVP.Promise(function(b,c){var d=new XMLHttpRequest;d.open("GET",a),d.onreadystatechange=function(){4===d.readyState&&(200===d.status||0===d.status&&d.responseText?b({content:d.responseText,type:d.getResponseHeader("content-type")}):c(new Error(d.statusText)))},setTimeout(function(){d.readyState<4&&d.abort()},basket.timeout),d.send()});return b},i=function(a){return h(a.url).then(function(b){var c=j(a,b);return a.skipCache||g(a.key,c),c})},j=function(a,b){var c=+new Date;return a.data=b.content,a.originalType=b.type,a.type=a.type||b.type,a.skipCache=a.skipCache||!1,a.stamp=c,a.expire=c+60*(a.expire||e)*60*1e3,a},k=function(a,b){return!a||a.expire-+new Date<0||b.unique!==a.unique||basket.isValidItem&&!basket.isValidItem(a,b)},l=function(a){var b,c,d;if(a.url)return a.key=a.key||a.url,b=basket.get(a.key),a.execute=a.execute!==!1,d=k(b,a),a.live||d?(a.unique&&(a.url+=(a.url.indexOf("?")>0?"&":"?")+"basket-unique="+a.unique),c=i(a),a.live&&!d&&(c=c.then(function(a){return a},function(){return b}))):(b.type=a.type||b.originalType,b.execute=a.execute,c=new RSVP.Promise(function(a){a(b)})),c},m=function(a){var d=b.createElement("script");d.defer=!0,d.text=a.data,c.appendChild(d)},n={"default":m},o=function(a){return a.type&&n[a.type]?n[a.type](a):n["default"](a)},p=function(a){return a.map(function(a){return a.execute&&o(a),a})},q=function(){var a,b,c=[];for(a=0,b=arguments.length;b>a;a++)c.push(l(arguments[a]));return RSVP.all(c)},r=function(){var a=q.apply(null,arguments),b=this.then(function(){return a}).then(p);return b.thenRequire=r,b};a.basket={require:function(){for(var a=0,b=arguments.length;b>a;a++)arguments[a].execute=arguments[a].execute!==!1,arguments[a].once&&f.indexOf(arguments[a].url)>=0?arguments[a].execute=!1:arguments[a].execute!==!1&&f.indexOf(arguments[a].url)<0&&f.push(arguments[a].url);var c=q.apply(null,arguments).then(p);return c.thenRequire=r,c},remove:function(a){return localStorage.removeItem(d+a),this},get:function(a){var b=localStorage.getItem(d+a);try{return JSON.parse(b||"false")}catch(c){return!1}},clear:function(a){var b,c,e=+new Date;for(b in localStorage)c=b.split(d)[1],c&&(!a||this.get(c).expire<=e)&&this.remove(c);return this},isValidItem:null,timeout:5e3,addHandler:function(a,b){Array.isArray(a)||(a=[a]),a.forEach(function(a){n[a]=b})},removeHandler:function(a){basket.addHandler(a,void 0)}},basket.clear(!0)}(this,document); +!function(){var a,b;return a=window.RSVP,b=function(a){var b,c=Function,d=new c("return this")(),e=d.document,f=e.head||e.getElementsByTagName("head")[0],g="basket-",h=5e3,i=[],j=function(a,c){try{return localStorage.setItem(g+a,JSON.stringify(c)),!0}catch(d){if(d.name.toUpperCase().indexOf("QUOTA")>=0){var e,f=[];for(e in localStorage)0===e.indexOf(g)&&f.push(JSON.parse(localStorage[e]));return f.length?(f.sort(function(a,b){return a.stamp-b.stamp}),b.remove(f[0].key),j(a,c)):void 0}return}},k=function(c){var d=new a.Promise(function(a,d){var e=new XMLHttpRequest;e.open("GET",c),e.onreadystatechange=function(){4===e.readyState&&(200===e.status||0===e.status&&e.responseText?a({content:e.responseText,type:e.getResponseHeader("content-type")}):d(new Error(e.statusText)))},setTimeout(function(){e.readyState<4&&e.abort()},b.timeout),e.send()});return d},l=function(a,b){var c=+new Date;return a.data=b.content,a.originalType=b.type,a.type=a.type||b.type,a.skipCache=a.skipCache||!1,a.stamp=c,a.expire=c+60*(a.expire||h)*60*1e3,a},m=function(a){return k(a.url).then(function(b){var c=l(a,b);return a.skipCache||j(a.key,c),c})},n=function(a,c){return!a||a.expire-+new Date<0||c.unique!==a.unique||b.isValidItem&&!b.isValidItem(a,c)},o=function(c){var d,e,f;if(c.url)return c.key=c.key||c.url,d=b.get(c.key),c.execute=c.execute!==!1,f=n(d,c),c.live||f?(c.unique&&(c.url+=(c.url.indexOf("?")>0?"&":"?")+"basket-unique="+c.unique),e=m(c),c.live&&!f&&(e=e.then(function(a){return a},function(){return d}))):(d.type=c.type||d.originalType,d.execute=c.execute,e=new a.resolve(d)),e},p=function(a){var b=e.createElement("script");b.defer=!0,b.text=a.data,f.appendChild(b)},q={"default":p},r=function(a){return a.type&&q[a.type]?q[a.type](a):q["default"](a)},s=function(b){for(var c,d=0;db;b++)d.push(o(arguments[b]));return a.all(d)},u=function(){var a=t.apply(null,arguments),b=this.then(function(){return a}).then(s);return b.thenRequire=u,b};return b={require:function(){for(var a=0,b=arguments.length;b>a;a++)arguments[a].execute=arguments[a].execute!==!1,arguments[a].once&&i.indexOf(arguments[a].url)>=0?arguments[a].execute=!1:arguments[a].execute!==!1&&i.indexOf(arguments[a].url)<0&&i.push(arguments[a].url);var c=t.apply(null,arguments).then(s);return c.thenRequire=u,c},remove:function(a){return localStorage.removeItem(g+a),this},get:function(a){var b=localStorage.getItem(g+a);try{return JSON.parse(b||"false")}catch(c){return!1}},clear:function(a){var b,c,d=+new Date;for(b in localStorage)c=b.split(g)[1],c&&(!a||this.get(c).expire<=d)&&this.remove(c);return this},isValidItem:null,timeout:5e3,addHandler:function(a,b){Array.isArray(a)||(a=[a]),a.forEach(function(a){q[a]=b})},removeHandler:function(a){b.addHandler(a,void 0)}},b.clear(!0),b}(a)}(); //# sourceMappingURL=basket.min.js.map \ No newline at end of file diff --git a/dist/basket.min.js.map b/dist/basket.min.js.map index a185094..150834c 100644 --- a/dist/basket.min.js.map +++ b/dist/basket.min.js.map @@ -1 +1 @@ -{"version":3,"file":"basket.min.js","sources":["basket.js"],"names":["window","document","head","getElementsByTagName","storagePrefix","defaultExpiration","inBasket","addLocalStorage","key","storeObj","localStorage","setItem","JSON","stringify","e","name","toUpperCase","indexOf","item","tempScripts","push","parse","length","sort","a","b","stamp","basket","remove","getUrl","url","promise","RSVP","Promise","resolve","reject","xhr","XMLHttpRequest","open","onreadystatechange","readyState","status","responseText","content","type","getResponseHeader","Error","statusText","setTimeout","abort","timeout","send","saveUrl","obj","then","result","wrapStoreData","skipCache","data","now","Date","originalType","expire","isCacheValid","source","unique","isValidItem","handleStackObject","shouldFetch","get","execute","live","injectScript","script","createElement","defer","text","appendChild","handlers","default","performActions","resources","map","fetch","i","l","promises","arguments","all","thenRequire","apply","this","require","once","removeItem","getItem","clear","expired","split","addHandler","types","handler","Array","isArray","forEach","removeHandler","undefined"],"mappings":"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"} \ No newline at end of file +{"version":3,"sources":["basket.js"],"names":["libRSVPwrapper","libBasketjs","window","RSVP","basket","Fn","Function","document","head","getElementsByTagName","storagePrefix","defaultExpiration","inBasket","addLocalStorage","key","storeObj","localStorage","setItem","JSON","stringify","e","name","toUpperCase","indexOf","item","tempScripts","push","parse","length","sort","a","b","stamp","remove","getUrl","url","promise","Promise","resolve","reject","xhr","XMLHttpRequest","open","onreadystatechange","readyState","status","responseText","content","type","getResponseHeader","Error","statusText","setTimeout","abort","timeout","send","wrapStoreData","obj","data","now","Date","originalType","skipCache","expire","saveUrl","then","result","isCacheValid","source","unique","isValidItem","handleStackObject","shouldFetch","get","execute","live","injectScript","script","createElement","defer","text","appendChild","handlers","default","performActions","resources","i","fetch","l","promises","arguments","all","thenRequire","apply","this","require","once","removeItem","getItem","clear","expired","split","addHandler","types","handler","Array","isArray","forEach","removeHandler","undefined"],"mappings":"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","file":"basket.min.js"} \ No newline at end of file diff --git a/lib/RSVP.wrapper.js b/lib/RSVP.wrapper.js new file mode 100644 index 0000000..2246ddb --- /dev/null +++ b/lib/RSVP.wrapper.js @@ -0,0 +1,5 @@ +/*global define, window*/ +define(function(){ + 'use strict'; + return window.RSVP; +}); \ No newline at end of file diff --git a/lib/basket.js b/lib/basket.js index 5abff79..854e236 100644 --- a/lib/basket.js +++ b/lib/basket.js @@ -1,11 +1,15 @@ -/*global document, XMLHttpRequest, localStorage, basket, RSVP*/ -(function( window, document ) { +/*global define, XMLHttpRequest, localStorage*/ +define(['./RSVP.wrapper'], function(RSVP) { 'use strict'; + var Fn = Function, window = new Fn('return this')(); + var document = window.document; + var head = document.head || document.getElementsByTagName('head')[0]; var storagePrefix = 'basket-'; var defaultExpiration = 5000; var inBasket = []; + var basket; var addLocalStorage = function( key, storeObj ) { try { @@ -78,18 +82,6 @@ return promise; }; - var saveUrl = function( obj ) { - return getUrl( obj.url ).then( function( result ) { - var storeObj = wrapStoreData( obj, result ); - - if (!obj.skipCache) { - addLocalStorage( obj.key , storeObj ); - } - - return storeObj; - }); - }; - var wrapStoreData = function( obj, data ) { var now = +new Date(); obj.data = data.content; @@ -102,6 +94,18 @@ return obj; }; + var saveUrl = function( obj ) { + return getUrl( obj.url ).then( function( result ) { + var storeObj = wrapStoreData( obj, result ); + + if (!obj.skipCache) { + addLocalStorage( obj.key , storeObj ); + } + + return storeObj; + }); + }; + var isCacheValid = function(source, obj) { return !source || source.expire - +new Date() < 0 || @@ -143,9 +147,7 @@ } else { source.type = obj.type || source.originalType; source.execute = obj.execute; - promise = new RSVP.Promise( function( resolve ){ - resolve( source ); - }); + promise = new RSVP.resolve(source); } return promise; @@ -173,13 +175,14 @@ }; var performActions = function( resources ) { - return resources.map( function( obj ) { + var obj; + for(var i = 0; i < resources.length; i++) { + obj = resources[i]; if( obj.execute ) { execute( obj ); } - - return obj; - } ); + } + return RSVP.resolve(resources); }; var fetch = function() { @@ -201,7 +204,7 @@ return promise; }; - window.basket = { + basket = { require: function() { for ( var a = 0, l = arguments.length; a < l; a++ ) { arguments[a].execute = arguments[a].execute !== false; @@ -268,4 +271,5 @@ // delete expired keys basket.clear( true ); -})( this, document ); + return basket; +}); \ No newline at end of file diff --git a/package.json b/package.json index 3bd395a..31b1607 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "test": "bower install && grunt test" }, "devDependencies": { + "amdclean": "^2.7.0", "bower": "1.x", "grunt": "^0.4.0", "grunt-cli": "^0.1.13", @@ -68,7 +69,10 @@ "grunt-contrib-connect": "^0.9.0", "grunt-contrib-jshint": "^0.11.0", "grunt-contrib-qunit": "^0.5.1", + "grunt-contrib-requirejs": "^0.3.0", "grunt-contrib-uglify": "^0.7.0", - "grunt-contrib-watch": "^0.6.1" + "grunt-contrib-watch": "^0.6.1", + "grunt-umd": "^2.3.5", + "requirejs": "~2.1.11" } } diff --git a/test/bundled.html b/test/bundled.html new file mode 100644 index 0000000..81df5d0 --- /dev/null +++ b/test/bundled.html @@ -0,0 +1,21 @@ + + + + + QUnit Tests + + + + + + + + +

Basket.js tests

+

+
    +
    +
      +
      + + diff --git a/test/config.js b/test/config.js new file mode 100644 index 0000000..4d60a68 --- /dev/null +++ b/test/config.js @@ -0,0 +1,8 @@ +require({ + 'paths': { + 'qunit': '../bower_components/qunit/qunit/qunit' + } +}, ['./tests'], function (tests) { + 'use strict'; + tests(); +}); \ No newline at end of file diff --git a/test/index.html b/test/modular.html similarity index 56% rename from test/index.html rename to test/modular.html index 779e801..a859cb4 100644 --- a/test/index.html +++ b/test/modular.html @@ -3,13 +3,14 @@ QUnit Tests - + + + + - + - -

      Basket.js tests

      diff --git a/test/tests.js b/test/tests.js index 4b9599e..c96ac66 100644 --- a/test/tests.js +++ b/test/tests.js @@ -1,780 +1,849 @@ -/*global module, asyncTest, test, ok, start, basket, sinon*/ -'use strict'; -module( 'Test script API', { - teardown: function() { - localStorage.clear(); - basket.fail = false; - basket.isValidItem = null; - basket.first = 0; - basket.second = 0; - } -}); - - -asyncTest( 'require() 1 script', 2, function() { - var cancel = setTimeout(function() { - ok( false, 'Callback never invoked' ); - start(); - }, 2500); - - basket.require( - {url: 'fixtures/jquery.min.js'} - ) - .then(function() { - clearTimeout( cancel ); - - ok( true, 'Callback invoked' ); - ok( basket.get('fixtures/jquery.min.js'), 'Data exists in localStorage' ); - - start(); - }); -}); - - -asyncTest( 'require() 2 scripts with .then()', 3, function() { - var cancel = setTimeout(function() { - ok( false, 'Callback never invoked' ); - start(); - }, 2500); - - basket.require( - { url: 'fixtures/jquery.min.js' }, - { url: 'fixtures/modernizr.min.js' } - ) - .then(function() { - clearTimeout( cancel ); - - ok( true, 'Callback invoked' ); - ok( basket.get('fixtures/jquery.min.js'), 'Data exists in localStorage' ); - ok( basket.get('fixtures/modernizr.min.js'), 'Data exists in localStorage' ); - - start(); - }); -}); +/*global sinon, define, window*/ +define([ + 'require' +], function( + require +){ + 'use strict'; + var modulePath = (window.isBundled?'../dist/basket.full':'../lib/basket'); + var basket; + var qunit = window.QUnit; + var module = qunit.module; + var test = qunit.test; + + module( 'Test script API', { + beforeEach: function(assert) { + var done = assert.async(); + require([modulePath], function (bsk) { + basket = bsk; + window.basket = bsk; + done(); + }); + }, + afterEach: function() { + localStorage.clear(); + basket.fail = false; + basket.isValidItem = null; + basket.first = 0; + basket.second = 0; + delete window.basket; + } + }); -asyncTest( 'require() 2 scripts (one non-executed) with .then()', 4, function() { - var cancel = setTimeout(function() { - ok( false, 'Callback never invoked' ); - start(); - }, 2500); + test( 'require() 1 script', 2, function(assert) { + var done = assert.async(); + var cancel = setTimeout(function() { + assert.ok( false, 'Callback never invoked' ); + done(); + }, 2500); - basket.require( - { url: 'fixtures/fail-script.js', execute: false }, - { url: 'fixtures/modernizr.min.js' } + basket.require( + {url: 'fixtures/jquery.min.js'} ) .then(function() { clearTimeout( cancel ); - ok( true, 'Callback invoked' ); - ok( basket.get('fixtures/modernizr.min.js'), 'Data exists in localStorage' ); - ok( basket.get('fixtures/fail-script.js'), 'Data exists in localStorage' ); - ok( basket.fail !== true, 'Script not executed' ); - - start(); + assert.ok( true, 'Callback invoked' ); + assert.ok( basket.get('fixtures/jquery.min.js'), 'Data exists in localStorage' ); + done(); }); -}); + }); -asyncTest( 'require(), custom key', 1, function() { - var key = +new Date(); + test( 'require() 2 scripts with .then()', 3, function(assert) { + var done = assert.async(); + var cancel = setTimeout(function() { + assert.ok( false, 'Callback never invoked' ); + done(); + }, 2500); - basket - .require({ url: 'fixtures/jquery.min.js', key: key }) - .then(function() { - ok( basket.get(key), 'Data exists in localStorage under custom key' ); + basket.require( + { url: 'fixtures/jquery.min.js' }, + { url: 'fixtures/modernizr.min.js' } + ) + .then(function() { + clearTimeout( cancel ); - start(); - }); -}); + assert.ok( true, 'Callback invoked' ); + assert.ok( basket.get('fixtures/jquery.min.js'), 'Data exists in localStorage' ); + assert.ok( basket.get('fixtures/modernizr.min.js'), 'Data exists in localStorage' ); + done(); + }); + }); -asyncTest( 'require() doesn\'t execute', 1, function() { - var cancel = setTimeout(function() { - ok( false, 'Callback never invoked' ); - start(); - }, 2500); - basket.require( - { url: 'fixtures/executefalse.js', execute: false } - ) - .then(function() { + test( 'require() 2 scripts (one non-executed) with .then()', 4, function(assert) { + var done = assert.async(); + var cancel = setTimeout(function() { + assert.ok( false, 'Callback never invoked' ); + done(); + }, 2500); - clearTimeout( cancel ); + basket.require( + { url: 'fixtures/fail-script.js', execute: false }, + { url: 'fixtures/modernizr.min.js' } + ) + .then(function() { + clearTimeout( cancel ); - ok( typeof basket.executed === 'undefined', 'Scipt executed' ); + assert.ok( true, 'Callback invoked' ); + assert.ok( basket.get('fixtures/modernizr.min.js'), 'Data exists in localStorage' ); + assert.ok( basket.get('fixtures/fail-script.js'), 'Data exists in localStorage' ); + assert.ok( basket.fail !== true, 'Script not executed' ); - start(); + done(); + }); }); -}); -asyncTest( 'require() twice doesn\'t execute on secound', 1, function() { - var cancel = setTimeout(function() { - ok( false, 'Callback never invoked' ); - start(); - }, 2500); + test( 'require(), custom key', 1, function(assert) { + var done = assert.async(); + var key = +new Date(); + + basket + .require({ url: 'fixtures/jquery.min.js', key: key }) + .then(function() { + assert.ok( basket.get(key), 'Data exists in localStorage under custom key' ); + + done(); + }); + }); + - basket.require( - { url: 'fixtures/executefalse2.js' } - ) - .then(function() { - basket.executed2 = undefined; + test( 'require() doesn\'t execute', 1, function(assert) { + var done = assert.async(); + var cancel = setTimeout(function() { + assert.ok( false, 'Callback never invoked' ); + done(); + }, 2500); basket.require( - { url: 'fixtures/executefalse2.js', execute: false } + { url: 'fixtures/executefalse.js', execute: false } ) .then(function() { clearTimeout( cancel ); - ok( typeof basket.executed2 === 'undefined', 'Scipt executed' ); + assert.ok( typeof basket.executed === 'undefined', 'Scipt executed' ); - start(); + done(); }); }); -}); -asyncTest( 'require() once', 1, function() { - var cancel = setTimeout(function() { - ok( false, 'Callback never invoked' ); - start(); - }, 2500); + test( 'require() twice doesn\'t execute on secound', 1, function(assert) { + var done = assert.async(); + var cancel = setTimeout(function() { + assert.ok( false, 'Callback never invoked' ); + done(); + }, 2500); - basket.require( - { url: 'fixtures/once.js', once: true } - ) - .then(function() { basket.require( - { url: 'fixtures/once.js', once: true } + { url: 'fixtures/executefalse2.js' } ) .then(function() { - clearTimeout( cancel ); + basket.executed2 = undefined; - ok( basket.once === 1, 'Script loaded twice' ); + basket.require( + { url: 'fixtures/executefalse2.js', execute: false } + ) + .then(function() { - start(); + clearTimeout( cancel ); + + assert.ok( typeof basket.executed2 === 'undefined', 'Scipt executed' ); + + done(); + }); }); }); -}); -asyncTest( 'require() once (force reload)', 1, function() { - var cancel = setTimeout(function() { - ok( false, 'Callback never invoked' ); - start(); - }, 2500); + test( 'require() once', 1, function(assert) { + var done = assert.async(); + var cancel = setTimeout(function() { + assert.ok( false, 'Callback never invoked' ); + done(); + }, 2500); - basket.require( - { url: 'fixtures/once2.js', once: true } - ) - .then(function() { basket.require( - { url: 'fixtures/once2.js' } + { url: 'fixtures/once.js', once: true } ) .then(function() { - clearTimeout( cancel ); + basket.require( + { url: 'fixtures/once.js', once: true } + ) + .then(function() { + clearTimeout( cancel ); - ok( basket.once2 === 2, 'Script loaded once' ); + assert.ok( basket.once === 1, 'Script loaded twice' ); - start(); + done(); + }); }); }); -}); -asyncTest( 'clear()', 1, function() { - basket - .require({ url: 'fixtures/jquery.min.js' }) + test( 'require() once (force reload)', 1, function(assert) { + var done = assert.async(); + var cancel = setTimeout(function() { + assert.ok( false, 'Callback never invoked' ); + done(); + }, 2500); + + basket.require( + { url: 'fixtures/once2.js', once: true } + ) .then(function() { - basket.clear(); - ok( !basket.get('fixtures/jquery.min.js'), 'basket.js data in localStorage cleared' ); + basket.require( + { url: 'fixtures/once2.js' } + ) + .then(function() { + clearTimeout( cancel ); - start(); + assert.ok( basket.once2 === 2, 'Script loaded once' ); + + done(); + }); }); -}); + }); -asyncTest( 'clear( expired ) - remove only expired keys ', 2, function() { - basket - .require( - { url: 'fixtures/largeScript.js', key: 'largeScript0', expire: -1 }, - { url: 'fixtures/largeScript.js', key: 'largeScript1' } - ).then(function() { - basket.clear( true ); - // check if scripts added was removed from localStorage - ok( !basket.get( 'largeScript0' ) , 'Expired script removed' ); - ok( basket.get( 'largeScript1' ) , 'Non-expired script exists in localstorage' ); + test( 'clear()', 1, function(assert) { + var done = assert.async(); + basket + .require({ url: 'fixtures/jquery.min.js' }) + .then(function() { + basket.clear(); + assert.ok( !basket.get('fixtures/jquery.min.js'), 'basket.js data in localStorage cleared' ); - start(); - }); -}); + done(); + }); + }); -asyncTest( 'store data using expiration (non-expired)', 2, function() { - basket - .require({ url: 'fixtures/stamp-script.js', expire: 1 }) - .then(function() { - var stamp = basket.get('fixtures/stamp-script.js').stamp; - ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); + test( 'clear( expired ) - remove only expired keys ', 2, function(assert) { + var done = assert.async(); + basket + .require( + { url: 'fixtures/largeScript.js', key: 'largeScript0', expire: -1 }, + { url: 'fixtures/largeScript.js', key: 'largeScript1' } + ).then(function() { + basket.clear( true ); + // check if scripts added was removed from localStorage + assert.ok( !basket.get( 'largeScript0' ) , 'Expired script removed' ); + assert.ok( basket.get( 'largeScript1' ) , 'Non-expired script exists in localstorage' ); + + done(); + }); + }); - basket - .require({ url: 'fixtures/stamp-script.js' }) - .then(function() { - var stampAfter = basket.get('fixtures/stamp-script.js').stamp; - ok( stamp === stampAfter, 'Data retrieved from localStorage' ); - start(); - }); - }); -}); + test( 'store data using expiration (non-expired)', 2, function(assert) { + var done = assert.async(); + basket + .require({ url: 'fixtures/stamp-script.js', expire: 1 }) + .then(function() { + var stamp = basket.get('fixtures/stamp-script.js').stamp; + assert.ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); + basket + .require({ url: 'fixtures/stamp-script.js' }) + .then(function() { + var stampAfter = basket.get('fixtures/stamp-script.js').stamp; + assert.ok( stamp === stampAfter, 'Data retrieved from localStorage' ); -asyncTest( 'store data using expiration (expired)', 2, function() { - basket - .require({ url: 'fixtures/stamp-script.js', expire: -1 }) - .then(function() { - var stamp = basket.get('fixtures/stamp-script.js').stamp; - ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); + done(); + }); + }); + }); - basket - .require({ url: 'fixtures/stamp-script.js' }) - .then(function() { - var stampAfter = basket.get('fixtures/stamp-script.js').stamp; - ok( stamp !== stampAfter, 'Data retrieved from server' ); - start(); - }); - }); -}); + test( 'store data using expiration (expired)', 2, function(assert) { + var done = assert.async(); + basket + .require({ url: 'fixtures/stamp-script.js', expire: -1 }) + .then(function() { + var stamp = basket.get('fixtures/stamp-script.js').stamp; + assert.ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); + basket + .require({ url: 'fixtures/stamp-script.js' }) + .then(function() { + var stampAfter = basket.get('fixtures/stamp-script.js').stamp; + assert.ok( stamp !== stampAfter, 'Data retrieved from server' ); -asyncTest( 'get()', 2, function() { - basket - .require({ url: 'fixtures/jquery.min.js', key: 'jquery' }) - .then(function() { - ok( basket.get('jquery'), 'Data retrieved under custom key' ); - ok( !basket.get('anotherkey').stamp, 'No Data retrieved under custom key' ); + done(); + }); + }); + }); - start(); - }); -}); + test( 'get()', 2, function(assert) { + var done = assert.async(); + basket + .require({ url: 'fixtures/jquery.min.js', key: 'jquery' }) + .then(function() { + assert.ok( basket.get('jquery'), 'Data retrieved under custom key' ); + assert.ok( !basket.get('anotherkey').stamp, 'No Data retrieved under custom key' ); -asyncTest( 'store data using file-versioning (not previous explicit version)', 3, function() { - basket - .require({ url: 'fixtures/stamp-script.js' }) - .then(function() { - var stamp = basket.get('fixtures/stamp-script.js').stamp; - ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); + done(); + }); + }); - basket - .require({ url: 'fixtures/stamp-script.js', unique: 123 }) - .then(function() { - var req = basket.get('fixtures/stamp-script.js'); - ok( stamp !== req.stamp, 'Data retrieved from server' ); - ok( req.url.indexOf('basket-unique=123') > 0, 'Sending basket unique parameter' ); - start(); - }); - }); -}); + test( 'store data using file-versioning (not previous explicit version)', 3, function(assert) { + var done = assert.async(); + basket + .require({ url: 'fixtures/stamp-script.js' }) + .then(function() { + var stamp = basket.get('fixtures/stamp-script.js').stamp; + assert.ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); + + basket + .require({ url: 'fixtures/stamp-script.js', unique: 123 }) + .then(function() { + var req = basket.get('fixtures/stamp-script.js'); + assert.ok( stamp !== req.stamp, 'Data retrieved from server' ); + assert.ok( req.url.indexOf('basket-unique=123') > 0, 'Sending basket unique parameter' ); + + done(); + }); + }); + }); -asyncTest( 'store data using file-versioning (same release)', 2, function() { - basket - .require({ url: 'fixtures/stamp-script.js', unique: 123 }) - .then(function() { - var stamp = basket.get('fixtures/stamp-script.js').stamp; - ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); + test( 'store data using file-versioning (same release)', 2, function(assert) { + var done = assert.async(); + basket + .require({ url: 'fixtures/stamp-script.js', unique: 123 }) + .then(function() { + var stamp = basket.get('fixtures/stamp-script.js').stamp; + assert.ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); - basket - .require({ url: 'fixtures/stamp-script.js', unique: 123 }) - .then(function() { - var stampAfter = basket.get('fixtures/stamp-script.js').stamp; - ok( stamp === stampAfter, 'Data retrieved from server' ); + basket + .require({ url: 'fixtures/stamp-script.js', unique: 123 }) + .then(function() { + var stampAfter = basket.get('fixtures/stamp-script.js').stamp; + assert.ok( stamp === stampAfter, 'Data retrieved from server' ); - start(); - }); - }); -}); + done(); + }); + }); + }); -asyncTest( 'store data using file-versioning (different release)', 3, function() { - basket - .require({ url: 'fixtures/stamp-script.js', unique: 123 }) - .then(function() { - var stamp = basket.get('fixtures/stamp-script.js').stamp; - ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); + test( 'store data using file-versioning (different release)', 3, function(assert) { + var done = assert.async(); + basket + .require({ url: 'fixtures/stamp-script.js', unique: 123 }) + .then(function() { + var stamp = basket.get('fixtures/stamp-script.js').stamp; + assert.ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); + + basket + .require({ url: 'fixtures/stamp-script.js', unique: 456 }) + .then(function() { + var req = basket.get('fixtures/stamp-script.js'); + assert.ok( stamp !== req.stamp, 'Data retrieved from server' ); + assert.ok( req.url.indexOf('basket-unique=456') > 0, 'Sending basket unique parameter' ); + done(); + }); + }); + }); + + + test( 'remove oldest script in localStorage when Quote Exceeded', 2, function(assert) { + var done = assert.async(); + var i = 0; + var l = 10; + (function add() { + // Try add script in localStorage basket - .require({ url: 'fixtures/stamp-script.js', unique: 456 }) + .require({ url: 'fixtures/largeScript.js', key: 'largeScript' + i }) .then(function() { - var req = basket.get('fixtures/stamp-script.js'); - ok( stamp !== req.stamp, 'Data retrieved from server' ); - ok( req.url.indexOf('basket-unique=456') > 0, 'Sending basket unique parameter' ); - start(); + if ( i < l ) { + // add one more file + add( ++i ); + } else { + // check if first script added was removed from localStorage + assert.ok( !basket.get( 'largeScript0' ) , 'First Script deleted' ); + // check if the last script added still on localStorage + assert.ok( basket.get( 'largeScript10' ) , 'Last Script still alive' ); + done(); + } }); - }); -}); + })(); + }); + /* + test( 'file is larger than quota limit ', 2, function(assert) { + var done = assert.async(); + basket + .require({ url: 'fixtures/largeScript.js', key: 'largeScript0' }, { url: 'fixtures/largeScript.js', key: 'largeScript1' }) + .thenRequire({ url: 'fixtures/veryLargeScript.js', key: 'largeScript2' }) + .then(function() { + // check if scripts added was removed from localStorage + assert.ok( !basket.get( 'largeScript0' ) , 'First Script deleted' ); + assert.ok( !basket.get( 'largeScript1' ) , 'Second Script deleted' ); + // check if the last script added still on localStorage + // TODO: Test is now failing in Chrome due to an anomoly, + // but passes in Safari. Investigate later. + // assert.ok( !basket.get( 'largeScript2' ) , 'Last Script not added' ); + done(); + }); + });*/ -asyncTest( 'remove oldest script in localStorage when Quote Exceeded', 2, function() { - var i = 0; - var l = 10; + test( 'non-existant file causes error handler to be called', 2, function(assert) { + var done = assert.async(); + basket + .require({ url: 'non-existant.js' }) + .then(function() { + assert.ok( false, 'The success callback should not be called' ); + done(); + }, function(error) { + assert.ok( error, 'Error callback called' ); + assert.ok( !basket.get( 'non-existant.js' ), 'No cache entry for missing file' ); + done(); + }); + }); - (function add() { - // Try add script in localStorage + test( 'handle the case where localStorage contains something we did not expect', 2, function(assert) { + var done = assert.async(); + localStorage.setItem( 'basket-test', 'An invalid JSON string' ); basket - .require({ url: 'fixtures/largeScript.js', key: 'largeScript' + i }) + .require({ url: 'fixtures/jquery.min.js', key: 'test' }) .then(function() { - if ( i < l ) { - // add one more file - add( ++i ); - } else { - // check if first script added was removed from localStorage - ok( !basket.get( 'largeScript0' ) , 'First Script deleted' ); - // check if the last script added still on localStorage - ok( basket.get( 'largeScript10' ) , 'Last Script still alive' ); - start(); - } + assert.ok( basket.get( 'test' ), 'successfully retrieved the script' ); + assert.ok( basket.get( 'test' ).key === 'test', 'got a valid cache object' ); + done(); }); - })(); -}); - -/* -asyncTest( 'file is larger than quota limit ', 2, function() { - basket - .require({ url: 'fixtures/largeScript.js', key: 'largeScript0' }, { url: 'fixtures/largeScript.js', key: 'largeScript1' }) - .thenRequire({ url: 'fixtures/veryLargeScript.js', key: 'largeScript2' }) - .then(function() { - // check if scripts added was removed from localStorage - ok( !basket.get( 'largeScript0' ) , 'First Script deleted' ); - ok( !basket.get( 'largeScript1' ) , 'Second Script deleted' ); - // check if the last script added still on localStorage - // TODO: Test is now failing in Chrome due to an anomoly, - // but passes in Safari. Investigate later. - // ok( !basket.get( 'largeScript2' ) , 'Last Script not added' ); - start(); - }); -});*/ + }); -asyncTest( 'non-existant file causes error handler to be called', 2, function() { - basket - .require({ url: 'non-existant.js' }) - .then(function() { - ok( false, 'The success callback should not be called' ); - start(); - }, function(error) { - ok( error, 'Error callback called' ); - ok( !basket.get( 'non-existant.js' ), 'No cache entry for missing file' ); - start(); - }); -}); + test( 'chaining with thenRequire', 3, function(assert) { + var done = assert.async(); + basket.clear(); + basket + .require({ url: 'fixtures/first.js', key: 'first' }) + .thenRequire({ url: 'fixtures/second.js', key: 'second' }) + .then(function() { + assert.ok( basket.get( 'first' ), 'first script loaded' ); + assert.ok( basket.get( 'second' ), 'second script loaded' ); + assert.ok( basket.order === 'firstsecond', 'scripts loaded in correct order' ); + done(); + }, function() { + assert.ok( false, 'error handler called unexpectedly' ); + done(); + }); + }); -asyncTest( 'handle the case where localStorage contains something we did not expect', 2, function() { - localStorage.setItem( 'basket-test', 'An invalid JSON string' ); - basket - .require({ url: 'fixtures/jquery.min.js', key: 'test' }) - .then(function() { - start(); - ok( basket.get( 'test' ), 'successfully retrieved the script' ); - ok( basket.get( 'test' ).key === 'test', 'got a valid cache object' ); - }); -}); + test( 'file is fetched from server even if it exists when isValidItem answers no', 2, function(assert) { + var done = assert.async(); + basket + .require({ url: 'fixtures/stamp-script.js'}) + .then(function() { + var stamp = basket.get('fixtures/stamp-script.js').stamp; + assert.ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); + basket.isValidItem = function() { + return false; + }; + basket + .require({ url: 'fixtures/stamp-script.js' }) + .then(function() { + var stampAfter = basket.get('fixtures/stamp-script.js').stamp; + assert.ok( stamp !== stampAfter, 'Data retrieved from server' ); + + done(); + }); + }); + }); -asyncTest( 'chaining with thenRequire', 3, function() { - basket.clear(); - basket - .require({ url: 'fixtures/first.js', key: 'first' }) - .thenRequire({ url: 'fixtures/second.js', key: 'second' }) - .then(function() { - start(); - ok( basket.get( 'first' ), 'first script loaded' ); - ok( basket.get( 'second' ), 'second script loaded' ); - ok( basket.order === 'firstsecond', 'scripts loaded in correct order' ); - }, function() { - start(); - ok( false, 'error handler called unexpectedly' ); - }); -}); + test( 'when first file fails, second file is fetched but not executed', 3, function(assert) { + var done = assert.async(); + var server = sinon.fakeServer.create(); + basket.first = basket.second = 0; + + server.respondWith( 'GET', '/second.js', [ 200, { 'Content-Type': 'text/javascript' }, 'basket.second = 1;' ] ); + + basket.require({ url: '/first.js' }) + .thenRequire({ url: '/second.js' }) + .then( function() {}, + function() { + // time out to make sure below function gets executed at next tick + // so that second request is fully finished. + // compact-promise resolve and reject immediately so it may happen + // quicker then rsvp. + setTimeout(function(){ + assert.ok( !basket.get( '/first.js' ), 'first script failed to load' ); + assert.ok( basket.get( '/second.js' ), 'second script was loaded and stored' ); + assert.ok( basket.second === 0, 'second script did not execute' ); + + done(); + server.restore(); + }); + }); -asyncTest( 'file is fetched from server even if it exists when isValidItem answers no', 2, function() { - basket - .require({ url: 'fixtures/stamp-script.js'}) - .then(function() { - var stamp = basket.get('fixtures/stamp-script.js').stamp; - ok( basket.get('fixtures/stamp-script.js'), 'Data exists in localStorage' ); - basket.isValidItem = function() { - return false; - }; - basket - .require({ url: 'fixtures/stamp-script.js' }) - .then(function() { - var stampAfter = basket.get('fixtures/stamp-script.js').stamp; - ok( stamp !== stampAfter, 'Data retrieved from server' ); + server.respond(); + }); - start(); - }); - }); -}); + test( 'second file is fetched early but executes later', 6, function(assert) { + var done = assert.async(); + var server = sinon.fakeServer.create(); + basket.first = basket.second = 0; -asyncTest( 'when first file fails, second file is fetched but not executed', 3, function() { - var server = sinon.fakeServer.create(); - basket.first = basket.second = 0; - server.respondWith( 'GET', '/second.js', [ 200, { 'Content-Type': 'text/javascript' }, 'basket.second = 1;' ] ); + var firstPromise = basket.require({ url: '/first.js' }); + firstPromise.then( function() { + assert.ok( basket.get( '/second.js' ), 'second script was already loaded and stored' ); + assert.ok( basket.first === 1, 'first script should have been executed' ); + assert.ok( basket.second === 0, 'second script should not have been executed yet' ); + }); - basket.require({ url: '/first.js' }) - .thenRequire({ url: '/second.js' }) - .then( function() {}, - function() { - ok( !basket.get( '/first.js' ), 'first script failed to load' ); - ok( basket.get( '/second.js' ), 'second script was loaded and stored' ); - ok( basket.second === 0, 'second script did not execute' ); + firstPromise + .thenRequire({ url: '/second.js' }) + .then( function() { + assert.ok( basket.first === 1, 'first script is eventually executed' ); + assert.ok( basket.second === 2, 'second script is eventually executed second' ); - start(); + done(); server.restore(); }); - server.respond(); -}); - -asyncTest( 'second file is fetched early but executes later', 6, function() { - var server = sinon.fakeServer.create(); - basket.first = basket.second = 0; + assert.ok( server.requests.length === 2, 'Both requests have been made' ); + server.requests[ 1 ].respond( 200, { 'Content-Type': 'text/javascript' }, 'basket.second = basket.first + 1;' ); - var firstPromise = basket.require({ url: '/first.js' }); - firstPromise.then( function() { - ok( basket.get( '/second.js' ), 'second script was already loaded and stored' ); - ok( basket.first === 1, 'first script should have been executed' ); - ok( basket.second === 0, 'second script should not have been executed yet' ); + setTimeout( function() { + server.requests[ 0 ].respond( 200, { 'Content-Type': 'text/javascript' }, 'basket.first = 1;' ); + }, 50); }); - firstPromise - .thenRequire({ url: '/second.js' }) - .then( function() { - ok( basket.first === 1, 'first script is eventually executed' ); - ok( basket.second === 2, 'second script is eventually executed second' ); + test( 'with thenRequire all requests fired immediately', 1, function(assert) { + var server = sinon.fakeServer.create(); - start(); - server.restore(); - }); - - ok( server.requests.length === 2, 'Both requests have been made' ); - - server.requests[ 1 ].respond( 200, { 'Content-Type': 'text/javascript' }, 'basket.second = basket.first + 1;' ); + basket + .require({ url: '/first.js' }) + .thenRequire({ url: '/second.js' }) + .thenRequire({ url: '/third.js' }); - setTimeout( function() { - server.requests[ 0 ].respond( 200, { 'Content-Type': 'text/javascript' }, 'basket.first = 1;' ); - }, 50); -}); + assert.ok( server.requests.length === 3, 'all requests were fired' ); -test( 'with thenRequire all requests fired immediately', 1, function() { - var server = sinon.fakeServer.create(); + server.restore(); + }); - basket - .require({ url: '/first.js' }) - .thenRequire({ url: '/second.js' }) - .thenRequire({ url: '/third.js' }); + test( 'the type of the stored object is the Content-Type of the resource', 4, function(assert) { + var done = assert.async(); + basket.clear(); - ok( server.requests.length === 3, 'all requests were fired' ); + var server = sinon.fakeServer.create(); - server.restore(); -}); + server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'Some text' ] ); + server.respondWith( 'GET', '/example.js', [ 200, { 'Content-Type': 'text/javascript' }, 'Some JavaScript' ] ); + server.respondWith( 'GET', '/example.xml', [ 200, { 'Content-Type': 'application/xml' }, 'Some XML' ] ); + server.respondWith( 'GET', '/example.json', [ 200, { 'Content-Type': 'application/json' }, '["some JSON"]' ] ); -asyncTest( 'the type of the stored object is the Content-Type of the resource', 4, function() { - basket.clear(); + // Without execute: false, the default handler will try to execute all of + // these files as JS, leading to Syntax Errors being reported. + basket.require({ url: '/example.txt', execute: false }, { url: '/example.js', execute: false }, { url: '/example.xml', execute: false }, { url: '/example.json', execute: false }) + .then( function() { + assert.ok( basket.get( '/example.txt' ).type === 'text/plain', 'text file had correct type' ); + assert.ok( basket.get( '/example.js' ).type === 'text/javascript', 'javascript file had correct type' ); + assert.ok( basket.get( '/example.xml' ).type === 'application/xml', 'xml file had correct type' ); + assert.ok( basket.get( '/example.json' ).type === 'application/json', 'json file had correct type' ); - var server = sinon.fakeServer.create(); + done(); + server.restore(); + }); - server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'Some text' ] ); - server.respondWith( 'GET', '/example.js', [ 200, { 'Content-Type': 'text/javascript' }, 'Some JavaScript' ] ); - server.respondWith( 'GET', '/example.xml', [ 200, { 'Content-Type': 'application/xml' }, 'Some XML' ] ); - server.respondWith( 'GET', '/example.json', [ 200, { 'Content-Type': 'application/json' }, '["some JSON"]' ] ); + server.respond(); + }); - // Without execute: false, the default handler will try to execute all of - // these files as JS, leading to Syntax Errors being reported. - basket.require({ url: '/example.txt', execute: false }, { url: '/example.js', execute: false }, { url: '/example.xml', execute: false }, { url: '/example.json', execute: false }) - .then( function() { - ok( basket.get( '/example.txt' ).type === 'text/plain', 'text file had correct type' ); - ok( basket.get( '/example.js' ).type === 'text/javascript', 'javascript file had correct type' ); - ok( basket.get( '/example.xml' ).type === 'application/xml', 'xml file had correct type' ); - ok( basket.get( '/example.json' ).type === 'application/json', 'json file had correct type' ); + test( 'the type of the stored object can be overriden at original require time', 1, function(assert) { + var done = assert.async(); + basket.clear(); - start(); - server.restore(); - }); + var server = sinon.fakeServer.create(); - server.respond(); -}); + server.respondWith( 'GET', '/example.json', [ 200, { 'Content-Type': 'application/json' }, '["some JSON"]' ] ); -asyncTest( 'the type of the stored object can be overriden at original require time', 1, function() { - basket.clear(); + basket.require({ url: '/example.json', execute: false, type: 'misc/other' }) + .then( function() { + assert.ok( basket.get( '/example.json' ).type === 'misc/other', 'json file had overriden type' ); - var server = sinon.fakeServer.create(); + done(); + server.restore(); + }); - server.respondWith( 'GET', '/example.json', [ 200, { 'Content-Type': 'application/json' }, '["some JSON"]' ] ); + server.respond(); + }); - basket.require({ url: '/example.json', execute: false, type: 'misc/other' }) - .then( function() { - ok( basket.get( '/example.json' ).type === 'misc/other', 'json file had overriden type' ); + test( 'different types can be handled separately', 1, function(assert) { + var done = assert.async(); + var text = 'some example text'; + var server = sinon.fakeServer.create(); - start(); + basket.clear(); + basket.addHandler( 'text/plain', function( obj ) { + assert.ok( obj.data === text, 'the text/plain handler was used' ); + done(); server.restore(); + basket.removeHandler( 'text/plain' ); }); - server.respond(); -}); + server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, text ] ); -asyncTest( 'different types can be handled separately', 1, function() { - var text = 'some example text'; - var server = sinon.fakeServer.create(); + basket.require({ url: '/example.txt' }); - basket.clear(); - basket.addHandler( 'text/plain', function( obj ) { - ok( obj.data === text, 'the text/plain handler was used' ); - start(); - server.restore(); - basket.removeHandler( 'text/plain' ); + server.respond(); }); - server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, text ] ); + test( 'handlers can be removed', 1, function(assert) { + var done = assert.async(); + var js = '// has to be valid JS to avoid a Syntax Error'; + var handled = 0; + var server = sinon.fakeServer.create(); - basket.require({ url: '/example.txt' }); + basket.clear(); + basket.addHandler( 'text/plain', function() { + handled++; + basket.removeHandler( 'text/plain' ); + }); - server.respond(); -}); + server.respondWith( 'GET', '/example.js', [ 200, { 'Content-Type': 'text/plain' }, js ] ); + server.respondWith( 'GET', '/example2.js', [ 200, { 'Content-Type': 'text/plain' }, js ] ); -asyncTest( 'handlers can be removed', 1, function() { - var js = '// has to be valid JS to avoid a Syntax Error'; - var handled = 0; - var server = sinon.fakeServer.create(); + basket.require({ url: '/example.js' }) + .thenRequire({ url: '/example2.js' }) + .then( function () { + assert.ok( handled === 1, 'the text/plain handler was only used once' ); + done(); + server.restore(); + }); - basket.clear(); - basket.addHandler( 'text/plain', function() { - handled++; - basket.removeHandler( 'text/plain' ); + server.respond(); }); - server.respondWith( 'GET', '/example.js', [ 200, { 'Content-Type': 'text/plain' }, js ] ); - server.respondWith( 'GET', '/example2.js', [ 200, { 'Content-Type': 'text/plain' }, js ] ); + test( 'the same resource can be handled differently', 2, function(assert) { + var done = assert.async(); + var server = sinon.fakeServer.create(); - basket.require({ url: '/example.js' }) - .thenRequire({ url: '/example2.js' }) - .then( function () { - ok( handled === 1, 'the text/plain handler was only used once' ); - start(); - server.restore(); + basket.clear(); + + basket.addHandler( 'first', function() { + assert.ok( true, 'first handler was called' ); }); - server.respond(); -}); + basket.addHandler( 'second', function() { + assert.ok( true, 'second handler was called' ); + done(); + server.restore(); + }); -asyncTest( 'the same resource can be handled differently', 2, function() { - var server = sinon.fakeServer.create(); + server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, '' ] ); - basket.clear(); + basket.require({ url: '/example.txt', type: 'first' }) + .thenRequire({ url: '/example.txt', type: 'second' }); - basket.addHandler( 'first', function() { - ok( true, 'first handler was called' ); + server.respond(); }); - basket.addHandler( 'second', function() { - ok( true, 'second handler was called' ); - start(); - server.restore(); - }); - - server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, '' ] ); + test( 'type falls back to Content-Type, even if previously overriden', 2, function(assert) { + var done = assert.async(); + var server = sinon.fakeServer.create(); - basket.require({ url: '/example.txt', type: 'first' }) - .thenRequire({ url: '/example.txt', type: 'second' }); + basket.clear(); - server.respond(); -}); + basket.addHandler( 'first', function() { + assert.ok( true, 'first handler was called' ); + }); -asyncTest( 'type falls back to Content-Type, even if previously overriden', 2, function() { - var server = sinon.fakeServer.create(); + basket.addHandler( 'text/plain', function() { + assert.ok( true, 'text/plain handler was called' ); + done(); + server.restore(); + }); - basket.clear(); + server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, '' ] ); - basket.addHandler( 'first', function() { - ok( true, 'first handler was called' ); - }); + basket.require({ url: '/example.txt', type: 'first' }) + .thenRequire({ url: '/example.txt' }); - basket.addHandler( 'text/plain', function() { - ok( true, 'text/plain handler was called' ); - start(); - server.restore(); + server.respond(); }); - server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, '' ] ); - - basket.require({ url: '/example.txt', type: 'first' }) - .thenRequire({ url: '/example.txt' }); - - server.respond(); -}); + // This test is here to cover the full set of possibilities for this section + // It doesn't really test anything that hasn't been tested elsewhere + test( 'with live: false, we fallback to the network', 1, function(assert) { + var done = assert.async(); + basket.clear(); + var server = sinon.fakeServer.create(); + server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'foo' ] ); + + basket.require({ url: '/example.txt', execute: false, live: false }) + .then( function() { + assert.ok( basket.get( '/example.txt' ).data === 'foo', 'nothing in the cache so we fetched from the network' ); + server.restore(); + done(); + }); -// This test is here to cover the full set of possibilities for this section -// It doesn't really test anything that hasn't been tested elsewhere -asyncTest( 'with live: false, we fallback to the network', 1, function() { - basket.clear(); - var server = sinon.fakeServer.create(); - server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'foo' ] ); + server.respond(); + }); - basket.require({ url: '/example.txt', execute: false, live: false }) - .then( function() { - ok( basket.get( '/example.txt' ).data === 'foo', 'nothing in the cache so we fetched from the network' ); - server.restore(); - start(); - }); + test( 'with live: false, we attempt to fetch from the cache first', 1, function(assert) { + var done = assert.async(); + basket.clear(); + var server = sinon.fakeServer.create(); + server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'bar' ] ); + + // Add the item directly to the cache + localStorage.setItem( 'basket-/example.txt', JSON.stringify( { + url: '/example.txt', + key: '/example.txt', + data: 'foo', + originalType: 'text/plain', + type: 'text/plain', + stamp: +new Date(), + expire: +new Date() + 5000 * 60 * 60 * 1000 + })); + + basket.require({ url: '/example.txt', execute: false, live: false }) + .then( function() { + assert.ok( basket.get( '/example.txt' ).data === 'foo', 'fetched from the cache rather than getting fresh data from the network' ); + server.restore(); + done(); + }); - server.respond(); -}); - -asyncTest( 'with live: false, we attempt to fetch from the cache first', 1, function() { - basket.clear(); - var server = sinon.fakeServer.create(); - server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'bar' ] ); - - // Add the item directly to the cache - localStorage.setItem( 'basket-/example.txt', JSON.stringify( { - url: '/example.txt', - key: '/example.txt', - data: 'foo', - originalType: 'text/plain', - type: 'text/plain', - stamp: +new Date(), - expire: +new Date() + 5000 * 60 * 60 * 1000 - })); - - basket.require({ url: '/example.txt', execute: false, live: false }) - .then( function() { - ok( basket.get( '/example.txt' ).data === 'foo', 'fetched from the cache rather than getting fresh data from the network' ); - server.restore(); - start(); - }); + server.respond(); + }); - server.respond(); -}); - -asyncTest( 'with live: true, we attempt to fetch from the network first', 1, function() { - basket.clear(); - var server = sinon.fakeServer.create(); - server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'bar' ] ); - - // Add the item directly to the cache - localStorage.setItem( 'basket-/example.txt', JSON.stringify( { - url: '/example.txt', - key: '/example.txt', - data: 'foo', - originalType: 'text/plain', - type: 'text/plain', - stamp: +new Date(), - expire: +new Date() + 5000 * 60 * 60 * 1000 - })); - - basket.require({ url: '/example.txt', execute: false, live: true }) - .then( function() { - ok( basket.get( '/example.txt' ).data === 'bar', 'fetched from the network even though cache was available' ); - server.restore(); - start(); - }); + test( 'with live: true, we attempt to fetch from the network first', 1, function(assert) { + var done = assert.async(); + basket.clear(); + var server = sinon.fakeServer.create(); + server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'bar' ] ); + + // Add the item directly to the cache + localStorage.setItem( 'basket-/example.txt', JSON.stringify( { + url: '/example.txt', + key: '/example.txt', + data: 'foo', + originalType: 'text/plain', + type: 'text/plain', + stamp: +new Date(), + expire: +new Date() + 5000 * 60 * 60 * 1000 + })); + + basket.require({ url: '/example.txt', execute: false, live: true }) + .then( function() { + assert.ok( basket.get( '/example.txt' ).data === 'bar', 'fetched from the network even though cache was available' ); + server.restore(); + done(); + }); - server.respond(); -}); + server.respond(); + }); -asyncTest( 'with live: true, we still store the result in the cache', 1, function() { - basket.clear(); - var server = sinon.fakeServer.create(); - server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'foo' ] ); + test( 'with live: true, we still store the result in the cache', 1, function(assert) { + var done = assert.async(); + basket.clear(); + var server = sinon.fakeServer.create(); + server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'foo' ] ); - basket.require({ url: '/example.txt', execute: false, live: true }) - .then( function() { - ok( basket.get( '/example.txt' ), 'result stored in the cache' ); - server.restore(); - start(); - }); + basket.require({ url: '/example.txt', execute: false, live: true }) + .then( function() { + assert.ok( basket.get( '/example.txt' ), 'result stored in the cache' ); + server.restore(); + done(); + }); - server.respond(); -}); - -asyncTest( 'with live: true, we fallback to the cache', 2, function() { - // TODO: How to test the navigator.onLine case? - basket.clear(); - var server = sinon.fakeServer.create(); - var clock = sinon.useFakeTimers(); - server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'baz' ] ); - - // Add the item directly to the cache - localStorage.setItem( 'basket-/example.txt', JSON.stringify( { - url: '/example.txt', - key: '/example.txt', - data: '12345', - originalType: 'text/plain', - type: 'text/plain', - stamp: +new Date(), - expire: +new Date() + 5000 * 60 * 60 * 1000 - })); - - ok( basket.get( '/example.txt' ), 'already exists in cache' ); - - basket.timeout = 100; - basket.require({ url: '/example.txt', execute: false, live: true }) - .then( function() { - ok( basket.get( '/example.txt' ).data === '12345', 'server timed out, so fetched from cache' ); - server.restore(); - clock.restore(); - start(); - }, function () { - ok( false, 'the require failed due to lack of network, but should have used the cache' ); - server.restore(); - clock.restore(); - start(); - }); + server.respond(); + }); - clock.tick(6000); - server.respond(); - basket.timeout = 5000; -}); + test( 'with live: true, we fallback to the cache', 2, function(assert) { + var done = assert.async(); + // TODO: How to test the navigator.onLine case? + basket.clear(); + var server = sinon.fakeServer.create(); + var clock = sinon.useFakeTimers(); + server.respondWith( 'GET', '/example.txt', [ 200, { 'Content-Type': 'text/plain' }, 'baz' ] ); + + // Add the item directly to the cache + localStorage.setItem( 'basket-/example.txt', JSON.stringify( { + url: '/example.txt', + key: '/example.txt', + data: '12345', + originalType: 'text/plain', + type: 'text/plain', + stamp: +new Date(), + expire: +new Date() + 5000 * 60 * 60 * 1000 + })); + + assert.ok( basket.get( '/example.txt' ), 'already exists in cache' ); + + basket.timeout = 100; + basket.require({ url: '/example.txt', execute: false, live: true }) + .then( function() { + assert.ok( basket.get( '/example.txt' ).data === '12345', 'server timed out, so fetched from cache' ); + server.restore(); + clock.restore(); + done(); + }, function () { + assert.ok( false, 'the require failed due to lack of network, but should have used the cache' ); + server.restore(); + clock.restore(); + done(); + }); -asyncTest( 'with skipCache: true, we do not cache data', 1, function() { - basket - .require({ url: 'fixtures/jquery.min.js', skipCache: true }) - .then(function() { - ok( !basket.get('fixtures/jquery.min.js'), 'Data does not exist in localStorage' ); + clock.tick(6000); + server.respond(); + basket.timeout = 5000; + }); - start(); - }); -}); + test( 'with skipCache: true, we do not cache data', 1, function(assert) { + var done = assert.async(); + basket + .require({ url: 'fixtures/jquery.min.js', skipCache: true }) + .then(function() { + assert.ok( !basket.get('fixtures/jquery.min.js'), 'Data does not exist in localStorage' ); -asyncTest( 'execute a cached script when execute: true', 2, function() { - var cancel = setTimeout(function() { - ok( false, 'Callback never invoked' ); - start(); - }, 2500); + done(); + }); + }); - function requireScript(execute, cb) { - basket.require( - { url: 'fixtures/executefalse.js', execute: execute } - ) - .then(cb); - } + test( 'execute a cached script when execute: true', 2, function(assert) { + var done = assert.async(); + var cancel = setTimeout(function() { + assert.ok( false, 'Callback never invoked' ); + done(); + }, 2500); + + function requireScript(execute, cb) { + basket.require( + { url: 'fixtures/executefalse.js', execute: execute } + ) + .then(cb); + } + + requireScript( false, function() { + clearTimeout( cancel ); - requireScript( false, function() { - clearTimeout( cancel ); + assert.ok( typeof basket.executed === 'undefined', 'None-cached script was not executed' ); - ok( typeof basket.executed === 'undefined', 'None-cached script was not executed' ); + requireScript( true, function() { + assert.ok( basket.executed === true, 'Cached script executed' ); - requireScript( true, function() { - ok( basket.executed === true, 'Cached script executed' ); + delete basket.executed; - start(); + done(); + }); }); }); -}); + + return function(){ + qunit.start(); + }; +}); \ No newline at end of file diff --git a/test/vendor/qunit.css b/test/vendor/qunit.css deleted file mode 100644 index 5684a44..0000000 --- a/test/vendor/qunit.css +++ /dev/null @@ -1,236 +0,0 @@ -/** - * QUnit v1.8.0 - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2012 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. - */ - -/** Font Family and Sizes */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { - font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; -} - -#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } -#qunit-tests { font-size: smaller; } - - -/** Resets */ - -#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { - margin: 0; - padding: 0; -} - - -/** Header */ - -#qunit-header { - padding: 0.5em 0 0.5em 1em; - - color: #8699a4; - background-color: #0d3349; - - font-size: 1.5em; - line-height: 1em; - font-weight: normal; - - border-radius: 15px 15px 0 0; - -moz-border-radius: 15px 15px 0 0; - -webkit-border-top-right-radius: 15px; - -webkit-border-top-left-radius: 15px; -} - -#qunit-header a { - text-decoration: none; - color: #c2ccd1; -} - -#qunit-header a:hover, -#qunit-header a:focus { - color: #fff; -} - -#qunit-header label { - display: inline-block; - padding-left: 0.5em; -} - -#qunit-banner { - height: 5px; -} - -#qunit-testrunner-toolbar { - padding: 0.5em 0 0.5em 2em; - color: #5E740B; - background-color: #eee; -} - -#qunit-userAgent { - padding: 0.5em 0 0.5em 2.5em; - background-color: #2b81af; - color: #fff; - text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; -} - - -/** Tests: Pass/Fail */ - -#qunit-tests { - list-style-position: inside; -} - -#qunit-tests li { - padding: 0.4em 0.5em 0.4em 2.5em; - border-bottom: 1px solid #fff; - list-style-position: inside; -} - -#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { - display: none; -} - -#qunit-tests li strong { - cursor: pointer; -} - -#qunit-tests li a { - padding: 0.5em; - color: #c2ccd1; - text-decoration: none; -} -#qunit-tests li a:hover, -#qunit-tests li a:focus { - color: #000; -} - -#qunit-tests ol { - margin-top: 0.5em; - padding: 0.5em; - - background-color: #fff; - - border-radius: 15px; - -moz-border-radius: 15px; - -webkit-border-radius: 15px; - - box-shadow: inset 0px 2px 13px #999; - -moz-box-shadow: inset 0px 2px 13px #999; - -webkit-box-shadow: inset 0px 2px 13px #999; -} - -#qunit-tests table { - border-collapse: collapse; - margin-top: .2em; -} - -#qunit-tests th { - text-align: right; - vertical-align: top; - padding: 0 .5em 0 0; -} - -#qunit-tests td { - vertical-align: top; -} - -#qunit-tests pre { - margin: 0; - white-space: pre-wrap; - word-wrap: break-word; -} - -#qunit-tests del { - background-color: #e0f2be; - color: #374e0c; - text-decoration: none; -} - -#qunit-tests ins { - background-color: #ffcaca; - color: #500; - text-decoration: none; -} - -/*** Test Counts */ - -#qunit-tests b.counts { color: black; } -#qunit-tests b.passed { color: #5E740B; } -#qunit-tests b.failed { color: #710909; } - -#qunit-tests li li { - margin: 0.5em; - padding: 0.4em 0.5em 0.4em 0.5em; - background-color: #fff; - border-bottom: none; - list-style-position: inside; -} - -/*** Passing Styles */ - -#qunit-tests li li.pass { - color: #5E740B; - background-color: #fff; - border-left: 26px solid #C6E746; -} - -#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } -#qunit-tests .pass .test-name { color: #366097; } - -#qunit-tests .pass .test-actual, -#qunit-tests .pass .test-expected { color: #999999; } - -#qunit-banner.qunit-pass { background-color: #C6E746; } - -/*** Failing Styles */ - -#qunit-tests li li.fail { - color: #710909; - background-color: #fff; - border-left: 26px solid #EE5757; - white-space: pre; -} - -#qunit-tests > li:last-child { - border-radius: 0 0 15px 15px; - -moz-border-radius: 0 0 15px 15px; - -webkit-border-bottom-right-radius: 15px; - -webkit-border-bottom-left-radius: 15px; -} - -#qunit-tests .fail { color: #000000; background-color: #EE5757; } -#qunit-tests .fail .test-name, -#qunit-tests .fail .module-name { color: #000000; } - -#qunit-tests .fail .test-actual { color: #EE5757; } -#qunit-tests .fail .test-expected { color: green; } - -#qunit-banner.qunit-fail { background-color: #EE5757; } - - -/** Result */ - -#qunit-testresult { - padding: 0.5em 0.5em 0.5em 2.5em; - - color: #2b81af; - background-color: #D2E0E6; - - border-bottom: 1px solid white; -} -#qunit-testresult .module-name { - font-weight: bold; -} - -/** Fixture */ - -#qunit-fixture { - position: absolute; - top: -10000px; - left: -10000px; - width: 1000px; - height: 1000px; -} diff --git a/test/vendor/qunit.js b/test/vendor/qunit.js deleted file mode 100644 index c1570c2..0000000 --- a/test/vendor/qunit.js +++ /dev/null @@ -1,1863 +0,0 @@ -/** - * QUnit v1.8.0 - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2012 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. - */ - -(function( window ) { - -var QUnit, - config, - onErrorFnPrev, - testId = 0, - fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""), - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty, - defined = { - setTimeout: typeof window.setTimeout !== "undefined", - sessionStorage: (function() { - var x = "qunit-test-string"; - try { - sessionStorage.setItem( x, x ); - sessionStorage.removeItem( x ); - return true; - } catch( e ) { - return false; - } - }()) -}; - -function Test( settings ) { - extend( this, settings ); - this.assertions = []; - this.testNumber = ++Test.count; -} - -Test.count = 0; - -Test.prototype = { - init: function() { - var a, b, li, - tests = id( "qunit-tests" ); - - if ( tests ) { - b = document.createElement( "strong" ); - b.innerHTML = this.name; - - // `a` initialized at top of scope - a = document.createElement( "a" ); - a.innerHTML = "Rerun"; - a.href = QUnit.url({ testNumber: this.testNumber }); - - li = document.createElement( "li" ); - li.appendChild( b ); - li.appendChild( a ); - li.className = "running"; - li.id = this.id = "qunit-test-output" + testId++; - - tests.appendChild( li ); - } - }, - setup: function() { - if ( this.module !== config.previousModule ) { - if ( config.previousModule ) { - runLoggingCallbacks( "moduleDone", QUnit, { - name: config.previousModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - }); - } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0 }; - runLoggingCallbacks( "moduleStart", QUnit, { - name: this.module - }); - } else if ( config.autorun ) { - runLoggingCallbacks( "moduleStart", QUnit, { - name: this.module - }); - } - - config.current = this; - - this.testEnvironment = extend({ - setup: function() {}, - teardown: function() {} - }, this.moduleTestEnvironment ); - - runLoggingCallbacks( "testStart", QUnit, { - name: this.testName, - module: this.module - }); - - // allow utility functions to access the current test environment - // TODO why?? - QUnit.current_testEnvironment = this.testEnvironment; - - if ( !config.pollution ) { - saveGlobal(); - } - if ( config.notrycatch ) { - this.testEnvironment.setup.call( this.testEnvironment ); - return; - } - try { - this.testEnvironment.setup.call( this.testEnvironment ); - } catch( e ) { - QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) ); - } - }, - run: function() { - config.current = this; - - var running = id( "qunit-testresult" ); - - if ( running ) { - running.innerHTML = "Running:
      " + this.name; - } - - if ( this.async ) { - QUnit.stop(); - } - - if ( config.notrycatch ) { - this.callback.call( this.testEnvironment, QUnit.assert ); - return; - } - - try { - this.callback.call( this.testEnvironment, QUnit.assert ); - } catch( e ) { - QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) ); - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - QUnit.start(); - } - } - }, - teardown: function() { - config.current = this; - if ( config.notrycatch ) { - this.testEnvironment.teardown.call( this.testEnvironment ); - return; - } else { - try { - this.testEnvironment.teardown.call( this.testEnvironment ); - } catch( e ) { - QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) ); - } - } - checkPollution(); - }, - finish: function() { - config.current = this; - if ( config.requireExpects && this.expected == null ) { - QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack ); - } else if ( this.expected != null && this.expected != this.assertions.length ) { - QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); - } else if ( this.expected == null && !this.assertions.length ) { - QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack ); - } - - var assertion, a, b, i, li, ol, - test = this, - good = 0, - bad = 0, - tests = id( "qunit-tests" ); - - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - if ( tests ) { - ol = document.createElement( "ol" ); - - for ( i = 0; i < this.assertions.length; i++ ) { - assertion = this.assertions[i]; - - li = document.createElement( "li" ); - li.className = assertion.result ? "pass" : "fail"; - li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" ); - ol.appendChild( li ); - - if ( assertion.result ) { - good++; - } else { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - // store result when possible - if ( QUnit.config.reorder && defined.sessionStorage ) { - if ( bad ) { - sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad ); - } else { - sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName ); - } - } - - if ( bad === 0 ) { - ol.style.display = "none"; - } - - // `b` initialized at top of scope - b = document.createElement( "strong" ); - b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; - - addEvent(b, "click", function() { - var next = b.nextSibling.nextSibling, - display = next.style.display; - next.style.display = display === "none" ? "block" : "none"; - }); - - addEvent(b, "dblclick", function( e ) { - var target = e && e.target ? e.target : window.event.srcElement; - if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { - target = target.parentNode; - } - if ( window.location && target.nodeName.toLowerCase() === "strong" ) { - window.location = QUnit.url({ testNumber: test.testNumber }); - } - }); - - // `li` initialized at top of scope - li = id( this.id ); - li.className = bad ? "fail" : "pass"; - li.removeChild( li.firstChild ); - a = li.firstChild; - li.appendChild( b ); - li.appendChild ( a ); - li.appendChild( ol ); - - } else { - for ( i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[i].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - } - - runLoggingCallbacks( "testDone", QUnit, { - name: this.testName, - module: this.module, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length - }); - - QUnit.reset(); - - config.current = undefined; - }, - - queue: function() { - var bad, - test = this; - - synchronize(function() { - test.init(); - }); - function run() { - // each of these can by async - synchronize(function() { - test.setup(); - }); - synchronize(function() { - test.run(); - }); - synchronize(function() { - test.teardown(); - }); - synchronize(function() { - test.finish(); - }); - } - - // `bad` initialized at top of scope - // defer when previous test run passed, if storage is available - bad = QUnit.config.reorder && defined.sessionStorage && - +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName ); - - if ( bad ) { - run(); - } else { - synchronize( run, true ); - } - } -}; - -// Root QUnit object. -// `QUnit` initialized at top of scope -QUnit = { - - // call on start of module test to prepend name to all tests - module: function( name, testEnvironment ) { - config.currentModule = name; - config.currentModuleTestEnviroment = testEnvironment; - }, - - asyncTest: function( testName, expected, callback ) { - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - QUnit.test( testName, expected, callback, true ); - }, - - test: function( testName, expected, callback, async ) { - var test, - name = "" + escapeInnerText( testName ) + ""; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - if ( config.currentModule ) { - name = "" + config.currentModule + ": " + name; - } - - test = new Test({ - name: name, - testName: testName, - expected: expected, - async: async, - callback: callback, - module: config.currentModule, - moduleTestEnvironment: config.currentModuleTestEnviroment, - stack: sourceFromStacktrace( 2 ) - }); - - if ( !validTest( test ) ) { - return; - } - - test.queue(); - }, - - // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. - expect: function( asserts ) { - config.current.expected = asserts; - }, - - start: function( count ) { - config.semaphore -= count || 1; - // don't start until equal number of stop-calls - if ( config.semaphore > 0 ) { - return; - } - // ignore if start is called more often then stop - if ( config.semaphore < 0 ) { - config.semaphore = 0; - } - // A slight delay, to avoid any current callbacks - if ( defined.setTimeout ) { - window.setTimeout(function() { - if ( config.semaphore > 0 ) { - return; - } - if ( config.timeout ) { - clearTimeout( config.timeout ); - } - - config.blocking = false; - process( true ); - }, 13); - } else { - config.blocking = false; - process( true ); - } - }, - - stop: function( count ) { - config.semaphore += count || 1; - config.blocking = true; - - if ( config.testTimeout && defined.setTimeout ) { - clearTimeout( config.timeout ); - config.timeout = window.setTimeout(function() { - QUnit.ok( false, "Test timed out" ); - config.semaphore = 1; - QUnit.start(); - }, config.testTimeout ); - } - } -}; - -// Asssert helpers -// All of these must call either QUnit.push() or manually do: -// - runLoggingCallbacks( "log", .. ); -// - config.current.assertions.push({ .. }); -QUnit.assert = { - /** - * Asserts rough true-ish result. - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - */ - ok: function( result, msg ) { - if ( !config.current ) { - throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) ); - } - result = !!result; - - var source, - details = { - result: result, - message: msg - }; - - msg = escapeInnerText( msg || (result ? "okay" : "failed" ) ); - msg = "" + msg + ""; - - if ( !result ) { - source = sourceFromStacktrace( 2 ); - if ( source ) { - details.source = source; - msg += "
      Source:
      " + escapeInnerText( source ) + "
      "; - } - } - runLoggingCallbacks( "log", QUnit, details ); - config.current.assertions.push({ - result: result, - message: msg - }); - }, - - /** - * Assert that the first two arguments are equal, with an optional message. - * Prints out both actual and expected values. - * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); - */ - equal: function( actual, expected, message ) { - QUnit.push( expected == actual, actual, expected, message ); - }, - - notEqual: function( actual, expected, message ) { - QUnit.push( expected != actual, actual, expected, message ); - }, - - deepEqual: function( actual, expected, message ) { - QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); - }, - - notDeepEqual: function( actual, expected, message ) { - QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); - }, - - strictEqual: function( actual, expected, message ) { - QUnit.push( expected === actual, actual, expected, message ); - }, - - notStrictEqual: function( actual, expected, message ) { - QUnit.push( expected !== actual, actual, expected, message ); - }, - - raises: function( block, expected, message ) { - var actual, - ok = false; - - if ( typeof expected === "string" ) { - message = expected; - expected = null; - } - - config.current.ignoreGlobalErrors = true; - try { - block.call( config.current.testEnvironment ); - } catch (e) { - actual = e; - } - config.current.ignoreGlobalErrors = false; - - if ( actual ) { - // we don't want to validate thrown error - if ( !expected ) { - ok = true; - // expected is a regexp - } else if ( QUnit.objectType( expected ) === "regexp" ) { - ok = expected.test( actual ); - // expected is a constructor - } else if ( actual instanceof expected ) { - ok = true; - // expected is a validation function which returns true is validation passed - } else if ( expected.call( {}, actual ) === true ) { - ok = true; - } - } - - QUnit.push( ok, actual, null, message ); - } -}; - -// @deprecated: Kept assertion helpers in root for backwards compatibility -extend( QUnit, QUnit.assert ); - -/** - * @deprecated: Kept for backwards compatibility - * next step: remove entirely - */ -QUnit.equals = function() { - QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" ); -}; -QUnit.same = function() { - QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" ); -}; - -// We want access to the constructor's prototype -(function() { - function F() {} - F.prototype = QUnit; - QUnit = new F(); - // Make F QUnit's constructor so that we can add to the prototype later - QUnit.constructor = F; -}()); - -/** - * Config object: Maintain internal state - * Later exposed as QUnit.config - * `config` initialized at top of scope - */ -config = { - // The queue of tests to run - queue: [], - - // block until document ready - blocking: true, - - // when enabled, show only failing tests - // gets persisted through sessionStorage and can be changed in UI via checkbox - hidepassed: false, - - // by default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, - - // by default, modify document.title when suite is done - altertitle: true, - - // when enabled, all tests must call expect() - requireExpects: false, - - urlConfig: [ "noglobals", "notrycatch" ], - - // logging callback queues - begin: [], - done: [], - log: [], - testStart: [], - testDone: [], - moduleStart: [], - moduleDone: [] -}; - -// Initialize more QUnit.config and QUnit.urlParams -(function() { - var i, - location = window.location || { search: "", protocol: "file:" }, - params = location.search.slice( 1 ).split( "&" ), - length = params.length, - urlParams = {}, - current; - - if ( params[ 0 ] ) { - for ( i = 0; i < length; i++ ) { - current = params[ i ].split( "=" ); - current[ 0 ] = decodeURIComponent( current[ 0 ] ); - // allow just a key to turn on a flag, e.g., test.html?noglobals - current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - urlParams[ current[ 0 ] ] = current[ 1 ]; - } - } - - QUnit.urlParams = urlParams; - - // String search anywhere in moduleName+testName - config.filter = urlParams.filter; - - // Exact match of the module name - config.module = urlParams.module; - - config.testNumber = parseInt( urlParams.testNumber, 10 ) || null; - - // Figure out if we're running the tests from a server or not - QUnit.isLocal = location.protocol === "file:"; -}()); - -// Export global variables, unless an 'exports' object exists, -// in that case we assume we're in CommonJS (dealt with on the bottom of the script) -if ( typeof exports === "undefined" ) { - extend( window, QUnit ); - - // Expose QUnit object - window.QUnit = QUnit; -} - -// Extend QUnit object, -// these after set here because they should not be exposed as global functions -extend( QUnit, { - config: config, - - // Initialize the configuration options - init: function() { - extend( config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: +new Date(), - updateRate: 1000, - blocking: false, - autostart: true, - autorun: false, - filter: "", - queue: [], - semaphore: 0 - }); - - var tests, banner, result, - qunit = id( "qunit" ); - - if ( qunit ) { - qunit.innerHTML = - "

      " + escapeInnerText( document.title ) + "

      " + - "

      " + - "
      " + - "

      " + - "
        "; - } - - tests = id( "qunit-tests" ); - banner = id( "qunit-banner" ); - result = id( "qunit-testresult" ); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = "Running...
         "; - } - }, - - // Resets the test setup. Useful for tests that modify the DOM. - // If jQuery is available, uses jQuery's html(), otherwise just innerHTML. - reset: function() { - var fixture; - - if ( window.jQuery ) { - jQuery( "#qunit-fixture" ).html( config.fixture ); - } else { - fixture = id( "qunit-fixture" ); - if ( fixture ) { - fixture.innerHTML = config.fixture; - } - } - }, - - // Trigger an event on an element. - // @example triggerEvent( document.body, "click" ); - triggerEvent: function( elem, type, event ) { - if ( document.createEvent ) { - event = document.createEvent( "MouseEvents" ); - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - - elem.dispatchEvent( event ); - } else if ( elem.fireEvent ) { - elem.fireEvent( "on" + type ); - } - }, - - // Safe object type checking - is: function( type, obj ) { - return QUnit.objectType( obj ) == type; - }, - - objectType: function( obj ) { - if ( typeof obj === "undefined" ) { - return "undefined"; - // consider: typeof null === object - } - if ( obj === null ) { - return "null"; - } - - var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || ""; - - switch ( type ) { - case "Number": - if ( isNaN(obj) ) { - return "nan"; - } - return "number"; - case "String": - case "Boolean": - case "Array": - case "Date": - case "RegExp": - case "Function": - return type.toLowerCase(); - } - if ( typeof obj === "object" ) { - return "object"; - } - return undefined; - }, - - push: function( result, actual, expected, message ) { - if ( !config.current ) { - throw new Error( "assertion outside test context, was " + sourceFromStacktrace() ); - } - - var output, source, - details = { - result: result, - message: message, - actual: actual, - expected: expected - }; - - message = escapeInnerText( message ) || ( result ? "okay" : "failed" ); - message = "" + message + ""; - output = message; - - if ( !result ) { - expected = escapeInnerText( QUnit.jsDump.parse(expected) ); - actual = escapeInnerText( QUnit.jsDump.parse(actual) ); - output += ""; - - if ( actual != expected ) { - output += ""; - output += ""; - } - - source = sourceFromStacktrace(); - - if ( source ) { - details.source = source; - output += ""; - } - - output += "
        Expected:
        " + expected + "
        Result:
        " + actual + "
        Diff:
        " + QUnit.diff( expected, actual ) + "
        Source:
        " + escapeInnerText( source ) + "
        "; - } - - runLoggingCallbacks( "log", QUnit, details ); - - config.current.assertions.push({ - result: !!result, - message: output - }); - }, - - pushFailure: function( message, source ) { - if ( !config.current ) { - throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) ); - } - - var output, - details = { - result: false, - message: message - }; - - message = escapeInnerText(message ) || "error"; - message = "" + message + ""; - output = message; - - if ( source ) { - details.source = source; - output += "
        Source:
        " + escapeInnerText( source ) + "
        "; - } - - runLoggingCallbacks( "log", QUnit, details ); - - config.current.assertions.push({ - result: false, - message: output - }); - }, - - url: function( params ) { - params = extend( extend( {}, QUnit.urlParams ), params ); - var key, - querystring = "?"; - - for ( key in params ) { - if ( !hasOwn.call( params, key ) ) { - continue; - } - querystring += encodeURIComponent( key ) + "=" + - encodeURIComponent( params[ key ] ) + "&"; - } - return window.location.pathname + querystring.slice( 0, -1 ); - }, - - extend: extend, - id: id, - addEvent: addEvent - // load, equiv, jsDump, diff: Attached later -}); - -/** - * @deprecated: Created for backwards compatibility with test runner that set the hook function - * into QUnit.{hook}, instead of invoking it and passing the hook function. - * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here. - * Doing this allows us to tell if the following methods have been overwritten on the actual - * QUnit object. - */ -extend( QUnit.constructor.prototype, { - - // Logging callbacks; all receive a single argument with the listed properties - // run test/logs.html for any related changes - begin: registerLoggingCallback( "begin" ), - - // done: { failed, passed, total, runtime } - done: registerLoggingCallback( "done" ), - - // log: { result, actual, expected, message } - log: registerLoggingCallback( "log" ), - - // testStart: { name } - testStart: registerLoggingCallback( "testStart" ), - - // testDone: { name, failed, passed, total } - testDone: registerLoggingCallback( "testDone" ), - - // moduleStart: { name } - moduleStart: registerLoggingCallback( "moduleStart" ), - - // moduleDone: { name, failed, passed, total } - moduleDone: registerLoggingCallback( "moduleDone" ) -}); - -if ( typeof document === "undefined" || document.readyState === "complete" ) { - config.autorun = true; -} - -QUnit.load = function() { - runLoggingCallbacks( "begin", QUnit, {} ); - - // Initialize the config, saving the execution queue - var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, - urlConfigHtml = "", - oldconfig = extend( {}, config ); - - QUnit.init(); - extend(config, oldconfig); - - config.blocking = false; - - len = config.urlConfig.length; - - for ( i = 0; i < len; i++ ) { - val = config.urlConfig[i]; - config[val] = QUnit.urlParams[val]; - urlConfigHtml += ""; - } - - // `userAgent` initialized at top of scope - userAgent = id( "qunit-userAgent" ); - if ( userAgent ) { - userAgent.innerHTML = navigator.userAgent; - } - - // `banner` initialized at top of scope - banner = id( "qunit-header" ); - if ( banner ) { - banner.innerHTML = "" + banner.innerHTML + " " + urlConfigHtml; - addEvent( banner, "change", function( event ) { - var params = {}; - params[ event.target.name ] = event.target.checked ? true : undefined; - window.location = QUnit.url( params ); - }); - } - - // `toolbar` initialized at top of scope - toolbar = id( "qunit-testrunner-toolbar" ); - if ( toolbar ) { - // `filter` initialized at top of scope - filter = document.createElement( "input" ); - filter.type = "checkbox"; - filter.id = "qunit-filter-pass"; - - addEvent( filter, "click", function() { - var tmp, - ol = document.getElementById( "qunit-tests" ); - - if ( filter.checked ) { - ol.className = ol.className + " hidepass"; - } else { - tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; - ol.className = tmp.replace( / hidepass /, " " ); - } - if ( defined.sessionStorage ) { - if (filter.checked) { - sessionStorage.setItem( "qunit-filter-passed-tests", "true" ); - } else { - sessionStorage.removeItem( "qunit-filter-passed-tests" ); - } - } - }); - - if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) { - filter.checked = true; - // `ol` initialized at top of scope - ol = document.getElementById( "qunit-tests" ); - ol.className = ol.className + " hidepass"; - } - toolbar.appendChild( filter ); - - // `label` initialized at top of scope - label = document.createElement( "label" ); - label.setAttribute( "for", "qunit-filter-pass" ); - label.innerHTML = "Hide passed tests"; - toolbar.appendChild( label ); - } - - // `main` initialized at top of scope - main = id( "qunit-fixture" ); - if ( main ) { - config.fixture = main.innerHTML; - } - - if ( config.autostart ) { - QUnit.start(); - } -}; - -addEvent( window, "load", QUnit.load ); - -// `onErrorFnPrev` initialized at top of scope -// Preserve other handlers -onErrorFnPrev = window.onerror; - -// Cover uncaught exceptions -// Returning true will surpress the default browser handler, -// returning false will let it run. -window.onerror = function ( error, filePath, linerNr ) { - var ret = false; - if ( onErrorFnPrev ) { - ret = onErrorFnPrev( error, filePath, linerNr ); - } - - // Treat return value as window.onerror itself does, - // Only do our handling if not surpressed. - if ( ret !== true ) { - if ( QUnit.config.current ) { - if ( QUnit.config.current.ignoreGlobalErrors ) { - return true; - } - QUnit.pushFailure( error, filePath + ":" + linerNr ); - } else { - QUnit.test( "global failure", function() { - QUnit.pushFailure( error, filePath + ":" + linerNr ); - }); - } - return false; - } - - return ret; -}; - -function done() { - config.autorun = true; - - // Log the last module results - if ( config.currentModule ) { - runLoggingCallbacks( "moduleDone", QUnit, { - name: config.currentModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - }); - } - - var i, key, - banner = id( "qunit-banner" ), - tests = id( "qunit-tests" ), - runtime = +new Date() - config.started, - passed = config.stats.all - config.stats.bad, - html = [ - "Tests completed in ", - runtime, - " milliseconds.
        ", - "", - passed, - " tests of ", - config.stats.all, - " passed, ", - config.stats.bad, - " failed." - ].join( "" ); - - if ( banner ) { - banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" ); - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - if ( config.altertitle && typeof document !== "undefined" && document.title ) { - // show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8-charset - document.title = [ - ( config.stats.bad ? "\u2716" : "\u2714" ), - document.title.replace( /^[\u2714\u2716] /i, "" ) - ].join( " " ); - } - - // clear own sessionStorage items if all tests passed - if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) { - // `key` & `i` initialized at top of scope - for ( i = 0; i < sessionStorage.length; i++ ) { - key = sessionStorage.key( i++ ); - if ( key.indexOf( "qunit-test-" ) === 0 ) { - sessionStorage.removeItem( key ); - } - } - } - - runLoggingCallbacks( "done", QUnit, { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - }); -} - -/** @return Boolean: true if this test should be ran */ -function validTest( test ) { - var include, - filter = config.filter && config.filter.toLowerCase(), - module = config.module, - fullName = (test.module + ": " + test.testName).toLowerCase(); - - if ( config.testNumber ) { - return test.testNumber === config.testNumber; - } - - if ( module && test.module !== module ) { - return false; - } - - if ( !filter ) { - return true; - } - - include = filter.charAt( 0 ) !== "!"; - if ( !include ) { - filter = filter.slice( 1 ); - } - - // If the filter matches, we need to honour include - if ( fullName.indexOf( filter ) !== -1 ) { - return include; - } - - // Otherwise, do the opposite - return !include; -} - -// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions) -// Later Safari and IE10 are supposed to support error.stack as well -// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack -function extractStacktrace( e, offset ) { - offset = offset === undefined ? 3 : offset; - - var stack, include, i, regex; - - if ( e.stacktrace ) { - // Opera - return e.stacktrace.split( "\n" )[ offset + 3 ]; - } else if ( e.stack ) { - // Firefox, Chrome - stack = e.stack.split( "\n" ); - if (/^error$/i.test( stack[0] ) ) { - stack.shift(); - } - if ( fileName ) { - include = []; - for ( i = offset; i < stack.length; i++ ) { - if ( stack[ i ].indexOf( fileName ) != -1 ) { - break; - } - include.push( stack[ i ] ); - } - if ( include.length ) { - return include.join( "\n" ); - } - } - return stack[ offset ]; - } else if ( e.sourceURL ) { - // Safari, PhantomJS - // hopefully one day Safari provides actual stacktraces - // exclude useless self-reference for generated Error objects - if ( /qunit.js$/.test( e.sourceURL ) ) { - return; - } - // for actual exceptions, this is useful - return e.sourceURL + ":" + e.line; - } -} -function sourceFromStacktrace( offset ) { - try { - throw new Error(); - } catch ( e ) { - return extractStacktrace( e, offset ); - } -} - -function escapeInnerText( s ) { - if ( !s ) { - return ""; - } - s = s + ""; - return s.replace( /[\&<>]/g, function( s ) { - switch( s ) { - case "&": return "&"; - case "<": return "<"; - case ">": return ">"; - default: return s; - } - }); -} - -function synchronize( callback, last ) { - config.queue.push( callback ); - - if ( config.autorun && !config.blocking ) { - process( last ); - } -} - -function process( last ) { - function next() { - process( last ); - } - var start = new Date().getTime(); - config.depth = config.depth ? config.depth + 1 : 1; - - while ( config.queue.length && !config.blocking ) { - if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { - config.queue.shift()(); - } else { - window.setTimeout( next, 13 ); - break; - } - } - config.depth--; - if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { - done(); - } -} - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in window ) { - // in Opera sometimes DOM element ids show up here, ignore them - if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) { - continue; - } - config.pollution.push( key ); - } - } -} - -function checkPollution( name ) { - var newGlobals, - deletedGlobals, - old = config.pollution; - - saveGlobal(); - - newGlobals = diff( config.pollution, old ); - if ( newGlobals.length > 0 ) { - QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") ); - } - - deletedGlobals = diff( old, config.pollution ); - if ( deletedGlobals.length > 0 ) { - QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") ); - } -} - -// returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var i, j, - result = a.slice(); - - for ( i = 0; i < result.length; i++ ) { - for ( j = 0; j < b.length; j++ ) { - if ( result[i] === b[j] ) { - result.splice( i, 1 ); - i--; - break; - } - } - } - return result; -} - -function extend( a, b ) { - for ( var prop in b ) { - if ( b[ prop ] === undefined ) { - delete a[ prop ]; - - // Avoid "Member not found" error in IE8 caused by setting window.constructor - } else if ( prop !== "constructor" || a !== window ) { - a[ prop ] = b[ prop ]; - } - } - - return a; -} - -function addEvent( elem, type, fn ) { - if ( elem.addEventListener ) { - elem.addEventListener( type, fn, false ); - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, fn ); - } else { - fn(); - } -} - -function id( name ) { - return !!( typeof document !== "undefined" && document && document.getElementById ) && - document.getElementById( name ); -} - -function registerLoggingCallback( key ) { - return function( callback ) { - config[key].push( callback ); - }; -} - -// Supports deprecated method of completely overwriting logging callbacks -function runLoggingCallbacks( key, scope, args ) { - //debugger; - var i, callbacks; - if ( QUnit.hasOwnProperty( key ) ) { - QUnit[ key ].call(scope, args ); - } else { - callbacks = config[ key ]; - for ( i = 0; i < callbacks.length; i++ ) { - callbacks[ i ].call( scope, args ); - } - } -} - -// Test for equality any JavaScript type. -// Author: Philippe Rathé -QUnit.equiv = (function() { - - // Call the o related callback with the given arguments. - function bindCallbacks( o, callbacks, args ) { - var prop = QUnit.objectType( o ); - if ( prop ) { - if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { - return callbacks[ prop ].apply( callbacks, args ); - } else { - return callbacks[ prop ]; // or undefined - } - } - } - - // the real equiv function - var innerEquiv, - // stack to decide between skip/abort functions - callers = [], - // stack to avoiding loops from circular referencing - parents = [], - - getProto = Object.getPrototypeOf || function ( obj ) { - return obj.__proto__; - }, - callbacks = (function () { - - // for string, boolean, number and null - function useStrictEquality( b, a ) { - if ( b instanceof a.constructor || a instanceof b.constructor ) { - // to catch short annotaion VS 'new' annotation of a - // declaration - // e.g. var i = 1; - // var j = new Number(1); - return a == b; - } else { - return a === b; - } - } - - return { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - - "nan": function( b ) { - return isNaN( b ); - }, - - "date": function( b, a ) { - return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); - }, - - "regexp": function( b, a ) { - return QUnit.objectType( b ) === "regexp" && - // the regex itself - a.source === b.source && - // and its modifers - a.global === b.global && - // (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function": function() { - var caller = callers[callers.length - 1]; - return caller !== Object && typeof caller !== "undefined"; - }, - - "array": function( b, a ) { - var i, j, len, loop; - - // b could be an object literal here - if ( QUnit.objectType( b ) !== "array" ) { - return false; - } - - len = a.length; - if ( len !== b.length ) { - // safe and faster - return false; - } - - // track reference to avoid circular references - parents.push( a ); - for ( i = 0; i < len; i++ ) { - loop = false; - for ( j = 0; j < parents.length; j++ ) { - if ( parents[j] === a[i] ) { - loop = true;// dont rewalk array - } - } - if ( !loop && !innerEquiv(a[i], b[i]) ) { - parents.pop(); - return false; - } - } - parents.pop(); - return true; - }, - - "object": function( b, a ) { - var i, j, loop, - // Default to true - eq = true, - aProperties = [], - bProperties = []; - - // comparing constructors is more strict than using - // instanceof - if ( a.constructor !== b.constructor ) { - // Allow objects with no prototype to be equivalent to - // objects with Object as their constructor. - if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) || - ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) { - return false; - } - } - - // stack constructor before traversing properties - callers.push( a.constructor ); - // track reference to avoid circular references - parents.push( a ); - - for ( i in a ) { // be strict: don't ensures hasOwnProperty - // and go deep - loop = false; - for ( j = 0; j < parents.length; j++ ) { - if ( parents[j] === a[i] ) { - // don't go down the same path twice - loop = true; - } - } - aProperties.push(i); // collect a's properties - - if (!loop && !innerEquiv( a[i], b[i] ) ) { - eq = false; - break; - } - } - - callers.pop(); // unstack, we are done - parents.pop(); - - for ( i in b ) { - bProperties.push( i ); // collect b's properties - } - - // Ensures identical properties name - return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); - } - }; - }()); - - innerEquiv = function() { // can take multiple arguments - var args = [].slice.apply( arguments ); - if ( args.length < 2 ) { - return true; // end transition - } - - return (function( a, b ) { - if ( a === b ) { - return true; // catch the most you can - } else if ( a === null || b === null || typeof a === "undefined" || - typeof b === "undefined" || - QUnit.objectType(a) !== QUnit.objectType(b) ) { - return false; // don't lose time with error prone cases - } else { - return bindCallbacks(a, callbacks, [ b, a ]); - } - - // apply transition with (1..n) arguments - }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) ); - }; - - return innerEquiv; -}()); - -/** - * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | - * http://flesler.blogspot.com Licensed under BSD - * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 - * - * @projectDescription Advanced and extensible data dumping for Javascript. - * @version 1.0.0 - * @author Ariel Flesler - * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} - */ -QUnit.jsDump = (function() { - function quote( str ) { - return '"' + str.toString().replace( /"/g, '\\"' ) + '"'; - } - function literal( o ) { - return o + ""; - } - function join( pre, arr, post ) { - var s = jsDump.separator(), - base = jsDump.indent(), - inner = jsDump.indent(1); - if ( arr.join ) { - arr = arr.join( "," + s + inner ); - } - if ( !arr ) { - return pre + post; - } - return [ pre, inner + arr, base + post ].join(s); - } - function array( arr, stack ) { - var i = arr.length, ret = new Array(i); - this.up(); - while ( i-- ) { - ret[i] = this.parse( arr[i] , undefined , stack); - } - this.down(); - return join( "[", ret, "]" ); - } - - var reName = /^function (\w+)/, - jsDump = { - parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance - stack = stack || [ ]; - var inStack, res, - parser = this.parsers[ type || this.typeOf(obj) ]; - - type = typeof parser; - inStack = inArray( obj, stack ); - - if ( inStack != -1 ) { - return "recursion(" + (inStack - stack.length) + ")"; - } - //else - if ( type == "function" ) { - stack.push( obj ); - res = parser.call( this, obj, stack ); - stack.pop(); - return res; - } - // else - return ( type == "string" ) ? parser : this.parsers.error; - }, - typeOf: function( obj ) { - var type; - if ( obj === null ) { - type = "null"; - } else if ( typeof obj === "undefined" ) { - type = "undefined"; - } else if ( QUnit.is( "regexp", obj) ) { - type = "regexp"; - } else if ( QUnit.is( "date", obj) ) { - type = "date"; - } else if ( QUnit.is( "function", obj) ) { - type = "function"; - } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) { - type = "window"; - } else if ( obj.nodeType === 9 ) { - type = "document"; - } else if ( obj.nodeType ) { - type = "node"; - } else if ( - // native arrays - toString.call( obj ) === "[object Array]" || - // NodeList objects - ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) - ) { - type = "array"; - } else { - type = typeof obj; - } - return type; - }, - separator: function() { - return this.multiline ? this.HTML ? "
        " : "\n" : this.HTML ? " " : " "; - }, - indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing - if ( !this.multiline ) { - return ""; - } - var chr = this.indentChar; - if ( this.HTML ) { - chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); - } - return new Array( this._depth_ + (extra||0) ).join(chr); - }, - up: function( a ) { - this._depth_ += a || 1; - }, - down: function( a ) { - this._depth_ -= a || 1; - }, - setParser: function( name, parser ) { - this.parsers[name] = parser; - }, - // The next 3 are exposed so you can use them - quote: quote, - literal: literal, - join: join, - // - _depth_: 1, - // This is the list of parsers, to modify them, use jsDump.setParser - parsers: { - window: "[Window]", - document: "[Document]", - error: "[ERROR]", //when no parser is found, shouldn"t happen - unknown: "[Unknown]", - "null": "null", - "undefined": "undefined", - "function": function( fn ) { - var ret = "function", - name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE - - if ( name ) { - ret += " " + name; - } - ret += "( "; - - ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" ); - return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" ); - }, - array: array, - nodelist: array, - "arguments": array, - object: function( map, stack ) { - var ret = [ ], keys, key, val, i; - QUnit.jsDump.up(); - if ( Object.keys ) { - keys = Object.keys( map ); - } else { - keys = []; - for ( key in map ) { - keys.push( key ); - } - } - keys.sort(); - for ( i = 0; i < keys.length; i++ ) { - key = keys[ i ]; - val = map[ key ]; - ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) ); - } - QUnit.jsDump.down(); - return join( "{", ret, "}" ); - }, - node: function( node ) { - var a, val, - open = QUnit.jsDump.HTML ? "<" : "<", - close = QUnit.jsDump.HTML ? ">" : ">", - tag = node.nodeName.toLowerCase(), - ret = open + tag; - - for ( a in QUnit.jsDump.DOMAttrs ) { - val = node[ QUnit.jsDump.DOMAttrs[a] ]; - if ( val ) { - ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" ); - } - } - return ret + close + open + "/" + tag + close; - }, - functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function - var args, - l = fn.length; - - if ( !l ) { - return ""; - } - - args = new Array(l); - while ( l-- ) { - args[l] = String.fromCharCode(97+l);//97 is 'a' - } - return " " + args.join( ", " ) + " "; - }, - key: quote, //object calls it internally, the key part of an item in a map - functionCode: "[code]", //function calls it internally, it's the content of the function - attribute: quote, //node calls it internally, it's an html attribute value - string: quote, - date: quote, - regexp: literal, //regex - number: literal, - "boolean": literal - }, - DOMAttrs: { - //attributes to dump from nodes, name=>realName - id: "id", - name: "name", - "class": "className" - }, - HTML: false,//if true, entities are escaped ( <, >, \t, space and \n ) - indentChar: " ",//indentation unit - multiline: true //if true, items in a collection, are separated by a \n, else just a space. - }; - - return jsDump; -}()); - -// from Sizzle.js -function getText( elems ) { - var i, elem, - ret = ""; - - for ( 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; -} - -// from jquery.js -function inArray( 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; -} - -/* - * Javascript Diff Algorithm - * By John Resig (http://ejohn.org/) - * Modified by Chu Alan "sprite" - * - * Released under the MIT license. - * - * More Info: - * http://ejohn.org/projects/javascript-diff-algorithm/ - * - * Usage: QUnit.diff(expected, actual) - * - * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over" - */ -QUnit.diff = (function() { - function diff( o, n ) { - var i, - ns = {}, - os = {}; - - for ( i = 0; i < n.length; i++ ) { - if ( ns[ n[i] ] == null ) { - ns[ n[i] ] = { - rows: [], - o: null - }; - } - ns[ n[i] ].rows.push( i ); - } - - for ( i = 0; i < o.length; i++ ) { - if ( os[ o[i] ] == null ) { - os[ o[i] ] = { - rows: [], - n: null - }; - } - os[ o[i] ].rows.push( i ); - } - - for ( i in ns ) { - if ( !hasOwn.call( ns, i ) ) { - continue; - } - if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) { - n[ ns[i].rows[0] ] = { - text: n[ ns[i].rows[0] ], - row: os[i].rows[0] - }; - o[ os[i].rows[0] ] = { - text: o[ os[i].rows[0] ], - row: ns[i].rows[0] - }; - } - } - - for ( i = 0; i < n.length - 1; i++ ) { - if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && - n[ i + 1 ] == o[ n[i].row + 1 ] ) { - - n[ i + 1 ] = { - text: n[ i + 1 ], - row: n[i].row + 1 - }; - o[ n[i].row + 1 ] = { - text: o[ n[i].row + 1 ], - row: i + 1 - }; - } - } - - for ( i = n.length - 1; i > 0; i-- ) { - if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && - n[ i - 1 ] == o[ n[i].row - 1 ]) { - - n[ i - 1 ] = { - text: n[ i - 1 ], - row: n[i].row - 1 - }; - o[ n[i].row - 1 ] = { - text: o[ n[i].row - 1 ], - row: i - 1 - }; - } - } - - return { - o: o, - n: n - }; - } - - return function( o, n ) { - o = o.replace( /\s+$/, "" ); - n = n.replace( /\s+$/, "" ); - - var i, pre, - str = "", - out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ), - oSpace = o.match(/\s+/g), - nSpace = n.match(/\s+/g); - - if ( oSpace == null ) { - oSpace = [ " " ]; - } - else { - oSpace.push( " " ); - } - - if ( nSpace == null ) { - nSpace = [ " " ]; - } - else { - nSpace.push( " " ); - } - - if ( out.n.length === 0 ) { - for ( i = 0; i < out.o.length; i++ ) { - str += "" + out.o[i] + oSpace[i] + ""; - } - } - else { - if ( out.n[0].text == null ) { - for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) { - str += "" + out.o[n] + oSpace[n] + ""; - } - } - - for ( i = 0; i < out.n.length; i++ ) { - if (out.n[i].text == null) { - str += "" + out.n[i] + nSpace[i] + ""; - } - else { - // `pre` initialized at top of scope - pre = ""; - - for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { - pre += "" + out.o[n] + oSpace[n] + ""; - } - str += " " + out.n[i].text + nSpace[i] + pre; - } - } - } - - return str; - }; -}()); - -// for CommonJS enviroments, export everything -if ( typeof exports !== "undefined" ) { - extend(exports, QUnit); -} - -// get at whatever the global object is, like window in browsers -}( (function() {return this;}.call()) ));