Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigiboard / vigiboard / public / js / mootools-more.js @ c2a0470d

History | View | Annotate | Download (59.4 KB)

1
//MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2009 Aaron Newton <http://clientcide.com/>, Valerio Proietti <http://mad4milk.net> & the MooTools team <http://mootools.net/developers>, MIT Style License.
2

    
3
/*
4
---
5

6
script: More.js
7

8
description: MooTools More
9

10
license: MIT-style license
11

12
authors:
13
- Guillermo Rauch
14
- Thomas Aylott
15
- Scott Kyle
16

17
requires:
18
- core:1.2.4/MooTools
19

20
provides: [MooTools.More]
21

22
...
23
*/
24

    
25
MooTools.More = {
26
        'version': '1.2.4.2',
27
        'build': 'bd5a93c0913cce25917c48cbdacde568e15e02ef'
28
};
29

    
30
/*
31
---
32

33
script: MooTools.Lang.js
34

35
description: Provides methods for localization.
36

37
license: MIT-style license
38

39
authors:
40
- Aaron Newton
41

42
requires:
43
- core:1.2.4/Events
44
- /MooTools.More
45

46
provides: [MooTools.Lang]
47

48
...
49
*/
50

    
51
(function(){
52

    
53
        var data = {
54
                language: 'en-US',
55
                languages: {
56
                        'en-US': {}
57
                },
58
                cascades: ['en-US']
59
        };
60
        
61
        var cascaded;
62

    
63
        MooTools.lang = new Events();
64

    
65
        $extend(MooTools.lang, {
66

    
67
                setLanguage: function(lang){
68
                        if (!data.languages[lang]) return this;
69
                        data.language = lang;
70
                        this.load();
71
                        this.fireEvent('langChange', lang);
72
                        return this;
73
                },
74

    
75
                load: function() {
76
                        var langs = this.cascade(this.getCurrentLanguage());
77
                        cascaded = {};
78
                        $each(langs, function(set, setName){
79
                                cascaded[setName] = this.lambda(set);
80
                        }, this);
81
                },
82

    
83
                getCurrentLanguage: function(){
84
                        return data.language;
85
                },
86

    
87
                addLanguage: function(lang){
88
                        data.languages[lang] = data.languages[lang] || {};
89
                        return this;
90
                },
91

    
92
                cascade: function(lang){
93
                        var cascades = (data.languages[lang] || {}).cascades || [];
94
                        cascades.combine(data.cascades);
95
                        cascades.erase(lang).push(lang);
96
                        var langs = cascades.map(function(lng){
97
                                return data.languages[lng];
98
                        }, this);
99
                        return $merge.apply(this, langs);
100
                },
101

    
102
                lambda: function(set) {
103
                        (set || {}).get = function(key, args){
104
                                return $lambda(set[key]).apply(this, $splat(args));
105
                        };
106
                        return set;
107
                },
108

    
109
                get: function(set, key, args){
110
                        if (cascaded && cascaded[set]) return (key ? cascaded[set].get(key, args) : cascaded[set]);
111
                },
112

    
113
                set: function(lang, set, members){
114
                        this.addLanguage(lang);
115
                        langData = data.languages[lang];
116
                        if (!langData[set]) langData[set] = {};
117
                        $extend(langData[set], members);
118
                        if (lang == this.getCurrentLanguage()){
119
                                this.load();
120
                                this.fireEvent('langChange', lang);
121
                        }
122
                        return this;
123
                },
124

    
125
                list: function(){
126
                        return Hash.getKeys(data.languages);
127
                }
128

    
129
        });
130

    
131
})();
132

    
133
/*
134
---
135

136
script: Log.js
137

138
description: Provides basic logging functionality for plugins to implement.
139

140
license: MIT-style license
141

142
authors:
143
- Guillermo Rauch
144
- Thomas Aylott
145
- Scott Kyle
146

147
requires:
148
- core:1.2.4/Class
149
- /MooTools.More
150

151
provides: [Log]
152

153
...
154
*/
155

    
156
(function(){
157

    
158
var global = this;
159

    
160
var log = function(){
161
        if (global.console && console.log){
162
                try {
163
                        console.log.apply(console, arguments);
164
                } catch(e) {
165
                        console.log(Array.slice(arguments));
166
                }
167
        } else {
168
                Log.logged.push(arguments);
169
        }
170
        return this;
171
};
172

    
173
var disabled = function(){
174
        this.logged.push(arguments);
175
        return this;
176
};
177

    
178
this.Log = new Class({
179
        
180
        logged: [],
181
        
182
        log: disabled,
183
        
184
        resetLog: function(){
185
                this.logged.empty();
186
                return this;
187
        },
188

    
189
        enableLog: function(){
190
                this.log = log;
191
                this.logged.each(function(args){
192
                        this.log.apply(this, args);
193
                }, this);
194
                return this.resetLog();
195
        },
196

    
197
        disableLog: function(){
198
                this.log = disabled;
199
                return this;
200
        }
201
        
202
});
203

    
204
Log.extend(new Log).enableLog();
205

    
206
// legacy
207
Log.logger = function(){
208
        return this.log.apply(this, arguments);
209
};
210

    
211
})();
212

    
213
/*
214
---
215

216
script: Class.Refactor.js
217

218
description: Extends a class onto itself with new property, preserving any items attached to the class's namespace.
219

220
license: MIT-style license
221

222
authors:
223
- Aaron Newton
224

225
requires:
226
- core:1.2.4/Class
227
- /MooTools.More
228

229
provides: [Class.refactor]
230

231
...
232
*/
233

    
234
Class.refactor = function(original, refactors){
235

    
236
        $each(refactors, function(item, name){
237
                var origin = original.prototype[name];
238
                if (origin && (origin = origin._origin) && typeof item == 'function') original.implement(name, function(){
239
                        var old = this.previous;
240
                        this.previous = origin;
241
                        var value = item.apply(this, arguments);
242
                        this.previous = old;
243
                        return value;
244
                }); else original.implement(name, item);
245
        });
246

    
247
        return original;
248

    
249
};
250

    
251
/*
252
---
253

254
script: Class.Binds.js
255

256
description: Automagically binds specified methods in a class to the instance of the class.
257

258
license: MIT-style license
259

260
authors:
261
- Aaron Newton
262

263
requires:
264
- core:1.2.4/Class
265
- /MooTools.More
266

267
provides: [Class.Binds]
268

269
...
270
*/
271

    
272
Class.Mutators.Binds = function(binds){
273
    return binds;
274
};
275

    
276
Class.Mutators.initialize = function(initialize){
277
        return function(){
278
                $splat(this.Binds).each(function(name){
279
                        var original = this[name];
280
                        if (original) this[name] = original.bind(this);
281
                }, this);
282
                return initialize.apply(this, arguments);
283
        };
284
};
285

    
286

    
287
/*
288
---
289

290
script: Class.Occlude.js
291

292
description: Prevents a class from being applied to a DOM element twice.
293

294
license: MIT-style license.
295

296
authors:
297
- Aaron Newton
298

299
requires: 
300
- core/1.2.4/Class
301
- core:1.2.4/Element
302
- /MooTools.More
303

304
provides: [Class.Occlude]
305

306
...
307
*/
308

    
309
Class.Occlude = new Class({
310

    
311
        occlude: function(property, element){
312
                element = document.id(element || this.element);
313
                var instance = element.retrieve(property || this.property);
314
                if (instance && !$defined(this.occluded))
315
                        return this.occluded = instance;
316

    
317
                this.occluded = false;
318
                element.store(property || this.property, this);
319
                return this.occluded;
320
        }
321

    
322
});
323

    
324
/*
325
---
326

327
script: Chain.Wait.js
328

329
description: value, Adds a method to inject pauses between chained events.
330

331
license: MIT-style license.
332

333
authors:
334
- Aaron Newton
335

336
requires: 
337
- core:1.2.4/Chain 
338
- core:1.2.4/Element
339
- core:1.2.4/Fx
340
- /MooTools.More
341

342
provides: [Chain.Wait]
343

344
...
345
*/
346

    
347
(function(){
348

    
349
        var wait = {
350
                wait: function(duration){
351
                        return this.chain(function(){
352
                                this.callChain.delay($pick(duration, 500), this);
353
                        }.bind(this));
354
                }
355
        };
356

    
357
        Chain.implement(wait);
358

    
359
        if (window.Fx){
360
                Fx.implement(wait);
361
                ['Css', 'Tween', 'Elements'].each(function(cls){
362
                        if (Fx[cls]) Fx[cls].implement(wait);
363
                });
364
        }
365

    
366
        Element.implement({
367
                chains: function(effects){
368
                        $splat($pick(effects, ['tween', 'morph', 'reveal'])).each(function(effect){
369
                                effect = this.get(effect);
370
                                if (!effect) return;
371
                                effect.setOptions({
372
                                        link:'chain'
373
                                });
374
                        }, this);
375
                        return this;
376
                },
377
                pauseFx: function(duration, effect){
378
                        this.chains(effect).get($pick(effect, 'tween')).wait(duration);
379
                        return this;
380
                }
381
        });
382

    
383
})();
384

    
385
/*
386
---
387

388
script: Date.js
389

390
description: Extends the Date native object to include methods useful in managing dates.
391

392
license: MIT-style license
393

394
authors:
395
- Aaron Newton
396
- Nicholas Barthelemy - https://svn.nbarthelemy.com/date-js/
397
- Harald Kirshner - mail [at] digitarald.de; http://digitarald.de
398
- Scott Kyle - scott [at] appden.com; http://appden.com
399

400
requires:
401
- core:1.2.4/Array
402
- core:1.2.4/String
403
- core:1.2.4/Number
404
- core:1.2.4/Lang
405
- core:1.2.4/Date.English.US
406
- /MooTools.More
407

408
provides: [Date]
409

410
...
411
*/
412

    
413
(function(){
414

    
415
var Date = this.Date;
416

    
417
if (!Date.now) Date.now = $time;
418

    
419
Date.Methods = {
420
        ms: 'Milliseconds',
421
        year: 'FullYear',
422
        min: 'Minutes',
423
        mo: 'Month',
424
        sec: 'Seconds',
425
        hr: 'Hours'
426
};
427

    
428
['Date', 'Day', 'FullYear', 'Hours', 'Milliseconds', 'Minutes', 'Month', 'Seconds', 'Time', 'TimezoneOffset',
429
        'Week', 'Timezone', 'GMTOffset', 'DayOfYear', 'LastMonth', 'LastDayOfMonth', 'UTCDate', 'UTCDay', 'UTCFullYear',
430
        'AMPM', 'Ordinal', 'UTCHours', 'UTCMilliseconds', 'UTCMinutes', 'UTCMonth', 'UTCSeconds'].each(function(method){
431
        Date.Methods[method.toLowerCase()] = method;
432
});
433

    
434
var pad = function(what, length){
435
        return new Array(length - String(what).length + 1).join('0') + what;
436
};
437

    
438
Date.implement({
439

    
440
        set: function(prop, value){
441
                switch ($type(prop)){
442
                        case 'object':
443
                                for (var p in prop) this.set(p, prop[p]);
444
                                break;
445
                        case 'string':
446
                                prop = prop.toLowerCase();
447
                                var m = Date.Methods;
448
                                if (m[prop]) this['set' + m[prop]](value);
449
                }
450
                return this;
451
        },
452

    
453
        get: function(prop){
454
                prop = prop.toLowerCase();
455
                var m = Date.Methods;
456
                if (m[prop]) return this['get' + m[prop]]();
457
                return null;
458
        },
459

    
460
        clone: function(){
461
                return new Date(this.get('time'));
462
        },
463

    
464
        increment: function(interval, times){
465
                interval = interval || 'day';
466
                times = $pick(times, 1);
467

    
468
                switch (interval){
469
                        case 'year':
470
                                return this.increment('month', times * 12);
471
                        case 'month':
472
                                var d = this.get('date');
473
                                this.set('date', 1).set('mo', this.get('mo') + times);
474
                                return this.set('date', d.min(this.get('lastdayofmonth')));
475
                        case 'week':
476
                                return this.increment('day', times * 7);
477
                        case 'day':
478
                                return this.set('date', this.get('date') + times);
479
                }
480

    
481
                if (!Date.units[interval]) throw new Error(interval + ' is not a supported interval');
482

    
483
                return this.set('time', this.get('time') + times * Date.units[interval]());
484
        },
485

    
486
        decrement: function(interval, times){
487
                return this.increment(interval, -1 * $pick(times, 1));
488
        },
489

    
490
        isLeapYear: function(){
491
                return Date.isLeapYear(this.get('year'));
492
        },
493

    
494
        clearTime: function(){
495
                return this.set({hr: 0, min: 0, sec: 0, ms: 0});
496
        },
497

    
498
        diff: function(date, resolution){
499
                if ($type(date) == 'string') date = Date.parse(date);
500
                
501
                return ((date - this) / Date.units[resolution || 'day'](3, 3)).toInt(); // non-leap year, 30-day month
502
        },
503

    
504
        getLastDayOfMonth: function(){
505
                return Date.daysInMonth(this.get('mo'), this.get('year'));
506
        },
507

    
508
        getDayOfYear: function(){
509
                return (Date.UTC(this.get('year'), this.get('mo'), this.get('date') + 1) 
510
                        - Date.UTC(this.get('year'), 0, 1)) / Date.units.day();
511
        },
512

    
513
        getWeek: function(){
514
                return (this.get('dayofyear') / 7).ceil();
515
        },
516
        
517
        getOrdinal: function(day){
518
                return Date.getMsg('ordinal', day || this.get('date'));
519
        },
520

    
521
        getTimezone: function(){
522
                return this.toString()
523
                        .replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/, '$1')
524
                        .replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, '$1$2$3');
525
        },
526

    
527
        getGMTOffset: function(){
528
                var off = this.get('timezoneOffset');
529
                return ((off > 0) ? '-' : '+') + pad((off.abs() / 60).floor(), 2) + pad(off % 60, 2);
530
        },
531

    
532
        setAMPM: function(ampm){
533
                ampm = ampm.toUpperCase();
534
                var hr = this.get('hr');
535
                if (hr > 11 && ampm == 'AM') return this.decrement('hour', 12);
536
                else if (hr < 12 && ampm == 'PM') return this.increment('hour', 12);
537
                return this;
538
        },
539

    
540
        getAMPM: function(){
541
                return (this.get('hr') < 12) ? 'AM' : 'PM';
542
        },
543

    
544
        parse: function(str){
545
                this.set('time', Date.parse(str));
546
                return this;
547
        },
548

    
549
        isValid: function(date) {
550
                return !!(date || this).valueOf();
551
        },
552

    
553
        format: function(f){
554
                if (!this.isValid()) return 'invalid date';
555
                f = f || '%x %X';
556
                f = formats[f.toLowerCase()] || f; // replace short-hand with actual format
557
                var d = this;
558
                return f.replace(/%([a-z%])/gi,
559
                        function($0, $1){
560
                                switch ($1){
561
                                        case 'a': return Date.getMsg('days')[d.get('day')].substr(0, 3);
562
                                        case 'A': return Date.getMsg('days')[d.get('day')];
563
                                        case 'b': return Date.getMsg('months')[d.get('month')].substr(0, 3);
564
                                        case 'B': return Date.getMsg('months')[d.get('month')];
565
                                        case 'c': return d.toString();
566
                                        case 'd': return pad(d.get('date'), 2);
567
                                        case 'H': return pad(d.get('hr'), 2);
568
                                        case 'I': return ((d.get('hr') % 12) || 12);
569
                                        case 'j': return pad(d.get('dayofyear'), 3);
570
                                        case 'm': return pad((d.get('mo') + 1), 2);
571
                                        case 'M': return pad(d.get('min'), 2);
572
                                        case 'o': return d.get('ordinal');
573
                                        case 'p': return Date.getMsg(d.get('ampm'));
574
                                        case 'S': return pad(d.get('seconds'), 2);
575
                                        case 'U': return pad(d.get('week'), 2);
576
                                        case 'w': return d.get('day');
577
                                        case 'x': return d.format(Date.getMsg('shortDate'));
578
                                        case 'X': return d.format(Date.getMsg('shortTime'));
579
                                        case 'y': return d.get('year').toString().substr(2);
580
                                        case 'Y': return d.get('year');
581
                                        case 'T': return d.get('GMTOffset');
582
                                        case 'Z': return d.get('Timezone');
583
                                }
584
                                return $1;
585
                        }
586
                );
587
        },
588

    
589
        toISOString: function(){
590
                return this.format('iso8601');
591
        }
592

    
593
});
594

    
595
Date.alias('toISOString', 'toJSON');
596
Date.alias('diff', 'compare');
597
Date.alias('format', 'strftime');
598

    
599
var formats = {
600
        db: '%Y-%m-%d %H:%M:%S',
601
        compact: '%Y%m%dT%H%M%S',
602
        iso8601: '%Y-%m-%dT%H:%M:%S%T',
603
        rfc822: '%a, %d %b %Y %H:%M:%S %Z',
604
        'short': '%d %b %H:%M',
605
        'long': '%B %d, %Y %H:%M'
606
};
607

    
608
var parsePatterns = [];
609
var nativeParse = Date.parse;
610

    
611
var parseWord = function(type, word, num){
612
        var ret = -1;
613
        var translated = Date.getMsg(type + 's');
614

    
615
        switch ($type(word)){
616
                case 'object':
617
                        ret = translated[word.get(type)];
618
                        break;
619
                case 'number':
620
                        ret = translated[month - 1];
621
                        if (!ret) throw new Error('Invalid ' + type + ' index: ' + index);
622
                        break;
623
                case 'string':
624
                        var match = translated.filter(function(name){
625
                                return this.test(name);
626
                        }, new RegExp('^' + word, 'i'));
627
                        if (!match.length)    throw new Error('Invalid ' + type + ' string');
628
                        if (match.length > 1) throw new Error('Ambiguous ' + type);
629
                        ret = match[0];
630
        }
631

    
632
        return (num) ? translated.indexOf(ret) : ret;
633
};
634

    
635
Date.extend({
636

    
637
        getMsg: function(key, args) {
638
                return MooTools.lang.get('Date', key, args);
639
        },
640

    
641
        units: {
642
                ms: $lambda(1),
643
                second: $lambda(1000),
644
                minute: $lambda(60000),
645
                hour: $lambda(3600000),
646
                day: $lambda(86400000),
647
                week: $lambda(608400000),
648
                month: function(month, year){
649
                        var d = new Date;
650
                        return Date.daysInMonth($pick(month, d.get('mo')), $pick(year, d.get('year'))) * 86400000;
651
                },
652
                year: function(year){
653
                        year = year || new Date().get('year');
654
                        return Date.isLeapYear(year) ? 31622400000 : 31536000000;
655
                }
656
        },
657

    
658
        daysInMonth: function(month, year){
659
                return [31, Date.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
660
        },
661

    
662
        isLeapYear: function(year){
663
                return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0);
664
        },
665

    
666
        parse: function(from){
667
                var t = $type(from);
668
                if (t == 'number') return new Date(from);
669
                if (t != 'string') return from;
670
                from = from.clean();
671
                if (!from.length) return null;
672

    
673
                var parsed;
674
                parsePatterns.some(function(pattern){
675
                        var bits = pattern.re.exec(from);
676
                        return (bits) ? (parsed = pattern.handler(bits)) : false;
677
                });
678

    
679
                return parsed || new Date(nativeParse(from));
680
        },
681

    
682
        parseDay: function(day, num){
683
                return parseWord('day', day, num);
684
        },
685

    
686
        parseMonth: function(month, num){
687
                return parseWord('month', month, num);
688
        },
689

    
690
        parseUTC: function(value){
691
                var localDate = new Date(value);
692
                var utcSeconds = Date.UTC(
693
                        localDate.get('year'),
694
                        localDate.get('mo'),
695
                        localDate.get('date'),
696
                        localDate.get('hr'),
697
                        localDate.get('min'),
698
                        localDate.get('sec')
699
                );
700
                return new Date(utcSeconds);
701
        },
702

    
703
        orderIndex: function(unit){
704
                return Date.getMsg('dateOrder').indexOf(unit) + 1;
705
        },
706

    
707
        defineFormat: function(name, format){
708
                formats[name] = format;
709
        },
710

    
711
        defineFormats: function(formats){
712
                for (var name in formats) Date.defineFormat(name, formats[name]);
713
        },
714

    
715
        parsePatterns: parsePatterns, // this is deprecated
716
        
717
        defineParser: function(pattern){
718
                parsePatterns.push((pattern.re && pattern.handler) ? pattern : build(pattern));
719
        },
720
        
721
        defineParsers: function(){
722
                Array.flatten(arguments).each(Date.defineParser);
723
        },
724
        
725
        define2DigitYearStart: function(year){
726
                startYear = year % 100;
727
                startCentury = year - startYear;
728
        }
729

    
730
});
731

    
732
var startCentury = 1900;
733
var startYear = 70;
734

    
735
var regexOf = function(type){
736
        return new RegExp('(?:' + Date.getMsg(type).map(function(name){
737
                return name.substr(0, 3);
738
        }).join('|') + ')[a-z]*');
739
};
740

    
741
var replacers = function(key){
742
        switch(key){
743
                case 'x': // iso8601 covers yyyy-mm-dd, so just check if month is first
744
                        return ((Date.orderIndex('month') == 1) ? '%m[.-/]%d' : '%d[.-/]%m') + '([.-/]%y)?';
745
                case 'X':
746
                        return '%H([.:]%M)?([.:]%S([.:]%s)?)? ?%p? ?%T?';
747
        }
748
        return null;
749
};
750

    
751
var keys = {
752
        d: /[0-2]?[0-9]|3[01]/,
753
        H: /[01]?[0-9]|2[0-3]/,
754
        I: /0?[1-9]|1[0-2]/,
755
        M: /[0-5]?\d/,
756
        s: /\d+/,
757
        o: /[a-z]*/,
758
        p: /[ap]\.?m\.?/,
759
        y: /\d{2}|\d{4}/,
760
        Y: /\d{4}/,
761
        T: /Z|[+-]\d{2}(?::?\d{2})?/
762
};
763

    
764
keys.m = keys.I;
765
keys.S = keys.M;
766

    
767
var currentLanguage;
768

    
769
var recompile = function(language){
770
        currentLanguage = language;
771
        
772
        keys.a = keys.A = regexOf('days');
773
        keys.b = keys.B = regexOf('months');
774
        
775
        parsePatterns.each(function(pattern, i){
776
                if (pattern.format) parsePatterns[i] = build(pattern.format);
777
        });
778
};
779

    
780
var build = function(format){
781
        if (!currentLanguage) return {format: format};
782
        
783
        var parsed = [];
784
        var re = (format.source || format) // allow format to be regex
785
         .replace(/%([a-z])/gi,
786
                function($0, $1){
787
                        return replacers($1) || $0;
788
                }
789
        ).replace(/\((?!\?)/g, '(?:') // make all groups non-capturing
790
         .replace(/ (?!\?|\*)/g, ',? ') // be forgiving with spaces and commas
791
         .replace(/%([a-z%])/gi,
792
                function($0, $1){
793
                        var p = keys[$1];
794
                        if (!p) return $1;
795
                        parsed.push($1);
796
                        return '(' + p.source + ')';
797
                }
798
        ).replace(/\[a-z\]/gi, '[a-z\\u00c0-\\uffff]'); // handle unicode words
799

    
800
        return {
801
                format: format,
802
                re: new RegExp('^' + re + '$', 'i'),
803
                handler: function(bits){
804
                        bits = bits.slice(1).associate(parsed);
805
                        var date = new Date().clearTime();
806
                        if ('d' in bits) handle.call(date, 'd', 1);
807
                        if ('m' in bits) handle.call(date, 'm', 1);
808
                        for (var key in bits) handle.call(date, key, bits[key]);
809
                        return date;
810
                }
811
        };
812
};
813

    
814
var handle = function(key, value){
815
        if (!value) return this;
816

    
817
        switch(key){
818
                case 'a': case 'A': return this.set('day', Date.parseDay(value, true));
819
                case 'b': case 'B': return this.set('mo', Date.parseMonth(value, true));
820
                case 'd': return this.set('date', value);
821
                case 'H': case 'I': return this.set('hr', value);
822
                case 'm': return this.set('mo', value - 1);
823
                case 'M': return this.set('min', value);
824
                case 'p': return this.set('ampm', value.replace(/\./g, ''));
825
                case 'S': return this.set('sec', value);
826
                case 's': return this.set('ms', ('0.' + value) * 1000);
827
                case 'w': return this.set('day', value);
828
                case 'Y': return this.set('year', value);
829
                case 'y':
830
                        value = +value;
831
                        if (value < 100) value += startCentury + (value < startYear ? 100 : 0);
832
                        return this.set('year', value);
833
                case 'T':
834
                        if (value == 'Z') value = '+00';
835
                        var offset = value.match(/([+-])(\d{2}):?(\d{2})?/);
836
                        offset = (offset[1] + '1') * (offset[2] * 60 + (+offset[3] || 0)) + this.getTimezoneOffset();
837
                        return this.set('time', this - offset * 60000);
838
        }
839

    
840
        return this;
841
};
842

    
843
Date.defineParsers(
844
        '%Y([-./]%m([-./]%d((T| )%X)?)?)?', // "1999-12-31", "1999-12-31 11:59pm", "1999-12-31 23:59:59", ISO8601
845
        '%Y%m%d(T%H(%M%S?)?)?', // "19991231", "19991231T1159", compact
846
        '%x( %X)?', // "12/31", "12.31.99", "12-31-1999", "12/31/2008 11:59 PM"
847
        '%d%o( %b( %Y)?)?( %X)?', // "31st", "31st December", "31 Dec 1999", "31 Dec 1999 11:59pm"
848
        '%b( %d%o)?( %Y)?( %X)?', // Same as above with month and day switched
849
        '%Y %b( %d%o( %X)?)?', // Same as above with year coming first
850
        '%o %b %d %X %T %Y' // "Thu Oct 22 08:11:23 +0000 2009"
851
);
852

    
853
MooTools.lang.addEvent('langChange', function(language){
854
        if (MooTools.lang.get('Date')) recompile(language);
855
}).fireEvent('langChange', MooTools.lang.getCurrentLanguage());
856

    
857
})();
858

    
859
/*
860
---
861

862
script: Element.Measure.js
863

864
description: Extends the Element native object to include methods useful in measuring dimensions.
865

866
credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
867

868
license: MIT-style license
869

870
authors:
871
- Aaron Newton
872

873
requires:
874
- core:1.2.4/Element.Style
875
- core:1.2.4/Element.Dimensions
876
- /MooTools.More
877

878
provides: [Element.Measure]
879

880
...
881
*/
882

    
883
Element.implement({
884

    
885
        measure: function(fn){
886
                var vis = function(el) {
887
                        return !!(!el || el.offsetHeight || el.offsetWidth);
888
                };
889
                if (vis(this)) return fn.apply(this);
890
                var parent = this.getParent(),
891
                        restorers = [],
892
                        toMeasure = []; 
893
                while (!vis(parent) && parent != document.body) {
894
                        toMeasure.push(parent.expose());
895
                        parent = parent.getParent();
896
                }
897
                var restore = this.expose();
898
                var result = fn.apply(this);
899
                restore();
900
                toMeasure.each(function(restore){
901
                        restore();
902
                });
903
                return result;
904
        },
905

    
906
        expose: function(){
907
                if (this.getStyle('display') != 'none') return $empty;
908
                var before = this.style.cssText;
909
                this.setStyles({
910
                        display: 'block',
911
                        position: 'absolute',
912
                        visibility: 'hidden'
913
                });
914
                return function(){
915
                        this.style.cssText = before;
916
                }.bind(this);
917
        },
918

    
919
        getDimensions: function(options){
920
                options = $merge({computeSize: false},options);
921
                var dim = {};
922
                var getSize = function(el, options){
923
                        return (options.computeSize)?el.getComputedSize(options):el.getSize();
924
                };
925
                var parent = this.getParent('body');
926
                if (parent && this.getStyle('display') == 'none'){
927
                        dim = this.measure(function(){
928
                                return getSize(this, options);
929
                        });
930
                } else if (parent){
931
                        try { //safari sometimes crashes here, so catch it
932
                                dim = getSize(this, options);
933
                        }catch(e){}
934
                } else {
935
                        dim = {x: 0, y: 0};
936
                }
937
                return $chk(dim.x) ? $extend(dim, {width: dim.x, height: dim.y}) : $extend(dim, {x: dim.width, y: dim.height});
938
        },
939

    
940
        getComputedSize: function(options){
941
                options = $merge({
942
                        styles: ['padding','border'],
943
                        plains: {
944
                                height: ['top','bottom'],
945
                                width: ['left','right']
946
                        },
947
                        mode: 'both'
948
                }, options);
949
                var size = {width: 0,height: 0};
950
                switch (options.mode){
951
                        case 'vertical':
952
                                delete size.width;
953
                                delete options.plains.width;
954
                                break;
955
                        case 'horizontal':
956
                                delete size.height;
957
                                delete options.plains.height;
958
                                break;
959
                }
960
                var getStyles = [];
961
                //this function might be useful in other places; perhaps it should be outside this function?
962
                $each(options.plains, function(plain, key){
963
                        plain.each(function(edge){
964
                                options.styles.each(function(style){
965
                                        getStyles.push((style == 'border') ? style + '-' + edge + '-' + 'width' : style + '-' + edge);
966
                                });
967
                        });
968
                });
969
                var styles = {};
970
                getStyles.each(function(style){ styles[style] = this.getComputedStyle(style); }, this);
971
                var subtracted = [];
972
                $each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom']
973
                        var capitalized = key.capitalize();
974
                        size['total' + capitalized] = size['computed' + capitalized] = 0;
975
                        plain.each(function(edge){ //top, left, right, bottom
976
                                size['computed' + edge.capitalize()] = 0;
977
                                getStyles.each(function(style, i){ //padding, border, etc.
978
                                        //'padding-left'.test('left') size['totalWidth'] = size['width'] + [padding-left]
979
                                        if (style.test(edge)){
980
                                                styles[style] = styles[style].toInt() || 0; //styles['padding-left'] = 5;
981
                                                size['total' + capitalized] = size['total' + capitalized] + styles[style];
982
                                                size['computed' + edge.capitalize()] = size['computed' + edge.capitalize()] + styles[style];
983
                                        }
984
                                        //if width != width (so, padding-left, for instance), then subtract that from the total
985
                                        if (style.test(edge) && key != style &&
986
                                                (style.test('border') || style.test('padding')) && !subtracted.contains(style)){
987
                                                subtracted.push(style);
988
                                                size['computed' + capitalized] = size['computed' + capitalized]-styles[style];
989
                                        }
990
                                });
991
                        });
992
                });
993

    
994
                ['Width', 'Height'].each(function(value){
995
                        var lower = value.toLowerCase();
996
                        if(!$chk(size[lower])) return;
997

    
998
                        size[lower] = size[lower] + this['offset' + value] + size['computed' + value];
999
                        size['total' + value] = size[lower] + size['total' + value];
1000
                        delete size['computed' + value];
1001
                }, this);
1002

    
1003
                return $extend(styles, size);
1004
        }
1005

    
1006
});
1007

    
1008
/*
1009
---
1010

1011
script: Element.Position.js
1012

1013
description: Extends the Element native object to include methods useful positioning elements relative to others.
1014

1015
license: MIT-style license
1016

1017
authors:
1018
- Aaron Newton
1019

1020
requires:
1021
- core:1.2.4/Element.Dimensions
1022
- /Element.Measure
1023

1024
provides: [Elements.Position]
1025

1026
...
1027
*/
1028

    
1029
(function(){
1030

    
1031
var original = Element.prototype.position;
1032

    
1033
Element.implement({
1034

    
1035
        position: function(options){
1036
                //call original position if the options are x/y values
1037
                if (options && ($defined(options.x) || $defined(options.y))) return original ? original.apply(this, arguments) : this;
1038
                $each(options||{}, function(v, k){ if (!$defined(v)) delete options[k]; });
1039
                options = $merge({
1040
                        // minimum: { x: 0, y: 0 },
1041
                        // maximum: { x: 0, y: 0},
1042
                        relativeTo: document.body,
1043
                        position: {
1044
                                x: 'center', //left, center, right
1045
                                y: 'center' //top, center, bottom
1046
                        },
1047
                        edge: false,
1048
                        offset: {x: 0, y: 0},
1049
                        returnPos: false,
1050
                        relFixedPosition: false,
1051
                        ignoreMargins: false,
1052
                        ignoreScroll: false,
1053
                        allowNegative: false
1054
                }, options);
1055
                //compute the offset of the parent positioned element if this element is in one
1056
                var parentOffset = {x: 0, y: 0}, 
1057
                                parentPositioned = false;
1058
                /* dollar around getOffsetParent should not be necessary, but as it does not return
1059
                 * a mootools extended element in IE, an error occurs on the call to expose. See:
1060
                 * http://mootools.lighthouseapp.com/projects/2706/tickets/333-element-getoffsetparent-inconsistency-between-ie-and-other-browsers */
1061
                var offsetParent = this.measure(function(){
1062
                        return document.id(this.getOffsetParent());
1063
                });
1064
                if (offsetParent && offsetParent != this.getDocument().body){
1065
                        parentOffset = offsetParent.measure(function(){
1066
                                return this.getPosition();
1067
                        });
1068
                        parentPositioned = offsetParent != document.id(options.relativeTo);
1069
                        options.offset.x = options.offset.x - parentOffset.x;
1070
                        options.offset.y = options.offset.y - parentOffset.y;
1071
                }
1072
                //upperRight, bottomRight, centerRight, upperLeft, bottomLeft, centerLeft
1073
                //topRight, topLeft, centerTop, centerBottom, center
1074
                var fixValue = function(option){
1075
                        if ($type(option) != 'string') return option;
1076
                        option = option.toLowerCase();
1077
                        var val = {};
1078
                        if (option.test('left')) val.x = 'left';
1079
                        else if (option.test('right')) val.x = 'right';
1080
                        else val.x = 'center';
1081
                        if (option.test('upper') || option.test('top')) val.y = 'top';
1082
                        else if (option.test('bottom')) val.y = 'bottom';
1083
                        else val.y = 'center';
1084
                        return val;
1085
                };
1086
                options.edge = fixValue(options.edge);
1087
                options.position = fixValue(options.position);
1088
                if (!options.edge){
1089
                        if (options.position.x == 'center' && options.position.y == 'center') options.edge = {x:'center', y:'center'};
1090
                        else options.edge = {x:'left', y:'top'};
1091
                }
1092

    
1093
                this.setStyle('position', 'absolute');
1094
                var rel = document.id(options.relativeTo) || document.body,
1095
                                calc = rel == document.body ? window.getScroll() : rel.getPosition(),
1096
                                top = calc.y, left = calc.x;
1097

    
1098
                var scrolls = rel.getScrolls();
1099
                top += scrolls.y;
1100
                left += scrolls.x;
1101

    
1102
                var dim = this.getDimensions({computeSize: true, styles:['padding', 'border','margin']});
1103
                var pos = {},
1104
                                prefY = options.offset.y,
1105
                                prefX = options.offset.x,
1106
                                winSize = window.getSize();
1107
                switch(options.position.x){
1108
                        case 'left':
1109
                                pos.x = left + prefX;
1110
                                break;
1111
                        case 'right':
1112
                                pos.x = left + prefX + rel.offsetWidth;
1113
                                break;
1114
                        default: //center
1115
                                pos.x = left + ((rel == document.body ? winSize.x : rel.offsetWidth)/2) + prefX;
1116
                                break;
1117
                }
1118
                switch(options.position.y){
1119
                        case 'top':
1120
                                pos.y = top + prefY;
1121
                                break;
1122
                        case 'bottom':
1123
                                pos.y = top + prefY + rel.offsetHeight;
1124
                                break;
1125
                        default: //center
1126
                                pos.y = top + ((rel == document.body ? winSize.y : rel.offsetHeight)/2) + prefY;
1127
                                break;
1128
                }
1129
                if (options.edge){
1130
                        var edgeOffset = {};
1131

    
1132
                        switch(options.edge.x){
1133
                                case 'left':
1134
                                        edgeOffset.x = 0;
1135
                                        break;
1136
                                case 'right':
1137
                                        edgeOffset.x = -dim.x-dim.computedRight-dim.computedLeft;
1138
                                        break;
1139
                                default: //center
1140
                                        edgeOffset.x = -(dim.totalWidth/2);
1141
                                        break;
1142
                        }
1143
                        switch(options.edge.y){
1144
                                case 'top':
1145
                                        edgeOffset.y = 0;
1146
                                        break;
1147
                                case 'bottom':
1148
                                        edgeOffset.y = -dim.y-dim.computedTop-dim.computedBottom;
1149
                                        break;
1150
                                default: //center
1151
                                        edgeOffset.y = -(dim.totalHeight/2);
1152
                                        break;
1153
                        }
1154
                        pos.x += edgeOffset.x;
1155
                        pos.y += edgeOffset.y;
1156
                }
1157
                pos = {
1158
                        left: ((pos.x >= 0 || parentPositioned || options.allowNegative) ? pos.x : 0).toInt(),
1159
                        top: ((pos.y >= 0 || parentPositioned || options.allowNegative) ? pos.y : 0).toInt()
1160
                };
1161
                var xy = {left: 'x', top: 'y'};
1162
                ['minimum', 'maximum'].each(function(minmax) {
1163
                        ['left', 'top'].each(function(lr) {
1164
                                var val = options[minmax] ? options[minmax][xy[lr]] : null;
1165
                                if (val != null && pos[lr] < val) pos[lr] = val;
1166
                        });
1167
                });
1168
                if (rel.getStyle('position') == 'fixed' || options.relFixedPosition){
1169
                        var winScroll = window.getScroll();
1170
                        pos.top+= winScroll.y;
1171
                        pos.left+= winScroll.x;
1172
                }
1173
                if (options.ignoreScroll) {
1174
                        var relScroll = rel.getScroll();
1175
                        pos.top-= relScroll.y;
1176
                        pos.left-= relScroll.x;
1177
                }
1178
                if (options.ignoreMargins) {
1179
                        pos.left += (
1180
                                options.edge.x == 'right' ? dim['margin-right'] : 
1181
                                options.edge.x == 'center' ? -dim['margin-left'] + ((dim['margin-right'] + dim['margin-left'])/2) : 
1182
                                        - dim['margin-left']
1183
                        );
1184
                        pos.top += (
1185
                                options.edge.y == 'bottom' ? dim['margin-bottom'] : 
1186
                                options.edge.y == 'center' ? -dim['margin-top'] + ((dim['margin-bottom'] + dim['margin-top'])/2) : 
1187
                                        - dim['margin-top']
1188
                        );
1189
                }
1190
                pos.left = Math.ceil(pos.left);
1191
                pos.top = Math.ceil(pos.top);
1192
                if (options.returnPos) return pos;
1193
                else this.setStyles(pos);
1194
                return this;
1195
        }
1196

    
1197
});
1198

    
1199
})();
1200

    
1201
/*
1202
---
1203

1204
script: Element.Shortcuts.js
1205

1206
description: Extends the Element native object to include some shortcut methods.
1207

1208
license: MIT-style license
1209

1210
authors:
1211
- Aaron Newton
1212

1213
requires:
1214
- core:1.2.4/Element.Style
1215
- /MooTools.More
1216

1217
provides: [Element.Shortcuts]
1218

1219
...
1220
*/
1221

    
1222
Element.implement({
1223

    
1224
        isDisplayed: function(){
1225
                return this.getStyle('display') != 'none';
1226
        },
1227

    
1228
        isVisible: function(){
1229
                var w = this.offsetWidth,
1230
                        h = this.offsetHeight;
1231
                return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.isDisplayed();
1232
        },
1233

    
1234
        toggle: function(){
1235
                return this[this.isDisplayed() ? 'hide' : 'show']();
1236
        },
1237

    
1238
        hide: function(){
1239
                var d;
1240
                try {
1241
                        // IE fails here if the element is not in the dom
1242
                        if ((d = this.getStyle('display')) == 'none') d = null;
1243
                } catch(e){}
1244
                
1245
                return this.store('originalDisplay', d || 'block').setStyle('display', 'none');
1246
        },
1247

    
1248
        show: function(display){
1249
                return this.setStyle('display', display || this.retrieve('originalDisplay') || 'block');
1250
        },
1251

    
1252
        swapClass: function(remove, add){
1253
                return this.removeClass(remove).addClass(add);
1254
        }
1255

    
1256
});
1257

    
1258

    
1259
/*
1260
---
1261

1262
script: Fx.Elements.js
1263

1264
description: Effect to change any number of CSS properties of any number of Elements.
1265

1266
license: MIT-style license
1267

1268
authors:
1269
- Valerio Proietti
1270

1271
requires:
1272
- core:1.2.4/Fx.CSS
1273
- /MooTools.More
1274

1275
provides: [Fx.Elements]
1276

1277
...
1278
*/
1279

    
1280
Fx.Elements = new Class({
1281

    
1282
        Extends: Fx.CSS,
1283

    
1284
        initialize: function(elements, options){
1285
                this.elements = this.subject = $$(elements);
1286
                this.parent(options);
1287
        },
1288

    
1289
        compute: function(from, to, delta){
1290
                var now = {};
1291
                for (var i in from){
1292
                        var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
1293
                        for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
1294
                }
1295
                return now;
1296
        },
1297

    
1298
        set: function(now){
1299
                for (var i in now){
1300
                        var iNow = now[i];
1301
                        for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
1302
                }
1303
                return this;
1304
        },
1305

    
1306
        start: function(obj){
1307
                if (!this.check(obj)) return this;
1308
                var from = {}, to = {};
1309
                for (var i in obj){
1310
                        var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
1311
                        for (var p in iProps){
1312
                                var parsed = this.prepare(this.elements[i], p, iProps[p]);
1313
                                iFrom[p] = parsed.from;
1314
                                iTo[p] = parsed.to;
1315
                        }
1316
                }
1317
                return this.parent(from, to);
1318
        }
1319

    
1320
});
1321

    
1322
/*
1323
---
1324

1325
script: Fx.Accordion.js
1326

1327
description: An Fx.Elements extension which allows you to easily create accordion type controls.
1328

1329
license: MIT-style license
1330

1331
authors:
1332
- Valerio Proietti
1333

1334
requires:
1335
- core:1.2.4/Element.Event
1336
- /Fx.Elements
1337

1338
provides: [Fx.Accordion]
1339

1340
...
1341
*/
1342

    
1343
var Accordion = Fx.Accordion = new Class({
1344

    
1345
        Extends: Fx.Elements,
1346

    
1347
        options: {/*
1348
                onActive: $empty(toggler, section),
1349
                onBackground: $empty(toggler, section),
1350
                fixedHeight: false,
1351
                fixedWidth: false,
1352
                */
1353
                display: 0,
1354
                show: false,
1355
                height: true,
1356
                width: false,
1357
                opacity: true,
1358
                alwaysHide: false,
1359
                trigger: 'click',
1360
                initialDisplayFx: true,
1361
                returnHeightToAuto: true
1362
        },
1363

    
1364
        initialize: function(){
1365
                var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});
1366
                this.parent(params.elements, params.options);
1367
                this.togglers = $$(params.togglers);
1368
                this.container = document.id(params.container);
1369
                this.previous = -1;
1370
                this.internalChain = new Chain();
1371
                if (this.options.alwaysHide) this.options.wait = true;
1372
                if ($chk(this.options.show)){
1373
                        this.options.display = false;
1374
                        this.previous = this.options.show;
1375
                }
1376
                if (this.options.start){
1377
                        this.options.display = false;
1378
                        this.options.show = false;
1379
                }
1380
                this.effects = {};
1381
                if (this.options.opacity) this.effects.opacity = 'fullOpacity';
1382
                if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
1383
                if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
1384
                for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
1385
                this.elements.each(function(el, i){
1386
                        if (this.options.show === i){
1387
                                this.fireEvent('active', [this.togglers[i], el]);
1388
                        } else {
1389
                                for (var fx in this.effects) el.setStyle(fx, 0);
1390
                        }
1391
                }, this);
1392
                if ($chk(this.options.display)) this.display(this.options.display, this.options.initialDisplayFx);
1393
                this.addEvent('complete', this.internalChain.callChain.bind(this.internalChain));
1394
        },
1395

    
1396
        addSection: function(toggler, element){
1397
                toggler = document.id(toggler);
1398
                element = document.id(element);
1399
                var test = this.togglers.contains(toggler);
1400
                this.togglers.include(toggler);
1401
                this.elements.include(element);
1402
                var idx = this.togglers.indexOf(toggler);
1403
                var displayer = this.display.bind(this, idx);
1404
                toggler.store('accordion:display', displayer);
1405
                toggler.addEvent(this.options.trigger, displayer);
1406
                if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
1407
                if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
1408
                element.fullOpacity = 1;
1409
                if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
1410
                if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
1411
                element.setStyle('overflow', 'hidden');
1412
                if (!test){
1413
                        for (var fx in this.effects) element.setStyle(fx, 0);
1414
                }
1415
                return this;
1416
        },
1417

    
1418
        detach: function(){
1419
                this.togglers.each(function(toggler) {
1420
                        toggler.removeEvent(this.options.trigger, toggler.retrieve('accordion:display'));
1421
                }, this);
1422
        },
1423

    
1424
        display: function(index, useFx){
1425
                if (!this.check(index, useFx)) return this;
1426
                useFx = $pick(useFx, true);
1427
                if (this.options.returnHeightToAuto){
1428
                        var prev = this.elements[this.previous];
1429
                        if (prev && !this.selfHidden){
1430
                                for (var fx in this.effects){
1431
                                        prev.setStyle(fx, prev[this.effects[fx]]);
1432
                                }
1433
                        }
1434
                }
1435
                index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
1436
                if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
1437
                this.previous = index;
1438
                var obj = {};
1439
                this.elements.each(function(el, i){
1440
                        obj[i] = {};
1441
                        var hide;
1442
                        if (i != index){
1443
                                hide = true;
1444
                        } else if (this.options.alwaysHide && ((el.offsetHeight > 0 && this.options.height) || el.offsetWidth > 0 && this.options.width)){
1445
                                hide = true;
1446
                                this.selfHidden = true;
1447
                        }
1448
                        this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
1449
                        for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
1450
                }, this);
1451
                this.internalChain.chain(function(){
1452
                        if (this.options.returnHeightToAuto && !this.selfHidden){
1453
                                var el = this.elements[index];
1454
                                if (el) el.setStyle('height', 'auto');
1455
                        };
1456
                }.bind(this));
1457
                return useFx ? this.start(obj) : this.set(obj);
1458
        }
1459

    
1460
});
1461

    
1462
/*
1463
---
1464

1465
script: Drag.js
1466

1467
description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
1468

1469
license: MIT-style license
1470

1471
authors:
1472
- Valerio Proietti
1473
- Tom Occhinno
1474
- Jan Kassens
1475

1476
requires:
1477
- core:1.2.4/Events
1478
- core:1.2.4/Options
1479
- core:1.2.4/Element.Event
1480
- core:1.2.4/Element.Style
1481
- /MooTools.More
1482

1483
provides: [Drag]
1484

1485
*/
1486

    
1487
var Drag = new Class({
1488

    
1489
        Implements: [Events, Options],
1490

    
1491
        options: {/*
1492
                onBeforeStart: $empty(thisElement),
1493
                onStart: $empty(thisElement, event),
1494
                onSnap: $empty(thisElement)
1495
                onDrag: $empty(thisElement, event),
1496
                onCancel: $empty(thisElement),
1497
                onComplete: $empty(thisElement, event),*/
1498
                snap: 6,
1499
                unit: 'px',
1500
                grid: false,
1501
                style: true,
1502
                limit: false,
1503
                handle: false,
1504
                invert: false,
1505
                preventDefault: false,
1506
                stopPropagation: false,
1507
                modifiers: {x: 'left', y: 'top'}
1508
        },
1509

    
1510
        initialize: function(){
1511
                var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
1512
                this.element = document.id(params.element);
1513
                this.document = this.element.getDocument();
1514
                this.setOptions(params.options || {});
1515
                var htype = $type(this.options.handle);
1516
                this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
1517
                this.mouse = {'now': {}, 'pos': {}};
1518
                this.value = {'start': {}, 'now': {}};
1519

    
1520
                this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
1521

    
1522
                this.bound = {
1523
                        start: this.start.bind(this),
1524
                        check: this.check.bind(this),
1525
                        drag: this.drag.bind(this),
1526
                        stop: this.stop.bind(this),
1527
                        cancel: this.cancel.bind(this),
1528
                        eventStop: $lambda(false)
1529
                };
1530
                this.attach();
1531
        },
1532

    
1533
        attach: function(){
1534
                this.handles.addEvent('mousedown', this.bound.start);
1535
                return this;
1536
        },
1537

    
1538
        detach: function(){
1539
                this.handles.removeEvent('mousedown', this.bound.start);
1540
                return this;
1541
        },
1542

    
1543
        start: function(event){
1544
                if (event.rightClick) return;
1545
                if (this.options.preventDefault) event.preventDefault();
1546
                if (this.options.stopPropagation) event.stopPropagation();
1547
                this.mouse.start = event.page;
1548
                this.fireEvent('beforeStart', this.element);
1549
                var limit = this.options.limit;
1550
                this.limit = {x: [], y: []};
1551
                for (var z in this.options.modifiers){
1552
                        if (!this.options.modifiers[z]) continue;
1553
                        if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
1554
                        else this.value.now[z] = this.element[this.options.modifiers[z]];
1555
                        if (this.options.invert) this.value.now[z] *= -1;
1556
                        this.mouse.pos[z] = event.page[z] - this.value.now[z];
1557
                        if (limit && limit[z]){
1558
                                for (var i = 2; i--; i){
1559
                                        if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
1560
                                }
1561
                        }
1562
                }
1563
                if ($type(this.options.grid) == 'number') this.options.grid = {x: this.options.grid, y: this.options.grid};
1564
                this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
1565
                this.document.addEvent(this.selection, this.bound.eventStop);
1566
        },
1567

    
1568
        check: function(event){
1569
                if (this.options.preventDefault) event.preventDefault();
1570
                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)));
1571
                if (distance > this.options.snap){
1572
                        this.cancel();
1573
                        this.document.addEvents({
1574
                                mousemove: this.bound.drag,
1575
                                mouseup: this.bound.stop
1576
                        });
1577
                        this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
1578
                }
1579
        },
1580

    
1581
        drag: function(event){
1582
                if (this.options.preventDefault) event.preventDefault();
1583
                this.mouse.now = event.page;
1584
                for (var z in this.options.modifiers){
1585
                        if (!this.options.modifiers[z]) continue;
1586
                        this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
1587
                        if (this.options.invert) this.value.now[z] *= -1;
1588
                        if (this.options.limit && this.limit[z]){
1589
                                if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
1590
                                        this.value.now[z] = this.limit[z][1];
1591
                                } else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
1592
                                        this.value.now[z] = this.limit[z][0];
1593
                                }
1594
                        }
1595
                        if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % this.options.grid[z]);
1596
                        if (this.options.style) {
1597
                                this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
1598
                        } else {
1599
                                this.element[this.options.modifiers[z]] = this.value.now[z];
1600
                        }
1601
                }
1602
                this.fireEvent('drag', [this.element, event]);
1603
        },
1604

    
1605
        cancel: function(event){
1606
                this.document.removeEvent('mousemove', this.bound.check);
1607
                this.document.removeEvent('mouseup', this.bound.cancel);
1608
                if (event){
1609
                        this.document.removeEvent(this.selection, this.bound.eventStop);
1610
                        this.fireEvent('cancel', this.element);
1611
                }
1612
        },
1613

    
1614
        stop: function(event){
1615
                this.document.removeEvent(this.selection, this.bound.eventStop);
1616
                this.document.removeEvent('mousemove', this.bound.drag);
1617
                this.document.removeEvent('mouseup', this.bound.stop);
1618
                if (event) this.fireEvent('complete', [this.element, event]);
1619
        }
1620

    
1621
});
1622

    
1623
Element.implement({
1624

    
1625
        makeResizable: function(options){
1626
                var drag = new Drag(this, $merge({modifiers: {x: 'width', y: 'height'}}, options));
1627
                this.store('resizer', drag);
1628
                return drag.addEvent('drag', function(){
1629
                        this.fireEvent('resize', drag);
1630
                }.bind(this));
1631
        }
1632

    
1633
});
1634

    
1635

    
1636
/*
1637
---
1638

1639
script: Drag.Move.js
1640

1641
description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
1642

1643
license: MIT-style license
1644

1645
authors:
1646
- Valerio Proietti
1647
- Tom Occhinno
1648
- Jan Kassens
1649
- Aaron Newton
1650
- Scott Kyle
1651

1652
requires:
1653
- core:1.2.4/Element.Dimensions
1654
- /Drag
1655

1656
provides: [Drag.Move]
1657

1658
...
1659
*/
1660

    
1661
Drag.Move = new Class({
1662

    
1663
        Extends: Drag,
1664

    
1665
        options: {/*
1666
                onEnter: $empty(thisElement, overed),
1667
                onLeave: $empty(thisElement, overed),
1668
                onDrop: $empty(thisElement, overed, event),*/
1669
                droppables: [],
1670
                container: false,
1671
                precalculate: false,
1672
                includeMargins: true,
1673
                checkDroppables: true
1674
        },
1675

    
1676
        initialize: function(element, options){
1677
                this.parent(element, options);
1678
                element = this.element;
1679
                
1680
                this.droppables = $$(this.options.droppables);
1681
                this.container = document.id(this.options.container);
1682
                
1683
                if (this.container && $type(this.container) != 'element')
1684
                        this.container = document.id(this.container.getDocument().body);
1685
                
1686
                var styles = element.getStyles('left', 'right', 'position');
1687
                if (styles.left == 'auto' || styles.top == 'auto')
1688
                        element.setPosition(element.getPosition(element.getOffsetParent()));
1689
                
1690
                if (styles.position == 'static')
1691
                        element.setStyle('position', 'absolute');
1692

    
1693
                this.addEvent('start', this.checkDroppables, true);
1694

    
1695
                this.overed = null;
1696
        },
1697

    
1698
        start: function(event){
1699
                if (this.container) this.options.limit = this.calculateLimit();
1700
                
1701
                if (this.options.precalculate){
1702
                        this.positions = this.droppables.map(function(el){
1703
                                return el.getCoordinates();
1704
                        });
1705
                }
1706
                
1707
                this.parent(event);
1708
        },
1709
        
1710
        calculateLimit: function(){
1711
                var offsetParent = this.element.getOffsetParent(),
1712
                        containerCoordinates = this.container.getCoordinates(offsetParent),
1713
                        containerBorder = {},
1714
                        elementMargin = {},
1715
                        elementBorder = {},
1716
                        containerMargin = {},
1717
                        offsetParentPadding = {};
1718

    
1719
                ['top', 'right', 'bottom', 'left'].each(function(pad){
1720
                        containerBorder[pad] = this.container.getStyle('border-' + pad).toInt();
1721
                        elementBorder[pad] = this.element.getStyle('border-' + pad).toInt();
1722
                        elementMargin[pad] = this.element.getStyle('margin-' + pad).toInt();
1723
                        containerMargin[pad] = this.container.getStyle('margin-' + pad).toInt();
1724
                        offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
1725
                }, this);
1726

    
1727
                var width = this.element.offsetWidth + elementMargin.left + elementMargin.right,
1728
                        height = this.element.offsetHeight + elementMargin.top + elementMargin.bottom,
1729
                        left = 0,
1730
                        top = 0,
1731
                        right = containerCoordinates.right - containerBorder.right - width,
1732
                        bottom = containerCoordinates.bottom - containerBorder.bottom - height;
1733

    
1734
                if (this.options.includeMargins){
1735
                        left += elementMargin.left;
1736
                        top += elementMargin.top;
1737
                } else {
1738
                        right += elementMargin.right;
1739
                        bottom += elementMargin.bottom;
1740
                }
1741
                
1742
                if (this.element.getStyle('position') == 'relative'){
1743
                        var coords = this.element.getCoordinates(offsetParent);
1744
                        coords.left -= this.element.getStyle('left').toInt();
1745
                        coords.top -= this.element.getStyle('top').toInt();
1746
                        
1747
                        left += containerBorder.left - coords.left;
1748
                        top += containerBorder.top - coords.top;
1749
                        right += elementMargin.left - coords.left;
1750
                        bottom += elementMargin.top - coords.top;
1751
                        
1752
                        if (this.container != offsetParent){
1753
                                left += containerMargin.left + offsetParentPadding.left;
1754
                                top += (Browser.Engine.trident4 ? 0 : containerMargin.top) + offsetParentPadding.top;
1755
                        }
1756
                } else {
1757
                        left -= elementMargin.left;
1758
                        top -= elementMargin.top;
1759
                        
1760
                        if (this.container == offsetParent){
1761
                                right -= containerBorder.left;
1762
                                bottom -= containerBorder.top;
1763
                        } else {
1764
                                left += containerCoordinates.left + containerBorder.left;
1765
                                top += containerCoordinates.top + containerBorder.top;
1766
                        }
1767
                }
1768
                
1769
                return {
1770
                        x: [left, right],
1771
                        y: [top, bottom]
1772
                };
1773
        },
1774

    
1775
        checkAgainst: function(el, i){
1776
                el = (this.positions) ? this.positions[i] : el.getCoordinates();
1777
                var now = this.mouse.now;
1778
                return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
1779
        },
1780

    
1781
        checkDroppables: function(){
1782
                var overed = this.droppables.filter(this.checkAgainst, this).getLast();
1783
                if (this.overed != overed){
1784
                        if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
1785
                        if (overed) this.fireEvent('enter', [this.element, overed]);
1786
                        this.overed = overed;
1787
                }
1788
        },
1789

    
1790
        drag: function(event){
1791
                this.parent(event);
1792
                if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
1793
        },
1794

    
1795
        stop: function(event){
1796
                this.checkDroppables();
1797
                this.fireEvent('drop', [this.element, this.overed, event]);
1798
                this.overed = null;
1799
                return this.parent(event);
1800
        }
1801

    
1802
});
1803

    
1804
Element.implement({
1805

    
1806
        makeDraggable: function(options){
1807
                var drag = new Drag.Move(this, options);
1808
                this.store('dragger', drag);
1809
                return drag;
1810
        }
1811

    
1812
});
1813

    
1814

    
1815
/*
1816
---
1817

1818
script: Request.Queue.js
1819

1820
description: Controls several instances of Request and its variants to run only one request at a time.
1821

1822
license: MIT-style license
1823

1824
authors:
1825
- Aaron Newton
1826

1827
requires:
1828
- core:1.2.4/Element
1829
- core:1.2.4/Request
1830
- /Log
1831

1832
provides: [Request.Queue]
1833

1834
...
1835
*/
1836

    
1837
Request.Queue = new Class({
1838

    
1839
        Implements: [Options, Events],
1840

    
1841
        Binds: ['attach', 'request', 'complete', 'cancel', 'success', 'failure', 'exception'],
1842

    
1843
        options: {/*
1844
                onRequest: $empty(argsPassedToOnRequest),
1845
                onSuccess: $empty(argsPassedToOnSuccess),
1846
                onComplete: $empty(argsPassedToOnComplete),
1847
                onCancel: $empty(argsPassedToOnCancel),
1848
                onException: $empty(argsPassedToOnException),
1849
                onFailure: $empty(argsPassedToOnFailure),
1850
                onEnd: $empty,
1851
                */
1852
                stopOnFailure: true,
1853
                autoAdvance: true,
1854
                concurrent: 1,
1855
                requests: {}
1856
        },
1857

    
1858
        initialize: function(options){
1859
                if(options){
1860
                        var requests = options.requests;
1861
                        delete options.requests;        
1862
                }
1863
                this.setOptions(options);
1864
                this.requests = new Hash;
1865
                this.queue = [];
1866
                this.reqBinders = {};
1867
                
1868
                if(requests) this.addRequests(requests);
1869
        },
1870

    
1871
        addRequest: function(name, request){
1872
                this.requests.set(name, request);
1873
                this.attach(name, request);
1874
                return this;
1875
        },
1876

    
1877
        addRequests: function(obj){
1878
                $each(obj, function(req, name){
1879
                        this.addRequest(name, req);
1880
                }, this);
1881
                return this;
1882
        },
1883

    
1884
        getName: function(req){
1885
                return this.requests.keyOf(req);
1886
        },
1887

    
1888
        attach: function(name, req){
1889
                if (req._groupSend) return this;
1890
                ['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){
1891
                        if(!this.reqBinders[name]) this.reqBinders[name] = {};
1892
                        this.reqBinders[name][evt] = function(){
1893
                                this['on' + evt.capitalize()].apply(this, [name, req].extend(arguments));
1894
                        }.bind(this);
1895
                        req.addEvent(evt, this.reqBinders[name][evt]);
1896
                }, this);
1897
                req._groupSend = req.send;
1898
                req.send = function(options){
1899
                        this.send(name, options);
1900
                        return req;
1901
                }.bind(this);
1902
                return this;
1903
        },
1904

    
1905
        removeRequest: function(req){
1906
                var name = $type(req) == 'object' ? this.getName(req) : req;
1907
                if (!name && $type(name) != 'string') return this;
1908
                req = this.requests.get(name);
1909
                if (!req) return this;
1910
                ['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){
1911
                        req.removeEvent(evt, this.reqBinders[name][evt]);
1912
                }, this);
1913
                req.send = req._groupSend;
1914
                delete req._groupSend;
1915
                return this;
1916
        },
1917

    
1918
        getRunning: function(){
1919
                return this.requests.filter(function(r){
1920
                        return r.running;
1921
                });
1922
        },
1923

    
1924
        isRunning: function(){
1925
                return !!(this.getRunning().getKeys().length);
1926
        },
1927

    
1928
        send: function(name, options){
1929
                var q = function(){
1930
                        this.requests.get(name)._groupSend(options);
1931
                        this.queue.erase(q);
1932
                }.bind(this);
1933
                q.name = name;
1934
                if (this.getRunning().getKeys().length >= this.options.concurrent || (this.error && this.options.stopOnFailure)) this.queue.push(q);
1935
                else q();
1936
                return this;
1937
        },
1938

    
1939
        hasNext: function(name){
1940
                return (!name) ? !!this.queue.length : !!this.queue.filter(function(q){ return q.name == name; }).length;
1941
        },
1942

    
1943
        resume: function(){
1944
                this.error = false;
1945
                (this.options.concurrent - this.getRunning().getKeys().length).times(this.runNext, this);
1946
                return this;
1947
        },
1948

    
1949
        runNext: function(name){
1950
                if (!this.queue.length) return this;
1951
                if (!name){
1952
                        this.queue[0]();
1953
                } else {
1954
                        var found;
1955
                        this.queue.each(function(q){
1956
                                if (!found && q.name == name){
1957
                                        found = true;
1958
                                        q();
1959
                                }
1960
                        });
1961
                }
1962
                return this;
1963
        },
1964

    
1965
        runAll: function() {
1966
                this.queue.each(function(q) {
1967
                        q();
1968
                });
1969
                return this;
1970
        },
1971

    
1972
        clear: function(name){
1973
                if (!name){
1974
                        this.queue.empty();
1975
                } else {
1976
                        this.queue = this.queue.map(function(q){
1977
                                if (q.name != name) return q;
1978
                                else return false;
1979
                        }).filter(function(q){ return q; });
1980
                }
1981
                return this;
1982
        },
1983

    
1984
        cancel: function(name){
1985
                this.requests.get(name).cancel();
1986
                return this;
1987
        },
1988

    
1989
        onRequest: function(){
1990
                this.fireEvent('request', arguments);
1991
        },
1992

    
1993
        onComplete: function(){
1994
                this.fireEvent('complete', arguments);
1995
                if (!this.queue.length) this.fireEvent('end');
1996
        },
1997

    
1998
        onCancel: function(){
1999
                if (this.options.autoAdvance && !this.error) this.runNext();
2000
                this.fireEvent('cancel', arguments);
2001
        },
2002

    
2003
        onSuccess: function(){
2004
                if (this.options.autoAdvance && !this.error) this.runNext();
2005
                this.fireEvent('success', arguments);
2006
        },
2007

    
2008
        onFailure: function(){
2009
                this.error = true;
2010
                if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext();
2011
                this.fireEvent('failure', arguments);
2012
        },
2013

    
2014
        onException: function(){
2015
                this.error = true;
2016
                if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext();
2017
                this.fireEvent('exception', arguments);
2018
        }
2019

    
2020
});
2021

    
2022

    
2023
/*
2024
---
2025

2026
script: Assets.js
2027

2028
description: Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
2029

2030
license: MIT-style license
2031

2032
authors:
2033
- Valerio Proietti
2034

2035
requires:
2036
- core:1.2.4/Element.Event
2037
- /MooTools.More
2038

2039
provides: [Assets]
2040

2041
...
2042
*/
2043

    
2044
var Asset = {
2045

    
2046
        javascript: function(source, properties){
2047
                properties = $extend({
2048
                        onload: $empty,
2049
                        document: document,
2050
                        check: $lambda(true)
2051
                }, properties);
2052

    
2053
                var script = new Element('script', {src: source, type: 'text/javascript'});
2054

    
2055
                var load = properties.onload.bind(script), 
2056
                        check = properties.check, 
2057
                        doc = properties.document;
2058
                delete properties.onload;
2059
                delete properties.check;
2060
                delete properties.document;
2061

    
2062
                script.addEvents({
2063
                        load: load,
2064
                        readystatechange: function(){
2065
                                if (['loaded', 'complete'].contains(this.readyState)) load();
2066
                        }
2067
                }).set(properties);
2068

    
2069
                if (Browser.Engine.webkit419) var checker = (function(){
2070
                        if (!$try(check)) return;
2071
                        $clear(checker);
2072
                        load();
2073
                }).periodical(50);
2074

    
2075
                return script.inject(doc.head);
2076
        },
2077

    
2078
        css: function(source, properties){
2079
                return new Element('link', $merge({
2080
                        rel: 'stylesheet',
2081
                        media: 'screen',
2082
                        type: 'text/css',
2083
                        href: source
2084
                }, properties)).inject(document.head);
2085
        },
2086

    
2087
        image: function(source, properties){
2088
                properties = $merge({
2089
                        onload: $empty,
2090
                        onabort: $empty,
2091
                        onerror: $empty
2092
                }, properties);
2093
                var image = new Image();
2094
                var element = document.id(image) || new Element('img');
2095
                ['load', 'abort', 'error'].each(function(name){
2096
                        var type = 'on' + name;
2097
                        var event = properties[type];
2098
                        delete properties[type];
2099
                        image[type] = function(){
2100
                                if (!image) return;
2101
                                if (!element.parentNode){
2102
                                        element.width = image.width;
2103
                                        element.height = image.height;
2104
                                }
2105
                                image = image.onload = image.onabort = image.onerror = null;
2106
                                event.delay(1, element, element);
2107
                                element.fireEvent(name, element, 1);
2108
                        };
2109
                });
2110
                image.src = element.src = source;
2111
                if (image && image.complete) image.onload.delay(1);
2112
                return element.set(properties);
2113
        },
2114

    
2115
        images: function(sources, options){
2116
                options = $merge({
2117
                        onComplete: $empty,
2118
                        onProgress: $empty,
2119
                        onError: $empty,
2120
                        properties: {}
2121
                }, options);
2122
                sources = $splat(sources);
2123
                var images = [];
2124
                var counter = 0;
2125
                return new Elements(sources.map(function(source){
2126
                        return Asset.image(source, $extend(options.properties, {
2127
                                onload: function(){
2128
                                        options.onProgress.call(this, counter, sources.indexOf(source));
2129
                                        counter++;
2130
                                        if (counter == sources.length) options.onComplete();
2131
                                },
2132
                                onerror: function(){
2133
                                        options.onError.call(this, counter, sources.indexOf(source));
2134
                                        counter++;
2135
                                        if (counter == sources.length) options.onComplete();
2136
                                }
2137
                        }));
2138
                }));
2139
        }
2140

    
2141
};
2142

    
2143
/*
2144
---
2145

2146
script: Group.js
2147

2148
description: Class for monitoring collections of events
2149

2150
license: MIT-style license
2151

2152
authors:
2153
- Valerio Proietti
2154

2155
requires:
2156
- core:1.2.4/Events
2157
- /MooTools.More
2158

2159
provides: [Group]
2160

2161
...
2162
*/
2163

    
2164
var Group = new Class({
2165

    
2166
        initialize: function(){
2167
                this.instances = Array.flatten(arguments);
2168
                this.events = {};
2169
                this.checker = {};
2170
        },
2171

    
2172
        addEvent: function(type, fn){
2173
                this.checker[type] = this.checker[type] || {};
2174
                this.events[type] = this.events[type] || [];
2175
                if (this.events[type].contains(fn)) return false;
2176
                else this.events[type].push(fn);
2177
                this.instances.each(function(instance, i){
2178
                        instance.addEvent(type, this.check.bind(this, [type, instance, i]));
2179
                }, this);
2180
                return this;
2181
        },
2182

    
2183
        check: function(type, instance, i){
2184
                this.checker[type][i] = true;
2185
                var every = this.instances.every(function(current, j){
2186
                        return this.checker[type][j] || false;
2187
                }, this);
2188
                if (!every) return;
2189
                this.checker[type] = {};
2190
                this.events[type].each(function(event){
2191
                        event.call(this, this.instances, instance);
2192
                }, this);
2193
        }
2194

    
2195
});
2196

    
2197

    
2198
/*
2199
---
2200

2201
script: Tips.js
2202

2203
description: Class for creating nice tips that follow the mouse cursor when hovering an element.
2204

2205
license: MIT-style license
2206

2207
authors:
2208
- Valerio Proietti
2209
- Christoph Pojer
2210

2211
requires:
2212
- core:1.2.4/Options
2213
- core:1.2.4/Events
2214
- core:1.2.4/Element.Event
2215
- core:1.2.4/Element.Style
2216
- core:1.2.4/Element.Dimensions
2217
- /MooTools.More
2218

2219
provides: [Tips]
2220

2221
...
2222
*/
2223

    
2224
(function(){
2225

    
2226
var read = function(option, element){
2227
        return (option) ? ($type(option) == 'function' ? option(element) : element.get(option)) : '';
2228
};
2229

    
2230
this.Tips = new Class({
2231

    
2232
        Implements: [Events, Options],
2233

    
2234
        options: {
2235
                /*
2236
                onAttach: $empty(element),
2237
                onDetach: $empty(element),
2238
                */
2239
                onShow: function(){
2240
                        this.tip.setStyle('display', 'block');
2241
                },
2242
                onHide: function(){
2243
                        this.tip.setStyle('display', 'none');
2244
                },
2245
                title: 'title',
2246
                text: function(element){
2247
                        return element.get('rel') || element.get('href');
2248
                },
2249
                showDelay: 100,
2250
                hideDelay: 100,
2251
                className: 'tip-wrap',
2252
                offset: {x: 16, y: 16},
2253
                fixed: false
2254
        },
2255

    
2256
        initialize: function(){
2257
                var params = Array.link(arguments, {options: Object.type, elements: $defined});
2258
                this.setOptions(params.options);
2259
                document.id(this);
2260
                
2261
                if (params.elements) this.attach(params.elements);
2262
        },
2263

    
2264
        toElement: function(){
2265
                if (this.tip) return this.tip;
2266
                
2267
                this.container = new Element('div', {'class': 'tip'});
2268
                return this.tip = new Element('div', {
2269
                        'class': this.options.className,
2270
                        styles: {
2271
                                position: 'absolute',
2272
                                top: 0,
2273
                                left: 0
2274
                        }
2275
                }).adopt(
2276
                        new Element('div', {'class': 'tip-top'}),
2277
                        this.container,
2278
                        new Element('div', {'class': 'tip-bottom'})
2279
                ).inject(document.body);
2280
        },
2281

    
2282
        attach: function(elements){
2283
                $$(elements).each(function(element){
2284
                        var title = read(this.options.title, element),
2285
                                text = read(this.options.text, element);
2286
                        
2287
                        element.erase('title').store('tip:native', title).retrieve('tip:title', title);
2288
                        element.retrieve('tip:text', text);
2289
                        this.fireEvent('attach', [element]);
2290
                        
2291
                        var events = ['enter', 'leave'];
2292
                        if (!this.options.fixed) events.push('move');
2293
                        
2294
                        events.each(function(value){
2295
                                var event = element.retrieve('tip:' + value);
2296
                                if (!event) event = this['element' + value.capitalize()].bindWithEvent(this, element);
2297
                                
2298
                                element.store('tip:' + value, event).addEvent('mouse' + value, event);
2299
                        }, this);
2300
                }, this);
2301
                
2302
                return this;
2303
        },
2304

    
2305
        detach: function(elements){
2306
                $$(elements).each(function(element){
2307
                        ['enter', 'leave', 'move'].each(function(value){
2308
                                element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value);
2309
                        });
2310
                        
2311
                        this.fireEvent('detach', [element]);
2312
                        
2313
                        if (this.options.title == 'title'){ // This is necessary to check if we can revert the title
2314
                                var original = element.retrieve('tip:native');
2315
                                if (original) element.set('title', original);
2316
                        }
2317
                }, this);
2318
                
2319
                return this;
2320
        },
2321

    
2322
        elementEnter: function(event, element){
2323
                this.container.empty();
2324
                
2325
                ['title', 'text'].each(function(value){
2326
                        var content = element.retrieve('tip:' + value);
2327
                        if (content) this.fill(new Element('div', {'class': 'tip-' + value}).inject(this.container), content);
2328
                }, this);
2329
                
2330
                $clear(this.timer);
2331
                this.timer = this.show.delay(this.options.showDelay, this, element);
2332
                this.position((this.options.fixed) ? {page: element.getPosition()} : event);
2333
        },
2334

    
2335
        elementLeave: function(event, element){
2336
                $clear(this.timer);
2337
                this.timer = this.hide.delay(this.options.hideDelay, this, element);
2338
                this.fireForParent(event, element);
2339
        },
2340

    
2341
        fireForParent: function(event, element){
2342
                if (!element) return;
2343
                parentNode = element.getParent();
2344
                if (parentNode == document.body) return;
2345
                if (parentNode.retrieve('tip:enter')) parentNode.fireEvent('mouseenter', event);
2346
                else this.fireForParent(parentNode, event);
2347
        },
2348

    
2349
        elementMove: function(event, element){
2350
                this.position(event);
2351
        },
2352

    
2353
        position: function(event){
2354
                var size = window.getSize(), scroll = window.getScroll(),
2355
                        tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight},
2356
                        props = {x: 'left', y: 'top'},
2357
                        obj = {};
2358
                
2359
                for (var z in props){
2360
                        obj[props[z]] = event.page[z] + this.options.offset[z];
2361
                        if ((obj[props[z]] + tip[z] - scroll[z]) > size[z]) obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
2362
                }
2363
                
2364
                this.tip.setStyles(obj);
2365
        },
2366

    
2367
        fill: function(element, contents){
2368
                if(typeof contents == 'string') element.set('html', contents);
2369
                else element.adopt(contents);
2370
        },
2371

    
2372
        show: function(element){
2373
                this.fireEvent('show', [this.tip, element]);
2374
        },
2375

    
2376
        hide: function(element){
2377
                this.fireEvent('hide', [this.tip, element]);
2378
        }
2379

    
2380
});
2381

    
2382
})();
2383

    
2384
/*
2385
---
2386

2387
script: Date.English.US.js
2388

2389
description: Date messages for US English.
2390

2391
license: MIT-style license
2392

2393
authors:
2394
- Aaron Newton
2395

2396
requires:
2397
- /Lang
2398
- /Date
2399

2400
provides: [Date.English.US]
2401

2402
...
2403
*/
2404

    
2405
MooTools.lang.set('en-US', 'Date', {
2406

    
2407
        months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
2408
        days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
2409
        //culture's date order: MM/DD/YYYY
2410
        dateOrder: ['month', 'date', 'year'],
2411
        shortDate: '%m/%d/%Y',
2412
        shortTime: '%I:%M%p',
2413
        AM: 'AM',
2414
        PM: 'PM',
2415

    
2416
        /* Date.Extras */
2417
        ordinal: function(dayOfMonth){
2418
                //1st, 2nd, 3rd, etc.
2419
                return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)];
2420
        },
2421

    
2422
        lessThanMinuteAgo: 'less than a minute ago',
2423
        minuteAgo: 'about a minute ago',
2424
        minutesAgo: '{delta} minutes ago',
2425
        hourAgo: 'about an hour ago',
2426
        hoursAgo: 'about {delta} hours ago',
2427
        dayAgo: '1 day ago',
2428
        daysAgo: '{delta} days ago',
2429
        weekAgo: '1 week ago',
2430
        weeksAgo: '{delta} weeks ago',
2431
        monthAgo: '1 month ago',
2432
        monthsAgo: '{delta} months ago',
2433
        yearAgo: '1 year ago',
2434
        yearsAgo: '{delta} years ago',
2435
        lessThanMinuteUntil: 'less than a minute from now',
2436
        minuteUntil: 'about a minute from now',
2437
        minutesUntil: '{delta} minutes from now',
2438
        hourUntil: 'about an hour from now',
2439
        hoursUntil: 'about {delta} hours from now',
2440
        dayUntil: '1 day from now',
2441
        daysUntil: '{delta} days from now',
2442
        weekUntil: '1 week from now',
2443
        weeksUntil: '{delta} weeks from now',
2444
        monthUntil: '1 month from now',
2445
        monthsUntil: '{delta} months from now',
2446
        yearUntil: '1 year from now',
2447
        yearsUntil: '{delta} years from now'
2448

    
2449
});