Revision as of 23:07, 19 April 2013 view sourceDragons flight (talk | contribs)Edit filter managers, Extended confirmed users, Rollbackers, Template editors25,792 edits sync with sandbox, merge cite_web_title to bare_url, better style control on translations, category suppression by config, tweak display authors← Previous edit | Revision as of 18:42, 25 April 2013 view source Dragons flight (talk | contribs)Edit filter managers, Extended confirmed users, Rollbackers, Template editors25,792 edits sync to sandbox, mostly code cleaning / organization. Also, ref = none, layurl as alias for laysummary, tweaks to COinS output, and changes to bareurl error.Next edit → | ||
Line 4: | Line 4: | ||
message_tail = {}; | message_tail = {}; | ||
} | } | ||
local SEEN = {}; | |||
local DATA = {}; | |||
-- Include translation message hooks, ID and error handling configuration settings. | -- Include translation message hooks, ID and error handling configuration settings. | ||
-- Note that require has tested to be significantly faster than loadData for this | |||
local cfg = require( 'Module:Citation/CS1/Configuration' ); | |||
-- usage. This might be a side effect of the unnecessary cloning described | |||
-- in bugzilla 47300. | |||
local cfg = require( 'Module:Citation/CS1/Configuration/sandbox' ); | |||
-- Contains a list of all recognized parameters | -- Contains a list of all recognized parameters | ||
local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' ); | local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' ); | ||
-- Whether variable is set or not | |||
-- Populates numbered arguments in a message string using | |||
function is_set( var ) | |||
-- an argument table. | |||
return not (var == nil or var == ''); | |||
function substitute( message, arguments ) | |||
end | |||
if arguments == nil then | |||
return message; | |||
-- 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 | ||
end | |||
message = message .. " "; | |||
-- Whether needle is in haystack | |||
for k, v in ipairs( arguments ) do | |||
function inArray( needle, haystack ) | |||
v = v:gsub( "%%", "%%%%" ); | |||
if needle == nil then | |||
message = message:gsub( "$" .. k .. "(%D)", v .. "%1" ); | |||
|
return false; | ||
end | |||
message = message:sub(1,-2); | |||
for n,v in ipairs( haystack ) do | |||
return message; | |||
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 | end | ||
-- Wraps a string using a message_list configuration taking one argument | -- Wraps a string using a message_list configuration taking one argument | ||
function wrap( |
function wrap( key, str ) | ||
if |
if not is_set( str ) then | ||
return ""; | return ""; | ||
elseif inArray( key, { 'italic-title', 'trans-italic-title' } ) then | |||
end | |||
if message_key == 'italic-title' or | |||
message_key == 'trans-italic-title' then | |||
str = safeforitalics( str ); | str = safeforitalics( str ); | ||
end | end | ||
return substitute( cfg. |
return substitute( cfg.messages, {str} ); | ||
end | end | ||
Line 48: | Line 63: | ||
]] | ]] | ||
function argument_wrapper( args ) | function argument_wrapper( args ) | ||
|
local origin = {}; | ||
local tbl = {}; | |||
|
return setmetatable({ | ||
|
ORIGIN = function( self, k ) | ||
|
local dummy = self; --force the variable to be loaded. | ||
return origin; | |||
end | |||
}, | |||
{ | |||
__index = function ( tbl, k ) | |||
if origin ~= nil then | |||
return nil; | return nil; | ||
end | end | ||
local list = cfg. |
local args, list, v = args, cfg.aliases; | ||
if list == nil then | if list == nil then | ||
error( cfg. |
error( cfg.messages ); | ||
elseif type( list ) == 'string' then | elseif type( list ) == 'string' then | ||
v = |
v, origin = args, list; | ||
else |
else | ||
v = selectone( |
v, origin = selectone( args, list, 'redundant_parameters' ); | ||
|
if origin == nil then | ||
origin = ''; --Empty string, not nil; | |||
end | |||
end | end | ||
if v == nil then | if v == nil then | ||
v = cfg. |
v = cfg.defaults or ""; | ||
origin = ''; --Empty string, not nil; | |||
end | end | ||
SEEN = true; | |||
tbl = rawset( tbl, k, v ); | |||
tbl = rawset( tbl, k, v ); | |||
return v; | return v; | ||
end, | end, | ||
} | }); | ||
return setmetatable( tbl, mt ); | |||
end | end | ||
Line 100: | Line 122: | ||
-- Formats a comment for error trapping | -- Formats a comment for error trapping | ||
function errorcomment( content, hidden ) | function errorcomment( content, hidden ) | ||
return wrap( hidden and 'hidden-error' or 'visible-error', content ); | |||
if hidden then | |||
return wrap( 'hidden-error', content ); | |||
else | |||
return wrap( 'visible-error', content ); | |||
end | |||
end | end | ||
Line 113: | Line 131: | ||
function seterror( error_id, arguments, raw, prefix, suffix ) | function seterror( error_id, arguments, raw, prefix, suffix ) | ||
local error_state = cfg.error_conditions; | local error_state = cfg.error_conditions; | ||
prefix = prefix or ""; | prefix = prefix or ""; | ||
suffix = suffix or ""; | suffix = suffix or ""; | ||
if error_state == nil then | |||
error( cfg.message_list ); | |||
end | |||
if error_state |
if error_state == nil then | ||
error( cfg.messages ); | |||
elseif is_set( error_state.category ) then | |||
table.insert( z.error_categories, error_state.category ); | table.insert( z.error_categories, error_state.category ); | ||
end | end | ||
local message = error_state.message; | local message = substitute( error_state.message, arguments ); | ||
message = substitute( message, arguments ); | |||
message = message .. " ( .. | |||
message = wikiescape(message) .. " ( .. | |||
"#" .. error_state.anchor .. "|" .. | "#" .. error_state.anchor .. "|" .. | ||
cfg. |
cfg.messages .. "]])"; | ||
z.error_ids = true; | z.error_ids = true; | ||
if (error_id |
if inArray( error_id, { 'bare_url_missing_title', 'trans_missing_title' } ) | ||
and z.error_ids then | and z.error_ids then | ||
return '', false; | return '', false; | ||
end | end | ||
message = prefix |
message = table.concat({ prefix, message, suffix }); | ||
if raw == true then | if raw == true then | ||
Line 144: | Line 160: | ||
return errorcomment( message, error_state.hidden ); | 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( '{|}]', { | |||
= '&', | |||
= ''', | |||
= '[', | |||
'] = ']', | |||
= '{', | |||
= '|', | |||
= '}' } ); | |||
return text; | |||
end | end | ||
-- Formats a wiki style external link | -- Formats a wiki style external link | ||
function externallinkid(options) | function externallinkid(options) | ||
local |
local url_string = options.id; | ||
options.suffix = options.suffix or "" | |||
local url_string = options.id | |||
if options.encode == true or options.encode == nil then | if options.encode == true or options.encode == nil then | ||
url_string = mw.uri.encode( url_string ); | url_string = mw.uri.encode( url_string ); | ||
end | end | ||
return mw.ustring.format( ']%s', | |||
|
options.link, options.label, options.separator or " ", | ||
options.prefix, url_string, options.suffix or "", | |||
mw.text.nowiki(options.id) | |||
); | |||
end | end | ||
-- Formats a wiki style internal link | -- Formats a wiki style internal link | ||
function internallinkid(options) | function internallinkid(options) | ||
return mw.ustring.format( ']%s]', | |||
local sep = options.separator or " " | |||
options. |
options.link, options.label, options.separator or " ", | ||
|
options.prefix, options.id, options.suffix or "", | ||
|
mw.text.nowiki(options.id) | ||
); | |||
end | end | ||
-- Format an external link with error checking | -- Format an external link with error checking | ||
function externallink( URL, label ) | function externallink( URL, label, source ) | ||
local error_str = ""; | local error_str = ""; | ||
if |
if not is_set( label ) then | ||
label = URL; | label = URL; | ||
if is_set( source ) then | |||
error_str = seterror( 'bare_url_missing_title', {}, false, " " ); | |||
error_str = seterror( 'bare_url_missing_title', { wrap( 'parameter', source ) }, false, " " ); | |||
else | |||
error( cfg.messages ); | |||
end | |||
end | end | ||
if not checkurl( URL ) then | if not checkurl( URL ) then | ||
error_str = seterror( 'bad_url', {}, false, " " ) .. error_str; | error_str = seterror( 'bad_url', {}, false, " " ) .. error_str; | ||
end | end | ||
return table.concat({ "", error_str }); | |||
return "" .. error_str; | |||
end | end | ||
-- Formats a link to Amazon | -- Formats a link to Amazon | ||
function amazon(id, domain) | function amazon(id, domain) | ||
if ( |
if not is_set(domain) then | ||
domain = "com" | domain = "com" | ||
elseif ( "jp" == domain or "uk" == domain ) then | elseif ( "jp" == domain or "uk" == domain ) then | ||
Line 213: | Line 220: | ||
local text; | local text; | ||
if ( |
if is_set(inactive) then | ||
text = "]:" .. id; | text = "]:" .. id; | ||
table.insert( z.error_categories, "Pages with DOIs inactive since " .. selectyear(inactive) ); | table.insert( z.error_categories, "Pages with DOIs inactive since " .. selectyear(inactive) ); | ||
inactive = " (" .. cfg. |
inactive = " (" .. cfg.messages .. " " .. inactive .. ")" | ||
else | else | ||
text = externallinkid({link = handler.link, label = handler.label, | text = externallinkid({link = handler.link, label = handler.label, | ||
Line 261: | Line 268: | ||
]] | ]] | ||
function checkurl( url_str ) | function checkurl( url_str ) | ||
-- Protocol-relative or URL scheme | |||
if url_str:sub(1,2) == "//" then | |||
return url_str:sub(1,2) == "//" or url_str:match( "^*:" ) ~= nil; | |||
-- 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 | end | ||
Line 312: | Line 311: | ||
-- Gets the display text for a wikilink like ] or ] gives B | -- Gets the display text for a wikilink like ] or ] gives B | ||
function removewikilink( str ) | function removewikilink( str ) | ||
|
return (str:gsub( "%]*)%]%]", function(l) | ||
|
return l:gsub( "^*|(.*)$", "%1" ):gsub("^%s*(.-)%s*$", "%1"); | ||
|
end)); | ||
end | end | ||
Line 324: | Line 323: | ||
return str:gsub( '\n]', { | return str:gsub( '\n]', { | ||
= '[', |
= '[', | ||
'] = ']', |
'] = ']', | ||
= ' ' } ); | = ' ' } ); | ||
end | end | ||
Line 331: | Line 330: | ||
-- Converts a hyphen to a dash | -- Converts a hyphen to a dash | ||
function hyphentodash( str ) | function hyphentodash( str ) | ||
if str |
if not is_set(str) or str:match( "{}<>]" ) ~= nil then | ||
return nil; | |||
end | |||
if str:match( "{}<>]" ) ~= nil then | |||
return str; | return str; | ||
end | end | ||
Line 347: | Line 343: | ||
tend to interact poorly under Mediawiki's HTML tidy. ]] | tend to interact poorly under Mediawiki's HTML tidy. ]] | ||
if |
if not is_set(str) then | ||
return str; | return str; | ||
else | else | ||
Line 439: | Line 435: | ||
-- Is the input a simple number? | -- Is the input a simple number? | ||
local num = tonumber( str ); | local num = tonumber( str ); | ||
if num ~= nil and num > 0 and num < 2100 and num == math. |
if num ~= nil and num > 0 and num < 2100 and num == math.floor(num) then | ||
return str; | return str; | ||
else | else | ||
Line 467: | Line 463: | ||
function listpeople(control, people) | function listpeople(control, people) | ||
local sep = control.sep; | local sep = control.sep; | ||
if sep:sub(-1,-1) ~= " " then sep = sep .. " " end | |||
local namesep = control.namesep | local namesep = control.namesep | ||
local format = control.format | local format = control.format | ||
Line 474: | Line 469: | ||
local text = {} | local text = {} | ||
local etal = false; | local etal = false; | ||
if maximum < 1 then return "", 0; end | |||
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 | for i,person in ipairs(people) do | ||
if (person.last |
if is_set(person.last) then | ||
local mask = person.mask | local mask = person.mask | ||
local one | local one | ||
local sep_one = sep; | local sep_one = sep; | ||
if |
if maximum ~= nil and i > maximum then | ||
etal = true; | etal = true; | ||
break; | break; | ||
Line 494: | Line 492: | ||
one = person.last | one = person.last | ||
local first = person.first | local first = person.first | ||
if (first |
if is_set(first) then | ||
if ( "vanc" == format ) then first = reducetoinitials(first) end | if ( "vanc" == format ) then first = reducetoinitials(first) end | ||
one = one .. namesep .. first | one = one .. namesep .. first | ||
end | end | ||
if (person.link |
if is_set(person.link) then one = "]" end | ||
end | end | ||
table.insert( text, one ) | table.insert( text, one ) | ||
Line 507: | Line 505: | ||
local count = #text / 2; | local count = #text / 2; | ||
if count > 0 then | if count > 0 then | ||
if count > 1 and lastauthoramp |
if count > 1 and is_set(lastauthoramp) and not etal then | ||
text = " & "; | text = " & "; | ||
end | end | ||
Line 515: | Line 513: | ||
local result = table.concat(text) -- construct list | local result = table.concat(text) -- construct list | ||
if etal then | if etal then | ||
local etal_text = cfg. |
local etal_text = cfg.messages; | ||
result = result .. " " .. etal_text; | result = result .. " " .. etal_text; | ||
end | end | ||
Line 528: | Line 526: | ||
-- Generates a CITEREF anchor ID. | -- Generates a CITEREF anchor ID. | ||
function anchorid( options ) | function anchorid( options ) | ||
return "CITEREF" .. |
return "CITEREF" .. table.concat( options ); | ||
end | end | ||
Line 538: | Line 536: | ||
while true do | while true do | ||
last = selectone( args, cfg. |
last = selectone( args, cfg.aliases, 'redundant_parameters', i ); | ||
if |
if not is_set(last) then | ||
-- just in case someone passed in an empty parameter | |||
names = { | |||
last = last, | |||
first = selectone( args, cfg.argument_map, 'redundant_parameters', i ), | |||
link = selectone( args, cfg.argument_map, 'redundant_parameters', i ), | |||
mask = selectone( args, cfg.argument_map, 'redundant_parameters', i ) | |||
} | |||
else | |||
break; | break; | ||
end | end | ||
names = { | |||
last = last, | |||
first = selectone( args, cfg.aliases, 'redundant_parameters', i ), | |||
link = selectone( args, cfg.aliases, 'redundant_parameters', i ), | |||
mask = selectone( args, cfg.aliases, 'redundant_parameters', i ) | |||
}; | |||
i = i + 1; | i = i + 1; | ||
end | end | ||
Line 557: | Line 555: | ||
function extractids( args ) | function extractids( args ) | ||
local id_list = {}; | local id_list = {}; | ||
for k, v in pairs( cfg.id_handlers ) do | for k, v in pairs( cfg.id_handlers ) do | ||
|
v = selectone( args, v.parameters, 'redundant_parameters' ); | ||
if is_set(v) then id_list = v; end | |||
end | end | ||
return id_list; | return id_list; | ||
end | end | ||
Line 567: | Line 564: | ||
-- Takes a table of IDs and turns it into a table of formatted ID outputs. | -- Takes a table of IDs and turns it into a table of formatted ID outputs. | ||
function buildidlist( id_list, options ) | function buildidlist( id_list, options ) | ||
local handler; | local new_list, handler = {}; | ||
local new_list = {}; | |||
function fallback(k) return { __index = function(t,i) return cfg.id_handlers end } end; | |||
for k, v in pairs( id_list ) do | for k, v in pairs( id_list ) do | ||
|
-- fallback to read-only cfg | ||
handler = setmetatable( { = v }, fallback(k) ); | |||
if handler.mode == 'external' then | |||
--Becasue cfg is read-only we have to copy it the hard way. | |||
for k2, v2 in pairs( cfg.id_handlers ) do | |||
handler = v2; | |||
end | |||
handler = v; | |||
if handler.mode == 'external' then | |||
table.insert( new_list, {handler.label, externallinkid( handler ) } ); | table.insert( new_list, {handler.label, externallinkid( handler ) } ); | ||
elseif handler.mode == 'internal' then | elseif handler.mode == 'internal' then | ||
table.insert( new_list, {handler.label, internallinkid( handler ) } ); | table.insert( new_list, {handler.label, internallinkid( handler ) } ); | ||
elseif handler.mode |
elseif handler.mode ~= 'manual' then | ||
|
error( cfg.messages ); | ||
table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } ); | 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.message_list ); | |||
end | |||
else | else | ||
error( cfg. |
error( cfg.messages ); | ||
end | end | ||
end | end | ||
function comp( a, b ) | function comp( a, b ) | ||
return a < b; | return a < b; | ||
end | end | ||
table.sort( new_list, comp ); | table.sort( new_list, comp ); | ||
for k, v in ipairs( new_list ) do | for k, v in ipairs( new_list ) do | ||
Line 622: | Line 613: | ||
local selected = ''; | local selected = ''; | ||
local error_list = {}; | local error_list = {}; | ||
if index ~= nil then index = tostring(index); end | if index ~= nil then index = tostring(index); end | ||
Line 628: | Line 620: | ||
for _, v in ipairs( possible ) do | for _, v in ipairs( possible ) do | ||
v = v:gsub( "#", "" ); | v = v:gsub( "#", "" ); | ||
if args |
if is_set(args) then | ||
if value ~= nil and selected ~= v then | if value ~= nil and selected ~= v then | ||
table.insert( error_list, v ); | table.insert( error_list, v ); | ||
Line 638: | Line 630: | ||
end | end | ||
end | end | ||
for _, v in ipairs( possible ) do | for _, v in ipairs( possible ) do | ||
if index ~= nil then | if index ~= nil then | ||
v = v:gsub( "#", index ); | v = v:gsub( "#", index ); | ||
end | end | ||
if args |
if is_set(args) then | ||
if value ~= nil then | if value ~= nil and selected ~= v then | ||
table.insert( error_list, v ); | table.insert( error_list, v ); | ||
else | else | ||
Line 652: | Line 644: | ||
end | end | ||
end | end | ||
if #error_list > 0 then | if #error_list > 0 then | ||
local error_str = ""; | local error_str = ""; | ||
for _, k in ipairs( error_list ) do | for _, k in ipairs( error_list ) do | ||
if error_str ~= "" then error_str = error_str .. |
if error_str ~= "" then error_str = error_str .. cfg.messages end | ||
error_str = error_str .. |
error_str = error_str .. wrap( 'parameter', k ); | ||
end | end | ||
if #error_list > 1 then | if #error_list > 1 then | ||
error_str = error_str .. |
error_str = error_str .. cfg.messages; | ||
else | else | ||
error_str = error_str .. |
error_str = error_str .. cfg.messages; | ||
end | end | ||
error_str = error_str .. |
error_str = error_str .. wrap( 'parameter', selected ); | ||
table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } ); | table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } ); | ||
end | end | ||
return value, selected; | return value, selected; | ||
end | |||
-- COinS metadata (see <http://ocoins.info/>) 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 = "bookitem"; | |||
OCinSoutput = data.Chapter; | |||
OCinSoutput = data.Title; | |||
elseif is_set(data.Periodical) then | |||
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"; | |||
OCinSoutput = "article"; | |||
OCinSoutput = data.Periodical; | |||
OCinSoutput = data.Title; | |||
else | |||
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; | |||
OCinSoutput = "book" | |||
OCinSoutput = data.Title; | |||
end | |||
OCinSoutput = data.PublicationPlace; | |||
OCinSoutput = data.Date; | |||
OCinSoutput = data.Series; | |||
OCinSoutput = data.Volume; | |||
OCinSoutput = data.Issue; | |||
OCinSoutput = data.Pages; | |||
OCinSoutput = data.Edition; | |||
OCinSoutput = data.PublisherName; | |||
for k, v in pairs( data.ID_list ) do | |||
local id, value = cfg.id_handlers.COinS; | |||
if k == 'ISBN' then value = cleanisbn( v ); else value = v; end | |||
if string.sub( id or "", 1, 4 ) == 'info' then | |||
OCinSoutput = table.concat{ id, "/", v }; | |||
else | |||
OCinSoutput = 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 = last; | |||
end | |||
if is_set(first) then | |||
OCinSoutput = first; | |||
end | |||
end | |||
if is_set(last) and is_set(first) then | |||
OCinSoutput = table.concat{ last, ", ", first }; | |||
elseif is_set(last) then | |||
OCinSoutput = 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 | end | ||
Line 686: | Line 759: | ||
local PPrefix = A | local PPrefix = A | ||
local PPPrefix = A | local PPPrefix = A | ||
if ( |
if is_set( A ) then PPPrefix = "" PPrefix = "" end | ||
-- Pick out the relevant fields from the arguments. Different citation templates | -- Pick out the relevant fields from the arguments. Different citation templates | ||
Line 715: | Line 788: | ||
local TitleType = A; | local TitleType = A; | ||
local ArchiveURL = A; | local ArchiveURL = A; | ||
local URL = A |
local URL = A | ||
local URLorigin = A:ORIGIN('URL'); | |||
local ChapterURL = A; | local ChapterURL = A; | ||
local ChapterURLorigin = A:ORIGIN('ChapterURL'); | |||
local ConferenceURL = A; | local ConferenceURL = A; | ||
local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); | |||
local Periodical = A; | local Periodical = A; | ||
if ( config.CitationClass == "encyclopaedia" ) then | if ( config.CitationClass == "encyclopaedia" ) then | ||
if ( |
if not is_set(Chapter) then | ||
if (Title |
if not is_set(Title) then | ||
Title = Periodical; | Title = Periodical; | ||
Periodical = |
Periodical = ''; | ||
else | else | ||
Chapter = Title | Chapter = Title | ||
TransChapter = TransTitle | TransChapter = TransTitle | ||
Title = |
Title = ''; | ||
TransTitle = |
TransTitle = ''; | ||
end | end | ||
end | end | ||
Line 737: | Line 813: | ||
local Volume = A; | local Volume = A; | ||
local Issue = A; | local Issue = A; | ||
local Position = |
local Position = ''; | ||
local Page, Pages, At, page_type; | local Page, Pages, At, page_type; | ||
Line 743: | Line 819: | ||
Pages = hyphentodash( A ); | Pages = hyphentodash( A ); | ||
At = A; | At = A; | ||
if Page ~= nil then | |||
if is_set(Page) then | |||
if is_set(Pages) or is_set(At) then | |||
Page = Page .. " " .. seterror('extra_pages'); | Page = Page .. " " .. seterror('extra_pages'); | ||
Pages = |
Pages = ''; | ||
At = |
At = ''; | ||
end | end | ||
elseif Pages |
elseif is_set(Pages) then | ||
if At |
if is_set(At) then | ||
Pages = Pages .. " " .. seterror('extra_pages'); | Pages = Pages .. " " .. seterror('extra_pages'); | ||
At = |
At = ''; | ||
end | end | ||
end | end | ||
local Edition = A; | local Edition = A; | ||
local PublicationPlace = A | local PublicationPlace = A | ||
local Place = A; | local Place = A; | ||
if PublicationPlace == nil and Place ~= nil then | |||
if not is_set(PublicationPlace) and is_set(Place) then | |||
PublicationPlace = Place; | PublicationPlace = Place; | ||
end | end | ||
if PublicationPlace == Place then Place = nil end | |||
if PublicationPlace == Place then Place = ''; end | |||
local PublisherName = A; | local PublisherName = A; | ||
Line 772: | Line 851: | ||
local DeadURL = A | local DeadURL = A | ||
local Language = A; | local Language = A; | ||
local Format = A | local Format = A; | ||
local Ref = A | local Ref = A; | ||
local DoiBroken = A | local DoiBroken = A; | ||
local ID = A; | local ID = A; | ||
local ASINTLD = A; | local ASINTLD = A; | ||
local IgnoreISBN = A | local IgnoreISBN = A; | ||
local ID_list = extractids( args ); | local ID_list = extractids( args ); | ||
local Quote = A; | local Quote = A; | ||
local PostScript = A | local PostScript = A; | ||
local |
local LayURL = A; | ||
local LaySource = A; | local LaySource = A; | ||
local Transcript = A; | local Transcript = A; | ||
local TranscriptURL = A |
local TranscriptURL = A | ||
local |
local TranscriptURLorigin = A:ORIGIN('TranscriptURL'); | ||
local |
local sepc = A; | ||
local |
local LastAuthorAmp = A; | ||
local no_tracking_cats = A; | |||
local this_page = mw.title.getCurrentTitle(); --Also used for COinS | local this_page = mw.title.getCurrentTitle(); --Also used for COinS | ||
if no_tracking_cats == "" then | |||
if not is_set(no_tracking_cats) then | |||
for k, v in pairs( cfg.uncategorized_namespaces ) do | for k, v in pairs( cfg.uncategorized_namespaces ) do | ||
if this_page.nsText == v then | if this_page.nsText == v then | ||
Line 802: | Line 883: | ||
end | end | ||
if ( config.CitationClass == "journal" ) then |
if ( config.CitationClass == "journal" ) then | ||
if (URL |
if not is_set(URL) and is_set(ID_list) then | ||
|
local Embargo = A; | ||
|
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; | |||
if good1 and good2 and tonumber( result1 ) < tonumber( result2 ) then | |||
|
URLorigin = cfg.id_handlers.parameters; | ||
end | |||
else | |||
URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list; | |||
end | end | ||
else | |||
URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list; | |||
URLorigin = cfg.id_handlers.parameters; | |||
end | end | ||
end | end | ||
Line 825: | Line 906: | ||
-- Account for the oddity that is {{cite conference}}, before generation of COinS data. | -- Account for the oddity that is {{cite conference}}, before generation of COinS data. | ||
if ( |
if is_set(BookTitle) then | ||
Chapter = Title | Chapter = Title; | ||
ChapterLink = TitleLink | ChapterLink = TitleLink; | ||
TransChapter = TransTitle | TransChapter = TransTitle; | ||
Title = BookTitle | Title = BookTitle; | ||
TitleLink = |
TitleLink = ''; | ||
TransTitle = |
TransTitle = ''; | ||
end | end | ||
-- Account for the oddity that is {{cite episode}}, before generation of COinS data. | -- Account for the oddity that is {{cite episode}}, before generation of COinS data. | ||
if config.CitationClass == "episode" then | if config.CitationClass == "episode" then | ||
local AirDate = A | local AirDate = A; | ||
local SeriesLink = A | local SeriesLink = A; | ||
local Season = A | local Season = A; | ||
local SeriesNumber = A | local SeriesNumber = A; | ||
local Network = A | local Network = A; | ||
local Station = A | local Station = A; | ||
local s = {} | local s, n = {}, {}; | ||
local Sep = (first_set(A, A) or "") .. " "; | |||
if Issue ~= nil then table.insert(s, cfg.message_list .. " " .. Issue) Issue = nil end | |||
if Season ~= nil then table.insert(s, cfg.message_list .. " " .. Season) end | |||
if |
if is_set(Issue) then table.insert(s, cfg.messages .. " " .. Issue); Issue = ''; end | ||
if is_set(Season) then table.insert(s, cfg.messages .. " " .. Season); end | |||
local n = {} | |||
if |
if is_set(SeriesNumber) then table.insert(s, cfg.messages .. " " .. SeriesNumber); end | ||
if |
if is_set(Network) then table.insert(n, Network); end | ||
if is_set(Station) then table.insert(n, Station); end | |||
Date = Date or AirDate | |||
|
|||
|
Date = Date or AirDate; | ||
|
Chapter = Title; | ||
|
ChapterLink = TitleLink; | ||
|
TransChapter = TransTitle; | ||
|
Title = Series; | ||
TitleLink = SeriesLink; | |||
local Sep = (A or A) .. " " | |||
|
TransTitle = ''; | ||
|
|||
Series = table.concat(s, Sep); | |||
ID = table.concat(n, Sep); | |||
end | end | ||
-- |
-- COinS metadata (see <http://ocoins.info/>) for | ||
-- automated |
-- automated parsing of citation information. | ||
local OCinSoutput = COinS{ | |||
local OCinSdata = {} -- COinS metadata excluding id, bibcode, doi, etc. | |||
= Periodical, | |||
local ctx_ver = "Z39.88-2004" | |||
= Chapter, | |||
OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:book" | |||
|
= Title, | ||
= PublicationPlace, | |||
OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal" | |||
= first_set(Date, Year, PublicationDate), | |||
OCinSdata = "article" | |||
|
= Series, | ||
= Volume, | |||
if ( nil ~= Title ) then OCinSdata = Title end | |||
= Issue, | |||
end | |||
= first_set(Page, Pages, At), | |||
if ( nil ~= Chapter and "" ~= Chapter) then | |||
= Edition, | |||
OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:book" | |||
|
= PublisherName, | ||
|
= first_set( URL, ChapterURL ), | ||
= a, | |||
if ( nil ~= Title ) then OCinSdata = Title end | |||
= ID_list, | |||
else | |||
|
= this_page.prefixedText, | ||
}; | |||
if ( nil ~= Title ) then OCinSdata = Title end | |||
end | |||
OCinSdata = PublicationPlace | |||
OCinSdata = Date or Year or PublicationDate | |||
OCinSdata = Series | |||
OCinSdata = Volume | |||
OCinSdata = Issue | |||
OCinSdata = Page or Pages or At | |||
OCinSdata = Edition | |||
OCinSdata = PublisherName | |||
for k, v in pairs( ID_list ) do | |||
if k == 'ISBN' then | |||
v = cleanisbn( v ); | |||
end | |||
if string.sub( cfg.id_handlers.COinS or "info", 1, 4 ) ~= 'info' then | |||
OCinSdata.COinS ] = v; | |||
end | |||
end | |||
OCinSdata.rft_id = URL or ChapterURL | |||
if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then | |||
local last, first; | |||
|
Chapter = Title; | ||
|
ChapterLink = TitleLink; | ||
|
TransChapter = TransTitle; | ||
|
Title = ''; | ||
|
TitleLink = ''; | ||
|
TransTitle = ''; | ||
OCinSdata = last; | |||
end | |||
if first ~= nil then | |||
OCinSdata = first; | |||
end | |||
end | |||
if last ~= nil and first ~= nil then | |||
table.insert( OCinSauthors, last .. ", " .. 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.COinS or "", 1, 4 ) == 'info' then | |||
OCinSids.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 | |||
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 | end | ||
Line 955: | Line 975: | ||
-- We also add leading spaces and surrounding markup and punctuation to the | -- We also add leading spaces and surrounding markup and punctuation to the | ||
-- various parts of the citation, but only when they are non-nil. | -- various parts of the citation, but only when they are non-nil. | ||
if ( |
if not is_set(Authors) then | ||
local Maximum = tonumber( A ); | local Maximum = tonumber( A ); | ||
-- Preserve old-style implicit et al. | -- Preserve old-style implicit et al. | ||
if Maximum |
if not is_set(Maximum) and #a == 9 then | ||
Maximum = 8; | Maximum = 8; | ||
table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } ); | table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } ); | ||
elseif Maximum |
elseif not is_set(Maximum) then | ||
Maximum = #a + 1; | Maximum = #a + 1; | ||
end | end | ||
Line 968: | Line 988: | ||
local control = { | local control = { | ||
sep = A .. " ", | sep = A .. " ", | ||
namesep = (A |
namesep = (first_set(A, A) or "") .. " ", | ||
format = A, | format = A, | ||
maximum = Maximum, | maximum = Maximum, | ||
lastauthoramp = LastAuthorAmp | lastauthoramp = LastAuthorAmp | ||
} | }; | ||
-- If the coauthor field is also used, prevent ampersand and et al. formatting. | -- If the coauthor field is also used, prevent ampersand and et al. formatting. | ||
if Coauthors |
if is_set(Coauthors) then | ||
control.lastauthoramp = nil; | control.lastauthoramp = nil; | ||
control.maximum = #a + 1; | control.maximum = #a + 1; | ||
end | end | ||
Authors = listpeople(control, a) | Authors = listpeople(control, a) | ||
end | end | ||
local EditorCount | local EditorCount | ||
if ( |
if not is_set(Editors) then | ||
local Maximum = tonumber( A ); | local Maximum = tonumber( A ); | ||
-- Preserve old-style implicit et al. | -- Preserve old-style implicit et al. | ||
if Maximum |
if not is_set(Maximum) and #e == 4 then | ||
Maximum = 3; | Maximum = 3; | ||
table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } ); | table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } ); | ||
elseif Maximum |
elseif not is_set(Maximum) then | ||
Maximum = #e + 1; | Maximum = #e + 1; | ||
end | end | ||
Line 996: | Line 1,016: | ||
local control = { | local control = { | ||
sep = A .. " ", | sep = A .. " ", | ||
namesep = (A |
namesep = (first_set(A, A) or "") .. " ", | ||
format = A, | format = A, | ||
maximum = Maximum, | maximum = Maximum, | ||
lastauthoramp = LastAuthorAmp | lastauthoramp = LastAuthorAmp | ||
}; | |||
Editors, EditorCount = listpeople(control, e) |
Editors, EditorCount = listpeople(control, e); | ||
else | else | ||
EditorCount = 1; | EditorCount = 1; | ||
end | end | ||
if ( Date == nil or Date == "") then | |||
if not is_set(Date) then | |||
-- there's something hinky with how this adds dashes to perfectly-good free-standing years | |||
Date = Year; | |||
if ( |
if is_set(Date) then | ||
local Month = |
local Month = A; | ||
if ( |
if is_set(Month) then | ||
|
Date = Month .. " " .. Date; | ||
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 = A | |||
if ( Month ~= nil and Month ~= "") then | |||
Date = Month .. " " .. Date | |||
local Day = A | local Day = A | ||
if ( |
if is_set(Day) then Date = Day .. " " .. Date end | ||
else Month = "" | |||
end | end | ||
else Date = "" | |||
end | end | ||
end | end | ||
if ( PublicationDate == Date or PublicationDate == Year ) then PublicationDate = nil end | |||
if |
if inArray(PublicationDate, {Date, Year}) then PublicationDate = ''; end | ||
if not is_set(Date) and is_set(PublicationDate) then | |||
Date = PublicationDate; | Date = PublicationDate; | ||
PublicationDate = |
PublicationDate = ''; | ||
end |
end | ||
-- Captures the value for Date prior to adding parens or other textual transformations | -- Captures the value for Date prior to adding parens or other textual transformations | ||
local DateIn = Date | local DateIn = Date; | ||
if |
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 | -- Test if cite web is called without giving a URL | ||
if ( config.CitationClass == "web" ) then | if ( config.CitationClass == "web" ) then | ||
table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } ); | table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } ); | ||
end | end | ||
-- Test if accessdate is given without giving a URL | -- Test if accessdate is given without giving a URL | ||
if ( |
if is_set(AccessDate) then | ||
table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } ); | table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } ); | ||
AccessDate = |
AccessDate = ''; | ||
end | end | ||
-- Test if format is given without giving a URL | -- Test if format is given without giving a URL | ||
if ( |
if is_set(Format) then | ||
Format = Format .. seterror( 'format_missing_url' ); | Format = Format .. seterror( 'format_missing_url' ); | ||
end | end | ||
end | |||
end | |||
-- Test if citation has no title | -- Test if citation has no title | ||
if |
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 ) } ); | table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } ); | ||
end | end | ||
|
Format = is_set(Format) and " (" .. Format .. ")" or ""; | ||
Format = " (" .. Format .. ")" else Format = "" end | |||
local OriginalURL = URL | local OriginalURL = URL | ||
Line 1,089: | Line 1,090: | ||
end | end | ||
end | end | ||
-- Format chapter / article title | -- Format chapter / article title | ||
if ( |
if is_set(Chapter) and is_set(ChapterLink) then | ||
Chapter = "]"; | Chapter = "]"; | ||
end | end | ||
if ( |
if is_set(Periodical) and is_set(Title) then | ||
Chapter = wrap( 'italic-title', Chapter ); | Chapter = wrap( 'italic-title', Chapter ); | ||
TransChapter = wrap( 'trans-italic-title', TransChapter ); | TransChapter = wrap( 'trans-italic-title', TransChapter ); | ||
Line 1,103: | Line 1,104: | ||
local TransError = "" | local TransError = "" | ||
if TransChapter |
if is_set(TransChapter) then | ||
TransError = " " .. seterror( 'trans_missing_chapter' ); | if not is_set(Chapter) then | ||
TransError = " " .. seterror( 'trans_missing_chapter' ); | |||
else | |||
TransChapter = " " .. TransChapter; | |||
end | |||
end | end | ||
|
Chapter = Chapter .. TransChapter; | ||
Chapter = Chapter .. TransChapter | |||
if Chapter |
if is_set(Chapter) then | ||
if ( |
if not is_set(ChapterLink) then | ||
if ( |
if is_set(ChapterURL) then | ||
Chapter = externallink( ChapterURL, Chapter ) .. TransError; | Chapter = externallink( ChapterURL, Chapter ) .. TransError; | ||
if |
if not is_set(URL) then | ||
Chapter = Chapter .. Format; | Chapter = Chapter .. Format; | ||
Format = ""; | Format = ""; | ||
end | end | ||
elseif ( |
elseif is_set(URL) then | ||
Chapter = externallink( URL, Chapter ) .. TransError .. Format; | Chapter = externallink( URL, Chapter ) .. TransError .. Format; | ||
URL = |
URL = ""; | ||
Format = "" | Format = ""; | ||
else | else | ||
Chapter = Chapter .. TransError; | Chapter = Chapter .. TransError; | ||
end | end | ||
elseif ChapterURL |
elseif is_set(ChapterURL) then | ||
Chapter = Chapter .. " " .. externallink( ChapterURL ) .. | Chapter = Chapter .. " " .. externallink( ChapterURL, nil, ChapterURLorigin ) .. | ||
TransError; | TransError; | ||
else | else | ||
Line 1,132: | Line 1,136: | ||
end | end | ||
Chapter = Chapter .. sepc .. " " -- with end-space | Chapter = Chapter .. sepc .. " " -- with end-space | ||
elseif ChapterURL |
elseif is_set(ChapterURL) then | ||
Chapter = " " .. externallink( ChapterURL ) .. sepc .. " "; | Chapter = " " .. externallink( ChapterURL, nil, ChapterURLorigin ) .. sepc .. " "; | ||
end | end | ||
-- Format main title. | -- Format main title. | ||
if ( |
if is_set(TitleLink) and is_set(Title) then | ||
|
Title = "]" | ||
Title = "]" | |||
end | |||
end | end | ||
if ( |
if is_set(Periodical) then | ||
Title = wrap( 'quoted-title', Title ); | Title = wrap( 'quoted-title', Title ); | ||
TransTitle = wrap( 'trans-quoted-title', TransTitle ); | TransTitle = wrap( 'trans-quoted-title', TransTitle ); | ||
elseif ( |
elseif inArray(config.CitationClass, {"web","news","pressrelease"}) and | ||
|
not is_set(Chapter) then | ||
or config.CitationClass == "pressrelease" ) and | |||
Chapter == "" then | |||
Title = wrap( 'quoted-title', Title ); | Title = wrap( 'quoted-title', Title ); | ||
TransTitle = wrap( 'trans-quoted-title', TransTitle ); | TransTitle = wrap( 'trans-quoted-title', TransTitle ); | ||
Line 1,157: | Line 1,157: | ||
end | end | ||
|
TransError = ""; | ||
if TransTitle |
if is_set(TransTitle) then | ||
TransError = " " .. seterror( 'trans_missing_title' ); | if not is_set(Title) then | ||
TransError = " " .. seterror( 'trans_missing_title' ); | |||
else | |||
TransTitle = " " .. TransTitle; | |||
end | |||
end | end | ||
|
Title = Title .. TransTitle; | ||
Title = Title .. TransTitle | |||
if Title |
if is_set(Title) then | ||
if ( |
if not is_set(TitleLink) and is_set(URL) then | ||
Title = externallink( URL, Title ) .. TransError .. Format | Title = externallink( URL, Title ) .. TransError .. Format | ||
URL = |
URL = ""; | ||
Format = |
Format = ""; | ||
else | else | ||
Title = Title .. TransError; | Title = Title .. TransError; | ||
end | end | ||
end | end | ||
if ( |
if is_set(Place) then | ||
if sepc == '.' then | if sepc == '.' then | ||
Place = " " .. wrap( 'written', Place ) .. sepc .. " "; | Place = " " .. wrap( 'written', Place ) .. sepc .. " "; | ||
else | else | ||
Place = " " .. substitute( cfg. |
Place = " " .. substitute( cfg.messages:lower(), {Place} ) .. sepc .. " "; | ||
end |
end | ||
else | |||
Place = ""; | |||
end | end | ||
if ( |
if is_set(Conference) then | ||
if ( |
if is_set(ConferenceURL) then | ||
Conference = externallink( ConferenceURL, Conference ); | Conference = externallink( ConferenceURL, Conference ); | ||
end | end | ||
Conference = " " .. Conference | Conference = " " .. Conference | ||
elseif ConferenceURL |
elseif is_set(ConferenceURL) then | ||
Conference = " " .. externallink( ConferenceURL ); | Conference = " " .. externallink( ConferenceURL, nil, ConferenceURLorigin ); | ||
else | |||
Conference = "" | |||
end | end | ||
if ( nil ~= Position or nil ~= Page or nil ~= Pages ) then At = nil end | |||
if ( |
if not is_set(Position) then | ||
local Minutes = A; | local Minutes = A; | ||
if ( |
if is_set(Minutes) then | ||
Position = " " .. Minutes .. " " .. cfg. |
Position = " " .. Minutes .. " " .. cfg.messages; | ||
else | else | ||
local Time = A; | local Time = A; | ||
if ( |
if is_set(Time) then | ||
local TimeCaption = A | local TimeCaption = A | ||
if TimeCaption |
if not is_set(TimeCaption) then | ||
TimeCaption = cfg. |
TimeCaption = cfg.messages; | ||
if sepc ~= '.' then | if sepc ~= '.' then | ||
TimeCaption = TimeCaption:lower(); | TimeCaption = TimeCaption:lower(); | ||
end | end | ||
end |
end | ||
Position = " " .. TimeCaption .. " " .. Time | Position = " " .. TimeCaption .. " " .. Time; | ||
else | |||
Position = "" | |||
end | end | ||
end | end | ||
else | else | ||
Position = " " .. Position | Position = " " .. Position; | ||
At = ''; | |||
end | end | ||
if ( nil == Page or "" == Page ) then | |||
|
if not is_set(Page) then | ||
if ( |
if is_set(Pages) then | ||
|
if is_set(Periodical) and | ||
not inArray(config.CitationClass, {"encyclopaedia","web","book","news"}) then | |||
elseif ( Periodical ~= nil and Periodical ~= "" and | |||
|
Pages = ": " .. Pages; | ||
|
elseif tonumber(Pages) ~= nil then | ||
|
Pages = sepc .." " .. PPrefix .. Pages; | ||
|
else | ||
Pages = " |
Pages = sepc .." " .. PPPrefix .. Pages; | ||
else | |||
if ( tonumber(Pages) ~= nil ) then | |||
Pages = sepc .." " .. PPrefix .. Pages | |||
else Pages = sepc .." " .. PPPrefix .. Pages | |||
end | end | ||
end | end | ||
else | else | ||
|
if is_set(Periodical) and | ||
not inArray(config.CitationClass, {"encyclopaedia","web","book","news"}) then | |||
if ( Periodical ~= nil and Periodical ~= "" and | |||
|
Page = ": " .. Page; | ||
config.CitationClass ~= "web" and | |||
config.CitationClass ~= "book" and | |||
config.CitationClass ~= "news") then | |||
Page = ": " .. Page | |||
else | else | ||
Page = sepc .." " .. PPrefix .. Page | Page = sepc .." " .. PPrefix .. Page; | ||
end | end | ||
end | end | ||
if ( At ~= nil and At ~="") then At = sepc .. " " .. At | |||
|
At = is_set(At) and (sepc .. " " .. At) or ""; | ||
Others = is_set(Others) and (sepc .. " " .. Others) or ""; | |||
if ( Coauthors == nil ) then Coauthors = "" end | |||
|
TitleType = is_set(TitleType) and (" (" .. TitleType .. ")") or ""; | ||
|
TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""; | ||
Language = is_set(Language) and (" " .. wrap( 'language', Language )) or ""; | |||
if ( TitleType ~= nil and TitleType ~="" ) then | |||
Edition = is_set(Edition) and (" " .. wrap( 'edition', Edition )) or ""; | |||
TitleType = " (" .. TitleType .. ")" else TitleType = "" end | |||
|
Issue = is_set(Issue) and (" (" .. Issue .. ")") or ""; | ||
|
Series = is_set(Series) and (sepc .. " " .. Series) or ""; | ||
|
OrigYear = is_set(OrigYear) and (" ") or ""; | ||
|
Agency = is_set(Agency) and (sepc .. " " .. Agency) or ""; | ||
if ( Edition ~= nil and Edition ~="" ) then | |||
if is_set(Volume) then | |||
Edition = " " .. wrap( 'edition', Edition ) else Edition = "" end | |||
if ( Volume ~= nil and Volume ~="" ) | |||
then | |||
if ( mw.ustring.len(Volume) > 4 ) | if ( mw.ustring.len(Volume) > 4 ) | ||
then Volume = sepc .." " .. Volume | then Volume = sepc .." " .. Volume; | ||
else Volume = " <b>" .. hyphentodash(Volume) .. "</b>" | else Volume = " <b>" .. hyphentodash(Volume) .. "</b>"; | ||
end | end | ||
|
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 = " " else OrigYear = "" end | |||
if ( Agency ~= nil and Agency ~="" ) then | |||
Agency = sepc .. " " .. Agency else Agency = "" end | |||
------------------------------------ totally unrelated data | ------------------------------------ totally unrelated data | ||
if ( |
if is_set(Via) then Via = " " .. wrap( 'via', Via ); end | ||
if ( |
if is_set(AccessDate) then | ||
|
local retrv_text = " " .. cfg.messages | ||
if ( |
if (sepc ~= ".") then retrv_text = retrv_text:lower() end | ||
AccessDate = '<span class="reference-accessdate">' .. sepc | |||
then local retrv_text = " " .. cfg.message_list | |||
|
.. substitute( retrv_text, {AccessDate} ) .. '</span>' | ||
AccessDate = '<span class="reference-accessdate">' .. sepc | |||
.. substitute( retrv_text, {AccessDate} ) .. '</span>' | |||
else AccessDate = "" end | |||
if ( SubscriptionRequired ~= nil and | |||
SubscriptionRequired ~= "" ) then | |||
SubscriptionRequired = sepc .. " " .. cfg.message_list; | |||
else | |||
SubscriptionRequired = "" | |||
end | end | ||
if ( ID ~= nil and ID ~="") then ID = sepc .." ".. ID else ID="" end | |||
if is_set(SubscriptionRequired) then | |||
SubscriptionRequired = sepc .. " " .. cfg.messages; | |||
end | |||
if is_set(ID) then ID = sepc .." ".. ID; end | |||
ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN} ); | ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN} ); | ||
if ( |
if is_set(URL) then | ||
URL = " " .. externallink( URL ); | URL = " " .. externallink( URL, nil, URLorigin ); | ||
else | |||
URL = "" | |||
end | end | ||
if ( |
if is_set(Quote) then | ||
if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then | if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then | ||
Quote = Quote:sub(2,-2); | Quote = Quote:sub(2,-2); | ||
end | end | ||
Quote = sepc .." " .. wrap( 'quoted-text', Quote ); | Quote = sepc .." " .. wrap( 'quoted-text', Quote ); | ||
PostScript = "" | PostScript = ""; | ||
elseif PostScript:lower() == "none" then | |||
else | |||
|
PostScript = ""; | ||
Quote = "" | |||
end | end | ||
local Archived | local Archived | ||
if ( |
if is_set(ArchiveURL) then | ||
if ( |
if not is_set(ArchiveDate) then | ||
ArchiveDate = seterror('archive_missing_date'); | ArchiveDate = seterror('archive_missing_date'); | ||
end | end | ||
if |
if "no" == DeadURL then | ||
local arch_text = cfg. |
local arch_text = cfg.messages; | ||
if |
if sepc ~= "." then arch_text = arch_text:lower() end | ||
Archived = sepc .. " " .. substitute( cfg. |
Archived = sepc .. " " .. substitute( cfg.messages, | ||
{ externallink( ArchiveURL, arch_text ), ArchiveDate } ); | { externallink( ArchiveURL, arch_text ), ArchiveDate } ); | ||
if OriginalURL |
if not is_set(OriginalURL) then | ||
Archived = Archived .. " " .. seterror('archive_missing_url'); | Archived = Archived .. " " .. seterror('archive_missing_url'); | ||
end | end | ||
elseif is_set(OriginalURL) then | |||
local arch_text = cfg.messages; | |||
if sepc ~= "." then arch_text = arch_text:lower() end | |||
Archived = sepc .. " " .. substitute( arch_text, | |||
{ externallink( OriginalURL, cfg.messages ), ArchiveDate } ); | |||
else | else | ||
local arch_text = cfg.messages; | |||
if OriginalURL ~= nil and OriginalURL ~= '' then | |||
|
if sepc ~= "." then arch_text = arch_text:lower() end | ||
|
Archived = sepc .. " " .. substitute( arch_text, | ||
{ seterror('archive_missing_url'), ArchiveDate } ); | |||
Archived = sepc .. " " .. substitute( arch_text, | |||
{ externallink( OriginalURL, cfg.message_list ), ArchiveDate } ); | |||
else | |||
local arch_text = cfg.message_list; | |||
if (sepc ~= ".") then arch_text = arch_text:lower() end | |||
Archived = sepc .. " " .. substitute( arch_text, | |||
{ seterror('archive_missing_url'), ArchiveDate } ); | |||
end | |||
end | end | ||
else | else | ||
Archived = "" | Archived = "" | ||
end | end | ||
local Lay | local Lay | ||
if ( |
if is_set(LayURL) then | ||
if ( |
if is_set(LayDate) then LayDate = " (" .. LayDate .. ")" end | ||
if ( |
if is_set(LaySource) then | ||
LaySource = " – ''" .. safeforitalics(LaySource) .. "''" |
LaySource = " – ''" .. safeforitalics(LaySource) .. "''"; | ||
else |
else | ||
LaySource = "" |
LaySource = ""; | ||
end | end | ||
if sepc == '.' then | if sepc == '.' then | ||
Lay = sepc .. " " .. externallink( |
Lay = sepc .. " " .. externallink( LayURL, cfg.messages ) .. LaySource .. LayDate | ||
else | else | ||
Lay = sepc .. " " .. externallink( |
Lay = sepc .. " " .. externallink( LayURL, cfg.messages:lower() ) .. LaySource .. LayDate | ||
end | end | ||
else | else | ||
Lay = "" | Lay = ""; | ||
end | end | ||
if ( nil ~= Transcript and "" ~= Transcript ) then | |||
if is_set(Transcript) then | |||
if ( TranscriptURL ~= nil ) then Transcript = externallink( TranscriptURL, Transcript ) end | |||
|
if is_set(TranscriptURL) then Transcript = externallink( TranscriptURL, Transcript ); end | ||
|
elseif is_set(TranscriptURL) then | ||
Transcript = externallink( TranscriptURL, nil, TranscriptURLorigin ); | |||
else | |||
Transcript = "" | |||
end | end | ||
local Publisher = "" | |||
local Publisher; | |||
if ( Periodical and Periodical ~= "" and | |||
if is_set(Periodical) and | |||
config.CitationClass ~= "encyclopaedia" and | |||
config.CitationClass |
not inArray(config.CitationClass, {"encyclopaedia","web","pressrelease"}) then | ||
|
if is_set(PublisherName) then | ||
|
if is_set(PublicationPlace) then | ||
if (PublicationPlace ~= nil and PublicationPlace ~= '') then | |||
Publisher = PublicationPlace .. ": " .. PublisherName; | Publisher = PublicationPlace .. ": " .. PublisherName; | ||
else | else | ||
Publisher = PublisherName; | Publisher = PublisherName; | ||
end |
end | ||
elseif (PublicationPlace |
elseif is_set(PublicationPlace) then | ||
Publisher= PublicationPlace; | Publisher= PublicationPlace; | ||
else | else | ||
Publisher = ""; | Publisher = ""; | ||
end | end | ||
if ( |
if is_set(PublicationDate) then | ||
if Publisher |
if is_set(Publisher) then | ||
Publisher = Publisher .. ", " .. wrap( 'published', PublicationDate ); | Publisher = Publisher .. ", " .. wrap( 'published', PublicationDate ); | ||
else | else | ||
Line 1,387: | Line 1,358: | ||
end | end | ||
end | end | ||
if Publisher |
if is_set(Publisher) then | ||
Publisher = " (" .. Publisher .. ")"; | Publisher = " (" .. Publisher .. ")"; | ||
end | end | ||
else | else | ||
if ( |
if is_set(PublicationDate) then | ||
PublicationDate = " (" .. wrap( 'published', PublicationDate ) .. ")" | PublicationDate = " (" .. wrap( 'published', PublicationDate ) .. ")"; | ||
else | |||
PublicationDate = "" | |||
end | end | ||
if ( |
if is_set(PublisherName) then | ||
if (PublicationPlace |
if is_set(PublicationPlace) then | ||
Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate; | Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate; | ||
else | else | ||
Publisher = sepc .. " " .. PublisherName .. PublicationDate; | Publisher = sepc .. " " .. PublisherName .. PublicationDate; | ||
end | end | ||
elseif (PublicationPlace |
elseif is_set(PublicationPlace) then | ||
Publisher= sepc .. " " .. PublicationPlace .. PublicationDate; | Publisher= sepc .. " " .. PublicationPlace .. PublicationDate; | ||
else | else | ||
Line 1,408: | Line 1,377: | ||
end | end | ||
end | end | ||
-- Several of the above rely upon detecting this as nil, so do it last. | -- Several of the above rely upon detecting this as nil, so do it last. | ||
if ( |
if is_set(Periodical) then | ||
if ( |
if is_set(Title) or is_set(TitleNote) then | ||
Periodical = sepc .. " " .. wrap( 'italic-title', Periodical ) | Periodical = sepc .. " " .. wrap( 'italic-title', Periodical ) | ||
else | else | ||
Periodical = wrap( 'italic-title', Periodical ) | Periodical = wrap( 'italic-title', Periodical ) | ||
end | end | ||
|
end | ||
-- Piece all bits together at last. Here, all should be non-nil. | -- Piece all bits together at last. Here, all should be non-nil. | ||
Line 1,422: | Line 1,392: | ||
local tcommon | local tcommon | ||
if |
if inArray(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then | ||
|
if is_set(Others) then Others = Others .. sepc .. " " end | ||
if (Others ~= "") then Others = Others .. sepc .. " " end | |||
tcommon = safejoin( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, | tcommon = safejoin( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, | ||
Language, Edition, Publisher, Agency, Volume, Issue, Position}, sepc ); | Language, Edition, Publisher, Agency, Volume, Issue, Position}, sepc ); | ||
Line 1,436: | Line 1,405: | ||
else | else | ||
ID_list = ID; | ID_list = ID; | ||
end | end | ||
local idcommon = safejoin( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc ); | local idcommon = safejoin( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc ); | ||
local text; | |||
local |
local pgtext = Page .. Pages .. At; | ||
local pgtext = Page .. Pages .. At | |||
if ( |
if is_set(Authors) then | ||
if (Coauthors |
if is_set(Coauthors) then | ||
|
Authors = Authors .. A .. " " .. Coauthors | ||
end | end | ||
if ( |
if is_set(Date) then | ||
|
Date = " ("..Date..")" .. OrigYear .. sepc .. " " | ||
elseif string.sub(Authors,-1,-1) == sepc then | |||
else | |||
|
Authors = Authors .. " " | ||
|
else | ||
Authors = Authors .. sepc .. " " | |||
else Authors = Authors .. sepc .. " " | |||
end | |||
end | end | ||
if ( |
if is_set(Editors) then | ||
local in_text = " in " | local in_text = " " .. cfg.messages .. " " | ||
if (sepc |
if (sepc ~= '.') then in_text = in_text:lower() end | ||
if (string.sub(Editors,-1,-1) == sepc) | if (string.sub(Editors,-1,-1) == sepc) | ||
then Editors = in_text .. Editors .. " " | then Editors = in_text .. Editors .. " " | ||
Line 1,464: | Line 1,432: | ||
text = safejoin( {Authors, Date, Chapter, Place, Editors, tcommon }, sepc ); | text = safejoin( {Authors, Date, Chapter, Place, Editors, tcommon }, sepc ); | ||
text = safejoin( {text, pgtext, idcommon}, sepc ); | text = safejoin( {text, pgtext, idcommon}, sepc ); | ||
elseif ( |
elseif is_set(Editors) then | ||
if ( |
if is_set(Date) then | ||
if EditorCount <= 1 then | if EditorCount <= 1 then | ||
Editors = Editors .. ", " .. cfg. |
Editors = Editors .. ", " .. cfg.messages; | ||
else | else | ||
Editors = Editors .. ", " .. cfg. |
Editors = Editors .. ", " .. cfg.messages; | ||
end | end | ||
Date = " (" .. Date ..")" .. OrigYear .. sepc .. " " | Date = " (" .. Date ..")" .. OrigYear .. sepc .. " " | ||
else | else | ||
if EditorCount <= 1 then | if EditorCount <= 1 then | ||
Editors = Editors .. " (" .. cfg. |
Editors = Editors .. " (" .. cfg.messages .. ")" .. sepc .. " " | ||
else | else | ||
Editors = Editors .. " (" .. cfg. |
Editors = Editors .. " (" .. cfg.messages .. ")" .. sepc .. " " | ||
end | end | ||
end | end | ||
Line 1,482: | Line 1,450: | ||
text = safejoin( {text, pgtext, idcommon}, sepc ); | text = safejoin( {text, pgtext, idcommon}, sepc ); | ||
else | else | ||
if ( |
if is_set(Date) then | ||
if ( string.sub(tcommon,-1,-1) ~= sepc ) | if ( string.sub(tcommon,-1,-1) ~= sepc ) | ||
then Date = sepc .." " .. Date .. OrigYear | then Date = sepc .." " .. Date .. OrigYear | ||
else Date = " " .. Date .. OrigYear | else Date = " " .. Date .. OrigYear | ||
end | end | ||
end |
end | ||
if |
if config.CitationClass=="journal" and is_set(Periodical) then | ||
text = safejoin( {Chapter, Place, tcommon}, sepc ); | text = safejoin( {Chapter, Place, tcommon}, sepc ); | ||
text = safejoin( {text, pgtext, Date, idcommon}, sepc ); | text = safejoin( {text, pgtext, Date, idcommon}, sepc ); | ||
else | else | ||
text = safejoin( {Chapter, Place, tcommon, Date}, sepc ); | text = safejoin( {Chapter, Place, tcommon, Date}, sepc ); | ||
text = safejoin( {text, pgtext, idcommon}, sepc ); | text = safejoin( {text, pgtext, idcommon}, sepc ); | ||
end | end | ||
end | end | ||
if PostScript |
if is_set(PostScript) and PostScript ~= sepc then | ||
text = safejoin( {text, sepc}, sepc ); --Deals with italics, spaces, etc. | text = safejoin( {text, sepc}, sepc ); --Deals with italics, spaces, etc. | ||
text = text:sub(1,-2); --Remove final seperator | text = text:sub(1,-2); --Remove final seperator | ||
Line 1,505: | Line 1,473: | ||
-- Now enclose the whole thing in a <span/> element | -- Now enclose the whole thing in a <span/> element | ||
if ( |
if not is_set(Year) then | ||
if ( |
if is_set(DateIn) then | ||
Year = selectyear( DateIn ) | Year = selectyear( DateIn ); | ||
elseif( |
elseif is_set(PublicationDate) then | ||
Year = selectyear( PublicationDate ) | Year = selectyear( PublicationDate ); | ||
else | |||
Year = "" | |||
end | end | ||
end | end | ||
local classname = "citation" | |||
local options = {}; | |||
if ( config.CitationClass ~= "citation" ) | |||
then classname = "citation " .. (config.CitationClass or "") end | |||
if is_set(config.CitationClass) and config.CitationClass ~= "citation" then | |||
local options = { class=classname } | |||
options.class = "citation " .. config.CitationClass; | |||
if ( Ref ~= nil ) then | |||
else | |||
options.class = "citation"; | |||
end | |||
if is_set(Ref) and Ref:lower() ~= "none" then | |||
local id = Ref | local id = Ref | ||
if ( "harv" == Ref ) then | if ( "harv" == Ref ) then | ||
local names = {} --table of last names & year | local names = {} --table of last names & year | ||
if ( |
if is_set(Authors) then | ||
for i,v in ipairs(a) do | for i,v in ipairs(a) do | ||
names = v.last | names = v.last | ||
if i == 4 then break end | if i == 4 then break end | ||
end | end | ||
elseif ( |
elseif is_set(Editors) then | ||
for i,v in ipairs(e) do | for i,v in ipairs(e) do | ||
names = v.last | names = v.last | ||
Line 1,545: | Line 1,516: | ||
end | end | ||
if options.id |
if is_set(options.id) then | ||
text = '<span id="' .. |
text = '<span id="' .. mw.uri.anchorEncode(options.id) ..'" class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>"; | ||
else | else | ||
text = '<span class="' .. |
text = '<span class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>"; | ||
end | end | ||
Line 1,554: | Line 1,525: | ||
-- Note: Using display: none on then COinS span breaks some clients. | -- Note: Using display: none on then COinS span breaks some clients. | ||
local OCinS = '<span title="' .. |
local OCinS = '<span title="' .. OCinSoutput .. '" class="Z3988">' .. empty_span .. '</span>'; | ||
text = text .. OCinS; | text = text .. OCinS; | ||
Line 1,560: | Line 1,531: | ||
text = text .. " "; | text = text .. " "; | ||
for i,v in ipairs( z.message_tail ) do | for i,v in ipairs( z.message_tail ) do | ||
if v |
if is_set(v) then | ||
if i == #z.message_tail then | if i == #z.message_tail then | ||
text = text .. errorcomment( v, v ); | text = text .. errorcomment( v, v ); | ||
Line 1,571: | Line 1,542: | ||
no_tracking_cats = no_tracking_cats:lower(); | no_tracking_cats = no_tracking_cats:lower(); | ||
if no_tracking_cats |
if inArray(no_tracking_cats, {"", "no", "false", "n"}) then | ||
no_tracking_cats == "false" or no_tracking_cats == "n" then | |||
for _, v in ipairs( z.error_categories ) do | for _, v in ipairs( z.error_categories ) do | ||
text = text .. ']'; | text = text .. ']'; | ||
Line 1,588: | Line 1,558: | ||
local suggestions = {}; | local suggestions = {}; | ||
local error_text, error_state; | local error_text, error_state; | ||
local config = {}; | |||
for k, v in pairs( frame.args ) do | |||
config = v; | |||
args = v; | |||
end | |||
for k, v in pairs( pframe.args ) do | for k, v in pairs( pframe.args ) do | ||
if v ~= '' then | if v ~= '' then | ||
Line 1,612: | Line 1,589: | ||
table.insert( z.message_tail, {error_text, error_state} ); | table.insert( z.message_tail, {error_text, error_state} ); | ||
end | end | ||
end |
end | ||
args = v; | args = v; | ||
elseif k == 'postscript' then | elseif args ~= nil or (k == 'postscript') then | ||
args = v; | |||
end | |||
end | |||
local config = {}; | |||
for k, v in pairs( frame.args ) do | |||
config = v; | |||
if args == nil and (v ~= '' or k == 'postscript') then | |||
args = v; | args = v; | ||
end | end |
Revision as of 18:42, 25 April 2013
Module documentation[view] [edit] [history] [purge]This Lua module is used on approximately 6,020,000 pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
This module can only be edited by administrators because it is transcluded onto one or more cascade-protected pages. |
This module uses TemplateStyles: |
This module and associated sub-modules support the Citation Style 1 and Citation Style 2 citation templates. In general, it is not intended to be called directly, but is called by one of the core CS1 and CS2 templates.
These files comprise the module support for CS1|2 citation templates:
Other documentation:
- Module talk:Citation/CS1/Feature requests
- Module talk:Citation/CS1/COinS
- Module:Cs1 documentation support – a set of functions (some experimental) that extract information from the module suite for the purpose of documenting CS1|2
- Module:Citation/CS1/doc/Category list – lists of category names taken directly from Module:Citation/CS1/Configuration and Module:Citation/CS1/Configuration/sandbox
testcases
- Module:Citation/CS1/testcases (run)
- Module:Citation/CS1/testcases/errors (run) – error and maintenance messaging
- Module:Citation/CS1/testcases/dates (run) – date validation
- Module:Citation/CS1/testcases/identifiers (run) – identifiers
- Module:Citation/CS1/testcases/anchor (run) – CITEREF anchors
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Subpages of this module.
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/sandbox' ); -- Contains a list of all recognized parameters local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' ); -- 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, {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; --force the variable to be loaded. return origin; end }, { __index = function ( tbl, k ) if origin ~= nil then return nil; end local args, list, v = args, cfg.aliases; if list == nil then error( cfg.messages ); elseif type( list ) == 'string' then v, origin = args, list; else v, origin = selectone( args, list, 'redundant_parameters' ); if origin == nil then origin = ''; --Empty string, not nil; end end if v == nil then v = cfg.defaults or ""; origin = ''; --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 then return true; end -- Arguments with numbers in them name = name:gsub( "%d+", "#" ); if whitelist.numbered_arguments 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; prefix = prefix or ""; suffix = suffix or ""; if error_state == nil then error( cfg.messages ); 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 .. " ( .. "#" .. error_state.anchor .. "|" .. cfg.messages .. "]])"; z.error_ids = true; if inArray( error_id, { 'bare_url_missing_title', 'trans_missing_title' } ) and z.error_ids 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', 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]', 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 ); end end if not checkurl( URL ) then error_str = seterror( 'bad_url', {}, false, " " ) .. error_str; end return table.concat({ "", 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; 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; local text; if is_set(inactive) then text = "]:" .. id; table.insert( z.error_categories, "Pages with DOIs inactive since " .. selectyear(inactive) ); inactive = " (" .. cfg.messages .. " " .. 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; 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( "", "" ); 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 ] or ] 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 ) if str:match( "%%]" ) ~= nil then table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } ); end return str:gsub( '\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 <i> 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, <i> 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 = "<span />" .. str; end if str:sub(-1,-1) == "'" then str = str .. "<span />"; 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 .. "<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.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 = "]" 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 = " & "; end text = nil; end local result = table.concat(text) -- construct list if etal then local etal_text = cfg.messages; result = result .. " " .. etal_text; end -- if necessary wrap result in <span> tag to format in Small Caps if ( "scap" == format ) then result = '<span class="smallcaps" style="font-variant:small-caps">' .. result .. '</span>'; 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, 'redundant_parameters', i ); if not is_set(last) then -- just in case someone passed in an empty parameter break; end names = { last = last, first = selectone( args, cfg.aliases, 'redundant_parameters', i ), link = selectone( args, cfg.aliases, 'redundant_parameters', i ), mask = selectone( args, cfg.aliases, '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 = 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 end } end; for k, v in pairs( id_list ) do -- fallback to read-only cfg handler = setmetatable( { = 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 ); 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 ); end end function comp( a, b ) return a < b; end table.sort( new_list, comp ); for k, v in ipairs( new_list ) do new_list = v; 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) then if value ~= nil and selected ~= v then table.insert( error_list, v ); else value = args; 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) then if value ~= nil and selected ~= v then table.insert( error_list, v ); else value = args; 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 end error_str = error_str .. wrap( 'parameter', k ); end if #error_list > 1 then error_str = error_str .. cfg.messages; else error_str = error_str .. cfg.messages; 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 <http://ocoins.info/>) 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 = "bookitem"; OCinSoutput = data.Chapter; OCinSoutput = data.Title; elseif is_set(data.Periodical) then OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"; OCinSoutput = "article"; OCinSoutput = data.Periodical; OCinSoutput = data.Title; else OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; OCinSoutput = "book" OCinSoutput = data.Title; end OCinSoutput = data.PublicationPlace; OCinSoutput = data.Date; OCinSoutput = data.Series; OCinSoutput = data.Volume; OCinSoutput = data.Issue; OCinSoutput = data.Pages; OCinSoutput = data.Edition; OCinSoutput = data.PublisherName; for k, v in pairs( data.ID_list ) do local id, value = cfg.id_handlers.COinS; if k == 'ISBN' then value = cleanisbn( v ); else value = v; end if string.sub( id or "", 1, 4 ) == 'info' then OCinSoutput = table.concat{ id, "/", v }; else OCinSoutput = 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 = last; end if is_set(first) then OCinSoutput = first; end end if is_set(last) and is_set(first) then OCinSoutput = table.concat{ last, ", ", first }; elseif is_set(last) then OCinSoutput = 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) --[[ 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 local PPPrefix = A if is_set( A ) then PPPrefix = "" PPrefix = "" end -- Pick out the relevant fields from the arguments. Different citation templates -- define different field names for the same underlying things. local Authors = A; local a = extractnames( args, 'AuthorList' ); local Coauthors = A; local Others = A; local Editors = A; local e = extractnames( args, 'EditorList' ); local Year = A; local PublicationDate = A; local OrigYear = A; local Date = A; local LayDate = A; ------------------------------------------------- Get title data local Title = A; local BookTitle = A; local Conference = A; local TransTitle = A; local TitleNote = A; local TitleLink = A; local Chapter = A; local ChapterLink = A; local TransChapter = A; local TitleType = A; local ArchiveURL = A; local URL = A local URLorigin = A:ORIGIN('URL'); local ChapterURL = A; local ChapterURLorigin = A:ORIGIN('ChapterURL'); local ConferenceURL = A; local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); local Periodical = A; 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; local Volume = A; local Issue = A; local Position = ''; local Page, Pages, At, page_type; Page = A; Pages = hyphentodash( A ); At = A; 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; local PublicationPlace = A local Place = A; if not is_set(PublicationPlace) and is_set(Place) then PublicationPlace = Place; end if PublicationPlace == Place then Place = ''; end local PublisherName = A; local SubscriptionRequired = A; local Via = A; local AccessDate = A; local ArchiveDate = A; local Agency = A; local DeadURL = A local Language = A; local Format = A; local Ref = A; local DoiBroken = A; local ID = A; local ASINTLD = A; local IgnoreISBN = A; local ID_list = extractids( args ); local Quote = A; local PostScript = A; local LayURL = A; local LaySource = A; local Transcript = A; local TranscriptURL = A local TranscriptURLorigin = A:ORIGIN('TranscriptURL'); local sepc = A; local LastAuthorAmp = A; local no_tracking_cats = A; 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) then local Embargo = A; 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; URLorigin = cfg.id_handlers.parameters; end else URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list; URLorigin = cfg.id_handlers.parameters; 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; local SeriesLink = A; local Season = A; local SeriesNumber = A; local Network = A; local Station = A; local s, n = {}, {}; local Sep = (first_set(A, A) or "") .. " "; if is_set(Issue) then table.insert(s, cfg.messages .. " " .. Issue); Issue = ''; end if is_set(Season) then table.insert(s, cfg.messages .. " " .. Season); end if is_set(SeriesNumber) then table.insert(s, cfg.messages .. " " .. 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 <http://ocoins.info/>) for -- automated parsing of citation information. local OCinSoutput = COinS{ = Periodical, = Chapter, = Title, = PublicationPlace, = first_set(Date, Year, PublicationDate), = Series, = Volume, = Issue, = first_set(Page, Pages, At), = Edition, = PublisherName, = first_set( URL, ChapterURL ), = a, = ID_list, = 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 ); -- 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 .. " ", namesep = (first_set(A, A) or "") .. " ", format = A, 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 ); -- 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 .. " ", namesep = (first_set(A, A) or "") .. " ", format = A, 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; if is_set(Month) then Date = Month .. " " .. Date; local Day = A if is_set(Day) then Date = Day .. " " .. Date end 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 ( ArchiveURL and "" < ArchiveURL ) then if ( DeadURL ~= "no" ) then URL = ArchiveURL end end -- Format chapter / article title if is_set(Chapter) and is_set(ChapterLink) then 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 Chapter = Chapter .. sepc .. " " -- with end-space elseif is_set(ChapterURL) then Chapter = " " .. externallink( ChapterURL, nil, ChapterURLorigin ) .. sepc .. " "; end -- Format main title. if is_set(TitleLink) and is_set(Title) then Title = "]" 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: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; if is_set(Minutes) then Position = " " .. Minutes .. " " .. cfg.messages; else local Time = A; if is_set(Time) then local TimeCaption = A if not is_set(TimeCaption) then TimeCaption = cfg.messages; 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 tonumber(Pages) ~= nil then Pages = sepc .." " .. PPrefix .. Pages; else Pages = sepc .." " .. PPPrefix .. Pages; end end else if is_set(Periodical) and not inArray(config.CitationClass, {"encyclopaedia","web","book","news"}) then Page = ": " .. Page; else Page = sepc .." " .. PPrefix .. Page; 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 ""; TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""; Language = is_set(Language) and (" " .. wrap( 'language', Language )) or ""; Edition = is_set(Edition) and (" " .. wrap( 'edition', Edition )) or ""; Issue = is_set(Issue) and (" (" .. Issue .. ")") or ""; Series = is_set(Series) and (sepc .. " " .. Series) or ""; OrigYear = is_set(OrigYear) and (" ") 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 = " <b>" .. hyphentodash(Volume) .. "</b>"; 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 if (sepc ~= ".") then retrv_text = retrv_text:lower() end AccessDate = '<span class="reference-accessdate">' .. sepc .. substitute( retrv_text, {AccessDate} ) .. '</span>' end if is_set(SubscriptionRequired) then SubscriptionRequired = sepc .. " " .. cfg.messages; 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; if sepc ~= "." then arch_text = arch_text:lower() end Archived = sepc .. " " .. substitute( cfg.messages, { 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; if sepc ~= "." then arch_text = arch_text:lower() end Archived = sepc .. " " .. substitute( arch_text, { externallink( OriginalURL, cfg.messages ), ArchiveDate } ); else local arch_text = cfg.messages; if sepc ~= "." then arch_text = arch_text:lower() end Archived = sepc .. " " .. substitute( arch_text, { seterror('archive_missing_url'), ArchiveDate } ); 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 ) .. LaySource .. LayDate else Lay = sepc .. " " .. externallink( LayURL, cfg.messages: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 if is_set(PublicationDate) then if is_set(Publisher) then Publisher = Publisher .. ", " .. wrap( 'published', PublicationDate ); else Publisher = PublicationDate; end end if is_set(Publisher) then Publisher = " (" .. Publisher .. ")"; end else if is_set(PublicationDate) then PublicationDate = " (" .. wrap( 'published', PublicationDate ) .. ")"; end if is_set(PublisherName) then if is_set(PublicationPlace) then Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate; else Publisher = sepc .. " " .. PublisherName .. PublicationDate; end elseif is_set(PublicationPlace) then Publisher= sepc .. " " .. PublicationPlace .. PublicationDate; else Publisher = PublicationDate; end end -- Several of the above rely upon detecting this as nil, so do it last. if is_set(Periodical) then if is_set(Title) or is_set(TitleNote) then Periodical = sepc .. " " .. wrap( 'italic-title', Periodical ) else Periodical = wrap( 'italic-title', Periodical ) end end -- 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, 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 is_set(Authors) then if is_set(Coauthors) then Authors = Authors .. A .. " " .. Coauthors end if is_set(Date) then Date = " ("..Date..")" .. OrigYear .. sepc .. " " elseif string.sub(Authors,-1,-1) == sepc then Authors = Authors .. " " else Authors = Authors .. sepc .. " " end if is_set(Editors) then local in_text = " " .. cfg.messages .. " " if (sepc ~= '.') then in_text = in_text:lower() 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 is_set(Editors) then if is_set(Date) then if EditorCount <= 1 then Editors = Editors .. ", " .. cfg.messages; else Editors = Editors .. ", " .. cfg.messages; end Date = " (" .. Date ..")" .. OrigYear .. sepc .. " " else if EditorCount <= 1 then Editors = Editors .. " (" .. cfg.messages .. ")" .. sepc .. " " else Editors = Editors .. " (" .. cfg.messages .. ")" .. sepc .. " " end end text = safejoin( {Editors, Date, Chapter, Place, tcommon}, sepc ); text = safejoin( {text, pgtext, idcommon}, sepc ); else if is_set(Date) then if ( string.sub(tcommon,-1,-1) ~= sepc ) then Date = sepc .." " .. Date .. OrigYear else Date = " " .. Date .. OrigYear end end if config.CitationClass=="journal" and is_set(Periodical) then text = safejoin( {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 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 <span/> 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 = v.last if i == 4 then break end end elseif is_set(Editors) then for i,v in ipairs(e) do names = v.last if i == 4 then break end end end names = Year; id = anchorid(names) end options.id = id; end if string.len(text:gsub("<span*>.-</span>", ""):gsub("%b<>","")) <= 2 then z.error_categories = {}; text = seterror('empty_citation'); z.message_tail = {}; end if is_set(options.id) then text = '<span id="' .. mw.uri.anchorEncode(options.id) ..'" class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>"; else text = '<span class="' .. mw.text.nowiki(options.class) .. '">' .. text .. "</span>"; end local empty_span = '<span style="display:none;"> </span>'; -- Note: Using display: none on then COinS span breaks some clients. local OCinS = '<span title="' .. OCinSoutput .. '" class="Z3988">' .. empty_span .. '</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) then if i == #z.message_tail then text = text .. errorcomment( v, v ); else text = text .. errorcomment( v .. "; ", v ); 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 .. ']'; 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 = v; args = 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 ~= nil then error_text, error_state = seterror( 'parameter_ignored_suggest', {k, suggestions}, 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 = v; elseif args ~= nil or (k == 'postscript') then args = v; end end return citation0( config, args) end return zCategories: