Module:Exchange

--[[

--]] -- -- -- Implements -- -- @todo document p._data, p._page, p._table and p.view better --

local p = {} local yesno = require( 'Module:Yesno' ) local addcommas = require( 'Module:Addcommas' )._add

-- -- Simple mw.loadData wrapper used to access data located on module subpages -- -- @todo Make sure first letter of `item` is upper case -- -- @param item {string} Item to retrieve data for -- @return {table} Table of item data -- local function load( 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, '' )

return mw.loadData( 'Module:Exchange/' .. item ) end

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

if format then return addcommas( ret ) end

return ret end

-- -- Returns the value of an item -- For use in other modules -- -- @param item {string} Item to get the static value -- @return {number} Value of item -- function p._value( item ) local data = load( item ) return data.value end

-- -- Gets an items icon with a link to the item page -- -- @param {string} Item to get icon for -- @param {string} Icon file with a link to the item's page --                If icon file is not listed, 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

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

if format then diff = addcommas( diff ) end

return diff end

return 0 end

-- -- For use in -- -- Essentially a replacement for -- @todo convert to html syntax when this goes live  --      136 uses as of 2014/06/22  -- -- @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

-- -- For access price data for use in exchange graphs -- -- @param item {string} -- @param size -- @param moreItems -- @return {string} -- function p._data( item, size, moreItems )

local title = mw.title.getCurrentTitle local _pdata = load( item .. '/Data' ) local pdata = {} local div1, div2, cats = , , ''

for k, v in pairs( _pdata ) do       pdata[k] = v    end

if title.subpageText == '/Data' and title.nsText == 'Exchange' then div1 = mw.html.create( 'div' ) :attr( 'id', 'contentSub' ) :css( {               margin = '0.75em 0 1.5em 1em',                ['line-height'] = '0.8em'            } ) :tag( 'span' ) :addClass( 'subpages' ) :wikitext( '&lt; ' .. title.nsText .. ':' .. title.baseText .. '' ) :done :done end

local dataPrices = mw.html.create( 'div' ) :addClass( 'GEdataprices' ) :attr( 'data-item', item ) :css( 'display', 'none' )

if mw.ustring.match( item, 'Index' ) then local idata = load( item ) dataPrices :attr( {               ['data-value'] = idata.value,                ['data-limit'] = idata.limit            } ) end

-- add price data as the last attr so it's easier to navigate in inspect element dataPrices :attr( 'data-data', table.concat( pdata, '|' ) )

if size ~= 'data' then local chartBox = mw.html.create( 'div' ) :addClass( 'GEChartBox' )

if moreItems then chartBox :tag( 'div' ) :addClass( 'addGEChartItems' ) :css( 'display', 'none' ) :wikitext( moreItems ) :done end

if not( title.nsText == 'Exchange' and title.isSubpage ) then

local dataChart = mw.html.create( 'div' ) :addClass( 'GEdatachart' ) :css( {                   border = '1px solid #666',                    ['padding-left'] = '1px'                } )

if size == 'small' then dataChart :addClass( 'smallChart' ) :css( {                       margin = '5px',                        float = 'left',                        width = '258px',                        height = '200px'                    } ) else dataChart :css( {                       margin = '5px 0 25px 0',                        height = '500px',                        ['min-width'] = '600px'                    } ) end

dataChart :tag( 'div' ) :css( {                       color = '#666',                        ['text-align'] = 'center',                        ['font-size'] = '11px',                    } ) :wikitext( 'Loading...' ) :done :done end

div2 = chartBox :node( dataPrices ) else div2 = dataPrices end

if title.subpageText == 'Data' then if title.nsText == 'Exchange' then cats = ''

if item ~= title.baseText then cats = cats .. ''           end elseif mw.ustring.match( item, 'Index' ) then cats = '' end end

return tostring( div1 ) .. tostring( div2 ) .. cats

end

-- -- For use on exchange ns pages -- -- @param item {string} -- @return {string} -- function p._page( item )

local lang = mw.language.getContentLanguage local title = mw.title.getCurrentTitle local frame = mw.getCurrentFrame local timeago = require( 'Module:TimeAgo' )._ago local round = require( 'Module:Number' )._round local data = load( item )

-- set variables here if possible to keep table building easier to follow local rowspan = 8 local volDate = data.volumeDate or lang:formatDate( 'c' ) local dateDiffZ = lang:formatDate( 'z' ) - lang:formatDate( 'z', volDate ) local dateDiffY = lang:formatDate( 'Y' ) - lang:formatDate( 'Y', volDate ) local price = addcommas( data.price ) or '\'\'N/A\'\'' local date = '\'\'N/A\'\'' local lowAlch = '\'\'Not alchable\'\'' local highAlch = '\'\'Not alchable\'\'' local memsIcon = '' local usage = {}

if data.volume and ( dateDiffZ + 365 * dateDiffY ) <= 7 then rowspan = 10 end

if data.date then date = lang:formatDate( 'j F Y, H:i "(UTC)"', data.date ) end

if data.alchable == nil or yesno( data.alchable ) then lowAlch = addcommas( math.floor( data.value * 0.4 ) ) highAlch = addcommas( math.floor( data.value * 0.6 ) ) end

if data.members ~= nil then if data.members then memsIcon = '' else memsIcon = '' end end

-- workaround for limitations of mw.loadData for k, v in pairs( data.usage ) do       usage[k] = v    end -- build table local tbl = mw.html.create( 'table' ) :addClass( 'wikitable' ) :css( {           ['border'] = '0',            ['border-collapse'] = 'separate',            ['margin-top'] = '-1em'        } ) :tag( 'tr' ) :tag( 'th' ) :attr( 'colspan', '2' ) :css( {                   border = '0',                    padding = '0'                } ) :tag( 'table' ) :css( {                       width = '100%',                        ['border-collapse'] = 'collapse'                    } ) :tag( 'tr' ) :tag( 'td' ) :css( {                               padding = '8px 16px',                                ['border-right'] = '0'                            } ) :wikitext( p._icon( item ) ) :done :tag( 'td' ) :css( {                               width = '100%',                                ['font-size'] = '135%',                                ['text-align'] = 'left',                                ['white-space'] = 'normal',                                padding = '8px 0',                                ['border-left'] = '0',                                ['border-right'] = '0'                            } ) -- this used to default to Grand Exchange Market Watch if item wasn't supplied -- but that only happened on 6 user pages, an orphanage and a preload :wikitext(  .. item ..  ) :tag( 'br' ) :done :tag( 'span' ) :css( {                                   ['font-weight'] = 'normal',                                    ['font-size'] = '75%'                                } ) :wikitext( data.examine or '' ) :done :done :tag( 'td' ) :css( {                               padding = '8px 16px',                                ['border-left'] = '0'                            } ) :wikitext( memsIcon ) :done :done :done :done :done :tag( 'tr' ) :tag( 'th' ) :css( {                   ['text-align'] = 'left',                    ['padding-left'] = '10px'                } ) :wikitext( 'Price data' ) :done :tag( 'td' ) :attr( 'rowspan', rowspan ) :css( 'vertical-align', 'top' ) :tag( 'div' ) :addClass( 'GEPage' ) :css( {                       padding = '10px 5px 0 5px',                        float = 'right'                    } ) :tag( 'div' ) :addClass( 'GEdatachart' ) :css( {                           margin = '5px 0 25px 0',                            ['min-width'] = '700px',                            ['height'] = '500px'                        } ) -- GE Chart goes here :done :done :done :done :tag( 'tr' ) :tag( 'td' ) :css( {                   ['vertical-align'] = 'top',                    ['padding-bottom'] = '5px'                } ) :tag( 'ul' ) :tag( 'li' ) :wikitext( '\'\'\'Price:\'\'\' ' ) :tag( 'span' ) :attr( 'id', 'GEPrice' ) :wikitext( price ) :done :done :tag( 'li' ) :wikitext( '\'\'\'Last updated:\'\'\' ' .. timeago{data.date, purge='yes'} ) :tag( 'br' ) :done :tag( 'span' ) :attr( 'id', 'GEDate' ) :wikitext( date ) :done :done :done :done :done

if data.volume and ( dateDiffZ + 365 * dateDiffY ) <= 7 then tbl :tag( 'tr' ) :tag( 'th' ) :css( {                       ['text-align'] = 'left',                        ['padding-left'] = '10px'                    } ) :wikitext( 'Volume data' ) :done :done :tag( 'tr' ) :tag( 'td' ) :attr( 'id', 'volumeData' ) :css ( {                       ['vertical-align'] = 'top',                        ['padding-bottom'] = '5px'                    } ) :tag( 'ul' ) :tag( 'li' ) :wikitext( '\'\'\'7-day total:\'\'\' ' ) :tag( 'span' ) :attr( 'id', 'GEVolume' ) :wikitext( addcommas( 1000000 * data.volume ) ) :done :done :tag( 'li' ) :wikitext( '\'\'\'1-day average\'\'\' ' .. addcommas( round( 1000000 / 7 * data.volume, 3 ) ) ) :done :tag( 'li' ) :wikitext( '\'\'\'Last updated:\'\'\' ' .. timeago{data.volumeDate, purge='yes'} ) :tag( 'br' ) :done :tag( 'span' ) :attr( 'id', 'GEVolumeDate' ) :wikitext( lang:formatDate( 'j F Y, H:i "(UTC)"', data.volumeDate ) ) :done :done :done :done end

tbl :tag( 'tr' ) :tag( 'th' ) :css( {                   ['text-align'] = 'left',                    ['padding-left'] = '10px'                } ) :wikitext( 'Other details' ) :done :done :tag( 'tr' ) :tag( 'td' ) :css( {                   ['vertical-align'] = 'top',                    ['padding-bottom'] = '5px'                } ) :tag( 'ul' ) :tag( 'li' ) :wikitext( '\'\'\'Exchange ID:\'\'\' ' ) :tag( 'span' ) :attr( 'id', 'GEDID' ) :wikitext( data.itemId or '\'\'Unknown\'\'' ) :done :done :tag( 'li' ) :wikitext( '\'\'\'Low Alchemy:\'\'\' ' .. lowAlch ) :done :tag( 'li' ) :wikitext( '\'\'\'High Alchemy:\'\'\' ' .. highAlch ) :done :tag( 'li' ) :wikitext( '\'\'\'Value:\'\'\' ' .. ( data.value and addcommas( data.value ) or '\'\'Unknown\'\'' ) ) :done :tag( 'li' ) :wikitext( '\'\'\'Exchange limit:\'\'\' ' .. ( data.limit and addcommas( data.limit ) or '\'\'Unknown\'\'' ) ) :done :tag( 'li' ) :wikitext( '\'\'\'GED Category:\'\'\' ' .. data.category or '\'\'Unknown\'\'' ) :done :done :done :done :tag( 'tr' ) :tag( 'th' ) :css( {                   ['text-align'] = 'left',                    ['padding-left'] = '10px'                } ) :wikitext( 'External links' ) :done :done :tag( 'tr' ) :tag( 'td' ) :css( {                   ['vertical-align'] = 'top',                    ['padding-bottom'] = '5px'                } ) :tag( 'ul' ) :tag( 'li' ) :wikitext( '.. data.itemId .. ' Lookup current price' ) :done :tag( 'li' ) :wikitext( '.. mw.uri.encode( data.item, 'QUERY' ) .. ' Search for relevant prices' ) :done :done :done :done :tag( 'tr' ) :tag( 'th' ) :css( {                   ['text-align'] = 'left',                    ['padding-left'] = '10px'                } ) :wikitext( 'Usage' ) :done :done :tag( 'tr' ) :tag( 'td' ) :css( {                   ['vertical-align'] = 'top',                    ['padding-left'] = '5px',                    ['padding-bottom'] = '5px'                } ) :wikitext( 'This item is used in:' ) :newline :wikitext( '* ' .. table.concat( usage, '\n* ' ) .. '' ) :done :done

-- categories local cats = '' local natPrice

if title.nsText == 'Exchange' then cat = cats ..                   ..                    .. ''        if data.item ~= title.text then cats = cats .. ''       end if data.item then -- have to preprocess this for it to work cats = cats .. frame:preprocess( '' ) end if not data.itemId then cats = cats .. ''       end if not data.icon then cats = cats .. ''       end if data.value and ( data.alchable == nil or yesno( data.alchable ) ) then --[=[           -- commented out until this goes live -- or until I move nat runes to this to test it           natPrice = load( 'Nature rune' ).price if ( ( data.value * 0.6 ) - price - natPrice ) > 0 then cats = cats .. ''           end if ( ( data.value * 0.4 ) - price - natPrice ) > 0 then cats = cats .. ''           end ]=]       else cats = cats .. ''       end

local lastUpdate = ( lang:formatDate( 'z' ) - lang:formatDate( 'z', data.date ) + 365 * ( lang:formatDate( 'Y' ) - lang:formatDate( 'Y', data.date ) ) + 3 ) / 7 lastUpdate = round( lastUpdate, 0 )

if lastUpdate == 1 then cats = cats .. ''       elseif lastUpdate >= 2 and lastUpdate <= 4 then cats = cats .. ''       elseif lastUpdate > 4 then cats = cats .. ''       end if not data.limit then cats = cats .. ''       end if not data.category then cats = cats .. ''       end end

local help = mw.html.create( 'div' ) :attr( 'id', 'gemw_guide' ) :css( {           padding = '5px 10px',            color = '#000',            border = '1px solid #6b5331',            background = '#fff3c3'        } ) :wikitext( 'If you need assistance on editing this page, or trying to understand how these Exchange pages work, please add a comment to Talk:Grand Exchange Market Watch.' )

local data = p._data( item )

return tostring( help ) .. ' ' .. tostring( tbl ) .. data .. cats end

-- -- For and -- function p.price( frame, fargs, pargs ) -- usage: or     local fargs = frame and frame.args or fargs local pargs = frame and frame:getParent.args or pargs local item = pargs[1] -- default to formatted for backwards compatibility with old GE templates local format = true local multi = 1

-- test fargs.format here so it can be overridden with pargs further down if fargs.format ~= nil then format = yesno( fargs.format ) end

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

if 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, format, multi ) end

-- -- For accessing data through #invoke -- -- @param frame {table} -- function p.view( frame )

local fargs = frame.args local pargs = frame:getParent.args local item = pargs[1] -- view and format can be set along with #invoke or through template arguments -- item (above) can only be set through template arguments local view = fargs.View or fargs.view or pargs.View or pargs.view or 'price' local format = yesno( pargs.format or fargs.format )

if item then item = mw.text.trim( item ) else return ' Error: Item not specified ' end

view = mw.ustring.lower( view )

local ret_views = { limit = true, value = true }

if ret_views[view] then local ret = load( item )[view]

if format then ret = addcommas( ret ) end

return tostring( ret ) end

if view == 'price' then return p._price( nil, fargs, pargs ) end if view == 'icon' then return p._icon( item ) end if view == 'diff' then return p._diff( item, format ) end if view == 'table' then return p._table( item ) end if view == 'data' then return p._data( item, pargs.size, pargs.moreItems ) end if view == 'page' then return p._page( item, pargs.uses ) end

end

return p