1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/trace" 5 ], 6 function(FBTrace) { 7 8 // ********************************************************************************************* // 9 // Constants 10 11 var Events = {}; 12 13 // ********************************************************************************************* // 14 15 Events.dispatch = function(listeners, name, args) 16 { 17 if (!listeners) 18 { 19 if (FBTrace.DBG_DISPATCH) 20 FBTrace.sysout("Events.dispatch "+name+" without listeners"); 21 22 return; 23 } 24 25 try 26 { 27 if (FBTrace.DBG_DISPATCH) 28 var noMethods = []; 29 30 for (var i = 0; i < listeners.length; ++i) 31 { 32 var listener = listeners[i]; 33 if (!listener) 34 { 35 if (FBTrace.DBG_DISPATCH || FBTrace.DBG_ERRORS) 36 FBTrace.sysout("Events.dispatch ERROR "+i+" "+name+" to null listener."); 37 continue; 38 } 39 40 if (listener[name]) 41 { 42 try 43 { 44 listener[name].apply(listener, args); 45 } 46 catch(exc) 47 { 48 if (FBTrace.DBG_ERRORS) 49 { 50 if (exc.stack) 51 { 52 var stack = exc.stack; 53 exc.stack = stack.split('\n'); 54 } 55 56 var culprit = listeners[i] ? listeners[i].dispatchName : null; 57 FBTrace.sysout("EXCEPTION in Events.dispatch "+(culprit?culprit+".":"")+ 58 name+": "+exc+" in "+(exc.fileName?exc.fileName:"")+ 59 (exc.lineNumber?":"+exc.lineNumber:""), exc); 60 } 61 } 62 } 63 else 64 { 65 if (FBTrace.DBG_DISPATCH) 66 noMethods.push(listener); 67 } 68 } 69 70 if (FBTrace.DBG_DISPATCH) 71 FBTrace.sysout("Events.dispatch "+name+" to "+listeners.length+" listeners, "+ 72 noMethods.length+" had no such method:", noMethods); 73 } 74 catch (exc) 75 { 76 if (FBTrace.DBG_ERRORS) 77 { 78 if (exc.stack) 79 { 80 var stack = exc.stack; 81 exc.stack = stack.split('\n'); 82 } 83 84 var culprit = listeners[i] ? listeners[i].dispatchName : null; 85 FBTrace.sysout("Exception in Events.dispatch "+(culprit?culprit+".":"")+ name+ 86 ": "+exc, exc); 87 } 88 } 89 }; 90 91 Events.dispatch2 = function(listeners, name, args) 92 { 93 try 94 { 95 if (FBTrace.DBG_DISPATCH) 96 var noMethods = []; 97 98 if (!listeners) 99 { 100 if (FBTrace.DBG_DISPATCH) 101 FBTrace.sysout("dispatch2, no listeners for "+name); 102 return; 103 } 104 105 for (var i = 0; i < listeners.length; ++i) 106 { 107 var listener = listeners[i]; 108 if (listener[name]) 109 { 110 var result = listener[name].apply(listener, args); 111 112 if (FBTrace.DBG_DISPATCH) 113 FBTrace.sysout("dispatch2 "+name+" to #"+i+" of "+listeners.length+ 114 " listeners, result "+result, {result: result, listener: listeners[i], 115 fn: listener[name].toSource()}); 116 117 if (result) 118 return result; 119 } 120 else 121 { 122 if (FBTrace.DBG_DISPATCH) 123 noMethods.push(listener); 124 } 125 } 126 127 if (FBTrace.DBG_DISPATCH && noMethods.length == listeners.length) 128 FBTrace.sysout("Events.dispatch2 "+name+" to "+listeners.length+" listeners, "+ 129 noMethods.length+" had no such method:", noMethods); 130 } 131 catch (exc) 132 { 133 if (typeof(FBTrace) != "undefined" && FBTrace.DBG_ERRORS) 134 { 135 if (exc.stack) 136 exc.stack = exc.stack.split('/n'); 137 138 FBTrace.sysout(" Exception in lib.dispatch2 "+ name+" exc:"+exc, exc); 139 } 140 } 141 }; 142 143 // ********************************************************************************************* // 144 // Events 145 146 Events.cancelEvent = function(event) 147 { 148 event.stopPropagation(); 149 event.preventDefault(); 150 }; 151 152 Events.isLeftClick = function(event, allowKeyModifiers) 153 { 154 return event.button == 0 && (allowKeyModifiers || this.noKeyModifiers(event)); 155 }; 156 157 Events.isMiddleClick = function(event, allowKeyModifiers) 158 { 159 return event.button == 1 && (allowKeyModifiers || this.noKeyModifiers(event)); 160 }; 161 162 Events.isRightClick = function(event, allowKeyModifiers) 163 { 164 return event.button == 2 && (allowKeyModifiers || this.noKeyModifiers(event)); 165 }; 166 167 Events.isSingleClick = function(event) 168 { 169 return event instanceof MouseEvent && event.detail == 1; 170 }; 171 172 Events.isDoubleClick = function(event) 173 { 174 return event instanceof MouseEvent && event.detail == 2; 175 }; 176 177 Events.noKeyModifiers = function(event) 178 { 179 return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey; 180 }; 181 182 Events.isControlClick = function(event) 183 { 184 return event.button == 0 && this.isControl(event); 185 }; 186 187 Events.isShiftClick = function(event) 188 { 189 return event.button == 0 && this.isShift(event); 190 }; 191 192 Events.isControl = function(event) 193 { 194 return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey; 195 }; 196 197 Events.isAlt = function(event) 198 { 199 return event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey; 200 }; 201 202 Events.isAltClick = function(event) 203 { 204 return event.button == 0 && this.isAlt(event); 205 }; 206 207 Events.isControlShift = function(event) 208 { 209 return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey; 210 }; 211 212 Events.isControlAlt = function(event) 213 { 214 return (event.metaKey || event.ctrlKey) && !event.shiftKey && event.altKey; 215 }; 216 217 Events.isShift = function(event) 218 { 219 return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey; 220 }; 221 222 // ********************************************************************************************* // 223 // DOM Events 224 225 const eventTypes = 226 { 227 composition: [ 228 "composition", 229 "compositionstart", 230 "compositionend" 231 ], 232 233 contextmenu: [ 234 "contextmenu" 235 ], 236 237 drag: [ 238 "dragenter", 239 "dragover", 240 "dragexit", 241 "dragdrop", 242 "draggesture" 243 ], 244 245 focus: [ 246 "focus", 247 "blur" 248 ], 249 250 form: [ 251 "submit", 252 "reset", 253 "change", 254 "select", 255 "input" 256 ], 257 258 key: [ 259 "keydown", 260 "keyup", 261 "keypress" 262 ], 263 264 load: [ 265 "load", 266 "beforeunload", 267 "unload", 268 "abort", 269 "error" 270 ], 271 272 mouse: [ 273 "mousedown", 274 "mouseup", 275 "click", 276 "dblclick", 277 "mouseover", 278 "mouseout", 279 "mousemove" 280 ], 281 282 mutation: [ 283 "DOMSubtreeModified", 284 "DOMNodeInserted", 285 "DOMNodeRemoved", 286 "DOMNodeRemovedFromDocument", 287 "DOMNodeInsertedIntoDocument", 288 "DOMAttrModified", 289 "DOMCharacterDataModified" 290 ], 291 292 paint: [ 293 "paint", 294 "resize", 295 "scroll" 296 ], 297 298 scroll: [ 299 "overflow", 300 "underflow", 301 "overflowchanged" 302 ], 303 304 text: [ 305 "text" 306 ], 307 308 ui: [ 309 "DOMActivate", 310 "DOMFocusIn", 311 "DOMFocusOut" 312 ], 313 314 xul: [ 315 "popupshowing", 316 "popupshown", 317 "popuphiding", 318 "popuphidden", 319 "close", 320 "command", 321 "broadcast", 322 "commandupdate" 323 ], 324 325 clipboard: [ 326 "cut", 327 "copy", 328 "paste" 329 ], 330 331 touch: [ 332 "touchstart", 333 "touchend", 334 "touchmove", 335 "touchenter", 336 "touchleave", 337 "touchcancel" 338 ] 339 }; 340 341 Events.getEventTypes = function(family) 342 { 343 var types = []; 344 for (var eventFamily in eventTypes) 345 { 346 if (!family || family == eventFamily) 347 { 348 for (type in eventTypes[eventFamily]) 349 types.push(eventTypes[eventFamily][type]); 350 } 351 } 352 353 return types; 354 }; 355 356 Events.isEventFamily = function(eventType) 357 { 358 for (var family in eventTypes) 359 { 360 if (eventType == family) 361 return true; 362 } 363 364 return false; 365 }; 366 367 Events.getEventFamily = function(eventType) 368 { 369 if (!this.families) 370 { 371 this.families = {}; 372 373 for (var family in eventTypes) 374 { 375 var types = eventTypes[family]; 376 for (var i = 0; i < types.length; ++i) 377 this.families[types[i]] = family; 378 } 379 } 380 381 return this.families[eventType]; 382 }; 383 384 Events.attachAllListeners = function(object, listener) 385 { 386 for (var family in eventTypes) 387 { 388 if (family != "mutation" || Firebug.attachMutationEvents) 389 this.attachFamilyListeners(family, object, listener); 390 } 391 }; 392 393 Events.detachAllListeners = function(object, listener) 394 { 395 for (var family in eventTypes) 396 { 397 if (family != "mutation" || Firebug.attachMutationEvents) 398 this.detachFamilyListeners(family, object, listener); 399 } 400 }; 401 402 Events.attachFamilyListeners = function(family, object, listener) 403 { 404 var types = eventTypes[family]; 405 for (var i = 0; i < types.length; ++i) 406 object.addEventListener(types[i], listener, false); 407 }; 408 409 Events.detachFamilyListeners = function(family, object, listener) 410 { 411 var types = eventTypes[family]; 412 for (var i = 0; i < types.length; ++i) 413 object.removeEventListener(types[i], listener, false); 414 }; 415 416 // ********************************************************************************************* // 417 // Event Listeners (+ support for tracking) 418 419 var listeners = []; 420 421 Events.addEventListener = function(parent, eventId, listener, capturing) 422 { 423 if (FBTrace.DBG_EVENTLISTENERS) 424 { 425 for (var i=0; i<listeners.length; i++) 426 { 427 var l = listeners[i]; 428 if (l.parent == parent && l.eventId == eventId && l.listener == listener && 429 l.capturing == capturing) 430 { 431 FBTrace.sysout("Events.addEventListener; ERROR already registered!", l); 432 return; 433 } 434 } 435 } 436 437 parent.addEventListener(eventId, listener, capturing); 438 439 if (FBTrace.DBG_EVENTLISTENERS) 440 { 441 var frames = []; 442 for (var frame = Components.stack; frame; frame = frame.caller) 443 frames.push(frame.filename + " (" + frame.lineNumber + ")"); 444 445 frames.shift(); 446 447 var pid = (typeof(parent.location) != "undefined" ? (parent.location + "") : typeof(parent)); 448 449 listeners.push({ 450 parentId: pid, 451 eventId: eventId, 452 capturing: capturing, 453 listener: listener, 454 stack: frames, 455 parent: parent, 456 }); 457 } 458 } 459 460 Events.removeEventListener = function(parent, eventId, listener, capturing) 461 { 462 try 463 { 464 parent.removeEventListener(eventId, listener, capturing); 465 } 466 catch (e) 467 { 468 if (FBTrace.DBG_ERRORS) 469 FBTrace.sysout("events.removeEventListener; (" + eventId + ") " + e, e); 470 } 471 472 if (FBTrace.DBG_EVENTLISTENERS) 473 { 474 for (var i=0; i<listeners.length; i++) 475 { 476 var l = listeners[i]; 477 if (l.parent == parent && l.eventId == eventId && l.listener == listener && 478 l.capturing == capturing) 479 { 480 listeners.splice(i, 1); 481 return; 482 } 483 } 484 485 var frames = []; 486 for (var frame = Components.stack; frame; frame = frame.caller) 487 frames.push(frame.filename + " (" + frame.lineNumber + ")"); 488 489 frames.shift(); 490 491 var info = { 492 eventId: eventId, 493 capturing: capturing, 494 listener: listener, 495 stack: frames, 496 }; 497 498 // xxxHonza: it's not necessary to pollute the tracing console with this message. 499 //FBTrace.sysout("Events.removeEventListener; ERROR not registered!", info); 500 } 501 } 502 503 if (FBTrace.DBG_EVENTLISTENERS && typeof(Firebug) != "undefined") 504 { 505 Firebug.Events = {}; 506 Firebug.Events.getRegisteredListeners = function() 507 { 508 return listeners; 509 }; 510 } 511 512 // ********************************************************************************************* // 513 514 return Events; 515 516 // ********************************************************************************************* // 517 }); 518