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

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial