1/* 2* Gifplayer v0.3.7 3* Customizable jquery plugin to play and stop animated gifs. Similar to 9gag's 4* (c)2014 Rubén Torres - [email protected] 5* Released under the MIT license 6*/ 7 8(function (factory) { 9 if(typeof module === "object" && typeof module.exports === "object") { 10 module.exports = factory(require("jquery")); 11 } else { 12 factory(jQuery); 13 } 14}(function($) { 15 16 function GifPlayer(preview, options){ 17 this.previewElement = preview; 18 this.options = options; 19 this.animationLoaded = false; 20 } 21 22 GifPlayer.scopes = new Array(); 23 24 GifPlayer.prototype = { 25 26 supportedFormats: ['gif', 'jpeg', 'jpg', 'png'], 27 28 activate: function(){ 29 var self = this; 30 if(this.previewElement.width() === 0){ 31 setTimeout(function(){ 32 self.activate(); 33 }, 100); 34 }else{ 35 self.mode = self.getOption('mode'); 36 self.wrap(); 37 self.addSpinner(); 38 self.addControl(); 39 self.addEvents(); 40 } 41 }, 42 43 wrap: function(){ 44 this.previewElement.addClass('gifplayer-ready'); 45 this.wrapper = this.previewElement.wrap("<div class='gifplayer-wrapper'></div>").parent(); 46 this.wrapper.css('width', this.previewElement.width()); 47 this.wrapper.css('height', this.previewElement.height()); 48 this.previewElement.css('cursor','pointer'); 49 }, 50 51 addSpinner: function(){ 52 this.spinnerElement = $("<div class = 'spinner'></div>"); 53 this.wrapper.append(this.spinnerElement); 54 this.spinnerElement.hide(); 55 }, 56 57 getOption: function(option){ 58 var dataOption = this.previewElement.data(option.toLowerCase()); 59 if(dataOption !== undefined && dataOption !== ''){ 60 return dataOption; 61 }else{ 62 return this.options[option]; 63 } 64 }, 65 66 addControl: function(){ 67 var label = this.getOption('label'); 68 this.playElement = $("<ins class='play-gif'></ins>").text(label); 69 this.wrapper.append(this.playElement); 70 this.playElement.css('top', this.previewElement.height()/2 - this.playElement.height()/2); 71 this.playElement.css('left', this.previewElement.width()/2 - this.playElement.width()/2); 72 }, 73 74 addEvents: function(){ 75 var gp = this; 76 var playOn = this.getOption('playOn'); 77 78 switch(playOn){ 79 case 'click': 80 gp.playElement.on( 'click', function(e){ 81 gp.previewElement.trigger('click'); 82 }); 83 gp.previewElement.on( 'click', function(e){ 84 // Fire event onClick 85 gp.getOption('onClick').call(gp.previewElement, e); 86 87 gp.loadAnimation(); 88 e.preventDefault(); 89 e.stopPropagation(); 90 }); 91 92 break; 93 case 'hover': 94 gp.previewElement.on( 'click mouseover', function(e){ 95 gp.loadAnimation(); 96 e.preventDefault(); 97 e.stopPropagation(); 98 }); 99 break; 100 case 'auto': 101 console.log('auto not implemented yet'); 102 break; 103 default: 104 console.log(playOn + ' is not accepted as playOn value.'); 105 } 106 }, 107 108 processScope: function(){ 109 var scope = this.getOption('scope'); 110 if( scope ){ 111 if(GifPlayer.scopes[scope]){ 112 GifPlayer.scopes[scope].stopGif(); 113 } 114 GifPlayer.scopes[scope] = this; 115 } 116 }, 117 118 loadAnimation: function(){ 119 this.processScope(); 120 121 this.spinnerElement.show(); 122 123 if( this.mode == 'gif'){ 124 this.loadGif(); 125 }else if(this.mode == 'video'){ 126 if(!this.videoLoaded){ 127 this.loadVideo(); 128 }else{ 129 this.playVideo(); 130 } 131 132 } 133 // Fire event onPlay 134 this.getOption('onPlay').call(this.previewElement); 135 }, 136 137 stopGif: function(){ 138 this.gifElement.hide(); 139 this.previewElement.show(); 140 this.playElement.show(); 141 this.resetEvents(); 142 this.getOption('onStop').call(this.previewElement); 143 }, 144 145 getFile: function( ext ){ 146 // Obtain the resource default path 147 var gif = this.getOption(ext); 148 if(gif !== undefined && gif !== ''){ 149 return gif; 150 }else{ 151 var replaceString = this.sanitizeURL(this.previewElement.attr('src')); 152 153 for (var i = 0; i < this.supportedFormats.length; i++) { 154 var pattrn = new RegExp( this.supportedFormats[i]+'$', 'i' ); 155 replaceString = replaceString.replace( pattrn, ext ); 156 } 157 158 return replaceString; 159 } 160 }, 161 162 loadGif: function(){ 163 var gp = this; 164 165 gp.playElement.hide(); 166 167 if(!this.animationLoaded){ 168 this.enableAbort(); 169 } 170 var gifSrc = this.sanitizeURL(this.getFile('gif')); 171 var gifWidth = this.previewElement.width(); 172 var gifHeight = this.previewElement.height(); 173 174 this.gifElement=$("<img class='gp-gif-element' width='"+ gifWidth + "' height=' "+ gifHeight +" '/>"); 175 176 var wait = this.getOption('wait'); 177 if(wait){ 178 //Wait until gif loads 179 this.gifElement.on({ load: function(){ 180 gp.animationLoaded = true; 181 gp.resetEvents(); 182 gp.previewElement.hide(); 183 gp.wrapper.append(gp.gifElement); 184 gp.spinnerElement.hide(); 185 gp.getOption('onLoadComplete').call(gp.previewElement); 186 }}); 187 }else{ 188 //Try to show gif instantly 189 gp.animationLoaded = true; 190 gp.resetEvents(); 191 gp.previewElement.hide(); 192 gp.wrapper.append(gp.gifElement); 193 gp.spinnerElement.hide(); 194 } 195 this.gifElement.css('cursor','pointer'); 196 this.gifElement.css('position','absolute'); 197 this.gifElement.css('top','0'); 198 this.gifElement.css('left','0'); 199 this.gifElement.attr('src', this.sanitizeURL(gifSrc)); 200 this.gifElement.click( function(e){ 201 // Fire event onClick 202 gp.getOption('onClick').call(gp.previewElement, e); 203 204 $(this).remove(); 205 gp.stopGif(); 206 e.preventDefault(); 207 e.stopPropagation(); 208 }); 209 gp.getOption('onLoad').call(gp.previewElement); 210 211 }, 212 213 loadVideo: function(){ 214 this.videoLoaded = true; 215 216 var videoSrcMp4 = this.sanitizeURL(this.getFile('mp4')); 217 var videoSrcWebm = this.sanitizeURL(this.getFile('webm')); 218 var videoWidth = this.previewElement.width(); 219 var videoHeight = this.previewElement.height(); 220 221 this.videoElement = $('<video>', { 222 class: 'gp-video-element', 223 width: videoWidth + 'px', 224 height: videoHeight + 'px', 225 style: 'margin:0 auto;width:' + videoWidth + 'px;height:' + videoHeight + 'px;', 226 autoplay: 'autoplay', 227 loop: 'loop', 228 muted: 'muted', 229 poster: this.sanitizeURL(this.previewElement.attr('src')) 230 }).append( 231 $('<source>', { 232 type: 'video/mp4', 233 src: this.sanitizeURL(videoSrcMp4) 234 }), 235 $('<source>', { 236 type: 'video/webm', 237 src: this.sanitizeURL(videoSrcWebm) 238 }) 239 ); 240 241 var gp = this; 242 243 var checkLoad = function(){ 244 if(gp.videoElement[0].readyState === 4){ 245 gp.playVideo(); 246 gp.animationLoaded = true; 247 }else{ 248 setTimeout(checkLoad, 100); 249 } 250 }; 251 252 var wait = this.getOption('wait'); 253 if(wait){ 254 checkLoad(); 255 }else{ 256 this.playVideo(); 257 } 258 259 this.videoElement.on('click', function(){ 260 if(gp.videoPaused){ 261 gp.resumeVideo(); 262 }else{ 263 gp.pauseVideo(); 264 } 265 }); 266 }, 267 268 playVideo: function(){ 269 this.spinnerElement.hide(); 270 this.previewElement.hide(); 271 this.playElement.hide(); 272 273 this.gifLoaded = true; 274 this.previewElement.hide(); 275 this.wrapper.append(this.videoElement); 276 this.videoPaused = false;
277 this.videoElement[0].play(); 278 this.getOption('onPlay').call(this.previewElement); 279 }, 280 281 pauseVideo: function(){ 282 this.videoPaused = true; 283 this.videoElement[0].pause(); 284 this.playElement.show(); 285 this.mouseoverEnabled = false; 286 this.getOption('onStop').call(this.previewElement); 287 }, 288 289 resumeVideo: function(){ 290 this.videoPaused = false; 291 this.videoElement[0].play(); 292 this.playElement.hide(); 293 this.getOption('onPlay').call(this.previewElement); 294 }, 295 296 enableAbort: function(){ 297 var gp = this; 298 this.previewElement.click( function(e){ 299 gp.abortLoading(e); 300 }); 301 this.spinnerElement.click( function(e){ 302 gp.abortLoading(e); 303 }); 304 }, 305 306 abortLoading: function(e){ 307 this.spinnerElement.hide(); 308 this.playElement.show(); 309 e.preventDefault(); 310 e.stopPropagation(); 311 this.gifElement.off('load').on( 'load', function(ev){ 312 ev.preventDefault(); 313 ev.stopPropagation(); 314 }); 315 this.resetEvents(); 316 this.getOption('onStop').call(this.previewElement); 317 }, 318 319 resetEvents: function(){ 320 this.previewElement.off('click'); 321 this.previewElement.off('mouseover'); 322 this.playElement.off('click'); 323 this.spinnerElement.off('click'); 324 this.addEvents(); 325 }, 326 327 sanitizeURL: function (url) { 328 let parser = document.createElement('a'); 329 parser.href = url; 330 const allowedProtocols = ["http:", "https:", ":", "file:"]; 331 if (allowedProtocols.includes(parser.protocol)) { 332 return parser.href; 333 } else { 334 return 'about:blank'; 335 } 336 } 337 338 }; 339 340 $.fn.gifplayer = function(options) { 341 342 // Check if we should operate with some method 343 if (/^(play|stop)$/i.test(options)) { 344 345 return this.each( function(){ 346 // Normalize method's name 347 options = options.toLowerCase(); 348 if($(this).hasClass('gifplayer-ready')){ 349 //Setup gifplayer object 350 var gp = new GifPlayer($(this), null); 351 gp.options = {}; 352 gp.options = $.extend({}, $.fn.gifplayer.defaults, gp.options); 353 gp.wrapper = $(this).parent(); 354 gp.spinnerElement = gp.wrapper.find('.spinner'); 355 gp.playElement = gp.wrapper.find('.play-gif'); 356 gp.gifElement = gp.wrapper.find('.gp-gif-element'); 357 gp.videoElement = gp.wrapper.find('.gp-video-element'); 358 gp.mode = gp.getOption('mode'); 359 360 switch(options){ 361 case 'play': 362 gp.playElement.trigger('click'); 363 break; 364 case 'stop': 365 if(!gp.playElement.is(':visible')){ 366 if(gp.mode == 'gif'){ 367 gp.stopGif(); 368 }else if( gp.mode == 'video'){ 369 gp.videoElement.trigger('click'); 370 } 371 } 372 break; 373 } 374 } 375 }); 376 377 }else{ //Create instance 378 return this.each(function(){ 379 options = $.extend({}, $.fn.gifplayer.defaults, options); 380 var gifplayer = new GifPlayer($(this), options); 381 gifplayer.activate(); 382 }); 383 } 384 }; 385 386 $.fn.gifplayer.defaults = { 387 label: 'GIF', 388 playOn: 'click', 389 mode: 'gif', 390 gif: '', 391 mp4: '', 392 webm: '', 393 wait: false, 394 scope: false, 395 onPlay: function(){}, 396 onStop: function(){}, 397 onClick: function(){}, 398 onLoad: function(){}, 399 onLoadComplete: function(){} 400 }; 401 402 return GifPlayer; 403 404}));
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.