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