置顶公告:【置顶】关于临时开启评论区所有功能的公告(2022.10.22) | 【置顶】关于本站Widget恢复使用的公告
  • 你好~!欢迎来到萌娘百科镜像站!如需查看或编辑,请联系本站管理员注册账号。
  • 本镜像站和其他萌娘百科的镜像站无关,请注意分别。

Module:少女歌剧

猛汉♂百科,万男皆可猛的百科全书!转载请标注来源页面的网页链接,并声明引自猛汉百科。内容不可商用。
跳到导航 跳到搜索
Template-info.svg 模块文档  [创建] [刷新]
  1. local module = {}
  2. module.BASEPAGE = "Module:少女歌剧" -- 常量,表示获取少女歌剧专题信息查询模块根页面路径。
  3. module.DATABASEPAGE = module.BASEPAGE .. "/Data" -- 常量,表示获取Data的模块根页面路径。
  4. module.NGROUPPAGE = module.BASEPAGE .. "/NGroup" -- 常量,表示获取名称组列表的模块根页面路径。
  5. local mps = require("Module:ModulePageSystem")
  6. --[==[
  7. 加载某一个模块页中的数据。
  8. --]==]
  9. local loadData = function(page, ignoreerror)
  10. if page == nil then return nil end
  11. local title = mw.title.new(page)
  12. if title == nil then
  13. mps.util.error(mw.ustring.format("加载数据页\"%s\"时发生错误:%s", page, "页面路径地址不符合规范。"), mps.util.ELID_WARNING)
  14. return nil
  15. elseif not title.exists then
  16. mps.util.error(mw.ustring.format("加载数据页\"%s\"时发生错误:%s", page, "页面不存在。"), mps.util.ELID_WARNING)
  17. return nil
  18. end
  19. local success, result = pcall(mw.loadData, page)
  20. if success then
  21. return result
  22. else
  23. mps.util.error(mw.ustring.format("加载数据页\"%s\"时发生错误:%s", page, result), mps.util.ELID_WARNING)
  24. return nil
  25. end
  26. end
  27. --[==[
  28. 获取传入的参数,若#invoke时传入至少一个以数字为参数名的参数,则使用#invoke时传入的参数列表;否则使用包装了模块#invoke的模板传入的参数列表。
  29. --]==]
  30. local getArgs = function(frame)
  31. local args
  32. if (function()
  33. for k, _ in pairs(frame.args) do
  34. if type(k) == "number" then
  35. return true
  36. end
  37. end
  38. end)() then
  39. args = frame.args
  40. else
  41. args = require("Module:Arguments").getArgs(frame:getParent() or frame)
  42. end
  43. return args
  44. end
  45. --[==[
  46. 获取分级索引关键词。
  47. --]==]
  48. local getPath = function(args, normalize)
  49. local path = {}
  50. for _, name in ipairs(args) do
  51. if normalize == true then name = mps.util.normalize(name) end
  52. table.insert(path, name)
  53. end
  54. return path
  55. end
  56. -- 可匹配多个正则。
  57. mw.ustring.gsub_m = function(s, patterns, repl, n)
  58. local length = mw.ustring.len(s)
  59. local init = 1
  60. local v1, v2
  61. local i = 0
  62. local ss = {}
  63. while (n == nil or i < n) and init < length do
  64. v1, v2 = length + 1, length
  65. local p = nil
  66. for _, pattern in ipairs(patterns) do
  67. local v3, v4 = mw.ustring.find(s, pattern, init, false)
  68. if v3 ~= nil and (v3 < v1 or (v3 == v1 and v4 <= v2)) then
  69. v1, v2 = v3, v4 -- 更新匹配范围。
  70. p = pattern -- 更新使用的正则。
  71. end
  72. end
  73. if p ~= nil then
  74. table.insert(ss, mw.ustring.sub(s, init, v1 - 1))
  75. local new_s = mw.ustring.gsub(mw.ustring.sub(s, v1, v2), p, repl, 1) -- 调用原生替换函数。
  76. table.insert(ss, new_s)
  77. else break
  78. end
  79. init = v2 + 1
  80. i = i + 1
  81. end
  82. table.insert(ss, mw.ustring.sub(s, init))
  83. return table.concat(ss)
  84. end
  85. module.gsub_m = function(str, func)
  86. local patterns = {
  87. "(%$(%$))",
  88. "(%$%(([^)]*)%))",
  89. "(%$(%d))",
  90. "(%$([^($]))"
  91. }
  92. return mw.ustring.gsub_m(str, patterns, func or "-")
  93. end
  94. function module.data(frame)
  95. local args = getArgs(frame)
  96. local ignoreerror = frame.args.ignoreerror == "yes" or args.ignoreerror == "yes" or false
  97. local path = getPath(args)
  98. local page = frame.args.page or args.page or module.DATABASEPAGE
  99. local params = { ngpage = { path = module.NGROUPPAGE, data = loadData(module.NGROUPPAGE) or {} } }
  100. local result = loadData(page, true) or {}
  101. for index, name in ipairs(path) do
  102. if type(result) ~= "table" then -- 上级节点含有值。
  103. -- 回溯一次查询,并将上级节点的键作为目录尝试加载本级页面,以重新从本级页面中查询。
  104. page = mw.ustring.format("%s/%s", page, path[index - 1])
  105. result = loadData(page, true) or {}
  106. end
  107. local keyexists = false
  108. for cname, subnodes in pairs(result) do
  109. if mps.util.KEYCOMPARER_NGROUP(cname, name, params) then -- 表中含有这个值
  110. result = subnodes
  111. keyexists = true
  112. end
  113. end
  114. if not keyexists then
  115. -- 尝试加载次级页面,以从次级页面中继续查询。
  116. page = mw.ustring.format("%s/%s", page, name)
  117. result = loadData(page, true) or {}
  118. end
  119. end
  120. if type(result) == "table" then -- 查询结果不是值。
  121. if not ignoreerror then error("查询到的值为空(可能在指定名称上的值的类型不是字符串,或者指定名称不存在)。")
  122. else return nil
  123. end
  124. else
  125. -- 成功查询到值。
  126. return result
  127. end
  128. end
  129. --[==[
  130. 【将要弃用】
  131. --]==]
  132. local vardefine = function(name, value, frame)
  133. local wiki
  134. if mw.ustring.sub(value, 1, 1) == "$" then -- 是wiki
  135. wiki = mw.ustring.gsub(mw.ustring.sub(value, 2), "%%[%%$]", function(m)
  136. if m == "%%" then return "%"
  137. elseif m == "%$" then return "$"
  138. else return ""
  139. end
  140. end)
  141. wiki = frame:preprocess(wiki)
  142. else
  143. wiki = value
  144. end
  145. return frame:callParserFunction("#vardefine", name, wiki)
  146. end
  147. --[==[
  148. 将指定的名称组通过{{#vardefine}}写入到维基文本中。
  149. --]==]
  150. function module.ngroup(frame)
  151. local ngroup = mw.loadData(module.NGROUPPAGE)
  152. local vdexprs = {}
  153. for gname, names in pairs(ngroup) do
  154. if type(gname) == "string" then
  155. for _, name in ipairs(names) do
  156. local expr = vardefine(mw.ustring.format("%s %s", "名称组", name), gname, frame)
  157. if expr ~= nil then
  158. table.insert(vdexprs, expr) -- 添加定义语句。
  159. end
  160. end
  161. end
  162. end
  163. return table.concat(vdexprs)
  164. end
  165. --[==[
  166. 将指定的数据通过{{#vardefine}}写入到维基文本中。
  167. --]==]
  168. function module.define(frame)
  169. local args = getArgs(frame)
  170. local datapage
  171. if mw.ustring.match(args.datapage or "", "^[/\\]") then -- 是相对路径
  172. datapage = module.DATABASEPAGE .. args.datapage
  173. else -- 是绝对路径
  174. datapage = args.datapage
  175. end
  176. local ngroup = mw.loadData(module.NGROUPPAGE)
  177. local vdexprs = {}
  178. local function defineInternal(data, path, dir)
  179. if type(data) == "table" then
  180. if #path == 0 then table.insert(path, "$") end -- 若path提供的匹配列表没有查询到底层节点,则插入默认的匹配所有键正则以查询到底层节点。
  181. elseif type(data) == "string" then
  182. if #path > 0 then error("无法继续向下层节点查询,因为当前节点已经是底层节点。") end
  183. local expr = vardefine(dir, data, frame)
  184. if expr ~= nil then
  185. table.insert(vdexprs, expr) -- 添加定义语句。
  186. end
  187. return
  188. else return -- 不支持其他类型的值。
  189. end
  190. local pattern = path[1]
  191. local isngroup = mw.ustring.sub(pattern, 1, 2) == "$$" -- 仅匹配名称组时在开头添加两个$字符。
  192. local isall = not isngroup and (mw.ustring.sub(pattern, 1, 1) == "$") -- 匹配所有键时在开头添加一个$字符。
  193. for dkey, dvalue in pairs(data) do
  194. if isall or (mw.ustring.sub(dkey, 1, 1) == "$") == isngroup then -- 同或,表示数据键和查找键正则同时是或同时不是名称组。
  195. if mw.ustring.sub(dkey, 1, 1) == "$" then -- 数据键是名称组。
  196. local ngkey = mps.util.normalize(mw.ustring.sub(dkey, 2)) -- 获取名称组键。
  197. if isall then
  198. pattern = mw.ustring.sub(pattern, 2) -- 获取正则。
  199. elseif isngroup then
  200. pattern = mw.ustring.sub(pattern, 3) -- 获取正则。
  201. end
  202. if mw.ustring.match(ngkey, pattern) then -- 匹配成功。
  203. local newdata = dvalue
  204. local newpath = {}
  205. for i = 2, #path do table.insert(newpath, path[i]) end
  206. local newdir
  207. if dir == nil then
  208. newdir = ngkey
  209. else
  210. newdir = mw.ustring.format("%s %s", dir, ngkey)
  211. end
  212. defineInternal(newdata, newpath, newdir)
  213. end
  214. else -- 数据键不是名称组。
  215. dkey = mps.util.normalize(dkey)
  216. if isall then
  217. pattern = mw.ustring.sub(pattern, 2) -- 获取正则。
  218. end
  219. if mw.ustring.match(dkey, pattern) then -- 匹配成功。
  220. local newdata = dvalue
  221. local newpath = {}
  222. for i = 2, #path do table.insert(newpath, path[i]) end
  223. local newdir
  224. if dir == nil then
  225. newdir = dkey
  226. else
  227. newdir = mw.ustring.format("%s %s", dir, dkey)
  228. end
  229. defineInternal(newdata, newpath, newdir)
  230. end
  231. end
  232. end
  233. end
  234. end
  235. defineInternal(mw.loadData(datapage), getPath(args), nil)
  236. return table.concat(vdexprs)
  237. end
  238. -- Only for Template:九九组成员对她的称呼 and Template:舞台少女称呼表
  239. function module.aishou(frame)
  240. local args = frame.args
  241. local data = mw.loadData(module.DATABASEPAGE.."/角色称呼表")
  242. local name = args.name or "爱城华恋" -- 她的名字。
  243. local node = mw.html.create("table")
  244. :css("font-size", "89%")
  245. :css("text-align", "center")
  246. :css("max-width", "260px")
  247. :css("float", "right")
  248. :css("background-color", "white")
  249. :tag("tr"):tag("th")
  250. :css("color", "white")
  251. :css("background-color", frame:callParserFunction{ name = "#var", args = { name } })
  252. :css("font-size", "100%")
  253. :css("font-weight", "bold")
  254. :css("padding", "1em")
  255. :attr("colspan", 2)
  256. :wikitext("其他人对")
  257. :wikitext(frame:expandTemplate{ title = "少女歌剧/角色信息", args = { name, "姓名地区转换" } })
  258. :wikitext("的称呼")
  259. :allDone()
  260. for _, cname in ipairs(module.sortNameGroup()) do
  261. local cothers = data["$" .. cname] or {}
  262. for cother, ass in pairs(cothers) do
  263. cother = mw.text.trim(cother, "$") -- 简单处理。
  264. if name == cother then
  265. -- 处理格式字符串 --
  266. local as_process = function(as)
  267. local patterns = {
  268. "(%$(%$))",
  269. "(%$%(([^)]*)%))",
  270. "(%$([^($]))"
  271. }
  272. -- 进行通用格式字符串替换,获得新格式字符串。
  273. as = mw.ustring.gsub_m(as, patterns, function(rawtext, matchtext)
  274. if rawtext == "$$" then return "$$"
  275. else
  276. local numindex = tonumber(matchtext) -- 如果索引值是数字,优先转换为number类型键。
  277. if numindex then
  278. return data.COMMON[numindex] or data.COMMON[matchtext] or rawtext
  279. else
  280. if not mw.ustring.find(matchtext, "^日文") then
  281. matchtext = "日文"..matchtext -- 在前方添加“日文”。
  282. rawtext = "$("..matchtext..")" -- 在前方添加“日文”。
  283. end
  284. return data.COMMON[matchtext] or rawtext
  285. end
  286. end
  287. end)
  288. as = mw.ustring.gsub_m(as, patterns, function(rawtext, matchtext)
  289. if rawtext == "$$" then return "$"
  290. else
  291. return frame:expandTemplate{ title = "少女歌剧/角色信息", args = { name, matchtext } } -- 调用模板,获取角色信息。
  292. end
  293. end)
  294. return as
  295. end
  296. local empty = false
  297. if type(ass) == "table" then
  298. local _ass = {}
  299. for i_as, as in ipairs(ass) do
  300. _ass[i_as] = frame:expandTemplate{ title = "lj", args = { as_process(as) } }
  301. end
  302. if #_ass == 0 then empty = true
  303. else
  304. ass = table.concat(_ass, "、")
  305. end
  306. else
  307. if mw.text.trim(ass) == "" then empty = true
  308. else
  309. ass = frame:expandTemplate{ title = "lj", args = { as_process(ass) } }
  310. end
  311. end
  312. if not empty then
  313. local ltext
  314. if name == cname then
  315. ltext = "自称"
  316. else
  317. ltext =
  318. frame:expandTemplate{ title = "少女歌剧/角色表述", args =
  319. {
  320. cname,
  321. "内链",
  322. frame:expandTemplate{ title = "Color", args =
  323. {
  324. "white",
  325. frame:expandTemplate{ title = "少女歌剧/角色信息", args = { cname, "名地区转换" } }
  326. } }
  327. } }
  328. end
  329. --------------------
  330. node:tag("tr")
  331. :tag("td")
  332. :css("color", "white")
  333. :css("background-color", frame:callParserFunction{ name = "#var", args = { cname } })
  334. :css("font-weight", "bold")
  335. :css("padding", "0 1em")
  336. :css("min-width", "80px")
  337. :wikitext(ltext)
  338. :done()
  339. :tag("td")
  340. :css("padding", "0 1em")
  341. :css("min-width", "140px")
  342. :wikitext(ass)
  343. end
  344. end
  345. end
  346. end
  347. return tostring(node)
  348. end
  349. -- 对名称组列表list,按categories(匿名函数)列表指示的顺序排序。
  350. function module.sortNameGroup(list, --[[ categories ]]...)
  351. local categories = {}
  352. -- 整理排序分类列表。
  353. for i = 1, select("#", ...) do
  354. local cat = select(i, ...)
  355. if cat then
  356. table.insert(categories, cat)
  357. end
  358. end
  359. local data = loadData(module.NGROUPPAGE) -- 加载名称组数据。
  360. if #categories == 0 then categories = data[0] end -- 使用默认排序分类列表。
  361. local result = {}
  362. for _, cat in ipairs(categories) do
  363. for _, catinfo in ipairs(data) do
  364. if catinfo.category == cat then
  365. for _, _ng in ipairs(catinfo) do
  366. if type(list) == "table" then
  367. for _, ng in ipairs(list) do
  368. if ng == _ng then
  369. table.insert(result, ng)
  370. end
  371. end
  372. elseif type(list) == "string" and list == _ng then
  373. table.insert(result, list)
  374. elseif list == nil then
  375. table.insert(result, _ng)
  376. else
  377. mps.util.error(mw.ustring.format("参数list的值类型错误。(应为%s,实际为%s)", "table、string或nil", type(list)), mps.util.ELID_ERROR)
  378. end
  379. end
  380. end
  381. end
  382. end
  383. return result
  384. end
  385. return module