mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Scribunto
synced 2024-11-24 00:05:00 +00:00
Debug console module
* Added a debug console to the edit page, allowing unsaved modules to be tested. * Removed the "preview" button from the edit page. * Only show the "ignore code errors" checkbox on module edit pages, not all edit pages. * Added Lua function mw.log() for sending messages to the debug log. Change-Id: Ia51f439e573a1deb5b83f94ddd1a86792d5569c1
This commit is contained in:
parent
16e9eba133
commit
b5c36bad59
|
@ -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.',
|
||||
|
|
|
@ -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 *****/
|
||||
|
||||
|
|
153
common/ApiScribuntoConsole.php
Normal file
153
common/ApiScribuntoConsole.php
Normal file
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* API module for serving debug console requests on the edit page
|
||||
*/
|
||||
|
||||
class ApiScribuntoConsole extends ApiBase {
|
||||
const SC_MAX_SIZE = 500000;
|
||||
const SC_SESSION_EXPIRY = 3600;
|
||||
|
||||
public function execute() {
|
||||
$params = $this->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';
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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 = '<div id="mw-scribunto-console"></div>';
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = '<ol class="scribunto-trace">';
|
||||
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 .= "<li>\n\t" .
|
||||
wfMsgExt( 'scribunto-lua-backtrace-line', $msgOptions, "<strong>$src</strong>", $function ) .
|
||||
|
|
|
@ -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
|
||||
|
|
55
modules/ext.scribunto.edit.css
Normal file
55
modules/ext.scribunto.edit.css
Normal file
|
@ -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;
|
||||
}
|
380
modules/ext.scribunto.edit.js
Normal file
380
modules/ext.scribunto.edit.js
Normal file
|
@ -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()
|
||||
{
|
||||
$( '<div/>' )
|
||||
.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;
|
||||
}
|
||||
|
||||
$( '<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
|
||||
})
|
||||
.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();
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready( function() {
|
||||
mw.scribunto.edit.init();
|
||||
});
|
||||
|
||||
})( jQuery, mediaWiki );
|
||||
|
|
@ -7,7 +7,7 @@ mw.scribunto = {
|
|||
this.errors = errors;
|
||||
},
|
||||
|
||||
'init': function () {
|
||||
'init': function() {
|
||||
var regex = /^mw-scribunto-error-(\d+)/;
|
||||
var that = this;
|
||||
var dialog = $( '<div/>' );
|
||||
|
@ -39,7 +39,7 @@ mw.scribunto = {
|
|||
.dialog( 'open' );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
$(document).ready( function() {
|
||||
|
|
Loading…
Reference in a new issue