oops, Dynarch.Calendar was used by CXGN.Calendar
[cxgn-jslib.git] / MochiKit / Visual.js
blob914b3b49a93a98613da9cfd9e4eb6c2da9cad725
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');
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", []);
28 try {
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') {
34 throw "";
36 } catch (e) {
37 throw "MochiKit.Visual depends on MochiKit.Base, MochiKit.DOM, MochiKit.Style, MochiKit.Position and MochiKit.Color!";
40 if (typeof(MochiKit.Visual) == "undefined") {
41 MochiKit.Visual = {};
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) {
59 e = this._doWrap(e);
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) {
87 return e;
89 var style = doc.defaultView.getComputedStyle(e, null);
90 if (typeof(style) === "undefined" || style === null) {
91 return e;
93 var wrapper = MochiKit.DOM.DIV({"style": {
94 display: "block",
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
101 padding: "0px"
103 paddingRight: "0px",
104 paddingLeft: "0px"
106 }});
107 wrapper.innerHTML = e.innerHTML;
108 e.innerHTML = "";
109 e.appendChild(wrapper);
110 return e;
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++) {
136 corner.appendChild(
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--) {
147 corner.appendChild(
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);
193 return slice;
196 _setOptions: function (options) {
197 this.options = {
198 corners: "all",
199 color: "fromElement",
200 bgColor: "fromParent",
201 blend: true,
202 border: false,
203 compact: false,
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")) {
214 return "";
217 var has_tl = (corners.indexOf("tl") != -1);
218 var has_tr = (corners.indexOf("tr") != -1);
219 if (has_tl && has_tr) {
220 return "";
222 if (has_tl) {
223 return "left";
225 if (has_tr) {
226 return "right";
228 return "";
231 _whichSideBottom: function () {
232 var corners = this.options.corners;
233 if (this._hasString(corners, "all", "bottom")) {
234 return "";
237 var has_bl = (corners.indexOf('bl') != -1);
238 var has_br = (corners.indexOf('br') != -1);
239 if (has_bl && has_br) {
240 return "";
242 if (has_bl) {
243 return "left";
245 if (has_br) {
246 return "right";
248 return "";
251 _borderColor: function (color, bgColor) {
252 if (color == "transparent") {
253 return bgColor;
254 } else if (this.options.border) {
255 return this.options.border;
256 } else if (this.options.blend) {
257 return bgColor.blendedColor(color);
259 return "";
263 _setMargin: function (el, n, corners) {
264 var marginSize = this._marginSize(n) + "px";
265 var whichSide = (
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";
276 } else {
277 style.marginLeft = marginSize;
278 style.marginRight = marginSize;
282 _setBorder: function (el, n, corners) {
283 var borderSize = this._borderSize(n) + "px";
284 var whichSide = (
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";
295 } else {
296 style.borderLeftWidth = borderSize;
297 style.borderRightWidth = borderSize;
301 _marginSize: function (n) {
302 if (this.isTransparent) {
303 return 0;
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];
316 } else {
317 var marginSizes = [5, 3, 2, 1];
318 return marginSizes[n];
322 _borderSize: function (n) {
323 var o = this.options;
324 var borderSizes;
325 if (o.compact && (o.blend || this.isTransparent)) {
326 return 1;
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];
335 } else {
336 return 0;
338 return borderSizes[n];
341 _hasString: function (str) {
342 for (var i = 1; i< arguments.length; i++) {
343 if (str.indexOf(arguments[i]) != -1) {
344 return true;
347 return false;
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(
375 tagName, className
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) {
384 /***
386 Change a node text to character in tags.
388 @param tagifyStyle: the style to apply to character nodes, default to
389 'position: relative'.
391 ***/
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) {
412 try {
413 element = MochiKit.DOM.getElement(element);
414 var n = document.createTextNode(' ');
415 element.appendChild(n);
416 element.removeChild(n);
417 } catch(e) {
421 /** @id MochiKit.Visual.multiple */
422 MochiKit.Visual.multiple = function (elements, effect, /* optional */options) {
423 /***
425 Launch the same effect subsequently on given elements.
427 ***/
428 options = MochiKit.Base.update({
429 speed: 0.1, delay: 0.0
430 }, options || {});
431 var masterDelay = options.delay;
432 var index = 0;
433 MochiKit.Base.map(function (innerelement) {
434 options.delay = index * options.speed + masterDelay;
435 new effect(innerelement, options);
436 index += 1;
437 }, elements);
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) {
449 /***
451 Toggle an item between two state depending of its visibility, making
452 a effect between these states. Default effect is 'appear', can be
453 'slide' or 'blind'.
455 ***/
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}
460 }, options || {});
461 var v = MochiKit.Visual;
462 v[element.style.display != 'none' ?
463 v.PAIRS[effect][1] : v.PAIRS[effect][0]](element, options);
466 /***
468 Transitions: define functions calculating variations depending of a position.
470 ***/
472 MochiKit.Visual.Transitions = {};
474 /** @id MochiKit.Visual.Transitions.linear */
475 MochiKit.Visual.Transitions.linear = function (pos) {
476 return 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) {
486 return 1 - 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) {
507 return 0;
510 /** @id MochiKit.Visual.Transitions.full */
511 MochiKit.Visual.Transitions.full = function (pos) {
512 return 1;
515 /***
517 Core effects
519 ***/
521 MochiKit.Visual.ScopedQueue = function () {
522 var cls = arguments.callee;
523 if (!(this instanceof cls)) {
524 return new cls();
526 this.__init__();
529 MochiKit.Base.update(MochiKit.Visual.ScopedQueue.prototype, {
530 __init__: function () {
531 this.effects = [];
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;
543 switch (position) {
544 case 'front':
545 // move unstarted effects after this effect
546 ma(function (e) {
547 if (e.state == 'idle') {
548 e.startOn += effect.finishOn;
549 e.finishOn += effect.finishOn;
551 }, this.effects);
552 break;
553 case 'end':
554 var finish;
555 // start effect after last queued effect has finished
556 ma(function (e) {
557 var i = e.finishOn;
558 if (i >= (finish || i)) {
559 finish = i;
561 }, this.effects);
562 timestamp = finish || timestamp;
563 break;
564 case 'break':
565 ma(function (e) {
566 e.finalize();
567 }, this.effects);
568 break;
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),
580 40);
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) {
592 return e != effect;
593 }, this.effects);
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 */
606 loop: function () {
607 var timePos = new Date().getTime();
608 MochiKit.Base.map(function (effect) {
609 effect.loop(timePos);
610 }, this.effects);
614 MochiKit.Visual.Queues = {
615 instances: {},
617 get: function (queueName) {
618 if (typeof(queueName) != 'string') {
619 return queueName;
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
636 from: 0.0,
637 to: 1.0,
638 delay: 0.0,
639 queue: 'parallel'
642 MochiKit.Visual.Base = function () {};
644 MochiKit.Visual.Base.prototype = {
645 /***
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.
650 ***/
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 || {},
658 v.DefaultOptions);
659 this.currentFrame = 0;
660 this.state = 'idle';
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);
677 var frame =
678 Math.round(pos * this.options.fps * this.options.duration);
679 if (frame > this.currentFrame) {
680 this.render(pos);
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');
691 this.setup();
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');
701 this.update(pos);
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 () {
717 this.render(1.0);
718 this.cancel();
719 this.event('beforeFinish');
720 this.finish();
721 this.event('afterFinish');
724 setup: function () {
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 */
744 repr: function () {
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, {
763 /***
765 Run multiple effects at the same time.
767 ***/
769 __class__ : MochiKit.Visual.Parallel,
771 __init__: function (effects, options) {
772 this.effects = effects || [];
773 this.start(options);
776 /** @id MochiKit.Visual.Parallel.prototype.update */
777 update: function (position) {
778 MochiKit.Base.map(function (effect) {
779 effect.render(position);
780 }, this.effects);
783 /** @id MochiKit.Visual.Parallel.prototype.finish */
784 finish: function () {
785 MochiKit.Base.map(function (effect) {
786 effect.finalize();
787 }, this.effects);
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, {
803 /***
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.
810 ***/
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});
823 options = b.update({
824 from: s.getStyle(this.element, 'opacity') || 0.0,
825 to: 1.0
826 }, options || {});
827 this.start(options);
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, {
848 /***
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.
854 ***/
856 __class__ : MochiKit.Visual.Move,
858 __init__: function (element, /* optional */options) {
859 this.element = MochiKit.DOM.getElement(element);
860 options = MochiKit.Base.update({
861 x: 0,
862 y: 0,
863 mode: 'relative'
864 }, options || {});
865 this.start(options);
868 /** @id MochiKit.Visual.Move.prototype.setup */
869 setup: function () {
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';
881 s.display = '';
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, {
919 /***
921 Change the size of an element.
923 @param percent: final_size = percent*original_size
925 @param options: several options changing scale behaviour
927 ***/
929 __class__ : MochiKit.Visual.Scale,
931 __init__: function (element, percent, /* optional */options) {
932 this.element = MochiKit.DOM.getElement(element);
933 options = MochiKit.Base.update({
934 scaleX: true,
935 scaleY: true,
936 scaleContent: true,
937 scaleFromCenter: false,
938 scaleMode: 'box', // 'box' or 'contents' or {} with provided values
939 scaleFrom: 100.0,
940 scaleTo: percent
941 }, options || {});
942 this.start(options);
945 /** @id MochiKit.Visual.Scale.prototype.setup */
946 setup: function () {
947 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
948 this.elementPositioning = MochiKit.Style.getStyle(this.element,
949 'position');
951 var ma = MochiKit.Base.map;
952 var b = MochiKit.Base.bind;
953 this.originalStyle = {};
954 ma(b(function (k) {
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];
976 } else {
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) {
1004 var d = {};
1005 var r = Math.round;
1006 if (/MSIE/.test(navigator.userAgent)) {
1007 r = Math.ceil;
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';
1025 } else {
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, {
1050 /***
1052 Highlight an item of the page.
1054 @param options: 'startcolor' for choosing highlighting color, default
1055 to '#ffff99'.
1057 ***/
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'
1065 }, options || {});
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') {
1075 this.cancel();
1076 return;
1078 // Disable background image during the effect
1079 this.oldStyle = {
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) {
1096 return parseInt(
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)
1101 - this._base[i];
1102 }, this), [0, 1, 2]);
1105 /** @id MochiKit.Visual.Highlight.prototype.update */
1106 update: function (position) {
1107 var m = '#';
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, {
1113 backgroundColor: m
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
1122 }));
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, {
1138 /***
1140 Scroll to an element in the page.
1142 ***/
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;
1154 p.prepare();
1155 var offsets = p.cumulativeOffset(this.element);
1156 if (this.options.offset) {
1157 offsets.y += this.options.offset;
1159 var max;
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;
1176 p.prepare();
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, {
1194 /***
1196 Morph effect: make a transformation from current style to the given style,
1197 automatically making a transition between the two.
1199 ***/
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 = {};
1213 this.styleEnd = {};
1214 this.units = {};
1215 var value, unit;
1216 for (var s in style) {
1217 value = style[s];
1218 s = b.camelize(s);
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;
1229 } else {
1230 var c = MochiKit.Color.Color;
1231 value = c.fromString(value);
1232 if (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) {
1239 return parseInt(
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) {
1243 return parseInt(
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) {
1253 var value;
1254 for (var s in this.styleStart) {
1255 if (this.units[s] == "color") {
1256 var m = '#';
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;
1264 } else {
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;
1272 /***
1274 Combination effects.
1276 ***/
1278 /** @id MochiKit.Visual.fade */
1279 MochiKit.Visual.fade = function (element, /* optional */ options) {
1280 /***
1282 Fade a given element: change its opacity and hide it in the end.
1284 @param options: 'to' and 'from' to change opacity.
1286 ***/
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,
1291 to: 0.0,
1292 afterFinishInternal: function (effect) {
1293 if (effect.options.to !== 0) {
1294 return;
1296 s.hideElement(effect.element);
1297 s.setStyle(effect.element, {'opacity': oldOpacity});
1299 }, options || {});
1300 return new MochiKit.Visual.Opacity(element, options);
1303 /** @id MochiKit.Visual.appear */
1304 MochiKit.Visual.appear = function (element, /* optional */ options) {
1305 /***
1307 Make an element appear.
1309 @param options: 'to' and 'from' to change opacity.
1311 ***/
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),
1317 to: 1.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);
1326 }, options || {});
1327 return new v.Opacity(element, options);
1330 /** @id MochiKit.Visual.puff */
1331 MochiKit.Visual.puff = function (element, /* optional */ options) {
1332 /***
1334 'Puff' an element: grow it to double size, fading it and make it hidden.
1336 ***/
1337 var s = MochiKit.Style;
1338 var v = MochiKit.Visual;
1339 element = MochiKit.DOM.getElement(element);
1340 var oldStyle = {
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);
1356 scaleContent: true,
1357 scaleFromCenter: true
1358 }, options || {});
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 })],
1364 options);
1367 /** @id MochiKit.Visual.blindUp */
1368 MochiKit.Visual.blindUp = function (element, /* optional */ options) {
1369 /***
1371 Blind an element up: change its vertical size to 0.
1373 ***/
1374 var d = MochiKit.DOM;
1375 element = d.getElement(element);
1376 var elemClip = d.makeClipping(element);
1377 options = MochiKit.Base.update({
1378 scaleContent: false,
1379 scaleX: false,
1380 restoreAfterFinish: true,
1381 afterFinishInternal: function (effect) {
1382 MochiKit.Style.hideElement(effect.element);
1383 d.undoClipping(effect.element, elemClip);
1385 }, options || {});
1387 return new MochiKit.Visual.Scale(element, 0, options);
1390 /** @id MochiKit.Visual.blindDown */
1391 MochiKit.Visual.blindDown = function (element, /* optional */ options) {
1392 /***
1394 Blind an element down: restore its vertical size.
1396 ***/
1397 var d = MochiKit.DOM;
1398 var s = MochiKit.Style;
1399 element = d.getElement(element);
1400 var elementDimensions = s.getElementDimensions(element);
1401 var elemClip;
1402 options = MochiKit.Base.update({
1403 scaleContent: false,
1404 scaleX: false,
1405 scaleFrom: 0,
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);
1417 }, options || {});
1418 return new MochiKit.Visual.Scale(element, 100, options);
1421 /** @id MochiKit.Visual.switchOff */
1422 MochiKit.Visual.switchOff = function (element, /* optional */ options) {
1423 /***
1425 Apply a switch-off-like effect.
1427 ***/
1428 var d = MochiKit.DOM;
1429 element = d.getElement(element);
1430 var oldOpacity = MochiKit.Style.getStyle(element, 'opacity');
1431 var elemClip;
1432 options = MochiKit.Base.update({
1433 duration: 0.3,
1434 scaleFromCenter: true,
1435 scaleX: false,
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});
1448 }, options || {});
1449 var v = MochiKit.Visual;
1450 return new v.appear(element, {
1451 duration: 0.4,
1452 from: 0,
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) {
1462 /***
1464 Make an element fall and disappear.
1466 ***/
1467 var d = MochiKit.DOM;
1468 var s = MochiKit.Style;
1469 element = d.getElement(element);
1470 var oldStyle = {
1471 top: s.getStyle(element, 'top'),
1472 left: s.getStyle(element, 'left'),
1473 opacity: s.getStyle(element, 'opacity')
1476 options = MochiKit.Base.update({
1477 duration: 0.5,
1478 distance: 100,
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);
1487 }, options || {});
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})],
1492 options);
1495 /** @id MochiKit.Visual.shake */
1496 MochiKit.Visual.shake = function (element, /* optional */ options) {
1497 /***
1499 Move an element from left to right several times.
1501 ***/
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({
1507 x: -20,
1508 y: 0,
1509 duration: 0.05,
1510 afterFinishInternal: function (effect) {
1511 d.undoPositioned(effect.element);
1512 s.setStyle(effect.element, oldStyle);
1514 }, options || {});
1515 var 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) {
1534 /***
1536 Slide an element down.
1537 It needs to have the content of the element wrapped in a container
1538 element with fixed height.
1540 ***/
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);
1551 var elemClip;
1552 options = b.update({
1553 scaleContent: false,
1554 scaleX: false,
1555 scaleFrom: 0,
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);
1579 } else {
1580 d.undoPositioned(effect.element.firstChild);
1581 d.undoPositioned(effect.element);
1583 s.setStyle(effect.element.firstChild,
1584 {bottom: oldInnerBottom});
1586 }, options || {});
1588 return new MochiKit.Visual.Scale(element, 100, options);
1591 /** @id MochiKit.Visual.slideUp */
1592 MochiKit.Visual.slideUp = function (element, /* optional */ options) {
1593 /***
1595 Slide an element up.
1596 It needs to have the content of the element wrapped in a container
1597 element with fixed height.
1599 ***/
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');
1609 var elemClip;
1610 options = b.update({
1611 scaleContent: false,
1612 scaleX: false,
1613 scaleMode: 'box',
1614 scaleFrom: 100,
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});
1636 }, options || {});
1637 return new MochiKit.Visual.Scale(element, 0, options);
1640 // Bug in opera makes the TD containing this element expand for a instance
1641 // after finish
1642 /** @id MochiKit.Visual.squish */
1643 MochiKit.Visual.squish = function (element, /* optional */ options) {
1644 /***
1646 Reduce an element and make it disappear.
1648 ***/
1649 var d = MochiKit.DOM;
1650 var b = MochiKit.Base;
1651 var elemClip;
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);
1661 }, options || {});
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) {
1668 /***
1670 Grow an element to its original size. Make it zero-sized before
1671 if necessary.
1673 ***/
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,
1683 scaleContent: true,
1684 scaleFromCenter: false
1685 }, options || {});
1686 var oldStyle = {
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;
1696 var moveX, moveY;
1698 switch (options.direction) {
1699 case 'top-left':
1700 initialMoveX = initialMoveY = moveX = moveY = 0;
1701 break;
1702 case 'top-right':
1703 initialMoveX = dims.w;
1704 initialMoveY = moveY = 0;
1705 moveX = -dims.w;
1706 break;
1707 case 'bottom-left':
1708 initialMoveX = moveX = 0;
1709 initialMoveY = dims.h;
1710 moveY = -dims.h;
1711 break;
1712 case 'bottom-right':
1713 initialMoveX = dims.w;
1714 initialMoveY = dims.h;
1715 moveX = -dims.w;
1716 moveY = -dims.h;
1717 break;
1718 case 'center':
1719 initialMoveX = dims.w / 2;
1720 initialMoveY = dims.h / 2;
1721 moveX = -dims.w / 2;
1722 moveY = -dims.h / 2;
1723 break;
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);
1736 }, options || {});
1738 return new v.Move(element, {
1739 x: initialMoveX,
1740 y: initialMoveY,
1741 duration: 0.01,
1742 beforeSetupInternal: function (effect) {
1743 s.hideElement(effect.element);
1744 d.makeClipping(effect.element);
1745 d.makePositioned(effect.element);
1747 afterFinishInternal: function (effect) {
1748 new v.Parallel(
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},
1760 sync: true,
1761 scaleFrom: /Opera/.test(navigator.userAgent) ? 1 : 0,
1762 transition: options.scaleTransition,
1763 scaleContent: options.scaleContent,
1764 scaleFromCenter: options.scaleFromCenter,
1765 restoreAfterFinish: true
1767 ], optionsParallel
1773 /** @id MochiKit.Visual.shrink */
1774 MochiKit.Visual.shrink = function (element, /* optional */ options) {
1775 /***
1777 Shrink an element and make it disappear.
1779 ***/
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,
1789 scaleContent: true,
1790 scaleFromCenter: false
1791 }, options || {});
1792 var oldStyle = {
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);
1801 var moveX, moveY;
1803 switch (options.direction) {
1804 case 'top-left':
1805 moveX = moveY = 0;
1806 break;
1807 case 'top-right':
1808 moveX = dims.w;
1809 moveY = 0;
1810 break;
1811 case 'bottom-left':
1812 moveX = 0;
1813 moveY = dims.h;
1814 break;
1815 case 'bottom-right':
1816 moveX = dims.w;
1817 moveY = dims.h;
1818 break;
1819 case 'center':
1820 moveX = dims.w / 2;
1821 moveY = dims.h / 2;
1822 break;
1824 var elemClip;
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);
1837 }, options || {});
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
1853 ], optionsParallel
1857 /** @id MochiKit.Visual.pulsate */
1858 MochiKit.Visual.pulsate = function (element, /* optional */ options) {
1859 /***
1861 Pulse an element between appear/fade.
1863 ***/
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({
1869 duration: 3.0,
1870 from: 0,
1871 afterFinishInternal: function (effect) {
1872 MochiKit.Style.setStyle(effect.element, {'opacity': oldOpacity});
1874 }, options || {});
1875 var transition = options.transition || v.Transitions.sinoidal;
1876 var reverser = b.bind(function (pos) {
1877 return transition(1 - v.Transitions.pulse(pos));
1878 }, transition);
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) {
1886 /***
1888 Fold an element, first vertically, then horizontally.
1890 ***/
1891 var d = MochiKit.DOM;
1892 var v = MochiKit.Visual;
1893 var s = MochiKit.Style;
1894 element = d.getElement(element);
1895 var oldStyle = {
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,
1904 scaleX: false,
1905 afterFinishInternal: function (effect) {
1906 new v.Scale(element, 1, {
1907 scaleContent: false,
1908 scaleY: false,
1909 afterFinishInternal: function (effect) {
1910 s.hideElement(effect.element);
1911 d.undoClipping(effect.element, elemClip);
1912 s.setStyle(effect.element, oldStyle);
1916 }, options || {});
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 = [
1940 "roundElement",
1941 "roundClass",
1942 "tagifyText",
1943 "multiple",
1944 "toggle",
1945 "Parallel",
1946 "Opacity",
1947 "Move",
1948 "Scale",
1949 "Highlight",
1950 "ScrollTo",
1951 "Morph",
1952 "fade",
1953 "appear",
1954 "puff",
1955 "blindUp",
1956 "blindDown",
1957 "switchOff",
1958 "dropOut",
1959 "shake",
1960 "slideDown",
1961 "slideUp",
1962 "squish",
1963 "grow",
1964 "shrink",
1965 "pulsate",
1966 "fold"
1969 MochiKit.Visual.EXPORT_OK = [
1970 "Base",
1971 "PAIRS"
1974 MochiKit.Visual.__new__();
1976 MochiKit.Base._exportSymbols(this, MochiKit.Visual);