Extension:JsMath

From MediaWiki.org
Jump to: navigation, search
MediaWiki extensions manual - list
Crystal Clear action run.png
JsMath

Release status: beta

Description Math rendering with the javascript library jsMath.
Author(s) Tommy Ekola
Latest version 0.9
MediaWiki tested with 1.11.0
License GPL
Download Here
Hooks used
SiteNoticeAfter

RenderPreferencesForm
ParserBeforeStrip

Translate the JsMath extension if possible

Check usage and version matrix; code metrics

JsMath is an extension that provides an additional math rendering mode using the javascript library jsMath. This extension will add an extra option "jsMath" to the math tab in the preferences form. When the user selects this rendering mode the javascript library jsMath will be included on pages and math formulas written using the <math> tag (in wiki texts) will be output inside special tags that are recognized and processed by jsMath on the user's web browser.

The main benefits of using jsMath, instead of the normal math rendering, are primarily that

  1. formulas resize when the text on a page is enlarged,
  2. it is possible to get high-quality print output, and
  3. it works on almost all browsers (compare this to MathML).

The main drawback is that it takes time for jsMath to process math formulas on the client, especially if a page contains a lot of math. A user might therefore only want to enable jsMath when he or she needs to do a print-out.

As an alternative: The MathJax javascript library, which is the jsMath replacement, can be used through the MathJax extension. This extension supports TeX style labeling (with automatic numbering) and referencing through \label{} and \eqref{}. (Comment placed here by the author of the MathJax extension Dirk Nuyens 22:13, 15 November 2010 (UTC))

Installation[edit | edit source]

Install jsMath[edit | edit source]

Detailed instructions are available from the homepage of jsMath.

Configure jsMath[edit | edit source]

Detailed instructions are available from the homepage of jsMath.

This extension assumes that jsMath is configured through the file easy/load.js. You can edit this file to your heart's content, but below are some recommendations:

  1. Turn off jsMath's special interpretation of \(, \[, $ and $$ as math mode delimiters.
    processSlashParens: 0,       // process \(...\) in text?
    processSlashBrackets: 0,     // process \[...\] in text?
    processDoubleDollars: 0,     // process $$...$$ in text?
    processSingleDollars: 0,     // process $...$ in text?
    processLaTeXenvironments: 0, // process \begin{xxx}...\end{xxx} outside math mode?
    fixEscapedDollars: 0,        // convert \$ to $ outside of math mode?
    
  2. Load AMSmath and some extra fonts. Notice that you also need to install the extra fonts cmbsy10, cmmib10, msam10 and msbm10 on the server, as outlined on this page.
    loadFiles: ["extensions/AMSmath.js", "extensions/AMSsymbols.js",
                "extensions/boldsymbol.js", "extensions/moreArrows.js"],
    loadFonts: ["cmmib10", "cmbsy10"],
    

Save the code[edit | edit source]

Create the directory $IP/extensions/JsMath and save the php code in the files JsMath.php and JsMath.i18n.php, respectively, in that directory.

Configure the extension[edit | edit source]

First of all you need to have TeX support installed (see this page). Make sure the file $IP/LocalSettings.php contains the line

$wgUseTeX = true;

Then you add the following lines at the end of the file $IP/LocalSettings.php

$wgJsMathRoot = ".../jsMath";
require_once( "$IP/extensions/JsMath/JsMath.php" );

where $wgJsMathRoot is the root directory of the javascript library jsMath on the server (it should have the same value as the root variable in easy/load.js).

If you want jsMath rendering to be the default for users then you should add the following line before JsMath.php is loaded

$wgJsMathDefault = 1;

If you want to include any plugins to jsMath that cannot be included in easy/load.js then you should add them to the $wgJsMathPlugins array before JsMath.php is loaded. For example, to load the smallFonts.js plugin you write

$wgJsMathPlugins = array( $wgJsMathRoot . "/plugins/smallFonts.js" );

The Code[edit | edit source]

JsMath.php[edit | edit source]

<?php
 
/* 
 * @author Tommy Ekola
 * @copyright © 2008 by Tommy Ekola (tek@kth.se)
 * @licence GNU General Public Licence 2 or later
 */
 
 
if (! defined( 'MEDIAWIKI' ) ) {
        echo( "This is an extension to the MediaWiki package and cannot be run standalone.\n" );
        die( -1 );
}
 
 
// Extension information
 
$wgExtensionCredits['other'][] = array(
        'name'        => 'JsMath',
        'description' => 'jsMath rendering of math formulas',
        'version'     => '0.9',
        'author'      => 'Tommy Ekola',
        'url'         => 'http://www.mediawiki.org/wiki/Extension:JsMath');
 
 
// Configuration
 
if(! isset( $wgJsMathRoot )) {
        echo( "The root directory of jsMath is not specified. Make sure \$wgJsMathRoot is set in $IP/LocalSettings.php" );
        die( -1 );
}
 
define( 'MW_MATH_JSMATH', 6 );
if( $wgJsMathDefault ) {
        $wgDefaultUserOptions['math'] = MW_MATH_JSMATH;
}
 
 
// Load jsMath on the page
 
$wgExtensionFunctions[] = 'wfJsMathSetup';
 
function wfJsMathSetup() {
  global $wgOut, $wgJsMimeType, $wgJsMathRoot, $wgUseTeX, $wgJsMathPlugins;
 
        if(! $wgUseTeX ) return;
 
        wfJsMathMessages();        
 
        // Put jsMath div tags in the content part of the page
        $wgOut->addInlineScript( "jsMath = {Setup: {DIV: function(id,styles) {\n"
                . "  var div = jsMath.document.createElement('div');\n"
                . "  div.id = 'jsMath_'+id;\n"
                . "  for (var i in styles) {div.style[i]= styles[i]}\n"
                . "  var el = jsMath.document.getElementById('content');\n"
                . "  if(!el) el = jsMath.document.getElementById('globalWrapper');\n"
                . "  if(!el) el = jsMath.document.body;\n"
                . "  if(!el) return;\n"
                . "  if (!el.hasChildNodes) {\n"
                . "    el.appendChild(div);\n"
                . "  }\n"
                . "  else {\n"
                . "    el.insertBefore(div,el.firstChild);\n"
                . "  }\n"
                . "  return div;\n"
                . "}}}" );
        if( isset( $wgJsMathPlugins )) {
                foreach( $wgJsMathPlugins as $plugin ) {
                        $wgOut->addScript( '<script type="' . $wgJsMimeType . '" src="'
                                . $plugin . '"></script>' );
                }
        }
        $wgOut->addScript( '<script type="' . $wgJsMimeType . '" src="'
                . $wgJsMathRoot . '/easy/load.js"></script>' );
}
 
 
// Internationalization
 
function wfJsMathMessages() {
        static $messagesLoaded = false;
        global $wgMessageCache;
        if ( !$messagesLoaded ) {
                $messagesLoaded = true;
 
                require( dirname( __FILE__ ) . '/JsMath.i18n.php' );
                foreach( $messages as $lang => $langMessages ) {
                        $wgMessageCache->addMessages( $langMessages, $lang );
                }
        }
        return true;
}
 
 
// Warn the user if javascript is turned off.
 
$wgHooks['SiteNoticeAfter'][] = 'wfJsMathNotice';
 
function wfJsMathNotice( &$sitenotice ) {
        global $wgUseTeX;
 
        if(! $wgUseTeX ) return true;
        $sitenotice .= "\n<noscript>\n"
                ."  <div style='color:#CC0000; text-align:center'>\n"
                ."    <b>Warning: "
                ."<a href='http://www.math.union.edu/locate/jsMath'>jsMath</a>"
                ." requires JavaScript\n"
                ."    to process the mathematics on this page.<br>\n"
                ."    If your browser supports JavaScript, be sure it"
                ." is enabled.</b>\n"
                ."  </div>\n"
                ."  <hr>\n"
                ."</noscript>";
        return true;
}
 
 
// Add a jsMath option to the math part of the preferences form.
 
$wgHooks['RenderPreferencesForm'][] = 'wfJsMathPreferences';
 
function wfJsMathPreferences( $preferences, $output) {
        global $wgUseTeX;
 
        if(! $wgUseTeX ) return true;
 
        $html = $output->getHTML();
 
        // Create html option for jsMath
        wfJsMathMessages();
        $checked = ( MW_MATH_JSMATH == $preferences->mMath );
        $jsmathopt = Xml::openElement( 'div' )
                . Xml::radioLabel( wfMsg( 'mw_math_jsmath' ), 'wpMath',
                        MW_MATH_JSMATH, "mw-sp-math-" . MW_MATH_JSMATH, $checked )
                . Xml::closeElement( 'div' ) . "\n";
 
        // Insert jsMath option into the form        
        $i = strpos( $html, "<fieldset>\n<legend>" . wfMsg('math') . '</legend>' );
        if( $i !== FALSE ) {
                $newhtml = substr($html,0,$i)
                        . preg_replace( '(</fieldset>)', $jsmathopt . '</fieldset>', substr($html,$i,-1), 1 );
 
                // Replace the old output with the new output        
                $output->clearHTML();
                $output->addHTML( $newhtml );
        }
 
        return true;
}
 
 
// Strip the math tag before the parser does, and associate the
// replacement marker with the corresponding jsMath code.
 
$wgHooks['ParserBeforeStrip'][] = 'wfJsMathParser';
 
function wfJsMathParser(&$parser, &$text, &$state) {
        global $wgUser, $wgContLang;
        if( $parser->mOptions->getUseTeX() && $wgUser->getOption('math') == MW_MATH_JSMATH ) {
                $render = ($parser->mOutputType == OT_HTML);
 
                $uniq_prefix = $parser->mUniqPrefix;
                $commentState = new ReplacementArray;
                $nowikiState = new ReplacementArray;
                $generalItems = array();
 
                $elements = array('math','nowiki');
                $matches = array();
                $text = Parser::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
 
                foreach( $matches as $marker => $data ) {
                        list( $element, $content, $params, $tag ) = $data;
                        if( $render ) {
                                $tagName = strtolower( $element );
                                switch( $tagName ) {
                                case '!--':
                                        // Comment
                                        if( substr( $tag, -3 ) == '-->' ) {
                                                $output = $tag;
                                        } else {
                                                // Unclosed comment in input.
                                                // Close it so later stripping can remove it
                                                $output = "$tag-->";
                                        }
                                        break;
                                case 'nowiki':
                                        $output = $tag;
                                        break;
                                case 'math':
                                        $attribs = Sanitizer::validateTagAttributes( $params, $tag );
                                        $attribs = Sanitizer::mergeAttributes( array('class' => 'math'), $attribs );
                                        $output = $wgContLang->armourMath( 
                                                Xml::tags( 'span', $attribs, "\\displaystyle " . $content ));
                                        break;
                                }
                        }
 
                        if( $element == '!--' ) {
                                $commentState->setPair( $marker, $output );
                        } elseif ( $element == 'nowiki' ) {
                                $nowikiState->setPair( $marker, $output );
                        } elseif ( $element == 'math' ) {
                                $generalItems[$marker] = $output;
                        } else {
                                throw new MWException( "Caught invalid tag $element in math strip" );
                        }
                }
                # Add the math items to the state
                $state->general->mergeArray( $generalItems );
 
                # Unstrip nowiki and comments 
                $text = $nowikiState->replace( $text );
                $text = $commentState->replace( $text );
        }
        return true;        
}

JsMath.i18n.php[edit | edit source]

<?php
 
/* 
 * @author Tommy Ekola
 * @copyright © 2008 by Tommy Ekola (tek@kth.se)
 * @licence GNU General Public Licence 2 or later
 */
 
$messages = 
    array(
      'en' =>
        array('mw_math_jsmath' => 'jsMath'));