1/** 2 * Right Context Menu configuration 3 * 4 * Some usefull variables: 5 * node.hns = headpage id; 6 * node.isdir = node is namespace; 7 * node.dokuid = the DW id (namespace parent in case of headpage); 8 * id = the DW id of the selected node (headpage id in case of headpage); 9 * index.config.urlbase = Url Base; 10 * index.config.sepchar = Url separator; 11 * 12 * HOWTO EDIT: 13 * 14 * To override menu entries or add a menu entry: 15 * - PLEASE EDIT ONLY the scripts/contextmenu.local.js file 16 * - DON'T EDIT this file, it is overwritten at plugin update 17 * 18 * Base structure of the context menu is displayed below. 19 * The entries with 'pg' are shown for page noded, these with 'ns' only for namespaces. 20 * 21 * Current available for everybody: 22 * indexmenu_contextmenu['all']['pg']['view'] = [...array with menu description here... ]; 23 * indexmenu_contextmenu['all']['pg']['edit'] = [ ... ]; 24 * indexmenu_contextmenu['all']['ns']['view'] = [ ... ]; 25 * 26 * Current available for admins: 27 * indexmenu_contextmenu['pg']['view'] = [ ... ]; 28 * indexmenu_contextmenu['ns']['view'] = [ ... ]; 29 * 30 * Current available for authenticated users: 31 * indexmenu_contextmenu['pg']['view'] = [ ... ]; 32 * indexmenu_contextmenu['ns']['view'] = [ ... ]; 33 * 34 * A menu description may contain four kind of entries: 35 * - section title: array with one entry e.g.: 36 * ['Section title (html allowed)'] 37 * - menu action: array with two entries e.g.: 38 * ['Title of action 1 (html allowed)', 'javascript here ... see for examples scripts/contextmenu.js'] 39 * - menu action with custom tooltip: array with three entries e.g.: 40 * ['Title of action 1 (html allowed)', 'javascript here ... see for examples scripts/contextmenu.js', 'Customized title'] 41 * - submenu: array with two entries where second entry is an array that describes again a menu e.g.: 42 * ['title of submenu (html allowed)', [ ...array with menu actions... ]] 43 * 44 * 45 * Examples: 46 * A menu description array: 47 * ... = [ 48 * ['section title'], 49 * ['title of action 1', 'javascript here'], 50 * ['title of submenu', [['title of subaction 1', 'javascript here'], ['title of subaction 1', 'javascript here', 'Click here for action']] ] 51 * ]; 52 * 53 * To Override the common menu title: 54 * indexmenu_contextmenu['all']['pg']['view'][0] = ['customtitle']; 55 * 56 * To override a menu entry, for example the menu title: 57 * indexmenu_contextmenu['all']['pg']['view'][0] = ['Custom Title']; 58 * 59 * To add option to page menu: 60 * Array.splice(index, howManyToRemove, description1) 61 * index = position to start (start counting at zero) 62 * howManyToRemove = number of elements that are removed (set to 1 to replace a element) 63 * description1 = array with menu entry description 64 * -> optional: description2 = optional you can add more elements at once by splice(index, howManyToRemove, description1, description2, etc) 65 * 66 * indexmenu_contextmenu['all']['pg']['view'].splice(1, 0, ['Input new page', '"javascript: IndexmenuContextmenu.reqpage(\'"+index.config.urlbase+"\',\'"+index.config.sepchar+"\',\'"+node.dokuid+"\');"']); 67 */ 68 69/* global LANG */ 70/* global DOKU_BASE */ 71/* global JSINFO */ 72 73 74// IMPORTANT: DON'T MODIFY THIS FILE, BUT EDIT contextmenu.local.js PLEASE! 75// THIS FILE IS OVERWRITTEN WHEN PLUGIN IS UPDATED 76 77/** 78 * Right Context Menu configuration for all users: 79 */ 80indexmenu_contextmenu['all']['pg'] = { 81 'view': [ 82 ['<span class="indexmenu_titlemenu"><b>'+LANG.plugins.indexmenu.page+'</b></span>'], 83 [LANG.plugins.indexmenu.revs, 'IndexmenuContextmenu.getid(index.config.urlbase,id)+"do=revisions"'], 84 [LANG.plugins.indexmenu.tocpreview, '"javascript: IndexmenuContextmenu.createTocMenu(\'call=indexmenu&req=toc&id="+id+"\',\'picker_"+index.treeName+"\',\'s"+index.treeName+node.id+"\');"'] 85 ], 86 //Menu items in edit mode, when previewing 87 'edit': [ 88 ['<span class="indexmenu_titlemenu"><b>'+LANG.plugins.indexmenu.editmode+'</b></span>'], 89 [LANG.plugins.indexmenu.insertdwlink, '"javascript: IndexmenuContextmenu.insertTags(\'"+id+"\',\'"+index.config.sepchar+"\');"+index.treeName+".divdisplay(\'r\',0);"', LANG.plugins.indexmenu.insertdwlinktooltip] 90 ] 91}; 92 93indexmenu_contextmenu['all']['ns'] = { 94 'view': [ 95 ['<span class="indexmenu_titlemenu"><b>'+LANG.plugins.indexmenu.ns+'</b></span>'], 96 [LANG.plugins.indexmenu.search, '"javascript: IndexmenuContextmenu.srchpage(\'"+index.config.urlbase+"\',\'"+index.config.sepchar+"\',\'"+node.isdir+"\',\'"+node.dokuid+"\');"', LANG.plugins.indexmenu.searchtooltip] 97 ] 98}; 99 100 101if (JSINFO && JSINFO.isadmin) { 102 /** 103 * Right Context Menu configuration for admin users: 104 */ 105 indexmenu_contextmenu['pg'] = { 106 'view': [ 107 [LANG.plugins.indexmenu.edit, 'IndexmenuContextmenu.getid(index.config.urlbase,id)+"do=edit"'], 108 ['<em>'+LANG.plugins.indexmenu.create+'--></em>', [ 109 [LANG.plugins.indexmenu.headpage, '"javascript: IndexmenuContextmenu.reqpage(\'"+index.config.urlbase+"\',\'"+index.config.sepchar+"\',\'"+node.dokuid+"\',\'"+node.name+"\');"', LANG.plugins.indexmenu.headpagetooltip], 110 [LANG.plugins.indexmenu.startpage, 'IndexmenuContextmenu.getid(index.config.urlbase,id+index.config.sepchar+"start")+"do=edit"', LANG.plugins.indexmenu.startpagetooltip], 111 [LANG.plugins.indexmenu.custompage, '"javascript: IndexmenuContextmenu.reqpage(\'"+index.config.urlbase+"\',\'"+index.config.sepchar+"\',\'"+node.dokuid+"\');"', LANG.plugins.indexmenu.custompagetooltip] 112 ]], 113 ['<em>'+LANG.plugins.indexmenu.more+'--></em>', [ 114 [LANG.plugins.indexmenu.acls, 'IndexmenuContextmenu.getid(index.config.urlbase,id)+"do=admin&page=acl"'], 115 [LANG.plugins.indexmenu.purgecache, 'IndexmenuContextmenu.getid(index.config.urlbase,id)+"purge=true"'], 116 [LANG.plugins.indexmenu.exporthtml, 'IndexmenuContextmenu.getid(index.config.urlbase,id)+"do=export_xhtml"'], 117 [LANG.plugins.indexmenu.exporttext, 'IndexmenuContextmenu.getid(index.config.urlbase,id)+"do=export_raw"'] 118 ]] 119 ] 120 }; 121 122 indexmenu_contextmenu['ns'] = { 123 'view': [ 124 [LANG.plugins.indexmenu.newpage, '"javascript: IndexmenuContextmenu.reqpage(\'"+index.config.urlbase+"\',\'"+index.config.sepchar+"\',\'"+node.dokuid+"\');"', LANG.plugins.indexmenu.newpagetooltip], 125 ['<em>'+LANG.plugins.indexmenu.more+'--></em>', [ 126 [LANG.plugins.indexmenu.headpagehere, '"javascript: IndexmenuContextmenu.reqpage(\'"+index.config.urlbase+"\',\'"+index.config.sepchar+"\',\'"+node.dokuid+"\',\'"+node.name+"\');"', LANG.plugins.indexmenu.headpageheretooltip], 127 [LANG.plugins.indexmenu.acls, 'IndexmenuContextmenu.getid(index.config.urlbase,node.dokuid)+"do=admin&page=acl"'] 128 ]] 129 ] 130 }; 131 132} else if (JSINFO && JSINFO.isauth) { 133 /** 134 * Right Context Menu configuration for authenticated users: 135 */ 136 indexmenu_contextmenu['pg'] = { 137 'view': [ 138 [LANG.plugins.indexmenu.newpagehere, '"javascript: IndexmenuContextmenu.reqpage(\'"+index.config.urlbase+"\',\'"+index.config.sepchar+"\',\'"+node.dokuid+"\');"'], 139 ['Edit', 'IndexmenuContextmenu.getid(index.config.urlbase,id)+"do=edit"', 1, 0 ], 140 ['<em>'+LANG.plugins.indexmenu.more+'--></em>', [ 141 [LANG.plugins.indexmenu.headpagehere, '"javascript: IndexmenuContextmenu.reqpage(\'"+index.config.urlbase+"\',\'"+index.config.sepchar+"\',\'"+node.dokuid+"\',\'"+node.name+"\');"'], 142 [LANG.plugins.indexmenu.purgecache, 'IndexmenuContextmenu.getid(index.config.urlbase,id)+"purge=true"'], 143 [LANG.plugins.indexmenu.exporthtml, 'IndexmenuContextmenu.getid(index.config.urlbase,id)+"do=export_xhtml"'] 144 ]] 145 ] 146 }; 147 148} 149 150var IndexmenuContextmenu = { 151 152 /** 153 * Common functions 154 * Insert your custom functions (available for all users) here. 155 */ 156 157 /** 158 * Navigate to the search page 159 * 160 * @param {string} urlbase index.config.urlbase 161 * @param {string} sepchar index.config.sepchar 162 * @param {boolean} isdir whether a directory (probably boolean, might be string) 163 * @param {string} nid page id of node (dokuid mentioned in indexmenu.js) 164 */ 165 166 srchpage: function (urlbase, sepchar, isdir, nid) { 167 const enteredText = prompt(LANG.plugins.indexmenu.insertkeywords, ""); 168 if (enteredText) { 169 let fnid = nid; 170 if (isdir == "0") { 171 fnid = fnid.substring(0, nid.lastIndexOf(sepchar)); 172 } 173 let b = urlbase, re = new RegExp(sepchar, 'g'); 174 fnid = fnid.replace(re, ":"); 175 b += (urlbase.indexOf("?id=") < 0) ? '?id=' : ''; 176 window.location.href = IndexmenuContextmenu.getid(b, enteredText + " @" + fnid) + "do=search"; 177 } 178 }, 179 180 /** 181 * Build a url to a wiki page 182 * 183 * @param {string} urlbase 184 * @param {string} id 185 * @returns {string} 186 */ 187 getid: function (urlbase, id) { 188 let url = (urlbase || '') + encodeURIComponent(id || ''); 189 url += (urlbase.indexOf("?") < 0) ? '?' : '&'; 190 return url; 191 }, 192 193 /** 194 * Navigate to the editor window 195 * 196 * @param {string} urlbase 197 * @param {string} sepchar 198 * @param {string} id 199 * @param {string} pagename 200 */ 201 reqpage: function (urlbase, sepchar, id, pagename) { 202 let newpageid; 203 if (pagename) { 204 newpageid = id + sepchar + pagename; 205 } else { 206 newpageid = prompt(LANG.plugins.indexmenu.insertpagename, ""); 207 if (!newpageid) { 208 return; 209 } 210 newpageid = id + sepchar + newpageid; 211 } 212 if (newpageid) { 213 window.location.href = IndexmenuContextmenu.getid(urlbase, newpageid) + "do=edit"; 214 } 215 }, 216 217 /** 218 * Insert link syntax with given id in current editor window 219 * 220 * @param {string} lnk page id 221 * @param {string} sepchar 222 */ 223 insertTags: function (lnk, sepchar) { 224 let r, l = lnk; 225 if (sepchar) { 226 r = new RegExp(sepchar, "g"); 227 l = lnk.replace(r, ':'); 228 } 229 insertTags('wiki__text', '[[', ']]', l); 230 }, 231 232 /** 233 * Create or catch the picker and hide it, next call the ajax content loading to get the ToC 234 * 235 * @param {string} get query string 236 * @param {string} picker id of picker 237 * @param {string} btn id of button 238 */ 239 createTocMenu: function (get, picker, btn) { 240 var $toc_picker = jQuery('#' + picker); 241 if (!$toc_picker.length) { 242 $toc_picker = IndexmenuUtils.createPicker(picker, 'indexmenu_toc'); 243 $toc_picker 244 .html('<a href="#"><img src="' + DOKU_BASE + 'lib/plugins/indexmenu/images/close.gif" class="indexmenu_close" /></a><div />') 245 .children().first().click(function (event) { 246 event.stopPropagation(); 247 return IndexmenuContextmenu.togglePicker($toc_picker, jQuery('#' + btn)); 248 }); 249 } else { 250 $toc_picker.hide(); 251 } 252 IndexmenuContextmenu.ajaxmenu(get, $toc_picker, jQuery('#' + btn), $toc_picker.children().last(), null); 253 }, 254 255 /** 256 * Shows the picker and adds to it or to an internal containter the ajax content 257 * 258 * @param {string} get query string 259 * @param {jQuery} $picker 260 * @param {jQuery} $btn 261 * @param {jQuery} $container if defined ajax result is added to it, otherwise to $picker 262 * @param {function} oncomplete called when defined to handle ajax result 263 */ 264 ajaxmenu: function (get, $picker, $btn, $container, oncomplete) { 265 var $indx_list; 266 $indx_list = $container || $picker; 267 268 if (!IndexmenuContextmenu.togglePicker($picker, $btn)) return; 269 270 var onComplete = function (data) { 271 $indx_list.html(''); 272 if (typeof oncomplete == 'function') { 273 oncomplete(data, $indx_list); 274 } else { 275 $indx_list.html(data); 276 } 277 }; 278 279 //get content for picker/container 280 jQuery.ajax({ 281 type: "POST", 282 url: DOKU_BASE + 'lib/exe/ajax.php', 283 data: get, 284 beforeSend: function () { 285 $indx_list.html('<div class="tocheader">'+LANG.plugins.indexmenu.loading+'</div>'); 286 }, 287 success: onComplete, 288 dataType: 'html' 289 }); 290 }, 291 292 293 /** 294 * Hide/show picker, will be shown beside btn 295 * 296 * @param {string|jQuery} $picker 297 * @param {jQuery} $btn 298 * @return {Boolean} true if open, false closed 299 */ 300 togglePicker: function ($picker, $btn) { 301 var x = 8, y = 0; 302 303 if (!$picker.is(':visible')) { 304 var pos = $btn.offset(); 305 //position + width of button 306 x += pos.left + $btn[0].offsetWidth; 307 y += pos.top; 308 309 $picker 310 .show() 311 .offset({ 312 left: x, 313 top: y 314 }); 315 316 return true; 317 } else { 318 $picker.hide(); 319 return false; 320 } 321 }, 322 323 /** 324 * Fills the contextmenu by creating entries from the given configuration arrays and concatenating these 325 * to the #r<id> picker 326 * 327 * @param {any[]} amenu (part of) the configuration array 328 * @param {dTree} index the indexmenu object 329 * @param {int} n node id 330 */ 331 arrconcat: function (amenu, index, n) { 332 var html, id, item, a, li; 333 if (typeof amenu == 'undefined' || typeof amenu['view'] == 'undefined') { 334 return; 335 } 336 var cmenu = amenu['view']; 337 if (jQuery('#tool__bar')[0] && amenu['edit'] instanceof Array) { 338 cmenu = amenu['edit'].concat(cmenu); 339 } 340 var node = index.aNodes[n]; 341 id = node.hns || node.dokuid; 342 343 var createCMenuEntry = function (entry) { 344 return '<a title="' + ((entry[2]) ? entry[2] : entry[0]) + '" href="' + eval(entry[1]) + '">' + entry[0] + '</a>'; 345 }; 346 347 jQuery.each(cmenu, function (i, cmenuentry) { 348 if (cmenuentry == '') { 349 return true; 350 } 351 item = document.createElement('li'); 352 var $cmenu = jQuery('#r' + index.treeName); 353 if (cmenuentry[1]) { 354 if (cmenuentry[1] instanceof Array) { 355 html = document.createElement('ul'); 356 jQuery.each(cmenuentry[1], function (a, subcmenuentry) { 357 li = document.createElement('li'); 358 li.innerHTML = createCMenuEntry(subcmenuentry); 359 html.appendChild(li); 360 }); 361 362 //} 363 item.innerHTML = '<span class="indexmenu_submenu">' + cmenuentry[0] + '</span>'; 364 html.left = $cmenu[0].width; 365 item.appendChild(html); 366 } else { 367 item.innerHTML = createCMenuEntry(cmenuentry); 368 } 369 } else { 370 item.innerHTML = cmenuentry; 371 } 372 $cmenu.children().last().append(item); 373 }); 374 }, 375 376 /** 377 * Absolute positioning of the div at place of mouseclick 378 * 379 * @param obj div element 380 * @param e 381 */ 382 mouseposition: function (obj, e) { 383 //http://www.quirksmode.org/js/events_properties.html 384 var X = 0, Y = 0; 385 if (!e) e = window.event; 386 if (e.pageX || e.pageY) { 387 X = e.pageX; 388 Y = e.pageY; 389 } 390 else if (e.clientX || e.clientY) { 391 X = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 392 Y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; 393 } 394 obj.style.left = X - 5 + 'px'; 395 obj.style.top = Y - 5 + 'px'; 396 }, 397 398 /** 399 * Check mouse button onmousedown event, only for middle and right mouse button contextmenu is shown 400 * 401 * @param {int} n node id 402 * @param {string|dTree} obj the unique name of a dTree object 403 * @param {event} e 404 */ 405 checkcontextm: function (n, obj, e) { 406 e = e || event; 407 // mouse clicks: which 3 === right, button 2 === right button 408 if ((e.which === 3 || e.button === 2) || (window.opera && e.which === 1 && e.ctrlKey)) { 409 obj.contextmenu(n, e); 410 IndexmenuContextmenu.stopevt(e); 411 } 412 }, 413 414 /** 415 * Prevent default oncontextmenu event 416 * 417 * @param {event} e 418 * @returns {boolean} 419 */ 420 stopevt: function (e) { 421 if (!window.indexmenu_contextmenu) { 422 return true; 423 } 424 e = e || event; 425 e.preventDefault ? e.preventDefault() : e.returnValue = false; 426 return false; 427 } 428}; 429 430