Difference between pages "Module:Citation/CS1" and "Module:Citation/CS1/sandbox"

(Difference between pages)
Jump to navigation Jump to search
Page 1
Page 2
m (1 revision imported)
 
m (1 revision)
 
Line 1: Line 1:
 +
--[[
 +
History of changes since last sync: 2014-03-30
 +
2014-03-30: normalize lccn identifiers;
 +
2014-04-04: firstn/lastn mismatch;
 +
2014-05-05: arXiv validation; No spaces in CitationClass values;
 +
2014-05-11: limit reducetoinitials() to two initials for Vancouver system;
 +
 +
]]
  
 
local z = {
 
local z = {
error_categories = {}; -- for categorizing citations that contain errors
+
    error_categories = {};
error_ids = {};
+
    error_ids = {};
message_tail = {};
+
    message_tail = {};
maintenance_cats = {}; -- for categorizing citations that aren't erroneous per se, but could use a little work
 
properties_cats = {}; -- for categorizing citations based on certain properties, language of source for instance
 
 
}
 
}
  
 
-- Whether variable is set or not
 
-- Whether variable is set or not
 
function is_set( var )
 
function is_set( var )
return not (var == nil or var == '');
+
    return not (var == nil or var == '');
 
end
 
end
  
 
-- First set variable or nil if none
 
-- First set variable or nil if none
 
function first_set(...)
 
function first_set(...)
local list = {...};
+
    local list = {...};
for _, var in pairs(list) do
+
    for _, var in pairs(list) do
if is_set( var ) then
+
        if is_set( var ) then
return var;
+
            return var;
end
+
        end
end
+
    end
 
end
 
end
  
 
-- Whether needle is in haystack
 
-- Whether needle is in haystack
 
function inArray( needle, haystack )
 
function inArray( needle, haystack )
if needle == nil then
+
    if needle == nil then
return false;
+
        return false;
end
+
    end
for n,v in ipairs( haystack ) do
+
    for n,v in ipairs( haystack ) do
if v == needle then
+
        if v == needle then
return n;
+
            return n;
end
+
        end
end
+
    end
return false;
+
    return false;
 
end
 
end
  
Line 39: Line 45:
 
Categorize and emit an error message when the citation contains one or more deprecated parameters.  Because deprecated parameters (currently |day=, |month=,
 
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)
 
|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 regardless of the number of deprecated parameters in the citation.
+
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()
 
function deprecated_parameter()
 
if true ~= Page_in_deprecated_cat then -- if we haven't been here before then set a  
 
if true ~= Page_in_deprecated_cat then -- if we haven't been here before then set a  
 
Page_in_deprecated_cat=true; -- sticky flag so that if there are more than one deprecated parameter the category is added only once
 
Page_in_deprecated_cat=true; -- sticky flag so that if there are more than one deprecated parameter the category is added only once
 +
-- table.insert( z.message_tail, { seterror( 'deprecated_params', {error_message}, true ) } ); -- add error message
 
table.insert( z.message_tail, { seterror( 'deprecated_params', {}, true ) } ); -- add error message
 
table.insert( z.message_tail, { seterror( 'deprecated_params', {}, true ) } ); -- add error message
 
end
 
end
Line 71: Line 78:
 
end
 
end
 
return str;
 
return str;
end
 
 
--[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >----------------------------------------
 
 
|script-title= holds title parameters that are not written in Latin based scripts: Chinese, Japanese, Arabic, Hebrew, etc. These scripts should
 
not be italicized and may be written right-to-left.  The value supplied by |script-title= is concatenated onto Title after Title has been wrapped
 
in italic markup.
 
 
Regardless of language, all values provided by |script-title= are wrapped in <bdi>...</bdi> tags to isolate rtl languages from the English left to right.
 
 
|script-title= provides a unique feature.  The value in |script-title= may be prefixed with a two-character ISO639-1 language code and a colon:
 
|script-title=ja:*** *** (where * represents a Japanese character)
 
Spaces between the two-character code and the colon and the colon and the first script character are allowed:
 
|script-title=ja : *** ***
 
|script-title=ja: *** ***
 
|script-title=ja :*** ***
 
Spaces preceding the prefix are allowed: |script-title = ja:*** ***
 
 
The prefix is checked for validity.  If it is a valid ISO639-1 language code, the lang attribute (lang="ja") is added to the <bdi> tag so that browsers can
 
know the language the tag contains.  This may help the browser render the script more correctly.  If the prefix is invalid, the lang attribute
 
is not added.  At this time there is no error message for this condition.
 
 
At this writing, only |script-title= is supported.  It is anticipated that additional parameters will be created to use this function.
 
 
TODO: error messages when prefix is invalid ISO639-1 code; when script_value has prefix but no script;
 
]]
 
 
function format_script_value (script_value)
 
local lang=''; -- initialize to empty string
 
local name;
 
if script_value:match('^%l%l%s*:') then -- if first 3 non-space characters are script language prefix
 
lang = script_value:match('^(%l%l)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script
 
if not is_set (lang) then
 
return ''; -- script_value was just the prefix so return empty string
 
end
 
-- if we get this far we have prefix and script
 
name = mw.language.fetchLanguageName( lang, "en" ); -- get language name so that we can use it to categorize
 
if is_set (name) then -- is prefix a proper ISO 639-1 language code?
 
script_value = script_value:gsub ('^%l%l%s*:%s*', ''); -- strip prefix from script
 
if inArray (lang, {'ar', 'ja', 'ko', 'he', 'zh'}) then -- is prefix one of these language codes?
 
table.insert( z.properties_cats, 'CS1 uses ' .. name .. '-language script ('..lang..')'); -- categorize in language-specific categories
 
else
 
table.insert( z.properties_cats, 'CS1 uses foreign language script'); -- use this category as a catchall until language-specific category is available
 
end
 
lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute
 
else
 
lang = ''; -- invalid so set lang to empty string
 
end
 
end
 
script_value = '<bdi' .. lang .. '>' .. script_value .. '</bdi>'; -- isolate incase script is rlt
 
 
return script_value;
 
end
 
 
--[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------
 
 
Initially for |title= and |script-title=, this function concatenates those two parameter values after the script value has been
 
wrapped in <bdi> tags.
 
]]
 
 
function script_concatenate (title, script)
 
if is_set (script) then
 
script = format_script_value (script); -- <bdi> tags, lang atribute, categorization, etc; returns empty string on error
 
if is_set (script) then
 
title = title .. ' ' .. script; -- concatenate title and script title
 
end
 
end
 
return title;
 
 
end
 
end
  
 
-- Wraps a string using a message_list configuration taking one argument
 
-- Wraps a string using a message_list configuration taking one argument
 
function wrap( key, str, lower )
 
function wrap( key, str, lower )
if not is_set( str ) then
+
    if not is_set( str ) then
return "";
+
        return "";
elseif inArray( key, { 'italic-title', 'trans-italic-title' } ) then
+
    elseif inArray( key, { 'italic-title', 'trans-italic-title' } ) then
str = safeforitalics( str );
+
        str = safeforitalics( str );
end
+
    end
if lower == true then
+
    if lower == true then
return substitute( cfg.messages[key]:lower(), {str} );
+
        return substitute( cfg.messages[key]:lower(), {str} );
else
+
    else
return substitute( cfg.messages[key], {str} );
+
        return substitute( cfg.messages[key], {str} );
end
+
    end      
 
end
 
end
  
Line 161: Line 100:
 
]]
 
]]
 
function argument_wrapper( args )
 
function argument_wrapper( args )
local origin = {};
+
    local origin = {};
+
   
return setmetatable({
+
    return setmetatable({
ORIGIN = function( self, k )
+
        ORIGIN = function( self, k )
local dummy = self[k]; --force the variable to be loaded.
+
            local dummy = self[k]; --force the variable to be loaded.
return origin[k];
+
            return origin[k];
end
+
        end
},
+
    },
{
+
    {
__index = function ( tbl, k )
+
        __index = function ( tbl, k )
if origin[k] ~= nil then
+
            if origin[k] ~= nil then
return nil;
+
                return nil;
end
+
            end
+
           
local args, list, v = args, cfg.aliases[k];
+
            local args, list, v = args, cfg.aliases[k];
+
           
if type( list ) == 'table' then
+
            if type( list ) == 'table' then
v, origin[k] = selectone( args, list, 'redundant_parameters' );
+
                v, origin[k] = selectone( args, list, 'redundant_parameters' );
if origin[k] == nil then
+
                if origin[k] == nil then
origin[k] = ''; -- Empty string, not nil
+
                    origin[k] = ''; -- Empty string, not nil
end
+
                end
elseif list ~= nil then
+
            elseif list ~= nil then
v, origin[k] = args[list], list;
+
                v, origin[k] = args[list], list;
else
+
            else
-- maybe let through instead of raising an error?
+
                -- maybe let through instead of raising an error?
-- v, origin[k] = args[k], k;
+
                -- v, origin[k] = args[k], k;
error( cfg.messages['unknown_argument_map'] );
+
                error( cfg.messages['unknown_argument_map'] );
end
+
            end
+
           
-- Empty strings, not nil;
+
            -- Empty strings, not nil;
if v == nil then
+
            if v == nil then
v = cfg.defaults[k] or '';
+
                v = cfg.defaults[k] or '';
origin[k] = '';
+
                origin[k] = '';
end
+
            end
+
           
tbl = rawset( tbl, k, v );
+
            tbl = rawset( tbl, k, v );
return v;
+
            return v;
end,
+
        end,
});
+
    });
 
end
 
end
  
Line 235: Line 174:
 
-- Formats a comment for error trapping
 
-- Formats a comment for error trapping
 
function errorcomment( content, hidden )
 
function errorcomment( content, hidden )
return wrap( hidden and 'hidden-error' or 'visible-error', content );
+
    return wrap( hidden and 'hidden-error' or 'visible-error', content );
 
end
 
end
  
Line 243: Line 182:
 
]]
 
]]
 
function seterror( error_id, arguments, raw, prefix, suffix )
 
function seterror( error_id, arguments, raw, prefix, suffix )
local error_state = cfg.error_conditions[ error_id ];
+
    local error_state = cfg.error_conditions[ error_id ];
+
   
prefix = prefix or "";
+
    prefix = prefix or "";
suffix = suffix or "";
+
    suffix = suffix or "";
+
   
if error_state == nil then
+
    if error_state == nil then
error( cfg.messages['undefined_error'] );
+
        error( cfg.messages['undefined_error'] );
elseif is_set( error_state.category ) then
+
    elseif is_set( error_state.category ) then
table.insert( z.error_categories, error_state.category );
+
        table.insert( z.error_categories, error_state.category );
end
+
    end
+
   
local message = substitute( error_state.message, arguments );
+
    local message = substitute( error_state.message, arguments );
+
   
message = message .. " ([[" .. cfg.messages['help page link'] ..  
+
    message = message .. " ([[" .. cfg.messages['help page link'] ..  
"#" .. error_state.anchor .. "|" ..
+
        "#" .. error_state.anchor .. "|" ..
cfg.messages['help page label'] .. "]])";
+
        cfg.messages['help page label'] .. "]])";
+
   
z.error_ids[ error_id ] = true;
+
    z.error_ids[ error_id ] = true;
if inArray( error_id, { 'bare_url_missing_title', 'trans_missing_title' } )
+
    if inArray( error_id, { 'bare_url_missing_title', 'trans_missing_title' } )
and z.error_ids['citation_missing_title'] then
+
            and z.error_ids['citation_missing_title'] then
return '', false;
+
        return '', false;
end
+
    end
+
   
message = table.concat({ prefix, message, suffix });
+
    message = table.concat({ prefix, message, suffix });
+
   
if raw == true then
+
    if raw == true then
return message, error_state.hidden;
+
        return message, error_state.hidden;
end
+
    end      
+
       
return errorcomment( message, error_state.hidden );
+
    return errorcomment( message, error_state.hidden );
 
end
 
end
  
 
-- Formats a wiki style external link
 
-- Formats a wiki style external link
 
function externallinkid(options)
 
function externallinkid(options)
local url_string = options.id;
+
    local url_string = options.id;
if options.encode == true or options.encode == nil then
+
    if options.encode == true or options.encode == nil then
url_string = mw.uri.encode( url_string );
+
        url_string = mw.uri.encode( url_string );
end
+
    end
return mw.ustring.format( '[[%s|%s]]%s[%s%s%s %s]',
+
    return mw.ustring.format( '[[%s|%s]]%s[%s%s%s %s]',
options.link, options.label, options.separator or "&nbsp;",
+
        options.link, options.label, options.separator or "&nbsp;",
options.prefix, url_string, options.suffix or "",
+
        options.prefix, url_string, options.suffix or "",
mw.text.nowiki(options.id)
+
        mw.text.nowiki(options.id)
);
+
    );
 
end
 
end
  
 
-- Formats a wiki style internal link
 
-- Formats a wiki style internal link
 
function internallinkid(options)
 
function internallinkid(options)
return mw.ustring.format( '[[%s|%s]]%s[[%s%s%s|%s]]',
+
    return mw.ustring.format( '[[%s|%s]]%s[[%s%s%s|%s]]',
options.link, options.label, options.separator or "&nbsp;",
+
        options.link, options.label, options.separator or "&nbsp;",
options.prefix, options.id, options.suffix or "",
+
        options.prefix, options.id, options.suffix or "",
mw.text.nowiki(options.id)
+
        mw.text.nowiki(options.id)
);
+
    );
 
end
 
end
  
 
-- Format an external link with error checking
 
-- Format an external link with error checking
 
function externallink( URL, label, source )
 
function externallink( URL, label, source )
local error_str = "";
+
    local error_str = "";
if not is_set( label ) then
+
    if not is_set( label ) then
label = URL;
+
        label = URL;
if is_set( source ) then
+
        if is_set( source ) then
error_str = seterror( 'bare_url_missing_title', { wrap( 'parameter', source ) }, false, " " );
+
            error_str = seterror( 'bare_url_missing_title', { wrap( 'parameter', source ) }, false, " " );
else
+
        else
error( cfg.messages["bare_url_no_origin"] );
+
            error( cfg.messages["bare_url_no_origin"] );
end
+
        end          
end
+
    end
if not checkurl( URL ) then
+
    if not checkurl( URL ) then
error_str = seterror( 'bad_url', {}, false, " " ) .. error_str;
+
        error_str = seterror( 'bad_url', {}, false, " " ) .. error_str;
end
+
    end
return table.concat({ "[", URL, " ", safeforurl( label ), "]", error_str });
+
    return table.concat({ "[", URL, " ", safeforurl( label ), "]", error_str });
 
end
 
end
  
 
-- Formats a link to Amazon
 
-- Formats a link to Amazon
 
function amazon(id, domain)
 
function amazon(id, domain)
if checkisbn( id ) then -- see if asin value is a 10-digit isbn
+
    if not is_set(domain) then  
table.insert( z.maintenance_cats, "CS1 maint: ASIN uses ISBN"); -- add to maint category so a bot or awb script can replace |asin= with |isbn=
+
        domain = "com"
end
+
    elseif ( "jp" == domain or "uk" == domain ) then
if not is_set(domain) then  
+
        domain = "co." .. domain
domain = "com";
+
    end
elseif inArray (domain, {'jp', 'uk'}) then -- Japan, United Kingdom
+
    local handler = cfg.id_handlers['ASIN'];
domain = "co." .. domain;
+
    return externallinkid({link = handler.link,
elseif inArray (domain, {'au', 'br', 'mx'}) then -- Australia, Brazil, Mexico
+
        label=handler.label , prefix="//www.amazon."..domain.."/dp/",id=id,
domain = "com." .. domain;
+
        encode=handler.encode, separator = handler.separator})
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
 
end
  
Line 335: Line 269:
 
format and error check arXiv identifier.  There are two valid forms of the identifier:
 
format and error check arXiv identifier.  There are two valid forms of the identifier:
 
the first form, valid only between date codes 9108 and 0703 is:
 
the first form, valid only between date codes 9108 and 0703 is:
arXiv:<archive>.<class>/<date code><number><version>
+
arXiv:<archive>.<class>/<date code><number>
 
where:
 
where:
 
<archive> is a string of alpha characters - may be hyphenated; no other punctuation
 
<archive> is a string of alpha characters - may be hyphenated; no other punctuation
Line 342: Line 276:
 
first digit of YY for this form can only 9 and 0
 
first digit of YY for this form can only 9 and 0
 
<number> is a three-digit number
 
<number> is a three-digit number
<version> is a 1 or more digit number preceded with a lowercase v; no spaces (undocumented)
 
 
 
 
the second form, valid from April 2007 is:
 
the second form, valid from April 2007 is:
Line 357: Line 290:
 
local err_cat = ""
 
local err_cat = ""
 
 
year, month, version = id:match("^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d([v%d]*)$"); -- test for the 9108-0703 format
+
year, month = id:match("^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d$"); -- test for the 9108-0703 format
 
if not year then -- arXiv id is not proper 9108-0703 form
 
if not year then -- arXiv id is not proper 9108-0703 form
 
year, month, version = id:match("^(%d%d)([01]%d)%.%d%d%d%d([v%d]*)$"); -- test for the 0704- format
 
year, month, version = id:match("^(%d%d)([01]%d)%.%d%d%d%d([v%d]*)$"); -- test for the 0704- format
Line 375: Line 308:
 
month = tonumber(month);
 
month = tonumber(month);
 
if ((91 > year and 7 < year) or (1 > month and 12 < month)) or -- if invalid year or invalid month
 
if ((91 > year and 7 < year) or (1 > month and 12 < month)) or -- if invalid year or invalid month
((91 == year and 8 > month) or (7 == year and 3 < month)) or -- if years ok, are starting and ending months ok?
+
((91 == year and 8 > month) or (7 == year and 3 < month)) then -- if years ok, are starting and ending months ok?
is_set (version) and nil == version:match("v%d+") then -- is version proper format of single 'v' followed by digits?
+
err_cat = ' ' .. seterror( 'bad_arxiv' ); -- set error message
err_cat = ' ' .. seterror( 'bad_arxiv' ); -- set error message
 
 
end
 
end
 
end
 
end
Line 393: Line 325:
 
b. Inspect the substring following (to the right of) the (removed) hyphen. Then (and assuming that steps 1 and 2 have been carried out):
 
b. Inspect the substring following (to the right of) the (removed) hyphen. Then (and assuming that steps 1 and 2 have been carried out):
 
1. All these characters should be digits, and there should be six or less. (not done in this function)
 
1. All these characters should be digits, and there should be six or less. (not done in this function)
2. If the length of the substring is less than 6, left-fill the substring with zeroes until the length is six.
+
2. If the length of the substring is less than 6, left-fill the substring with zeros until the length is six.
  
 
Returns a normalized lccn for lccn() to validate.  There is no error checking (step 3.b.1) performed in this function.
 
Returns a normalized lccn for lccn() to validate.  There is no error checking (step 3.b.1) performed in this function.
Line 422: Line 354:
  
 
length = 8 then all digits
 
length = 8 then all digits
length = 9 then lccn[1] is lower case alpha
+
length = 9 then lccn[1] is alpha
length = 10 then lccn[1] and lccn[2] are both lower case alpha or both digits
+
length = 10 then lccn[1] and lccn[2] are both alpha or both digits
length = 11 then lccn[1] is lower case alpha, lccn[2] and lccn[3] are both lower case alpha or both digits
+
length = 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 lower case alpha
+
length = 12 then lccn[1] and lccn[2] are both alpha
  
 
]]
 
]]
Line 441: Line 373:
 
end
 
end
 
elseif 9 == len then -- LCCN should be adddddddd
 
elseif 9 == len then -- LCCN should be adddddddd
if nil == id:match("%l%d%d%d%d%d%d%d%d") then -- does it match our pattern?
+
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
 
err_cat = ' ' .. seterror( 'bad_lccn' ); -- set an error message
 
end
 
end
 
elseif 10 == len then -- LCCN should be aadddddddd or dddddddddd
 
elseif 10 == len then -- LCCN should be aadddddddd or dddddddddd
 
if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ...
 
if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ...
if nil == id:match("^%l%l%d%d%d%d%d%d%d%d") then -- ... see if it matches our pattern
+
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
 
err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
 
end
 
end
 
end
 
end
 
elseif 11 == len then -- LCCN should be aaadddddddd or adddddddddd
 
elseif 11 == len then -- LCCN should be aaadddddddd or adddddddddd
if not (id:match("^%l%l%l%d%d%d%d%d%d%d%d") or id:match("^%l%d%d%d%d%d%d%d%d%d%d")) then -- see if it matches one of our patterns
+
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
 
err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
 
end
 
end
 
elseif 12 == len then -- LCCN should be aadddddddddd
 
elseif 12 == len then -- LCCN should be aadddddddddd
if not id:match("^%l%l%d%d%d%d%d%d%d%d%d%d") then -- see if it matches our pattern
+
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
 
err_cat = ' ' .. seterror( 'bad_lccn' ); -- no match, set an error message
 
end
 
end
Line 494: Line 426:
 
--[[
 
--[[
 
Determines if a PMC identifier's online version is embargoed. Compares the date in |embargo= against today's date.  If embargo date is
 
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; otherwise, returns false because the embargo has expired or |embargo= not set in this cite.
+
in the future, returns true; otherwse, returns false because the embargo has expired or |embargo= not set in this cite.
 
]]
 
]]
 
function is_embargoed(embargo)
 
function is_embargoed(embargo)
Line 524: Line 456:
 
local handler = cfg.id_handlers['PMC'];
 
local handler = cfg.id_handlers['PMC'];
 
local err_cat =  ''; -- presume that PMC is valid
 
local err_cat =  ''; -- presume that PMC is valid
+
   
 
local text;
 
local text;
  
Line 558: Line 490:
  
 
function doi(id, inactive)
 
function doi(id, inactive)
local cat = ""
+
    local cat = ""
local handler = cfg.id_handlers['DOI'];
+
    local handler = cfg.id_handlers['DOI'];
+
   
local text;
+
    local text;
 
if is_set(inactive) then
 
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
 
local inactive_year = inactive:match("%d%d%d%d") or ''; -- try to get the year portion from the inactive date
Line 585: Line 517:
 
-- Formats an OpenLibrary link, and checks for associated errors.
 
-- Formats an OpenLibrary link, and checks for associated errors.
 
function openlibrary(id)
 
function openlibrary(id)
local code = id:match("^%d+([AMW])$"); -- only digits followed by 'A', 'M', or 'W'
+
    local code = id:sub(-1,-1)
local handler = cfg.id_handlers['OL'];
+
    local handler = cfg.id_handlers['OL'];
 
+
    if ( code == "A" ) then
if ( code == "A" ) then
+
        return externallinkid({link=handler.link, label=handler.label,
return externallinkid({link=handler.link, label=handler.label,
+
            prefix="http://openlibrary.org/authors/OL",id=id, separator=handler.separator,
prefix="http://openlibrary.org/authors/OL",id=id, separator=handler.separator,
+
            encode = handler.encode})
encode = handler.encode})
+
    elseif ( code == "M" ) then
elseif ( code == "M" ) then
+
        return externallinkid({link=handler.link, label=handler.label,
return externallinkid({link=handler.link, label=handler.label,
+
            prefix="http://openlibrary.org/books/OL",id=id, separator=handler.separator,
prefix="http://openlibrary.org/books/OL",id=id, separator=handler.separator,
+
            encode = handler.encode})
encode = handler.encode})
+
    elseif ( code == "W" ) then
elseif ( code == "W" ) then
+
        return externallinkid({link=handler.link, label=handler.label,
return externallinkid({link=handler.link, label=handler.label,
+
            prefix= "http://openlibrary.org/works/OL",id=id, separator=handler.separator,
prefix= "http://openlibrary.org/works/OL",id=id, separator=handler.separator,
+
            encode = handler.encode})
encode = handler.encode})
+
    else
else
+
        return externallinkid({link=handler.link, label=handler.label,
return externallinkid({link=handler.link, label=handler.label,
+
            prefix= "http://openlibrary.org/OL",id=id, separator=handler.separator,
prefix= "http://openlibrary.org/OL",id=id, separator=handler.separator,
+
            encode = handler.encode}) ..  
encode = handler.encode}) ..  
+
            ' ' .. seterror( 'bad_ol' );
' ' .. seterror( 'bad_ol' );
+
    end
end
 
 
end
 
end
  
Line 624: Line 555:
 
local valid_issn = true;
 
local valid_issn = true;
  
id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and endashes from the issn
+
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 digits long, containing only 0-9 or X in the last position
 
if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the issn: 8 digits long, containing only 0-9 or X in the last position
Line 650: Line 581:
 
--[[
 
--[[
 
This function sets default title types (equivalent to the citation including |type=<default value>) for those citations that have defaults.
 
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 desirable to omit the title type from the rendered citation (|type=none).
+
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)
 
function set_titletype(cite_class, title_type)
Line 686: Line 617:
 
]]
 
]]
 
function checkurl( url_str )
 
function checkurl( url_str )
-- Protocol-relative or URL scheme
+
    -- Protocol-relative or URL scheme
return url_str:sub(1,2) == "//" or url_str:match( "^[^/]*:" ) ~= nil;
+
    return url_str:sub(1,2) == "//" or url_str:match( "^[^/]*:" ) ~= nil;
 
end
 
end
  
Line 693: Line 624:
 
-- Similar to that used for Special:BookSources
 
-- Similar to that used for Special:BookSources
 
function cleanisbn( isbn_str )
 
function cleanisbn( isbn_str )
return isbn_str:gsub( "[^-0-9X]", "" );
+
    return isbn_str:gsub( "[^-0-9X]", "" );
 
end
 
end
  
--[[--------------------------< E S C A P E _ L U A _ M A G I C _ C H A R S >----------------------------------
+
-- Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS.
 
+
--TODO: Fix so this code supports urls like this:
Returns a string where all of lua's magic characters have been escaped.  This is important because functions like
+
-- http://www.history.navy.mil/download/va125153.pdf#page=13 %w/:\.
string.gsub() treat their pattern and replace strings as patterns, not literal strings.
 
]]
 
function escape_lua_magic_chars (argument)
 
argument = argument:gsub("%%", "%%%%"); -- replace % with %%
 
argument = argument:gsub("([%^%$%(%)%.%[%]%*%+%-%?])", "%%%1"); -- replace all other lua magic pattern characters
 
return argument;
 
end
 
 
 
--[[--------------------------< S T R I P _ A P O S T R O P H E _ M A R K U P >--------------------------------
 
 
 
Strip wiki italic and bold markup from argument so that it doesn't contaminate COinS metadata.
 
This function strips common patterns of apostrophe markup.  We presume that editors who have taken the time to
 
markup a title have, as a result, provided valid markup. When they don't, some single apostrophes are left behind.
 
 
 
]]
 
function strip_apostrophe_markup (argument)
 
if not is_set (argument) then return argument; end
 
 
 
while true do
 
if argument:match ("%'%'%'%'%'") then -- bold italic (5)
 
argument=argument:gsub("%'%'%'%'%'", ""); -- remove all instances of it
 
elseif argument:match ("%'%'%'%'") then -- italic start and end without content (4)
 
argument=argument:gsub("%'%'%'%'", "");
 
elseif argument:match ("%'%'%'") then -- bold (3)
 
argument=argument:gsub("%'%'%'", "");
 
elseif argument:match ("%'%'") then -- italic (2)
 
argument=argument:gsub("%'%'", "");
 
else
 
break;
 
end
 
end
 
return argument; -- done
 
end
 
 
 
--[[--------------------------< M A K E _ C O I N S _ T I T L E >----------------------------------------------
 
 
 
Makes a title for COinS from Title and / or ScriptTitle (or any other name-script pairs)
 
 
 
Apostrophe markup (bold, italics) is stripped from each value so that the COinS metadata isn't correupted with strings
 
of %27%27...
 
]]
 
 
 
function make_coins_title (title, script)
 
if is_set (title) then
 
title = strip_apostrophe_markup (title); -- strip any apostrophe markup
 
else
 
title=''; -- if not set, make sure title is an empty string
 
end
 
if is_set (script) then
 
script = script:gsub ('^%l%l%s*:%s*', ''); -- remove language prefix if present (script value may now be empty string)
 
script = strip_apostrophe_markup (script); -- strip any apostrophe markup
 
else
 
script=''; -- if not set, make sure script is an empty string
 
end
 
if is_set (title) and is_set (script) then
 
script = ' ' .. script; -- add a space before we concatenate
 
end
 
return title .. script; -- return the concatenation
 
end
 
 
 
--[[--------------------------< G E T _ C O I N S _ P A G E S >------------------------------------------------
 
 
 
Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS.
 
 
 
]]
 
 
function get_coins_pages (pages)
 
function get_coins_pages (pages)
local pattern;
+
if not is_set (pages) then return pages; end -- if no page numbers then we're done
if not is_set (pages) then return pages; end -- if no page numbers then we're done
 
 
 
while true do
+
    while true do
pattern = pages:match("%[(%w*:?//[^ ]+%s+)[%w%d].*%]"); -- pattern is the opening bracket, the url and following space(s): "[url "
+
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
+
if nil == pattern then break; end -- no more urls
pattern = escape_lua_magic_chars (pattern); -- pattern is not a literal string; escape lua's magic pattern characters
+
pages = pages:gsub(pattern, ""); -- remove as many instances of pattern as possible
pages = pages:gsub(pattern, ""); -- remove as many instances of pattern as possible
 
 
end
 
end
pages = pages:gsub("[%[%]]", ""); -- remove the brackets
+
pages = pages:gsub("[%[%]]", ""); -- remove the brackets
 
pages = pages:gsub("–", "-" ); -- replace endashes with hyphens
 
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?
+
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;
 
return pages;
 
end
 
end
Line 825: Line 689:
 
-- Gets the display text for a wikilink like [[A|B]] or [[B]] gives B
 
-- Gets the display text for a wikilink like [[A|B]] or [[B]] gives B
 
function removewikilink( str )
 
function removewikilink( str )
return (str:gsub( "%[%[([^%[%]]*)%]%]", function(l)
+
    return (str:gsub( "%[%[([^%[%]]*)%]%]", function(l)
return l:gsub( "^[^|]*|(.*)$", "%1" ):gsub("^%s*(.-)%s*$", "%1");
+
        return l:gsub( "^[^|]*|(.*)$", "%1" ):gsub("^%s*(.-)%s*$", "%1");
end));
+
    end));
 
end
 
end
  
 
-- Escape sequences for content that will be used for URL descriptions
 
-- Escape sequences for content that will be used for URL descriptions
 
function safeforurl( str )
 
function safeforurl( str )
if str:match( "%[%[.-%]%]" ) ~= nil then  
+
    if str:match( "%[%[.-%]%]" ) ~= nil then  
table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } );
+
        table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } );
end
+
    end
+
   
return str:gsub( '[%[%]\n]', {
+
    return str:gsub( '[%[%]\n]', {  
['['] = '&#91;',
+
        ['['] = '&#91;',
[']'] = '&#93;',
+
        [']'] = '&#93;',
['\n'] = ' ' } );
+
        ['\n'] = ' ' } );
 
end
 
end
  
 
-- Converts a hyphen to a dash
 
-- Converts a hyphen to a dash
 
function hyphentodash( str )
 
function hyphentodash( str )
if not is_set(str) or str:match( "[%[%]{}<>]" ) ~= nil then
+
    if not is_set(str) or str:match( "[%[%]{}<>]" ) ~= nil then
return str;
+
        return str;
end
+
    end  
return str:gsub( '-', '–' );
+
    return str:gsub( '-', '–' );
 
end
 
end
  
 
-- Protects a string that will be wrapped in wiki italic markup '' ... ''
 
-- Protects a string that will be wrapped in wiki italic markup '' ... ''
 
function safeforitalics( str )
 
function safeforitalics( str )
--[[ Note: We cannot use <i> for italics, as the expected behavior for
+
    --[[ 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
+
    italics specified by ''...'' in the title is that they will be inverted
(i.e. unitalicized) in the resulting references.  In addition, <i> and ''
+
    (i.e. unitalicized) in the resulting references.  In addition, <i> and ''
tend to interact poorly under Mediawiki's HTML tidy. ]]
+
    tend to interact poorly under Mediawiki's HTML tidy. ]]
+
   
if not is_set(str) then
+
    if not is_set(str) then
return str;
+
        return str;
else
+
    else
if str:sub(1,1) == "'" then str = "<span />" .. str; end
+
        if str:sub(1,1) == "'" then str = "<span />" .. str; end
if str:sub(-1,-1) == "'" then str = str .. "<span />"; end
+
        if str:sub(-1,-1) == "'" then str = str .. "<span />"; end
+
       
-- Remove newlines as they break italics.
+
        -- Remove newlines as they break italics.
return str:gsub( '\n', ' ' );
+
        return str:gsub( '\n', ' ' );
end
+
    end
 
end
 
end
  
Line 877: Line 741:
 
]]
 
]]
 
function safejoin( tbl, duplicate_char )
 
function safejoin( tbl, duplicate_char )
--[[
+
    --[[
Note: we use string functions here, rather than ustring functions.
+
    Note: we use string functions here, rather than ustring functions.
+
   
This has considerably faster performance and should work correctly as  
+
    This has considerably faster performance and should work correctly as  
long as the duplicate_char is strict ASCII.  The strings
+
    long as the duplicate_char is strict ASCII.  The strings
in tbl may be ASCII or UTF8.
+
    in tbl may be ASCII or UTF8.
]]
+
    ]]
+
   
local str = '';
+
    local str = '';
local comp = '';
+
    local comp = '';
local end_chr = '';
+
    local end_chr = '';
local trim;
+
    local trim;
for _, value in ipairs( tbl ) do
+
    for _, value in ipairs( tbl ) do
if value == nil then value = ''; end
+
        if value == nil then value = ''; end
+
       
if str == '' then
+
        if str == '' then
str = value;
+
            str = value;
elseif value ~= '' then
+
        elseif value ~= '' then
if value:sub(1,1) == '<' then
+
            if value:sub(1,1) == '<' then
-- Special case of values enclosed in spans and other markup.
+
                -- Special case of values enclosed in spans and other markup.
comp = value:gsub( "%b<>", "" );
+
                comp = value:gsub( "%b<>", "" );
else
+
            else
comp = value;
+
                comp = value;
end
+
            end
+
           
if comp:sub(1,1) == duplicate_char then
+
            if comp:sub(1,1) == duplicate_char then
trim = false;
+
                trim = false;
end_chr = str:sub(-1,-1);
+
                end_chr = str:sub(-1,-1);
-- str = str .. "<HERE(enchr=" .. end_chr.. ")"
+
                -- str = str .. "<HERE(enchr=" .. end_chr.. ")"
if end_chr == duplicate_char then
+
                if end_chr == duplicate_char then
str = str:sub(1,-2);
+
                    str = str:sub(1,-2);
elseif end_chr == "'" then
+
                elseif end_chr == "'" then
if str:sub(-3,-1) == duplicate_char .. "''" then
+
                    if str:sub(-3,-1) == duplicate_char .. "''" then
str = str:sub(1, -4) .. "''";
+
                        str = str:sub(1, -4) .. "''";
elseif str:sub(-5,-1) == duplicate_char .. "]]''" then
+
                    elseif str:sub(-5,-1) == duplicate_char .. "]]''" then
trim = true;
+
                        trim = true;
elseif str:sub(-4,-1) == duplicate_char .. "]''" then
+
                    elseif str:sub(-4,-1) == duplicate_char .. "]''" then
trim = true;
+
                        trim = true;
end
+
                    end
elseif end_chr == "]" then
+
                elseif end_chr == "]" then
if str:sub(-3,-1) == duplicate_char .. "]]" then
+
                    if str:sub(-3,-1) == duplicate_char .. "]]" then
trim = true;
+
                        trim = true;
elseif str:sub(-2,-1) == duplicate_char .. "]" then
+
                    elseif str:sub(-2,-1) == duplicate_char .. "]" then
trim = true;
+
                        trim = true;
end
+
                    end
elseif end_chr == " " then
+
                elseif end_chr == " " then
if str:sub(-2,-1) == duplicate_char .. " " then
+
                    if str:sub(-2,-1) == duplicate_char .. " " then
str = str:sub(1,-3);
+
                        str = str:sub(1,-3);
end
+
                    end
end
+
                end
  
if trim then
+
                if trim then
if value ~= comp then  
+
                    if value ~= comp then  
local dup2 = duplicate_char;
+
                        local dup2 = duplicate_char;
if dup2:match( "%A" ) then dup2 = "%" .. dup2; end
+
                        if dup2:match( "%A" ) then dup2 = "%" .. dup2; end
+
                       
value = value:gsub( "(%b<>)" .. dup2, "%1", 1 )
+
                        value = value:gsub( "(%b<>)" .. dup2, "%1", 1 )
else
+
                    else
value = value:sub( 2, -1 );
+
                        value = value:sub( 2, -1 );
end
+
                    end
end
+
                end
end
+
            end
str = str .. value;
+
            str = str .. value;
end
+
        end
end
+
    end
return str;
+
    return str;
 
end   
 
end   
  
Line 959: Line 823:
 
-- Formats a list of people (e.g. authors / editors)  
 
-- Formats a list of people (e.g. authors / editors)  
 
function listpeople(control, people)
 
function listpeople(control, people)
local sep = control.sep;
+
    local sep = control.sep;
local namesep = control.namesep
+
    local namesep = control.namesep
local format = control.format
+
    local format = control.format
local maximum = control.maximum
+
    local maximum = control.maximum
local lastauthoramp = control.lastauthoramp;
+
    local lastauthoramp = control.lastauthoramp;
local text = {}
+
    local text = {}
local etal = false;
+
    local etal = false;
+
   
if sep:sub(-1,-1) ~= " " then sep = sep .. " " end
+
    if sep:sub(-1,-1) ~= " " then sep = sep .. " " end
if maximum ~= nil and maximum < 1 then return "", 0; end
+
    if maximum ~= nil and maximum < 1 then return "", 0; end
+
   
for i,person in ipairs(people) do
+
    for i,person in ipairs(people) do
if is_set(person.last) then
+
        if is_set(person.last) then
local mask = person.mask
+
            local mask = person.mask
local one
+
            local one
local sep_one = sep;
+
            local sep_one = sep;
if maximum ~= nil and i > maximum then
+
            if maximum ~= nil and i > maximum then
etal = true;
+
                etal = true;
break;
+
                break;
elseif (mask ~= nil) then
+
            elseif (mask ~= nil) then
local n = tonumber(mask)
+
                local n = tonumber(mask)
if (n ~= nil) then
+
                if (n ~= nil) then
one = string.rep("&mdash;",n)
+
                    one = string.rep("&mdash;",n)
else
+
                else
one = mask;
+
                    one = mask;
sep_one = " ";
+
                    sep_one = " ";
end
+
                end
else
+
            else
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 first = reducetoinitials(first) end
+
                    if ( "vanc" == format ) then first = reducetoinitials(first) end
one = one .. namesep .. first  
+
                    one = one .. namesep .. first  
end
+
                end
if is_set(person.link) and person.link ~= control.page_name then
+
                if is_set(person.link) then one = "[[" .. person.link .. "|" .. one .. "]]" end
one = "[[" .. person.link .. "|" .. one .. "]]" -- link author/editor if this page is not the author's/editor's page
+
                if is_set(person.link) and nil ~= person.link:find("//") then one = one .. " " .. seterror( 'bad_authorlink' ) end -- check for url in author link;
end
+
            end
 +
            table.insert( text, one )
 +
            table.insert( text, sep_one )
 +
        end
 +
    end
  
if is_set(person.link) and nil ~= person.link:find("//") then one = one .. " " .. seterror( 'bad_authorlink' ) end -- check for url in author link;
+
    local count = #text / 2;
end
+
    if count > 0 then  
table.insert( text, one )
+
        if count > 1 and is_set(lastauthoramp) and not etal then
table.insert( text, sep_one )
+
            text[#text-2] = " & ";
end
+
        end
end
+
        text[#text] = nil;  
 
+
    end
local count = #text / 2;
+
   
if count > 0 then  
+
    local result = table.concat(text) -- construct list
if count > 1 and is_set(lastauthoramp) and not etal then
+
    if etal then  
text[#text-2] = " & ";
+
        local etal_text = cfg.messages['et al'];
end
+
        result = result .. " " .. etal_text;
text[#text] = nil;  
+
    end
end
+
   
+
    -- if necessary wrap result in <span> tag to format in Small Caps
local result = table.concat(text) -- construct list
+
    if ( "scap" == format ) then result =  
if etal then  
+
        '<span class="smallcaps" style="font-variant:small-caps">' .. result .. '</span>';
local etal_text = cfg.messages['et al'];
+
    end  
result = result .. " " .. etal_text;
+
    return result, count
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
 
end
  
 
-- Generates a CITEREF anchor ID.
 
-- Generates a CITEREF anchor ID.
 
function anchorid( options )
 
function anchorid( options )
return "CITEREF" .. table.concat( options );
+
    return "CITEREF" .. table.concat( options );
 
end
 
end
  
Line 1,038: Line 899:
  
 
This function emits an error message when there is a |firstn= without a matching |lastn=.  When there are 'holes' in the list of last names, |last1= and |last3=
 
This function emits an error message when there is a |firstn= without a matching |lastn=.  When there are 'holes' in the list of last names, |last1= and |last3=
are present but |last2= is missing, an error message is emitted. |lastn= is not required to have a matching |firstn=.
+
are present but |last2= is missing, an error message is emitted. |lastn= is not required ot have a matching |firstn=.
 
]]
 
]]
 
function extractnames(args, list_name)
 
function extractnames(args, list_name)
 
local names = {}; -- table of names
 
local names = {}; -- table of names
local last; -- individual name components
 
local first;
 
local link;
 
local mask;
 
 
local i = 1; -- loop counter/indexer
 
local i = 1; -- loop counter/indexer
local n = 1; -- output table indexer
 
 
local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors)
 
local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors)
 
 
 
local err_msg_list_name = list_name:match ("(%w+)List") .. 's list'; -- modify AuthorList or EditorList for use in error messages if necessary
 
local err_msg_list_name = list_name:match ("(%w+)List") .. 's list'; -- modify AuthorList or EditorList for use in error messages if necessary
  
while true do
+
    while true do
last = selectone( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i ); -- search through args for name components beginning at 1
+
        names[i] = -- search through args for name components beginning at 1
first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i );
+
        {
link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i );
+
last = selectone( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i ),
mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i );
+
first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ),
 
+
link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ),
if first and not last then -- if there is a firstn without a matching lastn
+
mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i )
 +
};
 +
if names[i].first and not names[i].last then -- if there is a firstn without a matching lastn
 +
names[i].first = nil; -- set first to nil so we don't confuse the implict et al message code
 
table.insert( z.message_tail, { seterror( 'first_missing_last', {err_msg_list_name, i}, true ) } ); -- add this error message
 
table.insert( z.message_tail, { seterror( 'first_missing_last', {err_msg_list_name, i}, true ) } ); -- add this error message
elseif not first and not last then -- if both firstn and lastn aren't found, are we done?
+
break; -- and done because lastn not found
 +
elseif not names[i].first and not names[i].last then -- if both firstn and lastn aren't found, are we done?
 
count = count + 1; -- number of times we haven't found last and first
 
count = count + 1; -- number of times we haven't found last and first
 
if 2 == count then -- two missing names and we give up
 
if 2 == count then -- two missing names and we give up
 
break; -- normal exit or there is a two-name hole in the list; can't tell which
 
break; -- normal exit or there is a two-name hole in the list; can't tell which
 
end
 
end
else -- we have last with or without a first
+
else -- last with or without a first
names[n] = {last = last, first = first, link = link, mask = mask}; -- add this name to our names list
 
n = n + 1; -- point to next location in the names table
 
 
if 1 == count then -- if the previous name was missing
 
if 1 == count then -- if the previous name was missing
 
table.insert( z.message_tail, { seterror( 'missing_name', {err_msg_list_name, i-1}, true ) } ); -- add this error message
 
table.insert( z.message_tail, { seterror( 'missing_name', {err_msg_list_name, i-1}, true ) } ); -- add this error message
Line 1,073: Line 931:
 
count = 0; -- reset the counter, we're looking for two consecutive missing names
 
count = 0; -- reset the counter, we're looking for two consecutive missing names
 
end
 
end
i = i + 1; -- point to next args location
+
 
 +
i = i + 1; -- bump to the next name
 
end
 
end
 
return names; -- all done, return our list of names
 
return names; -- all done, return our list of names
Line 1,080: Line 939:
 
-- Populates ID table from arguments using configuration settings
 
-- Populates ID table from arguments using configuration settings
 
function extractids( args )
 
function extractids( args )
local id_list = {};
+
    local id_list = {};
for k, v in pairs( cfg.id_handlers ) do
+
    for k, v in pairs( cfg.id_handlers ) do  
v = selectone( args, v.parameters, 'redundant_parameters' );
+
        v = selectone( args, v.parameters, 'redundant_parameters' );
if is_set(v) then id_list[k] = v; end
+
        if is_set(v) then id_list[k] = v; end
end
+
    end
return id_list;
+
    return id_list;
 
end
 
end
  
 
-- Takes a table of IDs and turns it into a table of formatted ID outputs.
 
-- Takes a table of IDs and turns it into a table of formatted ID outputs.
 
function buildidlist( id_list, options )
 
function buildidlist( id_list, options )
local new_list, handler = {};
+
    local new_list, handler = {};
+
   
function fallback(k) return { __index = function(t,i) return cfg.id_handlers[k][i] end } end;
+
    function fallback(k) return { __index = function(t,i) return cfg.id_handlers[k][i] end } end;
+
   
for k, v in pairs( id_list ) do
+
    for k, v in pairs( id_list ) do
-- fallback to read-only cfg
+
        -- fallback to read-only cfg
handler = setmetatable( { ['id'] = v }, fallback(k) );
+
        handler = setmetatable( { ['id'] = v }, fallback(k) );
+
       
if handler.mode == 'external' then
+
        if handler.mode == 'external' then
table.insert( new_list, {handler.label, externallinkid( handler ) } );
+
            table.insert( new_list, {handler.label, externallinkid( handler ) } );
elseif handler.mode == 'internal' then
+
        elseif handler.mode == 'internal' then
table.insert( new_list, {handler.label, internallinkid( handler ) } );
+
            table.insert( new_list, {handler.label, internallinkid( handler ) } );
elseif handler.mode ~= 'manual' then
+
        elseif handler.mode ~= 'manual' then
error( cfg.messages['unknown_ID_mode'] );
+
            error( cfg.messages['unknown_ID_mode'] );
elseif k == 'DOI' then
+
        elseif k == 'DOI' then
table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } );
+
            table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } );
elseif k == 'ARXIV' then
+
        elseif k == 'ARXIV' then
table.insert( new_list, {handler.label, arxiv( v ) } );  
+
            table.insert( new_list, {handler.label, arxiv( v ) } );  
elseif k == 'ASIN' then
+
        elseif k == 'ASIN' then
table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } );  
+
            table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } );  
elseif k == 'LCCN' then
+
        elseif k == 'LCCN' then
table.insert( new_list, {handler.label, lccn( v ) } );
+
            table.insert( new_list, {handler.label, lccn( v ) } );
elseif k == 'OL' then
+
        elseif k == 'OL' then
table.insert( new_list, {handler.label, openlibrary( v ) } );
+
            table.insert( new_list, {handler.label, openlibrary( v ) } );
elseif k == 'PMC' then
+
        elseif k == 'PMC' then
table.insert( new_list, {handler.label, pmc( v, options.Embargo ) } );
+
            table.insert( new_list, {handler.label, pmc( v, options.Embargo ) } );
elseif k == 'PMID' then
+
        elseif k == 'PMID' then
table.insert( new_list, {handler.label, pmid( v ) } );
+
            table.insert( new_list, {handler.label, pmid( v ) } );
elseif k == 'ISSN' then
+
        elseif k == 'ISSN' then
table.insert( new_list, {handler.label, issn( v ) } );
+
        table.insert( new_list, {handler.label, issn( v ) } );
elseif k == 'ISBN' then
+
        elseif k == 'ISBN' then
local ISBN = internallinkid( handler );
+
            local ISBN = internallinkid( handler );
if not checkisbn( v ) and not is_set(options.IgnoreISBN) then
+
            if not checkisbn( v ) and not is_set(options.IgnoreISBN) then
ISBN = ISBN .. seterror( 'bad_isbn', {}, false, " ", "" );
+
                ISBN = ISBN .. seterror( 'bad_isbn', {}, false, " ", "" );
end
+
            end
table.insert( new_list, {handler.label, ISBN } );
+
            table.insert( new_list, {handler.label, ISBN } );              
else
+
        else
error( cfg.messages['unknown_manual_ID'] );
+
            error( cfg.messages['unknown_manual_ID'] );
end
+
        end
end
+
    end
+
   
function comp( a, b ) -- used in following table.sort()
+
    function comp( a, b ) -- used in following table.sort()
return a[1] < b[1];
+
        return a[1] < b[1];
end
+
    end
+
   
table.sort( new_list, comp );
+
    table.sort( new_list, comp );
for k, v in ipairs( new_list ) do
+
    for k, v in ipairs( new_list ) do
new_list[k] = v[2];
+
        new_list[k] = v[2];
end
+
    end
+
   
return new_list;
+
    return new_list;
 
end
 
end
 
    
 
    
Line 1,146: Line 1,005:
 
-- Generates an error if more than one match is present.
 
-- Generates an error if more than one match is present.
 
function selectone( args, possible, error_condition, index )
 
function selectone( args, possible, error_condition, index )
local value = nil;
+
    local value = nil;
local selected = '';
+
    local selected = '';
local error_list = {};
+
    local error_list = {};
+
   
if index ~= nil then index = tostring(index); end
+
    if index ~= nil then index = tostring(index); end
+
   
-- Handle special case of "#" replaced by empty string
+
    -- Handle special case of "#" replaced by empty string
if index == '1' then
+
    if index == '1' then
for _, v in ipairs( possible ) do
+
        for _, v in ipairs( possible ) do
v = v:gsub( "#", "" );
+
            v = v:gsub( "#", "" );
if is_set(args[v]) then
+
            if is_set(args[v]) then
if value ~= nil and selected ~= v then
+
                if value ~= nil and selected ~= v then
table.insert( error_list, v );
+
                    table.insert( error_list, v );
else
+
                else
value = args[v];
+
                    value = args[v];
selected = v;
+
                    selected = v;
end
+
                end
end
+
            end
end
+
        end      
end
+
    end
+
   
for _, v in ipairs( possible ) do
+
    for _, v in ipairs( possible ) do
if index ~= nil then
+
        if index ~= nil then
v = v:gsub( "#", index );
+
            v = v:gsub( "#", index );
end
+
        end
if is_set(args[v]) then
+
        if is_set(args[v]) then
if value ~= nil and selected ~=  v then
+
            if value ~= nil and selected ~=  v then
table.insert( error_list, v );
+
                table.insert( error_list, v );
else
+
            else
value = args[v];
+
                value = args[v];
selected = v;
+
                selected = v;
end
+
            end
end
+
        end
end
+
    end
+
   
if #error_list > 0 then
+
    if #error_list > 0 then
local error_str = "";
+
        local error_str = "";
for _, k in ipairs( error_list ) do
+
        for _, k in ipairs( error_list ) do
if error_str ~= "" then error_str = error_str .. cfg.messages['parameter-separator'] end
+
            if error_str ~= "" then error_str = error_str .. cfg.messages['parameter-separator'] end
error_str = error_str .. wrap( 'parameter', k );
+
            error_str = error_str .. wrap( 'parameter', k );
end
+
        end
if #error_list > 1 then
+
        if #error_list > 1 then
error_str = error_str .. cfg.messages['parameter-final-separator'];
+
            error_str = error_str .. cfg.messages['parameter-final-separator'];
else
+
        else
error_str = error_str .. cfg.messages['parameter-pair-separator'];
+
            error_str = error_str .. cfg.messages['parameter-pair-separator'];
end
+
        end
error_str = error_str .. wrap( 'parameter', selected );
+
        error_str = error_str .. wrap( 'parameter', selected );
table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } );
+
        table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } );
end
+
    end
+
   
return value, selected;
+
    return value, selected;
 
end
 
end
  
Line 1,202: Line 1,061:
 
-- the citation information.
 
-- the citation information.
 
function COinS(data)
 
function COinS(data)
if 'table' ~= type(data) or nil == next(data) then
+
    if 'table' ~= type(data) or nil == next(data) then
return '';
+
        return '';
end
+
    end
+
   
local ctx_ver = "Z39.88-2004";
+
    local ctx_ver = "Z39.88-2004";
+
   
-- treat table strictly as an array with only set values.
+
    -- treat table strictly as an array with only set values.
local OCinSoutput = setmetatable( {}, {
+
    local OCinSoutput = setmetatable( {}, {
__newindex = function(self, key, value)
+
        __newindex = function(self, key, value)
if is_set(value) then
+
            if is_set(value) then
rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( removewikilink( value ) ) } );
+
                rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( removewikilink( value ) ) } );
end
+
            end
end
+
        end
});
+
    });
+
   
if is_set(data.Chapter) then
+
    if is_set(data.Chapter) then
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
+
        OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
OCinSoutput["rft.genre"] = "bookitem";
+
        OCinSoutput["rft.genre"] = "bookitem";
OCinSoutput["rft.btitle"] = data.Chapter;
+
        OCinSoutput["rft.btitle"] = data.Chapter;
OCinSoutput["rft.atitle"] = data.Title;
+
        OCinSoutput["rft.atitle"] = data.Title;
elseif is_set(data.Periodical) then
+
    elseif is_set(data.Periodical) then
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal";
+
        OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal";
OCinSoutput["rft.genre"] = "article";
+
        OCinSoutput["rft.genre"] = "article";
OCinSoutput["rft.jtitle"] = data.Periodical;
+
        OCinSoutput["rft.jtitle"] = data.Periodical;
OCinSoutput["rft.atitle"] = data.Title;
+
        OCinSoutput["rft.atitle"] = data.Title;
else
+
    else
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
+
        OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
OCinSoutput["rft.genre"] = "book"
+
        OCinSoutput["rft.genre"] = "book"
OCinSoutput["rft.btitle"] = data.Title;
+
        OCinSoutput["rft.btitle"] = data.Title;
end
+
    end
+
   
OCinSoutput["rft.place"] = data.PublicationPlace;
+
    OCinSoutput["rft.place"] = data.PublicationPlace;
OCinSoutput["rft.date"] = data.Date;
+
    OCinSoutput["rft.date"] = data.Date;
OCinSoutput["rft.series"] = data.Series;
+
    OCinSoutput["rft.series"] = data.Series;
OCinSoutput["rft.volume"] = data.Volume;
+
    OCinSoutput["rft.volume"] = data.Volume;
OCinSoutput["rft.issue"] = data.Issue;
+
    OCinSoutput["rft.issue"] = data.Issue;
OCinSoutput["rft.pages"] = data.Pages;
+
    OCinSoutput["rft.pages"] = data.Pages;
OCinSoutput["rft.edition"] = data.Edition;
+
    OCinSoutput["rft.edition"] = data.Edition;
OCinSoutput["rft.pub"] = data.PublisherName;
+
    OCinSoutput["rft.pub"] = data.PublisherName;
+
   
for k, v in pairs( data.ID_list ) do
+
    for k, v in pairs( data.ID_list ) do
local id, value = cfg.id_handlers[k].COinS;
+
        local id, value = cfg.id_handlers[k].COinS;
if k == 'ISBN' then value = cleanisbn( v ); else value = v; end
+
        if k == 'ISBN' then value = cleanisbn( v ); else value = v; end
if string.sub( id or "", 1, 4 ) == 'info' then
+
        if string.sub( id or "", 1, 4 ) == 'info' then
OCinSoutput["rft_id"] = table.concat{ id, "/", v };
+
            OCinSoutput["rft_id"] = table.concat{ id, "/", v };
else
+
        else
OCinSoutput[ id ] = value;
+
            OCinSoutput[ id ] = value;
end
+
        end
end
+
    end
+
   
local last, first;
+
    local last, first;
for k, v in ipairs( data.Authors ) do
+
    for k, v in ipairs( data.Authors ) do
last, first = v.last, v.first;
+
        last, first = v.last, v.first;
if k == 1 then
+
        if k == 1 then
if is_set(last) then
+
            if is_set(last) then
OCinSoutput["rft.aulast"] = last;
+
                OCinSoutput["rft.aulast"] = last;
end
+
            end
if is_set(first) then  
+
            if is_set(first) then  
OCinSoutput["rft.aufirst"] = first;
+
                OCinSoutput["rft.aufirst"] = first;
end
+
            end
end
+
        end
if is_set(last) and is_set(first) then
+
        if is_set(last) and is_set(first) then
OCinSoutput["rft.au"] = table.concat{ last, ", ", first };
+
            OCinSoutput["rft.au"] = table.concat{ last, ", ", first };
elseif is_set(last) then
+
        elseif is_set(last) then
OCinSoutput["rft.au"] = last;
+
            OCinSoutput["rft.au"] = last;
end
+
        end
end
+
    end
+
   
OCinSoutput.rft_id = data.URL;
+
    OCinSoutput.rft_id = data.URL;
OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage };
+
    OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage };
OCinSoutput = setmetatable( OCinSoutput, nil );
+
    OCinSoutput = setmetatable( OCinSoutput, nil );
+
   
-- sort with version string always first, and combine.
+
    -- sort with version string always first, and combine.
table.sort( OCinSoutput );
+
    table.sort( OCinSoutput );
table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver );  -- such as "Z39.88-2004"
+
    table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver );  -- such as "Z39.88-2004"
return table.concat(OCinSoutput, "&");
+
    return table.concat(OCinSoutput, "&");
 
end
 
end
  
 
+
--[[
--[[--------------------------< G E T _ I S O 6 3 9 _ C O D E >------------------------------------------------
 
 
 
Validates language names provided in |language= parameter if not an ISO639-1 code.  Handles the special case that is Norwegian where
 
ISO639-1 code 'no' is mapped to language name 'Norwegian Bokmål' by Extention:CLDR.
 
 
 
Returns the language name and associated ISO639-1 code.  Because case of the source may be incorrect or different from the case that Wikimedia
 
uses, the name comparisons are done in lower case and when a match is found, the Wikimedia version (assumed to be correct) is returned along
 
with the code.  When there is no match, we return the original language name string.
 
 
 
mw.language.fetchLanguageNames() will return a list of languages that aren't part of ISO639-1. Names that aren't ISO639-1 but that are included
 
in the list will be found if that name is provided in the |language= parameter.  For example, if |language=Samaritan Aramaic, that name will be
 
found with the associated code 'sam', not an ISO639-1 code.  When names are found and the associated code is not two characters, this function
 
returns only the Wikimedia language name.
 
 
 
Adapted from code taken from Module:Check ISO 639-1.
 
]]
 
 
 
function get_iso639_code (lang)
 
if 'Norwegian' == lang then
 
return lang, 'no'; -- special case related to Wikimedia remap of code 'no' at Extension:CLDR
 
end
 
 
local languages = mw.language.fetchLanguageNames('en', 'all') -- get a list of language names known to Wikimedia
 
-- ('all' is required for North Ndebele, South Ndebele, and Ojibwa)
 
local langlc = mw.ustring.lower(lang); -- lower case version for comparisons
 
 
for code, name in pairs(languages) do -- scan the list to see if we can find our language
 
if langlc == mw.ustring.lower(name) then
 
if 2 ~= code:len() then -- ISO639-1 codes only
 
return name; -- so return the name but not the code
 
end
 
return name, code; -- found it, return name to ensure proper capitalization and the ISO639-1 code
 
end
 
end
 
return lang; -- not valid language; return language in original case and nil for ISO639-1 code
 
end
 
 
 
--[[--------------------------< L A N G U A G E _ P A R A M E T E R >------------------------------------------
 
 
 
Get language name from ISO639-1 code value provided.  If a code is valid use the returned name; if not, then use the value that was provided with the language parameter.
 
 
 
There is an exception.  There are three ISO639-1 codes for Norewegian language variants.  There are two official variants: Norwegian Bokmål (code 'nb') and
 
Norwegian Nynorsk (code 'nn').  The third, code 'no',  is defined by ISO639-1 as 'Norwegian' though in Norway this is pretty much meaningless.  However, it appears
 
that on enwiki, editors are for the most part unaware of the nb and nn variants (compare page counts for these variants at Category:Articles with non-English-language external links.
 
 
 
Because Norwegian Bokmål is the most common language variant, Media wiki has been modified to return Norwegian Bokmål for ISO639-1 code 'no'. Here we undo that and
 
return 'Norwegian' when editors use |language=no.  We presume that editors don't know about the variants or can't descriminate between them.
 
 
 
See Help talk:Citation Style_1#An ISO 639-1 language name test
 
 
 
When |language= contains a valid ISO639-1 code, the page is assigned to the category for that code: Category:Norwegian-language sources (no) if
 
the page is a mainspace page and the ISO639-1 code is not 'en'.  Similarly, if the  parameter is |language=Norwegian, it will be categorized in the same way.
 
 
 
TODO: Error message when language or language code is not ISO639-1?
 
]]
 
 
 
function language_parameter (lang, namespace)
 
local code; -- the ISO639-1 two character code
 
local name; -- the language name
 
local test='';
 
 
if 0 == namespace and (('en' == lang:lower()) or ('english' == lang:lower())) then
 
table.insert (z.maintenance_cats, 'CS1 maint: English language specified'); -- add maintenance category if |language=English or |language=en in article space
 
end
 
 
 
if 2 == lang:len() then -- ISO639-1 language code are 2 characters (fetchLanguageName also supports 3 character codes)
 
name = mw.language.fetchLanguageName( lang:lower(), "en" ); -- get ISO 639-1 language name if Language is a proper code
 
end
 
 
 
if is_set (name) then -- if Language specified a valid ISO639-1 code
 
code = lang:lower(); -- save it
 
else
 
name, code = get_iso639_code (lang); -- attempt to get code from name (assign name here so that we are sure of proper capitalization)
 
end
 
 
 
if is_set (code) then
 
if 'no' == code then name = 'Norwegian' end; -- override wikimedia when code is 'no'
 
if 0 == namespace and 'en' ~= code then -- is this page main / article space and English not the language?
 
table.insert( z.properties_cats, 'CS1 ' .. name .. '-language sources (' .. code .. ')'); -- in main space and not English: categorize
 
end
 
end
 
return (" " .. wrap( 'language', name)); -- wrap with '(in ...)'
 
end
 
 
 
 
 
 
 
 
 
--[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------
 
 
This is the main function doing the majority of the citation
 
This is the main function doing the majority of the citation
 
formatting.
 
formatting.
 
]]
 
]]
 
function citation0( config, args)
 
function citation0( config, args)
--[[  
+
    --[[  
Load Input Parameters
+
    Load Input Parameters
The argument_wrapper facilitates the mapping of multiple
+
    The argment_wrapper facillitates the mapping of multiple
aliases to single internal variable.
+
    aliases to single internal variable.
]]
+
    ]]
local A = argument_wrapper( args );
+
    local A = argument_wrapper( args );
  
local i  
+
    local i  
local PPrefix = A['PPrefix']
+
    local PPrefix = A['PPrefix']
local PPPrefix = A['PPPrefix']
+
    local PPPrefix = A['PPPrefix']
if is_set( A['NoPP'] ) then PPPrefix = "" PPrefix = "" end
+
    if is_set( A['NoPP'] ) then PPPrefix = "" PPrefix = "" end
+
   
-- 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.  
local Authors = A['Authors'];
+
    local Authors = A['Authors'];
local a = extractnames( args, 'AuthorList' );
+
    local a = extractnames( args, 'AuthorList' );
  
local Coauthors = A['Coauthors'];
+
    local Coauthors = A['Coauthors'];
local Others = A['Others'];
+
    local Others = A['Others'];
local Editors = A['Editors'];
+
    local Editors = A['Editors'];
local e = extractnames( args, 'EditorList' );
+
    local e = extractnames( args, 'EditorList' );
  
local Year = A['Year'];
+
    local Year = A['Year'];
local PublicationDate = A['PublicationDate'];
+
    local PublicationDate = A['PublicationDate'];
local OrigYear = A['OrigYear'];
+
    local OrigYear = A['OrigYear'];
local Date = A['Date'];
+
    local Date = A['Date'];
local LayDate = A['LayDate'];
+
    local LayDate = A['LayDate'];
------------------------------------------------- Get title data
+
    ------------------------------------------------- Get title data
local Title = A['Title'];
+
    local Title = A['Title'];
local ScriptTitle = A['ScriptTitle'];
+
    local BookTitle = A['BookTitle'];
local BookTitle = A['BookTitle'];
+
    local Conference = A['Conference'];
local Conference = A['Conference'];
+
    local TransTitle = A['TransTitle'];
local TransTitle = A['TransTitle'];
+
    local TitleNote = A['TitleNote'];
local TitleNote = A['TitleNote'];
+
    local TitleLink = A['TitleLink'];
local TitleLink = A['TitleLink'];
+
    local Chapter = A['Chapter'];
local Chapter = A['Chapter'];
+
    local ChapterLink = A['ChapterLink'];
local ChapterLink = A['ChapterLink'];
+
    local TransChapter = A['TransChapter'];
local TransChapter = A['TransChapter'];
+
    local TitleType = A['TitleType'];
local TitleType = A['TitleType'];
+
    local Degree = A['Degree'];
local Degree = A['Degree'];
+
    local Docket = A['Docket'];
local Docket = A['Docket'];
+
    local ArchiveURL = A['ArchiveURL'];
local ArchiveURL = A['ArchiveURL'];
+
    local URL = A['URL']
local URL = A['URL']
+
    local URLorigin = A:ORIGIN('URL');
local URLorigin = A:ORIGIN('URL'); -- get name of parameter that holds URL
+
    local ChapterURL = A['ChapterURL'];
local ChapterURL = A['ChapterURL'];
+
    local ChapterURLorigin = A:ORIGIN('ChapterURL');
local ChapterURLorigin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL
+
    local ConferenceURL = A['ConferenceURL'];
local ConferenceURL = A['ConferenceURL'];
+
    local ConferenceURLorigin = A:ORIGIN('ConferenceURL');
local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL
+
    local Periodical = A['Periodical'];
local Periodical = A['Periodical'];
 
  
 
local Series = A['Series'];
 
local Series = A['Series'];
local Volume = A['Volume'];
+
    local Volume = A['Volume'];
local Issue = A['Issue'];
+
    local Issue = A['Issue'];
local Position = '';
+
    local Position = '';
local Page = A['Page'];
+
    local Page = A['Page'];
local Pages = hyphentodash( A['Pages'] );
+
    local Pages = hyphentodash( A['Pages'] );
local At = A['At'];
+
    local At = A['At'];
  
local Edition = A['Edition'];
+
    local Edition = A['Edition'];
local PublicationPlace = A['PublicationPlace']
+
    local PublicationPlace = A['PublicationPlace']
local Place = A['Place'];
+
    local Place = A['Place'];
+
   
local PublisherName = A['PublisherName'];
+
    local PublisherName = A['PublisherName'];
local RegistrationRequired = A['RegistrationRequired'];
+
    local RegistrationRequired = A['RegistrationRequired'];
local SubscriptionRequired = A['SubscriptionRequired'];
+
    local SubscriptionRequired = A['SubscriptionRequired'];
local Via = A['Via'];
+
    local Via = A['Via'];
local AccessDate = A['AccessDate'];
+
    local AccessDate = A['AccessDate'];
local ArchiveDate = A['ArchiveDate'];
+
    local ArchiveDate = A['ArchiveDate'];
local Agency = A['Agency'];
+
    local Agency = A['Agency'];
local DeadURL = A['DeadURL']
+
    local DeadURL = A['DeadURL']
local Language = A['Language'];
+
    local Language = A['Language'];
local Format = A['Format'];
+
    local Format = A['Format'];
local Ref = A['Ref'];
+
    local Ref = A['Ref'];
 
local DoiBroken = A['DoiBroken'];
 
local DoiBroken = A['DoiBroken'];
 
local ID = A['ID'];
 
local ID = A['ID'];
local ASINTLD = A['ASINTLD'];
+
    local ASINTLD = A['ASINTLD'];
local IgnoreISBN = A['IgnoreISBN'];
+
    local IgnoreISBN = A['IgnoreISBN'];
local Embargo = A['Embargo'];
+
    local Embargo = A['Embargo'];
  
local ID_list = extractids( args );
+
    local ID_list = extractids( args );
--[[ Hide unfinished cite newsgroup code so that long delayed update can take place
+
   
-- special case for cite newsgroup which uses |id= for a usenet article or post id
+
    -- special case for cite newsgroup which uses |id= for a usenet article or post id
-- |id= is not included in COinS so here we convert it to an ID that will be included in COinS
+
    -- |id= is not included in COinS so here we convert it to an ID that will be included in COinS
if ('newsgroup' == config.CitationClass) and (is_set (ID)) then
+
    if ('newsgroup' == config.CitationClass) and (is_set (ID)) then
ID_list['USENETID']=ID; -- add this new 'id' to the list of IDs
+
    ID_list['USENETID']=ID; -- add this new 'id' to the list of IDs
ID = ''; -- and unset
+
    ID = ''; -- and unset
end
+
    end
]]
+
   
local Quote = A['Quote'];
+
    local Quote = A['Quote'];
local PostScript = A['PostScript'];
+
    local PostScript = A['PostScript'];
  
local LayURL = A['LayURL'];
+
    local LayURL = A['LayURL'];
local LaySource = A['LaySource'];
+
    local LaySource = A['LaySource'];
local Transcript = A['Transcript'];
+
    local Transcript = A['Transcript'];
local TranscriptURL = A['TranscriptURL']  
+
    local TranscriptURL = A['TranscriptURL']  
local TranscriptURLorigin = A:ORIGIN('TranscriptURL'); -- get name of parameter that holds TranscriptURL
+
    local TranscriptURLorigin = A:ORIGIN('TranscriptURL');
local sepc = A['Separator'];
+
    local sepc = A['Separator'];
  
local LastAuthorAmp = A['LastAuthorAmp'];
+
    local LastAuthorAmp = A['LastAuthorAmp'];
local no_tracking_cats = A['NoTracking'];
+
    local no_tracking_cats = A['NoTracking'];
  
 
--these are used by cite interview
 
--these are used by cite interview
Line 1,482: Line 1,252:
  
 
--local variables that are not cs1 parameters
 
--local variables that are not cs1 parameters
local page_type; -- is this needed?  Doesn't appear to be used anywhere;
+
    local page_type; -- is this needed?  Doesn't appear to be used anywhere;
local use_lowercase = ( sepc ~= '.' );
+
    local use_lowercase = ( sepc ~= '.' );
local this_page = mw.title.getCurrentTitle(); --Also used for COinS and for language
+
    local this_page = mw.title.getCurrentTitle(); --Also used for COinS and for language
 
local anchor_year; -- used in the CITEREF identifier
 
local anchor_year; -- used in the CITEREF identifier
 
local COinS_date; -- used in the COinS metadata
 
local COinS_date; -- used in the COinS metadata
Line 1,501: Line 1,271:
 
--check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories.
 
--check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories.
 
if not is_set(no_tracking_cats) then -- ignore if we are already not going to categorize this page
 
if not is_set(no_tracking_cats) then -- ignore if we are already not going to categorize this page
if inArray (this_page.nsText, cfg.uncategorized_namespaces) then
+
for k, v in pairs( cfg.uncategorized_namespaces ) do -- otherwise, spin through the list of namespaces we don't include in error categories
no_tracking_cats = "true"; -- set no_tracking_cats
+
if this_page.nsText == v then -- if we find one
end
+
no_tracking_cats = "true"; -- set no_trackin_cats
end
+
break; -- and we're done
 +
            end
 +
        end
 +
    end
  
 
-- check for extra |page=, |pages= or |at= parameters.  
 
-- check for extra |page=, |pages= or |at= parameters.  
if is_set(Page) then
+
    if is_set(Page) then
if is_set(Pages) or is_set(At) then
+
        if is_set(Pages) or is_set(At) then
Page = Page .. " " .. seterror('extra_pages'); -- add error message
+
            Page = Page .. " " .. seterror('extra_pages'); -- add error message
Pages = ''; -- unset the others
+
            Pages = ''; -- unset the others
At = '';
+
            At = '';
end
+
        end
elseif is_set(Pages) then
+
    elseif is_set(Pages) then
if is_set(At) then
+
        if is_set(At) then
Pages = Pages .. " " .. seterror('extra_pages'); -- add error messages
+
            Pages = Pages .. " " .. seterror('extra_pages'); -- add error messages
At = ''; -- unset
+
            At = ''; -- unset
end
+
        end
end
+
    end  
  
 
-- both |publication-place= and |place= (|location=) allowed if different
 
-- both |publication-place= and |place= (|location=) allowed if different
if not is_set(PublicationPlace) and is_set(Place) then
+
    if not is_set(PublicationPlace) and is_set(Place) then
PublicationPlace = Place; -- promote |place= (|location=) to |publication-place
+
        PublicationPlace = Place; -- promote |place= (|location=) to |publication-place
end
+
    end
+
   
if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
+
    if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
+
   
 
--[[
 
--[[
 
Parameter remapping for cite encyclopedia:
 
Parameter remapping for cite encyclopedia:
Line 1,571: Line 1,344:
 
-- check for special case where |separator=none
 
-- check for special case where |separator=none
 
if 'none' == sepc:lower() then -- if |separator=none
 
if 'none' == sepc:lower() then -- if |separator=none
sepc = ''; -- then set it to an empty string
+
sepc = ''; -- then set it to a empty string
 
end
 
end
  
Line 1,683: Line 1,456:
 
end
 
end
 
elseif is_set(PublicationDate) then -- use PublicationDate when |date= and |year= are not set
 
elseif is_set(PublicationDate) then -- use PublicationDate when |date= and |year= are not set
Date = PublicationDate; -- promote PublicationDate to Date
+
Date = PublicationDate; -- promonte PublicationDate to Date
 
PublicationDate = ''; -- unset, no longer needed
 
PublicationDate = ''; -- unset, no longer needed
 
end
 
end
Line 1,705: Line 1,478:
 
-- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.
 
-- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.
  
-- COinS metadata (see <http://ocoins.info/>) for
+
    -- COinS metadata (see <http://ocoins.info/>) for
-- automated parsing of citation information.
+
    -- automated parsing of citation information.
-- this is the function call to COinS()
+
    local OCinSoutput = COinS{
local OCinSoutput = COinS{
+
        ['Periodical'] = Periodical,
['Periodical'] = Periodical,
+
        ['Chapter'] = Chapter,
['Chapter'] = strip_apostrophe_markup (Chapter), -- Chapter stripped of bold / italic wikimarkup
+
        ['Title'] = Title,
['Title'] = make_coins_title (Title, ScriptTitle), -- Title and ScriptTitle stripped of bold / italic wikimarkup
+
        ['PublicationPlace'] = PublicationPlace,
['PublicationPlace'] = PublicationPlace,
+
        ['Date'] = first_set(COinS_date, Date), -- COinS_date has correctly formatted date if Date is valid; any reason to keep Date here?  Should we be including invalid dates in metadata?
['Date'] = first_set(COinS_date, Date), -- COinS_date has correctly formatted date if Date is valid; any reason to keep Date here?  Should we be including invalid dates in metadata?
+
        ['Series'] = Series,
['Series'] = Series,
+
        ['Volume'] = Volume,
['Volume'] = Volume,
+
        ['Issue'] = Issue,
['Issue'] = Issue,
+
        ['Pages'] = get_coins_pages (first_set(Page, Pages, At)), -- pages stripped of external links
['Pages'] = get_coins_pages (first_set(Page, Pages, At)), -- pages stripped of external links
+
        ['Edition'] = Edition,
['Edition'] = Edition,
+
        ['PublisherName'] = PublisherName,
['PublisherName'] = PublisherName,
+
        ['URL'] = first_set( URL, ChapterURL ),
['URL'] = first_set( URL, ChapterURL ),
+
        ['Authors'] = a,
['Authors'] = a,
+
        ['ID_list'] = ID_list,
['ID_list'] = ID_list,
+
        ['RawPage'] = this_page.prefixedText,
['RawPage'] = this_page.prefixedText,
+
    };
};
+
 
--[[Why is this here?  Why are we mapping Title to Chapter when Periodical is set?
+
    if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then
if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then
+
        Chapter = Title;
Chapter = Title;
+
        ChapterLink = TitleLink;
ChapterLink = TitleLink;
+
        TransChapter = TransTitle;
TransChapter = TransTitle;
+
        Title = '';
Title = '';
+
        TitleLink = '';
TitleLink = '';
+
        TransTitle = '';
TransTitle = '';
+
    end
end
+
 
]]
 
--[[ Hide unfinished cite newsgroup code so that long delayed update can take place
 
 
-- special case for cite newsgroup.  Do this after COinS because we are modifying Publishername and ID
 
-- special case for cite newsgroup.  Do this after COinS because we are modifying Publishername and ID
 
if 'newsgroup' == config.CitationClass then
 
if 'newsgroup' == config.CitationClass then
 
if is_set (PublisherName) then
 
if is_set (PublisherName) then
PublisherName = '[Newsgroup]:&nbsp;' ..  externallink( 'news:' .. PublisherName, PublisherName );
+
PublisherName = '[[Newsgroup]]:&nbsp;' ..  externallink( 'news:' .. PublisherName, PublisherName );
 
end
 
end
 
end
 
end
]]
 
  
  
-- Now perform various field substitutions.
+
 
-- We also add leading spaces and surrounding markup and punctuation to the
+
    -- Now perform various field substitutions.
-- various parts of the citation, but only when they are non-nil.
+
    -- We also add leading spaces and surrounding markup and punctuation to the
if not is_set(Authors) then
+
    -- various parts of the citation, but only when they are non-nil.
local Maximum = tonumber( A['DisplayAuthors'] );
+
    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  
+
        -- Preserve old-style implicit et al.
Maximum = 8;
+
        if not is_set(Maximum) and #a == 9 then  
table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } );
+
            Maximum = 8;
elseif not is_set(Maximum) then
+
            table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } );
Maximum = #a + 1;
+
        elseif not is_set(Maximum) then
end
+
            Maximum = #a + 1;
+
        end
local control = {  
+
           
sep = A["AuthorSeparator"] .. " ",
+
        local control = {  
namesep = (first_set(A["AuthorNameSeparator"], A["NameSeparator"]) or "") .. " ",
+
            sep = A["AuthorSeparator"] .. " ",
format = A["AuthorFormat"],
+
            namesep = (first_set(A["AuthorNameSeparator"], A["NameSeparator"]) or "") .. " ",
maximum = Maximum,
+
            format = A["AuthorFormat"],
lastauthoramp = LastAuthorAmp,
+
            maximum = Maximum,
page_name = this_page.text -- get current page name so that we don't wikilink to it via authorlinkn
+
            lastauthoramp = LastAuthorAmp
};
+
        };
+
       
-- If the coauthor field is also used, prevent ampersand and et al. formatting.
+
        -- If the coauthor field is also used, prevent ampersand and et al. formatting.
if is_set(Coauthors) then
+
        if is_set(Coauthors) then
control.lastauthoramp = nil;
+
            control.lastauthoramp = nil;
control.maximum = #a + 1;
+
            control.maximum = #a + 1;
end
+
        end
+
       
Authors = listpeople(control, a)  
+
        Authors = listpeople(control, a)  
end
+
    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
 
if not is_set(Authors) and is_set(Coauthors) then -- coauthors aren't displayed if one of authors=, authorn=, or lastn= isn't specified
Line 1,781: Line 1,551:
 
end
 
end
  
local EditorCount
+
    local EditorCount
if not is_set(Editors) then
+
    if not is_set(Editors) then
local Maximum = tonumber( A['DisplayEditors'] );
+
        local Maximum = tonumber( A['DisplayEditors'] );
-- Preserve old-style implicit et al.
+
        -- Preserve old-style implicit et al.
if not is_set(Maximum) and #e == 4 then  
+
        if not is_set(Maximum) and #e == 4 then  
Maximum = 3;
+
            Maximum = 3;
table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } );
+
            table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } );
elseif not is_set(Maximum) then
+
        elseif not is_set(Maximum) then
Maximum = #e + 1;
+
            Maximum = #e + 1;
end
+
        end
  
local control = {  
+
        local control = {  
sep = A["EditorSeparator"] .. " ",
+
            sep = A["EditorSeparator"] .. " ",
namesep = (first_set(A["EditorNameSeparator"], A["NameSeparator"]) or "") .. " ",
+
            namesep = (first_set(A["EditorNameSeparator"], A["NameSeparator"]) or "") .. " ",
format = A['EditorFormat'],
+
            format = A['EditorFormat'],
maximum = Maximum,
+
            maximum = Maximum,
lastauthoramp = LastAuthorAmp,
+
            lastauthoramp = LastAuthorAmp
page_name = this_page.text -- get current page name so that we don't wikilink to it via authorlinkn
+
        };
};
 
  
Editors, EditorCount = listpeople(control, e);
+
        Editors, EditorCount = listpeople(control, e);
else
+
    else
EditorCount = 1;
+
        EditorCount = 1;
end
+
    end
  
local Cartography = "";
+
    local Cartography = "";
local Scale = "";
+
    local Scale = "";
if config.CitationClass == "map" then
+
    if config.CitationClass == "map" then
if not is_set( Authors ) and is_set( PublisherName ) then
+
        if not is_set( Authors ) and is_set( PublisherName ) then
Authors = PublisherName;
+
            Authors = PublisherName;
PublisherName = "";
+
            PublisherName = "";
end
+
        end
Cartography = A['Cartography'];
+
        Cartography = A['Cartography'];
if is_set( Cartography ) then
+
        if is_set( Cartography ) then
Cartography = sepc .. " " .. wrap( 'cartography', Cartography, use_lowercase );
+
            Cartography = sepc .. " " .. wrap( 'cartography', Cartography, use_lowercase );
end
+
        end      
Scale = A['Scale'];
+
        Scale = A['Scale'];
if is_set( Scale ) then
+
        if is_set( Scale ) then
Scale = sepc .. " " .. Scale;
+
            Scale = sepc .. " " .. Scale;
end
+
        end      
end
+
    end
+
   
if  not is_set(URL) and
+
    if  not is_set(URL) and
not is_set(ChapterURL) and
+
        not is_set(ChapterURL) and
not is_set(ArchiveURL) and
+
        not is_set(ArchiveURL) and
not is_set(ConferenceURL) and
+
        not is_set(ConferenceURL) and
not is_set(TranscriptURL) then
+
        not is_set(TranscriptURL) then
+
       
-- Test if cite web or cite podcast |url= is missing or empty  
+
        -- Test if cite web or cite podcast |url= is missing or empty  
 
if inArray(config.CitationClass, {"web","podcast"}) then
 
if inArray(config.CitationClass, {"web","podcast"}) then
 
table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } );
 
table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } );
 
end
 
end
+
       
-- Test if accessdate is given without giving a URL
+
        -- Test if accessdate is given without giving a URL
if is_set(AccessDate) then
+
        if is_set(AccessDate) then
table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } );
+
            table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } );
AccessDate = '';
+
            AccessDate = '';
end
+
        end
+
       
-- Test if format is given without giving a URL
+
        -- Test if format is given without giving a URL
if is_set(Format) then
+
        if is_set(Format) then
Format = Format .. seterror( 'format_missing_url' );
+
            Format = Format .. seterror( 'format_missing_url' );
end
+
        end
end
+
    end
+
   
-- Test if citation has no title
+
    -- Test if citation has no title
if not is_set(Title) and
+
    if not is_set(Chapter) and
not is_set(Periodical) and
+
        not is_set(Title) and
not is_set(Conference) and
+
        not is_set(Periodical) and
not is_set(TransTitle) and
+
        not is_set(Conference) and
not is_set(ScriptTitle) then
+
        not is_set(TransTitle) and
table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } );
+
        not is_set(TransChapter) then
end
+
        table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } );
+
    end
Format = is_set(Format) and " (" .. Format .. ")" or "";
+
   
+
    Format = is_set(Format) and " (" .. Format .. ")" or "";
local OriginalURL = URL
+
   
DeadURL = DeadURL:lower();
+
    local OriginalURL = URL
if is_set( ArchiveURL ) then
+
    DeadURL = DeadURL:lower();
if ( DeadURL ~= "no" ) then
+
    if is_set( ArchiveURL ) then
URL = ArchiveURL
+
        if ( DeadURL ~= "no" ) then
URLorigin = A:ORIGIN('ArchiveURL')
+
            URL = ArchiveURL
end
+
            URLorigin = A:ORIGIN('ArchiveURL')
end
+
        end
+
    end
-- Format chapter / article title
+
   
if is_set(Chapter) and is_set(ChapterLink) then  
+
    -- Format chapter / article title
Chapter = "[[" .. ChapterLink .. "|" .. Chapter .. "]]";
+
    if is_set(Chapter) and is_set(ChapterLink) then  
end
+
        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","newsgroup"}) 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  
  
Chapter = kern_quotes (Chapter); -- if necessary, separate chapter title's leading and trailing quote marks from Module provided quote marks
+
--[[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,  
Chapter = wrap( 'quoted-title', Chapter );
+
use that value; if not, then use the value that was provided with the language parameter.
TransChapter = wrap( 'trans-quoted-title', TransChapter );
 
 
 
local TransError = ""
 
if is_set(TransChapter) then
 
if not is_set(Chapter) then
 
TransError = " " .. seterror( 'trans_missing_chapter' ); -- add error message
 
else
 
TransChapter = " " .. TransChapter;
 
end
 
end
 
 
 
Chapter = Chapter .. TransChapter;
+
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(Chapter) then
+
if is_set (Language) then
if not is_set(ChapterLink) then
+
-- local name = mw.language.fetchLanguageName( Language:lower(), "en" ); -- experiment: this seems to return correct ISO 639-1 language names
if is_set(ChapterURL) then
+
local name = cfg.iso639_1[Language:lower()]; -- get the language name if Language parameter has a valid iso 639-1 code
Chapter = externallink( ChapterURL, Chapter ) .. TransError;
+
if nil == name then
if not is_set(URL) then
+
Language=" " .. wrap( 'language', Language ); -- no match, use parameter's value
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; -- ChapterURLorigin holds the name of parameter
 
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 inArray(config.CitationClass, {"web","news","journal","pressrelease","conference","podcast"}) then
 
Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks
 
Title = wrap( 'quoted-title', Title );
 
Title = script_concatenate (Title, ScriptTitle); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped
 
TransTitle = wrap( 'trans-quoted-title', TransTitle );
 
-- Chapter = ''; -- chapter not allowed - disabled because doing this here doesn't display error msg and promoted url is lost
 
else
 
Title = wrap( 'italic-title', Title );
 
Title = script_concatenate (Title, ScriptTitle); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped
 
TransTitle = wrap( 'trans-italic-title', TransTitle );
 
end
 
 
 
TransError = "";
 
if is_set(TransTitle) then
 
if is_set(Title) then
 
TransTitle = " " .. TransTitle;
 
else
 
TransError = " " .. seterror( 'trans_missing_title' );
 
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
 
else
local Time = A['Time'];
+
if 0 == this_page.namespace and 'en' ~= Language:lower() then --found a match; is this page main / article space and English not the language?
if is_set(Time) then
+
Language=" " .. wrap( 'language', name .. '[[Category:Articles with ' .. name .. '-language external links]]' ); -- in main space and not English: categorize
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
 
else
Pages = sepc .." " .. PPPrefix .. Pages;
+
Language=" " .. wrap( 'language', name ); --not in mainspace or language is English so don't categorize
 
end
 
end
 
end
 
end
 
else
 
else
if is_set(Periodical) and
+
Language=""; -- language not specified so make sure this is an empty string;
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
 
 
 
if is_set (Language) then
 
Language = language_parameter (Language, this_page.namespace); -- format, categories (article namespace only), name from ISO639-1, etc
 
else
 
Language=""; -- language not specified so make sure this is an empty string;
 
 
end
 
end
  
Line 2,051: Line 1,843:
  
 
TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";
 
TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";
Edition = is_set(Edition) and (" " .. wrap( 'edition', Edition )) or "";
+
    Edition = is_set(Edition) and (" " .. wrap( 'edition', Edition )) or "";
Issue = is_set(Issue) and (" (" .. Issue .. ")") or "";
+
    Issue = is_set(Issue) and (" (" .. Issue .. ")") or "";
Series = is_set(Series) and (sepc .. " " .. Series) or "";
+
    Series = is_set(Series) and (sepc .. " " .. Series) or "";
OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or "";
+
    OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or "";
Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";
+
    Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";
  
if is_set(Volume) then
+
    if is_set(Volume) then
if ( mw.ustring.len(Volume) > 4 )
+
        if ( mw.ustring.len(Volume) > 4 )
  then Volume = sepc .." " .. Volume;
+
          then Volume = sepc .." " .. Volume;
  else Volume = " <b>" .. hyphentodash(Volume) .. "</b>";
+
          else Volume = " <b>" .. hyphentodash(Volume) .. "</b>";
end
+
        end
end
+
    end
  
 
--[[ This code commented out while discussion continues until after week of 2014-03-23 live module update;
 
--[[ This code commented out while discussion continues until after week of 2014-03-23 live module update;
if is_set(Volume) then
+
    if is_set(Volume) then
if ( mw.ustring.len(Volume) > 4 )
+
        if ( mw.ustring.len(Volume) > 4 )
  then Volume = sepc .. " " .. Volume;
+
          then Volume = sepc .. " " .. Volume;
  else
+
          else
  Volume = " <b>" .. hyphentodash(Volume) .. "</b>";
+
              Volume = " <b>" .. hyphentodash(Volume) .. "</b>";
  if is_set(Series) then Volume = sepc .. Volume;
+
              if is_set(Series) then Volume = sepc .. Volume;
  end
+
              end
end
+
        end
end
+
    end
]]
+
]]  
------------------------------------ totally unrelated data
+
    ------------------------------------ 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
+
    --[[ 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).
+
    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.
+
    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
+
    if is_set(Via) then
Via = " " .. wrap( 'via', Via );
+
        Via = " " .. wrap( 'via', Via );
end
+
    end
  
 
if is_set(SubscriptionRequired) then
 
if is_set(SubscriptionRequired) then
SubscriptionRequired = sepc .. " " .. cfg.messages['subscription']; --here when 'via' parameter not used but 'subscription' is
+
        SubscriptionRequired = sepc .. " " .. cfg.messages['subscription']; --here when 'via' parameter not used but 'subscription' is
elseif is_set(RegistrationRequired) then
+
    elseif is_set(RegistrationRequired) then
SubscriptionRequired = sepc .. " " .. cfg.messages['registration']; --here when 'via' and 'subscription' parameters not used but 'registration' is
+
        SubscriptionRequired = sepc .. " " .. cfg.messages['registration']; --here when 'via' and 'subscription' parameters not used but 'registration' is
end
+
    end
  
if is_set(AccessDate) then
+
    if is_set(AccessDate) then
local retrv_text = " " .. cfg.messages['retrieved']
+
        local retrv_text = " " .. cfg.messages['retrieved']
if (sepc ~= ".") then retrv_text = retrv_text:lower() end
+
        if (sepc ~= ".") then retrv_text = retrv_text:lower() end
AccessDate = '<span class="reference-accessdate">' .. sepc
+
        AccessDate = '<span class="reference-accessdate">' .. sepc
.. substitute( retrv_text, {AccessDate} ) .. '</span>'
+
            .. substitute( retrv_text, {AccessDate} ) .. '</span>'
end
+
    end
+
   
if is_set(ID) then ID = sepc .." ".. ID; end
+
    if is_set(ID) then ID = sepc .." ".. ID; end
 
   if "thesis" == config.CitationClass and is_set(Docket) then
 
   if "thesis" == config.CitationClass and is_set(Docket) then
 
ID = sepc .." Docket ".. Docket .. ID;
 
ID = sepc .." Docket ".. Docket .. ID;
 
end
 
end
  
+
   
ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo} );
+
    ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo} );
  
if is_set(URL) then
+
    if is_set(URL) then
URL = " " .. externallink( URL, nil, URLorigin );
+
        URL = " " .. externallink( URL, nil, URLorigin );
end
+
    end
  
if is_set(Quote) then
+
    if is_set(Quote) then
if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then
+
        if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then
Quote = Quote:sub(2,-2);
+
            Quote = Quote:sub(2,-2);
end
+
        end
Quote = sepc .." " .. wrap( 'quoted-text', Quote );  
+
        Quote = sepc .." " .. wrap( 'quoted-text', Quote );  
PostScript = ""; -- CS1 does not supply terminal punctuation when |quote= is set
+
        PostScript = ""; -- CS1 does not supply terminal punctuation when |quote= is set
end
+
    end
+
   
local Archived
+
    local Archived
if is_set(ArchiveURL) then
+
    if is_set(ArchiveURL) then
if not is_set(ArchiveDate) then
+
        if not is_set(ArchiveDate) then
ArchiveDate = seterror('archive_missing_date');
+
            ArchiveDate = seterror('archive_missing_date');
end
+
        end
if "no" == DeadURL then
+
        if "no" == DeadURL then
local arch_text = cfg.messages['archived'];
+
            local arch_text = cfg.messages['archived'];
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'],
{ externallink( ArchiveURL, arch_text ), ArchiveDate } );
+
                { externallink( ArchiveURL, arch_text ), ArchiveDate } );
if not is_set(OriginalURL) then
+
            if not is_set(OriginalURL) then
Archived = Archived .. " " .. seterror('archive_missing_url');  
+
                Archived = Archived .. " " .. seterror('archive_missing_url');                              
end
+
            end
elseif is_set(OriginalURL) then
+
        elseif is_set(OriginalURL) then
local arch_text = cfg.messages['archived-dead'];
+
            local arch_text = cfg.messages['archived-dead'];
if sepc ~= "." then arch_text = arch_text:lower() end
+
            if sepc ~= "." then arch_text = arch_text:lower() end
Archived = sepc .. " " .. substitute( arch_text,
+
            Archived = sepc .. " " .. substitute( arch_text,
{ externallink( OriginalURL, cfg.messages['original'] ), ArchiveDate } );
+
                { externallink( OriginalURL, cfg.messages['original'] ), ArchiveDate } );
else
+
        else
local arch_text = cfg.messages['archived-missing'];
+
            local arch_text = cfg.messages['archived-missing'];
if sepc ~= "." then arch_text = arch_text:lower() end
+
            if sepc ~= "." then arch_text = arch_text:lower() end
Archived = sepc .. " " .. substitute( arch_text,  
+
            Archived = sepc .. " " .. substitute( arch_text,  
{ seterror('archive_missing_url'), ArchiveDate } );
+
                { seterror('archive_missing_url'), ArchiveDate } );
end
+
        end
else
+
    else
Archived = ""
+
        Archived = ""
end
+
    end
+
   
local Lay
+
    local Lay
if is_set(LayURL) then
+
    if is_set(LayURL) then
if is_set(LayDate) then LayDate = " (" .. LayDate .. ")" end
+
        if is_set(LayDate) then LayDate = " (" .. LayDate .. ")" end
if is_set(LaySource) then  
+
        if is_set(LaySource) then  
LaySource = " &ndash; ''" .. safeforitalics(LaySource) .. "''";
+
            LaySource = " &ndash; ''" .. safeforitalics(LaySource) .. "''";
else
+
        else
LaySource = "";
+
            LaySource = "";
end
+
        end
if sepc == '.' then
+
        if sepc == '.' then
Lay = sepc .. " " .. externallink( LayURL, cfg.messages['lay summary'] ) .. LaySource .. LayDate
+
            Lay = sepc .. " " .. externallink( LayURL, cfg.messages['lay summary'] ) .. LaySource .. LayDate
else
+
        else
Lay = sepc .. " " .. externallink( LayURL, cfg.messages['lay summary']:lower() ) .. LaySource .. LayDate
+
            Lay = sepc .. " " .. externallink( LayURL, cfg.messages['lay summary']:lower() ) .. LaySource .. LayDate
end
+
        end          
else
+
    else
Lay = "";
+
        Lay = "";
end
+
    end
+
   
if is_set(Transcript) then
+
    if is_set(Transcript) then
if is_set(TranscriptURL) then Transcript = externallink( TranscriptURL, Transcript ); end
+
        if is_set(TranscriptURL) then Transcript = externallink( TranscriptURL, Transcript ); end
elseif is_set(TranscriptURL) then
+
    elseif is_set(TranscriptURL) then
Transcript = externallink( TranscriptURL, nil, TranscriptURLorigin );
+
        Transcript = externallink( TranscriptURL, nil, TranscriptURLorigin );
end
+
    end
+
   
local Publisher;
+
    local Publisher;
if is_set(Periodical) and
+
    if is_set(Periodical) and
not inArray(config.CitationClass, {"encyclopaedia","web","pressrelease","podcast"}) then
+
        not inArray(config.CitationClass, {"encyclopaedia","web","pressrelease","podcast"}) then
if is_set(PublisherName) then
+
        if is_set(PublisherName) then
if is_set(PublicationPlace) then
+
            if is_set(PublicationPlace) then
Publisher = PublicationPlace .. ": " .. PublisherName;
+
                Publisher = PublicationPlace .. ": " .. PublisherName;
else
+
            else
Publisher = PublisherName;   
+
                Publisher = PublisherName;   
end
+
            end
elseif is_set(PublicationPlace) then
+
        elseif is_set(PublicationPlace) then
Publisher= PublicationPlace;
+
            Publisher= PublicationPlace;
else  
+
        else  
Publisher = "";
+
            Publisher = "";
end
+
        end
if is_set(PublicationDate) then
+
        if is_set(PublicationDate) then
if is_set(Publisher) then
+
            if is_set(Publisher) then
Publisher = Publisher .. ", " .. wrap( 'published', PublicationDate );
+
                Publisher = Publisher .. ", " .. wrap( 'published', PublicationDate );
else
+
            else
Publisher = PublicationDate;
+
                Publisher = PublicationDate;
end
+
            end
end
+
        end
if is_set(Publisher) then
+
        if is_set(Publisher) then
Publisher = " (" .. Publisher .. ")";
+
            Publisher = " (" .. Publisher .. ")";
end
+
        end
else
+
    else
if is_set(PublicationDate) then
+
        if is_set(PublicationDate) then
PublicationDate = " (" .. wrap( 'published', PublicationDate ) .. ")";
+
            PublicationDate = " (" .. wrap( 'published', PublicationDate ) .. ")";
end
+
        end
if is_set(PublisherName) then
+
        if is_set(PublisherName) then
if is_set(PublicationPlace) then
+
            if is_set(PublicationPlace) then
Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
+
                Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
else
+
            else
Publisher = sepc .. " " .. PublisherName .. PublicationDate;   
+
                Publisher = sepc .. " " .. PublisherName .. PublicationDate;   
end
+
            end          
elseif is_set(PublicationPlace) then  
+
        elseif is_set(PublicationPlace) then  
Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
+
            Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
else  
+
        else  
Publisher = PublicationDate;
+
            Publisher = PublicationDate;
end
+
        end
end
+
    end
+
   
-- Several of the above rely upon detecting this as nil, so do it last.
+
    -- Several of the above rely upon detecting this as nil, so do it last.
if is_set(Periodical) then
+
    if is_set(Periodical) then
if is_set(Title) or is_set(TitleNote) then  
+
        if is_set(Title) or is_set(TitleNote) then  
Periodical = sepc .. " " .. wrap( 'italic-title', Periodical )  
+
            Periodical = sepc .. " " .. wrap( 'italic-title', Periodical )  
else  
+
        else  
Periodical = wrap( 'italic-title', Periodical )
+
            Periodical = wrap( 'italic-title', Periodical )
end
+
        end
end
+
    end
  
 
--[[
 
--[[
Line 2,232: Line 2,024:
 
end
 
end
  
-- Piece all bits together at last.  Here, all should be non-nil.
+
    -- Piece all bits together at last.  Here, all should be non-nil.
-- We build things this way because it is more efficient in LUA
+
    -- We build things this way because it is more efficient in LUA
-- not to keep reassigning to the same string variable over and over.
+
    -- not to keep reassigning to the same string variable over and over.
  
local tcommon
+
    local tcommon
if inArray(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then
+
    if inArray(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 = safejoin( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Scale, Series,  
+
        tcommon = safejoin( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Scale, Series,  
Language, Cartography, Edition, Publisher, Agency, Volume, Issue}, sepc );
+
            Language, Cartography, Edition, Publisher, Agency, Volume, Issue}, sepc );
else  
+
    else  
tcommon = safejoin( {Title, TitleNote, Conference, Periodical, Format, TitleType, Scale, Series, Language,  
+
        tcommon = safejoin( {Title, TitleNote, Conference, Periodical, Format, TitleType, Scale, Series, Language,  
Volume, Issue, Others, Cartography, Edition, Publisher, Agency}, sepc );
+
            Volume, Issue, Others, Cartography, Edition, Publisher, Agency}, sepc );
end
+
    end
+
   
if #ID_list > 0 then
+
    if #ID_list > 0 then
ID_list = safejoin( { sepc .. " ",  table.concat( ID_list, sepc .. " " ), ID }, sepc );
+
        ID_list = safejoin( { sepc .. " ",  table.concat( ID_list, sepc .. " " ), ID }, sepc );
else
+
    else
ID_list = ID;
+
        ID_list = ID;
end
+
    end
+
   
local idcommon = safejoin( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc );
+
    local idcommon = safejoin( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc );
local text;
+
    local text;
local pgtext = Position .. Page .. Pages .. At;
+
    local pgtext = Position .. Page .. Pages .. At;
+
   
if is_set(Authors) then
+
    if is_set(Authors) then
if is_set(Coauthors) then
+
        if is_set(Coauthors) then
Authors = Authors .. A['AuthorSeparator'] .. " " .. Coauthors
+
            Authors = Authors .. A['AuthorSeparator'] .. " " .. Coauthors
end
+
        end
if is_set(Date) then
+
        if is_set(Date) then
Date = " ("..Date..")" .. OrigYear .. sepc .. " "
+
            Date = " ("..Date..")" .. OrigYear .. sepc .. " "
elseif string.sub(Authors,-1,-1) == sepc then
+
        elseif string.sub(Authors,-1,-1) == sepc then
Authors = Authors .. " "
+
            Authors = Authors .. " "
else
+
        else
Authors = Authors .. sepc .. " "
+
            Authors = Authors .. sepc .. " "
end
+
        end
if is_set(Editors) then
+
        if is_set(Editors) then
local in_text = " ";
+
            local in_text = " ";
local post_text = "";
+
            local post_text = "";
if is_set(Chapter) then
+
            if is_set(Chapter) then
in_text = in_text .. cfg.messages['in'] .. " "
+
                in_text = in_text .. cfg.messages['in'] .. " "
else
+
            else
if EditorCount <= 1 then
+
                if EditorCount <= 1 then
post_text = ", " .. cfg.messages['editor'];
+
                    post_text = ", " .. cfg.messages['editor'];
else
+
                else
post_text = ", " .. cfg.messages['editors'];
+
                    post_text = ", " .. cfg.messages['editors'];
end
+
                end
end  
+
            end  
if (sepc ~= '.') then in_text = in_text:lower() end
+
            if (sepc ~= '.') then in_text = in_text:lower() end
Editors = in_text .. Editors .. post_text;
+
            Editors = in_text .. Editors .. post_text;
if (string.sub(Editors,-1,-1) == sepc)
+
            if (string.sub(Editors,-1,-1) == sepc)
then Editors = Editors .. " "
+
                then Editors = Editors .. " "
else Editors = Editors .. sepc .. " "
+
                else Editors = Editors .. sepc .. " "
end
+
            end
end
+
        end
text = safejoin( {Authors, Date, Chapter, Place, Editors, tcommon }, sepc );
+
        text = safejoin( {Authors, Date, Chapter, Place, Editors, tcommon }, sepc );
text = safejoin( {text, pgtext, idcommon}, sepc );
+
        text = safejoin( {text, pgtext, idcommon}, sepc );
elseif is_set(Editors) then
+
    elseif is_set(Editors) then
if is_set(Date) then
+
        if is_set(Date) then
if EditorCount <= 1 then
+
            if EditorCount <= 1 then
Editors = Editors .. ", " .. cfg.messages['editor'];
+
                Editors = Editors .. ", " .. cfg.messages['editor'];
else
+
            else
Editors = Editors .. ", " .. cfg.messages['editors'];
+
                Editors = Editors .. ", " .. cfg.messages['editors'];
end
+
            end
Date = " (" .. Date ..")" .. OrigYear .. sepc .. " "
+
            Date = " (" .. Date ..")" .. OrigYear .. sepc .. " "
else
+
        else
if EditorCount <= 1 then
+
            if EditorCount <= 1 then
Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " "
+
                Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " "
else
+
            else
Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " "
+
                Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " "
end
+
            end
end
+
        end
text = safejoin( {Editors, Date, Chapter, Place, tcommon}, sepc );
+
        text = safejoin( {Editors, Date, Chapter, Place, tcommon}, sepc );
text = safejoin( {text, pgtext, idcommon}, sepc );
+
        text = safejoin( {text, pgtext, idcommon}, sepc );
else
+
    else
if is_set(Date) then
+
        if is_set(Date) then
if ( string.sub(tcommon,-1,-1) ~= sepc )
+
            if ( string.sub(tcommon,-1,-1) ~= sepc )
  then Date = sepc .." " .. Date .. OrigYear
+
              then Date = sepc .." " .. Date .. OrigYear
  else Date = " " .. Date .. OrigYear
+
              else Date = " " .. Date .. OrigYear
end
+
            end
end
+
        end
if config.CitationClass=="journal" and is_set(Periodical) then
+
        if config.CitationClass=="journal" and is_set(Periodical) then
text = safejoin( {Chapter, Place, tcommon}, sepc );
+
            text = safejoin( {Chapter, Place, tcommon}, sepc );
text = safejoin( {text, pgtext, Date, idcommon}, sepc );
+
            text = safejoin( {text, pgtext, Date, idcommon}, sepc );
else
+
        else
text = safejoin( {Chapter, Place, tcommon, Date}, sepc );
+
            text = safejoin( {Chapter, Place, tcommon, Date}, sepc );
text = safejoin( {text, pgtext, idcommon}, sepc );
+
            text = safejoin( {text, pgtext, idcommon}, sepc );
end
+
        end
end
+
    end
+
   
 
if is_set(PostScript) and PostScript ~= sepc then
 
if is_set(PostScript) and PostScript ~= sepc then
 
text = safejoin( {text, sepc}, sepc );  --Deals with italics, spaces, etc.
 
text = safejoin( {text, sepc}, sepc );  --Deals with italics, spaces, etc.
 
text = text:sub(1,-sepc:len()-1);
 
text = text:sub(1,-sepc:len()-1);
 
-- text = text:sub(1,-2); --Remove final separator (assumes that sepc is only one character)
 
-- text = text:sub(1,-2); --Remove final separator (assumes that sepc is only one character)
end
+
end  
+
   
text = safejoin( {text, PostScript}, sepc );
+
    text = safejoin( {text, PostScript}, sepc );
  
-- Now enclose the whole thing in a <span/> element
+
    -- Now enclose the whole thing in a <span/> element
local options = {};
+
    local options = {};
+
   
if is_set(config.CitationClass) and config.CitationClass ~= "citation" then
+
    if is_set(config.CitationClass) and config.CitationClass ~= "citation" then
options.class = "citation " .. config.CitationClass;
+
        options.class = "citation " .. config.CitationClass;
else
+
    else
options.class = "citation";
+
        options.class = "citation";
end
+
    end
+
   
if is_set(Ref) and Ref:lower() ~= "none" then
+
    if is_set(Ref) and Ref:lower() ~= "none" then
local id = Ref
+
        local id = Ref
if ( "harv" == Ref ) then
+
        if ( "harv" == Ref ) then
local names = {} --table of last names & year
+
            local names = {} --table of last names & year
if #a > 0 then
+
            if #a > 0 then
for i,v in ipairs(a) do  
+
                for i,v in ipairs(a) do  
names[i] = v.last  
+
                    names[i] = v.last  
if i == 4 then break end
+
                    if i == 4 then break end
end
+
                end
elseif #e > 0 then
+
            elseif #e > 0 then
for i,v in ipairs(e) do  
+
                for i,v in ipairs(e) do  
names[i] = v.last  
+
                    names[i] = v.last  
if i == 4 then break end
+
                    if i == 4 then break end              
end
+
                end
end
+
            end
 
names[ #names + 1 ] = first_set(Year, anchor_year); -- Year first for legacy citations
 
names[ #names + 1 ] = first_set(Year, anchor_year); -- Year first for legacy citations
id = anchorid(names)
+
            id = anchorid(names)
end
+
        end
options.id = id;
+
        options.id = id;
end
+
    end
+
   
if string.len(text:gsub("<span[^>/]*>.-</span>", ""):gsub("%b<>","")) <= 2 then
+
    if string.len(text:gsub("<span[^>/]*>.-</span>", ""):gsub("%b<>","")) <= 2 then
z.error_categories = {};
+
        z.error_categories = {};
text = seterror('empty_citation');
+
        text = seterror('empty_citation');
z.message_tail = {};
+
        z.message_tail = {};
end
+
    end
+
   
if is_set(options.id) then  
+
    if is_set(options.id) then  
text = '<span id="' .. mw.uri.anchorEncode(options.id) ..'" class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>";
+
        text = '<span id="' .. mw.uri.anchorEncode(options.id) ..'" class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>";
else
+
    else
text = '<span class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>";
+
        text = '<span class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>";
end
+
    end      
  
local empty_span = '<span style="display:none;">&nbsp;</span>';
+
    local empty_span = '<span style="display:none;">&nbsp;</span>';
+
   
-- Note: Using display: none on then COinS span breaks some clients.
+
    -- Note: Using display: none on then COinS span breaks some clients.
local OCinS = '<span title="' .. OCinSoutput .. '" class="Z3988">' .. empty_span .. '</span>';
+
    local OCinS = '<span title="' .. OCinSoutput .. '" class="Z3988">' .. empty_span .. '</span>';
text = text .. OCinS;
+
    text = text .. OCinS;
+
   
if #z.message_tail ~= 0 then
+
    if #z.message_tail ~= 0 then
text = text .. " ";
+
        text = text .. " ";
for i,v in ipairs( z.message_tail ) do
+
        for i,v in ipairs( z.message_tail ) do
if is_set(v[1]) then
+
            if is_set(v[1]) then
if i == #z.message_tail then
+
                if i == #z.message_tail then
text = text .. errorcomment( v[1], v[2] );
+
                    text = text .. errorcomment( v[1], v[2] );
else
+
                else
text = text .. errorcomment( v[1] .. "; ", v[2] );
+
                    text = text .. errorcomment( v[1] .. "; ", v[2] );
end
+
                end
end
+
            end
end
+
        end
end
+
    end
+
   
no_tracking_cats = no_tracking_cats:lower();
+
    no_tracking_cats = no_tracking_cats:lower();
if inArray(no_tracking_cats, {"", "no", "false", "n"}) then
+
    if inArray(no_tracking_cats, {"", "no", "false", "n"}) then
for _, v in ipairs( z.error_categories ) do
+
        for _, v in ipairs( z.error_categories ) do
text = text .. '[[Category:' .. v ..']]';
+
            text = text .. '[[Category:' .. v ..']]';
end
+
        end
for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories
+
    end
text = text .. '[[Category:' .. v ..']]';
+
   
end
+
    return text
for _, v in ipairs( z.properties_cats ) do -- append maintenance categories
 
text = text .. '[[Category:' .. v ..']]';
 
end
 
end
 
 
return text
 
 
end
 
end
  
 
-- This is used by templates such as {{cite book}} to create the actual citation text.
 
-- This is used by templates such as {{cite book}} to create the actual citation text.
 
function z.citation(frame)
 
function z.citation(frame)
local pframe = frame:getParent()
+
    local pframe = frame:getParent()
+
   
if nil ~= string.find( frame:getTitle(), 'sandbox', 1, true ) then -- did the {{#invoke:}} use sandbox version?
+
    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 ...
+
    cfg = mw.loadData( 'Module:Citation/CS1/Configuration/sandbox' ); -- load sandbox versions of Configuration and Whitelist and ...
whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist/sandbox' );
+
    whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist/sandbox' );
dates = require('Module:Citation/CS1/Date_validation/sandbox').dates -- ... sandbox version of date validation code
+
    dates = require('Module:Citation/CS1/Date_validation/sandbox').dates -- ... sandbox version of date validation code
else -- otherwise
+
    else -- otherwise
cfg = mw.loadData( 'Module:Citation/CS1/Configuration' ); -- load live versions of Configuration and Whitelist and ...
+
    cfg = mw.loadData( 'Module:Citation/CS1/Configuration' ); -- load live versions of Configuration and Whitelist and ...
whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' );
+
    whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' );
dates = require('Module:Citation/CS1/Date_validation').dates -- ... live version of date validation code
+
    dates = require('Module:Citation/CS1/Date_validation').dates -- ... live version of date validation code
 
end
 
end
 
 
local args = {};
+
    local args = {};
local suggestions = {};
+
    local suggestions = {};
local error_text, error_state;
+
    local error_text, error_state;
  
local config = {};
+
    local config = {};
for k, v in pairs( frame.args ) do
+
    for k, v in pairs( frame.args ) do
config[k] = v;
+
        config[k] = v;
args[k] = v;  
+
        args[k] = v;      
end
+
    end  
  
for k, v in pairs( pframe.args ) do
+
    for k, v in pairs( pframe.args ) do
if v ~= '' then
+
        if v ~= '' then
if not validate( k ) then
+
            if not validate( k ) then          
error_text = "";
+
                error_text = "";
if type( k ) ~= 'string' then
+
                if type( k ) ~= 'string' then
-- Exclude empty numbered parameters
+
                    -- Exclude empty numbered parameters
if v:match("%S+") ~= nil then
+
                    if v:match("%S+") ~= nil then
error_text, error_state = seterror( 'text_ignored', {v}, true );
+
                        error_text, error_state = seterror( 'text_ignored', {v}, true );
end
+
                    end
elseif validate( k:lower() ) then  
+
                elseif validate( k:lower() ) then  
error_text, error_state = seterror( 'parameter_ignored_suggest', {k, k:lower()}, true );
+
                    error_text, error_state = seterror( 'parameter_ignored_suggest', {k, k:lower()}, true );
else
+
                else
if #suggestions == 0 then
+
                    if #suggestions == 0 then
suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' );
+
                        suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' );
end
+
                    end
if suggestions[ k:lower() ] ~= nil then
+
                    if suggestions[ k:lower() ] ~= nil then
error_text, error_state = seterror( 'parameter_ignored_suggest', {k, suggestions[ k:lower() ]}, true );
+
                        error_text, error_state = seterror( 'parameter_ignored_suggest', {k, suggestions[ k:lower() ]}, true );
else
+
                    else
error_text, error_state = seterror( 'parameter_ignored', {k}, true );
+
                        error_text, error_state = seterror( 'parameter_ignored', {k}, true );
end
+
                    end
end  
+
                end                
if error_text ~= '' then
+
                if error_text ~= '' then
table.insert( z.message_tail, {error_text, error_state} );
+
                    table.insert( z.message_tail, {error_text, error_state} );
end
+
                end              
end
+
            end
args[k] = v;
+
            args[k] = v;
elseif args[k] ~= nil or (k == 'postscript') then
+
        elseif args[k] ~= nil or (k == 'postscript') then
args[k] = v;
+
            args[k] = v;
end
+
        end      
end
+
    end  
+
   
return citation0( config, args)
+
    return citation0( config, args)
 
end
 
end
  
 
return z
 
return z