1// pass references around in a cleaner way 2// remove jquery dependency 3 4var freezeframe = (function($) { 5 var images, options, is_touch_device; 6 7 ////////////////////////////////////////////////////////////////////////////// 8 // // 9 // Private Methods // 10 // // 11 ////////////////////////////////////////////////////////////////////////////// 12 13 // decorated console.warn message 14 var warn = function(_message) { 15 console.warn("⨠freezeframe.js ⨠: " + _message); 16 }; 17 18 // does freezeframe instance have any captured images? 19 var has_images = function() { 20 return this.images.length == 0 ? false : true; 21 }; 22 23 // filter captured images by selector and warn if none found 24 var filter = function(_selector, _images) { 25 var filtered_images; 26 27 if (_selector != undefined && _images.length > 1) { 28 filtered_images = _images.filter($(_selector)); 29 if (filtered_images.length == 0) { 30 warn("no images found for selector '" + _selector + "'"); 31 return false; 32 } 33 } else { 34 filtered_images = _images; 35 } 36 37 return filtered_images; 38 }; 39 40 // reset .gif to first frame and write to canvas 41 var process = function($_image) { 42 var ff = this, 43 $canvas = $_image.siblings("canvas"), 44 transitionEnd = 45 "transitionend webkitTransitionEnd oTransitionEnd otransitionend", 46 image_width = $_image[0].clientWidth, 47 image_height = $_image[0].clientHeight; 48 49 $canvas.attr({ 50 width: image_width, 51 height: image_height 52 }); 53 54 context = $canvas[0].getContext("2d"); 55 context.drawImage($_image[0], 0, 0, image_width, image_height); 56 57 $canvas.addClass("ff-canvas-ready").on(transitionEnd, function() { 58 $(this).off(transitionEnd); 59 $_image.addClass("ff-image-ready"); 60 }); 61 }; 62 63 ////////////////////////////////////////////////////////////////////////////// 64 // // 65 // Constructor // 66 // // 67 ////////////////////////////////////////////////////////////////////////////// 68 function freezeframe(_options) { 69 var options; 70 71 // default options 72 this.options = { 73 selector: ".freezeframe", 74 animation_play_duration: 5000, 75 non_touch_device_trigger_event: "hover" 76 }; 77 78 // new selector as string 79 options = typeof _options == "string" ? { selector: _options } : _options; 80 81 // new options 82 if (options) { 83 for (attribute in options) { 84 if (attribute in this.options) { 85 this.options[attribute] = options[attribute]; 86 } else { 87 warn(attribute + "not a valid option"); 88 } 89 } 90 } 91 92 // is this a touch device? 93 this.is_touch_device = 94 "ontouchstart" in window || "onmsgesturechange" in window; 95 } 96 97 ////////////////////////////////////////////////////////////////////////////// 98 // // 99 // Capture Images // 100 // // 101 ////////////////////////////////////////////////////////////////////////////// 102 freezeframe.prototype.capture = function(_selector) { 103 var selector; 104 105 // Passed in string or default string 106 if (_selector !== undefined) { 107 selector = _selector; 108 } else if (this.options.selector !== undefined) { 109 selector = this.options.selector; 110 } else { 111 warn("no selector passed to capture function or set in options"); 112 return false; 113 } 114 115 // Empty jQuery object to add into 116 if (this.images == undefined) { 117 this.images = $(); 118 } 119 120 // Add new selection, jQuery keeps it non redundant 121 this.images = this.images.add($("img" + selector)); 122 123 // Get non gifs outta there 124 for (i = 0; i < this.images.length; i++) { 125 if ( 126 this.images[i].src 127 .split(".") 128 .pop() 129 .toLowerCase() 130 .substring(0, 3) !== "gif" 131 ) { 132 this.images.splice(i, 1); 133 } 134 } 135 136 // If nothing was found, throw a fit 137 if (this.images.length == 0) { 138 console.warn( 139 'freezeframe : no gifs found for selector "' + selector + '"' 140 ); 141 return false; 142 } 143 144 return this; 145 }; 146 147 ////////////////////////////////////////////////////////////////////////////// 148 // // 149 // Setup Elements // 150 // // 151 ////////////////////////////////////////////////////////////////////////////// 152 freezeframe.prototype.setup = function(_selector) { 153 var ff = this, 154 setup_required = this.images.not(".ff-setup"),
155 container_classnames = ["ff-container"]; 156 157 if (!has_images.call(ff)) { 158 warn("unable to run setup(), no images captured"); 159 return false; 160 } else if (setup_required.length == 0) { 161 warn("unable to run setup(), no images require setup"); 162 return false; 163 } 164 165 filter.call(ff, _selector, setup_required).each(function(e) { 166 var $image = $(this); 167 168 $image.addClass("ff-setup ff-image"); 169 170 if ($image.hasClass("freezeframe-responsive")) { 171 container_classnames.push("ff-responsive"); 172 } 173 174 $canvas = $("<canvas />", { 175 class: "ff-canvas" 176 }) 177 .attr({ 178 width: 0, 179 height: 0 180 }) 181 .insertBefore($image); 182 183 $image.add($canvas).wrapAll( 184 $("<div />", { 185 class: container_classnames.join(" ") 186 }) 187 ); 188 }); 189 190 imagesLoaded(setup_required).on("progress", function(instance, image) { 191 process.call(ff, $(image.img)); 192 }); 193 194 return this; 195 }; 196 197 ////////////////////////////////////////////////////////////////////////////// 198 // // 199 // Attach Hover / Click Events // 200 // // 201 ////////////////////////////////////////////////////////////////////////////// 202 freezeframe.prototype.attach = function(_selector) { 203 var ff = this, 204 click_timeout, 205 images; 206 207 if (!has_images.call(ff)) { 208 warn("unable to run attach(), no images captured"); 209 return false; 210 } 211 212 filter.call(ff, _selector, ff.images).each(function(e) { 213 var $image = $(this); 214 var $canvas = $(this).siblings("canvas"); 215 216 // hover ///////////////////////////////////////////////////////////////// 217 if ( 218 (!ff.is_touch_device && 219 ff.options.non_touch_device_trigger_event == "hover") || 220 ff.is_touch_device 221 ) { 222 $image.mouseenter(function() { 223 (function() { 224 if ($image.hasClass("ff-image-ready")) { 225 $image.attr("src", $image[0].src); 226 $canvas 227 .removeClass("ff-canvas-ready") 228 .addClass("ff-canvas-active"); 229 } 230 })(); 231 }); 232 233 $image.mouseleave(function() { 234 (function() { 235 if ($image.hasClass("ff-image-ready")) { 236 $canvas 237 .removeClass("ff-canvas-active") 238 .addClass("ff-canvas-ready"); 239 } 240 })(); 241 }); 242 } 243 244 // click ///////////////////////////////////////////////////////////////// 245 if ( 246 (!ff.is_touch_device && 247 ff.options.non_touch_device_trigger_event == "click") || 248 ff.is_touch_device 249 ) { 250 var click_timeout; 251 252 $image.click(function() { 253 (function() { 254 var clicked = $canvas.hasClass("ff-canvas-active"); 255 256 if ($image.hasClass("ff-image-ready")) { 257 if (clicked) { 258 if (ff.options.animation_play_duration != Infinity) { 259 clearTimeout(click_timeout); 260 } 261 262 $canvas 263 .removeClass("ff-canvas-active") 264 .addClass("ff-canvas-ready"); 265 } else { 266 $image.attr("src", $image[0].src); 267 $canvas 268 .removeClass("ff-canvas-ready") 269 .addClass("ff-canvas-active"); 270 271 if (ff.options.animation_play_duration != Infinity) { 272 click_timeout = setTimeout(function() { 273 $canvas 274 .removeClass("ff-canvas-active") 275 .addClass("ff-canvas-ready"); 276 }, ff.options.animation_play_duration); 277 } 278 } 279 } 280 })(); 281 }); 282 } 283 }); 284 285 return this; 286 }; 287 288 ////////////////////////////////////////////////////////////////////////////// 289 // // 290 // Trigger Animation // 291 // // 292 ////////////////////////////////////////////////////////////////////////////// 293 freezeframe.prototype.trigger = function(_selector) { 294 var ff = this, 295 errors = 0; 296 297 filter.call(ff, _selector, ff.images).each(function(e) { 298 if ($(this).hasClass("ff-image-ready")) { 299 $(this).attr("src", $(this)[0].src); 300 $(this) 301 .siblings("canvas") 302 .removeClass("ff-canvas-ready") 303 .addClass("ff-canvas-active"); 304 } else { 305 warn("image not done processing ! " + $(this).attr("src")); 306 errors++; 307 } 308 });
309 310 return errors == 0 ? true : false; 311 }; 312 313 ////////////////////////////////////////////////////////////////////////////// 314 // // 315 // Release Animation // 316 // // 317 ////////////////////////////////////////////////////////////////////////////// 318 freezeframe.prototype.release = function(_selector) { 319 var ff = this, 320 errors = 0; 321 322 filter.call(ff, _selector, ff.images).each(function(e) { 323 if ($(this).hasClass("ff-image-ready")) { 324 $(this) 325 .siblings("canvas") 326 .removeClass("ff-canvas-active") 327 .addClass("ff-canvas-ready"); 328 } else { 329 warn("image not done processing ! " + $(this).attr("src")); 330 errors++; 331 } 332 }); 333 334 return errors == 0 ? true : false; 335 }; 336 337 ////////////////////////////////////////////////////////////////////////////// 338 // // 339 // Freeze Images // 340 // // 341 ////////////////////////////////////////////////////////////////////////////// 342 freezeframe.prototype.freeze = function() { 343 this.capture() 344 .setup() 345 .attach(); // ⨠tada ⨠346 return this; 347 }; 348 349 return freezeframe; 350})(jQuery); 351 352// jQuery plugin 353$.fn.freezeframe = function(_options) { 354 if (this.length == 0) { 355 console.warn( 356 "⨠freezeframe.js ⨠: no images found for selector " + this.selector 357 ); 358 return false; 359 } 360 361 var ff = new freezeframe(_options); 362 363 ff.images = this; 364 365 ff.setup().attach(); 366 367 var self = this; 368 var methods = ["trigger", "release"]; 369 methods.forEach(function(method) { 370 self[method] = function() { 371 ff[method](self.selector); 372 return self; 373 }; 374 }); 375 376 return this; 377};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.