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

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial