michael@0: /*** michael@0: michael@0: MochiKit.Visual 1.4 michael@0: michael@0: See for documentation, downloads, license, etc. michael@0: michael@0: (c) 2005 Bob Ippolito and others. All rights Reserved. michael@0: michael@0: ***/ michael@0: michael@0: if (typeof(dojo) != 'undefined') { michael@0: dojo.provide('MochiKit.Visual'); michael@0: dojo.require('MochiKit.Base'); michael@0: dojo.require('MochiKit.DOM'); michael@0: dojo.require('MochiKit.Style'); michael@0: dojo.require('MochiKit.Color'); michael@0: } michael@0: michael@0: if (typeof(JSAN) != 'undefined') { michael@0: JSAN.use("MochiKit.Base", []); michael@0: JSAN.use("MochiKit.DOM", []); michael@0: JSAN.use("MochiKit.Style", []); michael@0: JSAN.use("MochiKit.Color", []); michael@0: } michael@0: michael@0: try { michael@0: if (typeof(MochiKit.Base) === 'undefined' || michael@0: typeof(MochiKit.DOM) === 'undefined' || michael@0: typeof(MochiKit.Style) === 'undefined' || michael@0: typeof(MochiKit.Color) === 'undefined') { michael@0: throw ""; michael@0: } michael@0: } catch (e) { michael@0: throw "MochiKit.Visual depends on MochiKit.Base, MochiKit.DOM, MochiKit.Style and MochiKit.Color!"; michael@0: } michael@0: michael@0: if (typeof(MochiKit.Visual) == "undefined") { michael@0: MochiKit.Visual = {}; michael@0: } michael@0: michael@0: MochiKit.Visual.NAME = "MochiKit.Visual"; michael@0: MochiKit.Visual.VERSION = "1.4"; michael@0: michael@0: MochiKit.Visual.__repr__ = function () { michael@0: return "[" + this.NAME + " " + this.VERSION + "]"; michael@0: }; michael@0: michael@0: MochiKit.Visual.toString = function () { michael@0: return this.__repr__(); michael@0: }; michael@0: michael@0: MochiKit.Visual._RoundCorners = function (e, options) { michael@0: e = MochiKit.DOM.getElement(e); michael@0: this._setOptions(options); michael@0: if (this.options.__unstable__wrapElement) { michael@0: e = this._doWrap(e); michael@0: } michael@0: michael@0: var color = this.options.color; michael@0: var C = MochiKit.Color.Color; michael@0: if (this.options.color === "fromElement") { michael@0: color = C.fromBackground(e); michael@0: } else if (!(color instanceof C)) { michael@0: color = C.fromString(color); michael@0: } michael@0: this.isTransparent = (color.asRGB().a <= 0); michael@0: michael@0: var bgColor = this.options.bgColor; michael@0: if (this.options.bgColor === "fromParent") { michael@0: bgColor = C.fromBackground(e.offsetParent); michael@0: } else if (!(bgColor instanceof C)) { michael@0: bgColor = C.fromString(bgColor); michael@0: } michael@0: michael@0: this._roundCornersImpl(e, color, bgColor); michael@0: }; michael@0: michael@0: MochiKit.Visual._RoundCorners.prototype = { michael@0: _doWrap: function (e) { michael@0: var parent = e.parentNode; michael@0: var doc = MochiKit.DOM.currentDocument(); michael@0: if (typeof(doc.defaultView) === "undefined" michael@0: || doc.defaultView === null) { michael@0: return e; michael@0: } michael@0: var style = doc.defaultView.getComputedStyle(e, null); michael@0: if (typeof(style) === "undefined" || style === null) { michael@0: return e; michael@0: } michael@0: var wrapper = MochiKit.DOM.DIV({"style": { michael@0: display: "block", michael@0: // convert padding to margin michael@0: marginTop: style.getPropertyValue("padding-top"), michael@0: marginRight: style.getPropertyValue("padding-right"), michael@0: marginBottom: style.getPropertyValue("padding-bottom"), michael@0: marginLeft: style.getPropertyValue("padding-left"), michael@0: // remove padding so the rounding looks right michael@0: padding: "0px" michael@0: /* michael@0: paddingRight: "0px", michael@0: paddingLeft: "0px" michael@0: */ michael@0: }}); michael@0: wrapper.innerHTML = e.innerHTML; michael@0: e.innerHTML = ""; michael@0: e.appendChild(wrapper); michael@0: return e; michael@0: }, michael@0: michael@0: _roundCornersImpl: function (e, color, bgColor) { michael@0: if (this.options.border) { michael@0: this._renderBorder(e, bgColor); michael@0: } michael@0: if (this._isTopRounded()) { michael@0: this._roundTopCorners(e, color, bgColor); michael@0: } michael@0: if (this._isBottomRounded()) { michael@0: this._roundBottomCorners(e, color, bgColor); michael@0: } michael@0: }, michael@0: michael@0: _renderBorder: function (el, bgColor) { michael@0: var borderValue = "1px solid " + this._borderColor(bgColor); michael@0: var borderL = "border-left: " + borderValue; michael@0: var borderR = "border-right: " + borderValue; michael@0: var style = "style='" + borderL + ";" + borderR + "'"; michael@0: el.innerHTML = "
" + el.innerHTML + "
"; michael@0: }, michael@0: michael@0: _roundTopCorners: function (el, color, bgColor) { michael@0: var corner = this._createCorner(bgColor); michael@0: for (var i = 0; i < this.options.numSlices; i++) { michael@0: corner.appendChild( michael@0: this._createCornerSlice(color, bgColor, i, "top") michael@0: ); michael@0: } michael@0: el.style.paddingTop = 0; michael@0: el.insertBefore(corner, el.firstChild); michael@0: }, michael@0: michael@0: _roundBottomCorners: function (el, color, bgColor) { michael@0: var corner = this._createCorner(bgColor); michael@0: for (var i = (this.options.numSlices - 1); i >= 0; i--) { michael@0: corner.appendChild( michael@0: this._createCornerSlice(color, bgColor, i, "bottom") michael@0: ); michael@0: } michael@0: el.style.paddingBottom = 0; michael@0: el.appendChild(corner); michael@0: }, michael@0: michael@0: _createCorner: function (bgColor) { michael@0: var dom = MochiKit.DOM; michael@0: return dom.DIV({style: {backgroundColor: bgColor.toString()}}); michael@0: }, michael@0: michael@0: _createCornerSlice: function (color, bgColor, n, position) { michael@0: var slice = MochiKit.DOM.SPAN(); michael@0: michael@0: var inStyle = slice.style; michael@0: inStyle.backgroundColor = color.toString(); michael@0: inStyle.display = "block"; michael@0: inStyle.height = "1px"; michael@0: inStyle.overflow = "hidden"; michael@0: inStyle.fontSize = "1px"; michael@0: michael@0: var borderColor = this._borderColor(color, bgColor); michael@0: if (this.options.border && n === 0) { michael@0: inStyle.borderTopStyle = "solid"; michael@0: inStyle.borderTopWidth = "1px"; michael@0: inStyle.borderLeftWidth = "0px"; michael@0: inStyle.borderRightWidth = "0px"; michael@0: inStyle.borderBottomWidth = "0px"; michael@0: // assumes css compliant box model michael@0: inStyle.height = "0px"; michael@0: inStyle.borderColor = borderColor.toString(); michael@0: } else if (borderColor) { michael@0: inStyle.borderColor = borderColor.toString(); michael@0: inStyle.borderStyle = "solid"; michael@0: inStyle.borderWidth = "0px 1px"; michael@0: } michael@0: michael@0: if (!this.options.compact && (n == (this.options.numSlices - 1))) { michael@0: inStyle.height = "2px"; michael@0: } michael@0: michael@0: this._setMargin(slice, n, position); michael@0: this._setBorder(slice, n, position); michael@0: michael@0: return slice; michael@0: }, michael@0: michael@0: _setOptions: function (options) { michael@0: this.options = { michael@0: corners: "all", michael@0: color: "fromElement", michael@0: bgColor: "fromParent", michael@0: blend: true, michael@0: border: false, michael@0: compact: false, michael@0: __unstable__wrapElement: false michael@0: }; michael@0: MochiKit.Base.update(this.options, options); michael@0: michael@0: this.options.numSlices = (this.options.compact ? 2 : 4); michael@0: }, michael@0: michael@0: _whichSideTop: function () { michael@0: var corners = this.options.corners; michael@0: if (this._hasString(corners, "all", "top")) { michael@0: return ""; michael@0: } michael@0: michael@0: var has_tl = (corners.indexOf("tl") != -1); michael@0: var has_tr = (corners.indexOf("tr") != -1); michael@0: if (has_tl && has_tr) { michael@0: return ""; michael@0: } michael@0: if (has_tl) { michael@0: return "left"; michael@0: } michael@0: if (has_tr) { michael@0: return "right"; michael@0: } michael@0: return ""; michael@0: }, michael@0: michael@0: _whichSideBottom: function () { michael@0: var corners = this.options.corners; michael@0: if (this._hasString(corners, "all", "bottom")) { michael@0: return ""; michael@0: } michael@0: michael@0: var has_bl = (corners.indexOf('bl') != -1); michael@0: var has_br = (corners.indexOf('br') != -1); michael@0: if (has_bl && has_br) { michael@0: return ""; michael@0: } michael@0: if (has_bl) { michael@0: return "left"; michael@0: } michael@0: if (has_br) { michael@0: return "right"; michael@0: } michael@0: return ""; michael@0: }, michael@0: michael@0: _borderColor: function (color, bgColor) { michael@0: if (color == "transparent") { michael@0: return bgColor; michael@0: } else if (this.options.border) { michael@0: return this.options.border; michael@0: } else if (this.options.blend) { michael@0: return bgColor.blendedColor(color); michael@0: } michael@0: return ""; michael@0: }, michael@0: michael@0: michael@0: _setMargin: function (el, n, corners) { michael@0: var marginSize = this._marginSize(n) + "px"; michael@0: var whichSide = ( michael@0: corners == "top" ? this._whichSideTop() : this._whichSideBottom() michael@0: ); michael@0: var style = el.style; michael@0: michael@0: if (whichSide == "left") { michael@0: style.marginLeft = marginSize; michael@0: style.marginRight = "0px"; michael@0: } else if (whichSide == "right") { michael@0: style.marginRight = marginSize; michael@0: style.marginLeft = "0px"; michael@0: } else { michael@0: style.marginLeft = marginSize; michael@0: style.marginRight = marginSize; michael@0: } michael@0: }, michael@0: michael@0: _setBorder: function (el, n, corners) { michael@0: var borderSize = this._borderSize(n) + "px"; michael@0: var whichSide = ( michael@0: corners == "top" ? this._whichSideTop() : this._whichSideBottom() michael@0: ); michael@0: michael@0: var style = el.style; michael@0: if (whichSide == "left") { michael@0: style.borderLeftWidth = borderSize; michael@0: style.borderRightWidth = "0px"; michael@0: } else if (whichSide == "right") { michael@0: style.borderRightWidth = borderSize; michael@0: style.borderLeftWidth = "0px"; michael@0: } else { michael@0: style.borderLeftWidth = borderSize; michael@0: style.borderRightWidth = borderSize; michael@0: } michael@0: }, michael@0: michael@0: _marginSize: function (n) { michael@0: if (this.isTransparent) { michael@0: return 0; michael@0: } michael@0: michael@0: var o = this.options; michael@0: if (o.compact && o.blend) { michael@0: var smBlendedMarginSizes = [1, 0]; michael@0: return smBlendedMarginSizes[n]; michael@0: } else if (o.compact) { michael@0: var compactMarginSizes = [2, 1]; michael@0: return compactMarginSizes[n]; michael@0: } else if (o.blend) { michael@0: var blendedMarginSizes = [3, 2, 1, 0]; michael@0: return blendedMarginSizes[n]; michael@0: } else { michael@0: var marginSizes = [5, 3, 2, 1]; michael@0: return marginSizes[n]; michael@0: } michael@0: }, michael@0: michael@0: _borderSize: function (n) { michael@0: var o = this.options; michael@0: var borderSizes; michael@0: if (o.compact && (o.blend || this.isTransparent)) { michael@0: return 1; michael@0: } else if (o.compact) { michael@0: borderSizes = [1, 0]; michael@0: } else if (o.blend) { michael@0: borderSizes = [2, 1, 1, 1]; michael@0: } else if (o.border) { michael@0: borderSizes = [0, 2, 0, 0]; michael@0: } else if (this.isTransparent) { michael@0: borderSizes = [5, 3, 2, 1]; michael@0: } else { michael@0: return 0; michael@0: } michael@0: return borderSizes[n]; michael@0: }, michael@0: michael@0: _hasString: function (str) { michael@0: for (var i = 1; i< arguments.length; i++) { michael@0: if (str.indexOf(arguments[i]) != -1) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: }, michael@0: michael@0: _isTopRounded: function () { michael@0: return this._hasString(this.options.corners, michael@0: "all", "top", "tl", "tr" michael@0: ); michael@0: }, michael@0: michael@0: _isBottomRounded: function () { michael@0: return this._hasString(this.options.corners, michael@0: "all", "bottom", "bl", "br" michael@0: ); michael@0: }, michael@0: michael@0: _hasSingleTextChild: function (el) { michael@0: return (el.childNodes.length == 1 && el.childNodes[0].nodeType == 3); michael@0: } michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.roundElement */ michael@0: MochiKit.Visual.roundElement = function (e, options) { michael@0: new MochiKit.Visual._RoundCorners(e, options); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.roundClass */ michael@0: MochiKit.Visual.roundClass = function (tagName, className, options) { michael@0: var elements = MochiKit.DOM.getElementsByTagAndClassName( michael@0: tagName, className michael@0: ); michael@0: for (var i = 0; i < elements.length; i++) { michael@0: MochiKit.Visual.roundElement(elements[i], options); michael@0: } michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.tagifyText */ michael@0: MochiKit.Visual.tagifyText = function (element, /* optional */tagifyStyle) { michael@0: /*** michael@0: michael@0: Change a node text to character in tags. michael@0: michael@0: @param tagifyStyle: the style to apply to character nodes, default to michael@0: 'position: relative'. michael@0: michael@0: ***/ michael@0: var tagifyStyle = tagifyStyle || 'position:relative'; michael@0: if (/MSIE/.test(navigator.userAgent)) { michael@0: tagifyStyle += ';zoom:1'; michael@0: } michael@0: element = MochiKit.DOM.getElement(element); michael@0: var ma = MochiKit.Base.map; michael@0: ma(function (child) { michael@0: if (child.nodeType == 3) { michael@0: ma(function (character) { michael@0: element.insertBefore( michael@0: MochiKit.DOM.SPAN({style: tagifyStyle}, michael@0: character == ' ' ? String.fromCharCode(160) : character), child); michael@0: }, child.nodeValue.split('')); michael@0: MochiKit.DOM.removeElement(child); michael@0: } michael@0: }, element.childNodes); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.forceRerendering */ michael@0: MochiKit.Visual.forceRerendering = function (element) { michael@0: try { michael@0: element = MochiKit.DOM.getElement(element); michael@0: var n = document.createTextNode(' '); michael@0: element.appendChild(n); michael@0: element.removeChild(n); michael@0: } catch(e) { michael@0: } michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.multiple */ michael@0: MochiKit.Visual.multiple = function (elements, effect, /* optional */options) { michael@0: /*** michael@0: michael@0: Launch the same effect subsequently on given elements. michael@0: michael@0: ***/ michael@0: options = MochiKit.Base.update({ michael@0: speed: 0.1, delay: 0.0 michael@0: }, options || {}); michael@0: var masterDelay = options.delay; michael@0: var index = 0; michael@0: MochiKit.Base.map(function (innerelement) { michael@0: options.delay = index * options.speed + masterDelay; michael@0: new effect(innerelement, options); michael@0: index += 1; michael@0: }, elements); michael@0: }; michael@0: michael@0: MochiKit.Visual.PAIRS = { michael@0: 'slide': ['slideDown', 'slideUp'], michael@0: 'blind': ['blindDown', 'blindUp'], michael@0: 'appear': ['appear', 'fade'], michael@0: 'size': ['grow', 'shrink'] michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.toggle */ michael@0: MochiKit.Visual.toggle = function (element, /* optional */effect, /* optional */options) { michael@0: /*** michael@0: michael@0: Toggle an item between two state depending of its visibility, making michael@0: a effect between these states. Default effect is 'appear', can be michael@0: 'slide' or 'blind'. michael@0: michael@0: ***/ michael@0: element = MochiKit.DOM.getElement(element); michael@0: effect = (effect || 'appear').toLowerCase(); michael@0: options = MochiKit.Base.update({ michael@0: queue: {position: 'end', scope: (element.id || 'global'), limit: 1} michael@0: }, options || {}); michael@0: var v = MochiKit.Visual; michael@0: v[element.style.display != 'none' ? michael@0: v.PAIRS[effect][1] : v.PAIRS[effect][0]](element, options); michael@0: }; michael@0: michael@0: /*** michael@0: michael@0: Transitions: define functions calculating variations depending of a position. michael@0: michael@0: ***/ michael@0: michael@0: MochiKit.Visual.Transitions = {} michael@0: michael@0: /** @id MochiKit.Visual.Transitions.linear */ michael@0: MochiKit.Visual.Transitions.linear = function (pos) { michael@0: return pos; michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.Transitions.sinoidal */ michael@0: MochiKit.Visual.Transitions.sinoidal = function (pos) { michael@0: return (-Math.cos(pos*Math.PI)/2) + 0.5; michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.Transitions.reverse */ michael@0: MochiKit.Visual.Transitions.reverse = function (pos) { michael@0: return 1 - pos; michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.Transitions.flicker */ michael@0: MochiKit.Visual.Transitions.flicker = function (pos) { michael@0: return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.Transitions.wobble */ michael@0: MochiKit.Visual.Transitions.wobble = function (pos) { michael@0: return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.Transitions.pulse */ michael@0: MochiKit.Visual.Transitions.pulse = function (pos) { michael@0: return (Math.floor(pos*10) % 2 == 0 ? michael@0: (pos*10 - Math.floor(pos*10)) : 1 - (pos*10 - Math.floor(pos*10))); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.Transitions.none */ michael@0: MochiKit.Visual.Transitions.none = function (pos) { michael@0: return 0; michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.Transitions.full */ michael@0: MochiKit.Visual.Transitions.full = function (pos) { michael@0: return 1; michael@0: }; michael@0: michael@0: /*** michael@0: michael@0: Core effects michael@0: michael@0: ***/ michael@0: michael@0: MochiKit.Visual.ScopedQueue = function () { michael@0: this.__init__(); michael@0: }; michael@0: michael@0: MochiKit.Base.update(MochiKit.Visual.ScopedQueue.prototype, { michael@0: __init__: function () { michael@0: this.effects = []; michael@0: this.interval = null; michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.ScopedQueue.prototype.add */ michael@0: add: function (effect) { michael@0: var timestamp = new Date().getTime(); michael@0: michael@0: var position = (typeof(effect.options.queue) == 'string') ? michael@0: effect.options.queue : effect.options.queue.position; michael@0: michael@0: var ma = MochiKit.Base.map; michael@0: switch (position) { michael@0: case 'front': michael@0: // move unstarted effects after this effect michael@0: ma(function (e) { michael@0: if (e.state == 'idle') { michael@0: e.startOn += effect.finishOn; michael@0: e.finishOn += effect.finishOn; michael@0: } michael@0: }, this.effects); michael@0: break; michael@0: case 'end': michael@0: var finish; michael@0: // start effect after last queued effect has finished michael@0: ma(function (e) { michael@0: var i = e.finishOn; michael@0: if (i >= (finish || i)) { michael@0: finish = i; michael@0: } michael@0: }, this.effects); michael@0: timestamp = finish || timestamp; michael@0: break; michael@0: case 'break': michael@0: ma(function (e) { michael@0: e.finalize(); michael@0: }, this.effects); michael@0: break; michael@0: } michael@0: michael@0: effect.startOn += timestamp; michael@0: effect.finishOn += timestamp; michael@0: if (!effect.options.queue.limit || michael@0: this.effects.length < effect.options.queue.limit) { michael@0: this.effects.push(effect); michael@0: } michael@0: michael@0: if (!this.interval) { michael@0: this.interval = this.startLoop(MochiKit.Base.bind(this.loop, this), michael@0: 40); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.ScopedQueue.prototype.startLoop */ michael@0: startLoop: function (func, interval) { michael@0: return setInterval(func, interval) michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.ScopedQueue.prototype.remove */ michael@0: remove: function (effect) { michael@0: this.effects = MochiKit.Base.filter(function (e) { michael@0: return e != effect; michael@0: }, this.effects); michael@0: if (this.effects.length == 0) { michael@0: this.stopLoop(this.interval); michael@0: this.interval = null; michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.ScopedQueue.prototype.stopLoop */ michael@0: stopLoop: function (interval) { michael@0: clearInterval(interval) michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.ScopedQueue.prototype.loop */ michael@0: loop: function () { michael@0: var timePos = new Date().getTime(); michael@0: MochiKit.Base.map(function (effect) { michael@0: effect.loop(timePos); michael@0: }, this.effects); michael@0: } michael@0: }); michael@0: michael@0: MochiKit.Visual.Queues = { michael@0: instances: {}, michael@0: michael@0: get: function (queueName) { michael@0: if (typeof(queueName) != 'string') { michael@0: return queueName; michael@0: } michael@0: michael@0: if (!this.instances[queueName]) { michael@0: this.instances[queueName] = new MochiKit.Visual.ScopedQueue(); michael@0: } michael@0: return this.instances[queueName]; michael@0: } michael@0: }; michael@0: michael@0: MochiKit.Visual.Queue = MochiKit.Visual.Queues.get('global'); michael@0: michael@0: MochiKit.Visual.DefaultOptions = { michael@0: transition: MochiKit.Visual.Transitions.sinoidal, michael@0: duration: 1.0, // seconds michael@0: fps: 25.0, // max. 25fps due to MochiKit.Visual.Queue implementation michael@0: sync: false, // true for combining michael@0: from: 0.0, michael@0: to: 1.0, michael@0: delay: 0.0, michael@0: queue: 'parallel' michael@0: }; michael@0: michael@0: MochiKit.Visual.Base = function () {}; michael@0: michael@0: MochiKit.Visual.Base.prototype = { michael@0: /*** michael@0: michael@0: Basic class for all Effects. Define a looping mechanism called for each step michael@0: of an effect. Don't instantiate it, only subclass it. michael@0: michael@0: ***/ michael@0: michael@0: __class__ : MochiKit.Visual.Base, michael@0: michael@0: /** @id MochiKit.Visual.Base.prototype.start */ michael@0: start: function (options) { michael@0: var v = MochiKit.Visual; michael@0: this.options = MochiKit.Base.setdefault(options || {}, michael@0: v.DefaultOptions); michael@0: this.currentFrame = 0; michael@0: this.state = 'idle'; michael@0: this.startOn = this.options.delay*1000; michael@0: this.finishOn = this.startOn + (this.options.duration*1000); michael@0: this.event('beforeStart'); michael@0: if (!this.options.sync) { michael@0: v.Queues.get(typeof(this.options.queue) == 'string' ? michael@0: 'global' : this.options.queue.scope).add(this); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Base.prototype.loop */ michael@0: loop: function (timePos) { michael@0: if (timePos >= this.startOn) { michael@0: if (timePos >= this.finishOn) { michael@0: return this.finalize(); michael@0: } michael@0: var pos = (timePos - this.startOn) / (this.finishOn - this.startOn); michael@0: var frame = michael@0: Math.round(pos * this.options.fps * this.options.duration); michael@0: if (frame > this.currentFrame) { michael@0: this.render(pos); michael@0: this.currentFrame = frame; michael@0: } michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Base.prototype.render */ michael@0: render: function (pos) { michael@0: if (this.state == 'idle') { michael@0: this.state = 'running'; michael@0: this.event('beforeSetup'); michael@0: this.setup(); michael@0: this.event('afterSetup'); michael@0: } michael@0: if (this.state == 'running') { michael@0: if (this.options.transition) { michael@0: pos = this.options.transition(pos); michael@0: } michael@0: pos *= (this.options.to - this.options.from); michael@0: pos += this.options.from; michael@0: this.event('beforeUpdate'); michael@0: this.update(pos); michael@0: this.event('afterUpdate'); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Base.prototype.cancel */ michael@0: cancel: function () { michael@0: if (!this.options.sync) { michael@0: MochiKit.Visual.Queues.get(typeof(this.options.queue) == 'string' ? michael@0: 'global' : this.options.queue.scope).remove(this); michael@0: } michael@0: this.state = 'finished'; michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Base.prototype.finalize */ michael@0: finalize: function () { michael@0: this.render(1.0); michael@0: this.cancel(); michael@0: this.event('beforeFinish'); michael@0: this.finish(); michael@0: this.event('afterFinish'); michael@0: }, michael@0: michael@0: setup: function () { michael@0: }, michael@0: michael@0: finish: function () { michael@0: }, michael@0: michael@0: update: function (position) { michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Base.prototype.event */ michael@0: event: function (eventName) { michael@0: if (this.options[eventName + 'Internal']) { michael@0: this.options[eventName + 'Internal'](this); michael@0: } michael@0: if (this.options[eventName]) { michael@0: this.options[eventName](this); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Base.prototype.repr */ michael@0: repr: function () { michael@0: return '[' + this.__class__.NAME + ', options:' + michael@0: MochiKit.Base.repr(this.options) + ']'; michael@0: } michael@0: } michael@0: michael@0: /** @id MochiKit.Visual.Parallel */ michael@0: MochiKit.Visual.Parallel = function (effects, options) { michael@0: this.__init__(effects, options); michael@0: }; michael@0: michael@0: MochiKit.Visual.Parallel.prototype = new MochiKit.Visual.Base(); michael@0: michael@0: MochiKit.Base.update(MochiKit.Visual.Parallel.prototype, { michael@0: /*** michael@0: michael@0: Run multiple effects at the same time. michael@0: michael@0: ***/ michael@0: __init__: function (effects, options) { michael@0: this.effects = effects || []; michael@0: this.start(options); michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Parallel.prototype.update */ michael@0: update: function (position) { michael@0: MochiKit.Base.map(function (effect) { michael@0: effect.render(position); michael@0: }, this.effects); michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Parallel.prototype.finish */ michael@0: finish: function () { michael@0: MochiKit.Base.map(function (effect) { michael@0: effect.finalize(); michael@0: }, this.effects); michael@0: } michael@0: }); michael@0: michael@0: /** @id MochiKit.Visual.Opacity */ michael@0: MochiKit.Visual.Opacity = function (element, options) { michael@0: this.__init__(element, options); michael@0: }; michael@0: michael@0: MochiKit.Visual.Opacity.prototype = new MochiKit.Visual.Base(); michael@0: michael@0: MochiKit.Base.update(MochiKit.Visual.Opacity.prototype, { michael@0: /*** michael@0: michael@0: Change the opacity of an element. michael@0: michael@0: @param options: 'from' and 'to' change the starting and ending opacities. michael@0: Must be between 0.0 and 1.0. Default to current opacity and 1.0. michael@0: michael@0: ***/ michael@0: __init__: function (element, /* optional */options) { michael@0: var b = MochiKit.Base; michael@0: var s = MochiKit.Style; michael@0: this.element = MochiKit.DOM.getElement(element); michael@0: // make this work on IE on elements without 'layout' michael@0: if (this.element.currentStyle && michael@0: (!this.element.currentStyle.hasLayout)) { michael@0: s.setStyle(this.element, {zoom: 1}); michael@0: } michael@0: options = b.update({ michael@0: from: s.getOpacity(this.element) || 0.0, michael@0: to: 1.0 michael@0: }, options || {}); michael@0: this.start(options); michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Opacity.prototype.update */ michael@0: update: function (position) { michael@0: MochiKit.Style.setOpacity(this.element, position); michael@0: } michael@0: }); michael@0: michael@0: /** @id MochiKit.Visual.Opacity.prototype.Move */ michael@0: MochiKit.Visual.Move = function (element, options) { michael@0: this.__init__(element, options); michael@0: }; michael@0: michael@0: MochiKit.Visual.Move.prototype = new MochiKit.Visual.Base(); michael@0: michael@0: MochiKit.Base.update(MochiKit.Visual.Move.prototype, { michael@0: /*** michael@0: michael@0: Move an element between its current position to a defined position michael@0: michael@0: @param options: 'x' and 'y' for final positions, default to 0, 0. michael@0: michael@0: ***/ michael@0: __init__: function (element, /* optional */options) { michael@0: this.element = MochiKit.DOM.getElement(element); michael@0: options = MochiKit.Base.update({ michael@0: x: 0, michael@0: y: 0, michael@0: mode: 'relative' michael@0: }, options || {}); michael@0: this.start(options); michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Move.prototype.setup */ michael@0: setup: function () { michael@0: // Bug in Opera: Opera returns the 'real' position of a static element michael@0: // or relative element that does not have top/left explicitly set. michael@0: // ==> Always set top and left for position relative elements in your michael@0: // stylesheets (to 0 if you do not need them) michael@0: MochiKit.DOM.makePositioned(this.element); michael@0: michael@0: var s = this.element.style; michael@0: var originalVisibility = s.visibility; michael@0: var originalDisplay = s.display; michael@0: if (originalDisplay == 'none') { michael@0: s.visibility = 'hidden'; michael@0: s.display = ''; michael@0: } michael@0: michael@0: this.originalLeft = parseFloat(MochiKit.Style.getStyle(this.element, 'left') || '0'); michael@0: this.originalTop = parseFloat(MochiKit.Style.getStyle(this.element, 'top') || '0'); michael@0: michael@0: if (this.options.mode == 'absolute') { michael@0: // absolute movement, so we need to calc deltaX and deltaY michael@0: this.options.x -= this.originalLeft; michael@0: this.options.y -= this.originalTop; michael@0: } michael@0: if (originalDisplay == 'none') { michael@0: s.visibility = originalVisibility; michael@0: s.display = originalDisplay; michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Move.prototype.update */ michael@0: update: function (position) { michael@0: MochiKit.Style.setStyle(this.element, { michael@0: left: Math.round(this.options.x * position + this.originalLeft) + 'px', michael@0: top: Math.round(this.options.y * position + this.originalTop) + 'px' michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: /** @id MochiKit.Visual.Scale */ michael@0: MochiKit.Visual.Scale = function (element, percent, options) { michael@0: this.__init__(element, percent, options); michael@0: }; michael@0: michael@0: MochiKit.Visual.Scale.prototype = new MochiKit.Visual.Base(); michael@0: michael@0: MochiKit.Base.update(MochiKit.Visual.Scale.prototype, { michael@0: /*** michael@0: michael@0: Change the size of an element. michael@0: michael@0: @param percent: final_size = percent*original_size michael@0: michael@0: @param options: several options changing scale behaviour michael@0: michael@0: ***/ michael@0: __init__: function (element, percent, /* optional */options) { michael@0: this.element = MochiKit.DOM.getElement(element) michael@0: options = MochiKit.Base.update({ michael@0: scaleX: true, michael@0: scaleY: true, michael@0: scaleContent: true, michael@0: scaleFromCenter: false, michael@0: scaleMode: 'box', // 'box' or 'contents' or {} with provided values michael@0: scaleFrom: 100.0, michael@0: scaleTo: percent michael@0: }, options || {}); michael@0: this.start(options); michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Scale.prototype.setup */ michael@0: setup: function () { michael@0: this.restoreAfterFinish = this.options.restoreAfterFinish || false; michael@0: this.elementPositioning = MochiKit.Style.getStyle(this.element, michael@0: 'position'); michael@0: michael@0: var ma = MochiKit.Base.map; michael@0: var b = MochiKit.Base.bind; michael@0: this.originalStyle = {}; michael@0: ma(b(function (k) { michael@0: this.originalStyle[k] = this.element.style[k]; michael@0: }, this), ['top', 'left', 'width', 'height', 'fontSize']); michael@0: michael@0: this.originalTop = this.element.offsetTop; michael@0: this.originalLeft = this.element.offsetLeft; michael@0: michael@0: var fontSize = MochiKit.Style.getStyle(this.element, michael@0: 'font-size') || '100%'; michael@0: ma(b(function (fontSizeType) { michael@0: if (fontSize.indexOf(fontSizeType) > 0) { michael@0: this.fontSize = parseFloat(fontSize); michael@0: this.fontSizeType = fontSizeType; michael@0: } michael@0: }, this), ['em', 'px', '%']); michael@0: michael@0: this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; michael@0: michael@0: if (/^content/.test(this.options.scaleMode)) { michael@0: this.dims = [this.element.scrollHeight, this.element.scrollWidth]; michael@0: } else if (this.options.scaleMode == 'box') { michael@0: this.dims = [this.element.offsetHeight, this.element.offsetWidth]; michael@0: } else { michael@0: this.dims = [this.options.scaleMode.originalHeight, michael@0: this.options.scaleMode.originalWidth]; michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Scale.prototype.update */ michael@0: update: function (position) { michael@0: var currentScale = (this.options.scaleFrom/100.0) + michael@0: (this.factor * position); michael@0: if (this.options.scaleContent && this.fontSize) { michael@0: MochiKit.Style.setStyle(this.element, { michael@0: fontSize: this.fontSize * currentScale + this.fontSizeType michael@0: }); michael@0: } michael@0: this.setDimensions(this.dims[0] * currentScale, michael@0: this.dims[1] * currentScale); michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Scale.prototype.finish */ michael@0: finish: function () { michael@0: if (this.restoreAfterFinish) { michael@0: MochiKit.Style.setStyle(this.element, this.originalStyle); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Scale.prototype.setDimensions */ michael@0: setDimensions: function (height, width) { michael@0: var d = {}; michael@0: var r = Math.round; michael@0: if (/MSIE/.test(navigator.userAgent)) { michael@0: r = Math.ceil; michael@0: } michael@0: if (this.options.scaleX) { michael@0: d.width = r(width) + 'px'; michael@0: } michael@0: if (this.options.scaleY) { michael@0: d.height = r(height) + 'px'; michael@0: } michael@0: if (this.options.scaleFromCenter) { michael@0: var topd = (height - this.dims[0])/2; michael@0: var leftd = (width - this.dims[1])/2; michael@0: if (this.elementPositioning == 'absolute') { michael@0: if (this.options.scaleY) { michael@0: d.top = this.originalTop - topd + 'px'; michael@0: } michael@0: if (this.options.scaleX) { michael@0: d.left = this.originalLeft - leftd + 'px'; michael@0: } michael@0: } else { michael@0: if (this.options.scaleY) { michael@0: d.top = -topd + 'px'; michael@0: } michael@0: if (this.options.scaleX) { michael@0: d.left = -leftd + 'px'; michael@0: } michael@0: } michael@0: } michael@0: MochiKit.Style.setStyle(this.element, d); michael@0: } michael@0: }); michael@0: michael@0: /** @id MochiKit.Visual.Highlight */ michael@0: MochiKit.Visual.Highlight = function (element, options) { michael@0: this.__init__(element, options); michael@0: }; michael@0: michael@0: MochiKit.Visual.Highlight.prototype = new MochiKit.Visual.Base(); michael@0: michael@0: MochiKit.Base.update(MochiKit.Visual.Highlight.prototype, { michael@0: /*** michael@0: michael@0: Highlight an item of the page. michael@0: michael@0: @param options: 'startcolor' for choosing highlighting color, default michael@0: to '#ffff99'. michael@0: michael@0: ***/ michael@0: __init__: function (element, /* optional */options) { michael@0: this.element = MochiKit.DOM.getElement(element); michael@0: options = MochiKit.Base.update({ michael@0: startcolor: '#ffff99' michael@0: }, options || {}); michael@0: this.start(options); michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Highlight.prototype.setup */ michael@0: setup: function () { michael@0: var b = MochiKit.Base; michael@0: var s = MochiKit.Style; michael@0: // Prevent executing on elements not in the layout flow michael@0: if (s.getStyle(this.element, 'display') == 'none') { michael@0: this.cancel(); michael@0: return; michael@0: } michael@0: // Disable background image during the effect michael@0: this.oldStyle = { michael@0: backgroundImage: s.getStyle(this.element, 'background-image') michael@0: }; michael@0: s.setStyle(this.element, { michael@0: backgroundImage: 'none' michael@0: }); michael@0: michael@0: if (!this.options.endcolor) { michael@0: this.options.endcolor = michael@0: MochiKit.Color.Color.fromBackground(this.element).toHexString(); michael@0: } michael@0: if (b.isUndefinedOrNull(this.options.restorecolor)) { michael@0: this.options.restorecolor = s.getStyle(this.element, michael@0: 'background-color'); michael@0: } michael@0: // init color calculations michael@0: this._base = b.map(b.bind(function (i) { michael@0: return parseInt( michael@0: this.options.startcolor.slice(i*2 + 1, i*2 + 3), 16); michael@0: }, this), [0, 1, 2]); michael@0: this._delta = b.map(b.bind(function (i) { michael@0: return parseInt(this.options.endcolor.slice(i*2 + 1, i*2 + 3), 16) michael@0: - this._base[i]; michael@0: }, this), [0, 1, 2]); michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Highlight.prototype.update */ michael@0: update: function (position) { michael@0: var m = '#'; michael@0: MochiKit.Base.map(MochiKit.Base.bind(function (i) { michael@0: m += MochiKit.Color.toColorPart(Math.round(this._base[i] + michael@0: this._delta[i]*position)); michael@0: }, this), [0, 1, 2]); michael@0: MochiKit.Style.setStyle(this.element, { michael@0: backgroundColor: m michael@0: }); michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.Highlight.prototype.finish */ michael@0: finish: function () { michael@0: MochiKit.Style.setStyle(this.element, michael@0: MochiKit.Base.update(this.oldStyle, { michael@0: backgroundColor: this.options.restorecolor michael@0: })); michael@0: } michael@0: }); michael@0: michael@0: /** @id MochiKit.Visual.ScrollTo */ michael@0: MochiKit.Visual.ScrollTo = function (element, options) { michael@0: this.__init__(element, options); michael@0: }; michael@0: michael@0: MochiKit.Visual.ScrollTo.prototype = new MochiKit.Visual.Base(); michael@0: michael@0: MochiKit.Base.update(MochiKit.Visual.ScrollTo.prototype, { michael@0: /*** michael@0: michael@0: Scroll to an element in the page. michael@0: michael@0: ***/ michael@0: __init__: function (element, /* optional */options) { michael@0: this.element = MochiKit.DOM.getElement(element); michael@0: this.start(options || {}); michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.ScrollTo.prototype.setup */ michael@0: setup: function () { michael@0: var p = MochiKit.Position; michael@0: p.prepare(); michael@0: var offsets = p.cumulativeOffset(this.element); michael@0: if (this.options.offset) { michael@0: offsets.y += this.options.offset; michael@0: } michael@0: var max; michael@0: if (window.innerHeight) { michael@0: max = window.innerHeight - window.height; michael@0: } else if (document.documentElement && michael@0: document.documentElement.clientHeight) { michael@0: max = document.documentElement.clientHeight - michael@0: document.body.scrollHeight; michael@0: } else if (document.body) { michael@0: max = document.body.clientHeight - document.body.scrollHeight; michael@0: } michael@0: this.scrollStart = p.windowOffset.y; michael@0: this.delta = (offsets.y > max ? max : offsets.y) - this.scrollStart; michael@0: }, michael@0: michael@0: /** @id MochiKit.Visual.ScrollTo.prototype.update */ michael@0: update: function (position) { michael@0: var p = MochiKit.Position; michael@0: p.prepare(); michael@0: window.scrollTo(p.windowOffset.x, this.scrollStart + (position * this.delta)); michael@0: } michael@0: }); michael@0: michael@0: /*** michael@0: michael@0: Combination effects. michael@0: michael@0: ***/ michael@0: michael@0: /** @id MochiKit.Visual.fade */ michael@0: MochiKit.Visual.fade = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Fade a given element: change its opacity and hide it in the end. michael@0: michael@0: @param options: 'to' and 'from' to change opacity. michael@0: michael@0: ***/ michael@0: var s = MochiKit.Style; michael@0: var oldOpacity = MochiKit.DOM.getElement(element).style.opacity || ''; michael@0: options = MochiKit.Base.update({ michael@0: from: s.getOpacity(element) || 1.0, michael@0: to: 0.0, michael@0: afterFinishInternal: function (effect) { michael@0: if (effect.options.to !== 0) { michael@0: return; michael@0: } michael@0: s.hideElement(effect.element); michael@0: s.setStyle(effect.element, {opacity: oldOpacity}); michael@0: } michael@0: }, options || {}); michael@0: return new MochiKit.Visual.Opacity(element, options); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.appear */ michael@0: MochiKit.Visual.appear = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Make an element appear. michael@0: michael@0: @param options: 'to' and 'from' to change opacity. michael@0: michael@0: ***/ michael@0: var s = MochiKit.Style; michael@0: var v = MochiKit.Visual; michael@0: options = MochiKit.Base.update({ michael@0: from: (s.getStyle(element, 'display') == 'none' ? 0.0 : michael@0: s.getOpacity(element) || 0.0), michael@0: to: 1.0, michael@0: // force Safari to render floated elements properly michael@0: afterFinishInternal: function (effect) { michael@0: v.forceRerendering(effect.element); michael@0: }, michael@0: beforeSetupInternal: function (effect) { michael@0: s.setOpacity(effect.element, effect.options.from); michael@0: s.showElement(effect.element); michael@0: } michael@0: }, options || {}); michael@0: return new v.Opacity(element, options); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.puff */ michael@0: MochiKit.Visual.puff = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: 'Puff' an element: grow it to double size, fading it and make it hidden. michael@0: michael@0: ***/ michael@0: var s = MochiKit.Style; michael@0: var v = MochiKit.Visual; michael@0: element = MochiKit.DOM.getElement(element); michael@0: var oldStyle = { michael@0: opacity: element.style.opacity || '', michael@0: position: s.getStyle(element, 'position'), michael@0: top: element.style.top, michael@0: left: element.style.left, michael@0: width: element.style.width, michael@0: height: element.style.height michael@0: }; michael@0: options = MochiKit.Base.update({ michael@0: beforeSetupInternal: function (effect) { michael@0: MochiKit.Position.absolutize(effect.effects[0].element) michael@0: }, michael@0: afterFinishInternal: function (effect) { michael@0: s.hideElement(effect.effects[0].element); michael@0: s.setStyle(effect.effects[0].element, oldStyle); michael@0: } michael@0: }, options || {}); michael@0: return new v.Parallel( michael@0: [new v.Scale(element, 200, michael@0: {sync: true, scaleFromCenter: true, michael@0: scaleContent: true, restoreAfterFinish: true}), michael@0: new v.Opacity(element, {sync: true, to: 0.0 })], michael@0: options); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.blindUp */ michael@0: MochiKit.Visual.blindUp = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Blind an element up: change its vertical size to 0. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: element = d.getElement(element); michael@0: var elemClip = d.makeClipping(element); michael@0: options = MochiKit.Base.update({ michael@0: scaleContent: false, michael@0: scaleX: false, michael@0: restoreAfterFinish: true, michael@0: afterFinishInternal: function (effect) { michael@0: MochiKit.Style.hideElement(effect.element); michael@0: d.undoClipping(effect.element, elemClip); michael@0: } michael@0: }, options || {}); michael@0: michael@0: return new MochiKit.Visual.Scale(element, 0, options); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.blindDown */ michael@0: MochiKit.Visual.blindDown = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Blind an element down: restore its vertical size. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: var s = MochiKit.Style; michael@0: element = d.getElement(element); michael@0: var elementDimensions = s.getElementDimensions(element); michael@0: var elemClip; michael@0: options = MochiKit.Base.update({ michael@0: scaleContent: false, michael@0: scaleX: false, michael@0: scaleFrom: 0, michael@0: scaleMode: {originalHeight: elementDimensions.h, michael@0: originalWidth: elementDimensions.w}, michael@0: restoreAfterFinish: true, michael@0: afterSetupInternal: function (effect) { michael@0: elemClip = d.makeClipping(effect.element); michael@0: s.setStyle(effect.element, {height: '0px'}); michael@0: s.showElement(effect.element); michael@0: }, michael@0: afterFinishInternal: function (effect) { michael@0: d.undoClipping(effect.element, elemClip); michael@0: } michael@0: }, options || {}); michael@0: return new MochiKit.Visual.Scale(element, 100, options); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.switchOff */ michael@0: MochiKit.Visual.switchOff = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Apply a switch-off-like effect. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: element = d.getElement(element); michael@0: var oldOpacity = element.style.opacity || ''; michael@0: var elemClip; michael@0: var options = MochiKit.Base.update({ michael@0: duration: 0.3, michael@0: scaleFromCenter: true, michael@0: scaleX: false, michael@0: scaleContent: false, michael@0: restoreAfterFinish: true, michael@0: beforeSetupInternal: function (effect) { michael@0: d.makePositioned(effect.element); michael@0: elemClip = d.makeClipping(effect.element); michael@0: }, michael@0: afterFinishInternal: function (effect) { michael@0: MochiKit.Style.hideElement(effect.element); michael@0: d.undoClipping(effect.element, elemClip); michael@0: d.undoPositioned(effect.element); michael@0: MochiKit.Style.setStyle(effect.element, {opacity: oldOpacity}); michael@0: } michael@0: }, options || {}); michael@0: var v = MochiKit.Visual; michael@0: return new v.appear(element, { michael@0: duration: 0.4, michael@0: from: 0, michael@0: transition: v.Transitions.flicker, michael@0: afterFinishInternal: function (effect) { michael@0: new v.Scale(effect.element, 1, options) michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.dropOut */ michael@0: MochiKit.Visual.dropOut = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Make an element fall and disappear. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: var s = MochiKit.Style; michael@0: element = d.getElement(element); michael@0: var oldStyle = { michael@0: top: s.getStyle(element, 'top'), michael@0: left: s.getStyle(element, 'left'), michael@0: opacity: element.style.opacity || '' michael@0: }; michael@0: michael@0: options = MochiKit.Base.update({ michael@0: duration: 0.5, michael@0: beforeSetupInternal: function (effect) { michael@0: d.makePositioned(effect.effects[0].element); michael@0: }, michael@0: afterFinishInternal: function (effect) { michael@0: s.hideElement(effect.effects[0].element); michael@0: d.undoPositioned(effect.effects[0].element); michael@0: s.setStyle(effect.effects[0].element, oldStyle); michael@0: } michael@0: }, options || {}); michael@0: var v = MochiKit.Visual; michael@0: return new v.Parallel( michael@0: [new v.Move(element, {x: 0, y: 100, sync: true}), michael@0: new v.Opacity(element, {sync: true, to: 0.0})], michael@0: options); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.shake */ michael@0: MochiKit.Visual.shake = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Move an element from left to right several times. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: var v = MochiKit.Visual; michael@0: var s = MochiKit.Style; michael@0: element = d.getElement(element); michael@0: options = MochiKit.Base.update({ michael@0: x: -20, michael@0: y: 0, michael@0: duration: 0.05, michael@0: afterFinishInternal: function (effect) { michael@0: d.undoPositioned(effect.element); michael@0: s.setStyle(effect.element, oldStyle); michael@0: } michael@0: }, options || {}); michael@0: var oldStyle = { michael@0: top: s.getStyle(element, 'top'), michael@0: left: s.getStyle(element, 'left') }; michael@0: return new v.Move(element, michael@0: {x: 20, y: 0, duration: 0.05, afterFinishInternal: function (effect) { michael@0: new v.Move(effect.element, michael@0: {x: -40, y: 0, duration: 0.1, afterFinishInternal: function (effect) { michael@0: new v.Move(effect.element, michael@0: {x: 40, y: 0, duration: 0.1, afterFinishInternal: function (effect) { michael@0: new v.Move(effect.element, michael@0: {x: -40, y: 0, duration: 0.1, afterFinishInternal: function (effect) { michael@0: new v.Move(effect.element, michael@0: {x: 40, y: 0, duration: 0.1, afterFinishInternal: function (effect) { michael@0: new v.Move(effect.element, options michael@0: ) }}) }}) }}) }}) }}); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.slideDown */ michael@0: MochiKit.Visual.slideDown = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Slide an element down. michael@0: It needs to have the content of the element wrapped in a container michael@0: element with fixed height. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: var b = MochiKit.Base; michael@0: var s = MochiKit.Style; michael@0: element = d.getElement(element); michael@0: if (!element.firstChild) { michael@0: throw "MochiKit.Visual.slideDown must be used on a element with a child"; michael@0: } michael@0: d.removeEmptyTextNodes(element); michael@0: var oldInnerBottom = s.getStyle(element.firstChild, 'bottom') || 0; michael@0: var elementDimensions = s.getElementDimensions(element); michael@0: var elemClip; michael@0: options = b.update({ michael@0: scaleContent: false, michael@0: scaleX: false, michael@0: scaleFrom: 0, michael@0: scaleMode: {originalHeight: elementDimensions.h, michael@0: originalWidth: elementDimensions.w}, michael@0: restoreAfterFinish: true, michael@0: afterSetupInternal: function (effect) { michael@0: d.makePositioned(effect.element); michael@0: d.makePositioned(effect.element.firstChild); michael@0: if (/Opera/.test(navigator.userAgent)) { michael@0: s.setStyle(effect.element, {top: ''}); michael@0: } michael@0: elemClip = d.makeClipping(effect.element); michael@0: s.setStyle(effect.element, {height: '0px'}); michael@0: s.showElement(effect.element); michael@0: }, michael@0: afterUpdateInternal: function (effect) { michael@0: s.setStyle(effect.element.firstChild, michael@0: {bottom: (effect.dims[0] - effect.element.clientHeight) + 'px'}) michael@0: }, michael@0: afterFinishInternal: function (effect) { michael@0: d.undoClipping(effect.element, elemClip); michael@0: // IE will crash if child is undoPositioned first michael@0: if (/MSIE/.test(navigator.userAgent)) { michael@0: d.undoPositioned(effect.element); michael@0: d.undoPositioned(effect.element.firstChild); michael@0: } else { michael@0: d.undoPositioned(effect.element.firstChild); michael@0: d.undoPositioned(effect.element); michael@0: } michael@0: s.setStyle(effect.element.firstChild, michael@0: {bottom: oldInnerBottom}); michael@0: } michael@0: }, options || {}); michael@0: michael@0: return new MochiKit.Visual.Scale(element, 100, options); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.slideUp */ michael@0: MochiKit.Visual.slideUp = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Slide an element up. michael@0: It needs to have the content of the element wrapped in a container michael@0: element with fixed height. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: var b = MochiKit.Base; michael@0: var s = MochiKit.Style; michael@0: element = d.getElement(element); michael@0: if (!element.firstChild) { michael@0: throw "MochiKit.Visual.slideUp must be used on a element with a child"; michael@0: } michael@0: d.removeEmptyTextNodes(element); michael@0: var oldInnerBottom = s.getStyle(element.firstChild, 'bottom'); michael@0: var elemClip; michael@0: options = b.update({ michael@0: scaleContent: false, michael@0: scaleX: false, michael@0: scaleMode: 'box', michael@0: scaleFrom: 100, michael@0: restoreAfterFinish: true, michael@0: beforeStartInternal: function (effect) { michael@0: d.makePositioned(effect.element); michael@0: d.makePositioned(effect.element.firstChild); michael@0: if (/Opera/.test(navigator.userAgent)) { michael@0: s.setStyle(effect.element, {top: ''}); michael@0: } michael@0: elemClip = d.makeClipping(effect.element); michael@0: s.showElement(effect.element); michael@0: }, michael@0: afterUpdateInternal: function (effect) { michael@0: s.setStyle(effect.element.firstChild, michael@0: {bottom: (effect.dims[0] - effect.element.clientHeight) + 'px'}); michael@0: }, michael@0: afterFinishInternal: function (effect) { michael@0: s.hideElement(effect.element); michael@0: d.undoClipping(effect.element, elemClip); michael@0: d.undoPositioned(effect.element.firstChild); michael@0: d.undoPositioned(effect.element); michael@0: s.setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); michael@0: } michael@0: }, options || {}); michael@0: return new MochiKit.Visual.Scale(element, 0, options); michael@0: }; michael@0: michael@0: // Bug in opera makes the TD containing this element expand for a instance michael@0: // after finish michael@0: /** @id MochiKit.Visual.squish */ michael@0: MochiKit.Visual.squish = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Reduce an element and make it disappear. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: var b = MochiKit.Base; michael@0: var elemClip; michael@0: options = b.update({ michael@0: restoreAfterFinish: true, michael@0: beforeSetupInternal: function (effect) { michael@0: elemClip = d.makeClipping(effect.element); michael@0: }, michael@0: afterFinishInternal: function (effect) { michael@0: MochiKit.Style.hideElement(effect.element); michael@0: d.undoClipping(effect.element, elemClip); michael@0: } michael@0: }, options || {}); michael@0: michael@0: return new MochiKit.Visual.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, options); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.grow */ michael@0: MochiKit.Visual.grow = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Grow an element to its original size. Make it zero-sized before michael@0: if necessary. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: var v = MochiKit.Visual; michael@0: var s = MochiKit.Style; michael@0: element = d.getElement(element); michael@0: options = MochiKit.Base.update({ michael@0: direction: 'center', michael@0: moveTransition: v.Transitions.sinoidal, michael@0: scaleTransition: v.Transitions.sinoidal, michael@0: opacityTransition: v.Transitions.full michael@0: }, options || {}); michael@0: var oldStyle = { michael@0: top: element.style.top, michael@0: left: element.style.left, michael@0: height: element.style.height, michael@0: width: element.style.width, michael@0: opacity: element.style.opacity || '' michael@0: }; michael@0: michael@0: var dims = s.getElementDimensions(element); michael@0: var initialMoveX, initialMoveY; michael@0: var moveX, moveY; michael@0: michael@0: switch (options.direction) { michael@0: case 'top-left': michael@0: initialMoveX = initialMoveY = moveX = moveY = 0; michael@0: break; michael@0: case 'top-right': michael@0: initialMoveX = dims.w; michael@0: initialMoveY = moveY = 0; michael@0: moveX = -dims.w; michael@0: break; michael@0: case 'bottom-left': michael@0: initialMoveX = moveX = 0; michael@0: initialMoveY = dims.h; michael@0: moveY = -dims.h; michael@0: break; michael@0: case 'bottom-right': michael@0: initialMoveX = dims.w; michael@0: initialMoveY = dims.h; michael@0: moveX = -dims.w; michael@0: moveY = -dims.h; michael@0: break; michael@0: case 'center': michael@0: initialMoveX = dims.w / 2; michael@0: initialMoveY = dims.h / 2; michael@0: moveX = -dims.w / 2; michael@0: moveY = -dims.h / 2; michael@0: break; michael@0: } michael@0: michael@0: var optionsParallel = MochiKit.Base.update({ michael@0: beforeSetupInternal: function (effect) { michael@0: s.setStyle(effect.effects[0].element, {height: '0px'}); michael@0: s.showElement(effect.effects[0].element); michael@0: }, michael@0: afterFinishInternal: function (effect) { michael@0: d.undoClipping(effect.effects[0].element); michael@0: d.undoPositioned(effect.effects[0].element); michael@0: s.setStyle(effect.effects[0].element, oldStyle); michael@0: } michael@0: }, options || {}); michael@0: michael@0: return new v.Move(element, { michael@0: x: initialMoveX, michael@0: y: initialMoveY, michael@0: duration: 0.01, michael@0: beforeSetupInternal: function (effect) { michael@0: s.hideElement(effect.element); michael@0: d.makeClipping(effect.element); michael@0: d.makePositioned(effect.element); michael@0: }, michael@0: afterFinishInternal: function (effect) { michael@0: new v.Parallel( michael@0: [new v.Opacity(effect.element, { michael@0: sync: true, to: 1.0, from: 0.0, michael@0: transition: options.opacityTransition michael@0: }), michael@0: new v.Move(effect.element, { michael@0: x: moveX, y: moveY, sync: true, michael@0: transition: options.moveTransition michael@0: }), michael@0: new v.Scale(effect.element, 100, { michael@0: scaleMode: {originalHeight: dims.h, michael@0: originalWidth: dims.w}, michael@0: sync: true, michael@0: scaleFrom: /Opera/.test(navigator.userAgent) ? 1 : 0, michael@0: transition: options.scaleTransition, michael@0: restoreAfterFinish: true michael@0: }) michael@0: ], optionsParallel michael@0: ); michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.shrink */ michael@0: MochiKit.Visual.shrink = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Shrink an element and make it disappear. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: var v = MochiKit.Visual; michael@0: var s = MochiKit.Style; michael@0: element = d.getElement(element); michael@0: options = MochiKit.Base.update({ michael@0: direction: 'center', michael@0: moveTransition: v.Transitions.sinoidal, michael@0: scaleTransition: v.Transitions.sinoidal, michael@0: opacityTransition: v.Transitions.none michael@0: }, options || {}); michael@0: var oldStyle = { michael@0: top: element.style.top, michael@0: left: element.style.left, michael@0: height: element.style.height, michael@0: width: element.style.width, michael@0: opacity: element.style.opacity || '' michael@0: }; michael@0: michael@0: var dims = s.getElementDimensions(element); michael@0: var moveX, moveY; michael@0: michael@0: switch (options.direction) { michael@0: case 'top-left': michael@0: moveX = moveY = 0; michael@0: break; michael@0: case 'top-right': michael@0: moveX = dims.w; michael@0: moveY = 0; michael@0: break; michael@0: case 'bottom-left': michael@0: moveX = 0; michael@0: moveY = dims.h; michael@0: break; michael@0: case 'bottom-right': michael@0: moveX = dims.w; michael@0: moveY = dims.h; michael@0: break; michael@0: case 'center': michael@0: moveX = dims.w / 2; michael@0: moveY = dims.h / 2; michael@0: break; michael@0: } michael@0: var elemClip; michael@0: michael@0: var optionsParallel = MochiKit.Base.update({ michael@0: beforeStartInternal: function (effect) { michael@0: elemClip = d.makePositioned(effect.effects[0].element); michael@0: d.makeClipping(effect.effects[0].element); michael@0: }, michael@0: afterFinishInternal: function (effect) { michael@0: s.hideElement(effect.effects[0].element); michael@0: d.undoClipping(effect.effects[0].element, elemClip); michael@0: d.undoPositioned(effect.effects[0].element); michael@0: s.setStyle(effect.effects[0].element, oldStyle); michael@0: } michael@0: }, options || {}); michael@0: michael@0: return new v.Parallel( michael@0: [new v.Opacity(element, { michael@0: sync: true, to: 0.0, from: 1.0, michael@0: transition: options.opacityTransition michael@0: }), michael@0: new v.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, { michael@0: sync: true, transition: options.scaleTransition, michael@0: restoreAfterFinish: true michael@0: }), michael@0: new v.Move(element, { michael@0: x: moveX, y: moveY, sync: true, transition: options.moveTransition michael@0: }) michael@0: ], optionsParallel michael@0: ); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.pulsate */ michael@0: MochiKit.Visual.pulsate = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Pulse an element between appear/fade. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: var v = MochiKit.Visual; michael@0: var b = MochiKit.Base; michael@0: var oldOpacity = d.getElement(element).style.opacity || ''; michael@0: options = b.update({ michael@0: duration: 3.0, michael@0: from: 0, michael@0: afterFinishInternal: function (effect) { michael@0: MochiKit.Style.setStyle(effect.element, {opacity: oldOpacity}); michael@0: } michael@0: }, options || {}); michael@0: var transition = options.transition || v.Transitions.sinoidal; michael@0: var reverser = b.bind(function (pos) { michael@0: return transition(1 - v.Transitions.pulse(pos)); michael@0: }, transition); michael@0: b.bind(reverser, transition); michael@0: return new v.Opacity(element, b.update({ michael@0: transition: reverser}, options)); michael@0: }; michael@0: michael@0: /** @id MochiKit.Visual.fold */ michael@0: MochiKit.Visual.fold = function (element, /* optional */ options) { michael@0: /*** michael@0: michael@0: Fold an element, first vertically, then horizontally. michael@0: michael@0: ***/ michael@0: var d = MochiKit.DOM; michael@0: var v = MochiKit.Visual; michael@0: var s = MochiKit.Style; michael@0: element = d.getElement(element); michael@0: var oldStyle = { michael@0: top: element.style.top, michael@0: left: element.style.left, michael@0: width: element.style.width, michael@0: height: element.style.height michael@0: }; michael@0: var elemClip = d.makeClipping(element); michael@0: options = MochiKit.Base.update({ michael@0: scaleContent: false, michael@0: scaleX: false, michael@0: afterFinishInternal: function (effect) { michael@0: new v.Scale(element, 1, { michael@0: scaleContent: false, michael@0: scaleY: false, michael@0: afterFinishInternal: function (effect) { michael@0: s.hideElement(effect.element); michael@0: d.undoClipping(effect.element, elemClip); michael@0: s.setStyle(effect.element, oldStyle); michael@0: } michael@0: }); michael@0: } michael@0: }, options || {}); michael@0: return new v.Scale(element, 5, options); michael@0: }; michael@0: michael@0: michael@0: // Compatibility with MochiKit 1.0 michael@0: MochiKit.Visual.Color = MochiKit.Color.Color; michael@0: MochiKit.Visual.getElementsComputedStyle = MochiKit.DOM.computedStyle; michael@0: michael@0: /* end of Rico adaptation */ michael@0: michael@0: MochiKit.Visual.__new__ = function () { michael@0: var m = MochiKit.Base; michael@0: michael@0: m.nameFunctions(this); michael@0: michael@0: this.EXPORT_TAGS = { michael@0: ":common": this.EXPORT, michael@0: ":all": m.concat(this.EXPORT, this.EXPORT_OK) michael@0: }; michael@0: michael@0: }; michael@0: michael@0: MochiKit.Visual.EXPORT = [ michael@0: "roundElement", michael@0: "roundClass", michael@0: "tagifyText", michael@0: "multiple", michael@0: "toggle", michael@0: "Base", michael@0: "Parallel", michael@0: "Opacity", michael@0: "Move", michael@0: "Scale", michael@0: "Highlight", michael@0: "ScrollTo", michael@0: "fade", michael@0: "appear", michael@0: "puff", michael@0: "blindUp", michael@0: "blindDown", michael@0: "switchOff", michael@0: "dropOut", michael@0: "shake", michael@0: "slideDown", michael@0: "slideUp", michael@0: "squish", michael@0: "grow", michael@0: "shrink", michael@0: "pulsate", michael@0: "fold" michael@0: ]; michael@0: michael@0: MochiKit.Visual.EXPORT_OK = [ michael@0: "PAIRS" michael@0: ]; michael@0: michael@0: MochiKit.Visual.__new__(); michael@0: michael@0: MochiKit.Base._exportSymbols(this, MochiKit.Visual);