1(function () { 2 function escapeRegExp(text) { 3 return String(text).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); 4 } 5 6 function slugifyTitle(title, options) { 7 const sep = options && options.sepchar ? options.sepchar : '_'; 8 const sepPattern = new RegExp(escapeRegExp(sep) + '+', 'g'); 9 const trimPattern = new RegExp('^' + escapeRegExp(sep) + '|' + escapeRegExp(sep) + '$', 'g'); 10 11 return String(title || '') 12 .normalize('NFD') 13 .replace(/[\u0300-\u036f]/g, '') 14 .replace(/['’]/g, '') 15 .toLowerCase() 16 .replace(/[^a-z0-9]+/g, sep) 17 .replace(sepPattern, sep) 18 .replace(trimPattern, ''); 19 } 20 21 function getStrings() { 22 const lang = window.LANG && LANG.plugins && LANG.plugins.newpagefill ? LANG.plugins.newpagefill : {}; 23 return { 24 title: lang.dialog_title || 'Creer une nouvelle page', 25 pageTitle: lang.dialog_page_title || 'Titre', 26 pageId: lang.dialog_page_id || 'Identifiant', 27 namespace: lang.dialog_namespace || 'Namespace', 28 pageMode: lang.dialog_page_mode || 'Type de creation', 29 pageModeStart: lang.dialog_page_mode_start || 'Page de demarrage', 30 pageModeNone: lang.dialog_page_mode_none || 'Page directe', 31 pageModeSame: lang.dialog_page_mode_same || 'Sous-page du meme nom', 32 create: lang.dialog_create || 'Creer', 33 cancel: lang.dialog_cancel || 'Annuler', 34 required: lang.dialog_required || 'L identifiant est requis.' 35 }; 36 } 37 38 function getPluginConfig() { 39 return window.JSINFO && JSINFO.plugins && JSINFO.plugins.newpagefill 40 ? JSINFO.plugins.newpagefill 41 : {}; 42 } 43 44 function appendToUrl(base, params) { 45 return base + (base.indexOf('?') >= 0 ? '&' : '?') + params; 46 } 47 48 function buildBaseHref(namespace, options) { 49 const root = typeof DOKU_BASE !== 'undefined' && DOKU_BASE ? DOKU_BASE : '/'; 50 const cleaned = String(namespace || '').trim().replace(/^:+|:+$/g, ''); 51 52 if (options && Number(options.userewrite) === 1) { 53 if (!cleaned) return root.replace(/\/$/, ''); 54 return root.replace(/\/$/, '') + '/' + cleaned.split(':').map(encodeURIComponent).join('/'); 55 } 56 57 const base = root.replace(/\/$/, '/'); 58 if (!cleaned) return base; 59 return appendToUrl(base, 'id=' + encodeURIComponent(cleaned)); 60 } 61 62 function buildTargetPageId(namespace, pageId, start) { 63 const parts = []; 64 const cleanedNamespace = String(namespace || '').trim().replace(/^:+|:+$/g, ''); 65 const cleanedPageId = String(pageId || '').trim().replace(/^:+|:+$/g, ''); 66 const cleanedStart = String(start || '').trim().replace(/^:+|:+$/g, ''); 67 68 if (cleanedNamespace) parts.push(cleanedNamespace); 69 if (cleanedPageId) parts.push(cleanedPageId); 70 if (cleanedStart) parts.push(cleanedStart); 71 72 return parts.join(':'); 73 } 74 75 function resolveStartSegment(pageId, startOption, defaultStart, defaultMode) { 76 if (startOption === null || typeof startOption === 'undefined') { 77 startOption = defaultMode; 78 } 79 80 if (String(startOption).toLowerCase() === '@ask@' || String(startOption).toLowerCase() === 'ask') { 81 return null; 82 } 83 84 if (startOption === true) { 85 return String(defaultStart || '').trim(); 86 } 87 88 if (startOption === false || startOption === '' || String(startOption).toLowerCase() === 'none') { 89 return ''; 90 } 91 92 if (String(startOption).toLowerCase() === '@same@' || String(startOption).toLowerCase() === 'same') { 93 return String(pageId || '').trim(); 94 } 95 96 if (String(startOption).toLowerCase() === 'start') { 97 return String(defaultStart || '').trim(); 98 } 99 100 return String(startOption).trim(); 101 } 102 103 function getDialog() { 104 let overlay = document.getElementById('newpagefill_overlay'); 105 if (overlay) return overlay; 106 107 overlay = document.createElement('div'); 108 overlay.id = 'newpagefill_overlay'; 109 overlay.innerHTML = [ 110 '<div class="newpagefill_dialog" role="dialog" aria-modal="true" aria-labelledby="newpagefill_title">', 111 ' <h3 id="newpagefill_title"></h3>', 112 ' <label class="newpagefill_label" for="newpagefill_input_title"></label>', 113 ' <input id="newpagefill_input_title" type="text" autocomplete="off">', 114 ' <label class="newpagefill_label" for="newpagefill_input_id"></label>', 115 ' <input id="newpagefill_input_id" type="text" autocomplete="off" autocapitalize="off" spellcheck="false">', 116 ' <div class="newpagefill_namespace_row">', 117 ' <label class="newpagefill_label" for="newpagefill_input_namespace"></label>', 118 ' <input id="newpagefill_input_namespace" type="text" autocomplete="off" autocapitalize="off" spellcheck="false">', 119 ' </div>', 120 ' <div class="newpagefill_mode_row">', 121 ' <label class="newpagefill_label" for="newpagefill_input_mode"></label>', 122 ' <select id="newpagefill_input_mode"></select>', 123 ' </div>', 124 ' <p class="newpagefill_error" aria-live="polite"></p>', 125 ' <div class="newpagefill_actions">', 126 ' <button type="button" class="newpagefill_cancel"></button>', 127 ' <button type="button" class="newpagefill_create"></button>', 128 ' </div>', 129 '</div>' 130 ].join(''); 131 document.body.appendChild(overlay); 132 133 overlay.addEventListener('click', function (event) { 134 if (event.target === overlay) { 135 overlay.classList.remove('is-open'); 136 } 137 }); 138 139 return overlay; 140 } 141 142 function openCreatePageDialog(options) { 143 const overlay = getDialog(); 144 const strings = getStrings(); 145 const titleEl = overlay.querySelector('#newpagefill_title'); 146 const titleLabel = overlay.querySelector('label[for="newpagefill_input_title"]'); 147 const namespaceRow = overlay.querySelector('.newpagefill_namespace_row'); 148 const namespaceLabel = overlay.querySelector('label[for="newpagefill_input_namespace"]'); 149 const modeRow = overlay.querySelector('.newpagefill_mode_row'); 150 const modeLabel = overlay.querySelector('label[for="newpagefill_input_mode"]'); 151 const idLabel = overlay.querySelector('label[for="newpagefill_input_id"]'); 152 const titleInput = overlay.querySelector('#newpagefill_input_title'); 153 const namespaceInput = overlay.querySelector('#newpagefill_input_namespace'); 154 const modeInput = overlay.querySelector('#newpagefill_input_mode'); 155 const idInput = overlay.querySelector('#newpagefill_input_id'); 156 const errorEl = overlay.querySelector('.newpagefill_error'); 157 const cancelBtn = overlay.querySelector('.newpagefill_cancel'); 158 const createBtn = overlay.querySelector('.newpagefill_create'); 159 const pluginConfig = getPluginConfig(); 160 const config = { 161 namespace: options && options.namespace ? options.namespace : '', 162 sepchar: options && options.sepchar ? options.sepchar : (pluginConfig.sepchar || '_'), 163 start: options && Object.prototype.hasOwnProperty.call(options, 'start') 164 ? options.start 165 : undefined, 166 defaultStart: pluginConfig.start || 'start', 167 defaultStartMode: pluginConfig.default_start_mode || 'start', 168 initialTitle: options && options.initialTitle ? options.initialTitle : '', 169 userewrite: options && typeof options.userewrite !== 'undefined' 170 ? options.userewrite 171 : pluginConfig.userewrite 172 }; 173 174 titleEl.textContent = strings.title; 175 titleLabel.textContent = strings.pageTitle; 176 namespaceLabel.textContent = strings.namespace; 177 modeLabel.textContent = strings.pageMode; 178 idLabel.textContent = strings.pageId; 179 cancelBtn.textContent = strings.cancel; 180 createBtn.textContent = strings.create; 181 errorEl.textContent = ''; 182 namespaceRow.style.display = config.namespace ? 'none' : ''; 183 const shouldAskStartMode = 184 String(config.start).toLowerCase() === '@ask@' || 185 String(config.start).toLowerCase() === 'ask' || 186 ((typeof config.start === 'undefined' || config.start === null) && config.defaultStartMode === 'ask'); 187 188 modeRow.style.display = shouldAskStartMode ? '' : 'none'; 189 modeInput.innerHTML = ''; 190 [ 191 {value: 'start', label: strings.pageModeStart}, 192 {value: 'none', label: strings.pageModeNone}, 193 {value: '@same@', label: strings.pageModeSame} 194 ].forEach(function (option) { 195 const el = document.createElement('option'); 196 el.value = option.value; 197 el.textContent = option.label; 198 modeInput.appendChild(el); 199 }); 200 201 let idDirty = false; 202 203 function updateSuggestedId() { 204 if (idDirty) return; 205 idInput.value = slugifyTitle(titleInput.value, config); 206 } 207 208 function closeDialog() { 209 overlay.classList.remove('is-open'); 210 document.removeEventListener('keydown', handleKeydown); 211 } 212 213 function submitDialog() { 214 const titleValue = titleInput.value.trim(); 215 const namespaceValue = namespaceInput.value.trim(); 216 const idValue = idInput.value.trim(); 217 if (!idValue) { 218 errorEl.textContent = strings.required; 219 idInput.focus(); 220 return; 221 } 222 223 const namespace = namespaceValue || config.namespace; 224 const startOption = shouldAskStartMode ? modeInput.value : config.start; 225 const startSegment = resolveStartSegment(idValue, startOption, config.defaultStart, config.defaultStartMode); 226 let targetUrl; 227 228 if (Number(config.userewrite) === 1) { 229 const baseHref = buildBaseHref(namespace, config); 230 targetUrl = baseHref + '/' + encodeURIComponent(idValue); 231 if (startSegment) { 232 targetUrl += '/' + encodeURIComponent(startSegment); 233 } 234 } else { 235 targetUrl = buildBaseHref('', config); 236 targetUrl = appendToUrl(targetUrl, 'id=' + encodeURIComponent(buildTargetPageId(namespace, idValue, startSegment))); 237 } 238 239 targetUrl = appendToUrl(targetUrl, 'do=edit'); 240 if (titleValue) { 241 targetUrl = appendToUrl(targetUrl, 'title=' + encodeURIComponent(titleValue)); 242 } 243 244 window.location.href = targetUrl; 245 } 246 247 function handleKeydown(event) { 248 if (!overlay.classList.contains('is-open')) return; 249 if (event.key === 'Escape') { 250 event.preventDefault(); 251 closeDialog(); 252 } 253 if (event.key === 'Enter') { 254 event.preventDefault(); 255 submitDialog(); 256 } 257 } 258 259 titleInput.value = config.initialTitle; 260 namespaceInput.value = config.namespace; 261 idInput.value = ''; 262 idDirty = false; 263 updateSuggestedId(); 264 265 titleInput.oninput = function () { 266 errorEl.textContent = ''; 267 updateSuggestedId(); 268 }; 269 270 idInput.oninput = function () { 271 errorEl.textContent = ''; 272 idDirty = idInput.value.trim() !== slugifyTitle(titleInput.value, config); 273 }; 274 275 cancelBtn.onclick = closeDialog; 276 createBtn.onclick = submitDialog; 277 278 overlay.classList.add('is-open'); 279 document.addEventListener('keydown', handleKeydown); 280 window.setTimeout(() => titleInput.focus(), 0); 281 } 282 283 window.NewPageFill = { 284 slugifyTitle, 285 openCreatePageDialog 286 }; 287})(); 288