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