Module:Citation/CS1: Difference between revisions
From Vigyanwiki
m (1 revision imported from alpha:Module:Citation/CS1) |
m (1 revision imported) |
||
| (16 intermediate revisions by 7 users not shown) | |||
| Line 1: | Line 1: | ||
require (' | 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 154: | Line 154: | ||
end | end | ||
for _, d in ipairs ( | 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 265: | ||
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 401: | ||
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 533: | Line 533: | ||
end | end | ||
-- if we get this far we have prefix and script | -- if we get this far we have prefix and script | ||
name = cfg. | 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 764: | Line 764: | ||
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. | 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,105: | Line 1,105: | ||
return table.concat(initials) -- Vancouver format does not include spaces. | return table.concat(initials) -- 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,242: | ||
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,290: | ||
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 | result = result .. sep .. cfg.messages['et al']; -- we've got a last-first list and etal so add et al. | ||
end | end | ||
| Line 1,316: | Line 1,406: | ||
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,419: | ||
]] | ]] | ||
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,435: | ||
-- 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 | 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,431: | Line 1,530: | ||
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 | 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, list_name); -- check for names that are composed of digits and punctuation | ||
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 | ||
| Line 1,441: | Line 1,540: | ||
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_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, list_name); -- check for names that are composed of digits and punctuation | name_is_numeric (first, list_name); -- check for names that are composed of digits and punctuation | ||
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 | ||
| Line 1,494: | Line 1,594: | ||
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,537: | Line 1,654: | ||
This function looks for: | This function looks for: | ||
<lang_param> as a tag in cfg. | <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,555: | Line 1,672: | ||
local tag; | local tag; | ||
name = cfg. | 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_lc; -- so return <name> from remap and <lang_param_lc> | return name, lang_param_lc; -- so return <name> from remap and <lang_param_lc> | ||
| Line 1,561: | Line 1,678: | ||
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. | 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> | ||
| Line 1,633: | Line 1,750: | ||
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, tag}, 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) | ||
| Line 1,682: | Line 1,798: | ||
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,772: | Line 1,889: | ||
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,786: | Line 1,908: | ||
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 (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}); | 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 2,089: | Line 2,211: | ||
--[[-------------------------< 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,113: | Line 2,235: | ||
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,123: | Line 2,245: | ||
end | end | ||
end | end | ||
vol = vol .. (utilities.is_set (issue) and utilities.substitute (cfg.messages['j-issue'], issue) or '') | |||
vol = vol .. (utilities.is_set (article) and utilities.substitute (cfg.messages['j-article-num'], article) or '') | |||
return vol; | return vol; | ||
end | end | ||
| Line 2,131: | Line 2,252: | ||
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,300: | Line 2,429: | ||
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,307: | Line 2,440: | ||
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)/'); -- get timestamp from archive.today urls | |||
if timestamp then -- if this was an archive.today url ... | |||
return url, date, timestamp; -- return ArchiveURL, ArchiveDate, and timestamp 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,336: | Line 2,474: | ||
err_msg = cfg.err_msg_supl.flag; | err_msg = cfg.err_msg_supl.flag; | ||
else | else | ||
return url, date; | return url, date, timestamp; -- return ArchiveURL, ArchiveDate, and timestamp from |archive-url= | ||
end | end | ||
end | end | ||
| Line 2,343: | Line 2,481: | ||
if is_preview_mode then | if is_preview_mode then | ||
return url, date; | 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,410: | Line 2,548: | ||
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['Nam | |||