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 = {}; -- talbe of tables listing valid template parameter names; defined in Module:Citation/CS1/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.  Because deprecated parameters (currently |month=,
 
|coauthor=, and |coauthors=) aren't related to each other and because these parameters may be concatenated into the variables used by |date= and |author#= (and aliases)
Categorize and emit an error message when the citation contains one or more deprecated parameters.  The function includes the
details of which parameter caused the error message are not provided.  Only one error message is emitted regardless of the number of deprecated parameters in the citation.
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 true ~= Page_in_deprecated_cat then -- if we haven't been here before then set a
if not page_in_deprecated_cat then
Page_in_deprecated_cat=true; -- sticky flag so that if there are more than one deprecated parameter the category is added only once
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
isbn_str = { isbn_str:byte(1, len) };
return is_valid_isxn_13 (isbn_str);
for i, v in ipairs( isbn_str ) do
temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) );
end
return temp % 10 == 0;
end
end
end
end


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


Validate and format an issnThis code fixes the case where an editor has included an ISSN in the citation but has separated the two groups of four
Determines whether an ISMN string is validSimilar to isbn-13, ismn is 13 digits begining 979-0-... and uses the
digits with a spaceWhen that condition occurred, the resulting link looked like this:
same check digit calculationsSee http://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf
 
section 2, pages 9–12.
|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 function ismn (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['ISMN'];
local handler = cfg.id_handlers['ISSN'];
local text;
local text;
local valid_issn = true;
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 past it here
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
if true ~= Page_in_vanc_error_cat then -- if we haven't been here before then set a sticky flag
add_vanc_error ();
Page_in_vanc_error_cat=true; -- so that if there are more than one error the category is added only once
table.insert( z.message_tail, { set_error( 'vancouver', {}, true ) } );
end
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) -- attempt to convert first name(s) to initials
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 .. "]]" -- link author/editor if this page is not the author's/editor's page
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
local etal_text = cfg.messages['et al'];
result = result .. ' ' .. cfg.messages['et al']; -- we've go a last-first list and etal so add et al.
result = result .. " " .. etal_text;
end
end
Line 1,419: Line 1,490:
end
end


--[[--------------------------< E X T R A C T _ N A M E S >----------------------------------------------------
--[[--------------------------< N A M E _ H A S _ E T A L >----------------------------------------------------
Gets name list from the input arguments
 
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 pattern = ",? *'*[Ee][Tt] *[Aa][Ll][%.']*$" -- variations on the 'et al' theme
 
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 );


local name = tostring(last);
last, etal = name_has_etal (last, etal, false); -- find and remove variations on et al.
if name:match (pattern) then -- varients on et al.
first, etal = name_has_etal (first, etal, false); -- find and remove variations on et al.
last = name:gsub (pattern, ''); -- if found, remove
etal = true;
end
name = tostring(first);
if name:match (pattern) then -- varients on et al.
first = name:gsub (pattern, ''); -- if found, remove
etal = true;
end


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 == count then -- two missing names and we give up
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
if true == etal then
add_maint_cat ('etal');
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, namespace)
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=
local unrec_cat = false -- flag so that we only add unrecognized category once
 
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 0 == namespace and (('en' == lang:lower()) or ('english' == lang:lower())) then
 
add_maint_cat ('english');
end
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 0 == namespace and 'en' ~= code then -- is this page main / article space and English not the language?
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
elseif false == unrec_cat then
else
unrec_cat = true; -- only add this category once
add_maint_cat ('unknown_lang'); -- add maint category if not already added
add_maint_cat (unknown_lang);
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)); -- wrap with '(in ...)'
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:upper()); -- force upper case, add leading space, parenthases, resize
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


--[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------
--[[--------------------------< 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.


This is the main function doing the majority of the citation
check Page and Pages for extraneous p, p., pp, and pp. at start of parameter value:
formatting.
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 citation0( config, args)
local function extra_text_in_page_check (page, nopp)
--[[
-- local good_pattern = '^P[^%.P%l]';
Load Input Parameters
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?
The argument_wrapper facilitates the mapping of multiple
-- local bad_pattern = '^[Pp][Pp]';
aliases to single internal variable.
local bad_pattern = '^[Pp]?[Pp]%.?[ %d]';
]]
local A = argument_wrapper( args );


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 is_set( A['NoPP'] ) then PPPrefix = "" PPrefix = "" 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['Authors'];
-- if Page:match ('^[Pp]?[Pp]%.?[ %d]') or  Page:match ('^[Pp]ages?[ %d]') or
local author_etal;
-- Pages:match ('^[Pp]?[Pp]%.?[ %d]') or  Pages:match ('^[Pp]ages?[ %d]') then
local a, author_etal = extract_names( args, 'AuthorList' );
-- add_maint_cat ('extra_text');
 
-- end
local Coauthors = A['Coauthors'];
end
local Others = A['Others'];
 
local Editors = A['Editors'];
 
local editor_etal;
--[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >--------------------------------
local e, editor_etal = extract_names( args, 'EditorList' );
 
 
This function extracts author / editor names from |vauthors= or |veditors= and finds matching |xxxxor-maskn= and
local NameListFormat = A['NameListFormat']; -- replaces |author-format= and |editor-format=
|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 = ''; -- unset the others
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 = ''; -- unset
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 LastFirstAuthors
do -- do-block to limit scope of last_first_list
local LastFirstAuthors;
local last_first_list;
local Maximum = A['DisplayAuthors'];
local maximum = A['DisplayAuthors'];


Maximum , author_etal = get_display_authors_editors (Maximum, #a, 'authors', author_etal);
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 = maximum,
lastauthoramp = LastAuthorAmp,
lastauthoramp = LastAuthorAmp,
page_name = this_page.text -- get current page name so that we don't wikilink to it via authorlinkn
page_name = this_page.text -- get current page name so that we don't wikilink to it via authorlinkn
};
};
-- If the coauthor field is also used, prevent ampersand and et al. formatting.
if is_set(Coauthors) then -- if the coauthor field is also used, prevent ampersand and et al. formatting.
if is_set(Coauthors) then
control.lastauthoramp = nil;
control.lastauthoramp = nil;
control.maximum = #a + 1;
control.maximum = #a + 1;
end
end
LastFirstAuthors = list_people(control, a, author_etal);
last_first_list = list_people(control, a, author_etal);


if is_set (Authors) then
if is_set (Authors) then
if is_set (LastFirstAuthors) then -- if both author name styles
Authors, author_etal = name_has_etal (Authors, author_etal, false); -- find and remove variations on et al.
table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'authors') .. ' and ' .. wrap_style ('parameter', 'last')}, true ) } ); -- add error message
if author_etal then
Authors = LastFirstAuthors; -- TODO: is this correct or should we use |authors= instead?
Authors = Authors .. ' ' .. cfg.messages['et al']; -- add et al. to authors parameter
end
end
else
else
Authors = LastFirstAuthors; -- either an author name list or an empty string
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
if not is_set(Editors) then
do
local Maximum = A['DisplayEditors'];
local last_first_list;
local maximum = A['DisplayEditors'];


Maximum , editor_etal = get_display_authors_editors (Maximum, #e, 'editors', editor_etal);
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(Maximum) and #e == 4 then  
if not is_set(maximum) and #e == 4 then  
Maximum = 3;
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 = maximum,
lastauthoramp = LastAuthorAmp,
lastauthoramp = LastAuthorAmp,
page_name = this_page.text -- get current page name so that we don't wikilink to it via editorlinkn
page_name = this_page.text -- get current page name so that we don't wikilink to it via editorlinkn
};
};


Editors, EditorCount = list_people(control, e, editor_etal);
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
else
EditorCount = 1;
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, this_page.namespace); -- format, categories (article namespace only), name from ISO639-1, etc
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 "";
Edition = is_set(Edition) and (" " .. wrap_msg ('edition', Edition)) 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)
Anonymous user