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