Editing
Module:Var
Jump to navigation
Jump to search
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
local p={} local lib_arg={} local lib_str=mw.ustring local yesno = require('Module:Yesno') local main_template_name = "變數" local module_call ="#invoke:Var" local soft_subst_main = "Softsubst" local messages,_L,lib_es,lib_tc,command_list = { magic_space = '\127'..string.char( 0xC2, 0xA0 ), strip_mark = "\127'\"`UNIQ--%s-%s-QINU`\"'\127", strip_mark_pattern = "\127'\"`UNIQ%%-%%-%s%%-%s%%-QINU`\"'\127", arg_global = '_arg_process', cant_subst = '替換引用階段無法存取變數', cat_subst_not_done = "未完成替換引用的頁面", not_support_strip_warning_cmt = "<!--可能需要手動調整的擴展標籤-->", not_support_strip_warning = "[[Template:softsubst]]警告:欲顯示替換引用原碼的模板中包含了擴展標籤,可能導致結果不準確,或替換引用失敗。", do_subst = '<p class="notice metadata" id="spoiler" style="font-size: small">以下内容為[[WP:Subst|替換引用]]後的預期內容,請自行拷貝原始碼完成替換引用。</p><div style="clear: both; overflow: auto; border: 1px solid #cccccc; padding: 4px; text-align: left; background: transparency;">', remove_hide_strip_mark = "<span%s+style%s*=%s*[\"\']%s*display%s*:%s*none%s*;?%s*[\"\']%s*>[^<]+UNIQ%-%-nowiki[^<]+<%s*/%s*span%s*>", var_undefine = '變數 "%s" 未定義。', NullPointerException = '[[NullPointerException|空指標]]錯誤'},{p=p},require("Module:EditState"), require("Module:TrackingCategory"),{'none', set = {'set', '設定', '設置'}, get = {'get', '取值','取', '取得'}, getconst = {'getconst'}, ref = {'ref', '取址'}, refget = {'refget', 'refgetname'}, hide = {'hide', '隱藏'}, subst = {'subst'}, call = {'call', 'callconst', '呼叫', '執行', '運行'}, hist = {'hist', '歷史'}, delete = {'del', 'delete', '刪除'}, new = {'new'}, }; local notsupport_strip = false local no_color = false local printf = lib_str.format local sub = lib_str.sub local gsub = lib_str.gsub local trim = mw.text.trim local split = mw.text.split local function _test_cmd(cmd_names, test) local result, trim_test = false, trim(lib_str.lower(test)) for i=1,#cmd_names do result = result or (trim_test == cmd_names[i]) end return result end local function addWarning(msg) return mw.addWarning(require('Module:Error').error{[1] = msg}) end local function codeNowikiWithColor(frame, code_lang, text) if no_color then return frame:extensionTag('nowiki',text) end return frame:extensionTag('syntaxhighlight',text, {inline='', lang=code_lang}) end local strip_code_color_map = {} local ignore_strip_code = { indicator = true, h=true, item=true } local function strip_code_color(frame, strip_code) local strip_type = strip_code:lower() if ignore_strip_code[strip_type] == true then return {'',''} end if no_color then return {messages.not_support_strip_warning_cmt .. '<' .. strip_type .. '>', '</' .. strip_type .. '>'} end local result = strip_code_color_map[strip_type] if result then return result else strip_code_color_map[strip_type] = { codeNowikiWithColor(frame, 'xml', messages.not_support_strip_warning_cmt .. '<' .. strip_type .. '>'), codeNowikiWithColor(frame, 'xml', '</' .. strip_type .. '>') } end return strip_code_color_map[strip_type] end local function no_color_check(frame, text) if no_color then return frame:extensionTag('pre',text) end return text end local function out_new_line() if no_color then return '\n' end return tostring(mw.html.create( 'br' )) end local subst_pattern = "%s*[Ss][Uu][Bb][Ss][Tt]%s*:%s*" local safesubst_pattern = "%s*[Ss][Aa][Ff][Ee][Ss][Uu][Bb][Ss][Tt]%s*:%s*" local msg_pattern = "%s*[Mm][Ss][Gg]%s*:%s*" local msgnw_pattern = "%s*[Mm][Ss][Gg][Nn][Ww]%s*:%s*" --區域變數結尾 local _jsonEncode =require('Module:EncoderUtil')._jsonEncode --函數本體 function p.Varfunc(frame, from_meta_table) local args, working_frame = p._arg_process(frame) if mw.isSubsting() then return require('Module:Template invocation').invocation(string.format("%s|%s",module_call,'softSubst_msg'), args) end local command = args['1'] or args[1] or '' if _test_cmd(command_list.set, command) or _test_cmd(command_list.new, command) or _test_cmd(command_list.delete, command) then local m_mode = 'VAR' if _test_cmd(command_list.delete, command) then m_mode = 'DELETE' elseif _test_cmd(command_list.new, command) then m_mode = 'NEW' end local call_args = {} for key,val in pairs(args) do local checker = tonumber(key) if checker ~= 1 then call_args[key] = val end end return p.makeVar(call_args, m_mode, from_meta_table) elseif _test_cmd(command_list.get, command) then return p.getVar({args['2'] or args[2] or '', args['3'] or args[3],default=args.default}, 'value', false ,from_meta_table) elseif _test_cmd(command_list.ref, command) then return p.getVar({args['2'] or args[2] or ''}, 'addr', false ,from_meta_table) elseif _test_cmd(command_list.getconst, command) then return p.getVar({args['2'] or args[2] or ''}, 'value', true, from_meta_table) elseif _test_cmd(command_list.refget, command) then local addr = args['2'] or args[2] or '' local result = p._getRefVarObj('VAR', addr) local remappingTable = {refget = 'value', refgetname = 'name'} if result then if result[remappingTable[command]] then return result[remappingTable[command]] end end return mw.getCurrentFrame():expandTemplate{ title = 'Error', args = { messages.NullPointerException } } elseif _test_cmd(command_list.hide, command) then if trim(args['2'] or args[2] or '')~= '' then working_frame:extensionTag('ref', args['2'] or args[2] or '', {group = 'UINQ-VARDEF-BLOCK-QINU'}) working_frame:extensionTag('references', '', {group = 'UINQ-VARDEF-BLOCK-QINU'}) end return '' elseif _test_cmd(command_list.subst, command) then return p._softSubst({args['2'] or args[2] or ''}) elseif _test_cmd(command_list.call, command) then return p.callVar(p._seek_arg(args, 1), command == 'callconst') elseif _test_cmd(command_list.hist, command) then return p.getVarHist(p._seek_arg(args, 1), from_meta_table) end return '' end p.Var={ set=function(i)local j=p._seek_arg(i,-1,true);for k,l in pairs(i)do j[k]=j[k]or l end j[1]='set';return p.Varfunc(j,true)end, new=function(i)local j=p._seek_arg(i,-1,true);for k,l in pairs(i)do j[k]=j[k]or l end j[1]='new';return p.Varfunc(j,true)end, delete=function(i)local j=p._seek_arg(i,-1,true);for k,l in pairs(i)do j[k]=j[k]or l end j[1]='delete';return p.Varfunc(j,true)end, get=function(i)local j=p._seek_arg(i,-1,true);j[1]='get';return p.Varfunc(j,true)end, getconst=function(i)local j=p._seek_arg(i,-1,true);j[1]='getconst';return p.Varfunc(j,true)end, hist=function(i)local j=p._seek_arg(i,-1,true);j[1]='hist';return p.Varfunc(j,true)end, call=function(i)local j=p._seek_arg(i,-1,true);j[1]='call';return p.Varfunc(j,true)end, refget=function(i)local j=p._seek_arg(i,-1,true);j[1]='refget';return p.Varfunc(j,true)end, refgetname=function(i)local j=p._seek_arg(i,-1,true);j[1]='refgetname';return p.Varfunc(j,true)end, ref=function(i)local j=p._seek_arg(i,-1,true);j[1]='ref';return p.Varfunc(j,true)end, vars=function(const) local arg_table = p._getVars("VAR", const) local restlt = {} for key,val in pairs(arg_table) do local get_result, check_json = mw.ustring.gsub(arg_table[key].value, "^%$JSON%$", '') if check_json > 0 then restlt[key] = mw.text.jsonDecode(get_result) else restlt[key] = get_result end end return restlt end } p.Var=setmetatable(p.Var, { __index = function(t, k) return t.get({k})end, __newindex = function(t, k, v) local stored = t.set({[k]=v}) p.Varfunc({'hide',stored}) end }) function p.makeVar(frame, mode, from_meta_table) local varMode = mode or 'VAR' if varMode == 'NEW' then varMode = 'VAR' end if trim(varMode) == '' then varMode = 'VAR' end local args, working_frame = p._arg_process(frame) local out_text = function(out_mode,name,value) return printf(messages.magic_space .. "$%s_DEF %s=%s" .. messages.magic_space, out_mode, name, value) end local body = '' local new_body = '' for key,val in pairs(args) do local store_value = val if from_meta_table == true then if type(store_value)~=type('string')then store_value="$JSON$".._jsonEncode(store_value)end end if mode == 'DELETE' then body = body..out_text(varMode,store_value,store_value)..';' else if mode == 'NEW' then new_body = new_body..out_text('DELETE',key,key)..';' end body = body..out_text(varMode,key,store_value)..';' end end if mode == 'NEW' then new_body = working_frame:extensionTag('nowiki', new_body) end body = new_body .. working_frame:extensionTag('nowiki', body) return mw.text.tag( 'span', {style="display:none;"}, body ) end function p.softSubst(frame) local args, working_frame = p._arg_process(frame) if mw.isSubsting() then return require('Module:Template invocation').invocation(soft_subst_main, args)end local template_title = trim(args['1'] or args['1'] or '') if template_title == '' then return '' end return p.softSubst_msg(p._seek_arg(args, 1), template_title, yesno(args['no cat']or args['no_cat']or args.nocat or args.NoCat or args.noCat or false)) end local function check_is_main_doc(working_frame) local checker = mw.ustring.lower(working_frame:preprocess( "{{BASEPAGENAME}}" )) if checker == mw.ustring.lower(main_template_name) or checker == mw.ustring.lower(soft_subst_main) or checker == 'var' then return true end return false end function p.softSubst_msg(frame, template_title, without_cat) local template_dis = template_title or main_template_name local args, working_frame = p._arg_process(frame) if mw.isSubsting() then return require('Module:Template invocation').invocation(string.format("%s|%s",module_call,'softSubst_msg'), args)end local result = require('Module:Template invocation').invocation(template_dis, args) if lib_es.isPreview() then local pre_result = working_frame:preprocess(result) if #(split(gsub(pre_result,'\r\n','\n'),'\n')) > 50 then no_color = true end result = p._softSubst({working_frame:preprocess(result)}) if template_title == nil then result = working_frame:expandTemplate{ title = 'Error', args = { messages.cant_subst } } .. result end if notsupport_strip==true then result = '<p>{{Error|' .. messages.not_support_strip_warning .. '}}</p>' .. result end else if (check_is_main_doc(working_frame) == false) and (not without_cat) then lib_tc.append(messages.cat_subst_not_done) end end if notsupport_strip==true then addWarning( messages.not_support_strip_warning ) end return working_frame:preprocess(result) end function p._softSubst(frame) local softSubst_data, softSubst_warp = 'arg_global', string.char(0x70) if type(softSubst_data) ~= type({'table'}) then --find method between global scope or local scope softSubst_data = {(_G[softSubst_warp] or _L[softSubst_warp])[messages[softSubst_data]](frame)} end local without_cat = yesno(softSubst_data['no cat']or softSubst_data['no_cat']or softSubst_data['nocat']or softSubst_data['NoCat']or softSubst_data['noCat']or false) --decode method local first_data, second_data, third_data = softSubst_data['1'] or softSubst_data[1], softSubst_data['2'] or softSubst_data[2], (_G[softSubst_warp] or _L[softSubst_warp])["_code_getter_wiki"] --2nd layer decode first_data = first_data['1'] or first_data[1] or '' --second_data = second_data['2'] or second_data[2] or '' --third_data = third_data['3'] or third_data[3] or '' if lib_es.isPreview() ~= true and (not without_cat) then if (check_is_main_doc(second_data) == false) then lib_tc.append(messages.cat_subst_not_done) end end local fourth_data = function(msg_data) local msg_data_list, result_code = split(msg_data, '\n'), '' for i=1,#msg_data_list do result_code = result_code .. third_data(second_data, msg_data_list[i]) end if no_color then result_code = no_color_check(second_data, result_code) end return result_code end --foreach method do if trim(first_data)~= '' then return mw.clone(messages.do_subst) .. fourth_data(gsub( gsub(first_data, messages.remove_hide_strip_mark, '') , '\r\n', '\n')) .. '</div>' end return '' end function p.callVar(frame, const) local args, working_frame = p._arg_process(frame) local vars = p._getVars('VAR', const) local var_name = args[1]or args['1']or'' local get_value = vars[var_name] if tonumber(var_name) and not get_value then get_value = vars[tonumber(var_name)] end if get_value then local func_name = trim(mw.clone(get_value.value),"|\t\r\n\f ") func_name = gsub(func_name,subst_pattern,'') func_name = gsub(func_name,safesubst_pattern,'') func_name = gsub(func_name,msg_pattern,'') func_name = gsub(func_name,msgnw_pattern,'') local tail_index = (lib_str.find(func_name,'|')or(lib_str.len(func_name)+1))-1 command_tail = sub(func_name,tail_index + 1,-1) func_name = sub(func_name,1,tail_index) local final_func_name = '' --template: local template_name = mw.title.new( func_name, 'Template' ) if template_name and template_name.exists then final_func_name = template_name.fullText if template_name.namespace == 0 then final_func_name = ':' .. final_func_name end end --int: local template_split = split(func_name, ':') or {func_name} if final_func_name=='' then local check_mw = func_name if #template_split > 1 and (sub(template_split[1],1,3) == 'int' or (lib_str.find(template_split[1],"[Mm]edia[Ww]iki"))) then check_mw = table.concat( template_split, ':', 2, #template_split ) end if mw.message.new(check_mw):exists() then final_func_name = 'int:' .. check_mw end end --#...: local fix_cs = false if final_func_name=='' then if xpcall(function()mw.getCurrentFrame():callParserFunction( func_name, func_name )end, function()end) == true then final_func_name = func_name fix_cs = true local parser_tail_check = sub(trim(final_func_name),-1) if parser_tail_check ~= ':' and parser_tail_check ~= '|' then final_func_name = final_func_name .. ':' end end end --may be magic word if final_func_name=='' then local checker, check_text = xpcall(function() return mw.getCurrentFrame():preprocess( printf("{{%s}}", func_name) ) end, function()end) local test_name = (template_name or { prefixedText = func_name }).prefixedText if not lib_str.find(check_text or '', printf("^{{%s}}$", test_name) ) and not lib_str.find(check_text or '', printf("^%%[%%[:%s%%]%%]$", test_name) ) then final_func_name = func_name end end if trim(final_func_name) ~= '' then local num_args = p._seek_arg(args, 1) local str_args = require('Module:Template invocation').invocation(final_func_name..command_tail, num_args) if fix_cs == true and trim(command_tail) == '' and sub(trim(final_func_name),-1) == ':' then str_args = gsub(str_args, "^({{)%s*(" .. final_func_name .. ")|", "%1%2") end return working_frame:preprocess(str_args) end local max_arg = 0 local num_args = {} for key,value in pairs( args ) do local arg_id = tonumber(key or '') if arg_id then if arg_id > max_arg then max_arg = arg_id end num_args[arg_id] = value end end local call_args = {} local get_obj = function(obj)return mw.text.jsonDecode(obj)end if max_arg > 1 then for i = 2,max_arg do if num_args[i] then local try_to_get_obj = mw.clone(num_args[i]) if not xpcall( function() --try try_to_get_obj = get_obj(num_args[i]) end, function(msg)return msg end ) then --catch call_args[i-1] = num_args[i] else --finally call_args[i-1] = try_to_get_obj end else call_args[i-1] = '' end end end local func_path = split(func_name,'%.') or { [1]=func_name } local func_body = mw[func_path[1]] or p[func_path[1]] or _G[func_path[1]] if lib_str.find(func_path[1],':') then func_body = require(func_path[1]) or func_body end if func_body then local old_obj = func_body for i=2,#func_path do func_body = func_body[func_path[i]] if func_body then old_obj = func_body else return '' end end local func_type = type( func_body ) local times = 10 for ti = 1,times do if func_type == type(nil) then return '' elseif func_type == type(0) then return '' .. func_body elseif func_type == type(true) then if func_body then return '1' end return '' elseif func_type == type(type) then if max_arg > 1 then func_body = func_body(unpack(call_args)) if func_body == nil then func_body = call_args[1] end else func_body = func_body() end elseif func_type == type("string") then return func_body elseif func_type == type({}) then return _jsonEncode(func_body) end func_type = type( func_body ) end end return func_name end if trim(var_name) ~= '' then return working_frame:expandTemplate{ title = 'Error', args = { printf(messages.var_undefine, var_name) } } end return '' end function p._arg_process(frame) local args, working_frame if frame == mw.getCurrentFrame() then -- We're being called via #invoke. The args are passed through to the module -- from the template page, so use the args that were passed into the template. if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end args = lib_arg.getArgs(frame, { parentFirst=true, trim = false, removeBlanks = false }) working_frame = frame else -- We're being called from another module or from the debug console, so assume -- the args are passed in directly. args = frame working_frame = mw.getCurrentFrame() if type(args) ~= type({}) then args = {frame} end end return args, working_frame end function p.getVar(frame, get_mode, const, from_meta_table) local to_get_mode = get_mode or 'value' local args, working_frame = p._arg_process(frame) local vars = p._getVars('VAR', const) local var_name = args[1]or args['1']or'' local get_value = vars[var_name] if tonumber(var_name) and not get_value then get_value = vars[tonumber(var_name)] end if get_value then local get_result, check_json = mw.ustring.gsub(get_value[to_get_mode], "^%$JSON%$", '') if check_json > 0 and from_meta_table == true then return mw.text.jsonDecode(get_result) end return get_result end if trim(var_name) ~= '' then if args[2]or args['2'] or args.default then return args[2]or args['2']or args.default end if from_meta_table == true then return error(printf(messages.var_undefine, var_name)) end return working_frame:expandTemplate{ title = 'Error', args = { printf(messages.var_undefine, var_name) } } end return '' end function p.getVarHist(frame, from_meta_table) local args, working_frame = p._arg_process(frame) local vars = p._getVars('VAR') local var_name = args[1]or args['1']or'' local get_value = vars[var_name] if tonumber(var_name) and not get_value then get_value = vars[tonumber(var_name)] end if get_value then local formats = args[2]or args['2']or'$' local seps = args[3]or args['3']or', ' local hists = split(get_value.hist,'$\127;\127^') local body = '' if from_meta_table == true then local get_result, check_json = {}, -1 for i=1,#hists do get_result[i], check_json = mw.ustring.gsub(hists[i], "^%$JSON%$", '') if check_json > 0 then get_result[i] = mw.text.jsonDecode(get_result[i]) end end return get_result end for i=1,#hists do if i>1 then body = body .. seps end body = body .. gsub(formats, '%$', hists[i]) end return body end if trim(var_name) ~= '' then if from_meta_table == true then return error(printf(messages.var_undefine, var_name)) end return working_frame:expandTemplate{ title = 'Error', args = { printf(messages.var_undefine, var_name) } } end return '' end function p._getRefVarObj(VarMode, addr) local addr_path = split(addr,':') if #addr_path == 2 then local strip_id, addr_loc = addr_path[1], addr_path[2] strip_id = tonumber(strip_id) addr_loc = tonumber(addr_loc) if strip_id and addr_loc then local try_get = mw.text.unstripNoWiki( p._stripMark('nowiki', string.format( "%08X", strip_id )) ) local in_index, arg_data = 1, nil gsub(try_get, messages.magic_space .. "%$" .. VarMode .. "_DEF%s+([^=]+)=([^" .. messages.magic_space .. ']*)', function(f_key, f_value) local key, value = f_key, f_value key = trim(key) if in_index == addr_loc then arg_data = { name=key, value=value, addr=string.format( "%d:%d", strip_id, in_index ) } end in_index = in_index + 1 end) return arg_data end end return nil end function p._seek_arg(input_args_list, offset, enable_minus) local num_args = {} if enable_minus~=true and offset <= 1 then offset = 1 end for key,value in pairs(input_args_list) do local num = tonumber(key) or 0 local stored = false if num then if num > offset then num_args[num - offset] = value stored = true end if num >= 1 and num <= offset then stored = true end end if stored ~= true then num_args[key] = value end end return num_args end function p._code_getter_wiki(working_frame, input_code) local current_text, code_lang = input_code, "moin" local flag1 = lib_str.find(current_text, "<%s*/?%s*[A-Za-z]") local flag2 = lib_str.find(current_text, "[A-Za-z\"']%s*/?%s*>") if flag1 or flag2 then code_lang = "html" end local body = '' local strip_to_remove = split(current_text,'\127') for i=1,#strip_to_remove do local current_strip = strip_to_remove[i] if lib_str.find(current_strip,"'\"`UNIQ%-%-") then if lib_str.find(current_strip, "nowiki") then local try_unstrip = '\127' .. current_strip .. '\127' try_unstrip = mw.text.unstripNoWiki( try_unstrip ) if lib_str.find(try_unstrip,"'\"`UNIQ%-%-") then --不存在的strip entity body = body .. '\127' .. current_strip .. '\127' else try_unstrip = mw.text.decode(try_unstrip) try_unstrip = printf("<nowiki>%s</nowiki>",table.concat( split(try_unstrip,"nowiki"),'</nowiki>nowiki<nowiki>')) body = body .. codeNowikiWithColor(working_frame, 'xml', try_unstrip) end else --保留原始strip符號令解析器自動替換 local strip_type = '' gsub(current_strip, "UNIQ%-%-([%dA-Za-z]+)%-", function(in_str)strip_type=in_str end) local out_strip_text = '\127' .. current_strip .. '\127' if strip_type ~= '' then local strip_code_color_data = strip_code_color(working_frame, strip_type) out_strip_text = strip_code_color_data[1] .. out_strip_text ..strip_code_color_data[2] end body = body .. out_strip_text notsupport_strip = true end elseif trim(current_strip) ~= '' then local avoid_t = split(current_strip, '%-{') or {avoid_t} for a_i = 1,#avoid_t do local a_ic = avoid_t[a_i] if a_i ~= 1 then a_ic = '{' .. a_ic end if a_i ~= #avoid_t then a_ic = a_ic .. '-' end body = body .. codeNowikiWithColor(working_frame, code_lang, a_ic) end end end return body .. out_new_line() end function p._getVars(VarMode, const) local frame = mw.getCurrentFrame() local mark_get_mark, max_id = frame:extensionTag( 'nowiki', 'nowiki' ), '' gsub(mark_get_mark, 'nowiki%-([0-9A-Za-z]+)%-', function(id)max_id = id end) local max_id_num, body = tonumber(max_id, 16), '' local symbol_table = {} for i=1,max_id_num do local try_get = mw.text.unstripNoWiki( p._stripMark('nowiki', string.format( "%08X", i-1 )) ) local in_index = 1 gsub(try_get, messages.magic_space .. "%$([A-Z]+)_DEF%s+([^=]+)=([^" .. messages.magic_space .. ']*)', function(sub_mode, f_key, f_value)--VarMode local key, value = f_key, f_value key = trim(key) if key ~= '' then local it if const == true then it = symbol_table[key] end local old = symbol_table[key] if sub_mode == 'DELETE' then symbol_table[key] = nil elseif sub_mode == VarMode then symbol_table[key] = it or { name=key, value=value, hist=((old or {}).hist and ( (old or {}).hist .. '$\127;\127^' ) or '') .. value, addr=string.format( "%d:%d", (i-1), in_index ) }end end in_index = in_index + 1 end) end return symbol_table end function p._stripMark(mark_name, mark_id) return printf(messages.strip_mark, mark_name, mark_id) end return p
Summary:
Please note that all contributions to wiki may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Wiki:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Templates used on this page:
Template:%s
(
edit
)
Template:Error
(
edit
)
Navigation menu
Personal tools
Not logged in
Talk
Contributions
Create account
Log in
Namespaces
Page
Discussion
English
Views
Read
Edit
View history
More
Search
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Tools
What links here
Related changes
Special pages
Page information