Rename trunk directory to public_html
[ldapsaisie.git] / public_html / includes / js / mootools-more.js
1 //MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2008 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
2
3 /*\r
4 Script: Fx.Slide.js\r
5         Effect to slide an element in and out of view.\r
6 \r
7 License:\r
8         MIT-style license.\r
9 */\r
10 \r
11 Fx.Slide = new Class({\r
12 \r
13         Extends: Fx,\r
14 \r
15         options: {\r
16                 mode: 'vertical'\r
17         },\r
18 \r
19         initialize: function(element, options){\r
20                 this.addEvent('complete', function(){\r
21                         this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);\r
22                         if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);\r
23                 }, true);\r
24                 this.element = this.subject = $(element);\r
25                 this.parent(options);\r
26                 var wrapper = this.element.retrieve('wrapper');\r
27                 this.wrapper = wrapper || new Element('div', {\r
28                         styles: $extend(this.element.getStyles('margin', 'position'), {'overflow': 'hidden'})\r
29                 }).wraps(this.element);\r
30                 this.element.store('wrapper', this.wrapper).setStyle('margin', 0);\r
31                 this.now = [];\r
32                 this.open = true;\r
33         },\r
34 \r
35         vertical: function(){\r
36                 this.margin = 'margin-top';\r
37                 this.layout = 'height';\r
38                 this.offset = this.element.offsetHeight;\r
39         },\r
40 \r
41         horizontal: function(){\r
42                 this.margin = 'margin-left';\r
43                 this.layout = 'width';\r
44                 this.offset = this.element.offsetWidth;\r
45         },\r
46 \r
47         set: function(now){\r
48                 this.element.setStyle(this.margin, now[0]);\r
49                 this.wrapper.setStyle(this.layout, now[1]);\r
50                 return this;\r
51         },\r
52 \r
53         compute: function(from, to, delta){\r
54                 var now = [];\r
55                 var x = 2;\r
56                 x.times(function(i){\r
57                         now[i] = Fx.compute(from[i], to[i], delta);\r
58                 });\r
59                 return now;\r
60         },\r
61 \r
62         start: function(how, mode){\r
63                 if (!this.check(arguments.callee, how, mode)) return this;\r
64                 this[mode || this.options.mode]();\r
65                 var margin = this.element.getStyle(this.margin).toInt();\r
66                 var layout = this.wrapper.getStyle(this.layout).toInt();\r
67                 var caseIn = [[margin, layout], [0, this.offset]];\r
68                 var caseOut = [[margin, layout], [-this.offset, 0]];\r
69                 var start;\r
70                 switch (how){\r
71                         case 'in': start = caseIn; break;\r
72                         case 'out': start = caseOut; break;\r
73                         case 'toggle': start = (this.wrapper['offset' + this.layout.capitalize()] == 0) ? caseIn : caseOut;\r
74                 }\r
75                 return this.parent(start[0], start[1]);\r
76         },\r
77 \r
78         slideIn: function(mode){\r
79                 return this.start('in', mode);\r
80         },\r
81 \r
82         slideOut: function(mode){\r
83                 return this.start('out', mode);\r
84         },\r
85 \r
86         hide: function(mode){\r
87                 this[mode || this.options.mode]();\r
88                 this.open = false;\r
89                 return this.set([-this.offset, 0]);\r
90         },\r
91 \r
92         show: function(mode){\r
93                 this[mode || this.options.mode]();\r
94                 this.open = true;\r
95                 return this.set([0, this.offset]);\r
96         },\r
97 \r
98         toggle: function(mode){\r
99                 return this.start('toggle', mode);\r
100         }\r
101 \r
102 });\r
103 \r
104 Element.Properties.slide = {\r
105 \r
106         set: function(options){\r
107                 var slide = this.retrieve('slide');\r
108                 if (slide) slide.cancel();\r
109                 return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));\r
110         },\r
111         \r
112         get: function(options){\r
113                 if (options || !this.retrieve('slide')){\r
114                         if (options || !this.retrieve('slide:options')) this.set('slide', options);\r
115                         this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));\r
116                 }\r
117                 return this.retrieve('slide');\r
118         }\r
119 \r
120 };\r
121 \r
122 Element.implement({\r
123 \r
124         slide: function(how, mode){\r
125                 how = how || 'toggle';\r
126                 var slide = this.get('slide'), toggle;\r
127                 switch (how){\r
128                         case 'hide': slide.hide(mode); break;\r
129                         case 'show': slide.show(mode); break;\r
130                         case 'toggle':\r
131                                 var flag = this.retrieve('slide:flag', slide.open);\r
132                                 slide[(flag) ? 'slideOut' : 'slideIn'](mode);\r
133                                 this.store('slide:flag', !flag);\r
134                                 toggle = true;\r
135                         break;\r
136                         default: slide.start(how, mode);\r
137                 }\r
138                 if (!toggle) this.eliminate('slide:flag');\r
139                 return this;\r
140         }\r
141 \r
142 });\r
143
144
145 /*
146 Script: Fx.Scroll.js
147         Effect to smoothly scroll any element, including the window.
148
149 License:
150         MIT-style license.
151 */
152
153 Fx.Scroll = new Class({
154
155         Extends: Fx,
156
157         options: {
158                 offset: {'x': 0, 'y': 0},
159                 wheelStops: true
160         },
161
162         initialize: function(element, options){
163                 this.element = this.subject = $(element);
164                 this.parent(options);
165                 var cancel = this.cancel.bind(this, false);
166
167                 if ($type(this.element) != 'element') this.element = $(this.element.getDocument().body);
168
169                 var stopper = this.element;
170
171                 if (this.options.wheelStops){
172                         this.addEvent('start', function(){
173                                 stopper.addEvent('mousewheel', cancel);
174                         }, true);
175                         this.addEvent('complete', function(){
176                                 stopper.removeEvent('mousewheel', cancel);
177                         }, true);
178                 }
179         },
180
181         set: function(){
182                 var now = Array.flatten(arguments);
183                 this.element.scrollTo(now[0], now[1]);
184         },
185
186         compute: function(from, to, delta){
187                 var now = [];
188                 var x = 2;
189                 x.times(function(i){
190                         now.push(Fx.compute(from[i], to[i], delta));
191                 });
192                 return now;
193         },
194
195         start: function(x, y){
196                 if (!this.check(arguments.callee, x, y)) return this;
197                 var offsetSize = this.element.getSize(), scrollSize = this.element.getScrollSize();
198                 var scroll = this.element.getScroll(), values = {x: x, y: y};
199                 for (var z in values){
200                         var max = scrollSize[z] - offsetSize[z];
201                         if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z].limit(0, max) : max;
202                         else values[z] = scroll[z];
203                         values[z] += this.options.offset[z];
204                 }
205                 return this.parent([scroll.x, scroll.y], [values.x, values.y]);
206         },
207
208         toTop: function(){
209                 return this.start(false, 0);
210         },
211
212         toLeft: function(){
213                 return this.start(0, false);
214         },
215
216         toRight: function(){
217                 return this.start('right', false);
218         },
219
220         toBottom: function(){
221                 return this.start(false, 'bottom');
222         },
223
224         toElement: function(el){
225                 var position = $(el).getPosition(this.element);
226                 return this.start(position.x, position.y);
227         }
228
229 });
230
231
232 /*
233 Script: Fx.Elements.js
234         Effect to change any number of CSS properties of any number of Elements.
235
236 License:
237         MIT-style license.
238 */
239
240 Fx.Elements = new Class({
241
242         Extends: Fx.CSS,
243
244         initialize: function(elements, options){
245                 this.elements = this.subject = $$(elements);
246                 this.parent(options);
247         },
248
249         compute: function(from, to, delta){
250                 var now = {};
251                 for (var i in from){
252                         var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
253                         for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
254                 }
255                 return now;
256         },
257
258         set: function(now){
259                 for (var i in now){
260                         var iNow = now[i];
261                         for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
262                 }
263                 return this;
264         },
265
266         start: function(obj){
267                 if (!this.check(arguments.callee, obj)) return this;
268                 var from = {}, to = {};
269                 for (var i in obj){
270                         var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
271                         for (var p in iProps){
272                                 var parsed = this.prepare(this.elements[i], p, iProps[p]);
273                                 iFrom[p] = parsed.from;
274                                 iTo[p] = parsed.to;
275                         }
276                 }
277                 return this.parent(from, to);
278         }
279
280 });
281
282 /*
283 Script: Drag.js
284         The base Drag Class. Can be used to drag and resize Elements using mouse events.
285
286 License:
287         MIT-style license.
288 */
289
290 var Drag = new Class({
291
292         Implements: [Events, Options],
293
294         options: {/*
295                 onBeforeStart: $empty,
296                 onStart: $empty,
297                 onDrag: $empty,
298                 onCancel: $empty,
299                 onComplete: $empty,*/
300                 snap: 6,
301                 unit: 'px',
302                 grid: false,
303                 style: true,
304                 limit: false,
305                 handle: false,
306                 invert: false,
307                 preventDefault: false,
308                 modifiers: {x: 'left', y: 'top'}
309         },
310
311         initialize: function(){
312                 var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
313                 this.element = $(params.element);
314                 this.document = this.element.getDocument();
315                 this.setOptions(params.options || {});
316                 var htype = $type(this.options.handle);
317                 this.handles = (htype == 'array' || htype == 'collection') ? $$(this.options.handle) : $(this.options.handle) || this.element;
318                 this.mouse = {'now': {}, 'pos': {}};
319                 this.value = {'start': {}, 'now': {}};
320                 
321                 this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
322                 
323                 this.bound = {
324                         start: this.start.bind(this),
325                         check: this.check.bind(this),
326                         drag: this.drag.bind(this),
327                         stop: this.stop.bind(this),
328                         cancel: this.cancel.bind(this),
329                         eventStop: $lambda(false)
330                 };
331                 this.attach();
332         },
333
334         attach: function(){
335                 this.handles.addEvent('mousedown', this.bound.start);
336                 return this;
337         },
338
339         detach: function(){
340                 this.handles.removeEvent('mousedown', this.bound.start);
341                 return this;
342         },
343
344         start: function(event){
345                 if (this.options.preventDefault) event.preventDefault();
346                 this.fireEvent('beforeStart', this.element);
347                 this.mouse.start = event.page;
348                 var limit = this.options.limit;
349                 this.limit = {'x': [], 'y': []};
350                 for (var z in this.options.modifiers){
351                         if (!this.options.modifiers[z]) continue;
352                         if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
353                         else this.value.now[z] = this.element[this.options.modifiers[z]];
354                         if (this.options.invert) this.value.now[z] *= -1;
355                         this.mouse.pos[z] = event.page[z] - this.value.now[z];
356                         if (limit && limit[z]){
357                                 for (var i = 2; i--; i){
358                                         if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
359                                 }
360                         }
361                 }
362                 if ($type(this.options.grid) == 'number') this.options.grid = {'x': this.options.grid, 'y': this.options.grid};
363                 this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
364                 this.document.addEvent(this.selection, this.bound.eventStop);
365         },
366
367         check: function(event){
368                 if (this.options.preventDefault) event.preventDefault();
369                 var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
370                 if (distance > this.options.snap){
371                         this.cancel();
372                         this.document.addEvents({
373                                 mousemove: this.bound.drag,
374                                 mouseup: this.bound.stop
375                         });
376                         this.fireEvent('start', this.element).fireEvent('snap', this.element);
377                 }
378         },
379
380         drag: function(event){
381                 if (this.options.preventDefault) event.preventDefault();
382                 this.mouse.now = event.page;
383                 for (var z in this.options.modifiers){
384                         if (!this.options.modifiers[z]) continue;
385                         this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
386                         if (this.options.invert) this.value.now[z] *= -1;
387                         if (this.options.limit && this.limit[z]){
388                                 if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
389                                         this.value.now[z] = this.limit[z][1];
390                                 } else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
391                                         this.value.now[z] = this.limit[z][0];
392                                 }
393                         }
394                         if (this.options.grid[z]) this.value.now[z] -= (this.value.now[z] % this.options.grid[z]);
395                         if (this.options.style) this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
396                         else this.element[this.options.modifiers[z]] = this.value.now[z];
397                 }
398                 this.fireEvent('drag', this.element);
399         },
400
401         cancel: function(event){
402                 this.document.removeEvent('mousemove', this.bound.check);
403                 this.document.removeEvent('mouseup', this.bound.cancel);
404                 if (event){
405                         this.document.removeEvent(this.selection, this.bound.eventStop);
406                         this.fireEvent('cancel', this.element);
407                 }
408         },
409
410         stop: function(event){
411                 this.document.removeEvent(this.selection, this.bound.eventStop);
412                 this.document.removeEvent('mousemove', this.bound.drag);
413                 this.document.removeEvent('mouseup', this.bound.stop);
414                 if (event) this.fireEvent('complete', this.element);
415         }
416
417 });
418
419 Element.implement({
420         
421         makeResizable: function(options){
422                 return new Drag(this, $merge({modifiers: {'x': 'width', 'y': 'height'}}, options));
423         }
424
425 });
426
427 /*
428 Script: Drag.Move.js
429         A Drag extension that provides support for the constraining of draggables to containers and droppables.
430
431 License:
432         MIT-style license.
433 */
434
435 Drag.Move = new Class({
436
437         Extends: Drag,
438
439         options: {
440                 droppables: [],
441                 container: false
442         },
443
444         initialize: function(element, options){
445                 this.parent(element, options);
446                 this.droppables = $$(this.options.droppables);
447                 this.container = $(this.options.container);
448                 if (this.container && $type(this.container) != 'element') this.container = $(this.container.getDocument().body);
449                 element = this.element;
450                 
451                 var current = element.getStyle('position');
452                 var position = (current != 'static') ? current : 'absolute';
453                 if (element.getStyle('left') == 'auto' || element.getStyle('top') == 'auto') element.position(element.getPosition(element.offsetParent));
454                 
455                 element.setStyle('position', position);
456                 
457                 this.addEvent('start', function(){
458                         this.checkDroppables();
459                 }, true);
460         },
461
462         start: function(event){
463                 if (this.container){
464                         var el = this.element, cont = this.container, ccoo = cont.getCoordinates(el.offsetParent), cps = {}, ems = {};
465
466                         ['top', 'right', 'bottom', 'left'].each(function(pad){
467                                 cps[pad] = cont.getStyle('padding-' + pad).toInt();
468                                 ems[pad] = el.getStyle('margin-' + pad).toInt();
469                         }, this);
470
471                         var width = el.offsetWidth + ems.left + ems.right, height = el.offsetHeight + ems.top + ems.bottom;
472                         var x = [ccoo.left + cps.left, ccoo.right - cps.right - width];
473                         var y = [ccoo.top + cps.top, ccoo.bottom - cps.bottom - height];
474
475                         this.options.limit = {x: x, y: y};
476                 }
477                 this.parent(event);
478         },
479
480         checkAgainst: function(el){
481                 el = el.getCoordinates();
482                 var now = this.mouse.now;
483                 return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
484         },
485
486         checkDroppables: function(){
487                 var overed = this.droppables.filter(this.checkAgainst, this).getLast();
488                 if (this.overed != overed){
489                         if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
490                         if (overed){
491                                 this.overed = overed;
492                                 this.fireEvent('enter', [this.element, overed]);
493                         } else {
494                                 this.overed = null;
495                         }
496                 }
497         },
498
499         drag: function(event){
500                 this.parent(event);
501                 if (this.droppables.length) this.checkDroppables();
502         },
503
504         stop: function(event){
505                 this.checkDroppables();
506                 this.fireEvent('drop', [this.element, this.overed]);
507                 this.overed = null;
508                 return this.parent(event);
509         }
510
511 });
512
513 Element.implement({
514
515         makeDraggable: function(options){
516                 return new Drag.Move(this, options);
517         }
518
519 });
520
521
522 /*\r
523 Script: Hash.Cookie.js\r
524         Class for creating, reading, and deleting Cookies in JSON format.\r
525 \r
526 License:\r
527         MIT-style license.\r
528 */\r
529 \r
530 Hash.Cookie = new Class({\r
531 \r
532         Extends: Cookie,\r
533 \r
534         options: {\r
535                 autoSave: true\r
536         },\r
537 \r
538         initialize: function(name, options){\r
539                 this.parent(name, options);\r
540                 this.load();\r
541         },\r
542 \r
543         save: function(){\r
544                 var value = JSON.encode(this.hash);\r
545                 if (!value || value.length > 4096) return false; //cookie would be truncated!\r
546                 if (value == '{}') this.dispose();\r
547                 else this.write(value);\r
548                 return true;\r
549         },\r
550 \r
551         load: function(){\r
552                 this.hash = new Hash(JSON.decode(this.read(), true));\r
553                 return this;\r
554         }\r
555 \r
556 });\r
557 \r
558 Hash.Cookie.implement((function(){\r
559         \r
560         var methods = {};\r
561         \r
562         Hash.each(Hash.prototype, function(method, name){\r
563                 methods[name] = function(){\r
564                         var value = method.apply(this.hash, arguments);\r
565                         if (this.options.autoSave) this.save();\r
566                         return value;\r
567                 };\r
568         });\r
569         \r
570         return methods;\r
571         \r
572 })());
573
574 /*
575 Script: Color.js
576         Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
577
578 License:
579         MIT-style license.
580 */
581
582 var Color = new Native({
583   
584         initialize: function(color, type){
585                 if (arguments.length >= 3){
586                         type = "rgb"; color = Array.slice(arguments, 0, 3);
587                 } else if (typeof color == 'string'){
588                         if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
589                         else if (color.match(/hsb/)) color = color.hsbToRgb();
590                         else color = color.hexToRgb(true);
591                 }
592                 type = type || 'rgb';
593                 switch (type){
594                         case 'hsb':
595                                 var old = color;
596                                 color = color.hsbToRgb();
597                                 color.hsb = old;
598                         break;
599                         case 'hex': color = color.hexToRgb(true); break;
600                 }
601                 color.rgb = color.slice(0, 3);
602                 color.hsb = color.hsb || color.rgbToHsb();
603                 color.hex = color.rgbToHex();
604                 return $extend(color, this);
605         }
606
607 });
608
609 Color.implement({
610
611         mix: function(){
612                 var colors = Array.slice(arguments);
613                 var alpha = ($type(colors.getLast()) == 'number') ? colors.pop() : 50;
614                 var rgb = this.slice();
615                 colors.each(function(color){
616                         color = new Color(color);
617                         for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
618                 });
619                 return new Color(rgb, 'rgb');
620         },
621
622         invert: function(){
623                 return new Color(this.map(function(value){
624                         return 255 - value;
625                 }));
626         },
627
628         setHue: function(value){
629                 return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
630         },
631
632         setSaturation: function(percent){
633                 return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
634         },
635
636         setBrightness: function(percent){
637                 return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
638         }
639
640 });
641
642 function $RGB(r, g, b){
643         return new Color([r, g, b], 'rgb');
644 };
645
646 function $HSB(h, s, b){
647         return new Color([h, s, b], 'hsb');
648 };
649
650 function $HEX(hex){
651         return new Color(hex, 'hex');
652 };
653
654 Array.implement({
655
656         rgbToHsb: function(){
657                 var red = this[0], green = this[1], blue = this[2];
658                 var hue, saturation, brightness;
659                 var max = Math.max(red, green, blue), min = Math.min(red, green, blue);
660                 var delta = max - min;
661                 brightness = max / 255;
662                 saturation = (max != 0) ? delta / max : 0;
663                 if (saturation == 0){
664                         hue = 0;
665                 } else {
666                         var rr = (max - red) / delta;
667                         var gr = (max - green) / delta;
668                         var br = (max - blue) / delta;
669                         if (red == max) hue = br - gr;
670                         else if (green == max) hue = 2 + rr - br;
671                         else hue = 4 + gr - rr;
672                         hue /= 6;
673                         if (hue < 0) hue++;
674                 }
675                 return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
676         },
677
678         hsbToRgb: function(){
679                 var br = Math.round(this[2] / 100 * 255);
680                 if (this[1] == 0){
681                         return [br, br, br];
682                 } else {
683                         var hue = this[0] % 360;
684                         var f = hue % 60;
685                         var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
686                         var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
687                         var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
688                         switch (Math.floor(hue / 60)){
689                                 case 0: return [br, t, p];
690                                 case 1: return [q, br, p];
691                                 case 2: return [p, br, t];
692                                 case 3: return [p, q, br];
693                                 case 4: return [t, p, br];
694                                 case 5: return [br, p, q];
695                         }
696                 }
697                 return false;
698         }
699
700 });
701
702 String.implement({
703
704         rgbToHsb: function(){
705                 var rgb = this.match(/\d{1,3}/g);
706                 return (rgb) ? hsb.rgbToHsb() : null;
707         },
708         
709         hsbToRgb: function(){
710                 var hsb = this.match(/\d{1,3}/g);
711                 return (hsb) ? hsb.hsbToRgb() : null;
712         }
713
714 });
715
716
717 /*
718 Script: Group.js
719         Class for monitoring collections of events
720
721 License:
722         MIT-style license.
723 */
724
725 var Group = new Class({
726
727         initialize: function(){
728                 this.instances = Array.flatten(arguments);
729                 this.events = {};
730                 this.checker = {};
731         },
732
733         addEvent: function(type, fn){
734                 this.checker[type] = this.checker[type] || {};
735                 this.events[type] = this.events[type] || [];
736                 if (this.events[type].contains(fn)) return false;
737                 else this.events[type].push(fn);
738                 this.instances.each(function(instance, i){
739                         instance.addEvent(type, this.check.bind(this, [type, instance, i]));
740                 }, this);
741                 return this;
742         },
743
744         check: function(type, instance, i){
745                 this.checker[type][i] = true;
746                 var every = this.instances.every(function(current, j){
747                         return this.checker[type][j] || false;
748                 }, this);
749                 if (!every) return;
750                 this.checker[type] = {};
751                 this.events[type].each(function(event){
752                         event.call(this, this.instances, instance);
753                 }, this);
754         }
755
756 });
757
758
759 /*
760 Script: Assets.js
761         Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
762
763 License:
764         MIT-style license.
765 */
766
767 var Asset = new Hash({
768
769         javascript: function(source, properties){
770                 properties = $extend({
771                         onload: $empty,
772                         document: document,
773                         check: $lambda(true)
774                 }, properties);
775                 
776                 var script = new Element('script', {'src': source, 'type': 'text/javascript'});
777                 
778                 var load = properties.onload.bind(script), check = properties.check, doc = properties.document;
779                 delete properties.onload; delete properties.check; delete properties.document;
780                 
781                 script.addEvents({
782                         load: load,
783                         readystatechange: function(){
784                                 if (['loaded', 'complete'].contains(this.readyState)) load();
785                         }
786                 }).setProperties(properties);
787                 
788                 
789                 if (Browser.Engine.webkit419) var checker = (function(){
790                         if (!$try(check)) return;
791                         $clear(checker);
792                         load();
793                 }).periodical(50);
794                 
795                 return script.inject(doc.head);
796         },
797
798         css: function(source, properties){
799                 return new Element('link', $merge({
800                         'rel': 'stylesheet', 'media': 'screen', 'type': 'text/css', 'href': source
801                 }, properties)).inject(document.head);
802         },
803
804         image: function(source, properties){
805                 properties = $merge({
806                         'onload': $empty,
807                         'onabort': $empty,
808                         'onerror': $empty
809                 }, properties);
810                 var image = new Image();
811                 var element = $(image) || new Element('img');
812                 ['load', 'abort', 'error'].each(function(name){
813                         var type = 'on' + name;
814                         var event = properties[type];
815                         delete properties[type];
816                         image[type] = function(){
817                                 if (!image) return;
818                                 if (!element.parentNode){
819                                         element.width = image.width;
820                                         element.height = image.height;
821                                 }
822                                 image = image.onload = image.onabort = image.onerror = null;
823                                 event.delay(1, element, element);
824                                 element.fireEvent(name, element, 1);
825                         };
826                 });
827                 image.src = element.src = source;
828                 if (image && image.complete) image.onload.delay(1);
829                 return element.setProperties(properties);
830         },
831
832         images: function(sources, options){
833                 options = $merge({
834                         onComplete: $empty,
835                         onProgress: $empty
836                 }, options);
837                 if (!sources.push) sources = [sources];
838                 var images = [];
839                 var counter = 0;
840                 sources.each(function(source){
841                         var img = new Asset.image(source, {
842                                 'onload': function(){
843                                         options.onProgress.call(this, counter, sources.indexOf(source));
844                                         counter++;
845                                         if (counter == sources.length) options.onComplete();
846                                 }
847                         });
848                         images.push(img);
849                 });
850                 return new Elements(images);
851         }
852
853 });
854
855 /*
856 Script: Sortables.js
857         Class for creating a drag and drop sorting interface for lists of items.
858
859 License:
860         MIT-style license.
861 */
862
863 var Sortables = new Class({
864
865         Implements: [Events, Options],
866
867         options: {/*
868                 onSort: $empty,
869                 onStart: $empty,
870                 onComplete: $empty,*/
871                 snap: 4,
872                 opacity: 1,
873                 clone: false,
874                 revert: false,
875                 handle: false,
876                 constrain: false
877         },
878
879         initialize: function(lists, options){
880                 this.setOptions(options);
881                 this.elements = [];
882                 this.lists = [];
883                 this.idle = true;
884                 
885                 this.addLists($$($(lists) || lists));
886                 if (!this.options.clone) this.options.revert = false;
887                 if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert));
888         },
889
890         attach: function(){
891                 this.addLists(this.lists);
892                 return this;
893         },
894
895         detach: function(){
896                 this.lists = this.removeLists(this.lists);
897                 return this;
898         },
899
900         addItems: function(){
901                 Array.flatten(arguments).each(function(element){
902                         this.elements.push(element);
903                         var start = element.retrieve('sortables:start', this.start.bindWithEvent(this, element));
904                         (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
905                 }, this);
906                 return this;
907         },
908
909         addLists: function(){
910                 Array.flatten(arguments).each(function(list){
911                         this.lists.push(list);
912                         this.addItems(list.getChildren());
913                 }, this);
914                 return this;
915         },
916
917         removeItems: function(){
918                 var elements = [];
919                 Array.flatten(arguments).each(function(element){
920                         elements.push(element);
921                         this.elements.erase(element);
922                         var start = element.retrieve('sortables:start');
923                         (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
924                 }, this);
925                 return $$(elements);
926         },
927
928         removeLists: function(){
929                 var lists = [];
930                 Array.flatten(arguments).each(function(list){
931                         lists.push(list);
932                         this.lists.erase(list);
933                         this.removeItems(list.getChildren());
934                 }, this);
935                 return $$(lists);
936         },
937
938         getClone: function(event, element){
939                 if (!this.options.clone) return new Element('div').inject(document.body);
940                 if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
941                 return element.clone(true).setStyles({
942                         'margin': '0px',
943                         'position': 'absolute',
944                         'visibility': 'hidden',
945                         'width': element.getStyle('width')
946                 }).inject(this.list).position(element.getPosition(element.getOffsetParent()));
947         },
948
949         getDroppables: function(){
950                 var droppables = this.list.getChildren();
951                 if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list);
952                 return droppables.erase(this.clone).erase(this.element);
953         },
954
955         insert: function(dragging, element){
956                 var where = 'inside';
957                 if (this.lists.contains(element)){
958                         this.list = element;
959                         this.drag.droppables = this.getDroppables();
960                 } else {
961                         where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
962                 }
963                 this.element.inject(element, where);
964                 this.fireEvent('sort', [this.element, this.clone]);
965         },
966
967         start: function(event, element){
968                 if (!this.idle) return;
969                 this.idle = false;
970                 this.element = element;
971                 this.opacity = element.get('opacity');
972                 this.list = element.getParent();
973                 this.clone = this.getClone(event, element);
974                 
975                 this.drag = new Drag.Move(this.clone, {
976                         snap: this.options.snap,
977                         container: this.options.constrain && this.element.getParent(),
978                         droppables: this.getDroppables(),
979                         onSnap: function(){
980                                 event.stop();
981                                 this.clone.setStyle('visibility', 'visible');
982                                 this.element.set('opacity', this.options.opacity || 0);
983                                 this.fireEvent('start', [this.element, this.clone]);
984                         }.bind(this),
985                         onEnter: this.insert.bind(this),
986                         onCancel: this.reset.bind(this),
987                         onComplete: this.end.bind(this)
988                 });
989                 
990                 this.clone.inject(this.element, 'before');
991                 this.drag.start(event);
992         },
993
994         end: function(){
995                 this.drag.detach();
996                 this.element.set('opacity', this.opacity);
997                 if (this.effect){
998                         var dim = this.element.getStyles('width', 'height');
999                         var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
1000                         this.effect.element = this.clone;
1001                         this.effect.start({
1002                                 top: pos.top,
1003                                 left: pos.left,
1004                                 width: dim.width,
1005                                 height: dim.height,
1006                                 opacity: 0.25
1007                         }).chain(this.reset.bind(this));
1008                 } else {
1009                         this.reset();
1010                 }
1011         },
1012
1013         reset: function(){
1014                 this.idle = true;
1015                 this.clone.destroy();
1016                 this.fireEvent('complete', this.element);
1017         },
1018
1019         serialize: function(){
1020                 var params = Array.link(arguments, {modifier: Function.type, index: $defined});
1021                 var serial = this.lists.map(function(list){
1022                         return list.getChildren().map(params.modifier || function(element){
1023                                 return element.get('id');
1024                         }, this);
1025                 }, this);
1026                 
1027                 var index = params.index;
1028                 if (this.lists.length == 1) index = 0;
1029                 return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial;
1030         }
1031
1032 });
1033
1034 /*\r
1035 Script: Tips.js\r
1036         Class for creating nice tips that follow the mouse cursor when hovering an element.\r
1037 \r
1038 License:\r
1039         MIT-style license.\r
1040 */\r
1041 \r
1042 var Tips = new Class({\r
1043 \r
1044         Implements: [Events, Options],\r
1045 \r
1046         options: {\r
1047                 onShow: function(tip){\r
1048                         tip.setStyle('visibility', 'visible');\r
1049                 },\r
1050                 onHide: function(tip){\r
1051                         tip.setStyle('visibility', 'hidden');\r
1052                 },\r
1053                 showDelay: 100,\r
1054                 hideDelay: 100,\r
1055                 className: null,\r
1056                 offsets: {x: 16, y: 16},\r
1057                 fixed: false\r
1058         },\r
1059 \r
1060         initialize: function(){\r
1061                 var params = Array.link(arguments, {options: Object.type, elements: $defined});\r
1062                 this.setOptions(params.options || null);\r
1063                 \r
1064                 this.tip = new Element('div').inject(document.body);\r
1065                 \r
1066                 if (this.options.className) this.tip.addClass(this.options.className);\r
1067                 \r
1068                 var top = new Element('div', {'class': 'tip-top'}).inject(this.tip);\r
1069                 this.container = new Element('div', {'class': 'tip'}).inject(this.tip);\r
1070                 var bottom = new Element('div', {'class': 'tip-bottom'}).inject(this.tip);\r
1071 \r
1072                 this.tip.setStyles({position: 'absolute', top: 0, left: 0, visibility: 'hidden'});\r
1073                 \r
1074                 if (params.elements) this.attach(params.elements);\r
1075         },\r
1076         \r
1077         attach: function(elements){\r
1078                 $$(elements).each(function(element){\r
1079                         var title = element.retrieve('tip:title', element.get('title'));\r
1080                         var text = element.retrieve('tip:text', element.get('rel') || element.get('href'));\r
1081                         var enter = element.retrieve('tip:enter', this.elementEnter.bindWithEvent(this, element));\r
1082                         var leave = element.retrieve('tip:leave', this.elementLeave.bindWithEvent(this, element));\r
1083                         element.addEvents({mouseenter: enter, mouseleave: leave});\r
1084                         if (!this.options.fixed){\r
1085                                 var move = element.retrieve('tip:move', this.elementMove.bindWithEvent(this, element));\r
1086                                 element.addEvent('mousemove', move);\r
1087                         }\r
1088                         element.store('tip:native', element.get('title'));\r
1089                         element.erase('title');\r
1090                 }, this);\r
1091                 return this;\r
1092         },\r
1093         \r
1094         detach: function(elements){\r
1095                 $$(elements).each(function(element){\r
1096                         element.removeEvent('mouseenter', element.retrieve('tip:enter') || $empty);\r
1097                         element.removeEvent('mouseleave', element.retrieve('tip:leave') || $empty);\r
1098                         element.removeEvent('mousemove', element.retrieve('tip:move') || $empty);\r
1099                         element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');\r
1100                         var original = element.retrieve('tip:native');\r
1101                         if (original) element.set('title', original);\r
1102                 });\r
1103                 return this;\r
1104         },\r
1105         \r
1106         elementEnter: function(event, element){\r
1107                 \r
1108                 $A(this.container.childNodes).each(Element.dispose);\r
1109                 \r
1110                 var title = element.retrieve('tip:title');\r
1111                 \r
1112                 if (title){\r
1113                         this.titleElement = new Element('div', {'class': 'tip-title'}).inject(this.container);\r
1114                         this.fill(this.titleElement, title);\r
1115                 }\r
1116                 \r
1117                 var text = element.retrieve('tip:text');\r
1118                 if (text){\r
1119                         this.textElement = new Element('div', {'class': 'tip-text'}).inject(this.container);\r
1120                         this.fill(this.textElement, text);\r
1121                 }\r
1122                 \r
1123                 this.timer = $clear(this.timer);\r
1124                 this.timer = this.show.delay(this.options.showDelay, this);\r
1125 \r
1126                 this.position((!this.options.fixed) ? event : {page: element.getPosition()});\r
1127         },\r
1128         \r
1129         elementLeave: function(event){\r
1130                 $clear(this.timer);\r
1131                 this.timer = this.hide.delay(this.options.hideDelay, this);\r
1132         },\r
1133         \r
1134         elementMove: function(event){\r
1135                 this.position(event);\r
1136         },\r
1137         \r
1138         position: function(event){\r
1139                 var size = window.getSize(), scroll = window.getScroll();\r
1140                 var tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight};\r
1141                 var props = {x: 'left', y: 'top'};\r
1142                 for (var z in props){\r
1143                         var pos = event.page[z] + this.options.offsets[z];\r
1144                         if ((pos + tip[z] - scroll[z]) > size[z]) pos = event.page[z] - this.options.offsets[z] - tip[z];\r
1145                         this.tip.setStyle(props[z], pos);\r
1146                 }\r
1147         },\r
1148         \r
1149         fill: function(element, contents){\r
1150                 (typeof contents == 'string') ? element.set('html', contents) : element.adopt(contents);\r
1151         },\r
1152 \r
1153         show: function(){\r
1154                 this.fireEvent('show', this.tip);\r
1155         },\r
1156 \r
1157         hide: function(){\r
1158                 this.fireEvent('hide', this.tip);\r
1159         }\r
1160 \r
1161 });
1162
1163 /*\r
1164 Script: SmoothScroll.js\r
1165         Class for creating a smooth scrolling effect to all internal links on the page.\r
1166 \r
1167 License:\r
1168         MIT-style license.\r
1169 */\r
1170 \r
1171 var SmoothScroll = new Class({\r
1172 \r
1173         Extends: Fx.Scroll,\r
1174 \r
1175         initialize: function(options, context){\r
1176                 context = context || document;\r
1177                 var doc = context.getDocument(), win = context.getWindow();\r
1178                 this.parent(doc, options);\r
1179                 this.links = (this.options.links) ? $$(this.options.links) : $$(doc.links);\r
1180                 var location = win.location.href.match(/^[^#]*/)[0] + '#';\r
1181                 this.links.each(function(link){\r
1182                         if (link.href.indexOf(location) != 0) return;\r
1183                         var anchor = link.href.substr(location.length);\r
1184                         if (anchor && $(anchor)) this.useLink(link, anchor);\r
1185                 }, this);\r
1186                 if (!Browser.Engine.webkit419) this.addEvent('complete', function(){\r
1187                         win.location.hash = this.anchor;\r
1188                 }, true);\r
1189         },\r
1190 \r
1191         useLink: function(link, anchor){\r
1192                 link.addEvent('click', function(event){\r
1193                         this.anchor = anchor;\r
1194                         this.toElement(anchor);\r
1195                         event.stop();\r
1196                 }.bind(this));\r
1197         }\r
1198 \r
1199 });
1200
1201 /*
1202 Script: Slider.js
1203         Class for creating horizontal and vertical slider controls.
1204
1205 License:
1206         MIT-style license.
1207 */
1208
1209 var Slider = new Class({
1210
1211         Implements: [Events, Options],
1212
1213         options: {/*
1214                 onChange: $empty,
1215                 onComplete: $empty,*/
1216                 onTick: function(position){
1217                         if(this.options.snap) position = this.toPosition(this.step);
1218                         this.knob.setStyle(this.property, position);
1219                 },
1220                 snap: false,
1221                 offset: 0,
1222                 range: false,
1223                 wheel: false,
1224                 steps: 100,
1225                 mode: 'horizontal'
1226         },
1227
1228         initialize: function(element, knob, options){
1229                 this.setOptions(options);
1230                 this.element = $(element);
1231                 this.knob = $(knob);
1232                 this.previousChange = this.previousEnd = this.step = -1;
1233                 this.element.addEvent('mousedown', this.clickedElement.bind(this));
1234                 if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement.bindWithEvent(this));
1235                 var offset, limit = {}, modifiers = {'x': false, 'y': false};
1236                 switch (this.options.mode){
1237                         case 'vertical':
1238                                 this.axis = 'y';
1239                                 this.property = 'top';
1240                                 offset = 'offsetHeight';
1241                                 break;
1242                         case 'horizontal':
1243                                 this.axis = 'x';
1244                                 this.property = 'left';
1245                                 offset = 'offsetWidth';
1246                 }
1247                 this.half = this.knob[offset] / 2;
1248                 this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
1249                 this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
1250                 this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
1251                 this.range = this.max - this.min;
1252                 this.steps = this.options.steps || this.full;
1253                 this.stepSize = Math.abs(this.range) / this.steps;
1254                 this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
1255                 
1256                 this.knob.setStyle('position', 'relative').setStyle(this.property, - this.options.offset);
1257                 modifiers[this.axis] = this.property;
1258                 limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
1259                 this.drag = new Drag(this.knob, {
1260                         snap: 0,
1261                         limit: limit,
1262                         modifiers: modifiers,
1263                         onDrag: this.draggedKnob.bind(this),
1264                         onStart: this.draggedKnob.bind(this),
1265                         onComplete: function(){
1266                                 this.draggedKnob();
1267                                 this.end();
1268                         }.bind(this)
1269                 });
1270                 if (this.options.snap) {
1271                         this.drag.options.grid = Math.ceil(this.stepWidth);
1272                         this.drag.options.limit[this.axis][1] = this.full;
1273                 }
1274         },
1275
1276         set: function(step){
1277                 if (!((this.range > 0) ^ (step < this.min))) step = this.min;
1278                 if (!((this.range > 0) ^ (step > this.max))) step = this.max;
1279                 
1280                 this.step = Math.round(step);
1281                 this.checkStep();
1282                 this.end();
1283                 this.fireEvent('tick', this.toPosition(this.step));
1284                 return this;
1285         },
1286
1287         clickedElement: function(event){
1288                 var dir = this.range < 0 ? -1 : 1;
1289                 var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
1290                 position = position.limit(-this.options.offset, this.full -this.options.offset);
1291                 
1292                 this.step = Math.round(this.min + dir * this.toStep(position));
1293                 this.checkStep();
1294                 this.end();
1295                 this.fireEvent('tick', position);
1296         },
1297         
1298         scrolledElement: function(event){
1299                 var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
1300                 this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
1301                 event.stop();
1302         },
1303
1304         draggedKnob: function(){
1305                 var dir = this.range < 0 ? -1 : 1;
1306                 var position = this.drag.value.now[this.axis];
1307                 position = position.limit(-this.options.offset, this.full -this.options.offset);
1308                 this.step = Math.round(this.min + dir * this.toStep(position));
1309                 this.checkStep();
1310         },
1311
1312         checkStep: function(){
1313                 if (this.previousChange != this.step){
1314                         this.previousChange = this.step;
1315                         this.fireEvent('change', this.step);
1316                 }
1317         },
1318
1319         end: function(){
1320                 if (this.previousEnd !== this.step){
1321                         this.previousEnd = this.step;
1322                         this.fireEvent('complete', this.step + '');
1323                 }
1324         },
1325
1326         toStep: function(position){
1327                 var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
1328                 return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
1329         },
1330
1331         toPosition: function(step){
1332                 return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
1333         }
1334
1335 });
1336
1337 /*
1338 Script: Scroller.js
1339         Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.
1340
1341 License:
1342         MIT-style license.
1343 */
1344
1345 var Scroller = new Class({
1346
1347         Implements: [Events, Options],
1348
1349         options: {
1350                 area: 20,
1351                 velocity: 1,
1352                 onChange: function(x, y){
1353                         this.element.scrollTo(x, y);
1354                 }
1355         },
1356
1357         initialize: function(element, options){
1358                 this.setOptions(options);
1359                 this.element = $(element);
1360                 this.listener = ($type(this.element) != 'element') ? $(this.element.getDocument().body) : this.element;
1361                 this.timer = null;
1362                 this.coord = this.getCoords.bind(this);
1363         },
1364
1365         start: function(){
1366                 this.listener.addEvent('mousemove', this.coord);
1367         },
1368
1369         stop: function(){
1370                 this.listener.removeEvent('mousemove', this.coord);
1371                 this.timer = $clear(this.timer);
1372         },
1373
1374         getCoords: function(event){
1375                 this.page = (this.listener.get('tag') == 'body') ? event.client : event.page;
1376                 if (!this.timer) this.timer = this.scroll.periodical(50, this);
1377         },
1378
1379         scroll: function(){
1380                 var size = this.element.getSize(), scroll = this.element.getScroll(), pos = this.element.getPosition(), change = {'x': 0, 'y': 0};
1381                 for (var z in this.page){
1382                         if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0)
1383                                 change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity;
1384                         else if (this.page[z] + this.options.area > (size[z] + pos[z]) && size[z] + size[z] != scroll[z])
1385                                 change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) * this.options.velocity;
1386                 }
1387                 if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]);
1388         }
1389
1390 });
1391
1392 /*\r
1393 Script: Accordion.js\r
1394         An Fx.Elements extension which allows you to easily create accordion type controls.\r
1395 \r
1396 License:\r
1397         MIT-style license.\r
1398 */\r
1399 \r
1400 var Accordion = new Class({\r
1401 \r
1402         Extends: Fx.Elements,\r
1403 \r
1404         options: {/*\r
1405                 onActive: $empty,\r
1406                 onBackground: $empty,*/\r
1407                 display: 0,\r
1408                 show: false,\r
1409                 height: true,\r
1410                 width: false,\r
1411                 opacity: true,\r
1412                 fixedHeight: false,\r
1413                 fixedWidth: false,\r
1414                 wait: false,\r
1415                 alwaysHide: false\r
1416         },\r
1417 \r
1418         initialize: function(){\r
1419                 var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});\r
1420                 this.parent(params.elements, params.options);\r
1421                 this.togglers = $$(params.togglers);\r
1422                 this.container = $(params.container);\r
1423                 this.previous = -1;\r
1424                 if (this.options.alwaysHide) this.options.wait = true;\r
1425                 if ($chk(this.options.show)){\r
1426                         this.options.display = false;\r
1427                         this.previous = this.options.show;\r
1428                 }\r
1429                 if (this.options.start){\r
1430                         this.options.display = false;\r
1431                         this.options.show = false;\r
1432                 }\r
1433                 this.effects = {};\r
1434                 if (this.options.opacity) this.effects.opacity = 'fullOpacity';\r
1435                 if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';\r
1436                 if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';\r
1437                 for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);\r
1438                 this.elements.each(function(el, i){\r
1439                         if (this.options.show === i){\r
1440                                 this.fireEvent('active', [this.togglers[i], el]);\r
1441                         } else {\r
1442                                 for (var fx in this.effects) el.setStyle(fx, 0);\r
1443                         }\r
1444                 }, this);\r
1445                 if ($chk(this.options.display)) this.display(this.options.display);\r
1446         },\r
1447 \r
1448         addSection: function(toggler, element, pos){\r
1449                 toggler = $(toggler);\r
1450                 element = $(element);\r
1451                 var test = this.togglers.contains(toggler);\r
1452                 var len = this.togglers.length;\r
1453                 this.togglers.include(toggler);\r
1454                 this.elements.include(element);\r
1455                 if (len && (!test || pos)){\r
1456                         pos = $pick(pos, len - 1);\r
1457                         toggler.inject(this.togglers[pos], 'before');\r
1458                         element.inject(toggler, 'after');\r
1459                 } else if (this.container && !test){\r
1460                         toggler.inject(this.container);\r
1461                         element.inject(this.container);\r
1462                 }\r
1463                 var idx = this.togglers.indexOf(toggler);\r
1464                 toggler.addEvent('click', this.display.bind(this, idx));\r
1465                 if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});\r
1466                 if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});\r
1467                 element.fullOpacity = 1;\r
1468                 if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;\r
1469                 if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;\r
1470                 element.setStyle('overflow', 'hidden');\r
1471                 if (!test){\r
1472                         for (var fx in this.effects) element.setStyle(fx, 0);\r
1473                 }\r
1474                 return this;\r
1475         },\r
1476 \r
1477         display: function(index){\r
1478                 index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;\r
1479                 if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;\r
1480                 this.previous = index;\r
1481                 var obj = {};\r
1482                 this.elements.each(function(el, i){\r
1483                         obj[i] = {};\r
1484                         var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));\r
1485                         this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);\r
1486                         for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];\r
1487                 }, this);\r
1488                 return this.start(obj);\r
1489         }\r
1490 \r
1491 });