Permanently protected module

Module:Common

From Touhou Wiki
Jump to navigation Jump to search
Module documentation[view] [edit] [history] [purge]

Add the following code before using any of the functions from the "Common" library.

local common = require("Module:Common")

General notes for all modules:

  • Don't remove functions without discussing it first. A lot of scripts may break if you do so.
  • Before adding any new functions, test it first if there are no syntax errors, so the new fragment of code won't break scripts that are using this library.
  • When modifying existing functions, make sure that the new version is compatible with old one. Any modification may only extend functionality, but can't reduce or change behavior of current version.
  • Creation of global functions and variables is no longer possible. Everything that's going to be exported has to be returned at the end of module.
  • Once again, test it before doing any modifications.


The names are being sorted in alphabetical order.

cv(a, b, c)[edit]

Useful in cases, when we have to choose one of 2 values to pass to same variable or table element.
If a evaluates to true then function will return b, otherwise it'll return c.
Part of code like

local tbl = {}
if condition then
  tbl[#tbl+1] = 'TRUE'
else
  tbl[#tbl+1] = 'FALSE'
end

can be simplified to

local tbl = {}
tbl[#tbl+1] = common.cv(condition, 'TRUE', 'FALSE')


exists(name)[edit]

Checks if given Touhou Wiki page exists. It uses the expensive preprocessor function {{#ifexist:Page name}}. Limit of expensive function calls for Touhou Wiki is currently 100. If that number is exceeded then for next calls exists will return false, whether page exists or not.


common.exists("Touhou Wiki")

will return true, unless the main page is going to be moved or deleted.


common.exists("Module:Common")

will return true or you'll get a nice, red "Script error" (unless you've implemented own exists).

getPage(name)[edit]

Tries to get contents of a given page. No expensive functions are being used.
This function will most probably fail for most of special pages, so it's generally usable for article pages.
If a given page doesn't exist or contents can't be retrieved, it'll return nil.

For example, following code will return the partially preprocessed text for main page:

local main = common.getPage("Touhou Wiki")

Page's content is partially preprocessed, so watch out for strip markers, especially in section headers. These are unique strings that start and end with 'DEL' character (127, 0x7f) and look like ⌦UNIQ3fc7e69e5822d9c0-nowiki-00000001-QINU⌫ (where ⌦ and ⌫ are starting and ending 'DEL' characters).
When retrieving page contents, section header == Documentation == is actually returned as text similar to ==⌦UNIQ3fc7e69e5822d9c0-h-6--QINU⌫ Documentation ==.
Usually it's not visible, because it's stripped from text displayed to users, but it's something to keep in mind when processing page contents in Lua (for example when searching for specific header).

Following code was used to expose the strip markers:

string.gsub(content, "%\127(UNIQ.-QINU)%\127", "⌦%1⌫")


isInTable(tbl, val)[edit]

Checks if given element exists in table and returns it's index. Returns nil if the element can't be found.

isset(val)[edit]

It will return false if passed argument is a nil value (undefined variable) or an empty string, and true in any other case.

partialTableCompare(tbl1, tbl2, s1, s2, n)[edit]

Compares n elements of two numbered tables, tbl1 and tbl2, starting with element s1 in table tbl1 and s2 in table tbl2 and returns true when appropriate elements in this range are equal, false if there's at least one difference.
In case that n=0 it'll return true, because it didn't find any differences.

stripTags(text)[edit]

Removes any valid HTML code from given string. It works only for proper pairs of opening and closing tags (like <div> </div>) or for closed tags (like <br/>).

sliceTable(values, i1, i2)[edit]

"Given a Lua table, extract a sub-array or slice. This function works like string.sub: if the end of the range is not specified, then everything up to the end is assumed; this argument can also be a negative number, where -1 is the end, -2 second-last, etc."

Source: http://snippets.luacode.org/snippets/Table_Slice_116

trunkTable(tbl)[edit]

The given table is sorted (using table.sort) and repeating elements are being removed. It directly modifies the passed table and no value is returned.

--[[
Library for all the functions that are going to be shared by other scripts.
To use all the functions NOT defined as local, add at the beginning of script the following:
require("Module:Common")

The names aren't being sorted, because this could break the function dependencies.
--]]

-- create global object "common" for most of exported functions
local common = {}

-- == constants ==
common.romanizable = {'zh-hans', 'zh-hant', 'zh-cn', 'zh-tw', 'zh-hk', 'zh-sg', 'zh-mo', 'zh-my', 'ja', 'ko', 'vi'}
-- Note: There's a full-width space to space conversion. Don't remove it!
common.normalization_table = {[' '] = ' ', ['~'] = '~', ['!'] = '!', ['?'] = '?'}

-- == helper functions ==
-- checks if string is set and if it's non-empty
function common.isset(target)
  return target ~= nil and target ~= ""
end

--[[ simulates the (a ? b : c) notation of C/C++ and PHP languages.
     Similar to {{#if:{{{a|}}}|{{{b|}}}|{{{c|}}}}} from parser functions. ]]
function common.cv(a, b, c)
  if a then return b else return c end
end

--slices a table to return another table containing values within a certain range
--source: http://snippets.luacode.org/snippets/Table_Slice_116
function common.sliceTable (values,i1,i2)
    local res = {}
    local n = #values
    -- default values for range
    i1 = i1 or 1
    i2 = i2 or n
    if i2 < 0 then
        i2 = n + i2 + 1
    elseif i2 > n then
        i2 = n
    end
    if i1 < 1 or i1 > n then
        return {}
    end
    local k = 1
    for i = i1,i2 do
        res[k] = values[i]
        k = k + 1
    end
    return res
end

-- checks if a given page exists
function common.exists(page)
  if not common.isset(page) then return false end
  return mw.getCurrentFrame():preprocess('{{#ifexist:' .. page .. '|1|0}}') == '1'
end

--[[ Tries to get contents of a page with given name.
     If the function fails it returns nil (page doesn't exist or can't be loaded)
     On success it returns the contents of page (it can be be partially preprocessed, so watch out for parser markers). ]]
function common.getPage(name)
  if not common.isset(name) then return nil end

  local frame = mw.getCurrentFrame()

  -- do a safe call to catch possible errors, like "template doesn't exist"
  local stat,page = pcall(frame.expandTemplate, frame, {title = ':' .. name})

  if not stat then
    -- TODO: 'page' contains the error message. Do some debugging?
    return nil
  end

  return page
end

function common.stripTags(text)
  if common.isset(text) then
    local tmp
    repeat
      tmp = text
      -- a pair of tags, like <td style="">...</td>
      text = string.gsub(text, '<%s*(%w+).->(.-)<%s*/%s*%1%s*>', '%2')
      -- closed tag, like <br/>
      text = string.gsub(text, '<%s*%w+%s*/%s*>', '')
    until tmp == text
  end
  return text
end

--[[ Sort table and remove repeating elements.
     Since tbl is passed as reference, any changes in it will affect the passed table. ]]
function common.trunkTable(tbl)
  table.sort(tbl)
  local last
  local redo
  repeat
    redo = false
    last = nil
    for k,v in pairs(tbl) do
      if v ~= last then
        last = v
      else
        table.remove(tbl, k)
        redo = true
        break
      end
    end
  until not redo
end

--[[ Checks if a given value is among the elements of a table and returns its index.
      Returns nil if it can't find it. ]]
function common.isInTable(tbl, val)
  for k,v in pairs(tbl) do
    if v == val then return k end
  end
  return nil
end

--[[ Compare 'n' elements in two tables, starting from 's1' in first table and 's2' in second table. ]]
function common.partialTableCompare(t1, t2, s1, s2, n)
  if n < 1 then return true end -- basically there's nothing to compare, so no differences were found

  for i = 0,(n-1) do
    -- Note that nil values are also valid.
    if t1[s1+i] ~= t2[s2+i] then return false end
  end

  return true
end

return common