Module:Citation/CS1

local z = { error_categories = {}; error_ids = {}; message_tail = {}; }

-- Include translation message hooks, ID and error handling configuration settings. local cfg = mw.loadData( 'Module:Citation/CS1/Configuration' ); -- Contains a list of all recognized parameters local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' );

-- Checks that parameter name is valid function validate( name ) name = tostring( name ); -- Normal arguments if whitelist.basic_arguments[ name ] then return true; end -- Arguments with numbers in them name = name:gsub( "%d+", "#" ); if whitelist.numbered_arguments[ name ] then return true; end -- Not found, argument not supported. return false end

-- Formats a comment for error trapping function errorcomment( content, hidden ) if hidden then return ' ' .. content .. ' ';   else return ' ' .. content .. ' ';   end end

-- Sets an error condition and returns the appropriate error message. The actual placement of the error message in the output is the responsibility of the calling function. function seterror( error_id, args, raw ) local error_state = cfg.error_conditions[ error_id ];

if error_state == nil then error( cfg.message_list['undefined_error'] ); end if error_state.category ~= nil and error_state.category ~= "" then table.insert( z.error_categories, error_state.category ); end local message = error_state.message; if args ~= nil then for k, m in ipairs( args ) do           m = m:gsub( "%%", "%%%%" ); message = message:gsub( "$" .. k .. "(%D)", m .. "%1" ); end end

message = wikiescape(message) .. " (" ..       cfg.message_list['help page label'] .. ")";

z.error_ids[ error_id ] = true; if (error_id == 'bare_url_missing_title' or error_id == 'trans_missing_title') and z.error_ids['citation_missing_title'] then return '', false; end if raw == true then return message, error_state.hidden; end return errorcomment( message, error_state.hidden ); end

-- This returns a string with HTML character entities for wikitext markup characters. function wikiescape(text) text = text:gsub( '[&\'%[%]{|}]', {               ['&'] = '&#38;',                ["'"] = '&#39;',                ['['] = '&#91;',                [']'] = '&#93;',                ['{'] = '&#123;',                ['|'] = '&#124;',	            ['}'] = '&#125;' } ); return text; end

-- Formats a wiki style external link function externallinkid(options) local sep = options.separator or " " options.suffix = options.suffix or "" local url_string = options.id   if options.encode == true or options.encode == nil then url_string = mw.uri.encode( url_string ); end return "" .. options.label .. "" .. sep .. "[" ..            options.prefix .. url_string .. options.suffix .. " " .. mw.text.nowiki(options.id) .. "]" end

-- Formats a wiki style internal link function internallinkid(options) local sep = options.separator or " " options.suffix = options.suffix or "" return "" .. options.label .. "" .. sep .. "" .. mw.text.nowiki(options.id) .. "" end

-- Format an external link with error checking function externallink( URL, label ) local error_str = ""; if label == nil or label == "" then label = URL; error_str = seterror( 'bare_url_missing_title' ); end if not checkurl( URL ) then error_str = seterror( 'bad_url' ) .. error_str; end

return "[" .. URL .. ' ' .. safeforurl( label ) .. "]" .. error_str; end

-- Formats a link to Amazon function amazon(id, domain) if ( nil == domain ) then domain = "com" elseif ( "jp" == domain or "uk" == domain ) then domain = "co." .. domain end local handler = cfg.id_handlers['ASIN']; return externallinkid({link = handler.link,       label=handler.label, prefix="//www.amazon."..domain.."/dp/",id=id,        encode=handler.encode, separator = handler.separator}) end

-- Formats a DOI and checks for DOI errors. function doi(id, inactive) local cat = "" local handler = cfg.id_handlers['DOI']; local text; if ( inactive ~= nil ) then text = "" .. handler.label .. ":" .. id; table.insert( z.error_categories, "Pages with DOIs inactive since " .. selectyear(inactive) ); inactive = " (" .. cfg.message_list['inactive'] .. " " .. inactive .. ")" else text = externallinkid({link = handler.link, label = handler.label,           prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) inactive = "" end if ( string.sub(id,1,3) ~= "10." ) then 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({link=handler.link, label=handler.label,           prefix="http://openlibrary.org/authors/OL",id=id, separator=handler.separator,            encode = handler.encode}) elseif ( code == "M" ) then return externallinkid({link=handler.link, label=handler.label,           prefix="http://openlibrary.org/books/OL",id=id, separator=handler.separator,            encode = handler.encode}) elseif ( code == "W" ) then return externallinkid({link=handler.link, label=handler.label,           prefix= "http://openlibrary.org/works/OL",id=id, separator=handler.separator,            encode = handler.encode}) else return externallinkid({link=handler.link, label=handler.label,           prefix= "http://openlibrary.org/OL",id=id, separator=handler.separator,            encode = handler.encode}) .. ' ' .. seterror( 'bad_ol' ); end end

--[[ Determines whether an 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 ) if url_str:sub(1,2) == "//" then -- Protocol-less URLs return true; elseif url_str:match( "^[^/]*:" ) ~= nil then -- Look for ":" prefix and assume it is a URI scheme return true; else -- Anything else is an error return false; end end

-- Determines whether an ISBN string is valid function checkisbn( isbn_str ) isbn_str = isbn_str:gsub("[- ]", ""):upper; local len = isbn_str:len; if len ~= 10 and len ~= 13 then return false; end local temp = 0; if len == 10 then if isbn_str:match( "^%d*X?$" ) == nil then return false; end isbn_str = { isbn_str:byte(1, len) }; for i, v in ipairs( isbn_str ) do           if v == string.byte( "X" ) then temp = temp + 10*( 11 - i ); else temp = temp + tonumber( string.char(v) )*(11-i); end end return temp % 11 == 0; else if isbn_str:match( "^%d*$" ) == nil then return false; end isbn_str = { isbn_str:byte(1, len) }; for i, v in ipairs( isbn_str ) do           temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) ); end return temp % 10 == 0; end end

-- Gets the display text for a wikilink like A|B or B gives B function removewikilink( str ) str = str:gsub( "%[%^|%*|([^%]]*)%]%]", "%1" ); str = str:gsub( "%[%[([^%]]*)%]%]", "%1" ); return str end

-- Escape sequences for content that will be used for URL descriptions function safeforurl( str ) if str:match( "%[%[.-%]%]" ) ~= nil then table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } ); end return str:gsub( '[%[%]\n]', {           ['['] = '&#91;',	        [']'] = '&#93;',	        ['\n'] = ' ' } ); end

-- Converts a hyphen to a dash function hyphentodash( str ) if str == nil then return nil; end if 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 ) -- Note: We can not use  for italics, as the expected behavior for   italics specified by ... in the title is that they will be inverted    (i.e. unitalicized) in the resulting references.  In addition,  and ''    tend to interact poorly under Mediawiki's HTML tidy. if str == nil or str == '' then return str; else if str:sub(1,1) == "'" then str = " " .. str; end if str:sub(-1,-1) == "'" then str = str .. " "; end return str; end end

-- Joins a sequence of strings together while checking for duplicate separation characters. function safejoin( tbl, duplicate_char ) --   Note: we use string functions here, rather than ustring functions.    This has considerably faster performance and should work correctly as     long as the duplicate_char is strict ASCII.  The strings    in tbl may be ASCII or UTF8. local str = ''; local comp = ''; local end_chr = ''; local trim; for _, value in ipairs( tbl ) do       if value == nil then value = ''; end if str == '' then str = value; elseif value ~= '' then if value:sub(1,1) == '<' then -- Special case of values enclosed in spans and other markup. comp = value:gsub( "%b<>", "" ); else comp = value; end if comp:sub(1,1) == duplicate_char then trim = false; end_chr = str:sub(-1,-1); -- str = str .. "<HERE(enchr=" .. end_chr.. ")" if end_chr == duplicate_char then str = str:sub(1,-2); elseif end_chr == "'" then if str:sub(-3,-1) == duplicate_char .. "''" then str = str:sub(1, -4) .. "''";                   elseif str:sub(-5,-1) == duplicate_char .. "]]''" then trim = true; elseif str:sub(-4,-1) == duplicate_char .. "]''" then trim = true; end elseif end_chr == "]" then if str:sub(-3,-1) == duplicate_char .. "]]" then trim = true; elseif str:sub(-2,-1) == duplicate_char .. "]" then trim = true; end elseif end_chr == " " then if str:sub(-2,-1) == duplicate_char .. " " then str = str:sub(1,-3); end end

if trim then 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

-- Return the year portion of a date string, if possible. Returns empty string if the argument can not be interpreted as a year. function selectyear( str ) -- Is the input a simple number? local num = tonumber( str ); if num ~= nil and num > 0 and num < 2100 and num == math.abs(num) then return str; else -- Use formatDate to interpret more complicated formats local lang = mw.getContentLanguage; local good, result; good, result = pcall( lang.formatDate, lang, 'Y', str ) if good then return result; else -- Can't make sense of this input, return blank. return ""; end end end

-- Attempts to convert names to initials. function reducetoinitials(first) local initials = {} for word in string.gmatch(first, "%S+") do       table.insert(initials, string.sub(word,1,1)) -- Vancouver format does not include full stops. 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; if sep:sub(-1,-1) ~= " " then sep = sep .. " " end local namesep = control.namesep local format = control.format local maximum = control.maximum local lastauthoramp = control.lastauthoramp; local text = {} local etal = false; for i,person in ipairs(people) do       if (person.last ~= nil or person.last ~= "") then local mask = person.mask local one if ( maximum ~= nil and i == maximum + 1 ) then etal = true; break; elseif (mask ~= nil) then local n = tonumber(mask) if (n ~= nil) then one = string.rep("&mdash;",n) else one = mask end else one = person.last local first = person.first if (first ~= nil and first ~= '') then if ( "vanc" == format ) then first = reducetoinitials(first) end one = one .. namesep .. first end if (person.link ~= nil and person.link ~= "") then one = "" .. one .. "" end end table.insert(text, one) end end local count = #text; if count > 1 and lastauthoramp ~= nil and lastauthoramp ~= "" and not etal then text[count-1] = text[count-1] .. " & " .. text[count]; text[count] = nil; end local result = table.concat(text, sep) -- construct list if etal then local etal_text = cfg.message_list['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, count end

-- Generates a CITEREF anchor ID. function anchorid( options ) return "CITEREF" .. mw.uri.anchorEncode( table.concat( options ) ); end

-- Gets author list from the input arguments function extractauthors(args) local authors = {}; local i = 1; local last; while true do       if i == 1 then last = selectone( args, {"author" .. i .. "-last", "author-last" .. i,                "last" .. i, "surname" .. i, "Author" .. i, "author" .. i,                 "author-last", "last", "surname", "Author", "author", "authors"}, 'redundant_parameters' ); else last = selectone( args, {"author" .. i .. "-last", "author-last" .. i,                "last" .. i, "surname" .. i, "Author" .. i, "author" .. i}, 'redundant_parameters' ); end if ( last and "" < last ) then -- just in case someone passed in an empty parameter if i == 1 then authors[i] = { last = last, first = selectone( args, {"author" .. i .. "-first", "author-first" .. i,                        "first" .. i, "given" .. i, "author-first",                         "first", "given"}, 'redundant_parameters' ), link = selectone( args, {"author" .. i .. "-link", "author-link" .. i,                        "author" .. i .. "link", "authorlink" .. i, "author-link",                          "authorlink"}, 'redundant_parameters' ), mask = selectone( args, {"author" .. i .. "-mask", "author-mask" .. i,                        "author" .. i .. "mask", "authormask" .. i, "author-mask",                         "authormask" }, 'redundant_parameters' ) }           else authors[i] = { last = last, first = selectone( args, {"author" .. i .. "-first", "author-first" .. i,                        "first" .. i, "given" .. i}, 'redundant_parameters' ), link = selectone( args, {"author" .. i .. "-link", "author-link" .. i,                        "author" .. i .. "link", "authorlink" .. i}, 'redundant_parameters' ), mask = selectone( args, {"author" .. i .. "-mask", "author-mask" .. i,                        "author" .. i .. "mask", "authormask" .. i}, 'redundant_parameters' ) }           end else break; end i = i + 1; end return authors; end

-- Gets editor list from the input arguments function extracteditors(args) local editors = {}; local i = 1; local last; while true do       if i == 1 then last = selectone( args, {"editor" .. i .. "-last", "editor-last" .. i,               "EditorSurname" .. i, "Editor" .. i, "editor" .. i, "editor-last",                 "EditorSurname", "Editor", "editor", "editors"}, 'redundant_parameters' ); else last = selectone( args, {"editor" .. i .. "-last", "editor-last" .. i,               "EditorSurname" .. i, "Editor" .. i, "editor" .. i}, 'redundant_parameters' ); end if ( last and "" < last ) then -- just in case someone passed in an empty parameter if i == 1 then editors[i] = { last = last, first = selectone( args, {"editor" .. i .. "-first",                        "editor-first" .. i, "EditorGiven" .. i, "editor-first",                         "EditorGiven"}, 'redundant_parameters' ), link = selectone( args, {"editor" .. i .. "-link", "editor-link" .. i,                        "editor" .. i .. "link", "editorlink" .. i, "editor-link",                         "editorlink"}, 'redundant_parameters' ), mask = selectone( args, {"editor" .. i .. "-mask", "editor-mask" .. i,                        "editor" .. i .. "mask", "editormask" .. i, "editor-mask",                          "editormask"}, 'redundant_parameters' ) }                           else editors[i] = { last = last, first = selectone( args, {"editor" .. i .. "-first",                        "editor-first" .. i, "EditorGiven" .. i}, 'redundant_parameters' ), link = selectone( args, {"editor" .. i .. "-link", "editor-link" .. i,                        "editor" .. i .. "link", "editorlink" .. i}, 'redundant_parameters' ), mask = selectone( args, {"editor" .. i .. "-mask", "editor-mask" .. i,                        "editor" .. i .. "mask", "editormask" .. i}, 'redundant_parameters' ) }           end else break; end i = i + 1; end return editors; end

-- Populates ID table from arguments using configuration settings function extractids( args ) local id_list = {}; for k, v in pairs( cfg.id_handlers ) do           id_list[k] = selectone( args, v.parameters, 'redundant_parameters' ); 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 handler; local new_list = {}; for k, v in pairs( id_list ) do       handler = {}; --Becasue cfg is read-only we have to copy it the hard way. for k2, v2 in pairs( cfg.id_handlers[k] ) do           handler[k2] = v2; end handler['id'] = v;       if handler.mode == 'external' then table.insert( new_list, {handler.label, externallinkid( handler ) } ); elseif handler.mode == 'internal' then table.insert( new_list, {handler.label, internallinkid( handler ) } ); elseif handler.mode == 'manual' then if k == 'DOI' then table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } ); elseif k == 'ASIN' then table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } ); elseif k == 'OL' then table.insert( new_list, {handler.label, openlibrary( v ) } ); elseif k == 'ISBN' then local ISBN = internallinkid( handler ); if not checkisbn( v ) and (IgnoreISBN == nil or IgnoreISBN == "") then ISBN = ISBN .. seterror( 'bad_isbn' ); end table.insert( new_list, {handler.label, ISBN } ); else error( cfg.message_list['unknown_manual_ID'] ); end else error( cfg.message_list['unknown_ID_mode'] ); end end

function comp( a, b ) 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 ) local value = nil; local selected = ''; local error_list = {}; for _, v in ipairs( possible ) do       if args[v] ~= nil then if value ~= nil 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 .. ", " end error_str = error_str .. " ";       end if #error_list > 1 then error_str = error_str .. ", and "; else error_str = error_str .. " and "; end error_str = error_str .. " ";       table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } ); end return value, selected; end

-- This is the main function foing the majority of the citation formatting. function citation0( config, args) -- Load Input Parameters

local i    local PPrefix = config.PPrefix or "p. " local PPPrefix = config.PPPrefix or "pp. " if ( nil ~= args.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 = args.authors local a = extractauthors( args );

local Coauthors = selectone( args, {'coauthors', 'coauthor' }, 'redundant_parameters' ); local Others = args.others local Editors = args.editors local e = extracteditors( args );

local Year = args.year local PublicationDate = selectone( args, {'publicationdate', 'publication-date' }, 'redundant_parameters' ); local OrigYear = args.origyear local Date = args.date local LayDate = args.laydate - Get title data local Title = args.title or args.encyclopaedia or args.encyclopedia or args.dictionary local BookTitle = args.booktitle local Conference = args.conference local TransTitle = selectone( args, {'trans-title', 'trans_title' }, 'redundant_parameters' ); local TitleNote = args.department local TitleLink = selectone( args, {'titlelink', 'episodelink' }, 'redundant_parameters' ); local Chapter = selectone( args, {'chapter', 'contribution', 'entry' }, 'redundant_parameters' ); local ChapterLink = args.chapterlink local TransChapter = selectone( args, {'trans-chapter', 'trans_chapter' }, 'redundant_parameters' ); local TitleType = args.type local ArchiveURL = selectone( args, {'archive-url', 'archiveurl' }, 'redundant_parameters' ); local URL = selectone( args, {'url', 'URL'}, 'redundant_parameters' ); local ChapterURL = selectone( args, {'chapter-url', 'chapterurl', 'contribution-url', 'contributionurl' }, 'redundant_parameters' ); local ConferenceURL = selectone( args, {'conference-url', 'conferenceurl' }, 'redundant_parameters' ); local Periodical = selectone( args, {'journal', 'newspaper', 'magazine', 'work', 'website',        'periodical', 'encyclopedia', 'encyclopaedia'}, 'redundant_parameters' ); if ( config.CitationClass == "encyclopaedia" ) then if ( args.article and args.article ~= "") then if ( Title and Title ~= "") then Periodical = Title end Chapter = args.article TransChapter = TransTitle Title = nil TransTitle = nil elseif ( Chapter == nil or Chapter == '' ) then if Title ~= args.encyclopedia then Chapter = Title TransChapter = TransTitle Title = nil TransTitle = nil end end if ( Periodical and Periodical ~= "") then if Periodical == Title or Periodical == Chapter then Periodical = nil end end end local Series = selectone( args, {'series', 'version'}, 'redundant_parameters' ); local Volume = args.volume local Issue = selectone( args, {'issue', 'number'}, 'redundant_parameters' ); local Position = nil local Page, Pages, At, page_type; Page, page_type = selectone( args, {'p', 'page', 'pp', 'pages', 'at'},        'extra_pages' ); if page_type == 'pp' or page_type == 'pages' then Pages = hyphentodash( Page ); Page = nil; elseif page_type == 'at' then At = Page; Page = nil; end local Edition = args.edition local PublicationPlace = selectone( args, {'publication-place', 'publicationplace' }, 'redundant_parameters' ); local Place = selectone( args, {'place', 'location'}, 'redundant_parameters' ); if PublicationPlace == nil and Place ~= nil then PublicationPlace = Place; end if PublicationPlace == Place then Place = nil end local PublisherName = args.publisher local SubscriptionRequired = args.subscription local Via = args.via local AccessDate = selectone( args, {'access-date', 'accessdate' }, 'redundant_parameters' ); local ArchiveDate = selectone( args, {'archive-date', 'archivedate' }, 'redundant_parameters' ); local Agency = args.agency local DeadURL = args.deadurl or "yes"          -- Only used if ArchiveURL is present. local Language = selectone( args, {'language', 'in'}, 'redundant_parameters' ); local Format = args.format local Ref = selectone( args, {'ref', 'Ref'}, 'redundant_parameters' );

local DoiBroken = selectone( args, {'doi_inactivedate', 'doi_brokendate', 'DoiBroken'}, 'redundant_parameters' ); local ID = selectone( args, {'id', 'ID', 'docket'}, 'redundant_parameters' ); local ASINTLD = selectone( args, {'ASIN-TLD', 'asin-tld'}, 'redundant_parameters' ); local IgnoreISBN = selectone( args, {'ignore-isbn-error', 'ignoreisbnerror'}, 'redundant_parameters' );

local ID_list = extractids( args ); local Quote = selectone( args, {'quote', 'quotation'}, 'redundant_parameters' ); local PostScript = args.postscript or "." local LaySummary = args.laysummary local LaySource = args.laysource local Transcript = args.transcript local TranscriptURL = selectone( args, {'transcript-url', 'transcripturl'}, 'redundant_parameters' ); local sepc = args.separator or "." local LastAuthorAmp = args.lastauthoramp local no_tracking_cats = selectone( args, {"template doc demo", 'nocat',        'notracking', "no-tracking"}, 'redundant_parameters' ) or "";

if ( config.CitationClass == "journal" ) then if (URL == nil or URL == "") then if (ID_list['PMC'] ~= nil) then local Embargo = args.embargo or args.Embargo; if Embargo ~= nil then local lang = mw.getContentLanguage; local good1, result1, good2, result2; good1, result1 = pcall( lang.formatDate, lang, 'U', Embargo ); good2, result2 = pcall( lang.formatDate, lang, 'U' );

if good1 and good2 and tonumber( result1 ) < tonumber( result2 ) then URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC']; end else URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC']; end end end end

-- At this point fields may be nil if they weren't specified in the template use. We can use that fact. -- Account for the oddity that is, before generation of COinS data. if ( BookTitle ) then Chapter = Title ChapterLink = TitleLink TransChapter = TransTitle Title = BookTitle TitleLink = nil TransTitle = nil end -- Account for the oddity that is, before generation of COinS data. if config.CitationClass == "episode" then local AirDate = args.airdate local SeriesLink = args.serieslink local Season = args.season local SeriesNumber = args.seriesnumber or args.seriesno local Network = args.network local Station = args.station local s = {} if Issue ~= nil then table.insert(s, cfg.message_list["episode"] .. " " .. Issue) Issue = nil end if Season ~= nil then table.insert(s, cfg.message_list["season"] .. " " .. Season) end if SeriesNumber ~= nil then table.insert(s, cfg.message_list["series"] .. " " .. SeriesNumber) end local n = {} if Network ~= nil then table.insert(n, Network) end if Station ~= nil then table.insert(n, Station) end Date = Date or AirDate Chapter = Title ChapterLink = TitleLink TransChapter = TransTitle Title = Series TitleLink = SeriesLink TransTitle = nil local Sep = args["series-separator"] or args["separator"] or ". " Series = table.concat(s, Sep) ID = table.concat(n, Sep) end -- These data form a COinS tag (see ) which allows -- automated tools to parse the citation information. local OCinSdata = {} -- COinS metadata excluding id, bibcode, doi, etc.   local ctx_ver = "Z39.88-2004" OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:book" if ( nil ~= Periodical ) then OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal" OCinSdata["rft.genre"] = "article" OCinSdata["rft.jtitle"] = Periodical if ( nil ~= Title ) then OCinSdata["rft.atitle"] = Title end end if ( nil ~= Chapter and "" ~= Chapter) then OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:book" OCinSdata["rft.genre"] = "bookitem" OCinSdata["rft.btitle"] = Chapter if ( nil ~= Title ) then OCinSdata["rft.atitle"] = Title end else OCinSdata["rft.genre"] = "book" if ( nil ~= Title ) then OCinSdata["rft.btitle"] = Title end end OCinSdata["rft.place"] = PublicationPlace OCinSdata["rft.date"] = Date or Year or PublicationDate OCinSdata["rft.series"] = Series OCinSdata["rft.volume"] = Volume OCinSdata["rft.issue"] = Issue OCinSdata["rft.pages"] = Page or Pages or At   OCinSdata["rft.edition"] = Edition OCinSdata["rft.pub"] = PublisherName for k, v in pairs( ID_list ) do       if string.sub( cfg.id_handlers[k].COinS or "info", 1, 4 ) ~= 'info' then OCinSdata[ cfg.id_handlers[k].COinS ] = v;       end end OCinSdata.rft_id = URL or ChapterURL

local last, first; local OCinSauthors = {}; for k, v in ipairs( a ) do       last = v.last; first = v.first; if k == 1 then if last ~= nil then OCinSdata["rft.aulast"] = last; end if first ~= nil then OCinSdata["rft.aufirst"] = first; end end if last ~= nil and first ~= nil then table.insert( OCinSauthors, last .. (args.NameSep or ", ") .. first ); elseif last ~= nil then table.insert( OCinSauthors, last ); end end

local OCinSids = {} -- COinS data only for id, bibcode, doi, pmid, etc.   for k, v in pairs( ID_list ) do        if string.sub( cfg.id_handlers[k].COinS or "", 1, 4 ) == 'info' then OCinSids[ cfg.id_handlers[k].COinS ] = v;       end end

local OCinStitle = "ctx_ver=" .. ctx_ver -- such as "Z39.88-2004" for name,value in pairs(OCinSdata) do       OCinStitle = OCinStitle .. "&" .. name .. "=" .. mw.uri.encode( removewikilink(value) ); end for _, value in ipairs(OCinSauthors) do       OCinStitle = OCinStitle .. "&rft.au=" .. mw.uri.encode( removewikilink(value) ); end for name,value in pairs(OCinSids) do       OCinStitle = OCinStitle .. "&rft_id=" .. mw.uri.encode(name .. "/" .. removewikilink(value) ); end local this_page = mw.title.getCurrentTitle; OCinStitle = OCinStitle .. "&rfr_id=info:sid/" .. mw.site.server:match( "[^/]*$" ) .. ":"      .. this_page.prefixedText  -- end COinS data by page's non-encoded pagename

if (Periodical ~= nil and Periodical ~= "") and (Chapter == nil or Chapter == '') and (Title ~= nil and Title ~= "") then Chapter = Title ChapterLink = TitleLink TransChapter = TransTitle Title = nil TitleLink = nil TransTitle = nil 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 ( Authors == nil ) then local Maximum = tonumber( (selectone( args, {"display-authors", "displayauthors"}, 'redundant_parameters' )) ); -- Preserve old-style implicit et al.       if Maximum == nil and #a == 9 then Maximum = 8; table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } ); elseif Maximum == nil then Maximum = #a + 1; end local control = { sep = (args["author-separator"] or ";") .. " ",           namesep = (args["author-name-separator"] or args["name-separator"] or ",") .. " ",           format = selectone( args, {"author-format", "authorformat" }, 'redundant_parameters' ), maximum = Maximum, lastauthoramp = LastAuthorAmp }       -- If the coauthor field is also used, prevent ampersand and et al. formatting. if Coauthors ~= nil and Coauthors ~= "" then control.lastauthoramp = nil; control.maximum = #a + 1; end Authors = listpeople(control, a)    end local EditorCount if ( Editors == nil ) then local Maximum = tonumber( (selectone( args, {"display-editors", "displayeditors"}, 'redundant_parameters' )) );

-- Preserve old-style implicit et al.       if Maximum == nil and #e == 4 then Maximum = 3; table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } ); elseif Maximum == nil then Maximum = #e + 1; end

local control = { sep = (args["editor-separator"] or ";") .. " ",           namesep = (args["editor-name-separator"] or args["name-separator"] or ",") .. " ",           format = selectone( args, {"editor-format", "editorformat" }, 'redundant_parameters' ), maximum = Maximum, lastauthoramp = LastAuthorAmp }

Editors, EditorCount = listpeople(control, e)    else EditorCount = 1; end if ( Date == nil or Date == "") then --  there's something hinky with how this adds dashes to perfectly-good free-standing years --       Date = Year        if ( Date ~= nil ) then            local Month = args.month            if ( Month == nil ) then                 local Began = args.began                local Ended = args.ended                if Began ~= nil and Ended ~= nil then                    Month = Began .. "–" .. Ended                else                    Month = "–"                end            end            Date = Month .. " " .. Date            local Day = args.day            if ( Day ~= nil ) then Date = Day .. " " .. Date end        end  -- so let's use the original version for now Date = Year if ( Date ~= nil and Date ~="") then local Month = args.month if ( Month ~= nil and Month ~= "") then Date = Month .. " " .. Date local Day = args.day if ( Day ~= nil ) then Date = Day .. " " .. Date end else Month = "" end else Date = "" end end if ( PublicationDate == Date or PublicationDate == Year ) then PublicationDate = nil end if( (Date == nil or Date == "") and PublicationDate ~= nil ) then Date = PublicationDate; PublicationDate = nil; end

-- Captures the value for Date prior to adding parens or other textual transformations local DateIn = Date if ( URL == nil or URL == '' ) and ( ChapterURL == nil or ChapterURL == '' ) and ( ArchiveURL == nil or ArchiveURL == '' ) and ( ConferenceURL == nil or ConferenceURL == '' ) and ( TranscriptURL == nil or TranscriptURL == '' ) then

-- Test if cite web is called without giving a URL if ( config.CitationClass == "web" ) then table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } ); end

-- Test if accessdate is given without giving a URL if ( AccessDate ~= nil and AccessDate ~= '' ) then table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } ); AccessDate = nil; end -- Test if format is given without giving a URL if ( Format ~= nil and Format ~= '' ) then Format = Format .. seterror( 'format_missing_url' ); end end

-- Test if citation has no title if ( Chapter == nil or Chapter == "" ) and ( Title == nil or Title == "" ) and ( Periodical == nil or Periodical == "" ) and ( Conference == nil or Conference == "" ) and ( TransTitle == nil or TransTitle == "" ) and ( TransChapter == nil or TransChapter == "" ) then table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } ); end

if ( Format ~= nil and Format ~="" ) then Format = " (" .. Format .. ")" else Format = "" end local OriginalURL = URL DeadURL = DeadURL:lower; if ( ArchiveURL and "" < ArchiveURL ) then if ( DeadURL ~= "no" ) then URL = ArchiveURL end end

if ( TransTitle and "" < TransTitle ) then TransTitle = " [" .. TransTitle .. "&#93;" else TransTitle = "" end if ( TransChapter and "" < TransChapter ) then TransChapter = " [" .. TransChapter .. "&#93;" else TransChapter = "" end -- Format chapter / article title if ( Chapter ~= nil and Chapter ~= "" ) then if ( ChapterLink and "" < ChapterLink ) then Chapter = "" .. Chapter .. "" end if ( Periodical and "" < Periodical ) and (Title ~= nil and Title ~= "" ) then Chapter = "''" .. safeforitalics(Chapter) .. "''"       else Chapter = "\"" .. Chapter .. "\"" end else Chapter = ""; end local TransError = "" if TransChapter ~= "" and Chapter == "" then TransError = " " .. seterror( 'trans_missing_chapter' ); end Chapter = Chapter .. TransChapter if Chapter ~= "" then if ( ChapterLink == nil ) then if ( ChapterURL and "" < ChapterURL ) then Chapter = externallink( ChapterURL, Chapter ) .. TransError; if URL == nil or URL == "" then Chapter = Chapter .. Format; Format = ""; end elseif ( URL and "" < URL ) then Chapter = externallink( URL, Chapter ) .. TransError .. Format; URL = nil Format = "" else Chapter = Chapter .. TransError; end elseif ChapterURL ~= nil and ChapterURL ~= "" then Chapter = Chapter .. " " .. externallink( ChapterURL ) .. TransError; else Chapter = Chapter .. TransError; end Chapter = Chapter .. sepc .. " " -- with end-space elseif ChapterURL ~= nil and ChapterURL ~= "" then Chapter = " " .. externallink( ChapterURL ) .. sepc .. " ";   end -- Format main title. if ( Title and "" < Title ) then if ( TitleLink and "" < TitleLink ) then Title = "" .. Title .. "" end if ( Periodical and "" < Periodical ) then Title = "\"" .. Title .. "\"" elseif ( config.CitationClass == "web"               or config.CitationClass == "news"                 or config.CitationClass == "pressrelease" ) and Chapter == "" then Title = "\"" .. Title .. "\"" else Title = "''" .. safeforitalics(Title) .. "''"       end else Title = ""; end local TransError = ""; if TransTitle ~= "" and Title == "" then TransError = " " .. seterror( 'trans_missing_title' ); end Title = Title .. TransTitle if Title ~= "" then if ( TitleLink == nil and URL and "" < URL ) then Title = externallink( URL, Title ) .. TransError .. Format URL = nil Format = '' else Title = Title .. TransError; end end

if ( Place ~= nil and Place ~= "" ) then if sepc == '.' then Place = " " .. cfg.message_list['written'] .. " " .. Place .. sepc .. " ";       else Place = " " .. cfg.message_list['written']:lower .. " " .. Place .. sepc .. " ";       end else Place = ""; end if ( Conference ~= nil and Conference ~="" ) then if ( ConferenceURL ~= nil ) then Conference = externallink( ConferenceURL, Conference ); end Conference = " " .. Conference elseif ConferenceURL ~= nil and ConferenceURL ~= "" then Conference = " " .. externallink( ConferenceURL ); else Conference = "" end if ( nil ~= Position or nil ~= Page or nil ~= Pages ) then At = nil end if ( nil == Position and "" ~= Position ) then local Minutes = args.minutes if ( nil ~= Minutes ) then Position = " " .. Minutes .. " " .. cfg.message_list['minutes']; else local Time = args.time if ( nil ~= Time ) then local TimeCaption = args.timecaption if TimeCaption == nil then TimeCaption = cfg.message_list['event']; if sepc ~= '.' then TimeCaption = TimeCaption:lower; end end Position = " " .. TimeCaption .. " " .. Time else Position = "" end end else Position = " " .. Position end if ( nil == Page or "" == Page ) then Page = "" if ( nil == Pages or "" == Pages) then Pages = "" elseif ( Periodical ~= nil and Periodical ~= "" and                config.CitationClass ~= "encyclopaedia" and                 config.CitationClass ~= "web" and                 config.CitationClass ~= "book" and                 config.CitationClass ~= "news") then Pages = ": " .. Pages else if ( tonumber(Pages) ~= nil ) then Pages = sepc .." " .. PPrefix .. Pages else Pages = sepc .." " .. PPPrefix .. Pages end end else Pages = "" if ( Periodical ~= nil and Periodical ~= "" and            config.CitationClass ~= "encyclopaedia" and             config.CitationClass ~= "web" and             config.CitationClass ~= "book" and             config.CitationClass ~= "news") then Page = ": " .. Page else Page = sepc .." " .. PPrefix .. Page end end if ( At ~= nil and At ~="") then At = sepc .. " " .. At   else At = "" end if ( Coauthors == nil ) then Coauthors = "" end if ( Others ~= nil and Others ~="" ) then Others = sepc .. " " .. Others else Others = "" end if ( TitleType ~= nil and TitleType ~="" ) then TitleType = " (" .. TitleType .. ")" else TitleType = "" end if ( TitleNote ~= nil and TitleNote ~="" ) then TitleNote = sepc .. " " .. TitleNote else TitleNote = "" end if ( Language ~= nil and Language ~="" ) then Language = " (" .. cfg.message_list['in'] .. " " .. Language .. ")" else Language = "" end if ( Edition ~= nil and Edition ~="" ) then Edition = " (" .. Edition .. " " .. cfg.message_list['edition'] .. ")" else Edition = "" end if ( Volume ~= nil and Volume ~="" ) then if ( mw.ustring.len(Volume) > 4 ) then Volume = sepc .." " .. Volume else Volume = " '''" .. hyphentodash(Volume) .. "'''"       end else Volume = "" end if ( Issue ~= nil and Issue ~="" ) then Issue = " (" .. Issue .. ")" else Issue = "" end if ( Series ~= nil and Series ~="" ) then Series = sepc .. " " .. Series else Series = "" end if ( OrigYear ~= nil and OrigYear ~="" ) then OrigYear = " [" .. OrigYear .. "]" else OrigYear = "" end if ( Agency ~= nil and Agency ~="" ) then Agency = sepc .. " " .. Agency else Agency = "" end totally unrelated data if ( Date ~= nil ) then Date = Date else Date = "" end if ( Via ~= nil and Via ~="" ) then Via = " &mdash; " .. cfg.message_list['via'] .. " " .. Via else Via = "" end if ( AccessDate ~= nil and AccessDate ~="" ) then local retrv_text = " " .. cfg.message_list['retrieved'] .. " "        if (sepc ~= ".") then retrv_text = retrv_text:lower end AccessDate = ' ' .. sepc .. retrv_text .. AccessDate .. ' '   else AccessDate = "" end if ( SubscriptionRequired ~= nil and        SubscriptionRequired ~= "" ) then SubscriptionRequired = sepc .. " " .. cfg.message_list['subscription']; else SubscriptionRequired = "" end if ( ID ~= nil and ID ~="") then ID = sepc .." ".. ID else ID="" end

ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN} );

if ( URL ~= nil and URL ~="") then URL = " " .. externallink( URL, URL ); local error_text = seterror( 'bare_url_missing_title' ); if config.CitationClass == "web" then URL = URL .. " " .. seterror( 'cite_web_title' ); else URL = URL .. error_text; end else URL = "" end

if ( Quote and Quote ~="" ) then if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then Quote = Quote:sub(2,-2); end Quote = sepc .." \"" .. Quote .. "\"" PostScript = "" else if ( PostScript == nil) then PostScript = "" end Quote = "" end local Archived if ( nil ~= ArchiveURL and "" ~= ArchiveURL ) then if ( ArchiveDate ~= nil and ArchiveDate ~="" ) then ArchiveDate = " " .. ArchiveDate else ArchiveDate = " " .. seterror('archive_missing_date') .. " "       end local arch_text = cfg.message_list['archived']; if (sepc ~= ".") then arch_text = arch_text:lower end if ( "no" == DeadURL ) then Archived = sepc .. " " .. externallink( ArchiveURL, arch_text ) .. " " ..                cfg.message_list['from'] .. " " .. cfg.message_list['original'] .. " " ..                cfg.message_list['on'] .. ArchiveDate if OriginalURL == nil or OriginalUrl == '' then Archived = Archived .. " " .. seterror('archive_missing_url_not_dead'); end else if OriginalURL ~= nil and OriginalURL ~= '' then Archived = sepc .. " " .. arch_text .. " " .. cfg.message_list['from'] .. " " .. externallink( OriginalURL, cfg.message_list['original'] ) .. " "                   .. cfg.message_list['on'] .. ArchiveDate else if config.CitationClass ~= 'web' then Archived = sepc .. " " .. arch_text .. " " .. cfg.message_list['from'] .. " " ..                    seterror('archive_missing_url') .. " " .. cfg.message_list['on'] .. ArchiveDate else Archived = sepc .. " " .. arch_text .. " " .. cfg.message_list['from'] .. " " .. cfg.message_list['original'] .. " " ..                        cfg.message_list['on'] .. ArchiveDate end end end else Archived = "" end local Lay if ( nil ~= LaySummary and "" ~= LaySummary ) then if ( LayDate ~= nil ) then LayDate = " (" .. LayDate .. ")" else LayDate = "" end if ( LaySource ~= nil ) then LaySource = " – ''" .. safeforitalics(LaySource) .. "''"        else LaySource = "" end if sepc == '.' then Lay = sepc .. " [" .. LaySummary .. " " .. cfg.message_list['lay summary'] .. "]" .. LaySource .. LayDate else Lay = sepc .. " [" .. LaySummary .. " " .. cfg.message_list['lay summary']:lower .. "]" .. LaySource .. LayDate end else Lay = "" end if ( nil ~= Transcript and "" ~= Transcript ) then if ( TranscriptURL ~= nil ) then Transcript = externallink( TranscriptURL, Transcript ) end elseif TranscriptURL ~= nil and TranscriptURL ~= "" then Transcript = externallink( TranscriptURL ) else Transcript = "" end local Publisher = "" if ( Periodical and Periodical ~= "" and        config.CitationClass ~= "encyclopaedia" and         config.CitationClass ~= "web" and         config.CitationClass ~= "pressrelease" ) then if ( PublisherName ~= nil and PublisherName ~="" ) then if (PublicationPlace ~= nil and PublicationPlace ~= '') then Publisher = PublicationPlace .. ": " .. PublisherName; else Publisher = PublisherName; end elseif (PublicationPlace ~= nil and PublicationPlace ~= '') then Publisher= PublicationPlace; else Publisher = ""; end if ( PublicationDate and PublicationDate ~="" ) then if Publisher ~= '' then Publisher = Publisher .. ", " .. cfg.message_list['published'] .. " " .. PublicationDate; else Publisher = PublicationDate; end end if Publisher ~= "" then Publisher = " (" .. Publisher .. ")"; end else if ( PublicationDate and PublicationDate ~="" ) then PublicationDate = " (" .. cfg.message_list['published'] .. " " .. PublicationDate .. ")" else PublicationDate = "" end if ( PublisherName ~= nil and PublisherName ~="" ) then if (PublicationPlace ~= nil and PublicationPlace ~= '') then Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate; else Publisher = sepc .. " " .. PublisherName .. PublicationDate; end elseif (PublicationPlace ~= nil and 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 ( Periodical ~= nil and Periodical ~="" ) then if ( Title and Title ~= "" ) or ( TitleNote and TitleNote ~= "" ) then Periodical = sepc .. " ''" .. safeforitalics(Periodical) .. "''"       else Periodical = "''" .. safeforitalics(Periodical) .. "''"       end else Periodical = "" 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 ( ( (config.CitationClass == "journal") or (config.CitationClass == "citation") ) and         Periodical ~= "" ) then if (Others ~= "") then Others = Others .. sepc .. " " end tcommon = safejoin( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series,            Language, Edition, Publisher, Agency, Volume, Issue, Position}, sepc ); else tcommon = safejoin( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language,            Volume, Issue, Others, Edition, Publisher, Agency, Position}, sepc ); end if #ID_list > 0 then ID_list = safejoin( { sepc .. " ", table.concat( ID_list, sepc .. " " ), ID }, sepc ); else ID_list = ID; end local idcommon = safejoin( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc );

local text local pgtext = Page .. Pages .. At   if ( "" ~= Authors ) then if (Coauthors ~= "") then Authors = Authors .. "; " .. Coauthors end if ( "" ~= Date ) then Date = " ("..Date..")" .. OrigYear .. sepc .. " "         else if ( string.sub(Authors,-1,-1) == sepc) --check end character then Authors = Authors .. " "             else Authors = Authors .. sepc .. " "           end end if ( "" ~= Editors) then local in_text = " in " if (sepc == '.') then in_text = " In " end if (string.sub(Editors,-1,-1) == sepc) then Editors = in_text .. Editors .. " "               else Editors = in_text .. Editors .. sepc .. " "           end end text = safejoin( {Authors, Date, Chapter, Place, Editors, tcommon }, sepc ); text = safejoin( {text, pgtext, idcommon}, sepc ); elseif ( "" ~= Editors) then if ( "" ~= Date ) then if EditorCount <= 1 then Editors = Editors .. ", " .. cfg.message_list['editor']; else Editors = Editors .. ", " .. cfg.message_list['editors']; end Date = " (" .. Date ..")" .. OrigYear .. sepc .. " "       else if EditorCount <= 1 then Editors = Editors .. " (" .. cfg.message_list['editor'] .. ")" .. sepc .. " "           else Editors = Editors .. " (" .. cfg.message_list['editors'] .. ")" .. sepc .. " "           end end text = safejoin( {Editors, Date, Chapter, Place, tcommon}, sepc ); text = safejoin( {text, pgtext, idcommon}, sepc ); else if ( "" ~= Date ) then if ( string.sub(tcommon,-1,-1) ~= sepc ) then Date = sepc .." " .. Date .. OrigYear else Date = " " .. Date .. OrigYear end end -- endif ""~=Date if ( config.CitationClass=="journal" and Periodical ) then text = safejoin( {Chapter, Place, tcommon}, sepc ); text = safejoin( {text, pgtext, Date, idcommon}, sepc ); else text = safejoin( {Chapter, Place, tcommon, Date}, sepc ); text = safejoin( {text, pgtext, idcommon}, sepc ); end end if PostScript ~= '' and PostScript ~= nil and PostScript ~= sepc then text = safejoin( {text, sepc}, sepc ); --Deals with italics, spaces, etc.        text = text:sub(1,-2); --Remove final seperator end text = safejoin( {text, PostScript}, sepc );

-- Now enclose the whole thing in a element if ( Year == nil ) then if ( DateIn ~= nil and DateIn ~= "" ) then Year = selectyear( DateIn ) elseif( PublicationDate ~= nil and PublicationDate ~= "" ) then Year = selectyear( PublicationDate ) else Year = "" end end local classname = "citation" if ( config.CitationClass ~= "citation" ) then classname = "citation " .. (config.CitationClass or "") end local options = { class=classname } if ( Ref ~= nil ) then local id = Ref if ( "harv" == Ref ) then local names = {} --table of last names & year if ( "" ~= Authors ) then for i,v in ipairs(a) do                    names[i] = v.last if i == 4 then break end end elseif ( "" ~= Editors ) then for i,v in ipairs(e) do                    names[i] = v.last if i == 4 then break end end end names[ #names + 1 ] = Year; 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 options.id ~= nil 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 for i,v in ipairs( z.message_tail ) do           if v[1] ~= nil and 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 if no_tracking_cats == '' then for _, v in ipairs( z.error_categories ) do           text = text .. '';       end end return text end

-- This is used by templates such as to create the actual citation text. function z.citation(frame) local pframe = frame:getParent local args = {}; local suggestions = {}; local error_text, error_state; 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', {v}, true ); end elseif validate( k:lower ) then error_text, error_state = seterror( 'parameter_ignored_suggest', {k, k:lower}, 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', {k, suggestions[ k:lower ]}, true ); else error_text, error_state = seterror( 'parameter_ignored', {k}, true ); end end if error_text ~= '' then table.insert( z.message_tail, {error_text, error_state} ); end end args[k] = v;       elseif k == 'postscript' then args[k] = v;       end end

local config = {}; for k, v in pairs( frame.args ) do       config[k] = v;        if args[k] == nil and (v ~= '' or k == 'postscript') then args[k] = v;       end end return citation0( config, args) end

return z - --NOTES -- -- NOTE A1: This Lua module was originally designed to handle a mix --     of citation styles, crossing Vancouver style with Wikipedia's --      local Citation Style 1 (CS1) from {Template:Citation/core}. --     However, the conflicting positions of parameters, scattered --     in twisted locations across this module, led to a separate --     variation just to untangle the CS1 format of citations. -- -- NOTE D2: The placement of dots and other separators between the --     displayed parameters has been a continual headache, to keep --     coordinated with the data in parentheses "(data)". There --     has been a need to pre-check for the existence of related --     options, to keep from putting double-dots ".." in some cases. --     In particular, the omission of the "title=" parameter has led --     to several cases of a spurious dot ". ." because the original --     design had treated the title as a mandatory parameter. --

--HISTORY: --18Oct2012 Fixed lead-space in Chapter by omitting " ". --18Oct2012 Fixed lead-space in Chapter/Title as end " " of Authors/Date/... --19Oct2012 Put HISTORY comments to log major changes (not typos). --19Oct2012 Fixed extra dot ".." in Title by omitting at end of "tcommon=...". --19Oct2012 For pages, put &nbsp in "p. " etc. --19Oct2012 Enhanced "pages=" to detect lone page as "p." else "pp." prefix. --19Oct2012 Fixed to show "." after Periodical name (work, newspaper...). --19Oct2012 Fixed web-link to have spaces "[... Archived] from the original". --19Oct2012 Fixed to show ";" between authors & coauthors. --19Oct2012 Fixed to omit extra "." after coauthors. --20Oct2012 Fixed COinS data to not urlencode all, as "ctx_ver=Z39.88-2004" --20Oct2012 Fixed COinS to not end as "&" but use lead "&rft...=" form. --20Oct2012 Fixed COinS to not url.encode page's "rfr_id=..." pagename. --20Oct2012 Fixed COinS data when "web" to default to rft.genre "book". --05Nov2012 Add a span wrapper even when there is no Ref parameter --15Feb2013 Added Agency for "agency=xx". --19Feb2013 Put NOTES comments to explain module operation. --19Feb2013 Copied as Module:Citation/CS1 to alter to match wp:CS1 form. --19Feb2013 Changed OrigYear to use [__] for CS1 style. --19Feb2013 Fixed to not show duplicate Publisher/Agency. --19Feb2013 Moved page-number parameters to after final date. --19Feb2013 Fixed to not put double-dots after title again. --20Feb2013 Changed to omit dot "." if already ends with dot. --20Feb2013 If class "journal" shows Publisher after Periodical/Series. --20Feb2013 Shifted Format to after Language, and Others after Volume. --20Feb2013 Set AccessDate + --20Feb2013 Fixed url when deadurl=no. --20Feb2013 Added sepc for separator character between parameters. --20Feb2013 Put "OCLC" for "Online Computer Library Center". --20Feb2013 Fix empty "authorlink=" as person.link ~= "". --20Feb2013 Added space after AuthorSep & AuthorNameSep. --21Feb2013 Added args.contributor (was missing parameter). --21Feb2013 Fixed EditorSep (was misspelled "EdithorSep"). --21Feb2013 Set OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:book" --21Feb2013 Checked to omit blank codes (asin= | doi= etc.). --21Feb2013 Set enddot to end line if not config.CitationClass "citation". --21Feb2013 Fixed to show "issn=x" as the ISSN code. --21Feb2013 Fixed to show "id=x" after Zbl code. --21Feb2013 Changed to omit double-dot before date when already dot. --21Feb2013 Order config.CitationClass "citation": Volume, Issue, Publisher. --21Feb2013 Put warning "Bad DOI (expected "10."..)" in DOI result. --21Feb2013 Automatically unbolded volume+comma when > 4 long. --21Feb2013 Changed to allow lowercase "asin-tld". --22Feb2013 Fixed ref=harv to extract Year from Date. --22Feb2013 Set Harvard refer. span id if config.CitationClass "citation". --22Feb2013 Fixed config.CitationClass "citation" as span class="citation". --22Feb2013 Capitalized "Archived/Retrieved" only when sepc is dot ".". --23Feb2013 Fixed author editor for "in" or "In" and put space after sepc. --23Feb2013 Changed to omit dot in "et al." when sepc is "." separator. --23Feb2013 Fixed "author1-first" to also get args.given or args.given1. --23Feb2013 Fixed args.article to set Title, after Periodical is Title. --23Feb2013 Fixed to allow blank Title (such as "contribution=mytitle"). --23Feb2013 Fixed double-dot ".." at end of Editors list --26Feb2013 Moved "issue=" data to show before "page=". --26Feb2013 Moved "type=" data to show after "format=". --26Feb2013 For "pmc=" link, omitted suffix "/?tool=pmcentrez". --27Feb2013 For coauthors, omitted extra separator after authors. --27Feb2013 For date, allowed empty date to use month/day/year. --27Feb2013 Fixed double-dot ".." at end of authors/coauthors list. --27Feb2013 Reset editor suffix as ", ed." when date exists. --27Feb2013 Removed duplicate display of "others=" data. --27Feb2013 Removed parentheses "" around "department" TitleNote. --05Mar2013 Moved Language to follow Periodical or Series. --05Mar2013 Fixed Edition to follow Series or Volume. --05Mar2013 Fixed class encyclopaedia to show article as quoted Chapter. --05Mar2013 Fixed class encyclopaedia to show page as "pp." or "p.". --07Mar2013 Changed class encyclopaedia to omit "" around publisher. --07Mar2013 Fixed end double-dot by string.sub(idcommon,-1,-1) was "-1,1". --13Mar2013 Removed enddot "." after "quote=" parameter. --13Mar2013 Changed config.CitationClass "news" to use "p." page format. --13Mar2013 Fixed missing "location=" when "web" or "encyclopaedia". --14Mar2013 Fixed end double-dot after book/work title. --14Mar2013 Fixed double-dot before "p." or "pp." page number. --14Mar2013 Fixed config.CitationClass "book" to use p./pp. page. --18Mar2013 Fixed "page=" to override "pages=" as in markup-based cites. --19Mar2013 Fixed date of class=journal Periodical to show after page. --19Mar2013 Changed null "postscript=" to suppress end-dot of citation. --20Mar2013 If CitationClass is journal, show "others=" before title. --20Mar2013 If CitationClass is book, show "others=" before edition. --20Mar2013 If CitationClass is journal, adjust "others=" to have sepc. --20Mar2013 For class "journal", use book format unless Periodical set. --03Apr2013 Changed safejoin to omit "." after wikilink ".]]" end dot. --03Apr2013 Changed safejoin to omit "." after external ".]" end dot. --03Apr2013 Changed safejoin to omit "." at italic wikilink ".]]" end. --03Apr2013 Changed safejoin to omit "." at italic external ".]" end. --04Apr2013 Moved sepc before for "..". -- --End