-- Penlight 1.11.0-1 | /lua/pl/xml.lua | https://github.com/lunarmodules/Penlight | License: MIT | Minified using https://www.npmjs.com/package/luamin/v/1.0.4
-- Copyright (C) 2009-2016 Steve Donovan, David Manura.
-- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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={["'"]="&apos;",["\""]="&quot;",["<"]="&lt;",[">"]="&gt;",["&"]="&amp;"}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
