1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 * This file contains the object manager. 27 * 28 */ 29 30 /** 31 * Creates an object manager. 32 * @class 33 * This class is used to high-light objects within a given view. 34 * 35 * @author Kevin Henrikson 36 * 37 * @param {DwtComposite} view the view this manager is going to high-light for 38 * @param {AjxCallback} selectCallback the callback triggered when user clicks on high-light object (provide if you want to do something before the clicked on object opens its corresponding view) 39 * @param {Boolean} skipHandlers <code>true</code> to avoid adding the standard handlers 40 */ 41 ZmObjectManager = function(view, selectCallback, skipHandlers) { 42 43 this._selectCallback = selectCallback; 44 this._uuid = Dwt.getNextId(); 45 this._objectIdPrefix = "OBJ_PREFIX_"; 46 this._objectHandlers = {}; 47 48 // don't include when looking for objects. only used to provide tool tips for images 49 if (appCtxt.get(ZmSetting.MAIL_ENABLED) && window["ZmImageAttachmentObjectHandler"]) { 50 this._imageAttachmentHandler = new ZmImageAttachmentObjectHandler(); 51 } 52 53 // create handlers (see registerHandler below) 54 if (!skipHandlers) { 55 this.initialized = false; 56 this._addAutoHandlers(); 57 } else { 58 this.initialized = true; 59 } 60 61 this.sortHandlers(); 62 this.reset(); 63 this.setView(view); 64 }; 65 66 ZmObjectManager._TOOLTIP_DELAY = 275; 67 68 // Define common types for quicker object matching. 69 ZmObjectManager.EMAIL = "email"; 70 ZmObjectManager.URL = "url"; 71 ZmObjectManager.PHONE = "phone"; 72 ZmObjectManager.DATE = "date"; 73 ZmObjectManager.ADDRESS = "address"; 74 75 // Allows callers to pass in a current date 76 ZmObjectManager.ATTR_CURRENT_DATE = "currentDate"; 77 78 ZmObjectManager._autohandlers = []; 79 80 /** 81 * Registers the handler. 82 * 83 * @param {Object} obj the object 84 * @param {constant} type the type 85 * @param {constant} priority the priority 86 */ 87 ZmObjectManager.registerHandler = 88 function(obj, type, priority) { 89 if (typeof obj == "string") { 90 obj = eval(obj); 91 } 92 var c = ZmObjectManager._autohandlers; 93 if (!obj.__registered) { 94 var id = c.push(obj); 95 var i = id - 1; 96 if(type) { 97 c[i].useType = type; 98 } 99 if(priority) { 100 c[i].usePrio = priority; 101 } 102 obj.__registered = true; 103 } 104 }; 105 106 /** 107 * @private 108 */ 109 ZmObjectManager.unregisterHandler = 110 function(obj) { 111 if (typeof obj == "string") { 112 obj = eval(obj); 113 } 114 var c = ZmObjectManager._autohandlers, i; 115 for (i = c.length; --i >= 0;) { 116 if (c[i] === obj) { 117 c.splice(i, 1); 118 break; 119 } 120 } 121 }; 122 123 /** 124 * Returns a string representation of the object. 125 * 126 * @return {String} a string representation of the object 127 */ 128 ZmObjectManager.prototype.toString = 129 function() { 130 return "ZmObjectManager"; 131 }; 132 133 /** 134 * Gets the handlers. 135 * 136 * @return {Array} an array of {@link ZmObjectHandler} objects 137 */ 138 ZmObjectManager.prototype.getHandlers = 139 function() { 140 if (!this.initialized) { 141 var zimletMgr = appCtxt.getZimletMgr(); 142 if (zimletMgr.isLoaded()) { 143 this.initialized = true; 144 var zimlets = zimletMgr.getContentZimlets(); 145 for (var i = 0; i < zimlets.length; i++) { 146 this.addHandler(zimlets[i], zimlets[i].type, zimlets[i].prio); 147 } 148 } 149 } 150 return this._objectHandlers; 151 }; 152 153 /** 154 * Adds the handler. 155 * 156 * @param {ZmObjectHandler} h the handler 157 * @param {constant} type the type 158 * @param {constant} priority the priority 159 */ 160 ZmObjectManager.prototype.addHandler = 161 function(h, type, priority) { 162 type = type || (h.getTypeName() ? h.getTypeName() : "none"); 163 priority = priority ? priority : -1; 164 h._prio = priority; 165 //DBG.println(AjxDebug.DBG3, "addHandler " + h + " type: " + type + " prio: " + priority); 166 var oh = this.getHandlers(); 167 if (!oh[type]) {oh[type] = [];} 168 oh[type].push(h); 169 }; 170 171 /** 172 * Removes the handler. 173 * 174 * @param {ZmObjectHandler} h the handler 175 * @param {constant} type the type 176 */ 177 ZmObjectManager.prototype.removeHandler = 178 function(h, type) { 179 type = type || (h.getTypeName() ? h.getTypeName() : "none"); 180 var oh = this.getHandlers(); 181 if (oh[type]) { 182 for (var i = 0, count = oh[type].length; i < count; i++) { 183 if (oh[type][i] == h) { 184 oh[type].splice(i, 1); 185 break; 186 } 187 } 188 } 189 }; 190 191 /** 192 * Sorts the handlers. 193 * 194 */ 195 ZmObjectManager.prototype.sortHandlers = 196 function() { 197 this._allObjectHandlers = []; 198 var objectHandlers = this.getHandlers(); 199 for (var type in objectHandlers) { 200 // Object handlers grouped by Type 201 objectHandlers[type].sort(ZmObjectManager.__byPriority); 202 203 // Copy each array to a single array of all Object Handlers 204 for (var k = 0; k < objectHandlers[type].length; k++) { 205 this._allObjectHandlers.push(objectHandlers[type][k]); 206 } 207 } 208 this._allObjectHandlers.sort(ZmObjectManager.__byPriority); 209 }; 210 211 /** 212 * @private 213 */ 214 ZmObjectManager.prototype._addAutoHandlers = 215 function() { 216 var c = ZmObjectManager._autohandlers, i, obj, prio; 217 for (i = 0; i < c.length; ++i) { 218 obj = c[i]; 219 var handler = obj; 220 var type = obj.TYPE; 221 if (!(obj.isZmObjectHandler)) { 222 handler = new obj(); 223 } 224 if (obj.useType) { 225 type = obj.useType; 226 } 227 if (obj.usePrio) { 228 prio = obj.usePrio; 229 } 230 this.addHandler(handler, type, prio); 231 } 232 }; 233 234 /** 235 * Resets the objects. 236 * 237 */ 238 ZmObjectManager.prototype.reset = 239 function() { 240 this._objects = {}; 241 }; 242 243 /** 244 * Sets the view. 245 * 246 * @param {DwtComposite} view the view 247 */ 248 ZmObjectManager.prototype.setView = 249 function(view) { 250 if (view != null && appCtxt.getZimletMgr().isLoaded()) { 251 view.addListener(DwtEvent.ONMOUSEOVER, new AjxListener(this, this._mouseOverListener)); 252 view.addListener(DwtEvent.ONMOUSEOUT, new AjxListener(this, this._mouseOutListener)); 253 view.addListener(DwtEvent.ONMOUSEDOWN, new AjxListener(this, this._mouseDownListener)); 254 view.addListener(DwtEvent.ONMOUSEUP, new AjxListener(this, this._mouseUpListener)); 255 view.addListener(DwtEvent.ONMOUSEMOVE, new AjxListener(this, this._mouseMoveListener)); 256 view.addListener(DwtEvent.ONCONTEXTMENU, new AjxListener(this, this._rightClickListener)); 257 this._hoverOverListener = new AjxListener(this, this._handleHoverOver); 258 this._hoverOutListener = new AjxListener(this, this._handleHoverOut); 259 } 260 this._view = view; 261 }; 262 263 ZmObjectManager.prototype.getView = function() { 264 return this._view; 265 }; 266 267 /** 268 * Gets the count of objects. 269 * 270 * @return {int} the count 271 */ 272 ZmObjectManager.prototype.objectsCount = 273 function() { 274 return (appCtxt.zimletsPresent()) ? appCtxt.getZimletMgr().getContentZimlets().length : 0; 275 }; 276 277 /** 278 * Gets the image attachment handler. 279 * 280 * @return {ZmImageAttachmentObjectHandler} the handler 281 */ 282 ZmObjectManager.prototype.getImageAttachmentHandler = 283 function() { 284 return this._imageAttachmentHandler; 285 }; 286 287 /** 288 * @private 289 */ 290 ZmObjectManager.prototype._getAjxEmailAddress = 291 function(obj){ 292 if(appCtxt.isChildWindow && obj.isAjxEmailAddress){ //Making sure child window knows its type AjxEmailAddress 293 obj = AjxEmailAddress.copy(obj); 294 } 295 return obj; 296 }; 297 298 /** 299 * Finds objects. 300 * 301 * @param {String} content the content 302 * @param {Boolean} htmlEncode <code>true</code> to HTML encode the content 303 * @param {constant} type the type 304 * @param {Boolean} isTextMsg <code>true</code> if is text msg 305 * @param {hash} options arbitrary options to pass to handler 306 * 307 * @return {String} the object 308 */ 309 ZmObjectManager.prototype.findObjects = 310 function(content, htmlEncode, type, isTextMsg, options) { 311 if (!content) {return "";} 312 var html = []; 313 var idx = 0; 314 315 var maxIndex = content.length; 316 var lastIndex = 0; 317 318 var objectHandlers = this.getHandlers(); 319 while (true) { 320 var lowestResult = null; 321 var lowestIndex = maxIndex; 322 var lowestHandler = null; 323 324 // if given a type, just go thru the handler defined for that type. 325 // otherwise, go thru every handler we have. Regardless, ask each handler 326 // to find us a match >= to lastIndex. Handlers that didn't match last 327 // time will simply return, handlers that matched last time that we didn't 328 // use (because we found a closer match) will simply return that match again. 329 // 330 // when we are done, we take the handler with the lowest index. 331 var i; 332 var handlers; 333 var chunk; 334 var result = null; 335 if (type) { 336 //DBG.println(AjxDebug.DBG3, "findObjects type [" + type + "]"); 337 handlers = objectHandlers[type]; 338 if (handlers) { 339 for (i = 0; i < handlers.length; i++) { 340 //DBG.println(AjxDebug.DBG3, "findObjects by TYPE (" + handlers[i] + ")"); 341 result = handlers[i].findObject(content, lastIndex, this); 342 // No match keep trying. 343 if(!result) {continue;} 344 // Got a match let's handle it. 345 if (result.index >= lowestIndex) {break;} 346 lowestResult = result; 347 lowestIndex = result.index; 348 lowestHandler = handlers[i]; 349 } 350 } 351 // If it's an email address just handle it and return the result. 352 if (type == "email" || content instanceof AjxEmailAddress) { 353 if (lowestHandler) { 354 content = this._getAjxEmailAddress(content); 355 this.generateSpan(lowestHandler, html, idx, content, lowestResult, options); 356 } else { 357 html[idx++] = AjxStringUtil.htmlEncode(content.toString()); 358 } 359 return html.join(""); 360 } 361 } else { 362 for (var j = 0; j < this._allObjectHandlers.length; j++) { 363 var handler = this._allObjectHandlers[j]; 364 //DBG.println(AjxDebug.DBG3, "findObjects trying (" + handler + ")"); 365 result = handler.findObject(content, lastIndex, this); 366 if (result && result.index < lowestIndex) { 367 lowestResult = result; 368 lowestIndex = result.index; 369 lowestHandler = handler; 370 } 371 } 372 } 373 374 if (!lowestResult) { 375 // all done 376 // do last chunk 377 chunk = content.substring(lastIndex, maxIndex); 378 if (htmlEncode) { 379 html[idx++] = AjxStringUtil.htmlEncode(chunk, !!isTextMsg); 380 } else { 381 html[idx++] = chunk; 382 } 383 break; 384 } 385 386 // add anything before the match 387 if (lowestIndex > lastIndex) { 388 chunk = content.substring(lastIndex, lowestIndex); 389 if (htmlEncode) { 390 html[idx++] = AjxStringUtil.htmlEncode(chunk, !!isTextMsg); 391 } else { 392 html[idx++] = chunk; 393 } 394 } 395 396 // add the match 397 if(lowestHandler) { 398 idx = this.generateSpan(lowestHandler, html, idx, lowestResult[0], lowestResult.context); 399 } else { 400 html[idx++] = lowestResult[0]; 401 } 402 403 // update the index 404 lastIndex = lowestResult.index + (lowestResult.matchLength || lowestResult[0].length); 405 } 406 407 return html.join(""); 408 }; 409 410 411 /** 412 * Added this customized method for the sake of ZmMailMsgView performance. 413 * 414 * TODO: Integrate this method to findObjectsInNode() 415 * 416 * @private 417 */ 418 ZmObjectManager.prototype.processObjectsInNode = function(doc, node){ 419 420 var objectManager = this; 421 doc = doc || node.ownerDocument; 422 var tmpdiv = doc.createElement("div"); 423 424 var recurse = function(node, handlers) { 425 var tmp, i, val, next; 426 switch (node.nodeType) { 427 case 1: // ELEMENT_NODE 428 node.normalize(); 429 tmp = node.tagName.toLowerCase(); 430 431 if (next == null) { 432 if (/^(img|a)$/.test(tmp)) { 433 var href; 434 try { 435 // IE can throw an "Invalid Argument" error depending on value of href 436 // e.g: http://0:0:0:0:0:0:0:1%0:7070/service/soap/ContactActionRequest:1331608015326:9c4f5868c5b0b4f2 437 href = node.href; 438 } 439 catch(e) { 440 //do nothing 441 } 442 443 var isMailToLink = tmp === "a" && ZmMailMsgView._MAILTO_RE.test(href), 444 isUrlLink = tmp === "a" && ZmMailMsgView._URL_RE.test(href); 445 446 if ((isMailToLink || isUrlLink) && node.target){ 447 // tricky. 448 var txt = isMailToLink ? href :RegExp.$1 ; 449 tmp = doc.createElement("div"); 450 tmp.innerHTML = objectManager.findObjects(AjxStringUtil.trim(txt)); 451 tmp = tmp.firstChild; 452 if (tmp.nodeType == 3 /* Node.TEXT_NODE */) { 453 // probably no objects were found. A warning would be OK here 454 // since the regexps guarantee that objects _should_ be found. 455 return tmp.nextSibling; 456 } 457 // here, tmp is an object span, but it 458 // contains the URL (href) instead of 459 // the original link text. 460 node.parentNode.insertBefore(tmp, node); // add it to DOM 461 tmp.innerHTML = ""; 462 tmp.appendChild(node); // we have the original link now 463 return tmp.nextSibling; // move on 464 } 465 handlers = false; 466 } 467 } else { 468 // consider processed 469 node = next; 470 } 471 472 // bug 28264: the only workaround possible seems to be 473 // to remove textIndent styles that have a negative value: 474 if (parseFloat(node.style.textIndent) < 0) { 475 node.style.textIndent = ""; 476 } 477 for (i = node.firstChild; i; i = recurse(i, handlers)) {} 478 return node.nextSibling; 479 480 case 3: // TEXT_NODE 481 case 4: // CDATA_SECTION_NODE (just in case) 482 // generate ObjectHandler-s 483 if (handlers && /[^\s\xA0]/.test(node.data)) try { 484 var a = null, b = null; 485 486 if (!AjxEnv.isIE) { 487 // this block of code is supposed to free the object handlers from 488 // dealing with whitespace. However, IE sometimes crashes here, for 489 // reasons that weren't possible to determine--hence we avoid this 490 // step for IE. (bug #5345) 491 var results = /^[\s\xA0]+/.exec(node.data); 492 if (results) { 493 a = node; 494 node = node.splitText(results[0].length); 495 } 496 results = /[\s\xA0]+$/.exec(node.data); 497 if (results) 498 b = node.splitText(node.data.length - results[0].length); 499 } 500 501 tmp = tmpdiv; 502 var code = objectManager.findObjects(node.data, true, null, false); 503 var disembowel = false; 504 if (AjxEnv.isIE) { 505 // Bug #6481, #4498: innerHTML in IE massacrates whitespace 506 // unless it sees a <pre> in the code. 507 tmp.innerHTML = [ "<pre>", code, "</pre>" ].join(""); 508 disembowel = true; 509 } else { 510 tmp.innerHTML = code; 511 } 512 513 if (a) 514 tmp.insertBefore(a, tmp.firstChild); 515 if (b) 516 tmp.appendChild(b); 517 518 a = node.parentNode; 519 if (disembowel) 520 tmp = tmp.firstChild; 521 while (tmp.firstChild) 522 a.insertBefore(tmp.firstChild, node); 523 tmp = node.nextSibling; 524 a.removeChild(node); 525 return tmp; 526 } catch(ex) {}; 527 } 528 return node.nextSibling; 529 }; 530 531 // Parse through the DOM directly and find objects. 532 if (node && node.childNodes && node.childNodes.length) { 533 for (var i = 0; i < node.childNodes.length; i++){ 534 recurse(node.childNodes[i], true); 535 } 536 } 537 }; 538 539 /** 540 * @private 541 */ 542 ZmObjectManager.prototype.findObjectsInNode = 543 function(node, re_discard, re_allow, callbacks) { 544 var objectManager = this, doc = node.ownerDocument, tmpdiv = doc.createElement("div"); 545 546 if (!re_discard) 547 re_discard = /^(script|link|object|iframe|applet)$/i; 548 549 // This inner function does the actual work. BEWARE that it return-s 550 // in various places, not only at the end. 551 var recurse = function(node, handlers) { 552 var tmp, i, val, next; 553 switch (node.nodeType) { 554 case 1: // ELEMENT_NODE 555 node.normalize(); 556 tmp = node.tagName.toLowerCase(); 557 if (callbacks && callbacks.foreachElement) { 558 next = callbacks.foreachElement(node, tmp, re_discard, re_allow); 559 } 560 if (next == null) { 561 if (/^(img|a)$/.test(tmp)) { 562 if (tmp == "a" && node.target 563 && (ZmMailMsgView._URL_RE.test(node.href) 564 || ZmMailMsgView._MAILTO_RE.test(node.href))) 565 { 566 // tricky. 567 var txt = RegExp.$1; 568 tmp = doc.createElement("div"); 569 tmp.innerHTML = objectManager.findObjects(AjxStringUtil.trim(RegExp.$1)); 570 tmp = tmp.firstChild; 571 if (tmp.nodeType == 3 /* Node.TEXT_NODE */) { 572 // probably no objects were found. A warning would be OK here 573 // since the regexps guarantee that objects _should_ be found. 574 return tmp.nextSibling; 575 } 576 // here, tmp is an object span, but it 577 // contains the URL (href) instead of 578 // the original link text. 579 node.parentNode.insertBefore(tmp, node); // add it to DOM 580 tmp.innerHTML = ""; 581 tmp.appendChild(node); // we have the original link now 582 return tmp.nextSibling; // move on 583 } 584 handlers = false; 585 } else if (re_discard.test(tmp) || (re_allow && !re_allow.test(tmp))) { 586 tmp = node.nextSibling; 587 node.parentNode.removeChild(node); 588 return tmp; 589 } 590 } else { 591 // consider processed 592 node = next; 593 } 594 595 if (AjxEnv.isIE) { 596 // strips expression()-s, bwuahahaha! 597 // granted, they get lost on the server-side anyway, but assuming some get through... 598 // the line below exterminates them. 599 node.style.cssText = node.style.cssText; 600 } 601 602 for (i = node.firstChild; i; i = recurse(i, handlers)) {} 603 return node.nextSibling; 604 605 case 3: // TEXT_NODE 606 case 4: // CDATA_SECTION_NODE (just in case) 607 // generate ObjectHandler-s 608 if (handlers && /[^\s\xA0]/.test(node.data)) try { 609 var a = null, b = null; 610 611 if (!AjxEnv.isIE) { 612 // this block of code is supposed to free the object handlers from 613 // dealing with whitespace. However, IE sometimes crashes here, for 614 // reasons that weren't possible to determine--hence we avoid this 615 // step for IE. (bug #5345) 616 var results = /^[\s\xA0]+/.exec(node.data); 617 if (results) { 618 a = node; 619 node = node.splitText(results[0].length); 620 } 621 results = /[\s\xA0]+$/.exec(node.data); 622 if (results) 623 b = node.splitText(node.data.length - results[0].length); 624 } 625 626 tmp = tmpdiv; 627 var code = objectManager.findObjects(node.data, true, null, false); 628 var disembowel = false; 629 if (AjxEnv.isIE) { 630 // Bug #6481, #4498: innerHTML in IE massacrates whitespace 631 // unless it sees a <pre> in the code. 632 tmp.innerHTML = [ "<pre>", code, "</pre>" ].join(""); 633 disembowel = true; 634 } else { 635 tmp.innerHTML = code; 636 } 637 638 if (a) 639 tmp.insertBefore(a, tmp.firstChild); 640 if (b) 641 tmp.appendChild(b); 642 643 a = node.parentNode; 644 if (disembowel) 645 tmp = tmp.firstChild; 646 while (tmp.firstChild) 647 a.insertBefore(tmp.firstChild, node); 648 tmp = node.nextSibling; 649 a.removeChild(node); 650 return tmp; 651 } catch(ex) {}; 652 } 653 return node.nextSibling; 654 }; 655 var df = doc.createDocumentFragment(); 656 while (node.firstChild) { 657 df.appendChild(node.firstChild); // NODE now out of the displayable DOM 658 recurse(df.lastChild, true, this); // parse tree and findObjects() 659 } 660 node.appendChild(df); // put nodes back in the document 661 }; 662 663 /** 664 * Sets handler attribute. 665 * 666 * @param {String} type the type 667 * @param {String} name the attribute name 668 * @param {Object} value the value 669 */ 670 ZmObjectManager.prototype.setHandlerAttr = 671 function(type, name, value) { 672 var handlers = this.getHandlers()[type]; 673 if (handlers) { 674 for (var i = 0; i < handlers.length; i++) { 675 handlers[i][name] = value; 676 } 677 } 678 }; 679 680 /** 681 * Generates the span. 682 * 683 * @private 684 */ 685 ZmObjectManager.prototype.generateSpan = 686 function(handler, html, idx, obj, context, options) { 687 var id = this._objectIdPrefix + Dwt.getNextId(); 688 if (handler && handler.name) { 689 id = id + "_" + handler.name; 690 } 691 this._objects[id] = {object: obj, handler: handler, id: id, context: context }; 692 return handler.generateSpan(html, idx, obj, id, context, options); 693 }; 694 695 /** 696 * @private 697 */ 698 ZmObjectManager.prototype._findObjectSpan = 699 function(e) { 700 while (e && (!e.id || e.id.indexOf(this._objectIdPrefix) !== 0)) { 701 e = e.parentNode; 702 } 703 return e; 704 }; 705 706 /** 707 * @private 708 */ 709 ZmObjectManager.prototype._mouseOverListener = 710 function(ev) { 711 var span = this._findObjectSpan(ev.target); 712 if (!span) {return false;} 713 var object = this._objects[span.id]; 714 if (!object) {return false;} 715 716 span.className = object.handler.getHoveredClassName(object.object, object.context, span.id); 717 if (object.handler.hasToolTipText()) { 718 var shell = DwtShell.getShell(window); 719 var manager = shell.getHoverMgr(); 720 if ((!manager.isHovering() || manager.getHoverObject() != object) && !DwtMenu.menuShowing()) { 721 manager.reset(); 722 manager.setHoverOverDelay(ZmObjectManager._TOOLTIP_DELAY); 723 manager.setHoverObject(object); 724 manager.setHoverOverData(object); 725 manager.setHoverOverListener(this._hoverOverListener); 726 manager.hoverOver(ev.docX, ev.docY); 727 ev.hoverStarted = true; 728 } 729 } 730 731 ev._returnValue = true; 732 ev._dontCallPreventDefault = true; 733 return false; 734 }; 735 736 /** 737 * @private 738 */ 739 ZmObjectManager.prototype._mouseOutListener = 740 function(ev) { 741 var span = this._findObjectSpan(ev.target); 742 var object = span ? this._objects[span.id] : null; 743 744 if (object) { 745 span.className = object.handler.getClassName(object.object, object.context, span.id); 746 var shell = DwtShell.getShell(window); 747 var manager = shell.getHoverMgr(); 748 manager.setHoverOutDelay(150); 749 manager.setHoverOutData(object); 750 manager.setHoverOutListener(this._hoverOutListener); 751 manager.hoverOut(); 752 } 753 754 return false; 755 }; 756 757 /** 758 * @private 759 */ 760 ZmObjectManager.prototype._mouseMoveListener = 761 function(ev) { 762 ev._returnValue = true; 763 ev._dontCallPreventDefault = true; 764 ev._stopPropagation = true; 765 var span = this._findObjectSpan(ev.target); 766 var object = span ? this._objects[span.id] : null; 767 768 if (object) { 769 var shell = DwtShell.getShell(window); 770 var manager = shell.getHoverMgr(); 771 if (!manager.isHovering()) { 772 // NOTE: mouseOver already init'd hover settings 773 manager.hoverOver(ev.docX, ev.docY); 774 } 775 } 776 777 return false; 778 }; 779 780 /** 781 * @private 782 */ 783 ZmObjectManager.prototype._rightClickListener = 784 function(ev) { 785 ev.button = DwtMouseEvent.RIGHT; 786 return this._mouseDownListener(ev); 787 }; 788 789 /** 790 * @private 791 */ 792 ZmObjectManager.prototype._mouseDownListener = 793 function(ev) { 794 795 // "authoritative" means a previous listener doesn't want propagation to get reset 796 if (!ev._authoritative) { 797 ev._dontCallPreventDefault = true; 798 ev._returnValue = true; 799 ev._stopPropagation = false; 800 } 801 802 var span = this._findObjectSpan(ev.target); 803 if (!span) { 804 return true; 805 } 806 var object = this._objects[span.id]; 807 if (!object) { 808 return true; 809 } 810 811 ev._stopPropagation = true; 812 813 var shell = DwtShell.getShell(window); 814 var manager = shell.getHoverMgr(); 815 manager.setHoverOutDelay(0); 816 manager.setHoverOutData(object); 817 manager.setHoverOutListener(this._hoverOutListener); 818 manager.hoverOut(); 819 820 span.className = object.handler.getActiveClassName(object.object, object.context, span.id); 821 if (ev.button == DwtMouseEvent.RIGHT) { 822 var menu = object.handler.getActionMenu(object.object, span, object.context, ev); 823 if (menu) { 824 menu.popup(0, ev.docX, ev.docY); 825 // if we have an action menu, don't let the browser show its context menu too 826 ev._dontCallPreventDefault = false; 827 ev._returnValue = false; 828 ev._stopPropagation = true; 829 return true; 830 } 831 } else if (ev.button == DwtMouseEvent.LEFT) { 832 if (this._selectCallback) { 833 this._selectCallback.run(); 834 } 835 object.handler.selected(object.object, span, ev, object.context); 836 return true; 837 } 838 return false; 839 }; 840 841 /** 842 * @private 843 */ 844 ZmObjectManager.prototype._mouseUpListener = 845 function(ev) { 846 ev._returnValue = true; 847 ev._dontCallPreventDefault = true; 848 ev._stopPropagation = true; 849 var span = this._findObjectSpan(ev.target); 850 if (!span) {return false;} 851 var object = this._objects[span.id]; 852 if (!object) {return false;} 853 854 span.className = object.handler.getHoveredClassName(object.object, object.context, span.id); 855 return false; 856 }; 857 858 /** 859 * @private 860 */ 861 ZmObjectManager.prototype._handleHoverOver = 862 function(event) { 863 if (!(event && event.object)) { return; } 864 865 var span = this._findObjectSpan(event.target); 866 var handler = event.object.handler; 867 var object = event.object.object; 868 var context = event.object.context; 869 var id = event.object.id; 870 var x = event.x; 871 var y = event.y; 872 873 handler.hoverOver(object, context, x, y, span, id); 874 }; 875 876 /** 877 * @private 878 */ 879 ZmObjectManager.prototype._handleHoverOut = 880 function(event) { 881 if (!(event && event.object)) { return; } 882 883 var span = this._findObjectSpan(event.target); 884 var handler = event.object.handler; 885 var object = event.object.object; 886 var context = event.object.context; 887 var id = event.object.id; 888 889 handler.hoverOut(object, context, span, id); 890 }; 891 892 // Private static functions 893 894 /** 895 * @private 896 */ 897 ZmObjectManager.__byPriority = 898 function(a, b) { 899 return (b._prio < a._prio) - (a._prio < b._prio); 900 }; 901