]]
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 >------------------------------------------------------------------
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
-- 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
table.insertadd_prop_cat ( z.properties_cats, 'CS1 uses script_with_name' .. , {name .. '-language script ('.., lang..')'}); -- categorize in language-specific categories
else
table.insertadd_prop_cat ( z.properties_cats, 'CS1 uses foreign language script'); -- use this category as a catchall until language-specific category is available
end
lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute
end
end
--[[--------------------------< S E L E C T _ O N E >----------------------------------------------------------
can be transparently aliased to single internal variable.
]]
local function argument_wrapper( args )
local origin = {};
nil - unsupported parameters
]]
local function validate( name )
local name = tostring( name );
local function is_valid_isxn (isxn_str, len)
local temp = 0;
isxn_str = { isxn_str:byte(1, len) }; -- make a table of bytesbyte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58
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(compares the byte value of 'X' which is 0x58)
temp = temp + 10*( len - i ); -- it represents 10 decimal
else
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
table.insertadd_maint_cat ( z.maintenance_cats, 'CS1 maint: ASIN uses ISBN'); -- add to maint category
elseif not is_set (err_cat) then
err_cat = ' ' .. set_error ('bad_asin'); -- asin is not isbn10
]]
local function arxiv (id, class)
local handler = cfg.id_handlers['ARXIV'];
local year, month, version;
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
end
return text = external_link_id({link = handler.link, label = handler.label,
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat;
end
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
--[[
]]
local function lccn(lccn)
local handler = cfg.id_handlers['LCCN'];
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 test_limit = 30000000; -- update this value as PMIDs approach
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
in the future, returns truethe content of |embargo=; otherwise, returns false 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 lang = mw.getContentLanguage();
local good1, embargo_date, good2, todays_date;
good2, todays_date = pcall( lang.formatDate, lang, 'U' );
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 trueembargo; -- still embargoed else add_maint_cat ('embargo') return ''; -- unset because embargo has expired end
end
end
return false''; -- embargo expired or |embargo= not setreturn empty string
end
--[[--------------------------< P M C >------------------------------------------------------------------------
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 futurethe PMC identifier will not be linked to the article. If the embargo specifies a date is today or in the past, or if it is empty or omitted, thenthe 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 citationhas |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
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 test_limit = 5000000; -- update this value as PMCs approach
end
if is_embargoedis_set (embargo) then -- is PMC is still embargoed? text="[[" .. handler.link .. "|" .. handler.label .. "]]:" .. handler.separator .. id .. err_cat; --still embargoed so no external link
else
text = external_link_id({link = handler.link, label = handler.label, --no embargo dateor embargo has expired, ok to link to article
prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) .. err_cat;
end
return text
end
--[[--------------------------< 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 citations templates that have defaults.
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)
if is_set(title_type) then
if "none" == title_type then
title_type = ""; -- if |type=none then type parameter not displayed
end
return title_type; -- if |type= has been set to any other value use that value
end
if "AV-media-notes" == return cfg.title_types [cite_class ] or "DVD''; -notes" == cite_class then -- if this citation is cite AV media notes or cite DVD notesset template's default title type; else empty string for concatenation return "Media notes"; -- display AV media notes / DVD media notes annotationend
elseif "mailinglist" == cite_class then -- if this citation is cite mailing list return "Mailing list"; [[--------------------------< C L E A N _ I S B N >---------------------------------------------------------- display mailing list annotation
elseif "map" == cite_class then -- if this citation is cite mapRemoves irrelevant text and dashes from ISBN number return "Map"; -- display map annotationSimilar to that used for Special:BookSources
elseif "podcast" == cite_class then -- if this citation is cite podcast return "Podcast"; -- display podcast annotation elseif "pressrelease" == cite_class then -- if this citation is cite press release return "Press release"; -- display press release annotation elseif "report" == cite_class then -- if this citation is cite report return "Report"; -- display report 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) endend]]
-- Removes irrelevant text and dashes from ISBN number
-- Similar to that used for Special:BookSources
local function clean_isbn( isbn_str )
return isbn_str:gsub( "[^-0-9X]", "" );
]]
local function strip_apostrophe_markup (argument)
if not is_set (argument) then return argument; end
]]
local function get_coins_pages (pages)
local pattern;
]]
local function safe_join( tbl, duplicate_char )
--[[
return str;
end
--[[--------------------------< I S _ G O O D _ V A N C _ N A M E >--------------------------------------------
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.
These things are not currently possible in this module so are left to the editor to do. This module cantest 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, however00D8–00F6, check00F8–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 the content of |lastn= also allowed to contain hyphens, spaces, and apostrophes. (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)|firstn= also allowed to see 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 names contain non-Latin (non-ASCII) characters code editor and emit an error messagepast it herewhen such characters are locatedbecause the code editor gets confused between character insertion point and cursor position.
Allow |lastn= to contain ASCII characters, hyphens, spaces, and apostrophes. (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
Allow |firstn= to contain ASCII characters, hyphens, spaces, apostrophes, and periods
]]
local function is_good_vanc_name (last, first)
if last:nil == mw.ustring.find (last, "[^%a[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%'%s%']*$") or first:nil == mw.ustring.find (first, "[^%a[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
Page_in_vanc_error_cat=true; -- so that if there are more than one error the category is added only once
return true;
end
--[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------
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/.
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)
if first:mw.ustring.match(first, "^%u%u$") then return first end; -- when first contains just two upper-case letters, nothing to do
local initials = {}
local i = 0; -- counter for number of initials
for word in stringmw.ustring.gmatch(first, "[^%s%.%-]+") do -- names separated by spaces, hyphens, or periods table.insert(initials, stringmw.ustring.sub(word,1,1)) -- Vancouver format does not include full stops.
i = i + 1; -- bump the counter
if 2 <= i then break; end -- only two initials allowed in Vancouver system; if 2, quit
--[[--------------------------< L I S T _ P E O P L E >-------------------------------------------------------
Formats a list of people (e.g. authors / editors)
]]
local function list_people(control, people, etal)
local sep;
if sep:sub(-1,-1) ~= " " then sep = sep .. " " end
if is_set (maximum ~= nil ) and maximum < 1 then return "", 0; end -- returned 0 is for EditorCount; not used for authors
for i,person in ipairs(people) do
local one
local sep_one = sep;
if is_set (maximum ~= nil ) and i > maximum then
etal = true;
break;
--[[--------------------------< 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.
if true == etal then
table.insertadd_maint_cat ( z.maintenance_cats, 'CS1 maint: Explicit use of et al.etal'); -- add to maint category
end
return names, etal; -- all done, return our list of names
--[[--------------------------< 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.
]]
local function build_id_list( id_list, options )
local new_list, handler = {};
table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } );
elseif k == 'ARXIV' then
table.insert( new_list, {handler.label, arxiv( v , options.Class ) } );
elseif k == 'ASIN' then
table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } );
-- COinS metadata (see <http://ocoins.info/>) allows automated tools to parse
-- the citation information.
local function COinS(data, class)
if 'table' ~= type(data) or nil == next(data) then
return '';
elseif is_set(data.Periodical) then
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal";
if 'arxiv' == class then OCinSoutput["rft.genre"] = "preprint"; -- cite arxiv else OCinSoutput["rft.genre"] = "article"; end
OCinSoutput["rft.jtitle"] = data.Periodical;
OCinSoutput["rft.atitle"] = data.Title;
Adapted from code taken from Module:Check ISO 639-1.
]]
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.
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 code; -- the ISO639-1 two character code local name; -- the language name local testlanguage_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 0 == namespace and (('en' == lang:lower()) or ('english' == lang:lower())) then table.insert (z.maintenance_catslanguage_list, name); name = 'CS1 maint: English language specified'); -- add maintenance category if |language=English or |language=en in article spaceso we can reuse it
end
code = #language_list -- reuse code as number of languages in the list if 2 >=code then name = lang:lentable.concat (language_list, ' and ') then -- ISO639-1 insert '<space>and<space>' between two language code are names elseif 2 characters (fetchLanguageName also supports 3 character codes)< code then name language_list[code] = mw'and ' .language.fetchLanguageNamelanguage_list[code]; -- prepend last name with 'and<space>' name = table.concat ( lang:lower()language_list, "en" ', '); - - get ISO 639-1 language name if Language is a proper codeand concatenate with '<comma><space>' separators
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 ]] local function set_cs1_style (ps) if not is_set (nameps) then -- if Language specified a valid ISO639-1 codeunless explicitely set to something code ps = 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)terminate the rendered citation with a period
end
return '.', ps; -- separator is a full stop
end
--[[--------------------------< 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 (codeps) then -- if |postscript= has not been set, set cs2 default if 'no' == code then name ps = 'Norwegian' end; -- override wikimedia when code is 'nomake sure it isn't nil end if 0 == namespace and 'en' ~= code then -- is this page main / article space and English not the language? table.insertis_set ( z.properties_cats, 'CS1 ' .. name .. '-language sources (' .. code .. 'ref)'); then -- in main space and if |ref= is not English: categorizeset end else table.insert (z.maintenance_cats, 'CS1 maint: Unrecognized language')ref = "harv"; -- add maintenance category when set default |languageref= does not appear to be ISO 639-1 languageharv
end
return (" " .. wrap_msg ('language,', name))ps, ref; -- wrap with '(in ...)'separator is a comma
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 >----------------------
When |mode= is not set or when its value is invalid, use config.CitationClass and parameter values to establish
rendered style.
local function get_settings_from_cite_class (ps, ref, cite_class)
local sep;
if (cite_class == "citation") then -- for citation templates (CS2)
sep = ','; -- set citation separator to its default (comma) if not is_set (ps) then -- if |postscript= has not been set, set cs2 default ps ref = ''; -- make sure it isn't nil end if not is_set set_cs2_style (ps, ref) then -- if |ref= is not set ref = "harv"; -- set default |ref=harv end
else -- not a citation template so CS1
sep , ps = '.'; -- set cite xxx separator to its default (period) if not is_set set_cs1_style (ps) then -- if |postscript= has not been set ps = '.'; -- set cs1 default end
end
local function set_style (mode, ps, ref, cite_class)
local sep; if is_set (mode) then if 'cs2' == mode then -- if this template is to be rendered in CS2 (citation) style sep = ','; -- separate elements with a comma if not is_set (ps) then -- unless explicitely set to something ps , ref = ''; -- make sure it isn't nil end if not is_set set_cs2_style (ps, ref) then -- unless explicitely set to something ref = 'harv'; -- set so this template renders with CITEREF anchor id end elseif 'cs1' == mode then -- if this template is to be rendered in CS1 (cite xxx) style sep , ps = '.'; -- separate elements with a period if not is_set set_cs1_style (ps) then -- unless explicitely set to something ps = '.'; -- terminate the rendered citation with a period end 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
sep, ps, ref = get_settings_from_cite_class (ps, ref, cite_class); -- get settings based on the template's CitationClass
end
else -- when |mode= empty or omitted
sep, ps, ref = get_settings_from_cite_class (ps, ref, cite_class); -- get settings based on the template's CitationClass
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 >------------------------------------------------------------
This is the main function doing the majority of the citation
formatting.
]]
local function citation0( config, args)
--[[
local Degree = A['Degree'];
local Docket = A['Docket'];
local ArchiveFormat = A['ArchiveFormat'];
local ArchiveURL = A['ArchiveURL'];
local URL = A['URL']
local ChapterURL = A['ChapterURL'];
local ChapterURLorigin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL
local ConferenceFormat = A['ConferenceFormat'];
local ConferenceURL = A['ConferenceURL'];
local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL
local IgnoreISBN = A['IgnoreISBN'];
local Embargo = A['Embargo'];
local Class = A['Class']; -- arxiv class identifier
local ID_list = extract_ids( args );
local Quote = A['Quote'];
local LayFormat = A['LayFormat'];
local LayURL = A['LayURL'];
local LaySource = A['LaySource'];
local Transcript = A['Transcript'];
local TranscriptFormat = A['TranscriptFormat'];
local TranscriptURL = A['TranscriptURL']
local TranscriptURLorigin = A:ORIGIN('TranscriptURL'); -- get name of parameter that holds TranscriptURL
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. 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
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
if (config.CitationClass == "mailinglist") then
Periodical = A ['MailingList'];
end
--Account for the oddity that is {{cite journal}} with |pmc= set and |url= not set
if config.CitationClass == "journal" and not is_set(URL) and is_set(ID_list['PMC']) then
if not is_embargoed(Embargo) then
URL=cfg.id_handlers['PMC'].prefix .. ID_list['PMC']; -- set url to be the same as the PMC external link if not embargoed
URLorigin = cfg.id_handlers['PMC'].parameters[1]; -- set URLorigin to parameter name for use in error message if citation is missing a |title=
end
end
URL = '';
end
elseelseif 'speech' ~= config.CitationClass then 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 -- Account for the oddity oddities that is are {{cite episode}} and {{cite serial}}, before generation of COinS data.--[[ -- {{cite episode}} is not currently supported by this module if 'episode' == config.CitationClass or 'serial' == "episode" config.CitationClass then
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 Season = A['Season'];
local SeriesNumber = A['SeriesNumber'];
local Network = A['Network'];
local Station = A['Station'];
local s, n = {}, {};
local Sep = (first_set(A["SeriesSeparator"], A["Separator"]) or "") .. " "; if is_set(Issue) then table.insert(s, cfg.messages["episode"] .. " " .. Issue); Issue = ''; end if is_set(Season) then table.insert(s, cfg.messages["season"] .. " " .. Season); end if is_set(SeriesNumber) then table.insert(s, cfg.messages["series"] .. " " .. SeriesNumber); end -- do common parameters first
if is_set(Network) then table.insert(n, Network); end
if is_set(Station) then table.insert(n, Station); end
ID = table.concat(n, sepc .. ' ');
if not is_set (Date = 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']; 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 TransTitle -- 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 = table'[[' .concat. SeriesLink .. '|' .. Series .. ']]'; end Series = wrap_style (s'italic-title', SepSeries); -- series is italicized ID 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.concatinsert(nz.message_tail, Sep{ 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
-- end of {{cite episode}} stuff]]
-- 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.
do -- create defined block to contain local variables error_message and mismatch
local error_message = '';
-- AirDate has been promoted to Date so not necessary to check it anchor_year, COinS_date, error_message = dates({['accessdate']=AccessDate, ['airdate']=AirDate, ['archivedate']=ArchiveDate, ['date']=Date, ['doi_brokendate']=DoiBroken,
['embargo']=Embargo, ['laydate']=LayDate, ['publicationdate']=PublicationDate, ['year']=Year});
error_message = error_message .. '|year= / |date= mismatch';
elseif 1 == mismatch then -- |year= matches year-value in |date=
table.insertadd_maint_cat ( z.maintenance_cats, 'CS1 maint: Date and yeardate_year'); -- add to maint category
end
end
end
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.
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
table.insertadd_maint_cat ( z.maintenance_cats, 'CS1 maint: Untitled periodicaluntitled'); -- add to maint category
end
-- this is the function call to COinS()
local OCinSoutput = COinS({
['Periodical'] = Periodical,
['Chapter'] = strip_apostrophe_markup (coins_chapter), -- Chapter stripped of bold / italic wikimarkup
['Volume'] = Volume,
['Issue'] = Issue,
['Pages'] = get_coins_pages (first_set(Sheet, Sheets, Page, Pages, At)), -- pages stripped of external links
['Edition'] = Edition,
['PublisherName'] = PublisherName,
['ID_list'] = ID_list,
['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
-- 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) thendo -- do-block to limit scope of LastFirstAuthors local LastFirstAuthors; local Maximum = tonumber( A['DisplayAuthors'] );
if is_set Maximum , author_etal = get_display_authors_editors (Maximum) then if Maximum >= , #a then -- if display-authors value greater than or equal to number of authors table.insert( z.maintenance_cats, 'CS1 maint: display-authors', author_etal); -- add maintenance category because display-authors parameter may be removed end else Maximum = #a + 1; -- number of authors + 1 end
local control = {
end
Authors LastFirstAuthors = list_people(control, a, author_etal) ; 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 -- endof 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
end
local EditorCount; -- used only for choosing {ed.) or (eds.) annotation at end of editor name-list
if not is_set(Editors) then
local Maximum = tonumber( A['DisplayEditors'] ; Maximum , editor_etal = get_display_authors_editors (Maximum, #e, 'editors', editor_etal);
-- Preserve old-style implicit et al.
if not is_set(Maximum) and #e == 4 then
Maximum = 3;
table.insert( z.message_tail, { set_error('implict_etal_editor', {}, true) } );
elseif not is_set(Maximum) then
Maximum = #e + 1;
end
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
EditorCount = 1;
end
-- cite map oddities local Cartography apply |[xx-]format= ""styling;at the end, these parameters hold correctly styled format annotation, local Scale = "";-- an error message if the associated url is not set, or an empty string for concatenation if config.CitationClass ArchiveFormat == "map" then Chapter = A[style_format (ArchiveFormat, ArchiveURL, 'Maparchive-format']; ChapterURL = A[, 'MapURLarchive-url']); ChapterURLorigin ChapterFormat = A:ORIGINstyle_format (ChapterFormat, ChapterURL, 'MapURLchapter-format', 'chapter-url'); ChapterFormat ConferenceFormat = A[style_format (ConferenceFormat, ConferenceURL, 'MapFormatconference-format']; Cartography = A[, 'Cartographyconference-url']); if is_set( Cartography ) then Cartography Format = sepc .. " " .. wrap_msg style_format (Format, URL, 'cartographyformat', Cartography, use_lowercase'url'); end Scale LayFormat = A[style_format (LayFormat, LayURL, 'Scalelay-format', 'lay-url']; if is_set( Scale ) then Scale = sepc .. " " .. Scale; end end Format TranscriptFormat = is_setstyle_format (FormatTranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl') and " (" .. Format .. ")" or "";
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
table.insert( z.message_tail, { set_error( 'accessdate_missing_url', {}, true ) } );
AccessDate = '';
end
-- Test if format is given without giving a URL
if is_set(Format) then
Format = Format .. set_error( 'format_missing_url', {'format', 'url'} );
end
end
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
if is_set( ArchiveURL ) then
if is_set (URL) then
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
URL = ArchiveURL -- swap-in the archive's url
URLorigin = A:ORIGIN('ArchiveURL') -- name of archive url parameter for error messages
Format = ArchiveFormat or ''; -- swap in archive's format
end
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
OriginalFormat = ChapterFormat; -- and original |format=
if 'no' ~= DeadURL then
ChapterURL = ArchiveURL -- swap-in the archive's url
URLorigin = A:ORIGIN('ArchiveURL') -- name of archive url parameter for error messages
ChapterFormat = ArchiveFormat or ''; -- swap in archive's format
end
end
end
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
if is_set (Chapter) or is_set (TransChapter) or is_set (ChapterURL)then -- chapter parameters not supported for these citation types
Chapter = format_chapter_title (Chapter, TransChapter, ChapterURL, ChapterURLorigin);
if is_set (Chapter) then
ChapterFormat = is_set(ChapterFormat) and " (" .. ChapterFormat .. ")" or "";
if is_set(ChapterFormat) and not is_set (ChapterURL) then -- Test if |chapter-format= is given without giving a |chapter-url=
ChapterFormat = ChapterFormat .. set_error( 'format_missing_url', {'chapter-format', 'chapter-url'} );
end
if 'map' == config.CitationClass and is_set (TitleType) then
Chapter = Chapter .. ' (' .. TitleType .. ')';
end
Chapter = Chapter .. ChapterFormat .. sepc .. ' ';
end
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
('map' == config.CitationClass and is_set (Periodical)) then -- special case for cite map when the map is in a periodical treat as an article
if is_set(Title) then
if not is_set(TitleLink) and is_set(URL) then
Title = external_link( URL, Title ) .. TransError .. Format ;
URL = "";
Format = "";
Place = " " .. wrap_msg ('written', Place, use_lowercase) .. sepc .. " ";
end
if is_set(Conference) then if is_set(ConferenceURL) then
Conference = external_link( ConferenceURL, Conference );
end
Conference = sepc .. " " .. Conference.. ConferenceFormat;
elseif is_set(ConferenceURL) then
Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURLorigin );
end
if not is_set(Position) then
local Minutes = A['Minutes'];
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 "";
Position = is_set(Position) and (sepc .. " " .. Position) or "";
Others = is_set(Others) and (sepc .. " " .. Others) or "";
-- 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
TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";
end
ID_list = build_id_list( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN, Embargo=Embargo, Class = Class} );
if is_set(URL) then
if sepc ~= "." then arch_text = arch_text:lower() end
Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'],
{ external_link( ArchiveURL, arch_text ).. ArchiveFormat, ArchiveDate } );
if not is_set(OriginalURL) then
Archived = Archived .. " " .. set_error('archive_missing_url');
if sepc ~= "." then arch_text = arch_text:lower() end
Archived = sepc .. " " .. substitute( arch_text,
{ external_link( OriginalURL, cfg.messages['original'] ).. OriginalFormat, ArchiveDate } );-- format already styled
else
local arch_text = cfg.messages['archived-missing'];
{ set_error('archive_missing_url'), ArchiveDate } );
end
elseif is_set (ArchiveFormat) then
Archived = ArchiveFormat; -- if set and ArchiveURL not set ArchiveFormat has error message
else
Archived = ""
end
local Lay= '';
if is_set(LayURL) then
if is_set(LayDate) then LayDate = " (" .. LayDate .. ")" end
end
if sepc == '.' then
Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary'] ) .. LayFormat .. LaySource .. LayDate
else
Lay = sepc .. " " .. external_link( LayURL, cfg.messages['lay summary']:lower() ) .. LayFormat .. LaySource .. LayDate
end
elseelseif is_set (LayFormat) then -- Test if |lay-format= is given without giving a |lay-url= Lay = ""sepc .. LayFormat; -- if set and LayURL not set, then LayFormat has error message
end
if is_set(Transcript) then
if is_set(TranscriptURL) then Transcript = external_link( TranscriptURL, Transcript ); end Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat;
elseif is_set(TranscriptURL) then
Transcript = external_link( TranscriptURL, nil, TranscriptURLorigin );
end
local Publisher;
if is_set(Periodical) and
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
tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language,
local idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc );
local text;
local pgtext = Position .. Sheet .. Page .. Pages .. At;
if is_set(Authors) then
if (sepc ~= '.') then in_text = in_text:lower() end
Editors = in_text .. Editors .. post_text;
if (string.sub(Editors,-1,-1) == sepc)or (string.sub(Editors,-3,-1) == sepc .. ']]') then -- if last editor name ends with sepc char then Editors = Editors .. " "; -- don't add another else Editors = Editors .. sepc .. " " -- otherwise terninate the editor list
end
end