وحدات القرآن
مفعلة فرق ملاعب الوصف
وحدة:Quran قالب:وصلة مقارنة الصفحات ملعب [تعديل] تحوي على وظائف القوالب ووظائف مساعدة لها، والتي يتم استدعاؤها من من الصفحات.
وحدات البيانات
وحدة:Quran/Configuration قالب:وصلة مقارنة الصفحات ملعب [تعديل] جداول الترجمة، ومعالجات الأخطاء والمعرفات.
وحدة:Quran/data general قالب:وصلة مقارنة الصفحات ملعب [تعديل] وحدة بيانات تحوي بيانات القرآن غير نص الآيات من أسماء السور وعدد آياتها
وحدة:Quran/data text لا ملعب لها وحدة بيانات تحوي النص القرآني مشكولاً بالرسم الإملائي الخالص
وحدة:Quran/data norm لا ملعب لها وحدة بيانات تحوي النص القرآني بدون تشكيل بالرسم الإملائي الخالص لغرض دعم البحث
وحدة:Quran/data KFGQPC لا ملعب لها وحدة بيانات تحوي النص القرآني بالرسم العثماني وفقًا لرموز خط KFGQPC HAFS Uthmanic Script من ملفات مجمع الملك فهد لطباعة المصحف الشريف
وحدة:Quran/data Othmani لا ملعب لها وحدة بيانات تحوي النص القرآني بالرسم العثماني وفقًا لرموز خط خطوط يونيكودية موافقة في الضبط والوقوف لنسخة مصحف المدينة النبوية

--[[
	القرآن
	Quran
	
	created and maintained in Arabic Wikipedia 
]]
local cfg = mw.loadData('Module:Quran/Configuration')
local quran_text_data
local quran_data 
local quran_norm
local aya_symbol_template = cfg.default.aya_symbol_template
local sour
local quran_shape = cfg.default.shape
local string=mw.ustring
local text=mw.text
local strspace = "[ " .. string.char(160) .. "]"

local function eastern_numbers(thenumber)
	local str_number = tostring(thenumber)
	local ret_str = ''	
	for i=1,#str_number do
		ret_str = ret_str .. string.char(0x0660 + tonumber(string.sub(str_number,i,i)))
	end
	return ret_str
end

local function shape_numbers(thenumber)
	local str_number = tostring(thenumber)
	local numbers_shape = cfg.presentation.numbers_shape
	if numbers_shape == "" then
		return str_number
	end
	local ret_str = ''
	local c_n
	for i=1,#str_number do
		c_n = tonumber(string.sub(str_number,i,i))+1
		ret_str = ret_str .. string.sub(numbers_shape, c_n,c_n)
	end
	return ret_str
end

local gtonumber = tonumber

local function tonumber(str)
    if not str then
        return nil
    end
    local thenumber = gtonumber(str) or mw.language.getContentLanguage():parseFormattedNumber(str)
    if not thenumber then
        return nil
    end
    return math.floor(thenumber)
end

local function set_quran_shape(in_shape)
	if in_shape then
		for k, shapelist in pairs(cfg.shape_aliases) do
			for _, shapevalue in ipairs(shapelist) do
				if in_shape == shapevalue then
					quran_shape = k
					return
				end
			end
		end	
	end
end

local function substitute( msg, args )
	return args and mw.message.newRawMessage( msg, args ):plain() or msg;
end

local function error_comment(msg, args )
	return substitute( cfg.presentation.error, {mw.getCurrentFrame():getParent():getTitle() , substitute( msg, args )} );
end

local function some_aya(souranum,ayanum,start_word,end_word)
	if not start_word and not end_word then
		return quran_data[souranum][ayanum],true
	end
	local fullaya = true
	local ayaText = ' ' .. quran_data[souranum][ayanum] .. ' '
	local f,l,wb
	local fnorm=1
	local norm_ayaText
	if start_word then
		f=string.find(ayaText,' ' .. start_word .. ' ',1,true)
		if not f then
			if not quran_norm then
				quran_norm = mw.loadData('Module:Quran/data norm')
			end
			norm_ayaText=' ' .. quran_norm[souranum][ayanum] .. ' '
			f=string.find(string.gsub(norm_ayaText, string.char(160),' '), ' ' .. start_word .. ' ',1,true)
			fnorm=f
			if not f then
				return error_comment(cfg.msgs.from_word_err)
			end
			if quran_shape ~= 'text' and string.sub(norm_ayaText,f,f)==string.char(160) then
				ff=1
				tmp=1
				while tmp do
					tmp = string.find(string.sub(norm_ayaText,1,f),' ',ff+1,true)
					if tmp then
						ff=tmp
					end
				end
				f=ff
			end
			_, wb = string.gsub(string.sub(norm_ayaText,1,f-1),strspace," ")
			f=1
			for i=1,wb do
				f=string.find(ayaText," ",f+1)
			end
		end
	end
	f=f or 1
	if end_word then
		
		l=string.find(ayaText,' ' .. end_word .. ' ',f,true)
		if l then
			l=l + end_word:len()
		else
			if not quran_norm then
				quran_norm = mw.loadData('Module:Quran/data norm')
			end
			norm_ayaText=' ' .. quran_norm[souranum][ayanum] .. ' '
			l = string.find(string.gsub(norm_ayaText,string.char(160),' '),' ' .. end_word .. ' ',fnorm,true)
			if not l then
				return error_comment(cfg.msgs.to_word_err)
			end
			l=l + string.len(end_word)
			_, wb =string.gsub(string.sub(norm_ayaText,1,l-1),strspace," ")
			l=1
			for i=1,wb do
				l=string.find(ayaText, " ",l+1)
			end
		end
		if l<#ayaText then
			fullaya=false
		end
	end
	return text.trim(string.sub(ayaText,f,l)),fullaya
end

local function argument_wrapper(arg)
	local nilargs = {}
	return setmetatable({},
	{
		__index = function ( tbl, k )
			local v = rawget(tbl,k)
			if v then
				return v
			elseif nilargs[k] then
				return nil
			end
			local list = cfg.aliases[k];
			for _,arglist in ipairs(arg) do
				if type( list ) == 'table' then
					for _, alias_key in ipairs( list ) do
						if arglist[alias_key] then
							v = arglist[alias_key]
							break;
						end
					end
				elseif list ~= nil then
					v = arglist[list]
				end

				if v then
					break;
				end
			end
			if v == nil then
				nilargs[k] = true
			elseif string.len(v) == 0 then
				nilargs[k] = true
				v = nil
			else
				rawset( tbl, k, v )
			end
			return v
		end,
	});
end

local function soura_number( str_soura )
	for i=1,114 do
		if sour[i].name == str_soura then
			return i
		end
	end

	for i=1,114 do
		for _, v in ipairs(sour[i].search) do
			if v == str_soura then
				return i
			end
		end
	end

	return nil
end

local function arg2soura_num(arg_soura)
    local soura_num = tonumber(arg_soura)
    
    if soura_num and (soura_num < 1 or soura_num > 114) then
        return  0, error_comment(cfg.msgs.soura_num_err)
    end
    
    if not soura_num then
        soura_num = soura_number(arg_soura)
        if not soura_num then
            return 0, error_comment(cfg.msgs.soura_name_err)
        end
    end
    return soura_num, ""
end

local function load_data()
	quran_data =  mw.loadData('Module:Quran/data ' .. quran_shape) 
end

local function aya_number(aya_num, frame)
    if aya_symbol_template then
        if cfg.presentation[aya_symbol_template] then
        	return substitute(cfg.presentation[aya_symbol_template], shape_numbers(aya_num))
    	else
        	return frame:expandTemplate{ title = aya_symbol_template, args = { aya_num } }
    	end
    else
        return '&#x06DD;' .. eastern_numbers(aya_num)
    end
end

local function get_ayat(soura, start_aya, end_aya)
	local ret_text	= ''
	for aya_num = start_aya, end_aya do
		ret_text = ret_text .. quran_data[soura][aya_num] .. aya_number(aya_num)
	end
	return string.sub(ret_text,1,#ret_text -1)	
end

local function aya(frame)
	local A = argument_wrapper({frame.args, frame:getParent().args,mw.loadData("Module:Art pref/data")["قرآن"] or {}})
	local soura =  tonumber(A[1] or A.soura or A.s)
	local aya =  tonumber(A.from_aya or A.a)
    local ret = ''
    if soura> 114 or soura<1 then
    	return error_comment(cfg.msgs.soura_num_err)
    end
	
    if A.shape then
		set_quran_shape(A.shape)
    end
    local soura_data = mw.loadData('Module:Quran/data general').sour[soura]
    if aya <1 or aya > soura_data.ayacount then
        return error_comment(cfg.msgs.from_aya_err,{soura_data.name,soura_data.ayacount}) 
    end
	load_data()
	local ret = quran_data[soura][aya]
	if quran_shape == "KFGQPC" then
		ret= string.gsub(ret,'آ', '&#x627;&#x653;')
	end
	return ret
end

local function search(A, frame)
	local soura= A[1] or A.soura
	local start_soura
	local ret = ''
	sour = mw.loadData('Module:Quran/data general').sour
	if soura then
		start_soura, errmsg = arg2soura_num(soura)
        if start_soura == 0 then
            return errmsg
        end
	end
	quran_norm = mw.loadData('Module:Quran/data norm')
	start_soura = start_soura or 1
	for soura_num=start_soura,114 do
		for aya_num=1,sour[soura_num].ayacount do
			if quran_norm[soura_num][aya_num]:gsub("\160"," "):find(A.search) then
				if not quran_data then load_data() end
				ret = ret .. "*" .. substitute(cfg.presentation.quran, { quran_shape, quran_data[soura_num][aya_num] .. ' ' .. aya_number(aya_num,frame)}) .. substitute(cfg.presentation.cite_quran, { sour[soura_num].name, ':' .. aya_num}) .. "\n"
				if A.viewtemplate and A.viewtemplate=="1" or A.viewtemplate=="نعم" then
					ret = ret .. substitute("<pre>{{قرآن|$1|$2}}</pre>",{soura_num,aya_num}) .. "\n"
				end
			end
		end
	end
	if #ret==0 then
		return error_comment("لا نتائج")
	else
		return ret
	end
end

local function ayat(frame)
	local A = argument_wrapper({frame:getParent().args, frame.args,mw.loadData("Module:Art pref/data")["قرآن"] or {}})
	local nocite = A["no-cite"] and A["no-cite"] ~= '0' and A["no-cite"] ~= 'لا'
    if A.shape and string.len(A.shape)>0 then
		set_quran_shape(A.shape)
		if quran_shape=='Text' then
			strspace = "[ " .. string.char(160) .. "]"
		else
			strspace = " "
		end
	end

    if A.aya_template and (cfg.presentation[A.aya_template] or mw.title.new("template:" .. A.aya_template).exists) then
        aya_symbol_template = A.aya_template
    end
    
    if A.search and string.len(A.search)>0 then
        return search(A,frame)    
    end

    sour = mw.loadData('Module:Quran/data general').sour
    local ret_text = ""

    if A.from_aya and string.len(A.from_aya)> 0 then
        local soura_num, soura, from_aya, to_aya, errmsg
        soura_num, errmsg = arg2soura_num(A[1] or A.soura)
        if soura_num == 0 then
            return errmsg
        end

        soura = sour[soura_num]
        if string.find(A.from_aya,'-',1,true) then
			local from_aya_splits = mw.text.split(A.from_aya, '-', true )
			from_aya = tonumber(from_aya_splits[1])
			to_aya = tonumber(from_aya_splits[2])
		else
			from_aya =  tonumber(A.from_aya)
		end
        
		if not from_aya or from_aya>soura.ayacount or from_aya<1 then
            return error_comment(cfg.msgs.from_aya_err,{soura.name,soura.ayacount})
        end

		if not to_aya or to_aya>soura.ayacount then
			local arg_aya_add = A.addition_ayat
			if arg_aya_add then
				to_aya = from_aya + (tonumber(arg_aya_add) or 0)
			elseif A.to_aya then
				to_aya = tonumber(A.to_aya)
			else
				to_aya = from_aya
			end
		end
        
		if to_aya>soura.ayacount or to_aya<1 then
            return error_comment(cfg.msgs.to_aya_err,{soura.name,soura.ayacount})
        end
		
		load_data()
        for aya_num = from_aya, to_aya do
            local fullaya=true
            if (aya_num==from_aya or aya_num == to_aya) and (A.from_word or A.to_word) then
            	local temp_text
            	temp_text, fullaya = some_aya(soura_num,aya_num, 
            		(aya_num==from_aya and A.from_word or nil), 
            		(aya_num == to_aya and A.to_word or nil))
            	if temp_text:find("error") then
            		return temp_text
        		end
            	ret_text = ret_text .. temp_text
        	else
            	ret_text = ret_text .. quran_data[soura_num][aya_num]
        	end
        	
        	if fullaya then
                ret_text = ret_text .. ' ' .. aya_number(aya_num, frame) .. ' '
			end
        end
        
        if quran_shape == "KFGQPC" then
			ret_text = string.gsub(ret_text,'آ', '&#x627;&#x653;')
		end
        
        return substitute(cfg.presentation.quran, { quran_shape, text.trim(ret_text) }) .. ((not nocite) and (substitute(cfg.presentation.cite_quran, { soura.name, ':' .. from_aya .. ((to_aya>from_aya) and ("–" .. to_aya) or '')})) or "")
    else
        ret_text = substitute(cfg.presentation.quran, { quran_shape, A[1] or A.user_text })
        if A.s and string.len(A.s)>0 then
            local soura_num, errmsg = arg2soura_num(A.s)
            if soura_num == 0 then
                return errmsg
            end
            local soura=sour[soura_num]
            ret_text = ret_text .. substitute(cfg.presentation.cite_quran, { soura.name, (A.a and (":" .. A.a) or "")})
        end
        return ret_text
    end
end

local function number_of_soura( frame )	
	local idx = tonumber(frame.args[1])
	if idx and idx>=1 and idx <=114 then
		return idx
	end
	sour = mw.loadData('Module:Quran/data general').sour
	return soura_number(frame.args[1])
end


local function soura_name( frame )	
	local idx = tonumber(frame.args[1])
	if not idx or idx<1 or idx > 114 then
		return nil
	end
	sour = mw.loadData('Module:Quran/data general').sour
	return sour[idx].name
end

local function aya_count( frame )	
	local idx = tonumber(frame.args[1])
	if not idx or idx<1 or idx > 114 then
		return nil
	end
	sour = mw.loadData('Module:Quran/data general').sour
	return sour[idx].ayacount
end

return { 
		aya = aya,
		ayat=ayat,
		number_of_soura = number_of_soura,
		soura_name = soura_name,
		aya_count = aya_count
}