blob: 040f8c1357206685665f1fd3e48e690a7d13c269 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001/* 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 * This file contains JS utilities used by other JS files in Monorail.
10 */
11
12
13/**
14 * Add an indexOf method to all arrays, if this brower's JS implementation
15 * does not already have it.
16 * @param {Object} item The item to find
17 * @return {number} The index of the given item, or -1 if not found.
18 */
19if (Array.prototype.indexOf == undefined) {
20 Array.prototype.indexOf = function(item) {
21 for (let i = 0; i < this.length; ++i) {
22 if (this[i] == item) return i;
23 }
24 return -1;
25 };
26}
27
28
29/**
30 * This function works around a FF HTML layout problem. The table
31 * width is somehow rendered at 100% when the table contains a
32 * display:none element, later, when that element is displayed, the
33 * table renders at the correct width. The work-around is to have the
34 * element initiallye displayed so that the table renders properly,
35 * but then immediately hide the element until it is needed.
36 *
37 * TODO(jrobbins): Find HTML markup that FF can render more
38 * consistently. After that, I can remove this hack.
39 */
40function TKR_forceProperTableWidth() {
41 let e = $('confirmarea');
42 if (e) e.style.display='none';
43}
44
45
46function TKR_parseIssueRef(issueRef) {
47 issueRef = issueRef.trim();
48 if (!issueRef) {
49 return null;
50 }
51
52 let projectName = window.CS_env.projectName;
53 let localId = issueRef;
54 if (issueRef.includes(':')) {
55 const parts = issueRef.split(':', 2);
56 projectName = parts[0];
57 localId = parts[1];
58 }
59
60 return {
61 project_name: projectName,
62 local_id: localId};
63}
64
65
66function _buildFieldsForIssueDelta(issueDelta, valuesByName) {
67 issueDelta.field_vals_add = [];
68 issueDelta.field_vals_remove = [];
69 issueDelta.fields_clear = [];
70
71 valuesByName.forEach((values, key, map) => {
72 if (key.startsWith('op_custom_') && values == 'clear') {
73 const field_id = key.substring('op_custom_'.length);
74 issueDelta.fields_clear.push({field_id: field_id});
75 } else if (key.startsWith('custom_')) {
76 const field_id = key.substring('custom_'.length);
77 values = values.filter(Boolean);
78 if (valuesByName.get('op_' + key) === 'remove') {
79 values.forEach((value) => {
80 issueDelta.field_vals_remove.push({
81 field_ref: {field_id: field_id},
82 value: value});
83 });
84 } else {
85 values.forEach((value) => {
86 issueDelta.field_vals_add.push({
87 field_ref: {field_id: field_id},
88 value: value});
89 });
90 }
91 }
92 });
93}
94
95
96function _classifyPlusMinusItems(values) {
97 let result = {
98 add: [],
99 remove: []};
100 values = new Set(values);
101 values.forEach((value) => {
102 if (!value.startsWith('-') && value) {
103 result.add.push(value);
104 } else if (value.startsWith('-') && value.substring(1)) {
105 result.remove.push(value);
106 }
107 });
108 return result;
109}
110
111
112function TKR_buildIssueDelta(valuesByName) {
113 let issueDelta = {};
114
115 if (valuesByName.has('status')) {
116 issueDelta.status = valuesByName.get('status')[0];
117 }
118 if (valuesByName.has('owner')) {
119 issueDelta.owner_ref = {
120 display_name: valuesByName.get('owner')[0].trim().toLowerCase()};
121 }
122 if (valuesByName.has('cc')) {
123 const cc_usernames = _classifyPlusMinusItems(
124 valuesByName.get('cc')[0].toLowerCase().split(/[,;\s]+/));
125 issueDelta.cc_refs_add = cc_usernames.add.map(
126 (email) => ({display_name: email}));
127 issueDelta.cc_refs_remove = cc_usernames.remove.map(
128 (email) => ({display_name: email}));
129 }
130 if (valuesByName.has('components')) {
131 const components = _classifyPlusMinusItems(
132 valuesByName.get('components')[0].split(/[,;\s]/));
133 issueDelta.comp_refs_add = components.add.map(
134 (path) => ({path: path}));
135 issueDelta.comp_refs_remove = components.remove.map(
136 (path) => ({path: path}));
137 }
138 if (valuesByName.has('label')) {
139 const labels = _classifyPlusMinusItems(valuesByName.get('label'));
140 issueDelta.label_refs_add = labels.add.map(
141 (label) => ({label: label}));
142 issueDelta.label_refs_remove = labels.remove.map(
143 (label) => ({label: label}));
144 }
145 if (valuesByName.has('blocked_on')) {
146 const blockedOn = _classifyPlusMinusItems(valuesByName.get('blocked_on'));
147 issueDelta.blocked_on_refs_add = blockedOn.add.map(TKR_parseIssueRef);
148 issueDelta.blocked_on_refs_add = blockedOn.remove.map(TKR_parseIssueRef);
149 }
150 if (valuesByName.has('blocking')) {
151 const blocking = _classifyPlusMinusItems(valuesByName.get('blocking'));
152 issueDelta.blocking_refs_add = blocking.add.map(TKR_parseIssueRef);
153 issueDelta.blocking_refs_add = blocking.remove.map(TKR_parseIssueRef);
154 }
155 if (valuesByName.has('merge_into')) {
156 issueDelta.merged_into_ref = TKR_parseIssueRef(
157 valuesByName.get('merge_into')[0]);
158 }
159 if (valuesByName.has('summary')) {
160 issueDelta.summary = valuesByName.get('summary')[0];
161 }
162
163 _buildFieldsForIssueDelta(issueDelta, valuesByName);
164
165 return issueDelta;
166}