Permanently protected module
From Wikipedia, the free encyclopedia


require('strict')



-- Module to create sister project link box

local getArgs = require('Module:Arguments').getArgs

local sideBox = require('Module:Side box')._main

local p = {}



local logo = {

	wikt="Wiktionary-logo-v2.svg",

	c="Commons-logo.svg",

	n="Wikinews-logo.svg",

	q="Wikiquote-logo.svg",

	s="Wikisource-logo.svg",

	b="Wikibooks-logo.svg",

	voy="Wikivoyage-Logo-v3-icon.svg",

	v="Wikiversity logo 2017.svg",

	species="Wikispecies-logo.svg",

	iw="Wikipedia-logo-v2.svg",

	iw1="Wikipedia-logo-v2.svg",

	iw2="Wikipedia-logo-v2.svg",

	d="Wikidata-logo.svg",

	m="Wikimedia Community Logo.svg",

	mw="MediaWiki-2020-icon.svg",

	f="Wikifunctions-logo-en.svg"}



local prefixList = {'wikt', 'c', 'n', 'q', 's', 'b', 'v', 'voy',

	'species', 'species_author', 'iw', 'iw1', 'iw2', 'd', 'm', 'mw', 'f'}



local sisterName = {

	wikt="Wiktionary",

	c="Commons",

	n="Wikinews",

	q="Wikiquote",

	s="Wikisource",

	b="Wikibooks",

	voy="Wikivoyage",

	v="Wikiversity",

	species="Wikispecies",

	iw="Wikipedia",

	iw1="Wikipedia",

	iw2="Wikipedia",

	d="Wikidata",

	m="Meta-Wiki",

	mw="MediaWiki",

    f="Wikifunctions"}



local sisterInfo = {

	wikt="Definitions",

	c="Media",

	n="News",

	q="Quotations",

	s="Texts",

	b="Textbooks",

	voy="Travel guides",

	v="Resources",

	species="Taxa",

	species_author="Authorship",

	iw="edition",

	iw1="edition",

	iw2="edition",

	d="Data",

	m="Discussions",

	mw="Documentation",

    f="Functions"}



local defaultSisters = {

	wikt=true,

	c=true,

	n=true,

	q=true,

	s=true,

	b=true,

	voy='auto',

	v=true,

	species='auto',

	species_author=false,

	iw=false,

	iw1=false,

	iw2=false,

	d=false,

	m=false,

	mw=false,

    f=false

}



local sisterDb = {	

	wikt="enwiktionary",

	n="enwikinews",

	q="enwikiquote",

	s="enwikisource",

	b="enwikibooks",

	voy="enwikivoyage",

	v="enwikiversity",

	species="specieswiki"}



local trackingType = {

	wdMismatch="Pages using Sister project links with wikidata mismatch",

	wdNamespace="Pages using Sister project links with wikidata namespace mismatch",

	wdHidden="Pages using Sister project links with hidden wikidata",

	defaultSearch="Pages using Sister project links with default search"}



local inSandbox = mw.getCurrentFrame():getTitle():find('sandbox', 1, true) 



-- Function to add "-sand" to classes when called from sandbox

local function sandbox(s)

	return inSandbox and s.."-sand" or s

end



-- Function to canonicalize string

-- search for variants of "yes", and "no", and transform

-- them into a standard form (like [[Template:YesNo]])

-- Argument:

--   s --- input string

-- Result:

--  {x,y} list of length 2

--    x = nil if s is canonicalized, otherwise has trimmed s

--    y = canonical form of s (true if "yes" or other, false if "no", nil if blank)

local function canonicalize(s)

	if s == nil then

		return {nil, nil}

	end

	-- if s is table/list, then assume already canonicalized and return unchanged

	if tostring(type(s)) == "table" then

		return s

	end

	s = mw.text.trim(tostring(s))

	if s == "" then

		return {nil, nil}

	end

	local lowerS = s:lower()

	-- Check for various forms of "yes"

	if lowerS == 'yes' or lowerS == 'y' or lowerS == 't' 

	      or lowerS == '1' or lowerS == 'true' or lowerS == 'on' then

		return {nil, true}

	end

    -- Check for various forms of "no"

	if lowerS == 'no' or lowerS == 'n' or lowerS == 'f' 

	       or lowerS == '0' or lowerS == 'false' or lowerS == 'off'then

		return {nil, false}

	end

    -- Neither yes nor no recognized, leave string trimmed

	return {s, true}

end



-- Merge two or more canonicalized argument lists

-- Arguments:

--  argList = list of canonicalized arguments

--  noAll = if true, return no when all argList is no.

--          otherwise, return blank when all argList is blank

local function mergeArgs(argList,noAll)

	local test = nil -- default, return blank if all blank

	if noAll then

		test = false -- return no if all no

	end

	local allSame = true

	-- Search through string for first non-no or non-blank

	for _, arg in ipairs(argList) do

		if arg2 then

			return arg -- found non-no and non-blank, return it

		end

		-- test to see if argList is all blank / no

		allSame = allSame and (arg2 == test)

	end

	-- if all blank / no, return blank / no

	if allSame then

		return {nil, test} -- all match no/blank, return it

	end

	-- otherwise, return no / blank

	if noAll then

		return {nil, nil}

	end

	return {nil, false}

end

		

-- Function to get sitelink for a wiki

-- Arguments:

--   wiki = db name of wiki to lookup

--   qid = QID of entity to search for, current page entity by default

local function getSitelink(wiki,qid)

	-- return nil if some sort of lookup failure

	return qid and mw.wikibase.getSitelink(qid,wiki)

end



-- Function to get sitelink for a wiki

-- Arguments:

--   prefix = prefix string for wiki to lookup

--   qid = QID of entity to search for, current page entity by default

local function fetchWikidata(prefix,qid)

	local sisterDbName = sisterDbprefix

	return sisterDbName and getSitelink(sisterDbName,qid)

end



-- Function to generate the sister link itself

-- Arguments:

--  args = argument table for function

--     args[1] = page to fetch

--     args.default = link when blank

--     args.auto = new auto mode (don't fall back to search)

--     args.sitelink = wikidata sitelink (if available)

--     args.qid = QID of entity

--     args.search = fallback string to search for

--     args.sisterPrefix = wikitext prefix for sister site

--     args.information = type of info sister site contains

--  tracking = tracking table

local function genSisterLink(args, tracking)

	if args1][2 == false or (not args.default and args1][2 == nil) then

		return nil --- either editor specified "no", or "blank" (and default=no), then skip this sister

	end

	local sitelink = args.sitelink or fetchWikidata(args.sisterPrefix,args.qid)

	if args.auto and not sitelink and args1][2 == nil then

		return nil --- in auto mode, if link is blank and no sitelink, then skip

	end

	-- fallback order of sister link: first specified page, then wikidata, then search

	local link = args1][1 or sitelink or (args.search and "Special:"..args.search)

	if not link then

		return nil --- no link found, just skip

	end

	if tracking then

		-- update state for tracking categories

		if args1][1 and sitelink then

			-- transform supplied page name to be in wiki-format

			local page = mw.ustring.gsub(args1][1],"_"," ")

			page = mw.ustring.sub(page,1,1):upper()..mw.ustring.sub(page,2)

			local pageNS = mw.ustring.match(page,"^([^:]+):")

			local sitelinkNS = mw.ustring.match(sitelink,"^([^:]+):")

			if page == sitelink then

				tracking.wdHidden = args.sisterPrefix

			elseif pageNS ~= sitelinkNS then

				tracking.wdNamespace = args.sisterPrefix

			else

				tracking.wdMismatch = args.sisterPrefix

			end

		-- if no page link, nor a wikidata entry, and search is on, then warn

		elseif not (args1][2 or sitelink) and args.search then

			tracking.defaultSearch = args.sisterPrefix

		end

	end

	return {prefix=args.sisterPrefix, link=link, logo=args.logo, name=args.name,

		    information=args.information, prep=args.prep}

end



-- Function to handle special case of commons link

local function commonsLinks(args, commonsPage)

	-- use [[Module:Commons link]] to determine best commons link

	local commonsLink = require('Module:Commons link')

	local cLink = (not args.commonscat) and commonsLink._hasGallery(args.qid)

	                 or commonsLink._hasCategory(args.qid)

	if commonsPage1 and not mw.ustring.match(commonsPage1]:lower(),"^category:") then

		commonsPage1 = (args.commonscat and "Category:" or "")..commonsPage1

	end

	local commonsSearch = "Search/"..(args.commonscat and "Category:" or "")..args1

	return {link=cLink, search=commonsSearch}

end



-- Function to handle special case for "author" and "cookbook"

local function handleSubtype(args)

	local ns = args.ns

	local ns_len = mw.ustring.len(ns)

	local result = {}

	result.sitelink = fetchWikidata(args.prefix, args.qid)

	local subtype = false

	if args.page then

		if mw.ustring.sub(args.page,1,ns_len) == ns then

    		subtype = true

    	elseif args.subtype then

    		result.page = ns..args.page

    		subtype = true

    	end

	elseif result.sitelink then

		subtype = mw.ustring.sub(result.sitelink,1,ns_len) == ns

	elseif args.subtype then

		result.search = "Search/"..ns..args.default

		subtype = true

	end

	if subtype then

		result.info = args.info

	end

	return result

end



-- Function to create a sister link, by prefix

-- Arguments:

--   prefix = sister prefix (e.g., "c" for commons)

--   args = arguments for this sister (see p._sisterLink above)

--   tracking = tracking table

local function sisterLink(prefix, args, tracking)

	-- determine arguments to genSisterLink according to prefix

	if prefix == 'species_author' and not args.species1 and args.species2 and not args.species_author1 and args.species_author2 then

		return nil

	end

	local default = defaultSistersprefix

	if default == 'auto' then

		default = args.auto

	end

	-- Handle exceptions by prefix

	local search = ((prefix == 'd' and "ItemByTitle/enwiki/") or "Search/")..args1

	local sitelink = prefix == 'd' and args.qid

    local page = argsprefix

    local info = sisterInfoprefix

    -- special case handling of author and cookbook

    local subtype = nil

    if prefix == 's' then

    	subtype = handleSubtype({prefix='s',qid=args.qid,subtype=args.author,page=page1],

    		                    ns='Author:',info=nil,default=args1]})

    elseif prefix == 'b' then

    	subtype = handleSubtype({prefix='b',qid=args.qid,subtype=args.cookbook,page=page1],

    		                    ns='Cookbook:',info='Recipes',default=args1]})

    end

    if subtype then

        page1 = subtype.page or page1

		search = subtype.search or search

		sitelink = subtype.sitelink or sitelink

		info = subtype.info or info

	end

    if prefix == 'voy' then

    	if not args.bar then

    		info = "Travel information"

    	end

    	if page1 then

    		if mw.ustring.match(page1],"phrasebook") then

    			info = "Phrasebook"

    		end

    	elseif page2 or args.auto then

    		sitelink = sitelink or fetchWikidata('voy',args.qid)

    		if sitelink and mw.ustring.match(sitelink,"phrasebook") then

    			info = "Phrasebook"

    		end

		end

    end

    info = args.information or info

    if prefix == 'c' then

    	local commons = commonsLinks(args, page)

    	search = commons.search

    	sitelink = commons.link

    end

    prefix = (prefix == 'species_author' and 'species') or prefix

    local logo = logoprefix

    local name = sisterNameprefix

    local prep = "from"

    if mw.ustring.sub(prefix,1,2) == 'iw' then

    	local lang = nil

    	local iw_arg = argsprefix

    	if iw_arg1 then

    		lang = iw_arg1

    	elseif iw_arg2 then

    		local P424 = mw.wikibase.getBestStatements(args.qid, "P424")[1

	        if P424 and P424.mainsnak.datavalue then

	        	lang = P424.mainsnak.datavalue.value

	        end

	    end

		if lang == nil then

			return nil

		end

	    prefix = ':'..lang

	    page1 = ""

	    page2 = true

	    local langname = mw.language.fetchLanguageName( lang, 'en')

	    if not langname or #langname == 0 then

	    	return nil

	    end

	    info = langname..' '..info

	    prep = "of"

    end

    return genSisterLink({

    	page,

    	auto=args.auto,

    	qid=args.qid,

    	logo=logo,

    	name=name,

    	prep=prep,

    	sitelink=sitelink,

    	default=default,

    	sisterPrefix = prefix,

    	search=search,

    	information=info}, tracking)

end



local function templatestyles_page(is_bar)

	local sandbox = inSandbox and 'sandbox/' or ''

	if is_bar then

		return mw.ustring.format(

			'Module:Sister project links/bar/%sstyles.css',

			sandbox

		)

	end

	return mw.ustring.format(

		'Module:Sister project links/%sstyles.css',

		sandbox

	)

end



-- Function to create html containers for sister project link list

-- Arguments:

--   args = table of arguments

--      args.position: if 'left', position links to left

--      args.collapsible: if non-empty, make box collapsible. If 'collapse', start box hidden

--      args.style: CSS style string appended to end of default CSS

--      args.display: boldface name to display

local function createSisterBox(sisterList, args)



	local list = mw.html.create('ul')

    for i, link in ipairs(sisterList) do

	  local li = list:tag('li')

	  -- html element for 27px-high logo

	  local logoSpan = li:tag('span')

	  logoSpan:addClass(sandbox("sister-logo"))

	  logoSpan:wikitext("[[File:"..link.logo.."|27x27px|middle|link=|alt=]]")

	  -- html element for link

	  local linkspan = li:tag('span')

	  linkspan:addClass(sandbox("sister-link"))

	  local linkText = "[["..link.prefix..":"..link.link.."|"..link.information .."]] "..link.prep.." "..link.name

	  linkspan:wikitext(linkText)

    end

    list:allDone()

    

    return sideBox({

		role = 'navigation',

		labelledby = 'sister-projects',

		class = sandbox("sister-box") .. ' sistersitebox plainlinks',

		position = args.position,

		style = args.style,

		abovestyle = args.collapsible and 'clear: both' or nil,

		above = mw.ustring.format(

			"<b>%s</b>  at Wikipedia's [[Wikipedia:Wikimedia sister projects|<span id=\"sister-projects\">sister projects</span>]]",

			args.display or args1

		),

		text = tostring(list),

		collapsible = args.collapsible,

		templatestyles = templatestyles_page()

	})

end



local function createSisterBar(sisterList,args)

	local nav = mw.html.create( 'div' )

	nav:addClass( 'noprint')

	nav:addClass( 'metadata')

	nav:addClass( sandbox('sister-bar'))

	nav:attr( 'role', 'navigation' )

	nav:attr( 'aria-label' , 'sister-projects' )

	local header = nav:tag('div')

	header:addClass(sandbox('sister-bar-header'))

	local pagename = header:tag('b')

	pagename:wikitext(args.display or args1])

	local headerText = " at Wikipedia's [[Wikipedia:Wikimedia sister projects|"

	headerText = headerText..'<span id="sister-projects" style="white-space:nowrap;">sister projects</span>]]:'

	header:wikitext(headerText)

	if #sisterList == 1 and args.trackSingle then

		header:wikitext("[[Category:Pages with single-entry sister bar]]")

	end

	local container = nav:tag('ul')

	container:addClass(sandbox('sister-bar-content'))

	for _, link in ipairs(sisterList) do

		local item = container:tag('li')

		item:addClass(sandbox('sister-bar-item'))

		local logoSpan = item:tag('span')

		logoSpan:addClass(sandbox('sister-bar-logo'))

		logoSpan:wikitext("[[File:"..link.logo.."|21x19px|link=|alt=]]")

		local linkSpan = item:tag('span')

		linkSpan:addClass(sandbox('sister-bar-link'))

		linkSpan:wikitext("<b>[["..link.prefix..":"..link.link.."|"..link.information .."]]</b> "..link.prep.." "..link.name)

	end

	return nav

end



function p._main(args)

	local titleObject = mw.title.getCurrentTitle()

	local ns = titleObject.namespace

	-- find qid, either supplied with args, from search string, or from current page

	args.qid = args.qid or mw.wikibase.getEntityIdForTitle(args1 or "") or mw.wikibase.getEntityIdForCurrentPage()

	args.qid = args.qid and args.qid:upper()

	-- search string defaults to PAGENAME

    args1 = args1 or mw.wikibase.getSitelink(args.qid or "") or titleObject.text

    -- handle redundant "commons"/"c" prefix

    args.c = args.c or args.commons

	-- Canonicalize all sister links (handle yes/no/empty)

	for _, k in ipairs(prefixList) do

		argsk = canonicalize(argsk])

	end

	-- Canonicalize cookbook

	args.cookbook = canonicalize(args.cookbook)

	args.b = mergeArgs({args.b,args.cookbook})

	args.cookbook = args.cookbook2

	-- handle trackSingle parameter

	if args.trackSingle == nil then

    	args.trackSingle = true

	end

    if ns ~= 0 and ns ~= 14 then

    	args.trackSingle = false

    end

    -- Canonicalize general parameters

    for _,k in pairs({"auto","commonscat","author","bar","tracking","sandbox","trackSingle"}) do

    	argsk = canonicalize(argsk])[2

    end

	-- Initialize tracking categories if main namespace

	local tracking = (args.tracking or ns == 0) and {}

    local sisterList = {}

    local prefix

    -- Loop through all sister projects, generate possible links

    for _, prefix in ipairs(prefixList) do

    	local link = sisterLink(prefix, args, tracking)

    	if link then

			table.insert(sisterList, link)

		end

	end

    local box = mw.html.create()

    if args.bar and #sisterList > 0 then

    	box:wikitext(mw.getCurrentFrame():extensionTag{

			name = 'templatestyles', args = { src = templatestyles_page(true) }

    	})

    	box:node(createSisterBar(sisterList,args))

    elseif #sisterList == 1 then

    	-- Use  single sister box instead of multi-sister box

    	local sister = sisterList1

    	local link =  "[["..sister.prefix..":"..sister.link.."|<b><i>"..(args.display or args1]).."</i></b>]]"

    	if sister.name == 'Commons' then

    		sister.name = 'Wikimedia Commons' -- make single sister commons box look like {{Commons}}

    	end

    	local text = sister.name.." has "..mw.ustring.lower(sister.information).." related to "..link.."."

    	if sister.name == 'Wikipedia' then  -- make single sister interwiki box look like {{InterWiki}}

    		text = "[["..sister.prefix..":"..sister.link.."|<b><i>"..sister.information.."</i></b>]] "..sister.prep.." [[Wikipedia]], the free encyclopedia"

    	end

    	box:wikitext(sideBox({

    		role = 'navigation',

    		position=args.position,

    		image="[[File:"..sister.logo.."|40x40px|class=noviewer|alt=|link=]]",

    		metadata='no',

    		class='plainlinks sistersitebox',

    	    text=text,

			templatestyles = templatestyles_page()

    	}))

    elseif #sisterList > 0 then

    	-- else use sister box if non-empty

    	box:wikitext(createSisterBox(sisterList,args))

    end

    if #sisterList == 0 and args.auto then

    	local generateWarning = require('Module:If preview')._warning

    	box:wikitext(generateWarning({"No sister project links found in Wikidata. Try auto=0"}))

    end

	-- Append tracking categories to container div

	-- Alpha ordering is by sister prefix

	if tracking then

		for k, v in pairs(tracking) do

			box:wikitext("[[Category:"..trackingTypek.."|"..v.."]]")

		end

    	if #sisterList == 0 then

    		box:wikitext("[[Category:Pages with empty sister project links]]")

    	end

	end

	return tostring(box)

end



-- Main entry point for generating sister project links box

function p.main(frame)

	local args = getArgs(frame,{frameOnly=false,parentOnly=false,parentFirst=false})

	return p._main(args)

end



-- Lua entry point for generate one sister link

function p._sisterlink(args)

    local prefix = args.prefix

	-- Canonicalize all sister links (handle yes/no/empty)

	for _, k in ipairs(prefixList) do

		argsk = canonicalize(argsk])

	end

	-- Canonicalize cookbook

	args.cookbook = canonicalize(args.cookbook)

	args.b = mergeArgs({args.b,args.cookbook})

	args.cookbook = args.cookbook2

    -- Canonicalize general parameters

    for _,k in pairs({"auto","commonscat","author"}) do

    	argsk = canonicalize(argsk])[2

    end

    args1 = args1 or mw.title.getCurrentTitle().text

	args.qid = args.qid or mw.wikibase.getEntityIdForCurrentPage()

	args.qid = args.qid and args.qid:upper()

	local link = sisterLink(prefix, args,nil)

	if not link then

		return ""

	end

	return "[["..link.prefix..":"..link.link.."|"..link.information .."]] "..link.prep.." "..link.name

end



-- Template entry point for generating one sister link

function p.link(frame)

	local args = getArgs(frame)

	return p._sisterlink(args)

end



return p
Permanently protected module
From Wikipedia, the free encyclopedia


require('strict')



-- Module to create sister project link box

local getArgs = require('Module:Arguments').getArgs

local sideBox = require('Module:Side box')._main

local p = {}



local logo = {

	wikt="Wiktionary-logo-v2.svg",

	c="Commons-logo.svg",

	n="Wikinews-logo.svg",

	q="Wikiquote-logo.svg",

	s="Wikisource-logo.svg",

	b="Wikibooks-logo.svg",

	voy="Wikivoyage-Logo-v3-icon.svg",

	v="Wikiversity logo 2017.svg",

	species="Wikispecies-logo.svg",

	iw="Wikipedia-logo-v2.svg",

	iw1="Wikipedia-logo-v2.svg",

	iw2="Wikipedia-logo-v2.svg",

	d="Wikidata-logo.svg",

	m="Wikimedia Community Logo.svg",

	mw="MediaWiki-2020-icon.svg",

	f="Wikifunctions-logo-en.svg"}



local prefixList = {'wikt', 'c', 'n', 'q', 's', 'b', 'v', 'voy',

	'species', 'species_author', 'iw', 'iw1', 'iw2', 'd', 'm', 'mw', 'f'}



local sisterName = {

	wikt="Wiktionary",

	c="Commons",

	n="Wikinews",

	q="Wikiquote",

	s="Wikisource",

	b="Wikibooks",

	voy="Wikivoyage",

	v="Wikiversity",

	species="Wikispecies",

	iw="Wikipedia",

	iw1="Wikipedia",

	iw2="Wikipedia",

	d="Wikidata",

	m="Meta-Wiki",

	mw="MediaWiki",

    f="Wikifunctions"}



local sisterInfo = {

	wikt="Definitions",

	c="Media",

	n="News",

	q="Quotations",

	s="Texts",

	b="Textbooks",

	voy="Travel guides",

	v="Resources",

	species="Taxa",

	species_author="Authorship",

	iw="edition",

	iw1="edition",

	iw2="edition",

	d="Data",

	m="Discussions",

	mw="Documentation",

    f="Functions"}



local defaultSisters = {

	wikt=true,

	c=true,

	n=true,

	q=true,

	s=true,

	b=true,

	voy='auto',

	v=true,

	species='auto',

	species_author=false,

	iw=false,

	iw1=false,

	iw2=false,

	d=false,

	m=false,

	mw=false,

    f=false

}



local sisterDb = {	

	wikt="enwiktionary",

	n="enwikinews",

	q="enwikiquote",

	s="enwikisource",

	b="enwikibooks",

	voy="enwikivoyage",

	v="enwikiversity",

	species="specieswiki"}



local trackingType = {

	wdMismatch="Pages using Sister project links with wikidata mismatch",

	wdNamespace="Pages using Sister project links with wikidata namespace mismatch",

	wdHidden="Pages using Sister project links with hidden wikidata",

	defaultSearch="Pages using Sister project links with default search"}



local inSandbox = mw.getCurrentFrame():getTitle():find('sandbox', 1, true) 



-- Function to add "-sand" to classes when called from sandbox

local function sandbox(s)

	return inSandbox and s.."-sand" or s

end



-- Function to canonicalize string

-- search for variants of "yes", and "no", and transform

-- them into a standard form (like [[Template:YesNo]])

-- Argument:

--   s --- input string

-- Result:

--  {x,y} list of length 2

--    x = nil if s is canonicalized, otherwise has trimmed s

--    y = canonical form of s (true if "yes" or other, false if "no", nil if blank)

local function canonicalize(s)

	if s == nil then

		return {nil, nil}

	end

	-- if s is table/list, then assume already canonicalized and return unchanged

	if tostring(type(s)) == "table" then

		return s

	end

	s = mw.text.trim(tostring(s))

	if s == "" then

		return {nil, nil}

	end

	local lowerS = s:lower()

	-- Check for various forms of "yes"

	if lowerS == 'yes' or lowerS == 'y' or lowerS == 't' 

	      or lowerS == '1' or lowerS == 'true' or lowerS == 'on' then

		return {nil, true}

	end

    -- Check for various forms of "no"

	if lowerS == 'no' or lowerS == 'n' or lowerS == 'f' 

	       or lowerS == '0' or lowerS == 'false' or lowerS == 'off'then

		return {nil, false}

	end

    -- Neither yes nor no recognized, leave string trimmed

	return {s, true}

end



-- Merge two or more canonicalized argument lists

-- Arguments:

--  argList = list of canonicalized arguments

--  noAll = if true, return no when all argList is no.

--          otherwise, return blank when all argList is blank

local function mergeArgs(argList,noAll)

	local test = nil -- default, return blank if all blank

	if noAll then

		test = false -- return no if all no

	end

	local allSame = true

	-- Search through string for first non-no or non-blank

	for _, arg in ipairs(argList) do

		if arg2 then

			return arg -- found non-no and non-blank, return it

		end

		-- test to see if argList is all blank / no

		allSame = allSame and (arg2 == test)

	end

	-- if all blank / no, return blank / no

	if allSame then

		return {nil, test} -- all match no/blank, return it

	end

	-- otherwise, return no / blank

	if noAll then

		return {nil, nil}

	end

	return {nil, false}

end

		

-- Function to get sitelink for a wiki

-- Arguments:

--   wiki = db name of wiki to lookup

--   qid = QID of entity to search for, current page entity by default

local function getSitelink(wiki,qid)

	-- return nil if some sort of lookup failure

	return qid and mw.wikibase.getSitelink(qid,wiki)

end



-- Function to get sitelink for a wiki

-- Arguments:

--   prefix = prefix string for wiki to lookup

--   qid = QID of entity to search for, current page entity by default

local function fetchWikidata(prefix,qid)

	local sisterDbName = sisterDbprefix

	return sisterDbName and getSitelink(sisterDbName,qid)

end



-- Function to generate the sister link itself

-- Arguments:

--  args = argument table for function

--     args[1] = page to fetch

--     args.default = link when blank

--     args.auto = new auto mode (don't fall back to search)

--     args.sitelink = wikidata sitelink (if available)

--     args.qid = QID of entity

--     args.search = fallback string to search for

--     args.sisterPrefix = wikitext prefix for sister site

--     args.information = type of info sister site contains

--  tracking = tracking table

local function genSisterLink(args, tracking)

	if args1][2 == false or (not args.default and args1][2 == nil) then

		return nil --- either editor specified "no", or "blank" (and default=no), then skip this sister

	end

	local sitelink = args.sitelink or fetchWikidata(args.sisterPrefix,args.qid)

	if args.auto and not sitelink and args1][2 == nil then

		return nil --- in auto mode, if link is blank and no sitelink, then skip

	end

	-- fallback order of sister link: first specified page, then wikidata, then search

	local link = args1][1 or sitelink or (args.search and "Special:"..args.search)

	if not link then

		return nil --- no link found, just skip

	end

	if tracking then

		-- update state for tracking categories

		if args1][1 and sitelink then

			-- transform supplied page name to be in wiki-format

			local page = mw.ustring.gsub(args1][1],"_"," ")

			page = mw.ustring.sub(page,1,1):upper()..mw.ustring.sub(page,2)

			local pageNS = mw.ustring.match(page,"^([^:]+):")

			local sitelinkNS = mw.ustring.match(sitelink,"^([^:]+):")

			if page == sitelink then

				tracking.wdHidden = args.sisterPrefix

			elseif pageNS ~= sitelinkNS then

				tracking.wdNamespace = args.sisterPrefix

			else

				tracking.wdMismatch = args.sisterPrefix

			end

		-- if no page link, nor a wikidata entry, and search is on, then warn

		elseif not (args1][2 or sitelink) and args.search then

			tracking.defaultSearch = args.sisterPrefix

		end

	end

	return {prefix=args.sisterPrefix, link=link, logo=args.logo, name=args.name,

		    information=args.information, prep=args.prep}

end



-- Function to handle special case of commons link

local function commonsLinks(args, commonsPage)

	-- use [[Module:Commons link]] to determine best commons link

	local commonsLink = require('Module:Commons link')

	local cLink = (not args.commonscat) and commonsLink._hasGallery(args.qid)

	                 or commonsLink._hasCategory(args.qid)

	if commonsPage1 and not mw.ustring.match(commonsPage1]:lower(),"^category:") then

		commonsPage1 = (args.commonscat and "Category:" or "")..commonsPage1

	end

	local commonsSearch = "Search/"..(args.commonscat and "Category:" or "")..args1

	return {link=cLink, search=commonsSearch}

end



-- Function to handle special case for "author" and "cookbook"

local function handleSubtype(args)

	local ns = args.ns

	local ns_len = mw.ustring.len(ns)

	local result = {}

	result.sitelink = fetchWikidata(args.prefix, args.qid)

	local subtype = false

	if args.page then

		if mw.ustring.sub(args.page,1,ns_len) == ns then

    		subtype = true

    	elseif args.subtype then

    		result.page = ns..args.page

    		subtype = true

    	end

	elseif result.sitelink then

		subtype = mw.ustring.sub(result.sitelink,1,ns_len) == ns

	elseif args.subtype then

		result.search = "Search/"..ns..args.default

		subtype = true

	end

	if subtype then

		result.info = args.info

	end

	return result

end



-- Function to create a sister link, by prefix

-- Arguments:

--   prefix = sister prefix (e.g., "c" for commons)

--   args = arguments for this sister (see p._sisterLink above)

--   tracking = tracking table

local function sisterLink(prefix, args, tracking)

	-- determine arguments to genSisterLink according to prefix

	if prefix == 'species_author' and not args.species1 and args.species2 and not args.species_author1 and args.species_author2 then

		return nil

	end

	local default = defaultSistersprefix

	if default == 'auto' then

		default = args.auto

	end

	-- Handle exceptions by prefix

	local search = ((prefix == 'd' and "ItemByTitle/enwiki/") or "Search/")..args1

	local sitelink = prefix == 'd' and args.qid

    local page = argsprefix

    local info = sisterInfoprefix

    -- special case handling of author and cookbook

    local subtype = nil

    if prefix == 's' then

    	subtype = handleSubtype({prefix='s',qid=args.qid,subtype=args.author,page=page1],

    		                    ns='Author:',info=nil,default=args1]})

    elseif prefix == 'b' then

    	subtype = handleSubtype({prefix='b',qid=args.qid,subtype=args.cookbook,page=page1],

    		                    ns='Cookbook:',info='Recipes',default=args1]})

    end

    if subtype then

        page1 = subtype.page or page1

		search = subtype.search or search

		sitelink = subtype.sitelink or sitelink

		info = subtype.info or info

	end

    if prefix == 'voy' then

    	if not args.bar then

    		info = "Travel information"

    	end

    	if page1 then

    		if mw.ustring.match(page1],"phrasebook") then

    			info = "Phrasebook"

    		end

    	elseif page2 or args.auto then

    		sitelink = sitelink or fetchWikidata('voy',args.qid)

    		if sitelink and mw.ustring.match(sitelink,"phrasebook") then

    			info = "Phrasebook"

    		end

		end

    end

    info = args.information or info

    if prefix == 'c' then

    	local commons = commonsLinks(args, page)

    	search = commons.search

    	sitelink = commons.link

    end

    prefix = (prefix == 'species_author' and 'species') or prefix

    local logo = logoprefix

    local name = sisterNameprefix

    local prep = "from"

    if mw.ustring.sub(prefix,1,2) == 'iw' then

    	local lang = nil

    	local iw_arg = argsprefix

    	if iw_arg1 then

    		lang = iw_arg1

    	elseif iw_arg2 then

    		local P424 = mw.wikibase.getBestStatements(args.qid, "P424")[1

	        if P424 and P424.mainsnak.datavalue then

	        	lang = P424.mainsnak.datavalue.value

	        end

	    end

		if lang == nil then

			return nil

		end

	    prefix = ':'..lang

	    page1 = ""

	    page2 = true

	    local langname = mw.language.fetchLanguageName( lang, 'en')

	    if not langname or #langname == 0 then

	    	return nil

	    end

	    info = langname..' '..info

	    prep = "of"

    end

    return genSisterLink({

    	page,

    	auto=args.auto,

    	qid=args.qid,

    	logo=logo,

    	name=name,

    	prep=prep,

    	sitelink=sitelink,

    	default=default,

    	sisterPrefix = prefix,

    	search=search,

    	information=info}, tracking)

end



local function templatestyles_page(is_bar)

	local sandbox = inSandbox and 'sandbox/' or ''

	if is_bar then

		return mw.ustring.format(

			'Module:Sister project links/bar/%sstyles.css',

			sandbox

		)

	end

	return mw.ustring.format(

		'Module:Sister project links/%sstyles.css',

		sandbox

	)

end



-- Function to create html containers for sister project link list

-- Arguments:

--   args = table of arguments

--      args.position: if 'left', position links to left

--      args.collapsible: if non-empty, make box collapsible. If 'collapse', start box hidden

--      args.style: CSS style string appended to end of default CSS

--      args.display: boldface name to display

local function createSisterBox(sisterList, args)



	local list = mw.html.create('ul')

    for i, link in ipairs(sisterList) do

	  local li = list:tag('li')

	  -- html element for 27px-high logo

	  local logoSpan = li:tag('span')

	  logoSpan:addClass(sandbox("sister-logo"))

	  logoSpan:wikitext("[[File:"..link.logo.."|27x27px|middle|link=|alt=]]")

	  -- html element for link

	  local linkspan = li:tag('span')

	  linkspan:addClass(sandbox("sister-link"))

	  local linkText = "[["..link.prefix..":"..link.link.."|"..link.information .."]] "..link.prep.." "..link.name

	  linkspan:wikitext(linkText)

    end

    list:allDone()

    

    return sideBox({

		role = 'navigation',

		labelledby = 'sister-projects',

		class = sandbox("sister-box") .. ' sistersitebox plainlinks',

		position = args.position,

		style = args.style,

		abovestyle = args.collapsible and 'clear: both' or nil,

		above = mw.ustring.format(

			"<b>%s</b>  at Wikipedia's [[Wikipedia:Wikimedia sister projects|<span id=\"sister-projects\">sister projects</span>]]",

			args.display or args1

		),

		text = tostring(list),

		collapsible = args.collapsible,

		templatestyles = templatestyles_page()

	})

end



local function createSisterBar(sisterList,args)

	local nav = mw.html.create( 'div' )

	nav:addClass( 'noprint')

	nav:addClass( 'metadata')

	nav:addClass( sandbox('sister-bar'))

	nav:attr( 'role', 'navigation' )

	nav:attr( 'aria-label' , 'sister-projects' )

	local header = nav:tag('div')

	header:addClass(sandbox('sister-bar-header'))

	local pagename = header:tag('b')

	pagename:wikitext(args.display or args1])

	local headerText = " at Wikipedia's [[Wikipedia:Wikimedia sister projects|"

	headerText = headerText..'<span id="sister-projects" style="white-space:nowrap;">sister projects</span>]]:'

	header:wikitext(headerText)

	if #sisterList == 1 and args.trackSingle then

		header:wikitext("[[Category:Pages with single-entry sister bar]]")

	end

	local container = nav:tag('ul')

	container:addClass(sandbox('sister-bar-content'))

	for _, link in ipairs(sisterList) do

		local item = container:tag('li')

		item:addClass(sandbox('sister-bar-item'))

		local logoSpan = item:tag('span')

		logoSpan:addClass(sandbox('sister-bar-logo'))

		logoSpan:wikitext("[[File:"..link.logo.."|21x19px|link=|alt=]]")

		local linkSpan = item:tag('span')

		linkSpan:addClass(sandbox('sister-bar-link'))

		linkSpan:wikitext("<b>[["..link.prefix..":"..link.link.."|"..link.information .."]]</b> "..link.prep.." "..link.name)

	end

	return nav

end



function p._main(args)

	local titleObject = mw.title.getCurrentTitle()

	local ns = titleObject.namespace

	-- find qid, either supplied with args, from search string, or from current page

	args.qid = args.qid or mw.wikibase.getEntityIdForTitle(args1 or "") or mw.wikibase.getEntityIdForCurrentPage()

	args.qid = args.qid and args.qid:upper()

	-- search string defaults to PAGENAME

    args1 = args1 or mw.wikibase.getSitelink(args.qid or "") or titleObject.text

    -- handle redundant "commons"/"c" prefix

    args.c = args.c or args.commons

	-- Canonicalize all sister links (handle yes/no/empty)

	for _, k in ipairs(prefixList) do

		argsk = canonicalize(argsk])

	end

	-- Canonicalize cookbook

	args.cookbook = canonicalize(args.cookbook)

	args.b = mergeArgs({args.b,args.cookbook})

	args.cookbook = args.cookbook2

	-- handle trackSingle parameter

	if args.trackSingle == nil then

    	args.trackSingle = true

	end

    if ns ~= 0 and ns ~= 14 then

    	args.trackSingle = false

    end

    -- Canonicalize general parameters

    for _,k in pairs({"auto","commonscat","author","bar","tracking","sandbox","trackSingle"}) do

    	argsk = canonicalize(argsk])[2

    end

	-- Initialize tracking categories if main namespace

	local tracking = (args.tracking or ns == 0) and {}

    local sisterList = {}

    local prefix

    -- Loop through all sister projects, generate possible links

    for _, prefix in ipairs(prefixList) do

    	local link = sisterLink(prefix, args, tracking)

    	if link then

			table.insert(sisterList, link)

		end

	end

    local box = mw.html.create()

    if args.bar and #sisterList > 0 then

    	box:wikitext(mw.getCurrentFrame():extensionTag{

			name = 'templatestyles', args = { src = templatestyles_page(true) }

    	})

    	box:node(createSisterBar(sisterList,args))

    elseif #sisterList == 1 then

    	-- Use  single sister box instead of multi-sister box

    	local sister = sisterList1

    	local link =  "[["..sister.prefix..":"..sister.link.."|<b><i>"..(args.display or args1]).."</i></b>]]"

    	if sister.name == 'Commons' then

    		sister.name = 'Wikimedia Commons' -- make single sister commons box look like {{Commons}}

    	end

    	local text = sister.name.." has "..mw.ustring.lower(sister.information).." related to "..link.."."

    	if sister.name == 'Wikipedia' then  -- make single sister interwiki box look like {{InterWiki}}

    		text = "[["..sister.prefix..":"..sister.link.."|<b><i>"..sister.information.."</i></b>]] "..sister.prep.." [[Wikipedia]], the free encyclopedia"

    	end

    	box:wikitext(sideBox({

    		role = 'navigation',

    		position=args.position,

    		image="[[File:"..sister.logo.."|40x40px|class=noviewer|alt=|link=]]",

    		metadata='no',

    		class='plainlinks sistersitebox',

    	    text=text,

			templatestyles = templatestyles_page()

    	}))

    elseif #sisterList > 0 then

    	-- else use sister box if non-empty

    	box:wikitext(createSisterBox(sisterList,args))

    end

    if #sisterList == 0 and args.auto then

    	local generateWarning = require('Module:If preview')._warning

    	box:wikitext(generateWarning({"No sister project links found in Wikidata. Try auto=0"}))

    end

	-- Append tracking categories to container div

	-- Alpha ordering is by sister prefix

	if tracking then

		for k, v in pairs(tracking) do

			box:wikitext("[[Category:"..trackingTypek.."|"..v.."]]")

		end

    	if #sisterList == 0 then

    		box:wikitext("[[Category:Pages with empty sister project links]]")

    	end

	end

	return tostring(box)

end



-- Main entry point for generating sister project links box

function p.main(frame)

	local args = getArgs(frame,{frameOnly=false,parentOnly=false,parentFirst=false})

	return p._main(args)

end



-- Lua entry point for generate one sister link

function p._sisterlink(args)

    local prefix = args.prefix

	-- Canonicalize all sister links (handle yes/no/empty)

	for _, k in ipairs(prefixList) do

		argsk = canonicalize(argsk])

	end

	-- Canonicalize cookbook

	args.cookbook = canonicalize(args.cookbook)

	args.b = mergeArgs({args.b,args.cookbook})

	args.cookbook = args.cookbook2

    -- Canonicalize general parameters

    for _,k in pairs({"auto","commonscat","author"}) do

    	argsk = canonicalize(argsk])[2

    end

    args1 = args1 or mw.title.getCurrentTitle().text

	args.qid = args.qid or mw.wikibase.getEntityIdForCurrentPage()

	args.qid = args.qid and args.qid:upper()

	local link = sisterLink(prefix, args,nil)

	if not link then

		return ""

	end

	return "[["..link.prefix..":"..link.link.."|"..link.information .."]] "..link.prep.." "..link.name

end



-- Template entry point for generating one sister link

function p.link(frame)

	local args = getArgs(frame)

	return p._sisterlink(args)

end



return p

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook