diff --git a/README.md b/README.md index 45f8588..e2a48aa 100644 --- a/README.md +++ b/README.md @@ -288,7 +288,7 @@ vim de.pm There's just a few sentences, so it will be quick to translate. Please consider to send me you language file in order to help the other users :smile:. ## Others projects dependancies -Lutim is written in Perl with the [Mojolicious](http://mojolicio.us) framework, uses the [Twitter bootstrap](http://getbootstrap.com) framework to look not too ugly, [JQuery](http://jquery.com) and [JQuery File Uploader](https://github.com/danielm/uploader/) (slightly modified) to add some modernity, [Raphaël](http://raphaeljs.com/) and [morris.js](http://www.oesmith.co.uk/morris.js/) for stats graphs. +Lutim is written in Perl with the [Mojolicious](http://mojolicio.us) framework, uses the [Twitter bootstrap](http://getbootstrap.com) framework to look not too ugly, [JQuery](http://jquery.com) and [JQuery File Uploader](https://github.com/danielm/uploader/) (slightly modified) to add some modernity, [Raphaël](http://raphaeljs.com/) and [morris.js](http://www.oesmith.co.uk/morris.js/) for stats graphs and [freezeframe.js](http://freezeframe.chrisantonellis.com/) (slightly modified) to be able to freeze animated gifs in twitter card. ## Main developers * Luc Didry, aka Sky (), core developer, [@framasky](https://twitter.com/framasky) diff --git a/lib/Lutim.pm b/lib/Lutim.pm index f760e4e..15405ac 100644 --- a/lib/Lutim.pm +++ b/lib/Lutim.pm @@ -293,8 +293,9 @@ sub startup { $self->asset('stats.css' => 'css/bootstrap.min.css', 'css/fontello-embedded.css', 'css/morris-0.4.3.min.css', 'css/hennypenny.css', 'css/lutim.css'); $self->asset('about.css' => 'css/bootstrap.min.css', 'css/fontello-embedded.css', 'css/hennypenny.css', 'css/lutim.css'); - $self->asset('index.js' => 'js/jquery-2.1.0.min.js', 'js/bootstrap.min.js', 'js/lutim.js', 'js/dmuploader.min.js'); - $self->asset('stats.js' => 'js/jquery-2.1.0.min.js', 'js/bootstrap.min.js', 'js/lutim.js', 'js/raphael-min.js', 'js/morris-0.4.3.min.js', 'js/stats.js'); + $self->asset('index.js' => 'js/jquery-2.1.0.min.js', 'js/bootstrap.min.js', 'js/lutim.js', 'js/dmuploader.min.js'); + $self->asset('stats.js' => 'js/jquery-2.1.0.min.js', 'js/bootstrap.min.js', 'js/lutim.js', 'js/raphael-min.js', 'js/morris-0.4.3.min.js', 'js/stats.js'); + $self->asset('freeze.js' => 'js/jquery-2.1.0.min.js', 'js/freezeframe.min.js'); $self->defaults(layout => 'default'); diff --git a/public/js/freezeframe.js b/public/js/freezeframe.js new file mode 100644 index 0000000..9cda8ec --- /dev/null +++ b/public/js/freezeframe.js @@ -0,0 +1,390 @@ +/* ----------------------------------------------------------------------------- + * Freezeframe + * freezeframe.js v2.0.2 + * 2014 Chris Antonellis + * + * Freezeframe.js is a script that automatically pauses animated GIFs and + * enables them to start animating on mouse hover. + * + * Website: http://freezeframe.chrisantonellis.com/ + * Documentation: http://freezeframe.chrisantonellis.com/documentation/ + * + * Licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License + * http://creativecommons.org/licenses/by-sa/3.0/deed.en_US + * -------------------------------------------------------------------------- */ + +FreezeFrame = (function($) { + + var _ff; + var canvas; + var context; + + var class_name; + var trigger_event; + var support_touch_devices; + var animation_play_duration; + + var loading_background_color; + var loading_background_image; + var loading_background_position; + var loading_fade_in_speed; + + var animation_icon_image; + var animation_icon_position; + var animation_fade_out_speed; + + var is_touch_device; + var freezeframe_count; + + function FreezeFrame( _options ) { + + _ff = this; + + _options.class_name == null ? + this.class_name = "freezeframe" : + this.class_name = _options.class_name; + + _options.trigger_event == null ? + this.trigger_event = "hover" : + this.trigger_event = _options.trigger_event.toLowerCase(); + + _options.support_touch_devices == null ? + this.support_touch_devices = true : + this.support_touch_devuces = _options.support_touch_devices.toLowerCase(); + + _options.animation_play_duration == null ? + this.animation_play_duration = 10000 : + this.animation_play_duration = parseInt(_options.animation_play_duration); + + _options.loading_background_color == null ? + this.loading_background_color = "#666" : + this.loading_background_color = _options.loading_background_color.toLowerCase(); + + _options.loading_background_image == null ? + this.loading_background_image = "data:image/gif;base64,R0lGODlhEAAQAPIAAGZmZv///4mJidbW1v///8PDw7CwsKampiH+GkNyZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQACgABACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkEAAoAAgAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkEAAoAAwAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkEAAoABAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQACgAFACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQACgAGACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAAKAAcALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==" : + this.loading_background_image = _options.loading_background_image; + + _options.loading_background_position == null ? + this.loading_background_position = "center center" : + this.loading_background_position = _options.loading_background_position.toLowerCase(); + + _options.animation_icon_image == null ? + this.animation_icon_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAl9JREFUeNrs2s2K2lAUAGATb3TE2Cr+oaJQNFOh1MVsxK0OuJbSdX2V9gG67GqW3RbaB7B9gy7qgKJF8G/EarHVWGMyJj230EXLLITm6q05F44/IVw5X3JPjiGCZVkuJw/R5fCBAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAjwjyMej79MpVKP/hcAwe6boul0+p3P5/vmdruv4evbVqvVdRRALpd7HQqFpn6/XxUEwdI07UZV1ffNZvMzjwDE7gk9Hs8mGAwuYCl8hTNB32630nw+f1IqlRY8QtgOQAjRZVleJxKJ77AcVLptOBwup9PpPR4hbAcQRdGUJOk2HA5riqKokUjEgKK4GY/HKo8QhMGcv4oKIJg0+Xw+r8H7bSaT0XiEICwnhyLoCgQCptfrNSgCjxDkED8ChdGiwSMEOaQ2jxDkGOtuH4jJZHJ/Nps9LRaLi+Vy2WDVUB0FYB+IXq+36ff7P0ajURj2eQbF9EO73W6cFMBdENA8mXQbNFDier0mhULhQaVSeZzNZj+d3Bnwe+i6LkDCAqx9QmMwGJwlk8nzarV6Cf3EDHZ5DvHx5ADuSjwWiz0sl8uX8H9izjLxowLskfgL1okfBYCnxA8KwGPiBwGg9xpWq5XIY+IsAQT6stvt6BGX4Nrt4jFxZgCmaYqGYRDo4nzdbncny3KGx8SZAcCRl6CH9yuKcl6r1S6i0egXHhP/Y53aGfV6/U2n02nA5yuIC7vntzsEBo/KXkG84vaI/12w8Flhhw8EQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEcNr4KcAABsB8S2vxAV8AAAAASUVORK5CYII=" : + this.animation_icon_image = _options.animation_icon_image; + + _options.animation_icon_position == null ? + this.animation_icon_position = "top left" : + this.animation_icon_position = _options.animation_icon_position.toLowerCase(); + + _options.loading_fade_in_speed == null ? + this.loading_fade_in_speed = 500 : + this.loading_fade_in_speed = parseInt(_options.loading_fade_in_speed); + + _options.animation_fade_out_speed == null ? + this.animation_fade_out_speed = 250 : + this.animation_fade_out_speed = parseInt(_options.animation_fade_out_speed); + + this.is_touch_device = isMouseEventSupported("ontouchstart"); + this.freezeframe_count = 0; + }; + + /** -------------------------------------------------------------------------- + * @function .setup() + * Sets up the freezeframe instance by adding a canvas element to the + * document and capturing references to the canvas and its 2D context + * ------------------------------------------------------------------------ */ + + FreezeFrame.prototype.setup = function() { + $("",{id:"freezeframe-canvas"}) + .css("display", "none") + .prependTo($("body")); + this.canvas = $("canvas#freezeframe-canvas"); + this.context = this.canvas[0].getContext('2d'); + }; + + /** -------------------------------------------------------------------------- + * .run() + * Starts freezeframe processing for each image. Copies the image to + * the freezeframe canvas and converts it to a data url + * ------------------------------------------------------------------------ */ + + FreezeFrame.prototype.run = function() { + var images = []; + var ext; + var figure_background = _ff.loading_background_color; + + if( _ff.loading_background_image !== false ) { + figure_background += " url('" + _ff.loading_background_image + "') " + + _ff.loading_background_position + " no-repeat"; + } + + // Select all images with a class matching the option class_name + images = $('img[class*="' + _ff.class_name + '"]') + .not('[class*="' + _ff.class_name + '_done"]'); + + // Process each image by resetting the animation sequence, copying to the + // canvas, converting to a data url, and attaching that data url to the + // image itself as an attribute + $(images).each(function(index) { + // Change image class so it won't be reprocessed if .run() is run again + $(this).removeClass(_ff.class_name).addClass(_ff.class_name + "_done"); + // Set cross-origin to anon to load images from remote services that send + // the correct header. Not working correctly, needs more testing + $(this).crossOrigin = "anonymous"; + // Determine file extension + ext = $(this)[0].src.split("."); + ext = ext[ext.length - 1].toLowerCase(); + // Remove non GIF files + if(ext !== "gif") { + images.splice(index, 1); + } else { + + var freezeframe_figure = $("
") + .attr("class", "freezeframe-container " + _ff.freezeframe_count) + .css({"display": "inline-block", + "overflow": "hidden", + "margin": 0, + "background-size": "100% auto", + "width": "100%", + "max-height": "100%", + "background": figure_background}); + + $(this).css({"opacity": 0, + "display": "block"}) + .wrap(freezeframe_figure); + + freezeframe_figure = $(this).parent(); + + // If an animation icon image is available, attach it + if(_ff.animation_icon_image !== false) { + var animation_icon = $("
") + .attr("class", "freezeframe-animation-icon") + .css({"display": "block", + "position": "absolute", + "background": "transparent " + "url('" + _ff.animation_icon_image + "') " + + _ff.animation_icon_position + " no-repeat", + "pointer-events": "none", + "z-index": 100, + "opacity": 0}); + + $(freezeframe_figure).prepend(animation_icon); + + animation_icon = $(this).siblings($(".freezeframe-animation-icon")); + } + + // Increment counter so each image gets a unique number + _ff.freezeframe_count++; + + // Create a temporary refernce to the image as non-object to pass to .drawImage + var _self = this; + + // Using imagesLoaded by Desandro because .load doesn't work on cached images + $(this).imagesLoaded(function() { + + $(this).off("imagesLoaded"); + + _ff.canvas[0].width = $(this)[0].clientWidth; + _ff.canvas[0].height = $(this)[0].clientHeight; + + $(this).attr("animated", $(this).attr("src")) + .attr("src", $(this).attr("src")); + + _ff.context.drawImage(_self, 0, 0, $(this)[0].clientWidth, $(this)[0].clientHeight); + + $(this).attr("src", _ff.canvas[0].toDataURL()); + + // If an animation icon image is available, show it + if(_ff.animation_icon_image !== false) { + $(animation_icon).css({ + "width": parseInt($(this)[0].width), + "height": parseInt($(this)[0].height)}) + .animate({"opacity": 1}, _ff.loading_fade_in_speed); + } + + // When fade in sequence is complete, enable interaction + $(this).animate({"opacity": 1}, _ff.loading_fade_in_speed, function() { + + $(freezeframe_figure).css("background", "url('" + $(this).attr("src") + "')"); + + $(this).css("opacity", 0) + .attr("src", $(this).attr("animated")); + + // Touch Device or Click Event + if((_ff.support_touch_devices && _ff.is_touch_device) || _ff.trigger_event.toLowerCase() == "click") { + + var stop_animation; + var animating = false; + + $(this).click(function() { + + // If not currently animating, start animating + if(!animating) { + $(this).attr("src", $(this).attr("src")) + .css("opacity", 1); + if(_ff.animation_icon_image !== false) { + $(animation_icon).css("opacity", 0); + } + + stop_animation = setInterval(function() { + clearInterval(stop_animation); + this.animate({"opacity": 0}, _ff.animation_fade_out_speed); + if(_ff.animation_icon_image !== false) { + $(animation_icon).animate({"opacity": 1}, _ff.animation_fade_out_speed); + } + animating = false; + }, _ff.animation_play_duration); + animating = true; + + // If currently animating, stop animating + } else { + clearInterval(stop_animation); + $(this).animate({"opacity": 0}, _ff.animation_fade_out_speed); + if(_ff.animation_icon_image !== false) { + $(animation_icon).animate({"opacity": 1}, _ff.animation_fade_out_speed); + } + animating = false; + } + }); + + } else { + + // Hover Event + $(this).mouseenter(function() { + $(this).attr("src", $(this).attr("src")) + .css("opacity", 1); + if(_ff.animation_icon_image !== false) { + $(animation_icon).css("opacity", 0); + } + }); + + $(this).mouseleave(function() { + $(this).animate({"opacity": 0}, _ff.animation_fade_out_speed); + if(_ff.animation_icon_image !== false) { + $(animation_icon).animate({"opacity": 1}, _ff.animation_fade_out_speed); + } + }); + } + }); + }); + } + }); + }; + + return FreezeFrame; +})(jQuery); + +/** ---------------------------------------------------------------------------- + * Setup & Run Freezeframe + * -------------------------------------------------------------------------- */ + +jQuery(document).ready(function($) { + typeof(freezeframe_options) == 'undefined' ? freezeframe_options = {} : null; + freezeframe = new FreezeFrame(freezeframe_options); + freezeframe.setup(); + freezeframe.run(); +}); + +/** ---------------------------------------------------------------------------- + * jQuery imagesLoaded plugin v2.1.1 + * http://github.com/desandro/imagesloaded + * + * MIT License. by Paul Irish et al. + * -------------------------------------------------------------------------- */ + +;(function($, undefined) { +'use strict'; +var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='; +$.fn.imagesLoaded = function( callback ) { + var $this = this, + deferred = $.isFunction($.Deferred) ? $.Deferred() : 0, + hasNotify = $.isFunction(deferred.notify), + $images = $this.find('img').add( $this.filter('img') ), + loaded = [], + proper = [], + broken = []; + if ($.isPlainObject(callback)) { + $.each(callback, function (key, value) { + if (key === 'callback') { + callback = value; + } else if (deferred) { + deferred[key](value); + } + }); + } + function doneLoading() { + var $proper = $(proper), + $broken = $(broken); + if ( deferred ) { + if ( broken.length ) { + deferred.reject( $images, $proper, $broken ); + } else { + deferred.resolve( $images ); + } + } + if ( $.isFunction( callback ) ) { + callback.call( $this, $images, $proper, $broken ); + } + } + function imgLoadedHandler( event ) { + imgLoaded( event.target, event.type === 'error' ); + } + function imgLoaded( img, isBroken ) { + if ( img.src === BLANK || $.inArray( img, loaded ) !== -1 ) { + return; + } + loaded.push( img ); + if ( isBroken ) { + broken.push( img ); + } else { + proper.push( img ); + } + $.data( img, 'imagesLoaded', { isBroken: isBroken, src: img.src } ); + if ( hasNotify ) { + deferred.notifyWith( $(img), [ isBroken, $images, $(proper), $(broken) ] ); + } + if ( $images.length === loaded.length ) { + setTimeout( doneLoading ); + $images.unbind( '.imagesLoaded', imgLoadedHandler ); + } + } + if ( !$images.length ) { + doneLoading(); + } else { + $images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoadedHandler ) + .each( function( i, el ) { + var src = el.src; + var cached = $.data( el, 'imagesLoaded' ); + if ( cached && cached.src === src ) { + imgLoaded( el, cached.isBroken ); + return; + } + if ( el.complete && el.naturalWidth !== undefined ) { + imgLoaded( el, el.naturalWidth === 0 || el.naturalHeight === 0 ); + return; + } + if ( el.readyState || el.complete ) { + el.src = BLANK; + el.src = src; + } + }); + } + return deferred ? deferred.promise( $this ) : $this; +}; +})(jQuery); + +/** ---------------------------------------------------------------------------- + * isMouseEventSupported by kangax + * http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + * -------------------------------------------------------------------------- */ + +function isMouseEventSupported(eventName) { + var el = document.createElement('div'); + eventName = 'on' + eventName; + var isSupported = (eventName in el); + if (!isSupported) { + el.setAttribute(eventName, 'return;'); + isSupported = typeof el[eventName] == 'function'; + } + el = null; + return isSupported; +} diff --git a/public/js/freezeframe.min.js b/public/js/freezeframe.min.js new file mode 100644 index 0000000..30650d4 --- /dev/null +++ b/public/js/freezeframe.min.js @@ -0,0 +1 @@ +function isMouseEventSupported(e){var t=document.createElement("div");e="on"+e;var n=e in t;if(!n){t.setAttribute(e,"return;");n=typeof t[e]=="function"}t=null;return n}FreezeFrame=function(e){function g(e){t=this;e.class_name==null?this.class_name="freezeframe":this.class_name=e.class_name;e.trigger_event==null?this.trigger_event="hover":this.trigger_event=e.trigger_event.toLowerCase();e.support_touch_devices==null?this.support_touch_devices=true:this.support_touch_devuces=e.support_touch_devices.toLowerCase();e.animation_play_duration==null?this.animation_play_duration=1e4:this.animation_play_duration=parseInt(e.animation_play_duration);e.loading_background_color==null?this.loading_background_color="#666":this.loading_background_color=e.loading_background_color.toLowerCase();e.loading_background_image==null?this.loading_background_image="data:image/gif;base64,R0lGODlhEAAQAPIAAGZmZv///4mJidbW1v///8PDw7CwsKampiH+GkNyZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQACgABACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkEAAoAAgAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkEAAoAAwAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkEAAoABAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQACgAFACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQACgAGACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAAKAAcALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==":this.loading_background_image=e.loading_background_image;e.loading_background_position==null?this.loading_background_position="center center":this.loading_background_position=e.loading_background_position.toLowerCase();e.animation_icon_image==null?this.animation_icon_image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAl9JREFUeNrs2s2K2lAUAGATb3TE2Cr+oaJQNFOh1MVsxK0OuJbSdX2V9gG67GqW3RbaB7B9gy7qgKJF8G/EarHVWGMyJj230EXLLITm6q05F44/IVw5X3JPjiGCZVkuJw/R5fCBAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAjwjyMej79MpVKP/hcAwe6boul0+p3P5/vmdruv4evbVqvVdRRALpd7HQqFpn6/XxUEwdI07UZV1ffNZvMzjwDE7gk9Hs8mGAwuYCl8hTNB32630nw+f1IqlRY8QtgOQAjRZVleJxKJ77AcVLptOBwup9PpPR4hbAcQRdGUJOk2HA5riqKokUjEgKK4GY/HKo8QhMGcv4oKIJg0+Xw+r8H7bSaT0XiEICwnhyLoCgQCptfrNSgCjxDkED8ChdGiwSMEOaQ2jxDkGOtuH4jJZHJ/Nps9LRaLi+Vy2WDVUB0FYB+IXq+36ff7P0ajURj2eQbF9EO73W6cFMBdENA8mXQbNFDier0mhULhQaVSeZzNZj+d3Bnwe+i6LkDCAqx9QmMwGJwlk8nzarV6Cf3EDHZ5DvHx5ADuSjwWiz0sl8uX8H9izjLxowLskfgL1okfBYCnxA8KwGPiBwGg9xpWq5XIY+IsAQT6stvt6BGX4Nrt4jFxZgCmaYqGYRDo4nzdbncny3KGx8SZAcCRl6CH9yuKcl6r1S6i0egXHhP/Y53aGfV6/U2n02nA5yuIC7vntzsEBo/KXkG84vaI/12w8Flhhw8EQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEcNr4KcAABsB8S2vxAV8AAAAASUVORK5CYII=":this.animation_icon_image=e.animation_icon_image;e.animation_icon_position==null?this.animation_icon_position="top left":this.animation_icon_position=e.animation_icon_position.toLowerCase();e.loading_fade_in_speed==null?this.loading_fade_in_speed=500:this.loading_fade_in_speed=parseInt(e.loading_fade_in_speed);e.animation_fade_out_speed==null?this.animation_fade_out_speed=250:this.animation_fade_out_speed=parseInt(e.animation_fade_out_speed);this.is_touch_device=isMouseEventSupported("ontouchstart");this.freezeframe_count=0}var t;var n;var r;var i;var s;var o;var u;var a;var f;var l;var c;var h;var p;var d;var v;var m;g.prototype.setup=function(){e("",{id:"freezeframe-canvas"}).css("display","none").prependTo(e("body"));this.canvas=e("canvas#freezeframe-canvas");this.context=this.canvas[0].getContext("2d")};g.prototype.run=function(){var n=[];var r;var i=t.loading_background_color;if(t.loading_background_image!==false){i+=" url('"+t.loading_background_image+"') "+t.loading_background_position+" no-repeat"}n=e('img[class*="'+t.class_name+'"]').not('[class*="'+t.class_name+'_done"]');e(n).each(function(s){e(this).removeClass(t.class_name).addClass(t.class_name+"_done");e(this).crossOrigin="anonymous";r=e(this)[0].src.split(".");r=r[r.length-1].toLowerCase();if(r!=="gif"){n.splice(s,1)}else{var o=e("
").attr("class","freezeframe-container "+t.freezeframe_count).css({display:"inline-block",overflow:"hidden","margin":0,"background-size":"100% auto","width":"100%","max-height":"100%",background:i});e(this).css({opacity:0,display:"block"}).wrap(o);o=e(this).parent();if(t.animation_icon_image!==false){var u=e("
").attr("class","freezeframe-animation-icon").css({display:"block",position:"absolute",background:"transparent "+"url('"+t.animation_icon_image+"') "+t.animation_icon_position+" no-repeat","pointer-events":"none","z-index":100,opacity:0});e(o).prepend(u);u=e(this).siblings(e(".freezeframe-animation-icon"))}t.freezeframe_count++;var a=this;e(this).imagesLoaded(function(){e(this).off("imagesLoaded");t.canvas[0].width=e(this)[0].clientWidth;t.canvas[0].height=e(this)[0].clientHeight;e(this).attr("animated",e(this).attr("src")).attr("src",e(this).attr("src"));t.context.drawImage(a,0,0,e(this)[0].clientWidth,e(this)[0].clientHeight);e(this).attr("src",t.canvas[0].toDataURL());if(t.animation_icon_image!==false){e(u).css({width:parseInt(e(this)[0].width),height:parseInt(e(this)[0].height)}).animate({opacity:1},t.loading_fade_in_speed)}e(this).animate({opacity:1},t.loading_fade_in_speed,function(){e(o).css("background","url('"+e(this).attr("src")+"')");e(this).css("opacity",0).attr("src",e(this).attr("animated"));if(t.support_touch_devices&&t.is_touch_device||t.trigger_event.toLowerCase()=="click"){var n;var r=false;e(this).click(function(){if(!r){e(this).attr("src",e(this).attr("src")).css("opacity",1);if(t.animation_icon_image!==false){e(u).css("opacity",0)}n=setInterval(function(){clearInterval(n);this.animate({opacity:0},t.animation_fade_out_speed);if(t.animation_icon_image!==false){e(u).animate({opacity:1},t.animation_fade_out_speed)}r=false},t.animation_play_duration);r=true}else{clearInterval(n);e(this).animate({opacity:0},t.animation_fade_out_speed);if(t.animation_icon_image!==false){e(u).animate({opacity:1},t.animation_fade_out_speed)}r=false}})}else{e(this).mouseenter(function(){e(this).attr("src",e(this).attr("src")).css("opacity",1);if(t.animation_icon_image!==false){e(u).css("opacity",0)}});e(this).mouseleave(function(){e(this).animate({opacity:0},t.animation_fade_out_speed);if(t.animation_icon_image!==false){e(u).animate({opacity:1},t.animation_fade_out_speed)}})}})})}})};return g}(jQuery);jQuery(document).ready(function(e){typeof freezeframe_options=="undefined"?freezeframe_options={}:null;freezeframe=new FreezeFrame(freezeframe_options);freezeframe.setup();freezeframe.run()});(function(e,t){"use strict";var n="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";e.fn.imagesLoaded=function(r){function c(){var t=e(f),n=e(l);if(s){if(l.length){s.reject(u,t,n)}else{s.resolve(u)}}if(e.isFunction(r)){r.call(i,u,t,n)}}function h(e){p(e.target,e.type==="error")}function p(t,r){if(t.src===n||e.inArray(t,a)!==-1){return}a.push(t);if(r){l.push(t)}else{f.push(t)}e.data(t,"imagesLoaded",{isBroken:r,src:t.src});if(o){s.notifyWith(e(t),[r,u,e(f),e(l)])}if(u.length===a.length){setTimeout(c);u.unbind(".imagesLoaded",h)}}var i=this,s=e.isFunction(e.Deferred)?e.Deferred():0,o=e.isFunction(s.notify),u=i.find("img").add(i.filter("img")),a=[],f=[],l=[];if(e.isPlainObject(r)){e.each(r,function(e,t){if(e==="callback"){r=t}else if(s){s[e](t)}})}if(!u.length){c()}else{u.bind("load.imagesLoaded error.imagesLoaded",h).each(function(r,i){var s=i.src;var o=e.data(i,"imagesLoaded");if(o&&o.src===s){p(i,o.isBroken);return}if(i.complete&&i.naturalWidth!==t){p(i,i.naturalWidth===0||i.naturalHeight===0);return}if(i.readyState||i.complete){i.src=n;i.src=s}})}return s?s.promise(i):i}})(jQuery) diff --git a/templates/twitter.html.ep b/templates/twitter.html.ep index 7b55862..e285cbc 100644 --- a/templates/twitter.html.ep +++ b/templates/twitter.html.ep @@ -1,4 +1,5 @@ % # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab: +% my $g = ($mimetype eq 'image/gif') ? 1 : 0; @@ -6,21 +7,28 @@ -% if ($mimetype eq 'image/gif') { + + +% if ($g) { + %= asset 'freeze.js' + %= javascript begin + freezeframe_options = { + trigger_event: "click", + animation_play_duration: 60000 + } + % end % } else { % } - - - - <%= $filename %> + > + style="<%= 'max-' unless ($g) %>width:100%; max-height:100%;" src="<%= url_for('index')->to_abs().$short %><%= '.gif' if ($g) %>" alt="<%= $filename %>">