Module:Infobox Item

-- -- insert wowee here

local p = {}

-- "imports" local infobox = require('Module:Infobox') local onmain = require('Module:Mainonly').on_main local paramtest = require('Module:Paramtest') local commas = require('Module:Addcommas')._add local exchange = require('Module:Exchange')

-- Main function called with invokes function p.main(frame) local args = frame:getParent.args local ret = infobox.new(args)

-- Parameter definitions ret:defineParams{ { name = 'name', func = 'name' }, { name = 'aka', func = 'has_content' }, { name = 'image', func = 'image' },

-- release and removal -- removal only shown if it exists { name = 'release', func = 'release' }, { name = 'removal', func = 'removal' }, { name = 'removaldisp', func = { name = removaldisp, params = { 'removal' } }, dupes = true },

{ name = 'members', func = 'has_content' }, { name = 'examine', func = 'has_content' }, { name = 'quest', func = 'has_content' }, { name = 'tradeable', func = tradeablearg }, { name = 'equipable', func = 'has_content' },

-- bankable; only show if "No"; default to "Yes" { name = 'bankable', func = { name = 'has_content', params = { 'bankable', 'Yes'}, flag = { 'd', 'r' } } }, { name = 'bankabledisp', func = { name = yesnodisp, params = { 'bankable', 'no' }, flag = { 'd', 'r' } }, dupes = true },

-- stacksinbank; only show if "No"; default to "Yes" { name = 'stacksinbank', func = { name = 'has_content', params = { 'stacksinbank', 'Yes'}, flag = { 'd', 'r' } } }, { name = 'stacksinbankdisp', func = { name = yesnodisp, params = { 'stacksinbank', 'no' }, flag = { 'd', 'r' } }, dupes = true },

{ name = 'stackable', func = 'has_content' },

-- edible; only show if "Yes"; default to "No" { name = 'edible', func = { name = 'has_content', params = { 'edible', 'No'}, flag = { 'd', 'r' } } }, { name = 'edibledisp', func = { name = yesnodisp, params = { 'edible' } }, dupes = true },

-- noteable; only show if "Yes"; default to "No" { name = 'noteable', func = { name = 'has_content', params = { 'noteable', 'No'}, flag = { 'd', 'r' } } }, { name = 'noteabledisp', func = { name = yesnodisp, params = { 'noteable' } }, dupes = true }, { name = 'destroy', func = 'has_content' }, { name = 'store', func = { name = storearg, params = { 'store', 'currency', 'seller' }, flag = 'p' } }, { name = 'val', func = { name = valraw, params = { 'value' }, flag = 'p' }, dupes = true }, { name = 'value', func = { name = valuearg, params = { 'val' }, flag = 'd' } }, -- gemw prices -- only displayed if they exist --dupes must exist for individual prices to have them display properly { name = 'gemw', func = { name = gemwarg, params = { 'exchange', 'tradeable' }, flag = {'p', 'd'} } }, { name = 'gemwname', func = { name = gemwnamearg, params = { 'name', 'gemwname' } } }, { name = 'gemwprice', func = { name = gemwpricearg, params = { 'gemw', 'gemwname' } }, dupes = true }, { name = 'exchange', func = { name = exchangearg, params = { 'gemwprice', 'gemwname' } }, dupes = true }, { name = 'graph', func = { name = gemwgrapharg, params = { 'gemwprice', 'gemwname' } }, dupes = true }, { name = 'buylimit', func = { name = buylimitarg, params = { 'gemwprice', 'gemwname' } }, dupes = true }, -- used for both exchange and graphs { name = 'gemwdisp', func = { name = gemwdisp, params = { 'gemwprice' } }, dupes = true },

-- alchemy { name = 'alchable', func = { name = alchablearg, params = { 'alchable', 'high', 'low' }, flag = 'p' }, dupes = true }, { name = 'alchmultiplier', func = multiplierarg }, { name = 'high', func = { name = alchvalues, params = { 'val', 'high', 'alchmultiplier', 1, 'alchable' }, flag = { 'd', 'p', 'd', 'r', 'd' } } }, { name = 'low', func = { name = alchvalues, params = { 'val', 'low', 'alchmultiplier', 2/3, 'alchable' }, flag = { 'd', 'p', 'd', 'r', 'd' } } },

{ name = 'weight', func = weightarg }, { name = 'weightraw', func = { name = weightargraw, params = { 'weight' }, flag = 'p' } },

-- not used; only for categories { name = 'id', func = 'numbers' }, { name = 'rscid', func = 'numbers' } }

ret:setMaxButtons(7) ret:create ret:cleanParams

-- parameter linkings for hidden rows ret:linkParams{ { 'removal', 'removaldisp' }, { 'exchange', 'gemwdisp' }, { 'graph', 'gemwdisp' }, { 'buylimit', 'gemwdisp' }, { 'bankable', 'bankabledisp' }, { 'stacksinbank', 'stacksinbankdisp' }, { 'noteable', 'noteabledisp' }, { 'edible', 'edibledisp' } }

ret:defineLinks({ { 'Template:%s/FAQ', 'FAQ' },			{ 'Template:%s/doc', 'doc' } })

ret:useSMW({		id = 'Item ID',		val = 'Value',		weightraw = 'Weight',	})

-- ret:caption ret:defineName('Infobox Item') ret:addClass('infobox-item') -- PARAMETER: name ret:addRow{ { tag = 'argh', content = 'name', class='infobox-header', colspan = '2' } } -- PARAMETER: image ret:addRow{ { tag = 'argd', content = 'image', class = 'infobox-image inventory-image', colspan = '2' } }

-- PARAMETER: release -- (update included automatically by infobox) :addRow{ { tag = 'th', content = 'Released' }, { tag = 'argd', content = 'release' } } -- PARAMETER: removal if ret:paramDefined('removal', 'all') then ret:addRow{ { tag = 'th', content = 'Removal' }, { tag = 'argd', content = 'removal' } } end

-- PARAMETER: aka -- add only if it exists if ret:paramDefined('aka') then ret:addRow{ { tag = 'th', content = 'AKA' }, { tag = 'argd', content = 'aka' } } end

-- PARAMETER: members ret:addRow{ { tag = 'th', content = 'Members' }, { tag = 'argd', content = 'members' } }

-- PARAMETER: quest :addRow{ { tag = 'th', content = 'Quest item' }, { tag = 'argd', content = 'quest' } } -- HEADER: Properties :addRow{ { tag = 'th', content = 'Properties', class = 'infobox-subheader', colspan = '2' } }

-- PARAMETER: tradeable :addRow{ { tag = 'th', content = 'Tradeable' }, { tag = 'argd', content = 'tradeable' } }

-- PARAMETER: bankable if ret:paramGrep('bankable','no') then ret:addRow{ { tag = 'th', content = 'Bankable' }, { tag = 'argd', content = 'bankable' } } end

-- PARAMETER: stacksinbank if ret:paramGrep('stacksinbank','no') then ret:addRow{ { tag = 'th', content = 'Stacks in bank' }, { tag = 'argd', content = 'stacksinbank' } } end

-- PARAMETER: equipable ret:addRow{ { tag = 'th', content = 'Equipable' }, { tag = 'argd', content = 'equipable' } }

-- PARAMETER: stackable :addRow{ { tag = 'th', content = 'Stackable' }, { tag = 'argd', content = 'stackable' } }

-- PARAMETER: noteable if ret:paramGrep('noteable','yes') then ret:addRow{ { tag = 'th', content = 'Noteable' }, { tag = 'argd', content = 'noteable' } } end

-- PARAMETER: edible if ret:paramGrep('edible','yes') then ret:addRow{ { tag = 'th', content = 'Edible' }, { tag = 'argd', content = 'edible' } } end -- PARAMETER: destroy ret:addRow{ { tag = 'th', content = 'Destroy' }, { tag = 'argd', content = 'destroy', css = { ['max-width'] = '200px' } } } -- HEADER: Values ret:addRow{ { tag = 'th', content = 'Values', class = 'infobox-subheader', colspan = '2' } }

-- PARAMETER: value -- for now don't show value at all, we may want to show for unalchable items -- if ret:paramDefined('value') then -- 	ret:addRow{ -- 		{ tag = 'th', content = 'Value' }, -- 		{ tag = 'argd', content = 'value' } } -- end

-- PARAMETER: alchable | high | low -- find if any version is alchable local anyalchable = ret:paramGrep('alchable',true)

-- if any are alchable, add both rows if anyalchable == true then ret:addRow{ { tag = 'th', content = 'High alch' }, { tag = 'argd', content = 'high' } } :addRow{ { tag = 'th', content = 'Low alch' }, { tag = 'argd', content = 'low' } } else -- otherwise add a single "no alch" row ret:addRow{ { tag = 'th', content = 'Alchemy' }, { tag = 'td', content = 'Not alchemisable' } } end -- store price ret:addRow{ { tag = 'th', content = 'Store price' }, { tag = 'argd', content = 'store' } }

-- find if we have any exchange prices local anygemw = ret:paramGrep('gemwprice', function(_arg) return _arg > 0 end)

-- if we have any on the ge, add the gemw row if anygemw == true then -- PARAMETER: exchange | gemwname ret:addRow{ { tag = 'th', content = 'Exchange' }, { tag = 'argd', content = 'exchange' } } :addRow{ { tag = 'th', content = 'Buy limit' }, { tag = 'argd', content = 'buylimit' } } end

-- PARAMETER: weight ret:addRow{ { tag = 'th', content = 'Weight' }, { tag = 'argd', content = 'weight' } } -- PARAMETER: examine :addRow{ { tag = 'th', content = 'Examine', class = 'infobox-subheader', colspan = '2' } } :addRow{ { tag = 'argd', content = 'examine', colspan = '2', css = { ['max-width'] = '300px', ['text-align'] = 'center' } } }

-- PARAMETER: graph if anygemw == true then ret:addRow{ { tag = 'th', content = 'Exchange history', class = 'infobox-subheader', colspan = '2' } } :addRow{ { tag = 'argd', content = 'graph', colspan = '2', css = { ['text-align'] = 'center', ['padding'] = '0 0 0.6em 0'} } } end

if onmain then local a1 = ret:param('all') local a2 = ret:categoryData ret:wikitext(addcategories(a1,a2)) end

return ret:tostring end

-- Store price function storearg(store, currency, seller) -- remove any commas store = string.gsub(store or ,',',)

-- no for not sold if string.lower(store) == 'no' then return 'Not sold' else store = tonumber(store,10) end

if type(store) == 'number' then -- default to coins as the currency currency = paramtest.default_to(currency,'coins')

store = plural(currency, store, currency)

-- seller not shown by default seller = paramtest.default_to(seller,false)

if seller then seller = seller:gsub('[%[%]]','') return string.format('%s (%s)',store,seller) else return store end else return nil end end

-- tradeable -- tradeablearg(value) function tradeablearg(v) v = string.lower(v or '') if v == 'yes' or v == 'no' then v = mw.text.split(v,'') v[1] = string.upper(v[1]) return table.concat(v,'') else return nil end end

-- value -- separate number storage for operation -- valraw(value) function valraw(v) local _ v = v or '' _v = string.gsub(v,',','')

return tonumber(v,10) end

-- value -- valuearg(value) -- actual value already parsed function valuearg(v)

v = tonumber(v,10) return plural('coin',v)

end -- Alchables -- alchablearg(alchable, high, low) function alchablearg(a,h,l) -- not alchable if both are false, or if "alchable" is false if string.lower(a or '') == 'no' then return false elseif string.lower(h or ) == 'no' and string.lower(l or ) == 'no' then return false else return true end end

-- alch multiplier arg -- only accepts numbers -- defaults to .6 function multiplierarg(v) return tonumber(v) or .6 end

-- high/low alch -- alchvalues(value, override value, multiplier, alchable) -- actual value already parsed -- value, template param, alch multiplier, override multiplier, not alchable bool function alchvalues(v,_p,a,m,n) -- remove commas and turn into a number v = tonumber(v,10) if paramtest.has_content(_p) then _p = string.gsub(_p,',','') _p = tonumber(_p,10) else _p = nil end

-- return override always if type(_p) == 'number' then return plural('coin',_p) end

-- if you can't alch it, return this -- used in the case of 1 version being alchable and the other not if n == false then return 'Not alchemisable' end -- otherwise try the value and multiply it	if v then local r = math.floor(killRoundingError(v * m * a)) return plural('coin',r) end

return nil end

-- weight -- weightarg(weight) function weightarg(w) if paramtest.has_content(w) then -- replace all "kg" and spaces here w = string.gsub(w or ,'[kg ]',)

-- replace hyphen with minus sign w = string.gsub(w,'-','&minus;')

-- use non-breaking spaces and html entities for display -- still necessary to convert the "kg" to html? return string.format('%s &#107;&#103;',w) end return nil end

-- weightargraw(weight) function weightargraw(w) if paramtest.has_content(w) then -- replace all "kg" and spaces here w = string.gsub(w or ,'[kg ]',) return tonumber(w) end return nil end

-- on ge or not -- only accepts "gemw" -- gemwarg(exchange,tradeable) function gemwarg(arg,arg2) g = string.lower(arg or '') return arg2 == 'Yes' and g == 'gemw' end

-- gemw names -- gemwnamearg(name, override name) function gemwnamearg(n,a) -- return override if a and a:find('%S') then return string.gsub(a,'','') -- otherwise use the "name" parameter elseif n and n:find('%S') then return string.gsub(n,'','') -- default to page name else return mw.title.getCurrentTitle.fullText end end

-- separate thing to hold all the prices as raw numbers -- gemwpricearg(gemw, name) function gemwpricearg(g,n) if g == true then -- return price if page is found -- -1 for errors if exchange._exists(n) then return tonumber(exchange._price(n),10) or -1 else return -1 end -- 0 for no price else return 0 end end

-- exchange display -- exchangearg(gemw price, name) -- uses the already fetched ge price to operate -- values less than 1 are used for parsing instructions function exchangearg(g,n) if type(g) ~= 'number' then g = tonumber(g,10) or 0 end -- 0 for not sold if g == 0 then return ' Not sold ' -- -1 for no page found elseif g == -1 then return badarg('exchange','was set to «gemw» but no page was found for «'..n..'».') -- all other numbers elseif g > 0 then -- plural done in format because we need a span around the value return string.format(' %s coin%s (info) ',g,commas(g),g>1 and 's' or '',n) -- not a number = nil -- shouldn't be used, but it's a fallback else return 0 end end

-- ge graphs -- gemwgrapharg(gemw price, name) -- uses the already fetched ge price to operate -- values less than 1 are used for parsing instructions function gemwgrapharg(g,n) if type(g) ~= 'number' then g = tonumber(g,10) or 0 end

-- 0 for not sold an	-- -1 for error -- call to hide the graph if g == 0 or g == -1 then return 'No data to display' -- all other numbers elseif g > 0 then -- TODO: Change Module:ExchangeData and give it a proper graph function -- remove reliance on frame return mw.getCurrentFrame:preprocess(string.format('',n)) -- not a number = nil -- shouldn't be used, but it's a fallback else return nil end end

-- ge buy limits -- buylimitarg(gemw price, name) -- uses the already fetched ge price to operate -- values less than 1 are used for parsing instructions function buylimitarg(g,n) if type(g) ~= 'number' then g = tonumber(g,10) or 0 end

-- 0 for not sold an	-- -1 for error -- call to hide the graph if g == 0 or g == -1 then return '-' -- all other numbers elseif g > 0 then local ret = exchange._limit(n) if ret == nil then return '-' else return commas(ret) end -- not a number = nil -- shouldn't be used, but it's a fallback else return nil end end

-- class names based on value -- gemwdisp(price) function gemwdisp(_p) if _p == 0 then return '' else return 'infobox-cell-shown' end end

-- Shows if the param matches the alt -- alt defaults to yes -- Everything else = hide function yesnodisp(arg, alt) arg = string.lower(arg or '') alt = string.lower(alt or 'yes') if arg == alt then return 'infobox-cell-shown' else return '' end end

-- Shows if has a date function removaldisp(arg) if string.find(arg or '','%[%[') then return 'infobox-cell-shown' else return '' end end

-- red ERR span with title hover for explanation function badarg(argname, argmessage) return ''.. 'ERR ' end

-- plural -- returns number with the word function plural(arg,n,alt) local _n = commas(tonumber(n,10) or 1) if tonumber(n,10) == 1 then return string.format('%s %s',_n,arg) elseif alt then return string.format('%s %s',_n,alt) else return string.format('%s %ss',_n,arg) end end

-- Does exactly what's on the tin function killRoundingError(n) return math.floor(n*1000+0.0000099)/1000 end

-- Categories -- oman this is still blatant copy pasta function addcategories(args,catargs) local ret = { 'Items' } local cat_map = { -- Added if the parameter has content defined = { aka = 'Pages with AKA' },		-- Added if the parameter has no content notdefined = { image = 'Needs image', members = 'Needs members status', release = 'Needs release date', examine = 'Needs examine added', level = 'Needs combat level', weight = 'Needs weight added', value = 'Items missing value', quest = 'Items missing quest', destroy = 'Missing destroy text', },		-- Parameters that have text -- map a category to a value grep = { members = { yes = 'Members\' items', no = 'Free-to-play items' }, stackable = { yes = 'Stackable items' }, equipable = { yes = 'Equipable items' }, gemw = { ['true'] = 'Grand Exchange items' }, tradeable = { yes = 'Tradeable items', no = 'Untradeable items' }, }	}

-- Run and add mapped categories

-- defined categories for n, v in pairs(cat_map.defined) do		if catargs[n] and catargs[n].one_defined then table.insert(ret,v) end end

-- undefined categories for n, v in pairs(cat_map.notdefined) do		if catargs[n] and catargs[n].all_defined == false then table.insert(ret,v) end end

-- searches for n, v in pairs(cat_map.grep) do		for m, w in pairs(v) do			if args[n] then if string.find(string.lower(tostring(args[n].d) or ''),m) then table.insert(ret,w) end if args[n].switches then for _, x in ipairs(args[n].switches) do						if string.find(string.lower(tostring(x)),m) then table.insert(ret,w) end end end end end end

-- quest items -- just look for a link if args.quest.d:find('%[%[') then table.insert(ret,'Quest items') elseif args.quest.switches then for _, v in ipairs(args.quest.switches) do			if v:find('%[%[') then table.insert(ret,'Quest items') break end end end

-- ids if not catargs.id.all_defined then -- rsc ids have no id		if catargs.rscid.all_defined then -- do nothing else table.insert(ret,'Needs ID') end end

-- alchemy -- non alchable if args.alchable.d == false then table.insert(ret,'Items that cannot be alchemised') elseif args.alchable.switches then for _, v in ipairs(args.alchable.switches) do			if v == false then table.insert(ret,'Items that cannot be alchemised') break end end end

-- gemw -- if item is both (not untradeable) and (not GEMW) then add Non-GE items if not args.gemw.d and string.lower(args.tradeable.d) ~= 'no' then table.insert(ret, 'Non-GE items') end -- switches; if tradeable switches exist, if gemwX and tradeableX are as above, add Non-GE items --						if no switches, gemwX and tradeable (default) if args.gemw.switches then if args.tradeable.switches then for i, v in ipairs(args.gemw.switches) do				if not v and string.lower(args.tradeable.switches[i] or args.tradeable.d) ~= 'no' then table.insert(ret, 'Non-GE items') end end else for i, v in ipairs(args.gemw.switches) do				if not v and string.lower(args.tradeable.d) ~= 'no' then table.insert(ret, 'Non-GE items') end end end end

-- combine table and format category wikicode for i, v in ipairs(ret) do		ret[i] = string.format('',v) end

return table.concat(ret,'') end

return p