require('strict')
local f = {};
local code_; -- used in styling error messages  
local lock_icons = { --icon classes are defined in Module:Citation/CS1/styles.css
['free'] = {'cs1-lock-free', 'Freely accessible'},
['registration'] = {'cs1-lock-registration', 'Free registration required'},
['limited'] = {'cs1-lock-limited', 'Free access subject to limited trial, subscription normally required'},
['subscription'] = {'cs1-lock-subscription', 'Paid subscription required'},
}  
----------------------------< I S _ S E T >------------------------------------------------------------------  
Whether variable is set or not. A variable is set when it is not nil and not empty.  
local function is_set( var )
return not (var == nil or var == '');
end  
----------------------------< S E L E C T _ O N E >----------------------------------------------------------  
Choose one parameter value from a list of parameter values. If more than one is set, emit error message.  
local function select_one (list, args)
local selected_param;
local selected_val='';  
for param, value in pairs (list) do -- loop through the list
if not is_set (selected_param) then -- if we have not yet selected a parameter value
if is_set (value) then -- is this value set?
selected_val = value; -- select it
selected_param = param; -- remember the name for possible error message
end
else
if is_set (value) then -- error message if we have selected and found another set parameter
args.err_msg = string.format (
' more than one of <code>|%s=</code> and <code>|%s=</code>',
code_style, selected_param, code_style, param
break;
end
end
end
return selected_val or ''; -- return selected value or empty string if set
end  
----------------------------< M A K E _ N A M E >------------------------------------------------------------  
Assembles last, first, link, or mask into a displayable author name.  
local function make_name (last, first, link, mask)
local name = last;  
if is_set (first) then
name = name .. ', ' .. first; -- concatenate first onto last
end  
if is_set (link) then
name = * .. name .. *; -- form a wikilink around the name
end  
if is_set (mask) then -- mask this author
mask = tonumber (mask); -- because the value provided might not be a number
if is_set (mask) then
name = string.rep ('—', mask) -- make a string that number length of mdashes
end
end  
return name;
end  
--display-authors= is empty or omitted, display is similar to cs1|2: display all names in last, first order
if |display-authors=etal then displays all author names in last, first order and append et al.
if value assigned to |display-authors= is less than the number of author last names, displays the specified number of author names in last, first order followed by et al.  
local function make_author_list (args, number_of_authors)
local authors = '';
local i = 1;
local count;
local etal = false; -- when |display-authors= is same as number of authors in contributor list  
if is_set (args.display_authors) then
if 'etal' == args.display_authors:lower:gsub("[ '%.]", '') then -- the :gsub portion makes 'etal' from a variety of 'et al.' spellings and stylings
count = number_of_authors; -- display all authors and ...
etal = true; -- ... append 'et al.'
else
count = tonumber (args.display_authors) or 0; -- 0 if can't be converted to a number
if 0 >= count then
args.err_msg = string.format ('%s invalid <code>|display-authors=</code>; ', args.err_msg, code_style);
-- args.err_msg = args.err_msg .. ' invalid |display-authors='; -- if zero, then emit error message
count = number_of_authors; -- and display all authors
end
end
if count > number_of_authors then
count = number_of_authors; -- when |display-authors= is more than the number of authors, use the number of authors
end
if count < number_of_authors then -- when |display-authors= is less than the number of authors
etal = true; -- append 'et al.'
end
else
count = number_of_authors; -- set count to display all of the authors
end  
while i <= count do
if is_set (authors) then
authors = authors .. '; ' .. make_name (args.last[i], args.first[i], args.link[i], args.mask[i]); -- the rest of the authors
else
authors = make_name (args.last[i], args.first[i], args.link[i], args.mask[i]); -- first author's name
end
i = i+1; -- bump the index
end
if true == etal then
authors = authors .. '; et al.'; -- append et al.
elseif 'yes' == args.last_author_amp then
authors = authors:gsub('; ([^;]+)$', ' & %1') -- replace last separator with ' & '
end  
-- if args.sepc ~= authors:sub(-1) and args.sepc .. ']]' ~= authors:sub(-3) then
-- authors = authors; -- add separator if not same as last character in name list (|first=John S. or et al.)
-- end
-- TODO: better way to handle wikilink case?
authors = authors:gsub ('%' .. args.sepc .. '$', '', 1); -- remove trailing separator character
authors = authors:gsub ('%' .. args.sepc .. ']]$', ']]', 1); -- remove trailing separator character inside wikilink  
return authors;
end  
--item= and, if present, |item-url= into the linked part and if present appends |date= and
|type= with appropriate markup to complete the item portion of the citation. This function assumes that item
has a value when it is called.  
local function make_item (item, url, item_date, item_type, item_access)
local output = {}; -- table of item bits
if is_set (url) then
item = string.format ('[%s %s]', url, item); -- make item into an external wikilink
end
if item_access and ('subscription' == item_access or 'registration' == item_access) then
table.insert (output, table.concat ({ -- opening quote mark then add access icon markup to this item
'"<span title="', -- and the title attribute
lock_icons[item_access][2], -- for an appropriate tool tip
'">', -- close the opening span tag
item,
'</span>"', -- and close the span and close the quote
}));
else
table.insert (output, string.format ('"%s"', item)); -- enclose in quotes and add to table
end
if is_set (item_date) then
table.insert (output, string.format ('(%s)', item_date)); -- enclose in parentheses and add to table
end
if is_set (item_type) then
table.insert (output, string.format ('[%s]', item_type)); -- enclose in square brackets and add to table
end  
return table.concat (output, ' '); -- concatenate with space as separator
end  
--collection= and, if present, |collection-url= into the linked part and if present, appends
the values from |fonds=, |series=, |box=, |file=, |itemid=, and |page= or |pages= to complete the collection
portion of the citation. This function assumes that collection has a value when it is called (because that is one
of the two required parameters)  
local function make_collection (args)
local output = {}; -- table of collections bits
local collection = args.collection;
if is_set (args.collectionURL) then
collection = string.format ('[%s %s]', args.collectionURL, collection); -- make collection into an external wikilink
end
table.insert (output, string.format ('%s', collection)); -- enclose in quotes and add to table
if is_set (args.fonds) then
table.insert (output, string.format ('Fonds: %s', args.fonds)); -- format and add to table
end
if is_set (args.series) then
table.insert (output, string.format ('Series: %s', args.series)); -- format and add to table
end
if is_set (args.box) then
table.insert (output, string.format ('Box: %s', args.box)); -- format and add to table
end
if is_set (args.file) then
table.insert (output, string.format ('File: %s', args.file)); -- format and add to table
end
if is_set (args.itemID) then
table.insert (output, string.format ('ID: %s', args.itemID)); -- format and add to table
end  
if is_set (args.p) then
table.insert (output, string.format ('%s%s', args.page_sep, args.p));
elseif is_set (args.pp) then
table.insert (output, string.format ('%s%s', args.pages_sep, args.pp));
end  
if is_set (args.p) and is_set (args.pp) then
args.err_msg = string.format ('%s more than one of <code>|page=</code> and <code>|pages=</code>; ', args.err_msg, code_style, code_style);
end