Module:Sandbox/Danmichaelo Explained

local z =

-- Whether variable is set or notfunction is_set(var) return not (var

nil or 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

nil then return false; end for n,v in ipairs(haystack) do if v

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

true then return substitute(cfg.messages[key]:lower,); else return substitute(cfg.messages[key],); end end

--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 return true; end -- valid actively supported parameter if false

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 return true; end -- valid actively supported parameter if false

state then deprecated_parameter ; -- parameter is deprecated but still supported return true; end return false; -- Not supported because not found or name is set to 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

nil then error(cfg.messages['undefined_error']); elseif is_set(error_state.category) then table.insert(z.error_categories, error_state.category); end local message = substitute(error_state.message, arguments); message = message .. " (" .. cfg.messages['help page label'] .. ")"; z.error_ids[error_id ] = true; if inArray(error_id,) and z.error_ids['citation_missing_title'] then return , false; end message = table.concat; if raw

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

true or 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 or "uk"

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

year) and (4 > month)) or -- when year is 07, is month invalid (before April)? is_set (version) and nil

version:match("v%d+") then -- is version proper format of single 'v' followed by digits? err_cat = ' ' .. seterror('bad_arxiv'); -- set error message 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 8 > month) or (7

year and 3 < month)) or -- if years ok, are starting and ending months ok? is_set (version) and nil

version:match("v%d+") then -- is version proper format of single 'v' followed by digits? err_cat = ' ' .. seterror('bad_arxiv'); -- set error message end end

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 if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) err_cat = ' ' .. seterror('bad_lccn'); -- set an error message end elseif 9

len then -- LCCN should be adddddddd if nil

id:match("%a%d%d%d%d%d%d%d%d") then -- does it match our pattern? err_cat = ' ' .. seterror('bad_lccn'); -- set an error message end elseif 10

len then -- LCCN should be aadddddddd or dddddddddd if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ... if nil

id:match("^%a%a%d%d%d%d%d%d%d%d") then -- ... see if it matches our pattern err_cat = ' ' .. seterror('bad_lccn'); -- no match, set an error message end end elseif 11

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

len then -- LCCN should be aadddddddddd if not id:match("^%a%a%d%d%d%d%d%d%d%d%d%d") then -- see if it matches our pattern err_cat = ' ' .. seterror('bad_lccn'); -- no match, set an error message end else err_cat = ' ' .. seterror('bad_lccn'); -- wrong length, set an error message end

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

id:match("^10%.[^%s–]-/[^%s–]-[^%.,]$") then -- doi must begin with '10.', must contain a fwd slash, must not contain spaces or endashes, and must not end with period or comma cat = ' ' .. seterror('bad_doi'); end return text .. inactive .. cat end

-- 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

"A") then return externallinkid elseif (code

"M") then return externallinkid elseif (code

"W") then return externallinkid else return externallinkid .. ' ' .. seterror('bad_ol'); endend

--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

id:match("^%d*X?$") then -- validate the issn: 8 digits long, containing only 0-9 or X in the last position valid_issn=false; -- wrong length or improper character else valid_issn=is_valid_isxn(id, 8); -- validate issn end

if true

valid_issn then id = string.sub(id, 1, 4) .. "-" .. string.sub(id, 5); -- if valid, display correctly formatted version else id = issn_copy; -- if not valid, use the show the invalid issn with error message end text = externallinkid if false

valid_issn then text = text .. ' ' .. seterror('bad_issn') -- add an error message if the issn is invalid end return textend

--type=) for those citations that have defaults.Also handles the special case where it is desirable to omit the title type from the rendered citation (|type=none).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"

cite_class or "DVD-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"

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 "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

--Determines whether a URL string is valid

At present the only check is whether the string appears to be prefixed with a URI scheme. It is not determined whether the URI scheme is valid or whether the URL is otherwise well formed.

function checkurl(url_str) -- Protocol-relative or URL scheme return url_str:sub(1,2)

"//" or url_str:match("^[^/]*:") ~= nil;end

-- Removes irrelevant text and dashes from ISBN number-- Similar to that used for Special: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

string.byte("X") then -- if checkdigit is X temp = temp + 10*(len - i); -- it represents 10 decimal else temp = temp + tonumber(string.char(v))*(len-i); end end return temp % 11

0; -- returns true if calculation result is 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

10 then if isbn_str:match("^%d*X?$")

nil then return false; end return is_valid_isxn(isbn_str, 10); else local temp = 0; if isbn_str:match("^97[89]%d*$")

nil then return false; end -- isbn13 begins with 978 or 979 isbn_str = ; for i, v in ipairs(isbn_str) do temp = temp + (3 - 2*(i % 2)) * tonumber(string.char(v)); end return temp % 10

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)

"'" then str = "

" .. str; end if str:sub(-1,-1)

"'" then str = str .. "

"; end -- Remove newlines as they break italics. return str:gsub('\n', ' '); endend

--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

']' which breaks the external link)]]function safejoin(tbl, duplicate_char) -- local str = ; local comp = ; local end_chr = ; local trim; for _, value in ipairs(tbl) do if value

nil then value = ; end if str

then str = value; elseif value ~= then if value:sub(1,1)

'<' then -- Special case of values enclosed in spans and other markup. comp = value:gsub("%b<>", ""); else comp = value; end if comp:sub(1,1)

duplicate_char then trim = false; end_chr = str:sub(-1,-1); -- str = str .. "

duplicate_char then str = str:sub(1,-2); elseif end_chr

"'" then if str:sub(-3,-1)

duplicate_char .. "" then str = str:sub(1, -4) .. ""; elseif str:sub(-5,-1)

duplicate_char .. "]]" then trim = true; elseif str:sub(-4,-1)

duplicate_char .. "]" then trim = true; end elseif end_chr

"]" then if str:sub(-3,-1)

duplicate_char .. "]]" then trim = true; elseif str:sub(-2,-1)

duplicate_char .. "]" then trim = true; end elseif end_chr

" " then if str:sub(-2,-1)

duplicate_char .. " " then str = str:sub(1,-3); end end

if trim then if value ~= comp then local dup2 = duplicate_char; if dup2:match("%A") then dup2 = "%" .. dup2; end value = value:gsub("(%b<>)" .. dup2, "%1", 1) else value = value:sub(2, -1); end end end 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"

format) then first = reducetoinitials(first) end one = one .. namesep .. first end if is_set(person.link) then one = "" .. one .. "" end if is_set(person.link) and nil ~= person.link:find("//") then one = one .. " " .. seterror('bad_authorlink') end -- check for url in author link; end table.insert(text, one) table.insert(text, sep_one) end end

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"

format) then result = '

' .. result .. ''; end return result, countend

-- 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 -- two missing names and we give up break; -- normal exit or there is a two-name hole in the list; can't tell which end else -- last with or without a first if 1

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

'external' then table.insert(new_list,); elseif handler.mode

'internal' then table.insert(new_list,); elseif handler.mode ~= 'manual' then error(cfg.messages['unknown_ID_mode']); elseif k

'DOI' then table.insert(new_list,); elseif k

'ARXIV' then table.insert(new_list,); elseif k

'ASIN' then table.insert(new_list,); elseif k

'LCCN' then table.insert(new_list,); elseif k

'OL' then table.insert(new_list,); elseif k

'PMC' then table.insert(new_list,); elseif k

'PMID' then table.insert(new_list,); elseif k

'ISSN' then table.insert(new_list,); elseif k

'ISBN' then local ISBN = internallinkid(handler); if not checkisbn(v) and not is_set(options.IgnoreISBN) then ISBN = ISBN .. seterror('bad_isbn',, false, " ", ""); end table.insert(new_list,); else error(cfg.messages['unknown_manual_ID']); end end function comp(a, b) -- used in following table.sort return a[1] < b[1]; end table.sort(new_list, comp); for k, v in ipairs(new_list) do new_list[k] = v[2]; end return new_list;end -- Chooses one matching parameter from a list of parameters to consider-- Generates an error if more than one match is present.function selectone(args, possible, error_condition, index) local value = nil; local selected = ; local error_list = ; if index ~= nil then index = tostring(index); end -- Handle special case of "#" replaced by empty string if index

'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 ) allows automated tools to parse-- the citation information.function COinS(data) if 'table' ~= type(data) or nil

next(data) then return ; end local ctx_ver = "Z39.88-2004"; -- treat table strictly as an array with only set values. local OCinSoutput = setmetatable; if is_set(data.Chapter) then OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; OCinSoutput["rft.genre"] = "bookitem"; OCinSoutput["rft.btitle"] = data.Chapter; OCinSoutput["rft.atitle"] = data.Title; elseif is_set(data.Periodical) then OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"; OCinSoutput["rft.genre"] = "article"; OCinSoutput["rft.jtitle"] = data.Periodical; OCinSoutput["rft.atitle"] = data.Title; else OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; OCinSoutput["rft.genre"] = "book" OCinSoutput["rft.btitle"] = data.Title; end OCinSoutput["rft.place"] = data.PublicationPlace; OCinSoutput["rft.date"] = data.Date; OCinSoutput["rft.series"] = data.Series; OCinSoutput["rft.volume"] = data.Volume; OCinSoutput["rft.issue"] = data.Issue; OCinSoutput["rft.pages"] = data.Pages; OCinSoutput["rft.edition"] = data.Edition; OCinSoutput["rft.pub"] = data.PublisherName; for k, v in pairs(data.ID_list) do local id, value = cfg.id_handlers[k].COinS; if k

'ISBN' then value = cleanisbn(v); else value = v; end if string.sub(id or "", 1, 4)

'info' then OCinSoutput["rft_id"] = table.concat; else OCinSoutput[id ] = value; end end local last, first; for k, v in ipairs(data.Authors) do last, first = v.last, v.first; if k

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'

config.CitationClass) and (is_set (ID)) then ID_list['USENETID']=ID; -- add this new 'id' to the list of IDs ID = ; -- and unset end

local Quote = A['Quote']; local PostScript = A['PostScript'];

local LayURL = A['LayURL']; local LaySource = A['LaySource']; local Transcript = A['Transcript']; local TranscriptURL = A['TranscriptURL'] local TranscriptURLorigin = A:ORIGIN('TranscriptURL'); local sepc = A['Separator'];

local LastAuthorAmp = A['LastAuthorAmp']; local no_tracking_cats = A['NoTracking'];

--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

'none' then -- if |postscript=none then PostScript = ; -- no postscript end end

--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

v then -- if we find one no_tracking_cats = "true"; -- set no_tracking_cats break; -- and we're done end end end

-- 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

Place then Place = ; end -- don't need both if they are the same --encyclopedia and |title then map |title to |article and |encyclopedia to |title |encyclopedia and |article then map |encyclopedia to |title |encyclopedia then map |encyclopedia to |title

|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

"citation") then -- for citation templates if not is_set (Ref) then -- if |ref= is not set Ref = "harv"; -- set default |ref=harv end if not is_set (sepc) then -- if |separator= is not set sepc = ','; -- set citation separator to its default (comma) end else -- not a citation template if not is_set (sepc) then -- if |separator= has not been set sepc = '.'; -- set cite xxx separator to its default (period) end end

-- check for special case where |separator=none if 'none'

sepc:lower then -- if |separator=none sepc = ; -- then set it to an empty string end

-- Special case for cite techreport. if (config.CitationClass

"techreport") then -- special case for cite techreport if is_set(Issue) then -- cite techreport uses 'number', which other citations aliase to 'issue' if not is_set(ID) then -- can we use ID for the "number"? ID = Issue; -- yes, use it Issue = ""; -- unset Issue so that "number" isn't duplicated in the rendered citation or COinS metadata else -- can't use ID so emit error message ID = ID .. " " .. seterror('redundant_parameters', '&#124;id= and &#124;number='); end end end

-- special case for cite interview if (config.CitationClass

"interview") then if is_set(Program) then ID = ' ' .. Program; end if is_set(Callsign) then if is_set(ID) then ID = ID .. sepc .. ' ' .. Callsign; else ID = ' ' .. Callsign; end end if is_set(City) then if is_set(ID) then ID = ID .. sepc .. ' ' .. City; else ID = ' ' .. City; end end

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

"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

-- 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

'conference' then ...) if is_set(BookTitle) then Chapter = Title; ChapterLink = TitleLink; TransChapter = TransTitle; Title = BookTitle; TitleLink = ; TransTitle = ; end

-- Account for the oddity that is, before generation of COinS data.---- is not currently supported by this module if config.CitationClass

"episode" then local AirDate = A['AirDate']; 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 if is_set(Network) then table.insert(n, Network); end if is_set(Station) then table.insert(n, Station); end Date = Date or AirDate; Chapter = Title; ChapterLink = TitleLink; TransChapter = TransTitle; Title = Series; TitleLink = SeriesLink; TransTitle = ; Series = table.concat(s, Sep); ID = table.concat(n, Sep); end-- end of stuff

-- 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

Date then PublicationDate = ; end -- if PublicationDate is same as Date, don't display in rendered citation

-- 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 ) for -- automated parsing of citation information. local OCinSoutput = COinS;

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

9 then Maximum = 8; table.insert(z.message_tail,); elseif not is_set(Maximum) then Maximum = #a + 1; end local control = ; -- If the coauthor field is also used, prevent ampersand and et al. formatting. if is_set(Coauthors) then control.lastauthoramp = nil; control.maximum = #a + 1; end Authors = listpeople(control, a) end

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

4 then Maximum = 3; table.insert(z.message_tail,); elseif not is_set(Maximum) then Maximum = #e + 1; end

local control = ;

Editors, EditorCount = listpeople(control, e); else EditorCount = 1; end

local Cartography = ""; local Scale = ""; if config.CitationClass

"map" then if not is_set(Authors) and is_set(PublisherName) then Authors = PublisherName; PublisherName = ""; end Cartography = A['Cartography']; if is_set(Cartography) then Cartography = sepc .. " " .. wrap('cartography', Cartography, use_lowercase); end Scale = A['Scale']; if is_set(Scale) then Scale = sepc .. " " .. Scale; end end if not is_set(URL) and not is_set(ChapterURL) and not is_set(ArchiveURL) and not is_set(ConferenceURL) and not is_set(TranscriptURL) then -- Test if cite web or cite podcast |url= is missing or empty if inArray(config.CitationClass,) then table.insert(z.message_tail,); end -- Test if accessdate is given without giving a URL if is_set(AccessDate) then table.insert(z.message_tail,); AccessDate = ; end -- Test if format is given without giving a URL if is_set(Format) then Format = Format .. seterror('format_missing_url'); end end -- Test if citation has no title if not is_set(Chapter) and not is_set(Title) and not is_set(Periodical) and not is_set(Conference) and not is_set(TransTitle) and not is_set(TransChapter) then table.insert(z.message_tail,); end Format = is_set(Format) and " (" .. Format .. ")" or ""; local OriginalURL = URL DeadURL = DeadURL:lower; if is_set(ArchiveURL) then if (DeadURL ~= "no") then URL = ArchiveURL URLorigin = A:ORIGIN('ArchiveURL') end end -- Format chapter / article title if is_set(Chapter) and is_set(ChapterLink) then Chapter = "" .. Chapter .. ""; end if is_set(Periodical) and is_set(Title) then Chapter = wrap('italic-title', Chapter); TransChapter = wrap('trans-italic-title', TransChapter); else Chapter = kern_quotes (Chapter); -- if necessary, separate chapter title's leading and trailing quote marks from Module provided quote marks Chapter = wrap('quoted-title', Chapter); TransChapter = wrap('trans-quoted-title', TransChapter); end local TransError = "" if is_set(TransChapter) then if not is_set(Chapter) then TransError = " " .. seterror('trans_missing_chapter'); else TransChapter = " " .. TransChapter; end end Chapter = Chapter .. TransChapter; if is_set(Chapter) then if not is_set(ChapterLink) then if is_set(ChapterURL) then Chapter = externallink(ChapterURL, Chapter) .. TransError; if not is_set(URL) then Chapter = Chapter .. Format; Format = ""; end elseif is_set(URL) then Chapter = externallink(URL, Chapter) .. TransError .. Format; URL = ""; Format = ""; else Chapter = Chapter .. TransError; end elseif is_set(ChapterURL) then Chapter = Chapter .. " " .. externallink(ChapterURL, nil, ChapterURLorigin) .. TransError; else Chapter = Chapter .. TransError; end Chapter = Chapter .. sepc .. " " -- with end-space elseif is_set(ChapterURL) then Chapter = " " .. externallink(ChapterURL, nil, ChapterURLorigin) .. sepc .. " "; end -- Format main title. if is_set(TitleLink) and is_set(Title) then Title = "" .. Title .. "" end if is_set(Periodical) then Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks Title = wrap('quoted-title', Title); TransTitle = wrap('trans-quoted-title', TransTitle);--Hide unfinished cite newsgroup code so that long delayed update can take place elseif inArray(config.CitationClass,) and elseif inArray(config.CitationClass,) and not is_set(Chapter) then Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks Title = wrap('quoted-title', Title); TransTitle = wrap('trans-quoted-title', TransTitle); else Title = wrap('italic-title', Title); TransTitle = wrap('trans-italic-title', TransTitle); end TransError = ""; if is_set(TransTitle) then if not is_set(Title) then TransError = " " .. seterror('trans_missing_title'); else TransTitle = " " .. TransTitle; end end Title = Title .. TransTitle; if is_set(Title) then if not is_set(TitleLink) and is_set(URL) then Title = externallink(URL, Title) .. TransError .. Format URL = ""; Format = ""; else Title = Title .. TransError; end end if is_set(Place) then Place = " " .. wrap('written', Place, use_lowercase) .. sepc .. " "; end if is_set(Conference) then if is_set(ConferenceURL) then Conference = externallink(ConferenceURL, Conference); end Conference = sepc .. " " .. Conference elseif is_set(ConferenceURL) then Conference = sepc .. " " .. externallink(ConferenceURL, nil, ConferenceURLorigin); end if not is_set(Position) then local Minutes = A['Minutes']; if is_set(Minutes) then Position = " " .. Minutes .. " " .. cfg.messages['minutes']; else local Time = A['Time']; if is_set(Time) then local TimeCaption = A['TimeCaption'] if not is_set(TimeCaption) then TimeCaption = cfg.messages['event']; if sepc ~= '.' then TimeCaption = TimeCaption:lower; end end Position = " " .. TimeCaption .. " " .. Time; end end else Position = " " .. Position; At = ; end if not is_set(Page) then if is_set(Pages) then if is_set(Periodical) and not inArray(config.CitationClass,) then Pages = ": " .. Pages; elseif tonumber(Pages) ~= nil then Pages = sepc .." " .. PPrefix .. Pages; else Pages = sepc .." " .. PPPrefix .. Pages; end end else if is_set(Periodical) and not inArray(config.CitationClass,) then Page = ": " .. Page; else Page = sepc .." " .. PPrefix .. Page; end end At = is_set(At) and (sepc .. " " .. At) or ""; Position = is_set(Position) and (sepc .. " " .. Position) or ""; if config.CitationClass

'map' then local Section = A['Section']; local Inset = A['Inset']; if first_set(Pages, Page, At) ~= nil or sepc ~= '.' then if is_set(Section) then Section = ", " .. wrap('section', Section, true); end if is_set(Inset) then Inset = ", " .. wrap('inset', Inset, true); end else if is_set(Section) then Section = sepc .. " " .. wrap('section', Section, use_lowercase); if is_set(Inset) then Inset = ", " .. wrap('inset', Inset, true); end elseif is_set(Inset) then Inset = sepc .. " " .. wrap('inset', Inset, use_lowercase); end end At = At .. Section .. Inset; end

--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

name then Language=" " .. wrap('language', Language); -- no match, use parameter's value else if 0

this_page.namespace and 'en' ~= Language:lower then --found a match; is this page main / article space and English not the language? 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"

TitleType then -- special case for cite thesis TitleType = Degree .. " thesis"; end end

if is_set(TitleType) then -- if type parameter is specified TitleType = " (" .. TitleType .. ")"; -- display it in parentheses end

TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""; Edition = is_set(Edition) and (" " .. wrap('edition', Edition)) or ""; Issue = is_set(Issue) and (" (" .. Issue .. ")") or ""; Series = is_set(Series) and (sepc .. " " .. Series) or ""; OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or ""; Agency = is_set(Agency) and (sepc .. " " .. Agency) or "";

if is_set(Volume) then if (mw.ustring.len(Volume) > 4) then Volume = sepc .." " .. Volume; else Volume = " " .. 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"

config.CitationClass and is_set(Docket) then ID = sepc .." Docket ".. Docket .. ID; end

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)

'"' and 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"

DeadURL then local arch_text = cfg.messages['archived']; if sepc ~= "." then arch_text = arch_text:lower end Archived = sepc .. " " .. substitute(cfg.messages['archived-not-dead'], ); if not is_set(OriginalURL) then Archived = Archived .. " " .. seterror('archive_missing_url'); end elseif is_set(OriginalURL) then local arch_text = cfg.messages['archived-dead']; if sepc ~= "." then arch_text = arch_text:lower end Archived = sepc .. " " .. substitute(arch_text, ); else local arch_text = cfg.messages['archived-missing']; if sepc ~= "." then arch_text = arch_text:lower end Archived = sepc .. " " .. substitute(arch_text, ); end else Archived = "" end local Lay if is_set(LayURL) then if is_set(LayDate) then LayDate = " (" .. LayDate .. ")" end if is_set(LaySource) then LaySource = "  - " .. safeforitalics(LaySource) .. ""; else LaySource = ""; end if sepc

'.' 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"

config.CitationClass then -- cite speech only TitleNote = " (Speech)"; -- annotate the citation if is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter if is_set (Conference) then -- and if |event= is set Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering end end end

-- 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 Authors = Authors .. " " else Authors = Authors .. sepc .. " " end if is_set(Editors) then local in_text = " "; local post_text = ""; if is_set(Chapter) then in_text = in_text .. cfg.messages['in'] .. " " else if EditorCount <= 1 then post_text = ", " .. cfg.messages['editor']; else post_text = ", " .. cfg.messages['editors']; end end if (sepc ~= '.') then in_text = in_text:lower end Editors = in_text .. Editors .. post_text; if (string.sub(Editors,-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

"journal" and is_set(Periodical) then text = safejoin(sepc); text = safejoin(sepc); else text = safejoin(sepc); text = safejoin(sepc); end end if is_set(PostScript) and PostScript ~= sepc then text = safejoin(sepc); --Deals with italics, spaces, etc. text = text:sub(1,-sepc:len-1);-- text = text:sub(1,-2); --Remove final separator (assumes that sepc is only one character) end text = safejoin(sepc);

-- 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"

Ref) then local names = --table of last names & year if #a > 0 then for i,v in ipairs(a) do names[i] = v.last if i

4 then break end end elseif #e > 0 then for i,v in ipairs(e) do names[i] = v.last if i

4 then break end end end names[#names + 1 ] = first_set(Year, anchor_year); -- Year first for legacy citations id = anchorid(names) end options.id = id; end if string.len(text:gsub("

/]*>.-
", ""):gsub("%b<>","")) <= 2 then z.error_categories = ; text = seterror('empty_citation'); z.message_tail = ; end if is_set(options.id) then text = '' .. text .. ""; else text = '' .. text .. ""; end

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

#z.message_tail then text = text .. errorcomment(v[1], v[2]); else text = text .. errorcomment(v[1] .. "; ", v[2]); end end end end no_tracking_cats = no_tracking_cats:lower; if inArray(no_tracking_cats,) then for _, v in ipairs(z.error_categories) do text = text .. ''; end end return textend

-- 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

0 then suggestions = mw.loadData('Module:Citation/CS1/Suggestions'); end if suggestions[k:lower ] ~= nil then error_text, error_state = seterror('parameter_ignored_suggest',, true); else error_text, error_state = seterror('parameter_ignored',, true); end end if error_text ~= then table.insert(z.message_tail,); end end args[k] = v; elseif args[k] ~= nil or (k

'postscript') then args[k] = v; end end return citation0(config, args)end

return z