OLD | NEW |
(Empty) | |
| 1 /* Copyright 2016 The Chromium Authors. All Rights Reserved. |
| 2 * |
| 3 * Use of this source code is governed by a BSD-style |
| 4 * license that can be found in the LICENSE file or at |
| 5 * https://developers.google.com/open-source/licenses/bsd |
| 6 */ |
| 7 |
| 8 /** |
| 9 * It is common to make a DIV temporarily visible to simulate |
| 10 * a popup window. Often, this is done by adding an onClick |
| 11 * handler to the element that can be clicked on to show the |
| 12 * popup. |
| 13 * |
| 14 * Unfortunately, closing the popup is not as simple. |
| 15 * The popup creator often wants to let the user close |
| 16 * the popup by clicking elsewhere on the window; however, |
| 17 * the popup only receives mouse events that occur |
| 18 * on the popup itself. Thus, popups need a mechanism |
| 19 * that notifies them that the user has clicked elsewhere |
| 20 * to try to get rid of them. |
| 21 * |
| 22 * PopupController is such a mechanism -- |
| 23 * it monitors all mousedown events that |
| 24 * occur in the window so that it can notify registered |
| 25 * popups of the mousedown, and the popups can choose |
| 26 * to deactivate themselves. |
| 27 * |
| 28 * For an object to qualify as a popup, it must have a |
| 29 * function called "deactivate" that takes a mousedown event |
| 30 * and returns a boolean indicating that it has deactivated |
| 31 * itself as a result of that event. |
| 32 * |
| 33 * EXAMPLE: |
| 34 * |
| 35 * // popup that attaches itself to the supplied div |
| 36 * function MyPopup(div) { |
| 37 * this._div = div; |
| 38 * this._isVisible = false; |
| 39 * this._innerHTML = ... |
| 40 * } |
| 41 * |
| 42 * MyPopup.prototype.show = function() { |
| 43 * this._div.display = ''; |
| 44 * this._isVisible = true; |
| 45 * PC_addPopup(this); |
| 46 * } |
| 47 * |
| 48 * MyPopup.prototype.hide = function() { |
| 49 * this._div.display = 'none'; |
| 50 * this._isVisible = false; |
| 51 * } |
| 52 * |
| 53 * MyPopup.prototype.deactivate = function(e) { |
| 54 * if (this._isVisible) { |
| 55 * var p = GetMousePosition(e); |
| 56 * if (nodeBounds(this._div).contains(p)) { |
| 57 * return false; // use clicked on popup, remain visible |
| 58 * } else { |
| 59 * this.hide(); |
| 60 * return true; // clicked outside popup, make invisible |
| 61 * } |
| 62 * } else { |
| 63 * return true; // already deactivated, not visible |
| 64 * } |
| 65 * } |
| 66 * |
| 67 * DEPENDENCIES (from this directory): |
| 68 * bind.js |
| 69 * listen.js |
| 70 * common.js |
| 71 * shapes.js |
| 72 * geom.js |
| 73 * |
| 74 * USAGE: |
| 75 * _PC_Install() must be called after the body is loaded |
| 76 */ |
| 77 |
| 78 /** |
| 79 * PopupController constructor. |
| 80 * @constructor |
| 81 */ |
| 82 function PopupController() { |
| 83 this.activePopups_ = []; |
| 84 } |
| 85 |
| 86 /** |
| 87 * @param {Document} opt_doc document to add PopupController to |
| 88 * DEFAULT: "document" variable that is currently in scope |
| 89 * @return {boolean} indicating if PopupController installed for the document; |
| 90 * returns false if document already had PopupController |
| 91 */ |
| 92 function _PC_Install(opt_doc) { |
| 93 if (gPopupControllerInstalled) return false; |
| 94 gPopupControllerInstalled = true; |
| 95 var doc = (opt_doc) ? opt_doc : document; |
| 96 |
| 97 // insert _notifyPopups in BODY's onmousedown chain |
| 98 listen(doc.body, 'mousedown', PC_notifyPopups); |
| 99 return true; |
| 100 } |
| 101 |
| 102 /** |
| 103 * Notifies each popup of a mousedown event, giving |
| 104 * each popup the chance to deactivate itself. |
| 105 * |
| 106 * @throws Error if a popup does not have a deactivate function |
| 107 * |
| 108 * @private |
| 109 */ |
| 110 function PC_notifyPopups(e) { |
| 111 if (gPopupController.activePopups_.length == 0) return false; |
| 112 e = e || window.event; |
| 113 for (var i = gPopupController.activePopups_.length - 1; i >= 0; --i) { |
| 114 var popup = gPopupController.activePopups_[i]; |
| 115 PC_assertIsPopup(popup); |
| 116 if (popup.deactivate(e)) { |
| 117 gPopupController.activePopups_.splice(i, 1); |
| 118 } |
| 119 } |
| 120 return true; |
| 121 } |
| 122 |
| 123 /** |
| 124 * Adds the popup to the list of popups to be |
| 125 * notified of a mousedown event. |
| 126 * |
| 127 * @return boolean indicating if added popup; false if already contained |
| 128 * @throws Error if popup does not have a deactivate function |
| 129 */ |
| 130 function PC_addPopup(popup) { |
| 131 PC_assertIsPopup(popup); |
| 132 for (var i = 0; i < gPopupController.activePopups_.length; ++i) { |
| 133 if (popup === gPopupController.activePopups_[i]) return false; |
| 134 } |
| 135 gPopupController.activePopups_.push(popup); |
| 136 return true; |
| 137 } |
| 138 |
| 139 /** asserts that popup has a deactivate function */ |
| 140 function PC_assertIsPopup(popup) { |
| 141 AssertType(popup.deactivate, Function, 'popup missing deactivate function'); |
| 142 } |
| 143 |
| 144 var gPopupController = new PopupController(); |
| 145 var gPopupControllerInstalled = false; |
OLD | NEW |