5 See <http://mochikit.com/> for documentation, downloads, license, etc.
7 (c) 2005 Bob Ippolito and others. All rights Reserved.
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Visual');
13 dojo.require('MochiKit.Base');
14 dojo.require('MochiKit.DOM');
15 dojo.require('MochiKit.Style');
16 dojo.require('MochiKit.Color');
17 dojo.require('MochiKit.Position');
20 if (typeof(JSAN) != 'undefined') {
21 JSAN.use("MochiKit.Base", []);
22 JSAN.use("MochiKit.DOM", []);
23 JSAN.use("MochiKit.Style", []);
24 JSAN.use("MochiKit.Color", []);
25 JSAN.use("MochiKit.Position", []);
29 if (typeof(MochiKit.Base) === 'undefined' ||
30 typeof(MochiKit.DOM) === 'undefined' ||
31 typeof(MochiKit.Style) === 'undefined' ||
32 typeof(MochiKit.Position) === 'undefined' ||
33 typeof(MochiKit.Color) === 'undefined') {
37 throw "MochiKit.Visual depends on MochiKit.Base, MochiKit.DOM, MochiKit.Style, MochiKit.Position and MochiKit.Color!";
40 if (typeof(MochiKit.Visual) == "undefined") {
44 MochiKit.Visual.NAME = "MochiKit.Visual";
45 MochiKit.Visual.VERSION = "1.4";
47 MochiKit.Visual.__repr__ = function () {
48 return "[" + this.NAME + " " + this.VERSION + "]";
51 MochiKit.Visual.toString = function () {
52 return this.__repr__();
55 MochiKit.Visual._RoundCorners = function (e, options) {
56 e = MochiKit.DOM.getElement(e);
57 this._setOptions(options);
58 if (this.options.__unstable__wrapElement) {
62 var color = this.options.color;
63 var C = MochiKit.Color.Color;
64 if (this.options.color === "fromElement") {
65 color = C.fromBackground(e);
66 } else if (!(color instanceof C)) {
67 color = C.fromString(color);
69 this.isTransparent = (color.asRGB().a <= 0);
71 var bgColor = this.options.bgColor;
72 if (this.options.bgColor === "fromParent") {
73 bgColor = C.fromBackground(e.offsetParent);
74 } else if (!(bgColor instanceof C)) {
75 bgColor = C.fromString(bgColor);
78 this._roundCornersImpl(e, color, bgColor);
81 MochiKit.Visual._RoundCorners.prototype = {
82 _doWrap: function (e) {
83 var parent = e.parentNode;
84 var doc = MochiKit.DOM.currentDocument();
85 if (typeof(doc.defaultView) === "undefined"
86 || doc.defaultView === null) {
89 var style = doc.defaultView.getComputedStyle(e, null);
90 if (typeof(style) === "undefined" || style === null) {
93 var wrapper = MochiKit.DOM.DIV({"style": {
95 // convert padding to margin
96 marginTop: style.getPropertyValue("padding-top"),
97 marginRight: style.getPropertyValue("padding-right"),
98 marginBottom: style.getPropertyValue("padding-bottom"),
99 marginLeft: style.getPropertyValue("padding-left"),
100 // remove padding so the rounding looks right
107 wrapper.innerHTML = e.innerHTML;
109 e.appendChild(wrapper);
113 _roundCornersImpl: function (e, color, bgColor) {
114 if (this.options.border) {
115 this._renderBorder(e, bgColor);
117 if (this._isTopRounded()) {
118 this._roundTopCorners(e, color, bgColor);
120 if (this._isBottomRounded()) {
121 this._roundBottomCorners(e, color, bgColor);
125 _renderBorder: function (el, bgColor) {
126 var borderValue = "1px solid " + this._borderColor(bgColor);
127 var borderL = "border-left: " + borderValue;
128 var borderR = "border-right: " + borderValue;
129 var style = "style='" + borderL + ";" + borderR + "'";
130 el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>";
133 _roundTopCorners: function (el, color, bgColor) {
134 var corner = this._createCorner(bgColor);
135 for (var i = 0; i < this.options.numSlices; i++) {
137 this._createCornerSlice(color, bgColor, i, "top")
140 el.style.paddingTop = 0;
141 el.insertBefore(corner, el.firstChild);
144 _roundBottomCorners: function (el, color, bgColor) {
145 var corner = this._createCorner(bgColor);
146 for (var i = (this.options.numSlices - 1); i >= 0; i--) {
148 this._createCornerSlice(color, bgColor, i, "bottom")
151 el.style.paddingBottom = 0;
152 el.appendChild(corner);
155 _createCorner: function (bgColor) {
156 var dom = MochiKit.DOM;
157 return dom.DIV({style: {backgroundColor: bgColor.toString()}});
160 _createCornerSlice: function (color, bgColor, n, position) {
161 var slice = MochiKit.DOM.SPAN();
163 var inStyle = slice.style;
164 inStyle.backgroundColor = color.toString();
165 inStyle.display = "block";
166 inStyle.height = "1px";
167 inStyle.overflow = "hidden";
168 inStyle.fontSize = "1px";
170 var borderColor = this._borderColor(color, bgColor);
171 if (this.options.border && n === 0) {
172 inStyle.borderTopStyle = "solid";
173 inStyle.borderTopWidth = "1px";
174 inStyle.borderLeftWidth = "0px";
175 inStyle.borderRightWidth = "0px";
176 inStyle.borderBottomWidth = "0px";
177 // assumes css compliant box model
178 inStyle.height = "0px";
179 inStyle.borderColor = borderColor.toString();
180 } else if (borderColor) {
181 inStyle.borderColor = borderColor.toString();
182 inStyle.borderStyle = "solid";
183 inStyle.borderWidth = "0px 1px";
186 if (!this.options.compact && (n == (this.options.numSlices - 1))) {
187 inStyle.height = "2px";
190 this._setMargin(slice, n, position);
191 this._setBorder(slice, n, position);
196 _setOptions: function (options) {
199 color: "fromElement",
200 bgColor: "fromParent",
204 __unstable__wrapElement: false
206 MochiKit.Base.update(this.options, options);
208 this.options.numSlices = (this.options.compact ? 2 : 4);
211 _whichSideTop: function () {
212 var corners = this.options.corners;
213 if (this._hasString(corners, "all", "top")) {
217 var has_tl = (corners.indexOf("tl") != -1);
218 var has_tr = (corners.indexOf("tr") != -1);
219 if (has_tl && has_tr) {
231 _whichSideBottom: function () {
232 var corners = this.options.corners;
233 if (this._hasString(corners, "all", "bottom")) {
237 var has_bl = (corners.indexOf('bl') != -1);
238 var has_br = (corners.indexOf('br') != -1);
239 if (has_bl && has_br) {
251 _borderColor: function (color, bgColor) {
252 if (color == "transparent") {
254 } else if (this.options.border) {
255 return this.options.border;
256 } else if (this.options.blend) {
257 return bgColor.blendedColor(color);
263 _setMargin: function (el, n, corners) {
264 var marginSize = this._marginSize(n) + "px";
266 corners == "top" ? this._whichSideTop() : this._whichSideBottom()
268 var style = el.style;
270 if (whichSide == "left") {
271 style.marginLeft = marginSize;
272 style.marginRight = "0px";
273 } else if (whichSide == "right") {
274 style.marginRight = marginSize;
275 style.marginLeft = "0px";
277 style.marginLeft = marginSize;
278 style.marginRight = marginSize;
282 _setBorder: function (el, n, corners) {
283 var borderSize = this._borderSize(n) + "px";
285 corners == "top" ? this._whichSideTop() : this._whichSideBottom()
288 var style = el.style;
289 if (whichSide == "left") {
290 style.borderLeftWidth = borderSize;
291 style.borderRightWidth = "0px";
292 } else if (whichSide == "right") {
293 style.borderRightWidth = borderSize;
294 style.borderLeftWidth = "0px";
296 style.borderLeftWidth = borderSize;
297 style.borderRightWidth = borderSize;
301 _marginSize: function (n) {
302 if (this.isTransparent) {
306 var o = this.options;
307 if (o.compact && o.blend) {
308 var smBlendedMarginSizes = [1, 0];
309 return smBlendedMarginSizes[n];
310 } else if (o.compact) {
311 var compactMarginSizes = [2, 1];
312 return compactMarginSizes[n];
313 } else if (o.blend) {
314 var blendedMarginSizes = [3, 2, 1, 0];
315 return blendedMarginSizes[n];
317 var marginSizes = [5, 3, 2, 1];
318 return marginSizes[n];
322 _borderSize: function (n) {
323 var o = this.options;
325 if (o.compact && (o.blend || this.isTransparent)) {
327 } else if (o.compact) {
328 borderSizes = [1, 0];
329 } else if (o.blend) {
330 borderSizes = [2, 1, 1, 1];
331 } else if (o.border) {
332 borderSizes = [0, 2, 0, 0];
333 } else if (this.isTransparent) {
334 borderSizes = [5, 3, 2, 1];
338 return borderSizes[n];
341 _hasString: function (str) {
342 for (var i = 1; i< arguments.length; i++) {
343 if (str.indexOf(arguments[i]) != -1) {
350 _isTopRounded: function () {
351 return this._hasString(this.options.corners,
352 "all", "top", "tl", "tr"
356 _isBottomRounded: function () {
357 return this._hasString(this.options.corners,
358 "all", "bottom", "bl", "br"
362 _hasSingleTextChild: function (el) {
363 return (el.childNodes.length == 1 && el.childNodes[0].nodeType == 3);
367 /** @id MochiKit.Visual.roundElement */
368 MochiKit.Visual.roundElement = function (e, options) {
369 new MochiKit.Visual._RoundCorners(e, options);
372 /** @id MochiKit.Visual.roundClass */
373 MochiKit.Visual.roundClass = function (tagName, className, options) {
374 var elements = MochiKit.DOM.getElementsByTagAndClassName(
377 for (var i = 0; i < elements.length; i++) {
378 MochiKit.Visual.roundElement(elements[i], options);
382 /** @id MochiKit.Visual.tagifyText */
383 MochiKit.Visual.tagifyText = function (element, /* optional */tagifyStyle) {
386 Change a node text to character in tags.
388 @param tagifyStyle: the style to apply to character nodes, default to
389 'position: relative'.
392 tagifyStyle = tagifyStyle || 'position:relative';
393 if (/MSIE/.test(navigator.userAgent)) {
394 tagifyStyle += ';zoom:1';
396 element = MochiKit.DOM.getElement(element);
397 var ma = MochiKit.Base.map;
398 ma(function (child) {
399 if (child.nodeType == 3) {
400 ma(function (character) {
401 element.insertBefore(
402 MochiKit.DOM.SPAN({style: tagifyStyle},
403 character == ' ' ? String.fromCharCode(160) : character), child);
404 }, child.nodeValue.split(''));
405 MochiKit.DOM.removeElement(child);
407 }, element.childNodes);
410 /** @id MochiKit.Visual.forceRerendering */
411 MochiKit.Visual.forceRerendering = function (element) {
413 element = MochiKit.DOM.getElement(element);
414 var n = document.createTextNode(' ');
415 element.appendChild(n);
416 element.removeChild(n);
421 /** @id MochiKit.Visual.multiple */
422 MochiKit.Visual.multiple = function (elements, effect, /* optional */options) {
425 Launch the same effect subsequently on given elements.
428 options = MochiKit.Base.update({
429 speed: 0.1, delay: 0.0
431 var masterDelay = options.delay;
433 MochiKit.Base.map(function (innerelement) {
434 options.delay = index * options.speed + masterDelay;
435 new effect(innerelement, options);
440 MochiKit.Visual.PAIRS = {
441 'slide': ['slideDown', 'slideUp'],
442 'blind': ['blindDown', 'blindUp'],
443 'appear': ['appear', 'fade'],
444 'size': ['grow', 'shrink']
447 /** @id MochiKit.Visual.toggle */
448 MochiKit.Visual.toggle = function (element, /* optional */effect, /* optional */options) {
451 Toggle an item between two state depending of its visibility, making
452 a effect between these states. Default effect is 'appear', can be
456 element = MochiKit.DOM.getElement(element);
457 effect = (effect || 'appear').toLowerCase();
458 options = MochiKit.Base.update({
459 queue: {position: 'end', scope: (element.id || 'global'), limit: 1}
461 var v = MochiKit.Visual;
462 v[element.style.display != 'none' ?
463 v.PAIRS[effect][1] : v.PAIRS[effect][0]](element, options);
468 Transitions: define functions calculating variations depending of a position.
472 MochiKit.Visual.Transitions = {};
474 /** @id MochiKit.Visual.Transitions.linear */
475 MochiKit.Visual.Transitions.linear = function (pos) {
479 /** @id MochiKit.Visual.Transitions.sinoidal */
480 MochiKit.Visual.Transitions.sinoidal = function (pos) {
481 return (-Math.cos(pos*Math.PI)/2) + 0.5;
484 /** @id MochiKit.Visual.Transitions.reverse */
485 MochiKit.Visual.Transitions.reverse = function (pos) {
489 /** @id MochiKit.Visual.Transitions.flicker */
490 MochiKit.Visual.Transitions.flicker = function (pos) {
491 return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
494 /** @id MochiKit.Visual.Transitions.wobble */
495 MochiKit.Visual.Transitions.wobble = function (pos) {
496 return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
499 /** @id MochiKit.Visual.Transitions.pulse */
500 MochiKit.Visual.Transitions.pulse = function (pos) {
501 return (Math.floor(pos*10) % 2 === 0 ?
502 (pos*10 - Math.floor(pos*10)) : 1 - (pos*10 - Math.floor(pos*10)));
505 /** @id MochiKit.Visual.Transitions.none */
506 MochiKit.Visual.Transitions.none = function (pos) {
510 /** @id MochiKit.Visual.Transitions.full */
511 MochiKit.Visual.Transitions.full = function (pos) {
521 MochiKit.Visual.ScopedQueue = function () {
522 var cls = arguments.callee;
523 if (!(this instanceof cls)) {
529 MochiKit.Base.update(MochiKit.Visual.ScopedQueue.prototype, {
530 __init__: function () {
532 this.interval = null;
535 /** @id MochiKit.Visual.ScopedQueue.prototype.add */
536 add: function (effect) {
537 var timestamp = new Date().getTime();
539 var position = (typeof(effect.options.queue) == 'string') ?
540 effect.options.queue : effect.options.queue.position;
542 var ma = MochiKit.Base.map;
545 // move unstarted effects after this effect
547 if (e.state == 'idle') {
548 e.startOn += effect.finishOn;
549 e.finishOn += effect.finishOn;
555 // start effect after last queued effect has finished
558 if (i >= (finish || i)) {
562 timestamp = finish || timestamp;
571 effect.startOn += timestamp;
572 effect.finishOn += timestamp;
573 if (!effect.options.queue.limit ||
574 this.effects.length < effect.options.queue.limit) {
575 this.effects.push(effect);
578 if (!this.interval) {
579 this.interval = this.startLoop(MochiKit.Base.bind(this.loop, this),
584 /** @id MochiKit.Visual.ScopedQueue.prototype.startLoop */
585 startLoop: function (func, interval) {
586 return setInterval(func, interval);
589 /** @id MochiKit.Visual.ScopedQueue.prototype.remove */
590 remove: function (effect) {
591 this.effects = MochiKit.Base.filter(function (e) {
594 if (!this.effects.length) {
595 this.stopLoop(this.interval);
596 this.interval = null;
600 /** @id MochiKit.Visual.ScopedQueue.prototype.stopLoop */
601 stopLoop: function (interval) {
602 clearInterval(interval);
605 /** @id MochiKit.Visual.ScopedQueue.prototype.loop */
607 var timePos = new Date().getTime();
608 MochiKit.Base.map(function (effect) {
609 effect.loop(timePos);
614 MochiKit.Visual.Queues = {
617 get: function (queueName) {
618 if (typeof(queueName) != 'string') {
622 if (!this.instances[queueName]) {
623 this.instances[queueName] = new MochiKit.Visual.ScopedQueue();
625 return this.instances[queueName];
629 MochiKit.Visual.Queue = MochiKit.Visual.Queues.get('global');
631 MochiKit.Visual.DefaultOptions = {
632 transition: MochiKit.Visual.Transitions.sinoidal,
633 duration: 1.0, // seconds
634 fps: 25.0, // max. 25fps due to MochiKit.Visual.Queue implementation
635 sync: false, // true for combining
642 MochiKit.Visual.Base = function () {};
644 MochiKit.Visual.Base.prototype = {
647 Basic class for all Effects. Define a looping mechanism called for each step
648 of an effect. Don't instantiate it, only subclass it.
652 __class__ : MochiKit.Visual.Base,
654 /** @id MochiKit.Visual.Base.prototype.start */
655 start: function (options) {
656 var v = MochiKit.Visual;
657 this.options = MochiKit.Base.setdefault(options || {},
659 this.currentFrame = 0;
661 this.startOn = this.options.delay*1000;
662 this.finishOn = this.startOn + (this.options.duration*1000);
663 this.event('beforeStart');
664 if (!this.options.sync) {
665 v.Queues.get(typeof(this.options.queue) == 'string' ?
666 'global' : this.options.queue.scope).add(this);
670 /** @id MochiKit.Visual.Base.prototype.loop */
671 loop: function (timePos) {
672 if (timePos >= this.startOn) {
673 if (timePos >= this.finishOn) {
674 return this.finalize();
676 var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
678 Math.round(pos * this.options.fps * this.options.duration);
679 if (frame > this.currentFrame) {
681 this.currentFrame = frame;
686 /** @id MochiKit.Visual.Base.prototype.render */
687 render: function (pos) {
688 if (this.state == 'idle') {
689 this.state = 'running';
690 this.event('beforeSetup');
692 this.event('afterSetup');
694 if (this.state == 'running') {
695 if (this.options.transition) {
696 pos = this.options.transition(pos);
698 pos *= (this.options.to - this.options.from);
699 pos += this.options.from;
700 this.event('beforeUpdate');
702 this.event('afterUpdate');
706 /** @id MochiKit.Visual.Base.prototype.cancel */
707 cancel: function () {
708 if (!this.options.sync) {
709 MochiKit.Visual.Queues.get(typeof(this.options.queue) == 'string' ?
710 'global' : this.options.queue.scope).remove(this);
712 this.state = 'finished';
715 /** @id MochiKit.Visual.Base.prototype.finalize */
716 finalize: function () {
719 this.event('beforeFinish');
721 this.event('afterFinish');
727 finish: function () {
730 update: function (position) {
733 /** @id MochiKit.Visual.Base.prototype.event */
734 event: function (eventName) {
735 if (this.options[eventName + 'Internal']) {
736 this.options[eventName + 'Internal'](this);
738 if (this.options[eventName]) {
739 this.options[eventName](this);
743 /** @id MochiKit.Visual.Base.prototype.repr */
745 return '[' + this.__class__.NAME + ', options:' +
746 MochiKit.Base.repr(this.options) + ']';
750 /** @id MochiKit.Visual.Parallel */
751 MochiKit.Visual.Parallel = function (effects, options) {
752 var cls = arguments.callee;
753 if (!(this instanceof cls)) {
754 return new cls(effects, options);
757 this.__init__(effects, options);
760 MochiKit.Visual.Parallel.prototype = new MochiKit.Visual.Base();
762 MochiKit.Base.update(MochiKit.Visual.Parallel.prototype, {
765 Run multiple effects at the same time.
769 __class__ : MochiKit.Visual.Parallel,
771 __init__: function (effects, options) {
772 this.effects = effects || [];
776 /** @id MochiKit.Visual.Parallel.prototype.update */
777 update: function (position) {
778 MochiKit.Base.map(function (effect) {
779 effect.render(position);
783 /** @id MochiKit.Visual.Parallel.prototype.finish */
784 finish: function () {
785 MochiKit.Base.map(function (effect) {
791 /** @id MochiKit.Visual.Opacity */
792 MochiKit.Visual.Opacity = function (element, options) {
793 var cls = arguments.callee;
794 if (!(this instanceof cls)) {
795 return new cls(element, options);
797 this.__init__(element, options);
800 MochiKit.Visual.Opacity.prototype = new MochiKit.Visual.Base();
802 MochiKit.Base.update(MochiKit.Visual.Opacity.prototype, {
805 Change the opacity of an element.
807 @param options: 'from' and 'to' change the starting and ending opacities.
808 Must be between 0.0 and 1.0. Default to current opacity and 1.0.
812 __class__ : MochiKit.Visual.Opacity,
814 __init__: function (element, /* optional */options) {
815 var b = MochiKit.Base;
816 var s = MochiKit.Style;
817 this.element = MochiKit.DOM.getElement(element);
818 // make this work on IE on elements without 'layout'
819 if (this.element.currentStyle &&
820 (!this.element.currentStyle.hasLayout)) {
821 s.setStyle(this.element, {zoom: 1});
824 from: s.getStyle(this.element, 'opacity') || 0.0,
830 /** @id MochiKit.Visual.Opacity.prototype.update */
831 update: function (position) {
832 MochiKit.Style.setStyle(this.element, {'opacity': position});
836 /** @id MochiKit.Visual.Move.prototype */
837 MochiKit.Visual.Move = function (element, options) {
838 var cls = arguments.callee;
839 if (!(this instanceof cls)) {
840 return new cls(element, options);
842 this.__init__(element, options);
845 MochiKit.Visual.Move.prototype = new MochiKit.Visual.Base();
847 MochiKit.Base.update(MochiKit.Visual.Move.prototype, {
850 Move an element between its current position to a defined position
852 @param options: 'x' and 'y' for final positions, default to 0, 0.
856 __class__ : MochiKit.Visual.Move,
858 __init__: function (element, /* optional */options) {
859 this.element = MochiKit.DOM.getElement(element);
860 options = MochiKit.Base.update({
868 /** @id MochiKit.Visual.Move.prototype.setup */
870 // Bug in Opera: Opera returns the 'real' position of a static element
871 // or relative element that does not have top/left explicitly set.
872 // ==> Always set top and left for position relative elements in your
873 // stylesheets (to 0 if you do not need them)
874 MochiKit.DOM.makePositioned(this.element);
876 var s = this.element.style;
877 var originalVisibility = s.visibility;
878 var originalDisplay = s.display;
879 if (originalDisplay == 'none') {
880 s.visibility = 'hidden';
884 this.originalLeft = parseFloat(MochiKit.Style.getStyle(this.element, 'left') || '0');
885 this.originalTop = parseFloat(MochiKit.Style.getStyle(this.element, 'top') || '0');
887 if (this.options.mode == 'absolute') {
888 // absolute movement, so we need to calc deltaX and deltaY
889 this.options.x -= this.originalLeft;
890 this.options.y -= this.originalTop;
892 if (originalDisplay == 'none') {
893 s.visibility = originalVisibility;
894 s.display = originalDisplay;
898 /** @id MochiKit.Visual.Move.prototype.update */
899 update: function (position) {
900 MochiKit.Style.setStyle(this.element, {
901 left: Math.round(this.options.x * position + this.originalLeft) + 'px',
902 top: Math.round(this.options.y * position + this.originalTop) + 'px'
907 /** @id MochiKit.Visual.Scale */
908 MochiKit.Visual.Scale = function (element, percent, options) {
909 var cls = arguments.callee;
910 if (!(this instanceof cls)) {
911 return new cls(element, percent, options);
913 this.__init__(element, percent, options);
916 MochiKit.Visual.Scale.prototype = new MochiKit.Visual.Base();
918 MochiKit.Base.update(MochiKit.Visual.Scale.prototype, {
921 Change the size of an element.
923 @param percent: final_size = percent*original_size
925 @param options: several options changing scale behaviour
929 __class__ : MochiKit.Visual.Scale,
931 __init__: function (element, percent, /* optional */options) {
932 this.element = MochiKit.DOM.getElement(element);
933 options = MochiKit.Base.update({
937 scaleFromCenter: false,
938 scaleMode: 'box', // 'box' or 'contents' or {} with provided values
945 /** @id MochiKit.Visual.Scale.prototype.setup */
947 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
948 this.elementPositioning = MochiKit.Style.getStyle(this.element,
951 var ma = MochiKit.Base.map;
952 var b = MochiKit.Base.bind;
953 this.originalStyle = {};
955 this.originalStyle[k] = this.element.style[k];
956 }, this), ['top', 'left', 'width', 'height', 'fontSize']);
958 this.originalTop = this.element.offsetTop;
959 this.originalLeft = this.element.offsetLeft;
961 var fontSize = MochiKit.Style.getStyle(this.element,
962 'font-size') || '100%';
963 ma(b(function (fontSizeType) {
964 if (fontSize.indexOf(fontSizeType) > 0) {
965 this.fontSize = parseFloat(fontSize);
966 this.fontSizeType = fontSizeType;
968 }, this), ['em', 'px', '%']);
970 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
972 if (/^content/.test(this.options.scaleMode)) {
973 this.dims = [this.element.scrollHeight, this.element.scrollWidth];
974 } else if (this.options.scaleMode == 'box') {
975 this.dims = [this.element.offsetHeight, this.element.offsetWidth];
977 this.dims = [this.options.scaleMode.originalHeight,
978 this.options.scaleMode.originalWidth];
982 /** @id MochiKit.Visual.Scale.prototype.update */
983 update: function (position) {
984 var currentScale = (this.options.scaleFrom/100.0) +
985 (this.factor * position);
986 if (this.options.scaleContent && this.fontSize) {
987 MochiKit.Style.setStyle(this.element, {
988 fontSize: this.fontSize * currentScale + this.fontSizeType
991 this.setDimensions(this.dims[0] * currentScale,
992 this.dims[1] * currentScale);
995 /** @id MochiKit.Visual.Scale.prototype.finish */
996 finish: function () {
997 if (this.restoreAfterFinish) {
998 MochiKit.Style.setStyle(this.element, this.originalStyle);
1002 /** @id MochiKit.Visual.Scale.prototype.setDimensions */
1003 setDimensions: function (height, width) {
1006 if (/MSIE/.test(navigator.userAgent)) {
1009 if (this.options.scaleX) {
1010 d.width = r(width) + 'px';
1012 if (this.options.scaleY) {
1013 d.height = r(height) + 'px';
1015 if (this.options.scaleFromCenter) {
1016 var topd = (height - this.dims[0])/2;
1017 var leftd = (width - this.dims[1])/2;
1018 if (this.elementPositioning == 'absolute') {
1019 if (this.options.scaleY) {
1020 d.top = this.originalTop - topd + 'px';
1022 if (this.options.scaleX) {
1023 d.left = this.originalLeft - leftd + 'px';
1026 if (this.options.scaleY) {
1027 d.top = -topd + 'px';
1029 if (this.options.scaleX) {
1030 d.left = -leftd + 'px';
1034 MochiKit.Style.setStyle(this.element, d);
1038 /** @id MochiKit.Visual.Highlight */
1039 MochiKit.Visual.Highlight = function (element, options) {
1040 var cls = arguments.callee;
1041 if (!(this instanceof cls)) {
1042 return new cls(element, options);
1044 this.__init__(element, options);
1047 MochiKit.Visual.Highlight.prototype = new MochiKit.Visual.Base();
1049 MochiKit.Base.update(MochiKit.Visual.Highlight.prototype, {
1052 Highlight an item of the page.
1054 @param options: 'startcolor' for choosing highlighting color, default
1059 __class__ : MochiKit.Visual.Highlight,
1061 __init__: function (element, /* optional */options) {
1062 this.element = MochiKit.DOM.getElement(element);
1063 options = MochiKit.Base.update({
1064 startcolor: '#ffff99'
1066 this.start(options);
1069 /** @id MochiKit.Visual.Highlight.prototype.setup */
1070 setup: function () {
1071 var b = MochiKit.Base;
1072 var s = MochiKit.Style;
1073 // Prevent executing on elements not in the layout flow
1074 if (s.getStyle(this.element, 'display') == 'none') {
1078 // Disable background image during the effect
1080 backgroundImage: s.getStyle(this.element, 'background-image')
1082 s.setStyle(this.element, {
1083 backgroundImage: 'none'
1086 if (!this.options.endcolor) {
1087 this.options.endcolor =
1088 MochiKit.Color.Color.fromBackground(this.element).toHexString();
1090 if (b.isUndefinedOrNull(this.options.restorecolor)) {
1091 this.options.restorecolor = s.getStyle(this.element,
1092 'background-color');
1094 // init color calculations
1095 this._base = b.map(b.bind(function (i) {
1097 this.options.startcolor.slice(i*2 + 1, i*2 + 3), 16);
1098 }, this), [0, 1, 2]);
1099 this._delta = b.map(b.bind(function (i) {
1100 return parseInt(this.options.endcolor.slice(i*2 + 1, i*2 + 3), 16)
1102 }, this), [0, 1, 2]);
1105 /** @id MochiKit.Visual.Highlight.prototype.update */
1106 update: function (position) {
1108 MochiKit.Base.map(MochiKit.Base.bind(function (i) {
1109 m += MochiKit.Color.toColorPart(Math.round(this._base[i] +
1110 this._delta[i]*position));
1111 }, this), [0, 1, 2]);
1112 MochiKit.Style.setStyle(this.element, {
1117 /** @id MochiKit.Visual.Highlight.prototype.finish */
1118 finish: function () {
1119 MochiKit.Style.setStyle(this.element,
1120 MochiKit.Base.update(this.oldStyle, {
1121 backgroundColor: this.options.restorecolor
1126 /** @id MochiKit.Visual.ScrollTo */
1127 MochiKit.Visual.ScrollTo = function (element, options) {
1128 var cls = arguments.callee;
1129 if (!(this instanceof cls)) {
1130 return new cls(element, options);
1132 this.__init__(element, options);
1135 MochiKit.Visual.ScrollTo.prototype = new MochiKit.Visual.Base();
1137 MochiKit.Base.update(MochiKit.Visual.ScrollTo.prototype, {
1140 Scroll to an element in the page.
1144 __class__ : MochiKit.Visual.ScrollTo,
1146 __init__: function (element, /* optional */options) {
1147 this.element = MochiKit.DOM.getElement(element);
1148 this.start(options || {});
1151 /** @id MochiKit.Visual.ScrollTo.prototype.setup */
1152 setup: function () {
1153 var p = MochiKit.Position;
1155 var offsets = p.cumulativeOffset(this.element);
1156 if (this.options.offset) {
1157 offsets.y += this.options.offset;
1160 if (window.innerHeight) {
1161 max = window.innerHeight - window.height;
1162 } else if (document.documentElement &&
1163 document.documentElement.clientHeight) {
1164 max = document.documentElement.clientHeight -
1165 document.body.scrollHeight;
1166 } else if (document.body) {
1167 max = document.body.clientHeight - document.body.scrollHeight;
1169 this.scrollStart = p.windowOffset.y;
1170 this.delta = (offsets.y > max ? max : offsets.y) - this.scrollStart;
1173 /** @id MochiKit.Visual.ScrollTo.prototype.update */
1174 update: function (position) {
1175 var p = MochiKit.Position;
1177 window.scrollTo(p.windowOffset.x, this.scrollStart + (position * this.delta));
1181 MochiKit.Visual.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1183 MochiKit.Visual.Morph = function (element, options) {
1184 var cls = arguments.callee;
1185 if (!(this instanceof cls)) {
1186 return new cls(element, options);
1188 this.__init__(element, options);
1191 MochiKit.Visual.Morph.prototype = new MochiKit.Visual.Base();
1193 MochiKit.Base.update(MochiKit.Visual.Morph.prototype, {
1196 Morph effect: make a transformation from current style to the given style,
1197 automatically making a transition between the two.
1201 __class__ : MochiKit.Visual.Morph,
1203 __init__: function (element, /* optional */options) {
1204 this.element = MochiKit.DOM.getElement(element);
1205 this.start(options || {});
1208 /** @id MochiKit.Visual.Morph.prototype.setup */
1209 setup: function () {
1210 var b = MochiKit.Base;
1211 var style = this.options.style;
1212 this.styleStart = {};
1216 for (var s in style) {
1219 if (MochiKit.Visual.CSS_LENGTH.test(value)) {
1220 var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
1221 value = parseFloat(components[1]);
1222 unit = (components.length == 3) ? components[2] : null;
1223 this.styleEnd[s] = value;
1224 this.units[s] = unit;
1225 value = MochiKit.Style.getStyle(this.element, s);
1226 components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
1227 value = parseFloat(components[1]);
1228 this.styleStart[s] = value;
1230 var c = MochiKit.Color.Color;
1231 value = c.fromString(value);
1233 this.units[s] = "color";
1234 this.styleEnd[s] = value.toHexString();
1235 value = MochiKit.Style.getStyle(this.element, s);
1236 this.styleStart[s] = c.fromString(value).toHexString();
1238 this.styleStart[s] = b.map(b.bind(function (i) {
1240 this.styleStart[s].slice(i*2 + 1, i*2 + 3), 16);
1241 }, this), [0, 1, 2]);
1242 this.styleEnd[s] = b.map(b.bind(function (i) {
1244 this.styleEnd[s].slice(i*2 + 1, i*2 + 3), 16);
1245 }, this), [0, 1, 2]);
1251 /** @id MochiKit.Visual.Morph.prototype.update */
1252 update: function (position) {
1254 for (var s in this.styleStart) {
1255 if (this.units[s] == "color") {
1257 var start = this.styleStart[s];
1258 var end = this.styleEnd[s];
1259 MochiKit.Base.map(MochiKit.Base.bind(function (i) {
1260 m += MochiKit.Color.toColorPart(Math.round(start[i] +
1261 (end[i] - start[i])*position));
1262 }, this), [0, 1, 2]);
1263 this.element.style[s] = m;
1265 value = this.styleStart[s] + Math.round((this.styleEnd[s] - this.styleStart[s]) * position * 1000) / 1000 + this.units[s];
1266 this.element.style[s] = value;
1274 Combination effects.
1278 /** @id MochiKit.Visual.fade */
1279 MochiKit.Visual.fade = function (element, /* optional */ options) {
1282 Fade a given element: change its opacity and hide it in the end.
1284 @param options: 'to' and 'from' to change opacity.
1287 var s = MochiKit.Style;
1288 var oldOpacity = s.getStyle(element, 'opacity');
1289 options = MochiKit.Base.update({
1290 from: s.getStyle(element, 'opacity') || 1.0,
1292 afterFinishInternal: function (effect) {
1293 if (effect.options.to !== 0) {
1296 s.hideElement(effect.element);
1297 s.setStyle(effect.element, {'opacity': oldOpacity});
1300 return new MochiKit.Visual.Opacity(element, options);
1303 /** @id MochiKit.Visual.appear */
1304 MochiKit.Visual.appear = function (element, /* optional */ options) {
1307 Make an element appear.
1309 @param options: 'to' and 'from' to change opacity.
1312 var s = MochiKit.Style;
1313 var v = MochiKit.Visual;
1314 options = MochiKit.Base.update({
1315 from: (s.getStyle(element, 'display') == 'none' ? 0.0 :
1316 s.getStyle(element, 'opacity') || 0.0),
1318 // force Safari to render floated elements properly
1319 afterFinishInternal: function (effect) {
1320 v.forceRerendering(effect.element);
1322 beforeSetupInternal: function (effect) {
1323 s.setStyle(effect.element, {'opacity': effect.options.from});
1324 s.showElement(effect.element);
1327 return new v.Opacity(element, options);
1330 /** @id MochiKit.Visual.puff */
1331 MochiKit.Visual.puff = function (element, /* optional */ options) {
1334 'Puff' an element: grow it to double size, fading it and make it hidden.
1337 var s = MochiKit.Style;
1338 var v = MochiKit.Visual;
1339 element = MochiKit.DOM.getElement(element);
1341 position: s.getStyle(element, 'position'),
1342 top: element.style.top,
1343 left: element.style.left,
1344 width: element.style.width,
1345 height: element.style.height,
1346 opacity: s.getStyle(element, 'opacity')
1348 options = MochiKit.Base.update({
1349 beforeSetupInternal: function (effect) {
1350 MochiKit.Position.absolutize(effect.effects[0].element);
1352 afterFinishInternal: function (effect) {
1353 s.hideElement(effect.effects[0].element);
1354 s.setStyle(effect.effects[0].element, oldStyle);
1357 scaleFromCenter: true
1359 return new v.Parallel(
1360 [new v.Scale(element, 200,
1361 {sync: true, scaleFromCenter: options.scaleFromCenter,
1362 scaleContent: options.scaleContent, restoreAfterFinish: true}),
1363 new v.Opacity(element, {sync: true, to: 0.0 })],
1367 /** @id MochiKit.Visual.blindUp */
1368 MochiKit.Visual.blindUp = function (element, /* optional */ options) {
1371 Blind an element up: change its vertical size to 0.
1374 var d = MochiKit.DOM;
1375 element = d.getElement(element);
1376 var elemClip = d.makeClipping(element);
1377 options = MochiKit.Base.update({
1378 scaleContent: false,
1380 restoreAfterFinish: true,
1381 afterFinishInternal: function (effect) {
1382 MochiKit.Style.hideElement(effect.element);
1383 d.undoClipping(effect.element, elemClip);
1387 return new MochiKit.Visual.Scale(element, 0, options);
1390 /** @id MochiKit.Visual.blindDown */
1391 MochiKit.Visual.blindDown = function (element, /* optional */ options) {
1394 Blind an element down: restore its vertical size.
1397 var d = MochiKit.DOM;
1398 var s = MochiKit.Style;
1399 element = d.getElement(element);
1400 var elementDimensions = s.getElementDimensions(element);
1402 options = MochiKit.Base.update({
1403 scaleContent: false,
1406 scaleMode: {originalHeight: elementDimensions.h,
1407 originalWidth: elementDimensions.w},
1408 restoreAfterFinish: true,
1409 afterSetupInternal: function (effect) {
1410 elemClip = d.makeClipping(effect.element);
1411 s.setStyle(effect.element, {height: '0px'});
1412 s.showElement(effect.element);
1414 afterFinishInternal: function (effect) {
1415 d.undoClipping(effect.element, elemClip);
1418 return new MochiKit.Visual.Scale(element, 100, options);
1421 /** @id MochiKit.Visual.switchOff */
1422 MochiKit.Visual.switchOff = function (element, /* optional */ options) {
1425 Apply a switch-off-like effect.
1428 var d = MochiKit.DOM;
1429 element = d.getElement(element);
1430 var oldOpacity = MochiKit.Style.getStyle(element, 'opacity');
1432 options = MochiKit.Base.update({
1434 scaleFromCenter: true,
1436 scaleContent: false,
1437 restoreAfterFinish: true,
1438 beforeSetupInternal: function (effect) {
1439 d.makePositioned(effect.element);
1440 elemClip = d.makeClipping(effect.element);
1442 afterFinishInternal: function (effect) {
1443 MochiKit.Style.hideElement(effect.element);
1444 d.undoClipping(effect.element, elemClip);
1445 d.undoPositioned(effect.element);
1446 MochiKit.Style.setStyle(effect.element, {'opacity': oldOpacity});
1449 var v = MochiKit.Visual;
1450 return new v.appear(element, {
1453 transition: v.Transitions.flicker,
1454 afterFinishInternal: function (effect) {
1455 new v.Scale(effect.element, 1, options);
1460 /** @id MochiKit.Visual.dropOut */
1461 MochiKit.Visual.dropOut = function (element, /* optional */ options) {
1464 Make an element fall and disappear.
1467 var d = MochiKit.DOM;
1468 var s = MochiKit.Style;
1469 element = d.getElement(element);
1471 top: s.getStyle(element, 'top'),
1472 left: s.getStyle(element, 'left'),
1473 opacity: s.getStyle(element, 'opacity')
1476 options = MochiKit.Base.update({
1479 beforeSetupInternal: function (effect) {
1480 d.makePositioned(effect.effects[0].element);
1482 afterFinishInternal: function (effect) {
1483 s.hideElement(effect.effects[0].element);
1484 d.undoPositioned(effect.effects[0].element);
1485 s.setStyle(effect.effects[0].element, oldStyle);
1488 var v = MochiKit.Visual;
1489 return new v.Parallel(
1490 [new v.Move(element, {x: 0, y: options.distance, sync: true}),
1491 new v.Opacity(element, {sync: true, to: 0.0})],
1495 /** @id MochiKit.Visual.shake */
1496 MochiKit.Visual.shake = function (element, /* optional */ options) {
1499 Move an element from left to right several times.
1502 var d = MochiKit.DOM;
1503 var v = MochiKit.Visual;
1504 var s = MochiKit.Style;
1505 element = d.getElement(element);
1506 options = MochiKit.Base.update({
1510 afterFinishInternal: function (effect) {
1511 d.undoPositioned(effect.element);
1512 s.setStyle(effect.element, oldStyle);
1516 top: s.getStyle(element, 'top'),
1517 left: s.getStyle(element, 'left') };
1518 return new v.Move(element,
1519 {x: 20, y: 0, duration: 0.05, afterFinishInternal: function (effect) {
1520 new v.Move(effect.element,
1521 {x: -40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1522 new v.Move(effect.element,
1523 {x: 40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1524 new v.Move(effect.element,
1525 {x: -40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1526 new v.Move(effect.element,
1527 {x: 40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1528 new v.Move(effect.element, options
1529 ) }}) }}) }}) }}) }});
1532 /** @id MochiKit.Visual.slideDown */
1533 MochiKit.Visual.slideDown = function (element, /* optional */ options) {
1536 Slide an element down.
1537 It needs to have the content of the element wrapped in a container
1538 element with fixed height.
1541 var d = MochiKit.DOM;
1542 var b = MochiKit.Base;
1543 var s = MochiKit.Style;
1544 element = d.getElement(element);
1545 if (!element.firstChild) {
1546 throw "MochiKit.Visual.slideDown must be used on a element with a child";
1548 d.removeEmptyTextNodes(element);
1549 var oldInnerBottom = s.getStyle(element.firstChild, 'bottom') || 0;
1550 var elementDimensions = s.getElementDimensions(element);
1552 options = b.update({
1553 scaleContent: false,
1556 scaleMode: {originalHeight: elementDimensions.h,
1557 originalWidth: elementDimensions.w},
1558 restoreAfterFinish: true,
1559 afterSetupInternal: function (effect) {
1560 d.makePositioned(effect.element);
1561 d.makePositioned(effect.element.firstChild);
1562 if (/Opera/.test(navigator.userAgent)) {
1563 s.setStyle(effect.element, {top: ''});
1565 elemClip = d.makeClipping(effect.element);
1566 s.setStyle(effect.element, {height: '0px'});
1567 s.showElement(effect.element);
1569 afterUpdateInternal: function (effect) {
1570 s.setStyle(effect.element.firstChild,
1571 {bottom: (effect.dims[0] - effect.element.clientHeight) + 'px'});
1573 afterFinishInternal: function (effect) {
1574 d.undoClipping(effect.element, elemClip);
1575 // IE will crash if child is undoPositioned first
1576 if (/MSIE/.test(navigator.userAgent)) {
1577 d.undoPositioned(effect.element);
1578 d.undoPositioned(effect.element.firstChild);
1580 d.undoPositioned(effect.element.firstChild);
1581 d.undoPositioned(effect.element);
1583 s.setStyle(effect.element.firstChild,
1584 {bottom: oldInnerBottom});
1588 return new MochiKit.Visual.Scale(element, 100, options);
1591 /** @id MochiKit.Visual.slideUp */
1592 MochiKit.Visual.slideUp = function (element, /* optional */ options) {
1595 Slide an element up.
1596 It needs to have the content of the element wrapped in a container
1597 element with fixed height.
1600 var d = MochiKit.DOM;
1601 var b = MochiKit.Base;
1602 var s = MochiKit.Style;
1603 element = d.getElement(element);
1604 if (!element.firstChild) {
1605 throw "MochiKit.Visual.slideUp must be used on a element with a child";
1607 d.removeEmptyTextNodes(element);
1608 var oldInnerBottom = s.getStyle(element.firstChild, 'bottom');
1610 options = b.update({
1611 scaleContent: false,
1615 restoreAfterFinish: true,
1616 beforeStartInternal: function (effect) {
1617 d.makePositioned(effect.element);
1618 d.makePositioned(effect.element.firstChild);
1619 if (/Opera/.test(navigator.userAgent)) {
1620 s.setStyle(effect.element, {top: ''});
1622 elemClip = d.makeClipping(effect.element);
1623 s.showElement(effect.element);
1625 afterUpdateInternal: function (effect) {
1626 s.setStyle(effect.element.firstChild,
1627 {bottom: (effect.dims[0] - effect.element.clientHeight) + 'px'});
1629 afterFinishInternal: function (effect) {
1630 s.hideElement(effect.element);
1631 d.undoClipping(effect.element, elemClip);
1632 d.undoPositioned(effect.element.firstChild);
1633 d.undoPositioned(effect.element);
1634 s.setStyle(effect.element.firstChild, {bottom: oldInnerBottom});
1637 return new MochiKit.Visual.Scale(element, 0, options);
1640 // Bug in opera makes the TD containing this element expand for a instance
1642 /** @id MochiKit.Visual.squish */
1643 MochiKit.Visual.squish = function (element, /* optional */ options) {
1646 Reduce an element and make it disappear.
1649 var d = MochiKit.DOM;
1650 var b = MochiKit.Base;
1652 options = b.update({
1653 restoreAfterFinish: true,
1654 beforeSetupInternal: function (effect) {
1655 elemClip = d.makeClipping(effect.element);
1657 afterFinishInternal: function (effect) {
1658 MochiKit.Style.hideElement(effect.element);
1659 d.undoClipping(effect.element, elemClip);
1663 return new MochiKit.Visual.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, options);
1666 /** @id MochiKit.Visual.grow */
1667 MochiKit.Visual.grow = function (element, /* optional */ options) {
1670 Grow an element to its original size. Make it zero-sized before
1674 var d = MochiKit.DOM;
1675 var v = MochiKit.Visual;
1676 var s = MochiKit.Style;
1677 element = d.getElement(element);
1678 options = MochiKit.Base.update({
1679 direction: 'center',
1680 moveTransition: v.Transitions.sinoidal,
1681 scaleTransition: v.Transitions.sinoidal,
1682 opacityTransition: v.Transitions.full,
1684 scaleFromCenter: false
1687 top: element.style.top,
1688 left: element.style.left,
1689 height: element.style.height,
1690 width: element.style.width,
1691 opacity: s.getStyle(element, 'opacity')
1694 var dims = s.getElementDimensions(element);
1695 var initialMoveX, initialMoveY;
1698 switch (options.direction) {
1700 initialMoveX = initialMoveY = moveX = moveY = 0;
1703 initialMoveX = dims.w;
1704 initialMoveY = moveY = 0;
1708 initialMoveX = moveX = 0;
1709 initialMoveY = dims.h;
1712 case 'bottom-right':
1713 initialMoveX = dims.w;
1714 initialMoveY = dims.h;
1719 initialMoveX = dims.w / 2;
1720 initialMoveY = dims.h / 2;
1721 moveX = -dims.w / 2;
1722 moveY = -dims.h / 2;
1726 var optionsParallel = MochiKit.Base.update({
1727 beforeSetupInternal: function (effect) {
1728 s.setStyle(effect.effects[0].element, {height: '0px'});
1729 s.showElement(effect.effects[0].element);
1731 afterFinishInternal: function (effect) {
1732 d.undoClipping(effect.effects[0].element);
1733 d.undoPositioned(effect.effects[0].element);
1734 s.setStyle(effect.effects[0].element, oldStyle);
1738 return new v.Move(element, {
1742 beforeSetupInternal: function (effect) {
1743 s.hideElement(effect.element);
1744 d.makeClipping(effect.element);
1745 d.makePositioned(effect.element);
1747 afterFinishInternal: function (effect) {
1749 [new v.Opacity(effect.element, {
1750 sync: true, to: 1.0, from: 0.0,
1751 transition: options.opacityTransition
1753 new v.Move(effect.element, {
1754 x: moveX, y: moveY, sync: true,
1755 transition: options.moveTransition
1757 new v.Scale(effect.element, 100, {
1758 scaleMode: {originalHeight: dims.h,
1759 originalWidth: dims.w},
1761 scaleFrom: /Opera/.test(navigator.userAgent) ? 1 : 0,
1762 transition: options.scaleTransition,
1763 scaleContent: options.scaleContent,
1764 scaleFromCenter: options.scaleFromCenter,
1765 restoreAfterFinish: true
1773 /** @id MochiKit.Visual.shrink */
1774 MochiKit.Visual.shrink = function (element, /* optional */ options) {
1777 Shrink an element and make it disappear.
1780 var d = MochiKit.DOM;
1781 var v = MochiKit.Visual;
1782 var s = MochiKit.Style;
1783 element = d.getElement(element);
1784 options = MochiKit.Base.update({
1785 direction: 'center',
1786 moveTransition: v.Transitions.sinoidal,
1787 scaleTransition: v.Transitions.sinoidal,
1788 opacityTransition: v.Transitions.none,
1790 scaleFromCenter: false
1793 top: element.style.top,
1794 left: element.style.left,
1795 height: element.style.height,
1796 width: element.style.width,
1797 opacity: s.getStyle(element, 'opacity')
1800 var dims = s.getElementDimensions(element);
1803 switch (options.direction) {
1815 case 'bottom-right':
1826 var optionsParallel = MochiKit.Base.update({
1827 beforeStartInternal: function (effect) {
1828 elemClip = d.makePositioned(effect.effects[0].element);
1829 d.makeClipping(effect.effects[0].element);
1831 afterFinishInternal: function (effect) {
1832 s.hideElement(effect.effects[0].element);
1833 d.undoClipping(effect.effects[0].element, elemClip);
1834 d.undoPositioned(effect.effects[0].element);
1835 s.setStyle(effect.effects[0].element, oldStyle);
1839 return new v.Parallel(
1840 [new v.Opacity(element, {
1841 sync: true, to: 0.0, from: 1.0,
1842 transition: options.opacityTransition
1844 new v.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, {
1845 sync: true, transition: options.scaleTransition,
1846 scaleContent: options.scaleContent,
1847 scaleFromCenter: options.scaleFromCenter,
1848 restoreAfterFinish: true
1850 new v.Move(element, {
1851 x: moveX, y: moveY, sync: true, transition: options.moveTransition
1857 /** @id MochiKit.Visual.pulsate */
1858 MochiKit.Visual.pulsate = function (element, /* optional */ options) {
1861 Pulse an element between appear/fade.
1864 var d = MochiKit.DOM;
1865 var v = MochiKit.Visual;
1866 var b = MochiKit.Base;
1867 var oldOpacity = MochiKit.Style.getStyle(element, 'opacity');
1868 options = b.update({
1871 afterFinishInternal: function (effect) {
1872 MochiKit.Style.setStyle(effect.element, {'opacity': oldOpacity});
1875 var transition = options.transition || v.Transitions.sinoidal;
1876 var reverser = b.bind(function (pos) {
1877 return transition(1 - v.Transitions.pulse(pos));
1879 b.bind(reverser, transition);
1880 return new v.Opacity(element, b.update({
1881 transition: reverser}, options));
1884 /** @id MochiKit.Visual.fold */
1885 MochiKit.Visual.fold = function (element, /* optional */ options) {
1888 Fold an element, first vertically, then horizontally.
1891 var d = MochiKit.DOM;
1892 var v = MochiKit.Visual;
1893 var s = MochiKit.Style;
1894 element = d.getElement(element);
1896 top: element.style.top,
1897 left: element.style.left,
1898 width: element.style.width,
1899 height: element.style.height
1901 var elemClip = d.makeClipping(element);
1902 options = MochiKit.Base.update({
1903 scaleContent: false,
1905 afterFinishInternal: function (effect) {
1906 new v.Scale(element, 1, {
1907 scaleContent: false,
1909 afterFinishInternal: function (effect) {
1910 s.hideElement(effect.element);
1911 d.undoClipping(effect.element, elemClip);
1912 s.setStyle(effect.element, oldStyle);
1917 return new v.Scale(element, 5, options);
1921 // Compatibility with MochiKit 1.0
1922 MochiKit.Visual.Color = MochiKit.Color.Color;
1923 MochiKit.Visual.getElementsComputedStyle = MochiKit.DOM.computedStyle;
1925 /* end of Rico adaptation */
1927 MochiKit.Visual.__new__ = function () {
1928 var m = MochiKit.Base;
1930 m.nameFunctions(this);
1932 this.EXPORT_TAGS = {
1933 ":common": this.EXPORT,
1934 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
1939 MochiKit.Visual.EXPORT = [
1969 MochiKit.Visual.EXPORT_OK = [
1974 MochiKit.Visual.__new__();
1976 MochiKit.Base._exportSymbols(this, MochiKit.Visual);