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