Changes

Jump to: navigation, search

Module:Citation/CS1

46,699 bytes removed, 06:07, 16 January 2016
Synch from sandbox;
local z = { error_categories = {}; -- for categorizing citations that contain errors error_ids cs1 = {}; message_tail = {}; maintenance_cats = {}; -- for categorizing citations that aren't erroneous per se, but could use a little work properties_cats = {}; -- for categorizing citations based on certain properties, language of source for instance}
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]
local dates, year_date_check -- functions in Module:Citation/CS1/Date_validation
local cfg = {}; dates, year_date_check, reformat_dates -- table of configuration tables that are defined functions in Module:Citation/CS1/Configurationlocal whitelist = {}; -- table of tables listing valid template parameter names; defined in Module:Citation/CS1/WhitelistDate_validation
local is_set, in_array, substitute, error_comment, set_error, select_one, --[[--------------------------< I S _ S E T >------------------------------------------------------------------functions in Module:Citation/CS1/Utilities add_maint_cat, wrap_style, safe_for_italics, remove_wiki_link;
Returns true if argument is setlocal z ={}; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.This function is global because it is called from both this module and from Date validation -- tables in Module:Citation/CS1/Utilities
]]local extract_ids, build_id_list, is_embargoed; -- functions in Module:Citation/CS1/Identifiersfunction is_set( var ) return not (var 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/Configurationlocal whitelist = nil or var == ''){}; -- table of tables listing valid template parameter names;enddefined in Module:Citation/CS1/Whitelist
--[[--------------------------< F I R S T _ S E T >------------------------------------------------------------
end
--[[--------------------------< I N A D D _ A R P R O P _ C A Y T >--------------------------------------------------------------
Whether needle is in haystackAdds a category to z.properties_cats using names from the configuration file with additional text if any.
]]
local added_prop_cats = {} -- list of property categories that have been added to z.properties_catslocal function in_arrayadd_prop_cat ( needlekey, haystack arguments) if needle not added_prop_cats [key] then added_prop_cats [key] == nil thentrue; -- note that we've added this category return falsetable.insert( z.properties_cats, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table
end
for n,v in ipairs( haystack ) do
if v == needle then
return n;
end
end
return false;
end
--[[--------------------------< S U B S T I T U T A D D _ V A N C _ E R R O R >----------------------------------------------------------
Populates numbered arguments in Adds a single Vancouver system error message string using to the template's output regardless of how many error actually exist.To prevent duplication, added_vanc_errs is nil until an argument tableerror message is emitted.
]]
local added_vanc_errs; -- flag so we only emit one Vancouver error / categorylocal function substituteadd_vanc_error ( msg, args ) return args and mwif not added_vanc_errs then added_vanc_errs = true; -- note that we've added this category table.messageinsert( z.newRawMessagemessage_tail, { set_error( msg'vancouver', {}, args true ):plain(} ) or msg; end
end
--[[--------------------------< E R R O R _ C O M M E N T >----------------------------------------------------
Wraps error messages --[[--------------------------< 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 itis in agreement with css markup according to the state 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 hiddenletters, digits, plus ("+"), period ("."), or hyphen ("-")returns true if it does, else false
]]
 local function error_commentis_scheme ( content, hidden scheme) return substitutescheme and scheme:match ( hidden and cfg'^%a[%a%d%+%.presentation['hidden%-error'] or cfg.presentation['visible-error*:'], content ); -- true if scheme is set and matches the pattern
end
--[[--------------------------< S E T _ E R R O R >--------------------------------------------------------------
Sets an error condition and returns the appropriate error message. The actual placement of the error message in the output isthe responsibility of the calling function.--[=[-------------------------< I S _ D O M A I N _ N A M E >--------------------------------------------------
]]local function set_error( error_id, arguments, raw, prefix, suffix ) local error_state = cfg.error_conditions[ error_id ]; prefix = prefix or ""; suffix = suffix or ""; if error_state == nil then error( cfg.messages['undefined_error'] ); elseif is_set( error_state.category ) then table.insert( z.error_categories, error_state.category ); end local message = substitute( error_state.message, arguments ); message = message .. " ([[" .. cfg.messages['help page link'] .. "#" .. error_state.anchor .. "|" .. cfg.messages['help page label'] .. "]])"; z.error_ids[ error_id ] = true; if in_array( error_id, { 'bare_url_missing_title', 'trans_missing_title' } ) and z.error_ids['citation_missing_title'] then return '', false; end message = table.concat({ prefix, message, suffix }); if raw == true then return message, error_state.hidden; end return error_comment( message, error_state.hidden );endDoes 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.5BNF defined here: https://tools.ietf.org/html/rfc4234Single character names are generally reserved; see https://tools.ietf.org/html/draft-[[--------------------------< A D D _ M A I N T _ C A T >-----------------------------------------------ietf-dnsind-iana-dns-01#page-15; see also [[Single-letter second-level domain]]list of tlds: https://www.iana.org/domains/root/db
Adds rfc952 (modified by rfc 1123) requires the first and last character of a category hostname to zbe a letter or a digit.maintenance_cats using names from Betweenthe configuration file with additional text if any.To prevent duplicationfirst and last characters the name may use letters, digits, and the added_maint_cats table lists the categories by key that have been added to z.maintenance_catshyphen.
]]Also allowed are IPv4 addresses. IPv6 not supported
local added_maint_cats = {} -- list domain is expected to be stripped of maintenance categories any path so that have been added to zthe last character in the last character of the tld.maintenance_cats tldlocal function add_maint_cat is two or more alpha characters. Any preceding '//' (key, argumentsfrom splitting a url with a scheme)will be stripped if here. Perhaps not added_maint_cats [key] then added_maint_cats [key] = true; -- note that we've added this category table.insert( z.maintenance_cats, substitute (cfgnecessary but retained incase it is necessary for IPv4 dot decimal.maint_cats [key], arguments)); -- make name then add to table endend
There are several tests: the first character of the whole domain name including subdomains must be a letter or a digit single-letter/digit second-[[--------------------------< A D D _ P R O P _ C A T >---------------------------------------------------level domains in the .org TLD 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
Adds returns true if domain appears to be a category to z.properties_cats using names from the configuration file with additional text if any.proper name and tld or IPv4 address, else false
]=]
local added_prop_cats function is_domain_name (domain) if not domain then return false; -- if not set, abandon end domain = {} domain:gsub ('^//', ''); -- list of property categories that strip '//' from domain name if present; done here so we only have been added to z.properties_catsdo it once local function add_prop_cat if not domain:match (key, arguments'^[%a%d]')then -- first character must be letter or digit return false; end -- Do most common case first if not added_prop_cats domain:match ('%f[%a%d][%a%d][%a%d%-]+[key%a%d] %.%a%a+$') then -- three or more character hostname.hostname or hostname.tld added_prop_cats return true; elseif domain:match ('%f[%a%d][key%a%d] = %.org$') then -- one character .org hostname return true; elseif domain:match ('%f[%a][qxz]%.com$') then -- assigned one character .com hostname (x.com times out 2015-12- note that we've added this category10) tablereturn true; elseif domain:match ('%f[%a][iq]%.net$') then -- assigned one character .insertnet hostname ( zq.properties_cats, substitute net registered but not active 2015-12-10) return true; elseif domain:match (cfg.prop_cats '%f[%a%d][key%a%d], arguments%.%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 -- make name two character hostname and tld return true; elseif domain:match ('^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?') then add to table -- IPv4 address return true; else return false;
end
end
--[[--------------------------< A D D _ V A N C _ E R R O R >----------------------------------------------------
Adds --[[--------------------------< I S _ U R L >------------------------------------------------------------------ returns true if the scheme and domain parts of a single Vancouver system error message url appear to be a valid url; else false. This function is the last step in the template's output regardless of how many error actually existvalidation process. This function is separate because there are cases thatTo prevent duplicationare not covered by split_url(), added_vanc_errs for example is_parameter_ext_wikilink() which is nil until an error message is emittedlooking for bracketted externalwikilinks.
]]
local added_vanc_errs; -- flag so we only emit one Vancouver error / categorylocal function add_vanc_error is_url (scheme, domain) if not added_vanc_errs is_set (scheme) then added_vanc_errs = true; -- note that we've added this categoryif scheme is set check it and domain table.insertreturn is_scheme ( z.message_tail, { set_errorscheme) and is_domain_name ( 'vancouver', {}, true domain) } ; else return is_domain_name (domain); -- scheme not set when url is protocol relative
end
end
--[[--------------------------< S P L I S T _ S C H E M E U R L >------------------------------------------------------------ Split a url into a scheme, authority indicator, and domain.If protocol relative url, return nil scheme and domain else return nil for both scheme and domain.
does this thing that purports to be a uri When not protocol relative, get scheme seem to be a valid scheme? , authority indicator, and domain. The scheme If there is checked to see if itan authority indicator (oneis in agreement with http://tools.ietf.org/htmlor more '/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 ("+"immediately following the scheme's colon), period ("."), or hyphen ("-")make sure that there are only 2.
returns true if it does, else falseStrip off any port and path;
]]
local function is_scheme split_url (schemeurl_str) return scheme and local scheme, authority, domain; url_str = url_str:match gsub ('^%a([%a%d])%+%.?/.*$', '%-]*:1'); -- true if scheme is set strip FQDN terminator and matches path (the patternendcapture 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-------------------------< 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?link= etc for properly formatted content: no wikilinks, no urls
Syntax defined here: http://tools.ietf.org/html/rfc1034#section-3.5BNF defined hereLink parameters are to hold the title of a wikipedia article so none of the WP: https://tools.ietf.org/html/rfc4234Single character names TITLESPECIALCHARACTERS are generally reserved; see httpsallowed://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/dbexcept the underscore which is used as a space in wiki urls and # which is used for section links
rfc952 (modified by rfc 1123) requires returns false when the first and last character value contains any of a hostname to be a letter or a digit. Betweenthe first and last these characters the name may use letters, digits, and the hyphen.
Also allowed When there are IPv4 addressesno 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). IPv6 not supported
domain is expected to be stripped of any path so that the last character in the last character of the tld. tldis two or more alpha characters. Any preceding '//' (from splitting a url with a scheme) will be strippedhere. Perhaps not necessary but retained incase it is necessary for IPv4 dot decimal.]]
There are several tests:local function link_param_ok (value) the first character of the whole local scheme, domain name including subdomains must be a letter or a digit; singleif value:find ('[<>%[%]|{}]') then -letter/digit second-level domains in the .org TLDif any prohibited characters return false; q, x, and z SL domains in the .com TLDend i and q SL domains in the .net TLD single-letter SL domains in the ccTLDs scheme, domain = split_url (where the ccTLD is two lettersvalue); -- get scheme or nil and domain or nil from url; two-character SL domains in gTLDs return not is_url (where the gTLD is two or more lettersscheme, domain); -- return true if value DOES NOT appear to be a valid url threeend --[[--------------------------< L I N K _ T I T L E _ O K >--------------------------------------------------plus-character SL domains in gTLDs  Use link_param_ok(where the gTLD is two or more letters)to validate |<param>-link= value and its matching |<title>= value. IPv4 dot|<title>= may be wikilinked but not when |<param>-decimal address format; TLD not allowedlink= has a value. This function emits an error message whenthat condition exists
returns true if domain appears to be a proper name and tld or IPv4 address, else false]]
]=]local function link_title_ok (link, lorig, title, torig)local orig;
local function is_domain_name if is_set (domainlink)then -- don't bother if link doesn't have a value if not domain link_param_ok (link) then -- check |<param>-link= markup orig = lorig; -- identify the failing link parameter return falseelseif not link_param_ok (title) then -- check |<title>-link= markup orig = torig; -- if not set, abandonidentify the failing link parameter end
end
domain = domain:gsub if is_set ('^//', ''orig); -- strip '//' from domain name if present; done here so we only have to do it oncethen if not domain:match table.insert( z.message_tail, { set_error('^[%a%d]bad_paramlink', orig)}) then ; -- first character must be letter url or digit return falsewikilink in |title= with |title-link=;
end
end   if domain:match ('%f--[%a%d][%a%d]%.org$') then -- one character .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 ('%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 ('^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?') then -- IPv4 address return true; else return false; endend-------< C H E C K _ U R L >------------------------------------------------------------
Determines whether a URL string appears to be valid.
--[[--------------------------< I S _ U R L >------------------------------------------------------------------First we test for space characters. If any are found, return false. Then split the url into scheme and domainportions, or for protocol relative (//example.com) urls, just the domain. Use is_url() to validate the twoportions of the url. If both are valid, or for protocol relative if domain is valid, return true, else false.
returns true if the scheme and domain parts of Because it is different from a standard url appear , and because this module used external_link() to be a valid url; else false.make external links This function is the last step in the validation processthat work for standard and news: links, we validate newsgroup names here. This function is separate because there are cases thatThe specification for a newsgroup nameare not covered by split_url(), for example is_parameter_ext_wikilink() which is looking for bracketted externalwikilinksat https://tools.ietf.org/html/rfc5536#section-3.1.4
]]
local function is_url check_url(scheme, domainurl_str ) if is_set nil == url_str:match (scheme"^%S+$") then -- if scheme is set check there are any spaces in |url=value it and domaincan't be a proper url return is_scheme (scheme) and is_domain_name (domain)false; else return is_domain_name (domain); -- scheme not set when url is protocol relative
end
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
--[[--------------------------< S P L I T _ U R L >------------------------------------------------------------
 
Split a url into a scheme, authority indicator, and domain.
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 (oneor more '/' characters following the scheme's colon), make sure that there are only 2.--[=[-------------------------< 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 firstnon-space characters following the opening bracket appear to be a url. The test will also find external wikilinksthat use protocol relative urls. Also finds bare urls.
local function split_url The frontier pattern prevents a match on interwiki links which are similar to scheme:path urls. The tests thatfind bracketed urls are required because the parameters that call this test (url_str) local schemecurrently |title=, |chapter=, authority|work=, domain; url_str and |publisher= url_str:gsub () may have wikilinks and there are articles or redirects like '(%a)/.*/Hus'so, '%1'); -- strip path information (the capture prevents false replacement of 'while uncommon, |title=[[//Hus]]is possible as might be [[en://')Hus]].
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 domain = nil; -- set to nil which will cause an error message end end return scheme, domain;end]
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; ------------------< L I N K _ P A R A M _ O K >---------------------------------------------------didn't find anything that is obviously a url end checks the content of |title-link= return is_url (scheme, |seriesdomain); -link=, |author-link= etc for properly formatted content: no wikilinks, no urlsreturn true if value appears to be a valid urlend
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.--[[-------------------------< C H E C K _ F O R _ U R L >-----------------------------------------------------
When there are no illegal characters, this function returns TRUE if value DOES NOT appear to be loop through a valid url (list of parameters and their values. Look at the|<param>-link= parameter is ok); else false when value appears to be a valid url (the |<param>-and if it has an external link= parameter is NOT ok), emit an error message.
]]
local function link_param_ok check_for_url (valueparameter_list) local scheme, domainerror_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:find ; 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 any prohibited charactersthere is an error message, display it return falsetable.insert( z.message_tail, { set_error( 'param_has_ext_link', {error_message}, true ) } );
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
--[[--------------------------< C H S A F E C K _ F O R _ U R L >------------------------------------------------------------
Determines whether a URL string appears to Escape sequences for content that will be valid. First we test used for space characters. If any are found, return false. Then split the url into scheme and domainportions, or for protocol relative (//example.com) urls, just the domain. Use is_url() to validate the twoportions of the url. If both are valid, or for protocol relative if domain is valid, return true, else false.URL descriptions
]]
local function check_urlsafe_for_url( url_str str ) if nil == url_strstr:match ("^%S+$[%[.-%]%]") ~= nil then -- if there are any spaces in |url=value it can't be a proper url return falsetable.insert( z.message_tail, { set_error( 'wikilink_in_url', {}, true ) } );
end
local scheme, domain;  schemereturn str:gsub( '[%[%]\n]', domain { ['['] = split_url (url_str)'&#91; -- get scheme or nil and domain or nil from url', [']'] = '&#93; ', return is_url (scheme, domain ['\n'] = ' ' } ); -- return true if value appears to be a valid url
end
--[[--------------------------< E X T E R N A L _ L I N K >----------------------------------------------------
--[=[-------------------------< I S _ P A R A M E T E R _ E X T _ W I K I L I N K >----------------------------Format an external link with error checking
Return true if a parameter value has a string that begins and ends with square brackets [ and ] and the firstnon-space characters following the opening bracket appear to be a url. The test will also find external wikilinksthat 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 thatfind bracketed urls are required because the parameters that call this test local function external_link(currently |title=URL, |chapter=label, |worksource ) local error_str =,""; if not is_set( label ) thenand |publisher label =URL; if is_set( source ) may have wikilinks and there are articles or redirects like then error_str = set_error( 'bare_url_missing_title', { wrap_style ('//Husparameter' so, while uncommonsource) }, |title=[false, " " ); else error( cfg.messages[//Hus]"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; endis possible as might be return table.concat({ "[[en://Hus", URL, " ", safe_for_url( label ), "]].", error_str });end ]=]--[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >--------------------------------------
local Categorize and emit an error message when the citation contains one or more deprecated parameters. The function is_parameter_ext_wikilink (value)includes thelocal scheme, domain;offending parameter name to the error message. Only one error message is emitted regardless of the number of deprecatedparameters in the citation.
value = value:gsub ('([^%s/])/[%a%d].*', '%1'); -- strip path information (the capture prevents false replacement of '//')
if value:match ('%f[%[]%[%a%S*:%S+.*%]') then local page_in_deprecated_cat; -- if ext wikilink with scheme and domain: [xxxx://yyyyy.zzz]sticky flag so that the category is added only once scheme, domain = value:match ('%f[%[]%[(%a%S*:)local function deprecated_parameter(%S+).*%]'name) elseif value:match ('%f[%[]%[//%S*%.%S+.*%]') if not page_in_deprecated_cat then -- if protocol relative ext wikilink: [//yyyyy.zzz] domain page_in_deprecated_cat = value:match ('%f[%[]%[//(%S*%.%S+).*%]')true; elseif value:match ('%a%S*:%S+') then -- if bare url with scheme; may have leading or trailing plain textnote that we've added this category schemetable.insert( z.message_tail, domain = value:match { set_error('(%a%S*:)(%S+)deprecated_params', {name}, true ); elseif value:match ('//%S*%.%S+') then -- if protocol relative bare url: //yyyyy.zzz; may have leading or trailing plain text domain = value:match ('//(%S*%.%S+)'} ); -- what is left should be the domain else return false; -- didn't find anything that is obviously a urladd error message
end
 
return is_url (scheme, domain); -- return true if value appears to be a valid url
end
--[[--------------------------< K E R N _ Q U O T E S >--------------------------------------------------------
--[[-------------------------< C H E C K _ F O R _ U R L >-----------------------------------------------------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.
loop through a list of parameters and their values. Look at the value and if it has an external linkCall this function for chapter titles, emit an error messagefor website titles, etc; not for book titles.
]]
local function check_for_url kern_quotes (parameter_liststr) local error_message cap= ''; for klocal cap2=''; cap, v in pairs cap2 = str:match (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 messagematch leading double or single quote but not double single quotes if is_set(error_messagecap) then -- once we've added the first portion of the error message ... error_message str =error_message substitute (cfg.. "presentation['kern-left'], {cap, "cap2}); -- ... add a comma space separator end error_message=error_message .. "&#124;" .. k .. "="; -- add the failed parameter end
end
  cap, cap2 = str:match ("^(.+[^\'])([\"\'])$") if is_set (error_messagecap) then -- done looping, if there is an error message, display it table.insertstr = substitute ( zcfg.message_tail, { set_error( presentation['param_has_ext_linkkern-right'], {error_message}cap, true ) cap2} );
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 shouldnot be italicized and may be written right-[[--------------------------< S A F E _ F O R _ I T A L I C S >--------------------------------------------to-left. The value supplied by |script-title= is concatenated onto Title after Title has been wrappedin italic markup.
Protects a string that will be Regardless of language, all values provided by |script-title= are wrapped in wiki italic markup '' <bdi>... ''</bdi> tags to isolate rtl languages from the English left to right.
Note: We cannot use <i> for italics, as the expected behavior for italics specified by ''|script-title= provides a unique feature...'' The value in the |script-title is that= may be prefixed with a two-character ISO639-1 language code and a colon:they will be inverted |script-title=ja:*** *** (i.e. unitalicizedwhere * represents a Japanese character) in Spaces between the two-character code and the colon and the resulting references. In addition, <i> colon and '' tend to interactthe first script character are allowed:poorly under Mediawiki's HTML tidy. |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 safe_for_italicsformat_script_value ( str 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(strlang) then return str''; -- script_value was just the prefix so return empty string end else -- if we get this far we have prefix and script if str:subname = mw.language.fetchLanguageName(1lang,1) == "'en" then str = "<span />" .. str); end -- get language name so that we can use it to categorize if str:subis_set (name) then -- is prefix a proper ISO 639-1language code? script_value = script_value:gsub ('^%l%l%s*:%s*',''); -- strip prefix from script --1is prefix one of these language codes? if in_array (lang, {'am', 'ar', 'bg', 'bs', 'dv', 'el', 'fa', 'he', 'hy', 'ja', 'ka', 'ko', 'ku', 'mk', 'ml', 'ps', 'ru', 'sd', 'sr', 'th', 'uk', 'ug', 'yi', 'zh'}) then add_prop_cat ('script_with_name', {name, lang}) else add_prop_cat ('script') end lang =' lang= "'" then str = str .. lang .. '"<span />"'; end -- convert prefix into a lang attribute else lang = ''; -- Remove newlines as they break italics.invalid so set lang to empty string return str:gsub( '\n', ' ' );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 F T E _ F O R _ U R L >------------------------------------------------------ Escape sequences for content that will be used for URL descriptions
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 safe_for_urlscript_concatenate ( str title, script) if str:matchis_set ( "%[%[.-%]%]" script) ~= nil then table.insertscript = format_script_value ( z.message_tailscript); -- <bdi> tags, lang atribute, categorization, { set_erroretc; returns empty string on error if is_set ( script) then title = title .. 'wikilink_in_url', {}, true ) } ).. script; -- concatenate title and script title end
end
return str:gsub( '[%[%]\n]', { ['['] = '&#91;', [']'] = '&#93;', ['\n'] = ' ' } )title;
end
--[[--------------------------< W R A P _ S T Y L E >----------------------------------------------------------
--[[--------------------------< W R A P _ M S G >-------------------------------------------------------------- Applies styling additional message text to various parametersparameter values. Supplied string is wrapped using a message_list configuration taking oneargument; protects italic styled parameters. Supports lower case text for {{citation}} templates. Additional text taken from citation_config.presentation messages - the reasonthis function is similar to but separate from wrap_msgwrap_style().
]]
local function wrap_style wrap_msg (key, str, lower)
if not is_set( str ) then
return "";
elseif in_array( key, { 'italic-title', 'trans-italic-title' } ) then
str = safe_for_italics( str );
end
if true == lower then
local msg;
msg = cfg.messages[key]:lower(); -- set the message to lower case before
return substitute( msg, str ); -- including template text
else
return substitute( cfg.messages[key], str );
end
end
return substitute( cfg.presentation[key], {str} );
end
--[[--------------------------< E X F O R M A T _ C H A P T E R N A L _ T I T L I N K E >----------------------------------------------------
Format an external link with the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=, and |chapter-url= into a single Chapter meta-parameter (chapter_url_source used for error checkingmessages).
]]
local function external_linkformat_chapter_title ( URLscriptchapter, labelchapter, source transchapter, chapterurl, chapter_url_source, no_quotes) local error_str chapter_error = ""''; if not is_set( label chapter) then label chapter = URL''; -- to be safe for concatenation else if is_set( source ) false == no_quotes then error_str chapter = set_errorkern_quotes ( chapter); -- if necessary, separate chapter title'bare_url_missing_title', { s leading and trailing quote marks from Module provided quote marks chapter = wrap_style ('parameterquoted-title', source) }, false, " " ); else error( cfg.messages["bare_url_no_origin"] chapter); end
end
if not check_url( URL ) then
error_str = set_error( 'bad_url', {wrap_style ('parameter', source)}, false, " " ) .. error_str;
end
return table.concat({ "[", URL, " ", safe_for_url( label ), "]", error_str });
end
--[[------------------------ chapter = script_concatenate (chapter, scriptchapter) --< E X T E R N A L _ L I N K _ I D bdi>----------------------------------------------tags, lang atribute, categorization, etc; must be done after title is wrapped
Formats a wiki style external link if is_set (transchapter) then transchapter = wrap_style ('trans-quoted-title', transchapter);]] local function external_link_id if is_set (optionschapter)then local url_string chapter = optionschapter .id. ' ' .. transchapter; if options.encode == true else -- here when transchapter without chapter or options.encode script-chapter chapter == nil thentranschapter; -- url_string chapter_error = mw' ' .uri.encodeset_error ( url_string 'trans_missing_title', {'chapter'}); end
end
return mw.ustring.format( '[[%s|%s]]%s[%s%s%s %s]',
options.link, options.label, options.separator or "&nbsp;",
options.prefix, url_string, options.suffix or "",
mw.text.nowiki(options.id)
);
end
if is_set (chapterurl) then chapter = external_link (chapterurl, chapter, chapter_url_source); --[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >--------------------------------------adds bare_url_missing_title error if appropriate end  return chapter .. chapter_error;end
Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes theoffending parameter name to the error message. Only one error message is emitted regardless of the number of deprecatedparameters in the citation.--[[--------------------------< H A S _ I N V I S I B L E _ C H A R S >----------------------------------------
]]This function searches a parameter's value for nonprintable or invisible characters. The search stops at thefirst match.
local page_in_deprecated_cat; -- sticky flag so that This function will detect the category visible replacement character when it is added only oncelocal function deprecated_parameter(name) if not page_in_deprecated_cat then page_in_deprecated_cat = true; -- note that we've added this category table.insert( zpart of the wikisource.message_tail, { set_error( 'deprecated_params', {name}, true ) } ); -- add error message endend
--[[--------------------------< K E R N _ Q U O T E S >--------------------------------------------------------Detects but ignores nowiki and math stripmarkers. Also detects other named stripmarkers (gallery, math, pre, ref)and identifies them with a slightly different error message. See also coins_cleanup().
Apply kerning to open Detects but ignores the space between character pattern that results from 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 isntransclusion of {{'t as wide as this example)Double single quotes (italic or bold wikimarkup) are not kerned}} templates.
Call Output of this function for chapter titlesis an error message that identifies the character or the Unicode group, or the stripmarkerthat was detected along with its position (or, for website titlesmulti-byte characters, etc; not for book titlesthe position of its first byte) in theparameter value.
]]
local function kern_quotes has_invisible_chars (strparam, v) local capposition =''; -- position of invisible char or starting position of stripmarker local cap2dummy; -- end of matching string; not used but required to hold end position when a capture is returned local capture; -- used by stripmarker detection to hold name of the stripmarker local i=''1; local stripmarker, apostrophe;
cap, cap2 capture = str:string.match ("^(v, '[\"\'%w%p ])([^\*'].+)"); -- match leading double or single quote but not double single quotesTest for values that are simple ASCII text and bypass other tests if true if is_set (cap) capture == v then -- if same there are no unicode characters str = substitute (cfg.presentation['kern-left'], {cap, cap2})return;
end
cap, cap2 while cfg.invisible_chars[i] do local char= str:match ("^(cfg.+invisible_chars[^\'i])([\"\'1])$") if is_set (cap) then -- the character or group name str local pattern= substitute (cfg.presentationinvisible_chars['kerni][2] --right']the pattern used to find it position, dummy, {capcapture = mw.ustring.find (v, cap2}pattern); -- see if the parameter value contains characters that match the pattern end return str;end if position then--[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >--------------------------- if 'nowiki' == capture or 'math' == capture or ('ref' == capture and 'quote' == param) then --nowiki, math, or quote param and ref stripmarker (not an error condition) if 'nowiki' == capture or 'math' == capture then --nowiki, math stripmarker (not an error condition) stripmarker = true; --set a flag elseif true == stripmarker and 'delete' == char then --because stripmakers begin and end with the delete char, assume that we've found one end of a stripmarker position = nil; --unset elseif 'apostrophe' == char then --apostrophe template uses &zwj;, hair space and zero-width space apostrophe = true;|script-title elseif true == holds title parameters that are not written in Latin based scripts: Chineseapostrophe and in_array (char, Japanese{'zero width joiner', Arabic'zero width space', Hebrew, etc. These scripts should'hair space'}) thennot be italicized and may be written right position = nil; -to-leftunset else local err_msg; if capture then err_msg = capture .. ' ' . The value supplied by |script-title= is concatenated onto Title after Title has been wrappedin italic markup.char; elseRegardless of language, all values provided by |script-title err_msg = are wrapped in <bdi>char .. ' ' ..'character'; end  table.</bdi> tags to isolate rtl languages from the English left to rightinsert( z.message_tail, { set_error( 'invisible_char', {err_msg, wrap_style ('parameter', param), position}, true ) } ); -- add error message return; -- and done with this parameter end end i=i+1; -- bump our index endend
|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 -[[--------------------------<bdiA R G U M E N T _ W R A P P E R > tag so that browsers canknow the language the tag contains. This may help the browser render the script more correctly. If the prefix is invalid, the lang attributeis not added. At this time there is no error message for this condition.----------------------------------------------
Supports |script-title= and |script-chapter=Argument wrapper. This function provides support for argument mapping defined in the configuration file so thatmultiple names can be transparently aliased to single internal variable.
TODO: error messages when prefix is invalid ISO639-1 code; when script_value has prefix but no script;
]]
local function format_script_value argument_wrapper(script_valueargs ) local langorigin =''{}; -- initialize to empty string local name; if script_value:matchreturn setmetatable('^%l%l%s*:') then -- if first 3 non-space characters are script language prefix{ lang ORIGIN = script_value:matchfunction('^(%l%l)%s*:%s*%S.*'self, k ) local dummy = self[k]; -- get force the language prefix or nil if there is no script if not is_set (lang) thenvariable to be loaded. return ''origin[k]; -- script_value was just the prefix so return empty string
end
-- if we get this far we have prefix and script }, { name __index = mw.language.fetchLanguageNamefunction ( langtbl, "en" k ); -- get language name so that we can use it to categorize if is_set (name) origin[k] ~= nil then -- is prefix a proper ISO 639-1 language code? return nil; end script_value local args, list, v = script_value:gsub ('^%l%l%s*:%s*'args, '')cfg.aliases[k]; -- strip prefix from script -- is prefix one of these language codes? if in_array type(lang, {list ) == 'artable'then v, 'bg'origin[k] = select_one( args, 'bs'list, 'dvredundant_parameters', ); if origin[k] == nil then origin[k] = 'el'; -- Empty string, 'fa', 'he', 'hy', 'ja', 'ka', 'ko', 'ku', 'mk', 'ps', 'ru', 'sd', 'sr', 'th', 'uk', 'ug', 'yi', 'zh'}) not nil end elseif list ~= nil then add_prop_cat ('script_with_name'v, {nameorigin[k] = args[list], lang})list;
else
add_prop_cat -- maybe let through instead of raising an error? -- v, origin[k] = args[k], k; error(cfg.messages['scriptunknown_argument_map'] );
end
lang -- Empty strings, not nil; if v == ' langnil then v ="' .. lang .cfg. defaults[k] or '" '; -- convert prefix into a lang attribute else lang origin[k] = ''; -- invalid so set lang to empty string end end script_value tbl = substitute rawset(cfg.presentation['bdi']tbl, {langk, script_value}v ); -- isolate in case script is rtl return v; end, return script_value});
end
--[[--------------------------< S C R V A L I P T _ C O N C A T E N D A T E >--------------------------------------------------------------Looks for a parameter's name in the whitelist.
Initially for |title= and |scriptParameters in the whitelist can have three values: true - active, supported parameters false -title=deprecated, this function concatenates those two parameter values after the script value has been supported parameters nil - unsupported parameterswrapped in <bdi> tags.
]]
local function script_concatenate validate(title, scriptname ) local name = tostring( name ); local state = whitelist.basic_arguments[ name ]; -- Normal arguments if is_set true == state then return true; end -- valid actively supported parameter if false == state then deprecated_parameter (scriptname) then; -- parameter is deprecated but still supported script return true; end -- Arguments with numbers in them name = format_script_value name:gsub(script"%d+", "#" ); -- <bdi> tags, lang atribute, categorization, etcreplace digit(s) with # (last25 becomes last# state = whitelist.numbered_arguments[ name ]; returns empty string on error if is_set (script) true == state thenreturn true; end -- valid actively supported parameter title if false == title .. ' ' .. scriptstate then deprecated_parameter (name); -- concatenate title and script titleparameter is deprecated but still supported endreturn true;
end
return titlefalse; -- Not supported because not found or name is set to nil
end
--[[--------------------------< N O W R A P _ M S G D A T E >-------------------------------------------------------- When date is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM---DD</span>. When date is DD MMMM YYYY or isMMMM DD, YYYY then wrap in nowrap span: <span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY
Applies additional message text to various parameter values. Supplied string is wrapped using a message_listconfiguration taking one argument. Supports lower case text for {{citation}} templates. Additional text takenfrom citation_config.messages - DOES NOT yet support MMMM YYYY or any of the reason this function is similar to but separate from wrap_style()date ranges.
]]
local function wrap_msg nowrap_date (key, str, lowerdate) if not is_set( str ) thenlocal cap=''; return "" local cap2=''; end if true == lower date:match("^%d%d%d%d%-%d%d%-%d%d$") then local msg; msg date = substitute (cfg.messagespresentation[key'nowrap1'], date); elseif date:match("^%a+%s*%d%d?,%s+%d%d%d%d$") or date:lowermatch ("^%d%d?%s*%a+%s+%d%d%d%d$"); -- set the message to lower case before then return substitutecap, cap2 = string.match ( msgdate, str "^(.*)%s+(%d%d%d%d)$"); -- including template text else return date = substitute( cfg.messagespresentation[key'nowrap2'], str {cap, cap2}); end return date;
end
--[[--------------------------< S E T _ T I T L E T Y P E >----------------------------------------------------
--[[-------------------------< I S _ A L I A S _ U S E D >----------------------------------------------------- This function is used by select_onesets default title types () equivalent to determine if one of a list of alias parameters is in the argument listcitation including |type=<default value>) for those templates that have defaults.provided by Also handles the template. Input: args – pointer special case where it is desirable to omit the arguments table title type from calling template alias – one of the list of possible aliases in the aliases lists from Module:Citation/CS1/Configuration index – for enumerated parameters, identifies which one enumerated – true/false flag used choose how enumerated aliases are examined value – value associated with an alias that has previously been selected; nil if not yet selected selected – the alias that has previously been selected; nil if not yet selected error_list – list of aliases that are duplicates of the alias already selected Returns: value – value associated with alias we selected or that was previously selected or nil if an alias not yet selected selected – the alias we selected or the alias that was previously selected or nil if an alias not yet selectedrendered citation (|type=none).
]]
local function is_alias_used set_titletype (argscite_class, alias, index, enumerated, value, selected, error_listtitle_type) if enumerated is_set(title_type) then -- is this a test for an enumerated parameters? alias if "none" == title_type then title_type = alias:gsub ('#', index)""; -- replace '#' with the value in indexif |type=none then type parameter not displayed else end alias = alias:gsub ('#', '')return title_type; -- remove '#' if it exists|type= has been set to any other value use that value
end
if is_set(argsreturn cfg.title_types [aliascite_class]) then -- alias is in the templateor ''s argument list if value ~= nil and selected ~= alias then -- if we have already selected one of the aliases local skip; for _, v in ipairs(error_list) do -- spin through the error list to see if weset template've added this alias if v == alias then skip = trues default title type; break; -- has been added so stop looking end end if not skip then -- has not been added so table.insert( error_list, alias ); -- add error alias to the error list end else value = args[alias]; -- not yet selected an alias, so select this one selected = alias; end end return value, selected; -- return newly selected alias, or previously selected aliasempty string for concatenation
end
--[[--------------------------< S H Y P H E L E C N _ T O _ O N E D A S H >----------------------------------------------------------
Chooses one matching parameter from Converts a list of parameters hyphen to consider. The list of parameters to consider is justnames. For parameters that may be enumerated, the position of the numerator in the parameter name is identifiedby the '#' so |author-last1= and |author1-last= are represented as 'author-last#' and 'author#-last'. Because enumerated parameter |<param>1= is an alias of |<param>= we must test for both possibilities.  Generates an error if more than one match is present.a dash
]]
local function select_onehyphen_to_dash( args, aliases_list, error_condition, index str ) local value = nil; -- the value assigned to the selected parameter local selected = ''; -- the name of the parameter we have chosen local error_list = {};  if index ~= nil then index = tostring(index); end  for _, alias in ipairsnot is_set( aliases_list str) do -- for each alias in the aliases list if aliasor str:match ('#'"[%[%]{}<>]" ) then -- if this alias can be enumerated if '1' ~== index nil then -- when index is 1 test for enumerated and non-enumerated aliases value, selected = is_alias_used (args, alias, index, false, value, selected, error_list); -- first test for non-enumerated alias end value, selected = is_alias_used (args, alias, index, true, value, selected, error_list); -- test for enumerated alias else value, selected = is_alias_used (args, alias, index, false, value, selected, error_list)return str; --test for non-enumerated alias end end  if #error_list > 0 and 'none' ~= error_condition then -- for cases where this code is used outside of extract_namesreturn str:gsub() local error_str = ""; for _, k in ipairs( error_list ) do if error_str ~= "" then error_str = error_str .. cfg.messages['parameter-separator'] end error_str = error_str .. wrap_style ('parameter', k); end if #error_list > 1 then error_str = error_str .. cfg.messages['parameter-final-separator']; else error_str = error_str .. cfg.messages['parameter-pair-separator']; end error_str = error_str .. wrap_style ('parameter', selected); table.insert( z.message_tail, { set_error( error_condition, {error_str}, true ) } ); end return value, selected;
end
--[[--------------------------< S A F O R M A T _ C H A P T E R _ T J O I T L E N >------------------------------------------------------------
Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=, and |chapter-url= into Joins a single Chapter meta-parameter (chapter_url_source used sequence of strings together while checking for error messages)duplicate separation characters.
]]
local function format_chapter_title safe_join(scriptchaptertbl, chapterduplicate_char ) --[[ Note: we use string functions here, transchapter, chapterurl, chapter_url_source, no_quotes)rather than ustring functions. This has considerably faster performance and should work correctly as long as the duplicate_char is strict ASCII. The strings in tbl may be ASCII or UTF8. local chapter_error = '';]]
if not is_set (chapter) thenlocal str = ''; -- the output string chapter local comp = ''; -- to be safe for concatenationwhat does 'comp' mean? local end_chr = ''; local trim; elsefor _, value in ipairs( tbl ) do if false value == no_quotes nil thenvalue = ''; end if str == '' then -- if output string is empty chapter str = kern_quotes (chapter)value; -- assign value to it (first time through the loop) elseif value ~= '' then if necessaryvalue:sub(1, separate chapter title1) == '<'s leading then -- Special case of values enclosed in spans and trailing quote marks from Module provided quote marksother markup. chapter comp = wrap_style value:gsub('quoted-title'"%b<>", chapter"" ); -- remove html markup (<span>string</span> -> string) end else comp = value; end -- typically duplicate_char is sepc chapter if comp:sub(1,1) == script_concatenate duplicate_char then -- is first charactier same as duplicate_char? why test first character? -- Because individual string segments often (chapter, scriptchapteralways?) begin with terminal punct for th -- <bdi> tags, lang atribute, categorization, preceding segment: 'First element' .. 'sepc next element' .. etc? trim = false; must be done after title is wrapped end_chr = str:sub(-1,-1); -- get the last character of the output string if is_set -- str = str .. "<HERE(transchapterenchr=" .. end_chr.. ") " -- debug stuff? if end_chr == duplicate_char then -- if same as separator transchapter str = wrap_style str:sub(1,-2); -- remove it elseif end_chr == "'trans" then -quoted-title', transchapter);if it might be wikimarkup if is_set str:sub(chapter-3,-1) == duplicate_char .. "''" then -- if last three chars of str are sepc'' chapter str = chapter str:sub(1, -4) .. "' ' .. transchapter"; -- remove them and add back '' else elseif str:sub(-5,-1) == duplicate_char .. "]]''" then - here when transchapter without chapter or script-chapterif last five chars of str are sepc]]'' chapter trim = transchaptertrue; -- why? why do this and next differently from previous? chapter_error elseif str:sub(-4,-1) == ' ' duplicate_char .. set_error ("]'trans_missing_title', {" then -- if last four chars of str are sepc]'chapter'} trim = true; -- same question end elseif end_chr == "]" then -- if it might be wikimarkup if str:sub(-3,-1)== duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink trim = true; elseif str:sub(-2,-1) == duplicate_char .. "]" then end-- if last two chars of str are sepc] external link trim = true; elseif str:sub(-4,-1) == duplicate_char .. "'']" then -- normal case when |url=something & |title=Title. trim = true; end elseif end_chr == " " then -- if last char of output string is a space if is_set str:sub(chapterurl-2,-1) == duplicate_char .. " " then -- if last two chars of str are <sepc><space> chapter str = external_link str:sub(chapterurl1, chapter, chapter_url_source-3); -- adds bare_url_missing_title error if appropriateremove them both end end
if trim then if value ~= comp then -- value does not equal comp when value contains html markup local dup2 = duplicate_char; if dup2:match( "%A" ) then dup2 = "%" .. dup2; end return chapter -- if duplicate_char not a letter then escape it value = value:gsub( "(%b<>)" .. dup2, "%1", 1 ) -- remove duplicate_char if it follows html markup else value = value:sub( 2, -1 ); -- remove duplicate_char when it is first character end end end str = str .. chapter_errorvalue; --add it to the output string end end return str;end  --[[--------------------------< H A I S _ I G O O D _ V A N V I S I B L E C _ C H N A R S M E >---------------------------------------- This function searches a parameter's value for nonprintable or invisible characters. The search stops at thefirst match.----
This function will detect For Vancouver Style, author/editor names are supposed to be rendered in Latin (read ASCII) characters. When a nameuses characters that contain diacritical marks, those characters are to converted to the visible replacement corresponding Latin character when it .When a name is part of written using a non-Latin alphabet or logogram, that name is to be transliterated into Latin characters.These things are not currently possible in this module so are left to the wikisourceeditor to do.
Detects but ignores nowiki This test allows |first= and math stripmarkers|last= names to contain any of the letters defined in the four Unicode Latin character sets [http://www.unicode.org/charts/PDF/U0000. Also detects other named stripmarkers (gallerypdf C0 Controls and Basic Latin] 0041–005A, math0061–007A [http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] 00C0–00D6, pre00D8–00F6, ref)00F8–00FF [http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended-A] 0100–017Fand identifies them with a slightly different error message [http://www.unicode. See also coins_cleanup()org/charts/PDF/U0180.pdf Latin Extended-B] 0180–01BF, 01C4–024F
Detects but ignores the character pattern that results from the transclusion of {{'}} templates|lastn= also allowed to contain hyphens, spaces, and apostrophes.(http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)|firstn= also allowed to contain hyphens, spaces, apostrophes, and periods
Output At the time of this function is an error message that identifies writing, I had to write the character or 'if nil == mw.ustring.find ...' test ouside of the Unicode group, or the stripmarkercode editor and paste it herethat was detected along with its position (or, for multi-byte characters, because the code editor gets confused between character insertion point and cursor position of its first byte) in theparameter value.
]]
local function has_invisible_chars is_good_vanc_name (paramlast, vfirst) local position if nil = = mw.ustring.find (last, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%']*$") or nil == mw.ustring.find (first, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]*$") then add_vanc_error (); -- position of invisible char or starting position of stripmarker local dummy return false; -- end not a string of matching stringlatin characters; not used but Vancouver required to hold end position when a capture is returnedRomanization local captureend; -- used by stripmarker detection to hold name of the stripmarker local i=1return true; local stripmarker, apostrophe;end --[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------
while cfg.invisible_chars[i] do local char=cfg.invisible_chars[i][1] -- the character or group name local pattern=cfg.invisible_chars[i][2] -- the pattern used Attempts to convert names to find it position, dummy, capture = mw.ustring.find (v, pattern) -- see if the parameter value contains characters that match the pattern if position then-- if 'nowiki' == capture or 'math' == capture or ('ref' == capture and 'quote' == param) then -- nowiki, math, or quote param and ref stripmarker (not an error condition) if 'nowiki' == capture or 'math' == capture then -- nowiki, math stripmarker (not an error condition) stripmarker = true; -- set a flag elseif true == stripmarker and 'delete' == char then -- because stripmakers begin and end with the delete char, assume that we've found one end initials in support of a stripmarker position = nil; -- unset elseif 'apostrophe' == char then -- apostrophe template uses &zwj;, hair space and zero-width space apostrophe = true; elseif true == apostrophe and in_array (char, {'zero width joiner', 'zero width space', 'hair space'}) then position = nil; |name-list- unset else local err_msg; if capture then err_msg = capture .. ' ' .. char; else err_msg format= char .. ' ' .vanc. 'character'; end
tableNames in |firstn= may be separated by spaces or hyphens, or for initials, a period. See http://www.ncbi.nlm.nih.insert( zgov/books/NBK7271/box/A35062/.message_tail, { set_error Vancouver style requires family rank designations ( 'invisible_char'Jr, {err_msgII, wrap_style ('parameter'III, parametc)to be rendered as Jr, position}2nd, 3rd, true etc. This form is notcurrently supported by this code so correctly formed names like Smith JL 2nd are converted to Smith J2. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/. This function uses ustring functions because firstname initials may be any of the unicode Latin characters accepted by is_good_vanc_name () } . ]] local function reduce_to_initials(first) if mw.ustring.match(first, "^%u%u$")then return first end; --when first contains just two upper- add error messagecase letters, nothing to do return local initials = {} local i = 0; -- and done with this parametercounter for number of initials end for word in mw.ustring.gmatch(first, "[^%s%.%-]+") do -- names separated by spaces, hyphens, or periods endtable.insert(initials, mw.ustring.sub(word,1,1)) -- Vancouver format does not include full stops. i=i+1; -- bump our indexthe counter if 2 <= i then break; end -- only two initials allowed in Vancouver system; if 2, quit
end
return table.concat(initials) -- Vancouver format does not include spaces.
end
--[[--------------------------< L I S T _ P E O P L E >-------------------------------------------------------
--[[--------------------------< A R G U M E N T _ W R A P P E R >---------------------------------------------- Argument wrapperFormats a list of people (e. This function provides support for argument mapping defined in the configuration file so thatmultiple names can be transparently aliased to single internal variableg.authors / editors)
]]
local function argument_wrapperlist_people( args control, people, etal, list_name) -- TODO: why is list_name here? not used in this function local sep; local namesep; local format = control.format local maximum = control.maximum local origin lastauthoramp = control.lastauthoramp; local text = {}; return setmetatable({if 'vanc' == format then -- Vancouver-like author/editor name styling? ORIGIN sep = function( self', k )'; -- name-list separator between authors is a comma local dummy namesep = self[k]' '; --force the variable to be loaded.last/first separator is a space else return origin[k] sep = ';' -- name-list separator between authors is a semicolon namesep = ', ' -- last/first separator is <comma><space> end }, { __index = function if sep:sub( tbl-1, k -1) if origin[k] ~= nil " " thensep = sep .. " " end if is_set (maximum) and maximum < 1 then return "", 0; end return nil-- returned 0 is for EditorCount;not used for authors for i,person in ipairs(people) do if is_set(person.last) then endlocal mask = person.mask local one local args, list, v sep_one = args, cfg.aliases[k]sep; if typeis_set ( list maximum) and i > maximum then etal =true; break; elseif (mask ~= 'table' nil) then v, origin[k] local n = select_onetonumber( args, list, 'redundant_parameters' mask); if origin[k] =(n ~= nil ) then origin[k] one = ''string.rep("&mdash; -- Empty string", not niln) endelse elseif list ~ one = nil thenmask; v, origin[k] sep_one = args[list], list" "; end
else
one = person.last local first = person.first if is_set(first) then if ( "vanc" == format ) then -- maybe let through instead of raising an error?if vancouver format one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/) if not person.corporate and is_good_vanc_name (one, first) then -- v, origin[k] and name is all Latin characters; corporate authors not tested first = reduce_to_initials(first) -- attempt to convert first name(s) to initials end end one = args[k], k;one .. namesep .. first end errorif is_set( cfgperson.link) and person.link ~= control.messagespage_name then one = "[[" .. person.link .. "|" .. one .. "]]" -- link author/editor if this page is not the author'unknown_argument_maps/editor'] );s page end
end
table.insert( text, one ) table.insert( text, sep_one ) end end  local count = #text / 2; -- Empty strings, not nil;(number of names + number of separators) divided by 2 if count > 0 then if v == nil count > 1 and is_set(lastauthoramp) and not etal then v = cfg.defaults text[k#text-2] or ''= " & "; -- replace last separator with ampersand text end origin text[k#text] = ''nil; -- erase the last separator end tbl local result = rawsettable.concat( tbl, k, v text) -- construct list if etal and is_set (result);then -- etal may be set by |display-authors=etal but we might not have a last-first list return v result = result .. sep .. ' ' .. cfg.messages['et al']; -- we've go a last-first list and etal so add et al. end, }); return result, count
end
--[[--------------------------< V A L N C H O R _ I D A T E >--------------------------------------------------------------Looks for Generates a parameter's CITEREF anchor ID if we have at least one name in the whitelistor a date. Otherwise returns an empty string.
Parameters in namelist is one of the whitelist can have three values: true contributor- active, supported parameters false author- deprecated, supported parameters nil or editor- unsupported parameters ]]name lists chosen in that order. year is Year or anchor_year.
]]local function validateanchor_id ( name namelist, year) local name names= tostring{}; -- a table for the one to four names and year for i,v in ipairs ( name namelist);do -- loop through the list and take up to the first four last names local state names[i] = whitelistv.basic_arguments[ name ];last -- Normal arguments if true i == state 4 then return true; break end -- valid actively supported parameter if false == state four then deprecated_parameter (name); -- parameter is deprecated but still supported return true;done
end
-- Arguments with numbers in them name = name:gsubtable.insert ( "%d+"names, "#" year); -- replace digit(s) with # (last25 becomes last#add the year at the end state local id = whitelisttable.numbered_arguments[ name ]concat(names); -- concatenate names and year for CITEREF id if true == state is_set (id) then -- if concatenation is not an empty string return true"CITEREF" .. id; end -- valid actively supported parameteradd the CITEREF portion if false == state thenelse deprecated_parameter (name)return ''; -- parameter is deprecated but still supported return truean empty string;no reason to include CITEREF id in this citation
end
return false; -- Not supported because not found or name is set to nil
end
-- Formats a wiki style internal linklocal function internal_link_id(options) return mw.ustring.format( '[[%s|%s]]%s[[%s%s%s|%s]]', options.link, options.label, options.separator or "&nbsp;", options.prefix, options.id, options.suffix or "", mw.text.nowiki(options.id) );end--------------------------< N A M E _ H A S _ E T A L >----------------------------------------------------
Evaluates the content of author and editor name parameters for variations on the theme of et al. If found,
the et al. is removed, a flag is set to true and the function returns the modified name and the flag.
This function never sets the flag to false but returns it's previous state because it may have been set byprevious passes through this function or by the parameters |display-authors=etal or |display-[[--------------------------< N O W R A P _ D A T E >--------------------------------------------------------editors=etal
When date is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM-DD</span>. When date is DD MMMM YYYY or isMMMM DD, YYYY then wrap in nowrap span: <span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY]]
DOES NOT yet support MMMM YYYY or any of the date ranges.local function name_has_etal (name, etal, nocat)
]] local function nowrap_date if is_set (datename)then -- name can be nil in which case just return local capetal_pattern ="[;,]? *[\"']*%f[%a][Ee][Tt] *[Aa][Ll][%.\"']*$" -- variations on the ';et al' theme local cap2others_pattern =''"[;,]? *%f[%a]and [Oo]thers"; -- and alternate to et al. if datename:match("^%d%d%d%d%etal_pattern) then -- variants on et al. name = name:gsub (etal_pattern, ''); -- if found, remove etal = true; --%d%d%set flag (may have been set previously here or by |display-%d%d$"authors=etal) if not nocat then -- no categorization for |vauthors= date = substitute add_maint_cat (cfg.presentation['nowrap1etal'], date); -- and add a category if not already added end elseif datename:match("^%a+%s*%d%d?others_pattern) then -- if not 'et al.',%s+%d%d%d%d$") or date:match ("^%d%dthen 'and others'?%s*%a+%s+%d%d%d%d$") then cap, cap2 name = string.match name:gsub (dateothers_pattern, "^(.*'')%s+; -- if found, remove etal = true; -- set flag (%d%d%d%d)$"may have been set previously here or by |display-authors=etal); date if not nocat then -- no categorization for |vauthors= substitute add_maint_cat (cfg.presentation['nowrap2etal'], {cap, cap2}); -- and add a category if not already added end end
end
return datename, etal; --
end
--[[--------------------------< IS E X T R A C T _ V N A L I D _ I M E S X N >-----------------------------------------------------Gets name list from the input arguments
ISBN-10 Searches through args in sequential order to find |lastn= and ISSN validator code calculates checksum across all isbn/issn digits including the check digit. ISBN-13 is checked in check_isbn|firstn= parameters (or their aliases), and their matching link and mask parameters.If the number is valid the result will be 0. Before calling this functionStops searching when both |lastn= and |firstn= are not found in args after two sequential attempts: found |last1=, |last2=, issbn/issn must be checked for length and stripped of dashes,|last3= but doesn'tspaces find |last4= and other non-isxn characters|last5= then the search is done.
]]This function emits an error message when there is a |firstn= without a matching |lastn=. When there are 'holes' in the list of last names, |last1= and |last3=are present but |last2= is missing, an error message is emitted. |lastn= is not required to have a matching |firstn=.
local function is_valid_isxn (isxn_str, len) local temp = 0; isxn_str = { isxn_str:byte(1, len) }; -- make a table When an author or editor parameter contains some form of byte values '0' → 0x30 .et al. '9' → 0x39, the 'Xet al.' → 0x58 len = len+1; -- adjust to be is stripped from the parameter and a loop counter for i, v in ipairsflag ( isxn_str etal) do -- loop through all of the bytes and calculate the checksumreturned if v == string.bytethat will cause list_people( "X" ) then -- if checkdigit is X (compares to add the byte value of static 'Xet al.' which is 0x58) temp = temp + 10*( len - i ); -- it represents 10 decimal else temp = temp + tonumber( stringtext from Module:Citation/CS1/Configuration.char(v) )*(len-i); end end return temp % 11 == 0; -- returns true if calculation result is zeroend  --[[--------------------------< IS _ V A L I D _ I S X N _ 1 3 >---------------------------------------------- ISBN-13 and ISMN validator code calculates checksum across all 13 isbn/ismn digits including This keeps 'et al.' out of the check digit.If the number is valid, the result will be 0template's metadata. Before calling When this functionoccurs, isbn-13/ismn must be checked for lengthand stripped of dashes, spaces and other non-isxn-13 charactersthe page is added to a maintenance category.
]]
local function is_valid_isxn_13 extract_names(isxn_strargs, list_name) local tempnames =0{}; -- table of names local last; -- individual name components local first; local link; local mask; isxn_str local i = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39loop counter/indexer for i, v in ipairs( isxn_str ) do temp local n = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) )1; -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digitoutput table indexer endlocal count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors) return temp % 10 =local etal= 0false; -- sum modulo 10 is zero return value set to true when isbn-13/ismn is correctendwe find some form of et al. in an author parameter
local err_msg_list_name = list_name:match ("(%w+)List") .. 's list'; --modify AuthorList or EditorList for use in error messages if necessary while true do last = select_one( args, cfg.aliases[[--------------------------< C H E C K _ I S B N >------------------------------------------------------list_name .. '-Last'], 'redundant_parameters', i ); --search through args for name components beginning at 1 first = select_one( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ); link = select_one( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ); mask = select_one( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i );
Determines whether an ISBN string is valid last, etal = name_has_etal (last, etal, false); -- find and remove variations on et al. first, etal = name_has_etal (first, etal, false); -- find and remove variations on et al.
]] if first and not last then -- if there is a firstn without a matching lastn table.insert( z.message_tail, { set_error( 'first_missing_last', {err_msg_list_name, i}, true ) } ); -- add this error message elseif not first and not last then -- if both firstn and lastn aren't found, are we done? count = count + 1; -- number of times we haven't found last and first if 2 <= count then -- two missing names and we give up break; -- normal exit or there is a two-name hole in the list; can't tell which end else -- we have last with or without a first link_title_ok (link, list_name:match ("(%w+)List"):lower() .. '-link' .. i, last, list_name:match ("(%w+)List"):lower() .. '-last' .. i); -- check for improper wikimarkup
local function check_isbn( isbn_str ) if nil ~= isbn_str:match(" names[^%s-0-9Xn]") then return false; end -- fail if isbn_str contains anything but digits= {last = last, hyphensfirst = first, or the uppercase X isbn_str link = isbn_str:gsub( "-"link, "" ):gsub( " "mask = mask, "" )corporate=false}; -- remove hyphens and spaces local len add this name to our names list (corporate for |vauthors= isbn_str:len(only); if len ~= 10 and len ~ n = 13 then return falsen + 1; -- point to next location in the names table end  if len 1 == 10 count then -- if the previous name was missing if isbn_str:match table.insert( "^%d*X?$" ) == nil then return false; end return is_valid_isxnz.message_tail, { set_error(isbn_str'missing_name', 10{err_msg_list_name, i-1}, true ) } ); -- add this error message else end local temp count = 0; -- reset the counter, we're looking for two consecutive missing names if isbn_str:match( "^97[89]%d*$" ) end i == nil then return falsei + 1; end -- isbn13 begins with 978 or 979; ismn begins with 979 return is_valid_isxn_13 (isbn_str);point to next args location
end
return names, etal; -- all done, return our list of names
end
--[[--------------------------< C H G E C K T _ I S M N O 6 3 9 _ C O D E >------------------------------------------------------------
Determines whether Validates language names provided in |language= parameter if not an ISMN string is validISO639-1 code. Similar to isbnHandles the special case that is Norwegian whereISO639-13, ismn 1 code 'no' is 13 digits begining 979-0-... and uses thesame check digit calculations. See httpmapped to language name 'Norwegian Bokmål' by Extention://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdfsection 2, pages 9–12CLDR.
]]Returns the language name and associated ISO639-1 code. Because case of the source may be incorrect or different from the case that Wikimediauses, the name comparisons are done in lower case and when a match is found, the Wikimedia version (assumed to be correct) is returned alongwith the code. When there is no match, we return the original language name string.
local function ismn mw.language.fetchLanguageNames(id)will return a list of languages that aren't part of ISO639-1. Names that aren't ISO639-1 but that are included local handler in the list will be found if that name is provided in the |language= cfgparameter.id_handlers[ For example, if |language=Samaritan Aramaic, that name will befound with the associated code 'ISMNsam'];, not an ISO639-1 code. When names are found and the associated code is not two characters, this function local text; local valid_ismn = true;returns only the Wikimedia language name.
id=idAdapted from code taken from Module:gsub( "[%sCheck ISO 639-–]", "" ); -- strip spaces, hyphens, and endashes from the ismn1.
]] local function get_iso639_code (lang) if 13 ~'norwegian' = id= lang:lenlower() or id:match( "^9790%d*$" ) == nil then -- ismn must be 13 digits and begin 9790 valid_ismn = false; elsespecial case related to Wikimedia remap of code 'no' at Extension:CLDR valid_ismn=is_valid_isxn_13 (id)return 'Norwegian', 'no'; -- validate ismnMake sure rendered version is properly capitalized
end
 
-- text = internal_link_id({link = handler.link, label = handler.label, -- use this (or external version) when there is some place to link to
-- prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
text="[[" .. handler.link .. "|" .. handler.label .. "]]" .. handler.separator .. id; -- because no place to link to yet
 
if false == valid_ismn then
text = text .. ' ' .. set_error( 'bad_ismn' ) -- add an error message if the issn is invalid
end
local languages = mw.language.fetchLanguageNames('en', 'all') -- get a list of language names known to Wikimedia -- ('all' is required for North Ndebele, South Ndebele, and Ojibwa) local langlc = mw.ustring.lower(lang); -- lower case version for comparisons for code, name in pairs(languages) do -- scan the list to see if we can find our language if langlc == mw.ustring.lower(name) then if 2 ~= code:len() then -- ISO639-1 codes only return textname; -- so return the name but not the code end return name, code; -- found it, return name to ensure proper capitalization and the ISO639-1 code end end return lang; -- not valid language; return language in original case and nil for ISO639-1 code
end
--[[--------------------------< I S S L A N G U A G E _ P A R A M E T E R >----------------------------------------------------------------------
Validate and format an issnGet language name from ISO639-1 code value provided. This If a code fixes is valid use the case where an editor has included an ISSN in returned name; if not, then use the citation but has separated value that was provided with the two groups of fourdigits with a spacelanguage parameter. When that condition occurred, the resulting link looked like this:
|issn=0819 4327 gives: [http://wwwThere is an exception.worldcat.org/issn/0819 4327 0819 4327] There are three ISO639-- can't have spaces in an external link This code now prevents that by inserting a hyphen at the issn midpoint. It also validates the issn 1 codes for length and makes sure that the checkdigit agreeswith the calculated valueNorewegian language variants. Incorrect length There are two official variants: Norwegian Bokmål (8 digitscode 'nb'), characters other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check issnerror messageNorwegian Nynorsk (code 'nn'). The issn third, code 'no', is always displayed with a hyphendefined by ISO639-1 as 'Norwegian' though in Norway this is pretty much meaningless. However, it appearsthat on enwiki, even if editors are for the issn was given as a single group most part unaware of 8 digitsthe nb and nn variants (compare page counts for these variants at Category:Articles with non-English-language external links.
]]Because Norwegian Bokmål is the most common language variant, Media wiki has been modified to return Norwegian Bokmål for ISO639-1 code 'no'. Here we undo that andreturn 'Norwegian' when editors use |language=no. We presume that editors don't know about the variants or can't descriminate between them.
local function issn(id) local issn_copy = id; See Help talk:Citation Style_1#An ISO 639-- save a copy of unadulterated issn; use this version for display if issn does not validate local handler = cfg.id_handlers['ISSN']; local text; local valid_issn = true;1 language name test
idWhen |language=idcontains a valid ISO639-1 code, the page is assigned to the category for that code: Category:gsubNorwegian-language sources ( "[%s-–]", "" no); ifthe page is a mainspace page and the ISO639-- strip spaces1 code is not 'en'. Similarly, hyphensif the parameter is |language=Norwegian, and endashes from it will be categorized in the issnsame way.
if 8 ~This function supports multiple languages in the form |language= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the issn: 8 digits longnb, French, containing only 0-9 or X in th where the last position valid_issn=false; -- wrong length language names or improper character else valid_issn=is_valid_isxn(id, 8); -- validate issn endcodes are separated from each other by commas.
if true == valid_issn then id = string.sub( id, 1, 4 ) .. "-" .. string.sub( id, 5 ); -- if valid, display correctly formatted version else id = issn_copy; -- if not valid, use the show the invalid issn with error message end text = external_link_id({link = handler.link, label = handler.label, prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) if false == valid_issn then text = text .. ' ' .. set_error( 'bad_issn' ) -- add an error message if the issn is invalid end return textend]]
local function language_parameter (lang) local code; --[[--------------------------< A M A Z O N >-----------------------------------------------------------the ISO639-1 two character code local name; --the language name local language_list = {}; --table of language names to be rendered local names_table = {}; --table made from the value assigned to |language=
Formats a link to Amazon names_table = mw. Do simple error checking: asin must be mix of 10 numeric or uppercase alphacharacterstext. If a mixsplit (lang, '%s*, first character must be uppercase alpha%s*'); if all numeric, asins must be 10 -digitisbn. If 10-digit isbn, add names should be a maintenance category so a bot or awb script can replace |asin= with |isbn=.Error message if not 10 characters, if not isbn10, if mixed and first character is a digit.comma separated list
]] for _, lang in ipairs (names_table) do -- reuse lang
local function amazon(id, domain) local err_cat = ""  if not idlang:match("'^[%da%u][a%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u]$"-') then err_cat = ' ' .. set_error ('bad_asin'); -- asin is not a mix of 10 uppercase alpha and numeric charactersstrip ietf language tags from code else if id lang = lang:match("^'(%da%da)%d%d%d%d%d%d%d[%dX]$"-') then -- if 10keep only 639-digit numeric (or 9 digits 1 code portion to lang; TODO: do something with terminal X)3166 alpha 2 country code? end if check_isbn2 == lang:len( id ) then -- see if asin value is isbn10 add_maint_cat ISO639-1 language code are 2 characters ('ASIN'fetchLanguageName also supports 3 character codes); elseif not is_set (err_cat) then err_cat name = ' ' mw.language. set_error fetchLanguageName('bad_asin'); -- asin is not isbn10 end elseif not idlang:matchlower(), "^%u[%d%u]+$en") then err_cat = ' ' .. set_error ('bad_asin'); --get ISO 639- asin doesn't begin with uppercase alpha1 language name if Language is a proper code
end
end
if not is_set(domain) then
domain = "com";
elseif in_array (domain, {'jp', 'uk'}) then -- Japan, United Kingdom
domain = "co." .. domain;
elseif in_array (domain, {'au', 'br', 'mx'}) then -- Australia, Brazil, Mexico
domain = "com." .. domain;
end
local handler = cfg.id_handlers['ASIN'];
return external_link_id({link=handler.link,
label=handler.label, prefix=handler.prefix .. domain .. "/dp/",
id=id, encode=handler.encode, separator = handler.separator}) .. err_cat;
end
 
--[[--------------------------< A R X I V >--------------------------------------------------------------------
 
See: http://arxiv.org/help/arxiv_identifier
 
format and error check arXiv identifier. There are three valid forms of the identifier:
the first form, valid only between date codes 9108 and 0703 is:
arXiv:<archive>.<class>/<date code><number><version>
where:
<archive> is a string of alpha characters - may be hyphenated; no other punctuation
<class> is a string of alpha characters - may be hyphenated; no other punctuation
<date code> is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01
first digit of YY for this form can only 9 and 0
<number> is a three-digit number
<version> is a 1 or more digit number preceded with a lowercase v; no spaces (undocumented)
the second form, if is_set (name) then -- if Language specified a valid from April 2007 through December 2014 is:ISO639-1 code arXiv:<date code>.<number><version>where= lang: <date code> is four digits in the form YYMM where YY is the last two digits of the fourlower(); -digit year and MM is the month number January = 01 <number> is a four-digit number <version> is a 1 or more digit number preceded with a lowercase v; no spacessave it elsethe third form name, valid from January 2015 is: arXiv:<date code>.<number><version>where: <date code> and <version> are as defined for 0704= get_iso639_code (lang); -1412 <number> is a five-digit number]] local function arxiv attempt to get code from name (id, classassign name here so that we are sure of proper capitalization) local handler = cfg.id_handlers['ARXIV']; local year, month, version; local err_cat = ''; local text; end
if id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%d$") or id:matchis_set ("^%a[%a%.%-]+/[90]%d[01]%d%d%d%dv%d+$"code) then -- test for the 9108-0703 format w/ & w/o version year, month if 'no' = id:match("^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d[v%d]*$"); year = tonumber(year); month code then name = tonumber(month)'Norwegian' end; if ((not (90 < year or 8 > year)) or (1 > month or 12 < month)) or -- if invalid year or invalid monthoverride wikimedia when code is 'no' ((91 == year and 7 > month) or (7 =if 'en' ~= year and 3 < month)) code then -- if years ok, are starting and ending months ok?English not the language err_cat = add_prop_cat (' foreign_lang_source' .. set_error, {name, code}) end else add_maint_cat ( 'bad_arxivunknown_lang' ); -- set error messageadd maint category if not already added
end
elseif id:match("^%d%d[01]%d% table.%d%d%d%d$") or id:matchinsert ("^%d%d[01]%d%.%d%d%d%dv%d+$") then -- test for the 0704-1412 w/ & w/o version yearlanguage_list, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d[v%d]*$"name); year name = tonumber(year)''; -- so we can reuse it end month code = tonumber(month);#language_list -- reuse code as number of languages in the list if ((7 > year) or (14 < year) or (1 2 > month or 12 < month)) or -- is year invalid or is month invalid? (doesn't test for future years) ((7 == year) and (4 > month)) code then --or -- when year is 07, is month invalid (before April)? err_cat name = ' ' .table. set_errorconcat ( language_list, 'bad_arxivand ' ); -- set error message endinsert '<space>and<space>' between two language names elseif id:match("^%d%d[01]%d%.%d%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%d%dv%d+$") 2 < code then -- test for the 1501- format w/ & w/o version year, month = id:match("^(%d%d)(language_list[01code]%d)%= 'and ' ..%d%d%d%d%dlanguage_list[v%dcode]*$"); year = tonumber(year); month = tonumber(month); if ((15 > year) or (1 > month or 12 < month)) then -- is year invalid or is month invalid? (doesnprepend last name with 't test for future years) err_cat = ' ' .. set_error( and<space>'bad_arxiv' ); -- set error message end else err_cat name = ' ' .table. set_errorconcat ( language_list, 'bad_arxiv, ' ); -- arXiv id doesnand concatenate with '<comma><space>'t match any formatseparators
end
  text = external_link_id({link = handler.link, label = handler.label, prefix=handler.prefix,id=id,separatorif 'English' =handler.separator, encode=handler.encode}) .. err_cat;  if is_set (class) name then class = ' [[' .. '//arxiv.org/archive/' .. class .. ' ' .. class .. ']]'; -- external link within square brackets, not wikilink else class = return ''; -- if one language and that language is English return an empty string for concatenation(no annotation)
end
return text (" " .. classwrap_msg ('language', name)); -- otherwise wrap with '(in ...)'
end
--[[lccn normalization (http://www.loc.gov/marc/lccn-namespace.html#normalization)-------------------------< S E T _ C S 1. Remove all blanks.2. If there is a forward slash (/) in the string, remove it, and remove all characters to the right of the forward slash.3. If there is a hyphen in the string: a. Remove it._ S T Y L E >---------------------------------------------------- b. Inspect the substring following (to the right of) the (removed) hyphen. Then (and assuming that steps 1 and 2 have been carried out): 1Set style settings for CS1 citation templates. All these characters should be digits, Returns separator and there should be six or less. (not done in this function) 2. If the length of the substring is less than 6, left-fill the substring with zeroes until the length is six.postscript settings
Returns a normalized lccn for lccn() to validate. There is no error checking (step 3.b.1) performed in this function.
]]
local function normalize_lccn set_cs1_style (lccnps) lccn = lccn:gsub if not is_set ("%s", ""ps); then -- 1. strip whitespaceunless explicitely set to something  if nil ~ ps = string'.find (lccn,'/') then lccn = lccn:match ("(.-)/"); -- 2. remove forward slash and all character to terminate the right of itrendered citation with a period
end
return '.', ps; -- separator is a full stop
end
 
--[[--------------------------< S E T _ C S 2 _ S T Y L E >----------------------------------------------------
 
Set style settings for CS2 citation templates. Returns separator, postscript, ref settings
local prefix local suffix prefix, suffix = lccn:match ("(.+)%-(.+)"); -- 3.a remove hyphen by splitting the string into prefix and suffix]]
local function set_cs2_style (ps, ref) if nil ~= suffix not is_set (ps) then -- if there was a hyphen suffix|postscript=string.rep("0"has not been set, 6-string.len (suffix)) .. suffix; -- 3.b.2 left fill the suffix with 0s if suffix length less than 6set cs2 default lccnps =prefix..suffix''; -- reassemble the lccnmake sure it isn't nil
end
if not is_set (ref) then -- if |ref= is not set return lccn ref = "harv"; -- set default |ref=harv
end
return ',', ps, ref; -- separator is a comma
end
--[[Format LCCN link and do simple error checking. LCCN is a character string 8-12 characters long. The length of the LCCN dictates the character type of the first 1-3 characters; therightmost eight are always digits. http://info-uri.info/registry/OAIHandler?verb=GetRecord&metadataPrefix=reg&identifier=info:lccn/-----------------------< G E T _ S E T T I N G S _ F R O M _ C I T E _ C L A S S >----------------------
length When |mode= 8 then all digitslength = 9 then lccn[1] is lower case alphalength = 10 then lccn[1] and lccn[2] are both lower case alpha not set or both digitslength = 11 then lccn[1] when its value is lower case alphainvalid, lccn[2] use config.CitationClass and lccn[3] are both lower case alpha or both digitsparameter values to establishlength = 12 then lccn[1] and lccn[2] are both lower case alpharendered style.
]]
local function lccnget_settings_from_cite_class (lccn) local handler = cfg.id_handlers['LCCN']; local err_cat = ''; -- presume that LCCN is valid local id = lccn; -- local copy of the lccn  id = normalize_lccn (id); -- get canonical form (no whitespaceps, hyphensref, forward slashescite_class) local len = id:len()sep; -- get the length of the lccn  if 8 == len then if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) err_cat = ' ' .. set_error( 'bad_lccn' ); -- set an error message end elseif 9 =cite_class = len then -- LCCN should be adddddddd if nil == id:match("%l%d%d%d%d%d%d%d%dcitation") then -- does it match our pattern? err_cat = ' ' .. set_errorfor citation templates ( 'bad_lccn' CS2); -- set an error message end elseif 10 sep, ps, ref == len then -- LCCN should be aadddddddd or dddddddddd if id:matchset_cs2_style ("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ... if nil == id:match("^%l%l%d%d%d%d%d%d%d%d") then -- ... see if it matches our pattern err_cat = ' ' .. set_error( 'bad_lccn' ps, ref); -- no match, set an error message end end elseif 11 == len then else -- LCCN should be aaadddddddd or adddddddddd if not (id:match("^%l%l%l%d%d%d%d%d%d%d%d") or id:match("^%l%d%d%d%d%d%d%d%d%d%d")) then -- see if it matches one of our patterns err_cat = ' ' .. set_error( 'bad_lccn' ); -- no match, set an error message end elseif 12 == len then -- LCCN should be aadddddddddda citation template so CS1 if not id:match("^%l%l%d%d%d%d%d%d%d%d%d%d") then -- see if it matches our pattern err_cat = ' ' .. set_error( 'bad_lccn' ); -- no matchsep, set an error message end else err_cat ps = ' ' .. set_errorset_cs1_style ( 'bad_lccn' ps); -- wrong length, set an error message
end
if not is_set (err_cat) and nil ~= lccn:find ('%s') then err_cat = ' ' .. set_error( 'bad_lccn' ); return sep, ps, ref -- lccn contains a space, set an error message end  return external_link_id({link = handler.link, label = handler.label, prefix=handler.prefix,id=lccn,separator=handler.separator, encode=handler.encode}) .. err_cat;them all
end
--[[--------------------------< S E T _ S T Y L E >------------------------------------------------------------Format PMID and do simple error checkingEstablish basic style settings to be used when rendering the citation. PMIDs are sequential numbers beginning at 1 Uses |mode= if set and counting upvalid or usesconfig. This code checks CitationClass from the PMID template's #invoke: to see that itestablish style.contains only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more PMIDs are issued.
]]
local function pmidset_style (idmode, ps, ref, cite_class) local test_limit sep; if 'cs2' = 30000000; = mode then -- update if this value as PMIDs approachtemplate is to be rendered in CS2 (citation) style local handler sep, ps, ref = cfg.id_handlers['PMID']set_cs2_style (ps, ref); local err_cat = elseif 'cs1'; == mode then -- presume that PMID if this template is valid if id:matchto be rendered in CS1 ("[^%d]"cite xxx) then -- if PMID has anything but digitsstyle err_cat sep, ps = ' ' .. set_errorset_cs1_style ( 'bad_pmid' ps); -- set an error message else -- PMID is only digitsanything but cs1 or cs2 local id_num sep, ps, ref = tonumberget_settings_from_cite_class (idps, ref, cite_class); -- convert id to a number for range testingget settings based on the template's CitationClass end if 1 > id_num or test_limit < id_num 'none' == ps:lower() then -- if PMID assigned value is outside test limit boundaries'none' then err_cat ps = ' ' .. set_error( 'bad_pmid' ); -- set an error message endto empty string
end
return external_link_id({link = handler.linksep, label = handler.labelps, prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat;ref
end
--[=[--------------------------< I S _ E M B A R G O E P D F >------------------------------------------------------------------
Determines if a PMC identifier's online version url has the file extension that is embargoed. Compares one of the date in |embargo= against today's datepdf file extensions used by [[MediaWiki:Common. If embargo date iscss]] whenin the future, returns applying the content of |embargo=; otherwise, returns and empty string because the embargo has expired or because|embargo= was not set in this citepdf icon to external links.
returns true if file extension is one of the recognized extension, else false ]=]
local function is_embargoed is_pdf (embargourl) if is_set return url:match (embargo) then local lang = mw'%.getContentLanguage(pdf[%?#]?'); local good1, embargo_date, good2, todays_date; good1, embargo_date = pcallor url:match ( lang.formatDate, lang, 'U', embargo ); good2, todays_date = pcall( lang%.formatDate, lang, 'U' ); if good1 and good2 then -- if embargo date and today's date are good dates if tonumber( embargo_date ) >= tonumber( todays_date ) then -- is embargo date is in the futurePDF[%?#]? return embargo; -- still embargoed else add_maint_cat ('embargo') return ''; -- unset because embargo has expired end end end return ''; -- |embargo= not set return empty string
end
--[[--------------------------< P S T Y L E _ F O R M C A T >------------------------------------------------------------------------
Format a PMCApplies css style to |format=, do simple error checking|chapter-format=, and check for embargoed articles. The embargo parameter takes a date for a value. If the embargo date is in the future the PMC identifier will notbe linked to the articleetc. If the embargo date is today or in the past, or Also emits an error message if it is empty or omitted, then theformat parameter doesPMC identifier is linked to the article through the link at cfg.id_handlers['PMC'].prefix. PMC embargo date testing is done in function is_embargoed () which is called earlier because when the citationhas |pmc=<value> but does not have a |matching url= then |title= is linked with the PMC linkparameter. Function is_embargoed ()returns If the embargo date if format parameter is not set and the PMC article url contains a file extension thatis still embargoedrecognized as a pdf document by MediaWiki's commons.css, otherwise it returns an empty string. PMCs are sequential numbers beginning at 1 and counting up. This this code checks will set the PMC format parameter to see that it contains only digits and is less(PDF) withthan test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issuedappropriate styling.
]]
local function pmcstyle_format (idformat, embargourl, fmt_param, url_param) local test_limit = 5000000; -- update this value as PMCs approach local handler = cfg.id_handlers['PMC']; local err_cat = ''; -- presume that PMC is valid local text;  if id:matchis_set ("[^%d]"format) then -- if PMC has anything but digits err_cat format = ' ' .. set_errorwrap_style ( 'bad_pmcformat' , format); -- set an error message else -- PMC is only digitsadd leading space, parenthases, resize local id_num = tonumberif not is_set (idurl); -- convert id to a number for range testing if 1 > id_num or test_limit < id_num then -- if PMC is outside test limit boundaries err_cat format = ' ' format .. set_error( 'bad_pmcformat_missing_url' , {fmt_param, url_param} ); -- set add an error message
end
end if is_set elseif is_pdf (embargourl) then -- format is PMC not set so if url is still embargoed?a pdf file then textformat ="[[" .. handler.link .. "|" .. handler.label .. "]]:" .. handler.separator .. id .. err_catwrap_style ('format', 'PDF'); -- still embargoed so no external linkset format to pdf
else
text format = external_link_id({link = handler.link, label = handler.label, ''; -- no embargo date or embargo has expired, ok to link to article prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat;empty string for concatenation
end
return textformat;
end
-- Formats a DOI and checks for DOI errors.[[--------------------------< G E T _ D I S P L A Y _ A U T H O R S _ E D I T O R S >------------------------
-- DOI Returns a number that defines the number of names contain two parts: prefix displayed for author and editor name lists and suffix separated by a forward slash.boolean flag-- Prefix: directory indicator '10to indicate when et al.' followed by a registrant code-- Suffix: character string of any length chosen by should be appended to the registrantname list.
When the value assigned to |display-- This function checks xxxxors= is a DOI name for: prefix/suffix. If number greater than or equal to zero, return the number andthe previous state of the 'etal' flag (false by default but may have been set to true if the doi name list contains spaces or endashes,-- or, if it ends with a period or a comma, this function will emit a bad_doi error messagesome variant of the text 'et al.').
When the value assigned to |display-- DOI names are case-insensitive and can incorporate any printable Unicode characters so xxxxors= is the test for spaces, endashkeyword 'etal',return a number that is one greater than the-- number of authors in the list and terminal punctuation may not be technically correct but it appears, that set the 'etal' flag true. This will cause the list_people() to display all ofthe names in practice these characters are rarely if ever used in doi namesthe name list followed by 'et al.'
local function doi(idIn all other cases, inactive) local cat = "" local handler = cfg.id_handlers['DOI']; local text; if is_set(inactive) then local inactive_year = inactive:match("%d%d%d%d") or ''; -- try to get returns nil and the year portion from previous state of the inactive date text = "[[" .. handler.link .. "|" .. handler.label .. "]]:" .. id; if is_set(inactive_year) then table.insert( z.error_categories, "Pages with DOIs inactive since " .. inactive_year ); else table.insert( z.error_categories, "Pages with inactive DOIs" ); -- when inactive doesn't contain a recognizable year end inactive = " (" .. cfg.messages[etal'inactive'] .. " " .. inactive .. ")" else text = external_link_id({link = handler.link, label = handler.label, prefix=handler.prefix,id=id,separator=handler.separator, encode=handlerflag.encode}) inactive = "" end
inputs: if nil == idmax:match("^10%.A[^%s–'DisplayAuthors']-/[^%s–]-or A[^%.,]$") then -- doi must begin with '10.DisplayEditors', must contain ]; a fwd slash, must not contain spaces number or endashes, and must not end with period some flavor of etal count: #a or comma#e cat = list_name: ' authors' .. set_error( or 'bad_doieditors' ); end return text .. inactive .. cat end  --[[--------------------------< O P E N L I B R A R Y >-------------------------------------------------------- Formats an OpenLibrary link, and checks for associated errors.etal: author_etal or editor_etal
]]
local function openlibrary(id)
local code = id:match("^%d+([AMW])$"); -- only digits followed by 'A', 'M', or 'W'
local handler = cfg.id_handlers['OL'];
local function get_display_authors_editors (max, count, list_name, etal) if is_set ( code == "A" max) then return external_link_id({link=handler.link, label=handler.label, prefix=handler.prefix .. if 'authors/OLetal', id=id, separator=handler.separator, encode = handler.encode}max:lower() elseif :gsub( code == "M[ '%.]" , '') then return external_link_id -- the :gsub({link=handler) portion makes 'etal' from a variety of 'et al.link, label=handler.label,' spellings and stylings prefixmax =handlercount + 1; -- number of authors + 1 so display all author name plus et al.prefix .. 'books/OL', idetal =id, separator=handler.separator, encode = handler.encode}true; -- overrides value set by extract_names() elseif max:match ( code == "W" '^%d+$') then -- if is a string of numbers return external_link_id max = tonumber ({linkmax); -- make it a number if max >=handler.link, labelcount then -- if |display-xxxxors=handler.label,value greater than or equal to number of authors/editors prefix=handler.prefix .. add_maint_cat ('works/OLdisp_auth_ed',list_name); id=id, separator=handler.separator, encode = handler.encode})end else -- not a valid keyword or number return external_link_id table.insert({link=handlerz.linkmessage_tail, label=handler.label{ set_error( 'invalid_param_val', prefix=handler.prefix .. {'OLdisplay-', id=id, separator=handler.separator. list_name, encode = handler.encodemax}, true ) .. ' ' .. set_error( 'bad_ol' } ); -- add error message max = nil; -- unset; as if |display-xxxxors= had not been set end
end
return max, etal;
end
--[[--------------------------< E X T R A _ T E X T _ I N _ P A G E _ C H E C K >------------------------------
--[[--------------------------< M E S S A G E _ I D >----------------------------------------------------------Adds page to Category:CS1 maint: extra text if |page= or |pages= has what appears to be some form of p. or pp. abbreviation in the first characters of the parameter content.
Validate check Page and format a usenet message idPages for extraneous p, p. Simple error checking, looks for pp, and pp. at start of parameter value: good pattern: 'id-left@id-right^P[^%.P%l]' matches when |page(s)= begins PX or P# but not enclosed inPx where x and X are letters and # is a dgiit bad pattern: '<^[Pp][Pp]' and/matches matches when |page(s)= begins pp or pP or Pp or '>' angle brackets.PP
]]
local function message_id extra_text_in_page_check (idpage)-- local good_pattern = '^P[^%.P%l]'; local handler good_pattern = cfg'^P[^%.id_handlersPp]'; -- ok to begin with uppercase P: P7 (pg 7 of section P) but not p123 (page 123) TODO: add Gg for PG or Pg?-- local bad_pattern = '^[Pp][Pp]'USENETID; local bad_pattern = '^[Pp]?[Pp]%.?[ %d]';
text = external_link_idif not page:match ({link = handler.link, label = handler.label,good_pattern) and (page:match (bad_pattern) or page:match ('^[Pp]ages?')) then prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}add_maint_cat ('extra_text'); end-- if not idPage:match('^[Pp]?[Pp]%.+@.+$?[ %d]') or not id Page:match('^[^<Pp].*ages?[^>%d]$')then or-- doesn Pages:match ('t have '@^[Pp]?[Pp]%.?[ %d]' ) or has one or first or last character is Pages:match ('< or '>^[Pp]ages?[ %d]') then text = text .. ' ' .. set_error-- add_maint_cat ( 'bad_message_idextra_text' ) ;-- add an error message if the message id is invalid end return text
end
--[[--------------------------< S E T _ T I T L E T Y P E >----------------------------------------------------
This function sets default title types (equivalent to the citation including |type=--[[--------------------------<default valueP A R S E _ V A U T H O R S _ V E D I T O R S >) for those templates that have defaults.Also handles the special case where it is desirable to omit the title type from the rendered citation (|type=none).--------------------------------
]]This function extracts author / editor names from |vauthors= or |veditors= and finds matching |xxxxor-maskn= and|xxxxor-linkn= in args. It then returns a table of assembled names just as extract_names() does.
local function set_titletype Author / editor names in |vauthors= or |veditors= must be in Vancouver system style. Corporate or institutional namesmay sometimes be required and because such names will often fail the is_good_vanc_name(cite_class, title_type)and other format compliance if is_settests, are wrapped in doubled paranethese ((title_typecorporate name)) then if "none" == title_type then title_type = ""; -- if |type=none then type parameter not displayed end return title_type; -- if |type= has been set to any other value use that value endsuppress the format tests.
return cfg.title_types [cite_class] or ''; -- set templateThis function sets the vancouver error when a reqired comma is missing and when there is a space between an author's default title type; else empty string for concatenationend --[[--------------------------< C L E A N _ I S B N >---------------------------------------------------------- Removes irrelevant text and dashes from ISBN numberSimilar to that used for Special:BookSourcesinitials.
]]
local function clean_isbnparse_vauthors_veditors ( isbn_str args, vparam, list_name) return isbn_str:gsub( "[^local names = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn= local v_name_table = {}; local etal = false; -0-9X]"return value set to true when we find some form of et al. vauthors parameter local last, first, link, "" )mask;end local corporate = false;
vparam, etal = name_has_etal (vparam, etal, true); --find and remove variations on et al. do not categorize (do it here because et al. might have a period) if vparam:find ('%[%[') or vparam:find ('%]%]') then --no wikilinking vauthors names add_vanc_error (); end v_name_table = mw.text.split(vparam, "%s*,%s*") ------------------------< E S C A P E _ L U A _ M A G I C _ C H A R S >----------------------------------names are separated by commas
Returns a for i, v_name in ipairs(v_name_table) do if v_name:match ('^%(%(.+%)%)$') then -- corporate authors are wrapped in doubled parenthese to supress vanc formatting and error detection first = ''; -- set to empty string where all of lua's magic characters for concatenation and because it may have been escapedset for previous author/editor last = v_name:match ('^%(%((. This is important because functions like+)%)%)$') corporate = true; elseif string.gsubfind(v_name, "%s") treat their pattern and replace strings as patterns, not literal strings.]]then local function escape_lua_magic_chars (argument)lastfirstTable = {} argument lastfirstTable = argument:gsubmw.text.split("%%"v_name, "%%%%s") first = table.remove(lastfirstTable); -- replace % with %%removes and returns value of last element in table which should be author intials argument last = argument:gsubtable.concat(lastfirstTable, " "([%^%$%(%)% -- returns a string that is the concatenation of all other names that are not initials if mw.ustring.match (last, '%[a+%]s+%*u+%s+%-%?]a+')"or mw.ustring.match (v_name, "' %u %%1"u$') then add_vanc_error (); -- replace all other lua magic pattern characters return argumentmatches last II last;the case when a comma is missing or a space between two intiials end else first = ''; --[[--------------------------< S T R I P _ A P O S T R O P H E _ M A R K U P >-------------------set to empty string for concatenation and because it may have been set for previous author/editor last = v_name; --last name or single corporate name? Doesn't support multiword corporate names? do we need this? end if is_set (first) and not mw.ustring.match (first, "^%u?%u$") then --first shall contain one or two upper-case letters, nothing else add_vanc_error (); end --this from extract_names () link = select_one( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ); mask = select_one( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i ); names[i] = {last = last, first = first, link = link, mask = mask, corporate=corporate}; --add this assembled name to our names list end return names, etal; --all done, return our list of namesend
Strip wiki italic and bold markup from argument so that it doesn't contaminate COinS metadata.This function strips common patterns of apostrophe markup. We presume that editors who have taken the time tomarkup a title have, as a result, provided valid markup. When they don't, some single apostrophes are left behind.--[[--------------------------< S E L E C T _ A U T H O R _ E D I T O R _ S O U R C E >------------------------
]]Select one of |authors=, |authorn= / |lastn / firstn=, or |vauthors= as the source of the author name list orselect one of |editors=, |editorn= / editor-lastn= / |editor-firstn= or |veditors= as the source of the editor name list.
local function strip_apostrophe_markup Only one of these appropriate three will be used. The hierarchy is: |authorn= (argumentand aliases)highest and |authors= lowest and if not is_set similarly, |editorn= (argumentand aliases) then return argument; endhighest and |editors= lowest
while true do if argument:match ("%'%'%'%'%'") then -- bold italic (5) argumentWhen looking for |authorn= / |editorn=argument:gsub("%'%'%'%'%'"parameters, ""); -- remove all instances of it elseif argument:match ("%'%'%'%'") then -- italic start test |xxxxor1= and end without content (4) argument|xxxxor2=argument:gsub("%'%'%'%'", ""and all of their aliases);stops after the second elseif argument:match ("%'%'%'") then -- bold test which mimicks the test used in extract_names(3)when looking for a hole in the author name list. There may be a better argument=argument:gsub("%'%'%'"way to do this, ""); elseif argument:match ("%'%'") then -- italic (2) argument=argument:gsub("%I just haven'%'", ""); else break; end end return argument; -- doneendt discovered what that way is.
--[[--------------------------< M A K E _ C O I N S _ T I T L E >----------------------------------------------Emits an error message when more than one xxxxor name source is provided.
Makes a title for COinS from Title and / In this function, vxxxxors = vauthors or ScriptTitle (veditors; xxxxors = authors or any other name-script pairs)editors as appropriate.
Apostrophe markup (bold, italics) is stripped from each value so that the COinS metadata isn't correupted with strings
of %27%27...
]]
local function make_coins_title select_author_editor_source (titlevxxxxors, scriptxxxxors, args, list_name)local lastfirst = false; if is_set select_one(titleargs, cfg.aliases[list_name .. '-Last'], 'none', 1 ) thenor -- do this twice incase we have a first 1 without a last1 title = strip_apostrophe_markup select_one(titleargs, cfg.aliases[list_name .. '-Last'], 'none', 2 ); -- strip any apostrophe markupthen else title lastfirst=''true; -- if not set, make sure title is an empty string
end
  if (is_set (scriptvxxxxors) then script and true == script:gsub ('^%l%l%s*:%s*', ''lastfirst); or -- remove language prefix if present these are the three error conditions (is_set (vxxxxors) and is_set (script value may now be empty stringxxxxors))or script (true = strip_apostrophe_markup = lastfirst and is_set (scriptxxxxors))then local err_name; if 'AuthorList' == list_name then -- strip any apostrophe markupfigure out which name should be used in error message err_name = 'author'; else script err_name ='editor'; end table.insert( z.message_tail, { set_error( 'redundant_parameters', {err_name .. '-name- if not setlist parameters'}, make sure script is an empty stringtrue ) } ); -- add error message
end
  if true == lastfirst then return 1 end; -- return a number indicating which author name source to use if is_set (titlevxxxxors) and then return 2 end; if is_set (scriptxxxxors) then script = ' ' .. scriptreturn 3 end; -- add a space before we concatenate end return title .. script1; -- no authors so return the concatenation1; this allows missing author name test to run in case there is a first without last
end
--[[--------------------------< G E T _ C O I N S _ P A G E S >------------------------------------------------
Extract page numbers from external wikilinks in any --[[--------------------------< I S _ V A L I D _ P A R A M E T E R _ V A L U E >------------------------------ This function is used to validate a parameter's assigned value for those parameters that have only a limited numberof the |page=allowable values (yes, y, true, |pages=no, etc). When the parameter value has not been assigned a value (missing or |at= parameters for use emptyin COinSthe source template) the function refurns true. If the parameter value is one of the list of allowed values returnstrue; else, emits an error message and returns false.
]]
local function get_coins_pages is_valid_parameter_value (pagesvalue, name, possible) local pattern; if not is_set (pagesvalue) then return pagestrue; end -- if no page numbers then we're donean empty parameter is ok while true do pattern = pageselseif in_array(value:matchlower("%[(%w*:?//[^ ]+%s+)[%w%d].*%]"); -- pattern is the opening bracket, the url and following space(spossible): "[url "then if nil == pattern then breakreturn true; end -- no more urls pattern = escape_lua_magic_chars (pattern); -- pattern is not a literal string; escape lua's magic pattern characters else pages = pages:gsubtable.insert(patternz.message_tail, ""); -- remove as many instances of pattern as possible end pages = pages:gsub{ set_error("[%[%]]"'invalid_param_val', {name, ""value}, true ); -- remove the brackets pages = pages:gsub("–", "-" } ); -- replace endashes with hyphensnot an allowed value so add error message pages = pages:gsub("&%w+;", "-" ); -- and replace html entities (&ndash; etc.) with hyphens; do we need to replace numerical entities like &#32; and the like? return false return pages;end
end
 -- Gets the display text for a wikilink like [[--------------------------< T E R M I N A|B]] T E _ N A M E _ L I S T >---------------------------------------- This function terminates a name list (author, contributor, editor) with a separator character (sepc) and a spacewhen the last character is not a sepc character or [[Bwhen the last three characters are not sepc followed by twoclosing square brackets (close of a wikilink). When either of these is true, the name_list is terminated with asingle space character. ]] gives B local function remove_wiki_linkterminate_name_list ( str name_list, sepc) return if (str:gsubstring.sub ( "%[%[name_list,-1,-1) == sepc) or (string.sub ([^%[%]]*name_list,-3,-1)%== sepc .. ']%]", function(l')then -- if last name in list ends with sepc char return l:gsub( "^[^|]*|(name_list ..*)$", "%1" ):gsub("^%s*(; -- don't add another else return name_list .. sepc ..' '; -)%s*$", "%1");- otherwise terninate the name list end));
end
-- Converts a hyphen to a dash
local function hyphen_to_dash( str )
if not is_set(str) or str:match( "[%[%]{}<>]" ) ~= nil then
return str;
end
return str:gsub( '-', '–' );
end
--[[--------------------------< S F O R M A F T _ V O L U M E _ J O I N S S U E >------------------------------------------------------------
Joins returns the concatenation of the formatted volume and issue parameters as a sequence of strings together while checking for duplicate separation characterssingle string; or formatted volumeor formatted issue, or an empty string if neither are set.
]]
 
local function safe_join( tbl, duplicate_char )
--[[
Note: we use string functions here, rather than ustring functions.
local function format_volume_issue (volume, issue, cite_class, origin, sepc, lower) This has considerably faster performance if not is_set (volume) and should work correctly as not is_set (issue) then return ''; end long as the duplicate_char is strict ASCII. The strings in tbl may be ASCII if 'magazine' == cite_class or UTF8.(in_array (cite_class, {'citation', 'map'}) and 'magazine' == origin) then if is_set (volume) and is_set (issue) then return wrap_msg ('vol-no', {sepc, volume, issue}, lower); elseif is_set (volume) then return wrap_msg ('vol', {sepc, volume}, lower); else return wrap_msg ('issue', {sepc, issue}, lower); end ]]end
local str vol = ''; -- the output string local comp = ''; -- what does 'comp' mean? local end_chr = ''; local trim; for _, value in ipairs( tbl ) do if value == nil then value = ''; end
if str == '' then -- if output string is empty str = value; -- assign value to it is_set (first time through the loopvolume)then elseif value ~= '' then if value:sub(1,1) == '4 <' then -- Special case of values enclosed in spans and other markupmw.ustring. comp = value:gsublen( "%b<>", "" volume); -- remove html markup (<span>string</span> -> string)then else comp vol = value; end -- typically duplicate_char is sepc if comp:subsubstitute (1,1) == duplicate_char then -- is first charactier same as duplicate_char? why test first character? -- Because individual string segments often (always?) begin with terminal punct for th -- preceding segment: 'First element' .cfg. messages['sepc next elementj-vol' .. etc? trim = false; end_chr = str:sub(-1],-1); -- get the last character of the output string -- str = str .. "<HERE(enchr=" .. end_chr.. ")" -- debug stuff? if end_chr == duplicate_char then -- if same as separator str = str:sub(1{sepc,-2volume}); -- remove it elseif end_chr == "'" then -- if it might be wikimarkup if str:sub(-3,-1) == duplicate_char .. "''" then -- if last three chars of str are sepc'' else str vol = str:subwrap_style (1, -4) .. "''"; vol-- remove them and add back bold'' elseif str:sub, hyphen_to_dash(-5,-1volume) == duplicate_char .. "]]''" then -- if last five chars of str are sepc]]'' trim = true; -- why? why do this and next differently from previous? elseif str:sub(-4,-1) == duplicate_char .. "]''" then -- if last four chars of str are sepc]'' trim = true; -- same question end elseif end_chr == "]" then -- if it might be wikimarkup end if str:subis_set (-3,-1issue) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink trim = true; elseif str:sub(-2,-1) == duplicate_char return vol .. "]" then -- if last two chars of str are sepc] external link trim = true; elseif str:subsubstitute (-4,-1) == duplicate_char .cfg. "messages['j-issue']" then -- normal case when |url=something & |title=Title. trim = true, issue); end elseif end_chr == " " then -- if last char of output string is a space if str:sub(-2,-1) == duplicate_char .. " " then -- if last two chars of str are <sepc><space> str = str:sub(1,-3) return vol; -- remove them both end end 
if trim then
if value ~= comp then -- value does not equal comp when value contains html markup
local dup2 = duplicate_char;
if dup2:match( "%A" ) then dup2 = "%" .. dup2; end -- if duplicate_char not a letter then escape it
value = value:gsub( "(%b<>)" .. dup2, "%1", 1 ) -- remove duplicate_char if it follows html markup
else
value = value:sub( 2, -1 ); -- remove duplicate_char when it is first character
end
end
end
str = str .. value; --add it to the output string
end
end
return str;
end
--[[--------------------------< I S _ G O O D _ V A N C _ N A M E >--------------------------------------------
For Vancouver Style, author/editor names are supposed to be rendered in Latin (read ASCII) characters. When a name
uses characters that contain diacritical marks, those characters are to converted to the corresponding Latin character.
When a name is written using a non-Latin alphabet or logogram, that name is to be transliterated into Latin characters.
These things are not currently possible in this module so are left to the editor to do.
This test allows |first= and |last= names to contain any of the letters defined in the four Unicode Latin character sets --[http://www.unicode.org/charts/PDF/U0000.pdf C0 Controls and Basic Latin] 0041–005A, 0061–007A [http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] 00C0–00D6, 00D8–00F6, 00F8–00FF [http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended------------------------< N O R M A] 0100–017F [http://www.unicode.org/charts/PDF/U0180.pdf Latin ExtendedL I Z E _ P A G E _ L I S T >-----------------------------------------B] 0180–01BF, 01C4–024F
|lastn= also allowed to contain hyphens, spaces, and apostrophes. (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)|firstn= also allowed to contain hyphens, spaces, apostrophes, and periodsnot currently used
At the time of this writingnormalizes a comma, ampersand, I had and/or space separated list to write the be 'if nil == mw.ustring.find <value>, <value>, ..., <value>' test ouside of the code editor returns list unchanged if there are no commas else strips whitespace and paste it herebecause then reformats the code editor gets confused between character insertion point and cursor position.list
]]
--[[
local function normalize_page_list (list)
if not list:find ('[,& ]') then return list end -- if list is not delimited with commas, ampersands, or spaces; done
list = mw.text.split (list, '[,&%s]+'); -- make a table of values
list = table.concat (list, ', '); -- and now make a normalized list
return list;
end
]]
local function is_good_vanc_name (last, first) if nil == mw.ustring.find (last, "^--[[-------------------------< F O R M A T _ P AG E S _ S H E E T S >--------------------------Za--ÖØ-öø-ƿDŽ-ɏ%-%s%']*$") or nil == mw.ustring.find (first, "^[A-Za--ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]*$") then add_vanc_error (); return false; -- not a string of latin characters; Vancouver required Romanization end; return true;end
--[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------adds static text to one of |page(s)= or |sheet(s)= values and returns it with all of the others set to empty strings.The return order is: page, pages, sheet, sheets
Attempts to convert names to initials in support of |name-list-format=vancSingular has priority over plural when both are provided.
Names in |firstn= may be separated by spaces or hyphens, or for initials, a period. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35062/.]]
Vancouver style requires family rank designations local function format_pages_sheets (Jrpage, IIpages, IIIsheet, etcsheets, cite_class, origin, sepc, nopp, lower) if 'map' == cite_class then -- only cite map supports sheet(s) to be rendered as Jrin-source locators if is_set (sheet) then if 'journal' == origin then return '', '', wrap_msg ('j-sheet', sheet, lower), ''; else return '', '', wrap_msg ('sheet', {sepc, sheet}, lower), ''; end elseif is_set (sheets) then if 'journal' == origin then return '', '', '', wrap_msg ('j-sheets', 2ndsheets, 3rdlower); else return '', etc. This form is not'', '', wrap_msg ('sheets', {sepc, sheets}, lower); end endcurrently supported by this code so correctly formed names like Smith JL 2nd are converted to Smith J2. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/. end
This function uses ustring functions because firstname initials may be any of the unicode Latin characters accepted by is_good_vanc_name local is_journal = 'journal' == cite_class or (in_array (cite_class, {'citation', 'map'}) and 'journal' == origin).;
]] local function reduce_to_initials if is_set (firstpage)then if mwis_journal then return substitute (cfg.ustring.matchmessages['j-page(firsts)'], "^%u%u$"page) , '', '', ''; elseif not nopp then return first end; substitute (cfg.messages['p-- when first contains just two upper-case lettersprefix'], {sepc, page}), '', '', nothing to do''; else local initials = return substitute (cfg.messages['nopp'], {sepc, page}), '', '', ''; end local i elseif is_set(pages) then if is_journal then return substitute (cfg.messages['j-page(s)'], pages), '', '', ''; elseif tonumber(pages) ~= 0; nil and not nopp then -- counter for if pages is only digits, assume a single page number of initials for word in mw.ustring return '', substitute (cfg.gmatch(first, "messages[^%s%.%'p-prefix']+", {sepc, pages}) do -- names separated by spaces, hyphens'', or periods''; table.insertelseif not nopp then return '', substitute (initials, mwcfg.ustring.sub(wordmessages['pp-prefix'],1{sepc,1pages})) -- Vancouver format does not include full stops., '', ''; i = i + 1else return '', substitute (cfg.messages['nopp'], {sepc, pages}), '', ''; -- bump the counter if 2 <= i then break; end -- only two initials allowed in Vancouver system; if 2, quit
end
return table.concat(initials) '', '', '', ''; -- Vancouver format does not include spaces.return empty strings
end
--[[--------------------------< L C I S T _ P E A T I O P L E N 0 >------------------------------------------------------------
Formats a list This is the main function doing the majority of people (ethe citation formatting.g. authors / editors)
]]
local function list_peoplecitation0(controlconfig, people, etal, list_nameargs) -- TODO: why is list_name here? not used in this function[[ Load Input Parameters The argument_wrapper facilitates the mapping of multiple aliases to single internal variable. ]] local sepA = argument_wrapper( args ); local namesepi   -- Pick out the relevant fields from the arguments. Different citation templates -- define different field names for the same underlying things. local author_etal; local format a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors= control.format local maximum = control.maximumAuthors; local lastauthoramp NameListFormat = control.lastauthorampA['NameListFormat']; local text Collaboration = {}A['Collaboration'];
if do -- to limit scope of selected local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'vancAuthorList' ); if 1 == format selected then a, author_etal = extract_names (args, 'AuthorList'); -- Vancouverfetch author list from |authorn= / |lastn= / |firstn=, |author-like linkn=, and |author/editor name styling?-maskn= sep elseif 2 == selected then NameListFormat = ',vanc'; -- override whatever |name-list separator between authors is -format= might be a comma, author_etal = parse_vauthors_veditors (args, args.vauthors, 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn= namesep elseif 3 == selected then Authors = A[' Authors']; -- last/first separator is a spaceuse content of |authors= else end sep if is_set (Collaboration) then author_etal = 'true;' -- nameso that |display-list separator between authors is a semicolon=etal not required namesep = ', ' -- last/first separator is <comma><space>end
end
 
local Coauthors = A['Coauthors'];
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=
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=
if sep:sub(local c = {}; --1,contributors list from |contributor-1) ~lastn= " " then sep / contributor-firstn= sep .. " " endpairs if is_set (maximum) and maximum < 1 then return "", 0local Contributors; end -- returned 0 is for EditorCount; not used for authorsassembled contributors name list local Contribution = A['Contribution']; for i,person in ipairs(people) do if is_setin_array(personconfig.lastCitationClass, {"book","citation"}) then local mask = person.mask local one local sep_one = sep; if and not is_set (maximumA['Periodical']) and i > maximum then etal -- |contributor= true; break; elseif (mask ~and |contribution= nil) thenonly supported in book cites local n c = tonumberextract_names (maskargs, 'ContributorList') if (n ~; -- fetch contributor list from |contributorn= nil) then one / |contributor-lastn= string.rep("&mdash;",n) else one -firstn= mask; sep_one , -linkn= " "; end else one , -maskn= person.last local first = person.first if is_set(first) 0 < #c then if not is_set ( "vanc" == format Contribution) then -- if vancouver format|contributor= requires |contribution= one = one:gsub table.insert( z.message_tail, { set_error('%.contributor_missing_required_param', 'contribution')}); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)add missing contribution error message if not person.corporate and is_good_vanc_name (one, first) then c = {}; -- and name blank the contributors' table; it is all Latin characters; corporate authors not testedused as a flag later end first if 0 = reduce_to_initials(first) = #a then -- attempt to convert first name(s) to initials end end one |contributor= requires |author= one .. namesep .. first end if is_settable.insert(personz.linkmessage_tail, { set_error( 'contributor_missing_required_param', 'author') and person.link ~= control.page_name then}); -- add missing author error message one c = "[[" .. person.link .. "|" .. one .. "]]" {}; -- link author/editor if this page is not blank the authorcontributors's/editor's page endtable; it is used as a flag later
end
table.insert( text, one )
table.insert( text, sep_one )
end
end  local count = #text / 2; else -- (number of names + number of separators) divided by 2 if count > 0 then not a book cite if count > select_one (args, cfg.aliases['ContributorList-Last'], 'redundant_parameters', 1 and is_set(lastauthoramp) and not etal then-- are there contributor name list parameters? text[#text-2] = " & "table.insert( z.message_tail, { set_error( 'contributor_ignored')}); -- replace last separator with ampersand textadd contributor ignored error message
end
text[#text] Contribution = nil; -- erase the last separatorunset
end
local result = table.concatif not is_valid_parameter_value (text) NameListFormat, 'name-list-format', cfg.keywords['name- construct list if etal and is_set (result-format']) then -- etal may be set by |display-authors=etal but we might not have a last-first listonly accepted value for this parameter is 'vanc' result NameListFormat = result .. sep .. ' ' .. cfg.messages['et al']; -- we've go a last-first list and etal so add et al.anything else, set to empty string
end
return result, count
end
-- local Year = A['Year']; local PublicationDate = A['PublicationDate']; local OrigYear = A['OrigYear']; local Date = A[--------------------------< 'Date']; local LayDate = A N C H O R _ I D >-------['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
Generates local Chapter = A['Chapter']; local ScriptChapter = A['ScriptChapter']; local ChapterLink -- = A['ChapterLink']; -- deprecated as a CITEREF anchor ID if we have at least one 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 ArchiveURL = A['ArchiveURL']; local URL = A['URL'] local URLorigin = A:ORIGIN('URL'); -- get name or a date. Otherwise returns an empty string.of parameter that holds URL local ChapterURL = A['ChapterURL'];namelist is one local ChapterURLorigin = A:ORIGIN('ChapterURL'); -- get name of the contributorparameter that holds ChapterURL local ConferenceFormat = A['ConferenceFormat']; local ConferenceURL = A['ConferenceURL']; local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); --, authorget name of parameter that holds ConferenceURL local Periodical = A['Periodical']; local Periodical_origin = A:ORIGIN('Periodical'); -, or editor-get the name lists chosen in that order. year is Year or anchor_year.of the periodical parameter
local Series = A['Series']]; local Volume; local Issue; local Page; local function anchor_id (namelist, year)Pages; local names={}At; -- a table for the one to four names and year  for iif in_array (config.CitationClass,v in ipairs cfg.templates_using_volume) and not ('conference' == config.CitationClass and not is_set (namelistPeriodical) do -- loop through the list and take up to the first four last names) then namesVolume = A[i'Volume'] = v; end if in_array (config.CitationClass, cfg.templates_using_issue) and not (in_array (config.last CitationClass, {'conference', 'map'}) and not is_set (Periodical))then if i Issue == 4 then break end -- if four then doneA['Issue'];
end
table.insert (names, year); -- add the year at the end local id Position = table.concat(names)''; -- concatenate names and year for CITEREF id if is_set not in_array (idconfig.CitationClass, cfg.templates_not_using_page) then -- if concatenation is not an empty string return "CITEREF" .. idPage = A['Page']; -- add the CITEREF portion else Pages = hyphen_to_dash( A['Pages'] ); return At = A['At']; -- return an empty string; no reason to include CITEREF id in this citation
end
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 Via = A['Via']; local AccessDate = A[--------------------------< N 'AccessDate']; local ArchiveDate = A M E _ H ['ArchiveDate']; local Agency = A S _ E T ['Agency']; local DeadURL = A L >-----------------------------------------------['DeadURL'] if not is_valid_parameter_value (DeadURL, 'dead-url', cfg.keywords ['deadurl']) then --set in config.defaults to 'yes' DeadURL = ''; --anything else, set to empty string end
Evaluates the content of author and editor name parameters for variations on the theme of et al 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. If found,keywords ['yes_true_y']) thenthe et al. is removed IgnoreISBN = nil; -- anything else, a flag is set to true and the function returns the modified name and the flag.empty string endThis function never sets the flag to false but returns it local Embargo = A['Embargo's previous state because it may have been set by];previous passes through this function or by the parameters |display local Class = A['Class']; -authors=etal or |display-editorsarxiv class identifier  local ID_list =etalextract_ids( args );
local Quote = A['Quote']];
local function name_has_etal 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, etal, nocat)of parameter that holds TranscriptURL
if is_set (name) then -- name can be nil in which case just return local etal_pattern LastAuthorAmp = "[;,]? *A[\"']*%f[%a][Ee][Tt] *[Aa][Ll][%.\"LastAuthorAmp']*$" -- variations on the 'et al' theme local others_pattern = "[;,]? *%f[%a]and [Oo]thers"; -- and alternate to et al. if name:match not is_valid_parameter_value (etal_pattern) then LastAuthorAmp, 'last-author- variants on et alamp', cfg. name = name:gsub (etal_pattern, keywords ['yes_true_y']); -- if found, removethen etal LastAuthorAmp = truenil; -- set flag (may have been set previously here or by |display-authors=etal)to empty string end if not nocat then -- no categorization for |vauthors local no_tracking_cats = add_maint_cat (A['etalNoTracking')]; -- and add a category if not already added end elseif name:match (others_pattern) then -- if not 'et al.'is_valid_parameter_value (no_tracking_cats, then 'and othersno-tracking'? name = name:gsub (others_pattern, cfg.keywords ['yes_true_y']); -- if found, removethen etal no_tracking_cats = truenil; -- set flag (may have been set previously here or by |display-authors=etal) if not nocat then -- no categorization for |vauthors= add_maint_cat ('etal'); -- and add a category if not already added endto empty string
end
end
return name, etal; --
end
--these are used by cite interview local Callsign = A['Callsign']; local City = A[--------------------------< E X T R 'City']; local Program = A C T _ N A M E S >----------------------------------------------------Gets name list from the input arguments['Program'];
Searches through args in sequential order to find |lastn--local variables that are not cs1 parameters local use_lowercase; -- controls capitalization of certain static text local this_page = and |firstn= parameters mw.title.getCurrentTitle(or their aliases), ; -- also used for COinS and their matching link and mask parameters.for languageStops searching when both |lastn= and |firstn= are not found local anchor_year; -- used in args after two sequential attempts: found |last1=, |last2=, and |last3= but doesn'tthe CITEREF identifierfind |last4 local COinS_date = and {}; -- holds date info extracted from |last5date= then for the search is done.COinS metadata by Module:Date verification
This function emits an error message when there is a |firstn= without a matching |lastn local DF =. When there are A['holesDF' ]; -- date format set in the list of last names, cs1|last1= and |last3=2 templateare present but |last2= is missing if not is_valid_parameter_value (DF, 'df', an error message is emittedcfg. |lastnkeywords['date-format']) then -- validate reformatting keyword DF = is ''; -- not required valid, set to have a matching |firstn=.empty string end
When an author or editor -- set default parameter values defined by |mode= parameter contains some form of 'et al.' If |mode= is empty or omitted, the use CitationClass to set these values local Mode = A['et al.Mode' is stripped from the parameter and a flag (etal) returned];that will cause list_people if not is_valid_parameter_value () to add the static Mode, 'et al.mode' text from Module:Citation/CS1/Configuration, cfg. This keeps keywords['et al.mode' out of the ]) thentemplate Mode = ''s metadata. When this occurs; end local sepc; -- separator between citation elements for CS1 a period, for CS2, the page is added to a maintenance category.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
local function extract_names--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 (argsno_tracking_cats) then -- ignore if we are already not going to categorize this page if in_array (this_page.nsText, list_namecfg.uncategorized_namespaces)then local names no_tracking_cats = {}"true"; -- table of namesset no_tracking_cats end local last; for _,v in ipairs (cfg.uncategorized_subpages) do -- individual cycle through page name componentspatterns local first; local link; local mask; local i = 1; if this_page.text:match (v) then -- loop counter/indexertest page name against each pattern local n no_tracking_cats = 1"true"; -- output table indexerset no_tracking_cats local count = 0 break; -- used to count the number of times we haven't bail out if one is found a |last= (or alias for authors, |editor-last or alias for editors) end end local etal=false; -- return value set to true when we find some form of et al. in an author parameterend
local err_msg_list_name -- check for extra |page=, |pages= or |at= list_name:match parameters. ("(%w+also sheet and sheets while we're at it)List") .. 's list'; -- modify AuthorList or EditorList for use in error messages if necessary while true do last = select_one( args, cfg.aliases[list_name .. {'-Lastpage'], 'redundant_parametersp', i ); -- search through args for name components beginning at 1 first = select_one( args, cfg.aliases[list_name .. '-Firstpp'], 'redundant_parameterspages', i ); link = select_one( args, cfg.aliases[list_name .. '-Linkat'], 'redundant_parameterssheet', i ); mask = select_one( args, cfg.aliases[list_name .. '-Masksheets']}, 'redundant_parameters', i ); -- this is a dummy call simply to get the error message and category
last, etal local NoPP = name_has_etal A['NoPP'] if is_set (NoPP) and is_valid_parameter_value (lastNoPP, etal'nopp', falsecfg.keywords ['yes_true_y'])then NoPP = true; -- find and remove variations on et al. else first, etal NoPP = name_has_etal (first, etal, false)nil; -- find and remove variations on et al.unset, used as a flag later end
if first and not last then -- if there is a firstn without a matching lastn table.insert( z.message_tail, { set_erroris_set( 'first_missing_last', {err_msg_list_name, i}, true ) } Page); -- add this error message elseif not first and not last then -- if both firstn and lastn aren't found, are we done? count = count + 1; -- number of times we haven't found last and first if 2 <= count then -- two missing names and we give up break; -- normal exit or there is a two-name hole in the list; can't tell which end else -- we have last with or without a first if is_set (linkPages) and false == link_param_ok or is_set(linkAt) then -- do this test here in case link is missing last table.insert( z.message_tail, { set_error( Pages = 'bad_paramlink', list_name:match ("(%w+)List"):lower() .. '-link' .. i )}); -- url or wikilink in author link; end names[n] = {last = last, first = first, link = link, mask = mask, corporate=false}; -- add this name to our names list (corporate for |vauthors= only) n = n + 1; -- point to next location in unset the names tableothers if 1 =At = count then -- if the previous name was missing table.insert( z.message_tail, { set_error( 'missing_name', {err_msg_list_name, i-1}, true ) } ); -- add this error message end count = 0; -- reset the counter, we're looking for two consecutive missing names
end
i = i + 1extra_text_in_page_check (Page); -- point add this page to next args locationmaint cat if |page= value begins with what looks like p. or pp. endelseif is_set(Pages) then if is_set(At) then return names, etal At = ''; -- all done, return our list of namesunset end  extra_text_in_page_check (Pages); --[[--------------------------< B U I L D _ I D _ L I S T >-------------------------------------------------------- Populates ID table from arguments using configuration settingsadd this page to maint cat if |pages= value begins with what looks like p. Loops through cfgor pp.id_handlers and searches args forany of the parameters listed in each cfg.id_handlers['...'].parameters. If found, adds the parameter and value tothe identifier list. Emits redundant error message is more than one alias exists in args end
]] local function extract_ids( args ) local id_list = {}; -- list of identifiers found in args for k, v in pairs( cfg.id_handlers ) do both |publication-- k is uc identifier name as index to cfg.id_handlers; e.g. cfg.id_handlers['ISBN'], v is a table v place= and |place= select_one( args, v.parameters, 'redundant_parameters' |location=); -- v.parameters is a table of aliases for k; here we pick one from args allowed if presentdifferent if not is_set(PublicationPlace) and is_set(vPlace) then id_list[k] PublicationPlace = vPlace; end -- if found in args, add identifier promote |place= (|location=) to our list|publication-place
end
return id_list if PublicationPlace == Place then Place = '';end -- don't need both if they are the sameend --[[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--[[--------------------------< B U I L D _ I D _ L I S T >--------------------------------------------------------mapped |url maps to |chapterurl when |title is remapped
Takes a table All other combinations of IDs created by extract_ids() |encyclopedia, |title, and turns it into a table of formatted ID outputs. inputs: id_list – table of identifiers built by extract_ids() options – table of various template parameter values used to modify some manually handled identifiers|article are not modified
]]
local function build_id_list( id_list, options ) local new_list, handler Encyclopedia = {}A['Encyclopedia'];
function fallbackif (kconfig.CitationClass == "encyclopaedia" ) return { __index or ( config.CitationClass = function= "citation" and is_set (t,iEncyclopedia)) return cfg.id_handlers[k][i] end } end;then -- test code for citation for k, v in pairs if is_set( id_list Periodical) do then -- k Periodical is uc identifier name as index to cfg.id_handlers; e.g. cfg.id_handlers['ISBN'], v set when |encyclopedia is a tableset -- fallback to read-only cfg handler = setmetatable if is_set( { ['id'] = v }, fallbackTitle) or is_set (kScriptTitle) );then if handler.mode == 'external' not is_set(Chapter) then table.insert( new_list, {handler.label, external_link_id( handler ) } ) Chapter = Title; -- |encyclopedia and |title are set so map |title to |article and |encyclopedia to |title elseif handler.mode ScriptChapter == 'internal' then table.insert( new_list, {handler.label, internal_link_id( handler ) } )ScriptTitle; elseif handler.mode ~ TransChapter = 'manual' then error( cfg.messages['unknown_ID_mode'] )TransTitle; elseif k ChapterURL == 'DOI' thenURL; table.insert if not is_set ( new_list, {handler.label, doiChapterURL) and is_set ( v, options.DoiBroken TitleLink) } );then elseif k = Chapter= 'ARXIV[[' then table.insert( new_list, {handler.label, arxiv( v, optionsTitleLink ..Class ) } ); elseif k == 'ASIN|' then table.insert( new_list, {handler.label, amazon( v, optionsChapter ..ASINTLD ) } )']]'; elseif k end Title =Periodical; ChapterFormat = 'LCCN' then table.insert( new_list, {handler.label, lccn( v ) } )Format; elseif k = Periodical = 'OL' or k =; -- redundant so unset TransTitle = 'OLA' then table.insert( new_list, {handler.label, openlibrary( v ) } ); elseif k = URL = 'PMC' then table.insert( new_list, {handler.label, pmc( v, options.Embargo ) } ); elseif k = Format = 'PMID' then table.insert( new_list, {handler.label, pmid( v ) } ); elseif k = TitleLink = 'ISMN' then table.insert( new_list, {handler.label, ismn( v ) } ); elseif k = ScriptTitle = 'ISSN' then table.insert( new_list, {handler.label, issn( v ) } ); elseif k == 'ISBN' then end local ISBN else -- |title not set Title = internal_link_id( handler )Periodical; if not check_isbn( v ) -- |encyclopedia set and |article set or not is_set(options.IgnoreISBN) thenset so map |encyclopedia to |title ISBN Periodical = ISBN .. set_error( 'bad_isbn', {}, false, " ", "" ); -- redundant so unset end table.insert( new_list, {handler.label, ISBN } ); elseif k == 'USENETID' then table.insert( new_list, {handler.label, message_id( v ) } ); else error( cfg.messages['unknown_manual_ID'] ); end end function comp( a, b ) -- used in following table.sort() return a[1] < b[1];
end
table-- Special case for cite techreport.sort( new_list, comp ); for k, v in ipairsif ( new_list config.CitationClass == "techreport") dothen -- special case for cite techreport new_listif is_set(A[k'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 = vA[2'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
return new_list;
end
--[[--------------------------< C O I N S _ C L E A N U P >---------------------------------------------------- Cleanup parameter values special case for the metadata by removing or replacing invisible characters and certain html entities. 2015-12-10: there is a bug in mw.text.unstripNoWiki (). It replaced math stripmarkers with the appropriate contentwhen it shouldn't. See https://phabricator.wikimedia.org/T121085 and Wikipedia_talk:Lua#stripmarkers_and_mw.text.unstripNoWiki.28.29 TODO: move the replacement patterns and replacement values into a table in /Configuration similar to the invisiblecharacters table?]] local function coins_cleanup (value)cite interview value = mw.text.unstripNoWiki if (value); -- replace nowiki stripmarkers with their content value = value:gsub ('<span class="nowrap" style="padding%-left:0%config.1em;">&#39;s</span>', "'s"); -- replace {{'s}} template with simple apostrophe-s value CitationClass = value:gsub ('&zwj;\226\128\138\039\226\128\139', "'"); -- replace {{'}} with simple apostrophe value = value:gsub ('\226\128\138\039\226\128\139', "'interview"); -- replace {{'}} with simple apostrophe (as of 2015-12-11)then value = value:gsub if is_set('&nbsp;', ' 'Program); -- replace &nbsp; entity with plain spacethen value = value:gsub ('\226\128\138', ' '); -- replace hair space with plain space value = value:gsub ('&zwj;', ''); -- remove &zwj; entities value ID = value:gsub ('[\226\128\141\226\128\139]', '') -- remove zero-width joiner, zero-width space value = value:gsub ('[\194\173\009\010\013]', ' '); -- replace soft hyphen, horizontal tab, line feed, carriage return with plain space return value;end  --[[--------------------------< C O I N S >-------------------------------------------------------------------- COinS metadata (see <http://ocoins.info/>) allows automated tools to parse the citation information]] local function COinS(data, class) if 'table' ~= type(data) or nil == next(data) then return ''; end  for k, v in pairs (data) do -- spin through all of the metadata parameter values if 'ID_list' ~= k and 'Authors' ~= k then -- except the ID_list and Author tables (author nowiki stripmarker done when Author table processed) data[k] = coins_cleanup (v)Program;
end
end  local ctx_ver = "Z39.88-2004"; -- treat table strictly as an array with only set values. local OCinSoutput = setmetatable( {}, { __newindex = functionif is_set(self, key, valueCallsign)then if is_set(valueID) then rawset( self, #self+1, tableID = ID .. sepc ..concat{ key, '' .. Callsign; else ID =', mw' .uri.encode( remove_wiki_link( value ) ) } )Callsign;
end
end
} if is_set(City);then if in_array (class, {'arxiv', 'journal', 'news'}) or (in_array (class, {'conference', 'interview', 'map', 'press release', 'web'}) and is_set(data.PeriodicalID)) or then ('citation' = ID = class and is_set(dataID .Periodical) and not is_set (data.Encyclopedia)) then OCinSoutputsepc .rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"; -- journal metadata identifier if 'arxiv' == class then -- set genre according to the type of citation template we are rendering OCinSoutput["rft.genre"] = "preprint"; -- cite arxiv elseif 'conference' == class then OCinSoutput["rft.genre"] = "conference"; -- cite conference (when Periodical set) elseif 'web' == class then OCinSoutput["rft.genre"] = "unknown"City; -- cite web (when Periodical set)
else
OCinSoutput["rft.genre"] ID = "article"; -- journal and other 'periodical' articles.. City;
end
OCinSoutput["rft.jtitle"] = data.Periodical; -- journal only end  if is_set(Others) then if is_set (data.MapTitleType) then OCinSoutput["rftOthers = ' ' .. TitleType .atitle"] . ' with ' .. Others; TitleType = data.Map''; -- for a map in a periodical
else
OCinSoutput["rftOthers = ' ' .atitle"] = data.Title; -- all other 'periodicalInterview with ' article titles.. Others;
end
-- these used onlu for periodicals OCinSoutput["rft.ssn"] = data.Season; -- keywords: winter, spring, summer, fall OCinSoutput["rft.chron"] = data.Chron; -- free-form date components OCinSoutput["rft.volume"] = data.Volume; -- does not apply to books OCinSoutput["rft.issue"] = data.Issue; OCinSoutput["rft.pages"] = data.Pages; -- also used in book metadata  elseif 'thesis' ~= class then -- all others except cite thesis are treated as 'book' metadata; genre distinguishes OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; -- book metadata identifier if 'report' == class or 'techreport' == class then -- cite report and cite techreportelse OCinSoutput["rft.genre"] Others = "report"; elseif 'conference' == class then -- cite conference when Periodical not set OCinSoutput["rft.genre"] = "conference"; elseif in_array (class, {'book', 'citation', 'encyclopaedia', 'interview', 'map'}) then if is_set (data.ChapterInterview) then OCinSoutput["rft.genre"] = "bookitem"; OCinSoutput["rft.atitle"] = data.Chapter; -- book chapter, encyclopedia article, interview in a book, or map title else if 'map' == class or 'interview' == class then OCinSoutput["rft.genre"] = 'unknown'; -- standalone map or interview else OCinSoutput["rft.genre"] = 'book'; -- book and encyclopedia end end else --{'audio-visual', 'AV-media-notes', 'DVD-notes', 'episode', 'interview', 'mailinglist', 'map', 'newsgroup', 'podcast', 'press release', 'serial', 'sign', 'speech', 'web'} OCinSoutput["rft.genre"] = "unknown";
end
OCinSoutput["rft.btitle"] = data.Title; -- book only
OCinSoutput["rft.place"] = data.PublicationPlace; -- book only
OCinSoutput["rft.series"] = data.Series; -- book only
OCinSoutput["rft.pages"] = data.Pages; -- book, journal
OCinSoutput["rft.edition"] = data.Edition; -- book only
OCinSoutput["rft.pub"] = data.PublisherName; -- book and dissertation
else -- cite thesis
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:dissertation"; -- dissertation metadata identifier
OCinSoutput["rft.title"] = data.Title; -- dissertation (also patent but that is not yet supported)
OCinSoutput["rft.degree"] = data.Degree; -- dissertation only
OCinSoutput['rft.inst'] = data.PublisherName; -- book and dissertation
end
-- and now common parameters (as much as possible) OCinSoutput["rft.date"] = data.Date; -- book, journal, dissertationspecial case for cite mailing list for k, v in pairsif ( dataconfig.ID_list ) do -- what to do about these? For now assume that they are common to all? if k CitationClass == 'ISBN' "mailinglist") then v = clean_isbn( v ) end local id Periodical = cfg.id_handlersA [k'MailingList'].COinS; if string.sub( id or "", 1, 4 ) elseif 'mailinglist' == A:ORIGIN('infoPeriodical' ) then -- for ids that are in the info:registry OCinSoutput["rft_id"] = table.concat{ id, "/", v }; elseif string.sub (id or "", 1, 3 ) =Periodical = 'rft' then -- for isbn, issn, eissn, etc that have defined COinS keywords OCinSoutput[ id ] = v; elseif id then -- when cfg.id_handlers[k].COinS unset because mailing list is not nil OCinSoutput["rft_id"] = table.concat{ cfg.id_handlers[k].prefix, v }; -- others; provide a url endonly used for cite mailing list
end
--[[ Account for kthe oddity that is {{cite conference}}, v in pairs( before generation of COinS data.ID_list if 'conference' == config.CitationClass then if is_set(BookTitle) do then Chapter = Title;-- what to do about these? For now assume that they are common to all? local id, value ChapterLink = cfg.id_handlers[k].COinSTitleLink; -- |chapterlink= is deprecated if k ChapterURL =URL; ChapterURLorigin = URLorigin; URLorigin = 'ISBN' then value ; ChapterFormat = Format; TransChapter = TransTitle; Title = clean_isbn( v )BookTitle; else value Format = v''; end-- if string.sub( id or "", 1, 4 ) =TitleLink = 'info' then; OCinSoutput["rft_id"] TransTitle = table.concat{ id, "/", v }''; else OCinSoutput[ id ] URL = value'';
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 last, firstCartography = ""; for k, v in ipairs( data.Authors ) dolocal Scale = ""; last, first local Sheet = coins_cleanup (v.last), coins_cleanup (v.first A['Sheet'] or ''); -- replace any nowiki strip markers, non-printing local Sheets = A['Sheets'] or invisible characers''; if k config.CitationClass == 1 "map" then -- for the first author name only if is_set(last) and is_set(first) then -- set these COinS values if |first Chapter = and |lastA['Map']; ChapterURL = specify the first author name OCinSoutputA["rft.aulast"'MapURL'] = last; -- book, journal, dissertation OCinSoutput TransChapter = A["rft.aufirst"'TransMap'] = first; -- book, journal, dissertation elseif is_set ChapterURLorigin = A:ORIGIN(last'MapURL') then ; OCinSoutput ChapterFormat = A["rft.au"'MapFormat'] = last; -- book, journal, dissertation -- otherwise use this form for the first name end else -- for all other authorsCartography = A['Cartography']; if is_set(last) and is_set(firstCartography ) then OCinSoutput[ Cartography = sepc .. " "rft.au"] = table.concat{ lastwrap_msg ('cartography', "Cartography, ", first }use_lowercase); end -- book, journal, dissertation elseif Scale = A['Scale']; if is_set(lastScale ) then OCinSoutput[ Scale = sepc .. " "rft.au"] = last. Scale; -- book, journal, dissertation end
end
end
OCinSoutput.rft_id = data.URL; OCinSoutput.rfr_id = table.concat-- Account for the oddities that are {{cite episode}} and {{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":"cite serial}}, before generation of COinS data.RawPage }; OCinSoutput if 'episode' == setmetatable( OCinSoutput, nil ); -- sort with version string always first, and combineconfig. tableCitationClass or 'serial' == config.sort( OCinSoutput );CitationClass then table.insert( OCinSoutput, 1, "ctx_ver local AirDate =" .. ctx_ver )A['AirDate']; -- such as "Z39.88-2004" return table.concat(OCinSoutput, "&") local SeriesLink = A['SeriesLink'];end
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 ------------------------< G E T _ I S O 6 3 9 _ C O D E >------------------------------------------------promote airdate to date Date = AirDate; end
Validates language names provided in |language if 'episode' = parameter if not an ISO639= config.CitationClass then --1 code. Handles handle the special case oddities that is Norwegian whereare strictly {{cite episode}}ISO639-1 code local Season = A['noSeason' is mapped to language name ]; local SeriesNumber = A['Norwegian BokmålSeriesNumber' by Extention:CLDR.];
Returns the language name if is_set (Season) and associated ISO639is_set (SeriesNumber) then -1 code- these are mutually exclusive so if both are set table. Because case of the source may be incorrect or different from the case that Wikimediausesinsert( z.message_tail, the name comparisons are done in lower case and when a match is found{ set_error( 'redundant_parameters', the Wikimedia version {wrap_style (assumed to be correct'parameter', 'season') is returned alongwith the code. When there is no match, we return the original language name stringmw' and ' .language.fetchLanguageNameswrap_style ('parameter', 'seriesno') will return a list of languages that aren}, true ) } ); -- add error message SeriesNumber = ''t part of ISO639; -1. Names that aren't ISO639-1 but that are includedin the list will be found if that name is provided in the unset; prefer |languageseason= parameter. For example, if over |languageseriesno=Samaritan Aramaic end -- assemble a table of parts concatenated later into Series if is_set(Season) then table.insert(s, that name will befound with the associated code wrap_msg ('samseason', not an ISO639-1 code. When names are found and the associated code is not two charactersSeason, this functionuse_lowercase)); endreturns only the Wikimedia language name if is_set(SeriesNumber) then tableAdapted from code taken from Module:Check ISO 639-1. ]] local function get_iso639_code insert(s, wrap_msg (lang'series', SeriesNumber, use_lowercase)); end if 'norwegian' == lang:loweris_set(Issue) then -- special case related to Wikimedia remap of code table.insert(s, wrap_msg ('noepisode' at Extension:CLDR, Issue, use_lowercase)); end return 'Norwegian', Issue = 'no'; -- Make sure rendered version unset because this is properly capitalized endnot a unique parameter
local languages Chapter = Title; -- promote title parameters to chapter ScriptChapter = ScriptTitle; ChapterLink = TitleLink; -- alias episodelink TransChapter = TransTitle; ChapterURL = URL; ChapterURLorigin = mw.language.fetchLanguageNamesA:ORIGIN('en', 'allURL') ; Title = Series; -- get a list of language names known promote series to Wikimediatitle -- TitleLink = SeriesLink; Series = table.concat(s, sepc .. 'all' ); -- this is required for North Ndebeleconcatenation of season, South Ndebeleseriesno, episode number  if is_set (ChapterLink) and Ojibwanot is_set (ChapterURL)then -- link but not URL local langlc Chapter = mw'[[' .ustring.lower(lang)ChapterLink .. '|' .. Chapter .. ']]'; -- lower case version for comparisonsok to wikilink for code, name in pairs elseif is_set (ChapterLink) and is_set (languagesChapterURL) do then -- scan the list to see if we can find our languageboth are set, URL links episode; if langlc Series == mw'[[' .. ChapterLink .. '|' .. Series .ustring.lower']]'; -- series links with ChapterLink (nameepisodelink -> TitleLink -> ChapterLink) thenugly end if 2 ~URL = code:len() then ''; -- ISO639unset TransTitle = ''; ScriptTitle = ''; else -1 codes only- now oddities that are cite serial return name Issue = ''; -- so return unset because this parameter no longer supported by the name but not the codecitation/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 = '[[' .. SeriesLink .. '|' .. Series .. ']]';
end
return nameSeries = wrap_style ('italic-title', codeSeries); -- found it, return name to ensure proper capitalization and the ISO639-1 codeseries is italicized end
end
return lang; -- not valid language; return language in original case and nil for ISO639-1 codeendof {{cite episode}} stuff
--Account for the oddities that are {{cite arxiv}}, before generation of COinS data. if 'arxiv' == config.CitationClass then if not is_set (ID_list[[--------------------------< L A N G U A G E _ P A R A M E T E R >--------------------------'ARXIV']) then --|arxiv= or |eprint= required for cite arxiv table.insert( z.message_tail, { set_error( 'arxiv_missing', {}, true ) } ); --add error message elseif is_set (Series) then --series is an alias of version ID_list['ARXIV'] = ID_list['ARXIV'] .. Series; --concatenate version onto the end of the arxiv identifier Series = ''; --unset deprecated_parameter ('version'); --deprecated parameter but only for cite arxiv end if first_set ({AccessDate, At, Chapter, Format, Page, Pages, Periodical, PublisherName, URL, --a crude list of parameters that are not supported by cite arxiv ID_list['ASIN'], ID_list['BIBCODE'], ID_list['DOI'], ID_list['ISBN'], ID_list['ISSN'], ID_list['JFM'], ID_list['JSTOR'], ID_list['LCCN'], ID_list['MR'], ID_list['OCLC'], ID_list['OL'], ID_list['OSTI'], ID_list['PMC'], ID_list['PMID'], ID_list['RFC'], ID_list['SSRN'], ID_list['USENETID'], ID_list['ZBL']},27) then table.insert( z.message_tail, { set_error( 'arxiv_params_not_supported', {}, true ) } ); --add error message
Get language name from ISO639 AccessDate= ''; -1 code value provided. If a code is valid use the returned name- set these to empty string; not supported in cite arXiv PublisherName = ''; -- (if notthe article has been published, then use the value cite journal, or other) Chapter = ''; URL = ''; Format = ''; Page = ''; Pages = ''; At = ''; end Periodical = 'arXiv'; -- set to arXiv for COinS; after that was provided with the language parameter., must be set to empty string end
There is an exception. There are three ISO639-1 codes - handle type parameter for Norewegian language variants. There are two official variants: Norwegian Bokmål (code 'nb') andthose CS1 citations that have default valuesNorwegian Nynorsk if in_array(code 'nn')config. The thirdCitationClass, code 'no'{"AV-media-notes", is defined by ISO639"DVD-1 as 'Norwegian' though in Norway this is pretty much meaninglessnotes", "mailinglist", "map", "podcast", "pressrelease", "report", "techreport", "thesis"}) then TitleType = set_titletype (config. HoweverCitationClass, it appearsTitleType);that on enwiki, editors are for the most part unaware of the nb if is_set(Degree) and nn variants (compare page counts for these variants at Category:Articles with non"Thesis" == TitleType then -English-language external linksspecial case for cite thesis TitleType = Degree .." thesis"; end end
Because Norwegian Bokmål if is_set(TitleType) then -- if type parameter is the most common language variant, Media wiki has been modified to return Norwegian Bokmål for ISO639-1 code 'no'. Here we undo that andspecifiedreturn 'Norwegian' when editors use |language TitleType =nosubstitute( cfg. We presume that editors donmessages['t know about the variants or cantype't descriminate between them.], TitleType); -- display it in parentheses end
See Help talk-- legacy:Citation Style_1#An ISO 639promote concatenation of |month=, and |year= to Date if Date not set; or, promote PublicationDate to Date if neither Date nor Year are set. 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 = ''; --1 language name testunset, no longer needed end end
When |language if PublicationDate = contains a valid ISO639= Date then PublicationDate = ''; end -1 code, the page is assigned to the category for that code: Category:Norwegian-language sources (no) ifthe page PublicationDate is a mainspace page and the ISO639-1 code is not same as Date, don'en'. Similarly, if the parameter is |language=Norwegian, it will be categorized t display in the same way.rendered citation
--[[Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This function supports multiple languages must be done before we do COinS because here is wherewe get the date used in the form |language=nb, French, th where the language names or codes are separated from each other by commasmetadata.
Date validation supporting code is in Module:Citation/CS1/Date_validation
]]
do -- create defined block to contain local variables error_message and mismatch
local error_message = '';
-- AirDate has been promoted to Date so not necessary to check it
anchor_year, error_message = dates({['access-date']=AccessDate, ['archive-date']=ArchiveDate, ['date']=Date, ['doi-broken-date']=DoiBroken,
['embargo']=Embargo, ['lay-date']=LayDate, ['publication-date']=PublicationDate, ['year']=Year}, COinS_date);
if is_set (Year) and is_set (Date) then -- both |date= and |year= not normally needed; local function language_parameter mismatch = year_date_check (langYear, Date) local code; if 0 == mismatch then -- the ISO639|year= does not match a year-value in |date= if is_set (error_message) then --1 two character codeif there is already an error message local name error_message = error_message .. ', '; -- the language nametack on this additional message end error_message = error_message .. '&#124;year= / &#124;date= mismatch'; local language_list elseif 1 == mismatch then -- |year= matches year-value in |date= add_maint_cat ('date_year'); end end  if is_set(error_message) then table.insert( z.message_tail, {set_error( 'bad_date', {error_message}, true ) }); -- table of language names to be renderedadd this error message elseif is_set (DF) then local names_table date_parameters_list = {['access-date']=AccessDate, ['archive-date']=ArchiveDate, ['date']=Date, ['doi-broken-date']=DoiBroken, ['embargo']=Embargo, ['lay-date']=LayDate, ['publication-date']=PublicationDate}; if reformat_dates (date_parameters_list, DF, false) then -- table made from the value assigned reformat to |languageDF format, use long month names if appropriate AccessDate =date_parameters_list['access-date']; -- overwrite date holding parameters with reformatted values ArchiveDate = date_parameters_list['archive-date']; -- TODO: move all dates into this table at the beginning and ... names_table Date = mwdate_parameters_list['date']; -- ..text.split (lang, use them from this table? no in-and-out like we're doing here DoiBroken = date_parameters_list['doi-broken-date']; Embargo = date_parameters_list['embargo']; LayDate = date_parameters_list['lay-date']; PublicationDate = date_parameters_list['%s*,%s*publication-date')]; end end end -- names should be a comma separated listend 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, lang in ipairs holds embargo date Embargo = is_embargoed (names_tableEmbargo) do ; -- reuse lang
if lang:match config.CitationClass == "journal" and not is_set(URL) and is_set(ID_list['^%a%a%-PMC']) then if not is_set (Embargo) then -- strip ietf language tags from codeif not embargoed or embargo has expired lang URL= lang:match (cfg.id_handlers['PMC'].prefix .. ID_list['PMC'(%a%a)%]; --set url to be the same as the PMC external link if not embargoed URLorigin = cfg.id_handlers['PMC') -].parameters[1]; - keep only 639-1 code portion set URLorigin to lang; TODO: do something with 3166 alpha 2 country code?parameter name for use in error message if citation is missing a |title=
end
if 2 == lang:len() then end -- ISO639-1 language code are 2 characters (fetchLanguageName also supports 3 character codes) name = mwAt this point fields may be nil if they weren't specified in the template use.language We can use that fact.fetchLanguageName( lang:lower(), "en" ); -- get ISO 639-1 language name Test if Language is a proper code endcitation has no title if not is_set (name) then -- if Language specified a valid ISO639-1 code code = lang:lower(Title); -- save itand else name, code = get_iso639_code not is_set(lang); -- attempt to get code from name (assign name here so that we are sure of proper capitalizationTransTitle)and end if not is_set (codeScriptTitle) then if 'noepisode' == code config.CitationClass then name = 'Norwegian' end; -- override wikimedia when code special case for cite episode; TODO: is there a better way to do this? table.insert( z.message_tail, { set_error( 'citation_missing_title', {'noseries'}, true ) } ); if 'en' ~= code then -- English not the languageelse add_prop_cat table.insert( z.message_tail, { set_error('foreign_lang_sourcecitation_missing_title', {name'title'}, codetrue ) });
end
else
add_maint_cat ('unknown_lang'); -- add maint category if not already added
end
table.insert (language_list, name);
name = ''; -- so we can reuse it
end
code = #language_list -- reuse code as number of languages in the list if 2 >'none' = code then name = tableTitle and in_array (config.concat (language_listCitationClass, {'journal', ' and citation'}) -- insert '<space>and<space>' between two language names elseif 2 < code then language_list[code] = 'is_set (Periodical) and ' .. language_list[code]; -- prepend last name with journal'and<space>' name = table.concat = A:ORIGIN(language_list, ', Periodical') then -- and concatenate with '<comma><space>' separatorsspecial case for journal cites end if 'English' = Title = name then return ''; -- if one language and that language is English return an set title to empty string add_maint_cat (no annotation'untitled');
end
return (" " .. wrap_msg ('language', name)); -- otherwise wrap with '(in ...)'
end
check_for_url ({ --add error message when any of these parameters contains a URL ['title']=Title, [--------------------------< S E T _ C S 1 _ S T Y L E >----------------------------------------------------A:ORIGIN('Chapter')]=Chapter, [A:ORIGIN('Periodical')]=Periodical, [A:ORIGIN('PublisherName')] = PublisherName });
Set style settings -- COinS metadata (see <http://ocoins.info/>) for CS1 automated parsing of citation templatesinformation. -- handle the oddity that is cite encyclopedia and {{citation |encyclopedia=something}}. Returns separator Here we presume that -- when Periodical, Title, and postscript settingsChapter 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 function set_cs1_style coins_title = Title; -- et tu if 'encyclopaedia' == config.CitationClass or (ps'citation' == config.CitationClass and is_set (Encyclopedia))then if not is_set (psChapter) 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; -- unless explicitely set to somethingdefault for coins rft.au if 0 < #c then -- but if contributor list ps coins_author = '.'c; -- terminate the rendered citation with a perioduse that instead
end
return '.', ps; -- separator is a full stop
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)), ----------------< S E T _ C S 2 _ S T Y L E >----------------------------------------------------pages stripped of external links ['Edition'] = Edition, ['PublisherName'] = PublisherName,Set style settings for CS2 citation templates. Returns separator ['URL'] = first_set ({ChapterURL, URL}, postscript2), ref settings ['Authors'] = coins_author, ['ID_list']= ID_list, ['RawPage']= this_page.prefixedText, }, config.CitationClass); local function set_cs2_style (ps-- Account for the oddities that are {{cite arxiv}}, ref)AFTER generation of COinS data. if not is_set (ps) 'arxiv' == config.CitationClass then -- if |postscript= has not been we have setrft.jtitle in COinS to arXiv, set cs2 defaultnow unset so it isn't displayed ps Periodical = ''; -- make sure it isn't nilperiodical not allowed in cite arxiv; 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 not 'newsgroup' == config.CitationClass then if is_set (refPublisherName) then -- if |ref PublisherName = is not setsubstitute (cfg.messages['newsgroup'], external_link( 'news:' .. PublisherName, PublisherName, A:ORIGIN('PublisherName') )); ref = "harv"; -- set default |ref=harvend
end
return ',', ps, ref; -- separator is a comma
end
--[[--------------------------< G E T _ S E T T I N G S _ F R O M _ C I T E _ C L A S S >----------------------
When |mode= is not set or when its value is invalid, use config.CitationClass and parameter values to establish
rendered style.
]] -- 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 maximum; 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 };
local function get_settings_from_cite_class (ps, ref, cite_class) local sep; if (cite_class == "citation") then do -- for citation templates (CS2)do editor name list first because coauthors can modify control table sep, ps control.maximum , ref editor_etal = set_cs2_style get_display_authors_editors (psA['DisplayEditors'], ref#e, 'editors', editor_etal); else -- not a citation template so CS1 sep last_first_list, ps EditorCount = set_cs1_style list_people(pscontrol, e, editor_etal, 'editor'); end
return sep 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., ps, ref |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; -- return them alleither an author name list or an empty string end
--[[-------------------------- if 1 == EditorCount and (true == editor_etal or 1 < S E T _ S T Y L E >------------------------------------------#e) then --only one editor displayed but includes etal then EditorCount = 2; --spoof to display (eds.) annotation end end do --now do translators control.maximum = #t; --number of translators Translators = list_people(control, t, false, 'translator'); --et al not currently supported end do --now do contributors control.maximum = #c; --number of contributors Contributors = list_people(control, c, false, 'contributor'); --et al not currently supported end do --now do authors Establish basic style settings to be used when rendering the citation control. Uses |modemaximum , author_etal = if set and valid or usesconfig.CitationClass from the templateget_display_authors_editors (A['DisplayAuthors's ], #invoke: to establish style.a, 'authors', author_etal);
]] if is_set(Coauthors) then -- if the coauthor field is also used, prevent ampersand and et al. formatting. control.lastauthoramp = nil; control.maximum = #a + 1; end last_first_list = list_people(control, a, author_etal, 'author');
local function set_style if is_set (modeAuthors) then Authors, psauthor_etal = name_has_etal (Authors, refauthor_etal, cite_classfalse) local sep;-- find and remove variations on et al. if 'cs2' == mode author_etal then -- if this template is to be rendered in CS2 (citation) style sep, ps, ref Authors = set_cs2_style (ps, ref); elseif Authors .. ' ' .. cfg.messages['cs1et al' == mode then ]; -- if this template is add et al. to be rendered in CS1 (cite xxx) styleauthors parameter sep, ps = set_cs1_style (ps); end else -- anything but cs1 or cs2 sep, ps, ref Authors = get_settings_from_cite_class (ps, ref, cite_class)last_first_list; -- get settings based on the template's CitationClasseither an author name list or an empty string end if 'none' == ps:lower() then -- if assigned value is 'none' then ps = ''; end -- set to empty string endof do
return sep, ps, ref if is_set (Authors) and is_set (Collaboration) then Authors = Authors .. ' (' .. Collaboration .. ')'; -- add collaboration after et al. end
if not is_set(Authors) and is_set(Coauthors) then --[coauthors aren't displayed if one of authors=[-------------------------< I S _ P D F >----------------------------------------------------------------, authorn=, or lastn= isn't specified table.insert( z.message_tail, { set_error('coauthors_missing_author', {}, true) } ); --emit error message end end
Determines -- apply |[xx-]format= styling; at the end, these parameters hold correctly styled format annotation,-- an error message if a the associated url has the file extension that is one of the pdf file extensions used by [[MediaWiki:Common.css]] whennot 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');applying the pdf icon to external links. TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl');
returns true -- special case for chapter format so no error message or cat when chapter not supported if file extension is one of the recognized extensionnot (in_array(config.CitationClass, else false{'web','news','journal', 'magazine', 'pressrelease','podcast', 'newsgroup', 'arxiv'}) or ('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia))) then ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url'); end
]=] local function is_pdf if not is_set(urlURL)then --and return url:match if in_array('%config.pdf[%?#]?'CitationClass, {"web","podcast", "mailinglist"}) then -- Test if cite web or cite podcast |url:match = is missing or empty table.insert( z.message_tail, { set_error('%.PDF[%?#]?cite_web_url', {}, true ) } ); end --[[--------------------------< S T Y L E _ F O R M A T >------------------------------------------------------ Applies css style to |format=, |chapter-format=, etc. Also emits an error message Test if the format parameter doesnot have a matching url parameter. If the format parameter accessdate is not set and the url contains given without giving a file extension thatURLis recognized as a pdf document by MediaWiki's commons.css, this code will set the format parameter to if is_set(PDFAccessDate) withthe appropriate styling. ]] local function style_format (format, url, fmt_param, url_param) if and not is_set (formatChapterURL) then format = wrap_style ('format', format); -- add leading space, parenthases, resize if ChapterURL may be set when the others are not is_set (url) thenset; TODO: move this to a separate test? format = format table.insert( z. message_tail, { set_error( 'format_missing_urlaccessdate_missing_url', {fmt_param}, url_paramtrue ) } ); -- add an error message AccessDate = '';
end
elseif is_pdf (url) then -- format is not set so if url is a pdf file then
format = wrap_style ('format', 'PDF'); -- set format to pdf
else
format = ''; -- empty string for concatenation
end
return format;
end
local OriginalURL, OriginalURLorigin, OriginalFormat; --[[--------------------------< G E T _ D I S P L A Y _ A U T H O R S _ E D I T O R S >------------------------ Returns a number TODO: swap chapter and title here so that may or may not limit the length of the author or editor name lists. When the value assigned archive applies to |display-authors= is a number greater than or equal to zero, return the number andthe previous state of the 'etal' flag (false by default but may have been most specific if both are set to true if the name list contains?some variant of the text 'et al.'). When the value assigned to |display-authors DeadURL = is the keyword 'etal', return a number that is one greater than thenumber of authors in the list and set the 'etal' flag true. This will cause the list_peopleDeadURL:lower() to display all ofthe names in the name list followed by 'et al.' In all other cases, returns nil and the previous state of the 'etal' flag. ]] local function get_display_authors_editors (max, count, list_name, etal); -- used later when assembling archived text if is_set (maxArchiveURL ) then if 'etal' == max:lower():gsubis_set ("[ '%.]", ''URL) then OriginalURL = URL; -- the :gsub() portion makes 'etal' from a variety save copy of 'et al.' spellings and stylingsoriginal source URL max OriginalURLorigin = count + 1URLorigin; -- number name of authors + 1 so display all author name plus et al.url parameter for error messages etal OriginalFormat = trueFormat; -- overrides value and original |format= if 'no' ~= DeadURL then -- if URL set by extract_namesthen 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 end elseif max:match is_set ('^%d+$'ChapterURL) then -- URL not set so if chapter-url is a string set apply archive url to it OriginalURL = ChapterURL; -- save copy of numberssource chapter's url for archive text max OriginalURLorigin = tonumber (max)ChapterURLorigin; -- make it a numbername of chapter-url parameter for error messages if max >OriginalFormat = count ChapterFormat; -- and original |format= if 'authorsno' ~=DeadURL then ChapterURL = list_name then ArchiveURL -- AUTHORS ONLY swap-in the archive's url ChapterURLorigin = A:ORIGIN('ArchiveURL') - if |display-xxxxors= value greater than or equal to number name of authors/editorsarchive-url parameter for error messages add_maint_cat (ChapterFormat = ArchiveFormat or 'disp_auth_ed', list_name); -- swap in archive's format
end
else -- not a valid keyword or number
table.insert( z.message_tail, { set_error( 'invalid_param_val', {'display-' .. list_name, max}, true ) } ); -- add error message
max = nil; -- unset
end
elseif 'authors' == list_name then -- AUTHORS ONLY need to clear implicit et al category
max = count + 1; -- number of authors + 1
end
return max, etal;
end
if in_array(config.CitationClass, {'web','news','journal', 'magazine', 'pressrelease','podcast', 'newsgroup', 'arxiv'}) 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------------------------< E X T R 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 _ T E X T _ I N _ P :ORIGIN ('ScriptChapter') else is_set (ChapterFormat) chap_param = A G E _ C H E C K >------------------------------:ORIGIN ('ChapterFormat') end
Adds page to Category:CS1 maint: extra text if is_set (chap_param) then -- if |pagewe found one table.insert( z.message_tail, { set_error( 'chapter_ignored', {chap_param}, true ) } ); -- add error message Chapter = or |pages= has what appears ''; -- and set them to empty string to be some form of p. or pp. safe with concatenation TransChapter = ''; ChapterURL = ''; ScriptChapter = ''; ChapterFormat = ''; end else -- otherwise, format chapter / article titleabbreviation in the first characters of local no_quotes = false; -- default assume that we will be quoting the chapter parameter contentvalue 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
check Page and Pages for extraneous p Chapter = format_chapter_title (ScriptChapter, Chapter, TransChapter, p.ChapterURL, ppChapterURLorigin, no_quotes); -- Contribution is also in Chapter if is_set (Chapter) then if 'map' == config.CitationClass and ppis_set (TitleType) then Chapter = Chapter .. ' ' .. at start of parameter value:TitleType; end good pattern: Chapter = Chapter .. ChapterFormat .. sepc .. '^P[^%.P%l]' matches when |page; elseif is_set (sChapterFormat)then -- |chapter= begins PX or P# not set but not Px where x and X are letters and # |chapter-format= is a dgiitso ... bad pattern: Chapter = ChapterFormat .. sepc .. '^[Pp][Pp]' matches matches when |page(s)= begins pp or pP or Pp or PP; -- ... ChapterFormat has error message, we want to see it end]] end
local function extra_text_in_page_check (page) -- local good_pattern = '^P[^%Format main title.P%l]'; local good_pattern = '^P[^%.Pp]'; -- ok to begin with uppercase P: P7 if is_set(pg 7 of section PTitleLink) but not p123 and is_set(page 123Title) TODO: add Gg for PG or Pg?then-- local bad_pattern Title = '^"[Pp][Pp]'; local bad_pattern = '^[Pp]?[Pp]%" .. TitleLink .. "|" .. Title ..?[ %d"]';  if not page:match (good_pattern) and (page:match (bad_pattern) or page:match ('^[Pp]ages?')) then add_maint_cat ('extra_text');"
end
-- if Page:match ('^[Pp]?[Pp]%.?[ %d]') or Page:match ('^[Pp]ages?[ %d]') or
-- Pages:match ('^[Pp]?[Pp]%.?[ %d]') or Pages:match ('^[Pp]ages?[ %d]') then
-- add_maint_cat ('extra_text');
-- end
end
if in_array(config.CitationClass, {'web','news','journal', 'magazine', 'pressrelease','podcast', 'newsgroup', 'mailinglist', 'arxiv'}) 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
--[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >-------------------------------- local TransError = ""; if is_set(TransTitle) thenThis function extracts author / editor names from |vauthors if is_set(Title) then TransTitle = or |veditors= and finds matching |xxxxor-maskn= and" " .. TransTitle; else|xxxxor-linkn TransError = in args" " .. It then returns a table of assembled names just as extract_namesset_error('trans_missing_title', {'title'} ) does.; end end Author / editor names in |vauthors Title = or |veditors= must be in Vancouver system styleTitle .. Corporate or institutional namesTransTitle;may sometimes be required and because such names will often fail the is_good_vanc_name if is_set(Title) and other format compliancethentests, are wrapped in doubled paranethese if not is_set(TitleLink) and is_set(corporate nameURL)then Title = external_link( URL, Title, URLorigin ) to suppress the format tests.. TransError .. Format; URL = ""; Format = ""; else Title = Title .. TransError; end end
This function sets the vancouver error when a reqired comma is missing and when there is a space between an author if is_set(Place) then Place = " " .. wrap_msg ('written's initials, Place, use_lowercase) .. sepc .." "; end
]] if is_set (Conference) then if is_set (ConferenceURL) thenlocal function parse_vauthors_veditors Conference = external_link(argsConferenceURL, vparamConference, list_nameConferenceURLorigin ); end local names Conference = {}sepc .. " " .. Conference .. ConferenceFormat; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn= local v_name_table = {};elseif is_set(ConferenceURL) then local etal Conference = false; -- return value set to true when we find some form of et alsepc .. " " .. vauthors parameter local lastexternal_link( ConferenceURL, firstnil, link, maskConferenceURLorigin ); local corporate = false;end
vparam, etal = name_has_etal (vparam, etal, true); -- find and remove variations on et al. do if not categorize is_set(do it here because et al. might have a periodPosition)then if vparam:find ('%[% local Minutes = A[') or vparam:find (Minutes'%]%]') then -- no wikilinking vauthors names; add_vanc_error ()local Time = A['Time']; end v_name_table = mw.text.split(vparam, "%s*,%s*") -- names are separated by commas
for i, v_name in ipairs(v_name_table) do if v_name:match is_set('^%(%(.+%)%)$'Minutes) then -- corporate authors are wrapped in doubled parenthese to supress vanc formatting and error detection first = ''; -- set to empty string for concatenation and because it may have been set for previous author/editor last = v_name:match if is_set ('^%(%((.+)%)%)$'Time)then corporate = true; elseif string table.findinsert(v_namez.message_tail, "%s") then lastfirstTable = {} lastfirstTable = mw.text.splitset_error(v_name'redundant_parameters', "%s") first = table.remove{wrap_style (lastfirstTable); -- removes and returns value of last element in table which should be author intials last = table.concat(lastfirstTable'parameter', " "'minutes') -- returns a string that is the concatenation of all other names that are not initials if mw.ustring.match (last, '%a+%s+%u+%s+%a+and ') or mw.ustring.match wrap_style (v_name'parameter', ' %u %u$time') then add_vanc_error (}, true ) } ); -- matches last II last; the case when a comma is missing or a space between two intiials
end
Position = " " .. Minutes .. " " .. cfg.messages['minutes'];
else
first if is_set(Time) then local TimeCaption = A['TimeCaption'] if not is_set(TimeCaption) then TimeCaption = cfg.messages['event']; -- set to empty string for concatenation and because it may have been set for previous author/editor last if sepc ~= '.' then TimeCaption = v_nameTimeCaption:lower(); -- last name or single corporate name? Doesn't support multiword corporate names? do we need this? end end if is_set (first) and not mw Position = " " .. TimeCaption .ustring.match (first, "^%u?%u$") then -- first shall contain one or two upper-case letters, nothing else.. Time; add_vanc_error ();end
end
-- this from extract_names () else link Position = select_one( args, cfg" " .aliases[list_name .. '-Link'], 'redundant_parameters', i )Position; mask At = select_one( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i ); names[i] = {last = last, first = first, link = link, mask = mask, corporate=corporate}; -- add this assembled name to our names list
end
return names, etal; -- all done, return our list of names
end
--[[--------------------------< S E L E C T _ A U T H O R _ E D I T O R _ S O U R C E >------------------------ Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase);
Select one of |authors At =, |authorn= / |lastn / firstn=, is_set(At) and (sepc .. " " .. At) or |vauthors""; Position = as the source of the author name list is_set(Position) and (sepc .. " " .. Position) or"";select one of |editors if config.CitationClass =, |editorn= / editor-lastn'map' then local Section = A['Section']; local Sections = / |editor-firstnA['Sections']; local Inset = or |veditorsA['Inset']; if is_set( Inset ) then Inset = as the source of the editor name listsepc .. " " .. wrap_msg ('inset', Inset, use_lowercase); end
Only one of these appropriate three will be used if is_set( Sections ) then Section = sepc .. " " .. The hierarchy is: |authorn= wrap_msg ('sections', Sections, use_lowercase); elseif is_set(and aliasesSection ) highest and |authors= lowest andthensimilarly, |editorn Section = sepc .. " " .. wrap_msg (and aliases'section', Section, use_lowercase) highest and |editors; end At = lowestAt .. Inset .. Section; end
When looking for |authorn= / |editorn= parameters, test |xxxxor1= and |xxxxor2= if is_set (and all of their aliasesLanguage); stops after the secondthentest which mimicks the test used in extract_names Language = language_parameter (Language) when looking for a hole in the author ; -- format, categories, name list. There may be a betterfrom ISO639-1, etc elseway to do Language=""; -- language not specified so make sure this, I just haven't discovered what that way is.an empty string; end
Emits an error message when more than one xxxxor name source is provided Others = is_set(Others) and (sepc .. " " .. Others) or ""; if is_set (Translators) then Others = sepc .. ' Translated by ' .. Translators .. Others; end
In this function, vxxxxors TitleNote = vauthors is_set(TitleNote) and (sepc .. " " .. TitleNote) or veditors""; xxxxors 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 = authors or editors as appropriate" " ..wrap_msg ('edition', Edition); else Edition = ''; end
Series = is_set(Series) and (sepc .. " " .. Series) or ""; OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]]") or ""; Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";
local function select_author_editor_source Volume = format_volume_issue (vxxxxors, xxxxorsVolume, argsIssue, list_name)local lastfirst = false; if select_one( args, cfgconfig.aliases[list_name .. '-Last']CitationClass, 'none'Periodical_origin, 1 ) or -- do this twice incase we have a first 1 without a last1 select_one( args, cfg.aliases[list_name .. '-Last']sepc, 'none', 2 use_lowercase) then lastfirst=true; end
if (is_set (vxxxxors) and true == lastfirst) or -- these are the three error conditions---------------------------------- totally unrelated data ( if is_set (vxxxxorsVia) and is_set (xxxxors)) orthen (true == lastfirst and is_set (xxxxors)) then local err_name; if 'AuthorList' == list_name then -- figure out which name should be used in error message err_name Via = 'author'; else err_name = 'editor'; end table" " .insert( z.message_tail, { set_errorwrap_msg ( 'redundant_parametersvia', {err_name .. '-name-list parameters'}, true ) } Via); -- add error message
end
if true == lastfirst then return 1 end; -- return a number indicating which author name source to use[[ if is_set (vxxxxors) then return 2 endSubscription implies paywall;Registration does not. If both are used in a citation, the subscription required link if is_set (xxxxors) then return 3 end; return 1; -- note is displayed. There are no authors so return 1; error messages for this allows missing author name test to run in case there is a first without last endcondition.
]]
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[[--------------------------< I S _ V A L I D _ P A R A M E T E R _ V A L U E >------------------------------'retrieved']
This function is used to validate a parameter AccessDate = nowrap_date (AccessDate); -- wrap in nowrap span if date in appropriate format if (sepc ~= ".") then retrv_text = retrv_text:lower() end -- if 'citation', lower case AccessDate = substitute (retrv_text, AccessDate); -- add retrieved text -- neither of these work; don's assigned value for those parameters t know why; it seems that have only a limited numbersubstitute() isn't being called of allowable values AccessDate = substitute (yescfg.presentation['accessdate'], y{sepc, true, no, etcAccessDate}); -- allow editors to hide accessdates end if is_set(ID)then ID = sepc .." ".. When the parameter value has not been assigned a value ID; end if "thesis" == config.CitationClass and is_set(missing or emptyDocket) then ID = sepc .." Docket ".. Docket .. ID;in the source template end if "report" == config.CitationClass and is_set(Docket) the function refurns truethen -- for cite report when |docket= is set ID = sepc . If the parameter value . ' ' .. Docket; -- overwrite ID even if |id= is one of the list of allowed values returnssettrue; else, emits an error message and returns false. end
]] ID_list = build_id_list( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo, Class = Class} );
local function is_valid_parameter_value (value, name, possible) if not is_set (valueURL) then return true; -- an empty parameter is ok elseif in_array(value:lower(), possible) then return true; else tableURL = " " .insert( z.message_tail, { set_errorexternal_link( 'invalid_param_val', {nameURL, value}nil, true ) } URLorigin ); -- not an allowed value so add error message return false
end
end
 --[[--------------------------< T E R M I N A T E _ N A M E _ L I S T >---------------------------------------- This function terminates a name list if is_set(author, contributor, editorQuote) with a separator character (sepc) and a spacethenwhen the last character is not a sepc character or when the last three characters are not sepc followed by twoclosing square brackets (close of a wikilink). When either of these is true, the name_list is terminated with asingle space character. ]] local function terminate_name_list (name_list, sepc) if (string.Quote:sub (name_list,-1,-1) == sepc) or (string.'"' and Quote:sub (name_list,-31,-1) == sepc .. ']]"') then -- if first and last name in list ends with sepc charcharacters of quote are quote marks return name_list .. " " Quote = Quote:sub(2,-2); -- don't add anotherstrip them off else end return name_list Quote = sepc .. sepc " " .. wrap_style (' quoted-text', Quote ); -- otherwise terninate the name listwrap in <q>...</q> tags PostScript = ""; -- cs1|2 does not supply terminal punctuation when |quote= is set
end
end
 
 
--[[-------------------------< F O R M A T _ V O L U M E _ I S S U E >----------------------------------------
 
returns the concatenation of the formatted volume and issue parameters as a single string; or formatted volume
or formatted issue, or an empty string if neither are set.
 
]]
local function format_volume_issue (volume, issue, cite_class, origin, sepc, lower)Archived if not is_set (volumeArchiveURL) and then if not is_set (issueArchiveDate) then return ArchiveDate = set_error('archive_missing_date'); end if "no" == DeadURL then if local arch_text = cfg.messages['magazinearchived' ]; if sepc ~="." then arch_text = cite_class or arch_text:lower(in_array ) end Archived = sepc .. " " .. substitute(cite_classcfg.messages['archived-not-dead'], {external_link( ArchiveURL, arch_text, A:ORIGIN('citationArchiveURL') ) .. ArchiveFormat, 'map'ArchiveDate }) and 'magazine' == origin) then; if not is_set (volumeOriginalURL) and is_set then Archived = Archived .. " " .. set_error(issue'archive_missing_url') then; return wrap_msg end elseif is_set(OriginalURL) then -- DeadURL is empty, 'vol-noyes', {sepc'true', volume'y', issue}'unfit', 'usurped' local arch_text = cfg.messages['archived-dead']; if sepc ~= "." then arch_text = arch_text:lower();end elseif is_set if in_array (volumeDeadURL, {'unfit', 'usurped'}) then Archived = sepc .. " " .. 'Archived from the original on ' .. ArchiveDate; -- format already styled return wrap_msg else -- DeadURL is empty, 'yes', 'true', or 'y' Archived = sepc .. " " .. substitute(arch_text, { external_link( OriginalURL, cfg.messages['voloriginal'], {sepcOriginalURLorigin ) .. OriginalFormat, volumeArchiveDate }, lower);-- format already styled end
else
return wrap_msg (local arch_text = cfg.messages['issuearchived-missing']; if sepc ~= "." then arch_text = arch_text:lower() end Archived = sepc .. " " .. substitute( arch_text, {sepcset_error('archive_missing_url'), issueArchiveDate }, lower);
end
elseif is_set (ArchiveFormat) then
Archived = ArchiveFormat; -- if set and ArchiveURL not set ArchiveFormat has error message
else
Archived = ""
end
local vol Lay = ''; if is_set (volumeLayURL) then if is_set(LayDate) then LayDate = " (4 < mw" .. LayDate .ustring.len")" end if is_set(volume)LaySource) then vol LaySource = substitute " &ndash; ''" .. safe_for_italics(cfgLaySource) .messages[. "'j-vol'], {sepc, volume})";
else
vol LaySource = wrap_style ('vol-bold', hyphen_to_dash(volume))"";
end
if sepc == '.' then
Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary'], A:ORIGIN('LayURL') ) .. LayFormat .. LaySource .. LayDate
else
Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower(), A:ORIGIN('LayURL') ) .. 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 (issueTranscript) then if is_set(TranscriptURL) then Transcript = external_link( TranscriptURL, Transcript, TranscriptURLorigin ); end return vol Transcript = sepc .. substitute (cfg.messages['j-issue'].. Transcript .. TranscriptFormat; elseif is_set(TranscriptURL) then Transcript = external_link( TranscriptURL, nil, issueTranscriptURLorigin );
end
return vol;
end
local Publisher;--[[-------------------------< F O R M A T _ P A G E S _ S H E E T S >----------------------------------------- adds static text to one of |page(s)= or |sheet if is_set(sPeriodical)= values and returns it with all of the others set to empty strings.The return order is: page, pages, sheet, sheets Singular has priority over plural when both are provided not in_array(config]] local function format_pages_sheets (pageCitationClass, pages{"encyclopaedia", sheet"web", sheets"pressrelease", cite_class, origin, sepc, nopp, lower"podcast"}) if 'map' == cite_class then -- only cite map supports sheet(s) as in-source locators if is_set (sheetPublisherName) then if 'journal' == origin is_set(PublicationPlace) then return '', '', wrap_msg ('j-sheet', sheet, lower), ''Publisher = PublicationPlace .. ": " .. PublisherName; else return '', '', wrap_msg ('sheet', {sepc, sheet}, lower), ''Publisher = PublisherName;
end
elseif is_set (sheetsPublicationPlace) then if 'journal' Publisher=PublicationPlace; else Publisher = origin ""; end if is_set(PublicationDate) then if is_set(Publisher) then return '', '', ''Publisher = Publisher .. ", " .. wrap_msg ('j-sheetspublished', sheets, lowerPublicationDate);
else
return '', '', '', wrap_msg ('sheets', {sepc, sheets}, lower)Publisher = PublicationDate;
end
end
end  local is_journal = 'journal' == cite_class or (in_array (cite_class, {'citation', 'map'}) and 'journal' == origin);  if is_set (pagePublisher) then if is_journal then return substitute Publisher = " (cfg" .messages['j-page(s)'], page), '', '', ''; elseif not nopp then return substitute (cfg.messages['p-prefix'], {sepc, page}), '', '', ''; else return substitute (cfgPublisher ..messages['nopp'], {sepc, page}"), '', '', ''";
end
elseif else if is_set(pagesPublicationDate) then if is_journal then return substitute PublicationDate = " (cfg" ..messages[wrap_msg ('j-page(s)published'], pagesPublicationDate), '', '', ''.. ")"; elseif tonumberend if is_set(pagesPublisherName) ~= nil and not nopp then -- if pages is only digits, assume a single page number return '', substitute if is_set(cfgPublicationPlace) then Publisher = sepc .. " " .messages['p-prefix'], {. PublicationPlace .. ": " .. PublisherName .. PublicationDate; else Publisher = sepc, pages}), '', ''.. " " .. PublisherName .. PublicationDate; end elseif not nopp is_set(PublicationPlace) then return '', substitute (cfgPublisher= sepc .. " " .. PublicationPlace ..messages['pp-prefix'], {sepc, pages}), '', ''PublicationDate; else return '', substitute (cfg.messages['nopp'], {sepc, pages}), '', ''Publisher = PublicationDate;
end
end
return ''-- 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', ''; -- return empty stringsPeriodical) end --[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------ This is the main function doing the majority of the citation formatting. 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).
]]
 local function citation0( if "speech" == config, args) .CitationClass then --[[ cite speech only Load Input Parameters The argument_wrapper facilitates the mapping of multiple aliases to single internal variable. ]] local A TitleNote = argument_wrapper" ( args Speech)"; local i   -- Pick out annotate the relevant fields from the arguments. Different citation templates -- define different field names for the same underlying things. local author_etal; local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors= local Authors; local NameListFormat = A['NameListFormat'];  do -- to limit scope of selected local selected = select_author_editor_source if is_set (A['Vauthors'], A['Authors'], args, 'AuthorList'Periodical); if 1 == selected then a, author_etal = extract_names (args, 'AuthorList'); -- fetch author list from |authorn= / |lastn= / |firstn=if Periodical, perhaps because of an included |author-linknwebsite=, and or |author-masknjournal=parameter elseif 2 == selected if is_set (Conference) then NameListFormat = 'vanc'; -- override whatever and if |name-list-formatevent= might beis set a, author_etal Conference = parse_vauthors_veditors (args, argsConference .. sepc ..vauthors, 'AuthorList')" "; -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn= elseif 3 == selected thenadd appropriate punctuation to the end of the Conference variable before rendering Authors = A['Authors']; -- use content of |authors=end
end
end
local Coauthors = A['Coauthors'];-- Piece all bits together at last. Here, all should be non-nil. local Others = A['Others'];-- 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 editor_etaltcommon; local e = {}tcommon2; -- editors list from |editor-lastn= / |editor-firstn= pairs or used for book cite when |veditorscontributor=is set local Editors;  do -- to limit scope of selected local selected = select_author_editor_source if in_array(A['Veditors']config.CitationClass, A['Editors']{"journal", args, 'EditorList'"citation"}) and is_set(Periodical);then if 1 is_set(Others) then Others =Others .. sepc .. " " end tcommon = selected thensafe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, eLanguage, Edition, Publisher, Agency, editor_etal = extract_names (argsVolume}, 'EditorList'sepc ); -- fetch editor list from |editorn= / |editor-lastn= / |editor-firstn= elseif in_array(config.CitationClass, |editor-linkn={"book", "citation"}) and |editornot is_set(Periodical) then --maskn=special cases for book cites elseif 2 == selected if is_set (Contributors) then NameListFormat = 'vanc'; -- override whatever |name-list-format= might bewhen we are citing foreword, preface, introduction, etc e, editor_etal tcommon = parse_vauthors_veditors safe_join(args{Title, args.veditorsTitleNote}, 'EditorList'sepc ); -- fetch editor list from |veditorsauthor and other stuff will come after this and before tcommon2 tcommon2 =safe_join( {Conference, Periodical, Format, TitleType, Series, Language, Volume, |editor-linkn=Others, Edition, Publisher, Agency}, and |editor-maskn=sepc ); elseif 3 == selected thenelse Editors tcommon = A['Editors']safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc ); -- use content of |editors=
end
end
local t elseif 'map' == {}; config.CitationClass then -- translators list from |translatorspecial cases for cite map if is_set (Chapter) then -lastn= / translator-firstn= pairs local Translatorsmap in a book; -- assembled translators name listTitleType is part of Chapter t tcommon = extract_names safe_join(args{Title, Format, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=Edition, Scale, Series, -firstn=Language, -linkn=Cartography, -maskn= local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs local Contributors; -- assembled contributors name list local Contribution = A['Contribution']; if in_array(config.CitationClassOthers, {"book"Publisher,"citation"Volume}, sepc ) and not ; elseif is_set(A['Periodical']) then -- |contributor= and |contribution= only supported map in book citesa periodical c tcommon = extract_names safe_join(args{Title, 'ContributorList'TitleType, Format, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc ); else -- fetch contributor list from |contributorn= / |contributora sheet or stand-lastnalone map tcommon =safe_join( {Title, TitleType, Format, Edition, Scale, Series, Language, Cartography, -firstn=Others, -linkn=Publisher}, -maskn=sepc ); end
if 0 < #c then if not is_set (Contribution) then -- |contributor= requires |contribution= table.insert( z.message_tail, { set_error( 'contributor_missing_required_param', elseif 'contributionepisode')}); -- add missing contribution error message c = {}; -- blank the contributors' table; it is used as a flag later end if 0 == #a config.CitationClass then -- |contributorspecial case for cite episode tcommon = requires |author= table.insertsafe_join( z.message_tail{Title, TitleNote, TitleType, Series, Transcript, Language, { set_error( 'contributor_missing_required_param'Edition, 'author')Publisher}, sepc ); -- add missing author error message c = {}; -- blank the contributors' table; it is used as a flag later end end else -- if not a book citeall other CS1 templates if select_one tcommon = safe_join(args{Title, cfg.aliases['ContributorList-Last']TitleNote, Conference, Periodical, Format, TitleType, Series, 'redundant_parameters'Language, 1 ) then -- are there contributor name list parameters? table.insert( z.message_tailVolume, Others, Edition, Publisher, { set_error( 'contributor_ignored')Agency}, sepc ); -- add contributor ignored error message end Contribution = nil; -- unset
end
if not is_valid_parameter_value #ID_list > 0 then ID_list = safe_join(NameListFormat{ sepc .. " ", 'name-list-format' table.concat( ID_list, cfgsepc .keywords['name-list-format']. " " ) then -- only accepted value for this parameter is 'vanc', ID }, sepc ); else NameListFormat ID_list = ''ID; -- anything else, set to empty string
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;
local Year = A['Year'];if is_set(Date) then local PublicationDate = A['PublicationDate']; if is_set (Authors) or is_set (Editors) then -- date follows authors or editors when authors not set local OrigYear = A['OrigYear']; local Date = A['" (" .. Date']..")" .. OrigYear .. sepc .. " "; local LayDate = A['LayDate']; --in paranetheses else ----------------------------------------------- Get title dataneither of authors and editors set 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']; if is_set (TitleLinkstring.sub(tcommon,-1,-1) and false == link_param_ok (TitleLinksepc) then -- if the last character of tcommon is sepc table Date = " " .. Date .insert( z.message_tail, { set_error( 'bad_paramlink', A:ORIGIN('TitleLink'))})OrigYear; -- url or wikilink in |title-linkDate does not begin with sepc else Date =sepc .. " " .. Date .. OrigYear; -- Date begins with sepc end
end
end local Chapter = A[if is_set(Authors) then if is_set(Coauthors) then if 'Chaptervanc']; local ScriptChapter = A['ScriptChapter']; local ChapterLink = NameListFormat then -- = A['ChapterLink']; separate authors and coauthors with proper name-list- deprecated as a parameter but still used internally by cite episodeseparator local TransChapter Authors = A[Authors .. 'TransChapter, '].. Coauthors; local TitleType = A['TitleType']; else local Degree Authors = A[Authors .. 'Degree']; local Docket = A['Docket'].. Coauthors; local ArchiveFormat = A['ArchiveFormat']; end local ArchiveURL = A['ArchiveURL']; end local URL = A['URL'] local URLorigin = A:ORIGIN if not is_set ('URL'Date); then -- get name of parameter that holds URL local ChapterURL = A[when date is set it'ChapterURL']s in parentheses;no Authors termination local ChapterURLorigin Authors = A:ORIGINterminate_name_list ('ChapterURL'Authors, sepc); -- get name of parameter that holds ChapterURLwhen no date, terminate with 0 or 1 sepc and a space local ConferenceFormat = A['ConferenceFormat']; end local ConferenceURL = A['ConferenceURL']; local ConferenceURLorigin = A:ORIGIN if is_set('ConferenceURL'Editors); -- get name of parameter that holds ConferenceURLthen local Periodical in_text = A['Periodical']" "; local Periodical_origin post_text = 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 is_set(config.CitationClass, cfg.templates_using_volumeChapter) and not ('conference' 0 == config.CitationClass and not is_set (Periodical)) #c then Volume in_text = Ain_text .. cfg.messages['Volumein'];.. " " end if in_array (configsepc ~= '.CitationClass, cfg.templates_using_issue) and not (in_array (config.CitationClass, {'conference', 'map'}) and not is_set then in_text = in_text:lower(Periodical))thenend -- lowercase for cs2 else Issue if EditorCount <= A['Issue'];1 then end local Position post_text = ''; if not in_array (config", " ..CitationClass, cfg.templates_not_using_page) then Page = Amessages['Pageeditor']; Pages = hyphen_to_dash( A['Pages'] ); else At post_text = A", " .. cfg.messages['Ateditors']; end end local Edition Editors = A['Edition']; local PublicationPlace = A['PublicationPlace'] local Place = A['Place']; local PublisherName = A['PublisherName']; local RegistrationRequired = A['RegistrationRequired']; if not is_valid_parameter_value terminate_name_list (RegistrationRequired, 'registration'in_text .. Editors .. post_text, cfg.keywords ['yes_true_y']sepc) then RegistrationRequired=nil;-- terminate with 0 or 1 sepc and a space
end
local SubscriptionRequired = A['SubscriptionRequired']; if not is_valid_parameter_value is_set (SubscriptionRequiredContributors) then -- book cite and we're citing the intro, preface, etc local by_text = sepc .. 'subscription', .. cfg.keywords messages['yes_true_yby']) then SubscriptionRequired=nil; end  local Via = A[.. 'Via']; local AccessDate if (sepc ~= A['AccessDate.'];) then by_text = by_text:lower() end -- lowercase for cs2 local ArchiveDate Authors = A['ArchiveDate']by_text .. Authors; -- author follows title so tweak it here if is_set (Editors) then -- when Editors make sure that Authors gets terminated local Agency Authors = A['Agency']terminate_name_list (Authors, sepc); -- terminate with 0 or 1 sepc and a space local DeadURL = A['DeadURL'] end if not is_valid_parameter_value is_set (DeadURL, 'dead-url', cfg.keywords ['deadurl']Date) then -- when date is set it's in config.defaults to 'yes'parentheses; no Contributors termination DeadURL Contributors = ''terminate_name_list (Contributors, sepc); -- anything 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, set to empty stringDate, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc );
end
  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'];elseif is_set(Editors) then if not is_valid_parameter_value is_set(IgnoreISBN, 'ignore-isbn-error', cfg.keywords ['yes_true_y']Date) then IgnoreISBN if EditorCount <= nil; -- anything else, set to empty string end1 then local Embargo Editors = AEditors .. ", " .. cfg.messages['Embargoeditor']; local Class = A['Class']; -- arxiv class identifier else local ID_list Editors = extract_ids( args );  local Quote = AEditors .. ", " .. cfg.messages['Quoteeditors']; end local LayFormat = A['LayFormat']; else local LayURL if EditorCount <= A['LayURL'];1 then local LaySource Editors = AEditors .. " (" .. cfg.messages['LaySourceeditor']; local Transcript = A['Transcript']; local TranscriptFormat = A['TranscriptFormat']; local TranscriptURL = A['TranscriptURL'] local TranscriptURLorigin = A:ORIGIN('TranscriptURL'.. "); -- get name of parameter that holds TranscriptURL" .. sepc .. " " else local LastAuthorAmp Editors = A['LastAuthorAmp']; if not is_valid_parameter_value Editors .. " (LastAuthorAmp, 'last-author-amp', " .. cfg.keywords messages['yes_true_yeditors'].. ") then" .. sepc .. " " LastAuthorAmp = nil; -- set to empty stringend
end
local no_tracking_cats text = A['NoTracking']safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc ); else if not is_valid_parameter_value config.CitationClass=="journal" and is_set(no_tracking_catsPeriodical) then text = safe_join( {Chapter, Place, tcommon, pgtext, Date, 'no-tracking'idcommon}, cfg.keywords ['yes_true_y']sepc ) then; else no_tracking_cats text = nilsafe_join( {Chapter, Place, tcommon, Date, pgtext, idcommon}, sepc ); -- set to empty string
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 );
--these are used by Now enclose the whole thing in a <cite interview/> element local Callsign options = A['Callsign']{}; local City = A['City']; local Program if is_set(config.CitationClass) and config.CitationClass ~= A['Program'];"citation" then --local variables that are not cs1 parameters local use_lowercase options.class = config.CitationClass; -- controls capitalization of certain static text local this_page options.class = mw"citation " ..titleconfig.getCurrentTitle()CitationClass; -- also used for COinS and class=citation required for language local anchor_year; -- blue highlight when used in the CITEREF identifier local COinS_date = {}; -- holds date info extracted from with |dateref= for the COinS metadata by Module:Date verification -- set default parameter values defined by |mode= parameter. If |mode= is empty or omitted, use CitationClass to set these values local Mode = A['Mode'];else if not is_valid_parameter_value (Mode, 'mode', cfg options.keywords['mode']) then Mode class = ''"citation";
end
local sepc; -- separator between citation elements for CS1 a period, for CS2, a comma local PostScript; local if is_set(Ref; sepc, PostScript, ) and Ref = set_style (Mode:lower(), A[~= "none" then -- set reference anchor if appropriate local id = Ref if ('PostScript'], A[harv'== Ref'], config.CitationClass)then local namelist = {}; -- holds selected contributor, author, editor name list use_lowercase local year = first_set ( sepc == '{Year, anchor_year},' 2); -- used to control capitalization Year first for legacy citations and for certain static textYMD dates that require disambiguation
--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) #c > 0 then -- ignore if we are already not going to categorize this pagethere is a contributor list if in_array (this_page.nsText, cfg.uncategorized_namespaces) then no_tracking_cats namelist = "true"c; -- set no_tracking_cats end for _,v in ipairs (cfg.uncategorized_subpages) do -- cycle through page name patternsselect it if this_page.text:match (v) elseif #a > 0 then -- test page name against each patternor an author list no_tracking_cats namelist = "true"a; elseif #e > 0 then -- set no_tracking_catsor an editor list breaknamelist = e; -- bail out if one is found
end
id = anchor_id (namelist, year); -- go make the CITEREF anchor
end
options.id = id;
end
if string.len(text:gsub("<span[^>/]*>.-- check for extra |page</span>", ""):gsub("%b<>","")) <=, |pages2 then z.error_categories = or |at{}; text = parameters. set_error(also sheet and sheets while we're at itempty_citation'); select_one( args, z.message_tail = {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'redundant_parameters' ); -- this is a dummy call simply to get the error message and categoryend local NoPP = A['NoPP'] if is_set (NoPPoptions.id) and is_valid_parameter_value then text = '<cite id="' .. mw.uri.anchorEncode(NoPP, options.id) ..'nopp" class="', cfg.keywords [. mw.text.nowiki(options.class) .. 'yes_true_y">']) then NoPP = true.. text .. "</cite>";
else
NoPP text = nil'<cite class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</cite>"; -- unset, used as a flag later end
local empty_span = '<span style="display:none;">&nbsp;</span>'; -- Note: Using display: none on the COinS span breaks some clients. local OCinS = '<span title="' .. OCinSoutput .. '" class="Z3988">' .. empty_span .. '</span>'; text = text .. OCinS; if is_set(Page) #z.message_tail ~= 0 then if is_settext = text .. " "; for i,v in ipairs(Pagesz.message_tail ) or do if is_set(Atv[1]) then Pages if i == #z.message_tail then text = ''text .. error_comment( v[1], v[2] ); -- unset the others At else text = ''text .. error_comment( v[1] .. "; ", v[2] ); end end
end
end  if #z.maintenance_cats ~= 0 then extra_text_in_page_check (Page); -- add this page to maint cat if |pagetext = value begins with what looks like ptext . or pp. elseif is_set(Pages) then'<span class="citation-comment" style="display:none; color:#33aa33">'; if is_setfor _, v in ipairs(Atz.maintenance_cats ) thendo -- append maintenance categories At text = text .. ' ' .. v .. '([[:Category:' .. v ..'|link]])'; -- unset
end
extra_text_in_page_check (Pages); -- add this page to maint cat if |pagestext = value begins with what looks like ptext . or pp. end  -- both |publication-place= and |place= (|location=) allowed if different if not is_set(PublicationPlace) and is_set(Place) then PublicationPlace = Place'</span>'; -- promote |place= maintenance mesages (|location=realy just the names of the categories for now) to |publication-place
end
no_tracking_cats = no_tracking_cats:lower(); if PublicationPlace in_array(no_tracking_cats, {"", "no", "false", "n"}) then for _, v in ipairs( z.error_categories ) do text =text .. '[[Category:' .. v ..']]'; end for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories text = Place then Place = text .. '[[Category:' .. v ..']]'; end for _, v in ipairs( z.properties_cats ) do -- donappend maintenance categories text = text .. 't need both if they are the same[[Category:' .. v ..']]'; end end
--[[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 |titlereturn text |encyclopedia then map |encyclopedia to |titleend
|trans_title maps to |trans_chapter when |title is re-mapped |url maps to |chapterurl when |title is remapped-[[--------------------------< C S 1 . C I T A T I O N >------------------------------------------------------
All other combinations of |encyclopedia, |title, and |article are not modifiedThis is used by templates such as {{cite book}} to create the actual citation text.
]]
function cs1.citation(frame) local Encyclopedia pframe = A[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 ('EncyclopediaModule: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 (utilities); -- so that functions in Date validataion can see 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
if ( config.CitationClass dates == "encyclopaedia" ) or ( configvalidation.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; if not is_set (ChapterURL) and is_set (TitleLink) then Chapter= '[[' .. 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']dates; -- 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 interview if (config.CitationClass == "interview") then if is_set(Program) then ID = ' ' .. Program; end if is_set(Callsign) then if is_set(ID) then ID = ID .. sepc .. ' ' .. Callsign; else ID = ' ' .. Callsign; end end if is_set(City) then if is_set(ID) then ID = ID .. sepc .. ' ' .. City; else ID = ' ' .. City; end end  if is_set(Others) then if is_set(TitleType) then Others = ' ' .. TitleType .. ' with ' .. Others; TitleType = ''; else Others = ' ' .. 'Interview with ' .. Others; end else Others = '(Interview)'; 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; 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']; 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']; if is_set (SeriesLink) and false == link_param_ok (SeriesLink) then table.insert( z.message_tail, { set_error( 'bad_paramlink', A:ORIGIN('SeriesLink'))}); end 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; 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 = '[[' .. ChapterLink .. '|' .. Chapter .. ']]'; -- ok to wikilink elseif is_set (ChapterLink) and is_set (ChapterURL) then -- if both are set, URL links episode; Series = '[[' .. ChapterLink .. '|' .. Series .. ']]'; -- series links with ChapterLink (episodelink -> TitleLink -> ChapterLink) ugly 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 = '[[' .. 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}}, before generation of COinS data. if 'arxiv' == config.CitationClass then if not is_set (ID_list['ARXIV']) then -- |arxiv= or |eprint= required for cite arxiv table.insert( z.message_tail, { set_error( 'arxiv_missing', {}, true ) } ); -- add error message elseif is_set (Series) then -- series is an alias of version ID_list['ARXIV'] = ID_list['ARXIV'] .. Series; -- concatenate version onto the end of the arxiv identifier Series = ''; -- unset deprecated_parameter ('version'); -- deprecated parameter but only for cite arxiv end if first_set ({AccessDate, At, Chapter, Format, Page, Pages, Periodical, PublisherName, URL, -- a crude list of parameters that are not supported by cite arxiv ID_list['ASIN'], ID_list['BIBCODE'], ID_list['DOI'], ID_list['ISBN'], ID_list['ISSN'], ID_list['JFM'], ID_list['JSTOR'], ID_list['LCCN'], ID_list['MR'], ID_list['OCLC'], ID_list['OL'], ID_list['OSTI'], ID_list['PMC'], ID_list['PMID'], ID_list['RFC'], ID_list['SSRN'], ID_list['USENETID'], ID_list['ZBL']},27) then table.insert( z.message_tail, { set_error( 'arxiv_params_not_supported', {}, true ) } ); -- add error message  AccessDate= ''; -- set these to empty string; not supported in cite arXiv PublisherName = ''; -- (if the article has been published, use cite journal, or other) Chapter = ''; URL = ''; Format = ''; Page = ''; Pages = ''; At = ''; end Periodical = 'arXiv'; -- set to arXiv for COinS; after that, must be set to empty string end -- handle type parameter for those CS1 citations that have default values if in_array(config.CitationClass, {"AV-media-notes", "DVD-notes", "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 .. " thesis"; end end  if is_set(TitleType) then -- if type parameter is specified TitleType = substitute( cfg.messages['type'], TitleType); -- display it in parentheses end -- legacy: promote concatenation of |month=, and |year= to Date if Date not set; or, promote PublicationDate to Date if neither Date nor Year are set. 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 end 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 wherewe get the date used in the metadata. Date validation supporting code is in imported functions from Module:Citation/CS1/Date_validation]]Date validation do -- create defined block to contain local variables error_message and mismatch local error_message = ''; -- AirDate has been promoted to Date so not necessary to check it anchor_year, error_message = dates({['access-date']=AccessDate, ['archive-date']=ArchiveDate, ['date']=Date, ['doi-broken-date']=DoiBroken, ['embargo']=Embargo, ['lay-date']=LayDate, ['publication-date']=PublicationDate, ['year']=Year}, COinS_date);  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 validation.. ', '; -- 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 is_set(error_message) then table.insert( z.message_tail, { set_error( 'bad_date', {error_message}, true ) } )year_date_check; -- add this error message end end -- end of do -- Account for the oddity that is {{cite journal}} with |pmcreformat_dates = 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= 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 ['Map'] = Map, ['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}}, AFTER generation of COinS data. if 'arxiv' == config.CitationClass then -- we have set rft.jtitle in COinS to arXiv, now unset so it isn't displayed Periodical = ''; -- periodical not allowed in cite arxiv; 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') )); 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 maximum; 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 };  do -- do editor name list first because coauthors can modify control table maximum , editor_etal = get_display_authors_editors (A['DisplayEditors'], #e, 'editors', editor_etal); -- Preserve old-style implicit et al. if not is_set(maximum) and #e == 4 then maximum = 3; table.insert( z.message_tail, { set_error('implict_etal_editor', {}, true) } ); end  control.maximum = maximum; last_first_list, EditorCount = list_people(control, e, editor_etal, 'editor');  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 translators control.maximum = #t; -- number of translators Translators = list_people(control, t, false, 'translator'); -- et al not currently supported end do -- now do contributors control.maximum = #c; -- number of contributors Contributors = list_people(control, c, false, 'contributor'); -- et al not currently supported end do -- now do authors control.maximum , author_etal = get_display_authors_editors (A['DisplayAuthors'], #a, 'authors', author_etal);  if is_set(Coauthors) then -- if the coauthor field is also used, prevent ampersand and et al. formatting. control.lastauthoramp = nil; control.maximum = #a + 1; end last_first_list = list_people(control, a, author_etal, 'author');  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 not is_set(Authors) and is_set(Coauthors) then -- coauthors aren't displayed if one of authors=, authorn=, or lastn= isn't specified table.insert( zvalidation.message_tail, { set_error('coauthors_missing_author', {}, true) } )reformat_dates; -- emit error message 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'}) 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 --and if in_array(config.CitationClass, {"web","podcast", "mailinglist"}) then -- Test if cite web or cite podcast |url= is missing or empty table.insert( z.message_tail, { set_error( 'cite_web_url', {}, true ) } ); end -- Test if accessdate is given without giving a URL if is_set(AccessDate) and not is_set(ChapterURL)then -- ChapterURL may be set when the others are not set; TODO: move this to a separate test? table.insert( z.message_tail, { set_error( 'accessdate_missing_url', {}, true ) } ); AccessDate = ''; end end  local OriginalURL, OriginalURLorigin, OriginalFormat; -- TODO: swap chapter and title here so that archive applies to most specific if both are set? DeadURL = DeadURL:lower(); -- used later when assembling archived text if is_set( ArchiveURL ) then if 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= if 'no' ~= DeadURL 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 end elseif is_set (ChapterURL) then -- URL not set so 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 |format= if 'no' ~= DeadURL 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 end end end  if in_array(config.CitationClass, {'web','news','journal', 'magazine', 'pressrelease','podcast', 'newsgroup', 'arxiv'}) 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); -- Contribution is also in Chapter if is_set (Chapter) then if 'map' == config.CitationClass and is_set (TitleType) then Chapter = Chapter .. ' ' .. TitleType; end Chapter = Chapter .. ChapterFormat .. 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 is_set(TitleLink) and is_set(Title) then Title = "[[" .. TitleLink .. "|" .. Title .. "]]" end  if in_array(config.CitationClass, {'web','news','journal', 'magazine', 'pressrelease','podcast', 'newsgroup', 'mailinglist', 'arxiv'}) 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  TransError = ""; if is_set(TransTitle) then if is_set(Title) then TransTitle = " " .. TransTitle; else TransError = " " .. set_error( 'trans_missing_title', {'title'} ); end end Title = Title .. TransTitle; if is_set(Title) then if not is_set(TitleLink) and is_set(URL) then Title = external_link( URL, Title, URLorigin ) .. TransError .. Format; URL = ""; Format = ""; else Title = Title .. TransError; end 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 ); end Conference = sepc .. " " .. Conference .. ConferenceFormat; elseif is_set(ConferenceURL) then Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURLorigin ); 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; end  Others = is_set(Others) and (sepc .. " " .. Others) or ""; if is_set (Translators) then Others = sepc .. ' Translated by ' .. Translators .. Others; 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 ""; 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 linknote 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 'citation', lower case AccessDate = substitute (retrv_text, AccessDate); -- add retrieved text -- neither of these work; don't know why; it seems that substitute() isn't being called 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, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo, Class = Class} );  if is_set(URL) then URL = " " .. external_link( URL, nil, URLorigin ); 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 "no" == DeadURL then local arch_text = cfg.messages['archived']; if sepc ~= "." then arch_text = arch_text:lower() end Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'], { external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL') ) .. ArchiveFormat, ArchiveDate } ); if not is_set(OriginalURL) then Archived = Archived .. " " .. set_error('archive_missing_url'); end elseif is_set(OriginalURL) then -- DeadURL is empty, 'yes', 'true', 'y', 'unfit', 'usurped' local arch_text = cfg.messages['archived-dead']; if sepc ~= "." then arch_text = arch_text:lower() end if in_array (DeadURL, {'unfit', 'usurped'}) then Archived = sepc .. " " .. 'Archived from the original on ' .. ArchiveDate; -- format already styled else -- DeadURL is empty, 'yes', 'true', or 'y' Archived = sepc .. " " .. substitute( arch_text, { external_link( OriginalURL, cfg.messages['original'], OriginalURLorigin ) .. 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') ) .. LayFormat .. LaySource .. LayDate else Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower(), A:ORIGIN('LayURL') ) .. 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 ); end Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat; elseif is_set(TranscriptURL) then Transcript = external_link( TranscriptURL, nil, TranscriptURLorigin ); end  local Publisher; if is_set(Periodical) and not in_array(config.CitationClass, {"encyclopaedia","web","pressrelease","podcast"}) then if is_set(PublisherName) then if is_set(PublicationPlace) then Publisher = PublicationPlace .. ": " .. PublisherName; else Publisher = PublisherName; end elseif is_set(PublicationPlace) then Publisher= PublicationPlace; else Publisher = ""; end if is_set(PublicationDate) then if is_set(Publisher) then Publisher = Publisher .. ", " .. wrap_msg ('published', PublicationDate); else Publisher = PublicationDate; end end if is_set(Publisher) then Publisher = " (" .. Publisher .. ")"; end else 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 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 thatthe 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 = Others .. sepc .. " " end 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 is_set(Coauthors) then if 'vanc' == NameListFormat then -- separate authors and coauthors with proper name-list-separator Authors = Authors .. ', ' .. Coauthors; else Authors = Authors .. '; ' .. Coauthors; end end 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() end -- lowercase for cs2 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) 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 config.CitationClass=="journal" 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 utilities.is_set; -- set reference anchor if appropriateimported functions from Module:Citation/CS1/Utilities local id in_array = Refutilities.in_array; if ('harv' substitute =utilities.substitute; error_comment = Ref ) thenutilities.error_comment; set_error = utilities.set_error; local namelist select_one = {}utilities.select_one; add_maint_cat = utilities.add_maint_cat; wrap_style = utilities.wrap_style; -- holds selected contributor, author, editor name list-- local year safe_for_italics = first_set (Year, anchor_year)utilities.safe_for_italics; -- Year first for legacy citations and for YMD dates that require disambiguation local year remove_wiki_link = first_set ({Year, anchor_year}, 2)utilities.remove_wiki_link; -- 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 id = anchor_id (namelist, year); -- go make the CITEREF anchor end options.id = id; end if string.len(text:gsub("<span[^>/]*>.-</span>", ""):gsub("%b<>","")) <z = 2 then zutilities.error_categories = {}; text = set_error('empty_citation'); z.message_tail = {}; end if is_set(options.id) then text = '<cite id="' .. mw.uri.anchorEncode(options.id) ..'" class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</cite>"; else text = '<cite class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</cite>"; end   local empty_span = '<span style="display:none;">&nbsp;</span>'; -- Notetable of error and category tables in Module: Using display: none on the COinS span breaks some clients. local OCinS = '<span title="' .. OCinSoutput .. '" class="Z3988">' .. empty_span .. '<Citation/CS1/span>'; text = text .. OCinS; if #z.message_tail ~= 0 then text = text .. " "; for i,v in ipairs( z.message_tail ) do if is_set(v[1]) then if i == #z.message_tail then text = text .. error_comment( v[1], v[2] ); else text = text .. error_comment( v[1] .. "; ", v[2] ); end end end endUtilities
if #z.maintenance_cats ~= 0 then text extract_ids = text .identifiers. '<span class="citation-comment" style="display:noneextract_ids; color:#33aa33">'; for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories text = text .. ' ' .. v .. ' ([[imported functions from Module:Category:' .. v ..'|link]])'; end text = text .. '<Citation/CS1/span>'; -- maintenance mesages (realy just the names of the categories for now) end Utilities no_tracking_cats build_id_list = no_tracking_cats:lower()identifiers.build_id_list; if in_array(no_tracking_cats, {"", "no", "false", "n"}) then for _, v in ipairs( z.error_categories ) do text is_embargoed = text .. '[[Category:' identifiers.. v ..']]'is_embargoed; end for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories text = text .. '[[Category:' .. v ..']]'; end for _, v in ipairs( z.properties_cats ) do -- append maintenance categories text = text .. '[[Category:' .. v ..']]'; end end
return textend --[[--------------------------< H A S _ I N V I S I B L E _ C H A R S >--------------------------------------make_coins_title = metadata.make_coins_title; --imported functions from Module:Citation/CS1/COinS This function searches a parameter's value for nonprintable or invisible characters. The search stops at the first match get_coins_pages = metadata.get_coins_pages; Sometime after this module is done with rendering a citation, some C0 control characters are replaced with thereplacement character. That replacement character is not detected by this test though it is visible to readersof the rendered citation. This function will detect the replacement character when it is part of the wikisource COinS = metadata.COinS;
Output of this function is an error message that identifies the character or the Unicode group that the character
belongs to along with its position in the parameter value.
 
]]
--[[
local function has_invisible_chars (param, v)
local position = '';
local i=1;
 
while cfg.invisible_chars[i] do
local char=cfg.invisible_chars[i][1] -- the character or group name
local pattern=cfg.invisible_chars[i][2] -- the pattern used to find it
v = mw.text.unstripNoWiki( v ); -- remove nowiki stripmarkers
position = mw.ustring.find (v, pattern) -- see if the parameter value contains characters that match the pattern
if position then
table.insert( z.message_tail, { set_error( 'invisible_char', {char, wrap_style ('parameter', param), position}, true ) } ); -- add error message
return; -- and done with this parameter
end
i=i+1; -- bump our index
end
end
]]
 
--[[--------------------------< Z . C I T A T I O N >----------------------------------------------------------
 
This is used by templates such as {{cite book}} to create the actual citation text.
 
]]
 
function z.citation(frame)
local pframe = frame:getParent()
local validation;
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 Configuration and Whitelist and ...
whitelist = mw.loadData ('Module:Citation/CS1/Whitelist/sandbox');
validation = require ('Module:Citation/CS1/Date_validation/sandbox'); -- ... sandbox version of date validation code
 
else -- otherwise
cfg = mw.loadData ('Module:Citation/CS1/Configuration'); -- load live versions of Configuration and Whitelist and ...
whitelist = mw.loadData ('Module:Citation/CS1/Whitelist');
validation = require ('Module:Citation/CS1/Date_validation'); -- ... live version of date validation code
end
 
dates = validation.dates; -- imported functions
year_date_check = validation.year_date_check;
 
local args = {};
local suggestions = {};
end
return zcs1;
Anonymous user

Navigation menu