--- CSS query module for LuaXML -- @module luaxml-cssquery -- @author Michal Hoftich " then -- simplest case, we need to match just the direct parent return match_query(query, el:get_parent()) elseif combinator == "+" then -- match previous element return match_query(query, get_previous_element(el)) elseif combinator == "~" then -- match all previous elements return match_sibling(query, get_previous_element(el)) elseif combinator == nil then return result end 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 a query table. -- XML namespaces can be supported using -- namespace|element syntax -- @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 if not atom[3] then value = atom[2] else -- save additional selector parts when available value = atom end -- support for XML namespaces in selectors -- the namespace should be added using "|" -- like namespace|element if key=="tag" then -- LuaXML doesn't support namespaces, so it is necessary -- to match namespace:element value=value:gsub("|", ":") end t[#t+1] = {key=key, value=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