On Fanlore, users with accounts can edit pages including user pages, can create pages, and more. Any information you publish on a page or an edit summary will be accessible by the public and to Fanlore personnel. Because Fanlore is a wiki, information published on Fanlore will be publicly available forever, even if edited later. Be mindful when sharing personal information, including your religious or political views, health, racial background, country of origin, sexual identity and/or personal relationships. To learn more, check out our Terms of Service and Privacy Policy. Select "dismiss" to agree to these terms.

Module:Italic title

From Fanlore
Jump to navigation Jump to search

-- This module implements {{italic title}}.

require('Module:No globals')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local yesno = require('Module:Yesno')

--------------------------------------------------------------------------------
-- ItalicTitle class
--------------------------------------------------------------------------------

local ItalicTitle = {}

do
    ----------------------------------------------------------------------------
    -- Class attributes and functions
    -- Things that belong to the class are here. Things that belong to each
    -- object are in the constructor.
    ----------------------------------------------------------------------------

    -- Keys of title parts that can be italicized.
    local italicizableKeys = {
        namespace = true,
        title = true,
        dab = true,
    }

    ----------------------------------------------------------------------------
    -- ItalicTitle constructor
    -- This contains all the dynamic attributes and methods.
    ----------------------------------------------------------------------------

    function ItalicTitle.new()
        local obj = {}

        -- Function for checking self variable in methods.
        local checkSelf = libraryUtil.makeCheckSelfFunction(
            'ItalicTitle',
            'obj',
            obj,
            'ItalicTitle object'
        )

        -- Checks a key is present in a lookup table.
        -- Param: name - the function name.
        -- Param: argId - integer position of the key in the argument list.
        -- Param: key - the key.
        -- Param: lookupTable - the table to look the key up in.
        local function checkKey(name, argId, key, lookupTable)
            if not lookupTable[key] then
                error(string.format(
                    "bad argument #%d to '%s' ('%s' is not a valid key)",
                    argId,
                    name,
                    key
                ), 3)
            end
        end

        -- Set up object structure.
        local parsed = false
        local categories = {}
        local italicizedKeys = {}
        local italicizedSubstrings = {}

        -- Parses a title object into its namespace text, title, and
        -- disambiguation text.
        -- Param: options - a table of options with the following keys:
        --     title - the title object to parse
        --     ignoreDab - ignore any disambiguation parentheses
        -- Returns the current object.
        function obj:parseTitle(options)
            checkSelf(self, 'parseTitle')
            checkType('parseTitle', 1, options, 'table')
            checkTypeForNamedArg('parseTitle', 'title', options.title, 'table')
            local title = options.title
        
            -- Title and dab text
            local prefix, parentheses
            if not options.ignoreDab then
                prefix, parentheses = mw.ustring.match(
                    title.text,
                    '^(.+) %(([^%(%)]+)%)$'
                )
            end
            if prefix and parentheses then
                self.title = prefix
                self.dab = parentheses
            else
                self.title = title.text
            end
        
            -- Namespace
            local namespace = mw.site.namespaces[title.namespace].name
            if namespace and #namespace >= 1 then
                self.namespace = namespace
            end

            -- Register the object as having parsed a title.
            parsed = true
        
            return self
        end

        -- Italicizes part of the title.
        -- Param: key - the key of the title part to be italicized. Possible
        -- keys are contained in the italicizableKeys table.
        -- Returns the current object.
        function obj:italicize(key)
            checkSelf(self, 'italicize')
            checkType('italicize', 1, key, 'string')
            checkKey('italicize', 1, key, italicizableKeys)
            italicizedKeys[key] = true
            return self
        end

        -- Un-italicizes part of the title.
        -- Param: key - the key of the title part to be un-italicized. Possible
        -- keys are contained in the italicizableKeys table.
        -- Returns the current object.
        function obj:unitalicize(key)
            checkSelf(self, 'unitalicize')
            checkType('unitalicize', 1, key, 'string')
            checkKey('unitalicize', 1, key, italicizableKeys)
            italicizedKeys[key] = nil
            return self
        end

        -- Italicizes a substring in the title. This only affects the main part
        -- of the title, not the namespace or the disambiguation text.
        -- Param: s - the substring to be italicized.
        -- Returns the current object.
        function obj:italicizeSubstring(s)
            checkSelf(self, 'italicizeSubstring')
            checkType('italicizeSubstring', 1, s, 'string')
            italicizedSubstrings[s] = true
            return self
        end

        -- Un-italicizes a substring in the title. This only affects the main
        -- part of the title, not the namespace or the disambiguation text.
        -- Param: s - the substring to be un-italicized.
        -- Returns the current object.
        function obj:unitalicizeSubstring(s)
            checkSelf(self, 'unitalicizeSubstring')
            checkType('unitalicizeSubstring', 1, s, 'string')
            italicizedSubstrings[s] = nil
            return self
        end

        -- Renders the object into a page name. If no title has yet been parsed,
        -- the current title is used.
        -- Returns string
        function obj:renderTitle()
            checkSelf(self, 'renderTitle')

            -- Italicizes a string
            -- Param: s - the string to italicize
            -- Returns string.
            local function italicize(s)
                assert(type(s) == 'string', 's was not a string')
                assert(s ~= '', 's was the empty string')
                return string.format('<i>%s</i>', s)
            end
        
            -- Escape characters in a string that are magic in Lua patterns.
            -- Param: pattern - the pattern to escape
            -- Returns string.
            local function escapeMagicCharacters(s)
                assert(type(s) == 'string', 's was not a string')
                return s:gsub('%p', '%%%0')
            end

            -- If a title hasn't been parsed yet, parse the current title.
            if not parsed then
                self:parseTitle{title = mw.title.getCurrentTitle()}
            end

            -- Italicize the different parts of the title and store them in a
            -- titleParts table to be joined together later.
            local titleParts = {}

            -- Italicize the italicizable keys.
            for key in pairs(italicizableKeys) do
                if self[key] then
                    if italicizedKeys[key] then
                        titleParts[key] = italicize(self[key])
                    else
                        titleParts[key] = self[key]
                    end
                end
            end

            -- Italicize substrings. If there are any substrings to be
            -- italicized then start from the raw title, as this overrides any
            -- italicization of the main part of the title.
            if next(italicizedSubstrings) then
                titleParts.title = self.title
                for s in pairs(italicizedSubstrings) do
                    local pattern = escapeMagicCharacters(s)
                    local italicizedTitle, nReplacements = titleParts.title:gsub(
                        pattern,
                        italicize
                    )
                    titleParts.title = italicizedTitle

                    -- If we didn't make any replacements then it means that we
                    -- have been passed a bad substring or that the page has
                    -- been moved to a bad title, so add a tracking category.
                    if nReplacements < 1 then
                        categories['Pages using italic title with no matching string'] = true
                    end
                end
            end

            -- Assemble the title together from the parts.
            local ret = ''
            if titleParts.namespace then
                ret = ret .. titleParts.namespace .. ':'
            end
            ret = ret .. titleParts.title
            if titleParts.dab then
                ret = ret .. ' (' .. titleParts.dab .. ')'
            end

            return ret
        end

        -- Returns an expanded DISPLAYTITLE parser function called with the
        -- result of obj:renderTitle, plus any other optional arguments.
        -- Returns string
        function obj:renderDisplayTitle(...)
            checkSelf(self, 'renderDisplayTitle')
            return mw.getCurrentFrame():callParserFunction(
                'DISPLAYTITLE',
                self:renderTitle(),
                ...
            )
        end

        -- Returns an expanded DISPLAYTITLE parser function called with the
        -- result of obj:renderTitle, plus any other optional arguments, plus
        -- any tracking categories.
        -- Returns string
        function obj:render(...)
            checkSelf(self, 'render')
            local ret = self:renderDisplayTitle(...)
            for cat in pairs(categories) do
                ret = ret .. string.format(
                    '[[Category:%s]]',
                    cat
                )
            end
            return ret
        end

        return obj
    end
end

--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------

local p = {}

local function getArgs(frame, wrapper)
    assert(type(wrapper) == 'string', 'wrapper was not a string')
    return require('Module:Arguments').getArgs(frame, {
        wrappers = wrapper
    })
end

-- Main function for {{italic title}}
function p._main(args)
    checkType('_main', 1, args, 'table')
    local italicTitle = ItalicTitle.new()
    italicTitle:parseTitle{
        title = mw.title.getCurrentTitle(),
        ignoreDab = yesno(args.all, false)
    }
    if args.string then
        italicTitle:italicizeSubstring(args.string)
    else
        italicTitle:italicize('title')
    end
    return italicTitle:render(args[1])
end

function p.main(frame)
    return p._main(getArgs(frame, 'Template:Italic title'))
end

function p._dabonly(args)
    return ItalicTitle.new()
        :italicize('dab')
        :render(args[1])
end

function p.dabonly(frame)
    return p._dabonly(getArgs(frame, 'Template:Italic dab'))
end


return p