/* * * @(#) $Id: animation.js,v 1.29 2012/12/27 06:12:20 mlemos Exp $ * */ var ML; if(ML === undefined) { ML = { }; } if(ML.Animation === undefined) { ML.Animation = { }; } if(ML.Animation.Utility === undefined) { ML.Animation.Utility = { setOpacity: function(element, value) { element.style.opacity = element.style['-moz-opacity'] = element.style['-khtml-opacity'] = value; element.style.filter = 'alpha(opacity=' + Math.round(100*value) + ')'; if(!element.currentStyle || !element.currentStyle.hasLayout) { element.style.zoom = '100%'; } }, getElementPosition: function(element) { var position = { x: 0, y: 0}; do { position.x += element.offsetLeft; position.y += element.offsetTop; element = element.offsetParent; if(!element) { return position; } } while(element.nodeName !== 'HTML'); position.x += element.offsetLeft; position.y += element.offsetTop; return position; }, getElementSize: function(element, inner) { if(inner) { if(element.innerWidth) { return { width: element.innerWidth, height: element.innerHeight }; } if(element.clientWidth) { return { width: element.clientWidth, height: element.clientHeight }; } } else { if(element.clip !== undefined) { return { width: element.clip.width, height: element.clip.height }; } } return { width: element.offsetWidth, height: element.offsetHeight }; }, getWindowViewport: function() { var viewport; if(window.pageXOffset !== undefined) { viewport = { x: window.pageXOffset, y: window.pageYOffset }; } else { if(document.documentElement && document.documentElement.scrollLeft !== undefined) { viewport = { x: document.documentElement.scrollLeft, y: document.documentElement.scrollTop }; } else { if(document.body) { viewport = { x: document.body.scrollLeft, y: document.body.scrollTop }; } else { viewport = { x: 0, y: 0 }; } } } if(window.innerHeight !== undefined) { viewport.width = window.innerWidth; viewport.height = window.innerHeight; } else { if(document.documentElement && document.documentElement.clientHeight !== undefined) { viewport.width = document.documentElement.clientWidth; viewport.height = document.documentElement.clientHeight; } else { if(document.body) { viewport.width = document.body.clientWidth; viewport.height = document.body.clientHeight; } else { viewport.width = 0; viewport.height = 0; } } } return viewport; } }; } if(ML.Animation.Effects === undefined) { ML.Animation.Effects = { AppendContent: function(effect) { this.start = function() { var e = document.getElementById(effect.element); if(e) { e.innerHTML += effect.content; } else { if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } } return true; }; }, CancelAnimation: function(effect) { this.start = function() { var a, c; for(a = 0, c = 0; a= 2) { alert('Inexisting animation to cancel "' + effect.animation + '" of effect ' + this.animation.effect); } return true; }; }, Emphasize: function(effect) { this.context = null; this.angle = Math.PI / 6; this.angleGap = Math.PI / 48; this.underlineAngle = Math.PI / 90; this.lineWidth = (effect.lineWidth !== undefined ? effect.lineWidth : 4); this.opacity = (effect.opacity !== undefined ? effect.opacity : 0.5); this.strokeStyle = (effect.strokeStyle !== undefined ? effect.strokeStyle : '#0000ff'); this.lastAngle = this.angle; this.lastStep = 0; function drawEllipse(context, x, y, w, h, start, end, anticlockwise) { var scale; if((w <= 0 || h <= 0) && this.debug) { alert('Inexisting animation to cancel "' + effect.animation + '" of effect ' + this.animation.effect); return; } scale = h / w; context.save(); context.scale(1, scale); context.beginPath(); context.arc(x, y / scale, w, start, end, anticlockwise); context.restore(); context.stroke(); } this.start = function() { var canvas, e = document.getElementById(effect.element); if(e) { canvas = document.createElement('canvas'); if(!canvas || !canvas.getContext || (this.context = canvas.getContext('2d')) === null) { if(this.debug) { alert('Canvas elements are not supported.'); } return true; } if(effect.canvas !== undefined) { canvas.id = effect.canvas; } this.size = ML.Animation.Utility.getElementSize(e, false); if(effect.method === 'circle') { canvas.width = this.size.width / Math.cos(this.angle) + this.lineWidth; canvas.style.left = (this.size.width - canvas.width)/2 + 'px'; this.size.width = canvas.width; canvas.height = this.size.height / Math.sin(this.angle) + this.lineWidth; canvas.style.top = (this.size.height - canvas.height)/2 + 'px'; this.size.height = canvas.height; e.insertBefore(canvas, e.firstChild); this.lastAngle = -this.angle; } else { if(effect.method === 'underline' || effect.method === 'double-underline') { canvas.width = this.size.width + this.lineWidth; canvas.style.left = (this.lineWidth / 2) + 'px'; canvas.height = (1 - Math.cos(this.underlineAngle)) * this.size.width / Math.sin(this.underlineAngle) + this.lineWidth * 1.5; canvas.style.top = this.size.height; if(effect.method === 'double-underline') { canvas.height += this.lineWidth * 2; } this.size.height = this.size.width / Math.sin(this.underlineAngle) - this.lineWidth; e.insertBefore(canvas, e.firstChild); this.lastAngle = -Math.PI / 2 - this.underlineAngle; } else { if(this.debug) { alert('It was not specified a valid method for effect ' + this.animation.effect + ' "' + effect.type + '"'); } return true; } } if(e.style.zIndex.length === 0) { e.style.zIndex = 0; } e.style.position = "relative"; canvas.style.zIndex = parseInt(e.style.zIndex, 10) - 1; canvas.style.position = 'absolute'; canvas.style.pointerEvents = 'none'; ML.Animation.Utility.setOpacity(canvas, this.opacity); this.context.lineWidth = this.lineWidth; this.context.lineCap = 'round'; this.context.strokeStyle = this.strokeStyle; return false; } if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } return true; }; this.step = function() { var p, angle; p = (new Date().getTime() - this.startTime)/(this.duration*1000.0); if(p>1) { p = 1; } if(effect.method === 'circle') { angle = p * p * (-2 * Math.PI + this.angleGap) - this.angle; drawEllipse(this.context, this.size.width/2, this.size.height/2, this.size.width/2 - this.lineWidth, this.size.height/2 - this.lineWidth, this.lastAngle, angle, true); this.lastAngle = angle; } else { if(effect.method === 'underline') { angle = -Math.PI / 2 - (1 - p * p) * this.underlineAngle; drawEllipse(this.context, this.size.width + this.lineWidth / 2, this.size.height + this.lineWidth / 2, this.size.height, this.size.height, this.lastAngle, angle, false); this.lastAngle = angle; } else { if(effect.method === 'double-underline') { if(p <= 0.5) { angle = -Math.PI / 2 - (1 - 4 * p * p) * this.underlineAngle; drawEllipse(this.context, this.size.width + this.lineWidth / 2, this.size.height + this.lineWidth / 2, this.size.height, this.size.height, this.lastAngle, angle, false); } else { angle = -Math.PI / 2 - (1 - 4 * (p - 0.5) * (p - 0.5)) * this.underlineAngle; if(this.lastStep < 0.5) { drawEllipse(this.context, this.size.width + this.lineWidth / 2, this.size.height + this.lineWidth / 2, this.size.height, this.size.height, this.lastAngle, -Math.PI / 2, false); this.lastAngle = -Math.PI / 2 - this.underlineAngle; } drawEllipse(this.context, this.size.width + this.lineWidth / 2, this.size.height + this.lineWidth * 2.5, this.size.height, this.size.height, this.lastAngle, angle, false); } this.lastAngle = angle; this.lastStep = p; } } } return(p < 1); }; }, FadeIn: function(effect) { this.node = null; this.opacity = 0; this.start = function() { this.node = document.getElementById(effect.element); if(this.node) { this.opacity = 0; ML.Animation.Utility.setOpacity(this.node, this.opacity); if(effect.visibility === 'display') { this.node.style.display = 'block'; } else { this.node.style.visibility = 'visible'; } return false; } if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } return true; }; this.step = function() { var p, opacity; p = (new Date().getTime() - this.startTime)/(this.duration*1000.0); if(p>1) { p = 1; } opacity = Math.round(p * ML.Animation.fadeStep) / ML.Animation.fadeStep; if(opacity !== this.opacity) { ML.Animation.Utility.setOpacity(this.node, this.opacity = opacity); } return (p < 1); }; }, FadeOut: function(effect) { this.node = null; this.opacity = 0; this.start = function() { this.node = document.getElementById(effect.element); if(this.node) { this.opacity = 1.0; ML.Animation.Utility.setOpacity(this.node, this.opacity); return false; } if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } return true; }; this.step = function() { var p, opacity, step; p = (new Date().getTime() - this.startTime)/(this.duration*1000.0); if(p>1) { p = 1; } opacity = Math.round((1.0 - p) * ML.Animation.fadeStep) / ML.Animation.fadeStep; if(opacity !== this.opacity) { ML.Animation.Utility.setOpacity(this.node, this.opacity = opacity); } step = p < 1; if(!step) { if(effect.visibility === 'display') { this.node.style.display = 'none'; } else { this.node.style.visibility = 'hidden'; } } return step; }; }, Hide: function(effect) { this.start = function() { var e = document.getElementById(effect.element); if(e) { if(effect.visibility === 'display') { e.style.display = 'none'; } else { e.node.style.visibility = 'hidden'; } } else { if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } } return true; }; }, MakeVisible: function(effect) { this.startX = 0; this.startY = 0; this.start = function() { var viewport, e = document.getElementById(effect.element); if(e) { viewport = ML.Animation.Utility.getWindowViewport(); this.startX = viewport.x; this.startY = viewport.y; } else { if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } } return false; }; this.step = function() { var p, e, viewport, position, size, x, y, limit; e = document.getElementById(effect.element); viewport = ML.Animation.Utility.getWindowViewport(); position = ML.Animation.Utility.getElementPosition(e); size = ML.Animation.Utility.getElementSize(e, false); x = viewport.x; limit = position.x + size.width - viewport.width; if(limit > x) { x = limit; } if(x > position.x) { x = position.x; } y = viewport.y; limit = position.y + size.height - viewport.height; if(limit > y) { y = limit; } if(y > position.y) { y = position.y; } if(x === viewport.x && y === viewport.y) { return false; } p = (new Date().getTime() - this.startTime)/(this.duration*1000.0); if(p>1) { p = 1; } x = this.startX + Math.round((x - this.startX) * p * p); y = this.startY + Math.round((y - this.startY) * p * p); window.scrollTo(x, y); return (p !== 1); }; }, PrependContent: function(effect) { this.start = function() { var e = document.getElementById(effect.element); if(e) { e.innerHTML = effect.content + e.innerHTML; } else { if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } } return true; }; }, ReplaceContent: function(effect) { this.start = function() { var e = document.getElementById(effect.element); if(e) { e.innerHTML = effect.content; } else { if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } } return true; }; }, Show: function(effect) { this.start = function() { var e = document.getElementById(effect.element); if(e) { if(effect.visibility === 'display') { e.style.display = 'block'; } else { e.style.visibility = 'visible'; } } else { if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } } return true; }; }, SlideIn: function(effect) { this.node = this.parent = this.edge = this.size = this.oldEdge = this.oldPosition = null; this.start = function() { var size; this.node = document.getElementById(effect.element); if(this.node) { this.parent = document.createElement('div'); this.parent.style.overflow = 'hidden'; this.node.style.display = 'block'; size = ML.Animation.Utility.getElementSize(this.node, false); switch(effect.edge) { case 'top': this.edge = this.size = size.height; this.oldEdge = this.node.style.bottom; this.node.style.bottom = this.edge + 'px'; this.parent.style.height = '0'; break; case 'bottom': this.edge = this.size = size.height; this.oldEdge = this.node.style.top; this.node.style.top = this.edge + 'px'; this.parent.style.height = '0'; break; case 'left': this.edge = this.size = size.width; this.oldEdge = this.node.style.right; this.node.style.right = this.edge + 'px'; this.parent.style.width = '0'; break; case 'right': this.edge = this.size = size.width; this.oldEdge = this.node.style.left; this.node.style.left = this.edge + 'px'; this.parent.style.width = '0'; break; default: if(this.debug) { alert('Invalid ' + effect.type + ' effect edge "' + effect.edge); this.parent = null; return true; } } this.oldPosition = this.node.style.position; this.node.style.position = 'relative'; this.node.parentNode.insertBefore(this.parent, this.node); this.node.parentNode.removeChild(this.node); this.parent.appendChild(this.node); return false; } if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } return true; }; this.step = function() { var p, edge, step; p = (new Date().getTime() - this.startTime)/(this.duration*1000.0); if(p>1) { p = 1; } edge = Math.round(this.size * (1 - p * p)); if(edge !== this.edge) { this.edge = edge; switch(effect.edge) { case 'top': this.node.style.bottom = this.edge + 'px'; this.parent.style.height = (this.size - this.edge) + 'px'; break; case 'bottom': this.node.style.top = this.edge + 'px'; this.parent.style.height = (this.size - this.edge) + 'px'; break; case 'left': this.node.style.right = this.edge + 'px'; this.parent.style.width = (this.size - this.edge) + 'px'; break; case 'right': this.node.style.left = this.edge + 'px'; this.parent.style.width = (this.size - this.edge) + 'px'; break; } } step = (p < 1); if(!step) { this.parent.removeChild(this.node); this.parent.parentNode.insertBefore(this.node, this.parent); switch(effect.edge) { case 'top': this.node.style.bottom = this.oldEdge; break; case 'bottom': this.node.style.top = this.oldEdge; break; case 'left': this.node.style.right = this.oldEdge; break; case 'right': this.node.style.left = this.oldEdge; break; } this.node.style.position = this.oldPosition; this.parent.parentNode.removeChild(this.parent); this.parent = null; } return step; }; }, SlideOut: function(effect) { this.node = this.parent = this.edge = this.size = this.oldEdge = this.oldPosition = null; this.start = function() { var size; this.node = document.getElementById(effect.element); if(this.node) { this.parent = document.createElement('div'); this.parent.style.overflow = 'hidden'; this.node.style.display = 'block'; size = ML.Animation.Utility.getElementSize(this.node, false); switch(effect.edge) { case 'top': this.parent.style.height = this.edge = this.size = size.height; this.oldEdge = this.node.style.bottom; this.node.style.bottom = this.edge + 'px'; break; case 'bottom': this.parent.style.height = this.edge = this.size = size.height; this.oldEdge = this.node.style.top; this.node.style.top = this.edge + 'px'; break; case 'left': this.parent.style.width = this.edge = this.size = size.width; this.oldEdge = this.node.style.right; this.node.style.right = this.edge + 'px'; break; case 'right': this.parent.style.width = this.edge = this.size = size.width; this.oldEdge = this.node.style.left; this.node.style.left = this.edge + 'px'; break; default: if(this.debug) { alert('Invalid ' + effect.type + ' effect edge "' + effect.edge); this.parent = null; return true; } } this.oldPosition = this.node.style.position; this.node.style.position = 'relative'; this.node.parentNode.insertBefore(this.parent, this.node); this.node.parentNode.removeChild(this.node); this.parent.appendChild(this.node); return false; } if(this.debug) { alert('Inexisting element "' + effect.element + '" of effect ' + this.animation.effect + ' "' + effect.type + '"'); } return true; }; this.step = function() { var p, edge, step; p = (new Date().getTime() - this.startTime)/(this.duration*1000.0); if(p>1) { p = 1; } edge = Math.round(this.size * p * p); if(edge !== this.edge) { this.edge = edge; switch(effect.edge) { case 'top': this.node.style.bottom = this.edge + 'px'; this.parent.style.height = (this.size - this.edge) + 'px'; break; case 'bottom': this.node.style.top = this.edge + 'px'; this.parent.style.height = (this.size - this.edge) + 'px'; break; case 'left': this.node.style.right = this.edge + 'px'; this.parent.style.width = (this.size - this.edge) + 'px'; break; case 'right': this.node.style.left = this.edge + 'px'; this.parent.style.width = (this.size - this.edge) + 'px'; break; } } step = (p < 1); if(!step) { this.parent.removeChild(this.node); this.parent.parentNode.insertBefore(this.node, this.parent); switch(effect.edge) { case 'top': this.node.style.bottom = this.oldEdge; break; case 'bottom': this.node.style.top = this.oldEdge; break; case 'left': this.node.style.right = this.oldEdge; break; case 'right': this.node.style.left = this.oldEdge; break; } this.node.style.position = this.oldPosition; this.parent.parentNode.removeChild(this.parent); this.parent = null; this.node.style.display = 'none'; } return step; }; }, Wait: function(effect) { this.start = function() { return false; }; this.step = function() { return (new Date().getTime() - this.startTime < this.duration*1000.0); }; } }; ML.Animation.registerEffects = function(name, effects) { if(ML.Animation.Effects[name] !== undefined) { return false; } ML.Animation.Effects[name] = effects; return true; }; } if(ML.Animation.Animate === undefined) { ML.Animation.animations = []; ML.Animation.running = 0; ML.Animation.poll = null; ML.Animation.frameRate = 120; ML.Animation.fadeStep = 100; ML.Animation.defaultDuration = 1; ML.Animation.cancelAnimation = function(animation) { ML.Animation.animations[animation.animation] = null; if(--ML.Animation.running === 0) { clearInterval(ML.Animation.poll); ML.Animation.poll = null; } }; ML.Animation.Animate = function() { var advanceAnimation, stepEffects, startEffect; advanceAnimation = function(animation) { animation.running = false; if(++animation.effect < animation.definition.effects.length) { startEffect(animation); } else { ML.Animation.cancelAnimation(animation); } }; stepEffects = function() { var a, animation, effect, step; for(a = 0; a