|
|
| Line 1: |
Line 1: |
| local Tabs = {}
| |
|
| |
|
| --[[
| |
| =================================================================
| |
| Start of dependency: Module:Table
| |
| =================================================================
| |
| ]]
| |
| local Table = {}
| |
|
| |
| function Table.size(tbl)
| |
| local i = 0
| |
| for _ in pairs(tbl) do
| |
| i = i + 1
| |
| end
| |
| return i
| |
| end
| |
|
| |
| function Table.includes(tbl, value, isPattern)
| |
| for _, entry in pairs(tbl) do
| |
| if isPattern and string.find(entry, value)
| |
| or not isPattern and entry == value then
| |
| return true
| |
| end
| |
| end
| |
| return false
| |
| end
| |
|
| |
| function Table.getKeyOfValue(tbl, value)
| |
| for key, entry in pairs(tbl) do
| |
| if entry == value then
| |
| return key
| |
| end
| |
| end
| |
| return nil
| |
| end
| |
|
| |
| function Table.filter(tbl, predicate, argument)
| |
| local filteredTbl = {}
| |
| local foundMatches = 1
| |
|
| |
| for _, entry in pairs(tbl) do
| |
| if predicate(entry, argument) then
| |
| filteredTbl[foundMatches] = entry
| |
| foundMatches = foundMatches + 1
| |
| end
| |
| end
| |
|
| |
| return filteredTbl
| |
| end
| |
|
| |
| function Table.filterByKey(tbl, predicate)
| |
| local filteredTbl = {}
| |
|
| |
| for key, entry in pairs(tbl) do
| |
| if predicate(key, entry) then
| |
| filteredTbl[key] = entry
| |
| end
| |
| end
| |
|
| |
| return filteredTbl
| |
| end
| |
|
| |
| function Table.isEmpty(tbl)
| |
| if tbl == nil then
| |
| return true
| |
| end
| |
| for _, _ in pairs(tbl) do
| |
| return false
| |
| end
| |
| return true
| |
| end
| |
|
| |
| function Table.isNotEmpty(tbl)
| |
| return not Table.isEmpty(tbl)
| |
| end
| |
|
| |
| function Table.copy(tbl)
| |
| local result = {}
| |
| for key, entry in pairs(tbl) do
| |
| result[key] = entry
| |
| end
| |
| return result
| |
| end
| |
|
| |
| function Table.deepCopy(tbl_, options)
| |
| options = options or {}
| |
| assert(type(tbl_) == 'table', 'Table.deepCopy: Input must be a table')
| |
|
| |
| local function deepCopy(tbl)
| |
| local result = {}
| |
| for key, value in pairs(tbl) do
| |
| result[key] = type(value) == 'table' and deepCopy(value) or value
| |
| end
| |
| if options.copyMetatable then
| |
| local metatable = getmetatable(tbl)
| |
| if type(metatable) == 'table' then
| |
| setmetatable(result, deepCopy(metatable))
| |
| end
| |
| end
| |
| return result
| |
| end
| |
|
| |
| return deepCopy(tbl_)
| |
| end
| |
|
| |
| function Table.deepEquals(xTable, yTable)
| |
| assert(type(xTable) == 'table', 'Table.deepEquals: First argument must be a table')
| |
| assert(type(yTable) == 'table', 'Table.deepEquals: Second argument must be a table')
| |
|
| |
| for key, value in pairs(xTable) do
| |
| if not Logic.deepEquals(value, yTable[key]) then
| |
| return false
| |
| end
| |
| end
| |
|
| |
| for key, _ in pairs(yTable) do
| |
| if xTable[key] == nil then
| |
| return false
| |
| end
| |
| end
| |
|
| |
| return true
| |
| end
| |
|
| |
| function Table.mergeInto(target, ...)
| |
| local objs = Table.pack(...)
| |
| for i = 1, objs.n do
| |
| if type(objs[i]) == 'table' then
| |
| for key, value in pairs(objs[i]) do
| |
| target[key] = value
| |
| end
| |
| end
| |
| end
| |
| return target
| |
| end
| |
|
| |
| function Table.merge(...)
| |
| return Table.mergeInto({}, ...)
| |
| end
| |
|
| |
| function Table.deepMergeInto(target, ...)
| |
| local tbls = Table.pack(...)
| |
| for i = 1, tbls.n do
| |
| if type(tbls[i]) == 'table' then
| |
| for key, value in pairs(tbls[i]) do
| |
| if type(target[key]) == 'table' and type(value) == 'table' then
| |
| Table.deepMergeInto(target[key], value)
| |
| else
| |
| target[key] = value
| |
| end
| |
| end
| |
| end
| |
| end
| |
| return target
| |
| end
| |
|
| |
| function Table.deepMerge(...)
| |
| return Table.deepMergeInto({}, ...)
| |
| end
| |
|
| |
| function Table.map(xTable, f)
| |
| local yTable = {}
| |
| for xKey, xValue in pairs(xTable) do
| |
| local yKey, yValue = f(xKey, xValue)
| |
| yTable[yKey] = yValue
| |
| end
| |
| return yTable
| |
| end
| |
|
| |
| function Table.mapArgumentsByPrefix(args, prefixes, f, noInterleave)
| |
| local function indexFromKey(key)
| |
| local prefix, index = key:match('^([%a_]+)(%d+)$')
| |
| if Table.includes(prefixes, prefix) then
| |
| return tonumber(index), prefix
| |
| else
| |
| return nil
| |
| end
| |
| end
| |
|
| |
| return Table.mapArguments(args, indexFromKey, f, noInterleave)
| |
| end
| |
|
| |
| function Table.mapArguments(args, indexFromKey, f, noInterleave)
| |
| local entriesByIndex = {}
| |
|
| |
| -- Non-numeric args
| |
| for key, _ in pairs(args) do
| |
| local function post(index, ...)
| |
| if index and not entriesByIndex[index] then
| |
| entriesByIndex[index] = f(key, index, ...)
| |
| end
| |
| end
| |
| if type(key) == 'string' then
| |
| post(indexFromKey(key))
| |
| end
| |
| end
| |
|
| |
| if noInterleave then
| |
| return entriesByIndex
| |
| end
| |
|
| |
| -- Numeric index entries fills in gaps of prefixN= entries if not disabled
| |
| local entryIndex = 1
| |
| for argIndex = 1, math.huge do
| |
| if not args[argIndex] then
| |
| break
| |
| end
| |
| while entriesByIndex[entryIndex] do
| |
| entryIndex = entryIndex + 1
| |
| end
| |
| entriesByIndex[entryIndex] = f(argIndex, entryIndex)
| |
| end
| |
|
| |
| return entriesByIndex
| |
| end
| |
|
| |
| function Table.mapValues(xTable, f)
| |
| local yTable = {}
| |
| for xKey, xValue in pairs(xTable) do
| |
| yTable[xKey] = f(xValue)
| |
| end
| |
| return yTable
| |
| end
| |
|
| |
| function Table.all(tbl, predicate)
| |
| for key, value in pairs(tbl) do
| |
| if not predicate(key, value) then
| |
| return false
| |
| end
| |
| end
| |
| return true
| |
| end
| |
|
| |
| function Table.any(tbl, predicate)
| |
| for key, value in pairs(tbl) do
| |
| if predicate(key, value) then
| |
| return true
| |
| end
| |
| end
| |
| return false
| |
| end
| |
|
| |
| function Table.groupBy(tbl, f)
| |
| local groups = {}
| |
| for key, value in pairs(tbl) do
| |
| local groupKey = f(key, value)
| |
| if not groups[groupKey] then
| |
| groups[groupKey] = {}
| |
| end
| |
| groups[groupKey][key] = value
| |
| end
| |
| return groups
| |
| end
| |
|
| |
| function Table.extract(tbl, key)
| |
| local value = tbl[key]
| |
| tbl[key] = nil
| |
| return value
| |
| end
| |
|
| |
| function Table.getByPathOrNil(tbl, path)
| |
| for _, fieldName in ipairs(path) do
| |
| if type(tbl) ~= 'table' then
| |
| return nil
| |
| end
| |
| tbl = tbl[fieldName]
| |
| end
| |
| return tbl
| |
| end
| |
|
| |
| function Table.setByPath(tbl, path, value)
| |
| for i = 1, #path - 1 do
| |
| if tbl[path[i]] == nil then
| |
| tbl[path[i]] = {}
| |
| end
| |
| tbl = tbl[path[i]]
| |
| end
| |
| tbl[path[#path]] = value
| |
| end
| |
|
| |
| function Table.uniqueKey(tbl)
| |
| local key0 = nil
| |
| for key, _ in pairs(tbl) do
| |
| if key0 ~= nil then return nil end
| |
| key0 = key
| |
| end
| |
| return key0
| |
| end
| |
|
| |
| function Table.entries(tbl)
| |
| local entries = {}
| |
| for key, value in pairs(tbl) do
| |
| table.insert(entries, {key, value})
| |
| end
| |
| return entries
| |
| end
| |
|
| |
| function Table.pack(...)
| |
| return {n = select('#', ...), ...}
| |
| end
| |
|
| |
| Table.iter = {}
| |
|
| |
| function Table.iter.spairs(tbl, order)
| |
| local keys = {}
| |
| for k in pairs(tbl) do keys[#keys+1] = k end
| |
|
| |
| if order then
| |
| table.sort(keys, function(a,b) return order(tbl, a, b) end)
| |
| else
| |
| table.sort(keys)
| |
| end
| |
|
| |
| local i = 0
| |
| return function()
| |
| i = i + 1
| |
| if keys[i] then
| |
| return keys[i], tbl[keys[i]]
| |
| end
| |
| end
| |
| end
| |
|
| |
| function Table.iter.pairsByPrefix(tbl, prefixes, options)
| |
| options = options or {}
| |
| if type(prefixes) == 'string' then
| |
| prefixes = {prefixes}
| |
| end
| |
|
| |
| local getByPrefixes = function(index)
| |
| for _, prefix in ipairs(prefixes) do
| |
| local key = prefix .. index
| |
| if tbl[key] then
| |
| return key, tbl[key]
| |
| end
| |
| end
| |
| end
| |
|
| |
| local i = 1
| |
| return function()
| |
| local key, value = getByPrefixes(i)
| |
| if options.requireIndex == false and i == 1 and not value then
| |
| key, value = getByPrefixes('')
| |
| end
| |
| i = i + 1
| |
| if value then
| |
| return key, value, (i - 1)
| |
| else
| |
| return nil
| |
| end
| |
| end
| |
| end
| |
|
| |
| --[[
| |
| =================================================================
| |
| Start of dependency: Module:Logic
| |
| =================================================================
| |
| ]]
| |
| local Logic = {}
| |
|
| |
| function Logic.emptyOr(val1, val2, default)
| |
| if not Logic.isEmpty(val1) then
| |
| return val1
| |
| elseif not Logic.isEmpty(val2) then
| |
| return val2
| |
| else
| |
| return default
| |
| end
| |
| end
| |
|
| |
| function Logic.nilOr(...)
| |
| local args = Table.pack(...)
| |
| for i = 1, args.n do
| |
| local arg = args[i]
| |
| local val
| |
| if type(arg) == 'function' then
| |
| val = arg()
| |
| else
| |
| val = arg
| |
| end
| |
| if val ~= nil then
| |
| return val
| |
| end
| |
| end
| |
| return nil
| |
| end
| |
|
| |
| function Logic.isEmpty(val)
| |
| if type(val) == 'table' then
| |
| return Table.isEmpty(val)
| |
| else
| |
| return val == '' or val == nil
| |
| end
| |
| end
| |
|
| |
| function Logic.isNotEmpty(val)
| |
| if type(val) == 'table' then
| |
| return Table.isNotEmpty(val)
| |
| else
| |
| return val ~= nil and val ~= ''
| |
| end
| |
| end
| |
|
| |
| function Logic.nilIfEmpty(val)
| |
| return Logic.isNotEmpty(val) and val or nil
| |
| end
| |
|
| |
| function Logic.isDeepEmpty(val)
| |
| return Logic.isEmpty(val) or type(val) == 'table' and
| |
| Table.all(val, function(key, item) return Logic.isDeepEmpty(item) end)
| |
| end
| |
|
| |
| function Logic.isNotDeepEmpty(val)
| |
| return not Logic.isDeepEmpty(val)
| |
| end
| |
|
| |
| function Logic.readBool(val)
| |
| return val == 'true' or val == 't' or val == 'yes' or val == 'y' or val == true or val == '1' or val == 1
| |
| end
| |
|
| |
| function Logic.readBoolOrNil(val)
| |
| if Logic.readBool(val) then
| |
| return true
| |
| elseif val == 'false' or val == 'f' or val == 'no' or val == 'n' or val == false or val == '0' or val == 0 then
| |
| return false
| |
| else
| |
| return nil
| |
| end
| |
| end
| |
|
| |
| function Logic.nilThrows(val)
| |
| if val == nil then
| |
| error('Unexpected nil', 2)
| |
| end
| |
| return val
| |
| end
| |
|
| |
| function Logic.tryCatch(try, catch)
| |
| local ran, result = pcall(try)
| |
| if not ran then
| |
| catch(result)
| |
| else
| |
| return result
| |
| end
| |
| end
| |
|
| |
| function Logic.deepEquals(x, y)
| |
| if x == y then
| |
| return true
| |
| elseif type(x) == 'table' and type(y) == 'table' then
| |
| return Table.deepEquals(x, y)
| |
| else
| |
| return false
| |
| end
| |
| end
| |
|
| |
| --[[
| |
| =================================================================
| |
| Start of dependency: Module:Array
| |
| =================================================================
| |
| ]]
| |
| local Array = {}
| |
|
| |
| function Array.randomize(tbl)
| |
| math.randomseed(os.time())
| |
| for i = #tbl, 2, -1 do
| |
| local j = math.random(i)
| |
| tbl[i], tbl[j] = tbl[j], tbl[i]
| |
| end
| |
| return tbl
| |
| end
| |
|
| |
| function Array.isArray(tbl)
| |
| return type(tbl) == 'table' and Table.size(tbl) == #tbl
| |
| end
| |
|
| |
| function Array.copy(tbl)
| |
| local copy = {}
| |
| for _, element in ipairs(tbl) do
| |
| table.insert(copy, element)
| |
| end
| |
| return copy
| |
| end
| |
|
| |
| function Array.sub(tbl, startIndex, endIndex)
| |
| if startIndex < 0 then startIndex = #tbl + 1 + startIndex end
| |
| if not endIndex then endIndex = #tbl end
| |
| if endIndex < 0 then endIndex = #tbl + 1 + endIndex end
| |
|
| |
| local subArray = {}
| |
| for index = startIndex, endIndex do
| |
| table.insert(subArray, tbl[index])
| |
| end
| |
| return subArray
| |
| end
| |
|
| |
| function Array.map(elements, funct)
| |
| local mappedArray = {}
| |
| for index, element in ipairs(elements) do
| |
| local mappedElement = funct(element, index)
| |
| table.insert(mappedArray, mappedElement)
| |
| end
| |
| return mappedArray
| |
| end
| |
|
| |
| function Array.filter(tbl, predicate)
| |
| local filteredArray = {}
| |
| for index, element in ipairs(tbl) do
| |
| if predicate(element, index) then
| |
| table.insert(filteredArray, element)
| |
| end
| |
| end
| |
| return filteredArray
| |
| end
| |
|
| |
| function Array.flatten(tbl)
| |
| local flattenedArray = {}
| |
| for _, x in ipairs(tbl) do
| |
| if type(x) == 'table' then
| |
| for _, y in ipairs(x) do
| |
| table.insert(flattenedArray, y)
| |
| end
| |
| else
| |
| table.insert(flattenedArray, x)
| |
| end
| |
| end
| |
| return flattenedArray
| |
| end
| |
|
| |
| function Array.flatMap(elements, funct)
| |
| return Array.flatten(Array.map(elements, funct))
| |
| end
| |
|
| |
| function Array.all(tbl, predicate)
| |
| for _, element in ipairs(tbl) do
| |
| if not predicate(element) then
| |
| return false
| |
| end
| |
| end
| |
| return true
| |
| end
| |
|
| |
| function Array.any(tbl, predicate)
| |
| for _, element in ipairs(tbl) do
| |
| if predicate(element) then
| |
| return true
| |
| end
| |
| end
| |
| return false
| |
| end
| |
|
| |
| function Array.find(tbl, predicate)
| |
| for index, element in ipairs(tbl) do
| |
| if predicate(element, index) then
| |
| return element
| |
| end
| |
| end
| |
| return nil
| |
| end
| |
|
| |
| function Array.groupBy(tbl, funct)
| |
| local groupsByKey = {}
| |
| local groups = {}
| |
| for _, xValue in ipairs(tbl) do
| |
| local yValue = funct(xValue)
| |
| if yValue then
| |
| local group = groupsByKey[yValue]
| |
| if not group then
| |
| group = {}
| |
| groupsByKey[yValue] = group
| |
| table.insert(groups, group)
| |
| end
| |
| table.insert(group, xValue)
| |
| end
| |
| end
| |
| return groups, groupsByKey
| |
| end
| |
|
| |
| function Array.groupAdjacentBy(array, f, equals)
| |
| equals = equals or Logic.deepEquals
| |
| local groups = {}
| |
| local currentKey
| |
| for index, elem in ipairs(array) do
| |
| local key = f(elem)
| |
| if index == 1 or not equals(key, currentKey) then
| |
| currentKey = key
| |
| table.insert(groups, {})
| |
| end
| |
| table.insert(groups[#groups], elem)
| |
| end
| |
| return groups
| |
| end
| |
|
| |
| function Array.lexicalCompare(tblX, tblY)
| |
| for index = 1, math.min(#tblX, #tblY) do
| |
| if tblX[index] < tblY[index] then
| |
| return true
| |
| elseif tblX[index] > tblY[index] then
| |
| return false
| |
| end
| |
| end
| |
| return #tblX < #tblY
| |
| end
| |
|
| |
| function Array.lexicalCompareIfTable(y1, y2)
| |
| if type(y1) == 'table' and type(y2) == 'table' then
| |
| return Array.lexicalCompare(y1, y2)
| |
| else
| |
| return y1 < y2
| |
| end
| |
| end
| |
|
| |
| function Array.sortBy(tbl, funct, compare)
| |
| local copy = Table.copy(tbl)
| |
| Array.sortInPlaceBy(copy, funct, compare)
| |
| return copy
| |
| end
| |
|
| |
| function Array.sortInPlaceBy(tbl, funct, compare)
| |
| compare = compare or Array.lexicalCompareIfTable
| |
| table.sort(tbl, function(x1, x2) return compare(funct(x1), funct(x2)) end)
| |
| end
| |
|
| |
| function Array.reverse(tbl)
| |
| local reversedArray = {}
| |
| for index = #tbl, 1, -1 do
| |
| table.insert(reversedArray, tbl[index])
| |
| end
| |
| return reversedArray
| |
| end
| |
|
| |
| function Array.append(tbl, ...)
| |
| return Array.appendWith(Array.copy(tbl), ...)
| |
| end
| |
|
| |
| function Array.appendWith(tbl, ...)
| |
| local elements = Table.pack(...)
| |
| for index = 1, elements.n do
| |
| if elements[index] ~= nil then
| |
| table.insert(tbl, elements[index])
| |
| end
| |
| end
| |
| return tbl
| |
| end
| |
|
| |
| function Array.extend(tbl, ...)
| |
| return Array.extendWith({}, tbl, ...)
| |
| end
| |
|
| |
| function Array.extendWith(tbl, ...)
| |
| local arrays = Table.pack(...)
| |
| for index = 1, arrays.n do
| |
| if type(arrays[index]) == 'table' then
| |
| for _, element in ipairs(arrays[index]) do
| |
| table.insert(tbl, element)
| |
| end
| |
| elseif arrays[index] ~= nil then
| |
| table.insert(tbl, arrays[index])
| |
| end
| |
| end
| |
| return tbl
| |
| end
| |
|
| |
| function Array.mapIndexes(funct)
| |
| local arr = {}
| |
| for index = 1, math.huge do
| |
| local y = funct(index)
| |
| if y then
| |
| table.insert(arr, y)
| |
| else
| |
| break
| |
| end
| |
| end
| |
| return arr
| |
| end
| |
|
| |
| function Array.range(from, to)
| |
| local elements = {}
| |
| for element = from, to do
| |
| table.insert(elements, element)
| |
| end
| |
| return elements
| |
| end
| |
|
| |
| function Array.extractKeys(tbl, iterator, ...)
| |
| iterator = iterator or pairs
| |
| local array = {}
| |
| for key, _ in iterator(tbl, ...) do
| |
| table.insert(array, key)
| |
| end
| |
| return array
| |
| end
| |
|
| |
| function Array.extractValues(tbl, iterator, ...)
| |
| iterator = iterator or pairs
| |
| local array = {}
| |
| for _, item in iterator(tbl, ...) do
| |
| table.insert(array, item)
| |
| end
| |
| return array
| |
| end
| |
|
| |
| function Array.forEach(elements, funct)
| |
| for index, element in ipairs(elements) do
| |
| funct(element, index)
| |
| end
| |
| end
| |
|
| |
| function Array.reduce(array, operator, initialValue)
| |
| local aggregate
| |
| if initialValue ~= nil then
| |
| aggregate = initialValue
| |
| else
| |
| aggregate = array[1]
| |
| end
| |
|
| |
| for index = initialValue ~= nil and 1 or 2, #array do
| |
| aggregate = operator(aggregate, array[index])
| |
| end
| |
| return aggregate
| |
| end
| |
|
| |
| function Array.maxBy(array, funct, compare)
| |
| compare = compare or Array.lexicalCompareIfTable
| |
| local max, maxScore
| |
| for _, item in ipairs(array) do
| |
| local score = funct(item)
| |
| if max == nil or compare(maxScore, score) then
| |
| max = item
| |
| maxScore = score
| |
| end
| |
| end
| |
| return max
| |
| end
| |
|
| |
| function Array.max(array, compare)
| |
| return Array.maxBy(array, function(x) return x end, compare)
| |
| end
| |
|
| |
| function Array.minBy(array, funct, compare)
| |
| compare = compare or Array.lexicalCompareIfTable
| |
| local min, minScore
| |
| for _, item in ipairs(array) do
| |
| local score = funct(item)
| |
| if min == nil or compare(score, minScore) then
| |
| min = item
| |
| minScore = score
| |
| end
| |
| end
| |
| return min
| |
| end
| |
|
| |
| function Array.min(array, compare)
| |
| return Array.minBy(array, function(x) return x end, compare)
| |
| end
| |
|
| |
| function Array.indexOf(array, pred)
| |
| for ix, elem in ipairs(array) do
| |
| if pred(elem, ix) then
| |
| return ix
| |
| end
| |
| end
| |
| return 0
| |
| end
| |
|
| |
| function Array.unique(elements)
| |
| local elementCache = {}
| |
| local uniqueElements = {}
| |
| for _, element in ipairs(elements) do
| |
| if elementCache[element] == nil then
| |
| table.insert(uniqueElements, element)
| |
| elementCache[element] = true
| |
| end
| |
| end
| |
| return uniqueElements
| |
| end
| |
|
| |
| function Array.parseCommaSeparatedString(inputString, sep)
| |
| if Logic.isEmpty(inputString) then return {} end
| |
| return Array.map(mw.text.split(inputString, sep or ','), mw.text.trim)
| |
| end
| |
|
| |
| function Array.interleave(elements, x)
| |
| local size = #elements
| |
| return Array.flatMap(elements, function(element, index)
| |
| if index == size then
| |
| return {element}
| |
| end
| |
| return {element, x}
| |
| end)
| |
| end
| |
|
| |
| --[[
| |
| =================================================================
| |
| Start of dependency: Module:Operator
| |
| =================================================================
| |
| ]]
| |
| local Operator = {}
| |
|
| |
| function Operator.add(a, b) return a + b end
| |
| function Operator.sub(a, b) return a - b end
| |
| function Operator.mul(a, b) return a * b end
| |
| function Operator.div(a, b) return a / b end
| |
| function Operator.pow(a, b) return a ^ b end
| |
| function Operator.eq(a, b) return a == b end
| |
| function Operator.neq(a, b) return a ~= b end
| |
| function Operator.lt(a, b) return a < b end
| |
| function Operator.le(a, b) return a <= b end
| |
| function Operator.gt(a, b) return a > b end
| |
| function Operator.ge(a, b) return a >= b end
| |
|
| |
| function Operator.property(item)
| |
| assert(type(item) == 'string' or type(item) == 'number', 'Invalid or missing input to `Operator.property`')
| |
| local pathSegments = mw.text.split(item, '.', true)
| |
| return function(tbl)
| |
| local selected = tbl
| |
| for segmentIndex, pathSegment in ipairs(pathSegments) do
| |
| if type(selected) ~= 'table' and segmentIndex == 1 then
| |
| error('Nil supplied to `Operator.property(' .. item .. ')`')
| |
| elseif type(selected) ~= 'table' then
| |
| local pathUntilHere = Array.sub(pathSegments, 1, segmentIndex - 1)
| |
| error('Could not index "tbl.' .. table.concat(pathUntilHere, '.') .. '"')
| |
| end
| |
| selected = selected[pathSegment] or selected[tonumber(pathSegment)]
| |
| end
| |
| return selected
| |
| end
| |
| end
| |
|
| |
| function Operator.method(funcName, ...)
| |
| local args = {...}
| |
| return function(obj)
| |
| return obj[funcName](obj, unpack(args))
| |
| end
| |
| end
| |
|
| |
| --[[
| |
| =================================================================
| |
| Start of dependency: Module:Page
| |
| =================================================================
| |
| ]]
| |
| local Page = {}
| |
|
| |
| function Page.exists(link)
| |
| local existingPage = mw.title.new(link)
| |
| if existingPage == nil then
| |
| return false
| |
| end
| |
| return existingPage.exists
| |
| end
| |
|
| |
| function Page.makeInternalLink(options, display, customLink)
| |
| if type(options) == 'string' then
| |
| customLink = display
| |
| display = options
| |
| end
| |
| if Logic.isEmpty(display) then
| |
| return nil
| |
| elseif Logic.isEmpty(customLink) then
| |
| customLink = display
| |
| end
| |
|
| |
| if (options or {}).onlyIfExists == true and (not Page.exists(customLink)) then
| |
| return nil
| |
| end
| |
|
| |
| return '[[' .. customLink .. '|' .. display .. ']]'
| |
| end
| |
|
| |
| function Page.makeExternalLink(display, link)
| |
| if Logic.isEmpty(display) or Logic.isEmpty(link) then
| |
| return nil
| |
| end
| |
| return '[' .. link .. ' ' .. display .. ']'
| |
| end
| |
|
| |
| function Page.pageifyLink(link)
| |
| if Logic.isEmpty(link) then
| |
| return nil
| |
| end
| |
| return (mw.ext.TeamLiquidIntegration.resolve_redirect(link):gsub(' ', '_'))
| |
| end
| |
|
| |
| --[[
| |
| =================================================================
| |
| Start of original Module:Tabs code
| |
| =================================================================
| |
| ]]
| |
| function Tabs._readArguments(args, options)
| |
| local tabArgs = {}
| |
| local tabIndex = 1
| |
| local this = tonumber(args.This)
| |
| local this2 = tonumber(args.This2)
| |
|
| |
| while args['name' .. tabIndex] or args['link' .. tabIndex] do
| |
| if args['content' .. tabIndex] or not options.removeEmptyTabs then
| |
| table.insert(tabArgs, {
| |
| name = Table.extract(args, 'name' .. tabIndex),
| |
| link = Table.extract(args, 'link' .. tabIndex),
| |
| content = Table.extract(args, 'content' .. tabIndex),
| |
| tabs = Table.extract(args, 'tabs' .. tabIndex),
| |
| this = this == tabIndex or (options.allowThis2 and this2 == tabIndex),
| |
| })
| |
| end
| |
| tabIndex = tabIndex + 1
| |
| end
| |
|
| |
| if Logic.readBool(args.returnIfEmpty) then
| |
| return tabArgs
| |
| end
| |
|
| |
| assert(Logic.isNotEmpty(tabArgs), 'You are trying to add a "Tabs" template without arguments for names nor links')
| |
|
| |
| return tabArgs
| |
| end
| |
|
| |
| function Tabs._setThis(tabArgs)
| |
| if Array.any(tabArgs, Operator.property('this')) then return end
| |
|
| |
| local fullPageName = mw.title.getCurrentTitle().prefixedText
| |
| local this
| |
| local maxLinkLength = -1
| |
|
| |
| Array.forEach(tabArgs, function (tab, tabIndex)
| |
| local link = tab.link
| |
| if not link then return end
| |
| link = link:gsub('_', ' ')
| |
| local linkLength = string.len(link)
| |
| local charAfter = string.sub(fullPageName, linkLength + 1, linkLength + 1)
| |
| local pagePartial = string.sub(fullPageName, 1, linkLength)
| |
| if pagePartial == link and (charAfter == '/' or charAfter == '') and linkLength > maxLinkLength then
| |
| maxLinkLength = linkLength
| |
| this = tabIndex
| |
| end
| |
| end)
| |
|
| |
| if not this then return end
| |
| tabArgs[this].this = true
| |
| end
| |
|
| |
| function Tabs._buildContentDiv(hasContent, hybridTabs, noPadding)
| |
| if hasContent then
| |
| local contentDiv = mw.html.create('div')
| |
| :addClass('tabs-content')
| |
| if hybridTabs then
| |
| contentDiv
| |
| :css('border-style', 'none !important')
| |
| :css('padding', '0 !important')
| |
| elseif noPadding then
| |
| contentDiv
| |
| :css('padding', '0 !important')
| |
| end
| |
| return contentDiv
| |
| end
| |
|
| |
| local style = ''
| |
| if hybridTabs then
| |
| style = 'border-style:none !important; padding:0 !important;'
| |
| elseif noPadding then
| |
| style = 'padding:0 !important;'
| |
| end
| |
| return '\n<div class="tabs-content" style="' .. style .. '">'
| |
| end
| |
|
| |
| function Tabs._single(tab, showHeader)
| |
| local header
| |
| if showHeader then
| |
| header = mw.html.create()
| |
| :tag('h6'):wikitext(tab.name):done()
| |
| :newline()
| |
| end
| |
| return mw.html.create()
| |
| :node(header)
| |
| :node(tab.content)
| |
| end
| |
|
| |
| function Tabs._getDisplayNameFromLink(link)
| |
| local linkParts = mw.text.split(link, '/', true)
| |
| return linkParts[#linkParts]
| |
| end
| |
|
| |
| function Tabs.static(args)
| |
| args = args or {}
| |
|
| |
| local tabArgs = Tabs._readArguments(args, {allowThis2 = true})
| |
| local tabCount = #tabArgs
| |
| if tabCount == 0 then return end
| |
|
| |
| Tabs._setThis(tabArgs)
| |
|
| |
| local tabs = mw.html.create('ul')
| |
| :attr('class', 'nav nav-tabs navigation-not-searchable tabs tabs' .. tabCount)
| |
| :attr('data-nosnippet')
| |
|
| |
| local subTabs = mw.html.create()
| |
|
| |
| Array.forEach(tabArgs, function(tab)
| |
| local name = tab.name or Tabs._getDisplayNameFromLink(tab.link)
| |
| local text = tab.link and Page.makeInternalLink({}, name, tab.link) or tab.name
| |
| tabs:tag('li'):addClass(tab.this and 'active' or nil):wikitext(text)
| |
| subTabs:node(tab.this and tab.tabs or nil)
| |
| end)
| |
|
| |
| return mw.html.create()
| |
| :tag('div')
| |
| :addClass('tabs-static')
| |
| :attr('data-nosnippet', '')
| |
| :node(tabs)
| |
| :done()
| |
| :node(subTabs)
| |
| end
| |
|
| |
| function Tabs.dynamic(args)
| |
| args = args or {}
| |
|
| |
| local tabArgs = Tabs._readArguments(args, {removeEmptyTabs = Logic.readBool(args.removeEmptyTabs)})
| |
| local tabCount = #tabArgs
| |
| if tabCount == 0 then return end
| |
|
| |
| local hasContent = Array.all(tabArgs, function(tab)
| |
| return Logic.isNotEmpty(tab.content) end)
| |
| local allEmpty = Array.all(tabArgs, function(tab)
| |
| return Logic.isEmpty(tab.content) end)
| |
| assert(hasContent or allEmpty, 'Some of the tabs have contents while others do not')
| |
|
| |
| local isSingular = tabCount == 1 and hasContent
| |
| if isSingular and not Logic.readBool(args.showSingularAsTab) then
| |
| return Tabs._single(tabArgs[1], not Logic.readBool(args.suppressHeader))
| |
| end
| |
|
| |
| local tabs = mw.html.create('ul')
| |
| :addClass('nav nav-tabs tabs tabs' .. tabCount)
| |
|
| |
| if not Array.any(tabArgs, Operator.property('this')) then
| |
| tabArgs[1].this = true
| |
| end
| |
|
| |
| local build = function(obj, elementType, content, class, isActive)
| |
| local element = mw.html.create(elementType)
| |
| :addClass(class)
| |
| :addClass(isActive and 'active' or nil)
| |
| :newline()
| |
| :node(content)
| |
|
| |
| obj:newline():node(element)
| |
| end
| |
|
| |
| Array.forEach(tabArgs, function(tabData, tabIndex)
| |
| build(tabs, 'li', tabData.name, 'tab' .. tabIndex, tabData.this)
| |
| end)
| |
|
| |
| if not Logic.nilOr(Logic.readBoolOrNil(args['hide-showall']), isSingular) then
| |
| tabs:tag('li')
| |
| :addClass('show-all')
| |
| :wikitext('Show All')
| |
| end
| |
|
| |
| tabs:newline()
| |
|
| |
| local contents = Tabs._buildContentDiv(
| |
| hasContent,
| |
| Logic.readBool(args['hybrid-tabs']),
| |
| Logic.readBool(args['no-padding'])
| |
| )
| |
|
| |
| if not hasContent then
| |
| return '<div class="tabs-dynamic navigation-not-searchable" data-nosnippet>\n'
| |
| .. tostring(tabs) .. contents
| |
| end
| |
|
| |
| Array.forEach(tabArgs, function(tabData, tabIndex)
| |
| build(contents, 'div', tabData.content, 'content' .. tabIndex, tabData.this)
| |
| end)
| |
|
| |
| return mw.html.create('div')
| |
| :addClass('tabs-dynamic navigation-not-searchable')
| |
| :attr('data-nosnippet')
| |
| :node(tabs)
| |
| :newline()
| |
| :node(contents)
| |
| end
| |
|
| |
| return Tabs
| |