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', 'বিনামূল্যে নিবন্ধন আবশ্যক'}, 	['limited'] = {'id-lock-limited', 'সীমিত ট্রায়াল সাপেক্ষে বিনামূল্যে প্রবেশাধিকার, সাধারণত সাবস্ক্রিপশন প্রয়োজন'}, 	['subscription'] = {'id-lock-subscription', 'অর্থের বিনিময়ে সাবস্ক্রিপশন প্রয়োজন'}, 	}  --[[--------------------------< T A R G E T _ C H E C K >------------------------------------------------------  look for anchor_id (CITEREF name-list and year or text from |ref=) in anchor_id_list  the 'no target' error may be suppressed with |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_list[anchor_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 = 'Category:Harv and Sfn no-target errors'; 	elseif 1 < number then 		msg = 'multiple targets (' .. number .. '×): ' .. anchor_id;			-- more than one anchor_id in this article 		category = 'Category:Harv and Sfn multiple-target errors'; 	end  	category = 0 == mw.title.getCurrentTitle().namespace and category or ;	-- only categorize in article space  --use this version to show error messages 	return msg and ' Harvc ত্রুটি: ' .. msg .. ' (সাহায্য)' .. category or ; --use this version to hide error messages --	return msg and ' Harvc ত্রুটি: ' .. msg .. ' (সাহায্য)' .. category or ; end  --[[--------------------------< I S _ S E T >------------------------------------------------------------------  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  --[[--------------------------< C H E C K _ Y E A R S >--------------------------------------------------------  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=</code>.'; 	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 = mw.ustring.match (year, 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=</code>.';												-- 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</code>.';		-- ay not set, so anchor_year is malformed 		end 		 --		if not anchor_year:match ('%l$') then --			return ' ' .. code_open_tag .. '|anchor-year=</code> missing dab.';	-- anchor_year must end with a disambiguator letter --		end 	 		if y ~= ay then 			return ' ' .. code_open_tag .. '|year=</code> / ' .. code_open_tag .. '|anchor-year=</code> 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 = ' ';  -- form the CITEREF anchor 	if is_set (args.id) then 		args.id = mw.uri.anchorEncode (args.id) 		span_open_tag = '<span id="' .. args.id .. '" class="citation">';		-- 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 = '<span id="' .. citeref .. '" class="citation">'; 	end   --[[ form the contributors display list: 	if |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=</code>';	-- 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] .. ' & ' .. args.last[3]; 		elseif 2 == #args.last then												-- two (first & second) 			contributors = args.last[1] .. ' & ' .. 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 .. ' & ' .. 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 .. ' & ' .. 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 .. ' (সাহায্য)'; 	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, '</span>');  	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 = ", পৃ. ", 		pages_sep = ", পৃ. ", 		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=<text> 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 .. ' প্রয়োজনীয় প্যারামিটার ' .. code_open_tag .. '|id= অনুপস্থিত।';		-- error message if contributor and source are the same 	end  	return table.concat ({frame:extensionTag ('templatestyles', '', {src='মডিউল:উদ্ধৃতি/শৈলী.css'}), core (args)}); end  --[[--------------------------< E X P O R T E D   F U N C T I O N S >------------------------------------------ ]]  return { 	harvc = harvc 	};