Module:Listing

--[[ 	Source script:	https://it.wikivoyage.org/wiki/Modulo:Listing 	Maintainer:		Andyrom75  	Lazy loads: 	require('Module:IsLatin').IsLatinValue 	require('Module:EmailTracking').EmailTrackingValue ]] local Marker = require('Module:Marker').MarkerModule local LinkModule = require('Module:LinkPhone') local MapLink = require('Module:MapLink').MapLinkModule local i18n = require( 'Module:Listing/i18n' ) local par = i18n.parameters local marPar = i18n.markerParameters  local function _templateStyle( frame, src )    return frame:extensionTag( 'templatestyles', '', { src = src } ) end  local function _isDefined(s) 	return s ~= '' and s end  local function _hiddenUnicode( value )     -- Notez que la citation ("") ci-dessous n'est pas vide et contient en réalité un caractère Unicode caché.     return mw.ustring.match(value or '',"‎") and '[[Category:' .. i18n.categories.hiddenChar .. ']]<span class="unicodeinfo debuginfo">' .. i18n.errorMsg.unicode .. '</span>' or '' end  local function _email( mail )     return '<span class="email listing-email">' .. LinkModule.LinkEmail{ args={mail} } .. '</span>' .. require('Module:EmailTracking').EmailTrackingValue(mail) end  local function _concatIndexed( tab )     local tt = {}     for _, v in pairs( tab ) do         tt[#tt+1] = v     end     return table.concat( tt ) end  local function _validateParams( params )     -- Je construis la liste des clés valides dans la langue configurée.     local validParams = {}     for _, translatedKey in pairs(par) do         validParams[translatedKey] = true     end 	-- Je vérifie si, parmi les paramètres reçus, il n'y a que les paramètres autorisés. 	local invalidParams = '' 	for key, _ in pairs( params ) do 		if not validParams[key] then 			invalidParams = invalidParams .. '<b>' .. key ..'</b>; ' 		end 	end 	return _isDefined(invalidParams) and (((mw.title.getCurrentTitle().namespace == 0) and ('[[Category:' .. i18n.categories.invalidPar .. ']]') or '') ..'<span class="debuginfo">' .. i18n.errorMsg.wrongPar .. invalidParams .. '</span>') or '' end  local function _sanitizeCaption(s) 	s = s or '' 	s = s:gsub('[\r\n]+', ' ')						-- pas de saut de ligne 	s = s:gsub('|', '&#124;')						-- pipe letterale 	s = s:gsub('%[', '&#91;'):gsub('%]', '&#93;')	-- quadre letterali 	return s end  local function _renderAdjustment( content ) 	if not _isDefined(content) then return '' end 	 	--Le remplacement temporaire des styles de modèles utilisés dans les descriptions, car leur contenu peut compromettre le traitement ultérieur, est nécessaire. 	local styles = {} 	local style = '' 	local patterns = {'(style=".-")', "(style='.-')"} 	for i=1, #patterns do 		local pos = mw.ustring.find(content, patterns[i]) 		while pos do 			style = mw.ustring.gsub(content, '.-'..patterns[i]..'.*', '%1') 			styles[#styles+1] = style 			content = mw.ustring.sub( content, 1, pos-1 ) .. "<<§@§>>" .. mw.ustring.sub( content, pos+#style) 			pos = mw.ustring.find(content, patterns[i]) 		end 	end  	local level1 = not mw.ustring.find(content, '\n[*#][*#]') and not mw.ustring.find(content, '\n:+[*#:][*#]') 	if mw.ustring.find(mw.ustring.sub(content,1,1), '[:;*#]') then 		-- Si la description commence par un caractère spécial, je la mets en forme pour qu'elle soit correctement interprétée par le wiki. 		content = '\n' .. content 	end 	 	-- Je limite l'utilisation coûteuse de gsub aux cas de descriptions multilignes. 	-- De plus, en raison des limitations actuelles, j'évite de traiter les descriptions contenant des listes HTML à plusieurs niveaux. 	if mw.ustring.find(content, '\n') and level1 then 		-- Je gère les cas les plus courants de « ; », bien que recréer le rendu exact du texte wiki soit pratiquement impossible. 		if mw.ustring.find(content, ';') then 			content = mw.ustring.gsub(content, "(\n:*);(:*[*#])", "%1:%2") 			content = mw.ustring.gsub(content, "(\n):*;", "%1<<§>>") 			content = mw.ustring.gsub(content, "(\n:*([*#][:;]*));", "%1<<§>>") 			content = mw.ustring.gsub(content, "<<§>>(.-)\n", "<dl><dt>%1</dt></dl>\n") 			content = mw.ustring.gsub(content, "<<§>>(.-)$", "<dl><dt>%1</dt></dl>") 		end 		local isUnorderedList = mw.ustring.find(content, '\n:* *%*') or mw.ustring.find(content, '^:* *%*') 		local isOrderedList = mw.ustring.find(content, '\n:* *%#') or mw.ustring.find(content, '^:* *%#') 		-- En raison des limitations actuelles, je ne traite que les descriptions contenant un seul type de listes. 		if (isUnorderedList and not isOrderedList) or (isOrderedList and not isUnorderedList) then 			--Je marque le début de chaque élément par un symbole potentiellement unique 			content = mw.ustring.gsub(content, "\n:? *[*#]", "\n<<§>>") 			content = mw.ustring.gsub(content, "<<§>>(.-)\n", "<li>%1</li>\n") 			content = mw.ustring.gsub(content, "<<§>>(.-)$", "<li>%1</li>") 			content = mw.ustring.gsub(content, "</li>\n*:*<li>", "</li><li>") 			if isUnorderedList then 				content = mw.ustring.gsub(content, "(<li>.-</li>\n)", "<ul>%1</ul>\n") 				content = mw.ustring.gsub(content, "(<li>.*</li>)$", "<ul>%1</ul>") 				content = mw.ustring.gsub(content, "</ul>\n:", "</ul>") 			elseif isOrderedList then 				content = mw.ustring.gsub(content, "(<li>.-</li>\n)", "<ol>%1</ol>\n") 				content = mw.ustring.gsub(content, "(<li>.*</li>)$", "<ol>%1</ol>") 				content = mw.ustring.gsub(content, "</ol>\n:", "</ol>") 			end 		end 		-- En raison des limitations actuelles, j'évite de développer des descriptions contenant à la fois des listes ordonnées et non ordonnées. 		if not (isUnorderedList and isOrderedList) then 			content = mw.ustring.gsub(content, "\n:+", "<br />") 			content = mw.ustring.gsub(content, "\n\n", "<br />") 			content = mw.ustring.gsub(content, "\n([^\n]+)", "%1") 		end 	end 	--ripristino gli stili rimossi 	for i=1, #styles do 		content = mw.ustring.gsub(content , '<<§@§>>', styles[i], 1) 	end 	return content end  local function _Listing(frame) 	local args = frame.args 	local output = '<bdi class="vcard">' 	local outputPriceForEnVoy = '' 	--Marker creation 	local MarkerArgs = { 		[marPar.counter] = args[par.counter] or args[par.mtype] or 'listing', 		[marPar.mtype] = args[par.mtype] or 'listing', 		[marPar.name] = args[par.name], 		[marPar.lat] = args[par.lat], 		[marPar.long] = args[par.long], 		[marPar.image] = args[par.image], 		[marPar.url] = args[par.url], 		[marPar.islisting] = i18n.msg.yes, 		[marPar.wikidata] = args[par.wikidata] 	} 	output = output .. Marker{ args = MarkerArgs }  	--Alt 	if _isDefined( args[par.alt] ) then 		output = output .. '&#32;(<span class="nickname listing-alt ' .. (require('Module:IsLatin').IsLatinValue(args[par.alt]) == 1 and 'IsLatin' or '') .. '">' .. args[par.alt] .. '</span>)' 	end  	--Indirizzo 	local anyPrevData = args[par.name] or args[par.url] 	if _isDefined( args[par.address] ) then 		output = output .. (anyPrevData and ',' or '') .. '&#32;<span class="adr listing-address street-address">' .. args[par.address] .. '</span>' 		anyPrevData = true 	end  	--indicazioni 	if _isDefined( args[par.directions] ) then 		output = output .. '&#32;(<span class="adr listing-directions">' .. args[par.directions] .. '</span>)' 		anyPrevData = true 	end  	--Telefono 	if _isDefined( args[par.tel] ) then 		output = output .. (anyPrevData and ',' or '') 			.. '&#32;<abbr title="' .. i18n.msg.phone .. '" class="listing-phone-symbol">' .. i18n.symbols.phone .. '</abbr> <span class="tel listing-phone">' 			.. LinkModule.LinkPhone{ args={args[par.tel]} } 			.. '</span>' 		anyPrevData = true 	end  	--Numero Verde 	if _isDefined( args[par.tollfree] ) then 		output = output .. (anyPrevData and ',' or '') 			.. '&#32;<abbr title="' .. i18n.msg.tollfree .. '" class="listing-tollfree-symbol">' .. i18n.symbols.tollfree .. '</abbr> <span class="tel listing-tollfree">' 			.. LinkModule.LinkTollfree{ args={args[par.tollfree]} } 			.. '</span>' 		anyPrevData = true 	end  	--Fax 	if _isDefined( args[par.fax] ) then 		output = output .. (anyPrevData and ',' or '') 			.. '&#32;<span class="tel"><span class="type">' .. i18n.msg.fax .. '</span>: <span class="value listing-fax">' 			.. LinkModule.LinkFax{ args={args[par.fax]} } 			.. '</span></span>' 		anyPrevData = true 	end  	--E-mail 	if _isDefined( args[par.email] ) then 		output = output .. (anyPrevData and ',&#32;' or '&#32;') .. _email(args[par.email]) 	end  	--Se sono state inserite informazioni nei campi precedenti, forzo un punto di fine periodo 	output = output .. (anyPrevData and '.' or '')  	--Prezzo 	if _isDefined( args[par.price] ) then 		local outputPrice = '&#32;' 			.. (_isDefined(i18n.msg.price) and _isDefined(i18n.icons.price) and ('<abbr title="' .. i18n.msg.price .. '">[[File:' .. i18n.icons.price .. '|13px|class=listing-price-icon|link=]]</abbr> ') or '') 			.. '<span class="note listing-price">' .. args[par.price] .. '</span>.' 		if i18n.wiki.home == 'enwikivoyage' then 			outputPriceForEnVoy = outputPrice 		else 			output = output .. outputPrice 		end 	end  	--Orari 	if _isDefined( args[par.hours] ) then 		output = output .. '&#32;' 			.. (_isDefined(i18n.msg.hours) and _isDefined(i18n.icons.hours) and ('<abbr title="' .. i18n.msg.hours .. '">[[File:' .. i18n.icons.hours .. '|15px|class=listing-hours-icon|link=]]</abbr> ') or '') 			.. '<span class="note listing-hours">' .. args[par.hours] .. '</span>.' 	end  	--Check-in e checkout 	if _isDefined( args[par.checkin] ) then 		output = output .. '&#32;<span class="note">' .. i18n.msg.checkin .. '<span class="listing-checkin">' .. args[par.checkin] .. '</span>' 		if _isDefined( args[par.checkout] ) then 			output = output .. i18n.msg.checkinNout .. '<span class="listing-checkout">' .. args[par.checkout] .. '</span>' 		end 		output = output .. '</span>.' 	else 		if _isDefined( args[par.checkout] ) then 			output = output .. '&#32;<span class="note">' .. i18n.msg.checkout .. '<span class="listing-checkout">' .. args[par.checkout] .. '</span></span>.' 		end 	end  	--Descrizione generale del listing in oggetto 	args[par.description] = _isDefined( args[par.description] ) or _isDefined( args[1] ) 	if args[par.description] then 		args[par.description] = _renderAdjustment( args[par.description] ) 		output = output .. '&#32;<bdi class="note listing-content">' .. args[par.description] .. '</bdi>' 	end 	-- eccezione per en:voy 	output = output .. outputPriceForEnVoy  	--Sister project icons 	local metadata = '' 	local wpLink = _isDefined( args[par.wikipedia] ) 	if _isDefined( args[par.wikidata] ) then 		--in presenza dell'istanza wikidata, recupero i dati relativi a Wikivoyage e Wikipedia 		local WIB_wikivoyage = _isDefined( mw.wikibase.getSitelink(args[par.wikidata], i18n.wiki.home) ) 		if WIB_wikivoyage then 			metadata = metadata .. '&#32;[[File:' .. i18n.icons.wikivoyage .. '|15px|class=listing-sister-wikivoyage|link=' .. WIB_wikivoyage .. '|' .. WIB_wikivoyage .. i18n.msg.wikivoyage .. ']]' 		end 		wpLink = wpLink or _isDefined( mw.wikibase.getSitelink(args[par.wikidata], i18n.wiki.wikipedia) ) 	end 	if _isDefined( args[par.wikipedia] ) ~= 'NA' and wpLink then 		metadata = metadata .. '&#32;[[File:' .. i18n.icons.wikipedia .. '|15px|class=listing-sister-wikipedia|link=w:' .. wpLink .. '|' .. wpLink .. i18n.msg.wikipedia .. ']]' 	end 	if _isDefined( args[par.wikidata] ) then 		metadata = metadata .. '&#32;[[File:' .. i18n.icons.wikidata .. '|16px|class=listing-sister-wikidata|link=d:' .. args[par.wikidata] 			..'|' .. (_sanitizeCaption(mw.wikibase.label(args[par.wikidata])) or '') .. ' ('.. args[par.wikidata] .. ')' .. i18n.msg.wikidata .. ']]' 	end 	if _isDefined( args[par.facebook] ) then 		metadata = metadata .. '&#32;[[File:' .. i18n.icons.facebook .. '|16px|class=listing-sister-facebook|link=' .. args[par.facebook] ..'|' .. i18n.msg.facebook .. ']]' 	end 	if _isDefined( args[par.twitter] ) then 		metadata = metadata .. '&#32;[[File:' .. i18n.icons.twitter .. '|16px|class=listing-sister-twitter|link=' .. args[par.twitter] ..'|' .. i18n.msg.twitter .. ']]' 	end 	if args[par.osm] ~= 'no' then 		metadata = metadata .. MapLink{ args = {lat=args[par.lat], long=args[par.long], wikidata=args[par.wikidata], map='osm'} } 	end 	metadata = metadata .. MapLink{ args = {lat=args[par.lat], long=args[par.long], wikidata=args[par.wikidata], map='apple'} } 	metadata = metadata .. MapLink{ args = {lat=args[par.lat], long=args[par.long], wikidata=args[par.wikidata], map='google'} } 	if _isDefined( metadata ) then 		metadata = '<span class="noprint listing-sister-icons">' .. metadata .. '</span>' 	end  	--Metadata - last edit date, "edit" link, etc 	local metadata2B, metadata2, metadata2E = '', '', '' 	if _isDefined( args[par.lastedit] ) then 		metadata2B = '(' 		metadata2 = '<span class="listing-lastedit">'  .. i18n.msg.lastedit .. mw.getContentLanguage():formatDate( 'M Y', args[par.lastedit] ) .. '</span>' 		metadata2E = ')' 	end 	metadata = metadata .. '<span class="listing-metadata">&nbsp;' .. metadata2B .. '<span class="listing-metadata-items">' .. metadata2 .. '</span>' .. metadata2E .. '</span>' 	local match = 0 	output, match = mw.ustring.gsub( output, "(</dt></dl></li></[uo]l></bdi>)$", metadata .. "%1") 	if match == 0 then 		output, match = mw.ustring.gsub( output, "(</..></[uod]l></bdi>)$", metadata .. "%1") 		if match == 0 then 			output = output .. metadata 		end 	end  	output = output .. '</bdi>'  	--Controllo la presenza di caratteri Unicode all'interno dei parametri forniti in input 	--A seguire controllo anche la presenza di parametri non gestiti da questo template 	output = output .. _hiddenUnicode( _concatIndexed( args ) ) .. _validateParams( args ) 	if args[par.wikipedia] and not args[par.wikidata] then 			--Non è considerato un errore se il parametro Wikipedia è usato per puntare a una specifica sezione 			output = output .. (mw.ustring.find(args[par.wikipedia], '#') == 0 and '[[Category:' .. i18n.categories.noWikidata .. ']]' or '') 	end      return _templateStyle( frame, 'Listing/styles.css' ) .. output end  local p = {}  function p.ListingTemplate(frame) 	return _Listing(frame:getParent()) end  function p.ListingInvoke(frame) 	return _Listing(frame) end  function p.ListingTemplateAndInvoke(frame)     -- faccio una copia di args del template genitore     local mergedArgs = {}     local parentFrame = frame and frame:getParent()     if parentFrame and parentFrame.args then         for k, v in pairs(parentFrame.args) do             mergedArgs[k] = v         end     end      -- sovrascrivo/aggiungo args passati direttamente all'#invoke     if frame and frame.args then 	    for k, v in pairs(frame.args) do 	        mergedArgs[k] = v 	    end     end  	-- creo un nuovo frame con la fusione dei due args     local title = frame:getTitle()     local mergedFrame = frame:newChild{ title = title, args = mergedArgs } 	return _Listing(mergedFrame) end  function p.ListingModule(frame) 	local Cframe = mw.getCurrentFrame() 	Cframe.args = frame.args 	return _Listing(Cframe) end  return p