From 457ccf07a43bf33b82905569681ec1c4c820dbea Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Thu, 14 Jul 2011 21:52:17 -0400 Subject: [PATCH] Fixed a bunch of issues, added more unit tests. All pass --- kis.js | 406 +++++++++++++++++++++++++++++-------------------- tests/tests.js | 63 ++++++-- 2 files changed, 287 insertions(+), 182 deletions(-) diff --git a/kis.js b/kis.js index b6d4666..31aae9d 100644 --- a/kis.js +++ b/kis.js @@ -6,7 +6,7 @@ */ (function (){ - "use strict"; + //"use strict"; // Property name for expandos on DOM objects var kis_expando = "KIS_0_2_0"; @@ -17,17 +17,7 @@ return; } - var $_, $, dcopy; - - /** - * String trim function polyfill - */ - if(typeof String.prototype.trim === "undefined") - { - String.prototype.trim = function(){ - return this.replace(/^\s+|\s+$/g, ""); - }; - } + var $_, $, dcopy, sel; /** * $ @@ -37,7 +27,7 @@ $ = function (a) { var x; - if (typeof a !== "string"){ return a;} + if (typeof a !== "string" || typeof a === "undefined"){ return a;} //Pick the quickest method for each kind of selector if(a.match(/^#([\w\-]+$)/)) @@ -62,21 +52,22 @@ * * Constructor function */ - $_ = function(sel) + $_ = function(s) { + //Have documentElement be default selector, just in case + if(typeof s == "undefined") + { + sel = (typeof $_.el !== "undefined") + ? $_.el + : document.documentElement; + } + else + { + sel = $(s);// || document.documentElement; + } + // Make a copy before adding properties var self = dcopy($_); - - //Get the DOM objects from the selector - sel = $(sel); - - //Have window be default selector, just in case - if(typeof sel === "undefined") - { - sel = (typeof self.el !== "undefined") - ? self.el - : window; - } self.el = sel; @@ -114,6 +105,40 @@ return new f(); }; + + //Function to add to $_ object, and get sel + $_.ext = function(name, obj) + { + $_[name] = obj; + obj.el = sel; + + console.log(obj.el); + }; + + //Selector iteration + $_.ext('each', function (callback) + { + if(typeof sel.length !== "undefined") + { + 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); + } + }); //Set global variables $_ = window.$_ = window.$_ || $_; @@ -127,44 +152,22 @@ }; } - // -------------------------------------------------------------------------- - - $_.each = function (callback) + /** + * String trim function polyfill + */ + if(typeof String.prototype.trim === "undefined") { - var sel = $_.el; - - //Don't try to iterate over the window object - if(sel === window || typeof sel === "undefined") - { - return; - } - - if(typeof sel.length !== "undefined") - { - var len = sel.length; + String.prototype.trim = function(){ + return this.replace(/^\s+|\s+$/g, ""); + }; + } + + // -------------------------------------------------------------------------- +}()); - if (len === 0) - { - return; - } - - if (len === 1) - { - return callback(sel); - } - - var selx; - for (var x = 0; x < sel.length; x++) - { - selx = (sel.item(x)) ? sel.item(x) : sel[x]; - callback(selx); - } - } - else - { - callback(sel); - } - }; +(function(){ + + "use strict"; /** * Ajax @@ -239,15 +242,13 @@ } }; - $_.get = function (url, data, callback) - { + $_.ext('get', function (url, data, callback){ ajax._do(url, data, callback, false); - }; + }); - $_.post = function (url, data, callback) - { + $_.ext('post', function (url, data, callback){ ajax._do(url, data, callback, true); - }; + }); }()); /** @@ -338,7 +339,7 @@ } }; - $_.qs = qs; + $_.ext('qs', qs); }()); @@ -380,7 +381,7 @@ } }; - $_.store = store; + $_.ext('store', store); }()); /** @@ -469,21 +470,10 @@ }; } - add_remove = function (event, callback, add) + add_remove = function (sel, event, callback, add) { var i, len; - //Get the DOM object - var sel = $_.el; - - if(arguments.length === 4) - { - sel = arguments[0]; - event = arguments[1]; - callback = arguments[2]; - add = arguments[3]; - } - if(typeof sel === "undefined") { console.log(arguments); @@ -500,34 +490,34 @@ for (i = 0; i < len; i++) { - add_remove(event[i], callback, add); + add_remove(sel, event[i], callback, add); } return; } - //Go over additonal DOM objects as needed - $_.each(function(e){ - (add === true) - ? attach(e, event, callback) - : remove(e, event, callback); - - }); + (add === true) + ? attach(e, event, callback) + : remove(e, event, callback); }; e = { add: function (event, callback) { - add_remove(event, callback, true); + $_.each(function(e){ + add_remove(e, event, callback, true); + }); }, remove: function (event, callback) { - add_remove(event, callback, false); + $_.each(function(e){ + add_remove(e, event, callback, false); + }); } }; - $_.event = e; + $_.ext('event', e); }()); @@ -601,87 +591,164 @@ return (typeof value !== "undefined") ? value : oldVal; } + + /* + * 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. + */ - // Private function for class manipulation - function _class(sel, c, add) + /*global self, document, DOMException */ + + /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/ + + if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) { - var ec, cs, len, i, classInd; + (function (view){ + "use strict"; - //We can do this the easy way - if (sel.classList) - { - if (add === true) - { - sel.classList.add(c); - return; - } - else if (add === false) - { - sel.classList.remove(c); - return; - } - } - else //Or the hard way - { - //No class attribute? Set an empty string - ec = sel.className;//_attr("class"); - ec = (typeof ec === "string") ? ec : ''; - - //Convert class attribute string into array - if (typeof ec === "string") - { - cs = (ec !== '') ? ec.split(" ") : []; - - len = cs.length; - classInd = false; - - //Check for the class in the array - for (i = 0; i < len; i++) + var classListProp = "classList", + protoProp = "prototype", + elemCtrProto = (view.HTMLElement || view.Element)[protoProp], + objCtr = Object, + strTrim = String[protoProp].trim || + function () { - if (cs[i] === c) + return this.replace(/^\s+|\s+$/g, ""); + }, + arrIndexOf = Array[protoProp].indexOf || + function (item) + { + var + i = 0, + len = this.length; + for (; i < len; i++) { - classInd = i; - break; + 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); } } } - - //Add or remove from class array - if (add === true) + else if (objCtr[protoProp].__defineGetter__) { - //Only add the class if isn't already there - if (classInd === false) - { - cs.push(c); - } - } - else if (add === false) - { - //Make sure the class you want to remove exists - if (classInd !== false) - { - cs.splice(classInd, 1); - } + elemCtrProto.__defineGetter__(classListProp, classListGetter); } - var cName = (cs.length > 1) ? cs.join(" ") : cs[0]; - - if (typeof sel.className !== "undefined") - { - sel.className = cName; - } - else if (typeof sel.setAttribute !== "undefined") - { - sel.setAttribute('class', cName); - } - else - { - console.log(sel); - } - - return cName; - } - + }(self)); } function _toCamel(s) @@ -733,13 +800,15 @@ addClass: function (c) { $_.each(function (e){ - _class(e, c, true); + console.log(e); + console.log(c); + e.classList.add(c); }); }, removeClass: function (c) { $_.each(function (e){ - _class(e, c, false); + e.classList.remove(c); }); }, hide: function () @@ -757,7 +826,7 @@ }, attr: function (name, value) { - var sel = $_.el; + var sel = this.el; //Make sure you don't try to get a bunch of elements if (sel.length > 1 && typeof value === "undefined") @@ -769,7 +838,7 @@ else if (sel.length > 1 && typeof value !== "undefined") //You can set a bunch, though { $_.each(function (e){ - _attr(e, name, value); + return _attr(e, name, value); }); } else //Normal behavior @@ -781,7 +850,7 @@ { var oldValue, set, type, sel; - sel = $_.el; + sel = this.el; set = (typeof value !== "undefined") ? true : false; @@ -811,7 +880,8 @@ } }; - $_.dom = d; + $_.ext('dom', d); + }()); }()); diff --git a/tests/tests.js b/tests/tests.js index 59458eb..15bf752 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -17,36 +17,68 @@ var y = $_("aside"); expect(1); - notEqual(x.el, y.el, "Unique Query Objects - see Issue #5"); + notStrictEqual(x.el, y.el, "Unique Query Objects - see Issue #5"); + }); + + test("Extend function", function(){ + var o = $_("ol"); + + expect(3); + ok(o.ext, "Extend function exists"); + + $_.ext('test', {}); + strictEqual(typeof o.test, "object", "Extend function adds to $_"); + equal(o.test.el, $_("ol").el, "Extend function adds selector to passed object"); + + + }); + + test("Selector tests", function(){ + var i=0; + $_("div").each(function(e){ + equal(e, $_("div").el[i], ".each function has current selector"); + i++; + }); + equal($_().el, window.document.documentElement, "Empty selector is set to documentElement"); }); test("Sub-modules", function(){ - expect(5); - ok($_.get, "AJAX get method"); - ok($_.post, "AJAX post method"); + expect(4); ok($_.qs, "Query String module"); + ok($_().event, "Event module"); ok($_.store, "Local Storage module"); - ok($_.dom, "Dom manipulation module"); + ok($_().dom, "Dom manipulation module"); }); // -------------------------------------------------------------------------- module("ajax"); + test("Methods defined", function(){ + expect(2); + ok($_.get, "AJAX get method"); + ok($_.post, "AJAX post method"); + }); // -------------------------------------------------------------------------- module("events"); + test("Events defined", function(){ + expect(2); + ok($_.event.add, "Add Method Exists"); + ok($_.event.remove, "Remove Method Exists"); + }); + test("Browser expando support", function() { expect(3); - // kis-js events uses expando properties to store event listeners + // kis-js events uses expando properties to store event listeners for IE // If this test fails, the event module will likely fail as well var ele = document.createElement("div"); ele.expando = {a:5, b:"c", c: function cool(){return ele}}; - equals(ele.expando.a, 5); - equals(ele.expando.b, "c"); - equals(ele.expando.c(), ele, + equal(ele.expando.a, 5); + equal(ele.expando.b, "c"); + equal(ele.expando.c(), ele, "Closure isn't broken by being assigned to an expando property"); }); @@ -56,19 +88,22 @@ test("Add/Remove Class", function() { expect(4); - var $test = $_("#testSpan"); - var ele = $test.el; + //var $test = $_("#testSpan"); + //var ele = $test.el; + var ele = document.getElementById('testSpan'); + var $test = $_(ele); $test.dom.addClass("coolClass"); - equals(ele.className, "coolClass"); + equal(ele.className, "coolClass"); $test.dom.addClass("anotherClass"); - equals(ele.className, "coolClass anotherClass"); + equal(ele.className, "coolClass anotherClass"); $test.dom.removeClass("coolClass"); - equals(ele.className, "anotherClass"); + equal(ele.className, "anotherClass"); $test.dom.removeClass("anotherClass"); ok(ele.className === undefined || ele.className === "", "testSpan.className is empty"); }); + }()); \ No newline at end of file