Mô đun:Check isxn

--

This code is derived from the ISXN validation code at Module:Citation/CS1. It allows validating ISBN,
ISMN, and ISSN without invoking a citation template.

local p = {}

------------------------------------------------------------------------------

error message supplements for check_isbn(); adapted from a similarly named table at Module:Citation/CS1/Configuration

local err_msg_supl_t = {
['char'] = 'invalid character',
['check'] = 'checksum',
['form'] = 'invalid form',
['group'] = 'invalid group id',
['length'] = 'length',
['prefix'] = 'invalid prefix',
}

---------------------------------------------------------------------------------

ISBN-10 and ISSN validator code calculates checksum across all isbn/issn digits including the check digit. ISBN-13 is checked in check_isbn().
If the number is valid the result will be 0. Before calling this function, issbn/issn must be checked for length and stripped of dashes,
spaces and other non-isxn characters.

local function is_valid_isxn (isxn_str, len)
local temp = 0;
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
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)
temp = temp + 10( len - i ); -- it represents 10 decimal
else
temp = temp + tonumber( string.char(v) )(len-i);
end
end
return temp % 11 == 0; -- returns true if calculation result is zero
end

--------------------------------------------------------------------------

ISBN-13 and ISMN validator code calculates checksum across all 13 isbn/ismn digits including the check digit.
If the number is valid, the result will be 0. Before calling this function, isbn-13/ismn must be checked for length
and stripped of dashes, spaces and other non-isxn-13 characters.

local function is_valid_isxn_13 (isxn_str)
local temp=0;

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
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
return temp % 10 == 0; -- sum modulo 10 is zero when isbn-13/ismn is correct
end

----------------------------------------------------------------------------------------

Determines whether an ISBN string is valid. Implements an ISBN validity check for , , , and
.

local function check_isbn (isbn_str, frame)
local function return_result (check, err_type) -- local function to render the various error returns
if not check then -- false when there is an error
local template = ((frame.args.template_name and ~= frame.args.template_name) and frame.args.template_name) or nil; -- get template name
if not template then
return '&nbsp;calling template requires template_name parameter';
end

local out_t = {}; -- open the error message span
table.insert (out_t, '&nbsp;Parameter error in :&nbsp;'); -- close wikilink; close 'template' markup
table.insert (out_t, err_type); -- type of isbn error
table.insert (out_t, ) -- close the error message span
if 0 == mw.title.getCurrentTitle().namespace then -- categorize only when this template is used in mainspace
local category = table.concat ({});
table.insert (out_t, category);
end
return table.concat (out_t); -- make a big string and done
end
return ; -- no error, return an empty string
end

if nil ~= isbn_str:match ('[^%s-0-9X]') then
return return_result (false, err_msg_supl_t.char); -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X
end

local id = isbn_str:gsub ('[%s-]', ); -- remove hyphens and whitespace

local len = id:len();

if len ~= 10 and len ~= 13 then
return return_result (false, err_msg_supl_t.length); -- fail if incorrect length
end

if len 10 then
if id:match ('^%dX?$') nil then -- fail if isbn_str has 'X' anywhere but last position
return return_result (false, err_msg_supl_t.form);
end
if id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not valid isbn group ids but are used by amazon as numeric identifiers (asin)
return return_result (false, err_msg_supl_t.group); -- fail if isbn-10 begins with 630/1
end
return return_result (is_valid_isxn (id, 10), err_msg_supl_t.check); -- pass if isbn-10 is numerically valid (checksum)
else
if id:match ('^%d+$') nil then
return return_result (false, err_msg_supl_t.char); -- fail if ISBN-13 is not all digits
end
if id:match ('^97[89]%d$') nil then
return return_result (false, err_msg_supl_t.prefix); -- fail when ISBN-13 does not begin with 978 or 979
end
if id:match ('^9790') then
return return_result (false, err_msg_supl_t.group); -- group identifier '0' is reserved to ISMN
end
return return_result (is_valid_isxn_13 (id), err_msg_supl_t.check); -- pass if isbn-10 is numerically valid (checksum)
end
end

----------------------------------------------------------------------------------------

Determines whether an ISMN string is valid. Similar to isbn-13, ismn is 13 digits begining 979-0-... and uses the
same check digit calculations. See http://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf
section 2, pages 9–12.

local function check_ismn (id, error_string)
local text;
local valid_ismn = true;

id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and endashes from the ismn

if 13 ~= id:len() or id:match( "^9790%d$" ) == nil then -- ismn must be 13 digits and begin 9790
valid_ismn = false;
else
valid_ismn=is_valid_isxn_13 (id); -- validate ismn
end

return valid_ismn and or error_string
end

--issn=0819 4327 gives: 4327 0819 4327 -- can't have spaces in an external link

This code now prevents that by inserting a hyphen at the issn midpoint. It also validates the issn for length and makes sure that the checkdigit agrees
with the calculated value. Incorrect length (8 digits), characters other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check issn
error message.

local function check_issn(id, error_string)
local issn_copy = id; -- save a copy of unadulterated issn; use this version for display if issn does not validate
local text;
local valid_issn = true;

if not id:match ('^%d%d%d%d%-%d%d%d[%dX]$') then
return error_string;
end

id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and endashes from the issn

if 8 ~= id:len() or nil == id:match( "^%dX?$" ) 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
else
valid_issn=is_valid_isxn(id, 8); -- validate issn
end

return valid_issn and or error_string
end

--------------------------------------------------------------------------------====

function p.check_isbn(frame)
return check_isbn(frame.args[1] or frame:getParent().args[1], frame)
end

function p.check_ismn(frame)
return check_ismn(frame.args[1] or frame:getParent().args[1], frame.args['error'] or frame:getParent().args['error'] or 'error')
end

function p.check_issn(frame)
return check_issn(frame.args[1] or frame:getParent().args[1], frame.args['error'] or frame:getParent().args['error'] or 'error')
end

return p