Module:Weather and Module:Weather/sandbox: Difference between pages
Appearance
(Difference between pages)
Content deleted Content added
m merge multi-line comments |
use require('strict') instead of require('Module:No globals') |
||
Line 1: | Line 1: | ||
local p = {} |
|||
--[[ |
|||
Efficient (fast) functions to implement cells in tables of weather data. |
|||
Temperature conversion is built-in, but for simplicity, temperatures |
|||
are assumed to be for habitable locations (from -100 to 100 °C). |
|||
]] |
|||
require('strict') |
|||
local MINUS = '−' -- Unicode U+2212 MINUS SIGN |
|||
local degree = "°" -- used by addUnitNames() |
|||
local function temperature_style(palette, value, out_rgb) |
|||
local minus = "−" -- used by makeRow() and makeTable() |
|||
-- Return style for a table cell based on the given value which |
|||
local thinSpace = mw.ustring.char(0x2009) -- used by makeCell() |
|||
-- should be a temperature in °C. |
|||
local function style(bg, fg) |
|||
local precision, decimals |
|||
local min, max = unpack(palette.white or { -23, 35 }) |
|||
if not fg and value and (value < min or value >= max) then |
|||
-- if not empty |
|||
fg = 'FFFFFF' |
|||
local function ine(var) |
|||
var = tostring(var) |
|||
if var == "" then |
|||
return nil |
|||
else |
|||
return var |
|||
end |
|||
end |
|||
-- Error message handling |
|||
local message = "" |
|||
local function addMessage(newMessage) |
|||
if ine(message) then |
|||
message = message .. " " .. newMessage |
|||
else |
|||
message = "Notices: " .. newMessage |
|||
end |
|||
end |
|||
local function monospace(str) |
|||
return '<span style="background-color: #EEE; font-family: monospace;">' .. str .. '</span>' |
|||
end |
|||
-- Input and output parameters |
|||
local function getFormat(inputParameter, outputParameter, palette, messages) |
|||
local length, inputUnit, outputUnit, palette, show, cellFormat |
|||
if inputParameter == nil then |
|||
error('Please provide the number of values and a unit in the input parameter') |
|||
else |
|||
-- Find as many as two digits in the input parameter. |
|||
length = tonumber(string.match(inputParameter, "(%d%d?)")) |
|||
if not length then |
|||
length = 13 |
|||
addMessage('getFormat has not found a length value in the input parameter; length defaults to "13"') |
|||
end |
end |
||
if fg then |
|||
-- Find C or F, but not both |
|||
if string.find(inputParameter, "C") and string.find(inputParameter, "F") then |
|||
error("Input unit must be either C (Celsius) or F (Fahrenheit)") |
|||
else |
else |
||
inputUnit = string.match(inputParameter, "([CF])") or error("Please provide an input unit in the input parameter: F for Fahrenheit or C for Celsius", 0) |
|||
fg = '' |
|||
end |
|||
if inputUnit == "C" then |
|||
outputUnit = "F" |
|||
else |
|||
outputUnit = "C" |
|||
end |
|||
-- Make sure nothing except C, F, numbers, or spaces is in the input parameter. |
|||
if string.find(inputParameter, "[^CF%d%s]") then |
|||
addMessage("There are extraneous characters in the " .. monospace("output") .. " parameter.") |
|||
end |
end |
||
return 'style="background:#' .. bg .. ';' .. fg .. ' font-size:100%;"' |
|||
end |
|||
if type(value) ~= 'number' then |
|||
return style('FFFFFF', '000000') |
|||
end |
end |
||
local rgb = out_rgb or {} |
|||
if outputParameter == nil then |
|||
for i, v in ipairs(palette) do |
|||
-- Since there are default values, the module will still generate output with an empty output parameter. |
|||
local a, b, c, d = unpack(v) |
|||
addMessage("No output format has been provided in the " .. monospace("output") .. " parameter, so default values will be used.") |
|||
if value <= a then |
|||
else |
|||
rgb[i] = 0 |
|||
cellFormat = {} |
|||
elseif value < b then |
|||
for i, unit in require("Module:StringTools").imatch(outputParameter, "[CF]") do |
|||
rgb[i] = (value - a) * 255 / (b - a) |
|||
cellFormat[i] = unit |
|||
elseif value <= c then |
|||
if i > 2 then |
|||
break |
|||
elseif value < d then |
|||
end |
|||
rgb[i] = 255 - ( (value - c) * 255 / (d - c) ) |
|||
end |
|||
local function setFormat(key, variable, value) |
|||
if string.find(outputParameter, key) then |
|||
cellFormat[variable] = value |
|||
else |
|||
cellFormat[variable] = not value |
|||
end |
|||
end |
|||
if cellFormat[1] then |
|||
cellFormat.first = cellFormat[1] |
|||
else |
else |
||
error('C or F not found in output parameter') |
|||
rgb[i] = 0 |
|||
end |
|||
if cellFormat[2] == nil then |
|||
cellFormat["convertUnits"] = false |
|||
else |
|||
if cellFormat[2] == cellFormat[1] then |
|||
error('There should not be two of the same unit name in the output parameter.') |
|||
else |
|||
cellFormat["convertUnits"] = true |
|||
end |
|||
end |
|||
setFormat("unit", "unitNames", true) |
|||
setFormat("no ?color", "color", false) |
|||
setFormat("sort", "sortable", true) |
|||
setFormat("full ?size", "smallFont", false) |
|||
setFormat("no ?brackets", "brackets", false) |
|||
setFormat("round", "decimals", "0", "") |
|||
if string.find(outputParameter, "line break") then |
|||
cellFormat["lineBreak"] = true |
|||
elseif string.find(outputParameter, "one line") then |
|||
cellFormat["lineBreak"] = false |
|||
else |
|||
cellFormat["lineBreak"] = "auto" |
|||
end |
|||
if string.find(outputParameter, "one line") and |
|||
string.find(outputParameter, "line break") then |
|||
error('Place either "one line" or "line break" in the output parameter, not both') |
|||
end |
end |
||
end |
end |
||
return style(string.format('%02X%02X%02X', rgb[1], rgb[2], rgb[3])) |
|||
palette = palette or "cool2avg" |
|||
show = messages == "show" |
|||
return { |
|||
length = length, inputUnit = inputUnit, outputUnit = outputUnit, |
|||
cellFormat = cellFormat, show = show, palette = palette |
|||
} |
|||
end |
end |
||
-- Math functions |
|||
local function format_cell(palette, value, intext, outtext) |
|||
-- Return one line of wikitext to make a cell in a table. |
|||
local function round(value, decimals) |
|||
value = tonumber(value) |
|||
return '|\n' |
|||
if type(value) == "number" then |
|||
end |
|||
return string.format("%." .. decimals .. "f", value) |
|||
local text |
|||
if outtext then |
|||
text = intext .. '<br>(' .. outtext .. ')' |
|||
else |
else |
||
error("Format was asked to operate on " .. tostring(value) .. ", which cannot be converted to a number.", 2) |
|||
text = intext |
|||
return "" |
|||
end |
end |
||
return '| ' .. temperature_style(palette, value) .. ' | ' .. text .. '\n' |
|||
end |
end |
||
local function |
local function convert(value, unit, decimals) -- Unit is the unit being converted from. |
||
if not unit then |
|||
--[[ Convert °C to °F or vice versa, assuming the temperature is for a |
|||
error("No unit supplied to convert.", 2) |
|||
habitable location, well inside the range -100 to 100 °C. |
|||
That simplifies determining precision and formatting (no commas are needed). |
|||
Return (celsius_value, intext, outtext) if valid; otherwise return nil. |
|||
The returned input and output are swapped if requested. |
|||
Each returned string has a Unicode MINUS as sign, if negative. ]] |
|||
local invalue = tonumber(intext) |
|||
if not invalue then return nil end |
|||
local integer, dot, decimals = intext:match('^%s*%-?(%d+)(%.?)(%d*)%s*$') |
|||
if not integer then return nil end |
|||
if invalue < 0 then |
|||
intext = MINUS .. integer .. dot .. decimals |
|||
end |
end |
||
if tonumber(value) then |
|||
local outtext |
|||
local value = tonumber(value) |
|||
if inunit == 'C' or inunit == 'F' then |
|||
if unit == "C" then |
|||
local celsius_value, outvalue |
|||
return round(value * 9/5 + 32, decimals) |
|||
if inunit == 'C' then |
|||
elseif unit == "F" then |
|||
outvalue = invalue * (9/5) + 32 |
|||
return round((value - 32) * 5/9, decimals) |
|||
celsius_value = invalue |
|||
else |
else |
||
error("Input unit not recognized", 2) |
|||
outvalue = (invalue - 32) * (5/9) |
|||
celsius_value = outvalue |
|||
end |
end |
||
else |
|||
local precision = dot == '' and 0 or #decimals |
|||
-- to avoid concatenation errors |
|||
outtext = string.format('%.' .. precision .. 'f', math.abs(outvalue) + 2e-14) |
|||
return "" |
|||
if outvalue < 0 and tonumber(outtext) ~= 0 then |
|||
-- Don't show minus if result is negative but rounds to zero. |
|||
outtext = MINUS .. outtext |
|||
end |
|||
if swap then |
|||
return celsius_value, outtext, intext |
|||
end |
|||
return celsius_value, intext, outtext |
|||
end |
end |
||
-- LATER Think about whether a no-conversion option would be useful. |
|||
return invalue, intext, outtext |
|||
end |
end |
||
-- Stick numbers into array. Find out if any have decimals. |
|||
local function temperature_row(palette, row, inunit, swap) |
|||
-- Throw an error if any are invalid. |
|||
--[[ |
|||
local function _makeArray(format) |
|||
Return 13 lines specifying the style/content of 13 table cells. |
|||
return function(parameter) |
|||
Input is 13 space-separated words, each a number (°C or °F). |
|||
if not parameter then |
|||
Any word that is not a number gives a blank cell ("M" for a missing cell). |
|||
return nil |
|||
Any excess words are ignored. |
|||
Function Input Output |
|||
------------------------ |
|||
CtoF C C/F |
|||
FfromC C F/C |
|||
CfromF F C/F |
|||
FtoC F F/C ]] |
|||
local nrcol = 13 |
|||
local results, n = {}, 0 |
|||
for word in row:gmatch('%S+') do |
|||
n = n + 1 |
|||
if n > nrcol then |
|||
break |
|||
end |
end |
||
local array = {} |
|||
results[n] = format_cell(palette, process_temperature(word, inunit, swap)) |
|||
-- If there are multiple parameters for numbers, and the first doesn't have |
|||
-- decimals, the rest will have their decimals rounded off. |
|||
format.precision = format.precision or parameter:find("%d%.%d") and "1" or "0" |
|||
local numbers = mw.text.split(parameter, "%s+") |
|||
if #numbers ~= format.length then |
|||
addMessage('There are not ' .. format.length .. ' values in the ' .. parameter .. ' parameter.') |
|||
end |
|||
for i, number in ipairs(numbers) do |
|||
if not number:find("^%-?%d%d?%d?.?(%d?)$") then |
|||
error('The number "' .. number .. '" does not fit the expected pattern.') |
|||
end |
|||
table.insert(array, number) |
|||
end |
|||
return array |
|||
end |
end |
||
for i = n + 1, nrcol do |
|||
results[i] = format_cell() |
|||
end |
|||
return table.concat(results) |
|||
end |
end |
||
-- Color generation |
|||
local palettes = { |
|||
-- A background color entry in a palette is a table of four numbers, |
|||
p.palettes = { |
|||
-- say { 11, 22, 33, 44 } (values in °C). |
|||
--[[ |
|||
-- That means the color is 0 below 11 and above 44, and is 255 from 22 to 33. |
|||
The first three arrays in each palette defines background color using a |
|||
-- The color rises from 0 to 255 between 11 and 22, and falls between 33 and 44. |
|||
table of four numbers, say { 11, 22, 33, 44 } (values in °C). |
|||
That means that, on the scale from 0 (black) to 255 (saturated), the color |
|||
is 0 below 11°C and above 44°C, and is 255 from 22°C to 33°C. |
|||
The color rises from 0 to 255 between 11°C and 22°C, and falls from 255 to 0 |
|||
between 33°C and 44°C. |
|||
]] |
|||
cool = { |
cool = { |
||
{ -42.75, 4.47, 41.5, 60 }, |
{ -42.75, 4.47, 41.5, 60 }, -- red |
||
{ -42.75, 4.47, 4.5, 41.5 }, |
{ -42.75, 4.47, 4.5, 41.5 }, -- green |
||
{ -90 , -42.78, 4.5, 23 }, |
{ -90 , -42.78, 4.5, 23 }, -- blue |
||
white = { -23.3, 37.8 }, |
white = { -23.3, 37.8 }, -- background |
||
}, |
}, |
||
cool2 = { |
cool2 = { |
||
Line 149: | Line 219: | ||
} |
} |
||
--[[ Return style for a table cell based on the given value which |
|||
local function temperatures(frame, inunit, swap) |
|||
should be a temperature in °C. ]] |
|||
local palette = palettes[frame.args.palette] or palettes.cool |
|||
local function temperatureColor(palette, value, outRGB) |
|||
return temperature_row(palette, frame.args[1], inunit, swap) |
|||
local backgroundColor, textColor |
|||
value = tonumber(value) |
|||
if not value then |
|||
backgroundColor, textColor = 'FFF', '000' |
|||
addMessage("Value supplied to " .. monospace("temperatureColor") .. " is not recognized.") |
|||
else |
|||
local min, max = unpack(palette.white or { -23, 35 }) |
|||
if value < min or value >= max then |
|||
textColor = 'FFF' |
|||
-- Else nil. |
|||
-- This assumes that black text color is the default for most readers. |
|||
end |
|||
local backgroundRGB = outRGB or {} |
|||
for i, v in ipairs(palette) do |
|||
local a, b, c, d = unpack(v) |
|||
if value <= a then |
|||
backgroundRGB[i] = 0 |
|||
elseif value < b then |
|||
backgroundRGB[i] = (value - a) * 255 / (b - a) |
|||
elseif value <= c then |
|||
backgroundRGB[i] = 255 |
|||
elseif value < d then |
|||
backgroundRGB[i] = 255 - ( (value - c) * 255 / (d - c) ) |
|||
else |
|||
backgroundRGB[i] = 0 |
|||
end |
|||
end |
|||
backgroundColor = string.format('%02X%02X%02X', unpack(backgroundRGB)) |
|||
end |
|||
return backgroundColor, textColor |
|||
end |
end |
||
local function |
local function colorCSS(backgroundColor, textColor) |
||
if backgroundColor and textColor then |
|||
return temperatures(frame, 'C') |
|||
return 'background: #' .. backgroundColor .. '; color: #' .. textColor .. ';' |
|||
elseif backgroundColor then |
|||
return 'background: #' .. backgroundColor .. ';' |
|||
else |
|||
return '' |
|||
end |
|||
end |
end |
||
local function |
local function temperatureColorCSS(palette, value, outRGB) |
||
return |
return colorCSS(temperatureColor(palette, value, outRGB)) |
||
end |
end |
||
local function |
local function temperatureCSS(value, unit, palette) |
||
local palette = p.palettes[palette] or p.palettes.cool |
|||
return temperatures(frame, 'F') |
|||
local value = tonumber(value) |
|||
if value == nil then |
|||
error("The function " .. monospace("temperatureCSS") .. " is receiving a nil value") |
|||
else |
|||
if unit == 'F' then |
|||
value = convert(value, 'F', decimals) |
|||
elseif unit ~= 'C' then |
|||
unitError(unit or "nil") |
|||
end |
|||
return colorCSS(temperatureColor(palette, value)) |
|||
end |
|||
end |
end |
||
local function |
local function styleAttribute(palette, value, outRGB) |
||
local fontSize = "font-size: 85%;" |
|||
return temperatures(frame, 'C', true) |
|||
local color = temperatureColorCSS(palette, value, outRGB) |
|||
return 'style=\"' .. color .. ' ' .. fontSize .. '\"' |
|||
end |
end |
||
local style_attribute = styleAttribute |
|||
--[=[ |
|||
Used by {{Average temperature table/row/C/sandbox}}, |
|||
{{Average temperature table/row/F/sandbox}}, |
|||
{{Average temperature table/row/C/sandbox}}, |
|||
{{Template:Avg temp row F/sandbox2}}, |
|||
{{Template:Avg temp row C/sandbox2}}. |
|||
]=] |
|||
function p.temperatureStyle(frame) |
|||
local palette = p.palettes[frame.args.palette] or p.palettes.cool |
|||
local unit = frame.args.unit or 'C' |
|||
local value = tonumber(frame.args[1]) |
|||
if unit == 'F' then |
|||
value = convert(value, 'F', 1) |
|||
elseif unit ~= 'C' then |
|||
error('Unrecognized unit: ' .. unit) |
|||
end |
|||
return styleAttribute(palette, value) |
|||
end |
|||
p.temperature_style = p.temperatureStyle |
|||
--[[ ==== Cell, row, table generation ==== ]] |
|||
local outputFormats = { |
|||
high_low_average_F = |
|||
{ first = "F", |
|||
convertUnits = true, |
|||
unitNames = false, |
|||
color = true, |
|||
smallFont = true, |
|||
sortable = true, |
|||
decimals = "0", |
|||
brackets = true, |
|||
lineBreak = "auto", }, |
|||
high_low_average_C = |
|||
{ first = "C", |
|||
convertUnits = true, |
|||
unitNames = false, |
|||
color = true, |
|||
smallFont = true, |
|||
sortable = true, |
|||
decimals = "0", |
|||
brackets = true, |
|||
lineBreak = "auto", }, |
|||
high_low_F = |
|||
{ first = "F", |
|||
convertUnits = true, |
|||
unitNames = false, |
|||
color = false, |
|||
smallFont = true, |
|||
sortable = false, |
|||
decimals = "", |
|||
brackets = true, |
|||
lineBreak = "auto", }, |
|||
high_low_C = |
|||
{ first = "C", |
|||
convertUnits = true, |
|||
unitNames = false, |
|||
color = false, |
|||
smallFont = true, |
|||
sortable = false, |
|||
decimals = "0", |
|||
brackets = true, |
|||
lineBreak = "auto", }, |
|||
average_F = |
|||
{ first = "F", |
|||
convertUnits = true, |
|||
unitNames = false, |
|||
color = true, |
|||
smallFont = true, |
|||
sortable = false, |
|||
decimals = "0", |
|||
brackets = true, |
|||
lineBreak = "auto", }, |
|||
average_C = |
|||
{ first = "C", |
|||
convertUnits = true, |
|||
unitNames = false, |
|||
color = true, |
|||
smallFont = true, |
|||
sortable = false, |
|||
decimals = "0", |
|||
brackets = true, |
|||
lineBreak = "auto", }, |
|||
} |
|||
local outputFormat |
|||
local function addUnitNames(value, yesOrNo, unit) |
|||
if not unit then |
|||
error("No unit supplied as argument 3 to addUnitNames", 2) |
|||
end |
|||
-- Don't add a unit name to an empty string |
|||
value = yesOrNo == true and ine(value) and value .. " " .. degree .. unit or value |
|||
return value |
|||
end |
|||
local function ifYes(parameter, realization1, realization2) |
|||
local result |
|||
if realization1 then |
|||
if realization2 then |
|||
result = parameter == true and { realization1, realization2 } or { "", "" } |
|||
else |
|||
result = parameter == true and realization1 or "" |
|||
end |
|||
else |
|||
result = "" |
|||
addMessage(monospace("ifYes") .. " needs at least one realization.") |
|||
end |
|||
return result |
|||
end |
|||
local function makeCell(outputFormat, a, b, c, format) |
|||
local cell, cellContent = "", "" |
|||
local colorCSS, otherCSS, titleAttribute, sortkey, attributeSeparator, convertedUnitsSeparator = |
|||
"", "", "", "", "", "", "" |
|||
-- Distinguish styleAttribute variable from styleAttribute function above. |
|||
local styleAttribute, highLowSeparator, brackets, values, convertedUnits = |
|||
{"", ""}, {"", ""}, {"", ""}, {"", ""}, {"", ""} |
|||
-- Precision is 1 if any number has one or more decimals. |
|||
decimals = tonumber(outputFormat.decimals) and outputFormat.decimals or format.precision |
|||
if tonumber(b) and tonumber(a) then |
|||
values, highLowSeparator = { round(a, decimals), round(b, decimals) }, |
|||
{ thinSpace .. "/" .. thinSpace, ifYes(outputFormat.convertUnits, thinSpace .. "/" .. thinSpace) } |
|||
elseif tonumber(a) then |
|||
values = { round(a, decimals), "" } |
|||
elseif tonumber(c) then |
|||
values = { round(c, decimals), "" } |
|||
end |
|||
if outputFormat.first == format.inputUnit then |
|||
if outputFormat.convertUnits == true then |
|||
convertedUnits = { addUnitNames(convert(values[1], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit), addUnitNames(convert(values[2], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit) } |
|||
end |
|||
values = { addUnitNames(values[1], outputFormat.unitNames, format.inputUnit), addUnitNames(values[2], outputFormat.unitNames, format.inputUnit) } |
|||
elseif outputFormat.first == "C" or outputFormat.first == "F" then |
|||
if outputFormat.convertUnits == true then |
|||
convertedUnits = { addUnitNames(values[1], outputFormat.unitNames, format.inputUnit), addUnitNames(values[2], outputFormat.unitNames, format.inputUnit) } |
|||
end |
|||
values = { addUnitNames(convert(values[1], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit), addUnitNames(convert(values[2], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit) } |
|||
else |
|||
addMessage(monospace(tostring(outputFormat.first)) .. ", the value for " .. monospace("first") .. " in " .. monospace("outputFormat") .. " is not recognized.") |
|||
end |
|||
--[[ |
|||
Regarding line breaks: |
|||
If there are two values, there will be at least three characters: 9/1. |
|||
If there is one decimal, numbers will be three to five characters long |
|||
and there will be 3 to 10 characters total even without unit conversion: |
|||
1.1, 116.5/88.0. |
|||
If there are units, that adds three characters per number: 25 °C/20 °C. |
|||
In each of these cases, a line break is needed so that table cells are not too wide; |
|||
even more so when more than one of these things are true. |
|||
]] |
|||
if outputFormat.convertUnits == true then |
|||
brackets = outputFormat.brackets == true and { "(", ")" } or { "", "" } |
|||
if outputFormat.lineBreak == "auto" then |
|||
convertedUnitsSeparator = ( ine(values[2]) or decimals ~= "0" or outputFormat.showUnits == true ) and "<br>" or " " |
|||
else |
|||
convertedUnitsSeparator = outputFormat.lineBreak == true and "<br>" or outputFormat.lineBreak == false and " " or error('Value for lineBreak not recognized') |
|||
end |
|||
end |
|||
cellContent = values[1] .. highLowSeparator[1] .. values[2] .. convertedUnitsSeparator .. brackets[1] .. convertedUnits[1] .. highLowSeparator[2] .. convertedUnits[2] .. brackets[2] |
|||
if tonumber(c) then |
|||
colorCSS = outputFormat.color == true and temperatureCSS(c, format.inputUnit, format.palette, format.inputUnit) or "" |
|||
if tonumber(b) and tonumber(a) then |
|||
local attributeValue = outputFormat.first == format.inputUnit and c or convert(c, format.inputUnit, decimals) |
|||
sortkey = outputFormat.sortable == true and " data-sort-value=\"" .. attributeValue .. "\"" or "" |
|||
titleAttribute = " title=\"Average temperature: " .. attributeValue .. " " .. degree .. outputFormat.first .. "\"" |
|||
end |
|||
elseif tonumber(b) then |
|||
colorCSS = "" |
|||
elseif tonumber(a) then |
|||
colorCSS = outputFormat.color == true and temperatureCSS(a, format.inputUnit, format.palette) or "" |
|||
else |
|||
addMessage('Neither a nor b nor c are strings.') |
|||
end |
|||
otherCSS = outputFormat.smallFont == true and "font-size: 85%;" or "" |
|||
if ine(colorCSS) or ine(otherCSS) then |
|||
styleAttribute = { "style=\"", "\"" } |
|||
end |
|||
if ine(otherCSS) or ine(colorCSS) or ine(titleAttribute) or ine(sortkey) then |
|||
attributeSeparator = " | " |
|||
end |
|||
cell = "\n| " .. styleAttribute[1] .. colorCSS .. otherCSS .. styleAttribute[2] .. titleAttribute .. sortkey .. attributeSeparator .. cellContent |
|||
return cell |
|||
end |
|||
--[[ |
|||
Replaces hyphens that have a punctuation or space character before them and a number after them, |
|||
making sure that hyphens in "data-sort-type" are not replaced with minuses. |
|||
If Lua had (?<=), a capture would not be necessary. |
|||
]] |
|||
local function hyphenToMinus(str) |
|||
return str:gsub("([%p%s])-(%d)", "%1" .. minus .. "%2") |
|||
end |
|||
function p.makeRow(frame) |
|||
local args = frame.args |
|||
local format = getFormat(args.input, args.output, args.palette, args.messages) |
|||
local makeArray = _makeArray(format) |
|||
local a, b, c = makeArray(args.a), makeArray(args.b), makeArray(args.c) |
|||
local output = {} |
|||
if args[1] then |
|||
table.insert(output, "\n|-") |
|||
table.insert(output, "\n! " .. args[1]) |
|||
if args[2] then |
|||
table.insert(output, " !! " .. args[2]) |
|||
end |
|||
end |
|||
if format.cellFormat then |
|||
outputFormat = format.cellFormat |
|||
end |
|||
-- Assumes that if c is defined, b and a are, and if b is defined, a is. |
|||
if c then |
|||
if not outputFormat then |
|||
outputFormat = outputFormats.high_low_average_F |
|||
end |
|||
for i = 1, format.length do |
|||
table.insert(output, makeCell(outputFormat, a[i], b[i], c[i], format)) |
|||
end |
|||
elseif b then |
|||
if not outputFormat then |
|||
outputFormat = outputFormats.high_low_F |
|||
end |
|||
for i = 1, format.length do |
|||
table.insert(output, makeCell(outputFormat, a[i], b[i], nil, format)) |
|||
end |
|||
elseif a then |
|||
if not outputFormat then |
|||
outputFormat = outputFormats.average_F |
|||
end |
|||
for i = 1, format.length do |
|||
table.insert(output, makeCell(outputFormat, a[i], nil, nil, format)) |
|||
end |
|||
end |
|||
output = table.concat(output) |
|||
output = hyphenToMinus(output) |
|||
return output |
|||
end |
|||
function p.makeTable(frame) |
|||
local args = frame.args |
|||
local format = getFormat(args.input, args.output, args.palette, args.messages) |
|||
local makeArray = _makeArray(format) |
|||
local a, b, c = makeArray(args.a), makeArray(args.b), makeArray(args.c) |
|||
local output = { "{| class=\"wikitable center nowrap\"" } |
|||
if format.cellFormat then |
|||
outputFormat = format.cellFormat |
|||
end |
|||
-- Assumes that if c is defined, b and a are, and if b is defined, a is. |
|||
if c then |
|||
for i = 1, format.length do |
|||
if not outputFormat then |
|||
outputFormat = outputFormats.high_low_average_F |
|||
end |
|||
table.insert(output, makeCell(outputFormat, a[i], b[i], c[i], format)) |
|||
end |
|||
elseif b then |
|||
for i = 1, format.length do |
|||
if not outputFormat then |
|||
outputFormat = outputFormats.high_low_F |
|||
end |
|||
table.insert(output, makeCell(outputFormat, a[i], b[i], nil, format)) |
|||
end |
|||
elseif a then |
|||
for i = 1, format.length do |
|||
if not outputFormat then |
|||
outputFormat = outputFormats.average_F |
|||
end |
|||
table.insert(output, makeCell(outputFormat, a[i], nil, nil, format)) |
|||
end |
|||
end |
|||
table.insert(output, "\n|}") |
|||
if format.show then |
|||
table.insert(output, "\n\n<span style=\"color: red; font-size: 80%; line-height: 100%;\">" .. message .. "</span>") |
|||
end |
|||
output = table.concat(output) |
|||
output = hyphenToMinus(output) |
|||
return output |
|||
end |
|||
local chart = [[ |
local chart = [[ |
||
Line 183: | Line 595: | ||
]] |
]] |
||
function p.show(frame) |
|||
-- |
-- For testing, return wikitext to show graphs of how the red/green/blue colors |
||
-- vary with temperature, and a table of the resulting colors. |
|||
local function collection() |
local function collection() |
||
-- Return a table to hold items. |
-- Return a table to hold items. |
||
Line 191: | Line 603: | ||
n = 0, |
n = 0, |
||
add = function (self, item) |
add = function (self, item) |
||
if item then |
|||
self.n = self.n + 1 |
|||
self[self.n] = item |
|||
end |
|||
end, |
end, |
||
join = function (self, sep) |
join = function (self, sep) |
||
Line 209: | Line 623: | ||
local function with_minus(value) |
local function with_minus(value) |
||
if value < 0 then |
if value < 0 then |
||
return |
return minus .. tostring(-value) |
||
end |
end |
||
return tostring(value) |
return tostring(value) |
||
Line 216: | Line 630: | ||
local first = args[1] or -90 |
local first = args[1] or -90 |
||
local last = args[2] or 59 |
local last = args[2] or 59 |
||
local palette = palettes[args.palette] or palettes.cool |
local palette = p.palettes[args.palette] or p.palettes.cool |
||
local xvals, reds, greens, blues = collection(), collection(), collection(), collection() |
local xvals, reds, greens, blues = collection(), collection(), collection(), collection() |
||
local wikitext = collection() |
local wikitext = collection() |
||
wikitext:add( |
wikitext:add('{| class="wikitable"\n|-\n') |
||
[[ |
|||
{| class="wikitable" |
|||
|- |
|||
]] |
|||
) |
|||
local columns = 0 |
local columns = 0 |
||
for celsius = first, last do |
for celsius = first, last do |
||
local |
local backgroundRGB = {} |
||
local style = |
local style = styleAttribute(palette, celsius, backgroundRGB) |
||
local R = math.floor( |
local R = math.floor(backgroundRGB[1]) |
||
local G = math.floor( |
local G = math.floor(backgroundRGB[2]) |
||
local B = math.floor( |
local B = math.floor(backgroundRGB[3]) |
||
xvals:add(celsius) |
xvals:add(celsius) |
||
reds:add(R) |
reds:add(R) |
||
Line 250: | Line 659: | ||
end |
end |
||
return |
return p |
||
CtoF = CtoF, |
|||
CfromF = CfromF, |
|||
FtoC = FtoC, |
|||
FfromC = FfromC, |
|||
show = show, |
|||
} |