Module:Citation/CS1: Difference between revisions
Synch from sandbox;
m>Waldir m (fix link to redirect) |
m>Trappist the monk (Synch from sandbox;) |
||
Line 13: | Line 13: | ||
local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration | local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration | ||
local whitelist = {}; -- | local whitelist = {}; -- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist | ||
--[[--------------------------< I S _ S E T >------------------------------------------------------------------ | --[[--------------------------< I S _ S E T >------------------------------------------------------------------ | ||
Line 119: | Line 119: | ||
Adds a category to z.maintenance_cats using names from the configuration file with additional text if any. | Adds a category to z.maintenance_cats using names from the configuration file with additional text if any. | ||
To prevent duplication, the added_maint_cats table lists the categories by key that have been added to z.maintenance_cats. | |||
]] | ]] | ||
local added_maint_cats = {} -- list of maintenance categories that have been added to z.maintenance_cats | |||
local function add_maint_cat (key, arguments) | local function add_maint_cat (key, arguments) | ||
table.insert( z.maintenance_cats, substitute (cfg.maint_cats [key], arguments)); -- make name then add to table | if not added_maint_cats [key] then | ||
added_maint_cats [key] = true; -- note that we've added this category | |||
table.insert( z.maintenance_cats, substitute (cfg.maint_cats [key], arguments)); -- make name then add to table | |||
end | |||
end | end | ||
Line 132: | Line 137: | ||
]] | ]] | ||
local added_prop_cats = {} -- list of property categories that have been added to z.properties_cats | |||
local function add_prop_cat (key, arguments) | local function add_prop_cat (key, arguments) | ||
table.insert( z.properties_cats, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table | if not added_prop_cats [key] then | ||
added_prop_cats [key] = true; -- note that we've added this category | |||
table.insert( z.properties_cats, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table | |||
end | |||
end | |||
--[[--------------------------< A D D _ V A N C _ E R R O R >---------------------------------------------------- | |||
Adds a single Vancouver system error message to the template's output regardless of how many error actually exist. | |||
To prevent duplication, added_vanc_errs is nil until an error message is emitted. | |||
]] | |||
local added_vanc_errs; -- flag so we only emit one Vancouver error / category | |||
local function add_vanc_error () | |||
if not added_vanc_errs then | |||
added_vanc_errs = true; -- note that we've added this category | |||
table.insert( z.message_tail, { set_error( 'vancouver', {}, true ) } ); | |||
end | |||
end | end | ||
Line 246: | Line 270: | ||
end | end | ||
--[[ | --[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >-------------------------------------- | ||
Categorize and emit an error message when the citation contains one or more deprecated parameters. | |||
Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes the | |||
offending parameter name to the error message. Only one error message is emitted regardless of the number of deprecated | |||
parameters in the citation. | |||
]] | ]] | ||
local page_in_deprecated_cat; -- sticky flag so that the category is added only once | |||
local function deprecated_parameter(name) | local function deprecated_parameter(name) | ||
if | if not page_in_deprecated_cat then | ||
page_in_deprecated_cat = true; -- note that we've added this category | |||
table.insert( z.message_tail, { set_error( 'deprecated_params', {name}, true ) } ); -- add error message | table.insert( z.message_tail, { set_error( 'deprecated_params', {name}, true ) } ); -- add error message | ||
end | end | ||
Line 610: | Line 638: | ||
end | end | ||
--[[--------------------------< IS _ V A L I D _ I S X N _ 1 3 >---------------------------------------------- | |||
ISBN-13 and ISMN validator code calculates checksum across all 13 isbn/ismn digits including the check digit. | |||
If the number is valid, the result will be 0. Before calling this function, isbn-13/ismn must be checked for length | |||
and stripped of dashes, spaces and other non-isxn-13 characters. | |||
]] | |||
local function is_valid_isxn_13 (isxn_str) | |||
local temp=0; | |||
isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39 | |||
for i, v in ipairs( isxn_str ) do | |||
temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit | |||
end | |||
return temp % 10 == 0; -- sum modulo 10 is zero when isbn-13/ismn is correct | |||
end | |||
--[[--------------------------< C H E C K _ I S B N >------------------------------------------------------------ | --[[--------------------------< C H E C K _ I S B N >------------------------------------------------------------ | ||
Line 631: | Line 677: | ||
else | else | ||
local temp = 0; | local temp = 0; | ||
if isbn_str:match( "^97[89]%d*$" ) == nil then return false; end -- isbn13 begins with 978 or 979 | if isbn_str:match( "^97[89]%d*$" ) == nil then return false; end -- isbn13 begins with 978 or 979; ismn begins with 979 | ||
return is_valid_isxn_13 (isbn_str); | |||
end | end | ||
end | end | ||
--[[--------------------------< I S | --[[--------------------------< C H E C K _ I S M N >------------------------------------------------------------ | ||
Determines whether an ISMN string is valid. Similar to isbn-13, ismn is 13 digits begining 979-0-... and uses the | |||
same check digit calculations. See http://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf | |||
section 2, pages 9–12. | |||
]] | ]] | ||
local function | local function ismn (id) | ||
local handler = cfg.id_handlers['ISMN']; | |||
local handler = cfg.id_handlers[' | |||
local text; | local text; | ||
local | local valid_ismn = true; | ||
id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and endashes from the issn | id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and endashes from the ismn | ||
if 13 ~= id:len() or id:match( "^9790%d*$" ) == nil then -- ismn must be 13 digits and begin 9790 | |||
valid_ismn = false; | |||
else | |||
valid_ismn=is_valid_isxn_13 (id); -- validate ismn | |||
end | |||
-- text = internal_link_id({link = handler.link, label = handler.label, -- use this (or external version) when there is some place to link to | |||
-- prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) | |||
text="[[" .. handler.link .. "|" .. handler.label .. "]]:" .. handler.separator .. id; -- because no place to link to yet | |||
if false == valid_ismn then | |||
text = text .. ' ' .. set_error( 'bad_ismn' ) -- add an error message if the issn is invalid | |||
end | |||
return text; | |||
end | |||
--[[--------------------------< I S S N >---------------------------------------------------------------------- | |||
Validate and format an issn. This code fixes the case where an editor has included an ISSN in the citation but has separated the two groups of four | |||
digits with a space. When that condition occurred, the resulting link looked like this: | |||
|issn=0819 4327 gives: [http://www.worldcat.org/issn/0819 4327 0819 4327] -- can't have spaces in an external link | |||
This code now prevents that by inserting a hyphen at the issn midpoint. It also validates the issn for length and makes sure that the checkdigit agrees | |||
with the calculated value. Incorrect length (8 digits), characters other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check issn | |||
error message. The issn is always displayed with a hyphen, even if the issn was given as a single group of 8 digits. | |||
]] | |||
local function issn(id) | |||
local issn_copy = id; -- save a copy of unadulterated issn; use this version for display if issn does not validate | |||
local handler = cfg.id_handlers['ISSN']; | |||
local text; | |||
local valid_issn = true; | |||
id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and endashes from the issn | |||
if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the issn: 8 digits long, containing only 0-9 or X in the last position | if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the issn: 8 digits long, containing only 0-9 or X in the last position | ||
Line 1,281: | Line 1,356: | ||
|firstn= also allowed to contain hyphens, spaces, apostrophes, and periods | |firstn= also allowed to contain hyphens, spaces, apostrophes, and periods | ||
At the time of this writing, I had to write the 'if nil == mw.ustring.find ...' test ouside of the code editor and | At the time of this writing, I had to write the 'if nil == mw.ustring.find ...' test ouside of the code editor and paste it here | ||
because the code editor gets confused between character insertion point and cursor position. | because the code editor gets confused between character insertion point and cursor position. | ||
Line 1,288: | Line 1,363: | ||
local function is_good_vanc_name (last, first) | local function is_good_vanc_name (last, first) | ||
if nil == mw.ustring.find (last, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%']*$") or nil == mw.ustring.find (first, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]*$") then | if nil == mw.ustring.find (last, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%']*$") or nil == mw.ustring.find (first, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]*$") then | ||
add_vanc_error (); | |||
return false; -- not a string of latin characters; Vancouver required Romanization | return false; -- not a string of latin characters; Vancouver required Romanization | ||
end; | end; | ||
Line 1,369: | Line 1,441: | ||
if ( "vanc" == format ) then -- if vancouver format | if ( "vanc" == format ) then -- if vancouver format | ||
one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/) | one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/) | ||
if is_good_vanc_name (one, first) then -- and name is all Latin characters | if not person.corporate and is_good_vanc_name (one, first) then -- and name is all Latin characters; corporate authors not tested | ||
first = reduce_to_initials(first) | first = reduce_to_initials(first) -- attempt to convert first name(s) to initials | ||
end | end | ||
end | end | ||
Line 1,376: | Line 1,448: | ||
end | end | ||
if is_set(person.link) and person.link ~= control.page_name then | if is_set(person.link) and person.link ~= control.page_name then | ||
one = "[[" .. person.link .. "|" .. one .. "]]" | one = "[[" .. person.link .. "|" .. one .. "]]" -- link author/editor if this page is not the author's/editor's page | ||
end | end | ||
if is_set(person.link) and ((nil ~= person.link:find("//")) or (nil ~= person.link:find("[%[%]]"))) then | if is_set(person.link) and ((nil ~= person.link:find("//")) or (nil ~= person.link:find("[%[%]]"))) then | ||
one = one .. " " .. set_error( 'bad_authorlink' ) end -- url or wikilink in author link; | one = one .. " " .. set_error( 'bad_authorlink' ) end -- url or wikilink in author link; | ||
end | end | ||
table.insert( text, one ) | table.insert( text, one ) | ||
Line 1,387: | Line 1,459: | ||
end | end | ||
local count = #text / 2; | local count = #text / 2; -- (number of names + number of separators) divided by 2 | ||
if count > 0 then | if count > 0 then | ||
if count > 1 and is_set(lastauthoramp) and not etal then | if count > 1 and is_set(lastauthoramp) and not etal then | ||
text[#text-2] = " & "; | text[#text-2] = " & "; -- replace last separator with ampersand text | ||
end | end | ||
text[#text] = nil; | text[#text] = nil; -- erase the last separator | ||
end | end | ||
local result = table.concat(text) -- construct list | local result = table.concat(text) -- construct list | ||
if etal then | if etal and is_set (result) then -- etal may be set by |display-authors=etal but we might not have a last-first list | ||
result = result .. ' ' .. cfg.messages['et al']; -- we've go a last-first list and etal so add et al. | |||
end | end | ||
Line 1,419: | Line 1,490: | ||
end | end | ||
--[[--------------------------< | --[[--------------------------< N A M E _ H A S _ E T A L >---------------------------------------------------- | ||
Evaluates the content of author and editor name parameters for variations on the theme eof et al. If found, | |||
the et al. is removed, a flag is set to true and the function returns the modified name and the flag. | |||
Searches through args in sequential order to find |lastn= and |firstn= parameters (or their aliases), and their matching link and mask parameters. | This function never sets the flag to false but returns it's previous state because it may have been set by | ||
previous passes through this function or by the parameters |display-authors=etal or |displayeditors=etal | |||
]] | |||
local function name_has_etal (name, etal, nocat) | |||
if is_set (name) then -- name can be nil in which case just return | |||
local pattern = "[;,]? *[\"']*%f[Ee][Ee][Tt] *[Aa][Ll][%.\"']*$" -- variations on the 'et al' theme | |||
if name:match (pattern) then -- variants on et al. | |||
name = name:gsub (pattern, ''); -- if found, remove | |||
etal = true; -- set flag (may have been set previously here or by |display-authors=etal) | |||
if not nocat then -- no categorization for |vauthors= | |||
add_maint_cat ('etal'); -- and add a category if not already added | |||
end | |||
end | |||
end | |||
return name, etal; -- | |||
end | |||
--[[--------------------------< E X T R A C T _ N A M E S >---------------------------------------------------- | |||
Gets name list from the input arguments | |||
Searches through args in sequential order to find |lastn= and |firstn= parameters (or their aliases), and their matching link and mask parameters. | |||
Stops searching when both |lastn= and |firstn= are not found in args after two sequential attempts: found |last1=, |last2=, and |last3= but doesn't | Stops searching when both |lastn= and |firstn= are not found in args after two sequential attempts: found |last1=, |last2=, and |last3= but doesn't | ||
find |last4= and |last5= then the search is done. | find |last4= and |last5= then the search is done. | ||
Line 1,445: | Line 1,542: | ||
local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors) | local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors) | ||
local etal=false; -- return value set to true when we find some form of et al. in an author parameter | local etal=false; -- return value set to true when we find some form of et al. in an author parameter | ||
local err_msg_list_name = list_name:match ("(%w+)List") .. 's list'; -- modify AuthorList or EditorList for use in error messages if necessary | local err_msg_list_name = list_name:match ("(%w+)List") .. 's list'; -- modify AuthorList or EditorList for use in error messages if necessary | ||
Line 1,455: | Line 1,551: | ||
mask = select_one( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i ); | mask = select_one( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i ); | ||
last, etal = name_has_etal (last, etal, false); -- find and remove variations on et al. | |||
first, etal = name_has_etal (first, etal, false); -- find and remove variations on et al. | |||
if first and not last then -- if there is a firstn without a matching lastn | if first and not last then -- if there is a firstn without a matching lastn | ||
Line 1,470: | Line 1,558: | ||
elseif not first and not last then -- if both firstn and lastn aren't found, are we done? | elseif not first and not last then -- if both firstn and lastn aren't found, are we done? | ||
count = count + 1; -- number of times we haven't found last and first | count = count + 1; -- number of times we haven't found last and first | ||
if 2 | if 2 <= count then -- two missing names and we give up | ||
break; -- normal exit or there is a two-name hole in the list; can't tell which | break; -- normal exit or there is a two-name hole in the list; can't tell which | ||
end | end | ||
else -- we have last with or without a first | else -- we have last with or without a first | ||
names[n] = {last = last, first = first, link = link, mask = mask}; -- add this name to our names list | names[n] = {last = last, first = first, link = link, mask = mask, corporate=false}; -- add this name to our names list (corporate for |vauthors= only) | ||
n = n + 1; -- point to next location in the names table | n = n + 1; -- point to next location in the names table | ||
if 1 == count then -- if the previous name was missing | if 1 == count then -- if the previous name was missing | ||
Line 1,484: | Line 1,572: | ||
end | end | ||
return names, etal; -- all done, return our list of names | return names, etal; -- all done, return our list of names | ||
end | end | ||
Line 1,535: | Line 1,620: | ||
elseif k == 'PMID' then | elseif k == 'PMID' then | ||
table.insert( new_list, {handler.label, pmid( v ) } ); | table.insert( new_list, {handler.label, pmid( v ) } ); | ||
elseif k == 'ISMN' then | |||
table.insert( new_list, {handler.label, ismn( v ) } ); | |||
elseif k == 'ISSN' then | elseif k == 'ISSN' then | ||
table.insert( new_list, {handler.label, issn( v ) } ); | table.insert( new_list, {handler.label, issn( v ) } ); | ||
Line 1,707: | Line 1,794: | ||
]] | ]] | ||
local function language_parameter (lang | local function language_parameter (lang) | ||
local code; -- the ISO639-1 two character code | local code; -- the ISO639-1 two character code | ||
local name; -- the language name | local name; -- the language name | ||
local language_list = {}; -- table of language names to be rendered | local language_list = {}; -- table of language names to be rendered | ||
local names_table = {}; -- table made from the value assigned to |language= | local names_table = {}; -- table made from the value assigned to |language= | ||
names_table = mw.text.split (lang, '%s*,%s*'); -- names should be a comma separated list | names_table = mw.text.split (lang, '%s*,%s*'); -- names should be a comma separated list | ||
for _, lang in ipairs (names_table) do -- reuse lang | for _, lang in ipairs (names_table) do -- reuse lang | ||
if 2 == lang:len() then -- ISO639-1 language code are 2 characters (fetchLanguageName also supports 3 character codes) | if 2 == lang:len() then -- ISO639-1 language code are 2 characters (fetchLanguageName also supports 3 character codes) | ||
name = mw.language.fetchLanguageName( lang:lower(), "en" ); -- get ISO 639-1 language name if Language is a proper code | name = mw.language.fetchLanguageName( lang:lower(), "en" ); -- get ISO 639-1 language name if Language is a proper code | ||
Line 1,733: | Line 1,816: | ||
if is_set (code) then | if is_set (code) then | ||
if 'no' == code then name = 'Norwegian' end; -- override wikimedia when code is 'no' | if 'no' == code then name = 'Norwegian' end; -- override wikimedia when code is 'no' | ||
if | if 'en' ~= code then -- English not the language | ||
add_prop_cat ('foreign_lang_source', {name, code}) | add_prop_cat ('foreign_lang_source', {name, code}) | ||
end | end | ||
else | |||
add_maint_cat ('unknown_lang'); -- add maint category if not already added | |||
end | end | ||
Line 1,752: | Line 1,834: | ||
name = table.concat (language_list, ', ') -- and concatenate with '<comma><space>' separators | name = table.concat (language_list, ', ') -- and concatenate with '<comma><space>' separators | ||
end | end | ||
return (" " .. wrap_msg ('language', name)); | if 'English' == name then | ||
return ''; -- if one language and that language is English return an enpty string (no annotation) | |||
end | |||
return (" " .. wrap_msg ('language', name)); -- otherwise wrap with '(in ...)' | |||
end | end | ||
Line 1,852: | Line 1,937: | ||
local function style_format (format, url, fmt_param, url_param) | local function style_format (format, url, fmt_param, url_param) | ||
if is_set (format) then | if is_set (format) then | ||
format = wrap_style ('format', format | format = wrap_style ('format', format); -- add leading space, parenthases, resize | ||
if not is_set (url) then | if not is_set (url) then | ||
format = format .. set_error( 'format_missing_url', {fmt_param, url_param} ); -- add an error message | format = format .. set_error( 'format_missing_url', {fmt_param, url_param} ); -- add an error message | ||
Line 1,901: | Line 1,986: | ||
end | end | ||
--[[--------------------------< | --[[--------------------------< E X T R A _ T E X T _ I N _ P A G E _ C H E C K >------------------------------ | ||
Adds page to Category:CS1 maint: extra text if |page= or |pages= has what appears to be some form of p. or pp. | |||
abbreviation in the first characters of the parameter content. | |||
check Page and Pages for extraneous p, p., pp, and pp. at start of parameter value: | |||
good pattern: '^P[^%.P%l]' matches when |page(s)= begins PX or P# but not Px where x and X are letters and # is a dgiit | |||
bad pattern: '^[Pp][Pp]' matches matches when |page(s)= begins pp or pP or Pp or PP | |||
]] | ]] | ||
local function | local function extra_text_in_page_check (page, nopp) | ||
-- local good_pattern = '^P[^%.P%l]'; | |||
local good_pattern = '^P[^%.Pp]'; -- ok to begin with uppercase P: P7 (pg 7 of section P) but not p123 (page 123) TODO: add Gg for PG or Pg? | |||
-- local bad_pattern = '^[Pp][Pp]'; | |||
local bad_pattern = '^[Pp]?[Pp]%.?[ %d]'; | |||
local | |||
local i | if is_set (nopp) then -- don't bother checking if |nopp= is set | ||
local PPrefix = A['PPrefix'] | return; | ||
local PPPrefix = A['PPPrefix'] | end | ||
if not page:match (good_pattern) and (page:match (bad_pattern) or page:match ('^[Pp]ages?')) then | |||
-- Pick out the relevant fields from the arguments. Different citation templates | add_maint_cat ('extra_text'); | ||
-- define different field names for the same underlying things. | end | ||
local Authors = A[' | -- if Page:match ('^[Pp]?[Pp]%.?[ %d]') or Page:match ('^[Pp]ages?[ %d]') or | ||
local | -- Pages:match ('^[Pp]?[Pp]%.?[ %d]') or Pages:match ('^[Pp]ages?[ %d]') then | ||
-- add_maint_cat ('extra_text'); | |||
-- end | |||
local Coauthors = A['Coauthors']; | end | ||
local Others = A['Others']; | |||
local Editors = A['Editors']; | |||
--[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >-------------------------------- | |||
This function extracts author / editor names from |vauthors= or |veditors= and finds matching |xxxxor-maskn= and | |||
|xxxxor-linkn= in args. It then returns a table of assembled names just as extract_names() does. | |||
if is_set (NameListFormat) and ('vanc' ~= NameListFormat) then -- only accepted value for this parameter is 'vanc' | |||
table.insert( z.message_tail, { set_error( 'invalid_param_val', {'name-list-format', NameListFormat}, true ) } ); -- not vanc so add error message | Author / editor names in |vauthors= or |veditors= must be in Vancouver system style. Corporate or institutional names | ||
NameListFormat = ''; -- set to empty string | may sometimes be required and because such names will often fail the is_good_vanc_name() and other format compliance | ||
end | tests, are wrapped in doubled paranethese ((corporate name)) to suppress the format tests. | ||
local Year = A['Year']; | ]] | ||
local PublicationDate = A['PublicationDate']; | |||
local OrigYear = A['OrigYear']; | local function parse_vauthors_veditors (args, vparam, list_name) | ||
local Date = A['Date']; | local names = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn= | ||
local LayDate = A['LayDate']; | local v_name_table = {}; | ||
------------------------------------------------- Get title data | local etal = false; -- return value set to true when we find some form of et al. vauthors parameter | ||
local Title = A['Title']; | local last, first, link, mask; | ||
local ScriptTitle = A['ScriptTitle']; | local corporate = false; | ||
local BookTitle = A['BookTitle']; | |||
local Conference = A['Conference']; | vparam, etal = name_has_etal (vparam, etal, true); -- find and remove variations on et al. do not categorize (do it here because et al. might have a period) | ||
local TransTitle = A['TransTitle']; | if vparam:find ('%[%[') or vparam:find ('%]%]') then -- no wikilinking vauthors names | ||
local TitleNote = A['TitleNote']; | add_vanc_error (); | ||
local TitleLink = A['TitleLink']; | end | ||
local Chapter = A['Chapter']; | v_name_table = mw.text.split(vparam, "%s*,%s*") -- names are separated by commas | ||
local ChapterLink = A['ChapterLink']; -- deprecated | |||
local TransChapter = A['TransChapter']; | for i, v_name in ipairs(v_name_table) do | ||
local TitleType = A['TitleType']; | if v_name:match ('^%(%(.+%)%)$') then -- corporate authors are wrapped in doubled parenthese to supress vanc formatting and error detection | ||
local Degree = A['Degree']; | first = ''; -- set to empty string for concatenation and because it may have been set for previous author/editor | ||
local Docket = A['Docket']; | last = v_name:match ('^%(%((.+)%)%)$') | ||
local ArchiveFormat = A['ArchiveFormat']; | corporate = true; | ||
local ArchiveURL = A['ArchiveURL']; | elseif string.find(v_name, "%s") then | ||
local URL = A['URL'] | lastfirstTable = {} | ||
local URLorigin = A:ORIGIN('URL'); -- get name of parameter that holds URL | lastfirstTable = mw.text.split(v_name, "%s") | ||
local ChapterURL = A['ChapterURL']; | first = table.remove(lastfirstTable); -- removes and returns value of last element in table which should be author intials | ||
local ChapterURLorigin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL | last = table.concat(lastfirstTable, " ") -- returns a string that is the concatenation of all other names that are not initials | ||
local ConferenceFormat = A['ConferenceFormat']; | else | ||
local ConferenceURL = A['ConferenceURL']; | first = ''; -- set to empty string for concatenation and because it may have been set for previous author/editor | ||
local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL | last = v_name; -- last name or single corporate name? Doesn't support multiword corporate names? do we need this? | ||
local Periodical = A['Periodical']; | end | ||
if is_set (first) and not mw.ustring.match (first, "^%u?%u$") then -- first shall contain one or two upper-case letters, nothing else | |||
add_vanc_error (); | |||
end | |||
-- this from extract_names () | |||
link = select_one( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ); | |||
mask = select_one( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i ); | |||
names[i] = {last = last, first = first, link = link, mask = mask, corporate=corporate}; -- add this assembled name to our names list | |||
end | |||
return names, etal; -- all done, return our list of names | |||
end | |||
--[[--------------------------< S E L E C T _ A U T H O R _ E D I T O R _ S O U R C E >------------------------ | |||
Select one of |authors=, |authorn= / |lastn / firstn=, or |vauthors= as the source of the author name list or | |||
select one of |editors=, |editorn= / editor-lastn= / |editor-firstn= or |veditors= as the source of the editor name list. | |||
Only one of these appropriate three will be used. The hierarchy is: |authorn= (and aliases) highest and |authors= lowest and | |||
similarly, |editorn= (and aliases) highest and |editors= lowest | |||
When looking for |authorn= / |editorn= parameters, test |xxxxor1= and |xxxxor2= (and all of their aliases); stops after the second | |||
test which mimicks the test used in extract_names() when looking for a hole in the author name list. There may be a better | |||
way to do this, I just haven't discovered what that way is. | |||
Emits an error message when more than one xxxxor name source is provided. | |||
In this function, vxxxxors = vauthors or veditors; xxxxors = authors or editors as appropriate. | |||
]] | |||
local function select_author_editor_source (vxxxxors, xxxxors, args, list_name) | |||
local lastfirst = false; | |||
if select_one( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', 1 ) or -- do this twice incase we have a first 1 without a last1 | |||
select_one( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', 2 ) then | |||
lastfirst=true; | |||
end | |||
if (is_set (vxxxxors) and true == lastfirst) or -- these are the three error conditions | |||
(is_set (vxxxxors) and is_set (xxxxors)) or | |||
(true == lastfirst and is_set (xxxxors)) then | |||
local err_name; | |||
if 'AuthorList' == list_name then -- figure out which name should be used in error message | |||
err_name = 'author'; | |||
else | |||
err_name = 'editor'; | |||
end | |||
table.insert( z.message_tail, { set_error( 'redundant_parameters', | |||
{err_name .. '-name-list parameters'}, true ) } ); -- add error message | |||
end | |||
if true == lastfirst then return 1 end; -- return a number indicating which author name source to use | |||
if is_set (vxxxxors) then return 2 end; | |||
if is_set (xxxxors) then return 3 end; | |||
return 0; -- no authors so return 0 | |||
end | |||
--[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------ | |||
This is the main function doing the majority of the citation formatting. | |||
]] | |||
local function citation0( config, args) | |||
--[[ | |||
Load Input Parameters | |||
The argument_wrapper facilitates the mapping of multiple aliases to single internal variable. | |||
]] | |||
local A = argument_wrapper( args ); | |||
local i | |||
local PPrefix = A['PPrefix'] | |||
local PPPrefix = A['PPPrefix'] | |||
local NoPP = A['NoPP'] | |||
if in_array(NoPP:lower(), {'yes', 'true', 'y'}) then | |||
PPPrefix = ''; -- unset these, prefix if used is in |page= or |pages= | |||
PPrefix = ''; | |||
else | |||
NoPP = nil; -- unset, used as a flag later | |||
end | |||
-- Pick out the relevant fields from the arguments. Different citation templates | |||
-- define different field names for the same underlying things. | |||
local author_etal; | |||
local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors= | |||
local Authors; | |||
local NameListFormat = A['NameListFormat']; -- replaces |author-format= and |editor-format= | |||
do -- to limit scope of selected | |||
local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'AuthorList'); | |||
if 1 == selected then | |||
a, author_etal = extract_names (args, 'AuthorList'); -- fetch author list from |authorn= / |lastn= / |firstn=, |author-linkn=, and |author-maskn= | |||
elseif 2 == selected then | |||
NameListFormat = 'vanc'; -- override whatever |name-list-format= might be | |||
a, author_etal = parse_vauthors_veditors (args, args.vauthors, 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn= | |||
elseif 3 == selected then | |||
Authors = A['Authors']; -- use content of |authors= | |||
end | |||
end | |||
local Coauthors = A['Coauthors']; | |||
local Others = A['Others']; | |||
local editor_etal; | |||
local e = {}; -- editors list from |editor-lastn= / |editor-firstn= pairs or |veditors= | |||
local Editors; | |||
do -- to limit scope of selected | |||
local selected = select_author_editor_source (A['Veditors'], A['Editors'], args, 'EditorList'); | |||
if 1 == selected then | |||
e, editor_etal = extract_names (args, 'EditorList'); -- fetch editor list from |editorn= / |editor-lastn= / |editor-firstn=, |editor-linkn=, and |editor-maskn= | |||
elseif 2 == selected then | |||
NameListFormat = 'vanc'; -- override whatever |name-list-format= might be | |||
e, editor_etal = parse_vauthors_veditors (args, args.veditors, 'EditorList'); -- fetch editor list from |veditors=, |editor-linkn=, and |editor-maskn= | |||
elseif 3 == selected then | |||
Editors = A['Editors']; -- use content of |editors= | |||
end | |||
end | |||
if is_set (NameListFormat) and ('vanc' ~= NameListFormat) then -- only accepted value for this parameter is 'vanc' | |||
table.insert( z.message_tail, { set_error( 'invalid_param_val', {'name-list-format', NameListFormat}, true ) } ); -- not vanc so add error message | |||
NameListFormat = ''; -- set to empty string | |||
end | |||
local Year = A['Year']; | |||
local PublicationDate = A['PublicationDate']; | |||
local OrigYear = A['OrigYear']; | |||
local Date = A['Date']; | |||
local LayDate = A['LayDate']; | |||
------------------------------------------------- Get title data | |||
local Title = A['Title']; | |||
local ScriptTitle = A['ScriptTitle']; | |||
local BookTitle = A['BookTitle']; | |||
local Conference = A['Conference']; | |||
local TransTitle = A['TransTitle']; | |||
local TitleNote = A['TitleNote']; | |||
local TitleLink = A['TitleLink']; | |||
local Chapter = A['Chapter']; | |||
local ChapterLink = A['ChapterLink']; -- deprecated but used internally by cite episode | |||
local TransChapter = A['TransChapter']; | |||
local TitleType = A['TitleType']; | |||
local Degree = A['Degree']; | |||
local Docket = A['Docket']; | |||
local ArchiveFormat = A['ArchiveFormat']; | |||
local ArchiveURL = A['ArchiveURL']; | |||
local URL = A['URL'] | |||
local URLorigin = A:ORIGIN('URL'); -- get name of parameter that holds URL | |||
local ChapterURL = A['ChapterURL']; | |||
local ChapterURLorigin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL | |||
local ConferenceFormat = A['ConferenceFormat']; | |||
local ConferenceURL = A['ConferenceURL']; | |||
local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL | |||
local Periodical = A['Periodical']; | |||
local Series = A['Series']; | local Series = A['Series']; | ||
Line 2,048: | Line 2,287: | ||
if is_set(Page) then | if is_set(Page) then | ||
if is_set(Pages) or is_set(At) then | if is_set(Pages) or is_set(At) then | ||
Page = Page .. " " .. set_error('extra_pages'); -- add error message | Page = Page .. " " .. set_error('extra_pages'); -- add error message | ||
Pages = ''; | Pages = ''; -- unset the others | ||
At = ''; | At = ''; | ||
end | end | ||
extra_text_in_page_check (Page, NoPP); -- add this page to maint cat if |page= value begins with what looks like p. or pp. | |||
elseif is_set(Pages) then | elseif is_set(Pages) then | ||
if is_set(At) then | if is_set(At) then | ||
Pages = Pages .. " " .. set_error('extra_pages'); -- add error messages | Pages = Pages .. " " .. set_error('extra_pages'); -- add error messages | ||
At = ''; | At = ''; -- unset | ||
end | end | ||
extra_text_in_page_check (Pages, NoPP); -- add this page to maint cat if |page= value begins with what looks like p. or pp. | |||
end | end | ||
Line 2,089: | Line 2,330: | ||
TransChapter = TransTitle; | TransChapter = TransTitle; | ||
ChapterURL = URL; | ChapterURL = URL; | ||
if not is_set (ChapterURL) and is_set (TitleLink) then | |||
Chapter= '[[' .. TitleLink .. '|' .. Chapter .. ']]'; | |||
end | |||
Title = Periodical; | Title = Periodical; | ||
ChapterFormat = Format; | ChapterFormat = Format; | ||
Line 2,095: | Line 2,339: | ||
URL = ''; -- redundant so unset | URL = ''; -- redundant so unset | ||
Format = ''; -- redundant so unset | Format = ''; -- redundant so unset | ||
TitleLink = ''; -- redundant so unset | |||
end | end | ||
else -- |title not set | else -- |title not set | ||
Line 2,268: | Line 2,513: | ||
if not is_set (ID_list['ARXIV']) then -- |arxiv= or |eprint= required for cite arxiv | if not is_set (ID_list['ARXIV']) then -- |arxiv= or |eprint= required for cite arxiv | ||
table.insert( z.message_tail, { set_error( 'arxiv_missing', {}, true ) } ); -- add error message | table.insert( z.message_tail, { set_error( 'arxiv_missing', {}, true ) } ); -- add error message | ||
elseif is_set (Series) then -- series is an alias of version | |||
ID_list['ARXIV'] = ID_list['ARXIV'] .. Series; -- concatenate version onto the end of the arxiv identifier | |||
Series = ''; -- unset | |||
deprecated_parameter ('version'); -- deprecated parameter but only for cite arxiv | |||
end | end | ||
if first_set (AccessDate, At, Chapter, Format, Page, Pages, Periodical, PublisherName, URL, -- a crude list of parameters that are not supported by cite arxiv | if first_set (AccessDate, At, Chapter, Format, Page, Pages, Periodical, PublisherName, URL, -- a crude list of parameters that are not supported by cite arxiv | ||
ID_list['ASIN'], ID_list['BIBCODE'], ID_list['DOI'], ID_list['ISBN'], ID_list['ISSN'], | ID_list['ASIN'], ID_list['BIBCODE'], ID_list['DOI'], ID_list['ISBN'], ID_list['ISSN'], | ||
Line 2,418: | Line 2,668: | ||
-- We also add leading spaces and surrounding markup and punctuation to the | -- We also add leading spaces and surrounding markup and punctuation to the | ||
-- various parts of the citation, but only when they are non-nil. | -- various parts of the citation, but only when they are non-nil. | ||
do -- do-block to limit scope of | do -- do-block to limit scope of last_first_list | ||
local | local last_first_list; | ||
local | local maximum = A['DisplayAuthors']; | ||
maximum , author_etal = get_display_authors_editors (maximum, #a, 'authors', author_etal); | |||
local control = { | local control = { | ||
format = NameListFormat, -- empty string or 'vanc' | format = NameListFormat, -- empty string or 'vanc' | ||
maximum = | maximum = maximum, | ||
lastauthoramp = LastAuthorAmp, | lastauthoramp = LastAuthorAmp, | ||
page_name = this_page.text | page_name = this_page.text -- get current page name so that we don't wikilink to it via authorlinkn | ||
}; | |||
-- | if is_set(Coauthors) then -- if the coauthor field is also used, prevent ampersand and et al. formatting. | ||
control.lastauthoramp = nil; | control.lastauthoramp = nil; | ||
control.maximum = #a + 1; | control.maximum = #a + 1; | ||
end | end | ||
last_first_list = list_people(control, a, author_etal); | |||
if is_set (Authors) then | if is_set (Authors) then | ||
Authors, author_etal = name_has_etal (Authors, author_etal, false); -- find and remove variations on et al. | |||
if author_etal then | |||
Authors = Authors .. ' ' .. cfg.messages['et al']; -- add et al. to authors parameter | |||
end | end | ||
else | else | ||
Authors = | Authors = last_first_list; -- either an author name list or an empty string | ||
end | end | ||
end -- end of do | end -- end of do | ||
Line 2,454: | Line 2,704: | ||
local EditorCount; -- used only for choosing {ed.) or (eds.) annotation at end of editor name-list | local EditorCount; -- used only for choosing {ed.) or (eds.) annotation at end of editor name-list | ||
do | |||
local | local last_first_list; | ||
local maximum = A['DisplayEditors']; | |||
maximum , editor_etal = get_display_authors_editors (maximum, #e, 'editors', editor_etal); | |||
-- Preserve old-style implicit et al. | -- Preserve old-style implicit et al. | ||
if not is_set( | if not is_set(maximum) and #e == 4 then | ||
maximum = 3; | |||
table.insert( z.message_tail, { set_error('implict_etal_editor', {}, true) } ); | table.insert( z.message_tail, { set_error('implict_etal_editor', {}, true) } ); | ||
end | end | ||
Line 2,466: | Line 2,717: | ||
local control = { | local control = { | ||
format = NameListFormat, -- empty string or 'vanc' | format = NameListFormat, -- empty string or 'vanc' | ||
maximum = | maximum = maximum, | ||
lastauthoramp = LastAuthorAmp, | lastauthoramp = LastAuthorAmp, | ||
page_name = this_page.text | page_name = this_page.text -- get current page name so that we don't wikilink to it via editorlinkn | ||
}; | }; | ||
last_first_list, EditorCount = list_people(control, e, editor_etal); | |||
if is_set (Editors) then | |||
if editor_etal then | |||
Editors = Editors .. ' ' .. cfg.messages['et al']; -- add et al. to editors parameter beause |display-editors=etal | |||
EditorCount = 2; -- with et al., |editors= is multiple names; spoof to display (eds.) annotation | |||
else | |||
EditorCount = 2; -- we don't know but assume |editors= is multiple names; spoof to display (eds.) annotation | |||
end | |||
else | |||
Editors = last_first_list; -- either an author name list or an empty string | |||
end | |||
if 1 == EditorCount and (true == editor_etal or 1 < #e) then -- only one editor displayed but includes etal then | if 1 == EditorCount and (true == editor_etal or 1 < #e) then -- only one editor displayed but includes etal then | ||
EditorCount = 2; -- spoof to display (eds.) annotation | EditorCount = 2; -- spoof to display (eds.) annotation | ||
end | end | ||
end | end | ||
-- apply |[xx-]format= styling; at the end, these parameters hold correctly styled format annotation, | -- apply |[xx-]format= styling; at the end, these parameters hold correctly styled format annotation, | ||
Line 2,695: | Line 2,957: | ||
if is_set (Language) then | if is_set (Language) then | ||
Language = language_parameter (Language | Language = language_parameter (Language); -- format, categories, name from ISO639-1, etc | ||
else | else | ||
Language=""; -- language not specified so make sure this is an empty string; | Language=""; -- language not specified so make sure this is an empty string; | ||
Line 2,703: | Line 2,965: | ||
TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""; | TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""; | ||
if is_set (Edition) then | |||
if Edition:match ('[Ee]d%.?$') or Edition:match ('[Ee]dition$') then | |||
add_maint_cat ('extra_text', 'edition'); | |||
end | |||
Edition = " " .. wrap_msg ('edition', Edition); | |||
else | |||
Edition = ''; | |||
end | |||
Issue = is_set(Issue) and (" (" .. Issue .. ")") or ""; | Issue = is_set(Issue) and (" (" .. Issue .. ")") or ""; | ||
Series = is_set(Series) and (sepc .. " " .. Series) or ""; | Series = is_set(Series) and (sepc .. " " .. Series) or ""; | ||
Line 3,060: | Line 3,329: | ||
if #z.maintenance_cats ~= 0 then | if #z.maintenance_cats ~= 0 then | ||
text = text .. ' <span class="citation-comment" style="display:none; color:#33aa33">'; | text = text .. '<span class="citation-comment" style="display:none; color:#33aa33">'; | ||
for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories | for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories | ||
text = text .. v .. ' ([[:Category:' .. v ..'|link]])'; | text = text .. ' ' .. v .. ' ([[:Category:' .. v ..'|link]])'; | ||
end | end | ||
text = text .. '</span>'; -- maintenance mesages (realy just the names of the categories for now) | text = text .. '</span>'; -- maintenance mesages (realy just the names of the categories for now) |