MediaWiki:Gadget-TabOverride.js: Difference between revisions

From Zelda Dungeon Wiki
Jump to navigation Jump to search
Want an adless experience? Log in or Create an account.
(Created page)
 
(No difference)

Latest revision as of 21:41, October 2, 2012

// register the keydown event listener on the content textarea element
// after the window is loaded to make sure that the element is accessible
// using the document's getElementById method
jQuery( document ).ready( function() {
	// only on edit pages (action=edit in the URL)
	if ( mw.config.get( 'wgAction' ) !== 'edit' ) {
		return;
	}
 
	var content = jQuery( '#wpTextbox1' ); // the MediaWiki page edit textarea
 
	// if there is no content textarea element on this page, do nothing
	if ( !content.length ) {
		return;
	}
 
	content.keydown( function( e ) {
		var text, // initial text in the textarea
			range, // the IE TextRange object
			tempRange, // used to calculate selection start and end positions in IE
			preNewlines, // the number of newline (\r\n) characters before the selection start (for IE)
			selNewlines, // the number of newline (\r\n) characters within the selection (for IE)
			initScrollTop, // initial scrollTop value to fix scrolling in Firefox
			selStart, // the selection start position
			selEnd, // the selection end position
			sel, // the selected text
			startLine, // for multi-line selections, the first character position of the first line
			endLine, // for multi-line selections, the last character position of the last line
			numTabs, // the number of tabs inserted / removed in the selection
			startTab, // if a tab was removed from the start of the first line
			preTab; // if a tab was removed before the start of the selection
 
		// tab key - insert / remove tab
		if ( e.keyCode === 9 ) {
			// initialize variables
			text = this.value;
			initScrollTop = this.scrollTop; // scrollTop is supported by all modern browsers
			numTabs = 0;
			startTab = 0;
			preTab = 0;
 
			if ( typeof this.selectionStart !== 'undefined' ) {
				selStart = this.selectionStart;
				selEnd = this.selectionEnd;
				sel = text.slice( selStart, selEnd );
			} else if ( document.selection ) { // IE
				range = document.selection.createRange();
				sel = range.text;
				tempRange = range.duplicate();
				tempRange.moveToElementText( this );
				tempRange.setEndPoint( 'EndToEnd', range );
				selEnd = tempRange.text.length;
				selStart = selEnd - sel.length;
				// whenever the value of the textarea is changed, the range needs to be reset
				// IE (and Opera) use both \r and \n for newlines - this adds an extra character
				// that needs to be accounted for when doing position calculations
				// these values are used to offset the selection start and end positions
				preNewlines = text.slice( 0, selStart ).split( '\r\n' ).length - 1;
				selNewlines = sel.split( '\r\n' ).length - 1;
			} else {
				// cannot access textarea selection - do nothing
				return;
			}
 
			// special case of multi-line selection
			if ( selStart !== selEnd && sel.indexOf( '\n' ) !== -1 ) {
				// for multiple lines, only insert / remove tabs from the beginning of each line
 
				// find the start of the first selected line
				if ( selStart === 0 || text.charAt( selStart - 1 ) === '\n' ) {
					// the selection starts at the beginning of a line
					startLine = selStart;
				} else {
					// the selection starts after the beginning of a line
					// set startLine to the beginning of the first partially selected line
					// subtract 1 from selStart in case the cursor is at the newline character,
					// for instance, if the very end of the previous line was selected
					// add 1 to get the next character after the newline
					// if there is none before the selection, lastIndexOf returns -1
					// when 1 is added to that it becomes 0 and the first character is used
					startLine = text.lastIndexOf( '\n', selStart - 1 ) + 1;
				}
 
				// find the end of the last selected line
				if ( selEnd === text.length || text.charAt( selEnd ) === '\n' ) {
					// the selection ends at the end of a line
					endLine = selEnd;
				} else {
					// the selection ends before the end of a line
					// set endLine to the end of the last partially selected line
					endLine = text.indexOf( '\n', selEnd );
					if ( endLine === -1 ) {
						endLine = text.length;
					}
				}
 
				// if the shift key was pressed, remove tabs instead of inserting them
				if ( e.shiftKey ) {
					if ( text.charAt( startLine ) === '\t' ) {
						// is this tab part of the selection?
						if ( startLine === selStart ) {
							// it is, remove it
							sel = sel.slice( 1 );
						} else {
							// the tab comes before the selection
							preTab = 1;
						}
						startTab = 1;
					}
 
					this.value = text.slice( 0, startLine ) + text.slice( startLine + preTab, selStart ) +
						sel.replace( /\n\t/g, function() {
							numTabs += 1;
							return '\n';
						}) + text.slice( selEnd );
 
					// set start and end points
					if ( range ) { // IE
						// setting end first makes calculations easier
						range.collapse();
						range.moveEnd( 'character', selEnd - startTab - numTabs - selNewlines - preNewlines );
						range.moveStart( 'character', selStart - preTab - preNewlines );
						range.select();
					} else {
						// set start first for Opera
						this.selectionStart = selStart - preTab; // preTab is 0 or 1
						// move the selection end over by the total number of tabs removed
						this.selectionEnd = selEnd - startTab - numTabs;
					}
				} else {
					// no shift key
					// insert tabs at the beginning of each line of the selection
					this.value = text.slice( 0, startLine ) + '\t' + text.slice( startLine, selStart ) +
						sel.replace( /\n/g, function() {
							numTabs += 1;
							return '\n\t';
						}) + text.slice( selEnd );
 
					// set start and end points
					if ( range ) { // IE
						range.collapse();
						range.moveEnd( 'character', selEnd + 1 - preNewlines ); // numTabs cancels out selNewlines
						range.moveStart( 'character', selStart + 1 - preNewlines );
						range.select();
					} else {
						// the selection start is always moved by 1 character
						this.selectionStart = selStart + 1;
						// move the selection end over by the total number of tabs inserted
						this.selectionEnd = selEnd + 1 + numTabs;
					}
				}
			} else {
				// "normal" case (no selection or selection on one line only)
 
				// if the shift key was pressed, remove a tab instead of inserting one
				if ( e.shiftKey ) {
					// if the character before the selection is a tab, remove it
					if ( text.charAt( selStart - 1 ) === '\t' ) {
						this.value = text.slice( 0, selStart - 1 ) + text.slice( selStart );
 
						// set start and end points
						if ( range ) { // IE
							// collapses range and moves it by -1 character
							range.move( 'character', selStart - 1 - preNewlines );
							range.select();
						} else {
							this.selectionEnd = this.selectionStart = selStart - 1;
						}
					}
				} else {
					// no shift key - insert a tab
					if ( range ) { // IE
						// if no text is selected and the cursor is at the beginning of a line
						// (except the first line), IE places the cursor at the carriage return character
						// the tab must be placed after the \r\n pair
						if ( text.charAt( selStart ) === '\r' ) {
							this.value = text.slice( 0, selStart + 2 ) + '\t' + text.slice( selEnd + 2 );
							// collapse the range and move it to the appropriate location
							range.move( 'character', selStart + 2 - preNewlines );
						} else {
							this.value = text.slice( 0, selStart ) + '\t' + text.slice( selEnd );
							// collapse the range and move it to the appropriate location
							range.move( 'character', selStart + 1 - preNewlines );
						}
						range.select();
					} else {
						this.value = text.slice( 0, selStart ) + '\t' + text.slice( selEnd );
						this.selectionEnd = this.selectionStart = selStart + 1;
					}
				}
			}
 
			// this is really just for Firefox, but will be executed by all browsers
			// whenever the textarea value property is reset, Firefox scrolls back to the top
			// this will reset it to the original scroll value
			this.scrollTop = initScrollTop;
 
			// prevent the default action
			if ( e.preventDefault ) {
				e.preventDefault();
			}
			e.returnValue = false;
		}
	} ).keypress( function( e ) {
		// Opera (and Firefox) also fire a keypress event when the tab key is pressed
		// Opera requires that the default action be prevented on this event, or the
		// textarea will lose focus (preventDefault is enough, IE never fires this
		// for the tab key)
		if ( e.keyCode === 9 && e.preventDefault ) {
			e.preventDefault();
		}
	} );
} );