User:Zocky/LinkComplete.js
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.
// <pre><nowiki>
// Auto complete for links in the editbox GPL, (c) 2006, [[en:User:Zocky]]
// type [[foo and press ctrl+space
// should work in most browsers
// hook
$(linkCompleteInit);
//init
function linkCompleteInit()
{
if ($('wpTextbox1'))
{
eventAddListener($('wpTextbox1'),'keydown',linkCompleteKeyHandler);
eventAddListener($('wpTextbox1'),'keyup',linkCompleteKeyIgnorer1);
eventAddListener($('wpTextbox1'),'keypress',linkCompleteKeyIgnorer2);
linkCompleteRegExp = window['linkCompleteTriggers'] || [ /\[\[([^\[\]\|\n]*?)$/ ];
}
}
// a bunch of globals [[a
var linkCompleteStatus='idle';
var linkCompleteFind='';
var linkCompleteMatches=[];
var linkCompleteNextPage;
var linkCompleteNextMatch=0;
var linkCompleteStart=0;
var linkCompleteTarget=null;
var linkCompleteRegExp=[];
// helpers
function linkCompleteInsert(s)
{
var top=linkCompleteTarget.scrollTop;
linkCompleteTarget.value = linkCompleteTarget.value.substr(0,linkCompleteStart)
+ s
+ linkCompleteTarget.value.substr(selectionGetStart(linkCompleteTarget));
linkCompleteTarget.scrollTop=top;
selectionSet(linkCompleteTarget, linkCompleteStart+s.length,linkCompleteStart+s.length);
}
function linkCompleteInsertMatch()
{
if (linkCompleteNextMatch<linkCompleteMatches.length)
{
linkCompleteInsert(linkCompleteMatches[linkCompleteNextMatch]);
linkCompleteNextMatch++;
return true;
}
else
{
return false;
}
}
function linkCompleteGetMatches(from)
{
linkCompleteStatus='waiting';
mwQueryPhp(linkCompleteLoaded,"allpages","apfrom", from, "aplimit","50","apfilterredir","nonredirects","apnamespace",window.linkCompleteNamespace || 0);
linkCompleteInsert(linkCompleteFind+'...');
}
function linkCompleteReset()
{
linkCompleteStatus='idle';
linkCompleteInsert(linkCompleteFind);
linkCompleteMatches=[];
}
// main function
function linkCompleteKeyIgnorer1(e)
{
keynum = eventKeyCode(e);
if (keynum==9) eventStop(e);
}
function linkCompleteKeyIgnorer2(e)
{
keynum = e.charCode || e.keyCode;
if (keynum==9) eventStop(e);
}
function linkCompleteKeyHandler(e)
{
keynum = eventKeyCode(e);
target=eventTarget(e);
if ((keynum==9 || e.ctrlKey && keynum==32) && selectionGetStart(target)==selectionGetEnd(target))
{
if (target!=linkCompleteTarget)
{
linkCompleteTarget=target;
linkCompleteStatus='idle';
}
switch(linkCompleteStatus)
{
case 'idle':
var find;
for (var i in linkCompleteRegExp)
{
find = (target.value.substr(0,selectionGetStart(target)).match(linkCompleteRegExp[i]) || [])[1];
if (find) break;
}
if (find)
{
linkCompleteMatches=[];
linkCompleteNextMatch=0;
linkCompleteFind=find;
linkCompleteStart=selectionGetStart(target)-find.length;
linkCompleteGetMatches(find.capitalize());
}
break;
case 'waiting':
break;
case 'loaded':
if(linkCompleteNextMatch<linkCompleteMatches.length)
{
linkCompleteInsertMatch() || linkCompleteReset();
}
else
{
if (linkCompleteNextPage)
{
linkCompleteGetMatches(linkCompleteNextPage);
}
else
{
linkCompleteNextMatch=0;
linkCompleteInsertMatch() || linkCompleteReset();
}
}
}
eventStop(e);
}
else
{
linkCompleteStatus == 'waiting' && linkCompleteReset();
linkCompleteStatus = 'idle';
}
}
// JSON callback
function linkCompleteLoaded(obj)
{
if (obj)
{
if (linkCompleteStatus=='waiting')
{
for (var i in obj.query.allpages)
{
page=obj.query.allpages[i];
if
(
page.title
&& (page.ns && page.title.replace(/^.*?:/,'').substr(0,linkCompleteFind.length) == linkCompleteFind.capitalize())
|| page.title.substr(0,linkCompleteFind.length) == linkCompleteFind.capitalize()
)
{
linkCompleteMatches[linkCompleteMatches.length]
= page.ns ? page.title : linkCompleteFind + page.title.substr(linkCompleteFind.length);
}
}
linkCompleteNextPage
= obj['query-continue']
? obj['query-continue'].allpages.continue.substr(0,linkCompleteFind.length) == linkCompleteFind.capitalize()
? obj['query-continue'].allpages.continue
: false : false;
linkCompleteInsertMatch() && (linkCompleteStatus='loaded') || linkCompleteReset();
}
}
else
{
linkCompleteReset();
}
}
// query.php wrapper
function mwQueryPhp (cb,what)
{
var x = window.XMLHttpRequest ? new XMLHttpRequest()
: window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP")
: false;
if (mwQueryPhp.arguments.length>1 && x)
{
var url= mw.config.get('wgScriptPath') +"/api.php?format=json&action=query&list="+what;
for (var i=2; i < mwQueryPhp.arguments.length; i+=2 )
{
url=url+"&"+escape(mwQueryPhp.arguments[i]) + "=" + escape(mwQueryPhp.arguments[i+1]);
}
x.onreadystatechange=function() {
if (x.readyState==4)
return x.status==200
? cb(x.responseText.parseJSON())
: cb(false);
};
x.open("GET",url,true);
x.setRequestHeader('Accept','text/*');
x.send(null);
return true;
}
else return false;
}
// cross-browser event functions
function eventAddListener (element,event,handler)
{
if (element.addEventListener)
element.addEventListener(event,handler,false)
else
element.attachEvent('on'+event,handler);
}
function eventRemoveListener (element, event, handler)
{
if (element.removeEventListener)
element.removeEventListener(event,handler,false)
else
element.detachEvent('on'+event,handler);
}
function eventStop(event)
{
if (event.preventDefault)
{
event.preventDefault();
event.stopPropagation();
}
else
{
event.returnValue = false;
event.cancelBubble = true;
}
}
function eventTarget(event)
{
return event.target || event.srcElement;
}
function eventKeyCode(event)
{
return event.preventDefault ? event.which : event.keyCode ;
}
function $(id)
{
return document.getElementById(id);
}
//from http://www.json.org/json.js
String.prototype.parseJSON = function () {
try {
return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
this.replace(/"(\\.|[^"\\])*"/g, ''))) &&
eval('(' + this + ')');
} catch (e) {
return false;
}
};
//"foo".capitalize() -> "Foo"
String.prototype.capitalize = function () { return this.substring(0,1).toUpperCase()+this.substring(1)};
function selectionSet(input, start, end) {
if (input.setSelectionRange)
{
input.setSelectionRange(start,end)
input.selectionEnd=end;
}
else
{
var range = input.createTextRange();
range.collapse(true);
range.moveStart("character", start);
range.moveEnd("character", end - start);
range.select();
}
};
function selectionGetStart(input)
{
if (input.setSelectionRange)
{
return input.selectionStart;
}
else
{
var range = document.selection.createRange();
var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
if (!isCollapsed)range.collapse(true);
var b = range.getBookmark();
return b.charCodeAt(2) - 2;
}
};
function selectionGetEnd(input) {
if (input.setSelectionRange)
{
return input.selectionEnd;
}
else
{
var range = document.selection.createRange();
var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
if (!isCollapsed)range.collapse(false);
var b = range.getBookmark();
return b.charCodeAt(2) - 2;
}
};
//<nowiki></pre>