Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
//script config
var es_namespaces = [0, 10, 14, 6]
var es_namespaces_names = {0: '',  6:'Image', 10:'\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0{\{\u00A0', 
 14:'Category'}
if (wgContentLanguage == 'ru') {
 es_namespaces_names[14] = 'Категория'
 es_namespaces_names[6]  = 'Изображение'
}



var editSuggest = new function(){ //global wrapper


var esForm, esText, linkName, esNS, isCtrlOrShift, txtBox, keyPrev
var selRange, selScroll, selWorking

this.start = function(){
 if (!esForm) init()
 if (esForm.style.display == 'none'){
   esText.value = getWikiText()
   setNS(0)
   showForm(true)
 }else{ 
   focusTextarea() //second click - hide form
 }
}

this.insert = ev_onsubmit

function init(){
 txtBox = document.getElementById('wpTextbox1') 
 //create form
 esForm = document.createElement('form')
 esForm.id = 'es_form'
 esForm.action = 'javascript:editSuggest.insert()'
 esForm.style.cssText = 'position:absolute; z-index:100; border:1px solid gray; padding:1px; background:#E0E0E0; opacity:0.8'
 //try { esForm.style.background = 'inherit'} catch(e){esForm.style.background = 'white'} //damn IE
 //add label
 var lbl = document.createElement('span')
 lbl.id = 'es_label'
 lbl.style.cssText = 'background:inherit; width:2em; font-size:125%; cursor:pointer; padding-right:5px'
 addHandler(lbl, 'click', cycleNS)
 esForm.appendChild(lbl)
 //add text field
 esText = appendInput('text', 'es_text', 'width:350px')
 esText.tabIndex = 0
 addHandler(esText, 'keydown', ev_onkeydown)
 addHandler(esText, 'keyup', ev_onkeyup)
 //add NS field
 esNS = appendInput('hidden', 'es_namespace')
 esNS.value = 1
 //position form
 var pos = os_getElementPosition('wpTextbox1')
 esForm.style.top  = (pos.top +2)+'px'
 esForm.style.left = (pos.left+2)+'px'
 //attach form and enable autosuggest
 document.body.appendChild(esForm)
 os_enableSuggestionsOn('es_text', 'es_form')
 os_createContainer(os_map['es_text'])
 esForm.style.display = 'none' //so it's not hidden by ev_onfocusTextarea
 return
 function appendInput(type, id, css){
   var inp = document.createElement('input')
   inp.type = type
   if (id) inp.id = inp.name = id
   if (css) inp.style.cssText = css
   esForm.appendChild(inp)
   return inp
 }
}



// *** METHODS *** 

function showForm(isOn){
 isOn = isOn==true
 esText.disabled = !isOn
 esForm.style.display = isOn ? '' : 'none'
 var el = document.getElementById('es_textSuggest')
 if (el) el.style.display = isOn ? '' : 'none' //"hidden" is not enouugh here: text cursor disappears under it in Firefox
 if (isOn){ 
   esText.focus()
   esText.select()
   fetchResults()
   setTimeout(setTextareaFocusHandler, 20) //if we set it now, weird IE6 will execute it
 }else{
   delHandler(txtBox, 'focus', ev_onfocusTextarea)
 }
}

function setTextareaFocusHandler(){ addHandler(txtBox, 'focus', ev_onfocusTextarea) }


function getWikiText(){ //returns target to search, remembers link name
 linkName = selGet()
 var targ, ma
 if (ma=linkName.match(/ *\[\[([^\]\|]+)\|?([^\]]+)?\]\] */)){ //user selected existing wikilink
   targ = ma[1]; linkName = ma[2] || ''
 }else
   targ = linkName
 return targ.replace(/́/g,'').replace(/ +$/, '') //remove accent and spaces at the end
}

function fetchResults(){
 var r = os_map['es_text']
 //r.query = '---' //null old query so the results are always shown
 r.original = ''
 //injectSpinner(esText, 'es')
 os_fetchResults(r, esText.value, 0)
}

function setWikiText(targ, name){
 //remove space at the end
 var isSpace = / +$/.test(name)
 if (isSpace) name = name.replace(/ +$/, '')
 var txt
 if (getNS() == 10){ //template
   txt = '{\{' + targ.substring(targ.indexOf(':')) + '|' + name + '}}'
 }else{ //construct wiki link
   txt =  '[\['
   if (normalize(name).indexOf(normalize(targ)) == 0) //target is substring
     txt += name.substring(0, targ.length) + ']]' + name.substring(targ.length) 
   else //target is different
     txt +=  targ + '|' + name + ']]'
 }
 //insert link
 if (isSpace) txt += ' ' 
 selSet(txt, true)
}

function focusSuggest(){esText.focus()}
function focusTextarea(){txtBox.focus(); selSelect()}

function setNS(ns){
 esNS.name = 'ns' + ns
 if (typeof es_namespaces_names[ns] != 'undefined') ns = es_namespaces_names[ns]
 document.getElementById('es_label').innerHTML = ns
}

function getNS(){
 var ns = esNS.name
 return  ns = ns ? ns.substring(2) : 0
}

function cycleNS(){
 var ns = getNS()
 for (var i=0; i<es_namespaces.length; i++)  if (ns == es_namespaces[i]) break  //find it in the list
 i++;  if (i >= es_namespaces.length) i = 0 //get next in the list
 setNS(es_namespaces[i]) //set new namespace
}


// *** EVENTS ***

function ev_onkeydown(e){
 e = e || window.event
 var key = (window.Event) ? e.which : e.keyCode
 switch (key){
  //Ctrl or Shift
  case 16: case 17: isCtrlOrShift = true; break
  //Escape
  case 27:
   //if (keyPrev==27 || !esText.value) 
   setTimeout(focusTextarea, 50)
   //else setTimeout(focusSuggest, 50) // bring cursor back after Escape, mwsuggest Opera issue
   break
 }
 keyPrev = key 
 //if (key == 9) focusTextarea() //Tab
}

function ev_onfocusTextarea(){
 if (esForm && (esForm.style.display != 'none')) setTimeout(showForm, 20)
} 

function ev_onkeyup(e){
 e = e || window.event
 var key = (window.Event) ? e.which : e.keyCode
 switch (key){
  /*Ctrl or Shift*/ case 16: case 17: isCtrlOrShift = false; break
  // /*Escape*/ case 27:    //e.cancelBubble = true;  if (e.stopPropagation) e.stopPropagation()   //break
  /*Space*/ case 32: if (!esText.value.replace(/ /g,'')){ cycleNS(); esText.value = ''}; break
  /*PgDnl*/ case 34: /*ScrollLock*/ case 145: cycleNS(); fetchResults(); break
 }
}

function ev_onsubmit(e){
 if (isCtrlOrShift){
   window.open (mw.config.get('wgServer') + mw.config.get('wgArticlePath').replace('\$1', esText.value))
 }else{
   setWikiText(esText.value, linkName)
   showForm(false)
   setTimeout(focusTextarea, 100)
 }
 return
}





// *** misc ***

function normalize(tt){ return (tt.substring(0,1).toUpperCase() + tt.substring(1)).replace(/_/g, ' ') }


function delHandler(element, event, handler){
 if (window.removeEventListener) element.removeEventListener(event, handler, false)
 else if (window.detachEvent) element.detachEvent('on' + event, handler)
}


//cross-browser textarea selection, need txtBox var


function selGet(){
 if (document.selection) { //IE/Opera
   selScroll = document.documentElement.scrollTop
   txtBox.focus() 
   selRange = document.selection.createRange()
   return selRange.text
  }else if (txtBox.selectionStart || txtBox.selectionStart == '0') { // Mozilla
   selScroll = txtBox.scrollTop
   //this.selStart = txtBox.selectionStart
   //txtBox.focus()
   return txtBox.value.substring(txtBox.selectionStart, txtBox.selectionEnd)
  }else return null
}

function selSelect(){ if(selRange) selRange.select() } //for IE only

function selSet(txt, isSelect){
  txtBox.focus()
  if (document.selection) { //IE/Opera
    selRange.text = txt
    selRange.select()
    //no need to restore scroll if the action is not instant
    //document.documentElement.scrollTop = this.selScroll //restore window scroll position
 }else if (txtBox.selectionStart || txtBox.selectionStart == '0') { // Mozilla
    var p1 = txtBox.selectionStart, p2 = txtBox.selectionEnd
    txtBox.value = txtBox.value.substring(0, p1) + txt + txtBox.value.substring(p2, txtBox.value.length)
    txtBox.selectionStart = p1 + txt.length //restore cursor position at end
    txtBox.selectionEnd = txtBox.selectionStart
    txtarea.scrollTop = selScroll //restore txtarea scroll
  }
  if (isSelect) selMove (-txt.length)
}

function selMove(mv1, mv2){
  if (document.selection) { //IE/Opera
   if (!selRange.moveStart) return
   if (mv1) selRange.moveStart('character', mv1) 
   if (mv2) selRange.moveEnd ('character',  mv2)
 }else if (txtBox.selectionStart || txtBox.selectionStart == '0') { // Mozilla
   if (mv1) txtBox.selectionStart += mv1
   if (mv2) txtBox.selectionEnd   += mv2
  }
 }
 
}



if (window.wgMWSuggestTemplate && (wgAction=='edit' || wgAction=='submit'))
 addOnloadHook(editSuggestButton)


function editSuggestButton(){
 var tlb = document.getElementById('toolbar')
 if (!tlb) return
 var btn = document.createElement('input'); btn.type = 'button'
 btn.style.cssText = 'background:#adbede; height:22px; vertical-align:middle; padding:0'
 btn.value = '[\[↓]]'; btn.title = 'Suggest link'
 btn.onclick = editSuggest.start; btn.id = 'editSuggest'
 tlb.appendChild(btn)
 if (window.es_accesskey){
   btn.accessKey = es_accesskey
   btn.title += ' ['+es_accesskey+']'
   updateTooltipAccessKeys([btn])
 } 
}