function FireworksController() { var self = this; this.intervalRate = 20; // rate (ms) to run animation at, general best default = 20 this.DEBUG = true; // debug mode disabled by default this.oFW = null; this.isIE = (navigator.appVersion.indexOf('MSIE')+1); this.isOpera = (navigator.userAgent.toLowerCase().indexOf('opera')+1); if (this.isOpera) this.isIE = false; // no impersonation allowed here! this.fireworks = []; this.animator = null; this.gOID = 0; // global object ID counter (for animation queue) this.particleTypes = 6; this.particleXY = 10; this.tweenFade = [100,90,80,70,60,50,40,30,20,10,0]; this.isSafari = (navigator.appVersion.toLowerCase().indexOf('safari')+1?1:0); this.canvasX = null; this.canvasY = null; this.screenY = null; // screen area (not entire page) self.scrollY = null; self.getWindowCoords = function() { self.canvasX = (document.documentElement.clientWidth||document.body.clientWidth||document.body.scrollWidth); self.canvasY = (document.documentElement.clientHeight||document.body.clientHeight||document.body.scrollHeight); self.screenY = self.canvasY; self.scrollY = parseInt(window.scrollY||document.documentElement.scrollTop||document.body.scrollTop); self.canvasY += self.scrollY; } this.getWindowCoordsAlt = function() { self.canvasX = window.innerWidth; self.canvasY = window.innerHeight; self.screenY = self.canvasY; self.scrollY = parseInt(window.scrollY||document.documentElement.scrollTop||document.body.scrollTop); self.canvasY += self.scrollY; } this.getPanX = function(x) { x = parseInt(x); var pos = x/self.canvasX; if (pos<0.4) { pos *= -1; } else if (pos >= 0.4 && pos <= 0.6) { pos = 0.5; } pos = parseInt(pos*100); // writeDebug('getPanX('+x+'): '+pos+'%'); return pos; } this.isEmpty = function(o) { // needs further hacking return (typeof(o)=='undefined'||(o==null&&o!=0)||(o==''&&o!=0)||o=='null'); } this.init = function() { self.oFW = document.getElementById('fw'); self.oFP = document.getElementById('fp'); if (typeof(enableDebugMode)!='undefined' && (self.DEBUG||window.location.toString().toLowerCase().indexOf('debug')>=0)) enableDebugMode(); self.getWindowCoords(); self.animator = new Animator(); } this.destructor = function() { for (var i=self.fireworks.length; i--;) { self.fireworks[i] = null; } self.fireworks = null; if (soundManager) { soundManager.destructor(); soundManager = null; } } if (this.isSafari || this.isOpera) this.getWindowCoords = this.getWindowCoordsAlt; } function Firework(oC,startX,startY,burstX,burstY,burstType,nRadius,nParticles,nCircles,allowRandom,obeyBoundaries) { var self = this; this.oID = 'fp'+(fc.gOID++); // may be unneeded var p = ''; for (var i=0; i=self.tween.length) { self.active = false; self.frame = 0; if (self._oncomplete) self._oncomplete(); self._oncomplete = null; return false; } return true; } this.destructor = function() { writeDebug('firework.destructor()'); // for (var i=0; i=self.tween[0].length-1) { self.active = false; self.frame = 0; if (self._oncomplete) self._oncomplete(); self._oncomplete = null; return false; } return true; } this.createBurst = function(circles,nMax,rMax,type) { // c: # of circles, n: # of particles per circle, r: max radius writeDebug('firework.createBurst('+circles+','+nMax+','+rMax+','+type+')'); var i=0, j=0; var tmp = 0; var radiusInc = rMax/circles; var radius = radiusInc; var angle = 0; var angleInc = 0; // per-loop increment var radiusOffset = (self.allowRandom?(0.33+Math.random()):1); var particlesPerCircle = []; var isRandom = Math.random()>0.5; var circleTypes = [type,circles>1?parseInt(Math.random()*fc.particleTypes):type]; var thisType = null; for (i=0; i=0) { if (toX>=xMax) self.vx *= -1; } else if (self.vx<0 && toX+self.baseX<=0) self.vx *= -1; if (self.vy>=0) { if (toY>=yMax) self.vy *= -1; } else if (self.vy<0) { if (toY+self.baseY-yMin<=0) self.vy *= -1; } } self.moveTo(self.x+self.vx,self.y+self.vy); } this.setOpacity = function(n) { // where n = 0..100 self.oImg.style.marginLeft = -100+(n*fc.particleXY/10)+'px'; } this.nextState = function() { var vis = self.o.style.visibility; if (self.state == 2 && vis != 'hidden') { self.o.style.visibility = 'hidden'; } else if (self.state != 2 && vis == 'hidden') { self.o.style.visibility = 'visible'; } self.state = parseInt(Math.random()*3); } this.slideTo = function(x1,y1) { // writeDebug('slideTo (x/y): '+x1+','+y1); if (self.isRandom) { // randomize a bit x1 += (x1*0.2*(Math.random()>0.5?1:-1)); y1 += (y1*0.2*(Math.random()>0.5?1:-1)); } self.tween = [fc.animator.createTween(self.x,x1,self.tweenType),fc.animator.createTween(self.y,y1,self.tweenType)]; // prevent X overflow (scrolling) var xMax = fc.canvasX-fc.particleXY; var yMax = fc.canvasY-fc.particleXY; var xMin = fc.particleXY-self.baseX; var yMin = fc.scrollY; var toX = null; var toY = null; if (self.obeyBoundaries) { for (var i=self.tween[0].length; i--;) { // bounce off walls where applicable toX = self.tween[0][i].data+self.baseX; toY = self.tween[1][i].data+self.baseY; if (toX>=xMax) { self.tween[0][i].data -= (toX-xMax)*2; // self.tween[0][i].event = 'bounce'; } else if (toX<0) { self.tween[0][i].data -= (toX*2); // self.tween[0][i].event = 'bounce'; } if (toY>=yMax) { self.tween[1][i].data -= (toY-yMax)*2; // self.tween[1][i].event = 'bounce'; } else if (toY-yMin<=0) { self.tween[1][i].data -= (toY-yMin)*2; // self.tween[1][i].event = 'bounce'; } } } } this.animate = function() { var f0 = self.tween[0][self.frame].data; var f1 = self.tween[1][self.frame].data; self.moveTo(f0,f1); // possible bounce event/sound hooks // if (self.tween[0][self.frame].event) soundManager.play(self.tween[0][self.frame].event); // if (self.tween[1][self.frame].event) soundManager.play(self.tween[1][self.frame].event); if (self.frame++>=self.tween[0].length-1) { if (self._oncomplete) self._oncomplete(); self._oncomplete = null; self.active = false; self.frame = 0; return false; } else if (self.frame>10) { self.nextState(); } return true; } this.destructor = function() { self.oImg = null; self.oC.removeChild(self.o); self.oC = null; self.o = null; } this.setType = function(t) { self.type = t; self.oImg.style.marginTop = -(fc.particleXY*t)+'px'; } self.setType(type); self.oC.appendChild(self.o); } function Animator() { var self = this; writeDebug('Animator()'); this.tweens = []; this.tweens['default'] = [1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1]; this.tweens['blast'] = [12,12,11,10,10,9,8,7,6,5,4,3,2,1]; this.tweens['fade'] = [10,10,10,10,10,10,10,10,10,10]; this.queue = []; this.queue.IDs = []; this.active = false; this.timer = null; this.createTween = function(start,end,type) { // return array of tween coordinate data (start->end) type = type||'default'; var tween = [start]; var tmp = start; var diff = end-start; var x = self.tweens[type].length; for (var i=0; i=0) { nRadius = parseInt(parseInt(nRadius)/100*fc.screenY); } else if (nRadius.toString().indexOf('.')>=0) { nRadius = parseInt(nRadius*fc.screenY); } else { nRadius = parseInt(nRadius*fc.screenY/100); } if (fc.isEmpty(nParticles)) { nParticles = 4+parseInt(Math.random()*64); } if (fc.isEmpty(nCircles)) { nCircles = Math.random()>0.5?2:1; } if (fc.isEmpty(allowRandom)) { allowRandom = Math.random()>0.5; } if (fc.isEmpty(obeyBoundaries)) { obeyBoundaries = Math.random()>0.5; } // update screen coordinates fc.getWindowCoords(); fc.fireworks[fc.fireworks.length] = new Firework(document.getElementById('fireContainer'),startX,startY,burstX,burstY,nBurstType,nRadius,nParticles,nCircles,allowRandom,obeyBoundaries); } function smNull() { // Null object for unsupported case this.movies = []; // movie references this.container = null; this.unsupported = 1; this.FlashObject = function(url) {} this.addMovie = function(name,url) {} this.setPan = function() {} this.destructor = function() {} this.play = function(movieName,soundID) {return false;} this.defaultName = 'default'; } var fc = new FireworksController(); // create null objects if APIs not present if (typeof(SoundManager)=='undefined') var soundManager = new smNull(); if (typeof(writeDebug)=='undefined') var writeDebug = function(){return false;} function addEventHandler(o,evtName,evtHandler) { typeof(attachEvent)=='undefined'?o.addEventListener(evtName,evtHandler,false):o.attachEvent('on'+evtName,evtHandler); } function removeEventHandler(o,evtName,evtHandler) { typeof(attachEvent)=='undefined'?o.removeEventListener(evtName,evtHandler,false):o.detachEvent('on'+evtName,evtHandler); } addEventHandler(window,'resize',fc.getWindowCoords); addEventHandler(window,'scroll',fc.getWindowCoords); addEventHandler(window,'load',fc.init); addEventHandler(window,'unload',fc.destructor);