Module:Sandbox/Liangent/zhwiki/Module:Citation/CS1

local z = {

error_categories = {};

error_ids = {};

message_tail = {};

}

-- Include translation message hooks, ID and error handling configuration settings.

-- Note that require has tested to be significantly faster than loadData for this

-- usage. This might be a side effect of the unnecessary cloning described

-- in bugzilla 47300.

local cfg = require( 'Module:Citation/CS1/Configuration' );

-- Contains a list of all recognized parameters

local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' );

-- LOCAL

function is_zh( str )

return not not str:find( '[\128-\255]' )

end

-- END LOCAL

-- Whether variable is set or not

function is_set( var )

return not (var == nil or var == '');

end

-- First set variable or nil if none

function first_set(...)

local list = {...};

for _, var in pairs(list) do

if is_set( var ) then

return var;

end

end

end

-- Whether needle is in haystack

function 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

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

end

-- Wraps a string using a message_list configuration taking one argument

function wrap( key, str )

if not is_set( str ) then

return "";

elseif inArray( key, { 'italic-title', 'trans-italic-title' } ) then

str = safeforitalics( str );

end

return substitute( cfg.messages[key], {str} );

end

--[[

Argument wrapper. This function provides support for argument

mapping defined in the configuration file so that multiple names

can be transparently aliased to single internal variable.

]]

function argument_wrapper( args )

local origin = {};

return setmetatable({

ORIGIN = function( self, k )

local dummy = self[k]; --force the variable to be loaded.

return origin[k];

end

},

{

__index = function ( tbl, k )

if origin[k] ~= nil then

return nil;

end

local args, list, v = args, cfg.aliases[k];

if list == nil then

error( cfg.messages['unknown_argument_map'] );

elseif type( list ) == 'string' then

v, origin[k] = args[list], list;

else

v, origin[k] = selectone( args, list, 'redundant_parameters' );

if origin[k] == nil then

origin[k] = ''; --Empty string, not nil;

end

end

if v == nil then

v = cfg.defaults[k] or "";

origin[k] = ''; --Empty string, not nil;

end

tbl = rawset( tbl, k, v );

return v;

end,

});

end

-- Checks that parameter name is valid using the whitelist

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 )

return wrap( hidden and 'hidden-error' or 'visible-error', content );

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, 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 link'] ..

"#" .. error_state.anchor .. "|" ..

cfg.messages['help page label'] .. "]])";

z.error_ids[ error_id ] = true;

if inArray( error_id, { 'bare_url_missing_title', 'trans_missing_title' } )

and z.error_ids['citation_missing_title'] then

return '', false;

end

message = table.concat({ prefix, message, suffix });

if raw == true then

return message, error_state.hidden;

end

return errorcomment( message, error_state.hidden );

end

-- Formats a wiki style external link

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

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

function 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', { wrap( 'parameter', source ) }, 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({ "[", URL, " ", safeforurl( label ), "]", error_str });

end

-- Formats a link to Amazon

function 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 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 is_set(inactive) then

text = "" .. handler.label .. ":" .. id;

table.insert( z.error_categories, "Pages with DOIs inactive since " .. selectyear(inactive) );

inactive = " (" .. cfg.messages['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 )

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

function cleanisbn( isbn_str )

return isbn_str:gsub( "[^-0-9X]", "" );

end

-- Determines whether an ISBN string is valid

function checkisbn( isbn_str )

isbn_str = cleanisbn( isbn_str ):gsub( "-", "" );

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 B or B gives B

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

function safeforurl( str )

do return str end -- LOCAL HACK https://en.wikipedia.org/w/index.php?title=Module_talk:Citation/CS1&diff=552318417&oldid=552202448

if str:match( "%[%[.-%]%]" ) ~= nil then

table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } );

end

return str:gsub( '[%[%]\n]', {

['['] = '[',

[']'] = ']',

['\n'] = ' ' } );

end

-- Converts a hyphen to a dash

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

--[[ 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 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', ' ' );

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

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.floor(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;

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

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 = is_zh( result ) and cfg.messages['et al'] or 'et al.'; -- LOCAL

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" .. table.concat( options );

end

-- Gets name list from the input arguments

function 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] = {

last = last,

first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ),

link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ),

mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i )

};

i = i + 1;

end

return names;

end

-- Populates ID table from arguments using configuration settings

function 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 { __index = function(t,i) return cfg.id_handlers[k][i] end } end;

for k, v in pairs( id_list ) do

-- fallback to read-only cfg

handler = setmetatable( { ['id'] = v }, fallback(k) );

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

error( cfg.messages['unknown_ID_mode'] );

elseif 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 not is_set(options.IgnoreISBN) then

ISBN = ISBN .. seterror( 'bad_isbn', {}, false, " ", "" );

end

table.insert( new_list, {handler.label, ISBN } );

else

error( cfg.messages['unknown_manual_ID'] );

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, 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, { seterror( error_condition, {error_str}, true ) } );

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( {}, {

__newindex = function(self, key, value)

if is_set(value) then

rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( removewikilink( value ) ) } );

end

end

});

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{ id, "/", v };

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{ last, ", ", first };

elseif is_set(last) then

OCinSoutput["rft.au"] = last;

end

end

OCinSoutput.rft_id = data.URL;

OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage };

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 foing the majority of the citation

formatting.

]]

function citation0( config, args, frame ) -- LOCAL

--[[

Load Input Parameters

The argment_wrapper facillitates 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

local PSuffix = A['PSuffix']

local PPSuffix = A['PPSuffix']

if ( nil ~= A['NoPP'] ) then PPSuffix = "" PSuffix = "" end

-- END LOCAL

-- 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 = A['PublicationDate'];

local OrigYear = A['OrigYear'];

local Date = A['Date'];

local LayDate = 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 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'];

if ( config.CitationClass == "encyclopaedia" ) then

if not is_set(Chapter) then

if not is_set(Title) then

Title = Periodical;

Periodical = '';

else

Chapter = Title

TransChapter = TransTitle

Title = '';

TransTitle = '';

end

end

end

local Series = A['Series'];

local Volume = A['Volume'];

local Issue = A['Issue'];

local Position = '';

local Page, Pages, At, page_type;

Page = A['Page'];

Pages = hyphentodash( A['Pages'] );

At = A['At'];

if is_set(Page) then

if is_set(Pages) or is_set(At) then

Page = Page .. " " .. seterror('extra_pages');

Pages = '';

At = '';

end

elseif is_set(Pages) then

if is_set(At) then

Pages = Pages .. " " .. seterror('extra_pages');

At = '';

end

end

local Edition = A['Edition'];

local PublicationPlace = A['PublicationPlace']

local Place = A['Place'];

if not is_set(PublicationPlace) and is_set(Place) then

PublicationPlace = Place;

end

if PublicationPlace == Place then Place = ''; end

local PublisherName = A['PublisherName'];

local SubscriptionRequired = A['SubscriptionRequired'];

local Via = A['Via'];

local AccessDate = A['AccessDate'];

local ArchiveDate = 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 ID_list = extractids( args );

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'];

local this_page = mw.title.getCurrentTitle(); --Also used for COinS

if not is_set(no_tracking_cats) then

for k, v in pairs( cfg.uncategorized_namespaces ) do

if this_page.nsText == v then

no_tracking_cats = "true";

break;

end

end

end

if ( config.CitationClass == "journal" ) then

if not is_set(URL) and is_set(ID_list['PMC']) then

local Embargo = A['Embargo'];

if is_set(Embargo) 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'];

URLorigin = cfg.id_handlers['PMC'].parameters[1];

end

else

URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC'];

URLorigin = cfg.id_handlers['PMC'].parameters[1];

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 {{cite conference}}, before generation of COinS data.

if is_set(BookTitle) then

Chapter = Title;

ChapterLink = TitleLink;

TransChapter = TransTitle;

Title = BookTitle;

TitleLink = '';

TransTitle = '';

end

-- Account for the oddity that is {{cite episode}}, before generation of COinS data.

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

-- COinS metadata (see ) for

-- automated parsing of citation information.

local OCinSoutput = COinS{

['Periodical'] = Periodical,

['Chapter'] = Chapter,

['Title'] = Title,

['PublicationPlace'] = PublicationPlace,

['Date'] = first_set(Date, Year, PublicationDate),

['Series'] = Series,

['Volume'] = Volume,

['Issue'] = Issue,

['Pages'] = first_set(Page, Pages, At),

['Edition'] = Edition,

['PublisherName'] = PublisherName,

['URL'] = first_set( URL, ChapterURL ),

['Authors'] = a,

['ID_list'] = ID_list,

['RawPage'] = this_page.prefixedText,

};

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, { seterror('implict_etal_author', {}, true ) } );

elseif not is_set(Maximum) then

Maximum = #a + 1;

end

local control = {

sep = A["AuthorSeparator"] .. " ",

namesep = (first_set(A["AuthorNameSeparator"], A["NameSeparator"]) or "") .. " ",

format = A["AuthorFormat"],

maximum = Maximum,

lastauthoramp = LastAuthorAmp

};

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

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, { seterror('implict_etal_editor', {}, true) } );

elseif not is_set(Maximum) then

Maximum = #e + 1;

end

local control = {

sep = A["EditorSeparator"] .. " ",

namesep = (first_set(A["EditorNameSeparator"], A["NameSeparator"]) or "") .. " ",

format = A['EditorFormat'],

maximum = Maximum,

lastauthoramp = LastAuthorAmp

};

Editors, EditorCount = listpeople(control, e);

else

EditorCount = 1;

end

if not is_set(Date) then

Date = Year;

if is_set(Date) then

local Month = A['Month'];

if is_set(Month) then

Date = Date .. '.' .. Month; -- LOCAL

local Day = A['Day']

if is_set(Day) then Date = Date .. '.' .. Month end -- LOCAL

end

end

end

if inArray(PublicationDate, {Date, Year}) then PublicationDate = ''; end

if not is_set(Date) and is_set(PublicationDate) then

Date = PublicationDate;

PublicationDate = '';

end

-- Captures the value for Date prior to adding parens or other textual transformations

local DateIn = Date;

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 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 is_set(AccessDate) then

table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } );

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, { seterror( 'citation_missing_title', {}, true ) } );

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

-- LOCAL

if not is_set(Language) or Language:lower() == 'zh' or Language:sub( 1, 3 ):lower() == 'zh-' then

xLCFlags = 'zh;zh-hans;zh-hant'

else

xLCFlags = 'R'

end

if is_set(Chapter) then

Chapter = '-{' .. xLCFlags .. '|' .. Chapter .. '}-'

end

if is_set(Title) then

Title = '-{' .. xLCFlags .. '|' .. Title .. '}-'

end

-- END LOCAL

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

if is_set(Title) then -- LOCAL

Chapter = Chapter .. '//' -- LOCAL

else -- LOCAL

Chapter = Chapter .. sepc .. " " -- with end-space

end -- LOCAL

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 = wrap( 'quoted-title', Title );

TransTitle = wrap( 'trans-quoted-title', TransTitle );

elseif inArray(config.CitationClass, {"web","news","pressrelease"}) and

not is_set(Chapter) then

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

if sepc == '.' then

Place = " " .. wrap( 'written', Place ) .. sepc .. " ";

else

Place = " " .. substitute( cfg.messages['written']:lower(), {Place} ) .. sepc .. " ";

end

end

if is_set(Conference) then

if is_set(ConferenceURL) then

Conference = externallink( ConferenceURL, Conference );

end

Conference = " " .. Conference

elseif is_set(ConferenceURL) then

Conference = " " .. 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, {"encyclopaedia","web","book","news"}) then

Pages = ": " .. Pages;

elseif config.CitationClass == "news" then -- LOCAL

Pages = ": (" .. Pages .. ")" -- LOCAL

elseif config.CitationClass == "book" then -- LOCAL

Pages = ": " .. Pages -- LOCAL

elseif tonumber(Pages) ~= nil then

Pages = sepc .." " .. PPrefix .. Pages .. PSuffix; -- LOCAL

else

Pages = sepc .." " .. PPPrefix .. Pages .. PPSuffix; -- LOCAL

end

end

else

if is_set(Periodical) and

not inArray(config.CitationClass, {"encyclopaedia","web","book","news"}) then

Page = ": " .. Page;

else

if config.CitationClass == "news" then -- LOCAL

Page = ": (" .. Page .. ")" -- LOCAL

elseif config.CitationClass == "news" then -- LOCAL

Page = ": " .. Page -- LOCAL

else -- LOCAL

Page = sepc .." " .. PPrefix .. Page .. PSuffix; -- LOCAL

end -- LOCAL

end

end

At = is_set(At) and (sepc .. " " .. At) or "";

Others = is_set(Others) and (sepc .. " " .. Others) or "";

TitleType = is_set(TitleType) and (", " .. TitleType) or ""; -- LOCAL

TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or "";

-- LOCAL

if is_set(Language) then

local langNameInLang = mw.getContentLanguage():getCode()

local languageCode = Language:lower()

if languageCode:sub( 1, 3 ) == 'zh-' then

langNameInLang = languageCode

end

local languageName = mw.language.fetchLanguageName( languageCode, langNameInLang )

if languageName == '' then

languageName = Language

end

Language = " " .. wrap( 'language', languageName )

else Language = "" end

-- END LOCAL

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

------------------------------------ totally unrelated data

if is_set(Via) then Via = " " .. wrap( 'via', Via ); end

if is_set(AccessDate) then

local retrv_text = " " .. cfg.messages['retrieved']

if (sepc ~= ".") then retrv_text = retrv_text:lower() end

AccessDate = '' -- LOCAL .. sepc

.. substitute( retrv_text, {AccessDate} ) .. ''

end

if is_set(SubscriptionRequired) then

SubscriptionRequired = sepc .. " " .. cfg.messages['subscription'];

end

if is_set(ID) then ID = sepc .." ".. ID; end

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

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

elseif PostScript:lower() == "none" then

PostScript = "";

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 = substitute( cfg.messages['archived-not-dead'],

{ externallink( ArchiveURL, arch_text ), ArchiveDate } );

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,

{ externallink( OriginalURL, cfg.messages['original'] ), ArchiveDate } );

else

local arch_text = cfg.messages['archived-missing'];

if sepc ~= "." then arch_text = arch_text:lower() end

Archived = substitute( arch_text,

{ seterror('archive_missing_url'), ArchiveDate } ); -- LOCAL

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, {"encyclopaedia","web","pressrelease"}) 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

--[[ LOCAL

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

--[[ LOCAL

if is_set(PublicationDate) then

PublicationDate = " (" .. wrap( 'published', PublicationDate ) .. ")";

end

]]

if is_set(PublisherName) then

if is_set(PublicationPlace) then

Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName -- LOCAL .. PublicationDate;

else

Publisher = sepc .. " " .. PublisherName -- LOCAL .. PublicationDate;

end

elseif is_set(PublicationPlace) then

Publisher= sepc .. " " .. PublicationPlace -- LOCAL .. PublicationDate;

else

Publisher = '' -- LOCAL .. 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

-- 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, {"journal","citation"}) and is_set(Periodical) then

if is_set(Others) then Others = Others .. sepc .. " " end

tcommon = safejoin( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series,

Edition, Publisher, Agency, Place, Position}, sepc ); -- LOCAL

else

tcommon = safejoin( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series,

Volume, Issue, Others, Edition, Publisher, Agency, Place, Position}, sepc ); -- LOCAL

end

if #ID_list > 0 then

ID_list = safejoin( { sepc .. " ", table.concat( ID_list, sepc .. " " ), ID }, sepc );

else

ID_list = ID;

end

-- LOCAL

local xDate = Date

local pgtext = Page .. Pages .. At

if ( is_set(Periodical) and Date ~= '' and

not inArray(config.CitationClass, {"encyclopaedia","web"}) )

or ( inArray(config.CitationClass, {"book","news"}) ) then

if inArray(config.CitationClass, {"journal","citation"}) and ( Volume ~= or Issue ~= ) then

xDate = xDate .. ',' .. Volume .. Issue

end

xDate = xDate .. pgtext

pgtext = ''

end

if PublicationDate and PublicationDate ~= '' then

xDate = xDate .. ' (' .. PublicationDate .. ')'

end

if OrigYear ~= '' then

xDate = xDate .. OrigYear

end

if AccessDate ~= '' then

xDate = xDate .. ' ' .. AccessDate

end

if xDate ~= '' then

xDate = sepc .. ' ' .. xDate

end

-- END LOCAL

local idcommon = safejoin( { URL, xDate, ID_list, Archived, Via, SubscriptionRequired, Lay, Language, Quote }, sepc ); -- LOCAL

local text;

-- LOCAL local pgtext = Page .. Pages .. At;

if is_set(Authors) then

if is_set(Coauthors) then

Authors = Authors .. A['AuthorSeparator'] .. " " .. Coauthors

end

--[[ LOCAL

if is_set(Date) then

Date = " ("..Date..")" .. OrigYear .. sepc .. " "

else]]if string.sub(Authors,-1,-1) == sepc then

Authors = Authors .. " "

else

Authors = Authors .. sepc .. " "

end

if is_set(Editors) then

local ed_is_zh = is_zh( Editors ) -- LOCAL

local in_text = ed_is_zh and '' or "In " -- LOCAL

if (sepc ~= '.') then in_text = in_text:lower() end

if (string.sub(Editors,-1,-1) == sepc)

or ed_is_zh -- LOCAL

then Editors = in_text .. Editors .. " "

else Editors = in_text .. Editors .. sepc .. " "

end

-- LOCAL

if ed_is_zh then

Editors = Editors .. ' ' .. cfg.messages['in'] .. sepc .. ' '

end

-- END LOCAL

end

text = safejoin( {Authors, Chapter, Editors, tcommon }, sepc ); -- LOCAL

text = safejoin( {text, pgtext, idcommon}, sepc );

elseif is_set(Editors) then

--[[ LOCAL

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

--[[ LOCAL

end

]]

text = safejoin( {Chapter, Editors, tcommon}, sepc ); -- LOCAL

text = safejoin( {text, pgtext, idcommon}, sepc );

else

--[[ LOCAL

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( {Chapter, Place, tcommon}, sepc );

text = safejoin( {text, pgtext, idcommon}, sepc ); -- LOCAL

else

text = safejoin( {Chapter, tcommon}, sepc ); -- LOCAL

text = safejoin( {text, pgtext, idcommon}, sepc );

end

end

if is_set(PostScript) 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 not is_set(Year) then

if is_set(DateIn) then

Year = selectyear( DateIn );

elseif is_set(PublicationDate) then

Year = selectyear( PublicationDate );

end

end

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 is_set(Authors) then

for i,v in ipairs(a) do

names[i] = v.last

if i == 4 then break end

end

elseif is_set(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 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, {"", "no", "false", "n"}) then

for _, v in ipairs( z.error_categories ) do

text = text .. 'Category:' .. v ..'';

end

end

return text

end

-- This is used by templates such as {{cite book}} to create the actual citation text.

function z.citation(frame)

local pframe = frame:getParent()

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', {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 args[k] ~= nil or (k == 'postscript') then

args[k] = v;

end

end

return citation0( config, args, frame ) -- LOCAL

end

return z