Adds jQuery collection to objects that will be used as global events context if provi...
[jquery.git] / test / otherlibs / mootools / 1.11 / mootools.js
1 //MooTools, My Object Oriented Javascript Tools. Copyright (c) 2006-2007 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
2
3 var MooTools = {
4         version: '1.11'
5 };
6
7 function $defined(obj){
8         return (obj != undefined);
9 };
10
11 function $type(obj){
12         if (!$defined(obj)) return false;
13         if (obj.htmlElement) return 'element';
14         var type = typeof obj;
15         if (type == 'object' && obj.nodeName){
16                 switch(obj.nodeType){
17                         case 1: return 'element';
18                         case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
19                 }
20         }
21         if (type == 'object' || type == 'function'){
22                 switch(obj.constructor){
23                         case Array: return 'array';
24                         case RegExp: return 'regexp';
25                         case Class: return 'class';
26                 }
27                 if (typeof obj.length == 'number'){
28                         if (obj.item) return 'collection';
29                         if (obj.callee) return 'arguments';
30                 }
31         }
32         return type;
33 };
34
35 function $merge(){
36         var mix = {};
37         for (var i = 0; i < arguments.length; i++){
38                 for (var property in arguments[i]){
39                         var ap = arguments[i][property];
40                         var mp = mix[property];
41                         if (mp && $type(ap) == 'object' && $type(mp) == 'object') mix[property] = $merge(mp, ap);
42                         else mix[property] = ap;
43                 }
44         }
45         return mix;
46 };
47
48 var $extend = function(){
49         var args = arguments;
50         if (!args[1]) args = [this, args[0]];
51         for (var property in args[1]) args[0][property] = args[1][property];
52         return args[0];
53 };
54
55 var $native = function(){
56         for (var i = 0, l = arguments.length; i < l; i++){
57                 arguments[i].extend = function(props){
58                         for (var prop in props){
59                                 if (!this.prototype[prop]) this.prototype[prop] = props[prop];
60                                 if (!this[prop]) this[prop] = $native.generic(prop);
61                         }
62                 };
63         }
64 };
65
66 $native.generic = function(prop){
67         return function(bind){
68                 return this.prototype[prop].apply(bind, Array.prototype.slice.call(arguments, 1));
69         };
70 };
71
72 $native(Function, Array, String, Number);
73
74 function $chk(obj){
75         return !!(obj || obj === 0);
76 };
77
78 function $pick(obj, picked){
79         return $defined(obj) ? obj : picked;
80 };
81
82 function $random(min, max){
83         return Math.floor(Math.random() * (max - min + 1) + min);
84 };
85
86 function $time(){
87         return new Date().getTime();
88 };
89
90 function $clear(timer){
91         clearTimeout(timer);
92         clearInterval(timer);
93         return null;
94 };
95
96 var Abstract = function(obj){
97         obj = obj || {};
98         obj.extend = $extend;
99         return obj;
100 };
101
102 var Window = new Abstract(window);
103 var Document = new Abstract(document);
104 document.head = document.getElementsByTagName('head')[0];
105
106 window.xpath = !!(document.evaluate);
107 if (window.ActiveXObject) window.ie = window[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true;
108 else if (document.childNodes && !document.all && !navigator.taintEnabled) window.webkit = window[window.xpath ? 'webkit420' : 'webkit419'] = true;
109 else if (document.getBoxObjectFor != null) window.gecko = true;
110
111 window.khtml = window.webkit;
112
113 Object.extend = $extend;
114
115 if (typeof HTMLElement == 'undefined'){
116         var HTMLElement = function(){};
117         if (window.webkit) document.createElement("iframe");
118         HTMLElement.prototype = (window.webkit) ? window["[[DOMElement.prototype]]"] : {};
119 }
120 HTMLElement.prototype.htmlElement = function(){};
121
122 if (window.ie6) try {document.execCommand("BackgroundImageCache", false, true);} catch(e){};
123
124 var Class = function(properties){
125         var klass = function(){
126                 return (arguments[0] !== null && this.initialize && $type(this.initialize) == 'function') ? this.initialize.apply(this, arguments) : this;
127         };
128         $extend(klass, this);
129         klass.prototype = properties;
130         klass.constructor = Class;
131         return klass;
132 };
133
134 Class.empty = function(){};
135
136 Class.prototype = {
137
138         extend: function(properties){
139                 var proto = new this(null);
140                 for (var property in properties){
141                         var pp = proto[property];
142                         proto[property] = Class.Merge(pp, properties[property]);
143                 }
144                 return new Class(proto);
145         },
146
147         implement: function(){
148                 for (var i = 0, l = arguments.length; i < l; i++) $extend(this.prototype, arguments[i]);
149         }
150
151 };
152
153 Class.Merge = function(previous, current){
154         if (previous && previous != current){
155                 var type = $type(current);
156                 if (type != $type(previous)) return current;
157                 switch(type){
158                         case 'function':
159                                 var merged = function(){
160                                         this.parent = arguments.callee.parent;
161                                         return current.apply(this, arguments);
162                                 };
163                                 merged.parent = previous;
164                                 return merged;
165                         case 'object': return $merge(previous, current);
166                 }
167         }
168         return current;
169 };
170
171 var Chain = new Class({
172
173         chain: function(fn){
174                 this.chains = this.chains || [];
175                 this.chains.push(fn);
176                 return this;
177         },
178
179         callChain: function(){
180                 if (this.chains && this.chains.length) this.chains.shift().delay(10, this);
181         },
182
183         clearChain: function(){
184                 this.chains = [];
185         }
186
187 });
188
189 var Events = new Class({
190
191         addEvent: function(type, fn){
192                 if (fn != Class.empty){
193                         this.$events = this.$events || {};
194                         this.$events[type] = this.$events[type] || [];
195                         this.$events[type].include(fn);
196                 }
197                 return this;
198         },
199
200         fireEvent: function(type, args, delay){
201                 if (this.$events && this.$events[type]){
202                         this.$events[type].each(function(fn){
203                                 fn.create({'bind': this, 'delay': delay, 'arguments': args})();
204                         }, this);
205                 }
206                 return this;
207         },
208
209         removeEvent: function(type, fn){
210                 if (this.$events && this.$events[type]) this.$events[type].remove(fn);
211                 return this;
212         }
213
214 });
215
216 var Options = new Class({
217
218         setOptions: function(){
219                 this.options = $merge.apply(null, [this.options].extend(arguments));
220                 if (this.addEvent){
221                         for (var option in this.options){
222                                 if ($type(this.options[option] == 'function') && (/^on[A-Z]/).test(option)) this.addEvent(option, this.options[option]);
223                         }
224                 }
225                 return this;
226         }
227
228 });
229
230 Array.extend({
231
232         forEach: function(fn, bind){
233                 for (var i = 0, j = this.length; i < j; i++) fn.call(bind, this[i], i, this);
234         },
235
236         filter: function(fn, bind){
237                 var results = [];
238                 for (var i = 0, j = this.length; i < j; i++){
239                         if (fn.call(bind, this[i], i, this)) results.push(this[i]);
240                 }
241                 return results;
242         },
243
244         map: function(fn, bind){
245                 var results = [];
246                 for (var i = 0, j = this.length; i < j; i++) results[i] = fn.call(bind, this[i], i, this);
247                 return results;
248         },
249
250         every: function(fn, bind){
251                 for (var i = 0, j = this.length; i < j; i++){
252                         if (!fn.call(bind, this[i], i, this)) return false;
253                 }
254                 return true;
255         },
256
257         some: function(fn, bind){
258                 for (var i = 0, j = this.length; i < j; i++){
259                         if (fn.call(bind, this[i], i, this)) return true;
260                 }
261                 return false;
262         },
263
264         indexOf: function(item, from){
265                 var len = this.length;
266                 for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
267                         if (this[i] === item) return i;
268                 }
269                 return -1;
270         },
271
272         copy: function(start, length){
273                 start = start || 0;
274                 if (start < 0) start = this.length + start;
275                 length = length || (this.length - start);
276                 var newArray = [];
277                 for (var i = 0; i < length; i++) newArray[i] = this[start++];
278                 return newArray;
279         },
280
281         remove: function(item){
282                 var i = 0;
283                 var len = this.length;
284                 while (i < len){
285                         if (this[i] === item){
286                                 this.splice(i, 1);
287                                 len--;
288                         } else {
289                                 i++;
290                         }
291                 }
292                 return this;
293         },
294
295         contains: function(item, from){
296                 return this.indexOf(item, from) != -1;
297         },
298
299         associate: function(keys){
300                 var obj = {}, length = Math.min(this.length, keys.length);
301                 for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
302                 return obj;
303         },
304
305         extend: function(array){
306                 for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
307                 return this;
308         },
309
310         merge: function(array){
311                 for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
312                 return this;
313         },
314
315         include: function(item){
316                 if (!this.contains(item)) this.push(item);
317                 return this;
318         },
319
320         getRandom: function(){
321                 return this[$random(0, this.length - 1)] || null;
322         },
323
324         getLast: function(){
325                 return this[this.length - 1] || null;
326         }
327
328 });
329
330 Array.prototype.each = Array.prototype.forEach;
331 Array.each = Array.forEach;
332
333 function $A(array){
334         return Array.copy(array);
335 };
336
337 function $each(iterable, fn, bind){
338         if (iterable && typeof iterable.length == 'number' && $type(iterable) != 'object'){
339                 Array.forEach(iterable, fn, bind);
340         } else {
341                  for (var name in iterable) fn.call(bind || iterable, iterable[name], name);
342         }
343 };
344
345 Array.prototype.test = Array.prototype.contains;
346
347 String.extend({
348
349         test: function(regex, params){
350                 return (($type(regex) == 'string') ? new RegExp(regex, params) : regex).test(this);
351         },
352
353         toInt: function(){
354                 return parseInt(this, 10);
355         },
356
357         toFloat: function(){
358                 return parseFloat(this);
359         },
360
361         camelCase: function(){
362                 return this.replace(/-\D/g, function(match){
363                         return match.charAt(1).toUpperCase();
364                 });
365         },
366
367         hyphenate: function(){
368                 return this.replace(/\w[A-Z]/g, function(match){
369                         return (match.charAt(0) + '-' + match.charAt(1).toLowerCase());
370                 });
371         },
372
373         capitalize: function(){
374                 return this.replace(/\b[a-z]/g, function(match){
375                         return match.toUpperCase();
376                 });
377         },
378
379         trim: function(){
380                 return this.replace(/^\s+|\s+$/g, '');
381         },
382
383         clean: function(){
384                 return this.replace(/\s{2,}/g, ' ').trim();
385         },
386
387         rgbToHex: function(array){
388                 var rgb = this.match(/\d{1,3}/g);
389                 return (rgb) ? rgb.rgbToHex(array) : false;
390         },
391
392         hexToRgb: function(array){
393                 var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
394                 return (hex) ? hex.slice(1).hexToRgb(array) : false;
395         },
396
397         contains: function(string, s){
398                 return (s) ? (s + this + s).indexOf(s + string + s) > -1 : this.indexOf(string) > -1;
399         },
400
401         escapeRegExp: function(){
402                 return this.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
403         }
404
405 });
406
407 Array.extend({
408
409         rgbToHex: function(array){
410                 if (this.length < 3) return false;
411                 if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
412                 var hex = [];
413                 for (var i = 0; i < 3; i++){
414                         var bit = (this[i] - 0).toString(16);
415                         hex.push((bit.length == 1) ? '0' + bit : bit);
416                 }
417                 return array ? hex : '#' + hex.join('');
418         },
419
420         hexToRgb: function(array){
421                 if (this.length != 3) return false;
422                 var rgb = [];
423                 for (var i = 0; i < 3; i++){
424                         rgb.push(parseInt((this[i].length == 1) ? this[i] + this[i] : this[i], 16));
425                 }
426                 return array ? rgb : 'rgb(' + rgb.join(',') + ')';
427         }
428
429 });
430
431 Function.extend({
432
433         create: function(options){
434                 var fn = this;
435                 options = $merge({
436                         'bind': fn,
437                         'event': false,
438                         'arguments': null,
439                         'delay': false,
440                         'periodical': false,
441                         'attempt': false
442                 }, options);
443                 if ($chk(options.arguments) && $type(options.arguments) != 'array') options.arguments = [options.arguments];
444                 return function(event){
445                         var args;
446                         if (options.event){
447                                 event = event || window.event;
448                                 args = [(options.event === true) ? event : new options.event(event)];
449                                 if (options.arguments) args.extend(options.arguments);
450                         }
451                         else args = options.arguments || arguments;
452                         var returns = function(){
453                                 return fn.apply($pick(options.bind, fn), args);
454                         };
455                         if (options.delay) return setTimeout(returns, options.delay);
456                         if (options.periodical) return setInterval(returns, options.periodical);
457                         if (options.attempt) try {return returns();} catch(err){return false;};
458                         return returns();
459                 };
460         },
461
462         pass: function(args, bind){
463                 return this.create({'arguments': args, 'bind': bind});
464         },
465
466         attempt: function(args, bind){
467                 return this.create({'arguments': args, 'bind': bind, 'attempt': true})();
468         },
469
470         bind: function(bind, args){
471                 return this.create({'bind': bind, 'arguments': args});
472         },
473
474         bindAsEventListener: function(bind, args){
475                 return this.create({'bind': bind, 'event': true, 'arguments': args});
476         },
477
478         delay: function(delay, bind, args){
479                 return this.create({'delay': delay, 'bind': bind, 'arguments': args})();
480         },
481
482         periodical: function(interval, bind, args){
483                 return this.create({'periodical': interval, 'bind': bind, 'arguments': args})();
484         }
485
486 });
487
488 Number.extend({
489
490         toInt: function(){
491                 return parseInt(this);
492         },
493
494         toFloat: function(){
495                 return parseFloat(this);
496         },
497
498         limit: function(min, max){
499                 return Math.min(max, Math.max(min, this));
500         },
501
502         round: function(precision){
503                 precision = Math.pow(10, precision || 0);
504                 return Math.round(this * precision) / precision;
505         },
506
507         times: function(fn){
508                 for (var i = 0; i < this; i++) fn(i);
509         }
510
511 });
512
513 var Element = new Class({
514
515         initialize: function(el, props){
516                 if ($type(el) == 'string'){
517                         if (window.ie && props && (props.name || props.type)){
518                                 var name = (props.name) ? ' name="' + props.name + '"' : '';
519                                 var type = (props.type) ? ' type="' + props.type + '"' : '';
520                                 delete props.name;
521                                 delete props.type;
522                                 el = '<' + el + name + type + '>';
523                         }
524                         el = document.createElement(el);
525                 }
526                 el = $(el);
527                 return (!props || !el) ? el : el.set(props);
528         }
529
530 });
531
532 var Elements = new Class({
533
534         initialize: function(elements){
535                 return (elements) ? $extend(elements, this) : this;
536         }
537
538 });
539
540 Elements.extend = function(props){
541         for (var prop in props){
542                 this.prototype[prop] = props[prop];
543                 this[prop] = $native.generic(prop);
544         }
545 };
546
547 function $(el){
548         if (!el) return null;
549         if (el.htmlElement) return Garbage.collect(el);
550         if ([window, document].contains(el)) return el;
551         var type = $type(el);
552         if (type == 'string'){
553                 el = document.getElementById(el);
554                 type = (el) ? 'element' : false;
555         }
556         if (type != 'element') return null;
557         if (el.htmlElement) return Garbage.collect(el);
558         if (['object', 'embed'].contains(el.tagName.toLowerCase())) return el;
559         $extend(el, Element.prototype);
560         el.htmlElement = function(){};
561         return Garbage.collect(el);
562 };
563
564 document.getElementsBySelector = document.getElementsByTagName;
565
566 function $$(){
567         var elements = [];
568         for (var i = 0, j = arguments.length; i < j; i++){
569                 var selector = arguments[i];
570                 switch($type(selector)){
571                         case 'element': elements.push(selector);
572                         case 'boolean': break;
573                         case false: break;
574                         case 'string': selector = document.getElementsBySelector(selector, true);
575                         default: elements.extend(selector);
576                 }
577         }
578         return $$.unique(elements);
579 };
580
581 $$.unique = function(array){
582         var elements = [];
583         for (var i = 0, l = array.length; i < l; i++){
584                 if (array[i].$included) continue;
585                 var element = $(array[i]);
586                 if (element && !element.$included){
587                         element.$included = true;
588                         elements.push(element);
589                 }
590         }
591         for (var n = 0, d = elements.length; n < d; n++) elements[n].$included = null;
592         return new Elements(elements);
593 };
594
595 Elements.Multi = function(property){
596         return function(){
597                 var args = arguments;
598                 var items = [];
599                 var elements = true;
600                 for (var i = 0, j = this.length, returns; i < j; i++){
601                         returns = this[i][property].apply(this[i], args);
602                         if ($type(returns) != 'element') elements = false;
603                         items.push(returns);
604                 };
605                 return (elements) ? $$.unique(items) : items;
606         };
607 };
608
609 Element.extend = function(properties){
610         for (var property in properties){
611                 HTMLElement.prototype[property] = properties[property];
612                 Element.prototype[property] = properties[property];
613                 Element[property] = $native.generic(property);
614                 var elementsProperty = (Array.prototype[property]) ? property + 'Elements' : property;
615                 Elements.prototype[elementsProperty] = Elements.Multi(property);
616         }
617 };
618
619 Element.extend({
620
621         set: function(props){
622                 for (var prop in props){
623                         var val = props[prop];
624                         switch(prop){
625                                 case 'styles': this.setStyles(val); break;
626                                 case 'events': if (this.addEvents) this.addEvents(val); break;
627                                 case 'properties': this.setProperties(val); break;
628                                 default: this.setProperty(prop, val);
629                         }
630                 }
631                 return this;
632         },
633
634         inject: function(el, where){
635                 el = $(el);
636                 switch(where){
637                         case 'before': el.parentNode.insertBefore(this, el); break;
638                         case 'after':
639                                 var next = el.getNext();
640                                 if (!next) el.parentNode.appendChild(this);
641                                 else el.parentNode.insertBefore(this, next);
642                                 break;
643                         case 'top':
644                                 var first = el.firstChild;
645                                 if (first){
646                                         el.insertBefore(this, first);
647                                         break;
648                                 }
649                         default: el.appendChild(this);
650                 }
651                 return this;
652         },
653
654         injectBefore: function(el){
655                 return this.inject(el, 'before');
656         },
657
658         injectAfter: function(el){
659                 return this.inject(el, 'after');
660         },
661
662         injectInside: function(el){
663                 return this.inject(el, 'bottom');
664         },
665
666         injectTop: function(el){
667                 return this.inject(el, 'top');
668         },
669
670         adopt: function(){
671                 var elements = [];
672                 $each(arguments, function(argument){
673                         elements = elements.concat(argument);
674                 });
675                 $$(elements).inject(this);
676                 return this;
677         },
678
679         remove: function(){
680                 return this.parentNode.removeChild(this);
681         },
682
683         clone: function(contents){
684                 var el = $(this.cloneNode(contents !== false));
685                 if (!el.$events) return el;
686                 el.$events = {};
687                 for (var type in this.$events) el.$events[type] = {
688                         'keys': $A(this.$events[type].keys),
689                         'values': $A(this.$events[type].values)
690                 };
691                 return el.removeEvents();
692         },
693
694         replaceWith: function(el){
695                 el = $(el);
696                 this.parentNode.replaceChild(el, this);
697                 return el;
698         },
699
700         appendText: function(text){
701                 this.appendChild(document.createTextNode(text));
702                 return this;
703         },
704
705         hasClass: function(className){
706                 return this.className.contains(className, ' ');
707         },
708
709         addClass: function(className){
710                 if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
711                 return this;
712         },
713
714         removeClass: function(className){
715                 this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1').clean();
716                 return this;
717         },
718
719         toggleClass: function(className){
720                 return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
721         },
722
723         setStyle: function(property, value){
724                 switch(property){
725                         case 'opacity': return this.setOpacity(parseFloat(value));
726                         case 'float': property = (window.ie) ? 'styleFloat' : 'cssFloat';
727                 }
728                 property = property.camelCase();
729                 switch($type(value)){
730                         case 'number': if (!['zIndex', 'zoom'].contains(property)) value += 'px'; break;
731                         case 'array': value = 'rgb(' + value.join(',') + ')';
732                 }
733                 this.style[property] = value;
734                 return this;
735         },
736
737         setStyles: function(source){
738                 switch($type(source)){
739                         case 'object': Element.setMany(this, 'setStyle', source); break;
740                         case 'string': this.style.cssText = source;
741                 }
742                 return this;
743         },
744
745         setOpacity: function(opacity){
746                 if (opacity == 0){
747                         if (this.style.visibility != "hidden") this.style.visibility = "hidden";
748                 } else {
749                         if (this.style.visibility != "visible") this.style.visibility = "visible";
750                 }
751                 if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
752                 if (window.ie) this.style.filter = (opacity == 1) ? '' : "alpha(opacity=" + opacity * 100 + ")";
753                 this.style.opacity = this.$tmp.opacity = opacity;
754                 return this;
755         },
756
757         getStyle: function(property){
758                 property = property.camelCase();
759                 var result = this.style[property];
760                 if (!$chk(result)){
761                         if (property == 'opacity') return this.$tmp.opacity;
762                         result = [];
763                         for (var style in Element.Styles){
764                                 if (property == style){
765                                         Element.Styles[style].each(function(s){
766                                                 var style = this.getStyle(s);
767                                                 result.push(parseInt(style) ? style : '0px');
768                                         }, this);
769                                         if (property == 'border'){
770                                                 var every = result.every(function(bit){
771                                                         return (bit == result[0]);
772                                                 });
773                                                 return (every) ? result[0] : false;
774                                         }
775                                         return result.join(' ');
776                                 }
777                         }
778                         if (property.contains('border')){
779                                 if (Element.Styles.border.contains(property)){
780                                         return ['Width', 'Style', 'Color'].map(function(p){
781                                                 return this.getStyle(property + p);
782                                         }, this).join(' ');
783                                 } else if (Element.borderShort.contains(property)){
784                                         return ['Top', 'Right', 'Bottom', 'Left'].map(function(p){
785                                                 return this.getStyle('border' + p + property.replace('border', ''));
786                                         }, this).join(' ');
787                                 }
788                         }
789                         if (document.defaultView) result = document.defaultView.getComputedStyle(this, null).getPropertyValue(property.hyphenate());
790                         else if (this.currentStyle) result = this.currentStyle[property];
791                 }
792                 if (window.ie) result = Element.fixStyle(property, result, this);
793                 if (result && property.test(/color/i) && result.contains('rgb')){
794                         return result.split('rgb').splice(1,4).map(function(color){
795                                 return color.rgbToHex();
796                         }).join(' ');
797                 }
798                 return result;
799         },
800
801         getStyles: function(){
802                 return Element.getMany(this, 'getStyle', arguments);
803         },
804
805         walk: function(brother, start){
806                 brother += 'Sibling';
807                 var el = (start) ? this[start] : this[brother];
808                 while (el && $type(el) != 'element') el = el[brother];
809                 return $(el);
810         },
811
812         getPrevious: function(){
813                 return this.walk('previous');
814         },
815
816         getNext: function(){
817                 return this.walk('next');
818         },
819
820         getFirst: function(){
821                 return this.walk('next', 'firstChild');
822         },
823
824         getLast: function(){
825                 return this.walk('previous', 'lastChild');
826         },
827
828         getParent: function(){
829                 return $(this.parentNode);
830         },
831
832         getChildren: function(){
833                 return $$(this.childNodes);
834         },
835
836         hasChild: function(el){
837                 return !!$A(this.getElementsByTagName('*')).contains(el);
838         },
839
840         getProperty: function(property){
841                 var index = Element.Properties[property];
842                 if (index) return this[index];
843                 var flag = Element.PropertiesIFlag[property] || 0;
844                 if (!window.ie || flag) return this.getAttribute(property, flag);
845                 var node = this.attributes[property];
846                 return (node) ? node.nodeValue : null;
847         },
848
849         removeProperty: function(property){
850                 var index = Element.Properties[property];
851                 if (index) this[index] = '';
852                 else this.removeAttribute(property);
853                 return this;
854         },
855
856         getProperties: function(){
857                 return Element.getMany(this, 'getProperty', arguments);
858         },
859
860         setProperty: function(property, value){
861                 var index = Element.Properties[property];
862                 if (index) this[index] = value;
863                 else this.setAttribute(property, value);
864                 return this;
865         },
866
867         setProperties: function(source){
868                 return Element.setMany(this, 'setProperty', source);
869         },
870
871         setHTML: function(){
872                 this.innerHTML = $A(arguments).join('');
873                 return this;
874         },
875
876         setText: function(text){
877                 var tag = this.getTag();
878                 if (['style', 'script'].contains(tag)){
879                         if (window.ie){
880                                 if (tag == 'style') this.styleSheet.cssText = text;
881                                 else if (tag ==  'script') this.setProperty('text', text);
882                                 return this;
883                         } else {
884                                 this.removeChild(this.firstChild);
885                                 return this.appendText(text);
886                         }
887                 }
888                 this[$defined(this.innerText) ? 'innerText' : 'textContent'] = text;
889                 return this;
890         },
891
892         getText: function(){
893                 var tag = this.getTag();
894                 if (['style', 'script'].contains(tag)){
895                         if (window.ie){
896                                 if (tag == 'style') return this.styleSheet.cssText;
897                                 else if (tag ==  'script') return this.getProperty('text');
898                         } else {
899                                 return this.innerHTML;
900                         }
901                 }
902                 return ($pick(this.innerText, this.textContent));
903         },
904
905         getTag: function(){
906                 return this.tagName.toLowerCase();
907         },
908
909         empty: function(){
910                 Garbage.trash(this.getElementsByTagName('*'));
911                 return this.setHTML('');
912         }
913
914 });
915
916 Element.fixStyle = function(property, result, element){
917         if ($chk(parseInt(result))) return result;
918         if (['height', 'width'].contains(property)){
919                 var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'];
920                 var size = 0;
921                 values.each(function(value){
922                         size += element.getStyle('border-' + value + '-width').toInt() + element.getStyle('padding-' + value).toInt();
923                 });
924                 return element['offset' + property.capitalize()] - size + 'px';
925         } else if (property.test(/border(.+)Width|margin|padding/)){
926                 return '0px';
927         }
928         return result;
929 };
930
931 Element.Styles = {'border': [], 'padding': [], 'margin': []};
932 ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
933         for (var style in Element.Styles) Element.Styles[style].push(style + direction);
934 });
935
936 Element.borderShort = ['borderWidth', 'borderStyle', 'borderColor'];
937
938 Element.getMany = function(el, method, keys){
939         var result = {};
940         $each(keys, function(key){
941                 result[key] = el[method](key);
942         });
943         return result;
944 };
945
946 Element.setMany = function(el, method, pairs){
947         for (var key in pairs) el[method](key, pairs[key]);
948         return el;
949 };
950
951 Element.Properties = new Abstract({
952         'class': 'className', 'for': 'htmlFor', 'colspan': 'colSpan', 'rowspan': 'rowSpan',
953         'accesskey': 'accessKey', 'tabindex': 'tabIndex', 'maxlength': 'maxLength',
954         'readonly': 'readOnly', 'frameborder': 'frameBorder', 'value': 'value',
955         'disabled': 'disabled', 'checked': 'checked', 'multiple': 'multiple', 'selected': 'selected'
956 });
957 Element.PropertiesIFlag = {
958         'href': 2, 'src': 2
959 };
960
961 Element.Methods = {
962         Listeners: {
963                 addListener: function(type, fn){
964                         if (this.addEventListener) this.addEventListener(type, fn, false);
965                         else this.attachEvent('on' + type, fn);
966                         return this;
967                 },
968
969                 removeListener: function(type, fn){
970                         if (this.removeEventListener) this.removeEventListener(type, fn, false);
971                         else this.detachEvent('on' + type, fn);
972                         return this;
973                 }
974         }
975 };
976
977 window.extend(Element.Methods.Listeners);
978 document.extend(Element.Methods.Listeners);
979 Element.extend(Element.Methods.Listeners);
980
981 var Garbage = {
982
983         elements: [],
984
985         collect: function(el){
986                 if (!el.$tmp){
987                         Garbage.elements.push(el);
988                         el.$tmp = {'opacity': 1};
989                 }
990                 return el;
991         },
992
993         trash: function(elements){
994                 for (var i = 0, j = elements.length, el; i < j; i++){
995                         if (!(el = elements[i]) || !el.$tmp) continue;
996                         if (el.$events) el.fireEvent('trash').removeEvents();
997                         for (var p in el.$tmp) el.$tmp[p] = null;
998                         for (var d in Element.prototype) el[d] = null;
999                         Garbage.elements[Garbage.elements.indexOf(el)] = null;
1000                         el.htmlElement = el.$tmp = el = null;
1001                 }
1002                 Garbage.elements.remove(null);
1003         },
1004
1005         empty: function(){
1006                 Garbage.collect(window);
1007                 Garbage.collect(document);
1008                 Garbage.trash(Garbage.elements);
1009         }
1010
1011 };
1012
1013 window.addListener('beforeunload', function(){
1014         window.addListener('unload', Garbage.empty);
1015         if (window.ie) window.addListener('unload', CollectGarbage);
1016 });
1017
1018 var Event = new Class({
1019
1020         initialize: function(event){
1021                 if (event && event.$extended) return event;
1022                 this.$extended = true;
1023                 event = event || window.event;
1024                 this.event = event;
1025                 this.type = event.type;
1026                 this.target = event.target || event.srcElement;
1027                 if (this.target.nodeType == 3) this.target = this.target.parentNode;
1028                 this.shift = event.shiftKey;
1029                 this.control = event.ctrlKey;
1030                 this.alt = event.altKey;
1031                 this.meta = event.metaKey;
1032                 if (['DOMMouseScroll', 'mousewheel'].contains(this.type)){
1033                         this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
1034                 } else if (this.type.contains('key')){
1035                         this.code = event.which || event.keyCode;
1036                         for (var name in Event.keys){
1037                                 if (Event.keys[name] == this.code){
1038                                         this.key = name;
1039                                         break;
1040                                 }
1041                         }
1042                         if (this.type == 'keydown'){
1043                                 var fKey = this.code - 111;
1044                                 if (fKey > 0 && fKey < 13) this.key = 'f' + fKey;
1045                         }
1046                         this.key = this.key || String.fromCharCode(this.code).toLowerCase();
1047                 } else if (this.type.test(/(click|mouse|menu)/)){
1048                         this.page = {
1049                                 'x': event.pageX || event.clientX + document.documentElement.scrollLeft,
1050                                 'y': event.pageY || event.clientY + document.documentElement.scrollTop
1051                         };
1052                         this.client = {
1053                                 'x': event.pageX ? event.pageX - window.pageXOffset : event.clientX,
1054                                 'y': event.pageY ? event.pageY - window.pageYOffset : event.clientY
1055                         };
1056                         this.rightClick = (event.which == 3) || (event.button == 2);
1057                         switch(this.type){
1058                                 case 'mouseover': this.relatedTarget = event.relatedTarget || event.fromElement; break;
1059                                 case 'mouseout': this.relatedTarget = event.relatedTarget || event.toElement;
1060                         }
1061                         this.fixRelatedTarget();
1062                 }
1063                 return this;
1064         },
1065
1066         stop: function(){
1067                 return this.stopPropagation().preventDefault();
1068         },
1069
1070         stopPropagation: function(){
1071                 if (this.event.stopPropagation) this.event.stopPropagation();
1072                 else this.event.cancelBubble = true;
1073                 return this;
1074         },
1075
1076         preventDefault: function(){
1077                 if (this.event.preventDefault) this.event.preventDefault();
1078                 else this.event.returnValue = false;
1079                 return this;
1080         }
1081
1082 });
1083
1084 Event.fix = {
1085
1086         relatedTarget: function(){
1087                 if (this.relatedTarget && this.relatedTarget.nodeType == 3) this.relatedTarget = this.relatedTarget.parentNode;
1088         },
1089
1090         relatedTargetGecko: function(){
1091                 try {Event.fix.relatedTarget.call(this);} catch(e){this.relatedTarget = this.target;}
1092         }
1093
1094 };
1095
1096 Event.prototype.fixRelatedTarget = (window.gecko) ? Event.fix.relatedTargetGecko : Event.fix.relatedTarget;
1097
1098 Event.keys = new Abstract({
1099         'enter': 13,
1100         'up': 38,
1101         'down': 40,
1102         'left': 37,
1103         'right': 39,
1104         'esc': 27,
1105         'space': 32,
1106         'backspace': 8,
1107         'tab': 9,
1108         'delete': 46
1109 });
1110
1111 Element.Methods.Events = {
1112
1113         addEvent: function(type, fn){
1114                 this.$events = this.$events || {};
1115                 this.$events[type] = this.$events[type] || {'keys': [], 'values': []};
1116                 if (this.$events[type].keys.contains(fn)) return this;
1117                 this.$events[type].keys.push(fn);
1118                 var realType = type;
1119                 var custom = Element.Events[type];
1120                 if (custom){
1121                         if (custom.add) custom.add.call(this, fn);
1122                         if (custom.map) fn = custom.map;
1123                         if (custom.type) realType = custom.type;
1124                 }
1125                 if (!this.addEventListener) fn = fn.create({'bind': this, 'event': true});
1126                 this.$events[type].values.push(fn);
1127                 return (Element.NativeEvents.contains(realType)) ? this.addListener(realType, fn) : this;
1128         },
1129
1130         removeEvent: function(type, fn){
1131                 if (!this.$events || !this.$events[type]) return this;
1132                 var pos = this.$events[type].keys.indexOf(fn);
1133                 if (pos == -1) return this;
1134                 var key = this.$events[type].keys.splice(pos,1)[0];
1135                 var value = this.$events[type].values.splice(pos,1)[0];
1136                 var custom = Element.Events[type];
1137                 if (custom){
1138                         if (custom.remove) custom.remove.call(this, fn);
1139                         if (custom.type) type = custom.type;
1140                 }
1141                 return (Element.NativeEvents.contains(type)) ? this.removeListener(type, value) : this;
1142         },
1143
1144         addEvents: function(source){
1145                 return Element.setMany(this, 'addEvent', source);
1146         },
1147
1148         removeEvents: function(type){
1149                 if (!this.$events) return this;
1150                 if (!type){
1151                         for (var evType in this.$events) this.removeEvents(evType);
1152                         this.$events = null;
1153                 } else if (this.$events[type]){
1154                         this.$events[type].keys.each(function(fn){
1155                                 this.removeEvent(type, fn);
1156                         }, this);
1157                         this.$events[type] = null;
1158                 }
1159                 return this;
1160         },
1161
1162         fireEvent: function(type, args, delay){
1163                 if (this.$events && this.$events[type]){
1164                         this.$events[type].keys.each(function(fn){
1165                                 fn.create({'bind': this, 'delay': delay, 'arguments': args})();
1166                         }, this);
1167                 }
1168                 return this;
1169         },
1170
1171         cloneEvents: function(from, type){
1172                 if (!from.$events) return this;
1173                 if (!type){
1174                         for (var evType in from.$events) this.cloneEvents(from, evType);
1175                 } else if (from.$events[type]){
1176                         from.$events[type].keys.each(function(fn){
1177                                 this.addEvent(type, fn);
1178                         }, this);
1179                 }
1180                 return this;
1181         }
1182
1183 };
1184
1185 window.extend(Element.Methods.Events);
1186 document.extend(Element.Methods.Events);
1187 Element.extend(Element.Methods.Events);
1188
1189 Element.Events = new Abstract({
1190
1191         'mouseenter': {
1192                 type: 'mouseover',
1193                 map: function(event){
1194                         event = new Event(event);
1195                         if (event.relatedTarget != this && !this.hasChild(event.relatedTarget)) this.fireEvent('mouseenter', event);
1196                 }
1197         },
1198
1199         'mouseleave': {
1200                 type: 'mouseout',
1201                 map: function(event){
1202                         event = new Event(event);
1203                         if (event.relatedTarget != this && !this.hasChild(event.relatedTarget)) this.fireEvent('mouseleave', event);
1204                 }
1205         },
1206
1207         'mousewheel': {
1208                 type: (window.gecko) ? 'DOMMouseScroll' : 'mousewheel'
1209         }
1210
1211 });
1212
1213 Element.NativeEvents = [
1214         'click', 'dblclick', 'mouseup', 'mousedown',
1215         'mousewheel', 'DOMMouseScroll',
1216         'mouseover', 'mouseout', 'mousemove',
1217         'keydown', 'keypress', 'keyup',
1218         'load', 'unload', 'beforeunload', 'resize', 'move',
1219         'focus', 'blur', 'change', 'submit', 'reset', 'select',
1220         'error', 'abort', 'contextmenu', 'scroll'
1221 ];
1222
1223 Function.extend({
1224
1225         bindWithEvent: function(bind, args){
1226                 return this.create({'bind': bind, 'arguments': args, 'event': Event});
1227         }
1228
1229 });
1230
1231 Elements.extend({
1232
1233         filterByTag: function(tag){
1234                 return new Elements(this.filter(function(el){
1235                         return (Element.getTag(el) == tag);
1236                 }));
1237         },
1238
1239         filterByClass: function(className, nocash){
1240                 var elements = this.filter(function(el){
1241                         return (el.className && el.className.contains(className, ' '));
1242                 });
1243                 return (nocash) ? elements : new Elements(elements);
1244         },
1245
1246         filterById: function(id, nocash){
1247                 var elements = this.filter(function(el){
1248                         return (el.id == id);
1249                 });
1250                 return (nocash) ? elements : new Elements(elements);
1251         },
1252
1253         filterByAttribute: function(name, operator, value, nocash){
1254                 var elements = this.filter(function(el){
1255                         var current = Element.getProperty(el, name);
1256                         if (!current) return false;
1257                         if (!operator) return true;
1258                         switch(operator){
1259                                 case '=': return (current == value);
1260                                 case '*=': return (current.contains(value));
1261                                 case '^=': return (current.substr(0, value.length) == value);
1262                                 case '$=': return (current.substr(current.length - value.length) == value);
1263                                 case '!=': return (current != value);
1264                                 case '~=': return current.contains(value, ' ');
1265                         }
1266                         return false;
1267                 });
1268                 return (nocash) ? elements : new Elements(elements);
1269         }
1270
1271 });
1272
1273 function $E(selector, filter){
1274         return ($(filter) || document).getElement(selector);
1275 };
1276
1277 function $ES(selector, filter){
1278         return ($(filter) || document).getElementsBySelector(selector);
1279 };
1280
1281 $$.shared = {
1282
1283         'regexp': /^(\w*|\*)(?:#([\w-]+)|\.([\w-]+))?(?:\[(\w+)(?:([!*^$]?=)["']?([^"'\]]*)["']?)?])?$/,
1284
1285         'xpath': {
1286
1287                 getParam: function(items, context, param, i){
1288                         var temp = [context.namespaceURI ? 'xhtml:' : '', param[1]];
1289                         if (param[2]) temp.push('[@id="', param[2], '"]');
1290                         if (param[3]) temp.push('[contains(concat(" ", @class, " "), " ', param[3], ' ")]');
1291                         if (param[4]){
1292                                 if (param[5] && param[6]){
1293                                         switch(param[5]){
1294                                                 case '*=': temp.push('[contains(@', param[4], ', "', param[6], '")]'); break;
1295                                                 case '^=': temp.push('[starts-with(@', param[4], ', "', param[6], '")]'); break;
1296                                                 case '$=': temp.push('[substring(@', param[4], ', string-length(@', param[4], ') - ', param[6].length, ' + 1) = "', param[6], '"]'); break;
1297                                                 case '=': temp.push('[@', param[4], '="', param[6], '"]'); break;
1298                                                 case '!=': temp.push('[@', param[4], '!="', param[6], '"]');
1299                                         }
1300                                 } else {
1301                                         temp.push('[@', param[4], ']');
1302                                 }
1303                         }
1304                         items.push(temp.join(''));
1305                         return items;
1306                 },
1307
1308                 getItems: function(items, context, nocash){
1309                         var elements = [];
1310                         var xpath = document.evaluate('.//' + items.join('//'), context, $$.shared.resolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
1311                         for (var i = 0, j = xpath.snapshotLength; i < j; i++) elements.push(xpath.snapshotItem(i));
1312                         return (nocash) ? elements : new Elements(elements.map($));
1313                 }
1314
1315         },
1316
1317         'normal': {
1318
1319                 getParam: function(items, context, param, i){
1320                         if (i == 0){
1321                                 if (param[2]){
1322                                         var el = context.getElementById(param[2]);
1323                                         if (!el || ((param[1] != '*') && (Element.getTag(el) != param[1]))) return false;
1324                                         items = [el];
1325                                 } else {
1326                                         items = $A(context.getElementsByTagName(param[1]));
1327                                 }
1328                         } else {
1329                                 items = $$.shared.getElementsByTagName(items, param[1]);
1330                                 if (param[2]) items = Elements.filterById(items, param[2], true);
1331                         }
1332                         if (param[3]) items = Elements.filterByClass(items, param[3], true);
1333                         if (param[4]) items = Elements.filterByAttribute(items, param[4], param[5], param[6], true);
1334                         return items;
1335                 },
1336
1337                 getItems: function(items, context, nocash){
1338                         return (nocash) ? items : $$.unique(items);
1339                 }
1340
1341         },
1342
1343         resolver: function(prefix){
1344                 return (prefix == 'xhtml') ? 'http://www.w3.org/1999/xhtml' : false;
1345         },
1346
1347         getElementsByTagName: function(context, tagName){
1348                 var found = [];
1349                 for (var i = 0, j = context.length; i < j; i++) found.extend(context[i].getElementsByTagName(tagName));
1350                 return found;
1351         }
1352
1353 };
1354
1355 $$.shared.method = (window.xpath) ? 'xpath' : 'normal';
1356
1357 Element.Methods.Dom = {
1358
1359         getElements: function(selector, nocash){
1360                 var items = [];
1361                 selector = selector.trim().split(' ');
1362                 for (var i = 0, j = selector.length; i < j; i++){
1363                         var sel = selector[i];
1364                         var param = sel.match($$.shared.regexp);
1365                         if (!param) break;
1366                         param[1] = param[1] || '*';
1367                         var temp = $$.shared[$$.shared.method].getParam(items, this, param, i);
1368                         if (!temp) break;
1369                         items = temp;
1370                 }
1371                 return $$.shared[$$.shared.method].getItems(items, this, nocash);
1372         },
1373
1374         getElement: function(selector){
1375                 return $(this.getElements(selector, true)[0] || false);
1376         },
1377
1378         getElementsBySelector: function(selector, nocash){
1379                 var elements = [];
1380                 selector = selector.split(',');
1381                 for (var i = 0, j = selector.length; i < j; i++) elements = elements.concat(this.getElements(selector[i], true));
1382                 return (nocash) ? elements : $$.unique(elements);
1383         }
1384
1385 };
1386
1387 Element.extend({
1388
1389         getElementById: function(id){
1390                 var el = document.getElementById(id);
1391                 if (!el) return false;
1392                 for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
1393                         if (!parent) return false;
1394                 }
1395                 return el;
1396         }/*compatibility*/,
1397
1398         getElementsByClassName: function(className){ 
1399                 return this.getElements('.' + className); 
1400         }
1401
1402 });
1403
1404 document.extend(Element.Methods.Dom);
1405 Element.extend(Element.Methods.Dom);
1406
1407 Element.extend({
1408
1409         getValue: function(){
1410                 switch(this.getTag()){
1411                         case 'select':
1412                                 var values = [];
1413                                 $each(this.options, function(option){
1414                                         if (option.selected) values.push($pick(option.value, option.text));
1415                                 });
1416                                 return (this.multiple) ? values : values[0];
1417                         case 'input': if (!(this.checked && ['checkbox', 'radio'].contains(this.type)) && !['hidden', 'text', 'password'].contains(this.type)) break;
1418                         case 'textarea': return this.value;
1419                 }
1420                 return false;
1421         },
1422
1423         getFormElements: function(){
1424                 return $$(this.getElementsByTagName('input'), this.getElementsByTagName('select'), this.getElementsByTagName('textarea'));
1425         },
1426
1427         toQueryString: function(){
1428                 var queryString = [];
1429                 this.getFormElements().each(function(el){
1430                         var name = el.name;
1431                         var value = el.getValue();
1432                         if (value === false || !name || el.disabled) return;
1433                         var qs = function(val){
1434                                 queryString.push(name + '=' + encodeURIComponent(val));
1435                         };
1436                         if ($type(value) == 'array') value.each(qs);
1437                         else qs(value);
1438                 });
1439                 return queryString.join('&');
1440         }
1441
1442 });
1443
1444 Element.extend({
1445
1446         scrollTo: function(x, y){
1447                 this.scrollLeft = x;
1448                 this.scrollTop = y;
1449         },
1450
1451         getSize: function(){
1452                 return {
1453                         'scroll': {'x': this.scrollLeft, 'y': this.scrollTop},
1454                         'size': {'x': this.offsetWidth, 'y': this.offsetHeight},
1455                         'scrollSize': {'x': this.scrollWidth, 'y': this.scrollHeight}
1456                 };
1457         },
1458
1459         getPosition: function(overflown){
1460                 overflown = overflown || [];
1461                 var el = this, left = 0, top = 0;
1462                 do {
1463                         left += el.offsetLeft || 0;
1464                         top += el.offsetTop || 0;
1465                         el = el.offsetParent;
1466                 } while (el);
1467                 overflown.each(function(element){
1468                         left -= element.scrollLeft || 0;
1469                         top -= element.scrollTop || 0;
1470                 });
1471                 return {'x': left, 'y': top};
1472         },
1473
1474         getTop: function(overflown){
1475                 return this.getPosition(overflown).y;
1476         },
1477
1478         getLeft: function(overflown){
1479                 return this.getPosition(overflown).x;
1480         },
1481
1482         getCoordinates: function(overflown){
1483                 var position = this.getPosition(overflown);
1484                 var obj = {
1485                         'width': this.offsetWidth,
1486                         'height': this.offsetHeight,
1487                         'left': position.x,
1488                         'top': position.y
1489                 };
1490                 obj.right = obj.left + obj.width;
1491                 obj.bottom = obj.top + obj.height;
1492                 return obj;
1493         }
1494
1495 });
1496
1497 Element.Events.domready = {
1498
1499         add: function(fn){
1500                 if (window.loaded){
1501                         fn.call(this);
1502                         return;
1503                 }
1504                 var domReady = function(){
1505                         if (window.loaded) return;
1506                         window.loaded = true;
1507                         window.timer = $clear(window.timer);
1508                         this.fireEvent('domready');
1509                 }.bind(this);
1510                 if (document.readyState && window.webkit){
1511                         window.timer = function(){
1512                                 if (['loaded','complete'].contains(document.readyState)) domReady();
1513                         }.periodical(50);
1514                 } else if (document.readyState && window.ie){
1515                         if (!$('ie_ready')){
1516                                 var src = (window.location.protocol == 'https:') ? '://0' : 'javascript:void(0)';
1517                                 document.write('<script id="ie_ready" defer src="' + src + '"><\/script>');
1518                                 $('ie_ready').onreadystatechange = function(){
1519                                         if (this.readyState == 'complete') domReady();
1520                                 };
1521                         }
1522                 } else {
1523                         window.addListener("load", domReady);
1524                         document.addListener("DOMContentLoaded", domReady);
1525                 }
1526         }
1527
1528 };
1529
1530 window.onDomReady = function(fn){ 
1531         return this.addEvent('domready', fn); 
1532 };
1533
1534 window.extend({
1535
1536         getWidth: function(){
1537                 if (this.webkit419) return this.innerWidth;
1538                 if (this.opera) return document.body.clientWidth;
1539                 return document.documentElement.clientWidth;
1540         },
1541
1542         getHeight: function(){
1543                 if (this.webkit419) return this.innerHeight;
1544                 if (this.opera) return document.body.clientHeight;
1545                 return document.documentElement.clientHeight;
1546         },
1547
1548         getScrollWidth: function(){
1549                 if (this.ie) return Math.max(document.documentElement.offsetWidth, document.documentElement.scrollWidth);
1550                 if (this.webkit) return document.body.scrollWidth;
1551                 return document.documentElement.scrollWidth;
1552         },
1553
1554         getScrollHeight: function(){
1555                 if (this.ie) return Math.max(document.documentElement.offsetHeight, document.documentElement.scrollHeight);
1556                 if (this.webkit) return document.body.scrollHeight;
1557                 return document.documentElement.scrollHeight;
1558         },
1559
1560         getScrollLeft: function(){
1561                 return this.pageXOffset || document.documentElement.scrollLeft;
1562         },
1563
1564         getScrollTop: function(){
1565                 return this.pageYOffset || document.documentElement.scrollTop;
1566         },
1567
1568         getSize: function(){
1569                 return {
1570                         'size': {'x': this.getWidth(), 'y': this.getHeight()},
1571                         'scrollSize': {'x': this.getScrollWidth(), 'y': this.getScrollHeight()},
1572                         'scroll': {'x': this.getScrollLeft(), 'y': this.getScrollTop()}
1573                 };
1574         },
1575         getPosition: function(){return {'x': 0, 'y': 0};}
1576
1577 });