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.
$( linkfixr_init ) ;

var linkfixr_div ;
var linkfixr_last_message = '' ;
var linkfixr_page_exists_yes = [] ;
var linkfixr_page_exists_no = [] ;
var linkfixr_page_disambig_yes = [] ;
var linkfixr_page_disambig_no = [] ;
var linkfixr_page_disambig_candidates = [] ;
var linkfixr_last_from = -1 ;
var linkfixr_last_to = -1 ;

function linkfixr_init () {
  if ( mw.config.get('wgPageName') == "User:Magnus_Manske/linkfixr.js" ) return ; // Doesn't work so good while editing JS...
  if ( mw.config.get('wgAction') != 'edit' && mw.config.get('wgAction') != 'submit' ) return ;
  var tb1 = document.getElementById('wpTextbox1') ;
  var fh = tb1 ;
  linkfixr_div = document.createElement ( 'div' ) ;
  linkfixr_div.id = 'linkfixr_div' ;
  fh.parentNode.insertBefore(linkfixr_div,fh.nextSibling);
  tb1.onmouseup = tb1.onkeyup = function () {
    linkfixr_update() ;
  };
  linkfixr_update() ;
}

function linkfixr_update () {
  var pos = linkfixr_get_cursor_position() ;
  var tb1 = document.getElementById('wpTextbox1') ;
  var v1 = tb1.value.substr ( 0 , pos ) ;
  var v2 = tb1.value.substr ( pos ) ;
  v1 = v1.split('[[') ;
  v2 = v2.split(']]') ;
  if ( v1.length == 1 && v2.length == 1 ) return ;

  v1 = v1.pop() ;
  v2 = v2.shift() ;

  var v = v1 + v2 ;
  if ( v.split(']]').length > 1 || v.split('[[').length > 1 ) {
    linkfixr_show ( '' ) ;
    return ;
  }

  var title , text ;
  v = v.split('|') ;
  if ( v.length == 1 ) {
    title = v.shift() ;
    text = '' ;
    linkfixr_last_from = pos - v1.length ;
    linkfixr_last_to = pos + v2.length ;
  } else if ( v.length == 2 ) {
    title = v.shift() ;
    text = v.pop() ;
    linkfixr_last_from = pos - v1.length ;
    linkfixr_last_to = pos + v2.length ;
  } else { // Image, ignore for now
    linkfixr_show ( '' ) ;
    return ;
  }

  var e = linkfixr_page_exists ( title ) ;
  if ( e != 1 ) {
    linkfixr_show ( "<span style='color:red'><b>" + title + "</b> DOES NOT EXIST</span>" ) ;
    return ;
  }

  var dis = linkfixr_is_disambig ( title ) ;
  if ( dis == 1 ) {
    var out = "<b>" + title + "</b> is a disambiguation page. Possible replacements are:<br/>" ;
    var opts = linkfixr_get_links ( title ) ;
    out += opts.join ( ', ' ) ;
    out += "<br/><i>Click one of the above links to replace the text in the edit box accordingly.</i>" ;
    out = "<div style='background:#DDDDDD'>" + out + "</div>" ;
    linkfixr_show ( out ) ;
    return ;
  }

  linkfixr_show ( '' ) ; // title
}

function linkfixr_replace ( text ) {
  text = unescape ( text ) ;

  var tb1 = document.getElementById('wpTextbox1') ;
  var v = tb1.value ;
  var left = v.substr ( 0 , linkfixr_last_from ) ;
  var right = v.substr ( linkfixr_last_to ) ;
  var mid = v.substr ( linkfixr_last_from , linkfixr_last_to - linkfixr_last_from ) ;
  var m = mid.split ( '|' ) ;
  if ( m.length == 1 ) {
    mid = text + '|' + mid ;
  } else {
    mid = text + '|' + m.pop () ;
  }

  tb1.value = left + mid + right ;
  tb1.focus() ;
  linkfixr_set_cursor ( left.length , left.length + mid.length ) ;
  linkfixr_update () ;
  return false ;
}

function linkfixr_set_cursor(selStart, selEnd) { 
 var inputEl = document.getElementById('wpTextbox1') ;
 if (inputEl.setSelectionRange) { 
  inputEl.focus(); 
  inputEl.setSelectionRange(selStart, selEnd); 
 } else if (inputEl.createTextRange) { 
  var range = inputEl.createTextRange(); 
  range.collapse(true); 
  range.moveEnd('character', selEnd); 
  range.moveStart('character', selStart); 
  range.select(); 
 } 
}

function linkfixr_get_links ( title ) {
  var ret = [] ;
  if ( linkfixr_page_disambig_yes[title] != 1 ) return ret ; // Paranoia
  if ( linkfixr_page_disambig_candidates[title] ) return linkfixr_page_disambig_candidates[title] ;
  var url = "http://en.wikipedia.org/w/api.php?format=xml&action=query&prop=links&pllimit=500&titles=" + escape ( title ) ;
  var x = new XMLHttpRequest();
  x.open('GET', url, false);
  x.send(null); 
  var a = x.responseXML ;
  var links = a.getElementsByTagName('pl') ;
  for ( var l = 0 ; l < links.length ; l++ ) {
    var link = links[l].getAttribute ( 'title' ) ;
    if ( link == 'Wikipedia:Disambiguation' ) continue ;
    var i = "<a href='#' onclick='linkfixr_replace(\"" + escape ( link ) + "\")'>" + link + "</a>" ;
    ret.push ( i ) ;
  }
  linkfixr_page_disambig_candidates[title] = ret ;
  return ret ;
}

function linkfixr_is_disambig ( title ) {
  if ( linkfixr_page_disambig_yes[title] == 1 ) return 1 ;
  if ( linkfixr_page_disambig_no[title] == 1 ) return 0 ;
  var url = "http://en.wikipedia.org/w/api.php?format=xml&action=query&prop=categories&cllimit=500&titles=" + escape ( title ) ;
  var x = new XMLHttpRequest();
  x.open('GET', url, false);
  x.send(null); 
  var a = x.responseXML ;

  var p = a.getElementsByTagName('page')[0] ;
  if ( p.getAttribute('missing') == null ) linkfixr_page_exists_yes[title] = 1 ;
  else linkfixr_page_exists_no[title] = 1 ;

  var cats = a.getElementsByTagName('cl') ;
  for ( var c = 0 ; c < cats.length ; c++ ) {
    var cn = cats[c].getAttribute('title') ;
    if ( cn == 'Category:All disambiguation pages' ) {
      linkfixr_page_disambig_yes[title] = 1 ;
      return 1 ;
    }
  }
  linkfixr_page_disambig_no[title] = 1 ;
  return 0 ;
}

function linkfixr_page_exists ( title ) {
  if ( linkfixr_page_exists_yes[title] == 1 ) return 1 ;
  if ( linkfixr_page_exists_no[title] == 1 ) return 0 ;
  linkfixr_is_disambig ( title ) ;
  return linkfixr_page_exists ( title ) ;
}

function linkfixr_show ( text ) {
  if ( linkfixr_last_message == text ) return ;
  linkfixr_div.innerHTML = text ;
  linkfixr_last_message = text ;
}

function linkfixr_get_cursor_position() {
  var el = document.getElementById('wpTextbox1') ;
  if (el.selectionStart) {
    return el.selectionStart;
  } else if (document.selection) {
    el.focus();

    var r = document.selection.createRange();
    if (r == null) {
      return 0;
    }

    var re = el.createTextRange(),
        rc = re.duplicate();
    re.moveToBookmark(r.getBookmark());
    rc.setEndPoint('EndToStart', re);

    return rc.text.length;
  } 
  return 0;
}