বিষয়বস্তুতে চলুন

মডিউল:উদ্ধৃতি

বইপিডিয়া থেকে

এই মডিউলের জন্য মডিউল:উদ্ধৃতি/নথি-এ নথিপত্র তৈরি করা হয়ে থাকতে পারে


local cs1 ={};

--[[--------------------------< F O R W A R D   D E C L A R A T I O N S >--------------------------------------
]]

local dates, year_date_check, reformat_dates, date_hyphen_to_dash,				-- functions in Module:Citation/CS1/Date_validation
	date_name_xlate

local is_set, in_array, substitute, error_comment, set_error, select_one,		-- functions in Module:Citation/CS1/Utilities
	add_maint_cat, wrap_style, safe_for_italics, is_wikilink, make_wikilink;

local z ={};																	-- tables in Module:Citation/CS1/Utilities

local extract_ids, extract_id_access_levels, build_id_list, is_embargoed;		-- functions in Module:Citation/CS1/Identifiers

local make_coins_title, get_coins_pages, COinS;									-- functions in Module:Citation/CS1/COinS

local cfg = {};																	-- table of configuration tables that are defined in Module:Citation/CS1/Configuration
local whitelist = {};															-- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist


--[[--------------------------< P A G E   S C O P E   V A R I A B L E S >--------------------------------------

delare variables here that have page-wide scope that are not brought in from other modules; thatare created here
and used here

]]

local added_deprecated_cat;														-- boolean flag so that the category is added only once
local added_prop_cats = {};														-- list of property categories that have been added to z.properties_cats
local added_vanc_errs;															-- boolean flag so we only emit one Vancouver error / category

local Frame;																	-- holds the module's frame table


--[[--------------------------< F I R S T _ S E T >------------------------------------------------------------

Locates and returns the first set value in a table of values where the order established in the table,
left-to-right (or top-to-bottom), is the order in which the values are evaluated.  Returns nil if none are set.

This version replaces the original 'for _, val in pairs do' and a similar version that used ipairs.  With the pairs
version the order of evaluation could not be guaranteed.  With the ipairs version, a nil value would terminate
the for-loop before it reached the actual end of the list.

]]

local function first_set (list, count)
	local i = 1;
	while i <= count do															-- loop through all items in list
		if is_set( list[i] ) then
			return list[i];														-- return the first set list member
		end
		i = i + 1;																-- point to next
	end
end


--[[--------------------------< A D D _ P R O P _ C A T >--------------------------------------------------------

Adds a category to z.properties_cats using names from the configuration file with additional text if any.

foreign_lang_source and foreign_lang_source_2 keys have a language code appended to them so that multiple languages
may be categorized but multiples of the same language are not categorized.

added_prop_cats is a table declared in page scope variables above

]]

local function add_prop_cat (key, arguments)
	if not added_prop_cats [key] then
		added_prop_cats [key] = true;											-- note that we've added this category
		key = key:gsub ('(foreign_lang_source_?2?)%a%a%a?', '%1');				-- strip lang code from keyname
		table.insert( z.properties_cats, substitute (cfg.prop_cats [key], arguments));		-- make name then add to table
	end
end


--[[--------------------------< A D D _ V A N C _ E R R O R >----------------------------------------------------
Module:নথি/রূপরেখা
Adds a single Vancouver system error message to the template's output regardless of how many error actually exist.
To prevent duplication, added_vanc_errs is nil until an error message is emitted.

added_vanc_errs is a boolean declared in page scope variables above

]]

local function add_vanc_error (source)
	if not added_vanc_errs then
		added_vanc_errs = true;													-- note that we've added this category
		table.insert( z.message_tail, { set_error( 'vancouver', {source}, true ) } );
	end
end


--[[--------------------------< I S _ S C H E M E >------------------------------------------------------------

does this thing that purports to be a uri scheme seem to be a valid scheme?  The scheme is checked to see if it
is in agreement with http://tools.ietf.org/html/std66#section-3.1 which says:
	Scheme names consist of a sequence of characters beginning with a
   letter and followed by any combination of letters, digits, plus
   ("+"), period ("."), or hyphen ("-").

returns true if it does, else false

]]

local function is_scheme (scheme)
	return scheme and scheme:match ('^%a[%a%d%+%.%-]*:');						-- true if scheme is set and matches the pattern
end


--[=[-------------------------< I S _ D O M A I N _ N A M E >--------------------------------------------------

Does this thing that purports to be a domain name seem to be a valid domain name?

Syntax defined here: http://tools.ietf.org/html/rfc1034#section-3.5
BNF defined here: https://tools.ietf.org/html/rfc4234
Single character names are generally reserved; see https://tools.ietf.org/html/draft-ietf-dnsind-iana-dns-01#page-15;
	see also [[Single-letter second-level domain]]
list of tlds: https://www.iana.org/domains/root/db

rfc952 (modified by rfc 1123) requires the first and last character of a hostname to be a letter or a digit.  Between
the first and last characters the name may use letters, digits, and the hyphen.

Also allowed are IPv4 addresses. IPv6 not supported

domain is expected to be stripped of any path so that the last character in the last character of the tld.  tld
is two or more alpha characters.  Any preceding '//' (from splitting a url with a scheme) will be stripped
here.  Perhaps not necessary but retained incase it is necessary for IPv4 dot decimal.

There are several tests:
	the first character of the whole domain name including subdomains must be a letter or a digit
	internationalized domain name (ascii characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the tld) see https://tools.ietf.org/html/rfc3490
	single-letter/digit second-level domains in the .org and .cash TLDs
	q, x, and z SL domains in the .com TLD
	i and q SL domains in the .net TLD
	single-letter SL domains in the ccTLDs (where the ccTLD is two letters)
	two-character SL domains in gTLDs (where the gTLD is two or more letters)
	three-plus-character SL domains in gTLDs (where the gTLD is two or more letters)
	IPv4 dot-decimal address format; TLD not allowed

returns true if domain appears to be a proper name and tld or IPv4 address, else false

]=]

local function is_domain_name (domain)
	if not domain then
		return false;															-- if not set, abandon
	end
	
	domain = domain:gsub ('^//', '');											-- strip '//' from domain name if present; done here so we only have to do it once
	
	if not domain:match ('^[%a%d]') then										-- first character must be letter or digit
		return false;
	end
																				-- Do most common case first
	if domain:match ('%f[%a%d][%a%d][%a%d%-]+[%a%d]%.%a%a+$') then				-- three or more character hostname.hostname or hostname.tld
		return true;
	elseif domain:match ('%f[%a%d][%a%d][%a%d%-]+[%a%d]%.xn%-%-[%a%d]+$') then	-- internationalized domain name with ACE prefix
		return true;
	elseif domain:match ('%f[%a%d][%a%d]%.cash$') then							-- one character/digit .cash hostname
		return true;
	elseif domain:match ('%f[%a%d][%a%d]%.org$') then							-- one character/digit .org hostname
		return true;
	elseif domain:match ('%f[%a][qxz]%.com$') then								-- assigned one character .com hostname (x.com times out 2015-12-10)
		return true;
	elseif domain:match ('%f[%a][iq]%.net$') then								-- assigned one character .net hostname (q.net registered but not active 2015-12-10)
		return true;
	elseif domain:match ('%f[%a%d][%a%d]%.%a%a$') then							-- one character hostname and cctld (2 chars)
		return true;
	elseif domain:match ('%f[%a%d][%a%d][%a%d]%.%a%a+$') then					-- two character hostname and tld
		return true;
	elseif domain:match ('^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?') then		-- IPv4 address
		return true;
	else
		return false;
	end
end


--[[--------------------------< I S _ U R L >------------------------------------------------------------------

returns true if the scheme and domain parts of a url appear to be a valid url; else false.

This function is the last step in the validation process.  This function is separate because there are cases that
are not covered by split_url(), for example is_parameter_ext_wikilink() which is looking for bracketted external
wikilinks.

]]

local function is_url (scheme, domain)
	if is_set (scheme) then														-- if scheme is set check it and domain
		return is_scheme (scheme) and is_domain_name (domain);
	else
		return is_domain_name (domain);											-- scheme not set when url is protocol relative
	end
end


--[[--------------------------< S P L I T _ U R L >------------------------------------------------------------

Split a url into a scheme, authority indicator, and domain.

First remove Fully Qualified Domain Name terminator (a dot following tld) (if any) and any path(/), query(?) or fragment(#).

If protocol relative url, return nil scheme and domain else return nil for both scheme and domain.

When not protocol relative, get scheme, authority indicator, and domain.  If there is an authority indicator (one
or more '/' characters immediately following the scheme's colon), make sure that there are only 2.

Strip off any port and path;

]]

local function split_url (url_str)
	local scheme, authority, domain;
	
	url_str = url_str:gsub ('([%a%d])%.?[/%?#].*$', '%1');						-- strip FQDN terminator and path(/), query(?), fragment (#) (the capture prevents false replacement of '//')

	if url_str:match ('^//%S*') then											-- if there is what appears to be a protocol relative url
		domain = url_str:match ('^//(%S*)')
	elseif url_str:match ('%S-:/*%S+') then										-- if there is what appears to be a scheme, optional authority indicator, and domain name
		scheme, authority, domain = url_str:match ('(%S-:)(/*)(%S+)');			-- extract the scheme, authority indicator, and domain portions
		authority = authority:gsub ('//', '', 1);								-- replace place 1 pair of '/' with nothing;
		if is_set(authority) then												-- if anything left (1 or 3+ '/' where authority should be) then
			return scheme;														-- return scheme only making domain nil which will cause an error message
		end
		domain = domain:gsub ('(%a):%d+', '%1');								-- strip port number if present
	end
	
	return scheme, domain;
end


--[[--------------------------< L I N K _ P A R A M _ O K >---------------------------------------------------

checks the content of |title-link=, |series-link=, |author-link= etc for properly formatted content: no wikilinks, no urls

Link parameters are to hold the title of a wikipedia article so none of the WP:TITLESPECIALCHARACTERS are allowed:
	# < > [ ] | { } _
except the underscore which is used as a space in wiki urls and # which is used for section links

returns false when the value contains any of these characters.

When there are no illegal characters, this function returns TRUE if value DOES NOT appear to be a valid url (the
|<param>-link= parameter is ok); else false when value appears to be a valid url (the |<param>-link= parameter is NOT ok).

]]

local function link_param_ok (value)
	local scheme, domain;
	if value:find ('[<>%[%]|{}]') then											-- if any prohibited characters
		return false;
	end

	scheme, domain = split_url (value);											-- get scheme or nil and domain or nil from url; 
	return not is_url (scheme, domain);											-- return true if value DOES NOT appear to be a valid url
end


--[[--------------------------< L I N K _ T I T L E _ O K >---------------------------------------------------

Use link_param_ok() to validate |<param>-link= value and its matching |<title>= value.

|<title>= may be wikilinked but not when |<param>-link= has a value.  This function emits an error message when
that condition exists

]]

local function link_title_ok (link, lorig, title, torig)
local orig;

	if is_set (link) then 														-- don't bother if <param>-link doesn't have a value
		if not link_param_ok (link) then										-- check |<param>-link= markup
			orig = lorig;														-- identify the failing link parameter
		elseif title:find ('%[%[') then											-- check |title= for wikilink markup
			orig = torig;														-- identify the failing |title= parameter
		end
	end

	if is_set (orig) then
		table.insert( z.message_tail, { set_error( 'bad_paramlink', orig)});	-- url or wikilink in |title= with |title-link=;
	end
end


--[[--------------------------< C H E C K _ U R L >------------------------------------------------------------

Determines whether a URL string appears to be valid.

First we test for space characters.  If any are found, return false.  Then split the url into scheme and domain
portions, or for protocol relative (//example.com) urls, just the domain.  Use is_url() to validate the two
portions of the url.  If both are valid, or for protocol relative if domain is valid, return true, else false.

Because it is different from a standard url, and because this module used external_link() to make external links
that work for standard and news: links, we validate newsgroup names here.  The specification for a newsgroup name
is at https://tools.ietf.org/html/rfc5536#section-3.1.4

]]

local function check_url( url_str )
	if nil == url_str:match ("^%S+$") then										-- if there are any spaces in |url=value it can't be a proper url
		return false;
	end
	local scheme, domain;

	scheme, domain = split_url (url_str);										-- get scheme or nil and domain or nil from url;
	
	if 'news:' == scheme then													-- special case for newsgroups
		return domain:match('^[%a%d%+%-_]+%.[%a%d%+%-_%.]*[%a%d%+%-_]$');
	end
	
	return is_url (scheme, domain);												-- return true if value appears to be a valid url
end


--[=[-------------------------< I S _ P A R A M E T E R _ E X T _ W I K I L I N K >----------------------------

Return true if a parameter value has a string that begins and ends with square brackets [ and ] and the first
non-space characters following the opening bracket appear to be a url.  The test will also find external wikilinks
that use protocol relative urls. Also finds bare urls.

The frontier pattern prevents a match on interwiki links which are similar to scheme:path urls.  The tests that
find bracketed urls are required because the parameters that call this test (currently |title=, |chapter=, |work=,
and |publisher=) may have wikilinks and there are articles or redirects like '//Hus' so, while uncommon, |title=[[//Hus]]
is possible as might be [[en://Hus]].

]=]

local function is_parameter_ext_wikilink (value)
local scheme, domain;

	if value:match ('%f[%[]%[%a%S*:%S+.*%]') then								-- if ext wikilink with scheme and domain: [xxxx://yyyyy.zzz]
		scheme, domain = split_url (value:match ('%f[%[]%[(%a%S*:%S+).*%]'));
	elseif value:match ('%f[%[]%[//%S+.*%]') then								-- if protocol relative ext wikilink: [//yyyyy.zzz]
		scheme, domain = split_url (value:match ('%f[%[]%[(//%S+).*%]'));
	elseif value:match ('%a%S*:%S+') then										-- if bare url with scheme; may have leading or trailing plain text
		scheme, domain = split_url (value:match ('(%a%S*:%S+)'));
	elseif value:match ('//%S+') then											-- if protocol relative bare url: //yyyyy.zzz; may have leading or trailing plain text
		scheme, domain = split_url (value:match ('(//%S+)'));					-- what is left should be the domain
	else
		return false;															-- didn't find anything that is obviously a url
	end

	return is_url (scheme, domain);												-- return true if value appears to be a valid url
end


--[[-------------------------< C H E C K _ F O R _ U R L >-----------------------------------------------------

loop through a list of parameters and their values.  Look at the value and if it has an external link, emit an error message.

]]

local function check_for_url (parameter_list)
local error_message = '';
	for k, v in pairs (parameter_list) do										-- for each parameter in the list
		if is_parameter_ext_wikilink (v) then									-- look at the value; if there is a url add an error message
			if is_set(error_message) then										-- once we've added the first portion of the error message ...
				error_message=error_message .. ", ";							-- ... add a comma space separator
			end
			error_message=error_message .. "&#124;" .. k .. "=";				-- add the failed parameter
		end
	end
	if is_set (error_message) then												-- done looping, if there is an error message, display it
		table.insert( z.message_tail, { set_error( 'param_has_ext_link', {error_message}, true ) } );
	end
end


--[[--------------------------< S A F E _ F O R _ U R L >------------------------------------------------------

Escape sequences for content that will be used for URL descriptions

]]

local function safe_for_url( str )
	if str:match( "%[%[.-%]%]" ) ~= nil then 
		table.insert( z.message_tail, { set_error( 'wikilink_in_url', {}, true ) } );
	end
	
	return str:gsub( '[%[%]\n]', {	
		['['] = '&#91;',
		[']'] = '&#93;',
		['\n'] = ' ' } );
end


--[[--------------------------< E X T E R N A L _ L I N K >----------------------------------------------------

Format an external link with error checking

]]

local function external_link( URL, label, source, access)
	local error_str = "";
	local domain;
	local path;
	local base_url;
	
	if not is_set( label ) then
		label = URL;
		if is_set( source ) then
			error_str = set_error( 'bare_url_missing_title', { wrap_style ('parameter', source) }, false, " " );
		else
			error( cfg.messages["bare_url_no_origin"] );
		end			
	end
	if not check_url( URL ) then
		error_str = set_error( 'bad_url', {wrap_style ('parameter', source)}, false, " " ) .. error_str;
	end
	
	domain, path = URL:match ('^([/%.%-%+:%a%d]+)([/%?#].*)$');					-- split the url into scheme plus domain and path
	if path then																-- if there is a path portion
		path = path:gsub ('[%[%]]', {['[']='%5b',[']']='%5d'});					-- replace '[' and ']' with their percent encoded values
		URL=domain..path;														-- and reassemble
	end
	
	if is_set (access) then														-- access level (subscription, registration, limited)
		label = safe_for_url (label);											-- replace square brackets and newlines

		base_url = table.concat (												-- assemble external link with access signal
			{
			'<span class="plainlinks">[',										-- opening css and url markup
			URL,																-- the url
			' ',																-- the required space
			label,
			'<span style="padding-left:0.15em">',								-- signal spacing css
			cfg.presentation[access],											-- the appropriate icon
			'</span>',															-- close signal spacing span
			']</span>'															-- close url markup and plain links span
			});
	else
		base_url = table.concat({ "[", URL, " ", safe_for_url( label ), "]" });	-- no signal markup
	end
	
	return table.concat({ base_url, error_str });
end


--[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >--------------------------------------

Categorize and emit an error message when the citation contains one or more deprecated parameters.  The function includes the
offending parameter name to the error message.  Only one error message is emitted regardless of the number of deprecated
parameters in the citation.

added_deprecated_cat is a boolean declared in page scope variables above

]]

local function deprecated_parameter(name)
	if not added_deprecated_cat then
		added_deprecated_cat = true;											-- note that we've added this category
		table.insert( z.message_tail, { set_error( 'deprecated_params', {name}, true ) } );		-- add error message
	end
end


--[=[-------------------------< K E R N _ Q U O T E S >--------------------------------------------------------

Apply kerning to open the space between the quote mark provided by the Module and a leading or trailing quote
mark contained in a |title= or |chapter= parameter's value.

This function will positive kern either single or double quotes:
	"'Unkerned title with leading and trailing single quote marks'"
	" 'Kerned title with leading and trailing single quote marks' " (in real life the kerning isn't as wide as this example)
Double single quotes (italic or bold wikimarkup) are not kerned.

Replaces unicode quotemarks in plain text or in the label portion of a [[L|D]] style wikilink with typewriter
quote marks regardless of the need for kerning.  Unicode quote marks are not replaced in simple [[D]] wikilinks.

Call this function for chapter titles, for website titles, etc; not for book titles.

]=]

local function kern_quotes (str)
	local cap='';
	local cap2='';
	local wl_type, label, link;

	wl_type, label, link = is_wikilink (str);									-- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
	
	if 1 == wl_type then														-- [[D]] simple wikilink with or without quote marks
		if mw.ustring.match (str, '%[%[[\"“”\'‘’].+[\"“”\'‘’]%]%]') then		-- leading and trailing quote marks
			str = substitute (cfg.presentation['kern-wl-both'], str);
		elseif mw.ustring.match (str, '%[%[[\"“”\'‘’].+%]%]')	then			-- leading quote marks
			str = substitute (cfg.presentation['kern-wl-left'], str);
		elseif mw.ustring.match (str, '%[%[.+[\"“”\'‘’]%]%]') then				-- trailing quote marks
			str = substitute (cfg.presentation['kern-wl-right'], str);
		end

	else																		-- plain text or [[L|D]]; text in label variable
		label= mw.ustring.gsub (label, '[“”]', '\"');							-- replace “” (U+201C & U+201D) with " (typewriter double quote mark)
		label= mw.ustring.gsub (label, '[‘’]', '\'');							-- replace ‘’ (U+2018 & U+2019) with ' (typewriter single quote mark)

		cap, cap2 = mw.ustring.match (label, "^([\"\'])([^\'].+)");				-- match leading double or single quote but not doubled single quotes (italic markup)
		if is_set (cap) then
			label = substitute (cfg.presentation['kern-left'], {cap, cap2});
		end
	
		cap, cap2 = mw.ustring.match (label, "^(.+[^\'])([\"\'])$")				-- match trailing double or single quote but not doubled single quotes (italic markup)
		if is_set (cap) then
			label = substitute (cfg.presentation['kern-right'], {cap, cap2});
		end
		
		if 2 == wl_type then
			str = make_wikilink (link, label);									-- reassemble the wikilink
		else
			str = label;
		end
	end
	return str;
end


--[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >----------------------------------------

|script-title= holds title parameters that are not written in Latin based scripts: Chinese, Japanese, Arabic, Hebrew, etc. These scripts should
not be italicized and may be written right-to-left.  The value supplied by |script-title= is concatenated onto Title after Title has been wrapped
in italic markup.

Regardless of language, all values provided by |script-title= are wrapped in <bdi>...</bdi> tags to isolate rtl languages from the English left to right.

|script-title= provides a unique feature.  The value in |script-title= may be prefixed with a two-character ISO639-1 language code and a colon:
	|script-title=ja:*** *** (where * represents a Japanese character)
Spaces between the two-character code and the colon and the colon and the first script character are allowed:
	|script-title=ja : *** ***
	|script-title=ja: *** ***
	|script-title=ja :*** ***
Spaces preceding the prefix are allowed: |script-title = ja:*** ***

The prefix is checked for validity.  If it is a valid ISO639-1 language code, the lang attribute (lang="ja") is added to the <bdi> tag so that browsers can
know the language the tag contains.  This may help the browser render the script more correctly.  If the prefix is invalid, the lang attribute
is not added.  At this time there is no error message for this condition.

Supports |script-title= and |script-chapter=

TODO: error messages when prefix is invalid ISO639-1 code; when script_value has prefix but no script;
]]

local function format_script_value (script_value)
	local lang='';																-- initialize to empty string
	local name;
	if script_value:match('^%l%l%s*:') then										-- if first 3 non-space characters are script language prefix
		lang = script_value:match('^(%l%l)%s*:%s*%S.*');						-- get the language prefix or nil if there is no script
		if not is_set (lang) then
			return '';															-- script_value was just the prefix so return empty string
		end
																				-- if we get this far we have prefix and script
		name = cfg.lang_code_remap[lang] or mw.language.fetchLanguageName( lang, "bn" );	-- get language name so that we can use it to categorize
		if is_set (name) then													-- is prefix a proper ISO 639-1 language code?
			script_value = script_value:gsub ('^%l%l%s*:%s*', '');				-- strip prefix from script
																				-- is prefix one of these language codes?
			if in_array (lang, cfg.script_lang_codes) then
				add_prop_cat ('script_with_name', {name, lang})
			else
				add_prop_cat ('script')
			end
			lang = ' lang="' .. lang .. '" ';									-- convert prefix into a lang attribute
		else
			lang = '';															-- invalid so set lang to empty string
		end
	end
	script_value = substitute (cfg.presentation['bdi'], {lang, script_value});	-- isolate in case script is rtl

	return script_value;
end


--[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------

Initially for |title= and |script-title=, this function concatenates those two parameter values after the script value has been 
wrapped in <bdi> tags.
]]

local function script_concatenate (title, script)
	if is_set (script) then
		script = format_script_value (script);									-- <bdi> tags, lang atribute, categorization, etc; returns empty string on error
		if is_set (script) then
			title = title .. ' ' .. script;										-- concatenate title and script title
		end
	end
	return title;
end