Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/webroot/rsrc/js/application/repository/repository-crossreference.js
12242 views
1
/**
2
* @provides javelin-behavior-repository-crossreference
3
* @requires javelin-behavior
4
* javelin-dom
5
* javelin-stratcom
6
* javelin-uri
7
*/
8
9
JX.behavior('repository-crossreference', function(config, statics) {
10
11
var highlighted;
12
var linked = [];
13
14
function isMacOS() {
15
return (navigator.platform.indexOf('Mac') > -1);
16
}
17
18
function isHighlightModifierKey(e) {
19
var signal_key;
20
if (isMacOS()) {
21
// On macOS, use the "Command" key.
22
signal_key = 91;
23
} else {
24
// On other platforms, use the "Control" key.
25
signal_key = 17;
26
}
27
28
return (e.getRawEvent().keyCode === signal_key);
29
}
30
31
function hasHighlightModifierKey(e) {
32
if (isMacOS()) {
33
return e.getRawEvent().metaKey;
34
} else {
35
return e.getRawEvent().ctrlKey;
36
}
37
}
38
39
var classHighlight = 'crossreference-item';
40
var classMouseCursor = 'crossreference-cursor';
41
42
// TODO maybe move the dictionary part of this list to the server?
43
var class_map = {
44
nc : 'class',
45
nf : 'function',
46
na : null,
47
nb : 'builtin',
48
n : null
49
};
50
51
function link(element, lang) {
52
JX.DOM.alterClass(element, 'repository-crossreference', true);
53
linked.push(element);
54
JX.DOM.listen(
55
element,
56
['mouseover', 'mouseout', 'click'],
57
'tag:span',
58
function(e) {
59
if (e.getType() === 'mouseout') {
60
unhighlight();
61
return;
62
}
63
if (!hasHighlightModifierKey(e)) {
64
return;
65
}
66
67
var target = e.getTarget();
68
69
if (!canLinkNode(target)) {
70
return;
71
}
72
73
// If only part of the symbol was edited, the symbol name itself will
74
// have another "<span />" inside of it which highlights only the
75
// edited part. Skip over it.
76
if (JX.DOM.isNode(target, 'span') && (target.className === 'bright')) {
77
target = target.parentNode;
78
}
79
80
if (e.getType() === 'mouseover') {
81
while (target && target !== document.body) {
82
if (JX.DOM.isNode(target, 'span') &&
83
(target.className in class_map)) {
84
highlighted = target;
85
JX.DOM.alterClass(highlighted, classHighlight, true);
86
break;
87
}
88
target = target.parentNode;
89
}
90
} else if (e.getType() === 'click') {
91
openSearch(target, {lang: lang});
92
}
93
});
94
}
95
96
function canLinkNode(node) {
97
try {
98
// If we're in an inline comment, don't link symbols.
99
if (JX.DOM.findAbove(node, 'div', 'differential-inline-comment')) {
100
return false;
101
}
102
} catch (ex) {
103
// Continue if we're not inside an inline comment.
104
}
105
106
// See T13644. Don't open symbols if we're inside a changeset header.
107
try {
108
if (JX.DOM.findAbove(node, 'h1')) {
109
return false;
110
}
111
} catch (ex) {
112
// Continue if not inside a header.
113
}
114
115
return true;
116
}
117
118
function unhighlight() {
119
highlighted && JX.DOM.alterClass(highlighted, classHighlight, false);
120
highlighted = null;
121
}
122
123
function openSearch(target, context) {
124
var symbol = target.textContent || target.innerText;
125
126
context = context || {};
127
context.lang = context.lang || null;
128
context.repositories =
129
context.repositories ||
130
(config && config.repositories) ||
131
[];
132
133
var query = JX.copy({}, context);
134
if (query.repositories.length) {
135
query.repositories = query.repositories.join(',');
136
} else {
137
delete query.repositories;
138
}
139
query.jump = true;
140
141
var c = target.className;
142
c = c.replace(classHighlight, '').trim();
143
144
if (class_map[c]) {
145
query.type = class_map[c];
146
}
147
148
if (target.hasAttribute('data-symbol-context')) {
149
query.context = target.getAttribute('data-symbol-context');
150
}
151
152
if (target.hasAttribute('data-symbol-name')) {
153
symbol = target.getAttribute('data-symbol-name');
154
}
155
156
var line = getLineNumber(target);
157
if (line !== null) {
158
query.line = line;
159
}
160
161
if (!query.hasOwnProperty('path')) {
162
var path = getPath(target);
163
if (path !== null) {
164
query.path = path;
165
}
166
}
167
168
var char = getChar(target);
169
if (char !== null) {
170
query.char = char;
171
}
172
173
var uri_symbol = symbol;
174
175
// In some cases, lexers may include whitespace in symbol tags. Trim it,
176
// since symbols with semantic whitespace aren't supported.
177
uri_symbol = uri_symbol.trim();
178
179
// See T13437. Symbols like "#define" need to be encoded.
180
// See T13644. Symbols like "a/b" must be double-encoded to survive
181
// one layer of decoding by the webserver.
182
uri_symbol = encodeURIComponent(uri_symbol);
183
uri_symbol = encodeURIComponent(uri_symbol);
184
185
var uri = JX.$U('/diffusion/symbol/' + uri_symbol + '/');
186
uri.addQueryParams(query);
187
188
window.open(uri.toString());
189
}
190
191
function linkAll() {
192
var blocks = JX.DOM.scry(document.body, 'div', 'remarkup-code-block');
193
for (var i = 0; i < blocks.length; ++i) {
194
if (blocks[i].hasAttribute('data-code-lang')) {
195
var lang = blocks[i].getAttribute('data-code-lang');
196
link(blocks[i], lang);
197
}
198
}
199
}
200
201
function getLineNumber(target) {
202
203
// Figure out the line number by finding the most recent "<th />" in this
204
// row with a number in it. We may need to skip over one "<th />" if the
205
// diff is being displayed in unified mode.
206
207
var cell = JX.DOM.findAbove(target, 'td');
208
if (!cell) {
209
return null;
210
}
211
212
var row = JX.DOM.findAbove(target, 'tr');
213
if (!row) {
214
return null;
215
}
216
217
var ii;
218
219
var cell_list = [];
220
for (ii = 0; ii < row.childNodes.length; ii++) {
221
cell_list.push(row.childNodes[ii]);
222
}
223
cell_list.reverse();
224
225
var found = false;
226
for (ii = 0; ii < cell_list.length; ii++) {
227
if (cell_list[ii] === cell) {
228
found = true;
229
}
230
231
if (found && JX.DOM.isType(cell_list[ii], 'th')) {
232
var int_value = parseInt(cell_list[ii].textContent, 10);
233
if (int_value) {
234
return int_value;
235
}
236
}
237
}
238
239
return null;
240
}
241
242
function getPath(target) {
243
// This method works in Differential, when browsing a changeset.
244
var changeset;
245
try {
246
changeset = JX.DOM.findAbove(target, 'div', 'differential-changeset');
247
return JX.Stratcom.getData(changeset).symbolPath || null;
248
} catch (ex) {
249
// Ignore.
250
}
251
252
return null;
253
}
254
255
function getChar(target) {
256
var cell = JX.DOM.findAbove(target, 'td');
257
if (!cell) {
258
return null;
259
}
260
261
var char = 1;
262
for (var ii = 0; ii < cell.childNodes.length; ii++) {
263
var node = cell.childNodes[ii];
264
265
if (node === target) {
266
return char;
267
}
268
269
var content = '' + node.textContent;
270
char += content.length;
271
}
272
273
return null;
274
}
275
276
JX.Stratcom.listen(
277
'differential-preview-update',
278
null,
279
function(e) {
280
linkAll(e.getData().container);
281
});
282
283
284
JX.Stratcom.listen(
285
['keydown', 'keyup'],
286
null,
287
function(e) {
288
if (!isHighlightModifierKey(e)) {
289
return;
290
}
291
292
setCursorMode(e.getType() === 'keydown');
293
294
if (!statics.active) {
295
unhighlight();
296
}
297
});
298
299
JX.Stratcom.listen(
300
'blur',
301
null,
302
function(e) {
303
if (e.getTarget()) {
304
return;
305
}
306
unhighlight();
307
setCursorMode(false);
308
});
309
310
function setCursorMode(active) {
311
statics.active = active;
312
linked.forEach(function(element) {
313
JX.DOM.alterClass(element, classMouseCursor, statics.active);
314
});
315
}
316
317
318
if (config && config.container) {
319
link(JX.$(config.container), config.lang);
320
}
321
322
JX.Stratcom.listen(
323
['mouseover', 'mouseout', 'click'],
324
['has-symbols', 'tag:span'],
325
function(e) {
326
var type = e.getType();
327
328
if (type === 'mouseout') {
329
unhighlight();
330
return;
331
}
332
333
if (!hasHighlightModifierKey(e)) {
334
return;
335
}
336
337
var target = e.getTarget();
338
339
if (!canLinkNode(target)) {
340
return;
341
}
342
343
// If only part of the symbol was edited, the symbol name itself will
344
// have another "<span />" inside of it which highlights only the
345
// edited part. Skip over it.
346
if (JX.DOM.isNode(target, 'span') && (target.className === 'bright')) {
347
target = target.parentNode;
348
}
349
350
if (type === 'click') {
351
openSearch(target, e.getNodeData('has-symbols').symbols);
352
e.kill();
353
return;
354
}
355
356
if (e.getType() === 'mouseover') {
357
while (target && target !== document.body) {
358
if (!JX.DOM.isNode(target, 'span')) {
359
target = target.parentNode;
360
continue;
361
}
362
363
if (!class_map.hasOwnProperty(target.className)) {
364
target = target.parentNode;
365
continue;
366
}
367
368
highlighted = target;
369
JX.DOM.alterClass(highlighted, classHighlight, true);
370
break;
371
}
372
}
373
});
374
375
});
376
377