Sətir 1: |
Sətir 1: |
− |
| |
− | local cs1 ={};
| |
| | | |
| --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- | | --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- |
Sətir 9: |
Sətir 7: |
| | | |
| local is_set, in_array, substitute, error_comment, set_error, select_one, -- functions in Module:Citation/CS1/Utilities | | local is_set, in_array, substitute, error_comment, set_error, select_one, -- functions in Module:Citation/CS1/Utilities |
− | add_maint_cat, wrap_style, safe_for_italics, is_wikilink, make_wikilink; | + | add_maint_cat, wrap_style, safe_for_italics, is_wikilink, make_wikilink, |
| + | strip_apostrophe_markup; |
| | | |
| local z ={}; -- tables in Module:Citation/CS1/Utilities | | local z ={}; -- tables in Module:Citation/CS1/Utilities |
Sətir 72: |
Sətir 71: |
| added_prop_cats [key] = true; -- note that we've added this category | | added_prop_cats [key] = true; -- note that we've added this category |
| key = key:gsub ('(foreign_lang_source_?2?)%a%a%a?', '%1'); -- strip lang code from keyname | | key = key:gsub ('(foreign_lang_source_?2?)%a%a%a?', '%1'); -- strip lang code from keyname |
− | table.insert( z.properties_cats, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table | + | table.insert( z.properties_cats, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table |
| end | | end |
| end | | end |
Sətir 133: |
Sətir 132: |
| the first character of the whole domain name including subdomains must be a letter or a digit | | the first character of the whole domain name including subdomains must be a letter or a digit |
| internationalized domain name (ascii characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the tld) see https://tools.ietf.org/html/rfc3490 | | internationalized domain name (ascii characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the tld) see https://tools.ietf.org/html/rfc3490 |
− | single-letter/digit second-level domains in the .org and .cash TLDs | + | single-letter/digit second-level domains in the .org, .cash, and .today TLDs |
| q, x, and z SL domains in the .com TLD | | q, x, and z SL domains in the .com TLD |
| i and q SL domains in the .net TLD | | i and q SL domains in the .net TLD |
Sətir 152: |
Sətir 151: |
| domain = domain:gsub ('^//', ''); -- strip '//' from domain name if present; done here so we only have to do it once | | domain = domain:gsub ('^//', ''); -- strip '//' from domain name if present; done here so we only have to do it once |
| | | |
− | if not domain:match ('^[%a%d]') then -- first character must be letter or digit | + | if not domain:match ('^[%w]') then -- first character must be letter or digit |
| return false; | | return false; |
| end | | end |
− | -- Do most common case first
| + | |
− | if domain:match ('%f[%a%d][%a%d][%a%d%-]+[%a%d]%.%a%a+$') then -- three or more character hostname.hostname or hostname.tld | + | if domain:match ('^%a+:') then -- hack to detect things that look like s:Page:Title where Page: is namespace at wikisource |
− | return true;
| |
− | elseif domain:match ('%f[%a%d][%a%d][%a%d%-]+[%a%d]%.xn%-%-[%a%d]+$') then -- internationalized domain name with ACE prefix
| |
− | return true;
| |
− | elseif domain:match ('%f[%a%d][%a%d]%.cash$') then -- one character/digit .cash hostname
| |
− | return true;
| |
− | elseif domain:match ('%f[%a%d][%a%d]%.org$') then -- one character/digit .org hostname
| |
− | return true;
| |
− | elseif domain:match ('%f[%a][qxz]%.com$') then -- assigned one character .com hostname (x.com times out 2015-12-10)
| |
− | return true;
| |
− | elseif domain:match ('%f[%a][iq]%.net$') then -- assigned one character .net hostname (q.net registered but not active 2015-12-10)
| |
− | return true;
| |
− | elseif domain:match ('%f[%a%d][%a%d]%.%a%a$') then -- one character hostname and cctld (2 chars)
| |
− | return true;
| |
− | elseif domain:match ('%f[%a%d][%a%d][%a%d]%.%a%a+$') then -- two character hostname and tld
| |
− | return true;
| |
− | elseif domain:match ('^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?') then -- IPv4 address
| |
− | return true;
| |
− | else
| |
| return false; | | return false; |
| end | | end |
| + | |
| + | local patterns = { -- patterns that look like urls |
| + | '%f[%w][%w][%w%-]+[%w]%.%a%a+$', -- three or more character hostname.hostname or hostname.tld |
| + | '%f[%w][%w][%w%-]+[%w]%.xn%-%-[%w]+$', -- internationalized domain name with ACE prefix |
| + | '%f[%a][qxz]%.com$', -- assigned one character .com hostname (x.com times out 2015-12-10) |
| + | '%f[%a][iq]%.net$', -- assigned one character .net hostname (q.net registered but not active 2015-12-10) |
| + | '%f[%w][%w]%.%a%a$', -- one character hostname and cctld (2 chars) |
| + | '%f[%w][%w][%w]%.%a%a+$', -- two character hostname and tld |
| + | '^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?', -- IPv4 address |
| + | } |
| + | |
| + | for _, pattern in ipairs (patterns) do -- loop through the patterns list |
| + | if domain:match (pattern) then |
| + | return true; -- if a match then we think that this thing that purports to be a url is a url |
| + | end |
| + | end |
| + | |
| + | for _, d in ipairs ({'cash', 'company', 'today', 'org'}) do -- look for single letter second level domain names for these top level domains |
| + | if domain:match ('%f[%w][%w]%.' .. d) then |
| + | return true |
| + | end |
| + | end |
| + | return false; -- no matches, we don't know what this thing is |
| end | | end |
| | | |
Sətir 363: |
Sətir 367: |
| error_message=error_message .. "|" .. k .. "="; -- add the failed parameter | | error_message=error_message .. "|" .. k .. "="; -- add the failed parameter |
| end | | end |
− | end
| |
− | if is_set (error_message) then -- done looping, if there is an error message, display it
| |
− | table.insert( z.message_tail, { set_error( 'param_has_ext_link', {error_message}, true ) } );
| |
| end | | end |
| end | | end |
Sətir 378: |
Sətir 379: |
| local function safe_for_url( str ) | | local function safe_for_url( str ) |
| if str:match( "%[%[.-%]%]" ) ~= nil then | | if str:match( "%[%[.-%]%]" ) ~= nil then |
− | table.insert( z.message_tail, { set_error( 'wikilink_in_url', {}, true ) } );
| |
| end | | end |
| | | |
Sətir 399: |
Sətir 399: |
| local path; | | local path; |
| local base_url; | | local base_url; |
− |
| + | |
| if not is_set( label ) then | | if not is_set( label ) then |
| label = URL; | | label = URL; |
Sətir 415: |
Sətir 415: |
| if path then -- if there is a path portion | | if path then -- if there is a path portion |
| path = path:gsub ('[%[%]]', {['[']='%5b',[']']='%5d'}); -- replace '[' and ']' with their percent encoded values | | path = path:gsub ('[%[%]]', {['[']='%5b',[']']='%5d'}); -- replace '[' and ']' with their percent encoded values |
− | URL=domain..path; -- and reassemble | + | URL = table.concat ({domain, path}); -- and reassemble |
| end | | end |
− | | + | |
| + | base_url = table.concat({ "[", URL, " ", safe_for_url (label), "]" }); -- assemble a wikimarkup url |
| + | |
| if is_set (access) then -- access level (subscription, registration, limited) | | if is_set (access) then -- access level (subscription, registration, limited) |
− | label = safe_for_url (label); -- replace square brackets and newlines | + | base_url = substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[access].class, cfg.presentation[access].title, base_url}); -- add the appropriate icon |
− | | |
− | base_url = table.concat ( -- assemble external link with access signal
| |
− | {
| |
− | '<span class="plainlinks">[', -- opening css and url markup
| |
− | URL, -- the url
| |
− | ' ', -- the required space
| |
− | label,
| |
− | '<span style="padding-left:0.15em">', -- signal spacing css
| |
− | cfg.presentation[access], -- the appropriate icon
| |
− | '</span>', -- close signal spacing span
| |
− | ']</span>' -- close url markup and plain links span
| |
− | });
| |
− | else
| |
− | base_url = table.concat({ "[", URL, " ", safe_for_url( label ), "]" }); -- no signal markup
| |
| end | | end |
| | | |
− | return table.concat({ base_url, error_str }); | + | return table.concat ({base_url, error_str}); |
| end | | end |
| | | |
Sətir 453: |
Sətir 441: |
| if not added_deprecated_cat then | | if not added_deprecated_cat then |
| added_deprecated_cat = true; -- note that we've added this category | | added_deprecated_cat = true; -- note that we've added this category |
− | table.insert( z.message_tail, { set_error( 'deprecated_params', {name}, true ) } ); -- add error message
| |
| end | | end |
| end | | end |
Sətir 535: |
Sətir 522: |
| is not added. At this time there is no error message for this condition. | | is not added. At this time there is no error message for this condition. |
| | | |
− | Supports |script-title= and |script-chapter= | + | Supports |script-title=, |script-chapter=, |script-<periodical>= |
| | | |
− | TODO: error messages when prefix is invalid ISO639-1 code; when script_value has prefix but no script;
| |
| ]] | | ]] |
| | | |
− | local function format_script_value (script_value) | + | local function format_script_value (script_value, script_param) |
| local lang=''; -- initialize to empty string | | local lang=''; -- initialize to empty string |
| local name; | | local name; |
Sətir 546: |
Sətir 532: |
| lang = script_value:match('^(%l%l)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script | | lang = script_value:match('^(%l%l)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script |
| if not is_set (lang) then | | if not is_set (lang) then |
| + | table.insert( z.message_tail, { set_error( 'script_parameter', {script_param, 'missing title part'}, true ) } ); -- prefix without 'title'; add error message |
| return ''; -- script_value was just the prefix so return empty string | | return ''; -- script_value was just the prefix so return empty string |
| end | | end |
| -- if we get this far we have prefix and script | | -- if we get this far we have prefix and script |
− | name = mw.language.fetchLanguageName( lang, "en" ); -- get language name so that we can use it to categorize | + | name = cfg.lang_code_remap[lang] or mw.language.fetchLanguageName( lang, "en" ); -- get language name so that we can use it to categorize |
| if is_set (name) then -- is prefix a proper ISO 639-1 language code? | | if is_set (name) then -- is prefix a proper ISO 639-1 language code? |
| script_value = script_value:gsub ('^%l%l%s*:%s*', ''); -- strip prefix from script | | script_value = script_value:gsub ('^%l%l%s*:%s*', ''); -- strip prefix from script |
| -- is prefix one of these language codes? | | -- is prefix one of these language codes? |
− | if in_array (lang, cfg.script_lang_codes) then
| |
− | add_prop_cat ('script_with_name', {name, lang})
| |
− | else
| |
− | add_prop_cat ('script')
| |
− | end
| |
| lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute | | lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute |
| else | | else |
| + | table.insert( z.message_tail, { set_error( 'script_parameter', {script_param, 'invalid language code'}, true ) } ); -- invalid language code; add error message |
| lang = ''; -- invalid so set lang to empty string | | lang = ''; -- invalid so set lang to empty string |
| end | | end |
| + | else |
| + | table.insert( z.message_tail, { set_error( 'script_parameter', {script_param, 'missing prefix'}, true ) } ); -- no language code prefix; add error message |
| end | | end |
| script_value = substitute (cfg.presentation['bdi'], {lang, script_value}); -- isolate in case script is rtl | | script_value = substitute (cfg.presentation['bdi'], {lang, script_value}); -- isolate in case script is rtl |
Sətir 575: |
Sətir 560: |
| ]] | | ]] |
| | | |
− | local function script_concatenate (title, script) | + | local function script_concatenate (title, script, script_param) |
| if is_set (script) then | | if is_set (script) then |
− | script = format_script_value (script); -- <bdi> tags, lang atribute, categorization, etc; returns empty string on error | + | script = format_script_value (script, script_param); -- <bdi> tags, lang atribute, categorization, etc; returns empty string on error |
| if is_set (script) then | | if is_set (script) then |
| title = title .. ' ' .. script; -- concatenate title and script title | | title = title .. ' ' .. script; -- concatenate title and script title |
Sətir 601: |
Sətir 586: |
| local msg; | | local msg; |
| msg = cfg.messages[key]:lower(); -- set the message to lower case before | | msg = cfg.messages[key]:lower(); -- set the message to lower case before |
− | return substitute( msg, str ); -- including template text | + | return substitute( msg, str ); -- including template text |
| else | | else |
| return substitute( cfg.messages[key], str ); | | return substitute( cfg.messages[key], str ); |
Sətir 608: |
Sətir 593: |
| | | |
| | | |
− | --[[--------------------------< F O R M A T _ C H A P T E R _ T I T L E >-------------------------------------- | + | --[[--------------------------< W I K I S O U R C E _ U R L _ M A K E >---------------------------------------- |
| + | |
| + | makes a wikisource url from wikisource interwiki link. returns the url and appropriate label; nil else. |
| | | |
− | Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=, and |chapter-url= into a single Chapter meta-
| + | str is the value assigned to |chapter= (or aliases) or |title= or |title-link= |
− | parameter (chapter_url_source used for error messages).
| |
| | | |
| ]] | | ]] |
| | | |
− | local function format_chapter_title (scriptchapter, chapter, transchapter, chapterurl, chapter_url_source, no_quotes, access) | + | local function wikisource_url_make (str) |
− | local chapter_error = ''; | + | local wl_type, D, L; |
− | | + | local ws_url, ws_label; |
− | if not is_set (chapter) then
| + | |
− | chapter = ''; -- to be safe for concatenation
| + | wl_type, D, L = is_wikilink (str); -- wl_type is 0 (not a wikilink), 1 (simple wikilink), 2 (complex wikilink) |
− | else | + | |
− | if false == no_quotes then
| + | if 0 == wl_type then -- not a wikilink; might be from |title-link= |
− | chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from Module provided quote marks
| + | str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace |
− | chapter = wrap_style ('quoted-title', chapter); | + | if is_set (str) then |
| + | ws_url = table.concat ({ -- build a wikisource url |
| + | 'https://en.wikisource.org/wiki/', -- prefix |
| + | str, -- article title |
| + | }); |
| + | ws_label = str; -- label for the url |
| + | end |
| + | elseif 1 == wl_type then -- simple wikilink: [[Wikisource:ws article]] |
| + | str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace |
| + | if is_set (str) then |
| + | ws_url = table.concat ({ -- build a wikisource url |
| + | 'https://en.wikisource.org/wiki/', -- prefix |
| + | str, -- article title |
| + | }); |
| + | ws_label = str; -- label for the url |
| end | | end |
− | end | + | elseif 2 == wl_type then -- non-so-simple wikilink: [[Wikisource:ws article|displayed text]] ([[L|D]]) |
− | | + | str = L:match ('^[Ww]ikisource:(.+)') or L:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace |
− | chapter = script_concatenate (chapter, scriptchapter) -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped
| + | if is_set (str) then |
− | | + | ws_label = D; -- get ws article name from display portion of interwiki link |
− | if is_set (transchapter) then
| + | ws_url = table.concat ({ -- build a wikisource url |
− | transchapter = wrap_style ('trans-quoted-title', transchapter); | + | 'https://en.wikisource.org/wiki/', -- prefix |
− | if is_set (chapter) then | + | str, -- article title without namespace from link portion of wikilink |
− | chapter = chapter .. ' ' .. transchapter; | + | }); |
− | else -- here when transchapter without chapter or script-chapter
| |
− | chapter = transchapter; -- | |
− | chapter_error = ' ' .. set_error ('trans_missing_title', {'chapter'});
| |
| end | | end |
| end | | end |
− | | + | |
− | if is_set (chapterurl) then | + | if ws_url then |
− | chapter = external_link (chapterurl, chapter, chapter_url_source, access); -- adds bare_url_missing_title error if appropriate | + | ws_url = mw.uri.encode (ws_url, 'WIKI'); -- make a usable url |
| + | ws_url = ws_url:gsub ('%%23', '#'); -- undo percent encoding of anchor |
| end | | end |
| | | |
− | return chapter .. chapter_error; | + | return ws_url, ws_label, L or D; -- return proper url or nil and a label or nil |
| end | | end |
| | | |
| | | |
− | --[[--------------------------< H A S _ I N V I S I B L E _ C H A R S >---------------------------------------- | + | --[[--------------------------< F O R M A T _ P E R I O D I C A L >-------------------------------------------- |
| | | |
− | This function searches a parameter's value for nonprintable or invisible characters. The search stops at the
| + | Format the four periodical parameters: |script-<periodical>=, |<periodical>=, and |trans-<periodical>= into a single Periodical meta- |
− | first match.
| + | parameter. |
| | | |
− | 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)
| + | local function format_periodical (script_periodical, script_periodical_source, periodical, trans_periodical) |
− | and identifies them with a slightly different error message. See also coins_cleanup().
| + | local periodical_error = ''; |
| | | |
− | Detects but ignores the character pattern that results from the transclusion of {{'}} templates.
| + | if not is_set (periodical) then |
| + | periodical = ''; -- to be safe for concatenation |
| + | else |
| + | periodical = wrap_style ('italic-title', periodical); -- style |
| + | end |
| | | |
− | Output of this function is an error message that identifies the character or the Unicode group, or the stripmarker
| + | periodical = script_concatenate (periodical, script_periodical, script_periodical_source); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped |
− | that was detected along with its position (or, for multi-byte characters, the position of its first byte) in the
| + | |
− | parameter value. | + | if is_set (trans_periodical) then |
| + | trans_periodical = wrap_style ('trans-italic-title', trans_periodical); |
| + | if is_set (periodical) then |
| + | periodical = periodical .. ' ' .. trans_periodical; |
| + | else -- here when transchapter without chapter or script-chapter |
| + | periodical = trans_periodical; |
| + | periodical_error = ' ' .. set_error ('trans_missing_title', {'periodical'}); |
| + | end |
| + | end |
| + | |
| + | return periodical .. periodical_error; |
| + | end |
| + | |
| + | |
| + | --[[--------------------------< F O R M A T _ C H A P T E R _ T I T L E >-------------------------------------- |
| + | |
| + | Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=, and |chapter-url= into a single Chapter meta- |
| + | parameter (chapter_url_source used for error messages). |
| | | |
| ]] | | ]] |
| | | |
− | local function has_invisible_chars (param, v) | + | local function format_chapter_title (scriptchapter, script_chapter_source, chapter, transchapter, chapterurl, chapter_url_source, no_quotes, access) |
− | local position = ''; -- position of invisible char or starting position of stripmarker | + | local chapter_error = ''; |
− | 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 ws_url, ws_label, L = wikisource_url_make (chapter); -- make a wikisource url and label from a wikisource interwiki link |
− | local i=1;
| + | if ws_url then |
− | local stripmarker, apostrophe;
| + | ws_label = ws_label:gsub ('_', ''); -- replace underscore separaters with space characters |
− |
| + | chapter = ws_label; |
− | capture = string.match (v, '[%w%p ]*'); -- Test for values that are simple ASCII text and bypass other tests if true
| |
− | if capture == v then -- if same there are no unicode characters
| |
− | return; | |
| end | | end |
| | | |
− | while cfg.invisible_chars[i] do | + | if not is_set (chapter) then |
− | local char=cfg.invisible_chars[i][1] -- the character or group name | + | chapter = ''; -- to be safe for concatenation |
− | local pattern=cfg.invisible_chars[i][2] -- the pattern used to find it
| + | else |
− | position, dummy, capture = mw.ustring.find (v, pattern) -- see if the parameter value contains characters that match the pattern
| + | if false == no_quotes then |
− |
| + | chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from Module provided quote marks |
− | if position and (char == 'zero width joiner') then -- if we found a zero width joiner character | + | chapter = wrap_style ('quoted-title', chapter); |
− | if mw.ustring.find (v, cfg.indic_script) then -- its ok if one of the indic scripts | |
− | position = nil; -- unset position
| |
− | end
| |
| end | | end |
− |
| + | end |
− | if position then
| + | |
− | if 'nowiki' == capture or 'math' == capture then -- nowiki, math stripmarker (not an error condition)
| + | chapter = script_concatenate (chapter, scriptchapter, script_chapter_source); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped |
− | stripmarker = true; -- set a flag
| + | |
− | elseif true == stripmarker and 'delete' == char then -- because stripmakers begin and end with the delete char, assume that we've found one end of a stripmarker
| + | if is_set (chapterurl) then |
− | position = nil; -- unset
| + | chapter = external_link (chapterurl, chapter, chapter_url_source, access); -- adds bare_url_missing_title error if appropriate |
− | else
| + | elseif ws_url then |
− | local err_msg;
| + | chapter = external_link (ws_url, chapter .. ' ', 'ws link in chapter'); -- adds bare_url_missing_title error if appropriate; space char to move icon away from chap text; TODO: better way to do this? |
− | if capture then
| + | chapter = substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, chapter}); |
− | err_msg = capture .. ' ' .. char;
| + | end |
− | else
| |
− | err_msg = char .. ' ' .. 'character';
| |
− | end
| |
| | | |
− | table.insert( z.message_tail, { set_error( 'invisible_char', {err_msg, wrap_style ('parameter', param), position}, true ) } ); -- add error message
| + | if is_set (transchapter) then |
− | return; -- and done with this parameter
| + | transchapter = wrap_style ('trans-quoted-title', transchapter); |
− | end | + | if is_set (chapter) then |
| + | chapter = chapter .. ' ' .. transchapter; |
| + | else -- here when transchapter without chapter or script-chapter |
| + | chapter = transchapter; |
| + | chapter_error = ' ' .. set_error ('trans_missing_title', {'chapter'}); |
| end | | end |
− | i=i+1; -- bump our index
| |
| end | | end |
| + | |
| + | -- if is_set (chapterurl) then |
| + | -- chapter = external_link (chapterurl, chapter, chapter_url_source, access); -- adds bare_url_missing_title error if appropriate |
| + | -- end |
| + | |
| + | return chapter .. chapter_error; |
| end | | end |
| | | |
| | | |
− | --[[--------------------------< A R G U M E N T _ W R A P P E R >---------------------------------------------- | + | --[[--------------------------< H A S _ I N V I S I B L E _ C H A R S >---------------------------------------- |
| | | |
− | Argument wrapper. This function provides support for argument mapping defined in the configuration file so that
| + | This function searches a parameter's value for nonprintable or invisible characters. The search stops at the |
− | multiple names can be transparently aliased to single internal variable.
| + | first match. |
| | | |
− | ]]
| + | This function will detect the visible replacement character when it is part of the wikisource. |
| | | |
− | local function argument_wrapper( args ) | + | Detects but ignores nowiki and math stripmarkers. Also detects other named stripmarkers (gallery, math, pre, ref) |
− | local origin = {}; | + | 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 stripmarker |
| + | that was detected along with its position (or, for multi-byte characters, the position of its first byte) in the |
| + | parameter 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; |
| | | |
− | return setmetatable({ | + | capture = string.match (v, '[%w%p ]*'); -- Test for values that are simple ASCII text and bypass other tests if true |
− | ORIGIN = function( self, k ) | + | if capture == v then -- if same there are no unicode characters |
− | local dummy = self[k]; --force the variable to be loaded.
| + | return; |
− | return origin[k]; | + | end |
| + | |
| + | 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 == 'zero width joiner') then -- if we found a zero width joiner character |
| + | if mw.ustring.find (v, cfg.indic_script) then -- its ok if one of the indic scripts |
| + | position = nil; -- unset position |
| + | end |
| end | | end |
− | }, | + | |
| + | if position then |
| + | if 'nowiki' == capture or 'math' == capture or -- nowiki and math stripmarkers (not an error condition) |
| + | ('templatestyles' == capture and in_array (param, {'id', 'quote'})) then -- templatestyles stripmarker allowed in these parameters |
| + | stripmarker = true; -- set a flag |
| + | elseif true == stripmarker and 'delete' == 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, { set_error( 'invisible_char', {err_msg, wrap_style ('parameter', param), position}, true ) } ); -- add error message |
| + | return; -- and done with this parameter |
| + | end |
| + | end |
| + | i=i+1; -- bump our index |
| + | end |
| + | end |
| + | |
| + | |
| + | --[[--------------------------< A R G U M E N T _ W R A P P E R >---------------------------------------------- |
| + | |
| + | Argument wrapper. This function provides support for argument mapping defined in the configuration file so that |
| + | multiple names can be transparently aliased to single internal variable. |
| + | |
| + | ]] |
| + | |
| + | local function argument_wrapper( args ) |
| + | local origin = {}; |
| + | |
| + | return setmetatable({ |
| + | ORIGIN = function( self, k ) |
| + | local dummy = self[k]; --force the variable to be loaded. |
| + | return origin[k]; |
| + | end |
| + | }, |
| { | | { |
| __index = function ( tbl, k ) | | __index = function ( tbl, k ) |
Sətir 744: |
Sətir 825: |
| -- maybe let through instead of raising an error? | | -- maybe let through instead of raising an error? |
| -- v, origin[k] = args[k], k; | | -- v, origin[k] = args[k], k; |
− | error( cfg.messages['unknown_argument_map'] ); | + | error( cfg.messages['unknown_argument_map'] .. ': ' .. k); |
| end | | end |
| | | |
Sətir 760: |
Sətir 841: |
| | | |
| | | |
− | --[[--------------------------< V A L I D A T E >-------------------------------------------------------------- | + | --[[--------------------------< N O W R A P _ D A T E >-------------------------------------------------------- |
| | | |
− | Looks for a parameter's name in one of several whitelists.
| + | When date is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM-DD</span>. When date is DD MMMM YYYY or is |
| + | MMMM DD, YYYY then wrap in nowrap span: <span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY |
| + | |
| + | DOES NOT yet support MMMM YYYY or any of the date ranges. |
| | | |
− | Parameters in the whitelist can have three values:
| |
− | true - active, supported parameters
| |
− | false - deprecated, supported parameters
| |
− | nil - unsupported parameters
| |
− |
| |
| ]] | | ]] |
| | | |
− | local function validate( name, cite_class ) | + | local function nowrap_date (date) |
− | local name = tostring( name ); | + | local cap=''; |
− | local state; | + | local cap2=''; |
| + | |
| + | if date:match("^%d%d%d%d%-%d%d%-%d%d$") then |
| + | date = substitute (cfg.presentation['nowrap1'], date); |
| + | |
| + | elseif date:match("^%a+%s*%d%d?,%s+%d%d%d%d$") or date:match ("^%d%d?%s*%a+%s+%d%d%d%d$") then |
| + | cap, cap2 = string.match (date, "^(.*)%s+(%d%d%d%d)$"); |
| + | date = substitute (cfg.presentation['nowrap2'], {cap, cap2}); |
| + | end |
| | | |
− | if in_array (cite_class, {'arxiv', 'biorxiv', 'citeseerx'}) then -- limited parameter sets allowed for these templates | + | return date; |
− | state = whitelist.limited_basic_arguments[ name ];
| + | end |
− | if true == state then return true; end -- valid actively supported parameter
| + | |
− | if false == state then
| |
− | deprecated_parameter (name); -- parameter is deprecated but still supported
| |
− | return true;
| |
− | end
| |
| | | |
− | if 'arxiv' == cite_class then -- basic parameters unique to these templates
| + | --[[--------------------------< S E T _ T I T L E T Y P E >---------------------------------------------------- |
− | state = whitelist.arxiv_basic_arguments[name];
| |
− | end
| |
− | if 'biorxiv' == cite_class then
| |
− | state = whitelist.biorxiv_basic_arguments[name];
| |
− | end
| |
− | if 'citeseerx' == cite_class then
| |
− | state = whitelist.citeseerx_basic_arguments[name];
| |
− | end
| |
| | | |
− | if true == state then return true; end -- valid actively supported parameter
| + | This function sets default title types (equivalent to the citation including |type=<default value>) for those templates that have defaults. |
− | if false == state then | + | Also handles the special case where it is desirable to omit the title type from the rendered citation (|type=none). |
− | deprecated_parameter (name); -- parameter is deprecated but still supported | + | |
− | return true;
| + | ]] |
| + | |
| + | local function set_titletype (cite_class, title_type) |
| + | if is_set(title_type) then |
| + | if "none" == title_type then |
| + | title_type = ""; -- if |type=none then type parameter not displayed |
| end | | end |
− | -- limited enumerated parameters list
| + | return title_type; -- if |type= has been set to any other value use that value |
− | name = name:gsub( "%d+", "#" ); -- replace digit(s) with # (last25 becomes last#)
| |
− | state = whitelist.limited_numbered_arguments[ name ];
| |
− | if true == state then return true; end -- valid actively supported parameter | |
− | if false == state then
| |
− | deprecated_parameter (name); -- parameter is deprecated but still supported
| |
− | return true;
| |
− | end
| |
− | | |
− | return false; -- not supported because not found or name is set to nil
| |
− | end -- end limited parameter-set templates
| |
− |
| |
− | state = whitelist.basic_arguments[ name ]; -- all other templates; all normal parameters allowed
| |
− |
| |
− | if true == state then return true; end -- valid actively supported parameter
| |
− | if false == state then
| |
− | deprecated_parameter (name); -- parameter is deprecated but still supported
| |
− | return true;
| |
| end | | end |
− | -- all enumerated parameters allowed
| |
− | name = name:gsub( "%d+", "#" ); -- replace digit(s) with # (last25 becomes last#
| |
− | state = whitelist.numbered_arguments[ name ];
| |
| | | |
− | if true == state then return true; end -- valid actively supported parameter | + | return cfg.title_types [cite_class] or ''; -- set template's default title type; else empty string for concatenation |
− | if false == state then
| |
− | deprecated_parameter (name); -- parameter is deprecated but still supported
| |
− | return true;
| |
− | end
| |
− |
| |
− | return false; -- not supported because not found or name is set to nil
| |
| end | | end |
| | | |
| | | |
− | --[[--------------------------< N O W R A P _ D A T E >-------------------------------------------------------- | + | --[[--------------------------< H Y P H E N _ T O _ D A S H >-------------------------------------------------- |
| | | |
− | When date is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM-DD</span>. When date is DD MMMM YYYY or is
| + | Converts a hyphen to a dash under certain conditions. The hyphen must separate like items; unlike items are |
− | MMMM DD, YYYY then wrap in nowrap span: <span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY
| + | returned unmodified. These forms are modified: |
| + | letter - letter (A - B) |
| + | digit - digit (4-5) |
| + | digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5) |
| + | letterdigit - letterdigit (A1-A5) (an optional separator between letter and digit is supported – a.1-a.5 or a-1-a-5) |
| + | digitletter - digitletter (5a - 5d) (an optional separator between letter and digit is supported – 5.a-5.d or 5-a-5-d) |
| + | |
| + | any other forms are returned unmodified. |
| | | |
− | DOES NOT yet support MMMM YYYY or any of the date ranges.
| + | str may be a comma- or semicolon-separated list |
| | | |
| ]] | | ]] |
| | | |
− | local function nowrap_date (date) | + | local function hyphen_to_dash( str ) |
− | local cap='';
| + | if not is_set (str) then |
− | local cap2='';
| + | return str; |
− | | + | end |
− | if date:match("^%d%d%d%d%-%d%d%-%d%d$") then | |
− | date = substitute (cfg.presentation['nowrap1'], date); | |
| | | |
− | elseif date:match("^%a+%s*%d%d?,%s+%d%d%d%d$") or date:match ("^%d%d?%s*%a+%s+%d%d%d%d$") then | + | str, count = str:gsub ('^%(%((.+)%)%)$', '%1'); -- remove accept-this-as-written markup when it wraps all of str |
− | cap, cap2 = string.match (date, "^(.*)%s+(%d%d%d%d)$");
| + | if 0 ~= count then -- non-zero when markup removed; zero else |
− | date = substitute (cfg.presentation['nowrap2'], {cap, cap2}); | + | return str; -- nothing to do, we're done |
| end | | end |
| | | |
− | return date; | + | str = str:gsub ('&[nm]dash;', {['–'] = '–', ['—'] = '—'}); -- replace — and – entities with their characters; semicolon mucks up the text.split |
| + | |
| + | local out = {}; |
| + | local list = mw.text.split (str, '%s*[,;]%s*'); -- split str at comma or semicolon separators if there are any |
| + | |
| + | for _, item in ipairs (list) do -- for each item in the list |
| + | if mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[%-–—]%s*%w*[%.%-]?%w+$') then -- if a hyphenated range or has endash or emdash separators |
| + | if item:match ('%a+[%.%-]?%d+%s*%-%s*%a+[%.%-]?%d+') or -- letterdigit hyphen letterdigit (optional separator between letter and digit) |
| + | item:match ('%d+[%.%-]?%a+%s*%-%s*%d+[%.%-]?%a+') or -- digitletter hyphen digitletter (optional separator between digit and letter) |
| + | item:match ('%d+[%.%-]%d+%s*%-%s*%d+[%.%-]%d+') or -- digit separator digit hyphen digit separator digit |
| + | item:match ('%d+%s*%-%s*%d+') or -- digit hyphen digit |
| + | item:match ('%a+%s*%-%s*%a+') then -- letter hyphen letter |
| + | item = item:gsub ('(%w*[%.%-]?%w+)%s*%-%s*(%w*[%.%-]?%w+)', '%1–%2'); -- replace hyphen, remove extraneous space characters |
| + | else |
| + | item = mw.ustring.gsub (item, '%s*[–—]%s*', '–'); -- for endash or emdash separated ranges, replace em with en, remove extraneous white space |
| + | end |
| + | end |
| + | item = item:gsub ('^%(%((.+)%)%)$', '%1'); -- remove the accept-this-as-written markup |
| + | table.insert (out, item); -- add the (possibly modified) item to the output table |
| + | end |
| + | |
| + | return table.concat (out, ', '); -- concatenate the output table into a comma separated string |
| end | | end |
| | | |
| | | |
− | --[[--------------------------< S E T _ T I T L E T Y P E >---------------------------------------------------- | + | --[[--------------------------< S A F E _ J O I N >------------------------------------------------------------ |
| | | |
− | This function sets default title types (equivalent to the citation including |type=<default value>) for those templates that have defaults.
| + | Joins a sequence of strings together while checking for duplicate separation characters. |
− | Also handles the special case where it is desirable to omit the title type from the rendered citation (|type=none).
| |
| | | |
| ]] | | ]] |
| | | |
− | local function set_titletype (cite_class, title_type) | + | local function safe_join( tbl, duplicate_char ) |
− | if is_set(title_type) then | + | local f = {}; -- create a function table appropriate to type of 'dupicate character' |
− | if "none" == title_type then | + | if 1 == #duplicate_char then -- for single byte ascii characters use the string library functions |
− | title_type = ""; -- if |type=none then type parameter not displayed | + | f.gsub=string.gsub |
| + | f.match=string.match |
| + | f.sub=string.sub |
| + | else -- for multi-byte characters use the ustring library functions |
| + | f.gsub=mw.ustring.gsub |
| + | f.match=mw.ustring.match |
| + | f.sub=mw.ustring.sub |
| end | | end |
− | return title_type; -- if |type= has been set to any other value use that value
| |
− | end
| |
| | | |
− | return cfg.title_types [cite_class] or ''; -- set template's default title type; else empty string for concatenation
| + | local str = ''; -- the output string |
− | end
| |
− | | |
− | | |
− | --[[--------------------------< H Y P H E N _ T O _ D A S H >--------------------------------------------------
| |
− | | |
− | Converts a hyphen to a dash
| |
− | | |
− | ]]
| |
− | | |
− | local function hyphen_to_dash( str )
| |
− | if not is_set(str) or str:match( "[%[%]{}<>]" ) ~= nil then
| |
− | return str;
| |
− | end
| |
− | return str:gsub( '-', '–' );
| |
− | end
| |
− | | |
− | | |
− | --[[--------------------------< S A F E _ J O I N >------------------------------------------------------------
| |
− | | |
− | Joins a sequence of strings together while checking for duplicate separation characters.
| |
− | | |
− | ]]
| |
− | | |
− | local function safe_join( 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 = ''; -- the output string | |
| local comp = ''; -- what does 'comp' mean? | | local comp = ''; -- what does 'comp' mean? |
| local end_chr = ''; | | local end_chr = ''; |
Sətir 920: |
Sətir 970: |
| end | | end |
| -- typically duplicate_char is sepc | | -- typically duplicate_char is sepc |
− | if comp:sub(1,1) == duplicate_char then -- is first charactier same as duplicate_char? why test first character? | + | if f.sub(comp, 1,1) == duplicate_char then -- is first character same as duplicate_char? why test first character? |
− | -- Because individual string segments often (always?) begin with terminal punct for th | + | -- Because individual string segments often (always?) begin with terminal punct for the |
| -- preceding segment: 'First element' .. 'sepc next element' .. etc? | | -- preceding segment: 'First element' .. 'sepc next element' .. etc? |
| trim = false; | | trim = false; |
− | end_chr = str:sub(-1,-1); -- get the last character of the output string | + | end_chr = f.sub(str, -1,-1); -- get the last character of the output string |
| -- str = str .. "<HERE(enchr=" .. end_chr.. ")" -- debug stuff? | | -- str = str .. "<HERE(enchr=" .. end_chr.. ")" -- debug stuff? |
| if end_chr == duplicate_char then -- if same as separator | | if end_chr == duplicate_char then -- if same as separator |
− | str = str:sub(1,-2); -- remove it | + | str = f.sub(str, 1,-2); -- remove it |
| elseif end_chr == "'" then -- if it might be wikimarkup | | elseif end_chr == "'" then -- if it might be wikimarkup |
− | if str:sub(-3,-1) == duplicate_char .. "''" then -- if last three chars of str are sepc'' | + | if f.sub(str, -3,-1) == duplicate_char .. "''" then -- if last three chars of str are sepc'' |
− | str = str:sub(1, -4) .. "''"; -- remove them and add back '' | + | str = f.sub(str, 1, -4) .. "''"; -- remove them and add back '' |
− | elseif str:sub(-5,-1) == duplicate_char .. "]]''" then -- if last five chars of str are sepc]]'' | + | elseif f.sub(str, -5,-1) == duplicate_char .. "]]''" then -- if last five chars of str are sepc]]'' |
| trim = true; -- why? why do this and next differently from previous? | | trim = true; -- why? why do this and next differently from previous? |
− | elseif str:sub(-4,-1) == duplicate_char .. "]''" then -- if last four chars of str are sepc]'' | + | elseif f.sub(str, -4,-1) == duplicate_char .. "]''" then -- if last four chars of str are sepc]'' |
| trim = true; -- same question | | trim = true; -- same question |
| end | | end |
| elseif end_chr == "]" then -- if it might be wikimarkup | | elseif end_chr == "]" then -- if it might be wikimarkup |
− | if str:sub(-3,-1) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink | + | if f.sub(str, -3,-1) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink |
| trim = true; | | trim = true; |
− | elseif str:sub(-3,-1) == duplicate_char .. '"]' then -- if last three chars of str are sepc"] quoted external link | + | elseif f.sub(str, -3,-1) == duplicate_char .. '"]' then -- if last three chars of str are sepc"] quoted external link |
| trim = true; | | trim = true; |
− | elseif str:sub(-2,-1) == duplicate_char .. "]" then -- if last two chars of str are sepc] external link | + | elseif f.sub(str, -2,-1) == duplicate_char .. "]" then -- if last two chars of str are sepc] external link |
| trim = true; | | trim = true; |
− | elseif str:sub(-4,-1) == duplicate_char .. "'']" then -- normal case when |url=something & |title=Title. | + | elseif f.sub(str, -4,-1) == duplicate_char .. "'']" then -- normal case when |url=something & |title=Title. |
| trim = true; | | trim = true; |
| end | | end |
| elseif end_chr == " " then -- if last char of output string is a space | | elseif end_chr == " " then -- if last char of output string is a space |
− | if str:sub(-2,-1) == duplicate_char .. " " then -- if last two chars of str are <sepc><space> | + | if f.sub(str, -2,-1) == duplicate_char .. " " then -- if last two chars of str are <sepc><space> |
− | str = str:sub(1,-3); -- remove them both | + | str = f.sub(str, 1,-3); -- remove them both |
| end | | end |
| end | | end |
Sətir 955: |
Sətir 1.005: |
| if value ~= comp then -- value does not equal comp when value contains html markup | | if value ~= comp then -- value does not equal comp when value contains html markup |
| local dup2 = duplicate_char; | | local dup2 = duplicate_char; |
− | if dup2:match( "%A" ) then dup2 = "%" .. dup2; end -- if duplicate_char not a letter then escape it | + | if f.match(dup2, "%A" ) then dup2 = "%" .. dup2; end -- if duplicate_char not a letter then escape it |
| | | |
− | value = value:gsub( "(%b<>)" .. dup2, "%1", 1 ) -- remove duplicate_char if it follows html markup | + | value = f.gsub(value, "(%b<>)" .. dup2, "%1", 1 ) -- remove duplicate_char if it follows html markup |
| else | | else |
− | value = value:sub( 2, -1 ); -- remove duplicate_char when it is first character | + | value = f.sub(value, 2, -1 ); -- remove duplicate_char when it is first character |
| end | | end |
| end | | end |
Sətir 967: |
Sətir 1.017: |
| end | | end |
| return str; | | return str; |
− | end | + | end |
| | | |
| | | |
Sətir 1.014: |
Sətir 1.064: |
| | | |
| local function is_good_vanc_name (last, first) | | local function is_good_vanc_name (last, first) |
− | local first, suffix = first:match ('(.-),?%s*([%dJS][%drndth]+)%.?$') or first; -- if first has something that looks like a generational suffix, get it | + | local first, suffix = first:match ('(.-),?%s*([%dJS][%drndth]+)%.?$') or first; -- if first has something that looks like a generational suffix, get it |
| | | |
| if is_set (suffix) then | | if is_set (suffix) then |
Sətir 1.105: |
Sətir 1.155: |
| | | |
| if 'vanc' == format then -- Vancouver-like author/editor name styling? | | if 'vanc' == format then -- Vancouver-like author/editor name styling? |
− | sep = ','; -- name-list separator between authors is a comma | + | sep = cfg.presentation['sep_nl_vanc']; -- name-list separator between authors is a comma |
− | namesep = ' '; -- last/first separator is a space | + | namesep = cfg.presentation['sep_name_vanc']; -- last/first separator is a space |
| else | | else |
− | sep = ';' -- name-list separator between authors is a semicolon | + | sep = cfg.presentation['sep_nl']; -- name-list separator between authors is a semicolon |
− | namesep = ', ' -- last/first separator is <comma><space> | + | namesep = cfg.presentation['sep_name']; -- last/first separator is <comma><space> |
| end | | end |
| | | |
Sətir 1.155: |
Sətir 1.205: |
| if count > 0 then | | if count > 0 then |
| if count > 1 and is_set(lastauthoramp) and not etal then | | if count > 1 and is_set(lastauthoramp) and not etal then |
− | text[#text-2] = " & "; -- replace last separator with ampersand text | + | text[#text-2] = ", "; -- replace last separator with ampersand text |
| end | | end |
| text[#text] = nil; -- erase the last separator | | text[#text] = nil; -- erase the last separator |
Sətir 1.194: |
Sətir 1.244: |
| --[[--------------------------< N A M E _ H A S _ E T A L >---------------------------------------------------- | | --[[--------------------------< N A M E _ H A S _ E T A L >---------------------------------------------------- |
| | | |
− | Evaluates the content of author and editor name parameters for variations on the theme of et al. If found, | + | Evaluates the content of name parameters (author, editor, etc) for variations on the theme of et al. If found, |
| the et al. is removed, a flag is set to true and the function returns the modified name and the flag. | | the et al. is removed, a flag is set to true and the function returns the modified name and the flag. |
| | | |
| This function never sets the flag to false but returns it's previous state because it may have been set by | | This function never sets the flag to false but returns it's previous state because it may have been set by |
− | previous passes through this function or by the parameters |display-authors=etal or |display-editors=etal | + | previous passes through this function or by the associated |display-<names>=etal parameter |
| | | |
| ]] | | ]] |
| | | |
− | local function name_has_etal (name, etal, nocat) | + | local function name_has_etal (name, etal, nocat, param) |
| | | |
| if is_set (name) then -- name can be nil in which case just return | | if is_set (name) then -- name can be nil in which case just return |
− | local etal_pattern = "[;,]? *[\"']*%f[%a][Ee][Tt] *[Aa][Ll][%.\"']*$" -- variations on the 'et al' theme | + | local patterns = cfg.et_al_patterns; --get patterns from configuration |
− | local others_pattern = "[;,]? *%f[%a]and [Oo]thers"; -- and alternate to et al.
| |
| | | |
− | if name:match (etal_pattern) then -- variants on et al. | + | for _, pattern in ipairs (patterns) do -- loop through all of the patterns |
− | name = name:gsub (etal_pattern, ''); -- if found, remove
| + | if name:match (pattern) then -- if this 'et al' pattern is found in name |
− | etal = true; -- set flag (may have been set previously here or by |display-authors=etal)
| + | name = name:gsub (pattern, ''); -- remove the offending text |
− | if not nocat then -- no categorization for |vauthors= | + | etal = true; -- set flag (may have been set previously here or by |display-<names>=etal) |
− | add_maint_cat ('etal'); -- and add a category if not already added
| + | if not nocat then -- no categorization for |vauthors= |
− | end
| + | table.insert( z.message_tail, {set_error ('etal', {param})}); -- and set an error if not added |
− | elseif name:match (others_pattern) then -- if not 'et al.', then 'and others'?
| + | end |
− | name = name:gsub (others_pattern, ''); -- if found, remove
| |
− | etal = true; -- set flag (may have been set previously here or by |display-authors=etal)
| |
− | if not nocat then -- no categorization for |vauthors=
| |
− | add_maint_cat ('etal'); -- and add a category if not already added
| |
| end | | end |
| end | | end |
| end | | end |
| + | |
| return name, etal; -- | | return name, etal; -- |
| end | | end |
Sətir 1.236: |
Sətir 1.282: |
| local function name_has_ed_markup (name, list_name) | | local function name_has_ed_markup (name, list_name) |
| local _, pattern; | | local _, pattern; |
− | local patterns = { -- these patterns match annotations at end of name | + | local patterns = cfg.editor_markup_patterns; -- get patterns from configuration |
− | '%f[%(%[][%(%[]%s*[Ee][Dd][Ss]?%.?%s*[%)%]]?$', -- (ed) or (eds): leading '(', case insensitive 'ed', optional 's', '.' and/or ')'
| |
− | '[,%.%s]%f[e]eds?%.?$', -- ed or eds: without '('or ')'; case sensitive (ED could be initials Ed could be name)
| |
− | '%f[%(%[][%(%[]%s*[Ee][Dd][Ii][Tt][Oo][Rr][Ss]?%.?%s*[%)%]]?$', -- (editor) or (editors): leading '(', case insensitive, optional '.' and/or ')'
| |
− | '[,%.%s]%f[Ee][Ee][Dd][Ii][Tt][Oo][Rr][Ss]?%.?$', -- editor or editors: without '('or ')'; case insensitive
| |
− |
| |
− | -- these patterns match annotations at beginning of name
| |
− | '^eds?[%.,;]', -- ed. or eds.: lower case only, optional 's', requires '.'
| |
− | '^[%(%[]%s*[Ee][Dd][Ss]?%.?%s*[%)%]]', -- (ed) or (eds): also sqare brackets, case insensitive, optional 's', '.'
| |
− | '^[%(%[]?%s*[Ee][Dd][Ii][Tt][Oo][Rr][Ss]?%A', -- (editor or (editors: also sq brackets, case insensitive, optional brackets, 's'
| |
− | '^[%(%[]?%s*[Ee][Dd][Ii][Tt][Ee][Dd]%A', -- (edited: also sq brackets, case insensitive, optional brackets
| |
− | }
| |
| | | |
| if is_set (name) then | | if is_set (name) then |
Sətir 1.273: |
Sətir 1.308: |
| if is_set (name) then | | if is_set (name) then |
| _, count = name:gsub ('[;,]', ''); -- count the number of separator-like characters | | _, count = name:gsub ('[;,]', ''); -- count the number of separator-like characters |
− |
| |
− | if 1 < count then -- param could be |author= or |editor= so one separator character is acceptable
| |
− | add_maint_cat ('mult_names', cfg.special_case_translation [list_name]); -- more than one separator indicates multiple names so add a maint cat for this template
| |
− | end
| |
| end | | end |
| return name; -- and done | | return name; -- and done |
Sətir 1.335: |
Sətir 1.366: |
| local etal=false; -- return value set to true when we find some form of et al. in an author parameter | | local etal=false; -- return value set to true when we find some form of et al. in an author parameter |
| | | |
− | local err_msg_list_name = list_name:match ("(%w+)List") .. 's list'; -- modify AuthorList or EditorList for use in error messages if necessary | + | local last_alias, first_alias; -- selected parameter aliases used in error messaging |
| while true do | | while true do |
− | last = select_one( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i ); -- search through args for name components beginning at 1 | + | last, last_alias = select_one( args, cfg.aliases[list_name .. '-Last'], 'redundant_parameters', i ); -- search through args for name components beginning at 1 |
− | first = select_one( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ); | + | first, first_alias = select_one( args, cfg.aliases[list_name .. '-First'], 'redundant_parameters', i ); |
| link = select_one( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ); | | link = select_one( args, cfg.aliases[list_name .. '-Link'], 'redundant_parameters', i ); |
| mask = select_one( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i ); | | mask = select_one( args, cfg.aliases[list_name .. '-Mask'], 'redundant_parameters', i ); |
| | | |
− | last, etal = name_has_etal (last, etal, false); -- find and remove variations on et al. | + | last, etal = name_has_etal (last, etal, false, last_alias); -- find and remove variations on et al. |
− | first, etal = name_has_etal (first, etal, false); -- find and remove variations on et al. | + | first, etal = name_has_etal (first, etal, false, first_alias); -- find and remove variations on et al. |
| last, first= name_checks (last, first, list_name); -- multiple names, extraneous annotation, etc checks | | last, first= name_checks (last, first, list_name); -- multiple names, extraneous annotation, etc checks |
| | | |
| if first and not last then -- if there is a firstn without a matching lastn | | if first and not last then -- if there is a firstn without a matching lastn |
− | table.insert( z.message_tail, { set_error( 'first_missing_last', {err_msg_list_name, i}, true ) } ); -- add this error message | + | table.insert( z.message_tail, { set_error( 'first_missing_last', {first_alias, first_alias:gsub('first', 'last')}, true ) } ); -- add this error message |
| elseif not first and not last then -- if both firstn and lastn aren't found, are we done? | | elseif not first and not last then -- if both firstn and lastn aren't found, are we done? |
| count = count + 1; -- number of times we haven't found last and first | | count = count + 1; -- number of times we haven't found last and first |
Sətir 1.359: |
Sətir 1.390: |
| n = n + 1; -- point to next location in the names table | | n = n + 1; -- point to next location in the names table |
| if 1 == count then -- if the previous name was missing | | if 1 == count then -- if the previous name was missing |
− | table.insert( z.message_tail, { set_error( 'missing_name', {err_msg_list_name, i-1}, true ) } ); -- add this error message | + | table.insert( z.message_tail, { set_error( 'missing_name', {list_name:match ("(%w+)List"):lower(), i-1}, true ) } ); -- add this error message |
| end | | end |
| count = 0; -- reset the counter, we're looking for two consecutive missing names | | count = 0; -- reset the counter, we're looking for two consecutive missing names |
Sətir 1.379: |
Sətir 1.410: |
| return the original language name string. | | return the original language name string. |
| | | |
− | mw.language.fetchLanguageNames(<local wiki language>, 'all') return a list of languages that in some cases may include | + | mw.language.fetchLanguageNames(<local wiki language>, 'all') returns a list of languages that in some cases may include |
| extensions. For example, code 'cbk-zam' and its associated name 'Chavacano de Zamboanga' (MediaWiki does not support | | extensions. For example, code 'cbk-zam' and its associated name 'Chavacano de Zamboanga' (MediaWiki does not support |
− | code 'cbk' or name 'Chavacano'. | + | code 'cbk' or name 'Chavacano'. Most (all?) of these languages are not used a 'language' codes per se, rather they |
| + | are used as sub-domain names: cbk-zam.wikipedia.org. A list of language names and codes supported by fetchLanguageNames() |
| + | can be found at Template:Citation Style documentation/language/doc |
| | | |
− | Names but that are included in the list will be found if that name is provided in the |language= parameter. For example, | + | Names that are included in the list will be found if that name is provided in the |language= parameter. For example, |
| if |language=Chavacano de Zamboanga, that name will be found with the associated code 'cbk-zam'. When names are found | | if |language=Chavacano de Zamboanga, that name will be found with the associated code 'cbk-zam'. When names are found |
− | and the associated code is not two or three characters, this function returns only the Wikimedia language name. | + | and the associated code is not two or three characters, this function returns only the WikiMedia language name. |
| + | |
| + | Some language names have multiple entries under different codes: |
| + | Aromanian has code rup and code roa-rup |
| + | When this occurs, this function returns the language name and the 2- or 3-character code |
| | | |
| Adapted from code taken from Module:Check ISO 639-1. | | Adapted from code taken from Module:Check ISO 639-1. |
Sətir 1.392: |
Sətir 1.429: |
| | | |
| local function get_iso639_code (lang, this_wiki_code) | | local function get_iso639_code (lang, this_wiki_code) |
− | if 'bangla' == lang:lower() then -- special case related to Wikimedia remap of code 'bn' at mw:Extension:CLDR | + | if cfg.lang_name_remap[lang:lower()] then -- if there is a remapped name (because MediaWiki uses something that we don't think is correct) |
− | return 'Bengali', 'bn'; -- make sure rendered version is properly capitalized | + | return cfg.lang_name_remap[lang:lower()][1], cfg.lang_name_remap[lang:lower()][2]; -- for this language 'name', return a possibly new name and appropriate code |
| end | | end |
| | | |
| + | local ietf_code; -- because some languages have both ietf-like codes and iso 639-like codes |
| + | local ietf_name; |
| + | |
| local languages = mw.language.fetchLanguageNames(this_wiki_code, 'all') -- get a list of language names known to Wikimedia | | local languages = mw.language.fetchLanguageNames(this_wiki_code, 'all') -- get a list of language names known to Wikimedia |
| -- ('all' is required for North Ndebele, South Ndebele, and Ojibwa) | | -- ('all' is required for North Ndebele, South Ndebele, and Ojibwa) |
| local langlc = mw.ustring.lower(lang); -- lower case version for comparisons | | local langlc = mw.ustring.lower(lang); -- lower case version for comparisons |
− |
| + | |
| for code, name in pairs(languages) do -- scan the list to see if we can find our language | | for code, name in pairs(languages) do -- scan the list to see if we can find our language |
| if langlc == mw.ustring.lower(name) then | | if langlc == mw.ustring.lower(name) then |
− | if 2 ~= code:len() and 3 ~= code:len() then -- two- or three-character codes only; extensions not supported | + | if 2 == code:len() or 3 == code:len() then -- two- or three-character codes only; extensions not supported |
− | return name; -- so return the name but not the code | + | return name, code; -- so return the name and the code |
| end | | end |
− | return name, code; -- found it, return name to ensure proper capitalization and the the code | + | ietf_code = code; -- remember that we found an ietf-like code and save its name |
| + | ietf_name = name; -- but keep looking for a 2- or 3-char code |
| end | | end |
| end | | end |
− | return lang; -- not valid language; return language in original case and nil for the code | + | -- didn't find name with 2- or 3-char code; if ietf-like code found return |
| + | return ietf_code and ietf_name or lang; -- associated name; return original language text else |
| end | | end |
| | | |
Sətir 1.442: |
Sətir 1.484: |
| | | |
| for _, lang in ipairs (names_table) do -- reuse lang | | for _, lang in ipairs (names_table) do -- reuse lang |
| + | name = cfg.lang_code_remap[lang:lower()]; -- first see if this is a code that is not supported by MediaWiki but is in remap |
| | | |
− | if lang:match ('^%a%a%-') then -- strip ietf language tags from code; TODO: is there a need to support 3-char with tag? | + | if name then -- there was a remapped code so |
− | lang = lang:match ('(%a%a)%-') -- keep only 639-1 code portion to lang; TODO: do something with 3166 alpha 2 country code?
| + | lang = lang:gsub ('^(%a%a%a?)%-.*', '%1'); -- strip ietf tags from code |
| + | else |
| + | if lang:match ('^%a%a%-') then -- strip ietf tags from code; TODO: is there a need to support 3-char with tag? |
| + | lang = lang:match ('(%a%a)%-') -- keep only 639-1 code portion to lang; TODO: do something with 3166 alpha 2 country code? |
| + | end |
| + | if 2 == lang:len() or 3 == lang:len() then -- if two-or three-character code |
| + | name = mw.language.fetchLanguageName (lang:lower(), this_wiki_code); -- get language name if |language= is a proper code |
| + | end |
| end | | end |
− | if 2 == lang:len() or 3 == lang:len() then -- if two-or three-character code
| + | |
− | name = mw.language.fetchLanguageName( lang:lower(), this_wiki_code); -- get language name if |language= is a proper code
| |
− | end
| |
− |
| |
| if is_set (name) then -- if |language= specified a valid code | | if is_set (name) then -- if |language= specified a valid code |
| code = lang:lower(); -- save it | | code = lang:lower(); -- save it |
Sətir 1.457: |
Sətir 1.504: |
| | | |
| if is_set (code) then -- only 2- or 3-character codes | | if is_set (code) then -- only 2- or 3-character codes |
− | if 'bn' == code then name = 'Bengali' end; -- override wikimedia when code is 'bn' | + | name = cfg.lang_code_remap[code] or name; -- override wikimedia when they misuse language codes/names -- add maint category if not already added |
− | if this_wiki_code ~= code then -- when the language is not the same as this wiki's language
| |
− | if 2 == code:len() then -- and is a two-character code
| |
− | add_prop_cat ('foreign_lang_source' .. code, {name, code}) -- categorize it
| |
− | else -- or is a recognized language (but has a three-character code)
| |
− | add_prop_cat ('foreign_lang_source_2' .. code, {code}) -- categorize it differently TODO: support mutliple three-character code categories per cs1|2 template
| |
− | end
| |
− | end
| |
− | else
| |
− | add_maint_cat ('unknown_lang'); -- add maint category if not already added
| |
| end | | end |
| | | |
Sətir 1.475: |
Sətir 1.513: |
| code = #language_list -- reuse code as number of languages in the list | | code = #language_list -- reuse code as number of languages in the list |
| if 2 >= code then | | if 2 >= code then |
− | name = table.concat (language_list, ' and ') -- insert '<space>and<space>' between two language names | + | name = table.concat (language_list, cfg.messages['parameter-pair-separator']) -- insert '<space>and<space>' between two language names |
| elseif 2 < code then | | elseif 2 < code then |
− | language_list[code] = 'and ' .. language_list[code]; -- prepend last name with 'and<space>' | + | name = table.concat (language_list, ', '); -- and concatenate with '<comma><space>' separators |
− | name = table.concat (language_list, ', ') -- and concatenate with '<comma><space>' separators | + | name = name:gsub (', ([^,]+)$', cfg.messages['parameter-final-separator'] .. '%1'); -- replace last '<comma><space>' separator with '<comma><space>and<space>' separator |
| end | | end |
| if this_wiki_name == name then | | if this_wiki_name == name then |
Sətir 1.493: |
Sətir 1.531: |
| | | |
| Set style settings for CS1 citation templates. Returns separator and postscript settings | | Set style settings for CS1 citation templates. Returns separator and postscript settings |
| + | At en.wiki, for cs1: |
| + | ps gets: '.' |
| + | sep gets: '.' |
| | | |
| ]] | | ]] |
Sətir 1.498: |
Sətir 1.539: |
| local function set_cs1_style (ps) | | local function set_cs1_style (ps) |
| if not is_set (ps) then -- unless explicitely set to something | | if not is_set (ps) then -- unless explicitely set to something |
− | ps = '.'; -- terminate the rendered citation with a period | + | ps = cfg.presentation['ps_cs1']; -- terminate the rendered citation |
| end | | end |
− | return '.', ps; -- separator is a full stop | + | return cfg.presentation['sep_cs1'], ps; -- element separator |
| end | | end |
| | | |
Sətir 1.507: |
Sətir 1.548: |
| | | |
| Set style settings for CS2 citation templates. Returns separator, postscript, ref settings | | Set style settings for CS2 citation templates. Returns separator, postscript, ref settings |
| + | At en.wiki, for cs2: |
| + | ps gets: '' (empty string - no terminal punctuation) |
| + | sep gets: ',' |
| | | |
| ]] | | ]] |
Sətir 1.512: |
Sətir 1.556: |
| local function set_cs2_style (ps, ref) | | local function set_cs2_style (ps, ref) |
| if not is_set (ps) then -- if |postscript= has not been set, set cs2 default | | if not is_set (ps) then -- if |postscript= has not been set, set cs2 default |
− | ps = ''; -- make sure it isn't nil | + | ps = cfg.presentation['ps_cs2']; -- terminate the rendered citation |
| end | | end |
| if not is_set (ref) then -- if |ref= is not set | | if not is_set (ref) then -- if |ref= is not set |
| ref = "harv"; -- set default |ref=harv | | ref = "harv"; -- set default |ref=harv |
| end | | end |
− | return ',', ps, ref; -- separator is a comma | + | return cfg.presentation['sep_cs2'], ps, ref; -- element separator |
| end | | end |
| | | |
Sətir 1.574: |
Sətir 1.618: |
| | | |
| local function is_pdf (url) | | local function is_pdf (url) |
− | return url:match ('%.pdf$') or url:match ('%.PDF$') or url:match ('%.pdf[%?#]') or url:match ('%.PDF[%?#]'); | + | return url:match ('%.pdf$') or url:match ('%.PDF$') or |
| + | url:match ('%.pdf[%?#]') or url:match ('%.PDF[%?#]') or |
| + | url:match ('%.PDF#') or url:match ('%.pdf#'); |
| end | | end |
| | | |
Sətir 1.602: |
Sətir 1.648: |
| | | |
| | | |
− | --[[--------------------------< G E T _ D I S P L A Y _ A U T H O R S _ E D I T O R S >------------------------ | + | --[[--------------------------< G E T _ D I S P L A Y _ N A M E S >-------------------------------------------- |
| | | |
| Returns a number that defines the number of names displayed for author and editor name lists and a boolean flag | | Returns a number that defines the number of names displayed for author and editor name lists and a boolean flag |
Sətir 1.625: |
Sətir 1.671: |
| ]] | | ]] |
| | | |
− | local function get_display_authors_editors (max, count, list_name, etal) | + | local function get_display_names (max, count, list_name, etal) |
| if is_set (max) then | | if is_set (max) then |
| if 'etal' == max:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings | | if 'etal' == max:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings |
Sətir 1.632: |
Sətir 1.678: |
| elseif max:match ('^%d+$') then -- if is a string of numbers | | elseif max:match ('^%d+$') then -- if is a string of numbers |
| max = tonumber (max); -- make it a number | | max = tonumber (max); -- make it a number |
− | if max >= count then -- if |display-xxxxors= value greater than or equal to number of authors/editors
| |
− | add_maint_cat ('disp_auth_ed', cfg.special_case_translation [list_name]);
| |
− | end
| |
| else -- not a valid keyword or number | | else -- not a valid keyword or number |
| table.insert( z.message_tail, { set_error( 'invalid_param_val', {'display-' .. list_name, max}, true ) } ); -- add error message | | table.insert( z.message_tail, { set_error( 'invalid_param_val', {'display-' .. list_name, max}, true ) } ); -- add error message |
Sətir 1.659: |
Sətir 1.702: |
| local good_pattern = '^P[^%.Pp]'; -- ok to begin with uppercase P: P7 (pg 7 of section P) but not p123 (page 123) TODO: add Gg for PG or Pg? | | local good_pattern = '^P[^%.Pp]'; -- ok to begin with uppercase P: P7 (pg 7 of section P) but not p123 (page 123) TODO: add Gg for PG or Pg? |
| local bad_pattern = '^[Pp]?[Pp]%.?[ %d]'; | | local bad_pattern = '^[Pp]?[Pp]%.?[ %d]'; |
− |
| |
− | if not page:match (good_pattern) and (page:match (bad_pattern) or page:match ('^[Pp]ages?')) then
| |
− | add_maint_cat ('extra_text');
| |
− | end
| |
| end | | end |
| | | |
Sətir 1.734: |
Sətir 1.773: |
| | | |
| vparam, etal = name_has_etal (vparam, etal, true); -- find and remove variations on et al. do not categorize (do it here because et al. might have a period) | | vparam, etal = name_has_etal (vparam, etal, true); -- 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, v_link_table); -- names are separated by commas | + | v_name_table = get_v_name_table (vparam, v_name_table, v_link_table); -- names are separated by commas |
| | | |
| for i, v_name in ipairs(v_name_table) do | | for i, v_name in ipairs(v_name_table) do |
Sətir 1.846: |
Sətir 1.885: |
| if not is_set (value) then | | if not is_set (value) then |
| return true; -- an empty parameter is ok | | return true; -- an empty parameter is ok |
− | elseif in_array(value:lower(), possible) then | + | elseif in_array (value, possible) then |
| return true; | | return true; |
| else | | else |
Sətir 1.897: |
Sətir 1.936: |
| end | | end |
| | | |
− | local vol = ''; | + | local vol = ''; -- here for all cites except magazine |
| | | |
| if is_set (volume) then | | if is_set (volume) then |
− | if (4 < mw.ustring.len(volume)) then | + | if volume:match ('^[MDCLXVI]+$') or volume:match ('^%d+$')then -- volume value is all digits or all uppercase roman numerals |
− | vol = substitute (cfg.messages['j-vol'], {sepc, volume}); | + | vol = substitute (cfg.presentation['vol-bold'], {sepc, hyphen_to_dash(volume)}); -- render in bold face |
− | else | + | elseif (4 < mw.ustring.len(volume)) then -- not all digits or roman numerals and longer than 4 characters |
− | vol = substitute (cfg.presentation['vol-bold'], {sepc, hyphen_to_dash(volume)}); | + | vol = substitute (cfg.messages['j-vol'], {sepc, volume}); -- not bold |
| + | else -- four or less characters |
| + | vol = substitute (cfg.presentation['vol-bold'], {sepc, hyphen_to_dash(volume)}); -- bold |
| end | | end |
| end | | end |
Sətir 1.940: |
Sətir 1.981: |
| end | | end |
| | | |
− | local is_journal = 'journal' == cite_class or (in_array (cite_class, {'citation', 'map'}) and 'journal' == origin); | + | local is_journal = 'journal' == cite_class or (in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin); |
| | | |
| if is_set (page) then | | if is_set (page) then |
Sətir 1.950: |
Sətir 1.991: |
| return substitute (cfg.messages['nopp'], {sepc, page}), '', '', ''; | | return substitute (cfg.messages['nopp'], {sepc, page}), '', '', ''; |
| end | | end |
− | elseif is_set(pages) then | + | end |
| + | if is_set(pages) then |
| if is_journal then | | if is_journal then |
| return substitute (cfg.messages['j-page(s)'], pages), '', '', ''; | | return substitute (cfg.messages['j-page(s)'], pages), '', '', ''; |
− | elseif tonumber(pages) ~= nil and not nopp then -- if pages is only digits, assume a single page number | + | elseif tonumber(pages) ~= nil and not nopp then -- if pages is only digits, assume a single page number |
− | return '', substitute (cfg.messages['p-prefix'], {sepc, pages}), '', ''; | + | return '', substitute (cfg.messages['pp-suffix'], {sepc, pages}), '', ''; |
| elseif not nopp then | | elseif not nopp then |
− | return '', substitute (cfg.messages['pp-prefix'], {sepc, pages}), '', ''; | + | return '', substitute (cfg.messages['pp-suffix'], {sepc, pages}), '', ''; |
| else | | else |
| return '', substitute (cfg.messages['nopp'], {sepc, pages}), '', ''; | | return '', substitute (cfg.messages['nopp'], {sepc, pages}), '', ''; |
Sətir 1.966: |
Sətir 2.008: |
| | | |
| | | |
− | --[=[-------------------------< A R C H I V E _ U R L _ C H E C K >-------------------------------------------- | + | --[[--------------------------< I N S O U R C E _ L O C _ G E T >---------------------------------------------- |
| + | |
| + | returns one of the in-source locators: page, pages, or at. |
| | | |
− | Check archive.org urls to make sure they at least look like they are pointing at valid archives and not to the
| + | If any of these are interwiki links to wikisource, returns the label portion of the interwikilink as plain text |
− | save snapshot url or to calendar pages. When the archive url is 'https://web.archive.org/save/' (or http://...)
| + | for use in COinS. This COinS thing is done because here we convert an interwiki link to and external link and |
− | archive.org saves a snapshot of the target page in the url. That is something that Wikipedia should not allow
| + | add an icon span around that; get_coins_pages() doesn't know about the span. TODO: should it? |
− | unwitting readers to do.
| |
| | | |
− | When the archive.org url does not have a complete timestamp, archive.org chooses a snapshot according to its own
| + | TODO: add support for sheet and sheets?; streamline; |
− | algorithm or provides a calendar 'search' result. [[WP:ELNO]] discourages links to search results.
| |
| | | |
− | This function looks at the value assigned to |archive-url= and returns empty strings for |archive-url= and
| + | TODO: make it so that this function returns only one of the three as the single in-source (the return value assigned |
− | |archive-date= and an error message when:
| + | to a new name)? |
− | |archive-url= holds an archive.org save command url
| |
− | |archive-url= is an archive.org url that does not have a complete timestamp (YYYYMMDDhhmmss 14 digits) in the
| |
− | correct place
| |
− | otherwise returns |archive-url= and |archive-date=
| |
| | | |
− | There are two mostly compatible archive.org urls:
| + | ]] |
− | //web.archive.org/<timestamp>... -- the old form
| |
− | //web.archive.org/web/<timestamp>... -- the new form
| |
| | | |
− | The old form does not support or map to the new form when it contains a display flag. There are four identified flags
| + | local function insource_loc_get (page, pages, at) |
− | ('id_', 'js_', 'cs_', 'im_') but since archive.org ignores others following the same form (two letters and an underscore)
| + | local ws_url, ws_label, coins_pages, L; -- for wikisource interwikilinks; TODO: this corrupts page metadata (span remains in place after cleanup; fix there?) |
− | we don't check for these specific flags but we do check the form.
| |
| | | |
− | This function supports a preview mode. When the article is rendered in preview mode, this funct may return a modified
| + | if is_set (page) then |
− | archive url:
| + | if is_set (pages) or is_set(at) then |
− | for save command errors, return undated wildcard (/*/)
| + | pages = ''; -- unset the others |
− | for timestamp errors when the timestamp has a wildcard, return the url unmodified
| + | at = ''; |
− | for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/)
| + | end |
| + | extra_text_in_page_check (page); -- add this page to maint cat if |page= value begins with what looks like p. or pp. |
| | | |
− | ]=] | + | ws_url, ws_label, L = wikisource_url_make (page); -- make ws url from |page= interwiki link; link portion L becomes tool tip label |
| + | if ws_url then |
| + | page = external_link (ws_url, ws_label .. ' ', 'ws link in page'); -- space char after label to move icon away from in-source text; TODO: a better way to do this? |
| + | page = substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, page}); |
| + | coins_pages = ws_label; |
| + | end |
| + | elseif is_set (pages) then |
| + | if is_set (at) then |
| + | at = ''; -- unset |
| + | end |
| + | extra_text_in_page_check (pages); -- add this page to maint cat if |pages= value begins with what looks like p. or pp. |
| | | |
− | local function archive_url_check (url, date)
| + | ws_url, ws_label, L = wikisource_url_make (pages); -- make ws url from |pages= interwiki link; link portion L becomes tool tip label |
− | local err_msg = ''; -- start with the error message empty
| + | if ws_url then |
− | local path, timestamp, flag; -- portions of the archive.or url
| + | pages = external_link (ws_url, ws_label .. ' ', 'ws link in pages'); -- space char after label to move icon away from in-source text; TODO: a better way to do this? |
| + | pages = substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, pages}); |
| + | coins_pages = ws_label; |
| + | end |
| + | elseif is_set (at) then |
| + | ws_url, ws_label, L = wikisource_url_make (at); -- make ws url from |at= interwiki link; link portion L becomes tool tip label |
| + | if ws_url then |
| + | at = external_link (ws_url, ws_label .. ' ', 'ws link in at'); -- space char after label to move icon away from in-source text; TODO: a better way to do this? |
| + | at = substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, at}); |
| + | coins_pages = ws_label; |
| + | end |
| + | end |
| | | |
− | if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then -- also deprecated liveweb Wayback machine url | + | return page, pages, at, coins_pages; |
− | return url, date; -- not an archive.org archive, return ArchiveURL and ArchiveDate
| + | end |
− | end
| + | |
| + | |
| + | |
| + | --[=[-------------------------< A R C H I V E _ U R L _ C H E C K >-------------------------------------------- |
| + | |
| + | Check archive.org urls to make sure they at least look like they are pointing at valid archives and not to the |
| + | save snapshot url or to calendar pages. When the archive url is 'https://web.archive.org/save/' (or http://...) |
| + | archive.org saves a snapshot of the target page in the url. That is something that Wikipedia should not allow |
| + | unwitting readers to do. |
| | | |
− | if url:match('//web%.archive%.org/save/') then -- if a save command url, we don't want to allow saving of the target page
| + | When the archive.org url does not have a complete timestamp, archive.org chooses a snapshot according to its own |
− | err_msg = 'save command';
| + | algorithm or provides a calendar 'search' result. [[WP:ELNO]] discourages links to search results. |
− | url = url:gsub ('(//web%.archive%.org)/save/', '%1/*/', 1); -- for preview mode: modify ArchiveURL
| |
− | elseif url:match('//liveweb%.archive%.org/') then
| |
− | err_msg = 'liveweb';
| |
− | else
| |
− | path, timestamp, flag = url:match('//web%.archive%.org/([^%d]*)(%d+)([^/]*)/'); -- split out some of the url parts for evaluation
| |
− |
| |
− | if not is_set(timestamp) or 14 ~= timestamp:len() then -- path and flag optional, must have 14-digit timestamp here
| |
− | err_msg = 'timestamp';
| |
− | if '*' ~= flag then
| |
− | url=url:gsub ('(//web%.archive%.org/[^%d]*%d?%d?%d?%d?%d?%d?)[^/]*', '%1*', 1) -- for preview, modify ts to be yearmo* max (0-6 digits plus splat)
| |
− | end
| |
− | elseif is_set(path) and 'web/' ~= path then -- older archive urls do not have the extra 'web/' path element
| |
− | err_msg = 'path';
| |
− | elseif is_set (flag) and not is_set (path) then -- flag not allowed with the old form url (without the 'web/' path element)
| |
− | err_msg = 'flag';
| |
− | elseif is_set (flag) and not flag:match ('%a%a_') then -- flag if present must be two alpha characters and underscore (requires 'web/' path element)
| |
− | err_msg = 'flag';
| |
− | else
| |
− | return url, date; -- return archiveURL and ArchiveDate
| |
− | end
| |
− | end
| |
− | -- if here, something not right so
| |
− | table.insert( z.message_tail, { set_error( 'archive_url', {err_msg}, true ) } ); -- add error message and
| |
− | if is_set (Frame:preprocess('{{REVISIONID}}')) then
| |
− | return '', ''; -- return empty strings for archiveURL and ArchiveDate
| |
− | else
| |
− | return url, date; -- preview mode so return archiveURL and ArchiveDate
| |
− | end
| |
− | end
| |
| | | |
| + | This function looks at the value assigned to |archive-url= and returns empty strings for |archive-url= and |
| + | |archive-date= and an error message when: |
| + | |archive-url= holds an archive.org save command url |
| + | |archive-url= is an archive.org url that does not have a complete timestamp (YYYYMMDDhhmmss 14 digits) in the |
| + | correct place |
| + | otherwise returns |archive-url= and |archive-date= |
| | | |
− | --[[--------------------------< M I S S I N G _ P I P E _ C H E C K >------------------------------------------ | + | There are two mostly compatible archive.org urls: |
| + | //web.archive.org/<timestamp>... -- the old form |
| + | //web.archive.org/web/<timestamp>... -- the new form |
| | | |
− | Look at the contents of a parameter. If the content has a string of characters and digits followed by an equal
| + | The old form does not support or map to the new form when it contains a display flag. There are four identified flags |
− | sign, compare the alphanumeric string to the list of cs1|2 parameters. If found, then the string is possibly a
| + | ('id_', 'js_', 'cs_', 'im_') but since archive.org ignores others following the same form (two letters and an underscore) |
− | parameter that is missing its pipe:
| + | we don't check for these specific flags but we do check the form. |
− | {{cite ... |title=Title access-date=2016-03-17}}
| |
| | | |
− | cs1|2 shares some parameter names with xml/html atributes: class=, title=, etc. To prevent false positives xml/html
| + | This function supports a preview mode. When the article is rendered in preview mode, this funct may return a modified |
− | tags are removed before the search.
| + | archive url: |
| + | for save command errors, return undated wildcard (/*/) |
| + | for timestamp errors when the timestamp has a wildcard, return the url unmodified |
| + | for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/) |
| | | |
− | If a missing pipe is detected, this function adds the missing pipe maintenance category.
| + | ]=] |
| | | |
− | ]]
| + | local function archive_url_check (url, date) |
− | | + | local err_msg = ''; -- start with the error message empty |
− | local function missing_pipe_check (value) | + | local path, timestamp, flag; -- portions of the archive.or url |
− | local capture; | + | |
− | value = value:gsub ('%b<>', ''); -- remove xml/html tags because attributes: class=, title=, etc
| + | if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then -- also deprecated liveweb Wayback machine url |
− | | + | return url, date; -- not an archive.org archive, return ArchiveURL and ArchiveDate |
− | capture = value:match ('%s+(%a[%a%d]+)%s*=') or value:match ('^(%a[%a%d]+)%s*='); -- find and categorize parameters with possible missing pipes | |
− | if capture and validate (capture) then -- if the capture is a valid parameter name
| |
− | add_maint_cat ('missing_pipe'); | |
| end | | end |
− | end | + | |
− | | + | if url:match('//web%.archive%.org/save/') then -- if a save command url, we don't want to allow saving of the target page |
− | | + | err_msg = 'save command'; |
− | --[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------ | + | url = url:gsub ('(//web%.archive%.org)/save/', '%1/*/', 1); -- for preview mode: modify ArchiveURL |
− | | + | elseif url:match('//liveweb%.archive%.org/') then |
− | This is the main function doing the majority of the citation formatting. | + | err_msg = 'liveweb'; |
| + | else |
| + | path, timestamp, flag = url:match('//web%.archive%.org/([^%d]*)(%d+)([^/]*)/'); -- split out some of the url parts for evaluation |
| + | |
| + | if not is_set(timestamp) or 14 ~= timestamp:len() then -- path and flag optional, must have 14-digit timestamp here |
| + | err_msg = 'timestamp'; |
| + | if '*' ~= flag then |
| + | url=url:gsub ('(//web%.archive%.org/[^%d]*%d?%d?%d?%d?%d?%d?)[^/]*', '%1*', 1) -- for preview, modify ts to be yearmo* max (0-6 digits plus splat) |
| + | end |
| + | elseif is_set(path) and 'web/' ~= path then -- older archive urls do not have the extra 'web/' path element |
| + | err_msg = 'path'; |
| + | elseif is_set (flag) and not is_set (path) then -- flag not allowed with the old form url (without the 'web/' path element) |
| + | err_msg = 'flag'; |
| + | elseif is_set (flag) and not flag:match ('%a%a_') then -- flag if present must be two alpha characters and underscore (requires 'web/' path element) |
| + | err_msg = 'flag'; |
| + | else |
| + | return url, date; -- return archiveURL and ArchiveDate |
| + | end |
| + | end |
| + | -- if here, something not right so |
| + | table.insert( z.message_tail, { set_error( 'archive_url', {err_msg}, true ) } ); -- add error message and |
| + | if is_set (Frame:preprocess('{{REVISIONID}}')) then |
| + | return '', ''; -- return empty strings for archiveURL and ArchiveDate |
| + | else |
| + | return url, date; -- preview mode so return archiveURL and ArchiveDate |
| + | end |
| + | end |
| + | |
| + | |
| + | --[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------ |
| + | |
| + | This is the main function doing the majority of the citation formatting. |
| | | |
| ]] | | ]] |
Sətir 2.103: |
Sətir 2.177: |
| elseif 3 == selected then | | elseif 3 == selected then |
| Authors = A['Authors']; -- use content of |authors= | | Authors = A['Authors']; -- use content of |authors= |
− | if 'authors' == A:ORIGIN('Authors') then -- but add a maint cat if the parameter is |authors=
| |
− | add_maint_cat ('authors'); -- because use of this parameter is discouraged; what to do about the aliases is a TODO:
| |
− | end
| |
| end | | end |
| if is_set (Collaboration) then | | if is_set (Collaboration) then |
Sətir 2.127: |
Sətir 2.198: |
| elseif 3 == selected then | | elseif 3 == selected then |
| Editors = A['Editors']; -- use content of |editors= | | Editors = A['Editors']; -- use content of |editors= |
− | add_maint_cat ('editors'); -- but add a maint cat because use of this parameter is discouraged
| |
| end | | end |
| end | | end |
| | | |
| + | local translator_etal; |
| local t = {}; -- translators list from |translator-lastn= / translator-firstn= pairs | | local t = {}; -- translators list from |translator-lastn= / translator-firstn= pairs |
| local Translators; -- assembled translators name list | | local Translators; -- assembled translators name list |
| t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn= | | t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn= |
| | | |
− | local interviewers_list = {}; | + | local interviewer_etal; |
− | local Interviewers = A['Interviewers'] | + | local interviewers_list = {}; |
− | if is_set (Interviewers) then -- add a maint cat if the |interviewers= is used
| + | local Interviewers; -- used later |
− | add_maint_cat ('interviewers'); -- because use of this parameter is discouraged
| + | interviewers_list = extract_names (args, 'InterviewerList'); -- process preferred interviewers parameters |
− | else | |
− | interviewers_list = extract_names (args, 'InterviewerList'); -- else, process preferred interviewers parameters
| |
− | end
| |
| | | |
| + | local contributor_etal; |
| local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs | | local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs |
| local Contributors; -- assembled contributors name list | | local Contributors; -- assembled contributors name list |
Sətir 2.166: |
Sətir 2.235: |
| end | | end |
| | | |
− | if not is_valid_parameter_value (NameListFormat, 'name-list-format', cfg.keywords['name-list-format']) then -- only accepted value for this parameter is 'vanc' | + | if not is_valid_parameter_value (NameListFormat, 'name-list-format', cfg.keywords['name-list-format']) then -- only accepted value for this parameter is 'vanc' |
| NameListFormat = ''; -- anything else, set to empty string | | NameListFormat = ''; -- anything else, set to empty string |
| end | | end |
Sətir 2.187: |
Sətir 2.256: |
| local Chapter = A['Chapter']; | | local Chapter = A['Chapter']; |
| local ScriptChapter = A['ScriptChapter']; | | local ScriptChapter = A['ScriptChapter']; |
| + | local ScriptChapterOrigin = A:ORIGIN ('ScriptChapter'); |
| local ChapterLink -- = A['ChapterLink']; -- deprecated as a parameter but still used internally by cite episode | | local ChapterLink -- = A['ChapterLink']; -- deprecated as a parameter but still used internally by cite episode |
| local TransChapter = A['TransChapter']; | | local TransChapter = A['TransChapter']; |
Sətir 2.199: |
Sətir 2.269: |
| ArchiveURL, ArchiveDate = archive_url_check (A['ArchiveURL'], A['ArchiveDate']) | | ArchiveURL, ArchiveDate = archive_url_check (A['ArchiveURL'], A['ArchiveDate']) |
| | | |
− | local DeadURL = A['DeadURL'] | + | local UrlStatus = A['UrlStatus']; |
− | if not is_valid_parameter_value (DeadURL, 'dead-url', cfg.keywords ['deadurl']) then -- set in config.defaults to 'yes' | + | if 'url-status' == A:ORIGIN ('UrlStatus') then -- interim: TODO: this line goes away |
− | DeadURL = ''; -- anything else, set to empty string | + | if not is_valid_parameter_value (UrlStatus, 'url-status', cfg.keywords ['url-status']) then -- set in config.defaults to 'dead' |
− | end | + | UrlStatus = ''; -- anything else, set to empty string |
− | | + | end -- interim: TODO: this line goes away |
| + | elseif is_set (A:ORIGIN ('UrlStatus')) then -- interim: while both |dead-url= and |url-status= allowed use separate keyword lists; TODO: remove this section |
| + | if not is_valid_parameter_value (UrlStatus, A:ORIGIN ('UrlStatus'), cfg.keywords ['deadurl']) then -- interim: assume |dead-url=; use those keywords; TODO: this line goes away |
| + | UrlStatus = ''; -- anything else, set to empty string; interim: TODO: this line goes away |
| + | end -- interim: TODO: this line goes away |
| + | end -- interim: TODO: this line goes away |
| + | |
| + | if in_array (UrlStatus, {'yes', 'true', 'y'}) then -- TODO: remove this whole thing when |dead-url=[yes|no] parameters removed from articles |
| + | UrlStatus = 'dead'; |
| + | elseif 'no' == UrlStatus then |
| + | UrlStatus = 'live'; |
| + | end |
| + | |
| local URL = A['URL'] | | local URL = A['URL'] |
| local URLorigin = A:ORIGIN('URL'); -- get name of parameter that holds URL | | local URLorigin = A:ORIGIN('URL'); -- get name of parameter that holds URL |
Sətir 2.212: |
Sətir 2.294: |
| local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL | | local ConferenceURLorigin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL |
| local Periodical = A['Periodical']; | | local Periodical = A['Periodical']; |
− | local Periodical_origin = A:ORIGIN('Periodical'); -- get the name of the periodical parameter | + | local Periodical_origin = ''; |
| + | if is_set (Periodical) then |
| + | Periodical_origin = A:ORIGIN('Periodical'); -- get the name of the periodical parameter |
| + | local i; |
| + | Periodical, i = strip_apostrophe_markup (Periodical); -- strip appostrophe markup so that metadata isn't contaminated |
| + | if i then -- non-zero when markup was stripped so emit an error message |
| + | table.insert( z.message_tail, {set_error ('apostrophe_markup', {Periodical_origin}, true)}); |
| + | end |
| + | end |
| + | |
| + | local ScriptPeriodical = A['ScriptPeriodical']; |
| + | local ScriptPeriodical_origin = A:ORIGIN('ScriptPeriodical'); |
| + | -- web and news not tested for now because of |
| + | -- Wikipedia:Administrators%27_noticeboard#Is_there_a_semi-automated_tool_that_could_fix_these_annoying_"Cite_Web"_errors? |
| + | |
| + | local TransPeriodical = A['TransPeriodical']; |
| | | |
| local Series = A['Series']; | | local Series = A['Series']; |
Sətir 2.222: |
Sətir 2.319: |
| local At; | | local At; |
| | | |
− | if in_array (config.CitationClass, cfg.templates_using_volume) then | + | if 'citation' == config.CitationClass then |
| + | if is_set (Periodical) then |
| + | if not in_array (Periodical_origin, {'website', 'mailinglist'}) then -- {{citation}} does not render volume for these 'periodicals' |
| + | Volume = A['Volume']; -- but does for all other 'periodicals' |
| + | end |
| + | elseif is_set (ScriptPeriodical) then |
| + | if 'script-website' ~= ScriptPeriodical_origin then -- {{citation}} does not render volume for |script-website= |
| + | Volume = A['Volume']; -- but does for all other 'periodicals' |
| + | end |
| + | else |
| + | Volume = A['Volume']; -- and does for non-'periodical' cites |
| + | end |
| + | elseif in_array (config.CitationClass, cfg.templates_using_volume) then -- render |volume= for cs1 according to the configuration settings |
| Volume = A['Volume']; | | Volume = A['Volume']; |
| + | end |
| + | |
| + | if 'citation' == config.CitationClass then |
| + | if is_set (Periodical) and in_array (Periodical_origin, {'journal', 'magazine', 'newspaper', 'periodical', 'work'}) or -- {{citation}} renders issue for these 'periodicals' |
| + | is_set (ScriptPeriodical) and in_array (ScriptPeriodical_origin, {'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work'}) then -- and these 'script-periodicals' |
| + | Issue = hyphen_to_dash (A['Issue']); |
| + | end |
| + | elseif in_array (config.CitationClass, cfg.templates_using_issue) then -- conference & map books do not support issue; {{citation}} listed here because included in settings table |
| + | if not (in_array (config.CitationClass, {'conference', 'map', 'citation'}) and not (is_set (Periodical) or is_set (ScriptPeriodical))) then |
| + | Issue = hyphen_to_dash (A['Issue']); |
| + | end |
| end | | end |
− | -- conference & map books do not support issue
| + | |
− | if in_array (config.CitationClass, cfg.templates_using_issue) and not (in_array (config.CitationClass, {'conference', 'map'}) and not is_set (Periodical))then
| |
− | Issue = A['Issue'];
| |
− | end
| |
| local Position = ''; | | local Position = ''; |
| if not in_array (config.CitationClass, cfg.templates_not_using_page) then | | if not in_array (config.CitationClass, cfg.templates_not_using_page) then |
| Page = A['Page']; | | Page = A['Page']; |
− | Pages = hyphen_to_dash( A['Pages'] ); | + | Pages = hyphen_to_dash (A['Pages']); |
| At = A['At']; | | At = A['At']; |
| end | | end |
Sətir 2.241: |
Sətir 2.358: |
| | | |
| local PublisherName = A['PublisherName']; | | local PublisherName = A['PublisherName']; |
| + | local PublisherName_origin = A:ORIGIN('PublisherName'); |
| + | if is_set (PublisherName) then |
| + | local i=0; |
| + | PublisherName, i = strip_apostrophe_markup (PublisherName); -- strip appostrophe markup so that metadata isn't contaminated; publisher is never italicized |
| + | |
| + | if i then -- non-zero when markup was stripped so emit an error message |
| + | table.insert( z.message_tail, {set_error ('apostrophe_markup', {PublisherName_origin}, true)}); |
| + | end |
| + | end |
| + | |
| local RegistrationRequired = A['RegistrationRequired']; | | local RegistrationRequired = A['RegistrationRequired']; |
| if not is_valid_parameter_value (RegistrationRequired, 'registration', cfg.keywords ['yes_true_y']) then | | if not is_valid_parameter_value (RegistrationRequired, 'registration', cfg.keywords ['yes_true_y']) then |
Sətir 2.259: |
Sətir 2.386: |
| table.insert( z.message_tail, { set_error( 'param_access_requires_param', {'url'}, true ) } ); | | table.insert( z.message_tail, { set_error( 'param_access_requires_param', {'url'}, true ) } ); |
| end | | end |
− | | + | |
| if is_set (UrlAccess) and is_set (SubscriptionRequired) then -- while not aliases, these are much the same so if both are set | | if is_set (UrlAccess) and is_set (SubscriptionRequired) then -- while not aliases, these are much the same so if both are set |
| table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'url-access') .. ' and ' .. wrap_style ('parameter', 'subscription')}, true ) } ); -- add error message | | table.insert( z.message_tail, { set_error( 'redundant_parameters', {wrap_style ('parameter', 'url-access') .. ' and ' .. wrap_style ('parameter', 'subscription')}, true ) } ); -- add error message |
Sətir 2.270: |
Sətir 2.397: |
| | | |
| local ChapterUrlAccess = A['ChapterUrlAccess']; | | local ChapterUrlAccess = A['ChapterUrlAccess']; |
− | if not is_valid_parameter_value (ChapterUrlAccess, 'chapter-url-access', cfg.keywords ['url-access']) then -- same as url-access | + | if not is_valid_parameter_value (ChapterUrlAccess, A:ORIGIN('ChapterUrlAccess'), cfg.keywords ['url-access']) then -- same as url-access |
| ChapterUrlAccess = nil; | | ChapterUrlAccess = nil; |
| end | | end |
| if not is_set(ChapterURL) and is_set(ChapterUrlAccess) then | | if not is_set(ChapterURL) and is_set(ChapterUrlAccess) then |
| ChapterUrlAccess = nil; | | ChapterUrlAccess = nil; |
− | table.insert( z.message_tail, { set_error( 'param_access_requires_param', {'chapter-url'}, true ) } ); | + | table.insert( z.message_tail, { set_error( 'param_access_requires_param', {A:ORIGIN('ChapterUrlAccess'):gsub ('%-access', '')}, true ) } ); |
| + | end |
| + | |
| + | local MapUrlAccess = A['MapUrlAccess']; |
| + | if not is_valid_parameter_value (MapUrlAccess, 'map-url-access', cfg.keywords ['url-access']) then |
| + | MapUrlAccess = nil; |
| + | end |
| + | if not is_set(A['MapURL']) and is_set(MapUrlAccess) then |
| + | MapUrlAccess = nil; |
| + | table.insert( z.message_tail, { set_error( 'param_access_requires_param', {'map-url'}, true ) } ); |
| end | | end |
| | | |
Sətir 2.296: |
Sətir 2.432: |
| | | |
| local ID_list = extract_ids( args ); | | local ID_list = extract_ids( args ); |
| + | if is_set (DoiBroken) and not ID_list['DOI'] then |
| + | table.insert( z.message_tail, { set_error( 'doibroken_missing_doi', A:ORIGIN('DoiBroken'))}); |
| + | end |
| local ID_access_levels = extract_id_access_levels( args, ID_list ); | | local ID_access_levels = extract_id_access_levels( args, ID_list ); |
| | | |
Sətir 2.327: |
Sətir 2.466: |
| if not is_valid_parameter_value (DF, 'df', cfg.keywords['date-format']) then -- validate reformatting keyword | | if not is_valid_parameter_value (DF, 'df', cfg.keywords['date-format']) then -- validate reformatting keyword |
| DF = ''; -- not valid, set to empty string | | DF = ''; -- not valid, set to empty string |
| + | end |
| + | if not is_set (DF) then |
| + | DF = cfg.global_df; |
| end | | end |
| | | |
− | local sepc; -- separator between citation elements for CS1 a period, for CS2, a comma | + | local sepc; -- separator between citation elements for CS1 a period, for CS2, a comma |
| local PostScript; | | local PostScript; |
| local Ref; | | local Ref; |
| sepc, PostScript, Ref = set_style (Mode:lower(), A['PostScript'], A['Ref'], config.CitationClass); | | sepc, PostScript, Ref = set_style (Mode:lower(), A['PostScript'], A['Ref'], config.CitationClass); |
− | use_lowercase = ( sepc == ',' ); -- used to control capitalization for certain static text | + | use_lowercase = ( sepc == ',' ); -- used to control capitalization for certain static text |
| | | |
| --check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories | | --check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories |
Sətir 2.349: |
Sətir 2.491: |
| | | |
| -- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it) | | -- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it) |
− | select_one( args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'redundant_parameters' ); -- this is a dummy call simply to get the error message and category | + | select_one( args, {'page', 'p', 'at', 'sheet', 'sheets'}, 'redundant_parameters' ); -- this is a dummy call simply to get the error message and category |
| + | |
| + | local coins_pages; |
| + | |
| + | Page, Pages, At, coins_pages = insource_loc_get (Page, Pages, At); |
| | | |
| local NoPP = A['NoPP'] | | local NoPP = A['NoPP'] |
Sətir 2.358: |
Sətir 2.504: |
| end | | end |
| | | |
− | if is_set(Page) then | + | if not is_set(PublicationPlace) and is_set(Place) then -- both |publication-place= and |place= (|location=) allowed if different |
− | if is_set(Pages) or is_set(At) then
| + | PublicationPlace = Place; -- promote |place= (|location=) to |publication-place |
− | Pages = ''; -- unset the others
| |
− | At = '';
| |
− | end
| |
− | extra_text_in_page_check (Page); -- add this page to maint cat if |page= value begins with what looks like p. or pp.
| |
− | elseif is_set(Pages) then
| |
− | if is_set(At) then
| |
− | At = ''; -- unset
| |
− | end
| |
− | extra_text_in_page_check (Pages); -- add this page to maint cat if |pages= value begins with what looks like p. or pp.
| |
− | end
| |
− | | |
− | -- both |publication-place= and |place= (|location=) allowed if different
| |
− | if not is_set(PublicationPlace) and is_set(Place) then
| |
− | PublicationPlace = Place; -- promote |place= (|location=) to |publication-place | |
| end | | end |
| | | |
− | if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same | + | if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same |
| | | |
| --[[ | | --[[ |
Sətir 2.383: |
Sətir 2.515: |
| |encyclopedia and |title then map |title to |article and |encyclopedia to |title | | |encyclopedia and |title then map |title to |article and |encyclopedia to |title |
| |encyclopedia and |article then map |encyclopedia to |title | | |encyclopedia and |article then map |encyclopedia to |title |
− | |encyclopedia then map |encyclopedia to |title | + | |
− |
| + | |trans-title maps to |trans-chapter when |title is re-mapped |
− | |trans_title maps to |trans_chapter when |title is re-mapped
| |
| |url maps to |chapterurl when |title is remapped | | |url maps to |chapterurl when |title is remapped |
| | | |
Sətir 2.395: |
Sətir 2.526: |
| | | |
| if ( config.CitationClass == "encyclopaedia" ) or ( config.CitationClass == "citation" and is_set (Encyclopedia)) then -- test code for citation | | if ( config.CitationClass == "encyclopaedia" ) or ( config.CitationClass == "citation" and is_set (Encyclopedia)) then -- test code for citation |
− | if is_set(Periodical) then -- Periodical is set when |encyclopedia is set | + | if is_set (Periodical) then -- Periodical is set when |encyclopedia is set |
| if is_set(Title) or is_set (ScriptTitle) then | | if is_set(Title) or is_set (ScriptTitle) then |
| if not is_set(Chapter) then | | if not is_set(Chapter) then |
| Chapter = Title; -- |encyclopedia and |title are set so map |title to |article and |encyclopedia to |title | | Chapter = Title; -- |encyclopedia and |title are set so map |title to |article and |encyclopedia to |title |
| ScriptChapter = ScriptTitle; | | ScriptChapter = ScriptTitle; |
| + | ScriptChapterOrigin = 'title'; |
| TransChapter = TransTitle; | | TransChapter = TransTitle; |
| ChapterURL = URL; | | ChapterURL = URL; |
Sətir 2.416: |
Sətir 2.548: |
| ScriptTitle = ''; | | ScriptTitle = ''; |
| end | | end |
− | else -- |title not set | + | elseif is_set (Chapter) then -- |title not set |
− | Title = Periodical; -- |encyclopedia set and |article set or not set so map |encyclopedia to |title | + | Title = Periodical; -- |encyclopedia set and |article set so map |encyclopedia to |title |
| Periodical = ''; -- redundant so unset | | Periodical = ''; -- redundant so unset |
| end | | end |
Sətir 2.437: |
Sətir 2.569: |
| if (config.CitationClass == "mailinglist") then | | if (config.CitationClass == "mailinglist") then |
| Periodical = A ['MailingList']; | | Periodical = A ['MailingList']; |
− | elseif 'mailinglist' == A:ORIGIN('Periodical') then | + | elseif 'mailinglist' == Periodical_origin then |
| Periodical = ''; -- unset because mailing list is only used for cite mailing list | | Periodical = ''; -- unset because mailing list is only used for cite mailing list |
| end | | end |
Sətir 2.470: |
Sətir 2.602: |
| Chapter = A['Map']; | | Chapter = A['Map']; |
| ChapterURL = A['MapURL']; | | ChapterURL = A['MapURL']; |
− | ChapterUrlAccess = UrlAccess; | + | ChapterUrlAccess = MapUrlAccess; |
| TransChapter = A['TransMap']; | | TransChapter = A['TransMap']; |
| ChapterURLorigin = A:ORIGIN('MapURL'); | | ChapterURLorigin = A:ORIGIN('MapURL'); |
Sətir 2.514: |
Sətir 2.646: |
| -- assemble a table of parts concatenated later into Series | | -- assemble a table of parts concatenated later into Series |
| if is_set(Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end | | if is_set(Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end |
− | if is_set(SeriesNumber) then table.insert(s, wrap_msg ('series', SeriesNumber, use_lowercase)); end | + | if is_set(SeriesNumber) then table.insert(s, wrap_msg ('seriesnum', SeriesNumber, use_lowercase)); end |
| if is_set(Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end | | if is_set(Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end |
| Issue = ''; -- unset because this is not a unique parameter | | Issue = ''; -- unset because this is not a unique parameter |
Sətir 2.520: |
Sətir 2.652: |
| Chapter = Title; -- promote title parameters to chapter | | Chapter = Title; -- promote title parameters to chapter |
| ScriptChapter = ScriptTitle; | | ScriptChapter = ScriptTitle; |
| + | ScriptChapterOrigin = 'title'; |
| ChapterLink = TitleLink; -- alias episodelink | | ChapterLink = TitleLink; -- alias episodelink |
| TransChapter = TransTitle; | | TransChapter = TransTitle; |
Sətir 2.550: |
Sətir 2.683: |
| -- end of {{cite episode}} stuff | | -- end of {{cite episode}} stuff |
| | | |
− | -- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, before generation of COinS data. | + | -- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite ssrn}}, before generation of COinS data. |
| do | | do |
− | if in_array (config.CitationClass, {'arxiv', 'biorxiv', 'citeseerx'}) then | + | if in_array (config.CitationClass, {'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) then |
| if not is_set (ID_list[config.CitationClass:upper()]) then -- |arxiv= or |eprint= required for cite arxiv; |biorxiv= & |citeseerx= required for their templates | | if not is_set (ID_list[config.CitationClass:upper()]) then -- |arxiv= or |eprint= required for cite arxiv; |biorxiv= & |citeseerx= required for their templates |
− | table.insert( z.message_tail, { set_error( config.CitationClass .. '_missing', {}, true ) } ); -- add error message | + | table.insert( z.message_tail, { set_error( config.CitationClass .. '_missing', {}, true ) } ); -- add error message |
− | end
| |
− |
| |
− | if 'arxiv' == config.CitationClass then
| |
− | Periodical = 'arXiv'; -- set to arXiv for COinS; after that, must be set to empty string
| |
| end | | end |
| | | |
− | if 'biorxiv' == config.CitationClass then | + | Periodical = ({['arxiv'] = 'arXiv', ['biorxiv'] = bioRxiv, ['citeseerx'] = 'CiteSeerX', ['ssrn'] = 'Social Science Research Network'})[config.CitationClass]; |
− | Periodical = 'bioRxiv'; -- set to bioRxiv for COinS; after that, must be set to empty string
| |
− | end
| |
− | | |
− | if 'citeseerx' == config.CitationClass then
| |
− | Periodical = 'CiteSeerX'; -- set to CiteSeerX for COinS; after that, must be set to empty string
| |
− | end
| |
| end | | end |
| end | | end |
Sətir 2.585: |
Sətir 2.708: |
| | | |
| -- legacy: promote PublicationDate to Date if neither Date nor Year are set. | | -- legacy: promote PublicationDate to Date if neither Date nor Year are set. |
| + | local Date_origin; -- to hold the name of parameter promoted to Date; required for date error messaging |
| + | |
| if not is_set (Date) then | | if not is_set (Date) then |
| Date = Year; -- promote Year to Date | | Date = Year; -- promote Year to Date |
Sətir 2.591: |
Sətir 2.716: |
| Date = PublicationDate; -- promote PublicationDate to Date | | Date = PublicationDate; -- promote PublicationDate to Date |
| PublicationDate = ''; -- unset, no longer needed | | PublicationDate = ''; -- unset, no longer needed |
| + | Date_origin = A:ORIGIN('PublicationDate'); -- save the name of the promoted parameter |
| + | else |
| + | Date_origin = A:ORIGIN('Year'); -- save the name of the promoted parameter |
| end | | end |
| + | else |
| + | Date_origin = A:ORIGIN('Date'); -- not a promotion; name required for error messaging |
| end | | end |
| | | |
Sətir 2.605: |
Sətir 2.735: |
| local error_message = ''; | | local error_message = ''; |
| -- AirDate has been promoted to Date so not necessary to check it | | -- AirDate has been promoted to Date so not necessary to check it |
− | local date_parameters_list = {['access-date']=AccessDate, ['archive-date']=ArchiveDate, ['date']=Date, ['doi-broken-date']=DoiBroken, | + | local date_parameters_list = { |
− | ['embargo']=Embargo, ['lay-date']=LayDate, ['publication-date']=PublicationDate, ['year']=Year};
| + | ['access-date'] = {val=AccessDate, name=A:ORIGIN ('AccessDate')}, |
| + | ['archive-date'] = {val=ArchiveDate, name=A:ORIGIN ('ArchiveDate')}, |
| + | ['date'] = {val=Date, name=Date_origin}, |
| + | ['doi-broken-date'] = {val=DoiBroken, name=A:ORIGIN ('DoiBroken')}, |
| + | ['embargo'] = {val=Embargo, name=A:ORIGIN ('Embargo')}, |
| + | ['lay-date'] = {val=LayDate, name=A:ORIGIN ('LayDate')}, |
| + | ['publication-date'] ={val=PublicationDate, name=A:ORIGIN ('PublicationDate')}, |
| + | ['year'] = {val=Year, name=A:ORIGIN ('Year')}, |
| + | }; |
| + | anchor_year, Embargo, error_message = dates(date_parameters_list, COinS_date); |
| | | |
− | anchor_year, Embargo, error_message = dates(date_parameters_list, COinS_date);
| + | -- end temporary Julian / Gregorian calendar uncertainty categorization |
| | | |
| if is_set (Year) and is_set (Date) then -- both |date= and |year= not normally needed; | | if is_set (Year) and is_set (Date) then -- both |date= and |year= not normally needed; |
Sətir 2.617: |
Sətir 2.756: |
| end | | end |
| error_message = error_message .. '|year= / |date= mismatch'; | | error_message = error_message .. '|year= / |date= mismatch'; |
− | elseif 1 == mismatch then -- |year= matches year-value in |date=
| |
− | add_maint_cat ('date_year');
| |
| end | | end |
| end | | end |
Sətir 2.631: |
Sətir 2.768: |
| if true == date_hyphen_to_dash (date_parameters_list) then -- convert hyphens to dashes where appropriate | | if true == date_hyphen_to_dash (date_parameters_list) then -- convert hyphens to dashes where appropriate |
| modified = true; | | modified = true; |
− | add_maint_cat ('date_format'); -- hyphens were converted so add maint category
| |
| end | | end |
| | | |
| -- for those wikis that can and want to have English date names translated to the local language, | | -- for those wikis that can and want to have English date names translated to the local language, |
| -- uncomment these three lines. Not supported by en.wiki (for obvious reasons) | | -- uncomment these three lines. Not supported by en.wiki (for obvious reasons) |
− | -- if date_name_xlate (date_parameters_list) then | + | -- set date_name_xlate() second argument to true to translate English digits to local digits (will translate ymd dates) |
| + | -- if date_name_xlate (date_parameters_list, false) then |
| -- modified = true; | | -- modified = true; |
| -- end | | -- end |
| | | |
| if modified then -- if the date_parameters_list values were modified | | if modified then -- if the date_parameters_list values were modified |
− | AccessDate = date_parameters_list['access-date']; -- overwrite date holding parameters with modified values | + | AccessDate = date_parameters_list['access-date'].val; -- overwrite date holding parameters with modified values |
− | ArchiveDate = date_parameters_list['archive-date']; | + | ArchiveDate = date_parameters_list['archive-date'].val; |
− | Date = date_parameters_list['date']; | + | Date = date_parameters_list['date'].val; |
− | DoiBroken = date_parameters_list['doi-broken-date']; | + | DoiBroken = date_parameters_list['doi-broken-date'].val; |
− | LayDate = date_parameters_list['lay-date']; | + | LayDate = date_parameters_list['lay-date'].val; |
− | PublicationDate = date_parameters_list['publication-date']; | + | PublicationDate = date_parameters_list['publication-date'].val; |
| end | | end |
− | else
| |
− | table.insert( z.message_tail, { set_error( 'bad_date', {error_message}, true ) } ); -- add this error message
| |
| end | | end |
| end -- end of do | | end -- end of do |
Sətir 2.674: |
Sətir 2.809: |
| not is_set(TransTitle) and | | not is_set(TransTitle) and |
| not is_set(ScriptTitle) then | | not is_set(ScriptTitle) then |
− | if 'episode' == config.CitationClass then -- special case for cite episode; TODO: is there a better way to do this? | + | if 'episode' == config.CitationClass then |
− | table.insert( z.message_tail, { set_error( 'citation_missing_title', {'series'}, true ) } );
| |
− | else
| |
− | table.insert( z.message_tail, { set_error( 'citation_missing_title', {'title'}, true ) } );
| |
| end | | end |
| end | | end |
− |
| + | |
− | if 'none' == Title and in_array (config.CitationClass, {'journal', 'citation'}) and is_set (Periodical) and 'journal' == A:ORIGIN('Periodical') then -- special case for journal cites | + | if 'none' == Title and |
− | Title = ''; -- set title to empty string
| + | in_array (config.CitationClass, {'journal', 'citation'}) and |
− | add_maint_cat ('untitled');
| + | (is_set (Periodical) or is_set (ScriptPeriodical)) and |
| + | ('journal' == Periodical_origin or 'script-journal' == ScriptPeriodical_origin) then -- special case for journal cites |
| + | Title = ''; -- set title to empty string |
| end | | end |
| | | |
− | check_for_url ({ -- add error message when any of these parameters contains a URL | + | check_for_url ({ -- add error message when any of these parameters hold a URL |
| ['title']=Title, | | ['title']=Title, |
| [A:ORIGIN('Chapter')]=Chapter, | | [A:ORIGIN('Chapter')]=Chapter, |
− | [A:ORIGIN('Periodical')]=Periodical, | + | [Periodical_origin] = Periodical, |
− | [A:ORIGIN('PublisherName')] = PublisherName | + | [PublisherName_origin] = PublisherName |
| }); | | }); |
| | | |
Sətir 2.713: |
Sətir 2.847: |
| -- this is the function call to COinS() | | -- this is the function call to COinS() |
| local OCinSoutput = COinS({ | | local OCinSoutput = COinS({ |
− | ['Periodical'] = Periodical, | + | ['Periodical'] = strip_apostrophe_markup (Periodical), -- no markup in the metadata |
− | ['Encyclopedia'] = Encyclopedia, | + | ['Encyclopedia'] = strip_apostrophe_markup (Encyclopedia), |
| ['Chapter'] = make_coins_title (coins_chapter, ScriptChapter), -- Chapter and ScriptChapter stripped of bold / italic wikimarkup | | ['Chapter'] = make_coins_title (coins_chapter, ScriptChapter), -- Chapter and ScriptChapter stripped of bold / italic wikimarkup |
| ['Degree'] = Degree; -- cite thesis only | | ['Degree'] = Degree; -- cite thesis only |
Sətir 2.725: |
Sətir 2.859: |
| ['Volume'] = Volume, | | ['Volume'] = Volume, |
| ['Issue'] = Issue, | | ['Issue'] = Issue, |
− | ['Pages'] = get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At}, 5)), -- pages stripped of external links | + | ['Pages'] = coins_pages or get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At}, 5)), -- pages stripped of external links |
| ['Edition'] = Edition, | | ['Edition'] = Edition, |
− | ['PublisherName'] = PublisherName, | + | ['PublisherName'] = PublisherName, -- any apostrophe markup already removed |
| ['URL'] = first_set ({ChapterURL, URL}, 2), | | ['URL'] = first_set ({ChapterURL, URL}, 2), |
| ['Authors'] = coins_author, | | ['Authors'] = coins_author, |
Sətir 2.734: |
Sətir 2.868: |
| }, config.CitationClass); | | }, config.CitationClass); |
| | | |
− | -- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, and {{cite citeseerx}} AFTER generation of COinS data. | + | -- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, and {{cite ssrn}} AFTER generation of COinS data. |
− | if in_array (config.CitationClass, {'arxiv', 'biorxiv', 'citeseerx'}) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, or CiteSeerX now unset so it isn't displayed | + | if in_array (config.CitationClass, {'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, CiteSeerX, or ssrn now unset so it isn't displayed |
| Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal | | Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal |
| end | | end |
Sətir 2.742: |
Sətir 2.876: |
| if 'newsgroup' == config.CitationClass then | | if 'newsgroup' == config.CitationClass then |
| if is_set (PublisherName) then | | if is_set (PublisherName) then |
− | PublisherName = substitute (cfg.messages['newsgroup'], external_link( 'news:' .. PublisherName, PublisherName, A:ORIGIN('PublisherName'), nil )); | + | PublisherName = substitute (cfg.messages['newsgroup'], external_link( 'news:' .. PublisherName, PublisherName, PublisherName_origin, nil )); |
| end | | end |
| end | | end |
− |
| |
− |
| |
| | | |
| -- Now perform various field substitutions. | | -- Now perform various field substitutions. |
Sətir 2.763: |
Sətir 2.895: |
| | | |
| do -- do editor name list first because the now unsupported coauthors used to modify control table | | do -- do editor name list first because the now unsupported coauthors used to modify control table |
− | control.maximum , editor_etal = get_display_authors_editors (A['DisplayEditors'], #e, 'editors', editor_etal); | + | control.maximum , editor_etal = get_display_names (A['DisplayEditors'], #e, 'editors', editor_etal); |
| last_first_list, EditorCount = list_people(control, e, editor_etal); | | last_first_list, EditorCount = list_people(control, e, editor_etal); |
| | | |
| if is_set (Editors) then | | if is_set (Editors) then |
| + | Editors, editor_etal = name_has_etal (Editors, editor_etal, false, 'editors'); -- find and remove variations on et al. |
| if editor_etal then | | if editor_etal then |
| Editors = Editors .. ' ' .. cfg.messages['et al']; -- add et al. to editors parameter beause |display-editors=etal | | Editors = Editors .. ' ' .. cfg.messages['et al']; -- add et al. to editors parameter beause |display-editors=etal |
Sətir 2.782: |
Sətir 2.915: |
| end | | end |
| do -- now do interviewers | | do -- now do interviewers |
− | control.maximum = #interviewers_list; -- number of interviewerss | + | control.maximum , interviewer_etal = get_display_names (A['DisplayInterviewers'], #interviewers_list, 'interviewers', interviewer_etal); |
− | Interviewers = list_people(control, interviewers_list, false); -- et al not currently supported | + | Interviewers = list_people (control, interviewers_list, interviewer_etal); |
| end | | end |
| do -- now do translators | | do -- now do translators |
− | control.maximum = #t; -- number of translators | + | control.maximum , translator_etal = get_display_names (A['DisplayTranslators'], #t, 'translators', translator_etal); |
− | Translators = list_people(control, t, false); -- et al not currently supported | + | Translators = list_people (control, t, translator_etal); |
| end | | end |
| do -- now do contributors | | do -- now do contributors |
− | control.maximum = #c; -- number of contributors | + | control.maximum , contributor_etal = get_display_names (A['DisplayContributors'], #c, 'contributors', contributor_etal); |
− | Contributors = list_people(control, c, false); -- et al not currently supported | + | Contributors = list_people (control, c, contributor_etal); |
| end | | end |
| do -- now do authors | | do -- now do authors |
− | control.maximum , author_etal = get_display_authors_editors (A['DisplayAuthors'], #a, 'authors', author_etal); | + | control.maximum , author_etal = get_display_names (A['DisplayAuthors'], #a, 'authors', author_etal); |
| | | |
| last_first_list = list_people(control, a, author_etal); | | last_first_list = list_people(control, a, author_etal); |
| | | |
| if is_set (Authors) then | | if is_set (Authors) then |
− | Authors, author_etal = name_has_etal (Authors, author_etal, false); -- find and remove variations on et al. | + | Authors, author_etal = name_has_etal (Authors, author_etal, false, 'authors'); -- find and remove variations on et al. |
| if author_etal then | | if author_etal then |
| Authors = Authors .. ' ' .. cfg.messages['et al']; -- add et al. to authors parameter | | Authors = Authors .. ' ' .. cfg.messages['et al']; -- add et al. to authors parameter |
Sətir 2.823: |
Sətir 2.956: |
| | | |
| -- special case for chapter format so no error message or cat when chapter not supported | | -- special case for chapter format so no error message or cat when chapter not supported |
− | if not (in_array(config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx'}) or | + | if not (in_array(config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or |
− | ('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia))) then | + | ('citation' == config.CitationClass and (is_set (Periodical) or is_set (ScriptPeriodical)) and not is_set (Encyclopedia))) then |
| ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url'); | | ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url'); |
| end | | end |
| | | |
| if not is_set(URL) then | | if not is_set(URL) then |
− | if in_array(config.CitationClass, {"web","podcast", "mailinglist"}) then -- |url= required for cite web, cite podcast, and cite mailinglist | + | if in_array(config.CitationClass, {"web","podcast", "mailinglist"}) or -- |url= required for cite web, cite podcast, and cite mailinglist |
− | table.insert( z.message_tail, { set_error( 'cite_web_url', {}, true ) } ); | + | ('citation' == config.CitationClass and ('website' == Periodical_origin or 'script-website' == ScriptPeriodical_origin)) then -- and required for {{citation}} with |website= or |script-website= |
| + | table.insert( z.message_tail, { set_error( 'cite_web_url', {}, true ) } ); |
| end | | end |
| | | |
Sətir 2.841: |
Sətir 2.975: |
| | | |
| local OriginalURL, OriginalURLorigin, OriginalFormat, OriginalAccess; | | local OriginalURL, OriginalURLorigin, OriginalFormat, OriginalAccess; |
− | DeadURL = DeadURL:lower(); -- used later when assembling archived text
| + | -- DeadURL = DeadURL:lower(); -- used later when assembling archived text |
| + | UrlStatus = UrlStatus:lower(); -- used later when assembling archived text |
| if is_set( ArchiveURL ) then | | if is_set( ArchiveURL ) then |
− | if is_set (ChapterURL) then -- URL not set so if chapter-url is set apply archive url to it | + | if is_set (ChapterURL) then -- if chapter-url is set apply archive url to it |
| OriginalURL = ChapterURL; -- save copy of source chapter's url for archive text | | OriginalURL = ChapterURL; -- save copy of source chapter's url for archive text |
| OriginalURLorigin = ChapterURLorigin; -- name of chapter-url parameter for error messages | | OriginalURLorigin = ChapterURLorigin; -- name of chapter-url parameter for error messages |
− | OriginalFormat = ChapterFormat; -- and original |format= | + | OriginalFormat = ChapterFormat; -- and original |chapter-format= |
− | if 'no' ~= DeadURL then
| + | -- if 'no' ~= DeadURL then |
| + | if 'live' ~= UrlStatus then |
| ChapterURL = ArchiveURL -- swap-in the archive's url | | ChapterURL = ArchiveURL -- swap-in the archive's url |
| ChapterURLorigin = A:ORIGIN('ArchiveURL') -- name of archive-url parameter for error messages | | ChapterURLorigin = A:ORIGIN('ArchiveURL') -- name of archive-url parameter for error messages |
| ChapterFormat = ArchiveFormat or ''; -- swap in archive's format | | ChapterFormat = ArchiveFormat or ''; -- swap in archive's format |
| + | ChapterUrlAccess = nil; -- restricted access levels do not make sense for archived urls |
| end | | end |
| elseif is_set (URL) then | | elseif is_set (URL) then |
Sətir 2.857: |
Sətir 2.994: |
| OriginalFormat = Format; -- and original |format= | | OriginalFormat = Format; -- and original |format= |
| OriginalAccess = UrlAccess; | | OriginalAccess = UrlAccess; |
− | if 'no' ~= DeadURL then -- if URL set then archive-url applies to it
| + | -- if 'no' ~= DeadURL then -- if URL set then archive-url applies to it |
| + | if 'live' ~= UrlStatus then -- if URL set then archive-url applies to it |
| URL = ArchiveURL -- swap-in the archive's url | | URL = ArchiveURL -- swap-in the archive's url |
| URLorigin = A:ORIGIN('ArchiveURL') -- name of archive url parameter for error messages | | URLorigin = A:ORIGIN('ArchiveURL') -- name of archive url parameter for error messages |
Sətir 2.866: |
Sətir 3.004: |
| end | | end |
| | | |
− | if in_array(config.CitationClass, {'web','news','journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx'}) or -- if any of the 'periodical' cites except encyclopedia | + | if in_array(config.CitationClass, {'web','news','journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or -- if any of the 'periodical' cites except encyclopedia |
− | ('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia)) then | + | ('citation' == config.CitationClass and (is_set (Periodical) or is_set (ScriptPeriodical)) and not is_set (Encyclopedia)) then |
| local chap_param; | | local chap_param; |
| if is_set (Chapter) then -- get a parameter name from one of these chapter related meta-parameters | | if is_set (Chapter) then -- get a parameter name from one of these chapter related meta-parameters |
Sətir 2.876: |
Sətir 3.014: |
| chap_param = A:ORIGIN ('ChapterURL') | | chap_param = A:ORIGIN ('ChapterURL') |
| elseif is_set (ScriptChapter) then | | elseif is_set (ScriptChapter) then |
− | chap_param = A:ORIGIN ('ScriptChapter') | + | chap_param = ScriptChapterOrigin; |
| else is_set (ChapterFormat) | | else is_set (ChapterFormat) |
| chap_param = A:ORIGIN ('ChapterFormat') | | chap_param = A:ORIGIN ('ChapterFormat') |
Sətir 2.897: |
Sətir 3.035: |
| end | | end |
| | | |
− | Chapter = format_chapter_title (ScriptChapter, Chapter, TransChapter, ChapterURL, ChapterURLorigin, no_quotes, ChapterUrlAccess); -- Contribution is also in Chapter | + | Chapter = format_chapter_title (ScriptChapter, ScriptChapterOrigin, Chapter, TransChapter, ChapterURL, ChapterURLorigin, no_quotes, ChapterUrlAccess); -- Contribution is also in Chapter |
| if is_set (Chapter) then | | if is_set (Chapter) then |
| Chapter = Chapter .. ChapterFormat ; | | Chapter = Chapter .. ChapterFormat ; |
Sətir 2.910: |
Sətir 3.048: |
| | | |
| -- Format main title. | | -- Format main title. |
− | Title = mw.ustring.gsub(Title, '%'..sepc..'$', ''); -- remove any trailing separator character | + | |
− | if is_set(TitleLink) and is_set(Title) then
| + | if Title:match ('^%(%(.*%)%)$') then -- if keep as written markup: |
− | Title = make_wikilink (TitleLink, Title); | + | Title= Title:gsub ('^%(%((.*)%)%)$', '%1') -- remove the markup |
| + | else |
| + | if '...' == Title:sub (-3) then -- if elipsis is the last three characters of |title= |
| + | Title = Title:gsub ('(%.%.%.)%.+$', '%1'); -- limit the number of dots to three |
| + | elseif not mw.ustring.find (Title, '%.%s*%a%.$') and -- end of title is not a 'dot-(optional space-)letter-dot' initialism ... |
| + | not mw.ustring.find (Title, '%s+%a%.$') then -- ...and not a 'space-letter-dot' initial (''Allium canadense'' L.) |
| + | Title = mw.ustring.gsub(Title, '%'..sepc..'$', ''); -- remove any trailing separator character; sepc and ms.ustring() here for languages that use multibyte separator characters |
| + | end |
| end | | end |
− | | + | |
− | if in_array(config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx'}) or | + | if in_array(config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or |
− | ('citation' == config.CitationClass and is_set (Periodical) and not is_set (Encyclopedia)) or | + | ('citation' == config.CitationClass and (is_set (Periodical) or is_set (ScriptPeriodical)) and not is_set (Encyclopedia)) or |
− | ('map' == config.CitationClass and is_set (Periodical)) then -- special case for cite map when the map is in a periodical treat as an article | + | ('map' == config.CitationClass and (is_set (Periodical) or is_set (ScriptPeriodical))) then -- special case for cite map when the map is in a periodical treat as an article |
| Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks | | Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from Module provided quote marks |
| Title = wrap_style ('quoted-title', Title); | | Title = wrap_style ('quoted-title', Title); |
− | Title = script_concatenate (Title, ScriptTitle); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped | + | Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped |
| TransTitle= wrap_style ('trans-quoted-title', TransTitle ); | | TransTitle= wrap_style ('trans-quoted-title', TransTitle ); |
| elseif 'report' == config.CitationClass then -- no styling for cite report | | elseif 'report' == config.CitationClass then -- no styling for cite report |
− | Title = script_concatenate (Title, ScriptTitle); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped | + | Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped |
| TransTitle= wrap_style ('trans-quoted-title', TransTitle ); -- for cite report, use this form for trans-title | | TransTitle= wrap_style ('trans-quoted-title', TransTitle ); -- for cite report, use this form for trans-title |
| else | | else |
| Title = wrap_style ('italic-title', Title); | | Title = wrap_style ('italic-title', Title); |
− | Title = script_concatenate (Title, ScriptTitle); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped | + | Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang atribute, categorization, etc; must be done after title is wrapped |
| TransTitle = wrap_style ('trans-italic-title', TransTitle); | | TransTitle = wrap_style ('trans-italic-title', TransTitle); |
| end | | end |
Sətir 2.939: |
Sətir 3.084: |
| end | | end |
| end | | end |
− |
| + | |
| if is_set(Title) then | | if is_set(Title) then |
− | if not is_set(TitleLink) and is_set(URL) then | + | if not is_set (TitleLink) and is_set (URL) then |
− |
| + | Title = external_link (URL, Title, URLorigin, UrlAccess) .. TransTitle .. TransError .. Format; |
− | Title = external_link( URL, Title, URLorigin, UrlAccess ) .. TransTitle .. TransError .. Format; | |
| URL = ''; -- unset these because no longer needed | | URL = ''; -- unset these because no longer needed |
| Format = ""; | | Format = ""; |
| + | elseif is_set (TitleLink) and not is_set (URL) then |
| + | local ws_url; |
| + | ws_url = wikisource_url_make (TitleLink); -- ignore ws_label return; not used here |
| + | if ws_url then |
| + | Title = external_link (ws_url, Title .. ' ', 'ws link in title-link'); -- space char after Title to move icon away from italic text; TODO: a better way to do this? |
| + | Title = substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], TitleLink, Title}); |
| + | Title = Title .. TransTitle .. TransError; |
| + | else |
| + | Title = make_wikilink (TitleLink, Title) .. TransTitle .. TransError; |
| + | end |
| else | | else |
− | Title = Title .. TransTitle .. TransError; | + | local ws_url, ws_label; |
| + | ws_url, ws_label, L = wikisource_url_make (Title); -- make ws url from |title= interwiki link; link portion L becomes tool tip label |
| + | if ws_url then |
| + | Title = Title:gsub ('%b[]', ws_label); -- replace interwiki link with ws_label to retain markup |
| + | Title = external_link (ws_url, Title .. ' ', 'ws link in title'); -- space char after Title to move icon away from italic text; TODO: a better way to do this? |
| + | Title = substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, Title}); |
| + | Title = Title .. TransTitle .. TransError; |
| + | else |
| + | Title = Title .. TransTitle .. TransError; |
| + | end |
| end | | end |
| else | | else |
Sətir 3.025: |
Sətir 3.188: |
| | | |
| if is_set (Translators) then | | if is_set (Translators) then |
− | Others = sepc .. ' ' .. wrap_msg ('translated', Translators, use_lowercase) .. Others; | + | Others = safe_join ({sepc .. ' ', wrap_msg ('translated', Translators, use_lowercase), Others}, sepc); |
| end | | end |
| if is_set (Interviewers) then | | if is_set (Interviewers) then |
− | Others = sepc .. ' ' .. wrap_msg ('interview', Interviewers, use_lowercase) .. Others; | + | Others = safe_join ({sepc .. ' ', wrap_msg ('interview', Interviewers, use_lowercase), Others}, sepc); |
| end | | end |
| | | |
| TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""; | | TitleNote = is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""; |
| if is_set (Edition) then | | if is_set (Edition) then |
− | if Edition:match ('%f[%a][Ee]d%.?$') or Edition:match ('%f[%a][Ee]dition$') then
| |
− | add_maint_cat ('extra_text', 'edition');
| |
− | end
| |
| Edition = " " .. wrap_msg ('edition', Edition); | | Edition = " " .. wrap_msg ('edition', Edition); |
| else | | else |
Sətir 3.041: |
Sətir 3.201: |
| end | | end |
| | | |
− | Series = is_set(Series) and (sepc .. " " .. Series) or ""; | + | Series = is_set (Series) and wrap_msg ('series', {sepc, Series}) or ""; -- not the same as SeriesNum |
− | OrigYear = is_set(OrigYear) and (" [" .. OrigYear .. "]") or ""; -- TODO: presentation
| + | OrigYear = is_set (OrigYear) and wrap_msg ('origyear', OrigYear) or ''; |
− | | + | Agency = is_set (Agency) and wrap_msg ('agency', {sepc, Agency}) or ""; |
− | Agency = is_set(Agency) and (sepc .. " " .. Agency) or ""; | |
− | | |
| Volume = format_volume_issue (Volume, Issue, config.CitationClass, Periodical_origin, sepc, use_lowercase); | | Volume = format_volume_issue (Volume, Issue, config.CitationClass, Periodical_origin, sepc, use_lowercase); |
| | | |
| ------------------------------------ totally unrelated data | | ------------------------------------ totally unrelated data |
− | if is_set(Via) then | + | Via = is_set (Via) and wrap_msg ('via', Via) or ''; |
− | Via = " " .. wrap_msg ('via', Via);
| |
− | end
| |
| | | |
| --[[ | | --[[ |
Sətir 3.103: |
Sətir 3.259: |
| ArchiveDate = set_error('archive_missing_date'); | | ArchiveDate = set_error('archive_missing_date'); |
| end | | end |
− | if "no" == DeadURL then | + | if "live" == UrlStatus then |
| local arch_text = cfg.messages['archived']; | | local arch_text = cfg.messages['archived']; |
| if sepc ~= "." then arch_text = arch_text:lower() end | | if sepc ~= "." then arch_text = arch_text:lower() end |
− | Archived = sepc .. " " .. substitute( cfg.messages['archived-not-dead'], | + | Archived = sepc .. " " .. substitute( cfg.messages['archived-live'], |
| { external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL'), nil ) .. ArchiveFormat, ArchiveDate } ); | | { external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL'), nil ) .. ArchiveFormat, ArchiveDate } ); |
| if not is_set(OriginalURL) then | | if not is_set(OriginalURL) then |
| Archived = Archived .. " " .. set_error('archive_missing_url'); | | Archived = Archived .. " " .. set_error('archive_missing_url'); |
| end | | end |
− | elseif is_set(OriginalURL) then -- DeadURL is empty, 'yes', 'true', 'y', 'unfit', 'usurped' | + | elseif is_set(OriginalURL) then -- UrlStatus is empty, 'dead', 'unfit', 'usurped', 'bot: unknown' |
| local arch_text = cfg.messages['archived-dead']; | | local arch_text = cfg.messages['archived-dead']; |
| if sepc ~= "." then arch_text = arch_text:lower() end | | if sepc ~= "." then arch_text = arch_text:lower() end |
− | if in_array (DeadURL, {'unfit', 'usurped', 'bot: unknown'}) then | + | if in_array (UrlStatus, {'unfit', 'usurped', 'bot: unknown'}) then |
| Archived = sepc .. " " .. 'Archived from the original on ' .. ArchiveDate; -- format already styled | | Archived = sepc .. " " .. 'Archived from the original on ' .. ArchiveDate; -- format already styled |
− | if 'bot: unknown' == DeadURL then
| + | else -- UrlStatus is empty, 'dead' |
− | add_maint_cat ('bot:_unknown'); -- and add a category if not already added
| |
− | else
| |
− | add_maint_cat ('unfit'); -- and add a category if not already added
| |
− | end
| |
− | else -- DeadURL is empty, 'yes', 'true', or 'y' | |
| Archived = sepc .. " " .. substitute( arch_text, | | Archived = sepc .. " " .. substitute( arch_text, |
| { external_link( OriginalURL, cfg.messages['original'], OriginalURLorigin, OriginalAccess ) .. OriginalFormat, ArchiveDate } ); -- format already styled | | { external_link( OriginalURL, cfg.messages['original'], OriginalURLorigin, OriginalAccess ) .. OriginalFormat, ArchiveDate } ); -- format already styled |
Sətir 3.180: |
Sətir 3.331: |
| | | |
| -- Several of the above rely upon detecting this as nil, so do it last. | | -- Several of the above rely upon detecting this as nil, so do it last. |
− | if is_set(Periodical) then | + | if (is_set (Periodical) or is_set (ScriptPeriodical) or is_set (TransPeriodical)) then |
| if is_set(Title) or is_set(TitleNote) then | | if is_set(Title) or is_set(TitleNote) then |
− | Periodical = sepc .. " " .. wrap_style ('italic-title', Periodical) | + | Periodical = sepc .. " " .. format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical); |
| else | | else |
− | Periodical = wrap_style ('italic-title', Periodical) | + | Periodical = format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical); |
| end | | end |
| end | | end |
Sətir 3.192: |
Sətir 3.343: |
| the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided). | | the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided). |
| ]] | | ]] |
− | if "speech" == config.CitationClass then -- cite speech only | + | if "speech" == config.CitationClass then -- cite speech only |
− | TitleNote = " (Speech)"; -- annotate the citation | + | TitleNote = " (Speech)"; -- annotate the citation |
− | if is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter | + | if is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter |
− | if is_set (Conference) then -- and if |event= is set | + | if is_set (Conference) then -- and if |event= is set |
− | Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering | + | Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering |
| end | | end |
| end | | end |
Sətir 3.209: |
Sətir 3.360: |
| | | |
| if in_array(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then | | if in_array(config.CitationClass, {"journal","citation"}) and is_set(Periodical) then |
− | if is_set(Others) then Others = Others .. sepc .. " " end | + | if is_set(Others) then Others = safe_join ({Others, sepc .. " "}, sepc) end -- add terminal punctuation & space; check for dup sepc; TODO why do we need to do this here? |
| tcommon = safe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Edition, Publisher, Agency, Volume}, sepc ); | | tcommon = safe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Edition, Publisher, Agency, Volume}, sepc ); |
| elseif in_array(config.CitationClass, {"book","citation"}) and not is_set(Periodical) then -- special cases for book cites | | elseif in_array(config.CitationClass, {"book","citation"}) and not is_set(Periodical) then -- special cases for book cites |
Sətir 3.229: |
Sətir 3.380: |
| | | |
| elseif 'episode' == config.CitationClass then -- special case for cite episode | | elseif 'episode' == config.CitationClass then -- special case for cite episode |
− | tcommon = safe_join( {Title, TitleNote, TitleType, Series, Transcript, Language, Edition, Publisher}, sepc ); | + | tcommon = safe_join( {Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc ); |
| | | |
| else -- all other CS1 templates | | else -- all other CS1 templates |
Sətir 3.242: |
Sətir 3.393: |
| end | | end |
| | | |
− | local idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc ); | + | local idcommon; |
| + | if 'audio-visual' == config.CitationClass or 'episode' == config.CitationClass then -- special case for cite AV media & cite episode position transcript |
| + | idcommon = safe_join( { ID_list, URL, Archived, Transcript, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc ); |
| + | else |
| + | idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, SubscriptionRequired, Lay, Quote }, sepc ); |
| + | end |
| + | |
| local text; | | local text; |
| local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At; | | local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At; |
Sətir 3.268: |
Sətir 3.425: |
| if (sepc ~= '.') then | | if (sepc ~= '.') then |
| in_text = in_text:lower() -- lowercase for cs2 | | in_text = in_text:lower() -- lowercase for cs2 |
− | end
| |
− | else
| |
− | if EditorCount <= 1 then
| |
− | post_text = ", " .. cfg.messages['editor'];
| |
− | else
| |
− | post_text = ", " .. cfg.messages['editors'];
| |
| end | | end |
− | end | + | end |
| + | if EditorCount <= 1 then |
| + | post_text = " (" .. cfg.messages['editor'] .. ")"; -- be consistent with no-author, no-date case |
| + | else |
| + | post_text = " (" .. cfg.messages['editors'] .. ")"; |
| + | end |
| Editors = terminate_name_list (in_text .. Editors .. post_text, sepc); -- terminate with 0 or 1 sepc and a space | | Editors = terminate_name_list (in_text .. Editors .. post_text, sepc); -- terminate with 0 or 1 sepc and a space |
| end | | end |
Sətir 3.316: |
Sətir 3.472: |
| | | |
| if is_set(PostScript) and PostScript ~= sepc then | | if is_set(PostScript) and PostScript ~= sepc then |
− | text = safe_join( {text, sepc}, sepc ); --Deals with italics, spaces, etc. | + | text = safe_join( {text, sepc}, sepc ); --Deals with italics, spaces, etc. |
| text = text:sub(1,-sepc:len()-1); | | text = text:sub(1,-sepc:len()-1); |
| end | | end |
Sətir 3.368: |
Sətir 3.524: |
| end | | end |
| | | |
− | table.insert (render, substitute (cfg.presentation['ocins'], {OCinSoutput})); -- append metadata to the citation | + | table.insert (render, substitute (cfg.presentation['ocins'], {OCinSoutput})); -- append metadata to the citation |
| | | |
− | if #z.message_tail ~= 0 then | + | if 0 ~= #z.message_tail then |
| table.insert (render, ' '); | | table.insert (render, ' '); |
| for i,v in ipairs( z.message_tail ) do | | for i,v in ipairs( z.message_tail ) do |
Sətir 3.383: |
Sətir 3.539: |
| end | | end |
| | | |
− | if #z.maintenance_cats ~= 0 then | + | if 0 ~= #z.maintenance_cats then |
− | table.insert (render, '<span class="citation-comment" style="display:none; color:#33aa33; margin-left:0.3em">'); | + | local maint_msgs = {}; -- here we collect all of the maint messages |
| for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories | | for _, v in ipairs( z.maintenance_cats ) do -- append maintenance categories |
− | table.insert (render, v); | + | local maint = {}; -- here we assemble a maintenence message |
− | table.insert (render, ' ('); | + | table.insert (maint, v); -- maint msg is the category name |
− | table.insert (render, make_wikilink (':Category:' .. v, 'link')); | + | table.insert (maint, ' ('); -- open the link text |
− | table.insert (render, ') '); | + | table.insert (maint, make_wikilink (':Category:' .. v, 'link')); -- add the link |
| + | table.insert (maint, ')'); -- and close it |
| + | table.insert (maint_msgs, table.concat (maint)); -- assemble new maint message and add it to the maint_msgs table |
| end | | end |
− | table.insert (render, '</span>'); | + | table.insert (render, substitute (cfg.presentation['hidden-maint'], table.concat (maint_msgs, ' '))); -- wrap the group of maint message with proper presentation and save |
| end | | end |
| | | |
Sətir 3.411: |
Sətir 3.569: |
| | | |
| | | |
− | --[[--------------------------< C S 1 . C I T A T I O N >------------------------------------------------------ | + | --[[--------------------------< V A L I D A T E >-------------------------------------------------------------- |
| | | |
− | This is used by templates such as {{cite book}} to create the actual citation text.
| + | Looks for a parameter's name in one of several whitelists. |
| | | |
| + | Parameters in the whitelist can have three values: |
| + | true - active, supported parameters |
| + | false - deprecated, supported parameters |
| + | nil - unsupported parameters |
| + | |
| ]] | | ]] |
| | | |
− | function cs1.citation(frame) | + | local function validate (name, cite_class) |
− | Frame = frame; -- save a copy incase we need to display an error message in preview mode
| + | local name = tostring (name); |
− | local pframe = frame:getParent() | + | local state; |
− | local validation, utilities, identifiers, metadata; | |
| | | |
− | if nil ~= string.find (frame:getTitle(), 'sandbox', 1, true) then -- did the {{#invoke:}} use sandbox version? | + | if in_array (cite_class, {'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) then -- limited parameter sets allowed for these templates |
− | cfg = mw.loadData ('Module:Citation/CS1/Configuration/sandbox'); -- load sandbox versions of support modules | + | state = whitelist.limited_basic_arguments[name]; |
− | whitelist = mw.loadData ('Module:Citation/CS1/Whitelist/sandbox'); | + | if true == state then return true; end -- valid actively supported parameter |
− | utilities = require ('Module:Citation/CS1/Utilities/sandbox'); | + | if false == state then |
− | validation = require ('Module:Citation/CS1/Date_validation/sandbox'); | + | deprecated_parameter (name); -- parameter is deprecated but still supported |
− | identifiers = require ('Module:Citation/CS1/Identifiers/sandbox'); | + | return true; |
− | metadata = require ('Module:Citation/CS1/COinS/sandbox'); | + | end |
− | | + | |
− | else -- otherwise | + | state = whitelist[cite_class .. '_basic_arguments'][name]; -- look in the parameter-list for the template identified by cite_class |
− | cfg = mw.loadData ('Module:Citation/CS1/Configuration'); -- load live versions of support modules | + | |
− | whitelist = mw.loadData ('Module:Citation/CS1/Whitelist'); | + | if true == state then return true; end -- valid actively supported parameter |
− | utilities = require ('Module:Citation/CS1/Utilities'); | + | if false == state then |
− | validation = require ('Module:Citation/CS1/Date_validation'); | + | deprecated_parameter (name); -- parameter is deprecated but still supported |
− | identifiers = require ('Module:Citation/CS1/Identifiers'); | + | return true; |
− | metadata = require ('Module:Citation/CS1/COinS'); | + | end |
− | end | + | -- limited enumerated parameters list |
− | | + | name = name:gsub("%d+", "#" ); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits) |
− | utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the cfg tables | + | state = whitelist.limited_numbered_arguments[name]; |
− | identifiers.set_selected_modules (cfg, utilities); -- so that functions in Identifiers can see the selected cfg tables and selected Utilities module | + | if true == state then return true; end -- valid actively supported parameter |
− | validation.set_selected_modules (cfg, utilities); -- so that functions in Date validataion can see selected cfg tables and the selected Utilities module | + | if false == state then |
− | metadata.set_selected_modules (cfg, utilities); -- so that functions in COinS can see the selected cfg tables and selected Utilities module | + | deprecated_parameter (name); -- parameter is deprecated but still supported |
− | | + | return true; |
− | dates = validation.dates; -- imported functions from Module:Citation/CS1/Date validation | + | end |
− | year_date_check = validation.year_date_check; | + | |
− | reformat_dates = validation.reformat_dates; | + | return false; -- not supported because not found or name is set to nil |
− | date_hyphen_to_dash = validation.date_hyphen_to_dash; | + | end -- end limited parameter-set templates |
− | date_name_xlate = validation.date_name_xlate; | + | |
− | | + | state = whitelist.basic_arguments[name]; -- all other templates; all normal parameters allowed |
− | is_set = utilities.is_set; -- imported functions from Module:Citation/CS1/Utilities | + | |
− | in_array = utilities.in_array; | + | if true == state then return true; end -- valid actively supported parameter |
− | substitute = utilities.substitute; | + | if false == state then |
− | error_comment = utilities.error_comment; | + | deprecated_parameter (name); -- parameter is deprecated but still supported |
− | set_error = utilities.set_error; | + | return true; |
− | select_one = utilities.select_one; | + | end |
− | add_maint_cat = utilities.add_maint_cat; | + | -- all enumerated parameters allowed |
− | wrap_style = utilities.wrap_style; | + | name = name:gsub("%d+", "#" ); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits) |
− | safe_for_italics = utilities.safe_for_italics; | + | state = whitelist.numbered_arguments[name]; |
− | is_wikilink = utilities.is_wikilink; | + | |
− | make_wikilink = utilities.make_wikilink; | + | if true == state then return true; end -- valid actively supported parameter |
− | | + | if false == state then |
− | z = utilities.z; -- table of error and category tables in Module:Citation/CS1/Utilities | + | deprecated_parameter (name); -- parameter is deprecated but still supported |
− | | + | return true; |
− | extract_ids = identifiers.extract_ids; -- imported functions from Module:Citation/CS1/Identifiers | + | end |
− | build_id_list = identifiers.build_id_list; | + | |
− | is_embargoed = identifiers.is_embargoed; | + | return false; -- not supported because not found or name is set to nil |
− | extract_id_access_levels = identifiers.extract_id_access_levels; | + | end |
− | | + | |
− | make_coins_title = metadata.make_coins_title; -- imported functions from Module:Citation/CS1/COinS | + | |
− | get_coins_pages = metadata.get_coins_pages; | + | --[[--------------------------< M I S S I N G _ P I P E _ C H E C K >------------------------------------------ |
− | COinS = metadata.COinS; | + | |
− | | + | Look at the contents of a parameter. If the content has a string of characters and digits followed by an equal |
− | local args = {}; -- table where we store all of the template's arguments | + | sign, compare the alphanumeric string to the list of cs1|2 parameters. If found, then the string is possibly a |
− | local suggestions = {}; -- table where we store suggestions if we need to loadData them | + | parameter that is missing its pipe: |
− | local error_text, error_state; | + | {{cite ... |title=Title access-date=2016-03-17}} |
| + | |
| + | cs1|2 shares some parameter names with xml/html atributes: 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_error ('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 cfg.punct_skip[param] then |
| + | return; -- parameter name found in the skip table so done |
| + | end |
| + | end |
| + | |
| + | |
| + | --[[--------------------------< C I T A T I O N >-------------------------------------------------------------- |
| + | |
| + | This is used by templates such as {{cite book}} to create the actual citation text. |
| + | |
| + | ]] |
| + | |
| + | local function citation(frame) |
| + | Frame = frame; -- save a copy incase we need to display an error message in preview mode |
| + | local pframe = frame:getParent() |
| + | local validation, utilities, identifiers, metadata, styles; |
| + | |
| + | if nil ~= string.find (frame:getTitle(), 'sandbox', 1, true) then -- did the {{#invoke:}} use sandbox version? |
| + | cfg = mw.loadData ('Module:Citation/CS1/Configuration/sandbox'); -- load sandbox versions of support modules |
| + | whitelist = mw.loadData ('Module:Citation/CS1/Whitelist/sandbox'); |
| + | utilities = require ('Module:Citation/CS1/Utilities/sandbox'); |
| + | validation = require ('Module:Citation/CS1/Date_validation/sandbox'); |
| + | identifiers = require ('Module:Citation/CS1/Identifiers/sandbox'); |
| + | metadata = require ('Module:Citation/CS1/COinS/sandbox'); |
| + | styles = 'Module:Citation/CS1/sandbox/styles.css'; |
| + | |
| + | else -- otherwise |
| + | cfg = mw.loadData ('Module:Citation/CS1/Configuration'); -- load live versions of support modules |
| + | whitelist = mw.loadData ('Module:Citation/CS1/Whitelist'); |
| + | utilities = require ('Module:Citation/CS1/Utilities'); |
| + | validation = require ('Module:Citation/CS1/Date_validation'); |
| + | identifiers = require ('Module:Citation/CS1/Identifiers'); |
| + | metadata = require ('Module:Citation/CS1/COinS'); |
| + | styles = 'Module:Citation/CS1/styles.css'; |
| + | |
| + | end |
| + | |
| + | utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the cfg tables |
| + | identifiers.set_selected_modules (cfg, utilities); -- so that functions in Identifiers can see the selected cfg tables and selected Utilities module |
| + | validation.set_selected_modules (cfg, utilities); -- so that functions in Date validataion can see selected cfg tables and the selected Utilities module |
| + | metadata.set_selected_modules (cfg, utilities); -- so that functions in COinS can see the selected cfg tables and selected Utilities module |
| + | |
| + | dates = validation.dates; -- imported functions from Module:Citation/CS1/Date validation |
| + | year_date_check = validation.year_date_check; |
| + | reformat_dates = validation.reformat_dates; |
| + | date_hyphen_to_dash = validation.date_hyphen_to_dash; |
| + | date_name_xlate = validation.date_name_xlate; |
| + | |
| + | is_set = utilities.is_set; -- imported functions from Module:Citation/CS1/Utilities |
| + | in_array = utilities.in_array; |
| + | substitute = utilities.substitute; |
| + | error_comment = utilities.error_comment; |
| + | set_error = utilities.set_error; |
| + | select_one = utilities.select_one; |
| + | add_maint_cat = utilities.add_maint_cat; |
| + | wrap_style = utilities.wrap_style; |
| + | safe_for_italics = utilities.safe_for_italics; |
| + | is_wikilink = utilities.is_wikilink; |
| + | make_wikilink = utilities.make_wikilink; |
| + | strip_apostrophe_markup = utilities.strip_apostrophe_markup; |
| + | |
| + | z = utilities.z; -- table of error and category tables in Module:Citation/CS1/Utilities |
| + | |
| + | extract_ids = identifiers.extract_ids; -- imported functions from Module:Citation/CS1/Identifiers |
| + | build_id_list = identifiers.build_id_list; |
| + | is_embargoed = identifiers.is_embargoed; |
| + | extract_id_access_levels = identifiers.extract_id_access_levels; |
| + | |
| + | make_coins_title = metadata.make_coins_title; -- imported functions from Module:Citation/CS1/COinS |
| + | get_coins_pages = metadata.get_coins_pages; |
| + | COinS = metadata.COinS; |
| + | |
| + | local args = {}; -- table where we store all of the template's arguments |
| + | local suggestions = {}; -- table where we store suggestions if we need to loadData them |
| + | local error_text, error_state; |
| | | |
| local config = {}; -- table to store parameters from the module {{#invoke:}} | | local config = {}; -- table to store parameters from the module {{#invoke:}} |
Sətir 3.486: |
Sətir 3.749: |
| for k, v in pairs( pframe.args ) do | | for k, v in pairs( pframe.args ) do |
| if v ~= '' then | | 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, config.CitationClass ) then | | if not validate( k, config.CitationClass ) then |
| error_text = ""; | | error_text = ""; |
Sətir 3.494: |
Sətir 3.760: |
| end | | end |
| elseif validate( k:lower(), config.CitationClass ) then | | elseif validate( k:lower(), config.CitationClass ) then |
− | error_text, error_state = set_error( 'parameter_ignored_suggest', {k, k:lower()}, true ); | + | error_text, error_state = set_error( 'parameter_ignored_suggest', {k, k:lower()}, true ); -- suggest the lowercase version of the parameter |
| else | | else |
| if nil == suggestions.suggestions then -- if this table is nil then we need to load it | | if nil == suggestions.suggestions then -- if this table is nil then we need to load it |
Sətir 3.506: |
Sətir 3.772: |
| capture = k:match (pattern); -- the whole match if no caputre in pattern else the capture if a match | | capture = k:match (pattern); -- the whole match if no caputre in pattern else the capture if a match |
| if capture then -- if the pattern matches | | if capture then -- if the pattern matches |
− | param = substitute( param, capture ); -- add the capture to the suggested parameter (typically the enumerator) | + | param = substitute (param, capture); -- add the capture to the suggested parameter (typically the enumerator) |
− | error_text, error_state = set_error( 'parameter_ignored_suggest', {k, param}, true ); -- set the error message | + | if validate (param, config.CitationClass) 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_error ('parameter_ignored_suggest', {k, param}, true); -- set the suggestion error message |
| + | else |
| + | error_text, error_state = set_error( 'parameter_ignored', {param}, true ); -- suggested param not supported by this template |
| + | end |
| end | | end |
| end | | end |
Sətir 3.523: |
Sətir 3.793: |
| end | | end |
| end | | end |
− | missing_pipe_check (v); -- do we think that there is a parameter that is missing a pipe? | + | missing_pipe_check (k, v); -- do we think that there is a parameter that is missing a pipe? |
− |
| + | -- TODO: is this the best place for this translation? |
− | args[k] = v;
| |
− | elseif args[k] ~= nil or (k == 'postscript') then
| |
| args[k] = v; | | args[k] = v; |
| + | elseif args[k] ~= nil or (k == 'postscript') then -- here when v is empty string |
| + | args[k] = v; -- why do we do this? we don't support 'empty' parameters |
| end | | end |
| end | | end |
Sətir 3.534: |
Sətir 3.804: |
| if 'string' == type (k) then -- don't evaluate positional parameters | | if 'string' == type (k) then -- don't evaluate positional parameters |
| has_invisible_chars (k, v); | | has_invisible_chars (k, v); |
| + | has_extraneous_punc (k, v); -- look for extraneous terminal punctuation in parameter values |
| end | | end |
| end | | end |
− | return citation0( config, args) | + | return table.concat ({citation0( config, args), frame:extensionTag ('templatestyles', '', {src=styles})}); |
| end | | end |
| | | |
− | return cs1; | + | --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ |
| + | ]] |
| + | |
| + | return {citation = citation}; |