Module:Citation/CS1: Difference between revisions
From All Skies Encyclopaedia
| imported>Dragons flight  (sync to sandbox, fixes COinS author list) | imported>Dragons flight   (sync to sandbox, very large update addressing configuration, error handling, id handling, and others) | ||
| Line 4: | Line 4: | ||
| } | } | ||
| -- Include translation message hooks, ID and error handling configuration settings. | |||
| local cfg = mw.loadData( 'Module:Citation/CS1/Configuration' ); | |||
| -- Contains a list of all recognized parameters | |||
| local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' ); | local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' ); | ||
| Line 25: | Line 29: | ||
| end | end | ||
| -- Formats a | -- Formats a comment for error trapping | ||
| function  | function errorcomment( content, hidden ) | ||
|     if hidden then  | |||
|     return '<span style="display: none;" class="citation-comment">' .. content .. '</span>'; | |||
|         return '<span style="display:none;font-size:100%" class="error citation-comment">' .. content .. '</span>'; | |||
|     else | |||
|         return '<span style="font-size:100%" class="error">' .. content .. '</span>'; | |||
|     end         | |||
| end | |||
| --[[ | |||
| Sets an error condition and returns the appropriate error message.  The actual placement | |||
| of the error message in the output is the responsibility of the calling function. | |||
| ]] | |||
| function seterror( error_id, args, raw ) | |||
|     local error_state = cfg.error_conditions[ error_id ]; | |||
|     if error_state == nil then | |||
|         error( cfg.message_list['undefined_error'] ); | |||
|     end | |||
|     if error_state.category ~= nil and error_state.category ~= "" then | |||
|         table.insert( z.error_categories, error_state.category ); | |||
|     end | |||
|     local message = error_state.message; | |||
|     if args ~= nil then | |||
|         for k, m in ipairs( args ) do | |||
|             message = message:gsub( "$" .. k .. "(%D)", m .. "%1" ); | |||
|         end | |||
|     end | |||
|     message = wikiescape(message) .. " ([[" .. cfg.message_list['help page link'] ..  | |||
|         "#" .. error_state.anchor .. "|" .. | |||
|         cfg.message_list['help page label'] .. "]])"; | |||
|     if raw == true then | |||
|         return message, error_state.hidden; | |||
|     end | |||
|     return errorcomment( message, error_state.hidden ); | |||
| end | end | ||
| Line 41: | Line 82: | ||
|             ['}'] = '}' } ); |             ['}'] = '}' } ); | ||
|     return text; |     return text; | ||
| end | |||
| -- Create an HTML tag | |||
| function createTag(t, frame) | |||
|     local name = t.name or "!-- --" | |||
|     local content = t.contents or "" | |||
|     local attrs = {} | |||
|     for n,v in pairs(t.params) do | |||
|         if (v) then | |||
|             table.insert(attrs, n .. "=\"" .. wikiescape(v) .. "\"") | |||
|         else | |||
|             table.insert(attrs, n) | |||
|         end | |||
|     end | |||
|     if ("" == content) then | |||
|         return "<" .. name .. " " .. table.concat(attrs, " ") .. "/>" | |||
|     else | |||
|         return "<" .. name .. " " .. table.concat(attrs, " ") .. ">" .. content .. "</" .. name .. ">" | |||
|     end | |||
| end | |||
| --[[ | |||
| This is a clone of mw.text.nowiki.  When the mw.text library is installed, | |||
| this can be replaced by a call to that library.  | |||
| ]] | |||
| function nowiki( s ) | |||
|     -- string.gsub is safe here, because we're only caring about ASCII chars | |||
|     s = string.gsub( s, '["&\'<=>%[%]{|}]', { | |||
|         ['"'] = '"', | |||
|         ['&'] = '&', | |||
|         ["'"] = ''', | |||
|         ['<'] = '<', | |||
|         ['='] = '=', | |||
|         ['>'] = '>', | |||
|         ['['] = '[', | |||
|         [']'] = ']', | |||
|         ['{'] = '{', | |||
|         ['|'] = '|', | |||
|         ['}'] = '}', | |||
|     } ) | |||
|     s = string.sub( string.gsub( '\n' .. s, '\n[#*:;]', { | |||
|         ["\n#"] = "\n#", | |||
|         ["\n*"] = "\n*", | |||
|         ["\n:"] = "\n:", | |||
|         ["\n;"] = "\n;", | |||
|     } ), 2 ) | |||
|     s = string.gsub( s, '://', '://' ) | |||
|     s = string.gsub( s, 'ISBN ', 'ISBN ' ) | |||
|     s = string.gsub( s, 'RFC ', 'RFC ' ) | |||
|     return s | |||
| end | end | ||
| Line 104: | Line 94: | ||
|     return "[[" .. options.link .. "|" .. options.label .. "]]" .. sep .. "[" ..  |     return "[[" .. options.link .. "|" .. options.label .. "]]" .. sep .. "[" ..  | ||
|             options.prefix .. url_string .. options.suffix .. " " .. nowiki(options.id) .. "]" |             options.prefix .. url_string .. options.suffix .. " " .. mw.text.nowiki(options.id) .. "]" | ||
| end | end | ||
| Line 112: | Line 102: | ||
|     options.suffix = options.suffix or "" |     options.suffix = options.suffix or "" | ||
|     return "[[" .. options.link .. "|" .. options.label .. "]]" .. sep .. "[[" ..  |     return "[[" .. options.link .. "|" .. options.label .. "]]" .. sep .. "[[" ..  | ||
|             options.prefix .. options.id .. options.suffix .. "|" .. nowiki(options.id) .. "]]" |             options.prefix .. options.id .. options.suffix .. "|" .. mw.text.nowiki(options.id) .. "]]" | ||
| end | end | ||
| Line 122: | Line 112: | ||
|         domain = "co." .. domain |         domain = "co." .. domain | ||
|     end |     end | ||
|     local handler = cfg.id_handlers['ASIN']; | |||
|     return externallinkid({link="Amazon Standard Identification Number", | |||
|     return externallinkid({link = handler.link, | |||
|         label="ASIN",prefix="//www.amazon."..domain.."/dp/",id=id,encode=false}) | |||
|         label=handler.label , prefix="//www.amazon."..domain.."/dp/",id=id, | |||
|         encode=handler.encode, separator = handler.separator}) | |||
| end | end | ||
| Line 129: | Line 121: | ||
| function doi(id, inactive) | function doi(id, inactive) | ||
|     local cat = "" |     local cat = "" | ||
|     local handler = cfg.id_handlers['DOI']; | |||
|     local text; |     local text; | ||
|     if ( inactive ~= nil ) then  |     if ( inactive ~= nil ) then  | ||
|         text = "[[ |         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 = " (inactive " .. inactive .. ")"  |         inactive = " (" .. cfg.message_list['inactive'] .. " " .. inactive .. ")"  | ||
|     else  |     else  | ||
|         text = externallinkid({link= |         text = externallinkid({link = handler.link, label = handler.label, | ||
|             prefix= |             prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode}) | ||
|         inactive = ""  |         inactive = ""  | ||
|     end |     end | ||
|     if ( string.sub(id,1,3) ~= "10." ) then |     if ( string.sub(id,1,3) ~= "10." ) then       | ||
|         cat = seterror( 'bad_doi' ); | |||
|         table.insert( z.error_categories, "Pages with DOI errors" );         | |||
|         cat = ' <span class="error">Bad DOI (expected "10." prefix) in code number</span>' | |||
|     end |     end | ||
|     return text .. inactive .. cat  |     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 ISBN string is valid | |||
| function checkisbn( isbn_str ) | |||
|     isbn_str = isbn_str:gsub("[- ]", ""):upper(); | |||
|     local len = isbn_str:len(); | |||
|     if len ~= 10 and len ~= 13 then | |||
|         return false; | |||
|     end | |||
|     local temp = 0; | |||
|     if len == 10 then | |||
|         if isbn_str:match( "^%d*X?$" ) == nil then return false; end | |||
|         isbn_str = { isbn_str:byte(1, len) }; | |||
|         for i, v in ipairs( isbn_str ) do | |||
|             if v == string.byte( "X" ) then | |||
|                 temp = temp + 10*( 11 - i ); | |||
|             else | |||
|                 temp = temp + tonumber( string.char(v) )*(11-i); | |||
|             end | |||
|         end | |||
|         return temp % 11 == 0; | |||
|     else | |||
|         if isbn_str:match( "^%d*$" ) == nil then return false; end | |||
|         isbn_str = { isbn_str:byte(1, len) }; | |||
|         for i, v in ipairs( isbn_str ) do | |||
|             temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) ); | |||
|         end | |||
|         return temp % 10 == 0; | |||
|     end | |||
| end | |||
| -- Escape sequences for content that will be used for URL descriptions | -- Escape sequences for content that will be used for URL descriptions | ||
| function safeforurl( str ) | function safeforurl( str ) | ||
|     if str:match( "%[%[.-%]%]" ) ~= nil then  |     if str:match( "%[%[.-%]%]" ) ~= nil then  | ||
|         table.insert( z. |         table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } ); | ||
|         table.insert( z.message_tail, "Wikilink embedded in URL title" ); | |||
|     end |     end | ||
| Line 281: | Line 327: | ||
|             return ""; |             return ""; | ||
|         end |         end | ||
|     end | |||
| end | |||
| -- Formats an OpenLibrary link, and checks for associated errors. | |||
| function openlibrary(id) | |||
|     local code = id:sub(-1,-1) | |||
|     if ( code == "A" ) then | |||
|         return externallinkid({link="Open Library",label="OL", | |||
|             prefix="http://openlibrary.org/authors/OL",id=id}) | |||
|     elseif ( code == "M" ) then | |||
|         return externallinkid({link="Open Library",label="OL", | |||
|             prefix="http://openlibrary.org/books/OL",id=id}) | |||
|     elseif ( code == "W" ) then | |||
|         return externallinkid({link="Open Library",label="OL", | |||
|             prefix= "http://openlibrary.org/works/OL",id=id}) | |||
|     else | |||
|         table.insert( z.error_categories, "Pages with OL errors" ); | |||
|         return externallinkid({link="Open Library",label="OL", | |||
|             prefix= "http://openlibrary.org/OL",id=id}) ..  | |||
|             ' <span class="error">Bad OL specified</span>'; | |||
|     end |     end | ||
| end | end | ||
| Line 356: | Line 382: | ||
|     local result = table.concat(text, sep) -- construct list |     local result = table.concat(text, sep) -- construct list | ||
|     if etal then  |     if etal then  | ||
|         local etal_text =  |         local etal_text = cfg.message_list['et al']; | ||
|         if (sepc == ".") then etal_text = "et al" end | |||
|         result = result .. " " .. etal_text; |         result = result .. " " .. etal_text; | ||
|     end |     end | ||
|     -- if necessary wrap result in <span> tag to format in Small Caps |     -- if necessary wrap result in <span> tag to format in Small Caps | ||
|     if ( "scap" == format ) then result |     if ( "scap" == format ) then result =  | ||
|         '<span class="smallcaps" style="font-variant:small-caps">' .. result .. '</span>'; | |||
|     end  | |||
|     return result, count |     return result, count | ||
| end | end | ||
| Line 389: | Line 415: | ||
|     local i = 1; |     local i = 1; | ||
|     local last; |     local last; | ||
|     while true do |     while true do | ||
|         if i == 1 then  | |||
|         last = args["author" .. i .. "-last"] or args["author-last" .. i] or  | |||
|             last = selectone( args, {"author" .. i .. "-last", "author-last" .. i,  | |||
|                 "last" .. i, "surname" .. i, "Author" .. i, "author" .. i,  | |||
|                 "author-last", "last", "surname", "Author", "author", "authors"}, 'redundant_parameters' ); | |||
|         else | |||
|             last = selectone( args, {"author" .. i .. "-last", "author-last" .. i,  | |||
|                 "last" .. i, "surname" .. i, "Author" .. i, "author" .. i}, 'redundant_parameters' ); | |||
|         end | |||
|         if ( last and "" < last ) then -- just in case someone passed in an empty parameter |         if ( last and "" < last ) then -- just in case someone passed in an empty parameter | ||
|             if i == 1 then  | |||
|                 authors[i] = { | |||
|                     last = last, | |||
|                     first = selectone( args, {"author" .. i .. "-first", "author-first" .. i,  | |||
|                         "first" .. i, "given" .. i, "author-first",  | |||
|                         "first", "given"}, 'redundant_parameters' ), | |||
|                     link = selectone( args, {"author" .. i .. "-link", "author-link" .. i,  | |||
|                         "author" .. i .. "link", "authorlink" .. i, "author-link",   | |||
|             } |                         "authorlink"}, 'redundant_parameters' ), | ||
|                     mask = selectone( args, {"author" .. i .. "-mask", "author-mask" .. i,  | |||
|                         "author" .. i .. "mask", "authormask" .. i, "author-mask",  | |||
|                         "authormask" }, 'redundant_parameters' ) | |||
|                 } | |||
|             else | |||
|                 authors[i] = { | |||
|                     last = last, | |||
|                     first = selectone( args, {"author" .. i .. "-first", "author-first" .. i,  | |||
|                         "first" .. i, "given" .. i}, 'redundant_parameters' ), | |||
|                     link = selectone( args, {"author" .. i .. "-link", "author-link" .. i,  | |||
|                         "author" .. i .. "link", "authorlink" .. i}, 'redundant_parameters' ), | |||
|                     mask = selectone( args, {"author" .. i .. "-mask", "author-mask" .. i,  | |||
|                         "author" .. i .. "mask", "authormask" .. i}, 'redundant_parameters' ) | |||
|                 } | |||
|             end             | |||
|         else |         else | ||
|             break; |             break; | ||
| Line 418: | Line 464: | ||
|     while true do |     while true do | ||
|         if i == 1 then | |||
|         last = args["editor" .. i .. "-last"] or args["editor-last" .. i] or  | |||
|             last = selectone( args, {"editor" .. i .. "-last", "editor-last" .. i, | |||
|                 "EditorSurname" .. i, "Editor" .. i, "editor" .. i, "editor-last",  | |||
|                 "EditorSurname", "Editor", "editor", "editors"}, 'redundant_parameters' ); | |||
|         else | |||
|             last = selectone( args, {"editor" .. i .. "-last", "editor-last" .. i, | |||
|                 "EditorSurname" .. i, "Editor" .. i, "editor" .. i}, 'redundant_parameters' ); | |||
|         end         | |||
|         if ( last and "" < last ) then -- just in case someone passed in an empty parameter |         if ( last and "" < last ) then -- just in case someone passed in an empty parameter | ||
|             if i == 1 then | |||
|                 editors[i] = { | |||
|                     last = last, | |||
|                 first = args["editor" .. i .. "-first"] or args["editor-first" .. i] or args["EditorGiven" .. i], | |||
|                     first = selectone( args, {"editor" .. i .. "-first",  | |||
|                         "editor-first" .. i, "EditorGiven" .. i, "editor-first",  | |||
|                         "EditorGiven"}, 'redundant_parameters' ), | |||
|                     link = selectone( args, {"editor" .. i .. "-link", "editor-link" .. i,  | |||
|                         "editor" .. i .. "link", "editorlink" .. i, "editor-link",  | |||
|             } | |||
|                         "editorlink"}, 'redundant_parameters' ), | |||
|                     mask = selectone( args, {"editor" .. i .. "-mask", "editor-mask" .. i,  | |||
|                         "editor" .. i .. "mask", "editormask" .. i, "editor-mask",   | |||
|                         "editormask"}, 'redundant_parameters' ) | |||
|                 }                 | |||
|             else | |||
|                 editors[i] = { | |||
|                     last = last, | |||
|                     first = selectone( args, {"editor" .. i .. "-first",  | |||
|                         "editor-first" .. i, "EditorGiven" .. i}, 'redundant_parameters' ), | |||
|                     link = selectone( args, {"editor" .. i .. "-link", "editor-link" .. i,  | |||
|                         "editor" .. i .. "link", "editorlink" .. i}, 'redundant_parameters' ), | |||
|                     mask = selectone( args, {"editor" .. i .. "-mask", "editor-mask" .. i,  | |||
|                         "editor" .. i .. "mask", "editormask" .. i}, 'redundant_parameters' ) | |||
|                 } | |||
|             end | |||
|         else |         else | ||
|             break; |             break; | ||
| Line 435: | Line 503: | ||
|     end |     end | ||
|     return editors; |     return editors; | ||
| end | |||
| -- Populates ID table from arguments using configuration settings | |||
| function extractids( args ) | |||
|     local id_list = {}; | |||
|     for k, v in pairs( cfg.id_handlers ) do     | |||
|         id_list[k] = selectone( args, v.parameters, 'redundant_parameters' ); | |||
|     end | |||
|     return id_list; | |||
| end | |||
| -- Takes a table of IDs and turns it into a table of formatted ID outputs. | |||
| function buildidlist( id_list, options ) | |||
|     local handler; | |||
|     local new_list = {}; | |||
|     for k, v in pairs( id_list ) do | |||
|         handler = {}; | |||
|         --Becasue cfg is read-only we have to copy it the hard way. | |||
|         for k2, v2 in pairs( cfg.id_handlers[k] ) do | |||
|             handler[k2] = v2; | |||
|         end | |||
|         handler['id'] = v; | |||
|         if handler.mode == 'external' then         | |||
|             table.insert( new_list, {handler.label, externallinkid( handler ) } ); | |||
|         elseif handler.mode == 'internal' then | |||
|             table.insert( new_list, {handler.label, internallinkid( handler ) } ); | |||
|         elseif handler.mode == 'manual' then | |||
|             if k == 'DOI' then | |||
|                 table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } ); | |||
|             elseif k == 'ASIN' then | |||
|                 table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } );  | |||
|             elseif k == 'OL' then | |||
|                 table.insert( new_list, {handler.label, openlibrary( v ) } ); | |||
|             elseif k == 'ISBN' then | |||
|                 local ISBN = internallinkid( handler ); | |||
|                 if not checkisbn( v ) then  | |||
|                     ISBN = ISBN .. seterror( 'bad_isbn' ); | |||
|                 end | |||
|                 table.insert( new_list, {handler.label, ISBN } );                 | |||
|             else | |||
|                 error( cfg.message_list['unknown_manual_ID'] ); | |||
|             end             | |||
|         else | |||
|             error( cfg.message_list['unknown_ID_mode'] ); | |||
|         end | |||
|     end | |||
|     function comp( a, b ) | |||
|         return a[1] < b[1]; | |||
|     end | |||
|     table.sort( new_list, comp ); | |||
|     for k, v in ipairs( new_list ) do | |||
|         new_list[k] = v[2]; | |||
|     end | |||
|     return new_list; | |||
| end | |||
| -- Chooses one matching parameter from a list of parameters to consider | |||
| -- Generates an error if more than one match is present. | |||
| function selectone( args, possible, error_condition ) | |||
|     local value = nil; | |||
|     local selected = ''; | |||
|     local error_list = {}; | |||
|     for _, v in ipairs( possible ) do | |||
|         if args[v] ~= nil then | |||
|             if value ~= nil then | |||
|                 table.insert( error_list, v ); | |||
|             else | |||
|                 value = args[v]; | |||
|                 selected = v; | |||
|             end | |||
|         end | |||
|     end | |||
|     if #error_list > 0 then | |||
|         local error_str = ""; | |||
|         for _, k in ipairs( error_list ) do | |||
|             if error_str ~= "" then error_str = error_str .. ", " end | |||
|             error_str = error_str .. "<code>|" .. k .. "=</code>"; | |||
|         end | |||
|         if #error_list > 1 then | |||
|             error_str = error_str .. ", and "; | |||
|         else | |||
|             error_str = error_str .. " and "; | |||
|         end | |||
|         error_str = error_str .. "<code>|" .. selected .. "=</code>"; | |||
|         table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } ); | |||
|     end | |||
|     return value, selected; | |||
| end | end | ||
| Line 444: | Line 610: | ||
|     -- Load Input Parameters |     -- Load Input Parameters | ||
|     local i  | |||
|     local PPrefix = config.PPrefix or "p. " |     local PPrefix = config.PPrefix or "p. " | ||
|     local PPPrefix = config.PPPrefix or "pp. " |     local PPPrefix = config.PPPrefix or "pp. " | ||
|     if ( nil ~= args.nopp ) then PPPrefix = "" PPrefix = "" end |     if ( nil ~= args.nopp ) then PPPrefix = "" PPrefix = "" end | ||
|     -- Transfer unnumbered arguments to numbered arguments if necessary. | |||
|     args["author1"] = args.author1 or args.author or args.authors | |||
|     args["author1-last"] = args["author1-last"] or args["author-last"] or args["last"] | |||
|     args["author1-first"] = args["author1-first"] or args["author-first"] | |||
|       or args.first or args.first1 or args.given or args.given1 | |||
|     args["author1-link"] = args["author1-link"] or args["author-link"] | |||
|     args["author1-mask"] = args["author1-mask"] or args["author-mask"] or args["authormask"] | |||
|     args["author1link"] = args["author1link"] or args["authorlink"]     | |||
|     args["editor1"] = args["editor1"] or args["editor"] | |||
|     args["editor1-last"] = args["editor1-last"] or args["editor-last"] | |||
|     args["editor1-first"] = args["editor1-first"] or args["editor-first"] | |||
|     args["editor1-link"] = args["editor1-link"] or args["editor-link"] | |||
|     args["editor1-mask"] = args["editor1-mask"] or args["editor-mask"] or args["editormask"] | |||
|     args["editor1link"] = args["editor1link"] or args["editorlink"]     | |||
|     -- Pick out the relevant fields from the arguments.  Different citation templates |     -- Pick out the relevant fields from the arguments.  Different citation templates | ||
|     -- define different field names for the same underlying things.     |     -- define different field names for the same underlying things.     | ||
|     local Authors = args.authors |     local Authors = args.authors | ||
|     local i  | |||
|     local a = extractauthors( args ); |     local a = extractauthors( args ); | ||
|     local Coauthors = args.coauthors or args.coauthor  |     local Coauthors = args.coauthors or args.coauthor  | ||
|     local Others = args.others  |     local Others = args.others  | ||
|     local EditorMask = args.editormask or args["editor-mask"] | |||
|     local EditorFormat = args["editor-format"] or args.editorformat | |||
|     local Editors = args.editors |     local Editors = args.editors | ||
|     local e = extracteditors( args ); |     local e = extracteditors( args ); | ||
| Line 488: | Line 637: | ||
|     local TitleNote = args.department |     local TitleNote = args.department | ||
|     local TitleLink = args.titlelink or args.episodelink |     local TitleLink = args.titlelink or args.episodelink | ||
|     local Chapter = args |     local Chapter = selectone( args, {'chapter', 'contribution', 'entry' }, 'redundant_parameters' ); | ||
|     local ChapterLink = args.chapterlink |     local ChapterLink = args.chapterlink | ||
|     local TransChapter = args["trans-chapter"] or args.trans_chapter |     local TransChapter = args["trans-chapter"] or args.trans_chapter | ||
| Line 496: | Line 645: | ||
|     local ChapterURL = args["chapter-url"] or args.chapterurl or args["contribution-url"] |     local ChapterURL = args["chapter-url"] or args.chapterurl or args["contribution-url"] | ||
|     local ConferenceURL = args["conference-url"] or args.conferenceurl |     local ConferenceURL = args["conference-url"] or args.conferenceurl | ||
|     local Periodical = args |     local Periodical = selectone( args, {'journal', 'newspaper', 'magazine', 'work', 'website',  | ||
|         'periodical', 'encyclopedia', 'encyclopaedia'}, 'redundant_parameters' ); | |||
|     if ( config.CitationClass == "encyclopaedia" ) then |     if ( config.CitationClass == "encyclopaedia" ) then | ||
| Line 518: | Line 667: | ||
|         end |         end | ||
|     end |     end | ||
|     local Series = args |     local Series = selectone( args, {'series', 'version'}, 'redundant_parameters' ); | ||
|     local Volume = args.volume |     local Volume = args.volume | ||
|     local Issue = args |     local Issue = selectone( args, {'issue', 'number'}, 'redundant_parameters' ); | ||
|     local Position = nil |     local Position = nil | ||
|     local Page  |     local Page, Pages, At, page_type; | ||
|     local Pages = hyphentodash( args.pp or args.pages ) | |||
|     local At = args.at | |||
|     local page_error = false; | |||
|     Page, page_type = selectone( args, {'p', 'page', 'pp', 'pages', 'at'},  | |||
|         'extra_pages' ); | |||
|         if (Pages ~= nil and Pages ~= '') or (At ~= nil and At ~= '') then | |||
|     if page_type == 'pp' or page_type == 'pages' then | |||
|         Pages = hyphentodash( Page ); | |||
|         Page = nil; | |||
|     elseif page_type == 'at' then | |||
|         At = Page; | |||
|         Page = nil; | |||
|             At = nil; | |||
|             page_error = true; | |||
|         end | |||
|     end |     end | ||
|     local Edition = args.edition |     local Edition = args.edition | ||
|     local PublicationPlace = args["publication-place"] or args.publicationplace  |     local PublicationPlace = args["publication-place"] or args.publicationplace  | ||
|     local Place = selectone( args, {'place', 'location'}, 'redundant_parameters' ); | |||
|             or args.place or args.location | |||
|     if PublicationPlace == nil and Place ~= nil then  | |||
|         PublicationPlace = Place; | |||
|     if PublicationPlace == Place then Place = nil; end | |||
|     end | |||
|     if PublicationPlace == Place then Place = nil end | |||
|     local PublisherName = args.publisher |     local PublisherName = args.publisher | ||
| Line 553: | Line 698: | ||
|     local Agency = args.agency |     local Agency = args.agency | ||
|     local DeadURL = args.deadurl or "yes"           -- Only used is ArchiveURL is present. |     local DeadURL = args.deadurl or "yes"           -- Only used is ArchiveURL is present. | ||
|     local Language = args |     local Language = selectone( args, {'language', 'in'}, 'redundant_parameters' ); | ||
|     local Format = args.format |     local Format = args.format | ||
|     local Ref = args.ref or args.Ref |     local Ref = args.ref or args.Ref | ||
|     local ARXIV = args.arxiv or args.ARXIV | |||
|     local ASIN = args.asin or args.ASIN  | |||
|     local ASINTLD = args["ASIN-TLD"] or args["asin-tld"] | |||
|     local BIBCODE = args.bibcode or args.BIBCODE | |||
|     local DOI = args.doi or args.DOI | |||
|     local DoiBroken = args.doi_inactivedate or args.doi_brokendate or args.DoiBroken |     local DoiBroken = args.doi_inactivedate or args.doi_brokendate or args.DoiBroken | ||
|     local ID = args |     local ID = selectone( args, {'id', 'ID', 'docket'}, 'redundant_parameters' ); | ||
|     local  |     local ASINTLD = args["ASIN-TLD"] or args["asin-tld"] | ||
|     local ISSN = args.issn or args.ISSN | |||
|     local  |     local ID_list = extractids( args ); | ||
|     local JSTOR = args.jstor or args.JSTOR | |||
|     local  |     local Quote = selectone( args, {'quote', 'quotation'}, 'redundant_parameters' ); | ||
|     local MR = args.mr or args.MR | |||
|     local OCLC = args.oclc or args.OCLC | |||
|     local OL = args.ol or args.OL | |||
|     local OSTI = args.osti or args.OSTI | |||
|     local PMC = args.pmc or args.PMC | |||
|     local PMID = args.pmid or args.PMID | |||
|     local RFC = args.rfc or args.RFC | |||
|     local SSRN = args.ssrn or args.SSRN | |||
|     local ZBL = args.zbl or args.ZBL | |||
|     local Quote = args.quote or args.quotation | |||
|     local PostScript = args.postscript or "." |     local PostScript = args.postscript or "." | ||
|     local LaySummary = args.laysummary |     local LaySummary = args.laysummary | ||
| Line 588: | Line 719: | ||
|             args.notracking or args["no-tracking"] or ""; |             args.notracking or args["no-tracking"] or ""; | ||
|     if ( config.CitationClass == "journal" ) then |     if ( config.CitationClass == "journal" ) then         | ||
|         if (URL == nil or URL == "") then |         if (URL == nil or URL == "") then | ||
|           if (PMC ~= nil  |             if (ID_list['PMC'] ~= nil) then  | ||
|                 local Embargo = args.embargo or args.Embargo; | |||
|             then URL="http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. PMC | |||
|                 if Embargo ~= nil then | |||
|                     local lang = mw.getContentLanguage(); | |||
|           end | |||
|                     local good1, result1, good2, result2; | |||
|                     good1, result1 = pcall( lang.formatDate, lang, 'U', Embargo ); | |||
|                     good2, result2 = pcall( lang.formatDate, lang, 'U' ); | |||
|                     if good1 and good2 and tonumber( result1 ) < tonumber( result2 ) then  | |||
|                         URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC']; | |||
|                     end | |||
|                 else | |||
|                     URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC'];            | |||
|                 end | |||
|             end | |||
|         end |         end | ||
|     end |     end | ||
| Line 617: | Line 759: | ||
|         local Station = args.station |         local Station = args.station | ||
|         local s = {} |         local s = {} | ||
|         if Issue ~= nil then table.insert(s, "episode " .. Issue) Issue = nil end |         if Issue ~= nil then table.insert(s, cfg.message_list["episode"] .. " " .. Issue) Issue = nil end | ||
|         if Season ~= nil then table.insert(s, "season " .. Season) end |         if Season ~= nil then table.insert(s, cfg.message_list["season"] .. " " .. Season) end | ||
|         if SeriesNumber ~= nil then table.insert(s, "series " .. SeriesNumber) end |         if SeriesNumber ~= nil then table.insert(s, cfg.message_list["series"] .. " " .. SeriesNumber) end | ||
|         local n = {} |         local n = {} | ||
|         if Network ~= nil then table.insert(n, Network) end |         if Network ~= nil then table.insert(n, Network) end | ||
| Line 663: | Line 805: | ||
|     OCinSdata["rft.edition"] = Edition |     OCinSdata["rft.edition"] = Edition | ||
|     OCinSdata["rft.pub"] = PublisherName |     OCinSdata["rft.pub"] = PublisherName | ||
|     OCinSdata["rft.isbn"] = ISBN | |||
|     for k, v in pairs( ID_list ) do | |||
|     OCinSdata["rft.issn"] = ISSN | |||
|         if string.sub( cfg.id_handlers[k].COinS or "info", 1, 4 ) ~= 'info' then | |||
|     OCinSdata["rft.jfm"] = JFM | |||
|     OCinSdata[ |             OCinSdata[ cfg.id_handlers[k].COinS ] = v; | ||
|         end | |||
|     OCinSdata["rft.lccn"] = LCCN | |||
|     end | |||
|     OCinSdata["rft.mr"] = MR | |||
|     OCinSdata.rft_id = URL or ChapterURL |     OCinSdata.rft_id = URL or ChapterURL | ||
| Line 692: | Line 835: | ||
|     local OCinSids = {} -- COinS data only for id, bibcode, doi, pmid, etc. |     local OCinSids = {} -- COinS data only for id, bibcode, doi, pmid, etc. | ||
|     for k, v in pairs( ID_list ) do | |||
|     OCinSids["info:arxiv"] = ARXIV | |||
|         if string.sub( cfg.id_handlers[k].COinS or "", 1, 4 ) == 'info' then | |||
|     OCinSids["info:asin"] = ASIN | |||
|     OCinSids[ |             OCinSids[ cfg.id_handlers[k].COinS ] = v; | ||
|         end | |||
|     OCinSids["info:doi"] = DOI | |||
|     end | |||
|     OCinSids["info:oclcnum"] = OCLC | |||
|     OCinSids["info:olnum"] = OL | |||
|     OCinSids["info:osti"] = OSTI | |||
|     OCinSids["info:pmc"] = PMC | |||
|     OCinSids["info:pmid"] = PMID | |||
|     OCinSids["info:rfc"] = RFC | |||
|     OCinSids["info:ssrn"] = SSRN | |||
|     OCinSids["info:zbl"] = ZBL | |||
|     local OCinStitle = "ctx_ver=" .. ctx_ver  -- such as "Z39.88-2004" |     local OCinStitle = "ctx_ver=" .. ctx_ver  -- such as "Z39.88-2004" | ||
|     for name,value in pairs(OCinSdata) do |     for name,value in pairs(OCinSdata) do | ||
| Line 716: | Line 853: | ||
|     local this_page = mw.title.getCurrentTitle(); |     local this_page = mw.title.getCurrentTitle(); | ||
|     OCinStitle = OCinStitle .. "&rfr_id=info:sid/ |     OCinStitle = OCinStitle .. "&rfr_id=info:sid/" .. mw.site.server:match( "[^/]*$" ) .. ":" | ||
|        .. this_page.prefixedText  -- end COinS data by page's non-encoded pagename |        .. this_page.prefixedText  -- end COinS data by page's non-encoded pagename | ||
| Line 739: | Line 876: | ||
|         if Maximum == nil and #a == 9 then  |         if Maximum == nil and #a == 9 then  | ||
|             Maximum = 8; |             Maximum = 8; | ||
|             table.insert( z. |             table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } ); | ||
|             table.insert( z.message_tail, 'Citation uses old-style implicit et al. for authors' ); | |||
|         elseif Maximum == nil then |         elseif Maximum == nil then | ||
|             Maximum = #a + 1; |             Maximum = #a + 1; | ||
| Line 768: | Line 904: | ||
|         if Maximum == nil and #e == 4 then  |         if Maximum == nil and #e == 4 then  | ||
|             Maximum = 3; |             Maximum = 3; | ||
|             table.insert( z. |             table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } ); | ||
|             table.insert( z.message_tail, 'Citation uses old-style implicit et al. for editors' ); | |||
|         elseif Maximum == nil then |         elseif Maximum == nil then | ||
|             Maximum = #e + 1; |             Maximum = #e + 1; | ||
| Line 834: | Line 969: | ||
|         -- 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. |             table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } ); | ||
|             table.insert( z.message_tail, 'No URL on cite web' ); | |||
|         end |         end | ||
|         -- Test if accessdate is given without giving a URL |         -- Test if accessdate is given without giving a URL | ||
|         if ( AccessDate ~= nil and AccessDate ~= '' ) then |         if ( AccessDate ~= nil and AccessDate ~= '' ) then | ||
|             table.insert( z. |             table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } ); | ||
|             table.insert( z.message_tail, 'Accessdate used without URL' ); | |||
|             AccessDate = nil; |             AccessDate = nil; | ||
|         end       |         end       | ||
| Line 847: | Line 980: | ||
|         -- Test if format is given without giving a URL |         -- Test if format is given without giving a URL | ||
|         if ( Format ~= nil and Format ~= '' ) then |         if ( Format ~= nil and Format ~= '' ) then | ||
|             Format = Format .. seterror( 'format_missing_url' ); | |||
|             Format = Format .. hiddencomment( "File format specified without giving a URL" ); | |||
|         end         |         end         | ||
|     end     |     end     | ||
| Line 859: | Line 991: | ||
|             ( TransTitle == nil or TransTitle == "" ) and |             ( TransTitle == nil or TransTitle == "" ) and | ||
|             ( TransChapter == nil or TransChapter == "" ) then |             ( TransChapter == nil or TransChapter == "" ) then | ||
|         table.insert( z. |         table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } ); | ||
|         table.insert( z.message_tail, 'Citation has no title' ); | |||
|     end |     end | ||
| Line 890: | Line 1,021: | ||
|     end |     end | ||
|     if TransChapter ~= "" and Chapter == "" then |     if TransChapter ~= "" and Chapter == "" then | ||
|         TransChapter = TransChapter .. seterror( 'trans_missing_chapter' ); | |||
|         table.insert( z.error_categories, 'Pages with citations using translated terms without the original' ); | |||
|         TransChapter = TransChapter .. hiddencomment( "Translated title included without the original" ); | |||
|     end |     end | ||
|     Chapter = Chapter .. TransChapter |     Chapter = Chapter .. TransChapter | ||
| Line 907: | Line 1,037: | ||
|                 Format = "" |                 Format = "" | ||
|             end |             end | ||
|         elseif ChapterURL ~= nil and ChapterURL ~= "" then | |||
|             Chapter = Chapter .. " [" .. ChapterURL .. " " .. safeforurl( ChapterURL ) .. "]" ..  | |||
|                 seterror( 'bare_url_missing_title' ); | |||
|         end |         end | ||
|         Chapter = Chapter .. sepc .. " " -- with end-space |         Chapter = Chapter .. sepc .. " " -- with end-space | ||
|     elseif ChapterURL ~= nil and ChapterURL ~= "" then | |||
|     end | |||
|         Chapter = " [" .. ChapterURL .. " " .. safeforurl( ChapterURL ) .. "]" ..  | |||
|             seterror( 'bare_url_missing_title' ) .. sepc .. " "; | |||
|     end         | |||
|     -- Format main title. |     -- Format main title. | ||
| Line 929: | Line 1,065: | ||
|     end     |     end     | ||
|     if TransTitle ~= "" and Title == "" then |     if TransTitle ~= "" and Title == "" then | ||
|         TransTitle = TransTitle .. seterror( 'trans_missing_title' ); | |||
|         table.insert( z.error_categories, 'Pages with citations using translated terms without the original' ); | |||
|         TransTitle = TransTitle .. hiddencomment( "Translated title included without the original" ); | |||
|     end |     end | ||
|     Title = Title .. TransTitle |     Title = Title .. TransTitle | ||
| Line 943: | Line 1,078: | ||
|     if ( Place ~= nil and Place ~= "" ) then |     if ( Place ~= nil and Place ~= "" ) then | ||
|         if sepc == '.' then |         if sepc == '.' then | ||
|             Place = "  |             Place = " " .. cfg.message_list['written'] .. " " .. Place .. sepc .. " "; | ||
|         else |         else | ||
|             Place = " written  |             Place = " " .. cfg.message_list['written']:lower() .. " " .. Place .. sepc .. " "; | ||
|         end             |         end             | ||
|     else |     else | ||
| Line 956: | Line 1,091: | ||
|         end |         end | ||
|         Conference = " " .. Conference |         Conference = " " .. Conference | ||
|     elseif ConferenceURL ~= nil and ConferenceURL ~= "" then | |||
|     else  | |||
|         Conference = " [" .. ConferenceURL .. " " .. safeforurl( ConferenceURL ) .. "]" ..  | |||
|             seterror( 'bare_url_missing_title' ); | |||
|     else | |||
|         Conference = ""  |         Conference = ""  | ||
|     end |     end | ||
| Line 963: | Line 1,101: | ||
|         local Minutes = args.minutes |         local Minutes = args.minutes | ||
|         if ( nil ~= Minutes ) then |         if ( nil ~= Minutes ) then | ||
|             Position = " " .. Minutes .. "  |             Position = " " .. Minutes .. " " .. cfg.message_list['minutes']; | ||
|         else |         else | ||
|             local Time = args.time |             local Time = args.time | ||
|             if ( nil ~= Time ) then |             if ( nil ~= Time ) then | ||
|                 local TimeCaption = args.timecaption  |                 local TimeCaption = args.timecaption  | ||
|                 if TimeCaption == nil then | |||
|                     TimeCaption = cfg.message_list['event']; | |||
|                     if sepc ~= '.' then | |||
|                         TimeCaption = TimeCaption:lower(); | |||
|                     end | |||
|                 end                 | |||
|                 Position = " " .. TimeCaption .. " " .. Time |                 Position = " " .. TimeCaption .. " " .. Time | ||
|             else |             else | ||
| Line 1,014: | Line 1,158: | ||
|         TitleNote = sepc .. " " .. TitleNote else TitleNote = "" end |         TitleNote = sepc .. " " .. TitleNote else TitleNote = "" end | ||
|     if ( Language ~= nil and Language ~="" ) then |     if ( Language ~= nil and Language ~="" ) then | ||
|         Language = " (in " .. Language .. ")" else Language = "" end |         Language = " (" .. cfg.message_list['in'] .. " " .. Language .. ")" else Language = "" end | ||
|     if ( Edition ~= nil and Edition ~="" ) then |     if ( Edition ~= nil and Edition ~="" ) then | ||
|         Edition = " (" .. Edition .. "  |         Edition = " (" .. Edition .. " " .. cfg.message_list['edition'] .. ")" else Edition = "" end | ||
|     if ( Volume ~= nil and Volume ~="" ) |     if ( Volume ~= nil and Volume ~="" ) | ||
|     then |     then | ||
| Line 1,035: | Line 1,179: | ||
|     if ( Date ~= nil ) then Date = Date else Date = "" end |     if ( Date ~= nil ) then Date = Date else Date = "" end | ||
|     if ( Via ~= nil and Via ~="" ) then |     if ( Via ~= nil and Via ~="" ) then | ||
|         Via = " — via " .. Via else Via = "" end |         Via = " — " .. cfg.message_list['via'] .. " " .. Via else Via = "" end | ||
|     if ( AccessDate ~= nil and AccessDate ~="" ) |     if ( AccessDate ~= nil and AccessDate ~="" ) | ||
|     then local retrv_text = " retrieved " |     then local retrv_text = " " .. cfg.message_list['retrieved'] .. " " | ||
|          if (sepc  |          if (sepc ~= ".") then retrv_text = retrv_text:lower() end | ||
|          AccessDate = '<span class="reference-accessdate">' .. sepc |          AccessDate = '<span class="reference-accessdate">' .. sepc | ||
|              .. retrv_text .. AccessDate .. '</span>' |              .. retrv_text .. AccessDate .. '</span>' | ||
| Line 1,044: | Line 1,188: | ||
|     if ( SubscriptionRequired ~= nil and |     if ( SubscriptionRequired ~= nil and | ||
|          SubscriptionRequired ~= "" ) then |          SubscriptionRequired ~= "" ) then | ||
|         SubscriptionRequired = sepc .. " " ..  |         SubscriptionRequired = sepc .. " " .. cfg.message_list['subscription']; | ||
|             params={style="font-size:0.95em; font-size: 90%; color: #555"}}) | |||
|     else |     else | ||
|         SubscriptionRequired = "" |         SubscriptionRequired = "" | ||
|     end |     end | ||
|     if ( ARXIV ~= nil and ARXIV ~= "" ) then | |||
|         ARXIV = sepc .. " " .. externallinkid({label="arXiv",link="arXiv", | |||
|             prefix="http://arxiv.org/abs/",id=ARXIV,separator=":",encode=false}) else ARXIV = "" end | |||
|     if ( ASIN ~= nil and ASIN ~= "" ) then  | |||
|         ASIN = sepc .. " " .. amazon(ASIN, ASINTLD) else ASIN = "" end | |||
|     if ( BIBCODE ~= nil and BIBCODE ~= "" ) then | |||
|         BIBCODE = sepc .. " " .. externallinkid({label="Bibcode",link="Bibcode", | |||
|             prefix="http://adsabs.harvard.edu/abs/",id=BIBCODE,separator=":"}) else BIBCODE = "" end | |||
|     if ( DOI ~= nil and DOI ~= "" ) then | |||
|         DOI = sepc .. " " .. doi(DOI, DoiBroken) else DOI = "" end | |||
|     if ( ID ~= nil and ID ~="") then ID = sepc .." ".. ID else ID="" end |     if ( ID ~= nil and ID ~="") then ID = sepc .." ".. ID else ID="" end | ||
|     if ( ISBN ~= nil and ISBN ~= "") then | |||
|     ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD} ); | |||
|         ISBN = sepc .. " " .. internallinkid({label="ISBN",link="International Standard Book Number", | |||
|             prefix="Special:BookSources/",id=ISBN}) else ISBN = "" end | |||
|     if ( ISSN ~= nil and ISSN ~="" ) then | |||
|         ISSN = sepc .. " " .. externallinkid({label="ISSN",link="International Standard Serial Number", | |||
|             prefix="//www.worldcat.org/issn/",id=ISSN,encode=false}) else ISSN = "" end | |||
|     if ( JFM ~= nil and JFM ~="" ) then | |||
|         JFM = sepc .." " .. externallinkid({label="JFM",link="Jahrbuch über die Fortschritte der Mathematik", | |||
|             prefix="http://www.zentralblatt-math.org/zmath/en/search/?format=complete&q=an:",id=JFM}) else JFM = "" end | |||
|     if ( JSTOR ~= nil and JSTOR ~="") then | |||
|         JSTOR = sepc .." " .. externallinkid({label="JSTOR",link="JSTOR", | |||
|             prefix="http://www.jstor.org/stable/",id=JSTOR}) else JSTOR = "" end | |||
|     if ( LCCN ~= nil and LCCN ~="" ) then | |||
|         LCCN = sepc .." " .. externallinkid({label="LCCN",link="Library of Congress Control Number", | |||
|             prefix="http://lccn.loc.gov/",id=LCCN,encode=false}) else LCCN = "" end | |||
|     if ( MR ~= nil and MR ~="" ) then | |||
|         MR = sepc .." " .. externallinkid({label="MR",link="Mathematical Reviews", | |||
|             prefix="http://www.ams.org/mathscinet-getitem?mr=",id=MR}) else MR = "" end | |||
|     if ( OCLC ~= nil and OCLC ~="") then  | |||
|         OCLC = sepc .." " .. externallinkid({label="OCLC",link="OCLC", | |||
|             prefix="//www.worldcat.org/oclc/",id=OCLC}) else OCLC = "" end | |||
|     if ( OL ~= nil and OL ~="") then | |||
|         OL = sepc .. " " .. openlibrary(OL) else OL = "" end     | |||
|     if ( OSTI ~= nil and OSTI ~="") then | |||
|         OSTI = sepc .." " .. externallinkid({label="OSTI",link="Office of Scientific and Technical Information", | |||
|             prefix="http://www.osti.gov/energycitations/product.biblio.jsp?osti_id=",id=OSTI}) else OSTI = "" end | |||
|     if ( PMC ~= nil and PMC ~="") then | |||
|         PMC = sepc .." " .. externallinkid({label="PMC",link="PubMed Central" | |||
|             ,prefix="//www.ncbi.nlm.nih.gov/pmc/articles/PMC",suffix=" ",id=PMC}) else PMC = "" end | |||
|     if ( PMID ~= nil and PMID ~="") then | |||
|         PMID = sepc .." " .. externallinkid({label="PMID",link="PubMed Identifier", | |||
|             prefix="//www.ncbi.nlm.nih.gov/pubmed/",id=PMID,encode=false}) else PMID = "" end | |||
|     if ( RFC ~= nil and RFC ~="") then | |||
|         RFC = sepc .." " .. externallinkid({label="RFC",link="Request for Comments", | |||
|             prefix="//tools.ietf.org/html/rfc",id=RFC,encode=false}) else RFC = "" end | |||
|     if ( SSRN ~= nil and SSRN ~="") then | |||
|         SSRN = sepc .." " .. externallinkid({label="SSRN",link="Social Science Research Network", | |||
|             prefix="http://ssrn.com/abstract=",id=SSRN}) else SSRN = "" end | |||
|     if ( ZBL ~= nil and ZBL ~="") then | |||
|         ZBL = sepc .." " .. externallinkid({label="Zbl",link="Zentralblatt MATH", | |||
|             prefix="http://www.zentralblatt-math.org/zmath/en/search/?format=complete&q=an:",id=ZBL}) else ZBL = "" end | |||
|     if ( URL ~= nil and URL ~="") then |     if ( URL ~= nil and URL ~="") then | ||
|         URL = " " .. "[" .. URL .. " " .. nowiki(URL) .. "]"; |         URL = " " .. "[" .. URL .. " " .. mw.text.nowiki(URL) .. "]"; | ||
|         local error_text = seterror( 'bare_url_missing_title' ); | |||
|         table.insert( z.error_categories, "Pages with citations having bare URLs" ); | |||
|         if config.CitationClass == "web" then |         if config.CitationClass == "web" then | ||
|             URL = URL .. " " .. seterror( 'cite_web_title' ); | |||
|             URL = URL .. " <span class='error'>No <code>title=</code> specified</span>" | |||
|         else |         else | ||
|             URL = URL ..  |             URL = URL .. error_text; | ||
|         end        |         end        | ||
|     else |     else | ||
| Line 1,132: | Line 1,225: | ||
|             ArchiveDate = " " .. ArchiveDate |             ArchiveDate = " " .. ArchiveDate | ||
|         else  |         else  | ||
|             ArchiveDate = "  |             ArchiveDate = " " .. seterror('archive_missing_date') .. " " | ||
|                 "you must also specify <code>archivedate=</code></span> " | |||
|             table.insert( z.error_categories, 'Pages with archiveurl citation errors' ); | |||
|         end |         end | ||
|         local arch_text = "  |         local arch_text = " " .. cfg.message_list['archived']; | ||
|         if (sepc  |         if (sepc ~= ".") then arch_text = arch_text:lower() end | ||
|         if ( "no" == DeadURL ) then |         if ( "no" == DeadURL ) then | ||
|             Archived = sepc .. " [" .. ArchiveURL .. arch_text .. "]  |             Archived = sepc .. " [" .. ArchiveURL .. arch_text .. "] " ..  | ||
|                 cfg.message_list['from'] .. " " .. cfg.message_list['original'] .. " " ..  | |||
|                 cfg.message_list['on'] .. ArchiveDate | |||
|             if OriginalURL == nil or OriginalUrl == '' then |             if OriginalURL == nil or OriginalUrl == '' then | ||
|                 Archived = Archived .. " " .. seterror('archive_missing_url_not_dead');                                | |||
|                 table.insert( z.error_categories, 'Pages with archiveurl citation errors' ); | |||
|                 Archived = Archived .. " <span class='error'>If you specify <code>archiveurl=</code>" .. | |||
|                     " and <code>deadurl=no</code>, then you must also specify <code>url=</code></span>";                                | |||
|             end |             end | ||
|         else |         else | ||
|             if OriginalURL ~= nil and OriginalURL ~= '' then |             if OriginalURL ~= nil and OriginalURL ~= '' then | ||
|                 Archived = sepc .. arch_text .. " from [" .. OriginalURL .. "  |                 Archived = sepc .. arch_text .. " " .. cfg.message_list['from'] .. " [" .. OriginalURL .. " " ..  | ||
|                     cfg.message_list['original'] .. "] " .. cfg.message_list['on'] .. ArchiveDate | |||
|             else |             else | ||
|                 if config.CitationClass ~= 'web' then  |                 if config.CitationClass ~= 'web' then  | ||
|                     Archived = sepc .. arch_text .. "  |                     Archived = sepc .. arch_text .. " " .. cfg.message_list['from'] .. " " ..  | ||
|                     seterror('archive_missing_url') .. " " .. cfg.message_list['on'] .. ArchiveDate | |||
|                     table.insert( z.error_categories, 'Pages with archiveurl citation errors' ); | |||
|                 else |                 else | ||
|                     Archived = sepc .. arch_text .. "  |                     Archived = sepc .. arch_text .. " " .. cfg.message_list['from'] ..  | ||
|                         " " .. cfg.message_list['original'] .. " " ..  | |||
|                         cfg.message_list['on'] .. ArchiveDate | |||
|                 end |                 end | ||
|             end                 |             end                 | ||
| Line 1,170: | Line 1,263: | ||
|         end |         end | ||
|         if sepc == '.' then |         if sepc == '.' then | ||
|             Lay = sepc .. " [" .. LaySummary .. "  |             Lay = sepc .. " [" .. LaySummary .. " " .. cfg.message_list['lay summary'] .. "]" .. LaySource .. LayDate | ||
|         else |         else | ||
|             Lay = sepc .. " [" .. LaySummary .. " lay summary]" .. LaySource .. LayDate |             Lay = sepc .. " [" .. LaySummary .. " " .. cfg.message_list['lay summary']:lower() .. "]" .. LaySource .. LayDate | ||
|         end             |         end             | ||
|     else |     else | ||
| Line 1,179: | Line 1,272: | ||
|     if ( nil ~= Transcript and "" ~= Transcript ) then |     if ( nil ~= Transcript and "" ~= Transcript ) then | ||
|         if ( TranscriptURL ~= nil ) then Transcript = "[" .. TranscriptURL .. Transcript .. "]" end |         if ( TranscriptURL ~= nil ) then Transcript = "[" .. TranscriptURL .. Transcript .. "]" end | ||
|     elseif TranscriptURL ~= nil and TranscriptURL ~= "" then | |||
|         Transcript = "[" .. TranscriptURL .. " " .. safeforurl( TranscriptURL ) .. "]" ..  | |||
|             seterror( 'bare_url_missing_title' )         | |||
|     else |     else | ||
|         Transcript = "" |         Transcript = "" | ||
| Line 1,200: | Line 1,296: | ||
|         if ( PublicationDate and PublicationDate ~="" ) then |         if ( PublicationDate and PublicationDate ~="" ) then | ||
|             if Publisher ~= '' then |             if Publisher ~= '' then | ||
|                 Publisher = Publisher .. ", published " .. PublicationDate; |                 Publisher = Publisher .. ", " .. cfg.message_list['published'] .. " " .. PublicationDate; | ||
|             else |             else | ||
|                 Publisher = PublicationDate; |                 Publisher = PublicationDate; | ||
| Line 1,210: | Line 1,306: | ||
|     else |     else | ||
|         if ( PublicationDate and PublicationDate ~="" ) then |         if ( PublicationDate and PublicationDate ~="" ) then | ||
|             PublicationDate = " (published " .. PublicationDate .. ")" |             PublicationDate = " (" .. cfg.message_list['published'] .. " " .. PublicationDate .. ")" | ||
|         else  |         else  | ||
|             PublicationDate = "" |             PublicationDate = "" | ||
| Line 1,250: | Line 1,346: | ||
|     end |     end | ||
|     if #ID_list > 0 then | |||
|     local idcommon = safejoin( { ARXIV, ASIN, BIBCODE, DOI, ISBN, ISSN, JFM, JSTOR, LCCN, MR, OCLC, OL, OSTI,  | |||
|         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 text | ||
|     local pgtext = Page .. Pages .. At |     local pgtext = Page .. Pages .. At | ||
|     if page_error then | |||
|         table.insert( z.error_categories, 'Pages with citations using conflicting page specifications' ); | |||
|         pgtext = pgtext .. hiddencomment('Bad page specification here'); | |||
|     end | |||
|     if ( "" ~= Authors ) then |     if ( "" ~= Authors ) then | ||
| Line 1,285: | Line 1,381: | ||
|         if ( "" ~= Date ) then |         if ( "" ~= Date ) then | ||
|             if EditorCount <= 1 then |             if EditorCount <= 1 then | ||
|                 Editors = Editors .. ",  |                 Editors = Editors .. ", " .. cfg.message_list['editor']; | ||
|             else |             else | ||
|                 Editors = Editors .. ",  |                 Editors = Editors .. ", " .. cfg.message_list['editors']; | ||
|             end |             end | ||
|             Date = " (" .. Date ..")" .. OrigYear .. sepc .. " " |             Date = " (" .. Date ..")" .. OrigYear .. sepc .. " " | ||
|         else |         else | ||
|             if EditorCount <= 1 then |             if EditorCount <= 1 then | ||
|                 Editors = Editors .. " ( |                 Editors = Editors .. " (" .. cfg.message_list['editor'] .. ")" .. sepc .. " " | ||
|             else |             else | ||
|                 Editors = Editors .. " ( |                 Editors = Editors .. " (" .. cfg.message_list['editors'] .. ")" .. sepc .. " " | ||
|             end |             end | ||
|         end |         end | ||
| Line 1,335: | Line 1,431: | ||
|     if ( config.CitationClass ~= "citation" ) |     if ( config.CitationClass ~= "citation" ) | ||
|        then classname = "citation " .. (config.CitationClass or "") end |        then classname = "citation " .. (config.CitationClass or "") end | ||
|     local  |     local options = { class=classname } | ||
|     if ( Ref ~= nil ) then  |     if ( Ref ~= nil ) then  | ||
|         local id = Ref |         local id = Ref | ||
| Line 1,358: | Line 1,454: | ||
|             id = anchorid(names) |             id = anchorid(names) | ||
|         end |         end | ||
|         options.id = id; | |||
|     end |     end | ||
|     if string.len(text:gsub("<span[^>/]*>.-</span>", ""):gsub("%b<>","")) <= 2 then |     if string.len(text:gsub("<span[^>/]*>.-</span>", ""):gsub("%b<>","")) <= 2 then | ||
|         z.error_categories = { |         z.error_categories = {}; | ||
|         text = ' |         text = seterror('empty_citation'); | ||
|         z.message_tail = {}; |         z.message_tail = {}; | ||
|     end |     end | ||
|     if options.id ~= nil then  | |||
|     text = createTag({name="span", contents=text, params=args}) | |||
|         text = '<span id="' .. wikiescape(options.id) ..'" class="' .. wikiescape(options.class) .. '">' .. text .. "</span>"; | |||
|     else | |||
|         text = '<span class="' .. wikiescape(options.class) .. '">' .. text .. "</span>"; | |||
|     end         | |||
|     local empty_span =  |     local empty_span = '<span style="display: none;"> </span>'; | ||
|     -- Note: Using display: none on then COinS span breaks some clients. |     -- Note: Using display: none on then COinS span breaks some clients. | ||
|     local OCinS =  |     local OCinS = '<span title="' .. wikiescape(OCinStitle) .. '" class="Z3988">' .. empty_span .. '</span>'; | ||
|     text = text .. OCinS; |     text = text .. OCinS; | ||
|     if #z.message_tail ~= 0 then |     if #z.message_tail ~= 0 then | ||
|         for i,v in ipairs( z.message_tail ) do | |||
|             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 | ||
| Line 1,393: | Line 1,499: | ||
|     local args = {}; |     local args = {}; | ||
|     local suggestions = {}; | |||
|     local error_text, error_state; | |||
|     for k, v in pairs( pframe.args ) do |     for k, v in pairs( pframe.args ) do | ||
|         if v ~= '' then |         if v ~= '' then | ||
|             if not validate( k ) then |             if not validate( k ) then             | ||
|                 if type( k ) ~= 'string' then |                 if type( k ) ~= 'string' then | ||
|                     -- Exclude empty numbered parameters |                     -- Exclude empty numbered parameters | ||
|                     if v:match("%S+") ~= nil then |                     if v:match("%S+") ~= nil then | ||
|                         error_text, error_state = seterror( 'text_ignored', {v}, true ); | |||
|                         table.insert( z.error_categories, 'Pages with citations using unsupported parameters' ); | |||
|                     end |                     end | ||
|                 elseif validate( k:lower() ) then  |                 elseif validate( k:lower() ) then  | ||
|                     error_text, error_state = seterror( 'parameter_ignored_suggest', {k, k:lower()}, true ); | |||
|                     table.insert( z.error_categories, 'Pages with citations using unsupported parameters' ); | |||
|                 else |                 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             |             end             | ||
|             args[k] = v; |             args[k] = v; | ||
Revision as of 20:51, 9 April 2013
<section begin=header />
|  | This Lua module is used on approximately 6,110,000 pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. | 
|  | This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. | 
|  | This module can only be edited by administrators because it is transcluded onto one or more cascade-protected pages. | 
Lua error: bad argument #1 to "get" (not a valid title).<section end=header /> This module and associated sub-modules support the Citation Style 1 and Citation Style 2 citation templates. In general, it is not intended to be called directly, but is called by one of the core CS1 and CS2 templates. <section begin=module_components_table /> These files comprise the module support for CS1|2 citation templates:
<section end=module_components_table />
Other documentation:
- Module talk:Citation/CS1/Feature requests
- Module talk:Citation/CS1/COinS
- Module:Cs1 documentation support – a set of functions (some experimental) that extract information from the module suite for the purpose of documenting CS1|2
- Module:Citation/CS1/doc/Category list – lists of category names taken directly from Module:Citation/CS1/Configuration and Module:Citation/CS1/Configuration/sandbox
 
testcases
- Module:Citation/CS1/testcases (run)
- Module:Citation/CS1/testcases/errors (run) – error and maintenance messaging
- Module:Citation/CS1/testcases/dates (run) – date validation
- Module:Citation/CS1/testcases/identifiers (run) – identifiers
- Module:Citation/CS1/testcases/anchor (run) – CITEREF anchors
local z = {
    error_categories = {};
    message_tail = {};
}
-- Include translation message hooks, ID and error handling configuration settings.
local cfg = mw.loadData( 'Module:Citation/CS1/Configuration' );
 
-- Contains a list of all recognized parameters
local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' );
-- Checks that parameter name is valid
function validate( name )
    name = tostring( name );
    
    -- Normal arguments
    if whitelist.basic_arguments[ name ] then
        return true;
    end
    
    -- Arguments with numbers in them
    name = name:gsub( "%d+", "#" );
    if whitelist.numbered_arguments[ name ] then
        return true;
    end
    
    -- Not found, argument not supported.
    return false
end
-- Formats a comment for error trapping
function errorcomment( content, hidden )
    if hidden then 
        return '<span style="display:none;font-size:100%" class="error citation-comment">' .. content .. '</span>';
    else
        return '<span style="font-size:100%" class="error">' .. content .. '</span>';
    end        
end
--[[
Sets an error condition and returns the appropriate error message.  The actual placement
of the error message in the output is the responsibility of the calling function.
]]
function seterror( error_id, args, raw )
    local error_state = cfg.error_conditions[ error_id ];
    if error_state == nil then
        error( cfg.message_list['undefined_error'] );
    end
    
    if error_state.category ~= nil and error_state.category ~= "" then
        table.insert( z.error_categories, error_state.category );
    end
    
    local message = error_state.message;
    if args ~= nil then
        for k, m in ipairs( args ) do
            message = message:gsub( "$" .. k .. "(%D)", m .. "%1" );
        end
    end
    message = wikiescape(message) .. " ([[" .. cfg.message_list['help page link'] .. 
        "#" .. error_state.anchor .. "|" ..
        cfg.message_list['help page label'] .. "]])";
    if raw == true then
        return message, error_state.hidden;
    end
        
    return errorcomment( message, error_state.hidden );
end
-- This returns a string with HTML character entities for wikitext markup characters.
function wikiescape(text)
    text = text:gsub( '[&\'%[%]{|}]', {    
            ['&'] = '&',    
            ["'"] = ''',    
            ['['] = '[',    
            [']'] = ']',    
            ['{'] = '{',    
            ['|'] = '|',	
            ['}'] = '}' } );
    return text;
end
-- Formats a wiki style external link
function externallinkid(options)
    local sep = options.separator or " "
    options.suffix = options.suffix or ""
    local url_string = options.id
    if options.encode == true or options.encode == nil then
        url_string = mw.uri.encode( url_string );
    end
    
    return "[[" .. options.link .. "|" .. options.label .. "]]" .. sep .. "[" .. 
            options.prefix .. url_string .. options.suffix .. " " .. mw.text.nowiki(options.id) .. "]"
end
-- Formats a wiki style internal link
function internallinkid(options)
    local sep = options.separator or " "
    options.suffix = options.suffix or ""
    return "[[" .. options.link .. "|" .. options.label .. "]]" .. sep .. "[[" .. 
            options.prefix .. options.id .. options.suffix .. "|" .. mw.text.nowiki(options.id) .. "]]"
end
-- Formats a link to Amazon
function amazon(id, domain)
    if ( nil == domain ) then 
        domain = "com"
    elseif ( "jp" == domain or "uk" == domain ) then
        domain = "co." .. domain
    end
    local handler = cfg.id_handlers['ASIN'];
    return externallinkid({link = handler.link,
        label=handler.label , prefix="//www.amazon."..domain.."/dp/",id=id,
        encode=handler.encode, separator = handler.separator})
end
-- Formats a DOI and checks for DOI errors.
function doi(id, inactive)
    local cat = ""
    local handler = cfg.id_handlers['DOI'];
    
    local text;
    if ( inactive ~= nil ) then 
        text = "[[" .. handler.link .. "|" .. handler.label .. "]]:" .. id;
        table.insert( z.error_categories, "Pages with DOIs inactive since " .. selectyear(inactive) );        
        inactive = " (" .. cfg.message_list['inactive'] .. " " .. inactive .. ")" 
    else 
        text = externallinkid({link = handler.link, label = handler.label,
            prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
        inactive = "" 
    end
    if ( string.sub(id,1,3) ~= "10." ) then      
        cat = seterror( 'bad_doi' );
    end
    return text .. inactive .. cat 
end
-- Formats an OpenLibrary link, and checks for associated errors.
function openlibrary(id)
    local code = id:sub(-1,-1)
    local handler = cfg.id_handlers['OL'];
    if ( code == "A" ) then
        return externallinkid({link=handler.link, label=handler.label,
            prefix="http://openlibrary.org/authors/OL",id=id, separator=handler.separator,
            encode = handler.encode})
    elseif ( code == "M" ) then
        return externallinkid({link=handler.link, label=handler.label,
            prefix="http://openlibrary.org/books/OL",id=id, separator=handler.separator,
            encode = handler.encode})
    elseif ( code == "W" ) then
        return externallinkid({link=handler.link, label=handler.label,
            prefix= "http://openlibrary.org/works/OL",id=id, separator=handler.separator,
            encode = handler.encode})
    else
        return externallinkid({link=handler.link, label=handler.label,
            prefix= "http://openlibrary.org/OL",id=id, separator=handler.separator,
            encode = handler.encode}) .. 
            ' ' .. seterror( 'bad_ol' );
    end
end
-- Determines whether an ISBN string is valid
function checkisbn( isbn_str )
    isbn_str = isbn_str:gsub("[- ]", ""):upper();
    local len = isbn_str:len();
 
    if len ~= 10 and len ~= 13 then
        return false;
    end
 
    local temp = 0;
    if len == 10 then
        if isbn_str:match( "^%d*X?$" ) == nil then return false; end
        isbn_str = { isbn_str:byte(1, len) };
        for i, v in ipairs( isbn_str ) do
            if v == string.byte( "X" ) then
                temp = temp + 10*( 11 - i );
            else
                temp = temp + tonumber( string.char(v) )*(11-i);
            end
        end
        return temp % 11 == 0;
    else
        if isbn_str:match( "^%d*$" ) == nil then return false; end
        isbn_str = { isbn_str:byte(1, len) };
        for i, v in ipairs( isbn_str ) do
            temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) );
        end
        return temp % 10 == 0;
    end
end
-- 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 str == nil then
        return nil;
    end    
    if str:match( "[%[%]{}<>]" ) ~= nil then 
        return str;
    end    
    return str:gsub( '-', '–' );
end
-- Protects a string that will be wrapped in wiki italic markup '' ... ''
function safeforitalics( str )
    --[[ Note: We can not use <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 str == nil or 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
        return str;
    end
end
--[[
Joins a sequence of strings together while checking for duplicate separation
characters.
]]
function safejoin( tbl, duplicate_char )
    --[[
    Note: we use string functions here, rather than ustring functions.
    
    This has considerably faster performance and should work correctly as 
    long as the duplicate_char is strict ASCII.  The strings
    in tbl may be ASCII or UTF8.
    ]]
    
    local str = '';
    local comp = '';
    local end_chr = '';
    local trim;
    for _, value in ipairs( tbl ) do
        if value == nil then value = ''; end
        
        if str == '' then
            str = value;
        elseif value ~= '' then
            if value:sub(1,1) == '<' then
                -- Special case of values enclosed in spans and other markup.
                comp = value:gsub( "%b<>", "" );
            else
                comp = value;
            end
            
            if comp:sub(1,1) == duplicate_char then
                trim = false;
                end_chr = str:sub(-1,-1);
                -- str = str .. "<HERE(enchr=" .. end_chr.. ")"
                if end_chr == duplicate_char then
                    str = str:sub(1,-2);
                elseif end_chr == "'" then
                    if str:sub(-3,-1) == duplicate_char .. "''" then
                        str = str:sub(1, -4) .. "''";
                    elseif str:sub(-5,-1) == duplicate_char .. "]]''" then
                        trim = true;
                    elseif str:sub(-4,-1) == duplicate_char .. "]''" then
                        trim = true;
                    end
                elseif end_chr == "]" then
                    if str:sub(-3,-1) == duplicate_char .. "]]" then
                        trim = true;
                    elseif str:sub(-2,-1) == duplicate_char .. "]" then
                        trim = true;
                    end
                elseif end_chr == " " then
                    if str:sub(-2,-1) == duplicate_char .. " " then
                        str = str:sub(1,-3);
                    end
                end
                if trim then
                    if value ~= comp then 
                        local dup2 = duplicate_char;
                        if dup2:match( "%A" ) then dup2 = "%" .. dup2; end
                        
                        value = value:gsub( "(%b<>)" .. dup2, "%1", 1 )
                    else
                        value = value:sub( 2, -1 );
                    end
                end
            end
            str = str .. value;
        end
    end
    return str;
end  
--[[
Return the year portion of a date string, if possible.  
Returns empty string if the argument can not be interpreted
as a year.
]]
function selectyear( str )
    -- Is the input a simple number?
    local num = tonumber( str ); 
    if num ~= nil and num > 0 and num < 2100 and num == math.abs(num) then
        return str;
    else
        -- Use formatDate to interpret more complicated formats
        local lang = mw.getContentLanguage();
        local good, result;
        good, result = pcall( lang.formatDate, lang, 'Y', str )
        if good then 
            return result;
        else
            -- Can't make sense of this input, return blank.
            return "";
        end
    end
end
-- Attempts to convert names to initials.
function reducetoinitials(first)
    local initials = {}
    for word in string.gmatch(first, "%S+") do
        table.insert(initials, string.sub(word,1,1)) -- Vancouver format does not include full stops.
    end
    return table.concat(initials) -- Vancouver format does not include spaces.
end
-- Formats a list of people (e.g. authors / editors) 
function listpeople(control, people)
    local sep = control.sep;
    if sep:sub(-1,-1) ~= " " then sep = sep .. " " end
    local namesep = control.namesep
    local format = control.format
    local maximum = control.maximum
    local lastauthoramp = control.lastauthoramp;
    local text = {}
    local etal = false;
    for i,person in ipairs(people) do
        if (person.last ~= nil or person.last ~= "") then
            local mask = person.mask
            local one
            if ( maximum ~= nil and i == maximum + 1 ) then
                etal = true;
                break;
            elseif (mask ~= nil) then
                local n = tonumber(mask)
                if (n ~= nil) then
                    one = string.rep("—",n)
                else
                    one = mask
                end
            else
                one = person.last
                local first = person.first
                if (first ~= nil and first ~= '') then 
                    if ( "vanc" == format ) then first = reducetoinitials(first) end
                    one = one .. namesep .. first 
                end
                if (person.link ~= nil and person.link ~= "") then one = "[[" .. person.link .. "|" .. one .. "]]" end
            end
            table.insert(text, one)
        end
    end
    local count = #text;
    if count > 1 and lastauthoramp ~= nil and lastauthoramp ~= "" and not etal then
        text[count-1] = text[count-1] .. " & " .. text[count];
        text[count] = nil;
    end    
    local result = table.concat(text, sep) -- construct list
    if etal then 
        local etal_text = cfg.message_list['et al'];
        result = result .. " " .. etal_text;
    end
    
    -- if necessary wrap result in <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)
    local P1 = options[1] or ""
    local P2 = options[2] or ""
    local P3 = options[3] or ""
    local P4 = options[4] or ""
    local P5 = options[5] or ""
    
    -- Bugzilla 46608
    local encoded = mw.uri.anchorEncode( P1 .. P2 .. P3 .. P4 .. P5 );
    if encoded == false then
        encoded = "";
    end
    
    return "CITEREF" .. encoded;
end
-- Gets author list from the input arguments
function extractauthors(args)
    local authors = {};
    local i = 1;
    local last;
    while true do
        if i == 1 then 
            last = selectone( args, {"author" .. i .. "-last", "author-last" .. i, 
                "last" .. i, "surname" .. i, "Author" .. i, "author" .. i, 
                "author-last", "last", "surname", "Author", "author", "authors"}, 'redundant_parameters' );
        else
            last = selectone( args, {"author" .. i .. "-last", "author-last" .. i, 
                "last" .. i, "surname" .. i, "Author" .. i, "author" .. i}, 'redundant_parameters' );
        end
        if ( last and "" < last ) then -- just in case someone passed in an empty parameter
            if i == 1 then 
                authors[i] = {
                    last = last,
                    first = selectone( args, {"author" .. i .. "-first", "author-first" .. i, 
                        "first" .. i, "given" .. i, "author-first", 
                        "first", "given"}, 'redundant_parameters' ),
                    link = selectone( args, {"author" .. i .. "-link", "author-link" .. i, 
                        "author" .. i .. "link", "authorlink" .. i, "author-link",  
                        "authorlink"}, 'redundant_parameters' ),
                    mask = selectone( args, {"author" .. i .. "-mask", "author-mask" .. i, 
                        "author" .. i .. "mask", "authormask" .. i, "author-mask", 
                        "authormask" }, 'redundant_parameters' )
                }
            else
                authors[i] = {
                    last = last,
                    first = selectone( args, {"author" .. i .. "-first", "author-first" .. i, 
                        "first" .. i, "given" .. i}, 'redundant_parameters' ),
                    link = selectone( args, {"author" .. i .. "-link", "author-link" .. i, 
                        "author" .. i .. "link", "authorlink" .. i}, 'redundant_parameters' ),
                    mask = selectone( args, {"author" .. i .. "-mask", "author-mask" .. i, 
                        "author" .. i .. "mask", "authormask" .. i}, 'redundant_parameters' )
                }
            end            
        else
            break;
        end
        i = i + 1;
    end
    return authors;
end
-- Gets editor list from the input arguments
function extracteditors(args)
    local editors = {};
    local i = 1;
    local last;
    
    while true do
        if i == 1 then
            last = selectone( args, {"editor" .. i .. "-last", "editor-last" .. i,
                "EditorSurname" .. i, "Editor" .. i, "editor" .. i, "editor-last", 
                "EditorSurname", "Editor", "editor", "editors"}, 'redundant_parameters' );
        else
            last = selectone( args, {"editor" .. i .. "-last", "editor-last" .. i,
                "EditorSurname" .. i, "Editor" .. i, "editor" .. i}, 'redundant_parameters' );
        end        
        if ( last and "" < last ) then -- just in case someone passed in an empty parameter
            if i == 1 then
                editors[i] = {
                    last = last,
                    first = selectone( args, {"editor" .. i .. "-first", 
                        "editor-first" .. i, "EditorGiven" .. i, "editor-first", 
                        "EditorGiven"}, 'redundant_parameters' ),
                    link = selectone( args, {"editor" .. i .. "-link", "editor-link" .. i, 
                        "editor" .. i .. "link", "editorlink" .. i, "editor-link", 
                        "editorlink"}, 'redundant_parameters' ),
                    mask = selectone( args, {"editor" .. i .. "-mask", "editor-mask" .. i, 
                        "editor" .. i .. "mask", "editormask" .. i, "editor-mask",  
                        "editormask"}, 'redundant_parameters' )
                }                
            else
                editors[i] = {
                    last = last,
                    first = selectone( args, {"editor" .. i .. "-first", 
                        "editor-first" .. i, "EditorGiven" .. i}, 'redundant_parameters' ),
                    link = selectone( args, {"editor" .. i .. "-link", "editor-link" .. i, 
                        "editor" .. i .. "link", "editorlink" .. i}, 'redundant_parameters' ),
                    mask = selectone( args, {"editor" .. i .. "-mask", "editor-mask" .. i, 
                        "editor" .. i .. "mask", "editormask" .. i}, 'redundant_parameters' )
                }
            end
        else
            break;
        end
        i = i + 1;
    end
    return editors;
end
-- Populates ID table from arguments using configuration settings
function extractids( args )
    local id_list = {};
    
    for k, v in pairs( cfg.id_handlers ) do    
        id_list[k] = selectone( args, v.parameters, 'redundant_parameters' );
    end
    return id_list;
end
-- Takes a table of IDs and turns it into a table of formatted ID outputs.
function buildidlist( id_list, options )
    local handler;
    local new_list = {};
    
    for k, v in pairs( id_list ) do
        handler = {};
        
        --Becasue cfg is read-only we have to copy it the hard way.
        for k2, v2 in pairs( cfg.id_handlers[k] ) do
            handler[k2] = v2;
        end
        handler['id'] = v;
        
        if handler.mode == 'external' then        
            table.insert( new_list, {handler.label, externallinkid( handler ) } );
        elseif handler.mode == 'internal' then
            table.insert( new_list, {handler.label, internallinkid( handler ) } );
        elseif handler.mode == 'manual' then
            if k == 'DOI' then
                table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } );
            elseif k == 'ASIN' then
                table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } ); 
            elseif k == 'OL' then
                table.insert( new_list, {handler.label, openlibrary( v ) } );
            elseif k == 'ISBN' then
                local ISBN = internallinkid( handler );
                if not checkisbn( v ) then 
                    ISBN = ISBN .. seterror( 'bad_isbn' );
                end
                table.insert( new_list, {handler.label, ISBN } );                
            else
                error( cfg.message_list['unknown_manual_ID'] );
            end            
        else
            error( cfg.message_list['unknown_ID_mode'] );
        end
    end
    function comp( a, b )
        return a[1] < b[1];
    end
    table.sort( new_list, comp );
    for k, v in ipairs( new_list ) do
        new_list[k] = v[2];
    end
    
    return new_list;
end
  
-- Chooses one matching parameter from a list of parameters to consider
-- Generates an error if more than one match is present.
function selectone( args, possible, error_condition )
    local value = nil;
    local selected = '';
    local error_list = {};
    
    for _, v in ipairs( possible ) do
        if args[v] ~= nil then
            if value ~= nil then
                table.insert( error_list, v );
            else
                value = args[v];
                selected = v;
            end
        end
    end
            
    if #error_list > 0 then
        local error_str = "";
        for _, k in ipairs( error_list ) do
            if error_str ~= "" then error_str = error_str .. ", " end
            error_str = error_str .. "<code>|" .. k .. "=</code>";
        end
        if #error_list > 1 then
            error_str = error_str .. ", and ";
        else
            error_str = error_str .. " and ";
        end
        error_str = error_str .. "<code>|" .. selected .. "=</code>";
        table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } );
    end
            
    return value, selected;
end
--[[
This is the main function foing the majority of the citation
formatting.
]]
function citation0( config, args)
    -- Load Input Parameters
    local i 
    local PPrefix = config.PPrefix or "p. "
    local PPPrefix = config.PPPrefix or "pp. "
    if ( nil ~= args.nopp ) then PPPrefix = "" PPrefix = "" end
    
    -- Pick out the relevant fields from the arguments.  Different citation templates
    -- define different field names for the same underlying things.    
    local Authors = args.authors
    local a = extractauthors( args );
    local Coauthors = args.coauthors or args.coauthor 
    local Others = args.others 
    local Editors = args.editors
    local e = extracteditors( args );
    local Year = args.year 
    local PublicationDate = args.publicationdate or args["publication-date"]
    local OrigYear = args.origyear
    local Date = args.date
    local LayDate = args.laydate
    ------------------------------------------------- Get title data
    local Title = args.title or args.encyclopaedia or args.encyclopedia or args.dictionary
    local BookTitle = args.booktitle
    local Conference = args.conference
    local TransTitle = args.trans_title
    local TitleNote = args.department
    local TitleLink = args.titlelink or args.episodelink
    local Chapter = selectone( args, {'chapter', 'contribution', 'entry' }, 'redundant_parameters' );
    local ChapterLink = args.chapterlink
    local TransChapter = args["trans-chapter"] or args.trans_chapter
    local TitleType = args.type
    local ArchiveURL = args["archive-url"] or args.archiveurl
    local URL = args.url or args.URL
    local ChapterURL = args["chapter-url"] or args.chapterurl or args["contribution-url"]
    local ConferenceURL = args["conference-url"] or args.conferenceurl
    local Periodical = selectone( args, {'journal', 'newspaper', 'magazine', 'work', 'website', 
        'periodical', 'encyclopedia', 'encyclopaedia'}, 'redundant_parameters' );
            
    if ( config.CitationClass == "encyclopaedia" ) then
        if ( args.article and args.article ~= "") then
            if ( Title and Title ~= "") then Periodical = Title end
            Chapter = args.article
            TransChapter = TransTitle
            Title = nil          
            TransTitle = nil
        elseif ( Chapter == nil or Chapter == '' ) then
            if Title ~= args.encyclopedia then 
                Chapter = Title
                TransChapter = TransTitle
                Title = nil 
                TransTitle = nil
            end
        end
        if ( Periodical and Periodical ~= "") then
            if Periodical == Title or Periodical == Chapter then Periodical = nil end
        end
    end
    local Series = selectone( args, {'series', 'version'}, 'redundant_parameters' );
    local Volume = args.volume
    local Issue = selectone( args, {'issue', 'number'}, 'redundant_parameters' );
    local Position = nil
    local Page, Pages, At, page_type;
    
    Page, page_type = selectone( args, {'p', 'page', 'pp', 'pages', 'at'}, 
        'extra_pages' );
    if page_type == 'pp' or page_type == 'pages' then
        Pages = hyphentodash( Page );
        Page = nil;
    elseif page_type == 'at' then
        At = Page;
        Page = nil;
    end
                
    local Edition = args.edition
    local PublicationPlace = args["publication-place"] or args.publicationplace 
    local Place = selectone( args, {'place', 'location'}, 'redundant_parameters' );
    if PublicationPlace == nil and Place ~= nil then 
        PublicationPlace = Place;
    end
    if PublicationPlace == Place then Place = nil end
    
    local PublisherName = args.publisher
    local SubscriptionRequired = args.subscription
    local Via = args.via
    local AccessDate = args["access-date"] or args.accessdate
    local ArchiveDate = args["archive-date"] or args.archivedate
    local Agency = args.agency
    local DeadURL = args.deadurl or "yes"           -- Only used is ArchiveURL is present.
    local Language = selectone( args, {'language', 'in'}, 'redundant_parameters' );
    local Format = args.format
    local Ref = args.ref or args.Ref
    local DoiBroken = args.doi_inactivedate or args.doi_brokendate or args.DoiBroken
    local ID = selectone( args, {'id', 'ID', 'docket'}, 'redundant_parameters' );
    local ASINTLD = args["ASIN-TLD"] or args["asin-tld"]
    local ID_list = extractids( args );
    
    local Quote = selectone( args, {'quote', 'quotation'}, 'redundant_parameters' );
    local PostScript = args.postscript or "."
    local LaySummary = args.laysummary
    local LaySource = args.laysource
    local Transcript = args.transcript
    local TranscriptURL = args["transcript-url"] or args.transcripturl
    local sepc = args.separator or "."
    local LastAuthorAmp = args.lastauthoramp
    local no_tracking_cats = args["template doc demo"] or args.nocat or 
            args.notracking or args["no-tracking"] or "";
    if ( config.CitationClass == "journal" ) then        
        if (URL == nil or URL == "") then
            if (ID_list['PMC'] ~= nil) then 
                local Embargo = args.embargo or args.Embargo;
                if Embargo ~= nil then
                    local lang = mw.getContentLanguage();
                    local good1, result1, good2, result2;
                    good1, result1 = pcall( lang.formatDate, lang, 'U', Embargo );
                    good2, result2 = pcall( lang.formatDate, lang, 'U' );
                    if good1 and good2 and tonumber( result1 ) < tonumber( result2 ) then 
                        URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC'];
                    end
                else
                    URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC'];           
                end
            end
        end
    end
    -- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.
    
    -- Account for the oddity that is {{cite conference}}, before generation of COinS data.
    if ( BookTitle ) then
        Chapter = Title
        ChapterLink = TitleLink
        TransChapter = TransTitle
        Title = BookTitle
        TitleLink = nil
        TransTitle = nil
    end
    -- Account for the oddity that is {{cite episode}}, before generation of COinS data.
    if config.CitationClass == "episode" then
        local AirDate = args.airdate
        local SeriesLink = args.serieslink
        local Season = args.season
        local SeriesNumber = args.seriesnumber or args.seriesno
        local Network = args.network
        local Station = args.station
        local s = {}
        if Issue ~= nil then table.insert(s, cfg.message_list["episode"] .. " " .. Issue) Issue = nil end
        if Season ~= nil then table.insert(s, cfg.message_list["season"] .. " " .. Season) end
        if SeriesNumber ~= nil then table.insert(s, cfg.message_list["series"] .. " " .. SeriesNumber) end
        local n = {}
        if Network ~= nil then table.insert(n, Network) end
        if Station ~= nil then table.insert(n, Station) end
        Date = Date or AirDate
        Chapter = Title
        ChapterLink = TitleLink
        TransChapter = TransTitle
        Title = Series
        TitleLink = SeriesLink
        TransTitle = nil
        local Sep = args["series-separator"] or args["separator"] or ". "
        Series = table.concat(s, Sep)
        ID = table.concat(n, Sep)
    end
    
    -- These data form a COinS tag (see <http://ocoins.info/>) which allows 
    -- automated tools to parse the citation information.
    local OCinSdata = {} -- COinS metadata excluding id, bibcode, doi, etc.
    local ctx_ver = "Z39.88-2004" 
    OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"
    if ( nil ~= Periodical ) then
        OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"
        OCinSdata["rft.genre"] = "article"
        OCinSdata["rft.jtitle"] = Periodical
        if ( nil ~= Title ) then OCinSdata["rft.atitle"] = Title end
    end
    if ( nil ~= Chapter and "" ~= Chapter) then
        OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"
        OCinSdata["rft.genre"] = "bookitem"
        OCinSdata["rft.btitle"] = Chapter
        if ( nil ~= Title ) then OCinSdata["rft.atitle"] = Title end
    else
        OCinSdata["rft.genre"] = "book"
        if ( nil ~= Title ) then OCinSdata["rft.btitle"] = Title end
    end
    OCinSdata["rft.place"] = PublicationPlace
    OCinSdata["rft.date"] = Date or Year or PublicationDate
    OCinSdata["rft.series"] = Series
    OCinSdata["rft.volume"] = Volume
    OCinSdata["rft.issue"] = Issue
    OCinSdata["rft.pages"] = Page or Pages or At
    OCinSdata["rft.edition"] = Edition
    OCinSdata["rft.pub"] = PublisherName
    
    for k, v in pairs( ID_list ) do
        if string.sub( cfg.id_handlers[k].COinS or "info", 1, 4 ) ~= 'info' then
            OCinSdata[ cfg.id_handlers[k].COinS ] = v;
        end
    end
    
    OCinSdata.rft_id = URL or ChapterURL
    local last, first;
    local OCinSauthors = {};
    for k, v in ipairs( a ) do
        last = v.last;
        first = v.first;
        if k == 1 then
            if last ~= nil then
                OCinSdata["rft.aulast"] = last;
            end
            if first ~= nil then 
                OCinSdata["rft.aufirst"] = first;
            end
        end
        if last ~= nil and first ~= nil then
            table.insert( OCinSauthors, last .. (args.NameSep or ", ") .. first );
    	elseif last ~= nil then
            table.insert( OCinSauthors, last );
        end
    end
    local OCinSids = {} -- COinS data only for id, bibcode, doi, pmid, etc.
    for k, v in pairs( ID_list ) do
        if string.sub( cfg.id_handlers[k].COinS or "", 1, 4 ) == 'info' then
            OCinSids[ cfg.id_handlers[k].COinS ] = v;
        end
    end
    local OCinStitle = "ctx_ver=" .. ctx_ver  -- such as "Z39.88-2004"
    for name,value in pairs(OCinSdata) do
        OCinStitle = OCinStitle .. "&" .. name .. "=" .. mw.uri.encode(value)
    end
    for _, value in ipairs(OCinSauthors) do
        OCinStitle = OCinStitle .. "&rft.au=" .. mw.uri.encode(value)
    end
    for name,value in pairs(OCinSids) do
        OCinStitle = OCinStitle .. "&rft_id=" .. mw.uri.encode(name .. "/" .. value)
    end
    
    local this_page = mw.title.getCurrentTitle();
    OCinStitle = OCinStitle .. "&rfr_id=info:sid/" .. mw.site.server:match( "[^/]*$" ) .. ":"
       .. this_page.prefixedText  -- end COinS data by page's non-encoded pagename
    if (Periodical ~= nil and Periodical ~= "") and
        (Chapter == nil or Chapter == '') and
        (Title ~= nil and Title ~= "") then
            Chapter = Title
            ChapterLink = TitleLink
            TransChapter = TransTitle
            Title = nil
            TitleLink = nil
            TransTitle = nil            
    end
    -- Now perform various field substitutions.
    -- We also add leading spaces and surrounding markup and punctuation to the
    -- various parts of the citation, but only when they are non-nil.
    if ( Authors == nil ) then 
        local Maximum = tonumber(args["display-authors"] or args.displayauthors);
        
        -- Preserve old-style implicit et al.
        if Maximum == nil and #a == 9 then 
            Maximum = 8;
            table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } );
        elseif Maximum == nil then
            Maximum = #a + 1;
        end
            
        local control = { 
            sep = (args["author-separator"] or ";") .. " ",
            namesep = (args["author-name-separator"] or args["name-separator"] or ",") .. " ",
            format = args["author-format"] or args.authorformat,
            maximum = Maximum,
            lastauthoramp = LastAuthorAmp
        }
        
        -- If the coauthor field is also used, prevent ampersand and et al. formatting.
        if Coauthors ~= nil and Coauthors ~= "" then
            control.lastauthoramp = nil;
            control.maximum = #a + 1;
        end
                
        Authors = listpeople(control, a) 
    end
    local EditorCount
    if ( Editors == nil ) then 
        local Maximum = tonumber(args["display-editors"] or args.displayeditors);
        -- Preserve old-style implicit et al.
        if Maximum == nil and #e == 4 then 
            Maximum = 3;
            table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } );
        elseif Maximum == nil then
            Maximum = #e + 1;
        end
        local control = { 
            sep = (args["editor-separator"] or ";") .. " ",
            namesep = (args["editor-name-separator"] or args["name-separator"] or ",") .. " ",
            format = args["editor-format"] or args.editorformat,
            maximum = Maximum,
            lastauthoramp = LastAuthorAmp
            }
        Editors, EditorCount = listpeople(control, e) 
    else
        EditorCount = 1;
    end
    if ( Date == nil or Date == "") then
--   there's something hinky with how this adds dashes to perfectly-good free-standing years
--[[        Date = Year
        if ( Date ~= nil ) then
            local Month = args.month
            if ( Month == nil ) then 
                local Began = args.began
                local Ended = args.ended
                if Began ~= nil and Ended ~= nil then
                    Month = Began .. "–" .. Ended
                else
                    Month = "–"
                end
            end
            Date = Month .. " " .. Date
            local Day = args.day
            if ( Day ~= nil ) then Date = Day .. " " .. Date end
        end
]] -- so let's use the original version for now
        Date = Year
        if ( Date ~= nil and Date ~="") then
            local Month = args.month
            if ( Month ~= nil and Month ~= "") then 
                Date = Month .. " " .. Date 
                local Day = args.day
                if ( Day ~= nil ) then Date = Day .. " " .. Date end
                else Month = ""
            end
            else Date = ""
        end
    end
    if ( PublicationDate == Date or PublicationDate == Year ) then PublicationDate = nil end
    if( (Date == nil or Date == "") and PublicationDate ~= nil ) then 
        Date = PublicationDate;
        PublicationDate = nil;
    end    
    -- Captures the value for Date prior to adding parens or other textual transformations
    local DateIn = Date
    
    if ( URL == nil or URL == '' ) and
            ( ChapterURL == nil or ChapterURL == '' ) and
            ( ArchiveURL == nil or ArchiveURL == '' ) and                
            ( ConferenceURL == nil or ConferenceURL == '' ) and                
            ( TranscriptURL == nil or TranscriptURL == '' ) then
        -- Test if cite web is called without giving a URL
        if ( config.CitationClass == "web" ) then
            table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } );
        end
        -- Test if accessdate is given without giving a URL
        if ( AccessDate ~= nil and AccessDate ~= '' ) then
            table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } );
            AccessDate = nil;
        end      
    
        -- Test if format is given without giving a URL
        if ( Format ~= nil and Format ~= '' ) then
            Format = Format .. seterror( 'format_missing_url' );
        end        
    end    
    -- Test if citation has no title
    if ( Chapter == nil or Chapter == "" ) and 
            ( Title == nil or Title == "" ) and
            ( Periodical == nil or Periodical == "" ) and
            ( Conference == nil or Conference == "" ) and 
            ( TransTitle == nil or TransTitle == "" ) and
            ( TransChapter == nil or TransChapter == "" ) then
        table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } );
    end
    if ( Format ~= nil and Format ~="" ) then
        Format = " (" .. Format .. ")" else Format = "" end
    
    local OriginalURL = URL
    DeadURL = DeadURL:lower();
    if ( ArchiveURL and "" < ArchiveURL ) then
        if ( DeadURL ~= "no" ) then
            URL = ArchiveURL
        end
    end
    if ( TransTitle and "" < TransTitle ) then TransTitle = " [" .. TransTitle .. "]" else TransTitle = "" end
    if ( TransChapter and "" < TransChapter ) then TransChapter = " [" .. TransChapter .. "]" else TransChapter = "" end
        
    -- Format chapter / article title
    if ( Chapter ~= nil and Chapter ~= "" ) then
        if ( ChapterLink and "" < ChapterLink ) then Chapter = "[[" .. ChapterLink .. "|" .. Chapter .. "]]" end
        if ( Periodical and "" < Periodical ) and (Title ~= nil and Title ~= "" )
        then
            Chapter = "''" .. safeforitalics(Chapter) .. "''"
        else
            Chapter = "\"" .. Chapter .. "\""
        end
    else
        Chapter = "";
    end
    if TransChapter ~= "" and Chapter == "" then
        TransChapter = TransChapter .. seterror( 'trans_missing_chapter' );
    end
    Chapter = Chapter .. TransChapter
    if Chapter ~= "" then
        if ( ChapterLink == nil ) then
            if ( ChapterURL and "" < ChapterURL ) then
                Chapter = "[" .. ChapterURL .. " " .. safeforurl( Chapter )  .. "]"
                if URL == nil or URL == "" then
                    Chapter = Chapter .. Format;
                    Format = "";
                end
            elseif ( URL and "" < URL ) then 
                Chapter = "[" .. URL .. " " .. safeforurl( Chapter ) .. "]" .. Format
                URL = nil
                Format = ""
            end
        elseif ChapterURL ~= nil and ChapterURL ~= "" then
            Chapter = Chapter .. " [" .. ChapterURL .. " " .. safeforurl( ChapterURL ) .. "]" .. 
                seterror( 'bare_url_missing_title' );
        end
        Chapter = Chapter .. sepc .. " " -- with end-space
    elseif ChapterURL ~= nil and ChapterURL ~= "" then
        Chapter = " [" .. ChapterURL .. " " .. safeforurl( ChapterURL ) .. "]" .. 
            seterror( 'bare_url_missing_title' ) .. sepc .. " ";
    end        
    
    -- Format main title.
    if ( Title and "" < Title ) then
        if ( TitleLink and "" < TitleLink ) then
            Title = "[[" .. TitleLink .. "|" .. Title .. "]]" end
        if ( Periodical and "" < Periodical ) then
            Title = "\"" .. Title .. "\""
        elseif ( config.CitationClass == "web"
                or config.CitationClass == "news" 
                or config.CitationClass == "pressrelease" ) and 
                Chapter == "" then
            Title = "\"" .. Title .. "\""
        else
            Title = "''" .. safeforitalics(Title) .. "''"
        end
    else
        Title = "";
    end    
    if TransTitle ~= "" and Title == "" then
        TransTitle = TransTitle .. seterror( 'trans_missing_title' );
    end
    Title = Title .. TransTitle
    if Title ~= "" then
        if ( TitleLink == nil and URL and "" < URL ) then 
            Title = "[" .. URL .. " " .. safeforurl( Title ) .. "]" .. Format
            URL = nil
            Format = ''
        end
    end
    if ( Place ~= nil and Place ~= "" ) then
        if sepc == '.' then
            Place = " " .. cfg.message_list['written'] .. " " .. Place .. sepc .. " ";
        else
            Place = " " .. cfg.message_list['written']:lower() .. " " .. Place .. sepc .. " ";
        end            
    else
        Place = "";
    end
    
    if ( Conference ~= nil and Conference ~="" ) then
        if ( ConferenceURL ~= nil ) then
            Conference = "[" .. ConferenceURL .. " " .. safeforurl( Conference ) .. "]"
        end
        Conference = " " .. Conference
    elseif ConferenceURL ~= nil and ConferenceURL ~= "" then
        Conference = " [" .. ConferenceURL .. " " .. safeforurl( ConferenceURL ) .. "]" .. 
            seterror( 'bare_url_missing_title' );
    else
        Conference = "" 
    end
    if ( nil ~= Position or nil ~= Page or nil ~= Pages ) then At = nil end
    if ( nil == Position and "" ~= Position ) then
        local Minutes = args.minutes
        if ( nil ~= Minutes ) then
            Position = " " .. Minutes .. " " .. cfg.message_list['minutes'];
        else
            local Time = args.time
            if ( nil ~= Time ) then
                local TimeCaption = args.timecaption 
                if TimeCaption == nil then
                    TimeCaption = cfg.message_list['event'];
                    if sepc ~= '.' then
                        TimeCaption = TimeCaption:lower();
                    end
                end                
                Position = " " .. TimeCaption .. " " .. Time
            else
                Position = ""
            end
        end
    else
        Position = " " .. Position
    end
    if ( nil == Page or "" == Page ) then 
        Page = "" 
        if ( nil == Pages or "" == Pages) then 
            Pages = ""
        elseif ( Periodical ~= nil and Periodical ~= "" and
                 config.CitationClass ~= "encyclopaedia" and
                 config.CitationClass ~= "web" and
                 config.CitationClass ~= "book" and
                 config.CitationClass ~= "news") then
            Pages = ": " .. Pages
        else
            if ( tonumber(Pages) ~= nil ) then
              Pages = sepc .." " .. PPrefix .. Pages
            else Pages = sepc .." " .. PPPrefix .. Pages
            end
        end
    else
        Pages = ""
        if ( Periodical ~= nil and Periodical ~= "" and
             config.CitationClass ~= "encyclopaedia" and
             config.CitationClass ~= "web" and
             config.CitationClass ~= "book" and
             config.CitationClass ~= "news") then
            Page = ": " .. Page
        else
            Page = sepc .." " .. PPrefix .. Page
        end
    end
    if ( At ~= nil and At ~="") then At = sepc .. " " .. At
    else At = "" end
    if ( Coauthors == nil ) then Coauthors = "" end
    if ( Others ~= nil and Others ~="" ) then
        Others = sepc .. " " .. Others else Others = "" end
    if ( TitleType ~= nil and TitleType ~="" ) then
        TitleType = " (" .. TitleType .. ")" else TitleType = "" end
    if ( TitleNote ~= nil and TitleNote ~="" ) then
        TitleNote = sepc .. " " .. TitleNote else TitleNote = "" end
    if ( Language ~= nil and Language ~="" ) then
        Language = " (" .. cfg.message_list['in'] .. " " .. Language .. ")" else Language = "" end
    if ( Edition ~= nil and Edition ~="" ) then
        Edition = " (" .. Edition .. " " .. cfg.message_list['edition'] .. ")" else Edition = "" end
    if ( Volume ~= nil and Volume ~="" )
    then
        if ( mw.ustring.len(Volume) > 4 )
          then Volume = sepc .." " .. Volume
          else Volume = " <b>" .. hyphentodash(Volume) .. "</b>"
        end
    else Volume = "" end
    if ( Issue ~= nil and Issue ~="" ) then
        Issue = " (" .. Issue .. ")" else Issue = "" end
    if ( Series ~= nil and Series ~="" ) then
        Series = sepc .. " " .. Series else Series = "" end
    if ( OrigYear ~= nil and OrigYear ~="" ) then
        OrigYear = " [" .. OrigYear .. "]" else OrigYear = "" end
    if ( Agency ~= nil and Agency ~="" ) then
        Agency = sepc .. " " .. Agency else Agency = "" end
    ------------------------------------ totally unrelated data
    if ( Date ~= nil ) then Date = Date else Date = "" end
    if ( Via ~= nil and Via ~="" ) then
        Via = " — " .. cfg.message_list['via'] .. " " .. Via else Via = "" end
    if ( AccessDate ~= nil and AccessDate ~="" )
    then local retrv_text = " " .. cfg.message_list['retrieved'] .. " "
         if (sepc ~= ".") then retrv_text = retrv_text:lower() end
         AccessDate = '<span class="reference-accessdate">' .. sepc
             .. retrv_text .. AccessDate .. '</span>'
    else AccessDate = "" end
    if ( SubscriptionRequired ~= nil and
         SubscriptionRequired ~= "" ) then
        SubscriptionRequired = sepc .. " " .. cfg.message_list['subscription'];
    else
        SubscriptionRequired = ""
    end
    if ( ID ~= nil and ID ~="") then ID = sepc .." ".. ID else ID="" end
    ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD} );
    if ( URL ~= nil and URL ~="") then
        URL = " " .. "[" .. URL .. " " .. mw.text.nowiki(URL) .. "]";
        local error_text = seterror( 'bare_url_missing_title' );
        if config.CitationClass == "web" then
            URL = URL .. " " .. seterror( 'cite_web_title' );
        else
            URL = URL .. error_text;
        end       
    else
        URL = ""
    end
    if ( Quote and Quote ~="" ) then 
        if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then
            Quote = Quote:sub(2,-2);
        end
        
        Quote = sepc .." \"" .. Quote .. "\"" 
        PostScript = ""
    else 
        if ( PostScript == nil) then PostScript = "" end
        Quote = "" 
    end
    
    local Archived
    if ( nil ~= ArchiveURL and "" ~= ArchiveURL ) then
        if ( ArchiveDate ~= nil and ArchiveDate ~="" ) then
            ArchiveDate = " " .. ArchiveDate
        else 
            ArchiveDate = " " .. seterror('archive_missing_date') .. " "
        end
        local arch_text = " " .. cfg.message_list['archived'];
        if (sepc ~= ".") then arch_text = arch_text:lower() end
        if ( "no" == DeadURL ) then
            Archived = sepc .. " [" .. ArchiveURL .. arch_text .. "] " .. 
                cfg.message_list['from'] .. " " .. cfg.message_list['original'] .. " " .. 
                cfg.message_list['on'] .. ArchiveDate
            if OriginalURL == nil or OriginalUrl == '' then
                Archived = Archived .. " " .. seterror('archive_missing_url_not_dead');                               
            end
        else
            if OriginalURL ~= nil and OriginalURL ~= '' then
                Archived = sepc .. arch_text .. " " .. cfg.message_list['from'] .. " [" .. OriginalURL .. " " .. 
                    cfg.message_list['original'] .. "] " .. cfg.message_list['on'] .. ArchiveDate
            else
                if config.CitationClass ~= 'web' then 
                    Archived = sepc .. arch_text .. " " .. cfg.message_list['from'] .. " " .. 
                    seterror('archive_missing_url') .. " " .. cfg.message_list['on'] .. ArchiveDate
                else
                    Archived = sepc .. arch_text .. " " .. cfg.message_list['from'] .. 
                        " " .. cfg.message_list['original'] .. " " .. 
                        cfg.message_list['on'] .. ArchiveDate
                end
            end                
        end
    else
        Archived = ""
    end
    local Lay
    if ( nil ~= LaySummary and "" ~= LaySummary ) then
        if ( LayDate ~= nil ) then LayDate = " (" .. LayDate .. ")" else LayDate = "" end
        if ( LaySource ~= nil ) then 
            LaySource = " – ''" .. safeforitalics(LaySource) .. "''" 
        else 
            LaySource = "" 
        end
        if sepc == '.' then
            Lay = sepc .. " [" .. LaySummary .. " " .. cfg.message_list['lay summary'] .. "]" .. LaySource .. LayDate
        else
            Lay = sepc .. " [" .. LaySummary .. " " .. cfg.message_list['lay summary']:lower() .. "]" .. LaySource .. LayDate
        end            
    else
        Lay = ""
    end
    if ( nil ~= Transcript and "" ~= Transcript ) then
        if ( TranscriptURL ~= nil ) then Transcript = "[" .. TranscriptURL .. Transcript .. "]" end
    elseif TranscriptURL ~= nil and TranscriptURL ~= "" then
        Transcript = "[" .. TranscriptURL .. " " .. safeforurl( TranscriptURL ) .. "]" .. 
            seterror( 'bare_url_missing_title' )        
    else
        Transcript = ""
    end
    local Publisher = ""
    if ( Periodical and Periodical ~= "" and
         config.CitationClass ~= "encyclopaedia" and
         config.CitationClass ~= "web" and
         config.CitationClass ~= "pressrelease" ) then
        if ( PublisherName ~= nil and PublisherName ~="" ) then
            if (PublicationPlace ~= nil and PublicationPlace ~= '') then
                Publisher = PublicationPlace .. ": " .. PublisherName;
            else
                Publisher = PublisherName;  
            end            
        elseif (PublicationPlace ~= nil and PublicationPlace ~= '') then 
            Publisher= PublicationPlace;
        else 
            Publisher = "";
        end
        if ( PublicationDate and PublicationDate ~="" ) then
            if Publisher ~= '' then
                Publisher = Publisher .. ", " .. cfg.message_list['published'] .. " " .. PublicationDate;
            else
                Publisher = PublicationDate;
            end
        end
        if Publisher ~= "" then
            Publisher = " (" .. Publisher .. ")";
        end
    else
        if ( PublicationDate and PublicationDate ~="" ) then
            PublicationDate = " (" .. cfg.message_list['published'] .. " " .. PublicationDate .. ")"
        else 
            PublicationDate = ""
        end
        if ( PublisherName ~= nil and PublisherName ~="" ) then
            if (PublicationPlace ~= nil and PublicationPlace ~= '') then
                Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
            else
                Publisher = sepc .. " " .. PublisherName .. PublicationDate;  
            end            
        elseif (PublicationPlace ~= nil and PublicationPlace ~= '') then 
            Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
        else 
            Publisher = PublicationDate;
        end
    end
    -- Several of the above rely upon detecting this as nil, so do it last.
    if ( Periodical ~= nil and Periodical ~="" ) then 
        if ( Title and Title ~= "" ) or ( TitleNote and TitleNote ~= "" ) then 
            Periodical = sepc .. " ''" .. safeforitalics(Periodical) .. "''"
        else 
            Periodical = "''" .. safeforitalics(Periodical) .. "''"
        end
    else Periodical = "" end
    -- Piece all bits together at last.  Here, all should be non-nil.
    -- We build things this way because it is more efficient in LUA
    -- not to keep reassigning to the same string variable over and over.
    local tcommon
    if ( ( (config.CitationClass == "journal") or (config.CitationClass == "citation") )  and
         Periodical ~= "" ) then
        if (Others ~= "") then Others = Others .. sepc .. " " end
        tcommon = safejoin( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, 
            Language, Edition, Publisher, Agency, Volume, Issue, Position}, sepc );
    else 
        tcommon = safejoin( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, 
            Volume, Issue, Others, Edition, Publisher, Agency, Position}, sepc );
    end
    
    if #ID_list > 0 then
        ID_list = safejoin( { sepc .. " ",  table.concat( ID_list, sepc .. " " ), ID }, sepc );
    else
        ID_list = ID;
    end    
    local idcommon = safejoin( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc );
    local text
    local pgtext = Page .. Pages .. At
    
    if ( "" ~= Authors ) then
        if (Coauthors ~= "") 
          then Authors = Authors .. "; " .. Coauthors
        end
        if ( "" ~= Date )
          then Date = " ("..Date..")" .. OrigYear .. sepc .. " "
          else
            if ( string.sub(Authors,-1,-1) == sepc) --check end character
              then Authors = Authors .. " "
              else Authors = Authors .. sepc .. " "
            end
        end
        if ( "" ~= Editors) then
            local in_text = " in "
            if (sepc == '.') then in_text = " In " end
            if (string.sub(Editors,-1,-1) == sepc)
                then Editors = in_text .. Editors .. " "
                else Editors = in_text .. Editors .. sepc .. " "
            end
        end
        text = safejoin( {Authors, Date, Chapter, Place, Editors, tcommon }, sepc );
        text = safejoin( {text, pgtext, idcommon}, sepc );
    elseif ( "" ~= Editors) then
        if ( "" ~= Date ) then
            if EditorCount <= 1 then
                Editors = Editors .. ", " .. cfg.message_list['editor'];
            else
                Editors = Editors .. ", " .. cfg.message_list['editors'];
            end
            Date = " (" .. Date ..")" .. OrigYear .. sepc .. " "
        else
            if EditorCount <= 1 then
                Editors = Editors .. " (" .. cfg.message_list['editor'] .. ")" .. sepc .. " "
            else
                Editors = Editors .. " (" .. cfg.message_list['editors'] .. ")" .. sepc .. " "
            end
        end
        text = safejoin( {Editors, Date, Chapter, Place, tcommon}, sepc );
        text = safejoin( {text, pgtext, idcommon}, sepc );
    else
        if ( "" ~= Date ) then
            if ( string.sub(tcommon,-1,-1) ~= sepc )
              then Date = sepc .." " .. Date .. OrigYear
              else Date = " " .. Date .. OrigYear
            end
        end -- endif ""~=Date
        if ( config.CitationClass=="journal" and Periodical ) then
          text = safejoin( {Chapter, Place, tcommon}, sepc );
          text = safejoin( {text, pgtext, Date, idcommon}, sepc );
        else
          text = safejoin( {Chapter, Place, tcommon, Date}, sepc );
          text = safejoin( {text, pgtext, idcommon}, sepc );
        end
    end
    
    if PostScript ~= '' and PostScript ~= nil and PostScript ~= sepc then
        text = safejoin( {text, sepc}, sepc );  --Deals with italics, spaces, etc.
        text = text:sub(1,-2); --Remove final seperator    
    end    
    
    text = safejoin( {text, PostScript}, sepc );
    -- Now enclose the whole thing in a <span/> element
    if ( Year == nil ) then
        if ( DateIn ~= nil and DateIn ~= "" ) then 
            Year = selectyear( DateIn )
        elseif( PublicationDate ~= nil and PublicationDate ~= "" ) then
            Year = selectyear( PublicationDate )
        else
            Year = ""
        end
    end
    local classname = "citation"
    if ( config.CitationClass ~= "citation" )
       then classname = "citation " .. (config.CitationClass or "") end
    local options = { class=classname }
    if ( Ref ~= nil ) then 
        local id = Ref
        if ( "harv" == Ref ) then
            local names = {} --table of last names & year
            if ( "" ~= Authors ) then
                for i,v in ipairs(a) do names[i] = v.last end
            elseif ( "" ~= Editors ) then
                for i,v in ipairs(e) do names[i] = v.last end
            end
            if ( names[1] == nil ) then 
                names[1] = Year 
            elseif ( names[2] == nil ) then 
                names[2] = Year 
            elseif ( names[3] == nil ) then
                names[3] = Year
            elseif ( names[4] == nil ) then
                names[4] = Year
            else
                names[5] = Year
            end
            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 options.id ~= nil then 
        text = '<span id="' .. wikiescape(options.id) ..'" class="' .. wikiescape(options.class) .. '">' .. text .. "</span>";
    else
        text = '<span class="' .. wikiescape(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="' .. wikiescape(OCinStitle) .. '" class="Z3988">' .. empty_span .. '</span>';
    text = text .. OCinS;
    
    if #z.message_tail ~= 0 then
        for i,v in ipairs( z.message_tail ) do
            if i == #z.message_tail then
                text = text .. errorcomment( v[1], v[2] );
            else
                text = text .. errorcomment( v[1] .. "; ", v[2] );
            end
        end
    end
    
    if no_tracking_cats == '' 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;
    for k, v in pairs( pframe.args ) do
        if v ~= '' then
            if not validate( k ) then            
                if type( k ) ~= 'string' then
                    -- Exclude empty numbered parameters
                    if v:match("%S+") ~= nil then
                        error_text, error_state = seterror( 'text_ignored', {v}, true );
                    end
                elseif validate( k:lower() ) then 
                    error_text, error_state = seterror( 'parameter_ignored_suggest', {k, k:lower()}, true );
                else
                    if #suggestions == 0 then
                        suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' );
                    end
                    if suggestions[ k:lower() ] ~= nil then
                        error_text, error_state = seterror( 'parameter_ignored_suggest', {k, suggestions[ k:lower() ]}, true );
                    else
                        error_text, error_state = seterror( 'parameter_ignored', {k}, true );
                    end
                end                  
                if error_text ~= '' then
                    table.insert( z.message_tail, {error_text, error_state} );
                end                
            end            
            args[k] = v;
        elseif k == 'postscript' then
            args[k] = v;
        end        
    end    
    local config = {};
    for k, v in pairs( frame.args ) do
        config[k] = v;
        if args[k] == nil and (v ~= '' or k == 'postscript') then
            args[k] = v;
        end        
    end    
    
    return citation0( config, args)
end
return z
---------------------------------------------------------------------
--NOTES
--
-- NOTE A1: This Lua module was originally designed to handle a mix
--      of citation styles, crossing Vancouver style with Wikipedia's
--      local Citation Style 1 (CS1) from {Template:Citation/core}.
--      However, the conflicting positions of parameters, scattered
--      in twisted locations across this module, led to a separate
--      variation just to untangle the CS1 format of citations.
--
-- NOTE D2: The placement of dots and other separators between the
--      displayed parameters has been a continual headache, to keep
--      coordinated with the data in parentheses "(data)". There
--      has been a need to pre-check for the existence of related
--      options, to keep from putting double-dots ".." in some cases.
--      In particular, the omission of the "title=" parameter has led
--      to several cases of a spurious dot ". ." because the original
--      design had treated the title as a mandatory parameter.
--
------------------------------------------------------------------------
--HISTORY:
--18Oct2012 Fixed lead-space in Chapter by omitting " ".
--18Oct2012 Fixed lead-space in Chapter/Title as end " " of Authors/Date/...
--19Oct2012 Put HISTORY comments to log major changes (not typos).
--19Oct2012 Fixed extra dot ".." in Title by omitting at end of "tcommon=...".
--19Oct2012 For pages, put   in "p. " etc.
--19Oct2012 Enhanced "pages=" to detect lone page as "p." else "pp." prefix.
--19Oct2012 Fixed to show "." after Periodical name (work, newspaper...).
--19Oct2012 Fixed web-link to have spaces "[...  Archived] from the original".
--19Oct2012 Fixed to show ";" between authors & coauthors.
--19Oct2012 Fixed to omit extra "." after coauthors.
--20Oct2012 Fixed COinS data to not urlencode all, as "ctx_ver=Z39.88-2004"
--20Oct2012 Fixed COinS to not end as "&" but use lead "&rft...=" form.
--20Oct2012 Fixed COinS to not url.encode page's "rfr_id=..." pagename.
--20Oct2012 Fixed COinS data when "web" to default to rft.genre "book".
--05Nov2012 Add a span wrapper even when there is no Ref parameter
--15Feb2013 Added Agency for "agency=xx".
--19Feb2013 Put NOTES comments to explain module operation.
--19Feb2013 Copied as Module:Citation/CS1 to alter to match wp:CS1 form.
--19Feb2013 Changed OrigYear to use [__] for CS1 style.
--19Feb2013 Fixed to not show duplicate Publisher/Agency.
--19Feb2013 Moved page-number parameters to after final date.
--19Feb2013 Fixed to not put double-dots after title again.
--20Feb2013 Changed to omit dot "." if already ends with dot.
--20Feb2013 If class "journal" shows Publisher after Periodical/Series.
--20Feb2013 Shifted Format to after Language, and Others after Volume.
--20Feb2013 Set AccessDate + <span class="reference-accessdate">
--20Feb2013 Fixed url when deadurl=no.
--20Feb2013 Added sepc for separator character between parameters.
--20Feb2013 Put "OCLC" for "Online Computer Library Center".
--20Feb2013 Fix empty "authorlink=" as person.link ~= "".
--20Feb2013 Added space after AuthorSep & AuthorNameSep.
--21Feb2013 Added args.contributor (was missing parameter).
--21Feb2013 Fixed EditorSep (was misspelled "EdithorSep").
--21Feb2013 Set OCinSdata.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"
--21Feb2013 Checked to omit blank codes (asin= | doi= etc.).
--21Feb2013 Set enddot to end line if not config.CitationClass "citation".
--21Feb2013 Fixed to show "issn=x" as the ISSN code.
--21Feb2013 Fixed to show "id=x" after Zbl code.
--21Feb2013 Changed to omit double-dot before date when already dot.
--21Feb2013 Order config.CitationClass "citation": Volume, Issue, Publisher.
--21Feb2013 Put warning "Bad DOI (expected "10."..)" in DOI result.
--21Feb2013 Automatically unbolded volume+comma when > 4 long.
--21Feb2013 Changed to allow lowercase "asin-tld".
--22Feb2013 Fixed ref=harv to extract Year from Date.
--22Feb2013 Set Harvard refer. span id if config.CitationClass "citation".
--22Feb2013 Fixed config.CitationClass "citation" as span class="citation".
--22Feb2013 Capitalized "Archived/Retrieved" only when sepc is dot ".".
--23Feb2013 Fixed author editor for "in" or "In" and put space after sepc.
--23Feb2013 Changed to omit dot in "et al." when sepc is "." separator.
--23Feb2013 Fixed "author1-first" to also get args.given or args.given1.
--23Feb2013 Fixed args.article to set Title, after Periodical is Title.
--23Feb2013 Fixed to allow blank Title (such as "contribution=mytitle").
--23Feb2013 Fixed double-dot ".." at end of Editors list
--26Feb2013 Moved "issue=" data to show before "page=".
--26Feb2013 Moved "type=" data to show after "format=".
--26Feb2013 For "pmc=" link, omitted suffix "/?tool=pmcentrez".
--27Feb2013 For coauthors, omitted extra separator after authors.
--27Feb2013 For date, allowed empty date to use month/day/year.
--27Feb2013 Fixed double-dot ".." at end of authors/coauthors list.
--27Feb2013 Reset editor suffix as ", ed." when date exists.
--27Feb2013 Removed duplicate display of "others=" data.
--27Feb2013 Removed parentheses "( )" around "department" TitleNote.
--05Mar2013 Moved Language to follow Periodical or Series.
--05Mar2013 Fixed Edition to follow Series or Volume.
--05Mar2013 Fixed class encyclopaedia to show article as quoted Chapter.
--05Mar2013 Fixed class encyclopaedia to show page as "pp." or "p.".
--07Mar2013 Changed class encyclopaedia to omit "( )" around publisher.
--07Mar2013 Fixed end double-dot by string.sub(idcommon,-1,-1) was "-1,1".
--13Mar2013 Removed enddot "." after "quote=" parameter.
--13Mar2013 Changed config.CitationClass "news" to use "p." page format.
--13Mar2013 Fixed missing "location=" when "web" or "encyclopaedia".
--14Mar2013 Fixed end double-dot after book/work title.
--14Mar2013 Fixed double-dot before "p." or "pp." page number.
--14Mar2013 Fixed config.CitationClass "book" to use p./pp. page.
--18Mar2013 Fixed "page=" to override "pages=" as in markup-based cites.
--19Mar2013 Fixed date of class=journal Periodical to show after page.
--19Mar2013 Changed null "postscript=" to suppress end-dot of citation.
--20Mar2013 If CitationClass is journal, show "others=" before title.
--20Mar2013 If CitationClass is book, show "others=" before edition.
--20Mar2013 If CitationClass is journal, adjust "others=" to have sepc.
--20Mar2013 For class "journal", use book format unless Periodical set.
--03Apr2013 Changed safejoin() to omit "." after wikilink ".]]" end dot.
--03Apr2013 Changed safejoin() to omit "." after external ".]" end dot.
--03Apr2013 Changed safejoin() to omit "." at italic wikilink ".]]" end.
--03Apr2013 Changed safejoin() to omit "." at italic external ".]" end.
--04Apr2013 Moved sepc before <span class="reference-accessdate"> for "..".
--
--End









