﻿/*
---
description: native css animations.

license: MIT-style

authors:
- Thierry Bela

credits:
- amadeus (CSSEvents)
- AndrÃ© Fiedler, eskimoblood (Fx.Tween.CSS3)

requires:
core/1.3:
- Array
- Element.Style
- Fx.CSS

provides: [FxCSS]

...
*/

!function () {


    var set = Element.prototype.setStyle,
		get = Element.prototype.getStyle,
    //vendor = '',
		div = new Element('div'),
		prefix = Browser.safari || Browser.chrome || Browser.Platform.ios ? 'webkit' : (Browser.opera ? 'o' : (Browser.ie ? 'ms' : '')),
		prefixes = ['Khtml', 'Moz', 'Webkit', 'O', 'ms'];

    function getPrefix(prop) {

        //return unprefixed property if supported. prefixed properties sometimes do not work fine (MozOpacity is an empty string in FF4)
        if (prop in div.style) return prop;

        var upper = prop.charAt(0).toUpperCase() + prop.slice(1);

        for (var i = prefixes.length; i--; ) if (prefixes[i] + upper in div.style) return prefixes[i] + upper;

        return prop;
    }

    Element.implement({

        setStyle: function (property, value) {

            return set.call(this, getPrefix(property), value);
        },
        getStyle: function (property) {

            return get.call(this, getPrefix(property));
        }
    });

    //eventTypes
    ['transitionStart', 'transitionEnd'/* , 'animationStart', 'animationIteration', 'animationEnd' */].each(function (eventType) {

        Element.NativeEvents[eventType.toLowerCase()] = 2;

        var customType = eventType;

        if (prefix) customType = prefix + customType.capitalize();
        else customType = customType.toLowerCase();

        Element.NativeEvents[customType] = 2;
        Element.Events[eventType.toLowerCase()] = { base: customType }

    });

    //detect if transition property is supported
    Fx.css3Transition = (function (prop) {

        //
        if (prop in div.style) return true;

        var prefixes = ['Khtml', 'Moz', 'Webkit', 'O', 'ms'], upper = prop.charAt(0).toUpperCase() + prop.slice(1);

        for (var i = prefixes.length; i--; ) if (prefixes[i] + upper in div.style) return true;

        return false
    })('transition');

    Fx.transitionTimings = {
        'linear': '0,0,1,1',
        'expo:in': '0.71,0.01,0.83,0',
        'expo:out': '0.14,1,0.32,0.99',
        'expo:in:out': '0.85,0,0.15,1',
        'circ:in': '0.34,0,0.96,0.23',
        'circ:out': '0,0.5,0.37,0.98',
        'circ:in:out': '0.88,0.1,0.12,0.9',
        'sine:in': '0.22,0.04,0.36,0',
        'sine:out': '0.04,0,0.5,1',
        'sine:in:out': '0.37,0.01,0.63,1',
        'quad:in': '0.14,0.01,0.49,0',
        'quad:out': '0.01,0,0.43,1',
        'quad:in:out': '0.47,0.04,0.53,0.96',
        'cubic:in': '0.35,0,0.65,0',
        'cubic:out': '0.09,0.25,0.24,1',
        'cubic:in:out': '0.66,0,0.34,1',
        'quart:in': '0.69,0,0.76,0.17',
        'quart:out': '0.26,0.96,0.44,1',
        'quart:in:out': '0.76,0,0.24,1',
        'quint:in': '0.64,0,0.78,0',
        'quint:out': '0.22,1,0.35,1',
        'quint:in:out': '0.9,0,0.1,1'
    };

    this.FxCSS = {

        Binds: ['onComplete'],
        initialize: function (element, options) {

            this.element = this.subject = document.id(element);
            this.parent(Object.merge({ transition: 'sine:in:out' }, options));
            this.events = { transitionend: this.onComplete }
        },

        check: function () {

            if (this.css) {

                if (!this.locked && !this.running) return true
            }

            else if (!this.timer) return true;

            switch (this.options.link) {

                case 'cancel': this.cancel(); return true;
                case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
            }

            return false;
        },

        onComplete: function () {

            //if(window.console && console.log) console.log(['completed', this.css]);
            if (this.css && this.running) {

                this.element.removeEvents(this.events).setStyle('transition', '');
                this.running = false
            }

            this.css = false;
            this.locked = false;

            return this.parent()
        },

        cancel: function () {

            if (this.css && this.running) {

                this.running = false;
                this.css = false
            }

            return this.parent()
        }
    }

} ();

/*
---
description: css3 transform rule parser.

license: MIT-style

authors:
- Thierry Bela

credits:
- Pat Cullen (Fx.CSS.Transform)

requires:
core/1.3:
- Array
- Fx.CSS

provides: [Fx.CSS.Parsers.Transform]

...
*/

!function () {

    Number.implement({
        toRad: function () { return this * Math.PI / 180; },
        toDeg: function () { return this * 180 / Math.PI; }
    });

    Fx.CSS.implement({

        compute: function (from, to, delta) {

            var computed = [];

            from = Array.from(from);
            to = Array.from(to);
            (Math.min(from.length, to.length)).times(function (i) {

                computed.push({ value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser });
            });
            computed.$family = Function.from('fx:css:value');
            return computed;
        },

        prepare: function (element, property, values) {

            values = Array.from(values);

            if (values[1] == null) {

                values[1] = values[0];
                values[0] = element.getStyle(property);
            }

            var parser, parsed;

            if (property == 'transform') {

                parser = Fx.CSS.Parsers.Transform;
                parsed = values.map(function (value) { return { value: parser.parse(value), parser: parser} })
            }

            else parsed = values.map(this.parse);

            return { from: parsed[0], to: parsed[1] };
        }
    });

    var deg = ['skew', 'rotate'],
	px = ['translate'],
	generics = ['scale'],
	coordinates = ['X', 'Y', 'Z'];

    px = px.concat(coordinates.map(function (side) { return px[0] + side }));
    generics = generics.concat(coordinates.map(function (side) { return generics[0] + side }));
    deg = deg.concat(coordinates.map(function (side) { return deg[0] + side })).concat(coordinates.map(function (side) { return deg[1] + side }));

    Object.merge(Fx.CSS.Parsers, {

        Transform: {

            parse: function (value) {

                if (!value) return false;

                var transform = {};

                if (px.every(function (t) {

                    if ((match = value.match(new RegExp(t + '\\(\\s*([-+]?([0-9]*\\.)?[0-9]+)(px)?\\s*(,\\s*([-+]?([0-9]*\\.)?[0-9]+)(px)?\\s*)?\\)', 'i')))) {

                        var x = parseFloat(match[1]),
							y = parseFloat(match[5]);

                        //allow optional unit for 0
                        if (!match[3] && x != 0) return false;

                        if (match[4]) {

                            if (!match[7] && y != 0) return false;
                            transform[t] = [x, y]
                        }

                        else transform[t] = x
                    }

                    return true
                }) && deg.every(function (t) {

                    if ((match = value.match(new RegExp(t + '\\(\\s*([-+]?([0-9]*\\.)?[0-9]+)(deg|rad)?\\s*(,\\s*([-+]?([0-9]*\\.)?[0-9]+)(deg|rad)?)?\\s*\\)', 'i')))) {

                        var x = parseFloat(match[1]),
							y = parseFloat(match[5]);

                        //allow optional unit for 0
                        if (!match[3] && x != 0) return false;
                        if (match[4]) {

                            if (!match[7] && y != 0) return false;
                            transform[t] = [match[3] == 'rad' ? parseFloat(match[1]).toDeg() : parseFloat(match[1]), match[7] == 'rad' ? parseFloat(match[5]).toDeg() : parseFloat(match[5])]
                        }

                        else transform[t] = match[3] == 'rad' ? parseFloat(match[1]).toDeg() : parseFloat(match[1])
                    }

                    return true
                }) && generics.every(function (t) {

                    if ((match = value.match(new RegExp(t + '\\(\\s*(([0-9]*\\.)?[0-9]+)\\s*(,\\s*(([0-9]*\\.)?[0-9]+)\\s*)?\\)', 'i')))) {

                        if (match[3]) transform[t] = [parseFloat(match[1]), parseFloat(match[4])];

                        else transform[t] = parseFloat(match[1])
                    }

                    return true

                })) return Object.getLength(transform) == 0 ? false : transform;

                return false
            },
            compute: function (from, to, delta) {

                var computed = {};

                Object.each(to, function (value, key) {

                    if (value instanceof Array) {

                        computed[key] = Array.from(from[key] == null ? value : Array.from(from[key])).map(function (val, index) {

                            return Fx.compute(val == null ? value[index] : val, value[index], delta)
                        })
                    }

                    else computed[key] = Fx.compute(from[key] == null ? value : from[key], value, delta)
                });

                return computed
            },
            serve: function (transform) {

                var style = '';

                deg.each(function (t) {

                    if (transform[t] != null) {

                        if (transform[t] instanceof Array) style += t + '(' + transform[t].map(function (val) { return val + 'deg' }) + ') ';
                        else style += t + '(' + transform[t] + 'deg) '
                    }
                });

                px.each(function (t) { if (transform[t] != null) style += t + '(' + Array.from(transform[t]).map(function (value) { return value + 'px' }) + ') ' });
                generics.each(function (t) { if (transform[t] != null) style += t + '(' + transform[t] + ') ' });

                return style
            }
        }
    })
} ();
