Modulo:Itemlist

Da Wikivoyage.
Jump to navigation Jump to search

Per come utilizzare questo modulo si veda il manuale dei template Citylist e Destinationlist.


local p = {}
local errors = {}
local getArgs = require('Module:Arguments').getArgs


-- Aggiunge tutti gli argomenti dal secondo in avanti alla tabella t
local function dump(t, ...)
	local args = {...}
	for _, s in ipairs(args) do
		table.insert(t, s)
	end
end


local function copy_list (t) 
    local target = {}
    for p,v in ipairs(t) do target[p] = v end
    return target
end

local function different_list(t1, t2)
	if #t1 ~= #t2 then return true end
	for p,v in ipairs(t1) do
		if v ~= t2[p] then return true end
	end
	return false
end

-- =================================================================
-- Classe per accumulare le liste di città raggruppate per titolo
-- =================================================================
local Title = {}
Title.__index = Title

function Title.new(title, listtype)
	return setmetatable({
		title = title or '',
		dirty = true, -- la lista nomi deve essere ordinata prima della stampa
		names_sorted = {}, -- lista di nomi delle città ordinati della stampa
		names_fixed = {}, -- array di nomi di città che devono andare in posizione fissa, indicizzati per posizione
		names_all = {}, -- lista nomi ordinati alfabeticamente + quelli in posizione fissa
		cities = {}, -- array di dati aggiuntivi sulle città {alt, description, full name} indicizzati per nome città
		sort_changed = false,
		listtype = listtype
	 }, Title)
end

function Title:sort()
	-- non ordino se non necessario
	if not self.dirty then return end
	-- ordino la lista dei nomi di città
	local old_list = copy_list(self.names_sorted)
	table.sort(self.names_sorted)
	self.sort_changed = different_list(old_list, self.names_sorted )
	for _,name in ipairs(self.names_sorted) do
		self.names_all[#self.names_all+1] = name
	end
	-- creo una lista delle posizioni fisse e la ordino
	local positions = {}
	for pos, _ in pairs(self.names_fixed) do
		positions[#positions+1] = pos
	end
	if #positions > 0 then self.sort_changed = true end
	table.sort(positions)
	-- inserisco i nomi che vanno in posizione fissa nella lista ordinata
	for _,pos in ipairs(positions) do
		table.insert(self.names_all, math.min(pos, #self.names_all+1), self.names_fixed[pos])
	end
	self.dirty = false
end

function Title:add_item(name, alt, description, position, lat, long)
	position = position or 0
	local label
	if position > 0 then
		if self.names_fixed[position] ~=nil then
			dump(errors, "Posizione " .. position .. " duplicata per città " .. self.names_fixed[position] .. " e " .. name)
			return
		end
		label = name
		self.names_fixed[position] = name
	else
		local wlink = mw.ustring.match(name, "^%[%[(.+)%]%]")
		if wlink then
			link, label = mw.ustring.match(wlink, "(.+)|(.*)") 
			if label == nil then
				label = wlink
			end 
		else
			label = name
		end
	self.names_sorted[#self.names_sorted+1] = label
	end
	self.cities[label] = {alt, description, name, lat, long}
	self.dirty = true
end

function Title.__tostring(self)
	if self.dirty then self:sort() end
	local answer = {}
	if self.names_sorted == 0 then return '' end
	if self.title ~= '' then dump(answer,"<b>", self.title, "</b>\n") end
	local tipo = {
	    citylist = "city",
	    destinationlist = "vicinity"
    }
	for _,name in ipairs(self.names_all) do
		local item = self.cities[name]
		local output_name = item[3]
		if not self.sort_changed and item[4] and item[5] then 
			local frame = mw.getCurrentFrame():getParent() or mw.getCurrentFrame()
			output_name = frame:expandTemplate{ title = 'marker', args = { tipo=tipo[self.listtype], nome=item[3], lat=item[4], long=item[5] } }
		end
		dump(answer, "*<b>", output_name, "</b>")
		if item[1] ~= '' then dump(answer, " (", item[1], ")") end
		if item[2] ~= '' then dump(answer, " &mdash; ", item[2]) end
		dump(answer, "\n")
	end
	return table.concat(answer)
end

setmetatable(Title, { __call = function(_, ...) return Title.new(...) end })
-- =================================================================
-- Fine classe Title
-- =================================================================

local function item(frame)
	local args = getArgs(frame)
	local parameter_name = { 'titolo', 'nome', 'alt', 'pos', 'lat', 'long', 'descrizione'}
	local return_values = {}
	for _,p_name in ipairs(parameter_name) do
		if args[p_name] then 
			return_values[#return_values+1] = string.format('<<%s>>%s<</%s>>', p_name, args[p_name], p_name)
		end
	end
	return table.concat(return_values, ' ')
end

local function string2table(inputData)
	local t = {}
	for k, v in mw.ustring.gmatch(inputData, '<<(%w+)>>(.-)<</%1>>') do
		t[k] = v
	end
	return t
end

local function itemlist(frame, listtype)
	-- Se chiamata mediante #invoke, usa gli argomenti passati al template invocante.
	-- Altrimenti a scopo di test assume che gli argomenti siano passati direttamente
	if frame == mw.getCurrentFrame() then
		origArgs = frame:getParent().args
	else
		origArgs = frame.args
	end

	listtype = listtype or 'citylist'; -- teoricamente superflua, ma evito che chiamate errate (senza listtype) mandino in blocco questa funzione
	local titles = {}
	local end_loop = false
	local first_title_done = false
	local n = 1
	local current_title = 1
	-- Carico le variabili
	while not end_loop do
		local itemargs = origArgs[n]
		local item = {} 
		local name
		if itemargs ~= nil then
			item = string2table(itemargs)
			name = item.nome 
		end 
		if name ~= nil then
			if n <= 20 then --gestisco solo i primi 20 parametri
				local title = item.titolo
				if title ~= nil then
					if first_title_done then current_title = current_title + 1 end
					titles[current_title] = Title(title, listtype)
					first_title_done = true
				elseif not first_title_done then
					titles[1] = Title('', listtype)
					first_title_done = true
				end
				local alt = item.alt or ''
				local description = item.descrizione or ''
				local position_string = item.pos
				if position_string ~= nil then
					position = tonumber(position_string) or 0
					if position == 0 then
						dump(errors, "Il parametro 'pos' della città " .. name .. " non è un numero")
					elseif position < 0 then
						dump(errors, "Il parametro 'pos' della città " .. name .. " è zero o negativo")
					else
						position = math.floor(position)
					end
				else
					position = 0
				end
				titles[current_title]:add_item(name, alt, description, position, item.lat, item.long)
			else
				end_loop = true
				dump(errors, "Il template gestisce fino a 20 parametri")
			end
		else
			if n > 20 then --controllo l'esistenza di almeno i primi 20 parametri
				end_loop = true
			end
		end
		n=n+1
	end
	-- se la lista di titoli ha lunghezza zero termino e ritorno stringa nulla
	if #titles == 0 then return '' end
	-- genero l'output
	local reply = {}
	for _,title in ipairs(titles) do
		reply[#reply+1] = tostring(title)
	end
	if #reply == 0 then return '' end
	if #errors>0 then
		local current_page = mw.title.getCurrentTitle()
		if current_page.namespace == 0 then
			dump(reply, '[[Categoria:' .. listtype .. ' con errori di compilazione]]')
		end
		dump(reply, '<div class="' .. listtype .. 'info debuginfo" style="display:none;">\n' .. table.concat(errors, '; ') .. '</div>')
	end
	return '<div id="' .. listtype .. '">\n' .. table.concat(reply) .. '</div>'
end

function p.GetTitle(frame)
	local t = frame2table(frame)
	return t['titolo']
end

function p.GetName(frame)
	local t = frame2table(frame)
	return t['nome']
end

function p.GetAlt(frame)
	local t = frame2table(frame)
	return t['alt']
end

function p.GetPos(frame)
	local t = frame2table(frame)
	return t['pos']
end

function p.GetDescription(frame)
	local t = frame2table(frame)
	return t['descrizione']
end

function p.GetNameBold(frame)
	local t = frame2table(frame)
	if t['nome'] and #t['nome']>0 then t['nome'] = "<b>" .. t['nome'] .. "</b>" end
	return t['nome']
end

function p.GetLat(frame)
	local t = frame2table(frame)
	return t['lat']
end

function p.GetLong(frame)
	local t = frame2table(frame)
	return t['long']
end

function frame2table(frame)
	local t = {}
	local args = getArgs(frame, {frameOnly=true})
	local inputData = args[1] or ''
	for k, v in mw.ustring.gmatch(inputData, '<<(%w+)>>(.-)<</%1>>') do
		t[k] = v
	end
	return t
end

function p.cityitem(frame)
	return item(frame, 'cityitem')
end

function p.destinationitem(frame)
	return item(frame, 'destinationitem')
end

function p.citylist(frame)
	return itemlist(frame, 'citylist')
end

function p.destinationlist(frame)
	return itemlist(frame, 'destinationlist')
end

return p