1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
(function(){
function run() {
var code = document.querySelector('.blob-content code');
if (code && typeof hljs !== 'undefined') hljs.highlightElement(code);
applyBlobLineSelection();
}
function parseLinesParam() {
var params = new URLSearchParams(window.location.search);
var s = params.get('lines');
if (!s) return [];
var re = /L(\d+)(?:C(\d+))?(?:-L(\d+)(?:C(\d+))?)?/g;
var ranges = [];
var m;
while ((m = re.exec(s)) !== null) {
var startLine = parseInt(m[1], 10);
var startCol = m[2] ? parseInt(m[2], 10) : null;
var endLine = m[3] !== undefined ? parseInt(m[3], 10) : null;
var endCol = m[4] ? parseInt(m[4], 10) : null;
ranges.push({ startLine: startLine, startCol: startCol, endLine: endLine, endCol: endCol });
}
return ranges;
}
function wrapCodeInLineSpans(code) {
var lines = [];
var current = [];
function flushLine() {
if (current.length) {
lines.push(current);
current = [];
}
}
var nodes = Array.from(code.childNodes);
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node.nodeType === Node.TEXT_NODE) {
var parts = node.textContent.split('\n');
for (var j = 0; j < parts.length; j++) {
if (j > 0) flushLine();
current.push(parts[j]);
}
} else {
current.push(node);
}
}
flushLine();
code.replaceChildren();
for (var i = 0; i < lines.length; i++) {
var span = document.createElement('span');
span.setAttribute('data-line', String(i + 1));
for (var k = 0; k < lines[i].length; k++) {
var item = lines[i][k];
if (typeof item === 'string') {
span.appendChild(document.createTextNode(item));
} else {
span.appendChild(item);
}
}
code.appendChild(span);
if (i < lines.length - 1) code.appendChild(document.createTextNode('\n'));
}
return code.querySelectorAll('[data-line]');
}
function wrapCharacterRangeInSpan(lineSpan, startChar, endChar) {
startChar = Math.max(0, startChar);
var len = lineSpan.textContent.length;
endChar = endChar == null ? len : Math.min(len, endChar);
if (startChar >= endChar) return;
var walker = document.createTreeWalker(lineSpan, NodeFilter.SHOW_TEXT);
var charIdx = 0;
var startNode = null, startOff = 0, endNode = null, endOff = 0;
var n;
while ((n = walker.nextNode())) {
var nLen = n.length;
if (startNode == null && charIdx + nLen > startChar) {
startNode = n;
startOff = startChar - charIdx;
}
if (endNode == null && charIdx + nLen >= endChar) {
endNode = n;
endOff = endChar - charIdx;
break;
}
charIdx += nLen;
}
if (!startNode || !endNode) return;
try {
var range = document.createRange();
range.setStart(startNode, startOff);
range.setEnd(endNode, endOff);
var span = document.createElement('span');
span.className = 'blob-selection';
range.surroundContents(span);
} catch (e) {}
}
function applyBlobLineSelection() {
var code = document.querySelector('.blob-content code');
if (!code) return;
var ranges = parseLinesParam();
if (!ranges.length) return;
var lineSpans = wrapCodeInLineSpans(code);
var firstTarget = null;
for (var r = 0; r < ranges.length; r++) {
var range = ranges[r];
var startLine = range.startLine;
var startCol = range.startCol;
var endLine = range.endLine != null ? range.endLine : startLine;
var endCol = range.endCol;
for (var i = startLine; i <= endLine; i++) {
var span = code.querySelector('[data-line="' + i + '"]');
if (!span) continue;
if (firstTarget === null) firstTarget = span;
var lineLen = span.textContent.length;
var startCh = (i === startLine && startCol != null) ? startCol - 1 : 0;
var endCh = (i === endLine && endCol != null) ? endCol : lineLen;
if (startCh === 0 && endCh >= lineLen) {
span.classList.add('blob-line-selected');
} else {
wrapCharacterRangeInSpan(span, startCh, endCh);
}
}
}
if (firstTarget) {
firstTarget.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
}
}
run();
})();