blob: 69a4949d5f94d4b67f5406316d7f6ff3830360d6 [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 * This file contains JS functions that implement keystroke accelerators
7 * for Monorail.
8 */
9
10/**
11 * Array of HTML elements where the kibbles cursor can be. E.g.,
12 * the TR elements of an issue list, or the TR's for comments on an issue.
13 */
14let TKR_cursorStops;
15
16/**
17 * Integer index into TKR_cursorStops of the currently selected cursor
18 * stop, or undefined if nothing has been selected yet.
19 */
20let TKR_selected = undefined;
21
22/**
23 * Register keystrokes that apply to all pages in the current component.
24 * E.g., keystrokes that should work on every page under the "Issues" tab.
25 * @param {string} listUrl Rooted URL of the artifact list.
26 * @param {string} entryUrl Rooted URL of the artifact entry page.
27 * @param {string} currentPageType One of 'list', 'entry', or 'detail'.
28 */
29function TKR_setupKibblesComponentKeys(listUrl, entryUrl, currentPageType) {
30 if (currentPageType != 'list') {
31 kibbles.keys.addKeyPressListener(
32 'u', function() {
33 TKR_go(listUrl);
34 });
35 }
36}
37
38
39/**
40 * On the artifact list page, go to the artifact at the kibbles cursor.
41 * @param {number} linkCellIndex row child that is expected to hold a link.
42 */
43function TKR_openArtifactAtCursor(linkCellIndex, newWindow) {
44 if (TKR_selected >= 0 && TKR_selected < TKR_cursorStops.length) {
45 window._goIssue(TKR_selected, newWindow);
46 }
47}
48
49
50/**
51 * On the artifact list page, toggle the checkbox for the artifact at
52 * the kibbles cursor.
53 * @param {number} cbCellIndex row child that is expected to hold a checkbox.
54 */
55function TKR_selectArtifactAtCursor(cbCellIndex) {
56 if (TKR_selected >= 0 && TKR_selected < TKR_cursorStops.length) {
57 const cell = TKR_cursorStops[TKR_selected].children[cbCellIndex];
58 let cb = cell.firstChild;
59 while (cb && cb.tagName != 'INPUT') {
60 cb = cb.nextSibling;
61 }
62 if (cb) {
63 cb.checked = cb.checked ? '' : 'checked';
64 TKR_highlightRow(cb);
65 }
66 }
67}
68
69/**
70 * On the artifact list page, toggle the star for the artifact at
71 * the kibbles cursor.
72 * @param {number} cbCellIndex row child that is expected to hold a checkbox
73 * and star widget.
74 */
75function TKR_toggleStarArtifactAtCursor(cbCellIndex) {
76 if (TKR_selected >= 0 && TKR_selected < TKR_cursorStops.length) {
77 const cell = TKR_cursorStops[TKR_selected].children[cbCellIndex];
78 let starIcon = cell.firstChild;
79 while (starIcon && starIcon.tagName != 'A') {
80 starIcon = starIcon.nextSibling;
81 }
82 if (starIcon) {
83 _TKR_toggleStar(
84 starIcon, issueRefs[TKR_selected]['project_name'],
85 issueRefs[TKR_selected]['id'], null, null);
86 }
87 }
88}
89
90/**
91 * Updates the style on new stop and clears the style on the former stop.
92 * @param {Object} newStop the cursor stop that the user is selecting now.
93 * @param {Object} formerStop the old cursor stop, if any.
94 */
95function TKR_updateCursor(newStop, formerStop) {
96 TKR_selected = undefined;
97 if (formerStop) {
98 formerStop.element.classList.remove('cursor_on');
99 formerStop.element.classList.add('cursor_off');
100 }
101 if (newStop && newStop.element) {
102 newStop.element.classList.remove('cursor_off');
103 newStop.element.classList.add('cursor_on');
104 TKR_selected = newStop.index;
105 }
106}
107
108
109/**
110 * Walk part of the page DOM to find elements that should be kibbles
111 * cursor stops. E.g., the rows of the issue list results table.
112 * @return {Array} an array of html elements.
113 */
114function TKR_findCursorRows() {
115 const rows = [];
116 const cursorarea = document.getElementById('cursorarea');
117 TKR_accumulateCursorRows(cursorarea, rows);
118 return rows;
119}
120
121
122/**
123 * Recusrively walk part of the page DOM to find elements that should
124 * be kibbles cursor stops. E.g., the rows of the issue list results
125 * table. The cursor stops are appended to the given rows array.
126 * @param {Element} parent html element to start on.
127 * @param {Array} rows array of html TR or DIV elements, each cursor stop will
128 * be added to this array.
129 */
130function TKR_accumulateCursorRows(parent, rows) {
131 for (let i = 0; i < parent.childNodes.length; i++) {
132 const elem = parent.childNodes[i];
133 const name = elem.tagName;
134 if (name && (name == 'TR' || name == 'DIV')) {
135 if (elem.className.indexOf('cursor') >= 0) {
136 elem.cursorIndex = rows.length;
137 rows.push(elem);
138 }
139 }
140 TKR_accumulateCursorRows(elem, rows);
141 }
142}
143
144
145/**
146 * Initialize kibbles cursors stops for the current page.
147 * @param {boolean} selectFirstStop True if the first stop should be
148 * selected before the user presses any keys.
149 */
150function TKR_setupKibblesCursorStops(selectFirstStop) {
151 kibbles.skipper.addStopListener(
152 kibbles.skipper.LISTENER_TYPE.PRE, TKR_updateCursor);
153
154 // Set the 'offset' option to return the middle of the client area
155 // an option can be a static value, or a callback
156 kibbles.skipper.setOption('padding_top', 50);
157
158 // Set the 'offset' option to return the middle of the client area
159 // an option can be a static value, or a callback
160 kibbles.skipper.setOption('padding_bottom', 50);
161
162 // register our stops with skipper
163 TKR_cursorStops = TKR_findCursorRows();
164 for (let i = 0; i < TKR_cursorStops.length; i++) {
165 const element = TKR_cursorStops[i];
166 kibbles.skipper.append(element);
167
168 if (element.className.indexOf('cursor_on') >= 0) {
169 kibbles.skipper.setCurrentStop(i);
170 }
171 }
172}
173
174
175/**
176 * Initialize kibbles keystrokes for an artifact entry page.
177 * @param {string} listUrl Rooted URL of the artifact list.
178 * @param {string} entryUrl Rooted URL of the artifact entry page.
179 */
180function TKR_setupKibblesOnEntryPage(listUrl, entryUrl) {
181 TKR_setupKibblesComponentKeys(listUrl, entryUrl, 'entry');
182}
183
184
185/**
186 * Initialize kibbles keystrokes for an artifact list page.
187 * @param {string} listUrl Rooted URL of the artifact list.
188 * @param {string} entryUrl Rooted URL of the artifact entry page.
189 * @param {string} projectName Name of the current project.
190 * @param {number} linkCellIndex table column that is expected to
191 * link to individual artifacts.
192 * @param {number} opt_checkboxCellIndex table column that is expected
193 * to contain a selection checkbox.
194 */
195function TKR_setupKibblesOnListPage(
196 listUrl, entryUrl, projectName, linkCellIndex,
197 opt_checkboxCellIndex) {
198 TKR_setupKibblesCursorStops(true);
199
200 kibbles.skipper.addFwdKey('j');
201 kibbles.skipper.addRevKey('k');
202
203 if (opt_checkboxCellIndex != undefined) {
204 const cbCellIndex = opt_checkboxCellIndex;
205 kibbles.keys.addKeyPressListener(
206 'x', function() {
207 TKR_selectArtifactAtCursor(cbCellIndex);
208 });
209 kibbles.keys.addKeyPressListener(
210 's',
211 function() {
212 TKR_toggleStarArtifactAtCursor(cbCellIndex);
213 });
214 }
215 kibbles.keys.addKeyPressListener(
216 'o', function() {
217 TKR_openArtifactAtCursor(linkCellIndex, false);
218 });
219 kibbles.keys.addKeyPressListener(
220 'O', function() {
221 TKR_openArtifactAtCursor(linkCellIndex, true);
222 });
223 kibbles.keys.addKeyPressListener(
224 'enter', function() {
225 TKR_openArtifactAtCursor(linkCellIndex);
226 });
227
228 TKR_setupKibblesComponentKeys(listUrl, entryUrl, 'list');
229}