testing/mochitest/tests/MochiKit-1.4.2/MochiKit/Visual.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:81d7ab81fa83
1 /***
2
3 MochiKit.Visual 1.4.2
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito and others. All rights Reserved.
8
9 ***/
10
11 MochiKit.Base._deps('Visual', ['Base', 'DOM', 'Style', 'Color', 'Position']);
12
13 MochiKit.Visual.NAME = "MochiKit.Visual";
14 MochiKit.Visual.VERSION = "1.4.2";
15
16 MochiKit.Visual.__repr__ = function () {
17 return "[" + this.NAME + " " + this.VERSION + "]";
18 };
19
20 MochiKit.Visual.toString = function () {
21 return this.__repr__();
22 };
23
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 }
30
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);
39
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 }
46
47 this._roundCornersImpl(e, color, bgColor);
48 };
49
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 },
81
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 },
93
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 },
101
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 },
112
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 },
123
124 _createCorner: function (bgColor) {
125 var dom = MochiKit.DOM;
126 return dom.DIV({style: {backgroundColor: bgColor.toString()}});
127 },
128
129 _createCornerSlice: function (color, bgColor, n, position) {
130 var slice = MochiKit.DOM.SPAN();
131
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";
138
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 }
154
155 if (!this.options.compact && (n == (this.options.numSlices - 1))) {
156 inStyle.height = "2px";
157 }
158
159 this._setMargin(slice, n, position);
160 this._setBorder(slice, n, position);
161
162 return slice;
163 },
164
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);
176
177 this.options.numSlices = (this.options.compact ? 2 : 4);
178 },
179
180 _whichSideTop: function () {
181 var corners = this.options.corners;
182 if (this._hasString(corners, "all", "top")) {
183 return "";
184 }
185
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 },
199
200 _whichSideBottom: function () {
201 var corners = this.options.corners;
202 if (this._hasString(corners, "all", "bottom")) {
203 return "";
204 }
205
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 },
219
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 },
230
231
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;
238
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 },
250
251 _setBorder: function (el, n, corners) {
252 var borderSize = this._borderSize(n) + "px";
253 var whichSide = (
254 corners == "top" ? this._whichSideTop() : this._whichSideBottom()
255 );
256
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 },
269
270 _marginSize: function (n) {
271 if (this.isTransparent) {
272 return 0;
273 }
274
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 },
290
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 },
309
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 },
318
319 _isTopRounded: function () {
320 return this._hasString(this.options.corners,
321 "all", "top", "tl", "tr"
322 );
323 },
324
325 _isBottomRounded: function () {
326 return this._hasString(this.options.corners,
327 "all", "bottom", "bl", "br"
328 );
329 },
330
331 _hasSingleTextChild: function (el) {
332 return (el.childNodes.length == 1 && el.childNodes[0].nodeType == 3);
333 }
334 };
335
336 /** @id MochiKit.Visual.roundElement */
337 MochiKit.Visual.roundElement = function (e, options) {
338 new MochiKit.Visual._RoundCorners(e, options);
339 };
340
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 };
350
351 /** @id MochiKit.Visual.tagifyText */
352 MochiKit.Visual.tagifyText = function (element, /* optional */tagifyStyle) {
353 /***
354
355 Change a node text to character in tags.
356
357 @param tagifyStyle: the style to apply to character nodes, default to
358 'position: relative'.
359
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 };
378
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 };
389
390 /** @id MochiKit.Visual.multiple */
391 MochiKit.Visual.multiple = function (elements, effect, /* optional */options) {
392 /***
393
394 Launch the same effect subsequently on given elements.
395
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 };
408
409 MochiKit.Visual.PAIRS = {
410 'slide': ['slideDown', 'slideUp'],
411 'blind': ['blindDown', 'blindUp'],
412 'appear': ['appear', 'fade'],
413 'size': ['grow', 'shrink']
414 };
415
416 /** @id MochiKit.Visual.toggle */
417 MochiKit.Visual.toggle = function (element, /* optional */effect, /* optional */options) {
418 /***
419
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'.
423
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 };
434
435 /***
436
437 Transitions: define functions calculating variations depending of a position.
438
439 ***/
440
441 MochiKit.Visual.Transitions = {};
442
443 /** @id MochiKit.Visual.Transitions.linear */
444 MochiKit.Visual.Transitions.linear = function (pos) {
445 return pos;
446 };
447
448 /** @id MochiKit.Visual.Transitions.sinoidal */
449 MochiKit.Visual.Transitions.sinoidal = function (pos) {
450 return 0.5 - Math.cos(pos*Math.PI)/2;
451 };
452
453 /** @id MochiKit.Visual.Transitions.reverse */
454 MochiKit.Visual.Transitions.reverse = function (pos) {
455 return 1 - pos;
456 };
457
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 };
462
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 };
467
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 };
478
479 /** @id MochiKit.Visual.Transitions.parabolic */
480 MochiKit.Visual.Transitions.parabolic = function (pos) {
481 return pos * pos;
482 };
483
484 /** @id MochiKit.Visual.Transitions.none */
485 MochiKit.Visual.Transitions.none = function (pos) {
486 return 0;
487 };
488
489 /** @id MochiKit.Visual.Transitions.full */
490 MochiKit.Visual.Transitions.full = function (pos) {
491 return 1;
492 };
493
494 /***
495
496 Core effects
497
498 ***/
499
500 MochiKit.Visual.ScopedQueue = function () {
501 var cls = arguments.callee;
502 if (!(this instanceof cls)) {
503 return new cls();
504 }
505 this.__init__();
506 };
507
508 MochiKit.Base.update(MochiKit.Visual.ScopedQueue.prototype, {
509 __init__: function () {
510 this.effects = [];
511 this.interval = null;
512 },
513
514 /** @id MochiKit.Visual.ScopedQueue.prototype.add */
515 add: function (effect) {
516 var timestamp = new Date().getTime();
517
518 var position = (typeof(effect.options.queue) == 'string') ?
519 effect.options.queue : effect.options.queue.position;
520
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 }
549
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 }
556
557 if (!this.interval) {
558 this.interval = this.startLoop(MochiKit.Base.bind(this.loop, this),
559 40);
560 }
561 },
562
563 /** @id MochiKit.Visual.ScopedQueue.prototype.startLoop */
564 startLoop: function (func, interval) {
565 return setInterval(func, interval);
566 },
567
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 },
578
579 /** @id MochiKit.Visual.ScopedQueue.prototype.stopLoop */
580 stopLoop: function (interval) {
581 clearInterval(interval);
582 },
583
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 });
592
593 MochiKit.Visual.Queues = {
594 instances: {},
595
596 get: function (queueName) {
597 if (typeof(queueName) != 'string') {
598 return queueName;
599 }
600
601 if (!this.instances[queueName]) {
602 this.instances[queueName] = new MochiKit.Visual.ScopedQueue();
603 }
604 return this.instances[queueName];
605 }
606 };
607
608 MochiKit.Visual.Queue = MochiKit.Visual.Queues.get('global');
609
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 };
620
621 MochiKit.Visual.Base = function () {};
622
623 MochiKit.Visual.Base.prototype = {
624 /***
625
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.
628
629 ***/
630
631 __class__ : MochiKit.Visual.Base,
632
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 },
648
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 },
664
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 },
684
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 },
693
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 },
702
703 setup: function () {
704 },
705
706 finish: function () {
707 },
708
709 update: function (position) {
710 },
711
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 },
721
722 /** @id MochiKit.Visual.Base.prototype.repr */
723 repr: function () {
724 return '[' + this.__class__.NAME + ', options:' +
725 MochiKit.Base.repr(this.options) + ']';
726 }
727 };
728
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 }
735
736 this.__init__(effects, options);
737 };
738
739 MochiKit.Visual.Parallel.prototype = new MochiKit.Visual.Base();
740
741 MochiKit.Base.update(MochiKit.Visual.Parallel.prototype, {
742 /***
743
744 Run multiple effects at the same time.
745
746 ***/
747
748 __class__ : MochiKit.Visual.Parallel,
749
750 __init__: function (effects, options) {
751 this.effects = effects || [];
752 this.start(options);
753 },
754
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 },
761
762 /** @id MochiKit.Visual.Parallel.prototype.finish */
763 finish: function () {
764 MochiKit.Base.map(function (effect) {
765 effect.finalize();
766 }, this.effects);
767 }
768 });
769
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 };
778
779 MochiKit.Visual.Sequence.prototype = new MochiKit.Visual.Base();
780
781 MochiKit.Base.update(MochiKit.Visual.Sequence.prototype, {
782
783 __class__ : MochiKit.Visual.Sequence,
784
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 },
795
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 },
809
810 /** @id MochiKit.Visual.Sequence.prototype.finish */
811 finish: function () {
812 MochiKit.Base.map(function (effect) {
813 effect.finalize();
814 }, this.effects);
815 }
816 });
817
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 };
826
827 MochiKit.Visual.Opacity.prototype = new MochiKit.Visual.Base();
828
829 MochiKit.Base.update(MochiKit.Visual.Opacity.prototype, {
830 /***
831
832 Change the opacity of an element.
833
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.
836
837 ***/
838
839 __class__ : MochiKit.Visual.Opacity,
840
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 },
856
857 /** @id MochiKit.Visual.Opacity.prototype.update */
858 update: function (position) {
859 MochiKit.Style.setStyle(this.element, {'opacity': position});
860 }
861 });
862
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 };
871
872 MochiKit.Visual.Move.prototype = new MochiKit.Visual.Base();
873
874 MochiKit.Base.update(MochiKit.Visual.Move.prototype, {
875 /***
876
877 Move an element between its current position to a defined position
878
879 @param options: 'x' and 'y' for final positions, default to 0, 0.
880
881 ***/
882
883 __class__ : MochiKit.Visual.Move,
884
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 },
894
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);
902
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 }
910
911 this.originalLeft = parseFloat(MochiKit.Style.getStyle(this.element, 'left') || '0');
912 this.originalTop = parseFloat(MochiKit.Style.getStyle(this.element, 'top') || '0');
913
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 },
924
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 });
933
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 };
942
943 MochiKit.Visual.Scale.prototype = new MochiKit.Visual.Base();
944
945 MochiKit.Base.update(MochiKit.Visual.Scale.prototype, {
946 /***
947
948 Change the size of an element.
949
950 @param percent: final_size = percent*original_size
951
952 @param options: several options changing scale behaviour
953
954 ***/
955
956 __class__ : MochiKit.Visual.Scale,
957
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 },
971
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');
977
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']);
984
985 this.originalTop = this.element.offsetTop;
986 this.originalLeft = this.element.offsetLeft;
987
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', '%']);
996
997 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
998
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 },
1008
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 },
1021
1022 /** @id MochiKit.Visual.Scale.prototype.finish */
1023 finish: function () {
1024 if (this.restoreAfterFinish) {
1025 MochiKit.Style.setStyle(this.element, this.originalStyle);
1026 }
1027 },
1028
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 });
1064
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 };
1073
1074 MochiKit.Visual.Highlight.prototype = new MochiKit.Visual.Base();
1075
1076 MochiKit.Base.update(MochiKit.Visual.Highlight.prototype, {
1077 /***
1078
1079 Highlight an item of the page.
1080
1081 @param options: 'startcolor' for choosing highlighting color, default
1082 to '#ffff99'.
1083
1084 ***/
1085
1086 __class__ : MochiKit.Visual.Highlight,
1087
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 },
1095
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 });
1112
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 },
1131
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 },
1143
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 });
1152
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 };
1161
1162 MochiKit.Visual.ScrollTo.prototype = new MochiKit.Visual.Base();
1163
1164 MochiKit.Base.update(MochiKit.Visual.ScrollTo.prototype, {
1165 /***
1166
1167 Scroll to an element in the page.
1168
1169 ***/
1170
1171 __class__ : MochiKit.Visual.ScrollTo,
1172
1173 __init__: function (element, /* optional */options) {
1174 this.element = MochiKit.DOM.getElement(element);
1175 this.start(options);
1176 },
1177
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 },
1199
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 });
1207
1208 MochiKit.Visual.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1209
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 };
1217
1218 MochiKit.Visual.Morph.prototype = new MochiKit.Visual.Base();
1219
1220 MochiKit.Base.update(MochiKit.Visual.Morph.prototype, {
1221 /***
1222
1223 Morph effect: make a transformation from current style to the given style,
1224 automatically making a transition between the two.
1225
1226 ***/
1227
1228 __class__ : MochiKit.Visual.Morph,
1229
1230 __init__: function (element, /* optional */options) {
1231 this.element = MochiKit.DOM.getElement(element);
1232 this.start(options);
1233 },
1234
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();
1264
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 },
1280
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 });
1301
1302 /***
1303
1304 Combination effects.
1305
1306 ***/
1307
1308 /** @id MochiKit.Visual.fade */
1309 MochiKit.Visual.fade = function (element, /* optional */ options) {
1310 /***
1311
1312 Fade a given element: change its opacity and hide it in the end.
1313
1314 @param options: 'to' and 'from' to change opacity.
1315
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 };
1332
1333 /** @id MochiKit.Visual.appear */
1334 MochiKit.Visual.appear = function (element, /* optional */ options) {
1335 /***
1336
1337 Make an element appear.
1338
1339 @param options: 'to' and 'from' to change opacity.
1340
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 };
1359
1360 /** @id MochiKit.Visual.puff */
1361 MochiKit.Visual.puff = function (element, /* optional */ options) {
1362 /***
1363
1364 'Puff' an element: grow it to double size, fading it and make it hidden.
1365
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 };
1399
1400 /** @id MochiKit.Visual.blindUp */
1401 MochiKit.Visual.blindUp = function (element, /* optional */ options) {
1402 /***
1403
1404 Blind an element up: change its vertical size to 0.
1405
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 };
1425
1426 /** @id MochiKit.Visual.blindDown */
1427 MochiKit.Visual.blindDown = function (element, /* optional */ options) {
1428 /***
1429
1430 Blind an element down: restore its vertical size.
1431
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 };
1456
1457 /** @id MochiKit.Visual.switchOff */
1458 MochiKit.Visual.switchOff = function (element, /* optional */ options) {
1459 /***
1460
1461 Apply a switch-off-like effect.
1462
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 };
1497
1498 /** @id MochiKit.Visual.dropOut */
1499 MochiKit.Visual.dropOut = function (element, /* optional */ options) {
1500 /***
1501
1502 Make an element fall and disappear.
1503
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 };
1513
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 };
1532
1533 /** @id MochiKit.Visual.shake */
1534 MochiKit.Visual.shake = function (element, /* optional */ options) {
1535 /***
1536
1537 Move an element from left to right several times.
1538
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 };
1570
1571 /** @id MochiKit.Visual.slideDown */
1572 MochiKit.Visual.slideDown = function (element, /* optional */ options) {
1573 /***
1574
1575 Slide an element down.
1576 It needs to have the content of the element wrapped in a container
1577 element with fixed height.
1578
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);
1626
1627 return new MochiKit.Visual.Scale(element, 100, options);
1628 };
1629
1630 /** @id MochiKit.Visual.slideUp */
1631 MochiKit.Visual.slideUp = function (element, /* optional */ options) {
1632 /***
1633
1634 Slide an element up.
1635 It needs to have the content of the element wrapped in a container
1636 element with fixed height.
1637
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 };
1681
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 /***
1687
1688 Reduce an element and make it disappear.
1689
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);
1708
1709 return new MochiKit.Visual.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, options);
1710 };
1711
1712 /** @id MochiKit.Visual.grow */
1713 MochiKit.Visual.grow = function (element, /* optional */ options) {
1714 /***
1715
1716 Grow an element to its original size. Make it zero-sized before
1717 if necessary.
1718
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;
1742
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 }
1770
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);
1782
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 };
1817
1818 /** @id MochiKit.Visual.shrink */
1819 MochiKit.Visual.shrink = function (element, /* optional */ options) {
1820 /***
1821
1822 Shrink an element and make it disappear.
1823
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 };
1844
1845 var dims = s.getElementDimensions(element, true);
1846 var moveX, moveY;
1847
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;
1870
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);
1883
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 };
1902
1903 /** @id MochiKit.Visual.pulsate */
1904 MochiKit.Visual.pulsate = function (element, /* optional */ options) {
1905 /***
1906
1907 Pulse an element between appear/fade.
1908
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 };
1927
1928 /** @id MochiKit.Visual.fold */
1929 MochiKit.Visual.fold = function (element, /* optional */ options) {
1930 /***
1931
1932 Fold an element, first vertically, then horizontally.
1933
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 };
1968
1969
1970 // Compatibility with MochiKit 1.0
1971 MochiKit.Visual.Color = MochiKit.Color.Color;
1972 MochiKit.Visual.getElementsComputedStyle = MochiKit.DOM.computedStyle;
1973
1974 /* end of Rico adaptation */
1975
1976 MochiKit.Visual.__new__ = function () {
1977 var m = MochiKit.Base;
1978
1979 m.nameFunctions(this);
1980
1981 this.EXPORT_TAGS = {
1982 ":common": this.EXPORT,
1983 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
1984 };
1985
1986 };
1987
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 ];
2018
2019 MochiKit.Visual.EXPORT_OK = [
2020 "Base",
2021 "PAIRS"
2022 ];
2023
2024 MochiKit.Visual.__new__();
2025
2026 MochiKit.Base._exportSymbols(this, MochiKit.Visual);

mercurial