testing/mochitest/MochiKit/Visual.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial