source: textareakeybindingsplugin/trunk/textareakeybindings/htdocs/js/textareakeybindings.js @ 17776

Last change on this file since 17776 was 17776, checked in by lucid, 4 years ago

TextareaKeyBindingsPlugin: Transform pasted links to TracLinks syntax. (fix #13831)

File size: 5.2 KB
Line 
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);
Note: See TracBrowser for help on using the repository browser.