Module:Sandbox/Polygnotus2
-- Module:ISBNValidator
-- A module to validate ISBN-10 and ISBN-13 strings with or without dashes
local p = {}
-- Main validation function
function p.isValid(frame)
local isbn = frame.args[1] or ""
if frame:getParent() then
isbn = frame.args[1] or frame:getParent().args[1] or ""
end
-- Clean the input and determine ISBN type
local cleanISBN, isbnType = p._cleanISBN(isbn)
-- Validate according to type
local result = false
if isbnType == 10 then
result = p._isValidISBN10(cleanISBN)
elseif isbnType == 13 then
result = p._isValidISBN13(cleanISBN)
end
return result and "valid" or "invalid"
end
-- Function to get the ISBN type (10 or 13)
function p.getType(frame)
local isbn = frame.args[1] or ""
if frame:getParent() then
isbn = frame.args[1] or frame:getParent().args[1] or ""
end
local _, isbnType = p._cleanISBN(isbn)
if isbnType == 10 then
return "ISBN-10"
elseif isbnType == 13 then
return "ISBN-13"
else
return "Unknown or invalid ISBN format"
end
end
-- Internal function to clean ISBN and determine type
function p._cleanISBN(isbn)
-- Trim whitespace
isbn = isbn:match("^%s*(.-)%s*$")
-- Remove common prefixes
isbn = isbn:gsub("^ISBN[%s:-]*", "")
isbn = isbn:gsub("^ISBN%s*[%s:-]*", "")
-- Remove all non-alphanumeric characters
local cleanISBN = isbn:gsub("[^%dXx]", "")
-- Determine type based on length
local isbnType = nil
if #cleanISBN == 10 then
isbnType = 10
elseif #cleanISBN == 13 then
isbnType = 13
end
return cleanISBN:upper(), isbnType
end
-- Validate ISBN-10
function p._isValidISBN10(isbn)
if #isbn ~= 10 then
return false
end
-- Check that first 9 characters are digits
if not isbn:sub(1, 9):match("^%d+$") then
return false
end
-- Check that last character is digit or 'X'
local lastChar = isbn:sub(10, 10)
if not (lastChar:match("%d") or lastChar == "X") then
return false
end
-- Calculate checksum
local sum = 0
for i = 1, 9 do
sum = sum + tonumber(isbn:sub(i, i)) * (11 - i)
end
-- Add the check digit
if isbn:sub(10, 10) == "X" then
sum = sum + 10
else
sum = sum + tonumber(isbn:sub(10, 10))
end
-- Valid if sum is divisible by 11
return sum % 11 == 0
end
-- Validate ISBN-13
function p._isValidISBN13(isbn)
if #isbn ~= 13 then
return false
end
-- Check all characters are digits
if not isbn:match("^%d+$") then
return false
end
-- Check that it starts with 978 or 979 (current ISBN-13 prefixes)
if not (isbn:sub(1, 3) == "978" or isbn:sub(1, 3) == "979") then
return false
end
-- Calculate checksum (alternating 1 and 3 weights)
local sum = 0
for i = 1, 13 do
local weight = (i % 2 == 1) and 1 or 3
sum = sum + tonumber(isbn:sub(i, i)) * weight
end
-- Valid if sum is divisible by 10
return sum % 10 == 0
end
-- Normalize ISBN to standard format with dashes
function p.normalize(frame)
local isbn = frame.args[1] or ""
if frame:getParent() then
isbn = frame.args[1] or frame:getParent().args[1] or ""
end
local cleanISBN, isbnType = p._cleanISBN(isbn)
-- Check if valid before normalizing
local isValid = false
if isbnType == 10 then
isValid = p._isValidISBN10(cleanISBN)
elseif isbnType == 13 then
isValid = p._isValidISBN13(cleanISBN)
end
if not isValid then
return "Invalid ISBN: " .. isbn
end
-- Normalize format based on type
if isbnType == 10 then
-- ISBN-10 typical format: group-publisher-title-check
-- This is simplified; actual grouping depends on ISBN ranges
-- For demonstration, use a simple 1-2-6-1 pattern
return cleanISBN:sub(1, 1) .. "-" ..
cleanISBN:sub(2, 3) .. "-" ..
cleanISBN:sub(4, 9) .. "-" ..
cleanISBN:sub(10, 10)
else -- ISBN-13
-- ISBN-13 typical format: prefix-group-publisher-title-check
-- This is simplified; actual grouping depends on ISBN ranges
-- For demonstration, use a simple 3-1-2-6-1 pattern
return cleanISBN:sub(1, 3) .. "-" ..
cleanISBN:sub(4, 4) .. "-" ..
cleanISBN:sub(5, 6) .. "-" ..
cleanISBN:sub(7, 12) .. "-" ..
cleanISBN:sub(13, 13)
end
end
-- Convert between ISBN-10 and ISBN-13 (when possible)
function p.convert(frame)
local isbn = frame.args[1] or ""
local targetType = frame.args[2] or "13" -- Default to converting to ISBN-13
if frame:getParent() then
isbn = frame.args[1] or frame:getParent().args[1] or ""
targetType = frame.args[2] or frame:getParent().args[2] or "13"
end
local cleanISBN, isbnType = p._cleanISBN(isbn)
-- Check if valid before converting
local isValid = false
if isbnType == 10 then
isValid = p._isValidISBN10(cleanISBN)
elseif isbnType == 13 then
isValid = p._isValidISBN13(cleanISBN)
end
if not isValid then
return "Invalid ISBN: " .. isbn
end
-- Convert based on source and target type
if isbnType == 10 and targetType == "13" then
return p._convertISBN10to13(cleanISBN)
elseif isbnType == 13 and targetType == "10" then
return p._convertISBN13to10(cleanISBN)
else
-- Already in target format
return cleanISBN
end
end
-- Convert ISBN-10 to ISBN-13
function p._convertISBN10to13(isbn10)
-- ISBN-13 starts with 978 prefix + first 9 digits of ISBN-10
local isbn13base = "978" .. isbn10:sub(1, 9)
-- Calculate the check digit
local sum = 0
for i = 1, 12 do
local weight = (i % 2 == 1) and 1 or 3
sum = sum + tonumber(isbn13base:sub(i, i)) * weight
end
local checkDigit = (10 - (sum % 10)) % 10
return isbn13base .. checkDigit
end
-- Convert ISBN-13 to ISBN-10 (only works for 978 prefix)
function p._convertISBN13to10(isbn13)
-- Check if it has 978 prefix (only these can be converted to ISBN-10)
if isbn13:sub(1, 3) ~= "978" then
return "Cannot convert to ISBN-10: ISBN-13 does not have 978 prefix"
end
-- Take digits 4-12 of the ISBN-13
local isbn10base = isbn13:sub(4, 12)
-- Calculate the check digit
local sum = 0
for i = 1, 9 do
sum = sum + tonumber(isbn10base:sub(i, i)) * (11 - i)
end
local remainder = sum % 11
local checkDigit = (11 - remainder) % 11
if checkDigit == 10 then
checkDigit = "X"
end
return isbn10base .. checkDigit
end
return p
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.