/** * The Link Wizard * * @author Andreas Gohr * @author Pierre Spring */ (function($){ $.fn.extend({ linkwiz: function(editor) { var wiz; var entry; var result; var timer; var textArea; var selected; var selection; /** * Initialize the linkwizard by creating the needed HTML * and attaching the eventhandlers */ var init = function(textAreaElement){ // create HTML Structure wiz = document.createElement('div'); wiz.style.position = 'absolute'; wiz.id = 'link__wiz'; wiz.className = 'picker'; wiz.style.top = (findPosY(textAreaElement)+20)+'px'; wiz.style.left = (findPosX(textAreaElement)+80)+'px'; wiz.style.marginLeft = '-10000px'; wiz.style.marginTop = '-10000px'; wiz.innerHTML = ''+ '
'+LANG['linkto']+'
'+ ''; $('#dw__editform')[0].parentNode.appendChild(wiz); textArea = textAreaElement; result = $('#link__wiz_result')[0]; entry = $('#link__wiz_entry')[0]; // attach event handlers var obj; var obj = $('#link__wiz_close')[0]; obj.onclick = hide; addEvent(entry,'keyup',onEntry); addEvent(result,'click',onResultClick); $(wiz).draggable({handle: '#link__wiz_header'}); }; /** * handle all keyup events in the entry field */ var onEntry = function(e){ if(e.keyCode == 37 || e.keyCode == 39){ //left/right return true; //ignore } if(e.keyCode == 27){ hide(); e.preventDefault(); e.stopPropagation(); return false; } if(e.keyCode == 38){ //Up select(selected -1); e.preventDefault(); e.stopPropagation(); return false; } if(e.keyCode == 40){ //Down select(selected +1); e.preventDefault(); e.stopPropagation(); return false; } if(e.keyCode == 13){ //Enter if(selected > -1){ var obj = getResult(selected); if(obj){ var a = $(obj).find('a')[0]; resultClick(a); } }else if(entry.value){ insertLink(entry.value); } e.preventDefault(); e.stopPropagation(); return false; } autocomplete(); }; /** * Get one of the result by index * * @param int result div to return * @returns DOMObject or null */ var getResult = function(num){ var obj; var childs = $(result).find('div'); var obj = childs[num]; if(obj){ return obj; }else{ return null; } }; /** * Select the given result */ var select = function(num){ if(num < 0){ deselect(); return; } var obj = getResult(num); if(obj){ deselect(); obj.className += ' selected'; // make sure the item is viewable in the scroll view // FIXME check IE compatibility if(obj.offsetTop > result.scrollTop + result.clientHeight){ result.scrollTop += obj.clientHeight; }else if(obj.offsetTop - result.clientHeight < result.scrollTop){ // this works but isn't quite right, fixes welcome result.scrollTop -= obj.clientHeight; } // now recheck - if still not in view, the user used the mouse to scroll if( (obj.offsetTop > result.scrollTop + result.clientHeight) || (obj.offsetTop < result.scrollTop) ){ obj.scrollIntoView(); } selected = num; } }; /** * deselect a result if any is selected */ var deselect = function(){ if(selected > -1){ var obj = getResult(selected); if(obj){ obj.className = obj.className.replace(/ ?selected/,''); } } selected = -1; }; /** * Handle clicks in the result set an dispatch them to * resultClick() */ var onResultClick = function(e){ if(e.target.tagName != 'A') return; e.stopPropagation(); e.preventDefault(); resultClick(e.target); return false; }; /** * Handles the "click" on a given result anchor */ var resultClick = function(a){ var id = a.title; if(id == '' || id.substr(id.length-1) == ':'){ entry.value = id; autocomplete_exec(); }else{ entry.value = id; if(a.nextSibling && a.nextSibling.tagName == 'SPAN'){ insertLink(a.nextSibling.innerHTML); }else{ insertLink(''); } } }; /** * Insert the id currently in the entry box to the textarea, * replacing the current selection or at the curso postion. * When no selection is available the given title will be used * as link title instead */ var insertLink = function(title){ if(!entry.value) return; var sel = getSelection(textArea); if(sel.start == 0 && sel.end == 0) sel = selection; var stxt = sel.getText(); // don't include trailing space in selection if(stxt.charAt(stxt.length - 1) == ' '){ sel.end--; var stxt = sel.getText(); } if(!stxt && !DOKU_UHC) stxt=title; // prepend colon inside namespaces for non namespace pages if(textArea.form['id'].value.indexOf(':') != -1 && entry.value.indexOf(':') == -1){ entry.value = ':'+entry.value; } var link = '[['+entry.value+'|'; if(stxt) link += stxt; link += ']]'; var so = entry.value.length+3; var eo = 2; pasteText(sel,link,{startofs: so, endofs: eo}); hide(); // reset the entry to the parent namespace and remove : at the beginning entry.value = entry.value.replace(/(^:)?[^:]*$/, ''); }; /** * Start the page/namespace lookup timer * * Calls autocomplete_exec when the timer runs out */ var autocomplete = function(){ if(timer !== null){ window.clearTimeout(timer); timer = null; } timer = window.setTimeout(autocomplete_exec,350); }; /** * Executes the AJAX call for the page/namespace lookup */ var autocomplete_exec = function(){ deselect(); result.innerHTML = ''; // because we need to use POST, we // can not use the .load() function. $.post( DOKU_BASE + 'lib/exe/ajax.php', { call: 'linkwiz', q: entry.value }, function (data) { result.innerHTML = data; }, 'html' ); }; /** * Clears the result area */ var clear = function(){ result.innerHTML = 'Search for a matching page name above, or browse through the pages on the right'; entry.value = ''; }; /** * Show the linkwizard */ var show = function(){ selection = getSelection(textArea); wiz.style.marginLeft = '0px'; wiz.style.marginTop = '0px'; entry.focus(); autocomplete(); }; /** * Hide the link wizard */ var hide = function(){ wiz.style.marginLeft = '-10000px'; wiz.style.marginTop = '-10000px'; textArea.focus(); }; /** * Toggle the link wizard */ var toggle = function(){ if(wiz.style.marginLeft == '-10000px'){ show(); }else{ hide(); } }; init($(editor)[0]); return this.each(function() { $(this).click(function (e) { e.preventDefault(); toggle(); }); }); } }); })(jQuery);