blob: ef475d411f66d4244ace5e736ddc72dc7ad687ce [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001// Copyright 2016 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5/**
6 * Functions used by Monorail to control the display of elements on
7 * the page, rollovers, and popup menus.
8 *
9 */
10
11
12/**
13 * Show a popup menu below a specified element. Optional x and y deltas can be
14 * used to fine-tune placement.
15 * @param {string} id The HTML id of the popup menu.
16 * @param {Element} el The HTML element that the popup should appear near.
17 * @param {number} opt_deltaX Optional X offset to finetune placement.
18 * @param {number} opt_deltaY Optional Y offset to finetune placement.
19 * @param {Element} opt_menuButton The HTML element for a menu button that
20 * was pressed to open the menu. When a button was used, we need to ignore
21 * the first "click" event, otherwise the menu will immediately close.
22 * @return Always returns false to indicate that the browser should handle the
23 * event normally.
24 */
25function TKR_showBelow(id, el, opt_deltaX, opt_deltaY, opt_menuButton) {
26 let popupDiv = $(id);
27 let elBounds = nodeBounds(el);
28 let startX = elBounds.x;
29 let startY = elBounds.y + elBounds.h;
30 if (BR_IsIE()) {
31 startX -= 1;
32 startY -= 2;
33 }
34 if (BR_IsSafari()) {
35 startX += 1;
36 }
37 popupDiv.style.display = 'block'; // needed so that offsetWidth != 0
38
39 popupDiv.style.left = '-2000px';
40 if (id == 'pop_dot' || id == 'redoMenu') {
41 startX = startX - popupDiv.offsetWidth + el.offsetWidth;
42 }
43 if (opt_deltaX) startX += opt_deltaX;
44 if (opt_deltaY) startY += opt_deltaY;
45 popupDiv.style.left = (startX)+'px';
46 popupDiv.style.top = (startY)+'px';
47 let popup = new TKR_MyPopup(popupDiv, opt_menuButton);
48 popup.show();
49 return false;
50}
51
52
53/**
54 * Show a popup menu to the right of a specified element. If there is not
55 * enough space to the right, then it will open to the left side instead.
56 * Optional x and y deltas can be used to fine-tune placement.
57 * TODO(jrobbins): reduce redundancy with function above.
58 * @param {string} id The HTML id of the popup menu.
59 * @param {Element} el The HTML element that the popup should appear near.
60 * @param {number} opt_deltaX Optional X offset to finetune placement.
61 * @param {number} opt_deltaY Optional Y offset to finetune placement.
62 * @return Always returns false to indicate that the browser should handle the
63 * event normally.
64 */
65function TKR_showRight(id, el, opt_deltaX, opt_deltaY) {
66 let popupDiv = $(id);
67 let elBounds = nodeBounds(el);
68 let startX = elBounds.x + elBounds.w;
69 let startY = elBounds.y;
70
71 // Calculate pageSize.w and pageSize.h
72 let docElemWidth = document.documentElement.clientWidth;
73 let docElemHeight = document.documentElement.clientHeight;
74 let pageSize = {
75 w: (window.innerWidth || docElemWidth && docElemWidth > 0 ?
76 docElemWidth : document.body.clientWidth) || 1,
77 h: (window.innerHeight || docElemHeight && docElemHeight > 0 ?
78 docElemHeight : document.body.clientHeight) || 1,
79 };
80
81 // We need to make the popupDiv visible in order to capture its width
82 popupDiv.style.display = 'block';
83 let popupDivBounds = nodeBounds(popupDiv);
84
85 // Show popup to the left
86 if (startX + popupDivBounds.w > pageSize.w) {
87 startX = elBounds.x - popupDivBounds.w;
88 if (BR_IsIE()) {
89 startX -= 4;
90 startY -= 2;
91 }
92 if (BR_IsNav()) {
93 startX -= 2;
94 }
95 if (BR_IsSafari()) {
96 startX += -1;
97 }
98
99 // Show popup to the right
100 } else {
101 if (BR_IsIE()) {
102 startY -= 2;
103 }
104 if (BR_IsNav()) {
105 startX += 2;
106 }
107 if (BR_IsSafari()) {
108 startX += 3;
109 }
110 }
111
112 popupDiv.style.left = '-2000px';
113 popupDiv.style.position = 'absolute';
114 if (opt_deltaX) startX += opt_deltaX;
115 if (opt_deltaY) startY += opt_deltaY;
116 popupDiv.style.left = (startX)+'px';
117 popupDiv.style.top = (startY)+'px';
118 let popup = new TKR_MyPopup(popupDiv);
119 popup.show();
120 return false;
121}
122
123
124/**
125 * Close the specified popup menu and unregister it with the popup
126 * controller, otherwise old leftover popup instances can mess with
127 * the future display of menus.
128 * @param {string} id The HTML ID of the element to hide.
129 */
130function TKR_closePopup(id) {
131 let e = $(id);
132 if (e) {
133 for (let i = 0; i < gPopupController.activePopups_.length; ++i) {
134 if (e === gPopupController.activePopups_[i]._div) {
135 let popup = gPopupController.activePopups_[i];
136 popup.hide();
137 gPopupController.activePopups_.splice(i, 1);
138 return;
139 }
140 }
141 }
142}
143
144
145var TKR_allColumnNames = []; // Will be defined in HTML file.
146
147/**
148 * Close all popup menus. Also, reset the hover state of the menu item that
149 * was selected. The list of popup menu names is computed from the list of
150 * columns specified in the HTML for the issue list page.
151 * @param menuItem {Element} The menu item that the user clicked.
152 * @return Always returns false to indicate that the browser should handle the
153 * event normally.
154 */
155function TKR_closeAllPopups(menuItem) {
156 for (let col_index = 0; col_index < TKR_allColumnNames.length; col_index++) {
157 TKR_closePopup('pop_' + col_index);
158 TKR_closePopup('filter_' + col_index);
159 }
160 TKR_closePopup('pop_dot');
161 TKR_closePopup('redoMenu');
162 menuItem.classList.remove('hover');
163 return false;
164}
165
166
167/**
168 * Close all the submenus (of which, one may be currently open).
169 * @return Always returns false to indicate that the browser should handle the
170 * event normally.
171 */
172function TKR_closeSubmenus() {
173 for (let col_index = 0; col_index < TKR_allColumnNames.length; col_index++) {
174 TKR_closePopup('filter_' + col_index);
175 }
176 return false;
177}
178
179
180/**
181 * Find the enclosing HTML element that controls this section of the
182 * page and set it to use CSS class "opened". That will make the
183 * section display in the opened state, regardless of what state is
184 * was in before.
185 * @param {Element} el The HTML element that the user clicked on.
186 * @return Always returns false to indicate that the browser should handle the
187 * event normally.
188 */
189function TKR_showHidden(el) {
190 while (el) {
191 if (el.classList.contains('closed')) {
192 el.classList.remove('closed');
193 el.classList.add('opened');
194 return false;
195 }
196 if (el.classList.contains('opened')) {
197 return false;
198 }
199 el = el.parentNode;
200 }
201}
202
203
204/**
205 * Toggle the display of a column in the issue list page. That is
206 * done by adding or removing a CSS class of an enclosing HTML
207 * element, and by CSS rules that trigger based on that CSS class.
208 * @param {string} colName The name of the column to toggle,
209 * corresponds to a CSS class.
210 * @return Always returns false to indicate that the browser should
211 * handle the event normally.
212 */
213function TKR_toggleColumn(colName) {
214 let controlDiv = $('colcontrol');
215 if (controlDiv.classList.contains(colName)) {
216 controlDiv.classList.remove(colName);
217 } else {
218 controlDiv.classList.add(colName);
219 }
220 return false;
221}
222
223
224/**
225 * Toggle the display of a set of rows in the issue list page. That is
226 * done by adding or removing a CSS class of an enclosing HTML
227 * element, and by CSS rules that trigger based on that CSS class.
228 * TODO(jrobbins): actually, this automatically hides the other groups.
229 * @param {string} rowClassName The name of the row group to toggle,
230 * corresponds to a CSS class.
231 * @return Always returns false to indicate that the browser should
232 * handle the event normally.
233 */
234function TKR_toggleRows(rowClassName) {
235 let controlDiv = $('colcontrol');
236 controlDiv.classList.add('hide_pri_groups');
237 controlDiv.classList.add('hide_mile_groups');
238 controlDiv.classList.add('hide_stat_groups');
239 TKR_toggleColumn(rowClassName);
240 return false;
241}
242
243
244/**
245 * A simple class that can manage the display of a popup menu. Instances
246 * of this class are used by popup_controller.js.
247 * @param {Element} div The div that contains the popup menu.
248 * @param {Element} opt_launcherEl The button that launched the popup menu,
249 * if any.
250 * @constructor
251 */
252function TKR_MyPopup(div, opt_launcherEl) {
253 this._div = div;
254 this._launcher = opt_launcherEl;
255 this._isVisible = false;
256}
257
258
259/**
260 * Show a popup menu. This method registers the popup with popup_controller.
261 */
262TKR_MyPopup.prototype.show = function() {
263 this._div.style.display = 'block';
264 this._isVisible = true;
265 PC_addPopup(this);
266};
267
268
269/**
270 * Show a popup menu. This method is called from the deactive method,
271 * which is called by popup_controller.
272 */
273TKR_MyPopup.prototype.hide = function() {
274 this._div.style.display = 'none';
275 this._isVisible = false;
276};
277
278
279/**
280 * When the popup_controller gets a user click, it calls deactive() on
281 * every active popup to check if the click should close that popup.
282 */
283TKR_MyPopup.prototype.deactivate = function(e) {
284 if (this._isVisible) {
285 let p = GetMousePosition(e);
286 if (nodeBounds(this._div).contains(p)) {
287 return false; // use clicked on popup, remain visible
288 } else if (this._launcher && nodeBounds(this._launcher).contains(p)) {
289 this._launcher = null;
290 return false; // mouseup element that launched menu, remain visible
291 } else {
292 this.hide();
293 return true; // clicked outside popup, make invisible
294 }
295 } else {
296 return true; // already deactivated, not visible
297 }
298};
299
300
301/**
302 * Highlight the issue row on the list page that contains the given
303 * checkbox.
304 * @param {Element} cb The checkbox that the user changed.
305 * @return Always returns false to indicate that the browser should
306 * handle the event normally.
307 */
308function TKR_highlightRow(el) {
309 let checked = el.checked;
310 while (el && el.tagName != 'TR') {
311 el = el.parentNode;
312 }
313 if (checked) {
314 el.classList.add('selected');
315 } else {
316 el.classList.remove('selected');
317 }
318 return false;
319}