Module:FrameTools: Difference between revisions
Jump to navigation
Jump to search
Mediawiki>ExE Boss (Hoist type checks to function start) |
m (1 revision imported) |
Latest revision as of 02:23, 30 April 2021
See Global Lua Modules/FrameTools
- Subpages {{#dpl: | namespace = Module | titlematch = FrameTools/% | skipthispage = false }}
-- <nowiki> -------------------------------------------------------------------------------- -- This module contains helper functions for dealing with frame objects. -- -- @script frameTools -- @alias p -- @author [[User:DarthKitty]] -- @author [[User:Dessamator]] -------------------------------------------------------------------------------- local p = {} local util = require "libraryUtil" -------------------------------------------------------------------------------- -- Frame methods are protected by a `checkSelf` function, which makes them more -- difficult to copy. -- -- @param {Frame|PseudoFrame} frame -- The frame of pseudo-frame to copy. -- @return {PseudoFrame} -------------------------------------------------------------------------------- function p.copy(frame) util.checkType("frameTools.copy", 1, frame, "table") local copy = mw.clone(frame) -- Point methods on `copy` to their `frame` counterparts for methodName, method in pairs(frame) do if type(method) == "function" and methodName ~= "getParent" then copy[methodName] = function (copy, ...) return method(frame, ...) end end end -- This method needs special treatment function copy:getParent() local parent = frame:getParent() if parent then return p.copy(parent) end end return copy end -------------------------------------------------------------------------------- -- Creates a pseudo frame with some useful functions available in -- [[mw:Extension:Scribunto]], e.g. `newChild`. -- -- @param[opt] {Frame} frame -- The frame that provides implementatinos for the `preprocess` method. -- @param[opt] {table} parentArgs -- The parameters available on `pseudoFrame:getParent().args` -- @param[opt] {table} childArgs -- The parameters available on `pseudoFrame.args` -- @return {PseudoFrame} -- The new pseudo-Frame -------------------------------------------------------------------------------- function p.makePseudoFrame(frame, parentArgs, childArgs) util.checkType("frameTools.makePseudoFrame", 1, frame, "table", true) util.checkType("frameTools.makePseudoFrame", 2, parentArgs, "table", true) util.checkType("frameTools.makePseudoFrame", 3, childArgs, "table", true) local pseudoFrame = {parent = {}} local checkSelf = util.makeCheckSelfFunction("pseudoFrame", "frame", pseudoFrame, "PseudoFrame object") local parentFrame = pseudoFrame.parent local checkSelfParent = util.makeCheckSelfFunction("pseudoFrame", "frame", parentFrame, "parent PseudoFrame object") local pArgs = {} local pTitle local args = {} if frame then args = frame.args for name in pairs(frame) do pseudoFrame[name] = function (pseudoFrame, ...) return frame[name](frame, ...) end parentFrame[name] = function (parent, ...) return pseudoFrame[name](frame, ...) end end if type(frame.getParent) == "function" and frame:getParent() then pArgs = frame:getParent().args pTitle = frame:getParent():getTitle() end end parentFrame.args = mw.clone(parentArgs or pArgs) pseudoFrame.args = mw.clone(childArgs or args) pseudoFrame.title = mw.title.getCurrentTitle().text parentFrame.title = pTitle or "" function pseudoFrame:getTitle() checkSelf(self, "getTitle") return self.title end function parentFrame:getTitle() checkSelfParent(self, "getTitle") return self.title end function pseudoFrame:getParent() checkSelf(self, "getParent") return self.parent end function parentFrame:getParent() checkSelfParent(self, "getParent") return self.parent end -- Creates a new Frame object that is a child of the current frame, optional -- arguments and title. -- Syntax: frame:newChild{title = title, args = table} function pseudoFrame:newChild(attributes) checkSelf(self, "newChild") local tmpTable = p.makePseudoFrame(frame, self.args, attributes.args) tmpTable.parent.title = self.title tmpTable.title = attributes.title return tmpTable end function pseudoFrame:showargs() checkSelf(self, "showargs") local childParams = "Child parameters:\nKey \t Value" local parentParams = "Parent parameters:\nKey \t Value" if self.args then for k, v in pairs(self.args) do childParams = childParams .. "\n" .. k .. "\t" .. v end end if self.parent.args then for k, v in pairs(self.parent.args) do parentParams = parentParams .. "\n" .. k .. "\t" .. v end end return childParams .. "\n\n" .. parentParams end function pseudoFrame:setArgs(childArgs, parentArgs) checkSelf(self, "setArgs") if childArgs then self.args = childArgs or {} end if parentArgs then self.parent.args = parentArgs end end local function preprocessMock(parent) return function(self, opt) if parent then checkSelfParent(self, "expandTemplate") else checkSelf(self, "expandTemplate") end local text if type(opt) == "table" then text = opt.text else text = opt end text = tostring(text) return text end end if not pseudoFrame.preprocess then pseudoFrame.preprocess = preprocessMock() end if not pseudoFrame.parent.preprocess then pseudoFrame.parent.preprocess = preprocessMock("parent") end local function expandTemplateMock(parent) return function(self, opt) if parent then checkSelfParent(self, "expandTemplate") else checkSelf(self, "expandTemplate") end if type(opt) ~= "table" then error("frame:expandTemplate: the first parameter must be a table") end local title if opt.title == nil then error("frame:expandTemplate: a title is required") else title = tostring(opt.title) end local args if opt.args == nil then args = {} elseif type(opt.args) == "table" then args = opt.args else error("frame:expandTitle: args must be a table") end local keys = {} for k in pairs(args) do table.insert(keys, k) end table.sort(keys) local text = {} table.insert(text, "{{" .. title) local anonymous_index = 0 for _, k in ipairs(keys) do table.insert(text, "|" .. (tonumber(k) == (anonymous_index + 1) and "" or (" " .. k .. " = ")) .. args[k]) if tonumber(k) == (anonymous_index + 1) then anonymous_index = anonymous_index + 1 end end table.insert(text, "}}") text = table.concat(text, '\n') return text end end if not pseudoFrame.expandTemplate then pseudoFrame.expandTemplate = expandTemplateMock() end if not parentFrame.expandTemplate then parentFrame.expandTemplate = expandTemplateMock("parent") end if mw.getCurrentFrame() == nil then _G.mw.getCurrentFrame = function() return pseudoFrame end end return pseudoFrame end -------------------------------------------------------------------------------- -- Returns a frame-like object with the given arguments removed. -- -- @param {Frame|PseudoFrame} frame -- The Frame to copy -- @param[opt] ... -- The arguments to omit from the new `Frame`'s `args` property -- @return {PseudoFrame} -------------------------------------------------------------------------------- function p.removeArgs(frame, ...) util.checkType("frameTools.removeArgs", 1, frame, "table") local pseudoFrame = p.copy(frame) local args = pseudoFrame.args local metatable = getmetatable(args) -- disable arg caching metatable.__index = nil metatable.__pairs = nil -- remove args for _, arg in ipairs{...} do args[arg] = nil end return pseudoFrame end -------------------------------------------------------------------------------- -- A frame-like object that provides mock implementations for the most common -- @{Frame|mw.frame} instance methods and properties -- -- @type {Frame} PseudoFrame -------------------------------------------------------------------------------- return p -- </nowiki> -- (Add categories here.)