1/** @license 2 * DHTML Snowstorm! JavaScript-based Snow for web pages 3 * -------------------------------------------------------- 4 * Version 1.43.20111201 (Previous rev: 1.42.20111120) 5 * Copyright (c) 2007, Scott Schiller. All rights reserved. 6 * Code provided under the BSD License: 7 * http://schillmania.com/projects/snowstorm/license.txt 8 */ 9 10/*global window, document, navigator, clearInterval, setInterval */ 11/*jslint white: false, onevar: true, plusplus: false, undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */ 12 13var snowStorm = (function(window, document) { 14 15 // --- common properties --- 16 17 this.autoStart = true; // Whether the snow should start automatically or not. 18 this.flakesMax = 128; // Limit total amount of snow made (falling + sticking) 19 this.flakesMaxActive = 64; // Limit amount of snow falling at once (less = lower CPU use) 20 this.animationInterval = 33; // Theoretical "miliseconds per frame" measurement. 20 = fast + smooth, but high CPU use. 50 = more conservative, but slower 21 this.excludeMobile = true; // Snow is likely to be bad news for mobile phones' CPUs (and batteries.) By default, be nice. 22 this.flakeBottom = null; // Integer for Y axis snow limit, 0 or null for "full-screen" snow effect 23 this.followMouse = true; // Snow movement can respond to the user's mouse 24 this.snowColor = '#fff'; // Don't eat (or use?) yellow snow. 25 this.snowCharacter = '•'; // • = bullet, · is square on some systems etc. 26 this.snowStick = true; // Whether or not snow should "stick" at the bottom. When off, will never collect. 27 this.targetElement = null; // element which snow will be appended to (null = document.body) - can be an element ID eg. 'myDiv', or a DOM node reference 28 this.useMeltEffect = true; // When recycling fallen snow (or rarely, when falling), have it "melt" and fade out if browser supports it 29 this.useTwinkleEffect = false; // Allow snow to randomly "flicker" in and out of view while falling 30 this.usePositionFixed = false; // true = snow does not shift vertically when scrolling. May increase CPU load, disabled by default - if enabled, used only where supported 31 32 // --- less-used bits --- 33 34 this.freezeOnBlur = true; // Only snow when the window is in focus (foreground.) Saves CPU. 35 this.flakeLeftOffset = 0; // Left margin/gutter space on edge of container (eg. browser window.) Bump up these values if seeing horizontal scrollbars. 36 this.flakeRightOffset = 0; // Right margin/gutter space on edge of container 37 this.flakeWidth = 8; // Max pixel width reserved for snow element 38 this.flakeHeight = 8; // Max pixel height reserved for snow element
39 this.vMaxX = 5; // Maximum X velocity range for snow 40 this.vMaxY = 4; // Maximum Y velocity range for snow 41 this.zIndex = 0; // CSS stacking order applied to each snowflake 42 43 // --- End of user section --- 44 45 var s = this, storm = this, i, 46 // UA sniffing and backCompat rendering mode checks for fixed position, etc. 47 isIE = navigator.userAgent.match(/msie/i), 48 isIE6 = navigator.userAgent.match(/msie 6/i), 49 isWin98 = navigator.appVersion.match(/windows 98/i), 50 isMobile = navigator.userAgent.match(/mobile|opera m(ob|in)/i), 51 isBackCompatIE = (isIE && document.compatMode === 'BackCompat'), 52 noFixed = (isMobile || isBackCompatIE || isIE6), 53 screenX = null, screenX2 = null, screenY = null, scrollY = null, vRndX = null, vRndY = null, 54 windOffset = 1, 55 windMultiplier = 2, 56 flakeTypes = 6, 57 fixedForEverything = false, 58 opacitySupported = (function(){ 59 try { 60 document.createElement('div').style.opacity = '0.5'; 61 } catch(e) { 62 return false; 63 } 64 return true; 65 }()), 66 didInit = false, 67 docFrag = document.createDocumentFragment(); 68 69 this.timers = []; 70 this.flakes = []; 71 this.disabled = false; 72 this.active = false; 73 this.meltFrameCount = 20; 74 this.meltFrames = []; 75 76 this.events = (function() { 77 78 var old = (!window.addEventListener && window.attachEvent), slice = Array.prototype.slice, 79 evt = { 80 add: (old?'attachEvent':'addEventListener'), 81 remove: (old?'detachEvent':'removeEventListener') 82 }; 83 84 function getArgs(oArgs) { 85 var args = slice.call(oArgs), len = args.length; 86 if (old) { 87 args[1] = 'on' + args[1]; // prefix 88 if (len > 3) { 89 args.pop(); // no capture 90 } 91 } else if (len === 3) { 92 args.push(false); 93 } 94 return args; 95 } 96 97 function apply(args, sType) { 98 var element = args.shift(), 99 method = [evt[sType]]; 100 if (old) { 101 element[method](args[0], args[1]); 102 } else { 103 element[method].apply(element, args); 104 } 105 } 106 107 function addEvent() { 108 apply(getArgs(arguments), 'add'); 109 } 110 111 function removeEvent() { 112 apply(getArgs(arguments), 'remove'); 113 } 114 115 return { 116 add: addEvent, 117 remove: removeEvent 118 }; 119 120 }()); 121 122 function rnd(n,min) { 123 if (isNaN(min)) { 124 min = 0; 125 } 126 return (Math.random()*n)+min; 127 } 128 129 function plusMinus(n) { 130 return (parseInt(rnd(2),10)===1?n*-1:n); 131 } 132 133 this.randomizeWind = function() { 134 var i; 135 vRndX = plusMinus(rnd(s.vMaxX,0.2)); 136 vRndY = rnd(s.vMaxY,0.2); 137 if (this.flakes) { 138 for (i=0; i<this.flakes.length; i++) { 139 if (this.flakes[i].active) { 140 this.flakes[i].setVelocities(); 141 } 142 } 143 } 144 }; 145 146 this.scrollHandler = function() { 147 var i; 148 // "attach" snowflakes to bottom of window if no absolute bottom value was given 149 scrollY = (s.flakeBottom?0:parseInt(window.scrollY||document.documentElement.scrollTop||document.body.scrollTop,10)); 150 if (isNaN(scrollY)) { 151 scrollY = 0; // Netscape 6 scroll fix 152 } 153 if (!fixedForEverything && !s.flakeBottom && s.flakes) { 154 for (i=s.flakes.length; i--;) { 155 if (s.flakes[i].active === 0) { 156 s.flakes[i].stick(); 157 } 158 } 159 } 160 }; 161 162 this.resizeHandler = function() { 163 if (window.innerWidth || window.innerHeight) { 164 screenX = window.innerWidth-16-s.flakeRightOffset; 165 screenY = (s.flakeBottom?s.flakeBottom:window.innerHeight); 166 } else { 167 screenX = (document.documentElement.clientWidth||document.body.clientWidth||document.body.scrollWidth)-(!isIE?8:0)-s.flakeRightOffset; 168 screenY = s.flakeBottom?s.flakeBottom:(document.documentElement.clientHeight||document.body.clientHeight||document.body.scrollHeight); 169 } 170 screenX2 = parseInt(screenX/2,10); 171 }; 172 173 this.resizeHandlerAlt = function() { 174 screenX = s.targetElement.offsetLeft+s.targetElement.offsetWidth-s.flakeRightOffset; 175 screenY = s.flakeBottom?s.flakeBottom:s.targetElement.offsetTop+s.targetElement.offsetHeight; 176 screenX2 = parseInt(screenX/2,10); 177 }; 178 179 this.freeze = function() { 180 // pause animation 181 var i; 182 if (!s.disabled) { 183 s.disabled = 1; 184 } else { 185 return false;
186 } 187 for (i=s.timers.length; i--;) { 188 clearInterval(s.timers[i]); 189 } 190 }; 191 192 this.resume = function() { 193 if (s.disabled) { 194 s.disabled = 0; 195 } else { 196 return false; 197 } 198 s.timerInit(); 199 }; 200 201 this.toggleSnow = function() { 202 if (!s.flakes.length) { 203 // first run 204 s.start(); 205 } else { 206 s.active = !s.active; 207 if (s.active) { 208 s.show(); 209 s.resume(); 210 } else { 211 s.stop(); 212 s.freeze(); 213 } 214 } 215 }; 216 217 this.stop = function() { 218 var i; 219 this.freeze(); 220 for (i=this.flakes.length; i--;) { 221 this.flakes[i].o.style.display = 'none'; 222 } 223 s.events.remove(window,'scroll',s.scrollHandler); 224 s.events.remove(window,'resize',s.resizeHandler); 225 if (s.freezeOnBlur) { 226 if (isIE) { 227 s.events.remove(document,'focusout',s.freeze); 228 s.events.remove(document,'focusin',s.resume); 229 } else { 230 s.events.remove(window,'blur',s.freeze); 231 s.events.remove(window,'focus',s.resume); 232 } 233 } 234 }; 235 236 this.show = function() { 237 var i; 238 for (i=this.flakes.length; i--;) { 239 this.flakes[i].o.style.display = 'block'; 240 } 241 }; 242 243 this.SnowFlake = function(parent,type,x,y) { 244 var s = this, storm = parent; 245 this.type = type; 246 this.x = x||parseInt(rnd(screenX-20),10); 247 this.y = (!isNaN(y)?y:-rnd(screenY)-12); 248 this.vX = null; 249 this.vY = null; 250 this.vAmpTypes = [1,1.2,1.4,1.6,1.8]; // "amplification" for vX/vY (based on flake size/type) 251 this.vAmp = this.vAmpTypes[this.type]; 252 this.melting = false; 253 this.meltFrameCount = storm.meltFrameCount; 254 this.meltFrames = storm.meltFrames; 255 this.meltFrame = 0; 256 this.twinkleFrame = 0; 257 this.active = 1; 258 this.fontSize = (10+(this.type/5)*10); 259 this.o = document.createElement('div'); 260 this.o.innerHTML = storm.snowCharacter; 261 this.o.style.color = storm.snowColor; 262 this.o.style.position = (fixedForEverything?'fixed':'absolute'); 263 this.o.style.width = storm.flakeWidth+'px'; 264 this.o.style.height = storm.flakeHeight+'px'; 265 this.o.style.fontFamily = 'arial,verdana'; 266 this.o.style.cursor = 'default'; 267 this.o.style.overflow = 'hidden'; 268 this.o.style.fontWeight = 'normal'; 269 this.o.style.zIndex = storm.zIndex; 270 docFrag.appendChild(this.o); 271 272 this.refresh = function() { 273 if (isNaN(s.x) || isNaN(s.y)) { 274 // safety check 275 return false; 276 } 277 s.o.style.left = s.x+'px'; 278 s.o.style.top = s.y+'px'; 279 }; 280 281 this.stick = function() { 282 if (noFixed || (storm.targetElement !== document.documentElement && storm.targetElement !== document.body)) { 283 s.o.style.top = (screenY+scrollY-storm.flakeHeight)+'px'; 284 } else if (storm.flakeBottom) { 285 s.o.style.top = storm.flakeBottom+'px'; 286 } else { 287 s.o.style.display = 'none'; 288 s.o.style.top = 'auto'; 289 s.o.style.bottom = '0px'; 290 s.o.style.position = 'fixed'; 291 s.o.style.display = 'block'; 292 } 293 }; 294 295 this.vCheck = function() { 296 if (s.vX>=0 && s.vX<0.2) { 297 s.vX = 0.2; 298 } else if (s.vX<0 && s.vX>-0.2) { 299 s.vX = -0.2; 300 } 301 if (s.vY>=0 && s.vY<0.2) { 302 s.vY = 0.2; 303 } 304 }; 305 306 this.move = function() { 307 var vX = s.vX*windOffset, yDiff; 308 s.x += vX; 309 s.y += (s.vY*s.vAmp); 310 if (s.x >= screenX || screenX-s.x < storm.flakeWidth) { // X-axis scroll check 311 s.x = 0; 312 } else if (vX < 0 && s.x-storm.flakeLeftOffset < -storm.flakeWidth) { 313 s.x = screenX-storm.flakeWidth-1; // flakeWidth; 314 } 315 s.refresh(); 316 yDiff = screenY+scrollY-s.y; 317 if (yDiff<storm.flakeHeight) { 318 s.active = 0; 319 if (storm.snowStick) { 320 s.stick(); 321 } else { 322 s.recycle(); 323 } 324 } else { 325 if (storm.useMeltEffect && s.active && s.type < 3 && !s.melting && Math.random()>0.998) { 326 // ~1/1000 chance of melting mid-air, with each frame 327 s.melting = true; 328 s.melt(); 329 // only incrementally melt one frame 330 // s.melting = false;
331 } 332 if (storm.useTwinkleEffect) { 333 if (!s.twinkleFrame) { 334 if (Math.random()>0.9) { 335 s.twinkleFrame = parseInt(Math.random()*20,10); 336 } 337 } else { 338 s.twinkleFrame--; 339 s.o.style.visibility = (s.twinkleFrame && s.twinkleFrame%2===0?'hidden':'visible'); 340 } 341 } 342 } 343 }; 344 345 this.animate = function() { 346 // main animation loop 347 // move, check status, die etc. 348 s.move(); 349 }; 350 351 this.setVelocities = function() { 352 s.vX = vRndX+rnd(storm.vMaxX*0.12,0.1); 353 s.vY = vRndY+rnd(storm.vMaxY*0.12,0.1); 354 }; 355 356 this.setOpacity = function(o,opacity) { 357 if (!opacitySupported) { 358 return false; 359 } 360 o.style.opacity = opacity; 361 }; 362 363 this.melt = function() { 364 if (!storm.useMeltEffect || !s.melting) { 365 s.recycle(); 366 } else { 367 if (s.meltFrame < s.meltFrameCount) { 368 s.setOpacity(s.o,s.meltFrames[s.meltFrame]); 369 s.o.style.fontSize = s.fontSize-(s.fontSize*(s.meltFrame/s.meltFrameCount))+'px'; 370 s.o.style.lineHeight = storm.flakeHeight+2+(storm.flakeHeight*0.75*(s.meltFrame/s.meltFrameCount))+'px'; 371 s.meltFrame++; 372 } else { 373 s.recycle(); 374 } 375 } 376 }; 377 378 this.recycle = function() { 379 s.o.style.display = 'none'; 380 s.o.style.position = (fixedForEverything?'fixed':'absolute'); 381 s.o.style.bottom = 'auto'; 382 s.setVelocities(); 383 s.vCheck(); 384 s.meltFrame = 0; 385 s.melting = false; 386 s.setOpacity(s.o,1); 387 s.o.style.padding = '0px'; 388 s.o.style.margin = '0px'; 389 s.o.style.fontSize = s.fontSize+'px'; 390 s.o.style.lineHeight = (storm.flakeHeight+2)+'px'; 391 s.o.style.textAlign = 'center'; 392 s.o.style.verticalAlign = 'baseline'; 393 s.x = parseInt(rnd(screenX-storm.flakeWidth-20),10); 394 s.y = parseInt(rnd(screenY)*-1,10)-storm.flakeHeight; 395 s.refresh(); 396 s.o.style.display = 'block'; 397 s.active = 1; 398 }; 399 400 this.recycle(); // set up x/y coords etc. 401 this.refresh(); 402 403 }; 404 405 this.snow = function() { 406 var active = 0, used = 0, waiting = 0, flake = null, i; 407 for (i=s.flakes.length; i--;) { 408 if (s.flakes[i].active === 1) { 409 s.flakes[i].move(); 410 active++; 411 } else if (s.flakes[i].active === 0) { 412 used++; 413 } else { 414 waiting++; 415 } 416 if (s.flakes[i].melting) { 417 s.flakes[i].melt(); 418 } 419 } 420 if (active<s.flakesMaxActive) { 421 flake = s.flakes[parseInt(rnd(s.flakes.length),10)]; 422 if (flake.active === 0) { 423 flake.melting = true; 424 } 425 } 426 }; 427 428 this.mouseMove = function(e) { 429 if (!s.followMouse) { 430 return true; 431 } 432 var x = parseInt(e.clientX,10); 433 if (x<screenX2) { 434 windOffset = -windMultiplier+(x/screenX2*windMultiplier); 435 } else { 436 x -= screenX2; 437 windOffset = (x/screenX2)*windMultiplier; 438 } 439 }; 440 441 this.createSnow = function(limit,allowInactive) { 442 var i; 443 for (i=0; i<limit; i++) { 444 s.flakes[s.flakes.length] = new s.SnowFlake(s,parseInt(rnd(flakeTypes),10)); 445 if (allowInactive || i>s.flakesMaxActive) { 446 s.flakes[s.flakes.length-1].active = -1; 447 } 448 } 449 storm.targetElement.appendChild(docFrag); 450 }; 451 452 this.timerInit = function() { 453 s.timers = (!isWin98?[setInterval(s.snow,s.animationInterval)]:[setInterval(s.snow,s.animationInterval*3),setInterval(s.snow,s.animationInterval)]); 454 }; 455 456 this.init = function() { 457 var i; 458 for (i=0; i<s.meltFrameCount; i++) { 459 s.meltFrames.push(1-(i/s.meltFrameCount)); 460 } 461 s.randomizeWind(); 462 s.createSnow(s.flakesMax); // create initial batch 463 s.events.add(window,'resize',s.resizeHandler); 464 s.events.add(window,'scroll',s.scrollHandler); 465 if (s.freezeOnBlur) { 466 if (isIE) { 467 s.events.add(document,'focusout',s.freeze); 468 s.events.add(document,'focusin',s.resume); 469 } else { 470 s.events.add(window,'blur',s.freeze); 471 s.events.add(window,'focus',s.resume); 472 } 473 } 474 s.resizeHandler(); 475 s.scrollHandler(); 476 if (s.followMouse) { 477 s.events.add(isIE?document:window,'mousemove',s.mouseMove); 478 } 479 s.animationInterval = Math.max(20,s.animationInterval); 480 s.timerInit(); 481 }; 482 483 this.start = function(bFromOnLoad) { 484 if (!didInit) { 485 didInit = true; 486 } else if (bFromOnLoad) { 487 // already loaded and running 488 return true; 489 } 490 if (typeof s.targetElement === 'string') { 491 var targetID = s.targetElement; 492 s.targetElement = document.getElementById(targetID); 493 if (!s.targetElement) { 494 throw new Error('Snowstorm: Unable to get targetElement "'+targetID+'"'); 495 } 496 } 497 if (!s.targetElement) { 498 s.targetElement = (!isIE?(document.documentElement?document.documentElement:document.body):document.body); 499 } 500 if (s.targetElement !== document.documentElement && s.targetElement !== document.body) { 501 s.resizeHandler = s.resizeHandlerAlt; // re-map handler to get element instead of screen dimensions 502 } 503 s.resizeHandler(); // get bounding box elements 504 s.usePositionFixed = (s.usePositionFixed && !noFixed); // whether or not position:fixed is supported 505 fixedForEverything = s.usePositionFixed; 506 if (screenX && screenY && !s.disabled) { 507 s.init(); 508 s.active = true; 509 } 510 }; 511 512 function doDelayedStart() { 513 window.setTimeout(function() { 514 s.start(true); 515 }, 20); 516 // event cleanup 517 s.events.remove(isIE?document:window,'mousemove',doDelayedStart); 518 } 519 520 function doStart() { 521 if (!s.excludeMobile || !isMobile) { 522 if (s.freezeOnBlur) { 523 s.events.add(isIE?document:window,'mousemove',doDelayedStart); 524 } else { 525 doDelayedStart(); 526 } 527 } 528 // event cleanup 529 s.events.remove(window, 'load', doStart); 530 } 531 532 // hooks for starting the snow 533 if (s.autoStart) { 534 s.events.add(window, 'load', doStart, false); 535 } 536 537 return this; 538 539}(window, document));
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.