| Modul ini dinilai siap digunakan secara umum. Modul ini sudah matang dan diperkirakan bebas dari bug dan siap digunakan pada tempat yang tepat di manapun. Modul ini siap disinggung di halaman-halaman bantuan dan sumber informasi Wikipedia lainnya sebagai pilihan untuk dipelajari para pengguna baru. Untuk mengurangi beban server dan output yang tidak diharapkan, modul ini harus diperlengkapi dengan halaman bak pasir dan kasus uji daripada harus melakukan suntingan coba-dan-salah berulang-ulang. |
| Modul Lua ini digunakan pada sekitar 411.000 halaman, atau sekira 10% dari keseluruhan halaman. Untuk mencegah suntingan mengganggu skala besar dan beban peladen, setiap perubahan harus diuji di subhalaman /bak pasir atau /kasus uji Modul:Buffer, atau bak pasir modul Anda. Perubahan yang telah diuji kemudian dapat ditambahkan ke halaman ini dalam satu suntingan. Pertimbangkan untuk mendiskusikan perubahan di halaman pembicaraan sebelum mengimplementasikannya. |
| Modul ini dilindungi. Modul ini sangat mencolok yang digunakan oleh banyak halaman, atau sangat sering disubstitusikan. Karena vandalisme atau kesalahan akan mempengaruhi banyak halaman, dan suntingan kecil dapat memberi beban besar pada server, modul ini dilindungi dari penyuntingan. |
Dokumentasi di atas ditransklusikan dari Modul:Buffer/doc. (sunting | riwayat)
Penyunting dapat melakukan uji coba pada halaman bak pasir (buat | cermin) dan kasus uji (buat) modul ini.
Subhalaman modul ini.
Penyunting dapat melakukan uji coba pada halaman bak pasir (buat | cermin) dan kasus uji (buat) modul ini.
Subhalaman modul ini.
--[[============================= 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 =============================--]] localfunctionValid(v)--type validation ifvandv~=truethen--reject nil/boolean; faster than 2 type() comparisons localstr=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 concat end end localnoOp,MBpairs=function()enddolocaliMap,vMap,oMap,pIter,pOther,pFast,Next--Map localfunctioninit()--init = noOp after first run functionNext(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 key functionpFast(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 cached localmk={__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 functionMBpairs(t,...)--pairs always iterates in order localiter,ex=... iter=iter==init()--nil ifiterandnotoMap[t]andex==nilandrawget(t,1)~=nilandnext(t,#t)==nilthen--while possible to miss keys, more thorough check would negate the benefit of pFast vMap[t]=#treturnpFast,t,nil elseif...ornotvMap[t]orselect('#',...)~=1then localti,tn,to,n={},{},{},#t--reduces table lookups iMap[t],vMap[t],oMap[t]=ti,tn,to fork=1,ndoti[k],tn[k]=k,k+1end--stage one avoids number type checking op in stage two for most numeric keys forkin(exorNext)(t)do ifnottn[k]thentable.insert(tonumber(k)~=kandtoorti,k)end end if#ti~=nthen table.sort(ti) fork=1,#tidotn[ti[k]]=k+1end--somewhat wasteful, but trying to avoid overwriting can be even more expensive end fork=1,#todotn[to[k]]=k+1end end returniterandpIteroroMap[t]andpOtherornoOp,t--noOp for mapless end end localparent,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 functions parent,rawkey,spec=setmetatable({},mkv),setmetatable({},mkv),setmetatable({},mkv)--shared meta less memory end localMB,MBi,MBmix,buffHTML,gfuncs,noCache,Elementdo--minimize number of locals per scope to reduce time spent sifting through irrelevant variable names local_streamdolocalstream--keep stream near top of scope localfunctioninit(f)--init = noOp after first run localfunctioneach(self,...) fork=1,select('#',...)do k=Valid(select(k,...))--slightly faster than table.insert(self, (Valid(select(k, ...)))) ifkthentable.insert(self,k)end end returnself end init,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]orvend setmetatable(stream,getmetatable(MB)) end function_stream(self,...)self.last_concat=init()returnsetmetatable(self,stream):each(...)end end localfunctionisMBfunc(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 string orassert(MB.__index(Buffer,s),('" %s " tidak ditemukan/tersedia pada fungsi Modul:Buffer'):format(s))(Buffer,...)--getParent is a one-way trip so one-time assert not expensive )orBuffer end localfunctionMBselect(n,...)--helper for :_out and :_str localn,seps=n-1,{select(2,...)} iftype(seps[n])=='table'then ifbuffHTMLandrawget(seps[n],buffHTML)thenreturn...end setmetatable(seps,{__index=setmetatable(seps[n],{__index=function(t)returnrawget(t,1)end})})[n]=nil end return...,seps end local_inHTMLdolocallastBuffer,lastHTML localfunctioninit(...)--init replaced and new version called on return localcreate,mwFunc=mw.html.createdo localmwHTMLmeta=getmetatable(create()) buffHTML,mwFunc,_inHTML=setmetatable(mw.clone(mwHTMLmeta),getmetatable(MB)),mwHTMLmeta.__index--buffHTML declared near top of module; remove _inHTML from outer scope functioninit(nodes,...) localname,args,tag=select(...andtype(...)=='table'and1or2,nil,...) tag=create(Valid(name),args) ifnodesthentable.insert(nodes,tag.parentandtagorrawset(tag,'parent',parent[nodes]))end ifargsthen locala,b=args.selfClosing,args.parent args.selfClosing,args.parent=nil ifnext(args)thenElement._add(parent(tag.nodes,tag),args)end args.selfClosing,args.parent=a,b--in case args is reused end returntag end fork,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]end end localnonSelf,BHi={tag=true,done=true,allDone=true},buffHTML.__indexdolocalg g={__index=function(t,i) ifgfuncsandgfuncs[i]theng.__index,gfuncs=gfuncsreturng.__index[i]end end} setmetatable(nonSelf,g) setmetatable(BHi,g) end forkinnext,nonSelfdo--any HTML objects returned by these funcs will be granted Module:Buffer enhancements localfunc=mwFunc[k] BHi[k]=function(t,...)localHTML=func(t,...)returnparent[HTML]andHTMLorsetmetatable(parent(HTML,t),buffHTML)end end dolocalfunctionjoinNode(HTML,sep) localnodes,join=HTML.nodes ifnoCacheandrawkey[sep]orValid(sep)thenjoin,HTML.nodes=tostring(rawset(HTML,'nodes',{MB.__call(nodes,sep)})),nodesend returnjoinortostring(HTML) end fork,vinnext,{ getParent=function(HTML,...)lastHTML=HTMLreturnMBi.getParent(HTML:allDone(),...)end,--return to Buffer that created the HTML tree getBuffer=function(HTML,...)lastHTML=HTMLreturnisMBfunc(lastBuffer,...)end,--return to last used killParent=function(HTML,...)MBi.killParent(HTML:allDone(),...)returnHTMLend, _out=function(HTML,...) if...==0thenMBi._out(HTML.nodes,...)returnHTMLend lastHTML,HTML=HTML,HTML:allDone() localn,ops,seps=select('#',...) ifn>1then localops,seps=MBselect(n,...) returnparent[HTML]:_in(joinNode(HTML,rawget(seps,0))):_out(ops,rawset(seps,buffHTML,true)) end returnparent[HTML]:_(joinNode(HTML,...)) end, _str=function(HTML,...)--does not set lastHTML if...==0thenreturnjoinNode(HTML,select(2,...))end--passing 0 strings without calling allDone() localHTML,n=HTML:allDone(),select('#',...) ifn>1then localops,seps=MBselect(n,...) returnparent[HTML]:_in(joinNode(HTML,rawget(seps,1))):_str(ops,rawset(seps,buffHTML,true)) end returnjoinNode(HTML,...) end, _parent=function(HTML,...)table.insert(HTML.nodes,parent[HTML:allDone()]:_str(...))returnHTMLend }doBHi[k]=vend end dolocalhtmlArg,skip,outFuncs={parent=true,selfClosing=true,tagName=true},{} dolocaloutlocalfunctionfunc(nodes,...)returnout(parent[nodes],...)end outFuncs=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-1or0end returnb end },{__index=function(nodes,i) ifrawget(BHi,i)thenout=BHi[i]returnfuncend--rawget to exclude globals end}) end Element={ _add=function(nodes,t) fork,vinMBpairs(t),t,skip[t]do(v~=trueandMBmixornoOp)(nodes,v)end localHTML=parent[nodes]fork,vinMBpairs(t,false)do ifhtmlArg[k]thenHTML[k]=v elseifvandv~=truethen ifnonSelf[k]then ifk=='tag'then iftype(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) ifk.selfClosingthenk.nodes=nilelsespec[k.nodes],parent[k.nodes]=nilend--free memory/reduce clutter; parent ref will auto-unset when k.nodes is nil ifnotk.tagNamethenk.styles,k.attributes=nilend elsetable.insert(nodes,create(v))end elseifmwFunc[k]then ifk=='done'andtonumber(v)~=vandv[1]andtonumber(v[1])==v[1]thenskip[v]=1end MBmix(outFuncs[k](nodes,skip[v]andv[1]).nodes,v) elseifv[1]orv[2]then k=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]then iftype(v)~='table'orrawget(v,'nodes')thenmwFunc[k](HTML,v) else localcss=k=='css' forx,yinMBpairs(v,true)do(yandy~=trueandmwFunc[k]ornoOp)(HTML,cssandx:gsub('_','-')orx,y)end--iterate non-numbers first for_,yinMBpairs(v,nil)do(yandy~=trueandmwFunc[k]ornoOp)(HTML,y)end--don't bother with gsub since text must be quoted anyhow end elseifrawget(Element,k)orrawget(MBi,k)then iftonumber(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 :_all else(Element[k]orMBi[k])(nodes,unpack(v,1,table.maxn(v)))end--v is definately a table elsemwFunc.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 returnnodes end } 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]end rawkey[setmetatable(Element,{__index=outFuncs,__concat=function(Element,v)returnsetmetatable({nodes=spec({},Element),orig=parent[v]},tempMeta)end})]=math.huge end functionMBi:getHTML(...) lastBuffer=self if...then ifselect('#',...)==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,...))end end returnlastHTML end functionMBi:_html(...)returnMBi._(self,lastHTML,select(spec[self]==Elementandselect('#',...)==0and1or2,true,...))end returninit(...) end function_inHTML(self,...) localHTML=init(nil,...) ifHTML.selfClosingandspec[self]==Elementthenself.last_concat=table.insert(self,HTML)returnselfend lastBuffer,lastHTML=self,setmetatable(parent(HTML,self),buffHTML)--set after 'args' table processed by :_add returnHTML end end local_var,unbuilddolocalprev,rebuild localfunctioninit(...)--init replaced before return localfunctionpick(b,v)returnbandtable.insert(b,v)orvend localfunctionc(a,num)returnrawset(a.aora,0,a[0]anda[0]+a.cornumanda[1]ora[1]:byte())[0]end localsame,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 contents number=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+c ift.tablethent[0]=(t[0]-1)%#t[1]+1end end functionrebuild(...) localv,c=... ifvorselect('#',...)==0then ifvandnotcthenreturnprevend localmeta,c=select(vand1or3,alt,c,same,0) returnsetmetatable({a=prev,_build=meta.__tostring,c=c},meta) elseifv==nilthen--no-op elseifcthenshift(prev,c)--v == false elseprev:_build()end end init,noCache=function(v,c)prev=setmetatable({v,c=c,_build=build[type(v)]orv,[type(v)]=true,alt={}},build)returnprevend,true returninit(...) end functionunbuild(sep) fork,vinMBpairs(sep,nil)do k=getmetatable(v)ifkand(k==buildork==alt)thenshift(v.aorv,-v.c)end end end function_var(self,...) localobjif...and...~=truethenobj=init(...) elseifprevthen if...~=falsethenobj=rebuild(...) elserebuild(...)end end returnobjandMBi._(self,obj,nil,true)orself end end locallib;MBi=setmetatable({stream=_stream, _inHTML=_inHTML, _var=_var, _=function(self,v,...) localat,raw=select(select('#',...)==1and...==trueand1or2,nil,...) ifrawthenrawkey[self]=math.hugeelsev=Valid(v)end ifvorrawthen ifatorrawkey[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)oratend 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) returnself end, _nil=function(self,at,...) if...~=trueand...~=falsethen--faster than type(...) ~= 'boolean' ifnotatorat=='0'then self[#self]=...if...thenrawkey[self]=math.hugeend else localn,v=tonumber(at),... ifn~=atthen ifnthenn=#self+at elseifat~=trueandselect('#',...)==0thenv,n=at,#selfend end ifnthen ifv==nilandn>0thentable.remove(self,n) elseself[math.floor(n)],rawkey[self]=v,math.hugeend--floor position for consistency with Table library end end self.last_concat=nil end returnself end, _all=function(self,t,valKey) fork,vinMBpairs(t)doMBmix(self,v,valKey)end fork,vinvalKeyandMBpairs(t,false)ornoOp,tdo iftonumber(v)thenMBi._(self,k,v)--self not always a buffer elseifrawget(MBi,k)andvandv~=truethen ifv[1]==nilorgetmetatable(v)thenMBi[k](self,v) elseMBi[k](self,unpack(v,1,table.maxn(v)))end end end returnself end, _str=function(t,...) localn=select('#',...) ifn>1then localk,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-1end returntable.concat(r,seps[k]ornil) end returnMB.__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>1then localk,ops,seps=1,MBselect(n,...) whileparent[t]andops>0dot,k,ops=parent[t]:_(t(seps[k])),k+1,ops-1end elseifparent[t]thenreturnparent[t]:_(t(...))end returnt end, _cc=function(self,clear,copy,meta) ifclearthen ifrawequal(clear,copy)thenreturnself,spec[MBi._cc]andsetmetatable(spec[MBi._cc],MB)--rawequal to avoid re-string via __eq in case both are different Buffer objects elseifcopy==truethencopy=selfend ifclear~=0then 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 forkinselfandnextornoOp,cleardorawset(clear,k,nil)end elsereturnMBi._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,clear fork=1,edotable.insert(clear,false)end whilei<=ndotable.insert(clear,i,'')i,null[i]=i+math.ldexp(2,select(2,math.frexp(n-i))-2),''end fork=1,edorawset(clear,k,nil)end end fork,vinnext,copydorawset(clear,k,type(v)=='table'andMBi._cc(false,0,v)orv)end elseifcopythenrawset(clear,1,(Valid(copy)))end rawkey[setmetatable(clear,meta)],parent[clear]=rawkey[copy],parent[copy] end returnselfandrawset(self,'last_concat',nil)orclear end, _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 basis localfunc=string[i]ormw.text[i]ormw.ustring[i]ortype(i)=='string'andmw.ustring[i:match'^u(.+)']iffuncthen lib=liborfunction(s,f,...) ifparent[s]andnext(s)==nilthenreturns:_((f(tostring(parent[Elementand(spec[s]==Elementands:allDone()orspec[parent[s]]==Elementandparent[s])ors]),...)))end returnf(tostring(s),...)--not using ternary/logical operators here to allow multiple return values end returnrawset(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] end end}) end functionMBmix(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 args local_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)then raw,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)then raw,i,j={},iandmath.floor(i),jandmath.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 fork,vinMBpairs(t)do ifraw[1]ornotiork>=ithenifjandk>jthenbreakend ifraw.sthenraw.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)ifkthen raw.s=rawseporsepandraw[1]andtable.insert(raw,sep) table.insert(raw,k) end end end ifrawsepandnotraw.sthenraw[#raw]=unbuild(sep)end--unbuild rawsep if final index in t was invalid t.last_concat=raw.lcreturntable.concat(raw) end returntable.concat(t,sep,iandmath.max(i,1),jandmath.min(j,#t)) end returnMB.__tostring(t) end, __tostring=function(t) ift.last_concatthenreturnt.last_concatend localr=rawkey[spec[t]]orrawkey[t] r=table.concat(randr>=#tandMBi._all({},t)ort) return(noCacheorrawset(t,'last_concat',r))andr end, __concat=function(a,b) ifbuffHTMLthen fork=1,2dolocalv=select(k,a,b)--faster than for k, v in pairs{a, b} do ifvandspec[v]andspec[v]==Elementthen ifparent[v].selfClosingthen ifrawequal(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/attributes end end end end returntable.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=MBorself ifnew_Gthenif...and_Gand...==_Gthennew_G=...end elseif...and(...==_Gortype(...)=='table'and(...)._G==...)then localNil,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 buffer localmG=getmetatable(new_G)or{__call=mG.__call} ifmG.__indexthenpcall(rawset,mG.__index,i,X) elsemG.__index=setmetatable(new_G,mG)and{[i]=X}end end returnself,...--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) ifi~='new_G'thenifiandi~=truethenrawset(new_G,i,v)end elseifnotvorv==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 noOp returnself end, _2=function(self,...) ifnew_G[...]~=nilthenreturnnew_G[...]end--higher priority so Buffer:_G('new_G', ...) can prevent an overwrite if...=='new_G'thenreturnrawset((select('#',...)~=1andMBi._R(new_G,...)ornew_G),'_G',_G)end returnselect(select('#',...)==1and1or2,self:_G(...))--return only one value; 'return select(2, self:_G(...)) or self' doesn't work for returning nil end, _B=function(self,v)returnvorv==nilandNilend }fork,vinnext,gfuncsdoMBi[k]=vend setmetatable(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...end end localnew=setmetatable({},self) if...and(...)==new_Gthenreturnselect(2,...)andMBmix(new:_G((select(2,...))),select(3,...))ornewend return...andMBi._(new,...)ornew end, __index=function(t,i) MB=MBortreturnMBi[i]andfunction(...)returnMBi[i](setmetatable({},t),select(...==tand2or1,...))end end })