local getArgs = require("Module:Arguments").getArgs
local mos = require("Module:MOS")
local rat = require("Module:Rational")
local tamnams = require("Module:TAMNAMS")
local tip = require("Module:Template input parse")
local utils = require("Module:Utils")
local yesno = require("Module:Yesno")
local p = {}
-- TODO:
-- - (High priority): Refactor code so instead of string concatenation, lines
-- are appended to a table, where table.concat() is called at the end.
-- - Add ability to autocollapse on large mos pages (say, more than 12 modes)
-- - Drop/replace delimiter-based mode name input; it's harder to maintain
-- - Limit table entries to ONLY proposed mode names and MAYBE one supplementary
-- column?! Any more is best done with a subst-instance of the table
-- - Think of better naming scheme for entries
-- Extract column info
-- Mode numbering is by BRIGHT GENS DOWN PER PERIOD
-- Nothing is returned; instead, args is modified
function p.preprocess_entries(num_modes, args, col_name)
local col_name = col_name or ""
local col_data = {}
local is_col_empty = true
for i = 1, num_modes do
-- Put together key, then use it to get data from args
local bright_gens_down = i - 1
local key = string.format("%sD", bright_gens_down) .. (col_name ~= "" and string.format(" %s", col_name) or "")
local cell = args[key] or ""
cell = utils.trim(cell)
-- Then transfer data to col_data
table.insert(col_data, cell)
-- Then remove data from args
args[key] = nil
-- Check whether data was present; absence of data is expressed as an
-- empty string.
is_col_empty = is_col_empty and cell == ""
end
if not is_col_empty then
args[string.format("%s Entries", col_name)] = col_data
end
end
-- "Main" function
-- To be called by wrapper
function p._mos_modes(args)
local is_collapsed = args["Is Collapsed"] == true
local input_mos = args["Input MOS"] or mos.new(5,2)
local mode_names = args["Name Entries"] or args["Mode Names"] or nil
local headers = args["Table Headers"]
local entries = args["Table Entries"]
-- Get UDPs and CPOs
local udps = tamnams.mos_mode_udps(input_mos)
local cpos = tamnams.mos_mode_cpos(input_mos)
-- Get the mos's modes
local mos_modes = mos.modes_by_brightness(input_mos)
-- Check whether to add mode names
local add_mode_names = mode_names ~= nil and #mode_names == #mos_modes
-- Check whether the number of headers times the number of modes equals the
-- number of entries. Supplementary info can only be added if this condition
-- is met. Limited to 3 columns of supplementary info.
local add_columns = #headers > 0 and #entries > 0
if add_columns then
add_columns = add_columns and #mos_modes * #headers == #entries and #headers <= 3
end
-- Table caption
local scale_sig = mos.as_string(input_mos)
-- Start of table
local result = "{| class=\"wikitable sortable center-2 center-3 mw-collapsible" .. (is_collapsed and " mw-collapsed\"\n" or "\"\n")
.. "|+ style=\"font-size: 105%; white-space: nowrap;\" | " .. string.format("Modes of %s\n", scale_sig)
.. "|-\n"
-- Table headers
result = result
.. "! [[UDP]]"
.. " !! Cyclic<br />order"
.. " !! Step<br />pattern"
-- Add header for mode names, if provided.
if add_mode_names then
result = result .. " !! class=\"unsortable\" | Mode names"
end
-- Add column headers for supplementary info, if provided.
if add_columns then
for i = 1, #headers do
result = result .. string.format(" !! class=\"unsortable\" | %s", headers[i])
end
end
result = result .. "\n"
-- Enter each row
for i = 1, #mos_modes do
result = result .. "|-\n"
-- Add the UDP, brightness order, and the mode's step pattern
result = result .. string.format("| %s || %s || %s",
udps[i], cpos[i], mos_modes[i])
-- Add the mode's name, if given
if add_mode_names then
result = result .. string.format(" || %s", mode_names[i])
end
-- Add columns if given
if add_columns then
for j = 1, #headers do
local index = (i - 1) * #headers + j
result = result .. string.format(" || %s", entries[index])
end
end
result = result .. "\n"
end
result = result .. "|}"
return result
end
-- Wrapper function; to be called by template
function p.modes_table(frame)
local args = getArgs(frame)
-- Process scalesig to input mos
local scale_sig = args["Scale Signature"]
local input_mos = mos.parse(scale_sig)
args["Input MOS"] = input_mos
args["Scale Signature"] = nil
-- Get collapse options
args["Collapsed"] = yesno(args["Collapsed"], false)
-- Get mode names entered
-- Arg names are formatted as "<bright-gens-down>D <col-name>"
-- These are placed in a table called "<col-name> Data"
local num_modes = mos.period_step_count(input_mos)
p.preprocess_entries(num_modes, args, "Name")
-- Get the mos's mode names, if given
-- Mode names are entered as a semicolon-delimited list
-- 5L 2s gets default names
local mode_names = nil
if scale_sig == "5L 2s" then
args["Mode Names"] = {
"Lydian",
"Ionian (major)",
"Mixolydian",
"Dorian",
"Aeolian (minor)",
"Phrygian",
"Locrian"
}
args["Name Entries"] = args["Mode Names"]
end
-- Temporarily re-allow previous input method
if args["Mode Names"] ~= nil then
args["Mode Names"] = tip.parse_entries(args["Mode Names"], "$")
end
if args["Table Headers"] ~= nil then
args["Table Headers"] = tip.parse_entries(args["Table Headers"], "$")
end
if args["Table Entries"] ~= nil then
args["Table Entries"] = tip.parse_entries(args["Table Entries"], "$")
end
local result = p._mos_modes(args)
-- Current means of adding entries is unmaintainable; to be deprecated.
if headers_unparsed ~= "" and entries_unparsed ~= "" then
result = result .. "[[Category:Pages with deprecated template parameters]]"
end
-- Debugger option
local debugg = yesno(args["debug"])
if debugg == true then
result = "<syntaxhighlight lang=\"wikitext\">" .. result .. "</syntaxhighlight>"
end
return frame:preprocess(result)
end
-- Tester
function p.tester()
local args = {
["0D Name"] = "AAA",
["1D Name"] = "BBB",
["2D Name"] = "CCC",
["3D Name"] = "DDD"
}
p.preprocess_entries(5, args, "Name")
return args
end
return p