Module:Exchange

--[[

--]] -- -- -- Implements various exchange templates -- See Individual method docs for more details -- -- See also Module:ExchangeData -- -- @todo document p._table and p.view better --

local p = {}

-- only load commonly used modules here local yesno = require( 'Module:Yesno' ) local addcommas = require( 'Module:Addcommas' )._add

-- map redirects to their correct pages local geRedirects = { ['1/2 anchovy pizza'] = '½ anchovy pizza', ['1/2 meat pizza'] = '½ meat pizza', ["1/2 p'apple pizza"] = "½ p'apple pizza", ['1/2 plain pizza'] = '½ plain pizza', ['1/3 evil turnip'] = '⅓ evil turnip', ['2/3 cake'] = '⅔ cake', ['2/3 chocolate cake'] = '⅔ chocolate cake', ['2/3 evil turnip'] = '⅔ evil turnip', ['Dragon platelegs/skirt ornament kit (or)'] = 'Dragon platelegs & skirt ornament kit (or)', ['Dragon platelegs/skirt ornament kit (sp)'] = 'Dragon platelegs & skirt ornament kit (sp)' }

-- -- Makes sure first letter of item is uppercase -- Automatically handles any redirects -- local function checkTitle( item ) -- upper case first letter to make sure we can find a valid item page item = mw.text.split( item, '' ) item[1] = mw.ustring.upper( item[1] ) item = table.concat( item )

-- automatically handle redirects if geRedirects[item] ~= nil then item = geRedirects[item] end

return item end -- -- Simple mw.loadData wrapper used to access data located on module subpages -- -- @param item {string} Item to retrieve data for -- @return {table} Table of item data -- local function load( item ) item = checkTitle( item ) return mw.loadData( 'Module:Exchange/' .. item ) end

-- -- Returns the price of an item -- -- @param item {string} Item to get current price of -- @param multi {number} (optional) Multiplies the output price by the specified number -- @param format {boolean} (optional) Format the result with commas (defaults to false) -- @return {number|string} Price of item. Will return a string if formatted, else a number. -- function p._price( item, multi, format ) local price = load( item ).price local multi = type( multi ) == 'number' and multi or 1 local format = type( format ) == 'boolean' and format or false local ret = price * multi

if format then return addcommas( ret ) end

return ret end

-- -- Returns the limit of an item -- -- @param item {string} Item to get the limit of -- @return {number} Limit of item -- function p._limit( item ) return load( item ).limit end

-- -- Returns the value of an item -- -- @param item {string} Item to get the value or -- @return {number} Value of item -- function p._value( item ) return load( item ).value end

-- -- Gets an items icon with a link to the item page -- -- @deprecated -- -- @param {string} Item to get icon for -- @param {string} Icon file with a link to the item's page --                If icon file is not found, defaults to -- @return {string} -- function p._icon( item ) local data = load( item ) local icon = data.icon or 'Coins 1000.png'

return '' end

-- -- Calculates the difference between the current price and the last price of an item -- -- @param item {string} Item to calculate price difference for -- @param format {boolean} `true` if the output is to be formatted with commas --                        Defaults to `false` -- @return {number|string} The price difference as a number --                        If `format` is set to `true` then this returns a string --                        If either of the prices to calculate the diff from are unavailable, this returns `0` (number) -- function p._diff( item, format ) local data = load( item ) local diff = 0

if data.price and data.last then diff = data.price - data.last

if format then diff = addcommas( diff ) end end

return diff end

-- -- internal method -- -- @todo check if this needs to be in a separate method --      can it be merged into p.table instead? -- -- @param item {string} Item to get data for -- @return {string} -- function p._table( item )

-- load data and any required modules local data = load( item ) local timeago = require( 'Module:TimeAgo' )._ago local changeperday = require( 'Module:ChangePerDay' )._change

-- set variables here to make the row building easier to follow local div = '\'\'Unknown\'\'' local limit = data.limit and addcommas( data.limit ) or '\'\'Unknown\'\'' local members

if data.last then local link = 'http://services.runescape.com/m=itemdb_rs/viewitem.ws?obj=' .. data.itemId local change = math.abs( changeperday( {data.price, data.last, data.date, data.lastDate} ) )

if data.price > data.last then arrow = '' elseif data.price < data.last then arrow = '' else arrow = '' end

if change >= 0.04 then arrow = arrow .. arrow .. arrow elseif change >= 0.02 then arrow = arrow .. arrow end

div = mw.html.create( 'div' ) :css( 'white-space', 'nowrap' ) :wikitext( arrow )

div = tostring( div ) end

if data.members == nil then members = '\'\'Unknown\'\'' elseif yesno( data.members ) then members = '' else members = '' end

-- build table row local tr = mw.html.create( 'tr' ) :tag( 'td' ) :wikitext( p._icon( item ) ) :done :tag( 'td' ) :css( {               ['width'] = '15%',                ['text-align'] = 'left'            } ) :wikitext(  .. item ..  ) :done :tag( 'td' ) :wikitext( addcommas( data.price ) ) :done :tag( 'td' ) :wikitext( div ) :done

if data.alchable == nil or yesno( data.alchable ) then local low, high = '\'\'Unknown\'\, '\'\'Unknown\'\

if data.value then low = addcommas( math.floor( data.value * 0.4 ) ) high = addcommas( math.floor( data.value * 0.6 ) ) end

tr           :tag( 'td' ) :wikitext( low ) :done :tag( 'td' ) :wikitext( high ) :done else tr           :tag( 'td' ) :attr( 'colspan', '2' ) :wikitext( '\'\'Cannot be alchemised\'\'' ) :done end

tr       :tag( 'td' ) :wikitext( limit ) :done :tag( 'td' ) :wikitext( members ) :done :tag( 'td' ) :css( 'white-space', 'nowrap' ) -- todo remove graph link when data is moved to modules :wikitext( 'view &bull; graph' ) :done :tag( 'td' ) :css( 'font-size', '85%' ) :wikitext( timeago( {data.date} ) ) :done

return tostring( tr )

end

-- -- Internal method for p.highAlchTable and p.lowAlchTable -- function p._alchTable( item, data, alch ) local timeago = require( 'Module:TimeAgo' )._ago local round = require( 'Module:Number' )._round local natPrice = load( 'Nature rune' ).price

local profit = alch - data.price - natPrice

local image = '' local itemStr =  .. item ..  local priceStr = addcommas( data.price ) local alchStr = addcommas( alch ) local profitStr = addcommas( profit ) local roi = tostring( round( ( profit / ( price + natPrice ) * 100 ), 1 ) ) .. '%'   local limit = data.limit and addcommas( data.limit ) or Unknown local maxProfit = Unknown local details = 'view &bull; graph' local lastUpdated = ''

if data.limit then local min = ( data.limit > 4800 ) and data.limit or min local maxProfit = addcommas( min * profit ) end

local tr = mw.html.create( 'tr' ) :tag( 'td' ) :wikitext( image ) :done :tag( 'td' ) :css( {               width = '15%',                ['text-align'] = 'left'            } ) :wikitext( itemStr ) :done :tag( 'td' ) :wikitext( priceStr ) :done :tag( 'td' ) :wikitext( alchStr ) :done :tag( 'td' ) :wikitext( profitStr ) :done :tag( 'td' ) :wikitext( roi ) :done :tag( 'td' ) :wikitext( limit ) :done :tag( 'td' ) :wikitext( maxProfit ) :done :tag( 'td' ) :wikitext( members ) :done :tag( 'td' ) :css( 'white-space', 'nowrap' ) :wikitext( details ) :done :tag( 'td' ) :css( 'font-size', '85%' ) :wikitext( lastUpdated ) :done

return tostring( tr ) end

-- -- -- -- @example -- function p.highAlchTable( frame ) local args = frame:getParent.args local item = checkTitle( args[1] ) local data = load( item ) local alch = math.floor( data.value * 0.6 )

return p._alchTable( item, data, alch ) end

-- -- -- -- @example -- function p.lowAlchTable( frame ) local args = frame:getParent.args local item = checkTitle( args[1] ) local data = load( item ) local alch = math.floor( data.value * 0.4 )

return p._alchTable( item, data, alch ) end

-- -- -- -- -- @example -- @example -- @example -- function p.price( frame ) -- usage: or     local args = frame.args local pargs = frame:getParent.args local item = pargs[1]

if item then item = mw.text.trim( item ) else error( '"item" argument not specified', 0 ) end

-- default to formatted for backwards compatibility with old GE templates local format = true local multi = 1

-- format is set with #invoke -- so set it first to allow it to be overridden by template args if args.format ~= nil then format = yesno( args.format ) end

if tonumber( pargs[2] ) ~= nil then multi = tonumber( pargs[2] )

-- indicated someone is trying to pass an equation as a mulitplier -- known use cases are fractions, but pass it to #expr to make sure it's handled correctly elseif pargs[2] ~= nil and mw.ustring.find( pargs[2], '[/*+-]' ) then multi = tonumber( frame:preprocess( '' ) )

-- uses elseif to prevent something like -- causing a formatted output, as 1 casts to true when passed to yesno elseif type( yesno( pargs[2] ) ) == 'boolean' then format = yesno( pargs[2] )

if tonumber( pargs[3] ) ~= nil then multi = tonumber( pargs[3] ) end end

return p._price( item, multi, format ) end

-- -- -- -- @example -- function p.table( frame ) local args = frame:getParent.args local item = args[1]

if item then item = mw.text.trim( item ) else error( '"item" argument not specified', 0 ) end

return p._table( item ) end

-- -- Implements -- -- @deprecated -- -- @example -- @example (outputs after the image) -- function p.icon( frame ) local args = frame:getParent.args local item = args[1] local w = args[2]

if item then item = mw.text.trim( item ) else error( '"item" argument not specified', 0 ) end

local ret = p._icon( item )

if w == 'w' then ret = ret .. ' ' .. item end

return ' ' .. ret .. ' ' end

-- -- -- -- @example -- @example (deprecated) -- function p.view( frame ) local args = frame:getParent.args local item = args[1] or args['Item'] local view = args.view or args.View or '' local loadView = {limit=true, value=true, itemId=true}

if item then item = mw.text.trim( item ) else error( '"item" argument not specified', 0 ) end

view = mw.ustring.lower( view )

if view == 'itemid' then view = 'itemId' end

if view == 'diff' then return p._diff( item )

elseif loadView[view] then return load( item )[view]

--   -- values of view beyond this point are deprecated and will eventually be removed -- except the default output (for Exchange ns pages) --   elseif view == 'price' then -- p.price defaults to formatted -- so pass to p._price instead return p._price( item )

elseif view == 'icon' then return p._icon( item )

elseif view == 'table' then return p._table( item )

else local default = require( 'Module:ExchangeDefault' ) -- handle redirects and casing of item before passing it on       item = checkTitle( item ) return default.main( frame, item ) end end

return p