The version of vichan running on lainchan.org
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

669 lignes
21KB

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