1/** 2 * The Indexmenu Wizard 3 * 4 * @author Gerrit Uitslag 5 * based on Linkwiz by 6 * @author Andreas Gohr <gohr@cosmocode.de> 7 * @author Pierre Spring <pierre.spring@caillou.ch> 8 * and the concepts of the old indexmenu wizard 9 */ 10const indexmenu_wiz = { 11 $wiz: null, 12 timer: null, 13 textArea: null, 14 15 defaulttheme: 'default', 16 fields: { 17 div1: { 18 elems: { 19 jstoggle: {label: 'js'} 20 } 21 }, 22 div2: { 23 tlbclass: 'jsitem theme', 24 elems: { 25 el1: {headerid: 'theme'} 26 } 27 }, 28 div3: { 29 elems: { 30 el2: {headerid: 'navigation'}, 31 navbar: {}, 32 context: {}, 33 nocookie: {tlbclass: 'jsitem'}, 34 noscroll: {tlbclass: 'jsitem'}, 35 notoc: {tlbclass: 'jsitem'} 36 } 37 }, 38 div4: { 39 elems: { 40 el3: {headerid: 'sort'}, 41 tsort: {}, 42 dsort: {}, 43 msort: {}, 44 hsort: {}, 45 rsort: {}, 46 nsort: {} 47 } 48 }, 49 div5: { 50 elems: { 51 el4: {headerid: 'filter'}, 52 nons: {}, 53 nopg: {} 54 } 55 }, 56 div6: { 57 tlbclass: 'jsitem', 58 elems: { 59 el5: {headerid: 'performance'}, 60 max: {tlbclass: 'jsitem', numberinput: ['maxn', 'maxm']}, 61 maxjs: {tlbclass: 'jsitem', numberinput: ['maxjsn']}, 62 id: {tlbclass: 'jsitem', numberinput: ['idn']} 63 } 64 } 65 }, 66 67 /** 68 * Initialize the indexmenu_wiz by creating the needed HTML 69 * and attaching the eventhandlers 70 */ 71 init: function ($editor) { 72 // position relative to the text area 73 const pos = $editor.position(); 74 75 // create HTML Structure 76 indexmenu_wiz.$wiz = jQuery(document.createElement('div')) 77 .dialog({ 78 autoOpen: false, 79 draggable: true, 80 title: LANG.plugins.indexmenu.indexmenuwizard, 81 resizable: false 82 }) 83 .html( 84 '<fieldset class="indexmenu_index"><legend>' + LANG.plugins.indexmenu.index + '</legend>' + 85 '<div><label>' + LANG.plugins.indexmenu.namespace + '<input id="namespace" type="text"></label></div>' + 86 '<div><label class="number">' + LANG.plugins.indexmenu.nsdepth + ' #<input id="nsdepth" type="text" value=1></label></div>' + 87 '</fieldset>' + 88 89 '<fieldset class="indexmenu_options"><legend>' + LANG.plugins.indexmenu.options + '</legend>' + 90 '</fieldset>' + 91 '<input type="submit" value="' + LANG.plugins.indexmenu.insert + '" class="button" id="indexmenu__insert">' + 92 93 '<fieldset class="indexmenu_metanumber">' + 94 '<label class="number">' + LANG.plugins.indexmenu.metanum + '<input type="text" id="metanumber"></label>' + 95 '<input type="submit" value="' + LANG.plugins.indexmenu.insertmetanum + '" class="button" id="indexmenu__insertmetanum">' + 96 '</fieldset>' 97 ) 98 .parent() 99 .attr('id', 'indexmenu__wiz') 100 .css({ 101 'position': 'absolute', 102 'top': (pos.top + 20) + 'px', 103 'left': (pos.left + 80) + 'px' 104 }) 105 .hide() 106 .appendTo('.dokuwiki:first'); 107 108 indexmenu_wiz.textArea = $editor[0]; 109 let $opt_fieldset = jQuery('#indexmenu__wiz fieldset.indexmenu_options'); 110 111 jQuery.each(indexmenu_wiz.fields, function (i, section) { 112 let div = jQuery('<div>').addClass(section.tlbclass); 113 114 jQuery.each(section.elems, function (elid, props) { 115 if (props.headerid) { 116 div.append('<strong>' + LANG.plugins.indexmenu[props.headerid] + '</strong><br />'); 117 } else { 118 let label = props.label || elid; 119 //checkbox 120 jQuery("<label>") 121 .addClass(props.tlbclass).addClass(props.numberinput ? ' hasnumber' : '') 122 .html('<input id="' + elid + '" type="checkbox">' + label) 123 .attr({title: LANG.plugins.indexmenu[elid]}) 124 .appendTo(div); 125 126 //number inputs 127 if (props.numberinput) { 128 jQuery.each(props.numberinput, function (j, numid) { 129 jQuery("<label>") 130 .attr({title: LANG.plugins.indexmenu[elid]}) 131 .addClass("number " + props.tlbclass) 132 .html('#<input type="text" id="' + numid + '">') 133 .appendTo(div); 134 }); 135 } 136 } 137 }); 138 $opt_fieldset.append(div); 139 }); 140 141 indexmenu_wiz.includeThemes(); 142 143 if (JSINFO && JSINFO.namespace) { 144 jQuery('#namespace').val(':' + JSINFO.namespace); 145 } 146 147 // attach event handlers 148 149 //toggle js fields 150 jQuery('#jstoggle') 151 .on('change', function () { 152 jQuery('#indexmenu__wiz .jsitem').toggle(this.checked); 153 }).trigger('change') 154 .parent().css({display: 'inline-block', width: '40px'}); //enlarge clickable area of label 155 156 //interactive number fields 157 jQuery('label.number input').on('keydown keyup', function () { 158 //allow only numbers 159 indexmenu_wiz.filterNumberinput(this); 160 //checked the option if a number in input 161 indexmenu_wiz.autoCheckboxForNumbers(this); 162 }); 163 164 jQuery('#indexmenu__insert').on('click', indexmenu_wiz.insertIndexmenu); 165 jQuery('#indexmenu__insertmetanum').on('click', indexmenu_wiz.insertMetaNumber); 166 167 jQuery('#indexmenu__wiz').find('.ui-dialog-titlebar-close').on('click', indexmenu_wiz.hide); 168 }, 169 170 /** 171 * Request and include themes in wizard 172 */ 173 includeThemes: function () { 174 175 let addButtons = function (data) { 176 jQuery('<div>') 177 .attr('id', 'themebar') 178 .addClass('toolbar') 179 .appendTo('div.theme'); 180 181 jQuery.each(data.themes, function (i, theme) { 182 let themeName = theme.split('.'); 183 184 let icoUrl = DOKU_BASE + data.themebase + '/' + theme + '/base.' + IndexmenuUtils.determineExtension(theme); 185 let $ico = jQuery('<div>') 186 .css({background: 'url(' + icoUrl + ') no-repeat center'}); 187 jQuery('<button>') 188 .addClass('themebutton toolbutton') 189 .attr('id', theme) 190 .attr('title', themeName[0]) 191 .append($ico) 192 .on('click', indexmenu_wiz.selectTheme) 193 .appendTo('div#themebar'); 194 }); 195 196 //select default theme 197 jQuery('#themebar button#' + indexmenu_wiz.defaulttheme).trigger('click'); 198 }; 199 200 jQuery.post( 201 DOKU_BASE + 'lib/exe/ajax.php', 202 {call: 'indexmenu', req: 'local'}, 203 addButtons, 204 'json' 205 ); 206 }, 207 208 /** 209 * set class 'selected' to clicked theme, remove from other 210 */ 211 selectTheme: function () { 212 jQuery('.themebutton').toggleClass('selected', false); 213 jQuery(this).toggleClass('selected', true); 214 }, 215 216 /** 217 * Allow only number, by direct removing other characters from input 218 */ 219 filterNumberinput: function (elem) { 220 if (elem.value.match(/\D/)) { 221 elem.value = this.value.replace(/\D/g, ''); 222 } 223 }, 224 225 /** 226 * When a number larger than zero is inputted, check the checkbox 227 */ 228 autoCheckboxForNumbers: function (elem) { 229 let checkboxid = elem.id.substring(0, elem.id.length - 1); 230 let value = elem.value; 231 //exception for second number field of max: only uncheck when first field is also empty 232 if (elem.id === 'maxm' && !(elem.value > 0)) { 233 value = parseInt(jQuery('input#maxn').val()); 234 } 235 jQuery('input#' + checkboxid).prop('checked', value > 0); 236 }, 237 238 /** 239 * Insert the indexmenu with options to the textarea, 240 * replacing the current selection or at the cursor position. 241 */ 242 insertIndexmenu: function () { 243 let options = ''; 244 jQuery('fieldset.indexmenu_options input').each(function (i, input) { 245 let $label = jQuery(this).parent(); 246 247 if (input.checked && (!$label.hasClass('jsitem') || jQuery('input#jstoggle').is(':checked'))) { 248 if (input.id === 'jstoggle') { 249 //add js options 250 options += ' js'; 251 //add theme 252 let themename = jQuery('#themebar button.selected').attr('id'); 253 if (indexmenu_wiz.defaulttheme !== themename) { //skip default theme 254 options += '#' + themename; 255 } 256 } else { 257 //add option 258 options += ' ' + input.id; 259 //add numbers 260 if ($label.hasClass('hasnumber')) { 261 jQuery.each(indexmenu_wiz.fields.div6.elems[input.id].numberinput, function (j, numid) { 262 let num = parseInt(jQuery('input#' + numid).val()); 263 options += num ? '#' + num : ''; 264 }); 265 } 266 } 267 } 268 269 }); 270 options = options ? '|' + options.trim() : ''; 271 272 let sel, ns, depth, syntax, eo; 273 274 // XXX: Compatibility Fix for 2014-05-05 "Ponder Stibbons", splitbrain/dokuwiki#505 275 if (DWgetSelection) { 276 sel = DWgetSelection(indexmenu_wiz.textArea); 277 } else { 278 sel = getSelection(indexmenu_wiz.textArea); 279 } 280 281 282 ns = jQuery('#namespace').val(); 283 depth = parseInt(jQuery('#nsdepth').val()); 284 depth = depth ? '#' + depth : ''; 285 286 syntax = '{{indexmenu>' + ns + depth + options + '}}'; 287 eo = depth.length + options.length + 2; 288 289 pasteText(sel, syntax, {startofs: 12, endofs: eo}); 290 indexmenu_wiz.hide(); 291 }, 292 293 /** 294 * Insert meta number for sorting in textarea 295 * Takes number from input, otherwise tries the selection in textarea 296 */ 297 insertMetaNumber: function () { 298 let sel, selnum, syntax, number; 299 300 // XXX: Compatibility Fix for 2014-05-05 "Ponder Stibbons", splitbrain/dokuwiki#505 301 if (DWgetSelection) { 302 sel = DWgetSelection(indexmenu_wiz.textArea); 303 } else { 304 sel = getSelection(indexmenu_wiz.textArea); 305 } 306 307 selnum = parseInt(sel.getText()); 308 number = parseInt(jQuery('input#metanumber').val()); 309 number = number || selnum || 1; 310 syntax = '{{indexmenu_n>' + number + '}}'; 311 312 pasteText(sel, syntax, {startofs: 14, endofs: 2}); 313 indexmenu_wiz.hide(); 314 }, 315 316 /** 317 * Show the indexmenu wizard 318 */ 319 show: function () { 320 // XXX: Compatibility Fix for 2014-05-05 "Ponder Stibbons", splitbrain/dokuwiki#505 321 if (DWgetSelection) { 322 indexmenu_wiz.selection = DWgetSelection(indexmenu_wiz.textArea); 323 } else { 324 indexmenu_wiz.selection = getSelection(indexmenu_wiz.textArea); 325 } 326 327 indexmenu_wiz.$wiz.show(); 328 jQuery('#namespace').trigger('focus'); 329 }, 330 331 /** 332 * Hide the indexmenu wizard 333 */ 334 hide: function () { 335 indexmenu_wiz.$wiz.hide(); 336 indexmenu_wiz.textArea.focus(); //pure js 337 }, 338 339 /** 340 * Toggle the indexmenu wizard 341 */ 342 toggle: function () { 343 if (indexmenu_wiz.$wiz.css('display') === 'none') { 344 indexmenu_wiz.show(); 345 } else { 346 indexmenu_wiz.hide(); 347 } 348 } 349}; 350