Moduł:ISBN

Z VatoWiki
Przejdź do nawigacji Przejdź do wyszukiwania

Dokumentacja dla tego modułu może zostać utworzona pod nazwą Moduł:ISBN/opis

local resources = {
	classLink = "isbn",
	classIncorrect = "isbn-incorrect",
	classSeparator = "isbn-do-sprawdzenia",
	classJustified = "isbn-usprawiedliwiony",
	classPretty = "isbn-ulepszony",
	specialBooksPrefix = "Specjalna:Książki/",
	findLinkPrefix = "Moduł:ISBN/",
	isbnPrefix = "ISBN ",
	categoryIncorrectNumber = "[[Kategoria:Artykuły z nieprawidłowymi numerami ISBN]]",
	categoryInvalidNumber = "[[Kategoria:Artykuły z błędnymi numerami ISBN]]",
	errorSyntax = "nieprawidłowa składnia",
	errorFormal = "numer jest poprawny, możliwe, że ten sam numer jest przypisany do kilku różnych tytułów",
	errorCheck10 = "nieprawidłowa cyfra kontrolna w numerze ISBN-10",
	errorCheck13 = "nieprawidłowa cyfra kontrolna w numerze ISBN-13",
	errorPretend13 = "numer ISBN-13 zawiera niewłaściwe cyfry na kluczowych pozycjach",
	defaultPrefix13 = "978-",
}

local function deduceSeparators(number, prefix)

	local function deduce(region, regionLen)
		for _, v in ipairs(region) do
			local minimum, maximum = string.match(v, "^(%d-)%-(%d-)$")
			if minimum and maximum and #minimum==#maximum and (minimum <= maximum) then
				local width = #minimum
				local minimum = tonumber(minimum)
				local maximum = tonumber(maximum)
				local publisher = tonumber(string.sub(number, regionLen+1, regionLen+width))
				if (minimum <= publisher) and (publisher <= maximum) then
					return string.sub(number, 1, regionLen).."-"..string.sub(number, regionLen+1, regionLen+width).."-"..string.sub(number, regionLen+width+1)
				end
			end
		end
	end
	
	local publishers = mw.loadData( "Moduł:ISBN/wydawcy" )

	local regionLen = 1
	while regionLen <= 5 do
		local region = publishers[(prefix or resources.defaultPrefix13)..string.sub(number, 1, regionLen)]
		if region then
			local pretty = deduce(region, regionLen)
			if pretty then
				return pretty
			end
		end
		
		regionLen = regionLen + 1
	end
	
	if prefix and (prefix ~= defaultPrefix13) then
		regionLen = 1
		while regionLen <= 5 do
			region = publishers["978-"..string.sub(number, 1, regionLen)]
			if region then
				local pretty = deduce(region, regionLen)
				if pretty then
					return pretty
				end
			end
			
			regionLen = regionLen + 1
		end
	end
end

local function analyze(isbn)
	local result = {}
	
	result.isbn = isbn
	if string.match(isbn, "^[0-9][0-9%-]+[0-9]%-?[0-9Xx]$") and not string.match(isbn, "%-%-") then
		-- numer ma odowiednie cyfry z opcjonalnymi separatorami
		local clean, n = string.gsub(isbn, "%-", "")
		result.code, result.n = string.upper(clean), n
		result.justified = mw.title.new(resources.findLinkPrefix..clean).exists
	end
	
	if result.code and string.match(result.code, "^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9X]$") then
		-- ISBN-10
		local b10, b9, b8, b7, b6, b5, b4, b3, b2, b1 = string.byte(result.code, 1, 10)
		result.expectedSum = (11 - ((10*(b10-48)+9*(b9-48)+8*(b8-48)+7*(b7-48)+6*(b6-48)+5*(b5-48)+4*(b4-48)+3*(b3-48)+2*(b2-48)) % 11)) % 11
		result.receivedSum = b1 == 88 and 10 or (b1 - 48)
		result.kind = 10
		result.error = result.expectedSum ~= result.receivedSum and resources.errorCheck10 or nil
		result.separatorWarn = (result.n~=0) and ((result.n~=3) or not string.match(isbn, "[0-9]%-[0-9Xx]$"))
		result.prefix = false
		result.number = string.sub(result.code, 1, 9)
		result.checksum = "-"..string.sub(result.code, 10, 10)
	elseif result.code and (string.match(result.code, "^978[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$") or string.match(result.code, "^979[1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$")) then
		-- ISBN-13
		local b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13 = string.byte(result.code, 1, 13)
		result.expectedSum = (10 - (((b1-48)+3*(b2-48)+(b3-48)+3*(b4-48)+(b5-48)+3*(b6-48)+(b7-48)+3*(b8-48)+(b9-48)+3*(b10-48)+(b11-48)+3*(b12-48)) % 10)) % 10
		result.receivedSum = b13 - 48
		result.kind = 13
		result.error = result.expectedSum ~= result.receivedSum and resources.errorCheck13 or nil
		result.separatorWarn = (result.n~=0) and ((result.n~=4) or not string.match(isbn, "^97[89]%-[0-9][0-9%-]-[0-9]%-[0-9]$"))
		result.prefix = string.sub(result.code, 1, 3).."-"
		result.number = string.sub(result.code, 4, 12)
		result.checksum = "-"..string.sub(result.code, 13, 13)
	elseif result.code and string.match(result.code, "^9[78][78][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9X]$") then
		-- ten numer udaje, że jest ISBN-13
		result.error = resources.errorPretend13
	else
		-- to nie jest numer ISBN, ma nieprawidłową liczbę cyfr lub nieprawidłowe znaki
		result.error = resources.errorSyntax
	end

	if result.justified and not result.error then
		result.error = resources.errorFormal
	end
	
	if not result.error and (result.n == 0) and (result.prefix ~= nil) and result.checksum and result.number then
		local prettyNumber = deduceSeparators(result.number, result.prefix)
		if prettyNumber then
			result.org = result.isbn
			result.isbn = (result.prefix or "")..prettyNumber..result.checksum
		end
	end

	return result
end

local function printISBN(builder, info, prefix)

	local ns = mw.title.getCurrentTitle().namespace
	
	if info.error and not info.justified and not mw.site.namespaces[ns].isTalk and (ns >= 0) and (ns <= 102) then
		mw.addWarning(mw.text.nowiki("{{ISBN|"..info.isbn.."}} "..info.error))
	end
	
	if not info.code then
		builder
			:wikitext(prefix)
			:tag("span")
				:addClass(resources.classIncorrect)
				:attr("title", mw.getContentLanguage():ucfirst(info.error))
				:wikitext(mw.text.nowiki(info.isbn), ns == 0 and resources.categoryIncorrectNumber or "")
		return
	end
	
	builder
		:wikitext("[[", resources.specialBooksPrefix, info.code, "|", prefix)
		:tag("span")
			:addClass(resources.classLink)
			:addClass(info.justified and resources.classJustified or nil)
			:addClass((not info.justified and info.error) and resources.classIncorrect or nil)
			:addClass(info.separatorWarn and resources.classSeparator or nil)
			:addClass(info.org and resources.classPretty or nil)
			:attr("title", info.error and mw.getContentLanguage():ucfirst(info.error) or nil)
			:wikitext(string.upper(info.isbn))
			:done()
		:wikitext("]]")
		:wikitext((not info.justified and info.error and (ns == 0)) and resources.categoryIncorrectNumber or "")
		:wikitext((info.justified and (ns == 0)) and resources.categoryInvalidNumber or "")
end

return {

link = function(builder, isbn)
	if isbn then
		local info = analyze(isbn)
		printISBN(builder, info, resources.isbnPrefix)
		return tostring(builder)
	end
	
	local isbn = builder[1] or builder.args[1] or builder:getParent().args[1]
	if isbn then
		isbn = mw.text.trim(isbn)
		if #isbn >= 0 then
			local info = analyze(isbn)
			local builder = mw.html.create()
			printISBN(builder, info, resources.isbnPrefix)
			return tostring(builder)
		end
	end
end,

opis = function(frame)
	local isbn = type(frame) == "string" and frame or (frame[1] or frame.args[1] or frame:getParent().args[1] or mw.title.getCurrentTitle().subpageText)
	local info = analyze(isbn)
	local result = mw.html.create()
		:tag("tt"):wikitext("{{[[Szablon:ISBN|ISBN]]|", mw.text.nowiki(isbn), "}}"):done()
		:wikitext(" → ")
	printISBN(result, info, resources.isbnPrefix)
	if info.error then
		result:wikitext("\n* ", mw.getContentLanguage():ucfirst(info.error), ".")
	end
	
	if info.justified then
		local status, data = pcall(mw.loadData, "Moduł:ISBN/"..info.code)
		if status then
			_ = mw.title.new("Module:ISBN/nietrywialne moduły generujące dokumentację").id
			result:wikitext("\n----")
			for i, v in ipairs(data) do
				local text = false
				if (type(v) == "table") and (type(v.title) == "string") and (type(v.args) == "table") then
					text = mw.getCurrentFrame():expandTemplate(v)
				elseif type(v) == "string" then
					text = mw.getCurrentFrame():preprocess(v)
				end
				
				if text and #text > 0 then
					result:wikitext("\n# ", text)
				end
			end
		
			if data.kategoria and type(data.kategoria) == "string" and (#data.kategoria > 0) then
				result:wikitext("\n[[Kategoria:", data.kategoria, "]]")
			end
		end
	end

	return tostring(result)
end,

}