testing/mochitest/MochiKit/Signal.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial