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

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

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

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

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

mercurial