├── Demos ├── demo.css ├── demo.js ├── index.html ├── mootools-class-extras.js ├── mootools-core.js ├── mootools.html ├── other.html └── style.css ├── LICENSE ├── README.md ├── Source ├── History.handleInitialState.js └── History.js └── package.yml /Demos/demo.css: -------------------------------------------------------------------------------- 1 | body > div > div { 2 | float: left; 3 | } 4 | 5 | div.content { 6 | margin-left: 2%; 7 | width: 80%; 8 | } 9 | 10 | div#more { 11 | width: 16%; 12 | 13 | font-weight: bold; 14 | } 15 | -------------------------------------------------------------------------------- /Demos/demo.js: -------------------------------------------------------------------------------- 1 | // If History doesn't need to handle the initial state it returns false so the code of the page can execute. 2 | // The first argument is the base path of your application. Usually it defaults to / but in the case of this 3 | // demo it likely only runs inside of a subtree. Please note that the handleState plugin is purely optional. 4 | if (!History.handleInitialState(location.pathname.substr(0, location.pathname.lastIndexOf('/') + 1))) (function(){ 5 | 6 | // This is our main handler that loads content from any URL 7 | var myURLHandler = function(url){ 8 | // Load URL via XHR 9 | 10 | new Request.HTML({ 11 | url: url, 12 | onSuccess: function(tree, elements, html){ 13 | document.body.set('html', html); // Change all content of the body with the newly loaded content 14 | update(); // Add the listeners again 15 | } 16 | }).get(); 17 | }; 18 | 19 | // Add the handler to the History change event 20 | History.addEvent('change', myURLHandler); 21 | 22 | // The listener that manages all clicks 23 | var listener = function(event){ 24 | event.preventDefault(); // Prevent following the URL 25 | 26 | var href = this.get('href'); // Get the URL 27 | History.push(href); // Push the new URL 28 | }; 29 | 30 | // Listener for the "Back" link 31 | var back = function(event){ 32 | event.preventDefault(); 33 | 34 | History.back(); // Go back 35 | }; 36 | 37 | // We use this method to update all links on the page and to attach the listener 38 | var update = function(){ 39 | // Even if we execute this method more than once, the listeners only get added once per element 40 | 41 | // Add the click listener to all anchor elements, ignore outbound links and links with the data-noxhr property 42 | document.getElements('a:not([href=#]):not([href^=http://]):not([data-noxhr])').addEvent('click', listener); 43 | 44 | // The back link 45 | document.getElement('a[href=#]').addEvent('click', back); 46 | }; 47 | 48 | // Call update initially to add the listener to all elements present on the initial page load 49 | window.addEvent('domready', update); 50 | 51 | // Handle the initial load of the page if the browser does not support pushState, check if the hash is set 52 | if (!History.hasPushState()) window.addEvent('domready', function(){ 53 | // Check if there is a hash 54 | var hash = document.location.hash.substr(1); 55 | if (!hash) return; 56 | 57 | // If the hash equals the current page, don't do anything 58 | var path = document.location.pathname.split('/'); 59 | path = path[path.length - 1]; 60 | if (hash == path) return; 61 | 62 | // Load the page specified in the hash 63 | myURLHandler(hash); 64 | }); 65 | 66 | })(); -------------------------------------------------------------------------------- /Demos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | History for MooTools 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | Back 19 | Load other.html 20 |
21 |
22 |

This demo shows you how mootools-history never actually reloads the page. In more recent browsers, it changes the URL and in older browsers it uses the hash. You can find the source of the demo here.

23 | 24 |

Click some links and check the native back/forward button in your browser.

25 | 26 |

Clicking any link will load all the content into the page via XHR. All new links will receive the listener via the "update"-method in the demo. For more advanced handling of generically changing content, see mootools-dynamic-matcher

27 | 28 |

Note that if the visitor of your website or web application does not have JavaScript activated, the page will still function normally.

29 |
30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /Demos/mootools-class-extras.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: Class.Binds 5 | 6 | description: Alternate Class.Binds Implementation 7 | 8 | authors: Scott Kyle (@appden), Christoph Pojer (@cpojer) 9 | 10 | license: MIT-style license. 11 | 12 | requires: [Core/Class, Core/Function] 13 | 14 | provides: Class.Binds 15 | 16 | ... 17 | */ 18 | 19 | Class.Binds = new Class({ 20 | 21 | $bound: {}, 22 | 23 | bound: function(name){ 24 | return this.$bound[name] ? this.$bound[name] : this.$bound[name] = this[name].bind(this); 25 | } 26 | 27 | }); 28 | -------------------------------------------------------------------------------- /Demos/mootools-core.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: Core 5 | 6 | description: The heart of MooTools. 7 | 8 | license: MIT-style license. 9 | 10 | copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/). 11 | 12 | authors: The MooTools production team (http://mootools.net/developers/) 13 | 14 | inspiration: 15 | - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php) 16 | - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php) 17 | 18 | provides: [Core, MooTools, Type, typeOf, instanceOf, Native] 19 | 20 | ... 21 | */ 22 | 23 | (function(){ 24 | 25 | this.MooTools = { 26 | version: '1.3dev', 27 | build: '0a7aeabbbac5bc23b021b4c1aa9ba722c40e303d' 28 | }; 29 | 30 | // typeOf, instanceOf 31 | 32 | var typeOf = this.typeOf = function(item){ 33 | if (item == null) return 'null'; 34 | if (item.$family) return item.$family(); 35 | 36 | if (item.nodeName){ 37 | if (item.nodeType == 1) return 'element'; 38 | if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace'; 39 | } else if (typeof item.length == 'number'){ 40 | if (item.callee) return 'arguments'; 41 | if ('item' in item) return 'collection'; 42 | } 43 | 44 | return typeof item; 45 | }; 46 | 47 | var instanceOf = this.instanceOf = function(item, object){ 48 | if (item == null) return false; 49 | var constructor = item.$constructor || item.constructor; 50 | while (constructor){ 51 | if (constructor === object) return true; 52 | constructor = constructor.parent; 53 | } 54 | return item instanceof object; 55 | }; 56 | 57 | // Function overloading 58 | 59 | var Function = this.Function; 60 | 61 | var enumerables = true; 62 | for (var i in {toString: 1}) enumerables = null; 63 | if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor']; 64 | 65 | Function.prototype.overloadSetter = function(usePlural){ 66 | var self = this; 67 | return function(a, b){ 68 | if (a == null) return this; 69 | if (usePlural || typeof a != 'string'){ 70 | for (var k in a) self.call(this, k, a[k]); 71 | if (enumerables) for (var i = enumerables.length; i--;){ 72 | k = enumerables[i]; 73 | if (a.hasOwnProperty(k)) self.call(this, k, a[k]); 74 | } 75 | } else { 76 | self.call(this, a, b); 77 | } 78 | return this; 79 | }; 80 | }; 81 | 82 | Function.prototype.overloadGetter = function(usePlural){ 83 | var self = this; 84 | return function(a){ 85 | var args, result; 86 | if (usePlural || typeof a != 'string') args = a; 87 | else if (arguments.length > 1) args = arguments; 88 | if (args){ 89 | result = {}; 90 | for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]); 91 | } else { 92 | result = self.call(this, a); 93 | } 94 | return result; 95 | }; 96 | }; 97 | 98 | Function.prototype.extend = function(key, value){ 99 | this[key] = value; 100 | }.overloadSetter(); 101 | 102 | Function.prototype.implement = function(key, value){ 103 | this.prototype[key] = value; 104 | }.overloadSetter(); 105 | 106 | // From 107 | 108 | var slice = Array.prototype.slice; 109 | 110 | Function.from = function(item){ 111 | return (typeOf(item) == 'function') ? item : function(){ 112 | return item; 113 | }; 114 | }; 115 | 116 | Array.from = function(item){ 117 | if (item == null) return []; 118 | return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item]; 119 | }; 120 | 121 | Number.from = function(item){ 122 | var number = parseFloat(item); 123 | return isFinite(number) ? number : null; 124 | }; 125 | 126 | String.from = function(item){ 127 | return item + ''; 128 | }; 129 | 130 | // hide, protect 131 | 132 | Function.implement({ 133 | 134 | hide: function(){ 135 | this.$hidden = true; 136 | return this; 137 | }, 138 | 139 | protect: function(){ 140 | this.$protected = true; 141 | return this; 142 | } 143 | 144 | }); 145 | 146 | // Type 147 | 148 | var Type = this.Type = function(name, object){ 149 | if (name){ 150 | var lower = name.toLowerCase(); 151 | var typeCheck = function(item){ 152 | return (typeOf(item) == lower); 153 | }; 154 | 155 | Type['is' + name] = typeCheck; 156 | if (object != null){ 157 | object.prototype.$family = (function(){ 158 | return lower; 159 | }).hide(); 160 | //<1.2compat> 161 | object.type = typeCheck; 162 | // 163 | } 164 | } 165 | 166 | if (object == null) return null; 167 | 168 | object.extend(this); 169 | object.$constructor = Type; 170 | object.prototype.$constructor = object; 171 | 172 | return object; 173 | }; 174 | 175 | var toString = Object.prototype.toString; 176 | 177 | Type.isEnumerable = function(item){ 178 | return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' ); 179 | }; 180 | 181 | var hooks = {}; 182 | 183 | var hooksOf = function(object){ 184 | var type = typeOf(object.prototype); 185 | return hooks[type] || (hooks[type] = []); 186 | }; 187 | 188 | var implement = function(name, method){ 189 | if (method && method.$hidden) return this; 190 | 191 | var hooks = hooksOf(this); 192 | 193 | for (var i = 0; i < hooks.length; i++){ 194 | var hook = hooks[i]; 195 | if (typeOf(hook) == 'type') implement.call(hook, name, method); 196 | else hook.call(this, name, method); 197 | } 198 | 199 | var previous = this.prototype[name]; 200 | if (previous == null || !previous.$protected) this.prototype[name] = method; 201 | 202 | if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){ 203 | return method.apply(item, slice.call(arguments, 1)); 204 | }); 205 | 206 | return this; 207 | }; 208 | 209 | var extend = function(name, method){ 210 | if (method && method.$hidden) return this; 211 | var previous = this[name]; 212 | if (previous == null || !previous.$protected) this[name] = method; 213 | return this; 214 | }; 215 | 216 | Type.implement({ 217 | 218 | implement: implement.overloadSetter(), 219 | 220 | extend: extend.overloadSetter(), 221 | 222 | alias: function(name, existing){ 223 | implement.call(this, name, this.prototype[existing]); 224 | }.overloadSetter(), 225 | 226 | mirror: function(hook){ 227 | hooksOf(this).push(hook); 228 | return this; 229 | } 230 | 231 | }); 232 | 233 | new Type('Type', Type); 234 | 235 | // Default Types 236 | 237 | var force = function(name, object, methods){ 238 | var isType = (object != Object), 239 | prototype = object.prototype; 240 | 241 | if (isType) object = new Type(name, object); 242 | 243 | for (var i = 0, l = methods.length; i < l; i++){ 244 | var key = methods[i], 245 | generic = object[key], 246 | proto = prototype[key]; 247 | 248 | if (generic) generic.protect(); 249 | 250 | if (isType && proto){ 251 | delete prototype[key]; 252 | prototype[key] = proto.protect(); 253 | } 254 | } 255 | 256 | if (isType) object.implement(prototype); 257 | 258 | return force; 259 | }; 260 | 261 | force('String', String, [ 262 | 'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search', 263 | 'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase' 264 | ])('Array', Array, [ 265 | 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 266 | 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' 267 | ])('Number', Number, [ 268 | 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision' 269 | ])('Function', Function, [ 270 | 'apply', 'call', 'bind' 271 | ])('RegExp', RegExp, [ 272 | 'exec', 'test' 273 | ])('Object', Object, [ 274 | 'create', 'defineProperty', 'defineProperties', 'keys', 275 | 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames', 276 | 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen' 277 | ])('Date', Date, ['now']); 278 | 279 | Object.extend = extend.overloadSetter(); 280 | 281 | Date.extend('now', function(){ 282 | return +(new Date); 283 | }); 284 | 285 | new Type('Boolean', Boolean); 286 | 287 | // fixes NaN returning as Number 288 | 289 | Number.prototype.$family = function(){ 290 | return isFinite(this) ? 'number' : 'null'; 291 | }.hide(); 292 | 293 | // Number.random 294 | 295 | Number.extend('random', function(min, max){ 296 | return Math.floor(Math.random() * (max - min + 1) + min); 297 | }); 298 | 299 | // forEach, each 300 | 301 | Object.extend('forEach', function(object, fn, bind){ 302 | for (var key in object){ 303 | if (object.hasOwnProperty(key)) fn.call(bind, object[key], key, object); 304 | } 305 | }); 306 | 307 | Object.each = Object.forEach; 308 | 309 | Array.implement({ 310 | 311 | forEach: function(fn, bind){ 312 | for (var i = 0, l = this.length; i < l; i++){ 313 | if (i in this) fn.call(bind, this[i], i, this); 314 | } 315 | }, 316 | 317 | each: function(fn, bind){ 318 | Array.forEach(this, fn, bind); 319 | return this; 320 | } 321 | 322 | }); 323 | 324 | // Array & Object cloning, Object merging and appending 325 | 326 | var cloneOf = function(item){ 327 | switch (typeOf(item)){ 328 | case 'array': return item.clone(); 329 | case 'object': return Object.clone(item); 330 | default: return item; 331 | } 332 | }; 333 | 334 | Array.implement('clone', function(){ 335 | var i = this.length, clone = new Array(i); 336 | while (i--) clone[i] = cloneOf(this[i]); 337 | return clone; 338 | }); 339 | 340 | var mergeOne = function(source, key, current){ 341 | switch (typeOf(current)){ 342 | case 'object': 343 | if (typeOf(source[key]) == 'object') Object.merge(source[key], current); 344 | else source[key] = Object.clone(current); 345 | break; 346 | case 'array': source[key] = current.clone(); break; 347 | default: source[key] = current; 348 | } 349 | return source; 350 | }; 351 | 352 | Object.extend({ 353 | 354 | merge: function(source, k, v){ 355 | if (typeOf(k) == 'string') return mergeOne(source, k, v); 356 | for (var i = 1, l = arguments.length; i < l; i++){ 357 | var object = arguments[i]; 358 | for (var key in object) mergeOne(source, key, object[key]); 359 | } 360 | return source; 361 | }, 362 | 363 | clone: function(object){ 364 | var clone = {}; 365 | for (var key in object) clone[key] = cloneOf(object[key]); 366 | return clone; 367 | }, 368 | 369 | append: function(original){ 370 | for (var i = 1, l = arguments.length; i < l; i++){ 371 | var extended = arguments[i] || {}; 372 | for (var key in extended) original[key] = extended[key]; 373 | } 374 | return original; 375 | } 376 | 377 | }); 378 | 379 | // Object-less types 380 | 381 | ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){ 382 | new Type(name); 383 | }); 384 | 385 | // Unique ID 386 | 387 | var UID = Date.now(); 388 | 389 | String.extend('generateUID', function(){ 390 | return (UID++).toString(36); 391 | }); 392 | 393 | //<1.2compat> 394 | 395 | var Hash = this.Hash = new Type('Hash', function(object){ 396 | if (typeOf(object) == 'hash') object = Object.clone(object.getClean()); 397 | for (var key in object) this[key] = object[key]; 398 | return this; 399 | }); 400 | 401 | Hash.implement({ 402 | 403 | forEach: function(fn, bind){ 404 | Object.forEach(this, fn, bind); 405 | }, 406 | 407 | getClean: function(){ 408 | var clean = {}; 409 | for (var key in this){ 410 | if (this.hasOwnProperty(key)) clean[key] = this[key]; 411 | } 412 | return clean; 413 | }, 414 | 415 | getLength: function(){ 416 | var length = 0; 417 | for (var key in this){ 418 | if (this.hasOwnProperty(key)) length++; 419 | } 420 | return length; 421 | } 422 | 423 | }); 424 | 425 | Hash.alias('each', 'forEach'); 426 | 427 | Object.type = Type.isObject; 428 | 429 | var Native = this.Native = function(properties){ 430 | return new Type(properties.name, properties.initialize); 431 | }; 432 | 433 | Native.type = Type.type; 434 | 435 | Native.implement = function(objects, methods){ 436 | for (var i = 0; i < objects.length; i++) objects[i].implement(methods); 437 | return Native; 438 | }; 439 | 440 | var arrayType = Array.type; 441 | Array.type = function(item){ 442 | return instanceOf(item, Array) || arrayType(item); 443 | }; 444 | 445 | this.$A = function(item){ 446 | return Array.from(item).slice(); 447 | }; 448 | 449 | this.$arguments = function(i){ 450 | return function(){ 451 | return arguments[i]; 452 | }; 453 | }; 454 | 455 | this.$chk = function(obj){ 456 | return !!(obj || obj === 0); 457 | }; 458 | 459 | this.$clear = function(timer){ 460 | clearTimeout(timer); 461 | clearInterval(timer); 462 | return null; 463 | }; 464 | 465 | this.$defined = function(obj){ 466 | return (obj != null); 467 | }; 468 | 469 | this.$each = function(iterable, fn, bind){ 470 | var type = typeOf(iterable); 471 | ((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind); 472 | }; 473 | 474 | this.$empty = function(){}; 475 | 476 | this.$extend = function(original, extended){ 477 | return Object.append(original, extended); 478 | }; 479 | 480 | this.$H = function(object){ 481 | return new Hash(object); 482 | }; 483 | 484 | this.$merge = function(){ 485 | var args = Array.slice(arguments); 486 | args.unshift({}); 487 | return Object.merge.apply(null, args); 488 | }; 489 | 490 | this.$lambda = Function.from; 491 | this.$mixin = Object.merge; 492 | this.$random = Number.random; 493 | this.$splat = Array.from; 494 | this.$time = Date.now; 495 | 496 | this.$type = function(object){ 497 | var type = typeOf(object); 498 | if (type == 'elements') return 'array'; 499 | return (type == 'null') ? false : type; 500 | }; 501 | 502 | this.$unlink = function(object){ 503 | switch (typeOf(object)){ 504 | case 'object': return Object.clone(object); 505 | case 'array': return Array.clone(object); 506 | case 'hash': return new Hash(object); 507 | default: return object; 508 | } 509 | }; 510 | 511 | // 512 | 513 | })(); 514 | 515 | 516 | /* 517 | --- 518 | 519 | name: Array 520 | 521 | description: Contains Array Prototypes like each, contains, and erase. 522 | 523 | license: MIT-style license. 524 | 525 | requires: Type 526 | 527 | provides: Array 528 | 529 | ... 530 | */ 531 | 532 | Array.implement({ 533 | 534 | invoke: function(methodName){ 535 | var args = Array.slice(arguments, 1); 536 | return this.map(function(item){ 537 | return item[methodName].apply(item, args); 538 | }); 539 | }, 540 | 541 | every: function(fn, bind){ 542 | for (var i = 0, l = this.length; i < l; i++){ 543 | if ((i in this) && !fn.call(bind, this[i], i, this)) return false; 544 | } 545 | return true; 546 | }, 547 | 548 | filter: function(fn, bind){ 549 | var results = []; 550 | for (var i = 0, l = this.length; i < l; i++){ 551 | if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]); 552 | } 553 | return results; 554 | }, 555 | 556 | clean: function(){ 557 | return this.filter(function(item){ 558 | return item != null; 559 | }); 560 | }, 561 | 562 | indexOf: function(item, from){ 563 | var len = this.length; 564 | for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){ 565 | if (this[i] === item) return i; 566 | } 567 | return -1; 568 | }, 569 | 570 | map: function(fn, bind){ 571 | var results = []; 572 | for (var i = 0, l = this.length; i < l; i++){ 573 | if (i in this) results[i] = fn.call(bind, this[i], i, this); 574 | } 575 | return results; 576 | }, 577 | 578 | some: function(fn, bind){ 579 | for (var i = 0, l = this.length; i < l; i++){ 580 | if ((i in this) && fn.call(bind, this[i], i, this)) return true; 581 | } 582 | return false; 583 | }, 584 | 585 | associate: function(keys){ 586 | var obj = {}, length = Math.min(this.length, keys.length); 587 | for (var i = 0; i < length; i++) obj[keys[i]] = this[i]; 588 | return obj; 589 | }, 590 | 591 | link: function(object){ 592 | var result = {}; 593 | for (var i = 0, l = this.length; i < l; i++){ 594 | for (var key in object){ 595 | if (object[key](this[i])){ 596 | result[key] = this[i]; 597 | delete object[key]; 598 | break; 599 | } 600 | } 601 | } 602 | return result; 603 | }, 604 | 605 | contains: function(item, from){ 606 | return this.indexOf(item, from) != -1; 607 | }, 608 | 609 | append: function(array){ 610 | this.push.apply(this, array); 611 | return this; 612 | }, 613 | 614 | getLast: function(){ 615 | return (this.length) ? this[this.length - 1] : null; 616 | }, 617 | 618 | getRandom: function(){ 619 | return (this.length) ? this[Number.random(0, this.length - 1)] : null; 620 | }, 621 | 622 | include: function(item){ 623 | if (!this.contains(item)) this.push(item); 624 | return this; 625 | }, 626 | 627 | combine: function(array){ 628 | for (var i = 0, l = array.length; i < l; i++) this.include(array[i]); 629 | return this; 630 | }, 631 | 632 | erase: function(item){ 633 | for (var i = this.length; i--;){ 634 | if (this[i] === item) this.splice(i, 1); 635 | } 636 | return this; 637 | }, 638 | 639 | empty: function(){ 640 | this.length = 0; 641 | return this; 642 | }, 643 | 644 | flatten: function(){ 645 | var array = []; 646 | for (var i = 0, l = this.length; i < l; i++){ 647 | var type = typeOf(this[i]); 648 | if (type == 'null') continue; 649 | array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]); 650 | } 651 | return array; 652 | }, 653 | 654 | pick: function(){ 655 | for (var i = 0, l = this.length; i < l; i++){ 656 | if (this[i] != null) return this[i]; 657 | } 658 | return null; 659 | }, 660 | 661 | hexToRgb: function(array){ 662 | if (this.length != 3) return null; 663 | var rgb = this.map(function(value){ 664 | if (value.length == 1) value += value; 665 | return value.toInt(16); 666 | }); 667 | return (array) ? rgb : 'rgb(' + rgb + ')'; 668 | }, 669 | 670 | rgbToHex: function(array){ 671 | if (this.length < 3) return null; 672 | if (this.length == 4 && this[3] == 0 && !array) return 'transparent'; 673 | var hex = []; 674 | for (var i = 0; i < 3; i++){ 675 | var bit = (this[i] - 0).toString(16); 676 | hex.push((bit.length == 1) ? '0' + bit : bit); 677 | } 678 | return (array) ? hex : '#' + hex.join(''); 679 | } 680 | 681 | }); 682 | 683 | //<1.2compat> 684 | 685 | Array.alias('extend', 'append'); 686 | 687 | var $pick = function(){ 688 | return Array.from(arguments).pick(); 689 | }; 690 | 691 | // 692 | 693 | 694 | /* 695 | --- 696 | 697 | name: String 698 | 699 | description: Contains String Prototypes like camelCase, capitalize, test, and toInt. 700 | 701 | license: MIT-style license. 702 | 703 | requires: Type 704 | 705 | provides: String 706 | 707 | ... 708 | */ 709 | 710 | String.implement({ 711 | 712 | test: function(regex, params){ 713 | return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this); 714 | }, 715 | 716 | contains: function(string, separator){ 717 | return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1; 718 | }, 719 | 720 | trim: function(){ 721 | return this.replace(/^\s+|\s+$/g, ''); 722 | }, 723 | 724 | clean: function(){ 725 | return this.replace(/\s+/g, ' ').trim(); 726 | }, 727 | 728 | camelCase: function(){ 729 | return this.replace(/-\D/g, function(match){ 730 | return match.charAt(1).toUpperCase(); 731 | }); 732 | }, 733 | 734 | hyphenate: function(){ 735 | return this.replace(/[A-Z]/g, function(match){ 736 | return ('-' + match.charAt(0).toLowerCase()); 737 | }); 738 | }, 739 | 740 | capitalize: function(){ 741 | return this.replace(/\b[a-z]/g, function(match){ 742 | return match.toUpperCase(); 743 | }); 744 | }, 745 | 746 | escapeRegExp: function(){ 747 | return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); 748 | }, 749 | 750 | toInt: function(base){ 751 | return parseInt(this, base || 10); 752 | }, 753 | 754 | toFloat: function(){ 755 | return parseFloat(this); 756 | }, 757 | 758 | hexToRgb: function(array){ 759 | var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); 760 | return (hex) ? hex.slice(1).hexToRgb(array) : null; 761 | }, 762 | 763 | rgbToHex: function(array){ 764 | var rgb = this.match(/\d{1,3}/g); 765 | return (rgb) ? rgb.rgbToHex(array) : null; 766 | }, 767 | 768 | substitute: function(object, regexp){ 769 | return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){ 770 | if (match.charAt(0) == '\\') return match.slice(1); 771 | return (object[name] != null) ? object[name] : ''; 772 | }); 773 | } 774 | 775 | }); 776 | 777 | 778 | /* 779 | --- 780 | 781 | name: Function 782 | 783 | description: Contains Function Prototypes like create, bind, pass, and delay. 784 | 785 | license: MIT-style license. 786 | 787 | requires: Type 788 | 789 | provides: Function 790 | 791 | ... 792 | */ 793 | 794 | Function.extend({ 795 | 796 | attempt: function(){ 797 | for (var i = 0, l = arguments.length; i < l; i++){ 798 | try { 799 | return arguments[i](); 800 | } catch (e){} 801 | } 802 | return null; 803 | } 804 | 805 | }); 806 | 807 | Function.implement({ 808 | 809 | attempt: function(args, bind){ 810 | try { 811 | return this.apply(bind, Array.from(args)); 812 | } catch (e){} 813 | 814 | return null; 815 | }, 816 | 817 | bind: function(bind){ 818 | var self = this, 819 | args = (arguments.length > 1) ? Array.slice(arguments, 1) : null; 820 | 821 | return function(){ 822 | if (!args && !arguments.length) return self.call(bind); 823 | if (args && arguments.length) return self.apply(bind, args.concat(Array.from(arguments))); 824 | return self.apply(bind, args || arguments); 825 | }; 826 | }, 827 | 828 | pass: function(args, bind){ 829 | var self = this; 830 | if (args != null) args = Array.from(args); 831 | return function(){ 832 | return self.apply(bind, args || arguments); 833 | }; 834 | }, 835 | 836 | delay: function(delay, bind, args){ 837 | return setTimeout(this.pass(args, bind), delay); 838 | }, 839 | 840 | periodical: function(periodical, bind, args){ 841 | return setInterval(this.pass(args, bind), periodical); 842 | } 843 | 844 | }); 845 | 846 | //<1.2compat> 847 | 848 | delete Function.prototype.bind; 849 | 850 | Function.implement({ 851 | 852 | create: function(options){ 853 | var self = this; 854 | options = options || {}; 855 | return function(event){ 856 | var args = options.arguments; 857 | args = (args != null) ? Array.from(args) : Array.slice(arguments, (options.event) ? 1 : 0); 858 | if (options.event) args = [event || window.event].extend(args); 859 | var returns = function(){ 860 | return self.apply(options.bind || null, args); 861 | }; 862 | if (options.delay) return setTimeout(returns, options.delay); 863 | if (options.periodical) return setInterval(returns, options.periodical); 864 | if (options.attempt) return Function.attempt(returns); 865 | return returns(); 866 | }; 867 | }, 868 | 869 | bind: function(bind, args){ 870 | var self = this; 871 | if (args != null) args = Array.from(args); 872 | return function(){ 873 | return self.apply(bind, args || arguments); 874 | }; 875 | }, 876 | 877 | bindWithEvent: function(bind, args){ 878 | var self = this; 879 | if (args != null) args = Array.from(args); 880 | return function(event){ 881 | return self.apply(bind, (args == null) ? arguments : [event].concat(args)); 882 | }; 883 | }, 884 | 885 | run: function(args, bind){ 886 | return this.apply(bind, Array.from(args)); 887 | } 888 | 889 | }); 890 | 891 | var $try = Function.attempt; 892 | 893 | // 894 | 895 | 896 | /* 897 | --- 898 | 899 | name: Number 900 | 901 | description: Contains Number Prototypes like limit, round, times, and ceil. 902 | 903 | license: MIT-style license. 904 | 905 | requires: Type 906 | 907 | provides: Number 908 | 909 | ... 910 | */ 911 | 912 | Number.implement({ 913 | 914 | limit: function(min, max){ 915 | return Math.min(max, Math.max(min, this)); 916 | }, 917 | 918 | round: function(precision){ 919 | precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0); 920 | return Math.round(this * precision) / precision; 921 | }, 922 | 923 | times: function(fn, bind){ 924 | for (var i = 0; i < this; i++) fn.call(bind, i, this); 925 | }, 926 | 927 | toFloat: function(){ 928 | return parseFloat(this); 929 | }, 930 | 931 | toInt: function(base){ 932 | return parseInt(this, base || 10); 933 | } 934 | 935 | }); 936 | 937 | Number.alias('each', 'times'); 938 | 939 | (function(math){ 940 | var methods = {}; 941 | math.each(function(name){ 942 | if (!Number[name]) methods[name] = function(){ 943 | return Math[name].apply(null, [this].concat(Array.from(arguments))); 944 | }; 945 | }); 946 | Number.implement(methods); 947 | })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']); 948 | 949 | 950 | /* 951 | --- 952 | 953 | name: Class 954 | 955 | description: Contains the Class Function for easily creating, extending, and implementing reusable Classes. 956 | 957 | license: MIT-style license. 958 | 959 | requires: [Array, String, Function, Number] 960 | 961 | provides: Class 962 | 963 | ... 964 | */ 965 | 966 | (function(){ 967 | 968 | var Class = this.Class = new Type('Class', function(params){ 969 | if (instanceOf(params, Function)) params = {initialize: params}; 970 | 971 | var newClass = function(){ 972 | reset(this); 973 | if (newClass.$prototyping) return this; 974 | this.$caller = null; 975 | var value = (this.initialize) ? this.initialize.apply(this, arguments) : this; 976 | this.$caller = this.caller = null; 977 | return value; 978 | }.extend(this).implement(params); 979 | 980 | newClass.$constructor = Class; 981 | newClass.prototype.$constructor = newClass; 982 | newClass.prototype.parent = parent; 983 | 984 | return newClass; 985 | }); 986 | 987 | var parent = function(){ 988 | if (!this.$caller) throw new Error('The method "parent" cannot be called.'); 989 | var name = this.$caller.$name, 990 | parent = this.$caller.$owner.parent, 991 | previous = (parent) ? parent.prototype[name] : null; 992 | if (!previous) throw new Error('The method "' + name + '" has no parent.'); 993 | return previous.apply(this, arguments); 994 | }; 995 | 996 | var reset = function(object){ 997 | for (var key in object){ 998 | var value = object[key]; 999 | switch (typeOf(value)){ 1000 | case 'object': 1001 | var F = function(){}; 1002 | F.prototype = value; 1003 | object[key] = reset(new F); 1004 | break; 1005 | case 'array': object[key] = value.clone(); break; 1006 | } 1007 | } 1008 | return object; 1009 | }; 1010 | 1011 | var wrap = function(self, key, method){ 1012 | if (method.$origin) method = method.$origin; 1013 | var wrapper = function(){ 1014 | if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.'); 1015 | var caller = this.caller, current = this.$caller; 1016 | this.caller = current; this.$caller = wrapper; 1017 | var result = method.apply(this, arguments); 1018 | this.$caller = current; this.caller = caller; 1019 | return result; 1020 | }.extend({$owner: self, $origin: method, $name: key}); 1021 | return wrapper; 1022 | }; 1023 | 1024 | var implement = function(key, value, retain){ 1025 | if (Class.Mutators.hasOwnProperty(key)){ 1026 | value = Class.Mutators[key].call(this, value); 1027 | if (value == null) return this; 1028 | } 1029 | 1030 | if (typeOf(value) == 'function'){ 1031 | if (value.$hidden) return this; 1032 | this.prototype[key] = (retain) ? value : wrap(this, key, value); 1033 | } else { 1034 | Object.merge(this.prototype, key, value); 1035 | } 1036 | 1037 | return this; 1038 | }; 1039 | 1040 | var getInstance = function(klass){ 1041 | klass.$prototyping = true; 1042 | var proto = new klass; 1043 | delete klass.$prototyping; 1044 | return proto; 1045 | }; 1046 | 1047 | Class.implement('implement', implement.overloadSetter()); 1048 | 1049 | Class.Mutators = { 1050 | 1051 | Extends: function(parent){ 1052 | this.parent = parent; 1053 | this.prototype = getInstance(parent); 1054 | }, 1055 | 1056 | Implements: function(items){ 1057 | Array.from(items).each(function(item){ 1058 | var instance = new item; 1059 | for (var key in instance) implement.call(this, key, instance[key], true); 1060 | }, this); 1061 | } 1062 | }; 1063 | 1064 | })(); 1065 | 1066 | 1067 | /* 1068 | --- 1069 | 1070 | name: Browser 1071 | 1072 | description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash. 1073 | 1074 | license: MIT-style license. 1075 | 1076 | requires: [Array, Function, Number, String] 1077 | 1078 | provides: [Browser, Window, Document] 1079 | 1080 | ... 1081 | */ 1082 | 1083 | (function(){ 1084 | 1085 | var document = this.document; 1086 | var window = document.window = this; 1087 | 1088 | var UID = 1; 1089 | 1090 | this.$uid = (window.ActiveXObject) ? function(item){ 1091 | return (item.uid || (item.uid = [UID++]))[0]; 1092 | } : function(item){ 1093 | return item.uid || (item.uid = UID++); 1094 | }; 1095 | 1096 | $uid(window); 1097 | $uid(document); 1098 | 1099 | var ua = navigator.userAgent.toLowerCase(), 1100 | platform = navigator.platform.toLowerCase(), 1101 | UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0], 1102 | mode = UA[1] == 'ie' && document.documentMode; 1103 | 1104 | var Browser = this.Browser = { 1105 | 1106 | extend: Function.prototype.extend, 1107 | 1108 | name: (UA[1] == 'version') ? UA[3] : UA[1], 1109 | 1110 | version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]), 1111 | 1112 | Platform: { 1113 | name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0] 1114 | }, 1115 | 1116 | Features: { 1117 | xpath: !!(document.evaluate), 1118 | air: !!(window.runtime), 1119 | query: !!(document.querySelector), 1120 | json: !!(window.JSON) 1121 | }, 1122 | 1123 | Plugins: {} 1124 | 1125 | }; 1126 | 1127 | Browser[Browser.name] = true; 1128 | Browser[Browser.name + parseInt(Browser.version, 10)] = true; 1129 | Browser.Platform[Browser.Platform.name] = true; 1130 | 1131 | // Request 1132 | 1133 | Browser.Request = (function(){ 1134 | 1135 | var XMLHTTP = function(){ 1136 | return new XMLHttpRequest(); 1137 | }; 1138 | 1139 | var MSXML2 = function(){ 1140 | return new ActiveXObject('MSXML2.XMLHTTP'); 1141 | }; 1142 | 1143 | var MSXML = function(){ 1144 | return new ActiveXObject('Microsoft.XMLHTTP'); 1145 | }; 1146 | 1147 | return Function.attempt(function(){ 1148 | XMLHTTP(); 1149 | return XMLHTTP; 1150 | }, function(){ 1151 | MSXML2(); 1152 | return MSXML2; 1153 | }, function(){ 1154 | MSXML(); 1155 | return MSXML; 1156 | }); 1157 | 1158 | })(); 1159 | 1160 | Browser.Features.xhr = !!(Browser.Request); 1161 | 1162 | // Flash detection 1163 | 1164 | var version = (Function.attempt(function(){ 1165 | return navigator.plugins['Shockwave Flash'].description; 1166 | }, function(){ 1167 | return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); 1168 | }) || '0 r0').match(/\d+/g); 1169 | 1170 | Browser.Plugins.Flash = { 1171 | version: Number(version[0] || '0.' + version[1]) || 0, 1172 | build: Number(version[2]) || 0 1173 | }; 1174 | 1175 | // String scripts 1176 | 1177 | Browser.exec = function(text){ 1178 | if (!text) return text; 1179 | if (window.execScript){ 1180 | window.execScript(text); 1181 | } else { 1182 | var script = document.createElement('script'); 1183 | script.setAttribute('type', 'text/javascript'); 1184 | script.text = text; 1185 | document.head.appendChild(script); 1186 | document.head.removeChild(script); 1187 | } 1188 | return text; 1189 | }; 1190 | 1191 | String.implement('stripScripts', function(exec){ 1192 | var scripts = ''; 1193 | var text = this.replace(/]*>([\s\S]*?)<\/script>/gi, function(all, code){ 1194 | scripts += code + '\n'; 1195 | return ''; 1196 | }); 1197 | if (exec === true) Browser.exec(scripts); 1198 | else if (typeOf(exec) == 'function') exec(scripts, text); 1199 | return text; 1200 | }); 1201 | 1202 | // Window, Document 1203 | 1204 | Browser.extend({ 1205 | Document: this.Document, 1206 | Window: this.Window, 1207 | Element: this.Element, 1208 | Event: this.Event 1209 | }); 1210 | 1211 | this.Window = this.$constructor = new Type('Window', function(){}); 1212 | 1213 | this.$family = Function.from('window').hide(); 1214 | 1215 | Window.mirror(function(name, method){ 1216 | window[name] = method; 1217 | }); 1218 | 1219 | this.Document = document.$constructor = new Type('Document', function(){}); 1220 | 1221 | document.$family = Function.from('document').hide(); 1222 | 1223 | Document.mirror(function(name, method){ 1224 | document[name] = method; 1225 | }); 1226 | 1227 | document.html = document.documentElement; 1228 | document.head = document.getElementsByTagName('head')[0]; 1229 | 1230 | if (document.execCommand) try { 1231 | document.execCommand("BackgroundImageCache", false, true); 1232 | } catch (e){} 1233 | 1234 | if (this.attachEvent && !this.addEventListener){ 1235 | var unloadEvent = function(){ 1236 | this.detachEvent('onunload', unloadEvent); 1237 | document.head = document.html = document.window = null; 1238 | }; 1239 | this.attachEvent('onunload', unloadEvent); 1240 | } 1241 | 1242 | // IE fails on collections and ) 1243 | var arrayFrom = Array.from; 1244 | try { 1245 | arrayFrom(document.html.childNodes); 1246 | } catch(e){ 1247 | Array.from = function(item){ 1248 | if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){ 1249 | var i = item.length, array = new Array(i); 1250 | while (i--) array[i] = item[i]; 1251 | return array; 1252 | } 1253 | return arrayFrom(item); 1254 | }; 1255 | 1256 | var prototype = Array.prototype, 1257 | slice = prototype.slice; 1258 | ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){ 1259 | var method = prototype[name]; 1260 | Array[name] = function(item){ 1261 | return method.apply(Array.from(item), slice.call(arguments, 1)); 1262 | }; 1263 | }); 1264 | } 1265 | 1266 | //<1.2compat> 1267 | 1268 | if (Browser.Platform.ios) Browser.Platform.ipod = true; 1269 | 1270 | Browser.Engine = {}; 1271 | 1272 | var setEngine = function(name, version){ 1273 | Browser.Engine.name = name; 1274 | Browser.Engine[name + version] = true; 1275 | Browser.Engine.version = version; 1276 | }; 1277 | 1278 | if (Browser.ie){ 1279 | Browser.Engine.trident = true; 1280 | 1281 | switch (Browser.version){ 1282 | case 6: setEngine('trident', 4); break; 1283 | case 7: setEngine('trident', 5); break; 1284 | case 8: setEngine('trident', 6); 1285 | } 1286 | } 1287 | 1288 | if (Browser.firefox){ 1289 | Browser.Engine.gecko = true; 1290 | 1291 | if (Browser.version >= 3) setEngine('gecko', 19); 1292 | else setEngine('gecko', 18); 1293 | } 1294 | 1295 | if (Browser.safari || Browser.chrome){ 1296 | Browser.Engine.webkit = true; 1297 | 1298 | switch (Browser.version){ 1299 | case 2: setEngine('webkit', 419); break; 1300 | case 3: setEngine('webkit', 420); break; 1301 | case 4: setEngine('webkit', 525); 1302 | } 1303 | } 1304 | 1305 | if (Browser.opera){ 1306 | Browser.Engine.presto = true; 1307 | 1308 | if (Browser.version >= 9.6) setEngine('presto', 960); 1309 | else if (Browser.version >= 9.5) setEngine('presto', 950); 1310 | else setEngine('presto', 925); 1311 | } 1312 | 1313 | if (Browser.name == 'unknown'){ 1314 | switch ((ua.match(/(?:webkit|khtml|gecko)/) || [])[0]){ 1315 | case 'webkit': 1316 | case 'khtml': 1317 | Browser.Engine.webkit = true; 1318 | break; 1319 | case 'gecko': 1320 | Browser.Engine.gecko = true; 1321 | } 1322 | } 1323 | 1324 | this.$exec = Browser.exec; 1325 | 1326 | // 1327 | 1328 | })(); 1329 | 1330 | 1331 | /* 1332 | --- 1333 | name: Slick.Parser 1334 | description: Standalone CSS3 Selector parser 1335 | provides: Slick.Parser 1336 | ... 1337 | */ 1338 | 1339 | (function(){ 1340 | 1341 | var parsed, 1342 | separatorIndex, 1343 | combinatorIndex, 1344 | reversed, 1345 | cache = {}, 1346 | reverseCache = {}, 1347 | reUnescape = /\\/g; 1348 | 1349 | var parse = function(expression, isReversed){ 1350 | if (expression == null) return null; 1351 | if (expression.Slick === true) return expression; 1352 | expression = ('' + expression).replace(/^\s+|\s+$/g, ''); 1353 | reversed = !!isReversed; 1354 | var currentCache = (reversed) ? reverseCache : cache; 1355 | if (currentCache[expression]) return currentCache[expression]; 1356 | parsed = {Slick: true, expressions: [], raw: expression, reverse: function(){ 1357 | return parse(this.raw, true); 1358 | }}; 1359 | separatorIndex = -1; 1360 | while (expression != (expression = expression.replace(regexp, parser))); 1361 | parsed.length = parsed.expressions.length; 1362 | return currentCache[expression] = (reversed) ? reverse(parsed) : parsed; 1363 | }; 1364 | 1365 | var reverseCombinator = function(combinator){ 1366 | if (combinator === '!') return ' '; 1367 | else if (combinator === ' ') return '!'; 1368 | else if ((/^!/).test(combinator)) return combinator.replace(/^!/, ''); 1369 | else return '!' + combinator; 1370 | }; 1371 | 1372 | var reverse = function(expression){ 1373 | var expressions = expression.expressions; 1374 | for (var i = 0; i < expressions.length; i++){ 1375 | var exp = expressions[i]; 1376 | var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)}; 1377 | 1378 | for (var j = 0; j < exp.length; j++){ 1379 | var cexp = exp[j]; 1380 | if (!cexp.reverseCombinator) cexp.reverseCombinator = ' '; 1381 | cexp.combinator = cexp.reverseCombinator; 1382 | delete cexp.reverseCombinator; 1383 | } 1384 | 1385 | exp.reverse().push(last); 1386 | } 1387 | return expression; 1388 | }; 1389 | 1390 | var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan MIT License 1391 | return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, "\\$&"); 1392 | }; 1393 | 1394 | var regexp = new RegExp( 1395 | /* 1396 | #!/usr/bin/env ruby 1397 | puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'') 1398 | __END__ 1399 | "(?x)^(?:\ 1400 | \\s* ( , ) \\s* # Separator \n\ 1401 | | \\s* ( + ) \\s* # Combinator \n\ 1402 | | ( \\s+ ) # CombinatorChildren \n\ 1403 | | ( + | \\* ) # Tag \n\ 1404 | | \\# ( + ) # ID \n\ 1405 | | \\. ( + ) # ClassName \n\ 1406 | | # Attribute \n\ 1407 | \\[ \ 1408 | \\s* (+) (?: \ 1409 | \\s* ([*^$!~|]?=) (?: \ 1410 | \\s* (?:\ 1411 | ([\"']?)(.*?)\\9 \ 1412 | )\ 1413 | ) \ 1414 | )? \\s* \ 1415 | \\](?!\\]) \n\ 1416 | | :+ ( + )(?:\ 1417 | \\( (?:\ 1418 | (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\ 1419 | ) \\)\ 1420 | )?\ 1421 | )" 1422 | */ 1423 | "^(?:\\s*(,)\\s*|\\s*(+)\\s*|(\\s+)|(+|\\*)|\\#(+)|\\.(+)|\\[\\s*(+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|:+(+)(?:\\((?:(?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+))\\))?)" 1424 | .replace(//, '[' + escapeRegExp(">+~`!@$%^&={}\\;/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])') 1426 | .replace(//g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])') 1427 | ); 1428 | 1429 | function parser( 1430 | rawMatch, 1431 | 1432 | separator, 1433 | combinator, 1434 | combinatorChildren, 1435 | 1436 | tagName, 1437 | id, 1438 | className, 1439 | 1440 | attributeKey, 1441 | attributeOperator, 1442 | attributeQuote, 1443 | attributeValue, 1444 | 1445 | pseudoClass, 1446 | pseudoQuote, 1447 | pseudoClassQuotedValue, 1448 | pseudoClassValue 1449 | ){ 1450 | if (separator || separatorIndex === -1){ 1451 | parsed.expressions[++separatorIndex] = []; 1452 | combinatorIndex = -1; 1453 | if (separator) return ''; 1454 | } 1455 | 1456 | if (combinator || combinatorChildren || combinatorIndex === -1){ 1457 | combinator = combinator || ' '; 1458 | var currentSeparator = parsed.expressions[separatorIndex]; 1459 | if (reversed && currentSeparator[combinatorIndex]) 1460 | currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator); 1461 | currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'}; 1462 | } 1463 | 1464 | var currentParsed = parsed.expressions[separatorIndex][combinatorIndex]; 1465 | 1466 | if (tagName){ 1467 | currentParsed.tag = tagName.replace(reUnescape, ''); 1468 | 1469 | } else if (id){ 1470 | currentParsed.id = id.replace(reUnescape, ''); 1471 | 1472 | } else if (className){ 1473 | className = className.replace(reUnescape, ''); 1474 | 1475 | if (!currentParsed.classList) currentParsed.classList = []; 1476 | if (!currentParsed.classes) currentParsed.classes = []; 1477 | currentParsed.classList.push(className); 1478 | currentParsed.classes.push({ 1479 | value: className, 1480 | regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)') 1481 | }); 1482 | 1483 | } else if (pseudoClass){ 1484 | pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue; 1485 | pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null; 1486 | 1487 | if (!currentParsed.pseudos) currentParsed.pseudos = []; 1488 | currentParsed.pseudos.push({ 1489 | key: pseudoClass.replace(reUnescape, ''), 1490 | value: pseudoClassValue 1491 | }); 1492 | 1493 | } else if (attributeKey){ 1494 | attributeKey = attributeKey.replace(reUnescape, ''); 1495 | attributeValue = (attributeValue || '').replace(reUnescape, ''); 1496 | 1497 | var test, regexp; 1498 | 1499 | switch (attributeOperator){ 1500 | case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break; 1501 | case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break; 1502 | case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break; 1503 | case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break; 1504 | case '=' : test = function(value){ 1505 | return attributeValue == value; 1506 | }; break; 1507 | case '*=' : test = function(value){ 1508 | return value && value.indexOf(attributeValue) > -1; 1509 | }; break; 1510 | case '!=' : test = function(value){ 1511 | return attributeValue != value; 1512 | }; break; 1513 | default : test = function(value){ 1514 | return !!value; 1515 | }; 1516 | } 1517 | 1518 | if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){ 1519 | return false; 1520 | }; 1521 | 1522 | if (!test) test = function(value){ 1523 | return value && regexp.test(value); 1524 | }; 1525 | 1526 | if (!currentParsed.attributes) currentParsed.attributes = []; 1527 | currentParsed.attributes.push({ 1528 | key: attributeKey, 1529 | operator: attributeOperator, 1530 | value: attributeValue, 1531 | test: test 1532 | }); 1533 | 1534 | } 1535 | 1536 | return ''; 1537 | }; 1538 | 1539 | // Slick NS 1540 | 1541 | var Slick = (this.Slick || {}); 1542 | 1543 | Slick.parse = function(expression){ 1544 | return parse(expression); 1545 | }; 1546 | 1547 | Slick.escapeRegExp = escapeRegExp; 1548 | 1549 | if (!this.Slick) this.Slick = Slick; 1550 | 1551 | }).apply(/**/(typeof exports != 'undefined') ? exports : /**/this); 1552 | 1553 | 1554 | /* 1555 | --- 1556 | name: Slick.Finder 1557 | description: The new, superfast css selector engine. 1558 | provides: Slick.Finder 1559 | requires: Slick.Parser 1560 | ... 1561 | */ 1562 | 1563 | (function(){ 1564 | 1565 | var local = {}; 1566 | 1567 | // Feature / Bug detection 1568 | 1569 | local.isNativeCode = function(fn){ 1570 | return (/\{\s*\[native code\]\s*\}/).test('' + fn); 1571 | }; 1572 | 1573 | local.isXML = function(document){ 1574 | return (!!document.xmlVersion) || (!!document.xml) || (Object.prototype.toString.call(document) === '[object XMLDocument]') || 1575 | (document.nodeType === 9 && document.documentElement.nodeName !== 'HTML'); 1576 | }; 1577 | 1578 | local.setDocument = function(document){ 1579 | 1580 | // convert elements / window arguments to document. if document cannot be extrapolated, the function returns. 1581 | 1582 | if (document.nodeType === 9); // document 1583 | else if (document.ownerDocument) document = document.ownerDocument; // node 1584 | else if (document.navigator) document = document.document; // window 1585 | else return; 1586 | 1587 | // check if it's the old document 1588 | 1589 | if (this.document === document) return; 1590 | this.document = document; 1591 | var root = this.root = document.documentElement; 1592 | 1593 | this.isXMLDocument = this.isXML(document); 1594 | 1595 | this.brokenStarGEBTN 1596 | = this.starSelectsClosedQSA 1597 | = this.idGetsName 1598 | = this.brokenMixedCaseQSA 1599 | = this.brokenGEBCN 1600 | = this.brokenCheckedQSA 1601 | = this.brokenEmptyAttributeQSA 1602 | = this.isHTMLDocument 1603 | = false; 1604 | 1605 | var starSelectsClosed, starSelectsComments, 1606 | brokenSecondClassNameGEBCN, cachedGetElementsByClassName; 1607 | 1608 | var selected, id; 1609 | var testNode = document.createElement('div'); 1610 | root.appendChild(testNode); 1611 | 1612 | // on non-HTML documents innerHTML and getElementsById doesnt work properly 1613 | try { 1614 | id = 'slick_getbyid_test'; 1615 | testNode.innerHTML = ''; 1616 | this.isHTMLDocument = !!document.getElementById(id); 1617 | } catch(e){}; 1618 | 1619 | if (this.isHTMLDocument){ 1620 | 1621 | testNode.style.display = 'none'; 1622 | 1623 | // IE returns comment nodes for getElementsByTagName('*') for some documents 1624 | testNode.appendChild(document.createComment('')); 1625 | starSelectsComments = (testNode.getElementsByTagName('*').length > 0); 1626 | 1627 | // IE returns closed nodes (EG:"") for getElementsByTagName('*') for some documents 1628 | try { 1629 | testNode.innerHTML = 'foo'; 1630 | selected = testNode.getElementsByTagName('*'); 1631 | starSelectsClosed = (selected && selected.length && selected[0].nodeName.charAt(0) == '/'); 1632 | } catch(e){}; 1633 | 1634 | this.brokenStarGEBTN = starSelectsComments || starSelectsClosed; 1635 | 1636 | // IE 8 returns closed nodes (EG:"") for querySelectorAll('*') for some documents 1637 | if (testNode.querySelectorAll) try { 1638 | testNode.innerHTML = 'foo'; 1639 | selected = testNode.querySelectorAll('*'); 1640 | this.starSelectsClosedQSA = (selected && selected.length && selected[0].nodeName.charAt(0) == '/'); 1641 | } catch(e){}; 1642 | 1643 | // IE returns elements with the name instead of just id for getElementsById for some documents 1644 | try { 1645 | id = 'slick_id_gets_name'; 1646 | testNode.innerHTML = ''; 1647 | this.idGetsName = document.getElementById(id) === testNode.firstChild; 1648 | } catch(e){}; 1649 | 1650 | // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode 1651 | try { 1652 | testNode.innerHTML = ''; 1653 | this.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiXedCaSe').length; 1654 | } catch(e){}; 1655 | 1656 | try { 1657 | testNode.innerHTML = ''; 1658 | testNode.getElementsByClassName('b').length; 1659 | testNode.firstChild.className = 'b'; 1660 | cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2); 1661 | } catch(e){}; 1662 | 1663 | // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one 1664 | try { 1665 | testNode.innerHTML = ''; 1666 | brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2); 1667 | } catch(e){}; 1668 | 1669 | this.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN; 1670 | 1671 | // Webkit dont return selected options on querySelectorAll 1672 | try { 1673 | testNode.innerHTML = ''; 1674 | this.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0); 1675 | } catch(e){}; 1676 | 1677 | // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll 1678 | try { 1679 | testNode.innerHTML = ''; 1680 | this.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0); 1681 | } catch(e){}; 1682 | 1683 | } 1684 | 1685 | root.removeChild(testNode); 1686 | testNode = null; 1687 | 1688 | // hasAttribute 1689 | 1690 | this.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) { 1691 | return node.hasAttribute(attribute); 1692 | } : function(node, attribute) { 1693 | node = node.getAttributeNode(attribute); 1694 | return !!(node && (node.specified || node.nodeValue)); 1695 | }; 1696 | 1697 | // contains 1698 | // FIXME: Add specs: local.contains should be different for xml and html documents? 1699 | this.contains = (root && this.isNativeCode(root.contains)) ? function(context, node){ 1700 | return context.contains(node); 1701 | } : (root && root.compareDocumentPosition) ? function(context, node){ 1702 | return context === node || !!(context.compareDocumentPosition(node) & 16); 1703 | } : function(context, node){ 1704 | if (node) do { 1705 | if (node === context) return true; 1706 | } while ((node = node.parentNode)); 1707 | return false; 1708 | }; 1709 | 1710 | // document order sorting 1711 | // credits to Sizzle (http://sizzlejs.com/) 1712 | 1713 | this.documentSorter = (root.compareDocumentPosition) ? function(a, b){ 1714 | if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0; 1715 | return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; 1716 | } : ('sourceIndex' in root) ? function(a, b){ 1717 | if (!a.sourceIndex || !b.sourceIndex) return 0; 1718 | return a.sourceIndex - b.sourceIndex; 1719 | } : (document.createRange) ? function(a, b){ 1720 | if (!a.ownerDocument || !b.ownerDocument) return 0; 1721 | var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); 1722 | aRange.setStart(a, 0); 1723 | aRange.setEnd(a, 0); 1724 | bRange.setStart(b, 0); 1725 | bRange.setEnd(b, 0); 1726 | return aRange.compareBoundaryPoints(Range.START_TO_END, bRange); 1727 | } : null ; 1728 | 1729 | this.getUID = (this.isHTMLDocument) ? this.getUIDHTML : this.getUIDXML; 1730 | 1731 | }; 1732 | 1733 | // Main Method 1734 | 1735 | local.search = function(context, expression, append, first){ 1736 | 1737 | var found = this.found = (first) ? null : (append || []); 1738 | 1739 | // context checks 1740 | 1741 | if (!context) return found; // No context 1742 | if (context.navigator) context = context.document; // Convert the node from a window to a document 1743 | else if (!context.nodeType) return found; // Reject misc junk input 1744 | 1745 | // setup 1746 | 1747 | var parsed, i; 1748 | 1749 | var uniques = this.uniques = {}; 1750 | 1751 | if (this.document !== (context.ownerDocument || context)) this.setDocument(context); 1752 | 1753 | // should sort if there are nodes in append and if you pass multiple expressions. 1754 | // should remove duplicates if append already has items 1755 | var shouldUniques = !!(append && append.length); 1756 | 1757 | // avoid duplicating items already in the append array 1758 | if (shouldUniques) for (i = found.length; i--;) this.uniques[this.getUID(found[i])] = true; 1759 | 1760 | // expression checks 1761 | 1762 | if (typeof expression == 'string'){ // expression is a string 1763 | 1764 | // Overrides 1765 | 1766 | for (i = this.overrides.length; i--;){ 1767 | var override = this.overrides[i]; 1768 | if (override.regexp.test(expression)){ 1769 | var result = override.method.call(context, expression, found, first); 1770 | if (result === false) continue; 1771 | if (result === true) return found; 1772 | return result; 1773 | } 1774 | } 1775 | 1776 | parsed = this.Slick.parse(expression); 1777 | if (!parsed.length) return found; 1778 | } else if (expression == null){ // there is no expression 1779 | return found; 1780 | } else if (expression.Slick){ // expression is a parsed Slick object 1781 | parsed = expression; 1782 | } else if (this.contains(context.documentElement || context, expression)){ // expression is a node 1783 | (found) ? found.push(expression) : found = expression; 1784 | return found; 1785 | } else { // other junk 1786 | return found; 1787 | } 1788 | 1789 | // cache elements for the nth selectors 1790 | 1791 | /**//**/ 1792 | 1793 | this.posNTH = {}; 1794 | this.posNTHLast = {}; 1795 | this.posNTHType = {}; 1796 | this.posNTHTypeLast = {}; 1797 | 1798 | /**//**/ 1799 | 1800 | // if append is null and there is only a single selector with one expression use pushArray, else use pushUID 1801 | this.push = (!shouldUniques && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID; 1802 | 1803 | if (found == null) found = []; 1804 | 1805 | // default engine 1806 | 1807 | var j, m, n; 1808 | var combinator, tag, id, classList, classes, attributes, pseudos; 1809 | var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions; 1810 | 1811 | search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){ 1812 | 1813 | combinator = 'combinator:' + currentBit.combinator; 1814 | if (!this[combinator]) continue search; 1815 | 1816 | tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase(); 1817 | id = currentBit.id; 1818 | classList = currentBit.classList; 1819 | classes = currentBit.classes; 1820 | attributes = currentBit.attributes; 1821 | pseudos = currentBit.pseudos; 1822 | lastBit = (j === (currentExpression.length - 1)); 1823 | 1824 | this.bitUniques = {}; 1825 | 1826 | if (lastBit){ 1827 | this.uniques = uniques; 1828 | this.found = found; 1829 | } else { 1830 | this.uniques = {}; 1831 | this.found = []; 1832 | } 1833 | 1834 | if (j === 0){ 1835 | this[combinator](context, tag, id, classes, attributes, pseudos, classList); 1836 | if (first && lastBit && found.length) break search; 1837 | } else { 1838 | if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){ 1839 | this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList); 1840 | if (found.length) break search; 1841 | } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList); 1842 | } 1843 | 1844 | currentItems = this.found; 1845 | } 1846 | 1847 | if (shouldUniques || (parsed.expressions.length > 1)) this.sort(found); 1848 | 1849 | return (first) ? (found[0] || null) : found; 1850 | }; 1851 | 1852 | // Utils 1853 | 1854 | local.uidx = 1; 1855 | local.uidk = 'slick:uniqueid'; 1856 | 1857 | local.getUIDXML = function(node){ 1858 | var uid = node.getAttribute(this.uidk); 1859 | if (!uid){ 1860 | uid = this.uidx++; 1861 | node.setAttribute(this.uidk, uid); 1862 | } 1863 | return uid; 1864 | }; 1865 | 1866 | local.getUIDHTML = function(node){ 1867 | return node.uniqueNumber || (node.uniqueNumber = this.uidx++); 1868 | }; 1869 | 1870 | // sort based on the setDocument documentSorter method. 1871 | 1872 | local.sort = function(results){ 1873 | if (!this.documentSorter) return results; 1874 | results.sort(this.documentSorter); 1875 | return results; 1876 | }; 1877 | 1878 | /**//**/ 1879 | 1880 | local.cacheNTH = {}; 1881 | 1882 | local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/; 1883 | 1884 | local.parseNTHArgument = function(argument){ 1885 | var parsed = argument.match(this.matchNTH); 1886 | if (!parsed) return false; 1887 | var special = parsed[2] || false; 1888 | var a = parsed[1] || 1; 1889 | if (a == '-') a = -1; 1890 | var b = +parsed[3] || 0; 1891 | parsed = 1892 | (special == 'n') ? {a: a, b: b} : 1893 | (special == 'odd') ? {a: 2, b: 1} : 1894 | (special == 'even') ? {a: 2, b: 0} : {a: 0, b: a}; 1895 | 1896 | return (this.cacheNTH[argument] = parsed); 1897 | }; 1898 | 1899 | local.createNTHPseudo = function(child, sibling, positions, ofType){ 1900 | return function(node, argument){ 1901 | var uid = this.getUID(node); 1902 | if (!this[positions][uid]){ 1903 | var parent = node.parentNode; 1904 | if (!parent) return false; 1905 | var el = parent[child], count = 1; 1906 | if (ofType){ 1907 | var nodeName = node.nodeName; 1908 | do { 1909 | if (el.nodeName !== nodeName) continue; 1910 | this[positions][this.getUID(el)] = count++; 1911 | } while ((el = el[sibling])); 1912 | } else { 1913 | do { 1914 | if (el.nodeType !== 1) continue; 1915 | this[positions][this.getUID(el)] = count++; 1916 | } while ((el = el[sibling])); 1917 | } 1918 | } 1919 | argument = argument || 'n'; 1920 | var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument); 1921 | if (!parsed) return false; 1922 | var a = parsed.a, b = parsed.b, pos = this[positions][uid]; 1923 | if (a == 0) return b == pos; 1924 | if (a > 0){ 1925 | if (pos < b) return false; 1926 | } else { 1927 | if (b < pos) return false; 1928 | } 1929 | return ((pos - b) % a) == 0; 1930 | }; 1931 | }; 1932 | 1933 | /**//**/ 1934 | 1935 | local.pushArray = function(node, tag, id, classes, attributes, pseudos){ 1936 | if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node); 1937 | }; 1938 | 1939 | local.pushUID = function(node, tag, id, classes, attributes, pseudos){ 1940 | var uid = this.getUID(node); 1941 | if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){ 1942 | this.uniques[uid] = true; 1943 | this.found.push(node); 1944 | } 1945 | }; 1946 | 1947 | local.matchNode = function(node, selector){ 1948 | var parsed = this.Slick.parse(selector); 1949 | if (!parsed) return true; 1950 | 1951 | // simple (single) selectors 1952 | if(parsed.length == 1 && parsed.expressions[0].length == 1){ 1953 | var exp = parsed.expressions[0][0]; 1954 | return this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos); 1955 | } 1956 | 1957 | var nodes = this.search(this.document, parsed); 1958 | for (var i = 0, item; item = nodes[i++];){ 1959 | if (item === node) return true; 1960 | } 1961 | return false; 1962 | }; 1963 | 1964 | local.matchPseudo = function(node, name, argument){ 1965 | var pseudoName = 'pseudo:' + name; 1966 | if (this[pseudoName]) return this[pseudoName](node, argument); 1967 | var attribute = this.getAttribute(node, name); 1968 | return (argument) ? argument == attribute : !!attribute; 1969 | }; 1970 | 1971 | local.matchSelector = function(node, tag, id, classes, attributes, pseudos){ 1972 | if (tag){ 1973 | if (tag == '*'){ 1974 | if (node.nodeName < '@') return false; // Fix for comment nodes and closed nodes 1975 | } else { 1976 | if (node.nodeName != tag) return false; 1977 | } 1978 | } 1979 | 1980 | if (id && node.getAttribute('id') != id) return false; 1981 | 1982 | var i, part, cls; 1983 | if (classes) for (i = classes.length; i--;){ 1984 | cls = ('className' in node) ? node.className : node.getAttribute('class'); 1985 | if (!(cls && classes[i].regexp.test(cls))) return false; 1986 | } 1987 | if (attributes) for (i = attributes.length; i--;){ 1988 | part = attributes[i]; 1989 | if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false; 1990 | } 1991 | if (pseudos) for (i = pseudos.length; i--;){ 1992 | part = pseudos[i]; 1993 | if (!this.matchPseudo(node, part.key, part.value)) return false; 1994 | } 1995 | return true; 1996 | }; 1997 | 1998 | var combinators = { 1999 | 2000 | ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level 2001 | 2002 | var i, item, children; 2003 | 2004 | if (this.isHTMLDocument){ 2005 | getById: if (id){ 2006 | item = this.document.getElementById(id); 2007 | if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){ 2008 | // all[id] returns all the elements with that name or id inside node 2009 | // if theres just one it will return the element, else it will be a collection 2010 | children = node.all[id]; 2011 | if (!children) return; 2012 | if (!children[0]) children = [children]; 2013 | for (i = 0; item = children[i++];) if (item.getAttributeNode('id').nodeValue == id){ 2014 | this.push(item, tag, null, classes, attributes, pseudos); 2015 | break; 2016 | } 2017 | return; 2018 | } 2019 | if (!item){ 2020 | // if the context is in the dom we return, else we will try GEBTN, breaking the getById label 2021 | if (this.contains(this.document.documentElement, node)) return; 2022 | else break getById; 2023 | } else if (this.document !== node && !this.contains(node, item)) return; 2024 | this.push(item, tag, null, classes, attributes, pseudos); 2025 | return; 2026 | } 2027 | getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){ 2028 | children = node.getElementsByClassName(classList.join(' ')); 2029 | if (!(children && children.length)) break getByClass; 2030 | for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos); 2031 | return; 2032 | } 2033 | } 2034 | getByTag: { 2035 | children = node.getElementsByTagName(tag); 2036 | if (!(children && children.length)) break getByTag; 2037 | if (!this.brokenStarGEBTN) tag = null; 2038 | for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos); 2039 | } 2040 | }, 2041 | 2042 | '>': function(node, tag, id, classes, attributes, pseudos){ // direct children 2043 | if ((node = node.firstChild)) do { 2044 | if (node.nodeType === 1) this.push(node, tag, id, classes, attributes, pseudos); 2045 | } while ((node = node.nextSibling)); 2046 | }, 2047 | 2048 | '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling 2049 | while ((node = node.nextSibling)) if (node.nodeType === 1){ 2050 | this.push(node, tag, id, classes, attributes, pseudos); 2051 | break; 2052 | } 2053 | }, 2054 | 2055 | '^': function(node, tag, id, classes, attributes, pseudos){ // first child 2056 | node = node.firstChild; 2057 | if (node){ 2058 | if (node.nodeType === 1) this.push(node, tag, id, classes, attributes, pseudos); 2059 | else this['combinator:+'](node, tag, id, classes, attributes, pseudos); 2060 | } 2061 | }, 2062 | 2063 | '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings 2064 | while ((node = node.nextSibling)){ 2065 | if (node.nodeType !== 1) continue; 2066 | var uid = this.getUID(node); 2067 | if (this.bitUniques[uid]) break; 2068 | this.bitUniques[uid] = true; 2069 | this.push(node, tag, id, classes, attributes, pseudos); 2070 | } 2071 | }, 2072 | 2073 | '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling 2074 | this['combinator:+'](node, tag, id, classes, attributes, pseudos); 2075 | this['combinator:!+'](node, tag, id, classes, attributes, pseudos); 2076 | }, 2077 | 2078 | '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings 2079 | this['combinator:~'](node, tag, id, classes, attributes, pseudos); 2080 | this['combinator:!~'](node, tag, id, classes, attributes, pseudos); 2081 | }, 2082 | 2083 | '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document 2084 | while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos); 2085 | }, 2086 | 2087 | '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level) 2088 | node = node.parentNode; 2089 | if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos); 2090 | }, 2091 | 2092 | '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling 2093 | while ((node = node.previousSibling)) if (node.nodeType === 1){ 2094 | this.push(node, tag, id, classes, attributes, pseudos); 2095 | break; 2096 | } 2097 | }, 2098 | 2099 | '!^': function(node, tag, id, classes, attributes, pseudos){ // last child 2100 | node = node.lastChild; 2101 | if (node){ 2102 | if (node.nodeType === 1) this.push(node, tag, id, classes, attributes, pseudos); 2103 | else this['combinator:!+'](node, tag, id, classes, attributes, pseudos); 2104 | } 2105 | }, 2106 | 2107 | '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings 2108 | while ((node = node.previousSibling)){ 2109 | if (node.nodeType !== 1) continue; 2110 | var uid = this.getUID(node); 2111 | if (this.bitUniques[uid]) break; 2112 | this.bitUniques[uid] = true; 2113 | this.push(node, tag, id, classes, attributes, pseudos); 2114 | } 2115 | } 2116 | 2117 | }; 2118 | 2119 | for (var c in combinators) local['combinator:' + c] = combinators[c]; 2120 | 2121 | var pseudos = { 2122 | 2123 | /**/ 2124 | 2125 | 'empty': function(node){ 2126 | var child = node.firstChild; 2127 | return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length; 2128 | }, 2129 | 2130 | 'not': function(node, expression){ 2131 | return !this.matchNode(node, expression); 2132 | }, 2133 | 2134 | 'contains': function(node, text){ 2135 | return (node.innerText || node.textContent || '').indexOf(text) > -1; 2136 | }, 2137 | 2138 | 'first-child': function(node){ 2139 | while ((node = node.previousSibling)) if (node.nodeType === 1) return false; 2140 | return true; 2141 | }, 2142 | 2143 | 'last-child': function(node){ 2144 | while ((node = node.nextSibling)) if (node.nodeType === 1) return false; 2145 | return true; 2146 | }, 2147 | 2148 | 'only-child': function(node){ 2149 | var prev = node; 2150 | while ((prev = prev.previousSibling)) if (prev.nodeType === 1) return false; 2151 | var next = node; 2152 | while ((next = next.nextSibling)) if (next.nodeType === 1) return false; 2153 | return true; 2154 | }, 2155 | 2156 | /**/ 2157 | 2158 | 'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'), 2159 | 2160 | 'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'), 2161 | 2162 | 'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true), 2163 | 2164 | 'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true), 2165 | 2166 | 'index': function(node, index){ 2167 | return this['pseudo:nth-child'](node, '' + index + 1); 2168 | }, 2169 | 2170 | 'even': function(node, argument){ 2171 | return this['pseudo:nth-child'](node, '2n'); 2172 | }, 2173 | 2174 | 'odd': function(node, argument){ 2175 | return this['pseudo:nth-child'](node, '2n+1'); 2176 | }, 2177 | 2178 | /**/ 2179 | 2180 | /**/ 2181 | 2182 | 'first-of-type': function(node){ 2183 | var nodeName = node.nodeName; 2184 | while ((node = node.previousSibling)) if (node.nodeName === nodeName) return false; 2185 | return true; 2186 | }, 2187 | 2188 | 'last-of-type': function(node){ 2189 | var nodeName = node.nodeName; 2190 | while ((node = node.nextSibling)) if (node.nodeName === nodeName) return false; 2191 | return true; 2192 | }, 2193 | 2194 | 'only-of-type': function(node){ 2195 | var prev = node, nodeName = node.nodeName; 2196 | while ((prev = prev.previousSibling)) if (prev.nodeName === nodeName) return false; 2197 | var next = node; 2198 | while ((next = next.nextSibling)) if (next.nodeName === nodeName) return false; 2199 | return true; 2200 | }, 2201 | 2202 | /**/ 2203 | 2204 | // custom pseudos 2205 | 2206 | 'enabled': function(node){ 2207 | return (node.disabled === false); 2208 | }, 2209 | 2210 | 'disabled': function(node){ 2211 | return (node.disabled === true); 2212 | }, 2213 | 2214 | 'checked': function(node){ 2215 | return node.checked || node.selected; 2216 | }, 2217 | 2218 | 'focus': function(node){ 2219 | return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex')); 2220 | }, 2221 | 2222 | 'root': function(node){ 2223 | return (node === this.root); 2224 | }, 2225 | 2226 | 'selected': function(node){ 2227 | return node.selected; 2228 | } 2229 | 2230 | /**/ 2231 | }; 2232 | 2233 | for (var p in pseudos) local['pseudo:' + p] = pseudos[p]; 2234 | 2235 | // attributes methods 2236 | 2237 | local.attributeGetters = { 2238 | 2239 | 'class': function(){ 2240 | return ('className' in this) ? this.className : this.getAttribute('class'); 2241 | }, 2242 | 2243 | 'for': function(){ 2244 | return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for'); 2245 | }, 2246 | 2247 | 'href': function(){ 2248 | return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href'); 2249 | }, 2250 | 2251 | 'style': function(){ 2252 | return (this.style) ? this.style.cssText : this.getAttribute('style'); 2253 | } 2254 | 2255 | }; 2256 | 2257 | local.getAttribute = function(node, name){ 2258 | // FIXME: check if getAttribute() will get input elements on a form on this browser 2259 | // getAttribute is faster than getAttributeNode().nodeValue 2260 | var method = this.attributeGetters[name]; 2261 | if (method) return method.call(node); 2262 | var attributeNode = node.getAttributeNode(name); 2263 | return attributeNode ? attributeNode.nodeValue : null; 2264 | }; 2265 | 2266 | // overrides 2267 | 2268 | local.overrides = []; 2269 | 2270 | local.override = function(regexp, method){ 2271 | this.overrides.push({regexp: regexp, method: method}); 2272 | }; 2273 | 2274 | /**/ 2275 | 2276 | /**/ 2277 | 2278 | var reEmptyAttribute = /\[.*[*$^]=(?:["']{2})?\]/; 2279 | 2280 | local.override(/./, function(expression, found, first){ //querySelectorAll override 2281 | 2282 | if (!this.querySelectorAll || this.nodeType != 9 || !local.isHTMLDocument || local.brokenMixedCaseQSA || 2283 | (local.brokenCheckedQSA && expression.indexOf(':checked') > -1) || 2284 | (local.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression)) || Slick.disableQSA) return false; 2285 | 2286 | var nodes, node; 2287 | try { 2288 | if (first) return this.querySelector(expression) || null; 2289 | else nodes = this.querySelectorAll(expression); 2290 | } catch(error){ 2291 | return false; 2292 | } 2293 | 2294 | var i, hasOthers = !!(found.length); 2295 | 2296 | if (local.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){ 2297 | if (node.nodeName > '@' && (!hasOthers || !local.uniques[local.getUIDHTML(node)])) found.push(node); 2298 | } else for (i = 0; node = nodes[i++];){ 2299 | if (!hasOthers || !local.uniques[local.getUIDHTML(node)]) found.push(node); 2300 | } 2301 | 2302 | if (hasOthers) local.sort(found); 2303 | 2304 | return true; 2305 | 2306 | }); 2307 | 2308 | /**/ 2309 | 2310 | /**/ 2311 | 2312 | local.override(/^[\w-]+$|^\*$/, function(expression, found, first){ // tag override 2313 | var tag = expression; 2314 | if (tag == '*' && local.brokenStarGEBTN) return false; 2315 | 2316 | var nodes = this.getElementsByTagName(tag); 2317 | 2318 | if (first) return nodes[0] || null; 2319 | var i, node, hasOthers = !!(found.length); 2320 | 2321 | for (i = 0; node = nodes[i++];){ 2322 | if (!hasOthers || !local.uniques[local.getUID(node)]) found.push(node); 2323 | } 2324 | 2325 | if (hasOthers) local.sort(found); 2326 | 2327 | return true; 2328 | }); 2329 | 2330 | /**/ 2331 | 2332 | /**/ 2333 | 2334 | local.override(/^\.[\w-]+$/, function(expression, found, first){ // class override 2335 | if (!local.isHTMLDocument || (!this.getElementsByClassName && this.querySelectorAll)) return false; 2336 | 2337 | var nodes, node, i, hasOthers = !!(found && found.length), className = expression.substring(1); 2338 | if (this.getElementsByClassName && !local.brokenGEBCN){ 2339 | nodes = this.getElementsByClassName(className); 2340 | if (first) return nodes[0] || null; 2341 | for (i = 0; node = nodes[i++];){ 2342 | if (!hasOthers || !local.uniques[local.getUIDHTML(node)]) found.push(node); 2343 | } 2344 | } else { 2345 | var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(className) +'(\\s|$)'); 2346 | nodes = this.getElementsByTagName('*'); 2347 | for (i = 0; node = nodes[i++];){ 2348 | className = node.className; 2349 | if (!className || !matchClass.test(className)) continue; 2350 | if (first) return node; 2351 | if (!hasOthers || !local.uniques[local.getUIDHTML(node)]) found.push(node); 2352 | } 2353 | } 2354 | if (hasOthers) local.sort(found); 2355 | return (first) ? null : true; 2356 | }); 2357 | 2358 | /**/ 2359 | 2360 | /**/ 2361 | 2362 | local.override(/^#[\w-]+$/, function(expression, found, first){ // ID override 2363 | if (!local.isHTMLDocument || this.nodeType != 9) return false; 2364 | 2365 | var id = expression.substring(1), el = this.getElementById(id); 2366 | if (!el) return found; 2367 | if (local.idGetsName && el.getAttributeNode('id').nodeValue != id) return false; 2368 | if (first) return el || null; 2369 | var hasOthers = !!(found.length); 2370 | if (!hasOthers || !local.uniques[local.getUIDHTML(el)]) found.push(el); 2371 | if (hasOthers) local.sort(found); 2372 | return true; 2373 | }); 2374 | 2375 | /**/ 2376 | 2377 | /**/ 2378 | 2379 | if (typeof document != 'undefined') local.setDocument(document); 2380 | 2381 | // Slick 2382 | 2383 | var Slick = local.Slick = (this.Slick || {}); 2384 | 2385 | Slick.version = '0.9dev'; 2386 | 2387 | // Slick finder 2388 | 2389 | Slick.search = function(context, expression, append){ 2390 | return local.search(context, expression, append); 2391 | }; 2392 | 2393 | Slick.find = function(context, expression){ 2394 | return local.search(context, expression, null, true); 2395 | }; 2396 | 2397 | // Slick containment checker 2398 | 2399 | Slick.contains = function(container, node){ 2400 | local.setDocument(container); 2401 | return local.contains(container, node); 2402 | }; 2403 | 2404 | // Slick attribute getter 2405 | 2406 | Slick.getAttribute = function(node, name){ 2407 | return local.getAttribute(node, name); 2408 | }; 2409 | 2410 | // Slick matcher 2411 | 2412 | Slick.match = function(node, selector){ 2413 | if (!(node && selector)) return false; 2414 | if (!selector || selector === node) return true; 2415 | if (typeof selector != 'string') return false; 2416 | local.setDocument(node); 2417 | return local.matchNode(node, selector); 2418 | }; 2419 | 2420 | // Slick attribute accessor 2421 | 2422 | Slick.defineAttributeGetter = function(name, fn){ 2423 | local.attributeGetters[name] = fn; 2424 | return this; 2425 | }; 2426 | 2427 | Slick.lookupAttributeGetter = function(name){ 2428 | return local.attributeGetters[name]; 2429 | }; 2430 | 2431 | // Slick pseudo accessor 2432 | 2433 | Slick.definePseudo = function(name, fn){ 2434 | local['pseudo:' + name] = function(node, argument){ 2435 | return fn.call(node, argument); 2436 | }; 2437 | return this; 2438 | }; 2439 | 2440 | Slick.lookupPseudo = function(name){ 2441 | var pseudo = local['pseudo:' + name]; 2442 | if (pseudo) return function(argument){ 2443 | return pseudo.call(this, argument); 2444 | }; 2445 | return null; 2446 | }; 2447 | 2448 | // Slick overrides accessor 2449 | 2450 | Slick.override = function(regexp, fn){ 2451 | local.override(regexp, fn); 2452 | return this; 2453 | }; 2454 | 2455 | Slick.isXML = local.isXML; 2456 | 2457 | Slick.uidOf = function(node){ 2458 | return local.getUIDHTML(node); 2459 | }; 2460 | 2461 | if (!this.Slick) this.Slick = Slick; 2462 | 2463 | }).apply(/**/(typeof exports != 'undefined') ? exports : /**/this); 2464 | 2465 | 2466 | /* 2467 | --- 2468 | 2469 | name: Element 2470 | 2471 | description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements. 2472 | 2473 | license: MIT-style license. 2474 | 2475 | requires: [Window, Document, Array, String, Function, Number, Slick.Parser, Slick.Finder] 2476 | 2477 | provides: [Element, Elements, $, $$, Iframe, Selectors] 2478 | 2479 | ... 2480 | */ 2481 | 2482 | var Element = function(tag, props){ 2483 | var konstructor = Element.Constructors[tag]; 2484 | if (konstructor) return konstructor(props); 2485 | if (typeof tag != 'string') return document.id(tag).set(props); 2486 | 2487 | if (!props) props = {}; 2488 | 2489 | if (!tag.test(/^[\w-]+$/)){ 2490 | var parsed = Slick.parse(tag).expressions[0][0]; 2491 | tag = (parsed.tag == '*') ? 'div' : parsed.tag; 2492 | if (parsed.id && props.id == null) props.id = parsed.id; 2493 | 2494 | var attributes = parsed.attributes; 2495 | if (attributes) for (var i = 0, l = attributes.length; i < l; i++){ 2496 | var attr = attributes[i]; 2497 | if (attr.value != null && attr.operator == '=' && props[attr.key] == null) 2498 | props[attr.key] = attr.value; 2499 | } 2500 | 2501 | if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' '); 2502 | } 2503 | 2504 | return document.newElement(tag, props); 2505 | }; 2506 | 2507 | if (Browser.Element) Element.prototype = Browser.Element.prototype; 2508 | 2509 | new Type('Element', Element).mirror(function(name){ 2510 | if (Array.prototype[name]) return; 2511 | 2512 | var obj = {}; 2513 | obj[name] = function(){ 2514 | var results = [], args = arguments, elements = true; 2515 | for (var i = 0, l = this.length; i < l; i++){ 2516 | var element = this[i], result = results[i] = element[name].apply(element, args); 2517 | elements = (elements && typeOf(result) == 'element'); 2518 | } 2519 | return (elements) ? new Elements(results) : results; 2520 | }; 2521 | 2522 | Elements.implement(obj); 2523 | }); 2524 | 2525 | if (!Browser.Element){ 2526 | Element.parent = Object; 2527 | 2528 | Element.Prototype = {'$family': Function.from('element').hide()}; 2529 | 2530 | Element.mirror(function(name, method){ 2531 | Element.Prototype[name] = method; 2532 | }); 2533 | } 2534 | 2535 | Element.Constructors = {}; 2536 | 2537 | //<1.2compat> 2538 | 2539 | Element.Constructors = new Hash; 2540 | 2541 | // 2542 | 2543 | var IFrame = new Type('IFrame', function(){ 2544 | var params = Array.link(arguments, { 2545 | properties: Type.isObject, 2546 | iframe: function(obj){ 2547 | return (obj != null); 2548 | } 2549 | }); 2550 | 2551 | var props = params.properties || {}, iframe; 2552 | if (params.iframe) iframe = document.id(params.iframe); 2553 | var onload = props.onload || function(){}; 2554 | delete props.onload; 2555 | props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.generateUID()].pick(); 2556 | iframe = new Element(iframe || 'iframe', props); 2557 | 2558 | var onLoad = function(){ 2559 | onload.call(iframe.contentWindow); 2560 | }; 2561 | 2562 | if (window.frames[props.id]) onLoad(); 2563 | else iframe.addListener('load', onLoad); 2564 | return iframe; 2565 | }); 2566 | 2567 | var Elements = this.Elements = function(nodes){ 2568 | if (nodes && nodes.length){ 2569 | var uniques = {}, node; 2570 | for (var i = 0; node = nodes[i++];){ 2571 | var uid = Slick.uidOf(node); 2572 | if (!uniques[uid]){ 2573 | uniques[uid] = true; 2574 | this.push(node); 2575 | } 2576 | } 2577 | } 2578 | }; 2579 | 2580 | Elements.prototype = {length: 0}; 2581 | Elements.parent = Array; 2582 | 2583 | new Type('Elements', Elements).implement({ 2584 | 2585 | filter: function(filter, bind){ 2586 | if (!filter) return this; 2587 | return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){ 2588 | return item.match(filter); 2589 | } : filter, bind)); 2590 | }.protect(), 2591 | 2592 | push: function(){ 2593 | var length = this.length; 2594 | for (var i = 0, l = arguments.length; i < l; i++){ 2595 | var item = document.id(arguments[i]); 2596 | if (item) this[length++] = item; 2597 | } 2598 | return (this.length = length); 2599 | }.protect(), 2600 | 2601 | concat: function(){ 2602 | var newElements = new Elements(this); 2603 | for (var i = 0, l = arguments.length; i < l; i++){ 2604 | var item = arguments[i]; 2605 | if (Type.isEnumerable(item)) newElements.append(item); 2606 | else newElements.push(item); 2607 | } 2608 | return newElements; 2609 | }.protect(), 2610 | 2611 | append: function(collection){ 2612 | for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]); 2613 | return this; 2614 | }.protect(), 2615 | 2616 | empty: function(){ 2617 | while (this.length) delete this[--this.length]; 2618 | return this; 2619 | }.protect() 2620 | 2621 | }); 2622 | 2623 | (function(){ 2624 | 2625 | // FF, IE 2626 | var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2}; 2627 | 2628 | splice.call(object, 1, 1); 2629 | if (object[1] == 1) Elements.implement('splice', function(){ 2630 | var length = this.length; 2631 | splice.apply(this, arguments); 2632 | while (length >= this.length) delete this[length--]; 2633 | return this; 2634 | }.protect()); 2635 | 2636 | Elements.implement(Array.prototype); 2637 | 2638 | Array.mirror(Elements); 2639 | 2640 | /**/ 2641 | var createElementAcceptsHTML; 2642 | try { 2643 | var x = document.createElement(''); 2644 | createElementAcceptsHTML = (x.name == 'x'); 2645 | } catch(e){} 2646 | 2647 | var escapeQuotes = function(html){ 2648 | return ('' + html).replace(/&/g, '&').replace(/"/g, '"'); 2649 | }; 2650 | /**/ 2651 | 2652 | Document.implement({ 2653 | 2654 | newElement: function(tag, props){ 2655 | if (props && props.checked != null) props.defaultChecked = props.checked; 2656 | /**/// Fix for readonly name and type properties in IE < 8 2657 | if (createElementAcceptsHTML && props){ 2658 | tag = '<' + tag; 2659 | if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"'; 2660 | if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"'; 2661 | tag += '>'; 2662 | delete props.name; 2663 | delete props.type; 2664 | } 2665 | /**/ 2666 | return this.id(this.createElement(tag)).set(props); 2667 | } 2668 | 2669 | }); 2670 | 2671 | })(); 2672 | 2673 | Document.implement({ 2674 | 2675 | newTextNode: function(text){ 2676 | return this.createTextNode(text); 2677 | }, 2678 | 2679 | getDocument: function(){ 2680 | return this; 2681 | }, 2682 | 2683 | getWindow: function(){ 2684 | return this.window; 2685 | }, 2686 | 2687 | id: (function(){ 2688 | 2689 | var types = { 2690 | 2691 | string: function(id, nocash, doc){ 2692 | id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1')); 2693 | return (id) ? types.element(id, nocash) : null; 2694 | }, 2695 | 2696 | element: function(el, nocash){ 2697 | $uid(el); 2698 | if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){ 2699 | Object.append(el, Element.Prototype); 2700 | } 2701 | return el; 2702 | }, 2703 | 2704 | object: function(obj, nocash, doc){ 2705 | if (obj.toElement) return types.element(obj.toElement(doc), nocash); 2706 | return null; 2707 | } 2708 | 2709 | }; 2710 | 2711 | types.textnode = types.whitespace = types.window = types.document = function(zero){ 2712 | return zero; 2713 | }; 2714 | 2715 | return function(el, nocash, doc){ 2716 | if (el && el.$family && el.uid) return el; 2717 | var type = typeOf(el); 2718 | return (types[type]) ? types[type](el, nocash, doc || document) : null; 2719 | }; 2720 | 2721 | })() 2722 | 2723 | }); 2724 | 2725 | if (window.$ == null) Window.implement('$', function(el, nc){ 2726 | return document.id(el, nc, this.document); 2727 | }); 2728 | 2729 | Window.implement({ 2730 | 2731 | getDocument: function(){ 2732 | return this.document; 2733 | }, 2734 | 2735 | getWindow: function(){ 2736 | return this; 2737 | } 2738 | 2739 | }); 2740 | 2741 | [Document, Element].invoke('implement', { 2742 | 2743 | getElements: function(expression){ 2744 | return Slick.search(this, expression, new Elements); 2745 | }, 2746 | 2747 | getElement: function(expression){ 2748 | return document.id(Slick.find(this, expression)); 2749 | } 2750 | 2751 | }); 2752 | 2753 | //<1.2compat> 2754 | 2755 | (function(search, find, match){ 2756 | 2757 | this.Selectors = {}; 2758 | var pseudos = this.Selectors.Pseudo = new Hash(); 2759 | 2760 | var addSlickPseudos = function(){ 2761 | for (var name in pseudos) if (pseudos.hasOwnProperty(name)){ 2762 | Slick.definePseudo(name, pseudos[name]); 2763 | delete pseudos[name]; 2764 | } 2765 | }; 2766 | 2767 | Slick.search = function(context, expression, append){ 2768 | addSlickPseudos(); 2769 | return search.call(this, context, expression, append); 2770 | }; 2771 | 2772 | Slick.find = function(context, expression){ 2773 | addSlickPseudos(); 2774 | return find.call(this, context, expression); 2775 | }; 2776 | 2777 | Slick.match = function(node, selector){ 2778 | addSlickPseudos(); 2779 | return match.call(this, node, selector); 2780 | }; 2781 | 2782 | })(Slick.search, Slick.find, Slick.match); 2783 | 2784 | if (window.$$ == null) Window.implement('$$', function(selector){ 2785 | var elements = new Elements; 2786 | if (arguments.length == 1 && typeof selector == 'string') return Slick.search(this.document, selector, elements); 2787 | var args = Array.flatten(arguments); 2788 | for (var i = 0, l = args.length; i < l; i++){ 2789 | var item = args[i]; 2790 | switch (typeOf(item)){ 2791 | case 'element': elements.push(item); break; 2792 | case 'string': Slick.search(this.document, item, elements); 2793 | } 2794 | } 2795 | return elements; 2796 | }); 2797 | 2798 | // 2799 | 2800 | if (window.$$ == null) Window.implement('$$', function(selector){ 2801 | if (arguments.length == 1){ 2802 | if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements); 2803 | else if (Type.isEnumerable(selector)) return new Elements(selector); 2804 | } 2805 | return new Elements(arguments); 2806 | }); 2807 | 2808 | (function(){ 2809 | 2810 | var collected = {}, storage = {}; 2811 | var props = {input: 'checked', option: 'selected', textarea: 'value'}; 2812 | 2813 | var get = function(uid){ 2814 | return (storage[uid] || (storage[uid] = {})); 2815 | }; 2816 | 2817 | var clean = function(item){ 2818 | if (item.removeEvents) item.removeEvents(); 2819 | if (item.clearAttributes) item.clearAttributes(); 2820 | var uid = item.uid; 2821 | if (uid != null){ 2822 | delete collected[uid]; 2823 | delete storage[uid]; 2824 | } 2825 | return item; 2826 | }; 2827 | 2828 | var camels = ['defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 2829 | 'rowSpan', 'tabIndex', 'useMap' 2830 | ]; 2831 | var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readOnly', 'multiple', 'selected', 2832 | 'noresize', 'defer' 2833 | ]; 2834 | var attributes = { 2835 | 'html': 'innerHTML', 2836 | 'class': 'className', 2837 | 'for': 'htmlFor', 2838 | 'text': (function(){ 2839 | var temp = document.createElement('div'); 2840 | return (temp.innerText == null) ? 'textContent' : 'innerText'; 2841 | })() 2842 | }; 2843 | var readOnly = ['type']; 2844 | var expandos = ['value', 'defaultValue']; 2845 | var uriAttrs = /^(?:href|src|usemap)$/i; 2846 | 2847 | bools = bools.associate(bools); 2848 | camels = camels.associate(camels.map(String.toLowerCase)); 2849 | readOnly = readOnly.associate(readOnly); 2850 | 2851 | Object.append(attributes, expandos.associate(expandos)); 2852 | 2853 | var inserters = { 2854 | 2855 | before: function(context, element){ 2856 | var parent = element.parentNode; 2857 | if (parent) parent.insertBefore(context, element); 2858 | }, 2859 | 2860 | after: function(context, element){ 2861 | var parent = element.parentNode; 2862 | if (parent) parent.insertBefore(context, element.nextSibling); 2863 | }, 2864 | 2865 | bottom: function(context, element){ 2866 | element.appendChild(context); 2867 | }, 2868 | 2869 | top: function(context, element){ 2870 | element.insertBefore(context, element.firstChild); 2871 | } 2872 | 2873 | }; 2874 | 2875 | inserters.inside = inserters.bottom; 2876 | 2877 | //<1.2compat> 2878 | 2879 | Object.each(inserters, function(inserter, where){ 2880 | 2881 | where = where.capitalize(); 2882 | 2883 | var methods = {}; 2884 | 2885 | methods['inject' + where] = function(el){ 2886 | inserter(this, document.id(el, true)); 2887 | return this; 2888 | }; 2889 | 2890 | methods['grab' + where] = function(el){ 2891 | inserter(document.id(el, true), this); 2892 | return this; 2893 | }; 2894 | 2895 | Element.implement(methods); 2896 | 2897 | }); 2898 | 2899 | // 2900 | 2901 | var injectCombinator = function(expression, combinator){ 2902 | if (!expression) return combinator; 2903 | 2904 | expression = Slick.parse(expression); 2905 | 2906 | var expressions = expression.expressions; 2907 | for (var i = expressions.length; i--;) 2908 | expressions[i][0].combinator = combinator; 2909 | 2910 | return expression; 2911 | }; 2912 | 2913 | Element.implement({ 2914 | 2915 | set: function(prop, value){ 2916 | var property = Element.Properties[prop]; 2917 | (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value); 2918 | }.overloadSetter(), 2919 | 2920 | get: function(prop){ 2921 | var property = Element.Properties[prop]; 2922 | return (property && property.get) ? property.get.apply(this) : this.getProperty(prop); 2923 | }.overloadGetter(), 2924 | 2925 | erase: function(prop){ 2926 | var property = Element.Properties[prop]; 2927 | (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop); 2928 | return this; 2929 | }, 2930 | 2931 | setProperty: function(attribute, value){ 2932 | attribute = camels[attribute] || attribute; 2933 | if (value == null) return this.removeProperty(attribute); 2934 | var key = attributes[attribute]; 2935 | (key) ? this[key] = value : 2936 | (bools[attribute]) ? this[attribute] = !!value : this.setAttribute(attribute, '' + value); 2937 | return this; 2938 | }, 2939 | 2940 | setProperties: function(attributes){ 2941 | for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]); 2942 | return this; 2943 | }, 2944 | 2945 | getProperty: function(attribute){ 2946 | attribute = camels[attribute] || attribute; 2947 | var key = attributes[attribute] || readOnly[attribute]; 2948 | return (key) ? this[key] : 2949 | (bools[attribute]) ? !!this[attribute] : 2950 | (uriAttrs.test(attribute) ? this.getAttribute(attribute, 2) : 2951 | (key = this.getAttributeNode(attribute)) ? key.nodeValue : null) || null; 2952 | }, 2953 | 2954 | getProperties: function(){ 2955 | var args = Array.from(arguments); 2956 | return args.map(this.getProperty, this).associate(args); 2957 | }, 2958 | 2959 | removeProperty: function(attribute){ 2960 | attribute = camels[attribute] || attribute; 2961 | var key = attributes[attribute]; 2962 | (key) ? this[key] = '' : 2963 | (bools[attribute]) ? this[attribute] = false : this.removeAttribute(attribute); 2964 | return this; 2965 | }, 2966 | 2967 | removeProperties: function(){ 2968 | Array.each(arguments, this.removeProperty, this); 2969 | return this; 2970 | }, 2971 | 2972 | hasClass: function(className){ 2973 | return this.className.clean().contains(className, ' '); 2974 | }, 2975 | 2976 | addClass: function(className){ 2977 | if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean(); 2978 | return this; 2979 | }, 2980 | 2981 | removeClass: function(className){ 2982 | this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1'); 2983 | return this; 2984 | }, 2985 | 2986 | toggleClass: function(className, force){ 2987 | if (force == null) force = !this.hasClass(className); 2988 | return (force) ? this.addClass(className) : this.removeClass(className); 2989 | }, 2990 | 2991 | adopt: function(){ 2992 | var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length; 2993 | if (length > 1) parent = fragment = document.createDocumentFragment(); 2994 | 2995 | for (var i = 0; i < length; i++){ 2996 | var element = document.id(elements[i], true); 2997 | if (element) parent.appendChild(element); 2998 | } 2999 | 3000 | if (fragment) this.appendChild(fragment); 3001 | 3002 | return this; 3003 | }, 3004 | 3005 | appendText: function(text, where){ 3006 | return this.grab(this.getDocument().newTextNode(text), where); 3007 | }, 3008 | 3009 | grab: function(el, where){ 3010 | inserters[where || 'bottom'](document.id(el, true), this); 3011 | return this; 3012 | }, 3013 | 3014 | inject: function(el, where){ 3015 | inserters[where || 'bottom'](this, document.id(el, true)); 3016 | return this; 3017 | }, 3018 | 3019 | replaces: function(el){ 3020 | el = document.id(el, true); 3021 | el.parentNode.replaceChild(this, el); 3022 | return this; 3023 | }, 3024 | 3025 | wraps: function(el, where){ 3026 | el = document.id(el, true); 3027 | return this.replaces(el).grab(el, where); 3028 | }, 3029 | 3030 | getPrevious: function(expression){ 3031 | return document.id(Slick.find(this, injectCombinator(expression, '!~'))); 3032 | }, 3033 | 3034 | getAllPrevious: function(expression){ 3035 | return Slick.search(this, injectCombinator(expression, '!~'), new Elements); 3036 | }, 3037 | 3038 | getNext: function(expression){ 3039 | return document.id(Slick.find(this, injectCombinator(expression, '~'))); 3040 | }, 3041 | 3042 | getAllNext: function(expression){ 3043 | return Slick.search(this, injectCombinator(expression, '~'), new Elements); 3044 | }, 3045 | 3046 | getFirst: function(expression){ 3047 | return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]); 3048 | }, 3049 | 3050 | getLast: function(expression){ 3051 | return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast()); 3052 | }, 3053 | 3054 | getParent: function(expression){ 3055 | return document.id(Slick.find(this, injectCombinator(expression, '!'))); 3056 | }, 3057 | 3058 | getParents: function(expression){ 3059 | return Slick.search(this, injectCombinator(expression, '!'), new Elements); 3060 | }, 3061 | 3062 | getSiblings: function(expression){ 3063 | return Slick.search(this, injectCombinator(expression, '~~'), new Elements); 3064 | }, 3065 | 3066 | getChildren: function(expression){ 3067 | return Slick.search(this, injectCombinator(expression, '>'), new Elements); 3068 | }, 3069 | 3070 | getWindow: function(){ 3071 | return this.ownerDocument.window; 3072 | }, 3073 | 3074 | getDocument: function(){ 3075 | return this.ownerDocument; 3076 | }, 3077 | 3078 | getElementById: function(id){ 3079 | return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1'))); 3080 | }, 3081 | 3082 | getSelected: function(){ 3083 | this.selectedIndex; // Safari 3.2.1 3084 | return new Elements(Array.from(this.options).filter(function(option){ 3085 | return option.selected; 3086 | })); 3087 | }, 3088 | 3089 | toQueryString: function(){ 3090 | var queryString = []; 3091 | this.getElements('input, select, textarea').each(function(el){ 3092 | var type = el.type; 3093 | if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return; 3094 | 3095 | var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){ 3096 | // IE 3097 | return document.id(opt).get('value'); 3098 | }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value'); 3099 | 3100 | Array.from(value).each(function(val){ 3101 | if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val)); 3102 | }); 3103 | }); 3104 | return queryString.join('&'); 3105 | }, 3106 | 3107 | clone: function(contents, keepid){ 3108 | contents = contents !== false; 3109 | var clone = this.cloneNode(contents); 3110 | var clean = function(node, element){ 3111 | if (!keepid) node.removeAttribute('id'); 3112 | if (Browser.ie){ 3113 | node.clearAttributes(); 3114 | node.mergeAttributes(element); 3115 | node.removeAttribute('uid'); 3116 | if (node.options){ 3117 | var no = node.options, eo = element.options; 3118 | for (var j = no.length; j--;) no[j].selected = eo[j].selected; 3119 | } 3120 | } 3121 | var prop = props[element.tagName.toLowerCase()]; 3122 | if (prop && element[prop]) node[prop] = element[prop]; 3123 | }; 3124 | 3125 | var i; 3126 | if (contents){ 3127 | var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*'); 3128 | for (i = ce.length; i--;) clean(ce[i], te[i]); 3129 | } 3130 | 3131 | clean(clone, this); 3132 | if (Browser.ie){ 3133 | var ts = this.getElementsByTagName('object'), 3134 | cs = clone.getElementsByTagName('object'), 3135 | tl = ts.length, cl = cs.length; 3136 | for (i = 0; i < tl && i < cl; i++) 3137 | cs[i].outerHTML = ts[i].outerHTML; 3138 | } 3139 | return document.id(clone); 3140 | }, 3141 | 3142 | destroy: function(){ 3143 | var children = clean(this).getElementsByTagName('*'); 3144 | Array.each(children, clean); 3145 | Element.dispose(this); 3146 | return null; 3147 | }, 3148 | 3149 | empty: function(){ 3150 | Array.from(this.childNodes).each(Element.dispose); 3151 | return this; 3152 | }, 3153 | 3154 | dispose: function(){ 3155 | return (this.parentNode) ? this.parentNode.removeChild(this) : this; 3156 | }, 3157 | 3158 | match: function(expression){ 3159 | return !expression || Slick.match(this, expression); 3160 | } 3161 | 3162 | }); 3163 | 3164 | var contains = {contains: function(element){ 3165 | return Slick.contains(this, element); 3166 | }}; 3167 | 3168 | if (!document.contains) Document.implement(contains); 3169 | if (!document.createElement('div').contains) Element.implement(contains); 3170 | 3171 | //<1.2compat> 3172 | 3173 | Element.implement('hasChild', function(element){ 3174 | return this !== element && this.contains(element); 3175 | }); 3176 | 3177 | // 3178 | 3179 | [Element, Window, Document].invoke('implement', { 3180 | 3181 | addListener: function(type, fn){ 3182 | if (type == 'unload'){ 3183 | var old = fn, self = this; 3184 | fn = function(){ 3185 | self.removeListener('unload', fn); 3186 | old(); 3187 | }; 3188 | } else { 3189 | collected[this.uid] = this; 3190 | } 3191 | if (this.addEventListener) this.addEventListener(type, fn, false); 3192 | else this.attachEvent('on' + type, fn); 3193 | return this; 3194 | }, 3195 | 3196 | removeListener: function(type, fn){ 3197 | if (this.removeEventListener) this.removeEventListener(type, fn, false); 3198 | else this.detachEvent('on' + type, fn); 3199 | return this; 3200 | }, 3201 | 3202 | retrieve: function(property, dflt){ 3203 | var storage = get(this.uid), prop = storage[property]; 3204 | if (dflt != null && prop == null) prop = storage[property] = dflt; 3205 | return prop != null ? prop : null; 3206 | }, 3207 | 3208 | store: function(property, value){ 3209 | var storage = get(this.uid); 3210 | storage[property] = value; 3211 | return this; 3212 | }, 3213 | 3214 | eliminate: function(property){ 3215 | var storage = get(this.uid); 3216 | delete storage[property]; 3217 | return this; 3218 | } 3219 | 3220 | }); 3221 | 3222 | // IE purge 3223 | if (window.attachEvent && !window.addEventListener) window.addListener('unload', function(){ 3224 | Object.each(collected, clean); 3225 | if (window.CollectGarbage) CollectGarbage(); 3226 | }); 3227 | 3228 | })(); 3229 | 3230 | Element.Properties = {}; 3231 | 3232 | //<1.2compat> 3233 | 3234 | Element.Properties = new Hash; 3235 | 3236 | // 3237 | 3238 | Element.Properties.style = { 3239 | 3240 | set: function(style){ 3241 | this.style.cssText = style; 3242 | }, 3243 | 3244 | get: function(){ 3245 | return this.style.cssText; 3246 | }, 3247 | 3248 | erase: function(){ 3249 | this.style.cssText = ''; 3250 | } 3251 | 3252 | }; 3253 | 3254 | Element.Properties.tag = { 3255 | 3256 | get: function(){ 3257 | return this.tagName.toLowerCase(); 3258 | } 3259 | 3260 | }; 3261 | 3262 | (function(maxLength){ 3263 | if (maxLength != null) Element.Properties.maxlength = Element.Properties.maxLength = { 3264 | get: function(){ 3265 | var maxlength = this.getAttribute('maxLength'); 3266 | return maxlength == maxLength ? null : maxlength; 3267 | } 3268 | }; 3269 | })(document.createElement('input').getAttribute('maxLength')); 3270 | 3271 | Element.Properties.html = (function(){ 3272 | 3273 | var tableTest = Function.attempt(function(){ 3274 | var table = document.createElement('table'); 3275 | table.innerHTML = ''; 3276 | }); 3277 | 3278 | var wrapper = document.createElement('div'); 3279 | 3280 | var translations = { 3281 | table: [1, '', '
'], 3282 | select: [1, ''], 3283 | tbody: [2, '', '
'], 3284 | tr: [3, '', '
'] 3285 | }; 3286 | translations.thead = translations.tfoot = translations.tbody; 3287 | 3288 | var html = { 3289 | set: function(){ 3290 | var html = Array.flatten(arguments).join(''); 3291 | var wrap = (!tableTest && translations[this.get('tag')]); 3292 | if (wrap){ 3293 | var first = wrapper; 3294 | first.innerHTML = wrap[1] + html + wrap[2]; 3295 | for (var i = wrap[0]; i--;) first = first.firstChild; 3296 | this.empty().adopt(first.childNodes); 3297 | } else { 3298 | this.innerHTML = html; 3299 | } 3300 | } 3301 | }; 3302 | 3303 | html.erase = html.set; 3304 | 3305 | return html; 3306 | })(); 3307 | 3308 | 3309 | /* 3310 | --- 3311 | 3312 | name: Object 3313 | 3314 | description: Object generic methods 3315 | 3316 | license: MIT-style license. 3317 | 3318 | requires: Type 3319 | 3320 | provides: [Object, Hash] 3321 | 3322 | ... 3323 | */ 3324 | 3325 | 3326 | Object.extend({ 3327 | 3328 | subset: function(object, keys){ 3329 | var results = {}; 3330 | for (var i = 0, l = keys.length; i < l; i++){ 3331 | var k = keys[i]; 3332 | results[k] = object[k]; 3333 | } 3334 | return results; 3335 | }, 3336 | 3337 | map: function(object, fn, bind){ 3338 | var results = {}; 3339 | for (var key in object){ 3340 | if (object.hasOwnProperty(key)) results[key] = fn.call(bind, object[key], key, object); 3341 | } 3342 | return results; 3343 | }, 3344 | 3345 | filter: function(object, fn, bind){ 3346 | var results = {}; 3347 | Object.each(object, function(value, key){ 3348 | if (fn.call(bind, value, key, object)) results[key] = value; 3349 | }); 3350 | return results; 3351 | }, 3352 | 3353 | every: function(object, fn, bind){ 3354 | for (var key in object){ 3355 | if (object.hasOwnProperty(key) && !fn.call(bind, object[key], key)) return false; 3356 | } 3357 | return true; 3358 | }, 3359 | 3360 | some: function(object, fn, bind){ 3361 | for (var key in object){ 3362 | if (object.hasOwnProperty(key) && fn.call(bind, object[key], key)) return true; 3363 | } 3364 | return false; 3365 | }, 3366 | 3367 | keys: function(object){ 3368 | var keys = []; 3369 | for (var key in object){ 3370 | if (object.hasOwnProperty(key)) keys.push(key); 3371 | } 3372 | return keys; 3373 | }, 3374 | 3375 | values: function(object){ 3376 | var values = []; 3377 | for (var key in object){ 3378 | if (object.hasOwnProperty(key)) values.push(object[key]); 3379 | } 3380 | return values; 3381 | }, 3382 | 3383 | getLength: function(object){ 3384 | return Object.keys(object).length; 3385 | }, 3386 | 3387 | keyOf: function(object, value){ 3388 | for (var key in object){ 3389 | if (object.hasOwnProperty(key) && object[key] === value) return key; 3390 | } 3391 | return null; 3392 | }, 3393 | 3394 | contains: function(object, value){ 3395 | return Object.keyOf(object, value) != null; 3396 | }, 3397 | 3398 | toQueryString: function(object, base){ 3399 | var queryString = []; 3400 | 3401 | Object.each(object, function(value, key){ 3402 | if (base) key = base + '[' + key + ']'; 3403 | var result; 3404 | switch (typeOf(value)){ 3405 | case 'object': result = Object.toQueryString(value, key); break; 3406 | case 'array': 3407 | var qs = {}; 3408 | value.each(function(val, i){ 3409 | qs[i] = val; 3410 | }); 3411 | result = Object.toQueryString(qs, key); 3412 | break; 3413 | default: result = key + '=' + encodeURIComponent(value); 3414 | } 3415 | if (value != null) queryString.push(result); 3416 | }); 3417 | 3418 | return queryString.join('&'); 3419 | } 3420 | 3421 | }); 3422 | 3423 | 3424 | //<1.2compat> 3425 | 3426 | Hash.implement({ 3427 | 3428 | has: Object.prototype.hasOwnProperty, 3429 | 3430 | keyOf: function(value){ 3431 | return Object.keyOf(this, value); 3432 | }, 3433 | 3434 | hasValue: function(value){ 3435 | return Object.contains(this, value); 3436 | }, 3437 | 3438 | extend: function(properties){ 3439 | Hash.each(properties || {}, function(value, key){ 3440 | Hash.set(this, key, value); 3441 | }, this); 3442 | return this; 3443 | }, 3444 | 3445 | combine: function(properties){ 3446 | Hash.each(properties || {}, function(value, key){ 3447 | Hash.include(this, key, value); 3448 | }, this); 3449 | return this; 3450 | }, 3451 | 3452 | erase: function(key){ 3453 | if (this.hasOwnProperty(key)) delete this[key]; 3454 | return this; 3455 | }, 3456 | 3457 | get: function(key){ 3458 | return (this.hasOwnProperty(key)) ? this[key] : null; 3459 | }, 3460 | 3461 | set: function(key, value){ 3462 | if (!this[key] || this.hasOwnProperty(key)) this[key] = value; 3463 | return this; 3464 | }, 3465 | 3466 | empty: function(){ 3467 | Hash.each(this, function(value, key){ 3468 | delete this[key]; 3469 | }, this); 3470 | return this; 3471 | }, 3472 | 3473 | include: function(key, value){ 3474 | if (this[key] == null) this[key] = value; 3475 | return this; 3476 | }, 3477 | 3478 | map: function(fn, bind){ 3479 | return new Hash(Object.map(this, fn, bind)); 3480 | }, 3481 | 3482 | filter: function(fn, bind){ 3483 | return new Hash(Object.filter(this, fn, bind)); 3484 | }, 3485 | 3486 | every: function(fn, bind){ 3487 | return Object.every(this, fn, bind); 3488 | }, 3489 | 3490 | some: function(fn, bind){ 3491 | return Object.some(this, fn, bind); 3492 | }, 3493 | 3494 | getKeys: function(){ 3495 | return Object.keys(this); 3496 | }, 3497 | 3498 | getValues: function(){ 3499 | return Object.values(this); 3500 | }, 3501 | 3502 | toQueryString: function(base){ 3503 | return Object.toQueryString(this, base); 3504 | } 3505 | 3506 | }); 3507 | 3508 | Hash.extend = Object.append; 3509 | 3510 | Hash.alias({indexOf: 'keyOf', contains: 'hasValue'}); 3511 | 3512 | // 3513 | 3514 | 3515 | /* 3516 | --- 3517 | 3518 | name: Event 3519 | 3520 | description: Contains the Event Class, to make the event object cross-browser. 3521 | 3522 | license: MIT-style license. 3523 | 3524 | requires: [Window, Document, Array, Function, String, Object] 3525 | 3526 | provides: Event 3527 | 3528 | ... 3529 | */ 3530 | 3531 | var Event = new Type('Event', function(event, win){ 3532 | if (!win) win = window; 3533 | var doc = win.document; 3534 | event = event || win.event; 3535 | if (event.$extended) return event; 3536 | this.$extended = true; 3537 | var type = event.type, 3538 | target = event.target || event.srcElement, 3539 | page = {}, 3540 | client = {}; 3541 | while (target && target.nodeType == 3) target = target.parentNode; 3542 | 3543 | if (type.indexOf('key') != -1){ 3544 | var code = event.which || event.keyCode; 3545 | var key = Object.keyOf(Event.Keys, code); 3546 | if (type == 'keydown'){ 3547 | var fKey = code - 111; 3548 | if (fKey > 0 && fKey < 13) key = 'f' + fKey; 3549 | } 3550 | if (!key) key = String.fromCharCode(code).toLowerCase(); 3551 | } else if (type.test(/click|mouse|menu/i)){ 3552 | doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body; 3553 | page = { 3554 | x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft, 3555 | y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop 3556 | }; 3557 | client = { 3558 | x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX, 3559 | y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY 3560 | }; 3561 | if (type.test(/DOMMouseScroll|mousewheel/)){ 3562 | var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3; 3563 | } 3564 | var rightClick = (event.which == 3) || (event.button == 2), 3565 | related = null; 3566 | if (type.test(/over|out/)){ 3567 | related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element']; 3568 | var testRelated = function(){ 3569 | while (related && related.nodeType == 3) related = related.parentNode; 3570 | return true; 3571 | }; 3572 | var hasRelated = (Browser.firefox2) ? testRelated.attempt() : testRelated(); 3573 | related = (hasRelated) ? related : null; 3574 | } 3575 | } else if (type.test(/gesture|touch/i)){ 3576 | this.rotation = event.rotation; 3577 | this.scale = event.scale; 3578 | this.targetTouches = event.targetTouches; 3579 | this.changedTouches = event.changedTouches; 3580 | var touches = this.touches = event.touches; 3581 | if (touches && touches[0]){ 3582 | var touch = touches[0]; 3583 | page = {x: touch.pageX, y: touch.pageY}; 3584 | client = {x: touch.clientX, y: touch.clientY}; 3585 | } 3586 | } 3587 | 3588 | return Object.append(this, { 3589 | event: event, 3590 | type: type, 3591 | 3592 | page: page, 3593 | client: client, 3594 | rightClick: rightClick, 3595 | 3596 | wheel: wheel, 3597 | 3598 | relatedTarget: document.id(related), 3599 | target: document.id(target), 3600 | 3601 | code: code, 3602 | key: key, 3603 | 3604 | shift: event.shiftKey, 3605 | control: event.ctrlKey, 3606 | alt: event.altKey, 3607 | meta: event.metaKey 3608 | }); 3609 | }); 3610 | 3611 | Event.Keys = { 3612 | 'enter': 13, 3613 | 'up': 38, 3614 | 'down': 40, 3615 | 'left': 37, 3616 | 'right': 39, 3617 | 'esc': 27, 3618 | 'space': 32, 3619 | 'backspace': 8, 3620 | 'tab': 9, 3621 | 'delete': 46 3622 | }; 3623 | 3624 | //<1.2compat> 3625 | 3626 | Event.Keys = new Hash(Event.Keys); 3627 | 3628 | // 3629 | 3630 | Event.implement({ 3631 | 3632 | stop: function(){ 3633 | return this.stopPropagation().preventDefault(); 3634 | }, 3635 | 3636 | stopPropagation: function(){ 3637 | if (this.event.stopPropagation) this.event.stopPropagation(); 3638 | else this.event.cancelBubble = true; 3639 | return this; 3640 | }, 3641 | 3642 | preventDefault: function(){ 3643 | if (this.event.preventDefault) this.event.preventDefault(); 3644 | else this.event.returnValue = false; 3645 | return this; 3646 | } 3647 | 3648 | }); 3649 | 3650 | 3651 | /* 3652 | --- 3653 | 3654 | name: Element.Event 3655 | 3656 | description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events. 3657 | 3658 | license: MIT-style license. 3659 | 3660 | requires: [Element, Event] 3661 | 3662 | provides: Element.Event 3663 | 3664 | ... 3665 | */ 3666 | 3667 | (function(){ 3668 | 3669 | Element.Properties.events = {set: function(events){ 3670 | this.addEvents(events); 3671 | }}; 3672 | 3673 | [Element, Window, Document].invoke('implement', { 3674 | 3675 | addEvent: function(type, fn){ 3676 | var events = this.retrieve('events', {}); 3677 | if (!events[type]) events[type] = {keys: [], values: []}; 3678 | if (events[type].keys.contains(fn)) return this; 3679 | events[type].keys.push(fn); 3680 | var realType = type, 3681 | custom = Element.Events[type], 3682 | condition = fn, 3683 | self = this; 3684 | if (custom){ 3685 | if (custom.onAdd) custom.onAdd.call(this, fn); 3686 | if (custom.condition){ 3687 | condition = function(event){ 3688 | if (custom.condition.call(this, event)) return fn.call(this, event); 3689 | return true; 3690 | }; 3691 | } 3692 | realType = custom.base || realType; 3693 | } 3694 | var defn = function(){ 3695 | return fn.call(self); 3696 | }; 3697 | var nativeEvent = Element.NativeEvents[realType]; 3698 | if (nativeEvent){ 3699 | if (nativeEvent == 2){ 3700 | defn = function(event){ 3701 | event = new Event(event, self.getWindow()); 3702 | if (condition.call(self, event) === false) event.stop(); 3703 | }; 3704 | } 3705 | this.addListener(realType, defn); 3706 | } 3707 | events[type].values.push(defn); 3708 | return this; 3709 | }, 3710 | 3711 | removeEvent: function(type, fn){ 3712 | var events = this.retrieve('events'); 3713 | if (!events || !events[type]) return this; 3714 | var list = events[type]; 3715 | var index = list.keys.indexOf(fn); 3716 | if (index == -1) return this; 3717 | var value = list.values[index]; 3718 | delete list.keys[index]; 3719 | delete list.values[index]; 3720 | var custom = Element.Events[type]; 3721 | if (custom){ 3722 | if (custom.onRemove) custom.onRemove.call(this, fn); 3723 | type = custom.base || type; 3724 | } 3725 | return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this; 3726 | }, 3727 | 3728 | addEvents: function(events){ 3729 | for (var event in events) this.addEvent(event, events[event]); 3730 | return this; 3731 | }, 3732 | 3733 | removeEvents: function(events){ 3734 | var type; 3735 | if (typeOf(events) == 'object'){ 3736 | for (type in events) this.removeEvent(type, events[type]); 3737 | return this; 3738 | } 3739 | var attached = this.retrieve('events'); 3740 | if (!attached) return this; 3741 | if (!events){ 3742 | for (type in attached) this.removeEvents(type); 3743 | this.eliminate('events'); 3744 | } else if (attached[events]){ 3745 | attached[events].keys.each(function(fn){ 3746 | this.removeEvent(events, fn); 3747 | }, this); 3748 | delete attached[events]; 3749 | } 3750 | return this; 3751 | }, 3752 | 3753 | fireEvent: function(type, args, delay){ 3754 | var events = this.retrieve('events'); 3755 | if (!events || !events[type]) return this; 3756 | args = Array.from(args); 3757 | 3758 | events[type].keys.each(function(fn){ 3759 | if (delay) fn.delay(delay, this, args); 3760 | else fn.apply(this, args); 3761 | }, this); 3762 | return this; 3763 | }, 3764 | 3765 | cloneEvents: function(from, type){ 3766 | from = document.id(from); 3767 | var events = from.retrieve('events'); 3768 | if (!events) return this; 3769 | if (!type){ 3770 | for (var eventType in events) this.cloneEvents(from, eventType); 3771 | } else if (events[type]){ 3772 | events[type].keys.each(function(fn){ 3773 | this.addEvent(type, fn); 3774 | }, this); 3775 | } 3776 | return this; 3777 | } 3778 | 3779 | }); 3780 | 3781 | // IE9 3782 | try { 3783 | if (typeof HTMLElement != 'undefined') 3784 | HTMLElement.prototype.fireEvent = Element.prototype.fireEvent; 3785 | } catch(e){} 3786 | 3787 | Element.NativeEvents = { 3788 | click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons 3789 | mousewheel: 2, DOMMouseScroll: 2, //mouse wheel 3790 | mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement 3791 | keydown: 2, keypress: 2, keyup: 2, //keyboard 3792 | orientationchange: 2, // mobile 3793 | touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch 3794 | gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture 3795 | focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements 3796 | load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window 3797 | error: 1, abort: 1, scroll: 1 //misc 3798 | }; 3799 | 3800 | var check = function(event){ 3801 | var related = event.relatedTarget; 3802 | if (related == null) return true; 3803 | if (!related) return false; 3804 | return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related)); 3805 | }; 3806 | 3807 | Element.Events = { 3808 | 3809 | mouseenter: { 3810 | base: 'mouseover', 3811 | condition: check 3812 | }, 3813 | 3814 | mouseleave: { 3815 | base: 'mouseout', 3816 | condition: check 3817 | }, 3818 | 3819 | mousewheel: { 3820 | base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel' 3821 | } 3822 | 3823 | }; 3824 | 3825 | //<1.2compat> 3826 | 3827 | Element.Events = new Hash(Element.Events); 3828 | 3829 | // 3830 | 3831 | })(); 3832 | 3833 | 3834 | /* 3835 | --- 3836 | 3837 | name: DOMReady 3838 | 3839 | description: Contains the custom event domready. 3840 | 3841 | license: MIT-style license. 3842 | 3843 | requires: [Browser, Element, Element.Event] 3844 | 3845 | provides: [DOMReady, DomReady] 3846 | 3847 | ... 3848 | */ 3849 | 3850 | (function(window, document){ 3851 | 3852 | var ready, 3853 | loaded, 3854 | checks = [], 3855 | shouldPoll, 3856 | timer, 3857 | isFramed = true; 3858 | 3859 | // Thanks to Rich Dougherty 3860 | try { 3861 | isFramed = window.frameElement != null; 3862 | } catch(e){} 3863 | 3864 | var domready = function(){ 3865 | clearTimeout(timer); 3866 | if (ready) return; 3867 | Browser.loaded = ready = true; 3868 | document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check); 3869 | 3870 | document.fireEvent('domready'); 3871 | window.fireEvent('domready'); 3872 | }; 3873 | 3874 | var check = function(){ 3875 | for (var i = checks.length; i--;) if (checks[i]()){ 3876 | domready(); 3877 | return true; 3878 | } 3879 | 3880 | return false; 3881 | }; 3882 | 3883 | var poll = function(){ 3884 | clearTimeout(timer); 3885 | if (!check()) timer = setTimeout(poll, 10); 3886 | }; 3887 | 3888 | document.addListener('DOMContentLoaded', domready); 3889 | 3890 | // doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/ 3891 | var testElement = document.createElement('div'); 3892 | if (testElement.doScroll && !isFramed){ 3893 | checks.push(function(){ 3894 | try { 3895 | testElement.doScroll(); 3896 | return true; 3897 | } catch (e){} 3898 | 3899 | return false; 3900 | }); 3901 | shouldPoll = true; 3902 | } 3903 | 3904 | if (document.readyState) checks.push(function(){ 3905 | var state = document.readyState; 3906 | return (state == 'loaded' || state == 'complete'); 3907 | }); 3908 | 3909 | if ('onreadystatechange' in document) document.addListener('readystatechange', check); 3910 | else shouldPoll = true; 3911 | 3912 | if (shouldPoll) poll(); 3913 | 3914 | Element.Events.domready = { 3915 | onAdd: function(fn){ 3916 | if (ready) fn.call(this); 3917 | } 3918 | }; 3919 | 3920 | // Make sure that domready fires before load 3921 | Element.Events.load = { 3922 | base: 'load', 3923 | onAdd: function(fn){ 3924 | if (loaded && this == window) fn.call(this); 3925 | }, 3926 | condition: function(){ 3927 | if (this == window){ 3928 | domready(); 3929 | delete Element.Events.load; 3930 | } 3931 | 3932 | return true; 3933 | } 3934 | }; 3935 | 3936 | // This is based on the custom load event 3937 | window.addEvent('load', function(){ 3938 | loaded = true; 3939 | }); 3940 | 3941 | })(window, document); 3942 | 3943 | 3944 | /* 3945 | --- 3946 | 3947 | name: Class.Extras 3948 | 3949 | description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks. 3950 | 3951 | license: MIT-style license. 3952 | 3953 | requires: Class 3954 | 3955 | provides: [Class.Extras, Chain, Events, Options] 3956 | 3957 | ... 3958 | */ 3959 | 3960 | (function(){ 3961 | 3962 | this.Chain = new Class({ 3963 | 3964 | $chain: [], 3965 | 3966 | chain: function(){ 3967 | this.$chain.append(Array.flatten(arguments)); 3968 | return this; 3969 | }, 3970 | 3971 | callChain: function(){ 3972 | return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false; 3973 | }, 3974 | 3975 | clearChain: function(){ 3976 | this.$chain.empty(); 3977 | return this; 3978 | } 3979 | 3980 | }); 3981 | 3982 | var removeOn = function(string){ 3983 | return string.replace(/^on([A-Z])/, function(full, first){ 3984 | return first.toLowerCase(); 3985 | }); 3986 | }; 3987 | 3988 | this.Events = new Class({ 3989 | 3990 | $events: {}, 3991 | 3992 | addEvent: function(type, fn, internal){ 3993 | type = removeOn(type); 3994 | 3995 | /*<1.2compat>*/ 3996 | if (fn == $empty) return this; 3997 | /**/ 3998 | 3999 | this.$events[type] = (this.$events[type] || []).include(fn); 4000 | if (internal) fn.internal = true; 4001 | return this; 4002 | }, 4003 | 4004 | addEvents: function(events){ 4005 | for (var type in events) this.addEvent(type, events[type]); 4006 | return this; 4007 | }, 4008 | 4009 | fireEvent: function(type, args, delay){ 4010 | type = removeOn(type); 4011 | var events = this.$events[type]; 4012 | if (!events) return this; 4013 | args = Array.from(args); 4014 | events.each(function(fn){ 4015 | if (delay) fn.delay(delay, this, args); 4016 | else fn.apply(this, args); 4017 | }, this); 4018 | return this; 4019 | }, 4020 | 4021 | removeEvent: function(type, fn){ 4022 | type = removeOn(type); 4023 | var events = this.$events[type]; 4024 | if (events && !fn.internal){ 4025 | var index = events.indexOf(fn); 4026 | if (index != -1) delete events[index]; 4027 | } 4028 | return this; 4029 | }, 4030 | 4031 | removeEvents: function(events){ 4032 | var type; 4033 | if (typeOf(events) == 'object'){ 4034 | for (type in events) this.removeEvent(type, events[type]); 4035 | return this; 4036 | } 4037 | if (events) events = removeOn(events); 4038 | for (type in this.$events){ 4039 | if (events && events != type) continue; 4040 | var fns = this.$events[type]; 4041 | for (var i = fns.length; i--;) this.removeEvent(type, fns[i]); 4042 | } 4043 | return this; 4044 | } 4045 | 4046 | }); 4047 | 4048 | this.Options = new Class({ 4049 | 4050 | setOptions: function(){ 4051 | var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments)); 4052 | if (!this.addEvent) return this; 4053 | for (var option in options){ 4054 | if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue; 4055 | this.addEvent(option, options[option]); 4056 | delete options[option]; 4057 | } 4058 | return this; 4059 | } 4060 | 4061 | }); 4062 | 4063 | })(); 4064 | 4065 | 4066 | /* 4067 | --- 4068 | 4069 | name: Request 4070 | 4071 | description: Powerful all purpose Request Class. Uses XMLHTTPRequest. 4072 | 4073 | license: MIT-style license. 4074 | 4075 | requires: [Object, Element, Chain, Events, Options, Browser] 4076 | 4077 | provides: Request 4078 | 4079 | ... 4080 | */ 4081 | 4082 | (function(){ 4083 | 4084 | var progressSupport = ('onprogress' in new Browser.Request); 4085 | 4086 | var Request = this.Request = new Class({ 4087 | 4088 | Implements: [Chain, Events, Options], 4089 | 4090 | options: {/* 4091 | onRequest: function(){}, 4092 | onLoadstart: function(event, xhr){}, 4093 | onProgress: function(event, xhr){}, 4094 | onComplete: function(){}, 4095 | onCancel: function(){}, 4096 | onSuccess: function(responseText, responseXML){}, 4097 | onFailure: function(xhr){}, 4098 | onException: function(headerName, value){}, 4099 | onTimeout: function(){}, 4100 | user: '', 4101 | password: '',*/ 4102 | url: '', 4103 | data: '', 4104 | headers: { 4105 | 'X-Requested-With': 'XMLHttpRequest', 4106 | 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' 4107 | }, 4108 | async: true, 4109 | format: false, 4110 | method: 'post', 4111 | link: 'ignore', 4112 | isSuccess: null, 4113 | emulation: true, 4114 | urlEncoded: true, 4115 | encoding: 'utf-8', 4116 | evalScripts: false, 4117 | evalResponse: false, 4118 | timeout: 0, 4119 | noCache: false 4120 | }, 4121 | 4122 | initialize: function(options){ 4123 | this.xhr = new Browser.Request(); 4124 | this.setOptions(options); 4125 | this.headers = this.options.headers; 4126 | }, 4127 | 4128 | onStateChange: function(){ 4129 | var xhr = this.xhr; 4130 | if (xhr.readyState != 4 || !this.running) return; 4131 | this.running = false; 4132 | this.status = 0; 4133 | Function.attempt(function(){ 4134 | var status = xhr.status; 4135 | this.status = (status == 1223) ? 204 : status; 4136 | }.bind(this)); 4137 | xhr.onreadystatechange = function(){}; 4138 | clearTimeout(this.timer); 4139 | 4140 | this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML}; 4141 | if (this.options.isSuccess.call(this, this.status)) 4142 | this.success(this.response.text, this.response.xml); 4143 | else 4144 | this.failure(); 4145 | }, 4146 | 4147 | isSuccess: function(){ 4148 | var status = this.status; 4149 | return (status >= 200 && status < 300); 4150 | }, 4151 | 4152 | isRunning: function(){ 4153 | return !!this.running; 4154 | }, 4155 | 4156 | processScripts: function(text){ 4157 | if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text); 4158 | return text.stripScripts(this.options.evalScripts); 4159 | }, 4160 | 4161 | success: function(text, xml){ 4162 | this.onSuccess(this.processScripts(text), xml); 4163 | }, 4164 | 4165 | onSuccess: function(){ 4166 | this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain(); 4167 | }, 4168 | 4169 | failure: function(){ 4170 | this.onFailure(); 4171 | }, 4172 | 4173 | onFailure: function(){ 4174 | this.fireEvent('complete').fireEvent('failure', this.xhr); 4175 | }, 4176 | 4177 | loadstart: function(event){ 4178 | this.fireEvent('loadstart', [event, this.xhr]); 4179 | }, 4180 | 4181 | progress: function(event){ 4182 | this.fireEvent('progress', [event, this.xhr]); 4183 | }, 4184 | 4185 | timeout: function(){ 4186 | this.fireEvent('timeout', this.xhr); 4187 | }, 4188 | 4189 | setHeader: function(name, value){ 4190 | this.headers[name] = value; 4191 | return this; 4192 | }, 4193 | 4194 | getHeader: function(name){ 4195 | return Function.attempt(function(){ 4196 | return this.xhr.getResponseHeader(name); 4197 | }.bind(this)); 4198 | }, 4199 | 4200 | check: function(){ 4201 | if (!this.running) return true; 4202 | switch (this.options.link){ 4203 | case 'cancel': this.cancel(); return true; 4204 | case 'chain': this.chain(this.caller.pass(arguments, this)); return false; 4205 | } 4206 | return false; 4207 | }, 4208 | 4209 | send: function(options){ 4210 | if (!this.check(options)) return this; 4211 | 4212 | this.options.isSuccess = this.options.isSuccess || this.isSuccess; 4213 | this.running = true; 4214 | 4215 | var type = typeOf(options); 4216 | if (type == 'string' || type == 'element') options = {data: options}; 4217 | 4218 | var old = this.options; 4219 | options = Object.append({data: old.data, url: old.url, method: old.method}, options); 4220 | var data = options.data, url = String(options.url), method = options.method.toLowerCase(); 4221 | 4222 | switch (typeOf(data)){ 4223 | case 'element': data = document.id(data).toQueryString(); break; 4224 | case 'object': case 'hash': data = Object.toQueryString(data); 4225 | } 4226 | 4227 | if (this.options.format){ 4228 | var format = 'format=' + this.options.format; 4229 | data = (data) ? format + '&' + data : format; 4230 | } 4231 | 4232 | if (this.options.emulation && !['get', 'post'].contains(method)){ 4233 | var _method = '_method=' + method; 4234 | data = (data) ? _method + '&' + data : _method; 4235 | method = 'post'; 4236 | } 4237 | 4238 | if (this.options.urlEncoded && ['post', 'put'].contains(method)){ 4239 | var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : ''; 4240 | this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding; 4241 | } 4242 | 4243 | if (!url) url = document.location.pathname; 4244 | 4245 | var trimPosition = url.lastIndexOf('/'); 4246 | if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition); 4247 | 4248 | if (this.options.noCache) 4249 | url += (url.contains('?') ? '&' : '?') + String.generateUID(); 4250 | 4251 | if (data && method == 'get'){ 4252 | url += (url.contains('?') ? '&' : '?') + data; 4253 | data = null; 4254 | } 4255 | 4256 | var xhr = this.xhr; 4257 | if (progressSupport){ 4258 | xhr.onloadstart = this.loadstart.bind(this); 4259 | xhr.onprogress = this.progress.bind(this); 4260 | } 4261 | 4262 | xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password); 4263 | if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true; 4264 | 4265 | xhr.onreadystatechange = this.onStateChange.bind(this); 4266 | 4267 | Object.each(this.headers, function(value, key){ 4268 | try { 4269 | xhr.setRequestHeader(key, value); 4270 | } catch (e){ 4271 | this.fireEvent('exception', [key, value]); 4272 | } 4273 | }, this); 4274 | 4275 | this.fireEvent('request'); 4276 | xhr.send(data); 4277 | if (!this.options.async) this.onStateChange(); 4278 | if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this); 4279 | return this; 4280 | }, 4281 | 4282 | cancel: function(){ 4283 | if (!this.running) return this; 4284 | this.running = false; 4285 | var xhr = this.xhr; 4286 | xhr.abort(); 4287 | clearTimeout(this.timer); 4288 | xhr.onreadystatechange = xhr.onprogress = xhr.onloadstart = function(){}; 4289 | this.xhr = new Browser.Request(); 4290 | this.fireEvent('cancel'); 4291 | return this; 4292 | } 4293 | 4294 | }); 4295 | 4296 | var methods = {}; 4297 | ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){ 4298 | methods[method] = function(data){ 4299 | return this.send({ 4300 | data: data, 4301 | method: method 4302 | }); 4303 | }; 4304 | }); 4305 | 4306 | Request.implement(methods); 4307 | 4308 | Element.Properties.send = { 4309 | 4310 | set: function(options){ 4311 | var send = this.get('send').cancel(); 4312 | send.setOptions(options); 4313 | return this; 4314 | }, 4315 | 4316 | get: function(){ 4317 | var send = this.retrieve('send'); 4318 | if (!send){ 4319 | send = new Request({ 4320 | data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action') 4321 | }); 4322 | this.store('send', send); 4323 | } 4324 | return send; 4325 | } 4326 | 4327 | }; 4328 | 4329 | Element.implement({ 4330 | 4331 | send: function(url){ 4332 | var sender = this.get('send'); 4333 | sender.send({data: this, url: url || sender.options.url}); 4334 | return this; 4335 | } 4336 | 4337 | }); 4338 | 4339 | })(); 4340 | 4341 | /* 4342 | --- 4343 | 4344 | name: Request.HTML 4345 | 4346 | description: Extends the basic Request Class with additional methods for interacting with HTML responses. 4347 | 4348 | license: MIT-style license. 4349 | 4350 | requires: [Element, Request] 4351 | 4352 | provides: Request.HTML 4353 | 4354 | ... 4355 | */ 4356 | 4357 | Request.HTML = new Class({ 4358 | 4359 | Extends: Request, 4360 | 4361 | options: { 4362 | update: false, 4363 | append: false, 4364 | evalScripts: true, 4365 | filter: false, 4366 | headers: { 4367 | Accept: 'text/html, application/xml, text/xml, */*' 4368 | } 4369 | }, 4370 | 4371 | success: function(text){ 4372 | var options = this.options, response = this.response; 4373 | 4374 | response.html = text.stripScripts(function(script){ 4375 | response.javascript = script; 4376 | }); 4377 | 4378 | var match = response.html.match(/]*>([\s\S]*?)<\/body>/i); 4379 | if (match) response.html = match[1]; 4380 | var temp = new Element('div').set('html', response.html); 4381 | 4382 | response.tree = temp.childNodes; 4383 | response.elements = temp.getElements('*'); 4384 | 4385 | if (options.filter) response.tree = response.elements.filter(options.filter); 4386 | if (options.update) document.id(options.update).empty().set('html', response.html); 4387 | else if (options.append) document.id(options.append).adopt(temp.getChildren()); 4388 | if (options.evalScripts) Browser.exec(response.javascript); 4389 | 4390 | this.onSuccess(response.tree, response.elements, response.html, response.javascript); 4391 | } 4392 | 4393 | }); 4394 | 4395 | Element.Properties.load = { 4396 | 4397 | set: function(options){ 4398 | var load = this.get('load').cancel(); 4399 | load.setOptions(options); 4400 | return this; 4401 | }, 4402 | 4403 | get: function(){ 4404 | var load = this.retrieve('load'); 4405 | if (!load){ 4406 | load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'}); 4407 | this.store('load', load); 4408 | } 4409 | return load; 4410 | } 4411 | 4412 | }; 4413 | 4414 | Element.implement({ 4415 | 4416 | load: function(){ 4417 | this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString})); 4418 | return this; 4419 | } 4420 | 4421 | }); 4422 | 4423 | -------------------------------------------------------------------------------- /Demos/mootools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | History for MooTools 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | Back 19 | Load index.html 20 | Load other.html 21 |
22 |
23 | Moo! 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /Demos/other.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | History for MooTools 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 22 |
23 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent gravida sollicitudin mi ornare elementum. Vestibulum malesuada, justo eu ornare lacinia, nulla massa vestibulum leo, ac mollis tellus libero id tortor. Donec vitae purus eget ligula laoreet lacinia in quis diam. Etiam ultrices nulla a sapien hendrerit tempus. Suspendisse volutpat ultricies quam, nec mollis ligula gravida non. In consectetur rhoncus sem, quis egestas felis iaculis vitae. Proin condimentum lacus sit amet leo vestibulum interdum. Etiam ultrices, metus at egestas sodales, nunc augue facilisis ante, ac laoreet nulla augue ut arcu. Nullam consectetur, diam eu consectetur tristique, lectus velit vestibulum sapien, in auctor arcu lacus fermentum lacus. Vivamus mi elit, dapibus eget dictum quis, tincidunt sit amet elit. Aenean eget ultricies enim. In ac sapien quis ante condimentum mattis. Aenean venenatis luctus sollicitudin. Suspendisse arcu augue, consequat pretium dapibus quis, hendrerit non tellus. 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /Demos/style.css: -------------------------------------------------------------------------------- 1 | /* reset.css */ 2 | html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;} 3 | body {line-height:1.5;} 4 | table {border-collapse:separate;border-spacing:0;} 5 | caption, th, td {text-align:left;font-weight:normal;} 6 | table, td, th {vertical-align:middle;} 7 | blockquote:before, blockquote:after, q:before, q:after {content:"";} 8 | blockquote, q {quotes:"" "";} 9 | a img {border:none;} 10 | 11 | /* typography.css */ 12 | html {font-size:100.01%;} 13 | body {font-size:80%;color:#222;background:#fff;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;} 14 | h1, h2, h3, h4, h5, h6 {font-weight:normal;color:#111;} 15 | h1 {font-size:3em;line-height:1;margin-bottom:0.5em;} 16 | h2 {font-size:2em;margin-bottom:0.75em;} 17 | h3 {font-size:1.5em;line-height:1;margin-bottom:1em;} 18 | h4 {font-size:1.2em;line-height:1.25;margin-bottom:1.25em;} 19 | h5 {font-size:1em;font-weight:bold;margin-bottom:1.5em;} 20 | h6 {font-size:1em;font-weight:bold;} 21 | h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin:0;} 22 | p {margin:0 0 1.5em;} 23 | p img.left {float:left;margin:1.5em 1.5em 1.5em 0;padding:0;} 24 | p img.right {float:right;margin:1.5em 0 1.5em 1.5em;} 25 | a:focus, a:hover {color:#000;} 26 | a {color:#009;text-decoration:underline;} 27 | blockquote {margin:1.5em;color:#666;font-style:italic;} 28 | strong {font-weight:bold;} 29 | em, dfn {font-style:italic;} 30 | dfn {font-weight:bold;} 31 | sup, sub {line-height:0;} 32 | abbr, acronym {border-bottom:1px dotted #666;} 33 | address {margin:0 0 1.5em;font-style:italic;} 34 | del {color:#666;} 35 | pre {margin:1.5em 0;white-space:pre;} 36 | pre, code, tt {font:1em 'andale mono', 'lucida console', monospace;line-height:1.5;} 37 | li ul, li ol {margin:0;} 38 | ul, ol {margin:0 1.5em 1.5em 0;padding-left:3.333em;} 39 | ul {list-style-type:disc;} 40 | ol {list-style-type:decimal;} 41 | 42 | body { 43 | background-color: #f2f2f2; 44 | } 45 | 46 | body > div { 47 | margin: 10px; 48 | } 49 | 50 | a.link, div#more a { 51 | font-weight: bold; 52 | text-align: center; 53 | margin: 5px 0; 54 | display: block; 55 | background: #4791d9; 56 | text-decoration: none; 57 | color: #fff; 58 | padding: 4px; 59 | outline: 0; 60 | border: 0; 61 | -moz-border-radius: 4px; 62 | -webkit-border-radius: 4px; 63 | -o-border-radius: 4px; 64 | -ms-border-radius: 4px; 65 | -khtml-border-radius: 4px; 66 | border-radius: 4px; 67 | -moz-transition-duration: 0.25s; 68 | -webkit-transition-duration: 0.25s; 69 | -o-transition-duration: 0.25s; 70 | transition-duration: 0.25s; 71 | -webkit-transition-property: -webkit-transform; 72 | -moz-transition-property: -moz-transform; 73 | -o-transition-property: -o-transform; 74 | transition-property: transform; 75 | -webkit-transform: scale(1) rotate(0); 76 | -moz-transform: scale(1) rotate(0); 77 | -o-transform: scale(1) rotate(0); 78 | transform: scale(1) rotate(0); 79 | } 80 | 81 | a.link:hover, div#more a:hover { 82 | background: #4791d9; 83 | text-decoration: none; 84 | color: #fff; 85 | -webkit-transform: scale(1.05) rotate(-1deg); 86 | -moz-transform: scale(1.05) rotate(-1deg); 87 | -o-transform: scale(1.05) rotate(-1deg); 88 | transform: scale(1.05) rotate(-1deg); 89 | } 90 | 91 | a.link:nth-child(2n):hover, div#more a:nth-child(2n):hover { 92 | -webkit-transform: scale(1.05) rotate(1deg); 93 | -moz-transform: scale(1.05) rotate(1deg); 94 | -o-transform: scale(1.05) rotate(1deg); 95 | transform: scale(1.05) rotate(1deg); 96 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MooTools History - MIT License 2 | 3 | Copyright (c) 2011 Christoph Pojer 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | History 2 | ======= 3 | 4 | History Management via popstate or hashchange. Replaces the URL of the page without a reload and falls back to Hashchange on older 5 | browsers. 6 | 7 | ![Screenshot](http://cpojer.net/Logo/history.png) 8 | 9 | This Plugin is part of MooTools [PowerTools!](http://cpojer.net/PowerTools). 10 | 11 | * [Build PowerTools!](http://cpojer.net/PowerTools) 12 | * [Fork PowerTools!](https://github.com/cpojer/PowerTools) 13 | 14 | Build 15 | ----- 16 | 17 | Build via [Packager](http://github.com/kamicane/packager), requires [MooTools Core](http://github.com/mootools/mootools-core) and [MooTools Class-Extras](http://github.com/cpojer/mootools-class-extras) to be registered to Packager already 18 | 19 | packager register /path/to/history 20 | packager build History/* > history.js 21 | 22 | To build this plugin without external dependencies use 23 | 24 | packager build History/* +use-only History > history.js 25 | 26 | Demo 27 | ---- 28 | 29 | See Demos/index.html 30 | 31 | How To Use 32 | ---------- 33 | 34 | This plugin provides a History object. 35 | 36 | Add a new URL 37 | 38 | History.push('/some/url'); 39 | 40 | Handle URL Change (via back button etc.) 41 | 42 | History.addEvent('change', function(url){ 43 | // The URL has changed! 44 | 45 | // Let's reload the content 46 | new Request(url).get(); 47 | }); 48 | 49 | Shortcuts (more or less useful) 50 | 51 | History.hasPushState(); // true if the Browser is modern and supports window.history.pushState 52 | 53 | History.back(); // Goes back 54 | 55 | History.forward(); // Goes forward 56 | 57 | Plugins 58 | ------- 59 | 60 | In addition there is also an optional module that can handle the initial state of your application. It redirects the browser to the URL a user would expect, based on browser features. 61 | 62 | Use it to wrap your whole page code or put it on top of your scripts (right after loading History) 63 | 64 | if (!History.handleInitialState()) (function(){ 65 | // Execute normal page load activities such as add something to domready etc. 66 | })(); 67 | 68 | Or standalone: 69 | 70 | History.handleInitialState(); 71 | 72 | This method will always return false if the user has pushState and only will return true if the page redirects the browser to another page in case the hashchange event is being used (ie. in older browsers). 73 | 74 | If you use this plugin this is what happens in your application: 75 | 76 | // If the user has pushState and goes to index.html 77 | nothing happens, returns false 78 | // If the user has pushState and goes to index.html#other.html 79 | the history change event is being fired and changes to 'other.html', returns false 80 | 81 | // If the user doesn't have pushState and goes to index.html 82 | nothing happens, returns false 83 | // If the user doesn't have pushState and goes to some/path/page.html 84 | the page redirects and reloads the page to example.com/#some/path/page.html, returns true 85 | // If the user doesn't have pushState and goes to some/path/page.html#another/page.html 86 | the page redirects and reloads the page to example.com/#another/page.html, returns true 87 | 88 | In addition the method takes the argument of the baseURL of your application. In an application that acts on the root of your web server you can omit this argument. If your page lives in a subfolder of your application you can specify it as a base parameter: 89 | 90 | History.handleInitialState('/blog/'); 91 | 92 | For more information on this plugin please see the demo. 93 | 94 | Notes 95 | ----- 96 | 97 | The `onChange` event does not fire for the initial page load. The HTML5 specification for the native `popstate` event suggests that `popstate` should be fired when the page initially loads. However, as of February 2011, browser implementations diverge in this aspect. The `onChange` event of History is designed to never fire for the initial page load. Handling this state should be done manually on a per-app basis as the use cases greatly vary. A pointer on how to handle the initial state can be the optional History.handleInitialState plugin as explained above. 98 | -------------------------------------------------------------------------------- /Source/History.handleInitialState.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: History.handleInitialState 5 | 6 | description: Provides a helper method to handle the initial state of your application. 7 | 8 | authors: Christoph Pojer (@cpojer) 9 | 10 | license: MIT-style license. 11 | 12 | requires: [History] 13 | 14 | provides: History.handleInitialState 15 | 16 | ... 17 | */ 18 | 19 | History.handleInitialState = function(base){ 20 | if (!base) base = ''; 21 | var location = window.location, 22 | pathname = History.cleanURL(location.href).substr(base.length), 23 | hash = location.hash, 24 | hasPushState = History.hasPushState(); 25 | 26 | if (!hasPushState && pathname.length > 1){ 27 | window.location = (base || '/') + '#' + pathname; 28 | return true; 29 | } 30 | 31 | if (!hash || hash.length <= 1) return false; 32 | if (hasPushState){ 33 | (function(){ 34 | History.push(History.cleanURL(hash.substr(1))); 35 | }).delay(1); 36 | return false; 37 | } 38 | 39 | if (!pathname || pathname == '/') return false; 40 | window.location = (base || '/') + hash; 41 | return true; 42 | }; 43 | -------------------------------------------------------------------------------- /Source/History.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: History 5 | 6 | description: History Management via popstate or hashchange. 7 | 8 | authors: Christoph Pojer (@cpojer) 9 | 10 | license: MIT-style license. 11 | 12 | requires: [Core/Events, Core/Element.Event, Class-Extras/Class.Binds] 13 | 14 | provides: History 15 | 16 | ... 17 | */ 18 | 19 | (function(){ 20 | 21 | var events = Element.NativeEvents, 22 | location = window.location, 23 | cleanURL = function(url){ 24 | if (url && url.match(/^https?:\/\//)) url = '/' + url.split('/').slice(3).join('/'); 25 | return url; 26 | }, 27 | base = cleanURL(location.href), 28 | history = window.history, 29 | hasPushState = ('pushState' in history), 30 | event = hasPushState ? 'popstate' : 'hashchange'; 31 | 32 | this.History = new new Class({ 33 | 34 | Implements: [Class.Binds, Events], 35 | 36 | initialize: hasPushState ? function(){ 37 | events[event] = 2; 38 | window.addEvent(event, this.bound('pop')); 39 | } : function(){ 40 | events[event] = 1; 41 | window.addEvent(event, this.bound('pop')); 42 | 43 | this.hash = location.hash; 44 | var hashchange = ('onhashchange' in window); 45 | if (!(hashchange && (document.documentMode === undefined || document.documentMode > 7))) 46 | this.timer = this.check.periodical(200, this); 47 | }, 48 | 49 | cleanURL: cleanURL, 50 | 51 | push: hasPushState ? function(url, title, state){ 52 | url = cleanURL(url); 53 | if (base && base != url) base = null; 54 | 55 | history.pushState(state || null, title || null, url); 56 | this.onChange(url, state); 57 | } : function(url){ 58 | location.hash = cleanURL(url); 59 | }, 60 | 61 | replace: hasPushState ? function(url, title, state){ 62 | history.replaceState(state || null, title || null, cleanURL(url)); 63 | } : function(url){ 64 | url = cleanURL(url); 65 | this.hash = '#' + url; 66 | this.push(url); 67 | }, 68 | 69 | pop: hasPushState ? function(event){ 70 | var url = cleanURL(location.href); 71 | if (url == base){ 72 | base = null; 73 | return; 74 | } 75 | this.onChange(url, event.event.state); 76 | } : function(){ 77 | var hash = location.hash; 78 | if (this.hash == hash) return; 79 | 80 | this.hash = hash; 81 | this.onChange(cleanURL(hash.substr(1))); 82 | }, 83 | 84 | onChange: function(url, state){ 85 | this.fireEvent('change', [url, state || {}]); 86 | }, 87 | 88 | back: function(){ 89 | history.back(); 90 | }, 91 | 92 | forward: function(){ 93 | history.forward(); 94 | }, 95 | 96 | getPath: function(){ 97 | return cleanURL(hasPushState ? location.href : location.hash.substr(1)); 98 | }, 99 | 100 | hasPushState: function(){ 101 | return hasPushState; 102 | }, 103 | 104 | check: function(){ 105 | if (this.hash != location.hash) this.pop(); 106 | } 107 | 108 | }); 109 | 110 | }).call(this); 111 | -------------------------------------------------------------------------------- /package.yml: -------------------------------------------------------------------------------- 1 | name: "History" 2 | 3 | exports: "history.js" 4 | 5 | web: "[cpojer.net](http://cpojer.net)" 6 | demo: http://cpojer.net/MooTools/history/Demos/ 7 | 8 | description: "History Management via popstate or hashchange." 9 | 10 | license: "[MIT License](http://mootools.net/license.txt)" 11 | 12 | copyright: "© [Christoph Pojer](http://cpojer.net/)" 13 | 14 | author: "cpojer" 15 | authors: "[Christoph Pojer](http://cpojer.net)" 16 | category: Utilities 17 | tags: [history, pushState, hashchange] 18 | 19 | sources: 20 | - "Source/History.js" 21 | - "Source/History.handleInitialState.js" 22 | --------------------------------------------------------------------------------