Module:Data/testcases

local p = { stat_order = {'HP','MP','ATK','DEF','MAG','SPR'}, equipment = { _class ={'Weapon', 'Armor', 'Accessory'}, _plural = {Clothes = 'Clothes', Staff = 'Staves'}, Dagger = 1, Sword = 1, ["Great Sword"] = 1, Katana = 1, Staff = 1, Rod = 1, Bow = 1, Axe = 1, Hammer = 1, Spear = 1, Instrument = 1, Whip = 1, ["Throwing Weapon"] = 1, Gun = 1, Mace = 1, Fist = 1, ["Light Shield"] = 2, ["Heavy Shield"] = 2, Hat = 2, Helm = 2, Clothes = 2, ["Light Armor"] = 2, ["Heavy Armor"] = 2, Robe = 2, Accessory = 3 },	ability = { _class = {"Special Ability", "Magic Ability"}, Active = 1, Passive = 1, White = 2, Black = 2, Green = 2, Blue = 2 },	ailment = { Poison = 1, Blind = 1, Sleep = 1, Silence = 1, Paralyze = 1, Confuse = 1, Disease = 1, Petrify = 1 },	killer = { --Special thanks to FencerTJ for category names (except he forgot humans) Avians = 'bird', Plantoids = "plant", Aquatics = "aqua", Beasts = "brute", Demons = "cysidus", Dragons = "dra[cg]o", Fairies = "[sf][pa]ir",--spirit or fairy Insects = "bug", Machinas = "m[ae]ch", Stones = "lith", Reapers = "undead", Humans = "man-?" },	patterns = { --Not all patterns here. Just those used more than once. wikilink = '%[%^%[%+%]%]', only = '%(%[%[([%w ]+)%]%] only%)', enable = '([^\n]*Enables access[^\n]-: )([^\n]+)', multi_break = '[^<]?%s-/?%s*([^>]?.-[^<])%s*/%s-[^>]-', rng_enable = '\n%(%d+%%%) +[^\n]+: Enables? ',		event = '%(%[?%[?Event[^,]-%)%s*%[?%[?' },	template = { evade = 'Chance to evade %s damage taken (%s)' } }

local result = {} function result:_(v) --simplified version of mw:en:Module:Buffer if v and v ~= '' then table.insert(self, v) end return self end local esc_seq = string.char(127,4,127,2)

function p.item(frame) local args, p_args, flags, currentTitle, namespace, invokePage = {}, frame:getParent.args, {} if p_args.page or args.page then --No worries. Scribunto is sandboxed, so overwriting mw.title.getCurrentTitle won't affect any other #invokes local emulate = mw.title.new(p_args.page or args.page) mw.title.getCurrentTitle = function return emulate end end for k, v in pairs(frame.args) do	--Basic cleaning of arguments and argument grouping v = mw.text.trim(v) if v and v~='' and v~='-' then args[k] = v end end local stats_sorted, stats = {}, {HP = {}, MP = {}, ATK = {}, DEF = {}, MAG = {}, SPR = {}} for k, v in pairs(stats) do	--grab percents and integers in the form such that DEF+7, DEF+50% is entered as 'DEF=7+50%' if args[k] then v[2] = args[k]:match'%-?%d+%%' if v[2] then v[1] = args[k]:match'^%+?(%-?%d+)%+'--tolerate ATK=+10 else table.insert(v, args[k]:match'^%+?(%-?%d+)$') end end end if p.equipment[args.type] then for i = 1, 2 do		--show percentage stats after integer stats for _, v in ipairs(p.stat_order) do--this line controls order of stats table.insert(					stats_sorted,					stats[v][i] and (i==1 and '%s+%s' or '%s +%s'):format(v, stats[v][i])				) end end end local function link(v, hide_disambiguation) --Page" return v and (v:find(p.patterns.wikilink) and v			or hide_disambiguation and v:find'%b' and v:gsub('([^%(]+) %(([^%)]+)%)', '%1')			or v:gsub('^(.-) -( ?\\?%+?%d?)$', function(a, b)					if v == invokePage then a, b = v, '' elseif b:sub(2, 2) ~= '+' or not tonumber(b:sub(3, 3)) then a, b = a .. b, '' end return ('%s%s'):format(a, b):gsub('\\%+', '+') end, 1)) end local function tooltip_link(v, hide_disambiguation) --Makes tooltip link. if v == currentTitle then return "'''" .. v .. "'''" end local fetch, only = {title = ':' .. v, args = {'effect', 'ability', mode = 'fetch'}} only = frame:expandTemplate(fetch):match('%[%[' .. args.name:gsub('%p', '%%%1') .. '%f[|%]][^%]]-%]%] *%((.- only)%)') or frame:expandTemplate(				fetch,				rawset(fetch.args, 2, nil),				rawset(fetch.args, 1, 'warning')			):match'[^,]+ only' only = only and ' (' .. only .. ')' or '' return ('') :format(v, hide_disambiguation and '' or '|full_pg=1') .. only end local function link2tooltip(v, hide_disambiguation) --checks all links for Module:Data and replaces them with tooltips when possible. Only use when non-Module:Data pages may be passed for lookback, wikilink, page in v:gmatch'(.?)(%[%[([^:#][^|%]]+)|?[^%]]*%]%])' do			if lookback ~= '(' and not (page:find':[^ _]' or page:find'#' or stats[page:match'(.+) %+%d'] or mw.title.new(page).redirectTarget) then				local tooltip = frame:preprocess()				if tooltip:lower:find'#invoke:data%s*&#124;%s*item' then					v = v:gsub(wikilink:gsub('%p', '%%%1'), tooltip_link(page, hide_disambiguation):gsub('%p', '%%%1'))				end			end		end		return v	end	local function split_link(v, hide_disambiguation, sep, alt_link_func)	--Splits v by commas and joins with sep (or comma if omitted). Links using link unless passed alt_link_func		local t = {}		for s in mw.text.gsplit(v:gsub("\\,", esc_seq), ',%s*') do			s = mw.text.trim(s)			if s ~=  then table.insert(t, (alt_link_func or link)(s, hide_disambiguation)) end		end		return table.concat(t, sep or ', '):gsub(esc_seq, ',')	end	local function multi_split(v, head_format, sep, headless_sep) --Splits by ';' and passes each to split_link. If first item prefixed by 'text:', treats 'text' as group name local result = {_=result._} for x in mw.text.gsplit(v, ';') do			local group, list = x:gsub('\\:', esc_seq):match'^([^:]+): -(.+)$' group, list = group:gsub(esc_seq, ':'), list:gsub(esc_seq, ':') result :_(group and head_format:format(group) or headless_sep) :_(split_link(list or x, nil, group and sep or headless_sep, group == 'Equipment' and tooltip_link)) :_'\n' end return table.concat(result) end local function ailment_link(v, hide_disambiguation) --Autolinks ailments. When hide_disambiguation is nil or true, also autolinks non-ailments. local status, chance = v:match'^([^%(]-) ?%(([^%)%(]-)%)'		if status and (p.ailment)[status] then			return ('%s (%s)'):format(status, status, chance)		elseif status and v:find' status ailments? ' then return (v:gsub(' (status ailments?) ', ' %1 ', 1)) end		if hide_disambiguation == false then return v end--for args.resist		return link(v, hide_disambiguation)	end	local function materia_ailment_link(v)	--auto-links ailments for materia.		if v then for i, pattern in ipairs{			'Cures? [^\n<]+ to ',			'Inflicts? [^\n<]+ %(', 'Increase resistance to [^\n<]+ %(',			'Increase [^\n<]+ resistance %(', '[Rr]emove ' } do if v:find(pattern) then for ailment in pairs(p.ailment) do				v = v:gsub(					" ('*)" .. ailment:lower,					(' %s1%s'):format('%', ailment, ailment:lower),					1				) end flags.groupStatusCure, v = flags.groupStatusCure or i == 1 and v:find'all allies', v:gsub(" ('*)(status ailments?)('*) ", ' %1%2%3 ', 1) break end end end return v	end local function icon(v) --Makes item image. If passed a string, links to it. If passed true, links to the #invoke page (not name param) return (v and  or ):format(			args.image or ('Icon-%s.png'):format(args.name),			args.name or '',			v == true and invokePage or v		) end local function recipe return args.rec_mat and link2tooltip(frame:preprocess(split_link(args.rec_mat, nil, ' ', function(v)			local item, quantity, found_img = v:match'(.-) ?%(?(%d+)%)?$'			found_img = mw.text.decode(frame:preprocess()):match'|%s*image%s*=%s*([^<]-%.png)'			return ():format(item or v, quantity or 1, found_img and '|image=' .. found_img or '')		end)), true) end local function widget_sort_stats(close) --provide html element attributes for Widget:Sort_Stats local sortdata = {} for k, v in pairs(stats) do if v[1] or v[2] then table.insert(sortdata, 'data-'..k..'="'..args[k]..'"') end end if args.evade then table.insert(sortdata, 'data-evade="'..args.evade..'"') end return ' style="text-align:left" class=stats_cell ' .. table.concat(sortdata, ' ') .. (close or ' | ') end local function stats_cell(mode_tooltip) --generates stats cell used on category/shop pages for equipment; if mode_tooltip, hide_disambiguation and return stats table without concat local only, lines = args.warning and args.warning:match"Useable by '?'?'?(%a+)'?'?'? only", {stats_sorted[1] and table.concat(stats_sorted, ', '), _=result._} lines :_(args.element and 'Element: ' .. args.element) :_(args.resist and 'Resistance: ' .. split_link(args.resist, false, nil, ailment_link)) :_(args.effect and 'Effect: ' .. link2tooltip(split_link(args.effect, mode_tooltip, nil, ailment_link), mode_tooltip)) :_(args.ability and 'Ability: ' .. link2tooltip(split_link(args.ability, mode_tooltip), mode_tooltip))--use tooltip_link after module used on all ability pages :_(args.warning and (only and ("%ss only"):format(mw.getContentLanguage:ucfirst(only)) or ("%s"):format(args.warning):gsub(", ?", " ") ))			:_(args.used_by and 'Exclusive: ' .. split_link(args.used_by)) return mode_tooltip and lines or lines[1] and table.concat(lines, ' ') end local function randomized_table(v) --takes a string with multiple lines (separated by or \n) and appends effects to each line from each double bullet in args.ability. local s = v:gsub('', '\n') --if not s:find'\n(%([^\n]+)' then return v end		return args.ability and s:find'\n%(' and ' ', 1)					:gsub("'", )					:gsub('%%', '%%%%')					:gsub( "\n(%([^\n]+)",						' %1  -  %%s'					) :gsub('(%) +%w+[- ] *%w+[- ] *)(%(?[%w]+)', '%1 %2') :format(select(2, unpack(mw.text.split(materia_ailment_link(args.ability):gsub('\n', ''), "%*.-%*%*")))) .. (not v:find' ' and '  ' or '') or v	end local function effect_auto_link(v) --autolink killers, equipment masteries and wields, and status ailments in materia effects if v:find' killer ' or v:find' against ' then for cat, initial in pairs(p.killer) do local match = v:match(" '*(" .. cat:sub(1, -1):lower .. "%l?)") or v:match(" '*(" .. initial .. "%l*)") v = match and v:gsub(match, ('%s'):format(cat, match), 1) or v			end elseif v:find'Increase .- equipped with' or v:find'Allow use' then for cat in pairs(p.equipment) do local match = v:match("'*([%a,']+ +'*" .. cat:lower .. (cat:sub(-1)=='y' and '?i?e?s?)' or 's?)'))					or cat == 'Staff' and						v:match("'*([%a,']+ +'*staves)")				v = match and match:sub(1, 5) ~= 'great' and v:gsub( match:gsub("^.- '*", '', 1), ('%s'):format(						(p.equipment._plural[cat] or cat:gsub('y$', 'ie') .. 's'						),						match:gsub("^.- '*", '', 1)					), 1				) or v			end		else v = materia_ailment_link(v) end		return v	end	local function passive_stat_boost(effects)		local list, group, last_percent = {[true] = {}, [false] = {}}, {[true] = {}, [false] = {}}, {[true] = {}, [false] = {}}		for i = 1, #p.stat_order do			if args[p.stat_order[i]] then				local positive = tonumber(args[p.stat_order[i]]:match"^'*(%-?%d*)%%'*$") > 0				if group[positive][1] then					if last_percent[positive] ~= args[p.stat_order[i]] then						table.insert(list[positive], ('%s (%s)'):format(table.concat(group[positive], '/'), last_percent[positive]))						group[positive], last_percent[positive] = {}, args[p.stat_order[i]]					end				else last_percent[positive] = args[p.stat_order[i]] end				table.insert(group[positive], p.stat_order[i])				effects[args[p.stat_order[i]]] = (effects[args[p.stat_order[i]]] or 0) + 1			end		end		for _, positive in ipairs{false, true} do			if group[positive][1] then table.insert(effects, 1, (('%screase %s%s%s (%s)') :format(						positive and 'In' or 'De',						table.concat(list[positive], ', '),						list[positive][1] and ' and ' or '',						table.concat(group[positive], '/'),						last_percent[positive]					) :gsub('%(%-', '(') ))			end end return effects end local function materia_effects(mode_tooltip) --generates ability materia effects. If passed true as, returns effects as a table without concat. local effects, transcluded, show_table = args.effect and mw.text.split(args.effect, '%s*\n%s*') or {}, invokePage ~= currentTitle, args.ability and (args.type ~= 'Passive' or args.mode == 'category') for k, v in ipairs(effects) do			local enable, skills = v:match(p.patterns.enable) if enable then effects[k] = args.effect:match(p.patterns.wikilink) and effects[k]:gsub('(\\?)\\,', '%1,') or enable .. split_link(skills) while v:find((effects[k + 1] or ''):match'%[(.+)%]%S*%s*=' or '^%$$') do table.remove(effects, k + 1) end elseif transcluded and args.ability and (v:find':[\n<][b%(][r%d]' or v:find'[Cc]ounter') then				if v:gsub('', '\n'):find'^[^\n]+:\n%(' then effects[k] = show_table and randomized_table(v) or v				elseif args.type == 'Passive' then for name, effect in args.ability:gmatch'%* *([^\n]-) *\n%*%*%s*([^\n]+)' do						effects[k] = v:gsub(							name:gsub('%p', '%%%1'), 							'%1 ' .. effect:gsub('%%', '%%%1'):gsub('Randomly use:', ) .. 						) end end else effects[k] = effect_auto_link(v) end end

--Note: Below inserted in backwards order to the front of the effects table if args.evade_m then table.insert(effects, 1, p.template.evade:format('magic', args.evade_m)) end if args.evade_p then table.insert(effects, 1, p.template.evade:format('physical', args.evade_p)) end passive_stat_boost(effects) if args.used_by and not args.learn and args.mode and args.mode ~= 'exclusiveFX' then table.insert(effects, 'Exclusive: ' .. split_link(args.used_by)) end return mode_tooltip and effects or effects[1] and table.concat(effects, ' '):gsub(' ', '%1') end local function type_link(parens) --makes Category:Weapons (Category:Guns). If parens is true, places them in parenthesis return p.ability[args.type] and ''				.. p.ability._class[p.ability[args.type:sub(1, -2) .. (p.ability[args.type] == 1 and 'ies (%s)]]' or 'ies]]') :format(args.type) or p.equipment[args.type] and (parens and ' (%s)' or '%s'):format(					p.equipment._plural[args.type] or args.type:gsub('y$', 'ie') .. 's',					args.type				) or args.usage and 'Category:Materials' or 'Miscellaneous Items' end local function multi_break(s) --takes args.hits or arg.MP_cost and splits them by backslash or semicolon, inserting same number of breaks per item as in args.ability if s and not tonumber(s) and s:find(p.patterns.multi_break) then local hits = ('/ ' .. s .. ' /'):gmatch(p.patterns.multi_break) s = {} for v in mw.text.gsplit(				args.ability					or args.effect:gsub('\nIf used after[^\n]+:', ' '):gsub(':\n', ' '),				args.ability and '\n%*%*' or '\n'			) do				table.insert(s, (' '):rep(select(2, v:gsub('', '')) + 1)) table.insert(s, (hits)) end if not args.ability then table.remove(s, 1) end s = table.concat(s) end return '\n| ' .. (s or '-') end local frame_tooltip = 	local function attack_frames(hits, v)	--[[		pass args.hits as first param. Reads args.atk_frm and makes tooltip.		Also recursively splits and tooltips args.atk_frm by / or ; characters if such are present in args.hits			hits = hits ~=  and hits ~= '-' and hits		if hits and args.atk_frm then			if not v and hits:find(p.patterns.multi_break) then				local split = {					hits = ('/ ' .. hits .. ' /'):gmatch(p.patterns.multi_break), 					a_frames = ('/ ' .. args.atk_frm .. ' /'):gmatch(p.patterns.multi_break)				}				for hit_count in split.hits do					local animation = split.a_frames					table.insert(split, animation and tonumber(hit_count) and attack_frames(hit_count, animation) or hit_count)				end				hits = table.concat(split, ' / ')			else				local output, frames = {_=result._}, mw.text.split(v or args.atk_frm, ' ?[,%-] ?')				output					:_(frame_tooltip)					:_(#frames > 1 and 'Frame delay: ' or 'Attack frame: ')					:_(frames[1])				if #frames > 1 then					for i = 2, #frames do output:_(-tonumber(frames[i]) + tonumber(frames[i - 1])) end					output						:_' Attack frames: '						:_(table.concat(frames, '-'))				else output:_(table.concat(frames, nil, 2, #frames)) end				hits = table.concat(output:_']]') end end return hits end local froms, hasExclusiveFX, learned = { --		{parameter name, how-to-obtain title, flag}		Set flag for sources that would not place an ability in Category:Items and to exclude source from how-to-obtain cell for mode=category		1 = list parameter for equipable sources		2 = non-equipment sources or non-list parameter {'drop', 'Dropped from'}, {'steal', 'Stolen from'}, {'explore', 'Collected from'}, {'shop', args.quartz and 'Fat Chocobo' or 'Shop'}, {'recipe', 'Recipe'}, {'chest', 'Chest'}, {'reward', 'Reward'}, {'equip', 'Equipment', 1}, {'materia', 'Ability Materia', 1}, {'learn', 'Learned by', 2}, {'esper', 'Esper', 2},--because not a comma-separates list {'enable', 'Enabled by', 2} },		args.effect and--do not check used_by here args.effect:match(p.patterns.only) or args.ability and args.ability:match(p.patterns.only)--use match not find currentTitle, namespace, invokePage = mw.title.getCurrentTitle.fullText, mw.title.getCurrentTitle.namespace, frame:getParent:getTitle args.name = args.name or not invokePage:find':' and invokePage args.mode = args.mode or p_args.mode or currentTitle == 'Module:Data' and 'demo' or currentTitle == 'Ability Awakening' and 'awaken' or (namespace == 14				or p.ability[args.type] and ( currentTitle == 'Ability Materia' or currentTitle == (p.ability[args.type] == 1 and						('Special Abilities (%s)'):format(args.type)						or 'Magic Abilities'					) )			) and 'category' or invokePage ~= currentTitle and (			(args.recipe or args.reward) and (args.recipe or args.reward):find( p.patterns.event .. currentTitle:gsub('_', ' '):gsub('%p', '%%%1') ) and 'event'			or (hasExclusiveFX == currentTitle or args.used_by and args.used_by:find(currentTitle)) and 'exclusiveFX'			or p.equipment[args.type] and 'item'			or 'typeless'		) local function format_skill_args --simplifies pattern matching by allowing newlines and to be used interchangeably args.effect = args.effect and args.effect :gsub('(([^\n]+)\n%', function(match, prev_line) if prev_line:find'=' then return match end return prev_line .. ' ('			end) :gsub('\n+(If used after)', '\n %1') if args.ability and not args.ability:find('\n*', 1, 1) then --automatically takes names from effect param for randomized abilities and transcludes effect if line == '[data]' local a = mw.text.split(args.ability, '\n') if a[2] then args.ability = {} for name in args.effect:gmatch'%([^\n<]+%) ([^\n<]+)' do					local abil = table.remove(a, 1) if abil == '[data]' then abil = frame:expandTemplate{title = ':' .. name:match'%[%[(.-)[|%]]', args = {'STATS', mode = 'fetch'}} end table.insert(args.ability, ('*%s\n**%s'):format(name, abil or 'NO DATA')) end args.ability = table.concat(args.ability, '\n') end end end local function format_split_args(v) return v and v:gsub(' ?; ?', ' / '):gsub(' +', ' '):gsub('^ ?/', '- /'):gsub('/ /', '/ - /') end if p.ability[args.type] then learned = not p_args.mode and args.learn and invokePage ~= currentTitle and {args.learn:match('\n|? *%[%[' .. currentTitle .. '.-|?[$|]?%s*(<[^\n]+>)%s*|?[$|] ?(%d%d?%d?)')} args.hits, args.atk_frm, args.MP_cost = args.hits and (				args.hits:find'^[dD][^/;]*$' and					((args.mode or learned and learned[1]) and 'D' or 'Default unit attack')					or format_split_args(args.hits)			), args.atk_frm and format_split_args(args.atk_frm), args.MP_cost and format_split_args(args.MP_cost) or args.type == 'Active' and 0 format_skill_args end if learned and learned[1] then --Display when transcluded onto a page listed in args.learn local unmerge, hits = args.type ~= 'Passive' or p_args[1] == 'unmerge' result :_'|-\n|' :_(learned[1]) :_'\n|' :_(learned[2]) :_'\n|' :_(icon(true)) :_'\n|style="text-align:left"|' :_(link(args.name, true)) :_'\n|style="text-align:left"' :_(unmerge and '|' or ' colspan=3|') :_(materia_effects:gsub("(%) with [^\n:]+): ? ?%([^\n]+", '%1'))--save random counter info for mode=conditional if unmerge then result :_(multi_break(attack_frames(args.hits))) :_(multi_break(args.MP_cost)) end elseif args.mode and args.mode ~= 'demo' then local function BADGE return ('%s %s (%s) '):format(				icon(true),				link(invokePage ~= args.name and invokePage .. '|' .. args.name or invokePage),				type_link			) end local function STATS(b) local v = (p.equipment[args.type] and stats_cell or materia_effects) return not (b or p_args.sep or args.mode == 'fetch' or p_args[1] == 'STATS') and v and widget_sort_stats .. v or v		end local function extend(sep) --Reads customization parameters from parent frame. Def = default value, sep = separator local magic_words = { STATS = {STATS}, ICON = {icon, true}, TYPE = {type_link}, LINK = {link, table.concat({invokePage, args.name}, '|')}, RECIPE = {recipe}, BADGE = {BADGE} }			for _, v in ipairs(p_args) do				_ = mw.text.trim(mw.text.killMarkers(v)) if _ ~= '' then local a, b = v:gsub('\\@', esc_seq):match'^(.-)@(.*)' result:_(sep or '\n| '):_(						a and (a~='' and ((args[a] or magic_words[a]) and 								b:gsub(esc_seq, '@'):format( frame.args[a] ~= '' and frame.args[a] or args[a] or magic_words[a][1](unpack(a == 'STATS' and {nil, b} or magic_words[a], 2)) )								or '-') or b and b:gsub(esc_seq, '@'))						or frame.args[_] ~= '' and frame.args[_]--return unprocessed non-empty args						or args[_]--for auto args						or magic_words[v] and magic_words[v][1](unpack(magic_words[v], 2))						or p_args.default or '-'					) end end end if args.mode == 'category' then --Display when on category page local obtain, equipped = {}, {} for i = 1, #froms do				if args[froms[i][1]] and froms[i][3] ~= 2 then table.insert(froms[i][3] and equipped or obtain, 						froms[i][3] and							split_link(args[froms[i][1]]:gsub('%]%] %([^%)]+%)', ']]'), nil, froms[i][3] and ' ') or ("%s: %s"):format(								froms[i][2],								split_link(args[froms[i][1]])					))				end			end			if args.esper then				for esper in args.esper:gmatch'%[%^:]-%]%]' do table.insert(equipped, esper) end			end			table.insert(obtain, args.trust and ("Trust: [[%s"):format(args.trust))			table.insert(obtain, args.obtain and multi_split(args.obtain, "%s: ", ', ', ''))			result				:_'|-\n|'				:_(tonumber(p_args.rowspan) and ('rowspan=%s style="text-align:center"| %s ||') :format(p_args.rowspan, args.mag_lv or '') )				:_(args.image and p.ability[args.type] and 'data-sort-value=' .. args.image:match'_(%d+)%.')				:_((' align=center|%s|| %s || '):format(					icon(true),					link(args.name, true)				))				:_((p.equipment[args.type] or args.type == 'Passive') and widget_sort_stats)				:_(p.equipment[args.type] and stats_cell or materia_effects)				:_' \n| '				:_(p.ability[args.type] and result :_(args.MP_cost and ('style="text-align:center"| %s ||'):format(args.MP_cost) or '') :_(equipped[1] and table.concat(equipped, ' ') or '-') :_' || '					and nil )				:_(obtain[1] and table.concat(obtain, ' ') or '-')			extend		elseif args.mode == 'exclusiveFX' then		--Display when current page title matches name of the unit which benefits from an exclusive effect			result				:_(p_args[1] ~= 'rowonly' and ("{| class=wikitable style='text-align:center;width:100%'\n!" ..					(p.equipment[args.type] and "Icon!!Name!!Type!!Effect" or "style='width:50px'|Icon!!style='width:130px'|Name!!Effect!!style='width:50px'|Hitsundefined!!style='width:50px'|MP")				))				:_"\n|-\n|"				:_(icon(true))				:_'\n| '				:_(link(args.name, true))				:_'\n|'				:_(p.equipment[args.type] and type_link ..'\n|')				:_(widget_sort_stats)				:_(p.equipment[args.type] and (stats_cell or '')--in case stats_cell returns nil or table.concat{ materia_effects, multi_break(attack_frames(args.hits)), multi_break(args.MP_cost) }				)			if p_args[1] ~= 'rowonly' then result:_'\n|}' end		elseif args.mode == 'conditional' then			local function conditional_row(rowspan, activator)				result					:_'|-\n|colspan=3'					:_(rowspan and result:_' rowspan=' and rowspan)					:_'|Activated by '					:_(activator ~= '' and activator or args.enable and split_link(args.enable) or link(args.name) )					:_'||style="text-align:left"| '			end			local ability_count, isPassive, randomized_counter, unmerge =				args.ability and select(2, ('\n'..args.ability):gsub("%*'''", 0)),				args.type == 'Passive',				args.effect and args.effect:match"Chance to counter[^\n]+with '?'?'?(.-)'?'?'?:%(%d+%%", p_args[1] == 'unmerge' if args.ability then conditional_row if randomized_counter then result :_(randomized_counter) :_'\n|style="text-align:left"| ' :_(randomized_table(materia_effects):gsub("Chance to counter[^\n]-:", 'Randomly use:')) :_(multi_break(attack_frames(args.hits))) :_(multi_break(args.MP_cost)) else if ability_count > 0 then table.insert(result, 2, ' rowspan=' .. ability_count) end if args.ability and not args.hits and args.ability:find'>Hits:' then args.hits, args.MP_cost = {} for line in args.ability:gmatch'\n%*%*[^\n]+' do							local a, b, h = line:find'Hits: *(.+)' if a then local offset = args.ability:find(line, 3, 1) args.ability = args.ability:sub(1, offset + a - 2) .. args.ability:sub(offset + b)								table.insert(args.hits, h)							else table.insert(args.hits, '-') end end args.hits = table.concat(args.hits, ' / '):gsub('Default unit attack', 'D') end result :_(args.ability and materia_ailment_link(args.ability)							:gsub("\n%* *(.-)", '|-\n|style="text-align:left"| %1')							:gsub("%* *(.-)", '%1')							:gsub('%*%*', '| style="text-align:left"' .. (isPassive and not (args.hits or args.MP_cost or unmerge) and									' colspan=3| '									or '| '							))						) if unmerge or args.hits or args.MP_cost then result :_(multi_break(attack_frames(args.hits))) :_(multi_break(args.MP_cost)) end end elseif args.enable then if p_args.rowspan == '0' then result:_'\n|-\n|' else conditional_row(tonumber(p_args.rowspan)) end result :_(link(args.name, true)) :_'\n|style="text-align:left"| ' :_(materia_effects) :_(multi_break(attack_frames(args.hits))) :_(multi_break(args.MP_cost)) else for line in args.effect:gsub('%s*%(', '\n('):gmatch'[^\n]*Enable[^\n]+' do if not line:find'%].*=' then local skills, ability_count = (select(2, line:match(p.patterns.enable)) or line:match' of (.+)') :gsub('([^\\]), *', '%1@$#') result:_(result[1] and '\n') conditional_row(					ability_count > 0 and ability_count + 1,					(line:gsub('\\:', esc_seq):match'^%(%d+%%%) *(.-):' or ''):gsub(esc_seq, ':')				) for k, v in ipairs(mw.text.split(skills, '@$#')) do--no gsplit because k needed if k > 1 then result:_'\n|-\n|style="text-align:left"| ' end local title, page = (v:match'%[%[(.-)[|%]\]' or v):gsub('\\,', ',') page = mw.title.new(title).exists and frame:expandTemplate{title = ':' .. title, args = {page = title}} :gsub('Default unit attack', 'D') local override, page_effect = frame.args.effect:match('\n%s*effect%[' .. title:gsub('%p', '%%%1') .. '%]%s*=%s*([^\n]+)') or frame.args.effect:match('\n%s*%[' .. title:gsub('%p', '%%%1') .. '%]effect%s*=%s*([^\n]+)'), page and page:match("Effect:\n:([^*=]+)\n") args.effect = (override or page_effect or 'ERROR - No page or override found for ' .. link(title)):gsub('\\n', '\n') result :_(page and link(v, true) or v)--when all abilities use Data, replace all with: --:_('') 						:_'\n|style="text-align:left"| ' :_(page and override and ' ') :_(args.effect) :_(override and page_effect and 							" Automated Warning A page exists for "							.. link(v)							.. " but an override has been set on "							.. link(invokePage)							.. ". If the effect shown is the same as the following, please remove the override:  ") --args.atk_frm = args.effect:match('\n%s*atk_frm%[' .. title:gsub('%p', '%%%1') .. '%]%s*=%s*([^\n]+)') or args.atk_frm for i, m in ipairs{{'Hits', 'hits'}, {'MP', 'MP_cost'}} do						result:_(multi_break( frame.args.effect:match('\n%s*' .. m[2] .. '%[' .. title:gsub('%p', '%%%1') .. '%]%s*=%s*([^\n]+)') or frame.args.effect:match('\n%s*%[' .. title:gsub('%p', '%%%1') .. '%]' .. m[2] .. '%s*=%s*([^\n]+)') or page and page:match("" .. m[1] .. ": ([^\n]+)") or '-' ))					end end end end end elseif args.mode == 'awaken' then local function bold(v) return v and ((v:find"" or v:find'') and v or ("%s'"):format(v)) or  end local function unbold(v, manual_bold)--removes striked text and all tags except those that start with a t (table) if manual_bold then return v end return v and (v					:gsub("", )					:gsub('  ' ', '\n')					:gsub(' ', '')					:gsub('%b<>', function(tag) if tag == ' ' or tag:find'^</?t' then return tag elseif tag:sub(2, 3) == 'br' then return '\n' end return '' end)					:gsub('font-weight:bold;', '')				) end local function calculate_effects return unbold(materia_effects) end local unit, o_args, passive_stats, effects, new_effects = p_args[1] ~= 'table' and p_args[1] ~= 'conditional' and p_args[1] or currentTitle == 'Ability Awakening' and 'Error - unspecified unit', {},				{},				unbold(materia_effects):gsub('\n', ' ') new_effects, passive_stats.lines = effects, passive_stat_boost{} local function parse_awakening(input) local to_replace, to_append, recalc, manual_bold = {}, {} if input then for _, v in next, {'effect', 'ability'} do _ = '%s+' .. v .. '%s*=%s*([^=]+%f[\n])([^=]+=?)' local m, m2, m3 = ('\n' .. input):match(_) if not m then _ = '%s+' .. v .. '%s*=%s*([^\n]*)' m3 = ('\n' .. input):match(_) end if m or m3 then input, args[v], o_args[v], recalc, manual_bold = ('\n' .. input):gsub(_, m2 and m2:find'=' and '%2' or ''), (m3 or (m2:find'=' and m or m .. m2):gsub('\n%(', ' (')):gsub('\\n', '\n'), args[v], true, manual_bold or (m or m3):find"'''" or (m or m3):find"<b>" end end end input = input and mw.text.split(input, '%s*\n%s*') or {'awk_mat=NO DATA,0'} for i = 1, #input do					if i == 1 and args[input[i]] then parse_awakening(args[input[i]]) else local diff = mw.text.split(input[i], '%s*=>%s*') if diff[2] then table.insert(to_replace, {diff[1]:gsub('\\n', '\n'), diff[2]:gsub('\\n', '\n')}) else diff = mw.text.split(input[i], '%s*=%s*') if diff[2] then diff[2] = diff[2]:gsub('\\n', '\n') if diff[2] == '' then diff[2] = nil end if ({hits=1,atk_frm=1,MP_cost=1})[diff[1]] then diff[2] = format_split_args(diff[2]) end if stats[diff[1]] then if diff[2] == '0' or diff[2] == '0%' then diff[2] = nil end if args[diff[1]] then passive_stats.lines[args[diff[1]]] = passive_stats.lines[args[diff[1]]] - 1 end o_args[diff[1]], args[diff[1]], passive_stats.changed, passive_stats[diff[1]] = args[diff[1]], diff[2], true, true elseif (diff[1] == 'evade_p' or diff[1] == 'evade_m') then if not recalc and args[diff[1]] ~= diff[2] then local physical, v, evade = diff[1] == 'evade_p' and 'physical', diff[2]--:gsub('%%', '%%%%') evade = physical or 'magic' if args[diff[1]] then table.insert(to_replace, {												new_effects:match(p.template.evade:format(evade, '[^(]+'):gsub('[]', '%%%1')), p.template.evade:format(evade, (#(frame.args[diff[1]] or '') > 0 or not unit and args[diff[1]]) and bold(v) or v)											})										else											new_effects = mw.text.split(new_effects, new_effects:find'\n' and '\n' or ' ')											table.insert( new_effects, 1 + #passive_stats.lines + (not physical and args.evade_p and 1 or 0), bold(p.template.evade:format(evade, v)) )											new_effects = table.concat(new_effects, ' ')										end									end									args[diff[1]] = diff[2]--no recalc								elseif not args[diff[1]] or unbold(diff[2]) ~= unbold(args[diff[1]]) then									o_args[diff[1]], args[diff[1]] =										args[diff[1]] or '0',										({awk_mat=1,name=1,atk_frm=1})[diff[1]] and diff[2] or bold(diff[2])								end							elseif input[i] ~=  then table.insert(to_append, input[i]) end						end					end				end				if recalc then					if args.ability then						format_skill_args						new_effects = frame.args.ability and frame.args.ability ~=  and bold(unbold(materia_effects, manual_bold))							or unbold(materia_effects, manual_bold)								:gsub('( ' and 5 or 1))			else				if p_args[1] == 'table' then					result:_'{|style="text-align:center;width:100%" class=wikitable\n|-\n!Name!!class=spacer|Effect!!style="width:50px"|Hits!!style="width:50px"|MP!!style="width:50px"|Type' for i = 1, 5 do result:_'!!style="width:20px"|T':_(i) end result:_'!!style="width:100px"|Gil' elseif p_args[1] == 'conditional' then result:_'|-\n! colspan="11" | Conditional\n|-' else result:_'|-class=subheader\n!colspan=11|' end local last_atk_frm = args.atk_frm for i = 1, tonumber(args.max_plus or p_args.max_plus) or 2 do					new_effects = unbold(new_effects) parse_awakening(args[(p_args.skill or currentTitle) .. '+' .. i] or missing_plus(p_args.skill or currentTitle, i)) result :_'\n|-\n|' :_(link(args.name, true)) :_' +'						:_(i) :_'\n|style="text-align:left"| ' :_(new_effects							:gsub("%b", "%1")							:gsub(("'"):rep(6),)							:gsub('\n', ' ')						) :_(atk_frm ~= args.atk_frm and							(function last_atk_frm = args.atk_frm return multi_break(attack_frames(args.hits)):gsub('[FA]', function(m)return'New '..m:lowerend) end)							or multi_break(args.hits)						) :_(multi_break(args.MP_cost)) if args.awk_mat then local mats = mw.text.split(args.awk_mat, '%s*,%s*') for c = 1, #mats - 1 do result:_'\n|':_(mats[c]) end result :_'\n|' :_((' -\n|'):rep(7 - #mats)) :_(frame:expandTemplate{title='Gil', args = {mats[#mats]}}) else result:_'\n|colspan=7|' if p_args.skill then if currentTitle ~= invokePage then result :_'via ' :_(link(p_args.skill)) :_' +'								:_(i) else result:_' -'end else result:_'awk_mat undefined' end end args.MP_cost, args.hits = unbold(args.MP_cost), unbold(args.hits) end end elseif args.mode == 'event' then result :_'|-\n|' :_(BADGE) :_'\n| ' :_(p.equipment[args.type] and stats_cell					or materia_effects) if args.recipe then result:_'\n| ':_(recipe) end extend elseif args.mode == 'custom' then result:_'|-' extend elseif args.mode == 'fetch' then extend(p_args.sep) if p_args.sep ~= '' then table.remove(result, 1) end--removes first sep elseif args.mode == 'tooltip' then local wikilink, tip, lines = link(args.name, not p_args.full_pg), mw.html.create'td', (p.equipment[args.type] and stats_cell or materia_effects)(true) lines[1] = stats_sorted[1] and p.equipment[args.type] and 'Stats: ' .. lines[1] :gsub('%((%-%d+%%?)%)', '( %1 )') :gsub('([^ ])(%+%d+%%?)', '%1 %2 ') or lines[1] for i = 1, #lines do				if lines[i]:find'^<table' then tip:node(lines[i]:gsub('(<table style=")', '%1font-size:8.5px;'))					lines.table = true				else tip					:tag'div'					:css{						['padding-left']='2ex',						['text-indent']='-2ex',						['font-size']=lines.table and '8.5px' or nil					}					:wikitext(lines[i])				end			end			tip:css{background = '#014'}			return frame:preprocess(tostring(mw.html.create'span'				:addClass'tool'					:tag'span'					:addClass'mobileonly'					:wikitext(wikilink)				:done					:tag'span'					:addClass'nomobile'					:wikitext(wikilink)				:done				:tag'span'					:addClass'tip module-tooltip'					:tag'table'						:tag'tr'							:tag'td'							:css{width='90px',['text-align']='center',['vertical-align']='top',padding='12px 6px'}							:attr{rowspan=2}							:wikitext(icon)						:done							:tag'td'							:css{padding='5px 0 0 5px',font='15px Arial, sans-serif',height='30px'}							:node(args.name:gsub(' %(%w+%)', )) :node((p.equipment[args.type] or p.ability[args.type] == 2) and mw.html.create'span'								:css{float='right',['margin-top']='-9px'}								:wikitext( frame:expandTemplate{ title = p.equipment[args.type] and 'Equip' or 'Affinity', args = {args.type == 'Throwing Weapon' and 'Throwing' or args.type }									},									p.ability[args.type] and ' Lvl ' .. args.mag_lv .. ' '								)							)						:done :done :tag'tr' :node(tip) :allDone ))		else --Display when on a shop page (mode = recipe/item/item2/typeless) result :_(('|-\n| %s@\n| %s %s')--don't remove spaces					:gsub('@', args.mode == 'item2' and '' or '%%s')					:format( args.mode == 'recipe' and 'Recipe for ' or '', args.mode == 'item2' and args.name or link(args.name, true), (args.mode == 'typeless' or args.mode == 'recipe') and (p.equipment[args.type] and '-\n| ' or 'colspan=2' .. (args.mode == 'recipe' and '| ' or '')) or type_link .. '\n|', args.mode == 'recipe' and '-' or widget_sort_stats .. (								p.equipment[args.type] and stats_cell								or materia_effects								or (args.usage or args.awk_mat) and 'Material'								or '-'				))) :_(p_args.limit ~= '0' and ('\n|%s'):format( args.quartz and 'Star Quartz' or 'Gil', args.quartz or ({item=1,item2=1})[args.mode] and args.buy_gil or args.mode == 'recipe' and args.buy_rec or '?', p_args.limit and (' (Limit: %s)'):format(p_args.limit) or '' ))			extend end else --info displayed on item (invoke) page result :_'\n' :_((args.image or args.name) and (' \n%s\n '):format( p.equipment[args.type] and 'equip' or p.ability[args.type] and 'ability' or 'item', icon ))			:_(args.desc and '\n\n' .. args.desc) if args.mode then table.remove(result, 1) end--mode = demo if args.type then if p.equipment[args.type] then local materia = {args.effect} table.insert(materia, args.ability) for k, v in ipairs(materia) do materia[k] = split_link(v, true, nil, ailment_link) end result :_"\n== Statistics ==\n*Type: " :_(args.type == 'Accessory' and 'Accessory'						or p.equipment._class[p.equipment[args.type]] .. type_link(true)) :_"\n*Stats: " :_(stats_sorted[1] and table.concat(stats_sorted, ', ') or '-') :_"\n*Element: " :_(args.element or '-') :_"\n*Resistance: " :_(args.resist and split_link(args.resist, false, nil, ailment_link) or '-') :_"\n*Additional effect: " :_(#materia > 0 and 						link2tooltip(args.ability and (--replace link2tooltip after all abilities use Data							args.effect and 								"%s and enables use of %s"								or 'Enables use of %s'							):format(unpack(materia)) or materia[1], true) .. (args.warning and ', ' .. args.warning or '')						or args.warning						or '-'					) elseif p.ability[args.type] then flags.rng_enable = args.effect and args.effect:find'%(%d+%%%) [^\n]*Enable' and not args.ability result :_"\n== Statistics ==\n*Type: " :_(p.ability._class[p.ability[args.type]]) :_' ('					:_(args.type)					:_(p.ability[args.type] == 2 and ' Magic Lv ' .. args.mag_lv)					:_")\n*Effect:\n:" :_(materia_effects) :_(args.hits and "\n*Hits: " .. attack_frames(args.hits)) :_(args.MP_cost and "\n*MP: " .. args.MP_cost) if args.ability or flags.rng_enable then result :_'\n==Abilities==\n' :_(args.ability and materia_ailment_link(args.ability)) if flags.rng_enable then local r, fake_parent = result, { getTitle = function return currentTitle end, args = {mode = 'conditional'}, getParent = frame.getParent }							function frame.getParent return fake_parent end result = {_ = result._} frame.getParent, result = fake_parent.getParent, r :_'{|class=wikitable style="text-align:center;width:100%"\n|-\n! colspan="3" | Condition !! width="130px" | Name !! class="spacer" | Effect !! width="50px" | Hits undefined !! width="50px" | MP\n' :_(p.item(frame)) :_'\n|}' end end elseif args.type == 'Item' and args.effect then result :_'\n== Effect ==\n' :_(materia_effects) end end result :_(args.notes and '\n==Notes==\n') :_(args.notes) :_'\n==Crafting recipe==\n' :_(args.recipe and args.recipe ~= 'none' and				('{|class=wikitable style="text-align:center"\n!Materials!!Gil\n|-\n|align=left|%s\n||\n|}'):format( recipe or ' is undefined', args.rec_gil or '?')				or 'None'			) :_((args.usage or args.quest) and result				:_"\n==Usage=="				:_(args.awk_mat and "\nAwakening Material\n:*" .. split_link(args.quest, nil, '\n:*'))				:_(args.usage and "\nCrafting Material" .. multi_split(args.usage, "\n:%s\n::*", '\n::*', '\n:*'))				:_(args.quest and "\nInvolved in Quest\n:*" .. split_link(args.quest, nil, '\n:*'))				and nil			) :_"\n==How to obtain==" local function simpleTable(v) local rows = mw.text.split(v, '\n+') for k, r in ipairs(rows) do				local cells = mw.text.split(r, "%$") for i, c in ipairs(cells) do if c:find'^ *%[%[.+%]%] *$' then cells[i] = 'style="text-align:left"| ' .. c end end rows[k] = table.concat(cells, ' || ') end result :_'{|class="wikitable sortable" style="text-align:center"\n' :_(table.concat(rows, '\n|-\n| ')) :_'\n|}' end for i = 1, #froms do			if args[froms[i][1]] then flags.isItem = flags.isItem or not froms[i][3] result :_"\n''" :_(froms[i][2]) :_"''\n:" if args[froms[i][1]]:find'^{|' then result:_(args[froms[i][1]]) elseif args[froms[i][1]]:find'^!' then simpleTable(args[froms[i][1]]) else result :_"*"					:_(split_link(args[froms[i][1]], nil, '\n:*', froms[i][1] == 'equip' and tooltip_link)) end end end result :_(args.trust and ("\nTrust Master Reward\n:*%s"):format(args.trust)) :_(args.obtain and multi_split(args.obtain, "\n%s\n:*", '\n:*', '\n*')) :_(args.used_by and "\n==Equippable By==\n:*" .. split_link(args.used_by, nil, '\n:*')) if args.learn then local awk = {} for name in args.learn:gmatch'\n|? *%[%[([^%]|]+)' do if args[name .. '+1'] then table.insert(awk, {name}) end end if args.enable then for skill in args.enable:gsub('\\,', '@$'):gmatch'%f[%w][^,]+' do if args[skill .. '+1'] then table.insert(awk, {skill:gsub('@$', ','), isSkill = true}) end end end if awk[1] then table.sort(awk, function(a, b) return a[1] < b[1] end) local fake_parent = { getTitle = function return currentTitle end, args = {'table', mode = 'awaken'} }				function frame.getParent return fake_parent end local r, wide_tab = result:_'\n==Ability Awakening==', 'width:200%%;margin-right:-99vh;%1"%2 mw-collapsible mw-collapsed" data-expandtext="Show table (hidden on testcase pages only due to width)"\n|+' for k, name in ipairs(awk) do					result, fake_parent.args.page, fake_parent.args.skill = setmetatable({}, {__index = r}), select(name.isSkill and 1 or 2, invokePage, name[1]) awk, fake_parent.args[1] = k== 1 and p.item(frame) :gsub('!Name', '!style="width:5em"|Unit/Skill!%1', 1) :gsub(currentTitle:find'testcases/' and 'width:100%%([^"]*" class=)(wikitable)' or '^$',								wide_tab,								1							) or p.item(frame) :gsub('(colspan=)(%d+)', function(a,b)return a..b+1 end, 1)--future maintenance proof r:_'\n':_(awk:gsub( '(%|%[%[.-%]%] %+)',						'|rowspan=' .. select(2, awk:gsub('[^|]%[%[([^%]|]+)%]%] ?%+1', '')) * 3 + 2 .. '|'							.. (name.isSkill and																or  							) .. ' '.. link(name[1]) ..'|%1', 1					))					for conditional in awk:gmatch'[^|]%[%[([^%]|]+)%]%] ?%+1' do						local m = '' for n in awk:gmatch('%[%[' .. conditional .. '%]%] ?%+%s') do m = m < n and n or m end r:_'\n':_(frame							:expandTemplate{title = ':' .. conditional, args = {								mode = 'awaken',								skill = args.name,								max_plus = m							}}							:gsub('undefined', '%1 at ' .. link(conditional))						) end end result = r:_'\n|}' end end local categories = {''} if args.type then if p.ability[args.type] then table.insert(categories, 'Module:Data Ability') if flags.isItem or args.trust or args.obtain then table.insert(categories, 'Items') table.insert(categories, 'Ability Materia') elseif args.esper or args.equip then table.insert(categories, 'Ability Materia') end if p.ability[args.type] == 1 then table.insert(categories, ('%s (%s)'):format( p.ability._class[p.ability[args.type]]:sub(1, -2) .. 'ies', args.type ))				else table.insert(categories, 'Magic') table.insert(categories, args.type .. ' Magic') table.insert(categories, args.type .. ' Magic Level ' .. args.mag_lv) end else table.insert(categories, 'Items') if p.equipment[args.type] then table.insert(categories, 'Equipment') if args.type == 'Accessory' then table.insert(categories, 'Accessories') else table.insert(categories, p.equipment._class[p.equipment[args.type]] .. 's') table.insert(categories, p.equipment._plural[args.type] or args.type .. 's') end else if args.usage or args.awk_mat then table.insert(categories, 'Materials') end end end end if (args.recipe or args.reward or ''):find(p.patterns.event) then table.insert(categories, 'Event Items') end for _, k in ipairs{ --{arg key, category name}, if key is not nil, put in cat {'recipe', 'Crafting Recipes'}, {'buy_gil', 'Purchasables'}, {'trust', 'Trust Master Rewards'}, {hasExclusiveFX and 'effect' or 'used_by', 'Unit Exclusive Items'}, {'awk_mat', 'Awakening Materials'}, {'usage', 'Crafting Materials'}, {'quest', 'Quest Items'}, {'equip', 'Equipment-given Abilities'}, {'esper', 'Esper-given Abilities'} } do			if args[k[1]] then table.insert(categories, k[2]) end end result :_((namespace ~= 0 and --escape catagories if not on main namespace table.concat(categories, ']]\n[[:Category:'):gsub('%[C', '[:C') or table.concat(categories, ']]\n[[Category:') ):sub(3, -1)) :_']]'	end return frame:preprocess(table.concat(result)) end

function p.wrap(frame) if frame.args.mode == 'shop' then result :_'{|class=wikitable' for k, v in ipairs(frame.args) do			local name = k==1 and frame.args.name or frame.args['name'..k] or '' result :_'\n|-\n!colspan=4| ' :_(name) :_'\n|-\n!Name!!' :_((name == 'Item Shop' or name == 'Ability Shop') and					'colspan=2| '					or 'Type!!'				) :_'Description!!Price' :_(v) end result:_'\n|}' end return frame:preprocess(table.concat(result)) end

function p.find(frame) local data, bad, title, result, this = {}, {}, mw.title.getCurrentTitle.fullText this = mw.text.decode(frame:preprocess('')) if title:find'User:' or not title:find':' and not title:find'/' then for m, p, l in this:gmatch'|%s*%[(%b[])%] *(.)(%d?)' do 			m = m:sub(2, -2):match'^[^|]+' if not (m:find':[^ _]' or m:find'#') and (p ~= '+' or l ~= '2') then local page = mw.text.decode(frame:preprocess()):gsub('<!%-%b--%->', ) if page:lower:find' %s*{{#invoke:data|item' and (not title:find'Abilit' or page:find'Ability_%d+%.png') then if this:sub(1, 99):find'Unit Infobox' then table.insert(page:find(title) and data or bad, p == '+' and tonumber(l) and m .. '|mode=awaken' or m)					else if title == 'Ability Awakening' then for awk in page:gmatch'|%s*([^\n]-)%+1%s*=' do if not this:find(m .. '|'.. awk, 1, 1) then local rowspan = this:match(										'rowspan%s*=%s*["\']?(%d+)[^\n]+'										.. awk .. '[^\n]+\n|%s*%[%[' .. m									)									table.insert(data, m .. '|' .. awk										.. (rowspan and '|' .. rowspan or ''))								end							end						else							local rowspan							if title:find'Abilit' and page:find'mag_lv%s*=%s*%d' then								rowspan = this:match('rowspan=["\']?(%d+)[^\n]+\n|[^\n]+|' .. m)							end table.insert(data, rowspan and								m .. '|rowspan=' .. rowspan								or m							) end end end end end end if data[1] then result = '{{Kupo2|Kupo! The following ' .. (data[2] and 'are Module:Data pages' or 'is a Module:Data page') .. ':\n {{:' .. table.concat(data, '}}\n{{:') .. '}} \nPlease replace where appropriate. Thanks!\n\nThis message only appears if links to such pages are found.\n}}' end if bad[1] then result = (result or '') .. '{{Kupo2|The following ' .. (bad[2] and 'Module:Data abilites do' or 'Module:Data ability does') .. ' not include this unit in {{param|learn}}:\n\n' 			.. table.concat(bad, ', ') 			.. '\n\nPlease add this unit to ' .. (bad[2] and 'those pages' or 'that page') .. '. Thanks, Kupo!}}' end local count = #data if bad[1] then count = '*' elseif #data > 9 then count = mw.ustring.char(('A'):byte + #data - 10) end return result and frame:preprocess(result) .. (not this:find'{{Unreleased}}' and  or ) --following script can be used on pages like Special Abilities Passive in edit mode --javascript:for(i=0,m=document.querySelector('div>pre').innerHTML.match(/[^{}:][^{}\n]+?(?=\}\})/g);m[i];i++)wpTextbox1.value=wpTextbox1.value.replace(new RegExp('\\n\\|\\-.*?\\n?.*?\n?.*?\\[\\[('+m[i].replace(/([^\w\s])/g, "\\$1")+')(\]\]|\\|)[\\s\\S]*?(?=\\n(\\|(\\-|\\})|\\{\\{:))'), "\n"); end

return p