browser/metro/base/content/helperui/MenuUI.js

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 // Positioning buffer enforced between the edge of a context menu
michael@0 6 // and the edge of the screen.
michael@0 7 const kPositionPadding = 10;
michael@0 8
michael@0 9 var AutofillMenuUI = {
michael@0 10 _popupState: null,
michael@0 11 __menuPopup: null,
michael@0 12
michael@0 13 get _panel() { return document.getElementById("autofill-container"); },
michael@0 14 get _popup() { return document.getElementById("autofill-popup"); },
michael@0 15 get commands() { return this._popup.childNodes[0]; },
michael@0 16
michael@0 17 get _menuPopup() {
michael@0 18 if (!this.__menuPopup) {
michael@0 19 this.__menuPopup = new MenuPopup(this._panel, this._popup);
michael@0 20 this.__menuPopup._wantTypeBehind = true;
michael@0 21 this.__menuPopup.controller = this;
michael@0 22 }
michael@0 23 return this.__menuPopup;
michael@0 24 },
michael@0 25
michael@0 26 _firePopupEvent: function _firePopupEvent(aEventName) {
michael@0 27 let menupopup = this._currentControl.menupopup;
michael@0 28 if (menupopup.hasAttribute(aEventName)) {
michael@0 29 let func = new Function("event", menupopup.getAttribute(aEventName));
michael@0 30 func.call(this);
michael@0 31 }
michael@0 32 },
michael@0 33
michael@0 34 _emptyCommands: function _emptyCommands() {
michael@0 35 while (this.commands.firstChild)
michael@0 36 this.commands.removeChild(this.commands.firstChild);
michael@0 37 },
michael@0 38
michael@0 39 _positionOptions: function _positionOptions() {
michael@0 40 return {
michael@0 41 bottomAligned: false,
michael@0 42 leftAligned: true,
michael@0 43 xPos: this._anchorRect.x,
michael@0 44 yPos: this._anchorRect.y + this._anchorRect.height,
michael@0 45 maxWidth: this._anchorRect.width,
michael@0 46 maxHeight: 350,
michael@0 47 source: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
michael@0 48 };
michael@0 49 },
michael@0 50
michael@0 51 show: function show(aAnchorRect, aSuggestionsList) {
michael@0 52 this.commands.addEventListener("select", this, true);
michael@0 53
michael@0 54 this._anchorRect = aAnchorRect;
michael@0 55 this._emptyCommands();
michael@0 56 for (let idx = 0; idx < aSuggestionsList.length; idx++) {
michael@0 57 let item = document.createElement("richlistitem");
michael@0 58 let label = document.createElement("label");
michael@0 59 label.setAttribute("value", aSuggestionsList[idx].label);
michael@0 60 item.setAttribute("value", aSuggestionsList[idx].value);
michael@0 61 item.setAttribute("data", aSuggestionsList[idx].value);
michael@0 62 item.appendChild(label);
michael@0 63 this.commands.appendChild(item);
michael@0 64 }
michael@0 65 this._menuPopup.show(this._positionOptions());
michael@0 66 },
michael@0 67
michael@0 68 selectByIndex: function mn_selectByIndex(aIndex) {
michael@0 69 this._menuPopup.hide();
michael@0 70 FormHelperUI.doAutoComplete(this.commands.childNodes[aIndex].getAttribute("data"));
michael@0 71 },
michael@0 72
michael@0 73 hide: function hide () {
michael@0 74 this.commands.removeEventListener("select", this, true);
michael@0 75
michael@0 76 this._menuPopup.hide();
michael@0 77 },
michael@0 78
michael@0 79 handleEvent: function (aEvent) {
michael@0 80 switch (aEvent.type) {
michael@0 81 case "select":
michael@0 82 FormHelperUI.doAutoComplete(this.commands.value);
michael@0 83 break;
michael@0 84 }
michael@0 85 }
michael@0 86 };
michael@0 87
michael@0 88 var ContextMenuUI = {
michael@0 89 _popupState: null,
michael@0 90 __menuPopup: null,
michael@0 91 _defaultPositionOptions: {
michael@0 92 bottomAligned: true,
michael@0 93 rightAligned: false,
michael@0 94 centerHorizontally: true,
michael@0 95 moveBelowToFit: true
michael@0 96 },
michael@0 97
michael@0 98 get _panel() { return document.getElementById("context-container"); },
michael@0 99 get _popup() { return document.getElementById("context-popup"); },
michael@0 100 get commands() { return this._popup.childNodes[0]; },
michael@0 101
michael@0 102 get _menuPopup() {
michael@0 103 if (!this.__menuPopup) {
michael@0 104 this.__menuPopup = new MenuPopup(this._panel, this._popup);
michael@0 105 this.__menuPopup.controller = this;
michael@0 106 }
michael@0 107 return this.__menuPopup;
michael@0 108 },
michael@0 109
michael@0 110 /*******************************************
michael@0 111 * External api
michael@0 112 */
michael@0 113
michael@0 114 /*
michael@0 115 * popupState - return the json object for this context. Called
michael@0 116 * by context command to invoke actions on the target.
michael@0 117 */
michael@0 118 get popupState() {
michael@0 119 return this._popupState;
michael@0 120 },
michael@0 121
michael@0 122 /*
michael@0 123 * showContextMenu - display a context sensitive menu based
michael@0 124 * on the data provided in a json data structure.
michael@0 125 *
michael@0 126 * @param aMessage data structure containing information about
michael@0 127 * the context.
michael@0 128 * aMessage.json - json data structure described below.
michael@0 129 * aMessage.target - target element on which to evoke
michael@0 130 *
michael@0 131 * @returns true if the context menu was displayed,
michael@0 132 * false otherwise.
michael@0 133 *
michael@0 134 * json: TBD
michael@0 135 */
michael@0 136 showContextMenu: function ch_showContextMenu(aMessage) {
michael@0 137 this._popupState = aMessage.json;
michael@0 138 this._popupState.target = aMessage.target;
michael@0 139 let contentTypes = this._popupState.types;
michael@0 140
michael@0 141 /*
michael@0 142 * Types in ContextMenuHandler:
michael@0 143 * image
michael@0 144 * link
michael@0 145 * input-text - generic form input control
michael@0 146 * copy - form input that has some selected text
michael@0 147 * selectable - form input with text that can be selected
michael@0 148 * input-empty - form input (empty)
michael@0 149 * paste - form input and there's text on the clipboard
michael@0 150 * selected-text - generic content text that is selected
michael@0 151 * content-text - generic content text
michael@0 152 * video
michael@0 153 * media-paused, media-playing
michael@0 154 * paste-url - url bar w/text on the clipboard
michael@0 155 */
michael@0 156
michael@0 157 Util.dumpLn("contentTypes:", contentTypes);
michael@0 158
michael@0 159 // Defines whether or not low priority items in images, text, and
michael@0 160 // links are displayed.
michael@0 161 let multipleMediaTypes = false;
michael@0 162 if (contentTypes.indexOf("link") != -1 &&
michael@0 163 (contentTypes.indexOf("image") != -1 ||
michael@0 164 contentTypes.indexOf("video") != -1 ||
michael@0 165 contentTypes.indexOf("selected-text") != -1))
michael@0 166 multipleMediaTypes = true;
michael@0 167
michael@0 168 for (let command of Array.slice(this.commands.childNodes)) {
michael@0 169 command.hidden = true;
michael@0 170 command.selected = false;
michael@0 171 }
michael@0 172
michael@0 173 let optionsAvailable = false;
michael@0 174 for (let command of Array.slice(this.commands.childNodes)) {
michael@0 175 let types = command.getAttribute("type").split(",");
michael@0 176 let lowPriority = (command.hasAttribute("priority") &&
michael@0 177 command.getAttribute("priority") == "low");
michael@0 178 let searchTextItem = (command.id == "context-search");
michael@0 179
michael@0 180 // filter low priority items if we have more than one media type.
michael@0 181 if (multipleMediaTypes && lowPriority)
michael@0 182 continue;
michael@0 183
michael@0 184 for (let i = 0; i < types.length; i++) {
michael@0 185 // If one of the item's types has '!' before it, treat it as an exclusion rule.
michael@0 186 if (types[i].charAt(0) == '!' && contentTypes.indexOf(types[i].substring(1)) != -1) {
michael@0 187 break;
michael@0 188 }
michael@0 189 if (contentTypes.indexOf(types[i]) != -1) {
michael@0 190 // If this is the special search text item, we need to set its label dynamically.
michael@0 191 if (searchTextItem && !ContextCommands.searchTextSetup(command, this._popupState.string)) {
michael@0 192 break;
michael@0 193 }
michael@0 194 optionsAvailable = true;
michael@0 195 command.hidden = false;
michael@0 196 break;
michael@0 197 }
michael@0 198 }
michael@0 199 }
michael@0 200
michael@0 201 if (!optionsAvailable) {
michael@0 202 this._popupState = null;
michael@0 203 return false;
michael@0 204 }
michael@0 205
michael@0 206 let coords = { x: aMessage.json.xPos, y: aMessage.json.yPos };
michael@0 207
michael@0 208 // chrome calls don't need to be translated and as such
michael@0 209 // don't provide target.
michael@0 210 if (aMessage.target && aMessage.target.localName === "browser") {
michael@0 211 coords = aMessage.target.msgBrowserToClient(aMessage, true);
michael@0 212 }
michael@0 213 this._menuPopup.show(Util.extend({}, this._defaultPositionOptions, {
michael@0 214 xPos: coords.x,
michael@0 215 yPos: coords.y,
michael@0 216 source: aMessage.json.source
michael@0 217 }));
michael@0 218 return true;
michael@0 219 },
michael@0 220
michael@0 221 hide: function hide () {
michael@0 222 for (let command of this.commands.querySelectorAll("richlistitem[selected]")) {
michael@0 223 command.removeAttribute("selected");
michael@0 224 }
michael@0 225 this._menuPopup.hide();
michael@0 226 this._popupState = null;
michael@0 227 },
michael@0 228
michael@0 229 reset: function reset() {
michael@0 230 this._popupState = null;
michael@0 231 }
michael@0 232 };
michael@0 233
michael@0 234 var MenuControlUI = {
michael@0 235 _currentControl: null,
michael@0 236 __menuPopup: null,
michael@0 237
michael@0 238 get _panel() { return document.getElementById("menucontrol-container"); },
michael@0 239 get _popup() { return document.getElementById("menucontrol-popup"); },
michael@0 240 get commands() { return this._popup.childNodes[0]; },
michael@0 241
michael@0 242 get _menuPopup() {
michael@0 243 if (!this.__menuPopup) {
michael@0 244 this.__menuPopup = new MenuPopup(this._panel, this._popup);
michael@0 245 this.__menuPopup.controller = this;
michael@0 246 }
michael@0 247 return this.__menuPopup;
michael@0 248 },
michael@0 249
michael@0 250 _firePopupEvent: function _firePopupEvent(aEventName) {
michael@0 251 let menupopup = this._currentControl.menupopup;
michael@0 252 if (menupopup.hasAttribute(aEventName)) {
michael@0 253 let func = new Function("event", menupopup.getAttribute(aEventName));
michael@0 254 func.call(this);
michael@0 255 }
michael@0 256 },
michael@0 257
michael@0 258 _emptyCommands: function _emptyCommands() {
michael@0 259 while (this.commands.firstChild)
michael@0 260 this.commands.removeChild(this.commands.firstChild);
michael@0 261 },
michael@0 262
michael@0 263 _positionOptions: function _positionOptions() {
michael@0 264 let position = this._currentControl.menupopup.position || "after_start";
michael@0 265 let rect = this._currentControl.getBoundingClientRect();
michael@0 266
michael@0 267 let options = {};
michael@0 268
michael@0 269 // TODO: Detect text direction and flip for RTL.
michael@0 270
michael@0 271 switch (position) {
michael@0 272 case "before_start":
michael@0 273 options.xPos = rect.left;
michael@0 274 options.yPos = rect.top;
michael@0 275 options.bottomAligned = true;
michael@0 276 options.leftAligned = true;
michael@0 277 break;
michael@0 278 case "before_end":
michael@0 279 options.xPos = rect.right;
michael@0 280 options.yPos = rect.top;
michael@0 281 options.bottomAligned = true;
michael@0 282 options.rightAligned = true;
michael@0 283 break;
michael@0 284 case "after_start":
michael@0 285 options.xPos = rect.left;
michael@0 286 options.yPos = rect.bottom;
michael@0 287 options.topAligned = true;
michael@0 288 options.leftAligned = true;
michael@0 289 break;
michael@0 290 case "after_end":
michael@0 291 options.xPos = rect.right;
michael@0 292 options.yPos = rect.bottom;
michael@0 293 options.topAligned = true;
michael@0 294 options.rightAligned = true;
michael@0 295 break;
michael@0 296
michael@0 297 // TODO: Support other popup positions.
michael@0 298 }
michael@0 299
michael@0 300 return options;
michael@0 301 },
michael@0 302
michael@0 303 show: function show(aMenuControl) {
michael@0 304 this._currentControl = aMenuControl;
michael@0 305 this._panel.setAttribute("for", aMenuControl.id);
michael@0 306 this._firePopupEvent("onpopupshowing");
michael@0 307
michael@0 308 this._emptyCommands();
michael@0 309 let children = this._currentControl.menupopup.children;
michael@0 310 for (let i = 0; i < children.length; i++) {
michael@0 311 let child = children[i];
michael@0 312 let item = document.createElement("richlistitem");
michael@0 313
michael@0 314 if (child.disabled)
michael@0 315 item.setAttribute("disabled", "true");
michael@0 316
michael@0 317 if (child.hidden)
michael@0 318 item.setAttribute("hidden", "true");
michael@0 319
michael@0 320 // Add selected as a class name instead of an attribute to not being overidden
michael@0 321 // by the richlistbox behavior (it sets the "current" and "selected" attribute
michael@0 322 if (child.selected)
michael@0 323 item.setAttribute("class", "selected");
michael@0 324
michael@0 325 let image = document.createElement("image");
michael@0 326 image.setAttribute("src", child.image || "");
michael@0 327 item.appendChild(image);
michael@0 328
michael@0 329 let label = document.createElement("label");
michael@0 330 label.setAttribute("value", child.label);
michael@0 331 item.appendChild(label);
michael@0 332
michael@0 333 this.commands.appendChild(item);
michael@0 334 }
michael@0 335
michael@0 336 this._menuPopup.show(this._positionOptions());
michael@0 337 },
michael@0 338
michael@0 339 selectByIndex: function mn_selectByIndex(aIndex) {
michael@0 340 this._currentControl.selectedIndex = aIndex;
michael@0 341
michael@0 342 // Dispatch a xul command event to the attached menulist
michael@0 343 if (this._currentControl.dispatchEvent) {
michael@0 344 let evt = document.createEvent("XULCommandEvent");
michael@0 345 evt.initCommandEvent("command", true, true, window, 0, false, false, false, false, null);
michael@0 346 this._currentControl.dispatchEvent(evt);
michael@0 347 }
michael@0 348
michael@0 349 this._menuPopup.hide();
michael@0 350 }
michael@0 351 };
michael@0 352
michael@0 353 function MenuPopup(aPanel, aPopup) {
michael@0 354 this._panel = aPanel;
michael@0 355 this._popup = aPopup;
michael@0 356 this._wantTypeBehind = false;
michael@0 357
michael@0 358 window.addEventListener('MozAppbarShowing', this, false);
michael@0 359 }
michael@0 360 MenuPopup.prototype = {
michael@0 361 get visible() { return !this._panel.hidden; },
michael@0 362 get commands() { return this._popup.childNodes[0]; },
michael@0 363
michael@0 364 show: function (aPositionOptions) {
michael@0 365 if (!this.visible) {
michael@0 366 this._animateShow(aPositionOptions);
michael@0 367 }
michael@0 368 },
michael@0 369
michael@0 370 hide: function () {
michael@0 371 if (this.visible) {
michael@0 372 this._animateHide();
michael@0 373 }
michael@0 374 },
michael@0 375
michael@0 376 _position: function _position(aPositionOptions) {
michael@0 377 let aX = aPositionOptions.xPos;
michael@0 378 let aY = aPositionOptions.yPos;
michael@0 379 let aSource = aPositionOptions.source;
michael@0 380
michael@0 381 // Set these first so they are set when we do misc. calculations below.
michael@0 382 if (aPositionOptions.maxWidth) {
michael@0 383 this._popup.style.maxWidth = aPositionOptions.maxWidth + "px";
michael@0 384 }
michael@0 385 if (aPositionOptions.maxHeight) {
michael@0 386 this._popup.style.maxHeight = aPositionOptions.maxHeight + "px";
michael@0 387 }
michael@0 388
michael@0 389 let width = this._popup.boxObject.width;
michael@0 390 let height = this._popup.boxObject.height;
michael@0 391 let halfWidth = width / 2;
michael@0 392 let screenWidth = ContentAreaObserver.width;
michael@0 393 let screenHeight = ContentAreaObserver.height;
michael@0 394
michael@0 395 if (aPositionOptions.rightAligned)
michael@0 396 aX -= width;
michael@0 397
michael@0 398 if (aPositionOptions.bottomAligned)
michael@0 399 aY -= height;
michael@0 400
michael@0 401 if (aPositionOptions.centerHorizontally)
michael@0 402 aX -= halfWidth;
michael@0 403
michael@0 404 // Always leave some padding.
michael@0 405 if (aX < kPositionPadding) {
michael@0 406 aX = kPositionPadding;
michael@0 407 } else if (aX + width + kPositionPadding > screenWidth){
michael@0 408 // Don't let the popup overflow to the right.
michael@0 409 aX = Math.max(screenWidth - width - kPositionPadding, kPositionPadding);
michael@0 410 }
michael@0 411
michael@0 412 if (aY < kPositionPadding && aPositionOptions.moveBelowToFit) {
michael@0 413 // show context menu below when it doesn't fit.
michael@0 414 aY = aPositionOptions.yPos;
michael@0 415 }
michael@0 416
michael@0 417 if (aY < kPositionPadding) {
michael@0 418 aY = kPositionPadding;
michael@0 419 } else if (aY + height + kPositionPadding > screenHeight){
michael@0 420 aY = Math.max(screenHeight - height - kPositionPadding, kPositionPadding);
michael@0 421 }
michael@0 422
michael@0 423 this._panel.left = aX;
michael@0 424 this._panel.top = aY;
michael@0 425
michael@0 426 if (!aPositionOptions.maxHeight) {
michael@0 427 // Make sure it fits in the window.
michael@0 428 let popupHeight = Math.min(aY + height + kPositionPadding, screenHeight - aY - kPositionPadding);
michael@0 429 this._popup.style.maxHeight = popupHeight + "px";
michael@0 430 }
michael@0 431
michael@0 432 if (!aPositionOptions.maxWidth) {
michael@0 433 let popupWidth = Math.min(aX + width + kPositionPadding, screenWidth - aX - kPositionPadding);
michael@0 434 this._popup.style.maxWidth = popupWidth + "px";
michael@0 435 }
michael@0 436 },
michael@0 437
michael@0 438 _animateShow: function (aPositionOptions) {
michael@0 439 let deferred = Promise.defer();
michael@0 440
michael@0 441 window.addEventListener("keypress", this, true);
michael@0 442 window.addEventListener("mousedown", this, true);
michael@0 443 window.addEventListener("touchstart", this, true);
michael@0 444 window.addEventListener("scroll", this, true);
michael@0 445 window.addEventListener("blur", this, true);
michael@0 446 Elements.stack.addEventListener("PopupChanged", this, false);
michael@0 447
michael@0 448 this._panel.hidden = false;
michael@0 449 let popupFrom = !aPositionOptions.bottomAligned ? "above" : "below";
michael@0 450 this._panel.setAttribute("showingfrom", popupFrom);
michael@0 451
michael@0 452 // This triggers a reflow, which sets transitionability.
michael@0 453 // All animation/transition setup must happen before here.
michael@0 454 this._position(aPositionOptions || {});
michael@0 455
michael@0 456 let self = this;
michael@0 457 this._panel.addEventListener("transitionend", function popupshown () {
michael@0 458 self._panel.removeEventListener("transitionend", popupshown);
michael@0 459 self._panel.removeAttribute("showingfrom");
michael@0 460
michael@0 461 self._dispatch("popupshown");
michael@0 462 deferred.resolve();
michael@0 463 });
michael@0 464
michael@0 465 this._panel.setAttribute("showing", "true");
michael@0 466 return deferred.promise;
michael@0 467 },
michael@0 468
michael@0 469 _animateHide: function () {
michael@0 470 let deferred = Promise.defer();
michael@0 471
michael@0 472 window.removeEventListener("keypress", this, true);
michael@0 473 window.removeEventListener("mousedown", this, true);
michael@0 474 window.removeEventListener("touchstart", this, true);
michael@0 475 window.removeEventListener("scroll", this, true);
michael@0 476 window.removeEventListener("blur", this, true);
michael@0 477 Elements.stack.removeEventListener("PopupChanged", this, false);
michael@0 478
michael@0 479 let self = this;
michael@0 480 this._panel.addEventListener("transitionend", function popuphidden() {
michael@0 481 self._panel.removeEventListener("transitionend", popuphidden);
michael@0 482 self._panel.removeAttribute("hiding");
michael@0 483 self._panel.hidden = true;
michael@0 484 self._popup.style.maxWidth = "none";
michael@0 485 self._popup.style.maxHeight = "none";
michael@0 486
michael@0 487 self._dispatch("popuphidden");
michael@0 488 deferred.resolve();
michael@0 489 });
michael@0 490
michael@0 491 this._panel.setAttribute("hiding", "true");
michael@0 492 this._panel.removeAttribute("showing");
michael@0 493 return deferred.promise;
michael@0 494 },
michael@0 495
michael@0 496 _dispatch: function _dispatch(aName) {
michael@0 497 let event = document.createEvent("Events");
michael@0 498 event.initEvent(aName, true, false);
michael@0 499 this._panel.dispatchEvent(event);
michael@0 500 },
michael@0 501
michael@0 502 handleEvent: function handleEvent(aEvent) {
michael@0 503 switch (aEvent.type) {
michael@0 504 case "keypress":
michael@0 505 // this.commands is not holding focus and not processing key events.
michael@0 506 // Proxying events so that they're handled properly.
michael@0 507
michael@0 508 // Avoid recursion
michael@0 509 if (aEvent.mine)
michael@0 510 break;
michael@0 511
michael@0 512 let ev = document.createEvent("KeyboardEvent");
michael@0 513 ev.initKeyEvent(
michael@0 514 "keypress", // in DOMString typeArg,
michael@0 515 false, // in boolean canBubbleArg,
michael@0 516 true, // in boolean cancelableArg,
michael@0 517 null, // in nsIDOMAbstractView viewArg, Specifies UIEvent.view. This value may be null.
michael@0 518 aEvent.ctrlKey, // in boolean ctrlKeyArg,
michael@0 519 aEvent.altKey, // in boolean altKeyArg,
michael@0 520 aEvent.shiftKey, // in boolean shiftKeyArg,
michael@0 521 aEvent.metaKey, // in boolean metaKeyArg,
michael@0 522 aEvent.keyCode, // in unsigned long keyCodeArg,
michael@0 523 aEvent.charCode); // in unsigned long charCodeArg);
michael@0 524
michael@0 525 ev.mine = true;
michael@0 526
michael@0 527 switch (aEvent.keyCode) {
michael@0 528 case aEvent.DOM_VK_ESCAPE:
michael@0 529 this.hide();
michael@0 530 break;
michael@0 531
michael@0 532 case aEvent.DOM_VK_RETURN:
michael@0 533 this.commands.currentItem.click();
michael@0 534 break;
michael@0 535 }
michael@0 536
michael@0 537 if (Util.isNavigationKey(aEvent.keyCode)) {
michael@0 538 aEvent.stopPropagation();
michael@0 539 aEvent.preventDefault();
michael@0 540 this.commands.dispatchEvent(ev);
michael@0 541 } else if (!this._wantTypeBehind) {
michael@0 542 // Hide the context menu so you can't type behind it.
michael@0 543 aEvent.stopPropagation();
michael@0 544 aEvent.preventDefault();
michael@0 545 this.hide();
michael@0 546 }
michael@0 547 break;
michael@0 548 case "blur":
michael@0 549 case "mousedown":
michael@0 550 case "touchstart":
michael@0 551 case "scroll":
michael@0 552 if (!this._popup.contains(aEvent.target)) {
michael@0 553 aEvent.stopPropagation();
michael@0 554 this.hide();
michael@0 555 }
michael@0 556 break;
michael@0 557 case "PopupChanged":
michael@0 558 if (aEvent.detail) {
michael@0 559 this.hide();
michael@0 560 }
michael@0 561 break;
michael@0 562 case "MozAppbarShowing":
michael@0 563 if (this.controller && this.controller.hide) {
michael@0 564 this.controller.hide()
michael@0 565 } else {
michael@0 566 this.hide();
michael@0 567 }
michael@0 568 break;
michael@0 569 }
michael@0 570 }
michael@0 571 };

mercurial