Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /***
3 MochiKit.Visual 1.4.2
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
7 (c) 2005 Bob Ippolito and others. All rights Reserved.
9 ***/
11 MochiKit.Base._deps('Visual', ['Base', 'DOM', 'Style', 'Color', 'Position']);
13 MochiKit.Visual.NAME = "MochiKit.Visual";
14 MochiKit.Visual.VERSION = "1.4.2";
16 MochiKit.Visual.__repr__ = function () {
17 return "[" + this.NAME + " " + this.VERSION + "]";
18 };
20 MochiKit.Visual.toString = function () {
21 return this.__repr__();
22 };
24 MochiKit.Visual._RoundCorners = function (e, options) {
25 e = MochiKit.DOM.getElement(e);
26 this._setOptions(options);
27 if (this.options.__unstable__wrapElement) {
28 e = this._doWrap(e);
29 }
31 var color = this.options.color;
32 var C = MochiKit.Color.Color;
33 if (this.options.color === "fromElement") {
34 color = C.fromBackground(e);
35 } else if (!(color instanceof C)) {
36 color = C.fromString(color);
37 }
38 this.isTransparent = (color.asRGB().a <= 0);
40 var bgColor = this.options.bgColor;
41 if (this.options.bgColor === "fromParent") {
42 bgColor = C.fromBackground(e.offsetParent);
43 } else if (!(bgColor instanceof C)) {
44 bgColor = C.fromString(bgColor);
45 }
47 this._roundCornersImpl(e, color, bgColor);
48 };
50 MochiKit.Visual._RoundCorners.prototype = {
51 _doWrap: function (e) {
52 var parent = e.parentNode;
53 var doc = MochiKit.DOM.currentDocument();
54 if (typeof(doc.defaultView) === "undefined"
55 || doc.defaultView === null) {
56 return e;
57 }
58 var style = doc.defaultView.getComputedStyle(e, null);
59 if (typeof(style) === "undefined" || style === null) {
60 return e;
61 }
62 var wrapper = MochiKit.DOM.DIV({"style": {
63 display: "block",
64 // convert padding to margin
65 marginTop: style.getPropertyValue("padding-top"),
66 marginRight: style.getPropertyValue("padding-right"),
67 marginBottom: style.getPropertyValue("padding-bottom"),
68 marginLeft: style.getPropertyValue("padding-left"),
69 // remove padding so the rounding looks right
70 padding: "0px"
71 /*
72 paddingRight: "0px",
73 paddingLeft: "0px"
74 */
75 }});
76 wrapper.innerHTML = e.innerHTML;
77 e.innerHTML = "";
78 e.appendChild(wrapper);
79 return e;
80 },
82 _roundCornersImpl: function (e, color, bgColor) {
83 if (this.options.border) {
84 this._renderBorder(e, bgColor);
85 }
86 if (this._isTopRounded()) {
87 this._roundTopCorners(e, color, bgColor);
88 }
89 if (this._isBottomRounded()) {
90 this._roundBottomCorners(e, color, bgColor);
91 }
92 },
94 _renderBorder: function (el, bgColor) {
95 var borderValue = "1px solid " + this._borderColor(bgColor);
96 var borderL = "border-left: " + borderValue;
97 var borderR = "border-right: " + borderValue;
98 var style = "style='" + borderL + ";" + borderR + "'";
99 el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>";
100 },
102 _roundTopCorners: function (el, color, bgColor) {
103 var corner = this._createCorner(bgColor);
104 for (var i = 0; i < this.options.numSlices; i++) {
105 corner.appendChild(
106 this._createCornerSlice(color, bgColor, i, "top")
107 );
108 }
109 el.style.paddingTop = 0;
110 el.insertBefore(corner, el.firstChild);
111 },
113 _roundBottomCorners: function (el, color, bgColor) {
114 var corner = this._createCorner(bgColor);
115 for (var i = (this.options.numSlices - 1); i >= 0; i--) {
116 corner.appendChild(
117 this._createCornerSlice(color, bgColor, i, "bottom")
118 );
119 }
120 el.style.paddingBottom = 0;
121 el.appendChild(corner);
122 },
124 _createCorner: function (bgColor) {
125 var dom = MochiKit.DOM;
126 return dom.DIV({style: {backgroundColor: bgColor.toString()}});
127 },
129 _createCornerSlice: function (color, bgColor, n, position) {
130 var slice = MochiKit.DOM.SPAN();
132 var inStyle = slice.style;
133 inStyle.backgroundColor = color.toString();
134 inStyle.display = "block";
135 inStyle.height = "1px";
136 inStyle.overflow = "hidden";
137 inStyle.fontSize = "1px";
139 var borderColor = this._borderColor(color, bgColor);
140 if (this.options.border && n === 0) {
141 inStyle.borderTopStyle = "solid";
142 inStyle.borderTopWidth = "1px";
143 inStyle.borderLeftWidth = "0px";
144 inStyle.borderRightWidth = "0px";
145 inStyle.borderBottomWidth = "0px";
146 // assumes css compliant box model
147 inStyle.height = "0px";
148 inStyle.borderColor = borderColor.toString();
149 } else if (borderColor) {
150 inStyle.borderColor = borderColor.toString();
151 inStyle.borderStyle = "solid";
152 inStyle.borderWidth = "0px 1px";
153 }
155 if (!this.options.compact && (n == (this.options.numSlices - 1))) {
156 inStyle.height = "2px";
157 }
159 this._setMargin(slice, n, position);
160 this._setBorder(slice, n, position);
162 return slice;
163 },
165 _setOptions: function (options) {
166 this.options = {
167 corners: "all",
168 color: "fromElement",
169 bgColor: "fromParent",
170 blend: true,
171 border: false,
172 compact: false,
173 __unstable__wrapElement: false
174 };
175 MochiKit.Base.update(this.options, options);
177 this.options.numSlices = (this.options.compact ? 2 : 4);
178 },
180 _whichSideTop: function () {
181 var corners = this.options.corners;
182 if (this._hasString(corners, "all", "top")) {
183 return "";
184 }
186 var has_tl = (corners.indexOf("tl") != -1);
187 var has_tr = (corners.indexOf("tr") != -1);
188 if (has_tl && has_tr) {
189 return "";
190 }
191 if (has_tl) {
192 return "left";
193 }
194 if (has_tr) {
195 return "right";
196 }
197 return "";
198 },
200 _whichSideBottom: function () {
201 var corners = this.options.corners;
202 if (this._hasString(corners, "all", "bottom")) {
203 return "";
204 }
206 var has_bl = (corners.indexOf('bl') != -1);
207 var has_br = (corners.indexOf('br') != -1);
208 if (has_bl && has_br) {
209 return "";
210 }
211 if (has_bl) {
212 return "left";
213 }
214 if (has_br) {
215 return "right";
216 }
217 return "";
218 },
220 _borderColor: function (color, bgColor) {
221 if (color == "transparent") {
222 return bgColor;
223 } else if (this.options.border) {
224 return this.options.border;
225 } else if (this.options.blend) {
226 return bgColor.blendedColor(color);
227 }
228 return "";
229 },
232 _setMargin: function (el, n, corners) {
233 var marginSize = this._marginSize(n) + "px";
234 var whichSide = (
235 corners == "top" ? this._whichSideTop() : this._whichSideBottom()
236 );
237 var style = el.style;
239 if (whichSide == "left") {
240 style.marginLeft = marginSize;
241 style.marginRight = "0px";
242 } else if (whichSide == "right") {
243 style.marginRight = marginSize;
244 style.marginLeft = "0px";
245 } else {
246 style.marginLeft = marginSize;
247 style.marginRight = marginSize;
248 }
249 },
251 _setBorder: function (el, n, corners) {
252 var borderSize = this._borderSize(n) + "px";
253 var whichSide = (
254 corners == "top" ? this._whichSideTop() : this._whichSideBottom()
255 );
257 var style = el.style;
258 if (whichSide == "left") {
259 style.borderLeftWidth = borderSize;
260 style.borderRightWidth = "0px";
261 } else if (whichSide == "right") {
262 style.borderRightWidth = borderSize;
263 style.borderLeftWidth = "0px";
264 } else {
265 style.borderLeftWidth = borderSize;
266 style.borderRightWidth = borderSize;
267 }
268 },
270 _marginSize: function (n) {
271 if (this.isTransparent) {
272 return 0;
273 }
275 var o = this.options;
276 if (o.compact && o.blend) {
277 var smBlendedMarginSizes = [1, 0];
278 return smBlendedMarginSizes[n];
279 } else if (o.compact) {
280 var compactMarginSizes = [2, 1];
281 return compactMarginSizes[n];
282 } else if (o.blend) {
283 var blendedMarginSizes = [3, 2, 1, 0];
284 return blendedMarginSizes[n];
285 } else {
286 var marginSizes = [5, 3, 2, 1];
287 return marginSizes[n];
288 }
289 },
291 _borderSize: function (n) {
292 var o = this.options;
293 var borderSizes;
294 if (o.compact && (o.blend || this.isTransparent)) {
295 return 1;
296 } else if (o.compact) {
297 borderSizes = [1, 0];
298 } else if (o.blend) {
299 borderSizes = [2, 1, 1, 1];
300 } else if (o.border) {
301 borderSizes = [0, 2, 0, 0];
302 } else if (this.isTransparent) {
303 borderSizes = [5, 3, 2, 1];
304 } else {
305 return 0;
306 }
307 return borderSizes[n];
308 },
310 _hasString: function (str) {
311 for (var i = 1; i< arguments.length; i++) {
312 if (str.indexOf(arguments[i]) != -1) {
313 return true;
314 }
315 }
316 return false;
317 },
319 _isTopRounded: function () {
320 return this._hasString(this.options.corners,
321 "all", "top", "tl", "tr"
322 );
323 },
325 _isBottomRounded: function () {
326 return this._hasString(this.options.corners,
327 "all", "bottom", "bl", "br"
328 );
329 },
331 _hasSingleTextChild: function (el) {
332 return (el.childNodes.length == 1 && el.childNodes[0].nodeType == 3);
333 }
334 };
336 /** @id MochiKit.Visual.roundElement */
337 MochiKit.Visual.roundElement = function (e, options) {
338 new MochiKit.Visual._RoundCorners(e, options);
339 };
341 /** @id MochiKit.Visual.roundClass */
342 MochiKit.Visual.roundClass = function (tagName, className, options) {
343 var elements = MochiKit.DOM.getElementsByTagAndClassName(
344 tagName, className
345 );
346 for (var i = 0; i < elements.length; i++) {
347 MochiKit.Visual.roundElement(elements[i], options);
348 }
349 };
351 /** @id MochiKit.Visual.tagifyText */
352 MochiKit.Visual.tagifyText = function (element, /* optional */tagifyStyle) {
353 /***
355 Change a node text to character in tags.
357 @param tagifyStyle: the style to apply to character nodes, default to
358 'position: relative'.
360 ***/
361 tagifyStyle = tagifyStyle || 'position:relative';
362 if (/MSIE/.test(navigator.userAgent)) {
363 tagifyStyle += ';zoom:1';
364 }
365 element = MochiKit.DOM.getElement(element);
366 var ma = MochiKit.Base.map;
367 ma(function (child) {
368 if (child.nodeType == 3) {
369 ma(function (character) {
370 element.insertBefore(
371 MochiKit.DOM.SPAN({style: tagifyStyle},
372 character == ' ' ? String.fromCharCode(160) : character), child);
373 }, child.nodeValue.split(''));
374 MochiKit.DOM.removeElement(child);
375 }
376 }, element.childNodes);
377 };
379 /** @id MochiKit.Visual.forceRerendering */
380 MochiKit.Visual.forceRerendering = function (element) {
381 try {
382 element = MochiKit.DOM.getElement(element);
383 var n = document.createTextNode(' ');
384 element.appendChild(n);
385 element.removeChild(n);
386 } catch(e) {
387 }
388 };
390 /** @id MochiKit.Visual.multiple */
391 MochiKit.Visual.multiple = function (elements, effect, /* optional */options) {
392 /***
394 Launch the same effect subsequently on given elements.
396 ***/
397 options = MochiKit.Base.update({
398 speed: 0.1, delay: 0.0
399 }, options);
400 var masterDelay = options.delay;
401 var index = 0;
402 MochiKit.Base.map(function (innerelement) {
403 options.delay = index * options.speed + masterDelay;
404 new effect(innerelement, options);
405 index += 1;
406 }, elements);
407 };
409 MochiKit.Visual.PAIRS = {
410 'slide': ['slideDown', 'slideUp'],
411 'blind': ['blindDown', 'blindUp'],
412 'appear': ['appear', 'fade'],
413 'size': ['grow', 'shrink']
414 };
416 /** @id MochiKit.Visual.toggle */
417 MochiKit.Visual.toggle = function (element, /* optional */effect, /* optional */options) {
418 /***
420 Toggle an item between two state depending of its visibility, making
421 a effect between these states. Default effect is 'appear', can be
422 'slide' or 'blind'.
424 ***/
425 element = MochiKit.DOM.getElement(element);
426 effect = (effect || 'appear').toLowerCase();
427 options = MochiKit.Base.update({
428 queue: {position: 'end', scope: (element.id || 'global'), limit: 1}
429 }, options);
430 var v = MochiKit.Visual;
431 v[MochiKit.Style.getStyle(element, 'display') != 'none' ?
432 v.PAIRS[effect][1] : v.PAIRS[effect][0]](element, options);
433 };
435 /***
437 Transitions: define functions calculating variations depending of a position.
439 ***/
441 MochiKit.Visual.Transitions = {};
443 /** @id MochiKit.Visual.Transitions.linear */
444 MochiKit.Visual.Transitions.linear = function (pos) {
445 return pos;
446 };
448 /** @id MochiKit.Visual.Transitions.sinoidal */
449 MochiKit.Visual.Transitions.sinoidal = function (pos) {
450 return 0.5 - Math.cos(pos*Math.PI)/2;
451 };
453 /** @id MochiKit.Visual.Transitions.reverse */
454 MochiKit.Visual.Transitions.reverse = function (pos) {
455 return 1 - pos;
456 };
458 /** @id MochiKit.Visual.Transitions.flicker */
459 MochiKit.Visual.Transitions.flicker = function (pos) {
460 return 0.25 - Math.cos(pos*Math.PI)/4 + Math.random()/2;
461 };
463 /** @id MochiKit.Visual.Transitions.wobble */
464 MochiKit.Visual.Transitions.wobble = function (pos) {
465 return 0.5 - Math.cos(9*pos*Math.PI)/2;
466 };
468 /** @id MochiKit.Visual.Transitions.pulse */
469 MochiKit.Visual.Transitions.pulse = function (pos, pulses) {
470 if (pulses) {
471 pos *= 2 * pulses;
472 } else {
473 pos *= 10;
474 }
475 var decimals = pos - Math.floor(pos);
476 return (Math.floor(pos) % 2 == 0) ? decimals : 1 - decimals;
477 };
479 /** @id MochiKit.Visual.Transitions.parabolic */
480 MochiKit.Visual.Transitions.parabolic = function (pos) {
481 return pos * pos;
482 };
484 /** @id MochiKit.Visual.Transitions.none */
485 MochiKit.Visual.Transitions.none = function (pos) {
486 return 0;
487 };
489 /** @id MochiKit.Visual.Transitions.full */
490 MochiKit.Visual.Transitions.full = function (pos) {
491 return 1;
492 };
494 /***
496 Core effects
498 ***/
500 MochiKit.Visual.ScopedQueue = function () {
501 var cls = arguments.callee;
502 if (!(this instanceof cls)) {
503 return new cls();
504 }
505 this.__init__();
506 };
508 MochiKit.Base.update(MochiKit.Visual.ScopedQueue.prototype, {
509 __init__: function () {
510 this.effects = [];
511 this.interval = null;
512 },
514 /** @id MochiKit.Visual.ScopedQueue.prototype.add */
515 add: function (effect) {
516 var timestamp = new Date().getTime();
518 var position = (typeof(effect.options.queue) == 'string') ?
519 effect.options.queue : effect.options.queue.position;
521 var ma = MochiKit.Base.map;
522 switch (position) {
523 case 'front':
524 // move unstarted effects after this effect
525 ma(function (e) {
526 if (e.state == 'idle') {
527 e.startOn += effect.finishOn;
528 e.finishOn += effect.finishOn;
529 }
530 }, this.effects);
531 break;
532 case 'end':
533 var finish;
534 // start effect after last queued effect has finished
535 ma(function (e) {
536 var i = e.finishOn;
537 if (i >= (finish || i)) {
538 finish = i;
539 }
540 }, this.effects);
541 timestamp = finish || timestamp;
542 break;
543 case 'break':
544 ma(function (e) {
545 e.finalize();
546 }, this.effects);
547 break;
548 }
550 effect.startOn += timestamp;
551 effect.finishOn += timestamp;
552 if (!effect.options.queue.limit ||
553 this.effects.length < effect.options.queue.limit) {
554 this.effects.push(effect);
555 }
557 if (!this.interval) {
558 this.interval = this.startLoop(MochiKit.Base.bind(this.loop, this),
559 40);
560 }
561 },
563 /** @id MochiKit.Visual.ScopedQueue.prototype.startLoop */
564 startLoop: function (func, interval) {
565 return setInterval(func, interval);
566 },
568 /** @id MochiKit.Visual.ScopedQueue.prototype.remove */
569 remove: function (effect) {
570 this.effects = MochiKit.Base.filter(function (e) {
571 return e != effect;
572 }, this.effects);
573 if (!this.effects.length) {
574 this.stopLoop(this.interval);
575 this.interval = null;
576 }
577 },
579 /** @id MochiKit.Visual.ScopedQueue.prototype.stopLoop */
580 stopLoop: function (interval) {
581 clearInterval(interval);
582 },
584 /** @id MochiKit.Visual.ScopedQueue.prototype.loop */
585 loop: function () {
586 var timePos = new Date().getTime();
587 MochiKit.Base.map(function (effect) {
588 effect.loop(timePos);
589 }, this.effects);
590 }
591 });
593 MochiKit.Visual.Queues = {
594 instances: {},
596 get: function (queueName) {
597 if (typeof(queueName) != 'string') {
598 return queueName;
599 }
601 if (!this.instances[queueName]) {
602 this.instances[queueName] = new MochiKit.Visual.ScopedQueue();
603 }
604 return this.instances[queueName];
605 }
606 };
608 MochiKit.Visual.Queue = MochiKit.Visual.Queues.get('global');
610 MochiKit.Visual.DefaultOptions = {
611 transition: MochiKit.Visual.Transitions.sinoidal,
612 duration: 1.0, // seconds
613 fps: 25.0, // max. 25fps due to MochiKit.Visual.Queue implementation
614 sync: false, // true for combining
615 from: 0.0,
616 to: 1.0,
617 delay: 0.0,
618 queue: 'parallel'
619 };
621 MochiKit.Visual.Base = function () {};
623 MochiKit.Visual.Base.prototype = {
624 /***
626 Basic class for all Effects. Define a looping mechanism called for each step
627 of an effect. Don't instantiate it, only subclass it.
629 ***/
631 __class__ : MochiKit.Visual.Base,
633 /** @id MochiKit.Visual.Base.prototype.start */
634 start: function (options) {
635 var v = MochiKit.Visual;
636 this.options = MochiKit.Base.setdefault(options,
637 v.DefaultOptions);
638 this.currentFrame = 0;
639 this.state = 'idle';
640 this.startOn = this.options.delay*1000;
641 this.finishOn = this.startOn + (this.options.duration*1000);
642 this.event('beforeStart');
643 if (!this.options.sync) {
644 v.Queues.get(typeof(this.options.queue) == 'string' ?
645 'global' : this.options.queue.scope).add(this);
646 }
647 },
649 /** @id MochiKit.Visual.Base.prototype.loop */
650 loop: function (timePos) {
651 if (timePos >= this.startOn) {
652 if (timePos >= this.finishOn) {
653 return this.finalize();
654 }
655 var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
656 var frame =
657 Math.round(pos * this.options.fps * this.options.duration);
658 if (frame > this.currentFrame) {
659 this.render(pos);
660 this.currentFrame = frame;
661 }
662 }
663 },
665 /** @id MochiKit.Visual.Base.prototype.render */
666 render: function (pos) {
667 if (this.state == 'idle') {
668 this.state = 'running';
669 this.event('beforeSetup');
670 this.setup();
671 this.event('afterSetup');
672 }
673 if (this.state == 'running') {
674 if (this.options.transition) {
675 pos = this.options.transition(pos);
676 }
677 pos *= (this.options.to - this.options.from);
678 pos += this.options.from;
679 this.event('beforeUpdate');
680 this.update(pos);
681 this.event('afterUpdate');
682 }
683 },
685 /** @id MochiKit.Visual.Base.prototype.cancel */
686 cancel: function () {
687 if (!this.options.sync) {
688 MochiKit.Visual.Queues.get(typeof(this.options.queue) == 'string' ?
689 'global' : this.options.queue.scope).remove(this);
690 }
691 this.state = 'finished';
692 },
694 /** @id MochiKit.Visual.Base.prototype.finalize */
695 finalize: function () {
696 this.render(1.0);
697 this.cancel();
698 this.event('beforeFinish');
699 this.finish();
700 this.event('afterFinish');
701 },
703 setup: function () {
704 },
706 finish: function () {
707 },
709 update: function (position) {
710 },
712 /** @id MochiKit.Visual.Base.prototype.event */
713 event: function (eventName) {
714 if (this.options[eventName + 'Internal']) {
715 this.options[eventName + 'Internal'](this);
716 }
717 if (this.options[eventName]) {
718 this.options[eventName](this);
719 }
720 },
722 /** @id MochiKit.Visual.Base.prototype.repr */
723 repr: function () {
724 return '[' + this.__class__.NAME + ', options:' +
725 MochiKit.Base.repr(this.options) + ']';
726 }
727 };
729 /** @id MochiKit.Visual.Parallel */
730 MochiKit.Visual.Parallel = function (effects, options) {
731 var cls = arguments.callee;
732 if (!(this instanceof cls)) {
733 return new cls(effects, options);
734 }
736 this.__init__(effects, options);
737 };
739 MochiKit.Visual.Parallel.prototype = new MochiKit.Visual.Base();
741 MochiKit.Base.update(MochiKit.Visual.Parallel.prototype, {
742 /***
744 Run multiple effects at the same time.
746 ***/
748 __class__ : MochiKit.Visual.Parallel,
750 __init__: function (effects, options) {
751 this.effects = effects || [];
752 this.start(options);
753 },
755 /** @id MochiKit.Visual.Parallel.prototype.update */
756 update: function (position) {
757 MochiKit.Base.map(function (effect) {
758 effect.render(position);
759 }, this.effects);
760 },
762 /** @id MochiKit.Visual.Parallel.prototype.finish */
763 finish: function () {
764 MochiKit.Base.map(function (effect) {
765 effect.finalize();
766 }, this.effects);
767 }
768 });
770 /** @id MochiKit.Visual.Sequence */
771 MochiKit.Visual.Sequence = function (effects, options) {
772 var cls = arguments.callee;
773 if (!(this instanceof cls)) {
774 return new cls(effects, options);
775 }
776 this.__init__(effects, options);
777 };
779 MochiKit.Visual.Sequence.prototype = new MochiKit.Visual.Base();
781 MochiKit.Base.update(MochiKit.Visual.Sequence.prototype, {
783 __class__ : MochiKit.Visual.Sequence,
785 __init__: function (effects, options) {
786 var defs = { transition: MochiKit.Visual.Transitions.linear,
787 duration: 0 };
788 this.effects = effects || [];
789 MochiKit.Base.map(function (effect) {
790 defs.duration += effect.options.duration;
791 }, this.effects);
792 MochiKit.Base.setdefault(options, defs);
793 this.start(options);
794 },
796 /** @id MochiKit.Visual.Sequence.prototype.update */
797 update: function (position) {
798 var time = position * this.options.duration;
799 for (var i = 0; i < this.effects.length; i++) {
800 var effect = this.effects[i];
801 if (time <= effect.options.duration) {
802 effect.render(time / effect.options.duration);
803 break;
804 } else {
805 time -= effect.options.duration;
806 }
807 }
808 },
810 /** @id MochiKit.Visual.Sequence.prototype.finish */
811 finish: function () {
812 MochiKit.Base.map(function (effect) {
813 effect.finalize();
814 }, this.effects);
815 }
816 });
818 /** @id MochiKit.Visual.Opacity */
819 MochiKit.Visual.Opacity = function (element, options) {
820 var cls = arguments.callee;
821 if (!(this instanceof cls)) {
822 return new cls(element, options);
823 }
824 this.__init__(element, options);
825 };
827 MochiKit.Visual.Opacity.prototype = new MochiKit.Visual.Base();
829 MochiKit.Base.update(MochiKit.Visual.Opacity.prototype, {
830 /***
832 Change the opacity of an element.
834 @param options: 'from' and 'to' change the starting and ending opacities.
835 Must be between 0.0 and 1.0. Default to current opacity and 1.0.
837 ***/
839 __class__ : MochiKit.Visual.Opacity,
841 __init__: function (element, /* optional */options) {
842 var b = MochiKit.Base;
843 var s = MochiKit.Style;
844 this.element = MochiKit.DOM.getElement(element);
845 // make this work on IE on elements without 'layout'
846 if (this.element.currentStyle &&
847 (!this.element.currentStyle.hasLayout)) {
848 s.setStyle(this.element, {zoom: 1});
849 }
850 options = b.update({
851 from: s.getStyle(this.element, 'opacity') || 0.0,
852 to: 1.0
853 }, options);
854 this.start(options);
855 },
857 /** @id MochiKit.Visual.Opacity.prototype.update */
858 update: function (position) {
859 MochiKit.Style.setStyle(this.element, {'opacity': position});
860 }
861 });
863 /** @id MochiKit.Visual.Move.prototype */
864 MochiKit.Visual.Move = function (element, options) {
865 var cls = arguments.callee;
866 if (!(this instanceof cls)) {
867 return new cls(element, options);
868 }
869 this.__init__(element, options);
870 };
872 MochiKit.Visual.Move.prototype = new MochiKit.Visual.Base();
874 MochiKit.Base.update(MochiKit.Visual.Move.prototype, {
875 /***
877 Move an element between its current position to a defined position
879 @param options: 'x' and 'y' for final positions, default to 0, 0.
881 ***/
883 __class__ : MochiKit.Visual.Move,
885 __init__: function (element, /* optional */options) {
886 this.element = MochiKit.DOM.getElement(element);
887 options = MochiKit.Base.update({
888 x: 0,
889 y: 0,
890 mode: 'relative'
891 }, options);
892 this.start(options);
893 },
895 /** @id MochiKit.Visual.Move.prototype.setup */
896 setup: function () {
897 // Bug in Opera: Opera returns the 'real' position of a static element
898 // or relative element that does not have top/left explicitly set.
899 // ==> Always set top and left for position relative elements in your
900 // stylesheets (to 0 if you do not need them)
901 MochiKit.Style.makePositioned(this.element);
903 var s = this.element.style;
904 var originalVisibility = s.visibility;
905 var originalDisplay = s.display;
906 if (originalDisplay == 'none') {
907 s.visibility = 'hidden';
908 s.display = '';
909 }
911 this.originalLeft = parseFloat(MochiKit.Style.getStyle(this.element, 'left') || '0');
912 this.originalTop = parseFloat(MochiKit.Style.getStyle(this.element, 'top') || '0');
914 if (this.options.mode == 'absolute') {
915 // absolute movement, so we need to calc deltaX and deltaY
916 this.options.x -= this.originalLeft;
917 this.options.y -= this.originalTop;
918 }
919 if (originalDisplay == 'none') {
920 s.visibility = originalVisibility;
921 s.display = originalDisplay;
922 }
923 },
925 /** @id MochiKit.Visual.Move.prototype.update */
926 update: function (position) {
927 MochiKit.Style.setStyle(this.element, {
928 left: Math.round(this.options.x * position + this.originalLeft) + 'px',
929 top: Math.round(this.options.y * position + this.originalTop) + 'px'
930 });
931 }
932 });
934 /** @id MochiKit.Visual.Scale */
935 MochiKit.Visual.Scale = function (element, percent, options) {
936 var cls = arguments.callee;
937 if (!(this instanceof cls)) {
938 return new cls(element, percent, options);
939 }
940 this.__init__(element, percent, options);
941 };
943 MochiKit.Visual.Scale.prototype = new MochiKit.Visual.Base();
945 MochiKit.Base.update(MochiKit.Visual.Scale.prototype, {
946 /***
948 Change the size of an element.
950 @param percent: final_size = percent*original_size
952 @param options: several options changing scale behaviour
954 ***/
956 __class__ : MochiKit.Visual.Scale,
958 __init__: function (element, percent, /* optional */options) {
959 this.element = MochiKit.DOM.getElement(element);
960 options = MochiKit.Base.update({
961 scaleX: true,
962 scaleY: true,
963 scaleContent: true,
964 scaleFromCenter: false,
965 scaleMode: 'box', // 'box' or 'contents' or {} with provided values
966 scaleFrom: 100.0,
967 scaleTo: percent
968 }, options);
969 this.start(options);
970 },
972 /** @id MochiKit.Visual.Scale.prototype.setup */
973 setup: function () {
974 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
975 this.elementPositioning = MochiKit.Style.getStyle(this.element,
976 'position');
978 var ma = MochiKit.Base.map;
979 var b = MochiKit.Base.bind;
980 this.originalStyle = {};
981 ma(b(function (k) {
982 this.originalStyle[k] = this.element.style[k];
983 }, this), ['top', 'left', 'width', 'height', 'fontSize']);
985 this.originalTop = this.element.offsetTop;
986 this.originalLeft = this.element.offsetLeft;
988 var fontSize = MochiKit.Style.getStyle(this.element,
989 'font-size') || '100%';
990 ma(b(function (fontSizeType) {
991 if (fontSize.indexOf(fontSizeType) > 0) {
992 this.fontSize = parseFloat(fontSize);
993 this.fontSizeType = fontSizeType;
994 }
995 }, this), ['em', 'px', '%']);
997 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
999 if (/^content/.test(this.options.scaleMode)) {
1000 this.dims = [this.element.scrollHeight, this.element.scrollWidth];
1001 } else if (this.options.scaleMode == 'box') {
1002 this.dims = [this.element.offsetHeight, this.element.offsetWidth];
1003 } else {
1004 this.dims = [this.options.scaleMode.originalHeight,
1005 this.options.scaleMode.originalWidth];
1006 }
1007 },
1009 /** @id MochiKit.Visual.Scale.prototype.update */
1010 update: function (position) {
1011 var currentScale = (this.options.scaleFrom/100.0) +
1012 (this.factor * position);
1013 if (this.options.scaleContent && this.fontSize) {
1014 MochiKit.Style.setStyle(this.element, {
1015 fontSize: this.fontSize * currentScale + this.fontSizeType
1016 });
1017 }
1018 this.setDimensions(this.dims[0] * currentScale,
1019 this.dims[1] * currentScale);
1020 },
1022 /** @id MochiKit.Visual.Scale.prototype.finish */
1023 finish: function () {
1024 if (this.restoreAfterFinish) {
1025 MochiKit.Style.setStyle(this.element, this.originalStyle);
1026 }
1027 },
1029 /** @id MochiKit.Visual.Scale.prototype.setDimensions */
1030 setDimensions: function (height, width) {
1031 var d = {};
1032 var r = Math.round;
1033 if (/MSIE/.test(navigator.userAgent)) {
1034 r = Math.ceil;
1035 }
1036 if (this.options.scaleX) {
1037 d.width = r(width) + 'px';
1038 }
1039 if (this.options.scaleY) {
1040 d.height = r(height) + 'px';
1041 }
1042 if (this.options.scaleFromCenter) {
1043 var topd = (height - this.dims[0])/2;
1044 var leftd = (width - this.dims[1])/2;
1045 if (this.elementPositioning == 'absolute') {
1046 if (this.options.scaleY) {
1047 d.top = this.originalTop - topd + 'px';
1048 }
1049 if (this.options.scaleX) {
1050 d.left = this.originalLeft - leftd + 'px';
1051 }
1052 } else {
1053 if (this.options.scaleY) {
1054 d.top = -topd + 'px';
1055 }
1056 if (this.options.scaleX) {
1057 d.left = -leftd + 'px';
1058 }
1059 }
1060 }
1061 MochiKit.Style.setStyle(this.element, d);
1062 }
1063 });
1065 /** @id MochiKit.Visual.Highlight */
1066 MochiKit.Visual.Highlight = function (element, options) {
1067 var cls = arguments.callee;
1068 if (!(this instanceof cls)) {
1069 return new cls(element, options);
1070 }
1071 this.__init__(element, options);
1072 };
1074 MochiKit.Visual.Highlight.prototype = new MochiKit.Visual.Base();
1076 MochiKit.Base.update(MochiKit.Visual.Highlight.prototype, {
1077 /***
1079 Highlight an item of the page.
1081 @param options: 'startcolor' for choosing highlighting color, default
1082 to '#ffff99'.
1084 ***/
1086 __class__ : MochiKit.Visual.Highlight,
1088 __init__: function (element, /* optional */options) {
1089 this.element = MochiKit.DOM.getElement(element);
1090 options = MochiKit.Base.update({
1091 startcolor: '#ffff99'
1092 }, options);
1093 this.start(options);
1094 },
1096 /** @id MochiKit.Visual.Highlight.prototype.setup */
1097 setup: function () {
1098 var b = MochiKit.Base;
1099 var s = MochiKit.Style;
1100 // Prevent executing on elements not in the layout flow
1101 if (s.getStyle(this.element, 'display') == 'none') {
1102 this.cancel();
1103 return;
1104 }
1105 // Disable background image during the effect
1106 this.oldStyle = {
1107 backgroundImage: s.getStyle(this.element, 'background-image')
1108 };
1109 s.setStyle(this.element, {
1110 backgroundImage: 'none'
1111 });
1113 if (!this.options.endcolor) {
1114 this.options.endcolor =
1115 MochiKit.Color.Color.fromBackground(this.element).toHexString();
1116 }
1117 if (b.isUndefinedOrNull(this.options.restorecolor)) {
1118 this.options.restorecolor = s.getStyle(this.element,
1119 'background-color');
1120 }
1121 // init color calculations
1122 this._base = b.map(b.bind(function (i) {
1123 return parseInt(
1124 this.options.startcolor.slice(i*2 + 1, i*2 + 3), 16);
1125 }, this), [0, 1, 2]);
1126 this._delta = b.map(b.bind(function (i) {
1127 return parseInt(this.options.endcolor.slice(i*2 + 1, i*2 + 3), 16)
1128 - this._base[i];
1129 }, this), [0, 1, 2]);
1130 },
1132 /** @id MochiKit.Visual.Highlight.prototype.update */
1133 update: function (position) {
1134 var m = '#';
1135 MochiKit.Base.map(MochiKit.Base.bind(function (i) {
1136 m += MochiKit.Color.toColorPart(Math.round(this._base[i] +
1137 this._delta[i]*position));
1138 }, this), [0, 1, 2]);
1139 MochiKit.Style.setStyle(this.element, {
1140 backgroundColor: m
1141 });
1142 },
1144 /** @id MochiKit.Visual.Highlight.prototype.finish */
1145 finish: function () {
1146 MochiKit.Style.setStyle(this.element,
1147 MochiKit.Base.update(this.oldStyle, {
1148 backgroundColor: this.options.restorecolor
1149 }));
1150 }
1151 });
1153 /** @id MochiKit.Visual.ScrollTo */
1154 MochiKit.Visual.ScrollTo = function (element, options) {
1155 var cls = arguments.callee;
1156 if (!(this instanceof cls)) {
1157 return new cls(element, options);
1158 }
1159 this.__init__(element, options);
1160 };
1162 MochiKit.Visual.ScrollTo.prototype = new MochiKit.Visual.Base();
1164 MochiKit.Base.update(MochiKit.Visual.ScrollTo.prototype, {
1165 /***
1167 Scroll to an element in the page.
1169 ***/
1171 __class__ : MochiKit.Visual.ScrollTo,
1173 __init__: function (element, /* optional */options) {
1174 this.element = MochiKit.DOM.getElement(element);
1175 this.start(options);
1176 },
1178 /** @id MochiKit.Visual.ScrollTo.prototype.setup */
1179 setup: function () {
1180 var p = MochiKit.Position;
1181 p.prepare();
1182 var offsets = p.cumulativeOffset(this.element);
1183 if (this.options.offset) {
1184 offsets.y += this.options.offset;
1185 }
1186 var max;
1187 if (window.innerHeight) {
1188 max = window.innerHeight - window.height;
1189 } else if (document.documentElement &&
1190 document.documentElement.clientHeight) {
1191 max = document.documentElement.clientHeight -
1192 document.body.scrollHeight;
1193 } else if (document.body) {
1194 max = document.body.clientHeight - document.body.scrollHeight;
1195 }
1196 this.scrollStart = p.windowOffset.y;
1197 this.delta = (offsets.y > max ? max : offsets.y) - this.scrollStart;
1198 },
1200 /** @id MochiKit.Visual.ScrollTo.prototype.update */
1201 update: function (position) {
1202 var p = MochiKit.Position;
1203 p.prepare();
1204 window.scrollTo(p.windowOffset.x, this.scrollStart + (position * this.delta));
1205 }
1206 });
1208 MochiKit.Visual.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1210 MochiKit.Visual.Morph = function (element, options) {
1211 var cls = arguments.callee;
1212 if (!(this instanceof cls)) {
1213 return new cls(element, options);
1214 }
1215 this.__init__(element, options);
1216 };
1218 MochiKit.Visual.Morph.prototype = new MochiKit.Visual.Base();
1220 MochiKit.Base.update(MochiKit.Visual.Morph.prototype, {
1221 /***
1223 Morph effect: make a transformation from current style to the given style,
1224 automatically making a transition between the two.
1226 ***/
1228 __class__ : MochiKit.Visual.Morph,
1230 __init__: function (element, /* optional */options) {
1231 this.element = MochiKit.DOM.getElement(element);
1232 this.start(options);
1233 },
1235 /** @id MochiKit.Visual.Morph.prototype.setup */
1236 setup: function () {
1237 var b = MochiKit.Base;
1238 var style = this.options.style;
1239 this.styleStart = {};
1240 this.styleEnd = {};
1241 this.units = {};
1242 var value, unit;
1243 for (var s in style) {
1244 value = style[s];
1245 s = b.camelize(s);
1246 if (MochiKit.Visual.CSS_LENGTH.test(value)) {
1247 var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
1248 value = parseFloat(components[1]);
1249 unit = (components.length == 3) ? components[2] : null;
1250 this.styleEnd[s] = value;
1251 this.units[s] = unit;
1252 value = MochiKit.Style.getStyle(this.element, s);
1253 components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
1254 value = parseFloat(components[1]);
1255 this.styleStart[s] = value;
1256 } else if (/[Cc]olor$/.test(s)) {
1257 var c = MochiKit.Color.Color;
1258 value = c.fromString(value);
1259 if (value) {
1260 this.units[s] = "color";
1261 this.styleEnd[s] = value.toHexString();
1262 value = MochiKit.Style.getStyle(this.element, s);
1263 this.styleStart[s] = c.fromString(value).toHexString();
1265 this.styleStart[s] = b.map(b.bind(function (i) {
1266 return parseInt(
1267 this.styleStart[s].slice(i*2 + 1, i*2 + 3), 16);
1268 }, this), [0, 1, 2]);
1269 this.styleEnd[s] = b.map(b.bind(function (i) {
1270 return parseInt(
1271 this.styleEnd[s].slice(i*2 + 1, i*2 + 3), 16);
1272 }, this), [0, 1, 2]);
1273 }
1274 } else {
1275 // For non-length & non-color properties, we just set the value
1276 this.element.style[s] = value;
1277 }
1278 }
1279 },
1281 /** @id MochiKit.Visual.Morph.prototype.update */
1282 update: function (position) {
1283 var value;
1284 for (var s in this.styleStart) {
1285 if (this.units[s] == "color") {
1286 var m = '#';
1287 var start = this.styleStart[s];
1288 var end = this.styleEnd[s];
1289 MochiKit.Base.map(MochiKit.Base.bind(function (i) {
1290 m += MochiKit.Color.toColorPart(Math.round(start[i] +
1291 (end[i] - start[i])*position));
1292 }, this), [0, 1, 2]);
1293 this.element.style[s] = m;
1294 } else {
1295 value = this.styleStart[s] + Math.round((this.styleEnd[s] - this.styleStart[s]) * position * 1000) / 1000 + this.units[s];
1296 this.element.style[s] = value;
1297 }
1298 }
1299 }
1300 });
1302 /***
1304 Combination effects.
1306 ***/
1308 /** @id MochiKit.Visual.fade */
1309 MochiKit.Visual.fade = function (element, /* optional */ options) {
1310 /***
1312 Fade a given element: change its opacity and hide it in the end.
1314 @param options: 'to' and 'from' to change opacity.
1316 ***/
1317 var s = MochiKit.Style;
1318 var oldOpacity = s.getStyle(element, 'opacity');
1319 options = MochiKit.Base.update({
1320 from: s.getStyle(element, 'opacity') || 1.0,
1321 to: 0.0,
1322 afterFinishInternal: function (effect) {
1323 if (effect.options.to !== 0) {
1324 return;
1325 }
1326 s.hideElement(effect.element);
1327 s.setStyle(effect.element, {'opacity': oldOpacity});
1328 }
1329 }, options);
1330 return new MochiKit.Visual.Opacity(element, options);
1331 };
1333 /** @id MochiKit.Visual.appear */
1334 MochiKit.Visual.appear = function (element, /* optional */ options) {
1335 /***
1337 Make an element appear.
1339 @param options: 'to' and 'from' to change opacity.
1341 ***/
1342 var s = MochiKit.Style;
1343 var v = MochiKit.Visual;
1344 options = MochiKit.Base.update({
1345 from: (s.getStyle(element, 'display') == 'none' ? 0.0 :
1346 s.getStyle(element, 'opacity') || 0.0),
1347 to: 1.0,
1348 // force Safari to render floated elements properly
1349 afterFinishInternal: function (effect) {
1350 v.forceRerendering(effect.element);
1351 },
1352 beforeSetupInternal: function (effect) {
1353 s.setStyle(effect.element, {'opacity': effect.options.from});
1354 s.showElement(effect.element);
1355 }
1356 }, options);
1357 return new v.Opacity(element, options);
1358 };
1360 /** @id MochiKit.Visual.puff */
1361 MochiKit.Visual.puff = function (element, /* optional */ options) {
1362 /***
1364 'Puff' an element: grow it to double size, fading it and make it hidden.
1366 ***/
1367 var s = MochiKit.Style;
1368 var v = MochiKit.Visual;
1369 element = MochiKit.DOM.getElement(element);
1370 var elementDimensions = MochiKit.Style.getElementDimensions(element, true);
1371 var oldStyle = {
1372 position: s.getStyle(element, 'position'),
1373 top: element.style.top,
1374 left: element.style.left,
1375 width: element.style.width,
1376 height: element.style.height,
1377 opacity: s.getStyle(element, 'opacity')
1378 };
1379 options = MochiKit.Base.update({
1380 beforeSetupInternal: function (effect) {
1381 MochiKit.Position.absolutize(effect.effects[0].element);
1382 },
1383 afterFinishInternal: function (effect) {
1384 s.hideElement(effect.effects[0].element);
1385 s.setStyle(effect.effects[0].element, oldStyle);
1386 },
1387 scaleContent: true,
1388 scaleFromCenter: true
1389 }, options);
1390 return new v.Parallel(
1391 [new v.Scale(element, 200,
1392 {sync: true, scaleFromCenter: options.scaleFromCenter,
1393 scaleMode: {originalHeight: elementDimensions.h,
1394 originalWidth: elementDimensions.w},
1395 scaleContent: options.scaleContent, restoreAfterFinish: true}),
1396 new v.Opacity(element, {sync: true, to: 0.0 })],
1397 options);
1398 };
1400 /** @id MochiKit.Visual.blindUp */
1401 MochiKit.Visual.blindUp = function (element, /* optional */ options) {
1402 /***
1404 Blind an element up: change its vertical size to 0.
1406 ***/
1407 var d = MochiKit.DOM;
1408 var s = MochiKit.Style;
1409 element = d.getElement(element);
1410 var elementDimensions = s.getElementDimensions(element, true);
1411 var elemClip = s.makeClipping(element);
1412 options = MochiKit.Base.update({
1413 scaleContent: false,
1414 scaleX: false,
1415 scaleMode: {originalHeight: elementDimensions.h,
1416 originalWidth: elementDimensions.w},
1417 restoreAfterFinish: true,
1418 afterFinishInternal: function (effect) {
1419 s.hideElement(effect.element);
1420 s.undoClipping(effect.element, elemClip);
1421 }
1422 }, options);
1423 return new MochiKit.Visual.Scale(element, 0, options);
1424 };
1426 /** @id MochiKit.Visual.blindDown */
1427 MochiKit.Visual.blindDown = function (element, /* optional */ options) {
1428 /***
1430 Blind an element down: restore its vertical size.
1432 ***/
1433 var d = MochiKit.DOM;
1434 var s = MochiKit.Style;
1435 element = d.getElement(element);
1436 var elementDimensions = s.getElementDimensions(element, true);
1437 var elemClip;
1438 options = MochiKit.Base.update({
1439 scaleContent: false,
1440 scaleX: false,
1441 scaleFrom: 0,
1442 scaleMode: {originalHeight: elementDimensions.h,
1443 originalWidth: elementDimensions.w},
1444 restoreAfterFinish: true,
1445 afterSetupInternal: function (effect) {
1446 elemClip = s.makeClipping(effect.element);
1447 s.setStyle(effect.element, {height: '0px'});
1448 s.showElement(effect.element);
1449 },
1450 afterFinishInternal: function (effect) {
1451 s.undoClipping(effect.element, elemClip);
1452 }
1453 }, options);
1454 return new MochiKit.Visual.Scale(element, 100, options);
1455 };
1457 /** @id MochiKit.Visual.switchOff */
1458 MochiKit.Visual.switchOff = function (element, /* optional */ options) {
1459 /***
1461 Apply a switch-off-like effect.
1463 ***/
1464 var d = MochiKit.DOM;
1465 var s = MochiKit.Style;
1466 element = d.getElement(element);
1467 var elementDimensions = s.getElementDimensions(element, true);
1468 var oldOpacity = s.getStyle(element, 'opacity');
1469 var elemClip;
1470 options = MochiKit.Base.update({
1471 duration: 0.7,
1472 restoreAfterFinish: true,
1473 beforeSetupInternal: function (effect) {
1474 s.makePositioned(element);
1475 elemClip = s.makeClipping(element);
1476 },
1477 afterFinishInternal: function (effect) {
1478 s.hideElement(element);
1479 s.undoClipping(element, elemClip);
1480 s.undoPositioned(element);
1481 s.setStyle(element, {'opacity': oldOpacity});
1482 }
1483 }, options);
1484 var v = MochiKit.Visual;
1485 return new v.Sequence(
1486 [new v.appear(element,
1487 { sync: true, duration: 0.57 * options.duration,
1488 from: 0, transition: v.Transitions.flicker }),
1489 new v.Scale(element, 1,
1490 { sync: true, duration: 0.43 * options.duration,
1491 scaleFromCenter: true, scaleX: false,
1492 scaleMode: {originalHeight: elementDimensions.h,
1493 originalWidth: elementDimensions.w},
1494 scaleContent: false, restoreAfterFinish: true })],
1495 options);
1496 };
1498 /** @id MochiKit.Visual.dropOut */
1499 MochiKit.Visual.dropOut = function (element, /* optional */ options) {
1500 /***
1502 Make an element fall and disappear.
1504 ***/
1505 var d = MochiKit.DOM;
1506 var s = MochiKit.Style;
1507 element = d.getElement(element);
1508 var oldStyle = {
1509 top: s.getStyle(element, 'top'),
1510 left: s.getStyle(element, 'left'),
1511 opacity: s.getStyle(element, 'opacity')
1512 };
1514 options = MochiKit.Base.update({
1515 duration: 0.5,
1516 distance: 100,
1517 beforeSetupInternal: function (effect) {
1518 s.makePositioned(effect.effects[0].element);
1519 },
1520 afterFinishInternal: function (effect) {
1521 s.hideElement(effect.effects[0].element);
1522 s.undoPositioned(effect.effects[0].element);
1523 s.setStyle(effect.effects[0].element, oldStyle);
1524 }
1525 }, options);
1526 var v = MochiKit.Visual;
1527 return new v.Parallel(
1528 [new v.Move(element, {x: 0, y: options.distance, sync: true}),
1529 new v.Opacity(element, {sync: true, to: 0.0})],
1530 options);
1531 };
1533 /** @id MochiKit.Visual.shake */
1534 MochiKit.Visual.shake = function (element, /* optional */ options) {
1535 /***
1537 Move an element from left to right several times.
1539 ***/
1540 var d = MochiKit.DOM;
1541 var v = MochiKit.Visual;
1542 var s = MochiKit.Style;
1543 element = d.getElement(element);
1544 var oldStyle = {
1545 top: s.getStyle(element, 'top'),
1546 left: s.getStyle(element, 'left')
1547 };
1548 options = MochiKit.Base.update({
1549 duration: 0.5,
1550 afterFinishInternal: function (effect) {
1551 s.undoPositioned(element);
1552 s.setStyle(element, oldStyle);
1553 }
1554 }, options);
1555 return new v.Sequence(
1556 [new v.Move(element, { sync: true, duration: 0.1 * options.duration,
1557 x: 20, y: 0 }),
1558 new v.Move(element, { sync: true, duration: 0.2 * options.duration,
1559 x: -40, y: 0 }),
1560 new v.Move(element, { sync: true, duration: 0.2 * options.duration,
1561 x: 40, y: 0 }),
1562 new v.Move(element, { sync: true, duration: 0.2 * options.duration,
1563 x: -40, y: 0 }),
1564 new v.Move(element, { sync: true, duration: 0.2 * options.duration,
1565 x: 40, y: 0 }),
1566 new v.Move(element, { sync: true, duration: 0.1 * options.duration,
1567 x: -20, y: 0 })],
1568 options);
1569 };
1571 /** @id MochiKit.Visual.slideDown */
1572 MochiKit.Visual.slideDown = function (element, /* optional */ options) {
1573 /***
1575 Slide an element down.
1576 It needs to have the content of the element wrapped in a container
1577 element with fixed height.
1579 ***/
1580 var d = MochiKit.DOM;
1581 var b = MochiKit.Base;
1582 var s = MochiKit.Style;
1583 element = d.getElement(element);
1584 if (!element.firstChild) {
1585 throw new Error("MochiKit.Visual.slideDown must be used on a element with a child");
1586 }
1587 d.removeEmptyTextNodes(element);
1588 var oldInnerBottom = s.getStyle(element.firstChild, 'bottom') || 0;
1589 var elementDimensions = s.getElementDimensions(element, true);
1590 var elemClip;
1591 options = b.update({
1592 scaleContent: false,
1593 scaleX: false,
1594 scaleFrom: 0,
1595 scaleMode: {originalHeight: elementDimensions.h,
1596 originalWidth: elementDimensions.w},
1597 restoreAfterFinish: true,
1598 afterSetupInternal: function (effect) {
1599 s.makePositioned(effect.element);
1600 s.makePositioned(effect.element.firstChild);
1601 if (/Opera/.test(navigator.userAgent)) {
1602 s.setStyle(effect.element, {top: ''});
1603 }
1604 elemClip = s.makeClipping(effect.element);
1605 s.setStyle(effect.element, {height: '0px'});
1606 s.showElement(effect.element);
1607 },
1608 afterUpdateInternal: function (effect) {
1609 var elementDimensions = s.getElementDimensions(effect.element, true);
1610 s.setStyle(effect.element.firstChild,
1611 {bottom: (effect.dims[0] - elementDimensions.h) + 'px'});
1612 },
1613 afterFinishInternal: function (effect) {
1614 s.undoClipping(effect.element, elemClip);
1615 // IE will crash if child is undoPositioned first
1616 if (/MSIE/.test(navigator.userAgent)) {
1617 s.undoPositioned(effect.element);
1618 s.undoPositioned(effect.element.firstChild);
1619 } else {
1620 s.undoPositioned(effect.element.firstChild);
1621 s.undoPositioned(effect.element);
1622 }
1623 s.setStyle(effect.element.firstChild, {bottom: oldInnerBottom});
1624 }
1625 }, options);
1627 return new MochiKit.Visual.Scale(element, 100, options);
1628 };
1630 /** @id MochiKit.Visual.slideUp */
1631 MochiKit.Visual.slideUp = function (element, /* optional */ options) {
1632 /***
1634 Slide an element up.
1635 It needs to have the content of the element wrapped in a container
1636 element with fixed height.
1638 ***/
1639 var d = MochiKit.DOM;
1640 var b = MochiKit.Base;
1641 var s = MochiKit.Style;
1642 element = d.getElement(element);
1643 if (!element.firstChild) {
1644 throw new Error("MochiKit.Visual.slideUp must be used on a element with a child");
1645 }
1646 d.removeEmptyTextNodes(element);
1647 var oldInnerBottom = s.getStyle(element.firstChild, 'bottom');
1648 var elementDimensions = s.getElementDimensions(element, true);
1649 var elemClip;
1650 options = b.update({
1651 scaleContent: false,
1652 scaleX: false,
1653 scaleMode: {originalHeight: elementDimensions.h,
1654 originalWidth: elementDimensions.w},
1655 scaleFrom: 100,
1656 restoreAfterFinish: true,
1657 beforeStartInternal: function (effect) {
1658 s.makePositioned(effect.element);
1659 s.makePositioned(effect.element.firstChild);
1660 if (/Opera/.test(navigator.userAgent)) {
1661 s.setStyle(effect.element, {top: ''});
1662 }
1663 elemClip = s.makeClipping(effect.element);
1664 s.showElement(effect.element);
1665 },
1666 afterUpdateInternal: function (effect) {
1667 var elementDimensions = s.getElementDimensions(effect.element, true);
1668 s.setStyle(effect.element.firstChild,
1669 {bottom: (effect.dims[0] - elementDimensions.h) + 'px'});
1670 },
1671 afterFinishInternal: function (effect) {
1672 s.hideElement(effect.element);
1673 s.undoClipping(effect.element, elemClip);
1674 s.undoPositioned(effect.element.firstChild);
1675 s.undoPositioned(effect.element);
1676 s.setStyle(effect.element.firstChild, {bottom: oldInnerBottom});
1677 }
1678 }, options);
1679 return new MochiKit.Visual.Scale(element, 0, options);
1680 };
1682 // Bug in opera makes the TD containing this element expand for a instance
1683 // after finish
1684 /** @id MochiKit.Visual.squish */
1685 MochiKit.Visual.squish = function (element, /* optional */ options) {
1686 /***
1688 Reduce an element and make it disappear.
1690 ***/
1691 var d = MochiKit.DOM;
1692 var b = MochiKit.Base;
1693 var s = MochiKit.Style;
1694 var elementDimensions = s.getElementDimensions(element, true);
1695 var elemClip;
1696 options = b.update({
1697 restoreAfterFinish: true,
1698 scaleMode: {originalHeight: elementDimensions.w,
1699 originalWidth: elementDimensions.h},
1700 beforeSetupInternal: function (effect) {
1701 elemClip = s.makeClipping(effect.element);
1702 },
1703 afterFinishInternal: function (effect) {
1704 s.hideElement(effect.element);
1705 s.undoClipping(effect.element, elemClip);
1706 }
1707 }, options);
1709 return new MochiKit.Visual.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, options);
1710 };
1712 /** @id MochiKit.Visual.grow */
1713 MochiKit.Visual.grow = function (element, /* optional */ options) {
1714 /***
1716 Grow an element to its original size. Make it zero-sized before
1717 if necessary.
1719 ***/
1720 var d = MochiKit.DOM;
1721 var v = MochiKit.Visual;
1722 var s = MochiKit.Style;
1723 element = d.getElement(element);
1724 options = MochiKit.Base.update({
1725 direction: 'center',
1726 moveTransition: v.Transitions.sinoidal,
1727 scaleTransition: v.Transitions.sinoidal,
1728 opacityTransition: v.Transitions.full,
1729 scaleContent: true,
1730 scaleFromCenter: false
1731 }, options);
1732 var oldStyle = {
1733 top: element.style.top,
1734 left: element.style.left,
1735 height: element.style.height,
1736 width: element.style.width,
1737 opacity: s.getStyle(element, 'opacity')
1738 };
1739 var dims = s.getElementDimensions(element, true);
1740 var initialMoveX, initialMoveY;
1741 var moveX, moveY;
1743 switch (options.direction) {
1744 case 'top-left':
1745 initialMoveX = initialMoveY = moveX = moveY = 0;
1746 break;
1747 case 'top-right':
1748 initialMoveX = dims.w;
1749 initialMoveY = moveY = 0;
1750 moveX = -dims.w;
1751 break;
1752 case 'bottom-left':
1753 initialMoveX = moveX = 0;
1754 initialMoveY = dims.h;
1755 moveY = -dims.h;
1756 break;
1757 case 'bottom-right':
1758 initialMoveX = dims.w;
1759 initialMoveY = dims.h;
1760 moveX = -dims.w;
1761 moveY = -dims.h;
1762 break;
1763 case 'center':
1764 initialMoveX = dims.w / 2;
1765 initialMoveY = dims.h / 2;
1766 moveX = -dims.w / 2;
1767 moveY = -dims.h / 2;
1768 break;
1769 }
1771 var optionsParallel = MochiKit.Base.update({
1772 beforeSetupInternal: function (effect) {
1773 s.setStyle(effect.effects[0].element, {height: '0px'});
1774 s.showElement(effect.effects[0].element);
1775 },
1776 afterFinishInternal: function (effect) {
1777 s.undoClipping(effect.effects[0].element);
1778 s.undoPositioned(effect.effects[0].element);
1779 s.setStyle(effect.effects[0].element, oldStyle);
1780 }
1781 }, options);
1783 return new v.Move(element, {
1784 x: initialMoveX,
1785 y: initialMoveY,
1786 duration: 0.01,
1787 beforeSetupInternal: function (effect) {
1788 s.hideElement(effect.element);
1789 s.makeClipping(effect.element);
1790 s.makePositioned(effect.element);
1791 },
1792 afterFinishInternal: function (effect) {
1793 new v.Parallel(
1794 [new v.Opacity(effect.element, {
1795 sync: true, to: 1.0, from: 0.0,
1796 transition: options.opacityTransition
1797 }),
1798 new v.Move(effect.element, {
1799 x: moveX, y: moveY, sync: true,
1800 transition: options.moveTransition
1801 }),
1802 new v.Scale(effect.element, 100, {
1803 scaleMode: {originalHeight: dims.h,
1804 originalWidth: dims.w},
1805 sync: true,
1806 scaleFrom: /Opera/.test(navigator.userAgent) ? 1 : 0,
1807 transition: options.scaleTransition,
1808 scaleContent: options.scaleContent,
1809 scaleFromCenter: options.scaleFromCenter,
1810 restoreAfterFinish: true
1811 })
1812 ], optionsParallel
1813 );
1814 }
1815 });
1816 };
1818 /** @id MochiKit.Visual.shrink */
1819 MochiKit.Visual.shrink = function (element, /* optional */ options) {
1820 /***
1822 Shrink an element and make it disappear.
1824 ***/
1825 var d = MochiKit.DOM;
1826 var v = MochiKit.Visual;
1827 var s = MochiKit.Style;
1828 element = d.getElement(element);
1829 options = MochiKit.Base.update({
1830 direction: 'center',
1831 moveTransition: v.Transitions.sinoidal,
1832 scaleTransition: v.Transitions.sinoidal,
1833 opacityTransition: v.Transitions.none,
1834 scaleContent: true,
1835 scaleFromCenter: false
1836 }, options);
1837 var oldStyle = {
1838 top: element.style.top,
1839 left: element.style.left,
1840 height: element.style.height,
1841 width: element.style.width,
1842 opacity: s.getStyle(element, 'opacity')
1843 };
1845 var dims = s.getElementDimensions(element, true);
1846 var moveX, moveY;
1848 switch (options.direction) {
1849 case 'top-left':
1850 moveX = moveY = 0;
1851 break;
1852 case 'top-right':
1853 moveX = dims.w;
1854 moveY = 0;
1855 break;
1856 case 'bottom-left':
1857 moveX = 0;
1858 moveY = dims.h;
1859 break;
1860 case 'bottom-right':
1861 moveX = dims.w;
1862 moveY = dims.h;
1863 break;
1864 case 'center':
1865 moveX = dims.w / 2;
1866 moveY = dims.h / 2;
1867 break;
1868 }
1869 var elemClip;
1871 var optionsParallel = MochiKit.Base.update({
1872 beforeStartInternal: function (effect) {
1873 s.makePositioned(effect.effects[0].element);
1874 elemClip = s.makeClipping(effect.effects[0].element);
1875 },
1876 afterFinishInternal: function (effect) {
1877 s.hideElement(effect.effects[0].element);
1878 s.undoClipping(effect.effects[0].element, elemClip);
1879 s.undoPositioned(effect.effects[0].element);
1880 s.setStyle(effect.effects[0].element, oldStyle);
1881 }
1882 }, options);
1884 return new v.Parallel(
1885 [new v.Opacity(element, {
1886 sync: true, to: 0.0, from: 1.0,
1887 transition: options.opacityTransition
1888 }),
1889 new v.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, {
1890 scaleMode: {originalHeight: dims.h, originalWidth: dims.w},
1891 sync: true, transition: options.scaleTransition,
1892 scaleContent: options.scaleContent,
1893 scaleFromCenter: options.scaleFromCenter,
1894 restoreAfterFinish: true
1895 }),
1896 new v.Move(element, {
1897 x: moveX, y: moveY, sync: true, transition: options.moveTransition
1898 })
1899 ], optionsParallel
1900 );
1901 };
1903 /** @id MochiKit.Visual.pulsate */
1904 MochiKit.Visual.pulsate = function (element, /* optional */ options) {
1905 /***
1907 Pulse an element between appear/fade.
1909 ***/
1910 var d = MochiKit.DOM;
1911 var v = MochiKit.Visual;
1912 var b = MochiKit.Base;
1913 var oldOpacity = MochiKit.Style.getStyle(element, 'opacity');
1914 options = b.update({
1915 duration: 3.0,
1916 from: 0,
1917 afterFinishInternal: function (effect) {
1918 MochiKit.Style.setStyle(effect.element, {'opacity': oldOpacity});
1919 }
1920 }, options);
1921 var transition = options.transition || v.Transitions.sinoidal;
1922 options.transition = function (pos) {
1923 return transition(1 - v.Transitions.pulse(pos, options.pulses));
1924 };
1925 return new v.Opacity(element, options);
1926 };
1928 /** @id MochiKit.Visual.fold */
1929 MochiKit.Visual.fold = function (element, /* optional */ options) {
1930 /***
1932 Fold an element, first vertically, then horizontally.
1934 ***/
1935 var d = MochiKit.DOM;
1936 var v = MochiKit.Visual;
1937 var s = MochiKit.Style;
1938 element = d.getElement(element);
1939 var elementDimensions = s.getElementDimensions(element, true);
1940 var oldStyle = {
1941 top: element.style.top,
1942 left: element.style.left,
1943 width: element.style.width,
1944 height: element.style.height
1945 };
1946 var elemClip = s.makeClipping(element);
1947 options = MochiKit.Base.update({
1948 scaleContent: false,
1949 scaleX: false,
1950 scaleMode: {originalHeight: elementDimensions.h,
1951 originalWidth: elementDimensions.w},
1952 afterFinishInternal: function (effect) {
1953 new v.Scale(element, 1, {
1954 scaleContent: false,
1955 scaleY: false,
1956 scaleMode: {originalHeight: elementDimensions.h,
1957 originalWidth: elementDimensions.w},
1958 afterFinishInternal: function (effect) {
1959 s.hideElement(effect.element);
1960 s.undoClipping(effect.element, elemClip);
1961 s.setStyle(effect.element, oldStyle);
1962 }
1963 });
1964 }
1965 }, options);
1966 return new v.Scale(element, 5, options);
1967 };
1970 // Compatibility with MochiKit 1.0
1971 MochiKit.Visual.Color = MochiKit.Color.Color;
1972 MochiKit.Visual.getElementsComputedStyle = MochiKit.DOM.computedStyle;
1974 /* end of Rico adaptation */
1976 MochiKit.Visual.__new__ = function () {
1977 var m = MochiKit.Base;
1979 m.nameFunctions(this);
1981 this.EXPORT_TAGS = {
1982 ":common": this.EXPORT,
1983 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
1984 };
1986 };
1988 MochiKit.Visual.EXPORT = [
1989 "roundElement",
1990 "roundClass",
1991 "tagifyText",
1992 "multiple",
1993 "toggle",
1994 "Parallel",
1995 "Sequence",
1996 "Opacity",
1997 "Move",
1998 "Scale",
1999 "Highlight",
2000 "ScrollTo",
2001 "Morph",
2002 "fade",
2003 "appear",
2004 "puff",
2005 "blindUp",
2006 "blindDown",
2007 "switchOff",
2008 "dropOut",
2009 "shake",
2010 "slideDown",
2011 "slideUp",
2012 "squish",
2013 "grow",
2014 "shrink",
2015 "pulsate",
2016 "fold"
2017 ];
2019 MochiKit.Visual.EXPORT_OK = [
2020 "Base",
2021 "PAIRS"
2022 ];
2024 MochiKit.Visual.__new__();
2026 MochiKit.Base._exportSymbols(this, MochiKit.Visual);