1/** 2 * Flurry jQuery Plugin 3 * 4 * Flurry is an easy-to-use animated snow plugin for jQuery. It takes advantage 5 * of CSS transforms, CSS transitions and requestAnimationFrame to provide 6 * smooth animation for modern browsers. Props to Jonathan Nicol @f6design 7 * for boilerplate code 8 * (see http://jonathannicol.com/blog/2012/05/06/a-jquery-plugin-boilerplate/) 9 * 10 * @link https://github.com/joshmcrty/Flurry 11 * @version 1.1.0 12 * @author Josh McCarty <[email protected]> 13 * @copyright 2016 Josh McCarty 14 * @license https://github.com/joshmcrty/Flurry/blob/master/LICENSE GPLv2 15 */ 16;(function($, undefined) { 17 18 /** 19 * requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel 20 * http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 21 * http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating 22 * 23 * @link https://gist.github.com/lenville/9e13e63af075c145d662 24 */ 25 (function() { 26 var lastTime = 0; 27 var vendors = ['ms', 'moz', 'webkit', 'o']; 28 for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 29 window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 30 window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; 31 } 32 33 if (!window.requestAnimationFrame) { 34 window.requestAnimationFrame = function(callback, element) { 35 var currTime = new Date().getTime(); 36 var timeToCall = Math.max(0, 16 - Math.abs(currTime - lastTime)); 37 var id = window.setTimeout(function() { 38 callback(currTime + timeToCall); 39 }, timeToCall); 40 lastTime = currTime + timeToCall; 41 return id; 42 }; 43 } 44 45 if (!window.cancelAnimationFrame) { 46 window.cancelAnimationFrame = function(id) { 47 clearTimeout(id); 48 }; 49 } 50 }()); 51 52 // Change this to your plugin name. 53 var pluginName = 'flurry'; 54 55 /** 56 * Behaves the same as setInterval except uses requestAnimationFrame() where possible for better performance 57 * 58 * http://www.joelambert.co.uk 59 * Copyright 2011, Joe Lambert. 60 * Free to use under the MIT license. 61 * 62 * @link https://gist.github.com/joelambert/1002116 63 * @param {function} fn The callback function 64 * @param {int} delay The delay in milliseconds 65 */ 66 function requestInterval(fn, delay) { 67 68 /* jshint -W010 */ 69 var start = new Date().getTime(), 70 handle = new Object(); 71 72 function loop() { 73 var current = new Date().getTime(), 74 delta = current - start; 75 76 if(delta >= delay) { 77 fn.call(); 78 start = new Date().getTime(); 79 } 80 81 handle.value = window.requestAnimationFrame(loop); 82 } 83 84 handle.value = window.requestAnimationFrame(loop); 85 return handle; 86 } 87 88 /** 89 * Behaves the same as clearInterval except uses cancelRequestAnimationFrame() where possible for better performance 90 * 91 * http://www.joelambert.co.uk 92 * Copyright 2011, Joe Lambert. 93 * Free to use under the MIT license. 94 * 95 * @link https://gist.github.com/joelambert/1002116 96 * @param {int|object} fn The callback function 97 */ 98 function clearRequestInterval(handle) { 99 100 /* jshint -W030 */ 101 window.cancelAnimationFrame ? window.cancelAnimationFrame(handle.value) : 102 window.webkitCancelAnimationFrame ? window.webkitCancelAnimationFrame(handle.value) : 103 window.webkitCancelRequestAnimationFrame ? window.webkitCancelRequestAnimationFrame(handle.value) : /* Support for legacy API */ 104 window.mozCancelRequestAnimationFrame ? window.mozCancelRequestAnimationFrame(handle.value) : 105 window.oCancelRequestAnimationFrame ? window.oCancelRequestAnimationFrame(handle.value) : 106 window.msCancelRequestAnimationFrame ? window.msCancelRequestAnimationFrame(handle.value) : 107 clearInterval(handle); 108 } 109 110 /** 111 * Detects whether the browser supports CSS transitions (adapted from https://gist.github.com/jonraasch/373874) 112 * 113 * @return {boolean} True if CSS transitions are supported 114 */ 115 function supportsTransitions() { 116 var thisBody = document.body || document.documentElement, 117 thisStyle = thisBody.style, 118 support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined; 119 return support; 120 } 121 122 /** 123 * Generate a random integer within a range provided 124 * 125 * @param {number} min The lowest number of the range 126 * @param {number} max The highest number of the range 127 * @return {number} The random integer within the range 128 */ 129 function randomNumberInRange(min, max) { 130 return Math.floor(Math.random() * (max - min + 1) + min); 131 } 132 133 /** 134 * Creates a snowflake with randomized movement based on the options provided 135 * 136 * @param {object} options The options set for the plugin 137 * @param {object} $container The jQuery element to append each snowflake to. 138 */ 139 function createFlake(options, $container, containerWidth) { 140 141 // Set the character. If multiple characters are provided, randomly select one. 142 var character = options.character.length === 1 ? options.character : options.character.charAt(Math.round(randomNumberInRange(0, options.character.length - 1))); 143 144 // Set the flake's starting position to a random number between the container width, including additional space for the wind setting 145 var startX = randomNumberInRange(-Math.abs(options.wind), containerWidth + Math.abs(options.wind)); 146 147 // Set the flake's ending X translation to a random number based on the wind and windVariance options 148 var endX = startX + randomNumberInRange(options.wind - options.windVariance, options.wind + options.windVariance); 149 150 // Set the flake's font size to a random number between the small and large options 151 var fontSize = randomNumberInRange(options.small, options.large); 152 153 // Set the flake's speed to a random number based on the speed setting and the randomized fontSize 154 var speed = options.speed / ((randomNumberInRange(fontSize * 1.2, fontSize * 0.8) - options.small) / (options.large - options.small) + 0.5); 155 156 // Set the flake's ending Y translation based on the height setting and the randomized fontSize 157 var endY = options.height - fontSize; 158 159 // Set the flake's rotation to a random degree based on the rotation and rotationVariance options 160 var endRotation = randomNumberInRange(options.rotation - options.rotationVariance, options.rotation + options.rotationVariance); 161 162 // Set the flake's color based on color options 163 var color = Array.isArray(options.color) ? options.color[Math.floor(Math.random() * options.color.length)] : options.color; 164 165 // Create object to store final CSS properties for the flake 166 var endCSS = {
167 "transform": "translateX(" + endX + "px) translateY(" + endY + "px) rotateZ(" + endRotation + "deg)", 168 "opacity": 0 169 }; 170 171 // Create the flake, set the CSS for it, and animate it 172 var $flake = $('<span></span>'); 173 $flake.html(character).css({ 174 "color": options.blur && fontSize < (options.large + options.small) / 2 ? "transparent" : color, 175 "text-shadow": options.blur && fontSize < (options.large + options.small) / 2 ? "0 0 1px " + color : "none", 176 "display": "inline-block", 177 "line-height": 1, 178 "margin": 0, 179 "padding": "2px", 180 "pointer-events": "none", 181 "font-size": fontSize + "px", 182 "opacity": options.startTransparency, 183 "position": "absolute", 184 "top": "-" + (options.large * 1.2) + "px", 185 "transform": "translateX(" + startX + "px) translateY(0px) rotateZ(" + options.startRotation + "deg)", 186 "transition": "transform " + (speed / 1000) + "s linear, opacity " + (speed / 1000) + "s " + options.opacityEasing, 187 "z-index": options.zIndex, 188 }).appendTo($container); 189 190 if (supportsTransitions) { 191 192 // Remove the flake element when it finishes transitioning 193 $flake.on('transitionend.flurry', function(event) { 194 $(event.target).remove(); 195 }); 196 197 // Set the endCSS to trigger the transition 198 window.requestAnimationFrame(function(){ 199 $flake.css(endCSS); 200 }); 201 } else { 202 203 // Use jQuery .animate() 204 $flake.animate(endCSS, speed, 'linear', function() { 205 $(this).remove(); 206 }); 207 } 208 } 209 210 /** 211 * Plugin object constructor. 212 * Implements the Revealing Module Pattern. 213 */ 214 function Plugin(element, options) { 215 216 // References to this plugin instance, DOM and jQuery versions of element. 217 var self = this; 218 var el = element; 219 var $el = $(element); 220 221 // Extend default options with those supplied by user. 222 options = $.extend({ 223 height: $el.height() > 200 ? 200 : $el.height(), // default to 200px or the height of the element, whichever is smaller 224 useRelative: $el.is('body') ? false : true, // default to false for the body element and true for all other elements 225 }, $.fn[pluginName].defaults, options); 226 227 // Ensure options that should be numbers are numbers 228 $.each(options, function(key, val) { 229 if (parseInt(val)) { 230 options[key] = parseInt(val); 231 } 232 }); 233 234 /** 235 * Initialize plugin. 236 */ 237 function init() { 238 239 // Add any initialization logic here... 240 241 // Set element position to relative if currently static 242 if (options.useRelative === true && $el.css('position') === 'static') { 243 $el.css({ 244 'position': 'relative' 245 }); 246 } 247 248 // Create container element to hold snowflakes 249 var $container = $(document.createElement('div')).addClass('flurry-container').css({ 250 'margin': 0, 251 'padding': 0, 252 'position': 'absolute', 253 'top': 0, 254 'right': 0, 255 'left': 0, 256 'height': options.height, 257 'overflow': options.overflow, 258 'pointer-events': 'none' 259 }).prependTo($el); 260 261 // On window resize, recalculate the width used to generate flakes within 262 var containerWidth = $container.width(); 263 $(window).resize(function() { 264 containerWidth = $container.width(); 265 }); 266 267 // Generate flakes at the interval set by the frequency setting 268 self.flakeInterval = requestInterval(function() { 269 createFlake(options, $container, containerWidth); 270 }, options.frequency); 271 272 // Call onInit hook 273 hook('onInit'); 274 } 275 276 /** 277 * Get/set a plugin option. 278 * Get usage: $('#el').demoplugin('option', 'key'); 279 * Set usage: $('#el').demoplugin('option', 'key', value); 280 */ 281 function option (key, val) { 282 if (val) { 283 options[key] = parseInt(val) || val; 284 } else { 285 return options[key]; 286 } 287 } 288 289 /** 290 * Destroy plugin. 291 * Usage: $('#el').demoplugin('destroy'); 292 */ 293 function destroy() { 294 295 // Iterate over each matching element. 296 $el.each(function() { 297 var el = this; 298 var $el = $(this); 299 300 // Add code to restore the element to its original state... 301 302 // Cancel snowflake generation 303 clearRequestInterval(self.flakeInterval); 304
305 // Remove container 306 $el.find('.flurry-container').remove(); 307 308 // Call onDestroy hook 309 hook('onDestroy'); 310 311 // Remove Plugin instance from the element. 312 $el.removeData('plugin_' + pluginName); 313 }); 314 } 315 316 /** 317 * Callback hooks. 318 * Usage: In the defaults object specify a callback function: 319 * hookName: function() {} 320 * Then somewhere in the plugin trigger the callback: 321 * hook('hookName'); 322 */ 323 function hook(hookName) { 324 if (options[hookName] !== undefined) { 325 326 // Call the user defined function. 327 // Scope is set to the jQuery element we are operating on. 328 options[hookName].call(el); 329 } 330 } 331 332 // Initialize the plugin instance. 333 init(); 334 335 // Expose methods of Plugin we wish to be public. 336 return { 337 option: option, 338 destroy: destroy 339 }; 340 } 341 342 /** 343 * Plugin definition. 344 */ 345 $.fn[pluginName] = function(options) { 346 347 // If the first parameter is a string, treat this as a call to a public method. 348 if (typeof arguments[0] === 'string') { 349 var methodName = arguments[0]; 350 var args = Array.prototype.slice.call(arguments, 1); 351 var returnVal; 352 this.each(function() { 353 354 // Check that the element has a plugin instance, and that the requested public method exists. 355 if ($.data(this, 'plugin_' + pluginName) && typeof $.data(this, 'plugin_' + pluginName)[methodName] === 'function') { 356 357 // Call the method of the Plugin instance, and Pass it the supplied arguments. 358 returnVal = $.data(this, 'plugin_' + pluginName)[methodName].apply(this, args); 359 } else { 360 throw new Error('Method ' + methodName + ' does not exist on jQuery.' + pluginName); 361 } 362 }); 363 if (returnVal !== undefined){ 364 365 // If the method returned a value, return the value. 366 return returnVal; 367 } else { 368 369 // Otherwise, returning 'this' preserves chainability. 370 return this; 371 } 372 373 // If the first parameter is an object (options), or was omitted, instantiate a new instance of the plugin. 374 } else if (typeof options === "object" || !options) { 375 return this.each(function() { 376 377 // Only allow the plugin to be instantiated once. 378 if (!$.data(this, 'plugin_' + pluginName)) { 379 380 // Pass options to Plugin constructor, and store Plugin instance in the elements jQuery data object. 381 $.data(this, 'plugin_' + pluginName, new Plugin(this, options)); 382 } 383 }); 384 } 385 }; 386 387 // Default plugin options. 388 // Options can be overwritten when initializing plugin, by 389 // passing an object literal, or after initialization: 390 // $('#el').demoplugin('option', 'key', value); 391 // The `height` and `useRelative` defaults are set in 392 // the `Plugin()` function as they rely on the selected 393 // element(s) to determine a default value 394 $.fn[pluginName].defaults = { 395 onInit: function() {}, 396 onDestroy: function() {}, 397 /* height: 200, */ 398 /* useRelative: false, */ 399 character: "â", 400 color: "white", 401 frequency: 100, 402 speed: 3000, 403 small: 8, 404 large: 28, 405 wind: 40, 406 windVariance: 20, 407 rotation: 90, 408 rotationVariance: 180, 409 startRotation: 0, 410 startOpacity: 1, 411 endOpacity: 0, 412 opacityEasing: "cubic-bezier(1,.3,.6,.74)", 413 blur: true, 414 overflow: "hidden", 415 zIndex: 9999 416 }; 417 418})(jQuery);
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.