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