Module:Citation/CS1: Difference between revisions

Jump to navigation Jump to search
m
1 revision
(Undo revision 3145 by Tito Dutta (talk))
m (1 revision)
(One intermediate revision by the same user not shown)
Line 1: Line 1:
if nil_or_not then
  x = nil_or_not.abc -- always OK
else
  -- do somethink if nil_or_not is nil.
end
local z = {
local z = {
     error_categories = {};
     error_categories = {};
Line 42: Line 37:
     end
     end
     return false;
     return false;
end
--[[
Categorize and emit an error message when the citation contains one or more deprecated parameters.  Because deprecated parameters (currently |day=, |month=,
|coauthor=, and |coauthors=) aren't related to each other and because these parameters may be concatenated into the variables used by |date= and |author#= (and aliases)
details of which parameter caused the error message are not provided.  Only one error message is emitted regarless of the number of deprecated parameters in the citation.
]]
function deprecated_parameter()
if true ~= Page_in_deprecated_cat then -- if we haven't been here before then set a
Page_in_deprecated_cat=true; -- sticky flag so that if there are more than one deprecated parameter the category is added only once
table.insert( z.message_tail, { seterror( 'deprecated_params', {error_message}, true ) } ); -- add error message
end
end
end


Line 110: Line 117:
end
end


-- Checks that parameter name is valid using the whitelist
--[[
Looks for a parameter's name in the whitelist.
 
Parameters in the whitelist can have three valuse:
true - active, supported parameters
false - deprecated, supported parameters
nil - unsupported parameters
]]
function validate( name )
function validate( name )
    name = tostring( name );
local name = tostring( name );
   
local state = whitelist.basic_arguments[ name ];
    -- Normal arguments
    if whitelist.basic_arguments[ name ] then
-- Normal arguments
        return true;
if true == state then return true; end -- valid actively supported parameter
    end
if false == state then
   
deprecated_parameter (); -- parameter is deprecated but still supported
    -- Arguments with numbers in them
return true;
    name = name:gsub( "%d+", "#" );
end
    if whitelist.numbered_arguments[ name ] then
        return true;
-- Arguments with numbers in them
    end
name = name:gsub( "%d+", "#" ); -- replace digit(s) with # (last25 becomes last#
   
state = whitelist.numbered_arguments[ name ];
    -- Not found, argument not supported.
if true == state then return true; end -- valid actively supported parameter
    return false
if false == state then
deprecated_parameter (); -- parameter is deprecated but still supported
return true;
end
return false; -- Not supported because not found or name is set to nil
end
end


Line 224: Line 243:


--[[
--[[
Formats a PMC and checks for embargoed articles. The embargo parameter takes a date for a value. If the embargo date is in the futue
Determines if a PMC identifier's online version is embargoed. Compares the date in |embargo= against today's date. If embargo date is
the PMC identifier will not be linked to the article.  If the embargo specifies a date in the past, or if it is empty or omitted, then
in the future, returns true; otherwse, returns false because the embargo has expired or |embargo= not set in this cite.
the PMC identifier is linked to the article through the link at cfg.id_handlers['PMC'].link.
 
The {{citation/core}} version of {{cite journal}} links the citation title (if url parameter is empty) when embargo date is in the past
or when embargo parameter is missing or empty. That behavior is inconsistent with the behavior of other identifiers used in CS1 and is
not supported here.
]]
]]
function pmc(id, embargo)
function is_embargoed(embargo)
local handler = cfg.id_handlers['PMC'];
   
local text;
if is_set(embargo) then
if is_set(embargo) then
local lang = mw.getContentLanguage();
local lang = mw.getContentLanguage();
Line 242: Line 252:
good1, embargo_date = pcall( lang.formatDate, lang, 'U', embargo );
good1, embargo_date = pcall( lang.formatDate, lang, 'U', embargo );
good2, todays_date = pcall( lang.formatDate, lang, 'U' );
good2, todays_date = pcall( lang.formatDate, lang, 'U' );
if good1 and good2 and tonumber( embargo_date ) >= tonumber( todays_date ) then --is embargo date is in the future?
return true; -- still embargoed
end
end
return false; -- embargo expired or |embargo= not set
end
--[[
Formats a PMC and checks for embargoed articles.  The embargo parameter takes a date for a value. If the embargo date is in the future
the PMC identifier will not be linked to the article.  If the embargo specifies a date in the past, or if it is empty or omitted, then
the PMC identifier is linked to the article through the link at cfg.id_handlers['PMC'].prefix.
]]
function pmc(id, embargo)
local handler = cfg.id_handlers['PMC'];
   
local text;


if good1 and good2 and tonumber( embargo_date ) < tonumber( todays_date ) then --if embargo date is in the past then
if is_embargoed(embargo) then
text = externallinkid({link = handler.link, label = handler.label, --ok to link to article
text="[[" .. handler.link .. "|" .. handler.label .. "]]:" .. handler.separator .. id; --still embargoed so no external link
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
else
text="[[" .. handler.link .. "|" .. handler.label .. "]]:" .. handler.separator .. id; --still embargoed so no external link
end
else
else
text = externallinkid({link = handler.link, label = handler.label, --no embargo date, ok to link to article
text = externallinkid({link = handler.link, label = handler.label, --no embargo date, ok to link to article
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
end
end
return text
return text;
end
end


Line 262: Line 285:
      
      
     local text;
     local text;
    if is_set(inactive) then
if is_set(inactive) then
        text = "[[" .. handler.link .. "|" .. handler.label .. "]]:" .. id;
local inactive_year = inactive:match("%d%d%d%d") or ''; -- try to get the year portion from the inactive date
        table.insert( z.error_categories, "Pages with DOIs inactive since " .. selectyear(inactive) );      
text = "[[" .. handler.link .. "|" .. handler.label .. "]]:" .. id;
        inactive = " (" .. cfg.messages['inactive'] .. " " .. inactive .. ")"  
if is_set(inactive_year) then
    else  
table.insert( z.error_categories, "Pages with DOIs inactive since " .. inactive_year );
        text = externallinkid({link = handler.link, label = handler.label,
else
            prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
table.insert( z.error_categories, "Pages with inactive DOIs" ); -- when inactive doesn't contain a recognizable year
        inactive = ""  
end
    end
inactive = " (" .. cfg.messages['inactive'] .. " " .. inactive .. ")"  
    if ( string.sub(id,1,3) ~= "10." ) then       
else  
        cat = seterror( 'bad_doi' );
text = externallinkid({link = handler.link, label = handler.label,
    end
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
    return text .. inactive .. cat  
inactive = ""  
end
if ( string.sub(id,1,3) ~= "10." ) then       
cat = seterror( 'bad_doi' );
end
return text .. inactive .. cat  
end
end


Line 312: Line 340:
]]
]]
function issn(id)
function issn(id)
local clean_issn;
local issn_copy = id; -- save a copy of unadulterated issn; use this version for display if issn does not validate
local issn_copy = id; -- save a copy of unadulterated issn; use this version for display if issn does not validate
local handler = cfg.id_handlers['ISSN'];
local handler = cfg.id_handlers['ISSN'];
local temp = 0;
local text;
local text;
local valid_issn = true;
local valid_issn = true;


id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and ndashes from the issn
id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and ndashes from the issn
clean_issn=string.sub( id, 1, 4 ) .. "-" .. string.sub( id, 5 ); -- make a copy with a hyphen after 4 digits; use this version for display if issn validates


if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the issn: 8 didgits long, containing only 0-9 or X in the last position
if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the issn: 8 didgits long, containing only 0-9 or X in the last position
valid_issn=false; -- wrong length or improper character
valid_issn=false; -- wrong length or improper character
else
else
id = { id:byte(1, 8) }; -- table of individual bytes
valid_issn=is_valid_isxn(id, 8); -- validate issn
for i, v in ipairs( id ) do -- loop through all of the byte an calculate the checksum
if v == string.byte( "X" ) then -- if checkdigit is X
temp = temp + 10*( 9 - i ); -- it represents 10 decimal
else
temp = temp + tonumber( string.char(v) )*(9-i);
end
end
valid_issn = temp % 11 == 0; -- checksum must be zero for valid issn
end
end


if true == valid_issn then
if true == valid_issn then
id = clean_issn; -- if valid, use the cleaned-up version for the display
id = string.sub( id, 1, 4 ) .. "-" .. string.sub( id, 5 ); -- if valid, display correctly formatted version
else
else
id = issn_copy; -- if not valid, use the show the invalid issn with error message
id = issn_copy; -- if not valid, use the show the invalid issn with error message
Line 351: Line 368:
return text
return text
end
end


--[[
--[[
Determines whether an URL string is valid
This function sets default title types (equivalent to the citation including |type=<default value>) for those citations that have defaults.
Also handles the special case where it is desireable to omit the title type from the rendered citation (|type=none).
]]
function set_titletype(cite_class, title_type)
if is_set(title_type) then
if "none" == title_type then
title_type = ""; -- if |type=none then type parameter not displayed
end
return title_type; -- if |type= has been set to any other value use that value
end
 
if "pressrelease" == cite_class then -- if this citation is cite press release
return "Press release"; -- display press release annotation
 
elseif "speech" == cite_class then -- if this citation is cite speech
return "Speech"; -- display speech annotation
elseif "techreport" == cite_class then -- if this citation is cite techreport
return "Technical report"; -- display techreport annotation
elseif "thesis" == cite_class then -- if this citation is cite thesis (degree option handled after this function returns)
return "Thesis"; -- display simple thesis annotation (without |degree= modification)
end
end


At present the only check is whether the string appears to
-- returns a number according to the month in a date: 1 for January, etcIf not a valid month, returns 0
be prefixed with a URI schemeIt is not determined whether
function get_month_number (month)
the URI scheme is valid or whether the URL is otherwise well
local long_months = {['january']=1, ['february']=2, ['march']=3, ['april']=4, ['may']=5, ['june']=6, ['july']=7, ['august']=8, ['september']=9, ['october']=10, ['november']=11, ['december']=12};
formed.
local short_months = {['jan']=1, ['feb']=2, ['mar']=3, ['apr']=4, ['may']=5, ['jun']=6, ['jul']=7, ['aug']=8, ['sep']=9, ['oct']=10, ['nov']=11, ['dec']=12};
]]
local temp;
function checkurl( url_str )
temp=long_months[month:lower()];
    -- Protocol-relative or URL scheme
if temp then return temp; end -- if month is the long-form name
    return url_str:sub(1,2) == "//" or url_str:match( "^[^/]*:" ) ~= nil;
temp=short_months[month:lower()];
if temp then return temp; end -- if month is the short-form name
return 0; -- misspelled or not a month name
end
end


-- Removes irrelevant text and dashes from ISBN number
-- returns true if date has one of the five seasons.  Else false.
-- Similar to that used for Special:BookSources
function is_valid_season (season)
function cleanisbn( isbn_str )
if inArray( season, {'winter', 'spring', 'summer', 'fall', 'autumn'} ) then
    return isbn_str:gsub( "[^-0-9X]", "" );
return true;
end
return false;
end
end


-- Determines whether an ISBN string is valid
--[[
function checkisbn( isbn_str )
Returns true if day is less than or equal to the number of days in month; else returns false.
    isbn_str = cleanisbn( isbn_str ):gsub( "-", "" );
 
    local len = isbn_str:len();
Assumes Julian calendar prior to year 1582 and Gregorian calendar thereafter. Accounts for Julian calendar leap years before 1582 and Gregorian leap years after 1582.
Where the two calendars overlap (1582 to approximately 1923) dates are assumed to be Gregorian.
    if len ~= 10 and len ~= 13 then
]]
        return false;
function is_valid_date (year, month, day)
    end
local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
local month_length;
    local temp = 0;
if (2==month) then -- if February
    if len == 10 then
month_length = 28; -- then 28 days unless
        if isbn_str:match( "^%d*X?$" ) == nil then return false; end
if 1582 > tonumber(year) then -- Julian calendar
        isbn_str = { isbn_str:byte(1, len) };
if 0==(year%4) then
        for i, v in ipairs( isbn_str ) do
month_length = 29;
            if v == string.byte( "X" ) then
end
                temp = temp + 10*( 11 - i );
else -- Gregorian calendar
            else
if (0==(year%4) and (0~=(year%100) or 0==(year%400))) then -- is a leap year?
                temp = temp + tonumber( string.char(v) )*(11-i);
month_length = 29; -- if leap year then 29 days in February
            end
end
        end
end
        return temp % 11 == 0;
else
    else
month_length=days_in_month[month];
        if isbn_str:match( "^%d*$" ) == nil then return false; end
end
        isbn_str = { isbn_str:byte(1, len) };
 
        for i, v in ipairs( isbn_str ) do
if tonumber (day) > month_length then
            temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) );
return false;
        end
end
        return temp % 10 == 0;
return true;
    end
end
end


-- Gets the display text for a wikilink like [[A|B]] or [[B]] gives B
--Check a pair of months or seasons to see if both are valid members of a month or season pair.
function removewikilink( str )
--TODO: Check order to make sure that the left month/season properly precedes the right month/season
    return (str:gsub( "%[%[([^%[%]]*)%]%]", function(l)
 
        return l:gsub( "^[^|]*|(.*)$", "%1" ):gsub("^%s*(.-)%s*$", "%1");
function is_valid_month_season_range(range_start, range_end)
    end));
if 0 == get_month_number (range_start:lower()) then -- is this a month range?
if true == is_valid_season (range_start:lower()) then -- not a month range, is this a season range?
return is_valid_season (range_end:lower()); -- range_start is season; return true if range_end also a season; else false
end
return false; -- range_start is not a month or a season
end
if 0 == get_month_number (range_end:lower()) then -- range_start is a month; is range_end also a  month?
return false; -- not a month range
end
return true;
end
end


-- Escape sequences for content that will be used for URL descriptions
--[[
function safeforurl( str )
Check date format to see that it is one of the formats approved by MOS:DATE: MMMM D, YYYY; D MMMM YYYY; MMMM YYYY; YYYY-MM-DD; YYYY.
    if str:match( "%[%[.-%]%]" ) ~= nil then
Additionally, check the date to see that it is a real date: no 31 in 30-day months; no 29 February when not a leap year.  Months, both long-form and three
        table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } );
character abbreviations, and seasons must be spelled correctly.
    end
 
   
If the date fails the fomat tests, this function returns false but does not return values for anchor_year and COinS_date.  When this happens, the date parameter is
    return str:gsub( '[%[%]\n]', {   
used in the COinS metadata and the CITEREF identifier gets its year from the year parameter if present.
        ['['] = '&#91;',
 
        [']'] = '&#93;',
Inputs:
        ['\n'] = ' ' } );
date_string - date string from date-holding parameters (date, year, accessdate, embargo, archivedate, etc)
end
 
Returns:
false if date string is not a real date; else
true, anchor_year, COinS_date
anchor_year can be used in CITEREF anchors
COinS_date is date_string without anchor_year disambiguator if any
]]
function check_date (date_string)
local year;
local month;
local day;
local anchor_year;
local coins_date;
 
if date_string:match("^%d%d%d%d%-%d%d%-%d%d$") then -- Year-initial numerical year month day format
coins_date = date_string:match("%d%d%d%d%-%d%d%-%d%d");
year, month, day=string.match(date_string, "(%d%d%d%d)%-(%d%d)%-(%d%d)");
anchor_year = year;
month=tonumber(month);
if 12 < month or 1 > month then return false; end


-- Converts a hyphen to a dash
elseif date_string:match("^%a+%s*%d%d*%s*,%s*%d%d%d%d%a?$") then -- month-initial: month day, year
function hyphentodash( str )
coins_date = date_string:match("%a+%s*%d%d*%s*,%s*%d%d%d%d");
    if not is_set(str) or str:match( "[%[%]{}<>]" ) ~= nil then
month, day, anchor_year, year=string.match(date_string, "(%a+)%s*(%d%d*)%s*,%s*((%d%d%d%d)%a?)");
        return str;
month = get_month_number (month:lower());
    end   
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
    return str:gsub( '-', '–' );
end
elseif date_string:match("^%d%d*%s*%a+%s*%d%d%d%d%a?$") then -- date-initial: day month year
coins_date = date_string:match("%d%d*%s*%a+%s*%d%d%d%d");
day, month, anchor_year, year=string.match(date_string, "(%d%d*)%s*(%a+)%s*((%d%d%d%d)%a?)");
month = get_month_number (month:lower());
if 0 == month then return false; end -- return false if month text isn't one of the twelve months


-- Protects a string that will be wrapped in wiki italic markup '' ... ''
elseif mw.ustring.match (date_string, "^%a+%s*[%s%-/–]%s*%a+%s*%d%d%d%d%a?$") then -- month/season range year
function safeforitalics( str )
local month2
    --[[ Note: We can not use <i> for italics, as the expected behavior for
coins_date = mw.ustring.match (date_string, "%a+%s*[%s%-/–]%s*%a+%s*%d%d%d%d");
    italics specified by ''...'' in the title is that they will be inverted
coins_date= mw.ustring.gsub( coins_date, "–", "-" ); -- replace ndash with hyphen
    (i.e. unitalicized) in the resulting references. In addition, <i> and ''
month, month2, anchor_year, year=mw.ustring.match (date_string, "(%a+)%s*[%s%-/–]%s*(%a+)%s*((%d%d%d%d)%a?)");
    tend to interact poorly under Mediawiki's HTML tidy. ]]
day=0; -- mark day as not used
   
if false == is_valid_month_season_range(month, month2) then
    if not is_set(str) then
return false;
        return str;
end
    else
        if str:sub(1,1) == "'" then str = "<span />" .. str; end
elseif date_string:match("^%a+%s*%d%d%d%d%a?$") then -- month/season year
        if str:sub(-1,-1) == "'" then str = str .. "<span />"; end
coins_date = date_string:match("%a+%s*%d%d%d%d");
       
month, anchor_year, year=string.match(date_string, "(%a+)%s*((%d%d%d%d)%a?)");
        -- Remove newlines as they break italics.
day=0; -- mark day as not used
        return str:gsub( '\n', ' ' );
local season=month; -- copy
    end
month = get_month_number (month:lower());
end
if month == 0 then -- if month text isn't one of the twelve months, might be a season
if false == is_valid_season (season:lower()) then
return false; -- return false not a month or one of the five seasons
end
end
 
elseif date_string:match("^%d%d%d%d?%a?$") then -- year; here accept either YYY or YYYY
coins_date = date_string:match("^%d%d%d%d?");
anchor_year, year=string.match(date_string, "((%d%d%d%d?)%a?)");
month, day = 0, 0; -- mark day and month as not used
else
return false; -- date format not one of the MOS:DATE approved formats
end
 
if 0~=month and 0~=day then -- check year month day dates for validity
if false==is_valid_date(year,month,day) then
return false; -- date string is not a real date return false; unset anchor_year and coins_date
end
end
return true, anchor_year, coins_date; -- format is good and date string represents a real date
end


--[[
--[[
Joins a sequence of strings together while checking for duplicate separation
Cycle the date-holding parameters in passed table date_parameters_list through check_date() to check compliance with MOS:DATE. For all valid dates, check_date() returns
characters.
true and values for anchor_year (used in CITEREF identifiers) and COinS_date (used in the COinS metadata).  The |date= parameter test is unique.  This function only
accepts anchor_year and COinS_date results from the |date= parameter test and |date= is the only date-holding parameter that is allowed to contain the no-date keywords
"n.d." or "nd" (without quotes).
 
Unlike most error messages created in this module, only one error message is created by this function. Because all of the date holding parameters are processed serially,
a single error message is created as the dates are tested.
]]
]]
function safejoin( tbl, duplicate_char )
    --[[
    Note: we use string functions here, rather than ustring functions.
   
    This has considerably faster performance and should work correctly as
    long as the duplicate_char is strict ASCII.  The strings
    in tbl may be ASCII or UTF8.
    ]]
   
    local str = '';
    local comp = '';
    local end_chr = '';
    local trim;
    for _, value in ipairs( tbl ) do
        if value == nil then value = ''; end
       
        if str == '' then
            str = value;
        elseif value ~= '' then
            if value:sub(1,1) == '<' then
                -- Special case of values enclosed in spans and other markup.
                comp = value:gsub( "%b<>", "" );
            else
                comp = value;
            end
           
            if comp:sub(1,1) == duplicate_char then
                trim = false;
                end_chr = str:sub(-1,-1);
                -- str = str .. "<HERE(enchr=" .. end_chr.. ")"
                if end_chr == duplicate_char then
                    str = str:sub(1,-2);
                elseif end_chr == "'" then
                    if str:sub(-3,-1) == duplicate_char .. "''" then
                        str = str:sub(1, -4) .. "''";
                    elseif str:sub(-5,-1) == duplicate_char .. "]]''" then
                        trim = true;
                    elseif str:sub(-4,-1) == duplicate_char .. "]''" then
                        trim = true;
                    end
                elseif end_chr == "]" then
                    if str:sub(-3,-1) == duplicate_char .. "]]" then
                        trim = true;
                    elseif str:sub(-2,-1) == duplicate_char .. "]" then
                        trim = true;
                    end
                elseif end_chr == " " then
                    if str:sub(-2,-1) == duplicate_char .. " " then
                        str = str:sub(1,-3);
                    end
                end


                if trim then
function dates(date_parameters_list)
                    if value ~= comp then  
local anchor_year; -- will return as nil if the date being tested is not |date=
                        local dup2 = duplicate_char;
local COinS_date; -- will return as nil if the date being tested is not |date=
                        if dup2:match( "%A" ) then dup2 = "%" .. dup2; end
local error_message ="";
                       
local good_date=false;
                        value = value:gsub( "(%b<>)" .. dup2, "%1", 1 )
                    else
for k, v in pairs(date_parameters_list) do -- for each date-holding parameter in the list
                        value = value:sub( 2, -1 );
if is_set(v) then -- if the parameter has a value
                    end
if v:match("^c%.%s%d%d%d%d?%a?$") then -- special case for c. year or with or without CITEREF disambiguator - only |date= and |year=
                end
if 'date'==k then
            end
good_date, anchor_year, COinS_date = true, v:match("((c%.%s%d%d%d%d?)%a?)"); -- anchor year and COinS_date only from |date= parameter
            str = str .. value;
elseif 'year'==k then
         end
good_date =  true;
end
elseif 'year'==k then -- if the parameter is |year= (but not c. year)
if v:match("^%d%d%d%d?%a?$") then -- year with or without CITEREF disambiguator
good_date = true;
end
elseif 'date'==k then -- if the parameter is |date=
if v:match("n%.d%.%a?") then -- if |date=n.d. with or without a CITEREF disambiguator
good_date, anchor_year, COinS_date = true, v:match("((n%.d%.)%a?)"); --"n.d."; no error when date parameter is set to no date
elseif v:match("nd%a?$") then -- if |date=nd with or without a CITEREF disambiguator
good_date, anchor_year, COinS_date = true, v:match("((nd)%a?)"); --"nd"; no error when date parameter is set to no date
else
good_date, anchor_year, COinS_date = check_date (v); -- go test the date
end
else -- any other date-holding parameter
good_date = check_date (v); -- go test the date
end
if false==good_date then -- assemble one error message so we don't add the tracking category multiple times
if is_set(error_message) then -- once we've added the first portion of the error message ...
error_message=error_message .. ", "; -- ... add a comma space separator
end
error_message=error_message .. "&#124;" .. k .. "="; -- add the failed parameter
end
end
end
if is_set(error_message) then
table.insert( z.message_tail, { seterror( 'bad_date', {error_message}, true ) } ); -- add this error message
end
 
return anchor_year, COinS_date; -- and done
end
 
--[[
Determines whether a URL string is valid
 
At present the only check is whether the string appears to
be prefixed with a URI scheme.  It is not determined whether
the URI scheme is valid or whether the URL is otherwise well
formed.
]]
function checkurl( url_str )
    -- Protocol-relative or URL scheme
    return url_str:sub(1,2) == "//" or url_str:match( "^[^/]*:" ) ~= nil;
end
 
-- Removes irrelevant text and dashes from ISBN number
-- Similar to that used for Special:BookSources
function cleanisbn( isbn_str )
    return isbn_str:gsub( "[^-0-9X]", "" );
end
 
--[[
ISBN-10 and ISSN validator code calculates checksum across all isbn/issn digits including the check digit. ISBN-13 is checked in checkisbn().
If the number is valid the result will be 0. Before calling this function, issbn/issn must be checked for length and stripped of dashes,
spaces and other non-isxn characters.
]]
function is_valid_isxn (isxn_str, len)
local temp = 0;
isxn_str = { isxn_str:byte(1, len) }; -- make a table of bytes
len = len+1; -- adjust to be a loop counter
for i, v in ipairs( isxn_str ) do -- loop through all of the bytes and calculate the checksum
if v == string.byte( "X" ) then -- if checkdigit is X
temp = temp + 10*( len - i ); -- it represents 10 decimal
else
temp = temp + tonumber( string.char(v) )*(len-i);
end
end
return temp % 11 == 0; -- returns true if calculation result is zero
end
 
-- Determines whether an ISBN string is valid
function checkisbn( isbn_str )
if nil ~= isbn_str:match("[^%s-0-9X]") then return false; end -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X
isbn_str = isbn_str:gsub( "-", "" ):gsub( " ", "" ); -- remove hyphens and spaces
local len = isbn_str:len();
if len ~= 10 and len ~= 13 then
return false;
end
 
if len == 10 then
if isbn_str:match( "^%d*X?$" ) == nil then return false; end
return is_valid_isxn(isbn_str, 10);
else
local temp = 0;
if isbn_str:match( "^97[89]%d*$" ) == nil then return false; end -- isbn13 begins with 978 or 979
isbn_str = { isbn_str:byte(1, len) };
for i, v in ipairs( isbn_str ) do
temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) );
end
return temp % 10 == 0;
end
end
 
-- Gets the display text for a wikilink like [[A|B]] or [[B]] gives B
function removewikilink( str )
    return (str:gsub( "%[%[([^%[%]]*)%]%]", function(l)
        return l:gsub( "^[^|]*|(.*)$", "%1" ):gsub("^%s*(.-)%s*$", "%1");
    end));
end
 
-- Escape sequences for content that will be used for URL descriptions
function safeforurl( str )
    if str:match( "%[%[.-%]%]" ) ~= nil then
         table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } );
     end
     end
     return str;
   
end
     return str:gsub( '[%[%]\n]', {   
        ['['] = '&#91;',
        [']'] = '&#93;',
        ['\n'] = ' ' } );
end


--[[
-- Converts a hyphen to a dash
Return the year portion of a date string, if possible. 
function hyphentodash( str )
Returns empty string if the argument can not be interpreted
     if not is_set(str) or str:match( "[%[%]{}<>]" ) ~= nil then
as a year.
]]
function selectyear( str )
     -- Is the input a simple number?
    local num = tonumber( str );
    if num ~= nil and num > 0 and num < 2100 and num == math.floor(num) then
         return str;
         return str;
     else
     end   
        -- Use formatDate to interpret more complicated formats
    return str:gsub( '-', '' );
        local lang = mw.getContentLanguage();
end
        local good, result;
        good, result = pcall( lang.formatDate, lang, 'Y', str );
        if good then
            return result;
        else
            -- extract year if the date uses seasons
            str=string.lower (str);
            local seasons={"winter", "spring", "summer", "fall", "autumn"};
            local date_string_split=mw.text.split (str, "[%s%-/–]");      -- split date string into parts; white space, hyphen, forward slash, and ndash are allowed separators
            local has_season=false;
           
            for n,season_value in ipairs(seasons) do                -- for each season ...
                for n,split_value in ipairs(date_string_split) do  -- ... loop through date string values
                    if split_value == season_value then            -- does the split value match the season value?
                        if has_season==false then                  -- found one. if this one is the first we've found ...
                            has_season=true;                        -- ... remember that we found a season
                        end
                    elseif has_season==true then                    -- if split_value isn't a season, and we've previously found a season ...
                        num = tonumber( split_value );              -- ... convert current split value to a number if we can
                        if num ~= nil and num > 0 and num < 2100 and num == math.floor(num) then    -- if it's a suitable number
                            return tostring( num );                                                 -- return it as a string
                        end -- if num
                    end -- if string.find
                end -- for split value loop
            end -- season value loop
        end -- if good
    end -- if num
end -- selectyear


-- Attempts to convert names to initials.
-- Protects a string that will be wrapped in wiki italic markup '' ... ''
function reducetoinitials(first)
function safeforitalics( str )
     local initials = {}
     --[[ Note: We can not use <i> for italics, as the expected behavior for
     for word in string.gmatch(first, "%S+") do
    italics specified by ''...'' in the title is that they will be inverted
         table.insert(initials, string.sub(word,1,1)) -- Vancouver format does not include full stops.
     (i.e. unitalicized) in the resulting references.  In addition, <i> and ''
    tend to interact poorly under Mediawiki's HTML tidy. ]]
   
    if not is_set(str) then
         return str;
    else
        if str:sub(1,1) == "'" then str = "<span />" .. str; end
        if str:sub(-1,-1) == "'" then str = str .. "<span />"; end
       
        -- Remove newlines as they break italics.
        return str:gsub( '\n', ' ' );
     end
     end
    return table.concat(initials) -- Vancouver format does not include spaces.
end
end


-- Formats a list of people (e.g. authors / editors)
--[[
function listpeople(control, people)
Joins a sequence of strings together while checking for duplicate separation
     local sep = control.sep;
characters.
     local namesep = control.namesep
]]
    local format = control.format
function safejoin( tbl, duplicate_char )
    local maximum = control.maximum
     --[[
    local lastauthoramp = control.lastauthoramp;
     Note: we use string functions here, rather than ustring functions.
    local text = {}
    local etal = false;
      
      
     if sep:sub(-1,-1) ~= " " then sep = sep .. " " end
     This has considerably faster performance and should work correctly as
     if maximum ~= nil and maximum < 1 then return "", 0; end
    long as the duplicate_char is strict ASCII. The strings
    in tbl may be ASCII or UTF8.
     ]]
      
      
     for i,person in ipairs(people) do
    local str = '';
         if is_set(person.last) then
    local comp = '';
            local mask = person.mask
    local end_chr = '';
            local one
    local trim;
            local sep_one = sep;
     for _, value in ipairs( tbl ) do
            if maximum ~= nil and i > maximum then
         if value == nil then value = ''; end
                etal = true;
       
                break;
        if str == '' then
            elseif (mask ~= nil) then
            str = value;
                local n = tonumber(mask)
        elseif value ~= '' then
                 if (n ~= nil) then
            if value:sub(1,1) == '<' then
                    one = string.rep("&mdash;",n)
                 -- Special case of values enclosed in spans and other markup.
                else
                comp = value:gsub( "%b<>", "" );
                    one = mask;
                    sep_one = " ";
                end
             else
             else
                 one = person.last
                 comp = value;
                 local first = person.first
            end
                 if is_set(first) then  
           
                     if ( "vanc" == format ) then first = reducetoinitials(first) end
            if comp:sub(1,1) == duplicate_char then
                     one = one .. namesep .. first
                trim = false;
                end_chr = str:sub(-1,-1);
                -- str = str .. "<HERE(enchr=" .. end_chr.. ")"
                 if end_chr == duplicate_char then
                    str = str:sub(1,-2);
                elseif end_chr == "'" then
                    if str:sub(-3,-1) == duplicate_char .. "''" then
                        str = str:sub(1, -4) .. "''";
                    elseif str:sub(-5,-1) == duplicate_char .. "]]''" then
                        trim = true;
                    elseif str:sub(-4,-1) == duplicate_char .. "]''" then
                        trim = true;
                    end
                 elseif end_chr == "]" then
                    if str:sub(-3,-1) == duplicate_char .. "]]" then
                        trim = true;
                     elseif str:sub(-2,-1) == duplicate_char .. "]" then
                        trim = true;
                    end
                elseif end_chr == " " then
                    if str:sub(-2,-1) == duplicate_char .. " " then
                        str = str:sub(1,-3);
                    end
                end
 
                if trim then
                     if value ~= comp then
                        local dup2 = duplicate_char;
                        if dup2:match( "%A" ) then dup2 = "%" .. dup2; end
                       
                        value = value:gsub( "(%b<>)" .. dup2, "%1", 1 )
                    else
                        value = value:sub( 2, -1 );
                    end
                 end
                 end
                if is_set(person.link) then one = "[[" .. person.link .. "|" .. one .. "]]" end
             end
             end
             table.insert( text, one )
             str = str .. value;
            table.insert( text, sep_one )
         end
         end
     end
     end
    return str;
end 


     local count = #text / 2;
-- Attempts to convert names to initials.
     if count > 0 then
function reducetoinitials(first)
         if count > 1 and is_set(lastauthoramp) and not etal then
     local initials = {}
            text[#text-2] = " & ";
     for word in string.gmatch(first, "%S+") do
        end
         table.insert(initials, string.sub(word,1,1)) -- Vancouver format does not include full stops.
        text[#text] = nil;
     end
     end
      
     return table.concat(initials) -- Vancouver format does not include spaces.
    local result = table.concat(text) -- construct list
    if etal then
        local etal_text = cfg.messages['et al'];
        result = result .. " " .. etal_text;
    end
   
    -- if necessary wrap result in <span> tag to format in Small Caps
    if ( "scap" == format ) then result =
        '<span class="smallcaps" style="font-variant:small-caps">' .. result .. '</span>';
    end
    return result, count
end
end


-- Generates a CITEREF anchor ID.
-- Formats a list of people (e.g. authors / editors)
function anchorid( options )
function listpeople(control, people)
     return "CITEREF" .. table.concat( options );
     local sep = control.sep;
end
    local namesep = control.namesep
 
    local format = control.format
-- Gets name list from the input arguments
    local maximum = control.maximum
function extractnames(args, list_name)
    local lastauthoramp = control.lastauthoramp;
     local names = {};
     local text = {}
     local i = 1;
     local etal = false;
     local last;
   
    if sep:sub(-1,-1) ~= " " then sep = sep .. " " end
     if maximum ~= nil and maximum < 1 then return "", 0; end
      
      
     while true do
     for i,person in ipairs(people) do
        last = selectone( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i );
         if is_set(person.last) then
         if not is_set(last) then
             local mask = person.mask
             -- just in case someone passed in an empty parameter
            local one
             break;
            local sep_one = sep;
        end
             if maximum ~= nil and i > maximum then
        names[i] = {
                etal = true;
             last = last,
                break;
            first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ),
            elseif (mask ~= nil) then
            link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ),
                local n = tonumber(mask)
             mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i )
                if (n ~= nil) then
         };
                    one = string.rep("&mdash;",n)
        i = i + 1;
                else
                    one = mask;
                    sep_one = " ";
                end
             else
                one = person.last
                local first = person.first
                if is_set(first) then
                    if ( "vanc" == format ) then first = reducetoinitials(first) end
                    one = one .. namesep .. first
                end
                if is_set(person.link) then one = "[[" .. person.link .. "|" .. one .. "]]" end
            end
            table.insert( text, one )
             table.insert( text, sep_one )
         end
     end
     end
    return names;
end


-- Populates ID table from arguments using configuration settings
     local count = #text / 2;
function extractids( args )
     if count > 0 then
     local id_list = {};
         if count > 1 and is_set(lastauthoramp) and not etal then
     for k, v in pairs( cfg.id_handlers ) do   
            text[#text-2] = " & ";
        v = selectone( args, v.parameters, 'redundant_parameters' );
        end
         if is_set(v) then id_list[k] = v; end
        text[#text] = nil;
     end
     end
    return id_list;
end
-- Takes a table of IDs and turns it into a table of formatted ID outputs.
function buildidlist( id_list, options )
    local new_list, handler = {};
      
      
     function fallback(k) return { __index = function(t,i) return cfg.id_handlers[k][i] end } end;
     local result = table.concat(text) -- construct list
    if etal then
        local etal_text = cfg.messages['et al'];
        result = result .. " " .. etal_text;
    end
      
      
     for k, v in pairs( id_list ) do
     -- if necessary wrap result in <span> tag to format in Small Caps
        -- fallback to read-only cfg
    if ( "scap" == format ) then result =
        handler = setmetatable( { ['id'] = v }, fallback(k) );
         '<span class="smallcaps" style="font-variant:small-caps">' .. result .. '</span>';
       
    end
         if handler.mode == 'external' then
    return result, count
            table.insert( new_list, {handler.label, externallinkid( handler ) } );
end
        elseif handler.mode == 'internal' then
 
            table.insert( new_list, {handler.label, internallinkid( handler ) } );
-- Generates a CITEREF anchor ID.
        elseif handler.mode ~= 'manual' then
function anchorid( options )
            error( cfg.messages['unknown_ID_mode'] );
    return "CITEREF" .. table.concat( options );
        elseif k == 'DOI' then
end
            table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } );
 
        elseif k == 'ASIN' then
-- Gets name list from the input arguments
            table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } );  
function extractnames(args, list_name)
        elseif k == 'OL' then
    local names = {};
            table.insert( new_list, {handler.label, openlibrary( v ) } );
    local i = 1;
        elseif k == 'PMC' then
    local last;
            table.insert( new_list, {handler.label, pmc( v, options.Embargo ) } );
   
        elseif k == 'ISSN' then
    while true do
        table.insert( new_list, {handler.label, issn( v ) } );
        last = selectone( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i );
        elseif k == 'ISBN' then
        if not is_set(last) then
            local ISBN = internallinkid( handler );
            -- just in case someone passed in an empty parameter
            if not checkisbn( v ) and not is_set(options.IgnoreISBN) then
             break;
                ISBN = ISBN .. seterror( 'bad_isbn', {}, false, " ", "" );
            end
            table.insert( new_list, {handler.label, ISBN } );               
        else
             error( cfg.messages['unknown_manual_ID'] );
         end
         end
        names[i] = {
            last = last,
            first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ),
            link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ),
            mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i )
        };
        i = i + 1;
     end
     end
      
     return names;
     function comp( a, b )
end
         return a[1] < b[1];
 
-- Populates ID table from arguments using configuration settings
function extractids( args )
    local id_list = {};
     for k, v in pairs( cfg.id_handlers ) do   
        v = selectone( args, v.parameters, 'redundant_parameters' );
         if is_set(v) then id_list[k] = v; end
     end
     end
    return id_list;
end
-- Takes a table of IDs and turns it into a table of formatted ID outputs.
function buildidlist( id_list, options )
    local new_list, handler = {};
      
      
     table.sort( new_list, comp );
     function fallback(k) return { __index = function(t,i) return cfg.id_handlers[k][i] end } end;
    for k, v in ipairs( new_list ) do
        new_list[k] = v[2];
    end
      
      
     return new_list;
     for k, v in pairs( id_list ) do
end
        -- fallback to read-only cfg
 
        handler = setmetatable( { ['id'] = v }, fallback(k) );
-- Chooses one matching parameter from a list of parameters to consider
       
-- Generates an error if more than one match is present.
        if handler.mode == 'external' then
function selectone( args, possible, error_condition, index )
            table.insert( new_list, {handler.label, externallinkid( handler ) } );
    local value = nil;
        elseif handler.mode == 'internal' then
    local selected = '';
            table.insert( new_list, {handler.label, internallinkid( handler ) } );
    local error_list = {};
        elseif handler.mode ~= 'manual' then
   
            error( cfg.messages['unknown_ID_mode'] );
    if index ~= nil then index = tostring(index); end
        elseif k == 'DOI' then
   
            table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } );
    -- Handle special case of "#" replaced by empty string
        elseif k == 'ASIN' then
    if index == '1' then
            table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } );
        for _, v in ipairs( possible ) do
        elseif k == 'OL' then
             v = v:gsub( "#", "" );
             table.insert( new_list, {handler.label, openlibrary( v ) } );
             if is_set(args[v]) then
        elseif k == 'PMC' then
                if value ~= nil and selected ~= v then
             table.insert( new_list, {handler.label, pmc( v, options.Embargo ) } );
                    table.insert( error_list, v );
        elseif k == 'ISSN' then
                else
        table.insert( new_list, {handler.label, issn( v ) } );
                    value = args[v];
        elseif k == 'ISBN' then
                    selected = v;
            local ISBN = internallinkid( handler );
                 end
            if not checkisbn( v ) and not is_set(options.IgnoreISBN) then
                 ISBN = ISBN .. seterror( 'bad_isbn', {}, false, " ", "" );
             end
             end
         end      
            table.insert( new_list, {handler.label, ISBN } );               
        else
            error( cfg.messages['unknown_manual_ID'] );
         end
    end
   
    function comp( a, b ) -- used in following table.sort()
        return a[1] < b[1];
     end
     end
      
      
     for _, v in ipairs( possible ) do
    table.sort( new_list, comp );
         if index ~= nil then
     for k, v in ipairs( new_list ) do
            v = v:gsub( "#", index );
         new_list[k] = v[2];
        end
        if is_set(args[v]) then
            if value ~= nil and selected ~= v then
                table.insert( error_list, v );
            else
                value = args[v];
                selected = v;
            end
        end
     end
     end
      
      
    if #error_list > 0 then
     return new_list;
        local error_str = "";
        for _, k in ipairs( error_list ) do
            if error_str ~= "" then error_str = error_str .. cfg.messages['parameter-separator'] end
            error_str = error_str .. wrap( 'parameter', k );
        end
        if #error_list > 1 then
            error_str = error_str .. cfg.messages['parameter-final-separator'];
        else
            error_str = error_str .. cfg.messages['parameter-pair-separator'];
        end
        error_str = error_str .. wrap( 'parameter', selected );
        table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } );
    end
   
     return value, selected;
end
end
 
 
-- COinS metadata (see <http://ocoins.info/>) allows automated tools to parse
-- Chooses one matching parameter from a list of parameters to consider
-- the citation information.
-- Generates an error if more than one match is present.
function COinS(data)
function selectone( args, possible, error_condition, index )
     if 'table' ~= type(data) or nil == next(data) then
     local value = nil;
        return '';
    local selected = '';
     end
     local error_list = {};
      
      
     local ctx_ver = "Z39.88-2004";
     if index ~= nil then index = tostring(index); end
      
      
     -- treat table strictly as an array with only set values.
     -- Handle special case of "#" replaced by empty string
     local OCinSoutput = setmetatable( {}, {
     if index == '1' then
        __newindex = function(self, key, value)
        for _, v in ipairs( possible ) do
             if is_set(value) then
            v = v:gsub( "#", "" );
                 rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( removewikilink( value ) ) } );
             if is_set(args[v]) then
            end
                 if value ~= nil and selected ~= v then
                    table.insert( error_list, v );
                else
                    value = args[v];
                    selected = v;
                end
            end
        end       
    end
   
    for _, v in ipairs( possible ) do
        if index ~= nil then
            v = v:gsub( "#", index );
         end
         end
    });
        if is_set(args[v]) then
            if value ~= nil and selected ~=  v then
                table.insert( error_list, v );
            else
                value = args[v];
                selected = v;
            end
        end
    end
      
      
     if is_set(data.Chapter) then
     if #error_list > 0 then
         OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
         local error_str = "";
         OCinSoutput["rft.genre"] = "bookitem";
         for _, k in ipairs( error_list ) do
        OCinSoutput["rft.btitle"] = data.Chapter;
            if error_str ~= "" then error_str = error_str .. cfg.messages['parameter-separator'] end
        OCinSoutput["rft.atitle"] = data.Title;
            error_str = error_str .. wrap( 'parameter', k );
    elseif is_set(data.Periodical) then
         end
         OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal";
         if #error_list > 1 then
         OCinSoutput["rft.genre"] = "article";
            error_str = error_str .. cfg.messages['parameter-final-separator'];
         OCinSoutput["rft.jtitle"] = data.Periodical;
         else
        OCinSoutput["rft.atitle"] = data.Title;
            error_str = error_str .. cfg.messages['parameter-pair-separator'];
    else
        end
         OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
         error_str = error_str .. wrap( 'parameter', selected );
         OCinSoutput["rft.genre"] = "book"
         table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } );
        OCinSoutput["rft.btitle"] = data.Title;
     end
     end
      
      
     OCinSoutput["rft.place"] = data.PublicationPlace;
     return value, selected;
    OCinSoutput["rft.date"] = data.Date;
end
    OCinSoutput["rft.series"] = data.Series;
 
     OCinSoutput["rft.volume"] = data.Volume;
-- COinS metadata (see <http://ocoins.info/>) allows automated tools to parse
    OCinSoutput["rft.issue"] = data.Issue;
-- the citation information.
    OCinSoutput["rft.pages"] = data.Pages;
function COinS(data)
    OCinSoutput["rft.edition"] = data.Edition;
     if 'table' ~= type(data) or nil == next(data) then
     OCinSoutput["rft.pub"] = data.PublisherName;
        return '';
     end
      
      
     for k, v in pairs( data.ID_list ) do
     local ctx_ver = "Z39.88-2004";
        local id, value = cfg.id_handlers[k].COinS;
        if k == 'ISBN' then value = cleanisbn( v ); else value = v; end
        if string.sub( id or "", 1, 4 ) == 'info' then
            OCinSoutput["rft_id"] = table.concat{ id, "/", v };
        else
            OCinSoutput[ id ] = value;
        end
    end
      
      
     local last, first;
     -- treat table strictly as an array with only set values.
     for k, v in ipairs( data.Authors ) do
     local OCinSoutput = setmetatable( {}, {
         last, first = v.last, v.first;
         __newindex = function(self, key, value)
        if k == 1 then
             if is_set(value) then
             if is_set(last) then
                 rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( removewikilink( value ) ) } );
                 OCinSoutput["rft.aulast"] = last;
            end
            if is_set(first) then
                OCinSoutput["rft.aufirst"] = first;
             end
             end
         end
         end
        if is_set(last) and is_set(first) then
    });
            OCinSoutput["rft.au"] = table.concat{ last, ", ", first };
        elseif is_set(last) then
            OCinSoutput["rft.au"] = last;
        end
    end
      
      
     OCinSoutput.rft_id = data.URL;
     if is_set(data.Chapter) then
    OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage };
        OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
    OCinSoutput = setmetatable( OCinSoutput, nil );
        OCinSoutput["rft.genre"] = "bookitem";
      
        OCinSoutput["rft.btitle"] = data.Chapter;
    -- sort with version string always first, and combine.
        OCinSoutput["rft.atitle"] = data.Title;
    table.sort( OCinSoutput );
     elseif is_set(data.Periodical) then
    table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver ); -- such as "Z39.88-2004"
        OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal";
     return table.concat(OCinSoutput, "&");
        OCinSoutput["rft.genre"] = "article";
end
        OCinSoutput["rft.jtitle"] = data.Periodical;
 
        OCinSoutput["rft.atitle"] = data.Title;
--[[
     else
This is the main function foing the majority of the citation
        OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book";
formatting.
        OCinSoutput["rft.genre"] = "book"
]]
        OCinSoutput["rft.btitle"] = data.Title;
function citation0( config, args)
     end
    --[[  
      
     Load Input Parameters
     OCinSoutput["rft.place"] = data.PublicationPlace;
     The argment_wrapper facillitates the mapping of multiple
     OCinSoutput["rft.date"] = data.Date;
     aliases to single internal variable.
     OCinSoutput["rft.series"] = data.Series;
     ]]
     OCinSoutput["rft.volume"] = data.Volume;
     local A = argument_wrapper( args );
     OCinSoutput["rft.issue"] = data.Issue;
 
     OCinSoutput["rft.pages"] = data.Pages;
     local i
     OCinSoutput["rft.edition"] = data.Edition;
     local PPrefix = A['PPrefix']
    OCinSoutput["rft.pub"] = data.PublisherName;
     local PPPrefix = A['PPPrefix']
     if is_set( A['NoPP'] ) then PPPrefix = "" PPrefix = "" end
      
      
     -- Pick out the relevant fields from the arguments.  Different citation templates
     for k, v in pairs( data.ID_list ) do
    -- define different field names for the same underlying things.  
        local id, value = cfg.id_handlers[k].COinS;
    local Authors = A['Authors'];
        if k == 'ISBN' then value = cleanisbn( v ); else value = v; end
    local a = extractnames( args, 'AuthorList' );
        if string.sub( id or "", 1, 4 ) == 'info' then
 
            OCinSoutput["rft_id"] = table.concat{ id, "/", v };
    local Coauthors = A['Coauthors'];
        else
    local Others = A['Others'];
            OCinSoutput[ id ] = value;
    local Editors = A['Editors'];
        end
    local e = extractnames( args, 'EditorList' );
     end
 
    local Year = A['Year'];
    local PublicationDate = A['PublicationDate'];
    local OrigYear = A['OrigYear'];
    local Date = A['Date'];
    local LayDate = A['LayDate'];
    ------------------------------------------------- Get title data
    local Title = A['Title'];
    local BookTitle = A['BookTitle'];
    local Conference = A['Conference'];
    local TransTitle = A['TransTitle'];
     local TitleNote = A['TitleNote'];
    local TitleLink = A['TitleLink'];
    local Chapter = A['Chapter'];
    local ChapterLink = A['ChapterLink'];
    local TransChapter = A['TransChapter'];
    local TitleType = A['TitleType'];
    local ArchiveURL = A['ArchiveURL'];
    local URL = A['URL']
    local URLorigin = A:ORIGIN('URL');
    local ChapterURL = A['ChapterURL'];
    local ChapterURLorigin = A:ORIGIN('ChapterURL');
    local ConferenceURL = A['ConferenceURL'];
    local ConferenceURLorigin = A:ORIGIN('ConferenceURL');
    local Periodical = A['Periodical'];
      
      
     if ( config.CitationClass == "encyclopaedia" ) then
     local last, first;
         if not is_set(Chapter) then
    for k, v in ipairs( data.Authors ) do
             if not is_set(Title) then
        last, first = v.last, v.first;
                 Title = Periodical;
         if k == 1 then
                Periodical = '';
             if is_set(last) then
             else
                 OCinSoutput["rft.aulast"] = last;
                 Chapter = Title
            end
                TransChapter = TransTitle
             if is_set(first) then
                Title = '';
                 OCinSoutput["rft.aufirst"] = first;
                TransTitle = '';
             end
             end
        end
        if is_set(last) and is_set(first) then
            OCinSoutput["rft.au"] = table.concat{ last, ", ", first };
        elseif is_set(last) then
            OCinSoutput["rft.au"] = last;
         end
         end
     end
     end
    local Series = A['Series'];
    local Volume = A['Volume'];
    local Issue = A['Issue'];
    local Position = '';
    local Page, Pages, At, page_type;
      
      
     Page = A['Page'];
     OCinSoutput.rft_id = data.URL;
     Pages = hyphentodash( A['Pages'] );
     OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage };
     At = A['At'];
     OCinSoutput = setmetatable( OCinSoutput, nil );
      
      
     if is_set(Page) then
     -- sort with version string always first, and combine.
        if is_set(Pages) or is_set(At) then
    table.sort( OCinSoutput );
            Page = Page .. " " .. seterror('extra_pages');
    table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver );  -- such as "Z39.88-2004"
            Pages = '';
    return table.concat(OCinSoutput, "&");
            At = '';
end
        end
 
    elseif is_set(Pages) then
--[[
        if is_set(At) then
This is the main function doing the majority of the citation
            Pages = Pages .. " " .. seterror('extra_pages');
formatting.
            At = '';
]]
        end
function citation0( config, args)
    end   
    --[[
      
    Load Input Parameters
     local Edition = A['Edition'];
    The argment_wrapper facillitates the mapping of multiple
     local PublicationPlace = A['PublicationPlace']
    aliases to single internal variable.
     local Place = A['Place'];
    ]]
    local A = argument_wrapper( args );
 
     local i
     local PPrefix = A['PPrefix']
     local PPPrefix = A['PPPrefix']
     if is_set( A['NoPP'] ) then PPPrefix = "" PPrefix = "" end
      
      
     if not is_set(PublicationPlace) and is_set(Place) then
     -- Pick out the relevant fields from the arguments.  Different citation templates
        PublicationPlace = Place;
     -- define different field names for the same underlying things.   
    end
     local Authors = A['Authors'];
   
     local a = extractnames( args, 'AuthorList' );
    if PublicationPlace == Place then Place = ''; end
 
      
     local Coauthors = A['Coauthors'];
     local PublisherName = A['PublisherName'];
     local Others = A['Others'];
     local RegistrationRequired = A['RegistrationRequired'];
     local Editors = A['Editors'];
    local SubscriptionRequired = A['SubscriptionRequired'];
     local e = extractnames( args, 'EditorList' );
     local Via = A['Via'];
     local AccessDate = A['AccessDate'];
     local ArchiveDate = A['ArchiveDate'];
     local Agency = A['Agency'];
    local DeadURL = A['DeadURL']
    local Language = A['Language'];
    local Format = A['Format'];
    local Ref = A['Ref'];
   
    local DoiBroken = A['DoiBroken'];
    local ID = A['ID'];
    local ASINTLD = A['ASINTLD'];
    local IgnoreISBN = A['IgnoreISBN'];
    local Embargo = A['Embargo'];


     local ID_list = extractids( args );
     local Year = A['Year'];
      
    local PublicationDate = A['PublicationDate'];
     local Quote = A['Quote'];
    local OrigYear = A['OrigYear'];
     local PostScript = A['PostScript'];
    local Date = A['Date'];
     local LayURL = A['LayURL'];
    local LayDate = A['LayDate'];
     local LaySource = A['LaySource'];
    ------------------------------------------------- Get title data
     local Transcript = A['Transcript'];
    local Title = A['Title'];
     local TranscriptURL = A['TranscriptURL']  
    local BookTitle = A['BookTitle'];
     local TranscriptURLorigin = A:ORIGIN('TranscriptURL');
    local Conference = A['Conference'];
     local sepc = A['Separator'];
    local TransTitle = A['TransTitle'];
     local LastAuthorAmp = A['LastAuthorAmp'];
    local TitleNote = A['TitleNote'];
     local no_tracking_cats = A['NoTracking'];
    local TitleLink = A['TitleLink'];
     local Chapter = A['Chapter'];
     local ChapterLink = A['ChapterLink'];
     local TransChapter = A['TransChapter'];
     local TitleType = A['TitleType'];
     local Degree = A['Degree'];
     local Docket = A['Docket'];
     local ArchiveURL = A['ArchiveURL'];
     local URL = A['URL']
    local URLorigin = A:ORIGIN('URL');
     local ChapterURL = A['ChapterURL'];
     local ChapterURLorigin = A:ORIGIN('ChapterURL');
    local ConferenceURL = A['ConferenceURL'];
     local ConferenceURLorigin = A:ORIGIN('ConferenceURL');
    local Periodical = A['Periodical'];
 
--[[
Parameter remapping for cite encyclopedia:
When the citation has these parameters:
|encyclopedia and |title then map |title to |article and |encyclopedia to |title
|encyclopedia and |article then map |encyclopedia to |title
|encyclopedia then map |encyclopedia to |title


    local use_lowercase = ( sepc ~= '.' );
|trans_title maps to |trans_chapter when |title is re-mapped
    local this_page = mw.title.getCurrentTitle(); --Also used for COinS and for language
 
All other combinations of |encyclopedia, |title, and |article are not modified
]]
if ( config.CitationClass == "encyclopaedia" ) then
if is_set(Periodical) then -- Periodical is set when |encyclopedia is set
if is_set(Title) then
if not is_set(Chapter) then
Chapter = Title; -- |encyclopedia and |title are set so map |title to |article and |encyclopedia to |title
TransChapter = TransTitle;
Title = Periodical;
Periodical = ''; -- redundant so unset
TransTitle = ''; -- redundant so unset
end
else -- |title not set
Title = Periodical; -- |encyclopedia set and |article set or not set so map |encyclopedia to |title
Periodical = ''; -- redundant so unset
end
end
end
 
local Series = A['Series'];
    local Volume = A['Volume'];
    local Issue = A['Issue'];
    local Position = '';
    local Page, Pages, At, page_type;
      
      
     if not is_set(no_tracking_cats) then
     Page = A['Page'];
        for k, v in pairs( cfg.uncategorized_namespaces ) do
    Pages = hyphentodash( A['Pages'] );
            if this_page.nsText == v then
     At = A['At'];
                no_tracking_cats = "true";
                break;
            end
        end
     end
 
    -- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.
      
      
    -- Account for the oddity that is {{cite conference}}, before generation of COinS data.
     if is_set(Page) then
     if is_set(BookTitle) then
         if is_set(Pages) or is_set(At) then
         Chapter = Title;
            Page = Page .. " " .. seterror('extra_pages');
        ChapterLink = TitleLink;
            Pages = '';
        TransChapter = TransTitle;
            At = '';
        Title = BookTitle;
        end
        TitleLink = '';
     elseif is_set(Pages) then
        TransTitle = '';
        if is_set(At) then
    end
            Pages = Pages .. " " .. seterror('extra_pages');
     -- Account for the oddity that is {{cite episode}}, before generation of COinS data.
            At = '';
    if config.CitationClass == "episode" then
         end
        local AirDate = A['AirDate'];
    end   
        local SeriesLink = A['SeriesLink'];
   
         local Season = A['Season'];
    local Edition = A['Edition'];
        local SeriesNumber = A['SeriesNumber'];
    local PublicationPlace = A['PublicationPlace']
        local Network = A['Network'];
    local Place = A['Place'];
        local Station = A['Station'];
   
        local s, n = {}, {};
    if not is_set(PublicationPlace) and is_set(Place) then
        local Sep = (first_set(A["SeriesSeparator"], A["Separator"]) or "") .. " ";
         PublicationPlace = Place;
       
        if is_set(Issue) then table.insert(s, cfg.messages["episode"] .. " " .. Issue); Issue = ''; end
        if is_set(Season) then table.insert(s, cfg.messages["season"] .. " " .. Season); end
        if is_set(SeriesNumber) then table.insert(s, cfg.messages["series"] .. " " .. SeriesNumber); end
        if is_set(Network) then table.insert(n, Network); end
        if is_set(Station) then table.insert(n, Station); end
       
        Date = Date or AirDate;
        Chapter = Title;
        ChapterLink = TitleLink;
        TransChapter = TransTitle;
        Title = Series;
        TitleLink = SeriesLink;
        TransTitle = '';
       
        Series = table.concat(s, Sep);
         ID = table.concat(n, Sep);
     end
     end
      
      
     -- COinS metadata (see <http://ocoins.info/>) for
     if PublicationPlace == Place then Place = ''; end
     -- automated parsing of citation information.
   
     local OCinSoutput = COinS{
    local PublisherName = A['PublisherName'];
        ['Periodical'] = Periodical,
    local RegistrationRequired = A['RegistrationRequired'];
        ['Chapter'] = Chapter,
     local SubscriptionRequired = A['SubscriptionRequired'];
        ['Title'] = Title,
     local Via = A['Via'];
        ['PublicationPlace'] = PublicationPlace,
    local AccessDate = A['AccessDate'];
        ['Date'] = first_set(Date, Year, PublicationDate),
    local ArchiveDate = A['ArchiveDate'];
        ['Series'] = Series,
    local Agency = A['Agency'];
        ['Volume'] = Volume,
    local DeadURL = A['DeadURL']
        ['Issue'] = Issue,
    local Language = A['Language'];
        ['Pages'] = first_set(Page, Pages, At),
    local Format = A['Format'];
        ['Edition'] = Edition,
    local Ref = A['Ref'];
        ['PublisherName'] = PublisherName,
   
        ['URL'] = first_set( URL, ChapterURL ),
    local DoiBroken = A['DoiBroken'];
        ['Authors'] = a,
        ['ID_list'] = ID_list,
-- Special case for cite techreport.
        ['RawPage'] = this_page.prefixedText,
local ID = A['ID'];
    };
if (config.CitationClass == "techreport") then -- special case for cite techreport
if is_set(Issue) then -- cite techreport uses 'number', which other citations aliase to 'issue'
if not is_set(ID) then -- can we use ID for the "number"?
ID = Issue; -- yes, use it
Issue = ""; -- unset Issue so that "number" isn't duplicated in the rendered citation or COinS metadata
else -- can't use ID so emit error message
ID = ID .. " " .. seterror('redundant_parameters', '<code>&#124;id=</code> and <code>&#124;number=</code>');
end
end
end
    local ASINTLD = A['ASINTLD'];
    local IgnoreISBN = A['IgnoreISBN'];
    local Embargo = A['Embargo'];
 
    local ID_list = extractids( args );
   
    local Quote = A['Quote'];
    local PostScript = A['PostScript'];
    local LayURL = A['LayURL'];
    local LaySource = A['LaySource'];
    local Transcript = A['Transcript'];
    local TranscriptURL = A['TranscriptURL']  
    local TranscriptURLorigin = A:ORIGIN('TranscriptURL');
    local sepc = A['Separator'];
    local LastAuthorAmp = A['LastAuthorAmp'];
    local no_tracking_cats = A['NoTracking'];


     if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then
     local use_lowercase = ( sepc ~= '.' );
         Chapter = Title;
    local this_page = mw.title.getCurrentTitle();  --Also used for COinS and for language
         ChapterLink = TitleLink;
   
    if not is_set(no_tracking_cats) then
        for k, v in pairs( cfg.uncategorized_namespaces ) do
            if this_page.nsText == v then
                no_tracking_cats = "true";
                break;
            end
        end
    end
 
local anchor_year; -- used in the CITEREF identifier
local COinS_date; -- used in the COinS metadata
 
-- legacy: promote concatenation of |day=, |month=, and |year= to Date if Date not set; or, promote PublicationDate to Date if neither Date nor Year are set.
if not is_set(Date) then
Date = Year; -- promote Year to Date
Year = nil; -- make nil so Year as empty string isn't used for CITEREF
if is_set(Date) then
local Month = A['Month'];
if is_set(Month) then
Date = Month .. " " .. Date;
local Day = A['Day']
if is_set(Day) then Date = Day .. " " .. Date end
end
elseif is_set(PublicationDate) then -- use PublicationDate when |date= and |year= are not set
Date = PublicationDate; -- promonte PublicationDate to Date
PublicationDate = ''; -- unset, no longer needed
end
end
 
if PublicationDate == Date then PublicationDate = ''; end -- if PublicationDate is same as Date, don't display in rendered citation
 
-- Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates.
-- TODO: 2013-10-27: AirDate is nil when dates() is called because it hasn't been set yet.  Move the call to dates() or set AirDate earlier.
anchor_year, COinS_date = dates({['accessdate']=AccessDate, ['airdate']=AirDate, ['archivedate']=ArchiveDate, ['date']=Date, ['doi_brokendate']=DoiBroken,
['embargo']=Embargo, ['laydate']=LayDate, ['publicationdate']=PublicationDate, ['year']=Year});
 
-- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.
 
--Account for the oddity that is {{cite journal}} with |pmc= set and |url= not set
if config.CitationClass == "journal" and not is_set(URL) and is_set(ID_list['PMC']) then
if not is_embargoed(Embargo) then
URL=cfg.id_handlers['PMC'].prefix .. ID_list['PMC']; -- set url to be the same as the PMC external link if not embargoed
end
end
 
    -- Account for the oddity that is {{cite conference}}, before generation of COinS data.
    if is_set(BookTitle) then
         Chapter = Title;
         ChapterLink = TitleLink;
         TransChapter = TransTitle;
         TransChapter = TransTitle;
         Title = '';
         Title = BookTitle;
         TitleLink = '';
         TitleLink = '';
         TransTitle = '';
         TransTitle = '';
     end
     end
 
     -- Account for the oddity that is {{cite episode}}, before generation of COinS data.
     -- Now perform various field substitutions.
     if config.CitationClass == "episode" then
     -- We also add leading spaces and surrounding markup and punctuation to the
        local AirDate = A['AirDate'];
    -- various parts of the citation, but only when they are non-nil.
        local SeriesLink = A['SeriesLink'];
    if not is_set(Authors) then
        local Season = A['Season'];
         local Maximum = tonumber( A['DisplayAuthors'] );
        local SeriesNumber = A['SeriesNumber'];
        local Network = A['Network'];
        local Station = A['Station'];
        local s, n = {}, {};
        local Sep = (first_set(A["SeriesSeparator"], A["Separator"]) or "") .. " ";
       
        if is_set(Issue) then table.insert(s, cfg.messages["episode"] .. " " .. Issue); Issue = ''; end
        if is_set(Season) then table.insert(s, cfg.messages["season"] .. " " .. Season); end
         if is_set(SeriesNumber) then table.insert(s, cfg.messages["series"] .. " " .. SeriesNumber); end
        if is_set(Network) then table.insert(n, Network); end
        if is_set(Station) then table.insert(n, Station); end
          
          
         -- Preserve old-style implicit et al.
         Date = Date or AirDate;
         if not is_set(Maximum) and #a == 9 then
         Chapter = Title;
            Maximum = 8;
        ChapterLink = TitleLink;
            table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } );
        TransChapter = TransTitle;
         elseif not is_set(Maximum) then
         Title = Series;
            Maximum = #a + 1;
         TitleLink = SeriesLink;
         end
         TransTitle = '';
           
         local control = {
            sep = A["AuthorSeparator"] .. " ",
            namesep = (first_set(A["AuthorNameSeparator"], A["NameSeparator"]) or "") .. " ",
            format = A["AuthorFormat"],
            maximum = Maximum,
            lastauthoramp = LastAuthorAmp
        };
          
          
         -- If the coauthor field is also used, prevent ampersand and et al. formatting.
         Series = table.concat(s, Sep);
        if is_set(Coauthors) then
         ID = table.concat(n, Sep);
            control.lastauthoramp = nil;
            control.maximum = #a + 1;
         end
       
        Authors = listpeople(control, a)  
     end
     end
   
    -- COinS metadata (see <http://ocoins.info/>) for
    -- automated parsing of citation information.
    local OCinSoutput = COinS{
        ['Periodical'] = Periodical,
        ['Chapter'] = Chapter,
        ['Title'] = Title,
        ['PublicationPlace'] = PublicationPlace,
        ['Date'] = first_set(COinS_date, Date), -- COinS_date has correctly formatted date if Date is valid; any reason to keep Date here?  Should we be including invalid dates in metadata?
        ['Series'] = Series,
        ['Volume'] = Volume,
        ['Issue'] = Issue,
        ['Pages'] = first_set(Page, Pages, At),
        ['Edition'] = Edition,
        ['PublisherName'] = PublisherName,
        ['URL'] = first_set( URL, ChapterURL ),
        ['Authors'] = a,
        ['ID_list'] = ID_list,
        ['RawPage'] = this_page.prefixedText,
    };


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 is_set(Periodical) and not is_set(Chapter) and is_set(Title) then
table.insert( z.message_tail, { seterror('coauthors_missing_author', {}, true) } ); -- emit error message
        Chapter = Title;
end
        ChapterLink = TitleLink;
 
        TransChapter = TransTitle;
     local EditorCount
        Title = '';
     if not is_set(Editors) then
        TitleLink = '';
         local Maximum = tonumber( A['DisplayEditors'] );
        TransTitle = '';
    end
 
    -- Now perform various field substitutions.
    -- We also add leading spaces and surrounding markup and punctuation to the
     -- various parts of the citation, but only when they are non-nil.
     if not is_set(Authors) then
         local Maximum = tonumber( A['DisplayAuthors'] );
       
         -- Preserve old-style implicit et al.
         -- Preserve old-style implicit et al.
         if not is_set(Maximum) and #e == 4 then  
         if not is_set(Maximum) and #a == 9 then  
             Maximum = 3;
             Maximum = 8;
             table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } );
             table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } );
         elseif not is_set(Maximum) then
         elseif not is_set(Maximum) then
             Maximum = #e + 1;
             Maximum = #a + 1;
         end
         end
 
           
         local control = {  
         local control = {  
             sep = A["EditorSeparator"] .. " ",
             sep = A["AuthorSeparator"] .. " ",
             namesep = (first_set(A["EditorNameSeparator"], A["NameSeparator"]) or "") .. " ",
             namesep = (first_set(A["AuthorNameSeparator"], A["NameSeparator"]) or "") .. " ",
             format = A['EditorFormat'],
             format = A["AuthorFormat"],
             maximum = Maximum,
             maximum = Maximum,
             lastauthoramp = LastAuthorAmp
             lastauthoramp = LastAuthorAmp
         };
         };
       
        -- If the coauthor field is also used, prevent ampersand and et al. formatting.
        if is_set(Coauthors) then
            control.lastauthoramp = nil;
            control.maximum = #a + 1;
        end
       
        Authors = listpeople(control, a)
    end


        Editors, EditorCount = listpeople(control, e);
if not is_set(Authors) and is_set(Coauthors) then -- coauthors aren't displayed if one of authors=, authorn=, or lastn= isn't specified
    else
table.insert( z.message_tail, { seterror('coauthors_missing_author', {}, true) } ); -- emit error message
        EditorCount = 1;
end
    end


     local Cartography = "";
     local EditorCount
    local Scale = "";
     if not is_set(Editors) then
     if config.CitationClass == "map" then
         local Maximum = tonumber( A['DisplayEditors'] );
        if not is_set( Authors ) and is_set( PublisherName ) then
        -- Preserve old-style implicit et al.
            Authors = PublisherName;
         if not is_set(Maximum) and #e == 4 then  
            PublisherName = "";
             Maximum = 3;
         end
            table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } );
        Cartography = A['Cartography'];
         elseif not is_set(Maximum) then
         if is_set( Cartography ) then
             Maximum = #e + 1;
             Cartography = sepc .. " " .. wrap( 'cartography', Cartography, use_lowercase );
        end       
        Scale = A['Scale'];
        if is_set( Scale ) then
            Scale = sepc .. " " .. Scale;
         end       
    end
   
    if not is_set(Date) then
        Date = Year;
        if is_set(Date) then
             local Month = A['Month'];
            if is_set(Month) then
                Date = Month .. " " .. Date;
                local Day = A['Day']
                if is_set(Day) then Date = Day .. " " .. Date end
            end
         end
         end
    end
   
    if inArray(PublicationDate, {Date, Year}) then PublicationDate = ''; end
    if not is_set(Date) and is_set(PublicationDate) then
        Date = PublicationDate;
        PublicationDate = '';
    end


    -- Captures the value for Date prior to adding parens or other textual transformations
        local control = {
    local DateIn = Date;
            sep = A["EditorSeparator"] .. " ",
   
            namesep = (first_set(A["EditorNameSeparator"], A["NameSeparator"]) or "") .. " ",
    if  not is_set(URL) and
            format = A['EditorFormat'],
         not is_set(ChapterURL) and
            maximum = Maximum,
         not is_set(ArchiveURL) and
            lastauthoramp = LastAuthorAmp
         not is_set(ConferenceURL) and
         };
        not is_set(TranscriptURL) then
 
       
         Editors, EditorCount = listpeople(control, e);
        -- Test if cite web is called without giving a URL
    else
        if ( config.CitationClass == "web" ) then
         EditorCount = 1;
            table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } );
    end
 
    local Cartography = "";
    local Scale = "";
    if config.CitationClass == "map" then
        if not is_set( Authors ) and is_set( PublisherName ) then
            Authors = PublisherName;
            PublisherName = "";
         end
         end
          
        Cartography = A['Cartography'];
         -- Test if accessdate is given without giving a URL
        if is_set( Cartography ) then
         if is_set(AccessDate) then
            Cartography = sepc .. " " .. wrap( 'cartography', Cartography, use_lowercase );
        end       
        Scale = A['Scale'];
        if is_set( Scale ) then
            Scale = sepc .. " " .. Scale;
        end       
    end
   
    if  not is_set(URL) and
        not is_set(ChapterURL) and
        not is_set(ArchiveURL) and
        not is_set(ConferenceURL) and
        not is_set(TranscriptURL) then
       
        -- Test if cite web is called without giving a URL
        if ( config.CitationClass == "web" ) then
            table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } );
        end
          
         -- Test if accessdate is given without giving a URL
         if is_set(AccessDate) then
             table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } );
             table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } );
             AccessDate = '';
             AccessDate = '';
Line 1,383: Line 1,639:
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).
Categories are assigned in a manner similar to the {{xx icon}} templates - categorizes only mainspace citations and only when the language code is not 'en' (English).
]]
]]
if is_set (Language) then
if is_set (Language) then
local name = cfg.iso639_1[Language:lower()]; -- get the language name if Language parameter has a valid iso 639-1 code
local name = cfg.iso639_1[Language:lower()]; -- get the language name if Language parameter has a valid iso 639-1 code
if nil == name then
if nil == name then
Language=" " .. wrap( 'language', Language ); -- no match, use parameter's value
Language=" " .. wrap( 'language', Language ); -- no match, use parameter's value
else
else
if 0 == this_page.namespace and 'en' ~= Language:lower() then --found a match; is this page main / article space and English not the language?
if 0 == this_page.namespace and 'en' ~= Language:lower() then --found a match; is this page main / article space and English not the language?
Language=" " .. wrap( 'language', name .. '[[Category:Articles with ' .. name .. '-language external links]]' ); -- in main space and not English: categorize
Language=" " .. wrap( 'language', name .. '[[Category:Articles with ' .. name .. '-language external links]]' ); -- in main space and not English: categorize
else
else
Language=" " .. wrap( 'language', name ); --not in mainspace or language is English so don't categorize
Language=" " .. wrap( 'language', name ); --not in mainspace or language is English so don't categorize
end
end
end
end
else
else
Language=""; -- language not specified so make sure this is an empty string;
Language=""; -- language not specified so make sure this is an empty string;
end
 
Others = is_set(Others) and (sepc .. " " .. Others) or "";
 
-- handle type parameter for those CS1 citations that have default values
 
if inArray(config.CitationClass, {"pressrelease","techreport","thesis", "speech"}) then
TitleType = set_titletype (config.CitationClass, TitleType);
if is_set(Degree) and "Thesis" == TitleType then -- special case for cite thesis
TitleType = Degree .. " thesis";
end
end
 
if is_set(TitleType) then -- if type parameter is specified
TitleType = " (" .. TitleType .. ")"; -- display it in parentheses
end
 
TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";
    Edition = is_set(Edition) and (" " .. wrap( 'edition', Edition )) or "";
    Issue = is_set(Issue) and (" (" .. Issue .. ")") or "";
    Series = is_set(Series) and (sepc .. " " .. Series) or "";
    OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or "";
    Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";
   
    if is_set(Volume) then
        if ( mw.ustring.len(Volume) > 4 )
          then Volume = sepc .." " .. Volume;
          else Volume = " <b>" .. hyphentodash(Volume) .. "</b>";
        end
    end
   
    ------------------------------------ totally unrelated data
    --[[ Loosely mimic {{subscription required}} template; Via parameter identifies a delivery source that is not the publisher; these sources often, but not always, exist
    behind a registration or paywall.  So here, we've chosen to decouple via from subscription (via has never been part of the registration required template).
   
    Subscription implies paywall; Registration does not.  If both are used in a citation, the subscription required link note is displayed. There are no error messages for this condition.
    ]]
    if is_set(Via) then
        Via = " " .. wrap( 'via', Via );
    end
 
if is_set(SubscriptionRequired) then
        SubscriptionRequired = sepc .. " " .. cfg.messages['subscription']; --here when 'via' parameter not used but 'subscription' is
    elseif is_set(RegistrationRequired) then
        SubscriptionRequired = sepc .. " " .. cfg.messages['registration']; --here when 'via' and 'subscription' parameters not used but 'registration' is
    end
 
    if is_set(AccessDate) then
        local retrv_text = " " .. cfg.messages['retrieved']
        if (sepc ~= ".") then retrv_text = retrv_text:lower() end
        AccessDate = '<span class="reference-accessdate">' .. sepc
            .. substitute( retrv_text, {AccessDate} ) .. '</span>'
    end
   
    if is_set(ID) then ID = sepc .." ".. ID; end
  if "thesis" == config.CitationClass and is_set(Docket) then
ID = sepc .." Docket ".. Docket .. ID;
end
end


    Others = is_set(Others) and (sepc .. " " .. Others) or "";
    TitleType = is_set(TitleType) and (" (" .. TitleType .. ")") or "";
    TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";
    Edition = is_set(Edition) and (" " .. wrap( 'edition', Edition )) or "";
    Issue = is_set(Issue) and (" (" .. Issue .. ")") or "";
    Series = is_set(Series) and (sepc .. " " .. Series) or "";
    OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or "";
    Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";
   
    if is_set(Volume) then
        if ( mw.ustring.len(Volume) > 4 )
          then Volume = sepc .." " .. Volume;
          else Volume = " <b>" .. hyphentodash(Volume) .. "</b>";
        end
    end
   
    ------------------------------------ totally unrelated data
    --[[ Loosely mimic {{subscription required}} template; Via parameter identifies a delivery source that is not the publisher; these sources often, but not always, exist
    behind a registration or paywall.  So here, we've chosen to decouple via from subscription (via has never been part of the registration required template).
   
    Subscription implies paywall; Registration does not.  If both are used in a citation, the subscription required link note is displayed. There are no error messages for this condition.
    ]]
    if is_set(Via) then
        Via = " " .. wrap( 'via', Via );
    end
if is_set(SubscriptionRequired) then
        SubscriptionRequired = sepc .. " " .. cfg.messages['subscription']; --here when 'via' parameter not used but 'subscription' is
    elseif is_set(RegistrationRequired) then
        SubscriptionRequired = sepc .. " " .. cfg.messages['registration']; --here when 'via' and 'subscription' parameters not used but 'registration' is
    end
    if is_set(AccessDate) then
        local retrv_text = " " .. cfg.messages['retrieved']
        if (sepc ~= ".") then retrv_text = retrv_text:lower() end
        AccessDate = '<span class="reference-accessdate">' .. sepc
            .. substitute( retrv_text, {AccessDate} ) .. '</span>'
    end
   
    if is_set(ID) then ID = sepc .." ".. ID; end
      
      
     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} );
Line 1,654: Line 1,927:


     -- Now enclose the whole thing in a <span/> element
     -- Now enclose the whole thing in a <span/> element
    if not is_set(Year) then
        if is_set(DateIn) then
            Year = selectyear( DateIn );
        elseif is_set(PublicationDate) then
            Year = selectyear( PublicationDate );
        end
    end
   
     local options = {};
     local options = {};
      
      
Line 1,685: Line 1,950:
                 end
                 end
             end
             end
            names[ #names + 1 ] = Year;
names[ #names + 1 ] = first_set(Year, anchor_year); -- Year first for legacy citations
-- names[ #names + 1 ] = Year or anchor_year; -- Year first for legacy citations
             id = anchorid(names)
             id = anchorid(names)
         end
         end
14,061

edits

Navigation menu