1 var STRIP_TAB_NEW_LINE = /\n|\t|\r/g, 2 SINGLE_ARG_READ = /^outer$|^inner$|^text$/, 3 IMMUTABLE_ATTR = /(button|input)/i, 4 SPECIAL_URL = /href|src|style/, 5 VALID_ELEMENTS = /^<([A-Z][A-Z0-9]*)([^>]*)>(.*)<\/\1>/i, 6 SPLIT_ATTRIBUTE = /([A-Z]*\s*=\s*['|"]?[A-Z0-9:;#\s]*['|"]?)/i, 7 TAG_LIST = {'UL':'LI','DL':'DT','TR':'TD'}, 8 QUOTE_MATCHER = /(["']?)/g, 9 /** 10 * @private - Borrowed from XUI project 11 * Wraps the HTML in a TAG, Tag is optional. If the html starts with a Tag, it will wrap the context in that tag. 12 */ 13 wrapHelper = function(xhtml, el) { 14 // insert into documentFragment to ensure insert occurs without messing up order 15 if( xhtml.toString === UNDEF || xhtml.toString().indexOf("[object ") > -1 || ( xhtml && !!xhtml.nodeType ) ){ 16 if( xhtml && xhtml.length !== UNDEF ){ 17 var docFrag = DOC.createDocumentFragment(); 18 xhtml = Simples.makeArray( xhtml, 0 ); 19 for(var p=0,r=xhtml.length;p<r;p++){ 20 docFrag.appendChild( xhtml[p] ); 21 } 22 xhtml = docFrag; 23 } 24 25 return xhtml; 26 } 27 var attributes = {}, element, x, i = 0, attr, node, attrList, result, tag; 28 xhtml = "" + xhtml; 29 if ( VALID_ELEMENTS.test(xhtml) ) { 30 result = VALID_ELEMENTS.exec(xhtml); 31 tag = result[1]; 32 33 // if the node has any attributes, convert to object 34 if (result[2] !== "") { 35 attrList = result[2].split( SPLIT_ATTRIBUTE ); 36 37 for (var l=attrList.length; i < l; i++) { 38 attr = Simples.trim( attrList[i] ); 39 if (attr !== "" && attr !== " ") { 40 node = attr.split('='); 41 attributes[ node[0] ] = node[1].replace( QUOTE_MATCHER, ""); 42 } 43 } 44 } 45 xhtml = result[3]; 46 } else { 47 tag = (el.firstChild === null) ? TAG_LIST[el.tagName] || el.tagName : el.firstChild.tagName; 48 } 49 50 element = DOC.createElement(tag); 51 52 for( x in attributes ){ 53 Simples.attr( element, x, attributes[x] ); 54 } 55 56 element.innerHTML = xhtml; 57 return element; 58 }; 59 60 Simples.merge( /** @lends Simples */ { 61 /** 62 * @description to read the html from a elem 63 * @param {Element} elem the element to read the dom html from 64 * @param {String} location to specify how to return the dom options are [ outer, text, inner/undefined ] use outer for outerHTML, text to read all the textNodes and inner or no argument for innerHTML 65 */ 66 domRead : function( elem, location ){ 67 if( elem && elem.nodeType ){ 68 switch( location ){ 69 case "outer" : 70 html = elem.outerHTML; 71 72 if ( !html ) { 73 var div = elem.ownerDocument.createElement("div"); 74 div.appendChild( elem.cloneNode(true) ); 75 html = div.innerHTML; 76 } 77 78 return html; 79 case "text" : 80 var str = "", elems = elem.childNodes; 81 for ( var i = 0; elems[i]; i++ ) { 82 elem = elems[i]; 83 84 // Get the text from text nodes and CDATA nodes 85 if ( elem.nodeType === 3 || elem.nodeType === 4 ) { 86 str += elem.nodeValue; 87 // Traverse everything else, except comment nodes 88 } else if ( elem.nodeType !== 8 ) { 89 str += Simples.domRead( elem, "text" ); 90 } 91 } 92 return str; 93 default : 94 return elem.innerHTML; 95 } 96 } 97 }, 98 /** 99 * @description to write the dom new html string or dom elements 100 * @param {Element} elem the element to read the dom html from 101 * @param {String} location to specify how to return the dom options are desctructive: [remove, empty, outer, text, inner/undefined ], non-destructive: [top, bottom, unwrap, before, after, wrap ] 102 * @param {String|Elements} html the string or Elements to put into the dom 103 */ 104 domManip : function( elem, location, html ){ 105 var el, parent = elem.parentNode; 106 if( !elem || !elem.nodeType ){ return; } 107 108 switch( location ){ 109 case 'text' : 110 Simples.cleanData( elem ); 111 while ( elem.firstChild ) { 112 elem.removeChild( elem.firstChild ); 113 } 114 elem.appendChild( (elem && elem.ownerDocument || DOC).createTextNode( html.toString() ) ); 115 break; 116 case 'remove' : 117 if( parent ){ 118 Simples.cleanData( elem ); 119 parent.removeChild(elem); 120 } 121 break; 122 default : 123 if( elem.nodeType === 3 || elem.nodeType === 8 ){ 124 return; 125 } 126 switch( location ){ 127 case 'outer' : 128 if( parent ){ 129 el = wrapHelper(html, elem); 130 Simples.cleanData( elem ); 131 parent.replaceChild( el, elem ); 132 } 133 break; 134 case 'top' : 135 elem.insertBefore( wrapHelper(html, elem), elem.firstChild); 136 break; 137 case 'bottom' : 138 elem.insertBefore( wrapHelper(html, elem), null); 139 break; 140 case 'unwrap' : 141 if( parent ){ 142 var docFrag = wrapHelper( elem.childNodes, elem ); 143 Simples.cleanData( elem ); 144 el = docFrag.childNodes; 145 parent.insertBefore( docFrag, elem ); 146 parent.removeChild( elem ); 147 } 148 break; 149 case 'empty' : 150 Simples.cleanData( elem, false ); 151 while ( elem.firstChild ) { 152 elem.removeChild( elem.firstChild ); 153 } 154 break; 155 case 'before' : 156 if( parent ){ 157 parent.insertBefore( wrapHelper(html, parent), elem); 158 } 159 break; 160 case 'after' : 161 if( parent ){ 162 parent.insertBefore( wrapHelper(html, parent), elem.nextSibling); 163 } 164 break; 165 case 'wrap' : 166 if( parent ){ 167 var elems = wrapHelper( html, parent ); 168 var wrap = ( elems.nodeType === 11 ? elems.firstChild : elems ); 169 parent.insertBefore( elems, elem ); 170 wrap.appendChild( elem ); 171 } 172 break; 173 default : 174 Simples.cleanData( this, false ); 175 html = html != null ? html : location; 176 var list, len, i = 0, testStringIndex = html.toString().indexOf("[object"); 177 if ( testStringIndex === -1 ) { 178 elem.innerHTML = ""+html; 179 list = elem.getElementsByTagName('SCRIPT'); 180 len = list.length; 181 for (; i < len; i++) { 182 eval(list[i].text); 183 } 184 } else if( testStringIndex > -1 ) { 185 elem.innerHTML = ""; 186 elem.appendChild( wrapHelper( html, elem ) ); 187 } 188 } 189 } 190 return el; 191 }, 192 /** 193 * @description to either check for a className, add or remove a className 194 * @param {Element} elem the element to manipulate the className on 195 * @param {String} className the class to work with 196 * @param {String} action to perform the step [ add, remove, has/undefined ] 197 */ 198 className : function( elem, className, action ){ 199 if( elem && elem.nodeType && elem.nodeType != ( 3 || 8 ) ){ 200 className = " "+className+" "; 201 var hasClassName = (" " + elem.className + " ").replace( STRIP_TAB_NEW_LINE, " ").indexOf( className ) > -1; 202 if( action === "add" ){ 203 if( !hasClassName ){ 204 elem.className = Simples.trim( Simples.trim( elem.className.replace( STRIP_TAB_NEW_LINE, " ") ) + className ); 205 } 206 } else if( action === "remove" ){ 207 if( hasClassName ){ 208 elem.className = Simples.trim( (' ' + elem.className.replace( STRIP_TAB_NEW_LINE, " ") +' ').replace( className, ' ' ) ); 209 } 210 } else { 211 return hasClassName; 212 } 213 } 214 }, 215 /** 216 * @description read / write the attribute on an element 217 * @param {Element} elem the element to manipulate the attribute 218 * @param {String} name the name of the attribute 219 * @param {String} value the value to specify, if undefined will read the attribute, if null will remove the attribute, else will add the value as a string 220 */ 221 attr : function( elem, name, value ){ 222 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { 223 return UNDEF; 224 } 225 226 if( value === UNDEF ){ 227 if ( elem.nodeName.toUpperCase() === "FORM" && elem.getAttributeNode(name) ) { 228 // browsers index elements by id/name on forms, give priority to attributes. 229 return elem.getAttributeNode( name ).nodeValue; 230 } else if ( name === "style" && !Simples.support.style ){ 231 // get style correctly 232 return elem.style.cssText; 233 } else if( elem.nodeType === 1 && !SPECIAL_URL.test( name ) && name in elem ){ 234 // These attributes don't require special treatment 235 return elem[ name ]; 236 } else { 237 // it must be this 238 return elem.getAttribute( name ); 239 } 240 return null; 241 } else if( value === null ){ 242 if ( elem.nodeType === 1 ) { 243 elem.removeAttribute( name ); 244 } 245 } else { 246 if ( ( typeof value == ( "function" || 'object' ) ) || ( name === "type" && IMMUTABLE_ATTR.test( elem.nodeName ) ) ) { 247 return UNDEF; 248 } 249 250 if( name === "style" && !Simples.support.style ){ 251 // get style correctly 252 elem.style.cssText = "" + value; 253 } else if ( elem.nodeType === 1 && !SPECIAL_URL.test( name ) && name in elem ) { 254 // These attributes don't require special treatment 255 elem[ name ] = ""+value; 256 } else { 257 // it must be this 258 elem.setAttribute(name, ""+value); 259 } 260 } 261 } 262 }); 263 264 Simples.extend( /** @lends Simples.fn */ { 265 /** 266 * @description to read or write to the dom basd on the elements on the Simples object 267 * @param {String} location to specify how to return the dom options are desctructive: [remove, empty, outer, text, inner/undefined ], non-destructive: [top, bottom, unwrap, before, after, wrap ] 268 * @param {String|Elements} html the string or Elements to put into the dom, if not specfied where location is [ outer, text, inner/undefined ] will read 269 * @returns {Simples|String} if writing to the dom will return this, else will return string of dom 270 */ 271 html : function( location, html ){ 272 273 if ( arguments.length === 0 || ( arguments.length === 1 && SINGLE_ARG_READ.test( location ) ) ) { 274 return Simples.domRead( this[0], location ); 275 } 276 location = location != null ? location : ""; 277 278 var c=0,i=0,l=this.length, results; 279 while(i<l){ 280 Simples.domManip( this[i++], location, html ); 281 } 282 283 return this; 284 }, 285 /** 286 * @description to determine whether any of the elements on the Simples object has the specified className 287 * @params {String} className the exact className to test for 288 * @returns {Boolean} indicating whether className is on elements of Simples object 289 */ 290 hasClass : function( className ){ 291 for ( var i = 0, l = this.length; i < l; i++ ) { 292 if ( Simples.className( this[i], className ) ) { 293 return true; 294 } 295 } 296 return false; 297 }, 298 /** 299 * @description to add the specified className to the elements on the Simples object with the specified className 300 * @params {String} className the className to add to the elements 301 */ 302 addClass : function( className ){ 303 var l = this.length; 304 while ( l ) { 305 Simples.className( this[ --l ], className, "add" ); 306 } 307 return this; 308 }, 309 /** 310 * @description to remove the specified className to the elements on the Simples object with the specified className 311 * @params {String} className the className to remove to the elements 312 */ 313 removeClass : function( className ){ 314 var l = this.length; 315 while ( l ) { 316 Simples.className( this[ --l ], className, "remove" ); 317 } 318 return this; 319 }, 320 /** 321 * @description to read / write the given attribute to the elements on the Simples object 322 * @param {String} name the name of the attribute 323 * @param {String} value the value to specify, if undefined will read the attribute, if null will remove the attribute, else will add the value as a string 324 */ 325 attr : function(name, value){ 326 var klass = Simples.getConstructor( name ); 327 328 if( klass === "Object" ){ 329 for( var key in name ){ 330 var i=0,l=this.length,val = name[key]; 331 while(i<l){ 332 Simples.attr( this[i++], key, val ); 333 } 334 } 335 } else if( klass === "String" ){ 336 if( value === UNDEF ){ 337 return Simples.attr( this[0], name ); 338 } else { 339 for(var m=0,n=this.length;m<n;m++){ 340 Simples.attr( this[m], name, value ); 341 } 342 } 343 } 344 return this; 345 }, 346 /* TODO: Rename me as I don't indicate functionality */ 347 /** 348 * @description to select a new set of elements off of the elements in the Simples object 349 * @params {String|Function} name the string to specify the traversing, i.e. childNodes, parentNode, etc or a function to walk 350 */ 351 traverse : function( name ){ 352 var isWhat = Simples.getConstructor( name ), results = new Simples(), i=0,l = this.length; 353 while( i<l ){ 354 var current = this[i++], elem = ( isWhat === "String" ) ? current[ name ] : ( isWhat === "Function" ) ? name.call( current, current ) : null; 355 if( elem ){ 356 results.push.apply( results, ( elem.item || elem.length ) ? Simples.makeArray( elem ) : [ elem ] ); 357 } 358 } 359 360 return results; 361 }, 362 /** 363 * @description to return a subset of the selected elements 364 * @params {Number} i the first element to start slicing 365 * @params {Number} len the last element to finish slicing this is optional if not specified then the slice is to the last element 366 */ 367 slice : function( i, len ){ 368 len = ( 0 < len ) ? len : 1 ; 369 return Simples( slice.apply( this, i < 0 ? [ i ] : [+i, i+len] ), true ); 370 } 371 });