/loggerhead/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/loggerhead/trunk

« back to all changes in this revision

Viewing changes to loggerhead/static/javascript/yui/build/anim/anim-base-debug.js

  • Committer: Martin Albisetti
  • Date: 2008-10-22 11:05:23 UTC
  • mfrom: (230 trunk)
  • mto: This revision was merged to the branch mainline in revision 233.
  • Revision ID: martin.albisetti@canonical.com-20081022110523-sb3mjicez8ktsc3x
MergeĀ fromĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
 
3
Code licensed under the BSD License:
 
4
http://developer.yahoo.net/yui/license.txt
 
5
version: 3.0.0pr1
 
6
*/
 
7
YUI.add('anim-base', function(Y) {
 
8
 
 
9
/**
 
10
 * Y.Animation Utility.
 
11
 * @module anim
 
12
 */
 
13
 
 
14
    /**
 
15
     * Handles animation _queueing and threading.
 
16
     * @class Anim
 
17
     * @constructor
 
18
     * @extends Base
 
19
     */
 
20
 
 
21
    var RUNNING = 'running',
 
22
        START_TIME = 'startTime',
 
23
        ELAPSED_TIME = 'elapsedTime',
 
24
        /**
 
25
        * @event start
 
26
        * @description fires when an animation begins.
 
27
        * @param {Event} ev The start event.
 
28
        * @type Event.Custom
 
29
        */
 
30
        START = 'start',
 
31
 
 
32
        /**
 
33
        * @event tween
 
34
        * @description fires every frame of the animation.
 
35
        * @param {Event} ev The tween event.
 
36
        * @type Event.Custom
 
37
        */
 
38
        TWEEN = 'tween',
 
39
 
 
40
        /**
 
41
        * @event end
 
42
        * @description fires after the animation completes.
 
43
        * @param {Event} ev The end event.
 
44
        * @type Event.Custom
 
45
        */
 
46
        END = 'end',
 
47
        NODE = 'node',
 
48
        PAUSED = 'paused',
 
49
        REVERSE = 'reverse', // TODO: cleanup
 
50
        ITERATION_COUNT = 'iterationCount',
 
51
 
 
52
        NUM = Number;
 
53
 
 
54
    var _running = {},
 
55
        _instances = {},
 
56
        _timer;
 
57
 
 
58
    var _setPrivate = function(anim, prop, val) {
 
59
        if (typeof prop == 'string') {
 
60
            anim._conf.add(prop, { value: val });
 
61
        } else {
 
62
            Y.each(prop, function(v, n) {
 
63
                _setPrivate(anim, n, v);
 
64
            });
 
65
        }
 
66
    };
 
67
    Y.Anim = function() {
 
68
        Y.Anim.superclass.constructor.apply(this, arguments);
 
69
        _instances[Y.stamp(this)] = this;
 
70
    };
 
71
 
 
72
    Y.Anim.NAME = 'anim';
 
73
 
 
74
    /**
 
75
     * Regex of properties that should use the default unit.
 
76
     *
 
77
     * @property RE_DEFAULT_UNIT
 
78
     * @static
 
79
     */
 
80
    Y.Anim.RE_DEFAULT_UNIT = /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i;
 
81
 
 
82
    /**
 
83
     * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
 
84
     *
 
85
     * @property DEFAULT_UNIT
 
86
     * @static
 
87
     */
 
88
    Y.Anim.DEFAULT_UNIT = 'px';
 
89
 
 
90
 
 
91
    /**
 
92
     * Bucket for custom getters and setters
 
93
     *
 
94
     * @property behaviors
 
95
     * @static
 
96
     */
 
97
    Y.Anim.behaviors = {
 
98
        left: {
 
99
            get: function(anim, attr) {
 
100
                return anim._getOffset(attr);
 
101
            }
 
102
        }
 
103
    };
 
104
 
 
105
    Y.Anim.behaviors.top = Y.Anim.behaviors.left;
 
106
 
 
107
    /**
 
108
     * The default setter to use when setting object properties.
 
109
     *
 
110
     * @property DEFAULT_SETTER
 
111
     * @static
 
112
     */
 
113
    Y.Anim.DEFAULT_SETTER = function(anim, att, from, to, elapsed, duration, fn, unit) {
 
114
        unit = unit || '';
 
115
        anim._node.setStyle(att, fn(elapsed, NUM(from), NUM(to) - NUM(from), duration) + unit);
 
116
    };
 
117
 
 
118
    /**
 
119
     * The default getter to use when getting object properties.
 
120
     *
 
121
     * @property DEFAULT_GETTER
 
122
     * @static
 
123
     */
 
124
    Y.Anim.DEFAULT_GETTER = function(anim, prop) {
 
125
        return anim._node.getComputedStyle(prop);
 
126
    };
 
127
 
 
128
    Y.Anim.ATTRS = {
 
129
        /**
 
130
         * The object to be animated.
 
131
         * @attribute node
 
132
         * @type Node
 
133
         */
 
134
        node: {
 
135
            set: function(node) {
 
136
                node = Y.Node.get(node);
 
137
                this._node = node;
 
138
                if (!node) {
 
139
                    Y.fail('Y.Anim: invalid node: ' + node);
 
140
                }
 
141
                return node;
 
142
            }
 
143
        },
 
144
 
 
145
        /**
 
146
         * The length of the animation.  Defaults to "1" (second).
 
147
         * @attribute duration
 
148
         * @type NUM
 
149
         */
 
150
        duration: {
 
151
            value: 1
 
152
        },
 
153
 
 
154
        /**
 
155
         * The method that will provide values to the attribute(s) during the animation. 
 
156
         * Defaults to "Easing.easeNone".
 
157
         * @attribute easing
 
158
         * @type Function
 
159
         */
 
160
        easing: {
 
161
            value: function (t, b, c, d) {
 
162
                return c * t / d + b; // linear easing
 
163
            }
 
164
        },
 
165
 
 
166
        /**
 
167
         * The starting values for the animated properties. 
 
168
         * Fields may be strings, numbers, or functions.
 
169
         * If a function is used, the return value becomes the from value.
 
170
         * If no from value is specified, the DEFAULT_GETTER will be used. 
 
171
         * @attribute from
 
172
         * @type Object
 
173
         */
 
174
        from: {},
 
175
 
 
176
        /**
 
177
         * The ending values for the animated properties. 
 
178
         * Fields may be strings, numbers, or functions.
 
179
         * @attribute to
 
180
         * @type Object
 
181
         */
 
182
        to: {},
 
183
 
 
184
        /**
 
185
         * Date stamp for the first frame of the animation.
 
186
         * @attribute startTime
 
187
         * @type Int
 
188
         * @default 0 
 
189
         * @readOnly
 
190
         */
 
191
        startTime: {
 
192
            value: 0,
 
193
            readOnly: true
 
194
        },
 
195
 
 
196
        /**
 
197
         * Current time the animation has been running.
 
198
         * @attribute elapsedTime
 
199
         * @type Int
 
200
         * @default 0 
 
201
         * @readOnly
 
202
         */
 
203
        elapsedTime: {
 
204
            value: 0,
 
205
            readOnly: true
 
206
        },
 
207
 
 
208
        /**
 
209
         * Whether or not the animation is currently running.
 
210
         * @attribute running 
 
211
         * @type Boolean
 
212
         * @default false 
 
213
         * @readOnly
 
214
         */
 
215
        running: {
 
216
            get: function() {
 
217
                return !!_running[Y.stamp(this)];
 
218
            },
 
219
            value: false,
 
220
            readOnly: true
 
221
        },
 
222
 
 
223
        /**
 
224
         * The number of times the animation should run 
 
225
         * @attribute iterations
 
226
         * @type Int
 
227
         * @default 1 
 
228
         */
 
229
        iterations: {
 
230
            value: 1
 
231
        },
 
232
 
 
233
        /**
 
234
         * The number of iterations that have occurred.
 
235
         * Resets when an animation ends (reaches iteration count or stop() called). 
 
236
         * @attribute iterationCount
 
237
         * @type Int
 
238
         * @default 0
 
239
         * @readOnly
 
240
         */
 
241
        iterationCount: {
 
242
            value: 0,
 
243
            readOnly: true
 
244
        },
 
245
 
 
246
        /**
 
247
         * How iterations of the animation should behave. 
 
248
         * Possible values are "normal" and "alternate".
 
249
         * Normal will repeat the animation, alternate will reverse on every other pass.
 
250
         *
 
251
         * @attribute direction
 
252
         * @type String
 
253
         * @default "normal"
 
254
         */
 
255
        direction: {
 
256
            value: 'normal' // | alternate (fwd on odd, rev on even per spec)
 
257
        },
 
258
 
 
259
        /**
 
260
         * Whether or not the animation is currently paused.
 
261
         * @attribute running 
 
262
         * @type Boolean
 
263
         * @default false 
 
264
         * @readOnly
 
265
         */
 
266
        paused: {
 
267
            readOnly: true,
 
268
            value: false
 
269
        },
 
270
 
 
271
        /**
 
272
         * If true, animation begins from last frame
 
273
         * @attribute reverse
 
274
         * @type Boolean
 
275
         * @default false 
 
276
         */
 
277
        reverse: {
 
278
            value: false
 
279
        }
 
280
 
 
281
 
 
282
    };
 
283
 
 
284
    /**
 
285
     * Runs all animation instances.
 
286
     * @method run
 
287
     * @static
 
288
     */    
 
289
    Y.Anim.run = function() {
 
290
        for (var i in _instances) {
 
291
            if (_instances[i].run) {
 
292
                _instances[i].run();
 
293
            }
 
294
        }
 
295
    };
 
296
 
 
297
    /**
 
298
     * Pauses all animation instances.
 
299
     * @method pause
 
300
     * @static
 
301
     */    
 
302
    Y.Anim.pause = function() {
 
303
        for (var i in _running) { // stop timer if nothing running
 
304
            if (_running[i].pause) {
 
305
                _running[i].pause();
 
306
            }
 
307
        }
 
308
        Y.Anim._stopTimer();
 
309
    };
 
310
 
 
311
    /**
 
312
     * Stops all animation instances.
 
313
     * @method stop
 
314
     * @static
 
315
     */    
 
316
    Y.Anim.stop = function() {
 
317
        for (var i in _running) { // stop timer if nothing running
 
318
            if (_running[i].stop) {
 
319
                _running[i].stop();
 
320
            }
 
321
        }
 
322
        Y.Anim._stopTimer();
 
323
    };
 
324
    
 
325
    Y.Anim._startTimer = function() {
 
326
        if (!_timer) {
 
327
            _timer = setInterval(Y.Anim._runFrame, 1);
 
328
        }
 
329
    };
 
330
 
 
331
    Y.Anim._stopTimer = function() {
 
332
        clearInterval(_timer);
 
333
        _timer = 0;
 
334
    };
 
335
 
 
336
    /**
 
337
     * Called per Interval to handle each animation frame.
 
338
     * @method _runFrame
 
339
     * @private
 
340
     * @static
 
341
     */    
 
342
    Y.Anim._runFrame = function() {
 
343
        var done = true;
 
344
        for (var anim in _running) {
 
345
            if (_running[anim]._runFrame) {
 
346
                done = false;
 
347
                _running[anim]._runFrame();
 
348
            }
 
349
        }
 
350
 
 
351
        if (done) {
 
352
            Y.Anim._stopTimer();
 
353
        }
 
354
    };
 
355
 
 
356
    Y.Anim.RE_UNITS = /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;
 
357
 
 
358
    var proto = {
 
359
        /**
 
360
         * Starts or resumes an animation.
 
361
         * percent start time marker.
 
362
         * @method run
 
363
         * @chainable
 
364
         */    
 
365
        run: function() {
 
366
            if (!this.get(RUNNING)) {
 
367
                this._start();
 
368
            } else if (this.get(PAUSED)) {
 
369
                this._resume();
 
370
            }
 
371
            return this;
 
372
        },
 
373
 
 
374
        /**
 
375
         * Pauses the animation and
 
376
         * freezes it in its current state and time.
 
377
         * Calling run() will continue where it left off.
 
378
         * @method pause
 
379
         * @chainable
 
380
         */    
 
381
        pause: function() {
 
382
            if (this.get(RUNNING)) {
 
383
                this._pause();
 
384
            }
 
385
            return this;
 
386
        },
 
387
 
 
388
        /**
 
389
         * Stops the animation and resets its time.
 
390
         * @method stop
 
391
         * @chainable
 
392
         */    
 
393
        stop: function(finish) {
 
394
            if (this.get(RUNNING) || this.get(PAUSED)) {
 
395
                this._end(finish);
 
396
            }
 
397
            return this;
 
398
        },
 
399
 
 
400
        _added: false,
 
401
 
 
402
        _start: function() {
 
403
            _setPrivate(this, START_TIME, new Date() - this.get(ELAPSED_TIME));
 
404
            this._actualFrames = 0;
 
405
            if (!this.get(PAUSED)) {
 
406
                this._initAttr();
 
407
            }
 
408
            _running[Y.stamp(this)] = this;
 
409
            Y.Anim._startTimer();
 
410
 
 
411
            this.fire(START);
 
412
        },
 
413
 
 
414
        _pause: function() {
 
415
            _setPrivate(this, START_TIME, null);
 
416
            _setPrivate(this, PAUSED, true);
 
417
            delete _running[Y.stamp(this)];
 
418
 
 
419
            /**
 
420
            * @event pause
 
421
            * @description fires when an animation is paused.
 
422
            * @param {Event} ev The pause event.
 
423
            * @type Event.Custom
 
424
            */
 
425
            this.fire('pause');
 
426
        },
 
427
 
 
428
        _resume: function() {
 
429
            _setPrivate(this, PAUSED, false);
 
430
            _running[Y.stamp(this)] = this;
 
431
 
 
432
            /**
 
433
            * @event resume
 
434
            * @description fires when an animation is resumed (run from pause).
 
435
            * @param {Event} ev The pause event.
 
436
            * @type Event.Custom
 
437
            */
 
438
            this.fire('resume');
 
439
        },
 
440
 
 
441
        _end: function(finish) {
 
442
            _setPrivate(this, START_TIME, null);
 
443
            _setPrivate(this, ELAPSED_TIME, 0);
 
444
            _setPrivate(this, PAUSED, false);
 
445
            //_setPrivate(this, REVERSE, false);
 
446
 
 
447
            delete _running[Y.stamp(this)];
 
448
            this.fire(END, {elapsed: this.get(ELAPSED_TIME)});
 
449
        },
 
450
 
 
451
        _runFrame: function() {
 
452
            var attr = this._runtimeAttr,
 
453
                customAttr = Y.Anim.behaviors,
 
454
                easing = attr.easing,
 
455
                d = attr.duration,
 
456
                t = new Date() - this.get(START_TIME),
 
457
                reversed = this.get(REVERSE),
 
458
                done = (t >= d),
 
459
                lastFrame = d,
 
460
                attribute,
 
461
                setter;
 
462
                
 
463
            if (reversed) {
 
464
                t = d - t;
 
465
                done = (t <= 0);
 
466
                lastFrame = 0;
 
467
            }
 
468
 
 
469
            for (var i in attr) {
 
470
                if (attr[i].to) {
 
471
                    attribute = attr[i];
 
472
                    setter = (i in customAttr && 'set' in customAttr[i]) ?
 
473
                            customAttr[i].set : Y.Anim.DEFAULT_SETTER;
 
474
 
 
475
                    if (!done) {
 
476
                        setter(this, i, attribute.from, attribute.to, t, d, easing, attribute.unit); 
 
477
                    } else { // ensure final frame value is set
 
478
                       // TODO: handle keyframes 
 
479
                        setter(this, i, attribute.from, attribute.to, lastFrame, d, easing, attribute.unit); 
 
480
                    }
 
481
                }
 
482
            }
 
483
 
 
484
            this._actualFrames += 1;
 
485
            _setPrivate(this, ELAPSED_TIME, t);
 
486
 
 
487
            this.fire(TWEEN);
 
488
            if (done) {
 
489
                this._lastFrame();
 
490
            }
 
491
        },
 
492
 
 
493
        _lastFrame: function() {
 
494
            var iter = this.get('iterations'),
 
495
                iterCount = this.get(ITERATION_COUNT);
 
496
 
 
497
            iterCount += 1;
 
498
            if (iter === 'infinite' || iterCount < iter) {
 
499
                if (this.get('direction') === 'alternate') {
 
500
                    this.set(REVERSE, !this.get(REVERSE)); // flip it
 
501
                }
 
502
                /**
 
503
                * @event iteration
 
504
                * @description fires when an animation begins an iteration.
 
505
                * @param {Event} ev The iteration event.
 
506
                * @type Event.Custom
 
507
                */
 
508
                this.fire('iteration');
 
509
            } else {
 
510
                iterCount = 0;
 
511
                this._end();
 
512
            }
 
513
 
 
514
            _setPrivate(this, START_TIME, new Date());
 
515
            _setPrivate(this, ITERATION_COUNT, iterCount);
 
516
        },
 
517
 
 
518
        _initAttr: function() {
 
519
            var from = this.get('from') || {},
 
520
                to = this.get('to') || {},
 
521
                dur = this.get('duration') * 1000,
 
522
                node = this.get(NODE),
 
523
                easing = this.get('easing') || {},
 
524
                attr = {},
 
525
                customAttr = Y.Anim.behaviors,
 
526
                unit, begin, end;
 
527
 
 
528
            Y.each(to, function(val, name) {
 
529
                if (typeof val === 'function') {
 
530
                    val = val.call(this, node);
 
531
                }
 
532
 
 
533
                begin = from[name];
 
534
                if (begin === undefined) {
 
535
                    begin = (name in customAttr && 'get' in customAttr[name])  ?
 
536
                            customAttr[name].get(this, name) : Y.Anim.DEFAULT_GETTER(this, name);
 
537
                } else if (typeof begin === 'function') {
 
538
                    begin = begin.call(this, node);
 
539
                }
 
540
 
 
541
                var mFrom = Y.Anim.RE_UNITS.exec(begin);
 
542
                var mTo = Y.Anim.RE_UNITS.exec(val);
 
543
 
 
544
                begin = mFrom ? mFrom[1] : begin;
 
545
                var end = mTo ? mTo[1] : val,
 
546
                    unit = mTo ? mTo[2] : mFrom ?  mFrom[2] : ''; // one might be zero TODO: mixed units
 
547
 
 
548
                if (!unit && Y.Anim.RE_DEFAULT_UNIT.test(name)) {
 
549
                    unit = Y.Anim.DEFAULT_UNIT;
 
550
                }
 
551
 
 
552
                if (!begin || !end) {
 
553
                    Y.fail('invalid "from" or "to" for "' + name + '"', 'Anim');
 
554
                    return;
 
555
                }
 
556
 
 
557
                attr[name] = {
 
558
                    from: begin,
 
559
                    to: end,
 
560
                    unit: unit
 
561
                };
 
562
 
 
563
                attr.duration = dur;
 
564
                attr.easing = easing;
 
565
 
 
566
            }, this);
 
567
 
 
568
            this._runtimeAttr = attr;
 
569
        },
 
570
 
 
571
 
 
572
        // TODO: move to computedStyle? (browsers dont agree on default computed offsets)
 
573
        _getOffset: function(attr) {
 
574
            var node = this._node,
 
575
                val = node.getComputedStyle(attr),
 
576
                get = (attr === 'left') ? 'getX': 'getY',
 
577
                set = (attr === 'left') ? 'setX': 'setY';
 
578
 
 
579
            if (val === 'auto') {
 
580
                var position = node.getStyle('position');
 
581
                if (position === 'absolute' || position === 'fixed') {
 
582
                    val = node[get]();
 
583
                    node[set](val);
 
584
                } else {
 
585
                    val = 0;
 
586
                }
 
587
            }
 
588
 
 
589
            return val;
 
590
        }
 
591
    };
 
592
 
 
593
    Y.extend(Y.Anim, Y.Base, proto);
 
594
 
 
595
 
 
596
}, '3.0.0pr1' ,{requires:['base', 'node']});