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