Difference between revisions of "Module:Citation/CS1"

From eVision
Jump to: navigation, search
(Synch from sandbox;)
 
m (1 revision)
 
Line 1: Line 1:
−
local z = {
 
−
    error_categories = {};
 
−
    error_ids = {};
 
−
    message_tail = {};
 
−
}
 
  
−
-- Include translation message hooks, ID and error handling configuration settings.
 
−
--local cfg = mw.loadData( 'Module:Citation/CS1/Configuration/sandbox' );
 
−
 
−
-- Contains a list of all recognized parameters
 
−
--local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist/sandbox' );
 
−
 
−
--local dates = require('Module:Citation/CS1/Date_validation/sandbox').dates -- location of date validation code
 
−
 
−
-- Whether variable is set or not
 
−
function is_set( var )
 
−
    return not (var == nil or var == '');
 
−
end
 
−
 
−
-- First set variable or nil if none
 
−
function first_set(...)
 
−
    local list = {...};
 
−
    for _, var in pairs(list) do
 
−
        if is_set( var ) then
 
−
            return var;
 
−
        end
 
−
    end
 
−
end
 
−
 
−
-- Whether needle is in haystack
 
−
function inArray( needle, haystack )
 
−
    if needle == nil then
 
−
        return false;
 
−
    end
 
−
    for n,v in ipairs( haystack ) do
 
−
        if v == needle then
 
−
            return n;
 
−
        end
 
−
    end
 
−
    return false;
 
−
end
 
−
 
−
--[[
 
−
Categorize and emit an error message when the citation contains one or more deprecated parameters.  Because deprecated parameters (currently |day=, |month=,
 
−
|coauthor=, and |coauthors=) aren't related to each other and because these parameters may be concatenated into the variables used by |date= and |author#= (and aliases)
 
−
details of which parameter caused the error message are not provided.  Only one error message is emitted regarless of the number of deprecated parameters in the citation.
 
−
]]
 
−
function deprecated_parameter()
 
−
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
 
−
-- 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
 
−
end
 
−
end
 
−
 
−
-- Populates numbered arguments in a message string using an argument table.
 
−
function substitute( msg, args )
 
−
-- return args and tostring( mw.message.newRawMessage( msg, args ) ) or msg;
 
−
return args and mw.message.newRawMessage( msg, args ):plain() or msg;
 
−
end
 
−
 
−
--[[
 
−
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:
 
−
"'Unkerned title with leading and trailing single quote marks'"
 
−
" 'Kerned title with leading and trailing single quote marks' " (in real life the kerning isn't as wide as this example)
 
−
]]
 
−
function kern_quotes (str)
 
−
local left='<span style="padding-left:0.2em;">%1</span>'; -- spacing to use when title contains leading single or double quote mark
 
−
local right='<span style="padding-right:0.2em;">%1</span>'; -- spacing to use when title contains trailing single or double quote mark
 
−
 
−
if  str:match ("^[\"\'][^\']") then
 
−
str = string.gsub( str, "^[\"\']", left, 1 ); -- replace (captured) leading single or double quote with left-side <span>
 
−
end
 
−
if str:match ("[^\'][\"\']$") then
 
−
str = string.gsub( str, "[\"\']$", right, 1 ); -- replace (captured) trailing single or double quote with right-side <span>
 
−
end
 
−
return str;
 
−
end
 
−
 
−
-- Wraps a string using a message_list configuration taking one argument
 
−
function wrap( key, str, lower )
 
−
    if not is_set( str ) then
 
−
        return "";
 
−
    elseif inArray( key, { 'italic-title', 'trans-italic-title' } ) then
 
−
        str = safeforitalics( str );
 
−
    end
 
−
    if lower == true then
 
−
        return substitute( cfg.messages[key]:lower(), {str} );
 
−
    else
 
−
        return substitute( cfg.messages[key], {str} );
 
−
    end       
 
−
end
 
−
 
−
--[[
 
−
Argument wrapper.  This function provides support for argument
 
−
mapping defined in the configuration file so that multiple names
 
−
can be transparently aliased to single internal variable.
 
−
]]
 
−
function argument_wrapper( args )
 
−
    local origin = {};
 
−
   
 
−
    return setmetatable({
 
−
        ORIGIN = function( self, k )
 
−
            local dummy = self[k]; --force the variable to be loaded.
 
−
            return origin[k];
 
−
        end
 
−
    },
 
−
    {
 
−
        __index = function ( tbl, k )
 
−
            if origin[k] ~= nil then
 
−
                return nil;
 
−
            end
 
−
           
 
−
            local args, list, v = args, cfg.aliases[k];
 
−
           
 
−
            if type( list ) == 'table' then
 
−
                v, origin[k] = selectone( args, list, 'redundant_parameters' );
 
−
                if origin[k] == nil then
 
−
                    origin[k] = ''; -- Empty string, not nil
 
−
                end
 
−
            elseif list ~= nil then
 
−
                v, origin[k] = args[list], list;
 
−
            else
 
−
                -- maybe let through instead of raising an error?
 
−
                -- v, origin[k] = args[k], k;
 
−
                error( cfg.messages['unknown_argument_map'] );
 
−
            end
 
−
           
 
−
            -- Empty strings, not nil;
 
−
            if v == nil then
 
−
                v = cfg.defaults[k] or '';
 
−
                origin[k] = '';
 
−
            end
 
−
           
 
−
            tbl = rawset( tbl, k, v );
 
−
            return v;
 
−
        end,
 
−
    });
 
−
end
 
−
 
−
--[[
 
−
Looks for a parameter's name in the whitelist.
 
−
 
−
Parameters in the whitelist can have three values:
 
−
true - active, supported parameters
 
−
false - deprecated, supported parameters
 
−
nil - unsupported parameters
 
−
]]
 
−
function validate( name )
 
−
local name = tostring( name );
 
−
local state = whitelist.basic_arguments[ name ];
 
−
 
−
-- Normal arguments
 
−
if true == state then return true; end -- valid actively supported parameter
 
−
if false == state then
 
−
deprecated_parameter (); -- parameter is deprecated but still supported
 
−
return true;
 
−
end
 
−
 
−
-- Arguments with numbers in them
 
−
name = name:gsub( "%d+", "#" ); -- replace digit(s) with # (last25 becomes last#
 
−
state = whitelist.numbered_arguments[ name ];
 
−
if true == state then return true; end -- valid actively supported parameter
 
−
if false == state then
 
−
deprecated_parameter (); -- parameter is deprecated but still supported
 
−
return true;
 
−
end
 
−
 
−
return false; -- Not supported because not found or name is set to nil
 
−
end
 
−
 
−
-- Formats a comment for error trapping
 
−
function errorcomment( content, hidden )
 
−
    return wrap( hidden and 'hidden-error' or 'visible-error', content );
 
−
end
 
−
 
−
--[[
 
−
Sets an error condition and returns the appropriate error message.  The actual placement
 
−
of the error message in the output is the responsibility of the calling function.
 
−
]]
 
−
function seterror( error_id, arguments, raw, prefix, suffix )
 
−
    local error_state = cfg.error_conditions[ error_id ];
 
−
   
 
−
    prefix = prefix or "";
 
−
    suffix = suffix or "";
 
−
   
 
−
    if error_state == nil then
 
−
        error( cfg.messages['undefined_error'] );
 
−
    elseif is_set( error_state.category ) then
 
−
        table.insert( z.error_categories, error_state.category );
 
−
    end
 
−
   
 
−
    local message = substitute( error_state.message, arguments );
 
−
   
 
−
    message = message .. " ([[" .. cfg.messages['help page link'] ..
 
−
        "#" .. error_state.anchor .. "|" ..
 
−
        cfg.messages['help page label'] .. "]])";
 
−
   
 
−
    z.error_ids[ error_id ] = true;
 
−
    if inArray( error_id, { 'bare_url_missing_title', 'trans_missing_title' } )
 
−
            and z.error_ids['citation_missing_title'] then
 
−
        return '', false;
 
−
    end
 
−
   
 
−
    message = table.concat({ prefix, message, suffix });
 
−
   
 
−
    if raw == true then
 
−
        return message, error_state.hidden;
 
−
    end       
 
−
       
 
−
    return errorcomment( message, error_state.hidden );
 
−
end
 
−
 
−
-- Formats a wiki style external link
 
−
function externallinkid(options)
 
−
    local url_string = options.id;
 
−
    if options.encode == true or options.encode == nil then
 
−
        url_string = mw.uri.encode( url_string );
 
−
    end
 
−
    return mw.ustring.format( '[[%s|%s]]%s[%s%s%s %s]',
 
−
        options.link, options.label, options.separator or "&nbsp;",
 
−
        options.prefix, url_string, options.suffix or "",
 
−
        mw.text.nowiki(options.id)
 
−
    );
 
−
end
 
−
 
−
-- Formats a wiki style internal link
 
−
function internallinkid(options)
 
−
    return mw.ustring.format( '[[%s|%s]]%s[[%s%s%s|%s]]',
 
−
        options.link, options.label, options.separator or "&nbsp;",
 
−
        options.prefix, options.id, options.suffix or "",
 
−
        mw.text.nowiki(options.id)
 
−
    );
 
−
end
 
−
 
−
-- Format an external link with error checking
 
−
function externallink( URL, label, source )
 
−
    local error_str = "";
 
−
    if not is_set( label ) then
 
−
        label = URL;
 
−
        if is_set( source ) then
 
−
            error_str = seterror( 'bare_url_missing_title', { wrap( 'parameter', source ) }, false, " " );
 
−
        else
 
−
            error( cfg.messages["bare_url_no_origin"] );
 
−
        end           
 
−
    end
 
−
    if not checkurl( URL ) then
 
−
        error_str = seterror( 'bad_url', {}, false, " " ) .. error_str;
 
−
    end
 
−
    return table.concat({ "[", URL, " ", safeforurl( label ), "]", error_str });
 
−
end
 
−
 
−
-- Formats a link to Amazon
 
−
function amazon(id, domain)
 
−
    if not is_set(domain) then
 
−
        domain = "com"
 
−
    elseif ( "jp" == domain or "uk" == domain ) then
 
−
        domain = "co." .. domain
 
−
    end
 
−
    local handler = cfg.id_handlers['ASIN'];
 
−
    return externallinkid({link = handler.link,
 
−
        label=handler.label , prefix="//www.amazon."..domain.."/dp/",id=id,
 
−
        encode=handler.encode, separator = handler.separator})
 
−
end
 
−
 
−
--[[
 
−
Format LCCN link and do simple error checking.  LCCN is a character string 8-12 characters long. The length of the LCCN dictates the character type of the first 1-3 characters; the
 
−
rightmost eight are always digits. http://info-uri.info/registry/OAIHandler?verb=GetRecord&metadataPrefix=reg&identifier=info:lccn/
 
−
 
−
length = 8 then all digits
 
−
length = 9 then lccn[1] is alpha
 
−
length = 10 then lccn[1] and lccn[2] are both alpha or both digits
 
−
length = 11 then lccn[1] is alpha, lccn[2] and lccn[3] are both alpha or both digits
 
−
length = 12 then lccn[1] and lccn[2] are both alpha
 
−
 
−
]]
 
−
function lccn(id)
 
−
local handler = cfg.id_handlers['LCCN'];
 
−
local err_cat =  ''; -- presume that LCCN is valid
 
−
 
−
local len = id:len(); -- get the length of the lccn
 
−
 
−
if 8 == len then
 
−
if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits)
 
−
err_cat = ' ' .. seterror( 'bad_lccn' ); -- set an error message
 
−
end
 
−
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?
 
−
err_cat = ' ' .. seterror( 'bad_lccn' ); -- set an error message
 
−
end
 
−
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 nil == id:match("^%a%a%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
 
−
end
 
−
end
 
−
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
 
−
err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
 
−
end
 
−
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
 
−
err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
 
−
end
 
−
else
 
−
err_cat = ' ' .. seterror( 'bad_lccn' ); -- wrong length, set an error message
 
−
end
 
−
 
−
return externallinkid({link = handler.link, label = handler.label,
 
−
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat;
 
−
end
 
−
 
−
--[[
 
−
Format PMID and do simple error checking.  PMIDs are sequential numbers beginning at 1 and counting up.  This code checks the PMID to see that it
 
−
contains only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more PMIDs are issued.
 
−
]]
 
−
function pmid(id)
 
−
local test_limit = 30000000; -- update this value as PMIDs approach
 
−
local handler = cfg.id_handlers['PMID'];
 
−
local err_cat =  ''; -- presume that PMID is valid
 
−
 
−
if id:match("[^%d]") then -- if PMID has anything but digits
 
−
err_cat = ' ' .. seterror( 'bad_pmid' ); -- set an error message
 
−
else -- PMID is only digits
 
−
local id_num = tonumber(id); -- convert id to a number for range testing
 
−
if 1 > id_num or test_limit < id_num then -- if PMID is outside test limit boundaries
 
−
err_cat = ' ' .. seterror( 'bad_pmid' ); -- set an error message
 
−
end
 
−
end
 
−
 
−
return externallinkid({link = handler.link, label = handler.label,
 
−
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat;
 
−
end
 
−
 
−
--[[
 
−
Determines if a PMC identifier's online version is embargoed. Compares the date in |embargo= against today's date.  If embargo date is
 
−
in the future, returns true; otherwse, returns false because the embargo has expired or |embargo= not set in this cite.
 
−
]]
 
−
function is_embargoed(embargo)
 
−
if is_set(embargo) then
 
−
local lang = mw.getContentLanguage();
 
−
local good1, embargo_date, good2, todays_date;
 
−
good1, embargo_date = pcall( lang.formatDate, lang, 'U', embargo );
 
−
good2, todays_date = pcall( lang.formatDate, lang, 'U' );
 
−
 
−
if good1 and good2 and tonumber( embargo_date ) >= tonumber( todays_date ) then --is embargo date is in the future?
 
−
return true; -- still embargoed
 
−
end
 
−
end
 
−
return false; -- embargo expired or |embargo= not set
 
−
end
 
−
 
−
--[[
 
−
Format a PMC, do simple error checking, and check for embargoed articles.
 
−
 
−
The embargo parameter takes a date for a value. If the embargo date is in the future
 
−
the PMC identifier will not be linked to the article.  If the embargo specifies a date in the past, or if it is empty or omitted, then
 
−
the PMC identifier is linked to the article through the link at cfg.id_handlers['PMC'].prefix.
 
−
 
−
PMCs are sequential numbers beginning at 1 and counting up.  This code checks the PMC to see that it contains only digits and is less
 
−
than test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issued.
 
−
]]
 
−
function pmc(id, embargo)
 
−
local test_limit = 5000000; -- update this value as PMCs approach
 
−
local handler = cfg.id_handlers['PMC'];
 
−
local err_cat =  ''; -- presume that PMC is valid
 
−
   
 
−
local text;
 
−
 
−
if id:match("[^%d]") then -- if PMC has anything but digits
 
−
err_cat = ' ' .. seterror( 'bad_pmc' ); -- set an error message
 
−
else -- PMC is only digits
 
−
local id_num = tonumber(id); -- convert id to a number for range testing
 
−
if 1 > id_num or test_limit < id_num then -- if PMC is outside test limit boundaries
 
−
err_cat = ' ' .. seterror( 'bad_pmc' ); -- set an error message
 
−
end
 
−
end
 
−
 
−
if is_embargoed(embargo) then
 
−
text="[[" .. handler.link .. "|" .. handler.label .. "]]:" .. handler.separator .. id .. err_cat; --still embargoed so no external link
 
−
else
 
−
text = externallinkid({link = handler.link, label = handler.label, --no embargo date, ok to link to article
 
−
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat;
 
−
end
 
−
return text;
 
−
end
 
−
 
−
-- Formats a DOI and checks for DOI errors.
 
−
 
−
-- DOI names contain two parts: prefix and suffix separated by a forward slash.
 
−
--  Prefix: directory indicator '10.' followed by a registrant code
 
−
--  Suffix: character string of any length chosen by the registrant
 
−
 
−
-- This function checks a DOI name for: prefix/suffix.  If the doi name contains spaces or endashes,
 
−
-- or, if it ends with a period or a comma, this function will emit a bad_doi error message.
 
−
 
−
-- DOI names are case-insensitive and can incorporate any printable Unicode characters so the test for spaces, endash,
 
−
-- and terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely if ever used in doi names.
 
−
 
−
function doi(id, inactive)
 
−
    local cat = ""
 
−
    local handler = cfg.id_handlers['DOI'];
 
−
   
 
−
    local text;
 
−
if is_set(inactive) then
 
−
local inactive_year = inactive:match("%d%d%d%d") or ''; -- try to get the year portion from the inactive date
 
−
text = "[[" .. handler.link .. "|" .. handler.label .. "]]:" .. id;
 
−
if is_set(inactive_year) then
 
−
table.insert( z.error_categories, "Pages with DOIs inactive since " .. inactive_year );
 
−
else
 
−
table.insert( z.error_categories, "Pages with inactive DOIs" ); -- when inactive doesn't contain a recognizable year
 
−
end
 
−
inactive = " (" .. cfg.messages['inactive'] .. " " .. inactive .. ")"
 
−
else
 
−
text = externallinkid({link = handler.link, label = handler.label,
 
−
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
 
−
inactive = ""
 
−
end
 
−
 
−
if nil == id:match("^10%.[^%s–]-/[^%s–]-[^%.,]$") then -- doi must begin with '10.', must contain a fwd slash, must not contain spaces or endashes, and must not end with period or comma
 
−
cat = ' ' .. seterror( 'bad_doi' );
 
−
end
 
−
return text .. inactive .. cat
 
−
end
 
−
 
−
-- Formats an OpenLibrary link, and checks for associated errors.
 
−
function openlibrary(id)
 
−
    local code = id:sub(-1,-1)
 
−
    local handler = cfg.id_handlers['OL'];
 
−
    if ( code == "A" ) then
 
−
        return externallinkid({link=handler.link, label=handler.label,
 
−
            prefix="http://openlibrary.org/authors/OL",id=id, separator=handler.separator,
 
−
            encode = handler.encode})
 
−
    elseif ( code == "M" ) then
 
−
        return externallinkid({link=handler.link, label=handler.label,
 
−
            prefix="http://openlibrary.org/books/OL",id=id, separator=handler.separator,
 
−
            encode = handler.encode})
 
−
    elseif ( code == "W" ) then
 
−
        return externallinkid({link=handler.link, label=handler.label,
 
−
            prefix= "http://openlibrary.org/works/OL",id=id, separator=handler.separator,
 
−
            encode = handler.encode})
 
−
    else
 
−
        return externallinkid({link=handler.link, label=handler.label,
 
−
            prefix= "http://openlibrary.org/OL",id=id, separator=handler.separator,
 
−
            encode = handler.encode}) ..
 
−
            ' ' .. seterror( 'bad_ol' );
 
−
    end
 
−
end
 
−
 
−
--[[
 
−
Validate and format an issn.  This code fixes the case where an editor has included an ISSN in the citation but has separated the two groups of four
 
−
digits with a space.  When that condition occurred, the resulting link looked like this:
 
−
 
−
|issn=0819 4327 gives: [http://www.worldcat.org/issn/0819 4327 0819 4327]  -- can't have spaces in an external link
 
−
 
−
This code now prevents that by inserting a hyphen at the issn midpoint.  It also validates the issn for length and makes sure that the checkdigit agrees
 
−
with the calculated value.  Incorrect length (8 digits), characters other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check issn
 
−
error message.  The issn is always displayed with a hyphen, even if the issn was given as a single group of 8 digits.
 
−
]]
 
−
function issn(id)
 
−
local issn_copy = id; -- save a copy of unadulterated issn; use this version for display if issn does not validate
 
−
local handler = cfg.id_handlers['ISSN'];
 
−
local text;
 
−
local valid_issn = true;
 
−
 
−
id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and ndashes from the issn
 
−
 
−
if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the issn: 8 didgits long, containing only 0-9 or X in the last position
 
−
valid_issn=false; -- wrong length or improper character
 
−
else
 
−
valid_issn=is_valid_isxn(id, 8); -- validate issn
 
−
end
 
−
 
−
if true == valid_issn then
 
−
id = string.sub( id, 1, 4 ) .. "-" .. string.sub( id, 5 ); -- if valid, display correctly formatted version
 
−
else
 
−
id = issn_copy; -- if not valid, use the show the invalid issn with error message
 
−
end
 
−
 
−
text = externallinkid({link = handler.link, label = handler.label,
 
−
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
 
−
 
−
if false == valid_issn then
 
−
text = text .. ' ' .. seterror( 'bad_issn' ) -- add an error message if the issn is invalid
 
−
end
 
−
 
−
return text
 
−
end
 
−
 
−
--[[
 
−
This function sets default title types (equivalent to the citation including |type=<default value>) for those citations that have defaults.
 
−
Also handles the special case where it is desireable to omit the title type from the rendered citation (|type=none).
 
−
]]
 
−
function set_titletype(cite_class, title_type)
 
−
if is_set(title_type) then
 
−
if "none" == title_type then
 
−
title_type = ""; -- if |type=none then type parameter not displayed
 
−
end
 
−
return title_type; -- if |type= has been set to any other value use that value
 
−
end
 
−
 
−
if "AV media notes" == cite_class or "DVD notes" == cite_class then -- if this citation is cite AV media notes or cite DVD notes
 
−
return "Media notes"; -- display AV media notes / DVD media notes annotation
 
−
 
−
elseif "podcast" == cite_class then -- if this citation is cite podcast
 
−
return "Podcast"; -- display podcast annotation
 
−
 
−
elseif "pressrelease" == cite_class then -- if this citation is cite press release
 
−
return "Press release"; -- display press release annotation
 
−
 
−
elseif "techreport" == cite_class then -- if this citation is cite techreport
 
−
return "Technical report"; -- display techreport annotation
 
−
 
−
elseif "thesis" == cite_class then -- if this citation is cite thesis (degree option handled after this function returns)
 
−
return "Thesis"; -- display simple thesis annotation (without |degree= modification)
 
−
end
 
−
end
 
−
 
−
--[[
 
−
Determines whether a URL string is valid
 
−
 
−
At present the only check is whether the string appears to
 
−
be prefixed with a URI scheme.  It is not determined whether
 
−
the URI scheme is valid or whether the URL is otherwise well
 
−
formed.
 
−
]]
 
−
function checkurl( url_str )
 
−
    -- Protocol-relative or URL scheme
 
−
    return url_str:sub(1,2) == "//" or url_str:match( "^[^/]*:" ) ~= nil;
 
−
end
 
−
 
−
-- Removes irrelevant text and dashes from ISBN number
 
−
-- Similar to that used for Special:BookSources
 
−
function cleanisbn( isbn_str )
 
−
    return isbn_str:gsub( "[^-0-9X]", "" );
 
−
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)
 
−
if not is_set (pages) then return pages; end -- if no page numbers then we're done
 
−
 
−
    while true do
 
−
pattern = pages:match("%[([%w/:\.]+%s+)[%w%d].*%]"); -- pattern is the opening bracket, the url and following space(s): "[url "
 
−
if nil == pattern then break; end -- no more urls
 
−
pages = pages:gsub(pattern, ""); -- remove as many instances of pattern as possible
 
−
end
 
−
pages = pages:gsub("[%[%]]", ""); -- remove the brackets
 
−
pages = pages:gsub("–", "-" ); -- replace endashes with hyphens
 
−
pages = pages:gsub("&%w+;", "-" ); -- and replace html entities (&ndash; etc) with hyphens; do we need to replace numerical entities like &#32; and the like?
 
−
return pages;
 
−
end
 
−
 
−
--[[
 
−
ISBN-10 and ISSN validator code calculates checksum across all isbn/issn digits including the check digit. ISBN-13 is checked in checkisbn().
 
−
If the number is valid the result will be 0. Before calling this function, issbn/issn must be checked for length and stripped of dashes,
 
−
spaces and other non-isxn characters.
 
−
]]
 
−
function is_valid_isxn (isxn_str, len)
 
−
local temp = 0;
 
−
isxn_str = { isxn_str:byte(1, len) }; -- make a table of bytes
 
−
len = len+1; -- adjust to be a loop counter
 
−
for i, v in ipairs( isxn_str ) do -- loop through all of the bytes and calculate the checksum
 
−
if v == string.byte( "X" ) then -- if checkdigit is X
 
−
temp = temp + 10*( len - i ); -- it represents 10 decimal
 
−
else
 
−
temp = temp + tonumber( string.char(v) )*(len-i);
 
−
end
 
−
end
 
−
return temp % 11 == 0; -- returns true if calculation result is zero
 
−
end
 
−
 
−
-- Determines whether an ISBN string is valid
 
−
function checkisbn( isbn_str )
 
−
if nil ~= isbn_str:match("[^%s-0-9X]") then return false; end -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X
 
−
isbn_str = isbn_str:gsub( "-", "" ):gsub( " ", "" ); -- remove hyphens and spaces
 
−
local len = isbn_str:len();
 
−
 
−
if len ~= 10 and len ~= 13 then
 
−
return false;
 
−
end
 
−
 
−
if len == 10 then
 
−
if isbn_str:match( "^%d*X?$" ) == nil then return false; end
 
−
return is_valid_isxn(isbn_str, 10);
 
−
else
 
−
local temp = 0;
 
−
if isbn_str:match( "^97[89]%d*$" ) == nil then return false; end -- isbn13 begins with 978 or 979
 
−
isbn_str = { isbn_str:byte(1, len) };
 
−
for i, v in ipairs( isbn_str ) do
 
−
temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) );
 
−
end
 
−
return temp % 10 == 0;
 
−
end
 
−
end
 
−
 
−
-- Gets the display text for a wikilink like [[A|B]] or [[B]] gives B
 
−
function removewikilink( str )
 
−
    return (str:gsub( "%[%[([^%[%]]*)%]%]", function(l)
 
−
        return l:gsub( "^[^|]*|(.*)$", "%1" ):gsub("^%s*(.-)%s*$", "%1");
 
−
    end));
 
−
end
 
−
 
−
-- Escape sequences for content that will be used for URL descriptions
 
−
function safeforurl( str )
 
−
    if str:match( "%[%[.-%]%]" ) ~= nil then
 
−
        table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } );
 
−
    end
 
−
   
 
−
    return str:gsub( '[%[%]\n]', {   
 
−
        ['['] = '&#91;',
 
−
        [']'] = '&#93;',
 
−
        ['\n'] = ' ' } );
 
−
end
 
−
 
−
-- Converts a hyphen to a dash
 
−
function hyphentodash( str )
 
−
    if not is_set(str) or str:match( "[%[%]{}<>]" ) ~= nil then
 
−
        return str;
 
−
    end   
 
−
    return str:gsub( '-', '–' );
 
−
end
 
−
 
−
-- Protects a string that will be wrapped in wiki italic markup '' ... ''
 
−
function safeforitalics( str )
 
−
    --[[ Note: We can not use <i> for italics, as the expected behavior for
 
−
    italics specified by ''...'' in the title is that they will be inverted
 
−
    (i.e. unitalicized) in the resulting references.  In addition, <i> and ''
 
−
    tend to interact poorly under Mediawiki's HTML tidy. ]]
 
−
   
 
−
    if not is_set(str) then
 
−
        return str;
 
−
    else
 
−
        if str:sub(1,1) == "'" then str = "<span />" .. str; end
 
−
        if str:sub(-1,-1) == "'" then str = str .. "<span />"; end
 
−
       
 
−
        -- Remove newlines as they break italics.
 
−
        return str:gsub( '\n', ' ' );
 
−
    end
 
−
end
 
−
 
−
--[[
 
−
Joins a sequence of strings together while checking for duplicate separation
 
−
characters.
 
−
]]
 
−
function safejoin( tbl, duplicate_char )
 
−
    --[[
 
−
    Note: we use string functions here, rather than ustring functions.
 
−
   
 
−
    This has considerably faster performance and should work correctly as
 
−
    long as the duplicate_char is strict ASCII.  The strings
 
−
    in tbl may be ASCII or UTF8.
 
−
    ]]
 
−
   
 
−
    local str = '';
 
−
    local comp = '';
 
−
    local end_chr = '';
 
−
    local trim;
 
−
    for _, value in ipairs( tbl ) do
 
−
        if value == nil then value = ''; end
 
−
       
 
−
        if str == '' then
 
−
            str = value;
 
−
        elseif value ~= '' then
 
−
            if value:sub(1,1) == '<' then
 
−
                -- Special case of values enclosed in spans and other markup.
 
−
                comp = value:gsub( "%b<>", "" );
 
−
            else
 
−
                comp = value;
 
−
            end
 
−
           
 
−
            if comp:sub(1,1) == duplicate_char then
 
−
                trim = false;
 
−
                end_chr = str:sub(-1,-1);
 
−
                -- str = str .. "<HERE(enchr=" .. end_chr.. ")"
 
−
                if end_chr == duplicate_char then
 
−
                    str = str:sub(1,-2);
 
−
                elseif end_chr == "'" then
 
−
                    if str:sub(-3,-1) == duplicate_char .. "''" then
 
−
                        str = str:sub(1, -4) .. "''";
 
−
                    elseif str:sub(-5,-1) == duplicate_char .. "]]''" then
 
−
                        trim = true;
 
−
                    elseif str:sub(-4,-1) == duplicate_char .. "]''" then
 
−
                        trim = true;
 
−
                    end
 
−
                elseif end_chr == "]" then
 
−
                    if str:sub(-3,-1) == duplicate_char .. "]]" then
 
−
                        trim = true;
 
−
                    elseif str:sub(-2,-1) == duplicate_char .. "]" then
 
−
                        trim = true;
 
−
                    end
 
−
                elseif end_chr == " " then
 
−
                    if str:sub(-2,-1) == duplicate_char .. " " then
 
−
                        str = str:sub(1,-3);
 
−
                    end
 
−
                end
 
−
 
−
                if trim then
 
−
                    if value ~= comp then
 
−
                        local dup2 = duplicate_char;
 
−
                        if dup2:match( "%A" ) then dup2 = "%" .. dup2; end
 
−
                       
 
−
                        value = value:gsub( "(%b<>)" .. dup2, "%1", 1 )
 
−
                    else
 
−
                        value = value:sub( 2, -1 );
 
−
                    end
 
−
                end
 
−
            end
 
−
            str = str .. value;
 
−
        end
 
−
    end
 
−
    return str;
 
−
end 
 
−
 
−
-- Attempts to convert names to initials.
 
−
function reducetoinitials(first)
 
−
    local initials = {}
 
−
    for word in string.gmatch(first, "%S+") do
 
−
        table.insert(initials, string.sub(word,1,1)) -- Vancouver format does not include full stops.
 
−
    end
 
−
    return table.concat(initials) -- Vancouver format does not include spaces.
 
−
end
 
−
 
−
-- Formats a list of people (e.g. authors / editors)
 
−
function listpeople(control, people)
 
−
    local sep = control.sep;
 
−
    local namesep = control.namesep
 
−
    local format = control.format
 
−
    local maximum = control.maximum
 
−
    local lastauthoramp = control.lastauthoramp;
 
−
    local text = {}
 
−
    local etal = false;
 
−
   
 
−
    if sep:sub(-1,-1) ~= " " then sep = sep .. " " end
 
−
    if maximum ~= nil and maximum < 1 then return "", 0; end
 
−
   
 
−
    for i,person in ipairs(people) do
 
−
        if is_set(person.last) then
 
−
            local mask = person.mask
 
−
            local one
 
−
            local sep_one = sep;
 
−
            if maximum ~= nil and i > maximum then
 
−
                etal = true;
 
−
                break;
 
−
            elseif (mask ~= nil) then
 
−
                local n = tonumber(mask)
 
−
                if (n ~= nil) then
 
−
                    one = string.rep("&mdash;",n)
 
−
                else
 
−
                    one = mask;
 
−
                    sep_one = " ";
 
−
                end
 
−
            else
 
−
                one = person.last
 
−
                local first = person.first
 
−
                if is_set(first) then
 
−
                    if ( "vanc" == format ) then first = reducetoinitials(first) end
 
−
                    one = one .. namesep .. first
 
−
                end
 
−
                if is_set(person.link) then one = "[[" .. person.link .. "|" .. one .. "]]" end
 
−
                if is_set(person.link) and nil ~= person.link:find("//") then one = one .. " " .. seterror( 'bad_authorlink' ) end -- check for url in author link;
 
−
            end
 
−
            table.insert( text, one )
 
−
            table.insert( text, sep_one )
 
−
        end
 
−
    end
 
−
 
−
    local count = #text / 2;
 
−
    if count > 0 then
 
−
        if count > 1 and is_set(lastauthoramp) and not etal then
 
−
            text[#text-2] = " & ";
 
−
        end
 
−
        text[#text] = nil;
 
−
    end
 
−
   
 
−
    local result = table.concat(text) -- construct list
 
−
    if etal then
 
−
        local etal_text = cfg.messages['et al'];
 
−
        result = result .. " " .. etal_text;
 
−
    end
 
−
   
 
−
    -- if necessary wrap result in <span> tag to format in Small Caps
 
−
    if ( "scap" == format ) then result =
 
−
        '<span class="smallcaps" style="font-variant:small-caps">' .. result .. '</span>';
 
−
    end
 
−
    return result, count
 
−
end
 
−
 
−
-- Generates a CITEREF anchor ID.
 
−
function anchorid( options )
 
−
    return "CITEREF" .. table.concat( options );
 
−
end
 
−
 
−
-- Gets name list from the input arguments
 
−
function extractnames(args, list_name)
 
−
    local names = {};
 
−
    local i = 1;
 
−
    local last;
 
−
   
 
−
    while true do
 
−
        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
 
−
 
−
-- Populates ID table from arguments using configuration settings
 
−
function extractids( args )
 
−
    local id_list = {};
 
−
    for k, v in pairs( cfg.id_handlers ) do   
 
−
        v = selectone( args, v.parameters, 'redundant_parameters' );
 
−
        if is_set(v) then id_list[k] = v; end
 
−
    end
 
−
    return id_list;
 
−
end
 
−
 
−
-- Takes a table of IDs and turns it into a table of formatted ID outputs.
 
−
function buildidlist( id_list, options )
 
−
    local new_list, handler = {};
 
−
   
 
−
    function fallback(k) return { __index = function(t,i) return cfg.id_handlers[k][i] end } end;
 
−
   
 
−
    for k, v in pairs( id_list ) do
 
−
        -- fallback to read-only cfg
 
−
        handler = setmetatable( { ['id'] = v }, fallback(k) );
 
−
       
 
−
        if handler.mode == 'external' then
 
−
            table.insert( new_list, {handler.label, externallinkid( handler ) } );
 
−
        elseif handler.mode == 'internal' then
 
−
            table.insert( new_list, {handler.label, internallinkid( handler ) } );
 
−
        elseif handler.mode ~= 'manual' then
 
−
            error( cfg.messages['unknown_ID_mode'] );
 
−
        elseif k == 'DOI' then
 
−
            table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } );
 
−
        elseif k == 'ASIN' then
 
−
            table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } );
 
−
        elseif k == 'LCCN' then
 
−
            table.insert( new_list, {handler.label, lccn( v ) } );
 
−
        elseif k == 'OL' then
 
−
            table.insert( new_list, {handler.label, openlibrary( v ) } );
 
−
        elseif k == 'PMC' then
 
−
            table.insert( new_list, {handler.label, pmc( v, options.Embargo ) } );
 
−
        elseif k == 'PMID' then
 
−
            table.insert( new_list, {handler.label, pmid( v ) } );
 
−
        elseif k == 'ISSN' then
 
−
        table.insert( new_list, {handler.label, issn( v ) } );
 
−
        elseif k == 'ISBN' then
 
−
            local ISBN = internallinkid( handler );
 
−
            if not checkisbn( v ) and not is_set(options.IgnoreISBN) then
 
−
                ISBN = ISBN .. seterror( 'bad_isbn', {}, false, " ", "" );
 
−
            end
 
−
            table.insert( new_list, {handler.label, ISBN } );               
 
−
        else
 
−
            error( cfg.messages['unknown_manual_ID'] );
 
−
        end
 
−
    end
 
−
   
 
−
    function comp( a, b ) -- used in following table.sort()
 
−
        return a[1] < b[1];
 
−
    end
 
−
   
 
−
    table.sort( new_list, comp );
 
−
    for k, v in ipairs( new_list ) do
 
−
        new_list[k] = v[2];
 
−
    end
 
−
   
 
−
    return new_list;
 
−
end
 
−
 
 
−
-- Chooses one matching parameter from a list of parameters to consider
 
−
-- Generates an error if more than one match is present.
 
−
function selectone( args, possible, error_condition, index )
 
−
    local value = nil;
 
−
    local selected = '';
 
−
    local error_list = {};
 
−
   
 
−
    if index ~= nil then index = tostring(index); end
 
−
   
 
−
    -- Handle special case of "#" replaced by empty string
 
−
    if index == '1' then
 
−
        for _, v in ipairs( possible ) do
 
−
            v = v:gsub( "#", "" );
 
−
            if is_set(args[v]) then
 
−
                if value ~= nil and selected ~= v then
 
−
                    table.insert( error_list, v );
 
−
                else
 
−
                    value = args[v];
 
−
                    selected = v;
 
−
                end
 
−
            end
 
−
        end       
 
−
    end
 
−
   
 
−
    for _, v in ipairs( possible ) do
 
−
        if index ~= nil then
 
−
            v = v:gsub( "#", index );
 
−
        end
 
−
        if is_set(args[v]) then
 
−
            if value ~= nil and selected ~=  v then
 
−
                table.insert( error_list, v );
 
−
            else
 
−
                value = args[v];
 
−
                selected = v;
 
−
            end
 
−
        end
 
−
    end
 
−
   
 
−
    if #error_list > 0 then
 
−
        local error_str = "";
 
−
        for _, k in ipairs( error_list ) do
 
−
            if error_str ~= "" then error_str = error_str .. cfg.messages['parameter-separator'] end
 
−
            error_str = error_str .. wrap( 'parameter', k );
 
−
        end
 
−
        if #error_list > 1 then
 
−
            error_str = error_str .. cfg.messages['parameter-final-separator'];
 
−
        else
 
−
            error_str = error_str .. cfg.messages['parameter-pair-separator'];
 
−
        end
 
−
        error_str = error_str .. wrap( 'parameter', selected );
 
−
        table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } );
 
−
    end
 
−
   
 
−
    return value, selected;
 
−
end
 
−
 
−
-- COinS metadata (see <http://ocoins.info/>) allows automated tools to parse
 
−
-- the citation information.
 
−
function COinS(data)
 
−
    if 'table' ~= type(data) or nil == next(data) then
 
−
        return '';
 
−
    end
 
−
   
 
−
    local ctx_ver = "Z39.88-2004";
 
−
   
 
−
    -- treat table strictly as an array with only set values.
 
−
    local OCinSoutput = setmetatable( {}, {
 
−
        __newindex = function(self, key, value)
 
−
            if is_set(value) then
 
−
                rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( removewikilink( value ) ) } );
 
−
            end
 
−
        end
 
−
    });
 
−
   
 
−
    if is_set(data.Chapter) then
 
−
        OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
 
−
        OCinSoutput["rft.genre"] = "bookitem";
 
−
        OCinSoutput["rft.btitle"] = data.Chapter;
 
−
        OCinSoutput["rft.atitle"] = data.Title;
 
−
    elseif is_set(data.Periodical) then
 
−
        OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal";
 
−
        OCinSoutput["rft.genre"] = "article";
 
−
        OCinSoutput["rft.jtitle"] = data.Periodical;
 
−
        OCinSoutput["rft.atitle"] = data.Title;
 
−
    else
 
−
        OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
 
−
        OCinSoutput["rft.genre"] = "book"
 
−
        OCinSoutput["rft.btitle"] = data.Title;
 
−
    end
 
−
   
 
−
    OCinSoutput["rft.place"] = data.PublicationPlace;
 
−
    OCinSoutput["rft.date"] = data.Date;
 
−
    OCinSoutput["rft.series"] = data.Series;
 
−
    OCinSoutput["rft.volume"] = data.Volume;
 
−
    OCinSoutput["rft.issue"] = data.Issue;
 
−
    OCinSoutput["rft.pages"] = data.Pages;
 
−
    OCinSoutput["rft.edition"] = data.Edition;
 
−
    OCinSoutput["rft.pub"] = data.PublisherName;
 
−
   
 
−
    for k, v in pairs( data.ID_list ) do
 
−
        local id, value = cfg.id_handlers[k].COinS;
 
−
        if k == 'ISBN' then value = cleanisbn( v ); else value = v; end
 
−
        if string.sub( id or "", 1, 4 ) == 'info' then
 
−
            OCinSoutput["rft_id"] = table.concat{ id, "/", v };
 
−
        else
 
−
            OCinSoutput[ id ] = value;
 
−
        end
 
−
    end
 
−
   
 
−
    local last, first;
 
−
    for k, v in ipairs( data.Authors ) do
 
−
        last, first = v.last, v.first;
 
−
        if k == 1 then
 
−
            if is_set(last) then
 
−
                OCinSoutput["rft.aulast"] = last;
 
−
            end
 
−
            if is_set(first) then
 
−
                OCinSoutput["rft.aufirst"] = first;
 
−
            end
 
−
        end
 
−
        if is_set(last) and is_set(first) then
 
−
            OCinSoutput["rft.au"] = table.concat{ last, ", ", first };
 
−
        elseif is_set(last) then
 
−
            OCinSoutput["rft.au"] = last;
 
−
        end
 
−
    end
 
−
   
 
−
    OCinSoutput.rft_id = data.URL;
 
−
    OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage };
 
−
    OCinSoutput = setmetatable( OCinSoutput, nil );
 
−
   
 
−
    -- sort with version string always first, and combine.
 
−
    table.sort( OCinSoutput );
 
−
    table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver );  -- such as "Z39.88-2004"
 
−
    return table.concat(OCinSoutput, "&");
 
−
end
 
−
 
−
--[[
 
−
This is the main function doing the majority of the citation
 
−
formatting.
 
−
]]
 
−
function citation0( config, args)
 
−
    --[[
 
−
    Load Input Parameters
 
−
    The argment_wrapper facillitates 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 BookTitle = A['BookTitle'];
 
−
    local Conference = A['Conference'];
 
−
    local TransTitle = A['TransTitle'];
 
−
    local TitleNote = A['TitleNote'];
 
−
    local TitleLink = A['TitleLink'];
 
−
    local Chapter = A['Chapter'];
 
−
    local ChapterLink = A['ChapterLink'];
 
−
    local TransChapter = A['TransChapter'];
 
−
    local TitleType = A['TitleType'];
 
−
    local Degree = A['Degree'];
 
−
    local Docket = A['Docket'];
 
−
    local ArchiveURL = A['ArchiveURL'];
 
−
    local URL = A['URL']
 
−
    local URLorigin = A:ORIGIN('URL');
 
−
    local ChapterURL = A['ChapterURL'];
 
−
    local ChapterURLorigin = A:ORIGIN('ChapterURL');
 
−
    local ConferenceURL = A['ConferenceURL'];
 
−
    local ConferenceURLorigin = A:ORIGIN('ConferenceURL');
 
−
    local Periodical = A['Periodical'];
 
−
 
−
local Series = A['Series'];
 
−
    local Volume = A['Volume'];
 
−
    local Issue = A['Issue'];
 
−
    local Position = '';
 
−
    local Page = A['Page'];
 
−
    local Pages = hyphentodash( A['Pages'] );
 
−
    local At = A['At'];
 
−
 
−
    local Edition = A['Edition'];
 
−
    local PublicationPlace = A['PublicationPlace']
 
−
    local Place = A['Place'];
 
−
   
 
−
    local PublisherName = A['PublisherName'];
 
−
    local RegistrationRequired = A['RegistrationRequired'];
 
−
    local SubscriptionRequired = A['SubscriptionRequired'];
 
−
    local Via = A['Via'];
 
−
    local AccessDate = A['AccessDate'];
 
−
    local ArchiveDate = A['ArchiveDate'];
 
−
    local Agency = A['Agency'];
 
−
    local DeadURL = A['DeadURL']
 
−
    local Language = A['Language'];
 
−
    local Format = A['Format'];
 
−
    local Ref = A['Ref'];
 
−
local DoiBroken = A['DoiBroken'];
 
−
local ID = A['ID'];
 
−
    local ASINTLD = A['ASINTLD'];
 
−
    local IgnoreISBN = A['IgnoreISBN'];
 
−
    local Embargo = A['Embargo'];
 
−
 
−
    local ID_list = extractids( args );
 
−
   
 
−
    local Quote = A['Quote'];
 
−
    local PostScript = A['PostScript'];
 
−
 
−
    local LayURL = A['LayURL'];
 
−
    local LaySource = A['LaySource'];
 
−
    local Transcript = A['Transcript'];
 
−
    local TranscriptURL = A['TranscriptURL']
 
−
    local TranscriptURLorigin = A:ORIGIN('TranscriptURL');
 
−
    local sepc = A['Separator'];
 
−
 
−
    local LastAuthorAmp = A['LastAuthorAmp'];
 
−
    local no_tracking_cats = A['NoTracking'];
 
−
 
−
--these are used by cite interview
 
−
local Callsign = A['Callsign'];
 
−
local City = A['City'];
 
−
local Cointerviewers = A['Cointerviewers']; -- deprecated
 
−
local Interviewer = A['Interviewer']; -- deprecated
 
−
local Program = A['Program'];
 
−
 
−
--local variables that are not cs1 parameters
 
−
    local page_type; -- is this needed?  Doesn't appear to be used anywhere;
 
−
    local use_lowercase = ( sepc ~= '.' );
 
−
    local this_page = mw.title.getCurrentTitle(); --Also used for COinS and for language
 
−
local anchor_year; -- used in the CITEREF identifier
 
−
local COinS_date; -- used in the COinS metadata
 
−
 
−
-- Set postscript default.
 
−
if not is_set (PostScript) then -- if |postscript= has not been set (Postscript is nil which is the default for {{citation}}) and
 
−
if (config.CitationClass ~= "citation") then -- this template is not a citation template
 
−
PostScript = '.'; -- must be a cite xxx template so set postscript to default (period)
 
−
end
 
−
else
 
−
if PostScript:lower() == 'none' then -- if |postscript=none then
 
−
PostScript = ''; -- no postscript
 
−
end
 
−
end
 
−
 
−
--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
 
−
for k, v in pairs( cfg.uncategorized_namespaces ) do -- otherwise, spin through the list of namespaces we don't include in error categories
 
−
if this_page.nsText == v then -- if we find one
 
−
no_tracking_cats = "true"; -- set no_trackin_cats
 
−
break; -- and we're done
 
−
            end
 
−
        end
 
−
    end
 
−
 
−
-- check for extra |page=, |pages= or |at= parameters.
 
−
    if is_set(Page) then
 
−
        if is_set(Pages) or is_set(At) then
 
−
            Page = Page .. " " .. seterror('extra_pages'); -- add error message
 
−
            Pages = ''; -- unset the others
 
−
            At = '';
 
−
        end
 
−
    elseif is_set(Pages) then
 
−
        if is_set(At) then
 
−
            Pages = Pages .. " " .. seterror('extra_pages'); -- add error messages
 
−
            At = ''; -- unset
 
−
        end
 
−
    end   
 
−
 
−
-- both |publication-place= and |place= (|location=) allowed if different
 
−
    if not is_set(PublicationPlace) and is_set(Place) then
 
−
        PublicationPlace = Place; -- promote |place= (|location=) to |publication-place
 
−
    end
 
−
   
 
−
    if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
 
−
   
 
−
--[[
 
−
Parameter remapping for cite encyclopedia:
 
−
When the citation has these parameters:
 
−
|encyclopedia and |title then map |title to |article and |encyclopedia to |title
 
−
|encyclopedia and |article then map |encyclopedia to |title
 
−
|encyclopedia then map |encyclopedia to |title
 
−
 
−
|trans_title maps to |trans_chapter when |title is re-mapped
 
−
 
−
All other combinations of |encyclopedia, |title, and |article are not modified
 
−
]]
 
−
if ( config.CitationClass == "encyclopaedia" ) then
 
−
if is_set(Periodical) then -- Periodical is set when |encyclopedia is set
 
−
if is_set(Title) then
 
−
if not is_set(Chapter) then
 
−
Chapter = Title; -- |encyclopedia and |title are set so map |title to |article and |encyclopedia to |title
 
−
TransChapter = TransTitle;
 
−
Title = Periodical;
 
−
Periodical = ''; -- redundant so unset
 
−
TransTitle = ''; -- redundant so unset
 
−
end
 
−
else -- |title not set
 
−
Title = Periodical; -- |encyclopedia set and |article set or not set so map |encyclopedia to |title
 
−
Periodical = ''; -- redundant so unset
 
−
end
 
−
end
 
−
end
 
−
 
−
--special cases for citation.
 
−
if (config.CitationClass == "citation") then -- for citation templates
 
−
if not is_set (Ref) then -- if |ref= is not set
 
−
Ref = "harv"; -- set default |ref=harv
 
−
end
 
−
if not is_set (sepc) then -- if |separator= is not set
 
−
sepc = ','; -- set citation separator to its default (comma)
 
−
end
 
−
else -- not a citation template
 
−
if not is_set (sepc) then -- if |separator= has not been set
 
−
sepc = '.'; -- set cite xxx separator to its default (period)
 
−
end
 
−
end
 
−
 
−
-- check for specital case where |separator=none
 
−
if 'none' == sepc:lower() then -- if |separator=none
 
−
sepc = ''; -- then set it to a empty string
 
−
end
 
−
 
−
-- Special case for cite techreport.
 
−
if (config.CitationClass == "techreport") then -- special case for cite techreport
 
−
if is_set(Issue) then -- cite techreport uses 'number', which other citations aliase to 'issue'
 
−
if not is_set(ID) then -- can we use ID for the "number"?
 
−
ID = Issue; -- yes, use it
 
−
Issue = ""; -- unset Issue so that "number" isn't duplicated in the rendered citation or COinS metadata
 
−
else -- can't use ID so emit error message
 
−
ID = ID .. " " .. seterror('redundant_parameters', '<code>&#124;id=</code> and <code>&#124;number=</code>');
 
−
end
 
−
end
 
−
end
 
−
 
−
-- special case for cite interview
 
−
if (config.CitationClass == "interview") then
 
−
if is_set(Program) then
 
−
ID = ' ' .. Program;
 
−
end
 
−
if is_set(Callsign) then
 
−
if is_set(ID) then
 
−
ID = ID .. sepc .. ' ' .. Callsign;
 
−
else
 
−
ID = ' ' .. Callsign;
 
−
end
 
−
end
 
−
if is_set(City) then
 
−
if is_set(ID) then
 
−
ID = ID .. sepc .. ' ' .. City;
 
−
else
 
−
ID = ' ' .. City;
 
−
end
 
−
end
 
−
 
−
if is_set(Interviewer) then
 
−
if is_set(TitleType) then
 
−
Others = ' ' .. TitleType .. ' with ' .. Interviewer;
 
−
TitleType = '';
 
−
else
 
−
Others = ' ' .. 'Interview with ' .. Interviewer;
 
−
end
 
−
if is_set(Cointerviewers) then
 
−
Others = Others .. sepc .. ' ' .. Cointerviewers;
 
−
end
 
−
else
 
−
Others = '(Interview)';
 
−
end
 
−
end
 
−
 
−
--Account for the oddity that is {{cite journal}} with |pmc= set and |url= not set
 
−
if config.CitationClass == "journal" and not is_set(URL) and is_set(ID_list['PMC']) then
 
−
if not is_embargoed(Embargo) then
 
−
URL=cfg.id_handlers['PMC'].prefix .. ID_list['PMC']; -- set url to be the same as the PMC external link if not embargoed
 
−
URLorigin = cfg.id_handlers['PMC'].parameters[1]; -- set URLorigin to parameter name for use in error message if citation is missing a |title=
 
−
end
 
−
end
 
−
 
−
-- Account for the oddity that is {{cite conference}}, before generation of COinS data.
 
−
--TODO: if this is only for {{cite conference}}, shouldn't we be checking? (if config.CitationClass=='conference' then ...)
 
−
if is_set(BookTitle) then
 
−
Chapter = Title;
 
−
ChapterLink = TitleLink;
 
−
TransChapter = TransTitle;
 
−
Title = BookTitle;
 
−
TitleLink = '';
 
−
TransTitle = '';
 
−
end
 
−
 
−
-- Account for the oddity that is {{cite episode}}, before generation of COinS data.
 
−
--[[ -- {{cite episode}} is not currently supported by this module
 
−
if config.CitationClass == "episode" then
 
−
local AirDate = A['AirDate'];
 
−
local SeriesLink = A['SeriesLink'];
 
−
local Season = A['Season'];
 
−
local SeriesNumber = A['SeriesNumber'];
 
−
local Network = A['Network'];
 
−
local Station = A['Station'];
 
−
local s, n = {}, {};
 
−
local Sep = (first_set(A["SeriesSeparator"], A["Separator"]) or "") .. " ";
 
−
 
−
if is_set(Issue) then table.insert(s, cfg.messages["episode"] .. " " .. Issue); Issue = ''; end
 
−
if is_set(Season) then table.insert(s, cfg.messages["season"] .. " " .. Season); end
 
−
if is_set(SeriesNumber) then table.insert(s, cfg.messages["series"] .. " " .. SeriesNumber); end
 
−
if is_set(Network) then table.insert(n, Network); end
 
−
if is_set(Station) then table.insert(n, Station); end
 
−
 
−
Date = Date or AirDate;
 
−
Chapter = Title;
 
−
ChapterLink = TitleLink;
 
−
TransChapter = TransTitle;
 
−
Title = Series;
 
−
TitleLink = SeriesLink;
 
−
TransTitle = '';
 
−
 
−
Series = table.concat(s, Sep);
 
−
ID = table.concat(n, Sep);
 
−
end
 
−
-- end of {{cite episode}} stuff]]
 
−
 
−
-- legacy: promote concatenation of |day=, |month=, and |year= to Date if Date not set; or, promote PublicationDate to Date if neither Date nor Year are set.
 
−
if not is_set(Date) then
 
−
Date = Year; -- promote Year to Date
 
−
Year = nil; -- make nil so Year as empty string isn't used for CITEREF
 
−
if is_set(Date) then
 
−
local Month = A['Month'];
 
−
if is_set(Month) then
 
−
Date = Month .. " " .. Date;
 
−
local Day = A['Day']
 
−
if is_set(Day) then Date = Day .. " " .. Date end
 
−
end
 
−
elseif is_set(PublicationDate) then -- use PublicationDate when |date= and |year= are not set
 
−
Date = PublicationDate; -- promonte PublicationDate to Date
 
−
PublicationDate = ''; -- unset, no longer needed
 
−
end
 
−
end
 
−
 
−
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
 
−
we get the date used in the metadata.
 
−
 
−
Date validation supporting code is in Module:Citation/CS1/Date_validation
 
−
]]
 
−
anchor_year, COinS_date, error_message = dates({['accessdate']=AccessDate, ['airdate']=AirDate, ['archivedate']=ArchiveDate, ['date']=Date, ['doi_brokendate']=DoiBroken,
 
−
['embargo']=Embargo, ['laydate']=LayDate, ['publicationdate']=PublicationDate, ['year']=Year});
 
−
if is_set(error_message) then
 
−
table.insert( z.message_tail, { seterror( 'bad_date', {error_message}, true ) } ); -- add this error message
 
−
end
 
−
 
−
-- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.
 
−
 
−
    -- COinS metadata (see <http://ocoins.info/>) for
 
−
    -- automated parsing of citation information.
 
−
    local OCinSoutput = COinS{
 
−
        ['Periodical'] = Periodical,
 
−
        ['Chapter'] = Chapter,
 
−
        ['Title'] = Title,
 
−
        ['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?
 
−
        ['Series'] = Series,
 
−
        ['Volume'] = Volume,
 
−
        ['Issue'] = Issue,
 
−
        ['Pages'] = get_coins_pages (first_set(Page, Pages, At)), -- pages stripped of external links
 
−
        ['Edition'] = Edition,
 
−
        ['PublisherName'] = PublisherName,
 
−
        ['URL'] = first_set( URL, ChapterURL ),
 
−
        ['Authors'] = a,
 
−
        ['ID_list'] = ID_list,
 
−
        ['RawPage'] = this_page.prefixedText,
 
−
    };
 
−
 
−
    if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then
 
−
        Chapter = Title;
 
−
        ChapterLink = TitleLink;
 
−
        TransChapter = TransTitle;
 
−
        Title = '';
 
−
        TitleLink = '';
 
−
        TransTitle = '';
 
−
    end
 
−
 
−
    -- Now perform various field substitutions.
 
−
    -- We also add leading spaces and surrounding markup and punctuation to the
 
−
    -- various parts of the citation, but only when they are non-nil.
 
−
    if not is_set(Authors) then
 
−
        local Maximum = tonumber( A['DisplayAuthors'] );
 
−
       
 
−
        -- Preserve old-style implicit et al.
 
−
        if not is_set(Maximum) and #a == 9 then
 
−
            Maximum = 8;
 
−
            table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } );
 
−
        elseif not is_set(Maximum) then
 
−
            Maximum = #a + 1;
 
−
        end
 
−
           
 
−
        local control = {
 
−
            sep = A["AuthorSeparator"] .. " ",
 
−
            namesep = (first_set(A["AuthorNameSeparator"], A["NameSeparator"]) or "") .. " ",
 
−
            format = A["AuthorFormat"],
 
−
            maximum = Maximum,
 
−
            lastauthoramp = LastAuthorAmp
 
−
        };
 
−
       
 
−
        -- If the coauthor field is also used, prevent ampersand and et al. formatting.
 
−
        if is_set(Coauthors) then
 
−
            control.lastauthoramp = nil;
 
−
            control.maximum = #a + 1;
 
−
        end
 
−
       
 
−
        Authors = listpeople(control, a)
 
−
    end
 
−
 
−
if not is_set(Authors) and is_set(Coauthors) then -- coauthors aren't displayed if one of authors=, authorn=, or lastn= isn't specified
 
−
table.insert( z.message_tail, { seterror('coauthors_missing_author', {}, true) } ); -- emit error message
 
−
end
 
−
 
−
    local EditorCount
 
−
    if not is_set(Editors) then
 
−
        local Maximum = tonumber( A['DisplayEditors'] );
 
−
        -- Preserve old-style implicit et al.
 
−
        if not is_set(Maximum) and #e == 4 then
 
−
            Maximum = 3;
 
−
            table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } );
 
−
        elseif not is_set(Maximum) then
 
−
            Maximum = #e + 1;
 
−
        end
 
−
 
−
        local control = {
 
−
            sep = A["EditorSeparator"] .. " ",
 
−
            namesep = (first_set(A["EditorNameSeparator"], A["NameSeparator"]) or "") .. " ",
 
−
            format = A['EditorFormat'],
 
−
            maximum = Maximum,
 
−
            lastauthoramp = LastAuthorAmp
 
−
        };
 
−
 
−
        Editors, EditorCount = listpeople(control, e);
 
−
    else
 
−
        EditorCount = 1;
 
−
    end
 
−
 
−
    local Cartography = "";
 
−
    local Scale = "";
 
−
    if config.CitationClass == "map" then
 
−
        if not is_set( Authors ) and is_set( PublisherName ) then
 
−
            Authors = PublisherName;
 
−
            PublisherName = "";
 
−
        end
 
−
        Cartography = A['Cartography'];
 
−
        if is_set( Cartography ) then
 
−
            Cartography = sepc .. " " .. wrap( 'cartography', Cartography, use_lowercase );
 
−
        end       
 
−
        Scale = A['Scale'];
 
−
        if is_set( Scale ) then
 
−
            Scale = sepc .. " " .. Scale;
 
−
        end       
 
−
    end
 
−
   
 
−
    if  not is_set(URL) and
 
−
        not is_set(ChapterURL) and
 
−
        not is_set(ArchiveURL) and
 
−
        not is_set(ConferenceURL) and
 
−
        not is_set(TranscriptURL) then
 
−
       
 
−
        -- Test if cite web or cite podcast |url= is missing or empty
 
−
if inArray(config.CitationClass, {"web","podcast"}) then
 
−
table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } );
 
−
end
 
−
       
 
−
        -- Test if accessdate is given without giving a URL
 
−
        if is_set(AccessDate) then
 
−
            table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } );
 
−
            AccessDate = '';
 
−
        end
 
−
       
 
−
        -- Test if format is given without giving a URL
 
−
        if is_set(Format) then
 
−
            Format = Format .. seterror( 'format_missing_url' );
 
−
        end
 
−
    end
 
−
   
 
−
    -- Test if citation has no title
 
−
    if  not is_set(Chapter) and
 
−
        not is_set(Title) and
 
−
        not is_set(Periodical) and
 
−
        not is_set(Conference) and
 
−
        not is_set(TransTitle) and
 
−
        not is_set(TransChapter) then
 
−
        table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } );
 
−
    end
 
−
   
 
−
    Format = is_set(Format) and " (" .. Format .. ")" or "";
 
−
   
 
−
    local OriginalURL = URL
 
−
    DeadURL = DeadURL:lower();
 
−
    if is_set( ArchiveURL ) then
 
−
        if ( DeadURL ~= "no" ) then
 
−
            URL = ArchiveURL
 
−
            URLorigin = A:ORIGIN('ArchiveURL')
 
−
        end
 
−
    end
 
−
   
 
−
    -- Format chapter / article title
 
−
    if is_set(Chapter) and is_set(ChapterLink) then
 
−
        Chapter = "[[" .. ChapterLink .. "|" .. Chapter .. "]]";
 
−
    end
 
−
    if is_set(Periodical) and is_set(Title) then
 
−
        Chapter = wrap( 'italic-title', Chapter );
 
−
        TransChapter = wrap( 'trans-italic-title', TransChapter );
 
−
    else
 
−
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 = ""
 
−
    if is_set(TransChapter) then
 
−
        if not is_set(Chapter) then
 
−
            TransError = " " .. seterror( 'trans_missing_chapter' );
 
−
        else
 
−
            TransChapter = " " .. TransChapter;
 
−
        end
 
−
    end
 
−
   
 
−
    Chapter = Chapter .. TransChapter;
 
−
   
 
−
    if is_set(Chapter) then
 
−
        if not is_set(ChapterLink) then
 
−
            if is_set(ChapterURL) then
 
−
                Chapter = externallink( ChapterURL, Chapter ) .. TransError;
 
−
                if not is_set(URL) then
 
−
                    Chapter = Chapter .. Format;
 
−
                    Format = "";
 
−
                end
 
−
            elseif is_set(URL) then
 
−
                Chapter = externallink( URL, Chapter ) .. TransError .. Format;
 
−
                URL = "";
 
−
                Format = "";
 
−
            else
 
−
                Chapter = Chapter .. TransError;
 
−
            end           
 
−
        elseif is_set(ChapterURL) then
 
−
            Chapter = Chapter .. " " .. externallink( ChapterURL, nil, ChapterURLorigin ) ..
 
−
                TransError;
 
−
        else
 
−
            Chapter = Chapter .. TransError;
 
−
        end
 
−
        Chapter = Chapter .. sepc .. " " -- with end-space
 
−
    elseif is_set(ChapterURL) then
 
−
        Chapter = " " .. externallink( ChapterURL, nil, ChapterURLorigin ) .. sepc .. " ";
 
−
    end       
 
−
   
 
−
    -- Format main title.
 
−
    if is_set(TitleLink) and is_set(Title) then
 
−
        Title = "[[" .. TitleLink .. "|" .. Title .. "]]"
 
−
    end
 
−
   
 
−
    if is_set(Periodical) 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 );
 
−
        TransTitle = wrap( 'trans-quoted-title', TransTitle );
 
−
    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 );
 
−
        TransTitle = wrap( 'trans-quoted-title', TransTitle );
 
−
    else
 
−
        Title = wrap( 'italic-title', Title );
 
−
        TransTitle = wrap( 'trans-italic-title', TransTitle );
 
−
    end
 
−
   
 
−
    TransError = "";
 
−
    if is_set(TransTitle) then
 
−
        if not is_set(Title) then
 
−
            TransError = " " .. seterror( 'trans_missing_title' );
 
−
        else
 
−
            TransTitle = " " .. TransTitle;
 
−
        end
 
−
    end
 
−
   
 
−
    Title = Title .. TransTitle;
 
−
   
 
−
    if is_set(Title) then
 
−
        if not is_set(TitleLink) and is_set(URL) then
 
−
            Title = externallink( URL, Title ) .. TransError .. Format     
 
−
            URL = "";
 
−
            Format = "";
 
−
        else
 
−
            Title = Title .. TransError;
 
−
        end
 
−
    end
 
−
   
 
−
    if is_set(Place) then
 
−
        Place = " " .. wrap( 'written', Place, use_lowercase ) .. sepc .. " ";
 
−
    end
 
−
   
 
−
    if is_set(Conference) then
 
−
        if is_set(ConferenceURL) then
 
−
            Conference = externallink( ConferenceURL, Conference );
 
−
        end
 
−
        Conference = sepc .. " " .. Conference
 
−
    elseif is_set(ConferenceURL) then
 
−
        Conference = sepc .. " " .. externallink( ConferenceURL, nil, ConferenceURLorigin );
 
−
    end
 
−
   
 
−
    if not is_set(Position) then
 
−
        local Minutes = A['Minutes'];
 
−
        if is_set(Minutes) then
 
−
            Position = " " .. Minutes .. " " .. cfg.messages['minutes'];
 
−
        else
 
−
            local Time = A['Time'];
 
−
            if is_set(Time) then
 
−
                local TimeCaption = A['TimeCaption']
 
−
                if not is_set(TimeCaption) then
 
−
                    TimeCaption = cfg.messages['event'];
 
−
                    if sepc ~= '.' then
 
−
                        TimeCaption = TimeCaption:lower();
 
−
                    end
 
−
                end
 
−
                Position = " " .. TimeCaption .. " " .. Time;
 
−
            end
 
−
        end
 
−
    else
 
−
        Position = " " .. Position;
 
−
        At = '';
 
−
    end
 
−
   
 
−
    if not is_set(Page) then
 
−
        if is_set(Pages) then
 
−
            if is_set(Periodical) and
 
−
                not inArray(config.CitationClass, {"encyclopaedia","web","book","news","podcast"}) then
 
−
                Pages = ": " .. Pages;
 
−
            elseif tonumber(Pages) ~= nil then
 
−
                Pages = sepc .." " .. PPrefix .. Pages;
 
−
            else
 
−
                Pages = sepc .." " .. PPPrefix .. Pages;
 
−
            end
 
−
        end
 
−
    else
 
−
        if is_set(Periodical) and
 
−
            not inArray(config.CitationClass, {"encyclopaedia","web","book","news","podcast"}) then
 
−
            Page = ": " .. Page;
 
−
        else
 
−
            Page = sepc .." " .. PPrefix .. Page;
 
−
        end
 
−
    end
 
−
   
 
−
    At = is_set(At) and (sepc .. " " .. At) or "";
 
−
    Position = is_set(Position) and (sepc .. " " .. Position) or "";
 
−
    if config.CitationClass == 'map' then
 
−
        local Section = A['Section'];
 
−
        local Inset = A['Inset'];
 
−
        if first_set( Pages, Page, At ) ~= nil or sepc ~= '.' then
 
−
            if is_set( Section ) then
 
−
                Section = ", " .. wrap( 'section', Section, true );
 
−
            end
 
−
            if is_set( Inset ) then
 
−
                Inset = ", " .. wrap( 'inset', Inset, true );
 
−
            end
 
−
        else
 
−
            if is_set( Section ) then
 
−
                Section = sepc .. " " .. wrap( 'section', Section, use_lowercase );
 
−
                if is_set( Inset ) then
 
−
                    Inset = ", " .. wrap( 'inset', Inset, true );
 
−
                end
 
−
            elseif is_set( Inset ) then
 
−
                Inset = sepc .. " " .. wrap( 'inset', Inset, use_lowercase );
 
−
            end           
 
−
        end           
 
−
        At = At .. Section .. Inset;       
 
−
    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
 
−
-- local name = mw.language.fetchLanguageName( Language:lower(), "en" ); -- experiment: this seems to return correct ISO 639-1 language names
 
−
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
 
−
Language=""; -- language not specified so make sure this is an empty string;
 
−
end
 
−
 
−
Others = is_set(Others) and (sepc .. " " .. Others) or "";
 
−
 
−
-- handle type parameter for those CS1 citations that have default values
 
−
 
−
if inArray(config.CitationClass, {"AV media notes", "DVD notes", "podcast", "pressrelease", "techreport", "thesis"}) then
 
−
TitleType = set_titletype (config.CitationClass, TitleType);
 
−
if is_set(Degree) and "Thesis" == TitleType then -- special case for cite thesis
 
−
TitleType = Degree .. " thesis";
 
−
end
 
−
end
 
−
 
−
if is_set(TitleType) then -- if type parameter is specified
 
−
TitleType = " (" .. TitleType .. ")"; -- display it in parentheses
 
−
end
 
−
 
−
TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";
 
−
    Edition = is_set(Edition) and (" " .. wrap( 'edition', Edition )) or "";
 
−
    Issue = is_set(Issue) and (" (" .. Issue .. ")") or "";
 
−
    Series = is_set(Series) and (sepc .. " " .. Series) or "";
 
−
    OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or "";
 
−
    Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";
 
−
 
−
    if is_set(Volume) then
 
−
        if ( mw.ustring.len(Volume) > 4 )
 
−
          then Volume = sepc .." " .. Volume;
 
−
          else Volume = " <b>" .. hyphentodash(Volume) .. "</b>";
 
−
        end
 
−
    end
 
−
 
−
--[[ This code commented out while discussion continues until after week of 2014-03-23 live module update;
 
−
    if is_set(Volume) then
 
−
        if ( mw.ustring.len(Volume) > 4 )
 
−
          then Volume = sepc .. " " .. Volume;
 
−
          else
 
−
              Volume = " <b>" .. hyphentodash(Volume) .. "</b>";
 
−
              if is_set(Series) then Volume = sepc .. Volume;
 
−
              end
 
−
        end
 
−
    end
 
−
]]   
 
−
    ------------------------------------ totally unrelated data
 
−
    --[[ Loosely mimic {{subscription required}} template; Via parameter identifies a delivery source that is not the publisher; these sources often, but not always, exist
 
−
    behind a registration or paywall.  So here, we've chosen to decouple via from subscription (via has never been part of the registration required template).
 
−
   
 
−
    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.
 
−
    ]]
 
−
    if is_set(Via) then
 
−
        Via = " " .. wrap( 'via', Via );
 
−
    end
 
−
 
−
if is_set(SubscriptionRequired) then
 
−
        SubscriptionRequired = sepc .. " " .. cfg.messages['subscription']; --here when 'via' parameter not used but 'subscription' is
 
−
    elseif is_set(RegistrationRequired) then
 
−
        SubscriptionRequired = sepc .. " " .. cfg.messages['registration']; --here when 'via' and 'subscription' parameters not used but 'registration' is
 
−
    end
 
−
 
−
    if is_set(AccessDate) then
 
−
        local retrv_text = " " .. cfg.messages['retrieved']
 
−
        if (sepc ~= ".") then retrv_text = retrv_text:lower() end
 
−
        AccessDate = '<span class="reference-accessdate">' .. sepc
 
−
            .. substitute( retrv_text, {AccessDate} ) .. '</span>'
 
−
    end
 
−
   
 
−
    if is_set(ID) then ID = sepc .." ".. ID; end
 
−
  if "thesis" == config.CitationClass and is_set(Docket) then
 
−
ID = sepc .." Docket ".. Docket .. ID;
 
−
end
 
−
 
−
   
 
−
    ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo} );
 
−
 
−
    if is_set(URL) then
 
−
        URL = " " .. externallink( URL, nil, URLorigin );
 
−
    end
 
−
 
−
    if is_set(Quote) then
 
−
        if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then
 
−
            Quote = Quote:sub(2,-2);
 
−
        end
 
−
        Quote = sepc .." " .. wrap( 'quoted-text', Quote );
 
−
        PostScript = ""; -- CS1 does not supply terminal punctuation when |quote= is set
 
−
    end
 
−
   
 
−
    local Archived
 
−
    if is_set(ArchiveURL) then
 
−
        if not is_set(ArchiveDate) then
 
−
            ArchiveDate = seterror('archive_missing_date');
 
−
        end
 
−
        if "no" == DeadURL then
 
−
            local arch_text = cfg.messages['archived'];
 
−
            if sepc ~= "." then arch_text = arch_text:lower() end
 
−
            Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'],
 
−
                { externallink( ArchiveURL, arch_text ), ArchiveDate } );
 
−
            if not is_set(OriginalURL) then
 
−
                Archived = Archived .. " " .. seterror('archive_missing_url');                             
 
−
            end
 
−
        elseif is_set(OriginalURL) then
 
−
            local arch_text = cfg.messages['archived-dead'];
 
−
            if sepc ~= "." then arch_text = arch_text:lower() end
 
−
            Archived = sepc .. " " .. substitute( arch_text,
 
−
                { externallink( OriginalURL, cfg.messages['original'] ), ArchiveDate } );
 
−
        else
 
−
            local arch_text = cfg.messages['archived-missing'];
 
−
            if sepc ~= "." then arch_text = arch_text:lower() end
 
−
            Archived = sepc .. " " .. substitute( arch_text,
 
−
                { seterror('archive_missing_url'), ArchiveDate } );
 
−
        end
 
−
    else
 
−
        Archived = ""
 
−
    end
 
−
   
 
−
    local Lay
 
−
    if is_set(LayURL) then
 
−
        if is_set(LayDate) then LayDate = " (" .. LayDate .. ")" end
 
−
        if is_set(LaySource) then
 
−
            LaySource = " &ndash; ''" .. safeforitalics(LaySource) .. "''";
 
−
        else
 
−
            LaySource = "";
 
−
        end
 
−
        if sepc == '.' then
 
−
            Lay = sepc .. " " .. externallink( LayURL, cfg.messages['lay summary'] ) .. LaySource .. LayDate
 
−
        else
 
−
            Lay = sepc .. " " .. externallink( LayURL, cfg.messages['lay summary']:lower() ) .. LaySource .. LayDate
 
−
        end           
 
−
    else
 
−
        Lay = "";
 
−
    end
 
−
   
 
−
    if is_set(Transcript) then
 
−
        if is_set(TranscriptURL) then Transcript = externallink( TranscriptURL, Transcript ); end
 
−
    elseif is_set(TranscriptURL) then
 
−
        Transcript = externallink( TranscriptURL, nil, TranscriptURLorigin );
 
−
    end
 
−
   
 
−
    local Publisher;
 
−
    if is_set(Periodical) and
 
−
        not inArray(config.CitationClass, {"encyclopaedia","web","pressrelease","podcast"}) then
 
−
        if is_set(PublisherName) then
 
−
            if is_set(PublicationPlace) then
 
−
                Publisher = PublicationPlace .. ": " .. PublisherName;
 
−
            else
 
−
                Publisher = PublisherName; 
 
−
            end
 
−
        elseif is_set(PublicationPlace) then
 
−
            Publisher= PublicationPlace;
 
−
        else
 
−
            Publisher = "";
 
−
        end
 
−
        if is_set(PublicationDate) then
 
−
            if is_set(Publisher) then
 
−
                Publisher = Publisher .. ", " .. wrap( 'published', PublicationDate );
 
−
            else
 
−
                Publisher = PublicationDate;
 
−
            end
 
−
        end
 
−
        if is_set(Publisher) then
 
−
            Publisher = " (" .. Publisher .. ")";
 
−
        end
 
−
    else
 
−
        if is_set(PublicationDate) then
 
−
            PublicationDate = " (" .. wrap( 'published', PublicationDate ) .. ")";
 
−
        end
 
−
        if is_set(PublisherName) then
 
−
            if is_set(PublicationPlace) then
 
−
                Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
 
−
            else
 
−
                Publisher = sepc .. " " .. PublisherName .. PublicationDate; 
 
−
            end           
 
−
        elseif is_set(PublicationPlace) then
 
−
            Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
 
−
        else
 
−
            Publisher = PublicationDate;
 
−
        end
 
−
    end
 
−
   
 
−
    -- Several of the above rely upon detecting this as nil, so do it last.
 
−
    if is_set(Periodical) then
 
−
        if is_set(Title) or is_set(TitleNote) then
 
−
            Periodical = sepc .. " " .. wrap( 'italic-title', Periodical )
 
−
        else
 
−
            Periodical = wrap( 'italic-title', Periodical )
 
−
        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
 
−
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
 
−
TitleNote = " (Speech)"; -- annotate the citation
 
−
if is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter
 
−
if is_set (Conference) then -- and if |event= is set
 
−
Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering
 
−
end
 
−
end
 
−
end
 
−
 
−
    -- Piece all bits together at last.  Here, all should be non-nil.
 
−
    -- We build things this way because it is more efficient in LUA
 
−
    -- not to keep reassigning to the same string variable over and over.
 
−
 
−
    local tcommon
 
−
    if inArray(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then
 
−
        if is_set(Others) then Others = Others .. sepc .. " " end
 
−
        tcommon = safejoin( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Scale, Series,
 
−
            Language, Cartography, Edition, Publisher, Agency, Volume, Issue}, sepc );
 
−
    else
 
−
        tcommon = safejoin( {Title, TitleNote, Conference, Periodical, Format, TitleType, Scale, Series, Language,
 
−
            Volume, Issue, Others, Cartography, Edition, Publisher, Agency}, sepc );
 
−
    end
 
−
   
 
−
    if #ID_list > 0 then
 
−
        ID_list = safejoin( { sepc .. " ",  table.concat( ID_list, sepc .. " " ), ID }, sepc );
 
−
    else
 
−
        ID_list = ID;
 
−
    end
 
−
   
 
−
    local idcommon = safejoin( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc );
 
−
    local text;
 
−
    local pgtext = Position .. Page .. Pages .. At;
 
−
   
 
−
    if is_set(Authors) then
 
−
        if is_set(Coauthors) then
 
−
            Authors = Authors .. A['AuthorSeparator'] .. " " .. Coauthors
 
−
        end
 
−
        if is_set(Date) then
 
−
            Date = " ("..Date..")" .. OrigYear .. sepc .. " "
 
−
        elseif string.sub(Authors,-1,-1) == sepc then
 
−
            Authors = Authors .. " "
 
−
        else
 
−
            Authors = Authors .. sepc .. " "
 
−
        end
 
−
        if is_set(Editors) then
 
−
            local in_text = " ";
 
−
            local post_text = "";
 
−
            if is_set(Chapter) then
 
−
                in_text = in_text .. cfg.messages['in'] .. " "
 
−
            else
 
−
                if EditorCount <= 1 then
 
−
                    post_text = ", " .. cfg.messages['editor'];
 
−
                else
 
−
                    post_text = ", " .. cfg.messages['editors'];
 
−
                end
 
−
            end
 
−
            if (sepc ~= '.') then in_text = in_text:lower() end
 
−
            Editors = in_text .. Editors .. post_text;
 
−
            if (string.sub(Editors,-1,-1) == sepc)
 
−
                then Editors = Editors .. " "
 
−
                else Editors = Editors .. sepc .. " "
 
−
            end
 
−
        end
 
−
        text = safejoin( {Authors, Date, Chapter, Place, Editors, tcommon }, sepc );
 
−
        text = safejoin( {text, pgtext, idcommon}, sepc );
 
−
    elseif is_set(Editors) then
 
−
        if is_set(Date) then
 
−
            if EditorCount <= 1 then
 
−
                Editors = Editors .. ", " .. cfg.messages['editor'];
 
−
            else
 
−
                Editors = Editors .. ", " .. cfg.messages['editors'];
 
−
            end
 
−
            Date = " (" .. Date ..")" .. OrigYear .. sepc .. " "
 
−
        else
 
−
            if EditorCount <= 1 then
 
−
                Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " "
 
−
            else
 
−
                Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " "
 
−
            end
 
−
        end
 
−
        text = safejoin( {Editors, Date, Chapter, Place, tcommon}, sepc );
 
−
        text = safejoin( {text, pgtext, idcommon}, sepc );
 
−
    else
 
−
        if is_set(Date) then
 
−
            if ( string.sub(tcommon,-1,-1) ~= sepc )
 
−
              then Date = sepc .." " .. Date .. OrigYear
 
−
              else Date = " " .. Date .. OrigYear
 
−
            end
 
−
        end
 
−
        if config.CitationClass=="journal" and is_set(Periodical) then
 
−
            text = safejoin( {Chapter, Place, tcommon}, sepc );
 
−
            text = safejoin( {text, pgtext, Date, idcommon}, sepc );
 
−
        else
 
−
            text = safejoin( {Chapter, Place, tcommon, Date}, sepc );
 
−
            text = safejoin( {text, pgtext, idcommon}, sepc );
 
−
        end
 
−
    end
 
−
   
 
−
if is_set(PostScript) and PostScript ~= sepc then
 
−
text = safejoin( {text, sepc}, sepc );  --Deals with italics, spaces, etc.
 
−
text = text:sub(1,-sepc:len()-1);
 
−
-- text = text:sub(1,-2); --Remove final separator (assumes that sepc is only one character)
 
−
end   
 
−
   
 
−
    text = safejoin( {text, PostScript}, sepc );
 
−
 
−
    -- Now enclose the whole thing in a <span/> element
 
−
    local options = {};
 
−
   
 
−
    if is_set(config.CitationClass) and config.CitationClass ~= "citation" then
 
−
        options.class = "citation " .. config.CitationClass;
 
−
    else
 
−
        options.class = "citation";
 
−
    end
 
−
   
 
−
    if is_set(Ref) and Ref:lower() ~= "none" then
 
−
        local id = Ref
 
−
        if ( "harv" == Ref ) then
 
−
            local names = {} --table of last names & year
 
−
            if #a > 0 then
 
−
                for i,v in ipairs(a) do
 
−
                    names[i] = v.last
 
−
                    if i == 4 then break end
 
−
                end
 
−
            elseif #e > 0 then
 
−
                for i,v in ipairs(e) do
 
−
                    names[i] = v.last
 
−
                    if i == 4 then break end               
 
−
                end
 
−
            end
 
−
names[ #names + 1 ] = first_set(Year, anchor_year); -- Year first for legacy citations
 
−
            id = anchorid(names)
 
−
        end
 
−
        options.id = id;
 
−
    end
 
−
   
 
−
    if string.len(text:gsub("<span[^>/]*>.-</span>", ""):gsub("%b<>","")) <= 2 then
 
−
        z.error_categories = {};
 
−
        text = seterror('empty_citation');
 
−
        z.message_tail = {};
 
−
    end
 
−
   
 
−
    if is_set(options.id) then
 
−
        text = '<span id="' .. mw.uri.anchorEncode(options.id) ..'" class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>";
 
−
    else
 
−
        text = '<span class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>";
 
−
    end       
 
−
 
−
    local empty_span = '<span style="display:none;">&nbsp;</span>';
 
−
   
 
−
    -- Note: Using display: none on then COinS span breaks some clients.
 
−
    local OCinS = '<span title="' .. OCinSoutput .. '" class="Z3988">' .. empty_span .. '</span>';
 
−
    text = text .. OCinS;
 
−
   
 
−
    if #z.message_tail ~= 0 then
 
−
        text = text .. " ";
 
−
        for i,v in ipairs( z.message_tail ) do
 
−
            if is_set(v[1]) then
 
−
                if i == #z.message_tail then
 
−
                    text = text .. errorcomment( v[1], v[2] );
 
−
                else
 
−
                    text = text .. errorcomment( v[1] .. "; ", v[2] );
 
−
                end
 
−
            end
 
−
        end
 
−
    end
 
−
   
 
−
    no_tracking_cats = no_tracking_cats:lower();
 
−
    if inArray(no_tracking_cats, {"", "no", "false", "n"}) then
 
−
        for _, v in ipairs( z.error_categories ) do
 
−
            text = text .. '[[Category:' .. v ..']]';
 
−
        end
 
−
    end
 
−
   
 
−
    return text
 
−
end
 
−
 
−
-- This is used by templates such as {{cite book}} to create the actual citation text.
 
−
function z.citation(frame)
 
−
    local pframe = frame:getParent()
 
−
   
 
−
    if nil ~= string.find( frame:getTitle(), 'sandbox', 1, true ) then -- did the {{#invoke:}} use sandbox version?
 
−
    cfg = mw.loadData( 'Module:Citation/CS1/Configuration/sandbox' ); -- load sandbox versions of Configuration and Whitelist and ...
 
−
    whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist/sandbox' );
 
−
    dates = require('Module:Citation/CS1/Date_validation/sandbox').dates -- ... sandbox version of date validation code
 
−
    else -- otherwise
 
−
    cfg = mw.loadData( 'Module:Citation/CS1/Configuration' ); -- load live versions of Configuration and Whitelist and ...
 
−
    whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' );
 
−
    dates = require('Module:Citation/CS1/Date_validation').dates -- ... live version of date validation code
 
−
end
 
−
 
−
    local args = {};
 
−
    local suggestions = {};
 
−
    local error_text, error_state;
 
−
 
−
    local config = {};
 
−
    for k, v in pairs( frame.args ) do
 
−
        config[k] = v;
 
−
        args[k] = v;     
 
−
    end   
 
−
 
−
    for k, v in pairs( pframe.args ) do
 
−
        if v ~= '' then
 
−
            if not validate( k ) then           
 
−
                error_text = "";
 
−
                if type( k ) ~= 'string' then
 
−
                    -- Exclude empty numbered parameters
 
−
                    if v:match("%S+") ~= nil then
 
−
                        error_text, error_state = seterror( 'text_ignored', {v}, true );
 
−
                    end
 
−
                elseif validate( k:lower() ) then
 
−
                    error_text, error_state = seterror( 'parameter_ignored_suggest', {k, k:lower()}, true );
 
−
                else
 
−
                    if #suggestions == 0 then
 
−
                        suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' );
 
−
                    end
 
−
                    if suggestions[ k:lower() ] ~= nil then
 
−
                        error_text, error_state = seterror( 'parameter_ignored_suggest', {k, suggestions[ k:lower() ]}, true );
 
−
                    else
 
−
                        error_text, error_state = seterror( 'parameter_ignored', {k}, true );
 
−
                    end
 
−
                end                 
 
−
                if error_text ~= '' then
 
−
                    table.insert( z.message_tail, {error_text, error_state} );
 
−
                end               
 
−
            end
 
−
            args[k] = v;
 
−
        elseif args[k] ~= nil or (k == 'postscript') then
 
−
            args[k] = v;
 
−
        end       
 
−
    end   
 
−
   
 
−
    return citation0( config, args)
 
−
end
 
−
 
−
return z
 

Latest revision as of 13:01, 17 July 2014

Documentation for this module may be created at Module:Citation/CS1/doc