Module:Sandbox/LVL: Difference between revisionsGive feedback
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
-- | -- @Liquipedia | ||
-- | -- page=Module:TabsCombined | ||
-- Combined version of Module:Tabs with all dependencies | |||
local Tabs = {} | |||
--[[ | |||
================================================================= | |||
Start of dependency: Module:Table | |||
================================================================= | |||
]] | |||
local Table = {} | |||
function Table.size(tbl) | |||
local | local i = 0 | ||
for _ in pairs(tbl) do | |||
for | i = i + 1 | ||
end | end | ||
return i | |||
end | end | ||
for _, | function Table.includes(tbl, value, isPattern) | ||
if | for _, entry in pairs(tbl) do | ||
if isPattern and string.find(entry, value) | |||
or not isPattern and entry == value then | |||
return true | |||
end | |||
end | end | ||
return false | return false | ||
end | end | ||
for _, | function Table.getKeyOfValue(tbl, value) | ||
if | 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 | end | ||
return true | return true | ||
end | end | ||
function Table.isNotEmpty(tbl) | |||
return not Table.isEmpty(tbl) | |||
function | end | ||
function Table.copy(tbl) | |||
local result = {} | local result = {} | ||
for | for key, entry in pairs(tbl) do | ||
result[ | result[key] = entry | ||
end | end | ||
return result | return result | ||
end | 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 = {} | 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) | function Logic.readBool(val) | ||
return val == true or val == 'true | return val == 'true' or val == 't' or val == 'yes' or val == 'y' or val == true or val == '1' or val == 1 | ||
end | end | ||
function Logic.readBoolOrNil(val) | function Logic.readBoolOrNil(val) | ||
if val == nil then return | if Logic.readBool(val) then | ||
return Logic. | 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 | end | ||
function | |||
return | 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 | end | ||
function | |||
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 | end | ||
function | |||
function Array.flatMap(elements, funct) | |||
return Array.flatten(Array.map(elements, funct)) | |||
end | end | ||
function Array.all(tbl, predicate) | |||
for _, element in ipairs(tbl) do | |||
function | if not predicate(element) then | ||
return false | |||
return | end | ||
end | end | ||
return true | |||
end | end | ||
function Array.any(tbl, predicate) | |||
for _, element in ipairs(tbl) do | |||
function | if predicate(element) then | ||
return | return true | ||
end | |||
end | |||
return false | |||
end | end | ||
function Array.find(tbl, predicate) | |||
for index, element in ipairs(tbl) do | |||
function | if predicate(element, index) then | ||
return element | |||
end | |||
end | |||
return nil | return nil | ||
end | end | ||
- | function Array.groupBy(tbl, funct) | ||
local | 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 | 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, ...) | |||
local | 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 | |||
end | aggregate = array[1] | ||
end | |||
for index = initialValue ~= nil and 1 or 2, #array do | |||
aggregate = operator(aggregate, array[index]) | |||
end | |||
return aggregate | |||
end | end | ||
function | function Array.maxBy(array, funct, compare) | ||
compare = compare or Array.lexicalCompareIfTable | |||
local | 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 | |||
local | 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 | 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 | end | ||
return 0 | |||
end | |||
local | 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 | 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 | |||
Array. | 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) | ||
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 | ||
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 not | if (options or {}).onlyIfExists == true and (not Page.exists(customLink)) then | ||
return | return nil | ||
end | 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 | |||
return mw. | function Page.pageifyLink(link) | ||
if Logic.isEmpty(link) then | |||
return nil | |||
end | |||
return (mw.ext.TeamLiquidIntegration.resolve_redirect(link):gsub(' ', '_')) | |||
end | end | ||
--[[ | |||
================================================================= | |||
Start of original Module:Tabs code | |||
================================================================= | |||
]] | |||
function Tabs._readArguments(args, options) | function Tabs._readArguments(args, options) | ||
local tabArgs = {} | local tabArgs = {} | ||
| Line 186: | Line 913: | ||
assert(Logic.isNotEmpty(tabArgs), 'You are trying to add a "Tabs" template without arguments for names nor links') | assert(Logic.isNotEmpty(tabArgs), 'You are trying to add a "Tabs" template without arguments for names nor links') | ||
return tabArgs | return tabArgs | ||
end | end | ||
| Line 200: | Line 928: | ||
if not link then return end | if not link then return end | ||
link = link:gsub('_', ' ') | link = link:gsub('_', ' ') | ||
local linkLength = | local linkLength = string.len(link) | ||
local charAfter = | local charAfter = string.sub(fullPageName, linkLength + 1, linkLength + 1) | ||
local pagePartial = | local pagePartial = string.sub(fullPageName, 1, linkLength) | ||
if pagePartial == link and (charAfter == '/' or charAfter == '') and linkLength > maxLinkLength then | if pagePartial == link and (charAfter == '/' or charAfter == '') and linkLength > maxLinkLength then | ||
maxLinkLength = linkLength | maxLinkLength = linkLength | ||
| Line 209: | Line 937: | ||
end) | end) | ||
if this then tabArgs[this].this = true | if not this then return end | ||
tabArgs[this].this = true | |||
end | end | ||
function Tabs._buildContentDiv(hasContent, hybridTabs, noPadding) | function Tabs._buildContentDiv(hasContent, hybridTabs, noPadding) | ||
if hasContent then | if hasContent then | ||
local contentDiv = mw.html.create('div'):addClass('tabs-content') | local contentDiv = mw.html.create('div') | ||
:addClass('tabs-content') | |||
if hybridTabs then | if hybridTabs then | ||
contentDiv:css('border-style', 'none !important'):css('padding', '0 !important') | contentDiv | ||
:css('border-style', 'none !important') | |||
:css('padding', '0 !important') | |||
elseif noPadding then | elseif noPadding then | ||
contentDiv:css('padding', '0 !important') | contentDiv | ||
:css('padding', '0 !important') | |||
end | end | ||
return contentDiv | return contentDiv | ||
| Line 224: | Line 957: | ||
local style = '' | local style = '' | ||
if hybridTabs then style = 'border-style:none !important; padding:0 !important;' | if hybridTabs then | ||
elseif noPadding then style = 'padding:0 !important;' end | style = 'border-style:none !important; padding:0 !important;' | ||
return '\n<div class="tabs-content" style="' .. style .. '">' end | elseif noPadding then | ||
style = 'padding:0 !important;' | |||
end | |||
return '\n<div class="tabs-content" style="' .. style .. '">' | |||
end | |||
function Tabs._single(tab, showHeader) | function Tabs._single(tab, showHeader) | ||
local header | local header | ||
if showHeader then | if showHeader then | ||
header = mw.html.create():tag('h6'):wikitext(tab.name):done():newline() | header = mw.html.create() | ||
:tag('h6'):wikitext(tab.name):done() | |||
:newline() | |||
end | end | ||
return mw.html.create():node(header):node(tab.content) | return mw.html.create() | ||
:node(header) | |||
:node(tab.content) | |||
end | end | ||
function Tabs._getDisplayNameFromLink(link) | function Tabs._getDisplayNameFromLink(link) | ||
local | local linkParts = mw.text.split(link, '/', true) | ||
return | 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 | ||