Module:Citation/CS1

From The Crowdsourced Resource-Based Economy Knowledgebase
Revision as of 10:50, 17 April 2013 by Ziggy (Talk | contribs)

Jump to: navigation, search

local z = {

   error_categories = {};
   error_ids = {};
   message_tail = {};

}

-- Include translation message hooks, ID and error handling configuration settings. local cfg = mw.loadData( 'Module:Citation/CS1/Configuration' );

-- Contains a list of all recognized parameters local whitelist = mw.loadData( 'Module:Citation/CS1/Whitelist' );

-- Checks that parameter name is valid function validate( name )

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

end

-- Formats a comment for error trapping function errorcomment( content, hidden )

   if hidden then 
       return '';
   else
       return '' .. content .. '';
   end        

end

--[[ Sets an error condition and returns the appropriate error message. The actual placement of the error message in the output is the responsibility of the calling function. ]] function seterror( error_id, args, raw, prefix, suffix )

   local error_state = cfg.error_conditions[ error_id ];
   prefix = prefix or "";
   suffix = suffix or "";
   if error_state == nil then
       error( cfg.message_list['undefined_error'] );
   end
   
   if error_state.category ~= nil and error_state.category ~= "" then
       table.insert( z.error_categories, error_state.category );
   end
   
   local message = error_state.message;
   if args ~= nil then
       for k, m in ipairs( args ) do
           m = m:gsub( "%%", "%%%%" );
           message = message:gsub( "$" .. k .. "(%D)", m .. "%1" );
       end
   end
   message = wikiescape(message) .. " ([[" .. cfg.message_list['help page link'] .. 
       "#" .. error_state.anchor .. "|" ..
       cfg.message_list['help page label'] .. "]])";
   z.error_ids[ error_id ] = true;
   if (error_id == 'bare_url_missing_title' or error_id == 'trans_missing_title')
           and z.error_ids['citation_missing_title'] then
       return , false;
   end
   
   message = prefix .. message .. suffix;
   
   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.label .. "" .. sep .. "[" .. 
           options.prefix .. url_string .. options.suffix .. " " .. mw.text.nowiki(options.id) .. "]"

end

-- Formats a wiki style internal link function internallinkid(options)

   local sep = options.separator or " "
   options.suffix = options.suffix or ""
   return "" .. options.label .. "" .. sep .. "[[" .. 
           options.prefix .. options.id .. options.suffix .. "|" .. mw.text.nowiki(options.id) .. "]]"

end

-- Format an external link with error checking function externallink( URL, label )

   local error_str = "";
   if label == nil or label == "" then
       label = URL;
       error_str = seterror( 'bare_url_missing_title', {}, false, " " );
   end
   if not checkurl( URL ) then
       error_str = seterror( 'bad_url', {}, false, " " ) .. error_str;
   end
   return "[" .. URL .. ' ' .. safeforurl( label ) .. "]" .. error_str;

end

-- Formats a link to Amazon function amazon(id, domain)

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

end

-- Formats a DOI and checks for DOI errors. function doi(id, inactive)

   local cat = ""
   local handler = cfg.id_handlers['DOI'];
   
   local text;
   if ( inactive ~= nil ) then 
       text = "" .. handler.label .. ":" .. id;
       table.insert( z.error_categories, "Pages with DOIs inactive since " .. selectyear(inactive) );        
       inactive = " (" .. cfg.message_list['inactive'] .. " " .. inactive .. ")" 
   else 
       text = externallinkid({link = handler.link, label = handler.label,
           prefix=handler.prefix,id=id,separator=handler.separator, encode=handler.encode})
       inactive = "" 
   end
   if ( string.sub(id,1,3) ~= "10." ) then      
       cat = seterror( 'bad_doi' );
   end
   return text .. inactive .. cat 

end

-- Formats an OpenLibrary link, and checks for associated errors. function openlibrary(id)

   local code = id:sub(-1,-1)
   local handler = cfg.id_handlers['OL'];
   if ( code == "A" ) then
       return externallinkid({link=handler.link, label=handler.label,
           prefix="http://openlibrary.org/authors/OL",id=id, separator=handler.separator,
           encode = handler.encode})
   elseif ( code == "M" ) then
       return externallinkid({link=handler.link, label=handler.label,
           prefix="http://openlibrary.org/books/OL",id=id, separator=handler.separator,
           encode = handler.encode})
   elseif ( code == "W" ) then
       return externallinkid({link=handler.link, label=handler.label,
           prefix= "http://openlibrary.org/works/OL",id=id, separator=handler.separator,
           encode = handler.encode})
   else
       return externallinkid({link=handler.link, label=handler.label,
           prefix= "http://openlibrary.org/OL",id=id, separator=handler.separator,
           encode = handler.encode}) .. 
           ' ' .. seterror( 'bad_ol' );
   end

end

--[[ Determines whether an URL string is valid

At present the only check is whether the string appears to be prefixed with a URI scheme. It is not determined whether the URI scheme is valid or whether the URL is otherwise well formed. ]] function checkurl( url_str )

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

end

-- Removes irrelevant text and dashes from ISBN number -- Similar to that used for Special:BookSources function cleanisbn( isbn_str )

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

end

-- Determines whether an ISBN string is valid function checkisbn( isbn_str )

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

   if len ~= 10 and len ~= 13 then
       return false;
   end

   local temp = 0;
   if len == 10 then
       if isbn_str:match( "^%d*X?$" ) == nil then return false; end
       isbn_str = { isbn_str:byte(1, len) };
       for i, v in ipairs( isbn_str ) do
           if v == string.byte( "X" ) then
               temp = temp + 10*( 11 - i );
           else
               temp = temp + tonumber( string.char(v) )*(11-i);
           end
       end
       return temp % 11 == 0;
   else
       if isbn_str:match( "^%d*$" ) == nil then return false; end
       isbn_str = { isbn_str:byte(1, len) };
       for i, v in ipairs( isbn_str ) do
           temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) );
       end
       return temp % 10 == 0;
   end

end

-- Gets the display text for a wikilink like B or B gives B function removewikilink( str )

   str = str:gsub( "%[%%*|([^%]]*)%]%]", "%1" );
   str = str:gsub( "%[%[([^%]]*)%]%]", "%1" );    
   return str

end

-- Escape sequences for content that will be used for URL descriptions function safeforurl( str )

   if str:match( "%[%[.-%]%]" ) ~= nil then 
       table.insert( z.message_tail, { seterror( 'wikilink_in_url', {}, true ) } );
   end
   
   return str:gsub( '[%[%]\n]', {    
       ['['] = '[',	
       [']'] = ']',	
       ['\n'] = ' ' } );

end

-- Converts a hyphen to a dash function hyphentodash( str )

   if str == nil then
       return nil;
   end    
   if str:match( "[%[%]{}<>]" ) ~= nil then 
       return str;
   end    
   return str:gsub( '-', '–' );

end

-- Protects a string that will be wrapped in wiki italic markup ... function safeforitalics( str )

   --[[ Note: We can not use  for italics, as the expected behavior for
   italics specified by ... in the title is that they will be inverted
   (i.e. unitalicized) in the resulting references.  In addition, <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:gsub( '\n', ' ' );
   end

end

--[[ Joins a sequence of strings together while checking for duplicate separation characters. ]] function safejoin( tbl, duplicate_char )

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

end

--[[ Return the year portion of a date string, if possible. Returns empty string if the argument can not be interpreted as a year. ]] function selectyear( str )

   -- Is the input a simple number?
   local num = tonumber( str ); 
   if num ~= nil and num > 0 and num < 2100 and num == math.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 = "" .. one .. "" end
           end
           table.insert(text, one)
       end
   end
   local count = #text;
   if count > 1 and lastauthoramp ~= nil and lastauthoramp ~= "" and not etal then
       text[count-1] = text[count-1] .. " & " .. text[count];
       text[count] = nil;
   end    
   local result = table.concat(text, sep) -- construct list
   if etal then 
       local etal_text = cfg.message_list['et al'];
       result = result .. " " .. etal_text;
   end
   
   -- if necessary wrap result in  tag to format in Small Caps
   if ( "scap" == format ) then result = 
       '' .. result .. '';
   end 
   return result, count

end

-- Generates a CITEREF anchor ID. function anchorid( options )

   return "CITEREF" .. mw.uri.anchorEncode( table.concat( options ) );

end

-- Gets author list from the input arguments function extractauthors(args)

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

end

-- Gets editor list from the input arguments function extracteditors(args)

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

end

-- Populates ID table from arguments using configuration settings function extractids( args )

   local id_list = {};
   
   for k, v in pairs( cfg.id_handlers ) do    
       id_list[k] = selectone( args, v.parameters, 'redundant_parameters' );
   end
   return id_list;

end

-- Takes a table of IDs and turns it into a table of formatted ID outputs. function buildidlist( id_list, options )

   local handler;
   local new_list = {};
   
   for k, v in pairs( id_list ) do
       handler = {};
       
       --Becasue cfg is read-only we have to copy it the hard way.
       for k2, v2 in pairs( cfg.id_handlers[k] ) do
           handler[k2] = v2;
       end
       handler['id'] = v;
       
       if handler.mode == 'external' then        
           table.insert( new_list, {handler.label, externallinkid( handler ) } );
       elseif handler.mode == 'internal' then
           table.insert( new_list, {handler.label, internallinkid( handler ) } );
       elseif handler.mode == 'manual' then
           if k == 'DOI' then
               table.insert( new_list, {handler.label, doi( v, options.DoiBroken ) } );
           elseif k == 'ASIN' then
               table.insert( new_list, {handler.label, amazon( v, options.ASINTLD ) } ); 
           elseif k == 'OL' then
               table.insert( new_list, {handler.label, openlibrary( v ) } );
           elseif k == 'ISBN' then
               local ISBN = internallinkid( handler );
               if not checkisbn( v ) and ( options.IgnoreISBN == nil or options.IgnoreISBN == "" ) then 
                   ISBN = ISBN .. seterror( 'bad_isbn', {}, false, " ", "" );
               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 .. "|" .. k .. "=";
       end
       if #error_list > 1 then
           error_str = error_str .. ", and ";
       else
           error_str = error_str .. " and ";
       end
       error_str = error_str .. "|" .. selected .. "=";
       table.insert( z.message_tail, { seterror( error_condition, {error_str}, true ) } );
   end
           
   return value, selected;

end

--[[ This is the main function foing the majority of the citation formatting. ]] function citation0( config, args)

   -- Load Input Parameters
   local i 
   local PPrefix = config.PPrefix or "p. "
   local PPPrefix = config.PPPrefix or "pp. "
   if ( nil ~= args.nopp ) then PPPrefix = "" PPrefix = "" end
   
   -- Pick out the relevant fields from the arguments.  Different citation templates
   -- define different field names for the same underlying things.    
   local Authors = args.authors
   local a = extractauthors( args );
   local Coauthors = selectone( args, {'coauthors', 'coauthor' }, 'redundant_parameters' );
   local Others = args.others 
   local Editors = args.editors
   local e = extracteditors( args );
   local Year = args.year 
   local PublicationDate = selectone( args, {'publicationdate', 'publication-date' }, 'redundant_parameters' );
   local OrigYear = args.origyear
   local Date = args.date
   local LayDate = args.laydate
   ------------------------------------------------- Get title data
   local Title = args.title or args.encyclopaedia or args.encyclopedia or args.dictionary
   local BookTitle = args.booktitle
   local Conference = args.conference
   local TransTitle = selectone( args, {'trans-title', 'trans_title' }, 'redundant_parameters' );
   local TitleNote = args.department
   local TitleLink = selectone( args, {'titlelink', 'episodelink' }, 'redundant_parameters' );
   local Chapter = selectone( args, {'chapter', 'contribution', 'entry' }, 'redundant_parameters' );
   local ChapterLink = args.chapterlink
   local TransChapter = selectone( args, {'trans-chapter', 'trans_chapter' }, 'redundant_parameters' );
   local TitleType = args.type
   local ArchiveURL = selectone( args, {'archive-url', 'archiveurl' }, 'redundant_parameters' );
   local URL = selectone( args, {'url', 'URL'}, 'redundant_parameters' );
   local ChapterURL = selectone( args, {'chapter-url', 'chapterurl', 'contribution-url', 'contributionurl' }, 'redundant_parameters' );
   local ConferenceURL = selectone( args, {'conference-url', 'conferenceurl' }, 'redundant_parameters' );
   local Periodical = selectone( args, {'journal', 'newspaper', 'magazine', 'work', 'website', 
       'periodical', 'encyclopedia', 'encyclopaedia'}, 'redundant_parameters' );
           
   if ( config.CitationClass == "encyclopaedia" ) then
       if ( args.article and args.article ~= "") then
           if ( Title and Title ~= "") then Periodical = Title end
           Chapter = args.article
           TransChapter = TransTitle
           Title = nil          
           TransTitle = nil
       elseif ( Chapter == nil or Chapter ==  ) then
           if Title ~= args.encyclopedia then 
               Chapter = Title
               TransChapter = TransTitle
               Title = nil 
               TransTitle = nil
           end
       end
       if ( Periodical and Periodical ~= "") then
           if Periodical == Title or Periodical == Chapter then Periodical = nil end
       end
   end
   local Series = selectone( args, {'series', 'version'}, 'redundant_parameters' );
   local Volume = args.volume
   local Issue = selectone( args, {'issue', 'number'}, 'redundant_parameters' );
   local Position = nil
   local Page, Pages, At, page_type;
   
   Page, page_type = selectone( args, {'p', 'page', 'pp', 'pages', 'at'}, 
       'extra_pages' );
   if page_type == 'pp' or page_type == 'pages' then
       Pages = hyphentodash( Page );
       Page = nil;
   elseif page_type == 'at' then
       At = Page;
       Page = nil;
   end
               
   local Edition = args.edition
   local PublicationPlace = selectone( args, {'publication-place', 'publicationplace' }, 'redundant_parameters' ); 
   local Place = selectone( args, {'place', 'location'}, 'redundant_parameters' );
   if PublicationPlace == nil and Place ~= nil then 
       PublicationPlace = Place;
   end
   if PublicationPlace == Place then Place = nil end
   
   local PublisherName = args.publisher
   local SubscriptionRequired = args.subscription
   local Via = args.via
   local AccessDate = selectone( args, {'access-date', 'accessdate' }, 'redundant_parameters' );
   local ArchiveDate = selectone( args, {'archive-date', 'archivedate' }, 'redundant_parameters' );
   local Agency = args.agency
   local DeadURL = args.deadurl or "yes"           -- Only used if ArchiveURL is present.
   local Language = selectone( args, {'language', 'in'}, 'redundant_parameters' );
   local Format = args.format
   local Ref = selectone( args, {'ref', 'Ref'}, 'redundant_parameters' );
   local DoiBroken = selectone( args, {'doi_inactivedate', 'doi_brokendate', 'DoiBroken'}, 'redundant_parameters' );
   local ID = selectone( args, {'id', 'ID', 'docket'}, 'redundant_parameters' );
   local ASINTLD = selectone( args, {'ASIN-TLD', 'asin-tld'}, 'redundant_parameters' );
   local IgnoreISBN = selectone( args, {'ignore-isbn-error', 'ignoreisbnerror'}, 'redundant_parameters' );
   local ID_list = extractids( args );
   
   local Quote = selectone( args, {'quote', 'quotation'}, 'redundant_parameters' );
   local PostScript = args.postscript or "."
   local LaySummary = args.laysummary
   local LaySource = args.laysource
   local Transcript = args.transcript
   local TranscriptURL = selectone( args, {'transcript-url', 'transcripturl'}, 'redundant_parameters' );
   local sepc = args.separator or "."
   local LastAuthorAmp = args.lastauthoramp
   local no_tracking_cats = selectone( args, {"template doc demo", 'nocat', 
       'notracking', "no-tracking"}, 'redundant_parameters' ) or "";
   if ( config.CitationClass == "journal" ) then        
       if (URL == nil or URL == "") then
           if (ID_list['PMC'] ~= nil) then 
               local Embargo = args.embargo or args.Embargo;
               if Embargo ~= nil then
                   local lang = mw.getContentLanguage();
                   local good1, result1, good2, result2;
                   good1, result1 = pcall( lang.formatDate, lang, 'U', Embargo );
                   good2, result2 = pcall( lang.formatDate, lang, 'U' );
                   if good1 and good2 and tonumber( result1 ) < tonumber( result2 ) then 
                       URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC'];
                   end
               else
                   URL = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" .. ID_list['PMC'];           
               end
           end
       end
   end
   -- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.
   
   -- Account for the oddity that is Template: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 Template: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 k == 'ISBN' then
           v = cleanisbn( v );
       end
       if string.sub( cfg.id_handlers[k].COinS or "info", 1, 4 ) ~= 'info' then
           OCinSdata[ cfg.id_handlers[k].COinS ] = v;
       end
   end
   
   OCinSdata.rft_id = URL or ChapterURL
   local last, first;
   local OCinSauthors = {};
   for k, v in ipairs( a ) do
       last = v.last;
       first = v.first;
       if k == 1 then
           if last ~= nil then
               OCinSdata["rft.aulast"] = last;
           end
           if first ~= nil then 
               OCinSdata["rft.aufirst"] = first;
           end
       end
       if last ~= nil and first ~= nil then
           table.insert( OCinSauthors, last .. (args.NameSep or ", ") .. first );
   	elseif last ~= nil then
           table.insert( OCinSauthors, last );
       end
   end
   local OCinSids = {} -- COinS data only for id, bibcode, doi, pmid, etc.
   for k, v in pairs( ID_list ) do
       if string.sub( cfg.id_handlers[k].COinS or "", 1, 4 ) == 'info' then
           OCinSids[ cfg.id_handlers[k].COinS ] = v;
       end
   end
   local OCinStitle = "ctx_ver=" .. ctx_ver  -- such as "Z39.88-2004"
   for name,value in pairs(OCinSdata) do
       OCinStitle = OCinStitle .. "&" .. name .. "=" .. mw.uri.encode( removewikilink(value) );
   end
   for _, value in ipairs(OCinSauthors) do
       OCinStitle = OCinStitle .. "&rft.au=" .. mw.uri.encode( removewikilink(value) );
   end
   for name,value in pairs(OCinSids) do
       OCinStitle = OCinStitle .. "&rft_id=" .. mw.uri.encode(name .. "/" .. removewikilink(value) );
   end
   
   local this_page = mw.title.getCurrentTitle();
   OCinStitle = OCinStitle .. "&rfr_id=info:sid/" .. mw.site.server:match( "[^/]*$" ) .. ":"
      .. this_page.prefixedText  -- end COinS data by page's non-encoded pagename
   if (Periodical ~= nil and Periodical ~= "") and
       (Chapter == nil or Chapter == ) and
       (Title ~= nil and Title ~= "") then
           Chapter = Title
           ChapterLink = TitleLink
           TransChapter = TransTitle
           Title = nil
           TitleLink = nil
           TransTitle = nil            
   end
   -- Now perform various field substitutions.
   -- We also add leading spaces and surrounding markup and punctuation to the
   -- various parts of the citation, but only when they are non-nil.
   if ( Authors == nil ) then 
       local Maximum = tonumber( (selectone( args, {"display-authors", "displayauthors"}, 'redundant_parameters' )) );
       
       -- Preserve old-style implicit et al.
       if Maximum == nil and #a == 9 then 
           Maximum = 8;
           table.insert( z.message_tail, { seterror('implict_etal_author', {}, true ) } );
       elseif Maximum == nil then
           Maximum = #a + 1;
       end
           
       local control = { 
           sep = (args["author-separator"] or ";") .. " ",
           namesep = (args["author-name-separator"] or args["name-separator"] or ",") .. " ",
           format = selectone( args, {"author-format", "authorformat" }, 'redundant_parameters' ),
           maximum = Maximum,
           lastauthoramp = LastAuthorAmp
       }
       
       -- If the coauthor field is also used, prevent ampersand and et al. formatting.
       if Coauthors ~= nil and Coauthors ~= "" then
           control.lastauthoramp = nil;
           control.maximum = #a + 1;
       end
               
       Authors = listpeople(control, a) 
   end
   local EditorCount
   if ( Editors == nil ) then 
       local Maximum = tonumber( (selectone( args, {"display-editors", "displayeditors"}, 'redundant_parameters' )) );
       -- Preserve old-style implicit et al.
       if Maximum == nil and #e == 4 then 
           Maximum = 3;
           table.insert( z.message_tail, { seterror('implict_etal_editor', {}, true) } );
       elseif Maximum == nil then
           Maximum = #e + 1;
       end
       local control = { 
           sep = (args["editor-separator"] or ";") .. " ",
           namesep = (args["editor-name-separator"] or args["name-separator"] or ",") .. " ",
           format = selectone( args, {"editor-format", "editorformat" }, 'redundant_parameters' ),
           maximum = Maximum,
           lastauthoramp = LastAuthorAmp
           }
       Editors, EditorCount = listpeople(control, e) 
   else
       EditorCount = 1;
   end
   if ( Date == nil or Date == "") then

-- there's something hinky with how this adds dashes to perfectly-good free-standing years --[[ Date = Year

       if ( Date ~= nil ) then
           local Month = args.month
           if ( Month == nil ) then 
               local Began = args.began
               local Ended = args.ended
               if Began ~= nil and Ended ~= nil then
                   Month = Began .. "–" .. Ended
               else
                   Month = "–"
               end
           end
           Date = Month .. " " .. Date
           local Day = args.day
           if ( Day ~= nil ) then Date = Day .. " " .. Date end
       end

]] -- so let's use the original version for now

       Date = Year
       if ( Date ~= nil and Date ~="") then
           local Month = args.month
           if ( Month ~= nil and Month ~= "") then 
               Date = Month .. " " .. Date 
               local Day = args.day
               if ( Day ~= nil ) then Date = Day .. " " .. Date end
               else Month = ""
           end
           else Date = ""
       end
   end
   if ( PublicationDate == Date or PublicationDate == Year ) then PublicationDate = nil end
   if( (Date == nil or Date == "") and PublicationDate ~= nil ) then 
       Date = PublicationDate;
       PublicationDate = nil;
   end    
   -- Captures the value for Date prior to adding parens or other textual transformations
   local DateIn = Date
   
   if ( URL == nil or URL ==  ) and
           ( ChapterURL == nil or ChapterURL ==  ) and
           ( ArchiveURL == nil or ArchiveURL ==  ) and                
           ( ConferenceURL == nil or ConferenceURL ==  ) and                
           ( TranscriptURL == nil or TranscriptURL ==  ) then
       -- Test if cite web is called without giving a URL
       if ( config.CitationClass == "web" ) then
           table.insert( z.message_tail, { seterror( 'cite_web_url', {}, true ) } );
       end
       -- Test if accessdate is given without giving a URL
       if ( AccessDate ~= nil and AccessDate ~=  ) then
           table.insert( z.message_tail, { seterror( 'accessdate_missing_url', {}, true ) } );
           AccessDate = nil;
       end      
   
       -- Test if format is given without giving a URL
       if ( Format ~= nil and Format ~=  ) then
           Format = Format .. seterror( 'format_missing_url' );
       end        
   end    
   -- Test if citation has no title
   if ( Chapter == nil or Chapter == "" ) and 
           ( Title == nil or Title == "" ) and
           ( Periodical == nil or Periodical == "" ) and
           ( Conference == nil or Conference == "" ) and 
           ( TransTitle == nil or TransTitle == "" ) and
           ( TransChapter == nil or TransChapter == "" ) then
       table.insert( z.message_tail, { seterror( 'citation_missing_title', {}, true ) } );
   end
   if ( Format ~= nil and Format ~="" ) then
       Format = " (" .. Format .. ")" else Format = "" end
   
   local OriginalURL = URL
   DeadURL = DeadURL:lower();
   if ( ArchiveURL and "" < ArchiveURL ) then
       if ( DeadURL ~= "no" ) then
           URL = ArchiveURL
       end
   end
   if ( TransTitle and "" < TransTitle ) then TransTitle = " [" .. TransTitle .. "]" 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 = "" .. Chapter .. "" end
       if ( Periodical and "" < Periodical ) and (Title ~= nil and Title ~= "" )
       then
           Chapter = "" .. safeforitalics(Chapter) .. ""
       else
           Chapter = "\"" .. Chapter .. "\""
       end
   else
       Chapter = "";
   end
   
   local TransError = ""
   if TransChapter ~= "" and Chapter == "" then
       TransError = " " .. seterror( 'trans_missing_chapter' );
   end
   Chapter = Chapter .. TransChapter
   if Chapter ~= "" then
       if ( ChapterLink == nil ) then
           if ( ChapterURL and "" < ChapterURL ) then                
               Chapter = externallink( ChapterURL, Chapter ) .. TransError;
               if URL == nil or URL == "" then
                   Chapter = Chapter .. Format;
                   Format = "";
               end
           elseif ( URL and "" < URL ) then 
               Chapter = externallink( URL, Chapter ) .. TransError .. Format;
               URL = nil
               Format = ""
           else
               Chapter = Chapter .. TransError;
           end            
       elseif ChapterURL ~= nil and ChapterURL ~= "" then
           Chapter = Chapter .. " " .. externallink( ChapterURL ) .. 
               TransError;
       else
           Chapter = Chapter .. TransError;
       end
       Chapter = Chapter .. sepc .. " " -- with end-space
   elseif ChapterURL ~= nil and ChapterURL ~= "" then
       Chapter = " " .. externallink( ChapterURL ) .. sepc .. " ";
   end        
   
   -- Format main title.
   if ( Title and "" < Title ) then
       if ( TitleLink and "" < TitleLink ) then
           Title = "" .. Title .. "" end
       if ( Periodical and "" < Periodical ) then
           Title = "\"" .. Title .. "\""
       elseif ( config.CitationClass == "web"
               or config.CitationClass == "news" 
               or config.CitationClass == "pressrelease" ) and 
               Chapter == "" then
           Title = "\"" .. Title .. "\""
       else
           Title = "" .. safeforitalics(Title) .. ""
       end
   else
       Title = "";
   end    
   
   local TransError = "";
   if TransTitle ~= "" and Title == "" then
       TransError = " " .. seterror( 'trans_missing_title' );
   end
   Title = Title .. TransTitle
   if Title ~= "" then
       if ( TitleLink == nil and URL and "" < URL ) then 
           Title = externallink( URL, Title ) .. TransError .. Format       
           URL = nil
           Format = 
       else
           Title = Title .. TransError;
       end
   end
   if ( Place ~= nil and Place ~= "" ) then
       if sepc == '.' then
           Place = " " .. cfg.message_list['written'] .. " " .. Place .. sepc .. " ";
       else
           Place = " " .. cfg.message_list['written']:lower() .. " " .. Place .. sepc .. " ";
       end            
   else
       Place = "";
   end
   
   if ( Conference ~= nil and Conference ~="" ) then
       if ( ConferenceURL ~= nil ) then
           Conference = externallink( ConferenceURL, Conference );
       end
       Conference = " " .. Conference
   elseif ConferenceURL ~= nil and ConferenceURL ~= "" then
       Conference = " " .. externallink( ConferenceURL );
   else
       Conference = "" 
   end
   if ( nil ~= Position or nil ~= Page or nil ~= Pages ) then At = nil end
   if ( nil == Position and "" ~= Position ) then
       local Minutes = args.minutes
       if ( nil ~= Minutes ) then
           Position = " " .. Minutes .. " " .. cfg.message_list['minutes'];
       else
           local Time = args.time
           if ( nil ~= Time ) then
               local TimeCaption = args.timecaption 
               if TimeCaption == nil then
                   TimeCaption = cfg.message_list['event'];
                   if sepc ~= '.' then
                       TimeCaption = TimeCaption:lower();
                   end
               end                
               Position = " " .. TimeCaption .. " " .. Time
           else
               Position = ""
           end
       end
   else
       Position = " " .. Position
   end
   if ( nil == Page or "" == Page ) then 
       Page = "" 
       if ( nil == Pages or "" == Pages) then 
           Pages = ""
       elseif ( Periodical ~= nil and Periodical ~= "" and
                config.CitationClass ~= "encyclopaedia" and
                config.CitationClass ~= "web" and
                config.CitationClass ~= "book" and
                config.CitationClass ~= "news") then
           Pages = ": " .. Pages
       else
           if ( tonumber(Pages) ~= nil ) then
             Pages = sepc .." " .. PPrefix .. Pages
           else Pages = sepc .." " .. PPPrefix .. Pages
           end
       end
   else
       Pages = ""
       if ( Periodical ~= nil and Periodical ~= "" and
            config.CitationClass ~= "encyclopaedia" and
            config.CitationClass ~= "web" and
            config.CitationClass ~= "book" and
            config.CitationClass ~= "news") then
           Page = ": " .. Page
       else
           Page = sepc .." " .. PPrefix .. Page
       end
   end
   if ( At ~= nil and At ~="") then At = sepc .. " " .. At
   else At = "" end
   if ( Coauthors == nil ) then Coauthors = "" end
   if ( Others ~= nil and Others ~="" ) then
       Others = sepc .. " " .. Others else Others = "" end
   if ( TitleType ~= nil and TitleType ~="" ) then
       TitleType = " (" .. TitleType .. ")" else TitleType = "" end
   if ( TitleNote ~= nil and TitleNote ~="" ) then
       TitleNote = sepc .. " " .. TitleNote else TitleNote = "" end
   if ( Language ~= nil and Language ~="" ) then
       Language = " (" .. cfg.message_list['in'] .. " " .. Language .. ")" else Language = "" end
   if ( Edition ~= nil and Edition ~="" ) then
       Edition = " (" .. Edition .. " " .. cfg.message_list['edition'] .. ")" else Edition = "" end
   if ( Volume ~= nil and Volume ~="" )
   then
       if ( mw.ustring.len(Volume) > 4 )
         then Volume = sepc .." " .. Volume
         else Volume = " " .. hyphentodash(Volume) .. ""
       end
   else Volume = "" end
   if ( Issue ~= nil and Issue ~="" ) then
       Issue = " (" .. Issue .. ")" else Issue = "" end
   if ( Series ~= nil and Series ~="" ) then
       Series = sepc .. " " .. Series else Series = "" end
   if ( OrigYear ~= nil and OrigYear ~="" ) then
       OrigYear = " [" .. OrigYear .. "]" else OrigYear = "" end
   if ( Agency ~= nil and Agency ~="" ) then
       Agency = sepc .. " " .. Agency else Agency = "" end
   ------------------------------------ totally unrelated data
   if ( Date ~= nil ) then Date = Date else Date = "" end
   if ( Via ~= nil and Via ~="" ) then
       Via = " — " .. cfg.message_list['via'] .. " " .. Via else Via = "" end
   if ( AccessDate ~= nil and AccessDate ~="" )
   then local retrv_text = " " .. cfg.message_list['retrieved'] .. " "
        if (sepc ~= ".") then retrv_text = retrv_text:lower() end
        AccessDate = '' .. sepc
            .. retrv_text .. AccessDate .. ''
   else AccessDate = "" end
   if ( SubscriptionRequired ~= nil and
        SubscriptionRequired ~= "" ) then
       SubscriptionRequired = sepc .. " " .. cfg.message_list['subscription'];
   else
       SubscriptionRequired = ""
   end
   if ( ID ~= nil and ID ~="") then ID = sepc .." ".. ID else ID="" end
   ID_list = buildidlist( ID_list, {DoiBroken = DoiBroken, ASINTLD = ASINTLD, IgnoreISBN = IgnoreISBN} );
   if ( URL ~= nil and URL ~="") then
       URL = " " .. externallink( URL, URL );
       local error_text = seterror( 'bare_url_missing_title' );
       if config.CitationClass == "web" then
           URL = URL .. " " .. seterror( 'cite_web_title' );
       else
           URL = URL .. error_text;
       end       
   else
       URL = ""
   end
   if ( Quote and Quote ~="" ) then 
       if Quote:sub(1,1) == '"' and Quote:sub(-1,-1) == '"' then
           Quote = Quote:sub(2,-2);
       end
       
       Quote = sepc .." \"" .. Quote .. "\"" 
       PostScript = ""
   else 
       if ( PostScript == nil) then PostScript = "" end
       Quote = "" 
   end
   
   local Archived
   if ( nil ~= ArchiveURL and "" ~= ArchiveURL ) then
       if ( ArchiveDate ~= nil and ArchiveDate ~="" ) then
           ArchiveDate = " " .. ArchiveDate
       else 
           ArchiveDate = " " .. seterror('archive_missing_date') .. " "
       end
       local arch_text = cfg.message_list['archived'];
       if (sepc ~= ".") then arch_text = arch_text:lower() end
       if ( "no" == DeadURL ) then
           Archived = sepc .. " " .. externallink( ArchiveURL, arch_text ) .. " " .. 
               cfg.message_list['from'] .. " " .. cfg.message_list['original'] .. " " .. 
               cfg.message_list['on'] .. ArchiveDate
           if OriginalURL == nil or OriginalUrl ==  then
               Archived = Archived .. " " .. seterror('archive_missing_url_not_dead');                               
           end
       else
           if OriginalURL ~= nil and OriginalURL ~=  then
               Archived = sepc .. " " .. arch_text .. " " .. cfg.message_list['from'] .. 
                   " " .. externallink( OriginalURL, cfg.message_list['original'] ) .. " "
                   .. cfg.message_list['on'] .. ArchiveDate
           else
               if config.CitationClass ~= 'web' then 
                   Archived = sepc .. " " .. arch_text .. " " .. cfg.message_list['from'] .. " " .. 
                   cfg.message_list['original'] .. seterror('archive_missing_url') .. " " .. cfg.message_list['on'] .. ArchiveDate
               else
                   Archived = sepc .. " " .. arch_text .. " " .. cfg.message_list['from'] .. " " .. 
                   cfg.message_list['original'] .. seterror('archive_missing_url_web') .. " " .. cfg.message_list['on'] .. ArchiveDate
               end
           end                
       end
   else
       Archived = ""
   end
   local Lay
   if ( nil ~= LaySummary and "" ~= LaySummary ) then
       if ( LayDate ~= nil ) then LayDate = " (" .. LayDate .. ")" else LayDate = "" end
       if ( LaySource ~= nil ) then 
           LaySource = " – " .. safeforitalics(LaySource) .. "" 
       else 
           LaySource = "" 
       end
       if sepc == '.' then
           Lay = sepc .. " [" .. LaySummary .. " " .. cfg.message_list['lay summary'] .. "]" .. LaySource .. LayDate
       else
           Lay = sepc .. " [" .. LaySummary .. " " .. cfg.message_list['lay summary']:lower() .. "]" .. LaySource .. LayDate
       end            
   else
       Lay = ""
   end
   if ( nil ~= Transcript and "" ~= Transcript ) then
       if ( TranscriptURL ~= nil ) then Transcript = externallink( TranscriptURL, Transcript ) end
   elseif TranscriptURL ~= nil and TranscriptURL ~= "" then
       Transcript = externallink( TranscriptURL )     
   else
       Transcript = ""
   end
   local Publisher = ""
   if ( Periodical and Periodical ~= "" and
        config.CitationClass ~= "encyclopaedia" and
        config.CitationClass ~= "web" and
        config.CitationClass ~= "pressrelease" ) then
       if ( PublisherName ~= nil and PublisherName ~="" ) then
           if (PublicationPlace ~= nil and PublicationPlace ~= ) then
               Publisher = PublicationPlace .. ": " .. PublisherName;
           else
               Publisher = PublisherName;  
           end            
       elseif (PublicationPlace ~= nil and PublicationPlace ~= ) then 
           Publisher= PublicationPlace;
       else 
           Publisher = "";
       end
       if ( PublicationDate and PublicationDate ~="" ) then
           if Publisher ~=  then
               Publisher = Publisher .. ", " .. cfg.message_list['published'] .. " " .. PublicationDate;
           else
               Publisher = PublicationDate;
           end
       end
       if Publisher ~= "" then
           Publisher = " (" .. Publisher .. ")";
       end
   else
       if ( PublicationDate and PublicationDate ~="" ) then
           PublicationDate = " (" .. cfg.message_list['published'] .. " " .. PublicationDate .. ")"
       else 
           PublicationDate = ""
       end
       if ( PublisherName ~= nil and PublisherName ~="" ) then
           if (PublicationPlace ~= nil and PublicationPlace ~= ) then
               Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
           else
               Publisher = sepc .. " " .. PublisherName .. PublicationDate;  
           end            
       elseif (PublicationPlace ~= nil and PublicationPlace ~= ) then 
           Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
       else 
           Publisher = PublicationDate;
       end
   end
   -- Several of the above rely upon detecting this as nil, so do it last.
   if ( Periodical ~= nil and Periodical ~="" ) then 
       if ( Title and Title ~= "" ) or ( TitleNote and TitleNote ~= "" ) then 
           Periodical = sepc .. " " .. safeforitalics(Periodical) .. ""
       else 
           Periodical = "" .. safeforitalics(Periodical) .. ""
       end
   else Periodical = "" end
   -- Piece all bits together at last.  Here, all should be non-nil.
   -- We build things this way because it is more efficient in LUA
   -- not to keep reassigning to the same string variable over and over.
   local tcommon
   if ( ( (config.CitationClass == "journal") or (config.CitationClass == "citation") )  and
        Periodical ~= "" ) then
       if (Others ~= "") then Others = Others .. sepc .. " " end
       tcommon = safejoin( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, 
           Language, Edition, Publisher, Agency, Volume, Issue, Position}, sepc );
   else 
       tcommon = safejoin( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, 
           Volume, Issue, Others, Edition, Publisher, Agency, Position}, sepc );
   end
   
   if #ID_list > 0 then
       ID_list = safejoin( { sepc .. " ",  table.concat( ID_list, sepc .. " " ), ID }, sepc );
   else
       ID_list = ID;
   end    
   local idcommon = safejoin( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc );
   local text
   local pgtext = Page .. Pages .. At
   
   if ( "" ~= Authors ) then
       if (Coauthors ~= "") 
         then Authors = Authors .. "; " .. Coauthors
       end
       if ( "" ~= Date )
         then Date = " ("..Date..")" .. OrigYear .. sepc .. " "
         else
           if ( string.sub(Authors,-1,-1) == sepc) --check end character
             then Authors = Authors .. " "
             else Authors = Authors .. sepc .. " "
           end
       end
       if ( "" ~= Editors) then
           local in_text = " in "
           if (sepc == '.') then in_text = " In " end
           if (string.sub(Editors,-1,-1) == sepc)
               then Editors = in_text .. Editors .. " "
               else Editors = in_text .. Editors .. sepc .. " "
           end
       end
       text = safejoin( {Authors, Date, Chapter, Place, Editors, tcommon }, sepc );
       text = safejoin( {text, pgtext, idcommon}, sepc );
   elseif ( "" ~= Editors) then
       if ( "" ~= Date ) then
           if EditorCount <= 1 then
               Editors = Editors .. ", " .. cfg.message_list['editor'];
           else
               Editors = Editors .. ", " .. cfg.message_list['editors'];
           end
           Date = " (" .. Date ..")" .. OrigYear .. sepc .. " "
       else
           if EditorCount <= 1 then
               Editors = Editors .. " (" .. cfg.message_list['editor'] .. ")" .. sepc .. " "
           else
               Editors = Editors .. " (" .. cfg.message_list['editors'] .. ")" .. sepc .. " "
           end
       end
       text = safejoin( {Editors, Date, Chapter, Place, tcommon}, sepc );
       text = safejoin( {text, pgtext, idcommon}, sepc );
   else
       if ( "" ~= Date ) then
           if ( string.sub(tcommon,-1,-1) ~= sepc )
             then Date = sepc .." " .. Date .. OrigYear
             else Date = " " .. Date .. OrigYear
           end
       end -- endif ""~=Date
       if ( config.CitationClass=="journal" and Periodical ) then
         text = safejoin( {Chapter, Place, tcommon}, sepc );
         text = safejoin( {text, pgtext, Date, idcommon}, sepc );
       else
         text = safejoin( {Chapter, Place, tcommon, Date}, sepc );
         text = safejoin( {text, pgtext, idcommon}, sepc );
       end
   end
   
   if PostScript ~=  and PostScript ~= nil and PostScript ~= sepc then
       text = safejoin( {text, sepc}, sepc );  --Deals with italics, spaces, etc.
       text = text:sub(1,-2); --Remove final seperator    
   end    
   
   text = safejoin( {text, PostScript}, sepc );
   -- Now enclose the whole thing in a <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 
                   if i == 4 then break end
               end
           elseif ( "" ~= Editors ) then
               for i,v in ipairs(e) do 
                   names[i] = v.last 
                   if i == 4 then break end                
               end
           end
           names[ #names + 1 ] = Year;
           id = anchorid(names)
       end
       options.id = id;
   end
   
   if string.len(text:gsub("/]*>.-", ""):gsub("%b<>","")) <= 2 then
       z.error_categories = {};
       text = seterror('empty_citation');
       z.message_tail = {};
   end
   
   if options.id ~= nil then 
       text = '' .. text .. "";
   else
       text = '' .. text .. "";
   end        
   local empty_span = ' ';
   
   -- Note: Using display: none on then COinS span breaks some clients.
   local OCinS = '' .. empty_span .. '';
   text = text .. OCinS;
   
   if #z.message_tail ~= 0 then
       for i,v in ipairs( z.message_tail ) do
           if v[1] ~= nil and v[1] ~= "" then 
               if i == #z.message_tail then
                   text = text .. errorcomment( v[1], v[2] );
               else
                   text = text .. errorcomment( v[1] .. "; ", v[2] );
               end
           end
       end
   end
   
   if no_tracking_cats ==  then
       for _, v in ipairs( z.error_categories ) do
           text = text .. ;
       end
   end
   
   return text

end

-- This is used by templates such as .  to create the actual citation text. function z.citation(frame)

   local pframe = frame:getParent()
   
   local args = {};
   local suggestions = {};
   local error_text, error_state;
   for k, v in pairs( pframe.args ) do
       if v ~=  then
           if not validate( k ) then            
               error_text = "";
               if type( k ) ~= 'string' then
                   -- Exclude empty numbered parameters
                   if v:match("%S+") ~= nil then
                       error_text, error_state = seterror( 'text_ignored', {v}, true );
                   end
               elseif validate( k:lower() ) then 
                   error_text, error_state = seterror( 'parameter_ignored_suggest', {k, k:lower()}, true );
               else
                   if #suggestions == 0 then
                       suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' );
                   end
                   if suggestions[ k:lower() ] ~= nil then
                       error_text, error_state = seterror( 'parameter_ignored_suggest', {k, suggestions[ k:lower() ]}, true );
                   else
                       error_text, error_state = seterror( 'parameter_ignored', {k}, true );
                   end
               end                  
               if error_text ~=  then
                   table.insert( z.message_tail, {error_text, error_state} );
               end                
           end            
           args[k] = v;
       elseif k == 'postscript' then
           args[k] = v;
       end        
   end    
   local config = {};
   for k, v in pairs( frame.args ) do
       config[k] = v;
       if args[k] == nil and (v ~=  or k == 'postscript') then
           args[k] = v;
       end        
   end    
   
   return citation0( config, args)

end

return z


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


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

Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox
Share