1 | (function($){ |
---|
2 | $(document).delegate('textarea', 'keydown', function(e) { |
---|
3 | var keyCode = e.keyCode || e.which; |
---|
4 | if (keyCode != 9 && keyCode != 13) return; |
---|
5 | |
---|
6 | var textbox = this; |
---|
7 | var all = textbox.value; |
---|
8 | var start = textbox.selectionStart, end = textbox.selectionEnd; |
---|
9 | |
---|
10 | function startsWith(s, sub) { |
---|
11 | if (typeof String.prototype.startsWith == 'function') return s.startsWith(sub); |
---|
12 | return s.lastIndexOf(sub, 0) === 0; |
---|
13 | } |
---|
14 | |
---|
15 | function indentLines(delta) { |
---|
16 | var indent = Array(Math.abs(delta) + 1).join(' '); |
---|
17 | var firstLinePos = Math.max(0, all.lastIndexOf("\n", start - 1) + 1); |
---|
18 | var afterLastLinePos = all.indexOf("\n", end); |
---|
19 | if (afterLastLinePos == -1) afterLastLinePos = all.length; |
---|
20 | |
---|
21 | var newStart = start; |
---|
22 | var lines = all.slice(firstLinePos, afterLastLinePos).split("\n"); |
---|
23 | for (var i = 0; i < lines.length; i++) { |
---|
24 | if (delta > 0) { |
---|
25 | lines[i] = indent + lines[i]; |
---|
26 | if (i == 0) newStart = start + delta; |
---|
27 | } |
---|
28 | if (delta < 0 && startsWith(lines[i], indent)) { |
---|
29 | lines[i] = lines[i].substring(-delta); |
---|
30 | if (i == 0) newStart = Math.max(firstLinePos, start + delta); |
---|
31 | } |
---|
32 | } |
---|
33 | textbox.value = all.substring(0, firstLinePos) + lines.join("\n") + all.substring(afterLastLinePos); |
---|
34 | textbox.selectionStart = newStart; |
---|
35 | textbox.selectionEnd = textbox.value.length - (all.length - end); |
---|
36 | }; |
---|
37 | |
---|
38 | if (keyCode == 9 && !e.shiftKey) { // TAB |
---|
39 | e.preventDefault(); |
---|
40 | indentLines(2); |
---|
41 | } |
---|
42 | if (keyCode == 9 && e.shiftKey) { // SHIFT-TAB |
---|
43 | e.preventDefault(); |
---|
44 | indentLines(-2); |
---|
45 | } |
---|
46 | if (keyCode == 13) { // ENTER |
---|
47 | if (start != end) return; |
---|
48 | |
---|
49 | var firstLinePos = Math.max(0, all.lastIndexOf("\n", start - 1) + 1); |
---|
50 | var afterLastLinePos = all.indexOf("\n", end); |
---|
51 | if (afterLastLinePos == -1) afterLastLinePos = all.length; |
---|
52 | var line = all.slice(firstLinePos, afterLastLinePos); |
---|
53 | |
---|
54 | var match = /^( *(?:[*-]|(?:\d\.)) ).*[^ ].*$/.exec(line); |
---|
55 | if (!match) return; |
---|
56 | if (start < firstLinePos + match[1].length - 1) return; |
---|
57 | e.preventDefault(); |
---|
58 | |
---|
59 | textbox.value = all.substring(0, start) + "\n" + match[1] + all.substring(end); |
---|
60 | textbox.selectionStart = textbox.selectionEnd = start + 1 + match[1].length; |
---|
61 | } |
---|
62 | |
---|
63 | }); |
---|
64 | $(document).delegate('textarea', 'paste', function(e) { |
---|
65 | |
---|
66 | let text_to_insert = (e.originalEvent.clipboardData || window.clipboardData).getData('text'); |
---|
67 | |
---|
68 | const data = textareakeybindings; |
---|
69 | const pattern_re = new RegExp(data.baseurl_pattern); |
---|
70 | text_to_insert = text_to_insert.replace(pattern_re, function(match, realm, rest, offset, string) { |
---|
71 | if (realm == 'wiki' || |
---|
72 | realm == 'changeset') { |
---|
73 | return realm + ':'+ rest; |
---|
74 | } |
---|
75 | if (realm == 'ticket') { |
---|
76 | if (rest.includes('#comment:')) { |
---|
77 | const t = rest.split('#comment:'); |
---|
78 | return 'comment:' + t[1] + ':ticket:' + t[0]; |
---|
79 | } |
---|
80 | return '#' + rest; |
---|
81 | } |
---|
82 | if (realm == 'browser') { |
---|
83 | const url = new URL(match); |
---|
84 | if (url.searchParams.has('rev')) { |
---|
85 | const rev = url.searchParams.get('rev'); |
---|
86 | url.searchParams.delete('rev'); |
---|
87 | url.pathname = url.pathname + '@' + rev; |
---|
88 | } |
---|
89 | if (url.searchParams.has('marks')) { |
---|
90 | const marks = url.searchParams.get('marks'); |
---|
91 | url.searchParams.delete('marks'); |
---|
92 | url.pathname = url.pathname + ':' + marks; |
---|
93 | } |
---|
94 | return 'source:' + url.href.replace(pattern_re, '$2') |
---|
95 | } |
---|
96 | if (realm == 'log') { |
---|
97 | const url = new URL(match); |
---|
98 | if (url.searchParams.has('rev')) { |
---|
99 | const rev = url.searchParams.get('rev'); |
---|
100 | url.searchParams.delete('rev'); |
---|
101 | url.pathname = url.pathname + '@' + rev; |
---|
102 | } |
---|
103 | return 'log:' + url.href.replace(pattern_re, '$2') |
---|
104 | } |
---|
105 | if (realm == 'attachment') { |
---|
106 | return realm + ':'+ rest.replace('/', ':').replace(/\/([^/]*)$/, ':$1'); |
---|
107 | } |
---|
108 | return match; |
---|
109 | }); |
---|
110 | |
---|
111 | Object.keys(data.links).forEach(key => { |
---|
112 | const url_re = data.links[key]; |
---|
113 | text_to_insert = text_to_insert.replaceAll(new RegExp(url_re, 'g'), key); |
---|
114 | }); |
---|
115 | |
---|
116 | const start = e.target.selectionStart; |
---|
117 | e.target.value = e.target.value.slice(0, start) + text_to_insert + e.target.value.slice(e.target.selectionEnd); |
---|
118 | e.target.selectionStart = start + text_to_insert.length; |
---|
119 | e.target.selectionEnd = start + text_to_insert.length; |
---|
120 | event.preventDefault(); |
---|
121 | }); |
---|
122 | })(jQuery); |
---|