Module:Sandbox/Was a bee/tree

local p = {}

datas = {}

-- These setdata() and getdata() functions are used to access (read and write) to "datas" table

function p.setdata(n, key, val)

if datas[n] == nil then

datas[n] = {}

end

datas[n][key] = val

end

function p.getdata(n, key, defval)

if datas[n] == nil or datas[n][key] == nil then

return defval

else

return datas[n][key]

end

end

function p.children(query, itemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )

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

-- children function which listing all childrens that certain item has.

-- Not listing grand childrens, or grand-grand childrens. Listing only childrens.

-- Final returning value "childrens" is like {Q123123, Q3984198237, Q1874138746}

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

local childrens = {}

local entity = mw.wikibase.getEntity( itemId )

local claims = nil

local x = 1

local tmpValue = {}

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

-- Start collecting data from the retrieved entity, and save that into the datas[] table.

-- Because getEntity() function is EXPENSIVE, so here, collecting all data at once.

-- https://en.wikipedia.org/wiki/WP:EXPENSIVE

-- https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua#mw.wikibase.getEntity

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

local globalSiteId_1 = lang1 .. 'wiki'

local globalSiteId_2

if lang2 and lang2 ~= '' then

globalSiteId_2 = lang2 .. 'wiki'

else

globalSiteId_2 = nil

end

p.setdata(itemId, "sitelink_1", entity:getSitelink( globalSiteId_1 ))

p.setdata(itemId, "label_1", entity:getLabel( lang1 ))

if lang2 and lang2 ~= '' then

p.setdata(itemId, "sitelink_2", entity:getSitelink( globalSiteId_2 ))

p.setdata(itemId, "label_2", entity:getLabel( lang2 ))

end

-- ★★★★ Start source specific code ★★★★ --

------------TA98------------

claims = entity['claims']['P1323'] --TA98 ID

if claims then

for _,claim in pairs( claims ) do

tmpValue[x] = claim.mainsnak.datavalue.value

x = x + 1

end

p.setdata(itemId, "TA98", tmpValue)

end

tmpValue = {} --initialize

x = 1 --initialize

claims = nil --initialize

------------MeSH------------

--claims = entity['claims']['P672'] --MeSH Code

--if claims then

-- for _,claim in pairs( claims ) do

-- tmpValue[x] = claim.mainsnak.datavalue.value

-- x = x + 1

-- end

-- p.setdata(itemId, "MeSH", tmpValue)

--end

--

--tmpValue = {} --initialize

--x = 1 --initialize

--claims = nil --initialize

-- ★★★★ End source specific code ★★★★ --

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

-- End collecting data

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

--mw.logObject(datas[itemId])

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

-- Start collecting childrens

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

claims = entity['claims'][property]

--local claims = wikidata.getClaims(query)

if not claims then

return {}

end

local has_source

if sources then -- If input setting about "sources" exists

has_source = false -- default

else

has_source = true -- when setting doesn't requires sources, assuming as if all cliams are sourced

end

for _,claim in pairs( claims ) do

if sources then -- If input setting about "sources" exists

has_source = false -- reset

-- ★★★★ Start source specific code ★★★★ --

if claim['references'] and claim['references'][1]['snaks']['P248'] then -- If P248 ("stated in") claim exists

if claim['references'][1]['snaks']['P248'][1]['datavalue']['value']['id'] == 'Q286567' then -- If it is TA98 ('Q286567')

has_source = true --If the claim is sourced by Terminologia Anatomica, then "has_source" is true

p.setdata('Q' .. claim.mainsnak.datavalue.value['numeric-id'], 'sourcedBy', 'TA98')-- set datas table, like datas[Q1234]['sourceType'] = 'TA98'

end

end

-- ★★★★ End source specific code ★★★★ --

end

--Adding item only when the claim is sourced by specific literature (e.g. Terminologia Anatomica)

--or adding all item if input setting doesn't need source

if has_source then

local value = claim.mainsnak.datavalue.value

local nextitem = 'Q' .. value['numeric-id']

childrens[x] = nextitem

x = x + 1

end

end

--mw.logObject(datas[itemId])

--mw.log("childrens are : ")

--mw.logObject(childrens)

return childrens

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

-- End collecting childrens

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

end

-- generator, see http://lua-users.org/wiki/LuaCoroutinesVersusPythonGenerators for lua doc

-- definition a function that when called several times will generate a sequence of strings

-- gensymbols = create_gensymbols("ABC")

-- gensymbols() == "A"

-- gensymbols() == "B" g

-- gensymbols() == "C"

-- gensymbols() == "AA" '

-- gensymbols() == "BABBA"

-- ...

function p.gensymbols(chars)

local i = 0

local charset = chars

local generator = function ()

local symbol = ""

local rest

rest = i

repeat

local j

j = rest % string.len(charset)

rest = math.floor(rest / string.len(charset))

symbol = symbol .. string.sub(charset, j+1, j+1)

until rest <= 0

i = i + 1

return symbol

end

return generator

end

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

-- Start definition of itemOutput function which returns text like '' .. label .. ''

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

function p.itemOutput(item, lang1, lang2, datas, setdata, getdata, refStyle, pencil)

local globalSiteId_1 = lang1 .. 'wiki'

local globalSiteId_2 = lang2 .. 'wiki'

--local lang_2 = 'en'

--local langWiki = '//'..lang..'.wikipedia.org' -- if lang is 'fr', then this is '//fr.wikipedia.org' used to compare with mw.site.server

--local currentEntity = mw.wikibase.getEntityObject( item )

local currentLabel

local currentLabel_temp

local content

local refTable = {}

local referenceText = ''

local pencilText

if pencil == 'none' then

pencilText = ''

else --(P527 "has part" or P361 "is part of")

pencilText = 'File:Blue pencil.svg'

end

refStyle = 'asdasdsa'

--mw.log('refStyle is: ' .. refStyle)

if refStyle and refStyle ~= 'none' then

-- ★★★★ Start source specific code ★★★★ --

if p.getdata(item, "sourcedBy", nil ) and p.getdata(item, "sourcedBy", nil ) == 'TA98' then

--mw.log('datas[] is: ')

--mw.logObject(datas)

mw.log('
')

mw.log('
')

mw.log('item is: ')

mw.logObject(item)

mw.log('datas[item] is: ')

mw.logObject(datas[item])

mw.log('datas[item]["TA98"] is: ')

mw.logObject(datas[item]["TA98"])

refTable = p.getdata(item, "TA98", nil )

mw.log('refTable is: ')

mw.logObject(refTable)

mw.log('
')

mw.log('
')

if refTable and refTable ~= '' then

for i, TA98ID in pairs( refTable ) do

refTable[i] = string.sub(refTable[i], 2) --Remove first character "A" from ID

refTable[i] = '[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/' .. refTable[i] .. '%20Entity%20TA98%20EN.htm' .. ' ' .. 'A' .. refTable[i] ..']' -- Creating wikitext which links to TA98 official site

--mw.log('refTable is :' .. tostring(table.concat(refTable)))

end

-- Converting multipul IDs into one line string, separated by comma, space ", "

referenceText = tostring( table.concat( refTable, ', ' ) )

referenceText = referenceText .. ' TA98, 1998'

if refStyle == 'normal' then

referenceText = tostring( mw.getCurrentFrame():extensionTag( 'ref', referenceText, {} ) )

else

referenceText = mw.getCurrentFrame():expandTemplate{ title = 'efn', args = {referenceText} }

end

else

referenceText = ''

end

else

referenceText = ''

end

-- ★★★★ End source specific code ★★★★ --

end

--mw.log('referenceText is :' .. referenceText)

--Link creation priority. Followings is an example.

--'ja'(Japanese) represents your own language, and 'en'(English) represents other language which is familiar with your readers.

--This section would be better to customize for your own readers.

--1. Sitelink(ja) --If local sitelink data exists, use that.

--2. Label(ja)(en link) --If local sitelink data doesn't exist, but local label data exists, use that (with en link).

--3. Sitelink(en) (en link) --If both local language data doesn't exist, use sitelink to English Wikipedia

--4. Label(en)(en link) --If such data doesn't exist, use English label

--5. Q123456 --If all of such kind data doesn't exist, use bare item ID

currentLabel = p.getdata(item, "sitelink_1", nil )

-- wd._references( {item, 'P361'} )

if currentLabel ~= nil then

content = '' .. currentLabel .. ''

content = content .. ' ' .. referenceText

content = content .. ' ' .. pencilText

return content ----1. Sitelink(ja) style

else

currentLabel = p.getdata(item, "label_1", nil )

if currentLabel ~= nil then

content = '' .. currentLabel .. ''

currentLabel_temp = p.getdata(item, "sitelink_2", nil )

if currentLabel_temp ~= nil then

content = content .. ' (' .. lang2 .. ')' ----adding (en) link

end

content = content .. ' ' .. referenceText

content = content .. ' ' .. pencilText

return content ----2. Label(ja)(en link) style

else

currentLabel = p.getdata(item, "sitelink_2", nil )

if currentLabel ~= nil then

content = currentLabel

content = content .. ' (' .. lang2 .. ')' ----adding (en) link

content = content .. ' ' .. referenceText

content = content .. ' ' .. pencilText

return content ----3. Sitelink(en) (en link) style

else

currentLabel = p.getdata(item, "label_2", nil )

if currentLabel ~= nil then

content = currentLabel

content = content .. ' (' .. lang2 .. ')' ----adding (en) link

content = content .. ' ' .. referenceText

content = content .. ' ' .. pencilText

return content ----4. Label(en)(en link) style

else

content = item

content = content .. ' ' .. referenceText

content = content .. ' ' .. pencilText

return content --5. Q123456 style

end

end

end

end

end

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

-- End definition of itemOutput function which returns text like '' .. label .. ''

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

function p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )

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

-- Start preparation of functions

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

----- topological sort and meta data of the DAG ( https://en.wikipedia.org/wiki/Topological_sorting )

--This firstPass() function is called only once, at first

local function firstPass( query, firstItemId, item_repr )

local content = p.itemOutput(firstItemId, lang1, lang2, datas, setdata, getdata, refStyle, pencil)

local opened = 1 -- This means the downstream branch of the item is under processing.

-- If encountering opened item through the processing, it indicates that infinite loop exists.

local closed = 2 -- This means the processing of all downstream branches of the item reached to its end.

-- The downest item is showed with " ? " in tree.

local incomplete = 3 -- This means the processing of all downstream branches of the item reached to its end.

-- If more downstream branches exist but not processed (because of limitation from maxdepth),

-- then the downest item showed with "…" in tree.

local marks = {} --opened(1) or closed(2) or incomplete(3) for each QID (e.g. marks[Q12234234] -> closed)

--local datas = {} --datas[QID]["nparents"], datas[QID]["symbol"], datas[QID]["looped"], datas[QID]["rank"], datas[QID]["status"]

--datas[QID]["nparents"] represents "number of parents" of certain item. This is normally 1.

--If this is 2 or more, it means that the exact same item appears several times in defferent branches of the tree repeatedly.

local childrens = {} -- for example, childrens[Q1234] contains data like {Q123123, Q3984198237, Q1874138746}, three childrens of Q1234

--while there are unmarked nodes do

-- select an unmarked node n

-- visit(n)

--function visit(node n)

-- if n has a temporary mark then stop (not a DAG)

-- if n is not marked (i.e. has not been visited yet) then

-- mark n temporarily

-- for each node m with an edge from n to m do

-- visit(m)

-- mark n permanently

-- add n to head of L

-- this function

-- * visits and builds the tree, DAG or graph,

-- * in the same pass, computes a topological ordering for the DAG

-- * annotates the nodes with informations

function visit(n, depth, rank)

--mw.log("depth is " .. depth .. ">=" .. "maxdepth is " .. maxdepth)

--mw.log(n .. ": mark is " .. tostring(marks[n]))

if marks[n] == opened then

p.setdata(n, "status", "loop")

p.setdata(n, "rank", rank)

return rank

elseif marks[n] ~= closed then

marks[n] = opened

childrens[n] = p.children( query, n, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2)

for _, node in ipairs(childrens[n]) do

p.setdata(node, "nparents",

p.getdata(node, "nparents", 0) + 1

)

--mw.log(node .. ": nparents is " .. tostring(p.getdata(node, "nparents", "aaa")))

if depth <= maxdepth then

rank = visit(node, depth + 1, rank + 1)

end

end

if depth <= maxdepth then

if p.getdata(n, "status", "complete") ~= "loop" then

p.setdata(n, "status", "complete")

end

marks[n] = closed

else

p.setdata(n, "status", "incomplete")

marks[n] = incomplete

end

p.setdata(n, "rank", rank)

end

return rank + 1

end

p.setdata(firstItemId, "nparents", 0)

visit(firstItemId, 1, 0)

return datas, childrens

end

local langobj = mw.language.new(lang1)

-- link inside tree

local function formatSymbol(prefix)

return '(' .. prefix .. ")"

end

local function genAnchor(id)

return ''

end

local function anchorLink(id, content)

return "" .. content .. ""

end

local function fmtTreeLinks(content)

return mw.text.tag("sup", {}, content)

end

local function renderTree( itemId, datas, children, alreadyOuted, gs )

local content = p.itemOutput( itemId, lang1, lang2, datas, setdata, getdata, refStyle, pencil)

local state

if datas[itemId]["status"] ~= nil then

state = datas[itemId]["status"]

end

--mw.log(itemId .. ": status is " .. state )

if datas[itemId] ["nparents"] > 1 and datas[itemId]["symbol"] == nil then

p.setdata(itemId, "symbol", gs() )

end

-- prevent infinite loops in non DAGs

if state == "loop" then

if datas[itemId]["looped"] == nil then

datas[itemId]["looped"] = "treated"

content = fmtTreeLinks(" ∞") .. " " .. content .. genAnchor(itemId)

else

return content .. fmtTreeLinks("∞" .. " ↑ " .. anchorLink(itemId, formatSymbol( datas[itemId]["symbol"] )))

end

elseif state == "incomplete" or state == "unvisited" then

content = content .. " " .. fmtTreeLinks("…")

return content

end

-- has no chilren ? display as leaf

if children[itemId] ~= nil and table.getn(children[itemId]) == 0 then

--return " " .. content .. " ? " -- would be great to use "?b, but font problem

return " " .. content

end

datas[itemId] ["nparents"] = datas[itemId]["nparents"] - 1

local parts = {}

-- sort children topologycally

if alreadyOuted[itemId] == nil then

local langobj = mw.language.new(lang1)

local prefix = " "

if datas[itemId] ["nparents"] > 0 then

local arrow = langobj:getArrow()

prefix = fmtTreeLinks(genAnchor(itemId) .. " " .. arrow .. " " .. formatSymbol(datas[itemId]["symbol"]))

end

order = children[itemId]

table.sort(order, function (a, b) return datas[a]["rank"] < datas[b]["rank"] end )

for i, childId in ipairs(order) do

table.insert( parts, renderTree( childId, datas, children, alreadyOuted, gs ) )

end

if direction == "child" or direction == "brother" then

local l = table.maxn( parts )

for i = 1,(l - 1) do

parts[i] = mw.text.tag( 'li', {}, parts[i] )

end

parts[l] = mw.text.tag( 'li', { class = 'lastline' }, parts[l] )

alreadyOuted[itemId] = prefix .. " " .. content .. mw.text.tag( 'ul', {}, table.concat( parts ) )

elseif direction == "parent" then

local texttmp

local matchCount = 0

--mw.log("parts is: ")

--mw.logObject(parts)

texttmp = mw.text.tag( 'ul', {}, mw.text.tag( 'li', { class = 'lastline' }, content ) )

alreadyOuted[itemId], matchCount = string.gsub(table.concat( parts ), "()", texttmp .. "%1", 1)

--mw.log('matchCount is:')

--mw.log(matchCount)

if matchCount == 0 then

alreadyOuted[itemId] = prefix .. " " .. string.gsub(table.concat( parts ), "$", texttmp, 1)

end

alreadyOuted[itemId] = prefix .. " " .. alreadyOuted[itemId]

--mw.log("alreadyOuted[itemId] is: ")

--mw.logObject(alreadyOuted[itemId])

end

end

if datas[itemId] ["nparents"] <= 0 or state == "loop" then

return alreadyOuted[itemId]

else

return content .. " " .. fmtTreeLinks(anchorLink(itemId, formatSymbol(datas[itemId]["symbol"])) .. " " .. langobj:getArrow(forward))

end

end

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

-- End preparation of functions

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

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

-- Start creating tree --

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

-- gen = p.gensymbols("??")

-- gen = p.gensymbols("12")

-- gen = p.gensymbols("★☆?")

-- These symbols are used as link button for "jumping" from certain branch to other branch.

-- "jumping" functionality is used when same item appears multipul times in one tree.

local gen = p.gensymbols("@*#")

local children

datas, children = firstPass( query, firstItemId, gen)

-- alreadyOuted is the table which contains html code for each QID.

-- For example, like this... alreadyOuted[Q1234] = "

  • Human body
  • "

    -- Actually items which located more nearer to the root (to say, FirstItemId) contain html codes of their branchs within it.

    -- For example, like this... alreadyOuted[Q1234] = "

  • Human body
  • "

    -- This is done by recursive calling of renderTree() function inside the renderTree() function.

    local alreadyOuted = {}

    rendering = {} -- What does this do?

    return renderTree( firstItemId, datas, children, alreadyOuted, gen)

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

    -- End creating tree --

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

    end

    function p.main( frame )

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

    -- Start adjustments of arguments

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

    local frame = frame:getParent()

    local query = frame.args

    local sources

    local refStyle -- Option for refrence style. "none" or "normal" or other

    local pencil -- if pencil == "none", wikidata-linked pencil image is not shown

    local maxdepth

    local lang1

    local lang2

    local firstItemId = query.items

    if firstItemId == nil or firstItemId == '' then

    --If QID is not defined, using the QID of the item which is connected to the current page.

    firstItemId = mw.wikibase.getEntityIdForCurrentPage()

    if not firstItemId then

    return 'No items passed as parameter' --If there is no connected wikidata page, abort.

    end

    end

    --local firstItemsId = mw.text.split( query.items, ' ' )

    --if firstItemsId[1] == nil or firstItemsId[1] == '' or table.maxn( firstItemsId ) == 0 then

    -- --If QID is not defined, using the QID of the item which is connected to the current page.

    -- firstItemsId[1] = mw.wikibase.getEntityIdForCurrentPage()

    -- if not firstItemsId[1] then

    -- return 'No items passed as parameter' --If there is no connected wikidata page, abort.

    -- end

    --end

    --for i, item in pairs( firstItemsId ) do

    if tonumber(firstItemId) then --legacy format

    firstItemId = 'Q'.. tostring(firstItemId)

    end

    --end

    if tonumber(query.childProperty) then --legacy format

    query.childProperty = 'P'.. tostring(query.childProperty)

    end

    if tonumber(query.parentProperty) then --legacy format

    query.parentProperty = 'P'.. tostring(query.parentProperty)

    end

    if query.childDepth and query.childDepth ~= '' then

    query.childDepth = tonumber( query.childDepth )

    else

    query.childDepth = 4

    end

    if query.parentDepth and query.parentDepth ~= '' then

    query.parentDepth = tonumber( query.parentDepth )

    else

    query.parentDepth = 2

    end

    if query.brotherDepth and query.brotherDepth ~= '' then

    query.brotherDepth = tonumber( query.brotherDepth )

    else

    query.brotherDepth = 0

    end

    if query.sources and query.sources ~= '' then

    sources = mw.text.split( query.sources, ' ' )

    else

    sources = nil

    end

    if query.refStyle and query.refStyle ~= '' then

    if query.refStyle == 'none' then

    refStyle = 'none'

    elseif query.refStyle == 'normal' then

    refStyle = 'normal'

    else

    refStyle = mw.text.split( query.sources, ' ' )

    end

    else

    refStyle = 'normal'

    end

    if query.pencil and query.pencil ~= '' then

    pencil = tostring(query.pencil)

    else

    pencil = nil

    end

    if query.lang1 and query.lang1 ~= '' then

    lang1 = query.lang1

    else

    lang1 = mw.language.getContentLanguage().code

    end

    if query.lang2 and query.lang2 ~= '' then

    lang2 = query.lang2

    else

    lang2 = 'en'

    end

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

    -- End adjustments of arguments

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

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

    -- Start creating tree --

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

    local content = ''

    local parent_content = nil

    local child_content = nil

    local brother_content = nil

    local direction -- tree searching direction. "child", "parent" or "brother"

    local property -- property used to search items. For example "P527" (has part) or "P361" (part of)

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

    --Creating tree for parent side (upstream side)

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

    if query.parentProperty and query.parentProperty ~= '' then

    parent_content = ''

    direction = "parent"

    property = query.parentProperty

    maxdepth = query.parentDepth

    --for _, item in pairs( firstItemsId ) do

    parent_content = mw.text.tag( 'li', { class = 'lastline' }, p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )

    --end

    end

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

    --Creating tree for brother side (substream side)

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

    -- Brother is, as definition, "parent's child". So both query.parentProperty and query.childProperty is needed.

    if query.brotherDepth and query.brotherDepth >= 0 and query.parentProperty and query.parentProperty ~= and query.parentDepth >= 1 and query.childProperty and query.childProperty ~= then

    brother_content = ''

    direction = "brother"

    maxdepth = query.brotherDepth

    property = query.parentProperty

    -- Get parent QID

    local parents = p.children(query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )

    --local currentEntity = mw.wikibase.getEntityObject( firstItemId )

    --local claims = currentEntity['claims'][query.parentProperty]

    --if claims then -- If firstItemId has parent

    -- local x = 1

    -- local parents = {}

    -- for _,claim in pairs( claims ) do -- Check all parent claims and get QID from there. And stored it into parents[] table.

    -- local value = claim.mainsnak.datavalue.value

    -- local nextitem = 'Q' .. value['numeric-id']

    -- parents[x] = nextitem

    -- x = x + 1

    -- end

    local currentEntity

    local claims

    local x = 1

    local brothers, brothersPart = {}

    property = query.childProperty

    mw.log("parents is :")

    mw.logObject(parents)

    -- Get brother QID

    for _,parent in pairs( parents ) do

    brothersPart = p.children(query, parent, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )

    mw.log("brothersPart is :")

    mw.logObject(brothersPart)

    --currentEntity = mw.wikibase.getEntityObject( parent )

    --claims = currentEntity['claims'][query.childProperty] -- Get child property in parent entity. Parent's child is brother.

    for _,bro in pairs( brothersPart ) do -- Check all child claims and get QID from there. And stored it into brothers[] table.

    --local value = claim.mainsnak.datavalue.value

    --local nextitem = 'Q' .. value['numeric-id']

    if bro ~= firstItemId then -- One of parent's child is ownself. It is not brother. So exclude that.

    brothers[x] = bro

    x = x + 1

    end

    end

    end

    mw.log("brothers is :")

    mw.logObject(brothers)

    -- Creating trees for brother

    local l = table.maxn( brothers )

    if l >= 1 then

    for i = 1,(l - 1) do

    brother_content = brother_content .. mw.text.tag( 'li', {}, p.outputTree( query, brothers[i], property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )

    end

    brother_content = brother_content .. mw.text.tag( 'li', {}, p.outputTree( query, brothers[l], property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )

    end

    --end

    end

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

    --Creating tree for child side (downstream side)

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

    if query.childProperty and query.childProperty ~= '' then

    child_content = ''

    direction = "child"

    property = query.childProperty

    maxdepth = query.childDepth

    if brother_content and brother_content ~= '' then

    child_content = mw.text.tag( 'li', { class = 'lastline' }, p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )

    else

    child_content = mw.text.tag( 'li', { class = 'lastline' }, p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )

    end

    end

    mw.log("parent_content is: ")

    mw.logObject(parent_content)

    mw.log("")

    mw.log("")

    mw.log("child_content is: ")

    mw.logObject(child_content)

    mw.log("")

    mw.log("")

    mw.log("brother_content is: ")

    mw.logObject(brother_content)

    mw.log("")

    mw.log("")

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

    -- Combine parent and child (and brother) trees

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

    if parent_content and parent_content ~= and child_content and child_content ~= then

    -- Append child-tree to parent-tree.

    -- What is doing here is only replacing the last item (final '

  • ...
  • ') in "parent_content", by "child_content"

    -- But because of Lua regex function is not high functioning, bit complicated process is done here.

    local a = 0

    local b = 0

    -- Finding start position of final item by searching

  • from the left to the right through text.

    while string.find(parent_content, '

  • ', b) do

    mw.log(a .. ' ' .. b)

    a, b = string.find(parent_content, '

  • ', b)

    end

    mw.log(a .. ' ' .. b)

    local c = 0

    local d = 0

    -- Finding end position of final item by searching

  • .

    c, d = string.find(parent_content, '.*$')

    --mw.log(c .. ' ' .. d)

    -- If c, d == nil, it means "parent_content" is one level, is not multipul level tree.

    -- In other words, it contains only one item which is correponding to "firstItemId".

    -- In such case, ignore "parent_content".

    -- Ignore "brother_content" also. Because if no parent known, no brother known.

    if c ~= nil and d ~= nil then

    -- Replacing final item of "parent_content" by "child_content".

    -- If "brother_content" exists, inserts it just after the "child_content".

    if brother_content and brother_content ~= '' then

    content = string.sub(parent_content, 1, a - 1) .. brother_content .. child_content .. string.sub(parent_content, c)

    else

    content = string.sub(parent_content, 1, a - 1) .. child_content .. string.sub(parent_content, c)

    end

    else

    content = child_content

    end

    end

    mw.log("content after is: ")

    mw.logObject(content)

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

    -- Final formatting and adding {{reflist}} like template at the bottom

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

    if parent_content or child_content then

    res = mw.text.tag( 'div', { class = 'treeview' }, mw.text.tag( 'ul', {}, content ) )

    if sources then -- if sources are shown after each items like [1], then put {{reflist}} like template at the bottom

    res = res .. frame:expandTemplate{ title = 'Hidden', args = { 'Sources', frame:expandTemplate{ title = 'notelist', args = {} } } }

    end

    end

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

    -- End creating tree --

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

    return res

    end

    return p