[go: nahoru, domu]

--[[

Este código é derivado do código de validação ISXN no Módulo:Citação/CS1. Ele permite validar ISBN, ISMN, e ISSN sem ter que
invocar uma predefinição de citação.

]]

local p = {}


--[[--------------------------< E R R _ M S G _ S U P L _ T >--------------------------------------------------

Mensagem de erro suplementar para check_isbn(); adaptado de uma tabela nomeada simelhante de Módulo:Citação/CS1/Configuração

]]

local err_msg_supl_t = {
	['char'] = 'caractere inválido',
	['check'] = 'soma de verificação',
	['form'] = 'forma inválida',
	['group'] = 'id do grupo inválido',
	['length'] = 'comprimento',
	['prefix'] = 'prefixo inválido',
	}


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

O código validador ISBN-10 e ISSN calcula a soma de verificação em todos os dígitos isbn/issn, incluindo o dígito de verificação. ISBN-13 é
verificado em check_isbn().
Se o número for válido, o resultado será 0. Antes de chamar esta função, issbn/issn deve ser verificado quanto ao comprimento e sem travessões,
espaços e outros caracteres não isxn.

]]

local function is_valid_isxn (isxn_str, len)
	local temp = 0;
	isxn_str = { isxn_str:byte(1, len) };	-- cria uma tabela de valores de bytes '0' → 0x30 .. '9' → 0x39, 'X' → 0x58
	len = len+1;							-- ajuste para ser um contador de loop
	for i, v in ipairs( isxn_str ) do		-- faz um loop por todos os bytes e calcular a soma de verificação
		if v == string.byte( "X" ) then		-- se o dígito de verificação é X (compara o valor de byte de 'X' que é 0x58)
			temp = temp + 10*( len - i );	-- representa o 10 decimal
		else
			temp = temp + tonumber( string.char(v) )*(len-i);
		end
	end
	return temp % 11 == 0;					-- retorna verdadeiro de o resultado do cálculo é zero
end


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

O código validador ISBN-13 e ISMN calcula a soma de verificação em todos os 13 dígitos isbn/ismn, incluindo o dígito de verificação.
Se o número for válido, o resultado será 0. Antes de chamar esta função, isbn-13/ismn deve ser verificado quanto ao comprimento e sem
travessões, espaços e outros caracteres diferentes de isxn-13.

]]

local function is_valid_isxn_13 (isxn_str)
	local temp=0;
	
	isxn_str = { isxn_str:byte(1, 13) };										-- cria uma tabela com os valores de bytes '0' → 0x30 .. '9'  → 0x39
	for i, v in ipairs( isxn_str ) do
		temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) );				-- multiplica índices ímpares por 1, pares por 3 e soma; incluindo o dígito de verificação
	end
	return temp % 10 == 0;														-- soma modulo 10 é zero quando a isbn-13/ismn está correta
end


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

Determina se uma ISBN é válida.

]]

local function check_isbn (isbn_str, frame)
	local function return_result (check, err_type)								-- função local para renderizar os vários retornos de erro
		if not check then														-- <check> é falso quando há um erro
			local template = ((frame.args.template_name and '' ~= frame.args.template_name) and frame.args.template_name) or nil;	-- pega o nome da predefinição
			if not template then
				return '<span class="error" style="font-size:100%">&nbsp;a chamada da predefinição requer o parâmetro template_name</span>';
			end

			local out_t = {'<span class="error" style="font-size:100%">'};		-- abre o span da mensagem de erro
			table.insert (out_t, '&nbsp;Erro de parâmetro em {{[[Predefinição:'); -- abre a marcação para 'predefinição'; abre a ligação para o domínio predefinição
			table.insert (out_t, template);										-- ligação para o nome da predefinição
			table.insert (out_t, '|');											-- pipe
			table.insert (out_t, template);										-- etiqueta da ligação
			table.insert (out_t, ']]}}:&nbsp;');								-- fecha ligação; fecha a marcação 'predefinição'
			table.insert (out_t, err_type);										-- tipo do erro da isbn
			table.insert (out_t, '</span>')										-- fecha o span da mensagem de erro
			if 0 == mw.title.getCurrentTitle().namespace then					-- categoriza apenas se a predefinição é utilizada no domínio principal
				local category = table.concat ({'[[Categoria:!Páginas com erro de ISBN]]'});
				table.insert (out_t, category);
			end
			return table.concat (out_t);										-- cria uma grande string e finaliza
		end
	return '';																	-- se não há erros, retorna uma string vazia
	end

	if nil ~= isbn_str:match ('[^%s-0-9X]') then
		return return_result (false, err_msg_supl_t.char);						-- erro se isbn_str contiver qualquer coisa além de dígitos, hifens ou X maiúsculo
	end

	local id = isbn_str:gsub ('[%s-]', '');										-- remove hifens e espaços em branco

	local len = id:len();
 
	if len ~= 10 and len ~= 13 then
		return return_result (false, err_msg_supl_t.length);					-- erro se o comprimento for incorreto
	end

	if len == 10 then
		if id:match ('^%d*X?$') == nil then										-- erro se isbn_str contiver 'X' em qualquer lugar que não seja no último dígito
			return return_result (false, err_msg_supl_t.form);									
		end
		if id:find ('^63[01]') then												-- 630xxxxxxx e 631xxxxxxx (aparentemente) não são IDs de grupo isbn válidos, mas são usados pela Amazon como identificadores numéricos (asin)
			return return_result (false, err_msg_supl_t.group);					-- erro se isbn-10 começa com 630/1
		end
		return return_result (is_valid_isxn (id, 10), err_msg_supl_t.check);	-- aprova se isbn-10 for numericamente válido (soma de verificação)
	else
		if id:match ('^%d+$') == nil then
			return return_result (false, err_msg_supl_t.char);					-- erro se ISBN-13 não for apenas dígitos
		end
		if id:match ('^97[89]%d*$') == nil then
			return return_result (false, err_msg_supl_t.prefix);				-- erro se ISBN-13 não começar com 978 ou 979
		end
		if id:match ('^9790') then
			return return_result (false, err_msg_supl_t.group);					-- o grupo identificador '0' é resercado para ISMN
		end
		return return_result (is_valid_isxn_13 (id), err_msg_supl_t.check);		-- aprova se isbn-10 for numericamente válido (soma de verificação)
	end
end


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

Determina se uma sequência ISMN é válida. Semelhante ao isbn-13, o ismn tem 13 dígitos começando com 979-0-... e usa os mesmos
cálculos de dígitos de verificação. Consulte http://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf
seção 2, páginas 9–12.

]]

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

	id=id:gsub( "[%s-–]", "" );													-- retira espaços, hífens e travessões do ismn

	if 13 ~= id:len() or id:match( "^9790%d*$" ) == nil then					-- ismn deve conter 13 dígitos e começar com 9790
		valid_ismn = false;
	else
		valid_ismn=is_valid_isxn_13 (id);										-- valida ismn
	end

	return valid_ismn and '' or error_string
end


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

Valida e formata um issn. Este código corrige o caso em que um editor incluiu um ISSN na citação, mas separou os dois grupos de quatro dígitos com
um espaço. Quando essa condição ocorre, a ligação resultante fica assim:

	|issn=0819 4327 gera: [http://www.worldcat.org/issn/0819 4327 0819 4327]	-- não poda haver espações numa ligação externa
	
Este código agora evita isso inserindo um hífen no ponto médio do issn. Ele também valida o comprimento do issn e garante que o dígito de verificação esteja de
acordo com o valor calculado. Comprimento incorreto (8 dígitos), caracteres diferentes de 0-9 e X ou incompatibilidade de dígito de verificação/valor calculado
causarão uma mensagem de erro de verificação issn.

]]

local function check_issn(id, error_string)
	local issn_copy = id;		-- salva uma cópia da issn não alterada; usa esta versão para exibir se a issn não for válida
	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-–]", "" );									-- retirar espaços, hífens e travessões do issn

	if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then		-- valida o issn: 8 digitos de comprimento, contendo apenas 0-9 ou X nã última posição
		valid_issn=false;										-- comprimento errado ou caractere impróprio
	else
		valid_issn=is_valid_isxn(id, 8);						-- valida issn
	end

	return valid_issn and '' or error_string
end


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

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