dom/tests/mochitest/ajax/mochikit/MochiKit/Visual.js

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial