/*!
* NETEYE Activity Indicator jQuery Plugin
*
* Copyright (c) 2010 NETEYE GmbH
* Licensed under the MIT license
*
* Author: Felix Gnass [fgnass at neteye dot de]
* Version: @{VERSION}
*/

/**
* Plugin that renders a customisable activity indicator (spinner) using SVG or VML.
*/
(function ($) {

    $.fn.activity = function (opts) {
        this.each(function () {
            var $this = $(this);
            var el = $this.data('activity');
            if (el) {
                clearInterval(el.data('interval'));
                el.remove();
                $this.removeData('activity');
            }
            if (opts !== false) {
                opts = $.extend({ color: $this.css('color') }, $.fn.activity.defaults, opts);

                el = render($this, opts).css('position', 'absolute').prependTo(opts.outside ? 'body' : $this);
                var h = $this.outerHeight() - el.height();
                var w = $this.outerWidth() - el.width();
                var margin = {
                    top: opts.valign == 'top' ? opts.padding : opts.valign == 'bottom' ? h - opts.padding : Math.floor(h / 2),
                    left: opts.align == 'left' ? opts.padding : opts.align == 'right' ? w - opts.padding : Math.floor(w / 2)
                };
                var offset = $this.offset();
                if (opts.outside) {
                    el.css({ top: offset.top + 'px', left: offset.left + 'px' });
                }
                else {
                    margin.top -= el.offset().top - offset.top;
                    margin.left -= el.offset().left - offset.left;
                }
                el.css({ marginTop: margin.top + 'px', marginLeft: margin.left + 'px' });
//                el.css("z-index", 10000)
                animate(el, opts.segments, Math.round(10 / opts.speed) / 10);
                $this.data('activity', el);

                if (opts.txt) {
                    var l = w - 38;
                    $('#dialogcontent').append("<div style='width:" + l + "px;margin-top:" + (margin.top + 35) + "px;text-align:center;'>" + opts.txt + "</div>")
                }
            }
        });
        return this;
    };

    $.fn.activity.defaults = {
        segments: 12,
        space: 3,
        length: 7,
        width: 4,
        speed: 1.2,
        align: 'center',
        valign: 'center',
        padding: 4
    };

    $.fn.activity.getOpacity = function (opts, i) {
        var steps = opts.steps || opts.segments - 1;
        var end = opts.opacity !== undefined ? opts.opacity : 1 / steps;
        return Math.min(i, steps) * (1 - end) / steps;
    };

    /**
    * Default rendering strategy. If neither SVG nor VML is available, a div with class-name 'busy' 
    * is inserted, that can be styled with CSS to display an animated gif as fallback.
    */
    var render = function () {
        return $('<div>').addClass('busy');
    };

    /**
    * The default animation strategy does nothing as we expect an animated gif as fallback.
    */
    var animate = function () {
    };

    /**
    * Utility function to create elements in the SVG namespace.
    */
    function svg(tag, attr) {
        var el = document.createElementNS("http://www.w3.org/2000/svg", tag || 'svg');
        if (attr) {
            $.each(attr, function (k, v) {
                el.setAttributeNS(null, k, v);
            });
        }
        return $(el);
    }

    if (document.createElementNS && document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGRect) {

        // =======================================================================================
        // SVG Rendering
        // =======================================================================================

        /**
        * Rendering strategy that creates a SVG tree.
        */
        render = function (target, d) {
            var innerRadius = d.width * 2 + d.space;
            var r = (innerRadius + d.length + Math.ceil(d.width / 2) + 1);

            var el = svg().width(r * 2).height(r * 2);

            var g = svg('g', {
                'stroke-width': d.width,
                'stroke-linecap': 'round',
                stroke: d.color
            }).appendTo(svg('g', { transform: 'translate(' + r + ',' + r + ')' }).appendTo(el));

            for (var i = 0; i < d.segments; i++) {
                g.append(svg('line', {
                    x1: 0,
                    y1: innerRadius,
                    x2: 0,
                    y2: innerRadius + d.length,
                    transform: 'rotate(' + (360 / d.segments * i) + ', 0, 0)',
                    opacity: $.fn.activity.getOpacity(d, i)
                }));
            }
            return $('<div>').append(el).width(2 * r).height(2 * r);
        };

        // Check if Webkit CSS animations are available, as they work much better on the iPad
        // than setTimeout() based animations.

        if (document.createElement('div').style.WebkitAnimationName !== undefined) {

            var animations = {};

            /**
            * Animation strategy that uses dynamically created CSS animation rules.
            */
            animate = function (el, steps, duration) {
                if (!animations[steps]) {
                    var name = 'spin' + steps;
                    var rule = '@-webkit-keyframes ' + name + ' {';
                    for (var i = 0; i < steps; i++) {
                        var p1 = Math.round(100000 / steps * i) / 1000;
                        var p2 = Math.round(100000 / steps * (i + 1) - 1) / 1000;
                        var value = '% { -webkit-transform:rotate(' + Math.round(360 / steps * i) + 'deg); }\n';
                        rule += p1 + value + p2 + value;
                    }
                    rule += '100% { -webkit-transform:rotate(100deg); }\n}';
                    document.styleSheets[0].insertRule(rule);
                    animations[steps] = name;
                }
                el.css('-webkit-animation', animations[steps] + ' ' + duration + 's linear infinite');
            };
        }
        else {

            /**
            * Animation strategy that transforms a SVG element using setInterval().
            */
            animate = function (el, steps, duration) {
                var rotation = 0;
                var g = el.find('g g').get(0);
                el.data('interval', setInterval(function () {
                    g.setAttributeNS(null, 'transform', 'rotate(' + (++rotation % steps * (360 / steps)) + ')');
                }, duration * 1000 / steps));
            };
        }

    }
    else {

        // =======================================================================================
        // VML Rendering
        // =======================================================================================

        var s = $('<shape>').css('behavior', 'url(#default#VML)');

        $('body').append(s);

        if (s.get(0).adj) {

            // VML support detected. Insert CSS rules for group, shape and stroke.
            var sheet = document.createStyleSheet();
            $.each(['group', 'shape', 'stroke'], function () {
                sheet.addRule(this, "behavior:url(#default#VML);");
            });

            /**
            * Rendering strategy that creates a VML tree. 
            */
            render = function (target, d) {

                var innerRadius = d.width * 2 + d.space;
                var r = (innerRadius + d.length + Math.ceil(d.width / 2) + 1);
                var s = r * 2;
                var o = -Math.ceil(s / 2);

                var el = $('<group>', { coordsize: s + ' ' + s, coordorigin: o + ' ' + o }).css({ top: o, left: o, width: s, height: s });
                for (var i = 0; i < d.segments; i++) {
                    el.append($('<shape>', { path: 'm ' + innerRadius + ',0  l ' + (innerRadius + d.length) + ',0' }).css({
                        width: s,
                        height: s,
                        rotation: (360 / d.segments * i) + 'deg'
                    }).append($('<stroke>', { color: d.color, weight: d.width + 'px', endcap: 'round', opacity: $.fn.activity.getOpacity(d, i) })));
                }
                return $('<group>', { coordsize: s + ' ' + s }).css({ width: s, height: s, overflow: 'hidden' }).append(el);
            };

            /**
            * Animation strategy that modifies the VML rotation property using setInterval().
            */
            animate = function (el, steps, duration) {
                var rotation = 0;
                var g = el.get(0);
                el.data('interval', setInterval(function () {
                    g.style.rotation = ++rotation % steps * (360 / steps);
                }, duration * 1000 / steps));
            };
        }
        $(s).remove();
    }

})(jQuery);

