ميدياويكي:Gadget-autocompleter.js
ملاحظة: بعد الحفظ، قد يلزمك إفراغ الكاش لرؤية التغييرات.
//[[en:User:Theopolisme/Scripts/autocompleter]]
/**
* autocompleter.js
* Bringing tab-based autocompletion to the mediawiki edit interface!
*
* @see [[User:Theopolisme/Scripts/autocompleter]]
* @author Theopolisme
*/
/* global jQuery, mediaWiki */
mw.loader.using('mediawiki.util').then(function () {
'use strict';
var DELIMTER = /[\[\:\s|]/,
MATCHERS = [
// Usernames: [[User (talk):$$$]], {{ping|$$$}}, {{u|$$$}}
/(?:\[\[user(?:[_ ]talk)?:(.*?)[\|\]#]|\{\{(?:ping|u)\|(.*?)\}\})/gi,
// Wikipages: [[Xyz]], [[Wikipedia:Xyz]], [[Talk:Foo|Bar]]
/\[\[(.*?)(?:\||\]\])/g
];
function log () {
var args = Array.prototype.slice.call( arguments );
if ( console && console.log ) {
args.unshift( '[autocompleter]' );
console.log.apply( console, args );
}
}
function Autocompleter ( $textarea ) {
this.$textarea = $textarea;
this.isListening = false;
this.cache = [];
this.updateCache();
}
Autocompleter.prototype.updateCache = function () {
var i, j, matcher, match, value,
cache = this.cache,
content = this.$textarea.val();
for ( i = 0; i < MATCHERS.length; i++ ) {
matcher = MATCHERS[i];
match = matcher.exec( content );
while ( match !== null ) {
j = match.length - 1;
do {
value = match[j];
j--;
} while ( value === undefined );
if ( cache.indexOf( value ) === -1 ) {
cache.push( value );
}
match = matcher.exec( content );
}
}
this.cache = cache;
log( 'cache updated', this.cache );
};
Autocompleter.prototype.autocomplete = function () {
var ac = this,
pattern, completions, currentCompletion,
content = this.$textarea.val(),
caretPosition = this.$textarea.textSelection( 'getCaretPosition' );
function findPattern( content, caretPosition ) {
var piece = content.substring( 0, caretPosition ),
i = piece.length;
while ( i >= 0 ) {
if ( DELIMTER.test( piece[i] ) ) {
return piece.substring( i + 1 ).toLowerCase();
}
i--;
}
log( 'could not find a delimeter' );
return false;
}
function complete( pattern ) {
var i, cache = ac.cache,
completions = [];
for ( i = 0; i < cache.length; i++ ) {
if ( cache[i].toLowerCase().indexOf( pattern ) === 0 ) {
completions.push( cache[i] );
}
}
return completions;
}
function updateTextarea ( content, caretPosition, pattern, completion ) {
var start = caretPosition - pattern.length,
end = start + completion.length,
newContent = content.substring( 0, start ) + completion + content.substring( caretPosition );
ac.$textarea.val( newContent );
ac.$textarea.textSelection( 'setSelection', {
start: start,
end: end
} );
}
pattern = findPattern( content, caretPosition );
completions = complete( pattern );
currentCompletion = 0;
log( 'pattern', pattern );
log( 'completions', completions );
if ( !completions.length ) {
log( 'could not find a match' );
return;
}
updateTextarea( content, caretPosition, pattern, completions[ currentCompletion ] );
// Allow the user to "scroll" through matches
function keydownHandler ( e ) {
switch ( e.which ) {
case 38: // up arrow
currentCompletion += 1;
if ( currentCompletion > completions.length - 1 ) {
currentCompletion = 0;
}
break;
case 40: // down arrow
currentCompletion -= 1;
if ( currentCompletion < 0 ) {
currentCompletion = completions.length - 1;
}
break;
default:
ac.$textarea.off( 'keydown', keydownHandler );
return;
}
updateTextarea( content, caretPosition, pattern, completions[ currentCompletion ] );
e.preventDefault();
return false;
}
this.$textarea.on( 'keydown', keydownHandler );
};
Autocompleter.prototype.listen = function () {
var ac = this;
if ( this.isListening ) {
return;
}
this.$textarea.on( 'keydown', function ( e ) {
if ( e.which === 9 ) { // tab
e.preventDefault();
ac.autocomplete();
return false;
}
} );
this.isListening = true;
};
$( document ).ready( function () {
if ( [ 'edit', 'submit' ].indexOf( mw.util.getParamValue( 'action' ) ) !== -1 ) {
mw.loader.using( 'jquery.textSelection', function () {
var autocompleter = new Autocompleter( $( '#wpTextbox1' ) );
autocompleter.listen();
} );
}
} );
});