| 🌟 | 現在、 鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。 |
「モジュール:Utility/TableUtil」の版間の差分
ナビゲーションに移動
検索に移動
([LineCell] 最大値を固定化する機能の実装) |
細 ([LineCell] 参照するオブジェクトのミスを修正) |
||
| (同じ利用者による、間の14版が非表示) | |||
| 146行目: | 146行目: | ||
-- [Function] Render | -- [Function] Render | ||
function __TableUtil__ValueCell:render(row, dataset, i | function __TableUtil__ValueCell:render(row, dataset, i) | ||
local value = dataset[self.dataIndex] | local value = dataset[self.dataIndex] | ||
local formattedValue = self.format(value) | local formattedValue = self.format(value) | ||
local cell = self:__cell__render(row, dataset, i | local cell = self:__cell__render(row, dataset, i) | ||
:attr('data-sort-value', value) | :attr('data-sort-value', value) | ||
:wikitext(formattedValue) | :wikitext(formattedValue) | ||
| 195行目: | 195行目: | ||
end | end | ||
local cell = row:tag('th') | local cell = row:tag('th') | ||
:attr('data-sort-value', | :attr('data-sort-value', self.currentRank) | ||
:wikitext(self.currentRank) | :wikitext(self.currentRank) | ||
return cell | return cell | ||
| 214行目: | 214行目: | ||
-- [Utils] | -- [Utils] | ||
local function multipleGradient(colorStop1, colorStop2 | local function multipleGradient(colorStop1, colorStop2) | ||
local | local gradientFormat = string.format('linear-gradient(to right,transparent %%s%%%%,%s %%s%%%%,%s %%s%%%%,transparent %%s%%%%)', colorStop1, colorStop2) | ||
return function(minValue, maxValue) | return function(minValue, maxValue) | ||
if minValue == maxValue then | if minValue == maxValue then | ||
| 234行目: | 229行目: | ||
end | end | ||
local function singleGradient(colorStop1, colorStop2 | local function singleGradient(colorStop1, colorStop2) | ||
local gradientFormat | local gradientFormat | ||
if colorStop1 == colorStop2 then | if colorStop1 == colorStop2 then | ||
gradientFormat = string.format('linear-gradient(to right,%s %%s%%%%,transparent %%s%%%%)', colorStop1) | |||
else | else | ||
gradientFormat = string.format('linear-gradient(to right,%s,%s %%s%%%%,transparent %%s%%%%)', colorStop1, colorStop2) | |||
end | end | ||
return function(value) | return function(value) | ||
| 262行目: | 249行目: | ||
obj.dataIndex = dataIndex | obj.dataIndex = dataIndex | ||
if type(opts) == 'table' then | if type(opts) == 'table' then | ||
obj.itemName = opts.itemName or 'Item 1' | |||
obj.startIndex = opts.startIndex or dataIndex | obj.startIndex = opts.startIndex or dataIndex | ||
obj.endIndex = opts.endIndex or dataIndex | obj.endIndex = opts.endIndex or dataIndex | ||
obj.colorStops = opts.colorStops or '# | obj.colorStops = opts.colorStops or '#A7D7F9' | ||
obj.hoverColor = opts.hoverColor or '#5FB6F4' | |||
obj._hasSubGraph = opts.subStartIndex and opts.subEndIndex | |||
if obj._hasSubGraph then | |||
obj.height = opts.height or 1.0 | |||
obj.subItemName = opts.subItemName or 'Item 2' | |||
obj.subStartIndex = opts.subStartIndex or opts.subDataIndex | |||
obj.subEndIndex = opts.subEndIndex or opts.subDataIndex | |||
obj.subColorStops = opts.subColorStops or '#F9C9A7' | |||
obj.subHoverColor = opts.subHoverColor or '#F49D5F' | |||
obj.subHeight = opts.subHeight or 0.4 | |||
else | |||
obj.height = opts.height or 1.2 | |||
end | |||
if opts.gridEnabled then | if opts.gridEnabled then | ||
obj.gridEnabled = opts.gridEnabled | obj.gridEnabled = opts.gridEnabled | ||
| 270行目: | 271行目: | ||
obj.gridEnabled = true | obj.gridEnabled = true | ||
end | end | ||
obj. | obj.hoverEnabled = opts.hoverEnabled or false | ||
obj. | obj.hoverFormat = opts.hoverFormat or __passthrough | ||
obj.minimumRate = opts.minimumRate or 0.95 | |||
obj.maximumValue = opts.maximumValue or math.huge | obj.maximumValue = opts.maximumValue or math.huge | ||
obj.maximumRate = opts.maximumRate or 1.05 | obj.maximumRate = opts.maximumRate or 1.05 | ||
obj.preferredGridCount = opts.preferredGridCount or 4 | obj.preferredGridCount = opts.preferredGridCount or 4 | ||
obj.scale = opts.scale or scale.LinearScale.new():setClamp(true) | |||
if obj.scale.typename ~= scale.LinearScale.__typename then | |||
obj.minimumValue = opts.minimumValue or -math.huge | |||
else | |||
obj.minimumValue = opts.minimumValue or 0 | |||
end | |||
obj.ticksFormat = opts.ticksFormat or __passthrough | |||
else | else | ||
obj.itemName = 'Item 1' | |||
obj.startIndex = dataIndex | obj.startIndex = dataIndex | ||
obj.endIndex = dataIndex | obj.endIndex = dataIndex | ||
obj.colorStops = '#A7D7F9' | obj.colorStops = '#A7D7F9' | ||
obj.hoverColor = '#5FB6F4' | |||
obj._hasSubGraph = false | |||
obj.subColorStops = '#F9C9A7' | |||
obj.subHoverColor = '#F49D5F' | |||
obj.gridEnabled = true | obj.gridEnabled = true | ||
obj.height = 1.2 | obj.height = 1.2 | ||
obj.hoverEnabled = false | |||
obj.hoverFormat = __passthrough | |||
obj.minimumRate = 0.95 | |||
obj.minimumValue = 0 | obj.minimumValue = 0 | ||
obj.maximumValue = math.huge | obj.maximumValue = math.huge | ||
obj.maximumRate = 1.05 | obj.maximumRate = 1.05 | ||
obj.preferredGridCount = 4 | obj.preferredGridCount = 4 | ||
obj.scale = scale.LinearScale.new():setClamp(true) | |||
obj.ticksFormat = __passthrough | |||
end | end | ||
obj._gridCount = 2 | obj._gridCount = 2 | ||
| 301行目: | 320行目: | ||
-- [Function] Reset context | -- [Function] Reset context | ||
function __TableUtil__LineCell:resetContext(datainfo) | function __TableUtil__LineCell:resetContext(datainfo) | ||
local | local startValue, stopValue | ||
if self.minimumValue == -math.huge then | |||
local minValue = datainfo[self.startIndex].minValue | |||
if self._hasSubGraph then | |||
local subMinValue = datainfo[self.subStartIndex].minValue | |||
if minValue < subMinValue then | |||
minValue = subMinValue | |||
end | |||
end | |||
startValue = self.minimumRate * minValue | |||
else | |||
startValue = self.minimumValue | |||
end | |||
if self.maximumValue == math.huge then | if self.maximumValue == math.huge then | ||
local maxValue = datainfo[self.endIndex].maxValue | |||
if self._hasSubGraph then | |||
local subMaxValue = datainfo[self.subEndIndex].maxValue | |||
if maxValue < subMaxValue then | |||
maxValue = subMaxValue | |||
end | |||
end | |||
stopValue = self.maximumRate * maxValue | |||
else | else | ||
stopValue = self.maximumValue | |||
end | end | ||
self. | self.scale | ||
:setDomain({ startValue, stopValue }) | |||
:nice(self.preferredGridCount) | |||
if self.gridEnabled then | if self.gridEnabled then | ||
self._ticks = scale:ticks(self.preferredGridCount) | self._ticks = self.scale:ticks(self.preferredGridCount) | ||
end | end | ||
if self.startIndex == self.endIndex then | if self.startIndex == self.endIndex then | ||
if type(self.colorStops) == 'table' then | if type(self.colorStops) == 'table' then | ||
self._gradientFormat = singleGradient(self.colorStops[1], self.colorStops[2] | self._gradientFormat = singleGradient(self.colorStops[1], self.colorStops[2]) | ||
else | else | ||
self._gradientFormat = singleGradient(self.colorStops, self.colorStops | self._gradientFormat = singleGradient(self.colorStops, self.colorStops) | ||
end | end | ||
else | else | ||
if type(self.colorStops) == 'table' then | if type(self.colorStops) == 'table' then | ||
self._gradientFormat = multipleGradient(self.colorStops[1], self.colorStops[2] | self._gradientFormat = multipleGradient(self.colorStops[1], self.colorStops[2]) | ||
else | else | ||
self._gradientFormat = multipleGradient(self.colorStops, self.colorStops | self._gradientFormat = multipleGradient(self.colorStops, self.colorStops) | ||
end | end | ||
end | |||
if self._hasSubGraph then | |||
if self.subStartIndex == self.subEndIndex then | |||
if type(self.subColorStops) == 'table' then | |||
self._subGradientFormat = singleGradient(self.subColorStops[1], self.subColorStops[2]) | |||
else | |||
self._subGradientFormat = singleGradient(self.subColorStops, self.subColorStops) | |||
end | |||
else | |||
if type(self.subColorStops) == 'table' then | |||
self._subGradientFormat = multipleGradient(self.subColorStops[1], self.subColorStops[2]) | |||
else | |||
self._subGradientFormat = multipleGradient(self.subColorStops, self.subColorStops) | |||
end | |||
end | |||
if self.gridEnabled then | |||
if self.scale.typename == scale.LogScale.__typename then | |||
local ticksCount = #self._ticks + 1 | |||
self._classes = string.format('st-grid st-grid-%d st-grid-log%d st-line st-line-h%d-h%d', ticksCount, ticksCount, 10 * self.height, 10 * self.subHeight) | |||
else | |||
self._classes = string.format('st-grid st-grid-%d st-line st-line-h%d-h%d', #self._ticks + 1, 10 * self.height, 10 * self.subHeight) | |||
end | |||
else | |||
self._classes = string.format('st-line st-line-h%d-h%d', 10 * self.height, 10 * self.subHeight) | |||
end | |||
elseif self.gridEnabled then | |||
if self.scale.typename == scale.LogScale.__typename then | |||
local ticksCount = #self._ticks + 1 | |||
self._classes = string.format('st-grid st-grid-%d st-grid-log%d st-line st-line-h%d', ticksCount, ticksCount, 10 * self.height) | |||
else | |||
self._classes = string.format('st-grid st-grid-%d st-line st-line-h%d', #self._ticks + 1, 10 * self.height) | |||
end | |||
else | |||
self._classes = string.format('st-line st-line-h%d', 10 * self.height) | |||
end | end | ||
end | end | ||
| 334行目: | 406行目: | ||
local footerCell = self:__cell__renderFooter(footerRow) | local footerCell = self:__cell__renderFooter(footerRow) | ||
if self.gridEnabled and footerCell then | if self.gridEnabled and footerCell then | ||
footerCell | footerCell:addClass('st-content') | ||
if self.mixinValue then | |||
:addClass | footerCell:addClass('st-mobile-content') | ||
end | |||
local list = footerCell:tag('ul') | local list = footerCell:tag('ul') | ||
:addClass('st-axis') | |||
:addClass(string.format('st-axis-%d', #self._ticks + 1)) | |||
local firstTickLabel = self.ticksFormat(self.scale.domain[1]) | |||
list:tag('li'):wikitext(firstTickLabel) | |||
for _, tick in ipairs(self._ticks) do | for _, tick in ipairs(self._ticks) do | ||
list:tag('li'):wikitext( | local tickLabel = self.ticksFormat(tick) | ||
list:tag('li'):wikitext(tickLabel) | |||
end | |||
local lastTickLabel = self.ticksFormat(self.scale.domain[2]) | |||
list:tag('li'):wikitext(lastTickLabel) | |||
if self._hasSubGraph then | |||
local legend = footerCell:tag('ul'):addClass('st-legend') | |||
local item = legend:tag('li') | |||
local mark = item:tag('span'):addClass('st-legend-mark'):wikitext('■') | |||
if type(self.colorStops) == 'table' then | |||
mark:css('background-image', string.format('linear-gradient(to right,%s,%s)!important', self.colorStops[1], self.colorStops[2])) | |||
else | |||
mark:css('background-color', self.colorStops) | |||
end | |||
item:wikitext(string.format(' %s', self.itemName)) | |||
local subItem = legend:tag('li') | |||
local subMark = subItem:tag('span'):addClass('st-legend-mark'):wikitext('■') | |||
if type(self.subColorStops) == 'table' then | |||
subMark:css('background-image', string.format('linear-gradient(to right,%s,%s)!important', self.subColorStops[1], self.subColorStops[2])) | |||
else | |||
subMark:css('background-color', self.subColorStops) | |||
end | |||
subItem:wikitext(string.format(' %s', self.subItemName)) | |||
end | end | ||
end | end | ||
| 348行目: | 448行目: | ||
-- [Function] Render | -- [Function] Render | ||
function __TableUtil__LineCell:render(row, dataset, i, | function __TableUtil__LineCell:render(row, dataset, i) | ||
local value = dataset[self.dataIndex] | |||
local cell = self:__cell__render(row, dataset, i) | |||
:addClass(self._classes) | |||
:attr('data-sort-value', value) | |||
local gradient | local gradient | ||
if self.startIndex ~= self.endIndex then | if self.startIndex ~= self.endIndex then | ||
local | local minimumValue = dataset[self.startIndex] | ||
local maximumPercentage = self. | local maximumValue = dataset[self.endIndex] | ||
local minimumPercentage = self.scale:scale(minimumValue) | |||
local maximumPercentage = self.scale:scale(maximumValue) | |||
gradient = self._gradientFormat(minimumPercentage, maximumPercentage) | gradient = self._gradientFormat(minimumPercentage, maximumPercentage) | ||
if self.hoverEnabled then | |||
cell:tag('span') | |||
:addClass('st-label') | |||
:addClass('st-label-item1') | |||
:addClass('st-label-lower') | |||
:css('color', self.hoverColor) | |||
:css('right', string.format('%s%%', aw.roundx(100 - minimumPercentage, 3))) | |||
:wikitext(self.hoverFormat(minimumValue)) | |||
cell:tag('span') | |||
:addClass('st-label') | |||
:addClass('st-label-item1') | |||
:addClass('st-label-upper') | |||
:css('color', self.hoverColor) | |||
:css('left', string.format('%s%%', aw.roundx(maximumPercentage, 3))) | |||
:wikitext(self.hoverFormat(maximumValue)) | |||
end | |||
else | else | ||
local percentage = self. | local percentage = self.scale:scale(value) | ||
gradient = self._gradientFormat(percentage) | gradient = self._gradientFormat(percentage) | ||
if self.hoverEnabled then | |||
cell:tag('span') | |||
:addClass('st-label') | |||
:addClass('st-label-item1') | |||
:addClass('st-label-upper') | |||
:css('color', self.hoverColor) | |||
:css('left', string.format('%s%%', aw.roundx(percentage, 3))) | |||
:wikitext(self.hoverFormat(value)) | |||
end | |||
end | end | ||
if self._hasSubGraph then | |||
local subGradient | |||
if self.subStartIndex ~= self.subEndIndex then | |||
local minimumValue2 = dataset[self.subStartIndex] | |||
local maximumValue2 = dataset[self.subEndIndex] | |||
local minimumPercentage = self.scale:scale(minimumValue2) | |||
local maximumPercentage = self.scale:scale(maximumValue2) | |||
subGradient = self._subGradientFormat(minimumPercentage, maximumPercentage) | |||
if self.hoverEnabled then | |||
cell:tag('span') | |||
:addClass('st-label') | |||
:addClass('st-label-item2') | |||
:addClass('st-label-lower') | |||
:css('color', self.subHoverColor) | |||
:css('right', string.format('%s%%', aw.roundx(100 - minimumPercentage, 3))) | |||
:wikitext(self.hoverFormat(minimumValue2)) | |||
cell:tag('span') | |||
:addClass('st-label') | |||
:addClass('st-label-item2') | |||
:addClass('st-label-upper') | |||
:css('color', self.subHoverColor) | |||
:css('left', string.format('%s%%', aw.roundx(maximumPercentage, 3))) | |||
:wikitext(self.hoverFormat(maximumValue2)) | |||
end | |||
else | |||
local value2 = dataset[self.subDataIndex] | |||
local percentage = self.scale:scale(value) | |||
subGradient = self._subGradientFormat(percentage) | |||
if self.hoverEnabled then | |||
cell:tag('span') | |||
:addClass('st-label') | |||
:addClass('st-label-item2') | |||
:addClass('st-label-upper') | |||
:css('color', self.subHoverColor) | |||
:css('left', string.format('%s%%', aw.roundx(percentage, 3))) | |||
:wikitext(self.hoverFormat(value2)) | |||
end | |||
end | |||
if not self.mixinValue then | |||
cell:css('background-image', string.format('%s,%s!important', gradient, subGradient)) | |||
else | |||
cell:css('background-image', string.format('%s,%s', gradient, subGradient)) | |||
end | |||
else | |||
if not self.mixinValue then | |||
cell:css('background-image', string.format('%s!important', gradient)) | |||
else | |||
cell:css('background-image', gradient) | |||
end | |||
end | end | ||
return cell | return cell | ||
end | end | ||
| 399行目: | 573行目: | ||
return setmetatable(obj, { __index = valueKlass }) | return setmetatable(obj, { __index = valueKlass }) | ||
end | end | ||
function valueKlass:render(row, dataset, i | function valueKlass:render(row, dataset, i) | ||
local value = dataset[self.dataIndex] | local value = dataset[self.dataIndex] | ||
local formattedValue = self.format(value) | local formattedValue = self.format(value) | ||
local cell = self:__super__render(row, dataset, i | local cell = self:__super__render(row, dataset, i) | ||
return cell:wikitext(formattedValue) | return cell:wikitext(formattedValue) | ||
end | end | ||
| 444行目: | 618行目: | ||
:addClass('sortable') | :addClass('sortable') | ||
:addClass('st') | :addClass('st') | ||
if opts and type(opts.tableClass) == 'string' then | if opts and type(opts.tableClass) == 'string' then | ||
tbl:addClass(opts.tableClass) | tbl:addClass(opts.tableClass) | ||
| 510行目: | 683行目: | ||
local row = tbl:tag('tr') | local row = tbl:tag('tr') | ||
for k, m in ipairs(metadata) do | for k, m in ipairs(metadata) do | ||
m:render(row, d, i | m:render(row, d, i) | ||
end | end | ||
end | end | ||
| 534行目: | 707行目: | ||
tableutil.LineCell.new('LineGradient', 1, { cellClass = 'st-graph', colorStops = { '#F8B3BC', '#F26D7D' }, height = 1.4 }), | tableutil.LineCell.new('LineGradient', 1, { cellClass = 'st-graph', colorStops = { '#F8B3BC', '#F26D7D' }, height = 1.4 }), | ||
tableutil.LineValueCell.new('LineValue', 2, { cellClass = 'st-graph st-textshadow' }), | tableutil.LineValueCell.new('LineValue', 2, { cellClass = 'st-graph st-textshadow' }), | ||
tableutil.LineCell.new('Line (Range)', 1, { cellClass = 'st-graph', startIndex = 1, endIndex = 2 }), | tableutil.LineCell.new('Line (Range)', 1, { cellClass = 'st-graph', startIndex = 1, endIndex = 2, scale = scale.LogScale.new() }), | ||
tableutil.LineCell.new('Line (Range & Sub)', 1, { cellClass = 'st-graph', startIndex = 1, endIndex = 2, subStartIndex = 1, subEndIndex = 3, hoverEnabled = true }), | |||
} | } | ||
local node = tableutil.createTable({{ 45, 110 }, { 40, 60 }, { 40, 120 }, { 50, 90 }}, metadata) | local node = tableutil.createTable({{ 45, 110, 140 }, { 40, 60, 90 }, { 40, 120, 190 }, { 50, 90, 110 }}, metadata) | ||
return tostring(node) | return tostring(node) | ||
end | end | ||
return tableutil | return tableutil | ||
2021年9月2日 (木) 11:35時点における最新版
このモジュールについての説明文ページを モジュール:Utility/TableUtil/doc に作成できます
-- Table Utility for MediaWiki
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Copyright (C) 2021 mntone. All rights reserved.
-- This script is released under the MIT License.
-- ====================
-- namespace: TableUtil
-- ====================
local tableutil = {}
-- =====
-- Config
-- =====
local aw = require('Module:Utility/Library')
local scale = require('Module:Utility/Scale')
local function __passthrough(num)
return num
end
-- ==========
-- type: Cell
-- ==========
local __TableUtil__CellType__Cell = 'cell'
local __TableUtil__Cell = {
__typename = __TableUtil__CellType__Cell,
}
-- [Constructor]
function __TableUtil__Cell.__new(typename, name, opts)
local obj = {
typename = typename,
name = name,
}
if opts then
if type(opts.attributes) == 'table' then
obj.attributes = opts.attributes
end
if type(opts.cellClass) == 'string' then
obj.cellClass = opts.cellClass
end
if type(opts.footerClass) == 'string' then
obj.footerClass = opts.footerClass
end
if type(opts.headerClass) == 'string' then
obj.headerClass = opts.headerClass
end
if type(opts.headerColspan) == 'number' then
obj.headerColspan = opts.headerColspan
else
obj.headerColspan = 1
end
if type(opts.headerStyle) == 'table' then
obj.headerStyle = opts.headerStyle
end
else
obj.headerColspan = 1
end
return setmetatable(obj, { __index = __TableUtil__Cell })
end
function __TableUtil__Cell.new(name, opts)
return __TableUtil__Cell.__new(__TableUtil__CellType__Cell, name, opts)
end
-- [Function] Reset context
function __TableUtil__Cell:resetContext()
end
-- [Function] Render header
function __TableUtil__Cell:renderHeader(headerRow)
if self.headerColspan <= 0 then
return nil
end
local headerCell = headerRow:tag('th'):wikitext(self.name)
if self.headerClass then
headerCell:addClass(self.headerClass)
end
if self.headerColspan > 1 then
headerCell:attr('colspan', self.headerColspan)
end
if self.headerStyle then
headerCell:css(self.headerStyle)
end
return headerCell
end
-- [Function] Render footer
function __TableUtil__Cell:renderFooter(footerRow)
if self.headerColspan <= 0 then
return nil
end
local footerCell = footerRow:tag('td')
if self.footerClass then
footerCell:addClass(self.footerClass)
end
if self.headerColspan > 1 then
footerCell:attr('colspan', self.headerColspan)
end
return footerCell
end
-- [Function] Render
function __TableUtil__Cell:render(row)
local cell = row:tag('td')
if self.attributes then
cell:attr(self.attributes)
end
if self.cellClass then
cell:addClass(self.cellClass)
end
return cell
end
-- [Export]
tableutil.Cell = __TableUtil__Cell
-- =====================
-- type: ValueCell: Cell
-- =====================
local __TableUtil__CellType__ValueCell = 'value'
local __TableUtil__ValueCell = {
__typename = __TableUtil__CellType__ValueCell,
}
setmetatable(__TableUtil__ValueCell, { __index = __TableUtil__Cell })
-- [Constructor]
function __TableUtil__ValueCell.__new(typename, name, dataIndex, opts)
local obj = __TableUtil__Cell.__new(typename, name, opts)
obj.__cell__render = obj.render
obj.dataIndex = dataIndex
if type(opts) == 'table' then
obj.format = opts.format or __passthrough
else
obj.format = __passthrough
end
return setmetatable(obj, { __index = __TableUtil__ValueCell })
end
function __TableUtil__ValueCell.new(name, dataIndex, opts)
return __TableUtil__ValueCell.__new(__TableUtil__CellType__ValueCell, name, dataIndex, opts)
end
-- [Function] Render
function __TableUtil__ValueCell:render(row, dataset, i)
local value = dataset[self.dataIndex]
local formattedValue = self.format(value)
local cell = self:__cell__render(row, dataset, i)
:attr('data-sort-value', value)
:wikitext(formattedValue)
return cell
end
-- [Export]
tableutil.ValueCell = __TableUtil__ValueCell
-- =========================
-- type: RankCell: ValueCell
-- =========================
local __TableUtil__CellType__RankCell = 'rank'
local __TableUtil__RankCell = {
__typename = __TableUtil__CellType__RankCell,
}
setmetatable(__TableUtil__RankCell, { __index = __TableUtil__ValueCell })
-- [Constructor]
function __TableUtil__RankCell.new(name, dataIndex, descending)
local obj = __TableUtil__ValueCell.__new(__TableUtil__CellType__RankCell, name, dataIndex)
obj.currentRank = 0
obj.currentValue = -1
obj.descending = descending
return setmetatable(obj, { __index = __TableUtil__RankCell })
end
-- [Property] Is descending?
function __TableUtil__RankCell:isDescending()
return self.descending
end
-- [Function] Reset context
function __TableUtil__RankCell:resetContext(datainfo)
self.currentRank = 0
self.currentValue = -1
end
-- [Function] Render
function __TableUtil__RankCell:render(row, dataset, i)
local value = dataset[self.dataIndex]
if self.currentValue ~= value then
self.currentRank = i
self.currentValue = value
end
local cell = row:tag('th')
:attr('data-sort-value', self.currentRank)
:wikitext(self.currentRank)
return cell
end
-- [Export]
tableutil.RankCell = __TableUtil__RankCell
-- ====================
-- type: LineCell: Cell
-- ====================
local __TableUtil__CellType__LineCell = 'line'
local __TableUtil__LineCell = {
__typename = __TableUtil__CellType__LineCell,
}
setmetatable(__TableUtil__LineCell, { __index = __TableUtil__ValueCell })
-- [Utils]
local function multipleGradient(colorStop1, colorStop2)
local gradientFormat = string.format('linear-gradient(to right,transparent %%s%%%%,%s %%s%%%%,%s %%s%%%%,transparent %%s%%%%)', colorStop1, colorStop2)
return function(minValue, maxValue)
if minValue == maxValue then
local value = 0.001 * aw.round(1000 * minValue)
minValue = value - 0.5
maxValue = value + 0.5
else
minValue = 0.001 * aw.round(1000 * minValue)
maxValue = 0.001 * aw.round(1000 * maxValue)
end
return string.format(gradientFormat, minValue, minValue, maxValue, maxValue)
end
end
local function singleGradient(colorStop1, colorStop2)
local gradientFormat
if colorStop1 == colorStop2 then
gradientFormat = string.format('linear-gradient(to right,%s %%s%%%%,transparent %%s%%%%)', colorStop1)
else
gradientFormat = string.format('linear-gradient(to right,%s,%s %%s%%%%,transparent %%s%%%%)', colorStop1, colorStop2)
end
return function(value)
value = 0.001 * aw.round(1000 * value)
return string.format(gradientFormat, value, value)
end
end
-- [Constructor]
function __TableUtil__LineCell.__new(typename, name, dataIndex, opts)
local obj = __TableUtil__Cell.__new(typename, name, opts)
obj.__cell__render = obj.render
obj.__cell__renderFooter = obj.renderFooter
obj.dataIndex = dataIndex
if type(opts) == 'table' then
obj.itemName = opts.itemName or 'Item 1'
obj.startIndex = opts.startIndex or dataIndex
obj.endIndex = opts.endIndex or dataIndex
obj.colorStops = opts.colorStops or '#A7D7F9'
obj.hoverColor = opts.hoverColor or '#5FB6F4'
obj._hasSubGraph = opts.subStartIndex and opts.subEndIndex
if obj._hasSubGraph then
obj.height = opts.height or 1.0
obj.subItemName = opts.subItemName or 'Item 2'
obj.subStartIndex = opts.subStartIndex or opts.subDataIndex
obj.subEndIndex = opts.subEndIndex or opts.subDataIndex
obj.subColorStops = opts.subColorStops or '#F9C9A7'
obj.subHoverColor = opts.subHoverColor or '#F49D5F'
obj.subHeight = opts.subHeight or 0.4
else
obj.height = opts.height or 1.2
end
if opts.gridEnabled then
obj.gridEnabled = opts.gridEnabled
else
obj.gridEnabled = true
end
obj.hoverEnabled = opts.hoverEnabled or false
obj.hoverFormat = opts.hoverFormat or __passthrough
obj.minimumRate = opts.minimumRate or 0.95
obj.maximumValue = opts.maximumValue or math.huge
obj.maximumRate = opts.maximumRate or 1.05
obj.preferredGridCount = opts.preferredGridCount or 4
obj.scale = opts.scale or scale.LinearScale.new():setClamp(true)
if obj.scale.typename ~= scale.LinearScale.__typename then
obj.minimumValue = opts.minimumValue or -math.huge
else
obj.minimumValue = opts.minimumValue or 0
end
obj.ticksFormat = opts.ticksFormat or __passthrough
else
obj.itemName = 'Item 1'
obj.startIndex = dataIndex
obj.endIndex = dataIndex
obj.colorStops = '#A7D7F9'
obj.hoverColor = '#5FB6F4'
obj._hasSubGraph = false
obj.subColorStops = '#F9C9A7'
obj.subHoverColor = '#F49D5F'
obj.gridEnabled = true
obj.height = 1.2
obj.hoverEnabled = false
obj.hoverFormat = __passthrough
obj.minimumRate = 0.95
obj.minimumValue = 0
obj.maximumValue = math.huge
obj.maximumRate = 1.05
obj.preferredGridCount = 4
obj.scale = scale.LinearScale.new():setClamp(true)
obj.ticksFormat = __passthrough
end
obj._gridCount = 2
return setmetatable(obj, { __index = __TableUtil__LineCell })
end
function __TableUtil__LineCell.new(name, dataIndex, opts)
return __TableUtil__LineCell.__new(__TableUtil__CellType__LineCell, name, dataIndex, opts)
end
-- [Property] Height
function __TableUtil__LineCell:setHeight(value)
self.height = value
return self
end
-- [Function] Reset context
function __TableUtil__LineCell:resetContext(datainfo)
local startValue, stopValue
if self.minimumValue == -math.huge then
local minValue = datainfo[self.startIndex].minValue
if self._hasSubGraph then
local subMinValue = datainfo[self.subStartIndex].minValue
if minValue < subMinValue then
minValue = subMinValue
end
end
startValue = self.minimumRate * minValue
else
startValue = self.minimumValue
end
if self.maximumValue == math.huge then
local maxValue = datainfo[self.endIndex].maxValue
if self._hasSubGraph then
local subMaxValue = datainfo[self.subEndIndex].maxValue
if maxValue < subMaxValue then
maxValue = subMaxValue
end
end
stopValue = self.maximumRate * maxValue
else
stopValue = self.maximumValue
end
self.scale
:setDomain({ startValue, stopValue })
:nice(self.preferredGridCount)
if self.gridEnabled then
self._ticks = self.scale:ticks(self.preferredGridCount)
end
if self.startIndex == self.endIndex then
if type(self.colorStops) == 'table' then
self._gradientFormat = singleGradient(self.colorStops[1], self.colorStops[2])
else
self._gradientFormat = singleGradient(self.colorStops, self.colorStops)
end
else
if type(self.colorStops) == 'table' then
self._gradientFormat = multipleGradient(self.colorStops[1], self.colorStops[2])
else
self._gradientFormat = multipleGradient(self.colorStops, self.colorStops)
end
end
if self._hasSubGraph then
if self.subStartIndex == self.subEndIndex then
if type(self.subColorStops) == 'table' then
self._subGradientFormat = singleGradient(self.subColorStops[1], self.subColorStops[2])
else
self._subGradientFormat = singleGradient(self.subColorStops, self.subColorStops)
end
else
if type(self.subColorStops) == 'table' then
self._subGradientFormat = multipleGradient(self.subColorStops[1], self.subColorStops[2])
else
self._subGradientFormat = multipleGradient(self.subColorStops, self.subColorStops)
end
end
if self.gridEnabled then
if self.scale.typename == scale.LogScale.__typename then
local ticksCount = #self._ticks + 1
self._classes = string.format('st-grid st-grid-%d st-grid-log%d st-line st-line-h%d-h%d', ticksCount, ticksCount, 10 * self.height, 10 * self.subHeight)
else
self._classes = string.format('st-grid st-grid-%d st-line st-line-h%d-h%d', #self._ticks + 1, 10 * self.height, 10 * self.subHeight)
end
else
self._classes = string.format('st-line st-line-h%d-h%d', 10 * self.height, 10 * self.subHeight)
end
elseif self.gridEnabled then
if self.scale.typename == scale.LogScale.__typename then
local ticksCount = #self._ticks + 1
self._classes = string.format('st-grid st-grid-%d st-grid-log%d st-line st-line-h%d', ticksCount, ticksCount, 10 * self.height)
else
self._classes = string.format('st-grid st-grid-%d st-line st-line-h%d', #self._ticks + 1, 10 * self.height)
end
else
self._classes = string.format('st-line st-line-h%d', 10 * self.height)
end
end
-- [Function] Render footer
function __TableUtil__LineCell:renderFooter(footerRow)
local footerCell = self:__cell__renderFooter(footerRow)
if self.gridEnabled and footerCell then
footerCell:addClass('st-content')
if self.mixinValue then
footerCell:addClass('st-mobile-content')
end
local list = footerCell:tag('ul')
:addClass('st-axis')
:addClass(string.format('st-axis-%d', #self._ticks + 1))
local firstTickLabel = self.ticksFormat(self.scale.domain[1])
list:tag('li'):wikitext(firstTickLabel)
for _, tick in ipairs(self._ticks) do
local tickLabel = self.ticksFormat(tick)
list:tag('li'):wikitext(tickLabel)
end
local lastTickLabel = self.ticksFormat(self.scale.domain[2])
list:tag('li'):wikitext(lastTickLabel)
if self._hasSubGraph then
local legend = footerCell:tag('ul'):addClass('st-legend')
local item = legend:tag('li')
local mark = item:tag('span'):addClass('st-legend-mark'):wikitext('■')
if type(self.colorStops) == 'table' then
mark:css('background-image', string.format('linear-gradient(to right,%s,%s)!important', self.colorStops[1], self.colorStops[2]))
else
mark:css('background-color', self.colorStops)
end
item:wikitext(string.format(' %s', self.itemName))
local subItem = legend:tag('li')
local subMark = subItem:tag('span'):addClass('st-legend-mark'):wikitext('■')
if type(self.subColorStops) == 'table' then
subMark:css('background-image', string.format('linear-gradient(to right,%s,%s)!important', self.subColorStops[1], self.subColorStops[2]))
else
subMark:css('background-color', self.subColorStops)
end
subItem:wikitext(string.format(' %s', self.subItemName))
end
end
return footerCell
end
-- [Function] Render
function __TableUtil__LineCell:render(row, dataset, i)
local value = dataset[self.dataIndex]
local cell = self:__cell__render(row, dataset, i)
:addClass(self._classes)
:attr('data-sort-value', value)
local gradient
if self.startIndex ~= self.endIndex then
local minimumValue = dataset[self.startIndex]
local maximumValue = dataset[self.endIndex]
local minimumPercentage = self.scale:scale(minimumValue)
local maximumPercentage = self.scale:scale(maximumValue)
gradient = self._gradientFormat(minimumPercentage, maximumPercentage)
if self.hoverEnabled then
cell:tag('span')
:addClass('st-label')
:addClass('st-label-item1')
:addClass('st-label-lower')
:css('color', self.hoverColor)
:css('right', string.format('%s%%', aw.roundx(100 - minimumPercentage, 3)))
:wikitext(self.hoverFormat(minimumValue))
cell:tag('span')
:addClass('st-label')
:addClass('st-label-item1')
:addClass('st-label-upper')
:css('color', self.hoverColor)
:css('left', string.format('%s%%', aw.roundx(maximumPercentage, 3)))
:wikitext(self.hoverFormat(maximumValue))
end
else
local percentage = self.scale:scale(value)
gradient = self._gradientFormat(percentage)
if self.hoverEnabled then
cell:tag('span')
:addClass('st-label')
:addClass('st-label-item1')
:addClass('st-label-upper')
:css('color', self.hoverColor)
:css('left', string.format('%s%%', aw.roundx(percentage, 3)))
:wikitext(self.hoverFormat(value))
end
end
if self._hasSubGraph then
local subGradient
if self.subStartIndex ~= self.subEndIndex then
local minimumValue2 = dataset[self.subStartIndex]
local maximumValue2 = dataset[self.subEndIndex]
local minimumPercentage = self.scale:scale(minimumValue2)
local maximumPercentage = self.scale:scale(maximumValue2)
subGradient = self._subGradientFormat(minimumPercentage, maximumPercentage)
if self.hoverEnabled then
cell:tag('span')
:addClass('st-label')
:addClass('st-label-item2')
:addClass('st-label-lower')
:css('color', self.subHoverColor)
:css('right', string.format('%s%%', aw.roundx(100 - minimumPercentage, 3)))
:wikitext(self.hoverFormat(minimumValue2))
cell:tag('span')
:addClass('st-label')
:addClass('st-label-item2')
:addClass('st-label-upper')
:css('color', self.subHoverColor)
:css('left', string.format('%s%%', aw.roundx(maximumPercentage, 3)))
:wikitext(self.hoverFormat(maximumValue2))
end
else
local value2 = dataset[self.subDataIndex]
local percentage = self.scale:scale(value)
subGradient = self._subGradientFormat(percentage)
if self.hoverEnabled then
cell:tag('span')
:addClass('st-label')
:addClass('st-label-item2')
:addClass('st-label-upper')
:css('color', self.subHoverColor)
:css('left', string.format('%s%%', aw.roundx(percentage, 3)))
:wikitext(self.hoverFormat(value2))
end
end
if not self.mixinValue then
cell:css('background-image', string.format('%s,%s!important', gradient, subGradient))
else
cell:css('background-image', string.format('%s,%s', gradient, subGradient))
end
else
if not self.mixinValue then
cell:css('background-image', string.format('%s!important', gradient))
else
cell:css('background-image', gradient)
end
end
return cell
end
-- [Export]
tableutil.LineCell = __TableUtil__LineCell
-- =================
-- type: Value+XCell
-- =================
local function transformValueCell(superklass, klasstype)
local valuetype = string.format('value+%s', klasstype)
local valueKlass = {
__typename = valuetype,
}
setmetatable(valueKlass, { __index = superklass })
function valueKlass.new(name, dataIndex, opts)
local obj = superklass.__new(valuetype, name, dataIndex, opts)
obj.__super__render = obj.render
obj.__super__renderHeader = obj.renderHeader
obj.__super__renderFooter = obj.renderFooter
obj.mixinValue = true
if type(opts) == 'table' then
obj.format = opts.format or __passthrough
else
obj.format = __passthrough
end
return setmetatable(obj, { __index = valueKlass })
end
function valueKlass:render(row, dataset, i)
local value = dataset[self.dataIndex]
local formattedValue = self.format(value)
local cell = self:__super__render(row, dataset, i)
return cell:wikitext(formattedValue)
end
return valueKlass
end
-- [Export]
tableutil.LineValueCell = transformValueCell(__TableUtil__LineCell, __TableUtil__CellType__LineCell)
-- ===================
-- func: specifyUICell
-- ===================
function tableutil.specifyUICell(klasstype, renderFunction, propNames)
local specifictype = string.format('ui+%s', klasstype)
local specificKlass = {
__typename = specifictype,
}
setmetatable(specificKlass, { __index = __TableUtil__Cell })
function specificKlass.new(name, dataIndex, opts)
local obj = __TableUtil__Cell.__new(specifictype, name, opts)
obj.__cell__render = obj.render
obj.dataIndex = dataIndex
if propNames and opts then
for _, name in ipairs(propNames) do
obj[name] = opts[name]
end
end
return setmetatable(obj, { __index = specificKlass })
end
specificKlass.render = renderFunction
return specificKlass
end
-- =================
-- func: createTable
-- =================
function tableutil.createTable(dataset, metadata, opts)
local tbl = mw.html.create('table')
:addClass('numbertable')
:addClass('sortable')
:addClass('st')
if opts and type(opts.tableClass) == 'string' then
tbl:addClass(opts.tableClass)
end
-- Build header
local headerRow = tbl:tag('tr'):addClass('row-sticky')
local sortIndex = 0
local isDesc = false
for _, m in ipairs(metadata) do
if m.typename == tableutil.RankCell.__typename then
sortIndex = m.dataIndex
isDesc = m:isDescending()
end
-- Render header
m:renderHeader(headerRow)
end
-- Sort data
if sortIndex ~= 0 then
local sortFunction
if isDesc then
sortFunction = function(a, b)
return a[sortIndex] > b[sortIndex]
end
else
sortFunction = function(a, b)
return a[sortIndex] < b[sortIndex]
end
end
table.sort(dataset, sortFunction)
end
-- Preprocess data
local datainfo = {}
for _, ary in ipairs(dataset) do
for key, d in pairs(ary) do
if type(d) == 'number' and d ~= math.huge then
if not datainfo[key] then
datainfo[key] = {
minValue = d,
maxValue = d,
}
else
if datainfo[key].minValue > d then
datainfo[key].minValue = d
end
if datainfo[key].maxValue < d then
datainfo[key].maxValue = d
end
end
end
end
end
-- Reset context
for _, m in ipairs(metadata) do
-- Reset context for building process
m:resetContext(datainfo)
end
-- Build cell
for i, d in ipairs(dataset) do
local row = tbl:tag('tr')
for k, m in ipairs(metadata) do
m:render(row, d, i)
end
end
-- Build footer
local footerRow = tbl:tag('tr'):addClass('sortbottom')
for _, m in ipairs(metadata) do
-- Render footer
m:renderFooter(footerRow)
end
return tbl
end
-- =================
-- test: Simple test
-- =================
function tableutil.__test()
local metadata = {
tableutil.RankCell.new('', 1),
tableutil.ValueCell.new('Value', 1),
tableutil.LineCell.new('Line', 1, { cellClass = 'st-graph' }),
tableutil.LineCell.new('LineGradient', 1, { cellClass = 'st-graph', colorStops = { '#F8B3BC', '#F26D7D' }, height = 1.4 }),
tableutil.LineValueCell.new('LineValue', 2, { cellClass = 'st-graph st-textshadow' }),
tableutil.LineCell.new('Line (Range)', 1, { cellClass = 'st-graph', startIndex = 1, endIndex = 2, scale = scale.LogScale.new() }),
tableutil.LineCell.new('Line (Range & Sub)', 1, { cellClass = 'st-graph', startIndex = 1, endIndex = 2, subStartIndex = 1, subEndIndex = 3, hoverEnabled = true }),
}
local node = tableutil.createTable({{ 45, 110, 140 }, { 40, 60, 90 }, { 40, 120, 190 }, { 50, 90, 110 }}, metadata)
return tostring(node)
end
return tableutil