Module:Navigation

From CraftWare Pro User Manual
Jump to navigation Jump to search

Documentation for this module may be created at Module:Navigation/doc

local function trim(s)
    return s:match "^%s*(.-)%s*$"
end

-- Supported line format:   '  ## Page Title    '
--         or:    '  ### [[Page Link ]]  extra text '
function parse_line2(line)
	return line:match '^%s*(#+)%s*%[*%s*([^%]]*[^%s%]]+)%s*%]*%s*.*$'
end

local function read_tree_lines(lines)
    local res = {lines={}, cur=0}
    for nr=1,#lines do
        repeat
        	-- mw.log('line ' .. nr .. ': <' .. lines[nr] ..'>')
        	level, title = parse_line2(lines[nr])
        	if level == nil then
        		break
        	else
        		level = #level
        	end
            res.lines[#res.lines + 1] = {level=level, title=title}
        until true
    end
    return res
end

local function next(nodes)
    if #nodes.lines > nodes.cur then
        nodes.cur = nodes.cur + 1
        local pair = nodes.lines[nodes.cur]
        return pair.level, pair.title
    else
        return -1, ''
    end
end

local function read_tree(tree, level, title, nodes)
    local cur_level = level
    while cur_level >= 0 and cur_level >= level do
        repeat
            if cur_level == level then
                tree.children[#tree.children + 1] = {content=title, children={}}
            else
                cur_level, title = read_tree(tree.children[#tree.children], level+1, title, nodes)
                break
            end
            cur_level, title = next(nodes)
        until true
    end
    return cur_level, title 
end

local function visit_tree(tree, visitor)
	if visitor.enter then
		visitor:enter(tree)
	end
    for i=1, #tree.children do
    	repeat
	        local child = tree.children[i]
	       	if visitor.filter and not visitor:filter(child) then
				break
			end
	        visitor:visit(child)
	        if #child['children'] > 0 then
	            visit_tree(child, visitor)
	        end
	    until true
    end
    if visitor.leave then
	    visitor:leave(tree)
	end
end

local Renderer = { parents={}, titles = {} }

function Renderer:enter(tree)
	self.parents[#self.parents + 1] = self.parents[#self.parents]:tag 'ol'
	self.titles[#self.titles + 1] = tree.content
end

function Renderer:visit(tree)
	local li = self.parents[#self.parents]:tag 'li'
    li:wikitext('[['..tree.content..']]')
end

function Renderer:leave(tree)
	self.parents[#self.parents] = nil
	self.titles[#self.titles] = nil
end

local CurrentFinder = { titles={}, result = nil }

function CurrentFinder:enter(tree)
	if tree.content then
		self.titles[#self.titles + 1] = tree.content
	end
end

function CurrentFinder:visit(tree)
	if tree.content == self.target then
		self.result = {}
		for i = 1,#self.titles do
			self.result[#self.result + 1] = self.titles[i]
		end
		self.result[#self.result + 1] = tree.content
	end
end

function CurrentFinder:leave(tree)
	self.titles[#self.titles] = nil
end

local function find_current_node(tree, title)
	CurrentFinder.parents = {}
	CurrentFinder.target = title
	CurrentFinder.result = nil
	visit_tree(tree, CurrentFinder)
end

local function render_tree(tree, current_node)
	local div = mw.html.create('div')
	div:wikitext('Navigation')
	div:cssText('border: 1px solid blue; background-color: #f0f0f0')
	Renderer.parents = {div}
	Renderer.filter = function (self, tree)
		--[[
		mw.log('======\nrender filter: ')
		mw.log("self titles:" ..mw.dumpObject(self.titles))
		mw.log("current titles: " ..mw.dumpObject(current_node))
		mw.log("tree: " ..mw.dumpObject(tree))
		]]
		if #self.titles > #current_node then
			return false
		end
		for i=1,#self.titles do
			if self.titles[i] ~= current_node[i] then
				return false
			end
		end
		return true
	end
	visit_tree(tree, Renderer)
	return tostring(div)
end

local function links_from_nodes(nodes, current_title)
    local level, title = next(nodes)
    local tree = {content='', children={}}
    read_tree(tree, level, title, nodes)
    
    find_current_node(tree, current_title)
    local current_node = CurrentFinder.result
    if current_node == nil then
    	current_node = {current_title}
    	mw.log(current_title .. ' not found in page')
    end
    return render_tree(tree, current_node)
end

-- Test
--[[
local tree = {content='', children={
 {content='a1', children={
     {content='a1-b1', children={}},
     {content='a1-b2', children={
         {content='a1-b2-c1', children= {
             {content='a2-b2-c1-d1', children={}}
         }}
     }}
 }},
 {content='a2', children= {
     {content='a2-b2', children={}},
     {content='a2-c2', children={}}
 }}
}}
]]--

local p = {}

function p.hello(frame)
	mw.log(mw.dumpObject(frame))
    return "Hello, world!"
end

function p.test(x)
	return x+1
end

local function parse_contents(contents_page)
	local frame = mw.getCurrentFrame()
    local title = mw.title.new(contents_page)
    mw.log('fetch contents of title: ' .. title.text)
    local content = title:getContent()
    
    if content == nil or #content == 0 then
		return ''
    end

    local function get_lines(text)
        if text:sub(-1)~="\n" then
            text = text .. "\n"
        end
        return text:gmatch("(.-)\n")
    end

    local lines = {}
    --[[
    for line in get_lines(content) do
        lines[#lines+1] = frame:preprocess(line)
    end
    ]]
    for line in get_lines(frame:preprocess(content)) do
        lines[#lines+1] = line
    end
    
    return lines
end

local function get_params(frame)
    local current_title = frame.args['selfpage'] or frame:preprocess '{{PAGENAME}}'
    mw.log('Current title: ' .. current_title)
    
    -- mw.logObject(frame)

   	local contents_page = frame.args[1] or nil
   	if not contents_page then
   		parent = frame:getParent()
   		contents_page = mw.title.new(parent:getTitle()).rootPageTitle.text .. "/Contents"
   	end
	mw.log('Contents page name: ' .. (contents_page or '<name missing>'))
	
	return current_title, contents_page
end

	
-- prev and next links
function p.create_prevnext(frame)
    local current_title, contents_page = get_params(frame)
	local lines = parse_contents(contents_page)
    local nodes = read_tree_lines(lines)
    local level, title = next(nodes)
    local tree = {content='', children={}}
    read_tree(tree, level, title, nodes)
    
    find_current_node(tree, current_title)
    local current_node = CurrentFinder.result

	local tree_lines = nodes.lines

	title_idx = nil
	mw.log("# tree lines: " .. #tree_lines)
	for i = 1, #tree_lines do
		title = tree_lines[i].title
		if title == current_title then
			title_idx = i
		end
	end
	mw.log("Title index: " .. (title_idx or "<title not found>"))
	
	if title_idx == nil then
		return frame:preprocess('{{Note|Page title ' .. current_title .. ' missing from ' .. name .. '|error}}')
	end
	
	local res = '{| class="navcontrol" style="margin-bottom:1em; padding:0.1em; 100%; width: 100%; text-align: left; background-color: #f0f0f0;"\n'
	
	if title_idx > 1 then
		res = res .. "|style='text-align:left'|'''&#9664;''' [[" .. tree_lines[title_idx-1].title .. "]]\n"
	end

	res = res .. "|style='text-align:center'| [[" .. current_node[2] .. ']]'
	for i = 3, #current_node do
		res = res .. ' > [[' .. current_node[i] .. ']]'
	end
	res = res .. '\n'
	
	if title_idx < #tree_lines then
		res = res .. "|style='text-align:right'| [[" .. tree_lines[title_idx+1].title .. "]] '''&#9654;'''\n"
	end

--	if #CurrentFinder.result > 1 then
--		local parent = CurrentFinder.result[#CurrentFinder.result-1]
--		if #parent > 0 then
--			res = res .."||'''&uarr; Up''' [[" .. parent .."]] "
--		end
--	end
	
	res = res .. "\n|}\n"
	
	-- return log .. res
	return res
end

function p.create_sidebar_tree(frame)
    local current_title, contents_page = get_params(frame)

	local lines = parse_contents(contents_page)
	--mw.logObject(lines)
    local nodes = read_tree_lines(lines).lines
	--mw.logObject(nodes)
    
    local res = '{{#tree:\n'
	for i=1,#nodes do
		--mw.logObject(nodes[i])
		for i = 1, nodes[i].level do
			res = res .. '*'
		end
		res = res .. nodes[i].title .. '\n'
	end
	res = res .. '}}'
	--mw.log(res)
	return frame:preprocess(res)
	--return res
end

function p.create_toc(frame)
    local current_title, contents_page = get_params(frame)
    mw.log(current_title)
	local lines = parse_contents(contents_page)
    local nodes = read_tree_lines(lines)
    return links_from_nodes(nodes, current_title)
end

--[[ How to test in Debug Console (no page saves required)
f = mw.getCurrentFrame():newChild{title="Debug", args={'Test toc', selfpage='Page3'}}
=p.create_links(f)

f = mw.getCurrentFrame():newChild{title="Manual/GettingStarted/Introduction", args={"Manual/Contents", selfpage="Manual/GettingStarted/Introduction"}}
]]

p.create_links = p.create_prevnext

return p