~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/app/javascript/lazr/yui/transition/transition.js

Added lazr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
 
3
Code licensed under the BSD License:
 
4
http://developer.yahoo.com/yui/license.html
 
5
version: 3.3.0
 
6
build: 3167
 
7
*/
 
8
YUI.add('transition-native', function(Y) {
 
9
 
 
10
/**
 
11
* Provides the transition method for Node.
 
12
* Transition has no API of its own, but adds the transition method to Node.
 
13
*
 
14
* @module transition
 
15
* @requires node-style
 
16
*/
 
17
 
 
18
var TRANSITION = '-webkit-transition',
 
19
    TRANSITION_CAMEL = 'WebkitTransition',
 
20
    TRANSITION_PROPERTY_CAMEL = 'WebkitTransitionProperty',
 
21
    TRANSITION_PROPERTY = '-webkit-transition-property',
 
22
    TRANSITION_DURATION = '-webkit-transition-duration',
 
23
    TRANSITION_TIMING_FUNCTION = '-webkit-transition-timing-function',
 
24
    TRANSITION_DELAY = '-webkit-transition-delay',
 
25
    TRANSITION_END = 'webkitTransitionEnd',
 
26
    ON_TRANSITION_END = 'onwebkittransitionend',
 
27
    TRANSFORM_CAMEL = 'WebkitTransform',
 
28
 
 
29
    EMPTY_OBJ = {},
 
30
 
 
31
/**
 
32
 * A class for constructing transition instances.
 
33
 * Adds the "transition" method to Node.
 
34
 * @class Transition
 
35
 * @constructor
 
36
 */
 
37
 
 
38
Transition = function() {
 
39
    this.init.apply(this, arguments);
 
40
};
 
41
 
 
42
Transition.fx = {};
 
43
Transition.toggles = {};
 
44
 
 
45
Transition._hasEnd = {};
 
46
 
 
47
Transition._toCamel = function(property) {
 
48
    property = property.replace(/-([a-z])/gi, function(m0, m1) {
 
49
        return m1.toUpperCase();
 
50
    });
 
51
 
 
52
    return property;
 
53
};
 
54
 
 
55
Transition._toHyphen = function(property) {
 
56
    property = property.replace(/([A-Z]?)([a-z]+)([A-Z]?)/g, function(m0, m1, m2, m3) {
 
57
        var str = '';
 
58
        if (m1) {
 
59
            str += '-' + m1.toLowerCase();
 
60
        }
 
61
        str += m2;
 
62
        
 
63
        if (m3) {
 
64
            str += '-' + m3.toLowerCase();
 
65
        }
 
66
 
 
67
        return str;
 
68
    }); 
 
69
 
 
70
    return property;
 
71
};
 
72
 
 
73
 
 
74
Transition._reKeywords = /^(?:node|duration|iterations|easing|delay|on|onstart|onend)$/i;
 
75
 
 
76
Transition.useNative = false;
 
77
 
 
78
if (TRANSITION in Y.config.doc.documentElement.style) {
 
79
    Transition.useNative = true;
 
80
    Transition.supported = true; // TODO: remove
 
81
}
 
82
 
 
83
Y.Node.DOM_EVENTS[TRANSITION_END] = 1; 
 
84
 
 
85
Transition.NAME = 'transition';
 
86
 
 
87
Transition.DEFAULT_EASING = 'ease';
 
88
Transition.DEFAULT_DURATION = 0.5;
 
89
Transition.DEFAULT_DELAY = 0;
 
90
 
 
91
Transition._nodeAttrs = {};
 
92
 
 
93
Transition.prototype = {
 
94
    constructor: Transition,
 
95
    init: function(node, config) {
 
96
        var anim = this;
 
97
        anim._node = node;
 
98
        if (!anim._running && config) {
 
99
            anim._config = config;
 
100
            node._transition = anim; // cache for reuse
 
101
 
 
102
            anim._duration = ('duration' in config) ?
 
103
                config.duration: anim.constructor.DEFAULT_DURATION;
 
104
 
 
105
            anim._delay = ('delay' in config) ?
 
106
                config.delay: anim.constructor.DEFAULT_DELAY;
 
107
 
 
108
            anim._easing = config.easing || anim.constructor.DEFAULT_EASING;
 
109
            anim._count = 0; // track number of animated properties
 
110
            anim._running = false;
 
111
 
 
112
        }
 
113
 
 
114
        return anim;
 
115
    },
 
116
 
 
117
    addProperty: function(prop, config) {
 
118
        var anim = this,
 
119
            node = this._node,
 
120
            uid = Y.stamp(node),
 
121
            nodeInstance = Y.one(node),
 
122
            attrs = Transition._nodeAttrs[uid],
 
123
            computed,
 
124
            compareVal,
 
125
            dur,
 
126
            attr,
 
127
            val;
 
128
 
 
129
        if (!attrs) {
 
130
            attrs = Transition._nodeAttrs[uid] = {};
 
131
        }
 
132
 
 
133
        attr = attrs[prop];
 
134
 
 
135
        // might just be a value
 
136
        if (config && config.value !== undefined) {
 
137
            val = config.value;
 
138
        } else if (config !== undefined) {
 
139
            val = config; 
 
140
            config = EMPTY_OBJ;
 
141
        }
 
142
 
 
143
        if (typeof val === 'function') {
 
144
            val = val.call(nodeInstance, nodeInstance);
 
145
        }
 
146
 
 
147
        if (attr && attr.transition) {
 
148
            // take control if another transition owns this property
 
149
            if (attr.transition !== anim) {
 
150
                attr.transition._count--; // remapping attr to this transition
 
151
            }
 
152
        } 
 
153
 
 
154
        anim._count++; // properties per transition
 
155
 
 
156
        // make 0 async and fire events
 
157
        dur = ((typeof config.duration != 'undefined') ? config.duration :
 
158
                    anim._duration) || 0.0001;
 
159
 
 
160
        attrs[prop] = {
 
161
            value: val,
 
162
            duration: dur,
 
163
            delay: (typeof config.delay != 'undefined') ? config.delay :
 
164
                    anim._delay,
 
165
 
 
166
            easing: config.easing || anim._easing,
 
167
 
 
168
            transition: anim
 
169
        };
 
170
 
 
171
        // native end event doesnt fire when setting to same value
 
172
        // supplementing with timer
 
173
        // val may be a string or number (height: 0, etc), but computedStyle is always string
 
174
        computed = Y.DOM.getComputedStyle(node, prop);
 
175
        compareVal = (typeof val === 'string') ? computed : parseFloat(computed);
 
176
 
 
177
        if (Transition.useNative && compareVal === val) {
 
178
            setTimeout(function() {
 
179
                anim._onNativeEnd.call(node, {
 
180
                    propertyName: prop,
 
181
                    elapsedTime: dur
 
182
                });
 
183
            }, dur * 1000);
 
184
        }
 
185
    },
 
186
 
 
187
    removeProperty: function(prop) {
 
188
        var anim = this,
 
189
            attrs = Transition._nodeAttrs[Y.stamp(anim._node)];
 
190
 
 
191
        if (attrs && attrs[prop]) {
 
192
            delete attrs[prop];
 
193
            anim._count--;
 
194
        }
 
195
 
 
196
    },
 
197
 
 
198
    initAttrs: function(config) {
 
199
        var attr,
 
200
            node = this._node;
 
201
 
 
202
        if (config.transform && !config[TRANSFORM_CAMEL]) {
 
203
            config[TRANSFORM_CAMEL] = config.transform;
 
204
            delete config.transform; // TODO: copy
 
205
        }
 
206
 
 
207
        for (attr in config) {
 
208
            if (config.hasOwnProperty(attr) && !Transition._reKeywords.test(attr)) {
 
209
                this.addProperty(attr, config[attr]);
 
210
 
 
211
                // when size is auto or % webkit starts from zero instead of computed 
 
212
                // (https://bugs.webkit.org/show_bug.cgi?id=16020)
 
213
                // TODO: selective set
 
214
                if (node.style[attr] === '') {
 
215
                    Y.DOM.setStyle(node, attr, Y.DOM.getComputedStyle(node, attr));
 
216
                }
 
217
            }
 
218
        }
 
219
    },
 
220
 
 
221
    /**
 
222
     * Starts or an animation.
 
223
     * @method run
 
224
     * @chainable
 
225
     * @private
 
226
     */    
 
227
    run: function(callback) {
 
228
        var anim = this,
 
229
            node = anim._node,
 
230
            config = anim._config,
 
231
            data = {
 
232
                type: 'transition:start',
 
233
                config: config
 
234
            };
 
235
 
 
236
 
 
237
        if (!anim._running) {
 
238
            anim._running = true;
 
239
 
 
240
            //anim._node.fire('transition:start', data);
 
241
 
 
242
            if (config.on && config.on.start) {
 
243
                config.on.start.call(Y.one(node), data);
 
244
            }
 
245
 
 
246
            anim.initAttrs(anim._config);
 
247
 
 
248
            anim._callback = callback;
 
249
            anim._start();
 
250
        }
 
251
 
 
252
 
 
253
        return anim;
 
254
    },
 
255
 
 
256
    _start: function() {
 
257
        this._runNative();
 
258
    },
 
259
 
 
260
    _prepDur: function(dur) {
 
261
        dur = parseFloat(dur);
 
262
 
 
263
        return dur + 's';
 
264
    },
 
265
 
 
266
    _runNative: function(time) {
 
267
        var anim = this,
 
268
            node = anim._node,
 
269
            uid = Y.stamp(node),
 
270
            style = node.style,
 
271
            computed = getComputedStyle(node),
 
272
            attrs = Transition._nodeAttrs[uid],
 
273
            cssText = '',
 
274
            cssTransition = computed[TRANSITION_PROPERTY],
 
275
 
 
276
            transitionText = TRANSITION_PROPERTY + ': ',
 
277
            duration = TRANSITION_DURATION + ': ',
 
278
            easing = TRANSITION_TIMING_FUNCTION + ': ',
 
279
            delay = TRANSITION_DELAY + ': ',
 
280
            hyphy,
 
281
            attr,
 
282
            name;
 
283
 
 
284
        // preserve existing transitions
 
285
        if (cssTransition !== 'all') {
 
286
            transitionText += cssTransition + ',';
 
287
            duration += computed[TRANSITION_DURATION] + ',';
 
288
            easing += computed[TRANSITION_TIMING_FUNCTION] + ',';
 
289
            delay += computed[TRANSITION_DELAY] + ',';
 
290
 
 
291
        }
 
292
 
 
293
        // run transitions mapped to this instance
 
294
        for (name in attrs) {
 
295
            hyphy = Transition._toHyphen(name);
 
296
            attr = attrs[name];
 
297
            if (attrs.hasOwnProperty(name) && attr.transition === anim) {
 
298
                if (name in node.style) { // only native styles allowed
 
299
                    duration += anim._prepDur(attr.duration) + ',';
 
300
                    delay += anim._prepDur(attr.delay) + ',';
 
301
                    easing += (attr.easing) + ',';
 
302
 
 
303
                    transitionText += hyphy + ',';
 
304
                    cssText += hyphy + ': ' + attr.value + '; ';
 
305
                } else {
 
306
                    this.removeProperty(name);
 
307
                }
 
308
            }
 
309
        }
 
310
 
 
311
        transitionText = transitionText.replace(/,$/, ';');
 
312
        duration = duration.replace(/,$/, ';');
 
313
        easing = easing.replace(/,$/, ';');
 
314
        delay = delay.replace(/,$/, ';');
 
315
 
 
316
        // only one native end event per node
 
317
        if (!Transition._hasEnd[uid]) {
 
318
            //anim._detach = Y.on(TRANSITION_END, anim._onNativeEnd, node);
 
319
            //node[ON_TRANSITION_END] = anim._onNativeEnd;
 
320
            node.addEventListener(TRANSITION_END, anim._onNativeEnd, false);
 
321
            Transition._hasEnd[uid] = true;
 
322
 
 
323
        }
 
324
        
 
325
        //setTimeout(function() { // allow updates to apply (size fix, onstart, etc)
 
326
            style.cssText += transitionText + duration + easing + delay + cssText;
 
327
        //}, 1);
 
328
 
 
329
    },
 
330
 
 
331
    _end: function(elapsed) {
 
332
        var anim = this,
 
333
            node = anim._node,
 
334
            callback = anim._callback,
 
335
            config = anim._config,
 
336
            data = {
 
337
                type: 'transition:end',
 
338
                config: config,
 
339
                elapsedTime: elapsed 
 
340
            },
 
341
 
 
342
            nodeInstance = Y.one(node); 
 
343
 
 
344
        anim._running = false;
 
345
        anim._callback = null;
 
346
 
 
347
        if (node) {
 
348
            if (config.on && config.on.end) {
 
349
                setTimeout(function() { // IE: allow previous update to finish
 
350
                    config.on.end.call(nodeInstance, data);
 
351
 
 
352
                    // nested to ensure proper fire order
 
353
                    if (callback) {
 
354
                        callback.call(nodeInstance, data);
 
355
                    }
 
356
 
 
357
                }, 1);
 
358
            } else if (callback) {
 
359
                setTimeout(function() { // IE: allow previous update to finish
 
360
                    callback.call(nodeInstance, data);
 
361
                }, 1);
 
362
            }
 
363
            //node.fire('transition:end', data);
 
364
        }
 
365
 
 
366
    },
 
367
 
 
368
    _endNative: function(name) {
 
369
        var node = this._node,
 
370
            value = node.ownerDocument.defaultView.getComputedStyle(node, '')[TRANSITION_PROPERTY];
 
371
 
 
372
        if (typeof value === 'string') {
 
373
            value = value.replace(new RegExp('(?:^|,\\s)' + name + ',?'), ',');
 
374
            value = value.replace(/^,|,$/, '');
 
375
            node.style[TRANSITION_CAMEL] = value;
 
376
        }
 
377
    },
 
378
 
 
379
    _onNativeEnd: function(e) {
 
380
        var node = this,
 
381
            uid = Y.stamp(node),
 
382
            event = e,//e._event,
 
383
            name = Transition._toCamel(event.propertyName),
 
384
            elapsed = event.elapsedTime,
 
385
            attrs = Transition._nodeAttrs[uid],
 
386
            attr = attrs[name],
 
387
            anim = (attr) ? attr.transition : null,
 
388
            data,
 
389
            config;
 
390
 
 
391
        if (anim) {
 
392
            anim.removeProperty(name);
 
393
            anim._endNative(name);
 
394
            config = anim._config[name];
 
395
 
 
396
            data = {
 
397
                type: 'propertyEnd',
 
398
                propertyName: name,
 
399
                elapsedTime: elapsed,
 
400
                config: config
 
401
            };
 
402
 
 
403
            if (config && config.on && config.on.end) {
 
404
                config.on.end.call(Y.one(node), data);
 
405
            }
 
406
 
 
407
            //node.fire('transition:propertyEnd', data);
 
408
 
 
409
            if (anim._count <= 0)  { // after propertyEnd fires
 
410
                anim._end(elapsed);
 
411
            }
 
412
        }
 
413
    },
 
414
 
 
415
    destroy: function() {
 
416
        var anim = this;
 
417
        /*
 
418
        if (anim._detach) {
 
419
            anim._detach.detach();
 
420
        }
 
421
        */
 
422
        //anim._node[ON_TRANSITION_END] = null;
 
423
        node.removeEventListener(TRANSITION_END, anim._onNativeEnd, false);
 
424
        anim._node = null;
 
425
    }
 
426
};
 
427
 
 
428
Y.Transition = Transition;
 
429
Y.TransitionNative = Transition; // TODO: remove
 
430
 
 
431
/** 
 
432
 *   Animate one or more css properties to a given value. Requires the "transition" module.
 
433
 *   <pre>example usage:
 
434
 *       Y.one('#demo').transition({
 
435
 *           duration: 1, // in seconds, default is 0.5
 
436
 *           easing: 'ease-out', // default is 'ease'
 
437
 *           delay: '1', // delay start for 1 second, default is 0
 
438
 *
 
439
 *           height: '10px',
 
440
 *           width: '10px',
 
441
 *
 
442
 *           opacity: { // per property
 
443
 *               value: 0,
 
444
 *               duration: 2,
 
445
 *               delay: 2,
 
446
 *               easing: 'ease-in'
 
447
 *           }
 
448
 *       });
 
449
 *   </pre>
 
450
 *   @for Node
 
451
 *   @method transition
 
452
 *   @param {Object} config An object containing one or more style properties, a duration and an easing.
 
453
 *   @param {Function} callback A function to run after the transition has completed. 
 
454
 *   @chainable
 
455
*/
 
456
Y.Node.prototype.transition = function(name, config, callback) {
 
457
    var 
 
458
        transitionAttrs = Transition._nodeAttrs[Y.stamp(this._node)],
 
459
        anim = (transitionAttrs) ? transitionAttrs.transition || null : null,
 
460
        fxConfig,
 
461
        prop;
 
462
    
 
463
    if (typeof name === 'string') { // named effect, pull config from registry
 
464
        if (typeof config === 'function') {
 
465
            callback = config;
 
466
            config = null;
 
467
        }
 
468
 
 
469
        fxConfig = Transition.fx[name];
 
470
 
 
471
        if (config && typeof config !== 'boolean') {
 
472
            config = Y.clone(config);
 
473
 
 
474
            for (prop in fxConfig) {
 
475
                if (fxConfig.hasOwnProperty(prop)) {
 
476
                    if (! (prop in config)) {
 
477
                        config[prop] = fxConfig[prop]; 
 
478
                    }
 
479
                }
 
480
            }
 
481
        } else {
 
482
            config = fxConfig;
 
483
        }
 
484
 
 
485
    } else { // name is a config, config is a callback or undefined
 
486
        callback = config;
 
487
        config = name;
 
488
    }
 
489
 
 
490
    if (anim && !anim._running) {
 
491
        anim.init(this, config);
 
492
    } else {
 
493
        anim = new Transition(this._node, config);
 
494
    }
 
495
 
 
496
    anim.run(callback);
 
497
    return this;
 
498
};
 
499
 
 
500
Y.Node.prototype.show = function(name, config, callback) {
 
501
    this._show(); // show prior to transition
 
502
    if (name && Y.Transition) {
 
503
        if (typeof name !== 'string' && !name.push) { // named effect or array of effects supercedes default
 
504
            if (typeof config === 'function') {
 
505
                callback = config;
 
506
                config = name;
 
507
            }
 
508
            name = this.SHOW_TRANSITION; 
 
509
        }    
 
510
        this.transition(name, config, callback);
 
511
    }    
 
512
    return this;
 
513
};
 
514
 
 
515
var _wrapCallBack = function(anim, fn, callback) {
 
516
    return function() {
 
517
        if (fn) {
 
518
            fn.call(anim);
 
519
        }
 
520
        if (callback) {
 
521
            callback.apply(anim._node, arguments);
 
522
        }
 
523
    };
 
524
};
 
525
 
 
526
Y.Node.prototype.hide = function(name, config, callback) {
 
527
    if (name && Y.Transition) {
 
528
        if (typeof config === 'function') {
 
529
            callback = config;
 
530
            config = null;
 
531
        }
 
532
 
 
533
        callback = _wrapCallBack(this, this._hide, callback); // wrap with existing callback
 
534
        if (typeof name !== 'string' && !name.push) { // named effect or array of effects supercedes default
 
535
            if (typeof config === 'function') {
 
536
                callback = config;
 
537
                config = name;
 
538
            }
 
539
            name = this.HIDE_TRANSITION; 
 
540
        }    
 
541
        this.transition(name, config, callback);
 
542
    } else {
 
543
        this._hide();
 
544
    }    
 
545
    return this;
 
546
}; 
 
547
 
 
548
/** 
 
549
 *   Animate one or more css properties to a given value. Requires the "transition" module.
 
550
 *   <pre>example usage:
 
551
 *       Y.all('.demo').transition({
 
552
 *           duration: 1, // in seconds, default is 0.5
 
553
 *           easing: 'ease-out', // default is 'ease'
 
554
 *           delay: '1', // delay start for 1 second, default is 0
 
555
 *
 
556
 *           height: '10px',
 
557
 *           width: '10px',
 
558
 *
 
559
 *           opacity: { // per property
 
560
 *               value: 0,
 
561
 *               duration: 2,
 
562
 *               delay: 2,
 
563
 *               easing: 'ease-in'
 
564
 *           }
 
565
 *       });
 
566
 *   </pre>
 
567
 *   @for NodeList
 
568
 *   @method transition
 
569
 *   @param {Object} config An object containing one or more style properties, a duration and an easing.
 
570
 *   @param {Function} callback A function to run after the transition has completed. The callback fires
 
571
 *       once per item in the NodeList.
 
572
 *   @chainable
 
573
*/
 
574
Y.NodeList.prototype.transition = function(config, callback) {
 
575
    var nodes = this._nodes,
 
576
        i = 0,
 
577
        node;
 
578
 
 
579
    while ((node = nodes[i++])) {
 
580
        Y.one(node).transition(config, callback);
 
581
    }
 
582
 
 
583
    return this;
 
584
};
 
585
 
 
586
Y.Node.prototype.toggleView = function(name, on) {
 
587
    var callback;
 
588
    this._toggles = this._toggles || [];
 
589
 
 
590
    if (typeof name == 'boolean') { // no transition, just toggle
 
591
        on = name;
 
592
    }
 
593
    if (typeof on === 'undefined' && name in this._toggles) {
 
594
        on = ! this._toggles[name];
 
595
    }
 
596
 
 
597
    on = (on) ? 1 : 0;
 
598
 
 
599
    if (on) {
 
600
        this._show();
 
601
    }  else {
 
602
        callback = _wrapCallBack(anim, this._hide);
 
603
    }
 
604
 
 
605
    this._toggles[name] = on;
 
606
    this.transition(Y.Transition.toggles[name][on], callback);
 
607
};
 
608
 
 
609
Y.NodeList.prototype.toggleView = function(config, callback) {
 
610
    var nodes = this._nodes,
 
611
        i = 0,
 
612
        node;
 
613
 
 
614
    while ((node = nodes[i++])) {
 
615
        Y.one(node).toggleView(config, callback);
 
616
    }
 
617
 
 
618
    return this;
 
619
};
 
620
 
 
621
Y.mix(Transition.fx, {
 
622
    fadeOut: {
 
623
        opacity: 0,
 
624
        duration: 0.5,
 
625
        easing: 'ease-out'
 
626
    },
 
627
 
 
628
    fadeIn: {
 
629
        opacity: 1,
 
630
        duration: 0.5,
 
631
        easing: 'ease-in'
 
632
    },
 
633
 
 
634
    sizeOut: {
 
635
        height: 0,
 
636
        width: 0,
 
637
        duration: 0.75,
 
638
        easing: 'ease-out'
 
639
    },
 
640
 
 
641
    sizeIn: {
 
642
        height: function(node) {
 
643
            return node.get('scrollHeight') + 'px';
 
644
        },
 
645
        width: function(node) {
 
646
            return node.get('scrollWidth') + 'px';
 
647
        },
 
648
        duration: 0.5,
 
649
        easing: 'ease-in',
 
650
        
 
651
        on: {
 
652
            start: function() {
 
653
                var overflow = this.getStyle('overflow');
 
654
                if (overflow !== 'hidden') { // enable scrollHeight/Width
 
655
                    this.setStyle('overflow', 'hidden');
 
656
                    this._transitionOverflow = overflow;
 
657
                }
 
658
            },
 
659
 
 
660
            end: function() {
 
661
                if (this._transitionOverflow) { // revert overridden value
 
662
                    this.setStyle('overflow', this._transitionOverflow);
 
663
                }
 
664
            }
 
665
        } 
 
666
    }
 
667
});
 
668
 
 
669
Y.mix(Transition.toggles, {
 
670
    size: ['sizeIn', 'sizeOut'],
 
671
    fade: ['fadeOut', 'fadeIn']
 
672
});
 
673
 
 
674
 
 
675
}, '3.3.0' ,{requires:['node-base']});
 
676
YUI.add('transition-timer', function(Y) {
 
677
 
 
678
/*
 
679
* The Transition Utility provides an API for creating advanced transitions.
 
680
* @module transition
 
681
*/
 
682
 
 
683
/*
 
684
* Provides the base Transition class, for animating numeric properties.
 
685
*
 
686
* @module transition
 
687
* @submodule transition-timer
 
688
*/
 
689
 
 
690
 
 
691
var Transition = Y.Transition;
 
692
 
 
693
Y.mix(Transition.prototype, {
 
694
    _start: function() {
 
695
        if (Transition.useNative) {
 
696
            this._runNative();
 
697
        } else {
 
698
            this._runTimer();
 
699
        }
 
700
    },
 
701
 
 
702
    _runTimer: function() {
 
703
        var anim = this;
 
704
        anim._initAttrs();
 
705
 
 
706
        Transition._running[Y.stamp(anim)] = anim;
 
707
        anim._startTime = new Date();
 
708
        Transition._startTimer();
 
709
    },
 
710
 
 
711
    _endTimer: function() {
 
712
        var anim = this;
 
713
        delete Transition._running[Y.stamp(anim)];
 
714
        anim._startTime = null;
 
715
    },
 
716
 
 
717
    _runFrame: function() {
 
718
        var t = new Date() - this._startTime;
 
719
        this._runAttrs(t);
 
720
    },
 
721
 
 
722
    _runAttrs: function(time) {
 
723
        var anim = this,
 
724
            node = anim._node,
 
725
            config = anim._config,
 
726
            uid = Y.stamp(node),
 
727
            attrs = Transition._nodeAttrs[uid],
 
728
            customAttr = Transition.behaviors,
 
729
            done = false,
 
730
            allDone = false,
 
731
            data,
 
732
            name,
 
733
            attribute,
 
734
            setter,
 
735
            elapsed,
 
736
            delay,
 
737
            d,
 
738
            t,
 
739
            i;
 
740
 
 
741
        for (name in attrs) {
 
742
            attribute = attrs[name];
 
743
            if ((attribute && attribute.transition === anim)) {
 
744
                d = attribute.duration;
 
745
                delay = attribute.delay;
 
746
                elapsed = (time - delay) / 1000;
 
747
                t = time;
 
748
                data = {
 
749
                    type: 'propertyEnd',
 
750
                    propertyName: name,
 
751
                    config: config,
 
752
                    elapsedTime: elapsed
 
753
                };
 
754
 
 
755
                setter = (i in customAttr && 'set' in customAttr[i]) ?
 
756
                        customAttr[i].set : Transition.DEFAULT_SETTER;
 
757
 
 
758
                done = (t >= d);
 
759
 
 
760
                if (t > d) {
 
761
                    t = d;
 
762
                }
 
763
 
 
764
                if (!delay || time >= delay) {
 
765
                    setter(anim, name, attribute.from, attribute.to, t - delay, d - delay,
 
766
                        attribute.easing, attribute.unit); 
 
767
 
 
768
                    if (done) {
 
769
                        delete attrs[name];
 
770
                        anim._count--;
 
771
 
 
772
                        if (config[name] && config[name].on && config[name].on.end) {
 
773
                            config[name].on.end.call(Y.one(node), data);
 
774
                        }
 
775
 
 
776
                        //node.fire('transition:propertyEnd', data);
 
777
 
 
778
                        if (!allDone && anim._count <= 0) {
 
779
                            allDone = true;
 
780
                            anim._end(elapsed);
 
781
                            anim._endTimer();
 
782
                        }
 
783
                    }
 
784
                }
 
785
 
 
786
            }
 
787
        }
 
788
    },
 
789
 
 
790
    _initAttrs: function() {
 
791
        var anim = this,
 
792
            customAttr = Transition.behaviors,
 
793
            uid = Y.stamp(anim._node),
 
794
            attrs = Transition._nodeAttrs[uid],
 
795
            attribute,
 
796
            duration,
 
797
            delay,
 
798
            easing,
 
799
            val,
 
800
            name,
 
801
            mTo,
 
802
            mFrom,
 
803
            unit, begin, end;
 
804
 
 
805
        for (name in attrs) {
 
806
            attribute = attrs[name];
 
807
            if (attrs.hasOwnProperty(name) && (attribute && attribute.transition === anim)) {
 
808
                duration = attribute.duration * 1000;
 
809
                delay = attribute.delay * 1000;
 
810
                easing = attribute.easing;
 
811
                val = attribute.value;
 
812
 
 
813
                // only allow supported properties
 
814
                if (name in anim._node.style || name in Y.DOM.CUSTOM_STYLES) {
 
815
                    begin = (name in customAttr && 'get' in customAttr[name])  ?
 
816
                            customAttr[name].get(anim, name) : Transition.DEFAULT_GETTER(anim, name);
 
817
 
 
818
                    mFrom = Transition.RE_UNITS.exec(begin);
 
819
                    mTo = Transition.RE_UNITS.exec(val);
 
820
 
 
821
                    begin = mFrom ? mFrom[1] : begin;
 
822
                    end = mTo ? mTo[1] : val;
 
823
                    unit = mTo ? mTo[2] : mFrom ?  mFrom[2] : ''; // one might be zero TODO: mixed units
 
824
 
 
825
                    if (!unit && Transition.RE_DEFAULT_UNIT.test(name)) {
 
826
                        unit = Transition.DEFAULT_UNIT;
 
827
                    }
 
828
 
 
829
                    if (typeof easing === 'string') {
 
830
                        if (easing.indexOf('cubic-bezier') > -1) {
 
831
                            easing = easing.substring(13, easing.length - 1).split(',');
 
832
                        } else if (Transition.easings[easing]) {
 
833
                            easing = Transition.easings[easing];
 
834
                        }
 
835
                    }
 
836
 
 
837
                    attribute.from = Number(begin);
 
838
                    attribute.to = Number(end);
 
839
                    attribute.unit = unit;
 
840
                    attribute.easing = easing;
 
841
                    attribute.duration = duration + delay;
 
842
                    attribute.delay = delay;
 
843
                } else {
 
844
                    delete attrs[name];
 
845
                    anim._count--;
 
846
                }
 
847
            }
 
848
        }
 
849
    },
 
850
 
 
851
    destroy: function() {
 
852
        this.detachAll();
 
853
        this._node = null;
 
854
    }
 
855
}, true);
 
856
 
 
857
Y.mix(Y.Transition, {
 
858
    _runtimeAttrs: {},
 
859
    /*
 
860
     * Regex of properties that should use the default unit.
 
861
     *
 
862
     * @property RE_DEFAULT_UNIT
 
863
     * @static
 
864
     */
 
865
    RE_DEFAULT_UNIT: /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i,
 
866
 
 
867
    /*
 
868
     * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
 
869
     *
 
870
     * @property DEFAULT_UNIT
 
871
     * @static
 
872
     */
 
873
    DEFAULT_UNIT: 'px',
 
874
 
 
875
    /*
 
876
     * Time in milliseconds passed to setInterval for frame processing 
 
877
     *
 
878
     * @property intervalTime
 
879
     * @default 20
 
880
     * @static
 
881
     */
 
882
    intervalTime: 20,
 
883
 
 
884
    /*
 
885
     * Bucket for custom getters and setters
 
886
     *
 
887
     * @property behaviors
 
888
     * @static
 
889
     */
 
890
    behaviors: {
 
891
        left: {
 
892
            get: function(anim, attr) {
 
893
                return Y.DOM._getAttrOffset(anim._node, attr);
 
894
            }
 
895
        }
 
896
    },
 
897
 
 
898
    /*
 
899
     * The default setter to use when setting object properties.
 
900
     *
 
901
     * @property DEFAULT_SETTER
 
902
     * @static
 
903
     */
 
904
    DEFAULT_SETTER: function(anim, att, from, to, elapsed, duration, fn, unit) {
 
905
        from = Number(from);
 
906
        to = Number(to);
 
907
 
 
908
        var node = anim._node,
 
909
            val = Transition.cubicBezier(fn, elapsed / duration);
 
910
 
 
911
        val = from + val[0] * (to - from);
 
912
 
 
913
        if (node) {
 
914
            if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
 
915
                unit = unit || '';
 
916
                Y.DOM.setStyle(node, att, val + unit);
 
917
            }
 
918
        } else {
 
919
            anim._end();
 
920
        }
 
921
    },
 
922
 
 
923
    /*
 
924
     * The default getter to use when getting object properties.
 
925
     *
 
926
     * @property DEFAULT_GETTER
 
927
     * @static
 
928
     */
 
929
    DEFAULT_GETTER: function(anim, att) {
 
930
        var node = anim._node,
 
931
            val = '';
 
932
 
 
933
        if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
 
934
            val = Y.DOM.getComputedStyle(node, att);
 
935
        }
 
936
 
 
937
        return val;
 
938
    },
 
939
 
 
940
    _startTimer: function() {
 
941
        if (!Transition._timer) {
 
942
            Transition._timer = setInterval(Transition._runFrame, Transition.intervalTime);
 
943
        }
 
944
    },
 
945
 
 
946
    _stopTimer: function() {
 
947
        clearInterval(Transition._timer);
 
948
        Transition._timer = null;
 
949
    },
 
950
 
 
951
    /*
 
952
     * Called per Interval to handle each animation frame.
 
953
     * @method _runFrame
 
954
     * @private
 
955
     * @static
 
956
     */    
 
957
    _runFrame: function() {
 
958
        var done = true,
 
959
            anim;
 
960
        for (anim in Transition._running) {
 
961
            if (Transition._running[anim]._runFrame) {
 
962
                done = false;
 
963
                Transition._running[anim]._runFrame();
 
964
            }
 
965
        }
 
966
 
 
967
        if (done) {
 
968
            Transition._stopTimer();
 
969
        }
 
970
    },
 
971
 
 
972
    cubicBezier: function(p, t) {
 
973
        var x0 = 0,
 
974
            y0 = 0,
 
975
            x1 = p[0],
 
976
            y1 = p[1],
 
977
            x2 = p[2],
 
978
            y2 = p[3],
 
979
            x3 = 1,
 
980
            y3 = 0,
 
981
 
 
982
            A = x3 - 3 * x2 + 3 * x1 - x0,
 
983
            B = 3 * x2 - 6 * x1 + 3 * x0,
 
984
            C = 3 * x1 - 3 * x0,
 
985
            D = x0,
 
986
            E = y3 - 3 * y2 + 3 * y1 - y0,
 
987
            F = 3 * y2 - 6 * y1 + 3 * y0,
 
988
            G = 3 * y1 - 3 * y0,
 
989
            H = y0,
 
990
 
 
991
            x = (((A*t) + B)*t + C)*t + D,
 
992
            y = (((E*t) + F)*t + G)*t + H;
 
993
 
 
994
        return [x, y];
 
995
    },
 
996
 
 
997
    easings: {
 
998
        ease: [0.25, 0, 1, 0.25],
 
999
        linear: [0, 0, 1, 1],
 
1000
        'ease-in': [0.42, 0, 1, 1],
 
1001
        'ease-out': [0, 0, 0.58, 1],
 
1002
        'ease-in-out': [0.42, 0, 0.58, 1]
 
1003
    },
 
1004
 
 
1005
    _running: {},
 
1006
    _timer: null,
 
1007
 
 
1008
    RE_UNITS: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/
 
1009
}, true); 
 
1010
 
 
1011
Transition.behaviors.top = Transition.behaviors.bottom = Transition.behaviors.right = Transition.behaviors.left;
 
1012
 
 
1013
Y.Transition = Transition;
 
1014
 
 
1015
 
 
1016
}, '3.3.0' ,{requires:['transition-native', 'node-style']});
 
1017
 
 
1018
 
 
1019
YUI.add('transition', function(Y){}, '3.3.0' ,{use:['transition-native', 'transition-timer']});
 
1020