Bug 435739 Poor performance of Firefox 3 with no X RENDER extension
[wine-gecko.git] / testing / mochitest / MochiKit / Visual.js
blobbf8b3dfc308f298a4cf9f771350c74a5bf71a765
1 /***
3 MochiKit.Visual 1.4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
7 (c) 2005 Bob Ippolito and others. All rights Reserved.
9 ***/
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');
19 if (typeof(JSAN) != 'undefined') {
20 JSAN.use("MochiKit.Base", []);
21 JSAN.use("MochiKit.DOM", []);
22 JSAN.use("MochiKit.Style", []);
23 JSAN.use("MochiKit.Color", []);
26 try {
27 if (typeof(MochiKit.Base) === 'undefined' ||
28 typeof(MochiKit.DOM) === 'undefined' ||
29 typeof(MochiKit.Style) === 'undefined' ||
30 typeof(MochiKit.Color) === 'undefined') {
31 throw "";
33 } catch (e) {
34 throw "MochiKit.Visual depends on MochiKit.Base, MochiKit.DOM, MochiKit.Style and MochiKit.Color!";
37 if (typeof(MochiKit.Visual) == "undefined") {
38 MochiKit.Visual = {};
41 MochiKit.Visual.NAME = "MochiKit.Visual";
42 MochiKit.Visual.VERSION = "1.4";
44 MochiKit.Visual.__repr__ = function () {
45 return "[" + this.NAME + " " + this.VERSION + "]";
48 MochiKit.Visual.toString = function () {
49 return this.__repr__();
52 MochiKit.Visual._RoundCorners = function (e, options) {
53 e = MochiKit.DOM.getElement(e);
54 this._setOptions(options);
55 if (this.options.__unstable__wrapElement) {
56 e = this._doWrap(e);
59 var color = this.options.color;
60 var C = MochiKit.Color.Color;
61 if (this.options.color === "fromElement") {
62 color = C.fromBackground(e);
63 } else if (!(color instanceof C)) {
64 color = C.fromString(color);
66 this.isTransparent = (color.asRGB().a <= 0);
68 var bgColor = this.options.bgColor;
69 if (this.options.bgColor === "fromParent") {
70 bgColor = C.fromBackground(e.offsetParent);
71 } else if (!(bgColor instanceof C)) {
72 bgColor = C.fromString(bgColor);
75 this._roundCornersImpl(e, color, bgColor);
78 MochiKit.Visual._RoundCorners.prototype = {
79 _doWrap: function (e) {
80 var parent = e.parentNode;
81 var doc = MochiKit.DOM.currentDocument();
82 if (typeof(doc.defaultView) === "undefined"
83 || doc.defaultView === null) {
84 return e;
86 var style = doc.defaultView.getComputedStyle(e, null);
87 if (typeof(style) === "undefined" || style === null) {
88 return e;
90 var wrapper = MochiKit.DOM.DIV({"style": {
91 display: "block",
92 // convert padding to margin
93 marginTop: style.getPropertyValue("padding-top"),
94 marginRight: style.getPropertyValue("padding-right"),
95 marginBottom: style.getPropertyValue("padding-bottom"),
96 marginLeft: style.getPropertyValue("padding-left"),
97 // remove padding so the rounding looks right
98 padding: "0px"
100 paddingRight: "0px",
101 paddingLeft: "0px"
103 }});
104 wrapper.innerHTML = e.innerHTML;
105 e.innerHTML = "";
106 e.appendChild(wrapper);
107 return e;
110 _roundCornersImpl: function (e, color, bgColor) {
111 if (this.options.border) {
112 this._renderBorder(e, bgColor);
114 if (this._isTopRounded()) {
115 this._roundTopCorners(e, color, bgColor);
117 if (this._isBottomRounded()) {
118 this._roundBottomCorners(e, color, bgColor);
122 _renderBorder: function (el, bgColor) {
123 var borderValue = "1px solid " + this._borderColor(bgColor);
124 var borderL = "border-left: " + borderValue;
125 var borderR = "border-right: " + borderValue;
126 var style = "style='" + borderL + ";" + borderR + "'";
127 el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>";
130 _roundTopCorners: function (el, color, bgColor) {
131 var corner = this._createCorner(bgColor);
132 for (var i = 0; i < this.options.numSlices; i++) {
133 corner.appendChild(
134 this._createCornerSlice(color, bgColor, i, "top")
137 el.style.paddingTop = 0;
138 el.insertBefore(corner, el.firstChild);
141 _roundBottomCorners: function (el, color, bgColor) {
142 var corner = this._createCorner(bgColor);
143 for (var i = (this.options.numSlices - 1); i >= 0; i--) {
144 corner.appendChild(
145 this._createCornerSlice(color, bgColor, i, "bottom")
148 el.style.paddingBottom = 0;
149 el.appendChild(corner);
152 _createCorner: function (bgColor) {
153 var dom = MochiKit.DOM;
154 return dom.DIV({style: {backgroundColor: bgColor.toString()}});
157 _createCornerSlice: function (color, bgColor, n, position) {
158 var slice = MochiKit.DOM.SPAN();
160 var inStyle = slice.style;
161 inStyle.backgroundColor = color.toString();
162 inStyle.display = "block";
163 inStyle.height = "1px";
164 inStyle.overflow = "hidden";
165 inStyle.fontSize = "1px";
167 var borderColor = this._borderColor(color, bgColor);
168 if (this.options.border && n === 0) {
169 inStyle.borderTopStyle = "solid";
170 inStyle.borderTopWidth = "1px";
171 inStyle.borderLeftWidth = "0px";
172 inStyle.borderRightWidth = "0px";
173 inStyle.borderBottomWidth = "0px";
174 // assumes css compliant box model
175 inStyle.height = "0px";
176 inStyle.borderColor = borderColor.toString();
177 } else if (borderColor) {
178 inStyle.borderColor = borderColor.toString();
179 inStyle.borderStyle = "solid";
180 inStyle.borderWidth = "0px 1px";
183 if (!this.options.compact && (n == (this.options.numSlices - 1))) {
184 inStyle.height = "2px";
187 this._setMargin(slice, n, position);
188 this._setBorder(slice, n, position);
190 return slice;
193 _setOptions: function (options) {
194 this.options = {
195 corners: "all",
196 color: "fromElement",
197 bgColor: "fromParent",
198 blend: true,
199 border: false,
200 compact: false,
201 __unstable__wrapElement: false
203 MochiKit.Base.update(this.options, options);
205 this.options.numSlices = (this.options.compact ? 2 : 4);
208 _whichSideTop: function () {
209 var corners = this.options.corners;
210 if (this._hasString(corners, "all", "top")) {
211 return "";
214 var has_tl = (corners.indexOf("tl") != -1);
215 var has_tr = (corners.indexOf("tr") != -1);
216 if (has_tl && has_tr) {
217 return "";
219 if (has_tl) {
220 return "left";
222 if (has_tr) {
223 return "right";
225 return "";
228 _whichSideBottom: function () {
229 var corners = this.options.corners;
230 if (this._hasString(corners, "all", "bottom")) {
231 return "";
234 var has_bl = (corners.indexOf('bl') != -1);
235 var has_br = (corners.indexOf('br') != -1);
236 if (has_bl && has_br) {
237 return "";
239 if (has_bl) {
240 return "left";
242 if (has_br) {
243 return "right";
245 return "";
248 _borderColor: function (color, bgColor) {
249 if (color == "transparent") {
250 return bgColor;
251 } else if (this.options.border) {
252 return this.options.border;
253 } else if (this.options.blend) {
254 return bgColor.blendedColor(color);
256 return "";
260 _setMargin: function (el, n, corners) {
261 var marginSize = this._marginSize(n) + "px";
262 var whichSide = (
263 corners == "top" ? this._whichSideTop() : this._whichSideBottom()
265 var style = el.style;
267 if (whichSide == "left") {
268 style.marginLeft = marginSize;
269 style.marginRight = "0px";
270 } else if (whichSide == "right") {
271 style.marginRight = marginSize;
272 style.marginLeft = "0px";
273 } else {
274 style.marginLeft = marginSize;
275 style.marginRight = marginSize;
279 _setBorder: function (el, n, corners) {
280 var borderSize = this._borderSize(n) + "px";
281 var whichSide = (
282 corners == "top" ? this._whichSideTop() : this._whichSideBottom()
285 var style = el.style;
286 if (whichSide == "left") {
287 style.borderLeftWidth = borderSize;
288 style.borderRightWidth = "0px";
289 } else if (whichSide == "right") {
290 style.borderRightWidth = borderSize;
291 style.borderLeftWidth = "0px";
292 } else {
293 style.borderLeftWidth = borderSize;
294 style.borderRightWidth = borderSize;
298 _marginSize: function (n) {
299 if (this.isTransparent) {
300 return 0;
303 var o = this.options;
304 if (o.compact && o.blend) {
305 var smBlendedMarginSizes = [1, 0];
306 return smBlendedMarginSizes[n];
307 } else if (o.compact) {
308 var compactMarginSizes = [2, 1];
309 return compactMarginSizes[n];
310 } else if (o.blend) {
311 var blendedMarginSizes = [3, 2, 1, 0];
312 return blendedMarginSizes[n];
313 } else {
314 var marginSizes = [5, 3, 2, 1];
315 return marginSizes[n];
319 _borderSize: function (n) {
320 var o = this.options;
321 var borderSizes;
322 if (o.compact && (o.blend || this.isTransparent)) {
323 return 1;
324 } else if (o.compact) {
325 borderSizes = [1, 0];
326 } else if (o.blend) {
327 borderSizes = [2, 1, 1, 1];
328 } else if (o.border) {
329 borderSizes = [0, 2, 0, 0];
330 } else if (this.isTransparent) {
331 borderSizes = [5, 3, 2, 1];
332 } else {
333 return 0;
335 return borderSizes[n];
338 _hasString: function (str) {
339 for (var i = 1; i< arguments.length; i++) {
340 if (str.indexOf(arguments[i]) != -1) {
341 return true;
344 return false;
347 _isTopRounded: function () {
348 return this._hasString(this.options.corners,
349 "all", "top", "tl", "tr"
353 _isBottomRounded: function () {
354 return this._hasString(this.options.corners,
355 "all", "bottom", "bl", "br"
359 _hasSingleTextChild: function (el) {
360 return (el.childNodes.length == 1 && el.childNodes[0].nodeType == 3);
364 /** @id MochiKit.Visual.roundElement */
365 MochiKit.Visual.roundElement = function (e, options) {
366 new MochiKit.Visual._RoundCorners(e, options);
369 /** @id MochiKit.Visual.roundClass */
370 MochiKit.Visual.roundClass = function (tagName, className, options) {
371 var elements = MochiKit.DOM.getElementsByTagAndClassName(
372 tagName, className
374 for (var i = 0; i < elements.length; i++) {
375 MochiKit.Visual.roundElement(elements[i], options);
379 /** @id MochiKit.Visual.tagifyText */
380 MochiKit.Visual.tagifyText = function (element, /* optional */tagifyStyle) {
381 /***
383 Change a node text to character in tags.
385 @param tagifyStyle: the style to apply to character nodes, default to
386 'position: relative'.
388 ***/
389 var tagifyStyle = tagifyStyle || 'position:relative';
390 if (/MSIE/.test(navigator.userAgent)) {
391 tagifyStyle += ';zoom:1';
393 element = MochiKit.DOM.getElement(element);
394 var ma = MochiKit.Base.map;
395 ma(function (child) {
396 if (child.nodeType == 3) {
397 ma(function (character) {
398 element.insertBefore(
399 MochiKit.DOM.SPAN({style: tagifyStyle},
400 character == ' ' ? String.fromCharCode(160) : character), child);
401 }, child.nodeValue.split(''));
402 MochiKit.DOM.removeElement(child);
404 }, element.childNodes);
407 /** @id MochiKit.Visual.forceRerendering */
408 MochiKit.Visual.forceRerendering = function (element) {
409 try {
410 element = MochiKit.DOM.getElement(element);
411 var n = document.createTextNode(' ');
412 element.appendChild(n);
413 element.removeChild(n);
414 } catch(e) {
418 /** @id MochiKit.Visual.multiple */
419 MochiKit.Visual.multiple = function (elements, effect, /* optional */options) {
420 /***
422 Launch the same effect subsequently on given elements.
424 ***/
425 options = MochiKit.Base.update({
426 speed: 0.1, delay: 0.0
427 }, options || {});
428 var masterDelay = options.delay;
429 var index = 0;
430 MochiKit.Base.map(function (innerelement) {
431 options.delay = index * options.speed + masterDelay;
432 new effect(innerelement, options);
433 index += 1;
434 }, elements);
437 MochiKit.Visual.PAIRS = {
438 'slide': ['slideDown', 'slideUp'],
439 'blind': ['blindDown', 'blindUp'],
440 'appear': ['appear', 'fade'],
441 'size': ['grow', 'shrink']
444 /** @id MochiKit.Visual.toggle */
445 MochiKit.Visual.toggle = function (element, /* optional */effect, /* optional */options) {
446 /***
448 Toggle an item between two state depending of its visibility, making
449 a effect between these states. Default effect is 'appear', can be
450 'slide' or 'blind'.
452 ***/
453 element = MochiKit.DOM.getElement(element);
454 effect = (effect || 'appear').toLowerCase();
455 options = MochiKit.Base.update({
456 queue: {position: 'end', scope: (element.id || 'global'), limit: 1}
457 }, options || {});
458 var v = MochiKit.Visual;
459 v[element.style.display != 'none' ?
460 v.PAIRS[effect][1] : v.PAIRS[effect][0]](element, options);
463 /***
465 Transitions: define functions calculating variations depending of a position.
467 ***/
469 MochiKit.Visual.Transitions = {}
471 /** @id MochiKit.Visual.Transitions.linear */
472 MochiKit.Visual.Transitions.linear = function (pos) {
473 return pos;
476 /** @id MochiKit.Visual.Transitions.sinoidal */
477 MochiKit.Visual.Transitions.sinoidal = function (pos) {
478 return (-Math.cos(pos*Math.PI)/2) + 0.5;
481 /** @id MochiKit.Visual.Transitions.reverse */
482 MochiKit.Visual.Transitions.reverse = function (pos) {
483 return 1 - pos;
486 /** @id MochiKit.Visual.Transitions.flicker */
487 MochiKit.Visual.Transitions.flicker = function (pos) {
488 return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
491 /** @id MochiKit.Visual.Transitions.wobble */
492 MochiKit.Visual.Transitions.wobble = function (pos) {
493 return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
496 /** @id MochiKit.Visual.Transitions.pulse */
497 MochiKit.Visual.Transitions.pulse = function (pos) {
498 return (Math.floor(pos*10) % 2 == 0 ?
499 (pos*10 - Math.floor(pos*10)) : 1 - (pos*10 - Math.floor(pos*10)));
502 /** @id MochiKit.Visual.Transitions.none */
503 MochiKit.Visual.Transitions.none = function (pos) {
504 return 0;
507 /** @id MochiKit.Visual.Transitions.full */
508 MochiKit.Visual.Transitions.full = function (pos) {
509 return 1;
512 /***
514 Core effects
516 ***/
518 MochiKit.Visual.ScopedQueue = function () {
519 this.__init__();
522 MochiKit.Base.update(MochiKit.Visual.ScopedQueue.prototype, {
523 __init__: function () {
524 this.effects = [];
525 this.interval = null;
528 /** @id MochiKit.Visual.ScopedQueue.prototype.add */
529 add: function (effect) {
530 var timestamp = new Date().getTime();
532 var position = (typeof(effect.options.queue) == 'string') ?
533 effect.options.queue : effect.options.queue.position;
535 var ma = MochiKit.Base.map;
536 switch (position) {
537 case 'front':
538 // move unstarted effects after this effect
539 ma(function (e) {
540 if (e.state == 'idle') {
541 e.startOn += effect.finishOn;
542 e.finishOn += effect.finishOn;
544 }, this.effects);
545 break;
546 case 'end':
547 var finish;
548 // start effect after last queued effect has finished
549 ma(function (e) {
550 var i = e.finishOn;
551 if (i >= (finish || i)) {
552 finish = i;
554 }, this.effects);
555 timestamp = finish || timestamp;
556 break;
557 case 'break':
558 ma(function (e) {
559 e.finalize();
560 }, this.effects);
561 break;
564 effect.startOn += timestamp;
565 effect.finishOn += timestamp;
566 if (!effect.options.queue.limit ||
567 this.effects.length < effect.options.queue.limit) {
568 this.effects.push(effect);
571 if (!this.interval) {
572 this.interval = this.startLoop(MochiKit.Base.bind(this.loop, this),
573 40);
577 /** @id MochiKit.Visual.ScopedQueue.prototype.startLoop */
578 startLoop: function (func, interval) {
579 return setInterval(func, interval)
582 /** @id MochiKit.Visual.ScopedQueue.prototype.remove */
583 remove: function (effect) {
584 this.effects = MochiKit.Base.filter(function (e) {
585 return e != effect;
586 }, this.effects);
587 if (this.effects.length == 0) {
588 this.stopLoop(this.interval);
589 this.interval = null;
593 /** @id MochiKit.Visual.ScopedQueue.prototype.stopLoop */
594 stopLoop: function (interval) {
595 clearInterval(interval)
598 /** @id MochiKit.Visual.ScopedQueue.prototype.loop */
599 loop: function () {
600 var timePos = new Date().getTime();
601 MochiKit.Base.map(function (effect) {
602 effect.loop(timePos);
603 }, this.effects);
607 MochiKit.Visual.Queues = {
608 instances: {},
610 get: function (queueName) {
611 if (typeof(queueName) != 'string') {
612 return queueName;
615 if (!this.instances[queueName]) {
616 this.instances[queueName] = new MochiKit.Visual.ScopedQueue();
618 return this.instances[queueName];
622 MochiKit.Visual.Queue = MochiKit.Visual.Queues.get('global');
624 MochiKit.Visual.DefaultOptions = {
625 transition: MochiKit.Visual.Transitions.sinoidal,
626 duration: 1.0, // seconds
627 fps: 25.0, // max. 25fps due to MochiKit.Visual.Queue implementation
628 sync: false, // true for combining
629 from: 0.0,
630 to: 1.0,
631 delay: 0.0,
632 queue: 'parallel'
635 MochiKit.Visual.Base = function () {};
637 MochiKit.Visual.Base.prototype = {
638 /***
640 Basic class for all Effects. Define a looping mechanism called for each step
641 of an effect. Don't instantiate it, only subclass it.
643 ***/
645 __class__ : MochiKit.Visual.Base,
647 /** @id MochiKit.Visual.Base.prototype.start */
648 start: function (options) {
649 var v = MochiKit.Visual;
650 this.options = MochiKit.Base.setdefault(options || {},
651 v.DefaultOptions);
652 this.currentFrame = 0;
653 this.state = 'idle';
654 this.startOn = this.options.delay*1000;
655 this.finishOn = this.startOn + (this.options.duration*1000);
656 this.event('beforeStart');
657 if (!this.options.sync) {
658 v.Queues.get(typeof(this.options.queue) == 'string' ?
659 'global' : this.options.queue.scope).add(this);
663 /** @id MochiKit.Visual.Base.prototype.loop */
664 loop: function (timePos) {
665 if (timePos >= this.startOn) {
666 if (timePos >= this.finishOn) {
667 return this.finalize();
669 var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
670 var frame =
671 Math.round(pos * this.options.fps * this.options.duration);
672 if (frame > this.currentFrame) {
673 this.render(pos);
674 this.currentFrame = frame;
679 /** @id MochiKit.Visual.Base.prototype.render */
680 render: function (pos) {
681 if (this.state == 'idle') {
682 this.state = 'running';
683 this.event('beforeSetup');
684 this.setup();
685 this.event('afterSetup');
687 if (this.state == 'running') {
688 if (this.options.transition) {
689 pos = this.options.transition(pos);
691 pos *= (this.options.to - this.options.from);
692 pos += this.options.from;
693 this.event('beforeUpdate');
694 this.update(pos);
695 this.event('afterUpdate');
699 /** @id MochiKit.Visual.Base.prototype.cancel */
700 cancel: function () {
701 if (!this.options.sync) {
702 MochiKit.Visual.Queues.get(typeof(this.options.queue) == 'string' ?
703 'global' : this.options.queue.scope).remove(this);
705 this.state = 'finished';
708 /** @id MochiKit.Visual.Base.prototype.finalize */
709 finalize: function () {
710 this.render(1.0);
711 this.cancel();
712 this.event('beforeFinish');
713 this.finish();
714 this.event('afterFinish');
717 setup: function () {
720 finish: function () {
723 update: function (position) {
726 /** @id MochiKit.Visual.Base.prototype.event */
727 event: function (eventName) {
728 if (this.options[eventName + 'Internal']) {
729 this.options[eventName + 'Internal'](this);
731 if (this.options[eventName]) {
732 this.options[eventName](this);
736 /** @id MochiKit.Visual.Base.prototype.repr */
737 repr: function () {
738 return '[' + this.__class__.NAME + ', options:' +
739 MochiKit.Base.repr(this.options) + ']';
743 /** @id MochiKit.Visual.Parallel */
744 MochiKit.Visual.Parallel = function (effects, options) {
745 this.__init__(effects, options);
748 MochiKit.Visual.Parallel.prototype = new MochiKit.Visual.Base();
750 MochiKit.Base.update(MochiKit.Visual.Parallel.prototype, {
751 /***
753 Run multiple effects at the same time.
755 ***/
756 __init__: function (effects, options) {
757 this.effects = effects || [];
758 this.start(options);
761 /** @id MochiKit.Visual.Parallel.prototype.update */
762 update: function (position) {
763 MochiKit.Base.map(function (effect) {
764 effect.render(position);
765 }, this.effects);
768 /** @id MochiKit.Visual.Parallel.prototype.finish */
769 finish: function () {
770 MochiKit.Base.map(function (effect) {
771 effect.finalize();
772 }, this.effects);
776 /** @id MochiKit.Visual.Opacity */
777 MochiKit.Visual.Opacity = function (element, options) {
778 this.__init__(element, options);
781 MochiKit.Visual.Opacity.prototype = new MochiKit.Visual.Base();
783 MochiKit.Base.update(MochiKit.Visual.Opacity.prototype, {
784 /***
786 Change the opacity of an element.
788 @param options: 'from' and 'to' change the starting and ending opacities.
789 Must be between 0.0 and 1.0. Default to current opacity and 1.0.
791 ***/
792 __init__: function (element, /* optional */options) {
793 var b = MochiKit.Base;
794 var s = MochiKit.Style;
795 this.element = MochiKit.DOM.getElement(element);
796 // make this work on IE on elements without 'layout'
797 if (this.element.currentStyle &&
798 (!this.element.currentStyle.hasLayout)) {
799 s.setStyle(this.element, {zoom: 1});
801 options = b.update({
802 from: s.getOpacity(this.element) || 0.0,
803 to: 1.0
804 }, options || {});
805 this.start(options);
808 /** @id MochiKit.Visual.Opacity.prototype.update */
809 update: function (position) {
810 MochiKit.Style.setOpacity(this.element, position);
814 /** @id MochiKit.Visual.Opacity.prototype.Move */
815 MochiKit.Visual.Move = function (element, options) {
816 this.__init__(element, options);
819 MochiKit.Visual.Move.prototype = new MochiKit.Visual.Base();
821 MochiKit.Base.update(MochiKit.Visual.Move.prototype, {
822 /***
824 Move an element between its current position to a defined position
826 @param options: 'x' and 'y' for final positions, default to 0, 0.
828 ***/
829 __init__: function (element, /* optional */options) {
830 this.element = MochiKit.DOM.getElement(element);
831 options = MochiKit.Base.update({
832 x: 0,
833 y: 0,
834 mode: 'relative'
835 }, options || {});
836 this.start(options);
839 /** @id MochiKit.Visual.Move.prototype.setup */
840 setup: function () {
841 // Bug in Opera: Opera returns the 'real' position of a static element
842 // or relative element that does not have top/left explicitly set.
843 // ==> Always set top and left for position relative elements in your
844 // stylesheets (to 0 if you do not need them)
845 MochiKit.DOM.makePositioned(this.element);
847 var s = this.element.style;
848 var originalVisibility = s.visibility;
849 var originalDisplay = s.display;
850 if (originalDisplay == 'none') {
851 s.visibility = 'hidden';
852 s.display = '';
855 this.originalLeft = parseFloat(MochiKit.Style.getStyle(this.element, 'left') || '0');
856 this.originalTop = parseFloat(MochiKit.Style.getStyle(this.element, 'top') || '0');
858 if (this.options.mode == 'absolute') {
859 // absolute movement, so we need to calc deltaX and deltaY
860 this.options.x -= this.originalLeft;
861 this.options.y -= this.originalTop;
863 if (originalDisplay == 'none') {
864 s.visibility = originalVisibility;
865 s.display = originalDisplay;
869 /** @id MochiKit.Visual.Move.prototype.update */
870 update: function (position) {
871 MochiKit.Style.setStyle(this.element, {
872 left: Math.round(this.options.x * position + this.originalLeft) + 'px',
873 top: Math.round(this.options.y * position + this.originalTop) + 'px'
878 /** @id MochiKit.Visual.Scale */
879 MochiKit.Visual.Scale = function (element, percent, options) {
880 this.__init__(element, percent, options);
883 MochiKit.Visual.Scale.prototype = new MochiKit.Visual.Base();
885 MochiKit.Base.update(MochiKit.Visual.Scale.prototype, {
886 /***
888 Change the size of an element.
890 @param percent: final_size = percent*original_size
892 @param options: several options changing scale behaviour
894 ***/
895 __init__: function (element, percent, /* optional */options) {
896 this.element = MochiKit.DOM.getElement(element)
897 options = MochiKit.Base.update({
898 scaleX: true,
899 scaleY: true,
900 scaleContent: true,
901 scaleFromCenter: false,
902 scaleMode: 'box', // 'box' or 'contents' or {} with provided values
903 scaleFrom: 100.0,
904 scaleTo: percent
905 }, options || {});
906 this.start(options);
909 /** @id MochiKit.Visual.Scale.prototype.setup */
910 setup: function () {
911 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
912 this.elementPositioning = MochiKit.Style.getStyle(this.element,
913 'position');
915 var ma = MochiKit.Base.map;
916 var b = MochiKit.Base.bind;
917 this.originalStyle = {};
918 ma(b(function (k) {
919 this.originalStyle[k] = this.element.style[k];
920 }, this), ['top', 'left', 'width', 'height', 'fontSize']);
922 this.originalTop = this.element.offsetTop;
923 this.originalLeft = this.element.offsetLeft;
925 var fontSize = MochiKit.Style.getStyle(this.element,
926 'font-size') || '100%';
927 ma(b(function (fontSizeType) {
928 if (fontSize.indexOf(fontSizeType) > 0) {
929 this.fontSize = parseFloat(fontSize);
930 this.fontSizeType = fontSizeType;
932 }, this), ['em', 'px', '%']);
934 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
936 if (/^content/.test(this.options.scaleMode)) {
937 this.dims = [this.element.scrollHeight, this.element.scrollWidth];
938 } else if (this.options.scaleMode == 'box') {
939 this.dims = [this.element.offsetHeight, this.element.offsetWidth];
940 } else {
941 this.dims = [this.options.scaleMode.originalHeight,
942 this.options.scaleMode.originalWidth];
946 /** @id MochiKit.Visual.Scale.prototype.update */
947 update: function (position) {
948 var currentScale = (this.options.scaleFrom/100.0) +
949 (this.factor * position);
950 if (this.options.scaleContent && this.fontSize) {
951 MochiKit.Style.setStyle(this.element, {
952 fontSize: this.fontSize * currentScale + this.fontSizeType
955 this.setDimensions(this.dims[0] * currentScale,
956 this.dims[1] * currentScale);
959 /** @id MochiKit.Visual.Scale.prototype.finish */
960 finish: function () {
961 if (this.restoreAfterFinish) {
962 MochiKit.Style.setStyle(this.element, this.originalStyle);
966 /** @id MochiKit.Visual.Scale.prototype.setDimensions */
967 setDimensions: function (height, width) {
968 var d = {};
969 var r = Math.round;
970 if (/MSIE/.test(navigator.userAgent)) {
971 r = Math.ceil;
973 if (this.options.scaleX) {
974 d.width = r(width) + 'px';
976 if (this.options.scaleY) {
977 d.height = r(height) + 'px';
979 if (this.options.scaleFromCenter) {
980 var topd = (height - this.dims[0])/2;
981 var leftd = (width - this.dims[1])/2;
982 if (this.elementPositioning == 'absolute') {
983 if (this.options.scaleY) {
984 d.top = this.originalTop - topd + 'px';
986 if (this.options.scaleX) {
987 d.left = this.originalLeft - leftd + 'px';
989 } else {
990 if (this.options.scaleY) {
991 d.top = -topd + 'px';
993 if (this.options.scaleX) {
994 d.left = -leftd + 'px';
998 MochiKit.Style.setStyle(this.element, d);
1002 /** @id MochiKit.Visual.Highlight */
1003 MochiKit.Visual.Highlight = function (element, options) {
1004 this.__init__(element, options);
1007 MochiKit.Visual.Highlight.prototype = new MochiKit.Visual.Base();
1009 MochiKit.Base.update(MochiKit.Visual.Highlight.prototype, {
1010 /***
1012 Highlight an item of the page.
1014 @param options: 'startcolor' for choosing highlighting color, default
1015 to '#ffff99'.
1017 ***/
1018 __init__: function (element, /* optional */options) {
1019 this.element = MochiKit.DOM.getElement(element);
1020 options = MochiKit.Base.update({
1021 startcolor: '#ffff99'
1022 }, options || {});
1023 this.start(options);
1026 /** @id MochiKit.Visual.Highlight.prototype.setup */
1027 setup: function () {
1028 var b = MochiKit.Base;
1029 var s = MochiKit.Style;
1030 // Prevent executing on elements not in the layout flow
1031 if (s.getStyle(this.element, 'display') == 'none') {
1032 this.cancel();
1033 return;
1035 // Disable background image during the effect
1036 this.oldStyle = {
1037 backgroundImage: s.getStyle(this.element, 'background-image')
1039 s.setStyle(this.element, {
1040 backgroundImage: 'none'
1043 if (!this.options.endcolor) {
1044 this.options.endcolor =
1045 MochiKit.Color.Color.fromBackground(this.element).toHexString();
1047 if (b.isUndefinedOrNull(this.options.restorecolor)) {
1048 this.options.restorecolor = s.getStyle(this.element,
1049 'background-color');
1051 // init color calculations
1052 this._base = b.map(b.bind(function (i) {
1053 return parseInt(
1054 this.options.startcolor.slice(i*2 + 1, i*2 + 3), 16);
1055 }, this), [0, 1, 2]);
1056 this._delta = b.map(b.bind(function (i) {
1057 return parseInt(this.options.endcolor.slice(i*2 + 1, i*2 + 3), 16)
1058 - this._base[i];
1059 }, this), [0, 1, 2]);
1062 /** @id MochiKit.Visual.Highlight.prototype.update */
1063 update: function (position) {
1064 var m = '#';
1065 MochiKit.Base.map(MochiKit.Base.bind(function (i) {
1066 m += MochiKit.Color.toColorPart(Math.round(this._base[i] +
1067 this._delta[i]*position));
1068 }, this), [0, 1, 2]);
1069 MochiKit.Style.setStyle(this.element, {
1070 backgroundColor: m
1074 /** @id MochiKit.Visual.Highlight.prototype.finish */
1075 finish: function () {
1076 MochiKit.Style.setStyle(this.element,
1077 MochiKit.Base.update(this.oldStyle, {
1078 backgroundColor: this.options.restorecolor
1079 }));
1083 /** @id MochiKit.Visual.ScrollTo */
1084 MochiKit.Visual.ScrollTo = function (element, options) {
1085 this.__init__(element, options);
1088 MochiKit.Visual.ScrollTo.prototype = new MochiKit.Visual.Base();
1090 MochiKit.Base.update(MochiKit.Visual.ScrollTo.prototype, {
1091 /***
1093 Scroll to an element in the page.
1095 ***/
1096 __init__: function (element, /* optional */options) {
1097 this.element = MochiKit.DOM.getElement(element);
1098 this.start(options || {});
1101 /** @id MochiKit.Visual.ScrollTo.prototype.setup */
1102 setup: function () {
1103 var p = MochiKit.Position;
1104 p.prepare();
1105 var offsets = p.cumulativeOffset(this.element);
1106 if (this.options.offset) {
1107 offsets.y += this.options.offset;
1109 var max;
1110 if (window.innerHeight) {
1111 max = window.innerHeight - window.height;
1112 } else if (document.documentElement &&
1113 document.documentElement.clientHeight) {
1114 max = document.documentElement.clientHeight -
1115 document.body.scrollHeight;
1116 } else if (document.body) {
1117 max = document.body.clientHeight - document.body.scrollHeight;
1119 this.scrollStart = p.windowOffset.y;
1120 this.delta = (offsets.y > max ? max : offsets.y) - this.scrollStart;
1123 /** @id MochiKit.Visual.ScrollTo.prototype.update */
1124 update: function (position) {
1125 var p = MochiKit.Position;
1126 p.prepare();
1127 window.scrollTo(p.windowOffset.x, this.scrollStart + (position * this.delta));
1131 /***
1133 Combination effects.
1135 ***/
1137 /** @id MochiKit.Visual.fade */
1138 MochiKit.Visual.fade = function (element, /* optional */ options) {
1139 /***
1141 Fade a given element: change its opacity and hide it in the end.
1143 @param options: 'to' and 'from' to change opacity.
1145 ***/
1146 var s = MochiKit.Style;
1147 var oldOpacity = MochiKit.DOM.getElement(element).style.opacity || '';
1148 options = MochiKit.Base.update({
1149 from: s.getOpacity(element) || 1.0,
1150 to: 0.0,
1151 afterFinishInternal: function (effect) {
1152 if (effect.options.to !== 0) {
1153 return;
1155 s.hideElement(effect.element);
1156 s.setStyle(effect.element, {opacity: oldOpacity});
1158 }, options || {});
1159 return new MochiKit.Visual.Opacity(element, options);
1162 /** @id MochiKit.Visual.appear */
1163 MochiKit.Visual.appear = function (element, /* optional */ options) {
1164 /***
1166 Make an element appear.
1168 @param options: 'to' and 'from' to change opacity.
1170 ***/
1171 var s = MochiKit.Style;
1172 var v = MochiKit.Visual;
1173 options = MochiKit.Base.update({
1174 from: (s.getStyle(element, 'display') == 'none' ? 0.0 :
1175 s.getOpacity(element) || 0.0),
1176 to: 1.0,
1177 // force Safari to render floated elements properly
1178 afterFinishInternal: function (effect) {
1179 v.forceRerendering(effect.element);
1181 beforeSetupInternal: function (effect) {
1182 s.setOpacity(effect.element, effect.options.from);
1183 s.showElement(effect.element);
1185 }, options || {});
1186 return new v.Opacity(element, options);
1189 /** @id MochiKit.Visual.puff */
1190 MochiKit.Visual.puff = function (element, /* optional */ options) {
1191 /***
1193 'Puff' an element: grow it to double size, fading it and make it hidden.
1195 ***/
1196 var s = MochiKit.Style;
1197 var v = MochiKit.Visual;
1198 element = MochiKit.DOM.getElement(element);
1199 var oldStyle = {
1200 opacity: element.style.opacity || '',
1201 position: s.getStyle(element, 'position'),
1202 top: element.style.top,
1203 left: element.style.left,
1204 width: element.style.width,
1205 height: element.style.height
1207 options = MochiKit.Base.update({
1208 beforeSetupInternal: function (effect) {
1209 MochiKit.Position.absolutize(effect.effects[0].element)
1211 afterFinishInternal: function (effect) {
1212 s.hideElement(effect.effects[0].element);
1213 s.setStyle(effect.effects[0].element, oldStyle);
1215 }, options || {});
1216 return new v.Parallel(
1217 [new v.Scale(element, 200,
1218 {sync: true, scaleFromCenter: true,
1219 scaleContent: true, restoreAfterFinish: true}),
1220 new v.Opacity(element, {sync: true, to: 0.0 })],
1221 options);
1224 /** @id MochiKit.Visual.blindUp */
1225 MochiKit.Visual.blindUp = function (element, /* optional */ options) {
1226 /***
1228 Blind an element up: change its vertical size to 0.
1230 ***/
1231 var d = MochiKit.DOM;
1232 element = d.getElement(element);
1233 var elemClip = d.makeClipping(element);
1234 options = MochiKit.Base.update({
1235 scaleContent: false,
1236 scaleX: false,
1237 restoreAfterFinish: true,
1238 afterFinishInternal: function (effect) {
1239 MochiKit.Style.hideElement(effect.element);
1240 d.undoClipping(effect.element, elemClip);
1242 }, options || {});
1244 return new MochiKit.Visual.Scale(element, 0, options);
1247 /** @id MochiKit.Visual.blindDown */
1248 MochiKit.Visual.blindDown = function (element, /* optional */ options) {
1249 /***
1251 Blind an element down: restore its vertical size.
1253 ***/
1254 var d = MochiKit.DOM;
1255 var s = MochiKit.Style;
1256 element = d.getElement(element);
1257 var elementDimensions = s.getElementDimensions(element);
1258 var elemClip;
1259 options = MochiKit.Base.update({
1260 scaleContent: false,
1261 scaleX: false,
1262 scaleFrom: 0,
1263 scaleMode: {originalHeight: elementDimensions.h,
1264 originalWidth: elementDimensions.w},
1265 restoreAfterFinish: true,
1266 afterSetupInternal: function (effect) {
1267 elemClip = d.makeClipping(effect.element);
1268 s.setStyle(effect.element, {height: '0px'});
1269 s.showElement(effect.element);
1271 afterFinishInternal: function (effect) {
1272 d.undoClipping(effect.element, elemClip);
1274 }, options || {});
1275 return new MochiKit.Visual.Scale(element, 100, options);
1278 /** @id MochiKit.Visual.switchOff */
1279 MochiKit.Visual.switchOff = function (element, /* optional */ options) {
1280 /***
1282 Apply a switch-off-like effect.
1284 ***/
1285 var d = MochiKit.DOM;
1286 element = d.getElement(element);
1287 var oldOpacity = element.style.opacity || '';
1288 var elemClip;
1289 var options = MochiKit.Base.update({
1290 duration: 0.3,
1291 scaleFromCenter: true,
1292 scaleX: false,
1293 scaleContent: false,
1294 restoreAfterFinish: true,
1295 beforeSetupInternal: function (effect) {
1296 d.makePositioned(effect.element);
1297 elemClip = d.makeClipping(effect.element);
1299 afterFinishInternal: function (effect) {
1300 MochiKit.Style.hideElement(effect.element);
1301 d.undoClipping(effect.element, elemClip);
1302 d.undoPositioned(effect.element);
1303 MochiKit.Style.setStyle(effect.element, {opacity: oldOpacity});
1305 }, options || {});
1306 var v = MochiKit.Visual;
1307 return new v.appear(element, {
1308 duration: 0.4,
1309 from: 0,
1310 transition: v.Transitions.flicker,
1311 afterFinishInternal: function (effect) {
1312 new v.Scale(effect.element, 1, options)
1317 /** @id MochiKit.Visual.dropOut */
1318 MochiKit.Visual.dropOut = function (element, /* optional */ options) {
1319 /***
1321 Make an element fall and disappear.
1323 ***/
1324 var d = MochiKit.DOM;
1325 var s = MochiKit.Style;
1326 element = d.getElement(element);
1327 var oldStyle = {
1328 top: s.getStyle(element, 'top'),
1329 left: s.getStyle(element, 'left'),
1330 opacity: element.style.opacity || ''
1333 options = MochiKit.Base.update({
1334 duration: 0.5,
1335 beforeSetupInternal: function (effect) {
1336 d.makePositioned(effect.effects[0].element);
1338 afterFinishInternal: function (effect) {
1339 s.hideElement(effect.effects[0].element);
1340 d.undoPositioned(effect.effects[0].element);
1341 s.setStyle(effect.effects[0].element, oldStyle);
1343 }, options || {});
1344 var v = MochiKit.Visual;
1345 return new v.Parallel(
1346 [new v.Move(element, {x: 0, y: 100, sync: true}),
1347 new v.Opacity(element, {sync: true, to: 0.0})],
1348 options);
1351 /** @id MochiKit.Visual.shake */
1352 MochiKit.Visual.shake = function (element, /* optional */ options) {
1353 /***
1355 Move an element from left to right several times.
1357 ***/
1358 var d = MochiKit.DOM;
1359 var v = MochiKit.Visual;
1360 var s = MochiKit.Style;
1361 element = d.getElement(element);
1362 options = MochiKit.Base.update({
1363 x: -20,
1364 y: 0,
1365 duration: 0.05,
1366 afterFinishInternal: function (effect) {
1367 d.undoPositioned(effect.element);
1368 s.setStyle(effect.element, oldStyle);
1370 }, options || {});
1371 var oldStyle = {
1372 top: s.getStyle(element, 'top'),
1373 left: s.getStyle(element, 'left') };
1374 return new v.Move(element,
1375 {x: 20, y: 0, duration: 0.05, afterFinishInternal: function (effect) {
1376 new v.Move(effect.element,
1377 {x: -40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1378 new v.Move(effect.element,
1379 {x: 40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1380 new v.Move(effect.element,
1381 {x: -40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1382 new v.Move(effect.element,
1383 {x: 40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1384 new v.Move(effect.element, options
1385 ) }}) }}) }}) }}) }});
1388 /** @id MochiKit.Visual.slideDown */
1389 MochiKit.Visual.slideDown = function (element, /* optional */ options) {
1390 /***
1392 Slide an element down.
1393 It needs to have the content of the element wrapped in a container
1394 element with fixed height.
1396 ***/
1397 var d = MochiKit.DOM;
1398 var b = MochiKit.Base;
1399 var s = MochiKit.Style;
1400 element = d.getElement(element);
1401 if (!element.firstChild) {
1402 throw "MochiKit.Visual.slideDown must be used on a element with a child";
1404 d.removeEmptyTextNodes(element);
1405 var oldInnerBottom = s.getStyle(element.firstChild, 'bottom') || 0;
1406 var elementDimensions = s.getElementDimensions(element);
1407 var elemClip;
1408 options = b.update({
1409 scaleContent: false,
1410 scaleX: false,
1411 scaleFrom: 0,
1412 scaleMode: {originalHeight: elementDimensions.h,
1413 originalWidth: elementDimensions.w},
1414 restoreAfterFinish: true,
1415 afterSetupInternal: function (effect) {
1416 d.makePositioned(effect.element);
1417 d.makePositioned(effect.element.firstChild);
1418 if (/Opera/.test(navigator.userAgent)) {
1419 s.setStyle(effect.element, {top: ''});
1421 elemClip = d.makeClipping(effect.element);
1422 s.setStyle(effect.element, {height: '0px'});
1423 s.showElement(effect.element);
1425 afterUpdateInternal: function (effect) {
1426 s.setStyle(effect.element.firstChild,
1427 {bottom: (effect.dims[0] - effect.element.clientHeight) + 'px'})
1429 afterFinishInternal: function (effect) {
1430 d.undoClipping(effect.element, elemClip);
1431 // IE will crash if child is undoPositioned first
1432 if (/MSIE/.test(navigator.userAgent)) {
1433 d.undoPositioned(effect.element);
1434 d.undoPositioned(effect.element.firstChild);
1435 } else {
1436 d.undoPositioned(effect.element.firstChild);
1437 d.undoPositioned(effect.element);
1439 s.setStyle(effect.element.firstChild,
1440 {bottom: oldInnerBottom});
1442 }, options || {});
1444 return new MochiKit.Visual.Scale(element, 100, options);
1447 /** @id MochiKit.Visual.slideUp */
1448 MochiKit.Visual.slideUp = function (element, /* optional */ options) {
1449 /***
1451 Slide an element up.
1452 It needs to have the content of the element wrapped in a container
1453 element with fixed height.
1455 ***/
1456 var d = MochiKit.DOM;
1457 var b = MochiKit.Base;
1458 var s = MochiKit.Style;
1459 element = d.getElement(element);
1460 if (!element.firstChild) {
1461 throw "MochiKit.Visual.slideUp must be used on a element with a child";
1463 d.removeEmptyTextNodes(element);
1464 var oldInnerBottom = s.getStyle(element.firstChild, 'bottom');
1465 var elemClip;
1466 options = b.update({
1467 scaleContent: false,
1468 scaleX: false,
1469 scaleMode: 'box',
1470 scaleFrom: 100,
1471 restoreAfterFinish: true,
1472 beforeStartInternal: function (effect) {
1473 d.makePositioned(effect.element);
1474 d.makePositioned(effect.element.firstChild);
1475 if (/Opera/.test(navigator.userAgent)) {
1476 s.setStyle(effect.element, {top: ''});
1478 elemClip = d.makeClipping(effect.element);
1479 s.showElement(effect.element);
1481 afterUpdateInternal: function (effect) {
1482 s.setStyle(effect.element.firstChild,
1483 {bottom: (effect.dims[0] - effect.element.clientHeight) + 'px'});
1485 afterFinishInternal: function (effect) {
1486 s.hideElement(effect.element);
1487 d.undoClipping(effect.element, elemClip);
1488 d.undoPositioned(effect.element.firstChild);
1489 d.undoPositioned(effect.element);
1490 s.setStyle(effect.element.firstChild, {bottom: oldInnerBottom});
1492 }, options || {});
1493 return new MochiKit.Visual.Scale(element, 0, options);
1496 // Bug in opera makes the TD containing this element expand for a instance
1497 // after finish
1498 /** @id MochiKit.Visual.squish */
1499 MochiKit.Visual.squish = function (element, /* optional */ options) {
1500 /***
1502 Reduce an element and make it disappear.
1504 ***/
1505 var d = MochiKit.DOM;
1506 var b = MochiKit.Base;
1507 var elemClip;
1508 options = b.update({
1509 restoreAfterFinish: true,
1510 beforeSetupInternal: function (effect) {
1511 elemClip = d.makeClipping(effect.element);
1513 afterFinishInternal: function (effect) {
1514 MochiKit.Style.hideElement(effect.element);
1515 d.undoClipping(effect.element, elemClip);
1517 }, options || {});
1519 return new MochiKit.Visual.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, options);
1522 /** @id MochiKit.Visual.grow */
1523 MochiKit.Visual.grow = function (element, /* optional */ options) {
1524 /***
1526 Grow an element to its original size. Make it zero-sized before
1527 if necessary.
1529 ***/
1530 var d = MochiKit.DOM;
1531 var v = MochiKit.Visual;
1532 var s = MochiKit.Style;
1533 element = d.getElement(element);
1534 options = MochiKit.Base.update({
1535 direction: 'center',
1536 moveTransition: v.Transitions.sinoidal,
1537 scaleTransition: v.Transitions.sinoidal,
1538 opacityTransition: v.Transitions.full
1539 }, options || {});
1540 var oldStyle = {
1541 top: element.style.top,
1542 left: element.style.left,
1543 height: element.style.height,
1544 width: element.style.width,
1545 opacity: element.style.opacity || ''
1548 var dims = s.getElementDimensions(element);
1549 var initialMoveX, initialMoveY;
1550 var moveX, moveY;
1552 switch (options.direction) {
1553 case 'top-left':
1554 initialMoveX = initialMoveY = moveX = moveY = 0;
1555 break;
1556 case 'top-right':
1557 initialMoveX = dims.w;
1558 initialMoveY = moveY = 0;
1559 moveX = -dims.w;
1560 break;
1561 case 'bottom-left':
1562 initialMoveX = moveX = 0;
1563 initialMoveY = dims.h;
1564 moveY = -dims.h;
1565 break;
1566 case 'bottom-right':
1567 initialMoveX = dims.w;
1568 initialMoveY = dims.h;
1569 moveX = -dims.w;
1570 moveY = -dims.h;
1571 break;
1572 case 'center':
1573 initialMoveX = dims.w / 2;
1574 initialMoveY = dims.h / 2;
1575 moveX = -dims.w / 2;
1576 moveY = -dims.h / 2;
1577 break;
1580 var optionsParallel = MochiKit.Base.update({
1581 beforeSetupInternal: function (effect) {
1582 s.setStyle(effect.effects[0].element, {height: '0px'});
1583 s.showElement(effect.effects[0].element);
1585 afterFinishInternal: function (effect) {
1586 d.undoClipping(effect.effects[0].element);
1587 d.undoPositioned(effect.effects[0].element);
1588 s.setStyle(effect.effects[0].element, oldStyle);
1590 }, options || {});
1592 return new v.Move(element, {
1593 x: initialMoveX,
1594 y: initialMoveY,
1595 duration: 0.01,
1596 beforeSetupInternal: function (effect) {
1597 s.hideElement(effect.element);
1598 d.makeClipping(effect.element);
1599 d.makePositioned(effect.element);
1601 afterFinishInternal: function (effect) {
1602 new v.Parallel(
1603 [new v.Opacity(effect.element, {
1604 sync: true, to: 1.0, from: 0.0,
1605 transition: options.opacityTransition
1607 new v.Move(effect.element, {
1608 x: moveX, y: moveY, sync: true,
1609 transition: options.moveTransition
1611 new v.Scale(effect.element, 100, {
1612 scaleMode: {originalHeight: dims.h,
1613 originalWidth: dims.w},
1614 sync: true,
1615 scaleFrom: /Opera/.test(navigator.userAgent) ? 1 : 0,
1616 transition: options.scaleTransition,
1617 restoreAfterFinish: true
1619 ], optionsParallel
1625 /** @id MochiKit.Visual.shrink */
1626 MochiKit.Visual.shrink = function (element, /* optional */ options) {
1627 /***
1629 Shrink an element and make it disappear.
1631 ***/
1632 var d = MochiKit.DOM;
1633 var v = MochiKit.Visual;
1634 var s = MochiKit.Style;
1635 element = d.getElement(element);
1636 options = MochiKit.Base.update({
1637 direction: 'center',
1638 moveTransition: v.Transitions.sinoidal,
1639 scaleTransition: v.Transitions.sinoidal,
1640 opacityTransition: v.Transitions.none
1641 }, options || {});
1642 var oldStyle = {
1643 top: element.style.top,
1644 left: element.style.left,
1645 height: element.style.height,
1646 width: element.style.width,
1647 opacity: element.style.opacity || ''
1650 var dims = s.getElementDimensions(element);
1651 var moveX, moveY;
1653 switch (options.direction) {
1654 case 'top-left':
1655 moveX = moveY = 0;
1656 break;
1657 case 'top-right':
1658 moveX = dims.w;
1659 moveY = 0;
1660 break;
1661 case 'bottom-left':
1662 moveX = 0;
1663 moveY = dims.h;
1664 break;
1665 case 'bottom-right':
1666 moveX = dims.w;
1667 moveY = dims.h;
1668 break;
1669 case 'center':
1670 moveX = dims.w / 2;
1671 moveY = dims.h / 2;
1672 break;
1674 var elemClip;
1676 var optionsParallel = MochiKit.Base.update({
1677 beforeStartInternal: function (effect) {
1678 elemClip = d.makePositioned(effect.effects[0].element);
1679 d.makeClipping(effect.effects[0].element);
1681 afterFinishInternal: function (effect) {
1682 s.hideElement(effect.effects[0].element);
1683 d.undoClipping(effect.effects[0].element, elemClip);
1684 d.undoPositioned(effect.effects[0].element);
1685 s.setStyle(effect.effects[0].element, oldStyle);
1687 }, options || {});
1689 return new v.Parallel(
1690 [new v.Opacity(element, {
1691 sync: true, to: 0.0, from: 1.0,
1692 transition: options.opacityTransition
1694 new v.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, {
1695 sync: true, transition: options.scaleTransition,
1696 restoreAfterFinish: true
1698 new v.Move(element, {
1699 x: moveX, y: moveY, sync: true, transition: options.moveTransition
1701 ], optionsParallel
1705 /** @id MochiKit.Visual.pulsate */
1706 MochiKit.Visual.pulsate = function (element, /* optional */ options) {
1707 /***
1709 Pulse an element between appear/fade.
1711 ***/
1712 var d = MochiKit.DOM;
1713 var v = MochiKit.Visual;
1714 var b = MochiKit.Base;
1715 var oldOpacity = d.getElement(element).style.opacity || '';
1716 options = b.update({
1717 duration: 3.0,
1718 from: 0,
1719 afterFinishInternal: function (effect) {
1720 MochiKit.Style.setStyle(effect.element, {opacity: oldOpacity});
1722 }, options || {});
1723 var transition = options.transition || v.Transitions.sinoidal;
1724 var reverser = b.bind(function (pos) {
1725 return transition(1 - v.Transitions.pulse(pos));
1726 }, transition);
1727 b.bind(reverser, transition);
1728 return new v.Opacity(element, b.update({
1729 transition: reverser}, options));
1732 /** @id MochiKit.Visual.fold */
1733 MochiKit.Visual.fold = function (element, /* optional */ options) {
1734 /***
1736 Fold an element, first vertically, then horizontally.
1738 ***/
1739 var d = MochiKit.DOM;
1740 var v = MochiKit.Visual;
1741 var s = MochiKit.Style;
1742 element = d.getElement(element);
1743 var oldStyle = {
1744 top: element.style.top,
1745 left: element.style.left,
1746 width: element.style.width,
1747 height: element.style.height
1749 var elemClip = d.makeClipping(element);
1750 options = MochiKit.Base.update({
1751 scaleContent: false,
1752 scaleX: false,
1753 afterFinishInternal: function (effect) {
1754 new v.Scale(element, 1, {
1755 scaleContent: false,
1756 scaleY: false,
1757 afterFinishInternal: function (effect) {
1758 s.hideElement(effect.element);
1759 d.undoClipping(effect.element, elemClip);
1760 s.setStyle(effect.element, oldStyle);
1764 }, options || {});
1765 return new v.Scale(element, 5, options);
1769 // Compatibility with MochiKit 1.0
1770 MochiKit.Visual.Color = MochiKit.Color.Color;
1771 MochiKit.Visual.getElementsComputedStyle = MochiKit.DOM.computedStyle;
1773 /* end of Rico adaptation */
1775 MochiKit.Visual.__new__ = function () {
1776 var m = MochiKit.Base;
1778 m.nameFunctions(this);
1780 this.EXPORT_TAGS = {
1781 ":common": this.EXPORT,
1782 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
1787 MochiKit.Visual.EXPORT = [
1788 "roundElement",
1789 "roundClass",
1790 "tagifyText",
1791 "multiple",
1792 "toggle",
1793 "Base",
1794 "Parallel",
1795 "Opacity",
1796 "Move",
1797 "Scale",
1798 "Highlight",
1799 "ScrollTo",
1800 "fade",
1801 "appear",
1802 "puff",
1803 "blindUp",
1804 "blindDown",
1805 "switchOff",
1806 "dropOut",
1807 "shake",
1808 "slideDown",
1809 "slideUp",
1810 "squish",
1811 "grow",
1812 "shrink",
1813 "pulsate",
1814 "fold"
1817 MochiKit.Visual.EXPORT_OK = [
1818 "PAIRS"
1821 MochiKit.Visual.__new__();
1823 MochiKit.Base._exportSymbols(this, MochiKit.Visual);