Module:U.S. Army Cemeteries Explorer
Appearance
-- U.S. Army Cemeteries Explorer.lua
-- License: CC-0. Public domain
-------------------
-- HELPER FUNCTIONS
-------------------
local function empty(val)
return not val or "" == val
end
local function serialize_table(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false
depth = depth or 0
local tmp = string.rep(" ", depth)
if name then tmp = tmp .. name .. " = " end
if type(val) == "table" then
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
for k, v in pairs(val) do
tmp = tmp .. serialize_table(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
end
tmp = tmp .. string.rep(" ", depth) .. "}"
elseif type(val) == "number" then
tmp = tmp .. tostring(val)
elseif type(val) == "string" then
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
return tmp
end
local function has_value(tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return false
end
local function table_invert(t)
local s={}
for k,v in pairs(t) do
s[v]=k
end
return s
end
local function length_and_string(str)
local s = tostring(str)
local size = #s -- # returns byte length for Lua strings
if size > 255 then
error(("string too long (%d bytes); max 255"):format(size))
end
return string.char(size) .. s
end
local function parent_frame_if_args_empty(frame)
local args_empty = true
for key, val in pairs(frame.args) do args_empty = false; break end
if args_empty and frame.getParent then
local parent = frame:getParent()
if parent then frame = parent end
end
return frame
end
------------
-- CONSTANTS
------------
local p = {}
p.passthrough_citation_parameters = {
"title", "url", "url-status", "archive-url", "archive-date", "access-date" }
local FIELD_CODES = {
last = "\n",
first = string.char(0x12),
middle = string.char(0x1A),
}
local FIELD_ORDER = {"last", "first", "middle"}
-- This is very incomplete.
local CEMETERY_NAMES_TO_SLUGS = {
["Arlington National Cemetery"] = "arlington-national",
["US Soldiers' and Airmen's Home"] = "soldiers-airmens-home",
["Fort Sill Post Cemetery"] = "ft-sill",
["Fort Bragg"] = "ft-bragg",
["Fort Benning Georgia Main Post Cemetery"] = "ft-benning"
}
local CEMETERY_SLUGS_TO_NAMES = table_invert(CEMETERY_NAMES_TO_SLUGS)
local SEARCH_PARAMETERS = {
'interment-date', 'birth-date', 'death-date',
'last', 'first', 'middle', 'cemeteryid'
}
local b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-------------------
-- MODULE FUNCTIONS
-------------------
-- See another implementation at [[Module:Cite taxon]].
function p.base64_encode(data)
if not data or #data == 0 then return "" end
local out = {}
local len = #data
for i = 1, len, 3 do
local a = data:byte(i) or 0
local b = data:byte(i + 1)
local c = data:byte(i + 2)
local n = a * 65536 + ( (b or 0) * 256 ) + (c or 0)
local i1 = math.floor(n / 262144) % 64 + 1
local i2 = math.floor(n / 4096) % 64 + 1
local i3 = math.floor(n / 64) % 64 + 1
local i4 = n % 64 + 1
if not b then
-- only 1 input byte -> two base64 chars + "=="
out[#out + 1] = b64chars:sub(i1, i1)
out[#out + 1] = b64chars:sub(i2, i2)
out[#out + 1] = "=="
elseif not c then
-- 2 input bytes -> three base64 chars + "="
out[#out + 1] = b64chars:sub(i1, i1)
out[#out + 1] = b64chars:sub(i2, i2)
out[#out + 1] = b64chars:sub(i3, i3)
out[#out + 1] = "="
else
-- 3 input bytes -> four base64 chars
out[#out + 1] = b64chars:sub(i1, i1)
out[#out + 1] = b64chars:sub(i2, i2)
out[#out + 1] = b64chars:sub(i3, i3)
out[#out + 1] = b64chars:sub(i4, i4)
end
end
return table.concat(out)
end
-- +search_fields+ should be a table whose keys are at least some of the values
-- from SEARCH_PARAMETERS, or a frame whose args are such a table.
-- Returns strict Base64 string or nil when no known search parameters are
-- provided.
-- +unknown_action+ is what to do when an unknown parameter is provided.
-- Accepted values are ignore, warn, and error.
function p.encode_search_query(search_fields, unknown_action)
if not unknown_action then unknown_action="warn" end
if not empty(search_fields.args) then
search_fields = search_fields.args
end
if not search_fields then return nil end
local res_parts = {}
for index, key in pairs(FIELD_ORDER) do
local value = search_fields[key]
if value then
local code = FIELD_CODES[key]
if code then
res_parts[#res_parts + 1] = code
res_parts[#res_parts + 1] = length_and_string(value)
else
if unknown_action == "warn" then
mw.log(("Module:U.S. Army Cemeteries Explorer#encode_search_query: unknown field code: %q"):format(tostring(key)))
elseif unknown_action == "error" then
error(("unknown field code: %q"):format(tostring(key)))
elseif unknown_action ~= "ignore" then
error(("unknown 2nd parameter value %q. Accepted values are warn, error, and ignore."):format(tostring(unknown_action)))
end
end
end
end
local raw = table.concat(res_parts)
if "" == raw then
mw.log("Module:U.S. Army Cemeteries Explorer#encode_search_query failed to generate a search query for " .. serialize_table(search_fields))
return nil
end
mw.log("Raw query: " .. tostring(raw))
mw.log(p.base64_encode(raw))
return p.base64_encode(raw)
end
-- Can inherit parameters from parent frame
function p.print_url(frame)
frame = parent_frame_if_args_empty(frame)
local iss_id = frame.args['iss-id']
if empty(iss_id) then return nil end
local cemetery_slug = p.cemetery_slug(frame)
if not cemetery_slug then return nil end
local url = "https://ancexplorer.army.mil/publicwmv/print.html#/" .. cemetery_slug .. "/burial/" .. iss_id .. "/"
return url
end
-- frame must contain parameters directly.
function p.cemetery_name(frame)
local args = frame
if frame.args then args = frame.args end
local res = args["cemetery-name"]
if res then return res end
res = CEMETERY_SLUGS_TO_NAMES[args["cemetery-slug"]]
if res then return res end
return nil
end
-- frame must contain parameters directly.
function p.cemetery_slug(frame)
local args = frame
if frame.args then args = frame.args end
local res = args["cemetery-slug"]
if res then return res end
res = CEMETERY_NAMES_TO_SLUGS[args["cemetery-name"]]
if res then return res end
return nil
end
-- Can inherit parameters from parent frame
function p.search_url(frame)
local res = "https://ancexplorer.army.mil/publicwmv/#/search-all/results/1/"
frame = parent_frame_if_args_empty(frame)
local query_slug = frame.args["encoded-query"]
if empty(query_slug) then
query_slug = p.encode_search_query(frame, "ignore")
if empty(query_slug) then
return nil
end
end
res = res .. query_slug .. "/"
return res
end
-- Can inherit parameters from parent frame
function p.make_citation(frame)
frame = parent_frame_if_args_empty(frame)
local module_args = frame.args
local print_url = p.print_url(frame)
local search_url = p.search_url(frame)
local output_params = {}
output_params.publisher = "United States Army"
if search_url then
output_params.url = search_url
elseif print_url then
output_params.url = print_url
else
output_params.url = "https://ancexplorer.army.mil/"
end
if not empty(module_args.middle) and empty(module_args.first) then
error("ERROR: Middle name given but not first name.")
end
if not empty(module_args.last) then
if not empty(module_args.first) then
output_params.title = module_args.last .. ", " .. module_args.first
else
output_params.title = module_args.last
end
elseif not empty(module_args.first) then
output_params.title = module_args.first
end
if not empty(module_args.middle) then
output_params.title = output_params.title .. " " .. module_args.middle
end
local iss_id = module_args["iss-id"]
if not output_params.title and not empty(iss_id) then
output_params.title = iss_id
end
local cemetery_name = p.cemetery_name(frame)
if output_params.title then
if cemetery_name then
output_params.title = output_params.title .. ": "
end
else
output_params.title = ""
end
if cemetery_name then
output_params.title = output_params.title .. cemetery_name
end
for key, value in pairs(p.passthrough_citation_parameters) do
local val = module_args[value]
if val then
output_params[value] = val
end
end
if empty(output_params.title) then
output_params.title = "Army Cemeteries Explorer"
else
output_params.website = "Army Cemeteries Explorer"
end
local res = mw.getCurrentFrame():expandTemplate{ title = 'Cite web', args = output_params }
if print_url and search_url then
res = res .. " [" .. print_url .. " Alternate view]."
end
return res
end
return p