local a=require'pl.utils'local b=a.split;local c=table.insert;local d=table.concat;local e=table.remove;local f=string.match;local tostring=tostring;local setmetatable=setmetatable;local getmetatable=getmetatable;local pairs=pairs;local ipairs=ipairs;local type=type;local next=next;local print=print;local g=a.unpack;local h=string.gsub;local i=string.find;local pcall,require,io=pcall,require,io;local j={}local k={__type="doc"}k.__index=k;function j.new(l,m)local n={tag=l,attr=m or{},last_add={}}return setmetatable(n,k)end;function j.parse(o,p,q)local r,s,t;if q then r=j.basic_parse else s,t=pcall(require,'lxp.lom')if not s then r=j.basic_parse else r=t.parse end end;if p then local u,v=io.open(o)if not u then return nil,v end;o=u:read'*a'u:close()end;local n,v=r(o)if not n then return nil,v end;if t then j.walk(n,false,function(w,x)setmetatable(x,k)end)end;return n end;function k:addtag(l,y)local z=j.new(l,y)(self.last_add[#self.last_add]or self):add_direct_child(z)c(self.last_add,z)return self end;function k:text(A)(self.last_add[#self.last_add]or self):add_direct_child(A)return self end;function k:up()e(self.last_add)return self end;function k:reset()local B=self.last_add;for C=1,#B do B[C]=nil end;return self end;function k:add_direct_child(D)c(self,D)end;function k:add_child(D)(self.last_add[#self.last_add]or self):add_direct_child(D)return self end;function k:set_attribs(E)for F,G in pairs(E)do self.attr[F]=G end end;function k:set_attrib(H,G)self.attr[H]=G end;function k:get_attribs()return self.attr end;local function I(z)return type(z)=='string'end;function j.elem(l,J)local z=j.new(l)if I(J)then J={J}end;if j.is_tag(J)then c(z,J)elseif type(J)=='table'then for F,G in pairs(J)do if I(F)then z.attr[F]=G;c(z.attr,F)else z[F]=G end end end;return z end;function j.tags(K)local L={}if I(K)then K=b(K,'%s*,%s*')end;for w,l in ipairs(K)do local M=function(J)return j.elem(l,J)end;c(L,M)end;return g(L)end;local N={}local function O(P)if I(P)then if N[P]then P=N[P]else local Q,v=P;P,v=j.parse(Q,false,true)if not P then return nil,v end;N[Q]=P end elseif not j.is_tag(P)then return nil,"template is not a document"end;return P end;local function R(S)return#S==0 or type(S[1])~='table'end;local function T(S)for C,G in ipairs(S)do S[tostring(C)]=G end end;function k.subst(P,S)local v;if type(S)~='table'or not next(S)then return nil,"data must be a non-empty table"end;if R(S)then T(S)end;P,v=O(P)if v then return nil,v end;local function U(V)return j.clone(P,function(z)return z:gsub('%$(%w+)',V)end)end;if R(S)then return U(S)end;local K={}for w,V in ipairs(S)do T(V)c(K,U(V))end;if S.tag then K=j.elem(S.tag,K)end;return K end;function k:child_with_name(l)for w,D in ipairs(self)do if D.tag==l then return D end end end;local W;function W(self,l,K,X)for w,D in ipairs(self)do if type(D)=='table'then if D.tag==l then c(K,D)end;if X then W(D,l,K,X)end end end end;function k:get_elements_with_name(l,Y)local Z={}W(self,l,Z,not Y)return Z end;function k:children()local C=0;return function(H)C=C+1;return H[C]end,self,C end;function k:first_childtag()if#self==0 then return end;for w,E in ipairs(self)do if type(E)=='table'then return E end end end;function k:matching_tags(l,_)_=_ or self.attr.xmlns;local a0=self;local a1,a2,G=1,#a0;return function()for C=a1,a2 do G=a0[C]if(not l or G.tag==l)and(not _ or _==G.attr.xmlns)then a1=C+1;return G end end end,a0,a1 end;function k:childtags()local C=0;return function(H)local G;repeat C=C+1;G=self[C]if G and type(G)=='table'then return G end until not G end,self[1],C end;function k:maptags(a3)local a4=j.is_tag;local C=1;while C<=#self do if a4(self[C])then local a5=a3(self[C])if a5==nil then e(self,C)else self[C]=a5;C=C+1 end end end;return self end;local a6;do local a7={["'"]="'",["\""]=""",["<"]="<",[">"]=">",["&"]="&"}function a6(Q)return h(Q,"['&<>\"]",a7)end;j.xml_escape=a6 end;local function a8(E,a9,self,a6,aa,ab,ac,ad)local ae=0;local l=E.tag;local af,ag=""," "if ac then af='\n'..ab end;if ad then ag='\n'..ab..ad end;c(a9,af.."<"..l)local function ah(F,G)if i(F,"\1",1,true)then local ai,aj=f(F,"^([^\1]*)\1?(.*)$")ae=ae+1;c(a9," xmlns:ns"..ae.."='"..a6(ai).."' ".."ns"..ae..":"..aj.."='"..a6(G).."'")elseif not(F=="xmlns"and G==aa)then c(a9,ag..F.."='"..a6(G).."'")end end;if#E.attr>0 then for w,F in ipairs(E.attr)do ah(F,E.attr[F])end else for F,G in pairs(E.attr)do ah(F,G)end end;local ak,al=#E;if ak==0 then local am="/>"if ad then am='\n'..ab..am end;c(a9,am)else c(a9,">")for an=1,ak do local D=E[an]if D.tag then self(D,a9,self,a6,E.attr.xmlns,ab and ab..ac,ac,ad)al=true else c(a9,a6(D))end end;c(a9,(al and af or'').."</"..l..">")end end;function j.tostring(E,ab,ac,ad,ao)local a9={}if ao then if type(ao)=="string"then a9[1]=ao else a9[1]="<?xml version='1.0'?>"end end;a8(E,a9,a8,a6,nil,ab,ac,ad)return d(a9)end;k.__tostring=j.tostring;function k:get_text()local Z={}for C,ap in ipairs(self)do if I(ap)then c(Z,ap)end end;return d(Z)end;function j.clone(n,aq)local ar={}local function as(at,au,av)if type(at)~="table"then if aq and I(at)then return aq(at,au,av)else return at end elseif ar[at]then return ar[at]end;local aw={}ar[at]=aw;local l=at.tag;aw.tag=as(l,'*TAG',av)if at.attr then local Z={}for m,ax in pairs(at.attr)do Z[m]=as(ax,m,at)end;aw.attr=Z end;for ay=1,#at do local G=as(at[ay],'*TEXT',at)c(aw,G)end;return setmetatable(aw,getmetatable(at))end;return as(n)end;k.filter=j.clone;function j.compare(az,aA)local aB=type(az)local aC=type(aA)if aB~=aC then return false,'type mismatch'end;if aB=='string'then return az==aA and true or'text '..az..' ~= text '..aA end;if aB~='table'or aC~='table'then return false,'not a document'end;if az.tag~=aA.tag then return false,'tag '..az.tag..' ~= tag '..aA.tag end;if#az~=#aA then return false,'size '..#az..' ~= size '..#aA..' for tag '..az.tag end;for F,G in pairs(az.attr)do if aA.attr[F]~=G then return false,'mismatch attrib'end end;for F,G in pairs(aA.attr)do if az.attr[F]~=G then return false,'mismatch attrib'end end;for C=1,#az do local aD,v=j.compare(az[C],aA[C])if not aD then return v end end;return true end;function j.is_tag(x)return type(x)=='table'and I(x.tag)end;function j.walk(n,aE,aF)if not aE then aF(n.tag,n)end;for w,x in ipairs(n)do if j.is_tag(x)then j.walk(x,aE,aF)end end;if aE then aF(n.tag,n)end end;local aG={br=true,img=true,meta=true,frame=true,area=true,hr=true,base=true,col=true,link=true,input=true,option=true,param=true,isindex=true,embed=true}local aH={quot="\"",apos="'",lt="<",gt=">",amp="&"}local function aI(Q)return Q:gsub("&(%a+);",aH)end;function j.parsehtml(z)return j.basic_parse(z,false,true)end;function j.basic_parse(z,aJ,aK)local c,e=table.insert,table.remove;local i,aL=string.find,string.sub;local aM={}local aN={}local function aO(z)local aP={}z:gsub("([%w:%-_]+)%s*=%s*([\"'])(.-)%2",function(aQ,w,H)if aK then aQ=aQ:lower()end;aP[aQ]=aI(H)end)if aK then z:gsub("([%w:%-_]+)%s*=%s*([^\"']+)%s*",function(aQ,H)aQ=aQ:lower()aP[aQ]=aI(H)end)end;return aP end;c(aM,aN)local aR,aS,aT,aU,aV,w,aW;local C=1;local aX;w,aW=i(z,'^%s*<%?[^%?]+%?>%s*')if not aW then w,aW=i(z,'^%s*<!DOCTYPE.->%s*')end;if aW then C=aW+1 end;while true do aR,aX,aS,aT,aU,aV=i(z,"<([%/!]?)([%w:%-_]+)(.-)(%/?)>",C)if not aR then break end;if aS=="!"then if not(aT:match'%-%-$'and aU=='')then if aU:match'%-%-$'then aX=aX-2 end;w,aX=i(z,"-->",aX,true)end else local A=aL(z,C,aR-1)if aK then aT=aT:lower()if aG[aT]then aV="/"end end;if aJ or not i(A,"^%s*$")then c(aN,aI(A))end;if aV=="/"then c(aN,setmetatable({tag=aT,attr=aO(aU),empty=1},k))elseif aS==""then aN=setmetatable({tag=aT,attr=aO(aU)},k)c(aM,aN)else local aY=e(aM)aN=aM[#aM]if#aM<1 then error("nothing to close with "..aT..':'..A)end;if aY.tag~=aT then error("trying to close "..aY.tag.." with "..aT.." "..A)end;c(aN,aY)end end;C=aX+1 end;local A=aL(z,C)if aJ or not i(A,"^%s*$")then c(aM[#aM],aI(A))end;if#aM>1 then error("unclosed "..aM[#aM].tag)end;local Z=aM[1]return I(Z[1])and Z[2]or Z[1]end;local function aV(m)return not m or not next(m)end;local function aZ(x)return type(x)=='table'and x.tag~=nil end;local function a_(E)local b0,ax=next(E)if next(E,b0)~=nil then return false end;return b0,ax end;local function b1(Z,b2)if not aV(b2)then local b0;if b2._ then b0=b2._;b2._=nil;if aV(b2)then return end end;local b3,b4=a_(b2)if b3==0 then b2=b4 end;if b0 then Z[b0]=b2 else c(Z,b2)end end end;local function b5(b6)if b6:find'^%d+$'then b6=tonumber(b6)end;return b6 end;local function b7(Z,b6,ax)b6=b5(b6:sub(2))Z[b6]=ax;return true end;local b8;function b8(x,b6,Z,b9)local a5=true;if x==nil then x=''end;if I(x)then if not I(b6)then return false end;if j.debug then print(x,b6)end;if b6:find'^%$'then return b7(Z,b6,x)else return x==b6 end else if j.debug then print(x.tag,b6.tag)end;local ba=b6.tag:match'^(.-)%-$'if ba then ba=b5(ba)Z[ba]=x.tag end;if x.tag==b6.tag or ba then if not aV(b6.attr)then if aV(x.attr)then a5=false else for bb,bc in pairs(b6.attr)do local bd=x.attr[bb]if not b8(bd,bc,Z)then a5=false;break end end end end;if a5 and#b6>0 then local C,aX=1,1;local function be()aX=aX+1;if I(x[aX])then aX=aX+1 end;return aX<=#x end;repeat local bf=b6[C]if aZ(bf)and bf.repeated then local bg;repeat local b2={}a5=b8(x[aX],bf,b2,false)if a5 then bg=false;b1(Z,b2)end until not be()or bg and not a5;C=C+1 else a5=b8(x[aX],bf,Z,false)if a5 then C=C+1 end end until not be()or C>#b6;if C>#b6 then return true end end;if a5 then return true end else a5=false end;if b9 then for D in x:childtags()do a5=b8(D,b6,Z,b9)if a5 then break end end end end;return a5 end;function k:match(b6)local v;b6,v=O(b6)if not b6 then return nil,v end;j.walk(b6,false,function(w,x)if I(x[1])and aZ(x[2])and I(x[3])and x[1]:find'%s*{{'and x[3]:find'}}%s*'then e(x,1)e(x,2)x[1].repeated=true end end)local Z={}local a5=b8(self,b6,Z,true)return Z,a5 end;return j