Module:Citation/CS1/styles.css
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Citation/CS1/styles.css/doc
local cs1 ={}; --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local dates, year_date_check, reformat_dates, date_hyphen_to_dash, -- functions in Module:Citation/CS1/Date_validation date_name_xlate 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; local z ={}; -- tables in Module:Citation/CS1/Utilities local extract_ids, extract_id_access_levels, build_id_list, is_embargoed; -- functions in Module:Citation/CS1/Identifiers local make_coins_title, get_coins_pages, COinS; -- functions in Module:Citation/CS1/COinS local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration local whitelist = {}; -- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist --[[--------------------------< P A G E S C O P E V A R I A B L E S >-------------------------------------- declare variables here that have page-wide scope that are not brought in from other modules; thatare created here and used here ]] local added_deprecated_cat; -- boolean flag so that the category is added only once local added_prop_cats = {}; -- list of property categories that have been added to z.properties_cats local added_vanc_errs; -- boolean flag so we only emit one Vancouver error / category local Frame; -- holds the module's frame table --[[--------------------------< F I R S T _ S E T >------------------------------------------------------------ Locates and returns the first set value in a table of values where the order established in the table, left-to-right (or top-to-bottom), is the order in which the values are evaluated. Returns nil if none are set. This version replaces the original 'for _, val in pairs do' and a similar version that used ipairs. With the pairs version the order of evaluation could not be guaranteed. With the ipairs version, a nil value would terminate the for-loop before it reached the actual end of the list. ]] local function first_set (list, count) local i = 1; while i <= count do -- loop through all items in list if is_set( list[i] ) then return list[i]; -- return the first set list member end i = i + 1; -- point to next end end --[[--------------------------< A D D _ P R O P _ C A T >-------------------------------------------------------- Adds a category to z.properties_cats using names from the configuration file with additional text if any. foreign_lang_source and foreign_lang_source_2 keys have a language code appended to them so that multiple languages may be categorized but multiples of the same language are not categorized. added_prop_cats is a table declared in page scope variables above ]] local function add_prop_cat (key, arguments) if not added_prop_cats [key] then 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 table.insert( z.properties_cats, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table end end --[[--------------------------< A D D _ V A N C _ E R R O R >---------------------------------------------------- Adds a single Vancouver system error message to the template's output regardless of how many error actually exist. To prevent duplication, added_vanc_errs is nil until an error message is emitted. added_vanc_errs is a boolean declared in page scope variables above ]] local function add_vanc_error (source) if not added_vanc_errs then added_vanc_errs = true; -- note that we've added this category table.insert( z.message_tail, { set_error( 'vancouver', {source}, true ) } ); end end --[[--------------------------< I S _ S C H E M E >------------------------------------------------------------ does this thing that purports to be a uri scheme seem to be a valid scheme? The scheme is checked to see if it is in agreement with http://tools.ietf.org/html/std66#section-3.1 which says: Scheme names consist of a sequence of characters beginning with a letter and followed by any combination of letters, digits, plus ("+"), period ("."), or hyphen ("-"). returns true if it does, else false ]] local function is_scheme (scheme) return scheme and scheme:match ('^%a[%a%d%+%.%-]*:'); -- true if scheme is set and matches the pattern end --[=[-------------------------< I S _ D O M A I N _ N A M E >-------------------------------------------------- Does this thing that purports to be a domain name seem to be a valid domain name? Syntax defined here: http://tools.ietf.org/html/rfc1034#section-3.5 BNF defined here: https://tools.ietf.org/html/rfc4234 Single character names are generally reserved; see https://tools.ietf.org/html/draft-ietf-dnsind-iana-dns-01#page-15; see also [[Single-letter second-level domain]] list of tlds: https://www.iana.org/domains/root/db rfc952 (modified by rfc 1123) requires the first and last character of a hostname to be a letter or a digit. Between the first and last characters the name may use letters, digits, and the hyphen. Also allowed are IPv4 addresses. IPv6 not supported domain is expected to be stripped of any path so that the last character in the last character of the tld. tld is two or more alpha characters. Any preceding '//' (from splitting a url with a scheme) will be stripped here. Perhaps not necessary but retained incase it is necessary for IPv4 dot decimal. There are several tests: 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 single-letter/digit second-level domains in the .org and .cash TLDs q, x, and z SL domains in the .com TLD i and q SL domains in the .net TLD single-letter SL domains in the ccTLDs (where the ccTLD is two letters) two-character SL domains in gTLDs (where the gTLD is two or more letters) three-plus-character SL domains in gTLDs (where the gTLD is two or more letters) IPv4 dot-decimal address format; TLD not allowed returns true if domain appears to be a proper name and tld or IPv4 address, else false ]=] local function is_domain_name (domain) if not domain then return false; -- if not set, abandon end 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 return false; end if domain:match ('^%a+:') then -- hack to detect things that look like s:Page:Title where Page: is namespace at wikisource return false; 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 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; end end --[[--------------------------< I S _ U R L >------------------------------------------------------------------ returns true if the scheme and domain parts of a url appear to be a valid url; else false. This function is the last step in the validation process. This function is separate because there are cases that are not covered by split_url(), for example is_parameter_ext_wikilink() which is looking for bracketted external wikilinks. ]] local function is_url (scheme, domain) if is_set (scheme) then -- if scheme is set check it and domain return is_scheme (scheme) and is_domain_name (domain); else return is_domain_name (domain); -- scheme not set when url is protocol relative end end --[[--------------------------< S P L I T _ U R L >------------------------------------------------------------ Split a url into a scheme, authority indicator, and domain. First remove Fully Qualified Domain Name terminator (a dot following tld) (if any) and any path(/), query(?) or fragment(#). If protocol relative url, return nil scheme and domain else return nil for both scheme and domain. When not protocol relative, get scheme, authority indicator, and domain. If there is an authority indicator (one or more '/' characters immediately following the scheme's colon), make sure that there are only 2. Strip off any port and path; ]] local function split_url (url_str) local scheme, authority, domain; url_str = url_str:gsub ('([%a%d])%.?[/%?#].*$', '%1'); -- strip FQDN terminator and path(/), query(?), fragment (#) (the capture prevents false replacement of '//') if url_str:match ('^//%S*') then -- if there is what appears to be a protocol relative url domain = url_str:match ('^//(%S*)') elseif url_str:match ('%S-:/*%S+') then -- if there is what appears to be a scheme, optional authority indicator, and domain name scheme, authority, domain = url_str:match ('(%S-:)(/*)(%S+)'); -- extract the scheme, authority indicator, and domain portions authority = authority:gsub ('//', '', 1); -- replace place 1 pair of '/' with nothing; if is_set(authority) then -- if anything left (1 or 3+ '/' where authority should be) then return scheme; -- return scheme only making domain nil which will cause an error message end domain = domain:gsub ('(%a):%d+', '%1'); -- strip port number if present end return scheme, domain; end --[[--------------------------< L I N K _ P A R A M _ O K >--------------------------------------------------- checks the content of |title-link=, |series-link=, |author-link= etc for properly formatted content: no wikilinks, no urls Link parameters are to hold the title of a wikipedia article so none of the WP:TITLESPECIALCHARACTERS are allowed: # < > [ ] | { } _ except the underscore which is used as a space in wiki urls and # which is used for section links returns false when the value contains any of these characters. When there are no illegal characters, this function returns TRUE if value DOES NOT appear to be a valid url (the |<param>-link= parameter is ok); else false when value appears to be a valid url (the |<param>-link= parameter is NOT ok). ]] local function link_param_ok (value) local scheme, domain; if value:find ('[<>%[%]|{}]') then -- if any prohibited characters return false; end scheme, domain = split_url (value); -- get scheme or nil and domain or nil from url; return not is_url (scheme, domain); -- return true if value DOES NOT appear to be a valid url end --[[--------------------------< L I N K _ T I T L E _ O K >--------------------------------------------------- Use link_param_ok() to validate |<param>-link= value and its matching |<title>= value. |<title>= may be wikilinked but not when |<param>-link= has a value. This function emits an error message when that condition exists ]] local function link_title_ok (link, lorig, title, torig) local orig; if is_set (link) then -- don't bother if <param>-link doesn't have a value if not link_param_ok (link) then -- check |<param>-link= markup orig = lorig; -- identify the failing link parameter elseif title:find ('%[%[') then -- check |title= for wikilink markup orig = torig; -- identify the failing |title= parameter end end if is_set (orig) then table.insert( z.message_tail, { set_error( 'bad_paramlink', orig)}); -- url or wikilink in |title= with |title-link=; end end --[[--------------------------< C H E C K _ U R L >------------------------------------------------------------ Determines whether a URL string appears to be valid. First we test for space characters. If any are found, return false. Then split the url into scheme and domain portions, or for protocol relative (//example.com) urls, just the domain. Use is_url() to validate the two portions of the url. If both are valid, or for protocol relative if domain is valid, return true, else false. Because it is different from a standard url, and because this module used external_link() to make external links that work for standard and news: links, we validate newsgroup names here. The specification for a newsgroup name is at https://tools.ietf.org/html/rfc5536#section-3.1.4 ]] local function check_url( url_str ) if nil == url_str:match ("^%S+$") then -- if there are any spaces in |url=value it can't be a proper url return false; end local scheme, domain; scheme, domain = split_url (url_str); -- get scheme or nil and domain or nil from url; if 'news:' == scheme then -- special case for newsgroups return domain:match('^[%a%d%+%-_]+%.[%a%d%+%-_%.]*[%a%d%+%-_]$'); end return is_url (scheme, domain); -- return true if value appears to be a valid url end --[=[-------------------------< I S _ P A R A M E T E R _ E X T _ W I K I L I N K >---------------------------- Return true if a parameter value has a string that begins and ends with square brackets [ and ] and the first non-space characters following the opening bracket appear to be a url. The test will also find external wikilinks that use protocol relative urls. Also finds bare urls. The frontier pattern prevents a match on interwiki links which are similar to scheme:path urls. The tests that find bracketed urls are required because the parameters that call this test (currently |title=, |chapter=, |work=, and |publisher=) may have wikilinks and there are articles or redirects like '//Hus' so, while uncommon, |title=[[//Hus]] is possible as might be [[en://Hus]]. ]=] local function is_parameter_ext_wikilink (value) local scheme, domain; if value:match ('%f[%[]%[%a%S*:%S+.*%]') then -- if ext wikilink with scheme and domain: [xxxx://yyyyy.zzz] scheme, domain = split_url (value:match ('%f[%[]%[(%a%S*:%S+).*%]')); elseif value:match ('%f[%[]%[//%S+.*%]') then -- if protocol relative ext wikilink: [//yyyyy.zzz] scheme, domain = split_url (value:match ('%f[%[]%[(//%S+).*%]')); elseif value:match ('%a%S*:%S+') then -- if bare url with scheme; may have leading or trailing plain text scheme, domain = split_url (value:match ('(%a%S*:%S+)')); elseif value:match ('//%S+') then -- if protocol relative bare url: //yyyyy.zzz; may have leading or trailing plain text scheme, domain = split_url (value:match ('(//%S+)')); -- what is left should be the domain else return false; -- didn't find anything that is obviously a url end return is_url (scheme, domain); -- return true if value appears to be a valid url end --[[-------------------------< C H E C K _ F O R _ U R L >----------------------------------------------------- loop through a list of parameters and their values. Look at the value and if it has an external link, emit an error message. ]] local function check_for_url (parameter_list) local error_message = ''; for k, v in pairs (parameter_list) do -- for each parameter in the list if is_parameter_ext_wikilink (v) then -- look at the value; if there is a url add an error message if is_set(error_message) then -- once we've added the first portion of the error message ... error_message=error_message .. ", "; -- ... add a comma space separator end error_message=error_message .. "|" .. k .. "="; -- add the failed parameter 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 --[[--------------------------< S A F E _ F O R _ U R L >------------------------------------------------------ Escape sequences for content that will be used for URL descriptions ]] local function safe_for_url( str ) if str:match( "%[%[.-%]%]" ) ~= nil then table.insert( z.message_tail, { set_error( 'wikilink_in_url', {}, true ) } ); end return str:gsub( '[%[%]\n]', { ['['] = '[', [']'] = ']', ['\n'] = ' ' } ); end --[[--------------------------< E X T E R N A L _ L I N K >---------------------------------------------------- Format an external link with error checking ]] local function external_link( URL, label, source, access) local error_str = ""; local domain; local path; local base_url; if not is_set( label ) then label = URL; if is_set( source ) then error_str = set_error( 'bare_url_missing_title', { wrap_style ('parameter', source) }, false, " " ); else error( cfg.messages["bare_url_no_origin"] ); end end if not check_url( URL ) then error_str = set_error( 'bad_url', {wrap_style ('parameter', source)}, false, " " ) .. error_str; end domain, path = URL:match ('^([/%.%-%+:%a%d]+)([/%?#].*)$'); -- split the url into scheme plus domain and path if path then -- if there is a path portion path = path:gsub ('[%[%]]', {['[']='%5b',[']']='%5d'}); -- replace '[' and ']' with their percent encoded values URL = table.concat ({domain, path}); -- and reassemble end base_url = table.concat({ "[", URL, " ", safe_for_url (label), "]" }); -- assemble a wikimarkup url 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 return table.concat ({base_url, error_str}); end --[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >-------------------------------------- Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes the offending parameter name to the error message. Only one error message is emitted regardless of the number of deprecated parameters in the citation. added_deprecated_cat is a boolean declared in page scope variables above ]] local function deprecated_parameter(name) if not added_deprecated_cat then added_deprecated_cat = true; -- note that we've added this category table.insert( z.message_tail, { set_error( 'deprecated_params', {name}, true ) } ); -- add error message end end