Index: appengine/monorail/static/js/graveyard/popup_controller.js |
diff --git a/appengine/monorail/static/js/graveyard/popup_controller.js b/appengine/monorail/static/js/graveyard/popup_controller.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5537cde7589e28257c52d2551838a6402ff92cac |
--- /dev/null |
+++ b/appengine/monorail/static/js/graveyard/popup_controller.js |
@@ -0,0 +1,145 @@ |
+/* Copyright 2016 The Chromium Authors. All Rights Reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style |
+ * license that can be found in the LICENSE file or at |
+ * https://developers.google.com/open-source/licenses/bsd |
+ */ |
+ |
+/** |
+ * It is common to make a DIV temporarily visible to simulate |
+ * a popup window. Often, this is done by adding an onClick |
+ * handler to the element that can be clicked on to show the |
+ * popup. |
+ * |
+ * Unfortunately, closing the popup is not as simple. |
+ * The popup creator often wants to let the user close |
+ * the popup by clicking elsewhere on the window; however, |
+ * the popup only receives mouse events that occur |
+ * on the popup itself. Thus, popups need a mechanism |
+ * that notifies them that the user has clicked elsewhere |
+ * to try to get rid of them. |
+ * |
+ * PopupController is such a mechanism -- |
+ * it monitors all mousedown events that |
+ * occur in the window so that it can notify registered |
+ * popups of the mousedown, and the popups can choose |
+ * to deactivate themselves. |
+ * |
+ * For an object to qualify as a popup, it must have a |
+ * function called "deactivate" that takes a mousedown event |
+ * and returns a boolean indicating that it has deactivated |
+ * itself as a result of that event. |
+ * |
+ * EXAMPLE: |
+ * |
+ * // popup that attaches itself to the supplied div |
+ * function MyPopup(div) { |
+ * this._div = div; |
+ * this._isVisible = false; |
+ * this._innerHTML = ... |
+ * } |
+ * |
+ * MyPopup.prototype.show = function() { |
+ * this._div.display = ''; |
+ * this._isVisible = true; |
+ * PC_addPopup(this); |
+ * } |
+ * |
+ * MyPopup.prototype.hide = function() { |
+ * this._div.display = 'none'; |
+ * this._isVisible = false; |
+ * } |
+ * |
+ * MyPopup.prototype.deactivate = function(e) { |
+ * if (this._isVisible) { |
+ * var p = GetMousePosition(e); |
+ * if (nodeBounds(this._div).contains(p)) { |
+ * return false; // use clicked on popup, remain visible |
+ * } else { |
+ * this.hide(); |
+ * return true; // clicked outside popup, make invisible |
+ * } |
+ * } else { |
+ * return true; // already deactivated, not visible |
+ * } |
+ * } |
+ * |
+ * DEPENDENCIES (from this directory): |
+ * bind.js |
+ * listen.js |
+ * common.js |
+ * shapes.js |
+ * geom.js |
+ * |
+ * USAGE: |
+ * _PC_Install() must be called after the body is loaded |
+ */ |
+ |
+/** |
+ * PopupController constructor. |
+ * @constructor |
+ */ |
+function PopupController() { |
+ this.activePopups_ = []; |
+} |
+ |
+/** |
+ * @param {Document} opt_doc document to add PopupController to |
+ * DEFAULT: "document" variable that is currently in scope |
+ * @return {boolean} indicating if PopupController installed for the document; |
+ * returns false if document already had PopupController |
+ */ |
+function _PC_Install(opt_doc) { |
+ if (gPopupControllerInstalled) return false; |
+ gPopupControllerInstalled = true; |
+ var doc = (opt_doc) ? opt_doc : document; |
+ |
+ // insert _notifyPopups in BODY's onmousedown chain |
+ listen(doc.body, 'mousedown', PC_notifyPopups); |
+ return true; |
+} |
+ |
+/** |
+ * Notifies each popup of a mousedown event, giving |
+ * each popup the chance to deactivate itself. |
+ * |
+ * @throws Error if a popup does not have a deactivate function |
+ * |
+ * @private |
+ */ |
+function PC_notifyPopups(e) { |
+ if (gPopupController.activePopups_.length == 0) return false; |
+ e = e || window.event; |
+ for (var i = gPopupController.activePopups_.length - 1; i >= 0; --i) { |
+ var popup = gPopupController.activePopups_[i]; |
+ PC_assertIsPopup(popup); |
+ if (popup.deactivate(e)) { |
+ gPopupController.activePopups_.splice(i, 1); |
+ } |
+ } |
+ return true; |
+} |
+ |
+/** |
+ * Adds the popup to the list of popups to be |
+ * notified of a mousedown event. |
+ * |
+ * @return boolean indicating if added popup; false if already contained |
+ * @throws Error if popup does not have a deactivate function |
+ */ |
+function PC_addPopup(popup) { |
+ PC_assertIsPopup(popup); |
+ for (var i = 0; i < gPopupController.activePopups_.length; ++i) { |
+ if (popup === gPopupController.activePopups_[i]) return false; |
+ } |
+ gPopupController.activePopups_.push(popup); |
+ return true; |
+} |
+ |
+/** asserts that popup has a deactivate function */ |
+function PC_assertIsPopup(popup) { |
+ AssertType(popup.deactivate, Function, 'popup missing deactivate function'); |
+} |
+ |
+var gPopupController = new PopupController(); |
+var gPopupControllerInstalled = false; |