[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / plugins-dist / compagnon / prive / javascript / jquery.twinkle.js
index 227cd25..536924c 100644 (file)
-/*
- * jQuery.twinkle 0.2
- * http://larsjung.de/twinkle
- * 
- * provided under the terms of the MIT License
- */
+/* jQuery.twinkle 0.8.0 - http://larsjung.de/jquery-twinkle/ */
+(function () {
+'use strict';
 
-( function( $ ) {
+var $ = jQuery;
 
+var defaults = {
+        widthRatio: 0.5,
+        heightRatio: 0.5,
+        delay: 0,
+        gap: 0,
+        effect: 'splash',
+        effectOptions: undefined,
+        callback: undefined
+    };
 
-       var TwinkleEvent = function ( offX, offY, element, posX, posY ) {
+var stopDefaults = {
+        id: undefined,
+        effectOptions: undefined,
+        callback: undefined
+    };
 
-               this.offset = { "left": offX, "top": offY };
-               this.element = element;
-               this.position = { "left": posX, "top": posY };
-       };
 
+function TwinkleEvent(offX, offY, element, posX, posY) {
 
-       var Twinkler = function () {
+    this.offset = {left: offX, top: offY};
+    this.element = element;
+    this.position = {left: posX, top: posY};
+}
 
-               this.defaults = {
-                       "widthRatio": 0.5,
-                       "heightRatio": 0.5,
-                       "delay": 0,
-                       "gap": 0,
-                       "effect": "splash",
-                       "effectOptions": undefined 
-               };
-               this.effects = {};
 
+function StopEvent(element) {
 
-               this.twinkle = function ( event, options ) {
+    this.element = element;
+}
 
-                       var settings = $.extend( {}, this.defaults, options );
 
-                       var effect = this.effects[settings.effect];
-                       if ( effect !== undefined ) {
-                               event.element = event.element || "body";
-                               effect.run( event, settings.effectOptions );
-                       };
-               };
+function Twinkler() {
 
+    var effects = {};
+    var running = {}; // element => {id: handle}
 
-               this.twinkleAtElement = function ( htmlElement, options ) {
+    function effectStarted(element, id, handle) {}
+    function effectStopped(element, id) {}
 
-                       var settings = $.extend( {}, this.defaults, options );
-                       
-                       var $ele = $( htmlElement );
-                       
-                       var off = $ele.offset();
-                       var offX = off.left + $ele.outerWidth( true ) * settings.widthRatio;
-                       var offY = off.top + $ele.outerHeight( true ) * settings.heightRatio;
+    this.add = function (effect) {
 
-                       var pos = $ele.position();
-                       var posX = pos.left + $ele.outerWidth( true ) * settings.widthRatio;
-                       var posY = pos.top + $ele.outerHeight( true ) * settings.heightRatio;
+        if (!effects[effect.id]) {
+            effects[effect.id] = effect;
+        }
+        return this;
+    };
 
-                       var event = new TwinkleEvent( offX, offY, htmlElement, posX, posY );
+    this.remove = function (effect) {
 
-                       this.twinkle( event, options );
-               };
+        if (effects[effect]) {
+            delete effects[effect];
+        } else if (effect.id && effects[effect.id]) {
+            delete effects[effect.id];
+        }
+        return this;
+    };
 
-               
-               this.twinkleAtElements = function ( htmlElements, options ) {
+    this.twinkle = function (event, options) {
 
-                       var THIS = this;
-                       var settings = $.extend( {}, this.defaults, options );
+        var settings = $.extend({}, defaults, options);
+        var effect = effects[settings.effect];
 
-                       var delay = settings.delay;
-                       $( htmlElements ).each( function () {
-                               var htmlElement = this;
-                               setTimeout( function () {
-                                       THIS.twinkleAtElement( htmlElement, options );
-                               }, delay );
-                               delay += settings.gap;
-                       } );
-               };
-       };
-       
+        if (effect) {
+            event.element = event.element || 'body';
+            effect.run(event, settings.effectOptions, function () {
 
+                if ($.isFunction(settings.callback)) {
+                    settings.callback();
+                }
+            });
+        }
+        return this;
+    };
 
-       var twinkler = new Twinkler();
-       var namespace = "twinkle";
+    this.stop = function (event, options) {
 
+        var settings = $.extend({}, stopDefaults, options);
+        var effect = effects[settings.effect];
 
-       var globals = {
+        if (effect) {
+            event.element = event.element || 'body';
+            effect.stop(event, settings.effectOptions, settings.callback);
+        }
+        return this;
+    };
+
+    this.twinkleAtElement = function (htmlElement, options) {
+
+        var settings = $.extend({}, defaults, options);
+        var $htmlElement = $(htmlElement);
+        var offset = $htmlElement.offset();
+        var position = $htmlElement.position();
+        var width = $htmlElement.outerWidth(true);
+        var height = $htmlElement.outerHeight(true);
+        var offX = offset.left + width * settings.widthRatio;
+        var offY = offset.top + height * settings.heightRatio;
+        var posX = position.left + width * settings.widthRatio;
+        var posY = position.top + height * settings.heightRatio;
+
+        return this.twinkle(new TwinkleEvent(offX, offY, htmlElement, posX, posY), options);
+    };
+
+    this.twinkleAtElements = function (htmlElements, options) {
+
+        var self = this;
+        var settings = $.extend({}, defaults, options);
+        var delay = settings.delay;
+        var $htmlElements = $(htmlElements);
+        var size = $htmlElements.size();
 
-               twinkle: function ( element, left, top, options ) {
+        $htmlElements.each(function (idx) {
+
+            var htmlElement = this;
+            var opts = $.extend({}, options);
 
-                       var event = new TwinkleEvent( 0, 0, element, left, top );
-                       twinkler.twinkle( event, options );
-                       return globals;
+            if (idx !== size - 1) {
+                delete opts.callback;
+            }
+
+            setTimeout(function () {
+                self.twinkleAtElement(htmlElement, opts);
+            }, delay);
+
+            delay += settings.gap;
+        });
+        return this;
+    };
+
+    this.stopAtElement = function (htmlElement, options) {
+
+        var settings = $.extend({}, defaults, options);
+        var $htmlElement = $(htmlElement);
+        var offset = $htmlElement.offset();
+        var position = $htmlElement.position();
+        var width = $htmlElement.outerWidth(true);
+        var height = $htmlElement.outerHeight(true);
+        var offX = offset.left + width * settings.widthRatio;
+        var offY = offset.top + height * settings.heightRatio;
+        var posX = position.left + width * settings.widthRatio;
+        var posY = position.top + height * settings.heightRatio;
+
+        return this.twinkle(new TwinkleEvent(offX, offY, htmlElement, posX, posY), options);
+    };
+
+    this.stopAtElements = function (htmlElements, options) {
+
+        var self = this;
+        var settings = $.extend({}, stopDefaults, options);
+        var delay = settings.delay;
+        var $htmlElements = $(htmlElements);
+        var size = $htmlElements.size();
+
+        $htmlElements.each(function (idx) {
+
+            var htmlElement = this,
+                opts = $.extend({}, options);
+
+            if (idx !== size - 1) {
+                delete opts.callback;
+            }
+
+            self.stopAtElement(htmlElement, opts);
+        });
+        return this;
+    };
+}
+
+
+
+/*!
+modplug 1.0
+http://larsjung.de/modplug
+MIT License
+*/
+
+// This function is ment to be copied into your plugin file as a local
+// variable.
+//
+// `modplug` expects a string `namespace` and a configuration object
+// `options`.
+//
+//      options = {
+//          statics: hash of functions,
+//          methods: hash of functions,
+//          defaultStatic: String/function,
+//          defaultMethod: String/function
+//      }
+//
+// For more details see <http://larsjung.de/modplug>.
+var modplug = function (namespace, options) {
+       'use strict';
+       /*global jQuery: true */
+
+               // Some references to enhance minification.
+       var slice = [].slice,
+               $ = jQuery,
+               extend = $.extend,
+               isFn = $.isFunction,
+
+               // Save the initial settings.
+               settings = extend({}, options),
+
+               // Helper function to apply default methods.
+               applyMethod = function (obj, args, methodName, methods) {
+
+                       // If `methodName` is a function apply it to get the actual
+                       // method name.
+                       methodName = isFn(methodName) ? methodName.apply(obj, args) : methodName;
+
+                       // If method exists then apply it and return the result ...
+                       if (isFn(methods[methodName])) {
+                               return methods[methodName].apply(obj, args);
+                       }
+
+                       // ... otherwise raise an error.
+                       $.error('Method "' + methodName + '" does not exist on jQuery.' + namespace);
                },
 
-               add: function ( effect ) {
-                       
-                       if ( twinkler.effects[effect.id] === undefined ) {
-                               twinkler.effects[effect.id] = effect;
-                       };
-                       return globals;
-               },
+               // This function gets exposed as `$.<namespace>`.
+               statics = function () {
 
-               remove: function ( effect ) {
-                       
-                       if ( twinkler.effects[effect.id] !== undefined ) {
-                               delete twinkler.effects[effect.id];
-                       };
-                       return globals;
-               }
-       };
+                       // Try to apply a default method.
+                       return applyMethod(this, slice.call(arguments), settings.defaultStatic, statics);
+               },
 
+               // This function gets exposed as `$(selector).<namespace>`.
+               methods = function (method) {
 
-       var methods = {
+                       // If `method` exists then apply it ...
+                       if (isFn(methods[method])) {
+                               return methods[method].apply(this, slice.call(arguments, 1));
+                       }
 
-               twinkle: function ( options ) {
-                       
-                       twinkler.twinkleAtElements( this, options );
-                       return this;
-               }
-       };
+                       // ... otherwise try to apply a default method.
+                       return applyMethod(this, slice.call(arguments), settings.defaultMethod, methods);
+               },
 
+               // Adds/overwrites plugin methods. This function gets exposed as
+               // `$.<namespace>.modplug` to make the plugin extendable.
+               plug = function (options) {
 
-       $[namespace] = globals;
-       $.fn[namespace] = function( method ) {
+                       if (options) {
+                               extend(statics, options.statics);
+                               extend(methods, options.methods);
+                       }
 
-               if ( methods[method] ) {
-                       return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ) );
-               } else if ( method === undefined || method instanceof Object ) {
-                       return methods.twinkle.apply( this, arguments );
-               } else {
-                       $.error( "Method " +  method + " does not exist on jQuery." + namespace );
+                       // Make sure that `$.<namespace>.modplug` points to this function
+                       // after adding new methods.
+                       statics.modplug = plug;
                };
+
+       // Save objects or methods previously registered to the desired namespace.
+       // They are available via `$.<namespace>.modplug.prev`.
+       plug.prev = {
+               statics: $[namespace],
+               methods: $.fn[namespace]
        };
 
+       // Init the plugin by adding the specified statics and methods.
+       plug(options);
+
+       // Register the plugin.
+       $[namespace] = statics;
+       $.fn[namespace] = methods;
+};
+
+
+var twinkler = new Twinkler();
+modplug('twinkle', {
+    statics: {
+        twinkle: function (element, left, top, options) {
+
+            twinkler.twinkle(new TwinkleEvent(0, 0, element, left, top), options);
+            return this;
+        },
+        add: function (effect) {
+
+            twinkler.add(effect);
+            return this;
+        },
+        remove: function (effect) {
+
+            twinkler.remove(effect);
+            return this;
+        }
+    },
+    methods: {
+        twinkle: function (options) {
+
+            twinkler.twinkleAtElements(this, options);
+            return this;
+        },
+        stop: function (options) {
+
+            twinkler.stopAtElements(this, options);
+            return this;
+        }
+    },
+    defaultStatic: 'twinkle',
+    defaultMethod: 'twinkle'
+});
+
+}());
+
+(function () {
+'use strict';
+/* CSS Effects */
+
+var $ = jQuery;
+
+function blockEvents(event) {
+
+    event.stopImmediatePropagation();
+    event.preventDefault();
+    return false;
+}
+
+function animation(css, event, settings, callback) {
+
+    var $dot;
+
+    function cleanUp() {
+
+        $dot.remove();
+        if (callback instanceof Function) {
+            callback();
+        }
+    }
+
+    function fadeOut() {
+
+        $dot.animate(
+            {
+                left: event.position.left - settings.radius,
+                top: event.position.top - settings.radius,
+                width: settings.radius * 2,
+                height: settings.radius * 2,
+                opacity: 0
+            },
+            settings.duration * 0.5,
+            'linear',
+            cleanUp
+        );
+    }
+
+    function fadeIn() {
+
+        $dot = $('<div />')
+                .css(css)
+                .on('click dblclick mousedown mouseenter mouseover mousemove', blockEvents);
+        $(event.element).after($dot);
+        $dot.animate(
+            {
+                left: event.position.left - settings.radius * 0.5,
+                top: event.position.top - settings.radius * 0.5,
+                width: settings.radius,
+                height: settings.radius,
+                opacity: 1
+            },
+            settings.duration * 0.5,
+            'linear',
+            fadeOut
+        );
+    }
+
+    function stop() {}
+
+    fadeIn();
+
+    return {
+        stop: stop
+    };
+}
+
+var splashDefaults = {
+        color: 'rgba(255,0,0,0.5)',
+        radius: 300,
+        duration: 1000
+    };
+
+function SplashEffect() {
+
+    this.id = 'splash-css';
+
+    this.run = function (event, options, callback) {
+
+        var settings = $.extend({}, splashDefaults, options);
+        var css = {
+                position: 'absolute',
+                zIndex: 1000,
+                display: 'block',
+                borderRadius: settings.radius,
+                backgroundColor: settings.color,
+                boxShadow: '0 0 30px ' + settings.color,
+                left: event.position.left,
+                top: event.position.top,
+                width: 0,
+                height: 0,
+                opacity: 0.4
+            };
 
-} )( jQuery );
+        animation(css, event, settings, callback);
+    };
+}
+
+var dropsDefaults = {
+        color: 'rgba(255,0,0,0.5)',
+        radius: 300,
+        duration: 1000,
+        width: 2,
+        count: 3,
+        delay: 300
+    };
+
+function DropsEffect() {
+
+    this.id = 'drops-css';
+
+    this.run = function (event, options, callback) {
+
+        var settings = $.extend({}, dropsDefaults, options);
+        var css = {
+                position: 'absolute',
+                zIndex: 1000,
+                display: 'block',
+                borderRadius: settings.radius,
+                border: settings.width + 'px solid ' + settings.color,
+                left: event.position.left,
+                top: event.position.top,
+                width: 0,
+                height: 0,
+                opacity: 0.4
+            };
+
+        function setTimer(delay, callback) {
+            setTimeout(function () {
+                animation(css, event, settings, callback);
+            }, delay);
+        }
 
+        var delay = 0;
+        var i;
 
-/*
- * jQuery.twinkle 0.2
- * CSS Effects
- * http://larsjung.de/twinkle
- * 
- * provided under the terms of the MIT License
- */
+        for (i = 0; i < settings.count; i += 1) {
+            setTimer(delay, i === settings.count - 1 ? callback : undefined);
+            delay += settings.delay;
+        }
+    };
+}
 
-( function( $ ) {
+function DropEffect() {
 
-       
-       function animation( css, event, settings ) {
+    var drops = new DropsEffect();
 
-               var $dot = $( "<div />" ).css( css ).appendTo( event.element );
+    this.id = 'drop-css';
 
-               var fadeIn = function() {
-                       $dot.animate(
-                               {
-                                       "left": event.position.left - settings.radius * 0.5,
-                                       "top": event.position.top - settings.radius * 0.5,
-                                       "width": settings.radius,
-                                       "height": settings.radius,
-                                       "opacity": 1
-                               },
-                               settings.duration * 0.5,
-                               "linear",
-                               fadeOut
-                       );
-               };
-               var fadeOut = function () {
-                       $dot.animate(
-                               {
-                                       "left": event.position.left - settings.radius,
-                                       "top": event.position.top - settings.radius,
-                                       "width": settings.radius * 2,
-                                       "height": settings.radius * 2,
-                                       "opacity": 0
-                               },
-                               settings.duration * 0.5,
-                               "linear",
-                               cleanUp
-                       );
-               };
-               var cleanUp = function () {
-                       $dot.remove();
-               };
+    this.run = function (event, options, callback) {
 
-               fadeIn();
-       };
+        drops.run(event, $.extend(options, { count: 1 }), callback);
+    };
+}
 
-       
-       var SplashEffect = function () {
+$.twinkle.add(new SplashEffect()).add(new DropEffect()).add(new DropsEffect());
 
-               var defaults = {
-                       "color": "rgba(255,0,0,0.5)",
-                       "radius": 300,
-                       "duration": 1000
-               };
+}());
 
-               this.id = "splash-css";
-
-               this.run = function ( event, options ) {
-       
-                       var settings = $.extend( {}, defaults, options );
-                       var css = {
-                               "position": "absolute",
-                               "z-index": 1000,
-                               "display": "block",
-                               "border-radius": settings.radius,
-                               "background-color": settings.color,
-                               "box-shadow": "0 0 30px " + settings.color,
-                               "left": event.position.left,
-                               "top": event.position.top,
-                               "width": 0,
-                               "height": 0,
-                               "opacity": 0.4
-                       };
-
-                       animation( css, event, settings );
-               };
-       };
+(function () {
+'use strict';
+/* Canvas Effects */
 
+var $ = jQuery;
+var Objects = {};
 
-       var DropEffect = function () {
+(function () {
 
-               var drops = new DropsEffect();
+function Interpolator(values) {
 
-               this.id = "drop-css";
+    var points;
 
-               this.run = function ( event, options ) {
-       
-                       drops.run( event, $.extend( options, { count: 1 } ) );
-               };
-       };
+    function equiDist(values) {
 
+        var dist = 1 / (values.length - 1);
+        var points = [];
+        var i;
 
-       var DropsEffect = function () {
+        for (i = 0; i < values.length; i += 1) {
+            points.push({ x: dist * i , y: values[i] });
+        }
+        return points;
+    }
 
-               var defaults = {
-                       "color": "rgba(255,0,0,0.5)",
-                       "radius": 300,
-                       "duration": 1000,
-                       "width": 2,
-                       "count": 3,
-                       "delay": 300
-               };
+    function interpolate(p1, p2, x) {
 
-               this.id = "drops-css";
-
-               this.run = function ( event, options ) {
-       
-                       var settings = $.extend( {}, defaults, options );
-                       var css = {
-                               "position": "absolute",
-                               "z-index": 1000,
-                               "display": "block",
-                               "border-radius": settings.radius,
-                               "border": "" + settings.width + "px solid " + settings.color,
-                               "left": event.position.left,
-                               "top": event.position.top,
-                               "width": 0,
-                               "height": 0,
-                               "opacity": 0.4
-                       };
-
-                       var delay = 0;
-                       for ( var i = 0; i < settings.count; i++ ) {
-                               setTimeout( function () {
-                                       animation( css, event, settings );
-                               }, delay );
-                               delay += settings.delay;
-                       };
-               };
-       };
+        var m = (p2.y - p1.y) / (p2.x - p1.x);
+        var y = p1.y + m * (x - p1.x);
 
+        return y;
+    }
 
-       $.twinkle.add( new SplashEffect() );
-       $.twinkle.add( new DropEffect() );
-       $.twinkle.add( new DropsEffect() );
+    function findSection(x) {
 
+        var i, prev, current;
 
-} )( jQuery );
+        for (i = 1; i < points.length; i += 1) {
+            prev = points[i-1];
+            current = points[i];
+            if (x >= prev.x && x <= current.x) {
+                return [ prev, current ];
+            }
+        }
 
+        return undefined;
+    }
 
-/*
- * jQuery.twinkle 0.2
- * Canvas Effects
- * http://larsjung.de/twinkle
- * 
- * provided under the terms of the MIT License
- */
+    points = equiDist(values);
 
-( function( $ ) {
+    this.get = function (x) {
 
+        var secPts;
 
-       var Interpolator = function ( values ) {
+        x = Math.max(0, Math.min(1, x));
+        secPts = findSection(x);
+        return interpolate(secPts[0], secPts[1], x);
+    };
+}
 
-               var equiDist = function( values ) {
+function scaleit(x, scale, offset) {
 
-                       var dist = 1 / ( values.length - 1 );
-                       var points = [];
-                       for ( var i = 0; i < values.length; i++ ) {
-                               points.push( { x: dist * i , y: values[i] } );
-                       };
-                       return points;
-               };
+    scale = scale || 1;
+    offset = offset || 0;
+    x = (x - offset) / scale;
+    return x >= 0 && x <= 1 ? x : undefined;
+}
 
-               var interpolate = function ( p1, p2, x ) {
-                       
-                       var m = ( p2.y - p1.y ) / ( p2.x - p1.x );
-                       var y = p1.y + m * ( x - p1.x );
-                       return y;
-               };
-               
-               var findSection = function ( x ) {
-                       
-                       for ( var i = 0; i < points.length; i++ ) {
-                               
-                               if ( i === 0 ) {
-                                       continue;
-                               };
-                               
-                               var prev = points[i-1];
-                               var current = points[i];
-                               if ( x >= prev.x && x <= current.x ) {
-                                       return [ prev, current ];
-                               };
-                       };
-                       return undefined;
-               };
+Objects.Interpolator = Interpolator;
+Objects.Interpolator.scale =  scaleit;
 
-               var points = equiDist( values );
-               
-               this.get = function ( x ) {
+}());
 
-                       x = Math.max( 0, Math.min( 1, x ) );
-                       var secPts = findSection( x );
-                       return interpolate( secPts[0], secPts[1], x );
-               };
-       
-       };
-       Interpolator.scale = function ( x, scale, offset ) {
-               
-               scale = scale || 1;
-               offset = offset || 0;
-               x = ( x - offset ) / scale;
-               return x >= 0 && x <= 1 ? x : undefined;
-       };
+(function () {
 
+var $ = jQuery;
 
-       var FrameEvent = function ( ctx, frac, millis ) {
+function Path(ctx) {
 
-               this.ctx = ctx;
-               this.frac = frac;
-               this.millis = millis;
-       };
+    var context = ctx.getContext();
 
+    context.beginPath();
 
-       var CanvasEffect = function ( twinkleEvent, width, height, frame ) {
-
-               this.element = twinkleEvent.element;
-               this.x = twinkleEvent.position.left;
-               this.y = twinkleEvent.position.top;
-               this.width = width;
-               this.height = height;
-               this.frame = frame;
-               this.$canvas = undefined;
-               this.ctx = undefined;
-
-               this.init = function () {
-                       
-                       var css = {
-                               "position": "absolute",
-                               "z-index": 1000,
-                               "display": "block",
-                               "left": this.x - this.width * 0.5,
-                               "top": this.y - this.height * 0.5,
-                               "width": this.width,
-                               "height": this.height
-                       };
-
-                       this.$canvas = $( "<canvas width='" + this.width + "' height='" + this.height + "' />" ).css( css ).appendTo( this.element );
-                       this.ctx = new Ctx( this.$canvas.get( 0 ).getContext( "2d" ) );
-               };
+    this.fill = function (fillStyle) {
 
-               this.destroy = function () {
+        context.fillStyle = fillStyle;
+        context.fill();
+        return ctx;
+    };
 
-                       this.$canvas.remove();
-                       this.$canvas = undefined;
-                       this.ctx = undefined;
-               };
+    this.stroke = function (lineWidth, strokeStyle) {
 
-               this.run = function ( duration, fps ) {
+        context.lineWidth = lineWidth;
+        context.strokeStyle = strokeStyle;
+        context.stroke();
+        return ctx;
+    };
 
-                       this.init();
+    this.draw = function (lineWidth, strokeStyle, fillStyle) {
 
-                       var THIS = this;
-                       var frameCount = duration / 1000 * fps;
-                       var delta = 1 / frameCount;
-                       for ( var i = 0; i < frameCount + 1; i++ ) {
-                               ( function ( frac ) {
-                                       setTimeout( function () {
-                                               if ( THIS.ctx ) {
-                                                       THIS.frame( new FrameEvent( THIS.ctx, frac, duration * frac ) );
-                                               };
-                                       }, duration * frac );
-                               } )( i * delta );
-                       };
+        this.fill(fillStyle);
+        this.stroke(lineWidth, strokeStyle);
+        return ctx;
+    };
 
-                       setTimeout( $.proxy( this.destroy, this ), duration );
-               };
+    this.circle = function (x, y, radius) {
 
-       };
+        context.arc(x, y, radius, 0, 2 * Math.PI, false);
+        return this;
+    };
+}
 
-       
-       var Path = function ( ctx ) {
+function Ctx(context) {
 
-               var context = ctx.context;
-               context.beginPath();
+    if (!context || !context.canvas) {
+        return undefined;
+    } else if (!(this instanceof Ctx)) {
+        return new Ctx(context);
+    }
 
-               this.circle = function ( x, y, radius ) {
-                       
-                       context.arc( x, y, radius, 0, 2 * Math.PI, false );
-                       return this;
-               };
+    var width = $(context.canvas).width();
+    var height = $(context.canvas).height();
 
-               this.stroke = function ( strokeWidth, strokeStyle ) {
+    this.getContext = function () {
 
-                       context.lineWidth = strokeWidth;
-                       context.strokeStyle = strokeStyle;
-                       context.stroke();
-                       return ctx;
-               };
-               
-               this.fill = function ( fillStyle ) {
+        return context;
+    };
 
-                       context.fillStyle = fillStyle;
-                       context.fill();
-                       return ctx;
-               };
-               
-               this.draw = function ( strokeWidth, strokeStyle, fillStyle ) {
+    this.getWidth = function () {
 
-                       this.stroke( strokeWidth, strokeStyle );
-                       this.fill( fillStyle );
-                       return ctx;
-               };
-       };
+        return width;
+    };
 
+    this.getHeight = function () {
 
-       var Ctx = function ( context ) {
-               
-               this.context = context;
-               this.width = $( context.canvas ).width();
-               this.height = $( context.canvas ).height();
-               
-               this.clear = function () {
+        return height;
+    };
 
-                       this.resetTransform();
-                       this.context.clearRect( 0, 0, this.width, this.height );
-                       return this;
-               };
+    this.clear = function () {
 
-               this.resetTransform = function () {
-                       
-                       this.context.setTransform( 1, 0, 0, 1, 0, 0 );
-                       return this;
-               };
-               
-               this.translate = function ( x, y ) {
+        this.resetTransform();
+        context.clearRect(0, 0, width, height);
+        return this;
+    };
 
-                       this.context.translate( x, y );
-                       return this;
-               };
+    this.resetTransform = function () {
 
-               this.rotate = function ( alpha ) {
-                       
-                       this.context.rotate( Math.PI * alpha / 180 );
-                       return this;
-               };
+        context.setTransform(1, 0, 0, 1, 0, 0);
+        return this;
+    };
 
-               this.opacity = function ( opacity ) {
-                       
-                       this.context.globalAlpha = opacity;                     
-                       return this;
-               };
+    this.translate = function (x, y) {
 
-               this.path = function () {
-                       
-                       return new Path( this );
-               };
-       };
+        context.translate(x, y);
+        return this;
+    };
 
+    this.rotate = function (alpha) {
 
+        context.rotate(Math.PI * alpha / 180);
+        return this;
+    };
 
-       var SplashEffect = function () {
+    this.opacity = function (opacity) {
 
-               var defaults = {
-                       "color": "rgba(255,0,0,0.5)",
-                       "radius": 300,
-                       "duration": 1000
-               };
+        context.globalAlpha = opacity;
+        return this;
+    };
 
-               this.id = "splash";
-
-               this.run = function ( twinkleEvent, options ) {
-
-                       var settings = $.extend( {}, defaults, options );
-                       var size = settings.radius * 2;
-                       var opacityIpl = new Interpolator( [ 0.4, 1, 0 ] );
-                       var radiusIpl = new Interpolator( [ 0, settings.radius ] );
-                       var frame = function ( frameEvent ) {
-                               
-                               var radius = radiusIpl.get( frameEvent.frac );
-                               var opacity = opacityIpl.get( frameEvent.frac );
-                               
-                               this.ctx
-                                       .clear()
-                                       .opacity( opacity )
-                                       .path()
-                                       .circle( this.width * 0.5, this.height * 0.5, radius )
-                                       .fill( settings.color );
-                       };
-                       
-                       new CanvasEffect( twinkleEvent, size, size, frame ).run( settings.duration, 25 );
-               };
-       };
+    this.path = function () {
 
-       $.twinkle.add( new SplashEffect() );
+        return new Path(this);
+    };
+}
 
+Objects.Ctx = Ctx;
 
+}());
 
-       var DropEffect = function () {
+(function () {
 
-               var defaults = {
-                       "color": "rgba(255,0,0,0.5)",
-                       "radius": 300,
-                       "duration": 1000,
-                       "width": 2
-               };
+function CanvasEffect(twinkleEvent, width, height, frame, callback) {
 
-               this.id = "drop";
-
-               this.run = function ( twinkleEvent, options ) {
-
-                       var settings = $.extend( {}, defaults, options );
-                       var size = settings.radius * 2;
-                       var opacityIpl = new Interpolator( [ 0.4, 1, 0 ] );
-                       var radiusIpl = new Interpolator( [ 0, settings.radius ] );
-                       var frame = function ( frameEvent ) {
-                               
-                               var radius = radiusIpl.get( frameEvent.frac );
-                               var opacity = opacityIpl.get( frameEvent.frac );
-
-                               this.ctx
-                                       .clear()
-                                       .opacity( opacity )
-                                       .path()
-                                       .circle( this.width * 0.5, this.height * 0.5, radius )
-                                       .stroke( settings.width, settings.color );
-                       };
-                       
-                       new CanvasEffect( twinkleEvent, size, size, frame ).run( settings.duration, 25 );
-               };
-       };
+    if (!(this instanceof Objects.CanvasEffect)) {
+        return new Objects.CanvasEffect(twinkleEvent, width, height, frame, callback);
+    }
 
-       $.twinkle.add( new DropEffect() );
+    var element = twinkleEvent.element;
+    var x = twinkleEvent.position.left;
+    var y = twinkleEvent.position.top;
+    var css = {
+            position: 'absolute',
+            zIndex: 1000,
+            display: 'block',
+            left: x - width * 0.5,
+            top: y - height * 0.5,
+            width: width,
+            height: height
+        };
 
+    this.run = function (duration, fps) {
 
+        var $canvas, ctx, i;
+        var frameCount = duration / 1000 * fps;
+        var delta = 1 / frameCount;
 
-       var DropsEffect = function () {
+        function setFrameTimer(fraction) {
 
-               var defaults = {
-                       "color": "rgba(255,0,0,0.5)",
-                       "radius": 300,
-                       "duration": 1000,
-                       "width": 2,
-                       "count": 3,
-                       "delay": 100
-               };
+            setTimeout(function () {
 
-               this.id = "drops";
-
-               this.run = function ( twinkleEvent, options ) {
-
-                       var settings = $.extend( {}, defaults, options );
-                       var size = settings.radius * 2;
-                       var opacityIpl = new Interpolator( [ 0.4, 1, 0 ] );
-                       var radiusIpl = new Interpolator( [ 0, settings.radius ] );
-                       var scale = ( settings.duration - ( settings.count - 1 ) * settings.delay ) / settings.duration;
-                       var offset = settings.delay / settings.duration;
-                       
-                       var frame = function ( frameEvent ) {
-                               
-                               this.ctx.clear();
-                               for ( var i = 0; i < settings.count; i++ ) {
-                                       var frac = Interpolator.scale( frameEvent.frac, scale, offset * i );
-
-                                       if ( frac !== undefined ) {
-                                               var radius = radiusIpl.get( frac );
-                                               var opacity = opacityIpl.get( frac );
-                                               this.ctx
-                                                       .opacity( opacity )
-                                                       .path()
-                                                       .circle( this.width * 0.5, this.height * 0.5, radius )
-                                                       .stroke( settings.width, settings.color );
-                                       };
-                               };
-                       };
-                       
-                       new CanvasEffect( twinkleEvent, size, size, frame ).run( settings.duration, 25 );
-               };
-       };
+                if (ctx) {
+                    frame({
+                        ctx: ctx,
+                        frac: fraction,
+                        millis: duration * fraction
+                    });
+                }
+            }, duration * fraction);
+        }
 
-       $.twinkle.add( new DropsEffect() );
+        function cleanUp() {
 
+            $canvas.remove();
+            $canvas = undefined;
+            ctx = undefined;
+            if (callback instanceof Function) {
+                callback();
+            }
+        }
 
+        function blockEvents(event) {
 
-       var PulseEffect = function () {
+            event.stopImmediatePropagation();
+            event.preventDefault();
+            return false;
+        }
 
-               var defaults = {
-                       "color": "rgba(255,0,0,0.5)",
-                       "radius": 100,
-                       "duration": 3000
-               };
+        $canvas = jQuery('<canvas />').attr('width', width).attr('height', height).css(css);
+        jQuery(element).after($canvas);
+        $canvas.on('click dblclick mousedown mouseenter mouseover mousemove', blockEvents);
+        ctx = new Objects.Ctx($canvas.get(0).getContext('2d'));
 
-               this.id = "pulse";
-
-               this.run = function ( twinkleEvent, options ) {
-       
-                       var settings = $.extend( {}, defaults, options );
-                       var size = settings.radius * 2;
-                       var opacityIpl = new Interpolator( [ 0, 1, 0.6, 1, 0.6, 1, 0 ] );
-                       var radiusIpl = new Interpolator( [ 0, settings.radius, settings.radius * 0.6, settings.radius, settings.radius * 0.6, settings.radius, 0 ] );
-                       var frame = function ( frameEvent ) {
-                               
-                               var x = this.width * 0.5;
-                               var y = this.height * 0.5;
-                               var radius = radiusIpl.get( frameEvent.frac );
-                               var opacity = opacityIpl.get( frameEvent.frac );
-       
-                               this.ctx
-                                       .clear()
-                                       .opacity( opacity )
-                                       .path()
-                                       .circle( this.width * 0.5, this.height * 0.5, radius )
-                                       .fill( settings.color );
-                       };
-       
-                       new CanvasEffect( twinkleEvent, size, size, frame ).run( settings.duration, 25 );
-               };
-       };
+        for (i = 0; i <= frameCount; i += 1) {
+            setFrameTimer(i * delta);
+        }
 
-       $.twinkle.add( new PulseEffect() );
+        setTimeout(cleanUp, duration);
+    };
+}
 
+Objects.CanvasEffect = CanvasEffect;
 
+}());
 
-       var OrbitEffect = function () {
+(function () {
 
-               var defaults = {
-                       "color": "rgba(255,0,0,0.5)",
-                       "radius": 100,
-                       "duration": 3000,
-                       "satellites": 10,
-                       "satellitesRadius": 10,
-                       "circulations": 1.5
-               };
+var $ = jQuery;
 
-               this.id = "orbit";
-
-               this.run = function ( twinkleEvent, options ) {
-       
-                       var settings = $.extend( {}, defaults, options );
-                       var size = settings.radius * 2;
-                       var opacityIpl = new Interpolator( [ 0.4, 1, 1, 0.4 ] );
-                       var r = settings.radius - settings.satellitesRadius;
-                       var radiusIpl = new Interpolator( [ 0, r, r, 0 ] );
-                       var frame = function ( frameEvent ) {
-                               
-                               var radius = radiusIpl.get( frameEvent.frac );
-                               var opacity = opacityIpl.get( frameEvent.frac );
-                               var bog = Math.PI * 2 * settings.circulations * frameEvent.frac;
-       
-                               this.ctx
-                                       .clear()
-                                       .opacity( opacity )
-                                       .translate( this.width * 0.5, this.height * 0.5 );
-                               
-                               var path = this.ctx.path();
-                               for ( var i = 0; i < settings.satellites; i++ ) {
-
-                                       bog += Math.PI * 2 / settings.satellites;
-                                       var x = Math.cos( bog ) * radius;
-                                       var y = Math.sin( bog ) * radius;
-                                       path.circle( x, y, settings.satellitesRadius );
-                               };
-                               path.fill( settings.color );
-                       };
-       
-                       new CanvasEffect( twinkleEvent, size, size, frame ).run( settings.duration, 25 );
-               };
-       };
+var defaults = {
+        color: 'rgba(255,0,0,0.5)',
+        radius: 300,
+        duration: 1000
+    };
+
+function SplashEffect() {
+
+    this.id = 'splash';
+
+    this.run = function (twinkleEvent, options, callback) {
+
+        var settings = $.extend({}, defaults, options);
+        var size = settings.radius * 2;
+        var opacityIpl = new Objects.Interpolator([ 0.4, 1, 0 ]);
+        var radiusIpl = new Objects.Interpolator([ 0, settings.radius ]);
+
+        function frame(frameEvent) {
+
+            var radius = radiusIpl.get(frameEvent.frac);
+            var opacity = opacityIpl.get(frameEvent.frac);
+            var ctx = frameEvent.ctx;
+
+            ctx
+                .clear()
+                .opacity(opacity)
+                .path()
+                .circle(ctx.getWidth() * 0.5, ctx.getHeight() * 0.5, radius)
+                .fill(settings.color);
+        }
+
+        new Objects.CanvasEffect(twinkleEvent, size, size, frame, callback).run(settings.duration, 25);
+    };
+}
+
+$.twinkle.add(new SplashEffect());
+
+}());
+
+(function () {
+
+var $ = jQuery;
+
+var defaults = {
+        color: 'rgba(255,0,0,0.5)',
+        radius: 300,
+        duration: 1000,
+        width: 2
+    };
+
+function DropEffect() {
+
+    this.id = 'drop';
+
+    this.run = function (twinkleEvent, options, callback) {
+
+        var settings = $.extend({}, defaults, options);
+        var size = settings.radius * 2;
+        var opacityIpl = new Objects.Interpolator([ 0.4, 1, 0 ]);
+        var radiusIpl = new Objects.Interpolator([ 0, settings.radius ]);
+
+        function frame(frameEvent) {
+
+            var radius = radiusIpl.get(frameEvent.frac);
+            var opacity = opacityIpl.get(frameEvent.frac);
+            var ctx = frameEvent.ctx;
+
+            ctx
+                .clear()
+                .opacity(opacity)
+                .path()
+                .circle(ctx.getWidth() * 0.5, ctx.getHeight() * 0.5, radius)
+                .stroke(settings.width, settings.color);
+        }
+
+        new Objects.CanvasEffect(twinkleEvent, size, size, frame, callback).run(settings.duration, 25);
+    };
+}
+
+$.twinkle.add(new DropEffect());
+
+}());
+
+(function () {
+
+var $ = jQuery;
+
+var defaults = {
+        color: 'rgba(255,0,0,0.5)',
+        radius: 300,
+        duration: 1000,
+        width: 2,
+        count: 3,
+        delay: 100
+    };
+
+function DropsEffect() {
+
+    this.id = 'drops';
+
+    this.run = function (twinkleEvent, options, callback) {
+
+        var settings = $.extend({}, defaults, options);
+        var size = settings.radius * 2;
+        var opacityIpl = new Objects.Interpolator([ 0.4, 1, 0 ]);
+        var radiusIpl = new Objects.Interpolator([ 0, settings.radius ]);
+        var scale = (settings.duration - (settings.count - 1) * settings.delay) / settings.duration;
+        var offset = settings.delay / settings.duration;
+
+        function frame(frameEvent) {
+
+            var i, frac, radius, opacity;
+            var ctx = frameEvent.ctx;
+            var width = ctx.getWidth();
+            var height = ctx.getHeight();
+
+            ctx.clear();
+            for (i = 0; i < settings.count; i += 1) {
+                frac = Objects.Interpolator.scale(frameEvent.frac, scale, offset * i);
+
+                if (frac !== undefined) {
+                    radius = radiusIpl.get(frac);
+                    opacity = opacityIpl.get(frac);
+                    ctx
+                        .opacity(opacity)
+                        .path()
+                        .circle(width * 0.5, height * 0.5, radius)
+                        .stroke(settings.width, settings.color);
+                }
+            }
+        }
+
+        new Objects.CanvasEffect(twinkleEvent, size, size, frame, callback).run(settings.duration, 25);
+    };
+}
+
+$.twinkle.add(new DropsEffect());
+
+}());
+
+(function () {
+
+var $ = jQuery;
+
+var defaults = {
+        color: 'rgba(255,0,0,0.5)',
+        radius: 100,
+        duration: 3000
+    };
+
+function PulseEffect() {
+
+    this.id = 'pulse';
+
+    this.run = function (twinkleEvent, options, callback) {
+
+        var settings = $.extend({}, defaults, options);
+        var size = settings.radius * 2;
+        var opacityIpl = new Objects.Interpolator([ 0, 1, 0.6, 1, 0.6, 1, 0 ]);
+        var radiusIpl = new Objects.Interpolator([ 0, settings.radius, settings.radius * 0.6, settings.radius, settings.radius * 0.6, settings.radius, 0 ]);
+
+        function frame(frameEvent) {
+
+            var radius = radiusIpl.get(frameEvent.frac),
+                opacity = opacityIpl.get(frameEvent.frac),
+                ctx = frameEvent.ctx;
+
+            ctx
+                .clear()
+                .opacity(opacity)
+                .path()
+                .circle(ctx.getWidth() * 0.5, ctx.getHeight() * 0.5, radius)
+                .fill(settings.color);
+        }
+
+        new Objects.CanvasEffect(twinkleEvent, size, size, frame, callback).run(settings.duration, 25);
+    };
+}
+
+$.twinkle.add(new PulseEffect());
+
+}());
+
+(function () {
+
+var $ = jQuery;
+
+var defaults = {
+        color: 'rgba(255,0,0,0.5)',
+        radius: 100,
+        duration: 3000,
+        satellites: 10,
+        satellitesRadius: 10,
+        circulations: 1.5
+    };
+
+function OrbitEffect() {
+
+    this.id = 'orbit';
+
+    this.run = function (twinkleEvent, options, callback) {
+
+        var settings = $.extend({}, defaults, options);
+        var size = settings.radius * 2;
+        var opacityIpl = new Objects.Interpolator([ 0.4, 1, 1, 0.4 ]);
+        var r = settings.radius - settings.satellitesRadius;
+        var radiusIpl = new Objects.Interpolator([ 0, r, r, 0 ]);
+
+        function frame(frameEvent) {
 
-       $.twinkle.add( new OrbitEffect() );
+            var radius = radiusIpl.get(frameEvent.frac);
+            var opacity = opacityIpl.get(frameEvent.frac);
+            var bog = Math.PI * 2 * settings.circulations * frameEvent.frac;
+            var ctx = frameEvent.ctx;
+            var path, i, x, y;
 
+            ctx
+                .clear()
+                .opacity(opacity)
+                .translate(ctx.getWidth() * 0.5, ctx.getHeight() * 0.5);
 
+            path = ctx.path();
+            for (i = 0; i < settings.satellites; i += 1) {
+                bog += Math.PI * 2 / settings.satellites;
+                x = Math.cos(bog) * radius;
+                y = Math.sin(bog) * radius;
+                ctx.getContext().moveTo(x, y);
+                path.circle(x, y, settings.satellitesRadius);
+            }
+            path.fill(settings.color);
+        }
 
-} )( jQuery );
+        new Objects.CanvasEffect(twinkleEvent, size, size, frame, callback).run(settings.duration, 25);
+    };
+}
 
+$.twinkle.add(new OrbitEffect());
 
+}());
 
+}());