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