Module:Citation/CS1: Difference between revisions

synch from sandbox;
m>Trappist the monk
(interim fix to kern_quotes(); see discussion;)
m>Trappist the monk
(synch from sandbox;)
Line 5: Line 5:
]]
]]


local dates, year_date_check, reformat_dates, date_hyphen_to_dash -- functions in Module:Citation/CS1/Date_validation
local dates, year_date_check, reformat_dates, date_hyphen_to_dash, -- functions in Module:Citation/CS1/Date_validation
date_name_xlate


local is_set, in_array, substitute, error_comment, set_error, select_one, -- functions in Module:Citation/CS1/Utilities
local is_set, in_array, substitute, error_comment, set_error, select_one, -- functions in Module:Citation/CS1/Utilities
add_maint_cat, wrap_style, safe_for_italics, remove_wiki_link;
add_maint_cat, wrap_style, safe_for_italics, is_wikilink, make_wikilink;


local z ={}; -- tables in Module:Citation/CS1/Utilities
local z ={}; -- tables in Module:Citation/CS1/Utilities
Line 54: Line 55:
end
end
end
end


--[[--------------------------< A D D _ P R O P _ C A T >--------------------------------------------------------
--[[--------------------------< A D D _ P R O P _ C A T >--------------------------------------------------------


Adds a category to z.properties_cats using names from the configuration file with additional text if any.
Adds a category to z.properties_cats using names from the configuration file with additional text if any.
foreign_lang_source and foreign_lang_source_2 keys have a language code appended to them so that multiple languages
may be categorized but multiples of the same language are not categorized.


added_prop_cats is a table declared in page scope variables above
added_prop_cats is a table declared in page scope variables above
Line 66: Line 71:
if not added_prop_cats [key] then
if not added_prop_cats [key] then
added_prop_cats [key] = true; -- note that we've added this category
added_prop_cats [key] = true; -- note that we've added this category
key = key:gsub ('(foreign_lang_source_?2?)%a%a%a?', '%1'); -- strip lang code from keyname
table.insert( z.properties_cats, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table
table.insert( z.properties_cats, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table
end
end
end
end


--[[--------------------------< A D D _ V A N C _ E R R O R >----------------------------------------------------
--[[--------------------------< A D D _ V A N C _ E R R O R >----------------------------------------------------
Line 126: Line 133:
the first character of the whole domain name including subdomains must be a letter or a digit
the first character of the whole domain name including subdomains must be a letter or a digit
internationalized domain name (ascii characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the tld) see https://tools.ietf.org/html/rfc3490
internationalized domain name (ascii characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the tld) see https://tools.ietf.org/html/rfc3490
single-letter/digit second-level domains in the .org TLD
single-letter/digit second-level domains in the .org and .cash TLDs
q, x, and z SL domains in the .com TLD
q, x, and z SL domains in the .com TLD
i and q SL domains in the .net TLD
i and q SL domains in the .net TLD
Line 153: Line 160:
elseif domain:match ('%f[%a%d][%a%d][%a%d%-]+[%a%d]%.xn%-%-[%a%d]+$') then -- internationalized domain name with ACE prefix
elseif domain:match ('%f[%a%d][%a%d][%a%d%-]+[%a%d]%.xn%-%-[%a%d]+$') then -- internationalized domain name with ACE prefix
return true;
return true;
elseif domain:match ('%f[%a%d][%a%d]%.org$') then -- one character .org hostname
elseif domain:match ('%f[%a%d][%a%d]%.cash$') then -- one character/digit .cash hostname
return true;
elseif domain:match ('%f[%a%d][%a%d]%.org$') then -- one character/digit .org hostname
return true;
return true;
elseif domain:match ('%f[%a][qxz]%.com$') then -- assigned one character .com hostname (x.com times out 2015-12-10)
elseif domain:match ('%f[%a][qxz]%.com$') then -- assigned one character .com hostname (x.com times out 2015-12-10)
Line 249: Line 258:
return not is_url (scheme, domain); -- return true if value DOES NOT appear to be a valid url
return not is_url (scheme, domain); -- return true if value DOES NOT appear to be a valid url
end
end


--[[--------------------------< L I N K _ T I T L E _ O K >---------------------------------------------------
--[[--------------------------< L I N K _ T I T L E _ O K >---------------------------------------------------
Line 376: Line 386:
['\n'] = ' ' } );
['\n'] = ' ' } );
end
end


--[[--------------------------< E X T E R N A L _ L I N K >----------------------------------------------------
--[[--------------------------< E X T E R N A L _ L I N K >----------------------------------------------------
Line 408: Line 419:
if is_set (access) then -- access level (subscription, registration, limited)
if is_set (access) then -- access level (subscription, registration, limited)
local label_head = '';
label = safe_for_url (label); -- replace square brackets and newlines
local label_tail;
local markup = ''; -- can't start a span inside italic markup and end it outside the italic markup


label = safe_for_url (label); -- replace square brackets and newlines (is this necessary? already done above?)
base_url = table.concat ( -- assemble external link with access signal
if label:match ("(.*)%s+(.+)('''?)$") then -- for italicized titles (cite book, etc)
label_head, label_tail, markup = label:match ("(.*)%s+(.+)('''?)$"); -- split the label at the right-most space; separate the markup
elseif label:match ("(.*)%s+(.+)$") then -- for upright titles (journal, news, magazine, etc)
label_head, label_tail = label:match ("(.*)%s+(.+)$"); -- split the label at the right-most space; no markup
elseif label:match ("(.+)('''?)$") then -- single word label with markup
label_tail, markup = label:match ("(.+)('''?)$"); -- save label text as label tail; separate the markup
else
label_tail = label;
end
 
base_url = table.concat (
{
{
'<span class="plainlinks">[', -- opening css
'<span class="plainlinks">[', -- opening css and url markup
URL, -- the url
URL, -- the url
' ', -- the required space
' ', -- the required space
label_head, -- all but the last word of the label
label,
' <span class="nowrap">', -- nowrap css for the last word and the signal icon
label_tail, -- last (or only) word of the label inside the span
'<span style="padding-left:0.15em">', -- signal spacing css
'<span style="padding-left:0.15em">', -- signal spacing css
cfg.presentation[access], -- the appropriate icon
cfg.presentation[access], -- the appropriate icon
'</span></span>', -- close signal spacing and nowrap spans
'</span>', -- close signal spacing span
markup, -- insert italic markup if any
']</span>' -- close url markup and plain links span
']</span>' -- close the plain links span
});
});
else
else
Line 462: Line 457:
end
end


--[[--------------------------< K E R N _ Q U O T E S >--------------------------------------------------------


Apply kerning to open the space between the quote mark provided by the Module and a leading or trailing quote mark contained in a |title= or |chapter= parameter's value.
--[=[-------------------------< K E R N _ Q U O T E S >--------------------------------------------------------
 
Apply kerning to open the space between the quote mark provided by the Module and a leading or trailing quote
mark contained in a |title= or |chapter= parameter's value.
 
This function will positive kern either single or double quotes:
This function will positive kern either single or double quotes:
"'Unkerned title with leading and trailing single quote marks'"
"'Unkerned title with leading and trailing single quote marks'"
Line 470: Line 468:
Double single quotes (italic or bold wikimarkup) are not kerned.
Double single quotes (italic or bold wikimarkup) are not kerned.


Replaces unicode quotemarks with typewriter quote marks regardless of the need for kerning.
Replaces unicode quotemarks in plain text or in the label portion of a [[L|D]] style wikilink with typewriter
quote marks regardless of the need for kerning.  Unicode quote marks are not replaced in simple [[D]] wikilinks.


Call this function for chapter titles, for website titles, etc; not for book titles.
Call this function for chapter titles, for website titles, etc; not for book titles.


]]
]=]


local function kern_quotes (str)
local function kern_quotes (str)
local cap='';
local cap='';
local cap2='';
local cap2='';
-- TODO: move this elswhere so that all title-holding elements get these quote marks replaced?
local wl_type, label, link;
-- str= mw.ustring.gsub (str, '[“”]', '\"'); -- replace “” (U+201C & U+201D) with " (typewriter double quote mark)
 
-- str= mw.ustring.gsub (str, '[‘’]', '\''); -- replace ‘’ (U+2018 & U+2019) with ' (typewriter single quote mark)
wl_type, label, link = is_wikilink (str); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
cap, cap2 = str:match ("^([\"\'])([^\'].+)"); -- match leading double or single quote but not double single quotes
if 1 == wl_type then -- [[D]] simple wikilink with or without quote marks
if is_set (cap) then
if mw.ustring.match (str, '%[%[[\"“”\'‘’].+[\"“”\'‘’]%]%]') then -- leading and trailing quote marks
str = substitute (cfg.presentation['kern-left'], {cap, cap2});
str = substitute (cfg.presentation['kern-wl-both'], str);
end
elseif mw.ustring.match (str, '%[%[[\"“”\'‘’].+%]%]') then -- leading quote marks
str = substitute (cfg.presentation['kern-wl-left'], str);
elseif mw.ustring.match (str, '%[%[.+[\"“”\'‘’]%]%]') then -- trailing quote marks
str = substitute (cfg.presentation['kern-wl-right'], str);
end
 
else -- plain text or [[L|D]]; text in label variable
label= mw.ustring.gsub (label, '[“”]', '\"'); -- replace “” (U+201C & U+201D) with " (typewriter double quote mark)
label= mw.ustring.gsub (label, '[‘’]', '\''); -- replace ‘’ (U+2018 & U+2019) with ' (typewriter single quote mark)


cap, cap2 = str:match ("^(.+[^\'])([\"\'])$")
cap, cap2 = mw.ustring.match (label, "^([\"\'])([^\'].+)"); -- match leading double or single quote but not doubled single quotes (italic markup)
if is_set (cap) then
if is_set (cap) then
str = substitute (cfg.presentation['kern-right'], {cap, cap2});
label = substitute (cfg.presentation['kern-left'], {cap, cap2});
end
cap, cap2 = mw.ustring.match (label, "^(.+[^\'])([\"\'])$") -- match trailing double or single quote but not doubled single quotes (italic markup)
if is_set (cap) then
label = substitute (cfg.presentation['kern-right'], {cap, cap2});
end
if 2 == wl_type then
str = make_wikilink (link, label); -- reassemble the wikilink
else
str = label;
end
end
end
return str;
return str;
end
end


--[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >----------------------------------------
--[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >----------------------------------------
Line 547: Line 567:
return script_value;
return script_value;
end
end


--[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------
--[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------
Line 594: Line 615:
]]
]]


local function format_chapter_title (scriptchapter, chapter, transchapter, chapterurl, chapter_url_source, no_quotes)
local function format_chapter_title (scriptchapter, chapter, transchapter, chapterurl, chapter_url_source, no_quotes, access)
local chapter_error = '';
local chapter_error = '';
Line 601: Line 622:
else
else
if false == no_quotes then
if false == no_quotes then
chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from Module provided quote marks
chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from Module provided quote marks
chapter = wrap_style ('quoted-title', chapter);
chapter = wrap_style ('quoted-title', chapter);
end
end
Line 619: Line 640:


if is_set (chapterurl) then
if is_set (chapterurl) then
chapter = external_link (chapterurl, chapter, chapter_url_source, nil); -- adds bare_url_missing_title error if appropriate
chapter = external_link (chapterurl, chapter, chapter_url_source, access); -- adds bare_url_missing_title error if appropriate
end
end


return chapter .. chapter_error;
return chapter .. chapter_error;
end
end


--[[--------------------------< H A S _ I N V I S I B L E _ C H A R S >----------------------------------------
--[[--------------------------< H A S _ I N V I S I B L E _ C H A R S >----------------------------------------
Line 659: Line 681:
local pattern=cfg.invisible_chars[i][2] -- the pattern used to find it
local pattern=cfg.invisible_chars[i][2] -- the pattern used to find it
position, dummy, capture = mw.ustring.find (v, pattern) -- see if the parameter value contains characters that match the pattern
position, dummy, capture = mw.ustring.find (v, pattern) -- see if the parameter value contains characters that match the pattern
if position and (char == 'zero width joiner') then -- if we found a zero width joiner character
if mw.ustring.find (v, cfg.indic_script) then -- its ok if one of the indic scripts
position = nil; -- unset position
end
end
if position then
if position then
Line 730: Line 758:
});
});
end
end


--[[--------------------------< V A L I D A T E >--------------------------------------------------------------
--[[--------------------------< V A L I D A T E >--------------------------------------------------------------
Line 742: Line 771:
]]
]]


--local function validate( name )
local function validate( name, cite_class )
local function validate( name, cite_class )
local name = tostring( name );
local name = tostring( name );
local state;
local state;
if in_array (cite_class, {'arxiv', 'biorxiv', 'citeseerx'}) then -- limited parameter sets allowed for these templates
if in_array (cite_class, {'arxiv', 'biorxiv', 'citeseerx'}) then -- limited parameter sets allowed for these templates
state = whitelist.limited_basic_arguments[ name ];
state = whitelist.limited_basic_arguments[ name ];
if true == state then return true; end -- valid actively supported parameter
if true == state then return true; end -- valid actively supported parameter
Line 826: Line 854:
return date;
return date;
end
end


--[[--------------------------< S E T _ T I T L E T Y P E >----------------------------------------------------
--[[--------------------------< S E T _ T I T L E T Y P E >----------------------------------------------------
Line 909: Line 938:
elseif end_chr == "]" then -- if it might be wikimarkup
elseif end_chr == "]" then -- if it might be wikimarkup
if str:sub(-3,-1) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink  
if str:sub(-3,-1) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink  
trim = true;
elseif str:sub(-3,-1) == duplicate_char .. '"]' then -- if last three chars of str are sepc"] quoted external link
trim = true;
trim = true;
elseif str:sub(-2,-1) == duplicate_char .. "]" then -- if last two chars of str are sepc] external link
elseif str:sub(-2,-1) == duplicate_char .. "]" then -- if last two chars of str are sepc] external link
Line 951: Line 982:
return false;
return false;
end
end


--[[--------------------------< I S _ G O O D _ V A N C _ N A M E >--------------------------------------------
--[[--------------------------< I S _ G O O D _ V A N C _ N A M E >--------------------------------------------
Line 997: Line 1,029:
return true;
return true;
end
end


--[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------
--[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------
Line 1,055: Line 1,088:
return table.concat(initials) -- Vancouver format does not include spaces.
return table.concat(initials) -- Vancouver format does not include spaces.
end
end


--[[--------------------------< L I S T  _ P E O P L E >-------------------------------------------------------
--[[--------------------------< L I S T  _ P E O P L E >-------------------------------------------------------
Line 1,073: Line 1,107:
sep = ','; -- name-list separator between authors is a comma
sep = ','; -- name-list separator between authors is a comma
namesep = ' '; -- last/first separator is a space
namesep = ' '; -- last/first separator is a space
elseif 'mla' == control.mode then
sep = ','; -- name-list separator between authors is a comma
namesep = ', ' -- last/first separator is <comma><space>
else
else
sep = ';' -- name-list separator between authors is a semicolon
sep = ';' -- name-list separator between authors is a semicolon
Line 1,104: Line 1,135:
local first = person.first
local first = person.first
if is_set(first) then
if is_set(first) then
if 'mla' == control.mode then  
if ( "vanc" == format ) then -- if vancouver format
if i == 1 then -- for mla
one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
one = one .. namesep .. first; -- first name last, first
if not person.corporate and is_good_vanc_name (one, first) then -- and name is all Latin characters; corporate authors not tested
else -- all other names
first = reduce_to_initials(first) -- attempt to convert first name(s) to initials
one = first .. ' ' .. one; -- first last
end
end
else
end
if ( "vanc" == format ) then -- if vancouver format
one = one .. namesep .. first;
one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
if not person.corporate and is_good_vanc_name (one, first) then -- and name is all Latin characters; corporate authors not tested
first = reduce_to_initials(first) -- attempt to convert first name(s) to initials
end
end
one = one .. namesep .. first;
end
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 = make_wikilink (person.link, one); -- link author/editor if this page is not the author's/editor's page
end
end
end
end
Line 1,132: Line 1,155:
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
if 'mla' == control.mode then
text[#text-2] = " & "; -- replace last separator with ampersand text
text[#text-2] = ", and "; -- replace last separator with ', and ' text
else
text[#text-2] = " & "; -- replace last separator with ampersand text
end
end
end
text[#text] = nil; -- erase the last separator
text[#text] = nil; -- erase the last separator
Line 1,148: Line 1,167:
return result, count
return result, count
end
end


--[[--------------------------< A N C H O R _ I D >------------------------------------------------------------
--[[--------------------------< A N C H O R _ I D >------------------------------------------------------------
Line 1,227: Line 1,247:
'^[%(%[]?%s*[Ee][Dd][Ii][Tt][Oo][Rr][Ss]?%A', -- (editor or (editors: also sq brackets, case insensitive, optional brackets, 's'
'^[%(%[]?%s*[Ee][Dd][Ii][Tt][Oo][Rr][Ss]?%A', -- (editor or (editors: also sq brackets, case insensitive, optional brackets, 's'
'^[%(%[]?%s*[Ee][Dd][Ii][Tt][Ee][Dd]%A', -- (edited: also sq brackets, case insensitive, optional brackets
'^[%(%[]?%s*[Ee][Dd][Ii][Tt][Ee][Dd]%A', -- (edited: also sq brackets, case insensitive, optional brackets
}
}


Line 1,349: Line 1,368:
return names, etal; -- all done, return our list of names
return names, etal; -- all done, return our list of names
end
end


--[[--------------------------< G E T _ I S O 6 3 9 _ C O D E >------------------------------------------------
--[[--------------------------< G E T _ I S O 6 3 9 _ C O D E >------------------------------------------------
Line 1,440: Line 1,460:
if this_wiki_code ~= code then -- when the language is not the same as this wiki's language
if this_wiki_code ~= code then -- when the language is not the same as this wiki's language
if 2 == code:len() then -- and is a two-character code
if 2 == code:len() then -- and is a two-character code
add_prop_cat ('foreign_lang_source', {name, code}) -- categorize it
add_prop_cat ('foreign_lang_source' .. code, {name, code}) -- categorize it
else -- or is a recognized language (but has a three-character code)
else -- or is a recognized language (but has a three-character code)
add_prop_cat ('foreign_lang_source_2', {code}) -- categorize it differently TODO: support mutliple three-character code categories per cs1|2 template
add_prop_cat ('foreign_lang_source_2' .. code, {code}) -- categorize it differently TODO: support mutliple three-character code categories per cs1|2 template
end
end
end
end
Line 1,468: Line 1,488:
]]
]]
end
end


--[[--------------------------< S E T _ C S 1 _ S T Y L E >----------------------------------------------------
--[[--------------------------< S E T _ C S 1 _ S T Y L E >----------------------------------------------------
Line 1,481: Line 1,502:
return '.', ps; -- separator is a full stop
return '.', ps; -- separator is a full stop
end
end


--[[--------------------------< S E T _ C S 2 _ S T Y L E >----------------------------------------------------
--[[--------------------------< S E T _ C S 2 _ S T Y L E >----------------------------------------------------
Line 1,497: Line 1,519:
return ',', ps, ref; -- separator is a comma
return ',', ps, ref; -- separator is a comma
end
end


--[[--------------------------< G E T _ S E T T I N G S _ F R O M _ C I T E _ C L A S S >----------------------
--[[--------------------------< G E T _ S E T T I N G S _ F R O M _ C I T E _ C L A S S >----------------------
Line 1,515: Line 1,538:
return sep, ps, ref -- return them all
return sep, ps, ref -- return them all
end
end


--[[--------------------------< S E T _ S T Y L E >------------------------------------------------------------
--[[--------------------------< S E T _ S T Y L E >------------------------------------------------------------
Line 1,528: Line 1,552:
sep, ps, ref = set_cs2_style (ps, ref);
sep, ps, ref = set_cs2_style (ps, ref);
elseif 'cs1' == mode then -- if this template is to be rendered in CS1 (cite xxx) style
elseif 'cs1' == mode then -- if this template is to be rendered in CS1 (cite xxx) style
sep, ps = set_cs1_style (ps);
elseif 'mla' == mode then -- if this template is to be rendered in mla style use cs1 for bot cs1 & cs2 templates
sep, ps = set_cs1_style (ps);
sep, ps = set_cs1_style (ps);
else -- anything but cs1 or cs2
else -- anything but cs1 or cs2
Line 1,540: Line 1,562:
return sep, ps, ref
return sep, ps, ref
end
end


--[=[-------------------------< I S _ P D F >------------------------------------------------------------------
--[=[-------------------------< I S _ P D F >------------------------------------------------------------------
Line 1,546: Line 1,569:
applying the pdf icon to external links.
applying the pdf icon to external links.


returns true if file extension is one of the recognized extension, else false
returns true if file extension is one of the recognized extensions, else false


]=]
]=]


local function is_pdf (url)
local function is_pdf (url)
return url:match ('%.pdf[%?#]?') or url:match ('%.PDF[%?#]?');
return url:match ('%.pdf$') or url:match ('%.PDF$') or url:match ('%.pdf[%?#]') or url:match ('%.PDF[%?#]');
end
end


--[[--------------------------< S T Y L E _ F O R M A T >------------------------------------------------------
--[[--------------------------< S T Y L E _ F O R M A T >------------------------------------------------------
Line 1,565: Line 1,589:
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); -- add leading space, parenthases, resize
format = wrap_style ('format', format); -- add leading space, parentheses, 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,576: Line 1,600:
return format;
return format;
end
end


--[[--------------------------< G E T _ D I S P L A Y _ A U T H O R S _ E D I T O R S >------------------------
--[[--------------------------< G E T _ D I S P L A Y _ A U T H O R S _ E D I T O R S >------------------------
Line 1,618: Line 1,643:
return max, etal;
return max, etal;
end
end


--[[--------------------------< E X T R A _ T E X T _ I N _ P A G E _ C H E C K >------------------------------
--[[--------------------------< E X T R A _ T E X T _ I N _ P A G E _ C H E C K >------------------------------
Line 1,631: Line 1,657:


local function extra_text_in_page_check (page)
local function extra_text_in_page_check (page)
-- 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 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 bad_pattern = '^[Pp]?[Pp]%.?[ %d]';


Line 1,639: Line 1,663:
add_maint_cat ('extra_text');
add_maint_cat ('extra_text');
end
end
-- if Page:match ('^[Pp]?[Pp]%.?[ %d]') or  Page:match ('^[Pp]ages?[ %d]') or
-- Pages:match ('^[Pp]?[Pp]%.?[ %d]') or  Pages:match ('^[Pp]ages?[ %d]') then
-- add_maint_cat ('extra_text');
-- end
end
end


--[[--------------------------< G E T _ V _ N A M E _ T A B L E >----------------------------------------------


split apart a |vautthors= or |veditors= parameter.  This function allows for corporate names, wrapped in doubled
--[=[-------------------------< G E T _ V _ N A M E _ T A B L E >----------------------------------------------
 
split apart a |vauthors= or |veditors= parameter.  This function allows for corporate names, wrapped in doubled
parentheses to also have commas; in the old version of the code, the doubled parnetheses were included in the
parentheses to also have commas; in the old version of the code, the doubled parnetheses were included in the
rendered citation and in the metadata.
rendered citation and in the metadata. Individual author names may be wikilinked


|vauthors=Jones AB, White EB, ((Black, Brown, and Co.))
|vauthors=Jones AB, [[E. B. White|White EB]], ((Black, Brown, and Co.))


This code is experimental and may not be retained.
]=]


]]
local function get_v_name_table (vparam, output_table, output_link_table)
local function get_v_name_table (vparam, output_table)
local name_table = mw.text.split(vparam, "%s*,%s*"); -- names are separated by commas
local name_table = mw.text.split(vparam, "%s*,%s*"); -- names are separated by commas
local wl_type, label, link; -- wl_type not used here; just a place holder
local i = 1;
local i = 1;
Line 1,673: Line 1,694:
end
end
table.insert (output_table, name); -- and add corporate name to the output table
table.insert (output_table, name); -- and add corporate name to the output table
table.insert (output_link_table, ''); -- no wikilink
else
else
table.insert (output_table, name_table[i]); -- add this name
wl_type, label, link = is_wikilink (name_table[i]); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
table.insert (output_table, label); -- add this name
if 1 == wl_type then
table.insert (output_link_table, label); -- simple wikilink [[D]]
else
table.insert (output_link_table, link); -- no wikilink or [[L|D]]; add this link if there is one, else empty string
end
end
end
i = i+1;
i = i+1;
Line 1,680: Line 1,708:
return output_table;
return output_table;
end
end


--[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >--------------------------------
--[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >--------------------------------
Line 1,699: Line 1,728:
local names = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn=
local names = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn=
local v_name_table = {};
local v_name_table = {};
local v_link_table = {}; -- when name is wikilinked, targets go in this table
local etal = false; -- return value set to true when we find some form of et al. vauthors parameter
local etal = false; -- return value set to true when we find some form of et al. vauthors parameter
local last, first, link, mask, suffix;
local last, first, link, mask, suffix;
Line 1,704: Line 1,734:


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)
vparam, etal = name_has_etal (vparam, etal, true); -- find and remove variations on et al. do not categorize (do it here because et al. might have a period)
if vparam:find ('%[%[') or vparam:find ('%]%]') then -- no wikilinking vauthors names
v_name_table = get_v_name_table (vparam, v_name_table, v_link_table); -- names are separated by commas
add_vanc_error ('wikilink');
end
v_name_table = get_v_name_table (vparam, v_name_table); -- names are separated by commas


for i, v_name in ipairs(v_name_table) do
for i, v_name in ipairs(v_name_table) do
Line 1,751: Line 1,778:
end
end
end
end
-- this from extract_names ()
 
link = select_one( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i );
link = select_one( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ) or v_link_table[i];
mask = select_one( args, cfg.aliases[list_name .. '-Mask'], '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
names[i] = {last = last, first = first, link = link, mask = mask, corporate=corporate}; -- add this assembled name to our names list
Line 1,811: Line 1,838:
This function is used to validate a parameter's assigned value for those parameters that have only a limited number
This function is used to validate a parameter's assigned value for those parameters that have only a limited number
of allowable values (yes, y, true, no, etc).  When the parameter value has not been assigned a value (missing or empty
of allowable values (yes, y, true, no, etc).  When the parameter value has not been assigned a value (missing or empty
in the source template) the function refurns true.  If the parameter value is one of the list of allowed values returns
in the source template) the function returns true.  If the parameter value is one of the list of allowed values returns
true; else, emits an error message and returns false.
true; else, emits an error message and returns false.


]]
]]


local function is_valid_parameter_value (value, name, possible, cite_class)
local function is_valid_parameter_value (value, name, possible)
-- begin hack to limit |mode=mla to a specific set of templates
if ('mode' == name) and ('mla' == value) and not in_array (cite_class, {'book', 'journal', 'news'}) then
table.insert( z.message_tail, { set_error( 'invalid_param_val', {name, value}, true ) } ); -- not an allowed value so add error message
return false
end
-- end hack
 
if not is_set (value) then
if not is_set (value) then
return true; -- an empty parameter is ok
return true; -- an empty parameter is ok
Line 1,862: Line 1,882:
]]
]]
local function format_volume_issue (volume, issue, cite_class, origin, sepc, lower, mode)
local function format_volume_issue (volume, issue, cite_class, origin, sepc, lower)
if not is_set (volume) and not is_set (issue) then
if not is_set (volume) and not is_set (issue) then
return '';
return '';
end
end
if ('mla' == mode) and ('journal' == cite_class) then -- same as cs1 for magazines
lower = true; -- mla 8th edition; force these to lower case
if is_set (volume) and is_set (issue) then
return wrap_msg ('vol-no', {sepc, volume, issue}, lower);
elseif is_set (volume) then
return wrap_msg ('vol', {sepc, volume}, lower);
else
return '';
end
end
if 'magazine' == cite_class or (in_array (cite_class, {'citation', 'map'}) and 'magazine' == origin) then
if 'magazine' == cite_class or (in_array (cite_class, {'citation', 'map'}) and 'magazine' == origin) then
if is_set (volume) and is_set (issue) then
if is_set (volume) and is_set (issue) then
Line 1,902: Line 1,911:
return vol;
return vol;
end
end
--[[-------------------------< N O R M A L I Z E _ P A G E _ L I S T >-----------------------------------------
not currently used
normalizes a comma, ampersand, and/or space separated list to be '<value>, <value>, ..., <value>'
returns list unchanged if there are no commas else strips whitespace and then reformats the list
]]
--[[
local function normalize_page_list (list)
if not list:find ('[,& ]') then return list end -- if list is not delimited with commas, ampersands, or spaces; done
list = mw.text.split (list, '[,&%s]+'); -- make a table of values
list = table.concat (list, ', '); -- and now make a normalized list
return list;
end
]]




Line 1,933: Line 1,923:
]]
]]


local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower, mode)
local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower)
if 'map' == cite_class then -- only cite map supports sheet(s) as in-source locators
if 'map' == cite_class then -- only cite map supports sheet(s) as in-source locators
if is_set (sheet) then
if is_set (sheet) then
Line 1,952: Line 1,942:
local is_journal = 'journal' == cite_class or (in_array (cite_class, {'citation', 'map'}) and 'journal' == origin);
local is_journal = 'journal' == cite_class or (in_array (cite_class, {'citation', 'map'}) and 'journal' == origin);
if is_journal and 'mla' == mode then
is_journal = false; -- mla always uses p & pp
end
if is_set (page) then
if is_set (page) then
if is_journal then
if is_journal then
Line 2,096: Line 2,082:
-- define different field names for the same underlying things.
-- define different field names for the same underlying things.


-- set default parameter values defined by |mode= parameter. If |mode= is empty or omitted, use CitationClass to set these values
-- set default parameter values defined by |mode= parameter.
local Mode = A['Mode'];
local Mode = A['Mode'];
if not is_valid_parameter_value (Mode, 'mode', cfg.keywords['mode'], config.CitationClass) then
if not is_valid_parameter_value (Mode, 'mode', cfg.keywords['mode']) then
Mode = '';
Mode = '';
end
end
Line 2,148: Line 2,134:
local Translators; -- assembled translators name list
local Translators; -- assembled translators name list
t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn=
t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn=


local interviewers_list = {};
local interviewers_list = {};
Line 2,236: Line 2,221:
local Pages;
local Pages;
local At;
local At;
-- previously conference books did not support volume
 
-- if in_array (config.CitationClass, cfg.templates_using_volume) and not ('conference' == config.CitationClass and not is_set (Periodical)) then
if in_array (config.CitationClass, cfg.templates_using_volume) then
if in_array (config.CitationClass, cfg.templates_using_volume) then
Volume = A['Volume'];
Volume = A['Volume'];
Line 2,261: Line 2,245:
RegistrationRequired=nil;
RegistrationRequired=nil;
end
end
local SubscriptionRequired = A['SubscriptionRequired'];
local SubscriptionRequired = A['SubscriptionRequired'];
if not is_valid_parameter_value (SubscriptionRequired, 'subscription', cfg.keywords ['yes_true_y']) then
if not is_valid_parameter_value (SubscriptionRequired, 'subscription', cfg.keywords ['yes_true_y']) then
SubscriptionRequired=nil;
SubscriptionRequired=nil;
end
end
local UrlAccess = A['UrlAccess'];
local UrlAccess = A['UrlAccess'];
if not is_valid_parameter_value (UrlAccess, 'url-access', cfg.keywords ['url-access']) then
if not is_valid_parameter_value (UrlAccess, 'url-access', cfg.keywords ['url-access']) then
Line 2,283: Line 2,269:
end
end


local ChapterUrlAccess = A['ChapterUrlAccess'];
if not is_valid_parameter_value (ChapterUrlAccess, 'chapter-url-access', cfg.keywords ['url-access']) then -- same as url-access
ChapterUrlAccess = nil;
end
if not is_set(ChapterURL) and is_set(ChapterUrlAccess) then
ChapterUrlAccess = nil;
table.insert( z.message_tail, { set_error( 'param_access_requires_param', {'chapter-url'}, true ) } );
end


local Via = A['Via'];
local Via = A['Via'];
Line 2,318: Line 2,312:
LastAuthorAmp = nil; -- set to empty string
LastAuthorAmp = nil; -- set to empty string
end
end
if 'mla' == Mode then
 
LastAuthorAmp = 'yes'; -- replaces last author/editor separator with ' and ' text
end
local no_tracking_cats = A['NoTracking'];
local no_tracking_cats = A['NoTracking'];
if not is_valid_parameter_value (no_tracking_cats, 'no-tracking', cfg.keywords ['yes_true_y']) then
if not is_valid_parameter_value (no_tracking_cats, 'no-tracking', cfg.keywords ['yes_true_y']) then
Line 2,326: Line 2,318:
end
end


--local variables that are not cs1 parameters
--local variables that are not cs1 parameters
local use_lowercase; -- controls capitalization of certain static text
local use_lowercase; -- controls capitalization of certain static text
local this_page = mw.title.getCurrentTitle(); -- also used for COinS and for language
local this_page = mw.title.getCurrentTitle(); -- also used for COinS and for language
Line 2,343: Line 2,335:
use_lowercase = ( sepc == ',' ); -- used to control capitalization for certain static text
use_lowercase = ( sepc == ',' ); -- used to control capitalization for certain static text


--check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories
--check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories
if not is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page
if not is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page
if in_array (this_page.nsText, cfg.uncategorized_namespaces) then
if in_array (this_page.nsText, cfg.uncategorized_namespaces) then
Line 2,356: Line 2,348:
end
end


-- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it)
-- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it)
select_one( args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'redundant_parameters' ); -- this is a dummy call simply to get the error message and category
select_one( args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'redundant_parameters' ); -- this is a dummy call simply to get the error message and category


Line 2,379: Line 2,371:
end
end


-- both |publication-place= and |place= (|location=) allowed if different
-- both |publication-place= and |place= (|location=) allowed if different
if not is_set(PublicationPlace) and is_set(Place) then
if not is_set(PublicationPlace) and is_set(Place) then
PublicationPlace = Place; -- promote |place= (|location=) to |publication-place
PublicationPlace = Place; -- promote |place= (|location=) to |publication-place
Line 2,386: Line 2,378:
if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
--[[
--[[
Parameter remapping for cite encyclopedia:
Parameter remapping for cite encyclopedia:
When the citation has these parameters:
When the citation has these parameters:
|encyclopedia and |title then map |title to |article and |encyclopedia to |title
|encyclopedia and |title then map |title to |article and |encyclopedia to |title
|encyclopedia and |article then map |encyclopedia to |title
|encyclopedia and |article then map |encyclopedia to |title
|encyclopedia then map |encyclopedia to |title
|encyclopedia then map |encyclopedia to |title
 
|trans_title maps to |trans_chapter when |title is re-mapped
|trans_title maps to |trans_chapter when |title is re-mapped
|url maps to |chapterurl when |title is remapped
|url maps to |chapterurl when |title is remapped
 
All other combinations of |encyclopedia, |title, and |article are not modified
All other combinations of |encyclopedia, |title, and |article are not modified
 
]]
]]


local Encyclopedia = A['Encyclopedia'];
local Encyclopedia = A['Encyclopedia'];
Line 2,410: Line 2,402:
TransChapter = TransTitle;
TransChapter = TransTitle;
ChapterURL = URL;
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
if not is_set (ChapterURL) and is_set (TitleLink) then
if not is_set (ChapterURL) and is_set (TitleLink) then
Chapter= '[[' .. TitleLink .. '|' .. Chapter .. ']]';
Chapter = make_wikilink (TitleLink, Chapter);
end
end
Title = Periodical;
Title = Periodical;
Line 2,429: Line 2,423:
end
end


-- Special case for cite techreport.
-- Special case for cite techreport.
if (config.CitationClass == "techreport") then -- special case for cite techreport
if (config.CitationClass == "techreport") then -- special case for cite techreport
if is_set(A['Number']) then -- cite techreport uses 'number', which other citations alias to 'issue'
if is_set(A['Number']) then -- cite techreport uses 'number', which other citations alias to 'issue'
Line 2,440: Line 2,434:
end
end


-- special case for cite mailing list
-- special case for cite mailing list
if (config.CitationClass == "mailinglist") then
if (config.CitationClass == "mailinglist") then
Periodical = A ['MailingList'];
Periodical = A ['MailingList'];
Line 2,447: Line 2,441:
end
end


-- Account for the oddity that is {{cite conference}}, before generation of COinS data.
-- Account for the oddity that is {{cite conference}}, before generation of COinS data.
if 'conference' == config.CitationClass then
if 'conference' == config.CitationClass then
if is_set(BookTitle) then
if is_set(BookTitle) then
Line 2,453: Line 2,447:
-- ChapterLink = TitleLink; -- |chapterlink= is deprecated
-- ChapterLink = TitleLink; -- |chapterlink= is deprecated
ChapterURL = URL;
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
ChapterURLorigin = URLorigin;
ChapterURLorigin = URLorigin;
URLorigin = '';
URLorigin = '';
Line 2,467: Line 2,462:
end
end


-- cite map oddities
-- cite map oddities
local Cartography = "";
local Cartography = "";
local Scale = "";
local Scale = "";
Line 2,475: Line 2,470:
Chapter = A['Map'];
Chapter = A['Map'];
ChapterURL = A['MapURL'];
ChapterURL = A['MapURL'];
ChapterUrlAccess = UrlAccess;
TransChapter = A['TransMap'];
TransChapter = A['TransMap'];
ChapterURLorigin = A:ORIGIN('MapURL');
ChapterURLorigin = A:ORIGIN('MapURL');
Line 2,489: Line 2,485:
end
end


-- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data.
-- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data.
if 'episode' == config.CitationClass or 'serial' == config.CitationClass then
if 'episode' == config.CitationClass or 'serial' == config.CitationClass then
local AirDate = A['AirDate'];
local AirDate = A['AirDate'];
Line 2,527: Line 2,523:
TransChapter = TransTitle;
TransChapter = TransTitle;
ChapterURL = URL;
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
ChapterURLorigin = A:ORIGIN('URL');
ChapterURLorigin = A:ORIGIN('URL');
Line 2,534: Line 2,531:


if is_set (ChapterLink) and not is_set (ChapterURL) then -- link but not URL
if is_set (ChapterLink) and not is_set (ChapterURL) then -- link but not URL
Chapter = '[[' .. ChapterLink .. '|' .. Chapter .. ']]'; -- ok to wikilink
Chapter = make_wikilink (ChapterLink, Chapter);
elseif is_set (ChapterLink) and is_set (ChapterURL) then -- if both are set, URL links episode;
elseif is_set (ChapterLink) and is_set (ChapterURL) then -- if both are set, URL links episode;
Series = '[[' .. ChapterLink .. '|' .. Series .. ']]'; -- series links with ChapterLink (episodelink -> TitleLink -> ChapterLink) ugly
Series = make_wikilink (ChapterLink, Series);
end
end
URL = ''; -- unset
URL = ''; -- unset
Line 2,546: Line 2,543:
Chapter = A['Episode']; -- TODO: make |episode= available to cite episode someday?
Chapter = A['Episode']; -- TODO: make |episode= available to cite episode someday?
if is_set (Series) and is_set (SeriesLink) then
if is_set (Series) and is_set (SeriesLink) then
Series = '[[' .. SeriesLink .. '|' .. Series .. ']]';
Series = make_wikilink (SeriesLink, Series);
end
end
Series = wrap_style ('italic-title', Series); -- series is italicized
Series = wrap_style ('italic-title', Series); -- series is italicized
end
end
end
end
-- end of {{cite episode}} stuff
-- end of {{cite episode}} stuff


-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, before generation of COinS data.
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, before generation of COinS data.
do
do
if in_array (config.CitationClass, {'arxiv', 'biorxiv', 'citeseerx'}) then
if in_array (config.CitationClass, {'arxiv', 'biorxiv', 'citeseerx'}) then
Line 2,563: Line 2,560:
Periodical = 'arXiv'; -- set to arXiv for COinS; after that, must be set to empty string
Periodical = 'arXiv'; -- set to arXiv for COinS; after that, must be set to empty string
end
end
if 'biorxiv' == config.CitationClass then
if 'biorxiv' == config.CitationClass then
Periodical = 'bioRxiv'; -- set to bioRxiv for COinS; after that, must be set to empty string
Periodical = 'bioRxiv'; -- set to bioRxiv for COinS; after that, must be set to empty string
end
end
if 'citeseerx' == config.CitationClass then
if 'citeseerx' == config.CitationClass then
Periodical = 'CiteSeerX'; -- set to CiteSeerX for COinS; after that, must be set to empty string
Periodical = 'CiteSeerX'; -- set to CiteSeerX for COinS; after that, must be set to empty string
Line 2,572: Line 2,571:
end
end


-- handle type parameter for those CS1 citations that have default values
-- handle type parameter for those CS1 citations that have default values
if in_array(config.CitationClass, {"AV-media-notes", "interview", "mailinglist", "map", "podcast", "pressrelease", "report", "techreport", "thesis"}) then
if in_array(config.CitationClass, {"AV-media-notes", "interview", "mailinglist", "map", "podcast", "pressrelease", "report", "techreport", "thesis"}) then
TitleType = set_titletype (config.CitationClass, TitleType);
TitleType = set_titletype (config.CitationClass, TitleType);
Line 2,585: Line 2,584:
end
end


-- legacy: promote PublicationDate to Date if neither Date nor Year are set.
-- legacy: promote PublicationDate to Date if neither Date nor Year are set.
if not is_set (Date) then
if not is_set (Date) then
Date = Year; -- promote Year to Date
Date = Year; -- promote Year to Date
Line 2,597: Line 2,596:
if PublicationDate == Date then PublicationDate = ''; end -- if PublicationDate is same as Date, don't display in rendered citation
if PublicationDate == Date then PublicationDate = ''; end -- if PublicationDate is same as Date, don't display in rendered citation


--[[
--[[
Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where
Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where
we get the date used in the metadata.
we get the date used in the metadata.
 
Date validation supporting code is in Module:Citation/CS1/Date_validation
Date validation supporting code is in Module:Citation/CS1/Date_validation
]]
]]
do -- create defined block to contain local variables error_message, date_parameters_list, mismatch
do -- create defined block to contain local variables error_message, date_parameters_list, mismatch
local error_message = '';
local error_message = '';
Line 2,625: Line 2,624:
if not is_set(error_message) then -- error free dates only
if not is_set(error_message) then -- error free dates only
local modified = false; -- flag
local modified = false; -- flag
if is_set (DF) then -- if we need to reformat dates
if is_set (DF) then -- if we need to reformat dates
modified = reformat_dates (date_parameters_list, DF, false); -- reformat to DF format, use long month names if appropriate
modified = reformat_dates (date_parameters_list, DF, false); -- reformat to DF format, use long month names if appropriate
end
end


if true == date_hyphen_to_dash (date_parameters_list) then -- convert hyphens to dashes where appropriate
if true == date_hyphen_to_dash (date_parameters_list) then -- convert hyphens to dashes where appropriate
modified = true;
modified = true;
add_maint_cat ('date_format'); -- hyphens were converted so add maint category
add_maint_cat ('date_format'); -- hyphens were converted so add maint category
end
end
-- for those wikis that can and want to have English date names translated to the local language,
-- uncomment these three lines.  Not supported by en.wiki (for obvious reasons)
-- if date_name_xlate (date_parameters_list) then
-- modified = true;
-- end
if modified then -- if the date_parameters_list values were modified
if modified then -- if the date_parameters_list values were modified
AccessDate = date_parameters_list['access-date']; -- overwrite date holding parameters with modified values
AccessDate = date_parameters_list['access-date']; -- overwrite date holding parameters with modified values
Line 2,647: Line 2,653:
end -- end of do
end -- end of do


-- Account for the oddity that is {{cite journal}} with |pmc= set and |url= not set.  Do this after date check but before COInS.
-- Account for the oddity that is {{cite journal}} with |pmc= set and |url= not set.  Do this after date check but before COInS.
-- Here we unset Embargo if PMC not embargoed (|embargo= not set in the citation) or if the embargo time has expired. Otherwise, holds embargo date
-- Here we unset Embargo if PMC not embargoed (|embargo= not set in the citation) or if the embargo time has expired. Otherwise, holds embargo date
Embargo = is_embargoed (Embargo); --
Embargo = is_embargoed (Embargo);


if config.CitationClass == "journal" and not is_set(URL) and is_set(ID_list['PMC']) then
if config.CitationClass == "journal" and not is_set(URL) and is_set(ID_list['PMC']) then
Line 2,663: Line 2,669:
end
end


-- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.
-- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.
-- Test if citation has no title
-- Test if citation has no title
if not is_set(Title) and
if not is_set(Title) and
Line 2,728: Line 2,734:
}, config.CitationClass);
}, config.CitationClass);


-- Account for the oddities that are {{cite arxiv}}, AFTER generation of COinS data.
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, and {{cite citeseerx}} AFTER generation of COinS data.
-- if 'arxiv' == config.CitationClass then -- we have set rft.jtitle in COinS to arXiv, now unset so it isn't displayed
if in_array (config.CitationClass, {'arxiv', 'biorxiv', 'citeseerx'}) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, or CiteSeerX now unset so it isn't displayed
if in_array (config.CitationClass, {'arxiv', 'biorxiv', 'citeseerx'}) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, or CiteSeerX now unset so it isn't displayed
Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal
Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal
end
end


-- special case for cite newsgroup.  Do this after COinS because we are modifying Publishername to include some static text
-- special case for cite newsgroup.  Do this after COinS because we are modifying Publishername to include some static text
if 'newsgroup' == config.CitationClass then
if 'newsgroup' == config.CitationClass then
if is_set (PublisherName) then
if is_set (PublisherName) then
Line 2,809: Line 2,814:
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,
-- an error message if the associated url is not set, or an empty string for concatenation
-- an error message if the associated url is not set, or an empty string for concatenation
ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url');
ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url');
ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url');
ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url');
Line 2,817: Line 2,822:
TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl');
TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl');


-- special case for chapter format so no error message or cat when chapter not supported
-- special case for chapter format so no error message or cat when chapter not supported
if not (in_array(config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx'}) or
if not (in_array(config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx'}) or
('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia))) then
('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia))) then
Line 2,892: Line 2,897:
end
end


Chapter = format_chapter_title (ScriptChapter, Chapter, TransChapter, ChapterURL, ChapterURLorigin, no_quotes); -- Contribution is also in Chapter
Chapter = format_chapter_title (ScriptChapter, Chapter, TransChapter, ChapterURL, ChapterURLorigin, no_quotes, ChapterUrlAccess); -- Contribution is also in Chapter
if is_set (Chapter) then
if is_set (Chapter) then
Chapter = Chapter .. ChapterFormat ;
Chapter = Chapter .. ChapterFormat ;
Line 2,905: Line 2,910:


-- Format main title.
-- Format main title.
Title = mw.ustring.gsub(Title, '%'..sepc..'$', ''); -- remove any trailing separator character
if is_set(TitleLink) and is_set(Title) then
if is_set(TitleLink) and is_set(Title) then
Title = "[[" .. TitleLink .. "|" .. Title .. "]]"
Title = make_wikilink (TitleLink, Title);
end
end


Line 2,938: Line 2,944:
Title = external_link( URL, Title, URLorigin, UrlAccess ) .. TransTitle .. TransError .. Format;
Title = external_link( URL, Title, URLorigin, UrlAccess ) .. TransTitle .. TransError .. Format;
-- this experiment hidden 2016-04-10; see Help_talk:Citation_Style_1#Recycled_urls
-- local temp_title = external_link( URL, Title, URLorigin ) .. TransError .. Format; -- do this so we get error message even if url is usurped no archive
-- if in_array (DeadURL, {'unfit no archive', 'usurped no archive'}) then -- when url links to inappropriate location and there is no archive of original source available
-- local err_msg
-- if temp_title:match ('%[%S+%s+(.+)%](<.+)') then -- if there is an error message
-- Title, err_msg = temp_title:match ('%[%S+%s+(.+)%](<.+)'); -- strip off external link; TODO: find a better to do this
-- Title = Title .. (err_msg or '');
-- end
-- else
-- Title = temp_title;
-- end
URL = ''; -- unset these because no longer needed
URL = ''; -- unset these because no longer needed
Format = "";
Format = "";
Line 2,955: Line 2,949:
Title = Title .. TransTitle .. TransError;
Title = Title .. TransTitle .. TransError;
end
end
else
Title = TransTitle .. TransError;
end
end


Line 2,996: Line 2,992:
end
end


Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase, Mode);
Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase);


At = is_set(At) and (sepc .. " " .. At) or "";
At = is_set(At) and (sepc .. " " .. At) or "";
Line 3,029: Line 3,025:
if is_set (Translators) then
if is_set (Translators) then
if 'mla' == Mode then
Others = sepc .. ' ' .. wrap_msg ('translated', Translators, use_lowercase) .. Others;
Others = sepc .. ' Trans. ' .. Translators .. Others;
else
Others = sepc .. ' ' .. wrap_msg ('translated', Translators, use_lowercase) .. Others;
end
end
end
if is_set (Interviewers) then
if is_set (Interviewers) then
Line 3,044: Line 3,036:
add_maint_cat ('extra_text', 'edition');
add_maint_cat ('extra_text', 'edition');
end
end
if 'mla' == Mode then
Edition = " " .. wrap_msg ('edition', Edition);
Edition = '. ' .. Edition .. ' ed.';
else
Edition = " " .. wrap_msg ('edition', Edition);
end
else
else
Edition = '';
Edition = '';
Line 3,054: Line 3,042:


Series = is_set(Series) and (sepc .. " " .. Series) or "";
Series = is_set(Series) and (sepc .. " " .. Series) or "";
if 'mla' == Mode then -- not in brackets for mla
OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or ""; -- TODO: presentation
OrigYear = is_set(OrigYear) and (". " .. OrigYear) or "";
 
else
OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or "";
end
Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";
Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";


Volume = format_volume_issue (Volume, Issue, config.CitationClass, Periodical_origin, sepc, use_lowercase, Mode);
Volume = format_volume_issue (Volume, Issue, config.CitationClass, Periodical_origin, sepc, use_lowercase);


------------------------------------ totally unrelated data
------------------------------------ totally unrelated data
Line 3,068: Line 3,053:
end
end


--[[
--[[
Subscription implies paywall; Registration does not.  If both are used in a citation, the subscription required link
Subscription implies paywall; Registration does not.  If both are used in a citation, the subscription required link
note is displayed. There are no error messages for this condition.
note is displayed. There are no error messages for this condition.
 
]]
]]
if is_set (SubscriptionRequired) then
if is_set (SubscriptionRequired) then
SubscriptionRequired = sepc .. " " .. cfg.messages['subscription']; -- subscription required message
SubscriptionRequired = sepc .. " " .. cfg.messages['subscription']; -- subscription required message
Line 3,085: Line 3,070:


AccessDate = nowrap_date (AccessDate); -- wrap in nowrap span if date in appropriate format
AccessDate = nowrap_date (AccessDate); -- wrap in nowrap span if date in appropriate format
if 'mla' == Mode then -- retrieved text not used in mla
if (sepc ~= ".") then retrv_text = retrv_text:lower() end -- if mode is cs2, lower case
AccessDate = ' ' .. AccessDate;
AccessDate = substitute (retrv_text, AccessDate); -- add retrieved text
else
 
if (sepc ~= ".") then retrv_text = retrv_text:lower() end -- if mode is cs2, lower case
AccessDate = substitute (retrv_text, AccessDate); -- add retrieved text
end
AccessDate = substitute (cfg.presentation['accessdate'], {sepc, AccessDate}); -- allow editors to hide accessdates
AccessDate = substitute (cfg.presentation['accessdate'], {sepc, AccessDate}); -- allow editors to hide accessdates
end
end
Line 3,206: Line 3,188:
end
end


--[[
--[[
Handle the oddity that is cite speech.  This code overrides whatever may be the value assigned to TitleNote (through |department=) and forces it to be " (Speech)" so that
Handle the oddity that is cite speech.  This code overrides whatever may be the value assigned to TitleNote (through |department=) and forces it to be " (Speech)" so that
the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided).
the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided).
]]
]]
if "speech" == config.CitationClass then -- cite speech only
if "speech" == config.CitationClass then -- cite speech only
TitleNote = " (Speech)"; -- annotate the citation
TitleNote = " (Speech)"; -- annotate the citation
Line 3,228: Line 3,210:
if in_array(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then
if in_array(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then
if is_set(Others) then Others = Others .. sepc .. " " end
if is_set(Others) then Others = Others .. sepc .. " " end
if 'mla' == Mode then
tcommon = safe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Edition, Publisher, Agency, Volume}, sepc );
tcommon = safe_join( {Conference, Periodical, Format, TitleType, Series, Language, Edition, Publisher, Agency, Volume}, sepc );
else
tcommon = safe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series,  
Language, Edition, Publisher, Agency, Volume}, sepc );
end
elseif in_array(config.CitationClass, {"book","citation"}) and not is_set(Periodical) then -- special cases for book cites
elseif in_array(config.CitationClass, {"book","citation"}) and not is_set(Periodical) then -- special cases for book cites
if is_set (Contributors) then -- when we are citing foreword, preface, introduction, etc
if is_set (Contributors) then -- when we are citing foreword, preface, introduction, etc
tcommon = safe_join( {Title, TitleNote}, sepc ); -- author and other stuff will come after this and before tcommon2
tcommon = safe_join( {Title, TitleNote}, sepc ); -- author and other stuff will come after this and before tcommon2
if 'mla' == Mode then
tcommon2 = safe_join( {Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc );
tcommon2 = safe_join( {Conference, Periodical, Format, TitleType, Series, Language, Volume, Edition, Publisher, Agency}, sepc );
else
tcommon2 = safe_join( {Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc );
end
elseif 'mla' == Mode then
tcommon = safe_join( {TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Publisher, Agency}, sepc );
else
else
tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc );
tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc );
Line 3,259: Line 3,230:
elseif 'episode' == config.CitationClass then -- special case for cite episode
elseif 'episode' == config.CitationClass then -- special case for cite episode
tcommon = safe_join( {Title, TitleNote, TitleType, Series, Transcript, Language, Edition, Publisher}, sepc );
tcommon = safe_join( {Title, TitleNote, TitleType, Series, Transcript, Language, Edition, Publisher}, sepc );
elseif ('news' == config.CitationClass) and ('mla' == Mode) then -- special case for cite news in MLA mode
tcommon = safe_join( {Periodical, Format, TitleType, Series, Language, Edition, Agency}, sepc );
elseif ('web' == config.CitationClass) and ('mla' == Mode) then -- special case for cite web in MLA mode
tcommon = safe_join( {Periodical, Format, TitleType, Series, Language,
Edition, Publisher, Agency}, sepc );


else -- all other CS1 templates
else -- all other CS1 templates
Line 3,283: Line 3,247:


if is_set(Date) then
if is_set(Date) then
if ('mla' == Mode) then
if is_set (Authors) or is_set (Editors) then -- date follows authors or editors when authors not set
if in_array (config.CitationClass, {'book', 'news', 'web'}) then
Date = ', ' .. Date; -- origyear follows title in mla
elseif 'journal' == config.CitationClass then
Date = ', (' .. Date .. ')';
end
elseif is_set (Authors) or is_set (Editors) then -- date follows authors or editors when authors not set
Date = " (" .. Date ..")" .. OrigYear .. sepc .. " "; -- in paranetheses
Date = " (" .. Date ..")" .. OrigYear .. sepc .. " "; -- in paranetheses
else -- neither of authors and editors set
else -- neither of authors and editors set
Line 3,300: Line 3,258:
end
end
if is_set(Authors) then
if is_set(Authors) then
if (not is_set (Date)) or ('mla' == Mode) then -- when date is set it's in parentheses; no Authors termination
if (not is_set (Date)) then -- when date is set it's in parentheses; no Authors termination
Authors = terminate_name_list (Authors, sepc); -- when no date, terminate with 0 or 1 sepc and a space
Authors = terminate_name_list (Authors, sepc); -- when no date, terminate with 0 or 1 sepc and a space
end
end
Line 3,306: Line 3,264:
local in_text = " ";
local in_text = " ";
local post_text = "";
local post_text = "";
if is_set(Chapter) and 0 == #c and 'mla' ~= Mode then
if is_set(Chapter) and 0 == #c then
in_text = in_text .. cfg.messages['in'] .. " "
in_text = in_text .. cfg.messages['in'] .. " "
if (sepc ~= '.') then in_text = in_text:lower() end -- lowercase for cs2
if (sepc ~= '.') then
elseif is_set(Chapter) and 'mla' == Mode then
in_text = in_text:lower() -- lowercase for cs2
if EditorCount <= 1 then
end
in_text = '. Ed. ';
else
in_text = '. Eds. ';
end
else
else
if EditorCount <= 1 then
if EditorCount <= 1 then
Line 3,328: Line 3,282:
if (sepc ~= '.') then by_text = by_text:lower() end -- lowercase for cs2
if (sepc ~= '.') then by_text = by_text:lower() end -- lowercase for cs2
Authors = by_text .. Authors; -- author follows title so tweak it here
Authors = by_text .. Authors; -- author follows title so tweak it here
if is_set (Editors) and is_set (Date) and ('mla' ~= Mode) then -- when Editors make sure that Authors gets terminated
if is_set (Editors) and is_set (Date) then -- when Editors make sure that Authors gets terminated
Authors = terminate_name_list (Authors, sepc); -- terminate with 0 or 1 sepc and a space
Authors = terminate_name_list (Authors, sepc); -- terminate with 0 or 1 sepc and a space
end
end
if (not is_set (Date)) or ('mla' == Mode) then -- when date is set it's in parentheses; no Contributors termination
if (not is_set (Date)) then -- when date is set it's in parentheses; no Contributors termination
Contributors = terminate_name_list (Contributors, sepc); -- terminate with 0 or 1 sepc and a space
Contributors = terminate_name_list (Contributors, sepc); -- terminate with 0 or 1 sepc and a space
end
end
if 'mla' == Mode then
text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc );
text = safe_join( {Contributors, Chapter, tcommon, OrigYear, Authors, Place, Others, Editors, tcommon2, Date, pgtext, idcommon }, sepc );
else
text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc );
end
elseif 'mla' == Mode then
tcommon = tcommon .. Date; -- hack to avoid duplicate separators
text = safe_join( {Authors, Chapter, Title, OrigYear, Others, Editors, Edition, Place, tcommon, pgtext, idcommon }, sepc );
else
else
text = safe_join( {Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc );
text = safe_join( {Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc );
Line 3,359: Line 3,306:
end
end
end
end
if 'mla' == Mode then
text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc );
if in_array(config.CitationClass, {'journal', 'news', 'web'}) and is_set(Periodical) then
text = safe_join( {Editors, Title, Place, tcommon, pgtext, Date, idcommon}, sepc );
else
text = safe_join( {Editors, Chapter, Title, Place, tcommon, Date, pgtext, idcommon}, sepc );
end
else
text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc );
end
elseif 'mla' == Mode then
if in_array(config.CitationClass, {'journal', 'news', 'web'}) and is_set(Periodical) then
text = safe_join( {Title, Place, tcommon, pgtext, Date, idcommon}, sepc );
else
text = safe_join( {Chapter, Title, Place, tcommon, Date, pgtext, idcommon}, sepc );
end
else
else
if in_array(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then
if in_array(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then
Line 3,427: Line 3,360:
end
end
local render = {}; -- here we collect the final bits for concatenation into the rendered citation
if is_set(options.id) then -- here we wrap the rendered citation in <cite ...>...</cite> tags
if is_set(options.id) then -- here we wrap the rendered citation in <cite ...>...</cite> tags
text = substitute (cfg.presentation['cite-id'], {mw.uri.anchorEncode(options.id), mw.text.nowiki(options.class), text}); -- when |ref= is set
table.insert (render, substitute (cfg.presentation['cite-id'], {mw.uri.anchorEncode(options.id), mw.text.nowiki(options.class), text})); -- when |ref= is set
else
else
text = substitute (cfg.presentation['cite'], {mw.text.nowiki(options.class), text}); -- all other cases
table.insert (render, substitute (cfg.presentation['cite'], {mw.text.nowiki(options.class), text})); -- all other cases
end
end


text = text .. substitute (cfg.presentation['ocins'], {OCinSoutput}); -- append metadata to the citation
table.insert (render, substitute (cfg.presentation['ocins'], {OCinSoutput})); -- append metadata to the citation
 
if #z.message_tail ~= 0 then
if #z.message_tail ~= 0 then
text = text .. " ";
table.insert (render, ' ');
for i,v in ipairs( z.message_tail ) do
for i,v in ipairs( z.message_tail ) do
if is_set(v[1]) then
if is_set(v[1]) then
if i == #z.message_tail then
if i == #z.message_tail then
text = text .. error_comment( v[1], v[2] );
table.insert (render, error_comment( v[1], v[2] ));
else
else
text = text .. error_comment( v[1] .. "; ", v[2] );
table.insert (render, error_comment( v[1] .. "; ", v[2] ));
end
end
end
end
Line 3,449: Line 3,384:


if #z.maintenance_cats ~= 0 then
if #z.maintenance_cats ~= 0 then
text = text .. '<span class="citation-comment" style="display:none; color:#33aa33; margin-left:0.3em">';
table.insert (render, '<span class="citation-comment" style="display:none; color:#33aa33; margin-left:0.3em">');
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]])';
table.insert (render, v);
table.insert (render, ' (');
table.insert (render, make_wikilink (':Category:' .. v, 'link'));
table.insert (render, ') ');
end
end
text = text .. '</span>'; -- maintenance mesages (realy just the names of the categories for now)
table.insert (render, '</span>');
end
end
Line 3,459: Line 3,397:
if in_array(no_tracking_cats, {"", "no", "false", "n"}) then
if in_array(no_tracking_cats, {"", "no", "false", "n"}) then
for _, v in ipairs( z.error_categories ) do
for _, v in ipairs( z.error_categories ) do
text = text .. '[[Category:' .. v ..']]';
table.insert (render, make_wikilink ('Category:' .. v));
end
end
for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories
for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories
text = text .. '[[Category:' .. v ..']]';
table.insert (render, make_wikilink ('Category:' .. v));
end
end
for _, v in ipairs( z.properties_cats ) do -- append maintenance categories
for _, v in ipairs( z.properties_cats ) do -- append properties categories
text = text .. '[[Category:' .. v ..']]';
table.insert (render, make_wikilink ('Category:' .. v));
end
end
end
end
 
return text
return table.concat (render);
end
end


Line 3,503: Line 3,441:
utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the cfg tables
utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the cfg tables
identifiers.set_selected_modules (cfg, utilities); -- so that functions in Identifiers can see the selected cfg tables and selected Utilities module
identifiers.set_selected_modules (cfg, utilities); -- so that functions in Identifiers can see the selected cfg tables and selected Utilities module
validation.set_selected_modules (utilities); -- so that functions in Date validataion can see the selected Utilities module
validation.set_selected_modules (cfg, utilities); -- so that functions in Date validataion can see selected cfg tables and the selected Utilities module
metadata.set_selected_modules (cfg, utilities); -- so that functions in COinS can see the selected cfg tables and selected Utilities module
metadata.set_selected_modules (cfg, utilities); -- so that functions in COinS can see the selected cfg tables and selected Utilities module


Line 3,510: Line 3,448:
reformat_dates = validation.reformat_dates;
reformat_dates = validation.reformat_dates;
date_hyphen_to_dash = validation.date_hyphen_to_dash;
date_hyphen_to_dash = validation.date_hyphen_to_dash;
date_name_xlate = validation.date_name_xlate;
 
is_set = utilities.is_set; -- imported functions from Module:Citation/CS1/Utilities
is_set = utilities.is_set; -- imported functions from Module:Citation/CS1/Utilities
in_array = utilities.in_array;
in_array = utilities.in_array;
Line 3,520: Line 3,459:
wrap_style = utilities.wrap_style;
wrap_style = utilities.wrap_style;
safe_for_italics = utilities.safe_for_italics;
safe_for_italics = utilities.safe_for_italics;
remove_wiki_link = utilities.remove_wiki_link;
is_wikilink = utilities.is_wikilink;
make_wikilink = utilities.make_wikilink;


z = utilities.z; -- table of error and category tables in Module:Citation/CS1/Utilities
z = utilities.z; -- table of error and category tables in Module:Citation/CS1/Utilities
Anonymous user