מדיה ויקי:Gadget-autocomplete.js: הבדלים בין גרסאות בדף

מתוך חב"דפדיה, אנציקלופדיה חב"דית חופשית
קפיצה לניווט קפיצה לחיפוש
(יצירת דף עם התוכן "←‏Autocomplete for links and templates Written by [[משתמש:ערן]]: mw.loader.using(['jquery.ui.widget','jquery.ui.autocomplete','jquery.textSelection'],...")
 
(עדכון מויקיפדיה: https://he.wikipedia.org/wiki/%D7%9E%D7%93%D7%99%D7%94_%D7%95%D7%99%D7%A7%D7%99:Gadget-autocomplete.js)
 
שורה 3: שורה 3:
Written by [[משתמש:ערן]]
Written by [[משתמש:ערן]]
*/
*/
mw.loader.using(['jquery.ui.widget','jquery.ui.autocomplete','jquery.textSelection'],function(){
mw.loader.using(['jquery.ui', 'jquery.textSelection'], function() {
//extends jquery with autoCompleteWikiText functionality for autocomplete of links and templates
    //extends jquery with autoCompleteWikiText functionality for autocomplete of links and templates
$.fn.autoCompleteWikiText=function(options){
    $.fn.autoCompleteWikiText = function(options) {
var mode="none";
        var mode = "none",
var ctrl=$(this);
            templateDataCache = {},
var settings = $.extend(true, {
            ctrl = $(this),
positionMy: $('body').is('.rtl')? "left top" : "right top", // be default, open below the control
            settings = $.extend(true, {
positionAt: $('body').is('.rtl')? "left bottom" : "right bottom",
            positionMy: $('body').is('.rtl') ? "left top" : "right top", // be default, open below the control
positionOf: ctrl,  
            positionAt: $('body').is('.rtl') ? "left bottom" : "right bottom",
positionOffset: "0",
            positionOf: ctrl,
filterResponse: function(a){return a;}, // function that expects array of string and returns array of strings
            positionOffset: "0",
menuCSS: {width: 'auto', maxHeight: '30em', 'overflow-y': 'auto'},
            filterResponse: function(a) {
itemCSS: {},
                return a;
onselected: function(item){
            }, // function that expects array of string and returns array of strings
var pos=ctrl.textSelection('getCaretPosition')-1;
            menuCSS: {
var txt=ctrl.val();
                width: 'auto',
var link = mode == "link",
                maxHeight: '30em',
open = link ? "[[" : "{{",
                'overflow-y': 'auto'
close = link ? "]]" : "|}}",
            },
caretBackwards = link ? 0 : 2;
            itemCSS: {
switch(mode){
                right: 'inherit'
case "none": return;
            },
case "template":
            onselected: function(item) {
item=item.substr(mw.config.get('wgFormattedNamespaces')[10].length+1);
                var pos = ctrl.textSelection('getCaretPosition') - 1,
link = false;
                    txt = ctrl.val(),
caretBackwards = 2;
                open, close, caretBackwards;
break;
 
case "link":
                switch (mode) {
if(item[item.length-1]==')') item+='|';
                    case "none":
break;
                        return;
}
                    case "templateValue":
var lastbegin=txt.lastIndexOf(open, pos);
                        open = "|";
if (txt[lastbegin + 2] == ':')
                        close = "";
item = ':' + item;
                        caretBackwards = 0;
                        break;
                    case "templateParams":
                        open = "|";
                        close = "=";
                        caretBackwards = 0;
                        break;
                    case "template":
                        item = item.substr(mw.config.get('wgFormattedNamespaces')[10].length + 1);
                        caretBackwards = 2;
                        open = "{{";
                        close = "|}}";
                        break;
                    case "link":
                        open = "[[";
                        close = "]]";
                        caretBackwards = 0;
                        if (item[item.length - 1] == ')') item += '|';
                        break;
                }
                var lastbegin = txt.lastIndexOf(open, pos);
                if (txt[lastbegin + 2] == ':')
                    item = ':' + item;
 
                var newTxt = txt.substr(0, lastbegin) + open + item + close + txt.substr(pos + 1);
                var orgScroll = ctrl.scrollTop();
                ctrl.val(newTxt);
                ctrl.textSelection('setSelection', {
                    start: lastbegin + (open + item + close).length - caretBackwards
                });
                ctrl.scrollTop(orgScroll);
            }
        }, options);


var newTxt=txt.substr(0,lastbegin)+ open +item+ close + txt.substr(pos+1);
        function findLinks(res) {
var orgScroll=ctrl.scrollTop();
            var pos = ctrl.textSelection('getCaretPosition') - 1;
ctrl.val(newTxt);
            var txt = ctrl.val();
ctrl.textSelection('setSelection',{start:lastbegin + (open+item+close).length - caretBackwards});
ctrl.scrollTop(orgScroll);
}
}, options);


function findLinks(res){
            var lastbegin = txt.lastIndexOf("[[", pos);
var pos=ctrl.textSelection('getCaretPosition')-1;
            var lastend = txt.lastIndexOf("]]", pos);
var txt=ctrl.val();
            var isLink = lastbegin > lastend;
            if (isLink) {
                mode = 'link';
                fillLinksList(res, txt.substr(lastbegin + 2, pos - lastbegin));
            } else {
                lastbegin = txt.lastIndexOf("{{", pos);
                lastend = txt.lastIndexOf("}}", pos);
                var isTemplate = lastbegin > lastend;
                if (isTemplate) {
                    var prefixName = mw.config.get('wgFormattedNamespaces')[10] + ':' + txt.substr(lastbegin + 2, pos - lastbegin - 1);
                    mode = (prefixName.indexOf('|') > -1) ? 'templateParams' : 'template';
                    fillLinksList(res, prefixName);
                } else {
                    mode = "none";
                    res([]);
                }
            }
        }


var lastbegin=txt.lastIndexOf("[[",pos);
var lastend=txt.lastIndexOf("]]",pos);
var isLink=lastbegin>lastend;
if(isLink) {
fillLinksList(res,txt.substr(lastbegin+2,pos-lastbegin));
mode='link';
}
else{
lastbegin=txt.lastIndexOf("{{",pos);
lastend=txt.lastIndexOf("}}",pos);
var isTemplate=lastbegin>lastend;
if(isTemplate){
var prefixName=mw.config.get('wgFormattedNamespaces')[10]+':'+txt.substr(lastbegin+2,pos-lastbegin);
fillLinksList(res,prefixName);
mode='template';
}
else{
res([]);
mode="none";
}
}
}


function fillLinksList(res,txt){
        function resolveTempalte(templateName) {
        txt = $.trim(txt);
            var dfd = new jQuery.Deferred();
if(txt.length<=1 || txt.indexOf('|')>-1 || (txt.indexOf('#')>-1 && mw.config.get('wgNamespaceNumber')==0)) res([]);
            if (!templateName) return dfd.reject().promise();
else if(txt.indexOf('#')>-1){
            if (templateDataCache[templateName]) return dfd.resolve(templateDataCache[templateName]).promise();
var pageTitle=txt.substr(0,txt.indexOf('#'));
            var api = new mw.Api();
var sectionPrefix=txt.substr(txt.indexOf('#')+1);
            api.get({
$.getJSON(mw.util.wikiScript('api'),{action:'parse',page:pageTitle,prop:'sections',format:'json'},function(data){
                action: 'templatedata',
if(data && data.parse && data.parse.sections) res($(data.parse.sections).map(function(){return this.line.indexOf(sectionPrefix) ==0 ? (pageTitle+'#'+this.line):null;
                titles: templateName,
}));
                redirects: 1
});
            }).done(function(data) {
}
                if (!data.pages) return dfd.reject();
else $.getJSON(
                for (var pageid in data.pages) {
            mw.util.wikiScript('api'),
                    templateDataCache[templateName] = data.pages[pageid];
            {action:'opensearch', search:txt, format:'json'},
                    dfd.resolve(templateDataCache[templateName]);
            function(data){
                }
                 if(data[1])  
                 if (!templateDataCache[templateName]) dfd.reject();
                    res(settings.filterResponse(data[1]));
             });
             });
}
            return dfd.promise();
        }
 
        function resolveApi(queryType, queryValue) {
            var dfd = new jQuery.Deferred(),
                api = new mw.Api();
 
            switch (queryType) {
                case 'users':
                    api.get({
                        action: 'query',
                        list: 'allusers',
                        auactiveusers: 1,
                        auprefix: queryValue
                    }).done(function(data) {
                        if (data && data.query && data.query.allusers) dfd.resolve($.map(data.query.allusers, function(e) {
                            return e.name;
                        }));
                        else dfd.reject();
                    });
                    break;
                case 'pages':
                    api.get({
                        action: 'opensearch',
                        search: queryValue
                    }).done(function(data) {
                        if (data[1]) dfd.resolve(settings.filterResponse(data[1]));
                        else dfd.reject();
                    });
                    break;
                default:
                    throw 'unexpected queryType';
            }


ctrl.autocomplete( {
            return dfd.promise();
source: function( request, response ) {
        }
if(fixArrowsBug(this))
 
response([]);
        function fillLinksList(res, txt) {
else
            txt = $.trim(txt);
findLinks(response);
            if (txt.length <= 1 || (mode != 'templateParams' && txt.indexOf('|') > -1) || (txt.indexOf('#') > -1 && mw.config.get('wgNamespaceNumber') === 0)) res([]);
},
            else if (mode === 'templateParams') {
focus:function(){return false;},
                var templateMatch = /(.+?)\|(?:.*\|)?([^=]+$)/.exec(txt);
select:function(e,ui){
                var templateParamMatch = /(.+?)\|(?:.*\|)?([^=]+=[^=]+$)/.exec(txt);
settings.onselected(ui.item.value);return false;
 
},
                $.when(resolveTempalte((templateMatch && templateMatch[1]) || (templateParamMatch && templateParamMatch[1]))).done(function(td) {
open:function(){
 
$(".ui-autocomplete")
                    var curTemplateData = td,
.css(settings.menuCSS)
                    suggestions = [],
.position({
                    curParamIndex = txt.split('|').length - 1;
my: settings.positionMy,
if (templateParamMatch && !templateMatch) {
at: settings.positionAt,
var paramNameVal = templateParamMatch[2].split('=');
of: settings.positionOf,
if (paramNameVal[0] in curTemplateData.params && 'suggestedvalues' in curTemplateData.params[paramNameVal[0]]) {
offset: settings.positionOffset,
                                mode = 'templateValue';
collision: 'none fit'
                                for (const suggestedVal of curTemplateData.params[paramNameVal[0]].suggestedvalues) {
})
                                if (suggestedVal.indexOf(paramNameVal[1])==0) suggestions.push(paramNameVal[0] + '=' + suggestedVal);
.find('li').css(settings.itemCSS);
                                }
}
});
var fixed=false;
//this is hack to prevent known serious bug in autocomplete.js that prevent default of the up and down key which may drive you crazy....
function fixArrowsBug(self){
if(fixed) return false;
fixed=true;
ctrl.off("keydown.autocomplete");
ctrl.off("keydown.autocomplete0");
ctrl.on("keydown.autocomplete",
function(event) {
var keyCode = $.ui.keyCode;
switch( event.keyCode ) {
case keyCode.PAGE_UP:
self._move( "previousPage", event );
break;
case keyCode.PAGE_DOWN:
self._move( "nextPage", event );
break;
case keyCode.UP:
if (!self.menu.element.is(":visible")) return;
self._move( "previous", event );
// prevent moving cursor to beginning of text field in some browsers
event.preventDefault();
break;
case keyCode.DOWN:
if (!self.menu.element.is(":visible")) return;
self._move( "next", event );
// prevent moving cursor to end of text field in some browsers
event.preventDefault();
break;
case keyCode.ENTER:
case keyCode.NUMPAD_ENTER:
// when menu is open or has focus
if ( self.menu.active ) {
event.preventDefault();
}
//passthrough - ENTER and TAB both select the current element
case keyCode.TAB:
if ( !self.menu.active ) {
return;
}
self.menu.select( event );
break;
case keyCode.ESCAPE:
self.element.val( self.term );
self.close( event );
break;
case keyCode.LEFT:
case keyCode.RIGHT:
case keyCode.SHIFT:
case keyCode.CONTROL:
case keyCode.ALT:
case keyCode.COMMAND:
case keyCode.COMMAND_RIGHT:
case keyCode.INSERT:
case keyCode.CAPS_LOCK:
case keyCode.END:
case keyCode.HOME:
// ignore metakeys (shift, ctrl, alt)
break;
default:
// keypress is triggered before the input value is changed
clearTimeout( self.searching );
self.searching = setTimeout(function() {
self.search( null, event );
}, self.options.delay );
break;
}
}
});
} else {
return true;
                    for (var paramName in curTemplateData.params) {
}
                        if (paramName == curParamIndex) {
}
                            var paramValue = templateMatch[2];
                            var dfd;
                            switch (curTemplateData.params[paramName].type) {
                                case 'wiki-page-name':
                                    dfd = $.when(resolveApi('pages', paramValue));
                                    mode = 'templateValue';
                                    break;
                                case 'wiki-file-name':
                                    dfd = $.when(resolveApi('pages', 'File:' + paramValue));
                                    mode = 'templateValue';
                                    break;
                                case 'wiki-template-name':
                                    dfd = $.when(resolveApi('pages', 'Template:' + paramValue));
                                    mode = 'templateValue';
                                    break;                                   
                                case 'wiki-user-name':
                                    dfd = $.when(resolveApi('users', paramValue));
                                    mode = 'templateValue';
                                    break;
case 'string':
 
if ('suggestedvalues' in curTemplateData.params[paramName]){
        mode = 'templateValue';
                        for (const suggestedVal of curTemplateData.params[paramName].suggestedvalues) {
                        if (suggestedVal.indexOf(paramValue)==0) suggestions.push(suggestedVal);
                        }        
return res(suggestions);
} else
return res([]);
                                default:
                                    return res([]); // dont suggest for this indexed param
                            }
                            return dfd.done(res).fail(function() {
                                res([]);
                            });
                        }
                        if (paramName === '1' || txt.indexOf(paramName) > -1) continue; //dont suggest used params
                        suggestions.push(paramName);
                    }
                  }
                  res(suggestions);
                }).fail(res);
            } else if (txt.indexOf('#') > -1) {
                var pageTitle = txt.substr(0, txt.indexOf('#'));
                var sectionPrefix = txt.substr(txt.indexOf('#') + 1);
                var api = new mw.Api();
                api.get({
                    action: 'parse',
                    page: pageTitle,
                    prop: 'sections'
                }).done(function(data) {
                    if (data && data.parse && data.parse.sections) res($(data.parse.sections).map(function() {
                        return this.line.indexOf(sectionPrefix) == 0 ? (pageTitle + '#' + this.line.replace(/[|\[\]\{\}]/g, escape)) : null;
                    }));
                });
            } else {
                $.when(resolveApi('pages', txt)).done(res).fail(function() {
                    res([])
                });
            }
        }
 
        ctrl.autocomplete({
            source: function(request, response) {
                if (fixArrowsBug(this))
                    response([]);
                else
                    findLinks(response);
            },
            focus: function() {
                return false;
            },
            select: function(e, ui) {
                settings.onselected(ui.item.value);
                return false;
            },
            open: function() {
                $(".ui-autocomplete")
                    .css(settings.menuCSS)
                    .position({
                        my: settings.positionMy,
                        at: settings.positionAt,
                        of: settings.positionOf,
                        offset: settings.positionOffset,
                        collision: 'none fit'
                    })
                    .find('li').css(settings.itemCSS);
            }
        });
        var fixed, stfu, escapes = 0;
        //this is hack to prevent known serious bug in autocomplete.js that prevent default of the up and down key which may drive you crazy....
        function fixArrowsBug(self) {
            if (fixed) return false;
            fixed = true;
 
  // on click selection may change. close the menu
            ctrl.on("click.autocomplete", function(e){
clearTimeout(self.searching);
self.close();
  });
            ctrl.off("keydown.autocomplete");
            ctrl.off("keydown.autocomplete0");
            ctrl.on("keydown.autocomplete",
                function(event) {
                    var keyCode = $.ui.keyCode;
                    // hack to allow cancelling the gadget: mostly useful when editing templates.
                    escapes = event.keyCode == keyCode.ESCAPE ? escapes + 1 : 0;
                    if ( stfu || ( stfu = escapes >= 3 ) ) {
                        self.close(event);
                    return;
                    }
                    switch (event.keyCode) {
                        case keyCode.PAGE_UP:
                            self._move("previousPage", event);
                            break;
                        case keyCode.PAGE_DOWN:
                            self._move("nextPage", event);
                            break;
                        case keyCode.UP:
                            if (!self.menu.element.is(":visible")) return;
                            self._move("previous", event);
                            // prevent moving cursor to beginning of text field in some browsers
                            event.preventDefault();
                            break;
                        case keyCode.DOWN:
                            if (!self.menu.element.is(":visible")) return;
                            self._move("next", event);
                            // prevent moving cursor to end of text field in some browsers
                            event.preventDefault();
                            break;
                        case keyCode.ENTER:
                        case keyCode.NUMPAD_ENTER:
                            // when menu is open or has focus
                            if (self.menu.active) {
                                event.preventDefault();
                            }
                            //passthrough - ENTER and TAB both select the current element
                        case keyCode.TAB:
                            if (!self.menu.active) {
                                return;
                            }
                            self.menu.select(event);
                            break;
                        case keyCode.ESCAPE:
                            self.element.val(self.term);
                            self.close(event);
                            break;
 
                        case keyCode.SHIFT:
                        case keyCode.CONTROL:
                        case keyCode.ALT:
                        case keyCode.COMMAND:
                        case keyCode.COMMAND_RIGHT:
                        case keyCode.INSERT:
                        case keyCode.CAPS_LOCK:
                        case keyCode.END:
                        case keyCode.HOME:
                        case keyCode.LEFT:
                        case keyCode.RIGHT:                       
                            // ignore metakeys (shift, ctrl, alt)
                            break;
                        default:
                            // keypress is triggered before the input value is changed
                            clearTimeout(self.searching);
                            self.searching = setTimeout(function() {
                                self.search(null, event);
                            }, self.options.delay);
                            break;
                    }
                });
            return true;
        }
    }
});
});


if($.inArray(mw.config.get('wgAction'), ['edit', 'submit'])+1)
if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) + 1)
mw.loader.using(['jquery.ui.widget','jquery.ui.autocomplete','jquery.textSelection'],function(){
    mw.loader.using(['jquery.ui', 'jquery.textSelection'], function() {
//enable autocomplete for editbox, relative to editform in an offset of -80 vertical
        //enable autocomplete for editbox, relative to editform in an offset of -80 vertical
$( "#wpTextbox1" ).autoCompleteWikiText(
        $("#wpTextbox1").autoCompleteWikiText({
{
            positionAt: $('#wpTextbox1').prop('dir') == 'rtl' ? "left top" : "right top",
positionAt: $('#wpTextbox1').prop('dir')=='rtl'? "left top" : "right top",
            positionOf: '#editform',
positionOf: '#editform',  
            positionOffset: "0 0",
positionOffset: "0 0",
            menuCSS: {
menuCSS: { background:'#E0EEF7', opacity:0.8},
                background: '#E0EEF7',
itemCSS: {padding: 0, margin: 0 }
                opacity: 0.8
});
            },
});
            itemCSS: {
                padding: 0,
                margin: 0
            }
        });
    });

גרסה אחרונה מ־14:21, 4 ביולי 2024

/*
Autocomplete for links and templates
Written by [[משתמש:ערן]]
*/
mw.loader.using(['jquery.ui', 'jquery.textSelection'], function() {
    //extends jquery with autoCompleteWikiText functionality for autocomplete of links and templates
    $.fn.autoCompleteWikiText = function(options) {
        var mode = "none",
            templateDataCache = {},
            ctrl = $(this),
            settings = $.extend(true, {
            positionMy: $('body').is('.rtl') ? "left top" : "right top", // be default, open below the control
            positionAt: $('body').is('.rtl') ? "left bottom" : "right bottom",
            positionOf: ctrl,
            positionOffset: "0",
            filterResponse: function(a) {
                return a;
            }, // function that expects array of string and returns array of strings
            menuCSS: {
                width: 'auto',
                maxHeight: '30em',
                'overflow-y': 'auto'
            },
            itemCSS: {
                right: 'inherit'
            },
            onselected: function(item) {
                var pos = ctrl.textSelection('getCaretPosition') - 1,
                    txt = ctrl.val(),
                	open, close, caretBackwards;

                switch (mode) {
                    case "none":
                        return;
                    case "templateValue":
                        open = "|";
                        close = "";
                        caretBackwards = 0;
                        break;
                    case "templateParams":
                        open = "|";
                        close = "=";
                        caretBackwards = 0;
                        break;
                    case "template":
                        item = item.substr(mw.config.get('wgFormattedNamespaces')[10].length + 1);
                        caretBackwards = 2;
                        open = "{{";
                        close = "|}}";
                        break;
                    case "link":
                        open = "[[";
                        close = "]]";
                        caretBackwards = 0;
                        if (item[item.length - 1] == ')') item += '|';
                        break;
                }
                var lastbegin = txt.lastIndexOf(open, pos);
                if (txt[lastbegin + 2] == ':')
                    item = ':' + item;

                var newTxt = txt.substr(0, lastbegin) + open + item + close + txt.substr(pos + 1);
                var orgScroll = ctrl.scrollTop();
                ctrl.val(newTxt);
                ctrl.textSelection('setSelection', {
                    start: lastbegin + (open + item + close).length - caretBackwards
                });
                ctrl.scrollTop(orgScroll);
            }
        }, options);

        function findLinks(res) {
            var pos = ctrl.textSelection('getCaretPosition') - 1;
            var txt = ctrl.val();

            var lastbegin = txt.lastIndexOf("[[", pos);
            var lastend = txt.lastIndexOf("]]", pos);
            var isLink = lastbegin > lastend;
            if (isLink) {
                mode = 'link';
                fillLinksList(res, txt.substr(lastbegin + 2, pos - lastbegin));
            } else {
                lastbegin = txt.lastIndexOf("{{", pos);
                lastend = txt.lastIndexOf("}}", pos);
                var isTemplate = lastbegin > lastend;
                if (isTemplate) {
                    var prefixName = mw.config.get('wgFormattedNamespaces')[10] + ':' + txt.substr(lastbegin + 2, pos - lastbegin - 1);
                    mode = (prefixName.indexOf('|') > -1) ? 'templateParams' : 'template';
                    fillLinksList(res, prefixName);
                } else {
                    mode = "none";
                    res([]);
                }
            }
        }


        function resolveTempalte(templateName) {
            var dfd = new jQuery.Deferred();
            if (!templateName) return dfd.reject().promise();
            if (templateDataCache[templateName]) return dfd.resolve(templateDataCache[templateName]).promise();
            var api = new mw.Api();
            api.get({
                action: 'templatedata',
                titles: templateName,
                redirects: 1
            }).done(function(data) {
                if (!data.pages) return dfd.reject();
                for (var pageid in data.pages) {
                    templateDataCache[templateName] = data.pages[pageid];
                    dfd.resolve(templateDataCache[templateName]);
                }
                if (!templateDataCache[templateName]) dfd.reject();
            });
            return dfd.promise();
        }

        function resolveApi(queryType, queryValue) {
            var dfd = new jQuery.Deferred(),
                api = new mw.Api();

            switch (queryType) {
                case 'users':
                    api.get({
                        action: 'query',
                        list: 'allusers',
                        auactiveusers: 1,
                        auprefix: queryValue
                    }).done(function(data) {
                        if (data && data.query && data.query.allusers) dfd.resolve($.map(data.query.allusers, function(e) {
                            return e.name;
                        }));
                        else dfd.reject();
                    });
                    break;
                case 'pages':
                    api.get({
                        action: 'opensearch',
                        search: queryValue
                    }).done(function(data) {
                        if (data[1]) dfd.resolve(settings.filterResponse(data[1]));
                        else dfd.reject();
                    });
                    break;
                default:
                    throw 'unexpected queryType';
            }

            return dfd.promise();
        }

        function fillLinksList(res, txt) {
            txt = $.trim(txt);
            if (txt.length <= 1 || (mode != 'templateParams' && txt.indexOf('|') > -1) || (txt.indexOf('#') > -1 && mw.config.get('wgNamespaceNumber') === 0)) res([]);
            else if (mode === 'templateParams') {
                var templateMatch = /(.+?)\|(?:.*\|)?([^=]+$)/.exec(txt);
                var templateParamMatch = /(.+?)\|(?:.*\|)?([^=]+=[^=]+$)/.exec(txt);

                $.when(resolveTempalte((templateMatch && templateMatch[1]) || (templateParamMatch && templateParamMatch[1]))).done(function(td) {

                    var curTemplateData = td,
                    	suggestions = [],
                    	curParamIndex = txt.split('|').length - 1;
		if (templateParamMatch && !templateMatch) {
			var paramNameVal = templateParamMatch[2].split('=');
			if (paramNameVal[0] in curTemplateData.params && 'suggestedvalues' in curTemplateData.params[paramNameVal[0]]) {
                                mode = 'templateValue';
                                for (const suggestedVal of curTemplateData.params[paramNameVal[0]].suggestedvalues) {
                                	if (suggestedVal.indexOf(paramNameVal[1])==0) suggestions.push(paramNameVal[0] + '=' + suggestedVal);
                                }
			}
		} else {
                    for (var paramName in curTemplateData.params) {
                        if (paramName == curParamIndex) {
                            var paramValue = templateMatch[2];
                            var dfd;
                            switch (curTemplateData.params[paramName].type) {
                                case 'wiki-page-name':
                                    dfd = $.when(resolveApi('pages', paramValue));
                                    mode = 'templateValue';
                                    break;
                                case 'wiki-file-name':
                                    dfd = $.when(resolveApi('pages', 'File:' + paramValue));
                                    mode = 'templateValue';
                                    break;
                                case 'wiki-template-name':
                                    dfd = $.when(resolveApi('pages', 'Template:' + paramValue));
                                    mode = 'templateValue';
                                    break;                                    
                                case 'wiki-user-name':
                                    dfd = $.when(resolveApi('users', paramValue));
                                    mode = 'templateValue';
                                    break;
				case 'string':

				if ('suggestedvalues' in curTemplateData.params[paramName]){
				        mode = 'templateValue';
		                        for (const suggestedVal of curTemplateData.params[paramName].suggestedvalues) {
		                        	if (suggestedVal.indexOf(paramValue)==0) suggestions.push(suggestedVal);
		                        }				        
					return res(suggestions);
				} else
					return res([]);
                                default:
                                    return res([]); // dont suggest for this indexed param
                            }
                            return dfd.done(res).fail(function() {
                                res([]);
                            });
                        }
                        if (paramName === '1' || txt.indexOf(paramName) > -1) continue; //dont suggest used params
                        suggestions.push(paramName);
                    }
                  }
                  res(suggestions);
                }).fail(res);
            } else if (txt.indexOf('#') > -1) {
                var pageTitle = txt.substr(0, txt.indexOf('#'));
                var sectionPrefix = txt.substr(txt.indexOf('#') + 1);
                var api = new mw.Api();
                api.get({
                    action: 'parse',
                    page: pageTitle,
                    prop: 'sections'
                }).done(function(data) {
                    if (data && data.parse && data.parse.sections) res($(data.parse.sections).map(function() {
                        return this.line.indexOf(sectionPrefix) == 0 ? (pageTitle + '#' + this.line.replace(/[|\[\]\{\}]/g, escape)) : null;
                    }));
                });
            } else {
                $.when(resolveApi('pages', txt)).done(res).fail(function() {
                    res([])
                });
            }
        }

        ctrl.autocomplete({
            source: function(request, response) {
                if (fixArrowsBug(this))
                    response([]);
                else
                    findLinks(response);
            },
            focus: function() {
                return false;
            },
            select: function(e, ui) {
                settings.onselected(ui.item.value);
                return false;
            },
            open: function() {
                $(".ui-autocomplete")
                    .css(settings.menuCSS)
                    .position({
                        my: settings.positionMy,
                        at: settings.positionAt,
                        of: settings.positionOf,
                        offset: settings.positionOffset,
                        collision: 'none fit'
                    })
                    .find('li').css(settings.itemCSS);
            }
        });
        var fixed, stfu, escapes = 0;
        //this is hack to prevent known serious bug in autocomplete.js that prevent default of the up and down key which may drive you crazy....
        function fixArrowsBug(self) {
            if (fixed) return false;
            fixed = true;

	   // on click selection may change. close the menu
            ctrl.on("click.autocomplete", function(e){
		clearTimeout(self.searching);
		self.close();
	   });
            ctrl.off("keydown.autocomplete");
            ctrl.off("keydown.autocomplete0");
            ctrl.on("keydown.autocomplete",
                function(event) {
                    var keyCode = $.ui.keyCode;
                    // hack to allow cancelling the gadget: mostly useful when editing templates.
                    escapes = event.keyCode == keyCode.ESCAPE ? escapes + 1 : 0;
                    if ( stfu || ( stfu = escapes >= 3 ) ) {
                        self.close(event);
                    	return;
                    }
                    switch (event.keyCode) {
                        case keyCode.PAGE_UP:
                            self._move("previousPage", event);
                            break;
                        case keyCode.PAGE_DOWN:
                            self._move("nextPage", event);
                            break;
                        case keyCode.UP:
                            if (!self.menu.element.is(":visible")) return;
                            self._move("previous", event);
                            // prevent moving cursor to beginning of text field in some browsers
                            event.preventDefault();
                            break;
                        case keyCode.DOWN:
                            if (!self.menu.element.is(":visible")) return;
                            self._move("next", event);
                            // prevent moving cursor to end of text field in some browsers
                            event.preventDefault();
                            break;
                        case keyCode.ENTER:
                        case keyCode.NUMPAD_ENTER:
                            // when menu is open or has focus
                            if (self.menu.active) {
                                event.preventDefault();
                            }
                            //passthrough - ENTER and TAB both select the current element
                        case keyCode.TAB:
                            if (!self.menu.active) {
                                return;
                            }
                            self.menu.select(event);
                            break;
                        case keyCode.ESCAPE:
                            self.element.val(self.term);
                            self.close(event);
                            break;

                        case keyCode.SHIFT:
                        case keyCode.CONTROL:
                        case keyCode.ALT:
                        case keyCode.COMMAND:
                        case keyCode.COMMAND_RIGHT:
                        case keyCode.INSERT:
                        case keyCode.CAPS_LOCK:
                        case keyCode.END:
                        case keyCode.HOME:
                        case keyCode.LEFT:
                        case keyCode.RIGHT:                        	
                            // ignore metakeys (shift, ctrl, alt)
                            break;
                        default:
                            // keypress is triggered before the input value is changed
                            clearTimeout(self.searching);
                            self.searching = setTimeout(function() {
                                self.search(null, event);
                            }, self.options.delay);
                            break;
                    }
                });
            return true;
        }
    }
});

if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) + 1)
    mw.loader.using(['jquery.ui', 'jquery.textSelection'], function() {
        //enable autocomplete for editbox, relative to editform in an offset of -80 vertical
        $("#wpTextbox1").autoCompleteWikiText({
            positionAt: $('#wpTextbox1').prop('dir') == 'rtl' ? "left top" : "right top",
            positionOf: '#editform',
            positionOffset: "0 0",
            menuCSS: {
                background: '#E0EEF7',
                opacity: 0.8
            },
            itemCSS: {
                padding: 0,
                margin: 0
            }
        });
    });