|
|
Line 1: |
Line 1: |
− | --[[ | + | {{CardTable2 |
− | A module for generating test case templates.
| + | | fr_name = Communication avec Yosenju Oroshi |
− | | + | | de_name = Yosenju-Oroshi-Kanalisierung |
− | This module incorporates code from the English Wikipedia's "Testcase table"
| + | | it_name = Incanalamento Yosenju Oroshi |
− | module,[1] written by Frietjes [2] with contributions by Mr. Stradivarius [3]
| + | | pt_name = Canalização de Yosenju Oroshi |
− | and Jackmcbarn,[4] and the English Wikipedia's "Testcase rows" module,[5]
| + | | es_name = Encauce de Yosenju Oroshi |
− | written by Mr. Stradivarius.
| + | | ja_name = {{Ruby|妖|よう}}{{Ruby|仙|せん}}{{Ruby|獣|じゅう}}の{{Ruby|神|かみ}}{{Ruby|颪|おろし}} |
− | | + | | romaji_name = Yōsenjū no Kamioroshi |
− | The "Testcase table" and "Testcase rows" modules are released under the
| + | | trans_name = Yosenju Divine Oroshi |
− | CC BY-SA 3.0 License [6] and the GFDL.[7]
| + | | ko_name = 요선수의 신풍 |
− | | + | | image = YosenjuOroshiChanneling-RIRA-EN-C-1E.png |
− | License: CC BY-SA 3.0 and the GFDL
| + | | card_type = Spell |
− | Author: Mr. Stradivarius
| + | | property = Normal |
− | | + | | password = 61884774 |
− | [1] https://en.wikipedia.org/wiki/Module:Testcase_table
| + | | effect_types = Activation requirement, Effect |
− | [2] https://en.wikipedia.org/wiki/User:Frietjes
| + | | lore = If you [[control]] no [[Monster Card|monsters]]: [[Activate]] 1 of the following [[effect]]s; also for the rest of this [[turn]] after this card [[resolve]]s, you cannot [[Special Summon]] monsters, except "[[Yosenju]]" monsters.<br />● [[Add a card|Add]] 1 [[Level]] 5 or higher "Yosenju" monster from your [[Main Deck|Deck]] to your [[hand]].<br />● Place 1 "[[Yosenju Shinchu L]]" and 1 "[[Yosenju Shinchu R]]" from your Deck in your [[Pendulum Zone]]s, but [[destroy]] them during your opponent's next [[End Phase]]. |
− | [3] https://en.wikipedia.org/wiki/User:Mr._Stradivarius
| + | | fr_lore = Si vous ne contrôlez aucun monstre : activez 1 des effets suivants ; et aussi, le reste de ce tour après la résolution de cette carte, vous ne pouvez pas Invoquer Spécialement de monstres (monstres "Yosenju" exclus).<br />● Ajoutez 1 monstre "Yosenju" de min. Niveau 5 depuis votre Deck à votre main.<br />● Placez 1 "Yosenju Shinchu G" et 1 "Yosenju Shinchu D" depuis votre Deck dans vos Zones Pendule, mais détruisez-les durant la prochaine End Phase de votre adversaire. |
− | [4] https://en.wikipedia.org/wiki/User:Jackmcbarn
| + | | de_lore = Falls du keine Monster kontrollierst: Aktiviere 1 der folgenden Effekte; zusätzlich kannst du für den Rest dieses Spielzugs, nachdem diese Karte aufgelöst wurde, keine Monster als Spezialbeschwörung beschwören, außer „Yosenju“-Monstern.<br />● Füge deiner Hand 1 „Yosenju“-Monster der Stufe 5 oder höher von deinem Deck hinzu.<br />● Lege 1 „Yosenju Shinchu L“ und 1 „Yosenju Shinchu R“ von deinem Deck in deine Pendelzonen, aber zerstöre sie während der nächsten End Phase deines Gegners. |
− | [5] https://en.wikipedia.org/wiki/Module:Testcase_rows
| + | | it_lore = Se non controlli nessun mostro: attiva 1 dei seguenti effetti; inoltre, per il resto di questo turno dopo che questa carta si è risolta, non puoi Evocare Specialmente mostri, eccetto mostri "Yosenju".<br />● Aggiungi 1 mostro "Yosenju" di Livello 5 o superiore dal tuo Deck alla tua mano.<br />● Metti 1 "Yosenju Shinchu S" e 1 "Yosenju Shinchu D" dal tuo Deck nelle tue Zone Pendulum, ma distruggili durante la prossima End Phase del tuo avversario. |
− | [6] https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License
| + | | pt_lore = Se você não controlar nenhum monstro: ative 1 dos seguintes efeitos; além disso, pelo resto deste turno depois que este card resolver, você não pode Invocar monstros por Invocação-Especial, exceto monstros "Yosenju".<br />● Adicione 1 monstro "Yosenju" de Nível 5 ou mais do seu Deck à sua mão.<br />● Coloque 1 "Yosenju Shinchu E" e 1 "Yosenju Shinchu D" do seu Deck nas suas Zonas de Pêndulo, mas destrua-os durante a Fase Final do seu oponente. |
− | [7] https://en.wikipedia.org/wiki/Wikipedia:Text_of_the_GNU_Free_Documentation_License
| + | | es_lore = Si no controlas monstruos: activa 1 de los siguientes efectos; y además, por el resto de este turno después de que esta carta se resuelva, no puedes Invocar monstruos de Modo Especial, excepto monstruos "Yosenju".<br />● Añade a tu mano 1 monstruo "Yosenju" de Nivel 5 o mayor en tu Deck.<br />● Pon en tu Zona de Péndulo 1 "Yosenju Shinchu I" y 1 "Yosenju Shinchu D" en tu Deck, pero destrúyelos durante la próxima End Phase de tu adversario. |
− | ]] | + | | ja_lore = ①:自分フィールドにモンスターが存在しない場合、以下の効果から1つを選択して発動できる。このカードの発動後、ターン終了時まで自分は「妖仙獣」モンスターしか特殊召喚できない。●デッキからレベル5以上の「妖仙獣」モンスター1体を手札に加える。●デッキから「妖仙獣 左鎌神柱」「妖仙獣 右鎌神柱」を1枚ずつ選び、自分のPゾーンに置く。この効果でPゾーンに置かれたカードは次の相手エンドフェイズに破壊される。 |
− | | + | | ko_lore = ① : 자신 필드에 몬스터가 존재하지 않을 경우, 이하의 효과에서 1개를 선택하고 발동할 수 있다. 이 카드의 발동후, 턴 종료시까지 자신은 "요선수" 몬스터밖에 특수 소환할 수 없다. ● 덱에서 레벨 5 이상의 "요선수" 몬스터 1장을 패에 넣는다. ● 덱에서 "요선수 좌겸신주" "요선수 우겸신주"를 1장씩 고르고, 자신의 펜듈럼 존에 놓는다. 이 효과로 펜듈럼 존에 놓여진 카드는 다음 상대 엔드 페이즈에 파괴된다. |
− | -- Load required modules
| + | | en_sets = |
− | local yesno = require('Module:Yesno')
| + | RIRA-EN092; Rising Rampage; Common |
− | | + | | fr_sets = |
− | -- Set constants
| + | RIRA-FR092; Rising Rampage; Common |
− | local DATA_MODULE = 'Module:Template test case/data'
| + | | de_sets = |
− | | + | RIRA-DE092; Rising Rampage; Common |
− | ------------------------------------------------------------------------------- | + | | it_sets = |
− | -- Shared methods
| + | RIRA-IT092; Rising Rampage; Common |
− | ------------------------------------------------------------------------------- | + | | pt_sets = |
− | | + | RIRA-PT092; Rising Rampage; Common |
− | local function message(self, key, ...)
| + | | sp_sets = |
− | -- This method is added to classes that need to deal with messages from the
| + | RIRA-SP092; Rising Rampage; Common |
− | -- config module.
| + | | jp_sets = |
− | local msg = self.cfg.msg[key]
| + | DBLE-JP020; Dimension Box Limited Edition; Ultra Parallel Rare |
− | if select(1, ...) then
| + | | kr_sets = |
− | return mw.message.newRawMessage(msg, ...):plain()
| + | DBLE-KR020; Dimension Box Limited Edition; Ultra Parallel Rare |
− | else
| + | | mentions = |
− | return msg
| + | * Yosenju Shinchu L |
− | end
| + | * Yosenju Shinchu R |
− | end
| + | | archseries = Yosenju |
− | | + | | supports_archetypes = Yosenju |
− | -------------------------------------------------------------------------------
| + | | action = |
− | -- Template class
| + | * Adds from Deck to hand |
− | -------------------------------------------------------------------------------
| + | * Places from Deck to Pendulum Zone |
− | | + | | m/s/t = Destroys your Spell Cards |
− | local Template = {}
| + | | summoning = Restricts the player's Special Summons to archetype specific monsters |
− | | + | | misc = Variable effects |
− | Template.memoizedMethods = {
| + | | database_id = 12864 |
− | -- Names of methods to be memoized in each object. This table should only
| + | }} |
− | -- hold methods with no parameters.
| |
− | getFullPage = true,
| |
− | getName = true,
| |
− | makeHeader = true,
| |
− | getOutput = true
| |
− | }
| |
− | | |
− | function Template.new(invocationObj, options)
| |
− | local obj = {}
| |
− | | |
− | -- Set input
| |
− | for k, v in pairs(options or {}) do
| |
− | if not Template[k] then
| |
− | obj[k] = v
| |
− | end
| |
− | end
| |
− | obj._invocation = invocationObj
| |
− | | |
− | -- Validate input
| |
− | if not obj.template and not obj.title then
| |
− | error('no template or title specified', 2)
| |
− | end
| |
− | | |
− | -- Memoize expensive method calls
| |
− | local memoFuncs = {}
| |
− | return setmetatable(obj, {
| |
− | __index = function (t, key)
| |
− | if Template.memoizedMethods[key] then
| |
− | local func = memoFuncs[key]
| |
− | if not func then
| |
− | local val = Template[key](t)
| |
− | func = function () return val end
| |
− | memoFuncs[key] = func
| |
− | end
| |
− | return func
| |
− | else
| |
− | return Template[key]
| |
− | end
| |
− | end
| |
− | })
| |
− | end
| |
− | | |
− | function Template:getFullPage()
| |
− | if not self.template then
| |
− | return self.title.prefixedText
| |
− | elseif self.template:sub(1, 7) == '#invoke' then
| |
− | return 'Module' .. self.template:sub(8):gsub('|.*', '')
| |
− | else
| |
− | local strippedTemplate, hasColon = self.template:gsub('^:', '', 1)
| |
− | hasColon = hasColon > 0
| |
− | local ns = strippedTemplate:match('^(.-):')
| |
− | ns = ns and mw.site.namespaces[ns]
| |
− | if ns then
| |
− | return strippedTemplate
| |
− | elseif hasColon then
| |
− | return strippedTemplate -- Main namespace
| |
− | else
| |
− | return mw.site.namespaces[10].name .. ':' .. strippedTemplate
| |
− | end
| |
− | end
| |
− | end
| |
− | | |
− | function Template:getName()
| |
− | if self.template then
| |
− | return self.template
| |
− | else
| |
− | return require('Module:Template invocation').name(self.title)
| |
− | end
| |
− | end
| |
− | | |
− | function Template:makeLink(display)
| |
− | if display then
| |
− | return string.format('[[:%s|%s]]', self:getFullPage(), display)
| |
− | else
| |
− | return string.format('[[:%s]]', self:getFullPage())
| |
− | end
| |
− | end
| |
− | | |
− | function Template:makeBraceLink(display)
| |
− | display = display or self:getName()
| |
− | local link = self:makeLink(display)
| |
− | return mw.text.nowiki('{{') .. link .. mw.text.nowiki('}}')
| |
− | end
| |
− | | |
− | function Template:makeHeader()
| |
− | return self.heading or self:makeBraceLink()
| |
− | end
| |
− | | |
− | function Template:getInvocation(format)
| |
− | local invocation = self._invocation:getInvocation{
| |
− | template = self:getName(),
| |
− | requireMagicWord = self.requireMagicWord,
| |
− | }
| |
− | if format == 'code' then
| |
− | invocation = '<code>' .. mw.text.nowiki(invocation) .. '</code>'
| |
− | elseif format == 'kbd' then
| |
− | invocation = '<kbd>' .. mw.text.nowiki(invocation) .. '</kbd>'
| |
− | elseif format == 'plain' then
| |
− | invocation = mw.text.nowiki(invocation)
| |
− | else
| |
− | -- Default is pre tags
| |
− | invocation = mw.text.encode(invocation, '&')
| |
− | invocation = '<pre style="white-space: pre-wrap;">' .. invocation .. '</pre>'
| |
− | invocation = mw.getCurrentFrame():preprocess(invocation)
| |
− | end
| |
− | return invocation
| |
− | end
| |
− | | |
− | function Template:getOutput()
| |
− | local protect = require('Module:Protect')
| |
− | -- calling self._invocation:getOutput{...}
| |
− | return protect(self._invocation.getOutput)(self._invocation, {
| |
− | template = self:getName(),
| |
− | requireMagicWord = self.requireMagicWord,
| |
− | })
| |
− | end
| |
− | | |
− | -------------------------------------------------------------------------------
| |
− | -- TestCase class
| |
− | -------------------------------------------------------------------------------
| |
− | | |
− | local TestCase = {}
| |
− | TestCase.__index = TestCase
| |
− | TestCase.message = message -- add the message method
| |
− | | |
− | TestCase.renderMethods = {
| |
− | -- Keys in this table are values of the "format" option, values are the
| |
− | -- method for rendering that format.
| |
− | columns = 'renderColumns',
| |
− | rows = 'renderRows',
| |
− | tablerows = 'renderRows',
| |
− | inline = 'renderInline',
| |
− | cells = 'renderCells',
| |
− | default = 'renderDefault'
| |
− | }
| |
− | | |
− | function TestCase.new(invocationObj, options, cfg)
| |
− | local obj = setmetatable({}, TestCase)
| |
− | obj.cfg = cfg
| |
− | | |
− | -- Separate general options from template options. Template options are
| |
− | -- numbered, whereas general options are not.
| |
− | local generalOptions, templateOptions = {}, {}
| |
− | for k, v in pairs(options) do
| |
− | local prefix, num
| |
− | if type(k) == 'string' then
| |
− | prefix, num = k:match('^(.-)([1-9][0-9]*)$')
| |
− | end
| |
− | if prefix then
| |
− | num = tonumber(num)
| |
− | templateOptions[num] = templateOptions[num] or {}
| |
− | templateOptions[num][prefix] = v
| |
− | else
| |
− | generalOptions[k] = v
| |
− | end
| |
− | end
| |
− | | |
− | -- Set general options
| |
− | generalOptions.showcode = yesno(generalOptions.showcode)
| |
− | generalOptions.showheader = yesno(generalOptions.showheader) ~= false
| |
− | generalOptions.showcaption = yesno(generalOptions.showcaption) ~= false
| |
− | generalOptions.collapsible = yesno(generalOptions.collapsible)
| |
− | generalOptions.notcollapsed = yesno(generalOptions.notcollapsed)
| |
− | generalOptions.wantdiff = yesno(generalOptions.wantdiff)
| |
− | obj.options = generalOptions
| |
− | | |
− | -- Preprocess template args
| |
− | for num, t in pairs(templateOptions) do
| |
− | if t.showtemplate ~= nil then
| |
− | t.showtemplate = yesno(t.showtemplate)
| |
− | end
| |
− | end
| |
− | | |
− | -- Set up first two template options tables, so that if only the
| |
− | -- "template3" is specified it isn't made the first template when the
| |
− | -- the table options array is compressed.
| |
− | templateOptions[1] = templateOptions[1] or {}
| |
− | templateOptions[2] = templateOptions[2] or {}
| |
− | | |
− | -- Allow the "template" option to override the "template1" option for
| |
− | -- backwards compatibility with [[Module:Testcase table]].
| |
− | if generalOptions.template then
| |
− | templateOptions[1].template = generalOptions.template
| |
− | end
| |
− | | |
− | -- Add default template options
| |
− | if templateOptions[1].template and not templateOptions[2].template then
| |
− | templateOptions[2].template = templateOptions[1].template ..
| |
− | '/' .. obj.cfg.sandboxSubpage
| |
− | end
| |
− | if not templateOptions[1].template then
| |
− | templateOptions[1].title = mw.title.getCurrentTitle().basePageTitle
| |
− | end
| |
− | if not templateOptions[2].template then
| |
− | templateOptions[2].title = templateOptions[1].title:subPageTitle(
| |
− | obj.cfg.sandboxSubpage
| |
− | )
| |
− | end
| |
− | | |
− | -- Remove template options for any templates where the showtemplate
| |
− | -- argument is false. This prevents any output for that template.
| |
− | for num, t in pairs(templateOptions) do
| |
− | if t.showtemplate == false then
| |
− | templateOptions[num] = nil
| |
− | end
| |
− | end
| |
− | | |
− | -- Check for missing template names.
| |
− | for num, t in pairs(templateOptions) do
| |
− | if not t.template and not t.title then
| |
− | error(obj:message(
| |
− | 'missing-template-option-error',
| |
− | num, num
| |
− | ), 2)
| |
− | end
| |
− | end
| |
− | | |
− | -- Compress templateOptions table so we can iterate over it with ipairs.
| |
− | templateOptions = (function (t)
| |
− | local nums = {}
| |
− | for num in pairs(t) do
| |
− | nums[#nums + 1] = num
| |
− | end
| |
− | table.sort(nums)
| |
− | local ret = {}
| |
− | for i, num in ipairs(nums) do
| |
− | ret[i] = t[num]
| |
− | end
| |
− | return ret
| |
− | end)(templateOptions)
| |
− | | |
− | -- Don't require the __TEMPLATENAME__ magic word for nowiki invocations if
| |
− | -- there is only one template being output.
| |
− | if #templateOptions <= 1 then
| |
− | templateOptions[1].requireMagicWord = false
| |
− | end
| |
− | | |
− | mw.logObject(templateOptions)
| |
− | | |
− | -- Make the template objects
| |
− | obj.templates = {}
| |
− | for i, options in ipairs(templateOptions) do
| |
− | table.insert(obj.templates, Template.new(invocationObj, options))
| |
− | end
| |
− | | |
− | -- Add tracking categories. At the moment we are only tracking templates
| |
− | -- that use any "heading" parameters or an "output" parameter.
| |
− | obj.categories = {}
| |
− | for k, v in pairs(options) do
| |
− | if type(k) == 'string' and k:find('heading') then
| |
− | obj.categories['Test cases using heading parameters'] = true
| |
− | elseif k == 'output' then
| |
− | obj.categories['Test cases using output parameter'] = true
| |
− | end
| |
− | end
| |
− | | |
− | return obj
| |
− | end
| |
− | | |
− | function TestCase:getTemplateOutput(templateObj)
| |
− | local output = templateObj:getOutput()
| |
− | if self.options.resetRefs then
| |
− | mw.getCurrentFrame():extensionTag('references')
| |
− | end
| |
− | return output
| |
− | end
| |
− | | |
− | function TestCase:templateOutputIsEqual()
| |
− | -- Returns a boolean showing whether all of the template outputs are equal.
| |
− | -- The random parts of strip markers (see [[Help:Strip markers]]) are
| |
− | -- removed before comparison. This means a strip marker can contain anything
| |
− | -- and still be treated as equal, but it solves the problem of otherwise
| |
− | -- identical wikitext not returning as exactly equal.
| |
− | local function normaliseOutput(obj)
| |
− | local out = obj:getOutput()
| |
− | -- Remove the random parts from strip markers.
| |
− | out = out:gsub('(\127[^\127]*UNIQ%-%-%l+%-)%x+(%-%-?QINU[^\127]*\127)', '%1%2')
| |
− | return out
| |
− | end
| |
− | local firstOutput = normaliseOutput(self.templates[1])
| |
− | for i = 2, #self.templates do
| |
− | local output = normaliseOutput(self.templates[i])
| |
− | if output ~= firstOutput then
| |
− | return false
| |
− | end
| |
− | end
| |
− | return true
| |
− | end
| |
− | | |
− | function TestCase:makeCollapsible(s)
| |
− | local title = self.options.title or self.templates[1]:makeHeader()
| |
− | if self.options.titlecode then
| |
− | title = self.templates[1]:getInvocation('kbd')
| |
− | end
| |
− | local isEqual = self:templateOutputIsEqual()
| |
− | local root = mw.html.create('div')
| |
− | root
| |
− | :addClass('mw-collapsible')
| |
− | :css('width', '100%')
| |
− | :css('border', 'solid silver 1px')
| |
− | :css('padding', '0.2em')
| |
− | :css('clear', 'both')
| |
− | :addClass(self.options.notcollapsed == false and 'mw-collapsed' or nil)
| |
− | if self.options.wantdiff then
| |
− | root
| |
− | :tag('div')
| |
− | :css('background-color', isEqual and 'yellow' or '#90a8ee')
| |
− | :css('font-weight', 'bold')
| |
− | :css('padding', '0.2em')
| |
− | :wikitext(title)
| |
− | :done()
| |
− | else
| |
− | if self.options.notcollapsed ~= true or false then
| |
− | root
| |
− | :addClass(isEqual and 'mw-collapsed' or nil)
| |
− | end
| |
− | root
| |
− | :tag('div')
| |
− | :css('background-color', isEqual and 'lightgreen' or 'yellow')
| |
− | :css('font-weight', 'bold')
| |
− | :css('padding', '0.2em')
| |
− | :wikitext(title)
| |
− | :done()
| |
− | end
| |
− | root
| |
− | :tag('div')
| |
− | :addClass('mw-collapsible-content')
| |
− | :newline()
| |
− | :wikitext(s)
| |
− | :newline()
| |
− | return tostring(root)
| |
− | end
| |
− | | |
− | function TestCase:renderColumns()
| |
− | local root = mw.html.create()
| |
− | if self.options.showcode then
| |
− | root
| |
− | :wikitext(self.templates[1]:getInvocation())
| |
− | :newline()
| |
− | end
| |
− | | |
− | local tableroot = root:tag('table')
| |
− | | |
− | if self.options.showheader then
| |
− | -- Caption
| |
− | if self.options.showcaption then
| |
− | tableroot
| |
− | :addClass(self.options.class)
| |
− | :cssText(self.options.style)
| |
− | :tag('caption')
| |
− | :wikitext(self.options.caption or self:message('columns-header'))
| |
− | end
| |
− | | |
− | -- Headers
| |
− | local headerRow = tableroot:tag('tr')
| |
− | if self.options.rowheader then
| |
− | -- rowheader is correct here. We need to add another th cell if
| |
− | -- rowheader is set further down, even if heading0 is missing.
| |
− | headerRow:tag('th'):wikitext(self.options.heading0)
| |
− | end
| |
− | local width
| |
− | if #self.templates > 0 then
| |
− | width = tostring(math.floor(100 / #self.templates)) .. '%'
| |
− | else
| |
− | width = '100%'
| |
− | end
| |
− | for i, obj in ipairs(self.templates) do
| |
− | headerRow
| |
− | :tag('th')
| |
− | :css('width', width)
| |
− | :wikitext(obj:makeHeader())
| |
− | end
| |
− | end
| |
− | | |
− | -- Row header
| |
− | local dataRow = tableroot:tag('tr'):css('vertical-align', 'top')
| |
− | if self.options.rowheader then
| |
− | dataRow:tag('th')
| |
− | :attr('scope', 'row')
| |
− | :wikitext(self.options.rowheader)
| |
− | end
| |
− |
| |
− | -- Template output
| |
− | for i, obj in ipairs(self.templates) do
| |
− | if self.options.output == 'nowiki+' then
| |
− | dataRow:tag('td')
| |
− | :newline()
| |
− | :wikitext(self.options.before)
| |
− | :wikitext(self:getTemplateOutput(obj))
| |
− | :wikitext(self.options.after)
| |
− | :wikitext('<pre style="white-space: pre-wrap;">')
| |
− | :wikitext(mw.text.nowiki(self.options.before or ""))
| |
− | :wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
| |
− | :wikitext(mw.text.nowiki(self.options.after or ""))
| |
− | :wikitext('</pre>')
| |
− | elseif self.options.output == 'nowiki' then
| |
− | dataRow:tag('td')
| |
− | :newline()
| |
− | :wikitext(mw.text.nowiki(self.options.before or ""))
| |
− | :wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
| |
− | :wikitext(mw.text.nowiki(self.options.after or ""))
| |
− | else
| |
− | dataRow:tag('td')
| |
− | :newline()
| |
− | :wikitext(self.options.before)
| |
− | :wikitext(self:getTemplateOutput(obj))
| |
− | :wikitext(self.options.after)
| |
− | end
| |
− | end
| |
− |
| |
− | return tostring(root)
| |
− | end
| |
− | | |
− | function TestCase:renderRows()
| |
− | local root = mw.html.create()
| |
− | if self.options.showcode then
| |
− | root
| |
− | :wikitext(self.templates[1]:getInvocation())
| |
− | :newline()
| |
− | end
| |
− | | |
− | local tableroot = root:tag('table')
| |
− | tableroot
| |
− | :addClass(self.options.class)
| |
− | :cssText(self.options.style)
| |
− | | |
− | if self.options.caption then
| |
− | tableroot
| |
− | :tag('caption')
| |
− | :wikitext(self.options.caption)
| |
− | end
| |
− | | |
− | for _, obj in ipairs(self.templates) do
| |
− | local dataRow = tableroot:tag('tr')
| |
− |
| |
− | -- Header
| |
− | if self.options.showheader then
| |
− | if self.options.format == 'tablerows' then
| |
− | dataRow:tag('th')
| |
− | :attr('scope', 'row')
| |
− | :css('vertical-align', 'top')
| |
− | :css('text-align', 'left')
| |
− | :wikitext(obj:makeHeader())
| |
− | dataRow:tag('td')
| |
− | :css('vertical-align', 'top')
| |
− | :css('padding', '0 1em')
| |
− | :wikitext('→')
| |
− | else
| |
− | dataRow:tag('td')
| |
− | :css('text-align', 'center')
| |
− | :css('font-weight', 'bold')
| |
− | :wikitext(obj:makeHeader())
| |
− | dataRow = tableroot:tag('tr')
| |
− | end
| |
− | end
| |
− |
| |
− | -- Template output
| |
− | if self.options.output == 'nowiki+' then
| |
− | dataRow:tag('td')
| |
− | :newline()
| |
− | :wikitext(self.options.before)
| |
− | :wikitext(self:getTemplateOutput(obj))
| |
− | :wikitext(self.options.after)
| |
− | :wikitext('<pre style="white-space: pre-wrap;">')
| |
− | :wikitext(mw.text.nowiki(self.options.before or ""))
| |
− | :wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
| |
− | :wikitext(mw.text.nowiki(self.options.after or ""))
| |
− | :wikitext('</pre>')
| |
− | elseif self.options.output == 'nowiki' then
| |
− | dataRow:tag('td')
| |
− | :newline()
| |
− | :wikitext(mw.text.nowiki(self.options.before or ""))
| |
− | :wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
| |
− | :wikitext(mw.text.nowiki(self.options.after or ""))
| |
− | else
| |
− | dataRow:tag('td')
| |
− | :newline()
| |
− | :wikitext(self.options.before)
| |
− | :wikitext(self:getTemplateOutput(obj))
| |
− | :wikitext(self.options.after)
| |
− | end
| |
− | end
| |
− | | |
− | return tostring(root)
| |
− | end
| |
− | | |
− | function TestCase:renderInline()
| |
− | local arrow = mw.language.getContentLanguage():getArrow('forwards')
| |
− | local ret = {}
| |
− | for i, obj in ipairs(self.templates) do
| |
− | local line = {}
| |
− | line[#line + 1] = self.options.prefix or '* '
| |
− | if self.options.showcode then
| |
− | line[#line + 1] = obj:getInvocation('code')
| |
− | line[#line + 1] = ' '
| |
− | line[#line + 1] = arrow
| |
− | line[#line + 1] = ' '
| |
− | end
| |
− | if self.options.output == 'nowiki+' then
| |
− | line[#line + 1] = self.options.before or ""
| |
− | line[#line + 1] = self:getTemplateOutput(obj)
| |
− | line[#line + 1] = self.options.after or ""
| |
− | line[#line + 1] = '<pre style="white-space: pre-wrap;">'
| |
− | line[#line + 1] = mw.text.nowiki(self.options.before or "")
| |
− | line[#line + 1] = mw.text.nowiki(self:getTemplateOutput(obj))
| |
− | line[#line + 1] = mw.text.nowiki(self.options.after or "")
| |
− | line[#line + 1] = '</pre>'
| |
− | elseif self.options.output == 'nowiki' then
| |
− | line[#line + 1] = mw.text.nowiki(self.options.before or "")
| |
− | line[#line + 1] = mw.text.nowiki(self:getTemplateOutput(obj))
| |
− | line[#line + 1] = mw.text.nowiki(self.options.after or "")
| |
− | else
| |
− | line[#line + 1] = self.options.before or ""
| |
− | line[#line + 1] = self:getTemplateOutput(obj)
| |
− | line[#line + 1] = self.options.after or ""
| |
− | end
| |
− | ret[#ret + 1] = table.concat(line)
| |
− | end
| |
− | if self.options.addline then
| |
− | local line = {}
| |
− | line[#line + 1] = self.options.prefix or '* '
| |
− | line[#line + 1] = self.options.addline
| |
− | ret[#ret + 1] = table.concat(line)
| |
− | end
| |
− | return table.concat(ret, '\n')
| |
− | end
| |
− | | |
− | function TestCase:renderCells()
| |
− | local root = mw.html.create()
| |
− | local dataRow = root:tag('tr')
| |
− | dataRow
| |
− | :css('vertical-align', 'top')
| |
− | :addClass(self.options.class)
| |
− | :cssText(self.options.style)
| |
− | | |
− | -- Row header
| |
− | if self.options.rowheader then
| |
− | dataRow:tag('th')
| |
− | :attr('scope', 'row')
| |
− | :newline()
| |
− | :wikitext(self.options.rowheader or self:message('row-header'))
| |
− | end
| |
− | -- Caption
| |
− | if self.options.showcaption then
| |
− | dataRow:tag('th')
| |
− | :attr('scope', 'row')
| |
− | :newline()
| |
− | :wikitext(self.options.caption or self:message('columns-header'))
| |
− | end
| |
− | | |
− | -- Show code
| |
− | if self.options.showcode then
| |
− | dataRow:tag('td')
| |
− | :newline()
| |
− | :wikitext(self:getInvocation('code'))
| |
− | end
| |
− | | |
− | -- Template output
| |
− | for i, obj in ipairs(self.templates) do
| |
− | if self.options.output == 'nowiki+' then
| |
− | dataRow:tag('td')
| |
− | :newline()
| |
− | :wikitext(self.options.before)
| |
− | :wikitext(self:getTemplateOutput(obj))
| |
− | :wikitext(self.options.after)
| |
− | :wikitext('<pre style="white-space: pre-wrap;">')
| |
− | :wikitext(mw.text.nowiki(self.options.before or ""))
| |
− | :wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
| |
− | :wikitext(mw.text.nowiki(self.options.after or ""))
| |
− | :wikitext('</pre>')
| |
− | elseif self.options.output == 'nowiki' then
| |
− | dataRow:tag('td')
| |
− | :newline()
| |
− | :wikitext(mw.text.nowiki(self.options.before or ""))
| |
− | :wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))
| |
− | :wikitext(mw.text.nowiki(self.options.after or ""))
| |
− | else
| |
− | dataRow:tag('td')
| |
− | :newline()
| |
− | :wikitext(self.options.before)
| |
− | :wikitext(self:getTemplateOutput(obj))
| |
− | :wikitext(self.options.after)
| |
− | end
| |
− | end
| |
− | | |
− | return tostring(root)
| |
− | end
| |
− | | |
− | function TestCase:renderDefault()
| |
− | local ret = {}
| |
− | if self.options.showcode then
| |
− | ret[#ret + 1] = self.templates[1]:getInvocation()
| |
− | end
| |
− | for i, obj in ipairs(self.templates) do
| |
− | ret[#ret + 1] = '<div style="clear: both;"></div>'
| |
− | if self.options.showheader then
| |
− | ret[#ret + 1] = obj:makeHeader()
| |
− | end
| |
− | if self.options.output == 'nowiki+' then
| |
− | ret[#ret + 1] = (self.options.before or "") ..
| |
− | self:getTemplateOutput(obj) ..
| |
− | (self.options.after or "") ..
| |
− | '<pre style="white-space: pre-wrap;">' ..
| |
− | mw.text.nowiki(self.options.before or "") ..
| |
− | mw.text.nowiki(self:getTemplateOutput(obj)) ..
| |
− | mw.text.nowiki(self.options.after or "") .. '</pre>'
| |
− | elseif self.options.output == 'nowiki' then
| |
− | ret[#ret + 1] = mw.text.nowiki(self.options.before or "") ..
| |
− | mw.text.nowiki(self:getTemplateOutput(obj)) ..
| |
− | mw.text.nowiki(self.options.after or "")
| |
− | else
| |
− | ret[#ret + 1] = (self.options.before or "") ..
| |
− | self:getTemplateOutput(obj) ..
| |
− | (self.options.after or "")
| |
− | end
| |
− | end
| |
− | return table.concat(ret, '\n\n')
| |
− | end
| |
− | | |
− | function TestCase:__tostring()
| |
− | local format = self.options.format
| |
− | local method = format and TestCase.renderMethods[format] or 'renderDefault'
| |
− | local ret = self[method](self)
| |
− | if self.options.collapsible then
| |
− | ret = self:makeCollapsible(ret)
| |
− | end
| |
− | for cat in pairs(self.categories) do
| |
− | ret = ret .. string.format('[[Category:%s]]', cat)
| |
− | end
| |
− | return ret
| |
− | end
| |
− | | |
− | -------------------------------------------------------------------------------
| |
− | -- Nowiki invocation class
| |
− | -------------------------------------------------------------------------------
| |
− | | |
− | local NowikiInvocation = {}
| |
− | NowikiInvocation.__index = NowikiInvocation
| |
− | NowikiInvocation.message = message -- Add the message method
| |
− | | |
− | function NowikiInvocation.new(invocation, cfg)
| |
− | local obj = setmetatable({}, NowikiInvocation)
| |
− | obj.cfg = cfg
| |
− | invocation = mw.text.unstrip(invocation)
| |
− | -- Decode HTML entities for <, >, and ". This means that HTML entities in
| |
− | -- the original code must be escaped as e.g. &lt;, which is unfortunate,
| |
− | -- but it is the best we can do as the distinction between <, >, " and <,
| |
− | -- >, " is lost during the original nowiki operation.
| |
− | invocation = invocation:gsub('<', '<')
| |
− | invocation = invocation:gsub('>', '>')
| |
− | invocation = invocation:gsub('"', '"')
| |
− | obj.invocation = invocation
| |
− | return obj
| |
− | end
| |
− | | |
− | function NowikiInvocation:getInvocation(options)
| |
− | local template = options.template:gsub('%%', '%%%%') -- Escape "%" with "%%"
| |
− | local invocation, count = self.invocation:gsub(
| |
− | self.cfg.templateNameMagicWordPattern,
| |
− | template
| |
− | )
| |
− | if options.requireMagicWord ~= false and count < 1 then
| |
− | error(self:message(
| |
− | 'nowiki-magic-word-error',
| |
− | self.cfg.templateNameMagicWord
| |
− | ))
| |
− | end
| |
− | return invocation
| |
− | end
| |
− | | |
− | function NowikiInvocation:getOutput(options)
| |
− | local invocation = self:getInvocation(options)
| |
− | return mw.getCurrentFrame():preprocess(invocation)
| |
− | end
| |
− | | |
− | -------------------------------------------------------------------------------
| |
− | -- Table invocation class
| |
− | -------------------------------------------------------------------------------
| |
− | | |
− | local TableInvocation = {}
| |
− | TableInvocation.__index = TableInvocation
| |
− | TableInvocation.message = message -- Add the message method
| |
− | | |
− | function TableInvocation.new(invokeArgs, nowikiCode, cfg)
| |
− | local obj = setmetatable({}, TableInvocation)
| |
− | obj.cfg = cfg
| |
− | obj.invokeArgs = invokeArgs
| |
− | obj.code = nowikiCode
| |
− | return obj
| |
− | end
| |
− | | |
− | function TableInvocation:getInvocation(options)
| |
− | if self.code then
| |
− | local nowikiObj = NowikiInvocation.new(self.code, self.cfg)
| |
− | return nowikiObj:getInvocation(options)
| |
− | else
| |
− | return require('Module:Template invocation').invocation(
| |
− | options.template,
| |
− | self.invokeArgs
| |
− | )
| |
− | end
| |
− | end
| |
− | | |
− | function TableInvocation:getOutput(options)
| |
− | if (options.template:sub(1, 7) == '#invoke') then
| |
− | local moduleCall = mw.text.split(options.template, '|', true)
| |
− | local args = mw.clone(self.invokeArgs)
| |
− | table.insert(args, 1, moduleCall[2])
| |
− | return mw.getCurrentFrame():callParserFunction(moduleCall[1], args)
| |
− | end
| |
− | return mw.getCurrentFrame():expandTemplate{
| |
− | title = options.template,
| |
− | args = self.invokeArgs
| |
− | }
| |
− | end
| |
− | | |
− | -------------------------------------------------------------------------------
| |
− | -- Bridge functions
| |
− | --
| |
− | -- These functions translate template arguments into forms that can be accepted
| |
− | -- by the different classes, and return the results.
| |
− | -------------------------------------------------------------------------------
| |
− | | |
− | local bridge = {}
| |
− | | |
− | function bridge.table(args, cfg)
| |
− | cfg = cfg or mw.loadData(DATA_MODULE)
| |
− | | |
− | local options, invokeArgs = {}, {}
| |
− | for k, v in pairs(args) do
| |
− | local optionKey = type(k) == 'string' and k:match('^_(.*)$')
| |
− | if optionKey then
| |
− | if type(v) == 'string' then
| |
− | v = v:match('^%s*(.-)%s*$') -- trim whitespace
| |
− | end
| |
− | if v ~= '' then
| |
− | options[optionKey] = v
| |
− | end
| |
− | else
| |
− | invokeArgs[k] = v
| |
− | end
| |
− | end
| |
− | | |
− | -- Allow passing a nowiki invocation as an option. While this means users
| |
− | -- have to pass in the code twice, whitespace is preserved and < etc.
| |
− | -- will work as intended.
| |
− | local nowikiCode = options.code
| |
− | options.code = nil
| |
− | | |
− | local invocationObj = TableInvocation.new(invokeArgs, nowikiCode, cfg)
| |
− | local testCaseObj = TestCase.new(invocationObj, options, cfg)
| |
− | return tostring(testCaseObj)
| |
− | end
| |
− | | |
− | function bridge.nowiki(args, cfg)
| |
− | cfg = cfg or mw.loadData(DATA_MODULE)
| |
− | | |
− | local code = args.code or args[1]
| |
− | local invocationObj = NowikiInvocation.new(code, cfg)
| |
− | args.code = nil
| |
− | args[1] = nil
| |
− | -- Assume we want to see the code as we already passed it in.
| |
− | args.showcode = args.showcode or true
| |
− | local testCaseObj = TestCase.new(invocationObj, args, cfg)
| |
− | return tostring(testCaseObj)
| |
− | end
| |
− | | |
− | -------------------------------------------------------------------------------
| |
− | -- Exports
| |
− | -------------------------------------------------------------------------------
| |
− | | |
− | local p = {}
| |
− | | |
− | function p.main(frame, cfg)
| |
− | cfg = cfg or mw.loadData(DATA_MODULE)
| |
− | | |
− | -- Load the wrapper config, if any.
| |
− | local wrapperConfig
| |
− | if frame.getParent then
| |
− | local title = frame:getParent():getTitle()
| |
− | local template = title:gsub(cfg.sandboxSubpagePattern, '')
| |
− | wrapperConfig = cfg.wrappers[template]
| |
− | end
| |
− | | |
− | -- Work out the function we will call, use it to generate the config for
| |
− | -- Module:Arguments, and use Module:Arguments to find the arguments passed
| |
− | -- by the user.
| |
− | local func = wrapperConfig and wrapperConfig.func or 'table'
| |
− | local userArgs = require('Module:Arguments').getArgs(frame, {
| |
− | parentOnly = wrapperConfig,
| |
− | frameOnly = not wrapperConfig,
| |
− | trim = func ~= 'table',
| |
− | removeBlanks = func ~= 'table'
| |
− | })
| |
− | | |
− | -- Get default args and build the args table. User-specified args overwrite
| |
− | -- default args.
| |
− | local defaultArgs = wrapperConfig and wrapperConfig.args or {}
| |
− | local args = {}
| |
− | for k, v in pairs(defaultArgs) do
| |
− | args[k] = v
| |
− | end
| |
− | for k, v in pairs(userArgs) do
| |
− | args[k] = v
| |
− | end
| |
− | | |
− | return bridge[func](args, cfg)
| |
− | end
| |
− | | |
− | function p._exportClasses() -- For testing
| |
− | return {
| |
− | Template = Template,
| |
− | TestCase = TestCase,
| |
− | NowikiInvocation = NowikiInvocation,
| |
− | TableInvocation = TableInvocation
| |
− | }
| |
− | end
| |
− | | |
− | return p
| |