]> jfr.im git - irc/quakenet/qwebirc.git/blob - static/js/mootools-1.2-more-nc.js
First stage of username/password stuff for hyperion.
[irc/quakenet/qwebirc.git] / static / js / mootools-1.2-more-nc.js
1 //MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2008 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
2
3 /*
4 Script: Fx.Slide.js
5 Effect to slide an element in and out of view.
6
7 License:
8 MIT-style license.
9 */
10
11 Fx.Slide = new Class({
12
13 Extends: Fx,
14
15 options: {
16 mode: 'vertical'
17 },
18
19 initialize: function(element, options){
20 this.addEvent('complete', function(){
21 this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
22 if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
23 }, true);
24 this.element = this.subject = $(element);
25 this.parent(options);
26 var wrapper = this.element.retrieve('wrapper');
27 this.wrapper = wrapper || new Element('div', {
28 styles: $extend(this.element.getStyles('margin', 'position'), {'overflow': 'hidden'})
29 }).wraps(this.element);
30 this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
31 this.now = [];
32 this.open = true;
33 },
34
35 vertical: function(){
36 this.margin = 'margin-top';
37 this.layout = 'height';
38 this.offset = this.element.offsetHeight;
39 },
40
41 horizontal: function(){
42 this.margin = 'margin-left';
43 this.layout = 'width';
44 this.offset = this.element.offsetWidth;
45 },
46
47 set: function(now){
48 this.element.setStyle(this.margin, now[0]);
49 this.wrapper.setStyle(this.layout, now[1]);
50 return this;
51 },
52
53 compute: function(from, to, delta){
54 var now = [];
55 var x = 2;
56 x.times(function(i){
57 now[i] = Fx.compute(from[i], to[i], delta);
58 });
59 return now;
60 },
61
62 start: function(how, mode){
63 if (!this.check(arguments.callee, how, mode)) return this;
64 this[mode || this.options.mode]();
65 var margin = this.element.getStyle(this.margin).toInt();
66 var layout = this.wrapper.getStyle(this.layout).toInt();
67 var caseIn = [[margin, layout], [0, this.offset]];
68 var caseOut = [[margin, layout], [-this.offset, 0]];
69 var start;
70 switch (how){
71 case 'in': start = caseIn; break;
72 case 'out': start = caseOut; break;
73 case 'toggle': start = (this.wrapper['offset' + this.layout.capitalize()] == 0) ? caseIn : caseOut;
74 }
75 return this.parent(start[0], start[1]);
76 },
77
78 slideIn: function(mode){
79 return this.start('in', mode);
80 },
81
82 slideOut: function(mode){
83 return this.start('out', mode);
84 },
85
86 hide: function(mode){
87 this[mode || this.options.mode]();
88 this.open = false;
89 return this.set([-this.offset, 0]);
90 },
91
92 show: function(mode){
93 this[mode || this.options.mode]();
94 this.open = true;
95 return this.set([0, this.offset]);
96 },
97
98 toggle: function(mode){
99 return this.start('toggle', mode);
100 }
101
102 });
103
104 Element.Properties.slide = {
105
106 set: function(options){
107 var slide = this.retrieve('slide');
108 if (slide) slide.cancel();
109 return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));
110 },
111
112 get: function(options){
113 if (options || !this.retrieve('slide')){
114 if (options || !this.retrieve('slide:options')) this.set('slide', options);
115 this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));
116 }
117 return this.retrieve('slide');
118 }
119
120 };
121
122 Element.implement({
123
124 slide: function(how, mode){
125 how = how || 'toggle';
126 var slide = this.get('slide'), toggle;
127 switch (how){
128 case 'hide': slide.hide(mode); break;
129 case 'show': slide.show(mode); break;
130 case 'toggle':
131 var flag = this.retrieve('slide:flag', slide.open);
132 slide[(flag) ? 'slideOut' : 'slideIn'](mode);
133 this.store('slide:flag', !flag);
134 toggle = true;
135 break;
136 default: slide.start(how, mode);
137 }
138 if (!toggle) this.eliminate('slide:flag');
139 return this;
140 }
141
142 });
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 /*
523 Script: Hash.Cookie.js
524 Class for creating, reading, and deleting Cookies in JSON format.
525
526 License:
527 MIT-style license.
528 */
529
530 Hash.Cookie = new Class({
531
532 Extends: Cookie,
533
534 options: {
535 autoSave: true
536 },
537
538 initialize: function(name, options){
539 this.parent(name, options);
540 this.load();
541 },
542
543 save: function(){
544 var value = JSON.encode(this.hash);
545 if (!value || value.length > 4096) return false; //cookie would be truncated!
546 if (value == '{}') this.dispose();
547 else this.write(value);
548 return true;
549 },
550
551 load: function(){
552 this.hash = new Hash(JSON.decode(this.read(), true));
553 return this;
554 }
555
556 });
557
558 Hash.Cookie.implement((function(){
559
560 var methods = {};
561
562 Hash.each(Hash.prototype, function(method, name){
563 methods[name] = function(){
564 var value = method.apply(this.hash, arguments);
565 if (this.options.autoSave) this.save();
566 return value;
567 };
568 });
569
570 return methods;
571
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 /*
1035 Script: Tips.js
1036 Class for creating nice tips that follow the mouse cursor when hovering an element.
1037
1038 License:
1039 MIT-style license.
1040 */
1041
1042 var Tips = new Class({
1043
1044 Implements: [Events, Options],
1045
1046 options: {
1047 onShow: function(tip){
1048 tip.setStyle('visibility', 'visible');
1049 },
1050 onHide: function(tip){
1051 tip.setStyle('visibility', 'hidden');
1052 },
1053 showDelay: 100,
1054 hideDelay: 100,
1055 className: null,
1056 offsets: {x: 16, y: 16},
1057 fixed: false
1058 },
1059
1060 initialize: function(){
1061 var params = Array.link(arguments, {options: Object.type, elements: $defined});
1062 this.setOptions(params.options || null);
1063
1064 this.tip = new Element('div').inject(document.body);
1065
1066 if (this.options.className) this.tip.addClass(this.options.className);
1067
1068 var top = new Element('div', {'class': 'tip-top'}).inject(this.tip);
1069 this.container = new Element('div', {'class': 'tip'}).inject(this.tip);
1070 var bottom = new Element('div', {'class': 'tip-bottom'}).inject(this.tip);
1071
1072 this.tip.setStyles({position: 'absolute', top: 0, left: 0, visibility: 'hidden'});
1073
1074 if (params.elements) this.attach(params.elements);
1075 },
1076
1077 attach: function(elements){
1078 $$(elements).each(function(element){
1079 var title = element.retrieve('tip:title', element.get('title'));
1080 var text = element.retrieve('tip:text', element.get('rel') || element.get('href'));
1081 var enter = element.retrieve('tip:enter', this.elementEnter.bindWithEvent(this, element));
1082 var leave = element.retrieve('tip:leave', this.elementLeave.bindWithEvent(this, element));
1083 element.addEvents({mouseenter: enter, mouseleave: leave});
1084 if (!this.options.fixed){
1085 var move = element.retrieve('tip:move', this.elementMove.bindWithEvent(this, element));
1086 element.addEvent('mousemove', move);
1087 }
1088 element.store('tip:native', element.get('title'));
1089 element.erase('title');
1090 }, this);
1091 return this;
1092 },
1093
1094 detach: function(elements){
1095 $$(elements).each(function(element){
1096 element.removeEvent('mouseenter', element.retrieve('tip:enter') || $empty);
1097 element.removeEvent('mouseleave', element.retrieve('tip:leave') || $empty);
1098 element.removeEvent('mousemove', element.retrieve('tip:move') || $empty);
1099 element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');
1100 var original = element.retrieve('tip:native');
1101 if (original) element.set('title', original);
1102 });
1103 return this;
1104 },
1105
1106 elementEnter: function(event, element){
1107
1108 $A(this.container.childNodes).each(Element.dispose);
1109
1110 var title = element.retrieve('tip:title');
1111
1112 if (title){
1113 this.titleElement = new Element('div', {'class': 'tip-title'}).inject(this.container);
1114 this.fill(this.titleElement, title);
1115 }
1116
1117 var text = element.retrieve('tip:text');
1118 if (text){
1119 this.textElement = new Element('div', {'class': 'tip-text'}).inject(this.container);
1120 this.fill(this.textElement, text);
1121 }
1122
1123 this.timer = $clear(this.timer);
1124 this.timer = this.show.delay(this.options.showDelay, this);
1125
1126 this.position((!this.options.fixed) ? event : {page: element.getPosition()});
1127 },
1128
1129 elementLeave: function(event){
1130 $clear(this.timer);
1131 this.timer = this.hide.delay(this.options.hideDelay, this);
1132 },
1133
1134 elementMove: function(event){
1135 this.position(event);
1136 },
1137
1138 position: function(event){
1139 var size = window.getSize(), scroll = window.getScroll();
1140 var tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight};
1141 var props = {x: 'left', y: 'top'};
1142 for (var z in props){
1143 var pos = event.page[z] + this.options.offsets[z];
1144 if ((pos + tip[z] - scroll[z]) > size[z]) pos = event.page[z] - this.options.offsets[z] - tip[z];
1145 this.tip.setStyle(props[z], pos);
1146 }
1147 },
1148
1149 fill: function(element, contents){
1150 (typeof contents == 'string') ? element.set('html', contents) : element.adopt(contents);
1151 },
1152
1153 show: function(){
1154 this.fireEvent('show', this.tip);
1155 },
1156
1157 hide: function(){
1158 this.fireEvent('hide', this.tip);
1159 }
1160
1161 });
1162
1163 /*
1164 Script: SmoothScroll.js
1165 Class for creating a smooth scrolling effect to all internal links on the page.
1166
1167 License:
1168 MIT-style license.
1169 */
1170
1171 var SmoothScroll = new Class({
1172
1173 Extends: Fx.Scroll,
1174
1175 initialize: function(options, context){
1176 context = context || document;
1177 var doc = context.getDocument(), win = context.getWindow();
1178 this.parent(doc, options);
1179 this.links = (this.options.links) ? $$(this.options.links) : $$(doc.links);
1180 var location = win.location.href.match(/^[^#]*/)[0] + '#';
1181 this.links.each(function(link){
1182 if (link.href.indexOf(location) != 0) return;
1183 var anchor = link.href.substr(location.length);
1184 if (anchor && $(anchor)) this.useLink(link, anchor);
1185 }, this);
1186 if (!Browser.Engine.webkit419) this.addEvent('complete', function(){
1187 win.location.hash = this.anchor;
1188 }, true);
1189 },
1190
1191 useLink: function(link, anchor){
1192 link.addEvent('click', function(event){
1193 this.anchor = anchor;
1194 this.toElement(anchor);
1195 event.stop();
1196 }.bind(this));
1197 }
1198
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 /*
1393 Script: Accordion.js
1394 An Fx.Elements extension which allows you to easily create accordion type controls.
1395
1396 License:
1397 MIT-style license.
1398 */
1399
1400 var Accordion = new Class({
1401
1402 Extends: Fx.Elements,
1403
1404 options: {/*
1405 onActive: $empty,
1406 onBackground: $empty,*/
1407 display: 0,
1408 show: false,
1409 height: true,
1410 width: false,
1411 opacity: true,
1412 fixedHeight: false,
1413 fixedWidth: false,
1414 wait: false,
1415 alwaysHide: false
1416 },
1417
1418 initialize: function(){
1419 var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});
1420 this.parent(params.elements, params.options);
1421 this.togglers = $$(params.togglers);
1422 this.container = $(params.container);
1423 this.previous = -1;
1424 if (this.options.alwaysHide) this.options.wait = true;
1425 if ($chk(this.options.show)){
1426 this.options.display = false;
1427 this.previous = this.options.show;
1428 }
1429 if (this.options.start){
1430 this.options.display = false;
1431 this.options.show = false;
1432 }
1433 this.effects = {};
1434 if (this.options.opacity) this.effects.opacity = 'fullOpacity';
1435 if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
1436 if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
1437 for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
1438 this.elements.each(function(el, i){
1439 if (this.options.show === i){
1440 this.fireEvent('active', [this.togglers[i], el]);
1441 } else {
1442 for (var fx in this.effects) el.setStyle(fx, 0);
1443 }
1444 }, this);
1445 if ($chk(this.options.display)) this.display(this.options.display);
1446 },
1447
1448 addSection: function(toggler, element, pos){
1449 toggler = $(toggler);
1450 element = $(element);
1451 var test = this.togglers.contains(toggler);
1452 var len = this.togglers.length;
1453 this.togglers.include(toggler);
1454 this.elements.include(element);
1455 if (len && (!test || pos)){
1456 pos = $pick(pos, len - 1);
1457 toggler.inject(this.togglers[pos], 'before');
1458 element.inject(toggler, 'after');
1459 } else if (this.container && !test){
1460 toggler.inject(this.container);
1461 element.inject(this.container);
1462 }
1463 var idx = this.togglers.indexOf(toggler);
1464 toggler.addEvent('click', this.display.bind(this, idx));
1465 if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
1466 if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
1467 element.fullOpacity = 1;
1468 if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
1469 if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
1470 element.setStyle('overflow', 'hidden');
1471 if (!test){
1472 for (var fx in this.effects) element.setStyle(fx, 0);
1473 }
1474 return this;
1475 },
1476
1477 display: function(index){
1478 index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
1479 if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
1480 this.previous = index;
1481 var obj = {};
1482 this.elements.each(function(el, i){
1483 obj[i] = {};
1484 var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
1485 this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
1486 for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
1487 }, this);
1488 return this.start(obj);
1489 }
1490
1491 });