Advertisement
decembre

GM - Flickr Quit Multiple Groups - ALESADAM - DECEMBRE tweak

Apr 27th, 2015
913
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name           Flickr - Quit Multiple Groups - by ALESADAM ( DECEMBRE tweak OK No BETA 2019)
  3. // @namespace      http://www.flickr.com/alesadam
  4. // @description    Allows to quit mutliple groups in one action, from your Groups page.
  5. // @creator        Aleas Dam (http://flickr.com/alesadam/)
  6. // @date           14/06/2012
  7. // @version        2.2
  8. // @modified       Sep. 20, 2013
  9. //
  10.  
  11. // @include        http*://*flickr.com/groups?view=tn
  12. // @include        http*://*flickr.com/groups?view=ls
  13.  
  14. // @exclude    http*://*flickr.com/photos/*/map*
  15. // @exclude    http*://*flickr.com/photos/*/page*
  16. // @exclude    http*://*flickr.com/groups/*/pool/*
  17.  
  18.  
  19. // @match          https://www.flickr.com/groups/*
  20. // @match          https://www.flickr.com/groups*
  21. //
  22. // @downloadURL    https://userscripts.org/scripts/source/136076.user.js
  23. // @updateURL      https://userscripts.org/scripts/source/136076.meta.js
  24. // @grant          none
  25. // @run-at     document-end
  26. // ==/UserScript==
  27. //
  28.  
  29. (function () {
  30. var FQMGversion = "2.2";
  31.  
  32. if (!document.location.href.match(/www.flickr.com\/groups\/?(?:\?.*)?$/)) {
  33.     return;
  34. }
  35.  
  36. var images = {
  37. //    error: 'http://l.yimg.com/g/images/icon_error_x_small.png',
  38.     error: 'https://www.flickr.com/images/icon_error_x_small.png',
  39.     success: 'https://l.yimg.com/g/images/icon_check_small.png',
  40.     updating: 'https://www.flickr.com/images/pulser2.gif'
  41. };
  42.  
  43. var scriptNumber = 136076;
  44. var installPage = 'http://userscripts.org/scripts/show/' + scriptNumber;
  45.  
  46. // Greased MooTools inline for lack of @require support in Chrome
  47.  
  48. /*
  49. ---
  50.  
  51. script: Core.js
  52.  
  53. description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts.
  54.  
  55. license: MIT-style license.
  56.  
  57. copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
  58.  
  59. authors: The MooTools production team (http://mootools.net/developers/)
  60.  
  61. inspiration:
  62. - 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)
  63. - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
  64.  
  65. provides: [MooTools, Native, Hash.base, Array.each, $util]
  66.  
  67. ...
  68. */
  69.  
  70. var MooTools = {
  71.     'version': '1.2.5dev',
  72.     'build': '168759f5904bfdaeafd6b1c0d1be16cd78b5d5c6'
  73. };
  74.  
  75. var Native = function(options){
  76.     options = options || {};
  77.     var name = options.name;
  78.     var legacy = options.legacy;
  79.     var protect = options.protect;
  80.     var methods = options.implement;
  81.     var generics = options.generics;
  82.     var initialize = options.initialize;
  83.     var afterImplement = options.afterImplement || function(){};
  84.     var object = initialize || legacy;
  85.     generics = generics !== false;
  86.  
  87.     object.constructor = Native;
  88.     object.$family = {name: 'native'};
  89.     if (legacy && initialize) object.prototype = legacy.prototype;
  90.     if (!object.prototype) object.prototype = {};
  91.     object.prototype.constructor = object;
  92.  
  93.     if (name){
  94.         var family = name.toLowerCase();
  95.         object.prototype.$family = {name: family};
  96.         Native.typize(object, family);
  97.     }
  98.  
  99.     var add = function(obj, name, method, force){
  100.         if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
  101.         if (generics) Native.genericize(obj, name, protect);
  102.         afterImplement.call(obj, name, method);
  103.         return obj;
  104.     };
  105.  
  106.     object.alias = function(a1, a2, a3){
  107.         if (typeof a1 == 'string'){
  108.             var pa1 = this.prototype[a1];
  109.             if ((a1 = pa1)) return add(this, a2, a1, a3);
  110.         }
  111.         for (var a in a1) this.alias(a, a1[a], a2);
  112.         return this;
  113.     };
  114.  
  115.     object.implement = function(a1, a2, a3){
  116.         if (typeof a1 == 'string') return add(this, a1, a2, a3);
  117.         for (var p in a1) add(this, p, a1[p], a2);
  118.         return this;
  119.     };
  120.  
  121.     if (methods) object.implement(methods);
  122.  
  123.     return object;
  124. };
  125.  
  126. Native.genericize = function(object, property, check){
  127.     if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
  128.         var args = Array.prototype.slice.call(arguments);
  129.         return object.prototype[property].apply(args.shift(), args);
  130.     };
  131. };
  132.  
  133. Native.implement = function(objects, properties){
  134.     for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
  135. };
  136.  
  137. Native.typize = function(object, family){
  138.     if (!object.type) object.type = function(item){
  139.         return ($type(item) === family);
  140.     };
  141. };
  142.  
  143. (function(){
  144.     var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
  145.     for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});
  146.  
  147.     var types = {'boolean': Boolean, 'native': Native, 'object': Object};
  148.     for (var t in types) Native.typize(types[t], t);
  149.  
  150.     var generics = {
  151.         'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
  152.         'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
  153.     };
  154.     for (var g in generics){
  155.         for (var i = generics[g].length; i--;) Native.genericize(natives[g], generics[g][i], true);
  156.     }
  157. })();
  158.  
  159. var Hash = new Native({
  160.  
  161.     name: 'Hash',
  162.  
  163.     initialize: function(object){
  164.         if ($type(object) == 'hash') object = $unlink(object.getClean());
  165.         for (var key in object) this[key] = object[key];
  166.         return this;
  167.     }
  168.  
  169. });
  170.  
  171. Hash.implement({
  172.  
  173.     forEach: function(fn, bind){
  174.         for (var key in this){
  175.             if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
  176.         }
  177.     },
  178.  
  179.     getClean: function(){
  180.         var clean = {};
  181.         for (var key in this){
  182.             if (this.hasOwnProperty(key)) clean[key] = this[key];
  183.         }
  184.         return clean;
  185.     },
  186.  
  187.     getLength: function(){
  188.         var length = 0;
  189.         for (var key in this){
  190.             if (this.hasOwnProperty(key)) length++;
  191.         }
  192.         return length;
  193.     }
  194.  
  195. });
  196.  
  197. Hash.alias('forEach', 'each');
  198.  
  199. Array.implement({
  200.  
  201.     forEach: function(fn, bind){
  202.         for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
  203.     }
  204.  
  205. });
  206.  
  207. Array.alias('forEach', 'each');
  208.  
  209. function $A(iterable){
  210.     if (iterable.item){
  211.         var l = iterable.length, array = new Array(l);
  212.         while (l--) array[l] = iterable[l];
  213.         return array;
  214.     }
  215.     return Array.prototype.slice.call(iterable);
  216. };
  217.  
  218. function $arguments(i){
  219.     return function(){
  220.         return arguments[i];
  221.     };
  222. };
  223.  
  224. function $chk(obj){
  225.     return !!(obj || obj === 0);
  226. };
  227.  
  228. function $clear(timer){
  229.     clearTimeout(timer);
  230.     clearInterval(timer);
  231.     return null;
  232. };
  233.  
  234. function $defined(obj){
  235.     return (obj != undefined);
  236. };
  237.  
  238. function $each(iterable, fn, bind){
  239.     var type = $type(iterable);
  240.     ((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
  241. };
  242.  
  243. function $empty(){};
  244.  
  245. function $extend(original, extended){
  246.     for (var key in (extended || {})) original[key] = extended[key];
  247.     return original;
  248. };
  249.  
  250. function $H(object){
  251.     return new Hash(object);
  252. };
  253.  
  254. function $lambda(value){
  255.     return ($type(value) == 'function') ? value : function(){
  256.         return value;
  257.     };
  258. };
  259.  
  260. function $merge(){
  261.     var args = Array.slice(arguments);
  262.     args.unshift({});
  263.     return $mixin.apply(null, args);
  264. };
  265.  
  266. function $mixin(mix){
  267.     for (var i = 1, l = arguments.length; i < l; i++){
  268.         var object = arguments[i];
  269.         if ($type(object) != 'object') continue;
  270.         for (var key in object){
  271.             var op = object[key], mp = mix[key];
  272.             mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op);
  273.         }
  274.     }
  275.     return mix;
  276. };
  277.  
  278. function $pick(){
  279.     for (var i = 0, l = arguments.length; i < l; i++){
  280.         if (arguments[i] != undefined) return arguments[i];
  281.     }
  282.     return null;
  283. };
  284.  
  285. function $random(min, max){
  286.     return Math.floor(Math.random() * (max - min + 1) + min);
  287. };
  288.  
  289. function $splat(obj){
  290.     var type = $type(obj);
  291.     return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
  292. };
  293.  
  294. var $time = Date.now || function(){
  295.     return +new Date;
  296. };
  297.  
  298. function $try(){
  299.     for (var i = 0, l = arguments.length; i < l; i++){
  300.         try {
  301.             return arguments[i]();
  302.         } catch(e){}
  303.     }
  304.     return null;
  305. };
  306.  
  307. function $type(obj){
  308.     if (obj == undefined) return false;
  309.     if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
  310.     if (obj.nodeName){
  311.         switch (obj.nodeType){
  312.             case 1: return 'element';
  313.             case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
  314.         }
  315.     } else if (typeof obj.length == 'number'){
  316.         if (obj.callee) return 'arguments';
  317.         else if (obj.item) return 'collection';
  318.     }
  319.     return typeof obj;
  320. };
  321.  
  322. function $unlink(object){
  323.     var unlinked;
  324.     switch ($type(object)){
  325.         case 'object':
  326.             unlinked = {};
  327.             for (var p in object) unlinked[p] = $unlink(object[p]);
  328.         break;
  329.         case 'hash':
  330.             unlinked = new Hash(object);
  331.         break;
  332.         case 'array':
  333.             unlinked = [];
  334.             for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
  335.         break;
  336.         default: return object;
  337.     }
  338.     return unlinked;
  339. };
  340.  
  341. /*
  342. ---
  343.  
  344. script: Browser.js
  345.  
  346. description: The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
  347.  
  348. license: MIT-style license.
  349.  
  350. requires:
  351. - /Native
  352. - /$util
  353.  
  354. provides: [Browser, Window, Document, $exec]
  355.  
  356. ...
  357. */
  358.  
  359. var Browser = $merge({
  360.  
  361.     Engine: {name: 'unknown', version: 0},
  362.  
  363.     Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
  364.  
  365.     Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
  366.  
  367.     Plugins: {},
  368.  
  369.     Engines: {
  370.  
  371.         presto: function(){
  372.             return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
  373.         },
  374.  
  375.         trident: function(){
  376.             return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
  377.         },
  378.  
  379.         webkit: function(){
  380.             return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
  381.         },
  382.  
  383.         gecko: function(){
  384.             return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
  385.         }
  386.  
  387.     }
  388.  
  389. }, Browser || {});
  390.  
  391. Browser.Platform[Browser.Platform.name] = true;
  392.  
  393. Browser.detect = function(){
  394.  
  395.     for (var engine in this.Engines){
  396.         var version = this.Engines[engine]();
  397.         if (version){
  398.             this.Engine = {name: engine, version: version};
  399.             this.Engine[engine] = this.Engine[engine + version] = true;
  400.             break;
  401.         }
  402.     }
  403.  
  404.     return {name: engine, version: version};
  405.  
  406. };
  407.  
  408. Browser.detect();
  409.  
  410. Browser.Request = function(){
  411.     return $try(function(){
  412.         return new XMLHttpRequest();
  413.     }, function(){
  414.         return new ActiveXObject('MSXML2.XMLHTTP');
  415.     }, function(){
  416.         return new ActiveXObject('Microsoft.XMLHTTP');
  417.     });
  418. };
  419.  
  420. Browser.Features.xhr = !!(Browser.Request());
  421.  
  422. Browser.Plugins.Flash = (function(){
  423.     var version = ($try(function(){
  424.         return navigator.plugins['Shockwave Flash'].description;
  425.     }, function(){
  426.         return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
  427.     }) || '0 r0').match(/\d+/g);
  428.     return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
  429. })();
  430.  
  431. function $exec(text){
  432.     if (!text) return text;
  433.     if (window.execScript){
  434.         window.execScript(text);
  435.     } else {
  436.         var script = document.createElement('script');
  437.         script.setAttribute('type', 'text/javascript');
  438.         script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
  439.         document.head.appendChild(script);
  440.         document.head.removeChild(script);
  441.     }
  442.     return text;
  443. };
  444.  
  445. Native.UID = 1;
  446.  
  447. var $uid = (Browser.Engine.trident) ? function(item){
  448.     return (item.uid || (item.uid = [Native.UID++]))[0];
  449. } : function(item){
  450.     return item.uid || (item.uid = Native.UID++);
  451. };
  452.  
  453. var Window = new Native({
  454.  
  455.     name: 'Window',
  456.  
  457.     legacy: (Browser.Engine.trident) ? null: window.Window,
  458.  
  459.     initialize: function(win){
  460.         $uid(win);
  461.         if (!win.Element){
  462.             win.Element = $empty;
  463.             if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
  464.             win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
  465.         }
  466.         win.document.window = win;
  467.         return $extend(win, Window.Prototype);
  468.     },
  469.  
  470.     afterImplement: function(property, value){
  471.         window[property] = Window.Prototype[property] = value;
  472.     }
  473.  
  474. });
  475.  
  476. Window.Prototype = {$family: {name: 'window'}};
  477.  
  478. new Window(window);
  479.  
  480. var Document = new Native({
  481.  
  482.     name: 'Document',
  483.  
  484.     legacy: (Browser.Engine.trident) ? null: window.Document,
  485.  
  486.     initialize: function(doc){
  487.         $uid(doc);
  488.         doc.head = doc.getElementsByTagName('head')[0];
  489.         doc.html = doc.getElementsByTagName('html')[0];
  490.         if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
  491.             doc.execCommand("BackgroundImageCache", false, true);
  492.         });
  493.         if (Browser.Engine.trident) doc.window.attachEvent('onunload', function(){
  494.             doc.window.detachEvent('onunload', arguments.callee);
  495.             doc.head = doc.html = doc.window = null;
  496.         });
  497.         return $extend(doc, Document.Prototype);
  498.     },
  499.  
  500.     afterImplement: function(property, value){
  501.         document[property] = Document.Prototype[property] = value;
  502.     }
  503.  
  504. });
  505.  
  506. Document.Prototype = {$family: {name: 'document'}};
  507.  
  508. new Document(document);
  509.  
  510. /*
  511. ---
  512.  
  513. script: Array.js
  514.  
  515. description: Contains Array Prototypes like each, contains, and erase.
  516.  
  517. license: MIT-style license.
  518.  
  519. requires:
  520. - /$util
  521. - /Array.each
  522.  
  523. provides: [Array]
  524.  
  525. ...
  526. */
  527.  
  528. Array.implement({
  529.  
  530.     every: function(fn, bind){
  531.         for (var i = 0, l = this.length; i < l; i++){
  532.             if (!fn.call(bind, this[i], i, this)) return false;
  533.         }
  534.         return true;
  535.     },
  536.  
  537.     filter: function(fn, bind){
  538.         var results = [];
  539.         for (var i = 0, l = this.length; i < l; i++){
  540.             if (fn.call(bind, this[i], i, this)) results.push(this[i]);
  541.         }
  542.         return results;
  543.     },
  544.  
  545.     clean: function(){
  546.         return this.filter($defined);
  547.     },
  548.  
  549.     indexOf: function(item, from){
  550.         var len = this.length;
  551.         for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
  552.             if (this[i] === item) return i;
  553.         }
  554.         return -1;
  555.     },
  556.  
  557.     map: function(fn, bind){
  558.         var results = [];
  559.         for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
  560.         return results;
  561.     },
  562.  
  563.     some: function(fn, bind){
  564.         for (var i = 0, l = this.length; i < l; i++){
  565.             if (fn.call(bind, this[i], i, this)) return true;
  566.         }
  567.         return false;
  568.     },
  569.  
  570.     associate: function(keys){
  571.         var obj = {}, length = Math.min(this.length, keys.length);
  572.         for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
  573.         return obj;
  574.     },
  575.  
  576.     link: function(object){
  577.         var result = {};
  578.         for (var i = 0, l = this.length; i < l; i++){
  579.             for (var key in object){
  580.                 if (object[key](this[i])){
  581.                     result[key] = this[i];
  582.                     delete object[key];
  583.                     break;
  584.                 }
  585.             }
  586.         }
  587.         return result;
  588.     },
  589.  
  590.     contains: function(item, from){
  591.         return this.indexOf(item, from) != -1;
  592.     },
  593.  
  594.     extend: function(array){
  595.         for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
  596.         return this;
  597.     },
  598.    
  599.     getLast: function(){
  600.         return (this.length) ? this[this.length - 1] : null;
  601.     },
  602.  
  603.     getRandom: function(){
  604.         return (this.length) ? this[$random(0, this.length - 1)] : null;
  605.     },
  606.  
  607.     include: function(item){
  608.         if (!this.contains(item)) this.push(item);
  609.         return this;
  610.     },
  611.  
  612.     combine: function(array){
  613.         for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
  614.         return this;
  615.     },
  616.  
  617.     erase: function(item){
  618.         for (var i = this.length; i--; i){
  619.             if (this[i] === item) this.splice(i, 1);
  620.         }
  621.         return this;
  622.     },
  623.  
  624.     empty: function(){
  625.         this.length = 0;
  626.         return this;
  627.     },
  628.  
  629.     flatten: function(){
  630.         var array = [];
  631.         for (var i = 0, l = this.length; i < l; i++){
  632.             var type = $type(this[i]);
  633.             if (!type) continue;
  634.             array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
  635.         }
  636.         return array;
  637.     },
  638.  
  639.     hexToRgb: function(array){
  640.         if (this.length != 3) return null;
  641.         var rgb = this.map(function(value){
  642.             if (value.length == 1) value += value;
  643.             return value.toInt(16);
  644.         });
  645.         return (array) ? rgb : 'rgb(' + rgb + ')';
  646.     },
  647.  
  648.     rgbToHex: function(array){
  649.         if (this.length < 3) return null;
  650.         if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
  651.         var hex = [];
  652.         for (var i = 0; i < 3; i++){
  653.             var bit = (this[i] - 0).toString(16);
  654.             hex.push((bit.length == 1) ? '0' + bit : bit);
  655.         }
  656.         return (array) ? hex : '#' + hex.join('');
  657.     }
  658.  
  659. });
  660.  
  661. /*
  662. ---
  663.  
  664. script: String.js
  665.  
  666. description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
  667.  
  668. license: MIT-style license.
  669.  
  670. requires:
  671. - /Native
  672.  
  673. provides: [String]
  674.  
  675. ...
  676. */
  677.  
  678. String.implement({
  679.  
  680.     test: function(regex, params){
  681.         return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
  682.     },
  683.  
  684.     contains: function(string, separator){
  685.         return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
  686.     },
  687.  
  688.     trim: function(){
  689.         return this.replace(/^\s+|\s+$/g, '');
  690.     },
  691.  
  692.     clean: function(){
  693.         return this.replace(/\s+/g, ' ').trim();
  694.     },
  695.  
  696.     camelCase: function(){
  697.         return this.replace(/-\D/g, function(match){
  698.             return match.charAt(1).toUpperCase();
  699.         });
  700.     },
  701.  
  702.     hyphenate: function(){
  703.         return this.replace(/[A-Z]/g, function(match){
  704.             return ('-' + match.charAt(0).toLowerCase());
  705.         });
  706.     },
  707.  
  708.     capitalize: function(){
  709.         return this.replace(/\b[a-z]/g, function(match){
  710.             return match.toUpperCase();
  711.         });
  712.     },
  713.  
  714.     escapeRegExp: function(){
  715.         return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
  716.     },
  717.  
  718.     toInt: function(base){
  719.         return parseInt(this, base || 10);
  720.     },
  721.  
  722.     toFloat: function(){
  723.         return parseFloat(this);
  724.     },
  725.  
  726.     hexToRgb: function(array){
  727.         var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
  728.         return (hex) ? hex.slice(1).hexToRgb(array) : null;
  729.     },
  730.  
  731.     rgbToHex: function(array){
  732.         var rgb = this.match(/\d{1,3}/g);
  733.         return (rgb) ? rgb.rgbToHex(array) : null;
  734.     },
  735.  
  736.     stripScripts: function(option){
  737.         var scripts = '';
  738.         var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
  739.             scripts += arguments[1] + '\n';
  740.             return '';
  741.         });
  742.         if (option === true) $exec(scripts);
  743.         else if ($type(option) == 'function') option(scripts, text);
  744.         return text;
  745.     },
  746.  
  747.     substitute: function(object, regexp){
  748.         return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
  749.             if (match.charAt(0) == '\\') return match.slice(1);
  750.             return (object[name] != undefined) ? object[name] : '';
  751.         });
  752.     }
  753.  
  754. });
  755.  
  756. /*
  757. ---
  758.  
  759. script: Function.js
  760.  
  761. description: Contains Function Prototypes like create, bind, pass, and delay.
  762.  
  763. license: MIT-style license.
  764.  
  765. requires:
  766. - /Native
  767. - /$util
  768.  
  769. provides: [Function]
  770.  
  771. ...
  772. */
  773.  
  774. Function.implement({
  775.  
  776.     extend: function(properties){
  777.         for (var property in properties) this[property] = properties[property];
  778.         return this;
  779.     },
  780.  
  781.     create: function(options){
  782.         var self = this;
  783.         options = options || {};
  784.         return function(event){
  785.             var args = options.arguments;
  786.             args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
  787.             if (options.event) args = [event || window.event].extend(args);
  788.             var returns = function(){
  789.                 return self.apply(options.bind || null, args);
  790.             };
  791.             if (options.delay) return setTimeout(returns, options.delay);
  792.             if (options.periodical) return setInterval(returns, options.periodical);
  793.             if (options.attempt) return $try(returns);
  794.             return returns();
  795.         };
  796.     },
  797.  
  798.     run: function(args, bind){
  799.         return this.apply(bind, $splat(args));
  800.     },
  801.  
  802.     pass: function(args, bind){
  803.         return this.create({bind: bind, arguments: args});
  804.     },
  805.  
  806.     bind: function(bind, args){
  807.         return this.create({bind: bind, arguments: args});
  808.     },
  809.  
  810.     bindWithEvent: function(bind, args){
  811.         return this.create({bind: bind, arguments: args, event: true});
  812.     },
  813.  
  814.     attempt: function(args, bind){
  815.         return this.create({bind: bind, arguments: args, attempt: true})();
  816.     },
  817.  
  818.     delay: function(delay, bind, args){
  819.         return this.create({bind: bind, arguments: args, delay: delay})();
  820.     },
  821.  
  822.     periodical: function(periodical, bind, args){
  823.         return this.create({bind: bind, arguments: args, periodical: periodical})();
  824.     }
  825.  
  826. });
  827.  
  828. /*
  829. ---
  830.  
  831. script: Number.js
  832.  
  833. description: Contains Number Prototypes like limit, round, times, and ceil.
  834.  
  835. license: MIT-style license.
  836.  
  837. requires:
  838. - /Native
  839. - /$util
  840.  
  841. provides: [Number]
  842.  
  843. ...
  844. */
  845.  
  846. Number.implement({
  847.  
  848.     limit: function(min, max){
  849.         return Math.min(max, Math.max(min, this));
  850.     },
  851.  
  852.     round: function(precision){
  853.         precision = Math.pow(10, precision || 0);
  854.         return Math.round(this * precision) / precision;
  855.     },
  856.  
  857.     times: function(fn, bind){
  858.         for (var i = 0; i < this; i++) fn.call(bind, i, this);
  859.     },
  860.  
  861.     toFloat: function(){
  862.         return parseFloat(this);
  863.     },
  864.  
  865.     toInt: function(base){
  866.         return parseInt(this, base || 10);
  867.     }
  868.  
  869. });
  870.  
  871. Number.alias('times', 'each');
  872.  
  873. (function(math){
  874.     var methods = {};
  875.     math.each(function(name){
  876.         if (!Number[name]) methods[name] = function(){
  877.             return Math[name].apply(null, [this].concat($A(arguments)));
  878.         };
  879.     });
  880.     Number.implement(methods);
  881. })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
  882.  
  883. /*
  884. ---
  885.  
  886. script: Hash.js
  887.  
  888. description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
  889.  
  890. license: MIT-style license.
  891.  
  892. requires:
  893. - /Hash.base
  894.  
  895. provides: [Hash]
  896.  
  897. ...
  898. */
  899.  
  900. Hash.implement({
  901.  
  902.     has: Object.prototype.hasOwnProperty,
  903.  
  904.     keyOf: function(value){
  905.         for (var key in this){
  906.             if (this.hasOwnProperty(key) && this[key] === value) return key;
  907.         }
  908.         return null;
  909.     },
  910.  
  911.     hasValue: function(value){
  912.         return (Hash.keyOf(this, value) !== null);
  913.     },
  914.  
  915.     extend: function(properties){
  916.         Hash.each(properties || {}, function(value, key){
  917.             Hash.set(this, key, value);
  918.         }, this);
  919.         return this;
  920.     },
  921.  
  922.     combine: function(properties){
  923.         Hash.each(properties || {}, function(value, key){
  924.             Hash.include(this, key, value);
  925.         }, this);
  926.         return this;
  927.     },
  928.  
  929.     erase: function(key){
  930.         if (this.hasOwnProperty(key)) delete this[key];
  931.         return this;
  932.     },
  933.  
  934.     get: function(key){
  935.         return (this.hasOwnProperty(key)) ? this[key] : null;
  936.     },
  937.  
  938.     set: function(key, value){
  939.         if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
  940.         return this;
  941.     },
  942.  
  943.     empty: function(){
  944.         Hash.each(this, function(value, key){
  945.             delete this[key];
  946.         }, this);
  947.         return this;
  948.     },
  949.  
  950.     include: function(key, value){
  951.         if (this[key] == undefined) this[key] = value;
  952.         return this;
  953.     },
  954.  
  955.     map: function(fn, bind){
  956.         var results = new Hash;
  957.         Hash.each(this, function(value, key){
  958.             results.set(key, fn.call(bind, value, key, this));
  959.         }, this);
  960.         return results;
  961.     },
  962.  
  963.     filter: function(fn, bind){
  964.         var results = new Hash;
  965.         Hash.each(this, function(value, key){
  966.             if (fn.call(bind, value, key, this)) results.set(key, value);
  967.         }, this);
  968.         return results;
  969.     },
  970.  
  971.     every: function(fn, bind){
  972.         for (var key in this){
  973.             if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
  974.         }
  975.         return true;
  976.     },
  977.  
  978.     some: function(fn, bind){
  979.         for (var key in this){
  980.             if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
  981.         }
  982.         return false;
  983.     },
  984.  
  985.     getKeys: function(){
  986.         var keys = [];
  987.         Hash.each(this, function(value, key){
  988.             keys.push(key);
  989.         });
  990.         return keys;
  991.     },
  992.  
  993.     getValues: function(){
  994.         var values = [];
  995.         Hash.each(this, function(value){
  996.             values.push(value);
  997.         });
  998.         return values;
  999.     },
  1000.  
  1001.     toQueryString: function(base){
  1002.         var queryString = [];
  1003.         Hash.each(this, function(value, key){
  1004.             if (base) key = base + '[' + key + ']';
  1005.             var result;
  1006.             switch ($type(value)){
  1007.                 case 'object': result = Hash.toQueryString(value, key); break;
  1008.                 case 'array':
  1009.                     var qs = {};
  1010.                     value.each(function(val, i){
  1011.                         qs[i] = val;
  1012.                     });
  1013.                     result = Hash.toQueryString(qs, key);
  1014.                 break;
  1015.                 default: result = key + '=' + encodeURIComponent(value);
  1016.             }
  1017.             if (value != undefined) queryString.push(result);
  1018.         });
  1019.  
  1020.         return queryString.join('&');
  1021.     }
  1022.  
  1023. });
  1024.  
  1025. Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
  1026.  
  1027. /*
  1028. ---
  1029.  
  1030. script: Element.js
  1031.  
  1032. 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.
  1033.  
  1034. license: MIT-style license.
  1035.  
  1036. requires:
  1037. - /Window
  1038. - /Document
  1039. - /Array
  1040. - /String
  1041. - /Function
  1042. - /Number
  1043. - /Hash
  1044.  
  1045. provides: [Element, Elements, $, $$, Iframe]
  1046.  
  1047. ...
  1048. */
  1049.  
  1050. var Element = new Native({
  1051.  
  1052.     name: 'Element',
  1053.  
  1054.     legacy: window.Element,
  1055.  
  1056.     initialize: function(tag, props){
  1057.         var konstructor = Element.Constructors.get(tag);
  1058.         if (konstructor) return konstructor(props);
  1059.         if (typeof tag == 'string') return document.newElement(tag, props);
  1060.         return document.id(tag).set(props);
  1061.     },
  1062.  
  1063.     afterImplement: function(key, value){
  1064.         Element.Prototype[key] = value;
  1065.         if (Array[key]) return;
  1066.         Elements.implement(key, function(){
  1067.             var items = [], elements = true;
  1068.             for (var i = 0, j = this.length; i < j; i++){
  1069.                 var returns = this[i][key].apply(this[i], arguments);
  1070.                 items.push(returns);
  1071.                 if (elements) elements = ($type(returns) == 'element');
  1072.             }
  1073.             return (elements) ? new Elements(items) : items;
  1074.         });
  1075.     }
  1076.  
  1077. });
  1078.  
  1079. Element.Prototype = {$family: {name: 'element'}};
  1080.  
  1081. Element.Constructors = new Hash;
  1082.  
  1083. var IFrame = new Native({
  1084.  
  1085.     name: 'IFrame',
  1086.  
  1087.     generics: false,
  1088.  
  1089.     initialize: function(){
  1090.         var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
  1091.         var props = params.properties || {};
  1092.         var iframe = document.id(params.iframe);
  1093.         var onload = props.onload || $empty;
  1094.         delete props.onload;
  1095.         props.id = props.name = $pick(props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + $time());
  1096.         iframe = new Element(iframe || 'iframe', props);
  1097.         var onFrameLoad = function(){
  1098.             var host = $try(function(){
  1099.                 return iframe.contentWindow.location.host;
  1100.             });
  1101.             if (!host || host == window.location.host){
  1102.                 var win = new Window(iframe.contentWindow);
  1103.                 new Document(iframe.contentWindow.document);
  1104.                 if(!win.Element.prototype) win.Element.prototype = {};
  1105.                 $extend(win.Element.prototype, Element.Prototype);
  1106.             }
  1107.             onload.call(iframe.contentWindow, iframe.contentWindow.document);
  1108.         };
  1109.         var contentWindow = $try(function(){
  1110.             return iframe.contentWindow;
  1111.         });
  1112.         ((contentWindow && contentWindow.document.body) || window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
  1113.         return iframe;
  1114.     }
  1115.  
  1116. });
  1117.  
  1118. var Elements = new Native({
  1119.  
  1120.     initialize: function(elements, options){
  1121.         options = $extend({ddup: true, cash: true}, options);
  1122.         elements = elements || [];
  1123.         if (options.ddup || options.cash){
  1124.             var uniques = {}, returned = [];
  1125.             for (var i = 0, l = elements.length; i < l; i++){
  1126.                 var el = document.id(elements[i], !options.cash);
  1127.                 if (options.ddup){
  1128.                     if (uniques[el.uid]) continue;
  1129.                     uniques[el.uid] = true;
  1130.                 }
  1131.                 if (el) returned.push(el);
  1132.             }
  1133.             elements = returned;
  1134.         }
  1135.         return (options.cash) ? $extend(elements, this) : elements;
  1136.     }
  1137.  
  1138. });
  1139.  
  1140. Elements.implement({
  1141.  
  1142.     filter: function(filter, bind){
  1143.         if (!filter) return this;
  1144.         return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
  1145.             return item.match(filter);
  1146.         } : filter, bind));
  1147.     }
  1148.  
  1149. });
  1150.  
  1151. Document.implement({
  1152.  
  1153.     newElement: function(tag, props){
  1154.         if (Browser.Engine.trident && props){
  1155.             ['name', 'type', 'checked'].each(function(attribute){
  1156.                 if (!props[attribute]) return;
  1157.                 tag += ' ' + attribute + '="' + props[attribute] + '"';
  1158.                 if (attribute != 'checked') delete props[attribute];
  1159.             });
  1160.             tag = '<' + tag + '>';
  1161.         }
  1162.         return document.id(this.createElement(tag)).set(props);
  1163.     },
  1164.  
  1165.     newTextNode: function(text){
  1166.         return this.createTextNode(text);
  1167.     },
  1168.  
  1169.     getDocument: function(){
  1170.         return this;
  1171.     },
  1172.  
  1173.     getWindow: function(){
  1174.         return this.window;
  1175.     },
  1176.    
  1177.     id: (function(){
  1178.        
  1179.         var types = {
  1180.  
  1181.             string: function(id, nocash, doc){
  1182.                 id = doc.getElementById(id);
  1183.                 return (id) ? types.element(id, nocash) : null;
  1184.             },
  1185.            
  1186.             element: function(el, nocash){
  1187.                 $uid(el);
  1188.                 if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
  1189.                     var proto = Element.Prototype;
  1190.                     for (var p in proto) el[p] = proto[p];
  1191.                 };
  1192.                 return el;
  1193.             },
  1194.            
  1195.             object: function(obj, nocash, doc){
  1196.                 if (obj.toElement) return types.element(obj.toElement(doc), nocash);
  1197.                 return null;
  1198.             }
  1199.            
  1200.         };
  1201.  
  1202.         types.textnode = types.whitespace = types.window = types.document = $arguments(0);
  1203.        
  1204.         return function(el, nocash, doc){
  1205.             if (el && el.$family && el.uid) return el;
  1206.             var type = $type(el);
  1207.             return (types[type]) ? types[type](el, nocash, doc || document) : null;
  1208.         };
  1209.  
  1210.     })()
  1211.  
  1212. });
  1213.  
  1214. if (window.$ == null) Window.implement({
  1215.     $: function(el, nc){
  1216.         return document.id(el, nc, this.document);
  1217.     }
  1218. });
  1219.  
  1220. Window.implement({
  1221.  
  1222.     $$: function(selector){
  1223.         if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
  1224.         var elements = [];
  1225.         var args = Array.flatten(arguments);
  1226.         for (var i = 0, l = args.length; i < l; i++){
  1227.             var item = args[i];
  1228.             switch ($type(item)){
  1229.                 case 'element': elements.push(item); break;
  1230.                 case 'string': elements.extend(this.document.getElements(item, true));
  1231.             }
  1232.         }
  1233.         return new Elements(elements);
  1234.     },
  1235.  
  1236.     getDocument: function(){
  1237.         return this.document;
  1238.     },
  1239.  
  1240.     getWindow: function(){
  1241.         return this;
  1242.     }
  1243.  
  1244. });
  1245.  
  1246. Native.implement([Element, Document], {
  1247.  
  1248.     getElement: function(selector, nocash){
  1249.         return document.id(this.getElements(selector, true)[0] || null, nocash);
  1250.     },
  1251.  
  1252.     getElements: function(tags, nocash){
  1253.         tags = tags.split(',');
  1254.         var elements = [];
  1255.         var ddup = (tags.length > 1);
  1256.         tags.each(function(tag){
  1257.             var partial = this.getElementsByTagName(tag.trim());
  1258.             (ddup) ? elements.extend(partial) : elements = partial;
  1259.         }, this);
  1260.         return new Elements(elements, {ddup: ddup, cash: !nocash});
  1261.     }
  1262.  
  1263. });
  1264.  
  1265. (function(){
  1266.  
  1267. var collected = {}, storage = {};
  1268. var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'};
  1269.  
  1270. var get = function(uid){
  1271.     return (storage[uid] || (storage[uid] = {}));
  1272. };
  1273.  
  1274. var clean = function(item, retain){
  1275.     if (!item) return;
  1276.     var uid = item.uid;
  1277.     if (Browser.Engine.trident){
  1278.         if (item.clearAttributes){
  1279.             var clone = retain && item.cloneNode(false);
  1280.             item.clearAttributes();
  1281.             if (clone) item.mergeAttributes(clone);
  1282.         } else if (item.removeEvents){
  1283.             item.removeEvents();
  1284.         }
  1285.         if ((/object/i).test(item.tagName)){
  1286.             for (var p in item){
  1287.                 if (typeof item[p] == 'function') item[p] = $empty;
  1288.             }
  1289.             Element.dispose(item);
  1290.         }
  1291.     }  
  1292.     if (!uid) return;
  1293.     collected[uid] = storage[uid] = null;
  1294. };
  1295.  
  1296. var purge = function(){
  1297.     Hash.each(collected, clean);
  1298.     if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
  1299.     if (window.CollectGarbage) CollectGarbage();
  1300.     collected = storage = null;
  1301. };
  1302.  
  1303. var walk = function(element, walk, start, match, all, nocash){
  1304.     var el = element[start || walk];
  1305.     var elements = [];
  1306.     while (el){
  1307.         if (el.nodeType == 1 && (!match || Element.match(el, match))){
  1308.             if (!all) return document.id(el, nocash);
  1309.             elements.push(el);
  1310.         }
  1311.         el = el[walk];
  1312.     }
  1313.     return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null;
  1314. };
  1315.  
  1316. var attributes = {
  1317.     'html': 'innerHTML',
  1318.     'class': 'className',
  1319.     'for': 'htmlFor',
  1320.     'defaultValue': 'defaultValue',
  1321.     'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
  1322. };
  1323. var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
  1324. var camels = ['value', 'type', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];
  1325.  
  1326. bools = bools.associate(bools);
  1327.  
  1328. Hash.extend(attributes, bools);
  1329. Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));
  1330.  
  1331. var inserters = {
  1332.  
  1333.     before: function(context, element){
  1334.         if (element.parentNode) element.parentNode.insertBefore(context, element);
  1335.     },
  1336.  
  1337.     after: function(context, element){
  1338.         if (!element.parentNode) return;
  1339.         var next = element.nextSibling;
  1340.         (next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
  1341.     },
  1342.  
  1343.     bottom: function(context, element){
  1344.         element.appendChild(context);
  1345.     },
  1346.  
  1347.     top: function(context, element){
  1348.         var first = element.firstChild;
  1349.         (first) ? element.insertBefore(context, first) : element.appendChild(context);
  1350.     }
  1351.  
  1352. };
  1353.  
  1354. inserters.inside = inserters.bottom;
  1355.  
  1356. Hash.each(inserters, function(inserter, where){
  1357.  
  1358.     where = where.capitalize();
  1359.  
  1360.     Element.implement('inject' + where, function(el){
  1361.         inserter(this, document.id(el, true));
  1362.         return this;
  1363.     });
  1364.  
  1365.     Element.implement('grab' + where, function(el){
  1366.         inserter(document.id(el, true), this);
  1367.         return this;
  1368.     });
  1369.  
  1370. });
  1371.  
  1372. Element.implement({
  1373.  
  1374.     set: function(prop, value){
  1375.         switch ($type(prop)){
  1376.             case 'object':
  1377.                 for (var p in prop) this.set(p, prop[p]);
  1378.                 break;
  1379.             case 'string':
  1380.                 var property = Element.Properties.get(prop);
  1381.                 (property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
  1382.         }
  1383.         return this;
  1384.     },
  1385.  
  1386.     get: function(prop){
  1387.         var property = Element.Properties.get(prop);
  1388.         return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
  1389.     },
  1390.  
  1391.     erase: function(prop){
  1392.         var property = Element.Properties.get(prop);
  1393.         (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
  1394.         return this;
  1395.     },
  1396.  
  1397.     setProperty: function(attribute, value){
  1398.         var key = attributes[attribute];
  1399.         if (value == undefined) return this.removeProperty(attribute);
  1400.         if (key && bools[attribute]) value = !!value;
  1401.         (key) ? this[key] = value : this.setAttribute(attribute, '' + value);
  1402.         return this;
  1403.     },
  1404.  
  1405.     setProperties: function(attributes){
  1406.         for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
  1407.         return this;
  1408.     },
  1409.  
  1410.     getProperty: function(attribute){
  1411.         var key = attributes[attribute];
  1412.         var value = (key) ? this[key] : this.getAttribute(attribute, 2);
  1413.         return (bools[attribute]) ? !!value : (key) ? value : value || null;
  1414.     },
  1415.  
  1416.     getProperties: function(){
  1417.         var args = $A(arguments);
  1418.         return args.map(this.getProperty, this).associate(args);
  1419.     },
  1420.  
  1421.     removeProperty: function(attribute){
  1422.         var key = attributes[attribute];
  1423.         (key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
  1424.         return this;
  1425.     },
  1426.  
  1427.     removeProperties: function(){
  1428.         Array.each(arguments, this.removeProperty, this);
  1429.         return this;
  1430.     },
  1431.  
  1432.     hasClass: function(className){
  1433.         return this.className.contains(className, ' ');
  1434.     },
  1435.  
  1436.     addClass: function(className){
  1437.         if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
  1438.         return this;
  1439.     },
  1440.  
  1441.     removeClass: function(className){
  1442.         this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
  1443.         return this;
  1444.     },
  1445.  
  1446.     toggleClass: function(className){
  1447.         return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
  1448.     },
  1449.  
  1450.     adopt: function(){
  1451.         Array.flatten(arguments).each(function(element){
  1452.             element = document.id(element, true);
  1453.             if (element) this.appendChild(element);
  1454.         }, this);
  1455.         return this;
  1456.     },
  1457.  
  1458.     appendText: function(text, where){
  1459.         return this.grab(this.getDocument().newTextNode(text), where);
  1460.     },
  1461.  
  1462.     grab: function(el, where){
  1463.         inserters[where || 'bottom'](document.id(el, true), this);
  1464.         return this;
  1465.     },
  1466.  
  1467.     inject: function(el, where){
  1468.         inserters[where || 'bottom'](this, document.id(el, true));
  1469.         return this;
  1470.     },
  1471.  
  1472.     replaces: function(el){
  1473.         el = document.id(el, true);
  1474.         el.parentNode.replaceChild(this, el);
  1475.         return this;
  1476.     },
  1477.  
  1478.     wraps: function(el, where){
  1479.         el = document.id(el, true);
  1480.         return this.replaces(el).grab(el, where);
  1481.     },
  1482.  
  1483.     getPrevious: function(match, nocash){
  1484.         return walk(this, 'previousSibling', null, match, false, nocash);
  1485.     },
  1486.  
  1487.     getAllPrevious: function(match, nocash){
  1488.         return walk(this, 'previousSibling', null, match, true, nocash);
  1489.     },
  1490.  
  1491.     getNext: function(match, nocash){
  1492.         return walk(this, 'nextSibling', null, match, false, nocash);
  1493.     },
  1494.  
  1495.     getAllNext: function(match, nocash){
  1496.         return walk(this, 'nextSibling', null, match, true, nocash);
  1497.     },
  1498.  
  1499.     getFirst: function(match, nocash){
  1500.         return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
  1501.     },
  1502.  
  1503.     getLast: function(match, nocash){
  1504.         return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
  1505.     },
  1506.  
  1507.     getParent: function(match, nocash){
  1508.         return walk(this, 'parentNode', null, match, false, nocash);
  1509.     },
  1510.  
  1511.     getParents: function(match, nocash){
  1512.         return walk(this, 'parentNode', null, match, true, nocash);
  1513.     },
  1514.    
  1515.     getSiblings: function(match, nocash){
  1516.         return this.getParent().getChildren(match, nocash).erase(this);
  1517.     },
  1518.  
  1519.     getChildren: function(match, nocash){
  1520.         return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
  1521.     },
  1522.  
  1523.     getWindow: function(){
  1524.         return this.ownerDocument.window;
  1525.     },
  1526.  
  1527.     getDocument: function(){
  1528.         return this.ownerDocument;
  1529.     },
  1530.  
  1531.     getElementById: function(id, nocash){
  1532.         var el = this.ownerDocument.getElementById(id);
  1533.         if (!el) return null;
  1534.         for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
  1535.             if (!parent) return null;
  1536.         }
  1537.         return document.id(el, nocash);
  1538.     },
  1539.  
  1540.     getSelected: function(){
  1541.         return new Elements($A(this.options).filter(function(option){
  1542.             return option.selected;
  1543.         }));
  1544.     },
  1545.  
  1546.     getComputedStyle: function(property){
  1547.         if (this.currentStyle) return this.currentStyle[property.camelCase()];
  1548.         var computed = this.getDocument().defaultView.getComputedStyle(this, null);
  1549.         return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
  1550.     },
  1551.  
  1552.     toQueryString: function(){
  1553.         var queryString = [];
  1554.         this.getElements('input, select, textarea', true).each(function(el){
  1555.             if (!el.name || el.disabled || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return;
  1556.             var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
  1557.                 return opt.value;
  1558.             }) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
  1559.             $splat(value).each(function(val){
  1560.                 if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
  1561.             });
  1562.         });
  1563.         return queryString.join('&');
  1564.     },
  1565.  
  1566.     clone: function(contents, keepid){
  1567.         contents = contents !== false;
  1568.         var clone = this.cloneNode(contents);
  1569.         var clean = function(node, element){
  1570.             if (!keepid) node.removeAttribute('id');
  1571.             if (Browser.Engine.trident){
  1572.                 node.clearAttributes();
  1573.                 node.mergeAttributes(element);
  1574.                 node.removeAttribute('uid');
  1575.                 if (node.options){
  1576.                     var no = node.options, eo = element.options;
  1577.                     for (var j = no.length; j--;) no[j].selected = eo[j].selected;
  1578.                 }
  1579.             }
  1580.             var prop = props[element.tagName.toLowerCase()];
  1581.             if (prop && element[prop]) node[prop] = element[prop];
  1582.         };
  1583.  
  1584.         if (contents){
  1585.             var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
  1586.             for (var i = ce.length; i--;) clean(ce[i], te[i]);
  1587.         }
  1588.  
  1589.         clean(clone, this);
  1590.         return document.id(clone);
  1591.     },
  1592.  
  1593.     destroy: function(){
  1594.         Element.empty(this);
  1595.         Element.dispose(this);
  1596.         clean(this, true);
  1597.         return null;
  1598.     },
  1599.  
  1600.     empty: function(){
  1601.         $A(this.childNodes).each(function(node){
  1602.             Element.destroy(node);
  1603.         });
  1604.         return this;
  1605.     },
  1606.  
  1607.     dispose: function(){
  1608.         return (this.parentNode) ? this.parentNode.removeChild(this) : this;
  1609.     },
  1610.  
  1611.     hasChild: function(el){
  1612.         el = document.id(el, true);
  1613.         if (!el) return false;
  1614.         if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
  1615.         return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
  1616.     },
  1617.  
  1618.     match: function(tag){
  1619.         return (!tag || (tag == this) || (Element.get(this, 'tag') == tag));
  1620.     }
  1621.  
  1622. });
  1623.  
  1624. Native.implement([Element, Window, Document], {
  1625.  
  1626.     addListener: function(type, fn){
  1627.         if (type == 'unload'){
  1628.             var old = fn, self = this;
  1629.             fn = function(){
  1630.                 self.removeListener('unload', fn);
  1631.                 old();
  1632.             };
  1633.         } else {
  1634.             collected[this.uid] = this;
  1635.         }
  1636.         if (this.addEventListener) this.addEventListener(type, fn, false);
  1637.         else this.attachEvent('on' + type, fn);
  1638.         return this;
  1639.     },
  1640.  
  1641.     removeListener: function(type, fn){
  1642.         if (this.removeEventListener) this.removeEventListener(type, fn, false);
  1643.         else this.detachEvent('on' + type, fn);
  1644.         return this;
  1645.     },
  1646.  
  1647.     retrieve: function(property, dflt){
  1648.         var storage = get(this.uid), prop = storage[property];
  1649.         if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
  1650.         return $pick(prop);
  1651.     },
  1652.  
  1653.     store: function(property, value){
  1654.         var storage = get(this.uid);
  1655.         storage[property] = value;
  1656.         return this;
  1657.     },
  1658.  
  1659.     eliminate: function(property){
  1660.         var storage = get(this.uid);
  1661.         delete storage[property];
  1662.         return this;
  1663.     }
  1664.  
  1665. });
  1666.  
  1667. window.addListener('unload', purge);
  1668.  
  1669. })();
  1670.  
  1671. Element.Properties = new Hash;
  1672.  
  1673. Element.Properties.style = {
  1674.  
  1675.     set: function(style){
  1676.         this.style.cssText = style;
  1677.     },
  1678.  
  1679.     get: function(){
  1680.         return this.style.cssText;
  1681.     },
  1682.  
  1683.     erase: function(){
  1684.         this.style.cssText = '';
  1685.     }
  1686.  
  1687. };
  1688.  
  1689. Element.Properties.tag = {
  1690.  
  1691.     get: function(){
  1692.         return this.tagName.toLowerCase();
  1693.     }
  1694.  
  1695. };
  1696.  
  1697. Element.Properties.html = (function(){
  1698.     var wrapper = document.createElement('div');
  1699.  
  1700.     var translations = {
  1701.         table: [1, '<table>', '</table>'],
  1702.         select: [1, '<select>', '</select>'],
  1703.         tbody: [2, '<table><tbody>', '</tbody></table>'],
  1704.         tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
  1705.     };
  1706.     translations.thead = translations.tfoot = translations.tbody;
  1707.  
  1708.     var html = {
  1709.         set: function(){
  1710.             var html = Array.flatten(arguments).join('');
  1711.             var wrap = Browser.Engine.trident && translations[this.get('tag')];
  1712.             if (wrap){
  1713.                 var first = wrapper;
  1714.                 first.innerHTML = wrap[1] + html + wrap[2];
  1715.                 for (var i = wrap[0]; i--;) first = first.firstChild;
  1716.                 this.empty().adopt(first.childNodes);
  1717.             } else {
  1718.                 this.innerHTML = html;
  1719.             }
  1720.         }
  1721.     };
  1722.  
  1723.     html.erase = html.set;
  1724.  
  1725.     return html;
  1726. })();
  1727.  
  1728. if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = {
  1729.     get: function(){
  1730.         if (this.innerText) return this.innerText;
  1731.         var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body);
  1732.         var text = temp.innerText;
  1733.         temp.destroy();
  1734.         return text;
  1735.     }
  1736. };
  1737.  
  1738. /*
  1739. ---
  1740.  
  1741. script: Element.Style.js
  1742.  
  1743. description: Contains methods for interacting with the styles of Elements in a fashionable way.
  1744.  
  1745. license: MIT-style license.
  1746.  
  1747. requires:
  1748. - /Element
  1749.  
  1750. provides: [Element.Style]
  1751.  
  1752. ...
  1753. */
  1754.  
  1755. Element.Properties.styles = {set: function(styles){
  1756.     this.setStyles(styles);
  1757. }};
  1758.  
  1759. Element.Properties.opacity = {
  1760.  
  1761.     set: function(opacity, novisibility){
  1762.         if (!novisibility){
  1763.             if (opacity == 0){
  1764.                 if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
  1765.             } else {
  1766.                 if (this.style.visibility != 'visible') this.style.visibility = 'visible';
  1767.             }
  1768.         }
  1769.         if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
  1770.         if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
  1771.         this.style.opacity = opacity;
  1772.         this.store('opacity', opacity);
  1773.     },
  1774.  
  1775.     get: function(){
  1776.         return this.retrieve('opacity', 1);
  1777.     }
  1778.  
  1779. };
  1780.  
  1781. Element.implement({
  1782.  
  1783.     setOpacity: function(value){
  1784.         return this.set('opacity', value, true);
  1785.     },
  1786.  
  1787.     getOpacity: function(){
  1788.         return this.get('opacity');
  1789.     },
  1790.  
  1791.     setStyle: function(property, value){
  1792.         switch (property){
  1793.             case 'opacity': return this.set('opacity', parseFloat(value));
  1794.             case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
  1795.         }
  1796.         property = property.camelCase();
  1797.         if ($type(value) != 'string'){
  1798.             var map = (Element.Styles.get(property) || '@').split(' ');
  1799.             value = $splat(value).map(function(val, i){
  1800.                 if (!map[i]) return '';
  1801.                 return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
  1802.             }).join(' ');
  1803.         } else if (value == String(Number(value))){
  1804.             value = Math.round(value);
  1805.         }
  1806.         this.style[property] = value;
  1807.         return this;
  1808.     },
  1809.  
  1810.     getStyle: function(property){
  1811.         switch (property){
  1812.             case 'opacity': return this.get('opacity');
  1813.             case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
  1814.         }
  1815.         property = property.camelCase();
  1816.         var result = this.style[property];
  1817.         if (!$chk(result)){
  1818.             result = [];
  1819.             for (var style in Element.ShortStyles){
  1820.                 if (property != style) continue;
  1821.                 for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
  1822.                 return result.join(' ');
  1823.             }
  1824.             result = this.getComputedStyle(property);
  1825.         }
  1826.         if (result){
  1827.             result = String(result);
  1828.             var color = result.match(/rgba?\([\d\s,]+\)/);
  1829.             if (color) result = result.replace(color[0], color[0].rgbToHex());
  1830.         }
  1831.         if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result, 10)))){
  1832.             if (property.test(/^(height|width)$/)){
  1833.                 var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
  1834.                 values.each(function(value){
  1835.                     size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
  1836.                 }, this);
  1837.                 return this['offset' + property.capitalize()] - size + 'px';
  1838.             }
  1839.             if ((Browser.Engine.presto) && String(result).test('px')) return result;
  1840.             if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
  1841.         }
  1842.         return result;
  1843.     },
  1844.  
  1845.     setStyles: function(styles){
  1846.         for (var style in styles) this.setStyle(style, styles[style]);
  1847.         return this;
  1848.     },
  1849.  
  1850.     getStyles: function(){
  1851.         var result = {};
  1852.         Array.flatten(arguments).each(function(key){
  1853.             result[key] = this.getStyle(key);
  1854.         }, this);
  1855.         return result;
  1856.     }
  1857.  
  1858. });
  1859.  
  1860. Element.Styles = new Hash({
  1861.     left: '@px', top: '@px', bottom: '@px', right: '@px',
  1862.     width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
  1863.     backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
  1864.     fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
  1865.     margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
  1866.     borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
  1867.     zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
  1868. });
  1869.  
  1870. Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
  1871.  
  1872. ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
  1873.     var Short = Element.ShortStyles;
  1874.     var All = Element.Styles;
  1875.     ['margin', 'padding'].each(function(style){
  1876.         var sd = style + direction;
  1877.         Short[style][sd] = All[sd] = '@px';
  1878.     });
  1879.     var bd = 'border' + direction;
  1880.     Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
  1881.     var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
  1882.     Short[bd] = {};
  1883.     Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
  1884.     Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
  1885.     Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
  1886. });
  1887.  
  1888. /*
  1889. ---
  1890.  
  1891. script: Element.Dimensions.js
  1892.  
  1893. description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
  1894.  
  1895. license: MIT-style license.
  1896.  
  1897. credits:
  1898. - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
  1899. - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
  1900.  
  1901. requires:
  1902. - /Element
  1903.  
  1904. provides: [Element.Dimensions]
  1905.  
  1906. ...
  1907. */
  1908.  
  1909. (function(){
  1910.  
  1911. Element.implement({
  1912.  
  1913.     scrollTo: function(x, y){
  1914.         if (isBody(this)){
  1915.             this.getWindow().scrollTo(x, y);
  1916.         } else {
  1917.             this.scrollLeft = x;
  1918.             this.scrollTop = y;
  1919.         }
  1920.         return this;
  1921.     },
  1922.  
  1923.     getSize: function(){
  1924.         if (isBody(this)) return this.getWindow().getSize();
  1925.         return {x: this.offsetWidth, y: this.offsetHeight};
  1926.     },
  1927.  
  1928.     getScrollSize: function(){
  1929.         if (isBody(this)) return this.getWindow().getScrollSize();
  1930.         return {x: this.scrollWidth, y: this.scrollHeight};
  1931.     },
  1932.  
  1933.     getScroll: function(){
  1934.         if (isBody(this)) return this.getWindow().getScroll();
  1935.         return {x: this.scrollLeft, y: this.scrollTop};
  1936.     },
  1937.  
  1938.     getScrolls: function(){
  1939.         var element = this, position = {x: 0, y: 0};
  1940.         while (element && !isBody(element)){
  1941.             position.x += element.scrollLeft;
  1942.             position.y += element.scrollTop;
  1943.             element = element.parentNode;
  1944.         }
  1945.         return position;
  1946.     },
  1947.  
  1948.     getOffsetParent: function(){
  1949.         var element = this;
  1950.         if (isBody(element)) return null;
  1951.         if (!Browser.Engine.trident) return element.offsetParent;
  1952.         while ((element = element.parentNode) && !isBody(element)){
  1953.             if (styleString(element, 'position') != 'static') return element;
  1954.         }
  1955.         return null;
  1956.     },
  1957.  
  1958.     getOffsets: function(){
  1959.         if (this.getBoundingClientRect){
  1960.             var bound = this.getBoundingClientRect(),
  1961.                 html = document.id(this.getDocument().documentElement),
  1962.                 htmlScroll = html.getScroll(),
  1963.                 elemScrolls = this.getScrolls(),
  1964.                 elemScroll = this.getScroll(),
  1965.                 isFixed = (styleString(this, 'position') == 'fixed');
  1966.  
  1967.             return {
  1968.                 x: bound.left.toInt() + elemScrolls.x - elemScroll.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
  1969.                 y: bound.top.toInt()  + elemScrolls.y - elemScroll.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
  1970.             };
  1971.         }
  1972.  
  1973.         var element = this, position = {x: 0, y: 0};
  1974.         if (isBody(this)) return position;
  1975.  
  1976.         while (element && !isBody(element)){
  1977.             position.x += element.offsetLeft;
  1978.             position.y += element.offsetTop;
  1979.  
  1980.             if (Browser.Engine.gecko){
  1981.                 if (!borderBox(element)){
  1982.                     position.x += leftBorder(element);
  1983.                     position.y += topBorder(element);
  1984.                 }
  1985.                 var parent = element.parentNode;
  1986.                 if (parent && styleString(parent, 'overflow') != 'visible'){
  1987.                     position.x += leftBorder(parent);
  1988.                     position.y += topBorder(parent);
  1989.                 }
  1990.             } else if (element != this && Browser.Engine.webkit){
  1991.                 position.x += leftBorder(element);
  1992.                 position.y += topBorder(element);
  1993.             }
  1994.  
  1995.             element = element.offsetParent;
  1996.         }
  1997.         if (Browser.Engine.gecko && !borderBox(this)){
  1998.             position.x -= leftBorder(this);
  1999.             position.y -= topBorder(this);
  2000.         }
  2001.         return position;
  2002.     },
  2003.  
  2004.     getPosition: function(relative){
  2005.         if (isBody(this)) return {x: 0, y: 0};
  2006.         var offset = this.getOffsets(),
  2007.                 scroll = this.getScrolls();
  2008.         var position = {
  2009.             x: offset.x - scroll.x,
  2010.             y: offset.y - scroll.y
  2011.         };
  2012.         var relativePosition = (relative && (relative = document.id(relative))) ? relative.getPosition() : {x: 0, y: 0};
  2013.         return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
  2014.     },
  2015.  
  2016.     getCoordinates: function(element){
  2017.         if (isBody(this)) return this.getWindow().getCoordinates();
  2018.         var position = this.getPosition(element),
  2019.                 size = this.getSize();
  2020.         var obj = {
  2021.             left: position.x,
  2022.             top: position.y,
  2023.             width: size.x,
  2024.             height: size.y
  2025.         };
  2026.         obj.right = obj.left + obj.width;
  2027.         obj.bottom = obj.top + obj.height;
  2028.         return obj;
  2029.     },
  2030.  
  2031.     computePosition: function(obj){
  2032.         return {
  2033.             left: obj.x - styleNumber(this, 'margin-left'),
  2034.             top: obj.y - styleNumber(this, 'margin-top')
  2035.         };
  2036.     },
  2037.  
  2038.     setPosition: function(obj){
  2039.         return this.setStyles(this.computePosition(obj));
  2040.     }
  2041.  
  2042. });
  2043.  
  2044.  
  2045. Native.implement([Document, Window], {
  2046.  
  2047.     getSize: function(){
  2048.         if (Browser.Engine.presto || Browser.Engine.webkit){
  2049.             var win = this.getWindow();
  2050.             return {x: win.innerWidth, y: win.innerHeight};
  2051.         }
  2052.         var doc = getCompatElement(this);
  2053.         return {x: doc.clientWidth, y: doc.clientHeight};
  2054.     },
  2055.  
  2056.     getScroll: function(){
  2057.         var win = this.getWindow(), doc = getCompatElement(this);
  2058.         return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
  2059.     },
  2060.  
  2061.     getScrollSize: function(){
  2062.         var doc = getCompatElement(this), min = this.getSize();
  2063.         return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
  2064.     },
  2065.  
  2066.     getPosition: function(){
  2067.         return {x: 0, y: 0};
  2068.     },
  2069.  
  2070.     getCoordinates: function(){
  2071.         var size = this.getSize();
  2072.         return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
  2073.     }
  2074.  
  2075. });
  2076.  
  2077. // private methods
  2078.  
  2079. var styleString = Element.getComputedStyle;
  2080.  
  2081. function styleNumber(element, style){
  2082.     return styleString(element, style).toInt() || 0;
  2083. };
  2084.  
  2085. function borderBox(element){
  2086.     return styleString(element, '-moz-box-sizing') == 'border-box';
  2087. };
  2088.  
  2089. function topBorder(element){
  2090.     return styleNumber(element, 'border-top-width');
  2091. };
  2092.  
  2093. function leftBorder(element){
  2094.     return styleNumber(element, 'border-left-width');
  2095. };
  2096.  
  2097. function isBody(element){
  2098.     return (/^(?:body|html)$/i).test(element.tagName);
  2099. };
  2100.  
  2101. function getCompatElement(element){
  2102.     var doc = element.getDocument();
  2103.     return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
  2104. };
  2105.  
  2106. })();
  2107.  
  2108. //aliases
  2109. Element.alias('setPosition', 'position'); //compatability
  2110.  
  2111. Native.implement([Window, Document, Element], {
  2112.  
  2113.     getHeight: function(){
  2114.         return this.getSize().y;
  2115.     },
  2116.  
  2117.     getWidth: function(){
  2118.         return this.getSize().x;
  2119.     },
  2120.  
  2121.     getScrollTop: function(){
  2122.         return this.getScroll().y;
  2123.     },
  2124.  
  2125.     getScrollLeft: function(){
  2126.         return this.getScroll().x;
  2127.     },
  2128.  
  2129.     getScrollHeight: function(){
  2130.         return this.getScrollSize().y;
  2131.     },
  2132.  
  2133.     getScrollWidth: function(){
  2134.         return this.getScrollSize().x;
  2135.     },
  2136.  
  2137.     getTop: function(){
  2138.         return this.getPosition().y;
  2139.     },
  2140.  
  2141.     getLeft: function(){
  2142.         return this.getPosition().x;
  2143.     }
  2144.  
  2145. });
  2146.  
  2147. /*
  2148. ---
  2149.  
  2150. script: Selectors.js
  2151.  
  2152. description: Adds advanced CSS-style querying capabilities for targeting HTML Elements. Includes pseudo selectors.
  2153.  
  2154. license: MIT-style license.
  2155.  
  2156. requires:
  2157. - /Element
  2158.  
  2159. provides: [Selectors]
  2160.  
  2161. ...
  2162. */
  2163.  
  2164. Native.implement([Document, Element], {
  2165.  
  2166.     getElements: function(expression, nocash){
  2167.         expression = expression.split(',');
  2168.         var items, local = {};
  2169.         for (var i = 0, l = expression.length; i < l; i++){
  2170.             var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
  2171.             if (i != 0 && elements.item) elements = $A(elements);
  2172.             items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
  2173.         }
  2174.         return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
  2175.     }
  2176.  
  2177. });
  2178.  
  2179. Element.implement({
  2180.  
  2181.     match: function(selector){
  2182.         if (!selector || (selector == this)) return true;
  2183.         var tagid = Selectors.Utils.parseTagAndID(selector);
  2184.         var tag = tagid[0], id = tagid[1];
  2185.         if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
  2186.         var parsed = Selectors.Utils.parseSelector(selector);
  2187.         return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
  2188.     }
  2189.  
  2190. });
  2191.  
  2192. var Selectors = {Cache: {nth: {}, parsed: {}}};
  2193.  
  2194. Selectors.RegExps = {
  2195.     id: (/#([\w-]+)/),
  2196.     tag: (/^(\w+|\*)/),
  2197.     quick: (/^(\w+|\*)$/),
  2198.     splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
  2199.     combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
  2200. };
  2201.  
  2202. Selectors.Utils = {
  2203.  
  2204.     chk: function(item, uniques){
  2205.         if (!uniques) return true;
  2206.         var uid = $uid(item);
  2207.         if (!uniques[uid]) return uniques[uid] = true;
  2208.         return false;
  2209.     },
  2210.  
  2211.     parseNthArgument: function(argument){
  2212.         if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
  2213.         var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
  2214.         if (!parsed) return false;
  2215.         var inta = parseInt(parsed[1], 10);
  2216.         var a = (inta || inta === 0) ? inta : 1;
  2217.         var special = parsed[2] || false;
  2218.         var b = parseInt(parsed[3], 10) || 0;
  2219.         if (a != 0){
  2220.             b--;
  2221.             while (b < 1) b += a;
  2222.             while (b >= a) b -= a;
  2223.         } else {
  2224.             a = b;
  2225.             special = 'index';
  2226.         }
  2227.         switch (special){
  2228.             case 'n': parsed = {a: a, b: b, special: 'n'}; break;
  2229.             case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
  2230.             case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
  2231.             case 'first': parsed = {a: 0, special: 'index'}; break;
  2232.             case 'last': parsed = {special: 'last-child'}; break;
  2233.             case 'only': parsed = {special: 'only-child'}; break;
  2234.             default: parsed = {a: (a - 1), special: 'index'};
  2235.         }
  2236.  
  2237.         return Selectors.Cache.nth[argument] = parsed;
  2238.     },
  2239.  
  2240.     parseSelector: function(selector){
  2241.         if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
  2242.         var m, parsed = {classes: [], pseudos: [], attributes: []};
  2243.         while ((m = Selectors.RegExps.combined.exec(selector))){
  2244.             var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
  2245.             if (cn){
  2246.                 parsed.classes.push(cn);
  2247.             } else if (pn){
  2248.                 var parser = Selectors.Pseudo.get(pn);
  2249.                 if (parser) parsed.pseudos.push({parser: parser, argument: pa});
  2250.                 else parsed.attributes.push({name: pn, operator: '=', value: pa});
  2251.             } else if (an){
  2252.                 parsed.attributes.push({name: an, operator: ao, value: av});
  2253.             }
  2254.         }
  2255.         if (!parsed.classes.length) delete parsed.classes;
  2256.         if (!parsed.attributes.length) delete parsed.attributes;
  2257.         if (!parsed.pseudos.length) delete parsed.pseudos;
  2258.         if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
  2259.         return Selectors.Cache.parsed[selector] = parsed;
  2260.     },
  2261.  
  2262.     parseTagAndID: function(selector){
  2263.         var tag = selector.match(Selectors.RegExps.tag);
  2264.         var id = selector.match(Selectors.RegExps.id);
  2265.         return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
  2266.     },
  2267.  
  2268.     filter: function(item, parsed, local){
  2269.         var i;
  2270.         if (parsed.classes){
  2271.             for (i = parsed.classes.length; i--; i){
  2272.                 var cn = parsed.classes[i];
  2273.                 if (!Selectors.Filters.byClass(item, cn)) return false;
  2274.             }
  2275.         }
  2276.         if (parsed.attributes){
  2277.             for (i = parsed.attributes.length; i--; i){
  2278.                 var att = parsed.attributes[i];
  2279.                 if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
  2280.             }
  2281.         }
  2282.         if (parsed.pseudos){
  2283.             for (i = parsed.pseudos.length; i--; i){
  2284.                 var psd = parsed.pseudos[i];
  2285.                 if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
  2286.             }
  2287.         }
  2288.         return true;
  2289.     },
  2290.  
  2291.     getByTagAndID: function(ctx, tag, id){
  2292.         if (id){
  2293.             var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
  2294.             return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
  2295.         } else {
  2296.             return ctx.getElementsByTagName(tag);
  2297.         }
  2298.     },
  2299.  
  2300.     search: function(self, expression, local){
  2301.         var splitters = [];
  2302.  
  2303.         var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
  2304.             splitters.push(m1);
  2305.             return ':)' + m2;
  2306.         }).split(':)');
  2307.  
  2308.         var items, filtered, item;
  2309.  
  2310.         for (var i = 0, l = selectors.length; i < l; i++){
  2311.  
  2312.             var selector = selectors[i];
  2313.  
  2314.             if (i == 0 && Selectors.RegExps.quick.test(selector)){
  2315.                 items = self.getElementsByTagName(selector);
  2316.                 continue;
  2317.             }
  2318.  
  2319.             var splitter = splitters[i - 1];
  2320.  
  2321.             var tagid = Selectors.Utils.parseTagAndID(selector);
  2322.             var tag = tagid[0], id = tagid[1];
  2323.  
  2324.             if (i == 0){
  2325.                 items = Selectors.Utils.getByTagAndID(self, tag, id);
  2326.             } else {
  2327.                 var uniques = {}, found = [];
  2328.                 for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
  2329.                 items = found;
  2330.             }
  2331.  
  2332.             var parsed = Selectors.Utils.parseSelector(selector);
  2333.  
  2334.             if (parsed){
  2335.                 filtered = [];
  2336.                 for (var m = 0, n = items.length; m < n; m++){
  2337.                     item = items[m];
  2338.                     if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
  2339.                 }
  2340.                 items = filtered;
  2341.             }
  2342.  
  2343.         }
  2344.  
  2345.         return items;
  2346.  
  2347.     }
  2348.  
  2349. };
  2350.  
  2351. Selectors.Getters = {
  2352.  
  2353.     ' ': function(found, self, tag, id, uniques){
  2354.         var items = Selectors.Utils.getByTagAndID(self, tag, id);
  2355.         for (var i = 0, l = items.length; i < l; i++){
  2356.             var item = items[i];
  2357.             if (Selectors.Utils.chk(item, uniques)) found.push(item);
  2358.         }
  2359.         return found;
  2360.     },
  2361.  
  2362.     '>': function(found, self, tag, id, uniques){
  2363.         var children = Selectors.Utils.getByTagAndID(self, tag, id);
  2364.         for (var i = 0, l = children.length; i < l; i++){
  2365.             var child = children[i];
  2366.             if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
  2367.         }
  2368.         return found;
  2369.     },
  2370.  
  2371.     '+': function(found, self, tag, id, uniques){
  2372.         while ((self = self.nextSibling)){
  2373.             if (self.nodeType == 1){
  2374.                 if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
  2375.                 break;
  2376.             }
  2377.         }
  2378.         return found;
  2379.     },
  2380.  
  2381.     '~': function(found, self, tag, id, uniques){
  2382.         while ((self = self.nextSibling)){
  2383.             if (self.nodeType == 1){
  2384.                 if (!Selectors.Utils.chk(self, uniques)) break;
  2385.                 if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
  2386.             }
  2387.         }
  2388.         return found;
  2389.     }
  2390.  
  2391. };
  2392.  
  2393. Selectors.Filters = {
  2394.  
  2395.     byTag: function(self, tag){
  2396.         return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
  2397.     },
  2398.  
  2399.     byID: function(self, id){
  2400.         return (!id || (self.id && self.id == id));
  2401.     },
  2402.  
  2403.     byClass: function(self, klass){
  2404.         return (self.className && self.className.contains && self.className.contains(klass, ' '));
  2405.     },
  2406.  
  2407.     byPseudo: function(self, parser, argument, local){
  2408.         return parser.call(self, argument, local);
  2409.     },
  2410.  
  2411.     byAttribute: function(self, name, operator, value){
  2412.         var result = Element.prototype.getProperty.call(self, name);
  2413.         if (!result) return (operator == '!=');
  2414.         if (!operator || value == undefined) return true;
  2415.         switch (operator){
  2416.             case '=': return (result == value);
  2417.             case '*=': return (result.contains(value));
  2418.             case '^=': return (result.substr(0, value.length) == value);
  2419.             case '$=': return (result.substr(result.length - value.length) == value);
  2420.             case '!=': return (result != value);
  2421.             case '~=': return result.contains(value, ' ');
  2422.             case '|=': return result.contains(value, '-');
  2423.         }
  2424.         return false;
  2425.     }
  2426.  
  2427. };
  2428.  
  2429. Selectors.Pseudo = new Hash({
  2430.  
  2431.     // w3c pseudo selectors
  2432.  
  2433.     checked: function(){
  2434.         return this.checked;
  2435.     },
  2436.    
  2437.     empty: function(){
  2438.         return !(this.innerText || this.textContent || '').length;
  2439.     },
  2440.  
  2441.     not: function(selector){
  2442.         return !Element.match(this, selector);
  2443.     },
  2444.  
  2445.     contains: function(text){
  2446.         return (this.innerText || this.textContent || '').contains(text);
  2447.     },
  2448.  
  2449.     'first-child': function(){
  2450.         return Selectors.Pseudo.index.call(this, 0);
  2451.     },
  2452.  
  2453.     'last-child': function(){
  2454.         var element = this;
  2455.         while ((element = element.nextSibling)){
  2456.             if (element.nodeType == 1) return false;
  2457.         }
  2458.         return true;
  2459.     },
  2460.  
  2461.     'only-child': function(){
  2462.         var prev = this;
  2463.         while ((prev = prev.previousSibling)){
  2464.             if (prev.nodeType == 1) return false;
  2465.         }
  2466.         var next = this;
  2467.         while ((next = next.nextSibling)){
  2468.             if (next.nodeType == 1) return false;
  2469.         }
  2470.         return true;
  2471.     },
  2472.  
  2473.     'nth-child': function(argument, local){
  2474.         argument = (argument == undefined) ? 'n' : argument;
  2475.         var parsed = Selectors.Utils.parseNthArgument(argument);
  2476.         if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
  2477.         var count = 0;
  2478.         local.positions = local.positions || {};
  2479.         var uid = $uid(this);
  2480.         if (!local.positions[uid]){
  2481.             var self = this;
  2482.             while ((self = self.previousSibling)){
  2483.                 if (self.nodeType != 1) continue;
  2484.                 count ++;
  2485.                 var position = local.positions[$uid(self)];
  2486.                 if (position != undefined){
  2487.                     count = position + count;
  2488.                     break;
  2489.                 }
  2490.             }
  2491.             local.positions[uid] = count;
  2492.         }
  2493.         return (local.positions[uid] % parsed.a == parsed.b);
  2494.     },
  2495.  
  2496.     // custom pseudo selectors
  2497.  
  2498.     index: function(index){
  2499.         var element = this, count = 0;
  2500.         while ((element = element.previousSibling)){
  2501.             if (element.nodeType == 1 && ++count > index) return false;
  2502.         }
  2503.         return (count == index);
  2504.     },
  2505.  
  2506.     even: function(argument, local){
  2507.         return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
  2508.     },
  2509.  
  2510.     odd: function(argument, local){
  2511.         return Selectors.Pseudo['nth-child'].call(this, '2n', local);
  2512.     },
  2513.    
  2514.     selected: function(){
  2515.         return this.selected;
  2516.     },
  2517.    
  2518.     enabled: function(){
  2519.         return (this.disabled === false);
  2520.     }
  2521.  
  2522. });
  2523.  
  2524. /*
  2525. ---
  2526.  
  2527. script: Event.js
  2528.  
  2529. description: Contains the Event Class, to make the event object cross-browser.
  2530.  
  2531. license: MIT-style license.
  2532.  
  2533. requires:
  2534. - /Window
  2535. - /Document
  2536. - /Hash
  2537. - /Array
  2538. - /Function
  2539. - /String
  2540.  
  2541. provides: [Event]
  2542.  
  2543. ...
  2544. */
  2545.  
  2546. var Event = new Native({
  2547.  
  2548.     name: 'Event',
  2549.  
  2550.     initialize: function(event, win){
  2551.         win = win || window;
  2552.         var doc = win.document;
  2553.         event = event || win.event;
  2554.         if (event.$extended) return event;
  2555.         this.$extended = true;
  2556.         var type = event.type;
  2557.         var target = event.target || event.srcElement;
  2558.         while (target && target.nodeType == 3) target = target.parentNode;
  2559.  
  2560.         if (type.test(/key/)){
  2561.             var code = event.which || event.keyCode;
  2562.             var key = Event.Keys.keyOf(code);
  2563.             if (type == 'keydown'){
  2564.                 var fKey = code - 111;
  2565.                 if (fKey > 0 && fKey < 13) key = 'f' + fKey;
  2566.             }
  2567.             key = key || String.fromCharCode(code).toLowerCase();
  2568.         } else if (type.match(/(click|mouse|menu)/i)){
  2569.             doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
  2570.             var page = {
  2571.                 x: event.pageX || event.clientX + doc.scrollLeft,
  2572.                 y: event.pageY || event.clientY + doc.scrollTop
  2573.             };
  2574.             var client = {
  2575.                 x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
  2576.                 y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
  2577.             };
  2578.             if (type.match(/DOMMouseScroll|mousewheel/)){
  2579.                 var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
  2580.             }
  2581.             var rightClick = (event.which == 3) || (event.button == 2);
  2582.             var related = null;
  2583.             if (type.match(/over|out/)){
  2584.                 switch (type){
  2585.                     case 'mouseover': related = event.relatedTarget || event.fromElement; break;
  2586.                     case 'mouseout': related = event.relatedTarget || event.toElement;
  2587.                 }
  2588.                 if (!(function(){
  2589.                     while (related && related.nodeType == 3) related = related.parentNode;
  2590.                     return true;
  2591.                 }).create({attempt: Browser.Engine.gecko})()) related = false;
  2592.             }
  2593.         }
  2594.  
  2595.         return $extend(this, {
  2596.             event: event,
  2597.             type: type,
  2598.  
  2599.             page: page,
  2600.             client: client,
  2601.             rightClick: rightClick,
  2602.  
  2603.             wheel: wheel,
  2604.  
  2605.             relatedTarget: related,
  2606.             target: target,
  2607.  
  2608.             code: code,
  2609.             key: key,
  2610.  
  2611.             shift: event.shiftKey,
  2612.             control: event.ctrlKey,
  2613.             alt: event.altKey,
  2614.             meta: event.metaKey
  2615.         });
  2616.     }
  2617.  
  2618. });
  2619.  
  2620. Event.Keys = new Hash({
  2621.     'enter': 13,
  2622.     'up': 38,
  2623.     'down': 40,
  2624.     'left': 37,
  2625.     'right': 39,
  2626.     'esc': 27,
  2627.     'space': 32,
  2628.     'backspace': 8,
  2629.     'tab': 9,
  2630.     'delete': 46
  2631. });
  2632.  
  2633. Event.implement({
  2634.  
  2635.     stop: function(){
  2636.         return this.stopPropagation().preventDefault();
  2637.     },
  2638.  
  2639.     stopPropagation: function(){
  2640.         if (this.event.stopPropagation) this.event.stopPropagation();
  2641.         else this.event.cancelBubble = true;
  2642.         return this;
  2643.     },
  2644.  
  2645.     preventDefault: function(){
  2646.         if (this.event.preventDefault) this.event.preventDefault();
  2647.         else this.event.returnValue = false;
  2648.         return this;
  2649.     }
  2650.  
  2651. });
  2652.  
  2653. /*
  2654. ---
  2655.  
  2656. script: Element.Event.js
  2657.  
  2658. description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events.
  2659.  
  2660. license: MIT-style license.
  2661.  
  2662. requires:
  2663. - /Element
  2664. - /Event
  2665.  
  2666. provides: [Element.Event]
  2667.  
  2668. ...
  2669. */
  2670.  
  2671. Element.Properties.events = {set: function(events){
  2672.     this.addEvents(events);
  2673. }};
  2674.  
  2675. Native.implement([Element, Window, Document], {
  2676.  
  2677.     addEvent: function(type, fn){
  2678.         var events = this.retrieve('events', {});
  2679.         events[type] = events[type] || {'keys': [], 'values': []};
  2680.         if (events[type].keys.contains(fn)) return this;
  2681.         events[type].keys.push(fn);
  2682.         var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
  2683.         if (custom){
  2684.             if (custom.onAdd) custom.onAdd.call(this, fn);
  2685.             if (custom.condition){
  2686.                 condition = function(event){
  2687.                     if (custom.condition.call(this, event)) return fn.call(this, event);
  2688.                     return true;
  2689.                 };
  2690.             }
  2691.             realType = custom.base || realType;
  2692.         }
  2693.         var defn = function(){
  2694.             return fn.call(self);
  2695.         };
  2696.         var nativeEvent = Element.NativeEvents[realType];
  2697.         if (nativeEvent){
  2698.             if (nativeEvent == 2){
  2699.                 defn = function(event){
  2700.                     event = new Event(event, self.getWindow());
  2701.                     if (condition.call(self, event) === false) event.stop();
  2702.                 };
  2703.             }
  2704.             this.addListener(realType, defn);
  2705.         }
  2706.         events[type].values.push(defn);
  2707.         return this;
  2708.     },
  2709.  
  2710.     removeEvent: function(type, fn){
  2711.         var events = this.retrieve('events');
  2712.         if (!events || !events[type]) return this;
  2713.         var pos = events[type].keys.indexOf(fn);
  2714.         if (pos == -1) return this;
  2715.         events[type].keys.splice(pos, 1);
  2716.         var value = events[type].values.splice(pos, 1)[0];
  2717.         var custom = Element.Events.get(type);
  2718.         if (custom){
  2719.             if (custom.onRemove) custom.onRemove.call(this, fn);
  2720.             type = custom.base || type;
  2721.         }
  2722.         return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
  2723.     },
  2724.  
  2725.     addEvents: function(events){
  2726.         for (var event in events) this.addEvent(event, events[event]);
  2727.         return this;
  2728.     },
  2729.  
  2730.     removeEvents: function(events){
  2731.         var type;
  2732.         if ($type(events) == 'object'){
  2733.             for (type in events) this.removeEvent(type, events[type]);
  2734.             return this;
  2735.         }
  2736.         var attached = this.retrieve('events');
  2737.         if (!attached) return this;
  2738.         if (!events){
  2739.             for (type in attached) this.removeEvents(type);
  2740.             this.eliminate('events');
  2741.         } else if (attached[events]){
  2742.             while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]);
  2743.             attached[events] = null;
  2744.         }
  2745.         return this;
  2746.     },
  2747.  
  2748.     fireEvent: function(type, args, delay){
  2749.         var events = this.retrieve('events');
  2750.         if (!events || !events[type]) return this;
  2751.         events[type].keys.each(function(fn){
  2752.             fn.create({'bind': this, 'delay': delay, 'arguments': args})();
  2753.         }, this);
  2754.         return this;
  2755.     },
  2756.  
  2757.     cloneEvents: function(from, type){
  2758.         from = document.id(from);
  2759.         var fevents = from.retrieve('events');
  2760.         if (!fevents) return this;
  2761.         if (!type){
  2762.             for (var evType in fevents) this.cloneEvents(from, evType);
  2763.         } else if (fevents[type]){
  2764.             fevents[type].keys.each(function(fn){
  2765.                 this.addEvent(type, fn);
  2766.             }, this);
  2767.         }
  2768.         return this;
  2769.     }
  2770.  
  2771. });
  2772.  
  2773. Element.NativeEvents = {
  2774.     click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
  2775.     mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
  2776.     mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
  2777.     keydown: 2, keypress: 2, keyup: 2, //keyboard
  2778.     focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
  2779.     load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
  2780.     error: 1, abort: 1, scroll: 1 //misc
  2781. };
  2782.  
  2783. (function(){
  2784.  
  2785. var $check = function(event){
  2786.     var related = event.relatedTarget;
  2787.     if (related == undefined) return true;
  2788.     if (related === false) return false;
  2789.     return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));
  2790. };
  2791.  
  2792. Element.Events = new Hash({
  2793.  
  2794.     mouseenter: {
  2795.         base: 'mouseover',
  2796.         condition: $check
  2797.     },
  2798.  
  2799.     mouseleave: {
  2800.         base: 'mouseout',
  2801.         condition: $check
  2802.     },
  2803.  
  2804.     mousewheel: {
  2805.         base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
  2806.     }
  2807.  
  2808. });
  2809.  
  2810. })();
  2811.  
  2812. /*
  2813. ---
  2814.  
  2815. script: Class.js
  2816.  
  2817. description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
  2818.  
  2819. license: MIT-style license.
  2820.  
  2821. requires:
  2822. - /$util
  2823. - /Native
  2824. - /Array
  2825. - /String
  2826. - /Function
  2827. - /Number
  2828. - /Hash
  2829.  
  2830. provides: [Class]
  2831.  
  2832. ...
  2833. */
  2834.  
  2835. function Class(params){
  2836.    
  2837.     if (params instanceof Function) params = {initialize: params};
  2838.    
  2839.     var newClass = function(){
  2840.         Object.reset(this);
  2841.         if (newClass._prototyping) return this;
  2842.         this._current = $empty;
  2843.         var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
  2844.         delete this._current; delete this.caller;
  2845.         return value;
  2846.     }.extend(this);
  2847.    
  2848.     newClass.implement(params);
  2849.    
  2850.     newClass.constructor = Class;
  2851.     newClass.prototype.constructor = newClass;
  2852.  
  2853.     return newClass;
  2854.  
  2855. };
  2856.  
  2857. Function.prototype.protect = function(){
  2858.     this._protected = true;
  2859.     return this;
  2860. };
  2861.  
  2862. Object.reset = function(object, key){
  2863.        
  2864.     if (key == null){
  2865.         for (var p in object) Object.reset(object, p);
  2866.         return object;
  2867.     }
  2868.    
  2869.     delete object[key];
  2870.    
  2871.     switch ($type(object[key])){
  2872.         case 'object':
  2873.             var F = function(){};
  2874.             F.prototype = object[key];
  2875.             var i = new F;
  2876.             object[key] = Object.reset(i);
  2877.         break;
  2878.         case 'array': object[key] = $unlink(object[key]); break;
  2879.     }
  2880.    
  2881.     return object;
  2882.    
  2883. };
  2884.  
  2885. new Native({name: 'Class', initialize: Class}).extend({
  2886.  
  2887.     instantiate: function(F){
  2888.         F._prototyping = true;
  2889.         var proto = new F;
  2890.         delete F._prototyping;
  2891.         return proto;
  2892.     },
  2893.    
  2894.     wrap: function(self, key, method){
  2895.         if (method._origin) method = method._origin;
  2896.        
  2897.         return function(){
  2898.             if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.');
  2899.             var caller = this.caller, current = this._current;
  2900.             this.caller = current; this._current = arguments.callee;
  2901.             var result = method.apply(this, arguments);
  2902.             this._current = current; this.caller = caller;
  2903.             return result;
  2904.         }.extend({_owner: self, _origin: method, _name: key});
  2905.  
  2906.     }
  2907.    
  2908. });
  2909.  
  2910. Class.implement({
  2911.    
  2912.     implement: function(key, value){
  2913.        
  2914.         if ($type(key) == 'object'){
  2915.             for (var p in key) this.implement(p, key[p]);
  2916.             return this;
  2917.         }
  2918.        
  2919.         var mutator = Class.Mutators[key];
  2920.        
  2921.         if (mutator){
  2922.             value = mutator.call(this, value);
  2923.             if (value == null) return this;
  2924.         }
  2925.        
  2926.         var proto = this.prototype;
  2927.  
  2928.         switch ($type(value)){
  2929.            
  2930.             case 'function':
  2931.                 if (value._hidden) return this;
  2932.                 proto[key] = Class.wrap(this, key, value);
  2933.             break;
  2934.            
  2935.             case 'object':
  2936.                 var previous = proto[key];
  2937.                 if ($type(previous) == 'object') $mixin(previous, value);
  2938.                 else proto[key] = $unlink(value);
  2939.             break;
  2940.            
  2941.             case 'array':
  2942.                 proto[key] = $unlink(value);
  2943.             break;
  2944.            
  2945.             default: proto[key] = value;
  2946.  
  2947.         }
  2948.        
  2949.         return this;
  2950.  
  2951.     }
  2952.    
  2953. });
  2954.  
  2955. Class.Mutators = {
  2956.    
  2957.     Extends: function(parent){
  2958.  
  2959.         this.parent = parent;
  2960.         this.prototype = Class.instantiate(parent);
  2961.  
  2962.         this.implement('parent', function(){
  2963.             var name = this.caller._name, previous = this.caller._owner.parent.prototype[name];
  2964.             if (!previous) throw new Error('The method "' + name + '" has no parent.');
  2965.             return previous.apply(this, arguments);
  2966.         }.protect());
  2967.  
  2968.     },
  2969.  
  2970.     Implements: function(items){
  2971.         $splat(items).each(function(item){
  2972.             if (item instanceof Function) item = Class.instantiate(item);
  2973.             this.implement(item);
  2974.         }, this);
  2975.  
  2976.     }
  2977.    
  2978. };
  2979.  
  2980. /*
  2981. ---
  2982.  
  2983. script: Class.Extras.js
  2984.  
  2985. description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
  2986.  
  2987. license: MIT-style license.
  2988.  
  2989. requires:
  2990. - /Class
  2991.  
  2992. provides: [Chain, Events, Options]
  2993.  
  2994. ...
  2995. */
  2996.  
  2997. var Chain = new Class({
  2998.  
  2999.     $chain: [],
  3000.  
  3001.     chain: function(){
  3002.         this.$chain.extend(Array.flatten(arguments));
  3003.         return this;
  3004.     },
  3005.  
  3006.     callChain: function(){
  3007.         return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
  3008.     },
  3009.  
  3010.     clearChain: function(){
  3011.         this.$chain.empty();
  3012.         return this;
  3013.     }
  3014.  
  3015. });
  3016.  
  3017. var Events = new Class({
  3018.  
  3019.     $events: {},
  3020.  
  3021.     addEvent: function(type, fn, internal){
  3022.         type = Events.removeOn(type);
  3023.         if (fn != $empty){
  3024.             this.$events[type] = this.$events[type] || [];
  3025.             this.$events[type].include(fn);
  3026.             if (internal) fn.internal = true;
  3027.         }
  3028.         return this;
  3029.     },
  3030.  
  3031.     addEvents: function(events){
  3032.         for (var type in events) this.addEvent(type, events[type]);
  3033.         return this;
  3034.     },
  3035.  
  3036.     fireEvent: function(type, args, delay){
  3037.         type = Events.removeOn(type);
  3038.         if (!this.$events || !this.$events[type]) return this;
  3039.         this.$events[type].each(function(fn){
  3040.             fn.create({'bind': this, 'delay': delay, 'arguments': args})();
  3041.         }, this);
  3042.         return this;
  3043.     },
  3044.  
  3045.     removeEvent: function(type, fn){
  3046.         type = Events.removeOn(type);
  3047.         if (!this.$events[type]) return this;
  3048.         if (!fn.internal) this.$events[type].erase(fn);
  3049.         return this;
  3050.     },
  3051.  
  3052.     removeEvents: function(events){
  3053.         var type;
  3054.         if ($type(events) == 'object'){
  3055.             for (type in events) this.removeEvent(type, events[type]);
  3056.             return this;
  3057.         }
  3058.         if (events) events = Events.removeOn(events);
  3059.         for (type in this.$events){
  3060.             if (events && events != type) continue;
  3061.             var fns = this.$events[type];
  3062.             for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
  3063.         }
  3064.         return this;
  3065.     }
  3066.  
  3067. });
  3068.  
  3069. Events.removeOn = function(string){
  3070.     return string.replace(/^on([A-Z])/, function(full, first){
  3071.         return first.toLowerCase();
  3072.     });
  3073. };
  3074.  
  3075. var Options = new Class({
  3076.  
  3077.     setOptions: function(){
  3078.         this.options = $merge.run([this.options].extend(arguments));
  3079.         if (!this.addEvent) return this;
  3080.         for (var option in this.options){
  3081.             if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
  3082.             this.addEvent(option, this.options[option]);
  3083.             delete this.options[option];
  3084.         }
  3085.         return this;
  3086.     }
  3087.  
  3088. });
  3089.  
  3090. /*
  3091. ---
  3092.  
  3093. script: Request.js
  3094.  
  3095. description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
  3096.  
  3097. license: MIT-style license.
  3098.  
  3099. requires:
  3100. - /Element
  3101. - /Chain
  3102. - /Events
  3103. - /Options
  3104. - /Browser
  3105.  
  3106. provides: [Request]
  3107.  
  3108. ...
  3109. */
  3110.  
  3111. var Request = new Class({
  3112.  
  3113.     Implements: [Chain, Events, Options],
  3114.  
  3115.     options: {/*
  3116.         onRequest: $empty,
  3117.         onComplete: $empty,
  3118.         onCancel: $empty,
  3119.         onSuccess: $empty,
  3120.         onFailure: $empty,
  3121.         onException: $empty,*/
  3122.         url: '',
  3123.         data: '',
  3124.         headers: {
  3125.             'X-Requested-With': 'XMLHttpRequest',
  3126.             'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
  3127.         },
  3128.         async: true,
  3129.         format: false,
  3130.         method: 'post',
  3131.         link: 'ignore',
  3132.         isSuccess: null,
  3133.         emulation: true,
  3134.         urlEncoded: true,
  3135.         encoding: 'utf-8',
  3136.         evalScripts: false,
  3137.         evalResponse: false,
  3138.         noCache: false
  3139.     },
  3140.  
  3141.     initialize: function(options){
  3142.         this.xhr = new Browser.Request();
  3143.         this.setOptions(options);
  3144.         this.options.isSuccess = this.options.isSuccess || this.isSuccess;
  3145.         this.headers = new Hash(this.options.headers);
  3146.     },
  3147.  
  3148.     onStateChange: function(){
  3149.         if (this.xhr.readyState != 4 || !this.running) return;
  3150.         this.running = false;
  3151.         this.status = 0;
  3152.         $try(function(){
  3153.             this.status = this.xhr.status;
  3154.         }.bind(this));
  3155.         this.xhr.onreadystatechange = $empty;
  3156.         if (this.options.isSuccess.call(this, this.status)){
  3157.             this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
  3158.             this.success(this.response.text, this.response.xml);
  3159.         } else {
  3160.             this.response = {text: null, xml: null};
  3161.             this.failure();
  3162.         }
  3163.     },
  3164.  
  3165.     isSuccess: function(){
  3166.         return ((this.status >= 200) && (this.status < 300));
  3167.     },
  3168.  
  3169.     processScripts: function(text){
  3170.         if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
  3171.         return text.stripScripts(this.options.evalScripts);
  3172.     },
  3173.  
  3174.     success: function(text, xml){
  3175.         this.onSuccess(this.processScripts(text), xml);
  3176.     },
  3177.  
  3178.     onSuccess: function(){
  3179.         this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
  3180.     },
  3181.  
  3182.     failure: function(){
  3183.         this.onFailure();
  3184.     },
  3185.  
  3186.     onFailure: function(){
  3187.         this.fireEvent('complete').fireEvent('failure', this.xhr);
  3188.     },
  3189.  
  3190.     setHeader: function(name, value){
  3191.         this.headers.set(name, value);
  3192.         return this;
  3193.     },
  3194.  
  3195.     getHeader: function(name){
  3196.         return $try(function(){
  3197.             return this.xhr.getResponseHeader(name);
  3198.         }.bind(this));
  3199.     },
  3200.  
  3201.     check: function(){
  3202.         if (!this.running) return true;
  3203.         switch (this.options.link){
  3204.             case 'cancel': this.cancel(); return true;
  3205.             case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
  3206.         }
  3207.         return false;
  3208.     },
  3209.  
  3210.     send: function(options){
  3211.         if (!this.check(options)) return this;
  3212.         this.running = true;
  3213.  
  3214.         var type = $type(options);
  3215.         if (type == 'string' || type == 'element') options = {data: options};
  3216.  
  3217.         var old = this.options;
  3218.         options = $extend({data: old.data, url: old.url, method: old.method}, options);
  3219.         var data = options.data, url = String(options.url), method = options.method.toLowerCase();
  3220.  
  3221.         switch ($type(data)){
  3222.             case 'element': data = document.id(data).toQueryString(); break;
  3223.             case 'object': case 'hash': data = Hash.toQueryString(data);
  3224.         }
  3225.  
  3226.         if (this.options.format){
  3227.             var format = 'format=' + this.options.format;
  3228.             data = (data) ? format + '&' + data : format;
  3229.         }
  3230.  
  3231.         if (this.options.emulation && !['get', 'post'].contains(method)){
  3232.             var _method = '_method=' + method;
  3233.             data = (data) ? _method + '&' + data : _method;
  3234.             method = 'post';
  3235.         }
  3236.  
  3237.         if (this.options.urlEncoded && method == 'post'){
  3238.             var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
  3239.             this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
  3240.         }
  3241.  
  3242.         if (this.options.noCache){
  3243.             var noCache = 'noCache=' + new Date().getTime();
  3244.             data = (data) ? noCache + '&' + data : noCache;
  3245.         }
  3246.  
  3247.         var trimPosition = url.lastIndexOf('/');
  3248.         if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
  3249.  
  3250.         if (data && method == 'get'){
  3251.             url = url + (url.contains('?') ? '&' : '?') + data;
  3252.             data = null;
  3253.         }
  3254.  
  3255.         this.xhr.open(method.toUpperCase(), url, this.options.async);
  3256.  
  3257.         this.xhr.onreadystatechange = this.onStateChange.bind(this);
  3258.  
  3259.         this.headers.each(function(value, key){
  3260.             try {
  3261.                 this.xhr.setRequestHeader(key, value);
  3262.             } catch (e){
  3263.                 this.fireEvent('exception', [key, value]);
  3264.             }
  3265.         }, this);
  3266.  
  3267.         this.fireEvent('request');
  3268.         this.xhr.send(data);
  3269.         if (!this.options.async) this.onStateChange();
  3270.         return this;
  3271.     },
  3272.  
  3273.     cancel: function(){
  3274.         if (!this.running) return this;
  3275.         this.running = false;
  3276.         this.xhr.abort();
  3277.         this.xhr.onreadystatechange = $empty;
  3278.         this.xhr = new Browser.Request();
  3279.         this.fireEvent('cancel');
  3280.         return this;
  3281.     }
  3282.  
  3283. });
  3284.  
  3285. (function(){
  3286.  
  3287. var methods = {};
  3288. ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
  3289.     methods[method] = function(){
  3290.         var params = Array.link(arguments, {url: String.type, data: $defined});
  3291.         return this.send($extend(params, {method: method}));
  3292.     };
  3293. });
  3294.  
  3295. Request.implement(methods);
  3296.  
  3297. })();
  3298.  
  3299. Element.Properties.send = {
  3300.  
  3301.     set: function(options){
  3302.         var send = this.retrieve('send');
  3303.         if (send) send.cancel();
  3304.         return this.eliminate('send').store('send:options', $extend({
  3305.             data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
  3306.         }, options));
  3307.     },
  3308.  
  3309.     get: function(options){
  3310.         if (options || !this.retrieve('send')){
  3311.             if (options || !this.retrieve('send:options')) this.set('send', options);
  3312.             this.store('send', new Request(this.retrieve('send:options')));
  3313.         }
  3314.         return this.retrieve('send');
  3315.     }
  3316.  
  3317. };
  3318.  
  3319. Element.implement({
  3320.  
  3321.     send: function(url){
  3322.         var sender = this.get('send');
  3323.         sender.send({data: this, url: url || sender.options.url});
  3324.         return this;
  3325.     }
  3326.  
  3327. });
  3328.  
  3329. /*
  3330. ---
  3331.  
  3332. script: Request.HTML.js
  3333.  
  3334. description: Extends the basic Request Class with additional methods for interacting with HTML responses.
  3335.  
  3336. license: MIT-style license.
  3337.  
  3338. requires:
  3339. - /Request
  3340. - /Element
  3341.  
  3342. provides: [Request.HTML]
  3343.  
  3344. ...
  3345. */
  3346.  
  3347. Request.HTML = new Class({
  3348.  
  3349.     Extends: Request,
  3350.  
  3351.     options: {
  3352.         update: false,
  3353.         append: false,
  3354.         evalScripts: true,
  3355.         filter: false
  3356.     },
  3357.  
  3358.     processHTML: function(text){
  3359.         var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
  3360.         text = (match) ? match[1] : text;
  3361.  
  3362.         var container = new Element('div');
  3363.  
  3364.         return $try(function(){
  3365.             var root = '<root>' + text + '</root>', doc;
  3366.             if (Browser.Engine.trident){
  3367.                 doc = new ActiveXObject('Microsoft.XMLDOM');
  3368.                 doc.async = false;
  3369.                 doc.loadXML(root);
  3370.             } else {
  3371.                 doc = new DOMParser().parseFromString(root, 'text/xml');
  3372.             }
  3373.             root = doc.getElementsByTagName('root')[0];
  3374.             if (!root) return null;
  3375.             for (var i = 0, k = root.childNodes.length; i < k; i++){
  3376.                 var child = Element.clone(root.childNodes[i], true, true);
  3377.                 if (child) container.grab(child);
  3378.             }
  3379.             return container;
  3380.         }) || container.set('html', text);
  3381.     },
  3382.  
  3383.     success: function(text){
  3384.         var options = this.options, response = this.response;
  3385.  
  3386.         response.html = text.stripScripts(function(script){
  3387.             response.javascript = script;
  3388.         });
  3389.  
  3390.         var temp = this.processHTML(response.html);
  3391.  
  3392.         response.tree = temp.childNodes;
  3393.         response.elements = temp.getElements('*');
  3394.  
  3395.         if (options.filter) response.tree = response.elements.filter(options.filter);
  3396.         if (options.update) document.id(options.update).empty().set('html', response.html);
  3397.         else if (options.append) document.id(options.append).adopt(temp.getChildren());
  3398.         if (options.evalScripts) $exec(response.javascript);
  3399.  
  3400.         this.onSuccess(response.tree, response.elements, response.html, response.javascript);
  3401.     }
  3402.  
  3403. });
  3404.  
  3405. Element.Properties.load = {
  3406.  
  3407.     set: function(options){
  3408.         var load = this.retrieve('load');
  3409.         if (load) load.cancel();
  3410.         return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options));
  3411.     },
  3412.  
  3413.     get: function(options){
  3414.         if (options || ! this.retrieve('load')){
  3415.             if (options || !this.retrieve('load:options')) this.set('load', options);
  3416.             this.store('load', new Request.HTML(this.retrieve('load:options')));
  3417.         }
  3418.         return this.retrieve('load');
  3419.     }
  3420.  
  3421. };
  3422.  
  3423. Element.implement({
  3424.  
  3425.     load: function(){
  3426.         this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
  3427.         return this;
  3428.     }
  3429.  
  3430. });
  3431.  
  3432. /*
  3433. ---
  3434.  
  3435. script: Fx.js
  3436.  
  3437. description: Contains the basic animation logic to be extended by all other Fx Classes.
  3438.  
  3439. license: MIT-style license.
  3440.  
  3441. requires:
  3442. - /Chain
  3443. - /Events
  3444. - /Options
  3445.  
  3446. provides: [Fx]
  3447.  
  3448. ...
  3449. */
  3450.  
  3451. var Fx = new Class({
  3452.  
  3453.     Implements: [Chain, Events, Options],
  3454.  
  3455.     options: {
  3456.         /*
  3457.         onStart: $empty,
  3458.         onCancel: $empty,
  3459.         onComplete: $empty,
  3460.         */
  3461.         fps: 50,
  3462.         unit: false,
  3463.         duration: 500,
  3464.         link: 'ignore'
  3465.     },
  3466.  
  3467.     initialize: function(options){
  3468.         this.subject = this.subject || this;
  3469.         this.setOptions(options);
  3470.         this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
  3471.         var wait = this.options.wait;
  3472.         if (wait === false) this.options.link = 'cancel';
  3473.     },
  3474.  
  3475.     getTransition: function(){
  3476.         return function(p){
  3477.             return -(Math.cos(Math.PI * p) - 1) / 2;
  3478.         };
  3479.     },
  3480.  
  3481.     step: function(){
  3482.         var time = $time();
  3483.         if (time < this.time + this.options.duration){
  3484.             var delta = this.transition((time - this.time) / this.options.duration);
  3485.             this.set(this.compute(this.from, this.to, delta));
  3486.         } else {
  3487.             this.set(this.compute(this.from, this.to, 1));
  3488.             this.complete();
  3489.         }
  3490.     },
  3491.  
  3492.     set: function(now){
  3493.         return now;
  3494.     },
  3495.  
  3496.     compute: function(from, to, delta){
  3497.         return Fx.compute(from, to, delta);
  3498.     },
  3499.  
  3500.     check: function(){
  3501.         if (!this.timer) return true;
  3502.         switch (this.options.link){
  3503.             case 'cancel': this.cancel(); return true;
  3504.             case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
  3505.         }
  3506.         return false;
  3507.     },
  3508.  
  3509.     start: function(from, to){
  3510.         if (!this.check(from, to)) return this;
  3511.         this.from = from;
  3512.         this.to = to;
  3513.         this.time = 0;
  3514.         this.transition = this.getTransition();
  3515.         this.startTimer();
  3516.         this.onStart();
  3517.         return this;
  3518.     },
  3519.  
  3520.     complete: function(){
  3521.         if (this.stopTimer()) this.onComplete();
  3522.         return this;
  3523.     },
  3524.  
  3525.     cancel: function(){
  3526.         if (this.stopTimer()) this.onCancel();
  3527.         return this;
  3528.     },
  3529.  
  3530.     onStart: function(){
  3531.         this.fireEvent('start', this.subject);
  3532.     },
  3533.  
  3534.     onComplete: function(){
  3535.         this.fireEvent('complete', this.subject);
  3536.         if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
  3537.     },
  3538.  
  3539.     onCancel: function(){
  3540.         this.fireEvent('cancel', this.subject).clearChain();
  3541.     },
  3542.  
  3543.     pause: function(){
  3544.         this.stopTimer();
  3545.         return this;
  3546.     },
  3547.  
  3548.     resume: function(){
  3549.         this.startTimer();
  3550.         return this;
  3551.     },
  3552.  
  3553.     stopTimer: function(){
  3554.         if (!this.timer) return false;
  3555.         this.time = $time() - this.time;
  3556.         this.timer = $clear(this.timer);
  3557.         return true;
  3558.     },
  3559.  
  3560.     startTimer: function(){
  3561.         if (this.timer) return false;
  3562.         this.time = $time() - this.time;
  3563.         this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
  3564.         return true;
  3565.     }
  3566.  
  3567. });
  3568.  
  3569. Fx.compute = function(from, to, delta){
  3570.     return (to - from) * delta + from;
  3571. };
  3572.  
  3573. Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
  3574.  
  3575. /*
  3576. ---
  3577.  
  3578. script: Fx.CSS.js
  3579.  
  3580. description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
  3581.  
  3582. license: MIT-style license.
  3583.  
  3584. requires:
  3585. - /Fx
  3586. - /Element.Style
  3587.  
  3588. provides: [Fx.CSS]
  3589.  
  3590. ...
  3591. */
  3592.  
  3593. Fx.CSS = new Class({
  3594.  
  3595.     Extends: Fx,
  3596.  
  3597.     //prepares the base from/to object
  3598.  
  3599.     prepare: function(element, property, values){
  3600.         values = $splat(values);
  3601.         var values1 = values[1];
  3602.         if (!$chk(values1)){
  3603.             values[1] = values[0];
  3604.             values[0] = element.getStyle(property);
  3605.         }
  3606.         var parsed = values.map(this.parse);
  3607.         return {from: parsed[0], to: parsed[1]};
  3608.     },
  3609.  
  3610.     //parses a value into an array
  3611.  
  3612.     parse: function(value){
  3613.         value = $lambda(value)();
  3614.         value = (typeof value == 'string') ? value.split(' ') : $splat(value);
  3615.         return value.map(function(val){
  3616.             val = String(val);
  3617.             var found = false;
  3618.             Fx.CSS.Parsers.each(function(parser, key){
  3619.                 if (found) return;
  3620.                 var parsed = parser.parse(val);
  3621.                 if ($chk(parsed)) found = {value: parsed, parser: parser};
  3622.             });
  3623.             found = found || {value: val, parser: Fx.CSS.Parsers.String};
  3624.             return found;
  3625.         });
  3626.     },
  3627.  
  3628.     //computes by a from and to prepared objects, using their parsers.
  3629.  
  3630.     compute: function(from, to, delta){
  3631.         var computed = [];
  3632.         (Math.min(from.length, to.length)).times(function(i){
  3633.             computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
  3634.         });
  3635.         computed.$family = {name: 'fx:css:value'};
  3636.         return computed;
  3637.     },
  3638.  
  3639.     //serves the value as settable
  3640.  
  3641.     serve: function(value, unit){
  3642.         if ($type(value) != 'fx:css:value') value = this.parse(value);
  3643.         var returned = [];
  3644.         value.each(function(bit){
  3645.             returned = returned.concat(bit.parser.serve(bit.value, unit));
  3646.         });
  3647.         return returned;
  3648.     },
  3649.  
  3650.     //renders the change to an element
  3651.  
  3652.     render: function(element, property, value, unit){
  3653.         element.setStyle(property, this.serve(value, unit));
  3654.     },
  3655.  
  3656.     //searches inside the page css to find the values for a selector
  3657.  
  3658.     search: function(selector){
  3659.         if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
  3660.         var to = {};
  3661.         Array.each(document.styleSheets, function(sheet, j){
  3662.             var href = sheet.href;
  3663.             if (href && href.contains('://') && !href.contains(document.domain)) return;
  3664.             var rules = sheet.rules || sheet.cssRules;
  3665.             Array.each(rules, function(rule, i){
  3666.                 if (!rule.style) return;
  3667.                 var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
  3668.                     return m.toLowerCase();
  3669.                 }) : null;
  3670.                 if (!selectorText || !selectorText.test('^' + selector + '$')) return;
  3671.                 Element.Styles.each(function(value, style){
  3672.                     if (!rule.style[style] || Element.ShortStyles[style]) return;
  3673.                     value = String(rule.style[style]);
  3674.                     to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
  3675.                 });
  3676.             });
  3677.         });
  3678.         return Fx.CSS.Cache[selector] = to;
  3679.     }
  3680.  
  3681. });
  3682.  
  3683. Fx.CSS.Cache = {};
  3684.  
  3685. Fx.CSS.Parsers = new Hash({
  3686.  
  3687.     Color: {
  3688.         parse: function(value){
  3689.             if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
  3690.             return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
  3691.         },
  3692.         compute: function(from, to, delta){
  3693.             return from.map(function(value, i){
  3694.                 return Math.round(Fx.compute(from[i], to[i], delta));
  3695.             });
  3696.         },
  3697.         serve: function(value){
  3698.             return value.map(Number);
  3699.         }
  3700.     },
  3701.  
  3702.     Number: {
  3703.         parse: parseFloat,
  3704.         compute: Fx.compute,
  3705.         serve: function(value, unit){
  3706.             return (unit) ? value + unit : value;
  3707.         }
  3708.     },
  3709.  
  3710.     String: {
  3711.         parse: $lambda(false),
  3712.         compute: $arguments(1),
  3713.         serve: $arguments(0)
  3714.     }
  3715.  
  3716. });
  3717.  
  3718. /*
  3719. ---
  3720.  
  3721. script: Fx.Tween.js
  3722.  
  3723. description: Formerly Fx.Style, effect to transition any CSS property for an element.
  3724.  
  3725. license: MIT-style license.
  3726.  
  3727. requires:
  3728. - /Fx.CSS
  3729.  
  3730. provides: [Fx.Tween, Element.fade, Element.highlight]
  3731.  
  3732. ...
  3733. */
  3734.  
  3735. Fx.Tween = new Class({
  3736.  
  3737.     Extends: Fx.CSS,
  3738.  
  3739.     initialize: function(element, options){
  3740.         this.element = this.subject = document.id(element);
  3741.         this.parent(options);
  3742.     },
  3743.  
  3744.     set: function(property, now){
  3745.         if (arguments.length == 1){
  3746.             now = property;
  3747.             property = this.property || this.options.property;
  3748.         }
  3749.         this.render(this.element, property, now, this.options.unit);
  3750.         return this;
  3751.     },
  3752.  
  3753.     start: function(property, from, to){
  3754.         if (!this.check(property, from, to)) return this;
  3755.         var args = Array.flatten(arguments);
  3756.         this.property = this.options.property || args.shift();
  3757.         var parsed = this.prepare(this.element, this.property, args);
  3758.         return this.parent(parsed.from, parsed.to);
  3759.     }
  3760.  
  3761. });
  3762.  
  3763. Element.Properties.tween = {
  3764.  
  3765.     set: function(options){
  3766.         var tween = this.retrieve('tween');
  3767.         if (tween) tween.cancel();
  3768.         return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options));
  3769.     },
  3770.  
  3771.     get: function(options){
  3772.         if (options || !this.retrieve('tween')){
  3773.             if (options || !this.retrieve('tween:options')) this.set('tween', options);
  3774.             this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
  3775.         }
  3776.         return this.retrieve('tween');
  3777.     }
  3778.  
  3779. };
  3780.  
  3781. Element.implement({
  3782.  
  3783.     tween: function(property, from, to){
  3784.         this.get('tween').start(arguments);
  3785.         return this;
  3786.     },
  3787.  
  3788.     fade: function(how){
  3789.         var fade = this.get('tween'), o = 'opacity', toggle;
  3790.         how = $pick(how, 'toggle');
  3791.         switch (how){
  3792.             case 'in': fade.start(o, 1); break;
  3793.             case 'out': fade.start(o, 0); break;
  3794.             case 'show': fade.set(o, 1); break;
  3795.             case 'hide': fade.set(o, 0); break;
  3796.             case 'toggle':
  3797.                 var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
  3798.                 fade.start(o, (flag) ? 0 : 1);
  3799.                 this.store('fade:flag', !flag);
  3800.                 toggle = true;
  3801.             break;
  3802.             default: fade.start(o, arguments);
  3803.         }
  3804.         if (!toggle) this.eliminate('fade:flag');
  3805.         return this;
  3806.     },
  3807.  
  3808.     highlight: function(start, end){
  3809.         if (!end){
  3810.             end = this.retrieve('highlight:original', this.getStyle('background-color'));
  3811.             end = (end == 'transparent') ? '#fff' : end;
  3812.         }
  3813.         var tween = this.get('tween');
  3814.         tween.start('background-color', start || '#ffff88', end).chain(function(){
  3815.             this.setStyle('background-color', this.retrieve('highlight:original'));
  3816.             tween.callChain();
  3817.         }.bind(this));
  3818.         return this;
  3819.     }
  3820.  
  3821. });
  3822.  
  3823. /*
  3824. ---
  3825.  
  3826. script: Fx.Transitions.js
  3827.  
  3828. description: Contains a set of advanced transitions to be used with any of the Fx Classes.
  3829.  
  3830. license: MIT-style license.
  3831.  
  3832. credits:
  3833. - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
  3834.  
  3835. requires:
  3836. - /Fx
  3837.  
  3838. provides: [Fx.Transitions]
  3839.  
  3840. ...
  3841. */
  3842.  
  3843. Fx.implement({
  3844.  
  3845.     getTransition: function(){
  3846.         var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
  3847.         if (typeof trans == 'string'){
  3848.             var data = trans.split(':');
  3849.             trans = Fx.Transitions;
  3850.             trans = trans[data[0]] || trans[data[0].capitalize()];
  3851.             if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
  3852.         }
  3853.         return trans;
  3854.     }
  3855.  
  3856. });
  3857.  
  3858. Fx.Transition = function(transition, params){
  3859.     params = $splat(params);
  3860.     return $extend(transition, {
  3861.         easeIn: function(pos){
  3862.             return transition(pos, params);
  3863.         },
  3864.         easeOut: function(pos){
  3865.             return 1 - transition(1 - pos, params);
  3866.         },
  3867.         easeInOut: function(pos){
  3868.             return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
  3869.         }
  3870.     });
  3871. };
  3872.  
  3873. Fx.Transitions = new Hash({
  3874.  
  3875.     linear: $arguments(0)
  3876.  
  3877. });
  3878.  
  3879. Fx.Transitions.extend = function(transitions){
  3880.     for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
  3881. };
  3882.  
  3883. Fx.Transitions.extend({
  3884.  
  3885.     Pow: function(p, x){
  3886.         return Math.pow(p, x[0] || 6);
  3887.     },
  3888.  
  3889.     Expo: function(p){
  3890.         return Math.pow(2, 8 * (p - 1));
  3891.     },
  3892.  
  3893.     Circ: function(p){
  3894.         return 1 - Math.sin(Math.acos(p));
  3895.     },
  3896.  
  3897.     Sine: function(p){
  3898.         return 1 - Math.sin((1 - p) * Math.PI / 2);
  3899.     },
  3900.  
  3901.     Back: function(p, x){
  3902.         x = x[0] || 1.618;
  3903.         return Math.pow(p, 2) * ((x + 1) * p - x);
  3904.     },
  3905.  
  3906.     Bounce: function(p){
  3907.         var value;
  3908.         for (var a = 0, b = 1; 1; a += b, b /= 2){
  3909.             if (p >= (7 - 4 * a) / 11){
  3910.                 value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
  3911.                 break;
  3912.             }
  3913.         }
  3914.         return value;
  3915.     },
  3916.  
  3917.     Elastic: function(p, x){
  3918.         return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
  3919.     }
  3920.  
  3921. });
  3922.  
  3923. ['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
  3924.     Fx.Transitions[transition] = new Fx.Transition(function(p){
  3925.         return Math.pow(p, [i + 2]);
  3926.     });
  3927. });
  3928.  
  3929. /*
  3930. ---
  3931.  
  3932. script: Fx.Morph.js
  3933.  
  3934. description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
  3935.  
  3936. license: MIT-style license.
  3937.  
  3938. requires:
  3939. - /Fx.CSS
  3940.  
  3941. provides: [Fx.Morph]
  3942.  
  3943. ...
  3944. */
  3945.  
  3946. Fx.Morph = new Class({
  3947.  
  3948.     Extends: Fx.CSS,
  3949.  
  3950.     initialize: function(element, options){
  3951.         this.element = this.subject = document.id(element);
  3952.         this.parent(options);
  3953.     },
  3954.  
  3955.     set: function(now){
  3956.         if (typeof now == 'string') now = this.search(now);
  3957.         for (var p in now) this.render(this.element, p, now[p], this.options.unit);
  3958.         return this;
  3959.     },
  3960.  
  3961.     compute: function(from, to, delta){
  3962.         var now = {};
  3963.         for (var p in from) now[p] = this.parent(from[p], to[p], delta);
  3964.         return now;
  3965.     },
  3966.  
  3967.     start: function(properties){
  3968.         if (!this.check(properties)) return this;
  3969.         if (typeof properties == 'string') properties = this.search(properties);
  3970.         var from = {}, to = {};
  3971.         for (var p in properties){
  3972.             var parsed = this.prepare(this.element, p, properties[p]);
  3973.             from[p] = parsed.from;
  3974.             to[p] = parsed.to;
  3975.         }
  3976.         return this.parent(from, to);
  3977.     }
  3978.  
  3979. });
  3980.  
  3981. Element.Properties.morph = {
  3982.  
  3983.     set: function(options){
  3984.         var morph = this.retrieve('morph');
  3985.         if (morph) morph.cancel();
  3986.         return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options));
  3987.     },
  3988.  
  3989.     get: function(options){
  3990.         if (options || !this.retrieve('morph')){
  3991.             if (options || !this.retrieve('morph:options')) this.set('morph', options);
  3992.             this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
  3993.         }
  3994.         return this.retrieve('morph');
  3995.     }
  3996.  
  3997. };
  3998.  
  3999. Element.implement({
  4000.  
  4001.     morph: function(props){
  4002.         this.get('morph').start(props);
  4003.         return this;
  4004.     }
  4005.  
  4006. });
  4007.  
  4008. /*
  4009. ---
  4010.  
  4011. script: DomReady.js
  4012.  
  4013. description: Contains the custom event domready.
  4014.  
  4015. license: MIT-style license.
  4016.  
  4017. requires:
  4018. - /Element.Event
  4019.  
  4020. provides: [DomReady]
  4021.  
  4022. ...
  4023. */
  4024.  
  4025. Element.Events.domready = {
  4026.  
  4027.     onAdd: function(fn){
  4028.         if (Browser.loaded) fn.call(this);
  4029.     }
  4030.  
  4031. };
  4032.  
  4033. (function(){
  4034.  
  4035.     var domready = function(){
  4036.         if (Browser.loaded) return;
  4037.         Browser.loaded = true;
  4038.         window.fireEvent('domready');
  4039.         document.fireEvent('domready');
  4040.     };
  4041.    
  4042.     window.addEvent('load', domready);
  4043.  
  4044.     if (Browser.Engine.trident){
  4045.         var temp = document.createElement('div');
  4046.         (function(){
  4047.             ($try(function(){
  4048.                 temp.doScroll(); // Technique by Diego Perini
  4049.                 return document.id(temp).inject(document.body).set('html', 'temp').dispose();
  4050.             })) ? domready() : arguments.callee.delay(50);
  4051.         })();
  4052.     } else if (Browser.Engine.webkit && Browser.Engine.version < 525){
  4053.         (function(){
  4054.             (['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
  4055.         })();
  4056.     } else {
  4057.         document.addEvent('DOMContentLoaded', domready);
  4058.     }
  4059.  
  4060. })();
  4061.  
  4062. /*
  4063. ---
  4064.  
  4065. script: Cookie.js
  4066.  
  4067. description: Class for creating, reading, and deleting browser Cookies.
  4068.  
  4069. license: MIT-style license.
  4070.  
  4071. credits:
  4072. - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
  4073.  
  4074. requires:
  4075. - /Options
  4076.  
  4077. provides: [Cookie]
  4078.  
  4079. ...
  4080. */
  4081.  
  4082. var Cookie = new Class({
  4083.  
  4084.     Implements: Options,
  4085.  
  4086.     options: {
  4087.         path: false,
  4088.         domain: false,
  4089.         duration: false,
  4090.         secure: false,
  4091.         document: document
  4092.     },
  4093.  
  4094.     initialize: function(key, options){
  4095.         this.key = key;
  4096.         this.setOptions(options);
  4097.     },
  4098.  
  4099.     write: function(value){
  4100.         value = encodeURIComponent(value);
  4101.         if (this.options.domain) value += '; domain=' + this.options.domain;
  4102.         if (this.options.path) value += '; path=' + this.options.path;
  4103.         if (this.options.duration){
  4104.             var date = new Date();
  4105.             date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
  4106.             value += '; expires=' + date.toGMTString();
  4107.         }
  4108.         if (this.options.secure) value += '; secure';
  4109.         this.options.document.cookie = this.key + '=' + value;
  4110.         return this;
  4111.     },
  4112.  
  4113.     read: function(){
  4114.         var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
  4115.         return (value) ? decodeURIComponent(value[1]) : null;
  4116.     },
  4117.  
  4118.     dispose: function(){
  4119.         new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
  4120.         return this;
  4121.     }
  4122.  
  4123. });
  4124.  
  4125. Cookie.write = function(key, value, options){
  4126.     return new Cookie(key, options).write(value);
  4127. };
  4128.  
  4129. Cookie.read = function(key){
  4130.     return new Cookie(key).read();
  4131. };
  4132.  
  4133. Cookie.dispose = function(key, options){
  4134.     return new Cookie(key, options).dispose();
  4135. };
  4136.  
  4137. /*
  4138. ---
  4139.  
  4140. script: Swiff.js
  4141.  
  4142. description: Wrapper for embedding SWF movies. Supports External Interface Communication.
  4143.  
  4144. license: MIT-style license.
  4145.  
  4146. credits:
  4147. - Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
  4148.  
  4149. requires:
  4150. - /Options
  4151. - /$util
  4152.  
  4153. provides: [Swiff]
  4154.  
  4155. ...
  4156. */
  4157.  
  4158. var Swiff = new Class({
  4159.  
  4160.     Implements: [Options],
  4161.  
  4162.     options: {
  4163.         id: null,
  4164.         height: 1,
  4165.         width: 1,
  4166.         container: null,
  4167.         properties: {},
  4168.         params: {
  4169.             quality: 'high',
  4170.             allowScriptAccess: 'always',
  4171.             wMode: 'transparent',
  4172.             swLiveConnect: true
  4173.         },
  4174.         callBacks: {},
  4175.         vars: {}
  4176.     },
  4177.  
  4178.     toElement: function(){
  4179.         return this.object;
  4180.     },
  4181.  
  4182.     initialize: function(path, options){
  4183.         this.instance = 'Swiff_' + $time();
  4184.  
  4185.         this.setOptions(options);
  4186.         options = this.options;
  4187.         var id = this.id = options.id || this.instance;
  4188.         var container = document.id(options.container);
  4189.  
  4190.         Swiff.CallBacks[this.instance] = {};
  4191.  
  4192.         var params = options.params, vars = options.vars, callBacks = options.callBacks;
  4193.         var properties = $extend({height: options.height, width: options.width}, options.properties);
  4194.  
  4195.         var self = this;
  4196.  
  4197.         for (var callBack in callBacks){
  4198.             Swiff.CallBacks[this.instance][callBack] = (function(option){
  4199.                 return function(){
  4200.                     return option.apply(self.object, arguments);
  4201.                 };
  4202.             })(callBacks[callBack]);
  4203.             vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
  4204.         }
  4205.  
  4206.         params.flashVars = Hash.toQueryString(vars);
  4207.         if (Browser.Engine.trident){
  4208.             properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
  4209.             params.movie = path;
  4210.         } else {
  4211.             properties.type = 'application/x-shockwave-flash';
  4212.             properties.data = path;
  4213.         }
  4214.         var build = '<object id="' + id + '"';
  4215.         for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
  4216.         build += '>';
  4217.         for (var param in params){
  4218.             if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
  4219.         }
  4220.         build += '</object>';
  4221.         this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
  4222.     },
  4223.  
  4224.     replaces: function(element){
  4225.         element = document.id(element, true);
  4226.         element.parentNode.replaceChild(this.toElement(), element);
  4227.         return this;
  4228.     },
  4229.  
  4230.     inject: function(element){
  4231.         document.id(element, true).appendChild(this.toElement());
  4232.         return this;
  4233.     },
  4234.  
  4235.     remote: function(){
  4236.         return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
  4237.     }
  4238.  
  4239. });
  4240.  
  4241. Swiff.CallBacks = {};
  4242.  
  4243. Swiff.remote = function(obj, fn){
  4244.     var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
  4245.     return eval(rs);
  4246. };
  4247.  
  4248. // end Greased MooTools
  4249. //
  4250. // hack to circumvent 'bug' when overriding toString (and others):
  4251. // https://mootools.lighthouseapp.com/projects/2706/tickets/651-classtostring-broken-on-122-big-regression
  4252. ['toString', 'toLocaleString', 'valueOf', 'toSource', 'watch', 'unwatch', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable'].each(function (method) {
  4253.     Class.Mutators[method] = $arguments(0);
  4254. });
  4255.  
  4256.         var getJSVariable = function(regex) {
  4257.                 // Thanks to Vispillo for this compact code
  4258.                 var retval;
  4259.                 $$('script').each(function(script) {
  4260.                         if (retval != undefined) {
  4261.                                 return;
  4262.                         }
  4263.                         var html = script.innerHTML;
  4264.                         try {  
  4265.                                 retval = html.match(regex)[1];
  4266.                         } catch(e) {
  4267.                         }
  4268.                 });
  4269.                 return retval;
  4270.         }
  4271.  
  4272.  
  4273.     GM_getMagisterLudi = function () {
  4274.     var retval = getJSVariable(/\"api_key\"[ :]+[\'\"]([^\'\"]+)[\'\"]/);
  4275.     if (!$chk(retval)) retval = getJSVariable(/global_magisterLudi[ =]+\'([^\']+)\'/);
  4276.     return retval;
  4277.     }
  4278.  
  4279.     GM_getAuthHash = function () {
  4280.     var retval = getJSVariable(/\"auth_hash\"[ :]+[\'\"]([^\'\"]+)[\'\"]/);
  4281.     if (!$chk(retval)) retval = getJSVariable(/global_auth_hash[ =]+\'([^\']+)\'/);
  4282.     return retval;
  4283.     }
  4284.  
  4285.     GM_getAuthToken = function () {
  4286.     var retval = getJSVariable(/\auth_token\"[ :]+[\'\"]([^\'\"]+)[\'\"]/);
  4287.     if (!$chk(retval)) retval = getJSVariable(/global_auth_token[ =]+\'([^\']+)\'/);
  4288.     return retval;
  4289.     }
  4290.  
  4291. if (typeof(GM_log) == "undefined") {
  4292.  
  4293.     var keyPrefix = 'FlickrQuitMultipleGroups.';
  4294.  
  4295.     GM_log = function (message) {
  4296.         if (Browser.Engine.webkit) {
  4297.             console.info("[FQMG] " + message);
  4298.         } else {
  4299.             opera.postError(message);
  4300.         }
  4301.     }
  4302.  
  4303.     GM_getValue = function(key, defValue) {
  4304.         var retval = window.localStorage.getItem(keyPrefix + key);
  4305.         if (!$chk(retval)) {
  4306.             return defValue;
  4307.         }
  4308.         return retval;
  4309.     }
  4310.  
  4311.     GM_setValue = function(key, value) {
  4312.         try {
  4313.             window.localStorage.setItem(keyPrefix + key, value);
  4314.         } catch (e) {
  4315.             GM_log("error setting value: " + e);
  4316.         }
  4317.     }
  4318.  
  4319.     GM_deleteValue = function(key) {
  4320.         try {
  4321.             window.localStorage.removeItem(keyPrefix + key);
  4322.         } catch (e) {
  4323.             GM_log("error removing value: " + e);
  4324.         }
  4325.     }
  4326.  
  4327. }
  4328.  
  4329.  
  4330.  
  4331.  
  4332. // the real code
  4333. var injectionPoint = $('groups-list');
  4334. var injectionDirection = 'top';
  4335. var nothingSelectedTitle = "first select the groups you want to quit";
  4336. var somethingSelectedTitle = "click to quit the selected groups";
  4337.  
  4338. new Element('button', {
  4339.     'class': 'CancelButt',
  4340.     id: 'FQMG-quit-button',
  4341.     html: 'Quit selected groups',
  4342.     title: nothingSelectedTitle,
  4343.     styles: {
  4344.         display: 'none',
  4345.         visibility: 'hidden'
  4346.     },
  4347.     events: {
  4348.         'update-state': function (evt) {
  4349.             var button = $('FQMG-quit-button');
  4350.             if ($$('input[id^=FQMG-checkbox]:checked').length > 0) {
  4351.                 button.addClass('DeleteButt').removeClass('CancelButt');
  4352.                 button.set('title', somethingSelectedTitle);
  4353.             } else {
  4354.                 button.addClass('CancelButt').removeClass('DeleteButt');
  4355.                 button.set('title', nothingSelectedTitle);
  4356.             }
  4357.         },
  4358.         'click': function (evt) {
  4359.             if (this.hasClass('CancelButt')) return;
  4360.             // then, do the quiting
  4361.             $$('input[id^=FQMG-checkbox]:checked').each(function (checkbox) {
  4362.                 var groupId = checkbox.get('id').replace(/^FQMG-checkbox-/, '');
  4363.                 new Element('img', {
  4364.                     src: images.updating,
  4365.                     id: 'FQMG-spinner-' + groupId,
  4366.                     title: 'quiting group',
  4367.                     height: '9px;'
  4368.                 }).replaces(checkbox);
  4369.                
  4370.                 var deletePhotos = $('FQMG-delete-checkbox-' + groupId).checked;
  4371.                 $('FQMG-delete-checkbox-' + groupId).dispose();
  4372.                 try {
  4373.                     $('FQMG-delete-checkbox-label-' + groupId).dispose();
  4374.                 } catch (e) {
  4375.                     // ignore
  4376.                 }
  4377. // DECEMBRE HTTPS
  4378.                 new Request({
  4379.                         url: 'https://www.flickr.com',
  4380.                         onSuccess: function (responseText, responseXML) {
  4381.                             try {
  4382.                                 var result;
  4383.                                 try {
  4384.                                     result = JSON.parse(responseText);
  4385.                                 } catch (e) {
  4386.                                     result = eval('(' + responseText + ')');
  4387.                                 }
  4388.                         if (result.stat === 'ok') {
  4389.                             try {
  4390.  
  4391. $('FQMG-spinner-' + groupId).getParent('div.group-badge').setStyle('opacity', '0.4');
  4392.                             } catch (e) {
  4393.                                 // ignore
  4394.                             }
  4395.                         }
  4396.                         new Element('img', {
  4397.                             src: result.stat === 'fail' ? images.error : images.success,
  4398.                             title: result.stat === 'fail' ? "failed: " + result.message : 'done',
  4399.                             height: '9px'
  4400.                         }).replaces($('FQMG-spinner-' + groupId));
  4401.                         } catch (e) {
  4402.                             GM_log("ERROR processing result: " + e);
  4403.                         new Element('img', {
  4404.                             src: images.error,
  4405.                             title: "ERROR processing result: " + e,
  4406.                             height: '9px'
  4407.                         }).replaces($('FQMG-spinner-' + groupId));
  4408.                         }
  4409.                     },
  4410.                     onFailure: function (response) {
  4411.                         GM_log("error: " + response.statusText);
  4412.                         new Element('img', {
  4413.                             src: images.error,
  4414.                             title: "error: " + response.statusText,
  4415.                             height: '9px'
  4416.                         }).replaces($('FQMG-spinner-' + groupId));
  4417.                     }
  4418.                     }).get('/services/rest', {
  4419.                         api_key: GM_getMagisterLudi(),
  4420.                         auth_hash: GM_getAuthHash(),
  4421.                         auth_token: GM_getAuthToken(),
  4422.                         format: 'json',
  4423.                         method: 'flickr.groups.leave',
  4424.                         nojsoncallback: 1,
  4425.                         group_id: groupId,
  4426.                     delete_photos: deletePhotos
  4427.                 });
  4428.             });
  4429.         }
  4430.     }
  4431. }).inject(injectionPoint, injectionDirection);
  4432.  
  4433. function createCheckbox(data) {
  4434.     var groupId = data.groupId;
  4435.     var parentElement = data.element;
  4436.  
  4437.     var checkbox = new Element('input', {
  4438.         type: 'checkbox',
  4439.         'class': 'FQMG-checkbox',
  4440.         id: 'FQMG-checkbox-' + groupId,
  4441.         title: 'leave this group?',
  4442.         styles: {
  4443.             'margin-left': '10px'
  4444.         },
  4445.         events: {
  4446.             change: function (evt) {
  4447.                 var checked = this.checked;
  4448.                 $('FQMG-delete-checkbox-' + groupId).setStyles({ display: checked ? 'inline' : 'hidden', visibility: checked ? 'visible' : 'hidden' });
  4449.                 $('FQMG-delete-checkbox-label-' + groupId).setStyles({ display: checked ? 'inline' : 'hidden', visibility: checked ? 'visible' : 'hidden' });
  4450.                 $('FQMG-quit-button').fireEvent('update-state');
  4451.             }
  4452.         }
  4453.     }).inject(parentElement);
  4454.     var leaveLabel = new Element('label', {
  4455.         html: 'leave this group',
  4456.         'for': 'FQMG-checkbox-' + groupId,
  4457.         id: 'FQMG-checkbox-label-' + groupId,
  4458.         title: 'leave this group?',
  4459.         styles: {
  4460.             'margin-left': '5px'
  4461.         }
  4462.     }).inject(checkbox, 'after');
  4463.     // remove photos
  4464.     var removeCheckbox = new Element('input', {
  4465.         type: 'checkbox',
  4466.         id: 'FQMG-delete-checkbox-' + groupId,
  4467.         title: 'delete all photos from group pool?',
  4468.         styles: {
  4469.             display: 'none',
  4470.             visibility: 'hidden',
  4471.             'margin-left': '10px'
  4472.         }
  4473.     }).inject(leaveLabel, 'after');
  4474.     new Element('label', {
  4475.         'for': 'FQMG-delete-checkbox-' + groupId,
  4476.         html: 'remove photos from pool',
  4477.         id: 'FQMG-delete-checkbox-label-' + groupId,
  4478.         title: 'delete all photos from group pool?',
  4479.         styles: {
  4480.             display: 'none',
  4481.             visibility: 'hidden',
  4482.             'margin-left': '5px'
  4483.         }
  4484.     }).inject(removeCheckbox, 'after');
  4485.     return checkbox;
  4486. }
  4487.  
  4488.  
  4489. $$('li.group-overview').each(function(groupBadge) {
  4490.     try {
  4491.  
  4492. // DECEMBRE TWEAK OK
  4493. // var groupId = groupBadge.getElement('img.buddy-icon').get('src').match(/flickr.com\/\d+\/buddyicons\/(\d+@\w\d{2})/)[1];
  4494.         var groupId = groupBadge.getElement('img.buddy-icon').get('src').match(/(\d+@\w\d{2})/)[1];
  4495.  
  4496.     } catch (e) {
  4497.         // no buddy icon?
  4498.         try {
  4499.         var groupId = groupBadge.getElement('img.buddy-icon').get('src').match(/images\/buddyicon.*(?:.gif|png|jpg)#(\d+@\w\d{2})/)[1];
  4500.         } catch (f) {
  4501.             GM_log("unable to extract group id: " + e + " - ignoring");
  4502.             return;
  4503.         }
  4504.     }
  4505.     var groupMeta = groupBadge.getElement('span span').getParent('span');
  4506.     new Element('span', {
  4507.         'class': 'divider',
  4508.         html: '|'
  4509.     }).inject(groupMeta);
  4510.     var span = new Element('span', {
  4511.         'class': 'first group-count pool-pending',
  4512.         nowrap: 'nowrap',
  4513.         styles: {
  4514.             'white-space': 'nowrap'
  4515.         }
  4516.     }).inject(groupMeta);
  4517.     var checkbox = createCheckbox({ groupId: groupId, element: span });
  4518.     // make the quit button available
  4519.     $('FQMG-quit-button').setStyles({ display: 'inline', visibility: 'visible' });
  4520.    
  4521. });
  4522.  
  4523.  
  4524.  
  4525. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement