Module:Buffer

Tú kanst upprætta kunningarsíðu um hetta modulið á Module:Buffer/doc

--[[=============================This Module was written by Alexander Zhikun He, also known as, User:Codehydro on the English Wiki HowAll methods were developed independently and any resemblance to other string buffer libraries would be coincidental.Furthermore, many methods will not work when compiled by standard Lua libraries as they depend on behaviors unique tothe MediaMiki Scribunto mod, which, for example, has a getmetatable() method that always returns nil on non-tables.https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manualSource code comments may be thin at some points because they are intended to be supplemented by the documentation page:https://www.how.com.vn/wiki/en/Module:Buffer/docLicensed under Creative Commons Attribution-ShareAlike 3.0 Unported Licensehttps://www.how.com.vn/wiki/en/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_Licensehttps://www.how.com.vn/wiki/en/Module:Bufferhttps://www.how.com.vn/wiki/en/User:Codehydro=============================--]]local function Valid(v)--type validationif v and v~=true then--reject nil/boolean; faster than 2 type() comparisonslocal str = tostring(v)--functions not filtered since unlikely passed by accident (Scribunto does not have userdata/thread types)if str~=v and str=='table' then return rawget(v, 1) and table.concat(v) end--tostring(string-type) returns same ref; same refs compare faster than type()if str~='' then return str end--numbers are coerced to string per table.concat op; appending in string form saves ops on repeat concatendendlocal noOp, MBpairs = function()end do local iMap, vMap, oMap, pIter, pOther, pFast, Next--Maplocal function init()--init = noOp after first runfunction Next(t) return next, t end--slightly faster to do this than to use select()function pIter(t, k) k = (iMap[t] or MBpairs(t, true) and iMap[t])[not k and 1 or vMap[t][k]] return k, t[k] end--don't use rawget; accepting unmapped tables does not measurably affect performance.function pOther(t, k) k = (oMap[t] or MBpairs(t, true) and oMap[t])[nil==k and 1 or vMap[t][k]] return k, t[k] end--comparison to nil because false is a valid keyfunction pFast(t, k) k = not k and 1 or k < (vMap[t] or #t) and k + 1 or nil return k, t[k] end--mapless iterator; almost as fast as native ipairs; slight performance penalty when length not cached   --k and k < (vMap[t] or #t) and k + 1 or not k and 1 or nil return k, t[k] end--mapless iterator; almost as fast as native ipairs; slight performance penalty when length not cachedlocal mk = {__mode = 'k'}--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, iMap, vMap, oMap = noOp, setmetatable({}, mk), setmetatable({}, mk), setmetatable({}, mk)--iMap is numeric keys, oMap is non-numeric keys, and vMap points to next keyendfunction MBpairs(t, ...)--pairs always iterates in orderlocal iter, ex = ...iter = iter==init()--nilif iter and not oMap[t] and ex==nil and rawget(t, 1)~=nil and next(t, #t)==nil then--while possible to miss keys, more thorough check would negate the benefit of pFastvMap[t] = #t return pFast, t, nilelseif ... or not vMap[t] or select('#', ...)~=1 thenlocal ti, tn, to, n = {}, {}, {}, #t--reduces table lookupsiMap[t], vMap[t], oMap[t] = ti, tn, tofor k = 1, n do ti[k], tn[k] = k, k + 1 end--stage one avoids number type checking op in stage two for most numeric keysfor k in (ex or Next)(t) doif not tn[k] then table.insert(tonumber(k)~=k and to or ti, k) endendif #ti~=n thentable.sort(ti)for k = 1, #ti do tn[ti[k]] = k + 1 end--somewhat wasteful, but trying to avoid overwriting can be even more expensiveendfor k = 1, #to do tn[to[k]] = k + 1 endendreturn iter and pIter or oMap[t] and pOther or noOp, t--noOp for maplessendendlocal parent, rawkey, spec do--new scope for variables not reused outside (reduces number of var names that need to checked outside of scope)local mkv = {__mode='kv', __call=function(t,k,v)t[k]=v return k end}--shared meta for Buffer parent property, raw mode, and specialized functionsparent, rawkey, spec = setmetatable({}, mkv), setmetatable({}, mkv), setmetatable({}, mkv)--shared meta less memoryendlocal MB, MBi, MBmix, buffHTML, gfuncs, noCache, Element do--minimize number of locals per scope to reduce time spent sifting through irrelevant variable nameslocal _stream do local stream--keep stream near top of scopelocal function init(f)--init = noOp after first runlocal function each(self, ...)for k = 1, select('#', ...) dok = Valid(select(k, ...))--slightly faster than table.insert(self, (Valid(select(k, ...))))if k then table.insert(self, k) endendreturn selfendinit, stream, _stream = noOp, {__call = function(t, v) v = v and Valid(v) return v and table.insert(t, v) or t end,--last_concat cleared before entering stream mode__index = function(t, i) return i=='each' and each or MB.__index(t, i) and setmetatable(t, MB)[i] end,--no table look up minimizes resources to retrieve the only stream function__tostring = function(t) return setmetatable(t, MB)() end} for k, v in next, MB do stream[k] = stream[k] or v endsetmetatable(stream, getmetatable(MB))endfunction _stream(self, ...) self.last_concat = init() return setmetatable(self, stream):each(...) endendlocal function isMBfunc(Buffer, s, ...)--helper for :getParent()-like methods (including getBuffer which does not return a parent)return s and (select('#', ...)==0 and--eventually should figure out to make this work for :getHTML which is very similar(not rawkey[s] and tostring(s):match'^_.*' and MB.__index(Buffer, s) and MB.__index(Buffer, s)(Buffer) or MBmix(Buffer, s))--unprefixed function names append as a stringor assert(MB.__index(Buffer, s), ('" %s " does not match any available Module:Buffer function'):format(s))(Buffer, ...)--getParent is a one-way trip so one-time assert not expensive) or Bufferendlocal function MBselect(n, ...)--helper for :_out and :_strlocal n, seps = n - 1, {select(2, ...)}if type(seps[n])=='table' then if buffHTML and rawget(seps[n], buffHTML) then return ... endsetmetatable(seps, {__index = setmetatable(seps[n], {__index = function(t) return rawget(t, 1) end})})[n] = nilendreturn ..., sepsendlocal _inHTML do local lastBuffer, lastHTMLlocal function init(...)--init replaced and new version called on returnlocal create, mwFunc = mw.html.create dolocal mwHTMLmeta = getmetatable(create())buffHTML, mwFunc, _inHTML = setmetatable(mw.clone(mwHTMLmeta), getmetatable(MB)), mwHTMLmeta.__index--buffHTML declared near top of module; remove _inHTML from outer scopefunction init(nodes, ...)local name, args, tag = select(... and type(...)=='table' and 1 or 2, nil, ...)tag = create(Valid(name), args)if nodes then table.insert(nodes, tag.parent and tag or rawset(tag, 'parent', parent[nodes])) endif args thenlocal a, b = args.selfClosing, args.parentargs.selfClosing, args.parent = nilif next(args) then Element._add(parent(tag.nodes, tag), args) endargs.selfClosing, args.parent = a, b--in case args is reusedendreturn tagendfor k, v in next, {[mw] = mwHTMLmeta,__call = function(h, v) return MBmix(spec[h.nodes] and h.nodes or spec(setmetatable(parent(h.nodes, h), MB), Element), v) end,__concat = false,--false means take from MB__eq = false} do buffHTML[k] = v or MB[k] endendlocal nonSelf, BHi = {tag=true,done=true,allDone=true}, buffHTML.__index do local gg = {__index = function(t, i)if gfuncs and gfuncs[i] then g.__index, gfuncs = gfuncs return g.__index[i] endend}setmetatable(nonSelf, g)setmetatable(BHi, g)endfor k in next, nonSelf do--any HTML objects returned by these funcs will be granted Module:Buffer enhancementslocal 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.nodesif noCache and rawkey[sep] or Valid(sep) then join, HTML.nodes = tostring(rawset(HTML, 'nodes', {MB.__call(nodes, sep)})), nodes endreturn join or tostring(HTML)endfor k, v in next, {getParent = function(HTML, ...) lastHTML = HTML return MBi.getParent(HTML:allDone(), ...) end,--return to Buffer that created the HTML treegetBuffer = function(HTML, ...) lastHTML = HTML return isMBfunc(lastBuffer, ...) end,--return to last usedkillParent = function(HTML, ...) MBi.killParent(HTML:allDone(), ...) return HTML end,_out = function(HTML, ...)if ...==0 then MBi._out(HTML.nodes, ...) return HTML endlastHTML, HTML = HTML, HTML:allDone()local n, ops, seps = select('#', ...)if n > 1 thenlocal ops, seps = MBselect(n, ...)return parent[HTML]:_in(joinNode(HTML, rawget(seps, 0))):_out(ops, rawset(seps, buffHTML, true))endreturn parent[HTML]:_(joinNode(HTML, ...))end,_str = function(HTML, ...)--does not set lastHTMLif ...==0 then return joinNode(HTML, select(2, ...)) end--passing 0 strings without calling allDone()local HTML, n = HTML:allDone(), select('#', ...)if n > 1 thenlocal ops, seps = MBselect(n, ...)return parent[HTML]:_in(joinNode(HTML, rawget(seps, 1))):_str(ops, rawset(seps, buffHTML, true))endreturn joinNode(HTML, ...)end,_parent = function(HTML, ...) table.insert(HTML.nodes, parent[HTML:allDone()]:_str(...)) return HTML end} do BHi[k] = v endenddo local htmlArg, skip, outFuncs = {parent=true,selfClosing=true,tagName=true}, {}do local out local function func(nodes, ...) return out(parent[nodes], ...) endoutFuncs = setmetatable({tag = function(nodes, ...) return parent(setmetatable(init(nodes, ...), buffHTML), parent[nodes]) end,done = function(b, ops)b = parent[b] while b.parent and ops~=0 do b, ops = b.parent, ops and ops - 1 or 0 endreturn bend}, {__index = function(nodes, i)if rawget(BHi, i) then out = BHi[i] return func end--rawget to exclude globalsend})endElement = {_add = function(nodes, t)for k, v in MBpairs(t), t, skip[t] do (v~=true and MBmix or noOp)(nodes, v) endlocal HTML = parent[nodes] for k, v in MBpairs(t, false) doif htmlArg[k] then HTML[k] = velseif v and v~=true thenif nonSelf[k] thenif k=='tag' thenif type(v)=='table' thenskip[v], k = 1, rawset(create(Valid(v[1])), 'parent', HTML)Element._add(spec(parent(k.nodes, k, table.insert(nodes, k)), Element), v)if k.selfClosing then k.nodes = nil else spec[k.nodes], parent[k.nodes] = nil end--free memory/reduce clutter; parent ref will auto-unset when k.nodes is nilif not k.tagName then k.styles, k.attributes = nil endelse table.insert(nodes, create(v)) endelseif mwFunc[k] thenif k=='done' and tonumber(v)~=v and v[1] and tonumber(v[1])==v[1] then skip[v] = 1 endMBmix(outFuncs[k](nodes, skip[v] and v[1]).nodes, v)elseif v[1] or v[2] thenk = MBi[k](nodes, unpack(v, 1, rawset(skip, v, k=='_B' and 1 or 2)[v]))Element._add(getmetatable(k) and rawget(k, 'nodes') or k, v)--if k is not a table, then v should not contain any extra keys or this may error.else MBi[k](nodes, v) end--k probably == '_G' or '_R'elseif mwFunc[k] thenif type(v)~='table' or rawget(v, 'nodes') then mwFunc[k](HTML, v)elselocal css = k=='css'for x, y in MBpairs(v, true) do (y and y~=true and mwFunc[k] or noOp)(HTML, css and x:gsub('_', '-') or x, y) end--iterate non-numbers firstfor _, y in MBpairs(v, nil) do (y and y~=true and mwFunc[k] or noOp)(HTML, y) end--don't bother with gsub since text must be quoted anyhowendelseif rawget(Element, k) or rawget(MBi, k) thenif tonumber(v)==v or v[1]==nil or getmetatable(v) then (Element[k] or MBi[k])(nodes, v)--v is probably string-able object, or a table to be handled by :_allelse (Element[k] or MBi[k])(nodes, unpack(v, 1, table.maxn(v))) end--v is definately a tableelse mwFunc.css(HTML, k:gsub('_', '-', 1), tostring(v)) end--oddly enough, :_add clocked its fastest runtime after adding auto-gsub as a featureskip[v] = nilendendreturn nodesend}local tempMeta = {mode='v', copy={styles=true,attributes=true}}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] endrawkey[setmetatable(Element, {__index = outFuncs, __concat=function(Element, v) return setmetatable({nodes=spec({}, Element),orig=parent[v]}, tempMeta) end})] = math.hugeendfunction MBi:getHTML(...)lastBuffer = selfif ... thenif select('#', ...)==1 then return not rawkey[s] and tostring(...):match'^_' and BHi[...] and BHi[...](lastHTML) or lastHTML(...)else return assert(BHi[...], ('" %s " does not match any mw.html or Buffer-mw.html function'):format(tostring(...)))(lastHTML, select(2, ...)) endendreturn lastHTMLendfunction MBi:_html(...) return MBi._(self, lastHTML, select(spec[self]==Element and select('#', ...)==0 and 1 or 2, true, ...)) endreturn init(...)endfunction _inHTML(self, ...)local HTML = init(nil, ...)if HTML.selfClosing and spec[self]==Element then self.last_concat = table.insert(self, HTML) return self endlastBuffer, lastHTML = self, setmetatable(parent(HTML, self), buffHTML)--set after 'args' table processed by :_addreturn HTMLendendlocal _var, unbuild do local prev, rebuildlocal function init(...)--init replaced before returnlocal function pick(b, v) return b and table.insert(b, v) or v endlocal function c(a, num) return rawset(a.a or a, 0, a[0] and a[0] + a.c or num and a[1] or a[1]:byte())[0] endlocal same, build, alt = {__tostring = function(a, b) return a.a[0] and pick(b, a.a.string and string.char(a.a[0]) or a.a.table and a.a[1][a.a[0]] or a.a[0]) end}, {__index = {c = 1},__tostring = function(t) return t:_build() end,table = function(a, b) local i = next(a[1], a[0]) or a[0]==#a[1] and next(a[1]) return pick(b, rawset(a.a or a, 0, i)[1][i]) end,--change rate (a.c) ignored since users control the table's contentsnumber = function(a, b) return pick(b, c(a, true)) end,string = function(a, b) return pick(b, string.char(c(a))) end}, {__index = function(a, i) return a.a[i] end, __tostring = function(a, b) return (rawget(a, 0) and a[0]==tostring(a[0]) and rawset(a, 0, a[0]:byte()) or a).a._build(a, b) end}local function shift(t, c)t[0] = t[0] and t[0] + c or t:_build() and t[0] - t.c + cif t.table then t[0] = (t[0] - 1) % #t[1] + 1 endendfunction rebuild(...)local v, c = ...if v or select('#', ...)==0 thenif v and not c then return prev endlocal meta, c = select(v and 1 or 3, alt, c, same, 0)return setmetatable({a = prev, _build = meta.__tostring, c = c}, meta)elseif v==nil then--no-opelseif c then shift(prev, c)--v == falseelse prev:_build() endendinit, noCache = function(v, c) prev = setmetatable({v, c = c, _build = build[type(v)] or v, [type(v)] = true, alt = {}}, build) return prev end, truereturn init(...)endfunction unbuild(sep)for k, v in MBpairs(sep, nil) dok = getmetatable(v) if k and (k==build or k==alt) then shift(v.a or v, -v.c) endendendfunction _var(self, ...)local obj if ... and ...~=true then obj = init(...)elseif prev thenif ...~=false then obj = rebuild(...)else rebuild(...) endendreturn obj and MBi._(self, obj, nil, true) or selfendendlocal lib; MBi = setmetatable({stream = _stream,_inHTML = _inHTML,_var = _var,_ = function(self, v, ...)local at, raw = select(select('#', ...)==1 and ...==true and 1 or 2, nil, ...)if raw then rawkey[self] = math.huge else v = Valid(v) endif v or raw thenif at or rawkey[self] then raw = #self end--if length increases by more than one after table.insert, then set rawkey[self] = math.huge; rawkey[self] may be equal to a previous 'at'at, self.last_concat = at and (tonumber(at)~=at and raw + at or at)table.insert(self, select(at and 1 or 2, at, v))if at and at < 0 or raw and #self - raw > 1 then rawkey[self] = math.huge elseif at and #self==raw then rawkey[self] = rawkey[self] and math.max(rawkey[self], at) or at endend--above line looks bizarre because one table.insert op may make length jump from 0 to 8: local wtf={[2]=2,[4]=4,[8]=8}mw.log(#wtf,table.insert(wtf,1),#wtf)return selfend,_nil = function(self, at, ...)if ...~=true and ...~=false then--faster than type(...) ~= 'boolean'if not at or at=='0' thenself[#self] = ... if ... then rawkey[self] = math.huge endelselocal n, v = tonumber(at), ...if n~=at then if n then n = #self + atelseif at~=true and select('#', ...)==0 then v, n = at, #self endendif n then if v==nil and n > 0 then table.remove(self, n)else self[math.floor(n)], rawkey[self] = v, math.huge end--floor position for consistency with Table libraryendendself.last_concat = nilendreturn selfend,_all = function(self, t, valKey)for k, v in MBpairs(t) do MBmix(self, v, valKey) endfor k, v in valKey and MBpairs(t, false) or noOp, t doif tonumber(v) then MBi._(self, k, v)--self not always a bufferelseif rawget(MBi, k) and v and v~=true thenif v[1]==nil or getmetatable(v) then MBi[k](self, v)else MBi[k](self, unpack(v, 1, table.maxn(v))) endendendreturn selfend,_str = function(t, ...)local n = select('#', ...)if n > 1 thenlocal k, ops, seps, r = 2, MBselect(n, ...)r = MB(t(seps[1]))while parent[t] and ops > 1 and r:_(parent[t](seps[k]), 1) do t, k, ops = parent[t], k + 1, ops - 1 endreturn table.concat(r, seps[k] or nil)endreturn MB.__call(t, ...)end,_in = function (self, ...) return parent(MB(...), self) end,_out = function(t, ...)if ...==0 then return parent(t, parent[t], MBi._cc(t, t, MB.__call(t, (select(2, ...))), getmetatable(t))) end--love how :_cc needed nothing new to implement this *self pat on back*local n = select('#', ...)if n > 1 thenlocal k, ops, seps = 1, MBselect(n, ...)while parent[t] and ops > 0 do t, k, ops = parent[t]:_(t(seps[k])), k + 1, ops - 1 endelseif parent[t] then return parent[t]:_(t(...)) endreturn tend,_cc = function(self, clear, copy, meta)if clear thenif rawequal(clear, copy) then return self, spec[MBi._cc] and setmetatable(spec[MBi._cc], MB)--rawequal to avoid re-string via __eq in case both are different Buffer objectselseif copy==true then copy = self endif clear~=0 thenassert(type(clear)=='table', debug.traceback('Buffer:_cc can only "clear" tables. Did you forget to call with a colon?', 2))--errors can be hard to trace without thisfor k in self and next or noOp, clear do rawset(clear, k, nil) endelse return MBi._cc(false, {unpack(copy)}, copy) end--copy length w/o empty strings; recursion to avoid self = false causing garbage collection (non-weak child may exist)if self==false or copy and type(copy)=='table' then--self==false means copy is a table (saves a type op for recursive calls)meta = meta or getmetatable(copy)if self and #copy > 1 then--preserves length with empty strings; developed from studying http://www.lua.org/source/5.1/ltable.c.htmllocal n, null, i, e = #copy, {}, math.ldexp(2, select(2, math.frexp(#copy)) - 2)e, spec[MBi._cc], parent[null] = i - 1, null, clearfor k = 1, e do table.insert(clear, false) endwhile i<=n do table.insert(clear, i, '') i, null[i] = i + math.ldexp(2, select(2, math.frexp(n - i)) - 2), '' endfor k = 1, e do rawset(clear, k, nil) endendfor k, v in next, copy do rawset(clear, k, type(v)=='table' and MBi._cc(false, 0, v) or v) endelseif copy then rawset(clear, 1, (Valid(copy))) endrawkey[setmetatable(clear, meta)], parent[clear] = rawkey[copy], parent[copy]endreturn self and rawset(self, 'last_concat', nil) or clearend,_parent = function(self, ...) return parent[self] and MBi._(self, parent[self]:_str(...)) or self end,getParent = function(self, ...) return isMBfunc(parent[self] or parent[parent(self, setmetatable({}, MB))], ...) end,killParent = function(self, ...) return parent[self] and isMBfunc(parent[self], ...) and parent(self) or self end,_build = function(self, t) table.insert(t, self()) end,--for compatibility with mw.html:node()last_concat = false--prevent library check}, {__index = function(t, i)--import string, mw.text, and mw.ustring libraries on an as-needed basislocal func = string[i] or mw.text[i] or mw.ustring[i] or type(i)=='string' and mw.ustring[i:match'^u(.+)'] if func thenlib= lib or function (s, f, ...)if parent[s] and next(s)==nil then return s:_((f(tostring(parent[Element and (spec[s]==Element and s:allDone() or spec[parent[s]]==Element and parent[s]) or s]), ...))) endreturn f(tostring(s), ...)--not using ternary/logical operators here to allow multiple return valuesendreturn rawset(t, i, i:match'^u?gsub' and function(self, p, r, ...)return lib(self, func, p, r or '', ...)end--Why are ugsub/gsub special? because empty strings are against my religion!or function(self, ...)return lib(self, func, ...)end)[i]endend})endfunction MBmix(t, v, ...) return v and ((type(v)~='table' or getmetatable(v)) and MBi._(t, v) or (select('#', ...)==0 and spec[t] and spec[t]._add or MBi._all)(t, v, ...)) or t end--:_all always passes two argslocal _G, new_G = _G--localize _G for console testing (console _G ~= module _G)return setmetatable({__index = function(t, i) return spec[t] and spec[t][i] or MBi[i] end,__call = function(t, ...)local rawsep, sep, i, j, raw = noCache and rawkey[...] and ..., ...if i or j or rawsep or Valid(sep) thenraw, sep, i, j = rawkey[spec[t]] or rawkey[t], rawsep or Valid(sep), i and (i~=tonumber(i) and i + #t or i), j and (j~=tonumber(j) and j + #t or j)if rawsep or raw and (raw>=(j or #t) or i < 1) thenraw, i, j = {}, i and math.floor(i), j and math.floor(j)--floor for consistency with table.concat(t, sep, i, j), which ignores decimalsraw.lc, t.last_concat = t.last_concat--temporarily unset last_concat to prevent disqualification from mapless iterationfor k, v in MBpairs(t) doif raw[1] or not i or k>=i then if j and k > j then break endif raw.s then raw.s = table.insert(raw, tostring(sep)) end--if sep contains v and v is a Buffer-variable, sep must be strung before vk = Valid(v) if k thenraw.s = rawsep or sep and raw[1] and table.insert(raw, sep)table.insert(raw, k)endendendif rawsep and not raw.s then raw[#raw] = unbuild(sep) end--unbuild rawsep if final index in t was invalidt.last_concat = raw.lc return table.concat(raw)endreturn table.concat(t, sep, i and math.max(i, 1), j and math.min(j, #t))endreturn MB.__tostring(t)end,__tostring = function(t)if t.last_concat then return t.last_concat endlocal r = rawkey[spec[t]] or rawkey[t]r = table.concat(r and r>=#t and MBi._all({}, t) or t)return (noCache or rawset(t, 'last_concat', r)) and rend,__concat = function(a, b)if buffHTML thenfor k = 1, 2 do local v = select(k, a, b)--faster than for k, v in pairs{a, b} doif v and spec[v] and spec[v]==Element thenif parent[v].selfClosing thenif rawequal(a, b) then return (not noCache or parent[v].tagName) and v:_str(0):rep(2) or v:_str(0)..v:_str(0) end--rawequal avoids premature tostring of Buffer:_var objects;b, a = select(k, b, parent[v], a)else local temp = Element .. v --helper method; returns a mirror of parent[v]MBmix(MBmix(parent(temp.nodes, temp), a), k==1 and spec[b]==Element and parent[b] or b)return buffHTML.__tostring(setmetatable(temp, {__index=parent[v], __mode='v'}))--switch from tempMeta to avoid MBi._cc op of styles/attributesendendendendreturn table.concat(MBmix(MBmix({}, a), b))end,__pairs = MBpairs,__ipairs = MBpairs,__eq = function(a, b) return tostring(a)==tostring(b) end--avoid a==b in this module; use rawequal(a,b) when they may be different Buffers (premature tostring waste ops and is bad for Buffer:_var)}, {__tostring = function()return''end,__call = function(self, ...) MB = MB or selfif new_G then if ... and _G and ...==_G then new_G = ... endelseif ... and (...==_G or type(...)=='table' and (...)._G==...) thenlocal Nil, mG = {}, (...):getmetatable() or (...):setmetatable{}:getmetatable()new_G, _G, gfuncs = ..., ..., {--gfuncs stored for Buffer:_inHTML; new_G is a is a Module:Buffer local declared just before the final return statement._G = function(self, i, ...)local X, save = rawget(new_G, i), select('#', ...)==0 and self or ...if i and i~=true and not (X and save and rawequal(X, save)) and rawset(new_G, i, save) and (X~=nil or save==nil and new_G[i]~=nil) then--rawequal in case X is another bufferlocal mG = getmetatable(new_G) or {__call=mG.__call}if mG.__index then pcall(rawset, mG.__index, i, X)else mG.__index = setmetatable(new_G, mG) and {[i] = X} endendreturn self, ...--avoiding __eq with rawequal(self,save) is overkill since buffers can self-save without being passed as saveend,_R = function(self, i, v, m)if i~='new_G' then if i and i~=true then rawset(new_G, i , v) endelseif not v or v==true or v._G~=_G then new_G = setmetatable(v~=true and v or {}, {__call = mG.__call, __index = v~=true and m~=true and (m or new_G) or nil})else new_G, (not m and (m~=nil or v==new_G) and Nil or getmetatable(v)).__index = v, m~=true and (m or new_G) or nil end--setting Nil.__index is noOpreturn selfend,_2 = function(self, ...)if new_G[...]~=nil then return new_G[...] end--higher priority so Buffer:_G('new_G', ...) can prevent an overwriteif ...=='new_G' then return rawset((select('#', ...)~=1 and MBi._R(new_G, ...) or new_G), '_G', _G) endreturn select(select('#', ...)==1 and 1 or 2, self:_G(...))--return only one value; 'return select(2, self:_G(...)) or self' doesn't work for returning nilend,_B = function(self, v) return v or v==nil and Nil end} for k, v in next, gfuncs do MBi[k] = v end setmetatable(Nil,{__concat=MB.__concat,__newindex=noOp,__call=noOp,__tostring=noOp,__metatable=MB,__index=setmetatable({_B=MBi._B,_=function()return Nil end,last_concat=''},{__index=function(t,i)return (MBi[i] or i and not tonumber(i)) and t._ or nil end})})function mG.__call(G, k, ...) return (k._G or G.type(k)=='table') and (G.select('#', ...)~=1 and G.rawset(k, ...) or G:rawset(..., k) and k) or G:rawset(k, (...)) and ... endendlocal new = setmetatable({}, self)if ... and (...)==new_G then return select(2, ...) and MBmix(new:_G((select(2, ...))), select(3, ...)) or new endreturn ... and MBi._(new, ...) or newend,__index = function(t, i)MB = MB or t return MBi[i] and function(...) return MBi[i](setmetatable({}, t), select(...==t and 2 or 1,...)) endend})