1/** 2 * Utopia: A JavaScript util library that assumes modern standards support and doesn't fix any browser bugs 3 * @author Lea Verou (http://lea.verou.me) 4 * MIT license (http://www.opensource.org/licenses/mit-license.php) 5 * Last update: 2012-4-29 6 */ 7 8function $(expr, con) { 9 return typeof expr === 'string'? (con || document).querySelector(expr) : expr; 10} 11 12function $$(expr, con) { 13 return Array.prototype.slice.call((con || document).querySelectorAll(expr)); 14} 15 16// Make each ID a global variable 17// Many browsers do this anyway (itâs in the HTML5 spec), so it ensures consistency 18$$('[id]').forEach(function(element) { window[element.id] = element; }); 19 20// Array#splice but for strings 21String.prototype.splice = function(i, remove, add) { 22 remove = +remove || 0; 23 add = add || ''; 24 25 return this.slice(0,i) + add + this.slice(i + remove); 26}; 27 28(function(){ 29 30var _ = window.Utopia = { 31 /** 32 * Returns the [[Class]] of an object in lowercase (eg. array, date, regexp, string etc) 33 * Caution: Results for DOM elements and collections aren't reliable. 34 * @param {Object} obj 35 * 36 * @return {String} 37 */ 38 type: function(obj) { 39 if(obj === null) { return 'null'; } 40 41 if(obj === undefined) { return 'undefined'; } 42 43 var ret = Object.prototype.toString.call(obj).match(/^\[object\s+(.*?)\]$/)[1]; 44 45 ret = ret? ret.toLowerCase() : ''; 46 47 if(ret == 'number' && isNaN(obj)) { 48 return 'NaN'; 49 } 50 51 return ret; 52 }, 53 54 /** 55 * Iterate over the properties of an object. Checks whether the properties actually belong to it. 56 * Can be stopped if the function explicitly returns a value that isn't null, undefined or NaN. 57 * 58 * @param obj {Object} The object to iterate over 59 * @param func {Function} The function used in the iteration. Can accept 2 parameters: one of the 60 * value of the object and one for its name. 61 * @param context {Object} Context for the above function. Default is the object being iterated. 62 * 63 * @return {Object} Null or the return value of func, if it broke the loop at some point. 64 */ 65 each: function(obj, func, context) { 66 if(!_.type(func) == 'function') { 67 throw Error('The second argument in Utopia.each() must be a function'); 68 }; 69 70 context = context || obj; 71 72 for (var i in obj) { 73 if(obj.hasOwnProperty && obj.hasOwnProperty(i)) { 74 var ret = func.call(context, obj[i], i); 75 76 if(!!ret || ret === 0 || ret === '') { 77 return ret; 78 } 79 } 80 } 81 82 return null; 83 }, 84 85 /** 86 * Copies the properties of one object onto another. 87 * When there is a collision, the later one wins 88 * 89 * @return {Object} destination object 90 */ 91 merge: function(objects) { 92 var ret = {}; 93 94 for(var i=0; i<arguments.length; i++) { 95 var o = arguments[i]; 96 97 for(var j in o) { 98 ret[j] = o[j]; 99 } 100 } 101 102 return ret; 103 }, 104 105 /** 106 * Copies the properties of one or more objects onto the first one 107 * When there is a collision, the first object wins 108 */ 109 attach: function(object, objects) { 110 for(var i=0; i<arguments.length; i++) { 111 var o = arguments[i]; 112 113 for(var j in o) { 114 if(!(j in object)) { 115 object[j] = o[j]; 116 } 117 } 118 } 119 120 return object; 121 }, 122 123 element: { 124 /** 125 * Creates a new DOM element 126 * @param options {Object} A set of key/value pairs for attributes, properties, contents, placement in the DOM etc 127 * @return The new DOM element 128 */ 129 create: function() { 130 var options; 131 132 if(_.type(arguments[0]) === 'string') { 133 if(_.type(arguments[1]) === 'object') { 134 // Utopia.element.create('div', { ... }); 135 options = arguments[1]; 136 options.tag = arguments[0]; 137 } 138 else { 139 // Utopia.element.create('div', ...); 140 options = { 141 tag: arguments[0] 142 }; 143 144 // Utopia.element.create('div', [contents]); 145 if(_.type(arguments[1]) === 'array') { 146 options.contents = arguments[1]; 147 } 148 // Utopia.element.create('div', 'Text contents'); 149 else if(_.type(arguments[1]) === 'string' || _.type(arguments[1]) === 'number') { 150 options.contents = ['' + arguments[1]]; 151 } 152 } 153 } 154 else { 155 options = arguments[0]; 156 } 157 158 var namespace = options.namespace || '', element; 159 160 if(namespace) { 161 element = document.createElementNS(namespace, options.tag); 162 } 163 else { 164 element = document.createElement(options.tag); 165 } 166 167 if(options.className) { 168 options.properties = options.properties || {}; 169 options.properties.className = options.className; 170 } 171 172 // Set properties, attributes and contents 173 _.element.set(element, options); 174 175 // Place the element in the DOM (inside, before or after an existing element) 176 // This could be a selector 177 if(options.inside) { 178 $(options.inside).appendChild(element); 179 } 180 else if(options.before) { 181 var before = $(options.before); 182 183 before.parentNode.insertBefore(element, before) 184 } 185 else if(options.after) { 186 var after = $(options.after); 187 188 after.parentNode.insertBefore(element, after.nextSibling) 189 } 190 191 return element; 192 }, 193 194 set: function(element, options) { 195 _.element.prop(element, options.properties || options.prop); 196 197 _.element.attr(element, options.attributes || options.attr); 198 199 _.element.contents(element, options.contents); 200 201 return element; 202 }, 203 204 prop: function (element, properties) { 205 if (properties) { 206 for (var prop in properties) { 207 try { // Bloody IE <= 9 208 element[prop] = properties[prop]; 209 } 210 catch(e) {} 211 } 212 } 213 214 return element; 215 }, 216 217 attr: function (element, attributes) { 218 if (attributes) { 219 for (attr in attributes) { 220 element.setAttribute(attr, attributes[attr]); 221 } 222 } 223 224 return element; 225 }, 226 227 /** 228 * Sets an elementâs contents 229 * Contents could be: One or multiple (as an array) of the following: 230 * - An object literal that will be passed through Utopia.element.create 231 * - A string or number, which will become a text node 232 * - An existing DOM element 233 */ 234 contents: function (element, contents, where) { 235 if(contents || contents === 0) { 236 if (_.type(contents) !== 'array') { 237 contents = [contents]; 238 } 239 240 var firstChild = element.firstChild; 241 242 for (var i=0; i<contents.length; i++) { 243 var content = contents[i], child; 244 245 switch(_.type(content)) { 246 case 'string': 247 if(content === '') { 248 continue; 249 } 250 // fall through 251 case 'number': 252 child = document.createTextNode(content); 253 break; 254 case 'object': 255 child = _.element.create(content); 256 break; 257 default: 258 child = content; 259 260 } 261 262 if(child) { 263 if (!where || where === 'end') { 264 element.appendChild(child); 265 } 266 else if (where === 'start') { 267 element.insertBefore(child, firstChild); 268 } 269 } 270 } 271 } 272 273 return element; 274 } 275 }, 276 277 elements: { 278 // set, attr, prop, contents functions from Utopia.element, but for multiple elements 279 }, 280 281 event: { 282 /** 283 * Binds one or more events to one or more elements 284 */ 285 bind: function(target, event, callback, traditional) { 286 if(_.type(target) === 'string' || _.type(target) === 'array') { 287 var elements = _.type(target) === 'string'? $$(target) : target; 288
289 elements.forEach(function(element) { 290 _.event.bind(element, event, callback, traditional); 291 }); 292 } 293 else if(_.type(event) === 'string') { 294 if(traditional) { 295 target['on' + event] = callback; 296 } 297 else { 298 target.addEventListener(event, callback, false); 299 } 300 } 301 else if(_.type(event) === 'array') { 302 for (var i=0; i<event.length; i++) { 303 _.event.bind(target, event[i], callback, arguments[2]); 304 } 305 } 306 else { 307 for (var name in event) { 308 _.event.bind(target, name, event[name], arguments[2]); 309 } 310 } 311 }, 312 313 /** 314 * Fire a custom event 315 */ 316 fire: function(target, type, properties) { 317 if(_.type(target) === 'string' || _.type(target) === 'array') { 318 var elements = _.type(target) === 'string'? $$(target) : target; 319 320 elements.forEach(function(element) { 321 _.event.fire(element, type, properties); 322 }); 323 } 324 else { 325 var evt = document.createEvent("HTMLEvents"); 326 327 evt.initEvent(type, true, true ); 328 evt.custom = true; 329 330 if(properties) { 331 _.attach(evt, properties); 332 } 333 334 target.dispatchEvent(evt); 335 } 336 } 337 }, 338 339 /** 340 * Helper for XHR requests 341 */ 342 xhr: function(o) { 343 document.body.setAttribute('data-loading', ''); 344 345 var xhr = new XMLHttpRequest(), 346 method = o.method || 'GET', 347 data = o.data || ''; 348 349 o.headers = o.headers || {}; 350 351 xhr.open(method, o.url + (method === 'GET' && data? '?' + data : ''), !o.sync); 352 353 if (method !== 'GET' && !o.headers['Content-type'] && !o.headers['Content-Type']) { 354 xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 355 } 356 357 for (var header in o.headers) { 358 xhr.setRequestHeader(header, o.headers[header]); 359 } 360 361 var callback = function() { 362 document.body.removeAttribute('data-loading'); 363 364 o.callback(xhr); 365 }; 366 367 xhr.onreadystatechange = function(){ 368 if (xhr.readyState === 4) { 369 callback(); 370 } 371 }; 372 373 xhr.send(method === 'GET'? null : data); 374 375 return xhr; 376 }, 377 378 /** 379 * Lazy loads an external script 380 */ 381 script: function(url, callback, doc) { 382 doc = doc || document; 383 384 return _.element.create('script', { 385 properties: { 386 src: url, 387 async: true, 388 onload: callback 389 }, 390 inside: doc.documentElement 391 }); 392 }, 393 394 /** 395 * Returns the absolute X, Y offsets for an element 396 */ 397 offset: function(element) { 398 var left = 0, top = 0, el = element; 399 400 if (el.parentNode) { 401 do { 402 left += el.offsetLeft; 403 top += el.offsetTop; 404 } while ((el = el.offsetParent) && el.nodeType < 9); 405 406 el = element; 407 408 do { 409 left -= el.scrollLeft; 410 top -= el.scrollTop; 411 } while ((el = el.parentNode) && !/body/i.test(el.nodeName)); 412 } 413 414 return { 415 top: top, 416 right: innerWidth - left - element.offsetWidth, 417 bottom: innerHeight - top - element.offsetHeight, 418 left: left, 419 }; 420 } 421}; 422 423['set', 'prop', 'attr', 'contents'].forEach(function(method) { 424 _.elements[method] = function(elements) { 425 elements = _.type(elements) === 'string'? $$(elements) : Array.prototype.slice.call(elements); 426 427 var args = Array.prototype.slice.call(arguments); 428 args.shift(); // Remove the elements argument 429 430 elements = elements.map(function(element) { 431 return _.element[method](element, args); 432 }); 433 434 return elements; 435 } 436}); 437 438})(); 439 440window.$u = window.$u || Utopia;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.