User:Tommy Ekola/LogCalculator
From MediaWiki.org
|
LogCalculator Release status: beta |
|||
|---|---|---|---|
| Implementation | Variable | ||
| Description | A basic javascript calculator with logarithm button | ||
| Author(s) | Tommy Ekola | ||
| Last version | 0.9 | ||
| MediaWiki | tested with 1.11.1 | ||
| License | GPL | ||
| Download | Here | ||
| Example | [1] | ||
|
|||
LogCalculator is an extension that defines a variable {{LOGCALCULATOR}} which, when used in a wikitext, includes a simple javascript calculator with a logarithm button on the resulting page.
Contents |
[edit] Example
If you include the following line in a wikitext
{{LOGCALCULATOR}}
then a javascript calculator is included in the resulting page:
(This is just an image. On the real calculator you can click on the keys.)
[edit] Installation
[edit] Save the code
Create the directory $IP/extensions/LogCalculator and save the code in the files LogCalculator.php, LogCalculator.js and LogCalculator.css, respectively, in that directory.
[edit] Configure the extension
Add the following lines at the end of the file $IP/LocalSettings.php
$wgLogCalculatorRoot = "/wiki/extensions/LogCalculator"; include_once( $IP . "/extensions/LogCalculator/LogCalculator.php" );
where
- $wgLogCalculatorRoot is the path to the javascript file LogCalculator.js and style file LogCalculator.css.
[edit] The code
[edit] LogCalculator.php
<?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' => 'LogCalculator', 'description' => 'A basic javascript calculator with logarithm button', 'version' => '0.9', 'author' => 'Tommy Ekola'); // Configuration if(! isset($wgLogCalculatorRoot) ) { echo( "The root directory of LogCalculator is not specified. Make sure \$wgLogCalculatorRoot is set in $IP/LocalSettings.php" ); die( -1 ); } // Include javascript and style files $wgExtensionFunctions[] = 'wfLogCalculatorSetup'; function wfLogCalculatorSetup() { global $wgOut, $wgLogCalculatorRoot, $wgJsMimeType; $wgOut->addScript( '<link rel="stylesheet" type="text/css" ' . 'href="' . $wgLogCalculatorRoot . '/LogCalculator.css" />' ); $wgOut->addScript( '<script type="' . $wgJsMimeType . '" src="' . $wgLogCalculatorRoot . '/LogCalculator.js"></script>' ); } // Define {{LOGCALCULATOR}} define( 'LOGCALCULATOR', 'logcalculator' ); $wgHooks['LanguageGetMagic'][] = 'wfLogCalculator_Magic'; function wfLogCalculator_Magic( &$MagicWords, &$langCode ) { $MagicWords[LOGCALCULATOR] = array( 0, 'log-calculator', 'logcalculator' ); return true; } $wgHooks['ParserGetVariableValueSwitch'][] = 'wfLogCalculatorVarAssign'; function wfLogCalculatorVarAssign( &$parser, &$cache, &$magicWordId, &$ret ) { if( $magicWordId == LOGCALCULATOR ) { $ret = "<div>"; $ret .= "<form onsubmit=\"keyclick('='); return false;\">"; $ret .= "<div id=\"logcalculator\" class=\"drag\">"; $ret .= "<div class=\"logcalculatorcontainer\">"; $ret .= "<input type=\"text\" name=\"input\" size=\"16\" id=\"result\" value=\"0\"><br/>"; $ret .= "<input type=\"button\" value=\"AC\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"C\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"LN\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"×\" onclick=\"keyclick(this.value)\"><br/>"; $ret .= "<input type=\"button\" value=\"7\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"8\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"9\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"÷\" onclick=\"keyclick(this.value)\"><br/>"; $ret .= "<input type=\"button\" value=\"4\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"5\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"6\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"+\" onclick=\"keyclick(this.value)\"><br/>"; $ret .= "<input type=\"button\" value=\"1\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"2\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"3\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"-\" onclick=\"keyclick(this.value)\"><br/>"; $ret .= "<input type=\"button\" value=\"+/-\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"0\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\".\" onclick=\"keyclick(this.value)\"><input type=\"button\" value=\"=\" onclick=\"keyclick(this.value)\">"; $ret .= "</div>"; $ret .= "</div>"; $ret .= "</form>"; $ret .= "</div>"; } return true; } $wgHooks['MagicWordwgVariableIDs'][] = 'wfLogCalculatorDeclareVarIds'; function wfLogCalculatorDeclareVarIds( &$aCustomVariableIds ) { $aCustomVariableIds[] = LOGCALCULATOR; return true; }
[edit] LogCalculator.js
function Stack(initial_stack,initial_opstack) { this.stack = new Array(); this.opstack = new Array(); }; Stack.prototype.top = function() { return this.stack[this.stack.length-1]; }; Stack.prototype.topop = function() { return this.opstack[this.opstack.length-1]; }; Stack.prototype.pop = function() { return this.stack.pop(); }; Stack.prototype.popop = function() { return this.opstack.pop(); }; Stack.prototype.push = function(a) { return this.stack.push(a); }; Stack.prototype.pushop = function(a) { return this.opstack.push(a); }; Stack.prototype.clear = function() { this.stack = []; this.opstack = []; }; Stack.prototype.reduce = function() { while(this.opstack.length>0) { op = this.popop(); this.op(op); } }; Stack.prototype.empty = function() { if (this.stack.length==0) return true; else return false; }; Stack.prototype.opempty = function() { if (this.opstack.length==0) return true; else return false; }; Stack.prototype.op = function(op) { switch(op) { case '+': a = this.pop(); b = this.pop(); try { c = parseFloat(a)+parseFloat(b); } catch(err) { this.clear(); this.push('Error'); state = 'error_state'; break; } if(!isFinite(c)) { this.clear(); this.push('Error'); state = 'error_state'; break; } this.push(c.toString()); break; case '-': a = this.pop(); b = this.pop(); try { c = parseFloat(b)-parseFloat(a); } catch(err) { this.clear(); this.push('Error'); state = 'error_state'; break; } if(!isFinite(c)) { this.clear(); this.push('Error'); state = 'error_state'; break; } this.push(c.toString()); break; case '×': a = this.pop(); b = this.pop(); try { c = parseFloat(a)*parseFloat(b); } catch(err) { this.clear(); this.push('Error'); state = 'error_state'; break; } if(!isFinite(c)) { this.clear(); this.push('Error'); state = 'error_state'; break; } this.push(c.toString()); break; case '÷': a = this.pop(); b = this.pop(); try { c = parseFloat(b)/parseFloat(a); } catch(err) { this.clear(); this.push('Error'); state = 'error_state'; break; } if(!isFinite(c)) { this.clear(); this.push('Error'); state = 'error_state'; break; } this.push(c.toString()); break; case '+/-': a = this.pop(); try { b = -parseFloat(a); } catch(err) { this.clear(); this.push('Error'); state = 'error_state'; break; } if(!isFinite(b)) { this.clear(); this.push('Error'); state = 'error_state'; break; } this.push(b.toString()); break; case 'LN': a = this.pop(); try { b = Math.log(parseFloat(a)); } catch(err) { this.clear(); this.push('Error'); state = 'error_state'; break; } if(!isFinite(b)) { this.clear(); this.push('Error'); state = 'error_state'; break; } this.push(b.toString()); break; default: throw('Unknown operator ' + op + '.'); break; } }; function keyclick(key) { key = key.toString(); switch(state) { case 'empty_state': switch(key) { case '0': buffer = key; show(buffer); state = 'zero_state'; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': buffer = key; show(buffer); if (stack.opempty()) stack.clear(); state = 'integer_state'; break; case '.': buffer = '0.'; show(buffer); if (stack.opempty()) stack.clear(); state = 'float_state'; break; case '=': stack.reduce(); show(stack.top()); break; case '+': case '-': stack.reduce(); show(stack.top()); stack.pushop(key); break; case '×': case '÷': if (stack.topop()=='×' || stack.topop()=='÷') stack.reduce(); else if(stack.stack.length!=stack.opstack.length+1) { stack.reduce(); } show(stack.top()); stack.pushop(key); break; case '+/-': case 'LN': if(stack.stack.length!=stack.opstack.length+1) { stack.reduce(); } stack.op(key); show(stack.top()); break; case 'C': buffer = '0'; show(buffer); stack.clear(); state = 'zero_state'; break; case 'AC': buffer = '0'; show(buffer); stack.clear(); state = 'zero_state'; break; default: break; } break; case 'zero_state': switch(key) { case '0': break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': buffer = key; show(buffer); state = 'integer_state'; break; case '.': buffer = '0.'; show(buffer); state = 'float_state'; break; case '=': stack.push(buffer); stack.reduce(); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case '+': case '-': stack.push(buffer); stack.reduce(); stack.pushop(key); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case '×': case '÷': stack.push(buffer); if(stack.topop()=='×' || stack.topop()=='÷') { stack.reduce(); } stack.pushop(key); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case '+/-': break; case 'LN': stack.push(buffer); stack.op(key); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case 'C': break; case 'AC': stack.clear(); break; default: break; } break; case 'integer_state': switch(key) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': buffer = buffer + key; show(buffer); break; case '.': buffer = buffer + '.'; show(buffer); state = 'float_state'; break; case '=': stack.push(buffer); stack.reduce(); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case '+': case '-': stack.push(buffer); stack.reduce(); stack.pushop(key); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case '×': case '÷': stack.push(buffer); if(stack.topop()=='×' || stack.topop()=='÷') { stack.reduce(); } stack.pushop(key); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case '+/-': a = parseInt(buffer); a = -a; buffer = a.toString(); show(buffer); break; case 'LN': stack.push(buffer); stack.op(key); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case 'C': buffer = '0'; show(buffer); state = 'zero_state'; break; case 'AC': buffer = '0'; show(buffer); stack.clear(); state = 'zero_state'; default: break; } break; case 'float_state': switch(key) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': buffer = buffer + key; show(buffer); break; case '.': break; case '=': stack.push(buffer); stack.reduce(); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case '+': case '-': stack.push(buffer); stack.reduce(); stack.pushop(key); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case '×': case '÷': stack.push(buffer); if(stack.topop()=='×' || stack.topop()=='÷') { stack.reduce(); } stack.pushop(key); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case '+/-': a = parseFloat(buffer); a = -a; buffer = a.toString(); show(buffer); break; case 'LN': stack.push(buffer); stack.op(key); show(stack.top()); if (state!="error_state") state = 'empty_state'; break; case 'C': buffer = '0'; show(buffer); state = 'zero_state'; break; case 'AC': buffer = '0'; show(buffer); stack.clear(); state = 'zero_state'; default: break; } break; case 'error_state': switch(key) { case 'AC': case 'C': show('0'); stack.clear(); buffer = '0'; state = 'zero_state'; break; default: break; } break; default: break; } }; function show(num) { num = num.toString(); if((num=="NaN") || (num=="Infinity") || (num=="-Infinity")) { show('Error'); } else { document.getElementById("result").value = num; } } var stack = new Stack([],[]); var buffer = "0"; var state = 'zero_state';
[edit] LogCalculator.css
#logcalculator { filter:alpha(opacity=85); -moz-opacity:.85; opacity:.85; } #logcalculator { background-color: #F4F4F4; width: 190px; } .logcalculatorcontainer { padding: 5px; border-top: 1px solid #C0C0C0; border-left: 1px solid #C0C0C0; border-right: 2px outset #C0C0C0; border-bottom: 2px outset #C0C0C0; } #logcalculator #control { text-align: right; } #logcalculator input { width: 40px; height: 30px; margin: 2px; background-color: #FFF; font-family: verdana,ariel,helvetica,sans-serif; font-size: 0.95em; border: 1px solid #C0C0C0; cursor: hand; cursor: pointer; } #logcalculator img { border: 0; } #logcalculator #result { width: 166px; text-align: right; font-family: courier new, monospace; font-size: 1.3em; padding: 2px; cursor: text; } #logcalculator .operator { color: #999; font-weight: bold; background-color: #DDD; } #logcalculator .equals { color: #FFF; font-weight: bold; background-color: #336699; }
[edit] Bugs and future improvements
- It is not possible to include two copies of the calculator on the same page.
- Numbers sometimes overflow the result window of the calculator.
