Moduuli:ISxN

Wikipediasta
Siirry navigaatioon Siirry hakuun

-- This template is a copy of the ISXN validation code from [[Module:Citation/CS1]]
-- which allows for validating ISBN, ISMN, and ISSN without invoking a citation template

local p = {}

--[[--------------------------< IS _ V A L I D _ I S X N >-----------------------------------------------------

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


--[[--------------------------< IS _ V A L I D _ I S X N  _ 1 3 >----------------------------------------------

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

-- :Fr:Biblio/Références
local function checkean13( ean_str )
	if type( ean_str ) ~= 'string' then
		return false
	end
	
	ean_str = ean_str:gsub( '[-%s]', '' )						-- supprime les traits d’union et espaces
	if ean_str:len() == 13 and ean_str:match( '^%d+$' ) then
		local temp, strVal = 0
		ean_str = { ean_str:byte( 1, 13 ) }
		for i = 1, #ean_str do
			strVal = tonumber( string.char( ean_str[i] ) )
			temp = temp + ( 3 - 2 * ( i % 2 ) ) * strVal
		end
		return temp % 10 == 0
	end
	return false
end

--[[--------------------------< C H E C K _ I S B N >------------------------------------------------------------

Determines whether an ISBN string is valid

]]

local function loc_check_isbn( isbn_str )
	if nil ~= isbn_str:match ('[^%s-0-9X]') then
		return false;													-- 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 false;
	end

	if len == 10 then
		if id:match ('^%d*X?$') == nil then								-- fail if isbn_str has 'X' anywhere but last position
			return false;									
		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 false;												-- fail if isbn-10 begins with 630/1
		end
		return is_valid_isxn (id, 10);									-- pass if isbn-10 is numerically valid (checksum)
	else
		if id:match ('^%d+$') == nil then
			return false;												-- fail if ISBN-13 is not all digits
		end
		if id:match ('^97[89]%d*$') == nil then
			return false;												-- fail when ISBN-13 does not begin with 978 or 979
		end
		if id:match ('^9790') then
			return false;												-- group identifier '0' is reserved to ISMN
		end
		return is_valid_isxn_13 (id);									-- pass if isbn-10 is numerically valid (checksum)
	end
end

--[[--------------------------< C H E C K _ I S M N >------------------------------------------------------------

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.

]]

-- fr-wikin versio vanhemmalle ISMN-tunnisteelle (10 merkkiä, alussa "M")
local function isValidIsmn10( ismn )
	local temp = 9
	if ismn:match( 'M%d%d%d%d%d%d%d%d%d' ) then
		for i = 2, 10 do
			temp = temp + ( 1 + 2 * ( i % 2 ) ) * ismn:sub( i, i )
		end
	end
	return temp % 10 == 0
end

local function loc_check_ismn (ismn_str)
	if type( ismn_str ) ~= 'string' then
		return false
	end
	id=ismn_str:gsub( "[%s-–]", "" );													-- strip spaces, hyphens, and endashes from the ismn
	
	-- vanhempi 10 merkin ISMN, alkaa M-kirjaimella
	if id:len() == 10 then
		return isValidIsmn10( id, 10 )
	end

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

--[[--------------------------< I S S N >----------------------------------------------------------------------

Validate and format an issn.  This code fixes the case where an editor has included an ISSN in the citation but has separated the two groups of four
digits with a space.  When that condition occurred, the resulting link looked like this:

	|issn=0819 4327 gives: [http://www.worldcat.org/issn/0819 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 loc_check_issn(id)
	if not id:match ('^%d%d%d%d%-%d%d%d[%dX]$') then
		return false;
	end
	
	id=id:gsub( "[%s-–]", "" );									-- strip spaces, hyphens, and endashes from the issn

	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
		return false;										-- wrong length or improper character
	else
		return is_valid_isxn(id, 8);						-- validate issn
	end
end


------------------------------< E N T R Y   P O I N T S >--------------------------------------------------====

-- entry point for another lua-module: just return true (ok) or false (invalid)
function p._check_isbn(id)
	return loc_check_isbn(id)
end

function p._check_ismn(id)
	return loc_check_ismn(id)
end

function p._check_issn(id)
	return loc_check_issn(id)
end

-- entry points for templates
function p.check_isbn(frame)
	local id = frame.args[1] or frame:getParent().args[1]
	if (loc_check_isbn(id) == false) then
		return frame.args['error'] or frame:getParent().args['error'] or 'error'
	end
	return ''
end

function p.check_ismn(frame)
	local id = frame.args[1] or frame:getParent().args[1]
	if (loc_check_ismn(id) == false) then
		return frame.args['error'] or frame:getParent().args['error'] or 'error'
	end
	return ''
end

function p.check_issn(frame)
	local id = frame.args[1] or frame:getParent().args[1]
	if (loc_check_issn(id) == false) then
		return frame.args['error'] or frame:getParent().args['error'] or 'error'
	end
	return ''
end

return p