Index: appengine/monorail/static/js/tracker/tracker-display.js |
diff --git a/appengine/monorail/static/js/tracker/tracker-display.js b/appengine/monorail/static/js/tracker/tracker-display.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..91c2b82057b70bc393efc4e0ff368ab2d6edd4f8 |
--- /dev/null |
+++ b/appengine/monorail/static/js/tracker/tracker-display.js |
@@ -0,0 +1,407 @@ |
+/* 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 |
+ */ |
+ |
+/** |
+ * Functions used by Monorail to control the display of elements on |
+ * the page, rollovers, and popup menus. |
+ * |
+ */ |
+ |
+ |
+/** |
+ * Show a popup menu below a specified element. Optional x and y deltas can be |
+ * used to fine-tune placement. |
+ * @param {string} id The HTML id of the popup menu. |
+ * @param {Element} el The HTML element that the popup should appear near. |
+ * @param {number} opt_deltaX Optional X offset to finetune placement. |
+ * @param {number} opt_deltaY Optional Y offset to finetune placement. |
+ * @param {Element} opt_menuButton The HTML element for a menu button that |
+ * was pressed to open the menu. When a button was used, we need to ignore |
+ * the first "click" event, otherwise the menu will immediately close. |
+ * @returns Always returns false to indicate that the browser should handle the |
+ * event normally. |
+ */ |
+function TKR_showBelow(id, el, opt_deltaX, opt_deltaY, opt_menuButton) { |
+ var popupDiv = $(id); |
+ var elBounds = nodeBounds(el) |
+ var startX = elBounds.x; |
+ var startY = elBounds.y + elBounds.h; |
+ if (BR_IsIE()) { |
+ startX -= 1; |
+ startY -= 2; |
+ } |
+ if (BR_IsSafari()) { |
+ startX += 1; |
+ } |
+ popupDiv.style.display = 'block'; //needed so that offsetWidth != 0 |
+ |
+ popupDiv.style.left = '-2000px'; |
+ if (id == 'pop_dot' || id == 'redoMenu') { |
+ startX = startX - popupDiv.offsetWidth + el.offsetWidth; |
+ } |
+ if (opt_deltaX) startX += opt_deltaX; |
+ if (opt_deltaY) startY += opt_deltaY; |
+ popupDiv.style.left = (startX)+'px'; |
+ popupDiv.style.top = (startY)+'px'; |
+ var popup = new TKR_MyPopup(popupDiv, opt_menuButton); |
+ popup.show(); |
+ return false; |
+} |
+ |
+ |
+/** |
+ * Show a popup menu to the right of a specified element. If there is not |
+ * enough space to the right, then it will open to the left side instead. |
+ * Optional x and y deltas can be used to fine-tune placement. |
+ * TODO(jrobbins): reduce redundancy with function above. |
+ * @param {string} id The HTML id of the popup menu. |
+ * @param {Element} el The HTML element that the popup should appear near. |
+ * @param {number} opt_deltaX Optional X offset to finetune placement. |
+ * @param {number} opt_deltaY Optional Y offset to finetune placement. |
+ * @returns Always returns false to indicate that the browser should handle the |
+ * event normally. |
+ */ |
+function TKR_showRight(id, el, opt_deltaX, opt_deltaY) { |
+ var popupDiv = $(id); |
+ var elBounds = nodeBounds(el); |
+ var startX = elBounds.x + elBounds.w; |
+ var startY = elBounds.y; |
+ |
+ // Calculate pageSize.w and pageSize.h |
+ var docElemWidth = document.documentElement.clientWidth; |
+ var docElemHeight = document.documentElement.clientHeight; |
+ var pageSize = { |
+ w: (window.innerWidth || docElemWidth && docElemWidth > 0 ? |
+ docElemWidth : document.body.clientWidth) || 1, |
+ h: (window.innerHeight || docElemHeight && docElemHeight > 0 ? |
+ docElemHeight : document.body.clientHeight) || 1 |
+ } |
+ |
+ // We need to make the popupDiv visible in order to capture its width |
+ popupDiv.style.display = 'block'; |
+ var popupDivBounds = nodeBounds(popupDiv); |
+ |
+ // Show popup to the left |
+ if (startX + popupDivBounds.w > pageSize.w) { |
+ startX = elBounds.x - popupDivBounds.w; |
+ if (BR_IsIE()) { |
+ startX -= 4; |
+ startY -= 2; |
+ } |
+ if (BR_IsNav()) { |
+ startX -= 2; |
+ } |
+ if (BR_IsSafari()) { |
+ startX += -1; |
+ } |
+ |
+ // Show popup to the right |
+ } else { |
+ if (BR_IsIE()) { |
+ startY -= 2; |
+ } |
+ if (BR_IsNav()) { |
+ startX += 2; |
+ } |
+ if (BR_IsSafari()) { |
+ startX += 3; |
+ } |
+ } |
+ |
+ popupDiv.style.left = '-2000px'; |
+ popupDiv.style.position = 'absolute'; |
+ if (opt_deltaX) startX += opt_deltaX; |
+ if (opt_deltaY) startY += opt_deltaY; |
+ popupDiv.style.left = (startX)+'px'; |
+ popupDiv.style.top = (startY)+'px'; |
+ var popup = new TKR_MyPopup(popupDiv); |
+ popup.show(); |
+ return false; |
+} |
+ |
+ |
+/** |
+ * Close the specified popup menu and unregister it with the popup |
+ * controller, otherwise old leftover popup instances can mess with |
+ * the future display of menus. |
+ * @param {string} id The HTML ID of the element to hide. |
+ */ |
+function TKR_closePopup(id) { |
+ var e = $(id); |
+ if (e) { |
+ for (var i = 0; i < gPopupController.activePopups_.length; ++i) { |
+ if (e === gPopupController.activePopups_[i]._div) { |
+ var popup = gPopupController.activePopups_[i]; |
+ popup.hide(); |
+ gPopupController.activePopups_.splice(i, 1); |
+ return; |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+var TKR_allColumnNames = []; // Will be defined in HTML file. |
+ |
+/** |
+ * Close all popup menus. Also, reset the hover state of the menu item that |
+ * was selected. The list of popup menu names is computed from the list of |
+ * columns specified in the HTML for the issue list page. |
+ * @param menuItem {Element} The menu item that the user clicked. |
+ * @returns Always returns false to indicate that the browser should handle the |
+ * event normally. |
+ */ |
+function TKR_closeAllPopups(menuItem) { |
+ for (var col_index = 0; col_index < TKR_allColumnNames.length; col_index++) { |
+ TKR_closePopup('pop_' + col_index); |
+ TKR_closePopup('filter_' + col_index); |
+ } |
+ TKR_closePopup('pop_dot'); |
+ TKR_closePopup('redoMenu'); |
+ menuItem.classList.remove('hover'); |
+ return false; |
+} |
+ |
+ |
+/** |
+ * Close all the submenus (of which, one may be currently open). |
+ * @returns Always returns false to indicate that the browser should handle the |
+ * event normally. |
+ */ |
+function TKR_closeSubmenus() { |
+ for (var col_index = 0; col_index < TKR_allColumnNames.length; col_index++) { |
+ TKR_closePopup('filter_' + col_index); |
+ } |
+ return false; |
+} |
+ |
+ |
+/** |
+ * Find the enclosing HTML element that controls this section of the |
+ * page and set it to use CSS class "opened". That will make the |
+ * section display in the opened state, regardless of what state is |
+ * was in before. |
+ * @param {Element} el The HTML element that the user clicked on. |
+ * @returns Always returns false to indicate that the browser should handle the |
+ * event normally. |
+ */ |
+function TKR_showHidden(el) { |
+ while (el) { |
+ if (el.classList.contains('closed')) { |
+ el.classList.remove('closed'); |
+ el.classList.add('opened'); |
+ return false; |
+ } |
+ if (el.classList.contains('opened')) { |
+ return false; |
+ } |
+ el = el.parentNode; |
+ } |
+} |
+ |
+ |
+/** |
+ * Toggle the display of a column in the issue list page. That is |
+ * done by adding or removing a CSS class of an enclosing HTML |
+ * element, and by CSS rules that trigger based on that CSS class. |
+ * @param {string} colName The name of the column to toggle, |
+ * corresponds to a CSS class. |
+ * @returns Always returns false to indicate that the browser should |
+ * handle the event normally. |
+ */ |
+function TKR_toggleColumn(colName) { |
+ var controlDiv = $('colcontrol'); |
+ if (controlDiv.classList.contains(colName)) { |
+ controlDiv.classList.remove(colName); |
+ } |
+ else { |
+ controlDiv.classList.add(colName); |
+ } |
+ return false; |
+} |
+ |
+ |
+/** |
+ * Toggle the display of a set of rows in the issue list page. That is |
+ * done by adding or removing a CSS class of an enclosing HTML |
+ * element, and by CSS rules that trigger based on that CSS class. |
+ * TODO(jrobbins): actually, this automatically hides the other groups. |
+ * @param {string} rowClassName The name of the row group to toggle, |
+ * corresponds to a CSS class. |
+ * @returns Always returns false to indicate that the browser should |
+ * handle the event normally. |
+ */ |
+function TKR_toggleRows(rowClassName) { |
+ var controlDiv = $('colcontrol'); |
+ controlDiv.classList.add('hide_pri_groups'); |
+ controlDiv.classList.add('hide_mile_groups'); |
+ controlDiv.classList.add('hide_stat_groups'); |
+ TKR_toggleColumn(rowClassName); |
+ return false; |
+} |
+ |
+ |
+/** |
+ * A simple class that can manage the display of a popup menu. Instances |
+ * of this class are used by popup_controller.js. |
+ * @param {Element} div The div that contains the popup menu. |
+ * @param {Element} opt_launcherEl The button that launched the popup menu, |
+ * if any. |
+ * @constructor |
+ */ |
+function TKR_MyPopup(div, opt_launcherEl) { |
+ this._div = div; |
+ this._launcher = opt_launcherEl; |
+ this._isVisible = false; |
+} |
+ |
+ |
+/** |
+ * Show a popup menu. This method registers the popup with popup_controller. |
+ */ |
+TKR_MyPopup.prototype.show = function() { |
+ this._div.style.display = 'block'; |
+ this._isVisible = true; |
+ PC_addPopup(this); |
+} |
+ |
+ |
+/** |
+ * Show a popup menu. This method is called from the deactive method, |
+ * which is called by popup_controller. |
+ */ |
+TKR_MyPopup.prototype.hide = function() { |
+ this._div.style.display = 'none'; |
+ this._isVisible = false; |
+} |
+ |
+ |
+/** |
+ * When the popup_controller gets a user click, it calls deactive() on |
+ * every active popup to check if the click should close that popup. |
+ */ |
+TKR_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 if (this._launcher && nodeBounds(this._launcher).contains(p)) { |
+ this._launcher = null; |
+ return false; // mouseup element that launched menu, remain visible |
+ } else { |
+ this.hide(); |
+ return true; // clicked outside popup, make invisible |
+ } |
+ } else { |
+ return true; // already deactivated, not visible |
+ } |
+} |
+ |
+ |
+/** |
+ * Highlight the issue row on the list page that contains the given |
+ * checkbox. |
+ * @param {Element} cb The checkbox that the user changed. |
+ * @returns Always returns false to indicate that the browser should |
+ * handle the event normally. |
+ */ |
+function TKR_highlightRow(el) { |
+ var checked = el.checked; |
+ while (el && el.tagName != 'TR') { |
+ el = el.parentNode; |
+ } |
+ if (checked) { |
+ el.classList.add('selected'); |
+ } |
+ else { |
+ el.classList.remove('selected'); |
+ } |
+ return false; |
+} |
+ |
+ |
+/** |
+ * Floats the metadata section on the LHS of issue/source detail pages. |
+ * It assumes that the metadata <div> has id 'meta-float' and its outer |
+ * container 'meta-container'. |
+ */ |
+function TKR_floatMetadata() { |
+ var el = $('meta-float'); |
+ var container = $('issuemeta'); |
+ |
+ window.addEventListener('scroll', function() { |
+ TKR_floatVertically(el, container); |
+ }, false); |
+} |
+ |
+/** |
+ * Floats the given element vertically within the provided container as user |
+ * scrolls up or down the page. It adjusts the width and padding of the parent |
+ * element since it sets the 'position' style of the target element to 'fixed'. |
+ * @param {Element} el The HTML element to float. |
+ * @param {Element} container The container HTML element. |
+ */ |
+function TKR_floatVertically(el, container) { |
+ var elBounds = nodeBounds(el); |
+ var containerBounds = nodeBounds(container); |
+ var scrollTop = GetScrollTop(window); |
+ |
+ if (!el.style.width) { |
+ el.style.width = elBounds.w + 'px'; |
+ } |
+ |
+ if ((scrollTop > containerBounds.y) && |
+ (scrollTop - containerBounds.y + elBounds.h <= |
+ container.style.top + containerBounds.h) && |
+ (GetWindowHeight(window) > elBounds.h)) { |
+ if (el.style.position != 'fixed') { |
+ el.style.position = 'fixed'; |
+ el.style.top = '2px'; |
+ if (BR_IsIE()) { |
+ el.parentNode.style.paddingRight = elBounds.w + 2 + 'px'; |
+ } else { |
+ el.parentNode.style.minWidth = elBounds.w + 'px'; |
+ } |
+ } |
+ el.style.left = (6 - GetScrollLeft(window)) + 'px'; |
+ } else if (el.style.position != 'relative') { |
+ el.style.position = 'relative'; |
+ el.style.left = '0'; |
+ if (BR_IsIE()) { |
+ el.parentNode.style.paddingRight = ''; |
+ } |
+ } |
+} |
+ |
+/** |
+ * XMLHTTP object used to remember display preferences on the server. |
+ */ |
+var TKR_prefsXmlHttp = undefined; |
+ |
+ |
+/** |
+ * Contact the server to remember a PeopleDetail display preference. |
+ * @param {string} projectName The name of the current project. |
+ * @param {number} expand Zero or one for the widget hide/show state. |
+ * @param {string} token The security token. |
+ */ |
+function TKR_setPeoplePrefs(projectName, expand, token) { |
+ TKR_prefsXmlHttp = XH_XmlHttpCreate() |
+ var prefsURL = '/p/' + projectName + '/people/detailPrefs.do'; |
+ var data = 'perms_expanded=' + expand + '&token=' + token; |
+ XH_XmlHttpPOST( |
+ TKR_prefsXmlHttp, prefsURL, data, TKR_prefsFeedCallback); |
+} |
+ |
+ |
+/** |
+ * The communication with the server has made some progress. If it is |
+ * done, then process the response. |
+ */ |
+function TKR_prefsFeedCallback() { |
+ // Actually, we don't use the return value at all, so do nothing. |
+} |