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