Module:Citation/CS1: Difference between revisions
Synch from sandbox;
m>Trappist the monk (Synch from sandbox;) |
m>Trappist the monk (Synch from sandbox;) |
||
Line 11: | Line 11: | ||
]] | ]] | ||
local dates, year_date_check -- functions in Module:Citation/CS1/Date_validation | local dates, year_date_check -- functions in Module:Citation/CS1/Date_validation | ||
local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration | |||
local whitelist = {}; -- talbe of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist | |||
--[[--------------------------< I S _ S E T >------------------------------------------------------------------ | --[[--------------------------< I S _ S E T >------------------------------------------------------------------ | ||
Line 111: | Line 114: | ||
return error_comment( message, error_state.hidden ); | return error_comment( message, error_state.hidden ); | ||
end | |||
--[[--------------------------< A D D _ M A I N T _ C A T >------------------------------------------------------ | |||
Adds a category to z.maintenance_cats using names from the configuration file with additional text if any. | |||
]] | |||
local function add_maint_cat (key, arguments) | |||
table.insert( z.maintenance_cats, substitute (cfg.maint_cats [key], arguments)); -- make name then add to table | |||
end | |||
--[[--------------------------< A D D _ P R O P _ C A T >-------------------------------------------------------- | |||
Adds a category to z.properties_cats using names from the configuration file with additional text if any. | |||
]] | |||
local function add_prop_cat (key, arguments) | |||
table.insert( z.properties_cats, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table | |||
end | end | ||
Line 302: | Line 325: | ||
-- is prefix one of these language codes? | -- is prefix one of these language codes? | ||
if in_array (lang, {'ar', 'bg', 'bs', 'dv', 'el', 'fa', 'hy', 'ja', 'ka', 'ko', 'ku', 'he', 'ps', 'ru', 'sd', 'sr', 'th', 'uk', 'ug', 'yi', 'zh'}) then | if in_array (lang, {'ar', 'bg', 'bs', 'dv', 'el', 'fa', 'hy', 'ja', 'ka', 'ko', 'ku', 'he', 'ps', 'ru', 'sd', 'sr', 'th', 'uk', 'ug', 'yi', 'zh'}) then | ||
add_prop_cat ('script_name', {name, lang}) | |||
else | else | ||
add_prop_cat ('script') | |||
end | end | ||
lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute | lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute | ||
Line 354: | Line 377: | ||
end | end | ||
end | end | ||
--[[--------------------------< S E L E C T _ O N E >---------------------------------------------------------- | --[[--------------------------< S E L E C T _ O N E >---------------------------------------------------------- | ||
Line 454: | Line 478: | ||
can be transparently aliased to single internal variable. | can be transparently aliased to single internal variable. | ||
]] | ]] | ||
local function argument_wrapper( args ) | local function argument_wrapper( args ) | ||
local origin = {}; | local origin = {}; | ||
Line 504: | Line 529: | ||
nil - unsupported parameters | nil - unsupported parameters | ||
]] | ]] | ||
local function validate( name ) | local function validate( name ) | ||
local name = tostring( name ); | local name = tostring( name ); | ||
Line 572: | Line 598: | ||
local function is_valid_isxn (isxn_str, len) | local function is_valid_isxn (isxn_str, len) | ||
local temp = 0; | local temp = 0; | ||
isxn_str = { isxn_str:byte(1, len) }; -- make a table of | isxn_str = { isxn_str:byte(1, len) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58 | ||
len = len+1; -- adjust to be a loop counter | 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 | 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 | if v == string.byte( "X" ) then -- if checkdigit is X (compares the byte value of 'X' which is 0x58) | ||
temp = temp + 10*( len - i ); -- it represents 10 decimal | temp = temp + 10*( len - i ); -- it represents 10 decimal | ||
else | else | ||
Line 674: | Line 700: | ||
if id:match("^%d%d%d%d%d%d%d%d%d[%dX]$") then -- if 10-digit numeric (or 9 digits with terminal X) | if id:match("^%d%d%d%d%d%d%d%d%d[%dX]$") then -- if 10-digit numeric (or 9 digits with terminal X) | ||
if check_isbn( id ) then -- see if asin value is isbn10 | if check_isbn( id ) then -- see if asin value is isbn10 | ||
add_maint_cat ('ASIN'); | |||
elseif not is_set (err_cat) then | elseif not is_set (err_cat) then | ||
err_cat = ' ' .. set_error ('bad_asin'); -- asin is not isbn10 | err_cat = ' ' .. set_error ('bad_asin'); -- asin is not isbn10 | ||
Line 724: | Line 750: | ||
]] | ]] | ||
local function arxiv (id) | local function arxiv (id, class) | ||
local handler = cfg.id_handlers['ARXIV']; | local handler = cfg.id_handlers['ARXIV']; | ||
local year, month, version; | local year, month, version; | ||
local err_cat = | local err_cat = ''; | ||
local text; | |||
if id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%d$") or id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%dv%d+$") then -- test for the 9108-0703 format w/ & w/o version | if id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%d$") or id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%dv%d+$") then -- test for the 9108-0703 format w/ & w/o version | ||
Line 756: | Line 783: | ||
end | end | ||
text = external_link_id({link = handler.link, label = handler.label, | |||
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat; | prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat; | ||
if is_set (class) then | |||
class = ' [[' .. '//arxiv.org/archive/' .. class .. ' ' .. class .. ']]'; -- external link within square brackets, not wikilink | |||
else | |||
class = ''; -- empty string for concatenation | |||
end | |||
return text .. class; | |||
end | end | ||
--[[ | --[[ | ||
Line 804: | Line 838: | ||
]] | ]] | ||
local function lccn(lccn) | local function lccn(lccn) | ||
local handler = cfg.id_handlers['LCCN']; | local handler = cfg.id_handlers['LCCN']; | ||
Line 850: | Line 885: | ||
contains only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more PMIDs are issued. | contains only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more PMIDs are issued. | ||
]] | ]] | ||
local function pmid(id) | local function pmid(id) | ||
local test_limit = 30000000; -- update this value as PMIDs approach | local test_limit = 30000000; -- update this value as PMIDs approach | ||
Line 868: | Line 904: | ||
end | end | ||
--[[ | --[[--------------------------< I S _ E M B A R G O E D >------------------------------------------------------ | ||
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 | in the future, returns the content of |embargo=; otherwise, returns and empty string because the embargo has expired or because | ||
|embargo= was not set in this cite. | |||
]] | ]] | ||
local function is_embargoed(embargo) | |||
if is_set(embargo) then | local function is_embargoed (embargo) | ||
if is_set (embargo) then | |||
local lang = mw.getContentLanguage(); | local lang = mw.getContentLanguage(); | ||
local good1, embargo_date, good2, todays_date; | local good1, embargo_date, good2, todays_date; | ||
Line 879: | Line 919: | ||
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? | if good1 and good2 then -- if embargo date and today's date are good dates | ||
if tonumber( embargo_date ) >= tonumber( todays_date ) then -- is embargo date is in the future? | |||
return embargo; -- still embargoed | |||
else | |||
add_maint_cat ('embargo') | |||
return ''; -- unset because embargo has expired | |||
end | |||
end | end | ||
end | end | ||
return | return ''; -- |embargo= not set return empty string | ||
end | end | ||
--[[ | --[[--------------------------< P M C >------------------------------------------------------------------------ | ||
Format a PMC, do simple error checking, and check for embargoed articles. | Format a PMC, do simple error checking, and check for embargoed articles. | ||
The embargo parameter takes a date for a value. If the embargo date is in the future | The embargo parameter takes a date for a value. If the embargo date is in the future the PMC identifier will not | ||
the PMC identifier will not be linked to the article. If the embargo | be linked to the article. If the embargo date is today or 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. | |||
PMC embargo date testing is done in function is_embargoed () which is called earlier because when the citation | |||
has |pmc=<value> but does not have a |url= then |title= is linked with the PMC link. Function is_embargoed () | |||
returns the embargo date if the PMC article is still embargoed, otherwise it returns an empty string. | |||
PMCs are sequential numbers beginning at 1 and counting up. This code checks the PMC to see that it contains only digits and is less | PMCs are sequential numbers beginning at 1 and counting up. This code checks the PMC to see that it contains only digits and is less | ||
than test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issued. | than test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issued. | ||
]] | ]] | ||
local function pmc(id, embargo) | local function pmc(id, embargo) | ||
local test_limit = 5000000; -- update this value as PMCs approach | local test_limit = 5000000; -- update this value as PMCs approach | ||
Line 912: | Line 964: | ||
end | end | ||
if | if is_set (embargo) then -- is PMC is still embargoed? | ||
text="[[" .. handler.link .. "|" .. handler.label .. "]]:" .. handler.separator .. id .. err_cat; --still embargoed so no external link | text="[[" .. handler.link .. "|" .. handler.label .. "]]:" .. handler.separator .. id .. err_cat; -- still embargoed so no external link | ||
else | else | ||
text = external_link_id({link = handler.link, label = handler.label, --no embargo date, ok to link to article | text = external_link_id({link = handler.link, label = handler.label, -- no embargo date or embargo has expired, ok to link to article | ||
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat; | prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat; | ||
end | end | ||
Line 1,004: | Line 1,056: | ||
return text | return text | ||
end | end | ||
--[[--------------------------< S E T _ T I T L E T Y P E >---------------------------------------------------- | --[[--------------------------< S E T _ T I T L E T Y P E >---------------------------------------------------- | ||
This function sets default title types (equivalent to the citation including |type=<default value>) for those | This function sets default title types (equivalent to the citation including |type=<default value>) for those templates 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 desirable to omit the title type from the rendered citation (|type=none). | ||
]] | ]] | ||
local function set_titletype(cite_class, title_type) | |||
local function set_titletype (cite_class, title_type) | |||
if is_set(title_type) then | if is_set(title_type) then | ||
if "none" == title_type then | if "none" == title_type then | ||
title_type = ""; | title_type = ""; -- if |type=none then type parameter not displayed | ||
end | end | ||
return title_type; | return title_type; -- if |type= has been set to any other value use that value | ||
end | end | ||
return cfg.title_types [cite_class] or ''; -- set template's default title type; else empty string for concatenation | |||
end | |||
--[[--------------------------< C L E A N _ I S B N >---------------------------------------------------------- | |||
Removes irrelevant text and dashes from ISBN number | |||
Similar to that used for Special:BookSources | |||
]] | |||
local function clean_isbn( isbn_str ) | local function clean_isbn( isbn_str ) | ||
return isbn_str:gsub( "[^-0-9X]", "" ); | return isbn_str:gsub( "[^-0-9X]", "" ); | ||
Line 1,071: | Line 1,104: | ||
]] | ]] | ||
local function strip_apostrophe_markup (argument) | local function strip_apostrophe_markup (argument) | ||
if not is_set (argument) then return argument; end | if not is_set (argument) then return argument; end | ||
Line 1,121: | Line 1,155: | ||
]] | ]] | ||
local function get_coins_pages (pages) | local function get_coins_pages (pages) | ||
local pattern; | local pattern; | ||
Line 1,157: | Line 1,192: | ||
]] | ]] | ||
local function safe_join( tbl, duplicate_char ) | local function safe_join( tbl, duplicate_char ) | ||
--[[ | --[[ | ||
Line 1,228: | Line 1,264: | ||
return str; | return str; | ||
end | end | ||
--[[--------------------------< I S _ G O O D _ V A N C _ N A M E >-------------------------------------------- | --[[--------------------------< I S _ G O O D _ V A N C _ N A M E >-------------------------------------------- | ||
Line 1,235: | Line 1,270: | ||
uses characters that contain diacritical marks, those characters are to converted to the corresponding Latin character. | uses characters that contain diacritical marks, those characters are to converted to the corresponding Latin character. | ||
When a name is written using a non-Latin alphabet or logogram, that name is to be transliterated into Latin characters. | When a name is written using a non-Latin alphabet or logogram, that name is to be transliterated into Latin characters. | ||
These things are not currently possible in this module so are left to the editor to do. | These things are not currently possible in this module so are left to the editor to do. | ||
This test allows |first= and |last= names to contain any of the letters defined in the four Unicode Latin character sets | |||
[http://www.unicode.org/charts/PDF/U0000.pdf C0 Controls and Basic Latin] 0041–005A, 0061–007A | |||
[http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] 00C0–00D6, 00D8–00F6, 00F8–00FF | |||
[http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended-A] 0100–017F | |||
[http://www.unicode.org/charts/PDF/U0180.pdf Latin Extended-B] 0180–01BF, 01C4–024F | |||
|lastn= also allowed to contain hyphens, spaces, and apostrophes. (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/) | |||
|firstn= also allowed to contain hyphens, spaces, apostrophes, and periods | |||
At the time of this writing, I had to write the 'if nil == mw.ustring.find ...' test ouside of the code editor and past it here | |||
because the code editor gets confused between character insertion point and cursor position. | |||
]] | ]] | ||
local function is_good_vanc_name (last, first) | local function is_good_vanc_name (last, first) | ||
if | if nil == mw.ustring.find (last, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%']*$") or nil == mw.ustring.find (first, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]*$") then | ||
if true ~= Page_in_vanc_error_cat then -- if we haven't been here before then set a sticky flag | if true ~= Page_in_vanc_error_cat then -- if we haven't been here before then set a sticky flag | ||
Page_in_vanc_error_cat=true; -- so that if there are more than one error the category is added only once | Page_in_vanc_error_cat=true; -- so that if there are more than one error the category is added only once | ||
Line 1,253: | Line 1,296: | ||
return true; | return true; | ||
end | end | ||
--[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------ | --[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------ | ||
Line 1,263: | Line 1,305: | ||
Vancouver style requires family rank designations (Jr, II, III, etc) to be rendered as Jr, 2nd, 3rd, etc. This form is not | Vancouver style requires family rank designations (Jr, II, III, etc) to be rendered as Jr, 2nd, 3rd, etc. This form is not | ||
currently supported by this code so correctly formed names like Smith JL 2nd are converted to Smith J2. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/. | currently supported by this code so correctly formed names like Smith JL 2nd are converted to Smith J2. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/. | ||
This function uses ustring functions because firstname initials may be any of the unicode Latin characters accepted by is_good_vanc_name (). | |||
]] | ]] | ||
local function reduce_to_initials(first) | local function reduce_to_initials(first) | ||
if | if mw.ustring.match(first, "^%u%u$") then return first end; -- when first contains just two upper-case letters, nothing to do | ||
local initials = {} | local initials = {} | ||
local i = 0; -- counter for number of initials | local i = 0; -- counter for number of initials | ||
for word in | for word in mw.ustring.gmatch(first, "[^%s%.%-]+") do -- names separated by spaces, hyphens, or periods | ||
table.insert(initials, | table.insert(initials, mw.ustring.sub(word,1,1)) -- Vancouver format does not include full stops. | ||
i = i + 1; -- bump the counter | i = i + 1; -- bump the counter | ||
if 2 <= i then break; end -- only two initials allowed in Vancouver system; if 2, quit | if 2 <= i then break; end -- only two initials allowed in Vancouver system; if 2, quit | ||
Line 1,279: | Line 1,323: | ||
--[[--------------------------< L I S T _ P E O P L E >------------------------------------------------------- | --[[--------------------------< L I S T _ P E O P L E >------------------------------------------------------- | ||
Formats a list of people (e.g. authors / editors) | Formats a list of people (e.g. authors / editors) | ||
]] | ]] | ||
local function list_people(control, people, etal) | local function list_people(control, people, etal) | ||
local sep; | local sep; | ||
Line 1,298: | Line 1,345: | ||
if sep:sub(-1,-1) ~= " " then sep = sep .. " " end | if sep:sub(-1,-1) ~= " " then sep = sep .. " " end | ||
if maximum | if is_set (maximum) and maximum < 1 then return "", 0; end -- returned 0 is for EditorCount; not used for authors | ||
for i,person in ipairs(people) do | for i,person in ipairs(people) do | ||
Line 1,305: | Line 1,352: | ||
local one | local one | ||
local sep_one = sep; | local sep_one = sep; | ||
if maximum | if is_set (maximum) and i > maximum then | ||
etal = true; | etal = true; | ||
break; | break; | ||
Line 1,358: | Line 1,405: | ||
--[[--------------------------< A N C H O R _ I D >------------------------------------------------------------ | --[[--------------------------< A N C H O R _ I D >------------------------------------------------------------ | ||
Generates a CITEREF anchor ID if we have at least one name or a date. Otherwise returns an empty string. | Generates a CITEREF anchor ID if we have at least one name or a date. Otherwise returns an empty string. | ||
Line 1,437: | Line 1,485: | ||
if true == etal then | if true == etal then | ||
add_maint_cat ('etal'); | |||
end | end | ||
return names, etal; -- all done, return our list of names | return names, etal; -- all done, return our list of names | ||
Line 1,453: | Line 1,501: | ||
--[[--------------------------< B U I L D _ I D _ L I S T >-------------------------------------------------------- | --[[--------------------------< B U I L D _ I D _ L I S T >-------------------------------------------------------- | ||
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. | ||
]] | ]] | ||
local function build_id_list( id_list, options ) | local function build_id_list( id_list, options ) | ||
local new_list, handler = {}; | local new_list, handler = {}; | ||
Line 1,474: | Line 1,524: | ||
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, options.Class ) } ); | ||
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 ) } ); | ||
Line 1,515: | Line 1,565: | ||
-- COinS metadata (see <http://ocoins.info/>) allows automated tools to parse | -- COinS metadata (see <http://ocoins.info/>) allows automated tools to parse | ||
-- the citation information. | -- the citation information. | ||
local function COinS(data) | local function COinS(data, class) | ||
if 'table' ~= type(data) or nil == next(data) then | if 'table' ~= type(data) or nil == next(data) then | ||
return ''; | return ''; | ||
Line 1,538: | Line 1,588: | ||
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"; | if 'arxiv' == class then | ||
OCinSoutput["rft.genre"] = "preprint"; -- cite arxiv | |||
else | |||
OCinSoutput["rft.genre"] = "article"; | |||
end | |||
OCinSoutput["rft.jtitle"] = data.Periodical; | OCinSoutput["rft.jtitle"] = data.Periodical; | ||
OCinSoutput["rft.atitle"] = data.Title; | OCinSoutput["rft.atitle"] = data.Title; | ||
Line 1,610: | Line 1,664: | ||
Adapted from code taken from Module:Check ISO 639-1. | Adapted from code taken from Module:Check ISO 639-1. | ||
]] | ]] | ||
Line 1,647: | Line 1,702: | ||
When |language= contains a valid ISO639-1 code, the page is assigned to the category for that code: Category:Norwegian-language sources (no) if | 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. | 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. | ||
This function supports multiple languages in the form |language=nb, French, th where the language names or codes are separated from each other by commas. | |||
]] | ]] | ||
local function language_parameter (lang, namespace) | local function language_parameter (lang, namespace) | ||
local code; | local code; -- the ISO639-1 two character code | ||
local name; | local name; -- the language name | ||
local | local language_list = {}; -- table of language names to be rendered | ||
local names_table = {}; -- table made from the value assigned to |language= | |||
local unrec_cat = false -- flag so that we only add unrecognized category once | |||
names_table = mw.text.split (lang, '%s*,%s*'); -- names should be a comma separated list | |||
for _, lang in ipairs (names_table) do -- reuse lang | |||
if 0 == namespace and (('en' == lang:lower()) or ('english' == lang:lower())) then | |||
add_maint_cat ('english'); | |||
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? | |||
add_prop_cat ('foreign_lang_source', {name, code}) | |||
end | |||
elseif false == unrec_cat then | |||
unrec_cat = true; -- only add this category once | |||
add_maint_cat (unknown_lang); | |||
end | |||
if | table.insert (language_list, name); | ||
table. | name = ''; -- so we can reuse it | ||
end | |||
code = #language_list -- reuse code as number of languages in the list | |||
if 2 >= code then | |||
name = table.concat (language_list, ' and ') -- insert '<space>and<space>' between two language names | |||
elseif 2 < code then | |||
language_list[code] = 'and ' .. language_list[code]; -- prepend last name with 'and<space>' | |||
name = table.concat (language_list, ', ') -- and concatenate with '<comma><space>' separators | |||
end | end | ||
return (" " .. wrap_msg ('language', name)); -- wrap with '(in ...)' | |||
end | |||
--[[--------------------------< S E T _ C S 1 _ S T Y L E >---------------------------------------------------- | |||
Set style settings for CS1 citation templates. Returns separator and postscript settings | |||
]] | |||
if | local function set_cs1_style (ps) | ||
if not is_set (ps) then -- unless explicitely set to something | |||
ps = '.'; -- terminate the rendered citation with a period | |||
end | end | ||
return '.', ps; -- separator is a full stop | |||
end | |||
if is_set ( | --[[--------------------------< S E T _ C S 2 _ S T Y L E >---------------------------------------------------- | ||
Set style settings for CS2 citation templates. Returns separator, postscript, ref settings | |||
]] | |||
local function set_cs2_style (ps, ref) | |||
if not is_set (ps) then -- if |postscript= has not been set, set cs2 default | |||
ps = ''; -- make sure it isn't nil | |||
end | end | ||
if not is_set (ref) then -- if |ref= is not set | |||
if is_set ( | ref = "harv"; -- set default |ref=harv | ||
end | end | ||
return | return ',', ps, ref; -- separator is a comma | ||
end | end | ||
--[[--------------------------< G E T _ S E T T I N G S _ F R O M _ C I T E _ C L A S S >---------------------- | --[[--------------------------< G E T _ S E T T I N G S _ F R O M _ C I T E _ C L A S S >---------------------- | ||
When |mode= is not set or when its value is invalid, use config.CitationClass and parameter values to establish | When |mode= is not set or when its value is invalid, use config.CitationClass and parameter values to establish | ||
rendered style. | rendered style. | ||
Line 1,687: | Line 1,792: | ||
local function get_settings_from_cite_class (ps, ref, cite_class) | local function get_settings_from_cite_class (ps, ref, cite_class) | ||
local sep; | local sep; | ||
if (cite_class == "citation") then -- for citation templates (CS2) | if (cite_class == "citation") then -- for citation templates (CS2) | ||
sep | sep, ps, ref = set_cs2_style (ps, ref); | ||
else -- not a citation template so CS1 | else -- not a citation template so CS1 | ||
sep = | sep, ps = set_cs1_style (ps); | ||
end | end | ||
Line 1,714: | Line 1,810: | ||
local function set_style (mode, ps, ref, cite_class) | local function set_style (mode, ps, ref, cite_class) | ||
local sep; | local sep; | ||
if 'cs2' == mode then -- if this template is to be rendered in CS2 (citation) style | |||
sep, ps, ref = set_cs2_style (ps, ref); | |||
elseif 'cs1' == mode then -- if this template is to be rendered in CS1 (cite xxx) style | |||
sep, ps = set_cs1_style (ps); | |||
else -- anything but cs1 or cs2 | |||
if is_set (mode) then | |||
table.insert( z.message_tail, { set_error( 'invalid_param_val', {'mode', mode}, true ) } ); -- add error message | table.insert( z.message_tail, { set_error( 'invalid_param_val', {'mode', mode}, true ) } ); -- add error message | ||
end | end | ||
sep, ps, ref = get_settings_from_cite_class (ps, ref, cite_class); -- get settings based on the template's CitationClass | sep, ps, ref = get_settings_from_cite_class (ps, ref, cite_class); -- get settings based on the template's CitationClass | ||
end | end | ||
Line 1,743: | Line 1,828: | ||
end | end | ||
--[=[-------------------------< I S _ P D F >------------------------------------------------------------------ | |||
Determines if a url has the file extension is one of the pdf file extensions used by [[MediaWiki:Common.css]] when | |||
applying the pdf icon to external links. | |||
returns true if file extension is one of the recognized extension, else false | |||
]=] | |||
local function is_pdf (url) | |||
return url:match ('%.pdf[%?#]?') or url:match ('%.PDF[%?#]?'); | |||
end | |||
--[[--------------------------< S T Y L E _ F O R M A T >------------------------------------------------------ | |||
Applies css style to |format=, |chapter-format=, etc. Also emits an error message if the format parameter does | |||
not have a matching url parameter. If the format parameter is not set and the url contains a file extension that | |||
is recognized as a pdf document by MediaWiki's commons.css, this code will set the format parameter to (PDF) with | |||
the appropriate styling. | |||
]] | |||
local function style_format (format, url, fmt_param, url_param) | |||
if is_set (format) then | |||
format = wrap_style ('format', format:upper()); -- force upper case, add leading space, parenthases, resize | |||
if not is_set (url) then | |||
format = format .. set_error( 'format_missing_url', {fmt_param, url_param} ); -- add an error message | |||
end | |||
elseif is_pdf (url) then -- format is not set so if url is a pdf file then | |||
format = wrap_style ('format', 'PDF'); -- set format to pdf | |||
else | |||
format = ''; -- empty string for concatenation | |||
end | |||
return format; | |||
end | |||
--[[--------------------------< G E T _ D I S P L A Y _ A U T H O R S _ E D I T O R S >------------------------ | |||
Returns a number that may or may not limit the length of the author or editor name lists. | |||
When the value assigned to |display-authors= is a number greater than or equal to zero, return the number and | |||
the previous state of the 'etal' flag (false by default but may have been set to true if the name list contains | |||
some variant of the text 'et al.'). | |||
When the value assigned to |display-authors= is the keyword 'etal', return a number that is one greater than the | |||
number of authors in the list and set the 'etal' flag true. This will cause the list_people() to display all of | |||
the names in the name list followed by 'et al.' | |||
In all other cases, returns nil and the previous state of the 'etal' flag. | |||
]] | |||
local function get_display_authors_editors (max, count, list_name, etal) | |||
if is_set (max) then | |||
if 'etal' == max:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings | |||
max = count + 1; -- number of authors + 1 so display all author name plus et al. | |||
etal = true; -- overrides value set by extract_names() | |||
elseif max:match ('^%d+$') then -- if is a string of numbers | |||
max = tonumber (max); -- make it a number | |||
if max >= count and 'authors' == list_name then -- AUTHORS ONLY -- if |display-xxxxors= value greater than or equal to number of authors/editors | |||
add_maint_cat ('disp_auth_ed', list_name); | |||
end | |||
else -- not a valid keyword or number | |||
table.insert( z.message_tail, { set_error( 'invalid_param_val', {'display-' .. list_name, max}, true ) } ); -- add error message | |||
max = nil; -- unset | |||
end | |||
elseif 'authors' == list_name then -- AUTHORS ONLY need to clear implicit et al category | |||
max = count + 1; -- number of authors + 1 | |||
end | |||
return max, etal; | |||
end | |||
--[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------ | --[[--------------------------< 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. | ||
]] | ]] | ||
local function citation0( config, args) | local function citation0( config, args) | ||
--[[ | --[[ | ||
Line 1,798: | Line 1,958: | ||
local Degree = A['Degree']; | local Degree = A['Degree']; | ||
local Docket = A['Docket']; | local Docket = A['Docket']; | ||
local ArchiveFormat = A['ArchiveFormat']; | |||
local ArchiveURL = A['ArchiveURL']; | local ArchiveURL = A['ArchiveURL']; | ||
local URL = A['URL'] | local URL = A['URL'] | ||
Line 1,803: | Line 1,964: | ||
local ChapterURL = A['ChapterURL']; | local ChapterURL = A['ChapterURL']; | ||
local ChapterURLorigin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL | local ChapterURLorigin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL | ||
local ConferenceFormat = A['ConferenceFormat']; | |||
local ConferenceURL = A['ConferenceURL']; | local ConferenceURL = A['ConferenceURL']; | ||
local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL | local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL | ||
Line 1,835: | Line 1,997: | ||
local IgnoreISBN = A['IgnoreISBN']; | local IgnoreISBN = A['IgnoreISBN']; | ||
local Embargo = A['Embargo']; | local Embargo = A['Embargo']; | ||
local Class = A['Class']; -- arxiv class identifier | |||
local ID_list = extract_ids( args ); | local ID_list = extract_ids( args ); | ||
Line 1,840: | Line 2,003: | ||
local Quote = A['Quote']; | local Quote = A['Quote']; | ||
local LayFormat = A['LayFormat']; | |||
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 TranscriptFormat = A['TranscriptFormat']; | |||
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'); -- get name of parameter that holds TranscriptURL | ||
Line 1,867: | Line 2,032: | ||
use_lowercase = ( sepc == ',' ); -- used to control capitalization for certain static text | use_lowercase = ( sepc == ',' ); -- used to control capitalization for certain static text | ||
--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 | if not is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page | ||
if in_array (this_page.nsText, cfg.uncategorized_namespaces) then | if in_array (this_page.nsText, cfg.uncategorized_namespaces) then | ||
no_tracking_cats = "true"; -- set no_tracking_cats | no_tracking_cats = "true"; -- set no_tracking_cats | ||
end | |||
for _,v in ipairs (cfg.uncategorized_subpages) do -- cycle through page name patterns | |||
if this_page.text:match (v) then -- test page name against each pattern | |||
no_tracking_cats = "true"; -- set no_tracking_cats | |||
break; -- bail out if one is found | |||
end | |||
end | end | ||
end | end | ||
Line 1,979: | Line 2,150: | ||
if (config.CitationClass == "mailinglist") then | if (config.CitationClass == "mailinglist") then | ||
Periodical = A ['MailingList']; | Periodical = A ['MailingList']; | ||
end | end | ||
Line 2,005: | Line 2,168: | ||
URL = ''; | URL = ''; | ||
end | end | ||
elseif 'speech' ~= config.CitationClass then | |||
Conference = ''; -- not cite conference so make sure this is empty string | Conference = ''; -- not cite conference or cite speech so make sure this is empty string | ||
end | |||
-- cite map oddities | |||
local Cartography = ""; | |||
local Scale = ""; | |||
local Sheet = A['Sheet'] or ''; | |||
local Sheets = A['Sheets'] or ''; | |||
if config.CitationClass == "map" then | |||
Chapter = A['Map']; | |||
ChapterURL = A['MapURL']; | |||
TransChapter = A['TransMap']; | |||
ChapterURLorigin = A:ORIGIN('MapURL'); | |||
ChapterFormat = A['MapFormat']; | |||
Cartography = A['Cartography']; | |||
if is_set( Cartography ) then | |||
Cartography = sepc .. " " .. wrap_msg ('cartography', Cartography, use_lowercase); | |||
end | |||
Scale = A['Scale']; | |||
if is_set( Scale ) then | |||
Scale = sepc .. " " .. Scale; | |||
end | |||
end | end | ||
-- Account for the | -- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data. | ||
if 'episode' == config.CitationClass or 'serial' == config.CitationClass then | |||
if config.CitationClass == | |||
local AirDate = A['AirDate']; | local AirDate = A['AirDate']; | ||
local Began = A['Began']; -- these two are deprecated because the module understands date ranges | |||
local Ended = A['Ended']; | |||
local SeriesLink = A['SeriesLink']; | local SeriesLink = A['SeriesLink']; | ||
local Network = A['Network']; | local Network = A['Network']; | ||
local Station = A['Station']; | local Station = A['Station']; | ||
local s, n = {}, {}; | local s, n = {}, {}; | ||
-- do common parameters first | |||
if is_set(Network) then table.insert(n, Network); end | if is_set(Network) then table.insert(n, Network); end | ||
if is_set(Station) then table.insert(n, Station); end | if is_set(Station) then table.insert(n, Station); end | ||
ID = table.concat(n, sepc .. ' '); | |||
Date | if not is_set (Date) then -- promote airdate or Began/Ended to date | ||
if is_set (AirDate) then | |||
Date = AirDate; | |||
elseif is_set (Began) then -- deprecated | |||
if Began:match('%s') or Ended:match('%s') then -- so we don't create errors: if either has spaces then | |||
Date = Began .. ' – ' .. Ended; -- use spaced ndash as separator | |||
else | |||
Date = Began .. '–' .. Ended; -- elsewise no spaces | |||
end | |||
end | |||
end | |||
-- | |||
if 'episode' == config.CitationClass then -- handle the oddities that are strictly {{cite episode}} | |||
local Season = A['Season']; | |||
local SeriesNumber = A['SeriesNumber']; | |||
-- legacy: promote concatenation of |month=, and |year= to Date if Date not set; or, promote PublicationDate to Date if neither Date nor Year are set. | if is_set (Season) and is_set (SeriesNumber) then -- these are mutually exclusive so if both are set | ||
table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'season') .. ' and ' .. wrap_style ('parameter', 'seriesno')}, true ) } ); -- add error message | |||
SeriesNumber = ''; -- unset; prefer |season= over |seriesno= | |||
end | |||
-- assemble a table of parts concatenated later into Series | |||
if is_set(Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end | |||
if is_set(SeriesNumber) then table.insert(s, wrap_msg ('series', SeriesNumber, use_lowercase)); end | |||
if is_set(Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end | |||
Issue = ''; -- unset because this is not a unique parameter | |||
Chapter = Title; -- promote title parameters to chapter | |||
ChapterLink = TitleLink; -- alias episodelink | |||
TransChapter = TransTitle; | |||
ChapterURL = URL; | |||
ChapterURLorigin = A:ORIGIN('URL'); | |||
Title = Series; -- promote series to title | |||
TitleLink = SeriesLink; | |||
Series = table.concat(s, sepc .. ' '); -- this is concatenation of season, seriesno, episode number | |||
if is_set (ChapterLink) and not is_set (ChapterURL) then -- link but not URL | |||
Chapter = '[[' .. ChapterLink .. '|' .. Chapter .. ']]'; -- ok to wikilink | |||
elseif is_set (ChapterLink) and is_set (ChapterURL) then -- if both are set, URL links episode; | |||
Series = '[[' .. ChapterLink .. '|' .. Series .. ']]'; -- series links with ChapterLink (episodelink -> TitleLink -> ChapterLink) ugly | |||
end | |||
URL = ''; -- unset | |||
TransTitle = ''; -- unset | |||
else -- now oddities that are cite serial | |||
Issue = ''; -- unset because this parameter no longer supported by the citation/core version of cite serial | |||
Chapter = A['Episode']; -- TODO: make |episode= available to cite episode someday? | |||
if is_set (Series) and is_set (SeriesLink) then | |||
Series = '[[' .. SeriesLink .. '|' .. Series .. ']]'; | |||
end | |||
Series = wrap_style ('italic-title', Series); -- series is italicized | |||
end | |||
end | |||
-- end of {{cite episode}} stuff | |||
-- Account for the oddities that are {{cite arxiv}}, before generation of COinS data. | |||
if 'arxiv' == config.CitationClass then | |||
if not is_set (ID_list['ARXIV']) then -- |arxiv= or |eprint= required for cite arxiv | |||
table.insert( z.message_tail, { set_error( 'arxiv_missing', {}, true ) } ); -- add error message | |||
end | |||
if first_set (AccessDate, At, Chapter, Format, Page, Pages, Periodical, PublisherName, URL, -- a crude list of parameters that are not supported by cite arxiv | |||
ID_list['ASIN'], ID_list['BIBCODE'], ID_list['DOI'], ID_list['ISBN'], ID_list['ISSN'], | |||
ID_list['JFM'], ID_list['JSTOR'], ID_list['LCCN'], ID_list['MR'], ID_list['OCLC'], ID_list['OL'], | |||
ID_list['OSTI'], ID_list['PMC'], ID_list['PMID'], ID_list['RFC'], ID_list['SSRN'], ID_list['USENETID'], ID_list['ZBL']) then | |||
table.insert( z.message_tail, { set_error( 'arxiv_params_not_supported', {}, true ) } ); -- add error message | |||
AccessDate= ''; -- set these to empty string; not supported in cite arXiv | |||
PublisherName = ''; -- (if the article has been published, use cite journal, or other) | |||
Chapter = ''; | |||
URL = ''; | |||
Format = ''; | |||
Page = ''; Pages = ''; At = ''; | |||
end | |||
Periodical = 'arXiv'; -- set to arXiv for COinS; after that, must be set to empty string | |||
end | |||
-- handle type parameter for those CS1 citations that have default values | |||
if in_array(config.CitationClass, {"AV-media-notes", "DVD-notes", "mailinglist", "map", "podcast", "pressrelease", "report", "techreport", "thesis"}) then | |||
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 | |||
-- legacy: promote concatenation of |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 | if not is_set (Date) then | ||
Date = Year; -- promote Year to Date | Date = Year; -- promote Year to Date | ||
Line 2,065: | Line 2,323: | ||
do -- create defined block to contain local variables error_message and mismatch | do -- create defined block to contain local variables error_message and mismatch | ||
local error_message = ''; | local error_message = ''; | ||
-- AirDate has been promoted to Date so not necessary to check it | |||
anchor_year, COinS_date, error_message = dates({['accessdate']=AccessDate | anchor_year, COinS_date, error_message = dates({['accessdate']=AccessDate, ['archivedate']=ArchiveDate, ['date']=Date, ['doi_brokendate']=DoiBroken, | ||
['embargo']=Embargo, ['laydate']=LayDate, ['publicationdate']=PublicationDate, ['year']=Year}); | ['embargo']=Embargo, ['laydate']=LayDate, ['publicationdate']=PublicationDate, ['year']=Year}); | ||
Line 2,077: | Line 2,335: | ||
error_message = error_message .. '|year= / |date= mismatch'; | error_message = error_message .. '|year= / |date= mismatch'; | ||
elseif 1 == mismatch then -- |year= matches year-value in |date= | elseif 1 == mismatch then -- |year= matches year-value in |date= | ||
add_maint_cat ('date_year'); | |||
end | end | ||
end | end | ||
Line 2,085: | Line 2,343: | ||
end | end | ||
end -- end of do | end -- end of do | ||
-- Account for the oddity that is {{cite journal}} with |pmc= set and |url= not set. Do this after date check but before COInS. | |||
-- Here we unset Embargo if PMC not embargoed (|embargo= not set in the citation) or if the embargo time has expired. Otherwise, holds embargo date | |||
Embargo = is_embargoed (Embargo); -- | |||
if config.CitationClass == "journal" and not is_set(URL) and is_set(ID_list['PMC']) then | |||
if not is_set (Embargo) then -- if not embargoed or embargo has expired | |||
URL=cfg.id_handlers['PMC'].prefix .. ID_list['PMC']; -- set url to be the same as the PMC external link if not embargoed | |||
URLorigin = cfg.id_handlers['PMC'].parameters[1]; -- set URLorigin to parameter name for use in error message if citation is missing a |title= | |||
end | |||
end | |||
-- 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. | ||
Line 2,096: | Line 2,365: | ||
if 'none' == Title and is_set(Periodical) and not (( config.CitationClass == "encyclopaedia" ) or ( config.CitationClass == "citation" and is_set (Encyclopedia))) then -- special case | if 'none' == Title and is_set(Periodical) and not (( config.CitationClass == "encyclopaedia" ) or ( config.CitationClass == "citation" and is_set (Encyclopedia))) then -- special case | ||
Title = ''; -- set title to empty string | Title = ''; -- set title to empty string | ||
add_maint_cat ('untitled'); | |||
end | end | ||
Line 2,114: | Line 2,383: | ||
-- this is the function call to COinS() | -- this is the function call to COinS() | ||
local OCinSoutput = COinS{ | local OCinSoutput = COinS({ | ||
['Periodical'] = Periodical, | ['Periodical'] = Periodical, | ||
['Chapter'] = strip_apostrophe_markup (coins_chapter), -- Chapter stripped of bold / italic wikimarkup | ['Chapter'] = strip_apostrophe_markup (coins_chapter), -- Chapter stripped of bold / italic wikimarkup | ||
Line 2,123: | Line 2,392: | ||
['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(Sheet, Sheets, Page, Pages, At)), -- pages stripped of external links | ||
['Edition'] = Edition, | ['Edition'] = Edition, | ||
['PublisherName'] = PublisherName, | ['PublisherName'] = PublisherName, | ||
Line 2,130: | Line 2,399: | ||
['ID_list'] = ID_list, | ['ID_list'] = ID_list, | ||
['RawPage'] = this_page.prefixedText, | ['RawPage'] = this_page.prefixedText, | ||
}; | }, config.CitationClass); | ||
-- Account for the oddities that are {{cite arxiv}}, AFTER generation of COinS data. | |||
if 'arxiv' == config.CitationClass then -- we have set rft.jtitle in COinS to arXiv, now unset so it isn't displayed | |||
Periodical = ''; -- periodical not allowed in cite arxiv; if article has been published, use cite journal | |||
end | |||
-- special case for cite newsgroup. Do this after COinS because we are modifying Publishername to include some static text | -- special case for cite newsgroup. Do this after COinS because we are modifying Publishername to include some static text | ||
Line 2,144: | Line 2,418: | ||
-- We also add leading spaces and surrounding markup and punctuation to the | -- We also add leading spaces and surrounding markup and punctuation to the | ||
-- various parts of the citation, but only when they are non-nil. | -- various parts of the citation, but only when they are non-nil. | ||
do -- do-block to limit scope of LastFirstAuthors | |||
local Maximum = | local LastFirstAuthors; | ||
local Maximum = A['DisplayAuthors']; | |||
Maximum , author_etal = get_display_authors_editors (Maximum, #a, 'authors', author_etal); | |||
local control = { | local control = { | ||
Line 2,168: | Line 2,437: | ||
end | end | ||
LastFirstAuthors = list_people(control, a, author_etal); | |||
end | |||
if is_set (Authors) then | |||
if is_set (LastFirstAuthors) then -- if both author name styles | |||
table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'authors') .. ' and ' .. wrap_style ('parameter', 'last')}, true ) } ); -- add error message | |||
Authors = LastFirstAuthors; -- TODO: is this correct or should we use |authors= instead? | |||
end | |||
else | |||
Authors = LastFirstAuthors; -- either an author name list or an empty string | |||
end | |||
end -- end of do | |||
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 2,175: | Line 2,453: | ||
end | end | ||
local EditorCount | local EditorCount; -- used only for choosing {ed.) or (eds.) annotation at end of editor name-list | ||
if not is_set(Editors) then | if not is_set(Editors) then | ||
local Maximum = | local Maximum = A['DisplayEditors']; | ||
Maximum , editor_etal = get_display_authors_editors (Maximum, #e, 'editors', editor_etal); | |||
-- 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, { set_error('implict_etal_editor', {}, true) } ); | table.insert( z.message_tail, { set_error('implict_etal_editor', {}, true) } ); | ||
end | end | ||
Line 2,194: | Line 2,472: | ||
Editors, EditorCount = list_people(control, e, editor_etal); | Editors, EditorCount = list_people(control, e, editor_etal); | ||
if 1 == EditorCount and (true == editor_etal or 1 < #e) then -- only one editor displayed but includes etal then | |||
EditorCount = 2; -- spoof to display (eds.) annotation | |||
end | |||
else | else | ||
EditorCount = 1; | EditorCount = 1; | ||
end | end | ||
-- | -- apply |[xx-]format= styling; at the end, these parameters hold correctly styled format annotation, | ||
-- an error message if the associated url is not set, or an empty string for concatenation | |||
ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url'); | |||
ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url'); | |||
ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url'); | |||
Format = style_format (Format, URL, 'format', 'url'); | |||
LayFormat = style_format (LayFormat, LayURL, 'lay-format', 'lay-url'); | |||
TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl'); | |||
if not is_set(URL) and | if not is_set(URL) then --and | ||
-- not is_set(ArchiveURL) then --and -- prevents format_missing_url error from registering | |||
-- not is_set(ConferenceURL) and -- TODO: keep this here? conference as part of cite web or cite podcast? | |||
-- not is_set(TranscriptURL) then -- TODO: remove? |transcript-url= and |transcript= has separate test | |||
-- Test if cite web or cite podcast |url= is missing or empty | -- Test if cite web or cite podcast |url= is missing or empty | ||
Line 2,233: | Line 2,502: | ||
table.insert( z.message_tail, { set_error( 'accessdate_missing_url', {}, true ) } ); | table.insert( z.message_tail, { set_error( 'accessdate_missing_url', {}, true ) } ); | ||
AccessDate = ''; | AccessDate = ''; | ||
end | end | ||
end | end | ||
local OriginalURL; | local OriginalURL, OriginalFormat; -- TODO: swap chapter and title here so that archive applies to most specific if both are set? | ||
DeadURL = DeadURL:lower(); -- used later when assembling archived text | DeadURL = DeadURL:lower(); -- used later when assembling archived text | ||
if is_set( ArchiveURL ) then | if is_set( ArchiveURL ) then | ||
if is_set (URL) then | if is_set (URL) then | ||
OriginalURL = URL; -- save copy of original source URL | OriginalURL = URL; -- save copy of original source URL | ||
OriginalFormat = Format; -- and original |format= | |||
if 'no' ~= DeadURL then -- if URL set then archive-url applies to it | if 'no' ~= DeadURL then -- if URL set then archive-url applies to it | ||
URL = ArchiveURL -- swap-in the archive's url | URL = ArchiveURL -- swap-in the archive's url | ||
URLorigin = A:ORIGIN('ArchiveURL') -- name of archive url parameter for error messages | URLorigin = A:ORIGIN('ArchiveURL') -- name of archive url parameter for error messages | ||
Format = ArchiveFormat or ''; -- swap in archive's format | |||
end | end | ||
elseif is_set (ChapterURL) then -- URL not set so if chapter-url is set apply archive url to it | elseif is_set (ChapterURL) then -- URL not set so if chapter-url is set apply archive url to it | ||
OriginalURL = ChapterURL; -- save copy of source chapter's url for archive text | OriginalURL = ChapterURL; -- save copy of source chapter's url for archive text | ||
OriginalFormat = ChapterFormat; -- and original |format= | |||
if 'no' ~= DeadURL then | if 'no' ~= DeadURL then | ||
ChapterURL = ArchiveURL -- swap-in the archive's url | ChapterURL = ArchiveURL -- swap-in the archive's url | ||
URLorigin = A:ORIGIN('ArchiveURL') -- name of archive url parameter for error messages | URLorigin = A:ORIGIN('ArchiveURL') -- name of archive url parameter for error messages | ||
ChapterFormat = ArchiveFormat or ''; -- swap in archive's format | |||
end | end | ||
end | end | ||
end | end | ||
if in_array(config.CitationClass, {"web","news","journal","pressrelease","podcast", "newsgroup"}) or | if in_array(config.CitationClass, {"web","news","journal","pressrelease","podcast", "newsgroup", 'arxiv'}) or | ||
('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia)) then | ('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia)) then | ||
if is_set (Chapter) or is_set (TransChapter) or is_set (ChapterURL)then -- chapter parameters not supported for these citation types | if is_set (Chapter) or is_set (TransChapter) or is_set (ChapterURL)then -- chapter parameters not supported for these citation types | ||
Line 2,270: | Line 2,538: | ||
Chapter = format_chapter_title (Chapter, TransChapter, ChapterURL, ChapterURLorigin); | Chapter = format_chapter_title (Chapter, TransChapter, ChapterURL, ChapterURLorigin); | ||
if is_set (Chapter) then | if is_set (Chapter) then | ||
if 'map' == config.CitationClass and is_set (TitleType) then | if 'map' == config.CitationClass and is_set (TitleType) then | ||
Chapter = Chapter .. ' | Chapter = Chapter .. ' ' .. TitleType; | ||
end | end | ||
Chapter = Chapter .. ChapterFormat .. sepc .. ' '; | Chapter = Chapter .. ChapterFormat .. sepc .. ' '; | ||
Line 2,286: | Line 2,550: | ||
end | end | ||
if in_array(config.CitationClass, {"web","news","journal","pressrelease","podcast", "newsgroup", "mailinglist"}) or | if in_array(config.CitationClass, {"web","news","journal","pressrelease","podcast", "newsgroup", "mailinglist", 'arxiv'}) or | ||
('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia)) or | ('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia)) or | ||
('map' == config.CitationClass and is_set (Periodical)) then -- special case for cite map when the map is in a periodical treat as an article | ('map' == config.CitationClass and is_set (Periodical)) then -- special case for cite map when the map is in a periodical treat as an article | ||
Line 2,316: | Line 2,580: | ||
if is_set(Title) then | if is_set(Title) then | ||
if not is_set(TitleLink) and is_set(URL) then | if not is_set(TitleLink) and is_set(URL) then | ||
Title = external_link( URL, Title ) .. TransError .. Format | Title = external_link( URL, Title ) .. TransError .. Format; | ||
URL = ""; | URL = ""; | ||
Format = ""; | Format = ""; | ||
Line 2,327: | Line 2,591: | ||
Place = " " .. wrap_msg ('written', Place, use_lowercase) .. sepc .. " "; | Place = " " .. wrap_msg ('written', Place, use_lowercase) .. sepc .. " "; | ||
end | end | ||
if is_set(Conference) then | if is_set (Conference) then | ||
if is_set(ConferenceURL) then | if is_set (ConferenceURL) then | ||
Conference = external_link( ConferenceURL, Conference ); | Conference = external_link( ConferenceURL, Conference ); | ||
end | end | ||
Conference = sepc .. " " .. Conference | Conference = sepc .. " " .. Conference .. ConferenceFormat; | ||
elseif is_set(ConferenceURL) then | elseif is_set(ConferenceURL) then | ||
Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURLorigin ); | Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURLorigin ); | ||
end | end | ||
if not is_set(Position) then | if not is_set(Position) then | ||
local Minutes = A['Minutes']; | local Minutes = A['Minutes']; | ||
Line 2,378: | Line 2,642: | ||
end | end | ||
end | end | ||
if 'map' == config.CitationClass then -- cite map oddity done after COinS call (and with other in-source locators) | |||
if is_set (Sheet) or is_set (Sheets) then | |||
local err_msg1 = 'sheet=, |sheets'; -- default error message in case any of page pages or at are set | |||
local err_msg2; | |||
if is_set (Page) or is_set (Pages) or is_set (At) then -- are any set? | |||
err_msg2 = 'page=, |pages=, |at'; -- a generic error message | |||
Page = ''; Pages = ''; At = ''; | |||
elseif is_set (Sheet) and is_set (Sheets) then -- if both are set make error message | |||
err_msg1 = 'sheet'; | |||
err_msg2 = 'sheets'; | |||
end | |||
if is_set (err_msg2) then | |||
table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', err_msg1) .. ' and ' .. wrap_style ('parameter', err_msg2)}, true ) } ); -- add error message | |||
end | |||
if not is_set (Sheet) then -- do sheet static text and formatting; Sheet has priority over Sheets if both provided | |||
if is_set (Sheets) then | |||
if is_set (Periodical) then | |||
Sheet = ": Sheets " .. Sheets; -- because Sheet has priority, no need to support both later on | |||
else | |||
Sheet = sepc .. " Sheets " .. Sheets; | |||
end | |||
end | |||
else | |||
if is_set (Periodical) then | |||
Sheet = ": Sheet " .. Sheet; | |||
else | |||
Sheet = sepc .. " Sheet " .. Sheet; | |||
end | |||
end | |||
end | |||
end | |||
At = is_set(At) and (sepc .. " " .. At) or ""; | At = is_set(At) and (sepc .. " " .. At) or ""; | ||
Position = is_set(Position) and (sepc .. " " .. Position) or ""; | Position = is_set(Position) and (sepc .. " " .. Position) or ""; | ||
Line 2,405: | Line 2,701: | ||
Others = is_set(Others) and (sepc .. " " .. Others) or ""; | Others = is_set(Others) and (sepc .. " " .. Others) or ""; | ||
TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""; | TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""; | ||
Line 2,469: | Line 2,752: | ||
end | end | ||
ID_list = build_id_list( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo} ); | ID_list = build_id_list( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo, Class = Class} ); | ||
if is_set(URL) then | if is_set(URL) then | ||
Line 2,492: | Line 2,775: | ||
if sepc ~= "." then arch_text = arch_text:lower() end | if sepc ~= "." then arch_text = arch_text:lower() end | ||
Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'], | Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'], | ||
{ external_link( ArchiveURL, arch_text ), ArchiveDate } ); | { external_link( ArchiveURL, arch_text ) .. ArchiveFormat, ArchiveDate } ); | ||
if not is_set(OriginalURL) then | if not is_set(OriginalURL) then | ||
Archived = Archived .. " " .. set_error('archive_missing_url'); | Archived = Archived .. " " .. set_error('archive_missing_url'); | ||
Line 2,500: | Line 2,783: | ||
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, | ||
{ external_link( OriginalURL, cfg.messages['original'] ), ArchiveDate } ); | { external_link( OriginalURL, cfg.messages['original'] ) .. OriginalFormat, ArchiveDate } ); -- format already styled | ||
else | else | ||
local arch_text = cfg.messages['archived-missing']; | local arch_text = cfg.messages['archived-missing']; | ||
Line 2,507: | Line 2,790: | ||
{ set_error('archive_missing_url'), ArchiveDate } ); | { set_error('archive_missing_url'), ArchiveDate } ); | ||
end | end | ||
elseif is_set (ArchiveFormat) then | |||
Archived = ArchiveFormat; -- if set and ArchiveURL not set ArchiveFormat has error message | |||
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 | ||
Line 2,520: | Line 2,805: | ||
end | end | ||
if sepc == '.' then | if sepc == '.' then | ||
Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary'] ) .. LaySource .. LayDate | Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary'] ) .. LayFormat .. LaySource .. LayDate | ||
else | else | ||
Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower() ) .. LaySource .. LayDate | Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower() ) .. LayFormat .. LaySource .. LayDate | ||
end | end | ||
elseif is_set (LayFormat) then -- Test if |lay-format= is given without giving a |lay-url= | |||
Lay = | Lay = sepc .. LayFormat; -- if set and LayURL not set, then LayFormat has error message | ||
end | end | ||
if is_set(Transcript) then | if is_set(Transcript) then | ||
if is_set(TranscriptURL) then Transcript = external_link( TranscriptURL, Transcript ); end | if is_set(TranscriptURL) then | ||
Transcript = external_link( TranscriptURL, Transcript ); | |||
end | |||
Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat; | |||
elseif is_set(TranscriptURL) then | elseif is_set(TranscriptURL) then | ||
Transcript = external_link( TranscriptURL, nil, TranscriptURLorigin ); | Transcript = external_link( TranscriptURL, nil, TranscriptURLorigin ); | ||
end | end | ||
local Publisher; | local Publisher; | ||
if is_set(Periodical) and | if is_set(Periodical) and | ||
Line 2,616: | Line 2,904: | ||
end | end | ||
elseif 'episode' == config.CitationClass then -- special case for cite episode | |||
tcommon = safe_join( {Title, TitleNote, TitleType, Series, Transcript, Language, Edition, Publisher}, sepc ); | |||
else -- all other CS1 templates | else -- all other CS1 templates | ||
tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, | tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, | ||
Line 2,629: | Line 2,919: | ||
local idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc ); | local idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc ); | ||
local text; | local text; | ||
local pgtext = Position .. Page .. Pages .. At; | local pgtext = Position .. Sheet .. Page .. Pages .. At; | ||
if is_set(Authors) then | if is_set(Authors) then | ||
Line 2,660: | Line 2,950: | ||
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) or (string.sub(Editors,-3,-1) == sepc .. ']]') then -- if last editor name ends with sepc char | ||
Editors = Editors .. " "; -- don't add another | |||
else | |||
Editors = Editors .. sepc .. " " -- otherwise terninate the editor list | |||
end | end | ||
end | end |