Bước tới nội dung

Mô đun:Buffer

Bách khoa toàn thư mở Wikipedia
--[[=============================This Module was written by Alexander Zhikun He, also known as, User:Codehydro on the English WikipediaAll 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://en.wikipedia.org/wiki/Module:Buffer/docLicensed under Creative Commons Attribution-ShareAlike 3.0 Unported Licensehttps://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_Licensehttps://en.wikipedia.org/wiki/Module:Bufferhttps://en.wikipedia.org/wiki/User:Codehydro=============================--]]local function Valid(v)--type validation	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)		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 concat	endendlocal noOp, MBpairs = function()end do local iMap, vMap, oMap, pIter, pOther, pFast, Next--Map	local function init()--init = noOp after first run		function 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 key		function 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 cached		local 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 key	end	function MBpairs(t, ...)--pairs always iterates in order		local iter, ex = ...		iter = iter==init()--nil		if 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 pFast			vMap[t] = #t return pFast, t, nil		elseif ... or not vMap[t] or select('#', ...)~=1 then			local ti, tn, to, n = {}, {}, {}, #t--reduces table lookups			iMap[t], vMap[t], oMap[t] = ti, tn, to			for 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 keys			for k in (ex or Next)(t) do				if not tn[k] then table.insert(tonumber(k)~=k and to or ti, k) end			end			if #ti~=n then				table.sort(ti)				for k = 1, #ti do tn[ti[k]] = k + 1 end--somewhat wasteful, but trying to avoid overwriting can be even more expensive			end			for k = 1, #to do tn[to[k]] = k + 1 end		end		return iter and pIter or oMap[t] and pOther or noOp, t--noOp for mapless	endendlocal 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 functions	parent, 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 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, 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 end			setmetatable(stream, getmetatable(MB))		end		function _stream(self, ...) self.last_concat = init() return setmetatable(self, stream):each(...) end	end	local 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 string				or assert(MB.__index(Buffer, s), ('" %s " không phù hợp với bất kỳ chức năng Mô đun:Buffer có sẵn nào'):format(s))(Buffer, ...)--getParent is a one-way trip so one-time assert not expensive			) or Buffer	end	local function MBselect(n, ...)--helper for :_out and :_str		local n, seps = n - 1, {select(2, ...)}		if type(seps[n])=='table' then 			if buffHTML and rawget(seps[n], buffHTML) then return ... end			setmetatable(seps, {__index = setmetatable(seps[n], {__index = function(t) return rawget(t, 1) end})})[n] = nil		end		return ..., seps	end	local _inHTML do 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(...)=='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])) end					if args then						local a, b = args.selfClosing, args.parent						args.selfClosing, args.parent = nil						if next(args) then Element._add(parent(tag.nodes, tag), args) end						args.selfClosing, args.parent = a, b--in case args is reused					end					return tag				end				for 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] end			end			local nonSelf, BHi = {tag=true,done=true,allDone=true}, buffHTML.__index do local g				g = {__index = function(t, i)					if gfuncs and gfuncs[i] then g.__index, gfuncs = gfuncs return g.__index[i] end				end}				setmetatable(nonSelf, g)				setmetatable(BHi, g)			end			for 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) end			end			do 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', {MB.__call(nodes, sep)})), nodes end					return join or tostring(HTML)				end				for k, v in next, {					getParent = function(HTML, ...) lastHTML = HTML return MBi.getParent(HTML:allDone(), ...) end,--return to Buffer that created the HTML tree					getBuffer = function(HTML, ...) lastHTML = HTML return isMBfunc(lastBuffer, ...) end,--return to last used					killParent = function(HTML, ...) MBi.killParent(HTML:allDone(), ...) return HTML end,					_out = function(HTML, ...)						if ...==0 then MBi._out(HTML.nodes, ...) return HTML end						lastHTML, HTML = HTML, HTML:allDone()						local n, ops, seps = select('#', ...)						if n > 1 then							local ops, seps = MBselect(n, ...)							return parent[HTML]:_in(joinNode(HTML, rawget(seps, 0))):_out(ops, rawset(seps, buffHTML, true))						end						return parent[HTML]:_(joinNode(HTML, ...))					end,					_str = function(HTML, ...)--does not set lastHTML						if ...==0 then return joinNode(HTML, select(2, ...)) end--passing 0 strings without calling allDone()						local HTML, n = HTML:allDone(), select('#', ...)						if n > 1 then							local ops, seps = MBselect(n, ...)							return parent[HTML]:_in(joinNode(HTML, rawget(seps, 1))):_str(ops, rawset(seps, buffHTML, true))						end						return joinNode(HTML, ...)					end,					_parent = function(HTML, ...) table.insert(HTML.nodes, parent[HTML:allDone()]:_str(...)) return HTML end				} do BHi[k] = v end			end			do local htmlArg, skip, outFuncs = {parent=true,selfClosing=true,tagName=true}, {}				do local out local function func(nodes, ...) return out(parent[nodes], ...) end					outFuncs = 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 end							return b						end					}, {__index = function(nodes, i)						if rawget(BHi, i) then out = BHi[i] return func end--rawget to exclude globals					end})				end				Element = {					_add = function(nodes, t)						for k, v in MBpairs(t), t, skip[t] do (v~=true and MBmix or noOp)(nodes, v) end						local HTML = parent[nodes] for k, v in MBpairs(t, false) do							if htmlArg[k] then HTML[k] = v							elseif v and v~=true then								if nonSelf[k] then									if k=='tag' then										if type(v)=='table' then											skip[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 nil											if not k.tagName then k.styles, k.attributes = nil end										else table.insert(nodes, create(v)) end									elseif mwFunc[k] then										if k=='done' and tonumber(v)~=v and v[1] and tonumber(v[1])==v[1] then skip[v] = 1 end										MBmix(outFuncs[k](nodes, skip[v] and v[1]).nodes, v)									elseif v[1] or v[2] then										k = 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] then									if type(v)~='table' or rawget(v, 'nodes') then mwFunc[k](HTML, v)									else										local 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 first										for _, 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 anyhow									end								elseif rawget(Element, k) or rawget(MBi, k) then									if 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 :_all									else (Element[k] or MBi[k])(nodes, unpack(v, 1, table.maxn(v))) end--v is definately a table								else mwFunc.css(HTML, k:gsub('_', '-', 1), tostring(v)) end--oddly enough, :_add clocked its fastest runtime after adding auto-gsub as a feature								skip[v] = nil							end						end						return nodes					end				}				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] end				rawkey[setmetatable(Element, {__index = outFuncs, __concat=function(Element, v) return setmetatable({nodes=spec({}, Element),orig=parent[v]}, tempMeta) end})] = math.huge			end			function MBi:getHTML(...)				lastBuffer = self				if ... then					if select('#', ...)==1 then return not rawkey[s] and tostring(...):match'^_' and BHi[...] and BHi[...](lastHTML) or lastHTML(...)					else return assert(BHi[...], ('" %s " không phù hợp với bất kỳ mw.html hoặc chức năng Buffer-mw.html'):format(tostring(...)))(lastHTML, select(2, ...)) end				end				return lastHTML			end			function MBi:_html(...) return MBi._(self, lastHTML, select(spec[self]==Element and select('#', ...)==0 and 1 or 2, true, ...)) end			return init(...)		end		function _inHTML(self, ...)			local HTML = init(nil, ...)			if HTML.selfClosing and spec[self]==Element then self.last_concat = table.insert(self, HTML) return self end			lastBuffer, lastHTML = self, setmetatable(parent(HTML, self), buffHTML)--set after 'args' table processed by :_add			return HTML		end	end	local _var, unbuild do local prev, rebuild		local function init(...)--init replaced before return			local function pick(b, v) return b and table.insert(b, v) or v end			local 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] end			local 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 contents				number = 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 + c				if t.table then t[0] = (t[0] - 1) % #t[1] + 1 end			end			function rebuild(...)				local v, c = ...				if v or select('#', ...)==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({a = prev, _build = meta.__tostring, c = c}, meta)				elseif v==nil then--no-op				elseif c then shift(prev, c)--v == false				else prev:_build() end			end			init, noCache = function(v, c) prev = setmetatable({v, c = c, _build = build[type(v)] or v, [type(v)] = true, alt = {}}, 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==build or k==alt) then shift(v.a or v, -v.c) end			end		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		end	end	local 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) end			if v or raw then				if 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 end			end--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 self		end,		_nil = function(self, at, ...)			if ...~=true and ...~=false then--faster than type(...) ~= 'boolean'				if not at or at=='0' then					self[#self] = ... if ... then rawkey[self] = math.huge end				else					local n, v = tonumber(at), ...					if n~=at then 						if n then n = #self + at						elseif at~=true and select('#', ...)==0 then v, n = at, #self end					end					if 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 library					end				end				self.last_concat = nil			end			return self		end,		_all = function(self, t, valKey)			for k, v in MBpairs(t) do MBmix(self, v, valKey) end			for k, v in valKey and MBpairs(t, false) or noOp, t do				if tonumber(v) then MBi._(self, k, v)--self not always a buffer				elseif rawget(MBi, k) and v and v~=true then					if v[1]==nil or getmetatable(v) then MBi[k](self, v)					else MBi[k](self, unpack(v, 1, table.maxn(v))) end				end			end			return self		end,		_str = function(t, ...)			local n = select('#', ...)			if n > 1 then				local 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 end				return table.concat(r, seps[k] or nil)			end			return 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 then				local 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 end			elseif parent[t] then return parent[t]:_(t(...)) end			return t		end,		_cc = function(self, clear, copy, meta)			if clear then				if 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 objects				elseif copy==true then copy = self end				if clear~=0 then					assert(type(clear)=='table', debug.traceback('Buffer:_cc chỉ có thể bảng "clear". Bạn đã quên gọi với một dấu hai chấm?', 2))--errors can be hard to trace without this					for k in self and next or noOp, clear do rawset(clear, k, nil) end				else 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.html								local n, null, i, e = #copy, {}, math.ldexp(2, select(2, math.frexp(#copy)) - 2)						e, spec[MBi._cc], parent[null] = i - 1, null, clear						for k = 1, e do table.insert(clear, false) end						while i<=n do table.insert(clear, i, '') i, null[i] = i + math.ldexp(2, select(2, math.frexp(n - i)) - 2), '' end						for k = 1, e do rawset(clear, k, nil) end					end					for k, v in next, copy do rawset(clear, k, type(v)=='table' and MBi._cc(false, 0, v) or v) end				elseif copy then rawset(clear, 1, (Valid(copy))) end				rawkey[setmetatable(clear, meta)], parent[clear] = rawkey[copy], parent[copy]			end			return self and rawset(self, 'last_concat', nil) or clear		end,		_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 basis		local func = string[i] or mw.text[i] or mw.ustring[i] or type(i)=='string' and mw.ustring[i:match'^u(.+)'] if func then			lib	= 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]), ...))) end				return f(tostring(s), ...)--not using ternary/logical operators here to allow multiple return values			end			return 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]		end	end})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) then			raw, 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) then				raw, i, j = {}, i and math.floor(i), j and math.floor(j)--floor for consistency with table.concat(t, sep, i, j), which ignores decimals				raw.lc, t.last_concat = t.last_concat--temporarily unset last_concat to prevent disqualification from mapless iteration				for k, v in MBpairs(t) do					if raw[1] or not i or k>=i then if j and k > j then break end						if 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 v						k = Valid(v) if k then							raw.s = rawsep or sep and raw[1] and table.insert(raw, sep)							table.insert(raw, k)						end					end				end				if rawsep and not raw.s then raw[#raw] = unbuild(sep) end--unbuild rawsep if final index in t was invalid				t.last_concat = raw.lc return table.concat(raw)			end			return table.concat(t, sep, i and math.max(i, 1), j and math.min(j, #t))		end		return MB.__tostring(t)	end,	__tostring = function(t)		if t.last_concat then return t.last_concat end		local 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 r	end,	__concat = function(a, b)		if buffHTML then			for k = 1, 2 do local v = select(k, a, b)--faster than for k, v in pairs{a, b} do				if v and spec[v] and spec[v]==Element then					if parent[v].selfClosing then						if 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/attributes					end				end			end		end		return 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 self		if new_G then if ... and _G and ...==_G then new_G = ... end		elseif ... and (...==_G or type(...)=='table' and (...)._G==...) then			local 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 buffer						local 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} end					end					return self, ...--avoiding __eq with rawequal(self,save) is overkill since buffers can self-save without being passed as save				end,				_R = function(self, i, v, m)					if i~='new_G' then if i and i~=true then rawset(new_G, i , v) end					elseif 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 noOp					return self				end,				_2 = function(self, ...)					if new_G[...]~=nil then return new_G[...] end--higher priority so Buffer:_G('new_G', ...) can prevent an overwrite					if ...=='new_G' then return rawset((select('#', ...)~=1 and MBi._R(new_G, ...) or new_G), '_G', _G) end					return 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 nil				end,				_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 ... end		end		local new = setmetatable({}, self)		if ... and (...)==new_G then return select(2, ...) and MBmix(new:_G((select(2, ...))), select(3, ...)) or new end		return ... and MBi._(new, ...) or new	end,	__index = function(t, i)		MB = MB or t return MBi[i] and function(...) return MBi[i](setmetatable({}, t), select(...==t and 2 or 1,...)) end	end})