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

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

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

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

     1 /***
     3 MochiKit.Signal 1.4.2
     5 See <http://mochikit.com/> for documentation, downloads, license, etc.
     7 (c) 2006 Jonathan Gardner, Beau Hartshorne, Bob Ippolito.  All rights Reserved.
     9 ***/
    11 MochiKit.Base._deps('Signal', ['Base', 'DOM', 'Style']);
    13 MochiKit.Signal.NAME = 'MochiKit.Signal';
    14 MochiKit.Signal.VERSION = '1.4.2';
    16 MochiKit.Signal._observers = [];
    18 /** @id MochiKit.Signal.Event */
    19 MochiKit.Signal.Event = function (src, e) {
    20     this._event = e || window.event;
    21     this._src = src;
    22 };
    24 MochiKit.Base.update(MochiKit.Signal.Event.prototype, {
    26     __repr__: function () {
    27         var repr = MochiKit.Base.repr;
    28         var str = '{event(): ' + repr(this.event()) +
    29             ', src(): ' + repr(this.src()) +
    30             ', type(): ' + repr(this.type()) +
    31             ', target(): ' + repr(this.target());
    33         if (this.type() &&
    34             this.type().indexOf('key') === 0 ||
    35             this.type().indexOf('mouse') === 0 ||
    36             this.type().indexOf('click') != -1 ||
    37             this.type() == 'contextmenu') {
    38             str += ', modifier(): ' + '{alt: ' + repr(this.modifier().alt) +
    39             ', ctrl: ' + repr(this.modifier().ctrl) +
    40             ', meta: ' + repr(this.modifier().meta) +
    41             ', shift: ' + repr(this.modifier().shift) +
    42             ', any: ' + repr(this.modifier().any) + '}';
    43         }
    45         if (this.type() && this.type().indexOf('key') === 0) {
    46             str += ', key(): {code: ' + repr(this.key().code) +
    47                 ', string: ' + repr(this.key().string) + '}';
    48         }
    50         if (this.type() && (
    51             this.type().indexOf('mouse') === 0 ||
    52             this.type().indexOf('click') != -1 ||
    53             this.type() == 'contextmenu')) {
    55             str += ', mouse(): {page: ' + repr(this.mouse().page) +
    56                 ', client: ' + repr(this.mouse().client);
    58             if (this.type() != 'mousemove' && this.type() != 'mousewheel') {
    59                 str += ', button: {left: ' + repr(this.mouse().button.left) +
    60                     ', middle: ' + repr(this.mouse().button.middle) +
    61                     ', right: ' + repr(this.mouse().button.right) + '}';
    62             }
    63             if (this.type() == 'mousewheel') {
    64                 str += ', wheel: ' + repr(this.mouse().wheel);
    65             }
    66             str += '}';
    67         }
    68         if (this.type() == 'mouseover' || this.type() == 'mouseout' || 
    69             this.type() == 'mouseenter' || this.type() == 'mouseleave') {
    70             str += ', relatedTarget(): ' + repr(this.relatedTarget());
    71         }
    72         str += '}';
    73         return str;
    74     },
    76      /** @id MochiKit.Signal.Event.prototype.toString */
    77     toString: function () {
    78         return this.__repr__();
    79     },
    81     /** @id MochiKit.Signal.Event.prototype.src */
    82     src: function () {
    83         return this._src;
    84     },
    86     /** @id MochiKit.Signal.Event.prototype.event  */
    87     event: function () {
    88         return this._event;
    89     },
    91     /** @id MochiKit.Signal.Event.prototype.type */
    92     type: function () {
    93         if (this._event.type === "DOMMouseScroll") {
    94             return "mousewheel";
    95         } else {
    96             return this._event.type || undefined;
    97         }
    98     },
   100     /** @id MochiKit.Signal.Event.prototype.target */
   101     target: function () {
   102         return this._event.target || this._event.srcElement;
   103     },
   105     _relatedTarget: null,
   106     /** @id MochiKit.Signal.Event.prototype.relatedTarget */
   107     relatedTarget: function () {
   108         if (this._relatedTarget !== null) {
   109             return this._relatedTarget;
   110         }
   112         var elem = null;
   113         if (this.type() == 'mouseover' || this.type() == 'mouseenter') {
   114             elem = (this._event.relatedTarget ||
   115                 this._event.fromElement);
   116         } else if (this.type() == 'mouseout' || this.type() == 'mouseleave') {
   117             elem = (this._event.relatedTarget ||
   118                 this._event.toElement);
   119         }
   120         try {
   121             if (elem !== null && elem.nodeType !== null) {
   122                 this._relatedTarget = elem;
   123                 return elem;
   124             }
   125         } catch (ignore) {
   126             // Firefox 3 throws a permission denied error when accessing
   127             // any property on XUL elements (e.g. scrollbars)...
   128         }
   130         return undefined;
   131     },
   133     _modifier: null,
   134     /** @id MochiKit.Signal.Event.prototype.modifier */
   135     modifier: function () {
   136         if (this._modifier !== null) {
   137             return this._modifier;
   138         }
   139         var m = {};
   140         m.alt = this._event.altKey;
   141         m.ctrl = this._event.ctrlKey;
   142         m.meta = this._event.metaKey || false; // IE and Opera punt here
   143         m.shift = this._event.shiftKey;
   144         m.any = m.alt || m.ctrl || m.shift || m.meta;
   145         this._modifier = m;
   146         return m;
   147     },
   149     _key: null,
   150     /** @id MochiKit.Signal.Event.prototype.key */
   151     key: function () {
   152         if (this._key !== null) {
   153             return this._key;
   154         }
   155         var k = {};
   156         if (this.type() && this.type().indexOf('key') === 0) {
   158             /*
   160                 If you're looking for a special key, look for it in keydown or
   161                 keyup, but never keypress. If you're looking for a Unicode
   162                 chracter, look for it with keypress, but never keyup or
   163                 keydown.
   165                 Notes:
   167                 FF key event behavior:
   168                 key     event   charCode    keyCode
   169                 DOWN    ku,kd   0           40
   170                 DOWN    kp      0           40
   171                 ESC     ku,kd   0           27
   172                 ESC     kp      0           27
   173                 a       ku,kd   0           65
   174                 a       kp      97          0
   175                 shift+a ku,kd   0           65
   176                 shift+a kp      65          0
   177                 1       ku,kd   0           49
   178                 1       kp      49          0
   179                 shift+1 ku,kd   0           0
   180                 shift+1 kp      33          0
   182                 IE key event behavior:
   183                 (IE doesn't fire keypress events for special keys.)
   184                 key     event   keyCode
   185                 DOWN    ku,kd   40
   186                 DOWN    kp      undefined
   187                 ESC     ku,kd   27
   188                 ESC     kp      27
   189                 a       ku,kd   65
   190                 a       kp      97
   191                 shift+a ku,kd   65
   192                 shift+a kp      65
   193                 1       ku,kd   49
   194                 1       kp      49
   195                 shift+1 ku,kd   49
   196                 shift+1 kp      33
   198                 Safari key event behavior:
   199                 (Safari sets charCode and keyCode to something crazy for
   200                 special keys.)
   201                 key     event   charCode    keyCode
   202                 DOWN    ku,kd   63233       40
   203                 DOWN    kp      63233       63233
   204                 ESC     ku,kd   27          27
   205                 ESC     kp      27          27
   206                 a       ku,kd   97          65
   207                 a       kp      97          97
   208                 shift+a ku,kd   65          65
   209                 shift+a kp      65          65
   210                 1       ku,kd   49          49
   211                 1       kp      49          49
   212                 shift+1 ku,kd   33          49
   213                 shift+1 kp      33          33
   215             */
   217             /* look for special keys here */
   218             if (this.type() == 'keydown' || this.type() == 'keyup') {
   219                 k.code = this._event.keyCode;
   220                 k.string = (MochiKit.Signal._specialKeys[k.code] ||
   221                     'KEY_UNKNOWN');
   222                 this._key = k;
   223                 return k;
   225             /* look for characters here */
   226             } else if (this.type() == 'keypress') {
   228                 /*
   230                     Special key behavior:
   232                     IE: does not fire keypress events for special keys
   233                     FF: sets charCode to 0, and sets the correct keyCode
   234                     Safari: sets keyCode and charCode to something stupid
   236                 */
   238                 k.code = 0;
   239                 k.string = '';
   241                 if (typeof(this._event.charCode) != 'undefined' &&
   242                     this._event.charCode !== 0 &&
   243                     !MochiKit.Signal._specialMacKeys[this._event.charCode]) {
   244                     k.code = this._event.charCode;
   245                     k.string = String.fromCharCode(k.code);
   246                 } else if (this._event.keyCode &&
   247                     typeof(this._event.charCode) == 'undefined') { // IE
   248                     k.code = this._event.keyCode;
   249                     k.string = String.fromCharCode(k.code);
   250                 }
   252                 this._key = k;
   253                 return k;
   254             }
   255         }
   256         return undefined;
   257     },
   259     _mouse: null,
   260     /** @id MochiKit.Signal.Event.prototype.mouse */
   261     mouse: function () {
   262         if (this._mouse !== null) {
   263             return this._mouse;
   264         }
   266         var m = {};
   267         var e = this._event;
   269         if (this.type() && (
   270             this.type().indexOf('mouse') === 0 ||
   271             this.type().indexOf('click') != -1 ||
   272             this.type() == 'contextmenu')) {
   274             m.client = new MochiKit.Style.Coordinates(0, 0);
   275             if (e.clientX || e.clientY) {
   276                 m.client.x = (!e.clientX || e.clientX < 0) ? 0 : e.clientX;
   277                 m.client.y = (!e.clientY || e.clientY < 0) ? 0 : e.clientY;
   278             }
   280             m.page = new MochiKit.Style.Coordinates(0, 0);
   281             if (e.pageX || e.pageY) {
   282                 m.page.x = (!e.pageX || e.pageX < 0) ? 0 : e.pageX;
   283                 m.page.y = (!e.pageY || e.pageY < 0) ? 0 : e.pageY;
   284             } else {
   285                 /*
   287                     The IE shortcut can be off by two. We fix it. See:
   288                     http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/getboundingclientrect.asp
   290                     This is similar to the method used in
   291                     MochiKit.Style.getElementPosition().
   293                 */
   294                 var de = MochiKit.DOM._document.documentElement;
   295                 var b = MochiKit.DOM._document.body;
   297                 m.page.x = e.clientX +
   298                     (de.scrollLeft || b.scrollLeft) -
   299                     (de.clientLeft || 0);
   301                 m.page.y = e.clientY +
   302                     (de.scrollTop || b.scrollTop) -
   303                     (de.clientTop || 0);
   305             }
   306             if (this.type() != 'mousemove' && this.type() != 'mousewheel') {
   307                 m.button = {};
   308                 m.button.left = false;
   309                 m.button.right = false;
   310                 m.button.middle = false;
   312                 /* we could check e.button, but which is more consistent */
   313                 if (e.which) {
   314                     m.button.left = (e.which == 1);
   315                     m.button.middle = (e.which == 2);
   316                     m.button.right = (e.which == 3);
   318                     /*
   320                         Mac browsers and right click:
   322                             - Safari doesn't fire any click events on a right
   323                               click:
   324                               http://bugs.webkit.org/show_bug.cgi?id=6595
   326                             - Firefox fires the event, and sets ctrlKey = true
   328                             - Opera fires the event, and sets metaKey = true
   330                         oncontextmenu is fired on right clicks between
   331                         browsers and across platforms.
   333                     */
   335                 } else {
   336                     m.button.left = !!(e.button & 1);
   337                     m.button.right = !!(e.button & 2);
   338                     m.button.middle = !!(e.button & 4);
   339                 }
   340             }
   341             if (this.type() == 'mousewheel') {
   342                 m.wheel = new MochiKit.Style.Coordinates(0, 0);
   343                 if (e.wheelDeltaX || e.wheelDeltaY) {
   344                     m.wheel.x = e.wheelDeltaX / -40 || 0;
   345                     m.wheel.y = e.wheelDeltaY / -40 || 0;
   346                 } else if (e.wheelDelta) {
   347                     m.wheel.y = e.wheelDelta / -40;
   348                 } else {
   349                     m.wheel.y = e.detail || 0;
   350                 }
   351             }
   352             this._mouse = m;
   353             return m;
   354         }
   355         return undefined;
   356     },
   358     /** @id MochiKit.Signal.Event.prototype.stop */
   359     stop: function () {
   360         this.stopPropagation();
   361         this.preventDefault();
   362     },
   364     /** @id MochiKit.Signal.Event.prototype.stopPropagation */
   365     stopPropagation: function () {
   366         if (this._event.stopPropagation) {
   367             this._event.stopPropagation();
   368         } else {
   369             this._event.cancelBubble = true;
   370         }
   371     },
   373     /** @id MochiKit.Signal.Event.prototype.preventDefault */
   374     preventDefault: function () {
   375         if (this._event.preventDefault) {
   376             this._event.preventDefault();
   377         } else if (this._confirmUnload === null) {
   378             this._event.returnValue = false;
   379         }
   380     },
   382     _confirmUnload: null,
   384     /** @id MochiKit.Signal.Event.prototype.confirmUnload */
   385     confirmUnload: function (msg) {
   386         if (this.type() == 'beforeunload') {
   387             this._confirmUnload = msg;
   388             this._event.returnValue = msg;
   389         }
   390     }
   391 });
   393 /* Safari sets keyCode to these special values onkeypress. */
   394 MochiKit.Signal._specialMacKeys = {
   395     3: 'KEY_ENTER',
   396     63289: 'KEY_NUM_PAD_CLEAR',
   397     63276: 'KEY_PAGE_UP',
   398     63277: 'KEY_PAGE_DOWN',
   399     63275: 'KEY_END',
   400     63273: 'KEY_HOME',
   401     63234: 'KEY_ARROW_LEFT',
   402     63232: 'KEY_ARROW_UP',
   403     63235: 'KEY_ARROW_RIGHT',
   404     63233: 'KEY_ARROW_DOWN',
   405     63302: 'KEY_INSERT',
   406     63272: 'KEY_DELETE'
   407 };
   409 /* for KEY_F1 - KEY_F12 */
   410 (function () {
   411     var _specialMacKeys = MochiKit.Signal._specialMacKeys;
   412     for (i = 63236; i <= 63242; i++) {
   413         // no F0
   414         _specialMacKeys[i] = 'KEY_F' + (i - 63236 + 1);
   415     }
   416 })();
   418 /* Standard keyboard key codes. */
   419 MochiKit.Signal._specialKeys = {
   420     8: 'KEY_BACKSPACE',
   421     9: 'KEY_TAB',
   422     12: 'KEY_NUM_PAD_CLEAR', // weird, for Safari and Mac FF only
   423     13: 'KEY_ENTER',
   424     16: 'KEY_SHIFT',
   425     17: 'KEY_CTRL',
   426     18: 'KEY_ALT',
   427     19: 'KEY_PAUSE',
   428     20: 'KEY_CAPS_LOCK',
   429     27: 'KEY_ESCAPE',
   430     32: 'KEY_SPACEBAR',
   431     33: 'KEY_PAGE_UP',
   432     34: 'KEY_PAGE_DOWN',
   433     35: 'KEY_END',
   434     36: 'KEY_HOME',
   435     37: 'KEY_ARROW_LEFT',
   436     38: 'KEY_ARROW_UP',
   437     39: 'KEY_ARROW_RIGHT',
   438     40: 'KEY_ARROW_DOWN',
   439     44: 'KEY_PRINT_SCREEN',
   440     45: 'KEY_INSERT',
   441     46: 'KEY_DELETE',
   442     59: 'KEY_SEMICOLON', // weird, for Safari and IE only
   443     91: 'KEY_WINDOWS_LEFT',
   444     92: 'KEY_WINDOWS_RIGHT',
   445     93: 'KEY_SELECT',
   446     106: 'KEY_NUM_PAD_ASTERISK',
   447     107: 'KEY_NUM_PAD_PLUS_SIGN',
   448     109: 'KEY_NUM_PAD_HYPHEN-MINUS',
   449     110: 'KEY_NUM_PAD_FULL_STOP',
   450     111: 'KEY_NUM_PAD_SOLIDUS',
   451     144: 'KEY_NUM_LOCK',
   452     145: 'KEY_SCROLL_LOCK',
   453     186: 'KEY_SEMICOLON',
   454     187: 'KEY_EQUALS_SIGN',
   455     188: 'KEY_COMMA',
   456     189: 'KEY_HYPHEN-MINUS',
   457     190: 'KEY_FULL_STOP',
   458     191: 'KEY_SOLIDUS',
   459     192: 'KEY_GRAVE_ACCENT',
   460     219: 'KEY_LEFT_SQUARE_BRACKET',
   461     220: 'KEY_REVERSE_SOLIDUS',
   462     221: 'KEY_RIGHT_SQUARE_BRACKET',
   463     222: 'KEY_APOSTROPHE'
   464     // undefined: 'KEY_UNKNOWN'
   465 };
   467 (function () {
   468     /* for KEY_0 - KEY_9 */
   469     var _specialKeys = MochiKit.Signal._specialKeys;
   470     for (var i = 48; i <= 57; i++) {
   471         _specialKeys[i] = 'KEY_' + (i - 48);
   472     }
   474     /* for KEY_A - KEY_Z */
   475     for (i = 65; i <= 90; i++) {
   476         _specialKeys[i] = 'KEY_' + String.fromCharCode(i);
   477     }
   479     /* for KEY_NUM_PAD_0 - KEY_NUM_PAD_9 */
   480     for (i = 96; i <= 105; i++) {
   481         _specialKeys[i] = 'KEY_NUM_PAD_' + (i - 96);
   482     }
   484     /* for KEY_F1 - KEY_F12 */
   485     for (i = 112; i <= 123; i++) {
   486         // no F0
   487         _specialKeys[i] = 'KEY_F' + (i - 112 + 1);
   488     }
   489 })();
   491 /* Internal object to keep track of created signals. */
   492 MochiKit.Signal.Ident = function (ident) {
   493     this.source = ident.source;
   494     this.signal = ident.signal;
   495     this.listener = ident.listener;
   496     this.isDOM = ident.isDOM;
   497     this.objOrFunc = ident.objOrFunc;
   498     this.funcOrStr = ident.funcOrStr;
   499     this.connected = ident.connected;
   500 };
   502 MochiKit.Signal.Ident.prototype = {};
   504 MochiKit.Base.update(MochiKit.Signal, {
   506     __repr__: function () {
   507         return '[' + this.NAME + ' ' + this.VERSION + ']';
   508     },
   510     toString: function () {
   511         return this.__repr__();
   512     },
   514     _unloadCache: function () {
   515         var self = MochiKit.Signal;
   516         var observers = self._observers;
   518         for (var i = 0; i < observers.length; i++) {
   519             if (observers[i].signal !== 'onload' && observers[i].signal !== 'onunload') {
   520                 self._disconnect(observers[i]);
   521             }
   522         }
   523     },
   525     _listener: function (src, sig, func, obj, isDOM) {
   526         var self = MochiKit.Signal;
   527         var E = self.Event;
   528         if (!isDOM) {
   529             /* We don't want to re-bind already bound methods */
   530             if (typeof(func.im_self) == 'undefined') {
   531                 return MochiKit.Base.bindLate(func, obj);
   532             } else {
   533                 return func;
   534             }
   535         }
   536         obj = obj || src;
   537         if (typeof(func) == "string") {
   538             if (sig === 'onload' || sig === 'onunload') {
   539                 return function (nativeEvent) {
   540                     obj[func].apply(obj, [new E(src, nativeEvent)]);
   542                     var ident = new MochiKit.Signal.Ident({
   543                         source: src, signal: sig, objOrFunc: obj, funcOrStr: func});
   545                     MochiKit.Signal._disconnect(ident);
   546                 };
   547             } else {
   548                 return function (nativeEvent) {
   549                     obj[func].apply(obj, [new E(src, nativeEvent)]);
   550                 };
   551             }
   552         } else {
   553             if (sig === 'onload' || sig === 'onunload') {
   554                 return function (nativeEvent) {
   555                     func.apply(obj, [new E(src, nativeEvent)]);
   557                     var ident = new MochiKit.Signal.Ident({
   558                         source: src, signal: sig, objOrFunc: func});
   560                     MochiKit.Signal._disconnect(ident);
   561                 };
   562             } else {
   563                 return function (nativeEvent) {
   564                     func.apply(obj, [new E(src, nativeEvent)]);
   565                 };
   566             }
   567         }
   568     },
   570     _browserAlreadyHasMouseEnterAndLeave: function () {
   571         return /MSIE/.test(navigator.userAgent);
   572     },
   574     _browserLacksMouseWheelEvent: function () {
   575         return /Gecko\//.test(navigator.userAgent);
   576     },
   578     _mouseEnterListener: function (src, sig, func, obj) {
   579         var E = MochiKit.Signal.Event;
   580         return function (nativeEvent) {
   581             var e = new E(src, nativeEvent);
   582             try {
   583                 e.relatedTarget().nodeName;
   584             } catch (err) {
   585                 /* probably hit a permission denied error; possibly one of
   586                  * firefox's screwy anonymous DIVs inside an input element.
   587                  * Allow this event to propogate up.
   588                  */
   589                 return;
   590             }
   591             e.stop();
   592             if (MochiKit.DOM.isChildNode(e.relatedTarget(), src)) {
   593                 /* We've moved between our node and a child. Ignore. */
   594                 return;
   595             }
   596             e.type = function () { return sig; };
   597             if (typeof(func) == "string") {
   598                 return obj[func].apply(obj, [e]);
   599             } else {
   600                 return func.apply(obj, [e]);
   601             }
   602         };
   603     },
   605     _getDestPair: function (objOrFunc, funcOrStr) {
   606         var obj = null;
   607         var func = null;
   608         if (typeof(funcOrStr) != 'undefined') {
   609             obj = objOrFunc;
   610             func = funcOrStr;
   611             if (typeof(funcOrStr) == 'string') {
   612                 if (typeof(objOrFunc[funcOrStr]) != "function") {
   613                     throw new Error("'funcOrStr' must be a function on 'objOrFunc'");
   614                 }
   615             } else if (typeof(funcOrStr) != 'function') {
   616                 throw new Error("'funcOrStr' must be a function or string");
   617             }
   618         } else if (typeof(objOrFunc) != "function") {
   619             throw new Error("'objOrFunc' must be a function if 'funcOrStr' is not given");
   620         } else {
   621             func = objOrFunc;
   622         }
   623         return [obj, func];
   624     },
   626     /** @id MochiKit.Signal.connect */
   627     connect: function (src, sig, objOrFunc/* optional */, funcOrStr) {
   628         src = MochiKit.DOM.getElement(src);
   629         var self = MochiKit.Signal;
   631         if (typeof(sig) != 'string') {
   632             throw new Error("'sig' must be a string");
   633         }
   635         var destPair = self._getDestPair(objOrFunc, funcOrStr);
   636         var obj = destPair[0];
   637         var func = destPair[1];
   638         if (typeof(obj) == 'undefined' || obj === null) {
   639             obj = src;
   640         }
   642         var isDOM = !!(src.addEventListener || src.attachEvent);
   643         if (isDOM && (sig === "onmouseenter" || sig === "onmouseleave")
   644                   && !self._browserAlreadyHasMouseEnterAndLeave()) {
   645             var listener = self._mouseEnterListener(src, sig.substr(2), func, obj);
   646             if (sig === "onmouseenter") {
   647                 sig = "onmouseover";
   648             } else {
   649                 sig = "onmouseout";
   650             }
   651         } else if (isDOM && sig == "onmousewheel" && self._browserLacksMouseWheelEvent()) {
   652             var listener = self._listener(src, sig, func, obj, isDOM);
   653             sig = "onDOMMouseScroll";
   654         } else {
   655             var listener = self._listener(src, sig, func, obj, isDOM);
   656         }
   658         if (src.addEventListener) {
   659             src.addEventListener(sig.substr(2), listener, false);
   660         } else if (src.attachEvent) {
   661             src.attachEvent(sig, listener); // useCapture unsupported
   662         }
   664         var ident = new MochiKit.Signal.Ident({
   665             source: src, 
   666             signal: sig, 
   667             listener: listener, 
   668             isDOM: isDOM, 
   669             objOrFunc: objOrFunc, 
   670             funcOrStr: funcOrStr, 
   671             connected: true
   672         });
   673         self._observers.push(ident);
   675         if (!isDOM && typeof(src.__connect__) == 'function') {
   676             var args = MochiKit.Base.extend([ident], arguments, 1);
   677             src.__connect__.apply(src, args);
   678         }
   680         return ident;
   681     },
   683     _disconnect: function (ident) {
   684         // already disconnected
   685         if (!ident.connected) {
   686             return;
   687         }
   688         ident.connected = false;
   689         var src = ident.source;
   690         var sig = ident.signal;
   691         var listener = ident.listener;
   692         // check isDOM
   693         if (!ident.isDOM) {
   694             if (typeof(src.__disconnect__) == 'function') {
   695                 src.__disconnect__(ident, sig, ident.objOrFunc, ident.funcOrStr);
   696             }
   697             return;
   698         }
   699         if (src.removeEventListener) {
   700             src.removeEventListener(sig.substr(2), listener, false);
   701         } else if (src.detachEvent) {
   702             src.detachEvent(sig, listener); // useCapture unsupported
   703         } else {
   704             throw new Error("'src' must be a DOM element");
   705         }
   706     },
   708      /** @id MochiKit.Signal.disconnect */
   709     disconnect: function (ident) {
   710         var self = MochiKit.Signal;
   711         var observers = self._observers;
   712         var m = MochiKit.Base;
   713         if (arguments.length > 1) {
   714             // compatibility API
   715             var src = MochiKit.DOM.getElement(arguments[0]);
   716             var sig = arguments[1];
   717             var obj = arguments[2];
   718             var func = arguments[3];
   719             for (var i = observers.length - 1; i >= 0; i--) {
   720                 var o = observers[i];
   721                 if (o.source === src && o.signal === sig && o.objOrFunc === obj && o.funcOrStr === func) {
   722                     self._disconnect(o);
   723                     if (!self._lock) {
   724                         observers.splice(i, 1);
   725                     } else {
   726                         self._dirty = true;
   727                     }
   728                     return true;
   729                 }
   730             }
   731         } else {
   732             var idx = m.findIdentical(observers, ident);
   733             if (idx >= 0) {
   734                 self._disconnect(ident);
   735                 if (!self._lock) {
   736                     observers.splice(idx, 1);
   737                 } else {
   738                     self._dirty = true;
   739                 }
   740                 return true;
   741             }
   742         }
   743         return false;
   744     },
   746     /** @id MochiKit.Signal.disconnectAllTo */
   747     disconnectAllTo: function (objOrFunc, /* optional */funcOrStr) {
   748         var self = MochiKit.Signal;
   749         var observers = self._observers;
   750         var disconnect = self._disconnect;
   751         var locked = self._lock;
   752         var dirty = self._dirty;
   753         if (typeof(funcOrStr) === 'undefined') {
   754             funcOrStr = null;
   755         }
   756         for (var i = observers.length - 1; i >= 0; i--) {
   757             var ident = observers[i];
   758             if (ident.objOrFunc === objOrFunc &&
   759                     (funcOrStr === null || ident.funcOrStr === funcOrStr)) {
   760                 disconnect(ident);
   761                 if (locked) {
   762                     dirty = true;
   763                 } else {
   764                     observers.splice(i, 1);
   765                 }
   766             }
   767         }
   768         self._dirty = dirty;
   769     },
   771     /** @id MochiKit.Signal.disconnectAll */
   772     disconnectAll: function (src/* optional */, sig) {
   773         src = MochiKit.DOM.getElement(src);
   774         var m = MochiKit.Base;
   775         var signals = m.flattenArguments(m.extend(null, arguments, 1));
   776         var self = MochiKit.Signal;
   777         var disconnect = self._disconnect;
   778         var observers = self._observers;
   779         var i, ident;
   780         var locked = self._lock;
   781         var dirty = self._dirty;
   782         if (signals.length === 0) {
   783             // disconnect all
   784             for (i = observers.length - 1; i >= 0; i--) {
   785                 ident = observers[i];
   786                 if (ident.source === src) {
   787                     disconnect(ident);
   788                     if (!locked) {
   789                         observers.splice(i, 1);
   790                     } else {
   791                         dirty = true;
   792                     }
   793                 }
   794             }
   795         } else {
   796             var sigs = {};
   797             for (i = 0; i < signals.length; i++) {
   798                 sigs[signals[i]] = true;
   799             }
   800             for (i = observers.length - 1; i >= 0; i--) {
   801                 ident = observers[i];
   802                 if (ident.source === src && ident.signal in sigs) {
   803                     disconnect(ident);
   804                     if (!locked) {
   805                         observers.splice(i, 1);
   806                     } else {
   807                         dirty = true;
   808                     }
   809                 }
   810             }
   811         }
   812         self._dirty = dirty;
   813     },
   815     /** @id MochiKit.Signal.signal */
   816     signal: function (src, sig) {
   817         var self = MochiKit.Signal;
   818         var observers = self._observers;
   819         src = MochiKit.DOM.getElement(src);
   820         var args = MochiKit.Base.extend(null, arguments, 2);
   821         var errors = [];
   822         self._lock = true;
   823         for (var i = 0; i < observers.length; i++) {
   824             var ident = observers[i];
   825             if (ident.source === src && ident.signal === sig &&
   826                     ident.connected) {
   827                 try {
   828                     ident.listener.apply(src, args);
   829                 } catch (e) {
   830                     errors.push(e);
   831                 }
   832             }
   833         }
   834         self._lock = false;
   835         if (self._dirty) {
   836             self._dirty = false;
   837             for (var i = observers.length - 1; i >= 0; i--) {
   838                 if (!observers[i].connected) {
   839                     observers.splice(i, 1);
   840                 }
   841             }
   842         }
   843         if (errors.length == 1) {
   844             throw errors[0];
   845         } else if (errors.length > 1) {
   846             var e = new Error("Multiple errors thrown in handling 'sig', see errors property");
   847             e.errors = errors;
   848             throw e;
   849         }
   850     }
   852 });
   854 MochiKit.Signal.EXPORT_OK = [];
   856 MochiKit.Signal.EXPORT = [
   857     'connect',
   858     'disconnect',
   859     'signal',
   860     'disconnectAll',
   861     'disconnectAllTo'
   862 ];
   864 MochiKit.Signal.__new__ = function (win) {
   865     var m = MochiKit.Base;
   866     this._document = document;
   867     this._window = win;
   868     this._lock = false;
   869     this._dirty = false;
   871     try {
   872         this.connect(window, 'onunload', this._unloadCache);
   873     } catch (e) {
   874         // pass: might not be a browser
   875     }
   877     this.EXPORT_TAGS = {
   878         ':common': this.EXPORT,
   879         ':all': m.concat(this.EXPORT, this.EXPORT_OK)
   880     };
   882     m.nameFunctions(this);
   883 };
   885 MochiKit.Signal.__new__(this);
   887 //
   888 // XXX: Internet Explorer blows
   889 //
   890 if (MochiKit.__export__) {
   891     connect = MochiKit.Signal.connect;
   892     disconnect = MochiKit.Signal.disconnect;
   893     disconnectAll = MochiKit.Signal.disconnectAll;
   894     signal = MochiKit.Signal.signal;
   895 }
   897 MochiKit.Base._exportSymbols(this, MochiKit.Signal);

mercurial