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

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

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

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

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

mercurial