มอดูล:Harvc

require('strict')
local anchor_id_list = mw.loadData ('Module:Footnotes/anchor_id_list').anchor_id_list;
local code_open_tag = ;								-- cs1-code class defined in Module:Citation/CS1/styles.css
local lock_icons = {															--icon classes are defined in Module:Citation/CS1/styles.css
	'registration' = {'id-lock-registration', 'Free registration required'},
	'limited' = {'id-lock-limited', 'Free access subject to limited trial, subscription normally required'},
	'subscription' = {'id-lock-subscription', 'Paid subscription required'},
	}
--ignore-err=yes when target cannot be found because target is inside
a template that wraps another template; 'multiple targets' error may not be suppressed
local function target_check (anchor_id, ignore)
	local number = anchor_id_listanchor_id;									-- nil when anchor_id not in list; else a number
	local msg;
	local category;
	if not number then
		if ignore then
			return ;															-- if ignore is true then no message, no category
		end
		msg = 'no target: ' .. anchor_id;										-- anchor_id not found in this article
		category = ;
	elseif 1 Harvc error: ' .. msg .. ' (help)' .. category or ;
--use this version to hide error messages
--	return msg and ' Harvc error: ' .. msg .. ' (help)' .. category or ;
end
----------------------------------------------------------------------------------------------
Whether variable is set or not.  A varable is set when it is not nil and not empty.
local function is_set( var )
	return not (var == nil or var == );
end
------------------------------------------------------------------------------------
evaluates params to see if they are one of these forms with or without lowercase letter disambiguator (same as in
Module:Footnotes):
	YYYY
	n.d.
	nd	
	c. YYYY
	YYYY–YYYY	(separator is endash)
	YYYY–YY		(separator is endash)
when anchor_year present, year portion must be same as year param and must have disambiguator
returns empty string when params have correct form; error message else
local function check_years (year, anchor_year)
	local y, ay;
	
	if not is_set (year) then													-- year is required so return error message when not set
		return ' missing ' .. code_open_tag .. '|year=.';
	end
	
	local patterns = {															-- allowed year patterns from Module:Footnotes (captures added here)
		'^(%d%d%d%d?)%l?$',														-- YYY or YYYY
		'^(n%.d%.)%l?$',														-- n.d.
		'^(nd)%l?$',															-- nd
		'^(c%. %d%d%d%d?)%l?$',													-- c. YYY or c. YYYY
		'^(%d%d%d%d–%d%d%d%d)%l?$',												-- YYYY–YYYY
		'^(%d%d%d%d–%d%d)%l?$'													-- YYYY–YY
		}
	for _, pattern in ipairs (patterns) do										-- spin through the patterns
		y = year:match (pattern);												-- y is the year portion
		if y then
			break;																-- when y is set, we found a match so done
		end
	end
	if not y then
		return ' invalid ' .. code_open_tag .. '|year=.';												-- y not set, so year is malformed
	end
	
	if is_set (anchor_year) then												-- anchor_year is optional
		for _, pattern in ipairs (patterns) do									-- spin through the patterns
			ay = anchor_year:match (pattern);									-- ay is the year portion
			if ay then
				break;															-- when ay is set, we found a match so done
			end
		end
		if not ay then
			return ' invalid ' .. code_open_tag .. '|anchor-year.';		-- ay not set, so anchor_year is malformed
		end
		
--		if not anchor_year:match ('%l$') then
--			return ' ' .. code_open_tag .. '|anchor-year= missing dab.';	-- anchor_year must end with a disambiguator letter
--		end
	
		if y ~= ay then
			return ' ' .. code_open_tag .. '|year= / ' .. code_open_tag .. '|anchor-year= mismatch.';	-- 'year' portions of year and anchor_year must be the same
		end
	end
	
	return ;																	-- both years are good; empty string for concatenation
end
----------------------------------------------------------------------------------------
Assembles last, first, link, or mask into a displayable contributor name.
local function make_name (last, first, link, mask)
	local name = last;
	
	if is_set (first) then
		name = name .. ', ' .. first;											-- concatenate first onto last
	end
	
	if is_set (link) then
		name =  .. name .. ;								-- form a wikilink around the name
	end
	
	if is_set (mask) then														-- mask this author
		if tonumber(mask) then
			name = string.rep ('—', mask)										-- make a string that number length of mdashes
		else
			name = mask;														-- mask is not a number so use the mask text
		end
	end
	
	return name;
end
--year=.  It is presumed that the dates of contributions are the same as the date of the enclosing work.
Even though not displayed, a year parameter is still required for the CITEREF anchor
local function core( args )
	local span_open_tag;														-- holds CITEREF and css
	local contributors = ;													-- chapter or contribution authors
	local source = ;															-- editor/author date list that forms a CITEREF link to a full citation
	local in_text = ' In ';
-- form the CITEREF anchor
	if is_set (args.id) then
		args.id = mw.uri.anchorEncode (args.id)
		span_open_tag = ;		-- for use when contributor name is same as source name
	else
		local citeref = 'CITEREF' .. table.concat (args.citeref) .. (is_set (args'anchor-year') and args'anchor-year' or args.year);
		citeref = mw.uri.anchorEncode (citeref);
		span_open_tag = ;
	end

--display-authors= is less than the number of author last names, displays the specified number of author names in last, first order followed by et al.
	if 'harv' ~= args.name_list_style then										-- default cs1|2 style contributor list
		local i = 1;
		local count;
		local etal = false;														-- when |display-authors= is same as number of authors in contributor list
		
		if is_set (args.display_authors) then
			if 'etal' == args.display_authors:lower():gsub(" '%.", ) then	-- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings
				count = #args.last;												-- display all authors and ...
				etal = true;													-- ... append 'et al.'
			else
				count = tonumber (args.display_authors) or 0;					-- 0 if can't be converted to a number
				if 0 >= count then
					args.err_msg = args.err_msg .. ' invalid ' .. code_open_tag .. '|display-authors=';	-- if zero, then emit error message
				end
			end
			if count > #args.last then
				count = #args.last;												-- when |display-authors= is more than the number of authors, use the number of authors
			end
			if count ',															-- close the opening span tag
				args.contribution,
				,														-- and close the span
				});
			end
		end	
	end
	if is_set (args'anchor-year') then
		contributors = contributors .. ' (' .. args'anchor-year' .. ')' .. args.sepc;
	elseif args.sepc ~= contributors:sub(-1) and args.sepc ..  ~= contributors:sub(-3) then
		contributors = contributors .. args.sepc;								-- add separator if not same as last character in name list (|first=John S. or et al.)
	end
-- pages and other insource location
	if args.p ~=  then
		args.p = args.page_sep .. args.p;
	elseif args.pp ~=  then
		args.p = args.pages_sep .. args.pp;										-- args.p not set so use it to hold common insource location info
	end      

	if args.loc ~=  then
		args.p = args.p .. ', ' .. args.loc;									-- add arg.loc to args.p
	end
--wrap error messages in span and add help link
	if is_set (args.err_msg) then
		args.err_msg = ' harvc:' .. args.err_msg .. ' (help)';
	end
	if ',' == args.sepc then
		in_text = in_text:lower();												-- CS2 style use lower case
	end
-- and put it all together
	local result = {};															-- the assemby of the above output
	table.insert (result, span_open_tag);
	table.insert (result, contributors);
	table.insert (result, no_quotes and ' ' or ' "');							-- foreword, afterword, introduction, preface contributions are not quoted; all other contributions are
	table.insert (result, args.contribution);
	table.insert (result, no_quotes and  or '"');								-- foreword, afterword, introduction, preface contributions are not quoted; all other contributions are
	table.insert (result, args.sepc);
	table.insert (result, in_text);
	table.insert (result, source);
	table.insert (result, args.p);
	table.insert (result, args.ps);
	table.insert (result, args.err_msg);
	table.insert (result, target_err_msg);
	table.insert (result, );
	return table.concat (result);												-- make a string and done
end
------------------------------------------------------------------------------------------------
Entry point from  template.  Fetches parent frame parameters, does a bit of simple error checking
local function harvc (frame)
	local args = {
		err_msg = ,
		page_sep = ", p. ",
		pages_sep = ", pp. ",
		sepc = '.',
		ps = '.',
		open = '(',																-- year brackets for source year
		close = ')',
		last = {},
		first = {},
		link = {},
		mask = {},
		citeref = {}
		}
	local pframe = frame:getParent();

	args.contribution =  pframe.args.c or										-- chapter or contribution
				pframe.args.chapter or
				pframe.args.contribution or ;
	args.id = pframe.args.id or ;
	args.in1 = pframe.args'in' or pframe.args.in1 or ;						-- source editor surnames; 'in' is a Lua reserved keyword
	args.in2 = pframe.args.in2 or ;
	args.in3 = pframe.args.in3 or ;
	args.in4 = pframe.args.in4 or ;
	args.display_authors = pframe.args'display-authors';						-- the number of contributor names to display; cs1|2 format includes first names
	args.name_list_style = pframe.args'name-list-style' or ;					-- when set to 'harv' display contributor list in sfn or harv style
	args.name_list_style = args.name_list_style:lower();						-- make it case agnostic
	if is_set (pframe.args.last) or is_set (pframe.args.last1) or
		is_set (pframe.args.author) or is_set (pframe.args.author1) then		-- must have at least this to continue
			args.last1 = pframe.args.last or pframe.args.last1 or pframe.args.author or pframe.args.author1;		-- get first contributor's last name
			args.citeref1 = args.last1;										-- add it to the citeref
			args.first1 = pframe.args.first or pframe.args.first1;			-- get first contributor's first name
			args.link1 = pframe.args'author-link' or pframe.args'author-link1';	-- get first contributor's article link
			args.mask1 = pframe.args'author-mask' or pframe.args'author-mask1';	-- get first contributor's article link
		
			local i = 2;														-- index for the rest of the names
			while is_set (pframe.args'last'..i) or is_set (pframe.args'author'..i) do	-- loop through pframe.args and get the rest of the names
				args.lasti = pframe.args'last'..i or pframe.args'author'..i;	-- last names
				args.firsti = pframe.args'first'..i;						-- first names
				args.linki = pframe.args'author-link'..i;					-- links
				args.maski = pframe.args'author-mask'..i;					-- masks
				if 5 > i then
					args.citerefi = args.lasti;								-- collect first four last names for CITEREF anchor
				end
				i = i + 1														-- bump the index
			end
	end
	args.p = pframe.args.p or pframe.args.page or ;							-- source page number(s) or location
	args.pp = pframe.args.pp or pframe.args.pages or ;
	args.loc = pframe.args.loc or ;
	args.ref = pframe.args.ref or pframe.args.Ref or ;						-- used to match |ref= in cs1|2 source template
	args.ignore = 'yes' == pframe.args'ignore-err';							-- suppress false-positive 'no target' errors
	if 'cs2' == pframe.args.mode then
		args.ps = ;															-- set postscript character to empty string, cs2 mode
		args.sepc = ',';														-- set seperator character to comma, cs2 mode
	end
	do																			-- to limit scope of local temp
		local temp = pframe.args.ps or pframe.args.postscript;
		
		if is_set (temp) then
			if 'none' == temp:lower() then										-- if |ps=none or |postscript=none then
				args.ps = ;													-- no postscript
			else
				args.ps = temp;													-- override default postscript
			end
		end
	end																			-- end of scope limit
	if 'yes' == pframe.args.nb then												-- if no brackets around year in link to cs1|2 template
		args.open = ;															-- unset these
		args.close = ;
	end
	
	args.url = pframe.args.url or												-- url for chapter or contribution
			pframe.args'chapter-url' or
			pframe.args'contribution-url' or ;
	
	args'url-access' = pframe.args'url-access';
	
	args.year = pframe.args.year or ;											-- required
	args'anchor-year' = pframe.args'anchor-year' or ;
	args.err_msg = args.err_msg .. check_years (args.year, args'anchor-year');
	if not is_set (args.contribution) then
		args.err_msg = args.err_msg .. ' required contribution is missing.';	-- error message if source not provided
		args.contribution = args.url;											-- if set it will give us linkable text
	end
	
	if args.last1 == args.in1 and
		args.last2 == args.in2 and
		args.last3 == args.in3 and
		args.last4 == args.in4 and
		not is_set (args.id) then
			args.err_msg = args.err_msg .. ' required ' .. code_open_tag .. '|id= parameter missing.';		-- error message if contributor and source are the same
	end
	return table.concat ({frame:extensionTag ('templatestyles', , {src='Module:Citation/CS1/styles.css'}), core (args)});
end
----------------------------------------------------------------------
return {
	harvc = harvc
	};