Module:Citation/CS1: Difference between revisions

    m>Trappist the monk
    (Synch from sandbox;)
    m>Trappist the monk
    (Synch from sandbox;)
    Line 12: Line 12:
    local z ={}; -- tables in Module:Citation/CS1/Utilities
    local z ={}; -- tables in Module:Citation/CS1/Utilities


    local extract_ids, build_id_list, is_embargoed; -- functions in Module:Citation/CS1/Identifiers
    local extract_ids, extract_id_access_levels, build_id_list, is_embargoed; -- functions in Module:Citation/CS1/Identifiers


    local make_coins_title, get_coins_pages, COinS; -- functions in Module:Citation/CS1/COinS
    local make_coins_title, get_coins_pages, COinS; -- functions in Module:Citation/CS1/COinS
    Line 383: Line 383:
    ]]
    ]]


    local function external_link( URL, label, source )
    local function external_link( URL, label, source, access)
    local error_str = "";
    local error_str = "";
    local domain;
    local domain;
    local path;
    local path;
    local base_url;
    if not is_set( label ) then
    if not is_set( label ) then
    Line 406: Line 407:
    end
    end
    return table.concat({ "[", URL, " ", safe_for_url( label ), "]", error_str });
    base_url = table.concat({ "[", URL, " ", safe_for_url( label ), "]" });
    if is_set(access) then -- access level (free, paywalled, ...)
    base_url = substitute(cfg.presentation[access], base_url);
    end
    return table.concat({ base_url, error_str });
    end
    end


    Line 584: Line 591:


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


    Line 1,000: Line 1,007:
    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,027: Line 1,037:
    one = person.last
    one = person.last
    local first = person.first
    local first = person.first
    if is_set(first) then  
    if is_set(first) then
    if ( "vanc" == format ) then -- if vancouver format
    if 'mla' == control.mode then
    one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
    if i == 1 then -- for mla
    if not person.corporate and is_good_vanc_name (one, first) then -- and name is all Latin characters; corporate authors not tested
    one = one .. namesep .. first; -- first name last, first
    first = reduce_to_initials(first) -- attempt to convert first name(s) to initials
    else -- all other names
    one = first .. ' ' .. one; -- first last
    end
    else
    if ( "vanc" == format ) then -- if vancouver format
    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
    end
    one = one .. namesep .. first;
    end
    end
    one = one .. namesep .. first
    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
    Line 1,048: Line 1,066:
    if count > 0 then  
    if count > 0 then  
    if count > 1 and is_set(lastauthoramp) and not etal then
    if count > 1 and is_set(lastauthoramp) and not etal then
    text[#text-2] = " & "; -- replace last separator with ampersand text
    if 'mla' == control.mode then
    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,134: Line 1,156:
    if 1 < count then -- param could be |author= or |editor= so one separactor character is acceptable
    if 1 < count then -- param could be |author= or |editor= so one separactor character is acceptable
    add_maint_cat ('mult_names', list_name:lower()); -- more than one separator indicates multiple names so add a maint cat for this template
    add_maint_cat ('mult_names', cfg.special_case_translation [list_name]); -- more than one separator indicates multiple names so add a maint cat for this template
    end
    end
    end
    end
    Line 1,177: Line 1,199:
    last, etal = name_has_etal (last, etal, false); -- find and remove variations on et al.
    last, etal = name_has_etal (last, etal, false); -- find and remove variations on et al.
    first, etal = name_has_etal (first, etal, false); -- find and remove variations on et al.
    first, etal = name_has_etal (first, etal, false); -- find and remove variations on et al.
    last = name_has_mult_names (last, err_msg_list_name); -- check for multiple names in last and its aliases
    -- last = name_has_mult_names (last, err_msg_list_name); -- check for multiple names in last and its aliases
    last = name_has_mult_names (last, list_name); -- check for multiple names in last and its aliases
    if first and not last then -- if there is a firstn without a matching lastn
    if first and not last then -- if there is a firstn without a matching lastn
    Line 1,310: Line 1,333:
    end
    end
    return (" " .. wrap_msg ('language', name)); -- otherwise wrap with '(in ...)'
    return (" " .. wrap_msg ('language', name)); -- otherwise wrap with '(in ...)'
    --[[ TODO: should only return blank or name rather than full list
    so we can clean up the bunched parenthetical elements Language, Type, Format
    ]]
    end
    end


    Line 1,371: Line 1,397:
    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,449: Line 1,477:
    max = tonumber (max); -- make it a number
    max = tonumber (max); -- make it a number
    if max >= count then -- if |display-xxxxors= value greater than or equal to number of authors/editors
    if max >= count then -- if |display-xxxxors= value greater than or equal to number of authors/editors
    add_maint_cat ('disp_auth_ed', list_name);
    add_maint_cat ('disp_auth_ed', cfg.special_case_translation [list_name]);
    end
    end
    else -- not a valid keyword or number
    else -- not a valid keyword or number
    Line 1,657: Line 1,685:
    ]]
    ]]


    local function is_valid_parameter_value (value, name, possible)
    local function is_valid_parameter_value (value, name, possible, cite_class)
    -- 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,694: Line 1,729:
    ]]
    ]]
    local function format_volume_issue (volume, issue, cite_class, origin, sepc, lower)
    local function format_volume_issue (volume, issue, cite_class, origin, sepc, lower, mode)
    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,754: Line 1,800:
    ]]
    ]]


    local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower)
    local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower, mode)
    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,772: Line 1,818:


    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
    Line 1,910: Line 1,960:
    -- Pick out the relevant fields from the arguments.  Different citation templates
    -- Pick out the relevant fields from the arguments.  Different citation templates
    -- 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
    local Mode = A['Mode'];
    if not is_valid_parameter_value (Mode, 'mode', cfg.keywords['mode'], config.CitationClass) then
    Mode = '';
    end
    local author_etal;
    local author_etal;
    local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors=
    local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors=
    Line 1,957: Line 2,014:
    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 = A['Interviewers']
    local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs
    local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs
    Line 2,036: Line 2,095:
    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) and not ('conference' == config.CitationClass and not is_set (Periodical)) then
    if in_array (config.CitationClass, cfg.templates_using_volume) then
    Volume = A['Volume'];
    Volume = A['Volume'];
    end
    end
    -- conference & map books do not support issue
    if in_array (config.CitationClass, cfg.templates_using_issue) and not (in_array (config.CitationClass, {'conference', 'map'}) and not is_set (Periodical))then
    if in_array (config.CitationClass, cfg.templates_using_issue) and not (in_array (config.CitationClass, {'conference', 'map'}) and not is_set (Periodical))then
    Issue = A['Issue'];
    Issue = A['Issue'];
    Line 2,062: Line 2,123:
    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
    local UrlAccess = A['UrlAccess'];
    if not is_valid_parameter_value (UrlAccess, 'url-access', cfg.keywords ['url-access']) then
    UrlAccess = nil;
    end
    if not is_set(URL) and is_set(UrlAccess) then
    UrlAccess = nil;
    table.insert( z.message_tail, { set_error( 'param_access_requires_param', {'url'}, true ) } );
    end
    end


    local Via = A['Via'];
    if is_set (UrlAccess) and is_set (SubscriptionRequired) then -- while not aliases, these are much the same so if both are set
    table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'url-access') .. ' and ' .. wrap_style ('parameter', 'subscription')}, true ) } ); -- add error message
    SubscriptionRequired = nil; -- unset; prefer |access= over |subscription=
    end
    if is_set (UrlAccess) and is_set (RegistrationRequired) then -- these are not the same but contradictory so if both are set
    table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'url-access') .. ' and ' .. wrap_style ('parameter', 'registration')}, true ) } ); -- add error message
    RegistrationRequired = nil; -- unset; prefer |access= over |registration=
    end
     
     
    local Via = A['Via'];
    local AccessDate = A['AccessDate'];
    local AccessDate = A['AccessDate'];
    local Agency = A['Agency'];
    local Agency = A['Agency'];
    Line 2,082: Line 2,161:


    local ID_list = extract_ids( args );
    local ID_list = extract_ids( args );
    local ID_access_levels = extract_id_access_levels( args, ID_list );


    local Quote = A['Quote'];
    local Quote = A['Quote'];
    Line 2,095: Line 2,175:
    local LastAuthorAmp = A['LastAuthorAmp'];
    local LastAuthorAmp = A['LastAuthorAmp'];
    if not is_valid_parameter_value (LastAuthorAmp, 'last-author-amp', cfg.keywords ['yes_true_y']) then
    if not is_valid_parameter_value (LastAuthorAmp, 'last-author-amp', cfg.keywords ['yes_true_y']) then
    LastAuthorAmp = nil; -- set to empty string
    LastAuthorAmp = nil; -- set to empty string
    end
    if 'mla' == Mode then
    LastAuthorAmp = 'yes'; -- replaces last author/editor separator with ' and ' text
    end
    end
    local no_tracking_cats = A['NoTracking'];
    local no_tracking_cats = A['NoTracking'];
    Line 2,102: Line 2,185:
    end
    end


    --these are used by cite interview
    --these deprecated parameters are used by cite interview
    local Callsign = A['Callsign'];
    local Callsign = A['Callsign'];
    local City = A['City'];
    local City = A['City'];
    Line 2,118: Line 2,201:
    end
    end


    -- set default parameter values defined by |mode= parameter.  If |mode= is empty or omitted, use CitationClass to set these values
    local Mode = A['Mode'];
    if not is_valid_parameter_value (Mode, 'mode', cfg.keywords['mode']) then
    Mode = '';
    end
    local sepc; -- separator between citation elements for CS1 a period, for CS2, a comma
    local sepc; -- separator between citation elements for CS1 a period, for CS2, a comma
    local PostScript;
    local PostScript;
    Line 2,226: Line 2,304:
    end
    end


    -- special case for cite interview -- TODO: make cite interveiw not need special cases
    -- special case for cite interview
    -- TODO: make cite interview not need any special cases
    --[[
    Program, Callsign, City deprecated, so avoid using /Configuration
    for reassignment, which would allow these parameters' use outside interview
    ]]
    if (config.CitationClass == "interview") then
    if (config.CitationClass == "interview") then
    if is_set(Program) then
    if is_set(Program) then
    ID = ' ' .. Program;
    if not is_set(Periodical) then
    Periodical = Program;
    end
    end
    end
    if is_set(Callsign) then
    if is_set(Callsign) then
    if is_set(ID) then
    if not is_set(PublisherName) then
    ID = ID .. sepc .. ' ' .. Callsign;
    PublisherName = Callsign;
    else
    ID = ' ' .. Callsign;
    end
    end
    end
    end
    if is_set(City) then
    if is_set(City) then
    if is_set(ID) then
    if not is_set(PublicationPlace) then
    ID = ID .. sepc .. ' ' .. City;
    PublicationPlace = City;
    else
    ID = ' ' .. City;
    end
    end
    end
    if is_set(Others) then
    if is_set(TitleType) then
    Others = substitute (cfg.messages['interview-type'], {TitleType, Others});
    TitleType = '';
    else
    Others = substitute (cfg.messages['interview'], Others)
    end
    else
    Others = cfg.title_types ['interview'];
    end
    end
    end
    end
    Line 2,399: Line 2,469:


    -- 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", "DVD-notes", "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);
    if is_set(Degree) and "Thesis" == TitleType then -- special case for cite thesis
    if is_set(Degree) and "Thesis" == TitleType then -- special case for cite thesis
    Line 2,407: Line 2,477:


    if is_set(TitleType) then -- if type parameter is specified
    if is_set(TitleType) then -- if type parameter is specified
    TitleType = substitute( cfg.messages['type'], TitleType); -- display it in parentheses
    TitleType = substitute( cfg.messages['type'], TitleType); -- display it in parentheses
    -- TODO: Hack on TitleType to fix bunched parentheses problem
    end
    end


    Line 2,561: Line 2,632:
    if 'newsgroup' == config.CitationClass then
    if 'newsgroup' == config.CitationClass then
    if is_set (PublisherName) then
    if is_set (PublisherName) then
    PublisherName = substitute (cfg.messages['newsgroup'], external_link( 'news:' .. PublisherName, PublisherName, A:ORIGIN('PublisherName') ));
    PublisherName = substitute (cfg.messages['newsgroup'], external_link( 'news:' .. PublisherName, PublisherName, A:ORIGIN('PublisherName'), nil ));
    end
    end
    end
    end
    Line 2,577: Line 2,648:
    maximum = nil, -- as if display-authors or display-editors not set
    maximum = nil, -- as if display-authors or display-editors not set
    lastauthoramp = LastAuthorAmp,
    lastauthoramp = LastAuthorAmp,
    page_name = this_page.text -- get current page name so that we don't wikilink to it via editorlinkn
    page_name = this_page.text, -- get current page name so that we don't wikilink to it via editorlinkn
    mode = Mode
    };
    };


    Line 2,662: Line 2,734:
    end
    end


    local OriginalURL, OriginalURLorigin, OriginalFormat;
    local OriginalURL, OriginalURLorigin, OriginalFormat, OriginalAccess;
    DeadURL = DeadURL:lower(); -- used later when assembling archived text
    DeadURL = DeadURL:lower(); -- used later when assembling archived text
    if is_set( ArchiveURL ) then
    if is_set( ArchiveURL ) then
    Line 2,677: Line 2,749:
    OriginalURL = URL; -- save copy of original source URL
    OriginalURL = URL; -- save copy of original source URL
    OriginalURLorigin = URLorigin; -- name of url parameter for error messages
    OriginalURLorigin = URLorigin; -- name of url parameter for error messages
    OriginalFormat = Format; -- and original |format=
    OriginalFormat = Format; -- and original |format=
    OriginalAccess = UrlAccess;
    if 'no' ~= DeadURL then -- if URL set then archive-url applies to it
    if 'no' ~= DeadURL then -- if URL set then archive-url applies to it
    URL = ArchiveURL -- swap-in the archive's url
    URL = ArchiveURL -- swap-in the archive's url
    URLorigin = A:ORIGIN('ArchiveURL') -- name of archive url parameter for error messages
    URLorigin = A:ORIGIN('ArchiveURL') -- name of archive url parameter for error messages
    Format = ArchiveFormat or ''; -- swap in archive's format
    Format = ArchiveFormat or ''; -- swap in archive's format
    UrlAccess = nil; -- restricted access levels do not make sense for archived urls
    end
    end
      end
      end
    Line 2,734: Line 2,808:
    end
    end


    if in_array(config.CitationClass, {'web','news','journal', 'magazine', 'pressrelease','podcast', 'newsgroup', 'mailinglist', 'arxiv'}) or
    if in_array(config.CitationClass, {'web','news','journal', 'magazine', 'pressrelease','podcast', 'newsgroup', 'mailinglist', 'arxiv', 'interview'}) or
    ('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia)) or
    ('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia)) or
    ('map' == config.CitationClass and is_set (Periodical)) then -- special case for cite map when the map is in a periodical treat as an article
    ('map' == config.CitationClass and is_set (Periodical)) then -- special case for cite map when the map is in a periodical treat as an article
    Line 2,763: Line 2,837:
    if is_set(Title) then
    if is_set(Title) then
    if not is_set(TitleLink) and is_set(URL) then
    if not is_set(TitleLink) and is_set(URL) then
    Title = external_link( URL, Title, URLorigin ) .. TransError .. Format;
    Title = external_link( URL, Title, URLorigin, UrlAccess ) .. TransError .. Format;
    -- this experiment hidden 2016-04-10; see Help_talk:Citation_Style_1#Recycled_urls
    -- 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
    -- local temp_title = external_link( URL, Title, URLorigin ) .. TransError .. Format; -- do this so we get error message even if url is usurped no archive
    Line 2,789: Line 2,864:
    if is_set (Conference) then
    if is_set (Conference) then
    if is_set (ConferenceURL) then
    if is_set (ConferenceURL) then
    Conference = external_link( ConferenceURL, Conference, ConferenceURLorigin );
    Conference = external_link( ConferenceURL, Conference, ConferenceURLorigin, nil );
    end
    end
    Conference = sepc .. " " .. Conference .. ConferenceFormat;
    Conference = sepc .. " " .. Conference .. ConferenceFormat;
    elseif is_set(ConferenceURL) then
    elseif is_set(ConferenceURL) then
    Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURLorigin );
    Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURLorigin, nil );
    end
    end


    Line 2,822: Line 2,897:
    end
    end


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


    At = is_set(At) and (sepc .. " " .. At) or "";
    At = is_set(At) and (sepc .. " " .. At) or "";
    Line 2,847: Line 2,922:
    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;
    --[[ TODO: need to extract the wrap_msg from language_parameter
    so that we can solve parentheses bunching problem with Format/Language/TitleType
    ]]
    end
    end


    Line 2,852: Line 2,930:
    if is_set (Translators) then
    if is_set (Translators) then
    Others = sepc .. ' ' .. wrap_msg ('translated', Translators, use_lowercase) .. Others;  
    if 'mla' == Mode then
    Others = sepc .. ' Trans. ' .. Translators .. Others;
    else
    Others = sepc .. ' ' .. wrap_msg ('translated', Translators, use_lowercase) .. Others;
    end
    end
    if is_set (Interviewers) then
    Others = sepc .. ' ' .. wrap_msg ('interview', Interviewers, use_lowercase) .. Others;
    end
    end
     
    TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";
    TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";
    if is_set (Edition) then
    if is_set (Edition) then
    Line 2,860: Line 2,945:
    add_maint_cat ('extra_text', 'edition');
    add_maint_cat ('extra_text', 'edition');
    end
    end
    Edition = " " .. wrap_msg ('edition', Edition);
    if 'mla' == Mode then
    Edition = '. ' .. Edition .. ' ed.';
    else
    Edition = " " .. wrap_msg ('edition', Edition);
    end
    else
    else
    Edition = '';
    Edition = '';
    Line 2,866: Line 2,955:


    Series = is_set(Series) and (sepc .. " " .. Series) or "";
    Series = is_set(Series) and (sepc .. " " .. Series) or "";
    OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or "";
    if 'mla' == Mode then -- not in brackets for mla
    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);
    Volume = format_volume_issue (Volume, Issue, config.CitationClass, Periodical_origin, sepc, use_lowercase, Mode);


    ------------------------------------ totally unrelated data
    ------------------------------------ totally unrelated data
    Line 2,893: Line 2,986:


    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 (sepc ~= ".") then retrv_text = retrv_text:lower() end -- if 'citation', lower case
    if 'mla' == Mode then -- retrieved text not used in mla
    AccessDate = substitute (retrv_text, AccessDate); -- add retrieved text
    AccessDate = ' ' .. AccessDate;
    -- neither of these work; don't know why; it seems that substitute() isn't being called
    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 2,907: Line 3,003:
    end
    end


    ID_list = build_id_list( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo, Class = Class} );
    ID_list = build_id_list( ID_list, {IdAccessLevels=ID_access_levels, DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo, Class = Class} );


    if is_set(URL) then
    if is_set(URL) then
    URL = " " .. external_link( URL, nil, URLorigin );
    URL = " " .. external_link( URL, nil, URLorigin, Access );
    end
    end


    Line 2,930: Line 3,026:
    if sepc ~= "." then arch_text = arch_text:lower() end
    if sepc ~= "." then arch_text = arch_text:lower() end
    Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'],
    Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'],
    { external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL') ) .. ArchiveFormat, ArchiveDate } );
    { external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL'), nil ) .. ArchiveFormat, ArchiveDate } );
    if not is_set(OriginalURL) then
    if not is_set(OriginalURL) then
    Archived = Archived .. " " .. set_error('archive_missing_url');    
    Archived = Archived .. " " .. set_error('archive_missing_url');    
    Line 2,946: Line 3,042:
    else -- DeadURL is empty, 'yes', 'true', or 'y'
    else -- DeadURL is empty, 'yes', 'true', or 'y'
    Archived = sepc .. " " .. substitute( arch_text,
    Archived = sepc .. " " .. substitute( arch_text,
    { external_link( OriginalURL, cfg.messages['original'], OriginalURLorigin ) .. OriginalFormat, ArchiveDate } ); -- format already styled
    { external_link( OriginalURL, cfg.messages['original'], OriginalURLorigin, OriginalAccess ) .. OriginalFormat, ArchiveDate } ); -- format already styled
    end
    end
    else
    else
    Line 2,969: Line 3,065:
    end
    end
    if sepc == '.' then
    if sepc == '.' then
    Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary'], A:ORIGIN('LayURL') ) .. LayFormat .. LaySource .. LayDate
    Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary'], A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate
    else
    else
    Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower(), A:ORIGIN('LayURL') ) .. LayFormat .. LaySource .. LayDate
    Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower(), A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate
    end
    end
    elseif is_set (LayFormat) then -- Test if |lay-format= is given without giving a |lay-url=
    elseif is_set (LayFormat) then -- Test if |lay-format= is given without giving a |lay-url=
    Line 2,979: Line 3,075:
    if is_set(Transcript) then
    if is_set(Transcript) then
    if is_set(TranscriptURL) then
    if is_set(TranscriptURL) then
    Transcript = external_link( TranscriptURL, Transcript, TranscriptURLorigin );
    Transcript = external_link( TranscriptURL, Transcript, TranscriptURLorigin, nil );
    end
    end
    Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat;
    Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat;
    elseif is_set(TranscriptURL) then
    elseif is_set(TranscriptURL) then
    Transcript = external_link( TranscriptURL, nil, TranscriptURLorigin );
    Transcript = external_link( TranscriptURL, nil, TranscriptURLorigin, nil );
    end
    end


    Line 3,033: Line 3,129:
    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
    tcommon = safe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series,  
    if 'mla' == Mode then
    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
    tcommon2 = safe_join( {Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc );
    if 'mla' == Mode then
    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,055: Line 3,160:
    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
    tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language,  
    tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language,  
    Line 3,071: Line 3,184:


    if is_set(Date) then
    if is_set(Date) then
    if is_set (Authors) or is_set (Editors) then -- date follows authors or editors when authors not set
    if ('mla' == Mode) then
    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,089: Line 3,208:
    end
    end
    end
    end
    if not is_set (Date) then -- when date is set it's in parentheses; no Authors termination
    if (not is_set (Date)) or ('mla' == Mode) 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,095: Line 3,214:
    local in_text = " ";
    local in_text = " ";
    local post_text = "";
    local post_text = "";
    if is_set(Chapter) and 0 == #c then
    if is_set(Chapter) and 0 == #c and 'mla' ~= Mode 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 in_text = in_text:lower() end -- lowercase for cs2
    elseif is_set(Chapter) and 'mla' == Mode then
    if EditorCount <= 1 then
    in_text = '. Ed. ';
    else
    in_text = '. Eds. ';
    end
    else
    else
    if EditorCount <= 1 then
    if EditorCount <= 1 then
    Line 3,111: Line 3,236:
    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) then -- when Editors make sure that Authors gets terminated
    if is_set (Editors) and ('mla' ~= Mode)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) then -- when date is set it's in parentheses; no Contributors termination
    if (not is_set (Date)) or ('mla' == Mode) 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
    text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc );
    if 'mla' == Mode then
    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,135: Line 3,267:
    end
    end
    end
    end
    text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc );
    if 'mla' == Mode then
    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,183: Line 3,329:
    end
    end
    if string.len(text:gsub("<span[^>/]*>.-</span>", ""):gsub("%b<>","")) <= 2 then
    if string.len(text:gsub("<span[^>/]*>(.-)</span>", "%1"):gsub("%b<>","")) <= 2 then -- remove <span> tags and other html-like markup; then get length of what remains
    z.error_categories = {};
    z.error_categories = {};
    text = set_error('empty_citation');
    text = set_error('empty_citation');
    Line 3,288: Line 3,434:
    build_id_list = identifiers.build_id_list;
    build_id_list = identifiers.build_id_list;
    is_embargoed = identifiers.is_embargoed;
    is_embargoed = identifiers.is_embargoed;
    extract_id_access_levels = identifiers.extract_id_access_levels;
    make_coins_title = metadata.make_coins_title; -- imported functions from Module:Citation/CS1/COinS
    make_coins_title = metadata.make_coins_title; -- imported functions from Module:Citation/CS1/COinS