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