For faster navigation, this Iframe is preloading the Wikiwand page for 模块:NavboxV2.

模块:NavboxV2

文档图示 模块文档[查看] [编辑] [历史] [清除缓存]

这是((NavboxV2))的Lua实现代码。

简介

合并了((Navbox))相关的一系列模板。融合了((Navbox))的行式、((Navbox subgroup))的子代模块包含、((Navbox with columns))的列式,((Navbox with collapsible groups)))的折叠行式。

改写自模块:Navbox

设计用途

Category:引用模板后大小超过限制的页面中,有相当一部分页面是由于((Navbox))模板超载导致。

  • 根据WP:模板限制中“嵌套展开”的说法,相同页面的多次嵌套调用是会被分次统计的(例如:页面A嵌入页面B,页面B嵌入页面C,页面C相对页面A统计到的展开字节数是被计算了2次)。而现在Navbox的子代块、列式,折叠行式的实现都是基于Navbox行式的模板调用或类似样式结构迭代,这样就符合内部多次调用Navbox的条件,页面很容易会超过模版展开后大小的限制。
  • 其次,实际上Lua的运行限制条件相当宽裕,50MB的内存限制,10秒的运行时限制,很多页面实际使用只在十分之一左右或以下,可以被大量压榨性能。

所以将Navbox所有的实现全部以Lua实现,希望能腾出解释器运行量到Lua运行量,降低解析器触发展开后大小限制的可能。

效果

在对于包含一层子Navbox的情况,展开后大小下降最多有50~60%左右。

对比例子

项目 ((Navbox))系列,参考基准 本模板
Template:哈利波特 Special:固定链接/60036259 User:Cwek/工作室2/Navbox/T:哈利波特
Template:广州 Special:固定链接/60035252 User:Cwek/工作室2/Navbox/T:广州
Template:广州地铁车站 Special:固定链接/59586101 User:Cwek/工作室2/Navbox/T:广州地铁
Template:粤港澳大湾区公共交通 Special:固定链接/59654656 User:Cwek/工作室2/Navbox/T:粤港澳大湾区公共交通
Template:JR东日本的车辆 Special:固定链接/61531803 Special:固定链接/62385808
Template:中华人民共和国广播电视付费频道 Special:固定链接/77500465 Special:固定链接/77504799

参数

与((Navbox))系列模板几乎兼容。但新增部分参数填入:

  • type:Navbox的类型,对应值为vertical(对应((Navbox)))、horizontal(对应((Navbox with columns)))、vertical_collapsible(对应((Navbox with collapsible groups))),默认值为vertical
  • border:Navbox的隐藏参数,用于控制Navbox的边框机制来使子Navbox能被嵌入到父Navbox的值字段(例如listcol等)中,实际对应((Navbox subgroup))的实现机制。对应值为childsubgroup任一个。
在本模板添加子Navbox层时,必须传入这两个参数,这是本模板区分是否存在子Navbox层的依赖。本模板首层Navbox层无需添加border,按需添加type
  • removeGroupPadding:用于区别((Navbox|child))和((Navbox subgroup)),后者在Groupn字段的单元格增加一组padding的配置,适用于子Navbox层。任意值,存在则可,为移除该padding配置(对应((Navbox|child)))。

自((Navbox))系列模板转换

将原有嵌入((Navbox))系列模板的值字段listn(其他类同)改为listn-,并作为相应嵌套子Navbox模板的参数的前缀来加入,使这些模板嵌套转换为扁平化的一层模板参数。

例子
((Navbox))系列 本模板
((Navbox
|name = Navbox/doc
|state = uncollapsed
|image = (({image))}
|imageleft = (({imageleft))}
|title = (({title))}
|above = (({above))}
|group1 = (({group1))}
|list1 = ((Navbox subgroup
 | title = (({list1-title))}
 | above = (({list1-above))}
 | below = (({list1-below))}
 | imageleft = (({list1-imageleft))}
 | image = (({list1-image))}
 | group1 = (({list1-group1))}
 | list1  = (({list1-list1))}
 | group2 = (({list1-group2))}
 | list2  = (({list1-list2))}
))
|group2 = (({group2))}
|list2 = ((Navbox subgroup
| group1 = (({list2-group1))}
| list1  = (({list2-list1))}
| group2 = (({list2-group2))}
| list2  = (({list2-list2))}
))
|below = (({below))}
))
((NavboxV2
|name = Navbox/doc
|state = uncollapsed
|image = (({image))}
|imageleft = (({imageleft))}
|title = (({title))}
|above = (({above))}
<!--  list1 -->
|group1 = (({group1))}
<!-- list1-sub-->
|list1-type =vertical <!--作为list1的子Navbox层,全部相应参数加上对应前缀“list1-”,下同,如此类推 -->
|list1-border=child
|list1-title = (({list1-title))}
|list1-above = (({list1-above))}
|list1-below = (({list1-below))}
|list1-imageleft = (({list1-imageleft))}
|list1-image = (({list1-image))}
|list1-group1 = (({list1-group1))}
|list1-list1  = (({list1-list1))}
|list1-group2 = (({list1-group2))}
|list1-list2  = (({list1-list2))}
<!--  list2 -->
|group2 = (({group2))}
<!-- list2-sub-->
|list2-type =vertical <!--作为list2的子Navbox层,全部相应参数加上对应前缀“list2-”,下同,如此类推 -->
|list2-border=child
|list2-group1 = (({list2-group1))}
|list2-list1  = (({list2-list1))}
|list2-group2 = (({list2-group2))}
|list2-list2  = (({list2-list2))}
<!--end-->
|below = (({below))}
))

转换注意

由于((Navbox))系列的实现较为复杂和涉及自我嵌套,本模板的实现也为此做了对应兼容性调整,可能会出现一些参数被过度透传(可能在样式控制部分,原因是原有设计通过控制参数传入来隔离,而本设计为了使参数扁平化,导致部分这些参数无法隔离)。而且模板参数非常依赖命名规律,在转换替换前,请进行testcase检查,确认转换后能与原来的样式、功能基本一致,才应用转换。如果出现问题,请保留案例并联络本模板维护编辑协助处理,或者放弃

虽然可以在值字段(例如listcol等)重新嵌入((Navbox))系列模板,但这和原有做法一样,失去了本模板降低解析器限制的作用,不建议这样做。

--
-- This module will implement ((Navbox))
--

local p = {}

--Context start
local _NavboxContext={}
local NavboxContextNewObj=function(_prefix,_level,_type)
    return {
         prefix = _prefix
        ,level = _level
        ,type = _type
        ,_Context={}
    }
end

local NavboxContextMetaFunction={
     __index=function(_obj,key)
      local _key = tostring(key)
      if _key=='prefix' or _key =='level' or _key =='type' then
       return _obj[_key]
   else
         return _obj._Context[_key]
        end
    end
    ,__newindex=function(_obj,key,val)
     local _key = tostring(key)
      if _key=='prefix' or _key =='level' or _key =='type' then
       _obj[_key]=val
   else
         _obj._Context[_key]=val
        end
    end
    ,__tostring=function(_obj)
        local output={}
        table.insert(output,'prefix='.._obj.prefix)
        table.insert(output,'level='.._obj.level)
        table.insert(output,'type='.._obj.type)
        for k,v in pairs(_obj._Context) do
            table.insert(output,k..'='..v)
        end
        return 'context:{\n'..table.concat(output,",\n")..'\n}'
    end
}

_NavboxContext.new=function(prefix,level,_type)
    local _prefix = prefix or ""
    local _level = level or 1

    local newobj=NavboxContextNewObj(_prefix,_level,_type or '')
    setmetatable(newobj,NavboxContextMetaFunction)
    --[[function newobj:remove(key)
        mw.log(self)
        NavboxContext_Remove(self,key)
    end]]
    return newobj
end
--Context end

local navbarFunc = require('Module:Navbar')._navbar
local NavboxContext = _NavboxContext
local getArgs -- lazily initialized
local DEBUG=false

--全局性参数锚
local args

--常量定义 (Constant Define)
local Limit={
      vertical=35
     ,horizontal={
         col =20
        ,list=6
      }
     ,vertical_collapsible=20
     ,child=10
}

local NavType={
     V="vertical" --垂直,list
    ,H="horizontal" --水平,col
    ,VC="vertical_collapsible" --折叠垂直
}

local MainTemplateName = 'Template:NavboxV2'

---------------------------------------------------------------
--
--工具箱方法 (Util Function)
--

local function trim(s)
    return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1"))
end

local function addNewline(s)
    if s:match('^[*:;#]') or s:match('^{|') then
        return '\n' .. s ..'\n'
    else
        return s
    end
end

---数组除重
local function removeDump(arr)
    local _t1,_t2={},{}
    for _,val in pairs(arr) do
        _t1[val]=true
    end
    --table.remove(arr)
    for key,_ in pairs(_t1) do
        table.insert(_t2,key)
    end
    return _t2
end

local function tableToString(_table)
    local outputs={}
    if type(_table)=='table' then
        for k,v in pairs(_table) do
            local output
            if type(v)=='table' then
                output=tableToString(v)
            else
                output=tostring(v)
            end
            table.insert(outputs,tostring(k).."="..output)
        end
    end
    return '{'..table.concat(outputs,",")..'}'
end

local debugLog=function(...)
    if DEBUG then
        if #arg==1 then
            mw.log(tostring(arg[1]))
            mw.log("--------------------")
        else
            for k,v in pairs(arg) do
                local pass= false or tostring(k)=='n'
                if not pass then
                    local _v=v
                    if type(v)=='table' then
                        _v=tableToString(v)
                    end
                    mw.log(tostring(_v))
                end
            end
        end
    end
end

---------------------------------------------------------------
--
--功能性方法 (Functional Function)
--

---获得有效的类型
local getValidType=function(input,defVal)
 if NavType.V==input or NavType.H==input or NavType.VC==input then
  return input
 end
 return defVal
end

---检查border判断是不是子Navbox
local borderIsChild=function(border)
 if border == 'subgroup' or border == 'child' then
  return true
 end
 return false
end

---获得参数
local getArg=function( _a , _b,defVal,context,_contextKey)
 local contextKey = _contextKey or _b
 if context and contextKey and context[contextKey] then
  --debugLog('getArg By Context',contextKey)
  return context[contextKey]
 else
  local prefix= _a~=nil and _a or ""
  local key= (_b==nil and _a~=nil) and _a or _b
  local argsKey=prefix .. (prefix=="" and "" or "-") ..key
  --debugLog('getArg By InputArg',argsKey)
  return args[ argsKey ] or defVal
 end
end

---子Navbox参数组判断
local checkHaveChild=function(prefix,valuekey)
    if getValidType(getArg(prefix, valuekey..'-'..'type'),nil) and
       borderIsChild(getArg(prefix, valuekey..'-'..'border')) then
        return true
    end
    return false
end

---获得listnum
local function getListnum(prefix,limit,contentEqList)
    debugLog("getListnum",{['prefix']=prefix,['limit']=limit})
    prefix=prefix:gsub('(-)','%%-')
    local listnums={}
    for k, v in pairs(args) do
        k = ('' .. k)
        --debugLog("getListnum,k=",k)
        local listnum =
              ( k:match('^'..prefix..(prefix=="" and "" or "%-")..'list(%d+)$') or
                k:match('^'..prefix..(prefix=="" and "" or "%-")..'list(%d+)%-')
              )or
              ( contentEqList and ( --VerticalCollapsibleTable 的 Content适配
                 k:match('^'..prefix..(prefix=="" and "" or "%-")..'content(%d+)$') or
                 k:match('^'..prefix..(prefix=="" and "" or "%-")..'content(%d+)%-')
              ) or nil)
        if listnum and tonumber(listnum)<=limit then
            listnum=tonumber(listnum)
            table.insert(listnums,listnum)
            --debugLog("getListnum,listnum=",listnum)
        end
    end
    listnums=removeDump(listnums)
    table.sort(listnums)
    debugLog('list:',listnums)
    debugLog('getListnum End')
    return listnums
end

---参数检查和摇匀
function p.shakeArgs(prefix,level)
    local _
    local list_limit=0

    _ = getArg(prefix,"title")
    _ = getArg(prefix,"above")

    if getArg(prefix,"type")==NavType.H then
       list_limit = Limit.horizontal.list
       for i = 1, Limit.horizontal.col do
           _ = getArg(prefix,"col"..tostring(i).."header")
           if checkHaveChild(prefix,"col"..tostring(i).."header") then
               p.shakeArgs((prefix=="" and "" or "-").."col"..tostring(i).."header",level+1)
           end

           _ = getArg(prefix,"col"..tostring(i))
           if checkHaveChild(prefix,"col"..tostring(i)) then
               p.shakeArgs((prefix=="" and "" or "-").."col"..tostring(i),level+1)
           end

           _ = getArg(prefix,"col"..tostring(i).."footer")
           if checkHaveChild(prefix,"col"..tostring(i).."footer") then
               p.shakeArgs((prefix=="" and "" or "-").."col"..tostring(i).."footer",level+1)
           end
       end
    end

    if getArg(prefix,"type")==NavType.V then
        list_limit = Limit.vertical
    elseif getArg(prefix,"type")==NavType.VC then
        list_limit = Limit.vertical_collapsible
    end
    for i = 1, list_limit do
        _ = getArg(prefix,"group"..tostring(i))
        _ = getArg(prefix,"list"..tostring(i))

        if checkHaveChild(prefix,"list"..tostring(i)) then
            --debugLog('shakeArgs',prefix,"list"..tostring(i))
            p.shakeArgs((prefix=="" and "" or "-").."list"..tostring(i),level+1)
        end
        if checkHaveChild(prefix,"content"..tostring(i)) then
            p.shakeArgs((prefix=="" and "" or "-").."content"..tostring(i),level+1)
        end
    end

    _ = getArg(prefix,"below")
end
---------------------------------------------------------------
--
--  元素渲染方法 (Element Render)
--

---创建表头
local function createNavTableHeader(context)
    debugLog('render TableHeader',context)
    local prefix = context.prefix
    local state ,title ,border =
             getArg(prefix,"state",nil,context)
            ,getArg(prefix,"title")
            ,getArg(prefix,"border",nil,context)
    debugLog('render TableHeader args',{['prefix']=prefix,['state']=state,['title']=title,['border']=border})

    local rootTable=
        mw.html.create('table')
            :attr('cellspacing', 0)
            :addClass('nowraplinks')
            :addClass(getArg(prefix,"bodyclass",nil,context))
            :css('border-spacing', 0)

    if title and (state ~= 'plain' and state ~= 'off') then
        rootTable
            :addClass('collapsible') 
            --:addClass('mw-collapsible') 
            :addClass( state or 'autocollapse')
    end

    if border == 'subgroup' or border == 'child' or border == 'none' then
        rootTable
            :addClass('navbox-subgroup')
            :cssText(getArg(prefix,"bodystyle",nil,context))
            :cssText(getArg(prefix,"style",nil,context))
    else -- regular navobx - bodystyle and style will be applied to the wrapper table
        rootTable
            :addClass('navbox-inner')
            :css('background', 'transparent')
            :css('color', 'inherit')
    end

    rootTable:cssText(getArg(prefix,"innerstyle"))

    debugLog('render TableHeader End')
    return rootTable
end

---分割行 (SplitRow)
local function renderSplitRow(rootTable,context)
    local colspan= context.splitRowcolspan or context.totalColspan
    debugLog('render SplitRow',{
        ['needAddSplitRow']=context.needAddSplitRow,
        ['colspan']=colspan,
    }
    ,context)
    if context.needAddSplitRow then
        rootTable
            :tag('tr')
                :css('height', '2px')
                :tag('td')
                    :attr('colspan',colspan)
    else
    end
    context.splitRowcolspan=colspan
    context.needAddSplitRow=true
end

---创建三键导航
local function renderNavBar(titleCell,context)
    local prefix = context.prefix
    local  navbar , state , border, name , titlestyle =
           getArg(prefix,"navbar",nil,context)
          ,getArg(prefix,"state",nil,context)
          ,getArg(prefix,"border",nil,context)
          ,getArg(prefix,"name")
          ,getArg(prefix,'titlestyle',nil,context)
    debugLog('render Navbar',
        {['prefix']=prefix,
         ['navbar']=navbar,['state']=state,
         ['border']=border,['name']=name,
         ['titlestyle']=titlestyle}
        ,context
    )
    -- Depending on the presence of the navbar and/or show/hide link, we may need to add a spacer div on the left
    -- or right to keep the title centered.
    local spacerSide = nil

    if navbar == 'off' then
        -- No navbar, and client wants no spacer, i.e. wants the title to be shifted to the left. If there's
        -- also no show/hide link, then we need a spacer on the right to achieve the left shift.
        if state == 'plain' then spacerSide = 'right' end
    elseif navbar == 'plain' or
        (   not name and
            mw.getCurrentFrame():getParent():getTitle() == MainTemplateName and
            (border == 'subgroup' or border == 'child' or border == 'none')
        )
        then
        -- No navbar. Need a spacer on the left to balance out the width of the show/hide link.
            if state ~= 'plain' then spacerSide = 'left' end
    else
        -- Will render navbar (or error message). If there's no show/hide link, need a spacer on the right
        -- to balance out the width of the navbar.
        if state == 'plain' then spacerSide = 'right' end

        titleCell:wikitext(navbarFunc{
            name,
            mini = 1,
            fontstyle = table.concat(
            {
                  getArg(prefix,'basestyle',nil,context) or ''
                 ,(titlestyle or '')
                 ,'background:none transparent;border:none;color:inherit'
            }
            ,';')
        })
    end

    -- Render the spacer div.
    if spacerSide then
        titleCell
            :tag('span')
                :css('float', spacerSide)
                :css('width', '8em')
                :css('font-size', '80%')
                :css('margin-' .. (spacerSide == 'left' and 'right' or 'left'), '0.5em')
                :wikitext('&nbsp;')
    end

    debugLog('render Navbar End')
end

---标题行
local function renderTitleRow(rootTable,context)
    local prefix = context.prefix
    local title = getArg(prefix,"title",nil,context)
    if not title then return end
    debugLog('render TitleRow',{['prefix']=prefix,['title']=title},context)
    local basestyle = getArg(prefix,"basestyle",nil,context)
    renderSplitRow(rootTable,context)
    local titleRow=rootTable:tag('tr')

    local titlegroup=getArg(prefix,"titlegroup")
    if titlegroup and (not context.notNeedTitlegroup) then
        titleRow
            :tag('th')
                :attr('scope', 'row')
                :addClass('navbox-group')
                :addClass(getArg(prefix,"titlegroupclass"))
                :cssText(basestyle)
                :cssText(getArg(prefix,"groupstyle",nil,context))
                :cssText(getArg(prefix,"titlegroupstyle"))
                :wikitext(titlegroup)
    end

    local titleCell = titleRow:tag('th'):attr('scope', 'col')
    local titleStyle = getArg(prefix,"titlestyle",nil,context)
    local titleColspan = context.totalColspan
    if titlegroup and (not context.notNeedTitlegroup) then
        titleCell
            :css('border-left', '2px solid #fdfdfd')
            :css('width', '100%')

        titleColspan = titleColspan - 1
    end
    titleCell
        :cssText(basestyle)
        :cssText(titleStyle)
        :addClass('navbox-title')
        :addClass(getArg(prefix,"titleclass",nil,context))
        :attr('colspan', titleColspan)

    renderNavBar(titleCell,context)

    titleCell
         :tag('div')
             :css('font-size', '110%')
             :wikitext(addNewline(title))

    debugLog('render TitleRow End')
end

---上列
local function renderAboveRow(rootTable,context)
    local prefix = context.prefix
    local above = getArg(prefix,"above")
    if not above then return end
    debugLog('render AboveRow',{['prefix']=prefix},context)
    renderSplitRow(rootTable,context)

    local Colspan=context.totalColspan
    local AboveRow=rootTable:tag('tr')
    AboveRow
        :tag('td')
            :addClass('navbox-abovebelow')
            :addClass(getArg(prefix,"aboveclass"))
            :cssText(getArg(prefix,"basestyle"))
            :cssText(getArg(prefix,"abovestyle"))
            :attr('colspan', Colspan)
            :tag('div')
                :wikitext(addNewline(above))

    debugLog('render AboveRow End')
end

---下列
local function renderBelowRow(rootTable,context)
    local prefix = context.prefix
    local below = getArg(prefix,"below")
    if not below then return end
    debugLog('render BelowRow',{['prefix']=prefix},context)
    renderSplitRow(rootTable,context)

    local Colspan=context.totalColspan
    local BelowRow=rootTable:tag('tr')
    BelowRow
        :tag('td')
            :addClass('navbox-abovebelow')
            :addClass(getArg(prefix,"belowclass"))
            :cssText(getArg(prefix,"basestyle"))
            :cssText(getArg(prefix,"belowstyle"))
            :attr('colspan', Colspan)
            :tag('div')
                :wikitext(addNewline(below))

    debugLog('render BelowRow End')
end

---------------------------------------------------------------

--数据列的方法生成器
local function _renderColRow_FunctionBuilder(rootTable,context,nodeFunc)
    debugLog("_renderColRow_FunctionBuilder builded",{['context']=context})
    return function(listCellDivToWrite,divNotClose)
        debugLog(debug.traceback('FunctionInrenderColRow'),{['context']=context,['divNotClose']=divNotClose})
        if not divNotClose then
          listCellDivToWrite:done()--div end
          :node(
              rootTable and nodeFunc(rootTable,context) or
              nodeFunc(context)
          )
          :tag('div'):done()
        else
          listCellDivToWrite:node(nodeFunc(rootTable,context))
        end
    end
end

---数据行,统一的实现
local function _renderListRow(rootTable,context,OtherListFunction)
    renderSplitRow(rootTable,context)

    local prefix, level = context.prefix, context.level
    local listnum = context.listnum or 1
    local isFirst, isOdd = (listnum==1) ,(listnum % 2) == 1
    local ImageRowspan = 2 * context.totalRowspan - 1 + ( context['imageCellCompensate'] or 0)
    local notNeedImage, notNeedGroup =
             context.notNeedImage
            ,context.notNeedGroup
    debugLog('ValueRow Implement',
            {['prefix']=prefix,
            ['listnum']=listnum,
            ['ImageRowspan']=ImageRowspan,
            ['HaveOtherListFunction']=tostring(not not OtherListFunction),
            ['notNeedImage']=notNeedImage,
            ['notNeedGroup']=notNeedGroup},
            context)
    local listRow=rootTable:tag('tr')
    local groupCell,listCell
    local insertImage=false

    --image
    local  imageLeft , image , insertImage =
                 getArg(prefix,"imageleft",nil,context)
                ,getArg(prefix,"image",nil,context)
                ,false
    --CollapsibleListRow 适配
    if context.notImageLeftCell then
        imageLeft = nil
    end
    if context.notImageCell then
        image = nil
    end
    if isFirst and (not notNeedImage) then
        if imageLeft then
            debugLog('imageLeftRow',{['imageLeft']=imageLeft})
            listRow:tag('td')--[[,{['parent']=listRow}]]
             :addClass('navbox-image')
             :addClass(getArg(prefix,"imageclass",nil,context))
             :css('width', '0%')
             :css('padding', '0px 2px 0px 0px')
             :cssText(getArg(prefix,"imageleftstyle",nil,context))
             :attr('rowspan', ImageRowspan)
             :tag('div')
                 :wikitext(addNewline(imageLeft))
             :done() --div done
            :done() --td done
        end
        if image then
          insertImage=true
        end
    end

    --list pre
    local listHaveChild = checkHaveChild(prefix,'list'..listnum)
    local contentHaveChild = context.contentEqList and checkHaveChild(prefix,'content'..listnum)

    --group
    local  group, groupwidth , grouppadding , removeGroupPadding =
             getArg(prefix,"group"..listnum,nil,context)
            ,( ( group and getArg(prefix,"groupwidth") ) or nil )
            ,( getArg(prefix,"grouppadding") or '0em 0.75em;' )
            ,( getArg(prefix,'removeGroupPadding',false) and level > 1 )
    if group and not notNeedGroup then
        debugLog('groupTh',{['group']=group})
        groupCell=listRow:tag('th')
            :attr('scope', 'row')
            :addClass('navbox-group')
            :addClass(getArg(prefix,"groupclass",nil,context))
            :cssText(getArg(prefix,"basestyle"))
            :css((( groupwidth and {['width']=groupwidth}) or {}))
            :cssText(getArg(prefix,"groupstyle"))
            :cssText(getArg(prefix,'group' .. listnum .. 'style'))
        local tempEle=groupCell
        if (borderIsChild(getArg(prefix,"border"))) and (getArg(prefix,'type')==NavType.V) and (not removeGroupPadding) then --针对列式子组合的适配
         groupCell:cssText("padding-left:0em;padding-right:0em;")

         tempEle=tempEle:tag("div"):css("padding",grouppadding)
        end
        tempEle:wikitext(group)

    end

    --list
    do
        listCell=listRow:tag('td')
        if group and not notNeedGroup then
            listCell
                :css('text-align', 'left')
                :css('border-left-width', '2px')
                :css('border-left-style', 'solid')
        else
            listCell:attr('colspan', 2)
        end

        if not groupwidth then
            listCell:css('width', '100%')
        end

        local evenOdd = getArg(prefix,"evenodd")
        evenOdd = (
            evenOdd == 'swap' and
            (isOdd and 'even' or 'odd') or
            (isOdd and (evenOdd or 'odd') or (evenOdd or 'even'))
        )
        local evenOddStyle=(isOdd and getArg(prefix,"evenstyle")) or getArg(prefix,"oddstyle")
        if context.lockEvenOdd then --CollapsibleListRow 适配
            evenOdd='odd'
        end
        if context.noEvenOddStyle then --CollapsibleListRow 适配
         evenOddStyle=''
        end

        local list1padding = (notNeedGroup) and getArg(prefix,"list1padding",nil,context) or
                             '0em 0.25em'
        local listNpadding = (isFirst and list1padding) or
                             ( getArg(prefix,"listpadding",nil,context) or '0em 0.25em' )
        local listNstyle= --((not notNeedGroup))
                        (isFirst and getArg(prefix,'list1style','',context) ) or
                        getArg(prefix,'list'..listnum .. 'style','')
        local liststyle = getArg(prefix,"liststyle",'',context)

        listCell
            :css('padding', '0px')
            :cssText(liststyle)
            :cssText(evenOddStyle)
            :cssText(listNstyle)
            :addClass('navbox-list')
            :addClass('navbox-' .. evenOdd)
            :addClass(getArg(prefix,"listclass"))
        local tempdiv=listCell:tag('div'):css('padding', listNpadding)
        --local listHaveChild = checkHaveChild(prefix,'list'..listnum)
        --local contentHaveChild = context.contentEqList and checkHaveChild(prefix,'content'..listnum)
        if OtherListFunction then
            debugLog('ValueRow OtherListFunction',{['otherListFunctionDivNotClose']=context.otherListFunctionDivNotClose})
            --OtherListFunction(listCell)
            OtherListFunction(tempdiv,context.otherListFunctionDivNotClose)
        elseif (listHaveChild or contentHaveChild) and level<= Limit.child then
            local listKeyName='list'
            if contentHaveChild then
                listKeyName='content'
            end
            local childContext=NavboxContext.new(
                 prefix..(prefix=='' and '' or '-')..listKeyName..listnum ,
                 level+1 ,
                 getValidType(
                     getArg(prefix..(prefix=='' and '' or '-')..
                                   listKeyName..listnum,'type'),
                     NavType.V
                 )
            )
            debugLog('ValueRow NewChild',childContext)
            tempdiv:done()-- div end
                :node(p.renderNavTable(childContext))
                :tag('div'):done()
        else
            local listContent = getArg(prefix,'list'..listnum,'')..
                           ( ((not context.contentEqList ) and '') or getArg(prefix,'content' .. listnum,''))
            debugLog('ValueRow listnum',{['listnum']=listnum})
            tempdiv:wikitext(addNewline(listContent))
        end
    end
    --end

    if insertImage then
      debugLog('imageRow',{['image']=image})
      listRow:tag('td')
          :addClass('navbox-image')
          :addClass(getArg(prefix,"imageclass",nil,context))
          :css('width', '0%')
          :css('padding', '0px 0px 0px 2px')
          :cssText(getArg(prefix,"imagestyle",nil,context))
          :attr('rowspan', ImageRowspan)
          :tag('div')
              :wikitext(addNewline(image))
    end
    debugLog('ValueRow Implement End')
end

---数据行,垂直式的具体实现
local function renderListRow(rootTable,context)
    debugLog('render ListRow',context)
    _renderListRow(rootTable,context)
    debugLog('render ListRow End')
end

---------------------------------------------------------------

---数据列,水平式的具体实现 (ColRow)
local function _renderColRow(rootTable,context)
    local prefix, level = context.prefix, context.level
    local fullwidth  =getArg(prefix,"fullwidth")
    local col1header,col1,col1footer=
           getArg(prefix,'col'..'1'..'header')
          ,getArg(prefix,'col'..'1')
          ,getArg(prefix,'col'..'1'..'footer')
    debugLog('ColRow Implement',{['prefix']=prefix},context)
    --new table root
    rootTable=mw.html.create('table')
    rootTable
        :addClass("navbox-columns-table")
        :attr("cellspacing","0")
        :cssText("text-align:left;")
        :cssText( ( col1header or fullwidth ) and
            "width:100%;" or
            "width:auto;margin-left:auto;margin-right:auto;"
        )
        :cssText(getArg(prefix,"coltablestyle"))

    local headerTR,colbodyTR,footerTR=nil,nil,nil

    context.needAddSplitRow=false
    context.splitRowcolspan=1
    --header
    if col1header then
        renderSplitRow(rootTable,context)
        debugLog('ColRow Header',{})
        headerTR=rootTable:tag('tr')
        for colnum=1,Limit.horizontal.col do
            debugLog('ColRow Header',colnum)
            local isFirst,isOdd = colnum == 1, (colnum % 2) == 1
            local colheaderkey='col'..colnum..'header'
            local colNheader= isFirst and col1header or getArg(prefix,colheaderkey)

            if  headerTR  and colNheader then
                debugLog('ColRow Herder Cell',{['colnum']=colnum})
                local headerNCell=headerTR:tag('td')
                headerNCell
                    :addClass('navbox-abovebelow')
                    :cssText(isFirst and "" or "border-left:2px solid #fdfdfd;")
                    :cssText(getArg(prefix, "colheaderstyle"))
                    :cssText(getArg(prefix, colheaderkey..'style'))
                    :attr(( colnum~=Limit.horizontal.col and {['colspan']=getArg(prefix,colheaderkey..'colspan',1)} ) or {})
--[[                if checkHaveChild(prefix,colheaderkey) and level<= Limit.child then
                    local childContext=NavboxContext.new(colheaderkey ,level+1 ,NavType.H)
                    debugLog('ColRow Herder NewChild',childContext)
                    headerNCell:node(p.renderNavTable(childContext):allDone())
                else]]
                    --debugLog('ColRow Herder Cell',{['colnum']=colnum})
                    --headerNCell
                    :wikitext(addNewline("'''"..colNheader.."'''"))
                --end
            end
        end
        debugLog('ColRow Header End',{['colnum']=colnum})
    end

    --col
    local col1havechild = checkHaveChild(prefix,"col1")
    if col1 or col1havechild then
        renderSplitRow(rootTable,context)
        debugLog('ColRow Body',{
         ['col1havechild']=col1havechild
        })
        colbodyTR=rootTable:tag('tr'):cssText('vertical-align:top;')
        if not(col1header or col1footer or fullwidth) then
            local padding,test0=getArg(prefix, "padding"),nil
            if padding then
                padding = trim(padding)
                test0=padding:find('^0[%%%a]?[%a]?[;]?$')
            end
            if test0~=nil or padding=='off' then else
                colbodyTR
                    :tag('td')
                        :css("width", padding or '5em')
                        :wikitext('&nbsp;&nbsp;&nbsp;')
                    :done()
            end
        end
        for colnum=1,Limit.horizontal.col do
            debugLog('ColRow Body',colnum)
            local isFirst,isOdd = colnum == 1, (colnum % 2) == 1
            local colkey = 'col'..colnum
            local colN = isFirst and col1 or getArg(prefix,colkey)
            local colNhavechild = isFirst and col1havechild or
                                  checkHaveChild(prefix,colkey)
            if  colN or colNhavechild then
                local oddevenstyle = getArg( prefix , isOdd and 'oddcolstyle' or 'evencolstyle')
                local colNCell=colbodyTR
                    :tag('td')
                    :css("padding","0px")
                    :cssText(((not isFirst) and "border-left:2px solid #fdfdfd;" ) or '')
                    :cssText(getArg(prefix,'colstyle'))
                    :cssText(oddevenstyle)
                    :cssText(getArg(prefix, colkey..'style'))
                    :css('width', ( getArg(prefix, colkey..'width') or getArg(prefix,'colwidth') ) or '10em')

                if checkHaveChild(prefix,colkey) and level<= Limit.child then
                    local childContext=NavboxContext.new(prefix..(prefix=='' and '' or '-')..colkey ,level+1 ,getValidType(getArg(prefix,'type'),NavType.H))
                    debugLog('ColRow Body NewChild',childContext)
                    colNCell:tag('div'):done()
                    :node(p.renderNavTable(childContext):allDone())
                    :tag('div'):done()
                else
                    debugLog('ColRow Body Cell',{['colnum']=colnum})
                    colNCell:tag('div'):wikitext(addNewline(colN))
                end
            end
            debugLog('ColRow Body End',{['colnum']=colnum})
        end
    end

    --footer
    if col1footer then
        renderSplitRow(rootTable,context)
        debugLog('ColRow footer',{})
        footerTR=rootTable:tag('tr')
        for colnum=1,Limit.horizontal.col do
            debugLog('ColRow footer',colnum)
            local isFirst,isOdd = colnum == 1, (colnum % 2) == 1
            local colfooterkey='col'..colnum..'footer'
            local colNfooter=isFirst and col1footer or getArg(prefix,colfooterkey)

            if colNfooter then
                debugLog('ColRow footer Cell',{['colnum']=colnum})
                local footerNCell=footerTR:tag('td')
                footerNCell
                    :addClass('navbox-abovebelow')
                    :cssText(isFirst and "" or "border-left:2px solid #fdfdfd;")
                    :cssText(getArg(prefix, "colfooterstyle"))
                    :cssText(getArg(prefix, colfooterkey..'style'))
                    :attr(( colnum~=Limit.horizontal.col and {['colspan']=getArg(prefix,colfooterkey..'colspan',1)}) or {})
--[[                if checkHaveChild(prefix,colfooterkey) and level<= Limit.child then
                    local childContext=NavboxContext.new(colfooterkey ,level+1 ,NavType.H)
                    debugLog('ColRow footer NewChild',childContext)
                    footerNCell:node(p.renderNavTable(childContext):allDone())
                else]]
                    --debugLog('ColRow footer Cell',{['colnum']=colnum})
                    --footerNCell
                    :wikitext(addNewline("'''"..colNfooter.."'''"))
                --end
            end
        end
        debugLog('ColRow footer End',{['colnum']=colnum})
    end

    debugLog('ColRow Implement End')
    return rootTable:allDone()
end

--数据列,具体实现
local function renderColRow(rootTable,context)
    debugLog('renderColRow',{['context']=context})

    context.notNeedGroup = true
    context['list1padding']='0px'
    context['list1style']="background:transparent;color:inherit;"
    context['otherListFunctionDivNotClose']=true
    context['imageCellCompensate']=2

    _renderListRow(
        rootTable,context,
        _renderColRow_FunctionBuilder(rootTable,context,_renderColRow)
    )
    
    --clean up
    context.notNeedGroup=nil
    context['list1padding']=nil
    context['list1style']=nil
    context['otherListFunctionDivNotClose']=nil
    context['imageCellCompensate']=nil
    debugLog('renderColRow End')
end

---------------------------------------------------------------

--折叠行式的子Nabox
local function _renderSmallNavboxInCollapsibleListRow(rootTable,context)
    local prefix, level = context.prefix, context.level
    debugLog('_renderSmallNavboxInCollapsibleListRow',{['prefix']=prefix},{['context']=context})
    local listnum = context.listnum

    --部分需要压制传入的样式
    context.bodyclass = ''
    context.titleclass = ''
    context.groupclass = ''
    context.imageclass = ''
    context.bodystyle = ''
    context.style = ''
    context.basestyle = ''
    context.imagestyle = ''
    context.imageleftstyle = ''

    --传入renderNavBar,renderTitleRow
    context.navbar='plain'
    context.border='child'
    local selected , abbrN , state =
          getArg(prefix,'selected') , getArg(prefix,'abbr'..listnum) , 'uncollapsed'
    if selected ~= nil and selected == abbrN then
      state='uncollapsed'
    else
      state=getArg(prefix,'state'..listnum,'collapsed')
    end
    context.state = state

    --传入renderTitleRow
    --context.titleEqGroup=true
    context.notNeedTitlegroup=true
    context.titlestyle=table.concat(
    {
          (getArg(prefix,'basestyle','')                  )
         ,(getArg(prefix,'groupstyle','')                 )
         ,(getArg(prefix,'secttitlestyle','')             )
         ,(getArg(prefix,'group'..listnum..'style','')    )
         ,(getArg(prefix,'sect'..listnum..'titlestyle',''))
    }
    ,';')
    context.title=(getArg(prefix,'group'..listnum,'')  )..
                  (getArg(prefix,'sect'..listnum,'')   )..
                  (getArg(prefix,'section'..listnum,''))

    --传入renderListRow
    context.contentEqList=true
    context.notNeedGroup=true
    context.liststyle=table.concat(
    {
          (getArg(prefix,'liststyle','')                )
         ,(getArg(prefix,'contentstyle','')             )
         ,(getArg(prefix,'list'..listnum..'style','')   )
         ,(getArg(prefix,'content'..listnum..'style',''))
    }
    ,';')
    local totalColspan=2 --title,above,below
    local totalRowspan=1 --image,imageleft
    --传入image
    local  imageLeft,image=
                 getArg(prefix,"imageleft"..listnum,nil,context,'imageleft')
                ,getArg(prefix,"image"..listnum,nil,context,'image')
    if imageLeft then
     totalColspan = totalColspan + 1
     context.imageleft = imageLeft
    else
     context.notImageLeftCell=true --CollapsibleListRow 适配
    end
    if image then
     totalColspan = totalColspan + 1
     context.image = image
    else
     context.notImageCell=true --CollapsibleListRow 适配
    end

    context.totalColspan = totalColspan
    context.totalRowspan = totalRowspan
    context.splitRowcolspan = totalColspan
    context.lockEvenOdd=true --CollapsibleListRow 适配

    debugLog('SmallNavboxInCollapsibleListRow Implement','listnum='..listnum,context)
    --start
    local rootTable2 = createNavTableHeader(context)
    renderTitleRow(rootTable2,context)
    --only 1 list
    local otherListFunction
    local listHaveChild = checkHaveChild(prefix,'list'..listnum)
    local contentHaveChild = context.contentEqList and checkHaveChild(prefix,'content'..listnum)
    if (listHaveChild or contentHaveChild) and level<= Limit.child then
        local listKeyName='list'
        if contentHaveChild then
            listKeyName='content'
        end
        local childContext=NavboxContext.new(
                               prefix..(prefix=='' and '' or '-')..listKeyName..listnum ,
                               level+1 ,
                               getValidType(getArg(prefix..(prefix=='' and '' or '-')..listKeyName..listnum,'type'),NavType.V))
        debugLog('SmallNavboxInCollapsibleListRow NewChild',childContext)
        otherListFunction=_renderColRow_FunctionBuilder(nil,childContext,p.renderNavTable)
    end
    context.noEvenOddStyle=true
    _renderListRow(rootTable2,context,otherListFunction)
    context.noEvenOddStyle=nil
    debugLog('_renderSmallNavboxInCollapsibleListRow End')
    return rootTable2:allDone()
end

---折叠行具体实现
local function renderCollapsibleListRow(rootTable,context)
   local prefix, level = context.prefix, context.level
   debugLog('renderCollapsibleListRow',{['prefix']=prefix},{['context']=context})
   context.notNeedGroup = true
   local listnum = context.listnum
   local context_function
   if getArg(prefix,'group'..listnum)   or
      getArg(prefix,'sect'..listnum)    or
      getArg(prefix,'section'..listnum) then
        local grandChild_context=NavboxContext.new(prefix,level)
        grandChild_context.notNeedGroup=true
        grandChild_context.listpadding=getArg(prefix,'listpadding')
        grandChild_context.listnum=listnum
        context_function=_renderColRow_FunctionBuilder(
            rootTable,grandChild_context,
            _renderSmallNavboxInCollapsibleListRow
        )
        debugLog('renderCollapsibleListRow function generate',{['context']=context,['grandChild_context']=grandChild_context})
    end
    context.noEvenOddStyle=true
    debugLog('renderCollapsibleListRow renderListRow',{['context']=context})
    _renderListRow(rootTable,context,context_function)
    context.noEvenOddStyle=nil
    debugLog('renderCollapsibleListRow End')
end

---------------------------------------------------------------
--
--   Tracking categories
--
local function needsHorizontalLists(context)
    local prefix = context.prefix
    local border ,listclass ,bodyclass =
             context.border    or getArg(prefix,'border')
            ,context.listclass or getArg(prefix,'listclass')
            ,context.bodyclass or getArg(prefix,'bodyclass')

    if borderIsChild(border)
        or getArg(prefix,'tracking') == 'no' then
        return false
    end

    local listClasses = {'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap', 'hlist vcard', 'vcard hlist', 'hlist vevent', 'hlist hlist-pipe'}
    for i, cls in ipairs(listClasses) do
        if listclass == cls or bodyclass == cls then
            return false
        end
    end

    return true
end

local function hasBackgroundColors(context)
    local prefix = context.prefix
    local titlestyle ,groupstyle ,basestyle =
             context.titlestyle or getArg(prefix,'titlestyle')
            ,context.groupstyle or getArg(prefix,'groupstyle')
            ,context.basestyle  or getArg(prefix,'basestyle')
    return mw.ustring.match(titlestyle or '','background') or
           mw.ustring.match(groupstyle or '','background') or
           mw.ustring.match(basestyle  or '','background')
end

local function argNameAndRealTitleAreDifferent(context)
    local prefix = context.prefix
    local border ,name =
             getArg(prefix,'border',nil,context)
            ,getArg(prefix,'name',nil,context)
    if borderIsChild(border)
        or getArg(prefix,'tracking') == 'no' then
        return false
    end

    if name ~= mw.title.getCurrentTitle().text then
        return true
    end
    return false
end

local function getTrackingCategories(context)
    local cats = {}
    if needsHorizontalLists(context) then table.insert(cats, '没有使用水平列表的导航框') end
    if hasBackgroundColors(context) then table.insert(cats, '使用背景颜色的导航框') end
    if argNameAndRealTitleAreDifferent(context) then table.insert(cats, 'name參數和實際不同的導航框') end
    return cats
end

local function renderTrackingCategories(builder,context)
    local title = mw.title.getCurrentTitle()
    if title.namespace ~= 10 then return end -- not in template space
    local subpage = title.subpageText
    if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end

    for i, cat in ipairs(getTrackingCategories(context)) do
        builder:wikitext('[[Category:' .. cat .. ']]')
    end
end

---------------------------------------------------------------
--
--  SubType Implement
--

---水平式
local function renderHorizontalTable(context)
    debugLog('render Horizontal NavTable',context)
    local prefix, level = context.prefix, context.level

    local rootTable = createNavTableHeader(context)

    local listnums=getListnum(prefix,Limit.horizontal.list)

    local totalColspan=2 --title,above,below
    local totalRowspan=#listnums --image,imageleft
    if getArg(prefix,"imageleft",nil,context) then totalColspan =totalColspan + 1 end
    if getArg(prefix,"image",nil,context) then totalColspan =totalColspan + 1 end
    context.totalColspan = totalColspan
    context.totalRowspan = totalRowspan
    --context.splitRowcolspan = totalColspan

    renderTitleRow(rootTable,context)
    renderAboveRow(rootTable,context)

    if listnums==nil or #listnums==0 then --没有list的话,只有col
     debugLog('render Horizontal NavTable,no list',{listnums})
     context.listnum=1
     renderColRow(rootTable,context)
     --context.notNeedImage=true
     context.splitRowcolspan = totalColspan
    else
     debugLog('render Horizontal NavTable,have list with col',{listnums})
     for i,listnum in ipairs(listnums) do
         context.listnum=listnum
         if listnum==1 then
           --一行Col
           renderColRow(rootTable,context)
           context.notNeedImage=true
           context.splitRowcolspan = totalColspan
         else
           context.notNeedImage=nil
         end
         _renderListRow(rootTable,context)
     end
    end
    renderBelowRow(rootTable,context)

    renderTrackingCategories(rootTable,context)
    debugLog('render Horizontal NavTable End')
    return rootTable
end

---垂直式
local function renderVerticalTable(context)
    debugLog('render Vertical NavTable',context)
    local prefix, level = context.prefix, context.level

    local rootTable = createNavTableHeader(context)

    local listnums=getListnum(prefix,Limit.vertical)

    local totalColspan=2 --title,above,below
    local totalRowspan=#listnums --image,imageleft
    if getArg(prefix,"imageleft",nil,context) then totalColspan =totalColspan + 1 end
    if getArg(prefix,"image",nil,context) then totalColspan =totalColspan + 1 end
    context.totalColspan = totalColspan
    context.totalRowspan = totalRowspan
    --context.splitRowcolspan = totalColspan

    renderTitleRow(rootTable,context)
    renderAboveRow(rootTable,context)
    for i,listnum in ipairs(listnums) do
        context.listnum=listnum
        renderListRow(rootTable,context)
    end
    renderBelowRow(rootTable,context)

    renderTrackingCategories(rootTable,context)
    debugLog('render Vertical NavTable End')
    return rootTable
end

---垂直折叠式
local function renderVerticalCollapsibleTable(context)
    debugLog('render VerticalCollapsible NavTable',context)
    local prefix, level = context.prefix, context.level

    local rootTable = createNavTableHeader(context)

    local listnums=getListnum(prefix,Limit.vertical
      ,(--[[context.contentEqList or ]]true) --VerticalCollapsibleTable 的 Content适配
    )

    local totalColspan=2 --title,above,below
    local totalRowspan=#listnums --image,imageleft
    if getArg(prefix,"imageleft",nil,context) then totalColspan =totalColspan + 1 end
    if getArg(prefix,"image",nil,context) then totalColspan =totalColspan + 1 end
    context.totalColspan = totalColspan
    context.totalRowspan = totalRowspan
    --context.splitRowcolspan = totalColspan

    renderTitleRow(rootTable,context)
    renderAboveRow(rootTable,context)
    for i,listnum in ipairs(listnums) do
        context.listnum=listnum
        renderCollapsibleListRow(rootTable,context)
    end
    renderBelowRow(rootTable,context)

    renderTrackingCategories(rootTable,context)
    debugLog('render VerticalCollapsible NavTable End')
    return rootTable
end

---Type Selector
function p.renderNavTable(context)
    local navtype  = context.type
    debugLog('render NavTable')
    debugLog('Type='..navtype)
    local result
    if navtype==NavType.H then
        result=renderHorizontalTable(context)
    elseif navtype==NavType.VC then
        result=renderVerticalCollapsibleTable(context)
    else
        result=renderVerticalTable(context)
    end
    debugLog('render NavTable End')
    return result
end

--Main Funtion
function p._navbox(context)
    debugLog('Navbox mainfuntion',context)
    local prefix, level = context.prefix, context.level
    local rootTable = mw.html.create('table')
    rootTable
        :attr('cellspacing', 0)
        :addClass('navbox')
        :css('border-spacing', 0)
        :cssText(getArg(prefix,'bodystyle'))
        :cssText(getArg(prefix,'style'))
        :tag('tr')
            :tag('td')
                :css('padding', '2px')
                :node(p.renderNavTable(context):allDone())
    debugLog('Navbox mainfuntion End')
    return rootTable
end

function p.navbox(frame)
    if not getArgs then
        getArgs = require('Module:Arguments').getArgs
    end
    local modelArgs=getArgs(frame,{frameOnly=true})
    DEBUG = modelArgs['DEBUG'] or DEBUG
    MainTemplateName=modelArgs['MainTemplateName'] or MainTemplateName
    debugLog('Navbox start')

    args = getArgs(frame, {wrappers = MainTemplateName, trim = true})
    debugLog('getArgs done,',args)

    local prefix, level = "", 1
    local NavType = getValidType(getArg(prefix,'type') or  modelArgs['type'],NavType.V)
    DEBUG = modelArgs['DEBUG'] or DEBUG
    -- Read the arguments in the order they'll be output in, to make references number in the right order.
    p.shakeArgs(prefix,level,NavType)

    local L0Context=NavboxContext.new(prefix,level,NavType)
    local rootNode=p._navbox(L0Context)
    debugLog('rootnode build done, Navbox end')
    return tostring(rootNode:allDone())
end

return p
{{bottomLinkPreText}} {{bottomLinkText}}
模块:NavboxV2
Listen to this article

This browser is not supported by Wikiwand :(
Wikiwand requires a browser with modern capabilities in order to provide you with the best reading experience.
Please download and use one of the following browsers:

This article was just edited, click to reload
This article has been deleted on Wikipedia (Why?)

Back to homepage

Please click Add in the dialog above
Please click Allow in the top-left corner,
then click Install Now in the dialog
Please click Open in the download dialog,
then click Install
Please click the "Downloads" icon in the Safari toolbar, open the first download in the list,
then click Install
{{::$root.activation.text}}

Install Wikiwand

Install on Chrome Install on Firefox
Don't forget to rate us

Tell your friends about Wikiwand!

Gmail Facebook Twitter Link

Enjoying Wikiwand?

Tell your friends and spread the love:
Share on Gmail Share on Facebook Share on Twitter Share on Buffer

Our magic isn't perfect

You can help our automatic cover photo selection by reporting an unsuitable photo.

This photo is visually disturbing This photo is not a good choice

Thank you for helping!


Your input will affect cover photo selection, along with input from other users.

X

Get ready for Wikiwand 2.0 🎉! the new version arrives on September 1st! Don't want to wait?