initial commit
[namibia] / public / js / vendor / ape-source / mootools-core.js
1 /*
2 ---
3
4 name: Core
5
6 description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts.
7
8 license: MIT-style license.
9
10 copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
11
12 authors: The MooTools production team (http://mootools.net/developers/)
13
14 inspiration:
15   - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
16   - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
17
18 provides: [MooTools, Native, Hash.base, Array.each, $util]
19
20 ...
21 */
22
23 var MooTools = {
24         'version': '1.2.5',
25         'build': '008d8f0f2fcc2044e54fdd3635341aaab274e757'
26 };
27
28 var Native = function(options){
29         options = options || {};
30         var name = options.name;
31         var legacy = options.legacy;
32         var protect = options.protect;
33         var methods = options.implement;
34         var generics = options.generics;
35         var initialize = options.initialize;
36         var afterImplement = options.afterImplement || function(){};
37         var object = initialize || legacy;
38         generics = generics !== false;
39
40         object.constructor = Native;
41         object.$family = {name: 'native'};
42         if (legacy && initialize) object.prototype = legacy.prototype;
43         object.prototype.constructor = object;
44
45         if (name){
46                 var family = name.toLowerCase();
47                 object.prototype.$family = {name: family};
48                 Native.typize(object, family);
49         }
50
51         var add = function(obj, name, method, force){
52                 if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
53                 if (generics) Native.genericize(obj, name, protect);
54                 afterImplement.call(obj, name, method);
55                 return obj;
56         };
57
58         object.alias = function(a1, a2, a3){
59                 if (typeof a1 == 'string'){
60                         var pa1 = this.prototype[a1];
61                         if ((a1 = pa1)) return add(this, a2, a1, a3);
62                 }
63                 for (var a in a1) this.alias(a, a1[a], a2);
64                 return this;
65         };
66
67         object.implement = function(a1, a2, a3){
68                 if (typeof a1 == 'string') return add(this, a1, a2, a3);
69                 for (var p in a1) add(this, p, a1[p], a2);
70                 return this;
71         };
72
73         if (methods) object.implement(methods);
74
75         return object;
76 };
77
78 Native.genericize = function(object, property, check){
79         if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
80                 var args = Array.prototype.slice.call(arguments);
81                 return object.prototype[property].apply(args.shift(), args);
82         };
83 };
84
85 Native.implement = function(objects, properties){
86         for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
87 };
88
89 Native.typize = function(object, family){
90         if (!object.type) object.type = function(item){
91                 return ($type(item) === family);
92         };
93 };
94
95 (function(){
96         var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
97         for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});
98
99         var types = {'boolean': Boolean, 'native': Native, 'object': Object};
100         for (var t in types) Native.typize(types[t], t);
101
102         var generics = {
103                 'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
104                 'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
105         };
106         for (var g in generics){
107                 for (var i = generics[g].length; i--;) Native.genericize(natives[g], generics[g][i], true);
108         }
109 })();
110
111 var Hash = new Native({
112
113         name: 'Hash',
114
115         initialize: function(object){
116                 if ($type(object) == 'hash') object = $unlink(object.getClean());
117                 for (var key in object) this[key] = object[key];
118                 return this;
119         }
120
121 });
122
123 Hash.implement({
124
125         forEach: function(fn, bind){
126                 for (var key in this){
127                         if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
128                 }
129         },
130
131         getClean: function(){
132                 var clean = {};
133                 for (var key in this){
134                         if (this.hasOwnProperty(key)) clean[key] = this[key];
135                 }
136                 return clean;
137         },
138
139         getLength: function(){
140                 var length = 0;
141                 for (var key in this){
142                         if (this.hasOwnProperty(key)) length++;
143                 }
144                 return length;
145         }
146
147 });
148
149 Hash.alias('forEach', 'each');
150
151 Array.implement({
152
153         forEach: function(fn, bind){
154                 for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
155         }
156
157 });
158
159 Array.alias('forEach', 'each');
160
161 function $A(iterable){
162         if (iterable.item){
163                 var l = iterable.length, array = new Array(l);
164                 while (l--) array[l] = iterable[l];
165                 return array;
166         }
167         return Array.prototype.slice.call(iterable);
168 };
169
170 function $arguments(i){
171         return function(){
172                 return arguments[i];
173         };
174 };
175
176 function $chk(obj){
177         return !!(obj || obj === 0);
178 };
179
180 function $clear(timer){
181         clearTimeout(timer);
182         clearInterval(timer);
183         return null;
184 };
185
186 function $defined(obj){
187         return (obj != undefined);
188 };
189
190 function $each(iterable, fn, bind){
191         var type = $type(iterable);
192         ((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
193 };
194
195 function $empty(){};
196
197 function $extend(original, extended){
198         for (var key in (extended || {})) original[key] = extended[key];
199         return original;
200 };
201
202 function $H(object){
203         return new Hash(object);
204 };
205
206 function $lambda(value){
207         return ($type(value) == 'function') ? value : function(){
208                 return value;
209         };
210 };
211
212 function $merge(){
213         var args = Array.slice(arguments);
214         args.unshift({});
215         return $mixin.apply(null, args);
216 };
217
218 function $mixin(mix){
219         for (var i = 1, l = arguments.length; i < l; i++){
220                 var object = arguments[i];
221                 if ($type(object) != 'object') continue;
222                 for (var key in object){
223                         var op = object[key], mp = mix[key];
224                         mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op);
225                 }
226         }
227         return mix;
228 };
229
230 function $pick(){
231         for (var i = 0, l = arguments.length; i < l; i++){
232                 if (arguments[i] != undefined) return arguments[i];
233         }
234         return null;
235 };
236
237 function $random(min, max){
238         return Math.floor(Math.random() * (max - min + 1) + min);
239 };
240
241 function $splat(obj){
242         var type = $type(obj);
243         return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
244 };
245
246 var $time = Date.now || function(){
247         return +new Date;
248 };
249
250 function $try(){
251         for (var i = 0, l = arguments.length; i < l; i++){
252                 try {
253                         return arguments[i]();
254                 } catch(e){}
255         }
256         return null;
257 };
258
259 function $type(obj){
260         if (obj == undefined) return false;
261         if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
262         if (obj.nodeName){
263                 switch (obj.nodeType){
264                         case 1: return 'element';
265                         case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
266                 }
267         } else if (typeof obj.length == 'number'){
268                 if (obj.callee) return 'arguments';
269                 else if (obj.item) return 'collection';
270         }
271         return typeof obj;
272 };
273
274 function $unlink(object){
275         var unlinked;
276         switch ($type(object)){
277                 case 'object':
278                         unlinked = {};
279                         for (var p in object) unlinked[p] = $unlink(object[p]);
280                 break;
281                 case 'hash':
282                         unlinked = new Hash(object);
283                 break;
284                 case 'array':
285                         unlinked = [];
286                         for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
287                 break;
288                 default: return object;
289         }
290         return unlinked;
291 };
292
293
294 /*
295 ---
296
297 name: Browser
298
299 description: The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
300
301 license: MIT-style license.
302
303 requires: [Native, $util]
304
305 provides: [Browser, Window, Document, $exec]
306
307 ...
308 */
309
310 var Browser = $merge({
311
312         Engine: {name: 'unknown', version: 0},
313
314         Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
315
316         Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
317
318         Plugins: {},
319
320         Engines: {
321
322                 presto: function(){
323                         return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
324                 },
325
326                 trident: function(){
327                         return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
328                 },
329
330                 webkit: function(){
331                         return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
332                 },
333
334                 gecko: function(){
335                         return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
336                 }
337
338         }
339
340 }, Browser || {});
341
342 Browser.Platform[Browser.Platform.name] = true;
343
344 Browser.detect = function(){
345
346         for (var engine in this.Engines){
347                 var version = this.Engines[engine]();
348                 if (version){
349                         this.Engine = {name: engine, version: version};
350                         this.Engine[engine] = this.Engine[engine + version] = true;
351                         break;
352                 }
353         }
354
355         return {name: engine, version: version};
356
357 };
358
359 Browser.detect();
360
361 Browser.Request = function(){
362         return $try(function(){
363                 return new XMLHttpRequest();
364         }, function(){
365                 return new ActiveXObject('MSXML2.XMLHTTP');
366         }, function(){
367                 return new ActiveXObject('Microsoft.XMLHTTP');
368         });
369 };
370
371 Browser.Features.xhr = !!(Browser.Request());
372
373 Browser.Plugins.Flash = (function(){
374         var version = ($try(function(){
375                 return navigator.plugins['Shockwave Flash'].description;
376         }, function(){
377                 return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
378         }) || '0 r0').match(/\d+/g);
379         return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
380 })();
381
382 function $exec(text){
383         if (!text) return text;
384         if (window.execScript){
385                 window.execScript(text);
386         } else {
387                 var script = document.createElement('script');
388                 script.setAttribute('type', 'text/javascript');
389                 script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
390                 document.head.appendChild(script);
391                 document.head.removeChild(script);
392         }
393         return text;
394 };
395
396 Native.UID = 1;
397
398 var $uid = (Browser.Engine.trident) ? function(item){
399         return (item.uid || (item.uid = [Native.UID++]))[0];
400 } : function(item){
401         return item.uid || (item.uid = Native.UID++);
402 };
403
404 var Window = new Native({
405
406         name: 'Window',
407
408         legacy: (Browser.Engine.trident) ? null: window.Window,
409
410         initialize: function(win){
411                 $uid(win);
412                 if (!win.Element){
413                         win.Element = $empty;
414                         if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
415                         win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
416                 }
417                 win.document.window = win;
418                 return $extend(win, Window.Prototype);
419         },
420
421         afterImplement: function(property, value){
422                 window[property] = Window.Prototype[property] = value;
423         }
424
425 });
426
427 Window.Prototype = {$family: {name: 'window'}};
428
429 new Window(window);
430
431 var Document = new Native({
432
433         name: 'Document',
434
435         legacy: (Browser.Engine.trident) ? null: window.Document,
436
437         initialize: function(doc){
438                 $uid(doc);
439                 doc.head = doc.getElementsByTagName('head')[0];
440                 doc.html = doc.getElementsByTagName('html')[0];
441                 if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
442                         doc.execCommand("BackgroundImageCache", false, true);
443                 });
444                 if (Browser.Engine.trident) doc.window.attachEvent('onunload', function(){
445                         doc.window.detachEvent('onunload', arguments.callee);
446                         doc.head = doc.html = doc.window = null;
447                 });
448                 return $extend(doc, Document.Prototype);
449         },
450
451         afterImplement: function(property, value){
452                 document[property] = Document.Prototype[property] = value;
453         }
454
455 });
456
457 Document.Prototype = {$family: {name: 'document'}};
458
459 new Document(document);
460
461
462 /*
463 ---
464
465 name: Array
466
467 description: Contains Array Prototypes like each, contains, and erase.
468
469 license: MIT-style license.
470
471 requires: [$util, Array.each]
472
473 provides: Array
474
475 ...
476 */
477
478 Array.implement({
479
480         every: function(fn, bind){
481                 for (var i = 0, l = this.length; i < l; i++){
482                         if (!fn.call(bind, this[i], i, this)) return false;
483                 }
484                 return true;
485         },
486
487         filter: function(fn, bind){
488                 var results = [];
489                 for (var i = 0, l = this.length; i < l; i++){
490                         if (fn.call(bind, this[i], i, this)) results.push(this[i]);
491                 }
492                 return results;
493         },
494
495         clean: function(){
496                 return this.filter($defined);
497         },
498
499         indexOf: function(item, from){
500                 var len = this.length;
501                 for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
502                         if (this[i] === item) return i;
503                 }
504                 return -1;
505         },
506
507         map: function(fn, bind){
508                 var results = [];
509                 for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
510                 return results;
511         },
512
513         some: function(fn, bind){
514                 for (var i = 0, l = this.length; i < l; i++){
515                         if (fn.call(bind, this[i], i, this)) return true;
516                 }
517                 return false;
518         },
519
520         associate: function(keys){
521                 var obj = {}, length = Math.min(this.length, keys.length);
522                 for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
523                 return obj;
524         },
525
526         link: function(object){
527                 var result = {};
528                 for (var i = 0, l = this.length; i < l; i++){
529                         for (var key in object){
530                                 if (object[key](this[i])){
531                                         result[key] = this[i];
532                                         delete object[key];
533                                         break;
534                                 }
535                         }
536                 }
537                 return result;
538         },
539
540         contains: function(item, from){
541                 return this.indexOf(item, from) != -1;
542         },
543
544         extend: function(array){
545                 for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
546                 return this;
547         },
548         
549         getLast: function(){
550                 return (this.length) ? this[this.length - 1] : null;
551         },
552
553         getRandom: function(){
554                 return (this.length) ? this[$random(0, this.length - 1)] : null;
555         },
556
557         include: function(item){
558                 if (!this.contains(item)) this.push(item);
559                 return this;
560         },
561
562         combine: function(array){
563                 for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
564                 return this;
565         },
566
567         erase: function(item){
568                 for (var i = this.length; i--; i){
569                         if (this[i] === item) this.splice(i, 1);
570                 }
571                 return this;
572         },
573
574         empty: function(){
575                 this.length = 0;
576                 return this;
577         },
578
579         flatten: function(){
580                 var array = [];
581                 for (var i = 0, l = this.length; i < l; i++){
582                         var type = $type(this[i]);
583                         if (!type) continue;
584                         array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
585                 }
586                 return array;
587         },
588
589         hexToRgb: function(array){
590                 if (this.length != 3) return null;
591                 var rgb = this.map(function(value){
592                         if (value.length == 1) value += value;
593                         return value.toInt(16);
594                 });
595                 return (array) ? rgb : 'rgb(' + rgb + ')';
596         },
597
598         rgbToHex: function(array){
599                 if (this.length < 3) return null;
600                 if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
601                 var hex = [];
602                 for (var i = 0; i < 3; i++){
603                         var bit = (this[i] - 0).toString(16);
604                         hex.push((bit.length == 1) ? '0' + bit : bit);
605                 }
606                 return (array) ? hex : '#' + hex.join('');
607         }
608
609 });
610
611
612 /*
613 ---
614
615 name: Function
616
617 description: Contains Function Prototypes like create, bind, pass, and delay.
618
619 license: MIT-style license.
620
621 requires: [Native, $util]
622
623 provides: Function
624
625 ...
626 */
627
628 try {
629         delete Function.prototype.bind;
630 } catch(e){}
631
632 Function.implement({
633
634         extend: function(properties){
635                 for (var property in properties) this[property] = properties[property];
636                 return this;
637         },
638
639         create: function(options){
640                 var self = this;
641                 options = options || {};
642                 return function(event){
643                         var args = options.arguments;
644                         args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
645                         if (options.event) args = [event || window.event].extend(args);
646                         var returns = function(){
647                                 return self.apply(options.bind || null, args);
648                         };
649                         if (options.delay) return setTimeout(returns, options.delay);
650                         if (options.periodical) return setInterval(returns, options.periodical);
651                         if (options.attempt) return $try(returns);
652                         return returns();
653                 };
654         },
655
656         run: function(args, bind){
657                 return this.apply(bind, $splat(args));
658         },
659
660         pass: function(args, bind){
661                 return this.create({bind: bind, arguments: args});
662         },
663
664         bind: function(bind, args){
665                 return this.create({bind: bind, arguments: args});
666         },
667
668         bindWithEvent: function(bind, args){
669                 return this.create({bind: bind, arguments: args, event: true});
670         },
671
672         attempt: function(args, bind){
673                 return this.create({bind: bind, arguments: args, attempt: true})();
674         },
675
676         delay: function(delay, bind, args){
677                 return this.create({bind: bind, arguments: args, delay: delay})();
678         },
679
680         periodical: function(periodical, bind, args){
681                 return this.create({bind: bind, arguments: args, periodical: periodical})();
682         }
683
684 });
685
686
687 /*
688 ---
689
690 name: Number
691
692 description: Contains Number Prototypes like limit, round, times, and ceil.
693
694 license: MIT-style license.
695
696 requires: [Native, $util]
697
698 provides: Number
699
700 ...
701 */
702
703 Number.implement({
704
705         limit: function(min, max){
706                 return Math.min(max, Math.max(min, this));
707         },
708
709         round: function(precision){
710                 precision = Math.pow(10, precision || 0);
711                 return Math.round(this * precision) / precision;
712         },
713
714         times: function(fn, bind){
715                 for (var i = 0; i < this; i++) fn.call(bind, i, this);
716         },
717
718         toFloat: function(){
719                 return parseFloat(this);
720         },
721
722         toInt: function(base){
723                 return parseInt(this, base || 10);
724         }
725
726 });
727
728 Number.alias('times', 'each');
729
730 (function(math){
731         var methods = {};
732         math.each(function(name){
733                 if (!Number[name]) methods[name] = function(){
734                         return Math[name].apply(null, [this].concat($A(arguments)));
735                 };
736         });
737         Number.implement(methods);
738 })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
739
740
741 /*
742 ---
743
744 name: String
745
746 description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
747
748 license: MIT-style license.
749
750 requires: Native
751
752 provides: String
753
754 ...
755 */
756
757 String.implement({
758
759         test: function(regex, params){
760                 return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
761         },
762
763         contains: function(string, separator){
764                 return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
765         },
766
767         trim: function(){
768                 return this.replace(/^\s+|\s+$/g, '');
769         },
770
771         clean: function(){
772                 return this.replace(/\s+/g, ' ').trim();
773         },
774
775         camelCase: function(){
776                 return this.replace(/-\D/g, function(match){
777                         return match.charAt(1).toUpperCase();
778                 });
779         },
780
781         hyphenate: function(){
782                 return this.replace(/[A-Z]/g, function(match){
783                         return ('-' + match.charAt(0).toLowerCase());
784                 });
785         },
786
787         capitalize: function(){
788                 return this.replace(/\b[a-z]/g, function(match){
789                         return match.toUpperCase();
790                 });
791         },
792
793         escapeRegExp: function(){
794                 return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
795         },
796
797         toInt: function(base){
798                 return parseInt(this, base || 10);
799         },
800
801         toFloat: function(){
802                 return parseFloat(this);
803         },
804
805         hexToRgb: function(array){
806                 var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
807                 return (hex) ? hex.slice(1).hexToRgb(array) : null;
808         },
809
810         rgbToHex: function(array){
811                 var rgb = this.match(/\d{1,3}/g);
812                 return (rgb) ? rgb.rgbToHex(array) : null;
813         },
814
815         stripScripts: function(option){
816                 var scripts = '';
817                 var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
818                         scripts += arguments[1] + '\n';
819                         return '';
820                 });
821                 if (option === true) $exec(scripts);
822                 else if ($type(option) == 'function') option(scripts, text);
823                 return text;
824         },
825
826         substitute: function(object, regexp){
827                 return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
828                         if (match.charAt(0) == '\\') return match.slice(1);
829                         return (object[name] != undefined) ? object[name] : '';
830                 });
831         }
832
833 });
834
835
836 /*
837 ---
838
839 name: Hash
840
841 description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
842
843 license: MIT-style license.
844
845 requires: Hash.base
846
847 provides: Hash
848
849 ...
850 */
851
852 Hash.implement({
853
854         has: Object.prototype.hasOwnProperty,
855
856         keyOf: function(value){
857                 for (var key in this){
858                         if (this.hasOwnProperty(key) && this[key] === value) return key;
859                 }
860                 return null;
861         },
862
863         hasValue: function(value){
864                 return (Hash.keyOf(this, value) !== null);
865         },
866
867         extend: function(properties){
868                 Hash.each(properties || {}, function(value, key){
869                         Hash.set(this, key, value);
870                 }, this);
871                 return this;
872         },
873
874         combine: function(properties){
875                 Hash.each(properties || {}, function(value, key){
876                         Hash.include(this, key, value);
877                 }, this);
878                 return this;
879         },
880
881         erase: function(key){
882                 if (this.hasOwnProperty(key)) delete this[key];
883                 return this;
884         },
885
886         get: function(key){
887                 return (this.hasOwnProperty(key)) ? this[key] : null;
888         },
889
890         set: function(key, value){
891                 if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
892                 return this;
893         },
894
895         empty: function(){
896                 Hash.each(this, function(value, key){
897                         delete this[key];
898                 }, this);
899                 return this;
900         },
901
902         include: function(key, value){
903                 if (this[key] == undefined) this[key] = value;
904                 return this;
905         },
906
907         map: function(fn, bind){
908                 var results = new Hash;
909                 Hash.each(this, function(value, key){
910                         results.set(key, fn.call(bind, value, key, this));
911                 }, this);
912                 return results;
913         },
914
915         filter: function(fn, bind){
916                 var results = new Hash;
917                 Hash.each(this, function(value, key){
918                         if (fn.call(bind, value, key, this)) results.set(key, value);
919                 }, this);
920                 return results;
921         },
922
923         every: function(fn, bind){
924                 for (var key in this){
925                         if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
926                 }
927                 return true;
928         },
929
930         some: function(fn, bind){
931                 for (var key in this){
932                         if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
933                 }
934                 return false;
935         },
936
937         getKeys: function(){
938                 var keys = [];
939                 Hash.each(this, function(value, key){
940                         keys.push(key);
941                 });
942                 return keys;
943         },
944
945         getValues: function(){
946                 var values = [];
947                 Hash.each(this, function(value){
948                         values.push(value);
949                 });
950                 return values;
951         },
952
953         toQueryString: function(base){
954                 var queryString = [];
955                 Hash.each(this, function(value, key){
956                         if (base) key = base + '[' + key + ']';
957                         var result;
958                         switch ($type(value)){
959                                 case 'object': result = Hash.toQueryString(value, key); break;
960                                 case 'array':
961                                         var qs = {};
962                                         value.each(function(val, i){
963                                                 qs[i] = val;
964                                         });
965                                         result = Hash.toQueryString(qs, key);
966                                 break;
967                                 default: result = key + '=' + encodeURIComponent(value);
968                         }
969                         if (value != undefined) queryString.push(result);
970                 });
971
972                 return queryString.join('&');
973         }
974
975 });
976
977 Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
978
979
980 /*
981 ---
982
983 name: Event
984
985 description: Contains the Event Class, to make the event object cross-browser.
986
987 license: MIT-style license.
988
989 requires: [Window, Document, Hash, Array, Function, String]
990
991 provides: Event
992
993 ...
994 */
995
996 var Event = new Native({
997
998         name: 'Event',
999
1000         initialize: function(event, win){
1001                 win = win || window;
1002                 var doc = win.document;
1003                 event = event || win.event;
1004                 if (event.$extended) return event;
1005                 this.$extended = true;
1006                 var type = event.type;
1007                 var target = event.target || event.srcElement;
1008                 while (target && target.nodeType == 3) target = target.parentNode;
1009
1010                 if (type.test(/key/)){
1011                         var code = event.which || event.keyCode;
1012                         var key = Event.Keys.keyOf(code);
1013                         if (type == 'keydown'){
1014                                 var fKey = code - 111;
1015                                 if (fKey > 0 && fKey < 13) key = 'f' + fKey;
1016                         }
1017                         key = key || String.fromCharCode(code).toLowerCase();
1018                 } else if (type.match(/(click|mouse|menu)/i)){
1019                         doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
1020                         var page = {
1021                                 x: event.pageX || event.clientX + doc.scrollLeft,
1022                                 y: event.pageY || event.clientY + doc.scrollTop
1023                         };
1024                         var client = {
1025                                 x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
1026                                 y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
1027                         };
1028                         if (type.match(/DOMMouseScroll|mousewheel/)){
1029                                 var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
1030                         }
1031                         var rightClick = (event.which == 3) || (event.button == 2);
1032                         var related = null;
1033                         if (type.match(/over|out/)){
1034                                 switch (type){
1035                                         case 'mouseover': related = event.relatedTarget || event.fromElement; break;
1036                                         case 'mouseout': related = event.relatedTarget || event.toElement;
1037                                 }
1038                                 if (!(function(){
1039                                         while (related && related.nodeType == 3) related = related.parentNode;
1040                                         return true;
1041                                 }).create({attempt: Browser.Engine.gecko})()) related = false;
1042                         }
1043                 }
1044
1045                 return $extend(this, {
1046                         event: event,
1047                         type: type,
1048
1049                         page: page,
1050                         client: client,
1051                         rightClick: rightClick,
1052
1053                         wheel: wheel,
1054
1055                         relatedTarget: related,
1056                         target: target,
1057
1058                         code: code,
1059                         key: key,
1060
1061                         shift: event.shiftKey,
1062                         control: event.ctrlKey,
1063                         alt: event.altKey,
1064                         meta: event.metaKey
1065                 });
1066         }
1067
1068 });
1069
1070 Event.Keys = new Hash({
1071         'enter': 13,
1072         'up': 38,
1073         'down': 40,
1074         'left': 37,
1075         'right': 39,
1076         'esc': 27,
1077         'space': 32,
1078         'backspace': 8,
1079         'tab': 9,
1080         'delete': 46
1081 });
1082
1083 Event.implement({
1084
1085         stop: function(){
1086                 return this.stopPropagation().preventDefault();
1087         },
1088
1089         stopPropagation: function(){
1090                 if (this.event.stopPropagation) this.event.stopPropagation();
1091                 else this.event.cancelBubble = true;
1092                 return this;
1093         },
1094
1095         preventDefault: function(){
1096                 if (this.event.preventDefault) this.event.preventDefault();
1097                 else this.event.returnValue = false;
1098                 return this;
1099         }
1100
1101 });
1102
1103
1104 /*
1105 ---
1106
1107 name: Class
1108
1109 description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
1110
1111 license: MIT-style license.
1112
1113 requires: [$util, Native, Array, String, Function, Number, Hash]
1114
1115 provides: Class
1116
1117 ...
1118 */
1119
1120 function Class(params){
1121         
1122         if (params instanceof Function) params = {initialize: params};
1123         
1124         var newClass = function(){
1125                 Object.reset(this);
1126                 if (newClass._prototyping) return this;
1127                 this._current = $empty;
1128                 var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
1129                 delete this._current; delete this.caller;
1130                 return value;
1131         }.extend(this);
1132         
1133         newClass.implement(params);
1134         
1135         newClass.constructor = Class;
1136         newClass.prototype.constructor = newClass;
1137
1138         return newClass;
1139
1140 };
1141
1142 Function.prototype.protect = function(){
1143         this._protected = true;
1144         return this;
1145 };
1146
1147 Object.reset = function(object, key){
1148                 
1149         if (key == null){
1150                 for (var p in object) Object.reset(object, p);
1151                 return object;
1152         }
1153         
1154         delete object[key];
1155         
1156         switch ($type(object[key])){
1157                 case 'object':
1158                         var F = function(){};
1159                         F.prototype = object[key];
1160                         var i = new F;
1161                         object[key] = Object.reset(i);
1162                 break;
1163                 case 'array': object[key] = $unlink(object[key]); break;
1164         }
1165         
1166         return object;
1167         
1168 };
1169
1170 new Native({name: 'Class', initialize: Class}).extend({
1171
1172         instantiate: function(F){
1173                 F._prototyping = true;
1174                 var proto = new F;
1175                 delete F._prototyping;
1176                 return proto;
1177         },
1178         
1179         wrap: function(self, key, method){
1180                 if (method._origin) method = method._origin;
1181                 
1182                 return function(){
1183                         if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.');
1184                         var caller = this.caller, current = this._current;
1185                         this.caller = current; this._current = arguments.callee;
1186                         var result = method.apply(this, arguments);
1187                         this._current = current; this.caller = caller;
1188                         return result;
1189                 }.extend({_owner: self, _origin: method, _name: key});
1190
1191         }
1192         
1193 });
1194
1195 Class.implement({
1196         
1197         implement: function(key, value){
1198                 
1199                 if ($type(key) == 'object'){
1200                         for (var p in key) this.implement(p, key[p]);
1201                         return this;
1202                 }
1203                 
1204                 var mutator = Class.Mutators[key];
1205                 
1206                 if (mutator){
1207                         value = mutator.call(this, value);
1208                         if (value == null) return this;
1209                 }
1210                 
1211                 var proto = this.prototype;
1212
1213                 switch ($type(value)){
1214                         
1215                         case 'function':
1216                                 if (value._hidden) return this;
1217                                 proto[key] = Class.wrap(this, key, value);
1218                         break;
1219                         
1220                         case 'object':
1221                                 var previous = proto[key];
1222                                 if ($type(previous) == 'object') $mixin(previous, value);
1223                                 else proto[key] = $unlink(value);
1224                         break;
1225                         
1226                         case 'array':
1227                                 proto[key] = $unlink(value);
1228                         break;
1229                         
1230                         default: proto[key] = value;
1231
1232                 }
1233                 
1234                 return this;
1235
1236         }
1237         
1238 });
1239
1240 Class.Mutators = {
1241         
1242         Extends: function(parent){
1243
1244                 this.parent = parent;
1245                 this.prototype = Class.instantiate(parent);
1246
1247                 this.implement('parent', function(){
1248                         var name = this.caller._name, previous = this.caller._owner.parent.prototype[name];
1249                         if (!previous) throw new Error('The method "' + name + '" has no parent.');
1250                         return previous.apply(this, arguments);
1251                 }.protect());
1252
1253         },
1254
1255         Implements: function(items){
1256                 $splat(items).each(function(item){
1257                         if (item instanceof Function) item = Class.instantiate(item);
1258                         this.implement(item);
1259                 }, this);
1260
1261         }
1262         
1263 };
1264
1265
1266 /*
1267 ---
1268
1269 name: Class.Extras
1270
1271 description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
1272
1273 license: MIT-style license.
1274
1275 requires: Class
1276
1277 provides: [Chain, Events, Options, Class.Extras]
1278
1279 ...
1280 */
1281
1282 var Chain = new Class({
1283
1284         $chain: [],
1285
1286         chain: function(){
1287                 this.$chain.extend(Array.flatten(arguments));
1288                 return this;
1289         },
1290
1291         callChain: function(){
1292                 return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
1293         },
1294
1295         clearChain: function(){
1296                 this.$chain.empty();
1297                 return this;
1298         }
1299
1300 });
1301
1302 var Events = new Class({
1303
1304         $events: {},
1305
1306         addEvent: function(type, fn, internal){
1307                 type = Events.removeOn(type);
1308                 if (fn != $empty){
1309                         this.$events[type] = this.$events[type] || [];
1310                         this.$events[type].include(fn);
1311                         if (internal) fn.internal = true;
1312                 }
1313                 return this;
1314         },
1315
1316         addEvents: function(events){
1317                 for (var type in events) this.addEvent(type, events[type]);
1318                 return this;
1319         },
1320
1321         fireEvent: function(type, args, delay){
1322                 type = Events.removeOn(type);
1323                 if (!this.$events || !this.$events[type]) return this;
1324                 this.$events[type].each(function(fn){
1325                         fn.create({'bind': this, 'delay': delay, 'arguments': args})();
1326                 }, this);
1327                 return this;
1328         },
1329
1330         removeEvent: function(type, fn){
1331                 type = Events.removeOn(type);
1332                 if (!this.$events[type]) return this;
1333                 if (!fn.internal) this.$events[type].erase(fn);
1334                 return this;
1335         },
1336
1337         removeEvents: function(events){
1338                 var type;
1339                 if ($type(events) == 'object'){
1340                         for (type in events) this.removeEvent(type, events[type]);
1341                         return this;
1342                 }
1343                 if (events) events = Events.removeOn(events);
1344                 for (type in this.$events){
1345                         if (events && events != type) continue;
1346                         var fns = this.$events[type];
1347                         for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
1348                 }
1349                 return this;
1350         }
1351
1352 });
1353
1354 Events.removeOn = function(string){
1355         return string.replace(/^on([A-Z])/, function(full, first){
1356                 return first.toLowerCase();
1357         });
1358 };
1359
1360 var Options = new Class({
1361
1362         setOptions: function(){
1363                 this.options = $merge.run([this.options].extend(arguments));
1364                 if (!this.addEvent) return this;
1365                 for (var option in this.options){
1366                         if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
1367                         this.addEvent(option, this.options[option]);
1368                         delete this.options[option];
1369                 }
1370                 return this;
1371         }
1372
1373 });
1374
1375
1376
1377 /*
1378 ---
1379
1380 name: Cookie
1381
1382 description: Class for creating, reading, and deleting browser Cookies.
1383
1384 license: MIT-style license.
1385
1386 credits: Based on the functions by Peter-Paul Koch (http://quirksmode.org).
1387
1388 requires: Options
1389
1390 provides: Cookie
1391
1392 ...
1393 */
1394
1395 var Cookie = new Class({
1396
1397         Implements: Options,
1398
1399         options: {
1400                 path: false,
1401                 domain: false,
1402                 duration: false,
1403                 secure: false,
1404                 document: document
1405         },
1406
1407         initialize: function(key, options){
1408                 this.key = key;
1409                 this.setOptions(options);
1410         },
1411
1412         write: function(value){
1413                 value = encodeURIComponent(value);
1414                 if (this.options.domain) value += '; domain=' + this.options.domain;
1415                 if (this.options.path) value += '; path=' + this.options.path;
1416                 if (this.options.duration){
1417                         var date = new Date();
1418                         date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
1419                         value += '; expires=' + date.toGMTString();
1420                 }
1421                 if (this.options.secure) value += '; secure';
1422                 this.options.document.cookie = this.key + '=' + value;
1423                 return this;
1424         },
1425
1426         read: function(){
1427                 var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
1428                 return (value) ? decodeURIComponent(value[1]) : null;
1429         },
1430
1431         dispose: function(){
1432                 new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
1433                 return this;
1434         }
1435
1436 });
1437
1438 Cookie.write = function(key, value, options){
1439         return new Cookie(key, options).write(value);
1440 };
1441
1442 Cookie.read = function(key){
1443         return new Cookie(key).read();
1444 };
1445
1446 Cookie.dispose = function(key, options){
1447         return new Cookie(key, options).dispose();
1448 };
1449
1450
1451 /*
1452 ---
1453
1454 name: Request
1455
1456 description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
1457
1458 license: MIT-style license.
1459
1460 requires: [Element, Chain, Events, Options, Browser]
1461
1462 provides: Request
1463
1464 ...
1465 */
1466
1467 var Request = new Class({
1468
1469         Implements: [Chain, Events, Options],
1470
1471         options: {/*
1472                 onRequest: $empty,
1473                 onComplete: $empty,
1474                 onCancel: $empty,
1475                 onSuccess: $empty,
1476                 onFailure: $empty,
1477                 onException: $empty,*/
1478                 url: '',
1479                 data: '',
1480                 headers: {
1481                         'X-Requested-With': 'XMLHttpRequest',
1482                         'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1483                 },
1484                 async: true,
1485                 format: false,
1486                 method: 'post',
1487                 link: 'ignore',
1488                 isSuccess: null,
1489                 emulation: true,
1490                 urlEncoded: true,
1491                 encoding: 'utf-8',
1492                 evalScripts: false,
1493                 evalResponse: false,
1494                 noCache: false
1495         },
1496
1497         initialize: function(options){
1498                 this.xhr = new Browser.Request();
1499                 this.setOptions(options);
1500                 this.options.isSuccess = this.options.isSuccess || this.isSuccess;
1501                 this.headers = new Hash(this.options.headers);
1502         },
1503
1504         onStateChange: function(){
1505                 if (this.xhr.readyState != 4 || !this.running) return;
1506                 this.running = false;
1507                 this.status = 0;
1508                 $try(function(){
1509                         this.status = this.xhr.status;
1510                 }.bind(this));
1511                 this.xhr.onreadystatechange = $empty;
1512                 if (this.options.isSuccess.call(this, this.status)){
1513                         this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
1514                         this.success(this.response.text, this.response.xml);
1515                 } else {
1516                         this.response = {text: null, xml: null};
1517                         this.failure();
1518                 }
1519         },
1520
1521         isSuccess: function(){
1522                 return ((this.status >= 200) && (this.status < 300));
1523         },
1524
1525         processScripts: function(text){
1526                 if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
1527                 return text.stripScripts(this.options.evalScripts);
1528         },
1529
1530         success: function(text, xml){
1531                 this.onSuccess(this.processScripts(text), xml);
1532         },
1533
1534         onSuccess: function(){
1535                 this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
1536         },
1537
1538         failure: function(){
1539                 this.onFailure();
1540         },
1541
1542         onFailure: function(){
1543                 this.fireEvent('complete').fireEvent('failure', this.xhr);
1544         },
1545
1546         setHeader: function(name, value){
1547                 this.headers.set(name, value);
1548                 return this;
1549         },
1550
1551         getHeader: function(name){
1552                 return $try(function(){
1553                         return this.xhr.getResponseHeader(name);
1554                 }.bind(this));
1555         },
1556
1557         check: function(){
1558                 if (!this.running) return true;
1559                 switch (this.options.link){
1560                         case 'cancel': this.cancel(); return true;
1561                         case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
1562                 }
1563                 return false;
1564         },
1565
1566         send: function(options){
1567                 if (!this.check(options)) return this;
1568                 this.running = true;
1569
1570                 var type = $type(options);
1571                 if (type == 'string' || type == 'element') options = {data: options};
1572
1573                 var old = this.options;
1574                 options = $extend({data: old.data, url: old.url, method: old.method}, options);
1575                 var data = options.data, url = String(options.url), method = options.method.toLowerCase();
1576
1577                 switch ($type(data)){
1578                         case 'element': data = document.id(data).toQueryString(); break;
1579                         case 'object': case 'hash': data = Hash.toQueryString(data);
1580                 }
1581
1582                 if (this.options.format){
1583                         var format = 'format=' + this.options.format;
1584                         data = (data) ? format + '&' + data : format;
1585                 }
1586
1587                 if (this.options.emulation && !['get', 'post'].contains(method)){
1588                         var _method = '_method=' + method;
1589                         data = (data) ? _method + '&' + data : _method;
1590                         method = 'post';
1591                 }
1592
1593                 if (this.options.urlEncoded && method == 'post'){
1594                         var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
1595                         this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
1596                 }
1597
1598                 if (this.options.noCache){
1599                         var noCache = 'noCache=' + new Date().getTime();
1600                         data = (data) ? noCache + '&' + data : noCache;
1601                 }
1602
1603                 var trimPosition = url.lastIndexOf('/');
1604                 if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
1605
1606                 if (data && method == 'get'){
1607                         url = url + (url.contains('?') ? '&' : '?') + data;
1608                         data = null;
1609                 }
1610
1611                 this.xhr.open(method.toUpperCase(), url, this.options.async);
1612
1613                 this.xhr.onreadystatechange = this.onStateChange.bind(this);
1614
1615                 this.headers.each(function(value, key){
1616                         try {
1617                                 this.xhr.setRequestHeader(key, value);
1618                         } catch (e){
1619                                 this.fireEvent('exception', [key, value]);
1620                         }
1621                 }, this);
1622
1623                 this.fireEvent('request');
1624                 this.xhr.send(data);
1625                 if (!this.options.async) this.onStateChange();
1626                 return this;
1627         },
1628
1629         cancel: function(){
1630                 if (!this.running) return this;
1631                 this.running = false;
1632                 this.xhr.abort();
1633                 this.xhr.onreadystatechange = $empty;
1634                 this.xhr = new Browser.Request();
1635                 this.fireEvent('cancel');
1636                 return this;
1637         }
1638
1639 });
1640
1641 (function(){
1642
1643 var methods = {};
1644 ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
1645         methods[method] = function(){
1646                 var params = Array.link(arguments, {url: String.type, data: $defined});
1647                 return this.send($extend(params, {method: method}));
1648         };
1649 });
1650
1651 Request.implement(methods);
1652
1653 })();