Wed, 31 Dec 2014 06:09:35 +0100
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
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);