From 69ee39623ddc0117e81eec743c6740d43a4e3896 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Fri, 21 Oct 2011 09:57:46 -0400 Subject: [PATCH] Added reverse_key_sort method to util module, updated tests and qunit --- combine.php | 16 +--- kis-all.js | 81 ++++++++++++++---- kis-min.js | 37 +++++---- src/util.js | 67 +++++++++++++-- tests/qunit/qunit.js | 193 ++++++++++++++++++++++++++++++------------- tests/tests.js | 70 +++++++++++++--- 6 files changed, 344 insertions(+), 120 deletions(-) diff --git a/combine.php b/combine.php index 90b80ea..0391cd8 100755 --- a/combine.php +++ b/combine.php @@ -29,27 +29,16 @@ if($dir = opendir($folder)) //Define files that aren't modules $special_files = array( 'core.js', - 'module_vars.js', ); //Filter out special files $src_files = array_diff($files, $special_files); -$syntax_start = array( - '//Function to maintain module scope', - '(function(){', - '', - ' "use strict";', -); - //Start with the core $new_file = file_get_contents($folder."/core.js") . "\n"; //Add the opening of the function for the modules -$new_file .= "\n// --------------------------------------------------------------------------\n\n".implode("\n", $syntax_start); - -//Add the module-global variables -$new_file .= "\n\n".file_get_contents($folder."/module_vars.js")."\n"; +$new_file .= "\n// --------------------------------------------------------------------------\n\n"; //Add the modules foreach($src_files as $f) @@ -71,9 +60,6 @@ foreach($src_files as $f) } -//Add the close of the module function -$new_file .= "\n}());"; - //Output the full file file_put_contents("kis-custom.js", $new_file); diff --git a/kis-all.js b/kis-all.js index d0fb1c0..0ca172a 100644 --- a/kis-all.js +++ b/kis-all.js @@ -185,13 +185,6 @@ // -------------------------------------------------------------------------- -//Function to maintain module scope -(function(){ - - "use strict"; - - //Fix $_ is not defined errors - var $_ = $_ || window.$_; // -------------------------------------------------------------------------- @@ -716,6 +709,11 @@ }()); + // -------------------------------------------------------------------------- + + //Fix $_ is not defined errors + var $_ = $_ || window.$_; + // -------------------------------------------------------------------------- /** @@ -805,17 +803,18 @@ * Various object and string manipulation functions */ (function(){ + var u = { object_keys: function(o) { var keys = [], - key; + k; - for(key in o) + for(k in o) { - if(o.hasOwnProperty(key)) + if(o.hasOwnProperty(k)) { - keys.push(key); + keys.push(k); } } @@ -832,8 +831,64 @@ } return vals; - } + }, + object_merge: function() + { + }, + reverse_key_sort: function(o) + { + //Define some variables + var keys = [], + num_keys = 0, + new_o = {}, + i, + k, + x; + + //Extract the keys + keys = this.object_keys(o); + + //Sort the keys + keys.sort(function (b, a) { + + var aFloat = parseFloat(a), + bFloat = parseFloat(b), + aNumeric = aFloat + '' === a, + bNumeric = bFloat + '' === b; + + if (aNumeric && bNumeric) + { + return aFloat > bFloat ? 1 : aFloat < bFloat ? -1 : 0; + } + else if (aNumeric && !bNumeric) + { + return 1; + } + else if (!aNumeric && bNumeric) + { + return -1; + } + + return a > b ? 1 : a < b ? -1 : 0; + }); + + //cache object/array size + num_keys = keys.length; + + //Recreate the object/array + for(i=0; i < num_keys; i++) + { + k = keys[i]; + new_o[k] = o[k]; + } + + return new_o; + }, + str_trans: function(string, from, to) + { + + } }; //Add it to the $_ object @@ -1019,5 +1074,3 @@ $_.ext('event', e); }()); - -}()); \ No newline at end of file diff --git a/kis-min.js b/kis-min.js index 6e7d610..ea25228 100644 --- a/kis-min.js +++ b/kis-min.js @@ -1,17 +1,20 @@ -(function(){if(document.querySelectorAll){var g,c,f,d;c=function(a){if(typeof a!=="string"||typeof a==="undefined")return a;if(a.match(/^#([\w\-]+$)/))return document.getElementById(a.split("#")[1]);else a=a.match(/^([\w\-]+)$/)?document.getElementsByTagName(a):document.querySelectorAll(a);return a.length===1?a[0]:a};g=function(a){d=typeof a==="undefined"?typeof g.el!=="undefined"?g.el:document.documentElement:typeof a!=="object"?c(a):a;g.prototype.el=d;var a=f(g),b;for(b in a)if(typeof a[b]==="object")a[b].el= -d;a.el=d;return a};f=function(a){var b;if(typeof a!=="undefined"){if(typeof Object.create!=="undefined")return Object.create(a);b=typeof a;if(!(b!=="object"&&b!=="function"))return b=function(){},b.prototype=a,new b}};g.ext=function(a,b){b.el=d;g[a]=b};g.ext("each",function(a){if(typeof d.length!=="undefined"&&d!==window){var b=d.length;if(b!==0)for(var e,h=0;h1&&typeof b==="undefined")console.log(e),console.log("Must be a singular element");else if(e.length>1&&typeof b!=="undefined")g.each(function(e){return c(e, -a,b)});else return c(e,a,b)},text:function(a){var b,e,h;h=this.el;e=typeof h.innerText!=="undefined"?"innerText":typeof h.textContent!=="undefined"?"textContent":"innerHTML";b=h[e];return typeof a!=="undefined"?h[e]=a:b},css:function(a,b){if(typeof b==="undefined")return d(this.el,a);g.each(function(e){d(e,a,b)})}})})();(function(){g.ext("store",{get:function(c){return JSON.parse(localStorage.getItem(c))},set:function(c,f){typeof f!=="string"&&(f=JSON.stringify(f));localStorage.setItem(c,f)},remove:function(c){localStorage.removeItem(c)}, -getAll:function(){var c,f,d;f=localStorage.length;d={};for(c=0;c1?c[1]:"";else if(c===false||c===void 0)c=window.location.search.substring(1);else return false;f=c.split("&");d=f.length;for(c=0;c1&&typeof a==="undefined")console.log(d),console.log("Must be a singular element");else if(d.length>1&&typeof a!== +"undefined")$_.each(function(d){return b(d,c,a)});else return b(d,c,a)},text:function(c){var a,d,b;b=this.el;d=typeof b.innerText!=="undefined"?"innerText":typeof b.textContent!=="undefined"?"textContent":"innerHTML";a=b[d];return typeof c!=="undefined"?b[d]=c:a},css:function(b,a){if(typeof a==="undefined")return f(this.el,b);$_.each(function(d){f(d,b,a)})}})})(); +(function(){$_.ext("store",{get:function(b){return JSON.parse(localStorage.getItem(b))},set:function(b,e){typeof e!=="string"&&(e=JSON.stringify(e));localStorage.setItem(b,e)},remove:function(b){localStorage.removeItem(b)},getAll:function(){var b,e,f;e=localStorage.length;f={};for(b=0;b1?b[1]:"";else if(b===false||b===void 0)b=window.location.search.substring(1);else return false;e=b.split("&");f=e.length;for(b=0;bc?1:ba?1:d bFloat ? 1 : aFloat < bFloat ? -1 : 0; + } + else if (aNumeric && !bNumeric) + { + return 1; + } + else if (!aNumeric && bNumeric) + { + return -1; + } + + return a > b ? 1 : a < b ? -1 : 0; + }); + + //cache object/array size + num_keys = keys.length; + + //Recreate the object/array + for(i=0; i < num_keys; i++) + { + k = keys[i]; + new_o[k] = o[k]; + } + + return new_o; + }, + str_trans: function(string, from, to) + { + + } }; //Add it to the $_ object diff --git a/tests/qunit/qunit.js b/tests/qunit/qunit.js index a711c82..b17f168 100755 --- a/tests/qunit/qunit.js +++ b/tests/qunit/qunit.js @@ -1,11 +1,13 @@ /** - * QUnit - A JavaScript Unit Testing Framework + * QUnit 1.2.0pre - A JavaScript Unit Testing Framework * * http://docs.jquery.com/QUnit * * Copyright (c) 2011 John Resig, Jörn Zaefferer * Dual licensed under the MIT (MIT-LICENSE.txt) * or GPL (GPL-LICENSE.txt) licenses. + * Pulled Live from Git Fri Oct 21 15:55:01 UTC 2011 + * Last Commit: ee156923cdb01820e35e6bb579d5cf6bf55736d4 */ (function(window) { @@ -21,7 +23,9 @@ var defined = { })() }; -var testId = 0; +var testId = 0, + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty; var Test = function(name, testName, expected, testEnvironmentArg, async, callback) { this.name = name; @@ -48,7 +52,7 @@ Test.prototype = { setup: function() { if (this.module != config.previousModule) { if ( config.previousModule ) { - QUnit.moduleDone( { + runLoggingCallbacks('moduleDone', QUnit, { name: config.previousModule, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, @@ -57,7 +61,7 @@ Test.prototype = { } config.previousModule = this.module; config.moduleStats = { all: 0, bad: 0 }; - QUnit.moduleStart( { + runLoggingCallbacks( 'moduleStart', QUnit, { name: this.module } ); } @@ -71,9 +75,10 @@ Test.prototype = { extend(this.testEnvironment, this.testEnvironmentArg); } - QUnit.testStart( { - name: this.testName - } ); + runLoggingCallbacks( 'testStart', QUnit, { + name: this.testName, + module: this.module + }); // allow utility functions to access the current test environment // TODO why?? @@ -90,6 +95,7 @@ Test.prototype = { } }, run: function() { + config.current = this; if ( this.async ) { QUnit.stop(); } @@ -108,11 +114,12 @@ Test.prototype = { // Restart the tests if they're blocking if ( config.blocking ) { - start(); + QUnit.start(); } } }, teardown: function() { + config.current = this; try { this.testEnvironment.teardown.call(this.testEnvironment); checkPollution(); @@ -121,7 +128,8 @@ Test.prototype = { } }, finish: function() { - if ( this.expected && this.expected != this.assertions.length ) { + config.current = this; + if ( this.expected != null && this.expected != this.assertions.length ) { QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); } @@ -210,8 +218,9 @@ Test.prototype = { fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); } - QUnit.testDone( { + runLoggingCallbacks( 'testDone', QUnit, { name: this.testName, + module: this.module, failed: bad, passed: this.assertions.length - bad, total: this.assertions.length @@ -243,7 +252,7 @@ Test.prototype = { if (bad) { run(); } else { - synchronize(run); + synchronize(run, true); }; } @@ -260,7 +269,7 @@ var QUnit = { asyncTest: function(testName, expected, callback) { if ( arguments.length === 2 ) { callback = expected; - expected = 0; + expected = null; } QUnit.test(testName, expected, callback, true); @@ -310,8 +319,8 @@ var QUnit = { result: a, message: msg }; - msg = escapeHtml(msg); - QUnit.log(details); + msg = escapeInnerText(msg); + runLoggingCallbacks( 'log', QUnit, details ); config.current.assertions.push({ result: a, message: msg @@ -387,8 +396,8 @@ var QUnit = { QUnit.ok(ok, message); }, - start: function() { - config.semaphore--; + start: function(count) { + config.semaphore -= count || 1; if (config.semaphore > 0) { // don't start until equal number of stop-calls return; @@ -408,28 +417,38 @@ var QUnit = { } config.blocking = false; - process(); + process(true); }, 13); } else { config.blocking = false; - process(); + process(true); } }, - stop: function(timeout) { - config.semaphore++; + stop: function(count) { + config.semaphore += count || 1; config.blocking = true; - if ( timeout && defined.setTimeout ) { + if ( config.testTimeout && defined.setTimeout ) { clearTimeout(config.timeout); config.timeout = window.setTimeout(function() { QUnit.ok( false, "Test timed out" ); + config.semaphore = 1; QUnit.start(); - }, timeout); + }, config.testTimeout); } } }; +//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; +})(); + // Backwards compatibility, deprecated QUnit.equals = QUnit.equal; QUnit.same = QUnit.deepEqual; @@ -453,7 +472,16 @@ var config = { // by default, modify document.title when suite is done altertitle: true, - urlConfig: ['noglobals', 'notrycatch'] + urlConfig: ['noglobals', 'notrycatch'], + + //logging callback queues + begin: [], + done: [], + log: [], + testStart: [], + testDone: [], + moduleStart: [], + moduleDone: [] }; // Load paramaters @@ -586,8 +614,7 @@ extend(QUnit, { return "null"; } - var type = Object.prototype.toString.call( obj ) - .match(/^\[object\s(.*)\]$/)[1] || ''; + var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || ''; switch (type) { case 'Number': @@ -618,10 +645,10 @@ extend(QUnit, { expected: expected }; - message = escapeHtml(message) || (result ? "okay" : "failed"); + message = escapeInnerText(message) || (result ? "okay" : "failed"); message = '' + message + ""; - expected = escapeHtml(QUnit.jsDump.parse(expected)); - actual = escapeHtml(QUnit.jsDump.parse(actual)); + expected = escapeInnerText(QUnit.jsDump.parse(expected)); + actual = escapeInnerText(QUnit.jsDump.parse(actual)); var output = message + ''; if (actual != expected) { output += ''; @@ -631,12 +658,12 @@ extend(QUnit, { var source = sourceFromStacktrace(); if (source) { details.source = source; - output += ''; + output += ''; } } output += "
Expected:
' + expected + '
Result:
' + actual + '
Source:
' + escapeHtml(source) + '
Source:
' + escapeInnerText(source) + '
"; - QUnit.log(details); + runLoggingCallbacks( 'log', QUnit, details ); config.current.assertions.push({ result: !!result, @@ -649,6 +676,9 @@ extend(QUnit, { var querystring = "?", key; for ( key in params ) { + if ( !hasOwn.call( params, key ) ) { + continue; + } querystring += encodeURIComponent( key ) + "=" + encodeURIComponent( params[ key ] ) + "&"; } @@ -657,23 +687,28 @@ extend(QUnit, { extend: extend, id: id, - addEvent: addEvent, + addEvent: addEvent +}); +//QUnit.constructor is set to the empty F() above so that we can add to it's prototype later +//Doing this allows us to tell if the following methods have been overwritten on the actual +//QUnit object, which is a deprecated way of using the callbacks. +extend(QUnit.constructor.prototype, { // Logging callbacks; all receive a single argument with the listed properties // run test/logs.html for any related changes - begin: function() {}, + begin: registerLoggingCallback('begin'), // done: { failed, passed, total, runtime } - done: function() {}, + done: registerLoggingCallback('done'), // log: { result, actual, expected, message } - log: function() {}, + log: registerLoggingCallback('log'), // testStart: { name } - testStart: function() {}, + testStart: registerLoggingCallback('testStart'), // testDone: { name, failed, passed, total } - testDone: function() {}, + testDone: registerLoggingCallback('testDone'), // moduleStart: { name } - moduleStart: function() {}, + moduleStart: registerLoggingCallback('moduleStart'), // moduleDone: { name, failed, passed, total } - moduleDone: function() {} + moduleDone: registerLoggingCallback('moduleDone') }); if ( typeof document === "undefined" || document.readyState === "complete" ) { @@ -681,7 +716,7 @@ if ( typeof document === "undefined" || document.readyState === "complete" ) { } QUnit.load = function() { - QUnit.begin({}); + runLoggingCallbacks( 'begin', QUnit, {} ); // Initialize the config, saving the execution queue var oldconfig = extend({}, config); @@ -756,12 +791,23 @@ QUnit.load = function() { addEvent(window, "load", QUnit.load); +// addEvent(window, "error") gives us a useless event object +window.onerror = function( message, file, line ) { + if ( QUnit.config.current ) { + ok( false, message + ", " + file + ":" + line ); + } else { + test( "global failure", function() { + ok( false, message + ", " + file + ":" + line ); + }); + } +}; + function done() { config.autorun = true; // Log the last module results if ( config.currentModule ) { - QUnit.moduleDone( { + runLoggingCallbacks( 'moduleDone', QUnit, { name: config.currentModule, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, @@ -803,7 +849,7 @@ function done() { ].join(" "); } - QUnit.done( { + runLoggingCallbacks( 'done', QUnit, { failed: config.stats.bad, passed: passed, total: config.stats.all, @@ -855,16 +901,14 @@ function sourceFromStacktrace() { } } -function escapeHtml(s) { +function escapeInnerText(s) { if (!s) { return ""; } s = s + ""; - return s.replace(/[\&"<>\\]/g, function(s) { + return s.replace(/[\&<>]/g, function(s) { switch(s) { case "&": return "&"; - case "\\": return "\\\\"; - case '"': return '\"'; case "<": return "<"; case ">": return ">"; default: return s; @@ -872,26 +916,30 @@ function escapeHtml(s) { }); } -function synchronize( callback ) { +function synchronize( callback, last ) { config.queue.push( callback ); if ( config.autorun && !config.blocking ) { - process(); + process(last); } } -function process() { - var start = (new Date()).getTime(); +function process( last ) { + var start = new Date().getTime(); + config.depth = config.depth ? config.depth + 1 : 1; while ( config.queue.length && !config.blocking ) { - if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { + if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { config.queue.shift()(); } else { - window.setTimeout( process, 13 ); + window.setTimeout( function(){ + process( last ); + }, 13 ); break; } } - if (!config.blocking && !config.queue.length) { + config.depth--; + if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { done(); } } @@ -901,6 +949,9 @@ function saveGlobal() { if ( config.noglobals ) { for ( var key in window ) { + if ( !hasOwn.call( window, key ) ) { + continue; + } config.pollution.push( key ); } } @@ -951,7 +1002,9 @@ function extend(a, b) { for ( var prop in b ) { if ( b[prop] === undefined ) { delete a[prop]; - } else { + + // Avoid "Member not found" error in IE8 caused by setting window.constructor + } else if ( prop !== "constructor" || a !== window ) { a[prop] = b[prop]; } } @@ -974,9 +1027,27 @@ function id(name) { 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 callbacks; + if ( QUnit.hasOwnProperty(key) ) { + QUnit[key].call(scope, args); + } else { + callbacks = config[key]; + for( var i = 0; i < callbacks.length; i++ ) { + callbacks[i].call( scope, args ); + } + } +} + // Test for equality any JavaScript type. -// Discussions and reference: http://philrathe.com/articles/equiv -// Test suites: http://philrathe.com/tests/equiv // Author: Philippe Rathé QUnit.equiv = function () { @@ -1226,7 +1297,12 @@ QUnit.jsDump = (function() { type = "document"; } else if (obj.nodeType) { type = "node"; - } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) { + } 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; @@ -1408,6 +1484,9 @@ QUnit.diff = (function() { } for (var 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]], @@ -1507,4 +1586,4 @@ QUnit.diff = (function() { }; })(); -})(this); +})(this); \ No newline at end of file diff --git a/tests/tests.js b/tests/tests.js index cd6d1d4..db9bd8e 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -176,22 +176,68 @@ equal(ele.style.display, "block", "Setting CSS"); equal($test.dom.css("display"), "block", "Getting CSS"); }); - - /*test("Children", function(){ - var $test = $_("section"); - var ele = $("section"); - var ele2 = $_("section aside").el; - - equal($_("section").dom.children().el, ele.children, "Returns children without parameters"); - equal($_("section").dom.children('#r14').el, document.getElementById('r14'), "Finds id"); - equal($_("section").dom.children("aside").el, ele2, "Finds children by tag name"); - equal($_("section aside").dom.children(".child").el, $_("#classChild .child").el, "Finds children by class"); - });*/ // -------------------------------------------------------------------------- module("util"); + + test("Object keys", function(){ + expect(1); + + var test_o = { + "x": 2, + "a": 4, + "q": 3, + "r": 6 + }; + + var test_keys = ["x", "a", "q", "r"]; + + deepEqual($_.util.object_keys(test_o), test_keys, "Retrieves object keys correctly"); + + }); + + test("Object values", function(){ + expect(1); + + var test_o = { + "x": 2, + "a": 4, + "q": 3, + "r": 6, + "p": "q" + }; + + var test_values = [2,4,3,6,"q"]; + + deepEqual($_.util.object_values(test_o), test_values, "Retrieves object values correctly"); + + }); + + test("Reverse Key Sort", function(){ + expect(2); + + var test_o = { + "x": 2, + "a": 4, + "q": 3, + "r": 6 + }; + + var test_sorted = { + "x": 2, + "r": 6, + "q": 3, + "a": 4 + }; + + var test_array = [7, 2, 6, 3]; + var test_array_sorted = [3, 6, 2, 7]; + + deepEqual($_.util.reverse_key_sort(test_o), test_sorted, "Object sort"); + deepEqual($_.util.object_values($_.util.reverse_key_sort(test_array)), test_array_sorted, "Array Sort"); + }); + - }()); \ No newline at end of file