1 // Save a reference to some core methods 2 var toString = Object.prototype.toString, 3 hasOwn = Object.prototype.hasOwnProperty, 4 push = Array.prototype.push, 5 slice = Array.prototype.slice, 6 indexOf = Array.prototype.indexOf, 7 trim = String.prototype.trim, 8 UNDEF, 9 WIN = window, 10 DOC = document, 11 RCAPITALISE = /\b(\w)(\w+)\b/g, 12 /** @private */ 13 fcapitalise = function( all, first, rest ){ 14 return first.toUpperCase() + rest.toLowerCase(); 15 }, 16 FIRST_SPACES = /^\s*/, 17 LAST_SPACES = /\s*$/, 18 DOMLOADED = "DOMContentLoaded", 19 READYSTATE = "onreadystatechange", 20 SCRIPT = "script", 21 OPACITY = "opacity", 22 TOP = "top", 23 LEFT = "left", 24 COMPLETE = "complete", 25 // The ready event handler 26 DOMContentLoaded, 27 // Has the ready events already been bound? 28 readyBound = false, 29 // The functions to execute on DOM ready 30 readyList = []; 31 /** 32 * @description used to instantiate the Simples object 33 * @param {String|Element} selector element is used by object and string is used to select Element(s), see Simples.Selector for more information 34 * @param {Element} context element used to provide context 35 **/ 36 function Simples( selector, context ) { 37 return new Simples.fn.init( selector, context ); 38 } 39 40 /** 41 * @description used to test the Constructor / Class of an object 42 * @param {Object} object the object to test 43 * @param {String} objectClass the class name to test 44 * @param {Boolean} doCapitalisation if you don't want to force the capitalisation of the className 45 **/ 46 Simples.isConstructor = function( obj, className, mustCapitalise ){ 47 if( obj !== null && obj !== UNDEF ){ 48 return toString.call( obj ) === "[object "+( mustCapitalise ? className.replace(RCAPITALISE,fcapitalise) : className )+"]"; 49 } 50 return false; 51 }; 52 /** 53 * @description used to test the Constructor / Class of an object 54 * @param {Object} the object to test 55 **/ 56 Simples.getConstructor = function( obj ){ 57 if( obj !== null && obj !== UNDEF ){ 58 return toString.call( obj ).replace("[object ","").replace("]",""); 59 } 60 return false; 61 }; 62 63 /** 64 * @description used to merge objects onto the first specfied object 65 * @param {Object} target native javascript object to be merged 66 * @param {Object|Array} obj1, obj2.... native javascript object or array to be merged onto first 67 **/ 68 Simples.merge = function(first /* obj1, obj2..... */ ) { 69 // if only 1 argument is passed in assume Simples is the target 70 var target = (arguments.length === 1 && !(this === WIN || this === DOC)) ? this: Simples.isConstructor( first, "Object" ) ? first : {}; 71 // set i to value based on whether there are more than 1 arguments 72 var i = arguments.length > 1 ? 1: 0; 73 // Loop over arguments 74 for (var l = arguments.length; i < l; i++) { 75 // if object apply directly to target with same keys 76 var klass = Simples.getConstructor( arguments[i] ); 77 if ( klass === "Object" ) { 78 for (var key in arguments[i]) { 79 if (hasOwn.call(arguments[i], key)) { 80 target[key] = arguments[i][key]; 81 } 82 } 83 } else if ( klass === "Array" ) { 84 // if array apply directly to target with numerical keys 85 push.apply(target, arguments[i]); 86 } 87 } 88 89 return target; 90 }; 91 92 Simples.merge( /** @lends Simples */ { 93 /** 94 * @description used to add functionality to the Simples instance object 95 * @param {Object|String} addMethods list of names of functions and the function in a opbject, when 2 arguments provided the first should be the name of the function and the function to be added. 96 */ 97 extend : function( addMethods ){ 98 // Detect whether addMethods is an object to extend onto subClass 99 var klass = Simples.getConstructor( addMethods ); 100 if( klass === "Object" ){ 101 for (var key in addMethods) { 102 if ( hasOwn.call( addMethods, key ) ) { 103 Simples.fn[key] = addMethods[key]; 104 } 105 } 106 } else if( klass === "String" && typeof arguments[1] === "function" ){ 107 Simples.fn[ arguments[0] ]= arguments[1]; 108 } 109 }, 110 /** 111 * @description used to coerce a NodeList, HTMLElementCollection or Object into Array 112 * @param {NodeList|HTMLElementCollection|Object} object to be coerced 113 * @param {Array|Object} output object to have coerced object added to 114 */ 115 makeArray : function( array, results ) { 116 results = results || []; 117 push.apply( results, slice.call( array || [], 0 ) ); 118 return results; 119 }, 120 /** 121 * @description used to check an object to see whether it is empty 122 * @param {Object} obj object to check whether is empty 123 */ 124 isEmptyObject : function( obj ) { 125 for ( var name in obj ) { return false; } 126 return true; 127 }, 128 /** 129 * @description a value to indicate whether the browser has been triggered as ready 130 */ 131 isReady : false, 132 /** 133 * @description used to add functions to browser ready queue and are triggered when DOMContentLoaded has been fired or latest window.onload 134 * @param {Function} callback the function to be fired when ready and or fired if already ready 135 */ 136 ready: function( callback ) { 137 // Attach the listeners 138 Simples.bindReady(); 139 140 // If the DOM is already ready 141 if ( Simples.isReady ) { 142 // Execute the function immediately 143 callback.call( DOC, Simples.Event( 'ready' ) ); 144 145 // Otherwise, remember the function for later 146 } else if ( readyList ) { 147 // Add the function to the wait list 148 readyList.push( callback ); 149 } 150 }, 151 /** @private Handle when the DOM is ready */ 152 readyHandler : function() { 153 // Make sure that the DOM is not already loaded 154 if ( !Simples.isReady ) { 155 // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). 156 if ( !DOC.body ) { 157 return WIN.setTimeout( Simples.readyHandler, 13 ); 158 } 159 160 // Remember that the DOM is ready 161 Simples.isReady = true; 162 163 // If there are functions bound, to execute 164 if ( readyList ) { 165 // Execute all of them 166 var fn, i = 0; 167 while ( (fn = readyList[ i++ ]) ) { 168 fn.call( DOC, Simples ); 169 } 170 171 // Reset the list of functions 172 readyList = null; 173 } 174 } 175 }, 176 /** @private To setup the event listeners for the ready event */ 177 bindReady : function(){ 178 if ( readyBound ) { return; } 179 180 readyBound = true; 181 182 // Catch cases where $(DOC).ready() is called after the 183 // browser event has already occurred. 184 if ( DOC.readyState === COMPLETE ) { 185 return Simples.readyHandler(); 186 } 187 188 // Mozilla, Opera and webkit nightlies currently support this event 189 if ( DOC.addEventListener ) { 190 // Use the handy event callback 191 DOC.addEventListener( DOMLOADED, DOMContentLoaded, false ); 192 193 // A fallback to WIN.onload, that will always work 194 WIN.addEventListener( "load", Simples.readyHandler, false ); 195 196 // If IE event model is used 197 } else if ( DOC.attachEvent ) { 198 // ensure firing before onload, 199 // maybe late but safe also for iframes 200 DOC.attachEvent( READYSTATE, DOMContentLoaded); 201 202 // A fallback to WIN.onload, that will always work 203 WIN.attachEvent( "onload", Simples.readyHandler ); 204 205 // If IE and not a frame 206 // continually check to see if the DOC is ready 207 var toplevel = false; 208 209 try { 210 toplevel = WIN.frameElement === null || WIN.frameElement === UNDEF; 211 } catch(e) {} 212 213 if ( DOC.documentElement.doScroll && toplevel ) { 214 doScrollCheck(); 215 } 216 } 217 }, 218 /** 219 * @description used to set the context on a function when the returned function is executed 220 * @param {Object} context object to use as the execution context 221 * @param {Function} func the function you want to call with the given context 222 */ 223 setContext : function( context, func ){ 224 return function(){ 225 return func.apply( context, arguments ); 226 }; 227 }, 228 /** 229 * @description Use native String.trim function wherever possible, Otherwise use our own trimming functionality 230 * @param {String} text String to trim 231 */ 232 trim : function( text ) { 233 text = text === null || text === UNDEF ? "" : text; 234 return trim ? trim.call( text ) : text.toString().replace( FIRST_SPACES, "" ).replace( LAST_SPACES, "" ); 235 }, 236 /** 237 * @description an empty function to use as noop function 238 */ 239 noop : function(){} 240 }); 241 242 // Perform a simple check to determine if the browser is capable of 243 // converting a NodeList to an array using builtin methods. 244 // Also verifies that the returned array holds DOM nodes 245 // (which is not the case in the Blackberry browser) 246 /** @ignore */ 247 try { 248 Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; 249 250 // Provide a fallback method if it does not work 251 } catch( e ) { 252 /** @ignore */ 253 Simples.makeArray = function( array, results ) { 254 array = array || []; 255 var i = 0, 256 ret = results || []; 257 258 if( Simples.isConstructor(array,"Array") ){ 259 push.apply( ret, array ); 260 } else { 261 if ( typeof array.length === "number" ) { 262 for ( var l = array.length; i < l; i++ ) { 263 ret.push( array[i] ); 264 } 265 } else { 266 for ( ; array[i]; i++ ) { 267 ret.push( array[i] ); 268 } 269 } 270 } 271 272 return ret; 273 }; 274 } 275 276 // Cleanup functions for the DOC ready method 277 /** @private */ 278 if ( DOC.addEventListener ) { 279 /** @private */ 280 DOMContentLoaded = function() { 281 DOC.removeEventListener( DOMLOADED, DOMContentLoaded, false ); 282 Simples.readyHandler(); 283 }; 284 285 } else if ( DOC.attachEvent ) { 286 /** @private */ 287 DOMContentLoaded = function() { 288 // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). 289 if ( DOC.readyState === COMPLETE ) { 290 DOC.detachEvent( READYSTATE, DOMContentLoaded ); 291 Simples.readyHandler(); 292 } 293 }; 294 } 295 296 /** @private The DOM ready check for Internet Explorer */ 297 function doScrollCheck() { 298 if ( Simples.isReady ) { 299 return; 300 } 301 302 try { 303 // If IE is used, use the trick by Diego Perini 304 // http://javascript.nwbox.com/IEContentLoaded/ 305 DOC.documentElement.doScroll(LEFT); 306 } catch(e) { 307 WIN.setTimeout( doScrollCheck, 1 ); 308 return; 309 } 310 311 // and execute any waiting functions 312 Simples.readyHandler(); 313 } 314 315 /** 316 * @namespace Simples.fn 317 * @description the instance of a Simples object functions / instance methods 318 */ 319 Simples.fn = Simples.prototype = { 320 /** @ignore */ 321 constructor : Simples, 322 /** 323 * @constructs 324 * @description to initialize the Simples constructor 325 * @param {String|Element} selector Query String to find in the DOM and add to Simples instance or the Element use as the selected object 326 * @param {Object} context The document or element used to do the query on 327 * @returns {Simples} the simples onject with the results of the selector 328 */ 329 init : function( selector, context ){ 330 331 // Handle $(""), $(null), or $(UNDEF) 332 if ( !selector ){ 333 return this; 334 } else if( selector.selector !== UNDEF ){ 335 return selector; 336 } 337 338 // Handle $(DOMElement) 339 if ( selector.nodeType || ( selector.document && selector.document.nodeType ) ) { 340 this.context = this[0] = selector; 341 this.length = 1; 342 return this; 343 } 344 345 // The body element only exists once, optimize finding it 346 if ( selector === "body" && !context ) { 347 this.context = DOC; 348 this[0] = DOC.body; 349 this.selector = "body"; 350 this.length = 1; 351 return this; 352 } 353 354 var klass = Simples.getConstructor( selector ); 355 if( klass === "String" ){ 356 this.context = context; 357 this.selector = selector; 358 359 Simples.Selector( selector, context, this ); 360 361 } else if( klass === "HTMLCollection" || klass === "NodeList" || ( klass === "Array" && context === true ) ){ 362 363 Simples.makeArray( selector, this ); 364 } else { 365 Simples.makeArray( selector, this ); 366 this.filter(function(){ return !!this.nodeType || !!this.document; }); 367 } 368 return this; 369 }, 370 /** 371 * @name Simples.fn.length 372 * @description The count of items on the Simples object 373 */ 374 length : 0, 375 /** 376 * @name Simples.fn.selector 377 * @description The selector used to create the Simples object 378 */ 379 selector : "", 380 /** 381 * @name Simples.fn.version 382 * @description The version of the Simples library 383 */ 384 version : '@VERSION', 385 // For internal use only. 386 // Behaves like an Array's method, not like a Simples method. For hooking up to Sizzle. 387 /** @private */ 388 push: push, 389 /** @private */ 390 sort: [].sort, 391 /** @private */ 392 splice: [].splice 393 }; 394 395 Simples.fn.init.prototype = Simples.fn; 396 397 Simples.extend( /** @lends Simples.fn */ { 398 /** 399 * @name Simples.fn.each 400 * @function 401 * @description To loop over each item in the Simples object 402 * @param {Function} callback The function to call with each item, this is current item, arguments[ item, index, object ] 403 */ 404 each : function( callback ){ 405 var i=0,l=this.length; 406 while(i<l){ 407 callback.call( this[i], this[i], i++, this ); 408 } 409 return this; 410 }, 411 /** 412 * @name Simples.fn.filter 413 * @function 414 * @description To filter the selected elements on the Simples object 415 * @param {Function} testFn The function to call with each item, this is current item, arguments[ item, index, object ], need to return true from callback to retain element all other return values will remove the element 416 */ 417 filter : function( testFn ){ 418 var i = 0,c = 0,l = this.length; 419 while( i<l ){ 420 if( testFn.call( this[c], this[c], i, this ) !== true ){ 421 this.splice( c--, 1 ); 422 } 423 c++; i++; 424 } 425 return this; 426 }, 427 /** 428 * @name Simples.fn.find 429 * @function 430 * @description used to find elements off of the elements currently on the Simples object 431 * @param {String} selector Selector string to find elements 432 */ 433 find: function( selector ){ 434 var results = Simples(), i=0,l=this.length; 435 while(i<l){ 436 Simples.Selector( selector, this[i++], results ); 437 } 438 return results; 439 }, 440 /** 441 * @name Simples.fn.add 442 * @function 443 * @description used to add more elements to the current Simples object 444 * @param {Elements} elems An array or Simples object of elements to concatenate to the current simples Object 445 */ 446 add : function( elems ){ 447 Simples.makeArray( Simples( elems ), this ); 448 return this; 449 }, 450 /** 451 * @name Simples.fn.makeArray 452 * @function 453 * @description convert the current Simples object into an Array 454 */ 455 makeArray : function(){ 456 return Simples.makeArray( this ); 457 } 458 });