diff --git a/Scribunto.i18n.php b/Scribunto.i18n.php
index bb8dde00..ac7fca2e 100644
--- a/Scribunto.i18n.php
+++ b/Scribunto.i18n.php
@@ -22,6 +22,18 @@ $messages['en'] = array(
'scribunto-error-long' => 'Script errors:
$1',
+
+ 'scribunto-console-intro' => '* The module exports are available as the variable "p", including unsaved modifications.
+* Precede a line with "=" to evaluate it as an expression, or use print().
+* Use mw.log() in module code to send messages to this console.',
+ 'scribunto-console-title' => 'Debug console',
+ 'scribunto-console-too-large' => 'This console session is too large. Please clear the console history or reduce the size of the module.',
+ 'scribunto-console-current-src' => 'console input',
+ 'scribunto-console-previous-src' => 'previous console input',
+ 'scribunto-console-clear' => 'Clear',
+ 'scribunto-console-cleared' => 'The console state was cleared because the module was updated.',
+
+
'scribunto-common-nosuchmodule' => 'Script error: No such module.',
'scribunto-common-nofunction' => 'Script error: You must specify a function to call.',
'scribunto-common-nosuchfunction' => 'Script error: The function you specified did not exist.',
diff --git a/Scribunto.php b/Scribunto.php
index 124f5327..cc0bbb7f 100644
--- a/Scribunto.php
+++ b/Scribunto.php
@@ -41,6 +41,7 @@ $wgAutoloadClasses['ScribuntoModuleBase'] = $dir.'common/Base.php';
$wgAutoloadClasses['ScribuntoHooks'] = $dir.'common/Hooks.php';
$wgAutoloadClasses['ScribuntoException'] = $dir.'common/Common.php';
$wgAutoloadClasses['Scribunto'] = $dir.'common/Common.php';
+$wgAutoloadClasses['ApiScribuntoConsole'] = $dir.'common/ApiScribuntoConsole.php';
$wgHooks['ParserFirstCallInit'][] = 'ScribuntoHooks::setupParserHook';
$wgHooks['ParserLimitReport'][] = 'ScribuntoHooks::reportLimits';
@@ -51,6 +52,7 @@ $wgHooks['ArticleViewCustom'][] = 'ScribuntoHooks::handleScriptView';
$wgHooks['TitleIsWikitextPage'][] = 'ScribuntoHooks::isWikitextPage';
$wgHooks['CodeEditorGetPageLanguage'][] = 'ScribuntoHooks::getCodeLanguage';
$wgHooks['EditPageBeforeEditChecks'][] = 'ScribuntoHooks::beforeEditChecks';
+$wgHooks['EditPageBeforeEditButtons'][] = 'ScribuntoHooks::beforeEditButtons';
$wgHooks['EditFilterMerged'][] = 'ScribuntoHooks::validateScript';
$wgHooks['UnitTestsList'][] = 'ScribuntoHooks::unitTestsList';
@@ -58,15 +60,30 @@ $wgParserTestFiles[] = $dir . 'tests/engines/LuaCommon/luaParserTests.txt';
$wgParserOutputHooks['ScribuntoError'] = 'ScribuntoHooks::parserOutputHook';
-$wgResourceModules['ext.scribunto'] = array(
+$sbtpl = array(
'localBasePath' => dirname( __FILE__ ) . '/modules',
'remoteExtPath' => 'Scribunto/modules',
+);
+
+$wgResourceModules['ext.scribunto'] = $sbtpl + array(
'scripts' => 'ext.scribunto.js',
'dependencies' => array( 'jquery.ui.dialog' ),
'messages' => array(
'scribunto-parser-dialog-title'
),
);
+$wgResourceModules['ext.scribunto.edit'] = $sbtpl + array(
+ 'scripts' => 'ext.scribunto.edit.js',
+ 'styles' => 'ext.scribunto.edit.css',
+ 'dependencies' => array( 'ext.scribunto' ),
+ 'messages' => array(
+ 'scribunto-console-title',
+ 'scribunto-console-intro',
+ 'scribunto-console-clear',
+ 'scribunto-console-cleared',
+ ),
+);
+$wgAPIModules['scribunto-console'] = 'ApiScribuntoConsole';
/***** Individual engines and their configurations *****/
diff --git a/common/ApiScribuntoConsole.php b/common/ApiScribuntoConsole.php
new file mode 100644
index 00000000..aa21fbe2
--- /dev/null
+++ b/common/ApiScribuntoConsole.php
@@ -0,0 +1,153 @@
+extractRequestParams();
+
+ $title = Title::newFromText( $params['title'] );
+ if ( !$title ) {
+ $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
+ }
+
+ if ( $params['session'] ) {
+ $sessionId = $params['session'];
+ } else {
+ $sessionId = mt_rand( 0, 0x7fffffff );
+ }
+
+ global $wgUser;
+ $sessionKey = wfMemcKey( 'scribunto-console', $wgUser->getId(), $sessionId );
+ $cache = ObjectCache::getInstance( CACHE_ANYTHING );
+ $session = null;
+ if ( $params['session'] ) {
+ $session = $cache->get( $sessionKey );
+ }
+ if ( !isset( $session['version'] ) ) {
+ $session = $this->newSession();
+ }
+
+ // Create a variable holding the session which will be stored if there
+ // are no errors. If there are errors, we don't want to store the current
+ // question to the state builder array, since that will cause subsequent
+ // requests to fail.
+ $newSession = $session;
+
+ if ( !empty( $params['clear'] ) ) {
+ $newSession['size'] -= strlen( implode( '', $newSession['questions'] ) );
+ $newSession['questions'] = array();
+ $session['questions'] = array();
+ }
+ if ( strlen( $params['question'] ) ) {
+ $newSession['size'] += strlen( $params['question'] );
+ $newSession['questions'][] = $params['question'];
+ }
+ if ( $params['content'] ) {
+ $newSession['size'] += strlen( $params['content'] ) - strlen( $newSession['content'] );
+ $newSession['content'] = $params['content'];
+ }
+
+ if ( $newSession['size'] > self::SC_MAX_SIZE ) {
+ $this->dieUsage( wfMsg( 'scribunto-console-too-large' ), 'scribunto-console-too-large' );
+ }
+ $result = $this->runConsole( array(
+ 'title' => $title,
+ 'content' => $newSession['content'],
+ 'prevQuestions' => $session['questions'],
+ 'question' => $params['question'] ) );
+
+ if ( $result['type'] === 'error' ) {
+ // Restore the questions array
+ $newSession['questions'] = $session['questions'];
+ }
+ $cache->set( $sessionKey, $newSession, self::SC_SESSION_EXPIRY );
+ $result['session'] = $sessionId;
+ $result['sessionSize'] = $newSession['size'];
+ $result['sessionMaxSize'] = self::SC_MAX_SIZE;
+ foreach ( $result as $key => $value ) {
+ $this->getResult()->addValue( null, $key, $value );
+ }
+ }
+
+ protected function runConsole( $params ) {
+ global $wgParser;
+ $options = new ParserOptions;
+ $options->setTemplateCallback( array( $this, 'templateCallback' ) );
+ $wgParser->startExternalParse( $params['title'], $options, Parser::OT_HTML, true );
+ $engine = Scribunto::getParserEngine( $wgParser );
+ try {
+ $result = $engine->runConsole( $params );
+ } catch ( ScribuntoException $e ) {
+ $trace = $e->getScriptTraceHtml();
+ $message = $e->getMessage();
+ $html = Html::element( 'p', array(), $message );
+ if ( $trace !== false ) {
+ $html .= Html::element( 'p', array(), wfMsgForContent( 'scribunto-common-backtrace' ) ) . $trace;
+ }
+
+ return array(
+ 'type' => 'error',
+ 'html' => $html,
+ 'message' => $message,
+ 'messagename' => $e->getMessageName() );
+ }
+ return array(
+ 'type' => 'normal',
+ 'print' => strval( $result['print'] ),
+ 'return' => strval( $result['return'] )
+ );
+ }
+
+ protected function newSession() {
+ return array(
+ 'content' => '',
+ 'questions' => array(),
+ 'size' => 0,
+ 'version' => 1,
+ );
+ }
+
+ public function getAllowedParams() {
+ return array(
+ 'title' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ),
+ 'content' => array(
+ ApiBase::PARAM_TYPE => 'string'
+ ),
+ 'session' => array(
+ ApiBase::PARAM_TYPE => 'integer',
+ ),
+ 'question' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true,
+ ),
+ 'clear' => array(
+ ApiBase::PARAM_TYPE => 'boolean',
+ ),
+ );
+ }
+
+ public function getParamDescription() {
+ return array(
+ 'title' => 'The module title to test',
+ 'content' => 'The new content of the module',
+ 'question' => 'The next line to evaluate as a script',
+ 'clear' => 'Set this to true to clear the current session state',
+ );
+ }
+
+ public function getDescription() {
+ return 'Internal module for servicing XHR requests from the Scribunto console';
+ }
+
+ public function getVersion() {
+ return __CLASS__.': 1';
+ }
+}
diff --git a/common/Base.php b/common/Base.php
index 7712dd81..3310f627 100644
--- a/common/Base.php
+++ b/common/Base.php
@@ -38,6 +38,21 @@ abstract class ScribuntoEngineBase {
*/
abstract protected function newModule( $text, $chunkName );
+ /**
+ * Run an interactive console request
+ *
+ * @param $params Associative array. Options are:
+ * - title: The title object for the module being debugged
+ * - content: The text content of the module
+ * - precedingQuestions: An array of previous "questions" used to establish the state
+ * - question: The current "question", a string script
+ *
+ * @return array containing:
+ * - print: The resulting print buffer
+ * - return: The resulting return value
+ */
+ abstract function runConsole( $params );
+
/**
* Constructor.
*
diff --git a/common/Hooks.php b/common/Hooks.php
index 65b6e09f..5ad6425f 100644
--- a/common/Hooks.php
+++ b/common/Hooks.php
@@ -213,6 +213,10 @@ class ScribuntoHooks {
* @param $tabindex Current tabindex
*/
public static function beforeEditChecks( &$editor, &$checkboxes, &$tabindex ) {
+ if ( $editor->getTitle()->getNamespace() !== NS_MODULE ) {
+ return true;
+ }
+
$req = RequestContext::getMain()->getRequest();
$name = 'scribunto_ignore_errors';
@@ -224,6 +228,26 @@ class ScribuntoHooks {
Xml::check( $name, $req->getCheck( $name ), $attribs ) .
' ' .
Xml::label( wfMsg( 'scribunto-ignore-errors' ), "mw-$name" );
+
+ // While we're here, lets set up the edit module
+ global $wgOut;
+ $wgOut->addModules( 'ext.scribunto.edit' );
+ $editor->editFormTextAfterTools = '
';
+ return true;
+ }
+
+ /**
+ * EditPageBeforeEditButtons hook
+ * @param $editor EditPage
+ * @param $buttons Button array
+ * @param $tabindex Current tabindex
+ */
+ public static function beforeEditButtons( &$editor, &$buttons, &$tabindex ) {
+ if ( $editor->getTitle()->getNamespace() !== NS_MODULE ) {
+ return true;
+ }
+
+ unset( $buttons['preview'] );
return true;
}
diff --git a/engines/LuaCommon/LuaCommon.php b/engines/LuaCommon/LuaCommon.php
index cce7d771..cb4bee3a 100644
--- a/engines/LuaCommon/LuaCommon.php
+++ b/engines/LuaCommon/LuaCommon.php
@@ -107,6 +107,51 @@ abstract class Scribunto_LuaEngine extends ScribuntoEngineBase {
return 'lua';
}
+ public function runConsole( $params ) {
+ /**
+ * TODO: provide some means for giving correct line numbers for errors
+ * in console input, and for producing an informative error message
+ * if there is an error in prevQuestions.
+ *
+ * Maybe each console line could be evaluated as a different chunk,
+ * apparently that's what lua.c does.
+ */
+ $code = "return function (__init)\n" .
+ "local p = mw.executeModule(__init)\n" .
+ "local print = mw.log\n";
+ foreach ( $params['prevQuestions'] as $q ) {
+ if ( substr( $q, 0, 1 ) === '=' ) {
+ $code .= "print(" . substr( $q, 1 ) . ")";
+ } else {
+ $code .= $q;
+ }
+ $code .= "\n";
+ }
+ $code .= "mw.clearLogBuffer()\n";
+ if ( substr( $params['question'], 0, 1 ) === '=' ) {
+ // Treat a statement starting with "=" as a return statement, like in lua.c
+ $code .= "return tostring(" . substr( $params['question'], 1 ) . "), mw.getLogBuffer()\n";
+ } else {
+ $code .= $params['question'] . "\n" .
+ "return nil, mw.getLogBuffer()\n";
+ }
+ $code .= "end\n";
+
+ $contentModule = $this->newModule(
+ $params['content'], $params['title']->getPrefixedDBkey() );
+ $contentInit = $contentModule->getInitChunk();
+
+ $consoleModule = $this->newModule( $code, wfMsg( 'scribunto-console-current-src' ) );
+ $consoleInit = $consoleModule->getInitChunk();
+ $ret = $this->getInterpreter()->callFunction( $consoleInit );
+ $func = $ret[0];
+ $ret = $this->getInterpreter()->callFunction( $func, $contentInit );
+ return array(
+ 'return' => isset( $ret[0] ) ? $ret[0] : null,
+ 'print' => isset( $ret[1] ) ? $ret[1] : '',
+ );
+ }
+
/**
* Workalike for luaL_checktype()
*
@@ -376,7 +421,7 @@ class Scribunto_LuaModule extends ScribuntoModuleBase {
}
class Scribunto_LuaError extends ScribuntoException {
- var $luaMessage;
+ var $luaMessage, $lineMap = array();
function __construct( $message, $options = array() ) {
$this->luaMessage = $message;
@@ -394,6 +439,10 @@ class Scribunto_LuaError extends ScribuntoException {
return $this->luaMessage;
}
+ function setLineMap( $map ) {
+ $this->lineMap = $map;
+ }
+
function getScriptTraceHtml( $options = array() ) {
if ( !isset( $this->params['trace'] ) ) {
return false;
@@ -406,13 +455,17 @@ class Scribunto_LuaError extends ScribuntoException {
$s = '';
foreach ( $this->params['trace'] as $info ) {
- $src = htmlspecialchars( $info['short_src'] );
- if ( $info['currentline'] > 0 ) {
- $src .= ':' . htmlspecialchars( $info['currentline'] );
+ $short_src = $srcdefined = $info['short_src'];
+ $currentline = $info['currentline'];
+ $linedefined = $info['linedefined'];
- $title = Title::newFromText( $info['short_src'] );
+ $src = htmlspecialchars( $short_src );
+ if ( $currentline > 0 ) {
+ $src .= ':' . htmlspecialchars( $currentline );
+
+ $title = Title::newFromText( $short_src );
if ( $title && $title->getNamespace() === NS_MODULE ) {
- $title->setFragment( '#mw-ce-l' . $info['currentline'] );
+ $title->setFragment( '#mw-ce-l' . $currentline );
$src = Html::rawElement( 'a',
array( 'href' => $title->getFullURL( 'action=edit' ) ),
$src );
@@ -427,7 +480,7 @@ class Scribunto_LuaError extends ScribuntoException {
$function = '?';
} else {
$function = wfMsgExt( 'scribunto-lua-in-function-at',
- $msgOptions, $info['short_src'], $info['linedefined'] );
+ $msgOptions, $srcdefined, $linedefined );
}
$s .= "- \n\t" .
wfMsgExt( 'scribunto-lua-backtrace-line', $msgOptions, "$src", $function ) .
diff --git a/engines/LuaCommon/lualib/mw.lua b/engines/LuaCommon/lualib/mw.lua
index d61d29e5..698d9bc2 100644
--- a/engines/LuaCommon/lualib/mw.lua
+++ b/engines/LuaCommon/lualib/mw.lua
@@ -5,6 +5,7 @@ local packageModuleFunc
local php
local setupDone
local allowEnvFuncs = false
+local logBuffer = ''
--- Put an isolation-friendly package module into the specified environment
-- table. The package module will have an empty cache, because caching of
@@ -382,4 +383,17 @@ function mw.executeFunction( chunk )
return table.concat( stringResults )
end
+function mw.log( msg )
+ logBuffer = logBuffer .. tostring( msg ) .. '\n'
+end
+
+function mw.clearLogBuffer()
+ logBuffer = ''
+end
+
+function mw.getLogBuffer()
+ return logBuffer
+end
+
+
return mw
diff --git a/modules/ext.scribunto.edit.css b/modules/ext.scribunto.edit.css
new file mode 100644
index 00000000..4d8d441d
--- /dev/null
+++ b/modules/ext.scribunto.edit.css
@@ -0,0 +1,55 @@
+
+.mw-scribunto-console-fieldset {
+ background: white;
+ color: black;
+}
+
+/* Preserve line breaks, but wrap too if browser supports it */
+#mw-scribunto-output {
+ white-space: pre;
+ white-space: -moz-pre-wrap;
+}
+
+#mw-scribunto-input {
+ width: 100%;
+ border: none;
+ padding: 0;
+ overflow: auto;
+ background: #e0e0e0;
+}
+.mw-scribunto-input {
+ color: blue;
+ font: inherit;
+ font-weight: bold;
+ margin-top: .5em;
+}
+.mw-scribunto-normalOutput {
+ color: black;
+ background: white;
+}
+.mw-scribunto-print {
+ color: #630;
+ background: white;
+}
+.mw-scribunto-error {
+ color: red;
+ background: white;
+}
+.mw-scribunto-propList {
+ color: green;
+ background: white;
+}
+.mw-scribunto-message {
+ color: green;
+ background: white;
+}
+.mw-scribunto-tabcomplete {
+ color: purple;
+ background: white;
+}
+.mw-scribunto-clear {
+ color: red;
+ text-align: center;
+ margin-top: 1em;
+ border-bottom: 1px solid red;
+}
diff --git a/modules/ext.scribunto.edit.js b/modules/ext.scribunto.edit.js
new file mode 100644
index 00000000..13a8074b
--- /dev/null
+++ b/modules/ext.scribunto.edit.js
@@ -0,0 +1,380 @@
+(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,
+ _in,
+ _out,
+ lastError = null,
+ sessionContent = null,
+ sessionKey = null,
+ pending = false,
+ clearNextRequest = false;
+
+function refocus()
+{
+ _in.blur(); // Needed for Mozilla to scroll correctly.
+ _in.focus();
+}
+
+function initConsole()
+{
+ _in = document.getElementById( "mw-scribunto-input" );
+ _out = document.getElementById( "mw-scribunto-output" );
+
+ recalculateInputHeight();
+ println( mw.msg( 'scribunto-console-intro' ), 'mw-scribunto-message' );
+}
+
+function inputKeydown( e ) {
+ // Use onkeydown because IE doesn't support onkeypress for arrow keys
+
+ 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( _in ) )
+ hist( 'up' );
+ } else if ( e.keyCode == 40 ) { // down
+ // go down in history if at end or ctrl-down
+ if ( e.ctrlKey || caretInLastLine( _in ) )
+ hist( 'down' );
+ } else { }
+
+ setTimeout( recalculateInputHeight, 0 );
+
+ //return true;
+}
+
+function inputFocus( e ) {
+ if ( sessionContent === null ) {
+ // No previous state to clear
+ return;
+ }
+
+ if ( clearNextRequest ) {
+ // User already knows
+ return;
+ }
+
+ if ( getContent() !== sessionContent ) {
+ printClearBar();
+ clearNextRequest = true;
+ }
+}
+
+function caretInFirstLine( textbox )
+{
+ // IE doesn't support selectionStart/selectionEnd
+ if ( textbox.selectionStart == undefined )
+ return true;
+
+ var firstLineBreak = textbox.value.indexOf( "\n" );
+
+ return ((firstLineBreak == -1) || (textbox.selectionStart <= firstLineBreak));
+}
+
+function caretInLastLine( textbox )
+{
+ // IE doesn't support selectionStart/selectionEnd
+ if ( textbox.selectionEnd == undefined )
+ return true;
+
+ var lastLineBreak = textbox.value.lastIndexOf( "\n" );
+
+ return ( textbox.selectionEnd > lastLineBreak );
+}
+
+function recalculateInputHeight()
+{
+ var rows = _in.value.split( /\n/ ).length
+ + 1 // prevent scrollbar flickering in Mozilla
+ + ( window.opera ? 1 : 0 ); // leave room for scrollbar in Opera
+
+ // without this check, it is impossible to select text in Opera 7.60 or Opera 8.0.
+ if ( _in.rows != rows )
+ _in.rows = rows;
+}
+
+function println( s, type )
+{
+ if( ( s = String( s ) ) )
+ {
+ var newdiv = document.createElement( "div" );
+ newdiv.appendChild( document.createTextNode( s ) );
+ newdiv.className = type;
+ _out.appendChild( newdiv );
+ return newdiv;
+ }
+}
+
+function printWithRunin( h, s, type )
+{
+ var div = println( s, type );
+ var head = document.createElement( "strong" );
+ head.appendChild( document.createTextNode( h + ": " ) );
+ div.insertBefore( head, div.firstChild );
+}
+
+function printClearBar()
+{
+ $( '' )
+ .attr( 'class', 'mw-scribunto-clear' )
+ .text( mw.msg( 'scribunto-console-cleared' ) )
+ .appendTo( _out );
+}
+
+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] = _in.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() {
+ _in.value = '';
+ _in.value = histList[histPos];
+ var caretPos = _in.value.length;
+ if (_in.setSelectionRange)
+ _in.setSelectionRange(caretPos, caretPos);
+ },
+ 0
+ );
+ }
+ }
+ else // down
+ {
+ if ( histPos < L-1 )
+ {
+ histPos++;
+ _in.value = histList[histPos];
+ }
+ else if ( histPos == L-1 )
+ {
+ // Already on the current entry: clear but save
+ if ( _in.value )
+ {
+ histList[histPos] = _in.value;
+ ++histPos;
+ _in.value = "";
+ }
+ }
+ }
+}
+
+function printQuestion( q )
+{
+ println( q, "mw-scribunto-input" );
+}
+
+function printError( er )
+{
+ var lineNumberString;
+
+ lastError = er; // for debugging the shell
+ 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
+ println( er, "mw-scribunto-error" ); // Because security errors in Moz /only/ have toString.
+}
+
+function setPending() {
+ pending = true;
+ _in.readOnly = true;
+}
+
+function clearPending() {
+ pending = false;
+ _in.readOnly = false;
+}
+
+function go()
+{
+ 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 = _in.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.
+ _in.value = '';
+ // can't preventDefault on input, so also clear it later
+ setTimeout( function() { _in.value = ""; }, 0 );
+
+ recalculateInputHeight();
+ printQuestion(question);
+
+ var params = {
+ action: 'scribunto-console',
+ title: mw.config.get( 'wgTitle' ),
+ question: question,
+ }
+
+ var content = getContent();
+ if ( !sessionKey || sessionContent !== content ) {
+ params.clear = true;
+ params.content = content;
+ }
+ if ( sessionKey ) {
+ params.session = sessionKey;
+ }
+ if ( clearNextRequest ) {
+ params.clear = true;
+ clearNextRequest = false;
+ }
+
+ var api = new mw.Api();
+ setPending();
+
+ api.post( params, {
+ ok: function( result ) {
+ sessionKey = result.session;
+ sessionContent = content;
+ if ( result.type === 'error' ) {
+ printError( result.message );
+ } else {
+ if ( result.print !== '' ) {
+ println( result.print, 'mw-scribunto-print' );
+ }
+ if ( result['return'] !== '' ) {
+ println( result['return'], "mw-scribunto-normalOutput" );
+ }
+ }
+ clearPending();
+ setTimeout( refocus, 0 );
+ },
+
+ err: function( code, result ) {
+ if ( 'error' in result && 'info' in result.error ) {
+ printError( result.error.info );
+ } else if ( 'exception' in result ) {
+ printError( result.exception.message );
+ } else {
+ console.log( result );
+ printError( 'error' );
+ }
+ clearPending();
+ setTimeout( refocus, 0 );
+ },
+ } );
+}
+
+function getContent() {
+ var $textarea = $( '#wpTextbox1' );
+ var context = $textarea.data( 'wikiEditor-context' );
+ if ( context == undefined || context.codeEditor == undefined ) {
+ return $textarea.val();
+ } else {
+ return $textarea.textSelection( 'getContents' );
+ }
+}
+
+function onClearClick( e ) {
+ $( '#mw-scribunto-output' ).empty();
+ clearNextRequest = true;
+ refocus();
+}
+
+mw.scribunto.edit = {
+ 'init': function () {
+ var action = mw.config.get( 'wgAction' );
+ if ( action == 'edit' || action == 'submit' || action == 'editredlink' ) {
+ this.initEditPage();
+ }
+ },
+
+ 'initEditPage': function() {
+ var console = document.getElementById( 'mw-scribunto-console' );
+ if ( !console ) {
+ return;
+ }
+
+ $( '' )
+ .attr( 'class', 'mw-scribunto-console-fieldset' )
+ .append( $( '' ).text( mw.msg( 'scribunto-console-title' ) ) )
+ .append( $( '' ) )
+ .append(
+ $( '' ).append(
+ $( '' )
+ .attr({
+ id: 'mw-scribunto-input',
+ 'class': 'mw-scribunto-input',
+ wrap: 'off',
+ rows: 1
+ })
+ .bind( 'keydown', inputKeydown )
+ .bind( 'focus', inputFocus )
+ )
+ )
+ .append(
+ $( '' ).append(
+ $( '' )
+ .attr({
+ type: 'button',
+ value: mw.msg( 'scribunto-console-clear' )
+ })
+ .bind( 'click', onClearClick )
+ )
+ )
+ .wrap( '' )
+ .appendTo( console );
+ initConsole();
+ }
+};
+
+$(document).ready( function() {
+ mw.scribunto.edit.init();
+});
+
+})( jQuery, mediaWiki );
+
diff --git a/modules/ext.scribunto.js b/modules/ext.scribunto.js
index 63839d20..9ebf1299 100644
--- a/modules/ext.scribunto.js
+++ b/modules/ext.scribunto.js
@@ -7,7 +7,7 @@ mw.scribunto = {
this.errors = errors;
},
- 'init': function () {
+ 'init': function() {
var regex = /^mw-scribunto-error-(\d+)/;
var that = this;
var dialog = $( '' );
@@ -39,7 +39,7 @@ mw.scribunto = {
.dialog( 'open' );
} );
} );
- }
+ },
};
$(document).ready( function() {