----------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
local in_array, is_set, is_wikilink, make_sep_list, select_one, set_message, substitute, wrap_style; -- functions in Module:Citation/CS1/Utilities
local z; -- table of tables defined in Module:Citation/CS1/Utilities
local cfg; -- table of configuration tables that are defined in Module:Citation/CS1/Configurationlocal suggestions;local whitelist;
----------------------------< P A G E S C O P E V A R I A B L E S >--------------------------------------
declare variables here that have page-wide scope that are not brought in from other modules; that are createdhere and used here
local added_deprecated_cat; -- boolean flag so that the category is added only oncelocal added_vanc_errs; -- boolean flag so we only emit one Vancouver error / categorylocal Frame; -- holds the module's frame table
--
local function is_valid_parameter_value (value, name, possible, ret_val) if not is_set (value) then return ret_val; -- an empty parameter is ok elseif in_array (value, possible) then return cfg.keywords_xlate[value]; -- return translation of parameter keyword else table.insert(z.message_tail,); -- not an allowed value so add error message return ret_val; endend
--authors=, |authorn= / |lastn / firstn=, or |vauthors= as the source of the author name list orselect one of |editorn= / editor-lastn= / |editor-firstn= or |veditors= as the source of the editor name list.
Only one of these appropriate three will be used. The hierarchy is: |authorn= (and aliases) highest and |authors= lowest;|editorn= (and aliases) highest and |veditors= lowest (support for |editors= withdrawn)
When looking for |authorn= / |editorn= parameters, test |xxxxor1= and |xxxxor2= (and all of their aliases); stops after the secondtest which mimicks the test used in extract_names when looking for a hole in the author name list. There may be a betterway to do this, I just haven't discovered what that way is.
Emits an error message when more than one xxxxor name source is provided.
In this function, vxxxxors = vauthors or veditors; xxxxors = authors as appropriate.
local function select_author_editor_source (vxxxxors, xxxxors, args, list_name) local lastfirst = false; if select_one (args, cfg.aliases[list_name .. '-Last'], 'none', 1) or -- do this twice in case we have a |first1= without a |last1=; this ... select_one (args, cfg.aliases[list_name .. '-First'], 'none', 1) or -- ... also catches the case where |first= is used with |vauthors= select_one (args, cfg.aliases[list_name .. '-Last'], 'none', 2) or select_one (args, cfg.aliases[list_name .. '-First'], 'none', 2) then lastfirst = true; end
if (is_set (vxxxxors) and true
lastfirst and is_set (xxxxors)) then local err_name; if 'AuthorList'
if true
----------------------------< N A M E L I S T S _ G E T >----------------------------------------------------
local function namelists_get (params) local author_etal; local a = ; -- authors list from |lastn= / |firstn= pairs or |vauthors= local Authors;
local NameListStyle = is_valid_parameter_value (params['NameListStyle'].value, params['NameListStyle'].origin, cfg.keywords_lists['name-list-style'], );
do -- to limit scope of selected local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'AuthorList'); if 1
selected then NameListStyle = 'vanc'; -- override whatever |name-list-style= might be a, author_etal = parse_vauthors_veditors (args, args.vauthors, 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn= elseif 3
A:ORIGIN('Authors') then -- but add a maint cat if the parameter is |authors= set_message ('maint_authors'); -- because use of this parameter is discouraged; what to do about the aliases is a TODO: end end if is_set (params['Collaboration'].value) then author_etal = true; -- so that |display-authors=etal not required end end
local Others = A['Others'];
local editor_etal; local e = ; -- editors list from |editor-lastn= / |editor-firstn= pairs or |veditors= local Editors;
do -- to limit scope of selected local selected = select_author_editor_source (A['Veditors'], nil, args, 'EditorList'); -- support for |editors= withdrawn if 1
selected then NameListStyle = 'vanc'; -- override whatever |name-list-style= might be e, editor_etal = parse_vauthors_veditors (args, args.veditors, 'EditorList'); -- fetch editor list from |veditors=, |editor-linkn=, and |editor-maskn= end end
local translator_etal; local t = ; -- translators list from |translator-lastn= / translator-firstn= pairs local Translators; -- assembled translators name list t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn=
local interviewer_etal; local interviewers_list = ; local Interviewers; -- used later interviewers_list = extract_names (args, 'InterviewerList'); -- process preferred interviewers parameters
local contributor_etal; local c = ; -- contributors list from |contributor-lastn= / contributor-firstn= pairs local Contributors; -- assembled contributors name list
local Chapter = A['Chapter']; -- done here so that we have access to |contribution= from |chapter= aliases local Chapter_origin = A:ORIGIN ('Chapter'); local Contribution; -- because contribution is required for contributor(s) if 'contribution'
if in_array (config.CitationClass,) and not is_set (A['Periodical']) then -- |contributor= and |contribution= only supported in book cites c = extract_names (args, 'ContributorList'); -- fetch contributor list from |contributorn= / |contributor-lastn=, -firstn=, -linkn=, -maskn= if 0 < #c then if not is_set (Contribution) then -- |contributor= requires |contribution= table.insert(z.message_tail,); -- add missing contribution error message c = ; -- blank the contributors' table; it is used as a flag later end if 0
----------------------------< D E P R E C A T E D _ P A R A M E T E R >--------------------------------------
Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes theoffending parameter name to the error message. Only one error message is emitted regardless of the number of deprecatedparameters in the citation.
added_deprecated_cat is a Boolean declared in page scope variables above
local function deprecated_parameter(name) if not added_deprecated_cat then added_deprecated_cat = true; -- note that we've added this category table.insert (z.message_tail,); -- add error message endend
--
local function build_param_list (args, params)-- local params = ; local val; local selected; for mparam, aliases in pairs (cfg.aliases) do -- spin through the aliases table (in ~/Configuration)--error (mw.dumpObject (aliases))
if 'table'
----------------------------< H A S _ I N V I S I B L E _ C H A R S >----------------------------------------
This function searches a parameter's value for non-printable or invisible characters. The search stops at thefirst match.
This function will detect the visible replacement character when it is part of the Wikisource.
Detects but ignores nowiki and math stripmarkers. Also detects other named stripmarkers (gallery, math, pre, ref)and identifies them with a slightly different error message. See also coins_cleanup.
Output of this function is an error message that identifies the character or the Unicode group, or the stripmarkerthat was detected along with its position (or, for multi-byte characters, the position of its first byte) in theparameter value.
local function has_invisible_chars (param, v) local position = ; -- position of invisible char or starting position of stripmarker local dummy; -- end of matching string; not used but required to hold end position when a capture is returned local capture; -- used by stripmarker detection to hold name of the stripmarker local i = 1; local stripmarker, apostrophe; capture = string.match (v, '[%w%p ]*'); -- test for values that are simple ASCII text and bypass other tests if true if capture
while cfg.invisible_chars[i] do local char = cfg.invisible_chars[i][1] -- the character or group name local pattern = cfg.invisible_chars[i][2] -- the pattern used to find it position, dummy, capture = mw.ustring.find (v, pattern) -- see if the parameter value contains characters that match the pattern if position and (char
capture or 'math'
capture and in_array (param,)) then -- templatestyles stripmarker allowed in these parameters stripmarker = true; -- set a flag elseif true
char then -- because stripmakers begin and end with the delete char, assume that we've found one end of a stripmarker position = nil; -- unset else local err_msg; if capture then err_msg = capture .. ' ' .. char; else err_msg = char .. ' ' .. 'character'; end
table.insert (z.message_tail,); -- add error message return; -- and done with this parameter end end i = i + 1; -- bump our index endend
--
local function validate (name, cite_class, empty) local name = tostring (name); local enum_name; -- for enumerated parameters, is name with enumerator replaced with '#' local state; local function state_test (state, name) -- local function to do testing of state values if true
state then if empty then return nil; end -- deprecated empty parameters are treated as unknowns deprecated_parameter (name); -- parameter is deprecated but still supported return true; end return nil; end
if name:find ('#') then -- # is a cs1|2 reserved character so parameters with # not permitted return nil; end
if in_array (cite_class, whitelist.preprint_template_list) then -- limited parameter sets allowed for these templates state = whitelist.limited_basic_arguments[name]; if true
state = whitelist.preprint_arguments[cite_class][name]; -- look in the parameter-list for the template identified by cite_class if true
-- limited enumerated parameters list enum_name = name:gsub ("%d+", "#"); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits) state = whitelist.limited_numbered_arguments[enum_name]; if true
return false; -- not supported because not found or name is set to nil end -- end limited parameter-set templates
if in_array (cite_class, whitelist.unique_param_template_list) then -- experiment for template-specific parameters for templates that accept parameters from the basic argument list state = whitelist.unique_arguments[cite_class][name]; -- look in the template-specific parameter-lists for the template identified by cite_class if true
state_test (state, name) then return true; end
-- all enumerated parameters allowed enum_name = name:gsub ("%d+", "#"); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits) state = whitelist.numbered_arguments[enum_name]; if true
return false; -- not supported because not found or name is set to nilend
--[=[-------------------------< I N T E R _ W I K I _ C H E C K >---------------------------------------------- check <value> for inter-language interwiki-link markup. <prefix> must be a MediaWiki-recognized language code. when these values have the form (without leading colon): [[<prefix>:link|label]] return label as plain-text return
fix>:link as plain-text return value as is else ]=] local function inter_wiki_check (parameter, value) local prefix = value:match ('%[%[(%a+):'); -- get an interwiki prefix if one exists local _; if prefix and cfg.inter_wiki_map[prefix:lower] then -- if prefix is in the map, needs preceding colon so table.insert (z.message_tail, {set_message ('err_bad_paramlink', parameter)}); -- emit an error message _, value, _ = is_wikilink (value); -- extract label portion from wikilink end return value; end --[[--------------------------< M I S S I N G _ P I P E _ C H E C K >------------------------------------------ Look at the contents of a parameter. If the content has a string of characters and digits followed by an equal sign, compare the alphanumeric string to the list of cs1|2 parameters. If found, then the string is possibly a parameter that is missing its pipe. There are two tests made: {{cite ... |title=Title access-date=2016-03-17}} -- the first parameter has a value and whitespace separates that value from the missing pipe parameter name {{cite ... |title=access-date=2016-03-17}} -- the first parameter has no value (whitespace after the first = is trimmed by MediaWiki) cs1|2 shares some parameter names with XML/HTML attributes: class=, title=, etc. To prevent false positives XML/HTML tags are removed before the search. If a missing pipe is detected, this function adds the missing pipe maintenance category. ]] local function missing_pipe_check (parameter, value) local capture; value = value:gsub ('%b<>', ''); -- remove XML/HTML tags because attributes: class=, title=, etc. capture = value:match ('%s+(%a[%w%-]+)%s*=') or value:match ('^(%a[%w%-]+)%s*='); -- find and categorize parameters with possible missing pipes if capture and validate (capture) then -- if the capture is a valid parameter name table.insert (z.message_tail, {set_message ('err_missing_pipe', parameter)}); end end --[[--------------------------< H A S _ E X T R A N E O U S _ P U N C T >-------------------------------------- look for extraneous terminal punctuation in most parameter values; parameters listed in skip table are not checked ]] local function has_extraneous_punc (param, value) if 'number' == type (param) then return; end param = param:gsub ('%d+', '#'); -- enumerated name-list mask params allow terminal punct; normalize if cfg.punct_skip[param] then return; -- parameter name found in the skip table so done end if value:match ('[,;:]$') then set_message ('maint_extra_punct'); -- has extraneous punctuation; add maint cat end end --[[--------------------------< A R G S _ G E T >-------------------------------------------------------------- get arguments from template frame, do validation and some error checking <args> an empty k/v table where k is parameter name and v is its value; filled here <pfargs> args table from the parent frame (the template's parameters) <class> config.CitationClass from #invoke: (frame) <sandbox> boolean; true when this function called from ~/CS1/sandbox <params> an empty k/v table filled by build_param_list ]] local function args_get (args, pfargs, class, sandbox, params) local suggestions = {}; -- table where we store suggestions if we need to loadData them local empty_unknowns = {}; -- sequence table of empty unknown parameter names local error_text, error_state; local capture; -- the single supported capture when matching unknown parameters using patterns for k, v in pairs (pfargs) do -- get parameters from the parent (template) frame v = mw.ustring.gsub (v, '^%s*(.-)%s*$', '%1'); -- trim leading/trailing whitespace; when v is only whitespace, becomes empty string if v ~= '' then if ('string' == type (k)) then k = mw.ustring.gsub (k, '%d', cfg.date_names.local_digits); -- for enumerated parameters, translate 'local' digits to Western 0-9 end if not validate (k, class) then error_text = ""; if type (k) ~= 'string' then -- exclude empty numbered parameters if v:match ("%S+") ~= nil then error_text, error_state = set_message ('err_text_ignored', {v}, true); end elseif validate (k:lower, class) then error_text, error_state = set_message ('err_parameter_ignored_suggest', {k, k:lower}, true); -- suggest the lowercase version of the parameter else if nil == suggestions.suggestions then -- if this table is nil then we need to load it -- if nil ~= string.find (frame:getTitle, 'sandbox', 1, true) then -- did the {{#invoke:}} use sandbox version? if sandbox then suggestions = mw.loadData ('Module:Citation/CS1/Suggestions/sandbox'); -- use the sandbox version else suggestions = mw.loadData ('Module:Citation/CS1/Suggestions'); -- use the live version end end for pattern, param in pairs (suggestions.patterns) do -- loop through the patterns to see if we can suggest a proper parameter capture = k:match (pattern); -- the whole match if no capture in pattern else the capture if a match if capture then -- if the pattern matches param = substitute (param, capture); -- add the capture to the suggested parameter (typically the enumerator) if validate (param, class) then -- validate the suggestion to make sure that the suggestion is supported by this template (necessary for limited parameter lists) error_text, error_state = set_message ('err_parameter_ignored_suggest', {k, param}, true); -- set the suggestion error message else error_text, error_state = set_message ('err_parameter_ignored', {param}, true); -- suggested param not supported by this template v = ''; -- unset end end end if not is_set (error_text) then -- couldn't match with a pattern, is there an explicit suggestion? if suggestions.suggestions[ k:lower ] ~= nil then error_text, error_state = set_message ('err_parameter_ignored_suggest', {k, suggestions.suggestions[ k:lower ]}, true); else error_text, error_state = set_message ('err_parameter_ignored', {k}, true); v = ''; -- unset value assigned to unrecognized parameters (this for the limited parameter lists) end end end if error_text ~= '' then table.insert (z.message_tail, {error_text, error_state}); end end args[k] = v; -- save this parameter and its value elseif not is_set (v) then -- for empty parameters if not validate (k, class, true) then -- is this empty parameter a valid parameter k = ('' == k) and '(empty string)' or k; -- when k is empty string (or was space(s) trimmed to empty string), replace with descriptive text; TODO: i18n table.insert (empty_unknowns, wrap_style ('parameter', k)); -- format for error message and add to the list end -- crude debug support that allows us to render a citation from module {{#invoke:}} TODO: keep? -- elseif args[k] ~= nil or (k == 'postscript') then -- when args[k] has a value from {{#invoke}} frame (we don't normally do that) -- args[k] = v; -- overwrite args[k] with empty string from pframe.args[k] (template frame); v is empty string here end -- not sure about the postscript bit; that gets handled in parameter validation; historical artifact? end if 0 ~= #empty_unknowns then -- create empty unknown error message table.insert (z.message_tail, {set_message ('err_param_unknown_empty', { 1 == #empty_unknowns and '' or 's', make_sep_list (#empty_unknowns, empty_unknowns) }, true)}); end for k, v in pairs(args) do if 'string' == type (k) then -- don't evaluate positional parameters has_invisible_chars (k, v); -- look for invisible characters end has_extraneous_punc (k, v); -- look for extraneous terminal punctuation in parameter values missing_pipe_check (k, v); -- do we think that there is a parameter that is missing a pipe? args[k] = inter_wiki_check (k, v); -- when language interwiki-linked parameter missing leading colon replace with wiki-link label end params = build_param_list (args, params); return args, params; end --[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >-------------------------------------- Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules. ]] local function set_selected_modules (cfg_table_ptr, utilities_page_ptr, whitelist_page_ptr) cfg = cfg_table_ptr; whitelist = whitelist_page_ptr; -- has_accept_as_written = utilities_page_ptr.has_accept_as_written; -- import functions from select Module:Citation/CS1/Utilities module in_array = utilities_page_ptr.in_array; is_set = utilities_page_ptr.is_set; is_wikilink = utilities_page_ptr.is_wikilink; make_sep_list = utilities_page_ptr.make_sep_list set_message = utilities_page_ptr.set_message; select_one = utilities_page_ptr.select_one; substitute = utilities_page_ptr.substitute; -- make_wikilink = utilities_page_ptr.make_wikilink; wrap_style = utilities_page_ptr.wrap_style; z = utilities_page_ptr.z; -- table of tables in Module:Citation/CS1/Utilities end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return { args_get = args_get, has_extraneous_punc = has_extraneous_punc, has_invisible_chars = has_invisible_chars, inter_wiki_check = inter_wiki_check, missing_pipe_check = missing_pipe_check, set_selected_modules = set_selected_modules, validate = validate, }