Module:Citation/CS1: Difference between revisions

    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