X-Git-Url: https://git.openstreetmap.org./rails.git/blobdiff_plain/06f3473d1c64d2e0a1ecc6f1a9c4679a52bc4761..2ccbc55236d81d790643265f082475b691a5e6b4:/public/javascripts/effects.js diff --git a/public/javascripts/effects.js b/public/javascripts/effects.js index 927400502..65aed2395 100644 --- a/public/javascripts/effects.js +++ b/public/javascripts/effects.js @@ -1,26 +1,27 @@ -// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) // Contributors: // Justin Palmer (http://encytemedia.com/) // Mark Pilgrim (http://diveintomark.org/) // Martin Bialasinki // -// See scriptaculous.js for full license. +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ // converts rgb() and #xxx to #xxxxxx format, // returns self (or first argument) if not convertable String.prototype.parseColor = function() { - var color = '#'; - if(this.slice(0,4) == 'rgb(') { + var color = '#'; + if (this.slice(0,4) == 'rgb(') { var cols = this.slice(4,this.length-1).split(','); var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); } else { - if(this.slice(0,1) == '#') { - if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); - if(this.length==7) color = this.toLowerCase(); + if (this.slice(0,1) == '#') { + if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if (this.length==7) color = this.toLowerCase(); } } - return(color.length==7 ? color : (arguments[0] || this)); -} + return (color.length==7 ? color : (arguments[0] || this)); +}; /*--------------------------------------------------------------------------*/ @@ -29,7 +30,7 @@ Element.collectTextNodes = function(element) { return (node.nodeType==3 ? node.nodeValue : (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); }).flatten().join(''); -} +}; Element.collectTextNodesIgnoreClass = function(element, className) { return $A($(element).childNodes).collect( function(node) { @@ -37,53 +38,18 @@ Element.collectTextNodesIgnoreClass = function(element, className) { ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? Element.collectTextNodesIgnoreClass(node, className) : '')); }).flatten().join(''); -} +}; Element.setContentZoom = function(element, percent) { element = $(element); - Element.setStyle(element, {fontSize: (percent/100) + 'em'}); - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); -} - -Element.getOpacity = function(element){ - var opacity; - if (opacity = Element.getStyle(element, 'opacity')) - return parseFloat(opacity); - if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/)) - if(opacity[1]) return parseFloat(opacity[1]) / 100; - return 1.0; -} - -Element.setOpacity = function(element, value){ - element= $(element); - if (value == 1){ - Element.setStyle(element, { opacity: - (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? - 0.999999 : null }); - if(/MSIE/.test(navigator.userAgent)) - Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')}); - } else { - if(value < 0.00001) value = 0; - Element.setStyle(element, {opacity: value}); - if(/MSIE/.test(navigator.userAgent)) - Element.setStyle(element, - { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') + - 'alpha(opacity='+value*100+')' }); - } -} - -Element.getInlineOpacity = function(element){ - return $(element).style.opacity || ''; -} + element.setStyle({fontSize: (percent/100) + 'em'}); + if (Prototype.Browser.WebKit) window.scrollBy(0,0); + return element; +}; -Element.childrenWithClassName = function(element, className, findFirst) { - var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)"); - var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { - return (c.className && c.className.match(classNameRegExp)); - }); - if(!results) results = []; - return results; -} +Element.getInlineOpacity = function(element){ + return $(element).style.opacity || ''; +}; Element.forceRerendering = function(element) { try { @@ -96,23 +62,63 @@ Element.forceRerendering = function(element) { /*--------------------------------------------------------------------------*/ -Array.prototype.call = function() { - var args = arguments; - this.each(function(f){ f.apply(this, args) }); -} - -/*--------------------------------------------------------------------------*/ - var Effect = { + _elementDoesNotExistError: { + name: 'ElementDoesNotExistError', + message: 'The specified DOM element does not exist, but is required for this effect to operate' + }, + Transitions: { + linear: Prototype.K, + sinoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + 0.5; + }, + reverse: function(pos) { + return 1-pos; + }, + flicker: function(pos) { + var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; + return pos > 1 ? 1 : pos; + }, + wobble: function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; + }, + pulse: function(pos, pulses) { + pulses = pulses || 5; + return ( + ((pos % (1/pulses)) * pulses).round() == 0 ? + ((pos * pulses * 2) - (pos * pulses * 2).floor()) : + 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor()) + ); + }, + spring: function(pos) { + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); + }, + none: function(pos) { + return 0; + }, + full: function(pos) { + return 1; + } + }, + DefaultOptions: { + duration: 1.0, // seconds + fps: 100, // 100= assume 66fps max. + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' + }, tagifyText: function(element) { var tagifyStyle = 'position:relative'; - if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1'; + if (Prototype.Browser.IE) tagifyStyle += ';zoom:1'; + element = $(element); $A(element.childNodes).each( function(child) { - if(child.nodeType==3) { + if (child.nodeType==3) { child.nodeValue.toArray().each( function(character) { element.insertBefore( - Builder.node('span',{style: tagifyStyle}, + new Element('span', {style: tagifyStyle}).update( character == ' ' ? String.fromCharCode(160) : character), child); }); @@ -122,8 +128,8 @@ var Effect = { }, multiple: function(element, effect) { var elements; - if(((typeof element == 'object') || - (typeof element == 'function')) && + if (((typeof element == 'object') || + Object.isFunction(element)) && (element.length)) elements = element; else @@ -132,7 +138,7 @@ var Effect = { var options = Object.extend({ speed: 0.1, delay: 0.0 - }, arguments[2] || {}); + }, arguments[2] || { }); var masterDelay = options.delay; $A(elements).each( function(element, index) { @@ -149,51 +155,20 @@ var Effect = { effect = (effect || 'appear').toLowerCase(); var options = Object.extend({ queue: { position:'end', scope:(element.id || 'global'), limit: 1 } - }, arguments[2] || {}); + }, arguments[2] || { }); Effect[element.visible() ? Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); } }; -var Effect2 = Effect; // deprecated - -/* ------------- transitions ------------- */ - -Effect.Transitions = {} - -Effect.Transitions.linear = function(pos) { - return pos; -} -Effect.Transitions.sinoidal = function(pos) { - return (-Math.cos(pos*Math.PI)/2) + 0.5; -} -Effect.Transitions.reverse = function(pos) { - return 1-pos; -} -Effect.Transitions.flicker = function(pos) { - return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; -} -Effect.Transitions.wobble = function(pos) { - return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; -} -Effect.Transitions.pulse = function(pos) { - return (Math.floor(pos*10) % 2 == 0 ? - (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10))); -} -Effect.Transitions.none = function(pos) { - return 0; -} -Effect.Transitions.full = function(pos) { - return 1; -} +Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; /* ------------- core effects ------------- */ -Effect.ScopedQueue = Class.create(); -Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { +Effect.ScopedQueue = Class.create(Enumerable, { initialize: function() { this.effects = []; - this.interval = null; + this.interval = null; }, _each: function(iterator) { this.effects._each(iterator); @@ -201,7 +176,7 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { add: function(effect) { var timestamp = new Date().getTime(); - var position = (typeof effect.options.queue == 'string') ? + var position = Object.isString(effect.options.queue) ? effect.options.queue : effect.options.queue.position; switch(position) { @@ -212,6 +187,9 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { e.finishOn += effect.finishOn; }); break; + case 'with-last': + timestamp = this.effects.pluck('startOn').max() || timestamp; + break; case 'end': // start effect after last queued effect has finished timestamp = this.effects.pluck('finishOn').max() || timestamp; @@ -221,115 +199,111 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { effect.startOn += timestamp; effect.finishOn += timestamp; - if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) + if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) this.effects.push(effect); - if(!this.interval) - this.interval = setInterval(this.loop.bind(this), 40); + if (!this.interval) + this.interval = setInterval(this.loop.bind(this), 15); }, remove: function(effect) { this.effects = this.effects.reject(function(e) { return e==effect }); - if(this.effects.length == 0) { + if (this.effects.length == 0) { clearInterval(this.interval); this.interval = null; } }, loop: function() { var timePos = new Date().getTime(); - this.effects.invoke('loop', timePos); + for(var i=0, len=this.effects.length;i= this.startOn) { - if(timePos >= this.finishOn) { + if (timePos >= this.startOn) { + if (timePos >= this.finishOn) { this.render(1.0); this.cancel(); this.event('beforeFinish'); - if(this.finish) this.finish(); + if (this.finish) this.finish(); this.event('afterFinish'); return; } - var pos = (timePos - this.startOn) / (this.finishOn - this.startOn); - var frame = Math.round(pos * this.options.fps * this.options.duration); - if(frame > this.currentFrame) { + var pos = (timePos - this.startOn) / this.totalTime, + frame = (pos * this.totalFrames).round(); + if (frame > this.currentFrame) { this.render(pos); this.currentFrame = frame; } } }, - render: function(pos) { - if(this.state == 'idle') { - this.state = 'running'; - this.event('beforeSetup'); - if(this.setup) this.setup(); - this.event('afterSetup'); - } - if(this.state == 'running') { - if(this.options.transition) pos = this.options.transition(pos); - pos *= (this.options.to-this.options.from); - pos += this.options.from; - this.position = pos; - this.event('beforeUpdate'); - if(this.update) this.update(pos); - this.event('afterUpdate'); - } - }, cancel: function() { - if(!this.options.sync) - Effect.Queues.get(typeof this.options.queue == 'string' ? + if (!this.options.sync) + Effect.Queues.get(Object.isString(this.options.queue) ? 'global' : this.options.queue.scope).remove(this); this.state = 'finished'; }, event: function(eventName) { - if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); - if(this.options[eventName]) this.options[eventName](this); + if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if (this.options[eventName]) this.options[eventName](this); }, inspect: function() { - return '#'; + var data = $H(); + for(property in this) + if (!Object.isFunction(this[property])) data.set(property, this[property]); + return '#'; } -} +}); -Effect.Parallel = Class.create(); -Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { +Effect.Parallel = Class.create(Effect.Base, { initialize: function(effects) { this.effects = effects || []; this.start(arguments[1]); @@ -342,23 +316,45 @@ Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { effect.render(1.0); effect.cancel(); effect.event('beforeFinish'); - if(effect.finish) effect.finish(position); + if (effect.finish) effect.finish(position); effect.event('afterFinish'); }); } }); -Effect.Opacity = Class.create(); -Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { +Effect.Tween = Class.create(Effect.Base, { + initialize: function(object, from, to) { + object = Object.isString(object) ? $(object) : object; + var args = $A(arguments), method = args.last(), + options = args.length == 5 ? args[3] : null; + this.method = Object.isFunction(method) ? method.bind(object) : + Object.isFunction(object[method]) ? object[method].bind(object) : + function(value) { object[method] = value }; + this.start(Object.extend({ from: from, to: to }, options || { })); + }, + update: function(position) { + this.method(position); + } +}); + +Effect.Event = Class.create(Effect.Base, { + initialize: function() { + this.start(Object.extend({ duration: 0 }, arguments[0] || { })); + }, + update: Prototype.emptyFunction +}); + +Effect.Opacity = Class.create(Effect.Base, { initialize: function(element) { this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); // make this work on IE on elements without 'layout' - if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout)) + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) this.element.setStyle({zoom: 1}); var options = Object.extend({ from: this.element.getOpacity() || 0.0, to: 1.0 - }, arguments[1] || {}); + }, arguments[1] || { }); this.start(options); }, update: function(position) { @@ -366,35 +362,30 @@ Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { } }); -Effect.Move = Class.create(); -Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { +Effect.Move = Class.create(Effect.Base, { initialize: function(element) { this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); var options = Object.extend({ x: 0, y: 0, mode: 'relative' - }, arguments[1] || {}); + }, arguments[1] || { }); this.start(options); }, setup: function() { - // Bug in Opera: Opera returns the "real" position of a static element or - // relative element that does not have top/left explicitly set. - // ==> Always set top and left for position relative elements in your stylesheets - // (to 0 if you do not need them) this.element.makePositioned(); this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); this.originalTop = parseFloat(this.element.getStyle('top') || '0'); - if(this.options.mode == 'absolute') { - // absolute movement, so we need to calc deltaX and deltaY + if (this.options.mode == 'absolute') { this.options.x = this.options.x - this.originalLeft; this.options.y = this.options.y - this.originalTop; } }, update: function(position) { this.element.setStyle({ - left: this.options.x * position + this.originalLeft + 'px', - top: this.options.y * position + this.originalTop + 'px' + left: (this.options.x * position + this.originalLeft).round() + 'px', + top: (this.options.y * position + this.originalTop).round() + 'px' }); } }); @@ -402,29 +393,29 @@ Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { // for backwards compatibility Effect.MoveBy = function(element, toTop, toLeft) { return new Effect.Move(element, - Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); + Object.extend({ x: toLeft, y: toTop }, arguments[3] || { })); }; -Effect.Scale = Class.create(); -Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { +Effect.Scale = Class.create(Effect.Base, { initialize: function(element, percent) { - this.element = $(element) + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); var options = Object.extend({ scaleX: true, scaleY: true, scaleContent: true, scaleFromCenter: false, - scaleMode: 'box', // 'box' or 'contents' or {} with provided values + scaleMode: 'box', // 'box' or 'contents' or { } with provided values scaleFrom: 100.0, scaleTo: percent - }, arguments[2] || {}); + }, arguments[2] || { }); this.start(options); }, setup: function() { this.restoreAfterFinish = this.options.restoreAfterFinish || false; this.elementPositioning = this.element.getStyle('position'); - this.originalStyle = {}; + this.originalStyle = { }; ['top','left','width','height','fontSize'].each( function(k) { this.originalStyle[k] = this.element.style[k]; }.bind(this)); @@ -433,8 +424,8 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { this.originalLeft = this.element.offsetLeft; var fontSize = this.element.getStyle('font-size') || '100%'; - ['em','px','%'].each( function(fontSizeType) { - if(fontSize.indexOf(fontSizeType)>0) { + ['em','px','%','pt'].each( function(fontSizeType) { + if (fontSize.indexOf(fontSizeType)>0) { this.fontSize = parseFloat(fontSize); this.fontSizeType = fontSizeType; } @@ -443,17 +434,17 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; this.dims = null; - if(this.options.scaleMode=='box') + if (this.options.scaleMode=='box') this.dims = [this.element.offsetHeight, this.element.offsetWidth]; - if(/^content/.test(this.options.scaleMode)) + if (/^content/.test(this.options.scaleMode)) this.dims = [this.element.scrollHeight, this.element.scrollWidth]; - if(!this.dims) + if (!this.dims) this.dims = [this.options.scaleMode.originalHeight, this.options.scaleMode.originalWidth]; }, update: function(position) { var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); - if(this.options.scaleContent && this.fontSize) + if (this.options.scaleContent && this.fontSize) this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); }, @@ -461,41 +452,43 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle); }, setDimensions: function(height, width) { - var d = {}; - if(this.options.scaleX) d.width = width + 'px'; - if(this.options.scaleY) d.height = height + 'px'; - if(this.options.scaleFromCenter) { + var d = { }; + if (this.options.scaleX) d.width = width.round() + 'px'; + if (this.options.scaleY) d.height = height.round() + 'px'; + if (this.options.scaleFromCenter) { var topd = (height - this.dims[0])/2; var leftd = (width - this.dims[1])/2; - if(this.elementPositioning == 'absolute') { - if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; - if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; + if (this.elementPositioning == 'absolute') { + if (this.options.scaleY) d.top = this.originalTop-topd + 'px'; + if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; } else { - if(this.options.scaleY) d.top = -topd + 'px'; - if(this.options.scaleX) d.left = -leftd + 'px'; + if (this.options.scaleY) d.top = -topd + 'px'; + if (this.options.scaleX) d.left = -leftd + 'px'; } } this.element.setStyle(d); } }); -Effect.Highlight = Class.create(); -Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { +Effect.Highlight = Class.create(Effect.Base, { initialize: function(element) { this.element = $(element); - var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { }); this.start(options); }, setup: function() { // Prevent executing on elements not in the layout flow - if(this.element.getStyle('display')=='none') { this.cancel(); return; } + if (this.element.getStyle('display')=='none') { this.cancel(); return; } // Disable background image during the effect - this.oldStyle = { - backgroundImage: this.element.getStyle('background-image') }; - this.element.setStyle({backgroundImage: 'none'}); - if(!this.options.endcolor) + this.oldStyle = { }; + if (!this.options.keepBackgroundImage) { + this.oldStyle.backgroundImage = this.element.getStyle('background-image'); + this.element.setStyle({backgroundImage: 'none'}); + } + if (!this.options.endcolor) this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); - if(!this.options.restorecolor) + if (!this.options.restorecolor) this.options.restorecolor = this.element.getStyle('background-color'); // init color calculations this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); @@ -503,7 +496,7 @@ Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), }, update: function(position) { this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ - return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); + return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); }, finish: function() { this.element.setStyle(Object.extend(this.oldStyle, { @@ -512,30 +505,21 @@ Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), } }); -Effect.ScrollTo = Class.create(); -Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { - initialize: function(element) { - this.element = $(element); - this.start(arguments[1] || {}); - }, - setup: function() { - Position.prepare(); - var offsets = Position.cumulativeOffset(this.element); - if(this.options.offset) offsets[1] += this.options.offset; - var max = window.innerHeight ? - window.height - window.innerHeight : - document.body.scrollHeight - - (document.documentElement.clientHeight ? - document.documentElement.clientHeight : document.body.clientHeight); - this.scrollStart = Position.deltaY; - this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; - }, - update: function(position) { - Position.prepare(); - window.scrollTo(Position.deltaX, - this.scrollStart + (position*this.delta)); - } -}); +Effect.ScrollTo = function(element) { + var options = arguments[1] || { }, + scrollOffsets = document.viewport.getScrollOffsets(), + elementOffsets = $(element).cumulativeOffset(), + max = (window.height || document.body.scrollHeight) - document.viewport.getHeight(); + + if (options.offset) elementOffsets[1] += options.offset; + + return new Effect.Tween(null, + scrollOffsets.top, + elementOffsets[1] > max ? max : elementOffsets[1], + options, + function(p){ scrollTo(scrollOffsets.left, p.round()) } + ); +}; /* ------------- combination effects ------------- */ @@ -543,15 +527,15 @@ Effect.Fade = function(element) { element = $(element); var oldOpacity = element.getInlineOpacity(); var options = Object.extend({ - from: element.getOpacity() || 1.0, - to: 0.0, - afterFinishInternal: function(effect) { - if(effect.options.to!=0) return; - effect.element.hide(); - effect.element.setStyle({opacity: oldOpacity}); - }}, arguments[1] || {}); + from: element.getOpacity() || 1.0, + to: 0.0, + afterFinishInternal: function(effect) { + if (effect.options.to!=0) return; + effect.element.hide().setStyle({opacity: oldOpacity}); + } + }, arguments[1] || { }); return new Effect.Opacity(element,options); -} +}; Effect.Appear = function(element) { element = $(element); @@ -563,69 +547,71 @@ Effect.Appear = function(element) { effect.element.forceRerendering(); }, beforeSetup: function(effect) { - effect.element.setOpacity(effect.options.from); - effect.element.show(); - }}, arguments[1] || {}); + effect.element.setOpacity(effect.options.from).show(); + }}, arguments[1] || { }); return new Effect.Opacity(element,options); -} +}; Effect.Puff = function(element) { element = $(element); - var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') }; + var oldStyle = { + opacity: element.getInlineOpacity(), + position: element.getStyle('position'), + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height + }; return new Effect.Parallel( [ new Effect.Scale(element, 200, { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], Object.extend({ duration: 1.0, beforeSetupInternal: function(effect) { - effect.effects[0].element.setStyle({position: 'absolute'}); }, + Position.absolutize(effect.effects[0].element) + }, afterFinishInternal: function(effect) { - effect.effects[0].element.hide(); - effect.effects[0].element.setStyle(oldStyle); } - }, arguments[1] || {}) + effect.effects[0].element.hide().setStyle(oldStyle); } + }, arguments[1] || { }) ); -} +}; Effect.BlindUp = function(element) { element = $(element); element.makeClipping(); - return new Effect.Scale(element, 0, + return new Effect.Scale(element, 0, Object.extend({ scaleContent: false, scaleX: false, restoreAfterFinish: true, afterFinishInternal: function(effect) { - effect.element.hide(); - effect.element.undoClipping(); + effect.element.hide().undoClipping(); } - }, arguments[1] || {}) + }, arguments[1] || { }) ); -} +}; Effect.BlindDown = function(element) { element = $(element); var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, - Object.extend({ scaleContent: false, - scaleX: false, - scaleFrom: 0, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makeClipping(); - effect.element.setStyle({height: '0px'}); - effect.element.show(); - }, - afterFinishInternal: function(effect) { - effect.element.undoClipping(); - } - }, arguments[1] || {}) - ); -} + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + } + }, arguments[1] || { })); +}; Effect.SwitchOff = function(element) { element = $(element); var oldOpacity = element.getInlineOpacity(); - return new Effect.Appear(element, { + return new Effect.Appear(element, Object.extend({ duration: 0.4, from: 0, transition: Effect.Transitions.flicker, @@ -634,19 +620,15 @@ Effect.SwitchOff = function(element) { duration: 0.3, scaleFromCenter: true, scaleX: false, scaleContent: false, restoreAfterFinish: true, beforeSetup: function(effect) { - effect.element.makePositioned(); - effect.element.makeClipping(); + effect.element.makePositioned().makeClipping(); }, afterFinishInternal: function(effect) { - effect.element.hide(); - effect.element.undoClipping(); - effect.element.undoPositioned(); - effect.element.setStyle({opacity: oldOpacity}); + effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); } }) } - }); -} + }, arguments[1] || { })); +}; Effect.DropOut = function(element) { element = $(element); @@ -663,113 +645,107 @@ Effect.DropOut = function(element) { effect.effects[0].element.makePositioned(); }, afterFinishInternal: function(effect) { - effect.effects[0].element.hide(); - effect.effects[0].element.undoPositioned(); - effect.effects[0].element.setStyle(oldStyle); + effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); } - }, arguments[1] || {})); -} + }, arguments[1] || { })); +}; Effect.Shake = function(element) { element = $(element); + var options = Object.extend({ + distance: 20, + duration: 0.5 + }, arguments[1] || {}); + var distance = parseFloat(options.distance); + var split = parseFloat(options.duration) / 10.0; var oldStyle = { top: element.getStyle('top'), left: element.getStyle('left') }; - return new Effect.Move(element, - { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { + return new Effect.Move(element, + { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { new Effect.Move(effect.element, - { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { new Effect.Move(effect.element, - { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { new Effect.Move(effect.element, - { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { new Effect.Move(effect.element, - { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { new Effect.Move(effect.element, - { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { - effect.element.undoPositioned(); - effect.element.setStyle(oldStyle); + { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { + effect.element.undoPositioned().setStyle(oldStyle); }}) }}) }}) }}) }}) }}); -} +}; Effect.SlideDown = function(element) { - element = $(element); - element.cleanWhitespace(); + element = $(element).cleanWhitespace(); // SlideDown need to have the content of the element wrapped in a container element with fixed height! - var oldInnerBottom = $(element.firstChild).getStyle('bottom'); + var oldInnerBottom = element.down().getStyle('bottom'); var elementDimensions = element.getDimensions(); return new Effect.Scale(element, 100, Object.extend({ scaleContent: false, scaleX: false, - scaleFrom: 0, + scaleFrom: window.opera ? 0 : 1, scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, restoreAfterFinish: true, afterSetup: function(effect) { effect.element.makePositioned(); - effect.element.firstChild.makePositioned(); - if(window.opera) effect.element.setStyle({top: ''}); - effect.element.makeClipping(); - effect.element.setStyle({height: '0px'}); - effect.element.show(); }, + effect.element.down().makePositioned(); + if (window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, afterUpdateInternal: function(effect) { - effect.element.firstChild.setStyle({bottom: + effect.element.down().setStyle({bottom: (effect.dims[0] - effect.element.clientHeight) + 'px' }); }, afterFinishInternal: function(effect) { - effect.element.undoClipping(); - // IE will crash if child is undoPositioned first - if(/MSIE/.test(navigator.userAgent)){ - effect.element.undoPositioned(); - effect.element.firstChild.undoPositioned(); - }else{ - effect.element.firstChild.undoPositioned(); - effect.element.undoPositioned(); - } - effect.element.firstChild.setStyle({bottom: oldInnerBottom}); } - }, arguments[1] || {}) + effect.element.undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } + }, arguments[1] || { }) ); -} - +}; + Effect.SlideUp = function(element) { - element = $(element); - element.cleanWhitespace(); - var oldInnerBottom = $(element.firstChild).getStyle('bottom'); - return new Effect.Scale(element, 0, + element = $(element).cleanWhitespace(); + var oldInnerBottom = element.down().getStyle('bottom'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, window.opera ? 0 : 1, Object.extend({ scaleContent: false, scaleX: false, scaleMode: 'box', scaleFrom: 100, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, restoreAfterFinish: true, - beforeStartInternal: function(effect) { + afterSetup: function(effect) { effect.element.makePositioned(); - effect.element.firstChild.makePositioned(); - if(window.opera) effect.element.setStyle({top: ''}); - effect.element.makeClipping(); - effect.element.show(); }, + effect.element.down().makePositioned(); + if (window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().show(); + }, afterUpdateInternal: function(effect) { - effect.element.firstChild.setStyle({bottom: - (effect.dims[0] - effect.element.clientHeight) + 'px' }); }, + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, afterFinishInternal: function(effect) { - effect.element.hide(); - effect.element.undoClipping(); - effect.element.firstChild.undoPositioned(); - effect.element.undoPositioned(); - effect.element.setStyle({bottom: oldInnerBottom}); } - }, arguments[1] || {}) + effect.element.hide().undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); + } + }, arguments[1] || { }) ); -} +}; // Bug in opera makes the TD containing this element expand for a instance after finish Effect.Squish = function(element) { - return new Effect.Scale(element, window.opera ? 1 : 0, - { restoreAfterFinish: true, - beforeSetup: function(effect) { - effect.element.makeClipping(effect.element); }, - afterFinishInternal: function(effect) { - effect.element.hide(effect.element); - effect.element.undoClipping(effect.element); } + return new Effect.Scale(element, window.opera ? 1 : 0, { + restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } }); -} +}; Effect.Grow = function(element) { element = $(element); @@ -778,7 +754,7 @@ Effect.Grow = function(element) { moveTransition: Effect.Transitions.sinoidal, scaleTransition: Effect.Transitions.sinoidal, opacityTransition: Effect.Transitions.full - }, arguments[1] || {}); + }, arguments[1] || { }); var oldStyle = { top: element.style.top, left: element.style.left, @@ -823,9 +799,7 @@ Effect.Grow = function(element) { y: initialMoveY, duration: 0.01, beforeSetup: function(effect) { - effect.element.hide(); - effect.element.makeClipping(); - effect.element.makePositioned(); + effect.element.hide().makeClipping().makePositioned(); }, afterFinishInternal: function(effect) { new Effect.Parallel( @@ -836,19 +810,16 @@ Effect.Grow = function(element) { sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) ], Object.extend({ beforeSetup: function(effect) { - effect.effects[0].element.setStyle({height: '0px'}); - effect.effects[0].element.show(); + effect.effects[0].element.setStyle({height: '0px'}).show(); }, afterFinishInternal: function(effect) { - effect.effects[0].element.undoClipping(); - effect.effects[0].element.undoPositioned(); - effect.effects[0].element.setStyle(oldStyle); + effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); } }, options) ) } }); -} +}; Effect.Shrink = function(element) { element = $(element); @@ -857,7 +828,7 @@ Effect.Shrink = function(element) { moveTransition: Effect.Transitions.sinoidal, scaleTransition: Effect.Transitions.sinoidal, opacityTransition: Effect.Transitions.none - }, arguments[1] || {}); + }, arguments[1] || { }); var oldStyle = { top: element.style.top, left: element.style.left, @@ -896,29 +867,26 @@ Effect.Shrink = function(element) { new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) ], Object.extend({ beforeStartInternal: function(effect) { - effect.effects[0].element.makePositioned(); - effect.effects[0].element.makeClipping(); }, + effect.effects[0].element.makePositioned().makeClipping(); + }, afterFinishInternal: function(effect) { - effect.effects[0].element.hide(); - effect.effects[0].element.undoClipping(); - effect.effects[0].element.undoPositioned(); - effect.effects[0].element.setStyle(oldStyle); } + effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } }, options) ); -} +}; Effect.Pulsate = function(element) { element = $(element); - var options = arguments[1] || {}; + var options = arguments[1] || { }; var oldOpacity = element.getInlineOpacity(); var transition = options.transition || Effect.Transitions.sinoidal; - var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) }; + var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; reverser.bind(transition); return new Effect.Opacity(element, - Object.extend(Object.extend({ duration: 3.0, from: 0, + Object.extend(Object.extend({ duration: 2.0, from: 0, afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } }, options), {transition: reverser})); -} +}; Effect.Fold = function(element) { element = $(element); @@ -927,7 +895,7 @@ Effect.Fold = function(element) { left: element.style.left, width: element.style.width, height: element.style.height }; - Element.makeClipping(element); + element.makeClipping(); return new Effect.Scale(element, 5, Object.extend({ scaleContent: false, scaleX: false, @@ -936,23 +904,217 @@ Effect.Fold = function(element) { scaleContent: false, scaleY: false, afterFinishInternal: function(effect) { - effect.element.hide(); - effect.element.undoClipping(); - effect.element.setStyle(oldStyle); + effect.element.hide().undoClipping().setStyle(oldStyle); } }); - }}, arguments[1] || {})); + }}, arguments[1] || { })); }; -['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom', - 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( - function(f) { Element.Methods[f] = Element[f]; } -); +Effect.Morph = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + style: { } + }, arguments[1] || { }); + + if (!Object.isString(options.style)) this.style = $H(options.style); + else { + if (options.style.include(':')) + this.style = options.style.parseStyle(); + else { + this.element.addClassName(options.style); + this.style = $H(this.element.getStyles()); + this.element.removeClassName(options.style); + var css = this.element.getStyles(); + this.style = this.style.reject(function(style) { + return style.value == css[style.key]; + }); + options.afterFinishInternal = function(effect) { + effect.element.addClassName(effect.options.style); + effect.transforms.each(function(transform) { + effect.element.style[transform.style] = ''; + }); + } + } + } + this.start(options); + }, + + setup: function(){ + function parseColor(color){ + if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; + color = color.parseColor(); + return $R(0,2).map(function(i){ + return parseInt( color.slice(i*2+1,i*2+3), 16 ) + }); + } + this.transforms = this.style.map(function(pair){ + var property = pair[0], value = pair[1], unit = null; + + if (value.parseColor('#zzzzzz') != '#zzzzzz') { + value = value.parseColor(); + unit = 'color'; + } else if (property == 'opacity') { + value = parseFloat(value); + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + } else if (Element.CSS_LENGTH.test(value)) { + var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); + value = parseFloat(components[1]); + unit = (components.length == 3) ? components[2] : null; + } + + var originalValue = this.element.getStyle(property); + return { + style: property.camelize(), + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), + targetValue: unit=='color' ? parseColor(value) : value, + unit: unit + }; + }.bind(this)).reject(function(transform){ + return ( + (transform.originalValue == transform.targetValue) || + ( + transform.unit != 'color' && + (isNaN(transform.originalValue) || isNaN(transform.targetValue)) + ) + ) + }); + }, + update: function(position) { + var style = { }, transform, i = this.transforms.length; + while(i--) + style[(transform = this.transforms[i]).style] = + transform.unit=='color' ? '#'+ + (Math.round(transform.originalValue[0]+ + (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + + (Math.round(transform.originalValue[1]+ + (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + + (Math.round(transform.originalValue[2]+ + (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : + (transform.originalValue + + (transform.targetValue - transform.originalValue) * position).toFixed(3) + + (transform.unit === null ? '' : transform.unit); + this.element.setStyle(style, true); + } +}); + +Effect.Transform = Class.create({ + initialize: function(tracks){ + this.tracks = []; + this.options = arguments[1] || { }; + this.addTracks(tracks); + }, + addTracks: function(tracks){ + tracks.each(function(track){ + track = $H(track); + var data = track.values().first(); + this.tracks.push($H({ + ids: track.keys().first(), + effect: Effect.Morph, + options: { style: data } + })); + }.bind(this)); + return this; + }, + play: function(){ + return new Effect.Parallel( + this.tracks.map(function(track){ + var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); + var elements = [$(ids) || $$(ids)].flatten(); + return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) }); + }).flatten(), + this.options + ); + } +}); + +Element.CSS_PROPERTIES = $w( + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + + 'fontSize fontWeight height left letterSpacing lineHeight ' + + 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ + 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + + 'right textIndent top width wordSpacing zIndex'); + +Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; + +String.__parseStyleElement = document.createElement('div'); +String.prototype.parseStyle = function(){ + var style, styleRules = $H(); + if (Prototype.Browser.WebKit) + style = new Element('div',{style:this}).style; + else { + String.__parseStyleElement.innerHTML = '
'; + style = String.__parseStyleElement.childNodes[0].style; + } + + Element.CSS_PROPERTIES.each(function(property){ + if (style[property]) styleRules.set(property, style[property]); + }); + + if (Prototype.Browser.IE && this.include('opacity')) + styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); -Element.Methods.visualEffect = function(element, effect, options) { - s = effect.gsub(/_/, '-').camelize(); - effect_class = s.charAt(0).toUpperCase() + s.substring(1); - new Effect[effect_class](element, options); - return $(element); + return styleRules; }; -Element.addMethods(); \ No newline at end of file +if (document.defaultView && document.defaultView.getComputedStyle) { + Element.getStyles = function(element) { + var css = document.defaultView.getComputedStyle($(element), null); + return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { + styles[property] = css[property]; + return styles; + }); + }; +} else { + Element.getStyles = function(element) { + element = $(element); + var css = element.currentStyle, styles; + styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) { + hash.set(property, css[property]); + return hash; + }); + if (!styles.opacity) styles.set('opacity', element.getOpacity()); + return styles; + }; +}; + +Effect.Methods = { + morph: function(element, style) { + element = $(element); + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); + return element; + }, + visualEffect: function(element, effect, options) { + element = $(element) + var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[klass](element, options); + return element; + }, + highlight: function(element, options) { + element = $(element); + new Effect.Highlight(element, options); + return element; + } +}; + +$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ + 'pulsate shake puff squish switchOff dropOut').each( + function(effect) { + Effect.Methods[effect] = function(element, options){ + element = $(element); + Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); + return element; + } + } +); + +$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( + function(f) { Effect.Methods[f] = Element[f]; } +); + +Element.addMethods(Effect.Methods);