Module:Citation/CS1: Difference between revisions
synch from sandbox;
imported>Rob Kam m (1 revision imported) |
enwiki:Mediawiki>Trappist the monk (synch from sandbox;) |
||
Line 415: | Line 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 | 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) | ||
base_url = substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[access].class, cfg.presentation[access].title, base_url}); -- add the appropriate icon | |||
end | end | ||
return table.concat({ base_url, error_str }); | return table.concat ({base_url, error_str}); | ||
end | end | ||
Line 693: | Line 681: | ||
if position then | if position then | ||
if 'nowiki' == capture or 'math' == capture | if 'nowiki' == capture or 'math' == capture or -- nowiki and math stripmarkers (not an error condition) | ||
stripmarker = true; | ('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 | 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 | position = nil; -- unset | ||
Line 764: | Line 753: | ||
--[[--------------------------< | --[[--------------------------< N O W R A P _ D A T E >-------------------------------------------------------- | ||
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. | |||
]] | ]] | ||
local function | local function nowrap_date (date) | ||
local | local cap=''; | ||
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 | |||
return date; | |||
end | |||
--[[--------------------------< S E T _ T I T L E T Y P E >---------------------------------------------------- | |||
This function sets default title types (equivalent to the citation including |type=<default value>) for those templates that have defaults. | |||
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) | |||
if | if is_set(title_type) then | ||
if "none" == title_type then | |||
title_type = ""; -- if |type=none then type parameter not displayed | |||
end | end | ||
return title_type; -- if |type= has been set to any other value use that value | |||
end | end | ||
return cfg.title_types [cite_class] or ''; -- set template's default title type; else empty string for concatenation | |||
end | end | ||
--[[--------------------------< N O | --[[--------------------------< H Y P H E N _ T O _ D A S H >-------------------------------------------------- | ||
Converts a hyphen to a dash under certain conditions. The hyphen must separate like items; unlike items are | |||
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. | |||
str may be a comma- or semicolon-separated list | |||
]] | ]] | ||
local function | local function hyphen_to_dash( str ) | ||
if not is_set (str) then | |||
return str; | |||
end | |||
if | |||
str, count = str:gsub ('^%(%((.+)%)%)$', '%1'); -- remove accept-this-as-written markup when it wraps all of str | |||
if 0 ~= count then -- non-zero when markup removed; zero else | |||
return str; -- nothing to do, we're done | |||
end | end | ||
return | 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 | --[[--------------------------< 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 ) | |||
local function safe_join( tbl, duplicate_char ) | |||
local f = {}; -- create a function table appropriate to type of 'dupicate character' | local f = {}; -- create a function table appropriate to type of 'dupicate character' | ||
if 1 == #duplicate_char then -- for single byte ascii characters use the string library functions | if 1 == #duplicate_char then -- for single byte ascii characters use the string library functions | ||
Line 2,059: | Line 2,012: | ||
--[[--------------------------< | --[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------ | ||
This is the main function doing the majority of the citation formatting. | |||
This is the main function doing the majority of the citation formatting. | |||
]] | ]] | ||
Line 2,246: | Line 2,174: | ||
-- conference & map books do not support issue | -- 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 | 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']; | Issue = hyphen_to_dash (A['Issue']); | ||
end | 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 | ||
Line 2,631: | Line 2,559: | ||
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 = { | local date_parameters_list = { | ||
['access-date'] = {val=AccessDate, name=A:ORIGIN ('AccessDate')}, | ['access-date'] = {val=AccessDate, name=A:ORIGIN ('AccessDate')}, | ||
Line 2,954: | Line 2,879: | ||
-- Format main title. | -- Format main title. | ||
if | if is_set (ArchiveURL) and mw.ustring.match (mw.ustring.lower(Title), cfg.special_case_translation['archived_copy']) then -- if title is 'Archived copy' (place holder added by bots that can't find proper title) | ||
add_maint_cat ('archived_copy'); -- add maintenance category before we modify the content of Title | |||
end | end | ||
if Title:match ('^%(%(.*%)%)$') then -- if keep as written markup: | |||
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 | |||
if is_set(TitleLink) and is_set(Title) then | if is_set(TitleLink) and is_set(Title) then | ||
Title = make_wikilink (TitleLink, Title); | Title = make_wikilink (TitleLink, Title); | ||
Line 3,459: | Line 3,394: | ||
--[[--------------------------< | --[[--------------------------< V A L I D A T E >-------------------------------------------------------------- | ||
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 | local function validate (name, cite_class) | ||
local name = tostring (name); | |||
local | local state; | ||
local | |||
if | if in_array (cite_class, {'arxiv', 'biorxiv', 'citeseerx'}) then -- limited parameter sets allowed for these templates | ||
state = whitelist.limited_basic_arguments[name]; | |||
whitelist | 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 | |||
state = whitelist[cite_class .. '_basic_arguments'][name]; -- look in the parameter-list for the template identified by cite_class | |||
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 | |||
-- limited enumerated parameters list | |||
name = name:gsub("%d+", "#" ); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits) | |||
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 | |||
else -- otherwise | 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 | |||
-- all enumerated parameters allowed | |||
name = name:gsub("%d+", "#" ); -- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits) | |||
state = whitelist.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 | |||
--[[--------------------------< M I S S I N G _ P I P E _ C H E C K >------------------------------------------ | |||
Look at the contents of a parameter. If the content has a string of characters and digits followed by an equal | |||
sign, compare the alphanumeric string to the list of cs1|2 parameters. If found, then the string is possibly a | |||
parameter that is missing its pipe: | |||
{{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 (value) | |||
local capture; | |||
value = value:gsub ('%b<>', ''); -- remove xml/html tags because attributes: class=, title=, etc | |||
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 | |||
--[[--------------------------< C S 1 . C I T A T I O N >------------------------------------------------------ | |||
This is used by templates such as {{cite book}} to create the actual citation text. | |||
]] | |||
function cs1.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 | cfg = mw.loadData ('Module:Citation/CS1/Configuration'); -- load live versions of support modules | ||
whitelist = mw.loadData ('Module:Citation/CS1/Whitelist'); | whitelist = mw.loadData ('Module:Citation/CS1/Whitelist'); | ||
Line 3,485: | Line 3,509: | ||
identifiers = require ('Module:Citation/CS1/Identifiers'); | identifiers = require ('Module:Citation/CS1/Identifiers'); | ||
metadata = require ('Module:Citation/CS1/COinS'); | metadata = require ('Module:Citation/CS1/COinS'); | ||
styles = 'Module:Citation/CS1/styles.css'; | |||
end | end | ||
Line 3,535: | Line 3,561: | ||
if v ~= '' then | if v ~= '' then | ||
if ('string' == type (k)) then | if ('string' == type (k)) then | ||
k = mw.ustring.gsub (k, '%d', cfg.date_names.local_digits); | k = mw.ustring.gsub (k, '%d', cfg.date_names.local_digits); -- for enumerated parameters, translate 'local' digits to Western 0-9 | ||
end | end | ||
if not validate( k, config.CitationClass ) then | if not validate( k, config.CitationClass ) then | ||
Line 3,545: | Line 3,571: | ||
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 | ||
Line 3,557: | Line 3,583: | ||
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 | ||
Line 3,587: | Line 3,617: | ||
end | end | ||
end | end | ||
return citation0( config, args) | return table.concat ({citation0( config, args), frame:extensionTag ('templatestyles', '', {src=styles})}); | ||
end | end | ||
return cs1; | return cs1; |