Module:Sandbox/Mzs42/ItemListTest
From Fallen London Wiki
Documentation for this module may be created at Module:Sandbox/Mzs42/ItemListTest/doc
local p = {}
local EMBEDDED_OPTIONS_FORMAT = '|%sEmbeddedOption|heading=%s|caption=%s|SmallHeading=%s%s|'
-- Taken from {{FontFate}} so we don't have to call out to the template
local FONT_FATE = "[[Fate|<font color=\"#2A9944\">'''FATE'''</font>]]"
--Pre-declare options so it can be called from find_options
local options
local function get_arg_or_nil(args, name)
local arg = args[name]
if arg == '' then
return nil
end
return arg
end
local function get_arg(args, parent_args, name, default)
return get_arg_or_nil(args, name) or get_arg_or_nil(parent_args, name) or default
end
local function get_title_from_heading(heading, default)
if heading then
return heading:gsub('|.+', '', 1)
end
return default
end
local function get_page_contents(frame, title)
local title_obj = mw.title.new(title, '')
local full_title = title_obj.nsText .. ':' .. title_obj.text
return frame:expandTemplate{title = full_title, args = {}}
end
--[[
Find embedded options in the "pseudo-template" format and expand into list items.
Input parameters:
@frame: frame object
@contents: a string with the contents of the page being embedded
Returns a string of all expanded options, wrapped in list items.
]]
local function find_options(frame, contents)
local search_pattern = string.format(
EMBEDDED_OPTIONS_FORMAT, '%%%%', '(.-)', '(.-)', '(.-)', '%%%%')
local actions = ''
for heading, caption, small_heading in mw.ustring.gmatch(contents, search_pattern) do
local use_small_heading = small_heading == 'yes'
actions = actions .. '<li style="margin-top: 0px;">' ..
options(frame, heading, caption, use_small_heading) .. '</li>'
end
return actions
end
--[==[
Find the main page image.
The expected default image specification is "[[File:Filename.png|right]]". Any
additional parameters specified should appear ''after'' the "|right", not before.
Input parameters:
@contents: a string with the contents of the page being embedded
Returns a string with the image name, including the leading "File:" and trailing
".png".
]==]
local function get_img(contents)
return contents:match('%[%[([^%]]-png)%|right[^%[]-%]%]') or 'File:Question.png'
end
local function invert_unb(str)
if (str:find('</b>') or str:len()) < (str:find('<b>') or 0) then
return str
:gsub('</b>(.-)<b>', '<b>%1</b>')
end
return str
end
local function get_info_line(contents, heading)
local info_line = contents:match('<b><span>(' .. heading .. ' .-)</span></b>')
if not info_line then
return ''
end
return invert_unb(info_line)
:gsub('\n%*', ', ') -- condense lists to one line
:gsub('%[%[Category:.-%]%]', '') -- remove categories
.. '<br/>' -- break between lines
end
local function transform_action_contents(contents)
-- Scale down small images from 40px to 20px
local pc = contents:gsub('small%.png', 'small.png|20px')
-- Remove paragraphs
pc = pc:gsub('^.-(==+ ?)', '\n \n%1')
-- Remove Success/Failure Instructions
pc = pc:gsub('...<i>[SF]?[au]?.- Instructions.-</i>\'+', '')
-- Remove categories
pc = pc:gsub('%[%[Category:.-%]%]', '')
-- Trim down carriage returns
pc = pc:gsub('\n\n', '\n')
-- Trim trailing whitespace
return pc:gsub('\n+%s*$', '')
end
local function format_heading(heading, caption, use_small_heading)
local out = ''
if use_small_heading then
out = out .. '<small>[[' .. heading .. ']]</small>'
else
out = out .. '[[' .. heading .. ']]'
end
if caption then
out = out .. '<small> ' .. caption .. '</small>'
end
return out
end
local function make_option(heading, caption, use_small_heading, title, img, contents)
return '<div class="mw-option" style="display:flex;margin:.5em 0"><div ' ..
'style="flex-grow:0; flex-shrink:0;">[[' .. img .. '|45px|link=' ..
title .. '|class=option-img]]</div><div style="display:flex;' ..
'flex-direction:column;flex-grow:1"><h3 style="margin:0 0 .3em">' ..
'<span style="display:inline;">' .. format_heading(heading, caption, use_small_heading) ..
'</span></h3><ul style="margin:0"><li class="mw-collapsible mw-collapsed"' ..
'style="list-style-type:none;border-left:2px solid #565646;margin-top:0px;' ..
'padding-left:0.5em;"><span style="background-color: #999080;">' ..
' Spoiler </span><div class="mw-collapsible-content">' .. contents ..
'\n[[#' .. title .. '|^]]</div></li></ul></div></div>'
end
--[[
Turn Options into a "pseudo-template" format for use in Embed.
This is a sort of hack to prevent needing to fully re-render Options multiple times.
Input parameters:
@heading: A string with the options heading, potentially including an alternate appearance
@caption: A string with the non-linkified caption
@use_small_heading: A boolean, true if the heading should be rendered small
Returns a string with the formatted pseudo-template.
]]
local function embedded_options(heading, caption, use_small_heading)
local small_heading = ''
if use_small_heading then
small_heading = 'yes'
end
return string.format(
EMBEDDED_OPTIONS_FORMAT, '%%', heading, caption, small_heading, '%%')
end
--[[
Render an {{Options}}
Input parameters:
@frame: frame object
@heading: A string with the options heading, potentially including an alternate appearance
@caption: A string with the non-linkified caption
@use_small_heading: A boolean, true if the heading should be rendered small
A string with the fully rendered {{Options}}
]]
options = function(frame, heading, caption, use_small_heading)
local title = get_title_from_heading(heading, 'Scheme: Set up a Salon')
if not caption then
local name, cost = mw.ustring.match(title, '^(.-) %((%d+) FATE%)$')
if name then
caption = '(' .. cost .. ' ' .. FONT_FATE .. ')'
if heading == title then
heading = title .. '|' .. name
end
end
end
local success, contents = pcall(get_page_contents, frame, title)
if not success then
return make_option(heading, caption, use_small_heading, title, 'File:Question.png', '')
end
local img = get_img(contents)
local action_cost = get_info_line(contents, 'Action Cost:')
local unlocked_with = get_info_line(contents, 'Unlocked with')
local locked_with = get_info_line(contents, 'Locked with')
local friend_unlocked_with = get_info_line(contents, 'Your friend needs')
local transformed = transform_action_contents(contents)
local inner = '[[' .. img .. '|right]]'
inner = inner .. action_cost .. unlocked_with .. locked_with ..
friend_unlocked_with .. transformed
return make_option(heading, caption, use_small_heading, title, img, inner)
end
--[[
Render an {{Embed}}
Input parameters:
@frame: frame object
@heading: A string with the embed heading, potentially including an alternate appearance
@caption: A string with the non-linkified caption
@use_small_heading: A boolean, true if the heading should be rendered small
A string with the fully rendered {{Embed}}
]]
local function embed(frame, heading, caption, use_small_heading)
local title = get_title_from_heading(heading, 'A fine day in the Flit')
local success, contents = pcall(get_page_contents, frame, title)
if not success then
return make_option(heading, caption, use_small_heading, title, 'File:Question.png', '')
end
local img = get_img(contents)
local success, options_list = pcall(find_options, frame, contents)
if not success then
return make_option(heading, caption, use_small_heading, title, img, '')
end
local appears_in = get_info_line(contents, 'Storylet appears in')
local drawn_in = get_info_line(contents, 'Card only drawn in')
local unlocked_with = get_info_line(contents, 'Unlocked with')
local locked_with = get_info_line(contents, 'Locked with')
local frequency = get_info_line(contents, 'Occurs with')
--local inner = '[[' .. img .. '|right]]'
local inner = appears_in .. drawn_in .. unlocked_with .. locked_with .. frequency
inner = inner .. '<ul style="margin:0; list-style-type:none;">' ..
options_list .. '</ul>'
return make_option(heading, caption, use_small_heading, title, img, inner)
end
function p.options(frame)
local args = frame.args
local parent_args = frame:getParent().args
local heading = get_arg(args, parent_args, 1, nil)
local caption = get_arg(args, parent_args, 2, nil)
local use_small_heading = get_arg(args, parent_args, 'SmallHeading', false)
local embedded = frame:callParserFunction('#var', 'embeddedoptions') ~= ''
if embedded then
return embedded_options(heading or '', caption or '', use_small_heading)
end
return options(frame, heading, caption, use_small_heading)
end
function p.embed(frame)
local args = frame.args
local parent_args = frame:getParent().args
local heading = get_arg(args, parent_args, 1, nil)
local caption = get_arg(args, parent_args, 2, nil)
local use_small_heading = get_arg(args, parent_args, 'SmallHeading', false)
return embed(frame, heading, caption, use_small_heading)
end
return p