Difference between revisions of "Module:Citation/CS1"

Jump to navigation Jump to search
sync from sandbox;
m (1 revision imported: Content from Template:Reflist on Wikipedia)
(sync from sandbox;)
Line 1: Line 1:
require ('Module:No globals');
require ('strict');


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


local utilities; -- functions in Module:Citation/CS1/Utilities
local utilities; -- functions in Module:Citation/CS1/Utilities
local z ={}; -- table of tables in Module:Citation/CS1/Utilities
local z = {}; -- table of tables in Module:Citation/CS1/Utilities


local identifiers; -- functions and tables in Module:Citation/CS1/Identifiers
local identifiers; -- functions and tables in Module:Citation/CS1/Identifiers
Line 28: Line 28:
local added_vanc_errs; -- Boolean flag so we only emit one Vancouver error / category
local added_vanc_errs; -- Boolean flag so we only emit one Vancouver error / category
local added_generic_name_errs; -- Boolean flag so we only emit one generic name error / category and stop testing names once an error is encountered
local added_generic_name_errs; -- Boolean flag so we only emit one generic name error / category and stop testing names once an error is encountered
local added_numeric_name_errs; -- Boolean flag so we only emit one numeric name error / category and stop testing names once an error is encountered
local added_numeric_name_maint; -- Boolean flag so we only emit one numeric name maint category and stop testing names once a category has been emitted
local Frame; -- holds the module's frame table
local Frame; -- holds the module's frame table
local is_preview_mode; -- true when article is in preview mode; false when using 'Preview page with this template' (previewing the module)
local is_preview_mode; -- true when article is in preview mode; false when using 'Preview page with this template' (previewing the module)
Line 154: Line 156:
end
end


for _, d in ipairs ({'cash', 'company', 'today', 'org'}) do -- look for single letter second level domain names for these top level domains
for _, d in ipairs (cfg.single_letter_2nd_lvl_domains_t) do -- look for single letter second level domain names for these top level domains
if domain:match ('%f[%w][%w]%.' .. d) then
if domain:match ('%f[%w][%w]%.' .. d) then
return true
return true
Line 265: Line 267:


local function link_title_ok (link, lorig, title, torig)
local function link_title_ok (link, lorig, title, torig)
local orig;
local orig;
if utilities.is_set (link) then -- don't bother if <param>-link doesn't have a value
if utilities.is_set (link) then -- don't bother if <param>-link doesn't have a value
if not link_param_ok (link) then -- check |<param>-link= markup
if not link_param_ok (link) then -- check |<param>-link= markup
Line 401: Line 403:
utilities.set_message ('err_bare_url_missing_title', {utilities.wrap_style ('parameter', source)});
utilities.set_message ('err_bare_url_missing_title', {utilities.wrap_style ('parameter', source)});
else
else
error (cfg.messages["bare_url_no_origin"]);
error (cfg.messages["bare_url_no_origin"]); -- programmer error; valid parameter name does not have matching meta-parameter
end
end
end
end
Line 529: Line 531:
lang = script_value:match('^(%l%l%l?)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script
lang = script_value:match('^(%l%l%l?)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script
if not utilities.is_set (lang) then
if not utilities.is_set (lang) then
utilities.set_message ('err_script_parameter', {script_param, 'missing title part'}); -- prefix without 'title'; add error message
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing title part']}); -- prefix without 'title'; add error message
return ''; -- script_value was just the prefix so return empty string
return ''; -- script_value was just the prefix so return empty string
end
end
-- if we get this far we have prefix and script
-- if we get this far we have prefix and script
name = cfg.lang_code_remap[lang] or mw.language.fetchLanguageName( lang, cfg.this_wiki_code ); -- get language name so that we can use it to categorize
name = cfg.lang_tag_remap[lang] or mw.language.fetchLanguageName( lang, cfg.this_wiki_code ); -- get language name so that we can use it to categorize
if utilities.is_set (name) then -- is prefix a proper ISO 639-1 language code?
if utilities.is_set (name) then -- is prefix a proper ISO 639-1 language code?
script_value = script_value:gsub ('^%l+%s*:%s*', ''); -- strip prefix from script
script_value = script_value:gsub ('^%l+%s*:%s*', ''); -- strip prefix from script
Line 540: Line 542:
utilities.add_prop_cat ('script', {name, lang})
utilities.add_prop_cat ('script', {name, lang})
else
else
utilities.set_message ('err_script_parameter', {script_param, 'unknown language code'}); -- unknown script-language; add error message
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['unknown language code']}); -- unknown script-language; add error message
end
end
lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute
lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute
else
else
utilities.set_message ('err_script_parameter', {script_param, 'invalid language code'}); -- invalid language code; add error message
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['invalid language code']}); -- invalid language code; add error message
lang = ''; -- invalid so set lang to empty string
lang = ''; -- invalid so set lang to empty string
end
end
else
else
utilities.set_message ('err_script_parameter', {script_param, 'missing prefix'}); -- no language code prefix; add error message
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing prefix']}); -- no language code prefix; add error message
end
end
script_value = utilities.substitute (cfg.presentation['bdi'], {lang, script_value}); -- isolate in case script is RTL
script_value = utilities.substitute (cfg.presentation['bdi'], {lang, script_value}); -- isolate in case script is RTL
Line 764: Line 766:
if mw.ustring.find (v, cfg.indic_script) then -- it's ok if one of the Indic scripts
if mw.ustring.find (v, cfg.indic_script) then -- it's ok if one of the Indic scripts
position = nil; -- unset position
position = nil; -- unset position
elseif cfg.emoji[mw.ustring.codepoint (v, position+1)] then -- is zwj followed by a character listed in emoji{}?
elseif cfg.emoji_t[mw.ustring.codepoint (v, position+1)] then -- is zwj followed by a character listed in emoji{}?
position = nil; -- unset position
position = nil; -- unset position
end
end
Line 1,037: Line 1,039:
end
end
end
end
if nil == mw.ustring.find (last, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143%-%s%']*$") or
if nil == mw.ustring.find (last, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191%-%s%']*$") or
nil == mw.ustring.find (first, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143%-%s%'%.]*$") then
nil == mw.ustring.find (first, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191%-%s%'%.]*$") then
add_vanc_error (cfg.err_msg_supl['non-Latin char'], position);
add_vanc_error (cfg.err_msg_supl['non-Latin char'], position);
return false; -- not a string of Latin characters; Vancouver requires Romanization
return false; -- not a string of Latin characters; Vancouver requires Romanization
Line 1,063: Line 1,065:
]]
]]


local function reduce_to_initials(first, position)
local function reduce_to_initials (first, position)
local name, suffix = mw.ustring.match(first, "^(%u+) ([%dJS][%drndth]+)$");
if first:find (',', 1, true) then
return first; -- commas not allowed; abandon
end
 
local name, suffix = mw.ustring.match (first, "^(%u+) ([%dJS][%drndth]+)$");


if not name then -- if not initials and a suffix
if not name then -- if not initials and a suffix
name = mw.ustring.match(first, "^(%u+)$"); -- is it just initials?
name = mw.ustring.match (first, "^(%u+)$"); -- is it just initials?
end
end


Line 1,085: Line 1,091:
end -- if here then name has 3 or more uppercase letters so treat them as a word
end -- if here then name has 3 or more uppercase letters so treat them as a word


local initials, names = {}, {}; -- tables to hold name parts and initials
local initials_t, names_t = {}, {}; -- tables to hold name parts and initials
local i = 1; -- counter for number of initials
local i = 1; -- counter for number of initials


names = mw.text.split (first, '[%s,]+'); -- split into a table of names and possible suffix
names_t = mw.text.split (first, '[%s%-]+'); -- split into a sequence of names and possible suffix


while names[i] do -- loop through the table
while names_t[i] do -- loop through the sequence
if 1 < i and names[i]:match ('[%dJS][%drndth]+%.?$') then -- if not the first name, and looks like a suffix (may have trailing dot)
if 1 < i and names_t[i]:match ('[%dJS][%drndth]+%.?$') then -- if not the first name, and looks like a suffix (may have trailing dot)
names[i] = names[i]:gsub ('%.', ''); -- remove terminal dot if present
names_t[i] = names_t[i]:gsub ('%.', ''); -- remove terminal dot if present
if is_suffix (names[i]) then -- if a legitimate suffix
if is_suffix (names_t[i]) then -- if a legitimate suffix
table.insert (initials, ' ' .. names[i]); -- add a separator space, insert at end of initials table
table.insert (initials_t, ' ' .. names_t[i]); -- add a separator space, insert at end of initials sequence
break; -- and done because suffix must fall at the end of a name
break; -- and done because suffix must fall at the end of a name
end -- no error message if not a suffix; possibly because of Romanization
end -- no error message if not a suffix; possibly because of Romanization
end
end
if 3 > i then
if 3 > i then
table.insert (initials, mw.ustring.sub(names[i], 1, 1)); -- insert the initial at end of initials table
table.insert (initials_t, mw.ustring.sub (names_t[i], 1, 1)); -- insert the initial at end of initials sequence
end
end
i = i + 1; -- bump the counter
i = i + 1; -- bump the counter
end
end
return table.concat(initials) -- Vancouver format does not include spaces.
return table.concat (initials_t); -- Vancouver format does not include spaces.
end
 
 
--[[--------------------------< I N T E R W I K I _ P R E F I X E N _ G E T >----------------------------------
 
extract interwiki prefixen from <value>.  Returns two one or two values:
false – no prefixen
nil – prefix exists but not recognized
project prefix, language prefix – when value has either of:
:<project>:<language>:<article>
:<language>:<project>:<article>
project prefix, nil – when <value> has only a known single-letter prefix
nil, language prefix – when <value> has only a known language prefix
 
accepts single-letter project prefixen: 'd' (wikidata), 's' (wikisource), and 'w' (wikipedia) prefixes; at this
writing, the other single-letter prefixen (b (wikibook), c (commons), m (meta), n (wikinews), q (wikiquote), and
v (wikiversity)) are not supported.
 
]]
 
local function interwiki_prefixen_get (value, is_link)
if not value:find (':%l+:') then -- if no prefix
return false; -- abandon; boolean here to distinguish from nil fail returns later
end
 
local prefix_patterns_linked_t = { -- sequence of valid interwiki and inter project prefixen
'^%[%[:([dsw]):(%l%l+):', -- wikilinked; project and language prefixes
'^%[%[:(%l%l+):([dsw]):', -- wikilinked; language and project prefixes
'^%[%[:([dsw]):', -- wikilinked; project prefix
'^%[%[:(%l%l+):', -- wikilinked; language prefix
}
local prefix_patterns_unlinked_t = { -- sequence of valid interwiki and inter project prefixen
'^:([dsw]):(%l%l+):', -- project and language prefixes
'^:(%l%l+):([dsw]):', -- language and project prefixes
'^:([dsw]):', -- project prefix
'^:(%l%l+):', -- language prefix
}
local cap1, cap2;
for _, pattern in ipairs ((is_link and prefix_patterns_linked_t) or prefix_patterns_unlinked_t) do
cap1, cap2 = value:match (pattern);
if cap1 then
break; -- found a match so stop looking
end
end
if cap1 and cap2 then -- when both then :project:language: or :language:project: (both forms allowed)
if 1 == #cap1 then -- length == 1 then :project:language:
if cfg.inter_wiki_map[cap2] then -- is language prefix in the interwiki map?
return cap1, cap2; -- return interwiki project and interwiki language
end
else -- here when :language:project:
if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map?
return cap2, cap1; -- return interwiki project and interwiki language
end
end
return nil; -- unknown interwiki language
elseif not (cap1 or cap2) then -- both are nil?
return nil; -- we got something that looks like a project prefix but isn't; return fail
elseif 1 == #cap1 then -- here when one capture
return cap1, nil; -- length is 1 so return project, nil language
else -- here when one capture and its length it more than 1
if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map?
return nil, cap1; -- return nil project, language
end
end
end
end


Line 1,175: Line 1,248:
one = utilities.make_wikilink (person.link, one); -- link author/editor
one = utilities.make_wikilink (person.link, one); -- link author/editor
end
end
if one then -- if <one> has a value (name, mdash replacement, or mask text replacement)
if one then -- if <one> has a value (name, mdash replacement, or mask text replacement)
local proj, tag = interwiki_prefixen_get (one, true); -- get the interwiki prefixen if present
if 'w' == proj and ('Wikipedia' == mw.site.namespaces.Project['name']) then
proj = nil; -- for stuff like :w:de:<article>, :w is unnecessary TODO: maint cat?
end
if proj then
proj = ({['d'] = 'Wikidata', ['s'] = 'Wikisource', ['w'] = 'Wikipedia'})[proj]; -- :w (wikipedia) for linking from a non-wikipedia project
if proj then
one = one .. utilities.wrap_style ('interproj', proj); -- add resized leading space, brackets, static text, language name
tag = nil; -- unset; don't do both project and language
end
end
if tag == cfg.this_wiki_code then
tag = nil; -- stuff like :en:<article> at en.wiki is pointless TODO: maint cat?
end
if tag then
local lang = cfg.lang_tag_remap[tag] or cfg.mw_languages_by_tag_t[tag];
if lang then -- error messaging done in extract_names() where we know parameter names
one = one .. utilities.wrap_style ('interwiki', lang); -- add resized leading space, brackets, static text, language name
end
end
table.insert (name_list, one); -- add it to the list of names
table.insert (name_list, one); -- add it to the list of names
table.insert (name_list, sep_one); -- add the proper name-list separator
table.insert (name_list, sep_one); -- add the proper name-list separator
Line 1,200: Line 1,296:
local result = table.concat (name_list); -- construct list
local result = table.concat (name_list); -- construct list
if etal and utilities.is_set (result) then -- etal may be set by |display-authors=etal but we might not have a last-first list
if etal and utilities.is_set (result) then -- etal may be set by |display-authors=etal but we might not have a last-first list
result = result .. sep .. ' ' .. cfg.messages['et al']; -- we've got a last-first list and etal so add et al.
result = result .. sep .. cfg.messages['et al']; -- we've got a last-first list and etal so add et al.
end
end
Line 1,293: Line 1,389:
--[[---------------------< N A M E _ I S _ N U M E R I C >----------------------
--[[---------------------< N A M E _ I S _ N U M E R I C >----------------------


Add maint cat when name parameter value does not contain letters.  Does not catch
Add an error message and category when <name> parameter value does not contain letters.   
mixed alphanumeric names so |last=A. Green (1922-1987) does not get caught in the
 
current version of this test but |first=(1888) is caught.
Add a maintenance category when <name> parameter value has numeric characters mixed with characters that are
not numeric characters; could be letters and/or punctuation characters.
 
This function will only emit one error and one maint message for the current template. Does not emit both error
and maint messages/categories for the same parameter value.


returns nothing
returns nothing
Line 1,301: Line 1,401:
]]
]]


local function name_is_numeric (name, list_name)
local function name_is_numeric (name, name_alias, list_name)
if utilities.is_set (name) then
local patterns = {
if mw.ustring.match (name, '^[%A]+$') then -- when name does not contain any letters
'^%D+%d', -- <name> must have digits preceded by other characters
utilities.set_message ('maint_numeric_names', cfg.special_case_translation [list_name]); -- add a maint cat for this template
'^%D*%d+%D+', -- <name> must have digits followed by other characters
}
 
if not added_numeric_name_errs and mw.ustring.match (name, '^[%A]+$') then -- if we have not already set an error message and <name> does not have any alpha characters
utilities.set_message ('err_numeric_names', name_alias); -- add an error message
added_numeric_name_errs = true; -- set the flag so we emit only one error message
return; -- when here no point in further testing; abandon
end
 
if not added_numeric_name_maint then -- if we have already set a maint message
for _, pattern in ipairs (patterns) do -- spin through list of patterns
if mw.ustring.match (name, pattern) then -- digits preceded or followed by anything but digits; %D+ includes punctuation
utilities.set_message ('maint_numeric_names', cfg.special_case_translation [list_name]); -- add a maint cat for this template
added_numeric_name_maint = true; -- set the flag so we emit only one maint message
return; -- when here no point in further testing; abandon
end
end
end
end
end
Line 1,316: Line 1,431:
semicolons. Escaped semicolons are ones used as part of selected HTML entities.
semicolons. Escaped semicolons are ones used as part of selected HTML entities.
If the condition is met, the function adds the multiple name maintenance category.
If the condition is met, the function adds the multiple name maintenance category.
Same test for first except that commas should not appear in given names (MOS:JR says
that the generational suffix does not take a separator character).  Titles, degrees,
postnominals, affiliations, all normally comma separated don't belong in a citation.
<name> – name parameter value
<list_name> – AuthorList, EditorList, etc
<limit> – number of allowed commas; 1 (default) for surnames; 0 for given names


returns nothing
returns nothing
Line 1,321: Line 1,444:
]]
]]


local function name_has_mult_names (name, list_name)
local function name_has_mult_names (name, list_name, limit)
local _, commas, semicolons, nbsps;
local _, commas, semicolons, nbsps;
limit = limit and limit or 1;
if utilities.is_set (name) then
if utilities.is_set (name) then
_, commas = name:gsub (',', ''); -- count the number of commas
_, commas = name:gsub (',', ''); -- count the number of commas
Line 1,336: Line 1,460:
-- from semicolons to 'escape' them. If additional entities are added,
-- from semicolons to 'escape' them. If additional entities are added,
-- they also can be subtracted.
-- they also can be subtracted.
if 1 < commas or 0 < (semicolons - nbsps) then
if limit < commas or 0 < (semicolons - nbsps) then
utilities.set_message ('maint_mult_names', cfg.special_case_translation [list_name]); -- add a maint message
utilities.set_message ('maint_mult_names', cfg.special_case_translation [list_name]); -- add a maint message
end
end
Line 1,343: Line 1,467:




--[[--------------------------< I S _ G E N E R I C >----------------------------------------------------------
--[=[-------------------------< I S _ G E N E R I C >----------------------------------------------------------
 
Compares values assigned to various parameters according to the string provided as <item> in the function call.
<item> can have on of two values:
'generic_names' – for name-holding parameters: |last=, |first=, |editor-last=, etc
'generic_titles' – for |title=


Compares values assigned to various parameter according to the string provided as <item> in the function call:
There are two types of generic tests.  The 'accept' tests look for a pattern that should not be rejected by the
'generic_names': |last=, |first=, |editor-last=, etc value against list of known generic name patterns
'reject' test.  For example,
'generic_titles': |title=
|author=[[John Smith (author)|Smith, John]]
Returns true when pattern matches; nil else
would be rejected by the 'author' reject test.  But piped wikilinks with 'author' disambiguation should not be
rejected so the 'accept' test prevents that from happening.  Accept tests are always performed before reject
tests.


The k/v pairs in cfg.special_case_translation[item] each contain two tables, one for English and one for another
Each of the 'accept' and 'reject' sequence tables hold tables for en.wiki (['en']) and local.wiki (['local'])
'local' language.Each of those tables contain another table that holds the string or pattern (whole or fragment)
that each can hold a test sequence table  The sequence table holds, at index [1], a test pattern, and, at index
in index [1]. index [2] is a Boolean that tells string.find() or mw.ustring.find() to do plain-text search (true)
[2], a boolean control value.  The control value tells string.find() or mw.ustring.find() to do plain-text search (true)
or a pattern search (false).  The intent of all this complexity is to make these searches as fast as possible so
or a pattern search (false).  The intent of all this complexity is to make these searches as fast as possible so
that we don't run out of processing time on very large articles.
that we don't run out of processing time on very large articles.


]]
Returns
true when a reject test finds the pattern or string
false when an accept test finds the pattern or string
nil else
 
]=]


local function is_generic (item, value)
local function is_generic (item, value, wiki)
local test_val;
local test_val;
local str_lower = { -- use string.lower() for en.wiki (['en']) and use mw.ustring.lower() or local.wiki (['local'])
['en'] = string.lower,
['local'] = mw.ustring.lower,
}
local str_find = { -- use string.find() for en.wiki (['en']) and use mw.ustring.find() or local.wiki (['local'])
['en'] = string.find,
['local'] = mw.ustring.find,
}


for _, generic_value in ipairs (cfg.special_case_translation[item]) do -- spin through the list of known generic value fragments
local function test (val, test_t, wiki) -- local function to do the testing; <wiki> selects lower() and find() functions
test_val = generic_value['en'][2] and value:lower() or value; -- when set to 'true', plaintext search using lowercase value
val = test_t[2] and str_lower[wiki](value) or val; -- when <test_t[2]> set to 'true', plaintext search using lowercase value
return str_find[wiki] (val, test_t[1], 1, test_t[2]); -- return nil when not found or matched
end
local test_types_t = {'accept', 'reject'}; -- test accept patterns first, then reject patterns
local wikis_t = {'en', 'local'}; -- do tests for each of these keys; en.wiki first, local.wiki second


if test_val:find (generic_value['en'][1], 1, generic_value['en'][2]) then
for _, test_type in ipairs (test_types_t) do -- for each test type
return true; -- found English generic value so done
for _, generic_value in pairs (cfg.special_case_translation[item][test_type]) do -- spin through the list of generic value fragments to accept or reject
 
for _, wiki in ipairs (wikis_t) do
elseif generic_value['local'] then -- to keep work load down, generic_<value>['local'] should be nil except when there is a local version of the generic value
if generic_value[wiki] then
test_val = generic_value['local'][2] and mw.ustring.lower(value) or value; -- when set to 'true', plaintext search using lowercase value
if test (value, generic_value[wiki], wiki) then -- go do the test
 
return ('reject' == test_type); -- param value rejected, return true; false else
if mw.ustring.find (test_val, generic_value['local'][1], 1, generic_value['local'][2]) then -- mw.ustring() because might not be Latin script
end
return true; -- found local generic value so done
end
end
end
end
end
Line 1,406: Line 1,555:


if not accept_name then -- <last> not wrapped in accept-as-written markup
if not accept_name then -- <last> not wrapped in accept-as-written markup
name_has_mult_names (last, list_name); -- check for multiple names in the parameter (last only)
name_has_mult_names (last, list_name); -- check for multiple names in the parameter
name_is_numeric (last, list_name); -- check for names that are composed of digits and punctuation
name_is_numeric (last, last_alias, list_name); -- check for names that have no letters or are a mix of digits and other characters
name_is_generic (last, last_alias); -- check for names found in the generic names list
name_is_generic (last, last_alias); -- check for names found in the generic names list
end
end
Line 1,416: Line 1,565:


if not accept_name then -- <first> not wrapped in accept-as-written markup
if not accept_name then -- <first> not wrapped in accept-as-written markup
name_is_numeric (first, list_name); -- check for names that are composed of digits and punctuation
name_has_mult_names (first, list_name, 0); -- check for multiple names in the parameter; 0 is number of allowed commas in a given name
name_is_numeric (first, first_alias, list_name); -- check for names that have no letters or are a mix of digits and other characters
name_is_generic (first, first_alias); -- check for names found in the generic names list
name_is_generic (first, first_alias); -- check for names found in the generic names list
end
end
Line 1,469: Line 1,619:
link, link_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i );
link, link_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i );
mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );
mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );
 
if last then -- error check |lastn= alias for unknown interwiki link prefix; done here because this is where we have the parameter name
local project, language = interwiki_prefixen_get (last, true); -- true because we expect interwiki links in |lastn= to be wikilinked
if nil == project and nil == language then -- when both are nil
utilities.set_message ('err_bad_paramlink', last_alias); -- not known, emit an error message -- TODO: err_bad_interwiki?
last = utilities.remove_wiki_link (last); -- remove wikilink markup; show display value only
end
end
if link then -- error check |linkn= alias for unknown interwiki link prefix
local project, language = interwiki_prefixen_get (link, false); -- false because wiki links in |author-linkn= is an error
if nil == project and nil == language then -- when both are nil
utilities.set_message ('err_bad_paramlink', link_alias); -- not known, emit an error message -- TODO: err_bad_interwiki?
link = nil; -- unset so we don't link
link_alias = nil;
end
end
last, etal = name_has_etal (last, etal, false, last_alias); -- find and remove variations on et al.
last, etal = name_has_etal (last, etal, false, last_alias); -- find and remove variations on et al.
first, etal = name_has_etal (first, etal, false, first_alias); -- find and remove variations on et al.
first, etal = name_has_etal (first, etal, false, first_alias); -- find and remove variations on et al.
Line 1,512: Line 1,679:


This function looks for:
This function looks for:
<lang_param> as a tag in cfg.lang_code_remap{}
<lang_param> as a tag in cfg.lang_tag_remap{}
<lang_param> as a name in cfg.lang_name_remap{}
<lang_param> as a name in cfg.lang_name_remap{}
Line 1,521: Line 1,688:
and look for the new <lang_param> in cfg.mw_languages_by_tag_t{}
and look for the new <lang_param> in cfg.mw_languages_by_tag_t{}


on success, return name and matching tag; on failure return nil
on success, returns name (in properly capitalized form) and matching tag (in lowercase); on failure returns nil


]]
]]
Line 1,530: Line 1,697:
local tag;
local tag;


name = cfg.lang_code_remap[lang_param_lc]; -- assume <lang_param_lc> is a tag; attempt to get remapped language name  
name = cfg.lang_tag_remap[lang_param_lc]; -- assume <lang_param_lc> is a tag; attempt to get remapped language name  
if name then -- when <name>, <lang_param> is a tag for a remapped language name
if name then -- when <name>, <lang_param> is a tag for a remapped language name
return name, lang_param; -- so return <name> from remap and <lang_param>
if cfg.lang_name_remap[name:lower()][2] ~= lang_param_lc then
utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added
return name, cfg.lang_name_remap[name:lower()][2]; -- so return name and tag from lang_name_remap[name]; special case to xlate sr-ec and sr-el to sr-cyrl and sr-latn
end
return name, lang_param_lc; -- so return <name> from remap and <lang_param_lc>
end
end


tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- still assuming that <lang_param_lc> is a tag; strip script, region, variant subtags
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- still assuming that <lang_param_lc> is a tag; strip script, region, variant subtags
name = cfg.lang_code_remap[tag]; -- attempt to get remapped language name with language subtag only
name = cfg.lang_tag_remap[tag]; -- attempt to get remapped language name with language subtag only
if name then -- when <name>, <tag> is a tag for a remapped language name
if name then -- when <name>, <tag> is a tag for a remapped language name
return name, tag; -- so return <name> from remap and <tag>
return name, tag; -- so return <name> from remap and <tag>
end
end


if cfg.lang_name_remap[lang_param_lc] then -- not a tag, assume <lang_param_lc> is a name; attempt to get remapped language tag  
if cfg.lang_name_remap[lang_param_lc] then -- not a remapped tag, assume <lang_param_lc> is a name; attempt to get remapped language tag  
return cfg.lang_name_remap[lang_param_lc][1], cfg.lang_name_remap[lang_param_lc][2]; -- for this <lang_param_lc>, return a (possibly) new name and appropriate tag
return cfg.lang_name_remap[lang_param_lc][1], cfg.lang_name_remap[lang_param_lc][2]; -- for this <lang_param_lc>, return a (possibly) new name and appropriate tag
end
end


name = cfg.mw_languages_by_tag_t[lang_param_lc]; -- assume that <lang_param_lc> is a tag; attempt to get its matching language name
if name then
return name, lang_param_lc; -- <lang_param_lc> is a tag so return it and <name>
end
tag = cfg.mw_languages_by_name_t[lang_param_lc]; -- assume that <lang_param_lc> is a language name; attempt to get its matching tag
tag = cfg.mw_languages_by_name_t[lang_param_lc]; -- assume that <lang_param_lc> is a language name; attempt to get its matching tag
Line 1,551: Line 1,728:
end
end


name = cfg.mw_languages_by_tag_t[lang_param_lc]; -- assume that <lang_param_lc> is a tag; attempt to get its matching language name
if name then
return name, lang_param; -- <lang_param_lc> is a tag so return <name> and the tag
end
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- is <lang_param_lc> an IETF-like tag that MediaWiki doesn't recognize? <tag> gets the language subtag; nil else
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- is <lang_param_lc> an IETF-like tag that MediaWiki doesn't recognize? <tag> gets the language subtag; nil else


Line 1,601: Line 1,772:


for _, lang in ipairs (names_t) do -- reuse lang here because we don't yet know if lang is a language name or a language tag
for _, lang in ipairs (names_t) do -- reuse lang here because we don't yet know if lang is a language name or a language tag
name, tag = name_tag_get (lang); -- attempt to get name/tag pair for <lang>
name, tag = name_tag_get (lang); -- attempt to get name/tag pair for <lang>; <name> has proper capitalization; <tag> is lowercase


if utilities.is_set (tag) then
if utilities.is_set (tag) then
lang_subtag = tag:lower():gsub ('^(%a%a%a?)%-.*', '%1'); -- for categorization, strip any IETF-like tags from language tag
lang_subtag = tag:gsub ('^(%a%a%a?)%-.*', '%1'); -- for categorization, strip any IETF-like tags from language tag


if cfg.this_wiki_code ~= lang_subtag then -- when the language is not the same as this wiki's language
if cfg.this_wiki_code ~= lang_subtag then -- when the language is not the same as this wiki's language
if 2 == lang_subtag:len() then -- and is a two-character tag
if 2 == lang_subtag:len() then -- and is a two-character tag
utilities.add_prop_cat ('foreign-lang-source', {name, lang_subtag}, lang_subtag); -- categorize it; tag appended to allow for multiple language categorization
utilities.add_prop_cat ('foreign-lang-source', {name, tag}, lang_subtag); -- categorize it; tag appended to allow for multiple language categorization
else -- or is a recognized language (but has a three-character tag)
else -- or is a recognized language (but has a three-character tag)
utilities.add_prop_cat ('foreign-lang-source-2', {lang_subtag}, lang_subtag); -- categorize it differently TODO: support multiple three-character tag categories per cs1|2 template?
utilities.add_prop_cat ('foreign-lang-source-2', {lang_subtag}, lang_subtag); -- categorize it differently TODO: support multiple three-character tag categories per cs1|2 template?
Line 1,656: Line 1,827:
return cfg.presentation['sep_' .. mode], postscript;
return cfg.presentation['sep_' .. mode], postscript;
end
end


--[[--------------------------< S E T _ S T Y L E >-----------------------------
--[[--------------------------< S E T _ S T Y L E >-----------------------------
Line 1,679: Line 1,851:
-- emit a maintenance message if user postscript is the default cs2 postscript
-- emit a maintenance message if user postscript is the default cs2 postscript
-- we catch the opposite case for cs1 in set_cs_style
-- we catch the opposite case for cs1 in set_cs_style
if 'cs2' == mode or 'citation' == cite_class then
if 'cs2' == mode or ('cs1' ~= mode and 'citation' == cite_class) then -- {{citation |title=Title |mode=cs1 |postscript=none}} should not emit maint message
utilities.set_message ('maint_postscript');
utilities.set_message ('maint_postscript');
end
end
Line 1,746: Line 1,918:


inputs:
inputs:
max: A['DisplayAuthors'] or A['DisplayEditors']; a number or some flavor of etal
max: A['DisplayAuthors'] or A['DisplayEditors'], etc; a number or some flavor of etal
count: #a or #e
count: #a or #e
list_name: 'authors' or 'editors'
list_name: 'authors' or 'editors'
etal: author_etal or editor_etal
etal: author_etal or editor_etal
This function sets an error message when |display-xxxxors= value greater than or equal to number of names but
not when <max> comes from {{cs1 config}} global settings.  When using global settings, <param> is set to the
keyword 'cs1 config' which is used to supress the normal error.  Error is suppressed because it is to be expected
that some citations in an article will have the same or fewer names that the limit specified in {{cs1 config}}.


]]
]]
Line 1,760: Line 1,937:
elseif max:match ('^%d+$') then -- if is a string of numbers
elseif max:match ('^%d+$') then -- if is a string of numbers
max = tonumber (max); -- make it a number
max = tonumber (max); -- make it a number
if max >= count then -- if |display-xxxxors= value greater than or equal to number of authors/editors
if (max >= count) and ('cs1 config' ~= param) then -- error when local |display-xxxxors= value greater than or equal to number of names; not an error when using global setting
utilities.set_message ('err_disp_name', {param, max}); -- add error message
utilities.set_message ('err_disp_name', {param, max}); -- add error message
max = nil;
max = nil;
end
end
else -- not a valid keyword or number
else -- not a valid keyword or number
utilities.set_message ('err_disp_name', {param, max}); -- add error message
utilities.set_message ('err_disp_name', {param, max}); -- add error message
max = nil; -- unset; as if |display-xxxxors= had not been set
max = nil; -- unset; as if |display-xxxxors= had not been set
end
end
Line 1,800: Line 1,977:
--[[--------------------------< E X T R A _ T E X T _ I N _ V O L _ I S S _ C H E C K >------------------------
--[[--------------------------< E X T R A _ T E X T _ I N _ V O L _ I S S _ C H E C K >------------------------


Adds error if |volume= or |issue= has what appears to be some form of redundant 'type' indicator.
Adds error if |volume= or |issue= has what appears to be some form of redundant 'type' indicator.  Applies to
both; this function looks for issue text in both |issue= and |volume= and looks for volume-like text in |voluem=
and |issue=.


For |volume=:
For |volume=:
Line 1,809: Line 1,988:
For |issue=:
For |issue=:
'No.', 'I.', 'Iss.' (with or without the dot) abbreviations, or 'Issue' in the first characters of the
'No.', 'I.', 'Iss.' (with or without the dot) abbreviations, or 'Issue' in the first characters of the
parameter content (all case insensitive).
parameter content (all case insensitive); numero styling: 'n°' with degree sign U+00B0, and № precomposed
numero sign U+2116.
Single character values ('v', 'i', 'n') allowed when not followed by separator character ('.', ':', '=', or
Single character values ('v', 'i', 'n') allowed when not followed by separator character ('.', ':', '=', or
Line 1,827: Line 2,007:
end
end
local patterns = 'v' == selector and cfg.vol_iss_pg_patterns.vpatterns or cfg.vol_iss_pg_patterns.ipatterns;
local handler = 'v' == selector and 'err_extra_text_volume' or 'err_extra_text_issue';
local handler = 'v' == selector and 'err_extra_text_volume' or 'err_extra_text_issue';
val = val:lower(); -- force parameter value to lower case
val = val:lower(); -- force parameter value to lower case
for _, pattern in ipairs (patterns) do -- spin through the selected sequence table of patterns
 
for _, pattern in ipairs (cfg.vol_iss_pg_patterns.vi_patterns_t) do -- spin through the sequence table of patterns
if val:match (pattern) then -- when a match, error so
if val:match (pattern) then -- when a match, error so
utilities.set_message (handler, name); -- add error message
utilities.set_message (handler, name); -- add error message
Line 2,063: Line 2,242:




--[[-------------------------< F O R M A T _ V O L U M E _ I S S U E >----------------------------------------
--[[-------------------------< 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
returns the concatenation of the formatted volume and issue (or journal article number) parameters as a single
or formatted issue, or an empty string if neither are set.
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)
local function format_volume_issue (volume, issue, article, cite_class, origin, sepc, lower)
if not utilities.is_set (volume) and not utilities.is_set (issue) then
if not utilities.is_set (volume) and not utilities.is_set (issue) and not utilities.is_set (article) then
return '';
return '';
end
end
Line 2,087: Line 2,266:
if is_journal then -- journal-style formatting
if is_journal then -- journal-style formatting
local vol = '';
local vol = '';
 
if utilities.is_set (volume) then
if utilities.is_set (volume) then
if is_numeric_vol then -- |volume= value all digits or all uppercase Roman numerals?
if is_numeric_vol then -- |volume= value all digits or all uppercase Roman numerals?
Line 2,097: Line 2,276:
end
end
end
end
if utilities.is_set (issue) then
vol = vol .. (utilities.is_set (issue) and utilities.substitute (cfg.messages['j-issue'], issue) or '')
return vol .. utilities.substitute (cfg.messages['j-issue'], issue);
vol = vol .. (utilities.is_set (article) and utilities.substitute (cfg.messages['j-article-num'], article) or '')
end
return vol;
return vol;
end
end
Line 2,105: Line 2,283:
if 'podcast' == cite_class and utilities.is_set (issue) then
if 'podcast' == cite_class and utilities.is_set (issue) then
return wrap_msg ('issue', {sepc, issue}, lower);
return wrap_msg ('issue', {sepc, issue}, lower);
end
if 'conference' == cite_class and utilities.is_set (article) then -- |article-number= supported only in journal and conference cites
if utilities.is_set (volume) and utilities.is_set (article) then -- both volume and article number
return wrap_msg ('vol-art', {sepc, utilities.hyphen_to_dash (volume), article}, lower);
elseif utilities.is_set (article) then -- article number alone; when volume alone, handled below
return wrap_msg ('art', {sepc, article}, lower);
end
end
end


Line 2,274: Line 2,460:
for timestamp errors when the timestamp has a wildcard, return the URL unmodified
for timestamp errors when the timestamp has a wildcard, return the URL unmodified
for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/)
for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/)
A secondary function is to return an archive-url timestamp from those urls that have them (archive.org and
archive.today).  The timestamp is used by validation.archive_date_check() to see if the value in |archive-date=
matches the timestamp in the archive url.


]=]
]=]
Line 2,281: Line 2,471:
local path, timestamp, flag; -- portions of the archive.org URL
local path, timestamp, flag; -- portions of the archive.org URL
timestamp = url:match ('//archive.today/(%d%d%d%d%d%d%d%d%d%d%d%d%d%d)/') or -- get timestamp from archive.today urls
url:match ('//archive.today/(%d%d%d%d%.%d%d%.%d%d%-%d%d%d%d%d%d)/'); -- this timestamp needs cleanup
if timestamp then -- if this was an archive.today url ...
return url, date, timestamp:gsub ('[%.%-]', ''); -- return ArchiveURL, ArchiveDate, and timestamp (dots and dashes removed) from |archive-url=, and done
end
-- here for archive.org urls
if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then -- also deprecated liveweb Wayback machine URL
if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then -- also deprecated liveweb Wayback machine URL
return url, date; -- not an archive.org archive, return ArchiveURL and ArchiveDate
return url, date; -- not an archive.org archive, return ArchiveURL and ArchiveDate
Line 2,310: Line 2,506:
err_msg = cfg.err_msg_supl.flag;
err_msg = cfg.err_msg_supl.flag;
else
else
return url, date; -- return ArchiveURL and ArchiveDate
return url, date, timestamp; -- return ArchiveURL, ArchiveDate, and timestamp from |archive-url=
end
end
end
end
Line 2,317: Line 2,513:


if is_preview_mode then
if is_preview_mode then
return url, date; -- preview mode so return ArchiveURL and ArchiveDate
return url, date, timestamp; -- preview mode so return ArchiveURL, ArchiveDate, and timestamp from |archive-url=
else
else
return '', ''; -- return empty strings for ArchiveURL and ArchiveDate
return '', ''; -- return empty strings for ArchiveURL and ArchiveDate
Line 2,360: Line 2,556:
return true;
return true;
end
end
end
end
--[[--------------------------< D I S P L A Y _ N A M E S _ S E L E C T >--------------------------------------
for any of the |display-authors=, |display-editors=, etc parameters, select either the local or global setting.
When both are present, look at <local_display_names> value.  When the value is some sort of 'et al.'string,
special handling is required.
When {{cs1 config}} has |display-<namelist>= AND this template has |display-<namelist>=etal AND:
the number of names specified by <number_of_names> is:
greater than the number specified in the global |display-<namelist>= parameter (<global_display_names>)
use global |display-<namelist>= parameter value
set overridden maint category
less than or equal to the number specified in the global |display-<namelist>=  parameter
use local |display-<namelist>= parameter value
The purpose of this function is to prevent categorizing a template that has fewer names than the global setting
to keep the etal annotation specified by <local_display_names>.
]]
local function display_names_select (global_display_names, local_display_names, param_name, number_of_names, test)
if global_display_names and utilities.is_set (local_display_names) then -- when both
if 'etal' == local_display_names:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings
number_of_names = tonumber (number_of_names); -- convert these to numbers for comparison
local global_display_names_num = tonumber (global_display_names); -- <global_display_names> not set when parameter value is not digits
if number_of_names > global_display_names_num then -- template has more names than global config allows to be displayed?
utilities.set_message ('maint_overridden_setting'); -- set a maint message because global is overriding local |display-<namelist>=etal
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
else
return local_display_names, param_name; -- return local because fewer names so let <local_display_names> control
end
end
-- here when <global_display_names> and <local_display_names> both numbers; <global_display_names> controls
utilities.set_message ('maint_overridden_setting'); -- set a maint message
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
end
-- here when only one of <global_display_names> or <local_display_names> set
if global_display_names then
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
else
return local_display_names, param_name; -- return local
end
end
end
end
Line 2,384: Line 2,625:
local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors=
local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors=
local Authors;
local Authors;
local NameListStyle = is_valid_parameter_value (A['NameListStyle'], A:ORIGIN('NameListStyle'), cfg.keywords_lists['name-list-style'], '');
local NameListStyle;
if cfg.global_cs1_config_t['NameListStyle'] then -- global setting in {{cs1 config}} overrides local |name-list-style= parameter value; nil when empty or assigned value invalid
NameListStyle = is_valid_parameter_value (cfg.global_cs1_config_t['NameListStyle'], 'cs1 config: name-list-style', cfg.keywords_lists['name-list-style'], ''); -- error messaging 'param' here is a hoax
else
NameListStyle = is_valid_parameter_value (A['NameListStyle'], A:ORIGIN('NameListStyle'), cfg.keywords_lists['name-list-style'], '');
end
 
if cfg.global_cs1_config_t['NameListStyle'] and utilities.is_set (A['NameListStyle']) then -- when template has |name-list-style=<something> which global setting has overridden
utilities.set_message ('maint_overridden_setting'); -- set a maint message
end
 
local Collaboration = A['Collaboration'];
local Collaboration = A['Collaboration'];


Line 2,393: Line 2,644:
elseif 2 == selected then
elseif 2 == selected then
NameListStyle = 'vanc'; -- override whatever |name-list-style= might be
NameListStyle = 'vanc'; -- override whatever |name-list-style= might be
a, author_etal = parse_vauthors_veditors (args, args.vauthors, 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn=
a, author_etal = parse_vauthors_veditors (args, A['Vauthors'], 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn=
elseif 3 == selected then
elseif 3 == selected then
Authors = A['Authors']; -- use content of |authors=
Authors = A['Authors']; -- use content of |people= or |credits=; |authors= is deprecated; TODO: constrain |people= and |credits= to cite av media, episode, serial?
if 'authors' == A:ORIGIN('Authors') then -- but add a maint cat if the parameter is |authors=
utilities.set_message ('maint_authors'); -- because use of this parameter is discouraged; what to do about the aliases is a TODO:
end
end
end
if utilities.is_set (Collaboration) then
if utilities.is_set (Collaboration) then
Line 2,449: Line 2,697:
local TitleLink = A['TitleLink'];
local TitleLink = A['TitleLink'];


local auto_select = ''; -- default is auto
local auto_select = ''; -- default is auto
local accept_link;
local accept_link;
TitleLink, accept_link = utilities.has_accept_as_written (TitleLink, true); -- test for accept-this-as-written markup
TitleLink, accept_link = utilities.has_accept_as_written (TitleLink, true); -- test for accept-this-as-written markup
Line 2,466: Line 2,714:


local Periodical = A['Periodical'];
local Periodical = A['Periodical'];
local Periodical_origin = '';
local Periodical_origin = A:ORIGIN('Periodical');
local ScriptPeriodical = A['ScriptPeriodical'];
local ScriptPeriodical_origin = A:ORIGIN('ScriptPeriodical');
local TransPeriodical =  A['TransPeriodical'];
local TransPeriodical_origin =  A:ORIGIN ('TransPeriodical');
if (utilities.in_array (config.CitationClass, {'book', 'encyclopaedia'}) and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical))) then
local param;
if utilities.is_set (Periodical) then -- get a parameter name from one of these periodical related meta-parameters
Periodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = Periodical_origin -- get parameter name for error messaging
elseif utilities.is_set (TransPeriodical) then
TransPeriodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = TransPeriodical_origin; -- get parameter name for error messaging
elseif utilities.is_set (ScriptPeriodical) then
ScriptPeriodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = ScriptPeriodical_origin; -- get parameter name for error messaging
end
 
if utilities.is_set (param) then -- if we found one
utilities.set_message ('err_periodical_ignored', {param}); -- emit an error message
end
end
 
if utilities.is_set (Periodical) then
if utilities.is_set (Periodical) then
Periodical_origin = A:ORIGIN('Periodical'); -- get the name of the periodical parameter
local i;
local i;
Periodical, i = utilities.strip_apostrophe_markup (Periodical); -- strip apostrophe markup so that metadata isn't contaminated  
Periodical, i = utilities.strip_apostrophe_markup (Periodical); -- strip apostrophe markup so that metadata isn't contaminated  
Line 2,478: Line 2,748:
if 'mailinglist' == config.CitationClass then -- special case for {{cite mailing list}}
if 'mailinglist' == config.CitationClass then -- special case for {{cite mailing list}}
if utilities.is_set (Periodical) and utilities.is_set (A ['MailingList']) then -- both set emit an error TODO: make a function for this and similar?
if utilities.is_set (Periodical) and utilities.is_set (A ['MailingList']) then -- both set emit an error TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', Periodical_origin) .. ' and ' .. utilities.wrap_style ('parameter', 'mailinglist')});
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', Periodical_origin) .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'mailinglist')});
end
end


Line 2,484: Line 2,754:
Periodical_origin = A:ORIGIN('MailingList');
Periodical_origin = A:ORIGIN('MailingList');
end
end
local ScriptPeriodical = A['ScriptPeriodical'];


-- web and news not tested for now because of  
-- web and news not tested for now because of  
Line 2,498: Line 2,766:
local Volume;
local Volume;
local ScriptPeriodical_origin = A:ORIGIN('ScriptPeriodical');
if 'citation' == config.CitationClass then
if 'citation' == config.CitationClass then
if utilities.is_set (Periodical) then
if utilities.is_set (Periodical) then
if not utilities.in_array (Periodical_origin, {'website', 'mailinglist'}) then -- {{citation}} does not render volume for these 'periodicals' --TODO: move 'array' to ~/Configuration
if not utilities.in_array (Periodical_origin, cfg.citation_no_volume_t) then -- {{citation}} does not render |volume= when these parameters are used
Volume = A['Volume']; -- but does for all other 'periodicals'
Volume = A['Volume']; -- but does for all other 'periodicals'
end
end
Line 2,518: Line 2,785:
local Issue;
local Issue;
if 'citation' == config.CitationClass then
if 'citation' == config.CitationClass then
if utilities.is_set (Periodical) and utilities.in_array (Periodical_origin, {'journal', 'magazine', 'newspaper', 'periodical', 'work'}) or -- {{citation}} renders issue for these 'periodicals'--TODO: move 'array' to ~/Configuration
if utilities.is_set (Periodical) and utilities.in_array (Periodical_origin, cfg.citation_issue_t) then -- {{citation}} may render |issue= when these parameters are used
utilities.is_set (ScriptPeriodical) and utilities.in_array (ScriptPeriodical_origin, {'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work'}) then -- and these 'script-periodicals'
Issue = utilities.hyphen_to_dash (A['Issue']);
Issue = utilities.hyphen_to_dash (A['Issue']);
end
end
elseif utilities.in_array (config.CitationClass, cfg.templates_using_issue) then -- conference & map books do not support issue; {{citation}} listed here because included in settings table
elseif utilities.in_array (config.CitationClass, cfg.templates_using_issue) then -- conference & map books do not support issue; {{citation}} listed here because included in settings table
Line 2,527: Line 2,793:
end
end
end
end
local ArticleNumber;
if utilities.in_array (config.CitationClass, {'journal', 'conference'}) or ('citation' == config.CitationClass and utilities.is_set (Periodical) and 'journal' == Periodical_origin) then
ArticleNumber = A['ArticleNumber'];
end
extra_text_in_vol_iss_check (Issue, A:ORIGIN ('Issue'), 'i');
extra_text_in_vol_iss_check (Issue, A:ORIGIN ('Issue'), 'i');


Line 2,532: Line 2,805:
local Pages;
local Pages;
local At;
local At;
if not utilities.in_array (config.CitationClass, cfg.templates_not_using_page) then
local QuotePage;
local QuotePages;
if not utilities.in_array (config.CitationClass, cfg.templates_not_using_page) then -- TODO: rewrite to emit ignored parameter error message?
Page = A['Page'];
Page = A['Page'];
Pages = utilities.hyphen_to_dash (A['Pages']);
Pages = utilities.hyphen_to_dash (A['Pages']);
At = A['At'];
At = A['At'];
QuotePage = A['QuotePage'];
QuotePages = utilities.hyphen_to_dash (A['QuotePages']);
end
end


Line 2,544: Line 2,821:
local PublisherName = A['PublisherName'];
local PublisherName = A['PublisherName'];
local PublisherName_origin = A:ORIGIN('PublisherName');
local PublisherName_origin = A:ORIGIN('PublisherName');
if utilities.is_set (PublisherName) then
if utilities.is_set (PublisherName) and (cfg.keywords_xlate['none'] ~= PublisherName) then
local i = 0;
local i = 0;
PublisherName, i = utilities.strip_apostrophe_markup (PublisherName); -- strip apostrophe markup so that metadata isn't contaminated; publisher is never italicized
PublisherName, i = utilities.strip_apostrophe_markup (PublisherName); -- strip apostrophe markup so that metadata isn't contaminated; publisher is never italicized
if i then -- non-zero when markup was stripped so emit an error message
if i and (0 < i) then -- non-zero when markup was stripped so emit an error message
utilities.set_message ('err_apostrophe_markup', {PublisherName_origin});
utilities.set_message ('err_apostrophe_markup', {PublisherName_origin});
end
end
end
if ('document' == config.CitationClass) and not utilities.is_set (PublisherName) then
utilities.set_message ('err_missing_publisher', {config.CitationClass, 'publisher'});
end
end


Line 2,556: Line 2,837:


if 'newsgroup' == config.CitationClass then
if 'newsgroup' == config.CitationClass then
if utilities.is_set (PublisherName) then -- general use parameter |publisher= not allowed in cite newsgroup
if utilities.is_set (PublisherName) and (cfg.keywords_xlate['none'] ~= PublisherName) then -- general use parameter |publisher= not allowed in cite newsgroup
utilities.set_message ('err_parameter_ignored', {PublisherName_origin});
utilities.set_message ('err_parameter_ignored', {PublisherName_origin});
end
end
Line 2,589: Line 2,870:
-- check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories
-- 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 utilities.is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page
if not utilities.is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page
if utilities.in_array (this_page.nsText, cfg.uncategorized_namespaces) then
if cfg.uncategorized_namespaces[this_page.namespace] then -- is this page's namespace id one of the uncategorized namespace ids?
no_tracking_cats = "true"; -- set no_tracking_cats
no_tracking_cats = "true"; -- set no_tracking_cats
end
end
Line 2,618: Line 2,899:


if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
 
local URL_origin = A:ORIGIN('URL'); -- get name of parameter that holds URL
local URL_origin = A:ORIGIN('URL'); -- get name of parameter that holds URL
local ChapterURL_origin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL
local ChapterURL_origin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL
Line 2,633: Line 2,914:
Parameter remapping for cite encyclopedia:
Parameter remapping for cite encyclopedia:
When the citation has these parameters:
When the citation has these parameters:
|encyclopedia= and |title= then map |title= to |article= and |encyclopedia= to |title=
|encyclopedia= and |title= then map |title= to |article= and |encyclopedia= to |title= for rendering
|encyclopedia= and |article= then map |encyclopedia= to |title=
|encyclopedia= and |article= then map |encyclopedia= to |title= for rendering


|trans-title= maps to |trans-chapter= when |title= is re-mapped
|trans-title= maps to |trans-chapter= when |title= is re-mapped
Line 2,644: Line 2,925:


local Encyclopedia = A['Encyclopedia']; -- used as a flag by this module and by ~/COinS
local Encyclopedia = A['Encyclopedia']; -- used as a flag by this module and by ~/COinS
local ScriptEncyclopedia = A['ScriptEncyclopedia'];
local TransEncyclopedia = A['TransEncyclopedia'];


if utilities.is_set (Encyclopedia) then -- emit error message when Encyclopedia set but template is other than {{cite encyclopedia}} or {{citation}}
if utilities.is_set (Encyclopedia) or utilities.is_set (ScriptEncyclopedia) then -- emit error message when Encyclopedia set but template is other than {{cite encyclopedia}} or {{citation}}
if 'encyclopaedia' ~= config.CitationClass and 'citation' ~= config.CitationClass then
if 'encyclopaedia' ~= config.CitationClass and 'citation' ~= config.CitationClass then
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('Encyclopedia')});
if utilities.is_set (Encyclopedia) then
Encyclopedia = nil; -- unset because not supported by this template
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('Encyclopedia')});
else
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('ScriptEncyclopedia')});
end
Encyclopedia = nil; -- unset these because not supported by this template
ScriptEncyclopedia = nil;
TransEncyclopedia = nil;
end
end
elseif utilities.is_set (TransEncyclopedia) then
utilities.set_message ('err_trans_missing_title', {'encyclopedia'});
end
end


if ('encyclopaedia' == config.CitationClass) or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then
if ('encyclopaedia' == config.CitationClass) or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then
if utilities.is_set (Periodical) and utilities.is_set (Encyclopedia) then -- when both set emit an error TODO: make a function for this and similar?
if utilities.is_set (Periodical) and utilities.is_set (Encyclopedia) then -- when both parameters set emit an error message; {{citation}} only; Periodical not allowed in {{cite encyclopedia}}
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', A:ORIGIN ('Encyclopedia')) .. ' and ' .. utilities.wrap_style ('parameter', Periodical_origin)});
utilities.set_message ('err_periodical_ignored', {Periodical_origin});
end
end


if utilities.is_set (Encyclopedia) then
if utilities.is_set (Encyclopedia) or utilities.is_set (ScriptEncyclopedia) then
Periodical = Encyclopedia; -- error or no, set Periodical to Encyclopedia; allow periodical without encyclopedia
Periodical = Encyclopedia; -- error or no, set Periodical to Encyclopedia for rendering; {{citation}} could (not legitimately) have both; use Encyclopedia
Periodical_origin = A:ORIGIN ('Encyclopedia');
Periodical_origin = A:ORIGIN ('Encyclopedia');
end
ScriptPeriodical = ScriptEncyclopedia;
ScriptPeriodical_origin = A:ORIGIN ('ScriptEncyclopedia');


if utilities.is_set (Periodical) then -- Periodical is set when |encyclopedia= is set
if utilities.is_set (Title) or utilities.is_set (ScriptTitle) then
if utilities.is_set (Title) or utilities.is_set (ScriptTitle) then
if not utilities.is_set (Chapter) then
if not utilities.is_set (Chapter) then
Chapter = Title; -- |encyclopedia= and |title= are set so map |title= to |article= and |encyclopedia= to |title=
Chapter = Title; -- |encyclopedia= and |title= are set so map |title= params to |article= params for rendering
ScriptChapter = ScriptTitle;
ScriptChapter = ScriptTitle;
ScriptChapter_origin = A:ORIGIN('ScriptTitle')
ScriptChapter_origin = A:ORIGIN('ScriptTitle')
Line 2,671: Line 2,962:
ChapterURL = URL;
ChapterURL = URL;
ChapterURL_origin = URL_origin;
ChapterURL_origin = URL_origin;
ChapterUrlAccess = UrlAccess;
ChapterUrlAccess = UrlAccess;
ChapterFormat = Format;


if not utilities.is_set (ChapterURL) and utilities.is_set (TitleLink) then
if not utilities.is_set (ChapterURL) and utilities.is_set (TitleLink) then
Chapter = utilities.make_wikilink (TitleLink, Chapter);
Chapter = utilities.make_wikilink (TitleLink, Chapter);
end
end
Title = Periodical;
Title = Periodical; -- now map |encyclopedia= params to |title= params for rendering
ChapterFormat = Format;
ScriptTitle = ScriptPeriodical or '';
TransTitle = TransEncyclopedia or '';
Periodical = ''; -- redundant so unset
Periodical = ''; -- redundant so unset
TransTitle = '';
ScriptPeriodical = '';
URL = '';
URL = '';
Format = '';
Format = '';
TitleLink = '';
TitleLink = '';
ScriptTitle = '';
end
end
elseif utilities.is_set (Chapter) or utilities.is_set (ScriptChapter) then -- |title= not set
elseif utilities.is_set (Chapter) or utilities.is_set (ScriptChapter) then -- |title= not set
Title = Periodical; -- |encyclopedia= set and |article= set so map |encyclopedia= to |title=
Title = Periodical; -- |encyclopedia= set and |article= set so map |encyclopedia= to |title= for rendering
ScriptTitle = ScriptPeriodical or '';
TransTitle = TransEncyclopedia or '';
Periodical = ''; -- redundant so unset
Periodical = ''; -- redundant so unset
ScriptPeriodical = '';
end
end
end
end
Line 2,700: Line 2,994:
ID = A['Number']; -- yes, use it
ID = A['Number']; -- yes, use it
else -- ID has a value so emit error message
else -- ID has a value so emit error message
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'id') .. ' and ' .. utilities.wrap_style ('parameter', 'number')});
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'id') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'number')});
end
end
end
end
Line 2,733: Line 3,027:
-- CS1/2 mode
-- CS1/2 mode
local Mode = is_valid_parameter_value (A['Mode'], A:ORIGIN('Mode'), cfg.keywords_lists['mode'], '');
local Mode;
if cfg.global_cs1_config_t['Mode'] then -- global setting in {{cs1 config}} overrides local |mode= parameter value; nil when empty or assigned value invalid
Mode = is_valid_parameter_value (cfg.global_cs1_config_t['Mode'], 'cs1 config: mode', cfg.keywords_lists['mode'], ''); -- error messaging 'param' here is a hoax
else
Mode = is_valid_parameter_value (A['Mode'], A:ORIGIN('Mode'), cfg.keywords_lists['mode'], '');
end
 
if cfg.global_cs1_config_t['Mode'] and utilities.is_set (A['Mode']) then -- when template has |mode=<something> which global setting has overridden
utilities.set_message ('maint_overridden_setting'); -- set a maint message
end
 
-- separator character and postscript
-- separator character and postscript
local sepc, PostScript = set_style (Mode:lower(), A['PostScript'], config.CitationClass);
local sepc, PostScript = set_style (Mode:lower(), A['PostScript'], config.CitationClass);
Line 2,746: Line 3,050:
if config.CitationClass == "map" then
if config.CitationClass == "map" then
if utilities.is_set (Chapter) then --TODO: make a function for this and similar?
if utilities.is_set (Chapter) then --TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'map') .. ' and ' .. utilities.wrap_style ('parameter', Chapter_origin)}); -- add error message
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'map') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', Chapter_origin)}); -- add error message
end
end
Chapter = A['Map'];
Chapter = A['Map'];
Line 2,789: Line 3,093:


if utilities.is_set (Season) and utilities.is_set (SeriesNumber) then -- these are mutually exclusive so if both are set TODO: make a function for this and similar?
if utilities.is_set (Season) and utilities.is_set (SeriesNumber) then -- these are mutually exclusive so if both are set TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'season') .. ' and ' .. utilities.wrap_style ('parameter', 'seriesno')}); -- add error message
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'season') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'seriesno')}); -- add error message
SeriesNumber = ''; -- unset; prefer |season= over |seriesno=
SeriesNumber = ''; -- unset; prefer |season= over |seriesno=
end
end
Line 2,806: Line 3,110:
ChapterUrlAccess = UrlAccess;
ChapterUrlAccess = UrlAccess;
ChapterURL_origin = URL_origin;
ChapterURL_origin = URL_origin;
ChapterFormat = Format;
 
Title = Series; -- promote series to title
Title = Series; -- promote series to title
TitleLink = SeriesLink;
TitleLink = SeriesLink;
Line 2,819: Line 3,124:
TransTitle = '';
TransTitle = '';
ScriptTitle = '';
ScriptTitle = '';
Format = '';
else -- now oddities that are cite serial
else -- now oddities that are cite serial
Line 2,834: Line 3,140:
local TitleType = A['TitleType'];
local TitleType = A['TitleType'];
local Degree = A['Degree'];
local Degree = A['Degree'];
if utilities.in_array (config.CitationClass, {'AV-media-notes', 'interview', 'mailinglist', 'map', 'podcast', 'pressrelease', 'report', 'speech', 'techreport', 'thesis'}) then
if utilities.in_array (config.CitationClass, {'AV-media-notes', 'document', 'interview', 'mailinglist', 'map', 'podcast', 'pressrelease', 'report', 'speech', 'techreport', 'thesis'}) then
TitleType = set_titletype (config.CitationClass, TitleType);
TitleType = set_titletype (config.CitationClass, TitleType);
if utilities.is_set (Degree) and "Thesis" == TitleType then -- special case for cite thesis
if utilities.is_set (Degree) and "Thesis" == TitleType then -- special case for cite thesis
Line 2,883: Line 3,189:
local ArchiveDate;
local ArchiveDate;
local ArchiveFormat = A['ArchiveFormat'];
local ArchiveFormat = A['ArchiveFormat'];
 
local archive_url_timestamp; -- timestamp from wayback machine url
ArchiveURL, ArchiveDate = archive_url_check (A['ArchiveURL'], A['ArchiveDate'])
ArchiveURL, ArchiveDate, archive_url_timestamp = archive_url_check (A['ArchiveURL'], A['ArchiveDate'])
ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url');
ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url');
ArchiveURL, ArchiveDate = is_unique_archive_url (ArchiveURL, URL, ChapterURL, A:ORIGIN('ArchiveURL'), ArchiveDate); -- add error message when URL or ChapterURL == ArchiveURL
ArchiveURL, ArchiveDate = is_unique_archive_url (ArchiveURL, URL, ChapterURL, A:ORIGIN('ArchiveURL'), ArchiveDate); -- add error message when URL or ChapterURL == ArchiveURL


local AccessDate = A['AccessDate'];
local AccessDate = A['AccessDate'];
local LayDate = A['LayDate'];
local COinS_date = {}; -- holds date info extracted from |date= for the COinS metadata by Module:Date verification
local COinS_date = {}; -- holds date info extracted from |date= for the COinS metadata by Module:Date verification
local DoiBroken = A['DoiBroken'];
local DoiBroken = A['DoiBroken'];
Line 2,905: Line 3,210:
['doi-broken-date'] = {val = DoiBroken, name = A:ORIGIN ('DoiBroken')},
['doi-broken-date'] = {val = DoiBroken, name = A:ORIGIN ('DoiBroken')},
['pmc-embargo-date'] = {val = Embargo, name = A:ORIGIN ('Embargo')},
['pmc-embargo-date'] = {val = Embargo, name = A:ORIGIN ('Embargo')},
['lay-date'] = {val = LayDate, name = A:ORIGIN ('LayDate')},
['publication-date'] = {val = PublicationDate, name = A:ORIGIN ('PublicationDate')},
['publication-date'] = {val = PublicationDate, name = A:ORIGIN ('PublicationDate')},
['year'] = {val = Year, name = A:ORIGIN ('Year')},
['year'] = {val = Year, name = A:ORIGIN ('Year')},
Line 2,912: Line 3,216:
local error_list = {};
local error_list = {};
anchor_year, Embargo = validation.dates(date_parameters_list, COinS_date, error_list);
anchor_year, Embargo = validation.dates(date_parameters_list, COinS_date, error_list);
-- start temporary Julian / Gregorian calendar uncertainty categorization
if COinS_date.inter_cal_cat then
utilities.add_prop_cat ('jul-greg-uncertainty');
end
-- end temporary Julian / Gregorian calendar uncertainty categorization


if utilities.is_set (Year) and utilities.is_set (Date) then -- both |date= and |year= not normally needed;  
if utilities.is_set (Year) and utilities.is_set (Date) then -- both |date= and |year= not normally needed;  
Line 2,946: Line 3,244:
Date = date_parameters_list['date'].val;
Date = date_parameters_list['date'].val;
DoiBroken = date_parameters_list['doi-broken-date'].val;
DoiBroken = date_parameters_list['doi-broken-date'].val;
LayDate = date_parameters_list['lay-date'].val;
PublicationDate = date_parameters_list['publication-date'].val;
PublicationDate = date_parameters_list['publication-date'].val;
end
if archive_url_timestamp and utilities.is_set (ArchiveDate) then
validation.archive_date_check (ArchiveDate, archive_url_timestamp, DF); -- does YYYYMMDD in archive_url_timestamp match date in ArchiveDate
end
end
else
else
Line 2,953: Line 3,254:
end
end
end -- end of do
end -- end of do
if utilities.in_array (config.CitationClass, {'book', 'encyclopaedia'}) or -- {{cite book}}, {{cite encyclopedia}}; TODO: {{cite conference}} and others?
('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) or -- {{citation}} as an encylopedia citation
('citation' == config.CitationClass and not utilities.is_set (Periodical)) then -- {{citation}} as a book citation
if utilities.is_set (PublicationPlace) then
if not utilities.is_set (PublisherName) then
local date = COinS_date.rftdate and tonumber (COinS_date.rftdate:match ('%d%d%d%d')); -- get year portion of COinS date (because in Arabic numerals); convert string to number
if date and (1850 <= date) then -- location has no publisher; if date is 1850 or later
utilities.set_message ('maint_location_no_publisher'); -- add maint cat
end
else -- PublisherName has a value
if cfg.keywords_xlate['none'] == PublisherName then -- if that value is 'none' (only for book and encyclopedia citations)
PublisherName = ''; -- unset
end
end
end
end


local ID_list = {}; -- sequence table of rendered identifiers
local ID_list = {}; -- sequence table of rendered identifiers
Line 2,966: Line 3,284:
ID_list, ID_list_coins = identifiers.identifier_lists_get (args, {DoiBroken = DoiBroken, ASINTLD = A['ASINTLD'], Embargo = Embargo, Class = Class}, ID_support);
ID_list, ID_list_coins = identifiers.identifier_lists_get (args, {DoiBroken = DoiBroken, ASINTLD = A['ASINTLD'], Embargo = Embargo, Class = Class}, ID_support);


-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite ssrn}}, before generation of COinS data.
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite medrxiv}}, {{cite ssrn}}, before generation of COinS data.
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list) then
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list_t) then -- |arxiv= or |eprint= required for cite arxiv; |biorxiv=, |citeseerx=, |medrxiv=, |ssrn= required for their templates
if not utilities.is_set (ID_list_coins[config.CitationClass:upper()]) then -- |arxiv= or |eprint= required for cite arxiv; |biorxiv= & |citeseerx= required for their templates
if not (args[cfg.id_handlers[config.CitationClass:upper()].parameters[1]] or -- can't use ID_list_coins k/v table here because invalid parameters omitted
utilities.set_message ('err_' .. config.CitationClass .. '_missing'); -- add error message
args[cfg.id_handlers[config.CitationClass:upper()].parameters[2]]) then -- which causes unexpected parameter missing error message
utilities.set_message ('err_' .. config.CitationClass .. '_missing'); -- add error message
end
end


Periodical = ({['arxiv'] = 'arXiv', ['biorxiv'] = 'bioRxiv', ['citeseerx'] = 'CiteSeerX', ['ssrn'] = 'Social Science Research Network'})[config.CitationClass];
Periodical = ({['arxiv'] = 'arXiv', ['biorxiv'] = 'bioRxiv', ['citeseerx'] = 'CiteSeerX', ['medrxiv'] = 'medRxiv', ['ssrn'] = 'Social Science Research Network'})[config.CitationClass];
end
end


Line 2,991: Line 3,310:
  end
  end


if utilities.is_set (URL) and utilities.is_set (AccessDate) then -- access date requires |url=; identifier-created URL is not |url=
if utilities.is_set (URL) then -- set when using an identifier-created URL
utilities.set_message ('err_accessdate_missing_url'); -- add an error message
if utilities.is_set (AccessDate) then -- |access-date= requires |url=; identifier-created URL is not |url=
AccessDate = ''; -- unset
utilities.set_message ('err_accessdate_missing_url'); -- add an error message
end
AccessDate = ''; -- unset
end
 
if utilities.is_set (ArchiveURL) then -- |archive-url= requires |url=; identifier-created URL is not |url=
utilities.set_message ('err_archive_missing_url'); -- add an error message
ArchiveURL = ''; -- unset
end
end
end
end


Line 3,029: Line 3,355:
end
end
local QuotePage = A['QuotePage'];
local QuotePages = utilities.hyphen_to_dash (A['QuotePages']);
-- this is the function call to COinS()
-- this is the function call to COinS()
local OCinSoutput = metadata.COinS({
local OCinSoutput = metadata.COinS({
Line 3,040: Line 3,363:
['Title'] = metadata.make_coins_title (coins_title, ScriptTitle), -- Title and ScriptTitle stripped of bold / italic / accept-as-written markup
['Title'] = metadata.make_coins_title (coins_title, ScriptTitle), -- Title and ScriptTitle stripped of bold / italic / accept-as-written markup
['PublicationPlace'] = PublicationPlace,
['PublicationPlace'] = PublicationPlace,
['Date'] = COinS_date.rftdate, -- COinS_date has correctly formatted date if Date is valid;
['Date'] = COinS_date.rftdate, -- COinS_date.* has correctly formatted date values if Date is valid;
['Season'] = COinS_date.rftssn,
['Season'] = COinS_date.rftssn,
['Quarter'] = COinS_date.rftquarter,
['Quarter'] = COinS_date.rftquarter,
['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?
['Chron'] =  COinS_date.rftchron,
['Series'] = Series,
['Series'] = Series,
['Volume'] = Volume,
['Volume'] = Volume,
['Issue'] = Issue,
['Issue'] = Issue,
['ArticleNumber'] = ArticleNumber,
['Pages'] = coins_pages or metadata.get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At, QuotePage, QuotePages}, 7)), -- pages stripped of external links
['Pages'] = coins_pages or metadata.get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At, QuotePage, QuotePages}, 7)), -- pages stripped of external links
['Edition'] = Edition,
['Edition'] = Edition,
Line 3,056: Line 3,380:
}, config.CitationClass);
}, config.CitationClass);


-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, and {{cite ssrn}} AFTER generation of COinS data.
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite medrxiv}}, and {{cite ssrn}} AFTER generation of COinS data.
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, CiteSeerX, or ssrn now unset so it isn't displayed
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list_t) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, CiteSeerX, medRxiv, or ssrn now unset so it isn't displayed
Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal
Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal
end
end
Line 3,085: Line 3,409:
local last_first_list;
local last_first_list;
local control = {  
local control = {  
format = NameListStyle, -- empty string or 'vanc'
format = NameListStyle, -- empty string, '&', 'amp', 'and', or 'vanc'
maximum = nil, -- as if display-authors or display-editors not set
maximum = nil, -- as if display-authors or display-editors not set
mode = Mode
mode = Mode
Line 3,091: Line 3,415:


do -- do editor name list first because the now unsupported coauthors used to modify control table
do -- do editor name list first because the now unsupported coauthors used to modify control table
control.maximum , editor_etal = get_display_names (A['DisplayEditors'], #e, 'editors', editor_etal, A:ORIGIN ('DisplayEditors'));
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayEditors'], A['DisplayEditors'], A:ORIGIN ('DisplayEditors'), #e);
control.maximum, editor_etal = get_display_names (display_names, #e, 'editors', editor_etal, param);
 
Editors, EditorCount = list_people (control, e, editor_etal);
Editors, EditorCount = list_people (control, e, editor_etal);


Line 3,099: Line 3,425:
end
end
do -- now do interviewers
do -- now do interviewers
control.maximum, interviewer_etal = get_display_names (A['DisplayInterviewers'], #interviewers_list, 'interviewers', interviewer_etal, A:ORIGIN ('DisplayInterviewers'));
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayInterviewers'], A['DisplayInterviewers'], A:ORIGIN ('DisplayInterviewers'), #interviewers_list);
control.maximum, interviewer_etal = get_display_names (display_names, #interviewers_list, 'interviewers', interviewer_etal, param);
 
Interviewers = list_people (control, interviewers_list, interviewer_etal);
Interviewers = list_people (control, interviewers_list, interviewer_etal);
end
end
do -- now do translators
do -- now do translators
control.maximum, translator_etal = get_display_names (A['DisplayTranslators'], #t, 'translators', translator_etal, A:ORIGIN ('DisplayTranslators'));
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayTranslators'], A['DisplayTranslators'], A:ORIGIN ('DisplayTranslators'), #t);
control.maximum, translator_etal = get_display_names (display_names, #t, 'translators', translator_etal, param);
 
Translators = list_people (control, t, translator_etal);
Translators = list_people (control, t, translator_etal);
end
end
do -- now do contributors
do -- now do contributors
control.maximum, contributor_etal = get_display_names (A['DisplayContributors'], #c, 'contributors', contributor_etal, A:ORIGIN ('DisplayContributors'));
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayContributors'], A['DisplayContributors'], A:ORIGIN ('DisplayContributors'), #c);
control.maximum, contributor_etal = get_display_names (display_names, #c, 'contributors', contributor_etal, param);
 
Contributors = list_people (control, c, contributor_etal);
Contributors = list_people (control, c, contributor_etal);
end
end
do -- now do authors
do -- now do authors
control.maximum, author_etal = get_display_names (A['DisplayAuthors'], #a, 'authors', author_etal, A:ORIGIN ('DisplayAuthors'));
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayAuthors'], A['DisplayAuthors'], A:ORIGIN ('DisplayAuthors'), #a, author_etal);
control.maximum, author_etal = get_display_names (display_names, #a, 'authors', author_etal, param);


last_first_list = list_people (control, a, author_etal);
last_first_list = list_people (control, a, author_etal);
Line 3,137: Line 3,470:


-- special case for chapter format so no error message or cat when chapter not supported
-- special case for chapter format so no error message or cat when chapter not supported
if not (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or
if not (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia))) then
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia))) then
ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url');
ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url');
Line 3,190: Line 3,523:
end
end


if utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or -- if any of the 'periodical' cites except encyclopedia
if utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or -- if any of the 'periodical' cites except encyclopedia
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) then
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) then
local chap_param;
local chap_param;
Line 3,263: Line 3,596:
end
end


if (not plain_title) and (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or
if (not plain_title) and (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'document', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) or
('map' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)))) then -- special case for cite map when the map is in a periodical treat as an article
('map' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)))) then -- special case for cite map when the map is in a periodical treat as an article
Line 3,344: Line 3,677:
if utilities.is_set (Minutes) then
if utilities.is_set (Minutes) then
if utilities.is_set (Time) then --TODO: make a function for this and similar?
if utilities.is_set (Time) then --TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'minutes') .. ' and ' .. utilities.wrap_style ('parameter', 'time')});
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'minutes') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'time')});
end
end
Position = " " .. Minutes .. " " .. cfg.messages['minutes'];
Position = " " .. Minutes .. " " .. cfg.messages['minutes'];
Line 3,414: Line 3,747:


Series = utilities.is_set (Series) and wrap_msg ('series', {sepc, Series}) or ""; -- not the same as SeriesNum
Series = utilities.is_set (Series) and wrap_msg ('series', {sepc, Series}) or ""; -- not the same as SeriesNum
local Agency = A['Agency'];
local Agency = A['Agency'] or ''; -- |agency= only supported in {{cite news}}, {{cite press release}}, {{cite web}} and certain {{citation}} templates
Agency = utilities.is_set (Agency) and wrap_msg ('agency', {sepc, Agency}) or "";
if utilities.is_set (Agency) then -- this testing done here because {{citation}} supports 'news' citations
Volume = format_volume_issue (Volume, Issue, config.CitationClass, Periodical_origin, sepc, use_lowercase);
if utilities.in_array (config.CitationClass, {'news', 'pressrelease', 'web'}) or ('citation' == config.CitationClass and utilities.in_array (Periodical_origin, {"newspaper", "work"})) then
Agency = wrap_msg ('agency', {sepc, Agency}); -- format for rendering
else
Agency = ''; -- unset; not supported
utilities.set_message ('err_parameter_ignored', {'agency'}); -- add error message
end
end
Volume = format_volume_issue (Volume, Issue, ArticleNumber, config.CitationClass, Periodical_origin, sepc, use_lowercase);


if utilities.is_set (AccessDate) then
if utilities.is_set (AccessDate) then
Line 3,452: Line 3,793:
end
end
end
end
 
Quote = kern_quotes (Quote); -- kern if needed
Quote = utilities.wrap_style ('quoted-text', Quote ); -- wrap in <q>...</q> tags
Quote = utilities.wrap_style ('quoted-text', Quote ); -- wrap in <q>...</q> tags
Line 3,505: Line 3,847:
local Archived;
local Archived;
if utilities.is_set (ArchiveURL) then
if utilities.is_set (ArchiveURL) then
local arch_text;
if not utilities.is_set (ArchiveDate) then -- ArchiveURL set but ArchiveDate not set
if not utilities.is_set (ArchiveDate) then
utilities.set_message ('err_archive_missing_date'); -- emit an error message
utilities.set_message ('err_archive_missing_date');
ArchiveURL = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
end
else
if utilities.is_set (ArchiveDate) then -- ArchiveURL not set but ArchiveDate is set
utilities.set_message ('err_archive_date_missing_url'); -- emit an error message
ArchiveURL = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
end
end
end
if utilities.is_set (ArchiveURL) then
local arch_text;
if "live" == UrlStatus then
if "live" == UrlStatus then
arch_text = cfg.messages['archived'];
arch_text = cfg.messages['archived'];
Line 3,544: Line 3,896:
end
end
else -- OriginalUrl not set
else -- OriginalUrl not set
arch_text = cfg.messages['archived-missing'];
if sepc ~= "." then arch_text = arch_text:lower() end
utilities.set_message ('err_archive_missing_url');
utilities.set_message ('err_archive_missing_url');
Archived = ''; -- empty string for concatenation
Archived = ''; -- empty string for concatenation
Line 3,555: Line 3,905:
end
end
local Lay = '';
local LaySource = A['LaySource'];
local LayURL = A['LayURL'];
local LayFormat = A['LayFormat'];
LayFormat = style_format (LayFormat, LayURL, 'lay-format', 'lay-url');
if utilities.is_set (LayURL) then
if utilities.is_set (LayDate) then LayDate = " (" .. LayDate .. ")" end
if utilities.is_set (LaySource) then
LaySource = " &ndash; ''" .. utilities.safe_for_italics (LaySource) .. "''";
else
LaySource = "";
end
if sepc == '.' then
Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary'], A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate
else
Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower(), A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate
end
elseif utilities.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
local TranscriptURL = A['TranscriptURL']
local TranscriptURL = A['TranscriptURL']
local TranscriptFormat = A['TranscriptFormat'];
local TranscriptFormat = A['TranscriptFormat'];
Line 3,606: Line 3,935:
end
end
local TransPeriodical =  A['TransPeriodical'];
local TransPeriodical_origin =  A:ORIGIN ('TransPeriodical');
-- Several of the above rely upon detecting this as nil, so do it last.
-- Several of the above rely upon detecting this as nil, so do it last.
if (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical)) then
if (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical)) then
Line 3,649: Line 3,976:
local tcommon2; -- used for book cite when |contributor= is set
local tcommon2; -- used for book cite when |contributor= is set
if utilities.in_array (config.CitationClass, {"journal", "citation"}) and utilities.is_set (Periodical) then
if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (Periodical) then -- special cases for book cites
if utilities.is_set (Others) then Others = safe_join ({Others, sepc .. " "}, sepc) end -- add terminal punctuation & space; check for dup sepc; TODO why do we need to do this here?
tcommon = safe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Edition, Publisher, Agency, Volume}, sepc );
elseif utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (Periodical) then -- special cases for book cites
if utilities.is_set (Contributors) then -- when we are citing foreword, preface, introduction, etc.
if utilities.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
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 );
tcommon2 = safe_join ({TitleType, Series, Language, Volume, Others, Edition, Publisher}, sepc);
else
else
tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc );
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Language, Volume, Others, Edition, Publisher}, sepc);
end
end


elseif 'map' == config.CitationClass then -- special cases for cite map
elseif 'map' == config.CitationClass then -- special cases for cite map
if utilities.is_set (Chapter) then -- map in a book; TitleType is part of Chapter
if utilities.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 );
tcommon = safe_join ({Title, Edition, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc);
elseif utilities.is_set (Periodical) then -- map in a periodical
elseif utilities.is_set (Periodical) then -- map in a periodical
tcommon = safe_join( {Title, TitleType, Format, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc );
tcommon = safe_join ({Title, TitleType, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc);
else -- a sheet or stand-alone map
else -- a sheet or stand-alone map
tcommon = safe_join( {Title, TitleType, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher}, sepc );
tcommon = safe_join ({Title, TitleType, Edition, Scale, Series, Language, Cartography, Others, Publisher}, sepc);
end
end
elseif 'episode' == config.CitationClass then -- special case for cite episode
elseif 'episode' == config.CitationClass then -- special case for cite episode
tcommon = safe_join( {Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc );
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc);


else -- all other CS1 templates
else -- all other CS1 templates
tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language,  
tcommon = safe_join ({Title, TitleNote, Conference, Periodical, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc);
Volume, Others, Edition, Publisher, Agency}, sepc );
end
end
Line 3,687: Line 4,010:
local idcommon;
local idcommon;
if 'audio-visual' == config.CitationClass or 'episode' == config.CitationClass then -- special case for cite AV media & cite episode position transcript
if 'audio-visual' == config.CitationClass or 'episode' == config.CitationClass then -- special case for cite AV media & cite episode position transcript
idcommon = safe_join( { ID_list, URL, Archived, Transcript, AccessDate, Via, Lay, Quote }, sepc );
idcommon = safe_join( { ID_list, URL, Archived, Transcript, AccessDate, Via, Quote }, sepc );
else
else
idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, Lay, Quote }, sepc );
idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, Quote }, sepc );
end
end
Line 3,713: Line 4,036:
end
end
if utilities.is_set (Editors) then
if utilities.is_set (Editors) then
local in_text = " ";
local in_text = '';
local post_text = "";
local post_text = '';
if utilities.is_set (Chapter) and 0 == #c then
if utilities.is_set (Chapter) and 0 == #c then
in_text = in_text .. cfg.messages['in'] .. " "
in_text = cfg.messages['in'] .. ' ';
if (sepc ~= '.') then
if (sepc ~= '.') then
in_text = in_text:lower() -- lowercase for cs2
in_text = in_text:lower(); -- lowercase for cs2
end
end
end
end
if EditorCount <= 1 then
if EditorCount <= 1 then
post_text = " (" .. cfg.messages['editor'] .. ")"; -- be consistent with no-author, no-date case
post_text = ' (' .. cfg.messages['editor'] .. ')'; -- be consistent with no-author, no-date case
else
else
post_text = " (" .. cfg.messages['editors'] .. ")";
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
Editors = terminate_name_list (in_text .. Editors .. post_text, sepc); -- terminate with 0 or 1 sepc and a space
Line 3,745: Line 4,068:
if utilities.is_set (Date) then
if utilities.is_set (Date) then
if EditorCount <= 1 then
if EditorCount <= 1 then
Editors = Editors .. ", " .. cfg.messages['editor'];
Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editor'];
else
else
Editors = Editors .. ", " .. cfg.messages['editors'];
Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editors'];
end
end
else
else
Line 3,764: Line 4,087:
end
end
end
end
 
if utilities.is_set (PostScript) and PostScript ~= sepc then
if utilities.is_set (PostScript) and PostScript ~= sepc then
text = safe_join( {text, sepc}, sepc ); -- Deals with italics, spaces, etc.
text = safe_join( {text, sepc}, sepc ); -- Deals with italics, spaces, etc.
text = text:sub(1, -sepc:len() - 1);
if '.' == sepc then -- remove final seperator if present
text = text:gsub ('%' .. sepc .. '$', ''); -- dot must be escaped here
else
text = mw.ustring.gsub (text, sepc .. '$', ''); -- using ustring for non-dot sepc (likely a non-Latin character)
end
end
end
Line 3,822: Line 4,149:


local template_name = ('citation' == config.CitationClass) and 'citation' or 'cite ' .. (cfg.citation_class_map_t[config.CitationClass] or config.CitationClass);
local template_name = ('citation' == config.CitationClass) and 'citation' or 'cite ' .. (cfg.citation_class_map_t[config.CitationClass] or config.CitationClass);
local template_link = '[[Template:' .. template_name .. '|' .. template_name .. ']]'; -- TODO: if kept, these require some sort of i18n
local template_link = '[[Template:' .. template_name .. '|' .. template_name .. ']]';
local msg_prefix = '<code class="cs1-code">{{' .. template_link .. '}}</code>: ';
local msg_prefix = '<code class="cs1-code">{{' .. template_link .. '}}</code>: ';


Line 3,863: Line 4,190:


if not no_tracking_cats then
if not no_tracking_cats then
local sort_key;
local cat_wikilink = 'cat wikilink';
if cfg.enable_sort_keys then -- when namespace sort keys enabled
local namespace_number = mw.title.getCurrentTitle().namespace; -- get namespace number for this wikitext
sort_key = (0 ~= namespace_number and (cfg.name_space_sort_keys[namespace_number] or cfg.name_space_sort_keys.other)) or nil; -- get sort key character; nil for mainspace
cat_wikilink = (not sort_key and 'cat wikilink') or 'cat wikilink sk'; -- make <cfg.messages> key
end
for _, v in ipairs (z.error_cats_t) do -- append error categories
for _, v in ipairs (z.error_cats_t) do -- append error categories
table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v));
table.insert (render_t, utilities.substitute (cfg.messages[cat_wikilink], {v, sort_key}));
end
end
for _, v in ipairs (z.maint_cats_t) do -- append maintenance categories
for _, v in ipairs (z.maint_cats_t) do -- append maintenance categories
table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v));
table.insert (render_t, utilities.substitute (cfg.messages[cat_wikilink], {v, sort_key}));
end
end
for _, v in ipairs (z.prop_cats_t) do -- append properties categories
for _, v in ipairs (z.prop_cats_t) do -- append properties categories
table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v));
table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v)); -- no sort keys
end
end
end
end
Line 3,891: Line 4,227:
local function validate (name, cite_class, empty)
local function validate (name, cite_class, empty)
local name = tostring (name);
local name = tostring (name);
local enum_name; -- for enumerated parameters, is name with enumerator replaced with '#'
local enum_name; -- parameter name with enumerator (if any) replaced with '#'
local state;
local state;
local function state_test (state, name) -- local function to do testing of state values
local function state_test (state, name) -- local function to do testing of state values
Line 3,911: Line 4,247:
return nil;
return nil;
end
end
-- replace enumerator digit(s) with # (|last25= becomes |last#=) (mw.ustring because non-Western 'local' digits)
enum_name = mw.ustring.gsub (name, '%d+$', '#'); -- where enumerator is last charaters in parameter name (these to protect |s2cid=)
enum_name = mw.ustring.gsub (enum_name, '%d+([%-l])', '#%1'); -- where enumerator is in the middle of the parameter name; |author#link= is the oddity


if utilities.in_array (cite_class, whitelist.preprint_template_list ) then -- limited parameter sets allowed for these templates
if 'document' == cite_class then -- special case for {{cite document}}
state = whitelist.limited_basic_arguments[name];
state = whitelist.document_parameters_t[enum_name]; -- this list holds enumerated and nonenumerated parameters
if true == state_test (state, name) then return true; end
if true == state_test (state, name) then return true; end
return false;
end


state = whitelist.preprint_arguments[cite_class][name]; -- look in the parameter-list for the template identified by cite_class
if utilities.in_array (cite_class, whitelist.preprint_template_list_t) then -- limited parameter sets allowed for these templates
state = whitelist.limited_parameters_t[enum_name]; -- this list holds enumerated and nonenumerated parameters
if true == state_test (state, name) then return true; end
if true == state_test (state, name) then return true; end


-- limited enumerated parameters list
state = whitelist.preprint_arguments_t[cite_class][name]; -- look in the parameter-list for the template identified by cite_class
enum_name = name:gsub("%d+", "#" ); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits)
state = whitelist.limited_numbered_arguments[enum_name];
if true == state_test (state, name) then return true; end
if true == state_test (state, name) then return true; end


Line 3,927: Line 4,268:
end -- end limited parameter-set templates
end -- end limited parameter-set templates


if utilities.in_array (cite_class, whitelist.unique_param_template_list) then -- experiment for template-specific parameters for templates that accept parameters from the basic argument list
if utilities.in_array (cite_class, whitelist.unique_param_template_list_t) then -- template-specific parameters for templates that accept parameters from the basic argument list
state = whitelist.unique_arguments[cite_class][name]; -- look in the template-specific parameter-lists for the template identified by cite_class
state = whitelist.unique_arguments_t[cite_class][name]; -- look in the template-specific parameter-lists for the template identified by cite_class
if true == state_test (state, name) then return true; end
if true == state_test (state, name) then return true; end
end -- if here, fall into general validation
end -- if here, fall into general validation
state = whitelist.basic_arguments[name]; -- all other templates; all normal parameters allowed
if true == state_test (state, name) then return true; end


-- all enumerated parameters allowed
state = whitelist.common_parameters_t[enum_name]; -- all other templates; all normal parameters allowed; this list holds enumerated and nonenumerated parameters
enum_name = name:gsub("%d+", "#" ); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits)
state = whitelist.numbered_arguments[enum_name];
if true == state_test (state, name) then return true; end
if true == state_test (state, name) then return true; end


Line 4,042: Line 4,378:
local function citation(frame)
local function citation(frame)
Frame = frame; -- save a copy in case we need to display an error message in preview mode
Frame = frame; -- save a copy in case we need to display an error message in preview mode
is_sandbox = nil ~= string.find (frame:getTitle(), 'sandbox', 1, true);
 
local config = {}; -- table to store parameters from the module {{#invoke:}}
for k, v in pairs( frame.args ) do -- get parameters from the {{#invoke}} frame
config[k] = v;
-- args[k] = v; -- crude debug support that allows us to render a citation from module {{#invoke:}}; skips parameter validation; TODO: keep?
end
-- i18n: set the name that your wiki uses to identify sandbox subpages from sandbox template invoke (or can be set here)
local sandbox = ((config.SandboxPath and '' ~= config.SandboxPath) and config.SandboxPath) or '/sandbox'; -- sandbox path from {{#invoke:Citation/CS1/sandbox|citation|SandboxPath=/...}}
is_sandbox = nil ~= string.find (frame:getTitle(), sandbox, 1, true); -- is this invoke the sandbox module?
sandbox = is_sandbox and sandbox or ''; -- use i18n sandbox to load sandbox modules when this module is the sandox; live modules else
 
local pframe = frame:getParent()
local pframe = frame:getParent()
local styles;
local styles;
if is_sandbox then -- did the {{#invoke:}} use sandbox version?
cfg = mw.loadData ('Module:Citation/CS1/Configuration' .. sandbox); -- load sandbox versions of support modules when {{#invoke:Citation/CS1/sandbox|...}}; live modules else
cfg = mw.loadData ('Module:Citation/CS1/Configuration/sandbox'); -- load sandbox versions of support modules
whitelist = mw.loadData ('Module:Citation/CS1/Whitelist' .. sandbox);
whitelist = mw.loadData ('Module:Citation/CS1/Whitelist/sandbox');
utilities = require ('Module:Citation/CS1/Utilities' .. sandbox);
utilities = require ('Module:Citation/CS1/Utilities/sandbox');
validation = require ('Module:Citation/CS1/Date_validation' .. sandbox);
validation = require ('Module:Citation/CS1/Date_validation/sandbox');
identifiers = require ('Module:Citation/CS1/Identifiers' .. sandbox);
identifiers = require ('Module:Citation/CS1/Identifiers/sandbox');
metadata = require ('Module:Citation/CS1/COinS' .. sandbox);
metadata = require ('Module:Citation/CS1/COinS/sandbox');
styles = 'Module:Citation/CS1' .. sandbox .. '/styles.css';
styles = 'Module:Citation/CS1/sandbox/styles.css';
else -- otherwise
cfg = mw.loadData ('Module:Citation/CS1/Configuration'); -- load live versions of support modules
whitelist = mw.loadData ('Module:Citation/CS1/Whitelist');
utilities = require ('Module:Citation/CS1/Utilities');
validation = require ('Module:Citation/CS1/Date_validation');
identifiers = require ('Module:Citation/CS1/Identifiers');
metadata = require ('Module:Citation/CS1/COinS');
styles = 'Module:Citation/CS1/styles.css';
end


utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the selected cfg tables
utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the selected cfg tables
Line 4,077: Line 4,412:
local suggestions = {}; -- table where we store suggestions if we need to loadData them
local suggestions = {}; -- table where we store suggestions if we need to loadData them
local error_text; -- used as a flag
local error_text; -- used as a flag
local config = {}; -- table to store parameters from the module {{#invoke:}}
for k, v in pairs( frame.args ) do -- get parameters from the {{#invoke}} frame
config[k] = v;
-- args[k] = v; -- crude debug support that allows us to render a citation from module {{#invoke:}}; skips parameter validation; TODO: keep?
end


local capture; -- the single supported capture when matching unknown parameters using patterns
local capture; -- the single supported capture when matching unknown parameters using patterns
Line 4,101: Line 4,430:
else
else
if nil == suggestions.suggestions then -- if this table is nil then we need to load it
if nil == suggestions.suggestions then -- if this table is nil then we need to load it
if is_sandbox then -- did the {{#invoke:}} use sandbox version?
suggestions = mw.loadData ('Module:Citation/CS1/Suggestions' .. sandbox); --load sandbox version of suggestion module when {{#invoke:Citation/CS1/sandbox|...}}; live module else
suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions/sandbox' ); -- use the sandbox version
else
suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' ); -- use the live version
end
end
end
for pattern, param in pairs (suggestions.patterns) do -- loop through the patterns to see if we can suggest a proper parameter
for pattern, param in pairs (suggestions.patterns) do -- loop through the patterns to see if we can suggest a proper parameter

Navigation menu