Moduuli tuottaa automaattisesti päivittyvän hakemiston yksittäisestä sivusta tai tietyn sivun arkistoista.

Käyttö sivun hakemiston tuottamiseen:
{{#invoke:Hakemisto|luo_hakemisto
| nimiavaruus     = 
| sivu            = 
}}

Esimerkki:
{{#invoke:Hakemisto|luo_hakemisto|nimiavaruus=Wikisanakirja|sivu=Kahvihuone/arkisto4}}

Käyttö arkistohakemiston tuottamiseen:
{{#invoke:Hakemisto|arkistot
| nimiavaruus     = 
| sivu            = 
| väli            = 
| etuliite        =
| ensimmäinen     = 
| viimeinen       = 
}}

Arkistohakemiston voi aloittaa tietystä arkistonumerosta (parametrilla ensimmäinen, oletusarvo 1) ja päättää tiettyyn numeroon (parametrilla viimeinen). Mikäli parametrille viimeinen ei määritetä mitään arvoa, moduuli muodostaa hakemistot viimeisimpään olemassaolevaan arkistoon saakka. Jos arkistonumeroiden edellä ei käytetä välilyöntiä (tyyliin Arkisto3), on parametrin väli arvoksi asetettava ei. Lisäksi jos arkistoetuliitteenä on jokin muu teksti kuin Arkisto, voi tarvittavan säädön tehdä parametrilla etuliite. Esimerkiksi pienellä alkukirjaimella kirjoitetun etuliitteen tapauksessa moduulikutsussa tarvitaan kohtaa | etuliite = arkisto.

Esimerkkejä:
{{#invoke:Hakemisto|arkistot|nimiavaruus=Wikisanakirja|sivu=Kahvihuone|etuliite=arkisto|väli=ei}}

{{#invoke:Hakemisto|arkistot|nimiavaruus=Wikisanakirja|sivu=Ylläpitäjien ilmoitustaulu}}

{{#invoke:Hakemisto|arkistot|nimiavaruus=Keskustelu käyttäjästä|sivu=Sentree|väli=ei}}

{{#invoke:Hakemisto|arkistot
| nimiavaruus     = Wikisanakirja
| sivu            = Kahvihuone
| väli            = ei
| etuliite        = arkisto
| ensimmäinen     = 7
| viimeinen       = 11
}}

Moduulia substatessa kannattaa käyttää parametria subst ja asettaa sen arvoksi jotakin muuta kuin ei.

Esimerkkejä:

{{subst:#invoke:Hakemisto|arkistot|nimiavaruus=Wikisanakirja|sivu=Kahvihuone|etuliite=arkisto|väli=ei|subst=k}}
{{subst:#invoke:Hakemisto|arkistot|nimiavaruus=Wikisanakirja|sivu=Kahvihuone|etuliite=arkisto|väli=ei|ensimmäinen=3|viimeinen=7|subst=k}}
{{subst:#invoke:Hakemisto|arkistot|nimiavaruus=Keskustelu käyttäjästä|sivu=Sentree|väli=ei|subst=k}}

local arkistoLista = require( 'Module:Archive list' )

local p = {}


local function poista_linkit(alkup)
	local result = alkup

    -- Poistetaan sisäiset linkitykset ja mahdolliset [[-merkinnän jälkeiset
    -- kaksoispisteet (esim. [[: Luokka:Sejase]] → Luokka:Sejase).
	result = string.gsub(result, '%[%[.-|(.-)%]%]', '%1')
	result = string.gsub(result, '%[%[%s-:?%s-(.-)%]%]', '%1')

	-- Poistetaan http:-alkuiset ulkoiset linkitykset.
	result = string.gsub(result, '%[http.-%/%/%S.-%s%s-(.-)%]', '%1')
	
	return mw.text.trim(result)
end

 
 -- paikallinen funktio hakemistolinkin luomiseen
 local function hakemistolinkki(indeksoitava_sivu, otsake, nro, substaus)
	local linkin_alkuosa, linkin_loppuosa
 	local linkki
    local syntH = '[sS][yY][nN][tT][aA][xX][hH][iI][gG][hH][lL][iI][gG][hH][tT]'
	local noWiki = '[nN][oO][wW][iI][kK][iI]'
	
	if nro == nil or nro < 1 then
		nro = 1
	else
		nro = math.floor(nro)
	end
	local subst = substaus or false

	if(otsake ~= nil) then
		-- Poistetaan turha whitespace otsakkeen alusta ja lopusta.
		-- Muodostetaan linkki kahdessa osassa, jotta on helpompi  
		-- käsitellä otsikon tiettyä osaa erikseen. 
		linkin_alkuosa = mw.text.trim(otsake)
		linkin_loppuosa = linkin_alkuosa
	else 
		linkki = ''
		return linkki
	end

	-- Poistetaan kohdelinkistä <nowiki>-, <pre>-, <code>-,
	-- <tt>-, <syntaxhighlight>- ja <poem>-tagit. 
  	linkin_alkuosa = string.gsub(linkin_alkuosa, 
  		'<'..noWiki..'>(.-)</'..noWiki..'%s->', '%1')
  	linkin_alkuosa = string.gsub(linkin_alkuosa, 
  		'<[pP][rR][eE]>(.-)</[pP][rR][eE]%s->', '%1')
  	linkin_alkuosa = string.gsub(linkin_alkuosa, 
  		'<[cC][oO][dD][eE][^><]->(.-)</[cC][oO][dD][eE]%s->', '%1')
  	linkin_alkuosa = string.gsub(linkin_alkuosa, 
  		'<[tT][tT][^><]->(.-)</[tT][tT]%s->', '%1')
  	linkin_alkuosa = string.gsub(linkin_alkuosa, 
  		'<'..syntH..'>(.-)</syntH%s->', '%1')
  	linkin_alkuosa = string.gsub(linkin_alkuosa, 
  		'<[pP][oO][eE][mM]>(.-)</[pP][oO][eE][mM]%s->', '%1')
  	
  	-- Korvataan kohdelinkissä {{m|Malline...}} tekstillä {{Malline...}}
  	linkin_alkuosa = string.gsub(linkin_alkuosa, '{{%s-[Mm]%s-|(.-)}}', '{{%1}}')
	linkin_alkuosa = string.gsub(linkin_alkuosa, '{{%s-[Mm]alline%s-|(.-)}}', '{{%1}}')

	if subst == false then
		-- Jos ei olla substaamassa moduulin tulostetta, otetaan linkin 
		-- näkyväksi tekstiosaksi otsikon siivottu versio.
		linkin_loppuosa = linkin_alkuosa
	else
	  	-- Substauksessa korvataan esimerkiksi {{m|Malline}} 
	  	-- tekstillä <nowiki>{{Malline}}</nowiki>. 
	  	linkin_loppuosa = string.gsub(linkin_loppuosa, 
	  		'{{%s-[Mm]%s-|(.-)}}', '<nowiki>{{%1}}</nowiki>')
		linkin_loppuosa = string.gsub(linkin_loppuosa, 
			'{{%s-[Mm]alline%s-|(.-)}}', '<nowiki>{{%1}}</nowiki>')
		-- Jos linkissä ei ole <nowiki>-tageja (ja moduulin tulostetta
		-- ollaan substaamassa), ympäröidään {{malline}}-tekstit niillä.
		if string.find(linkin_loppuosa, '<nowiki%s->') == nil then
			linkin_loppuosa = string.gsub(linkin_loppuosa, 
				'{{%s-(%S%S-)%s-}}','<nowiki>{{%1}}</nowiki>')
		end
	end

	-- Poistetaan kohdelinkissä toimintakytkinten (behavior switches) alaviivat.
  	linkin_alkuosa = string.gsub(linkin_alkuosa, '__(.-)__', '%1')

  	-- Estetään toimintakytkinten toiminta sanayhdistimellä (word joiner, &#8288;).
  	linkin_loppuosa = string.gsub(linkin_loppuosa, '__(.-)__', '_%&#8288;_%1__')

	-- Siistitään kohdelinkin <span>tekstiä</span>-tyyppiset osat.
   	linkin_alkuosa = string.gsub(linkin_alkuosa, '<span.->(.-)</span>', '%1')

	-- Poistetaan kohdelinkistä kursivointi ja lihavointi. 
   	linkin_alkuosa = string.gsub(linkin_alkuosa, "'?''(.-)'''?", '%1')

	-- Varmistetaan normaalisti moduulin tulosteessa näkymättömän kohdan 
	-- aiheuttavien tagien näkyminen linkkitekstissä sanayhdistimen avulla. 
	-- Linkin muihin <-merkkeihin tarttuminen ei haittaa, sillä kohde-
	-- linkitykseen ei kajota vaan ainoastaan lukijalle näkyvään tekstiin.
	if subst == false then
		linkin_loppuosa = string.gsub(linkin_loppuosa, '<', '<%&#8288;')
	end

	-- Prosenttikoodataan harmaita hiuksia aiheuttavat merkit mw.uri.encodella.
     linkin_alkuosa = mw.uri.encode( linkin_alkuosa, 'WIKI' )
     
     -- Otetaan tarvittaessa toistuvan otsikon järjestysnumero huomioon.
     if nro > 1 then
		linkin_alkuosa = linkin_alkuosa..'_'..nro
     end

	-- Koostetaan linkki.
	linkin_alkuosa = indeksoitava_sivu..'#'..linkin_alkuosa
	linkki = '[['..linkin_alkuosa..'|'..linkin_loppuosa..']]'
	return mw.text.trim(linkki)
end


-- muuntaa html-otsikot wikiformaattiin
local function hWikitys(hAlku,heading,hLoppu)
	if hAlku == nil or heading == nil or hLoppu == nil then
		return ''
	end

	local i
	local merkki = {}
	local otsake = heading
	local alkuTaso = tonumber(hAlku) 
	local loppuTaso = tonumber(hLoppu) 

	if alkuTaso == nil or loppuTaso == nil then
		return ''
	end

	if alkuTaso < 1 or alkuTaso > 6 or loppuTaso < 1 or loppuTaso > 6 then
		return ''
	end

    for i = 1, alkuTaso do
    	table.insert(merkki, '=')
    end
    otsake = string.gsub(otsake, '\n', '') 
	return '\n\n'..table.concat(merkki)..otsake..table.concat(merkki)..'\n\n'	
end


-- sivun hakemiston automaattisesti generoiva funktio
local function hakemisto(sivu, nimiavaruus, substaus)
	-- Haetaan sivun sisältö. 
    local title = mw.title.new(sivu, nimiavaruus)
	local aineisto = title:getContent()
	assert(title, 'Valitettavasti sivun sisällön hakeminen ei onnistunut (virhe 1)')
	assert(aineisto, 'Valitettavasti sivun sisällön hakeminen ei onnistunut (virhe 2)')

	local subst = substaus or false
	local alku, loppu, i, w, taso, aiempi_taso
	local otsikko, paljas_otsikko
	local hakemisto = {}
    local otsikot = {}
    local syntH = '[sS][yY][nN][tT][aA][xX][hH][iI][gG][hH][lL][iI][gG][hH][tT]'
	local noWiki = '[nN][oO][wW][iI][kK][iI]'
	
    aineisto = string.gsub(aineisto,'\n<', '\n <')
    aineisto = string.gsub(aineisto,'<[pP][rR][eE]%s[^>]->', '<pre>')
    aineisto = string.gsub(aineisto,'<'..noWiki..'%s[^>]->', '<nowiki>')
    aineisto = string.gsub(aineisto,'<'..syntH..'%s[^>]->', '<syntaxhighlight>')
    aineisto = string.gsub(aineisto,'<[pP][oO][eE][mM]%s[^>]->', '<poem>')

	-- Otsikko muodostuu (yksirivisenä), vaikka sen sisällä olevien <pre>- 
	-- <nowiki>-, <syntaxhighlight>- ja <poem>-tagien välissä olisi 
	-- rivinvaihtoja, joten poistetaan ko. tagien sisällä olevat rivinvaihdot. 
    aineisto = string.gsub(aineisto, '<[pP][rR][eE]>.-</[pP][rR][eE]%s->', 
    	function (z) return string.gsub(z, '\n', '') end )
    aineisto = string.gsub(aineisto, '<'..noWiki..'>.-</'..noWiki..'%s->', 
    	function (z) return string.gsub(z, '\n', '') end )
    aineisto = string.gsub(aineisto, '<'..syntH..'>.-</'..syntH..'%s->', 
    	function (z) return string.gsub(z, '\n', '') end )
    aineisto = string.gsub(aineisto, 
    	'<[pP][oO][eE][mM]>.-</[pP][oO][eE][mM]%s->', 
    	function (z) return string.gsub(z, '\n', '') end )

	-- Karsitaan pois <pre>-, <nowiki>-, <syntaxhighlight>- ja <poem>--tagein 
	-- merkitty sisältö, joka sijaitsee muualla kuin otsikoiden sisällä, jotta 
	-- ko. tagien sisällä olevat otsikot vältetään otsikoita haettaessa.
    aineisto = string.gsub(aineisto, 
    	'(\n[^=\n][^\n]-)<[pP][rR][eE]>.-</[pP][rR][eE]%s->', '%1')
    aineisto = string.gsub(aineisto, 
    	'(\n[^=\n][^\n]-)<[nN][oO][wW][iI][kK][iI]>.-</[nN][oO][wW][iI][kK][iI]%s->', '%1')
    aineisto = string.gsub(aineisto, 
    	'(\n[^=\n][^\n]-)<'..syntH..'>.-</'..syntH..'%s->', '%1')
    aineisto = string.gsub(aineisto, 
    	'(\n[^=\n][^\n]-)<[pP][oO][eE][mM]>.-</[pP][oO][eE][mM]%s->', '%1')

	-- Poistetaan kaikki piilokommentit.
    aineisto = string.gsub(aineisto, '<%!%-%-.-%-%->', '' )

	-- Muunnetaan html-tageilla luodut otsikot wikiformaattin, jotta 
	-- kaikki otsikot saadaan purkkiin samalla haulla.
	aineisto = string.gsub(aineisto,'(</?[hH]%d)%s[^>]-(>)', '%1%2')
    aineisto = string.gsub(aineisto, '<[hH](%d)%s->(.-)</[hH](%d)%s->', hWikitys)

	-- Otsikoiden etsimistä yksinkertaistavia toimenpiteitä.
	aineisto = '\n'..aineisto..'\n'
	aineisto = string.gsub(aineisto, '(\n=[^\n]-[^\n%s][^\n]-=)%s-\n', '\n%1\n\n' )

	-- Poistetaan kokonaan tyhjät otsikot. 
    aineisto = string.gsub(aineisto, '\n==-%s-==-%s-\n', '\n\n' ) 

    -- Etsitään ensimmäisen otsikon alku ja loppu.
    alku, loppu = string.find(aineisto, '\n=[^\n]-[^\n%s][^\n]-=%s-\n')

	if alku == nil or loppu == nil then
	    local alkup = mw.title.new(sivu, nimiavaruus)
		return 'Sivulta ei löytynyt yhtään otsikkoa! ' 
		.. 'Sivun getContent-funktiolla haettu sisältö:<br /><br />'..alkup:getContent()
		.. '<br /><br /><br />Aineisto hakemistomoduulin tekemien muutosten jälkeen:<br /><br />'..aineisto
	end

	-- Haetaan sivun otsikot tauluun. 
    for w in string.gfind(aineisto, '\n(=[^\n]-[^\n%s][^\n]-=)%s-\n') do
      table.insert(otsikot, w)
    end

	table.insert(hakemisto, '<ol>')
	aiempi_taso = 2

	-- Silmukka käy läpi kaikki sivulta löytyneet otsikot. 
    for index, otsikko in ipairs(otsikot) do 
		
		-- Lasketaan while-silmukan avulla yhtäsuuruusmerkkien määrä (eli 
		-- käytännössä otsikkotaso) ja pannaan se muistiin.
		i = 1
		while string.sub(otsikko,i,i) == '=' do 
			i = i + 1 
		end
        taso = i - 1
        
		-- Otsikkotason muuttuessa lisätään riittävä määrä järjestetyn 
		-- luettelon aloitus-/lopetustageja, jotta otsikot saadaan 
		-- lopulliseen hakemistoon oikeille tasoilleen.
        if taso > aiempi_taso then
			for i = 1, (taso - aiempi_taso) do 
				table.insert(hakemisto, '<ol>')
        	end
        elseif taso < aiempi_taso then
        	for i = 1, (aiempi_taso - taso) do
				table.insert(hakemisto, '</ol>')
        	end
        end
        
		-- Pistetään otsikkotaso muistiin silmukan seuraavaa kierrosta varten.
        aiempi_taso = taso

		-- Poistetaan otsikosta linkitykset sekä yhtäsuuruusmerkit alusta ja lopusta.
		paljas_otsikko = poista_linkit(string.sub(otsikko, taso+1, -taso-1))

		-- Lasketaan montako kertaa otsikko on aiemmin esiintynyt hakemistossa.
		-- Jos otsikossa on Luan erikoismerkkejä, lisätään gsubia varten 
		-- %-merkki niiden eteen, jotta ne eivät sekoita pattern matchingia 
		-- (laskeminen tapahtuu gsubin suorittamien korvaustoimintojen lukumäärän 
		-- perusteella).
		i = select(2, table.concat(hakemisto):gsub('|'..
			paljas_otsikko:gsub('([%(%)%%%.%+%-%*%[%]%?])', '%%%1')..
			'%]%]</li>', ''))
			
		-- Lisätään hakemistoon seuraava otsikko.  
		table.insert(hakemisto, '<li>')
    	table.insert(hakemisto, 
    		hakemistolinkki(title.fullText, paljas_otsikko, i+1, subst))
    	table.insert(hakemisto,'</li>')
    	if subst == true then
	    	table.insert(hakemisto,'\n')
	    end
	end

	-- Viimeistellään hakemisto.
	if (taso > 2) then
		for i = 2, taso do
			table.insert(hakemisto,'</ol>')
		end
	elseif (taso > 1) then
		table.insert(hakemisto,'</ol>')
	end

	return table.concat( hakemisto )
end


-- Muodostaa yksittäisen sivun hakemiston. 
function p.luo_hakemisto(frame)
	-- Käsitellään pari virhetilannetta.
	assert(frame.args['nimiavaruus'], 'Nimiavaruus tarvitaan hakemiston tuottamiseksi')
	assert(frame.args['sivu'], 'Sivun nimi tarvitaan hakemiston tuottamiseksi')
	
	local nimiavaruus = frame.args['nimiavaruus']
	nimiavaruus = string.gsub(nimiavaruus, '%s', '_')

	-- Jos on annettu argumentti subst (eikä sen arvo ole 'ei'), 
	-- pyritään tekemään moduulin substaaminen toimivaksi. 
	-- Muussa tapauksessa suoritetaan toimenpiteet substaamattoman 
	-- tulosteen kannalta järkevällä tavalla.
	local substaus
	if frame.args['subst'] == nil or frame.args['subst'] == 'ei' then
		substaus = false
	else
		substaus = true
	end

	return hakemisto(frame.args['sivu'], nimiavaruus, substaus)
end


--[[Funktio arkistot muodostaa hakemistot halutuista tai kaikista arkistosivuista. 
Käyttöesimerkkejä: 
{{#invoke:Hakemisto|arkistot|nimiavaruus=User_talk|sivu=Ejs-80|ensimmäinen=4|viimeinen=8}}
{{#invoke:Hakemisto|arkistot|nimiavaruus=Wikipedia|sivu=Ylläpitäjien ilmoitustaulu}}
{{#invoke:Hakemisto|arkistot|nimiavaruus=Wikipedia|sivu=Botit/pyynnöt|väli=ei}}
]]--
function p.arkistot(frame)
	-- Käsitellään pari virhetilannetta.
	assert(frame.args['nimiavaruus'], 'Nimiavaruus tarvitaan hakemiston tuottamiseksi')
	assert(frame.args['sivu'], 'Sivun nimi tarvitaan hakemiston tuottamiseksi')
	
	local nimiavaruus = frame.args['nimiavaruus']
	nimiavaruus = string.gsub(nimiavaruus, '%s', '_')

	local sivu = frame.args['sivu']
	local aloitusNro, lopetusNro
	local etuliite
	local arkistohakemisto = {}
	local space 
	local i = 1

	-- Jos on annettu argumentti subst (eikä sen arvo ole 'ei'), 
	-- pyritään tekemään moduulin substaaminen toimivaksi. 
	-- Muussa tapauksessa suoritetaan toimenpiteet substaamattoman 
	-- tulosteen kannalta järkevällä tavalla.
	local substaus
	if frame.args['subst'] == nil or frame.args['subst'] == 'ei' then
		substaus = false
	else
		substaus = true
	end
	
	-- Otetaan arkistoissa käytetty etuliite joko argumentista 
	-- 'etuliite' tai asetetaan sen arvoksi 'Arkisto'. 
	if frame.args['etuliite'] ~= nil then
		etuliite = frame.args['etuliite']
		if etuliite == '' then
			etuliite = 'Arkisto'
		end
	else
		etuliite = 'Arkisto'
	end

	-- Lisätään välilyönti arkistonumeron edelle paitsi jos 'väli'-
	-- argumentilla on annettu tieto, jonka mukaan arkistoissa ei 
	-- käytetä välilyöntiä numeron edellä. 
	space = 'yes'
	if frame.args['väli'] ~= nil then
		if frame.args['väli'] == 'ei' then
			space = ''
		end
	end

	-- Muuttujaan aloitusNro tulee sen arkistosivun nro, 
	-- josta hakemistojen luominen aloitetaan.
	if tonumber(frame.args['ensimmäinen']) ~= nil then
		aloitusNro = tonumber(frame.args['ensimmäinen'])
	else
		aloitusNro = 1
	end

	-- argumentit Archive list -moduulin count-funktiota varten
	local arkistoArgs = { 
		root = nimiavaruus..':'..sivu,
        prefix = etuliite, 
        prefixspace = space, 
        alku = aloitusNro
	}

	-- Muuttujaan lopetusNro tulee sen arkistosivun nro, 
	-- johon hakemistojen luominen lopetetaan.
	if tonumber(frame.args['viimeinen']) ~= nil then
		lopetusNro = tonumber (frame.args['viimeinen'])
	else
		-- Jos argumenteissa ei ole kerrottu arkistonumeroa, johon 
		-- hakemistojen teko halutaan lopettaa, etsitään suurin 
		-- arkistonumero Archive list -moduulin count-funktiolla. 
		lopetusNro = arkistoLista.count(arkistoArgs)
		if lopetusNro == nil then 
			lopetusNro = 1
		end
	end
	
	-- Tässä for-silmukassa kerätään otsikot tauluun ja muodostetaan 
	-- käsiteltävien arkistosivujen hakemistot. 
	for i = aloitusNro, lopetusNro, 1 do 
		if space == 'yes' then 
			sivu = frame.args['sivu'] .. '/' .. etuliite .. ' ' .. i
		else
			sivu = frame.args['sivu'] .. '/' .. etuliite .. i
		end
		table.insert(arkistohakemisto, '<h2>[[' .. nimiavaruus..':'..sivu)
		table.insert(arkistohakemisto, '|Arkisto ' .. i ..']]')
		table.insert(arkistohakemisto, '</h2>\n')
		table.insert(arkistohakemisto, hakemisto(sivu, nimiavaruus, substaus))
		if i < lopetusNro then
			table.insert(arkistohakemisto, '\n\n')
		end
	end

	-- Koostetaan kaikki arkistohakemisto-tauluun kertyneet merkkijonot 
	-- yhteen ja palautetaan kokonaisuus funktion kutsujalle. 
	return table.concat( arkistohakemisto )
end

return p