--[[============================= This Module was written by Alexander Zhikun He, also known as, User:Codehydro on the English Wikipedia  All 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 to the 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_manual  Source 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/doc  Licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License  https://en.wikipedia.org/wiki/Module:Buffer https://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 	end end local 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 	end end local 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 memory end  local 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 " does not match any available Module:Buffer function'):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 " does not match any mw.html or Buffer-mw.html function'):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 can only "clear" tables. Did you forget to call with a colon?', 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}) end  function 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 args  local _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()returnend, 	__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 })