Module:Calculator widget
Appearance
Creates an interactive 4-function calculator widget (See right). Requires the calculator gadget to work.
Usage
{{#invoke:Calculator widget|getWidget}}
Optional parameter fallback to determine what to do for users without javascript (or calculator gadget enabled). Default "all" to show full thing, "keypad" to show only keypad and "none" to make calculator invisible
local p = {}
-- Make a calculator widget using {{calculator}}
-- As a work around to the fact that buttons can only affect one field, store everything in a single number
-- layout (status = bits 0-2)
-- 9 bits mem, 2 bits memexp, 17 bits x reg, 2 bits exp, 17 bits y reg, 2 bits exp, 1 bit decimal flag, 2 bit status.
-- Negative means show Y reg instead of X reg.
-- status:
-- 0 = Add
-- 1 = sub
-- 2 = mul
-- 3 = div
local ALL_START = 0
local STATUS_START = 0
local STATUS_END = 2
local DECIMAL_START = 2
local DECIMAL_END = 3
local Y_EXP_START = 3
local Y_EXP_END = 5
local Y_START = 5
local Y_END = 22
local X_EXP_START = 22
local X_EXP_END = 24
local X_START = 24
local X_END = 41
local MEM_EXP_START = 41
local MEM_EXP_END = 43
local MEM_START = 43
local MEM_END = 52
local ALL_END = 52
-- These functions return {{calculator}} formulas
-- that are evaled client side.
-- Get value of packed variable
local function unpack(startIndex, endIndex)
local s = 2^startIndex
local e = 2^(endIndex-startIndex)
return "floor((abs(reg)/"..s..")%"..e..")"
end
local function pack( value, offset, endOffset )
local maxLen = 2^(endOffset-offset)
offsetMul = 2^offset
return 'ifgreaterorequal( value, ' .. maxLen .. ',NaN,' .. offsetMul .. '*' .. value .. ')'
end
-- select some bits with rest set to 0.
local function selectBits(startIndex, endIndex)
return '('..unpack(startIndex,endIndex) .. '*' .. (2^startIndex).. ')'
end
local function get(reg)
local main_start, main_end, exp_start, exp_end
if reg == 'X' then
main_start = X_START
main_end = X_END
exp_start = X_EXP_START
exp_end = X_EXP_END
elseif reg == 'Y' then
main_start = Y_START
main_end = Y_END
exp_start = Y_EXP_START
exp_end = Y_EXP_END
elseif reg == 'MEM' then
main_start = MEM_START
main_end = MEM_END
exp_start = MEM_EXP_START
exp_end = MEM_EXP_END
else
error( "unknown register" )
end
return '(' .. unpack( X_START, X_END ) ..
'*(pow(10,' .. unpack( X_EXP_START, X_EXP_END ) .. ')))'
end
local function regFull(x)
if x ~= 'X' then
error( "unimplemented" )
end
return 'or('
.. 'ifgreaterorequal('
.. unpack( X_START, X_END ) .. ','
.. ( 2^(X_END-X_START) - 1 )
.. '),'
.. 'ifgreater('
.. unpack( X_EXP_START, X_EXP_END ) .. ','
.. ( 2^(X_EXP_END-X_EXP_START) - 1 )
.. ')'
.. ')'
end
local function getDisplay()
return 'ifpositive(reg,' .. get('X') .. ',' .. get('Y') .. ')'
end
-- User presses a number.
-- If status
local function pressNumber(x)
return selectBits( ALL_START, X_EXP_START ) .. '+' ..
selectBits( X_END, ALL_END ) .. '+' ..
'ifzero( '
.. regFull("X") .. ',' ..
-- we are not full, so add something
pack(
unpack( X_START, X_END ) .. '*10+' .. x,
X_START,
X_END
) .. '+' ..
pack(
unpack( X_EXP_START, X_EXP_END )
.. '+' .. unpack( DECIMAL_START, DECIMAL_END ),
X_EXP_START,
X_EXP_END
) ..
',' ..
-- Overflow, do not allow additional digits
selectBits( X_EXP_START, X_END )
.. ')'
end
function p.getWidget( frame )
local buttons = {
{ "MC", "" },
{ "MR", "" },
{ "M−", "" },
{ "M+", "" },
{ "C", "" },
{ "±", "" },
{ "%", "" },
{ "√", "" },
{ "7", pressNumber(7) },
{ "8", pressNumber(8) },
{ "9", pressNumber(9) },
{ "÷", "" },
{ "4", pressNumber(4) },
{ "5", pressNumber(5) },
{ "6", pressNumber(6) },
{ "×", "" },
{ "1", pressNumber(1) },
{ "2", pressNumber(2) },
{ "3", pressNumber(3) },
{ "−", "" },
{ "0", pressNumber(0) },
{ ".", "" },
{ "=", "" },
{ "+", "" },
}
local calc = '<div class="calculatorwidget calculator-container" style="display:grid;grid-template-columns:repeat(4, 1fr);grid-gap:5px;min-width:256px;width:20ch;border:thin solid gray;padding: 5px; background: #eee">'
-- On Calculator page, we want to show the layout of buttons on print, but
-- not the answer field.
.. '<div style="grid-column:1/5;display:none" class="calculatorgadget-enabled">{{Calculator codex text|default=0|id=ans|formula=' .. getDisplay() .. '|style=text-align:right|readonly=1|NaN-text=Error}}</div>'
for i, v in ipairs(buttons) do
local type = v[1] == 'C' and 'destructive' or 'default'
-- maybe make "=" be progressive instead of default. Perhaps weight = primary?
calc = calc .. '{{calculator button|type='.. type ..'|weight=normal|for=reg|formula=' .. v[2] .. '|contents='.. v[1] .. '}}'
end
calc = calc .. '{{calculator|type=hidden|id=reg}}'
calc = calc .. '</div>'
return frame:preprocess(calc)
end
return p