Module:Citation/CS1: Difference between revisions

    m>Dragons flight
    m (Changed protection level of Module:Citation/CS1: Remove cascading, doesn't work like as you would expect for Modules/Templates ([Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite)))
    m>Trappist the monk
    (Synch from sandbox;)
    Line 1: Line 1:
    local z = {
    local z = {
    error_categories = {};
    error_categories = {}; -- for categorizing citations that contain errors
    error_ids = {};
    error_ids = {};
    message_tail = {};
    message_tail = {};
    maintenance_cats = {}; -- for categorizing citations that aren't erroneous per se, but could use a little work
    properties_cats = {}; -- for categorizing citations based on certain properties, language of source for instance
    }
    }


    Line 41: Line 44:
    if true ~= Page_in_deprecated_cat then -- if we haven't been here before then set a  
    if true ~= Page_in_deprecated_cat then -- if we haven't been here before then set a  
    Page_in_deprecated_cat=true; -- sticky flag so that if there are more than one deprecated parameter the category is added only once
    Page_in_deprecated_cat=true; -- sticky flag so that if there are more than one deprecated parameter the category is added only once
    -- table.insert( z.message_tail, { seterror( 'deprecated_params', {error_message}, true ) } ); -- add error message
    table.insert( z.message_tail, { seterror( 'deprecated_params', {}, true ) } ); -- add error message
    table.insert( z.message_tail, { seterror( 'deprecated_params', {}, true ) } ); -- add error message
    end
    end
    Line 69: Line 71:
    end
    end
    return str;
    return str;
    end
    --[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >----------------------------------------
    |script-title= holds title parameters that are not written in Latin based scripts: Chinese, Japanese, Arabic, Hebrew, etc. These scripts should
    not be italicized and may be written right-to-left.  The value supplied by |script-title= is concatenated onto Title after Title has been wrapped
    in italic markup.
    Regardless of language, all values provided by |script-title= are wrapped in <bdi>...</bdi> tags to isolate rtl languages from the English left to right.
    |script-title= provides a unique feature.  The value in |script-title= may be prefixed with a two-character ISO639-1 language code and a colon:
    |script-title=ja:*** *** (where * represents a Japanese character)
    Spaces between the two-character code and the colon and the colon and the first script character are allowed:
    |script-title=ja : *** ***
    |script-title=ja: *** ***
    |script-title=ja :*** ***
    Spaces preceding the prefix are allowed: |script-title = ja:*** ***
    The prefix is checked for validity.  If it is a valid ISO639-1 language code, the lang attribute (lang="ja") is added to the <bdi> tag so that browsers can
    know the language the tag contains.  This may help the browser render the script more correctly.  If the prefix is invalid, the lang attribute
    is not added.  At this time there is no error message for this condition.
    At this writing, only |script-title= is supported.  It is anticipated that additional parameters will be created to use this function.
    TODO: error messages when prefix is invalid ISO639-1 code; when script_value has prefix but no script;
    ]]
    function format_script_value (script_value)
    local lang=''; -- initialize to empty string
    local name;
    if script_value:match('^%l%l%s*:') then -- if first 3 non-space characters are script language prefix
    lang = script_value:match('^(%l%l)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script
    if not is_set (lang) then
    return ''; -- script_value was just the prefix so return empty string
    end
    -- if we get this far we have prefix and script
    name = mw.language.fetchLanguageName( lang, "en" ); -- get language name so that we can use it to categorize
    if is_set (name) then -- is prefix a proper ISO 639-1 language code?
    script_value = script_value:gsub ('^%l%l%s*:%s*', ''); -- strip prefix from script
    if inArray (lang, {'ar', 'ja', 'ko', 'he', 'zh'}) then -- is prefix one of these language codes?
    table.insert( z.properties_cats, 'CS1 uses ' .. name .. '-language script ('..lang..')'); -- categorize in language-specific categories
    else
    table.insert( z.properties_cats, 'CS1 uses foreign language script'); -- use this category as a catchall until language-specific category is available
    end
    lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute
    else
    lang = ''; -- invalid so set lang to empty string
    end
    end
    script_value = '<bdi' .. lang .. '>' .. script_value .. '</bdi>'; -- isolate incase script is rlt
    return script_value;
    end
    --[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------
    Initially for |title= and |script-title=, this function concatenates those two parameter values after the script value has been
    wrapped in <bdi> tags.
    ]]
    function script_concatenate (title, script)
    if is_set (script) then
    script = format_script_value (script); -- <bdi> tags, lang atribute, categorization, etc; returns empty string on error
    if is_set (script) then
    title = title .. ' ' .. script; -- concatenate title and script title
    end
    end
    return title;
    end
    end


    Line 246: Line 316:
    -- Formats a link to Amazon
    -- Formats a link to Amazon
    function amazon(id, domain)
    function amazon(id, domain)
    if checkisbn( id ) then -- see if asin value is a 10-digit isbn
    table.insert( z.maintenance_cats, "CS1 maint: ASIN uses ISBN"); -- add to maint category so a bot or awb script can replace |asin= with |isbn=
    end
    if not is_set(domain) then  
    if not is_set(domain) then  
    domain = "com"
    domain = "com";
    elseif ( "jp" == domain or "uk" == domain ) then
    elseif inArray (domain, {'jp', 'uk'}) then -- Japan, United Kingdom
    domain = "co." .. domain
    domain = "co." .. domain;
    elseif inArray (domain, {'au', 'br', 'mx'}) then -- Australia, Brazil, Mexico
    domain = "com." .. domain;
    end
    end
    local handler = cfg.id_handlers['ASIN'];
    local handler = cfg.id_handlers['ASIN'];
    Line 347: Line 422:


    length = 8 then all digits
    length = 8 then all digits
    length = 9 then lccn[1] is alpha
    length = 9 then lccn[1] is lower case alpha
    length = 10 then lccn[1] and lccn[2] are both alpha or both digits
    length = 10 then lccn[1] and lccn[2] are both lower case alpha or both digits
    length = 11 then lccn[1] is alpha, lccn[2] and lccn[3] are both alpha or both digits
    length = 11 then lccn[1] is lower case alpha, lccn[2] and lccn[3] are both lower case alpha or both digits
    length = 12 then lccn[1] and lccn[2] are both alpha
    length = 12 then lccn[1] and lccn[2] are both lower case alpha


    ]]
    ]]
    Line 366: Line 441:
    end
    end
    elseif 9 == len then -- LCCN should be adddddddd
    elseif 9 == len then -- LCCN should be adddddddd
    if nil == id:match("%a%d%d%d%d%d%d%d%d") then -- does it match our pattern?
    if nil == id:match("%l%d%d%d%d%d%d%d%d") then -- does it match our pattern?
    err_cat = ' ' .. seterror( 'bad_lccn' ); -- set an error message
    err_cat = ' ' .. seterror( 'bad_lccn' ); -- set an error message
    end
    end
    elseif 10 == len then -- LCCN should be aadddddddd or dddddddddd
    elseif 10 == len then -- LCCN should be aadddddddd or dddddddddd
    if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ...
    if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ...
    if nil == id:match("^%a%a%d%d%d%d%d%d%d%d") then -- ... see if it matches our pattern
    if nil == id:match("^%l%l%d%d%d%d%d%d%d%d") then -- ... see if it matches our pattern
    err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
    err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
    end
    end
    end
    end
    elseif 11 == len then -- LCCN should be aaadddddddd or adddddddddd
    elseif 11 == len then -- LCCN should be aaadddddddd or adddddddddd
    if not (id:match("^%a%a%a%d%d%d%d%d%d%d%d") or id:match("^%a%d%d%d%d%d%d%d%d%d%d")) then -- see if it matches one of our patterns
    if not (id:match("^%l%l%l%d%d%d%d%d%d%d%d") or id:match("^%l%d%d%d%d%d%d%d%d%d%d")) then -- see if it matches one of our patterns
    err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
    err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
    end
    end
    elseif 12 == len then -- LCCN should be aadddddddddd
    elseif 12 == len then -- LCCN should be aadddddddddd
    if not id:match("^%a%a%d%d%d%d%d%d%d%d%d%d") then -- see if it matches our pattern
    if not id:match("^%l%l%d%d%d%d%d%d%d%d%d%d") then -- see if it matches our pattern
    err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
    err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
    end
    end
    Line 510: Line 585:
    -- Formats an OpenLibrary link, and checks for associated errors.
    -- Formats an OpenLibrary link, and checks for associated errors.
    function openlibrary(id)
    function openlibrary(id)
    local code = id:sub(-1,-1)
    local code = id:match("^%d+([AMW])$"); -- only digits followed by 'A', 'M', or 'W'
    local handler = cfg.id_handlers['OL'];
    local handler = cfg.id_handlers['OL'];
    if ( code == "A" ) then
    if ( code == "A" ) then
    return externallinkid({link=handler.link, label=handler.label,
    return externallinkid({link=handler.link, label=handler.label,
    Line 620: Line 696:
    end
    end


    -- Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS.
     
    --TODO: Fix so this code supports urls like this:
    --[[
    -- http://www.history.navy.mil/download/va125153.pdf#page=13 %w/:\.
    Strip wiki italic and bold markup from argument so that it doesn't contaminate COinS metadata
    function get_coins_pages (pages)
    This function strips common patterns of apostrophe markup.  We presume that editors who have taken the time to
    markup a title have, as a result, provided valid markup.
    ]]
    function strip_apostrophe_markup (argument)
    local pattern, c1, c2;
    if not is_set (argument) then return argument; end
    while true do -- look for and remove all 5-apostrophe (bold and italic) markup
    if argument:match ("%'%'%'%'%'") then -- is there an instance of bold-italic?
    if argument:match ("%'%'%'%'%'.*%'%'%'%'%'") then -- 5, 5
    pattern, c1 = argument:match ("(%'%'%'%'%'(.*)%'%'%'%'%')");
    c2 = ""; -- set to empty string so we do only one replacement at end
    elseif argument:match ("%'%'%'%'%'.*%'%'%'.*%'%'") then -- bold italic followed by italic (5, 3, 2)
    pattern, c1, c2 = argument:match ("(%'%'%'%'%'(.*)%'%'%'(.*)%'%')");
    elseif argument:match ("%'%'%'%'%'.*%'%'.*%'%'%'") then -- bold italic followed by bold (5, 2, 3)
    pattern, c1, c2 = argument:match ("(%'%'%'%'%'(.*)%'%'(.*)%'%'%')");
    elseif argument:match ("%'%'%'.*%'%'.*%'%'%'%'%'") then -- bold italic followed by italic (3, 2, 5)
    pattern, c1, c2 = argument:match ("(%'%'%'(.*)%'%'(.*)%'%'%'%'%')");
    elseif argument:match ("%'%'.*%'%'%'.*%'%'%'%'%'") then -- italic followed by bold (2, 3, 5)
    pattern, c1, c2 = argument:match ("(%'%'(.*)%'%'%'(.*)%'%'%'%'%')");
    end
    argument=argument:gsub(pattern, c1..c2); -- remove the markup
    else
    break; -- none or no more 5-apostrophe matches
    end
    end
    while true do -- look for and remove all 3-apostrophe (bold) markup
    if argument:match ("%'%'%'.*%'%'%'") then -- is there an instance of bold?
    pattern, c1 = argument:match ("(%'%'%'(.*)%'%'%')")
    argument=argument:gsub(pattern, c1); -- remove the markup
    else
    break; -- none or no more 3 matches
    end
    end
    while true do -- look for and remove all 2-apostrophe (italic) markup
    if argument:match ("%'%'.*%'%'") then -- is there an instance of italic?
    pattern, c1 = argument:match ("(%'%'(.*)%'%')")
    argument=argument:gsub(pattern, c1); -- remove the markup
    else
    break; -- none or no more 2 matches
    end
    end
    return argument; -- done
    end
     
    --[[--------------------------< M A K E _ C O I N S _ T I T L E >----------------------------------------------
     
    Makes a title for COinS from Title and / or ScriptTitle (or any other name-script pairs)
     
    Apostrophe markup (bold, italics) is stripped from each value so that the COinS metadata isn't correupted with strings
    of %27%27...
    ]]
     
    function make_coins_title (title, script)
    if is_set (title) then
    title = strip_apostrophe_markup (title); -- strip any apostrophe markup
    else
    title=''; -- if not set, make sure title is an empty string
    end
    if is_set (script) then
    script = script:gsub ('^%l%l%s*:%s*', ''); -- remove language prefix if present (script value may now be empty string)
    script = strip_apostrophe_markup (script); -- strip any apostrophe markup
    else
    script=''; -- if not set, make sure script is an empty string
    end
    if is_set (title) and is_set (script) then
    script = ' ' .. script; -- add a space before we concatenate
    end
    return title .. script; -- return the concatenation
    end
     
    -- Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS.
    function get_coins_pages (pages)
    local pattern;
    if not is_set (pages) then return pages; end -- if no page numbers then we're done
    if not is_set (pages) then return pages; end -- if no page numbers then we're done
    Line 851: Line 1,000:
    one = one .. namesep .. first  
    one = one .. namesep .. first  
    end
    end
    if is_set(person.link) then one = "[[" .. person.link .. "|" .. one .. "]]" end
    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
    end
     
    if is_set(person.link) and nil ~= person.link:find("//") then one = one .. " " .. seterror( 'bad_authorlink' ) end -- check for url in author link;
    if is_set(person.link) and nil ~= person.link:find("//") then one = one .. " " .. seterror( 'bad_authorlink' ) end -- check for url in author link;
    end
    end
    Line 895: Line 1,047:
    are present but |last2= is missing, an error message is emitted. |lastn= is not required to have a matching |firstn=.
    are present but |last2= is missing, an error message is emitted. |lastn= is not required to have a matching |firstn=.
    ]]
    ]]
    --Original function
    function extractnames(args, list_name)
    function extractnames(args, list_name)
        local names = {};
    local names = {}; -- table of names
        local i = 1;
    local last; -- individual name components
        local last;
    local first;
    local link;
        while true do
    local mask;
            last = selectone( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i );
            if not is_set(last) then
                -- just in case someone passed in an empty parameter
                break;
            end
            names[i] = {
                last = last,
                first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ),
                link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ),
                mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i )
            };
            i = i + 1;
        end
        return names;
    end
     
    --[[ Broken.  Editor CITEREF IDs are broken by this code (no editor list). Author CITEREF ID render correctly
    function extractnames(args, list_name)
    local names = {}; -- table of names
    local i = 1; -- loop counter/indexer
    local i = 1; -- loop counter/indexer
    local n = 1; -- output table indexer
    local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors)
    local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors)
    Line 927: Line 1,060:


    while true do
    while true do
    names[i] = -- search through args for name components beginning at 1
    last = selectone( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i ); -- search through args for name components beginning at 1
    {
    first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i );
    last = selectone( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i ),
    link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i );
    first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ),
    mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i );
    link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ),
     
    mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i )
    if first and not last then -- if there is a firstn without a matching lastn
    };
    if names[i].first and not names[i].last then -- if there is a firstn without a matching lastn
    names[i].first = nil; -- set first to nil so we don't confuse the implicit et al message code
    table.insert( z.message_tail, { seterror( 'first_missing_last', {err_msg_list_name, i}, true ) } ); -- add this error message
    table.insert( z.message_tail, { seterror( 'first_missing_last', {err_msg_list_name, i}, true ) } ); -- add this error message
    break; -- and done because lastn not found
    elseif not first and not last then -- if both firstn and lastn aren't found, are we done?
    elseif not names[i].first and not names[i].last then -- if both firstn and lastn aren't found, are we done?
    count = count + 1; -- number of times we haven't found last and first
    count = count + 1; -- number of times we haven't found last and first
    if 2 == count then -- two missing names and we give up
    if 2 == count then -- two missing names and we give up
    break; -- normal exit or there is a two-name hole in the list; can't tell which
    break; -- normal exit or there is a two-name hole in the list; can't tell which
    end
    end
    else -- last with or without a first
    else -- we have last with or without a first
    names[n] = {last = last, first = first, link = link, mask = mask}; -- add this name to our names list
    n = n + 1; -- point to next location in the names table
    if 1 == count then -- if the previous name was missing
    if 1 == count then -- if the previous name was missing
    table.insert( z.message_tail, { seterror( 'missing_name', {err_msg_list_name, i-1}, true ) } ); -- add this error message
    table.insert( z.message_tail, { seterror( 'missing_name', {err_msg_list_name, i-1}, true ) } ); -- add this error message
    Line 949: Line 1,080:
    count = 0; -- reset the counter, we're looking for two consecutive missing names
    count = 0; -- reset the counter, we're looking for two consecutive missing names
    end
    end
    i = i + 1; -- bump to the next name
    i = i + 1; -- point to next args location
    end
    end
    return names; -- all done, return our list of names
    return names; -- all done, return our list of names
    end
    end
    ]]


    -- Populates ID table from arguments using configuration settings
    -- Populates ID table from arguments using configuration settings
    Line 1,159: Line 1,287:
    end
    end


    --[[
     
    This is the main function doing the majority of the citation
    --[[--------------------------< G E T _ I S O 6 3 9 _ C O D E >------------------------------------------------
    formatting.
     
    Validates language names provided in |language= parameter if not an ISO639-1 code.  Handles the special case that is Norwegian where
    ISO639-1 code 'no' is mapped to language name 'Norwegian Bokmål' by Extention:CLDR.
     
    Returns the language name and associated ISO639-1 code.  Because case of the source may be incorrect or different from the case that Wikimedia
    uses, the name comparisons are done in lower case and when a match is found, the Wikimedia version (assumed to be correct) is returned along
    with the code.  When there is no match, we return the original language name string.
     
    mw.language.fetchLanguageNames() will return a list of languages that aren't part of ISO639-1. Names that aren't ISO639-1 but that are included
    in the list will be found if that name is provided in the |language= parameter.  For example, if |language=Samaritan Aramaic, that name will be
    found with the associated code 'sam', not an ISO639-1 code.  When names are found and the associated code is not two characters, this function
    returns only the Wikimedia language name.
     
    Adapted from code taken from Module:Check ISO 639-1.
    ]]
    ]]
    function citation0( config, args)
    --[[
    Load Input Parameters
    The argument_wrapper facilitates the mapping of multiple
    aliases to single internal variable.
    ]]
    local A = argument_wrapper( args );


    local i
    function get_iso639_code (lang)
    local PPrefix = A['PPrefix']
    if 'Norwegian' == lang then
    local PPPrefix = A['PPPrefix']
    return lang, 'no'; -- special case related to Wikimedia remap of code 'no' at Extension:CLDR
    if is_set( A['NoPP'] ) then PPPrefix = "" PPrefix = "" end
    end
    local languages = mw.language.fetchLanguageNames('en', 'all') -- get a list of language names known to Wikimedia
    -- ('all' is required for North Ndebele, South Ndebele, and Ojibwa)
    local langlc = mw.ustring.lower(lang); -- lower case version for comparisons
    -- Pick out the relevant fields from the arguments.  Different citation templates
    for code, name in pairs(languages) do -- scan the list to see if we can find our language
    -- define different field names for the same underlying things.
    if langlc == mw.ustring.lower(name) then
    local Authors = A['Authors'];
    if 2 ~= code:len() then -- ISO639-1 codes only
    local a = extractnames( args, 'AuthorList' );
    return name; -- so return the name but not the code
     
    end
    local Coauthors = A['Coauthors'];
    return name, code; -- found it, return name to ensure proper capitalization and the ISO639-1 code
    local Others = A['Others'];
    end
    local Editors = A['Editors'];
    end
    local e = extractnames( args, 'EditorList' );
    return lang; -- not valid language; return language in original case and nil for ISO639-1 code
     
    end
    local Year = A['Year'];
     
    local PublicationDate = A['PublicationDate'];
    --[[--------------------------< L A N G U A G E _ P A R A M E T E R >------------------------------------------
    local OrigYear = A['OrigYear'];
     
    local Date = A['Date'];
    Get language name from ISO639-1 code value provided.  If a code is valid use the returned name; if not, then use the value that was provided with the language parameter.
    local LayDate = A['LayDate'];
     
    ------------------------------------------------- Get title data
    There is an exception.  There are three ISO639-1 codes for Norewegian language variants.  There are two official variants: Norwegian Bokmål (code 'nb') and
    local Title = A['Title'];
    Norwegian Nynorsk (code 'nn').  The third, code 'no',  is defined by ISO639-1 as 'Norwegian' though in Norway this is pretty much meaningless.  However, it appears
    local BookTitle = A['BookTitle'];
    that on enwiki, editors are for the most part unaware of the nb and nn variants (compare page counts for these variants at Category:Articles with non-English-language external links.
    local Conference = A['Conference'];
     
    local TransTitle = A['TransTitle'];
    Because Norwegian Bokmål is the most common language variant, Media wiki has been modified to return Norwegian Bokmål for ISO639-1 code 'no'. Here we undo that and
    local TitleNote = A['TitleNote'];
    return 'Norwegian' when editors use |language=no.  We presume that editors don't know about the variants or can't descriminate between them.
    local TitleLink = A['TitleLink'];
     
    See Help talk:Citation Style_1#An ISO 639-1 language name test
     
    When |language= contains a valid ISO639-1 code, the page is assigned to the category for that code: Category:Norwegian-language sources (no) if
    the page is a mainspace page and the ISO639-1 code is not 'en'.  Similarly, if the  parameter is |language=Norwegian, it will be categorized in the same way.
     
    TODO: Error message when language or language code is not ISO639-1?
    ]]
     
    function language_parameter (lang, namespace)
    local code; -- the ISO639-1 two character code
    local name; -- the language name
    local test='';
    if 0 == namespace and (('en' == lang:lower()) or ('english' == lang:lower())) then
    table.insert (z.maintenance_cats, 'CS1 maint: English language specified'); -- add maintenance category if |language=English or |language=en in article space
    end
     
    if 2 == lang:len() then -- ISO639-1 language code are 2 characters (fetchLanguageName also supports 3 character codes)
    name = mw.language.fetchLanguageName( lang:lower(), "en" ); -- get ISO 639-1 language name if Language is a proper code
    end
     
    if is_set (name) then -- if Language specified a valid ISO639-1 code
    code = lang:lower(); -- save it
    else
    name, code = get_iso639_code (lang); -- attempt to get code from name (assign name here so that we are sure of proper capitalization)
    end
     
    if is_set (code) then
    if 'no' == code then name = 'Norwegian' end; -- override wikimedia when code is 'no'
    if 0 == namespace and 'en' ~= code then -- is this page main / article space and English not the language?
    table.insert( z.properties_cats, 'CS1 ' .. name .. '-language sources (' .. code .. ')'); -- in main space and not English: categorize
    end
    end
    return (" " .. wrap( 'language', name)); -- wrap with '(in ...)'
    end
     
     
     
     
    --[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------
    This is the main function doing the majority of the citation
    formatting.
    ]]
    function citation0( config, args)
    --[[
    Load Input Parameters
    The argument_wrapper facilitates the mapping of multiple
    aliases to single internal variable.
    ]]
    local A = argument_wrapper( args );
     
    local i
    local PPrefix = A['PPrefix']
    local PPPrefix = A['PPPrefix']
    if is_set( A['NoPP'] ) then PPPrefix = "" PPrefix = "" end
    -- Pick out the relevant fields from the arguments.  Different citation templates
    -- define different field names for the same underlying things.
    local Authors = A['Authors'];
    local a = extractnames( args, 'AuthorList' );
     
    local Coauthors = A['Coauthors'];
    local Others = A['Others'];
    local Editors = A['Editors'];
    local e = extractnames( args, 'EditorList' );
     
    local Year = A['Year'];
    local PublicationDate = A['PublicationDate'];
    local OrigYear = A['OrigYear'];
    local Date = A['Date'];
    local LayDate = A['LayDate'];
    ------------------------------------------------- Get title data
    local Title = A['Title'];
    local ScriptTitle = A['ScriptTitle'];
    local BookTitle = A['BookTitle'];
    local Conference = A['Conference'];
    local TransTitle = A['TransTitle'];
    local TitleNote = A['TitleNote'];
    local TitleLink = A['TitleLink'];
    local Chapter = A['Chapter'];
    local Chapter = A['Chapter'];
    local ChapterLink = A['ChapterLink'];
    local ChapterLink = A['ChapterLink'];
    Line 1,291: Line 1,508:
    --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
    for k, v in pairs( cfg.uncategorized_namespaces ) do -- otherwise, spin through the list of namespaces we don't include in error categories
    if inArray (this_page.nsText, cfg.uncategorized_namespaces) then
    if this_page.nsText == v then -- if we find one
    no_tracking_cats = "true"; -- set no_tracking_cats
    no_tracking_cats = "true"; -- set no_tracking_cats
    break; -- and we're done
    end
    end
    end
    end
    end
    Line 1,500: Line 1,714:
    -- COinS metadata (see <http://ocoins.info/>) for
    -- COinS metadata (see <http://ocoins.info/>) for
    -- automated parsing of citation information.
    -- automated parsing of citation information.
    -- this is the function call to COinS()
    local OCinSoutput = COinS{
    local OCinSoutput = COinS{
    ['Periodical'] = Periodical,
    ['Periodical'] = Periodical,
    ['Chapter'] = Chapter,
    ['Chapter'] = strip_apostrophe_markup (Chapter), -- Chapter stripped of bold / italic wikimarkup
    ['Title'] = Title,
    ['Title'] = make_coins_title (Title, ScriptTitle), -- strip_apostrophe_markup (Title), -- Title stripped of bold / italic wikimarkup
    ['PublicationPlace'] = PublicationPlace,
    ['PublicationPlace'] = PublicationPlace,
    ['Date'] = first_set(COinS_date, Date), -- COinS_date has correctly formatted date if Date is valid; any reason to keep Date here?  Should we be including invalid dates in metadata?
    ['Date'] = first_set(COinS_date, Date), -- COinS_date has correctly formatted date if Date is valid; any reason to keep Date here?  Should we be including invalid dates in metadata?
    Line 1,517: Line 1,732:
    ['RawPage'] = this_page.prefixedText,
    ['RawPage'] = this_page.prefixedText,
    };
    };
     
    --[[Why is this here?  Why are we mapping Title to Chapter when Periodical is set?
    if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then
    if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then
    Chapter = Title;
    Chapter = Title;
    Line 1,526: Line 1,741:
    TransTitle = '';
    TransTitle = '';
    end
    end
    ]]
    --[[ Hide unfinished cite newsgroup code so that long delayed update can take place
    --[[ Hide unfinished cite newsgroup code so that long delayed update can take place
    -- special case for cite newsgroup.  Do this after COinS because we are modifying Publishername and ID
    -- special case for cite newsgroup.  Do this after COinS because we are modifying Publishername and ID
    Line 1,555: Line 1,771:
    format = A["AuthorFormat"],
    format = A["AuthorFormat"],
    maximum = Maximum,
    maximum = Maximum,
    lastauthoramp = LastAuthorAmp
    lastauthoramp = LastAuthorAmp,
    page_name = this_page.text -- get current page name so that we don't wikilink to it via authorlinkn
    };
    };
    Line 1,587: Line 1,804:
    format = A['EditorFormat'],
    format = A['EditorFormat'],
    maximum = Maximum,
    maximum = Maximum,
    lastauthoramp = LastAuthorAmp
    lastauthoramp = LastAuthorAmp,
    page_name = this_page.text -- get current page name so that we don't wikilink to it via authorlinkn
    };
    };


    Line 1,636: Line 1,854:
    -- Test if citation has no title
    -- Test if citation has no title
    if not is_set(Chapter) and
    if not is_set(Title) and
    not is_set(Title) and
    not is_set(Periodical) and
    not is_set(Periodical) and
    not is_set(Conference) and
    not is_set(Conference) and
    not is_set(TransTitle) and
    not is_set(TransTitle) and
    not is_set(TransChapter) then
    not is_set(ScriptTitle) then
    table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } );
    table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } );
    end
    end
    Line 1,660: Line 1,877:
    Chapter = "[[" .. ChapterLink .. "|" .. Chapter .. "]]";
    Chapter = "[[" .. ChapterLink .. "|" .. Chapter .. "]]";
    end
    end
    if is_set(Periodical) and is_set(Title) then
     
    Chapter = wrap( 'italic-title', Chapter );
    Chapter = kern_quotes (Chapter); -- if necessary, separate chapter title's leading and trailing quote marks from Module provided quote marks
    TransChapter = wrap( 'trans-italic-title', TransChapter );
    Chapter = wrap( 'quoted-title', Chapter );
    else
    TransChapter = wrap( 'trans-quoted-title', TransChapter );
    Chapter = kern_quotes (Chapter); -- if necessary, separate chapter title's leading and trailing quote marks from Module provided quote marks
     
    Chapter = wrap( 'quoted-title', Chapter );
    TransChapter = wrap( 'trans-quoted-title', TransChapter );
    end
    local TransError = ""
    local TransError = ""
    if is_set(TransChapter) then
    if is_set(TransChapter) then
    Line 1,710: Line 1,923:
    Title = "[[" .. TitleLink .. "|" .. Title .. "]]"
    Title = "[[" .. TitleLink .. "|" .. Title .. "]]"
    end
    end
     
    if is_set(Periodical) then
    if inArray(config.CitationClass, {"web","news","journal","pressrelease","conference","podcast"}) then
    Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks
    Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks
    Title = wrap( 'quoted-title', Title );
    TransTitle = wrap( 'trans-quoted-title', TransTitle );
    --[[ Hide unfinished cite newsgroup code so that long delayed update can take place
    elseif inArray(config.CitationClass, {"web","news","pressrelease","conference","podcast","newsgroup"}) and
    ]] elseif inArray(config.CitationClass, {"web","news","pressrelease","conference","podcast"}) and
    not is_set(Chapter) then
    Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks
    Title = wrap( 'quoted-title', Title );
    Title = wrap( 'quoted-title', Title );
    Title = script_concatenate (Title, ScriptTitle); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped
    TransTitle = wrap( 'trans-quoted-title', TransTitle );
    TransTitle = wrap( 'trans-quoted-title', TransTitle );
    Chapter = ''; -- chapter not allowed
    else
    else
    Title = wrap( 'italic-title', Title );
    Title = wrap( 'italic-title', Title );
    Title = script_concatenate (Title, ScriptTitle); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped
    TransTitle = wrap( 'trans-italic-title', TransTitle );
    TransTitle = wrap( 'trans-italic-title', TransTitle );
    end
    end
     
    TransError = "";
    TransError = "";
    if is_set(TransTitle) then
    if is_set(TransTitle) then
    if not is_set(Title) then
    if is_set(Title) then
    TransTitle = " " .. TransTitle;
    else
    TransError = " " .. seterror( 'trans_missing_title' );
    TransError = " " .. seterror( 'trans_missing_title' );
    else
    TransTitle = " " .. TransTitle;
    end
    end
    end
    end
    Line 1,828: Line 2,037:
    end
    end


    --[[Look in the list of iso639-1 language codes to see if the value provided in the language parameter matches one of them.  If a match is found,
    use that value; if not, then use the value that was provided with the language parameter.
    Categories are assigned in a manner similar to the {{xx icon}} templates - categorizes only mainspace citations and only when the language code is not 'en' (English).
    ]]
    if is_set (Language) then
    if is_set (Language) then
    -- local name = mw.language.fetchLanguageName( Language:lower(), "en" ); -- experiment: this seems to return correct ISO 639-1 language names
    Language = language_parameter (Language, this_page.namespace); -- format, categories (article namespace only), name from ISO639-1, etc
    local name = cfg.iso639_1[Language:lower()]; -- get the language name if Language parameter has a valid iso 639-1 code
    if nil == name then
    Language=" " .. wrap( 'language', Language ); -- no match, use parameter's value
    else
    if 0 == this_page.namespace and 'en' ~= Language:lower() then --found a match; is this page main / article space and English not the language?
    Language=" " .. wrap( 'language', name .. '[[Category:Articles with ' .. name .. '-language external links]]' ); -- in main space and not English: categorize
    else
    Language=" " .. wrap( 'language', name ); --not in mainspace or language is English so don't categorize
    end
    end
    else
    else
    Language=""; -- language not specified so make sure this is an empty string;
    Language=""; -- language not specified so make sure this is an empty string;
    end
    end


    Line 2,207: Line 2,401:
    if inArray(no_tracking_cats, {"", "no", "false", "n"}) then
    if inArray(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 ..']]';
    end
    for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories
    text = text .. '[[Category:' .. v ..']]';
    end
    for _, v in ipairs( z.properties_cats ) do -- append maintenance categories
    text = text .. '[[Category:' .. v ..']]';
    text = text .. '[[Category:' .. v ..']]';
    end
    end