-- This module implements .  local categoryHandler = require('মডিউল:বিষয়শ্রেণী পরিচালনাকারী').main  local p = {}  -- Helper functions  local function checkNum(val, default) 	-- Checks whether a value is a number greater than or equal to zero. If so, 	-- returns it as a number. If not, returns a default value. 	val = tonumber(val) 	if val and val >= 0 then 		return val 	else 		return default 	end end  local function addSuffix(num, suffix) 	-- Turns a number into a string and adds a suffix. 	if num then 		return tostring(num) .. suffix 	else 		return nil 	end end  local function checkNumAndAddSuffix(num, default, suffix) 	-- Checks a value with checkNum and adds a suffix. 	num = checkNum(num, default) 	return addSuffix(num, suffix) end  local function makeCat(cat, sort) 	-- Makes a category link. 	if sort then 		return mw.ustring.format('%s', cat, sort) 	else 		return mw.ustring.format('বিষয়শ্রেণী:%s', cat) 	end end  -- Argument processing  local function makeInvokeFunc(funcName) 	return function (frame) 		local origArgs = require('Module:Arguments').getArgs(frame) 		local args = {} 		for k, v in pairs(origArgs) do 			args[k] = v 		end 		return p.main(funcName, args) 	end end  p.userbox = makeInvokeFunc('_userbox') p['userbox-2'] = makeInvokeFunc('_userbox-2') p['userbox-r'] = makeInvokeFunc('_userbox-r')  -- Main functions  function p.main(funcName, args) 	local userboxData = p[funcName](args) 	local userbox = p.render(userboxData) 	local cats = p.categories(args) 	return userbox .. (cats or ) end  function p._userbox(args) 	-- Does argument processing for . 	local data = {}  	-- Get div tag values. 	data.float = args.float or 'left' 	local borderWidthNum = checkNum(args['border-width'] or args['border-s'], 1) -- Used to calculate width. 	data.borderWidth = addSuffix(borderWidthNum, 'px') 	data.borderColor = args['border-color'] or args['border-c'] or args[1] or args['id-c'] or '#999' 	data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag. 	data.bodyClass = args.bodyclass  	-- Get table tag values. 	data.backgroundColor = args['info-background'] or args[2] or args['info-c'] or '#eee'  	-- Get info values. 	data.info = args.info or args[4] or "" 	data.infoTextAlign = args['info-a'] or 'left' 	data.infoFontSize = checkNumAndAddSuffix(args['info-size'] or args['info-s'], 8, 'pt') 	data.infoHeight = checkNumAndAddSuffix(args['logo-height'] or args['id-h'], 45, 'px') 	data.infoPadding = args['info-padding'] or args['info-p'] or '0 4px 0 4px' 	data.infoLineHeight = args['info-line-height'] or args['info-lh'] or '1.25em' 	data.infoColor = args['info-color'] or args['info-fc'] or 'black' 	data.infoOtherParams = args['info-other-param'] or args['info-op'] 	data.infoClass = args['info-class']  	-- Get id values. 	local id = args.logo or args[3] or args.id 	data.id = id 	data.showId = id and true or false 	data.idWidth = checkNumAndAddSuffix(args['logo-width'] or args['id-w'], 45, 'px') 	data.idHeight = checkNumAndAddSuffix(args['logo-height'] or args['id-h'], 45, 'px') 	data.idBackgroundColor = args['logo-background'] or args[1] or args['id-c'] or '#ddd' 	data.idTextAlign = args['id-a'] or 'center' 	data.idFontSize = checkNumAndAddSuffix(args['logo-size'] or args[5] or args['id-s'], 14, 'pt') 	data.idColor = args['logo-color'] or args['id-fc'] or data.infoColor 	data.idPadding = args['logo-padding'] or args['id-p'] or '0 1px 0 0' 	data.idLineHeight = args['logo-line-height'] or args['id-lh'] or '1.25em' 	data.idOtherParams = args['logo-other-param'] or args['id-op'] 	data.idClass = args['id-class']  	return data end  p['_userbox-2'] = function (args) 	-- Does argument processing for . 	local data = {}  	-- Get div tag values. 	data.float = args.float or 'left' 	local borderWidthNum = checkNum(args['border-s'] or args[9], 1) -- Used to calculate width. 	data.borderWidth = addSuffix(borderWidthNum, 'px') 	data.borderColor = args['border-c'] or args[6] or args['id1-c'] or args[1] or '#999999' 	data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag. 	data.bodyClass = args.bodyclass  	-- Get table tag values. 	data.backgroundColor = args['info-c'] or args[2] or '#eeeeee'  	-- Get info values. 	data.info = args.info or args[4] or "" 	data.infoTextAlign = args['info-a'] or 'left' 	data.infoFontSize = checkNumAndAddSuffix(args['info-s'], 8, 'pt') 	data.infoColor = args['info-fc'] or args[8] or 'black' 	data.infoPadding = args['info-p'] or '0 4px 0 4px' 	data.infoLineHeight = args['info-lh'] or '1.25em' 	data.infoOtherParams = args['info-op']  	-- Get id values. 	data.showId = true 	data.id = args.logo or args[3] or args.id1 or 'id1' 	data.idWidth = checkNumAndAddSuffix(args['id1-w'], 45, 'px') 	data.idHeight = checkNumAndAddSuffix(args['id-h'], 45, 'px') 	data.idBackgroundColor = args['id1-c'] or args[1] or '#dddddd' 	data.idTextAlign = 'center' 	data.idFontSize = checkNumAndAddSuffix(args['id1-s'], 14, 'pt') 	data.idLineHeight = args['id1-lh'] or '1.25em' 	data.idColor = args['id1-fc'] or data.infoColor 	data.idPadding = args['id1-p'] or '0 1px 0 0' 	data.idOtherParams = args['id1-op']  	-- Get id2 values. 	data.showId2 = true 	data.id2 = args.logo or args[5] or args.id2 or 'id2' 	data.id2Width = checkNumAndAddSuffix(args['id2-w'], 45, 'px') 	data.id2Height = data.idHeight 	data.id2BackgroundColor = args['id2-c'] or args[7] or args[1] or '#dddddd' 	data.id2TextAlign = 'center' 	data.id2FontSize = checkNumAndAddSuffix(args['id2-s'], 14, 'pt') 	data.id2LineHeight = args['id2-lh'] or '1.25em' 	data.id2Color = args['id2-fc'] or data.infoColor 	data.id2Padding = args['id2-p'] or '0 0 0 1px' 	data.id2OtherParams = args['id2-op']  	return data end  p['_userbox-r'] = function (args) 	-- Does argument processing for . 	local data = {}  	-- Get div tag values. 	data.float = args.float or 'left' 	local borderWidthNum = checkNum(args['border-width'] or args['border-s'], 1) -- Used to calculate width. 	data.borderWidth = addSuffix(borderWidthNum, 'px') 	data.borderColor = args['border-color'] or args['border-c'] or args[1] or args['id-c'] or '#999' 	data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag. 	data.bodyClass = args.bodyclass 	 	-- Get table tag values. 	data.backgroundColor = args['info-background'] or args[2] or args['info-c'] or '#eee'  	-- Get id values. 	data.showId = false -- We only show id2 in userbox-r.  	-- Get info values. 	data.info = args.info or args[4] or "" 	data.infoTextAlign = args['info-align'] or args['info-a'] or 'left' 	data.infoFontSize = checkNumAndAddSuffix(args['info-size'] or args['info-s'], 8, 'pt') 	data.infoPadding = args['info-padding'] or args['info-p'] or '0 4px 0 4px' 	data.infoLineHeight = args['info-line-height'] or args['info-lh'] or '1.25em' 	data.infoColor = args['info-color'] or args['info-fc'] or 'black' 	data.infoOtherParams = args['info-other-param'] or args['info-op'] 	 	-- Get id2 values. 	data.showId2 = true 	data.id2 = args.logo or args[3] or args.id or 'id' 	data.id2Width = checkNumAndAddSuffix(args['logo-width'] or args['id-w'], 45, 'px') 	data.id2Height = checkNumAndAddSuffix(args['logo-height'] or args['id-h'], 45, 'px') 	data.id2BackgroundColor = args['logo-background'] or args[1] or args['id-c'] or '#ddd' 	data.id2TextAlign = args['id-a'] or 'center' 	data.id2FontSize = checkNumAndAddSuffix(args['logo-size'] or args[5] or args['id-s'], 14, 'pt') 	data.id2Color = args['logo-color'] or args['id-fc'] or data.infoColor 	data.id2Padding = args['logo-padding'] or args['id-p'] or '0 0 0 1px' 	data.id2LineHeight = args['logo-line-height'] or args['id-lh'] or '1.25em' 	data.id2OtherParams = args['logo-other-param'] or args['id-op']  	return data end  function p.render(data) 	-- Renders the userbox html using the content of the data table.  	-- Render the div tag html. 	local root = mw.html.create('div') 	root 		:css('float', data.float) 		:css('border', (data.borderWidth or ) .. ' solid ' .. (data.borderColor or )) 		:css('margin', '1px') 		:css('width', data.width) 		:addClass('wikipediauserbox') 		:addClass(data.bodyClass)  	-- Render the table tag html. 	local tableroot = root:tag('table') 	tableroot 		:attr('role', 'presentation') 		:css('border-collapse', 'collapse') 		:css('width', data.width) 		:css('margin-bottom', '0') 		:css('margin-top', '0') 		:css('background', data.backgroundColor) 	 	-- Render the id html. 	local tablerow = tableroot:tag('tr') 	if data.showId then 		tablerow:tag('td') 			:css('border', '0') 			:css('width', data.idWidth) 			:css('height', data.idHeight) 			:css('background', data.idBackgroundColor) 			:css('text-align', data.idTextAlign) 			:css('font-size', data.idFontSize) 			:css('font-weight', 'bold') 			:css('color', data.idColor) 			:css('padding', data.idPadding) 			:css('line-height', data.idLineHeight) 			:css('vertical-align', 'middle') 			:cssText(data.idOtherParams) 			:addClass(data.idClass) 			:wikitext(data.id) 	end  	-- Render the info html. 	tablerow:tag('td') 		:css('border', '0') 		:css('text-align', data.infoTextAlign) 		:css('font-size', data.infoFontSize) 		:css('padding', data.infoPadding) 		:css('height', data.infoHeight) 		:css('line-height', data.infoLineHeight) 		:css('color', data.infoColor) 		:css('vertical-align', 'middle') 		:cssText(data.infoOtherParams) 		:addClass(data.infoClass) 		:wikitext(data.info) 	 	-- Render the second id html. 	if data.showId2 then 		tablerow:tag('td') 			:css('border', '0') 			:css('width', data.id2Width) 			:css('height', data.id2Height) 			:css('background', data.id2BackgroundColor) 			:css('text-align', data.id2TextAlign) 			:css('font-size', data.id2FontSize) 			:css('font-weight', 'bold') 			:css('color', data.id2Color) 			:css('padding', data.id2Padding) 			:css('line-height', data.id2LineHeight) 			:css('vertical-align', 'middle') 			:cssText(data.id2OtherParams) 			:wikitext(data.id2) 	end  	local title = mw.title.getCurrentTitle() 	if (title.namespace == 2) and not title.text:match("/") then 		return tostring(root) -- regular user page 	elseif title.namespace == 14 then 		return tostring(root) -- category 	elseif title.isTalkPage then 		return tostring(root) -- talk page 	end  	local legible = true 	local contrast = require('Module:Color contrast')._ratio  	local function has_text(wikitext) 		local function get_alt(text) 			return text:match("|alt=([^|]*)") or "" 		end 	 		wikitext = wikitext:gsub("]]", "|]]") 		wikitext = wikitext:gsub("%[%[%s*[Mm][Ee][Dd][Ii][Aa]%s*:[^|]-(|.-)]]", get_alt) 		wikitext = wikitext:gsub("%[%[%s*[Ii][Mm][Aa][Gg][Ee]%s*:[^|]-(|.-)]]", get_alt) 		wikitext = wikitext:gsub("%[%[%s*চিত্র%s*:[^|]-(|.-)]]", get_alt) 		wikitext = wikitext:gsub("%[%[%s*[Ff][Ii][Ll][Ee]%s*:[^|]-(|.-)]]", get_alt) 		return mw.text.trim(wikitext) ~= "" 	end  	if contrast { data.infoColor, data.backgroundColor, error = 0 } < 4.5 then 		legible = false 	end  	if data.showId and contrast { data.idColor, data.idBackgroundColor, error = 0 } < 4.5 then 		if has_text(data.id or "") then 			legible = false 		end 	end  	if data.showId2 and contrast { data.id2Color, data.id2BackgroundColor, error = 0 } < 4.5 then 		if has_text(data.id2 or "") then 			legible = false 		end 	end  	if not legible then 		root:wikitext('বিষয়শ্রেণী:অপর্যাপ্ত রঙ বৈপরীত্যসহ ব্যবহারকারী বাক্স') 	end  	return tostring(root) end  function p.categories(args, page) 	-- Gets categories from Module:Category handler. 	-- The page parameter makes the function act as though the module was being called from that page. 	-- It is included for testing purposes. 	local cats = {} 	cats[#cats + 1] = args.usercategory 	cats[#cats + 1] = args.usercategory2 	cats[#cats + 1] = args.usercategory3 	cats[#cats + 1] = args.usercategory4 	cats[#cats + 1] = args.usercategory5 	-- Get the title object 	local title 	if page then 		title = mw.title.new(page) 	else 		title = mw.title.getCurrentTitle() 	end 	-- Build category handler arguments. 	local chargs = {} 	chargs.page = page 	chargs.nocat = args.nocat 	chargs.main = 'Category:Pages with templates in the wrong namespace' 	if args.notcatsubpages then 		chargs.subpage = 'no' 	end 	-- User namespace. 	local user =  	for i, cat in ipairs(cats) do 		user = user .. makeCat(cat) 	end 	chargs.user = user 	-- Template namespace. 	local basepage = title.baseText 	local template = '' 	for i, cat in ipairs(cats) do 		template = template .. makeCat(cat, ' ' .. basepage) 	end 	chargs.template = template 	return categoryHandler(chargs) end  return p