Μετάβαση στο περιεχόμενο

Module:Citation/CS1

Μόνιμα προστατευμένο module
Από τη Βικιπαίδεια, την ελεύθερη εγκυκλοπαίδεια
Αυτή είναι μια παλιά έκδοση της σελίδας, όπως διαμορφώθηκε από τον Ιπποκράτης2020 (συζήτηση | συνεισφορές) στις 08:58, 30 Ιουλίου 2023 (Αναίρεση έκδοσης 10154862 από τον 185.80.221.236 (Συζήτηση)). Η τρέχουσα διεύθυνση (URL) είναι μόνιμος σύνδεσμος προς αυτή την έκδοση, που μπορεί να διαφέρει σημαντικά από την τρέχουσα έκδοση.

local function citation0( config, args)
	--[[ 
	Load Input Parameters
	The argument_wrapper facilitates the mapping of multiple aliases to single internal variable.
	]]
	local A = argument_wrapper( args );
	local i 

	-- Pick out the relevant fields from the arguments.  Different citation templates
	-- define different field names for the same underlying things.	

-- set default parameter values defined by |mode= parameter.
	local Mode = A['Mode'];
	if not is_valid_parameter_value (Mode, 'mode', cfg.keywords['mode']) then
		Mode = '';
	end

	local author_etal;
	local a	= {};																-- authors list from |lastn= / |firstn= pairs or |vauthors=
	local Authors;
	local NameListFormat = A['NameListFormat'];
	local Collaboration = A['Collaboration'];

	do																			-- to limit scope of selected
		local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'AuthorList');
		if 1 == selected then
			a, author_etal = extract_names (args, 'AuthorList');				-- fetch author list from |authorn= / |lastn= / |firstn=, |author-linkn=, and |author-maskn=
		elseif 2 == selected then
			NameListFormat = 'vanc';											-- override whatever |name-list-format= might be
			a, author_etal = parse_vauthors_veditors (args, args.vauthors, 'AuthorList');	-- fetch author list from |vauthors=, |author-linkn=, and |author-maskn=
		elseif 3 == selected then
			Authors = A['Authors'];												-- use content of |authors=
			if 'authors' == A:ORIGIN('Authors') then							-- but add a maint cat if the parameter is |authors=
				add_maint_cat ('authors');										-- because use of this parameter is discouraged; what to do about the aliases is a TODO:
			end
		end
		if is_set (Collaboration) then
			author_etal = true;													-- so that |display-authors=etal not required
		end
	end

	local Others = A['Others'];

	local editor_etal;
	local e	= {};																-- editors list from |editor-lastn= / |editor-firstn= pairs or |veditors=
	local Editors;

	do																			-- to limit scope of selected
		local selected = select_author_editor_source (A['Veditors'], A['Editors'], args, 'EditorList');
		if 1 == selected then
			e, editor_etal = extract_names (args, 'EditorList');				-- fetch editor list from |editorn= / |editor-lastn= / |editor-firstn=, |editor-linkn=, and |editor-maskn=
		elseif 2 == selected then
			NameListFormat = 'vanc';											-- override whatever |name-list-format= might be
			e, editor_etal = parse_vauthors_veditors (args, args.veditors, 'EditorList');	-- fetch editor list from |veditors=, |editor-linkn=, and |editor-maskn=
		elseif 3 == selected then
			Editors = A['Editors'];												-- use content of |editors=
			add_maint_cat ('editors');											-- but add a maint cat because use of this parameter is discouraged
		end
	end

	local t = {};																-- translators list from |translator-lastn= / translator-firstn= pairs
	local Translators;															-- assembled translators name list
	t = extract_names (args, 'TranslatorList');									-- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn=

	local interviewers_list = {};
	local Interviewers = A['Interviewers']
	if is_set (Interviewers) then												-- add a maint cat if the |interviewers= is used
		add_maint_cat ('interviewers');											-- because use of this parameter is discouraged
	else
		interviewers_list = extract_names (args, 'InterviewerList');			-- else, process preferred interviewers parameters
	end

	local c = {};																-- contributors list from |contributor-lastn= / contributor-firstn= pairs
	local Contributors;															-- assembled contributors name list
	local Contribution = A['Contribution'];
	if in_array(config.CitationClass, {"book","citation"}) and not is_set(A['Periodical']) then	-- |contributor= and |contribution= only supported in book cites
		c = extract_names (args, 'ContributorList');							-- fetch contributor list from |contributorn= / |contributor-lastn=, -firstn=, -linkn=, -maskn=
		
		if 0 < #c then
			if not is_set (Contribution) then									-- |contributor= requires |contribution=
				table.insert( z.message_tail, { set_error( 'contributor_missing_required_param', 'contribution')});	-- add missing contribution error message
				c = {};															-- blank the contributors' table; it is used as a flag later
			end
			if 0 == #a then														-- |contributor= requires |author=
				table.insert( z.message_tail, { set_error( 'contributor_missing_required_param', 'author')});	-- add missing author error message
				c = {};															-- blank the contributors' table; it is used as a flag later
			end
		end
	else																		-- if not a book cite
		if select_one (args, cfg.aliases['ContributorList-Last'], 'redundant_parameters', 1 ) then	-- are there contributor name list parameters?
			table.insert( z.message_tail, { set_error( 'contributor_ignored')});	-- add contributor ignored error message
		end
		Contribution = nil;														-- unset
	end

	if not is_valid_parameter_value (NameListFormat, 'name-list-format', cfg.keywords['name-list-format']) then			-- only accepted value for this parameter is 'vanc'
		NameListFormat = '';													-- anything else, set to empty string
	end

	local Year = A['Year'];
	local PublicationDate = A['PublicationDate'];
	local OrigYear = A['OrigYear'];
	local Date = A['Date'];
	local LayDate = A['LayDate'];
	------------------------------------------------- Get title data
	local Title = A['Title'];
	local ScriptTitle = A['ScriptTitle'];
	local BookTitle = A['BookTitle'];
	local Conference = A['Conference'];
	local TransTitle = A['TransTitle'];
	local TitleNote = A['TitleNote'];
	local TitleLink = A['TitleLink'];
			link_title_ok (TitleLink, A:ORIGIN ('TitleLink'), Title, 'title');	-- check for wikimarkup in |title-link= or wikimarkup in |title= when |title-link= is set

	local Chapter = A['Chapter'];
	local ScriptChapter = A['ScriptChapter'];
	local ChapterLink	-- = A['ChapterLink'];									-- deprecated as a parameter but still used internally by cite episode
	local TransChapter = A['TransChapter'];
	local TitleType = A['TitleType'];
	local Degree = A['Degree'];
	local Docket = A['Docket'];
	local ArchiveFormat = A['ArchiveFormat'];

	local ArchiveDate;
	local ArchiveURL;

	ArchiveURL, ArchiveDate = archive_url_check (A['ArchiveURL'], A['ArchiveDate'])
	
	local UrlStatus = A['UrlStatus'];
	if 'url-status' == A:ORIGIN ('UrlStatus') then								-- interim: TODO: this line goes away
		if not is_valid_parameter_value (UrlStatus, 'url-status', cfg.keywords ['url-status']) then	-- set in config.defaults to 'dead'
			UrlStatus = '';														-- anything else, set to empty string
		end																		-- interim: TODO: this line goes away
	elseif is_set (A:ORIGIN ('UrlStatus')) then									-- interim: while both |dead-url= and |url-status= allowed use separate keyword lists; TODO: remove this section
		if not is_valid_parameter_value (UrlStatus, A:ORIGIN ('UrlStatus'), cfg.keywords ['deadurl']) then	-- interim: assume |dead-url=; use those keywords; TODO: this line goes away
			UrlStatus = '';														-- anything else, set to empty string; interim: TODO: this line goes away
		end																		-- interim: TODO: this line goes away
	end																			-- interim: TODO: this line goes away
	
	if in_array (UrlStatus, {'yes', 'true', 'y'}) then							-- TODO: remove this whole thing when |dead-url=[yes|no] parameters removed from articles
		UrlStatus = 'dead';
	elseif 'no' == UrlStatus then
		UrlStatus = 'live';
	end
	
	local URL = A['URL']
	local URLorigin = A:ORIGIN('URL');											-- get name of parameter that holds URL
	local ChapterURL = A['ChapterURL'];
	local ChapterURLorigin = A:ORIGIN('ChapterURL');							-- get name of parameter that holds ChapterURL
	local ConferenceFormat = A['ConferenceFormat'];
	local ConferenceURL = A['ConferenceURL'];
	local ConferenceURLorigin = A:ORIGIN('ConferenceURL');						-- get name of parameter that holds ConferenceURL
	local Periodical = A['Periodical'];
	local Periodical_origin = A:ORIGIN('Periodical');							-- get the name of the periodical parameter

	local Series = A['Series'];
	
	local Volume;
	local Issue;
	local Page;
	local Pages;
	local At;

	if in_array (config.CitationClass, cfg.templates_using_volume) then
		Volume = A['Volume'];
	end
																				-- conference & map books do not support issue
	if in_array (config.CitationClass, cfg.templates_using_issue) and not (in_array (config.CitationClass, {'conference', 'map'}) and not is_set (Periodical))then
		Issue = A['Issue'];
	end
	local Position = '';
	if not in_array (config.CitationClass, cfg.templates_not_using_page) then
		Page = A['Page'];
		Pages = hyphen_to_dash( A['Pages'] );	
		At = A['At'];
	end

	local Edition = A['Edition'];
	local PublicationPlace = A['PublicationPlace']
	local Place = A['Place'];
	
	local PublisherName = A['PublisherName'];
	local RegistrationRequired = A['RegistrationRequired'];
		if not is_valid_parameter_value (RegistrationRequired, 'registration', cfg.keywords ['yes_true_y']) then
			RegistrationRequired=nil;
		end

	local SubscriptionRequired = A['SubscriptionRequired'];
		if not is_valid_parameter_value (SubscriptionRequired, 'subscription', cfg.keywords ['yes_true_y']) then
			SubscriptionRequired=nil;
		end

	local UrlAccess = A['UrlAccess'];
		if not is_valid_parameter_value (UrlAccess, 'url-access', cfg.keywords ['url-access']) then
			UrlAccess = nil;
		end
		if not is_set(URL) and is_set(UrlAccess) then
			UrlAccess = nil;
			table.insert( z.message_tail, { set_error( 'param_access_requires_param', {'url'}, true ) } );
		end

		if is_set (UrlAccess) and is_set (SubscriptionRequired) then			-- while not aliases, these are much the same so if both are set
			table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'url-access') .. ' and ' .. wrap_style ('parameter', 'subscription')}, true ) } );		-- add error message
			SubscriptionRequired = nil;											-- unset; prefer |access= over |subscription=
		end
		if is_set (UrlAccess) and is_set (RegistrationRequired) then			-- these are not the same but contradictory so if both are set
			table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'url-access') .. ' and ' .. wrap_style ('parameter', 'registration')}, true ) } );		-- add error message
			RegistrationRequired = nil;											-- unset; prefer |access= over |registration=
		end

	local ChapterUrlAccess = A['ChapterUrlAccess'];
		if not is_valid_parameter_value (ChapterUrlAccess, 'chapter-url-access', cfg.keywords ['url-access']) then	-- same as url-access
			ChapterUrlAccess = nil;
		end
		if not is_set(ChapterURL) and is_set(ChapterUrlAccess) then
			ChapterUrlAccess = nil;
			table.insert( z.message_tail, { set_error( 'param_access_requires_param', {'chapter-url'}, true ) } );
		end

	local Via = A['Via'];
	local AccessDate = A['AccessDate'];
	local Agency = A['Agency'];

	local Language = A['Language'];
	local Format = A['Format'];
	local ChapterFormat = A['ChapterFormat'];
	local DoiBroken = A['DoiBroken'];
	local ID = A['ID'];
	local ASINTLD = A['ASINTLD'];
	local IgnoreISBN = A['IgnoreISBN'];
		if not is_valid_parameter_value (IgnoreISBN, 'ignore-isbn-error', cfg.keywords ['yes_true_y']) then
			IgnoreISBN = nil;													-- anything else, set to empty string
		end
	local Embargo = A['Embargo'];
	local Class = A['Class'];													-- arxiv class identifier

	local ID_list = extract_ids( args );
	local ID_access_levels = extract_id_access_levels( args, ID_list );

	local Quote = A['Quote'];

	local LayFormat = A['LayFormat'];
	local LayURL = A['LayURL'];
	local LaySource = A['LaySource'];
	local Transcript = A['Transcript'];
	local TranscriptFormat = A['TranscriptFormat'];
	local TranscriptURL = A['TranscriptURL'] 
	local TranscriptURLorigin = A:ORIGIN('TranscriptURL');						-- get name of parameter that holds TranscriptURL

	local LastAuthorAmp = A['LastAuthorAmp'];
		if not is_valid_parameter_value (LastAuthorAmp, 'last-author-amp', cfg.keywords ['yes_true_y']) then
			LastAuthorAmp = nil;												-- set to empty string
		end

	local no_tracking_cats = A['NoTracking'];
		if not is_valid_parameter_value (no_tracking_cats, 'no-tracking', cfg.keywords ['yes_true_y']) then
			no_tracking_cats = nil;												-- set to empty string
		end

	--local variables that are not cs1 parameters
	local use_lowercase;														-- controls capitalization of certain static text
	local this_page = mw.title.getCurrentTitle();								-- also used for COinS and for language
	local anchor_year;															-- used in the CITEREF identifier
	local COinS_date = {};														-- holds date info extracted from |date= for the COinS metadata by Module:Date verification

	local DF = is_set(A['DF']) and A['DF'] or 'dmy-all';							-- date format set in cs1|2 template
	if not is_valid_parameter_value (DF, 'df', cfg.keywords['date-format']) then	-- validate reformatting keyword
		DF = '';																-- not valid, set to empty string
	end

	local sepc;											-- separator between citation elements for CS1 a period, for CS2, a comma
	local PostScript;
	local Ref;
	sepc, PostScript, Ref = set_style (Mode:lower(), A['PostScript'], A['Ref'], config.CitationClass);
	use_lowercase = ( sepc == ',' );					-- used to control capitalization for certain static text

	--check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories
	if not is_set (no_tracking_cats) then										-- ignore if we are already not going to categorize this page
		if in_array (this_page.nsText, cfg.uncategorized_namespaces) then
			no_tracking_cats = "true";											-- set no_tracking_cats
		end
		for _,v in ipairs (cfg.uncategorized_subpages) do						-- cycle through page name patterns
			if this_page.text:match (v) then									-- test page name against each pattern
				no_tracking_cats = "true";										-- set no_tracking_cats
				break;															-- bail out if one is found
			end
		end
	end

	-- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it)
	select_one( args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'redundant_parameters' );		-- this is a dummy call simply to get the error message and category

	local NoPP = A['NoPP'] 
	if is_set (NoPP) and is_valid_parameter_value (NoPP, 'nopp', cfg.keywords ['yes_true_y']) then
		NoPP = true;
	else
		NoPP = nil;																-- unset, used as a flag later
	end

	if is_set(Page) then
		if is_set(Pages) or is_set(At) then
			Pages = '';															-- unset the others
			At = '';
		end
		extra_text_in_page_check (Page);										-- add this page to maint cat if |page= value begins with what looks like p. or pp.
	elseif is_set(Pages) then
		if is_set(At) then
			At = '';															-- unset
		end
		extra_text_in_page_check (Pages);										-- add this page to maint cat if |pages= value begins with what looks like p. or pp.
	end	

	-- both |publication-place= and |place= (|location=) allowed if different
	if not is_set(PublicationPlace) and is_set(Place) then
		PublicationPlace = Place;							-- promote |place= (|location=) to |publication-place
	end
	
	if PublicationPlace == Place then Place = ''; end		-- don't need both if they are the same
	
	--[[
	Parameter remapping for cite encyclopedia:
	When the citation has these parameters:
		|encyclopedia and |title then map |title to |article and |encyclopedia to |title
		|encyclopedia and |article then map |encyclopedia to |title
		|encyclopedia then map |encyclopedia to |title
	
		|trans-title maps to |trans-chapter when |title is re-mapped
		|url maps to |chapterurl when |title is remapped
	
	All other combinations of |encyclopedia, |title, and |article are not modified
	
	]]

local Encyclopedia = A['Encyclopedia'];

	if ( config.CitationClass == "encyclopaedia" ) or ( config.CitationClass == "citation" and is_set (Encyclopedia)) then	-- test code for citation
		if is_set(Periodical) then												-- Periodical is set when |encyclopedia is set
			if is_set(Title) or is_set (ScriptTitle) then
				if not is_set(Chapter) then
					Chapter = Title;											-- |encyclopedia and |title are set so map |title to |article and |encyclopedia to |title
					ScriptChapter = ScriptTitle;
					TransChapter = TransTitle;
					ChapterURL = URL;
					ChapterUrlAccess = UrlAccess;

					if not is_set (ChapterURL) and is_set (TitleLink) then
						Chapter = make_wikilink (TitleLink, Chapter);
					end
					Title = Periodical;
					ChapterFormat = Format;
					Periodical = '';											-- redundant so unset
					TransTitle = '';
					URL = '';
					Format = '';
					TitleLink = '';
					ScriptTitle = '';
				end
			else																-- |title not set
				Title = Periodical;												-- |encyclopedia set and |article set or not set so map |encyclopedia to |title
				Periodical = '';												-- redundant so unset
			end
		end
	end

	-- Special case for cite techreport.
	if (config.CitationClass == "techreport") then								-- special case for cite techreport
		if is_set(A['Number']) then												-- cite techreport uses 'number', which other citations alias to 'issue'
			if not is_set(ID) then												-- can we use ID for the "number"?
				ID = A['Number'];												-- yes, use it
			else																-- ID has a value so emit error message
				table.insert( z.message_tail, { set_error('redundant_parameters', {wrap_style ('parameter', 'id') .. ' and ' .. wrap_style ('parameter', 'number')}, true )});
			end
		end	
	end

	-- special case for cite mailing list
	if (config.CitationClass == "mailinglist") then
		Periodical = A ['MailingList'];
	elseif 'mailinglist' == A:ORIGIN('Periodical') then
		Periodical = '';														-- unset because mailing list is only used for cite mailing list
	end

	-- Account for the oddity that is {{cite conference}}, before generation of COinS data.
	if 'conference' == config.CitationClass then
		if is_set(BookTitle) then
			Chapter = Title;
--			ChapterLink = TitleLink;											-- |chapterlink= is deprecated
			ChapterURL = URL;
			ChapterUrlAccess = UrlAccess;
			ChapterURLorigin = URLorigin;
			URLorigin = '';
			ChapterFormat = Format;
			TransChapter = TransTitle;
			Title = BookTitle;
			Format = '';
--			TitleLink = '';
			TransTitle = '';
			URL = '';
		end
	elseif 'speech' ~= config.CitationClass then
		Conference = '';														-- not cite conference or cite speech so make sure this is empty string
	end

	-- cite map oddities
	local Cartography = "";
	local Scale = "";
	local Sheet = A['Sheet'] or '';
	local Sheets = A['Sheets'] or '';
	if config.CitationClass == "map" then
		Chapter = A['Map'];
		ChapterURL = A['MapURL'];
		ChapterUrlAccess = UrlAccess;
		TransChapter = A['TransMap'];
		ChapterURLorigin = A:ORIGIN('MapURL');
		ChapterFormat = A['MapFormat'];
		
		Cartography = A['Cartography'];
		if is_set( Cartography ) then
			Cartography = sepc .. " " .. wrap_msg ('cartography', Cartography, use_lowercase);
		end		
		Scale = A['Scale'];
		if is_set( Scale ) then
			Scale = sepc .. " " .. Scale;
		end
	end

	-- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data.
	if 'episode' == config.CitationClass or 'serial' == config.CitationClass then
		local AirDate = A['AirDate'];
		local SeriesLink = A['SeriesLink'];

		link_title_ok (SeriesLink, A:ORIGIN ('SeriesLink'), Series, 'series');	-- check for wikimarkup in |series-link= or wikimarkup in |series= when |series-link= is set

		local Network = A['Network'];
		local Station = A['Station'];
		local s, n = {}, {};
																				-- do common parameters first
		if is_set(Network) then table.insert(n, Network); end
		if is_set(Station) then table.insert(n, Station); end
		ID = table.concat(n, sepc .. ' ');
		
		if not is_set (Date) and is_set (AirDate) then							-- promote airdate to date
			Date = AirDate;
		end

		if 'episode' == config.CitationClass then								-- handle the oddities that are strictly {{cite episode}}
			local Season = A['Season'];
			local SeriesNumber = A['SeriesNumber'];

			if is_set (Season) and is_set (SeriesNumber) then					-- these are mutually exclusive so if both are set
				table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'season') .. ' and ' .. wrap_style ('parameter', 'seriesno')}, true ) } );		-- add error message
				SeriesNumber = '';												-- unset; prefer |season= over |seriesno=
			end
																				-- assemble a table of parts concatenated later into Series
			if is_set(Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end
			if is_set(SeriesNumber) then table.insert(s, wrap_msg ('series', SeriesNumber, use_lowercase)); end
			if is_set(Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end
			Issue = '';															-- unset because this is not a unique parameter
	
			Chapter = Title;													-- promote title parameters to chapter
			ScriptChapter = ScriptTitle;
			ChapterLink = TitleLink;											-- alias episodelink
			TransChapter = TransTitle;
			ChapterURL = URL;
			ChapterUrlAccess = UrlAccess;
			ChapterURLorigin = A:ORIGIN('URL');
			
			Title = Series;														-- promote series to title
			TitleLink = SeriesLink;
			Series = table.concat(s, sepc .. ' ');								-- this is concatenation of season, seriesno, episode number

			if is_set (ChapterLink) and not is_set (ChapterURL) then			-- link but not URL
				Chapter = make_wikilink (ChapterLink, Chapter);
			elseif is_set (ChapterLink) and is_set (ChapterURL) then			-- if both are set, URL links episode;
				Series = make_wikilink (ChapterLink, Series);
			end
			URL = '';															-- unset
			TransTitle = '';
			ScriptTitle = '';
			
		else																	-- now oddities that are cite serial
			Issue = '';															-- unset because this parameter no longer supported by the citation/core version of cite serial
			Chapter = A['Episode'];												-- TODO: make |episode= available to cite episode someday?
			if is_set (Series) and is_set (SeriesLink) then
				Series = make_wikilink (SeriesLink, Series);
			end
			Series = wrap_style ('italic-title', Series);						-- series is italicized
		end	
	end
	-- end of {{cite episode}} stuff

	-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, before generation of COinS data.
	do
		if in_array (config.CitationClass, {'arxiv', 'biorxiv', 'citeseerx'}) then
			if not is_set (ID_list[config.CitationClass:upper()]) then			-- |arxiv= or |eprint= required for cite arxiv; |biorxiv= & |citeseerx= required for their templates
				table.insert( z.message_tail, { set_error( config.CitationClass .. '_missing', {}, true ) } );		-- add error message
			end
			
			if 'arxiv' == config.CitationClass then
				Periodical = 'arXiv';											-- set to arXiv for COinS; after that, must be set to empty string
			end

			if 'biorxiv' == config.CitationClass then
				Periodical = 'bioRxiv';											-- set to bioRxiv for COinS; after that, must be set to empty string
			end

			if 'citeseerx' == config.CitationClass then
				Periodical = 'CiteSeerX';										-- set to CiteSeerX for COinS; after that, must be set to empty string
			end
		end
	end

	-- handle type parameter for those CS1 citations that have default values
	if in_array(config.CitationClass, {"AV-media-notes", "interview", "mailinglist", "map", "podcast", "pressrelease", "report", "techreport", "thesis"}) then
		TitleType = set_titletype (config.CitationClass, TitleType);
		if is_set(Degree) and "Thesis" == TitleType then						-- special case for cite thesis
			TitleType = Degree .. ' ' .. cfg.title_types ['thesis']:lower();
		end
	end

	if is_set(TitleType) then													-- if type parameter is specified
		TitleType = substitute( cfg.messages['type'], TitleType);				-- display it in parentheses
	-- TODO: Hack on TitleType to fix bunched parentheses problem
	end

	-- legacy: promote PublicationDate to Date if neither Date nor Year are set.
 	local Date_origin;															-- to hold the name of parameter promoted to Date; required for date error messaging

	if not is_set (Date) then
		Date = Year;															-- promote Year to Date
		Year = nil;																-- make nil so Year as empty string isn't used for CITEREF
		if not is_set (Date) and is_set(PublicationDate) then					-- use PublicationDate when |date= and |year= are not set
			Date = PublicationDate;												-- promote PublicationDate to Date
			PublicationDate = '';												-- unset, no longer needed
			Date_origin = A:ORIGIN('PublicationDate');							-- save the name of the promoted parameter
		else
			Date_origin = A:ORIGIN('Year');										-- save the name of the promoted parameter
		end
	else
		Date_origin = A:ORIGIN('Date');											-- not a promotion; name required for error messaging
	end

	if PublicationDate == Date then PublicationDate = ''; end					-- if PublicationDate is same as Date, don't display in rendered citation

	--[[
	Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where
	we get the date used in the metadata.
	
	Date validation supporting code is in Module:Citation/CS1/Date_validation
	]]
	do	-- create defined block to contain local variables error_message, date_parameters_list, mismatch
		local error_message = '';
																				-- AirDate has been promoted to Date so not necessary to check it
--		local date_parameters_list = {['access-date']=AccessDate, ['archive-date']=ArchiveDate, ['date']=Date, ['doi-broken-date']=DoiBroken,
--				['embargo']=Embargo, ['lay-date']=LayDate, ['publication-date']=PublicationDate, ['year']=Year};

		local date_parameters_list = {
			['access-date'] = {val=AccessDate, name=A:ORIGIN ('AccessDate')},
			['archive-date'] = {val=ArchiveDate, name=A:ORIGIN ('ArchiveDate')},
			['date'] = {val=Date, name=Date_origin},
			['doi-broken-date'] = {val=DoiBroken, name=A:ORIGIN ('DoiBroken')},
			['embargo'] = {val=Embargo, name=A:ORIGIN ('Embargo')},
			['lay-date'] = {val=LayDate, name=A:ORIGIN ('LayDate')},
			['publication-date'] ={val=PublicationDate, name=A:ORIGIN ('PublicationDate')},
			['year'] = {val=Year, name=A:ORIGIN ('Year')},
			};
		anchor_year, Embargo, error_message = dates(date_parameters_list, COinS_date);

-- start temporary Julian / Gregorian calendar uncertainty categorization
		if COinS_date.inter_cal_cat then
			add_prop_cat ('jul_greg_uncertainty');
		end
-- end temporary Julian / Gregorian calendar uncertainty categorization

		if is_set (Year) and is_set (Date) then									-- both |date= and |year= not normally needed; 
			local mismatch = year_date_check (Year, Date)
			if 0 == mismatch then												-- |year= does not match a year-value in |date=
				if is_set (error_message) then									-- if there is already an error message
					error_message = error_message .. ', ';						-- tack on this additional message
				end
				error_message = error_message .. '&#124;year= / &#124;date= mismatch';
			elseif 1 == mismatch then											-- |year= matches year-value in |date=
				add_maint_cat ('date_year');
			end
		end
		
		if not is_set(error_message) then										-- error free dates only
			local modified = false;												-- flag
			
			if is_set (DF) then													-- if we need to reformat dates
				modified = reformat_dates (date_parameters_list, DF, false);	-- reformat to DF format, use long month names if appropriate
			end

			if true == date_hyphen_to_dash (date_parameters_list) then			-- convert hyphens to dashes where appropriate
				modified = true;
				add_maint_cat ('date_format');									-- hyphens were converted so add maint category
			end
			
	-- for those wikis that can and want to have English date names translated to the local language,
	-- uncomment these three lines.  Not supported by en.wiki (for obvious reasons)
	-- set date_name_xlate() second argument to true to translate English digits to local digits (will translate ymd dates)
			if date_name_xlate (date_parameters_list, false) then
				modified = true;
			end

			if modified then													-- if the date_parameters_list values were modified
				AccessDate = date_parameters_list['access-date'].val;			-- overwrite date holding parameters with modified values
				ArchiveDate = date_parameters_list['archive-date'].val;
				Date = date_parameters_list['date'].val;
				DoiBroken = date_parameters_list['doi-broken-date'].val;
				LayDate = date_parameters_list['lay-date'].val;
				PublicationDate = date_parameters_list['publication-date'].val;
			end
		else
			table.insert( z.message_tail, { set_error( 'bad_date', {error_message}, true ) } );	-- add this error message
		end
	end	-- end of do

	-- Account for the oddity that is {{cite journal}} with |pmc= set and |url= not set.  Do this after date check but before COInS.
	-- Here we unset Embargo if PMC not embargoed (|embargo= not set in the citation) or if the embargo time has expired. Otherwise, holds embargo date
	Embargo = is_embargoed (Embargo);

	if config.CitationClass == "journal" and not is_set(URL) and is_set(ID_list['PMC']) then
		if not is_set (Embargo) then											-- if not embargoed or embargo has expired
			URL=cfg.id_handlers['PMC'].prefix .. ID_list['PMC'];				-- set url to be the same as the PMC external link if not embargoed
			URLorigin = cfg.id_handlers['PMC'].parameters[1];					-- set URLorigin to parameter name for use in error message if citation is missing a |title=
			if is_set(AccessDate) then											-- access date requires |url=; pmc created url is not |url=
				table.insert( z.message_tail, { set_error( 'accessdate_missing_url', {}, true ) } );
				AccessDate = '';												-- unset
			end

		end
	end

	-- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.
	-- Test if citation has no title
	if	not is_set(Title) and
		not is_set(TransTitle) and
		not is_set(ScriptTitle) then
			if 'episode' == config.CitationClass then							-- special case for cite episode; TODO: is there a better way to do this?
				table.insert( z.message_tail, { set_error( 'citation_missing_title', {'series'}, true ) } );
			else
				table.insert( z.message_tail, { set_error( 'citation_missing_title', {'title'}, true ) } );
			end
	end
	
	if 'none' == Title and in_array (config.CitationClass, {'journal', 'citation'}) and is_set (Periodical) and 'journal' == A:ORIGIN('Periodical') then	-- special case for journal cites
		Title = '';																-- set title to empty string
		add_maint_cat ('untitled');
	end

	check_for_url ({															-- add error message when any of these parameters contains a URL
		['title']=Title,
		[A:ORIGIN('Chapter')]=Chapter,
		[A:ORIGIN('Periodical')]=Periodical,
		[A:ORIGIN('PublisherName')] = PublisherName
		});

	-- COinS metadata (see <http://ocoins.info/>) for automated parsing of citation information.
	-- handle the oddity that is cite encyclopedia and {{citation |encyclopedia=something}}. Here we presume that
	-- when Periodical, Title, and Chapter are all set, then Periodical is the book (encyclopedia) title, Title
	-- is the article title, and Chapter is a section within the article.  So, we remap 
	
	local coins_chapter = Chapter;												-- default assuming that remapping not required
	local coins_title = Title;													-- et tu
	if 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and is_set (Encyclopedia)) then
		if is_set (Chapter) and is_set (Title) and is_set (Periodical) then		-- if all are used then
			coins_chapter = Title;												-- remap
			coins_title = Periodical;
		end
	end
	local coins_author = a;														-- default for coins rft.au 
	if 0 < #c then																-- but if contributor list
		coins_author = c;														-- use that instead
	end

	-- this is the function call to COinS()
	local OCinSoutput = COinS({
		['Periodical'] = Periodical,
		['Encyclopedia'] = Encyclopedia,
		['Chapter'] = make_coins_title (coins_chapter, ScriptChapter),			-- Chapter and ScriptChapter stripped of bold / italic wikimarkup
		['Degree'] = Degree;													-- cite thesis only
		['Title'] = make_coins_title (coins_title, ScriptTitle),				-- Title and ScriptTitle stripped of bold / italic wikimarkup
		['PublicationPlace'] = PublicationPlace,
		['Date'] = COinS_date.rftdate,											-- COinS_date has correctly formatted date if Date is valid;
		['Season'] = COinS_date.rftssn,
		['Chron'] =  COinS_date.rftchron or (not COinS_date.rftdate and Date) or '',	-- chron but if not set and invalid date format use Date; keep this last bit?
		['Series'] = Series,
		['Volume'] = Volume,
		['Issue'] = Issue,
		['Pages'] = get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At}, 5)),				-- pages stripped of external links
		['Edition'] = Edition,
		['PublisherName'] = PublisherName,
		['URL'] = first_set ({ChapterURL, URL}, 2),
		['Authors'] = coins_author,
		['ID_list'] = ID_list,
		['RawPage'] = this_page.prefixedText,
	}, config.CitationClass);

	-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, and {{cite citeseerx}} AFTER generation of COinS data.
	if in_array (config.CitationClass, {'arxiv', 'biorxiv', 'citeseerx'}) then	-- we have set rft.jtitle in COinS to arXiv, bioRxiv, or CiteSeerX now unset so it isn't displayed
		Periodical = '';														-- periodical not allowed in these templates; if article has been published, use cite journal
	end

	-- special case for cite newsgroup.  Do this after COinS because we are modifying Publishername to include some static text
	if 'newsgroup' == config.CitationClass then
		if is_set (PublisherName) then
			PublisherName = substitute (cfg.messages['newsgroup'], external_link( 'news:' .. PublisherName, PublisherName, A:ORIGIN('PublisherName'), nil ));
		end
	end



	-- Now perform various field substitutions.
	-- We also add leading spaces and surrounding markup and punctuation to the
	-- various parts of the citation, but only when they are non-nil.
	local EditorCount;															-- used only for choosing {ed.) or (eds.) annotation at end of editor name-list
	do
		local last_first_list;
		local control = { 
			format = NameListFormat,											-- empty string or 'vanc'
			maximum = nil,														-- as if display-authors or display-editors not set
			lastauthoramp = LastAuthorAmp,
			page_name = this_page.text,											-- get current page name so that we don't wikilink to it via editorlinkn
			mode = Mode
		};

		do																		-- do editor name list first because the now unsupported coauthors used to modify control table
			control.maximum , editor_etal = get_display_authors_editors (A['DisplayEditors'], #e, 'editors', editor_etal);
			last_first_list, EditorCount = list_people(control, e, editor_etal);

			if is_set (Editors) then
				if editor_etal then
					Editors = Editors .. ' ' .. cfg.messages['et al'];			-- add et al. to editors parameter beause |display-editors=etal
					EditorCount = 2;											-- with et al., |editors= is multiple names; spoof to display (eds.) annotation
				else
					EditorCount = 2;											-- we don't know but assume |editors= is multiple names; spoof to display (eds.) annotation
				end
			else
				Editors = last_first_list;										-- either an author name list or an empty string
			end

			if 1 == EditorCount and (true == editor_etal or 1 < #e) then		-- only one editor displayed but includes etal then 
				EditorCount = 2;												-- spoof to display (eds.) annotation
			end
		end
		do																		-- now do interviewers
			control.maximum = #interviewers_list;							-- number of interviewerss
			Interviewers = list_people(control, interviewers_list, false);	-- et al not currently supported
		end
		do																		-- now do translators
			control.maximum = #t;												-- number of translators
			Translators = list_people(control, t, false);						-- et al not currently supported
		end
		do																		-- now do contributors
			control.maximum = #c;												-- number of contributors
			Contributors = list_people(control, c, false);						-- et al not currently supported
		end
		do																		-- now do authors
			control.maximum , author_etal = get_display_authors_editors (A['DisplayAuthors'], #a, 'authors', author_etal);

			last_first_list = list_people(control, a, author_etal);

			if is_set (Authors) then
				Authors, author_etal = name_has_etal (Authors, author_etal, false);	-- find and remove variations on et al.
				if author_etal then
					Authors = Authors .. ' ' .. cfg.messages['et al'];			-- add et al. to authors parameter
				end
			else
				Authors = last_first_list;										-- either an author name list or an empty string
			end
		end																		-- end of do
	
		if is_set (Authors) and is_set (Collaboration) then
			Authors = Authors .. ' (' .. Collaboration .. ')';					-- add collaboration after et al.
		end

	end

	-- apply |[xx-]format= styling; at the end, these parameters hold correctly styled format annotation,
	-- an error message if the associated url is not set, or an empty string for concatenation
	ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url');
	ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url');
	Format = style_format (Format, URL, 'format', 'url');
	LayFormat = style_format (LayFormat, LayURL, 'lay-format', 'lay-url');
	TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl');

	-- special case for chapter format so no error message or cat when chapter not supported
	if not (in_array(config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx'}) or
		('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia))) then
			ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url');
	end

	if not is_set(URL) then
		if in_array(config.CitationClass, {"web","podcast", "mailinglist"}) then	-- |url= required for cite web, cite podcast, and cite mailinglist
			table.insert( z.message_tail, { set_error( 'cite_web_url', {}, true ) } );
		end
		
		-- do we have |accessdate= without either |url= or |chapter-url=?
		if is_set(AccessDate) and not is_set(ChapterURL)then					-- ChapterURL may be set when URL is not set;
			table.insert( z.message_tail, { set_error( 'accessdate_missing_url', {}, true ) } );
			AccessDate = '';
		end
	end

	local OriginalURL, OriginalURLorigin, OriginalFormat, OriginalAccess;
--	DeadURL = DeadURL:lower();													-- used later when assembling archived text
	UrlStatus = UrlStatus:lower();												-- used later when assembling archived text
	if is_set( ArchiveURL ) then
		if is_set (ChapterURL) then 											-- if chapter-url is set apply archive url to it
			OriginalURL = ChapterURL;											-- save copy of source chapter's url for archive text
			OriginalURLorigin = ChapterURLorigin;								-- name of chapter-url parameter for error messages
			OriginalFormat = ChapterFormat;										-- and original |chapter-format=
--			if 'no' ~= DeadURL then
			if 'live' ~= UrlStatus then
				ChapterURL = ArchiveURL											-- swap-in the archive's url
				ChapterURLorigin = A:ORIGIN('ArchiveURL')						-- name of archive-url parameter for error messages
				ChapterFormat = ArchiveFormat or '';							-- swap in archive's format
				ChapterUrlAccess = nil;											-- restricted access levels do not make sense for archived urls
			end
		elseif is_set (URL) then
			OriginalURL = URL;													-- save copy of original source URL
			OriginalURLorigin = URLorigin;										-- name of url parameter for error messages
			OriginalFormat = Format; 											-- and original |format=
			OriginalAccess = UrlAccess;
--			if 'no' ~= DeadURL then												-- if URL set then archive-url applies to it
			if 'live' ~= UrlStatus then											-- if URL set then archive-url applies to it
				URL = ArchiveURL												-- swap-in the archive's url
				URLorigin = A:ORIGIN('ArchiveURL')								-- name of archive url parameter for error messages
				Format = ArchiveFormat or '';									-- swap in archive's format
				UrlAccess = nil;												-- restricted access levels do not make sense for archived urls
			end
 		end
	end

	if in_array(config.CitationClass, {'web','news','journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx'}) or	-- if any of the 'periodical' cites except encyclopedia
		('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia)) then
			local chap_param;
			if is_set (Chapter) then											-- get a parameter name from one of these chapter related meta-parameters
				chap_param = A:ORIGIN ('Chapter')
			elseif is_set (TransChapter) then
				chap_param = A:ORIGIN ('TransChapter')
			elseif is_set (ChapterURL) then
				chap_param = A:ORIGIN ('ChapterURL')
			elseif is_set (ScriptChapter) then
				chap_param = A:ORIGIN ('ScriptChapter')
			else is_set (ChapterFormat)
				chap_param = A:ORIGIN ('ChapterFormat')
			end

			if is_set (chap_param) then											-- if we found one
				table.insert( z.message_tail, { set_error( 'chapter_ignored', {chap_param}, true ) } );		-- add error message
				Chapter = '';													-- and set them to empty string to be safe with concatenation
				TransChapter = '';
				ChapterURL = '';
				ScriptChapter = '';
				ChapterFormat = '';
			end
	else																		-- otherwise, format chapter / article title
		local no_quotes = false;												-- default assume that we will be quoting the chapter parameter value
		if is_set (Contribution) and 0 < #c then								-- if this is a contribution with contributor(s)
			if in_array (Contribution:lower(), cfg.keywords.contribution) then	-- and a generic contribution title
				no_quotes = true;												-- then render it unquoted
			end
		end

		Chapter = format_chapter_title (ScriptChapter, Chapter, TransChapter, ChapterURL, ChapterURLorigin, no_quotes, ChapterUrlAccess);		-- Contribution is also in Chapter
		if is_set (Chapter) then
			Chapter = Chapter .. ChapterFormat ;
			if 'map' == config.CitationClass and is_set (TitleType) then
				Chapter = Chapter .. ' ' .. TitleType;							-- map annotation here; not after title
			end
			Chapter = Chapter.. sepc .. ' ';
		elseif is_set (ChapterFormat) then										-- |chapter= not set but |chapter-format= is so ...
			Chapter = ChapterFormat .. sepc .. ' ';								-- ... ChapterFormat has error message, we want to see it
		end
	end

	-- Format main title.
	if '...' == Title:sub (-3) then												-- if elipsis is the last three characters of |title=
		Title = mw.ustring.gsub (Title, '(%.%.%.)%.+$', '%1');					-- limit the number of dots to three
	elseif not mw.ustring.find (Title, '%.%s*%a%.') then						-- end of title is not a 'dot-(optional space-)letter-dot' initialism
		Title = mw.ustring.gsub(Title, '%'..sepc..'$', '');						-- remove any trailing separator character
	end
	if is_set(TitleLink) and is_set(Title) then
		Title = make_wikilink (TitleLink, Title);
	end

	if in_array(config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx'}) or
		('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia)) or
		('map' == config.CitationClass and is_set (Periodical)) then			-- special case for cite map when the map is in a periodical treat as an article
			Title = kern_quotes (Title);										-- if necessary, separate title's leading and trailing quote marks from Module provided quote marks
			Title = wrap_style ('quoted-title', Title);
			Title = script_concatenate (Title, ScriptTitle);					-- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped
			TransTitle= wrap_style ('trans-quoted-title', TransTitle );
	elseif 'report' == config.CitationClass then								-- no styling for cite report
		Title = script_concatenate (Title, ScriptTitle);						-- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped
		TransTitle= wrap_style ('trans-quoted-title', TransTitle );				-- for cite report, use this form for trans-title
	else
		Title = wrap_style ('italic-title', Title);
		Title = script_concatenate (Title, ScriptTitle);						-- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped
		TransTitle = wrap_style ('trans-italic-title', TransTitle);
	end

	local TransError = "";
	if is_set(TransTitle) then
		if is_set(Title) then
			TransTitle = " " .. TransTitle;
		else
			TransError = " " .. set_error( 'trans_missing_title', {'title'} );
		end
	end
	
	if is_set(Title) then
		if not is_set(TitleLink) and is_set(URL) then
			
			Title = external_link( URL, Title, URLorigin, UrlAccess ) .. TransTitle .. TransError .. Format;
			URL = '';															-- unset these because no longer needed
			Format = "";
		else
			Title = Title .. TransTitle .. TransError;
		end
	else
		Title = TransTitle .. TransError;
	end

	if is_set(Place) then
		Place = " " .. wrap_msg ('written', Place, use_lowercase) .. sepc .. " ";
	end

	if is_set (Conference) then
		if is_set (ConferenceURL) then
			Conference = external_link( ConferenceURL, Conference, ConferenceURLorigin, nil );
		end
		Conference = sepc .. " " .. Conference .. ConferenceFormat;
	elseif is_set(ConferenceURL) then
		Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURLorigin, nil );
	end

	if not is_set(Position) then
		local Minutes = A['Minutes'];
		local Time = A['Time'];

		if is_set(Minutes) then
			if is_set (Time) then
				table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'minutes') .. ' and ' .. wrap_style ('parameter', 'time')}, true ) } );
			end
			Position = " " .. Minutes .. " " .. cfg.messages['minutes'];
		else
			if is_set(Time) then
				local TimeCaption = A['TimeCaption']
				if not is_set(TimeCaption) then
					TimeCaption = cfg.messages['event'];
					if sepc ~= '.' then
						TimeCaption = TimeCaption:lower();
					end
				end
				Position = " " .. TimeCaption .. " " .. Time;
			end
		end
	else
		Position = " " .. Position;
		At = '';
	end

	Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase);

	At = is_set(At) and (sepc .. " " .. At) or "";
	Position = is_set(Position) and (sepc .. " " .. Position) or "";
	if config.CitationClass == 'map' then
		local Section = A['Section'];
		local Sections = A['Sections'];
		local Inset = A['Inset'];
		
		if is_set( Inset ) then
			Inset = sepc .. " " .. wrap_msg ('inset', Inset, use_lowercase);
		end			

		if is_set( Sections ) then
			Section = sepc .. " " .. wrap_msg ('sections', Sections, use_lowercase);
		elseif is_set( Section ) then
			Section = sepc .. " " .. wrap_msg ('section', Section, use_lowercase);
		end
		At = At .. Inset .. Section;		
	end	

	if is_set (Language) then
		Language = language_parameter (Language);								-- format, categories, name from ISO639-1, etc
	else
		Language="";															-- language not specified so make sure this is an empty string;
	--[[ TODO: need to extract the wrap_msg from language_parameter
	so that we can solve parentheses bunching problem with Format/Language/TitleType
	]]
	end

	Others = is_set(Others) and (sepc .. " " .. Others) or "";
	
	if is_set (Translators) then
		Others = safe_join ({sepc .. ' ', wrap_msg ('translated', Translators, use_lowercase), Others}, sepc);
	end
	if is_set (Interviewers) then
		Others = safe_join ({sepc .. ' ', wrap_msg ('interview', Interviewers, use_lowercase), Others}, sepc);
	end
	
	TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";
	if is_set (Edition) then
		if Edition:match ('%f[%a][Ee]d%.?$') or Edition:match ('%f[%a][Ee]dition$') then
			add_maint_cat ('extra_text', 'edition');
		end
		Edition = " " .. wrap_msg ('edition', Edition);
	else
		Edition = '';
	end

	Series = is_set(Series) and (sepc .. " " .. Series) or "";
		OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or "";		-- TODO: presentation

	Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";

	Volume = format_volume_issue (Volume, Issue, config.CitationClass, Periodical_origin, sepc, use_lowercase);

	------------------------------------ totally unrelated data
	if is_set(Via) then
		Via = " " .. wrap_msg ('via', Via);
	end

	--[[
	Subscription implies paywall; Registration does not.  If both are used in a citation, the subscription required link
	note is displayed. There are no error messages for this condition.
	
	]]
	if is_set (SubscriptionRequired) then
		SubscriptionRequired = sepc .. " " .. cfg.messages['subscription'];		-- subscription required message
	elseif is_set (RegistrationRequired) then
		SubscriptionRequired = sepc .. " " .. cfg.messages['registration'];		-- registration required message
	else
		SubscriptionRequired = '';												-- either or both might be set to something other than yes true y
	end

	if is_set(AccessDate) then
		local retrv_text = " " .. cfg.messages['retrieved']

		AccessDate = nowrap_date (AccessDate);									-- wrap in nowrap span if date in appropriate format
		if (sepc ~= ".") then retrv_text = retrv_text:lower() end				-- if mode is cs2, lower case
		AccessDate = substitute (retrv_text, AccessDate);						-- add retrieved text

		AccessDate = substitute (cfg.presentation['accessdate'], {sepc, AccessDate});	-- allow editors to hide accessdates
	end
	
	if is_set(ID) then ID = sepc .." ".. ID; end
   	if "thesis" == config.CitationClass and is_set(Docket) then
		ID = sepc .." Docket ".. Docket .. ID;
	end
   	if "report" == config.CitationClass and is_set(Docket) then					-- for cite report when |docket= is set
		ID = sepc .. ' ' .. Docket;												-- overwrite ID even if |id= is set
	end

	ID_list = build_id_list( ID_list, {IdAccessLevels=ID_access_levels, DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo, Class = Class} );

	if is_set(URL) then
		URL = " " .. external_link( URL, nil, URLorigin, UrlAccess );
	end

	if is_set(Quote) then
		if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then				-- if first and last characters of quote are quote marks
			Quote = Quote:sub(2,-2);											-- strip them off
		end
		Quote = sepc .." " .. wrap_style ('quoted-text', Quote ); 				-- wrap in <q>...</q> tags
		PostScript = "";														-- cs1|2 does not supply terminal punctuation when |quote= is set
	end
	
	local Archived
	if is_set(ArchiveURL) then
		if not is_set(ArchiveDate) then
			ArchiveDate = set_error('archive_missing_date');
		end
		if "live" == UrlStatus then
			local arch_text = cfg.messages['archived'];
			if sepc ~= "." then arch_text = arch_text:lower() end
			Archived = sepc .. " " .. substitute( cfg.messages['archived-live'],
				{ external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL'), nil ) .. ArchiveFormat, ArchiveDate } );
			if not is_set(OriginalURL) then
				Archived = Archived .. " " .. set_error('archive_missing_url');							   
			end
		elseif is_set(OriginalURL) then											-- UrlStatus is empty, 'dead', 'unfit', 'usurped', 'bot: unknown'
			local arch_text = cfg.messages['archived-dead'];
			if sepc ~= "." then arch_text = arch_text:lower() end
			if in_array (UrlStatus, {'unfit', 'usurped', 'bot: unknown'}) then
				Archived = sepc .. " " .. 'Αρχειοθετήθηκε από το πρωτότυπο στις ' .. ArchiveDate;	-- format already styled
				if 'bot: unknown' == UrlStatus then
					add_maint_cat ('bot:_unknown');								-- and add a category if not already added
				else
					add_maint_cat ('unfit');									-- and add a category if not already added
				end
			else																-- UrlStatus is empty, 'dead'
				Archived = sepc .. " " .. substitute( arch_text,
					{ external_link( OriginalURL, cfg.messages['original'], OriginalURLorigin, OriginalAccess ) .. OriginalFormat, ArchiveDate } );	-- format already styled
			end	
		else
			local arch_text = cfg.messages['archived-missing'];
			if sepc ~= "." then arch_text = arch_text:lower() end
			Archived = sepc .. " " .. substitute( arch_text, 
				{ set_error('archive_missing_url'), ArchiveDate } );
		end
	elseif is_set (ArchiveFormat) then
		Archived = ArchiveFormat;												-- if set and ArchiveURL not set ArchiveFormat has error message
	else
		Archived = ""
	end
	
	local Lay = '';
	if is_set(LayURL) then
		if is_set(LayDate) then LayDate = " (" .. LayDate .. ")" end
		if is_set(LaySource) then 
			LaySource = " &ndash; ''" .. safe_for_italics(LaySource) .. "''";
		else
			LaySource = "";
		end
		if sepc == '.' then
			Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary'], A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate
		else
			Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower(), A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate
		end			
	elseif is_set (LayFormat) then												-- Test if |lay-format= is given without giving a |lay-url=
		Lay = sepc .. LayFormat;												-- if set and LayURL not set, then LayFormat has error message
	end

	if is_set(Transcript) then
		if is_set(TranscriptURL) then
			Transcript = external_link( TranscriptURL, Transcript, TranscriptURLorigin, nil );
		end
		Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat;
	elseif is_set(TranscriptURL) then
		Transcript = external_link( TranscriptURL, nil, TranscriptURLorigin, nil );
	end

	local Publisher;
	if is_set(PublicationDate) then
		PublicationDate = wrap_msg ('published', PublicationDate);
	end
	if is_set(PublisherName) then
		if is_set(PublicationPlace) then
			Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
		else
			Publisher = sepc .. " " .. PublisherName .. PublicationDate;  
		end			
	elseif is_set(PublicationPlace) then 
		Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
	else 
		Publisher = PublicationDate;
	end
	
	-- Several of the above rely upon detecting this as nil, so do it last.
	if is_set(Periodical) then
		if is_set(Title) or is_set(TitleNote) then 
			Periodical = sepc .. " " .. wrap_style ('italic-title', Periodical) 
		else 
			Periodical = wrap_style ('italic-title', Periodical)
		end
	end

	--[[
	Handle the oddity that is cite speech.  This code overrides whatever may be the value assigned to TitleNote (through |department=) and forces it to be " (Speech)" so that
	the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided).
	]]
	if "speech" == config.CitationClass then				-- cite speech only
		TitleNote = " (Speech)";							-- annotate the citation
		if is_set (Periodical) then							-- if Periodical, perhaps because of an included |website= or |journal= parameter 
			if is_set (Conference) then						-- and if |event= is set
				Conference = Conference .. sepc .. " ";		-- then add appropriate punctuation to the end of the Conference variable before rendering
			end
		end
	end

	-- Piece all bits together at last.  Here, all should be non-nil.
	-- We build things this way because it is more efficient in LUA
	-- not to keep reassigning to the same string variable over and over.

	local tcommon;
	local tcommon2;																-- used for book cite when |contributor= is set
	
	if in_array(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then
		if is_set(Others) then Others = safe_join ({Others, sepc .. " "}, sepc) end		-- add terminal punctuation & space; check for dup sepc; TODO why do we need to do this here?
		tcommon = safe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Edition, Publisher, Agency, Volume}, sepc );
	elseif in_array(config.CitationClass, {"book","citation"}) and not is_set(Periodical) then		-- special cases for book cites
		if is_set (Contributors) then											-- when we are citing foreword, preface, introduction, etc
			tcommon = safe_join( {Title, TitleNote}, sepc );					-- author and other stuff will come after this and before tcommon2
			tcommon2 = safe_join( {Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc );
		else
			tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc );
		end

	elseif 'map' == config.CitationClass then									-- special cases for cite map
		if is_set (Chapter) then												-- map in a book; TitleType is part of Chapter
			tcommon = safe_join( {Title, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc );
		elseif is_set (Periodical) then											-- map in a periodical
			tcommon = safe_join( {Title, TitleType, Format, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc );
		else																	-- a sheet or stand-alone map
			tcommon = safe_join( {Title, TitleType, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher}, sepc );
		end
		
	elseif 'episode' == config.CitationClass then								-- special case for cite episode
		tcommon = safe_join( {Title, TitleNote, TitleType, Series, Transcript, Language, Edition, Publisher}, sepc );

	else																		-- all other CS1 templates
		tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, 
			Volume, Others, Edition, Publisher, Agency}, sepc );
	end
	
	if #ID_list > 0 then
		ID_list = safe_join( { sepc .. " ",  table.concat( ID_list, sepc .. " " ), ID }, sepc );
	else
		ID_list = ID;
	end
	
	local idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc );
	local text;
	local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At;

	if is_set(Date) then
		if is_set (Authors) or is_set (Editors) then							-- date follows authors or editors when authors not set
			Date = " (" .. Date ..")" .. OrigYear .. sepc .. " ";				-- in paranetheses
		else																	-- neither of authors and editors set
			if (string.sub(tcommon,-1,-1) == sepc) then							-- if the last character of tcommon is sepc
				Date = " " .. Date .. OrigYear;									-- Date does not begin with sepc
			else
				Date = sepc .. " " .. Date .. OrigYear;							-- Date begins with sepc
			end
		end
	end	
	if is_set(Authors) then
		if (not is_set (Date)) then												-- when date is set it's in parentheses; no Authors termination
			Authors = terminate_name_list (Authors, sepc);						-- when no date, terminate with 0 or 1 sepc and a space
		end
		if is_set(Editors) then
			local in_text = " ";
			local post_text = "";
			if is_set(Chapter) and 0 == #c then
				in_text = in_text .. cfg.messages['in'] .. " "
				if (sepc ~= '.') then
					in_text = in_text:lower()									-- lowercase for cs2
				 end
			else
				if EditorCount <= 1 then
					post_text = ", " .. cfg.messages['editor'];
				else
					post_text = ", " .. cfg.messages['editors'];
				end
			end 
			Editors = terminate_name_list (in_text .. Editors .. post_text, sepc);	-- terminate with 0 or 1 sepc and a space
		end
		if is_set (Contributors) then											-- book cite and we're citing the intro, preface, etc
			local by_text = sepc .. ' ' .. cfg.messages['by'] .. ' ';
			if (sepc ~= '.') then by_text = by_text:lower() end					-- lowercase for cs2
			Authors = by_text .. Authors;										-- author follows title so tweak it here
			if is_set (Editors) and is_set (Date) then							-- when Editors make sure that Authors gets terminated
				Authors = terminate_name_list (Authors, sepc);					-- terminate with 0 or 1 sepc and a space
			end
			if (not is_set (Date)) then											-- when date is set it's in parentheses; no Contributors termination
				Contributors = terminate_name_list (Contributors, sepc);		-- terminate with 0 or 1 sepc and a space
			end
			text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc );
		else
			text = safe_join( {Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc );
		end
	elseif is_set(Editors) then
		if is_set(Date) then
			if EditorCount <= 1 then
				Editors = Editors .. ", " .. cfg.messages['editor'];
			else
				Editors = Editors .. ", " .. cfg.messages['editors'];
			end
		else
			if EditorCount <= 1 then
				Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " "
			else
				Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " "
			end
		end
		text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc );
	else
		if in_array(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then
			text = safe_join( {Chapter, Place, tcommon, pgtext, Date, idcommon}, sepc );
		else
			text = safe_join( {Chapter, Place, tcommon, Date, pgtext, idcommon}, sepc );
		end
	end
	
	if is_set(PostScript) and PostScript ~= sepc then
		text = safe_join( {text, sepc}, sepc );  --Deals with italics, spaces, etc.
		text = text:sub(1,-sepc:len()-1);
	end	
	
	text = safe_join( {text, PostScript}, sepc );

	-- Now enclose the whole thing in a <cite/> element
	local options = {};
	
	if is_set(config.CitationClass) and config.CitationClass ~= "citation" then
		options.class = config.CitationClass;
		options.class = "citation " .. config.CitationClass;					-- class=citation required for blue highlight when used with |ref=
	else
		options.class = "citation";
	end
	
	if is_set(Ref) and Ref:lower() ~= "none" then								-- set reference anchor if appropriate
		local id = Ref
		if ('harv' == Ref ) then
			local namelist = {};												-- holds selected contributor, author, editor name list
			local year = first_set ({Year, anchor_year}, 2);					-- Year first for legacy citations and for YMD dates that require disambiguation

			if #c > 0 then														-- if there is a contributor list
				namelist = c;													-- select it
			elseif #a > 0 then													-- or an author list
				namelist = a;
			elseif #e > 0 then													-- or an editor list
				namelist = e;
			end
			if #namelist > 0 then												-- if there are names in namelist
				id = anchor_id (namelist, year);								-- go make the CITEREF anchor
			else
				id = '';														-- unset
			end
		end
		options.id = id;
	end
	
	if string.len(text:gsub("<span[^>/]*>(.-)</span>", "%1"):gsub("%b<>","")) <= 2 then	-- remove <span> tags and other html-like markup; then get length of what remains
		z.error_categories = {};
		text = set_error('empty_citation');
		z.message_tail = {};
	end
	
	local render = {};															-- here we collect the final bits for concatenation into the rendered citation

	if is_set(options.id) then													-- here we wrap the rendered citation in <cite ...>...</cite> tags
		table.insert (render, substitute (cfg.presentation['cite-id'], {mw.uri.anchorEncode(options.id), mw.text.nowiki(options.class), text}));	-- when |ref= is set
	else
		table.insert (render, substitute (cfg.presentation['cite'], {mw.text.nowiki(options.class), text}));	-- all other cases
	end		

	table.insert (render, substitute (cfg.presentation['ocins'], {OCinSoutput}));		-- append metadata to the citation

	if #z.message_tail ~= 0 then
		table.insert (render, ' ');
		for i,v in ipairs( z.message_tail ) do
			if is_set(v[1]) then
				if i == #z.message_tail then
					table.insert (render, error_comment( v[1], v[2] ));
				else
					table.insert (render, error_comment( v[1] .. "; ", v[2] ));
				end
			end
		end
	end

	if #z.maintenance_cats ~= 0 then
		table.insert (render, '<span class="citation-comment cs1-maint nomobile noprint">');
		for _, v in ipairs( z.maintenance_cats ) do								-- append maintenance categories
			table.insert (render, v);
			table.insert (render, ' (');
			table.insert (render, make_wikilink (':Category:' .. v, 'link'));
			table.insert (render, ') ');
		end
		table.insert (render, '</span>');
	end
	
	no_tracking_cats = no_tracking_cats:lower();
	if in_array(no_tracking_cats, {"", "no", "false", "n"}) then
		for _, v in ipairs( z.error_categories ) do
			table.insert (render, make_wikilink ('Category:' .. v));
		end
		for _, v in ipairs( z.maintenance_cats ) do								-- append maintenance categories
			table.insert (render, make_wikilink ('Category:' .. v));
		end
		for _, v in ipairs( z.properties_cats ) do								-- append properties categories
			table.insert (render, make_wikilink ('Category:' .. v));
		end
	end

	return table.concat (render);	
end


--[[--------------------------< C S 1 . C I T A T I O N >------------------------------------------------------

This is used by templates such as {{cite book}} to create the actual citation text.

]]

function cs1.citation(frame)
	Frame = frame;																-- save a copy incase we need to display an error message in preview mode
	local pframe = frame:getParent()
	local validation, utilities, identifiers, metadata;
	
	if nil ~= string.find (frame:getTitle(), 'sandbox', 1, true) then			-- did the {{#invoke:}} use sandbox version?
		cfg = mw.loadData ('Module:Citation/CS1/Configuration/sandbox');		-- load sandbox versions of support modules
		whitelist = mw.loadData ('Module:Citation/CS1/Whitelist/sandbox');
		utilities = require ('Module:Citation/CS1/Utilities/sandbox');
		validation = require ('Module:Citation/CS1/Date_validation/sandbox');
		identifiers = require ('Module:Citation/CS1/Identifiers/sandbox');
		metadata = require ('Module:Citation/CS1/COinS/sandbox');
		
	else																		-- otherwise
		cfg = mw.loadData ('Module:Citation/CS1/Configuration');				-- load live versions of support modules
		whitelist = mw.loadData ('Module:Citation/CS1/Whitelist');
		utilities = require ('Module:Citation/CS1/Utilities');
		validation = require ('Module:Citation/CS1/Date_validation');
		identifiers = require ('Module:Citation/CS1/Identifiers');
		metadata = require ('Module:Citation/CS1/COinS');
	end

	utilities.set_selected_modules (cfg);										-- so that functions in Utilities can see the cfg tables
	identifiers.set_selected_modules (cfg, utilities);							-- so that functions in Identifiers can see the selected cfg tables and selected Utilities module
	validation.set_selected_modules (cfg, utilities);							-- so that functions in Date validataion can see selected cfg tables and the selected Utilities module
	metadata.set_selected_modules (cfg, utilities);								-- so that functions in COinS can see the selected cfg tables and selected Utilities module

	dates = validation.dates;													-- imported functions from Module:Citation/CS1/Date validation
	year_date_check = validation.year_date_check;
	reformat_dates = validation.reformat_dates;
	date_hyphen_to_dash = validation.date_hyphen_to_dash;
	date_name_xlate = validation.date_name_xlate;

	is_set = utilities.is_set;													-- imported functions from Module:Citation/CS1/Utilities
	in_array = utilities.in_array;
	substitute = utilities.substitute;
	error_comment = utilities.error_comment;
	set_error = utilities.set_error;
	select_one = utilities.select_one;
	add_maint_cat = utilities.add_maint_cat;
	wrap_style = utilities.wrap_style;
	safe_for_italics = utilities.safe_for_italics;
	is_wikilink = utilities.is_wikilink;
	make_wikilink = utilities.make_wikilink;

	z = utilities.z;															-- table of error and category tables in Module:Citation/CS1/Utilities

	extract_ids = identifiers.extract_ids;										-- imported functions from Module:Citation/CS1/Identifiers
	build_id_list = identifiers.build_id_list;
	is_embargoed = identifiers.is_embargoed;
	extract_id_access_levels = identifiers.extract_id_access_levels;
	
	make_coins_title = metadata.make_coins_title;								-- imported functions from Module:Citation/CS1/COinS
	get_coins_pages = metadata.get_coins_pages;
	COinS = metadata.COinS;

	local args = {};															-- table where we store all of the template's arguments
	local suggestions = {};														-- table where we store suggestions if we need to loadData them
	local error_text, error_state;

	local config = {};															-- table to store parameters from the module {{#invoke:}}
	for k, v in pairs( frame.args ) do
		config[k] = v;
--		args[k] = v;															-- debug tool that allows us to render a citation from module {{#invoke:}}
	end	

	local capture;																-- the single supported capture when matching unknown parameters using patterns
	for k, v in pairs( pframe.args ) do
		if v ~= '' then
			if ('string' == type (k)) then
				k = mw.ustring.gsub (k, '%d', cfg.date_names.local_digits);			-- for enumerated parameters, translate 'local' digits to Western 0-9
			end
			if not validate( k, config.CitationClass ) then			
				error_text = "";
				if type( k ) ~= 'string' then
					-- Exclude empty numbered parameters
					if v:match("%S+") ~= nil then
						error_text, error_state = set_error( 'text_ignored', {v}, true );
					end
				elseif validate( k:lower(), config.CitationClass ) then 
					error_text, error_state = set_error( 'parameter_ignored_suggest', {k, k:lower()}, true );
				else
					if nil == suggestions.suggestions then						-- if this table is nil then we need to load it
						if nil ~= string.find (frame:getTitle(), 'sandbox', 1, true) then			-- did the {{#invoke:}} use sandbox version?
							suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions/sandbox' );	-- use the sandbox version
						else
							suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' );			-- use the live version
						end
					end
					for pattern, param in pairs (suggestions.patterns) do		-- loop through the patterns to see if we can suggest a proper parameter
						capture = k:match (pattern);							-- the whole match if no caputre in pattern else the capture if a match
						if capture then											-- if the pattern matches 
							param = substitute( param, capture );				-- add the capture to the suggested parameter (typically the enumerator)
							error_text, error_state = set_error( 'parameter_ignored_suggest', {k, param}, true );	-- set the error message
						end
					end
					if not is_set (error_text) then								-- couldn't match with a pattern, is there an expicit suggestion?
						if suggestions.suggestions[ k:lower() ] ~= nil then
							error_text, error_state = set_error( 'parameter_ignored_suggest', {k, suggestions.suggestions[ k:lower() ]}, true );
						else
							error_text, error_state = set_error( 'parameter_ignored', {k}, true );
							v = '';												-- unset value assigned to unrecognized parameters (this for the limited parameter lists)
						end
					end
				end				  
				if error_text ~= '' then
					table.insert( z.message_tail, {error_text, error_state} );
				end				
			end
			missing_pipe_check (v);												-- do we think that there is a parameter that is missing a pipe?
																				-- TODO: is this the best place for this translation?
			args[k] = v;
		elseif args[k] ~= nil or (k == 'postscript') then						-- here when v is empty string
			args[k] = v;														-- why do we do this?  we don't support 'empty' parameters
		end		
	end	

	for k, v in pairs( args ) do
		if 'string' == type (k) then											-- don't evaluate positional parameters
			has_invisible_chars (k, v);
		end
	end
	return citation0( config, args)
end

return cs1;