Module:PageFilter

From Granblue Fantasy Wiki
Revision as of 00:23, 11 January 2020 by Hal (talk | contribs)
Jump to navigation Jump to search

Documentation for this module may be created at Module:PageFilter/doc

local M = {}

local queryTables, queryFields = {
	character = 'characters',
	summon = 'summons',
	weapon = 'weapons',
}, {
	character = 'id,_pageName=link,rarity,type,element,race,weapon',
	summon = '_pageName=link,id,rarity,element,evo_max,obtain',
	weapon = '_pageName=link,rarity,type,element,evo_max,obtain',
}
function M.getFilterQueryInfo(filterObjectType)
	return queryTables[filterObjectType], queryFields[filterObjectType]
end

local dataTransforms = {}
function dataTransforms.character(args)
	args.race = args.race or 'none'
	args.shortid = args.id and args.id:sub(3,3) .. args.id:sub(5,7) or ''
	args.ftype, args.link, args.id = 'c'
end
function dataTransforms.weapon(args)
	args.ftype, args.link = "w", nil
end
function dataTransforms.summon(args)
	args.shortid = args.id and args.id:sub(3,3) .. args.id:sub(5,7) or ''
	args.ftype, args.link, args.id = 's'
end
function M.transformFilterData(filterObjectType, row, modifyInPlace)
	local ret, tf = row, dataTransforms[filterObjectType]
	if not modifyInPlace then
		ret = {}
		for k, e in queryFields[filterObjectType]:gmatch('([^,=]+)(=?)') do
			if e == '' then
				ret[k] = row[k]
			end
		end
	end
	if tf then
		tf(ret)
	end
	return ret
end


local dataNames = {
	shortid = "short-id",
	ftype = "type",
	evo_max = "filter-evo-max",
}
function M.renderFilterAttributes(args)	
	local ret = {}
	for k, v in pairs(args) do
		if v ~= "" then
			ret[#ret+1] = (' data-%s=%q'):format(dataNames[k] or ("filter-" .. k), v:lower())
		end
	end

	return table.concat(ret, "")
end
function M.renderFilterAttributesForData(filterObjectType, cargoRow, modifyInPlace)
	return cargoRow and M.renderFilterAttributes(M.transformFilterData(filterObjectType, cargoRow, modifyInPlace)) or ""
end

function M.renderTemplateWithCharacterData(frame, template, link)
	local template = template or frame.args.template or frame.args[1]
	local page = link or frame.args.link or frame.args[2]
	local filterObjectType = 'character'
	local queryTable = queryTables[filterObjectType]
	local queryColumns = queryFields[filterObjectType]
	local queryArgs = {limit = 1, where = ('_pageName = %q'):format(normalizePageTitle(page))}
	local cargoRow = mw.ext.cargo.query(queryTable, queryColumns, queryArgs)[1]
	cargoRow.filter = M.renderFilterAttributesForData(filterObjectType, cargoRow)
	if frame then
		for k,v in pairs(frame.args) do
			cargoRow[k] = v
		end
	end
	return frame:expandTemplate{title = template, args = cargoRow}
end


local function normalizePageTitle(title)
	return (title:match("^%s*(.-)%s*$"):gsub("^.[\128-\191]*", mw.ustring.upper))
end
for filterObjectType in pairs(queryTables) do
	local fn = ('render%sFilterData'):format((filterObjectType:gsub('^.', string.upper)))
	M[fn] = function(frame, link)
		local page = link or frame.args.link or frame.args[1]
		if not page then return "" end

		local queryTable = queryTables[filterObjectType]
		local queryColumns = queryFields[filterObjectType]
		local queryArgs = {limit = 1, where = ('_pageName = %q'):format(normalizePageTitle(page))}
		local cargoRow = mw.ext.cargo.query(queryTable, queryColumns, queryArgs)[1]

		return M.renderFilterAttributesForData(filterObjectType, cargoRow, true)
	end
end
-- older method name compatibility
M.renderFilterData = M.renderCharacterFilterData


local htmlEntities = {['>']='&gt;', ['<']='&lt;', ['"']='&quot;'}
local function trimNonEmpty(s)
	return s and s:match('^%s*(%S.-)%s*$')
end
local function trimAndEscape(s)
	s = s and s:match('^%s*(%S.-)%s*$')
	return s and s:gsub('[<">]', htmlEntities)
end
local function addCustomFilter(out, args, i, wrapInElement)
	local kp = 'f' .. i
	local ret = {
		trimNonEmpty(args[kp .. "_label"]),
		trimNonEmpty(args[kp .. "_datakey"]),
	}
	if not (ret[1] and ret[2]) then return end
	local v = 1
	while 1 do
		local kvp = kp .. 'v' .. v
		local label, value = trimNonEmpty(args[kvp .. '_label']), trimNonEmpty(args[kvp .. '_value'])
		if not (label and value) then
			break
		end
		ret[#ret+1], ret[#ret+2], v = label, value, v + 1
	end
	if v > 1 then
		if wrapInElement then
			out[#out+1] = '<' .. wrapInElement
		end
		out[#out+1] = (' data-extra-filter="%s"'):format(table.concat(ret,':'):gsub('[<">]', htmlEntities))
		if wrapInElement then
			out[#out+1] = '></' .. wrapInElement .. '>'
		end
	end
	return true
end
function M.renderFilterBox(frame, args)
	args = args or (frame.args.noparent and frame or frame:getParent()).args
	local out = {'<div class="page-filter-container'}
	if args.class and args.class:match("%S") then
		out[#out+1] = " " .. args.class:gsub('[<">]', htmlEntities)
	end
	out[#out+1] = '"'

	local skipFilters = trimAndEscape(args.skipFilters)
	local whitelistFilters = trimAndEscape(args.onlyFilters)
	out[#out+1] = skipFilters and (' data-skip-filters="%s"'):format(skipFilters)
	out[#out+1] = whitelistFilters and (' data-only-filter="%s"'):format(whitelistFilters)

	addCustomFilter(out, args, 1)
	out[#out+1] = '>'
	local f = 2
	while addCustomFilter(out, args, f, 'span') do
		f = f + 1
	end
	out[#out+1] = args.inner
	out[#out+1] = '</div>'
	out[#out+1] = args.post
	out[#out+1] = '<div style="clear:both"></div>'

	return table.concat(out, '')
end

function M.renderFilterContainerAttributes(frame, args)
	args = args or frame.args
	local groupID = trimNonEmpty(args.id or args[1])
	groupID = groupID and groupID:gsub("[^a-z0-9%-_]+", '-'):match('^.*[^%-].*$')
	groupID = groupID or ("gr-%04x%04x"):format(math.random(2^16)-1, math.random(2^16)-1)
	return (' data-page-filter-group-id="%s"'):format(groupID:gsub('[<">]', htmlEntities))
end

return M