コンテンツにスキップ

モジュール:Remix

提供:Wikisource
モジュールの解説[表示] [編集] [履歴] [キャッシュを破棄]

モジュール:Remixは、いくつかのWikitext支援機能を提供します。

  • 関数conjureは、<page>タグと似たトランスクルージョン機能を提供します。詳細は「テンプレート:Conjure」をご確認ください。
  • 関数foreachは、繰り返し実行の機能を提供します。詳細は「テンプレート:Foreach」をご確認ください。
  • 関数demoshowは、Wikitextとその実行結果を表示する機能を提供します。詳細は「テンプレート:Demoshow」をご確認ください。
  • 関数inheritcatは、引数pagenameで渡されたページのカテゴリを取得し展開します。詳細は「テンプレート:Inheritcat」をご確認ください。
  • 関数replace_multipleは、引数contentで渡された文字列に対してrplptnNとrplfmtNの文字列置換を正数Nの小さい順に行った文字列を返します。使用例は「テンプレート:Interval」をご確認ください。
  • 関数hexdumpは、引数contentで渡された文字列をhexdump出力します。

local bi = {}

function bi.create_rplset(in_args)
	local rplset = {}
	for k, v in pairs(in_args) do
		local m = mw.ustring.match(k, '^rplptn(%d+)$')
		if m ~= nil then
			local ptn = mw.text.decode(bi.get_alt_if_empty(v, ''))
			local fmt = mw.text.decode(bi.get_alt_if_empty(in_args['rplfmt' .. m], ''))
			local err = bi.get_alt_if_empty(in_args['rplerr' .. m], '')
			local han = bi.get_alt_if_empty(in_args['rplhan' .. m], '0')
			if han ~= '0' then
				ptn = bi.zen2han(ptn)
				fmt = bi.zen2han(fmt)
			end
			
			if #ptn > 0 then
				rplset[#rplset + 1] = {['idx'] = m, ['ptn'] = ptn, ['fmt'] = fmt, ['err'] = err, ['idx'] = m}
			end
		end
	end
	
	table.sort(rplset, function(a,b)
		return tonumber(a['idx']) < tonumber(b['idx'])
	end)
	
	return rplset
end

function bi.use_rplset(content, rplset)
	for i, v in ipairs(rplset) do
		local k = v['ptn']
		if v['err'] == '' or v['err'] == 'skip' then
			-- skip error
		elseif mw.ustring.match(content, k) == nil then
			if v['err'] == 'error' then
				return bi.createErrorMessage('NOT MATCH PATTERN#' .. v['idx'] .. ' "<nowiki>' .. k .. '</nowiki>"')
			elseif v['err'] == 'blank' then
				return ''
			else
				return v['err']
			end
		end
		content = mw.ustring.gsub(content, k, v['fmt'])
	end
	return content
end

function bi.get_contents(in_args)
	local rplset = bi.create_rplset(in_args)
	local nilqa = {}
	local argindex = bi.get_alt_if_empty(in_args['index'], nil)
	local argpages = bi.get_alt_if_empty(in_args['pages'], nil)
	local argdelim = bi.get_alt_if_empty(in_args['delim'], '')
	local argfromsection = bi.get_alt_if_empty(in_args['fromsection'], nil)
	local argtosection = bi.get_alt_if_empty(in_args['tosection'], nil)
	local argonlysection = bi.get_alt_if_empty(in_args['onlysection'], nil)
	local argshowlink = bi.get_alt_if_empty(in_args['showlink'], '1')
	if argshowlink == '1' then
		argshowlink = '元ページ'
	end
	local arghexdump = bi.get_alt_if_empty(in_args['hexdump'], '0')
	local arggaplink = bi.get_alt_if_empty(in_args['gaplink'], '')
	local argbindCJK = bi.get_alt_if_empty(in_args['bindCJK'], '0')
	local argbindwest = bi.get_alt_if_empty(in_args['bindwest'], '0')
	local argtitlepath = bi.get_alt_if_empty(in_args['titlepath'], nil)
	local argkanji_old2new = bi.get_alt_if_empty(in_args['kanji_old2new'], '0')
	local rgx_ss_begin_Q = '<section%s+begin%s*=%s*"'; local rgx_ss_begin_E = '"%s*/>'
	local rgx_ss_end_Q = '<section%s+end%s*=%s*"'; local rgx_ss_end_E = '"%s*/>'
	local single_pagelink = ''
	local err_nosection = "\n[[Category:Pages transcluding nonexistent sections]]\n"
	local match_count = 0

	if argonlysection ~= nil then
		if argfromsection ~= nil or argtosection ~= nil then
			return bi.createErrorMessage('onlysection and (from,to)section is exclusive.')
		else
			argfromsection = argonlysection
			argtosection = argonlysection
		end
	end
	
	if argfromsection ~= nil then
		argfromsection = bi.escape_lua_regex(argfromsection)
	end
	
	if argtosection ~= nil then
		argtosection = bi.escape_lua_regex(argtosection)
	end
	
	if argpages == nil then
		local fname = argindex
		local title = bi.get_title(fname)
		local text = title:getContent()
		
		if text == nil then
			return bi.createErrorMessage('NOT EXIST TITLE "' .. fname .. '"')
		end
		
		if argfromsection ~= nil then
			local ptnstr = '^.*(' .. rgx_ss_begin_Q .. argfromsection .. rgx_ss_begin_E .. ')'
			text, match_count = mw.ustring.gsub(text, ptnstr, '%1')
			if match_count ~= 1 then
				return bi.createErrorMessage('INVALID FROMSECTION "' .. argfromsection .. '" IN "' .. fname .. '"') .. err_nosection
			end
		end
		
		if argtosection ~= nil then
			local ptnstr = '(' .. rgx_ss_end_Q .. argtosection .. rgx_ss_end_E .. ').*$'
			text, match_count = mw.ustring.gsub(text, ptnstr, '%1')
			if match_count ~= 1 then
				return bi.createErrorMessage('INVALID TOSECTION "' .. argtosection .. '" IN "' .. fname .. '"') .. err_nosection
			end
		end
		
		if argshowlink ~= '0' then
			single_pagelink = bi.create_pagelink(fname, argshowlink, arggaplink)
		end
		
		table.insert(nilqa, text)
		table.insert(nilqa, argdelim)
	else
		if mw.ustring.match(mw.ustring.lower(argindex), '^page:.+$') == nil then
			argindex = 'Page:' .. argindex
		end
		
		local num_arr = bi.parse_num_arr_str(argpages, ',', ';')
		for idx, num in ipairs(num_arr) do
			repeat
				local fname = argindex .. '/' .. num
				local page = mw.ext.proofreadPage.newPage(mw.ustring.sub(fname, 6))
				local title = page.title
				local text = title:getContent()
				if text == nil then
					table.insert(nilqa, bi.createErrorMessage('NOT EXIST PAGE "' .. fname .. '"'))
					break
				end
				
				if idx == 1 and argfromsection ~= nil then
					local ptnstr = '^.*(' .. rgx_ss_begin_Q .. argfromsection .. rgx_ss_begin_E .. ')'
					text, match_count = mw.ustring.gsub(text, ptnstr, '%1')
					if match_count ~= 1 then
						table.insert(nilqa, bi.createErrorMessage('INVALID FROMSECTION "' .. argfromsection .. '" IN "' .. fname .. '"') .. err_nosection)
						break
					end
				end
				
				if idx == table.getn(num_arr) and argtosection ~= nil then
					local ptnstr = '(' .. rgx_ss_end_Q .. argtosection .. rgx_ss_end_E .. ').*$'
					text, match_count = mw.ustring.gsub(text, ptnstr, '%1')
					if match_count ~= 1 then
						table.insert(nilqa, bi.createErrorMessage('INVALID TOSECTION "' .. argtosection .. '" IN "' .. fname .. '"') .. err_nosection)
						break
					end
				end
				
				if argshowlink ~= '0' then
					table.insert(nilqa, bi.create_pagelink(fname, page.displayedNumber, arggaplink))
				end
				
				table.insert(nilqa, text)
				table.insert(nilqa, argdelim)
			until true
		end
	end
	
	local content = table.concat(nilqa, "")
	
	content = bi.use_rplset(content, rplset)
	
	if argkanji_old2new ~= '0' then
		local chjpn = require('Module:CharacterJPN')
		content = chjpn.kanji_old2new_direct2(content, in_args['oldnew'], in_args['exclude'], in_args['exold'], in_args['exnew'])
	end
	
	if argbindCJK ~= '0' then
		content = bi.bindCJK(content, argbindCJK)
	end
	
	if argbindwest ~= '0' then
		content = bi.bindwest(content)
	end
	
	if argpages == nil then
		content = bi.resolve_relative_path(argtitlepath or argindex, content)
	elseif argtitlepath then
		content = bi.resolve_relative_path(argtitlepath, content)
	end
	
	content = single_pagelink .. content
	
	if arghexdump ~= '0' then
		return bi.get_hexdump_html(content, false)
	end
	
	return content
end

function bi.replace_multiple(frame)
	local content = bi.get_alt_if_empty(frame.args['content'], '')
	local arghexdump = bi.get_alt_if_empty(frame.args['hexdump'], '0')
	local rplset = bi.create_rplset(frame.args)
	
	content = bi.use_rplset(content, rplset)
	
	if arghexdump ~= '0' then
		return bi.get_hexdump_html(content, true)
	end
	
	return content
end

function bi.conjure(frame)
	local content = mw.getCurrentFrame():preprocess(bi.get_contents(frame.args))
	local str_excludecat = bi.get_alt_if_empty(frame.args['excludecat'], '')
	if #str_excludecat > 0 then
		local ret_content, include_cats, exclude_cats = bi.exclude_category(content, str_excludecat)
		content = ret_content
	end
	return content
end

function bi.foreach(frame)
	local argkey1 = bi.zen2han(bi.get_alt_if_empty(frame.args['key'], ''))
	local argkey2 = bi.zen2han(bi.get_alt_if_empty(frame.args['key2'], ''))
	local argkey3 = bi.zen2han(bi.get_alt_if_empty(frame.args['key3'], ''))
	local argvalues1= bi.zen2han(bi.get_alt_if_empty(frame.args['values'], ''))
	local argvalues2 = bi.zen2han(bi.get_alt_if_empty(frame.args['values2'], ''))
	local argvalues3 = bi.zen2han(bi.get_alt_if_empty(frame.args['values3'], ''))
	local argcontent = bi.get_alt_if_empty(frame.args['content'], '')
	local argheader1 = bi.get_alt_if_empty(frame.args['header'], '')
	local argheader2 = bi.get_alt_if_empty(frame.args['header2'], '')
	local argheader3 = bi.get_alt_if_empty(frame.args['header3'], '')
	local argfooter1 = bi.get_alt_if_empty(frame.args['footer'], '')
	local argfooter2 = bi.get_alt_if_empty(frame.args['footer2'], '')
	local argfooter3 = bi.get_alt_if_empty(frame.args['footer3'], '')
	local argpreprocess = bi.get_alt_if_empty(frame.args['preprocess'], '0')
	local argtrim = bi.get_alt_if_empty(frame.args['trim'], '0')
	local argcomma = bi.get_alt_if_empty(frame.args['comma'], ',')
	local argsemicolon = bi.get_alt_if_empty(frame.args['semicolon'], ';')
	local arghexdump = bi.get_alt_if_empty(frame.args['hexdump'], '0')
	local num_arr1 = bi.parse_num_arr_str(argvalues1, argcomma, argsemicolon)
	local num_arr2 = bi.parse_num_arr_str(argvalues2, argcomma, argsemicolon)
	local num_arr3 = bi.parse_num_arr_str(argvalues3, argcomma, argsemicolon)
	local nilqa = {}
	local content = ''
	repeat
		if argcontent == '' then
			content = bi.createErrorMessage("argument '''content''' not found")
		elseif #argkey1 == 0 then
			content = bi.createErrorMessage("argument '''key''' not found")
		elseif #num_arr1 == 0 then
			content = bi.createErrorMessage("argument '''values''' not found")
		elseif #num_arr2 > 0 and #argkey2 == 0 then
			content = bi.createErrorMessage("argument '''key2''' not found")
		elseif #num_arr2 == 0 and #argkey2 > 0 then
			content = bi.createErrorMessage("argument '''values2''' not found")
		elseif #num_arr3 > 0 and #argkey3 == 0 then
			content = bi.createErrorMessage("argument '''key3''' not found")
		elseif #num_arr3 == 0 and #argkey3 > 0 then
			content = bi.createErrorMessage("argument '''values3''' not found")
		elseif #num_arr3 > 0 and #num_arr2 == 0 then
			content = bi.createErrorMessage("argument '''values2''' not found")
		end
		
		if #content > 0 then -- treat as error
			content = mw.getCurrentFrame():preprocess(content)
			break
		end
		
		if #argheader1 > 0 then
			table.insert(nilqa, argheader1)
		end
		
		for k1, v1 in pairs(num_arr1) do
			if argtrim ~= '0' then
				v1 = mw.ustring.gsub(v1, "^%s*(.-)%s*$", "%1")
			end
			local str1 = mw.ustring.gsub(argcontent, argkey1, v1)
			if #num_arr2 == 0 then
				table.insert(nilqa, str1)
			else
				if #argheader2 > 0 then
					local header = mw.ustring.gsub(argheader2, argkey1, v1)
					table.insert(nilqa, header)
				end
				for k2, v2 in pairs(num_arr2) do
					if argtrim ~= '0' then
						v2 = mw.ustring.gsub(v2, "^%s*(.-)%s*$", "%1")
					end
					local str2 = mw.ustring.gsub(str1, argkey2, v2)
					if #num_arr3 == 0 then
						table.insert(nilqa, str2)
					else
						if #argheader3 > 0 then
							local header = mw.ustring.gsub(mw.ustring.gsub(argheader3, argkey1, v1), argkey2, v2)
							table.insert(nilqa, header)
						end
						for k3, v3 in pairs(num_arr3) do
							if argtrim ~= '0' then
								v3 = mw.ustring.gsub(v3, "^%s*(.-)%s*$", "%1")
							end
							local str3 = mw.ustring.gsub(str2, argkey3, v3)
							table.insert(nilqa, str3)
						end
						if #argfooter3 > 0 then
							local footer = mw.ustring.gsub(mw.ustring.gsub(argfooter3, argkey1, v1), argkey2, v2)
							table.insert(nilqa, footer)
						end
					end
				end
				if #argfooter2 > 0 then
					local footer = mw.ustring.gsub(argfooter2, argkey1, v1)
					table.insert(nilqa, footer)
				end
			end
		end
		
		if #argfooter1 > 0 then
			table.insert(nilqa, argfooter1)
		end
		
		content = table.concat(nilqa, "")
		content = bi.zen2han(mw.text.decode(content))
		if argpreprocess ~= '0' then
			content = mw.getCurrentFrame():preprocess(content)
		end
	until true
	
	if arghexdump ~= '0' then
		return bi.get_hexdump_html(content, true)
	end
	
	return content
end

function bi.foreach_replace(frame)
	return bi.foreach(frame)
end

function bi.demoshow(frame)
	local argwikitext = bi.get_alt_if_empty(frame.args['wikitext'], nil)
	local argbefore = bi.get_alt_if_empty(frame.args['before'], nil)
	local argafter = bi.get_alt_if_empty(frame.args['after'], nil)
	local argcontent = bi.get_alt_if_empty(frame.args['content'], nil)
	local argusepre = bi.get_alt_if_empty(frame.args['usepre'], '0')
	local argbr = bi.get_alt_if_empty(frame.args['br'], nil)
	local content = ''
	repeat
		if argwikitext == nil then
			content = bi.createErrorMessage("argument '''wikitext''' not found")
			break
		elseif argbefore == nil then
			content = bi.createErrorMessage("argument '''before''' not found")
			break
		elseif argafter == nil then
			content = bi.createErrorMessage("argument '''after''' not found")
			break
		elseif argcontent == nil then
			content = bi.createErrorMessage("argument '''content''' not found")
			break
		end
		
		local bra_nowiki, ket_nowiki = '<nowiki>', '</nowiki>'
		if argusepre ~= '0' then
			bra_nowiki, ket_nowiki = '<pre>', '</pre>'
		end
		
		content = argcontent
		local wikitext = bi.zen2han(argwikitext)
		local before_wikitext = bra_nowiki .. wikitext .. ket_nowiki
		if argbr ~= nil then
			if argusepre == '0' then
				before_wikitext = mw.ustring.gsub(before_wikitext, argbr, ket_nowiki .. '<br/>' .. bra_nowiki)
			else
				before_wikitext = mw.ustring.gsub(before_wikitext, argbr, "\n")
			end
			wikitext = mw.ustring.gsub(wikitext, argbr, "\n")
		end
		content = mw.ustring.gsub(mw.ustring.gsub(content, argbefore, before_wikitext), argafter, wikitext)
	until true
	content = bi.zen2han(mw.text.decode(content))
	return mw.getCurrentFrame():preprocess(content)
end

function bi.inheritcat(frame)
	local pagename = frame.args['pagename']
	local str_excludecat = bi.get_alt_if_empty(frame.args['excludecat'], nil)
	local useroot = bi.get_alt_if_empty(frame.args['useroot'], '0')
	if useroot ~= '0' then
		if mw.ustring.match(pagename, '/') == nil then
			return mw.getCurrentFrame():preprocess(bi.createErrorMessage('NOT SUBPAGE "' .. pagename .. '"'))
		end
		pagename = mw.ustring.gsub(pagename, "^([^/]+)/.+$", "%1")
	end
	local title = bi.get_title(pagename)
	local text = title:getContent()
	local content = mw.getCurrentFrame():preprocess(text)
	local ret_content, include_cats, exclude_cats = bi.exclude_category(content, str_excludecat)
	local retstr = table.concat(include_cats, "\n")
	return retstr
end

function bi.inherit_categories(frame)
	return bi.inheritcat(frame)
end

function bi.exclude_category(str_content, str_exclude)
	local u_200E = '‎' -- 目視しにくいU+200Eが入っているので注意
	local excarr = {}
	if str_exclude ~= nil then
		str_exclude = mw.ustring.gsub(str_exclude, u_200E, '')
		for e1 in mw.ustring.gmatch(str_exclude, '[^,]+') do
			local e2 = bi.escape_lua_regex(e1)
			table.insert(excarr, e2)
		end
	end
	if #excarr > 0 then
		str_content = mw.ustring.gsub(str_content, u_200E, '')
	end
	local ret_content = str_content
	local include_cats = {}
	local exclude_cats = {}
	for e1 in mw.ustring.gmatch(str_content, '%[%[%s*[Cc]ategory%s*:[^%]]+%]%]') do
		local rgx_matched = nil
		for idx, val in ipairs(excarr) do
			local rgx_cat = '%[%[%s*[Cc]ategory%s*:%s*' .. val .. '%s*%]%]'
			if mw.ustring.match(e1, rgx_cat) ~= nil then
				rgx_matched = rgx_cat
				break
			end
			rgx_cat = '%[%[%s*[Cc]ategory%s*:%s*' .. val .. '%s*%|[^%]]*%s*%]%]'
			if mw.ustring.match(e1, rgx_cat) ~= nil then
				rgx_matched = rgx_cat
				break
			end
		end
		if rgx_matched == nil then
			table.insert(include_cats, e1)
		else
			table.insert(exclude_cats, e1)
			ret_content = mw.ustring.gsub(ret_content, rgx_matched, '')
		end
	end
	for e1 in mw.ustring.gmatch(str_content, '%[%[%s*カテゴリ%s*:[^%]]+%]%]') do
		local rgx_matched = nil
		for idx, val in ipairs(excarr) do
			local rgx_cat = '%[%[%s*カテゴリ%s*:%s*' .. val .. '%s*%]%]'
			if mw.ustring.match(e1, rgx_cat) ~= nil then
				rgx_matched = rgx_cat
				break
			end
			rgx_cat = '%[%[%s*カテゴリ%s*:%s*' .. val .. '%s*%|[^%]]*%s*%]%]'
			if mw.ustring.match(e1, rgx_cat) ~= nil then
				rgx_matched = rgx_cat
				break
			end
		end
		if rgx_matched == nil then
			table.insert(include_cats, e1)
		else
			table.insert(exclude_cats, e1)
			ret_content = mw.ustring.gsub(ret_content, rgx_matched, '')
		end
	end
	return ret_content, include_cats, exclude_cats
end

-- @param num_arr_str number array string separated by ','. number range format is 'FROM;TO;STEP'. STEP can be omitted, and treated as 1 or -1.
-- @return sample '2,7;11,5,40' => {2,7,8,9,10,11,5,40}. '7;12;2,3,10,40' => {7,9,11,3,10,40}. '9,6;4,20;15;-2,20' => {9,6,5,4,20,18,16,20}
function bi.parse_num_arr_str(num_arr_str, sep_comma, sep_semicolon)
	local ret_arr = {}
	for e1 in mw.ustring.gmatch(num_arr_str, '[^' .. sep_comma .. ']+') do
		if mw.ustring.match(e1, '^[^' .. sep_semicolon .. ']+$') then
			table.insert(ret_arr, e1)
		elseif mw.ustring.match(e1, '^%d+' .. sep_semicolon .. '%d+.*$') then
			local p = {}
			for e2 in mw.ustring.gmatch(e1, '[^' .. sep_semicolon .. ']+') do
				table.insert(p, math.floor(e2))
			end
			local psize = #p
			if psize == 2 then
				if p[1] <= p[2] then
					p[3] = 1
				else
					p[3] = -1
				end
			elseif psize == 3 then
				assert(mw.ustring.match(p[3], '^-?%d+$'), 'invalid step ' .. p[3])
			else
				assert(false, 'invalid range ' .. e2)
			end
			for n = p[1], p[2], p[3] do
				table.insert(ret_arr, math.floor(n))
			end
		else
			assert(false, 'invalid array ' .. e1)
		end
	end
	return ret_arr
end

function bi.escape_lua_regex(instr)
	return mw.ustring.gsub(instr, '([%(%)%.%%%+%-%*%?%[%]%^%$])', '%%%1')
end

function bi.bindCJK(content, bind_option)
	local range_cjk = ' -ヿ⺀⿟々〇〻㐀-䶿一-鿿가-힣-﫿︀-️!-ᄒ𠀀-𱍊󠄀-󠇯' -- 目視しにくい異体字セレクター(U+FE00〜U+FE0F、U+E0100〜U+E01EF)を含んでいるので、編集は要注意
	local rgxcap_cjk = '([' .. range_cjk .. '])'
	local rgx_lf, fmt_after = '\n', '%1%2'
	local ret_str = mw.ustring.gsub(content, rgxcap_cjk .. rgx_lf .. rgxcap_cjk, fmt_after)
	
	if bind_option == '1' then
		return ret_str
	elseif bind_option == '2' then
		bind_option = 'r,ruby,rs'
	elseif bind_option == '3' then
		bind_option = 'r,ruby,rs,rbcmnt,rbcmnt2'
	end
	
	for e1 in mw.ustring.gmatch(bind_option, '[^,]+') do
		local tmplptn = e1
		local ch = mw.ustring.codepoint(e1, 1, 1)
		if 0x41 <= ch and ch <= 0x5A then
			tmplptn = '[' .. mw.ustring.char(ch) .. mw.ustring.char(ch + 0x20) .. ']' .. mw.ustring.sub(e1, 2)
		elseif 0x61 <= ch and ch <= 0x7A then
			tmplptn = '[' .. mw.ustring.char(ch - 0x20) .. mw.ustring.char(ch) .. ']' .. mw.ustring.sub(e1, 2)
		end
		-- 以下rgxcap_tmpl_arrの一つ目は引数なし。二つ目は引数ありで出現頻度の高いルビ末尾{{く}}や{{ぐ}}に対応するため末尾を「}}}?}?」としている。
		local rgxcap_tmpl_arr = {'({{' .. tmplptn .. '}})', '({{' .. tmplptn .. '|[^}]-}}}?}?)'}
		for idx, rgxcap_tmpl in ipairs(rgxcap_tmpl_arr) do
			ret_str = mw.ustring.gsub(ret_str, rgxcap_tmpl .. rgx_lf .. rgxcap_cjk, fmt_after)
			ret_str = mw.ustring.gsub(ret_str, rgxcap_cjk .. rgx_lf .. rgxcap_tmpl, fmt_after)
			ret_str = mw.ustring.gsub(ret_str, rgxcap_tmpl .. rgx_lf .. rgxcap_tmpl, fmt_after)
		end
	end
	
	return ret_str
end

function bi.split_log(text)
	return mw.ustring.gsub(text, '(.)', '_%1')
end

function bi.bindwest(content)
	local range_west = 'A-Za-zÀ-֏'
	local rgx_str1 = '([' .. range_west .. '])%-\n([' .. range_west .. '])'
	local rgx_str2 = '([' .. range_west .. '])\\%-\n([' .. range_west .. '])'
	return mw.ustring.gsub(mw.ustring.gsub(content, rgx_str1, '%1%2'), rgx_str2, '%1-%2')
end

function bi.create_pagelink(str_page, str_alias, str_gap)
	local gap = ''
	if #str_gap > 0 then
		gap = '{{Gap|' .. str_gap .. '}}'
	end
	return '{{MarginNote|margin=left|margin-width=300|text=[[' .. str_page .. '|&#91;' .. str_alias .. '&#93;]]' .. gap .. '}}'
end

function bi.get_alt_if_empty(instr, altstr)
	if instr == nil or #instr == 0 then
		return altstr
	end
	return instr
end

function bi.get_title(fname)
	local title = mw.title.new(fname)
	local redir_title = title.redirectTarget
	if redir_title then
		title = redir_title
	end
	return title
end

function bi.createErrorMessage(message)
	return '{{color|red|🚧🚨👷‍♀️ ' .. message .. ' 👷‍♂️🚨🚧}}'
end

local mypagename = ''

function bi.resolve_relative_path(str_pagename, text)
	mypagename = str_pagename
	local content = mw.ustring.gsub(text, '(%[%[%s*)([%./]+)([^%./%]]*)(%]%])', bi.rplfnc_resolve_relative_path)
	return content
end

function bi.rplfnc_resolve_relative_path(c1, c2, c3, c4)
	if c2 == nil and c3 == nil and c4 == nil then
		return c1
	elseif c2 == '/' then
		if mw.ustring.find(c3, '^[^%|%[%]]+') ~= nil then
			return c1 .. mypagename .. '/' .. c3 .. c4
		end
	else
		local match_count = 0
		_, match_count = mw.ustring.gsub(c2, '%.%./', '../')
		if match_count > 0 then
			local rgxstr_tail = mw.ustring.rep('/[^/]+', match_count) .. '$'
			local neostr = mw.ustring.gsub(mypagename, rgxstr_tail, '')
			if mw.ustring.find(c3, '^[^%|%[%]]+') ~= nil then
				neostr = neostr .. '/'
			end
			return c1 .. neostr .. c3 .. c4
		end
	end
	return c1 .. c2 .. c3 .. c4
end

function bi.zen2han(text)
	local content = mw.ustring.gsub(text, '([!-}])', bi.rplfnc_zen2han)
	return content
end

function bi.rplfnc_zen2han(c1)
	local ch = mw.ustring.codepoint(c1, 1, 1)
	if 0xFF01 <= ch and ch <= 0xFF5D then
		return mw.ustring.char(ch - 0xFEE0)
	end
	return c1
end

function bi.insert_for_hexdump(datas, hexs, raws, colmax, altch, br)
	if #hexs > 0 then
		local hstr = table.concat(hexs, "")
		local gaplen = colmax * 7 - mw.ustring.len(hstr)
		table.insert(datas, hstr)
		table.insert(datas, mw.ustring.rep(" ", gaplen))
		table.insert(datas, " 🧐")
		table.insert(datas, table.concat(raws, ""))
		table.insert(datas, "🧐")
		table.insert(datas, br)
		hexs = {}
		raws = {}
	end
	return datas, hexs, raws
end

function bi.get_hexdump_html(instr, b_preprocess)
	local datas = {}
	local hexs = {}
	local raws = {}
	local colmax = 16
	local altch = "."
	local br = "\n"
	local idx = 0
	table.insert(datas, "<pre>\n")
	for chnum in mw.ustring.gcodepoint(instr) do
		idx = idx + 1
		if ((idx - 1) % colmax == 0) then
			datas, hexs, raws = bi.insert_for_hexdump(datas, hexs, raws, colmax, altch, br)
			table.insert(datas, mw.ustring.format("%08X🧐", idx - 1))
		end
		if chnum <= 0xFFFF then
			table.insert(hexs, mw.ustring.format("   %04X", chnum))
		elseif chnum <= 0xFFFFF then
			table.insert(hexs, mw.ustring.format("  %05X", chnum))
		else
			table.insert(hexs, mw.ustring.format(" %06X", chnum))
		end
		local ch = mw.ustring.char(chnum)
		if mw.ustring.find(ch, "^%s$") and ch ~= ' ' then
			table.insert(raws, altch)
		else
			table.insert(raws, ch)
		end
	end
	datas, hexs, raws = bi.insert_for_hexdump(datas, hexs, raws, colmax, altch, br)
	table.insert(datas, "</pre>")
	local content = table.concat(datas, "")
	if b_preprocess then
		return mw.getCurrentFrame():preprocess(content)
	end
	return content
end

function bi.hexdump(frame)
	local content = bi.get_alt_if_empty(frame.args['content'], '')
	return bi.get_hexdump_html(content, true)
end

return bi