انتقل إلى المحتوى

وحدة:Cite Q/ملعب

من ويكاموس، القاموس الحر
--[[	Cite Q
		النسخة العربية
		مشتق من النسخة الإنجليزية 
]]

local p = {}

require('strict')
local sandbox = ''
local WdItem = require('Module:WdItem' .. sandbox)
local cfg = mw.loadData('Module:Cite Q/config' .. sandbox)

local getStatementsByVol = WdItem.getStatementsByVol

local rtl_lang = {ar = true, fa  = true, ur = true, ku = true,
he  = true, arc  = true, dv  = true, ha  = true, khw  = true,
ks  = true,	ps  = true, yi  = true, ota = true }
local ref_cash = {}


--[[-------------------< A R G U M E N T _ W R A P P E R >----------------------

Argument wrapper.  This function provides support for argument mapping defined
in the configuration file so that multiple names can be transparently aliased to
single internal variable.

]]

local function argument_wrapper ( arglist )
	local origin = {};
	local nilargs = {};
	return setmetatable({
		ORIGIN = function ( self, k )
			local dummy = self[k];												-- force the variable to be loaded.
			return origin[k];
		end
	},
	{
		__index = function ( tbl, k )
			local v = rawget(tbl,k)
			if v then
				return v
			elseif nilargs[k] then
				return nil
			end
			local list = (type(k)=="number") and k or cfg.aliases[k];
			if type( list ) == 'table' then
				for _, alias_key in pairs( list ) do
					if arglist[alias_key] then
						v = arglist[alias_key]
						origin[k] = alias_key
						break;
					end
				end
			elseif list ~= nil then
				v = arglist[list]
				if v then
					origin[k] = list
				end
			end

			if v == nil then
				nilargs[k] = true
			else
				rawset( tbl, k, v )
			end

			return v
		end,

	});
end

-------------------------------------------------------------------------------
-- makeOrdinal needs to be internationalised along with the above i18n
-- takes cardinal number as a numeric and returns the ordinal as a string
-- we need three exceptions in English for 1st, 2nd, 3rd, 21st, .. 31st, etc.
-------------------------------------------------------------------------------
p.makeOrdinal = function(cardinal)
	local card = tonumber(cardinal)
	if not card then return cardinal end
	local ordsuffix = cfg.i18n.ordinal.default
	if card % 10 == 1 then
		ordsuffix = cfg.i18n.ordinal[1]
	elseif card % 10 == 2 then
		ordsuffix = cfg.i18n.ordinal[2]
	elseif card % 10 == 3 then
		ordsuffix = cfg.i18n.ordinal[3]
	end
	-- In English, 1, 21, 31, etc. use 'st', but 11, 111, etc. use 'th'
	-- similarly for 12 and 13, etc.
	if (card % 100 == 11) or (card % 100 == 12) or (card % 100 == 13) then
		ordsuffix = cfg.i18n.ordinal.default
	end
	return card .. ordsuffix
end

--[[--------------------------< I S _ S E T >--------------------------------------------------------------
Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.
]]
local function is_set( var )
	return not (var == nil or var == '')
end

--[[--------------------------< I N _ A R R A Y >--------------------------------------------------------------
Whether needle is in haystack (taken from Module:Citation/CS1/Utilities)
]]
local function in_array( needle, haystack )
	if needle == nil then
		return false
	end
	for n, v in ipairs( haystack ) do
		if v == needle then
			return n
		end
	end
	return false
end


--[[--------------------------< A C C E P T _ V A L U E >-------------------------------------------------------
Accept WD value by framing in ((...)) if param_val is equal to keyword; else pass-through WD value as is.
]]
local function accept_value( param_val, wd_val )
	local val = param_val

	if val then
		if in_array (val, {'accept', '))((', ':d:'}) then
			val = '((' .. wd_val .. '))'
		elseif '((accept))' == val then
			val = 'accept'
		elseif '(())(())' == val then
			val = '))(('
		elseif '((:d:))' == val then
			val = ':d:'
		else
			val = wd_val
		end
	end

	return val
end



-- function to fetch a value to display
local function makelink(v, out, link, maxpos, wdl, lang)
	local label
	if v.mainsnak.snaktype == "value" then
		if v.mainsnak.datatype == "wikibase-item" then
			local qnumber = v.mainsnak.datavalue.value.id
			local sitelink = mw.wikibase.getSitelink(qnumber)
			if qnumber == "Q2818964" then sitelink = nil end -- suppress link to "Various authors"
			-- من المهم التحقق من وجود datavalue
			if v.qualifiers and v.qualifiers.P1932 and v.qualifiers.P1932[1].datavalue then
				label = v.qualifiers.P1932[1].datavalue.value
			else
				label = mw.wikibase.getLabelByLang(qnumber, lang) or mw.wikibase.getLabel(qnumber)
				if label then
					label = mw.text.nowiki(label)
				else
					label = qnumber -- should add tracking category
				end
			end
			local position = maxpos + 1 -- Default to 'next' author.
			-- use P1545 (series ordinal) instead of default position.
			if v["qualifiers"] and v.qualifiers["P1545"] and v.qualifiers["P1545"][1] then
				position = tonumber(v.qualifiers["P1545"][1].datavalue.value)
			end
			maxpos = math.max(maxpos, position)
			if sitelink then
				-- just the plain name,
				-- but keep a record of the links, using the same index
				out[position] = label
				link[position] = sitelink
			else
				if wdl then
					-- show that there's a Wikidata entry available
					out[position] = "[[:d:Q" .. v.mainsnak.datavalue.value["numeric-id"] .. "|" .. label .. "]]&nbsp;<span title='" .. cfg.i18n["errors"]["local-article-not-found"] .. "'>[[ملف:Wikidata-logo.svg|16px|alt=|link=]]</span>"
				else
					-- no Wikidata links wanted, so just give the plain label
					out[position] = label
				end
			end
		elseif v.mainsnak.datatype == "string" then
			local position = maxpos + 1 -- Default to 'next' author.
			-- use P1545 (series ordinal) instead of default position.
			if v["qualifiers"] and v.qualifiers["P1545"] and v.qualifiers["P1545"][1] then
				position = tonumber(v.qualifiers["P1545"][1].datavalue.value)
			end
			maxpos = math.max(maxpos, position)
			out[position] = v.mainsnak.datavalue.value
		else
			-- not a wikibase-item or a string!
		end
	else
		-- code here if we want to return something when author is "unknown"
		-- من المهم التحقق من وجود datavalue
		if v.qualifiers and v.qualifiers.P1932 and v.qualifiers.P1932[1].datavalue then
			label = v.qualifiers.P1932[1].datavalue.value
		else
			label = mw.wikibase.getLabelByLang("Q4233718", lang):gsub("^%l", mw.ustring.upper) 
		end
		maxpos = maxpos + 1
		out[maxpos] = label
	end
	return maxpos
end

--[=[-------------------------< G E T _ N A M E _ L I S T >----------------------------------------------------
get_name_list -- adapted from getAuthors code taken from Module:RexxS
arguments:
	nl_type - type of name list to fetch: nl_type = 'author' for authors; 'editor' for editors; 'translator' for translators
	args - pointer to the parameter arguments table from the template call
	qid - value from |qid= parameter; the Q-id of the source (book, etc.) in qid
	wdl - value from the |wdl= parameter; a Boolean passed to enable links to Wikidata when no article exists
returns nothing; modifies the args table
]=]

local function get_name_list (nl_type, args, qid, wdl, lang, vol, chapter, refsnak)
	local propertyID = "P50"
	local fallbackID = "P2093" -- author name string

    if nl_type =="author" then
		propertyID = 'P50'		-- for authors
		fallbackID = 'P2093'	-- author-string
	elseif nl_type =="editor" then
		propertyID = 'P5769'	-- "editor-in-chief"
		fallbackID = 'P98'		-- for editors - So-called "fallbacks" are actually a second set of properties processed
		-- TBD. Take book series editors into account as well (if they have a separate P code as well)?
	elseif nl_type == "translator" then
		propertyID = 'P655'		-- for translators
		fallbackID = nil
--	elseif 'contributor' == nl_type then
--		f.e. author of forewords (P2679) and afterwords (P2680); requires |contribution=, |title= and |author=
--		propertyID = 'P'		-- for contributors
--		fallbackID = nil
	else
		return					-- not specified so return
	end

	-- wdl is a Boolean passed to enable links to Wikidata when no article exists
	-- if "false" or "no" or "0" is passed set it false
	-- if nothing or an empty string is passed set it false
	if wdl and (#wdl > 0) then
		wdl = wdl:lower()
		wdl = in_array (wdl, {"false", "no", "0"})
	else
		-- wdl is empty, so
		wdl = false
	end

	local props = nil
	local fallback = nil
	if qid then
		if mw.wikibase.entityExists(qid) then
			props = getStatementsByVol(qid, propertyID, vol, chapter)
            if #props == 0 and fallbackID then
				fallback = getStatementsByVol(qid, fallbackID, vol, chapter)
			end
		end
	elseif refsnak then
		props = refsnak[propertyID]
		if not props and fallbackID then
			fallback = refsnak[fallbackID]
		end
	end
	-- Make sure it actually has at least one of the properties requested
	if not (props and props[1]) and not (fallback and fallback[1]) then
		return nil
	end

	-- So now we have something to return:
	-- table 'out' is going to store the names(s):
	-- and table 'link' will store any links to the name's article
	local out = {}
	local link = {}
	local maxpos = 0
	if props and props[1] then
		for k, v in pairs(props) do
			maxpos = makelink(v, out, link, maxpos, wdl, lang)
		end
	end
	if fallback and fallback[1] then
		-- second properties
		for k, v in pairs(fallback) do
			maxpos = makelink(v, out, link, maxpos, wdl, lang)
		end
	end

	-- if there's anything to return, then insert the additions in the template arguments table
	-- in the form |author1=firstname secondname |author2= ...
	-- Renumber, in case we have inconsistent numbering
	local keys = {}
	for k, v in pairs(out) do
		keys[#keys + 1] = k
	end
	table.sort(keys) -- as they might be out of order
	for i, k in ipairs(keys) do
		out[k] = out[k]:gsub ('&#39;', '\'');									-- prevent cs1|2 multiple names categorization; replace html entity with the actual character
		if args[nl_type .. i] then -- name gets overwritten
			-- pull corresponding -link only if overwritten name is same as WD name
			if link[k] and (args[nl_type .. i] == out[k]) then
				args[nl_type .. '-link' .. i] = args[nl_type .. '-link' .. i] or link[k] -- author-linkn or editor-linkn
			end
		else -- name does not get overwritten, so pull name from WD
			args[nl_type .. i] = out[k]
			if link[k] then
				args[nl_type .. '-link' .. i] = args[nl_type .. '-link' .. i] or link[k] -- author-linkn or editor-linkn
			end
		end
	end
end


function p._getLangOfSnakProp(snak, pid)
	if not pid or not snak then return {} end
	local out = {}
	local props = snak[pid]
	for i, v in ipairs(props) do
		if v.datatype == "monolingualtext" and v.datavalue then
			out[#out + 1] = v.datavalue.value.language
		end
	end
	return out
end

function p.getLangOfProp(frame)
	local pid = frame.args.pid or mw.text.trim(frame.args[1] or "")
	if pid == "" then return end
	local qid = frame.args.qid
	if qid == "" then qid = nil end
	local Q = WdItem.new(qid, { fwd = "ALL", osd = false, noicon = true })
	return table.concat(Q:getLangOfProp(pid), ", ")
end

-- gets the language codes of a Wikidata entry as a table
local function _lang_code(Qitem)
	local lc = Qitem:getPropOfProp( "P407", "P424")
	if lc then return mw.text.split( lc, "[,، ]+" ) end --modified
	lc = Qitem:getPropOfProp( "P407", "P218" )
	if lc then return mw.text.split( lc, "[,، ]+" ) end --modified
	return Qitem:getLangOfProp("P1476")
end

function p.lang_code(frame)
	local Q = WdItem.newItem(frame.args.qid or mw.text.trim(frame.args[1] or ""), { fwd = "ALL", osd = false, noicon = true })
	return table.concat(_lang_code(Q), ", ")
end

-- wraps a string in nowiki unless disable flag is set
local function wrap_nowiki(str, disable)
	if disable then return str or '' end
	return mw.text.nowiki(str or '')
end

-- sort sequence table whose values are key-value pairs by key
local function comp_key(a, b)
	local param_a, enum_a = a[1]:match ('(%D+)(%d+)$');							-- get param name and enumerator from <a>
	local param_b, enum_b = b[1]:match ('(%D+)(%d+)$');							-- get param name and enumerator from <b>

	if enum_a and enum_b then													-- if both parameters have neumerators
		if param_a == param_b then												-- are parameter names the same?
			return tonumber (enum_a) < tonumber (enum_b);						-- yes: compare enumerators
		end
	end

	return a[1] < b[1];															-- alpha sort if here
end

-- sort sequence table whose values are key-value pairs by value
local function comp_val(a, b)
	return a[2] < b[2]
end

-- ref filtering for citing from wikidata
local function is_rejected(item)
	-- rejected Q
	if cfg.rejected.sources[item]==true then
		return true;
	end

	-- rejected Properties
	local item_entity = mw.wikibase.getEntity(item);
	for _, v in pairs(cfg.rejected.properties) do
		if (#item_entity:getAllStatements( v )>0 ) then
			return true;
		end
	end

	-- Rejected InatanceOf
	local instance = item_entity:getAllStatements( "P31" )
	for _,v in pairs(instance) do
		if  cfg.rejected.instances[v.mainsnak.datavalue.value.id] then
			return true;
		end
	end

	--No used Properties
	local has_properties = false
	for k, _ in pairs(cfg.used_properties) do
		if (#item_entity:getAllStatements( k )>0 ) then
			has_properties = true;
			break;
		end
	end

	return not has_properties;
end

function p._cite_web(citeq_args)
	local refsnak=	citeq_args.snak
	local Q
	citeq_args.snak = nil
	-- retrieve all the parameters
	local pqid = citeq_args.pqid
	citeq_args.pqid = nil
	local wdl = false
	local qid = nil
	local multi_args = {
		P50 = "author",
		P2093 = "author",
		P5769 = "editor",
		P98 = "editor",
		P655 = "translator",
	}

	Q = WdItem.newSnakItem(refsnak, {fwd = "ALL", osd = false, noicon = true})
	-- find language
	local langcodes = {}
	if refsnak["P1476"] then
		langcodes = p._getLangOfSnakProp(refsnak, "P1476")
		citeq_args.language =  citeq_args.language or langcodes[1]
	end
	langcodes[#langcodes+1] = 'ar'
	langcodes[#langcodes+1] = 'en'
	Q:setOptions({ langs = langcodes })
	local labellang = langcodes[1]
	local rtl = rtl_lang[langcodes[1]] or false

	local oth = {}
	for i in pairs(refsnak) do
		local label = ""
		-- multiple authors may be given
		if refsnak[i][1].snaktype ~= 'novalue' then
			if multi_args[i] then
				get_name_list (multi_args[i], citeq_args, qid, wdl, labellang)
			elseif cfg.used_properties[i] and type(cfg.used_properties[i]) == "table" then
				local data = cfg.used_properties[i]
				local langidx = 1
				while citeq_args[data.arg] == nil and langidx<=#langcodes do
					citeq_args[data.arg] = Q:getValue( i, { maxvals = data.maxvals, linked = data.linked, rank = data.rank or "best", lang=langcodes[langidx] } )
					if citeq_args[data.arg] == nil then
						break
					elseif  citeq_args[data.arg]:find('[[تصنيف:Articles with missing Wikidata information]]', 1, true) then
						citeq_args[data.arg] = nil
						langidx = langidx + 1
					else
						break
					end
				end

				if not citeq_args[data.arg] then
					citeq_args[data.arg] = Q:getValue( i, { maxvals = data.maxvals, linked = data.linked} )
					if citeq_args[data.arg]:find('^Q%d+$')  then -- qid was returned
						-- try fallback to qid's native language
						local qid_languages = _lang_code(WdItem.newItem( citeq_args[data.arg], { fwd = "ALL", osd = false, noicon = true } ))
						citeq_args[data.arg] = Q:getValue( i, { maxvals = data.maxvals, linked = data.linked, lang = qid_languages[1]} )
						if citeq_args[data.arg]:find('^Q%d+$') then -- qid was returned again
							citeq_args[data.arg] = nil
						else
							-- record the language found if no lang specified
							citeq_args.language = citeq_args.language or qid_languages[1]
						end
					end
				end
				--- translate unknown value
				if citeq_args[data.arg] == "Unknown"  then
					if rtl then
						citeq_args[data.arg] = cfg.i18n.ar["unknown-" .. data.arg] or cfg.i18n.ar["unknown"]
					else
						citeq_args[data.arg] = mw.wikibase.getLabelByLang("Q4233718", lang):gsub("^%l", mw.ustring.upper)
					end
				end
				-----------------

				if data.others then
					oth[#oth + 1] = citeq_args[data.arg] and ((cfg.i18n.ar[data.arg] or data.arg:gsub("^%l", string.upper)) .. ": " .. citeq_args[data.arg])
					citeq_args[data.arg] = nil
				end
			elseif cfg.used_properties[i] then
				if i == "P648" then
					citeq_args.ol = (Q:getValue( "P648", { maxvals = 1 } ) or ''):gsub("^OL(.+)$", "%1")
					if citeq_args.ol == "" then
						citeq_args.ol = nil
					end
				elseif i == "P957" then
					citeq_args.isbn = Q:getValue("P957", { maxvals = 1 } )
				elseif in_array(i,{"P854", "P953", "P856", "P2699"})  then
					local url;
					for _, pr in ipairs( {"P854", "P953", "P856", "P2699"} ) do
						url = Q:getValue( pr, { maxvals = 1 } )
						if url then
							citeq_args.url = mw.text.split( url, " (", true )[1]
							local arcurl = mw.ustring.match( url, " %((.*)%)" )				-- when there is an archive url, <url> holds: url<space>(archive url); here extract the archive url if present
							if arcurl then
								local arcy, arcm, arcd = arcurl:match("(20%d%d)%p?(%d%d)%p?(%d%d)")
								if arcy and arcm and arcd then
									citeq_args["archive-url"] = arcurl
									citeq_args["archive-date"] = tonumber(arcd) .. " " .. cfg.i18n.months[tonumber(arcm)] .. " " .. arcy
								end
							end
							break
						end
					end
				end
			end
		end
	end

	if citeq_args.url then
		if refsnak["P813"] then
			citeq_args.accessdate = Q:getValue("P813", { maxvals = 1 } )
		end
		if refsnak["P1065"] and refsnak["P2960"] then
			citeq_args["archive-url"] = Q:getValue("P1065", { maxvals = 1 } )
			citeq_args["archive-date"]= Q:getValue("P2960", { maxvals = 1 } )
		end
	end


	citeq_args.others = citeq_args.others or table.concat(oth, ". ")
	if citeq_args.others == "" then
		citeq_args.others = nil
	end

	citeq_args.biorxiv = citeq_args.biorxiv and ("10.1101/" .. citeq_args.biorxiv)


	-- if url then see if there's an archive: citeq_args.url
	local url

	local title_display = citeq_args.title
	if citeq_args.url then
		citeq_args.title = wrap_nowiki(title_display)
	end

	-- TBD: incorporate |at, |sheets= and |sheet= here as well
	-- Sort out what should happen if several of them are given at the same time
	if citeq_args.page or citeq_args.p then -- let single take precedence over multiple
		citeq_args.pages = nil
		citeq_args.pp = nil
	end
	if citeq_args.pages then
		local _, count = string.gsub(citeq_args.pages, "[,;%s]%d+", "")
		if count == 1 then
			citeq_args.page = citeq_args.pages
			citeq_args.pages = nil
		end
	end

	for k, v in pairs(citeq_args) do
		if in_array (v, {'(())', 'unset', 'ignore'}) or 'string' ~= type(k) then -- empty accept-as-is-written (()) markup to indicate an empty/unused parameter value, other ((...)) markups are deliberately passed down to {{citation}}
			citeq_args[k] = nil
		elseif in_array (v, {'((unset))', '((ignore))'}) then -- strip off markup for free-text values clashing with local keywords
			citeq_args[k] = 'unset'
		end
	end

	local author_count = 0
	for k, v in pairs(citeq_args) do
		if k:find("^author%d+$") then
			author_count = author_count + 1
		end
	end
	if author_count > 8 then -- convention in astronomy journals, optional mode for this?
		if 'all' == citeq_args['display-authors'] then
			citeq_args['display-authors'] = nil;								-- unset because no longer needed
		else
			citeq_args['display-authors'] = citeq_args['display-authors'] or 3	-- limit to three displayed names
		end
	end

	-- replace no-break space by space, User:Mr. Ibrahem
	if citeq_args.title then
		citeq_args.title = mw.ustring.gsub( citeq_args.title, "\194\160", " " )
	end

	local editor_count = 0
	for k, v in pairs(citeq_args) do
		if k:find("^editor%d+$") then
			editor_count = editor_count + 1
		end
	end
	if editor_count > 8 then -- convention in astronomy journals, optional mode for this?
		if 'all' == citeq_args['display-editors'] then
			citeq_args['display-editors'] = nil;								-- unset because no longer needed
		else
			citeq_args['display-editors'] = citeq_args['display-editors'] or 3	-- limit to three displayed names
		end
	end

	-- code to make a guess what template to use from the supplied parameters
	-- (first draft for proof-of-concept)
	local template = "ويب"

	-- |id= could hold more than one identifier pulled from Wikidata not supported by {{citation}}, right now only add our qid to the list
	local list_sep = '. '
	if citeq_args.mode ~= 'cs1' then
		list_sep = ', '
	end

	-- clean up any blank parameters
	for k, v in pairs(citeq_args) do
		if v == "" then citeq_args[k] = nil end
	end
	if template then
	--		citeq_args.mode = citeq_args.mode or "cs1"								-- a cs1 template already knows that it is cs1 so this line is superfluous
		template = "استشهاد " .. template
	else
	--		citeq_args.mode = citeq_args.mode or "cs2"								-- a cs2 template already knows that it is cs2 so this line is superfluous
		template = "استشهاد"
	end
	citeq_args['no-tracking'] = 'true' -- لإيقاف ظهور تصانيف التتبع

	return mw.getCurrentFrame():expandTemplate{title = template, args = citeq_args}  -- render the template
end
--[[-------------------------< C I T E _ Q >------------------------------------------------------------------
Takes standard CS1|2 template parameters and passes all to {{citation}}.  If neither of |author= and |author1=
are set, calls get_authors() to try to get an author name-list from Wikidata.  The result is passed to
{{citation}} for rendering.
--]]
function p._cite_q (in_citeq_args)
	local frame = mw.getCurrentFrame()
	local citeq_args = {}
	local origin_args = {}
	local Q
	-- parameters that don't get passed to Citation
	local expand = in_citeq_args.expand -- when set to anything, causes {{cite q}} to render <code><nowiki>{{citation|...}}</nowiki></code>
	local qid = in_citeq_args.qid or in_citeq_args[1]
	local wdl = in_citeq_args.wdl
	local template = in_citeq_args.template
	local cite_class = 'citation'
	local rtl = true
	local snak = in_citeq_args.snak or {}
    local pqid = in_citeq_args.pqid
	in_citeq_args.expand = nil
	in_citeq_args[1] = nil
	in_citeq_args.qid = nil
	in_citeq_args.wdl = nil
	in_citeq_args.template = nil
	in_citeq_args.snak = nil
	in_citeq_args.pqid = nil

	if in_citeq_args.wikidatacite then
		citeq_args = in_citeq_args
	else
		local A = argument_wrapper(in_citeq_args)
		local loaded_args ={}

		for _,args_table in pairs({cfg.simple_properties,cfg.used_args}) do

			for k,_ in pairs(args_table) do
				if	A[k] then
					citeq_args[k] = A[k]

					if k ~= A:ORIGIN(k) then
						origin_args[k] = A:ORIGIN(k)
						table.insert(loaded_args, A:ORIGIN(k))
					else
						table.insert(loaded_args, k)
					end
				end
			end
		end
		for k,v in pairs(in_citeq_args) do
			if	not in_array(k,loaded_args) then
				citeq_args[k] = v
			end
		end
	end
	-- if title supplied, flag to not read html title
	local vol = citeq_args.volume
    local chapter = citeq_args.chapter
    local titleforced = (citeq_args.title ~= nil)
	local default_opt = {rank = "best", fetchwikidata = "ALL", onlysourced = false, noicon = true, vol=vol, chapter=chapter}
	if #snak > 0 then
		Q = WdItem.newSnakItem(snak,default_opt)
	else
		Q = WdItem.newItem(qid, default_opt)
	end
	local oth = {}

	-- put the language codes into a sequential table langcodes[]
	local langcodes = {}
	if citeq_args.language then
		-- check these are a supported language codes
		for lc in mw.text.gsplit( citeq_args.language, "[,، ]+", false ) do --modified
			langcodes[#langcodes+1] = mw.language.isSupportedLanguage(lc) and lc
		end
	end

	if not langcodes[1] then
		-- try to find language of work
		langcodes = _lang_code(Q)
	end

	if not langcodes[1] then
		-- try fallback to journal's language
		local journal_qid = Q:followQid("P1433")
		langcodes = journal_qid and _lang_code(journal_qid)
	end
	citeq_args.language = citeq_args.language or table.concat(langcodes, ", ")
	Q:setOptions( { langs = langcodes } );
	local labellang = langcodes[1] or 'ar'

	rtl = rtl_lang[langcodes[1]] or false
	-- loop through list of simple properties and get their values in citeq_args
	for name, data in pairs(cfg.simple_properties) do
		citeq_args[name] = citeq_args[name] or Q:getValue(data.id,  { maxvals = data.maxvals, linked = data.linked, qual = data.qual, no_vol_qual = data.no_vol_qual } )
		if not citeq_args[name] and data.populate_from_journal then
			local publishedin = Q:getValue("P1433", {maxvals = 0, lang = labellang, qual = data.id, qualsonly = true} )
            citeq_args[name] = publishedin or Q:getPropOfProp("P1433", data.id, {lang = labellang, maxvals = data.maxvals})
		end

		if citeq_args[name] and citeq_args[name]:find('[[تصنيف:Articles with missing Wikidata information]]', 1, true) then
			-- try fallback to work's native language
			citeq_args[name] = Q:getValue( data.id, { maxvals = data.maxvals, linked = data.linked, no_vol_qual = data.no_vol_qual} )
			if citeq_args[name]:find('^Q%d+$') then -- qid was returned
				-- try fallback to qid's native language
				local qid_languages = _lang_code(citeq_args[name])
				citeq_args[name] = Q:getValue( data.id, {maxvals = data.maxvals, linked = data.linked, lang = qid_languages[1], no_vol_qual = data.no_vol_qual} )
				if citeq_args[name]:find('^Q%d+$') then -- qid was returned again
					citeq_args[name] = nil
				else
					-- record the language found if no lang specified
					citeq_args.language = citeq_args.language or qid_languages[1]
				end
			end
		end
		if citeq_args[name] and snak[data.id] then
			snak[data.id] = nil
		end

		--- translate unknown value
		if citeq_args[name] == "Unknown" and rtl then
			citeq_args[name] = cfg.i18n.ar["unknown-" .. name] or cfg.i18n.ar["unknown"]
		end
		-----------------

		if data.others then
			oth[#oth + 1] = citeq_args[name] and ((rtl and cfg.i18n.ar[name] or name:gsub("^%l", string.upper)) .. ": " .. citeq_args[name])
			citeq_args[name] = nil
		end
	end

	citeq_args.others = citeq_args.others or table.concat(oth, ". ")
	if citeq_args.others == "" then
		citeq_args.others = nil
	end

	citeq_args.journal = citeq_args.journal and citeq_args.journal:gsub("^''", ""):gsub("''$", ""):gsub("|''", "|"):gsub("'']]", "]]")

	citeq_args.ol = citeq_args.ol or (Q:getValue("P648", { maxvals = 1 } ) or ''):gsub("^OL(.+)$", "%1")
	if citeq_args.ol == "" then
		citeq_args.ol = nil
	end
	-- TBD. Take care of |ol-access=?

	citeq_args.biorxiv = citeq_args.biorxiv and ("10.1101/" .. citeq_args.biorxiv)

	citeq_args.isbn = citeq_args.isbn or Q:getValue("P957", { maxvals = 1, rank="best" } ) -- try ISBN 10 (only one value accepted)

	-- if url then see if there's an archive: citeq_args.url
	local url
	--find formatted url
	if snak ~= {} then
		for k, v in pairs(snak) do
			if (v[1].datatype == "external-id") then
				local furl = mw.wikibase.getBestStatements( k, "P1630" )
				if 0 ~= #furl  then
					furl = furl[1]["mainsnak"]["datavalue"]["value"]
					citeq_args.url = mw.message.newRawMessage(furl, {v[1]["datavalue"]["value"]}):plain()
					snak[k] = nil
					break;
				end
			end
		end

		if citeq_args.url == nil and pqid and qid then
			local refproperty = mw.wikibase.getBestStatements(qid , "P1687" )

			if 0 ~= #refproperty then
				-- خطأ لوا في وحدة:Cite_Q على السطر 1124: attempt to index field 'datavalue' (a nil value).
				if refproperty[1]["mainsnak"] and refproperty[1]["mainsnak"]["datavalue"] and refproperty[1]["mainsnak"]["datavalue"]["value"] then
					refproperty=refproperty[1]["mainsnak"]["datavalue"]["value"]["id"]
					local furl = mw.wikibase.getBestStatements( refproperty, "P1630" )
					if 0 ~= #furl then
						furl = furl[1]["mainsnak"]["datavalue"]["value"]
						local refval = mw.wikibase.getBestStatements( pqid, refproperty )
						refval = (0 ~= #refval) and refval[1]["mainsnak"]["datavalue"]["value"]
						if refval then
							citeq_args.url = mw.message.newRawMessage(furl, {refval}):plain()
						end
					end
				end
			end
		end
	end


	if not citeq_args.url then
		for i, pr in ipairs( {"P953", "P856", "P2699"} ) do
			url = Q:getValue(pr, { maxvals = 1,  qual="P1065" } )
			if url then
				citeq_args.url = mw.text.split( url, " (", true )[1]
				local arcurl = mw.ustring.match( url, " %((.*)%)" )				-- when there is an archive url, <url> holds: url<space>(archive url); here extract the archive url if present
				if arcurl then
					local arcy, arcm, arcd = arcurl:match("(20%d%d)%p?(%d%d)%p?(%d%d)")
					if arcy and arcm and arcd then
						citeq_args["archive-url"] = arcurl
						citeq_args["archive-date"] = tonumber(arcd) .. " " .. cfg.i18n.months[tonumber(arcm)] .. " " .. arcy
					end
				end
				break
			end
		end
	end

	if citeq_args.publisher == "Unknown" then -- look for "stated as" (P1932)
		local stated_as = Q:getValue( "P123", { maxvals = 1, qual="P1932", qualsonly=true} )
		if stated_as then citeq_args.publisher = stated_as end
	end

	if not titleforced then
		-- Handle subtitle.
		mw.log('in subtitle')
		if citeq_args.title then
			local subtitle = Q:getValue('P1680');
			mw.log('subtitle: ' .. tostring(subtitle))
			if subtitle  then
				citeq_args.title = citeq_args.title .. ": " .. subtitle
			end
		end

		
		local htmltitle = Q:getValue("P1476",  {qual = "P6833", maxvals = 1, qualsonly = true} )
		mw.log('htmltitle: ' .. tostring(htmltitle))
		if htmltitle then
			citeq_args.title = htmltitle:gsub("</?i>", "''")
		else
			local title_display = citeq_args.title
                or (langcodes[1] and mw.wikibase.getLabelByLang(qid, langcodes[1]))
                or mw.wikibase.getLabel(qid)

			if title_display and snak and snak.P1810 then
				title_display=title_display .. " | " .. snak.P1810[1].datavalue.value
				snak.P1810 = nil
			end

			title_display = title_display or ("No label or title -- debug: " .. qid)

			if citeq_args.url then
				citeq_args.title = wrap_nowiki(title_display)
			else
				local slink = mw.wikibase.getSitelink(qid)
				local slink_flag = false
				local wrap_title = ''
				local wslink = false
				if not slink then
					-- See if we have wikisource
					if not citeq_args.url then
						local wikisource_sitelink = mw.wikibase.getSitelink(qid, "arwikisource") or nil
						if wikisource_sitelink then
							slink = ':s:'..wikisource_sitelink
							wslink = true
						end
					end
				end
				if citeq_args.title then
					if slink then
						wrap_title = wrap_nowiki(citeq_args.title)
						slink_flag = true
					else
						citeq_args.title = wrap_nowiki(citeq_args.title)
					end
				else
					if slink and not wslink then
						if slink:lower() == title_display:lower() then
							citeq_args.title = '[[' .. slink .. ']]'
						else
							wrap_title = wrap_nowiki(slink:gsub("%s%(.+%)$", ""):gsub(",.+$", ""))
							slink_flag = true
						end
					elseif wslink then
						wrap_title = wrap_nowiki(title_display)
						slink_flag = true
					else
						citeq_args.title = wrap_nowiki(title_display)
					end
				end
				if slink_flag then
					if slink == wrap_title and not wslink then -- direct link
						citeq_args.title = '[[' .. slink .. ']]'
					else -- piped link
						citeq_args.title = '[[' .. slink .. '|' .. wrap_title .. ']]'
					end
				end
			end
		end
	end

	-- replace no-break space by space, User:Mr. Ibrahem 
	if citeq_args.title then
		citeq_args.title = mw.ustring.gsub( citeq_args.title, "\194\160", " " )
	end

	-- fetch other snak properties for debugging
	-- if snak and snak ~= {} then
	-- 	for k,v in pairs(snak) do
	-- 		if not used_properties[k] then
	-- 		end
	-- 	end
	-- end


	-- TBD: incorporate |at, |sheets= and |sheet= here as well
	-- Sort out what should happen if several of them are given at the same time

	if citeq_args.page then -- let single take precedence over multiple
		citeq_args.pages = nil
		citeq_args.pp = nil
	end
	if citeq_args.pages then
		local _, count = string.gsub(citeq_args.pages, "[,;%s]%d+", "")
		if count == 1 then
			citeq_args.page = citeq_args.pages
			citeq_args.pages = nil
		end
	end

	if is_set (qid) then
		if not is_set (citeq_args.author) and not is_set (citeq_args.author1)
			and not is_set (citeq_args.subject) and not is_set (citeq_args.subject1)
			and not is_set (citeq_args.host) and not is_set (citeq_args.host1)
			and not is_set (citeq_args.last) and not is_set (citeq_args.last1)
			and not is_set (citeq_args.surname) and not is_set (citeq_args.surname1)
			and not is_set (citeq_args['author-last']) and not is_set (citeq_args['author-last1']) and not is_set (citeq_args['author1-last'])
			and not is_set (citeq_args['author-surname']) and not is_set (citeq_args['author-surname1']) and not is_set (citeq_args['author1-surname1']) then	-- if neither are set, try to get authors from Wikidata
			get_name_list ('author', citeq_args, qid, wdl, labellang, vol, chapter)				-- modify citeq_args table with authors from Wikidata
		end

		if not is_set (citeq_args.editor) and not is_set (citeq_args.editor1)
			and not is_set (citeq_args['editor-last']) and not is_set (citeq_args['editor-last1']) and not is_set (citeq_args['editor1-last'])
			and not is_set (citeq_args['editor-surname']) and not is_set (citeq_args['editor-surname1']) and not is_set (citeq_args['editor1-surname']) then	-- if neither are set, try to get editors from Wikidata
			get_name_list ('editor', citeq_args, qid, wdl, labellang, vol, chapter)				-- modify citeq_args table with editors from Wikidata
		end

		if not is_set (citeq_args.translator) and not is_set (citeq_args.translator1)
			and not is_set (citeq_args['translator-last']) and not is_set (citeq_args['translator-last1']) and not is_set (citeq_args['translator1-last'])
			and not is_set (citeq_args['translator-surname']) and not is_set (citeq_args['translator-surname1']) and not is_set (citeq_args['translator1-surname']) then	-- if neither are set, try to get translators from Wikidata
			get_name_list ('translator', citeq_args, qid, wdl, labellang, vol, chapter)			-- modify citeq_args table with translators from Wikidata
		end
	end

	for k, v in pairs(citeq_args) do
		if in_array (v, {'(())', 'unset', 'ignore'}) or 'string' ~= type(k) then -- empty accept-as-is-written (()) markup to indicate an empty/unused parameter value, other ((...)) markups are deliberately passed down to {{citation}}
			citeq_args[k] = nil
		elseif in_array (v, {'((unset))', '((ignore))'}) then -- strip off markup for free-text values clashing with local keywords
			citeq_args[k] = 'unset'
		end
	end

	local author_count = 0
	for k, v in pairs(citeq_args) do
		if k:find("^author%d+$") then
			author_count = author_count + 1
		end
	end
	if author_count > 8 then -- convention in astronomy journals, optional mode for this?
		if 'all' == citeq_args['display-authors'] then
			citeq_args['display-authors'] = nil;								-- unset because no longer needed
		else
			citeq_args['display-authors'] = citeq_args['display-authors'] or 3	-- limit to three displayed names
		end
	end

	local editor_count = 0
	for k, v in pairs(citeq_args) do
		if k:find("^editor%d+$") then
			editor_count = editor_count + 1
		end
	end
	if editor_count > 8 then -- convention in astronomy journals, optional mode for this?
		if 'all' == citeq_args['display-editors'] then
			citeq_args['display-editors'] = nil;								-- unset because no longer needed
		else
			citeq_args['display-editors'] = citeq_args['display-editors'] or 3	-- limit to three displayed names
		end
	end

	-- change edition to ordinal if it's set and numeric
	if (rtl == false) then citeq_args.edition = citeq_args.edition and p.makeOrdinal(citeq_args.edition) end

	-- code to make a guess what template to use from the supplied parameters
	-- (first draft for proof-of-concept)
	if citeq_args.journal then
		template = template or "بدورية محكمة"
	elseif citeq_args.isbn then
		template = template or "بكتاب"
		citeq_args.asin = nil -- suppress ASIN if ISBN exists
	elseif citeq_args.website then
		template = template or "ويب"
	end

	-- template is CS1 designator: journal, web, news, etc.
	if template then
--		citeq_args.mode = citeq_args.mode or "cs1"								-- a cs1 template already knows that it is cs1 so this line is superfluous
		local citeclasses = {["بدورية محكمة"] = 'journal', ["بكتاب"] = "book", ["ويب"] = "web" }
		cite_class = citeclasses[template]
		template = "استشهاد " .. template
	else
--		citeq_args.mode = citeq_args.mode or "cs2"								-- a cs2 template already knows that it is cs2 so this line is superfluous
		template = "استشهاد"
	end

	-- |id= could hold more than one identifier pulled from Wikidata not supported by {{citation}}, right now only add our qid to the list
	local list_sep = '. '
	if citeq_args.mode ~= 'cs1' then
		list_sep = ', '
	end
	local id = 'QID:[[:d:' .. qid .. '|' .. qid .. ']]' -- go through "WDQ (identifier)" redirect to reduce clutter in "What links here" and improve reverse lookup. Keep in sync with {{QID}}.
	local old_id = citeq_args.id
	if wdl then -- show WD logo
		id = id .. '[[ملف:Wikidata-logo.svg|16px|alt=|link=]]' -- possibly replace by WD edit icon?
	end
	if is_set (old_id) then
		citeq_args.id = old_id .. list_sep .. id -- append to user-specified contents
	else
		citeq_args.id = id
	end

	-- clean up any blank parameters
	for k, v in pairs(citeq_args) do
		if v == "" then citeq_args[k] = nil end
	end
	-- use real param names for CS1 messages
	for k,v in pairs(origin_args) do
		if citeq_args[k] and citeq_args[k] == in_citeq_args[v] then
			citeq_args[v] = citeq_args[k]
			citeq_args[k] = nil
		end
	end

	-- if |expand=<anything>, write a nowiki'd version to see what the {{citation}} template call looks like
	if expand then
		local expand_args = { "{{" .. template .. "\n" }		-- init with citation template
		if expand == "self" then
			citeq_args.id = old_id -- restore original |id= parameter
			expand_args = { "{{cite Q|" .. qid } -- expand to itself
		end
		-- make a sortable table and sort it by param name
		local sorttable = {}
		for param, val in pairs (citeq_args) do
			table.insert(sorttable, {param, val})
		end
		table.sort(sorttable, comp_key)
		-- add contents to expand_args
		for idx, val in ipairs(sorttable) do
			table.insert(expand_args, val[1] .. '=' .. val[2] .. "\n")
		end
		-- make the nowiki'd string and done
		return table.concat (expand_args, '| ') .. "\n}}"
	end

	local erratumid = Q:getPropertyIDs( "P2507", { maxvals = 1 } )
	if erratumid then
		erratumid = " [[d:" .. erratumid .. "|(erratum)]]" .. "[[تصنيف:Cite Q - cites a work with an erratum]]"
	else
		erratumid = ""
	end

	local opt_cat = ''
	if Q:getValue( "P5824" ) then
		opt_cat = '[[تصنيف:Cite Q - cites a retracted work]]<!-- retracted -->'
	end
	if Q:getValue( "P1366" ) then
		opt_cat = opt_cat .. '[[تصنيف:Cite Q - cites a replaced work]]<!-- replaced -->'
	end

	if citeq_args.wikidatacite then
		return frame:expandTemplate{title = template, args = citeq_args} .. erratumid .. opt_cat -- render the template
	else
		local CS = require("Module:Citation/CS1" .. sandbox)
		return CS._citation(frame , citeq_args, {['CitationClass'] = cite_class}) .. erratumid .. opt_cat
	end
end

--[[-------------------------< C I T E _ Q >------------------------------------------------------------------
Takes standard CS1|2 template parameters and passes all to {{citation}}.  If neither of |author= and |author1=
are set, calls get_authors() to try to get an author name-list from Wikidata.  The result is passed to
{{citation}} for rendering.
--]]
function p._cite_wikidata (ref, pqid)
	-- early exit
	if not ref then
		return nil
	end
	local snak = ref.snaks
	-- مراجع غير مقبولة
	if snak.P143 or snak.P3452  then
		return nil
	end
	local hash = ref.hash
	if hash and hash ~= "" and ref_cash[hash] then
		return ref_cash[hash]
	end

	local citeq_args = {}
	local frame = mw.getCurrentFrame()

	if snak.P248 then
		for _, prop in pairs(snak.P248) do
			if prop.snaktype == "value" then
				citeq_args.qid =  "Q" .. prop.datavalue.value["numeric-id"]
			end
		end

		snak.P248 = nil;
	end

	if citeq_args.qid and is_rejected(citeq_args.qid) == true then
		return nil
	end

	local reference
	citeq_args.snak = snak
	citeq_args.pqid = pqid
	citeq_args.wikidatacite = 1
	if citeq_args.qid then
		reference = p._cite_q(citeq_args)
	else
		local has_properties = false
		for k, _ in pairs(cfg.used_properties) do
			if snak[k] then
				has_properties = true;
				break;
			end
		end
		reference = (has_properties and p._cite_web(citeq_args) or nil)
	end

	if reference and reference ~= "" then
		reference = frame:extensionTag("ref", reference, {name = 'wikidata-' .. hash})
	end

	if hash and hash ~= "" then
		ref_cash[hash] = reference
	end
	return reference
end

function p.cite_q (frame)
	local args = {}

	for k, v in pairs(frame:getParent().args) do
		if v ~= "" then args[k] = v end
	end
	for k, v in pairs(frame.args) do
		if v ~= "" then args[k] = v end
	end
	args.qid = args.qid or args[1] or ""
	if args.qid == "" then return nil end
	args[1] = nil

	local citesep = (args.citesep or "")
	if citesep == "" then citesep = ", " end
	citesep = citesep:gsub('"', '') -- strip double quotes after setting default to allow |citesep="" as a blank separator
	args.citesep = nil

	local tag = args.tag or ""
	if tag == "" then tag = nil end
	args.tag = nil

	local list = args.list or ""
	if list == "" then list = nil end
	args.list = nil

	args.language = args.language or args.lang
	args.lang = nil

	local cites = {}
	for q in args.qid:gmatch("Q%d+") do
		-- make a new copy of the arguments
		local newargs = {}
		for k, v in pairs(args) do
			if k ~= "qid" then
				newargs[k] = v
			end
		end
		newargs.qid = q
		if tag == "ref" then
			cites[#cites + 1] = frame:callParserFunction{ name = "#tag:ref", args = { p._cite_q(newargs), name = q } }
			-- expand like this: args = { p._cite_q(newargs), name = 'foo', group = 'bar' }
		else
			cites[#cites + 1] = p._cite_q(newargs)
		end
	end

	if list then
		return frame:expandTemplate{ title = list, args = cites }
	else
		return table.concat(cites, citesep)
	end
end

return p