Module:Recipe: Difference between revisions
ReisuDesign (talk | contribs) Created page with "local p = {} local Card = require('Module:Card') local category = mw.html.create() --Arguments for template local param = { sCharArg = 'character', sTypeArg = 'type', sSortArg = 'sort', sSortDelim = ';', --Needs to be pattern sSortDelim2 = '¤', sTimeArg = 'time', sYieldArg = 'yield', sPage = 'page' } p.param = param function padLeft(str, amt) return string.rep('0', amt - str:len()) .. str end function addRecipe(sAmt, sName) local cardNode = Card._m..." |
No edit summary |
||
| (22 intermediate revisions by 2 users not shown) | |||
| Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local Card = require('Module:Card') | local Card = require('Module:Card') | ||
--Arguments for template | -- Arguments for template | ||
local param = { | local param = { | ||
sCharArg = 'character', | sCharArg = 'character', | ||
sKitchArg = 'kitchenware', | |||
sTypeArg = 'type', | sTypeArg = 'type', | ||
sSortArg = 'sort', | sSortArg = 'sort', | ||
sSortDelim = ';', | sSortDelim = ';', | ||
sSortDelim2 = '¤', | sSortDelim2 = '¤', | ||
sTimeArg = 'time', | sTimeArg = 'time', | ||
| Line 14: | Line 15: | ||
sPage = 'page' | sPage = 'page' | ||
} | } | ||
p.param = param | p.param = param | ||
function padLeft(str, amt) | -------------------------------------------------------------------------------- | ||
-- Utility | |||
-------------------------------------------------------------------------------- | |||
local function padLeft(str, amt) | |||
str = tostring(str or '') | |||
return string.rep('0', math.max(amt - str:len(), 0)) .. str | |||
end | end | ||
function | local function pluralize(sOut, plural) | ||
local | if plural then | ||
sOut = sOut .. 's' | |||
end | |||
return sOut | |||
end | |||
local function normalizeText(s) | |||
return mw.ustring.lower(mw.text.trim(tostring(s or ''))) | |||
end | |||
local function argExists(args, key) | |||
return args[key] ~= nil and tostring(args[key]) ~= '' | |||
end | |||
local function addUseCategory(category, name, quality, sort) | |||
if not name or tostring(name) == '' then | |||
return | |||
end | |||
if quality and tostring(quality) ~= '' then | |||
if sort and tostring(sort) ~= '' then | |||
category:wikitext('[[Category:Uses ', quality, '-Quality ', name, '|', sort, ']]') | |||
else | |||
category:wikitext('[[Category:Uses ', quality, '-Quality ', name, ']]') | |||
end | |||
return | |||
end | |||
if sort and tostring(sort) ~= '' then | |||
category:wikitext('[[Category:Uses ', name, '|', sort, ']]') | |||
else | |||
category:wikitext('[[Category:Uses ', name, ']]') | |||
end | |||
end | |||
local function getCurrentPageName(args) | |||
return args[param.sPage] or args.page or mw.title.getCurrentTitle().text | |||
end | |||
local function getQualityClass(quality) | |||
if not quality or tostring(quality) == '' then | |||
return nil | |||
end | |||
return 'card-quality-' .. normalizeText(quality) | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Card helpers | |||
-------------------------------------------------------------------------------- | |||
local function addRecipeCard(category, sAmt, sName, quality) | |||
local cardArgs = { | |||
name = sName, | name = sName, | ||
text = sAmt, | text = sAmt, | ||
show_caption = '1' | show_caption = '1' | ||
} | |||
category | if quality then | ||
cardArgs.quality = quality | |||
end | |||
local cardNode = Card._main(cardArgs) | |||
addUseCategory( | |||
category, | |||
sName, | |||
quality, | |||
padLeft(tostring(sAmt or ''):gsub('%D', ''), 10) | |||
) | |||
return cardNode | return cardNode | ||
end | end | ||
function | local function addMiniCard(category, sName, sText, quality) | ||
if | local cardArgs = { | ||
name = sName, | |||
text = sText or '', | |||
show_caption = '1', | |||
mini = '1' | |||
} | |||
if quality then | |||
cardArgs.quality = quality | |||
end | end | ||
return | |||
local cardNode = Card._main(cardArgs) | |||
addUseCategory(category, sName) | |||
return cardNode | |||
end | end | ||
function | local function addYieldCard(pageTitle, sYield) | ||
return Card._main({ | |||
name = pageTitle, | |||
text = sYield, | |||
show_caption = '1' | |||
}) | }) | ||
end | end | ||
function | -------------------------------------------------------------------------------- | ||
local output | -- Header rendering | ||
-------------------------------------------------------------------------------- | |||
local function renderHeader(args, output, category) | |||
local header = output:tag('div'):addClass('recipe-header') | |||
local | local sType = args[param.sTypeArg] | ||
header:tag('span'):addClass('recipe-header-main') | |||
:tag('span'):addClass('recipe-header-text'):wikitext('[[' .. sType .. ']]'):done() | |||
category:wikitext('[[Category:' .. sType .. ']]') | |||
return header | |||
end | |||
local function renderTime(args, header) | |||
if type(args[param.sTimeArg]) ~= 'string' then | |||
return | |||
end | |||
local tTime = {} | |||
local key = false | |||
local plural = false | |||
for n1, d, n2, h, n3, m, n4, s in mw.ustring.gmatch( | |||
args[param.sTimeArg], | |||
'(%d+)(d?)(%d*)(h?)(%d*)(m?)(%d*)(s?)' | |||
) do | |||
table.insert(tTime, n1) | |||
if n1 ~= '1' then | |||
plural = true | |||
end | |||
if d == 'd' then | |||
table.insert(tTime, pluralize('day', plural)) | |||
plural = false | |||
key = true | |||
end | |||
if n2 ~= '' then | |||
table.insert(tTime, n2) | |||
if n2 ~= '1' then | |||
plural = true | |||
end | end | ||
end | |||
if h == 'h' then | |||
table.insert(tTime, pluralize('hour', plural)) | |||
plural = false | |||
key = true | |||
end | |||
if n3 ~= '' then | |||
table.insert(tTime, n3) | |||
if n3 ~= '1' then | |||
plural = true | |||
end | end | ||
end | |||
if m == 'm' then | |||
table.insert(tTime, pluralize('minute', plural)) | |||
plural = false | |||
key = true | |||
end | |||
if n4 ~= '' then | |||
table.insert(tTime, n4) | |||
if n4 ~= '1' then | |||
plural = true | |||
end | end | ||
end | end | ||
-- | |||
if type(args[param.sCharArg]) | if s == 's' then | ||
table.insert(tTime, pluralize('second', plural)) | |||
key = true | |||
end | |||
end | |||
if not key then | |||
table.insert(tTime, pluralize('minute', plural)) | |||
end | |||
local sTime = '' | |||
for _, each in ipairs(tTime) do | |||
sTime = sTime .. each .. ' ' | |||
end | |||
header:tag('span'):addClass('recipe-header-sub') | |||
:tag('span'):wikitext(' for '):done() | |||
:tag('span'):addClass('recipe-header-text'):wikitext(sTime):done() | |||
end | |||
local function renderCharacter(args, header) | |||
if type(args[param.sCharArg]) ~= 'string' then | |||
return | |||
end | |||
local sCharName = args[param.sCharArg] | |||
header:tag('span'):addClass('recipe-header-sub') | |||
:tag('span'):wikitext('[[File:' .. sCharName .. ' Icon.png|24x24px|link=' .. sCharName .. ']]'):done() | |||
:tag('span'):addClass('recipe-header-text'):wikitext('[[' .. sCharName .. ']]'):done() | |||
end | |||
local function renderKitchenwareHeader(args, header) | |||
if type(args[param.sKitchArg]) ~= 'string' then | |||
return | |||
end | |||
local sKitchName = args[param.sKitchArg] | |||
header:tag('span'):addClass('recipe-header-sub') | |||
:tag('span'):wikitext('[[File:' .. sKitchName .. ' Icon.png|24x24px|link=' .. sKitchName .. ']]'):done() | |||
:tag('span'):addClass('recipe-header-text'):wikitext('[[' .. sKitchName .. ']]'):done() | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Standard recipe rendering | |||
-------------------------------------------------------------------------------- | |||
local function isRestrictedStandardParam(sKey) | |||
for _, keyParam in pairs(param) do | |||
if keyParam == sKey then | |||
return true | |||
end | |||
end | |||
return false | |||
end | |||
local function renderStandardYield(args, body) | |||
if args[param.sYieldArg] == '0' then | |||
return | |||
end | |||
local sYield = args[param.sYieldArg] or nil | |||
local pageTitle = getCurrentPageName(args) | |||
local yieldCard = addYieldCard(pageTitle, sYield) | |||
body | |||
:tag('div'):addClass('recipe-body-icon') | |||
:wikitext('➜'):done() | |||
:tag('div'):addClass('recipe-body-yield') | |||
:node(yieldCard) | |||
end | |||
local function renderStandardRecipe(args, frame, output, category) | |||
local tArgsProcessed = {} | |||
local header = renderHeader(args, output, category) | |||
renderTime(args, header) | |||
renderCharacter(args, header) | |||
local body = output:tag('div'):addClass('recipe-body') | |||
local sSortDelim = param.sSortDelim | |||
if ((args[param.sSortArg] or ''):find(param.sSortDelim2) ~= nil) then | |||
sSortDelim = param.sSortDelim2 | |||
end | |||
for sKey in string.gmatch(args[param.sSortArg] or '', '([^' .. sSortDelim .. ']+)') do | |||
if type(args[sKey]) ~= 'nil' then | |||
body:node(addRecipeCard(category, args[sKey], sKey)) | |||
tArgsProcessed[sKey] = true | |||
end | |||
end | |||
for sKey, sVal in pairs(args) do | |||
if tArgsProcessed[sKey] ~= true and not isRestrictedStandardParam(sKey) then | |||
body:node(addRecipeCard(category, sVal, sKey)) | |||
end | end | ||
end | |||
renderStandardYield(args, body) | |||
body:done() | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Cooking parsing | |||
-------------------------------------------------------------------------------- | |||
local function getMaxCookingItemIndex(args) | |||
local maxIndex = 0 | |||
for key, _ in pairs(args) do | |||
local index = tostring(key):match('^item_(%d+)') | |||
if index then | |||
index = tonumber(index) | |||
if index and index > maxIndex then | |||
maxIndex = index | |||
end | end | ||
end | end | ||
end | |||
-- | return maxIndex | ||
end | |||
local | local function getCookingIngredient(args, index) | ||
local prefix = 'item_' .. index | |||
local ingredient = { | |||
index = index, | |||
amount = args[prefix .. '_amount'] or nil, | |||
quality = args[prefix .. '_quality'], | |||
if | item = args[prefix], | ||
types = {}, | |||
options = {} | |||
} | |||
if argExists(args, prefix .. '_type') then | |||
table.insert(ingredient.types, args[prefix .. '_type']) | |||
end | |||
local typeIndex = 1 | |||
while argExists(args, prefix .. '_type_' .. typeIndex) do | |||
table.insert(ingredient.types, args[prefix .. '_type_' .. typeIndex]) | |||
typeIndex = typeIndex + 1 | |||
end | |||
local optionIndex = 1 | |||
while argExists(args, prefix .. '_option_' .. optionIndex) do | |||
table.insert(ingredient.options, args[prefix .. '_option_' .. optionIndex]) | |||
optionIndex = optionIndex + 1 | |||
end | |||
local hasIngredient = | |||
argExists(args, prefix) | |||
or #ingredient.types > 0 | |||
or #ingredient.options > 0 | |||
if not hasIngredient then | |||
return nil | |||
end | |||
return ingredient | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Cooking type split-card helpers | |||
-------------------------------------------------------------------------------- | |||
local function makeTypeIcon(typeName) | |||
return '[[File:' .. typeName .. ' Icon.png|74x74px|link=' .. typeName .. ']]' | |||
end | |||
local function joinTypeCaption(types) | |||
local out = {} | |||
for _, typeName in ipairs(types) do | |||
table.insert(out, '[[' .. typeName .. ']]') | |||
end | |||
return table.concat(out, ' / ') | |||
end | |||
local function renderCookingSplitTypeCard(category, body, ingredient) | |||
local qualityClass = getQualityClass(ingredient.quality or 'Gray') | |||
local container = body:tag('span') | |||
:addClass('card-container') | |||
:addClass('recipe-type-split-card') | |||
local wrapper = container:tag('span') | |||
:addClass('card-wrapper') | |||
local cardBody = wrapper:tag('span') | |||
:addClass('card-body') | |||
local imageContainer = cardBody:tag('span') | |||
:addClass('card-image-container') | |||
:addClass('recipe-type-split-image') | |||
imageContainer:tag('span') | |||
:addClass('recipe-type-split-half') | |||
:addClass('recipe-type-split-half-a') | |||
:wikitext(makeTypeIcon(ingredient.types[1])) | |||
:done() | |||
imageContainer:tag('span') | |||
:addClass('recipe-type-split-half') | |||
:addClass('recipe-type-split-half-b') | |||
:wikitext(makeTypeIcon(ingredient.types[2])) | |||
:done() | |||
if qualityClass then | |||
imageContainer:addClass(qualityClass) | |||
end | |||
if tostring(ingredient.amount or '') ~= '' then | |||
container:tag('span') | |||
:addClass('card-label') | |||
:wikitext(tostring(ingredient.amount or '1')) | |||
:done() | |||
end | |||
container:tag('span') | |||
:addClass('card-caption') | |||
:wikitext(joinTypeCaption(ingredient.types)) | |||
:done() | |||
for _, typeName in ipairs(ingredient.types) do | |||
addUseCategory(category, typeName) | |||
end | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Cooking display | |||
-------------------------------------------------------------------------------- | |||
local function renderCookingSpecificItem(category, body, ingredient) | |||
body:node(addRecipeCard( | |||
category, | |||
ingredient.amount, | |||
ingredient.item, | |||
ingredient.quality | |||
)) | |||
end | |||
local function renderCookingTypeFallback(category, body, ingredient) | |||
local wrapper = body:tag('div') | |||
:addClass('recipe-body-cooking-ingredient') | |||
:addClass('recipe-body-cooking-type-cards') | |||
for _, typeName in ipairs(ingredient.types) do | |||
wrapper:node(addMiniCard( | |||
category, | |||
typeName, | |||
ingredient.amount, | |||
ingredient.quality | |||
)) | |||
end | |||
end | |||
local function renderCookingTypeCards(category, body, ingredient) | |||
if #ingredient.types == 2 then | |||
renderCookingSplitTypeCard(category, body, ingredient) | |||
else | |||
renderCookingTypeFallback(category, body, ingredient) | |||
end | |||
end | |||
local function renderCookingOptionCards(category, body, ingredient) | |||
local wrapper = body:tag('div') | |||
:addClass('recipe-body-cooking-ingredient') | |||
:addClass('recipe-body-cooking-option-cards') | |||
for _, itemName in ipairs(ingredient.options) do | |||
wrapper:node(addMiniCard( | |||
category, | |||
itemName, | |||
ingredient.amount, | |||
ingredient.quality | |||
)) | |||
end | |||
end | |||
local function renderCookingIngredient(category, body, ingredient) | |||
if ingredient.item then | |||
renderCookingSpecificItem(category, body, ingredient) | |||
return | |||
end | |||
if #ingredient.options > 0 then | |||
renderCookingOptionCards(category, body, ingredient) | |||
return | |||
end | |||
if #ingredient.types > 0 then | |||
renderCookingTypeCards(category, body, ingredient) | |||
return | |||
end | |||
end | |||
local function renderCookingYield(args, category, body) | |||
if args[param.sYieldArg] == '0' then | |||
return | |||
end | |||
local sYield = args[param.sYieldArg] or nil | |||
local pageTitle = getCurrentPageName(args) | |||
local yieldCard = addYieldCard(pageTitle, sYield) | |||
body | |||
:tag('div'):addClass('recipe-body-icon') | |||
:wikitext('➜'):done() | |||
:tag('div'):addClass('recipe-body-yield') | |||
:node(yieldCard) | |||
end | |||
local function renderCookingRecipe(args, frame, output, category) | |||
local header = renderHeader(args, output, category) | |||
renderTime(args, header) | |||
renderCharacter(args, header) | |||
renderKitchenwareHeader(args, header) | |||
local body = output:tag('div') | |||
:addClass('recipe-body') | |||
:addClass('recipe-body-cooking') | |||
local foundIngredient = false | |||
local maxIndex = getMaxCookingItemIndex(args) | |||
for index = 1, maxIndex do | |||
local ingredient = getCookingIngredient(args, index) | |||
if ingredient then | |||
foundIngredient = true | |||
renderCookingIngredient(category, body, ingredient) | |||
end | end | ||
end | |||
if not foundIngredient then | |||
body:tag('span') | |||
:addClass('error') | |||
:wikitext('Cooking recipe error. No cooking ingredients found.') | |||
end | |||
renderCookingYield(args, category, body) | |||
body:done() | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Entry points | |||
-------------------------------------------------------------------------------- | |||
function p.main(frame) | |||
local args = require('Module:Arguments').getArgs(frame, { | |||
parentFirst = true, | |||
wrapper = { 'Template:Recipe' } | |||
}) | |||
return p._main(args, frame) | |||
end | |||
function p._main(args, frame) | |||
local output = mw.html.create('div') | |||
:addClass('recipe-container') | |||
local category = mw.html.create() | |||
if type(args[param.sTypeArg]) ~= 'string' then | |||
output:tag('span') | |||
:addClass('error') | |||
:wikitext('Recipe type error. Must include recipe type.') | |||
else | |||
local sType = args[param.sTypeArg] | |||
if normalizeText(sType) == 'cooking' then | |||
renderCookingRecipe(args, frame, output, category) | |||
else | |||
renderStandardRecipe(args, frame, output, category) | |||
end | end | ||
end | end | ||
output:node(require('Module:Namespace detect').main{["main"]=category}) | |||
output:node(require('Module:Namespace detect').main({ | |||
["main"] = category | |||
})) | |||
return output | return output | ||
end | end | ||
return p | return p | ||