Module:Citation/CS1: Difference between revisions
m>Dragons flight (sync with sandbox, merge cite_web_title to bare_url, better style control on translations, category suppression by config, tweak display authors) |
m>Dragons flight (sync to sandbox, mostly code cleaning / organization. Also, ref = none, layurl as alias for laysummary, tweaks to COinS output, and changes to bareurl error.) |
||
Line 5: | Line 5: | ||
} | } | ||
-- Include translation message hooks, ID and error handling configuration settings. | |||
local | -- 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 | -- 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 | ||
-- | function is_set( var ) | ||
function | return not (var == nil or var == ''); | ||
if | end | ||
return | |||
-- 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 | 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 str | if not is_set( str ) then | ||
return ""; | return ""; | ||
elseif inArray( key, { 'italic-title', 'trans-italic-title' } ) then | |||
str = safeforitalics( str ); | str = safeforitalics( str ); | ||
end | end | ||
return substitute( cfg. | return substitute( cfg.messages[key], {str} ); | ||
end | end | ||
Line 48: | Line 63: | ||
]] | ]] | ||
function argument_wrapper( args ) | function argument_wrapper( args ) | ||
local origin = {}; | |||
local | |||
local | return setmetatable({ | ||
__index = function ( tbl, k ) | ORIGIN = function( self, k ) | ||
if | local dummy = self[k]; --force the variable to be loaded. | ||
return origin[k]; | |||
end | |||
}, | |||
{ | |||
__index = function ( tbl, k ) | |||
if origin[k] ~= nil then | |||
return nil; | return nil; | ||
end | end | ||
local list = cfg. | local args, list, v = args, cfg.aliases[k]; | ||
if list == nil then | if list == nil then | ||
error( cfg. | error( cfg.messages['unknown_argument_map'] ); | ||
elseif type( list ) == 'string' then | elseif type( list ) == 'string' then | ||
v = | v, origin[k] = args[list], list; | ||
else | else | ||
v = selectone( | v, origin[k] = selectone( args, list, 'redundant_parameters' ); | ||
' | if origin[k] == nil then | ||
origin[k] = ''; --Empty string, not nil; | |||
end | |||
end | end | ||
if v == nil then | if v == nil then | ||
v = cfg. | v = cfg.defaults[k] or ""; | ||
origin[k] = ''; --Empty string, not nil; | |||
end | end | ||
tbl = rawset( tbl, k, v ); | tbl = rawset( tbl, k, v ); | ||
return v; | return v; | ||
end, | end, | ||
} | }); | ||
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 ); | |||
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[ error_id ]; | local error_state = cfg.error_conditions[ error_id ]; | ||
prefix = prefix or ""; | prefix = prefix or ""; | ||
suffix = suffix or ""; | suffix = suffix or ""; | ||
if error_state == nil then | if error_state == nil then | ||
error( cfg. | error( cfg.messages['undefined_error'] ); | ||
elseif is_set( error_state.category ) then | |||
table.insert( z.error_categories, error_state.category ); | |||
end | end | ||
local message = substitute( error_state.message, arguments ); | |||
message = message .. " ([[" .. cfg.messages['help page link'] .. | |||
"#" .. error_state.anchor .. "|" .. | "#" .. error_state.anchor .. "|" .. | ||
cfg. | cfg.messages['help page label'] .. "]])"; | ||
z.error_ids[ error_id ] = true; | z.error_ids[ error_id ] = true; | ||
if (error_id | if inArray( error_id, { 'bare_url_missing_title', 'trans_missing_title' } ) | ||
and z.error_ids['citation_missing_title'] then | and z.error_ids['citation_missing_title'] 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 | end | ||
-- Formats a wiki style external link | -- Formats a wiki style external link | ||
function externallinkid(options) | function externallinkid(options) | ||
local url_string = options.id; | |||
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|%s]]%s[%s%s%s %s]', | |||
return | 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|%s]]%s[[%s%s%s|%s]]', | |||
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 label | if not is_set( label ) then | ||
label = URL; | label = URL; | ||
error_str = seterror( 'bare_url_missing_title', {}, false, " " ); | if is_set( source ) then | ||
error_str = seterror( 'bare_url_missing_title', { wrap( 'parameter', source ) }, false, " " ); | |||
else | |||
error( cfg.messages["bare_url_no_origin"] ); | |||
end | |||
end | 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({ "[", URL, " ", safeforurl( label ), "]", error_str }); | |||
return "[" | |||
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 ( inactive | if is_set(inactive) then | ||
text = "[[" .. handler.link .. "|" .. handler.label .. "]]:" .. id; | text = "[[" .. handler.link .. "|" .. handler.label .. "]]:" .. 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'] .. " " .. 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 | |||
return url_str:sub(1,2) == "//" or url_str:match( "^[^/]*:" ) ~= nil; | |||
end | end | ||
Line 312: | Line 311: | ||
-- Gets the display text for a wikilink like [[A|B]] or [[B]] gives B | -- Gets the display text for a wikilink like [[A|B]] or [[B]] 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]', { | ||
['['] = '[', | ['['] = '[', | ||
[']'] = ']', | [']'] = ']', | ||
['\n'] = ' ' } ); | ['\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 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 str | 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; | ||
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 = "[[" .. person.link .. "|" .. 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-2] = " & "; | text[#text-2] = " & "; | ||
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['et al']; | ||
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[list_name .. '-Last'], 'redundant_parameters', i ); | ||
if ( | if not is_set(last) then | ||
-- just in case someone passed in an empty parameter | |||
break; | break; | ||
end | end | ||
names[i] = { | |||
last = last, | |||
first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ), | |||
link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ), | |||
mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i ) | |||
}; | |||
i = i + 1; | 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[k] = 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 = {}; | ||
function fallback(k) return { __index = function(t,i) return cfg.id_handlers[k][i] 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( { ['id'] = v }, fallback(k) ); | |||
-- | |||
if handler.mode == 'external' then | 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['unknown_ID_mode'] ); | |||
elseif k == 'DOI' then | |||
table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } ); | |||
elseif k == 'ASIN' then | |||
table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } ); | |||
elseif k == 'OL' then | |||
table.insert( new_list, {handler.label, openlibrary( v ) } ); | |||
elseif k == 'ISBN' then | |||
local ISBN = internallinkid( handler ); | |||
if not checkisbn( v ) and not is_set(options.IgnoreISBN) then | |||
ISBN = ISBN .. seterror( 'bad_isbn', {}, false, " ", "" ); | |||
end | |||
table.insert( new_list, {handler.label, ISBN } ); | |||
else | else | ||
error( cfg. | error( cfg.messages['unknown_manual_ID'] ); | ||
end | end | ||
end | end | ||
function comp( a, b ) | function comp( a, b ) | ||
return a[1] < b[1]; | return a[1] < b[1]; | ||
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[v] | if is_set(args[v]) 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[v] | if is_set(args[v]) 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['parameter-separator'] 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['parameter-final-separator']; | ||
else | else | ||
error_str = error_str .. | error_str = error_str .. cfg.messages['parameter-pair-separator']; | ||
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["rft.genre"] = "bookitem"; | |||
OCinSoutput["rft.btitle"] = data.Chapter; | |||
OCinSoutput["rft.atitle"] = data.Title; | |||
elseif is_set(data.Periodical) then | |||
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"; | |||
OCinSoutput["rft.genre"] = "article"; | |||
OCinSoutput["rft.jtitle"] = data.Periodical; | |||
OCinSoutput["rft.atitle"] = data.Title; | |||
else | |||
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; | |||
OCinSoutput["rft.genre"] = "book" | |||
OCinSoutput["rft.btitle"] = data.Title; | |||
end | |||
OCinSoutput["rft.place"] = data.PublicationPlace; | |||
OCinSoutput["rft.date"] = data.Date; | |||
OCinSoutput["rft.series"] = data.Series; | |||
OCinSoutput["rft.volume"] = data.Volume; | |||
OCinSoutput["rft.issue"] = data.Issue; | |||
OCinSoutput["rft.pages"] = data.Pages; | |||
OCinSoutput["rft.edition"] = data.Edition; | |||
OCinSoutput["rft.pub"] = data.PublisherName; | |||
for k, v in pairs( data.ID_list ) do | |||
local id, value = cfg.id_handlers[k].COinS; | |||
if k == 'ISBN' then value = cleanisbn( v ); else value = v; end | |||
if string.sub( id or "", 1, 4 ) == 'info' then | |||
OCinSoutput["rft_id"] = table.concat{ id, "/", v }; | |||
else | |||
OCinSoutput[ id ] = value; | |||
end | |||
end | |||
local last, first; | |||
for k, v in ipairs( data.Authors ) do | |||
last, first = v.last, v.first; | |||
if k == 1 then | |||
if is_set(last) then | |||
OCinSoutput["rft.aulast"] = last; | |||
end | |||
if is_set(first) then | |||
OCinSoutput["rft.aufirst"] = first; | |||
end | |||
end | |||
if is_set(last) and is_set(first) then | |||
OCinSoutput["rft.au"] = table.concat{ last, ", ", first }; | |||
elseif is_set(last) then | |||
OCinSoutput["rft.au"] = last; | |||
end | |||
end | |||
OCinSoutput.rft_id = data.URL; | |||
OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage }; | |||
OCinSoutput = setmetatable( OCinSoutput, nil ); | |||
-- sort with version string always first, and combine. | |||
table.sort( OCinSoutput ); | |||
table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver ); -- such as "Z39.88-2004" | |||
return table.concat(OCinSoutput, "&"); | |||
end | end | ||
Line 686: | Line 759: | ||
local PPrefix = A['PPrefix'] | local PPrefix = A['PPrefix'] | ||
local PPPrefix = A['PPPrefix'] | local PPPrefix = A['PPPrefix'] | ||
if ( | if is_set( A['NoPP'] ) 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['TitleType']; | local TitleType = A['TitleType']; | ||
local ArchiveURL = A['ArchiveURL']; | local ArchiveURL = A['ArchiveURL']; | ||
local URL = A['URL']; | local URL = A['URL'] | ||
local URLorigin = A:ORIGIN('URL'); | |||
local ChapterURL = A['ChapterURL']; | local ChapterURL = A['ChapterURL']; | ||
local ChapterURLorigin = A:ORIGIN('ChapterURL'); | |||
local ConferenceURL = A['ConferenceURL']; | local ConferenceURL = A['ConferenceURL']; | ||
local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); | |||
local Periodical = A['Periodical']; | local Periodical = A['Periodical']; | ||
if ( config.CitationClass == "encyclopaedia" ) then | if ( config.CitationClass == "encyclopaedia" ) then | ||
if ( Chapter | 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['Volume']; | local Volume = A['Volume']; | ||
local Issue = A['Issue']; | local Issue = A['Issue']; | ||
local Position = | local Position = ''; | ||
local Page, Pages, At, page_type; | local Page, Pages, At, page_type; | ||
Line 743: | Line 819: | ||
Pages = hyphentodash( A['Pages'] ); | Pages = hyphentodash( A['Pages'] ); | ||
At = A['At']; | At = A['At']; | ||
if Page | |||
if Pages | 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['Edition']; | local Edition = A['Edition']; | ||
local PublicationPlace = A['PublicationPlace'] | local PublicationPlace = A['PublicationPlace'] | ||
local Place = A['Place']; | local Place = A['Place']; | ||
if PublicationPlace | |||
if not is_set(PublicationPlace) and is_set(Place) then | |||
PublicationPlace = Place; | PublicationPlace = Place; | ||
end | end | ||
if PublicationPlace == Place then Place = | |||
if PublicationPlace == Place then Place = ''; end | |||
local PublisherName = A['PublisherName']; | local PublisherName = A['PublisherName']; | ||
Line 772: | Line 851: | ||
local DeadURL = A['DeadURL'] | local DeadURL = A['DeadURL'] | ||
local Language = A['Language']; | local Language = A['Language']; | ||
local Format = A['Format'] | local Format = A['Format']; | ||
local Ref = A['Ref'] | local Ref = A['Ref']; | ||
local DoiBroken = A['DoiBroken'] | local DoiBroken = A['DoiBroken']; | ||
local ID = A['ID']; | local ID = A['ID']; | ||
local ASINTLD = A['ASINTLD']; | local ASINTLD = A['ASINTLD']; | ||
local IgnoreISBN = A['IgnoreISBN'] | local IgnoreISBN = A['IgnoreISBN']; | ||
local ID_list = extractids( args ); | local ID_list = extractids( args ); | ||
local Quote = A['Quote']; | local Quote = A['Quote']; | ||
local PostScript = A['PostScript'] | local PostScript = A['PostScript']; | ||
local | local LayURL = A['LayURL']; | ||
local LaySource = A['LaySource']; | local LaySource = A['LaySource']; | ||
local Transcript = A['Transcript']; | local Transcript = A['Transcript']; | ||
local TranscriptURL = A['TranscriptURL']; | local TranscriptURL = A['TranscriptURL'] | ||
local sepc = A['Separator'] | local TranscriptURLorigin = A:ORIGIN('TranscriptURL'); | ||
local LastAuthorAmp = A['LastAuthorAmp'] | local sepc = A['Separator']; | ||
local no_tracking_cats = A['NoTracking'] | local LastAuthorAmp = A['LastAuthorAmp']; | ||
local no_tracking_cats = A['NoTracking']; | |||
local this_page = mw.title.getCurrentTitle(); --Also used for COinS | local this_page = mw.title.getCurrentTitle(); --Also used for COinS | ||
if no_tracking_cats | |||
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['PMC']) then | ||
local Embargo = A['Embargo']; | |||
if is_set(Embargo) then | |||
local lang = mw.getContentLanguage(); | |||
local good1, result1, good2, result2; | |||
good1, result1 = pcall( lang.formatDate, lang, 'U', Embargo ); | |||
good2, result2 = pcall( lang.formatDate, lang, 'U' ); | |||
if good1 and good2 and tonumber( result1 ) < tonumber( result2 ) then | |||
URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC']; | |||
URLorigin = cfg.id_handlers['PMC'].parameters[1]; | |||
end | end | ||
else | |||
URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC']; | |||
URLorigin = cfg.id_handlers['PMC'].parameters[1]; | |||
end | end | ||
end | 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 ( BookTitle ) then | 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['AirDate'] | local AirDate = A['AirDate']; | ||
local SeriesLink = A['SeriesLink'] | local SeriesLink = A['SeriesLink']; | ||
local Season = A['Season'] | local Season = A['Season']; | ||
local SeriesNumber = A['SeriesNumber'] | local SeriesNumber = A['SeriesNumber']; | ||
local Network = A['Network'] | local Network = A['Network']; | ||
local Station = A['Station'] | local Station = A['Station']; | ||
local s = {} | local s, n = {}, {}; | ||
if Issue | local Sep = (first_set(A["SeriesSeparator"], A["Separator"]) or "") .. " "; | ||
if Season | |||
if SeriesNumber | if is_set(Issue) then table.insert(s, cfg.messages["episode"] .. " " .. Issue); Issue = ''; end | ||
if is_set(Season) then table.insert(s, cfg.messages["season"] .. " " .. Season); end | |||
if Network | if is_set(SeriesNumber) then table.insert(s, cfg.messages["series"] .. " " .. SeriesNumber); end | ||
if Station | if is_set(Network) then table.insert(n, Network); end | ||
Date = Date or AirDate | if is_set(Station) then table.insert(n, Station); end | ||
Chapter = Title | |||
ChapterLink = TitleLink | Date = Date or AirDate; | ||
TransChapter = TransTitle | Chapter = Title; | ||
Title = Series | ChapterLink = TitleLink; | ||
TitleLink = SeriesLink | TransChapter = TransTitle; | ||
TransTitle = | Title = Series; | ||
TitleLink = SeriesLink; | |||
Series = table.concat(s, Sep) | TransTitle = ''; | ||
ID = table.concat(n, Sep | |||
Series = table.concat(s, Sep); | |||
ID = table.concat(n, Sep); | |||
end | end | ||
-- COinS metadata (see <http://ocoins.info/>) for | |||
-- automated parsing of citation information. | |||
local OCinSoutput = COinS{ | |||
local | ['Periodical'] = Periodical, | ||
['Chapter'] = Chapter, | |||
['Title'] = Title, | |||
['PublicationPlace'] = PublicationPlace, | |||
['Date'] = first_set(Date, Year, PublicationDate), | |||
['Series'] = Series, | |||
['Volume'] = Volume, | |||
['Issue'] = Issue, | |||
['Pages'] = first_set(Page, Pages, At), | |||
['Edition'] = Edition, | |||
['PublisherName'] = PublisherName, | |||
['URL'] = first_set( URL, ChapterURL ), | |||
['Authors'] = a, | |||
['ID_list'] = ID_list, | |||
['RawPage'] = this_page.prefixedText, | |||
}; | |||
if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then | |||
Chapter = Title; | |||
ChapterLink = TitleLink; | |||
TransChapter = TransTitle; | |||
Title = ''; | |||
TitleLink = ''; | |||
TransTitle = ''; | |||
if (Periodical | |||
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 ( Authors | if not is_set(Authors) then | ||
local Maximum = tonumber( A['DisplayAuthors'] ); | local Maximum = tonumber( A['DisplayAuthors'] ); | ||
-- 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["AuthorSeparator"] .. " ", | sep = A["AuthorSeparator"] .. " ", | ||
namesep = (A["AuthorNameSeparator"] | namesep = (first_set(A["AuthorNameSeparator"], A["NameSeparator"]) or "") .. " ", | ||
format = A["AuthorFormat"], | format = A["AuthorFormat"], | ||
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 ( Editors | if not is_set(Editors) then | ||
local Maximum = tonumber( A['DisplayEditors'] ); | local Maximum = tonumber( A['DisplayEditors'] ); | ||
-- 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["EditorSeparator"] .. " ", | sep = A["EditorSeparator"] .. " ", | ||
namesep = (A["EditorNameSeparator"] | namesep = (first_set(A["EditorNameSeparator"], A["NameSeparator"]) or "") .. " ", | ||
format = A['EditorFormat'], | format = A['EditorFormat'], | ||
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 not is_set(Date) then | |||
Date = Year; | |||
if is_set(Date) then | |||
local Month = A['Month']; | |||
if is_set(Month) then | |||
Date = Month .. " " .. Date; | |||
Date = Year | |||
if ( Date | |||
local Month = A['Month'] | |||
if ( Month | |||
Date = Month .. " " .. Date | |||
local Day = A['Day'] | local Day = A['Day'] | ||
if ( Day | if is_set(Day) then Date = Day .. " " .. Date end | ||
end | end | ||
end | end | ||
end | end | ||
if ( PublicationDate | |||
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 ( URL | 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 ( AccessDate | 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 ( Format | 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 ( Chapter | 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 ""; | |||
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 ( Chapter | if is_set(Chapter) and is_set(ChapterLink) then | ||
Chapter = "[[" .. ChapterLink .. "|" .. Chapter .. "]]"; | Chapter = "[[" .. ChapterLink .. "|" .. 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 | ||
if not is_set(Chapter) then | |||
TransError = " " .. seterror( 'trans_missing_chapter' ); | |||
else | |||
TransChapter = " " .. TransChapter; | |||
end | |||
end | end | ||
Chapter = Chapter .. TransChapter; | |||
if Chapter | if is_set(Chapter) then | ||
if ( ChapterLink | if not is_set(ChapterLink) then | ||
if ( | if is_set(ChapterURL) then | ||
Chapter = externallink( ChapterURL, Chapter ) .. TransError; | Chapter = externallink( ChapterURL, Chapter ) .. TransError; | ||
if URL | 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 ( TitleLink and | if is_set(TitleLink) and is_set(Title) then | ||
Title = "[[" .. TitleLink .. "|" .. Title .. "]]" | |||
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 ( config.CitationClass | elseif inArray(config.CitationClass, {"web","news","pressrelease"}) and | ||
not is_set(Chapter) then | |||
Chapter | |||
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 | ||
if not is_set(Title) then | |||
TransError = " " .. seterror( 'trans_missing_title' ); | |||
else | |||
TransTitle = " " .. TransTitle; | |||
end | |||
end | end | ||
Title = Title .. TransTitle; | |||
if Title | if is_set(Title) then | ||
if ( TitleLink | 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 ( Place | 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['written']:lower(), {Place} ) .. sepc .. " "; | ||
end | end | ||
end | end | ||
if ( Conference | if is_set(Conference) then | ||
if ( ConferenceURL | 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 ); | ||
end | end | ||
if ( | if not is_set(Position) then | ||
local Minutes = A['Minutes']; | local Minutes = A['Minutes']; | ||
if ( | if is_set(Minutes) then | ||
Position = " " .. Minutes .. " " .. cfg. | Position = " " .. Minutes .. " " .. cfg.messages['minutes']; | ||
else | else | ||
local Time = A['Time']; | local Time = A['Time']; | ||
if ( | if is_set(Time) then | ||
local TimeCaption = A['TimeCaption'] | local TimeCaption = A['TimeCaption'] | ||
if TimeCaption | if not is_set(TimeCaption) then | ||
TimeCaption = cfg. | TimeCaption = cfg.messages['event']; | ||
if sepc ~= '.' then | if sepc ~= '.' then | ||
TimeCaption = TimeCaption:lower(); | TimeCaption = TimeCaption:lower(); | ||
end | end | ||
end | end | ||
Position = " " .. TimeCaption .. " " .. Time | Position = " " .. TimeCaption .. " " .. Time; | ||
end | end | ||
end | end | ||
else | else | ||
Position = " " .. Position | Position = " " .. Position; | ||
At = ''; | |||
end | end | ||
if ( | |||
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 | |||
Pages = ": " .. Pages; | |||
elseif tonumber(Pages) ~= nil then | |||
Pages = sepc .." " .. PPrefix .. Pages; | |||
else | |||
Pages = sepc .." " .. PPPrefix .. Pages; | |||
else Pages = sepc .." " .. PPPrefix .. Pages | |||
end | end | ||
end | end | ||
else | else | ||
if is_set(Periodical) and | |||
if ( Periodical | not inArray(config.CitationClass, {"encyclopaedia","web","book","news"}) then | ||
Page = ": " .. Page; | |||
Page = ": " .. Page | |||
else | else | ||
Page = sepc .." " .. PPrefix .. Page | Page = sepc .." " .. PPrefix .. Page; | ||
end | end | ||
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 (" [" .. OrigYear .. "]") or ""; | |||
Agency = is_set(Agency) and (sepc .. " " .. Agency) or ""; | |||
if is_set(Volume) then | |||
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 | |||
------------------------------------ totally unrelated data | ------------------------------------ totally unrelated data | ||
if ( | if is_set(Via) then Via = " " .. wrap( 'via', Via ); end | ||
if is_set(AccessDate) then | |||
local retrv_text = " " .. cfg.messages['retrieved'] | |||
if ( AccessDate | 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 | |||
if ( SubscriptionRequired | SubscriptionRequired = sepc .. " " .. cfg.messages['subscription']; | ||
SubscriptionRequired = sepc .. " " .. cfg. | |||
end | end | ||
if ( ID | |||
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 ( URL | if is_set(URL) then | ||
URL = " " .. externallink( URL ); | URL = " " .. externallink( URL, nil, URLorigin ); | ||
end | end | ||
if ( Quote | 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 | |||
PostScript = ""; | |||
end | end | ||
local Archived | local Archived | ||
if ( | if is_set(ArchiveURL) then | ||
if ( ArchiveDate | 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['archived']; | ||
if | if sepc ~= "." then arch_text = arch_text:lower() end | ||
Archived = sepc .. " " .. substitute( cfg. | Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'], | ||
{ 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['archived-dead']; | |||
if sepc ~= "." then arch_text = arch_text:lower() end | |||
Archived = sepc .. " " .. substitute( arch_text, | |||
{ externallink( OriginalURL, cfg.messages['original'] ), ArchiveDate } ); | |||
else | else | ||
local arch_text = cfg.messages['archived-missing']; | |||
if sepc ~= "." then arch_text = arch_text:lower() end | |||
Archived = sepc .. " " .. substitute( arch_text, | |||
{ seterror('archive_missing_url'), ArchiveDate } ); | |||
end | end | ||
else | else | ||
Archived = "" | Archived = "" | ||
end | end | ||
local Lay | local Lay | ||
if ( | if is_set(LayURL) then | ||
if ( LayDate | if is_set(LayDate) then LayDate = " (" .. LayDate .. ")" end | ||
if ( LaySource | 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['lay summary'] ) .. LaySource .. LayDate | ||
else | else | ||
Lay = sepc .. " " .. externallink( | Lay = sepc .. " " .. externallink( LayURL, cfg.messages['lay summary']:lower() ) .. LaySource .. LayDate | ||
end | end | ||
else | else | ||
Lay = "" | Lay = ""; | ||
end | end | ||
if ( | |||
if ( TranscriptURL | if is_set(Transcript) then | ||
elseif TranscriptURL | if is_set(TranscriptURL) then Transcript = externallink( TranscriptURL, Transcript ); end | ||
Transcript = externallink( TranscriptURL ) | elseif is_set(TranscriptURL) then | ||
Transcript = externallink( TranscriptURL, nil, TranscriptURLorigin ); | |||
end | end | ||
local Publisher | |||
if ( Periodical | local Publisher; | ||
if is_set(Periodical) and | |||
not inArray(config.CitationClass, {"encyclopaedia","web","pressrelease"}) then | |||
if is_set(PublisherName) then | |||
if ( PublisherName | if is_set(PublicationPlace) then | ||
if (PublicationPlace | |||
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 ( PublicationDate | 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 ( PublicationDate | if is_set(PublicationDate) then | ||
PublicationDate = " (" .. wrap( 'published', PublicationDate ) .. ")" | PublicationDate = " (" .. wrap( 'published', PublicationDate ) .. ")"; | ||
end | end | ||
if ( PublisherName | 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 ( Periodical | if is_set(Periodical) then | ||
if ( Title | 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 | |||
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 text | 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['AuthorSeparator'] .. " " .. Coauthors | |||
end | end | ||
if ( | if is_set(Date) then | ||
Date = " ("..Date..")" .. OrigYear .. sepc .. " " | |||
elseif string.sub(Authors,-1,-1) == sepc then | |||
Authors = Authors .. " " | |||
else | |||
Authors = Authors .. sepc .. " " | |||
end | end | ||
if ( | if is_set(Editors) then | ||
local in_text = " in " | local in_text = " " .. cfg.messages['in'] .. " " | ||
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['editor']; | ||
else | else | ||
Editors = Editors .. ", " .. cfg. | Editors = Editors .. ", " .. cfg.messages['editors']; | ||
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['editor'] .. ")" .. sepc .. " " | ||
else | else | ||
Editors = Editors .. " (" .. cfg. | Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. 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( {text, pgtext, Date, idcommon}, sepc ); | |||
else | else | ||
text = safejoin( {Chapter, Place, tcommon, Date}, 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 ( Year | if not is_set(Year) then | ||
if ( DateIn | if is_set(DateIn) then | ||
Year = selectyear( DateIn ) | Year = selectyear( DateIn ); | ||
elseif( PublicationDate | elseif is_set(PublicationDate) then | ||
Year = selectyear( PublicationDate ) | Year = selectyear( PublicationDate ); | ||
end | end | ||
end | end | ||
local | |||
if ( config.CitationClass ~= "citation" | local options = {}; | ||
if is_set(config.CitationClass) and config.CitationClass ~= "citation" then | |||
if ( Ref ~= | options.class = "citation " .. config.CitationClass; | ||
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[i] = v.last | names[i] = 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[i] = v.last | names[i] = 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[1] | if is_set(v[1]) then | ||
if i == #z.message_tail then | if i == #z.message_tail then | ||
text = text .. errorcomment( v[1], v[2] ); | text = text .. errorcomment( v[1], v[2] ); | ||
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 | ||
for _, v in ipairs( z.error_categories ) do | for _, v in ipairs( z.error_categories ) do | ||
text = text .. '[[Category:' .. v ..']]'; | text = text .. '[[Category:' .. v ..']]'; | ||
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[k] = v; | |||
args[k] = 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[k] = v; | args[k] = v; | ||
elseif | elseif args[k] ~= nil or (k == 'postscript') then | ||
args[k] = v; | args[k] = v; | ||
end | end |
Revision as of 18:42, 25 April 2013
Documentation for this module may be created at Module:Citation/CS1/doc
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[key], {str} ); end --[[ Argument wrapper. This function provides support for argument mapping defined in the configuration file so that multiple names can be transparently aliased to single internal variable. ]] function argument_wrapper( args ) local origin = {}; return setmetatable({ ORIGIN = function( self, k ) local dummy = self[k]; --force the variable to be loaded. return origin[k]; end }, { __index = function ( tbl, k ) if origin[k] ~= nil then return nil; end local args, list, v = args, cfg.aliases[k]; if list == nil then error( cfg.messages['unknown_argument_map'] ); elseif type( list ) == 'string' then v, origin[k] = args[list], list; else v, origin[k] = selectone( args, list, 'redundant_parameters' ); if origin[k] == nil then origin[k] = ''; --Empty string, not nil; end end if v == nil then v = cfg.defaults[k] or ""; origin[k] = ''; --Empty string, not nil; end tbl = rawset( tbl, k, v ); return v; end, }); end -- Checks that parameter name is valid using the whitelist function validate( name ) name = tostring( name ); -- Normal arguments if whitelist.basic_arguments[ name ] then return true; end -- Arguments with numbers in them name = name:gsub( "%d+", "#" ); if whitelist.numbered_arguments[ name ] then return true; end -- Not found, argument not supported. return false end -- Formats a comment for error trapping function errorcomment( content, hidden ) return wrap( hidden and 'hidden-error' or 'visible-error', content ); end --[[ Sets an error condition and returns the appropriate error message. The actual placement of the error message in the output is the responsibility of the calling function. ]] function seterror( error_id, arguments, raw, prefix, suffix ) local error_state = cfg.error_conditions[ error_id ]; prefix = prefix or ""; suffix = suffix or ""; if error_state == nil then error( cfg.messages['undefined_error'] ); elseif is_set( error_state.category ) then table.insert( z.error_categories, error_state.category ); end local message = substitute( error_state.message, arguments ); message = message .. " ([[" .. cfg.messages['help page link'] .. "#" .. error_state.anchor .. "|" .. cfg.messages['help page label'] .. "]])"; z.error_ids[ error_id ] = true; if inArray( error_id, { 'bare_url_missing_title', 'trans_missing_title' } ) and z.error_ids['citation_missing_title'] then return '', false; end message = table.concat({ prefix, message, suffix }); if raw == true then return message, error_state.hidden; end return errorcomment( message, error_state.hidden ); end -- Formats a wiki style external link function externallinkid(options) local url_string = options.id; if options.encode == true or options.encode == nil then url_string = mw.uri.encode( url_string ); end return mw.ustring.format( '[[%s|%s]]%s[%s%s%s %s]', options.link, options.label, options.separator or " ", options.prefix, url_string, options.suffix or "", mw.text.nowiki(options.id) ); end -- Formats a wiki style internal link function internallinkid(options) return mw.ustring.format( '[[%s|%s]]%s[[%s%s%s|%s]]', options.link, options.label, options.separator or " ", options.prefix, options.id, options.suffix or "", mw.text.nowiki(options.id) ); end -- Format an external link with error checking function externallink( URL, label, source ) local error_str = ""; if not is_set( label ) then label = URL; if is_set( source ) then error_str = seterror( 'bare_url_missing_title', { wrap( 'parameter', source ) }, false, " " ); else error( cfg.messages["bare_url_no_origin"] ); end end if not checkurl( URL ) then error_str = seterror( 'bad_url', {}, false, " " ) .. error_str; end return table.concat({ "[", URL, " ", safeforurl( label ), "]", error_str }); end -- Formats a link to Amazon function amazon(id, domain) if not is_set(domain) then domain = "com" elseif ( "jp" == domain or "uk" == domain ) then domain = "co." .. domain end local handler = cfg.id_handlers['ASIN']; return externallinkid({link = handler.link, label=handler.label , prefix="//www.amazon."..domain.."/dp/",id=id, encode=handler.encode, separator = handler.separator}) end -- Formats a DOI and checks for DOI errors. function doi(id, inactive) local cat = "" local handler = cfg.id_handlers['DOI']; local text; if is_set(inactive) then text = "[[" .. handler.link .. "|" .. handler.label .. "]]:" .. id; table.insert( z.error_categories, "Pages with DOIs inactive since " .. selectyear(inactive) ); inactive = " (" .. cfg.messages['inactive'] .. " " .. inactive .. ")" else text = externallinkid({link = handler.link, label = handler.label, prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) inactive = "" end if ( string.sub(id,1,3) ~= "10." ) then cat = seterror( 'bad_doi' ); end return text .. inactive .. cat end -- Formats an OpenLibrary link, and checks for associated errors. function openlibrary(id) local code = id:sub(-1,-1) local handler = cfg.id_handlers['OL']; if ( code == "A" ) then return externallinkid({link=handler.link, label=handler.label, prefix="http://openlibrary.org/authors/OL",id=id, separator=handler.separator, encode = handler.encode}) elseif ( code == "M" ) then return externallinkid({link=handler.link, label=handler.label, prefix="http://openlibrary.org/books/OL",id=id, separator=handler.separator, encode = handler.encode}) elseif ( code == "W" ) then return externallinkid({link=handler.link, label=handler.label, prefix= "http://openlibrary.org/works/OL",id=id, separator=handler.separator, encode = handler.encode}) else return externallinkid({link=handler.link, label=handler.label, prefix= "http://openlibrary.org/OL",id=id, separator=handler.separator, encode = handler.encode}) .. ' ' .. seterror( 'bad_ol' ); end end --[[ Determines whether an URL string is valid At present the only check is whether the string appears to be prefixed with a URI scheme. It is not determined whether the URI scheme is valid or whether the URL is otherwise well formed. ]] function checkurl( url_str ) -- Protocol-relative or URL scheme return url_str:sub(1,2) == "//" or url_str:match( "^[^/]*:" ) ~= nil; end -- Removes irrelevant text and dashes from ISBN number -- Similar to that used for Special:BookSources function cleanisbn( isbn_str ) return isbn_str:gsub( "[^-0-9X]", "" ); end -- Determines whether an ISBN string is valid function checkisbn( isbn_str ) isbn_str = cleanisbn( isbn_str ):gsub( "-", "" ); local len = isbn_str:len(); if len ~= 10 and len ~= 13 then return false; end local temp = 0; if len == 10 then if isbn_str:match( "^%d*X?$" ) == nil then return false; end isbn_str = { isbn_str:byte(1, len) }; for i, v in ipairs( isbn_str ) do if v == string.byte( "X" ) then temp = temp + 10*( 11 - i ); else temp = temp + tonumber( string.char(v) )*(11-i); end end return temp % 11 == 0; else if isbn_str:match( "^%d*$" ) == nil then return false; end isbn_str = { isbn_str:byte(1, len) }; for i, v in ipairs( isbn_str ) do temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) ); end return temp % 10 == 0; end end -- Gets the display text for a wikilink like [[A|B]] or [[B]] gives B function removewikilink( str ) return (str:gsub( "%[%[([^%[%]]*)%]%]", function(l) return l:gsub( "^[^|]*|(.*)$", "%1" ):gsub("^%s*(.-)%s*$", "%1"); end)); end -- Escape sequences for content that will be used for URL descriptions function safeforurl( str ) if str:match( "%[%[.-%]%]" ) ~= nil then table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } ); end return str:gsub( '[%[%]\n]', { ['['] = '[', [']'] = ']', ['\n'] = ' ' } ); end -- Converts a hyphen to a dash function hyphentodash( str ) if not is_set(str) or str:match( "[%[%]{}<>]" ) ~= nil then return str; end return str:gsub( '-', '–' ); end -- Protects a string that will be wrapped in wiki italic markup '' ... '' function safeforitalics( str ) --[[ Note: We can not use <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 = "[[" .. person.link .. "|" .. one .. "]]" end end table.insert( text, one ) table.insert( text, sep_one ) end end local count = #text / 2; if count > 0 then if count > 1 and is_set(lastauthoramp) and not etal then text[#text-2] = " & "; end text[#text] = nil; end local result = table.concat(text) -- construct list if etal then local etal_text = cfg.messages['et al']; 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[list_name .. '-Last'], 'redundant_parameters', i ); if not is_set(last) then -- just in case someone passed in an empty parameter break; end names[i] = { last = last, first = selectone( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ), link = selectone( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ), mask = selectone( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i ) }; i = i + 1; end return names; end -- Populates ID table from arguments using configuration settings function extractids( args ) local id_list = {}; for k, v in pairs( cfg.id_handlers ) do v = selectone( args, v.parameters, 'redundant_parameters' ); if is_set(v) then id_list[k] = v; end end return id_list; end -- Takes a table of IDs and turns it into a table of formatted ID outputs. function buildidlist( id_list, options ) local new_list, handler = {}; function fallback(k) return { __index = function(t,i) return cfg.id_handlers[k][i] end } end; for k, v in pairs( id_list ) do -- fallback to read-only cfg handler = setmetatable( { ['id'] = v }, fallback(k) ); if handler.mode == 'external' then table.insert( new_list, {handler.label, externallinkid( handler ) } ); elseif handler.mode == 'internal' then table.insert( new_list, {handler.label, internallinkid( handler ) } ); elseif handler.mode ~= 'manual' then error( cfg.messages['unknown_ID_mode'] ); elseif k == 'DOI' then table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } ); elseif k == 'ASIN' then table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } ); elseif k == 'OL' then table.insert( new_list, {handler.label, openlibrary( v ) } ); elseif k == 'ISBN' then local ISBN = internallinkid( handler ); if not checkisbn( v ) and not is_set(options.IgnoreISBN) then ISBN = ISBN .. seterror( 'bad_isbn', {}, false, " ", "" ); end table.insert( new_list, {handler.label, ISBN } ); else error( cfg.messages['unknown_manual_ID'] ); end end function comp( a, b ) return a[1] < b[1]; end table.sort( new_list, comp ); for k, v in ipairs( new_list ) do new_list[k] = v[2]; end return new_list; end -- Chooses one matching parameter from a list of parameters to consider -- Generates an error if more than one match is present. function selectone( args, possible, error_condition, index ) local value = nil; local selected = ''; local error_list = {}; if index ~= nil then index = tostring(index); end -- Handle special case of "#" replaced by empty string if index == '1' then for _, v in ipairs( possible ) do v = v:gsub( "#", "" ); if is_set(args[v]) then if value ~= nil and selected ~= v then table.insert( error_list, v ); else value = args[v]; selected = v; end end end end for _, v in ipairs( possible ) do if index ~= nil then v = v:gsub( "#", index ); end if is_set(args[v]) then if value ~= nil and selected ~= v then table.insert( error_list, v ); else value = args[v]; selected = v; end end end if #error_list > 0 then local error_str = ""; for _, k in ipairs( error_list ) do if error_str ~= "" then error_str = error_str .. cfg.messages['parameter-separator'] end error_str = error_str .. wrap( 'parameter', k ); end if #error_list > 1 then error_str = error_str .. cfg.messages['parameter-final-separator']; else error_str = error_str .. cfg.messages['parameter-pair-separator']; end error_str = error_str .. wrap( 'parameter', selected ); table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } ); end return value, selected; end -- COinS metadata (see <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["rft.genre"] = "bookitem"; OCinSoutput["rft.btitle"] = data.Chapter; OCinSoutput["rft.atitle"] = data.Title; elseif is_set(data.Periodical) then OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"; OCinSoutput["rft.genre"] = "article"; OCinSoutput["rft.jtitle"] = data.Periodical; OCinSoutput["rft.atitle"] = data.Title; else OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; OCinSoutput["rft.genre"] = "book" OCinSoutput["rft.btitle"] = data.Title; end OCinSoutput["rft.place"] = data.PublicationPlace; OCinSoutput["rft.date"] = data.Date; OCinSoutput["rft.series"] = data.Series; OCinSoutput["rft.volume"] = data.Volume; OCinSoutput["rft.issue"] = data.Issue; OCinSoutput["rft.pages"] = data.Pages; OCinSoutput["rft.edition"] = data.Edition; OCinSoutput["rft.pub"] = data.PublisherName; for k, v in pairs( data.ID_list ) do local id, value = cfg.id_handlers[k].COinS; if k == 'ISBN' then value = cleanisbn( v ); else value = v; end if string.sub( id or "", 1, 4 ) == 'info' then OCinSoutput["rft_id"] = table.concat{ id, "/", v }; else OCinSoutput[ id ] = value; end end local last, first; for k, v in ipairs( data.Authors ) do last, first = v.last, v.first; if k == 1 then if is_set(last) then OCinSoutput["rft.aulast"] = last; end if is_set(first) then OCinSoutput["rft.aufirst"] = first; end end if is_set(last) and is_set(first) then OCinSoutput["rft.au"] = table.concat{ last, ", ", first }; elseif is_set(last) then OCinSoutput["rft.au"] = last; end end OCinSoutput.rft_id = data.URL; OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage }; OCinSoutput = setmetatable( OCinSoutput, nil ); -- sort with version string always first, and combine. table.sort( OCinSoutput ); table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver ); -- such as "Z39.88-2004" return table.concat(OCinSoutput, "&"); end --[[ This is the main function foing the majority of the citation formatting. ]] function citation0( config, args) --[[ Load Input Parameters The argment_wrapper facillitates the mapping of multiple aliases to single internal variable. ]] local A = argument_wrapper( args ); local i local PPrefix = A['PPrefix'] local PPPrefix = A['PPPrefix'] if is_set( A['NoPP'] ) then PPPrefix = "" PPrefix = "" end -- Pick out the relevant fields from the arguments. Different citation templates -- define different field names for the same underlying things. local Authors = A['Authors']; local a = extractnames( args, 'AuthorList' ); local Coauthors = A['Coauthors']; local Others = A['Others']; local Editors = A['Editors']; local e = extractnames( args, 'EditorList' ); local Year = A['Year']; local PublicationDate = A['PublicationDate']; local OrigYear = A['OrigYear']; local Date = A['Date']; local LayDate = A['LayDate']; ------------------------------------------------- Get title data local Title = A['Title']; local BookTitle = A['BookTitle']; local Conference = A['Conference']; local TransTitle = A['TransTitle']; local TitleNote = A['TitleNote']; local TitleLink = A['TitleLink']; local Chapter = A['Chapter']; local ChapterLink = A['ChapterLink']; local TransChapter = A['TransChapter']; local TitleType = A['TitleType']; local ArchiveURL = A['ArchiveURL']; local URL = A['URL'] local URLorigin = A:ORIGIN('URL'); local ChapterURL = A['ChapterURL']; local ChapterURLorigin = A:ORIGIN('ChapterURL'); local ConferenceURL = A['ConferenceURL']; local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); local Periodical = A['Periodical']; if ( config.CitationClass == "encyclopaedia" ) then if not is_set(Chapter) then if not is_set(Title) then Title = Periodical; Periodical = ''; else Chapter = Title TransChapter = TransTitle Title = ''; TransTitle = ''; end end end local Series = A['Series']; local Volume = A['Volume']; local Issue = A['Issue']; local Position = ''; local Page, Pages, At, page_type; Page = A['Page']; Pages = hyphentodash( A['Pages'] ); At = A['At']; if is_set(Page) then if is_set(Pages) or is_set(At) then Page = Page .. " " .. seterror('extra_pages'); Pages = ''; At = ''; end elseif is_set(Pages) then if is_set(At) then Pages = Pages .. " " .. seterror('extra_pages'); At = ''; end end local Edition = A['Edition']; local PublicationPlace = A['PublicationPlace'] local Place = A['Place']; if not is_set(PublicationPlace) and is_set(Place) then PublicationPlace = Place; end if PublicationPlace == Place then Place = ''; end local PublisherName = A['PublisherName']; local SubscriptionRequired = A['SubscriptionRequired']; local Via = A['Via']; local AccessDate = A['AccessDate']; local ArchiveDate = A['ArchiveDate']; local Agency = A['Agency']; local DeadURL = A['DeadURL'] local Language = A['Language']; local Format = A['Format']; local Ref = A['Ref']; local DoiBroken = A['DoiBroken']; local ID = A['ID']; local ASINTLD = A['ASINTLD']; local IgnoreISBN = A['IgnoreISBN']; local ID_list = extractids( args ); local Quote = A['Quote']; local PostScript = A['PostScript']; local LayURL = A['LayURL']; local LaySource = A['LaySource']; local Transcript = A['Transcript']; local TranscriptURL = A['TranscriptURL'] local TranscriptURLorigin = A:ORIGIN('TranscriptURL'); local sepc = A['Separator']; local LastAuthorAmp = A['LastAuthorAmp']; local no_tracking_cats = A['NoTracking']; local this_page = mw.title.getCurrentTitle(); --Also used for COinS if not is_set(no_tracking_cats) then for k, v in pairs( cfg.uncategorized_namespaces ) do if this_page.nsText == v then no_tracking_cats = "true"; break; end end end if ( config.CitationClass == "journal" ) then if not is_set(URL) and is_set(ID_list['PMC']) then local Embargo = A['Embargo']; if is_set(Embargo) then local lang = mw.getContentLanguage(); local good1, result1, good2, result2; good1, result1 = pcall( lang.formatDate, lang, 'U', Embargo ); good2, result2 = pcall( lang.formatDate, lang, 'U' ); if good1 and good2 and tonumber( result1 ) < tonumber( result2 ) then URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC']; URLorigin = cfg.id_handlers['PMC'].parameters[1]; end else URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC']; URLorigin = cfg.id_handlers['PMC'].parameters[1]; end end end -- At this point fields may be nil if they weren't specified in the template use. We can use that fact. -- Account for the oddity that is {{cite conference}}, before generation of COinS data. if is_set(BookTitle) then Chapter = Title; ChapterLink = TitleLink; TransChapter = TransTitle; Title = BookTitle; TitleLink = ''; TransTitle = ''; end -- Account for the oddity that is {{cite episode}}, before generation of COinS data. if config.CitationClass == "episode" then local AirDate = A['AirDate']; local SeriesLink = A['SeriesLink']; local Season = A['Season']; local SeriesNumber = A['SeriesNumber']; local Network = A['Network']; local Station = A['Station']; local s, n = {}, {}; local Sep = (first_set(A["SeriesSeparator"], A["Separator"]) or "") .. " "; if is_set(Issue) then table.insert(s, cfg.messages["episode"] .. " " .. Issue); Issue = ''; end if is_set(Season) then table.insert(s, cfg.messages["season"] .. " " .. Season); end if is_set(SeriesNumber) then table.insert(s, cfg.messages["series"] .. " " .. SeriesNumber); end if is_set(Network) then table.insert(n, Network); end if is_set(Station) then table.insert(n, Station); end Date = Date or AirDate; Chapter = Title; ChapterLink = TitleLink; TransChapter = TransTitle; Title = Series; TitleLink = SeriesLink; TransTitle = ''; Series = table.concat(s, Sep); ID = table.concat(n, Sep); end -- COinS metadata (see <http://ocoins.info/>) for -- automated parsing of citation information. local OCinSoutput = COinS{ ['Periodical'] = Periodical, ['Chapter'] = Chapter, ['Title'] = Title, ['PublicationPlace'] = PublicationPlace, ['Date'] = first_set(Date, Year, PublicationDate), ['Series'] = Series, ['Volume'] = Volume, ['Issue'] = Issue, ['Pages'] = first_set(Page, Pages, At), ['Edition'] = Edition, ['PublisherName'] = PublisherName, ['URL'] = first_set( URL, ChapterURL ), ['Authors'] = a, ['ID_list'] = ID_list, ['RawPage'] = this_page.prefixedText, }; if is_set(Periodical) and not is_set(Chapter) and is_set(Title) then Chapter = Title; ChapterLink = TitleLink; TransChapter = TransTitle; Title = ''; TitleLink = ''; TransTitle = ''; end -- Now perform various field substitutions. -- We also add leading spaces and surrounding markup and punctuation to the -- various parts of the citation, but only when they are non-nil. if not is_set(Authors) then local Maximum = tonumber( A['DisplayAuthors'] ); -- Preserve old-style implicit et al. if not is_set(Maximum) and #a == 9 then Maximum = 8; table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } ); elseif not is_set(Maximum) then Maximum = #a + 1; end local control = { sep = A["AuthorSeparator"] .. " ", namesep = (first_set(A["AuthorNameSeparator"], A["NameSeparator"]) or "") .. " ", format = A["AuthorFormat"], maximum = Maximum, lastauthoramp = LastAuthorAmp }; -- If the coauthor field is also used, prevent ampersand and et al. formatting. if is_set(Coauthors) then control.lastauthoramp = nil; control.maximum = #a + 1; end Authors = listpeople(control, a) end local EditorCount if not is_set(Editors) then local Maximum = tonumber( A['DisplayEditors'] ); -- Preserve old-style implicit et al. if not is_set(Maximum) and #e == 4 then Maximum = 3; table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } ); elseif not is_set(Maximum) then Maximum = #e + 1; end local control = { sep = A["EditorSeparator"] .. " ", namesep = (first_set(A["EditorNameSeparator"], A["NameSeparator"]) or "") .. " ", format = A['EditorFormat'], maximum = Maximum, lastauthoramp = LastAuthorAmp }; Editors, EditorCount = listpeople(control, e); else EditorCount = 1; end if not is_set(Date) then Date = Year; if is_set(Date) then local Month = A['Month']; if is_set(Month) then Date = Month .. " " .. Date; local Day = A['Day'] 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 = "[[" .. ChapterLink .. "|" .. 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 = "[[" .. TitleLink .. "|" .. Title .. "]]" end if is_set(Periodical) then Title = wrap( 'quoted-title', Title ); TransTitle = wrap( 'trans-quoted-title', TransTitle ); elseif inArray(config.CitationClass, {"web","news","pressrelease"}) and not is_set(Chapter) then Title = wrap( 'quoted-title', Title ); TransTitle = wrap( 'trans-quoted-title', TransTitle ); else Title = wrap( 'italic-title', Title ); TransTitle = wrap( 'trans-italic-title', TransTitle ); end TransError = ""; if is_set(TransTitle) then if not is_set(Title) then TransError = " " .. seterror( 'trans_missing_title' ); else TransTitle = " " .. TransTitle; end end Title = Title .. TransTitle; if is_set(Title) then if not is_set(TitleLink) and is_set(URL) then Title = externallink( URL, Title ) .. TransError .. Format URL = ""; Format = ""; else Title = Title .. TransError; end end if is_set(Place) then if sepc == '.' then Place = " " .. wrap( 'written', Place ) .. sepc .. " "; else Place = " " .. substitute( cfg.messages['written']:lower(), {Place} ) .. sepc .. " "; end end if is_set(Conference) then if is_set(ConferenceURL) then Conference = externallink( ConferenceURL, Conference ); end Conference = " " .. Conference elseif is_set(ConferenceURL) then Conference = " " .. externallink( ConferenceURL, nil, ConferenceURLorigin ); end if not is_set(Position) then local Minutes = A['Minutes']; if is_set(Minutes) then Position = " " .. Minutes .. " " .. cfg.messages['minutes']; else local Time = A['Time']; if is_set(Time) then local TimeCaption = A['TimeCaption'] if not is_set(TimeCaption) then TimeCaption = cfg.messages['event']; if sepc ~= '.' then TimeCaption = TimeCaption:lower(); end end Position = " " .. TimeCaption .. " " .. Time; end end else Position = " " .. Position; At = ''; end if not is_set(Page) then if is_set(Pages) then if is_set(Periodical) and not inArray(config.CitationClass, {"encyclopaedia","web","book","news"}) then Pages = ": " .. Pages; elseif 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 (" [" .. OrigYear .. "]") or ""; Agency = is_set(Agency) and (sepc .. " " .. Agency) or ""; if is_set(Volume) then if ( mw.ustring.len(Volume) > 4 ) then Volume = sepc .." " .. Volume; else Volume = " <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['retrieved'] 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['subscription']; end if is_set(ID) then ID = sepc .." ".. ID; end ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN} ); if is_set(URL) then URL = " " .. externallink( URL, nil, URLorigin ); end if is_set(Quote) then if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then Quote = Quote:sub(2,-2); end Quote = sepc .." " .. wrap( 'quoted-text', Quote ); PostScript = ""; elseif PostScript:lower() == "none" then PostScript = ""; end local Archived if is_set(ArchiveURL) then if not is_set(ArchiveDate) then ArchiveDate = seterror('archive_missing_date'); end if "no" == DeadURL then local arch_text = cfg.messages['archived']; if sepc ~= "." then arch_text = arch_text:lower() end Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'], { externallink( ArchiveURL, arch_text ), ArchiveDate } ); if not is_set(OriginalURL) then Archived = Archived .. " " .. seterror('archive_missing_url'); end elseif is_set(OriginalURL) then local arch_text = cfg.messages['archived-dead']; if sepc ~= "." then arch_text = arch_text:lower() end Archived = sepc .. " " .. substitute( arch_text, { externallink( OriginalURL, cfg.messages['original'] ), ArchiveDate } ); else local arch_text = cfg.messages['archived-missing']; if sepc ~= "." then arch_text = arch_text:lower() end Archived = 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['lay summary'] ) .. LaySource .. LayDate else Lay = sepc .. " " .. externallink( LayURL, cfg.messages['lay summary']:lower() ) .. LaySource .. LayDate end else Lay = ""; end if is_set(Transcript) then if is_set(TranscriptURL) then Transcript = externallink( TranscriptURL, Transcript ); end elseif is_set(TranscriptURL) then Transcript = externallink( TranscriptURL, nil, TranscriptURLorigin ); end local Publisher; if is_set(Periodical) and not inArray(config.CitationClass, {"encyclopaedia","web","pressrelease"}) then if is_set(PublisherName) then if is_set(PublicationPlace) then Publisher = PublicationPlace .. ": " .. PublisherName; else Publisher = PublisherName; end elseif is_set(PublicationPlace) then Publisher= PublicationPlace; else Publisher = ""; end 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['AuthorSeparator'] .. " " .. Coauthors end if is_set(Date) then Date = " ("..Date..")" .. OrigYear .. sepc .. " " elseif string.sub(Authors,-1,-1) == sepc then Authors = Authors .. " " else Authors = Authors .. sepc .. " " end if is_set(Editors) then local in_text = " " .. cfg.messages['in'] .. " " 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['editor']; else Editors = Editors .. ", " .. cfg.messages['editors']; end Date = " (" .. Date ..")" .. OrigYear .. sepc .. " " else if EditorCount <= 1 then Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " " else Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " " end end text = safejoin( {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[i] = v.last if i == 4 then break end end elseif is_set(Editors) then for i,v in ipairs(e) do names[i] = v.last if i == 4 then break end end end names[ #names + 1 ] = Year; id = anchorid(names) end options.id = id; end if string.len(text:gsub("<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[1]) then if i == #z.message_tail then text = text .. errorcomment( v[1], v[2] ); else text = text .. errorcomment( v[1] .. "; ", v[2] ); end end end end no_tracking_cats = no_tracking_cats:lower(); if inArray(no_tracking_cats, {"", "no", "false", "n"}) then for _, v in ipairs( z.error_categories ) do text = text .. '[[Category:' .. v ..']]'; end end return text end -- This is used by templates such as {{cite book}} to create the actual citation text. function z.citation(frame) local pframe = frame:getParent() local args = {}; local suggestions = {}; local error_text, error_state; local config = {}; for k, v in pairs( frame.args ) do config[k] = v; args[k] = v; end for k, v in pairs( pframe.args ) do if v ~= '' then if not validate( k ) then error_text = ""; if type( k ) ~= 'string' then -- Exclude empty numbered parameters if v:match("%S+") ~= nil then error_text, error_state = seterror( 'text_ignored', {v}, true ); end elseif validate( k:lower() ) then error_text, error_state = seterror( 'parameter_ignored_suggest', {k, k:lower()}, true ); else if #suggestions == 0 then suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' ); end if suggestions[ k:lower() ] ~= nil then error_text, error_state = seterror( 'parameter_ignored_suggest', {k, suggestions[ k:lower() ]}, true ); else error_text, error_state = seterror( 'parameter_ignored', {k}, true ); end end if error_text ~= '' then table.insert( z.message_tail, {error_text, error_state} ); end end args[k] = v; elseif args[k] ~= nil or (k == 'postscript') then args[k] = v; end end return citation0( config, args) end return z