1jQuery(function () {
2    jQuery('#wiki__text').on('paste', function (event) {
3
4        let isMediaLink = false;
5
6        /**
7         * Decodes HTML entities
8         *
9         * @param input
10         * @returns {string}
11         */
12        function htmlDecode(input) {
13            const doc = new DOMParser().parseFromString(input, "text/html");
14            return doc.documentElement.textContent;
15        }
16
17        /**
18         * Checks all interwiki patterns to reverse engineer
19         * an interwiki shortcut from pasted text.
20         * Returns false if nothing matches.
21         *
22         * @param url
23         * @returns {string|boolean}
24         */
25        function getIwl(url) {
26            const patterns = JSON.parse(JSINFO.plugins.interwikipaste.patterns);
27            for (let i = 0; i < patterns.length; i++) {
28                let patternConf = patterns[i];
29                let regex = new RegExp(patternConf.pattern, 'g');
30                let matched = regex.exec(url);
31                if (matched !== null) {
32                    let captured = matched[1] ? matched[1] : '';
33                    if (patternConf.encode) {
34                        captured = htmlDecode(captured);
35                    }
36                    return `${patternConf.shortcut}>${captured}`;
37                }
38            }
39            return false;
40        }
41
42        /**
43         * Possible local links:
44         *  - page id without URL rewriting http://example.doku/doku.php?id=test:start
45         *  - page id without URL rewriting http://example.doku/doku.php?id=test:plugins#interwikipaste
46         *  - page id with .htaccess URL rewriting http://example.doku/test:plugins
47         *  - page id with .htaccess URL rewriting and 'useslash' config http://example.doku/test/plugins
48         *  - page id with internal URL rewriting http://example.doku/doku.php/test:plugins
49         *  - http://example.doku/lib/exe/detail.php?id=test%3Aplugins&media=ns:image.jpg
50         *  - http://example.doku/lib/exe/fetch.php?w=400&tok=097122&media=ns:image.jpg
51         *  - http://example.doku/lib/exe/fetch.php?media=test:file.pdf
52         *  - http://example.doku/_detail/ns:image.jpg?id=test%3Aplugins
53         *  - http://example.doku/_media/test:file.pdf
54         *  - http://example.doku/_detail/ns/image.jpg?id=test%3Aplugins
55         *  - http://example.doku/_media/test/file.pdf
56         *
57         * @param pasted
58         */
59        function getLocal(pasted) {
60            const url = new URL(pasted);
61            const path = url.pathname.concat(url.hash);
62            const href = url.href;
63
64            // no URL rewriting
65            if (path.indexOf('/doku.php') === 0 && url.search.indexOf('?') === 0) {
66                const idMatch = new RegExp('(?:id=)([^&]+)');
67                const matches = idMatch.exec(href);
68                if (matches[1]) {
69                    return normalizeId(matches[1]);
70                }
71            } else if (path.indexOf('/doku.php/') === 0) {
72                // page with internal URL rewriting
73                const idMatch = /(?:\/doku.php\/)([^&\?]+)/;
74                const matches = path.match(idMatch);
75                if (matches[1]) {
76                    return normalizeId(matches[1]);
77                }
78            } else if (path.indexOf('/lib/exe/detail.php/') === 0 || path.indexOf('/lib/exe/fetch.php/') === 0) {
79                // media with internal rewriting
80                isMediaLink = true;
81                const mediaIdMatch = new RegExp(
82                    '(?:\\/lib\\/exe\\/detail.php\\/|\\/lib\\/exe\\/fetch.php\\/)([^&]+)$'
83                );
84                const matches = mediaIdMatch.exec(path);
85                if (matches[1]) {
86                    return normalizeId(matches[1]);
87                }
88            } else if (path.indexOf('/lib/exe/detail.php') === 0 || path.indexOf('/lib/exe/fetch.php') === 0) {
89                // media without rewriting
90                isMediaLink = true;
91                const mediaIdMatch = new RegExp('(?:media=)([^&]+)');
92                const matches = mediaIdMatch.exec(href);
93                if (matches[1]) {
94                    return normalizeId(matches[1]);
95                }
96            } else if (path.indexOf('/_media/') === 0) { // media with .htaccess rewriting
97                isMediaLink = true;
98                const mediaIdMatch = /(?:_media\/)([^&\?]+)/;
99                const matches = href.match(mediaIdMatch);
100                if (matches[1]) {
101                    return normalizeId(matches[1]);
102                }
103            } else if (path.indexOf('/_detail/') === 0) { // media with .htaccess rewriting
104                isMediaLink = true;
105                const mediaIdMatch = /(?:_detail\/)([^&\?]+)/;
106                const matches = href.match(mediaIdMatch);
107                if (matches[1]) {
108                    return normalizeId(matches[1]);
109                }
110            } else {
111                // page with .htaccess URL rewriting
112                const idMatch = /(?:\/)([^&\?]+)/;
113                const matches = path.match(idMatch);
114                if (matches && matches[1]) {
115                    return normalizeId(matches[1]);
116                }
117            }
118            return false;
119        }
120
121        function normalizeId(id) {
122            return ':' + id.replace(/\//g, ":");
123        }
124
125        const $editor = jQuery(this);
126        const currentSelection = DWgetSelection($editor[0]);
127        const selected = currentSelection.getText();
128        const pasted = event.originalEvent.clipboardData.getData('text');
129        let result;
130
131        // if not a URL, let the browser handle it
132        if (pasted.search(/^http[^ ]+$/) === -1) {
133            return;
134        }
135        // first check for internal link
136        if (pasted.indexOf(window.location.origin) === 0) {
137            result = getLocal(pasted);
138        } else {
139            // next try interwiki links
140            result = getIwl(pasted);
141        }
142
143        if (result) {
144            event.preventDefault();
145            const openSyntax = isMediaLink ? '{{' : '[[';
146            const closeSyntax = isMediaLink ? '?linkonly|}}' : ']]';
147
148            // if some text is selected we assume it is the link title
149            if (selected) {
150                result = `${openSyntax}${result}|${selected}${closeSyntax}`;
151            } else {
152                // check current position for surrounding link syntax
153                const allInput = $editor.val();
154                const caretPos = currentSelection.start;
155
156                // check for opening brackets before
157                const regBefore = new RegExp('\\[\\[ *');
158                const linkOpened = regBefore.exec(allInput.substring(caretPos, caretPos - 5));
159
160                // check for closing brackets (before opening ones) on the same line
161                const textAfter = allInput.substring(caretPos);
162                const nl = /\n/;
163                const eol = textAfter.search(nl);
164
165                const linkClosed = textAfter.substring(0, eol).indexOf(']]') > textAfter.substring(0, eol).indexOf('[[');
166
167                if (!linkOpened) {
168                    result = openSyntax + result;
169                }
170                if (!linkClosed) {
171                    result = result + closeSyntax;
172                }
173            }
174            pasteText(currentSelection, result, {nosel: true});
175        }
176    });
177});
178