模組: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', 'class', 'bodystyle', 'titlestyle', 'abovestyle', 'belowstyle', 'basestyle', 'imagestyle', 'imageleftstyle', 'sectstyle', 'groupstyle', 'liststyle', 'listpadding' }
  607. for _, val in ipairs(list_valid) do
  608. if args[val] then newargs[val] = args[val] end
  609. end
  610. return newargs
  611. end
  612. local function _columnsbox(args, frame)
  613. args[1] = args[2]
  614. args[2] = nil
  615. local bodystyle = __nnew(args.bodystyle)
  616. args.style = (args.style or "")..iif(bodystyle, ";"..(bodystyle or ""), "")
  617. args.tracking = "no"
  618. local lists = {}
  619. -- 收集所有含有列的列表。
  620. for k, v in pairs(args) do
  621. if type(k) == "string" then
  622. local lraw, craw, lindex, cindex -- list和column的raw和index。
  623. craw, cindex = paramindexes(k, "^col%$") -- 為兼容,先檢查是否存在 col+數字 格式的參數。
  624. if cindex ~= nil then
  625. lraw, lindex = "1", 1
  626. else -- 匹配指定列表和列的序號的參數。
  627. lraw, craw, lindex, cindex = paramindexes(k, "^list%col%$")
  628. end
  629. if lindex ~= nil and lindex > 0 and cindex > 0 and __nnew(v) then
  630. local cols
  631. if lists[tostring(lindex)] then
  632. cols = lists[lists[tostring(lindex)]]
  633. else
  634. cols = {}
  635. table.insert(lists, cols) -- 添加新項
  636. lists[tostring(lindex)] = #lists -- 將字符串格式的搜索鍵映射到對應的新項索引
  637. lists["#"] = math.max(lists["#"] or 0, lindex)
  638. end
  639. table.insert(cols, { { lraw, craw }, { lindex, cindex }, v }) -- 添加新項
  640. cols[tostring(cindex)] = #cols -- 將字符串格式的搜索鍵映射到對應的新項索引
  641. cols["#"] = math.max(cols["#"] or 0, cindex)
  642. args[k] = nil -- 清除原有的內容以便覆寫,防止影響Navbox構造邏輯。
  643. end
  644. end
  645. end
  646. local coltablestyle = __nnew(args.coltablestyle)
  647. local fullwidth = __nnew(args.fullwidth)
  648. local colwidth = __nnew(args.colwidth)
  649. local colheaderstyle = __nnew(args.colheaderstyle)
  650. local padding = __nnew(args.padding)
  651. local colstyle = __nnew(args.colstyle)
  652. local oddcolstyle = __nnew(args.oddcolstyle)
  653. local evencolstyle = __nnew(args.evencolstyle)
  654. for lindex = 1, lists["#"] do
  655. local cols = lists[lists[tostring(lindex)]]
  656. if cols then
  657. local node = mw.html.create("table")
  658. :addClass("navbox-columns-table")
  659. :css("border-spacing", "0px")
  660. :css("text-align", "left")
  661. :cssText(coltablestyle)
  662. --if fullwidth then
  663. node = node:css("width", "100%")
  664. --else
  665. -- node = node
  666. -- :css("width", "auto")
  667. -- :css("margin-left", "auto")
  668. -- :css("margin-right", "auto")
  669. --end
  670. local header = mw.html.create("tr") -- Header row
  671. local main = mw.html.create("tr") -- Main columns
  672. local footer = mw.html.create("tr") -- Footer row
  673. local hasheader = false
  674. local hasfooter = false
  675. local visiblecol = 0 -- 計數可見的列。
  676. for cindex = 1, cols["#"] do
  677. if cols[cols[tostring(cindex)]] then
  678. visiblecol = visiblecol + 1 -- 增加計數。
  679. local raw, index, col_i = unpack(cols[cols[tostring(cindex)]])
  680. local colheader_i = indexedparamvalue(args, "list%col%header", raw, index)
  681. local colfooter_i = indexedparamvalue(args, "list%col%footer", raw, index)
  682. local colstyle_i = indexedparamvalue(args, "list%col%style", raw, index)
  683. local colheadercolspan_i = indexedparamvalue(args, "list%col%headercolspan", raw, index)
  684. local colfootercolspan_i = indexedparamvalue(args, "list%col%footercolspan", raw, index)
  685. local colheaderstyle_i = indexedparamvalue(args, "list%col%headerstyle", raw, index)
  686. local colfooterstyle_i = indexedparamvalue(args, "list%col%footerstyle", raw, index)
  687. local colwidth_i = indexedparamvalue(args, "list%col%width", raw, index)
  688. if lindex == 1 then -- 如果是第一個列表,則需要考慮兼容,檢查省略list1的參數名稱。
  689. colheader_i = colheader_i or indexedparamvalue(args, "col%header", raw[2], index[2])
  690. colfooter_i = colfooter_i or indexedparamvalue(args, "col%footer", raw[2], index[2])
  691. colstyle_i = colstyle_i or indexedparamvalue(args, "col%style", raw[2], index[2])
  692. colheadercolspan_i = colheadercolspan_i or indexedparamvalue(args, "col%headercolspan", raw[2], index[2])
  693. colfootercolspan_i = colfootercolspan_i or indexedparamvalue(args, "col%footercolspan", raw[2], index[2])
  694. colheaderstyle_i = colheaderstyle_i or indexedparamvalue(args, "col%headerstyle", raw[2], index[2])
  695. colfooterstyle_i = colfooterstyle_i or indexedparamvalue(args, "col%footerstyle", raw[2], index[2])
  696. colwidth_i = colwidth_i or indexedparamvalue(args, "col%width", raw[2], index[2])
  697. end
  698. --- Header row ---
  699. local hcol
  700. if colheader_i then
  701. hasheader = true
  702. hcol = header:tag("td")
  703. :addClass("navbox-abovebelow")
  704. :attr("colspan", colheadercolspan_i or 1)
  705. :css("font-weight", "bold")
  706. :cssText(colheaderstyle)
  707. :cssText(colheaderstyle_i)
  708. :wikitext(colheader_i)
  709. end
  710. --- Main columns ---
  711. main:css("vertical-align", "top")
  712. if visiblecol == 1 and
  713. not (colheader_i or colfooter_i or fullwidth) and
  714. not (padding == "off" or mw.ustring.find(padding, "^;*-?0+%a+;*$"))
  715. then
  716. main:tag("td"):css("width", padding or "5em"):wikitext("&nbsp;&nbsp;&nbsp;")
  717. end
  718. mcol = main:tag("td")
  719. :css("padding", "0px")
  720. :css("width", colwidth_i or colwidth or "10em")
  721. :cssText(colstyle)
  722. :cssText(iif(visiblecol % 2 == 1, oddcolstyle, evencolstyle))
  723. :cssText(colstyle_i)
  724. :tag("div")
  725. :wikitext(col_i)
  726. :done()
  727. --- Footer row ---
  728. local fcol
  729. if colfooter_i then
  730. hasfooter = true
  731. fcol = footer:tag("td")
  732. :addClass("navbox-abovebelow")
  733. :att("colspan", colfootercolspan_i or 1)
  734. :css("font-weight", "bold")
  735. :cssText(colfooterstyle)
  736. :cssText(colfooterstyle_i)
  737. :wikitext(colfooter_i)
  738. end
  739. if visiblecol ~= 1 then
  740. mcol:css("border-left", "2px solid #fdfdfd")
  741. if hcol then
  742. hcol:css("border-left", "2px solid #fdfdfd")
  743. end
  744. if fcol then
  745. fcol:css("border-left", "2px solid #fdfdfd")
  746. end
  747. end
  748. end
  749. end
  750. node:node(header) -- 添加Header row。
  751. if hasheader then
  752. node:tag("tr"):css("height", "2px"):tag("td") -- 添加header下方的分隔線。
  753. end
  754. node:node(main) -- 添加Main columns。
  755. if hasfooter then
  756. node:tag("tr"):css("height", "2px"):tag("td") -- 添加footer上方的分隔線。
  757. end
  758. node:node(footer) -- 添加Footer row。
  759. args["list"..tostring(lindex)] = tostring(node)
  760. args["list"..tostring(lindex).."style"] = "background:transparent;color:inherit;"
  761. args["list"..tostring(lindex).."padding"] = "0px"
  762. end
  763. end
  764. return args
  765. end
  766. local checkNamespaces = function(frame)
  767. local title = mw.title.new(frame:getParent():getTitle())
  768. if not title:inNamespaces(
  769. "Template"
  770. ) and not mw.title.equals(title, mw.title.new("沙盒", "Help")) and not mw.title.equals(title, mw.title.new("Nav", "Module_talk")) then
  771. return "[[Category:在非模板名字空間下的頁面中調用Nav模塊]]"
  772. end
  773. end
  774. local getArgs = function(frame)
  775. local _params_ = __nnew(frame.args._params_, "overwrite")
  776. if _params_ == "self" then
  777. return frame.args
  778. elseif _params_ == "overwrite" then
  779. local parent = frame:getParent()
  780. local args = {}
  781. if parent ~= nil then
  782. for k, v in pairs(parent.args) do
  783. args[k] = v
  784. end
  785. end
  786. for k, v in pairs(frame.args) do
  787. if k ~= "_params_" then
  788. args[k] = v
  789. end
  790. end
  791. return args
  792. elseif _params_ == "default" then
  793. local parent = frame:getParent()
  794. local args = {}
  795. for k, v in pairs(frame.args) do
  796. if k ~= "_params_" then
  797. args[k] = v
  798. end
  799. end
  800. if parent ~= nil then
  801. for k, v in pairs(parent.args) do
  802. args[k] = v
  803. end
  804. end
  805. return args
  806. else
  807. return nil
  808. end
  809. end
  810. function nav.box(frame)
  811. local result = checkNamespaces(frame) or ""
  812. local args = getArgs(frame)
  813. if args == nil then
  814. return require("Module:Error").error({ message = mw.ustring.format("不能識別的參數獲取方式:“%s”", frame.args._params_ or "") })
  815. end
  816. local version = __nne(args[1])
  817. if version ~= nil then version = mw.text.trim(version) end
  818. local border = __nnew(args.border) or version
  819. if border == "subgroup" then
  820. args = _subgroupbox(args, frame)
  821. elseif version == "collapsible groups" then
  822. args = _collapsiblegroupsbox(args, frame)
  823. elseif version == "columns" then
  824. args = _columnsbox(args, frame)
  825. end
  826. return result..tostring(_box(args, frame))
  827. end
  828. function nav.bar(frame)
  829. local result = checkNamespaces(frame) or ""
  830. local args = getArgs(frame)
  831. if args == nil then
  832. return require("Module:Error").error({ message = mw.ustring.format("不能識別的參數獲取方式:“%s”", frame.args._params_ or "") })
  833. end
  834. return result..tostring(_bar(args, frame))
  835. end
  836. return nav