Module:DPLlua
Uses Module:DPLlua/Helper to make it possible to include all parameters of a template while maintaining good performance.
This module is a helper module to be used by other modules; it may not be designed to be invoked directly. See RuneScape:Lua/Helper modules for a full list and more information.
Module | Function | Type | Use |
---|---|---|---|
DPLlua | ask( ... ) | tables | ask takes a series of tables each containing the settings for a DPL query; it will return the same number of result tables as input tables. All formatting settings are stripped from the config. If the config does not contains include , the result will be a simple list of page names.
{ <pagename#1>, <pagename#2>, <pagename#3>, } A query with an { ['include'] = { ['template#1'] = { [1] = val#1, [2] = val#2, ['param1'] = val#3, ['param2'] = val#4, }, ['template#2'] = { [3] = val#5, ['param1'] = val#6, }, ['%0'] = val#7 }, ['title'] = <pagename> } You can also do If the config value The output contains a If the DPL throws an error it will be available in the Differences with normal DPL:
|notcategory = val#1 |notcategory = val#2Note of warning, if you include content containing § symbols the result may be unreliable. If you include a whole template (e.g. include = '{some template}' ), content inside strip markers (not nowiki) can't be cleaned up inside lua so pipe characters (| ) will be replaced with § characters and the { and } characters are replaced by ❴ (U+2774) and ❵ (U+2775). Use include = '{some template}, {some template}:1:2:3' instead for the problem parameters. |
Example:
local dpl = require( 'Module:DPLlua' )
local a, b = dpl.ask( {
namespace = '',
linksto = 'Treasure trails',
distinct = 'strict',
ordermethod = 'title',
count = 5,
ignorecase = true
},{
namespace = '',
uses = 'Template:Recipe',
count = 1,
include = '{Recipe},{Infobox Item}',
ignorecase = true
} )
mw.logObject(a)
mw.logObject(b)
--[=[
table#1 {
"(beginner)",
"(easy)",
"(elite)",
"(g)",
"(hard)",
["time"] = 0.0541,
}
table#1 {
table#2 {
["include"] = table#3 {
["Infobox Item"] = table#4 {
["destroy"] = "Drop",
["equipable"] = "No",
["examine"] = "It's a bar of 'perfect' gold.",
["id"] = "2365",
["image"] = "[[File:'perfect' gold bar.png]]",
["members"] = "Yes",
["name"] = "'perfect' gold bar",
["noteable"] = "No",
["placeholder"] = "Yes",
["quest"] = "[[Family Crest]]",
["release"] = "[[9 April]] [[2002]]",
["stackable"] = "No",
["tradeable"] = "No",
["update"] = "New members quest online!",
["value"] = "300",
["weight"] = "1.814",
},
["Recipe"] = table#5 {
["facilities"] = "Furnace",
["mat1"] = "'perfect' gold ore",
["mat1cost"] = "No",
["members"] = "Yes",
["notes"] = "Partial completion of [[Family Crest]]",
["outputcost"] = "No",
["outputname"] = "'perfect' gold bar",
["skill1"] = "Smithing",
["skill1exp"] = "22.5",
["skill1lvl"] = "40",
["ticks"] = "4",
},
},
["title"] = "'perfect' gold bar",
},
["time"] = 0.0541,
}
]=]
-- <nowiki>
local dpl = {}
local libraryUtil = require( 'libraryUtil' )
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
dpl.pipe = '¦'
local dataContentMarker = '`#@@#`'
local allIncludedParamNames = {}
-- Custom function for splitting a string because mw.text.split() is waaay too slow
local function split( str, pattern, plain )
local res = {}
local continue = true
local startIndex = 1
while continue do
local i, j = string.find( str, pattern, startIndex, plain )
if i then
table.insert( res, string.sub( str, startIndex, i-1 ) )
startIndex = j + 1
else
table.insert( res, string.sub( str, startIndex ) )
continue = false
end
end
return res
end
-- Also custom function for speed
local function trim( str )
return string.match( str, '^%s*(.-)%s*$' )
end
local escapeChars = {
['{'] = '{',
['❴'] = '{', -- Wtf dpl...
['}'] = '}',
['❵'] = '}',
['['] = '[',
[']'] = ']',
['|'] = '|',
['-'] = '‐'
}
local function escape( str )
-- the \226\157\180\181 are used to match ❴ (U+2774) and ❵ (U+2775) wich are 3 bytes long (UTF-8) so
-- we can't use them directly inside [] patterns. Ustring would fix this but it's way too slow.
str = string.gsub( str, '[{}%[%]|%-]', escapeChars ):gsub( '\226\157[\180\181]', escapeChars )
return str
end
local unEscapeChars = {
['{'] = '{',
['}'] = '}',
['['] = '[',
[']'] = ']',
['|'] = '|',
['‐'] = '-'
}
local function unEscape( str )
str = string.gsub( str, '&#%d+;', unEscapeChars )
return str
end
local function removeFormattingSettings( query )
local toRemove = {
'mode',
'table',
'tablerow',
'tablesortcol',
'headingmode',
'headingcount',
'listattr',
'itemattr',
'hlistattr',
'hitemattr',
'userdateformat',
'shownamespace',
'escapelinks',
'titlemaxlength',
'replaceintitle',
'columns',
'rows',
'rowsize',
'rowcolformat',
'resultsheader',
'resultsfooter',
'oneresultheader',
'oneresultfooter',
'noresultsheader',
'suppresserrors',
'noresultsfooter',
'format'
}
for _, k in ipairs( toRemove ) do
query[k] = nil
end
end
local function formatInclude( query )
checkTypeForNamedArg( 'include', 'Module:DPLlua.ask', query, 'string' )
query = split( query, ',', true )
local includedParamNames = {}
for i = 1, #query do
if query[i]:match( '%b{}' ) then -- Check if we are including a template
local templateName, params = query[i]:match( '{(.-)[¦|}]([^,]*)' )
if params:find( '%S' ) then
params:gsub( '^:%-', '' )
query[i] = string.format( '{%s}%s', templateName, params )
for param in params:gmatch( ':([^:]*)' ) do
param = trim( param )
table.insert( includedParamNames, { name=templateName, isTemplate=true, param=param } )
end
else
query[i] = string.format( '{%s¦DPLlua helper}', templateName ) -- Use a helper template to get all the parameters of our included template
table.insert( includedParamNames, { name=templateName, isTemplate=true, includeAll=true } )
end
else
table.insert( includedParamNames, { name=trim( query[i] ) } )
end
end
return table.concat( query, ',' ), includedParamNames
end
local function formatDpl( query )
local queries = {}
local count = query.count or 500
local offset = query.offset or 0
local usesInclude = false
local includedParamNames = {}
query.count = nil
query.offset = nil
-- We use table format when the include parameter is used to make sure we can
-- differentiate between the results in case more than one item is included
local dplStringInclude =
[=[
{{#dpl:
|[email protected]@
|count=%s
|offset=%s
|%s
|table=,
|tablerow=%s
}}]=]
-- Table format requires an include statement so we use format instead.
-- This is also a lot faster than adding an empty include statement
local dplStringNoInclude =
[=[
{{#dpl:
|[email protected]@
|count=%s
|offset=%s
|%s
|format=,¦-¦[[%%PAGE%%¦]],,
}}]=]
-- Auto generate more than one dpl if count > 500
-- The results of these are later combined
for i = 1, math.ceil( count / 500 ) do
local params = {}
for k, v in pairs( query ) do
if k == 'include' then
v, includedParamNames = formatInclude( v )
usesInclude = true
end
if type( v ) == 'table' then
for _, x in ipairs( v ) do
table.insert( params, k .. '=' .. tostring( x ):gsub( '|', '¦' ) )
end
else
table.insert( params, k .. '=' .. tostring( v ):gsub( '|', '¦' ) )
end
end
if usesInclude then
table.insert( queries, string.format(
dplStringInclude,
count > 500 and 500 or count,
offset,
table.concat( params, '\n|' ),
string.rep( dataContentMarker..'%%'..dataContentMarker..',', #includedParamNames )
) )
else
table.insert( queries, string.format(
dplStringNoInclude,
count > 500 and 500 or count,
offset,
table.concat( params, '\n|' )
) )
end
count = count - 500
offset = offset + 500
end
table.insert( allIncludedParamNames, includedParamNames )
return table.concat( queries )
end
local function toTable( query )
local includedParamNames = table.remove( allIncludedParamNames, 1 )
local usesInclude = #includedParamNames > 0
local res = {}
query = query:gsub( '<p>Extension:DynamicPageList .-</p>', function(item) res.error = item; return '' end )
if query:find( '^@@' ) then -- @@ is used when no result is found
return res
end
if usesInclude then
query = query:gsub( '\127\'"`UNIQ%-%-nowiki%-%x+%-QINU`"\'\127', function(item) return '<nowiki>' .. item .. '</nowiki>' end ) -- Unstrip nowiki so we can clean their content
query = mw.text.unstripNoWiki( query )
query = query:gsub( dataContentMarker..'(.-)'..dataContentMarker, escape )
query = query:gsub( '{|.-|%-', '' ) -- Remove the header of the table
-- Replace the footer of the table width a row indicator. This effectively
-- combines the output of multiple dpl queries when count > 500
query = query:gsub( '|}', '|-' )
end
query = trim( query )
query = split( query, '|-', true ) -- Results of the returned pages are separated by |-
for _, v in ipairs( query ) do
if v:find( '%S' ) and not v:find( '^@@' ) then
v = trim( v )
local title = v:match( '^|%[%[(.-)|' )
local rawDataList = v:match( '^|.-|.-|(.*)' ) -- This is everything after the title
if not usesInclude then
if title and title ~= '' then
table.insert( res, title )
end
else
-- When multiple includes are used (e.g. include={Template1},{Template2} or include={Template}:1:2) its results are separated by a pipe
rawDataList = split( rawDataList, '|', true )
local cleanedDataList = {}
for incIndex, dataItem in ipairs( rawDataList ) do
dataItem = unEscape( dataItem )
-- When we include an entire template we use the %ARGS% parameter supplied by dpl.
-- However all | characters are repaced with §, e.g.:
-- §nameLessParam
-- §param = text [[wowee§link text]]
-- §param2 = text {{something§something else}}
dataItem = dataItem:gsub( '%b{}', function(x) return x:gsub( '§', '|' ) end ) -- Restore pipe characters inside links and templates
dataItem = dataItem:gsub( '%b[]', function(x) return x:gsub( '§', '|' ) end )
dataItem = dataItem:gsub( '<nowiki>(.-)</nowiki>', function(x) return mw.getCurrentFrame():extensionTag( 'nowiki', x ) end ) -- Restrip nowiki
dataItem = trim( dataItem )
if includedParamNames[ incIndex ].isTemplate and includedParamNames[ incIndex ].includeAll then -- Check if we included a full template
local _dataItem = {}
if dataItem ~= '' then
dataItem = split( dataItem:sub( 3 ), '§' ) -- The sub(3) removes the first § at the start. § is 2 bytes wide so start at index 3
for i, item in ipairs( dataItem ) do
if item:find( '=' ) then -- Check if the parameter is named or unnamed
local param, value = item:match( '^%s*(.-)%s*=%s*(.-)%s*$' )
_dataItem[ param ] = value
else
table.insert( _dataItem, trim( item ) )
end
end
end
dataItem = _dataItem
end
if includedParamNames[ incIndex ].isTemplate and not includedParamNames[ incIndex ].includeAll then -- This means there was an include in the form 'include = {template}:param'
local templateName = includedParamNames[ incIndex ].name
local paramName = includedParamNames[ incIndex ].param
paramName = tonumber( paramName ) or paramName -- Keep as string if tonumber fails
cleanedDataList[ templateName ] = cleanedDataList[ templateName ] or {}
cleanedDataList[ templateName ][ paramName ] = dataItem
else
cleanedDataList[ includedParamNames[ incIndex ].name ] = dataItem
end
end
if title and title ~= '' then
table.insert( res, { title=title, include=cleanedDataList } )
end
end
end
end
return res
end
-- Accepts a series of tables each containig the settings for a dpl query.
-- Combinig multiple dpl queries yields better performance than doing them sequentially
function dpl.ask( ... )
local queries = { ... }
for i = 1, #queries do
checkType( 'Module:DPLlua.ask', i, queries[i], 'table' )
removeFormattingSettings( queries[i] )
queries[i] = formatDpl( queries[i] )
end
queries = table.concat( queries, '[email protected]µ@$' )
local time = os.clock()
queries = mw.getCurrentFrame():preprocess( queries )
time = os.clock() - time
queries = split( queries, '[email protected]µ@$', true )
for i = 1, #queries do
queries[i] = toTable( queries[i] )
queries[i].time = time
end
return unpack( queries )
end
return dpl
-- </nowiki>