Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

// request.cf · coarse context

A page that knows where it met you.

Only coarse request metadata is shown. This demo does not display or persist visitor IP addresses.

Country
US
Cloudflare location
CMH
Connection
HTTP/2
Language
Not provided

Ray ID: a40441e26ad218aa

Jump to content

Module:U.S. Army Cemeteries Explorer

From Wikipedia, the free encyclopedia
-- 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