Module:Autoname
Appearance
| This module is rated as beta. It is considered ready for widespread use, but as it is still relatively new, it should be applied with some caution to ensure results are as expected. |
| This module is currently protected from editing. See the protection policy and protection log for more details. Please discuss any changes on the talk page; you may submit an edit request to ask an administrator to make an edit if it is uncontroversial or supported by consensus. You may also request that this page be unprotected. |
| This module depends on the following other modules: |
Implements {{Autoname}}
local utilities = require ('Module:Citation/CS1/Utilities');
local cfg = mw.loadData ('Module:Citation/CS1/Configuration');
local data = mw.loadData ('Module:Autoname/data');
--[[--------------------------< N A M E _ H A S _ E T A L >----------------------------------------------------
Evaluates the content of name parameters (contributor, author, editor) for variations on the theme of et al. If
found, the et al. is removed.
Adapted from same-named function in Module:Citation/CS1
]]
local function name_has_etal (name)
if utilities.is_set (name) then -- name can be nil in which case just return
local patterns = cfg.et_al_patterns; -- get patterns from configuration
for _, pattern in ipairs (patterns) do -- loop through all of the patterns
if name:match (pattern) then -- if this 'et al' pattern is found in name
name = name:gsub (pattern, ''); -- remove the offending text
return name; -- and done;
end
end
end
return name; -- no etal found, return <name>
end
--[=[-------------------------< G E T _ V _ N A M E _ T A B L E >----------------------------------------------
split apart a |vauthors= or |veditors= parameter. This function allows for corporate names, wrapped in accept-
as-written markup to also have commas. Individual author names may be wikilinked.
|vauthors=Jones AB, [[E. B. White|White EB]], ((Black, Brown, and Co.))
Adapted from same-named function in Module:Citation/CS1
]=]
local function get_v_name_table (vparam, out_t)
local names_t = mw.text.split (vparam, "%s*,%s*"); -- names in vparam are separated by commas
local label; -- label from a wikilinked name
local i = 1; -- initialize indexer
while names_t[i] and i < 5 do -- no more than four names
if names_t[i]:match ('^%(%(.*[^%)][^%)]$') then -- first segment of corporate with one or more commas; this segment has the opening doubled parentheses
local name = names_t[i];
i = i + 1; -- bump indexer to next segment
while names_t[i] do
name = name .. ', ' .. names_t[i]; -- concatenate with previous segments
if names_t[i]:match ('^.*%)%)$') then -- if this table member has the closing doubled parentheses
break; -- and done reassembling so
end
i = i + 1; -- bump indexer
end
table.insert (out_t, name); -- and add corporate name to the output table
else
local _;
_, label, _ = utilities.is_wikilink (names_t[i]); -- wl_type is: 0, no wl (text in <label>); 1, [[D]]; 2, [[L|D]]
table.insert (out_t, label); -- add this name
end
i = i + 1;
end
return out_t;
end
--[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >--------------------------------
This function extracts author / editor surnames from |vauthors= or |veditors=. Initials and generational suffixes
are discarded. Accept-as-written markup around an individual name is stripped.
Adapted from same-named function in Module:Citation/CS1
]]
local function parse_vauthors_veditors (vparam)
local names_t = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn=
local v_name_table = {};
local last, first, suffix;
vparam = name_has_etal (vparam); -- find and remove variations on et al. do not categorize (do it here because et al. might have a period)
v_name_table = get_v_name_table (vparam, v_name_table); -- names are separated by commas
for i, v_name in ipairs (v_name_table) do
first = ''; -- set to empty string for concatenation and because it may have been set for previous author/editor
local accept_name;
v_name, accept_name = utilities.has_accept_as_written (v_name); -- remove accept-this-as-written markup when it wraps all of <v_name>
if accept_name then
last = v_name;
elseif string.find (v_name, "%s") then
local lastfirst_t = mw.text.split (v_name, "%s+");
first = table.remove (lastfirst_t); -- removes and returns value of last element in table which should be initials or generational suffix
if not mw.ustring.match (first, '^%u+$') then -- mw.ustring here so that later we will catch non-Latin characters
suffix = first; -- not initials so assume that whatever we got is a generational suffix
first = table.remove (lastfirst_t); -- get what should be the initials from the table
end
last = table.concat (lastfirst_t, ' '); -- returns a string that is the concatenation of all other names that are not initials and generational suffix
if not utilities.is_set (last) then
first = ''; -- unset
last = v_name; -- last empty because something wrong with first
end
else
last = v_name; -- last name or single corporate name? Doesn't support multiword corporate names? do we need this?
end
if utilities.is_set (first) then
if utilities.is_set (suffix) then
suffix = ''; -- unset so we don't add this suffix to all subsequent names
end
end
names_t[i] = last; -- add name to names list
end
return names_t; -- all done, return list of names
end
--[[--------------------------< Y E A R _ G E T >--------------------------------------------------------------
because |date= and |publication-date= will be full aliases ofeach other at the next cs1|2 suite update, accept
only one of them; assume that |date= is the canonical form
]]
local function year_get (args_t)
local date, year;
local d_dab, y_dab; -- used to identify which to returm when both 'date' and year are set
date, d_dab = (args_t['date'] or args_t['publication-date'] or ''):match ('([1-9]%d%d%d?(%l?))'); -- if both are present prefer |date=
year, y_dab = (args_t['year'] or ''):match ('([1-9]%d%d%d?(%l?))');
if date and year then
if '' ~= d_dab then -- when no CITEREF disambiguator, <d_dab> is empty string
return date; -- prefer |date= and |publication-date= over year when 'date' has a CITEREF disambiguator
elseif '' ~= y_dab then -- when no CITEREF disambiguator, <y_dab> is empty string
return year; -- prefer |year= over 'date' when |year= has a CITEREF disambiguator
end
end
return date or year; -- when no dabs or only one of |date=, |publication-date=, or |year= set
end
--[[--------------------------< D O M A I N _ G E T >----------------------------------------------------------
extract domain name from a url.
]]
local function domain_get (url)
url = mw.text.trim (url or '')
if url == '' then return nil end
local domain = mw.ustring.match(url, "^%a[%w+.-]*://([^/?#]+)") -- Extract the domain (with or without protocol)
or mw.ustring.match(url, "^//([^/?#]+)")
or mw.ustring.match(url, "^([^/?#]+)")
if not domain then return nil end
domain = domain:gsub ("^.*@", ""):gsub (":%d+$", "") -- Remove credentials (user:pass@) and port (:8080)
local _, segments = domain:gsub ("[^.]+", "")
if segments < 2 or domain:find ('%.%.') then -- Reject values that don't look like a domain name
return nil
elseif segments > 2 then -- Remove common prefixes
domain = domain:gsub ("^ww[w%d]%d*%.", ""):gsub ("^m%.", ""):gsub ("^ftp%.", "")
end
return domain
end
--[[--------------------------< A U T O N A M E _ M A K E >----------------------------------------------------
Common function to create autoname from whichever of contributor / author / editor name list is chosen.
Attempt to prevent the autoname from being over 78 characters (80 total including a 2-digit index).
]]
local function autoname_make (names_t, year)
local suffix = (year or '') .. '-'; -- add year to name list followed by a hyphen to separate year from ve-added index digit(s)
local length = mw.ustring.len (suffix);
local output = '';
for i, name in ipairs(names_t) do
local name_length = mw.ustring.len (name);
if length + name_length > 78 then -- check if the next item would push the string over 78 characters
if i == 1 then -- first item in the list, try to truncate first on punctuation, then on spaces
local prefix = mw.ustring.sub (name, 1, 78 - length);
name = mw.ustring.match (prefix, "^(.*)%p") or mw.ustring.match (prefix, "^(.*)%s") or name;
else -- stop concatenation if it would push the string over 78 characters
break;
end
end
output = output .. name;
length = length + name_length;
end
return mw.uri.anchorEncode (output .. suffix); -- concatenate, anchor encode, and done
end
--[[--------------------------< _ A U T O N A M E >------------------------------------------------------------
entry point from another module or from the debug console
]]
local function _autoname (args_t)
local params_order_t = data.params_order_t;
local enum_params_order_t = data.enum_params_order_t;
local enum_params_t = data.enum_params_t;
local params_t = data.params_t;
local names_t = {}; -- sequence to hold values to potentially pass to autoname_make
for _, ntype in ipairs (params_order_t) do
names_t[ntype] = {};
end
for param, value in pairs (args_t) do
local enum = tonumber (param:match ('%d+')); -- get the enumerator if this parameter has one; nil else
local index = param:gsub ('%d+', '#');
local accept;
value, accept = utilities.has_accept_as_written (value); -- remove accept-as-written markup if present
if not accept then
value = name_has_etal (value);
end
if enum and (5 > enum) then -- sort enumerated name parameter values into the various sequences
for _, ntype in ipairs (enum_params_order_t) do
if enum_params_t[ntype][index] then
names_t[ntype][enum] = value;
break;
end
end
else -- sort non-enumerated name parameter values into the various sequences
for _, ntype in ipairs (params_order_t) do
if params_t[ntype][index] then
if ntype == 'url' then
names_t[ntype][1] = domain_get (value)
else
names_t[ntype][1] = value;
end
break;
end
end
end
end
if args_t['vauthors'] and #names_t.author == 0 then
names_t.author = parse_vauthors_veditors (args_t['vauthors']); -- fetch a sequence of vauthor surnames
end
if args_t['veditors'] and #names_t.editor == 0 then
names_t.editor = parse_vauthors_veditors (args_t['veditors']); -- fetch a sequence of veditor surnames
end
local year = year_get (args_t); -- get year portion of date from |date=, |publication-date=, or |year=
for _, ntype in ipairs(params_order_t) do -- in params_order_t order, make a CITEREF-like anchor for autonaming
if names_t[ntype][1] then
return autoname_make (names_t[ntype], year);
end
end
end
--[[--------------------------< A U T O N A M E >--------------------------------------------------------------
entry point from a template
]]
local function autoname (frame)
local args_t = require ('Module:Arguments').getArgs (frame); -- get the template's parameters
return _autoname (args_t); -- and render the reference name
end
--[[--------------------------E X P O R T S >------------------------------------------------------------------
]]
return {
autoname = autoname, -- entry point from a template
_autoname = _autoname, -- entry point from another module or from the debug console
}