Module:Harvc

require('strict') local anchor_id_list = mw.loadData ('Module:Footnotes/anchor_id_list').anchor_id_list;

local code_open_tag = ' .'; 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

--[[--< M A K E _ N A M E >

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

--[[--< C O R E >--

Assembles the various parts provided by the template into a properly formatted bridging citation. Adds punctuation and text; encloses the whole within a span with id and class attributes.

This creates a CITEREF anchor from |last1= through |last4= and |year=. It also creates a CITEREF link from |in1= through
 * in4= and |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 --name-list-style=harv, display is similar to and, 1 to 4 last names;	if |display-authors= is empty or omitted, display is similar to cs1|2: display all names in last, first order 	if |display-authors=etal then displays all author names in last, first order and append et al.	if value assigned to |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 < #args.last then											-- when |display-authors= is less than the number of authors etal = true;													-- append 'et al.' end else count = #args.last;													-- set count to display all of the authors end

while i <= count do			if is_set (contributors) then contributors = contributors .. '; ' .. make_name (args.last[i], args.first[i], args.link[i], args.mask[i]);			-- the rest of the contributors else contributors = make_name (args.last[i], args.first[i], args.link[i], args.mask[i]);			-- first contributor's name end i = i+1;															-- bump the index end if true == etal then contributors = contributors .. ' et al.';							-- append et al.		elseif 'amp' == args.name_list_style then contributors = contributors:gsub('; ([^;]+)$', ' & %1')				-- replace last separator with ' & ' end else																		-- do default harv- or sfn-style contributor display if 4 <= #args.last then													-- four or more contributors (first followed by et al.) contributors = args.last[1] .. ' et al.'; elseif 3 == #args.last then												-- three (display them all) contributors = args.last[1] .. ', ' .. args.last[2] .. ' &amp; ' .. args.last[3]; elseif 2 == #args.last then												-- two (first & second) contributors = args.last[1] .. ' &amp; ' .. args.last[2]; elseif 1 == #args.last then												-- just one (first) contributors = args.last[1]; else args.err_msg = args.err_msg .. ' no authors in contributor list.';	-- this code used to find holes in the list; no more end end

--form the source author-date list if is_set (args.in4) and is_set (args.in3) and is_set (args.in2) and is_set (args.in1) then source = args.in1 .. ' et al.'; elseif not is_set (args.in4) and is_set (args.in3) and is_set (args.in2) and is_set (args.in1) then source = args.in1 .. ', ' .. args.in2 .. ' &amp; ' .. args.in3; elseif not is_set (args.in4) and not is_set (args.in3) and is_set (args.in2) and is_set (args.in1) then source = args.in1 .. ' &amp; ' .. args.in2; elseif not is_set (args.in4) and not is_set (args.in3) and not is_set (args.in2) and is_set (args.in1) then source = args.in1; else args.err_msg = args.err_msg .. ' author missing from source list.' end

source = source .. ' ' .. args.open .. args.year .. args.close;				-- add the year with or without brackets

--assemble CITEREF wikilink local anchor_id; local target_err_msg; if '' ~= args.ref then anchor_id = mw.uri.anchorEncode (args.ref) else anchor_id = mw.uri.anchorEncode(table.concat ({'CITEREF', args.in1, args.in2, args.in3, args.in4, args.year})); end target_err_msg = target_check (anchor_id, args.ignore);						-- see if there is a target for this anchor_id source = '" .. source .. "";

-- special case for afterword, foreword, introduction, preface local no_quotes = ({['afterword']=true, ['foreword']=true, ['introduction']=true, ['preface']=true})[args.contribution:lower];

--combine contribution with url to make external link if args.url ~= '' then args.contribution = '[' .. args.url .. ' ' .. args.contribution .. ']';	-- format external link

if args['url-access'] then if lock_icons[args['url-access']] then args.contribution = table.concat ({									-- add access icon markup to this item				'',															-- 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

--[[--< H A R V C >

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.last[1] = pframe.args.last or pframe.args.last1 or pframe.args.author or pframe.args.author1;		-- get first contributor's last name args.citeref[1] = args.last[1];										-- add it to the citeref args.first[1] = pframe.args.first or pframe.args.first1;			-- get first contributor's first name args.link[1] = pframe.args['author-link'] or pframe.args['author-link1'];	-- get first contributor's article link args.mask[1] = 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.last[i] = pframe.args['last'..i] or pframe.args['author'..i];	-- last names args.first[i] = pframe.args['first'..i];						-- first names args.link[i] = pframe.args['author-link'..i];					-- links args.mask[i] = pframe.args['author-mask'..i];					-- masks if 5 > i then args.citeref[i] = args.last[i];								-- collect first four last names for CITEREF anchor end i = i + 1														-- bump the index end end

if 0 == #args.last then														-- |last= is required args.err_msg = args.err_msg .. ' no authors in contributor list.'; 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.last[1] == args.in1 and args.last[2] == args.in2 and args.last[3] == args.in3 and args.last[4] == 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

----< E X P O R T E D  F U N C T I O N S >--

return { harvc = harvc };