Module:Recipes

--- l10n info -- local l10n_info = mw.loadData('Module:Recipes/l10n')

--- The following is not related to l10n. --

local item_link = require('Module:Item').go local trim = mw.text.trim local cargo = mw.ext.cargo local cache = require 'mw.ext.LuaCache'

local currentFrame -- global cache for current frame object. local inputArgs -- global args cache. local lang -- cache current lang. local l10n_table

local resultanchor

local l10n = function(key) return l10n_table[key] or l10n_info['en'][key] end

local extCols_stationBefore = nil local extCols_stationAfter = nil

local extCols_A = nil local extCols_B = nil local extCols_C = nil local extCols_D = nil

function getArg(key) local v = trim(inputArgs[key] or '') if v=='' then return nil else return v	end end

local itemLink = (function	local cache = {}	return function(name, args)		local key = name.."|"		if args then			for k, v in pairs(args) do				key = key..k..'='..tostring(v)..'|'			end		end		if not cache[key] then			local args = args and mw.clone(args) or {}			args['mode'] = args['mode'] or nil			args[1] = name			if (not args[2]) or args[2] == '' then				args[2] = currentFrame:expandTemplate{ title = 'tr', args = {name, lang=lang} }			end			args['small'] = 'y'			args['lang'] = lang or 'en'			args['nolink'] = args['nolink'] and 'y' or nil			args['class'] = args['class'] or nil			cache[key] = item_link(currentFrame, args)		end		return cache[key]	end end)

-- credit: http://richard.warburton.it -- this version is with trim. local explode = function(div,str) if (div=='') then return false end local pos,arr = 0,{} -- for each divider found for st,sp in function return string.find(str,div,pos,true) end do		table.insert(arr, trim(string.sub(str,pos,st-1))) -- Attach chars left of current divider pos = sp + 1 -- Jump past current divider end table.insert(arr, trim(string.sub(str,pos))) -- Attach chars right of last divider return arr end

-- retuan a array of itemname, split xxx/yyy to item1=xxx, item2=yyy. If it's something like "Lead/Iron Bar", it will normalize as item1 = Iron Bar, item2 = Lead Bar. local split = (function	local metals = {		['Copper/Tin'] = 1,		['Silver/Tungsten'] = 1,		['Gold/Platinum'] = 1,		['Iron/Lead'] = 1,		['Cobalt/Palladium'] = 1,		['Mythril/Orichalcum'] = 1,		['Adamantite/Titanium'] = 1,		['Tin/Copper'] = 2,		['Tungsten/Silver'] = 2,		['Platinum/Gold'] = 2,		['Lead/Iron'] = 2,		['Palladium/Cobalt'] = 2,		['Orichalcum/Mythril'] = 2,		['Titanium/Adamantite'] = 2,	}	return function(name)		local count = select(2, name:gsub("/", "/", 2))		if count == 0 then			-- only 1 item			return { trim(name) }		elseif count == 1 then			-- 2 items			local item1a, item1b, item2a, item2b = name:match("^%s*(%S+)%s*(.-)/%s*(%S+)%s*(.-)$")			local x = metals[item1a..'/'..item2a]			if tostring(item1b) == '' and x then				item1b = item2b			end			if x == 2 then				return {trim(item2a..' '..item2b), trim(item1a..' '..item1b)}			else				return {trim(item1a..' '..item1b), trim(item2a..' '..item2b)} end else -- 3 or more items return explode('/', name) end end end)

-- return 1 or 2 value(s), when input is name[note], return item, note. local itemname = function(str) local item, note = str:match("^(.-)(%b[])$") if item then return item, note else return str end end

-- normalize ingredient name input, Lead Bar=>‡Lead Bar‡, Iron/Lead Bar => ‡Iron Bar‡Lead Bar‡, Lead/Iron Bar => ‡Iron Bar‡Lead Bar‡ .... local normalize = function(name) local result = '‡' for k, v in ipairs(split(name)) do result = result .. itemname(v) .. '‡'	end return result end

local escape = function(str) return str:gsub("'", "\\'"):gsub("&#39;", "\\'") end local enclose = function(str) return "'" .. escape(str) .. "'" end

local getItemGroupName = function(item) if item == 'Wood' or item == 'Ebonwood' or item == 'Rich Mahogany' or item == 'Pearlwood' or item == 'Shadewood' or item == 'Spooky Wood' or item == 'Boreal Wood' or item == 'Palm Wood' or item == 'Acidwood' then return 'Any Wood' elseif item == 'Iron Bar' or item == 'Lead Bar' then return 'Any Iron Bar' elseif item == 'Sand Block' or item == 'Pearlsand Block' or item == 'Crimsand Block' or item == 'Ebonsand Block' or item == 'Hardened Sand Block' or item == 'Astral Sand' then return 'Any Sand' elseif item == 'Red Pressure Plate' or item == 'Green Pressure Plate' or item == 'Gray Pressure Plate' or item == 'Brown Pressure Plate' or item == 'Blue Pressure Plate' or item == 'Yellow Pressure Plate' or item == 'Lihzahrd Pressure Plate' then return 'Any Pressure Plate' elseif item == 'Bird' or item == 'Blue Jay' or item == 'Cardinal' then return 'Any Bird' elseif item == 'Black Scorpion' or item == 'Scorpion' then return 'Any Scorpion' elseif item == 'Squirrel' or item == 'Red Squirrel' then return 'Any Squirrel' elseif item == 'Grubby' or item == 'Sluggy' or item == 'Buggy' then return 'Any Bug' elseif item == 'Mallard Duck' or item == 'Duck' then return 'Any Duck' elseif item == 'Sulphur Butterfly' or item == 'Julia Butterfly' or item == 'Monarch Butterfly' or item == 'Purple Emperor Butterfly' or item == 'Red Admiral Butterfly' or item == 'Tree Nymph Butterfly' or item == 'Ulysses Butterfly' or item == 'Zebra Swallowtail Butterfly' then return 'Any Butterfly' elseif item == 'Firefly' or item == 'Lightning Bug' or item == 'Twinkler' then return 'Any Firefly' elseif item == 'Snail' or item == 'Glowing Snail' then return 'Any Snail' elseif item == 'Apple' or item == 'Apricot' or item == 'Banana' or item == 'Blackcurrant' or item == 'Blood Orange' or item == 'Cherry' or item == 'Coconut' or item == 'Dragon Fruit' or item == 'Elderberry' or item == 'Grapefruit' or item == 'Lemon' or item == 'Mango' or item == 'Peach' or item == 'Pineapple' or item == 'Plum' or item == 'Rambutan' or item == 'Star Fruit' then return 'Any Fruit' elseif item == 'Black Dragonfly' or item == 'Blue Dragonfly' or item == 'Green Dragonfly' or item == 'Orange Dragonfly' or item == 'Red Dragonfly' or item == 'Yellow Dragonfly' then return 'Any Dragonfly' elseif item == 'Turtle' or item == 'Jungle Turtle' then return 'Any Turtle' elseif item == 'Copper Bar' or item == 'Tin Bar' then return 'Any Copper Bar' elseif item == 'Gold Ore' or item == 'Platinum Ore' then return 'Any Gold Ore' elseif item == 'Gold Bar' or item == 'Platinum Bar' then return 'Any Gold Bar' elseif item == 'Ebonstone Block' or item == 'Crimstone Block' then return 'Any Evil Block' elseif item == 'Demonite Bar' or item == 'Crimtane Bar' then return 'Any Evil Bar' elseif item == 'Cobalt Bar' or item == 'Palladium Bar' then return 'Any Cobalt Bar' elseif item == 'Adamantite Bar' or item == 'Titanium Bar' then return 'Any Adamantite Bar' elseif item == 'Nightmare Fuel' or item == 'Endothermic Energy' then return 'Any Seasonal Energy' elseif item == 'Fetid Essence' or item == 'Bloodletting Essence' then return 'Any Evil Essence' elseif item == 'Shadow Scale' or item == 'Tissue Sample' then return 'Any Evil Flesh' elseif item == 'Flask of Cursed Flames' or item == 'Flask of Ichor' then return 'Any Evil Flask' elseif item == 'Unholy Water' or item == 'Blood Water' then return 'Any Evil Water' elseif item == 'Cursed Flame' or item == 'Ichor' then return 'Any Hardmode Evil Component' elseif item == 'Snow Block' or item == 'Astral Snow' then return 'Any Snow Block' elseif item == 'Mythril Anvil' or item == 'Orichalcum Anvil' then return 'Any Hardmode Anvil' elseif item == 'Adamantite Forge' or item == 'Titanium Forge' then return 'Any Hardmode Forge' elseif item == 'Ice Block' or item == 'Purple Ice Block' or item == 'Red Ice Block' or item == 'Pink Ice Block' or item == 'Astral Ice' then return 'Any Ice Block' elseif item == 'Silt Block' or item == 'Slush Block' or item == 'Novae Slag' then return 'Any Silt Block' elseif item == 'Solar Flare Pickaxe' or item == 'Vortex Pickaxe' or item == 'Nebula Pickaxe' or item == 'Stardust Pickaxe' then return 'Lunar Pickaxe' elseif item == 'Solar Flare Hamaxe' or item == 'Vortex Hamaxe' or item == 'Nebula Hamaxe' or item == 'Stardust Hamaxe' then return 'Lunar Hamaxe' elseif item == 'Cooked Fish' or item == 'Cooked Marshmallow' or item == 'Pad Thai' or item == 'Pho' or item == 'Cooked Shrimp' or item == 'Sashimi' or item == 'Bacon' or item == 'Bowl of Soup' or item == 'Grub Soup' or item == 'Gingerbread Cookie' or item == 'Sugar Cookie' or item == 'Christmas Pudding' or item == 'Pumpkin Pie' or item == 'Baguette' or item == 'Delicious Meat' or item == 'Hadal Stew' then return 'Any Food' elseif item == 'Demon Wings' or item == 'Angel Wings' or item == 'Red\'s Wings' or item == 'Butterfly Wings' or item == 'Fairy Wings' or item == 'Harpy Wings' or item == 'Bone Wings' or item == 'Flame Wings' or item == 'Frozen Wings' or item == 'Spectre Wings' or item == 'Steampunk Wings' or item == 'Leaf Wings' or item == 'Bat Wings' or item == 'Bee Wings' or item == 'D-Town\'s Wings' or item == 'Will\'s Wings' or item == 'Crowno\'s Wings' or item == 'Cenx\'s Wings' or item == 'Tattered Fairy Wings' or item == 'Spooky Wings' or item == 'Hoverboard' or item == 'Festive Wings' or item == 'Beetle Wings' or item == 'Fin Wings' or item == 'Fishron Wings' or item == 'Mothron Wings' or item == 'Solar Wings' or item == 'Vortex Booster' or item == 'Nebula Mantle' or item == 'Stardust Wings' or item == 'Yoraiz0r\'s Spell' or item == 'Jim\'s Wings' or item == 'Skiphs\'s Paws' or item == 'Loki\'s Wings' or item == 'Betsy\'s Wings' or item == 'Arkhalis\'s Lightwings' or item == 'Skyline Wings' or item == 'Starlight Wings' or item == 'Aureate Booster' or item == 'Hadal Mantle' or item == 'Exodus Wings' or item == 'Hadarian Wings' or item == 'Tarragon Wings' or item == 'Silva Wings' then return 'Any Wings' end end

local normalizeStation = function(station) if station == 'Altar' then station = 'Demon Altar' end return station end

local criStr = function(args) local constraints = {} -- station = ? and station != ? local _station = trim(args['station'] or '') local _stationnot = trim(args['stationnot'] or '') local str = '' if _station ~= '' then for _, v in ipairs(explode('/', _station)) do			if str ~= '' then str = str .. ' OR ' end str = str .. "station = " .. enclose(normalizeStation(v)) end end if _stationnot ~= '' then if str ~= '' then str = '(' .. str .. ')' end for _, v in ipairs(explode('/', _stationnot)) do			if str ~= '' then str = str .. ' AND ' end str = str .. 'station <> ' .. enclose(normalizeStation(v)) end end constraints['station'] = str -- result = ? and result != ? local _result = trim(args['result'] or '') local _resultnot = trim(args['resultnot'] or '') local str = '' if _result ~= '' then for _, v in ipairs(explode('/', _result)) do			if str ~= '' then str = str .. ' OR ' end if mw.ustring.sub(v, 1, 5) == 'LIKE ' then str = str .. "result LIKE " .. enclose(trim(mw.ustring.sub(v, 6))) else str = str .. 'result=' .. enclose(v) end end end if _resultnot ~= '' then if str ~= '' then str = '(' .. str .. ')' end for _, v in ipairs(explode('/', _resultnot)) do			if str ~= '' then str = str .. ' AND ' end if mw.ustring.sub(v, 1, 5) == 'LIKE ' then str = str .. "result NOT LIKE " .. enclose(trim(mw.ustring.sub(v, 6))) else str = str .. 'result <> ' .. enclose(v) end end end if str ~= '' then constraints['result'] = str end -- ingredient = ? local _ingredient = trim(args['ingredient'] or '') if _ingredient ~= '' then local str = '' for _, v in ipairs(explode('/', _ingredient)) do			if str ~= '' then str = str .. ' OR ' end if mw.ustring.sub(v, 1, 1) == '#' then str = str .. "ingredients HOLDS LIKE '%‡" .. escape(mw.ustring.sub(v, 2)) .. "‡%'"			elseif mw.ustring.sub(v, 1, 5) == 'LIKE ' then str = str .. "ingredients HOLDS LIKE '%‡" .. escape(trim(mw.ustring.sub(v, 6))) .. "‡%'"			else str = str .. "ingredients HOLDS LIKE '%‡" .. escape(v) .. "‡%'"				-- any xxx local group = getItemGroupName(v) if group then str = str .. " OR ingredients HOLDS LIKE '%‡" .. escape(group) .. "‡%'"				end end end constraints['ingredient'] = str end

local where = '' if constraints['station'] then where = constraints['station'] end if constraints['result'] then if where ~= '' then where = where .. ' AND ' end where = where .. '(' .. constraints['result'] .. ')' end if constraints['ingredient'] then if where ~= '' then where = where .. ' AND ' end where = where .. '(' .. constraints['ingredient'] .. ')' end -- query historical recipes? if trim(args['historical'] or '') == 'only' then where = where .. ' AND historical IS NOT NULL' elseif trim(args['historical'] or '') ~= 'include' then where = where .. ' AND historical IS NULL' end return where end

local resultCell = function(row, needLink, template) local result, resultimage, resulttext, amount, historical = row['result'], row['resultimage'], row['resulttext'], row['amount'], row['historical'] local str = '' local args = {anchor = resultanchor, nolink = not needLink, class='multi-line'} if resultimage then args['image'] = resultimage end if resulttext then args[2] = currentFrame:expandTemplate{ title = 'tr', args = {resulttext, lang=lang} } end str = str .. itemLink(result, args) if amount ~= '1' then str = str .. ' ('..amount..') ' end if historical == "1" then str = str .. l10n('historical_recipe_note') end if template then local template_str = currentFrame:expandTemplate{ title = template, args = { link = needLink, resultimage=resultimage, resulttext=resulttext, result=result, amount=amount, } }		str = template_str:gsub('@@@@', str) end return str end

local ingredientsCell = function(args) local str = '' for _, v in ipairs(explode('^', args)) do str = str .. '' local item, amount = v:match('^(.-)‡(.-)$') local s		if item ~= nil then for _, itemname in ipairs(split(item)) do				local actualitemname, itemtext if string.match(itemname, '¦') ~= nil then actualitemname, itemtext = itemname:match('^(.-)¦(.-)$') end if s then s = s .. l10n('ingredients_sep') end if itemtext then local itemlinkoptions = {} itemlinkoptions[2] = currentFrame:expandTemplate{ title = 'tr', args = {itemtext, lang=lang} } s = (s or '') .. itemLink(actualitemname, itemlinkoptions) else s = (s or '') .. itemLink(itemname) end end else s = l10n('no_ingredients') end str = str .. s		if amount ~= nil then if amount ~= '1' then str = str .. ' ('..amount..') ' end end str = str .. '' end str = str .. '' return str end

local stationCell = function(station, options) options = options or {wrap = 'y'} if station == 'By Hand' then return l10n('station_by_hand') elseif station == 'Furnace' or station == 'Work Bench' or station == 'Sawmill' or station == "Tinkerer's Workshop" or station == 'Dye Vat' or station == 'Loom' or station == 'Keg' or station == 'Hellforge' or station == 'Bookcase' or station == 'Imbuing Station' or station == 'Lava' or station == 'Honey' or station == 'Glass Kiln' or station == 'Flesh Cloning Vat' or station == 'Autohammer' or station == 'Crystal Ball' or station == 'Ice Machine' or station == 'Meat Grinder' or station == 'Living Loom' or station == 'Heavy Work Bench' or station == 'Sky Mill' or station == 'Solidifier' or station == 'Honey Dispenser' or station == 'Bone Welder' or station == 'Blend-O-Matic' or station == 'Steampunk Boiler' or station == 'Ancient Manipulator' or station == 'Lihzahrd Furnace' or station == 'Ancient Altar' or station == 'Ashen Altar' or station == 'Botanic Planter' or station == "Draedon's Forge" or station == 'Effulgent Manipulator' or station == 'Eutrophic Shelf' or station == 'Monolith Amalgam' or station == 'Plague Infuser' or station == 'Profaned Crucible' or station == 'Static Refiner' or station == 'Void Condenser' or station == 'Particle Accelerator' then return itemLink(station, options) elseif station == 'Iron Anvil' then return itemLink('Iron Anvil', options) .. l10n('station_sep_or') .. itemLink('Lead Anvil', options) elseif station == 'Adamantite Forge' then return itemLink('Adamantite Forge', options) .. l10n('station_sep_or') .. itemLink('Titanium Forge', options) elseif station == 'Mythril Anvil' then return itemLink('Mythril Anvil', options) .. l10n('station_sep_or') .. itemLink('Orichalcum Anvil', options) elseif station == 'Demon Altar' then return itemLink('Demon Altar', options) .. l10n('station_sep_or') .. itemLink('Crimson Altar', options) elseif station == 'Cooking Pot' then return itemLink('Cooking Pot', options) .. l10n('station_sep_or') .. itemLink('Cauldron', options) elseif station == 'Placed Bottle' then return itemLink('Placed Bottle', options) .. l10n('station_sep_or') .. itemLink('Alchemy Table', options) elseif station == 'Water' then return itemLink('Water', options) .. l10n('station_sep_or') .. itemLink('Sink', options) elseif station == 'Table and Chair' then return itemLink('Table', options) .. l10n('station_sep_and') .. itemLink('Chair', options) elseif station == 'Work Bench and Chair' then return itemLink('Work Bench', options) .. l10n('station_sep_and') .. itemLink('Chair', options) elseif station == 'Crystal Ball and Lava' then return itemLink('Crystal Ball', options) .. l10n('station_sep_and') .. itemLink('Lava', options) elseif station == 'Crystal Ball and Honey' then return itemLink('Crystal Ball', options) .. l10n('station_sep_and') .. itemLink('Honey', options) elseif station == 'Crystal Ball and Water' then return itemLink('Crystal Ball', options) .. l10n('station_sep_and').. ' ' .. itemLink('Water', options) .. l10n('station_sep_or') .. itemLink('Sink', options) .. ' '	elseif station == 'Sky Mill and Water' then return itemLink('Sky Mill', options) .. l10n('station_sep_and').. ' ' .. itemLink('Water', options) .. l10n('station_sep_or') .. itemLink('Sink', options) .. ' '	elseif station == 'Sky Mill and Snow Biome' then return itemLink('Sky Mill', options) .. l10n('station_sep_and').. l10n('snow_biome') elseif station == 'Placed Bottle only' then return itemLink('Placed Bottle', options) elseif station == 'Alchemy Table only' then return itemLink('Alchemy Table', options) elseif station == 'Profaned Crucible and Lava' then return itemLink('Profaned Crucible', options) .. l10n('station_sep_and') .. itemLink('Lava', options) else return station end end -- for extract. local compactStation = function(station) if station == 'By Hand' then return '(' .. l10n('station_by_hand') .. ')' elseif station == 'Furnace' or station == 'Work Bench' or station == 'Sawmill' or station == "Tinkerer's Workshop" or station == 'Dye Vat' or station == 'Loom' or station == 'Keg' or station == 'Hellforge' or station == 'Bookcase' or station == 'Imbuing Station' or station == 'Lava' or station == 'Honey' or station == 'Glass Kiln' or station == 'Flesh Cloning Vat' or station == 'Autohammer' or station == 'Crystal Ball' or station == 'Ice Machine' or station == 'Meat Grinder' or station == 'Living Loom' or station == 'Heavy Work Bench' or station == 'Sky Mill' or station == 'Solidifier' or station == 'Honey Dispenser' or station == 'Bone Welder' or station == 'Blend-O-Matic' or station == 'Steampunk Boiler' or station == 'Ancient Manipulator' or station == 'Lihzahrd Furnace' or station == 'Ancient Altar' or station == 'Ashen Altar' or station == 'Botanic Planter' or station == "Draedon's Forge" or station == 'Effulgent Manipulator' or station == 'Eutrophic Shelf' or station == 'Monolith Amalgam' or station == 'Plague Infuser' or station == 'Profaned Crucible' or station == 'Static Refiner' or station == 'Void Condenser' then return l10n('compact_before') .. itemLink(station, {mode = 'image'}) elseif station == 'Iron Anvil' then return l10n('compact_before') .. itemLink('Iron Anvil', {mode = 'image'}) .. "&thinsp;/&thinsp;" .. itemLink('Lead Anvil', {mode = 'image'}) elseif station == 'Adamantite Forge' then return l10n('compact_before') .. itemLink('Adamantite Forge', {mode = 'image'}) .. "&thinsp;/&thinsp;" .. itemLink('Titanium Forge', {mode = 'image'}) elseif station == 'Mythril Anvil' then return l10n('compact_before') .. itemLink('Mythril Anvil', {mode = 'image'}) .. "&thinsp;/&thinsp;" .. itemLink('Orichalcum Anvil', {mode = 'image'}) elseif station == 'Demon Altar' then return l10n('compact_before') .. itemLink('Demon Altar', {mode = 'image'}) .. "&thinsp;/&thinsp;" .. itemLink('Crimson Altar', {mode = 'image'}) elseif station == 'Cooking Pot' then return l10n('compact_before') .. itemLink('Cooking Pot', {mode = 'image'}) .. "&thinsp;/&thinsp;" .. itemLink('Cauldron', {mode = 'image'}) elseif station == 'Placed Bottle' then return l10n('compact_before') .. itemLink('Placed Bottle', {mode = 'image'}) .. "&thinsp;/&thinsp;" .. itemLink('Alchemy Table', {mode = 'image'}) elseif station == 'Water' then return l10n('compact_before') .. itemLink('Water', {mode = 'image'}) .. "&thinsp;/&thinsp;" .. itemLink('Sink', {mode = 'image'}) elseif station == 'Table and Chair' then return l10n('compact_before') .. itemLink('Table', {mode = 'image'}) .. "&thinsp;&amp;&thinsp;" .. itemLink('Chair', {mode = 'image'}) elseif station == 'Work Bench and Chair' then return l10n('compact_before') .. itemLink('Work Bench', {mode = 'image'}) .. "&thinsp;&amp;&thinsp;" .. itemLink('Chair', {mode = 'image'}) elseif station == 'Crystal Ball and Lava' then return l10n('compact_before') .. itemLink('Crystal Ball', {mode = 'image'}) .. "&thinsp;&amp;&thinsp;" .. itemLink('Lava', {mode = 'image'}) elseif station == 'Crystal Ball and Honey' then return l10n('compact_before') .. itemLink('Crystal Ball', {mode = 'image'}) .. "&thinsp;&amp;&thinsp;" .. itemLink('Honey', {mode = 'image'}) elseif station == 'Crystal Ball and Water' then return l10n('compact_before') .. itemLink('Crystal Ball', {mode = 'image'}) .. "&thinsp;&amp;&thinsp;".. itemLink('Water', {mode = 'image'}) .. " / " ..		itemLink('Crystal Ball', {mode = 'image'}) .. "&thinsp;&amp;&thinsp;" .. itemLink('Sink', {mode = 'image'}) elseif station == 'Sky Mill and Water' then return l10n('compact_before') .. itemLink('Sky Mill', {mode = 'image'}) .. "&thinsp;&amp;&thinsp;".. itemLink('Water', {mode = 'image'}) .. " / " .. 		itemLink('Sky Mill', {mode = 'image'}) .. "&thinsp;&amp;&thinsp;".. itemLink('Sink', {mode = 'image'}) elseif station == 'Sky Mill and Snow Biome' then return l10n('compact_before') .. itemLink('Sky Mill', {mode = 'image'}) .. "&thinsp;&amp;&thinsp;".. l10n('compact_snow_biome') elseif station == 'Placed Bottle only' then return l10n('compact_before') .. itemLink('Placed Bottle', {mode = 'image'}) elseif station == 'Alchemy Table only' then return l10n('compact_before') .. itemLink('Alchemy Table', {mode = 'image'}) elseif station == 'Profaned Crucible and Lava' then return l10n('compact_before') .. itemLink('Profaned Crucible', {mode = 'image'}) .. "&thinsp;&amp;&thinsp;".. itemLink('Lava', {mode = 'image'}) else return l10n('compact_before') .. station end end

local getFlags = function(args) local needCate = 1 local needLink = true local _cate = trim(args['cate'] or '') if _cate == 'force' or _cate == 'all' then needCate = 2 elseif _cate == 'n' or _cate == 'no' then needCate = nil end local _link = trim(args['link'] or '') if _link == 'y' or _link == 'yes' or _link == 'force' then needLink = true elseif _link == 'n' or _link == 'no' then needLink = false end return needCate, needLink end

local addCate, cateStr -- for table body. init in p.query

local tableStart = function(title, withStation) local header_ local str = ' total: '..rows_count..' row(s)   ' if expectedrows and rows_count ~= expectedrows then str = str .. ''	end if not expectedrows and rows_count == 0 then str = str .. ''	end return str end

local tableRow = function(str, row, current_station, station_count, rows_count, withStation, needCate, needLink, needGroup, current_result, result_count, current_result_ext, result_ext_count, template, stationGroup) local str_w = '' -- before result col local str_x = '' -- between result and ingredients cols local str_y = '' -- between ingredients and station cols local str_z = '' -- after station local str_resultCell = ''

local result_index = getArg('result-index-#'..rows_count) or getArg('result-index-'..row['result'])

str = str .. ''

if needGroup then local result = row['result']..'|'..row['resultimage']..'|'..row['resulttext']..'|'..row['amount'] -- grouping result col if current_result == result then -- is same group ?? result_count = result_count + 1 else --new group: -- rowspan value for prev group, if needed. if result_count then str = str:gsub("yyyrowspanyyy", tostring(result_count)) end -- begin this group current_result = result result_count = 1 str_resultCell = ''.. resultCell(row, needLink, template).. ' '		end -- grouping ext cols if result_index and (current_result_ext == result_index) then -- is same group ?? result_ext_count = result_ext_count + 1 else --new group: -- rowspan value for prev group, if needed. if result_ext_count then str = str:gsub("zzzrowspanzzz", tostring(result_ext_count)) end -- begin this group current_result_ext = result_index result_ext_count = 1 if extCols_A then for _, v in ipairs(extCols_A) do					if result_index then str_w = str_w .. '' .. (getArg(result_index .. '-row-' .. v) or '') .. ' '					else str_w = str_w .. ' ' end end end if extCols_B then for _, v in ipairs(extCols_B) do					if result_index then str_x = str_x .. '' .. (getArg(result_index .. '-row-' .. v) or '') .. ' '					else str_x = str_x .. ' ' end end end if extCols_C then for _, v in ipairs(extCols_C) do					if result_index then str_y = str_y .. '' .. (getArg(result_index .. '-row-' .. v) or '') .. ' '					else str_y = str_y .. ' ' end end end if extCols_D then for _, v in ipairs(extCols_D) do					if result_index then str_z = str_z .. '' .. (getArg(result_index .. '-row-' .. v) or '') .. ' '					else str_z = str_z .. ' ' end end end end else if extCols_A then for _, v in ipairs(extCols_A) do				if result_index then str_w = str_w .. '' .. (getArg(result_index .. '-row-' .. v) or '') .. ' '				else str_w = str_w .. '<td class="'..v..'"> ' end end end if extCols_B then for _, v in ipairs(extCols_B) do				if result_index then str_x = str_x .. '<td class="'..v..'">' .. (getArg(result_index .. '-row-' .. v) or '') .. ' '				else str_x = str_x .. '<td class="'..v..'"> ' end end end if extCols_C then for _, v in ipairs(extCols_C) do				if result_index then str_y = str_y .. '<td class="'..v..'">' .. (getArg(result_index .. '-row-' .. v) or '') .. ' '				else str_y = str_y .. '<td class="'..v..'"> ' end end end if extCols_D then for _, v in ipairs(extCols_D) do				if result_index then str_z = str_z .. '<td class="'..v..'">' .. (getArg(result_index .. '-row-' .. v) or '') .. ' '				else str_z = str_z .. '<td class="'..v..'"> ' end end end str_resultCell = ' '.. resultCell(row, needLink, template).. ' '	end

str = str .. str_w .. str_resultCell .. str_x .. ' ' .. ingredientsCell(row['args']).. ' ' .. str_y

if withStation then local station = row['station'] if stationGroup then if current_station == station then -- is same group ?? station_count = station_count + 1 else --new group: -- rowspan value for prev group, if needed. if station_count then str = str:gsub("xxxrowspanxxx", tostring(station_count)) end -- begin this group current_station = station station_count = 1 local station_index = getArg('station-index-'..station) -- station before: if extCols_stationBefore then for _, v in ipairs(extCols_stationBefore) do						if station_index then str = str .. '<td class="station '..v..'" rowspan="xxxrowspanxxx">' .. (getArg(station_index .. '-row-' .. v) or '') .. ' '						else str = str .. '<td class="station '..v..'" rowspan="xxxrowspanxxx"> ' end end end str = str .. '<td class="station" rowspan="xxxrowspanxxx">'.. stationCell(station) ..' ' -- station after: if extCols_stationAfter then for _, v in ipairs(extCols_stationAfter) do						if station_index then str = str .. '<td class="station '..v..'" rowspan="xxxrowspanxxx">' .. (getArg(station_index .. '-row-' .. v) or '') .. ' '						else str = str .. '<td class="station '..v..'" rowspan="xxxrowspanxxx"> ' end end end end else if current_station == station then -- is same group ?? station_count = station_count + 1 else current_station = station station_count = 1 end local station_index = getArg('station-index-'..station) -- station before: if extCols_stationBefore then for _, v in ipairs(extCols_stationBefore) do					if station_index then str = str .. '<td class="station '..v..'">' .. (getArg(station_index .. '-row-' .. v) or '') .. ' '					else str = str .. '<td class="station '..v..'"> ' end end end str = str .. ' '.. stationCell(station) ..' ' -- station after: if extCols_stationAfter then for _, v in ipairs(extCols_stationAfter) do					if station_index then str = str .. '<td class="station '..v..'">' .. (getArg(station_index .. '-row-' .. v) or '') .. ' '					else str = str .. '<td class="station '..v..'"> ' end end end end end

str = str .. str_z ..' ' return str, current_station, station_count, current_result, result_count, current_result_ext, result_ext_count end

local extRows = function(withStation, isTop) local prefix if isTop then prefix = 'topextrow-' else prefix = 'extrow-' end local returnstr = '' local valid = true local p	local str local _i = 1 local temp while valid do local i = tostring(_i) .. '-'		p = prefix .. i		valid = false str = '<tr data-'..prefix..'id="'..tostring(_i)..'">' if extCols_A then for _, v in ipairs(extCols_A) do				temp = getArg(p..v)				if temp then valid = true str = str .. '<td class="'..v..'">' .. temp .. ' '				else str = str .. '<td class="'..v..'"> ' end end end temp = getArg(p..'col-result') if temp then valid = true str = str .. ' ' .. temp .. ' '		else str = str .. ' '		end if extCols_B then for _, v in ipairs(extCols_B) do				temp = getArg(p..v)				if temp then valid = true str = str .. '<td class="'..v..'">' .. temp .. ' '				else str = str .. '<td class="'..v..'"> ' end end end temp = getArg(p..'col-ingredients') if temp then valid = true str = str .. ' ' .. temp .. ' '		else str = str .. ' '		end if extCols_C then for _, v in ipairs(extCols_C) do				temp = getArg(p..v)				if temp then valid = true str = str .. '<td class="'..v..'">' .. temp .. ' '				else str = str .. '<td class="'..v..'"> ' end end end if withStation then -- station before: if extCols_stationBefore then for _, v in ipairs(extCols_stationBefore) do					temp = getArg(p..v)					if temp then valid = true str = str .. '<td class="station '..v..'">' .. temp .. ' '					else str = str .. '<td class="station '..v..'"> ' end end end temp = getArg(p..'col-station') if temp then valid = true str = str .. ' ' .. temp .. ' '			else str = str .. ' '			end -- station after: if extCols_stationAfter then for _, v in ipairs(extCols_stationAfter) do					temp = getArg(p..v)					if temp then valid = true str = str .. '<td class="station '..v..'">' .. temp .. ' '					else str = str .. '<td class="station '..v..'"> ' end end end end if extCols_D then for _, v in ipairs(extCols_D) do				temp = getArg(p..v)				if temp then valid = true str = str .. '<td class="'..v..'">' .. temp .. ' '				else str = str .. '<td class="'..v..'"> ' end end end str = str .. ' '

if valid then _i = _i + 1 returnstr = returnstr .. str end end return returnstr end

local tableBody = function(result, withStation, needGroup, needCate, needLink, rootpagename, title, expectedrows, template, stationGroup) local str = tableStart(title, withStation) -- top ext rows: str = str .. extRows(withStation, true) -- main rows: local current_station local station_count local rows_count = 0 local current_result local result_count local current_result_ext local result_ext_count for _, row in ipairs(result) do			rows_count = rows_count + 1 -- table row: str, current_station, station_count, current_result, result_count, current_result_ext, result_ext_count = tableRow(str, row, current_station, station_count, rows_count, withStation, needCate, needLink, needGroup, current_result, result_count, current_result_ext, result_ext_count, template, stationGroup) -- cate: if needCate then if needCate == 2 or rootpagename == currentFrame:expandTemplate{ title = 'tr', args = {row['result'], lang=lang} } then addCate(row['station']) end end end -- rowspan value for last station group and result group if withStation and station_count and stationGroup then str = str:gsub("xxxrowspanxxx", tostring(station_count)) end if needGroup then str = str:gsub("yyyrowspanyyy", tostring(result_count)) str = str:gsub("zzzrowspanzzz", tostring(result_ext_count)) end -- ext rows: str = str .. extRows(withStation) -- table end str = str .. tableEnd(rows_count, expectedrows)

-- cate if needCate then str = str .. cateStr end

return str end

local infoboxesBody = function(result, needCate, needLink, rootpagename, title, expectedrows) -- main rows: local current_station local station_count local rows_count = 0 local current_result local result_count local str = currentFrame:expandTemplate{ title = 'flexstart' } for _, row in ipairs(result) do			rows_count = rows_count + 1 -- table row: str = str .. ' '			-- cate: if needCate then if needCate == 2 or rootpagename == currentFrame:expandTemplate{ title = 'tr', args = {row['result'], lang=lang} } then addCate(row['station']) end end end

str = str .. currentFrame:expandTemplate{ title = 'flexend' }

-- cate if expectedrows and rows_count ~= expectedrows then str = str .. ''		end if not expectedrows and rows_count == 0 then return ' Recipes: No result ' end str = str .. cateStr

return str end -

local p = {}

-- for p.register = function(frame) local args = frame:getParent.args local historical

if (trim(args['historical'] or 'n')):sub(1,1) == 'y' then historical = true end

-- 	local ingredients = {} -- list of {index, itemname, amount} for k, v in pairs(args) do		if(type(k) == 'number') then if k % 2 == 1 then -- 2n-1, nth item local index, item, amount = (k+1)/2, trim(v), trim(args[k+1]) ingredients[index] = {item, amount} end end end

local serialized = '' -- serialized ingredients list for _, v in ipairs(ingredients) do serialized = serialized .. '^' .. v[1] .. '‡' .. v[2] end serialized = mw.ustring.sub(serialized, 2)

table.sort(ingredients, function(a, b) return a[1] < b[1] end) -- sort by ingredient item name local ingredients_string = '' local ingredients_string_full = '' for _, v in ipairs(ingredients) do		local name, amount = unpack(v) local ingstr = normalize(name) ingredients_string = ingredients_string .. '^' .. ingstr ingredients_string_full = ingredients_string_full .. '^' .. ingstr .. amount end

--store frame:callParserFunction('#cargo_store:_table=Recipes',{		result = trim(args['result'] or ),		resultimage = trim(args['image'] or ),		resulttext = trim(args['text'] or ),		amount = trim(args['amount'] or ),		historical = historical,		station = normalizeStation(trim(args['station'] or '')),		ingredients = mw.ustring.sub(ingredients_string, 2),		ings = mw.ustring.sub(ingredients_string_full, 2),		args = serialized,	}) end -- p.register

-- for p.query = function(frame) currentFrame = frame -- global frame cache local args = frame:getParent.args inputArgs = args

lang = frame.args['lang'] or 'en' l10n_table = l10n_info[lang] or l10n_info['en']

resultanchor = trim(args['resultanchor'] or '')

addCate, cateStr = (function		local cate = l10n('station_cate')		local cateCache = {}		local addCate = function(station)			cateCache[station] = true		end		local cateStr = function			local str = 			for station, _ in pairs(cateCache) do				if station == 'By Hand' then					str = str .. 				else					str = str .. 				end			end			if str ~=  then				str = str .. ''			end			return str		end		return addCate, cateStr	end)

local where = trim(args['where'] or '') if where == '' then where = criStr(args) end

-- no constraint no result. if where == '' then return ' Recipes: No constraint ' end

-- format: local needCate, needLink = getFlags(args) local needGroup = true if (getArg('grouping') or 'y'):sub(1,1) == 'n' then needGroup = false end local _title = trim(args['title'] or '') local _expectedrows = trim(args['expectedrows'] or '') if _expectedrows ~= '' then _expectedrows = tonumber(_expectedrows) else _expectedrows = nil end local rootpagename = mw.title.getCurrentTitle.rootText

if getArg('view') == 'infobox' then local result = mw.ext.cargo.query('Recipes', 'result, resultimage, resulttext, amount, historical, station, args', {			where = where,			groupBy = "result, amount, ings",			orderBy = "result, amount DESC", -- Don't order by station			limit = 2000,		}) return infoboxesBody(result, needCate, needLink, rootpagename, _title, _expectedrows) else if trim(args['nostation'] or ) ~=  then -- no station -- query, still need contain station field for cate. local result = mw.ext.cargo.query('Recipes', 'result, resultimage, resulttext, amount, historical, station, args', {				where = where,				groupBy = "result, amount, ings",				orderBy = "result, amount DESC", -- Don't order by station				limit = 2000,			}) return tableBody(result, false, needGroup, needCate, needLink, rootpagename, _title, _expectedrows, getArg('resulttemplate'), false) else -- with station local stationGroup = true if (getArg('stationgrouping') or 'y'):sub(1,1) == 'n' then stationGroup = false end -- query local result = mw.ext.cargo.query('Recipes', 'result, resultimage, resulttext, amount, historical, station, args', {				where = where,				groupBy = "result, amount, ings",				orderBy = "station, result, amount DESC, ings", -- order by station first for station grouping.				limit = 2000,			}) return tableBody(result, true, needGroup, needCate, needLink, rootpagename, _title, _expectedrows, getArg('resulttemplate'), stationGroup) end end end -- p.query

-- for p.extract = function(frame) currentFrame = frame -- global frame cache

local args = frame:getParent.args inputArgs = args

lang = frame.args['lang'] or 'en' l10n_table = l10n_info[lang] or l10n_info['en']

local where = trim(args['where'] or '') if where == '' then where = criStr(args) end

-- no constraint no result. if where == '' then return ' Recipes/extract: No constraint ' end

-- query: local result = mw.ext.cargo.query('Recipes', 'result, resultimage, resulttext, amount, historical, station, args', {		where = where,		groupBy = "result, amount, ings",		orderBy = "result, amount DESC", -- Don't order by station		limit = 20, -- enough.	})

-- output local mode = getArg('mode') local sep = getArg('sep') or getArg('seperator') if not mode or mode =='compact' or mode == '' then --default mode = compact local sep = sep or l10n('default_sep_compact') local withResult = getArg('withresult') local withStation = not getArg('nostation') local str = nil for _, row in ipairs(result) do			if str then str = str .. sep else str = '' end str = str .. ' '			local ingFlag = nil for _, v in ipairs(explode('^', row['args'])) do				if ingFlag then str = str .. ' + '				else ingFlag = true end local item, amount = v:match('^(.-)‡(.-)$') if amount ~= '1' then str = str .. amount .. ' '				end local s				for _, itemname in ipairs(split(item)) do					if s then s = s .. "&thinsp;/&thinsp;" .. itemLink(itemname, {mode='image'}) else s = itemLink(itemname, {mode='image'}) end end str = str .. s			end if withResult then str = str .. ' = '				if row['amount'] ~= '1' then str = str .. row['amount'] .. ' '				end local args = {mode='image'} if row['resultimage'] then args['image'] = row['resultimage'] end str = str .. itemLink(row['result'], args) end if withStation then str = str .. compactStation(row['station']) end str = str..' ' end return str elseif mode == 'ingredients' then local sep = sep or l10n('default_sep_ingredients') local str = nil for _, row in ipairs(result) do			if str then str = str .. sep else str = '' end str = str .. ingredientsCell(row['args']) end return ' '..str..' ' elseif mode == 'station' then -- only return first row. for _, row in ipairs(result) do			return stationCell(row['station'], {}) end elseif mode == 'stationraw' then -- only return first row. for _, row in ipairs(result) do			return row['station'] end elseif mode == 'result' then -- only return first row. local needCate, needLink = getFlags(args) for _, row in ipairs(result) do			return resultCell(row, needLink, getArg('resulttemplate')) end elseif mode == 'amount' then -- only return first row. for _, row in ipairs(result) do			return row['amount'] end elseif mode == 'ingredients-buy' then -- doesn't work now, left for possible future expansion -- only process first row. for _, row in ipairs(result) do			local value = 0 for _, v in ipairs(explode('^', row['args'])) do				local item, amount = v:match('^(.-)‡(.-)$') value = value + require('Module:Iteminfo').getItemStat( tonumber(currentFrame:expandTemplate{ title = 'itemIdFromName', args = {item, lang='en'} }) or 0, 'value' ) * amount end return value end elseif mode == 'ingredients-sell' then -- doesn't work now, left for possible future expansion -- only process first row. for _, row in ipairs(result) do			local value = 0 for _, v in ipairs(explode('^', row['args'])) do				local item, amount = v:match('^(.-)‡(.-)$') value = value + math.floor(require('Module:Iteminfo').getItemStat( tonumber(currentFrame:expandTemplate{ title = 'itemIdFromName', args = {item, lang='en'} }) or 0, 'value' )/5) * amount end return value end else return ' Recipes/extract: Invalid mode ' end end -- p.extract

-- count p.count = function(frame) local args = frame:getParent.args local where = trim(args['where'] or '') if where == '' then where = criStr(args) end -- no constraint no result. if where == '' then return end -- query: since we must use group by to eliminate duplicates, so we can not use COUNT to get row count directly. local result = mw.ext.cargo.query('Recipes', 'result, resultimage, resulttext, amount, station, args', {		where = where,		groupBy = "result, amount, ings",		limit = 2000,	}) -- count local count = 0 for _, row in ipairs(result) do		count = count + 1 end return count end -- p.count

-- return "yes" or "" p.exist = function(frame) local args = frame:getParent.args local where = trim(args['where'] or '') if where == '' then where = criStr(args) end -- no constraint no result. if where == '' then return end -- query: local result = mw.ext.cargo.query('Recipes', 'result', {		where = where,		limit = 1, -- enough.	}) -- output for _, row in ipairs(result) do		return 'yes' end end -- p.exist

return p