User:Theopolisme/Scripts/autocompleter.js

/** * autocompleter.js * Bringing tab-based autocompletion to the mediawiki edit interface! * * @see User:Theopolisme/Scripts/autocompleter * @author Theopolisme */ /* global jQuery, mediaWiki */ ( function ( $, mw ) {	'use strict';

var DELIMTER = /[\[\:\s|]/, MATCHERS = [ // Usernames: User (talk):$$, , /(?:\[\[user(?:[_ ]talk)?:(.*?)[\|\]#]|\{\{(?:ping|u)\|(.*?)\}\})/gi, // Wikipages: Xyz, Xyz, 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 {		mw.loader.using( 'mediawiki.util', function  { if ( [ 'edit', 'submit' ].indexOf( mw.util.getParamValue( 'action' ) ) !== -1 ) { mw.loader.using( 'jquery.textSelection', function {					var autocompleter = new Autocompleter( $( '#wpTextbox1' ) );					autocompleter.listen;				} ); }		});	} );

}( jQuery, mediaWiki ) );