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