1/** 2 * The Link Wizard 3 * 4 * @author Andreas Gohr <gohr@cosmocode.de> 5 */ 6var linkwiz = { 7 wiz: null, 8 entry: null, 9 result: null, 10 timer: null, 11 sack: null, 12 textArea: null, 13 selected: -1, 14 selection: null, 15 16 /** 17 * Initialize the linkwizard by creating the needed HTML 18 * and attaching the eventhandlers 19 */ 20 init: function(textArea){ 21 // prepare AJAX object 22 linkwiz.sack = new sack(DOKU_BASE + 'lib/exe/ajax.php'); 23 linkwiz.sack.AjaxFailedAlert = ''; 24 linkwiz.sack.encodeURIString = false; 25 26 // create HTML Structure 27 linkwiz.wiz = document.createElement('div'); 28 linkwiz.wiz.id = 'link__wiz'; 29 linkwiz.wiz.className = 'picker'; 30 linkwiz.wiz.style.top = (findPosY(textArea)+20)+'px'; 31 linkwiz.wiz.style.left = (findPosX(textArea)+80)+'px'; 32 linkwiz.wiz.style.marginLeft = '-10000px'; 33 linkwiz.wiz.style.marginTop = '-10000px'; 34 linkwiz.wiz.style.position = 'absolute'; 35 36 linkwiz.wiz.innerHTML = 37 '<div id="link__wiz_header">'+ 38 '<img src="'+DOKU_BASE+'lib/images/close.png" width="16" height="16" align="right" alt="" id="link__wiz_close" />'+ 39 LANG['linkwiz']+'</div>'+ 40 '<div>'+LANG['linkto']+' <input type="text" class="edit" id="link__wiz_entry" autocomplete="off" /></div>'+ 41 '<div id="link__wiz_result"></div>'; 42 $('dw__editform').parentNode.appendChild(linkwiz.wiz); 43 linkwiz.textArea = textArea; 44 linkwiz.result = $('link__wiz_result'); 45 linkwiz.entry = $('link__wiz_entry'); 46 47 // attach event handlers 48 var obj; 49 obj = $('link__wiz_close'); 50 obj.onclick = linkwiz.hide; 51 52 linkwiz.sack.elementObj = linkwiz.result; 53 addEvent(linkwiz.entry,'keyup',linkwiz.onEntry); 54 addEvent(linkwiz.result,'click',linkwiz.onResultClick); 55 drag.attach(linkwiz.wiz,$('link__wiz_header')); 56 }, 57 58 /** 59 * handle all keyup events in the entry field 60 */ 61 onEntry: function(e){ 62 if(e.keyCode == 37 || e.keyCode == 39){ //left/right 63 return true; //ignore 64 } 65 if(e.keyCode == 27){ 66 linkwiz.hide(); 67 e.preventDefault(); 68 e.stopPropagation(); 69 return false; 70 } 71 if(e.keyCode == 38){ //Up 72 linkwiz.select(linkwiz.selected -1); 73 e.preventDefault(); 74 e.stopPropagation(); 75 return false; 76 } 77 if(e.keyCode == 40){ //Down 78 linkwiz.select(linkwiz.selected +1); 79 e.preventDefault(); 80 e.stopPropagation(); 81 return false; 82 } 83 if(e.keyCode == 13){ //Enter 84 if(linkwiz.selected > -1){ 85 var obj = linkwiz.getResult(linkwiz.selected); 86 if(obj){ 87 var a = obj.getElementsByTagName('A')[0]; 88 linkwiz.resultClick(a); 89 } 90 }else if(linkwiz.entry.value){ 91 linkwiz.insertLink(linkwiz.entry.value); 92 } 93 94 e.preventDefault(); 95 e.stopPropagation(); 96 return false; 97 } 98 linkwiz.autocomplete(); 99 }, 100 101 /** 102 * Get one of the result by index 103 * 104 * @param int result div to return 105 * @returns DOMObject or null 106 */ 107 getResult: function(num){ 108 var obj; 109 var childs = linkwiz.result.getElementsByTagName('DIV'); 110 obj = childs[num]; 111 if(obj){ 112 return obj; 113 }else{ 114 return null; 115 } 116 }, 117 118 /** 119 * Select the given result 120 */ 121 select: function(num){ 122 if(num < 0){ 123 linkwiz.deselect(); 124 return; 125 } 126 127 var obj = linkwiz.getResult(num); 128 if(obj){ 129 linkwiz.deselect(); 130 obj.className += ' selected'; 131 132 // make sure the item is viewable in the scroll view 133 // FIXME check IE compatibility 134 if(obj.offsetTop > linkwiz.result.scrollTop + linkwiz.result.clientHeight){ 135 linkwiz.result.scrollTop += obj.clientHeight; 136 }else if(obj.offsetTop - linkwiz.result.clientHeight < linkwiz.result.scrollTop){ // this works but isn't quite right, fixes welcome 137 linkwiz.result.scrollTop -= obj.clientHeight; 138 } 139 // now recheck - if still not in view, the user used the mouse to scroll 140 if( (obj.offsetTop > linkwiz.result.scrollTop + linkwiz.result.clientHeight) || 141 (obj.offsetTop < linkwiz.result.scrollTop) ){ 142 obj.scrollIntoView(); 143 } 144 145 linkwiz.selected = num; 146 } 147 }, 148 149 /** 150 * deselect a result if any is selected 151 */ 152 deselect: function(){ 153 if(linkwiz.selected > -1){ 154 var obj = linkwiz.getResult(linkwiz.selected); 155 if(obj){ 156 obj.className = obj.className.replace(/ ?selected/,''); 157 } 158 } 159 linkwiz.selected = -1; 160 }, 161 162 /** 163 * Handle clicks in the result set an dispatch them to 164 * resultClick() 165 */ 166 onResultClick: function(e){ 167 if(e.target.tagName != 'A') return; 168 e.stopPropagation(); 169 e.preventDefault(); 170 linkwiz.resultClick(e.target); 171 return false; 172 }, 173 174 /** 175 * Handles the "click" on a given result anchor 176 */ 177 resultClick: function(a){ 178 var id = a.title; 179 if(id == '' || id.substr(id.length-1) == ':'){ 180 linkwiz.entry.value = id; 181 linkwiz.autocomplete_exec(); 182 }else{ 183 linkwiz.entry.value = id; 184 if(a.nextSibling && a.nextSibling.tagName == 'SPAN'){ 185 linkwiz.insertLink(a.nextSibling.innerHTML); 186 }else{ 187 linkwiz.insertLink(''); 188 } 189 } 190 }, 191 192 /** 193 * Insert the id currently in the entry box to the textarea, 194 * replacing the current selection or at the curso postion. 195 * When no selection is available the given title will be used 196 * as link title instead 197 */ 198 insertLink: function(title){ 199 if(!linkwiz.entry.value) return; 200 201 var sel = getSelection(linkwiz.textArea); 202 if(sel.start == 0 && sel.end == 0) sel = linkwiz.selection; 203 204 var stxt = sel.getText(); 205 206 // don't include trailing space in selection 207 if(stxt.charAt(stxt.length - 1) == ' '){ 208 sel.end--; 209 stxt = sel.getText(); 210 } 211 212 if(!stxt && !DOKU_UHC) stxt=title; 213 214 // prepend colon inside namespaces for non namespace pages 215 if(linkwiz.textArea.form['id'].value.indexOf(':') != -1 && 216 linkwiz.entry.value.indexOf(':') == -1){ 217 linkwiz.entry.value = ':'+linkwiz.entry.value; 218 } 219 220 var link = '[['+linkwiz.entry.value+'|'; 221 if(stxt) link += stxt; 222 link += ']]'; 223 224 var so = linkwiz.entry.value.length+3; 225 var eo = 2; 226 227 pasteText(sel,link,{startofs: so, endofs: eo}); 228 linkwiz.hide(); 229 // reset the entry to the parent namespace and remove : at the beginning 230 linkwiz.entry.value = linkwiz.entry.value.replace(/(^:)?[^:]*$/, ''); 231 }, 232 233 /** 234 * Start the page/namespace lookup timer 235 * 236 * Calls autocomplete_exec when the timer runs out 237 */ 238 autocomplete: function(){ 239 if(linkwiz.timer !== null){ 240 window.clearTimeout(linkwiz.timer); 241 linkwiz.timer = null; 242 } 243 244 linkwiz.timer = window.setTimeout(linkwiz.autocomplete_exec,350); 245 }, 246 247 /** 248 * Executes the AJAX call for the page/namespace lookup 249 */ 250 autocomplete_exec: function(){ 251 linkwiz.deselect(); 252 linkwiz.result.innerHTML = '<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />'; 253 linkwiz.sack.runAJAX('call=linkwiz&q='+encodeURI(linkwiz.entry.value)); 254 }, 255 256 /** 257 * Clears the result area 258 */ 259 clear: function(){ 260 linkwiz.result.innerHTML = 'Search for a matching page name above, or browse through the pages on the right'; 261 linkwiz.entry.value = ''; 262 }, 263 264 /** 265 * Show the linkwizard 266 */ 267 show: function(){ 268 linkwiz.selection = getSelection(linkwiz.textArea); 269 linkwiz.wiz.style.marginLeft = '0px'; 270 linkwiz.wiz.style.marginTop = '0px'; 271 linkwiz.entry.focus(); 272 linkwiz.autocomplete(); 273 }, 274 275 /** 276 * Hide the link wizard 277 */ 278 hide: function(){ 279 linkwiz.wiz.style.marginLeft = '-10000px'; 280 linkwiz.wiz.style.marginTop = '-10000px'; 281 linkwiz.textArea.focus(); 282 }, 283 284 /** 285 * Toggle the link wizard 286 */ 287 toggle: function(){ 288 if(linkwiz.wiz.style.marginLeft == '-10000px'){ 289 linkwiz.show(); 290 }else{ 291 linkwiz.hide(); 292 } 293 } 294}; 295 296