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