diff options
Diffstat (limited to 'www/wiki/extensions/Scribunto/modules/ext.scribunto.edit.js')
-rw-r--r-- | www/wiki/extensions/Scribunto/modules/ext.scribunto.edit.js | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/www/wiki/extensions/Scribunto/modules/ext.scribunto.edit.js b/www/wiki/extensions/Scribunto/modules/ext.scribunto.edit.js new file mode 100644 index 00000000..ff533899 --- /dev/null +++ b/www/wiki/extensions/Scribunto/modules/ext.scribunto.edit.js @@ -0,0 +1,398 @@ +/* eslint-disable no-use-before-define */ + +( function ( $, mw ) { + + /** + * Debug console + * Based on JavaScript Shell 1.4 by Jesse Ruderman (GPL/LGPL/MPL tri-license) + * + * TODO: + * * Refactor, more jQuery, etc. + * * Spinner? + * * A prompt in front of input lines and the textarea + * * Collapsible backtrace display + */ + + var histList = [ '' ], + histPos = 0, + question, + input, + output, + $spinner, + sessionContent = null, + sessionKey = null, + pending = false, + clearNextRequest = false; + + function refocus() { + // Needed for Mozilla to scroll correctly + input.blur(); + input.focus(); + } + + function initConsole() { + input = document.getElementById( 'mw-scribunto-input' ); + output = document.getElementById( 'mw-scribunto-output' ); + $spinner = $.createSpinner( { size: 'small', type: 'block' } ); + + recalculateInputHeight(); + println( mw.msg( 'scribunto-console-intro' ), 'mw-scribunto-message' ); + } + + /** + * Use onkeydown because IE doesn't support onkeypress for arrow keys + * + * @param {jQuery.Event} e + */ + function inputKeydown( e ) { + if ( e.shiftKey && e.keyCode === 13 ) { + // shift-enter + // don't do anything; allow the shift-enter to insert a line break as normal + } else if ( e.keyCode === 13 ) { + // enter + // execute the input on enter + go(); + } else if ( e.keyCode === 38 ) { + // up + // go up in history if at top or ctrl-up + if ( e.ctrlKey || caretInFirstLine( input ) ) { + hist( 'up' ); + } + } else if ( e.keyCode === 40 ) { + // down + // go down in history if at end or ctrl-down + if ( e.ctrlKey || caretInLastLine( input ) ) { + hist( 'down' ); + } + } + + setTimeout( recalculateInputHeight, 0 ); + } + + function inputFocus() { + if ( sessionContent === null ) { + // No previous state to clear + return; + } + + if ( clearNextRequest ) { + // User already knows + return; + } + + if ( getContent() !== sessionContent ) { + printClearBar( 'scribunto-console-cleared' ); + clearNextRequest = true; + } + } + + function caretInFirstLine( textbox ) { + var firstLineBreak; + + // IE doesn't support selectionStart/selectionEnd + if ( textbox.selectionStart === undefined ) { + return true; + } + + firstLineBreak = textbox.value.indexOf( '\n' ); + + return ( ( firstLineBreak === -1 ) || ( textbox.selectionStart <= firstLineBreak ) ); + } + + function caretInLastLine( textbox ) { + var lastLineBreak; + + // IE doesn't support selectionStart/selectionEnd + if ( textbox.selectionEnd === undefined ) { + return true; + } + + lastLineBreak = textbox.value.lastIndexOf( '\n' ); + + return ( textbox.selectionEnd > lastLineBreak ); + } + + function recalculateInputHeight() { + var rows = input.value.split( /\n/ ).length + + // prevent scrollbar flickering in Mozilla + 1 + + // leave room for scrollbar in Opera + ( window.opera ? 1 : 0 ); + + // without this check, it is impossible to select text in Opera 7.60 or Opera 8.0. + if ( input.rows !== rows ) { + input.rows = rows; + } + } + + function println( s, type ) { + var newdiv; + if ( ( s = String( s ) ) ) { + newdiv = document.createElement( 'div' ); + newdiv.appendChild( document.createTextNode( s ) ); + newdiv.className = type; + output.appendChild( newdiv ); + return newdiv; + } + } + + function printClearBar( msg ) { + $( '<div>' ) + .attr( 'class', 'mw-scribunto-clear' ) + .text( mw.msg( msg ) ) + .appendTo( output ); + } + + function hist( direction ) { + // histList[0] = first command entered, [1] = second, etc. + // type something, press up --> thing typed is now in "limbo" + // (last item in histList) and should be reachable by pressing + // down again. + + var L = histList.length; + + if ( L === 1 ) { + return; + } + + if ( direction === 'up' ) { + if ( histPos === L - 1 ) { + // Save this entry in case the user hits the down key. + histList[ histPos ] = input.value; + } + + if ( histPos > 0 ) { + histPos--; + // Use a timeout to prevent up from moving cursor within new text + // Set to nothing first for the same reason + setTimeout( + function () { + var caretPos; + input.value = ''; + input.value = histList[ histPos ]; + caretPos = input.value.length; + if ( input.setSelectionRange ) { + input.setSelectionRange( caretPos, caretPos ); + } + }, + 0 + ); + } + } else { + // direction down + if ( histPos < L - 1 ) { + histPos++; + input.value = histList[ histPos ]; + } else if ( histPos === L - 1 ) { + // Already on the current entry: clear but save + if ( input.value ) { + histList[ histPos ] = input.value; + ++histPos; + input.value = ''; + } + } + } + } + + function printQuestion( q ) { + println( q, 'mw-scribunto-input' ); + } + + function printError( er ) { + var lineNumberString; + + if ( er.name ) { + // lineNumberString should not be '', to avoid a very wacky bug in IE 6. + lineNumberString = ( er.lineNumber !== undefined ) ? ( ' on line ' + er.lineNumber + ': ' ) : ': '; + // Because IE doesn't have error.toString. + println( er.name + lineNumberString + er.message, 'mw-scribunto-error' ); + } else { + // Because security errors in Moz /only/ have toString. + println( er, 'mw-scribunto-error' ); + } + } + + function setPending() { + pending = true; + input.readOnly = true; + $spinner.insertBefore( input ); + } + + function clearPending() { + $spinner.remove(); + pending = false; + input.readOnly = false; + } + + function go() { + var params, api, content, sentContent; + + if ( pending ) { + // If there is an XHR request pending, don't send another one + // We set readOnly on the textarea to give a UI indication, this is + // just for paranoia. + return; + } + + question = input.value; + + if ( question === '' ) { + return; + } + + histList[ histList.length - 1 ] = question; + histList[ histList.length ] = ''; + histPos = histList.length - 1; + + // Unfortunately, this has to happen *before* the script is run, so that + // print() output will go in the right place. + input.value = ''; + // can't preventDefault on input, so also clear it later + setTimeout( function () { + input.value = ''; + }, 0 ); + + recalculateInputHeight(); + printQuestion( question ); + + params = { + action: 'scribunto-console', + title: mw.config.get( 'wgPageName' ), + question: question + }; + + content = getContent(); + sentContent = false; + + if ( !sessionKey || sessionContent !== content ) { + params.clear = true; + params.content = content; + sentContent = true; + } + if ( sessionKey ) { + params.session = sessionKey; + } + if ( clearNextRequest ) { + params.clear = true; + clearNextRequest = false; + } + + api = new mw.Api(); + setPending(); + + api.post( params ) + .done( function ( result ) { + if ( result.sessionIsNew === '' && !sentContent ) { + // Session was lost. Resend query, with content + printClearBar( 'scribunto-console-cleared-session-lost' ); + sessionContent = null; + clearPending(); + input.value = params.question; + go(); + return; + } + sessionKey = result.session; + sessionContent = content; + if ( result.type === 'error' ) { + $( '<div>' ).addClass( 'mw-scribunto-error' ).html( result.html ).appendTo( output ); + } else { + if ( result.print !== '' ) { + println( result.print, 'mw-scribunto-print' ); + } + if ( result.return !== '' ) { + println( result.return, 'mw-scribunto-normalOutput' ); + } + } + clearPending(); + setTimeout( refocus, 0 ); + } ) + .fail( function ( code, result ) { + if ( result.error && result.error.info ) { + printError( result.error.info ); + } else if ( result.exception ) { + printError( 'Error sending API request: ' + result.exception ); + } else { + mw.log( result ); + printError( 'error' ); + } + clearPending(); + setTimeout( refocus, 0 ); + } ); + } + + function getContent() { + var $textarea = $( '#wpTextbox1' ), + context = $textarea.data( 'wikiEditor-context' ); + + if ( context === undefined || context.codeEditor === undefined ) { + return $textarea.val(); + } else { + return $textarea.textSelection( 'getContents' ); + } + } + + function onClearClick() { + $( '#mw-scribunto-output' ).empty(); + clearNextRequest = true; + refocus(); + } + + function initEditPage() { + var $wpTextbox1, + $console = $( '#mw-scribunto-console' ); + if ( !$console.length ) { + // There is no console in the DOM; on read-only (protected) pages, + // we need to add it here, because the hook does not insert + // it server-side. + $wpTextbox1 = $( '#wpTextbox1' ); + if ( !$wpTextbox1.length || !$wpTextbox1.prop( 'readonly' ) ) { + return; + } + + $console = $( '<div>' ).attr( { id: 'mw-scribunto-console' } ); + $wpTextbox1.after( $console ); + } + + $( '<fieldset>' ) + .attr( 'class', 'mw-scribunto-console-fieldset' ) + .append( $( '<legend>' ).text( mw.msg( 'scribunto-console-title' ) ) ) + .append( $( '<div id="mw-scribunto-output"></div>' ) ) + .append( + $( '<div>' ).append( + $( '<textarea>' ) + .attr( { + id: 'mw-scribunto-input', + 'class': 'mw-scribunto-input', + wrap: 'off', + rows: 1, + dir: 'ltr', + lang: 'en' + } ) + .bind( 'keydown', inputKeydown ) + .bind( 'focus', inputFocus ) + ) + ) + .append( + $( '<div>' ).append( + $( '<input>' ) + .attr( { + type: 'button', + value: mw.msg( 'scribunto-console-clear' ) + } ) + .bind( 'click', onClearClick ) + ) + ) + .wrap( '<form>' ) + .appendTo( $console ); + + initConsole(); + } + + $( function () { + var action = mw.config.get( 'wgAction' ); + if ( action === 'edit' || action === 'submit' || action === 'editredlink' ) { + initEditPage(); + } + } ); + +}( jQuery, mediaWiki ) ); |