summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex/luaxml
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2018-02-09 00:16:06 +0000
committerKarl Berry <karl@freefriends.org>2018-02-09 00:16:06 +0000
commitb2153506a41b9f934841bc0eb31dd9c3946b407b (patch)
treedebea85a21708749fafa1748c894ab25b0e125e8 /Master/texmf-dist/tex/luatex/luaxml
parente6304d3f0b0d72c2f38350d3ae4b4fa6bd862793 (diff)
luaxml (8feb18)
git-svn-id: svn://tug.org/texlive/trunk@46570 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luaxml')
-rw-r--r--Master/texmf-dist/tex/luatex/luaxml/dom-sample.lua29
-rw-r--r--Master/texmf-dist/tex/luatex/luaxml/luaxml-cssquery.lua214
-rw-r--r--Master/texmf-dist/tex/luatex/luaxml/luaxml-domobject.lua510
-rw-r--r--Master/texmf-dist/tex/luatex/luaxml/luaxml-mod-xml.lua13
-rw-r--r--Master/texmf-dist/tex/luatex/luaxml/luaxml-parse-query.lua43
-rw-r--r--Master/texmf-dist/tex/luatex/luaxml/luaxml-selectors.lua76
6 files changed, 779 insertions, 106 deletions
diff --git a/Master/texmf-dist/tex/luatex/luaxml/dom-sample.lua b/Master/texmf-dist/tex/luatex/luaxml/dom-sample.lua
deleted file mode 100644
index d3fcde51ff0..00000000000
--- a/Master/texmf-dist/tex/luatex/luaxml/dom-sample.lua
+++ /dev/null
@@ -1,29 +0,0 @@
---kpse.set_program_name("luatex")
-function traverseDom(parser, current,level)
- local level = level or 0
- local spaces = string.rep(" ",level)
- local root= current or parser._handler.root
- local name = root._name or "unnamed"
- local xtype = root._type or "untyped"
- local attributes = root._attr or {}
- if xtype == "TEXT" then
- print(spaces .."TEXT : " .. root._text)
- else
- print(spaces .. xtype .. " : " .. name)
- end
- for k, v in pairs(attributes) do
- print(spaces .. " ".. k.."="..v)
- end
- local children = root._children or {}
- for _, child in ipairs(children) do
- traverseDom(parser,child, level + 1)
- end
-end
-
-local xml = require('luaxml-mod-xml')
-local handler = require('luaxml-mod-handler')
-local x = '<p>hello <a href="http://world.com/">world</a>, how are you?</p>'
-local domHandler = handler.domHandler()
-local parser = xml.xmlParser(domHandler)
-parser:parse(x)
-traverseDom(parser)
diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-cssquery.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-cssquery.lua
new file mode 100644
index 00000000000..9f283ed6e64
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luaxml/luaxml-cssquery.lua
@@ -0,0 +1,214 @@
+--- CSS query module for LuaXML
+-- @module luaxml-cssquery
+-- @author Michal Hoftich <michal.h21@gmail.com
+local parse_query = require("luaxml-parse-query")
+
+--- CssQuery constructor
+-- @function cssquery
+-- @return CssQuery object
+local function cssquery()
+ --- @type CssQuery
+ local CssQuery = {}
+ CssQuery.__index = CssQuery
+ CssQuery.__debug = false
+ CssQuery.querylist = {}
+
+ function CssQuery.debug(self)
+ self.__debug = true
+ end
+
+ function CssQuery:debug_print(text)
+ if self.__debug then
+ print("[CSS Object]: " .. text)
+ end
+ end
+ --- Calculate CSS specificity of the query
+ -- @param query table created by CssQuery:prepare_selector() function
+ -- @return integer speficity value
+ function CssQuery:calculate_specificity(query)
+ local query = query or {}
+ local specificity = 0
+ for _, item in ipairs(query.query or {}) do
+ for key, value in pairs(item) do
+ if key == "id" then
+ specificity = specificity + 100
+ elseif key == "tag" then
+ specificity = specificity + 1
+ else
+ specificity = specificity + 10
+ end
+ end
+ end
+ return specificity
+ end
+
+ --- Test prepared querylist
+ -- @param domobj DOM element to test
+ -- @param querylist [optional] List of queries to test
+ -- @return table with CSS queries, which match the selected DOM element
+ function CssQuery:match_querylist(domobj, querylist)
+ local matches = {}
+ -- querylist can be explicit, saved queries can be used otherwise
+ local querylist = querylist or self.querylist
+
+ local function test_part(key, value, el)
+ -- print("testing", key, value, el:get_element_name())
+ if key == "tag" then
+ return el:get_element_name() == value
+ elseif key == "id" then
+ local id = el:get_attribute "id"
+ return id and id == value
+ elseif key == "class" then
+ local class = el:get_attribute "class"
+ if not class then return false end
+ local c = {}
+ for part in class:gmatch "([^%s]+)" do
+ c[part] = true
+ end
+ return c[value] == true
+ end
+ -- TODO: Add more cases
+ -- just return true for not supported selectors
+ return true
+ end
+
+ local function test_object(query, el)
+ -- test one object in CSS selector
+ local matched = {}
+ for key, value in pairs(query) do
+ matched[#matched+1] = test_part(key, value, el)
+ end
+ if #matched == 0 then return false end
+ for k, v in ipairs(matched) do
+ if v ~= true then return false end
+ end
+ return true
+ end
+
+ local function match_query(query, el)
+ local query = query or {}
+ local object = table.remove(query) -- get current object from the query stack
+ if not object then return true end -- if the query stack is empty, then we can be sure that it matched previous items
+ if not el:is_element() then return false end -- if there is object to test, but current node isn't element, test failed
+ local result = test_object(object, el)
+ if result then
+ return match_query(query, el:get_parent())
+ end
+ return false
+ end
+ for _,element in ipairs(querylist) do
+ local query = {}
+ for k,v in ipairs(element.query) do query[k] = v end
+ if #query > 0 then -- don't try to match empty query
+ local result = match_query(query, domobj)
+ if result then matches[#matches+1] = element end
+ end
+ end
+ return matches
+ end
+
+ --- Get elements that match the selector
+ -- @return table with DOM_Object elements
+ function CssQuery:get_selector_path(
+ domobj, -- DOM_Object
+ selectorlist -- querylist table created using CssQuery:prepare_selector
+ )
+ local nodelist = {}
+ domobj:traverse_elements(function(el)
+ local matches = self:match_querylist(el, selectorlist)
+ self:debug_print("Matching " .. el:get_element_name() .." "..#matches)
+ if #matches > 0 then nodelist[#nodelist+1] = el
+ end
+ end)
+ return nodelist
+ end
+
+ --- Parse CSS selector to query table
+ -- @return table querylist
+ function CssQuery:prepare_selector(
+ selector -- string CSS selector query
+ )
+ local querylist = {}
+ local function parse_selector(item)
+ local query = {}
+ -- for i = #item, 1, -1 do
+ -- local part = item[i]
+ for _, part in ipairs(item) do
+ local t = {}
+ for _, atom in ipairs(part) do
+ local key = atom[1]
+ local value = atom[2]
+ t[key] = value
+ end
+ query[#query + 1] = t
+ end
+ return query
+ end
+ -- for item in selector:gmatch("([^%s]+)") do
+ -- elements[#elements+1] = parse_selector(item)
+ -- end
+ local parts = parse_query.parse_query(selector) or {}
+ -- several selectors may be separated using ",", we must process them separately
+ local sources = selector:explode(",")
+ for i, part in ipairs(parts) do
+ querylist[#querylist+1] = {query = parse_selector(part), source = sources[i]}
+ end
+ return querylist
+ end
+
+ --- Add selector to CSS object list of selectors,
+ -- func is called when the selector matches a DOM object
+ -- params is table which will be passed to the func
+ -- @return integer number of elements in the prepared selector
+ function CssQuery:add_selector(
+ selector, -- CSS selector string
+ func, -- function which will be executed on matched elements
+ params -- table with parameters for the function
+ )
+ local selector_list = self:prepare_selector(selector)
+ for k, query in ipairs(selector_list) do
+ query.specificity = self:calculate_specificity(query)
+ query.func = func
+ query.params = params
+ table.insert(self.querylist, query)
+ end
+ self:sort_querylist()
+ return #selector_list
+ end
+
+ --- Sort selectors according to their specificity
+ -- It is called automatically when the selector is added
+ -- @return querylist table
+ function CssQuery:sort_querylist(
+ querylist -- [optional] querylist table
+ )
+ local querylist = querylist or self.querylist
+ table.sort(self.querylist, function(a,b)
+ return a.specificity > b.specificity
+ end)
+ return querylist
+ end
+
+ --- It tests list of queries agaings a DOM element and executes the
+ --- coresponding function that is saved for the matched query.
+ -- @return nothing
+ function CssQuery:apply_querylist(
+ domobj, -- DOM element
+ querylist -- querylist table
+ )
+ for _, query in ipairs(querylist) do
+ -- use default empty function which will pass to another match
+ local func = query.func or function() return true end
+ local params = query.params or {}
+ local status = func(domobj, params)
+ -- break the execution when the function return false
+ if status == false then
+ break
+ end
+ end
+ end
+
+ return setmetatable({}, CssQuery)
+end
+
+return cssquery
diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-domobject.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-domobject.lua
new file mode 100644
index 00000000000..7047c76a937
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luaxml/luaxml-domobject.lua
@@ -0,0 +1,510 @@
+--- DOM module for LuaXML
+-- @module luaxml-domobject
+-- @author Michal Hoftich <michal.h21@gmail.com
+local dom = {}
+local xml = require("luaxml-mod-xml")
+local handler = require("luaxml-mod-handler")
+local css_query = require("luaxml-cssquery")
+
+
+local void = {area = true, base = true, br = true, col = true, hr = true, img = true, input = true, link = true, meta = true, param = true}
+
+local escapes = {
+ [">"] = "&gt;",
+ ["<"] = "&lt;",
+ ["&"] = "&amp;",
+ ['"'] = "&quot;",
+ ["'"] = "&#39;"
+}
+
+local function escape(search, text)
+ return text:gsub(search, function(ch)
+ return escapes[ch] or ""
+ end)
+end
+
+local function escape_element(text)
+ return escape("([<>&])", text)
+end
+
+local function escape_attr(text)
+ return escape("([<>&\"'])", text)
+end
+
+local actions = {
+ TEXT = {text = "%s"},
+ COMMENT = {start = "<!-- ", text = "%s", stop = " -->"},
+ ELEMENT = {start = "<%s%s>", stop = "</%s>", void = "<%s%s />"},
+ DECL = {start = "<?%s %s?>"},
+ DTD = {start = "<!DOCTYPE ", text = "%s" , stop=">"}
+}
+
+--- It serializes the DOM object back to the XML.
+-- This function is mainly used for internal purposes, it is better to
+-- use the `DOM_Object:serialize()`.
+-- @param parser DOM object
+-- @param current Element which should be serialized
+-- @param level
+-- @param output
+-- @return table Table with XML strings. It can be concenated using table.concat() function to get XML string corresponding to the DOM_Object.
+local function serialize_dom(parser, current,level, output)
+ local output = output or {}
+ local function get_action(typ, action)
+ local ac = actions[typ] or {}
+ local format = ac[action] or ""
+ return format
+ end
+ local function insert(format, ...)
+ table.insert(output, string.format(format, ...))
+ end
+ local function prepare_attributes(attr)
+ local t = {}
+ local attr = attr or {}
+ for k, v in pairs(attr) do
+ t[#t+1] = string.format("%s='%s'", k, escape_attr(v))
+ end
+ if #t == 0 then return "" end
+ -- add space before attributes
+ return " " .. table.concat(t, " ")
+ end
+ local function start(typ, el, attr)
+ local format = get_action(typ, "start")
+ insert(format, el, prepare_attributes(attr))
+ end
+ local function text(typ, text)
+ local format = get_action(typ, "text")
+ insert(format, escape_element(text))
+ end
+ local function stop(typ, el)
+ local format = get_action(typ, "stop")
+ insert(format,el)
+ end
+ local level = level or 0
+ local spaces = string.rep(" ",level)
+ local root= current or parser._handler.root
+ local name = root._name or "unnamed"
+ local xtype = root._type or "untyped"
+ local text_content = root._text or ""
+ local attributes = root._attr or {}
+ -- if xtype == "TEXT" then
+ -- print(spaces .."TEXT : " .. root._text)
+ -- elseif xtype == "COMMENT" then
+ -- print(spaces .. "Comment : ".. root._text)
+ -- else
+ -- print(spaces .. xtype .. " : " .. name)
+ -- end
+ -- for k, v in pairs(attributes) do
+ -- print(spaces .. " ".. k.."="..v)
+ -- end
+ if xtype == "DTD" then
+ text_content = string.format('%s %s "%s" "%s"', name, attributes["_type"] or "", attributes._name, attributes._uri )
+ -- remove unused fields
+ text_content = text_content:gsub('"nil"','')
+ text_content = text_content:gsub('%s*$','')
+ attributes = {}
+ elseif xtype == "ELEMENT" and void[name] then
+ local format = get_action(xtype, "void")
+ insert(format, name, prepare_attributes(attributes))
+ return output
+ end
+
+ start(xtype, name, attributes)
+ text(xtype,text_content)
+ local children = root._children or {}
+ for _, child in ipairs(children) do
+ output = serialize_dom(parser,child, level + 1, output)
+ end
+ stop(xtype, name)
+ return output
+end
+
+--- XML parsing function
+-- Parse the XML text and create the DOM object.
+-- @return DOM_Object
+local parse = function(
+ xmltext --- String to be parsed
+ )
+ local domHandler = handler.domHandler()
+ --- @type DOM_Object
+ local DOM_Object = xml.xmlParser(domHandler)
+ -- preserve whitespace
+ DOM_Object.options.stripWS = nil
+ DOM_Object:parse(xmltext)
+ DOM_Object.current = DOM_Object._handler.root
+ DOM_Object.__index = DOM_Object
+ DOM_Object.css_query = css_query()
+
+ local function save_methods(element)
+ setmetatable(element,DOM_Object)
+ local children = element._children or {}
+ for _, x in ipairs(children) do
+ save_methods(x)
+ end
+ end
+ local parser = setmetatable({}, DOM_Object)
+
+ --- Returns root element of the DOM_Object
+ -- @return DOM_Object
+ function DOM_Object:root_node()
+ return self._handler.root
+ end
+
+
+ --- Get current node type
+ -- @param el [optional] node to get the type of
+ function DOM_Object:get_node_type(
+ el --- [optional] element to test
+ )
+ local el = el or self
+ return el._type
+ end
+
+ --- Test if the current node is an element.
+ -- You can pass different element as parameter
+ -- @return boolean
+ function DOM_Object:is_element(
+ el --- [optional] element to test
+ )
+ local el = el or self
+ return self:get_node_type(el) == "ELEMENT" -- @bool
+ end
+
+
+ --- Test if current node is text
+ -- @return boolean
+ function DOM_Object:is_text(
+ el --- [optional] element to test
+ )
+ local el = el or self
+ return self:get_node_type(el) == "TEXT"
+ end
+
+ local lower = string.lower
+
+ --- Return name of the current element
+ -- @return string
+ function DOM_Object:get_element_name(
+ el --- [optional] element to test
+ )
+ local el = el or self
+ return el._name or "unnamed"
+ end
+
+ --- Get value of an attribute
+ -- @return string
+ function DOM_Object:get_attribute(
+ name --- Attribute name
+ )
+ local el = self
+ if self:is_element(el) then
+ local attr = el._attr or {}
+ return attr[name]
+ end
+ end
+
+ --- Set value of an attribute
+ -- @return boolean
+ function DOM_Object:set_attribute(
+ name --- Attribute name
+ , value --- Value to be set
+ )
+ local el = self
+ if self:is_element(el) then
+ el._attr[name] = value
+ return true
+ end
+ end
+
+
+ --- Serialize the current node back to XML
+ -- @return string
+ function DOM_Object:serialize(
+ current --- [optional] element to be serialized
+ )
+ local current = current
+ -- if no current element is added and self is not plain parser object
+ -- (_type is then nil), use the current object as serialized root
+ if not current and self._type then
+ current = self
+ end
+ return table.concat(serialize_dom(self, current))
+ end
+
+ --- Get text content from the node and all of it's children
+ -- @return string
+ function DOM_Object:get_text(
+ current --- [optional] element which should be converted to text
+ )
+ local current = current or self
+ local text = {}
+ for _, el in ipairs(current:get_children()) do
+ if el:is_text() then
+ text[#text+1] = el._text or ""
+ elseif el:is_element() then
+ text[#text+1] = el:get_text()
+ end
+ end
+ return table.concat(text)
+ end
+
+
+
+ --- Retrieve elements from the given path.
+ -- The path is list of elements separated by space,
+ -- starting from the top element of the current element
+ -- @return table of elements which match the path
+ function DOM_Object:get_path(
+ path --- path to be traversed
+ , current --- [optional] element which should be traversed. Default element is the root element of the DOM_Object
+ )
+ local function traverse_path(path_elements, current, t)
+ local t = t or {}
+ if #path_elements == 0 then
+ -- for _, x in ipairs(current._children or {}) do
+ -- table.insert(t,x)
+ -- end
+ table.insert(t,current)
+ return t
+ end
+ local current_path = table.remove(path_elements, 1)
+ for _, x in ipairs(self:get_children(current)) do
+ if self:is_element(x) then
+ local name = string.lower(self:get_element_name(x))
+ if name == current_path then
+ t = traverse_path(path_elements, x, t)
+ end
+ end
+ end
+ return t
+ end
+ local current = current or self:root_node() -- self._handler.root
+ local path_elements = {}
+ local path = string.lower(path)
+ for el in path:gmatch("([^%s]+)") do table.insert(path_elements, el) end
+ return traverse_path(path_elements, current)
+ end
+
+ --- Select elements chidlren using CSS selector syntax
+ -- @return table with elements matching the selector.
+ function DOM_Object:query_selector(
+ selector --- String using the CSS selector syntax
+ )
+ local css_query = self.css_query
+ local css_parts = css_query:prepare_selector(selector)
+ return css_query:get_selector_path(self, css_parts)
+ end
+
+ --- Get table with children of the current element
+ -- @return table with children of the selected element
+ function DOM_Object:get_children(
+ el --- [optional] element to be selected
+ )
+ local el = el or self
+ local children = el._children or {}
+ return children
+ end
+
+ --- Get the parent element
+ -- @return DOM_Object parent element
+ function DOM_Object:get_parent(
+ el --- [optional] element to be selected
+ )
+ local el = el or self
+ return el._parent
+ end
+
+ --- Execute function on the current element and all it's children elements
+ -- @return nothing
+ function DOM_Object:traverse_elements(
+ fn, --- function which will be executed on the current element and all it's children
+ current --- [optional] element to be selected
+ )
+ local current = current or self --
+ -- Following situation may happen when this method is called directly on the parsed object
+ if not current:get_node_type() then
+ current = self:root_node()
+ end
+ local status = true
+ if self:is_element(current) or self:get_node_type(current) == "ROOT"then
+ local status = fn(current)
+ -- don't traverse child nodes when the user function return false
+ if status ~= false then
+ for _, child in ipairs(self:get_children(current)) do
+ self:traverse_elements(fn, child)
+ end
+ end
+ end
+ end
+
+ --- Execute function on list of elements returned by DOM_Object:get_path()
+ function DOM_Object:traverse_node_list(
+ nodelist --- table with nodes selected by DOM_Object:get_path()
+ , fn --- function to be executed
+ )
+ local nodelist = nodelist or {}
+ for _, node in ipairs(nodelist) do
+ for _, element in ipairs(node._children) do
+ fn(element)
+ end
+ end
+ end
+
+ --- Replace the current node with new one
+ -- @return boolean, message
+ function DOM_Object:replace_node(
+ new --- element which should replace the current element
+ )
+ local old = self
+ local parent = self:get_parent(old)
+ local id,msg = self:find_element_pos( old)
+ if id then
+ parent._children[id] = new
+ return true
+ end
+ return false, msg
+ end
+
+ --- Add child node to the current node
+ function DOM_Object:add_child_node(
+ child --- element to be inserted as a current node child
+ )
+ local parent = self
+ child._parent = parent
+ table.insert(parent._children, child)
+ end
+
+
+ --- Create copy of the current node
+ -- @return DOM_Object element
+ function DOM_Object:copy_node(
+ element --- [optional] element to be copied
+ )
+ local element = element or self
+ local t = {}
+ for k, v in pairs(element) do
+ if type(v) == "table" and k~="_parent" then
+ t[k] = self:copy_node(v)
+ else
+ t[k] = v
+ end
+ end
+ save_methods(t)
+ return t
+ end
+
+
+ --- Create a new element
+ -- @return DOM_Object element
+ function DOM_Object:create_element(
+ name, -- New tag name
+ attributes, -- Table with attributes
+ parent -- [optional] element which should be saved as the element's parent
+ )
+ local parent = parent or self
+ local new = {}
+ new._type = "ELEMENT"
+ new._name = name
+ new._attr = attributes or {}
+ new._children = {}
+ new._parent = parent
+ save_methods(new)
+ return new
+ end
+
+ --- Create new text node
+ -- @return DOM_Object text object
+ function DOM_Object:create_text_node(
+ text, -- string
+ parent -- [optional] element which should be saved as the element's parent
+ )
+ local parent = parent or self
+ local new = {}
+ new._type = "TEXT"
+ new._parent = parent
+ new._text = text
+ save_methods(new)
+ return new
+ end
+
+ --- Delete current node
+ function DOM_Object:remove_node(
+ element -- [optional] element to be removed
+ )
+ local element = element or self
+ local parent = self:get_parent(element)
+ local pos = self:find_element_pos(element)
+ -- if pos then table.remove(parent._children, pos) end
+ if pos then
+ -- table.remove(parent._children, pos)
+ parent._children[pos] = setmetatable({_type = "removed"}, DOM_Object)
+ end
+ end
+
+ --- Find the element position in the current node list
+ -- @return integer position of the current element in the element table
+ function DOM_Object:find_element_pos(
+ el -- [optional] element which should be looked up
+ )
+ local el = el or self
+ local parent = self:get_parent(el)
+ if not self:is_element(parent) and self:get_node_type(parent) ~= "ROOT" then return nil, "The parent isn't element" end
+ for i, x in ipairs(parent._children) do
+ if x == el then return i end
+ end
+ return false, "Cannot find element"
+ end
+
+ --- Get node list which current node is part of
+ -- @return table with elements
+ function DOM_Object:get_siblibgs(
+ el -- [optional] element for which the sibling element list should be retrieved
+ )
+ local el = el or self
+ local parent = el:get_parent()
+ if parent:is_element() then
+ return parent:get_children()
+ end
+ end
+
+ --- Get sibling node of the current node
+ -- @param change Distance from the current node
+ -- @return DOM_Object node
+ function DOM_Object:get_sibling_node( change)
+ local el = self
+ local pos = el:find_element_pos()
+ local siblings = el:get_siblibgs()
+ if pos and siblings then
+ return siblings[pos + change]
+ end
+ end
+
+ --- Get next node
+ -- @return DOM_Object node
+ function DOM_Object:get_next_node(
+ el --- [optional] node to be used
+ )
+ local el = el or self
+ return el:get_sibling_node(1)
+ end
+
+ --- Get previous node
+ -- @return DOM_Object node
+ function DOM_Object:get_prev_node(
+ el -- [optional] node to be used
+ )
+ local el = el or self
+ return el:get_sibling_node(-1)
+ end
+
+
+ -- include the methods to all xml nodes
+ save_methods(parser._handler.root)
+ -- parser:
+ return parser
+end
+
+--- @export
+return {
+ parse = parse,
+ serialize_dom= serialize_dom
+}
diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-mod-xml.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-mod-xml.lua
index 4da0edc43bb..3e52ecec8f8 100644
--- a/Master/texmf-dist/tex/luatex/luaxml/luaxml-mod-xml.lua
+++ b/Master/texmf-dist/tex/luatex/luaxml/luaxml-mod-xml.lua
@@ -293,8 +293,14 @@ local xmlParser = function(handler)
break
end
end
+ local errorstring = tagstr:sub(errstart, errend)
+ -- it seems that it causes error if an attribute starts with `=`
+ if errorstring:match("^=") then break end
+
extstart,extend,endt2 = string.find(str,self._TAGEXT,endmatch+1)
- tagstr = tagstr .. string.sub(string,endmatch,extend-1)
+ if not extstart then break end
+
+ tagstr = tagstr .. string.sub(str,endmatch,extend-1)
if not match then
self:_err(self._errstr.xmlErr,pos)
end
@@ -362,6 +368,7 @@ local xmlParser = function(handler)
obj._DTD3 = '<!DOCTYPE%s+(.-)%s*(%b[])%s*>'
obj._DTD4 = '<!DOCTYPE%s+(.-)%s+(SYSTEM)%s+["\'](.-)["\']%s*>'
obj._DTD5 = '<!DOCTYPE%s+(.-)%s+(PUBLIC)%s+["\'](.-)["\']%s+["\'](.-)["\']%s*>'
+ obj._DTD6 = '<!DOCTYPE%s+(.-)%s*>'
--obj._DTD6 = "<!DOCTYPE%s+(.-)%s+(PUBLIC)%s+[\"'](.-)[\"']%s+[\"'](.-)[\"']%s*>"
obj._ATTRERR1 = '=%s*"[^"]*$'
@@ -453,6 +460,10 @@ local xmlParser = function(handler)
if m then
return m,e,{_root=r,_type=t,_uri=u}
end
+ m,e,r = string.find(s, self._DTD6, pos)
+ if m then
+ return m,e, {_root=r }
+ end
return nil
end
diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-parse-query.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-parse-query.lua
new file mode 100644
index 00000000000..95679670529
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luaxml/luaxml-parse-query.lua
@@ -0,0 +1,43 @@
+-- Source: https://github.com/leafo/web_sanitize
+-- Author: Leaf Corcoran
+local R, S, V, P
+do
+ local _obj_0 = require("lpeg")
+ R, S, V, P = _obj_0.R, _obj_0.S, _obj_0.V, _obj_0.P
+end
+local C, Cs, Ct, Cmt, Cg, Cb, Cc, Cp
+do
+ local _obj_0 = require("lpeg")
+ C, Cs, Ct, Cmt, Cg, Cb, Cc, Cp = _obj_0.C, _obj_0.Cs, _obj_0.Ct, _obj_0.Cmt, _obj_0.Cg, _obj_0.Cb, _obj_0.Cc, _obj_0.Cp
+end
+local alphanum = R("az", "AZ", "09")
+local num = R("09")
+local white = S(" \t\n") ^ 0
+local word = (alphanum + S("_-")) ^ 1
+local mark
+mark = function(name)
+ return function(...)
+ return {
+ name,
+ ...
+ }
+ end
+end
+local parse_query
+parse_query = function(query)
+ local tag = word / mark("tag")
+ local cls = P(".") * (word / mark("class"))
+ local id = P("#") * (word / mark("id"))
+ local any = P("*") / mark("any")
+ local nth = P(":nth-child(") * C(num ^ 1) * ")" / mark("nth-child")
+ local first = P(":first-child") / mark("first-child")
+ local attr = P("[") * C(word) * P("]") / mark("attr")
+ local selector = Ct((any + nth + first + tag + cls + id + attr) ^ 1)
+ local pq = Ct(selector * (white * selector) ^ 0)
+ local pqs = Ct(pq * (white * P(",") * white * pq) ^ 0)
+ pqs = pqs * (white * -1)
+ return pqs:match(query)
+end
+return {
+ parse_query = parse_query
+}
diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-selectors.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-selectors.lua
deleted file mode 100644
index 1e65d3b8058..00000000000
--- a/Master/texmf-dist/tex/luatex/luaxml/luaxml-selectors.lua
+++ /dev/null
@@ -1,76 +0,0 @@
---module(...,package.seeall)
-
-local M = {}
-local inside = 0
-local function makeTag(s)
- local function makeTag(fuf)
- return fuf .. "[^>]*"
- end
- local print = texio.write_nl
- if inside > 0 then print ("inside "..inside) else print("outside") end
- --[[if inside then
- return s .. "[^>]*"
- else
- inside = true--]]
- inside = inside + 1
- local f = "<"..s.."[^>]*>"
- inside = inside - 1
- return f
- --end
-end
-
-M.makeTag = makeTag
-local function matchTag(tg)
- return makeTag(tg)
-end
-M.matchTag=matchTag
-local function matchDescendand(a,b)
- return makeTag(a)..makeTag(b)
-end
-M.matchDescendand = matchDescendand
-
-local function matchChild(a,b)
- return makeTag(a)..".*"..makeTag(b)
-end
-M.matchChild = matchChild
-
-local function matchSibling(a,b)
- return a .. "[^>]*".."@%("..b.."[^>]*%)"
-end
-M.matchSibling = matchSibling
-
-local function matchClass(tg,class)
- return tg.."[^>]*class=[|]*[^>]*|"..class.."[^>]*|"
-end
-
-M.matchClass = matchClass
-local function matchId(tg,id)
- return tg.."[^>]*id="..id
-end
-M.matchId = matchId
-local matcher = {}
-M.matcher= matcher
-local function makeElement(s)
- local function makeTag(fuf)
- return fuf .. "[^>]*"
- end
- return "<"..s .. "[^>]*>"
-end
-
-M.makeElement = makeElement
-function matcher.new()
- local self = {}
- local selectors={}
- function self:addSelector(sel,val)
- selectors[sel.."$"] = val
- end
- function self:testPath(path,fn)
- for k, v in pairs(selectors) do
- if path:match(k) then
- fn(v)
- end
- end
- end
- return self
-end
-return M