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

Module:Nav

猛汉♂百科,万男皆可猛的百科全书!转载请标注来源页面的网页链接,并声明引自猛汉百科。内容不可商用。
跳到导航 跳到搜索
Template-info.svg 模块文档  [查看] [编辑] [历史] [刷新]

简介

本模块为优化分析器展开模板过程中耗费的各项资源,仅针对{{Navbar}}、{{Navbox}}及其姊妹模板,作者为User:サンムル,部分模板在翻译改写过程中按照使用者习惯等因素对参数有部分增删。

外部调用

nav.bar

模块的导出函数之一,在Wiki代码中通过{{#invoke:Nav|bar}}调用,用于构建模板{{Navbar}}的Wiki代码。

nav.box

模块的导出函数之一,在Wiki代码中通过{{#invoke:Nav|box}}调用,用于构建模板{{Navbox}}及其姊妹模板的Wiki代码。

模块的第二个无名参数为Navbox的辅助模板{{Navbox subgroup}}、{{Navbox with columns}}和{{Navbox with collapsible groups}}样式启用进行填写,例如:{{#invoke:Nav|box|collapsible groups}},目前Navbox subgroup已经使用本模块,建议不要另外调用。

  1. local nav = {}
  2. local notnil = function(value, default) return value or default end
  3. local __nn = notnil
  4. local notnilnorempty = function(value, default)
  5. if value == nil then return default
  6. elseif type(value) == "string" and value == "" then return default
  7. else return value
  8. end
  9. end
  10. local __nne = notnilnorempty
  11. local notnilnorwhitespace = function(value, default)
  12. if value == nil then return default
  13. elseif type(value) == "string" then
  14. if value == "" then return value
  15. elseif mw.text.trim(value) == "" then return default
  16. else return value
  17. end
  18. else return value
  19. end
  20. end
  21. local __nnw = notnilnorwhitespace
  22. local notnilnoremptynorwhitespace = function(value, default)
  23. if value == nil then return default
  24. elseif type(value) == "string" and mw.text.trim(value) == "" then return default
  25. else return value
  26. end
  27. end
  28. local __nnew = notnilnoremptynorwhitespace
  29. local iif = function(condition, truevalue, falsevalue)
  30. if condition then return truevalue
  31. else return falsevalue
  32. end
  33. end
  34. --[[
  35. 获取当前参数的序号,支持多个序号,且支持多种写法兼容。
  36. pattern:
  37. 格式以正则表达式为基础,另增以下匹配符:
  38. % - 将自动替换成正则表达式的“(%d+)”,用以匹配这个位置上的序号。
  39. %% - 将自动替换成正则表达式的“%”,用作正则表达式中“%”字符的转义。
  40. 返回:
  41. 【所有序号文本】 【所有序号数值】
  42. 注:以上每个值段都是一个独立的返回值列表项;
  43. 示例:
  44. paramname: prefix000infix10postfix08
  45. pattern: ^prefix%infix%postfix%$
  46. 返回:000 10 08 0 10 8
  47. --]]
  48. local paramindexes = function(paramname, pattern)
  49. local indexes = { mw.ustring.match(paramname, mw.ustring.gsub(pattern, "%%%%?", function(m)
  50. if m == "%%" then return "%"
  51. else return "(%d+)"
  52. end
  53. end)) }
  54. local count = #indexes
  55. if count ~= 0 then
  56. for i = 1, count do
  57. indexes[i + count] = tonumber(indexes[i])
  58. end
  59. return unpack(indexes)
  60. end
  61. end
  62. local indexedparamvalue = function(args, pattern, raw, index, rawfunc, indexfunc)
  63. if args == nil or pattern == nil or raw == nil or index == nil then return nil end
  64. if rawfunc ~= nil and indexfunc == nil then indexfunc = rawfunc end
  65. rawfunc = rawfunc or __nnew
  66. indexfunc = indexfunc or __nnew
  67. if type(raw) ~= "table" then raw = { raw } end
  68. if type(index) ~= "table" then index = { index } end
  69. local i = 0
  70. rawname = mw.ustring.gsub(pattern, "%%%%?", function(m)
  71. if m == "%%" then return "%"
  72. else
  73. i = i + 1
  74. return tostring(raw[i] or "")
  75. end
  76. end)
  77. rawvalue = args[rawname]
  78. local result = rawfunc(rawvalue)
  79. if result then return result end
  80. i = 0
  81. indexname = mw.ustring.gsub(pattern, "%%%%?", function(m)
  82. if m == "%%" then return "%"
  83. else
  84. i = i + 1
  85. return tostring(index[i] or "")
  86. end
  87. end)
  88. indexvalue = args[indexname]
  89. result = indexfunc(indexvalue)
  90. if result then return result end
  91. local fuzzyMatchingOn = __nnew(args.fuzzyMatchingOn, "no") == "yes" -- 模糊匹配。
  92. if not fuzzyMatchingOn then return nil end -- 不启用参数名模糊匹配时处理流程到此结束。
  93. -- 开始进行参数名模糊匹配。
  94. -- 由于大多数参数名格式均为“【名称】【数字序号】”,将【名称】前缀作为第一道筛选程序将会减少耗时的正则匹配函数运行次数,大大优化代码运行效率。
  95. local prefixpos = 1
  96. local v1, v2 = 1, 0
  97. while true do
  98. local v1, v2 = mw.ustring.find(pattern, "%%+", v1)
  99. if v1 == nil then
  100. prefixpos = mw.ustring.len(pattern) + 1
  101. break
  102. elseif (v2 - v1) % 2 == 0 then
  103. prefixpos = v2
  104. break
  105. else
  106. v1 = v2 + 1
  107. end
  108. end
  109. local prefix = mw.ustring.gsub(mw.ustring.sub(pattern, 1, prefixpos - 1), "%%%%", "%") -- 获取纯文本前缀。
  110. local prefixlen = mw.ustring.len(prefix) -- 获取纯文本前缀字符长度。
  111. for k, v in pairs(args) do
  112. if k ~= rawname and k ~= indexname and mw.ustring.sub(k, 1, prefixlen) == prefix then -- 排除已处理参数名称,并筛选【名称】前缀。
  113. local indexes = { paramindexes(k, "^"..pattern.."$") } -- 获取各序号部分。
  114. local offset = #indexes / 2 -- 纯数字序号部分的偏移值,也作为序号的个数。
  115. if #index == offset then -- 序号数量一致。
  116. local equal = true -- 序号序列一致标识符。
  117. for _i, _index in ipairs(index) do
  118. if _index ~= indexes[_i + offset] then
  119. equal = false
  120. break
  121. end
  122. end
  123. if equal then
  124. result = rawfunc(v) or indexfunc(v)
  125. if result then return result end
  126. end
  127. end
  128. end
  129. end
  130. end
  131. local xor = function(left, right) return (left == true and right ~= true) or (left ~= true and right == true) end
  132. local detectevenodd = function(list_previous, evenodd_i, evenodd, iseven)
  133. if evenodd_i == "swap" then
  134. if evenodd == "even" or evenodd == "odd" then
  135. return true, evenodd -- 每次swap都交换一次,基础值:全局evenodd。
  136. else
  137. return true, nil -- 每次swap都交换一次。
  138. end
  139. end
  140. if evenodd == "even" or evenodd == "odd" then
  141. return false, evenodd -- 不交换,基础值:全局evenodd。
  142. end
  143. if list_previous then
  144. -- 在上一个列表的HTML代码中查找最后一项的class。
  145. list_previous = mw.ustring.gsub(list_previous, "<!%-%-.-%-%->", "") -- 删除HTML注释。
  146. local evenodd_previous = nil -- 上一个navbox-list奇偶样式(抓取自td标签的class属性)。
  147. for tagstart, classstr in mw.ustring.gmatch(list_previous, [[<%s*([Tt][Dd][^>]-)%s[Cc][Ll][Aa][Ss][Ss]="([^"]-navbox%-list[^"]-)"]]) do
  148. if mw.text.trim(mw.ustring.sub(tagstart, 3, 3)) == "" then -- 标签是td。
  149. for _, class in ipairs(mw.text.split(classstr, "%s+")) do
  150. class = mw.ustring.match(class, "^navbox%-(.+)$")
  151. if class == "even" or class == "odd" then
  152. evenodd_previous = class
  153. end
  154. end
  155. end
  156. end
  157. if evenodd_previous then -- 找到上一个列表中的最后一项的class
  158. return xor(evenodd_previous == "even", not iseven), nil -- 当最后一项的奇偶性与当前项的奇偶性相同时交换一次。
  159. end
  160. end
  161. return false, nil -- 不进行样式修正。
  162. end
  163. local function _bar(args, frame)
  164. local node = mw.html.create()
  165. local nodiv = __nnew(args.nodiv)
  166. local style = __nnew(args.style)
  167. if nodiv then
  168. node = node:wikitext("&nbsp;"):tag("span")
  169. else
  170. node = node:tag("div")
  171. end
  172. node:addClass("noprint")
  173. :addClass("plainlinks")
  174. :addClass("hlist")
  175. :addClass("navbar")
  176. :addClass("nomobile")
  177. :cssText(style)
  178. ---------------核心代码---------------
  179. --- 左方括号 ---
  180. local brackets = __nnew(args.brackets)
  181. if brackets then
  182. node = node:wikitext("&#91;")
  183. end
  184. --- 前文 ---
  185. local mini = __nnew(args.mini)
  186. local miniv = __nnew(args.miniv)
  187. local plain = __nnew(args.plain) or __nnew(args.viewplain)
  188. if not (mini or miniv or plain) then
  189. node:wikitext("本模板:&nbsp;")
  190. end
  191. local _1 = __nn(args[1], "{{{1}}}")
  192. local fontstyle = __nnew(args.fontstyle)
  193. local fontcolor = __nnew(args.fontcolor, "#002bb8")
  194. local history = __nnew(args.history)
  195. local purge = __nnew(args.purge)
  196. local span
  197. node:wikitext("[[Template:".._1.."|")
  198. span = node:tag("span")
  199. :css("background", "transparent!important")
  200. if fontstyle then
  201. span:cssText(fontstyle)
  202. else
  203. span:css("color", fontcolor)
  204. end
  205. span:attr("title", "-{zh-hans:查看;zh-hant:檢視}-这个模板")
  206. if miniv then
  207. span:wikitext("v")
  208. elseif plain then
  209. span:wikitext("view")
  210. elseif mini then
  211. span:wikitext("查")
  212. else
  213. span:wikitext("-{zh-hans:查看;zh-hant:檢視}-")
  214. end
  215. node:wikitext("]]")
  216. if not (miniv or plain) then
  217. node:wikitext("&nbsp;·&nbsp;[[Template talk:".._1.."|")
  218. span = node:tag("span")
  219. :css("background", "transparent!important")
  220. if fontstyle then
  221. span:cssText(fontstyle)
  222. else
  223. span:css("color", fontcolor)
  224. end
  225. span:attr("title", "关于这个模板的讨论页面")
  226. if mini then
  227. span:wikitext("论")
  228. else
  229. span:wikitext("讨论")
  230. end
  231. node:wikitext("]]&nbsp;·&nbsp;[")
  232. :wikitext(frame:preprocess("{{fullurl:Template:".._1.."|action=edit}}"))
  233. :wikitext(" ")
  234. span = node:tag("span")
  235. :css("background", "transparent!important")
  236. if fontstyle then
  237. span:cssText(fontstyle)
  238. else
  239. span:css("color", fontcolor)
  240. end
  241. span:attr("title", "您可以编辑这个模板。请在储存变更之前先预览")
  242. if mini then
  243. span:wikitext("编")
  244. else
  245. span:wikitext("编辑")
  246. end
  247. node:wikitext("]")
  248. if history then
  249. node:wikitext("&nbsp;·&nbsp;[")
  250. :wikitext(frame:preprocess("{{fullurl:Template:".._1.."|action=history}}"))
  251. :wikitext(" ")
  252. span = node:tag("span")
  253. :css("background", "transparent!important")
  254. if fontstyle then
  255. span:cssText(fontstyle)
  256. else
  257. span:css("color", fontcolor)
  258. end
  259. span:attr("title", "-{zh-hans:查看;zh-hant:查詢}-这个模板的编辑历史")
  260. if mini then
  261. span:wikitext("历")
  262. else
  263. span:wikitext("历史")
  264. end
  265. node:wikitext("]")
  266. end
  267. if purge then
  268. node:wikitext("&nbsp;·&nbsp;[")
  269. :wikitext(frame:preprocess("{{fullurl:Template:".._1.."|action=purge}}"))
  270. :wikitext(" ")
  271. span = node:tag("span")
  272. :css("background", "transparent!important")
  273. if fontstyle then
  274. span:cssText(fontstyle)
  275. else
  276. span:css("color", fontcolor)
  277. end
  278. span:attr("title", "清除这个模板的缓存")
  279. if mini then
  280. span:wikitext("清")
  281. else
  282. span:wikitext("清除缓存")
  283. end
  284. node:wikitext("]")
  285. end
  286. end
  287. --- 右方括号 ---
  288. if brackets then
  289. node = node:wikitext("&#93;"):done()
  290. end
  291. --------------------------------------
  292. if nodiv then
  293. node:wikitext("&nbsp;")
  294. end
  295. return node
  296. end
  297. local function _box(args, frame)
  298. local node = mw.html.create()
  299. local border = __nnew(args.border) or __nne(args[1])
  300. if border ~= nil then border = mw.text.trim(border) end
  301. -- 删去可能会有的多余的空白字符。
  302. if type(border) == "string" then
  303. border = mw.text.trim(border)
  304. end
  305. -- 当前模板用于生成子列表时,关闭父模板用于padding的div
  306. if border == "subgroup" or border == "child" then
  307. node:wikitext("</div>")
  308. elseif border ~= "none" then
  309. node = node:tag("table")
  310. :addClass("navbox")
  311. :addClass(__nnew(args.class))
  312. :attr("cellspacing", 0)
  313. :cssText(__nnew(args.bodystyle))
  314. :cssText(__nnew(args.style))
  315. :tag("tr")
  316. :tag("td")
  317. :css("padding", "2px")
  318. end
  319. node = node:tag("table")
  320. :attr("cellspacing", 0)
  321. :addClass("nowraplinks")
  322. :css("display", "table")
  323. :css("width", "100%")
  324. :cssText(__nnew(args.innerstyle))
  325. local title = __nnew(args.title)
  326. local state = __nnew(args.state)
  327. if title then
  328. if state ~= "plain" and state ~= "off" then
  329. node:addClass("mw-collapsible")
  330. :addClass(state or "autocollapse")
  331. end
  332. end
  333. if border == "subgroup" or border == "child" or border == "none" then
  334. node:addClass("navbox-subgroup")
  335. :cssText(__nnew(args.bodystyle))
  336. :cssText(__nnew(args.style))
  337. else
  338. node:css("background", "transparent")
  339. :css("color", "inherit")
  340. end
  341. ---------------核心代码---------------
  342. local imageleft = __nnew(args.imageleft)
  343. local image = __nnew(args.image)
  344. --- Title and Navbar ---
  345. local grouppadding = __nnew(args.grouppadding)
  346. local groupstyle = __nnew(args.groupstyle)
  347. local basestyle = __nnew(args.basestyle)
  348. if title then
  349. local temp = node
  350. node = node:tag("tr")
  351. local titlegroup = __nnew(args.titlegroup)
  352. if titlegroup then
  353. node = node:tag("td")
  354. :addClass("navbox-group")
  355. :cssText(basestyle)
  356. :css("padding", grouppadding or ("0 "..iif(border == "subgroup", "0.75em", "1em")))
  357. :cssText(groupstyle)
  358. :cssText(__nnew(args.titlegroupstyle))
  359. :wikitext(titlegroup)
  360. :tag("th")
  361. :css("border-left", "2px solid #fdfdfd")
  362. :css("width", "100%")
  363. else
  364. node = node:tag("th")
  365. end
  366. local titlestyle = __nnew(args.titlestyle)
  367. node:cssText(basestyle)
  368. :cssText(titlestyle)
  369. :attr("colspan", 2 + iif(imageleft, 1, 0) + iif(image, 1, 0) + iif(titlegroup, -1, 0))
  370. :addClass("navbox-title")
  371. local navbar = __nnew(args.navbar)
  372. local name = __nnew(args.name)
  373. if (navbar == "plain" or navbar == "off") or
  374. (not name and (border == "subgroup" or border == "child" or border == "none")) then
  375. if navbar == "off" then
  376. if state == "plain" then
  377. node:tag("div")
  378. :css("float", "right")
  379. :css("width", "2.78em")
  380. :wikitext("&nbsp;")
  381. end
  382. elseif state ~= "plain" then
  383. node:tag("div")
  384. :css("float", "left")
  385. :css("width", "2.78em")
  386. :css("text-align", 'left')
  387. :wikitext("&nbsp;")
  388. end
  389. else
  390. node:tag("div")
  391. :css("float", "left")
  392. :css("width", "2.78em")
  393. :css("text-align", 'left')
  394. :tag("span")
  395. :addClass("mobileonly")
  396. :wikitext("&nbsp;")
  397. :done()
  398. :node(_bar({
  399. [1] = args.name,
  400. fontstyle = iif(basestyle, (basestyle or "")..";", "")..iif(titlestyle, (titlestyle or "")..";", "").."border:none",
  401. mini = 1
  402. }, frame))
  403. if state == "plain" then
  404. node:tag("div")
  405. :css("float", "right")
  406. :css("width", "2.78em")
  407. :wikitext("&nbsp;")
  408. end
  409. end
  410. node:tag("span")
  411. :css("font-size", iif(border == "subgroup" or border == "child" or border == "none", "100%", "110%"))
  412. :wikitext(args.title or "{{{title}}}")
  413. node = temp -- 复原节点位置
  414. end
  415. --- Above ---
  416. local above = __nnew(args.above)
  417. if above then
  418. if title then
  419. node:tag("tr"):css("height", "2px"):tag("td")
  420. end
  421. node:tag("tr"):tag("td")
  422. :addClass("navbox-abovebelow")
  423. :cssText(basestyle)
  424. :cssText(__nnew(args.abovestyle))
  425. :attr("colspan", 2 + iif(imageleft, 1, 0) + iif(image, 1, 0))
  426. :wikitext(args.above or "{{{above}}}")
  427. end
  428. --- Body ---
  429. local lists = {}
  430. local listmax = 0
  431. for k, v in pairs(args) do
  432. if type(k) == "string" and mw.ustring.sub(k, 1, 4) == "list" then
  433. local raw, index = paramindexes(k, "^list%$")
  434. if index ~= nil and index > 0 and __nnew(v) then
  435. table.insert(lists, { raw, index, v }) -- 添加新项
  436. lists[tostring(index)] = #lists -- 将字符串格式的搜索键映射到对应的新项索引
  437. listmax = math.max(listmax, index)
  438. end
  439. end
  440. end
  441. --- groups/lists ---
  442. local evenstyle = __nnew(args.evenstyle)
  443. local oddstyle = __nnew(args.oddstyle)
  444. local evenodd = __nnew(args.evenodd)
  445. local liststyle = __nnew(args.liststyle)
  446. local listpadding = __nnew(args.listpadding)
  447. local visiblelist = 0 -- 可见的列表。
  448. local imageleftnode, imagenode
  449. local swap = evenodd == "swap" -- 列表项奇偶样式是否交换。
  450. local autoSwapOn = __nnew(args.autoSwapOn, "yes") == "yes" -- 自动交换开关。
  451. for index = 1, listmax do
  452. if lists[tostring(index)] then -- 存在这个键
  453. visiblelist = visiblelist + 1 -- 增加计数。
  454. local raw, _, list_i = unpack(lists[lists[tostring(index)]])
  455. if visiblelist > 1 or (title or above) then
  456. node:tag("tr"):css("height", "2px"):tag("td")
  457. end
  458. local tr = node:tag("tr")
  459. if visiblelist == 1 then
  460. if imageleft then
  461. imageleftnode = tr:tag("td")
  462. :css("width", "0%")
  463. :css("padding", "0px 2px 0px 0px")
  464. :cssText(__nnew(args.imageleftstyle))
  465. :wikitext(imageleft)
  466. end
  467. end
  468. local td
  469. local group_i = indexedparamvalue(args, "group%", raw, index)
  470. if group_i then
  471. local groupstyle_i = indexedparamvalue(args, "group%style", raw, index)
  472. td = tr:tag("td")
  473. :addClass("navbox-group")
  474. :cssText(basestyle)
  475. :css("padding", grouppadding or ("0 "..iif(border == "subgroup", "0.75em", "1em")))
  476. :cssText(groupstyle)
  477. :cssText(groupstyle_i)
  478. :wikitext(group_i)
  479. :done()
  480. :tag("td")
  481. :css("text-align", "left")
  482. :css("border-left", "2px solid #fdfdfd")
  483. else
  484. td = tr:tag("td")
  485. :attr("colspan", 2)
  486. end
  487. local liststyle_i = indexedparamvalue(args, "list%style", raw, index)
  488. local list_previous = nil -- 上一个可见列表内容。
  489. if autoSwapOn then -- 通过控制list_previous是否为nil来控制是否执行自动交换程序。
  490. for _index = index - 1, 1, -1 do
  491. if lists[tostring(_index)] then -- 存在这个键
  492. _, _, list_previous = unpack(lists[lists[tostring(_index)]])
  493. break
  494. end
  495. end
  496. end
  497. local evenodd_i = indexedparamvalue(args, "evenodd%", raw, index)
  498. if evenodd_i ~= "even" and evenodd_i ~= "odd" then
  499. local swap_i = nil
  500. swap_i, evenodd_i = detectevenodd(list_previous, evenodd_i, evenodd, xor(visiblelist % 2 == 0, swap)) -- 查找上一列表中的最后一项的样式。
  501. if swap_i then swap = not swap end -- 交换列表项奇偶样式。
  502. if evenodd_i then
  503. if swap then
  504. evenodd_i = iif(evenodd_i == "even", "odd", "even")
  505. end
  506. else
  507. evenodd_i = iif(xor(visiblelist % 2 == 0, swap), "even", "odd")
  508. end
  509. end
  510. td:addClass("navbox-list")
  511. :addClass("navbox-"..evenodd_i)
  512. :css("width", "100%")
  513. :css("padding", "0px")
  514. :cssText(liststyle)
  515. if evenodd_i == "even" then td:cssText(evenstyle)
  516. elseif evenodd_i == "odd" then td:cssText(oddstyle)
  517. else td:cssText(iif(xor(visiblelist % 2 == 0, swap), evenstyle, oddstyle)) -- 偶数行使用evenstyle,奇数行使用oddstyle。(特殊情况下交换。)
  518. end
  519. td:cssText(liststyle_i)
  520. :tag("div")
  521. :css("padding", listpadding or "0em 0.25em")
  522. :wikitext(list_i)
  523. if visiblelist == 1 then
  524. if image then
  525. imagenode = tr:tag("td")
  526. :css("width", "0%")
  527. :css("padding", "0px 0px 0px 2px")
  528. :cssText(__nnew(args.imagestyle))
  529. :wikitext(image)
  530. end
  531. end
  532. end
  533. end
  534. if imageleftnode then
  535. imageleftnode:attr("rowspan", visiblelist * 2 - 1)
  536. end
  537. if imagenode then
  538. imagenode:attr("rowspan", visiblelist * 2 - 1)
  539. end
  540. --- Below ---
  541. local below = __nnew(args.below)
  542. if below then
  543. if title or above or visiblelist ~= 0 then
  544. node:tag("tr"):css("height", "2px"):tag("td")
  545. end
  546. node:tag("tr"):tag("td")
  547. :addClass("navbox-abovebelow")
  548. :cssText(basestyle)
  549. :cssText(__nnew(args.belowstyle))
  550. :attr("colspan", 2 + iif(imageleft, 1, 0) + iif(image, 1, 0))
  551. :wikitext(below)
  552. end
  553. --------------------------------------
  554. node = node:allDone() -- 回到最上层节点。
  555. -- 当前模板用于生成子列表时,对标上文父模板被关闭的div
  556. if border == "subgroup" or border == "child" then
  557. node:wikitext("<div>")
  558. end
  559. return node
  560. end
  561. local function _subgroupbox(args, frame)
  562. args.border = __nnew(args.border, "subgroup")
  563. args.style = (args.style or "")..iif(args.bodystyle, ";"..(args.bodystyle or ""), "")
  564. return args
  565. end
  566. -- 前排提醒,不要边遍历table边给元素设置为nil,特别是非当前元素!
  567. local function _collapsiblegroupsbox(args, frame)
  568. local newargs = {}
  569. newargs[1] = args[2]
  570. newargs.style = (args.style or "")..iif(args.bodystyle, ";"..(args.bodystyle or ""), "")
  571. local selected = __nnew(args.selected)
  572. local basestyle = __nnew(args.basestyle)
  573. local groupstyle = __nnew(args.groupstyle)
  574. local sectstyle = __nnew(args.sectstyle)
  575. local secttitlestyle = __nnew(args.secttitlestyle)
  576. local liststyle = __nnew(args.liststyle)
  577. for k, v in pairs(args) do
  578. if type(k) == "string" then
  579. local raw, index = paramindexes(k, "^list%$")
  580. if index ~= nil and index > 0 and __nnew(v) then
  581. local group_i = indexedparamvalue(args, "group%", raw, index)
  582. local sect_i = indexedparamvalue(args, "sect%", raw, index)
  583. local abbr_i = indexedparamvalue(args, "abbr%", raw, index)
  584. local state_i = indexedparamvalue(args, "state%", raw, index)
  585. local groupstyle_i = indexedparamvalue(args, "group%style", raw, index)
  586. local sectstyle_i = indexedparamvalue(args, "sect%style", raw, index)
  587. local liststyle_i = indexedparamvalue(args, "list%style", raw, index)
  588. local image_i = indexedparamvalue(args, "image%", raw, index)
  589. local imageleft_i = indexedparamvalue(args, "imageleft%", raw, index)
  590. local _args = {
  591. border = "child",
  592. state = iif(selected == abbr_i, "mw-uncollapsed", state_i or "mw-collapsed"),
  593. style = (sectstyle or "")..iif(sectstyle_i, ";"..(sectstyle_i or ""), ""),
  594. titlestyle = (basestyle or "")..iif(groupstyle, ";"..(groupstyle or ""), "")..iif(secttitlestyle, ";"..(secttitlestyle or ""), "")..iif(groupstyle_i, ";"..(groupstyle_i or ""), ""),
  595. liststyle = (liststyle or "")..iif(liststyle_i, ";"..(liststyle_i or ""), ""),
  596. title = group_i or sect_i,
  597. list1 = v,
  598. image = image_i,
  599. imageleft = imageleft_i
  600. }
  601. newargs[k] = tostring(_box(_args, frame))
  602. end
  603. end
  604. end
  605. -- 白名单,不保证没有缺漏或多余
  606. local list_valid = { 'name', 'state', 'navbar', 'border', 'title', 'above', 'image', 'imageleft', 'below', 'selected', 'bodystyle', 'titlestyle',
  607. 'abovestyle', 'belowstyle', 'basestyle', 'imagestyle', 'imageleftstyle', 'sectstyle', 'groupstyle', 'liststyle', 'listpadding' }
  608. for _, val in ipairs(list_valid) do
  609. if args[val] then newargs[val] = args[val] end
  610. end
  611. return newargs
  612. end
  613. local function _columnsbox(args, frame)
  614. args[1] = args[2]
  615. args[2] = nil
  616. local bodystyle = __nnew(args.bodystyle)
  617. args.style = (args.style or "")..iif(bodystyle, ";"..(bodystyle or ""), "")
  618. args.tracking = "no"
  619. local lists = {}
  620. -- 收集所有含有列的列表。
  621. for k, v in pairs(args) do
  622. if type(k) == "string" then
  623. local lraw, craw, lindex, cindex -- list和column的raw和index。
  624. craw, cindex = paramindexes(k, "^col%$") -- 为兼容,先检查是否存在 col+数字 格式的参数。
  625. if cindex ~= nil then
  626. lraw, lindex = "1", 1
  627. else -- 匹配指定列表和列的序号的参数。
  628. lraw, craw, lindex, cindex = paramindexes(k, "^list%col%$")
  629. end
  630. if lindex ~= nil and lindex > 0 and cindex > 0 and __nnew(v) then
  631. local cols
  632. if lists[tostring(lindex)] then
  633. cols = lists[lists[tostring(lindex)]]
  634. else
  635. cols = {}
  636. table.insert(lists, cols) -- 添加新项
  637. lists[tostring(lindex)] = #lists -- 将字符串格式的搜索键映射到对应的新项索引
  638. lists["#"] = math.max(lists["#"] or 0, lindex)
  639. end
  640. table.insert(cols, { { lraw, craw }, { lindex, cindex }, v }) -- 添加新项
  641. cols[tostring(cindex)] = #cols -- 将字符串格式的搜索键映射到对应的新项索引
  642. cols["#"] = math.max(cols["#"] or 0, cindex)
  643. args[k] = nil -- 清除原有的内容以便覆写,防止影响Navbox构造逻辑。
  644. end
  645. end
  646. end
  647. local coltablestyle = __nnew(args.coltablestyle)
  648. local fullwidth = __nnew(args.fullwidth)
  649. local colwidth = __nnew(args.colwidth)
  650. local colheaderstyle = __nnew(args.colheaderstyle)
  651. local padding = __nnew(args.padding)
  652. local colstyle = __nnew(args.colstyle)
  653. local oddcolstyle = __nnew(args.oddcolstyle)
  654. local evencolstyle = __nnew(args.evencolstyle)
  655. for lindex = 1, lists["#"] do
  656. local cols = lists[lists[tostring(lindex)]]
  657. if cols then
  658. local node = mw.html.create("table")
  659. :addClass("navbox-columns-table")
  660. :css("border-spacing", "0px")
  661. :css("text-align", "left")
  662. :cssText(coltablestyle)
  663. --if fullwidth then
  664. node = node:css("width", "100%")
  665. --else
  666. -- node = node
  667. -- :css("width", "auto")
  668. -- :css("margin-left", "auto")
  669. -- :css("margin-right", "auto")
  670. --end
  671. local header = mw.html.create("tr") -- Header row
  672. local main = mw.html.create("tr") -- Main columns
  673. local footer = mw.html.create("tr") -- Footer row
  674. local hasheader = false
  675. local hasfooter = false
  676. local visiblecol = 0 -- 计数可见的列。
  677. for cindex = 1, cols["#"] do
  678. if cols[cols[tostring(cindex)]] then
  679. visiblecol = visiblecol + 1 -- 增加计数。
  680. local raw, index, col_i = unpack(cols[cols[tostring(cindex)]])
  681. local colheader_i = indexedparamvalue(args, "list%col%header", raw, index)
  682. local colfooter_i = indexedparamvalue(args, "list%col%footer", raw, index)
  683. local colstyle_i = indexedparamvalue(args, "list%col%style", raw, index)
  684. local colheadercolspan_i = indexedparamvalue(args, "list%col%headercolspan", raw, index)
  685. local colfootercolspan_i = indexedparamvalue(args, "list%col%footercolspan", raw, index)
  686. local colheaderstyle_i = indexedparamvalue(args, "list%col%headerstyle", raw, index)
  687. local colfooterstyle_i = indexedparamvalue(args, "list%col%footerstyle", raw, index)
  688. local colwidth_i = indexedparamvalue(args, "list%col%width", raw, index)
  689. if lindex == 1 then -- 如果是第一个列表,则需要考虑兼容,检查省略list1的参数名称。
  690. colheader_i = colheader_i or indexedparamvalue(args, "col%header", raw[2], index[2])
  691. colfooter_i = colfooter_i or indexedparamvalue(args, "col%footer", raw[2], index[2])
  692. colstyle_i = colstyle_i or indexedparamvalue(args, "col%style", raw[2], index[2])
  693. colheadercolspan_i = colheadercolspan_i or indexedparamvalue(args, "col%headercolspan", raw[2], index[2])
  694. colfootercolspan_i = colfootercolspan_i or indexedparamvalue(args, "col%footercolspan", raw[2], index[2])
  695. colheaderstyle_i = colheaderstyle_i or indexedparamvalue(args, "col%headerstyle", raw[2], index[2])
  696. colfooterstyle_i = colfooterstyle_i or indexedparamvalue(args, "col%footerstyle", raw[2], index[2])
  697. colwidth_i = colwidth_i or indexedparamvalue(args, "col%width", raw[2], index[2])
  698. end
  699. --- Header row ---
  700. local hcol
  701. if colheader_i then
  702. hasheader = true
  703. hcol = header:tag("td")
  704. :addClass("navbox-abovebelow")
  705. :attr("colspan", colheadercolspan_i or 1)
  706. :css("font-weight", "bold")
  707. :cssText(colheaderstyle)
  708. :cssText(colheaderstyle_i)
  709. :wikitext(colheader_i)
  710. end
  711. --- Main columns ---
  712. main:css("vertical-align", "top")
  713. if visiblecol == 1 and
  714. not (colheader_i or colfooter_i or fullwidth) and
  715. not (padding == "off" or mw.ustring.find(padding, "^;*-?0+%a+;*$"))
  716. then
  717. main:tag("td"):css("width", padding or "5em"):wikitext("&nbsp;&nbsp;&nbsp;")
  718. end
  719. mcol = main:tag("td")
  720. :css("padding", "0px")
  721. :css("width", colwidth_i or colwidth or "10em")
  722. :cssText(colstyle)
  723. :cssText(iif(visiblecol % 2 == 1, oddcolstyle, evencolstyle))
  724. :cssText(colstyle_i)
  725. :tag("div")
  726. :wikitext(col_i)
  727. :done()
  728. --- Footer row ---
  729. local fcol
  730. if colfooter_i then
  731. hasfooter = true
  732. fcol = footer:tag("td")
  733. :addClass("navbox-abovebelow")
  734. :att("colspan", colfootercolspan_i or 1)
  735. :css("font-weight", "bold")
  736. :cssText(colfooterstyle)
  737. :cssText(colfooterstyle_i)
  738. :wikitext(colfooter_i)
  739. end
  740. if visiblecol ~= 1 then
  741. mcol:css("border-left", "2px solid #fdfdfd")
  742. if hcol then
  743. hcol:css("border-left", "2px solid #fdfdfd")
  744. end
  745. if fcol then
  746. fcol:css("border-left", "2px solid #fdfdfd")
  747. end
  748. end
  749. end
  750. end
  751. node:node(header) -- 添加Header row。
  752. if hasheader then
  753. node:tag("tr"):css("height", "2px"):tag("td") -- 添加header下方的分隔线。
  754. end
  755. node:node(main) -- 添加Main columns。
  756. if hasfooter then
  757. node:tag("tr"):css("height", "2px"):tag("td") -- 添加footer上方的分隔线。
  758. end
  759. node:node(footer) -- 添加Footer row。
  760. args["list"..tostring(lindex)] = tostring(node)
  761. args["list"..tostring(lindex).."style"] = "background:transparent;color:inherit;"
  762. args["list"..tostring(lindex).."padding"] = "0px"
  763. end
  764. end
  765. return args
  766. end
  767. local checkNamespaces = function(frame)
  768. local title = mw.title.new(frame:getParent():getTitle())
  769. if not title:inNamespaces(
  770. "Template"
  771. ) and not mw.title.equals(title, mw.title.new("沙盒", "Help")) and not mw.title.equals(title, mw.title.new("Nav", "Module_talk")) then
  772. return "[[Category:在非模板名字空间下的页面中调用Nav模块]]"
  773. end
  774. end
  775. local getArgs = function(frame)
  776. local _params_ = __nnew(frame.args._params_, "overwrite")
  777. if _params_ == "self" then
  778. return frame.args
  779. elseif _params_ == "overwrite" then
  780. local parent = frame:getParent()
  781. local args = {}
  782. if parent ~= nil then
  783. for k, v in pairs(parent.args) do
  784. args[k] = v
  785. end
  786. end
  787. for k, v in pairs(frame.args) do
  788. if k ~= "_params_" then
  789. args[k] = v
  790. end
  791. end
  792. return args
  793. elseif _params_ == "default" then
  794. local parent = frame:getParent()
  795. local args = {}
  796. for k, v in pairs(frame.args) do
  797. if k ~= "_params_" then
  798. args[k] = v
  799. end
  800. end
  801. if parent ~= nil then
  802. for k, v in pairs(parent.args) do
  803. args[k] = v
  804. end
  805. end
  806. return args
  807. else
  808. return nil
  809. end
  810. end
  811. function nav.box(frame)
  812. local result = checkNamespaces(frame) or ""
  813. local args = getArgs(frame)
  814. if args == nil then
  815. return require("Module:Error").error({ message = mw.ustring.format("不能识别的参数获取方式:“%s”", frame.args._params_ or "") })
  816. end
  817. local version = __nne(args[1])
  818. if version ~= nil then version = mw.text.trim(version) end
  819. local border = __nnew(args.border) or version
  820. if border == "subgroup" then
  821. args = _subgroupbox(args, frame)
  822. elseif version == "collapsible groups" then
  823. args = _collapsiblegroupsbox(args, frame)
  824. elseif version == "columns" then
  825. args = _columnsbox(args, frame)
  826. end
  827. return result..tostring(_box(args, frame))
  828. end
  829. function nav.bar(frame)
  830. local result = checkNamespaces(frame) or ""
  831. local args = getArgs(frame)
  832. if args == nil then
  833. return require("Module:Error").error({ message = mw.ustring.format("不能识别的参数获取方式:“%s”", frame.args._params_ or "") })
  834. end
  835. return result..tostring(_bar(args, frame))
  836. end
  837. return nav