Jump to content

Module:Buffer

Ón Vicipéid, an chiclipéid shaor.

Documentation for this module may be created at Module:Buffer/doc

--[[=============================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=============================--]]localfunctionValid(v)--type validationifvandv~=truethen--reject nil/boolean; faster than 2 type() comparisonslocalstr=tostring(v)--functions not filtered since unlikely passed by accident (Scribunto does not have userdata/thread types)ifstr~=vandstr=='table'thenreturnrawget(v,1)andtable.concat(v)end--tostring(string-type) returns same ref; same refs compare faster than type()ifstr~=''thenreturnstrend--numbers are coerced to string per table.concat op; appending in string form saves ops on repeat concatendendlocalnoOp,MBpairs=function()enddolocaliMap,vMap,oMap,pIter,pOther,pFast,Next--Maplocalfunctioninit()--init = noOp after first runfunctionNext(t)returnnext,tend--slightly faster to do this than to use select()functionpIter(t,k)k=(iMap[t]orMBpairs(t,true)andiMap[t])[notkand1orvMap[t][k]]returnk,t[k]end--don't use rawget; accepting unmapped tables does not measurably affect performance.functionpOther(t,k)k=(oMap[t]orMBpairs(t,true)andoMap[t])[nil==kand1orvMap[t][k]]returnk,t[k]end--comparison to nil because false is a valid keyfunctionpFast(t,k)k=notkand1ork<(vMap[t]or#t)andk+1ornilreturnk,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 cachedlocalmk={__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 keyendfunctionMBpairs(t,...)--pairs always iterates in orderlocaliter,ex=...iter=iter==init()--nilifiterandnotoMap[t]andex==nilandrawget(t,1)~=nilandnext(t,#t)==nilthen--while possible to miss keys, more thorough check would negate the benefit of pFastvMap[t]=#treturnpFast,t,nilelseif...ornotvMap[t]orselect('#',...)~=1thenlocalti,tn,to,n={},{},{},#t--reduces table lookupsiMap[t],vMap[t],oMap[t]=ti,tn,tofork=1,ndoti[k],tn[k]=k,k+1end--stage one avoids number type checking op in stage two for most numeric keysforkin(exorNext)(t)doifnottn[k]thentable.insert(tonumber(k)~=kandtoorti,k)endendif#ti~=nthentable.sort(ti)fork=1,#tidotn[ti[k]]=k+1end--somewhat wasteful, but trying to avoid overwriting can be even more expensiveendfork=1,#todotn[to[k]]=k+1endendreturniterandpIteroroMap[t]andpOtherornoOp,t--noOp for maplessendendlocalparent,rawkey,specdo--new scope for variables not reused outside (reduces number of var names that need to checked outside of scope)localmkv={__mode='kv',__call=function(t,k,v)t[k]=vreturnkend}--shared meta for Buffer parent property, raw mode, and specialized functionsparent,rawkey,spec=setmetatable({},mkv),setmetatable({},mkv),setmetatable({},mkv)--shared meta less memoryendlocalMB,MBi,MBmix,buffHTML,gfuncs,noCache,Elementdo--minimize number of locals per scope to reduce time spent sifting through irrelevant variable nameslocal_streamdolocalstream--keep stream near top of scopelocalfunctioninit(f)--init = noOp after first runlocalfunctioneach(self,...)fork=1,select('#',...)dok=Valid(select(k,...))--slightly faster than table.insert(self, (Valid(select(k, ...))))ifkthentable.insert(self,k)endendreturnselfendinit,stream,_stream=noOp,{__call=function(t,v)v=vandValid(v)returnvandtable.insert(t,v)ortend,--last_concat cleared before entering stream mode__index=function(t,i)returni=='each'andeachorMB.__index(t,i)andsetmetatable(t,MB)[i]end,--no table look up minimizes resources to retrieve the only stream function__tostring=function(t)returnsetmetatable(t,MB)()end}fork,vinnext,MBdostream[k]=stream[k]orvendsetmetatable(stream,getmetatable(MB))endfunction_stream(self,...)self.last_concat=init()returnsetmetatable(self,stream):each(...)endendlocalfunctionisMBfunc(Buffer,s,...)--helper for :getParent()-like methods (including getBuffer which does not return a parent)returnsand(select('#',...)==0and--eventually should figure out to make this work for :getHTML which is very similar(notrawkey[s]andtostring(s):match'^_.*'andMB.__index(Buffer,s)andMB.__index(Buffer,s)(Buffer)orMBmix(Buffer,s))--unprefixed function names append as a stringorassert(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)orBufferendlocalfunctionMBselect(n,...)--helper for :_out and :_strlocaln,seps=n-1,{select(2,...)}iftype(seps[n])=='table'thenifbuffHTMLandrawget(seps[n],buffHTML)thenreturn...endsetmetatable(seps,{__index=setmetatable(seps[n],{__index=function(t)returnrawget(t,1)end})})[n]=nilendreturn...,sepsendlocal_inHTMLdolocallastBuffer,lastHTMLlocalfunctioninit(...)--init replaced and new version called on returnlocalcreate,mwFunc=mw.html.createdolocalmwHTMLmeta=getmetatable(create())buffHTML,mwFunc,_inHTML=setmetatable(mw.clone(mwHTMLmeta),getmetatable(MB)),mwHTMLmeta.__index--buffHTML declared near top of module; remove _inHTML from outer scopefunctioninit(nodes,...)localname,args,tag=select(...andtype(...)=='table'and1or2,nil,...)tag=create(Valid(name),args)ifnodesthentable.insert(nodes,tag.parentandtagorrawset(tag,'parent',parent[nodes]))endifargsthenlocala,b=args.selfClosing,args.parentargs.selfClosing,args.parent=nilifnext(args)thenElement._add(parent(tag.nodes,tag),args)endargs.selfClosing,args.parent=a,b--in case args is reusedendreturntagendfork,vinnext,{[mw]=mwHTMLmeta,__call=function(h,v)returnMBmix(spec[h.nodes]andh.nodesorspec(setmetatable(parent(h.nodes,h),MB),Element),v)end,__concat=false,--false means take from MB__eq=false}dobuffHTML[k]=vorMB[k]endendlocalnonSelf,BHi={tag=true,done=true,allDone=true},buffHTML.__indexdolocalgg={__index=function(t,i)ifgfuncsandgfuncs[i]theng.__index,gfuncs=gfuncsreturng.__index[i]endend}setmetatable(nonSelf,g)setmetatable(BHi,g)endforkinnext,nonSelfdo--any HTML objects returned by these funcs will be granted Module:Buffer enhancementslocalfunc=mwFunc[k]BHi[k]=function(t,...)localHTML=func(t,...)returnparent[HTML]andHTMLorsetmetatable(parent(HTML,t),buffHTML)endenddolocalfunctionjoinNode(HTML,sep)localnodes,join=HTML.nodesifnoCacheandrawkey[sep]orValid(sep)thenjoin,HTML.nodes=tostring(rawset(HTML,'nodes',{MB.__call(nodes,sep)})),nodesendreturnjoinortostring(HTML)endfork,vinnext,{getParent=function(HTML,...)lastHTML=HTMLreturnMBi.getParent(HTML:allDone(),...)end,--return to Buffer that created the HTML treegetBuffer=function(HTML,...)lastHTML=HTMLreturnisMBfunc(lastBuffer,...)end,--return to last usedkillParent=function(HTML,...)MBi.killParent(HTML:allDone(),...)returnHTMLend,_out=function(HTML,...)if...==0thenMBi._out(HTML.nodes,...)returnHTMLendlastHTML,HTML=HTML,HTML:allDone()localn,ops,seps=select('#',...)ifn>1thenlocalops,seps=MBselect(n,...)returnparent[HTML]:_in(joinNode(HTML,rawget(seps,0))):_out(ops,rawset(seps,buffHTML,true))endreturnparent[HTML]:_(joinNode(HTML,...))end,_str=function(HTML,...)--does not set lastHTMLif...==0thenreturnjoinNode(HTML,select(2,...))end--passing 0 strings without calling allDone()localHTML,n=HTML:allDone(),select('#',...)ifn>1thenlocalops,seps=MBselect(n,...)returnparent[HTML]:_in(joinNode(HTML,rawget(seps,1))):_str(ops,rawset(seps,buffHTML,true))endreturnjoinNode(HTML,...)end,_parent=function(HTML,...)table.insert(HTML.nodes,parent[HTML:allDone()]:_str(...))returnHTMLend}doBHi[k]=vendenddolocalhtmlArg,skip,outFuncs={parent=true,selfClosing=true,tagName=true},{}dolocaloutlocalfunctionfunc(nodes,...)returnout(parent[nodes],...)endoutFuncs=setmetatable({tag=function(nodes,...)returnparent(setmetatable(init(nodes,...),buffHTML),parent[nodes])end,done=function(b,ops)b=parent[b]whileb.parentandops~=0dob,ops=b.parent,opsandops-1or0endreturnbend},{__index=function(nodes,i)ifrawget(BHi,i)thenout=BHi[i]returnfuncend--rawget to exclude globalsend})endElement={_add=function(nodes,t)fork,vinMBpairs(t),t,skip[t]do(v~=trueandMBmixornoOp)(nodes,v)endlocalHTML=parent[nodes]fork,vinMBpairs(t,false)doifhtmlArg[k]thenHTML[k]=velseifvandv~=truethenifnonSelf[k]thenifk=='tag'theniftype(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)ifk.selfClosingthenk.nodes=nilelsespec[k.nodes],parent[k.nodes]=nilend--free memory/reduce clutter; parent ref will auto-unset when k.nodes is nilifnotk.tagNamethenk.styles,k.attributes=nilendelsetable.insert(nodes,create(v))endelseifmwFunc[k]thenifk=='done'andtonumber(v)~=vandv[1]andtonumber(v[1])==v[1]thenskip[v]=1endMBmix(outFuncs[k](nodes,skip[v]andv[1]).nodes,v)elseifv[1]orv[2]thenk=MBi[k](nodes,unpack(v,1,rawset(skip,v,k=='_B'and1or2)[v]))Element._add(getmetatable(k)andrawget(k,'nodes')ork,v)--if k is not a table, then v should not contain any extra keys or this may error.elseMBi[k](nodes,v)end--k probably == '_G' or '_R'elseifmwFunc[k]theniftype(v)~='table'orrawget(v,'nodes')thenmwFunc[k](HTML,v)elselocalcss=k=='css'forx,yinMBpairs(v,true)do(yandy~=trueandmwFunc[k]ornoOp)(HTML,cssandx:gsub('_','-')orx,y)end--iterate non-numbers firstfor_,yinMBpairs(v,nil)do(yandy~=trueandmwFunc[k]ornoOp)(HTML,y)end--don't bother with gsub since text must be quoted anyhowendelseifrawget(Element,k)orrawget(MBi,k)theniftonumber(v)==vorv[1]==nilorgetmetatable(v)then(Element[k]orMBi[k])(nodes,v)--v is probably string-able object, or a table to be handled by :_allelse(Element[k]orMBi[k])(nodes,unpack(v,1,table.maxn(v)))end--v is definately a tableelsemwFunc.css(HTML,k:gsub('_','-',1),tostring(v))end--oddly enough, :_add clocked its fastest runtime after adding auto-gsub as a featureskip[v]=nilendendreturnnodesend}localtempMeta={mode='v',copy={styles=true,attributes=true}}functiontempMeta.__index(t,i)returntempMeta.copy[i]andrawset(t,i,MBi._cc(false,0,t.orig[i]))[i]ort.orig[i]endrawkey[setmetatable(Element,{__index=outFuncs,__concat=function(Element,v)returnsetmetatable({nodes=spec({},Element),orig=parent[v]},tempMeta)end})]=math.hugeendfunctionMBi:getHTML(...)lastBuffer=selfif...thenifselect('#',...)==1thenreturnnotrawkey[s]andtostring(...):match'^_'andBHi[...]andBHi[...](lastHTML)orlastHTML(...)elsereturnassert(BHi[...],('" %s " does not match any mw.html or Buffer-mw.html function'):format(tostring(...)))(lastHTML,select(2,...))endendreturnlastHTMLendfunctionMBi:_html(...)returnMBi._(self,lastHTML,select(spec[self]==Elementandselect('#',...)==0and1or2,true,...))endreturninit(...)endfunction_inHTML(self,...)localHTML=init(nil,...)ifHTML.selfClosingandspec[self]==Elementthenself.last_concat=table.insert(self,HTML)returnselfendlastBuffer,lastHTML=self,setmetatable(parent(HTML,self),buffHTML)--set after 'args' table processed by :_addreturnHTMLendendlocal_var,unbuilddolocalprev,rebuildlocalfunctioninit(...)--init replaced before returnlocalfunctionpick(b,v)returnbandtable.insert(b,v)orvendlocalfunctionc(a,num)returnrawset(a.aora,0,a[0]anda[0]+a.cornumanda[1]ora[1]:byte())[0]endlocalsame,build,alt={__tostring=function(a,b)returna.a[0]andpick(b,a.a.stringandstring.char(a.a[0])ora.a.tableanda.a[1][a.a[0]]ora.a[0])end},{__index={c=1},__tostring=function(t)returnt:_build()end,table=function(a,b)locali=next(a[1],a[0])ora[0]==#a[1]andnext(a[1])returnpick(b,rawset(a.aora,0,i)[1][i])end,--change rate (a.c) ignored since users control the table's contentsnumber=function(a,b)returnpick(b,c(a,true))end,string=function(a,b)returnpick(b,string.char(c(a)))end},{__index=function(a,i)returna.a[i]end,__tostring=function(a,b)return(rawget(a,0)anda[0]==tostring(a[0])andrawset(a,0,a[0]:byte())ora).a._build(a,b)end}localfunctionshift(t,c)t[0]=t[0]andt[0]+cort:_build()andt[0]-t.c+cift.tablethent[0]=(t[0]-1)%#t[1]+1endendfunctionrebuild(...)localv,c=...ifvorselect('#',...)==0thenifvandnotcthenreturnprevendlocalmeta,c=select(vand1or3,alt,c,same,0)returnsetmetatable({a=prev,_build=meta.__tostring,c=c},meta)elseifv==nilthen--no-opelseifcthenshift(prev,c)--v == falseelseprev:_build()endendinit,noCache=function(v,c)prev=setmetatable({v,c=c,_build=build[type(v)]orv,[type(v)]=true,alt={}},build)returnprevend,truereturninit(...)endfunctionunbuild(sep)fork,vinMBpairs(sep,nil)dok=getmetatable(v)ifkand(k==buildork==alt)thenshift(v.aorv,-v.c)endendendfunction_var(self,...)localobjif...and...~=truethenobj=init(...)elseifprevthenif...~=falsethenobj=rebuild(...)elserebuild(...)endendreturnobjandMBi._(self,obj,nil,true)orselfendendlocallib;MBi=setmetatable({stream=_stream,_inHTML=_inHTML,_var=_var,_=function(self,v,...)localat,raw=select(select('#',...)==1and...==trueand1or2,nil,...)ifrawthenrawkey[self]=math.hugeelsev=Valid(v)endifvorrawthenifatorrawkey[self]thenraw=#selfend--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=atand(tonumber(at)~=atandraw+atorat)table.insert(self,select(atand1or2,at,v))ifatandat<0orrawand#self-raw>1thenrawkey[self]=math.hugeelseifatand#self==rawthenrawkey[self]=rawkey[self]andmath.max(rawkey[self],at)oratendend--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)returnselfend,_nil=function(self,at,...)if...~=trueand...~=falsethen--faster than type(...) ~= 'boolean'ifnotatorat=='0'thenself[#self]=...if...thenrawkey[self]=math.hugeendelselocaln,v=tonumber(at),...ifn~=atthenifnthenn=#self+atelseifat~=trueandselect('#',...)==0thenv,n=at,#selfendendifnthenifv==nilandn>0thentable.remove(self,n)elseself[math.floor(n)],rawkey[self]=v,math.hugeend--floor position for consistency with Table libraryendendself.last_concat=nilendreturnselfend,_all=function(self,t,valKey)fork,vinMBpairs(t)doMBmix(self,v,valKey)endfork,vinvalKeyandMBpairs(t,false)ornoOp,tdoiftonumber(v)thenMBi._(self,k,v)--self not always a bufferelseifrawget(MBi,k)andvandv~=truethenifv[1]==nilorgetmetatable(v)thenMBi[k](self,v)elseMBi[k](self,unpack(v,1,table.maxn(v)))endendendreturnselfend,_str=function(t,...)localn=select('#',...)ifn>1thenlocalk,ops,seps,r=2,MBselect(n,...)r=MB(t(seps[1]))whileparent[t]andops>1andr:_(parent[t](seps[k]),1)dot,k,ops=parent[t],k+1,ops-1endreturntable.concat(r,seps[k]ornil)endreturnMB.__call(t,...)end,_in=function(self,...)returnparent(MB(...),self)end,_out=function(t,...)if...==0thenreturnparent(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*localn=select('#',...)ifn>1thenlocalk,ops,seps=1,MBselect(n,...)whileparent[t]andops>0dot,k,ops=parent[t]:_(t(seps[k])),k+1,ops-1endelseifparent[t]thenreturnparent[t]:_(t(...))endreturntend,_cc=function(self,clear,copy,meta)ifclearthenifrawequal(clear,copy)thenreturnself,spec[MBi._cc]andsetmetatable(spec[MBi._cc],MB)--rawequal to avoid re-string via __eq in case both are different Buffer objectselseifcopy==truethencopy=selfendifclear~=0thenassert(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 thisforkinselfandnextornoOp,cleardorawset(clear,k,nil)endelsereturnMBi._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)ifself==falseorcopyandtype(copy)=='table'then--self==false means copy is a table (saves a type op for recursive calls)meta=metaorgetmetatable(copy)ifselfand#copy>1then--preserves length with empty strings; developed from studying http://www.lua.org/source/5.1/ltable.c.html localn,null,i,e=#copy,{},math.ldexp(2,select(2,math.frexp(#copy))-2)e,spec[MBi._cc],parent[null]=i-1,null,clearfork=1,edotable.insert(clear,false)endwhilei<=ndotable.insert(clear,i,'')i,null[i]=i+math.ldexp(2,select(2,math.frexp(n-i))-2),''endfork=1,edorawset(clear,k,nil)endendfork,vinnext,copydorawset(clear,k,type(v)=='table'andMBi._cc(false,0,v)orv)endelseifcopythenrawset(clear,1,(Valid(copy)))endrawkey[setmetatable(clear,meta)],parent[clear]=rawkey[copy],parent[copy]endreturnselfandrawset(self,'last_concat',nil)orclearend,_parent=function(self,...)returnparent[self]andMBi._(self,parent[self]:_str(...))orselfend,getParent=function(self,...)returnisMBfunc(parent[self]orparent[parent(self,setmetatable({},MB))],...)end,killParent=function(self,...)returnparent[self]andisMBfunc(parent[self],...)andparent(self)orselfend,_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 basislocalfunc=string[i]ormw.text[i]ormw.ustring[i]ortype(i)=='string'andmw.ustring[i:match'^u(.+)']iffuncthenlib=liborfunction(s,f,...)ifparent[s]andnext(s)==nilthenreturns:_((f(tostring(parent[Elementand(spec[s]==Elementands:allDone()orspec[parent[s]]==Elementandparent[s])ors]),...)))endreturnf(tostring(s),...)--not using ternary/logical operators here to allow multiple return valuesendreturnrawset(t,i,i:match'^u?gsub'andfunction(self,p,r,...)returnlib(self,func,p,ror'',...)end--Why are ugsub/gsub special? because empty strings are against my religion!orfunction(self,...)returnlib(self,func,...)end)[i]endend})endfunctionMBmix(t,v,...)returnvand((type(v)~='table'orgetmetatable(v))andMBi._(t,v)or(select('#',...)==0andspec[t]andspec[t]._addorMBi._all)(t,v,...))ortend--:_all always passes two argslocal_G,new_G=_G--localize _G for console testing (console _G ~= module _G)returnsetmetatable({__index=function(t,i)returnspec[t]andspec[t][i]orMBi[i]end,__call=function(t,...)localrawsep,sep,i,j,raw=noCacheandrawkey[...]and...,...ifiorjorrawseporValid(sep)thenraw,sep,i,j=rawkey[spec[t]]orrawkey[t],rawseporValid(sep),iand(i~=tonumber(i)andi+#tori),jand(j~=tonumber(j)andj+#torj)ifrawseporrawand(raw>=(jor#t)ori<1)thenraw,i,j={},iandmath.floor(i),jandmath.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 iterationfork,vinMBpairs(t)doifraw[1]ornotiork>=ithenifjandk>jthenbreakendifraw.sthenraw.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)ifkthenraw.s=rawseporsepandraw[1]andtable.insert(raw,sep)table.insert(raw,k)endendendifrawsepandnotraw.sthenraw[#raw]=unbuild(sep)end--unbuild rawsep if final index in t was invalidt.last_concat=raw.lcreturntable.concat(raw)endreturntable.concat(t,sep,iandmath.max(i,1),jandmath.min(j,#t))endreturnMB.__tostring(t)end,__tostring=function(t)ift.last_concatthenreturnt.last_concatendlocalr=rawkey[spec[t]]orrawkey[t]r=table.concat(randr>=#tandMBi._all({},t)ort)return(noCacheorrawset(t,'last_concat',r))andrend,__concat=function(a,b)ifbuffHTMLthenfork=1,2dolocalv=select(k,a,b)--faster than for k, v in pairs{a, b} doifvandspec[v]andspec[v]==Elementthenifparent[v].selfClosingthenifrawequal(a,b)thenreturn(notnoCacheorparent[v].tagName)andv:_str(0):rep(2)orv:_str(0)..v:_str(0)end--rawequal avoids premature tostring of Buffer:_var objects;b,a=select(k,b,parent[v],a)elselocaltemp=Element..v--helper method; returns a mirror of parent[v]MBmix(MBmix(parent(temp.nodes,temp),a),k==1andspec[b]==Elementandparent[b]orb)returnbuffHTML.__tostring(setmetatable(temp,{__index=parent[v],__mode='v'}))--switch from tempMeta to avoid MBi._cc op of styles/attributesendendendendreturntable.concat(MBmix(MBmix({},a),b))end,__pairs=MBpairs,__ipairs=MBpairs,__eq=function(a,b)returntostring(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=MBorselfifnew_Gthenif...and_Gand...==_Gthennew_G=...endelseif...and(...==_Gortype(...)=='table'and(...)._G==...)thenlocalNil,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,...)localX,save=rawget(new_G,i),select('#',...)==0andselfor...ifiandi~=trueandnot(Xandsaveandrawequal(X,save))andrawset(new_G,i,save)and(X~=nilorsave==nilandnew_G[i]~=nil)then--rawequal in case X is another bufferlocalmG=getmetatable(new_G)or{__call=mG.__call}ifmG.__indexthenpcall(rawset,mG.__index,i,X)elsemG.__index=setmetatable(new_G,mG)and{[i]=X}endendreturnself,...--avoiding __eq with rawequal(self,save) is overkill since buffers can self-save without being passed as saveend,_R=function(self,i,v,m)ifi~='new_G'thenifiandi~=truethenrawset(new_G,i,v)endelseifnotvorv==trueorv._G~=_Gthennew_G=setmetatable(v~=trueandvor{},{__call=mG.__call,__index=v~=trueandm~=trueand(mornew_G)ornil})elsenew_G,(notmand(m~=nilorv==new_G)andNilorgetmetatable(v)).__index=v,m~=trueand(mornew_G)ornilend--setting Nil.__index is noOpreturnselfend,_2=function(self,...)ifnew_G[...]~=nilthenreturnnew_G[...]end--higher priority so Buffer:_G('new_G', ...) can prevent an overwriteif...=='new_G'thenreturnrawset((select('#',...)~=1andMBi._R(new_G,...)ornew_G),'_G',_G)endreturnselect(select('#',...)==1and1or2,self:_G(...))--return only one value; 'return select(2, self:_G(...)) or self' doesn't work for returning nilend,_B=function(self,v)returnvorv==nilandNilend}fork,vinnext,gfuncsdoMBi[k]=vendsetmetatable(Nil,{__concat=MB.__concat,__newindex=noOp,__call=noOp,__tostring=noOp,__metatable=MB,__index=setmetatable({_B=MBi._B,_=function()returnNilend,last_concat=''},{__index=function(t,i)return(MBi[i]oriandnottonumber(i))andt._ornilend})})functionmG.__call(G,k,...)return(k._GorG.type(k)=='table')and(G.select('#',...)~=1andG.rawset(k,...)orG:rawset(...,k)andk)orG:rawset(k,(...))and...endendlocalnew=setmetatable({},self)if...and(...)==new_Gthenreturnselect(2,...)andMBmix(new:_G((select(2,...))),select(3,...))ornewendreturn...andMBi._(new,...)ornewend,__index=function(t,i)MB=MBortreturnMBi[i]andfunction(...)returnMBi[i](setmetatable({},t),select(...==tand2or1,...))endend})
close