1 /* 2 * classList.js: Cross-browser full element.classList implementation. 3 * 2011-06-15 4 * 5 * By Eli Grey, http://eligrey.com 6 * Public Domain. 7 * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 */ 9 10 if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) 11 { 12 (function (view){ 13 14 var classListProp = "classList", 15 protoProp = "prototype", 16 elemCtrProto = (view.HTMLElement || view.Element)[protoProp], 17 objCtr = Object, 18 strTrim = String[protoProp].trim || 19 function () 20 { 21 return this.replace(/^\s+|\s+$/g, ""); 22 }, 23 arrIndexOf = Array[protoProp].indexOf || 24 function (item) 25 { 26 var 27 i = 0, 28 len = this.length; 29 for (; i < len; i++) 30 { 31 if (i in this && this[i] === item) 32 { 33 return i; 34 } 35 } 36 return -1; 37 } 38 // Vendors: please allow content code to instantiate DOMExceptions 39 , 40 /** 41 * @private 42 */ 43 DOMEx = function (type, message) 44 { 45 this.name = type; 46 this.code = DOMException[type]; 47 this.message = message; 48 }, 49 /** 50 * @private 51 */ 52 checkTokenAndGetIndex = function (classList, token) 53 { 54 if (token === "") 55 { 56 throw new DOMEx("SYNTAX_ERR", "An invalid or illegal string was specified"); 57 } 58 if (/\s/.test(token)) 59 { 60 throw new DOMEx("INVALID_CHARACTER_ERR", "String contains an invalid character"); 61 } 62 return arrIndexOf.call(classList, token); 63 }, 64 /** 65 * @private 66 */ 67 ClassList = function (elem) 68 { 69 var 70 trimmedClasses = strTrim.call(elem.className), 71 classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [], 72 i = 0, 73 len = classes.length; 74 for (; i < len; i++) 75 { 76 this.push(classes[i]); 77 } 78 this._updateClassName = function () 79 { 80 elem.className = this.toString(); 81 }; 82 }, 83 classListProto = ClassList[protoProp] = [], 84 /** 85 * @private 86 */ 87 classListGetter = function () 88 { 89 return new ClassList(this); 90 }; 91 // Most DOMException implementations don't allow calling DOMException's toString() 92 // on non-DOMExceptions. Error's toString() is sufficient here. 93 DOMEx[protoProp] = Error[protoProp]; 94 classListProto.item = function (i) 95 { 96 return this[i] || null; 97 }; 98 classListProto.contains = function (token) 99 { 100 token += ""; 101 return checkTokenAndGetIndex(this, token) !== -1; 102 }; 103 classListProto.add = function (token) 104 { 105 token += ""; 106 if (checkTokenAndGetIndex(this, token) === -1) 107 { 108 this.push(token); 109 this._updateClassName(); 110 } 111 }; 112 classListProto.remove = function (token) 113 { 114 token += ""; 115 var index = checkTokenAndGetIndex(this, token); 116 if (index !== -1) 117 { 118 this.splice(index, 1); 119 this._updateClassName(); 120 } 121 }; 122 classListProto.toggle = function (token) 123 { 124 token += ""; 125 if (checkTokenAndGetIndex(this, token) === -1) 126 { 127 this.add(token); 128 } 129 else 130 { 131 this.remove(token); 132 } 133 }; 134 classListProto.toString = function () 135 { 136 return this.join(" "); 137 }; 138 139 if (objCtr.defineProperty) 140 { 141 var classListPropDesc = { 142 get: classListGetter, 143 enumerable: true, 144 configurable: true 145 }; 146 try 147 { 148 objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 149 } 150 catch (ex) 151 { // IE 8 doesn't support enumerable:true 152 if (ex.number === -0x7FF5EC54) 153 { 154 classListPropDesc.enumerable = false; 155 objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 156 } 157 } 158 } 159 else if (objCtr[protoProp].__defineGetter__) 160 { 161 elemCtrProto.__defineGetter__(classListProp, classListGetter); 162 } 163 164 }(self)); 165 } 166 167 // -------------------------------------------------------------------------- 168 169 (function (){ 170 171 "use strict"; 172 173 var d, tag_reg, class_reg; 174 175 tag_reg = /^([\w\-]+)$/; 176 class_reg = /\.([\w\-]+)$/; 177 178 179 //Private function for getting/setting attributes 180 function _attr(sel, name, value) 181 { 182 var oldVal, doAttr; 183 184 //Get the value of the attribute, if it exists 185 if (typeof sel.hasAttribute !== "undefined") 186 { 187 if (sel.hasAttribute(name)) 188 { 189 oldVal = sel.getAttribute(name); 190 } 191 192 doAttr = true; 193 } 194 else if (typeof sel[name] !== "undefined") 195 { 196 oldVal = sel[name]; 197 doAttr = false; 198 } 199 else if (name === "class" && typeof sel.className !== "undefined") //className attribute 200 { 201 name = "className"; 202 oldVal = sel.className; 203 doAttr = false; 204 } 205 206 //Well, I guess that attribute doesn't exist 207 if (typeof oldVal === "undefined" && (typeof value === "undefined" || value === null)) 208 { 209 console.log(value); 210 console.log(sel); 211 console.log("Element does not have the selected attribute"); 212 return; 213 } 214 215 //No value to set? Return the current value 216 if (typeof value === "undefined") 217 { 218 return oldVal; 219 } 220 221 //Determine what to do with the attribute 222 if (typeof value !== "undefined" && value !== null) 223 { 224 if(doAttr === true) 225 { 226 sel.setAttribute(name, value); 227 } 228 else 229 { 230 sel[name] = value; 231 } 232 } 233 else if (value === null) 234 { 235 if(doAttr === true) 236 { 237 sel.removeAttribute(name); 238 } 239 else 240 { 241 delete sel[name]; 242 } 243 } 244 245 return (typeof value !== "undefined") ? value : oldVal; 246 } 247 248 function _toCamel(s) 249 { 250 return s.replace(/(\-[a-z])/g, function($1){ 251 return $1.toUpperCase().replace('-',''); 252 }); 253 } 254 255 function _css(sel, prop, val) 256 { 257 var equi; 258 259 //Camel-case 260 prop = _toCamel(prop); 261 262 //Equivalent properties for 'special' browsers 263 equi = { 264 outerHeight: "offsetHeight", 265 outerWidth: "offsetWidth", 266 top: "posTop" 267 }; 268 269 270 //If you don't define a value, try returning the existing value 271 if(typeof val === "undefined" && sel.style[prop] !== "undefined") 272 { 273 return sel.style[prop]; 274 } 275 else if(typeof val === "undefined" && sel.style[equi[prop]] !== "undefined") 276 { 277 return sel.style[equi[prop]]; 278 } 279 280 //Let's try the easy way first 281 if(typeof sel.style[prop] !== "undefined") 282 { 283 sel.style[prop] = val; 284 285 //Short circuit 286 return; 287 } 288 else if(sel.style[equi[prop]]) 289 { 290 sel.style[equi[prop]] = val; 291 return; 292 } 293 294 //No matches? Well, lets log it for now 295 console.log("Property " + prop + " nor an equivalent seems to exist"); 296 } 297 298 // -------------------------------------------------------------------------- 299 300 /** 301 * DOM 302 * 303 * Dom manipulation module 304 * @namespace 305 * @memberOf $_ 306 * @name dom 307 */ 308 d = { 309 /** 310 * Adds a class to the element(s) specified by the current 311 * selector 312 * 313 * @name addClass 314 * @memberOf $_.dom 315 * @function 316 * @param string class 317 * @return void 318 */ 319 addClass: function (c) 320 { 321 $_.each(function (e){ 322 e.classList.add(c); 323 }); 324 }, 325 /** 326 * Removes a class from the element(s) specified by the current 327 * selector 328 * 329 * @name removeClass 330 * @memberOf $_.dom 331 * @function 332 * @param string class 333 * @return void 334 */ 335 removeClass: function (c) 336 { 337 $_.each(function (e){ 338 e.classList.remove(c); 339 }); 340 }, 341 /** 342 * Hides the element(s) specified by the current selector 343 * 344 * @name hide 345 * @memberOf $_.dom 346 * @function 347 * @return void 348 */ 349 hide: function () 350 { 351 this.css('display', 'none'); 352 }, 353 /** 354 * Shows the element(s) specified by the current selector. 355 * if type is specified, the element will have it's style 356 * property set to "display:[your type]". If type is not 357 * specified, the element is set to "display:block". 358 * 359 * @name show 360 * @memberOf $_.dom 361 * @function 362 * @param [string] type 363 * @return void 364 */ 365 show: function (type) 366 { 367 if (typeof type === "undefined") 368 { 369 type = "block"; 370 } 371 372 this.css("display", type); 373 }, 374 /** 375 * Sets attributes on element(s) specified by the current 376 * selector, or, if name is not specified, returns the 377 * value of the attribute of the element specified by the 378 * current selector. 379 * 380 * @name attr 381 * @memberOf $_.dom 382 * @function 383 * @param string name 384 * @param string value 385 * @return string 386 * @type string 387 */ 388 attr: function (name, value) 389 { 390 var sel = this.el; 391 392 //Make sure you don't try to get a bunch of elements 393 if (sel.length > 1 && typeof value === "undefined") 394 { 395 console.log(sel); 396 console.log("Must be a singular element"); 397 return; 398 } 399 else if (sel.length > 1 && typeof value !== "undefined") //You can set a bunch, though 400 { 401 $_.each(function (e){ 402 return _attr(e, name, value); 403 }); 404 } 405 else //Normal behavior 406 { 407 return _attr(sel, name, value); 408 } 409 }, 410 /** 411 * Sets or retrieves the text content of the element 412 * specified by the current selector. If a value is 413 * passed, it will set that value on the current element, 414 * otherwise it will return the value of the current element 415 * 416 * @name text 417 * @memberOf $_.util 418 * @function 419 * @param [string] value 420 * @returns string 421 * @type string 422 */ 423 text: function (value) 424 { 425 var oldValue, set, type, sel; 426 427 sel = this.el; 428 429 set = (typeof value !== "undefined") ? true : false; 430 431 type = (typeof sel.innerText !== "undefined") 432 ? "innerText" 433 : (typeof sel.textContent !== "undefined") 434 ? "textContent" 435 : "innerHTML"; 436 437 oldValue = sel[type]; 438 439 if(set) 440 { 441 sel[type] = value; 442 return value; 443 } 444 else 445 { 446 return oldValue; 447 } 448 }, 449 /** 450 * Sets or retrieves a css property of the element 451 * specified by the current selector. If a value is 452 * passed, it will set that value on the current element, 453 * otherwise it will return the value of the css property 454 * on the current element 455 * 456 * @name css 457 * @memberOf $_.util 458 * @function 459 * @param string property 460 * @param [string] value 461 * @returns string 462 * @type string 463 */ 464 css: function (prop, val) 465 { 466 //Return the current value if a value is not set 467 if(typeof val === "undefined") 468 { 469 return _css(this.el, prop); 470 } 471 472 $_.each(function (e){ 473 _css(e, prop, val); 474 }); 475 } 476 }; 477 478 $_.ext('dom', d); 479 480 }()); 481