Editing Module:Sandbox/LVLGive feedback
The edit can be undone.
Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
| Latest revision | Your text | ||
| Line 1: | Line 1: | ||
local | -- @Liquipedia | ||
-- page=Module:TabsCombined | |||
-- Combined version of Module:Tabs with all dependencies | |||
local Tabs = {} | |||
--[[ | |||
================================================================= | |||
Start of dependency: Module:Table | |||
================================================================= | |||
]] | |||
local Table = {} | |||
local | |||
function Table.size(tbl) | |||
local i = 0 | |||
for _ in pairs(tbl) do | |||
i = i + 1 | |||
end | |||
return i | |||
end | 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 | 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 | 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 | 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 | end | ||
return | return Tabs | ||