--
-- Performs type validationlocal function Valid(v) if v and v ~= true then --reject nil/boolean; faster than 2 type comparisons local str = tostring(v) --functions not filtered since unlikely passed by accident (Scribunto does not have userdata/thread types) --tostring(string-type) returns same ref; same refs compare faster than type if str ~= v and str
local MBpairslocal noOp = function enddo local iMap, vMap, oMap, pIter, pOther, pFast, Next --Map local function init -- init = noOp after first run function Next(t) return next, t -- slightly faster to do this than to use select end
function pIter(t, k) -- don't use rawget; accepting unmapped tables does not measurably affect performance. k = (iMap[t] or MBpairs(t, true) and iMap[t])[not k and 1 or vMap[t][k]] return k, t[k] end
function pOther(t, k) -- comparison to nil because false is a valid key k = (oMap[t] or MBpairs(t, true) and oMap[t])[nil==k and 1 or vMap[t][k]] return k, t[k] end
function pFast(t, k) -- mapless iterator; almost as fast as native ipairs; slight performance penalty when length not cached k = not k and 1 or k < (vMap[t] or #t) and k + 1 or nil return k, t[k] end
local mk = -- use mode 'k'; found that mode 'kv' sometimes garbage collects maps mid-loop (may not error because iterators auto re-map, but that's expensive) init = noOp iMap = setmetatable(mk) -- numeric keys vMap = setmetatable(mk) -- next key oMap = setmetatable(mk) -- non-numeric keys end
function MBpairs(t, ...) -- pairs always iterates in order local iter, ex = ... init iter = iter
nil and rawget(t, 1)~=nil and next(t, #t)
local parent, rawkey, specdo --new scope for variables not reused outside (reduces number of var names that need to checked outside of scope) --shared meta for Buffer parent property, raw mode, and specialized functions local mkv = --shared meta less memory parent = setmetatable(mkv) rawkey = setmetatable(mkv) spec = setmetatable(mkv)end
local MB, MBi, MBmix, buffHTML, gfuncs, noCache, Elementdo --minimize number of locals per scope to reduce time spent sifting through irrelevant variable names local _stream do local stream --keep stream near top of scope
local function init(f) --init = noOp after first run local function each(self, ...) for k = 1, select('#', ...) do k = Valid(select(k, ...)) -- slightly faster than table.insert(self, (Valid(select(k, ...)))) if k then table.insert(self, k) end end return self end init = noOp stream = for k, v in next, MB do stream[k] = stream[k] or v end setmetatable(stream, getmetatable(MB)) end
function _stream(self, ...) init self.last_concat = nil return setmetatable(self, stream):each(...) end end
-- helper for :getParent-like methods (including getBuffer which does not return a parent) local function isMBfunc(Buffer, s, ...) --eventually should figure out to make this work for :getHTML which is very similar return s and (select('#', ...)
-- helper for :_out and :_str local function MBselect(n, ...) local n, seps = n - 1, if type(seps[n])
local _inHTMLdo local lastBuffer, lastHTML local function init(...) -- init replaced and new version called on return local create, mwFunc = mw.html.create do local mwHTMLmeta = getmetatable(create) buffHTML, mwFunc, _inHTML = setmetatable(mw.clone(mwHTMLmeta), getmetatable(MB)), mwHTMLmeta.__index -- buffHTML declared near top of module; remove _inHTML from outer scope function init(nodes, ...) local name, args, tag = select(... and type(...)
local nonSelf, BHi =, buffHTML.__indexdo local g g = setmetatable(nonSelf, g) setmetatable(BHi, g)endfor k in next, nonSelf do -- any HTML objects returned by these funcs will be granted Module:Buffer enhancements local func = mwFunc[k] BHi[k] = function(t, ...) local HTML = func(t, ...) return parent[HTML] and HTML or setmetatable(parent(HTML, t), buffHTML) endenddo local function joinNode(HTML, sep) local nodes, join = HTML.nodes if noCache and rawkey[sep] or Valid(sep) then join, HTML.nodes = tostring(rawset(HTML, 'nodes',)), nodes end return join or tostring(HTML) end for k, v in next, do BHi[k] = vend end do local htmlArg, skip, outFuncs =, do local out local function func(nodes, ...) return out(parent[nodes], ...) end outFuncs = setmetatable end Element = local tempMeta = function tempMeta.__index(t, i) return tempMeta.copy[i] and rawset(t, i, MBi._cc(false, 0, t.orig[i]))[i] or t.orig[i] end rawkey[setmetatable(Element, {__index = outFuncs, __concat = function(Element, v) return setmetatable({nodes = spec({}, Element),orig = parent[v]}, tempMeta) end})] = math.hugeend
function MBi:getHTML(...) lastBuffer = self if ... then if select('#', ...)
function MBi:_html(...) return MBi._(self, lastHTML, select(spec[self]
0 and 1 or 2, true, ...))end
return init(...) end function _inHTML(self, ...) local HTML = init(nil, ...) if HTML.selfClosing and spec[self]
0 then if v and not c then return prev end local meta, c = select(v and 1 or 3, alt, c, same, 0) return setmetatable(meta) elseif v
false else prev:_build end end init, noCache = function(v, c) prev = setmetatable(build) return prev end, true return init(...)end
function unbuild(sep) for k, v in MBpairs(sep, nil) do k = getmetatable(v) if k and (k
alt) then shift(v.a or v, -v.c) endend end function _var(self, ...) local obj if ... and ... ~= true then obj = init(...) elseif prev then if ... ~= false then obj = rebuild(...) else rebuild(...) end end return obj and MBi._(self, obj, nil, true) or self endend
local lib; MBi = setmetatableend
function MBmix(t, v, ...) return v and ((type(v) ~= 'table' or getmetatable(v)) and MBi._(t, v) or (select('#', ...)
local _G, new_G = _G -- localize _G for console testing (console _G ~= module _G)return setmetatable