FANDOM


--- String provides access to basic string functions.
--  This module allows simple text manipulation and is dozens of times
--  more efficient that its parser function counterparts. MediaWiki's
--  native string functions have a upper limit defined by 
--  `wgPFStringLengthLimit` as 1000 characters - this module lacks any
--  such limit.
--  
--  The default category name for errors is [{{fullurl:Category:Errors
--  reported by Module String}} "Errors reported by Module String"].
--  However, this can be overriden if your wiki configures
--  '''Module:String/category''' to return a different category name.
--  On this wiki, the category is [[Module:String/category|configured
--  to Scribunto's general error category]], and this configuration
--  corresponds to "{{int:scribunto-common-error-category}}" on wikis
--  written in your user language.
--  
--  The majority of template functions provided can be invoked with
--  named parameters, unnamed parameters, or a mixture.  If named
--  parameters are used, Mediawiki will automatically remove leading
--  or trailing whitespace from the parameter - see
--  @{str._getParameters}.
--  
--  Template options:
--   * `ignore_errors`: If set to `true` or `1`, any error condition will
--  result in an empty string being returned rather than an error
--  message.
--   * `error_category`: If an error occurs, overrides the name of a
--  category to include with the error message.
--   * `no_category`: If set to `true` or `1`, no category will be added
--  if an error is generated.
--  
--  @module             str
--  @release            stable
--  @author             [[User:Dessamator|Dessamator]]
--  @credit             [[wikipedia:User:Anomie|Anomie]] (Wikipedia)
--  @attribution        [[wikipedia:Module:String|Wikipedia]]
--  @see                [[wikipedia:Module:String/doc|Wikipedia documentation]]
--  @see                [[Module talk:String/testcases|Test suite for module]]
--  
--  <nowiki>
 
local str = {}
 
-- begin dev wiki change
local DEFAULT_CATEGORY = select(2, pcall(require, 'Module:String/category'))
if type(DEFAULT_CATEGORY) ~= 'string' then
    DEFAULT_CATEGORY = 'Errors reported by Module String'
end
-- end dev wiki change
 
--  Module dependencies.
local ustring, text = mw.ustring, mw.text
local entrypoint = require( 'Dev:Entrypoint' )
local yesno = require( 'Dev:Yesno' )
 
--  Template functions.
 
--- Computes the length of the target string.
--  @function           str.len
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param[opt]         {string} frame.args.s Target Unicode string.
--  @return             {number} Length of Unicode string.
--  @usage              {{string|len|target string}}
--  @usage              {{string|len|s=target string}}
function str.len( frame )
    local args = str._getParameters( frame.args, {'s'} )
    local s = args['s'] or ''
    return ustring.len( s )
end
 
--- Extracts a substring of the target string at specified indices.
--  Indexing is **1-based** for the target string to extract from. If
--  either `i` or `j` is a negative value, it is interpreted the same as
--  selecting a character by counting from the end of the string.
--  Hence, a value of `-1` is the same as selecting the last character
--  of the string.
--  
--  If the requested indices are out of range for the given string, an
--  error is reported.
--  
--  @function           str.sub
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.s The string to return a
--                      subset of.
--  @param              {string} frame.args.i The fist index of the substring
--                      to return; default: `1`.
--  @param              {string} frame.args.j: The last index of the string
--                      to return; default: `#s`.
--  @error[99]          {string} 'string subset index out of range'
--  @error[102]         {string} 'string subset indices out of order'
--  @return             Substring from `i`/`1` to `j`/`#s`.
--  @usage              {{string|sub|target string|start index|end index}}
--  @usage              {{string|sub|s= target string |i= start index |j = end index }}
function str.sub( frame )
    local args = str._getParameters( frame.args, { 's', 'i', 'j' } )
    local s = args['s'] or ''
    local i = tonumber( args['i'] ) or 1
    local j = tonumber( args['j'] ) or -1
 
    local len = ustring.len( s )
 
    -- Convert negatives for range checking
    if i < 0 then
        i = len + i + 1
    end
    if j < 0 then
        j = len + j + 1
    end
 
    if i > len or j > len or i < 1 or j < 1 then
        return str._error( 'string subset index out of range', frame )
    end
    if j < i then
        return str._error( 'string subset indices out of order', frame )
    end
 
    return ustring.sub( s, i, j )
end
 
--- Implements `{{str sub old}}`.
--  This function is kept in order to maintain these older templates.
--  Indexing is **0-based** for the substring start position.
--  
--  @function           str.sublength
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.s Source string to search.
--  @param              {string} frame.args.i Index to begin output
--                      substring at. Default: `0`.
--  @param              {string} frame.args.len Length of output substring.
--  @return             {string} Substring starting with `i`/`0` of
--                      length `len`.
--  @warning            This function is deprecated in favor of @{str.sub}.
function str.sublength( frame )
    local args = str._getParameters( frame.args, { 's', 'i', 'len' } )
    local i = tonumber( args['i'] ) or 0
    local len = tonumber( args['len'] )
    return ustring.sub( args['s'], i + 1, len and ( i + len ) )
end
 
--- Extracts a substring matching a pattern from the source string.
--  If `match` or `start` are out of range for the string being queried,
--  then this function generates an error.  An error is also generated if
--  no match is found. If one adds the parameter ignore_errors=true, then
--  the error will be suppressed and an empty string will be returned on
--  any failure.
--  
--  @function           str.match
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.s Target string to search.
--  @param              {string} frame.args.pattern The pattern or string
--                      to find within the string.
--  @param              {string} frame.args.start The index within the
--                      source string to start the search. The first
--                      character of the string has index 1. Default: `1`.
--  @param              {string} frame.args.match In some cases it may be
--                      possible to make multiple matches on a single
--                      string. This specifies which match to return,
--                      where the first match is `match = 1`. If a
--                      negative number is specified then a match is
--                      returned counting from the last match. Hence
--                      `match = -1` is the same as requesting the last
--                      match.  Default: `1`.
--  @param              {string} frame.args.plain A flag indicating that
--                      the pattern should be understood as a literal.
--                      Default: `false`.
--  @param              {string} frame.args.nomatch If no match is found,
--                      output the "nomatch" value rather than an error.
--  @error[192]         {string} 'target string is empty'
--  @error[195]         {string} 'pattern string is empty'
--  @error[198]         {string} 'requested start is out of range'
--  @error[201]         {string} 'match index is out of range'
--  @error[241]         {string} 'match not found'
--  @return             {string} Substring of the source string matching
--                      a pattern or string literal.
--  @usage
--      
--      {{string|match|source string|pattern string|start index|match number|plain flag|nomatch output}}
--      
--  @usage
--      
--      {{string|match
--      | s = source string
--      | pattern = pattern string
--      | start = start index
--      | match = match number
--      | plain = plain flag
--      | nomatch = nomatch output
--      }}
--      
--  @see                [[Lua reference manual/Standard libraries#Patterns|Native patterns]]
--  @see                [[Lua reference manual/Standard libraries#Patterns|Unicode patterns]]
function str.match( frame )
    local args = str._getParameters( frame.args, { 's', 'pattern', 'start', 'match', 'plain', 'nomatch' } )
    local s = args['s'] or ''
    local start = tonumber( args['start'] ) or 1
    local plain_flag = str._getBoolean( args['plain'] or false )
    local pattern = args['pattern'] or ''
    local match_index = math.floor( tonumber( args['match'] ) or 1 )
    local nomatch = args['nomatch']
 
    if s == '' then
        return str._error( 'target string is empty', frame )
    end
    if pattern == '' then
        return str._error( 'pattern string is empty', frame )
    end
    if math.abs( start ) < 1 or math.abs( start ) > ustring.len( s ) then
        return str._error( 'requested start is out of range', frame )
    end
    if match_index == 0 then
        return str._error( 'match index is out of range', frame )
    end
    if plain_flag then
        pattern = str._escapePattern( pattern )
    end
 
    local result
    if match_index == 1 then
        -- Find first match is simple case
        result = ustring.match( s, pattern, start )
    else
        if start > 1 then
            s = ustring.sub( s, start )
        end
 
        local iterator = ustring.gmatch( s, pattern )
        if match_index > 0 then
            -- Forward search
            for w in iterator do
                match_index = match_index - 1
                if match_index == 0 then
                    result = w
                    break
                end
            end
        else
            -- Reverse search
            local result_table = {}
            local count = 1
            for w in iterator do
                result_table[count] = w
                count = count + 1
            end
 
            result = result_table[ count + match_index ]
        end
    end
 
    if result == nil then
        return nomatch == nil
            and str._error( 'match not found', frame )
            or  nomatch
    else
        return result
    end
end
 
--- Returns a single character from the target string.
--  Indexing is **1-based** for the target string position.
--  
--  If one requests a negative value, this function will select a
--  character by counting backwards from the end of the string. In other
--  words, `pos = -1` is the same as asking for the last character.
--  
--  A requested value of zero, or a value greater than the length of the
--  string returns an error.
--  
--  @function           str.pos
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.target The string to search.
--  @param              {string} frame.args.pos The index for the character
--                      to return. Can be negative for reverse indexing.
--  @error[274]         {string} 'string index out of range'
--  @return             {string} Single character at position `pos`.
--  @usage              {{string|pos|target string|index value}}
--  @usage              {{string|pos|target=target string|pos = index value }}
function str.pos( frame )
    local args = str._getParameters( frame.args, { 'target', 'pos' } )
    local target = args['target'] or ''
    local pos = tonumber( args['pos'] ) or 0
 
    if pos == 0 or math.abs( pos ) > ustring.len( target ) then
        return str._error( 'string index out of range', frame )
    end
 
    return ustring.sub( target, pos, pos )
end
 
--- Searches for a target string/pattern in a string.
--  
--  This function should be safe for UTF-8 strings.
--  
--  @function           str.find
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source The string to search.
--  @param              {string} frame.args.target The string or pattern
--                      to find within the `source` string.
--  @param              {string} frame.args.start The index within the source
--                      string to start the search. Default: `1`.
--  @param              {string} frame.args.plain Boolean flag indicating
--                      that target should be understood as a literal and
--                      not as a Lua style regular expression. Default: `true`.
--  @return             First index >= `start`/`1` that `target` is found
--                      within `source`. Indexing is **1-based**. If `target`
--                      is not found, then this function returns 0. If
--                      either "source" or "target" are missing / empty, this
--                      function also returns 0.
--  @usage
--      
--      {{string|find|source string|target string|start index|plain flag}}
--      
--  @usage
--      
--      {{string|find
--      | source = source string
--      | target = target string
--      | start = start index
--      | plain = plain flag
--      }}
--      
function str.find( frame )
    local args = str._getParameters( frame.args, { 'source', 'target', 'start', 'plain' } )
    local source = args['source'] or ''
    local pattern = args['target'] or ''
    local start_pos = tonumber( args['start'] ) or 1
    local plain = args['plain'] or true
 
    if source == '' or pattern == '' then
        return 0
    end
 
    plain = str._getBoolean( plain )
 
    local start = ustring.find( source, pattern, start_pos, plain )
 
    return start or 0
end
 
--- Duplicates the behavior of `{{str find}}` including its quirks.
--  This is provided in order to support older templates.
--  
--  @function           str.str_find
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Source string to find a
--                      match in.
--  @param              {string} frame.args.target Search string within `source`.
--  @return             The first index in `source` that is a match to
--                      `target`. Indexing is **1-based**, and the function
--                      returns `-1` if the `target` string is not present
--                      in `source`.
--  @warning            This function is deprecated in favour of @{str.find}.
--  @note               If the "target" string is empty / missing, this
--                      function returns a value of `'1'`, which is
--                      generally unexpected behavior, and must be
--                      accounted for separatetly.
function str.str_find( frame )
    local args = str._getParameters( frame.args, { 'source', 'target' } )
    local source = args['source'] or ''
    local target = args['target'] or ''
 
    if target == '' then
        return 1
    end
 
    local start = ustring.find( source, target, 1, true )
 
    return start or -1
end
 
--- Determines the presence of a prefix in a string.
--  @function           str.prefix
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Source string to test.
--  @param              {string} frame.args.prefix Suffix to test for.
--  @return             {string} Boolean flag indicating prefix presence.
--  @usage              {{string|prefix|source string|prefix string}}
--  @usage              {{string|prefix|source= source string |prefix = prefix string }}
function str.prefix( frame )
    local args = str._getParameters( frame.args, { 'source', 'prefix' } )
    local source = args['source'] or ''
    local prefix = args['prefix'] or ''
    if prefix == '' then
        return 'yes' -- All strings end with the empty string.
    end
    return ustring.sub( source, 1, ustring.len(prefix) ) == prefix
        and 'yes'
        or  'no'
end
 
--- Determines the presence of a suffix in a string.
--  @function           str.suffix
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Source string to test.
--  @param              {string} frame.args.suffix Suffix to test for.
--  @return             {string} Boolean flag indicating suffix presence.
--  @usage              {{string|suffix|source string|suffix string}}
--  @usage              {{string|suffix|source= source string |suffix = suffix string }}
function str.suffix( frame )
    local args = str._getParameters( frame.args, { 'source', 'suffix' } )
    local source = args['source'] or ''
    local suffix = args['suffix'] or ''
    if suffix == '' then
        return 'yes' -- All strings end with the empty string.
    end
    return ustring.sub( source, -ustring.len(suffix), -1 ) == suffix
        and 'yes'
        or  'no'
end
 
--- Counts the number of occurrences of one string in another.
--  @function           str.count
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Source string to count
--                      occurences in.
--  @param              {string} frame.args.pattern Lua pattern or string
--                      to match against.
--  @param[opt]         {string} frame.args.plain Boolean flag indicating
--                      that pattern should be understood as a literal
--                      and not as a Lua style regular expression.
--                      Default: `'true'`.
--  @return             {number} Number of occurences in target string.
--  @usage
--      
--      {{string|count|source string|pattern string|plain flag}}
--      
--  @usage
--      
--      {{string|count
--      | source = source string
--      | pattern = pattern string
--      | plain = plain flag
--      }}
--      
function str.count( frame )
    local args = str._getParameters( frame.args, { 'source', 'pattern', 'plain' } )
    local source = args['source'] or ''
    local pattern = args['pattern'] or ''
    local plain = str._getBoolean( args.plain or 'true' )
    if plain then
        pattern = str._escapePattern( pattern )
    end
    return select( 2, ustring.gsub( source, pattern, '' ) )
end
 
--- Replaces a target string or pattern within another string.
--  
--  @function           str.replace
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source The string to search.
--  @param              {string} frame.args.pattern The string or pattern
--                      to find within the source.
--  @param              {string} frame.args.replace The replacement text
--  @param              {string} frame.args.count The number of occurences
--                      to replace. Defaults to all occurences.
--  @param              {string} frame.args.plain Boolean flag indicating
--                      that pattern should be understood as a literal
--                      and not as a Lua style regular expression.
--                      Default: `'true'`.
-- @usage
--     
--      {{string|replace|source string|pattern string|replace string|replacement count|plain flag}}
--     
-- @usage
--     
--     {{string|replace
--     | source = source_string
--     | pattern = pattern string
--     | replace = replace string
--     | count = replacement count
--     | plain = plain flag
--     }}
--     
function str.replace( frame )
    local args = str._getParameters( frame.args, { 'source', 'pattern', 'replace', 'count', 'plain' } )
    local source = args['source'] or ''
    local pattern = args['pattern'] or ''
    local replace = args['replace'] or ''
    local count = tonumber( args['count'] )
    local plain = args['plain'] or true
 
    if source == '' or pattern == '' then
        return source_str
    end
    plain = str._getBoolean( plain )
 
    if plain then
        pattern = str._escapePattern( pattern )
        --Only need to escape replacement sequences.
        replace = ustring.gsub( replace, "%%", "%%%%" );
    end
 
    if count ~= nil then
        return ( ustring.gsub( source, pattern, replace, count ) )
    else
        return ( ustring.gsub( source, pattern, replace ) )
    end
end
 
--- Repeats a string $$\{n \in \N \}$$ times.
--  A simple template pipe for the `string.rep` Lua function.
--  @function           str.rep
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Source string to repeat.
--  @param              {string} frame.args.count Integer for number of
--                      output repetitions.
--  @return             {string} String with repeated copies of `source`.
--  @error[513]         {string} 'function rep expects a number as second
--                      parameter, received $count'
--  @usage              {{string|rep|hello|3}}
--  @usage              {{string|rep|source = hello |count = 3 }}
function str.rep( frame )
    local args = str._getParameters( frame.args, {'source', 'count' } )
    local repetitions = tonumber( args['count'] )
    if not repetitions then
        return str._error( 'function rep expects a number as second parameter, received "' .. ( args['count'] or 'nil' ) .. '"', frame )
    end
    return ustring.rep( args['source'] or '', repetitions )
end
 
--- Convert string to lowercase Unicode character sequence.
--  @function           str.lc
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Source string to change case.
--  @return             Lowercase UTF-8 string.
--  @usage              {{string|lc|INPUT STRING}}
--  @usage              {{string|lc|source = INPUT STRING }}
function str.lc( frame )
    return ustring.lower( frame.args['source'] or frame.args[1] )
end
 
--- Convert string to uppercase Unicode character sequence.
--  @function           str.uc
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.str String of indeterminate case.
--  @return             Uppercase UTF-8 string.
--  @usage              {{string|uc|input string}}
--  @usage              {{string|uc|source = input string }}
function str.uc( frame )
    return ustring.upper( frame.args['source'] or frame.args[1] )
end
 
--- Convert string prefix to lowercase Unicode character.
--  @function           str.lcfirst
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source String of indeterminate case.
--  @return             UTF-8 string with lowercase prefix letter.
--  @usage              {{string|lcfirst|Input string}}
--  @usage              {{string|lcfirst|source = Input string }}
function str.lcfirst( frame )
    return ( ustring.gsub( frame.args['source'] or frame.args[1], '^%u', ustring.lower ) )
end
 
--- Convert string prefix to uppercase Unicode character.
--  @function           str.ucfirst
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Source string to change case.
--  @return             UTF-8 string with uppercase prefix letter.
--  @usage              {{string|ucfirst|iNPUT STRING}}
--  @usage              {{string|ucfirst|source = iNPUT STRING }}
function str.ucfirst( frame )
    return ( ustring.gsub( frame.args['source'] or frame.args[1], '^%l', ustring.upper ) )
end
 
--- Pads beginning of a string with a character or whitespace.
--  @function           str.padleft
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.str Source string to pad.
--  @param              {string} frame.args.len Length of output string.
--  @param[opt]         {string} frame.args.char Start padding character.
--                      Default: `' '`.
--  @return             {string} String padded to the left.
--  @usage
--     
--     {{string|padleft|source string|expected length|pad character}}
--     
--  @usage
--     
--     {{string|padleft
--     | source = source string
--     | len = expected length
--     | char = pad character
--     }}
--     
function str.padleft( frame )
    local args = str._getParameters( frame.args, { 'source', 'len', 'char' } )
    local source = args['source'] or ''
    local len = tonumber( args['len'] ) or 0
    local char = ( args['char'] or ' ' ):sub( 1, 1 )
    return char:rep( len - #source ) .. source
end
 
--- Pads end of a string with a character or whitespace.
--  @function           str.padright
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Source string to pad.
--  @param              {string} frame.args.len Length of output string.
--  @param[opt]         {string} frame.args.char End padding character.
--                      Default: `' '`.
--  @return             {string} String padded to the right.
--  @usage
--     
--     {{string|padright|source string|expected length|pad character}}
--     
--  @usage
--     
--     {{string|padright
--     | source = source string
--     | len = expected length
--     | char = pad character
--     }}
--     
function str.padright( frame )
    local args = str._getParameters( frame.args, { 'source', 'len', 'char' } )
    local source = args['source'] or ''
    local len = tonumber( args['len'] ) or 0
    local char = ( args['char'] or ' ' ):sub( 1, 1 )
    return source .. char:rep( len - #source )
end
 
--- Return delimited string piece, like PHP's `explode()`.
--  Indexing is **0-based** to match the behavior of `{{#explode}}`
--  parser function.
--  
--  @function           str.explode
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Delimited string to split.
--  @param              {string} frame.args.dlm Symbol or character to split
--                      with.
--  @param[opt]         {string} frame.args.pos Initial piece position.
--                      Default: `0`.
--  @param[opt]         {string} frame.args.lim Maximum number of pieces to
--                      append. Default: `1`.
--  @return             {string} Percent-encoded string.
--  @usage
--     
--     {{string|explode|source string|delimiter|position 0-index|piece limit}}
--     
--  @usage
--     
--     {{string|explode
--     | source = source string
--     | dlm = delimiter
--     | pos = position 0-index
--     | lim = piece limit
--     }}
--     
function str.explode( frame )
    local args = str._getParameters( frame.args, { 'source', 'dlm', 'pos', 'lim' } )
    local source = args['source'] or ''
    local delim = str._escapePattern( args['dlm'] or ' ' )
    local pos = ( tonumber( args['pos'] or '0' ) + 1 )
    local pieces = text.split(source, delim)
    local limit = tonumber( args['lim'] ) or #pieces
    local dividers = {}
    for div in ustring.gmatch(source, delim) do
        table.insert(dividers, div)
    end
    if limit < #pieces then
        for index, value in ipairs(pieces) do
            if index > limit then
                pieces[limit] = pieces[limit] .. dividers[index-1] .. value
            end
        end
        for index, value in ipairs(pieces) do
            if index > limit then pieces[index] = nil end
        end
    end
    if pos < 1 then
        pos = #pieces + pos
    end
    return pieces[pos] or ''
end
 
--- Percent-encoding for strings.
--  @function           str.urlencode
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Source string to encode.
--  @param              {string} frame.args.code Encoding type (`QUERY`, `PATH`, `WIKI`).
--  @return             {string} Percent-encoded string.
--  @usage              {{string|urlencode|unencoded string}}
--  @usage              {{string|urlencode|source = unencoded string |code = encoding type }}
function str.urlencode( frame )
    local args = str._getParameters( frame.args, { 'source', 'code' } )
    local code = args['code'] or 'QUERY'
    return mw.uri.encode( args['source'], code )
end
 
--- URL decoding for strings.
--  @function           str.urldecode
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.source Source string to decode.
--  @param              {string} frame.args.code Encoding type (`QUERY`, `PATH`, `WIKI`).
--  @return             {string} Percent-decoded string.
--  @usage              {{string|urldecode|percent-encoded string}}
--  @usage              {{string|urldecode|source = percent-encoded string |code = encoding type }}
function str.urldecode( frame )
    local args = str._getParameters( frame.args, { 'source', 'code' } )
    local code = args['code'] or 'QUERY'
    return mw.uri.decode( args['source'], code )
end
 
--- Replaces characters in a string with HTML entities.
--  @function           str.htmlencode
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.s Source string to encode.
--  @param              {string} frame.args.charset A string as appropriate to go inside brackets in a Ustring pattern.
--  @return             {string} HTML-encoded string.
--  @usage              {{string|htmlencode|unencoded string}}
--  @usage              {{string|htmlencode|s = unencoded string |code = encoding type }}
function str.htmlencode( frame )
    local args = str._getParameters( frame.args, { 's', 'charset' } )
    return mw.text.encode( args['s'], args['charset'] )
end
 
--- Replaces HTML entities in the string with the corresponding characters.
--  @function           str.htmldecode
--  @param              {table} frame Invocation frame object.
--  @param              {table} frame.args Invocation/template arguments.
--  @param              {string} frame.args.s Source string to decode.
--  @param              {string} frame.args.decodeNamedEntities If decodeNamedEntities is omitted or false, the only named entities recognized are '&lt;', '&gt;', '&amp;', and '&quot;'. Otherwise, the list of HTML5 named entities to recognize is loaded from PHP's get_html_translation_table function.
--  @return             {string} Decoded string.
--  @usage              {{string|htmldecode|encoded string}}
--  @usage              {{string|htmldecode|s = encoded string |decodeNamedEntities = something }}
function str.htmldecode( frame )
    local args = str._getParameters( frame.args, { 's', 'decodeNamedEntities' } )
    return mw.text.decode( args['s'], args['decodeNamedEntities'] )
end
 
--- Helper functions.
--  @section            str.utils
 
--- Populates an argument list with both named/unnamed parameters.
--  This is relevant because named parameters are not identical to unnamed
--  parameters due to string trimming, and when dealing with strings we
--  sometimes want to either preserve or remove that whitespace
--  depending on the application.
--  @function           str._getParameters
--  @param              {table} frame_args Table of sequential and named arguments.
--  @param              {table} arg_list Array of parameter names.
--  @return             {table} Map of named arguments corresponding to `arg_list`.
function str._getParameters( frame_args, arg_list )
    local new_args = {}
    local index = 1
    local value
 
    for i,arg in ipairs( arg_list ) do
        value = frame_args[arg]
        if value == nil then
            value = frame_args[index]
            index = index + 1
        end
        new_args[arg] = value
    end
 
    return new_args
end
 
--- Helper function to handle error messages.
--  @function           str._error
--  @param              {string} exception Error string to display to user.
--  @param              {table} frame Current frame object (from string template or module).
--  @return             {string} Optional error message, with or without categorisation.
function str._error( exception, frame )
    -- begin dev wiki change
    local category = frame.args['error_category'] or DEFAULT_CATEGORY
    -- end dev wiki change
    local silent = frame.args['ignore_errors'] or false
    local anonymous = frame.args['no_category'] or false
 
    if str._getBoolean( silent ) then
        return ''
    end
 
    local exception = '<strong class="error">String Module Error: ' .. exception .. '</strong>'
    -- begin dev wiki change
    if type(category) == 'string' and #category > 0 and not str._getBoolean( anonymous ) then
    -- end dev wiki change
        exception = '[[Category:' .. category .. ']]' .. exception
    end
 
    return exception
end
 
--- Helper function to interpret boolean strings.
--  @function           str._getBoolean
--  @param              {string} str Boolean-like wikitext string.
--  @return             {boolean} Boolean value corresponding to `str`.
function str._getBoolean( str )
    return yesno( str, true ) or false
end
 
--- Helper function that escapes all pattern characters.
--  This allows patterns to be treated as plain text.
--  @function           str._escapePattern
--  @param              {string} pattern_str Lua pattern string with special characters.
--  @return             {string} Escaped Lua pattern string for literal string matches.
function str._escapePattern( pattern_str )
    return ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" )
end
 
--- Wrapper function for string template.
--  @function           str.main
--  @usage              {{string|function|...}}
str.main = entrypoint( str )
 
return str
Community content is available under CC-BY-SA unless otherwise noted.

Fandom may earn an affiliate commission on sales made from links on this page.

Stream the best stories.

Fandom may earn an affiliate commission on sales made from links on this page.

Get Disney+