local z =
-- Whether variable is set or notfunction is_set(var) return not (var
);end
-- First set variable or nil if nonefunction first_set(...) local list = ; for _, var in pairs(list) do if is_set(var) then return var; end endend
-- Whether needle is in haystackfunction inArray(needle, haystack) if needle
needle then return n; end end return false;end
-- If date is on the ISO 8601 format 'YYYY-MM-DD', we format it as given -- by cfg.messages['dateformat'], with 'j F Y' as default.function formatIsoDate(date) if date:match("^%d%d%d%d%-%d%d%-%d%d$") then local lang = mw.getContentLanguage; return lang:formatDate(cfg.messages['dateformat'] or 'j F Y', date) end return dateend
--day=, |month=,|coauthor=, and |coauthors=) aren't related to each other and because these parameters may be concatenated into the variables used by |date= and |author#= (and aliases)details of which parameter caused the error message are not provided. Only one error message is emitted regardless of the number of deprecated parameters in the citation.function deprecated_parameter if true ~= Page_in_deprecated_cat then -- if we haven't been here before then set a Page_in_deprecated_cat=true; -- sticky flag so that if there are more than one deprecated parameter the category is added only once-- table.insert(z.message_tail,); -- add error message table.insert(z.message_tail,); -- add error message endend
-- Populates numbered arguments in a message string using an argument table.function substitute(msg, args)-- return args and tostring(mw.message.newRawMessage(msg, args)) or msg; return args and mw.message.newRawMessage(msg, args):plain or msg;end
--title= or |chapter= parameter's value.This function will positive kern either single or double quotes: "'Unkerned title with leading and trailing single quote marks'" " 'Kerned title with leading and trailing single quote marks' " (in real life the kerning isn't as wide as this example)function kern_quotes (str) local left='
%1'; -- spacing to use when title contains leading single or double quote mark local right='%1'; -- spacing to use when title contains trailing single or double quote mark if str:match ("^[\"\'][^\']") then str = string.gsub(str, "^[\"\']", left, 1); -- replace (captured) leading single or double quote with left-side end if str:match ("[^\'][\"\']$") then str = string.gsub(str, "[\"\']$", right, 1); -- replace (captured) trailing single or double quote with right-side end return str;end-- Wraps a string using a message_list configuration taking one argumentfunction wrap(key, str, lower) if not is_set(str) then return ""; elseif inArray(key,) then str = safeforitalics(str); end if lower
--Argument wrapper. This function provides support for argument mapping defined in the configuration file so that multiple namescan be transparently aliased to single internal variable.function argument_wrapper(args) local origin = ; return setmetatable;end
--function validate(name) local name = tostring(name); local state = whitelist.basic_arguments[name ]; -- Normal arguments if true
state then deprecated_parameter ; -- parameter is deprecated but still supported return true; end -- Arguments with numbers in them name = name:gsub("%d+", "#"); -- replace digit(s) with # (last25 becomes last# state = whitelist.numbered_arguments[name ]; if true
state then deprecated_parameter ; -- parameter is deprecated but still supported return true; end return false; -- Not supported because not found or name is set to nilend
-- Formats a comment for error trappingfunction errorcomment(content, hidden) return wrap(hidden and 'hidden-error' or 'visible-error', content);end
--Sets an error condition and returns the appropriate error message. The actual placementof the error message in the output is the responsibility of the calling function.function seterror(error_id, arguments, raw, prefix, suffix) local error_state = cfg.error_conditions[error_id ]; prefix = prefix or ""; suffix = suffix or ""; if error_state
true then return message, error_state.hidden; end return errorcomment(message, error_state.hidden);end
-- Formats a wiki style external linkfunction externallinkid(options) local url_string = options.id; if options.encode
nil then url_string = mw.uri.encode(url_string); end return mw.ustring.format('%s%s[%s%s%s %s]', options.link, options.label, options.separator or " ", options.prefix, url_string, options.suffix or "", mw.text.nowiki(options.id) );end
-- Formats a wiki style internal linkfunction internallinkid(options) return mw.ustring.format('%s%s%s', options.link, options.label, options.separator or " ", options.prefix, options.id, options.suffix or "", mw.text.nowiki(options.id) );end
-- Format an external link with error checkingfunction externallink(URL, label, source) local error_str = ""; if not is_set(label) then label = URL; if is_set(source) then error_str = seterror('bare_url_missing_title',, false, " "); else error(cfg.messages["bare_url_no_origin"]); end end if not checkurl(URL) then error_str = seterror('bad_url',, false, " ") .. error_str; end return table.concat;end
-- Formats a link to Amazonfunction amazon(id, domain) if not is_set(domain) then domain = "com" elseif ("jp"
domain) then domain = "co." .. domain end local handler = cfg.id_handlers['ASIN']; return externallinkidend
--
function arxiv (id) local handler = cfg.id_handlers['ARXIV']; local year, month, version; local err_cat = "" year, month, version = id:match("^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d([v%d]*)$"); -- test for the 9108-0703 format if not year then -- arXiv id is not proper 9108-0703 form year, month, version = id:match("^(%d%d)([01]%d)%.%d%d%d%d([v%d]*)$"); -- test for the 0704- format if not year then err_cat = ' ' .. seterror('bad_arxiv'); -- arXiv id doesn't match either format else -- id is the 0704- format year = tonumber(year); month = tonumber(month); if ((7 > year) or (1 > month and 12 < month)) or -- is year invalid or is month invalid? (doesn't test for future years) ((7
version:match("v%d+") then -- is version proper format of single 'v' followed by digits? err_cat = ' ' .. seterror('bad_arxiv'); -- set error message end end else -- id is the 9108-0703 format; are the date values ok year = tonumber(year); month = tonumber(month); if ((91 > year and 7 < year) or (1 > month and 12 < month)) or -- if invalid year or invalid month ((91
year and 3 < month)) or -- if years ok, are starting and ending months ok? is_set (version) and nil
return externallinkid .. err_cat;end
--
function normalize_lccn (lccn) lccn = lccn:gsub ("%s", ""); -- 1. strip whitespace
if nil ~= string.find (lccn,'/') then lccn = lccn:match ("(.-)/"); -- 2. remove forward slash and all character to the right of it end
local prefix local suffix prefix, suffix = lccn:match ("(.+)%-(.+)"); -- 3.a remove hyphen by splitting the string into prefix and suffix
if nil ~= suffix then -- if there was a hyphen suffix=string.rep("0", 6-string.len (suffix)) .. suffix; -- 3.b.2 left fill the suffix with 0s if suffix length less than 6 lccn=prefix..suffix; -- reassemble the lccn end return lccn; end
--function lccn(lccn) local handler = cfg.id_handlers['LCCN']; local err_cat = ; -- presume that LCCN is valid local id = lccn; -- local copy of the lccn
id = normalize_lccn (id); -- get canonical form (no whitespace, hyphens, forward slashes) local len = id:len; -- get the length of the lccn
if 8
len then -- LCCN should be adddddddd if nil
len then -- LCCN should be aadddddddd or dddddddddd if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ... if nil
len then -- LCCN should be aaadddddddd or adddddddddd if not (id:match("^%a%a%a%d%d%d%d%d%d%d%d") or id:match("^%a%d%d%d%d%d%d%d%d%d%d")) then -- see if it matches one of our patterns err_cat = ' ' .. seterror('bad_lccn'); -- no match, set an error message end elseif 12
if not is_set (err_cat) and nil ~= lccn:find ('%s') then err_cat = ' ' .. seterror('bad_lccn'); -- lccn contains a space, set an error message end
return externallinkid .. err_cat;end
--Format PMID and do simple error checking. PMIDs are sequential numbers beginning at 1 and counting up. This code checks the PMID to see that itcontains only digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically as more PMIDs are issued.function pmid(id) local test_limit = 30000000; -- update this value as PMIDs approach local handler = cfg.id_handlers['PMID']; local err_cat = ; -- presume that PMID is valid if id:match("[^%d]") then -- if PMID has anything but digits err_cat = ' ' .. seterror('bad_pmid'); -- set an error message else -- PMID is only digits local id_num = tonumber(id); -- convert id to a number for range testing if 1 > id_num or test_limit < id_num then -- if PMID is outside test limit boundaries err_cat = ' ' .. seterror('bad_pmid'); -- set an error message end end return externallinkid .. err_cat;end
--embargo= against today's date. If embargo date isin the future, returns true; otherwise, returns false because the embargo has expired or |embargo= not set in this cite.function is_embargoed(embargo) if is_set(embargo) then local lang = mw.getContentLanguage; local good1, embargo_date, good2, todays_date; good1, embargo_date = pcall(lang.formatDate, lang, 'U', embargo); good2, todays_date = pcall(lang.formatDate, lang, 'U'); if good1 and good2 and tonumber(embargo_date) >= tonumber(todays_date) then --is embargo date is in the future? return true; -- still embargoed end end return false; -- embargo expired or |embargo= not setend
--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 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.
PMCs are sequential numbers beginning at 1 and counting up. This code checks the PMC to see that it contains only digits and is lessthan test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issued.
function pmc(id, embargo) local test_limit = 5000000; -- update this value as PMCs approach local handler = cfg.id_handlers['PMC']; local err_cat = ; -- presume that PMC is valid local text;if id:match("[^%d]") then -- if PMC has anything but digits err_cat = ' ' .. seterror('bad_pmc'); -- set an error message else -- PMC is only digits local id_num = tonumber(id); -- convert id to a number for range testing if 1 > id_num or test_limit < id_num then -- if PMC is outside test limit boundaries err_cat = ' ' .. seterror('bad_pmc'); -- set an error message end end if is_embargoed(embargo) then text="" .. handler.label .. ":" .. handler.separator .. id .. err_cat; --still embargoed so no external link else text = externallinkid .. err_cat; end return text;end
-- Formats a DOI and checks for DOI errors.
-- DOI names contain two parts: prefix and suffix separated by a forward slash.-- Prefix: directory indicator '10.' followed by a registrant code-- Suffix: character string of any length chosen by the registrant
-- This function checks a DOI name for: prefix/suffix. If the doi name contains spaces or endashes,-- or, if it ends with a period or a comma, this function will emit a bad_doi error message.
-- DOI names are case-insensitive and can incorporate any printable Unicode characters so the test for spaces, endash,-- and terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely if ever used in doi names.
function doi(id, inactive) local cat = "" local handler = cfg.id_handlers['DOI']; local text; if is_set(inactive) then local inactive_year = inactive:match("%d%d%d%d") or ; -- try to get the year portion from the inactive date text = "" .. handler.label .. ":" .. id; if is_set(inactive_year) then table.insert(z.error_categories, "Pages with DOIs inactive since " .. inactive_year); else table.insert(z.error_categories, "Pages with inactive DOIs"); -- when inactive doesn't contain a recognizable year end inactive = " (" .. cfg.messages['inactive'] .. " " .. inactive .. ")" else text = externallinkid inactive = "" end
if nil
-- Formats an OpenLibrary link, and checks for associated errors.function openlibrary(id) local code = id:sub(-1,-1) local handler = cfg.id_handlers['OL']; if (code
"M") then return externallinkid elseif (code
--issn=0819 4327 gives: 4327 0819 4327 -- can't have spaces in an external link This code now prevents that by inserting a hyphen at the issn midpoint. It also validates the issn for length and makes sure that the checkdigit agreeswith the calculated value. Incorrect length (8 digits), characters other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check issnerror message. The issn is always displayed with a hyphen, even if the issn was given as a single group of 8 digits.function issn(id) local issn_copy = id; -- save a copy of unadulterated issn; use this version for display if issn does not validate local handler = cfg.id_handlers['ISSN']; local text; local valid_issn = true;
id=id:gsub("[%s-–]", ""); -- strip spaces, hyphens, and endashes from the issn
if 8 ~= id:len or nil
if true
valid_issn then text = text .. ' ' .. seterror('bad_issn') -- add an error message if the issn is invalid end return textend
--type=
if "AV-media-notes"
cite_class then -- if this citation is cite AV media notes or cite DVD notes return "Media notes"; -- display AV media notes / DVD media notes annotation
elseif "podcast"
elseif "pressrelease"
elseif "techreport"
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
--Determines whether a URL string is valid
At present the only check is whether the string appears to be prefixed with a URI scheme. It is not determined whether the URI scheme is valid or whether the URL is otherwise well formed.
function checkurl(url_str) -- Protocol-relative or URL scheme return url_str:sub(1,2)-- Removes irrelevant text and dashes from ISBN number-- Similar to that used for Special:BookSourcesfunction cleanisbn(isbn_str) return isbn_str:gsub("[^-0-9X]", "");end
-- Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS.--TODO: Fix so this code supports urls like this:-- http://www.history.navy.mil/download/va125153.pdf#page=13 %w/:\.function get_coins_pages (pages) if not is_set (pages) then return pages; end -- if no page numbers then we're done while true do pattern = pages:match("%[(%w*:?//[^ ]+%s+)[%w%d].*%]"); -- pattern is the opening bracket, the url and following space(s): "[url " if nil == pattern then break; end -- no more urls pattern = pattern:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1"); -- pattern is not a literal string; escape lua's magic pattern characters pages = pages:gsub(pattern, ""); -- remove as many instances of pattern as possible end pages = pages:gsub("[%[%]]", ""); -- remove the brackets pages = pages:gsub("–", "-"); -- replace endashes with hyphens pages = pages:gsub("&%w+;", "-"); -- and replace html entities ( - etc.) with hyphens; do we need to replace numerical entities like and the like? return pages;end
--ISBN-10 and ISSN validator code calculates checksum across all isbn/issn digits including the check digit. ISBN-13 is checked in checkisbn.If the number is valid the result will be 0. Before calling this function, issbn/issn must be checked for length and stripped of dashes,spaces and other non-isxn characters.function is_valid_isxn (isxn_str, len) local temp = 0; isxn_str = ; -- make a table of bytes len = len+1; -- adjust to be a loop counter for i, v in ipairs(isxn_str) do -- loop through all of the bytes and calculate the checksum if v
0; -- returns true if calculation result is zeroend
-- Determines whether an ISBN string is validfunction checkisbn(isbn_str) if nil ~= isbn_str:match("[^%s-0-9X]") then return false; end -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X isbn_str = isbn_str:gsub("-", ""):gsub(" ", ""); -- remove hyphens and spaces local len = isbn_str:len; if len ~= 10 and len ~= 13 then return false; end
if len
nil then return false; end return is_valid_isxn(isbn_str, 10); else local temp = 0; if isbn_str:match("^97[89]%d*$")
0; endend
-- Gets the display text for a wikilink like B or B gives Bfunction removewikilink(str) return (str:gsub("%[%[([^%[%]]*)%]%]", function(l) return l:gsub("^[^|]*|(.*)$", "%1"):gsub("^%s*(.-)%s*$", "%1"); end));end
-- Escape sequences for content that will be used for URL descriptionsfunction safeforurl(str) if str:match("%[%[.-%]%]") ~= nil then table.insert(z.message_tail,); end return str:gsub('[%[%]\n]',);end
-- Converts a hyphen to a dashfunction hyphentodash(str) if not is_set(str) or str:match("[%[%]<>]") ~= nil then return str; end return str:gsub('-', '–');end
-- Protects a string that will be wrapped in wiki italic markup ... function safeforitalics(str) -- if not is_set(str) then return str; else if str:sub(1,1)
--title= / |url= combination.This is because by the time we get here, |title=http://somesite.com and |title=Document Title. have been combined: and Document Title.so that now, the last character is not sepc but is (unless sepc
nil then value = ; end if str
'<' then -- Special case of values enclosed in spans and other markup. comp = value:gsub("%b<>", ""); else comp = value; end if comp:sub(1,1)
duplicate_char then str = str:sub(1,-2); elseif end_chr
duplicate_char .. "" then str = str:sub(1, -4) .. ""; elseif str:sub(-5,-1)
duplicate_char .. "]" then trim = true; end elseif end_chr
duplicate_char .. "]]" then trim = true; elseif str:sub(-2,-1)
" " then if str:sub(-2,-1)
if trim then if value ~= comp then local dup2 = duplicate_char; if dup2:match("%A") then dup2 = "%" .. dup2; end value = value:gsub("(%b<>)" .. dup2, "%1", 1) else value = value:sub(2, -1); end end end str = str .. value; end end return str;end
-- Attempts to convert names to initials.function reducetoinitials(first) local initials = local i = 0; -- counter for number of initials for word in string.gmatch(first, "%S+") do table.insert(initials, string.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 end return table.concat(initials) -- Vancouver format does not include spaces.end
-- Formats a list of people (e.g. authors / editors) function listpeople(control, people) local sep = control.sep; local namesep = control.namesep local format = control.format local maximum = control.maximum local lastauthoramp = control.lastauthoramp; local text = local etal = false; if sep:sub(-1,-1) ~= " " then sep = sep .. " " end if maximum ~= nil and maximum < 1 then return "", 0; end for i,person in ipairs(people) do if is_set(person.last) then local mask = person.mask local one local sep_one = sep; if maximum ~= nil and i > maximum then etal = true; break; elseif (mask ~= nil) then local n = tonumber(mask) if (n ~= nil) then one = string.rep(" - ",n) else one = mask; sep_one = " "; end else one = person.last local first = person.first if is_set(first) then if ("vanc"
local count = #text / 2; if count > 0 then if count > 1 and is_set(lastauthoramp) and not etal then text[#text-2] = " & "; end text[#text] = nil; end local result = table.concat(text) -- construct list if etal then local etal_text = cfg.messages['et al']; result = result .. " " .. etal_text; end -- if necessary wrap result in
tag to format in Small Caps if ("scap"-- Generates a CITEREF anchor ID.function anchorid(options) return "CITEREF" .. table.concat(options);end
--lastn= and |firstn= parameters (or their aliases), and their matching link and mask parameters.Stops searching when both |lastn= and |firstn= are not found in args after two sequential attempts: found |last1=, |last2=, and |last3= but doesn'tfind |last4= and |last5= then the search is done.
This function emits an error message when there is a |firstn= without a matching |lastn=. When there are 'holes' in the list of last names, |last1= and |last3=are present but |last2= is missing, an error message is emitted. |lastn= is not required to have a matching |firstn=.
--Original functionfunction extractnames(args, list_name) local names = ; local i = 1; local last; while true do last = selectone(args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i); if not is_set(last) then -- just in case someone passed in an empty parameter break; end names[i] = ; i = i + 1; end return names;end--last= (or alias for authors, |editor-last or alias for editors) local err_msg_list_name = list_name:match ("(%w+)List") .. 's list'; -- modify AuthorList or EditorList for use in error messages if necessary
while true do names[i] = -- search through args for name components beginning at 1 ; if names[i].first and not names[i].last then -- if there is a firstn without a matching lastn names[i].first = nil; -- set first to nil so we don't confuse the implicit et al message code table.insert(z.message_tail,); -- add this error message break; -- and done because lastn not found elseif not names[i].first and not names[i].last then -- if both firstn and lastn aren't found, are we done? count = count + 1; -- number of times we haven't found last and first if 2
count then -- if the previous name was missing table.insert(z.message_tail,); -- add this error message end count = 0; -- reset the counter, we're looking for two consecutive missing names end i = i + 1; -- bump to the next name end return names; -- all done, return our list of namesend
-- Populates ID table from arguments using configuration settingsfunction extractids(args) local id_list = ; for k, v in pairs(cfg.id_handlers) do v = selectone(args, v.parameters, 'redundant_parameters'); if is_set(v) then id_list[k] = v; end end return id_list;end
-- Takes a table of IDs and turns it into a table of formatted ID outputs.function buildidlist(id_list, options) local new_list, handler = ; function fallback(k) return end; for k, v in pairs(id_list) do -- fallback to read-only cfg handler = setmetatable(fallback(k)); if handler.mode
'internal' then table.insert(new_list,); elseif handler.mode ~= 'manual' then error(cfg.messages['unknown_ID_mode']); elseif k
'ARXIV' then table.insert(new_list,); elseif k
'LCCN' then table.insert(new_list,); elseif k
'PMC' then table.insert(new_list,); elseif k
'ISSN' then table.insert(new_list,); elseif k
'1' then for _, v in ipairs(possible) do v = v:gsub("#", ""); if is_set(args[v]) then if value ~= nil and selected ~= v then table.insert(error_list, v); else value = args[v]; selected = v; end end end end for _, v in ipairs(possible) do if index ~= nil then v = v:gsub("#", index); end if is_set(args[v]) then if value ~= nil and selected ~= v then table.insert(error_list, v); else value = args[v]; selected = v; end end end if #error_list > 0 then local error_str = ""; for _, k in ipairs(error_list) do if error_str ~= "" then error_str = error_str .. cfg.messages['parameter-separator'] end error_str = error_str .. wrap('parameter', k); end if #error_list > 1 then error_str = error_str .. cfg.messages['parameter-final-separator']; else error_str = error_str .. cfg.messages['parameter-pair-separator']; end error_str = error_str .. wrap('parameter', selected); table.insert(z.message_tail,); end return value, selected;end
-- COinS metadata (see
'ISBN' then value = cleanisbn(v); else value = v; end if string.sub(id or "", 1, 4)
1 then if is_set(last) then OCinSoutput["rft.aulast"] = last; end if is_set(first) then OCinSoutput["rft.aufirst"] = first; end end if is_set(last) and is_set(first) then OCinSoutput["rft.au"] = table.concat; elseif is_set(last) then OCinSoutput["rft.au"] = last; end end OCinSoutput.rft_id = data.URL; OCinSoutput.rfr_id = table.concat; OCinSoutput = setmetatable(OCinSoutput, nil); -- sort with version string always first, and combine. table.sort(OCinSoutput); table.insert(OCinSoutput, 1, "ctx_ver=" .. ctx_ver); -- such as "Z39.88-2004" return table.concat(OCinSoutput, "&");end
--This is the main function doing the majority of the citationformatting.function citation0(config, args) --Load Input Parameters The argument_wrapper facilitates the mapping of multiple aliases to single internal variable. local A = argument_wrapper(args);
local i local PPrefix = A['PPrefix'] local PPPrefix = A['PPPrefix'] if is_set(A['NoPP']) then PPPrefix = "" PPrefix = "" end -- Pick out the relevant fields from the arguments. Different citation templates -- define different field names for the same underlying things. local Authors = A['Authors']; local a = extractnames(args, 'AuthorList');
local Coauthors = A['Coauthors']; local Others = A['Others']; local Editors = A['Editors']; local e = extractnames(args, 'EditorList');
local Year = A['Year']; local PublicationDate = formatIsoDate(A['PublicationDate']); local OrigYear = A['OrigYear']; local Date = formatIsoDate(A['Date']); local LayDate = formatIsoDate(A['LayDate']); ------------------------------------------------- Get title data local Title = A['Title']; local BookTitle = A['BookTitle']; local Conference = A['Conference']; local TransTitle = A['TransTitle']; local TitleNote = A['TitleNote']; local TitleLink = A['TitleLink']; local Chapter = A['Chapter']; local ChapterLink = A['ChapterLink']; local TransChapter = A['TransChapter']; local TitleType = A['TitleType']; local Degree = A['Degree']; local Docket = A['Docket']; local ArchiveURL = A['ArchiveURL']; local URL = A['URL'] local URLorigin = A:ORIGIN('URL'); local ChapterURL = A['ChapterURL']; local ChapterURLorigin = A:ORIGIN('ChapterURL'); local ConferenceURL = A['ConferenceURL']; local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); local Periodical = A['Periodical'];
local Series = A['Series']; local Volume = A['Volume']; local Issue = A['Issue']; local Position = ; local Page = A['Page']; local Pages = hyphentodash(A['Pages']); local At = A['At'];
local Edition = A['Edition']; local PublicationPlace = A['PublicationPlace'] local Place = A['Place']; local PublisherName = A['PublisherName']; local RegistrationRequired = A['RegistrationRequired']; local SubscriptionRequired = A['SubscriptionRequired']; local Via = A['Via']; local AccessDate = formatIsoDate(A['AccessDate']); local ArchiveDate = formatIsoDate(A['ArchiveDate']); local Agency = A['Agency']; local DeadURL = A['DeadURL'] local Language = A['Language']; local Format = A['Format']; local Ref = A['Ref']; local DoiBroken = A['DoiBroken']; local ID = A['ID']; local ASINTLD = A['ASINTLD']; local IgnoreISBN = A['IgnoreISBN']; local Embargo = A['Embargo'];
local ID_list = extractids(args);--id= for a usenet article or post id -- |id= is not included in COinS so here we convert it to an ID that will be included in COinS if ('newsgroup'
local LayURL = A['LayURL']; local LaySource = A['LaySource']; local Transcript = A['Transcript']; local TranscriptURL = A['TranscriptURL'] local TranscriptURLorigin = A:ORIGIN('TranscriptURL'); local sepc = A['Separator'];
local LastAuthorAmp = A['LastAuthorAmp']; local no_tracking_cats = A['NoTracking'];
--these are used by cite interview local Callsign = A['Callsign']; local City = A['City']; local Cointerviewers = A['Cointerviewers']; -- deprecated local Interviewer = A['Interviewer']; -- deprecated local Program = A['Program'];
--local variables that are not cs1 parameters local page_type; -- is this needed? Doesn't appear to be used anywhere; local use_lowercase = (sepc ~= '.'); local this_page = mw.title.getCurrentTitle; --Also used for COinS and for language local anchor_year; -- used in the CITEREF identifier local COinS_date; -- used in the COinS metadata
-- Set postscript default. if not is_set (PostScript) then -- if |postscript= has not been set (Postscript is nil which is the default for) and if (config.CitationClass ~= "citation") then -- this template is not a citation template PostScript = '.'; -- must be a cite xxx template so set postscript to default (period) end else if PostScript:lower
--check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories. if not is_set(no_tracking_cats) then -- ignore if we are already not going to categorize this page for k, v in pairs(cfg.uncategorized_namespaces) do -- otherwise, spin through the list of namespaces we don't include in error categories if this_page.nsText
-- check for extra |page=, |pages= or |at= parameters. if is_set(Page) then if is_set(Pages) or is_set(At) then Page = Page .. " " .. seterror('extra_pages'); -- add error message Pages = ; -- unset the others At = ; end elseif is_set(Pages) then if is_set(At) then Pages = Pages .. " " .. seterror('extra_pages'); -- add error messages At = ; -- unset end end
-- both |publication-place= and |place= (|location=) allowed if different if not is_set(PublicationPlace) and is_set(Place) then PublicationPlace = Place; -- promote |place= (|location=) to |publication-place end if PublicationPlace
|trans_title maps to |trans_chapter when |title is re-mapped
All other combinations of |encyclopedia, |title, and |article are not modified
if (config.CitationClass"encyclopaedia") then if is_set(Periodical) then -- Periodical is set when |encyclopedia is set if is_set(Title) then if not is_set(Chapter) then Chapter = Title; -- |encyclopedia and |title are set so map |title to |article and |encyclopedia to |title TransChapter = TransTitle; Title = Periodical; Periodical = ; -- redundant so unset TransTitle = ; -- redundant so unset end else -- |title not set Title = Periodical; -- |encyclopedia set and |article set or not set so map |encyclopedia to |title Periodical = ; -- redundant so unset end end end
--special cases for citation. if (config.CitationClass
-- check for special case where |separator=none if 'none'
-- Special case for cite techreport. if (config.CitationClass
|id=
and |number=
'); end end end-- special case for cite interview if (config.CitationClass
if is_set(Interviewer) then if is_set(TitleType) then Others = ' ' .. TitleType .. ' with ' .. Interviewer; TitleType = ; else Others = ' ' .. 'Interview with ' .. Interviewer; end if is_set(Cointerviewers) then Others = Others .. sepc .. ' ' .. Cointerviewers; end else Others = '(Interview)'; end end
--Account for the oddity that is with |pmc= set and |url= not set if config.CitationClass
-- Account for the oddity that is, before generation of COinS data.--TODO: if this is only for, shouldn't we be checking? (if config.CitationClass
-- Account for the oddity that is, before generation of COinS data.---- is not currently supported by this module if config.CitationClass
-- legacy: promote concatenation of |day=, |month=, and |year= to Date if Date not set; or, promote PublicationDate to Date if neither Date nor Year are set. if not is_set(Date) then Date = Year; -- promote Year to Date Year = nil; -- make nil so Year as empty string isn't used for CITEREF if is_set(Date) then local Month = A['Month']; if is_set(Month) then Date = Month .. " " .. Date; local Day = A['Day'] if is_set(Day) then Date = Day .. " " .. Date end end elseif is_set(PublicationDate) then -- use PublicationDate when |date= and |year= are not set Date = PublicationDate; -- promote PublicationDate to Date PublicationDate = ; -- unset, no longer needed end end
if PublicationDate
-- anchor_year, COinS_date, error_message = dates; if is_set(error_message) then table.insert(z.message_tail,); -- add this error message end
-- At this point fields may be nil if they weren't specified in the template use. We can use that fact.
-- COinS metadata (see
if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then Chapter = Title; ChapterLink = TitleLink; TransChapter = TransTitle; Title = ; TitleLink = ; TransTitle = ; end--
-- Now perform various field substitutions. -- We also add leading spaces and surrounding markup and punctuation to the -- various parts of the citation, but only when they are non-nil. if not is_set(Authors) then local Maximum = tonumber(A['DisplayAuthors']); -- Preserve old-style implicit et al. if not is_set(Maximum) and #a
if not is_set(Authors) and is_set(Coauthors) then -- coauthors aren't displayed if one of authors=, authorn=, or lastn= isn't specified table.insert(z.message_tail,); -- emit error message end
local EditorCount if not is_set(Editors) then local Maximum = tonumber(A['DisplayEditors']); -- Preserve old-style implicit et al. if not is_set(Maximum) and #e
local control = ;
Editors, EditorCount = listpeople(control, e); else EditorCount = 1; end
local Cartography = ""; local Scale = ""; if config.CitationClass
'map' then local Section = A['Section']; local Inset = A['Inset']; if first_set(Pages, Page, At) ~= nil or sepc ~= '.' then if is_set(Section) then Section = ", " .. wrap('section', Section, true); end if is_set(Inset) then Inset = ", " .. wrap('inset', Inset, true); end else if is_set(Section) then Section = sepc .. " " .. wrap('section', Section, use_lowercase); if is_set(Inset) then Inset = ", " .. wrap('inset', Inset, true); end elseif is_set(Inset) then Inset = sepc .. " " .. wrap('inset', Inset, use_lowercase); end end At = At .. Section .. Inset; end
--Look in the list of iso639-1 language codes to see if the value provided in the language parameter matches one of them. If a match is found, use that value; if not, then use the value that was provided with the language parameter. Categories are assigned in a manner similar to the templates - categorizes only mainspace citations and only when the language code is not 'en' (English). if is_set (Language) then-- local name = mw.language.fetchLanguageName(Language:lower, "en"); -- experiment: this seems to return correct ISO 639-1 language names local name = cfg.iso639_1[Language:lower]; -- get the language name if Language parameter has a valid iso 639-1 code if nil
this_page.namespace and 'en' ~= Language:lower then --found a match; is this page main / article space and English not the language? Language=" " .. wrap('language', name .. ''); -- in main space and not English: categorize else Language=" " .. wrap('language', name); --not in mainspace or language is English so don't categorize end end else Language=""; -- language not specified so make sure this is an empty string; end
Others = is_set(Others) and (sepc .. " " .. Others) or "";
-- handle type parameter for those CS1 citations that have default values
if inArray(config.CitationClass,) then TitleType = set_titletype (config.CitationClass, TitleType); if is_set(Degree) and "Thesis"
if is_set(TitleType) then -- if type parameter is specified TitleType = " (" .. TitleType .. ")"; -- display it in parentheses end
TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""; Edition = is_set(Edition) and (" " .. wrap('edition', Edition)) or ""; Issue = is_set(Issue) and (" (" .. Issue .. ")") or ""; Series = is_set(Series) and (sepc .. " " .. Series) or ""; OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or ""; Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";
if is_set(Volume) then if (mw.ustring.len(Volume) > 4) then Volume = sepc .." " .. Volume; else Volume = " " .. hyphentodash(Volume) .. ""; end end
--This code commented out while discussion continues until after week of 2014-03-23 live module update; if is_set(Volume) then if (mw.ustring.len(Volume) > 4) then Volume = sepc .. " " .. Volume; else Volume = " " .. hyphentodash(Volume) .. ""; if is_set(Series) then Volume = sepc .. Volume; end end end ------------------------------------ totally unrelated data --Loosely mimic template; Via parameter identifies a delivery source that is not the publisher; these sources often, but not always, exist behind a registration or paywall. So here, we've chosen to decouple via from subscription (via has never been part of the registration required template). Subscription implies paywall; Registration does not. If both are used in a citation, the subscription required link note is displayed. There are no error messages for this condition. if is_set(Via) then Via = " " .. wrap('via', Via); end
if is_set(SubscriptionRequired) then SubscriptionRequired = sepc .. " " .. cfg.messages['subscription']; --here when 'via' parameter not used but 'subscription' is elseif is_set(RegistrationRequired) then SubscriptionRequired = sepc .. " " .. cfg.messages['registration']; --here when 'via' and 'subscription' parameters not used but 'registration' is end
if is_set(AccessDate) then local retrv_text = " " .. cfg.messages['retrieved'] if (sepc ~= ".") then retrv_text = retrv_text:lower end AccessDate = '
' .. sepc .. substitute(retrv_text,) .. '' end if is_set(ID) then ID = sepc .." ".. ID; end if "thesis"ID_list = buildidlist(ID_list,);
if is_set(URL) then URL = " " .. externallink(URL, nil, URLorigin); end
if is_set(Quote) then if Quote:sub(1,1)
'"' then Quote = Quote:sub(2,-2); end Quote = sepc .." " .. wrap('quoted-text', Quote); PostScript = ""; -- CS1 does not supply terminal punctuation when |quote= is set end local Archived if is_set(ArchiveURL) then if not is_set(ArchiveDate) then ArchiveDate = seterror('archive_missing_date'); end if "no"
'.' then Lay = sepc .. " " .. externallink(LayURL, cfg.messages['lay summary']) .. LaySource .. LayDate else Lay = sepc .. " " .. externallink(LayURL, cfg.messages['lay summary']:lower) .. LaySource .. LayDate end else Lay = ""; end if is_set(Transcript) then if is_set(TranscriptURL) then Transcript = externallink(TranscriptURL, Transcript); end elseif is_set(TranscriptURL) then Transcript = externallink(TranscriptURL, nil, TranscriptURLorigin); end local Publisher; if is_set(Periodical) and not inArray(config.CitationClass,) then if is_set(PublisherName) then if is_set(PublicationPlace) then Publisher = PublicationPlace .. ": " .. PublisherName; else Publisher = PublisherName; end elseif is_set(PublicationPlace) then Publisher= PublicationPlace; else Publisher = ""; end if is_set(PublicationDate) then if is_set(Publisher) then Publisher = Publisher .. ", " .. wrap('published', PublicationDate); else Publisher = PublicationDate; end end if is_set(Publisher) then Publisher = " (" .. Publisher .. ")"; end else if is_set(PublicationDate) then PublicationDate = " (" .. wrap('published', PublicationDate) .. ")"; end if is_set(PublisherName) then if is_set(PublicationPlace) then Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate; else Publisher = sepc .. " " .. PublisherName .. PublicationDate; end elseif is_set(PublicationPlace) then Publisher= sepc .. " " .. PublicationPlace .. PublicationDate; else Publisher = PublicationDate; end end -- Several of the above rely upon detecting this as nil, so do it last. if is_set(Periodical) then if is_set(Title) or is_set(TitleNote) then Periodical = sepc .. " " .. wrap('italic-title', Periodical) else Periodical = wrap('italic-title', Periodical) end end
--department=) and forces it to be " (Speech)" so thatthe annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided). if "speech"
-- Piece all bits together at last. Here, all should be non-nil. -- We build things this way because it is more efficient in LUA -- not to keep reassigning to the same string variable over and over.
local tcommon if inArray(config.CitationClass,) and is_set(Periodical) then if is_set(Others) then Others = Others .. sepc .. " " end tcommon = safejoin(sepc); else tcommon = safejoin(sepc); end if #ID_list > 0 then ID_list = safejoin(sepc); else ID_list = ID; end local idcommon = safejoin(sepc); local text; local pgtext = Position .. Page .. Pages .. At; if is_set(Authors) then if is_set(Coauthors) then Authors = Authors .. A['AuthorSeparator'] .. " " .. Coauthors end if is_set(Date) then Date = " ("..Date..")" .. OrigYear .. sepc .. " " elseif string.sub(Authors,-1,-1)
sepc) then Editors = Editors .. " " else Editors = Editors .. sepc .. " " end end text = safejoin(sepc); text = safejoin(sepc); elseif is_set(Editors) then if is_set(Date) then if EditorCount <= 1 then Editors = Editors .. ", " .. cfg.messages['editor']; else Editors = Editors .. ", " .. cfg.messages['editors']; end Date = " (" .. Date ..")" .. OrigYear .. sepc .. " " else if EditorCount <= 1 then Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " " else Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " " end end text = safejoin(sepc); text = safejoin(sepc); else if is_set(Date) then if (string.sub(tcommon,-1,-1) ~= sepc) then Date = sepc .." " .. Date .. OrigYear else Date = " " .. Date .. OrigYear end end if config.CitationClass
-- Now enclose the whole thing in a
element local options = ; if is_set(config.CitationClass) and config.CitationClass ~= "citation" then options.class = "citation " .. config.CitationClass; else options.class = "citation"; end if is_set(Ref) and Ref:lower ~= "none" then local id = Ref if ("harv"4 then break end end elseif #e > 0 then for i,v in ipairs(e) do names[i] = v.last if i
local empty_span = '
'; -- Note: Using display: none on then COinS span breaks some clients. local OCinS = '' .. empty_span .. ''; text = text .. OCinS; if #z.message_tail ~= 0 then text = text .. " "; for i,v in ipairs(z.message_tail) do if is_set(v[1]) then if i-- This is used by templates such as Book: to create the actual citation text.function z.citation(frame) local pframe = frame:getParent if nil ~= string.find(frame:getTitle, 'sandbox', 1, true) then -- did the use sandbox version? cfg = mw.loadData('Module:Citation/CS1/Configuration/sandbox'); -- load sandbox versions of Configuration and Whitelist and ... whitelist = mw.loadData('Module:Citation/CS1/Whitelist/sandbox'); dates = require('Module:Citation/CS1/Date_validation/sandbox').dates -- ... sandbox version of date validation code else -- otherwise cfg = mw.loadData('Module:Citation/CS1/Configuration'); -- load live versions of Configuration and Whitelist and ... whitelist = mw.loadData('Module:Citation/CS1/Whitelist'); dates = require('Module:Citation/CS1/Date_validation').dates -- ... live version of date validation code end local args = ; local suggestions = ; local error_text, error_state;
local config = ; for k, v in pairs(frame.args) do config[k] = v; args[k] = v; end
for k, v in pairs(pframe.args) do if v ~= then if not validate(k) then error_text = ""; if type(k) ~= 'string' then -- Exclude empty numbered parameters if v:match("%S+") ~= nil then error_text, error_state = seterror('text_ignored',, true); end elseif validate(k:lower) then error_text, error_state = seterror('parameter_ignored_suggest',, true); else if #suggestions
'postscript') then args[k] = v; end end return citation0(config, args)end
return z