Module:Loops

From Tears of Themis Wiki

Lua module implementing features similar to mw:Extension:Loops, specifically with functionality to parse lists, since the AE Wiki mainly utilizes the extension for that purpose.

Usage[edit source]

{{#invoke:Loops|function_name}}

Functions[edit source]

parseList[edit source]

Parses a string list using a given delimiter (defaults to %s if not given). The value is then passed into the specified template or formatted according to a given string format pattern.

Transitioning from Extension:Loops[edit source]

This is documentation on how to transition from using Extension:Loops to Module:Loops to parse lists.

Extension:Loops Syntax

{{#vardefine:count|0}}
{{#while:
| {{#if: {{#explode:<list>|<delimiter>|{{#var:count}}}} | true }}
| <!-- do something -->
}}

The key variables here are <list> and <delimiter>. These can simply be specified in the module invoke like so:

{{#invoke:
|list=<list>
|delimiter=<delimiter>
...
}}

The rest of the invoke depends on how complex the "do something"/block statement portion is, you can choose to either use a string format (simple output) or template format (more complex output).

  • If your block statement is simple formatting, then use string format output.
    • Because of the order the parser runs statements, some Wikitext may not be compatible with string format. Thus it's recommended for more complex Wikitext use template format instead.
    • If you use {{#explode:<list>|<delimiter>|{{#var:count}}}} inside your block statement, replace it with a replace string of choice. This should be a unique string pattern, as you don't want to erroneously replace other parts of the format string.
  • If your block statement is more complex, consider moving it into a template which you can then specify with the module invoke.
    • If you use {{#explode:<list>|<delimiter>|{{#var:count}}}} inside your block statement, replace it with {{{1|}}}.
    • If you use your index/counter variable in your template code, you can replace it with {{#var:count}}.

See the parseList examples for sample usages.

Usage[edit source]

  • Template format
{{#invoke:Loops|parseList
|list=
|delimiter=
|template=
|template!<arg>= //optional
}}
  • String format
{{#invoke:Loops|parseList
|list=
|delimiter=
|format=
|replaceString=
}}

Examples[edit source]

  • Template format
{{#invoke:Loops|parseList
|list=Marius "Dream of Thebes",Marius "Dream of Thebes",Marius "Dream of Thebes"
|delimiter=,
|template=Card icon
|template!size=50px
|template!text=500
}}
  • String format example #1
{{#invoke:Loops|parseList
|list=git,gem,fp
|delimiter=,
|format=|format=<nowiki/>
* Test $1
|replaceString=$1
}}

  • Test git
  • Test gem
  • Test fp
  • String format example #2
{| class="wikitable"
! Test
{{#invoke:Loops|parseList
|list=git,gem,fp
|delimiter=,
|format=<tr><td>$1 in a cell</td></tr>
|replaceString=$1
}}
|}
Test
git in a cell
gem in a cell
fp in a cell

numElements[edit source]

Returns the number of elements in a string list given a delimiter (defaults to %s if not given).

Usage[edit source]

{{#invoke:Loops|numElements
|list=
|delimiter=
}}

Example[edit source]

{{#invoke:Loops|numElements
|list=this,list,has,5,elements
|delimiter=,
}}

5

loop[edit source]

Duplicates the mw:Extension:Loop##loop function. Output can either be a given template or string format.

Usage[edit source]

{{#invoke:Loops|loop
|startValue=
|numLoops=
|template=
|template!<arg>= //optional
}}
  • String format
{{#invoke:Loops|parseList
|startValue=
|numLoops=
|format=
|replaceString= //optional
}}

Example[edit source]

  • Template format
{{#invoke:Loops|loop
|startValue=3
|numLoops=5
|template=Text
|template!style=border: 1px solid black;
}}

Lua error: expandTemplate: template "Text" does not exist.

  • String format
{{#invoke:Loops|loop
|startValue=8
|numLoops=3
|format=<nowiki/>
* Test $1
|replaceString=$1
}}

  • Test 8
  • Test 9
  • Test 10

-- This module was created for the Another Eden Miraheze Wiki to replace
-- Extension:Loops. For most Loops usage cases, we were parsing lists, so 
-- most of the module currently focuses on that.
-- 
-- @module loops
-- @author [[User:Elaeagnifolia]]

-- Dependencies
VariablesLua = mw.ext.VariablesLua

local loops = {};

--- Returns an error message
-- @param {string} message
-- @return {string}
local function userError(message)
      return '<strong class="error">' .. message .. '</strong>';
end

--- Splits a string list based on the given delimiter.
-- @param {string} list
-- @param {string} delimiter
-- @return {table}
local function explode(list, delimiter)
      -- Default the delimiter to space if not given
    if delimiter == nil then
            delimiter = "%s"
      end
      t = {}
      for str in string.gmatch(list, "([^" .. delimiter .. "]+)") do
            table.insert(t, str)
      end
      return t
end

--- Returns the number of elements in a string list split by a given delimiter
--
-- Usage:
--   {{#invoke:Loops|numElements
--   |list=
--   |delimiter=
--   }}
--
-- @param {Frame} frame
-- @return {integer}
function loops.numElements(frame)
      -- Instantiate variables
      local list, delimiter;
      local frameArgs = frame.args;
      
      -- Grab the frame arguments
      list = frameArgs.list;
      delimiter = frameArgs.delimiter;
      
      local listElements = explode(list, delimiter);
      return #listElements;
end

--- Iterates the list based on a given delimiter and parses the value through
--- either a template or a specified format string. Similar to exploding and
--- iterating a list with #while.
-- 
-- See Module:Loops/doc for additional usage examples.
--
-- @param {Frame} frame
-- @return {table} result
function loops.parseList(frame)
      local list, delimiter, template, format, replaceString;
      local frameArgs = frame.args;
      local templateArgs = {};
      local templateArgPrefix = 'template!';

      -- Grab the frame arguments
      list = frameArgs.list;
      delimiter = frameArgs.delimiter;
      template = frameArgs.template;
      format = frameArgs.format;
      replaceString = frameArgs.replaceString;
      
      -- Validate the frame arguments
      if list == nil then
            return userError('list argument has not been specified');
      end

      if template == nil and format == nil then
            return userError('template or format argument has not been specified');
      end
      
      if template ~= nil and format ~= nil then
            return userError('template and format arguments cannot be used together');
      end
      
      if format ~= nil and replaceString == nil then
            return userError('replaceString argument must be specified with format');
      end

      -- For delimiter, default to %s if one hasn't been specified
      if delimiter == nil then
            delimiter = '%s';
      end
      
      -- Get the additional arguments that will be sent to the template
      for key, value in pairs(frameArgs) do
            if string.find(key, templateArgPrefix) ~= nil then
                  local _, endIndex = string.find(key, templateArgPrefix);
                  templateArgs[string.sub(key, endIndex+1)] = value
            end
      end
      
      local result = {}
      local count = 0;
      local listElements = explode(list, delimiter)
      
      -- Loop through the exploded list and send value to specified template
      for key, value in pairs(listElements) do
            -- Increment loop counter
            count = count + 1;
            
            -- Keep a count of how many loops in {{#var:count}}
            -- This var can be used in the given template if needed
            VariablesLua.vardefine('count', count);
            
            -- If template argument was specified, format by expanding template
            if template ~= nil then
                  -- Add the list element value to the template arguments
                  -- The value must be {{{1}}} in the given template
                  table.insert(templateArgs, 1, value);
            
                  -- Pass the arguments to the template and expand template
                  result[#result+1] = frame:expandTemplate{title = template, args = templateArgs };
            end
            
            -- Format based on the given string
            if format ~= nil then
                  result[#result+1] = format:gsub(replaceString, value);
            end
      end
      
      return table.concat(result);
end

--- Duplicates functionality of Extension:Loop's #loop. Takes a
--- start value to increment a specified loop amount. Will also
--- output either a given template or string each loop.
-- 
-- See Module:Loops/doc for additional usage examples.
--
-- @param {Frame} frame
-- @return {table} result
function loops.loop(frame)
      local startValue, numLoops, template, format, replaceString;
      local frameArgs = frame.args;
      local templateArgs = {};
      local templateArgPrefix = 'template!';

      -- Grab the frame arguments
      startValue = frameArgs.startValue;
      numLoops = frameArgs.numLoops;
      template = frameArgs.template;
      format = frameArgs.format;
      replaceString = frameArgs.replaceString;
      
      -- Validate the frame arguments
      if startValue == nil then
            return userError('startValue argument has not been specified');
      elseif tonumber(startValue) == nil then
            return userError('startValue must be a valid number');
      end

      if numLoops == nil then
            return userError('numLoops argument has not been specified');
      elseif tonumber(numLoops) == nil then
            return userError('numLoops must be a valid number');
      end

      if template == nil and format == nil then
            return userError('template or format argument has not been specified');
      end
      
      if template ~= nil and format ~= nil then
            return userError('template and format arguments cannot be used together');
      end

      -- Get the additional arguments that will be sent to the template
      for key, value in pairs(frameArgs) do
            if string.find(key, templateArgPrefix) ~= nil then
                  local _, endIndex = string.find(key, templateArgPrefix);
                  templateArgs[string.sub(key, endIndex+1)] = value
            end
      end
      
      local result = {}
      local count = 0;

      for i=1,tonumber(numLoops) do
            -- Increment loop counter
            count = count + 1;
            
            -- Keep a count of how many loops in {{#var:count}}
            -- This var can be used in the given template if needed
            VariablesLua.vardefine('count', count);

            -- Also increment startValue and store it in a var to be used in templates
            VariablesLua.vardefine('startValue', startValue);
            
            -- If template argument was specified, format by expanding template
            if template ~= nil then
                  -- Add the list element value to the template arguments
                  -- The value must be {{{1}}} in the given template
                  table.insert(templateArgs, 1, startValue);
            
                  -- Pass the arguments to the template and expand template
                  result[#result+1] = frame:expandTemplate{title = template, args = templateArgs };
            end
            
            -- Format based on the given string
            if format ~= nil then
                  result[#result+1] = format:gsub(replaceString, startValue);
            end
            
            -- Increment startValue
            startValue = startValue + 1;
      end

      return table.concat(result);
end

function loops.varLoop(frame)
	local list, delimiter, valueDelimiter;
	local frameArgs = frame.args;
	
	list = frameArgs.list;
	delimiter = frameArgs.delimiter;
	valueDelimiter = frameArgs.valueDelimiter;
	
	local result = {}
	
	local listElements = explode(list, delimiter);
	for key, value in pairs(listElements) do
		result[#result+1] = value .. '///';
		--VariablesLua.vardefine(
		--	value:sub(1, value:find(valueDelimiter)-1),
		--	value:sub(value:find(valueDelimiter)+1)
		--);
	end
	return table.concat(result);
	--return '';
end

return loops;