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