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