Module:Citation/CS1/Identifiers: Difference between revisions
sync from sandbox;
No edit summary |
(sync from sandbox;) |
||
Line 77: | Line 77: | ||
Formats a wiki-style external link | Formats a wiki-style external link | ||
]] | ]] | ||
Line 93: | Line 87: | ||
if options.encode == true or options.encode == nil then | if options.encode == true or options.encode == nil then | ||
url_string = mw.uri.encode( url_string ); | url_string = mw.uri.encode (url_string); | ||
end | end | ||
Line 100: | Line 94: | ||
end | end | ||
ext_link = mw.ustring.format ('[%s%s%s %s]', options.prefix, url_string, options.suffix or "", mw.text.nowiki(options.id)); | ext_link = mw.ustring.format ('[%s%s%s %s]', options.prefix, url_string, options.suffix or "", mw.text.nowiki (options.id)); | ||
if is_set(options.access) then | if is_set (options.access) then | ||
ext_link = substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[options.access].class, cfg.presentation[options.access].title, ext_link}); -- add the free-to-read / paywall lock | ext_link = substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[options.access].class, cfg.presentation[options.access].title, ext_link}); -- add the free-to-read / paywall lock | ||
end | end | ||
Line 116: | Line 110: | ||
Formats a wiki-style internal link | Formats a wiki-style internal link | ||
TODO: Does not currently need to support options.access, options.encode, auto-linking and COinS (as in external_link_id), | |||
but may be needed in the future for :m:Interwiki_map custom-prefixes like :arxiv:, :bibcode:, :DOI:, :hdl:, :ISSN:, | |||
:JSTOR:, :Openlibrary:, :PMID:, :RFC:. | |||
]] | ]] | ||
local function internal_link_id(options) | local function internal_link_id (options) | ||
local id = mw.ustring.gsub (options.id, '%d', cfg.date_names.local_digits); -- translate 'local' digits to Western 0-9 | local id = mw.ustring.gsub (options.id, '%d', cfg.date_names.local_digits); -- translate 'local' digits to Western 0-9 | ||
Line 141: | Line 139: | ||
--[[--------------------------< I S _ E M B A R G O E D >------------------------------------------------------ | --[[--------------------------< I S _ E M B A R G O E D >------------------------------------------------------ | ||
Determines if a PMC identifier's online version is embargoed. Compares the date in |pmc-embargo-date= against today's date. If embargo date is | Determines if a PMC identifier's online version is embargoed. Compares the date in |pmc-embargo-date= against | ||
in the future, returns the content of |pmc-embargo-date=; otherwise, returns an empty string because the embargo has expired or because | today's date. If embargo date is in the future, returns the content of |pmc-embargo-date=; otherwise, returns | ||
|pmc-embargo-date= was not set in this cite. | an empty string because the embargo has expired or because |pmc-embargo-date= was not set in this cite. | ||
]] | ]] | ||
Line 192: | Line 190: | ||
local lang_object = mw.getContentLanguage(); | local lang_object = mw.getContentLanguage(); | ||
good1, biorxiv_ts = pcall (lang_object.formatDate, lang_object, 'U', biorxiv_date ); -- convert biorxiv_date value to Unix timestamp | good1, biorxiv_ts = pcall (lang_object.formatDate, lang_object, 'U', biorxiv_date); -- convert biorxiv_date value to Unix timestamp | ||
good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow | good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow | ||
Line 220: | Line 218: | ||
isxn_str = { isxn_str:byte(1, len) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58 | isxn_str = { isxn_str:byte(1, len) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58 | ||
len = len + 1; -- adjust to be a loop counter | len = len + 1; -- adjust to be a loop counter | ||
for i, v in ipairs( isxn_str ) do -- loop through all of the bytes and calculate the checksum | for i, v in ipairs (isxn_str) do -- loop through all of the bytes and calculate the checksum | ||
if v == string.byte( "X" ) then -- if checkdigit is X (compares the byte value of 'X' which is 0x58) | if v == string.byte ("X" ) then -- if checkdigit is X (compares the byte value of 'X' which is 0x58) | ||
temp = temp + 10*( len - i ); -- it represents 10 decimal | temp = temp + 10 * (len - i); -- it represents 10 decimal | ||
else | else | ||
temp = temp + tonumber( string.char(v) )*(len-i); | temp = temp + tonumber (string.char (v) )*(len-i); | ||
end | end | ||
end | end | ||
Line 243: | Line 241: | ||
isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39 | isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39 | ||
for i, v in ipairs( isxn_str ) do | for i, v in ipairs (isxn_str) do | ||
temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit | temp = temp + (3 - 2*(i % 2)) * tonumber (string.char (v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit | ||
end | end | ||
return temp % 10 == 0; -- sum modulo 10 is zero when ISBN-13/ISMN is correct | return temp % 10 == 0; -- sum modulo 10 is zero when ISBN-13/ISMN is correct | ||
Line 315: | Line 313: | ||
]] | ]] | ||
local function arxiv (id | local function arxiv (options) | ||
local handler = | local id = options.id; | ||
local class = options.Class; -- TODO: lowercase? | |||
local handler = options.handler; | |||
local year, month, version; | local year, month, version; | ||
local err_cat = false; -- assume no error message | local err_cat = false; -- assume no error message | ||
Line 323: | Line 323: | ||
if id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%d$") or id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%dv%d+$") then -- test for the 9107-0703 format with or without version | if id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%d$") or id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%dv%d+$") then -- test for the 9107-0703 format with or without version | ||
year, month = id:match("^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d[v%d]*$"); | year, month = id:match("^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d[v%d]*$"); | ||
year = tonumber(year); | year = tonumber (year); | ||
month = tonumber(month); | month = tonumber (month); | ||
if ((not (90 < year or 8 > year)) or (1 > month or 12 < month)) or -- if invalid year or invalid month | if ((not (90 < year or 8 > year)) or (1 > month or 12 < month)) or -- if invalid year or invalid month | ||
((91 == year and 7 > month) or (7 == year and 3 < month)) then -- if years ok, are starting and ending months ok? | ((91 == year and 7 > month) or (7 == year and 3 < month)) then -- if years ok, are starting and ending months ok? | ||
Line 332: | Line 332: | ||
elseif id:match("^%d%d[01]%d%.%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%dv%d+$") then -- test for the 0704-1412 with or without version | elseif id:match("^%d%d[01]%d%.%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%dv%d+$") then -- test for the 0704-1412 with or without version | ||
year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d[v%d]*$"); | year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d[v%d]*$"); | ||
year = tonumber(year); | year = tonumber (year); | ||
month = tonumber(month); | month = tonumber (month); | ||
if ((7 > year) or (14 < year) or (1 > month or 12 < month)) or -- is year invalid or is month invalid? (doesn't test for future years) | if ((7 > year) or (14 < year) or (1 > month or 12 < month)) or -- is year invalid or is month invalid? (doesn't test for future years) | ||
((7 == year) and (4 > month)) then -- when year is 07, is month invalid (before April)? | ((7 == year) and (4 > month)) then -- when year is 07, is month invalid (before April)? | ||
Line 341: | Line 341: | ||
elseif id:match("^%d%d[01]%d%.%d%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%d%dv%d+$") then -- test for the 1501- format with or without version | elseif id:match("^%d%d[01]%d%.%d%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%d%dv%d+$") then -- test for the 1501- format with or without version | ||
year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d%d[v%d]*$"); | year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d%d[v%d]*$"); | ||
year = tonumber(year); | year = tonumber (year); | ||
month = tonumber(month); | month = tonumber (month); | ||
if ((15 > year) or (1 > month or 12 < month)) then -- is year invalid or is month invalid? (doesn't test for future years) | if ((15 > year) or (1 > month or 12 < month)) then -- is year invalid or is month invalid? (doesn't test for future years) | ||
err_cat = true; -- flag for error message | err_cat = true; -- flag for error message | ||
Line 386: | Line 386: | ||
]] | ]] | ||
local function bibcode (id | local function bibcode (options) | ||
local handler = | local id = options.id; | ||
local access = options.access; | |||
local handler = options.handler; | |||
local err_type; | local err_type; | ||
local year; | local year; | ||
Line 402: | Line 404: | ||
err_type = cfg.err_msg_supl.value; -- so value error | err_type = cfg.err_msg_supl.value; -- so value error | ||
else | else | ||
local next_year = tonumber(os.date ('%Y')) + 1; -- get the current year as a number and add one for next year | local next_year = tonumber (os.date ('%Y')) + 1; -- get the current year as a number and add one for next year | ||
year = tonumber (year); -- convert year portion of bibcode to a number | year = tonumber (year); -- convert year portion of bibcode to a number | ||
if (1000 > year) or (year > next_year) then | if (1000 > year) or (year > next_year) then | ||
Line 414: | Line 416: | ||
if is_set (err_type) then -- if there was an error detected | if is_set (err_type) then -- if there was an error detected | ||
text = text .. ' ' .. set_message( 'err_bad_bibcode', {err_type}); | text = text .. ' ' .. set_message ('err_bad_bibcode', {err_type}); | ||
end | end | ||
return text; | return text; | ||
Line 435: | Line 437: | ||
]] | ]] | ||
local function biorxiv(id | local function biorxiv (options) | ||
local handler = | local id = options.id; | ||
local handler = options.handler; | |||
local err_cat = true; -- flag; assume that there will be an error | local err_cat = true; -- flag; assume that there will be an error | ||
Line 461: | Line 464: | ||
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | ||
prefix = handler.prefix, id = id, separator = handler.separator, | prefix = handler.prefix, id = id, separator = handler.separator, | ||
encode = handler.encode, access = handler.access}) .. (err_cat and (' ' .. set_message( 'err_bad_biorxiv')) or ''); | encode = handler.encode, access = handler.access}) .. (err_cat and (' ' .. set_message ('err_bad_biorxiv')) or ''); | ||
end | end | ||
Line 472: | Line 475: | ||
]] | ]] | ||
local function citeseerx (id | local function citeseerx (options) | ||
local handler = | local id = options.id; | ||
local handler = options.handler; | |||
local matched; | local matched; | ||
Line 482: | Line 486: | ||
matched = id:match ("^10%.1%.1%.[1-9]%d?%d?%d?%.[1-9]%d?%d?%d?$"); | matched = id:match ("^10%.1%.1%.[1-9]%d?%d?%d?%.[1-9]%d?%d?%d?$"); | ||
if not matched then | if not matched then | ||
text = text .. ' ' .. set_message( 'err_bad_citeseerx' ); | text = text .. ' ' .. set_message ('err_bad_citeseerx' ); | ||
end | end | ||
return text; | return text; | ||
Line 505: | Line 509: | ||
]] | ]] | ||
local function doi (id | local function doi (options) | ||
local id = options.id; | |||
local inactive = options.DoiBroken | |||
local access = options.access; | |||
local ignore_invalid = options.accept; | |||
local handler = options.handler; | |||
local err_cat; | local err_cat; | ||
local text; | local text; | ||
if is_set (inactive) then | if is_set (inactive) then | ||
Line 526: | Line 534: | ||
end | end | ||
if is_set(inactive_year) and is_set (inactive_month) then | if is_set (inactive_year) and is_set (inactive_month) then | ||
set_message ('maint_doi_inactive_dated', {inactive_year, inactive_month, ' '}); | set_message ('maint_doi_inactive_dated', {inactive_year, inactive_month, ' '}); | ||
elseif is_set(inactive_year) then | elseif is_set (inactive_year) then | ||
set_message ('maint_doi_inactive_dated', {inactive_year, '', ''}); | set_message ('maint_doi_inactive_dated', {inactive_year, '', ''}); | ||
else | else | ||
Line 546: | Line 554: | ||
'^%d%d?%d?$', -- less than 4 digits without subcode (with subcode is legitimate) | '^%d%d?%d?$', -- less than 4 digits without subcode (with subcode is legitimate) | ||
'^5555$', -- test registrant will never resolve | '^5555$', -- test registrant will never resolve | ||
'% | '[^%d%.]', -- any character that isn't a digit or a dot | ||
} | } | ||
Line 594: | Line 602: | ||
]] | ]] | ||
local function hdl(id | local function hdl (options) | ||
local handler = | local id = options.id; | ||
local access = options.access; | |||
local handler = options.handler; | |||
local query_params = { -- list of known query parameters from http://www.handle.net/proxy_servlet.html | local query_params = { -- list of known query parameters from http://www.handle.net/proxy_servlet.html | ||
'noredirect', | 'noredirect', | ||
Line 630: | Line 640: | ||
if nil == id:match("^[^%s–]-/[^%s–]-[^%.,]$") then -- HDL must contain a forward slash, must not contain spaces, endashes, and must not end with period or comma | if nil == id:match("^[^%s–]-/[^%s–]-[^%.,]$") then -- HDL must contain a forward slash, must not contain spaces, endashes, and must not end with period or comma | ||
text = text .. ' ' .. set_message( 'err_bad_hdl' ); | text = text .. ' ' .. set_message ('err_bad_hdl' ); | ||
end | end | ||
return text; | return text; | ||
Line 642: | Line 652: | ||
]] | ]] | ||
local function isbn (isbn_str | local function isbn (options) | ||
local handler = | local isbn_str = options.id; | ||
local ignore_invalid = options.accept; | |||
local handler = options.handler; | |||
local function return_result (check, err_type) -- local function to handle the various returns | local function return_result (check, err_type) -- local function to handle the various returns | ||
local ISBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect, | local ISBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect, | ||
Line 673: | Line 686: | ||
return return_result (false, cfg.err_msg_supl.form); | return return_result (false, cfg.err_msg_supl.form); | ||
end | end | ||
return return_result (is_valid_isxn(id, 10), cfg.err_msg_supl.check); | return return_result (is_valid_isxn (id, 10), cfg.err_msg_supl.check); | ||
else | else | ||
if id:match ('^%d+$') == nil then | if id:match ('^%d+$') == nil then | ||
Line 703: | Line 716: | ||
]] | ]] | ||
local function asin (id | local function asin (options) | ||
local id = options.id; | |||
local domain = options.ASINTLD; | |||
local err_cat = "" | local err_cat = "" | ||
Line 710: | Line 726: | ||
else | else | ||
if id:match("^%d%d%d%d%d%d%d%d%d[%dX]$") then -- if 10-digit numeric (or 9 digits with terminal X) | if id:match("^%d%d%d%d%d%d%d%d%d[%dX]$") then -- if 10-digit numeric (or 9 digits with terminal X) | ||
if isbn ( | if isbn (options) then -- see if ASIN value is or validates as ISBN-10 | ||
if not id:find ('^630') then -- 630xxxxxxx is (apparently) not a valid isbn prefix but is used by amazon as a numeric identifier | if not id:find ('^630') then -- 630xxxxxxx is (apparently) not a valid isbn prefix but is used by amazon as a numeric identifier | ||
set_message ('maint_asin'); -- begins with something other than 630 so possibly an isbn | set_message ('maint_asin'); -- begins with something other than 630 so possibly an isbn | ||
Line 721: | Line 737: | ||
end | end | ||
end | end | ||
if not is_set(domain) then | if not is_set (domain) then | ||
domain = "com"; | domain = "com"; | ||
elseif in_array (domain, {'jp', 'uk'}) then -- Japan, United Kingdom | elseif in_array (domain, {'jp', 'uk'}) then -- Japan, United Kingdom | ||
Line 728: | Line 744: | ||
domain = "com." .. domain; | domain = "com." .. domain; | ||
end | end | ||
local handler = | |||
local handler = options.handler; | |||
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | ||
prefix = handler.prefix .. domain .. "/dp/", | prefix = handler.prefix .. domain .. "/dp/", | ||
Line 743: | Line 760: | ||
]] | ]] | ||
local function ismn (id | local function ismn (options) | ||
local handler = | local id = options.id; | ||
local handler = options.handler; | |||
local text; | local text; | ||
local valid_ismn = true; | local valid_ismn = true; | ||
Line 752: | Line 770: | ||
id = id:gsub ('[%s-]', ''); -- remove hyphens and white space | id = id:gsub ('[%s-]', ''); -- remove hyphens and white space | ||
if 13 ~= id:len() or id:match( "^9790%d*$" ) == nil then -- ISMN must be 13 digits and begin with 9790 | if 13 ~= id:len() or id:match ("^9790%d*$" ) == nil then -- ISMN must be 13 digits and begin with 9790 | ||
valid_ismn = false; | valid_ismn = false; | ||
else | else | ||
Line 769: | Line 787: | ||
if false == valid_ismn then | if false == valid_ismn then | ||
text = text .. ' ' .. set_message( 'err_bad_ismn' ) -- add an error message if the ISMN is invalid | text = text .. ' ' .. set_message ('err_bad_ismn' ) -- add an error message if the ISMN is invalid | ||
end | end | ||
Line 791: | Line 809: | ||
]] | ]] | ||
local function issn (id | local function issn (options) | ||
local id = options.id; | |||
local handler = options.handler; | |||
local ignore_invalid = options.accept; | |||
local issn_copy = id; -- save a copy of unadulterated ISSN; use this version for display if ISSN does not validate | local issn_copy = id; -- save a copy of unadulterated ISSN; use this version for display if ISSN does not validate | ||
local text; | local text; | ||
local valid_issn = true; | local valid_issn = true; | ||
id = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace | id = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace | ||
if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the ISSN: 8 digits long, containing only 0-9 or X in the last position | if 8 ~= id:len() or nil == id:match ("^%d*X?$" ) then -- validate the ISSN: 8 digits long, containing only 0-9 or X in the last position | ||
valid_issn = false; -- wrong length or improper character | valid_issn = false; -- wrong length or improper character | ||
else | else | ||
valid_issn = is_valid_isxn(id, 8); -- validate ISSN | valid_issn = is_valid_isxn (id, 8); -- validate ISSN | ||
end | end | ||
if true == valid_issn then | if true == valid_issn then | ||
id = string.sub( id, 1, 4 ) .. "-" .. string.sub( id, 5 ); -- if valid, display correctly formatted version | id = string.sub (id, 1, 4 ) .. "-" .. string.sub (id, 5 ); -- if valid, display correctly formatted version | ||
else | else | ||
id = issn_copy; -- if not valid, show the invalid ISSN with error message | id = issn_copy; -- if not valid, show the invalid ISSN with error message | ||
Line 820: | Line 839: | ||
else | else | ||
if false == valid_issn then | if false == valid_issn then | ||
text = text .. ' ' .. set_message ('err_bad_issn', | text = text .. ' ' .. set_message ('err_bad_issn', (options.hkey == 'EISSN') and 'e' or ''); -- add an error message if the ISSN is invalid | ||
end | end | ||
end | end | ||
Line 834: | Line 853: | ||
]] | ]] | ||
local function jfm (id | local function jfm (options) | ||
local handler = | local id = options.id; | ||
local handler = options.handler; | |||
local id_num; | local id_num; | ||
local err_cat = ''; | local err_cat = ''; | ||
Line 843: | Line 863: | ||
if is_set (id_num) then | if is_set (id_num) then | ||
set_message ('maint_jfm_format'); | set_message ('maint_jfm_format'); | ||
else -- plain number without | else -- plain number without JFM prefix | ||
id_num = id; -- if here id does not have prefix | id_num = id; -- if here id does not have prefix | ||
end | end | ||
Line 850: | Line 870: | ||
id = id_num; -- jfm matches pattern | id = id_num; -- jfm matches pattern | ||
else | else | ||
err_cat = ' ' .. set_message( 'err_bad_jfm' ); -- set an error message | err_cat = ' ' .. set_message ('err_bad_jfm' ); -- set an error message | ||
end | end | ||
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | ||
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}) .. err_cat; | prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}) .. err_cat; | ||
end | |||
--[[--------------------------< J S T O R >-------------------------------------------------------------------- | |||
Format a JSTOR with some error checking | |||
]] | |||
local function jstor (options) | |||
local id = options.id; | |||
local access = options.access; | |||
local handler = options.handler; | |||
local err_msg = ''; | |||
if id:find ('[Jj][Ss][Tt][Oo][Rr]') or id:find ('^https?://') or id:find ('%s') then | |||
err_msg = ' ' .. set_message ('err_bad_jstor'); -- set an error message | |||
end | |||
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | |||
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}) .. err_msg; | |||
end | end | ||
Line 872: | Line 913: | ||
]] | ]] | ||
local function lccn(lccn | local function lccn (options) | ||
local handler = | local lccn = options.id; | ||
local handler = options.handler; | |||
local err_cat = ''; -- presume that LCCN is valid | local err_cat = ''; -- presume that LCCN is valid | ||
local id = lccn; -- local copy of the LCCN | local id = lccn; -- local copy of the LCCN | ||
Line 921: | Line 963: | ||
]] | ]] | ||
local function mr (id | local function mr (options) | ||
local handler = | local id = options.id; | ||
local handler = options.handler; | |||
local id_num; | local id_num; | ||
local id_len; | local id_len; | ||
Line 937: | Line 980: | ||
id_len = id_num and id_num:len() or 0; | id_len = id_num and id_num:len() or 0; | ||
if (7 >= id_len) and (0 ~= id_len) then | if (7 >= id_len) and (0 ~= id_len) then | ||
id = string.rep ('0', 7-id_len ) .. id_num; -- zero-fill leading digits | id = string.rep ('0', 7-id_len) .. id_num; -- zero-fill leading digits | ||
else | else | ||
err_cat = ' ' .. set_message ('err_bad_mr'); -- set an error message | err_cat = ' ' .. set_message ('err_bad_mr'); -- set an error message | ||
Line 954: | Line 997: | ||
]] | ]] | ||
local function oclc (id | local function oclc (options) | ||
local handler = | local id = options.id; | ||
local handler = options.handler; | |||
local number; | local number; | ||
local err_msg = ''; -- empty string for concatenation | local err_msg = ''; -- empty string for concatenation | ||
Line 996: | Line 1,040: | ||
]] | ]] | ||
local function openlibrary(id | local function openlibrary (options) | ||
local handler = | local id = options.id; | ||
local access = options.access; | |||
local handler = options.handler; | |||
local ident, code = id:gsub('^OL', ''):match("^(%d+([AMW]))$"); -- strip optional OL prefix followed immediately by digits followed by 'A', 'M', or 'W'; | local ident, code = id:gsub('^OL', ''):match("^(%d+([AMW]))$"); -- strip optional OL prefix followed immediately by digits followed by 'A', 'M', or 'W'; | ||
local error_msg = ''; | local error_msg = ''; | ||
Line 1,017: | Line 1,063: | ||
id = ident, separator = handler.separator, encode = handler.encode, | id = ident, separator = handler.separator, encode = handler.encode, | ||
access = access}) .. error_msg; | access = access}) .. error_msg; | ||
end | |||
--[[--------------------------< O S T I >---------------------------------------------------------------------- | |||
Format OSTI and do simple error checking. OSTIs are sequential numbers beginning at 1 and counting up. This | |||
code checks the OSTI to see that it contains only digits and is less than test_limit specified in the configuration; | |||
the value in test_limit will need to be updated periodically as more OSTIs are issued. | |||
NB. 1018 is the lowest OSTI number found in the wild (so far) and resolving OK on the OSTI site | |||
]] | |||
local function osti (options) | |||
local id = options.id; | |||
local access = options.access; | |||
local handler = options.handler; | |||
local err_cat = ''; -- presume that OSTI is valid | |||
if id:match("[^%d]") then -- if OSTI has anything but digits | |||
err_cat = ' ' .. set_message ('err_bad_osti'); -- set an error message | |||
else -- OSTI is only digits | |||
local id_num = tonumber (id); -- convert id to a number for range testing | |||
if 1018 > id_num or handler.id_limit < id_num then -- if OSTI is outside test limit boundaries | |||
err_cat = ' ' .. set_message ('err_bad_osti'); -- set an error message | |||
end | |||
end | |||
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | |||
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}) .. err_cat; | |||
end | end | ||
Line 1,037: | Line 1,113: | ||
]] | ]] | ||
local function pmc (id | local function pmc (options) | ||
local handler = | local id = options.id; | ||
local embargo = options.Embargo; -- TODO: lowercase? | |||
local handler = options.handler; | |||
local err_cat; | local err_cat; | ||
local id_num; | local id_num; | ||
Line 1,052: | Line 1,130: | ||
if is_set (id_num) then -- id_num has a value so test it | if is_set (id_num) then -- id_num has a value so test it | ||
id_num = tonumber(id_num); -- convert id_num to a number for range testing | id_num = tonumber (id_num); -- convert id_num to a number for range testing | ||
if 1 > id_num or handler.id_limit < id_num then -- if PMC is outside test limit boundaries | if 1 > id_num or handler.id_limit < id_num then -- if PMC is outside test limit boundaries | ||
err_cat = ' ' .. set_message ('err_bad_pmc'); -- set an error message | err_cat = ' ' .. set_message ('err_bad_pmc'); -- set an error message | ||
Line 1,088: | Line 1,166: | ||
]] | ]] | ||
local function pmid(id | local function pmid (options) | ||
local handler = | local id = options.id; | ||
local handler = options.handler; | |||
local err_cat = ''; -- presume that PMID is valid | local err_cat = ''; -- presume that PMID is valid | ||
Line 1,095: | Line 1,174: | ||
err_cat = ' ' .. set_message ('err_bad_pmid'); -- set an error message | err_cat = ' ' .. set_message ('err_bad_pmid'); -- set an error message | ||
else -- PMID is only digits | else -- PMID is only digits | ||
local id_num = tonumber(id); -- convert id to a number for range testing | local id_num = tonumber (id); -- convert id to a number for range testing | ||
if 1 > id_num or handler.id_limit < id_num then -- if PMID is outside test limit boundaries | if 1 > id_num or handler.id_limit < id_num then -- if PMID is outside test limit boundaries | ||
err_cat = ' ' .. set_message ('err_bad_pmid'); -- set an error message | err_cat = ' ' .. set_message ('err_bad_pmid'); -- set an error message | ||
Line 1,103: | Line 1,182: | ||
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | ||
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}) .. err_cat; | prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode}) .. err_cat; | ||
end | |||
--[[--------------------------< R F C >------------------------------------------------------------------------ | |||
Format RFC and do simple error checking. RFCs are sequential numbers beginning at 1 and counting up. This | |||
code checks the RFC to see that it contains only digits and is less than test_limit specified in the configuration; | |||
the value in test_limit will need to be updated periodically as more RFCs are issued. | |||
An index of all RFCs is here: https://tools.ietf.org/rfc/ | |||
]] | |||
local function rfc (options) | |||
local id = options.id; | |||
local handler = options.handler; | |||
local err_cat = ''; -- presume that RFC is valid | |||
if id:match("[^%d]") then -- if RFC has anything but digits | |||
err_cat = ' ' .. set_message ('err_bad_rfc'); -- set an error message | |||
else -- RFC is only digits | |||
local id_num = tonumber (id); -- convert id to a number for range testing | |||
if 1 > id_num or handler.id_limit < id_num then -- if RFC is outside test limit boundaries | |||
err_cat = ' ' .. set_message ('err_bad_rfc'); -- set an error message | |||
end | |||
end | |||
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | |||
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access}) .. err_cat; | |||
end | end | ||
Line 1,116: | Line 1,224: | ||
]] | ]] | ||
local function s2cid (id | local function s2cid (options) | ||
local handler = | local id = options.id; | ||
local access = options.access; | |||
local handler = options.handler; | |||
local err_cat = ''; -- presume that S2CID is valid | local err_cat = ''; -- presume that S2CID is valid | ||
local id_num; | local id_num; | ||
Line 1,125: | Line 1,235: | ||
if is_set (id_num) then -- id_num has a value so test it | if is_set (id_num) then -- id_num has a value so test it | ||
id_num = tonumber(id_num); -- convert id_num to a number for range testing | id_num = tonumber (id_num); -- convert id_num to a number for range testing | ||
if handler.id_limit < id_num then -- if S2CID is outside test limit boundaries | if handler.id_limit < id_num then -- if S2CID is outside test limit boundaries | ||
err_cat = ' ' .. set_message (' | err_cat = ' ' .. set_message ('err_bad_s2cid'); -- set an error message | ||
end | end | ||
else -- when id format incorrect | else -- when id format incorrect | ||
err_cat = ' ' .. set_message (' | err_cat = ' ' .. set_message ('err_bad_s2cid'); -- set an error message | ||
end | end | ||
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | ||
prefix = handler.prefix, id = id | prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access}) .. err_cat; | ||
return text; | return text; | ||
Line 1,147: | Line 1,257: | ||
]] | ]] | ||
local function sbn (id | local function sbn (options) | ||
local handler = | local id = options.id; | ||
local ignore_invalid = options.accept; | |||
local handler = options.handler; | |||
local function return_result (check, err_type) -- local function to handle the various returns | local function return_result (check, err_type) -- local function to handle the various returns | ||
local SBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect, | local SBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect, | ||
Line 1,190: | Line 1,302: | ||
]] | ]] | ||
local function ssrn (id | local function ssrn (options) | ||
local handler = | local id = options.id; | ||
local handler = options.handler; | |||
local err_cat = ''; -- presume that SSRN is valid | local err_cat = ''; -- presume that SSRN is valid | ||
local id_num; | local id_num; | ||
Line 1,199: | Line 1,312: | ||
if is_set (id_num) then -- id_num has a value so test it | if is_set (id_num) then -- id_num has a value so test it | ||
id_num = tonumber(id_num); -- convert id_num to a number for range testing | id_num = tonumber (id_num); -- convert id_num to a number for range testing | ||
if 100 > id_num or handler.id_limit < id_num then -- if SSRN is outside test limit boundaries | if 100 > id_num or handler.id_limit < id_num then -- if SSRN is outside test limit boundaries | ||
err_cat = ' ' .. set_message ('err_bad_ssrn'); -- set an error message | err_cat = ' ' .. set_message ('err_bad_ssrn'); -- set an error message | ||
Line 1,221: | Line 1,334: | ||
]] | ]] | ||
local function usenet_id (id | local function usenet_id (options) | ||
local handler = | local id = options.id; | ||
local handler = options.handler; | |||
local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, | ||
Line 1,245: | Line 1,359: | ||
]] | ]] | ||
local function zbl (id | local function zbl (options) | ||
local handler = | local id = options.id; | ||
local handler = options.handler; | |||
local err_cat = ''; | local err_cat = ''; | ||
Line 1,261: | Line 1,376: | ||
--============================<< I N T E R F A C E F U N C T I O N S >>========================================== | --============================<< I N T E R F A C E F U N C T I O N S >>========================================== | ||
--[[--------------------------< E X T R A C T _ I D S >------------------------------------------------------------ | --[[--------------------------< E X T R A C T _ I D S >------------------------------------------------------------ | ||
Line 1,363: | Line 1,385: | ||
]] | ]] | ||
local function extract_ids( args ) | local function extract_ids (args) | ||
local id_list = {}; -- list of identifiers found in args | local id_list = {}; -- list of identifiers found in args | ||
for k, v in pairs( cfg.id_handlers ) do -- k is uppercase identifier name as index to cfg.id_handlers; e.g. cfg.id_handlers['ISBN'], v is a table | for k, v in pairs (cfg.id_handlers) do -- k is uppercase identifier name as index to cfg.id_handlers; e.g. cfg.id_handlers['ISBN'], v is a table | ||
v = select_one( args, v.parameters, 'err_redundant_parameters' ); | v = select_one (args, v.parameters, 'err_redundant_parameters' ); -- v.parameters is a table of aliases for k; here we pick one from args if present | ||
if is_set(v) then id_list[k] = v; end -- if found in args, add identifier to our list | if is_set (v) then id_list[k] = v; end -- if found in args, add identifier to our list | ||
end | end | ||
return id_list; | return id_list; | ||
Line 1,375: | Line 1,397: | ||
--[[--------------------------< E X T R A C T _ I D _ A C C E S S _ L E V E L S >-------------------------------------- | --[[--------------------------< E X T R A C T _ I D _ A C C E S S _ L E V E L S >-------------------------------------- | ||
Fetches custom id access levels from arguments using configuration settings. | Fetches custom id access levels from arguments using configuration settings. Parameters which have a predefined access | ||
Parameters which have a predefined access level (e.g. arxiv) do not use this | level (e.g. arxiv) do not use this function as they are directly rendered as free without using an additional parameter. | ||
function as they are directly rendered as free without using an additional parameter. | |||
returns a table of k/v pairs where k is same as the identifier's key in cfg.id_handlers and v is the assigned (valid) keyword | |||
access-level values must match the case used in cfg.keywords_lists['id-access'] (lowercase unless there is some special reason for something else) | access-level values must match the case used in cfg.keywords_lists['id-access'] (lowercase unless there is some special reason for something else) | ||
Line 1,383: | Line 1,406: | ||
]] | ]] | ||
local function extract_id_access_levels( args, id_list ) | local function extract_id_access_levels (args, id_list) | ||
local id_accesses_list = {}; | local id_accesses_list = {}; | ||
for k, v in pairs( cfg.id_handlers ) do | for k, v in pairs (cfg.id_handlers) do | ||
local access_param = v.custom_access; -- name of identifier's access-level parameter | local access_param = v.custom_access; -- name of identifier's access-level parameter | ||
if is_set(access_param) then | if is_set (access_param) then | ||
local access_level = args[access_param]; -- get the assigned value if there is one | local access_level = args[access_param]; -- get the assigned value if there is one | ||
if is_set (access_level) then | if is_set (access_level) then | ||
if not in_array (access_level, cfg.keywords_lists['id-access']) then -- exact match required | if not in_array (access_level, cfg.keywords_lists['id-access']) then -- exact match required | ||
table.insert( z.message_tail, { set_message( 'invalid_param_val', {access_param, access_level}, true ) } ); | table.insert (z.message_tail, { set_message ('invalid_param_val', {access_param, access_level}, true) } ); | ||
access_level = nil; -- invalid so unset | access_level = nil; -- invalid so unset | ||
end | end | ||
if not is_set(id_list[k]) then -- identifier access-level must have a matching identifier | if not is_set (id_list[k]) then -- identifier access-level must have a matching identifier | ||
table.insert( z.message_tail, { set_message( 'err_param_access_requires_param', {k:lower()}, true ) } ); -- parameter name is uppercase in cfg.id_handlers (k); lowercase for error message | table.insert (z.message_tail, { set_message ('err_param_access_requires_param', {k:lower()}, true) } ); -- parameter name is uppercase in cfg.id_handlers (k); lowercase for error message | ||
end | end | ||
id_accesses_list[k] = cfg.keywords_xlate[access_level]; -- get translated keyword | id_accesses_list[k] = cfg.keywords_xlate[access_level]; -- get translated keyword | ||
Line 1,402: | Line 1,425: | ||
end | end | ||
return id_accesses_list; | return id_accesses_list; | ||
end | |||
--[[--------------------------< B U I L D _ I D _ L I S T >---------------------------------------------------- | |||
render the identifiers into a sorted sequence table | |||
<id_list_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value | |||
<options_t> is a table of is a table of various k/v option pairs provided in the call to new_build_id_list(); | |||
modified by this function and passed to all identifier rendering functions | |||
<access_levels_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value (if valid) | |||
returns a sequence table of sorted (by hkey) rendered identifier strings | |||
]] | |||
local function build_id_list (id_list_t, options_t, access_levels_t) | |||
local new_list_t = {}; | |||
local accept; | |||
local func_map = { --function map points to functions associated with hkey identifier | |||
['ARXIV'] = arxiv, | |||
['ASIN'] = asin, | |||
['BIBCODE'] = bibcode, | |||
['BIORXIV'] = biorxiv, | |||
['CITESEERX'] = citeseerx, | |||
['DOI'] = doi, | |||
['EISSN'] = issn, | |||
['HDL'] = hdl, | |||
['ISBN'] = isbn, | |||
['ISMN'] = ismn, | |||
['ISSN'] = issn, | |||
['JFM'] = jfm, | |||
['JSTOR'] = jstor, | |||
['LCCN'] = lccn, | |||
['MR'] = mr, | |||
['OCLC'] = oclc, | |||
['OL'] = openlibrary, | |||
['OSTI'] = osti, | |||
['PMC'] = pmc, | |||
['PMID'] = pmid, | |||
['RFC'] = rfc, | |||
['S2CID'] = s2cid, | |||
['SBN'] = sbn, | |||
['SSRN'] = ssrn, | |||
['USENETID'] = usenet_id, | |||
['ZBL'] = zbl, | |||
} | |||
for hkey, v in pairs (id_list_t) do | |||
v, accept = has_accept_as_written (v); -- remove accept-as-written markup if present; accept is boolean true when markup removed; false else | |||
-- every function gets the options table with value v and accept boolean | |||
options_t.hkey = hkey; -- ~/Configuration handler key | |||
options_t.id = v; -- add that identifier value to the options table | |||
options_t.accept = accept; -- add the accept boolean flag | |||
options_t.access = access_levels_t[hkey]; -- add the access level for those that have an |<identifier-access= parameter | |||
options_t.handler = cfg.id_handlers[hkey]; | |||
if func_map[hkey] then | |||
table.insert (new_list_t, {hkey, func_map[hkey] (options_t)}); -- call the function and add the results to the output sequence table | |||
-- TODO: also retrieve identifier validity status, OL A/M/W/X type and ASIN TLD info from the corresponding function call for improved metadata generation in COinS() in ~/COinS | |||
else | |||
error (cfg.messages['unknown_ID_key'] .. ' ' .. hkey); -- here when func_map doesn't have a function for hkey | |||
end | |||
end | |||
local function comp (a, b) -- used by following table.sort() | |||
return a[1]:lower() < b[1]:lower(); -- sort by hkey | |||
end | |||
table.sort (new_list_t, comp); -- sequence table of tables sort | |||
for k, v in ipairs (new_list_t) do -- convert sequence table of tables to simple sequence table of strings | |||
new_list_t[k] = v[2]; | |||
end | |||
return new_list_t; | |||
end | |||
--[[--------------------------< I D E N T I F I E R _ L I S T S _ G E T >-------------------------------------- | |||
Creates two identifier lists: a k/v table of identifiers and their values to be used locally and for use in the | |||
COinS metadata, and a sequence table of the rendered identifier strings that will be included in the rendered | |||
citation. | |||
]] | |||
local function identifier_lists_get (args, options_t) | |||
local ID_list_coins_t = extract_ids (args); -- get a table of identifiers and their values for use locally and for use in COinS | |||
local ID_access_levels_t = extract_id_access_levels (args, ID_list_coins_t); -- get a table of identifier access levels | |||
local ID_list_t = build_id_list (ID_list_coins_t, options_t, ID_access_levels_t); -- get a sequence table of rendered identifier strings | |||
-- TODO: add code to retrieve identifier validity status, OL A/M/W/X type and ASIN TLD info from ID_list_t and add this to ID_list_coins_t for improved metadata generation in COinS() in ~/COinS | |||
return ID_list_t, ID_list_coins_t; -- return the tables | |||
end | end | ||
Line 1,432: | Line 1,550: | ||
auto_link_urls = auto_link_urls, -- table of identifier URLs to be used when auto-linking |title= | auto_link_urls = auto_link_urls, -- table of identifier URLs to be used when auto-linking |title= | ||
identifier_lists_get = identifier_lists_get, -- experiment to replace individual calls to build_id_list, extract_ids, extract_id_access_levels | |||
is_embargoed = is_embargoed; | is_embargoed = is_embargoed; | ||
set_selected_modules = set_selected_modules; | set_selected_modules = set_selected_modules; | ||
} | } |