diff --git a/README.md b/README.md index 1bae268..1dae13c 100644 --- a/README.md +++ b/README.md @@ -25,21 +25,29 @@ Browser support: IE8+, Latest versions of Firefox, Chrome, Safari, Opera **functions:** * each: For applying changes to every item matched by a selector - Use: + $_(selector).dom.each(callback); - Example : $_(".foo").dom.each(function(e){ + Example : + + $_(".foo").dom.each(function(e){ $_(e).dom.text(value); }): * ext: For extending the library, adds this.el to the object or function supplied - Use: $_.ext("name", functionOrObject); + + + $_.ext("name", functionOrObject); - Example: $_.ext("zip", function(){ //function }); + + Example: + + $_.ext("zip", function(){ //function }); Adds 'zip' function to $_. * type: For getting the type of a variable - Use: $_.type(var); + + $_.type(var); ### Ajax: simple, jQuery-like ajax functions ### @@ -47,11 +55,11 @@ Browser support: IE8+, Latest versions of Firefox, Chrome, Safari, Opera functions: * Get: - Use: + $_.get(url, data_object, callback); * Post: - Use: + $_.post(url, data_object, callback); ### QS: querystring parsing and serialization for hashbang strings, and pushState urls ### @@ -59,15 +67,15 @@ functions: **functions:** * Parse: - Use: + $_.qs.parse(hb); * Set: This function will set the hash url if browser doesn't have history.pushState - Use: + $_.qs.set(key, value); * Get: Retrieves the value of the key in the url string - Use: + $_.qs.get(key); ### Store: localstorage wrapper with automatic data serialization ### @@ -75,15 +83,19 @@ functions: **functions:** * Get: - Use: + $_.store.get(key); * Set - Use: + $_.store.set(key, value); + +* Remove + + $_.store.remove(key); * getALL: Retreives all localstorage data in raw form - Use: + $_.store.getAll(); @@ -92,11 +104,11 @@ functions: **functions:** * Add: - Use: + $_(selector).event.add(event, callback); * Remove - Use: + $_(selector).event.remove(event, callback); ### DOM: Dom manipulation module ### @@ -104,34 +116,34 @@ functions: **functions:** * addClass: - Use: + $_(selector).dom.addClass(className); * RemoveClass: - Use: + $_(selector).dom.removeClass(className); * show: For setting dom elements as visible. Type defaults as "block", can be set with optional second parameter. - Use: + $_(selector).dom.show([type]); * hide: Hides the elements matching the selector - Use: + $_(selector).dom.hide(); * attr: Gets, sets, or removes an attribute from a selector. - Use: + Set: $_(selector).dom.attr(attributeName, attributeValue); Get: $_(selector).dom.attr(attributeName); Remove: $_(selector).dom.attr(attributeName, null); * text: Gets or sets the text in between an element's tags - Use: + Set: $_(selector).dom.text(text); Get: $_(selector).dom.text(); * css: Sets css styles on the selected element(s) - Use: + Set: $_(selector).dom.css(property, value); Get: $_(selector).dom.css(property); diff --git a/kis-custom.js b/kis-custom.js new file mode 100644 index 0000000..eb19921 --- /dev/null +++ b/kis-custom.js @@ -0,0 +1,1036 @@ +/** + Kis JS Keep It Simple JS Library + Copyright Timothy J. Warren + License Public Domain + Version 0.3.0 + */ +(function (){ + + "use strict"; + + //Browser requirements check + if (!document.querySelectorAll) + { + return; + } + + var $_, $, dcopy, sel; + + /** + * $ + * + * Simple DOM selector function + */ + $ = function (a) + { + var x; + if (typeof a !== "string" || typeof a === "undefined"){ return a;} + + //Pick the quickest method for each kind of selector + if(a.match(/^#([\w\-]+$)/)) + { + return document.getElementById(a.split('#')[1]); + } + else if(a.match(/^([\w\-]+)$/)) + { + x = document.getElementsByTagName(a); + } + else + { + x = document.querySelectorAll(a); + } + + //Return the single object if applicable + return (x.length === 1) ? x[0] : x; + }; + + /** + * $_ + * + * Constructor function + */ + $_ = function(s) + { + //Have documentElement be default selector, just in case + if(typeof s === "undefined") + { + sel = (typeof $_.el !== "undefined") + ? $_.el + : document.documentElement; + } + else + { + sel = (typeof s !== "object") ? $(s) : s; + } + + // Make a copy before adding properties + var self = dcopy($_); + + // Give sel to each extension. + for(var i in self) + { + if(typeof self[i] === "object") + { + self[i].el = sel; + } + } + + self.el = sel; + + return self; + }; + + /** + * Deep copy/prototypical constructor function + */ + dcopy = function(obj) + { + var type, F; + + if(typeof obj === "undefined") + { + return; + } + + if(typeof Object.create !== "undefined") + { + return Object.create(obj); + } + + type = typeof obj; + + if(type !== "object" && type !== "function") + { + return; + } + + F = function(){}; + + F.prototype = obj; + + return new F(); + + }; + + //Function to add to $_ object, and get sel + $_.ext = function(name, obj) + { + obj.el = sel; + $_[name] = obj; + }; + + //Selector iteration + $_.ext('each', function (callback) + { + if(typeof sel.length !== "undefined" && sel !== window) + { + var len = sel.length; + + if (len === 0) + { + return; + } + + var selx; + for (var x = 0; x < len; x++) + { + selx = (sel.item(x)) ? sel.item(x) : sel[x]; + callback(selx); + } + } + else + { + callback(sel); + } + }); + + //Type retriever + $_.type = function(obj) + { + if((function() {return obj && (obj !== this)}).call(obj)) + { + //fallback on 'typeof' for truthy primitive values + return (typeof obj).toLowerCase(); + } + + return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase(); + } + + //Set global variables + $_ = window.$_ = window.$_ || $_; + $_.$ = $; + + //console.log polyfill + if(typeof window.console === "undefined") + { + window.console = { + log:function(){} + }; + } + + /** + * String trim function polyfill + */ + if(typeof String.prototype.trim === "undefined") + { + String.prototype.trim = function(){ + return this.replace(/^\s+|\s+$/g, ""); + }; + } + +}()); + +// -------------------------------------------------------------------------- + +//Function to maintain module scope +(function(){ + + "use strict"; + + //Fix $_ is not defined errors + var $_ = $_ || window.$_; + + // -------------------------------------------------------------------------- + + /* + * classList.js: Cross-browser full element.classList implementation. + * 2011-06-15 + * + * By Eli Grey, http://eligrey.com + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ + + if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) + { + (function (view){ + + var classListProp = "classList", + protoProp = "prototype", + elemCtrProto = (view.HTMLElement || view.Element)[protoProp], + objCtr = Object, + strTrim = String[protoProp].trim || + function () + { + return this.replace(/^\s+|\s+$/g, ""); + }, + arrIndexOf = Array[protoProp].indexOf || + function (item) + { + var + i = 0, + len = this.length; + for (; i < len; i++) + { + if (i in this && this[i] === item) + { + return i; + } + } + return -1; + } + // Vendors: please allow content code to instantiate DOMExceptions + , + DOMEx = function (type, message) + { + this.name = type; + this.code = DOMException[type]; + this.message = message; + }, + checkTokenAndGetIndex = function (classList, token) + { + if (token === "") + { + throw new DOMEx("SYNTAX_ERR", "An invalid or illegal string was specified"); + } + if (/\s/.test(token)) + { + throw new DOMEx("INVALID_CHARACTER_ERR", "String contains an invalid character"); + } + return arrIndexOf.call(classList, token); + }, + ClassList = function (elem) + { + var + trimmedClasses = strTrim.call(elem.className), + classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [], + i = 0, + len = classes.length; + for (; i < len; i++) + { + this.push(classes[i]); + } + this._updateClassName = function () + { + elem.className = this.toString(); + }; + }, + classListProto = ClassList[protoProp] = [], + classListGetter = function () + { + return new ClassList(this); + }; + // Most DOMException implementations don't allow calling DOMException's toString() + // on non-DOMExceptions. Error's toString() is sufficient here. + DOMEx[protoProp] = Error[protoProp]; + classListProto.item = function (i) + { + return this[i] || null; + }; + classListProto.contains = function (token) + { + token += ""; + return checkTokenAndGetIndex(this, token) !== -1; + }; + classListProto.add = function (token) + { + token += ""; + if (checkTokenAndGetIndex(this, token) === -1) + { + this.push(token); + this._updateClassName(); + } + }; + classListProto.remove = function (token) + { + token += ""; + var index = checkTokenAndGetIndex(this, token); + if (index !== -1) + { + this.splice(index, 1); + this._updateClassName(); + } + }; + classListProto.toggle = function (token) + { + token += ""; + if (checkTokenAndGetIndex(this, token) === -1) + { + this.add(token); + } + else + { + this.remove(token); + } + }; + classListProto.toString = function () + { + return this.join(" "); + }; + + if (objCtr.defineProperty) + { + var classListPropDesc = { + get: classListGetter, + enumerable: true, + configurable: true + }; + try + { + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + catch (ex) + { // IE 8 doesn't support enumerable:true + if (ex.number === -0x7FF5EC54) + { + classListPropDesc.enumerable = false; + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + } + } + else if (objCtr[protoProp].__defineGetter__) + { + elemCtrProto.__defineGetter__(classListProp, classListGetter); + } + + }(self)); + } + + // -------------------------------------------------------------------------- + + /** + * Dom manipulation object + * + */ + (function (){ + var d, tag_reg, class_reg; + + tag_reg = /^([\w\-]+)$/; + class_reg = /\.([\w\-]+)$/; + + + //Private function for getting/setting attributes + function _attr(sel, name, value) + { + var oldVal, doAttr; + + //Get the value of the attribute, if it exists + if (typeof sel.hasAttribute !== "undefined") + { + if (sel.hasAttribute(name)) + { + oldVal = sel.getAttribute(name); + } + + doAttr = true; + } + else if (typeof sel[name] !== "undefined") + { + oldVal = sel[name]; + doAttr = false; + } + else if (name === "class" && typeof sel.className !== "undefined") //className attribute + { + name = "className"; + oldVal = sel.className; + doAttr = false; + } + + //Well, I guess that attribute doesn't exist + if (typeof oldVal === "undefined" && (typeof value === "undefined" || value === null)) + { + console.log(value); + console.log(sel); + console.log("Element does not have the selected attribute"); + return; + } + + //No value to set? Return the current value + if (typeof value === "undefined") + { + return oldVal; + } + + //Determine what to do with the attribute + if (typeof value !== "undefined" && value !== null) + { + if(doAttr === true) + { + sel.setAttribute(name, value); + } + else + { + sel[name] = value; + } + } + else if (value === null) + { + if(doAttr === true) + { + sel.removeAttribute(name); + } + else + { + delete sel[name]; + } + } + + return (typeof value !== "undefined") ? value : oldVal; + } + + function _toCamel(s) + { + return s.replace(/(\-[a-z])/g, function($1){ + return $1.toUpperCase().replace('-',''); + }); + } + + function _css(sel, prop, val) + { + var equi; + + //Camel-case + prop = _toCamel(prop); + + //Equivalent properties for 'special' browsers + equi = { + outerHeight: "offsetHeight", + outerWidth: "offsetWidth", + top: "posTop" + }; + + + //If you don't define a value, try returning the existing value + if(typeof val === "undefined" && sel.style[prop] !== "undefined") + { + return sel.style[prop]; + } + else if(typeof val === "undefined" && sel.style[equi[prop]] !== "undefined") + { + return sel.style[equi[prop]]; + } + + //Let's try the easy way first + if(typeof sel.style[prop] !== "undefined") + { + sel.style[prop] = val; + + //Short circuit + return; + } + else if(sel.style[equi[prop]]) + { + sel.style[equi[prop]] = val; + return; + } + + //No matches? Well, lets log it for now + console.log("Property " + prop + " nor an equivalent seems to exist"); + } + + function _sel_filter(filter, curr_sel) + { + var i, + len = curr_sel.length, + matches = []; + + //Filter by tag + if(filter.match(tag_reg)) + { + for(i=0;i 1 && typeof value === "undefined") + { + console.log(sel); + console.log("Must be a singular element"); + return; + } + else if (sel.length > 1 && typeof value !== "undefined") //You can set a bunch, though + { + $_.each(function (e){ + return _attr(e, name, value); + }); + } + else //Normal behavior + { + return _attr(sel, name, value); + } + }, + text: function (value) + { + var oldValue, set, type, sel; + + sel = this.el; + + set = (typeof value !== "undefined") ? true : false; + + type = (typeof sel.innerText !== "undefined") + ? "innerText" + : (typeof sel.textContent !== "undefined") + ? "textContent" + : "innerHTML"; + + oldValue = sel[type]; + + if(set) + { + sel[type] = value; + return value; + } + else + { + return oldValue; + } + }, + css: function (prop, val) + { + //Return the current value if a value is not set + if(typeof val === "undefined") + { + return _css(this.el, prop); + } + + $_.each(function (e){ + _css(e, prop, val); + }); + }, + children: function(filter) + { + //Return the children directly, if there is no filter + if(typeof filter === "undefined") + { + return $_(this.el.children); + } + + var childs = (typeof this.el.children !== "undefined") ? this.el.children : this.el; + + if($_.type(filter) !== "string") + { + return $_(filter); + } + else if(filter.match(/#([\w\-]+$)/)) + { + return $_($_.$(filter)); + } + else + { + var filtered = _sel_filter(filter, childs); + return $_(filtered); + } + } + }; + + $_.ext('dom', d); + + }()); + + // -------------------------------------------------------------------------- + + /** + * Store object + * + * Wrapper for localstorage data serialization + */ + (function (){ + var store = { + get: function (key) + { + return JSON.parse(localStorage.getItem(key)); + }, + set: function (key, value) + { + if (typeof value !== "string") + { + value = JSON.stringify(value); + } + localStorage.setItem(key, value); + }, + getAll: function () + { + var i, + len, + data; + len = localStorage.length; + data = {}; + + for (i = 0; i < len; i++) + { + var name = localStorage.key(i); + var value = localStorage.getItem(name); + data[name] = value; + } + + return data; + } + }; + + $_.ext('store', store); + }()); + + // -------------------------------------------------------------------------- + + /** + * Qs + * + * Object for encoding and decoding querystrings and hashbang strings + */ + (function (){ + + $_.hb = (history.pushState) ? false : true; + + var qs = { + parse: function (hb) + { + hb = hb || $_.hb; + + var h, i, hString, pairs, pLen, data, y; + + data = {}; + + if (hb === true) + { + h = location.hash.split('#!/'); + hString = (h.length > 1) ? h[1] : ''; + } + else if (hb === false || hb === undefined) + { + hString = window.location.search.substring(1); + } + else + { + return false; + } + + pairs = hString.split('&'); + + pLen = pairs.length; + + for (i = 0; i < pLen; i++) + { + y = pairs[i].split('='); + + if (y.length < 2) + { + return data; + } + + data[y[0]] = y[1]; + } + + return data; + }, + set: function (key, value, hb) + { + hb = hb || $_.hb; + var pairs = this.parse(hb); + + if (key !== undefined && value !== undefined) + { + pairs[key] = value; + } + + var vars = []; + + for (var x in pairs) + { + if (pairs.hasOwnProperty(x)) + { + vars.push(x + '=' + pairs[x]); + } + } + + var qs = vars.join('&'); + + if (hb === true) + { + qs = '!/' + qs; + location.hash = qs; + } + + return qs; + }, + get: function (key, hb) + { + hb = hb || $_.hb; + var pairs = this.parse(hb); + return (pairs[key]) ? pairs[key] : ''; + } + }; + + $_.ext('qs', qs); + + }()); + + // -------------------------------------------------------------------------- + + /** + * Ajax + * + * Object for making ajax requests + */ + (function (){ + + var ajax = { + _do: function (url, data, callback, isPost) + { + if (typeof callback === "undefined") + { + callback = function (){}; + } + + var request = (typeof window.XMLHttpRequest !== "undefined") + ? new XMLHttpRequest() + : false; + + var type = (isPost) ? "POST" : "GET"; + + url += (type === "GET") ? "?"+this._serialize(data) : ''; + + request.open(type, url); + + request.onreadystatechange = function () + { + if (request.readyState === 4) + { + callback(request.responseText); + } + }; + + if (type === "POST") + { + request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + request.send(this._serialize(data)); + } + else + { + request.send(null); + } + }, + _serialize: function (data) + { + var pairs = []; + + for (var name in data) + { + if (!data.hasOwnProperty(name)) + { + continue; + } + if (typeof data[name] === "function") + { + continue; + } + + var value = data[name].toString(); + + name = encodeURIComponent(name); + value = encodeURIComponent(value); + + pairs.push(name + "=" + value); + } + + return pairs.join("&"); + } + }; + + $_.ext('get', function (url, data, callback){ + ajax._do(url, data, callback, false); + }); + + $_.ext('post', function (url, data, callback){ + ajax._do(url, data, callback, true); + }); + }()); + + // -------------------------------------------------------------------------- + + /** + * Event object + * + * Event api wrapper + */ + (function (){ + + // Property name for expandos on DOM objects + var kis_expando = "KIS_0_3_0"; + + var attach, remove, add_remove, e, attach_delegate, attach_live; + + // Define the proper attach and remove functions + // based on browser support + if(typeof document.addEventListener !== "undefined") + { + attach = function (sel, event, callback) + { + if (typeof sel.addEventListener !== "undefined") + { + sel.addEventListener(event, callback, false); + } + }; + remove = function (sel, event, callback) + { + if (typeof sel.removeEventListener !== "undefined") + { + sel.removeEventListener(event, callback, false); + } + }; + } + //typeof function doesn't work in IE where attachEvent is available: brute force it + else if(typeof document.attachEvent !== "undefined") + { + attach = function (sel, event, callback) + { + function listener () { + // Internet Explorer fails to correctly set the 'this' object + // for event listeners, so we need to set it ourselves. + callback.apply(arguments); + } + + if (typeof sel.attachEvent !== "undefined") + { + remove(event, callback); // Make sure we don't have duplicate listeners + + sel.attachEvent("on" + event, listener); + // Store our listener so we can remove it later + var expando = sel[kis_expando] = sel[kis_expando] || {}; + expando.listeners = expando.listeners || {}; + expando.listeners[event] = expando.listeners[event] || []; + expando.listeners[event].push({ + callback: callback, + listener: listener + }); + } + else + { + console.log("Failed to attach event:"+event+" on "+sel); + } + }; + remove = function (sel, event, callback) + { + if(typeof sel.detachEvent !== "undefined") + { + var expando = sel[kis_expando]; + if (expando && expando.listeners + && expando.listeners[event]) + { + var listeners = expando.listeners[event]; + var len = listeners.length; + for (var i=0; i1&&typeof b==="undefined")console.log(a),console.log("Must be a singular element");else if(a.length>1&&typeof b!== -"undefined")e.each(function(a){return c(a,d,b)});else return c(a,d,b)},text:function(d){var b,a,c;c=this.el;a=typeof c.innerText!=="undefined"?"innerText":typeof c.textContent!=="undefined"?"textContent":"innerHTML";b=c[a];return typeof d!=="undefined"?c[a]=d:b},css:function(d,b){if(typeof b==="undefined")return f(this.el,d);e.each(function(a){f(a,d,b)})},children:function(d){if(typeof d==="undefined")return e(this.el.children);var b=typeof this.el.children!=="undefined"?this.el.children:this.el; -if(e.type(d)!=="string")return e(d);else if(d.match(/#([\w\-]+$)/))return e(e.$(d));else{var c,f=b.length,g=[];if(d.match(a))for(c=0;c1?c[1]:"";else if(c===!1||c===void 0)c=window.location.search.substring(1);else return!1;g=c.split("&");f=g.length;for(c=0;c1&&typeof b==="undefined")console.log(a),console.log("Must be a singular element");else if(a.length> +1&&typeof b!=="undefined")e.each(function(a){return c(a,d,b)});else return c(a,d,b)},text:function(d){var b,a,c;c=this.el;a=typeof c.innerText!=="undefined"?"innerText":typeof c.textContent!=="undefined"?"textContent":"innerHTML";b=c[a];return typeof d!=="undefined"?c[a]=d:b},css:function(d,b){if(typeof b==="undefined")return f(this.el,d);e.each(function(a){f(a,d,b)})},children:function(d){if(typeof d==="undefined")return e(this.el.children);var b=typeof this.el.children!=="undefined"?this.el.children: +this.el;if(e.type(d)!=="string")return e(d);else if(d.match(/#([\w\-]+$)/))return e(e.$(d));else{var c,f=b.length,g=[];if(d.match(a))for(c=0;c1?c[1]:"";else if(c===false||c===void 0)c=window.location.search.substring(1);else return false;g=c.split("&");f=g.length;for(c=0;c li:last-child { diff --git a/tests/qunit/qunit.js b/tests/qunit/qunit.js index e00cca9..a711c82 100755 --- a/tests/qunit/qunit.js +++ b/tests/qunit/qunit.js @@ -15,10 +15,10 @@ var defined = { sessionStorage: (function() { try { return !!sessionStorage.getItem; - } catch(e){ + } catch(e) { return false; } - })() + })() }; var testId = 0; @@ -132,7 +132,7 @@ Test.prototype = { config.moduleStats.all += this.assertions.length; if ( tests ) { - var ol = document.createElement("ol"); + var ol = document.createElement("ol"); for ( var i = 0; i < this.assertions.length; i++ ) { var assertion = this.assertions[i]; @@ -275,7 +275,7 @@ var QUnit = { } // is 2nd argument a testEnvironment? if ( expected && typeof expected === 'object') { - testEnvironmentArg = expected; + testEnvironmentArg = expected; expected = null; } @@ -400,6 +400,9 @@ var QUnit = { // 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); } @@ -439,12 +442,18 @@ var config = { // 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, - noglobals: false, - notrycatch: false + // by default, modify document.title when suite is done + altertitle: true, + + urlConfig: ['noglobals', 'notrycatch'] }; // Load paramaters @@ -462,9 +471,6 @@ var config = { // 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 ]; - if ( current[ 0 ] in config ) { - config[ current[ 0 ] ] = current[ 1 ]; - } } } @@ -649,6 +655,10 @@ extend(QUnit, { return window.location.pathname + querystring.slice( 0, -1 ); }, + extend: extend, + id: id, + addEvent: addEvent, + // Logging callbacks; all receive a single argument with the listed properties // run test/logs.html for any related changes begin: function() {}, @@ -670,7 +680,7 @@ if ( typeof document === "undefined" || document.readyState === "complete" ) { config.autorun = true; } -addEvent(window, "load", function() { +QUnit.load = function() { QUnit.begin({}); // Initialize the config, saving the execution queue @@ -680,15 +690,19 @@ addEvent(window, "load", function() { config.blocking = false; + var urlConfigHtml = '', len = config.urlConfig.length; + for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) { + config[val] = QUnit.urlParams[val]; + urlConfigHtml += ''; + } + var userAgent = id("qunit-userAgent"); if ( userAgent ) { userAgent.innerHTML = navigator.userAgent; } var banner = id("qunit-header"); if ( banner ) { - banner.innerHTML = ' ' + banner.innerHTML + ' ' + - '' + - ''; + banner.innerHTML = ' ' + banner.innerHTML + ' ' + urlConfigHtml; addEvent( banner, "change", function( event ) { var params = {}; params[ event.target.name ] = event.target.checked ? true : undefined; @@ -711,13 +725,13 @@ addEvent(window, "load", function() { } if ( defined.sessionStorage ) { if (filter.checked) { - sessionStorage.setItem("qunit-filter-passed-tests", "true"); + sessionStorage.setItem("qunit-filter-passed-tests", "true"); } else { sessionStorage.removeItem("qunit-filter-passed-tests"); } } }); - if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { + if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { filter.checked = true; var ol = document.getElementById("qunit-tests"); ol.className = ol.className + " hidepass"; @@ -738,7 +752,9 @@ addEvent(window, "load", function() { if (config.autostart) { QUnit.start(); } -}); +}; + +addEvent(window, "load", QUnit.load); function done() { config.autorun = true; @@ -778,10 +794,13 @@ function done() { id( "qunit-testresult" ).innerHTML = html; } - if ( typeof document !== "undefined" && document.title ) { + 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; + document.title = [ + (config.stats.bad ? "\u2716" : "\u2714"), + document.title.replace(/^[\u2714\u2716] /i, "") + ].join(" "); } QUnit.done( { @@ -828,6 +847,10 @@ function sourceFromStacktrace() { } else if (e.stack) { // Firefox, Chrome return e.stack.split("\n")[4]; + } else if (e.sourceURL) { + // Safari, PhantomJS + // TODO sourceURL points at the 'throw new Error' line above, useless + //return e.sourceURL + ":" + e.line; } } } @@ -868,9 +891,9 @@ function process() { break; } } - if (!config.blocking && !config.queue.length) { - done(); - } + if (!config.blocking && !config.queue.length) { + done(); + } } function saveGlobal() { @@ -957,170 +980,182 @@ function id(name) { // Author: Philippe Rathé QUnit.equiv = function () { - var innerEquiv; // the real equiv function - var callers = []; // stack to decide between skip/abort functions - var parents = []; // stack to avoiding loops from circular referencing + var innerEquiv; // the real equiv function + var callers = []; // stack to decide between skip/abort functions + var parents = []; // stack to avoiding loops from circular referencing - // 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 - } - } - } + // 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 + } + } + } - var callbacks = function () { + var 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; - } - } + // 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, + return { + "string" : useStrictEquality, + "boolean" : useStrictEquality, + "number" : useStrictEquality, + "null" : useStrictEquality, + "undefined" : useStrictEquality, - "nan": function (b) { - return isNaN(b); - }, + "nan" : function(b) { + return isNaN(b); + }, - "date": function (b, a) { - return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); - }, + "date" : function(b, a) { + return QUnit.objectType(b) === "date" + && a.valueOf() === b.valueOf(); + }, - "regexp": function (b, a) { - return QUnit.objectType(b) === "regexp" && - a.source === b.source && // the regex itself - a.global === b.global && // and its modifers (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline; - }, + "regexp" : function(b, a) { + return QUnit.objectType(b) === "regexp" + && a.source === b.source && // the regex itself + a.global === b.global && // and its modifers + // (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"; - }, + // - 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, loop; - var len; + "array" : function(b, a) { + var i, j, loop; + var len; - // b could be an object literal here - if ( ! (QUnit.objectType(b) === "array")) { - return false; - } + // 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; - } + 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;jbrown fox jumped jumps over" */ QUnit.diff = (function() { - function diff(o, n){ - var ns = new Object(); - var os = new Object(); + function diff(o, n) { + var ns = {}; + var os = {}; for (var i = 0; i < n.length; i++) { if (ns[n[i]] == null) ns[n[i]] = { - rows: new Array(), + rows: [], o: null }; ns[n[i]].rows.push(i); @@ -1339,7 +1401,7 @@ QUnit.diff = (function() { for (var i = 0; i < o.length; i++) { if (os[o[i]] == null) os[o[i]] = { - rows: new Array(), + rows: [], n: null }; os[o[i]].rows.push(i); @@ -1392,7 +1454,7 @@ QUnit.diff = (function() { }; } - return function(o, n){ + return function(o, n) { o = o.replace(/\s+$/, ''); n = n.replace(/\s+$/, ''); var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));