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