模块:Category handler:修订间差异

    来自中国珂学院
    Cquote>Quest for Truth
    无编辑摘要
    Cquote>Koala0090
    无编辑摘要
    第1行: 第1行:
    ----------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------
    --                                                                                                     --
    --                                                                           --
    --                                         CATEGORY HANDLER                                           --
    --                             CATEGORY HANDLER                             --
    --                                                                                                     --
    --                                                                           --
    --      This module implements the {{category handler}} template in Lua, with a few improvements: all   --
    --      This module implements the {{category handler}} template in Lua,     --
    --      namespaces and all namespace aliases are supported, and namespace names are detected           --
    --      with a few improvements: all namespaces and all namespace aliases    --
    --      automatically for the local wiki. This module requires [[Module:Namespace detect]] and          --
    --      are supported, and namespace names are detected automatically for    --
    --      [[Module:Yesno]] to be available on the local wiki. It can be configured for different wikis    --
    --      the local wiki. This module requires [[Module:Namespace detect]]     --
    --      by altering the values in the "cfg" table.                                                      --
    --      and [[Module:Yesno]] to be available on the local wiki. It can be     --
    --                                                                                                     --
    --      configured for different wikis by altering the values in             --
    ----------------------------------------------------------------------------------------------------------
    --     [[Module:Category handler/config]], and pages can be blacklisted      --
    --     from categorisation by using [[Module:Category handler/blacklist]].  --
    --                                                                           --
    --------------------------------------------------------------------------------


    ----------------------------------------------------------------------------------------------------------
    -- Load required modules
    --                                          Configuration data                                          --
    local yesno = require('Module:Yesno')
    --                      Language-specific parameter names and values can be set here.                  --
    ----------------------------------------------------------------------------------------------------------


    local cfg = {}
    -- Lazily load things we don't always need
    local mShared, mappings


    -- The following config values set the names of parameters that suppress categorisation. They are used
    local p = {}
    -- with Module:Yesno, and work as follows:
    --
    -- cfg.nocat:
    -- Result of yesno(args[cfg.nocat])        Effect
    -- true                                    Categorisation is suppressed
    -- false                                    Categorisation is allowed, and the blacklist check is skipped
    -- nil                                      Categorisation is allowed
    --
    -- cfg.categories:
    -- Result of yesno(args[cfg.categories])    Effect
    -- true                                    Categorisation is allowed, and the blacklist check is skipped
    -- false                                    Categorisation is suppressed
    -- nil                                      Categorisation is allowed
    cfg.nocat = 'nocat'   
    cfg.categories = 'categories'


    -- The parameter name for the legacy "category2" parameter. This skips the blacklist if set to the
    --------------------------------------------------------------------------------
    -- cfg.category2Yes value, and suppresses categorisation if present but equal to anything other than
    -- Helper functions
    -- cfg.category2Yes or cfg.category2Negative.
    --------------------------------------------------------------------------------
    cfg.category2 = 'category2'
    cfg.category2Yes = 'yes'
    cfg.category2Negative = '¬'


    -- cfg.subpage is the parameter name to specify how to behave on subpages. cfg.subpageNo is the value to
    local function trimWhitespace(s, removeBlanks)
    -- specify to not categorise on subpages; cfg.only is the value to specify to only categorise on subpages.
    if type(s) ~= 'string' then
    cfg.subpage = 'subpage'
    return s
    cfg.subpageNo = 'no'
    end
    cfg.subpageOnly = 'only'
    s = s:match('^%s*(.-)%s*$')
    if removeBlanks then
    if s ~= '' then
    return s
    else
    return nil
    end
    else
    return s
    end
    end


    -- The parameter for data to return in all namespaces.
    --------------------------------------------------------------------------------
    cfg.all = 'all'
    -- CategoryHandler class
    --------------------------------------------------------------------------------


    -- The parameter name for data to return if no data is specified for the namespace that is detected. This
    local CategoryHandler = {}
    -- must be the same as the cfg.other parameter in [[Module:Namespace detect]].
    CategoryHandler.__index = CategoryHandler
    cfg.other = 'other'


    -- The parameter name used to specify a page other than the current page; used for testing and
    function CategoryHandler.new(data, args)
    -- demonstration. This must be the same as the cfg.page parameter in [[Module:Namespace detect]].
    local obj = setmetatable({ _data = data, _args = args }, CategoryHandler)
    cfg.page = 'page'
    -- Set the title object
    do
    local pagename = obj:parameter('demopage')
    local success, titleObj
    if pagename then
    success, titleObj = pcall(mw.title.new, pagename)
    end
    if success and titleObj then
    obj.title = titleObj
    if titleObj == mw.title.getCurrentTitle() then
    obj._usesCurrentTitle = true
    end
    else
    obj.title = mw.title.getCurrentTitle()
    obj._usesCurrentTitle = true
    end
    end


    -- The categorisation blacklist. Pages that match Lua patterns in this list will not be categorised.
    -- Set suppression parameter values
    -- (However, see the explanation of cfg.nocat, cfg.categories and cfg.category2 for some exceptions.)
    for _, key in ipairs{'nocat', 'categories'} do
    -- If the namespace name has a space in, it must be written with an underscore, e.g. "Wikipedia_talk".
    local value = obj:parameter(key)
    -- Other parts of the title can have either underscores or spaces.
    value = trimWhitespace(value, true)
    cfg.blacklist = {
    obj['_' .. key] = yesno(value)
        '^Wikipedia:首页$', -- don't categorise the main page.
    end
       
    do
        -- Don't categorise the following pages or their subpages.
    local subpage = obj:parameter('subpage')
        '^Wikipedia:Cascade%-protected items$',
    local category2 = obj:parameter('category2')
        '^Wikipedia:Cascade%-protected items/.*$',
    if type(subpage) == 'string' then
        '^User:UBX$', -- The userbox "template" space.
    subpage = mw.ustring.lower(subpage)
        '^User:UBX/.*$',
    end
        '^User_talk:UBX$',
    if type(category2) == 'string' then
        '^User_talk:UBX/.*$',
    subpage = mw.ustring.lower(category2)
       
    end
        -- Don't categorise subpages of these pages, but allow
    obj._subpage = trimWhitespace(subpage, true)
        -- categorisation of the base page.
    obj._category2 = trimWhitespace(category2) -- don't remove blank values
        '^Wikipedia:模板消息/.+$',
    end
       
    return obj
        -- Don't categorise archives.
    end
        '/存檔',
        '/存档',
        '/[aA]rchive'
    }


    -- This is a table of namespaces to categorise by default. They should be in the format of parameter
    function CategoryHandler:parameter(key)
    -- names accepted by [[Module:Namespace detect]].
    local parameterNames = self._data.parameters[key]
    cfg.defaultNamespaces = {
    local pntype = type(parameterNames)
        'main',
    if pntype == 'string' or pntype == 'number' then
        'file',
    return self._args[parameterNames]
        'help',
    elseif pntype == 'table' then
        'category'
    for _, name in ipairs(parameterNames) do
    }
    local value = self._args[name]
    if value ~= nil then
    return value
    end
    end
    return nil
    else
    error(string.format(
    'invalid config key "%s"',
    tostring(key)
    ), 2)
    end
    end


    ----------------------------------------------------------------------------------------------------------
    function CategoryHandler:isSuppressedByArguments()
    --                                          End configuration data                                      --
    return
    ----------------------------------------------------------------------------------------------------------
    -- See if a category suppression argument has been set.
    self._nocat == true
    or self._categories == false
    or (
    self._category2
    and self._category2 ~= self._data.category2Yes
    and self._category2 ~= self._data.category2Negative
    )


    -- Get dependent modules
    -- Check whether we are on a subpage, and see if categories are
    local nsDetect = require('Module:Namespace detect')
    -- suppressed based on our subpage status.
    local yesno = require('Module:Yesno')
    or self._subpage == self._data.subpageNo and self.title.isSubpage
    or self._subpage == self._data.subpageOnly and not self.title.isSubpage
    end
     
    function CategoryHandler:shouldSkipBlacklistCheck()
    -- Check whether the category suppression arguments indicate we
    -- should skip the blacklist check.
    return self._nocat == false
    or self._categories == true
    or self._category2 == self._data.category2Yes
    end
     
    function CategoryHandler:matchesBlacklist()
    if self._usesCurrentTitle then
    return self._data.currentTitleMatchesBlacklist
    else
    mShared = mShared or require('Module:Category handler/shared')
    return mShared.matchesBlacklist(
    self.title.prefixedText,
    mw.loadData('Module:Category handler/blacklist')
    )
    end
    end


    ----------------------------------------------------------------------------------------------------------
    function CategoryHandler:isSuppressed()
    --                                          Local functions                                            --
    -- Find if categories are suppressed by either the arguments or by
    --      The following are internal functions, which we do not want to be accessible from other modules. --
    -- matching the blacklist.
    ----------------------------------------------------------------------------------------------------------
    return self:isSuppressedByArguments()
    or not self:shouldSkipBlacklistCheck() and self:matchesBlacklist()
    end


    -- Find whether we need to return a category or not.
    function CategoryHandler:getNamespaceParameters()
    local function needsCategory(pageObject, args)
    if self._usesCurrentTitle then
        -- Don't categorise if the relevant options are set.
    return self._data.currentTitleNamespaceParameters
        if yesno(args[cfg.nocat])
    else
            or yesno(args[cfg.categories]) == false
    if not mappings then
            or (
    mShared = mShared or require('Module:Category handler/shared')
                args[cfg.category2]
    mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData
                and args[cfg.category2] ~= cfg.category2Yes
    end
                and args[cfg.category2] ~= cfg.category2Negative
    return mShared.getNamespaceParameters(
            )
    self.title,
        then
    mappings
            return false
    )
        end
    end
        -- If there is no pageObject available, then that either means that we are over
        -- the expensive function limit or that the title specified was invalid. Invalid
        -- titles will probably only be a problem during testing, so we choose the best
        -- fallback for being over the expensive function limit. The fallback behaviour
        -- of the old template was to assume the page was not a subpage, so we will do
        -- the same here.
        if args[cfg.subpage] == cfg.subpageNo and pageObject and pageObject.isSubpage then
            return false
        end
        if args[cfg.subpage] == cfg.subpageOnly
            and (not pageObject or (pageObject and not pageObject.isSubpage))
        then
            return false
        end
        return true
    end
    end


    -- Find whether we need to check the blacklist or not.
    function CategoryHandler:namespaceParametersExist()
    local function needsBlacklistCheck(args)
    -- Find whether any namespace parameters have been specified.
        if yesno(args[cfg.nocat]) == false
    -- We use the order "all" --> namespace params --> "other" as this is what
            or yesno(args[cfg.categories]) == true
    -- the old template did.
            or args[cfg.category2] == cfg.category2Yes
    if self:parameter('all') then
        then
    return true
            return false
    end
        else
    if not mappings then
            return true
    mShared = mShared or require('Module:Category handler/shared')
        end
    mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData
    end
    for ns, params in pairs(mappings) do
    for i, param in ipairs(params) do
    if self._args[param] then
    return true
    end
    end
    end
    if self:parameter('other') then
    return true
    end
    return false
    end
    end


    -- Find whether any namespace parameters have been specified.
    function CategoryHandler:getCategories()
    -- Mappings is the table of parameter mappings taken from
    local params = self:getNamespaceParameters()
    -- [[Module:Namespace detect]].
    local nsCategory
    local function nsParamsExist(mappings, args)
    for i, param in ipairs(params) do
        if args[cfg.all] or args[cfg.other] then
    local value = self._args[param]
            return true
    if value ~= nil then
        end
    nsCategory = value
        for ns, params in pairs(mappings) do
    break
            for i, param in ipairs(params) do
    end
                if args[param] then
    end
                    return true
    if nsCategory ~= nil or self:namespaceParametersExist() then
                end
    -- Namespace parameters exist - advanced usage.
            end
    if nsCategory == nil then
        end
    nsCategory = self:parameter('other')
        return false
    end
    local ret = {self:parameter('all')}
    local numParam = tonumber(nsCategory)
    if numParam and numParam >= 1 and math.floor(numParam) == numParam then
    -- nsCategory is an integer
    ret[#ret + 1] = self._args[numParam]
    else
    ret[#ret + 1] = nsCategory
    end
    if #ret < 1 then
    return nil
    else
    return table.concat(ret)
    end
    elseif self._data.defaultNamespaces[self.title.namespace] then
    -- Namespace parameters don't exist, simple usage.
    return self._args[1]
    end
    return nil
    end
    end


    ----------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------
    --                                          Global functions                                            --
    -- Exports
    --      The following functions are global, because we want them to be accessible from #invoke and      --
    --------------------------------------------------------------------------------
    --     from other Lua modules.                                                                        --
    ----------------------------------------------------------------------------------------------------------


    local p = {}
    local p = {}


    -- Find if a string matches the blacklist. Returns the match if one is found, or nil otherwise.
    function p._exportClasses()
    -- Input should be a page title with a namespace prefix, e.g. "Wikipedia talk:Articles for deletion".
    -- Used for testing purposes.
    function p.matchesBlacklist(page)
    return {
        if type(page) ~= 'string' then return end
    CategoryHandler = CategoryHandler
        for i, pattern in ipairs(cfg.blacklist) do
    }
            local match = mw.ustring.match(page, pattern)
            if match then
                return match
            end
        end
    end
    end


    -- The main structure of the module. Checks whether we need to categorise,
    function p._main(args, data)
    -- and then passes the relevant arguments to [[Module:Namespace detect]].
    data = data or mw.loadData('Module:Category handler/data')
    function p._main(args)
    local handler = CategoryHandler.new(data, args)
        -- Get the page object and argument mappings from
    if handler:isSuppressed() then
        -- [[Module:Namespace detect]], to save us from having to rewrite the
    return nil
        -- code.
    end
        local pageObject = nsDetect.getPageObject(args[cfg.page])
    return handler:getCategories()
        local mappings = nsDetect.getParamMappings()
       
        if not needsCategory(pageObject, args) then return end
       
        local ret = ''
        -- Check blacklist if necessary.
        if not needsBlacklistCheck(args) or not p.matchesBlacklist(pageObject.prefixedText) then
            if not nsParamsExist(mappings, args) then
                -- No namespace parameters exist; basic usage. Pass args[1] to
                -- [[Module:Namespace detect]] using the default namespace
                -- parameters, and return the result.
                local ndargs = {}
                for _, ndarg in ipairs(cfg.defaultNamespaces) do
                    ndargs[ndarg] = args[1]
                end
                ndargs.page = args.page
                ndargs.demospace = args.demospace
                local ndresult = nsDetect._main(ndargs)
                if ndresult then
                    ret = ret .. ndresult
                end
            else
                -- Namespace parameters exist; advanced usage.
                -- If the all parameter is specified, return it.
                local all = args.all
                if type(all) == 'string' then
                    ret = ret .. all
                end
               
                -- Get the arguments to pass to [[Module:Namespace detect]].
                local ndargs = {}
                for ns, params in pairs(mappings) do
                    for _, param in ipairs(params) do
                        ndargs[param] = args[param] or args[cfg.other] or nil
                    end
                end
                ndargs.other = args.other
                ndargs.page = args.page
                ndargs.demospace = args.demospace
               
                local data = nsDetect._main(ndargs)
               
                -- Work out what to return based on the result of the namespace detect call.
                local datanum = tonumber(data)
                if type(datanum) == 'number' then
                    -- "data" is a number, so return that positional parameter.
                    -- Remove non-positive integer values, as only positive integers
                    -- from 1-10 were used with the old template.
                    if datanum > 0 and math.floor(datanum) == datanum then
                        local dataArg = args[datanum]
                        if type(dataArg) == 'string' then
                            ret = ret .. dataArg
                        end
                    end
                else
                    -- "data" is not a number, so return it as it is.
                    if type(data) == 'string' then
                        ret = ret .. data
                    end
                end
            end
        end
        return ret
    end
    end


    function p.main(frame)
    function p.main(frame, data)
        -- If called via #invoke, use the args passed into the invoking
    data = data or mw.loadData('Module:Category handler/data')
        -- template, or the args passed to #invoke if any exist. Otherwise
    local args = require('Module:Arguments').getArgs(frame, {
        -- assume args are being passed directly in.
    wrappers = data.wrappers,
        local origArgs
    valueFunc = function (k, v)
        if frame == mw.getCurrentFrame() then
    v = trimWhitespace(v)
            origArgs = frame:getParent().args
    if type(k) == 'number' then
            for k, v in pairs(frame.args) do
    if v ~= '' then
                origArgs = frame.args
    return v
                break
    else
            end
    return nil
        else
    end
            origArgs = frame
    else
        end
    return v
     
    end
        -- Trim whitespace and remove blank arguments for the following args:
    end
        -- 1, 2, 3 etc., "nocat", "categories", "subpage", and "page".
    })
        local args = {}
    return p._main(args, data)
        for k, v in pairs(origArgs) do
            if type(v) == 'string' then
                v = mw.text.trim(v) -- Trim whitespace.
            end
            if type(k) == 'number'
                or k == cfg.nocat
                or k == cfg.categories
                or k == cfg.subpage
                or k == cfg.page
            then
                if v ~= '' then
                    args[k] = v
                end
            else
                args[k] = v
            end
        end
       
        -- Lower-case "nocat", "categories", "category2", and "subpage". These
        -- parameters are put in lower case whenever they appear in the old
        -- template, so we can just do it once here and save ourselves some work.
        local lowercase = {cfg.nocat, cfg.categories, cfg.category2, cfg.subpage}
        for _, v in ipairs(lowercase) do
            local argVal = args[v]
            if type(argVal) == 'string' then
                args[v] = mw.ustring.lower(argVal)
            end
        end
       
        return p._main(args)
    end
    end


    return p
    return p

    2015年9月2日 (三) 20:47的版本

    可在模块:Category handler/doc创建此模块的帮助文档

    --------------------------------------------------------------------------------
    --                                                                            --
    --                              CATEGORY HANDLER                              --
    --                                                                            --
    --      This module implements the {{category handler}} template in Lua,      --
    --      with a few improvements: all namespaces and all namespace aliases     --
    --      are supported, and namespace names are detected automatically for     --
    --      the local wiki. This module requires [[Module:Namespace detect]]      --
    --      and [[Module:Yesno]] to be available on the local wiki. It can be     --
    --      configured for different wikis by altering the values in              --
    --      [[Module:Category handler/config]], and pages can be blacklisted      --
    --      from categorisation by using [[Module:Category handler/blacklist]].   --
    --                                                                            --
    --------------------------------------------------------------------------------
    
    -- Load required modules
    local yesno = require('Module:Yesno')
    
    -- Lazily load things we don't always need
    local mShared, mappings
    
    local p = {}
    
    --------------------------------------------------------------------------------
    -- Helper functions
    --------------------------------------------------------------------------------
    
    local function trimWhitespace(s, removeBlanks)
    	if type(s) ~= 'string' then
    		return s
    	end
    	s = s:match('^%s*(.-)%s*$')
    	if removeBlanks then
    		if s ~= '' then
    			return s
    		else
    			return nil
    		end
    	else
    		return s
    	end
    end
    
    --------------------------------------------------------------------------------
    -- CategoryHandler class
    --------------------------------------------------------------------------------
    
    local CategoryHandler = {}
    CategoryHandler.__index = CategoryHandler
    
    function CategoryHandler.new(data, args)
    	local obj = setmetatable({ _data = data, _args = args }, CategoryHandler)
    	
    	-- Set the title object
    	do
    		local pagename = obj:parameter('demopage')
    		local success, titleObj
    		if pagename then
    			success, titleObj = pcall(mw.title.new, pagename)
    		end
    		if success and titleObj then
    			obj.title = titleObj
    			if titleObj == mw.title.getCurrentTitle() then
    				obj._usesCurrentTitle = true
    			end
    		else
    			obj.title = mw.title.getCurrentTitle()
    			obj._usesCurrentTitle = true
    		end
    	end
    
    	-- Set suppression parameter values
    	for _, key in ipairs{'nocat', 'categories'} do
    		local value = obj:parameter(key)
    		value = trimWhitespace(value, true)
    		obj['_' .. key] = yesno(value)
    	end
    	do
    		local subpage = obj:parameter('subpage')
    		local category2 = obj:parameter('category2')
    		if type(subpage) == 'string' then
    			subpage = mw.ustring.lower(subpage)
    		end
    		if type(category2) == 'string' then
    			subpage = mw.ustring.lower(category2)
    		end
    		obj._subpage = trimWhitespace(subpage, true)
    		obj._category2 = trimWhitespace(category2) -- don't remove blank values
    	end
    	return obj
    end
    
    function CategoryHandler:parameter(key)
    	local parameterNames = self._data.parameters[key]
    	local pntype = type(parameterNames)
    	if pntype == 'string' or pntype == 'number' then
    		return self._args[parameterNames]
    	elseif pntype == 'table' then
    		for _, name in ipairs(parameterNames) do
    			local value = self._args[name]
    			if value ~= nil then
    				return value
    			end
    		end
    		return nil
    	else
    		error(string.format(
    			'invalid config key "%s"',
    			tostring(key)
    		), 2)
    	end
    end
    
    function CategoryHandler:isSuppressedByArguments()
    	return
    		-- See if a category suppression argument has been set.
    		self._nocat == true
    		or self._categories == false
    		or (
    			self._category2
    			and self._category2 ~= self._data.category2Yes
    			and self._category2 ~= self._data.category2Negative
    		)
    
    		-- Check whether we are on a subpage, and see if categories are
    		-- suppressed based on our subpage status.
    		or self._subpage == self._data.subpageNo and self.title.isSubpage
    		or self._subpage == self._data.subpageOnly and not self.title.isSubpage
    end
    
    function CategoryHandler:shouldSkipBlacklistCheck()
    	-- Check whether the category suppression arguments indicate we
    	-- should skip the blacklist check.
    	return self._nocat == false
    		or self._categories == true
    		or self._category2 == self._data.category2Yes
    end
    
    function CategoryHandler:matchesBlacklist()
    	if self._usesCurrentTitle then
    		return self._data.currentTitleMatchesBlacklist
    	else
    		mShared = mShared or require('Module:Category handler/shared')
    		return mShared.matchesBlacklist(
    			self.title.prefixedText,
    			mw.loadData('Module:Category handler/blacklist')
    		)
    	end
    end
    
    function CategoryHandler:isSuppressed()
    	-- Find if categories are suppressed by either the arguments or by
    	-- matching the blacklist.
    	return self:isSuppressedByArguments()
    		or not self:shouldSkipBlacklistCheck() and self:matchesBlacklist()
    end
    
    function CategoryHandler:getNamespaceParameters()
    	if self._usesCurrentTitle then
    		return self._data.currentTitleNamespaceParameters
    	else
    		if not mappings then
    			mShared = mShared or require('Module:Category handler/shared')
    			mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData
    		end
    		return mShared.getNamespaceParameters(
    			self.title,
    			mappings
    		)
    	end
    end
    
    function CategoryHandler:namespaceParametersExist()
    	-- Find whether any namespace parameters have been specified.
    	-- We use the order "all" --> namespace params --> "other" as this is what
    	-- the old template did.
    	if self:parameter('all') then
    		return true
    	end
    	if not mappings then
    		mShared = mShared or require('Module:Category handler/shared')
    		mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData
    	end
    	for ns, params in pairs(mappings) do
    		for i, param in ipairs(params) do
    			if self._args[param] then
    				return true
    			end
    		end
    	end
    	if self:parameter('other') then
    		return true
    	end
    	return false
    end
    
    function CategoryHandler:getCategories()
    	local params = self:getNamespaceParameters()
    	local nsCategory
    	for i, param in ipairs(params) do
    		local value = self._args[param]
    		if value ~= nil then
    			nsCategory = value
    			break
    		end
    	end
    	if nsCategory ~= nil or self:namespaceParametersExist() then
    		-- Namespace parameters exist - advanced usage.
    		if nsCategory == nil then
    			nsCategory = self:parameter('other')
    		end
    		local ret = {self:parameter('all')}
    		local numParam = tonumber(nsCategory)
    		if numParam and numParam >= 1 and math.floor(numParam) == numParam then
    			-- nsCategory is an integer
    			ret[#ret + 1] = self._args[numParam]
    		else
    			ret[#ret + 1] = nsCategory
    		end
    		if #ret < 1 then
    			return nil
    		else
    			return table.concat(ret)
    		end
    	elseif self._data.defaultNamespaces[self.title.namespace] then
    		-- Namespace parameters don't exist, simple usage.
    		return self._args[1]
    	end
    	return nil
    end
    
    --------------------------------------------------------------------------------
    -- Exports
    --------------------------------------------------------------------------------
    
    local p = {}
    
    function p._exportClasses()
    	-- Used for testing purposes.
    	return {
    		CategoryHandler = CategoryHandler
    	}
    end
    
    function p._main(args, data)
    	data = data or mw.loadData('Module:Category handler/data')
    	local handler = CategoryHandler.new(data, args)
    	if handler:isSuppressed() then
    		return nil
    	end
    	return handler:getCategories()
    end
    
    function p.main(frame, data)
    	data = data or mw.loadData('Module:Category handler/data')
    	local args = require('Module:Arguments').getArgs(frame, {
    		wrappers = data.wrappers,
    		valueFunc = function (k, v)
    			v = trimWhitespace(v)
    			if type(k) == 'number' then
    				if v ~= '' then
    					return v
    				else
    					return nil
    				end
    			else
    				return v
    			end
    		end
    	})
    	return p._main(args, data)
    end
    
    return p