Structural pseudo-classes such as :first-child,
:last-of-type and :nth-child(-n+5) now translate to
XPath that tests for a sibling at a given position, e.g.
not(preceding-sibling::*[1]), instead of counting them all
with count(preceding-sibling::*) = 0. The two forms match
the same elements, but the new one lets the XPath engine stop
early. With libxml2 (used by XML and xml2),
selecting from a list with thousands of siblings takes
milliseconds rather than seconds. Only the an part of
:nth-child(an+b) with a other than 1 or -1, and an
exact position together with an of S argument, still
count siblings. Code that compares the generated XPath text will
need updating.
With an of S argument, :nth-child() and
:nth-last-child() now test whether the element itself
matches S before counting its siblings, so elements that
do not match are rejected without the count. When most elements
fail S, selection is several times faster. The XPath for
:nth-child(an+b) also no longer includes a lower bound
that its mod test already implies, e.g. for 3n+2.
Nested of S arguments using such a formula now produce
much shorter XPath. Both changes alter the generated XPath text
but not which elements match.
selectr no longer depends on R6, or on any package other than base R. The translators are now built by a small internal class helper; the generated XPath is unchanged.
Translating a selector is about four times faster.
css_to_xpath() takes a quarter of the time it used to,
and so do querySelector() and its relatives on small
documents, where translation is most of the work. On large
documents, where most of the time goes to evaluating the XPath,
they save about a millisecond per call. Parsing is about seven
times faster, and simple selectors like "#main" or
"p.note" parse 10 to 15 times faster. Parse-tree nodes,
the token stream and intermediate XPath expressions are now
lightweight S3 objects and plain environments instead of R6
objects. Tokenizing takes a single regular-expression pass. The
generated XPath is unchanged.
Tokenizing a long selector that contains any non-ASCII character now takes time proportional to its length, as it already did for a pure-ASCII one.
A comment's opening * no longer also closes it:
"/*/" opens a comment that runs to the next */, or
to the end of the selector, as CSS specifies. It used to be read
as a complete comment, so "a/*/ b" selected b
elements inside a elements; it now selects a
elements, the rest being an unterminated comment.
The querySelector() family now accepts a set of
nodes, so queries can be chained: the xml2
xml_nodeset or XML XMLNodeSet returned by one
call can be passed to the next. The selector is evaluated from
each node in turn, and a node matching from more than one of them
is returned once, at the position it first matched.
The querySelector() family now uses the html
translator by default for documents parsed as HTML (by
XML::htmlParse() or xml2::read_html()), so names
match case-insensitively and :checked, :disabled,
:link and :lang() behave as in a browser without
passing translator = "html". An explicit translator
still wins, and the choice carries through a chain of queries.
An xml2 xml_missing, as returned by a failed
xml_find_first(), is now accepted by the
querySelector() family and yields no matches rather than an
error.
A new ?selectors help topic documents every selector,
combinator and pseudo-class selectr understands, which translators
support each, and where the implementation diverges from CSS
Selectors Level 4.
The html and xhtml translators now support
:read-write, :read-only, :placeholder-shown
and :default, which were previously unknown; on the
generic translator they never match. Pseudo-classes needing
live document state, such as :indeterminate, remain
unknown.
Parse errors now include a source-pointer block showing the
selector with a caret (^) beneath the offending character,
aligned correctly across tabs and wide characters.
Parse, translation and argument errors are now structured
conditions inheriting a common selectr_error class, so
callers need not match on message text:
selectr_parse_error (fields $selector,
$pos), selectr_translation_error (fields
$selector, $feature and $pos/$column),
and selectr_argument_error. Message text is unchanged.
A reported position - $pos and the caret - now points
at the token the message names rather than wherever the parser
stopped; for example the caret under "a||b" sits on the
first |, and under "a:before:empty" on the
:before called misplaced.
The minimum required version of R has been increased from
3.3 to 3.6, required by errorCondition().
css_to_xpath() now signals an error when
selector, prefix or translator have lengths
that are not all 1 or the common (maximum) length, instead of
recycling and silently mispairing them.
Generated XPath expressions are shorter and cheaper to
evaluate, with no change to the node set selected. Pseudo-class
translations drop disjuncts ruled out by the compound's element
name (prefixed and *|-qualified compounds included), name
the element before testing anything else of it, factor out
conditions every disjunct shares, and write enumerated
attribute-value sets as a single contains(); a compound
holding a never-matching simple selector folds to [0]
alone; and a condition or selector-list branch repeating an
earlier one exactly is kept once (":is(a, a)" gives
"*[self::a]"). Code comparing css_to_xpath() output
against stored strings will need updating.
Arithmetic in the generated XPath now has a space on both
sides of the operator ("(count(preceding-sibling::*) + 2)",
"string-length(@href) - 2"). The XPath is unchanged in
meaning, but the string differs.
Translating long selectors is measurably faster: the tokenizer matches against a bounded window of the remaining input rather than re-slicing it per token, and translator instances are cached and reused. The test suite has been migrated to testthat edition 3.
querySelector() no longer finds every match to return
the first: it asks XPath for the first node directly on XML
inputs, and stops at the first matching node of an
xml_nodeset. The node returned is unchanged.
The html translator now compares the values of the
attributes HTML defines as ASCII case-insensitive (type,
rel, lang, dir, ... - the list in HTML's
"Case-sensitivity of selectors") without regard to case, as a
browser does, so "input[type=radio]" selects
<input type="RADIO">. A trailing s flag asks for the
exact comparison back. class, id, href,
name, data-*, namespaced attributes and the
generic/xhtml translators are unaffected.
The html translator now lowercases element and
attribute names by folding A-Z only, as an HTML
parser does. tolower() also mapped non-ASCII letters, and
by locale, so a selector such as "\C4" became a name test
no document could match.
:placeholder-shown now requires a non-empty
placeholder and, for <input>, a type the
attribute applies to (so not checkbox, date,
submit, ...); both previously matched.
An <input> of type range, color,
submit, image, reset or button is now
neither :required nor :optional under the
html and xhtml translators, joining
type="hidden", since required does not apply to
them.
A large An+B value is now written into the generated
XPath in full. Values of five or more digits used R's default
number printing, so css_to_xpath(":nth-child(100001)")
produced "... = 1e+05", which XPath 1.0 has no syntax for
(libxml2 accepted it, so matches were correct but not portable).
An A or B too large for an R integer (e.g.
":nth-child(99999999999)") is now saturated to
.Machine$integer.max instead of raising an error; no
document has that many siblings, so the clamped series selects
the same nodes.
The html-qualified pseudo-classes (:checked,
:disabled, :enabled, :link/:any-link,
:optional, :required) now identify elements by local
name, matching the xhtml translator's rule for type
selectors; previously they matched nothing, or the wrong
elements, on a namespaced document. The html translator is
unaffected in practice.
querySelectorNS() and querySelectorAllNS() now
search only within the node given, as their un-namespaced
counterparts do: the namespace filter was an absolute path
(//svg:*) and is now relative
(descendant-or-self::svg:*). Queries from a document are
unaffected.
A selector with a long chain of combinators is now
translated by walking the parse tree in a loop rather than
recursively; beyond a few hundred combinators these failed with
"evaluation nested too deeply". Deeply nested functional
pseudo-classes (e.g. hundreds of :not()) now raise a
selectr_translation_error instead of letting the base R
error escape.
An unusually long selector is now translated without being cached, rather than failing with "variable names are limited to 10000 bytes" - the translation cache keys are R symbols.
A backslash at the very end of a selector is now decoded per css-syntax instead of raising an error: in an identifier or hash name it becomes U+FFFD, and inside an open string it is dropped and the string auto-closed.
A unicode escape decoding to a null, surrogate or
out-of-range code point (e.g. "\0", "\D800",
"\110000") now yields U+FFFD as css-syntax requires,
instead of NA or an empty string.
An+B expressions are now validated while parsing, so
a malformed value is reported with a source position and names the
pseudo-class it appeared in.
An+B expressions are now read from tokens rather than
decoded text, so a digit written as an escape is no longer taken
for an integer: ":nth-child(\32 )" and friends are
rejected as browsers reject them. Escapes spelling letters are
unaffected (":nth-child(\65 ven)" is still "even").
A number with an identifier written straight onto it is now
the single <dimension-token> css-syntax makes it, so
":nth-child(2n+1of b)" and ":nth-child(1of b)" are
rejected as browsers reject them. The spelling with a space, and
dimensions An+B does admit ("2n+1", "2n-1"),
are unaffected.
An ID selector's hash must now be identifier-shaped:
"#1", other hashes starting with a digit or - plus a
digit, and a lone "#-" are rejected with a hint on
escaping, instead of being silently mistranslated.
A lone - is now a delimiter rather than a name, so
"-", "a - b" and ".-" are rejected instead of
translated into an expression nothing can match. Names that do
start with - ("-a", "--a", "--",
"\-") are unaffected, as is the sign in
":nth-child(2n - 1)".
--> and <!-- are now read as the single CDC
and CDO tokens CSS makes them and rejected whole at their starting
position. A --> inside a name is unchanged, so
"a-->b" still selects a b child of an a--.
An empty :is() or :where() argument list is
now accepted per its <forgiving-selector-list> grammar and
matches nothing, instead of raising a parse error.
:lang() ranges must now be comma-separated;
whitespace alone (":lang(en fr)") is no longer a separator.
Multi-token wildcard ranges such as ":lang(*-CH)" are
unaffected.
:lang(*) now matches only elements whose language is
known - one inheriting a non-empty lang or xml:lang
- instead of every element.
The xhtml translator's :lang() now also reads
xml:lang, preferring it over lang on the nearest
declaring ancestor-or-self, per HTML's language-determination
rules.
An <option> in a disabled <optgroup>, and an
<optgroup> or <option> whose nearest ancestor
<select> is disabled, are now :disabled under the
html and xhtml translators without an attribute of
their own, per HTML's "actually disabled". The walk stops where
the standard stops, so an <option> in a <datalist>
stays :enabled.
An <input type="hidden"> is now :disabled when
it carries disabled (or sits in a disabled
<fieldset>) and :enabled otherwise; previously it
was neither, a carve-out HTML does not make. The
:required/:optional carve-out is unchanged.
A <fieldset> inside a disabled <fieldset> is
now :disabled, following the rule already applied to form
controls (descendants outside the first <legend> child).
:link and :any-link no longer match a
<link> element with an href: HTML's hyperlink
elements for these pseudo-classes are <a> and <area>
only, so "link:link" now matches nothing.
The of-type pseudo-classes now reject a namespaced wildcard
subject (e.g. "svg|*:first-of-type") with a translation
error, instead of silently counting siblings by namespace rather
than expanded name.
An unprefixed element name that cannot be written as an
XPath name test is now pinned to the null namespace ("é"
gives *[name() = 'é' and namespace-uri() = '']), giving it
the same meaning as a plain "p"; previously it also matched
in a default namespace.
An element or attribute name whose local part cannot
be written as an XPath name test now keeps its prefix in the node
test and compares the local part alone ("svg|di\[v" gives
svg:*[local-name() = 'di[v']), so the prefix resolves
through the supplied namespace map instead of matching whatever
prefix the document happens to use.
A namespace prefix that is not a valid XPath NCName
(e.g. "\31 ns|div") is now rejected with a translation
error, since the name() comparison previously emitted
tested the document's spelling rather than the bound URI. A
non-ASCII prefix (e.g. "äöü|a") is now translated instead.
NA and empty-string values in the ns argument
of the querySelectorNS() family are now rejected rather
than used as a namespace URI.
A name in the ns argument that is not a valid
XML NCName (e.g. c("s p" = "urn:s")) is now
rejected; previously it was interpolated into XPath that failed to
compile, giving a libxml2 warning and no matches. The condition
matches the one applied to prefixes written in selectors.
A zero-length ns argument (character(0) or
list()) to the xml2 querySelector() family now
means "no namespaces" and skips the xml_ns() walk, rather
than behaving like NULL.
A selector or prefix holding bytes invalid in
their declared encoding is now rejected with a
selectr_argument_error, instead of surfacing base R's
"invalid multibyte string".
An invalid translator argument now raises a message
naming the translator argument and the value passed,
instead of match.arg()'s message naming an internal
argument. Unambiguous abbreviations are still accepted.
p:nth-of-type(n of .a) and the other of-type
pseudo-classes now report the same "'of' is only allowed in
:nth-child() and :nth-last-child()" error as
:nth-child(), instead of a confusing "Expected an
argument".
:nth-child(0) (and other An+B with
a == 0 and b < 1) now gives the tidy [0]
early exit rather than
[count(preceding-sibling::*) = -1].
A handful of parser and argument messages have been
reworded. Positions embedded in message text no longer duplicate
the caret gutter (they are still given, as "at position N", where
the gutter is suppressed). Four messages now name the actual
problem: an R-level XML tree is reported as one; a
functional pseudo-element such as ::part() is reported as
unsupported; div.5 gets the escaping hint #5
already had; and [data-id=1] is told to quote the value.
The html and xhtml :lang() now
implements Selectors 4 / RFC 4647 extended filtering, so
:lang(de-DE) matches lang="de-Latn-DE". The
generic translator keeps Selectors 3 prefix matching, as
XPath 1.0's lang() cannot express skipped subtags.
:lang("") now matches an untagged element per spec, rather
than only a literal lang="".
A :lang() range that is not an RFC 4647 extended
language range (e.g. ":lang(en-)", ":lang(--x)",
":lang(en*)") is now rejected, naming the offending range;
previously such ranges were normalised and over-matched.
An empty item in the comma-separated :lang()
argument list (":lang(en, )") is now rejected rather than
silently dropped, as the <ident># grammar requires.
A :lang() range of nothing but wildcards
(":lang(\"*-*\")") now matches any element with a known
language, as ":lang(*)" does, rather than producing an
empty predicate.
An escaped * before a | is now rejected as the
namespace prefix it is: only the delimiter * means "any
namespace", so "\2a|a" and "[\2a|href]" now raise
"The namespace prefix '*' is not an XPath name ...", where they
previously matched every namespace. "*|a" and
"[*|href]" are unchanged.
"*|\2a" now selects an element named * in any
namespace, as "ns|\2a" and "\2a" already did; the
local name was tested against the character * rather than
for being the universal selector, so the selector matched every
element.
Added support for case-sensitivity flags in attribute
selectors: [attr="value" i] matches the attribute value
ASCII case-insensitively, while [attr="value" s]
explicitly requests the default case-sensitive matching.
The :required and :optional pseudo-classes
are now supported. With the html or xhtml
translator they match form elements (input other than
type="hidden", select, textarea) by the
presence or absence of the required attribute; with the
generic translator they translate to a never-matching
expression, as :checked does.
The :focus-within and :focus-visible
pseudo-classes are now accepted and, like the rest of the
user-action family (:focus, :hover, :active,
...), translate to a never-matching expression, since a static
document has no such dynamic state. They previously raised "The
pseudo-class ... is unknown".
Added support for a leading :scope pseudo-class,
anchoring the selector at the queried node:
querySelectorAll(node, ":scope > a") returns only the
a children of node. css_to_xpath()
translates such selectors with the XPath self axis in
place of the prefix argument (":scope > a" becomes
"self::*/a"). A :scope anywhere else in a selector
cannot be expressed in XPath 1.0 and is rejected with a clear
error.
:not() may now appear inside the arguments of
functional pseudo-classes (e.g. :not(:not(a)),
:is(:not(.x)), :nth-child(2 of :not(.foo))).
:has() now supports leading combinators in its
arguments (e.g. e:has(> img), e:has(~ p),
e:has(+ p)).
Functional pseudo-class arguments may now be complex
selectors (contain combinators): :is(a b),
:not(a > b), e:has(> a b), and
li:nth-child(2 of ol li) previously produced an
"Expected an argument" error and are now translated.
The performance of selectr has been improved roughly 2x. There are several smaller changes that have contributed to this.
The unsupported Selectors 4 column combinator now raises
"The column combinator '||' is not supported" instead of a raw
tokenizer error, and an unknown functional pseudo-class is
reported with the user's hyphenated spelling
(:nth-col(), previously :nth_col()).
Generated XPath expressions now include parentheses only
when precedence requires them (an or-expression joined with
another condition): e[id][title] translates to
e[@id and @title] rather than
e[(@id) and (@title)]. The expressions are semantically
unchanged, but code comparing css_to_xpath() output
against stored strings will need updating.
Attribute blocks, functional pseudo-classes, and strings
left unclosed at the end of a selector are now auto-closed, as
css-syntax requires: [rel=stylesheet and :lang(fr
parse and translate exactly as their closed forms, as does a
string missing its closing quote.
The stringr and methods dependencies have been dropped. R6 is now the only package selectr imports, shrinking the install footprint.
css_to_xpath() now translates each distinct
combination of selector, prefix, and translator only once per
call, so duplicates in a vectorized call are not re-translated.
The adjacent sibling combinator no longer emits a
tautological [self::*] predicate when the right-hand side
does not name an element: h1 + *[rel=up] now translates to
h1/following-sibling::*[1][@rel = 'up'].
The minimum required version of R has been increased from
3.0 to 3.3, matching what the code (which uses
startsWith()) has in fact required for some time.
The non-standard extensions inherited from the Python
'cssselect' package — the :contains("text") pseudo-class
and the [attr!=value] attribute operator — have been
removed and now produce an error. Standard alternatives:
:not([attr=value]) for the former operator, and an XPath
contains(., 'text') predicate applied outside of
selectr for :contains().
With the html or xhtml translator,
:any-link now matches the same elements as :link.
:any-link means ":link or :visited", a superset of
:link, but it previously translated to a never-matching
expression while :link matched - the subset relation
inverted. (In a static document no link is visited, so the two
pseudo-classes coincide.)
Escape sequences in identifiers, hashes, and strings are
now decoded in a single left-to-right pass, as css-syntax
requires, so an escaped backslash followed by hex digits is no
longer decoded twice: e[foo="x\\79 z"] now matches
x\79 z rather than xyz.
A prefixed wildcard inside a pseudo-class argument
(e.g. :is(svg|*)) now translates to the node test
self::svg:* rather than the never-matching comparison
name() = 'svg:*'.
When :lang() is given an invalid argument after
valid ones (e.g. :lang(en, 5)), the error now reports the
offending argument rather than the first one.
Strings containing a raw newline are now rejected with "Unclosed string", as the CSS grammar requires.
The namespace argument of the querySelector() family
of functions now signals an error when given a list containing an
element that is not a single string; previously the prefix-to-URI
pairing was silently corrupted.
:only-child and :only-of-type now match the
root element, consistent with :first-child:last-child,
which Selectors defines :only-child to be equivalent to.
The HTML translator's :enabled and :disabled
pseudo-classes now match input elements that have no
type attribute (which default to type=text).
:dir() now enforces its CSS Selectors Level 4
argument grammar of exactly one identifier, rejecting the
strings, wildcards, and lists (e.g. :dir(ltr, rtl)) it
previously accepted via the shared :lang() grammar.
Prefixed element names inside pseudo-class arguments
(e.g. :is(svg|g), :nth-child(2 of svg|g)) are now
matched with an XPath name test (self::svg:g, or the path
step .//svg:g for :has()) instead of a
name() string comparison, so the prefix resolves through
the namespace map supplied at evaluation time (URI-based), just
as at the top level of a selector.
The no-namespace form |e now retains its namespace
constraint when the element name cannot be written as an XPath
name test (e.g. a Unicode name such as |é); previously
such names also matched in a default namespace.
The of-type pseudo-classes now work on element names that
cannot be written as an XPath name test
(e.g. é:only-of-type, *|e:first-of-type);
previously these failed with the misleading error
"*:only-of-type is not implemented".
The querySelector() and querySelectorAll()
methods for xml2 documents now accept a named list as the
ns argument, consistent with the methods for XML
documents.
The querySelector() family of functions now signals
an error when the selector argument is not a single
character string; previously all but the first selector were
silently ignored when querying the document.
css_to_xpath() now signals an error when any of its
arguments contain NA values; previously NAs were
removed before recycling, silently shifting the pairing of the
remaining values.
An+B expressions are now matched ASCII
case-insensitively, as required by CSS Syntax, so
:nth-child(2N), :nth-child(ODD), and
:nth-child(EVEN) are no longer rejected.
An+B expressions now only permit whitespace around
the sign that separates the B value, so an invalid
selector such as :nth-child(3 7) is rejected rather than
silently treated as :nth-child(37).
An+B expressions now reject non-integer values
(e.g. :nth-child(1.9), :nth-child(2e1)) rather
than silently truncating them.
Computing the specificity of a :has() selector with
a single argument (e.g. e:has(img)) no longer fails with
"incorrect number of dimensions".
Computing the specificity of a single-argument
:is() or :matches() selector no longer fails, and
the specificity of the compound the pseudo-class is attached to
is no longer dropped: div:is(.foo) now reports (0, 1, 1)
rather than (0, 1, 0).
Nesting :has() inside :has()
(e.g. section:has(article:has(div))) is now rejected, as
required by CSS Selectors Level 4 and matching browsers; sibling
uses such as e:has(a):has(b) remain valid.
:lang() and :dir() no longer accept a lone
-, which is not a valid CSS identifier.
Attribute selectors with an empty value
(e.g. [attr=""]) no longer throw an error for the
= and |= operators.
The HTML translator no longer lowercases attribute
values, only attribute names, so
[data-state="Active"] no longer silently misses matches.
The any namespace selector *|e and the
no namespace selector |e no longer both collapse
to the bare name e: *|e now translates to
*[local-name() = 'e'] so it matches e in any
namespace, and similarly for [*|attr].
Unicode escapes are now supported in identifiers and ID
selectors, not just strings, and are decoded to the characters
they represent: #\31 23 (an ID starting with a digit) no
longer fails to tokenize, and "\E9" matches é
rather than the literal value E9.
The alternatives of :is(), :matches(), and
:where() are now grouped as a single condition:
div.foo:is(.a, .b) translates to foo and (a or b)
rather than foo or a or b, and stacked uses such as
e:is(.a):is(.b) now require both conditions rather than
either.
The universal selector * is no longer silently
dropped when it appears alongside other arguments in a selector
list: :is(div, *) now matches every element,
:not(div, *) matches nothing, and
:nth-child(2 of div, *) counts all siblings.
Wildcard language ranges such as :lang(en-*) now
match under the generic (XML) translator; previously they
translated to lang('en-'), which can never match.
Fixed handling of CSS unicode escapes in attribute values.
This would be observed when the attribute value contained hexadecimal
sequences like (abcdef) where the characters inside the parentheses
were not properly escaped. This fix ensures that such sequences are correctly
translated to their XPath equivalents. Thanks to André Veríssimo for
reporting the issue.
Added support for CSS Selectors Level 4 pseudo-classes
:is(), :where(), and :has(). The :is()
pseudo-class matches elements against a list of selectors, taking the
maximum specificity from its arguments. The :where() pseudo-class
works similarly to :is() but always has zero specificity. The
:has() pseudo-class represents an element if any of the relative
selectors match when anchored against that element, with specificity
calculated from the maximum of its arguments.
Added support for complex selectors in :nth-child() and
:nth-last-child() using the of S syntax
(e.g., :nth-child(2 of .foo)). This allows matching the nth
child that matches a specific selector or selector list.
Extended :not() to accept multiple selectors separated by
commas (e.g., :not(.foo, #bar)), following CSS Selectors Level 4.
Specificity is now calculated as the maximum specificity among all
arguments, rather than the sum.
Added support for additional CSS Selectors Level 4 pseudo-classes:
:any-link, :target-within, and :local-link. These
pseudo-classes do not match any elements in static XML/HTML documents
and translate to XPath expressions that always evaluate to false.
For now, most of the new Level 4 pseudo-classes that depend on dynamic
document state e.g. :user-valid and :placeholder-shown are
not implemented, but may be at a future date to be non-matching selectors.
The :lang() and :dir() pseudo-classes now support
multiple comma-separated arguments (e.g., :lang(en, fr, de)).
Added :matches() as a backwards-compatible alias for
:is().
Improved sibling selector translation to use a more compact form.
For the adjacent sibling combinator a + b, the generated XPath now
uses a/following-sibling::*[1][self::b] instead of
a/following-sibling::*[(name() = 'b') and (position() = 1)].
The descendant combinator a b now uses
a//b instead of a/descendant::b for a more concise XPath.
Unfortunately a similar optimisation cannot be applied to in general
when attempting to replace descendant-or-self:: with .//a
as it would prevent root nodes being matched correctly.
Improved validation of CSS selector arguments. Better error
messages are now provided when pseudo-elements appear inside functional
pseudo-classes where they are not permitted (e.g., inside :is(),
:matches(), :where(), or :has()).
Enhanced input validation for :lang() and :dir()
pseudo-classes to ensure proper argument formatting and to reject
invalid or empty language tags.
Improved handling of edge cases in selector parsing, including better validation of class selector syntax and more robust handling of null or missing element components.
Simplified method registration for XML and xml2 objects. No longer necessary to hook into package load/unload events.
Improve handling of vectors of length > 1 in logical comparison. Contributed by Garrick Aden-Buie.
Minor improvements to error message construction. Contributed by Michael Chirico.
When the R.oo package is attached, the use of class
selectors no longer worked. This is due to the use of the Class
name for R.oo's base class object, where selectr was also
using it (but not exporting) the same name of Class for
representing a class selector. Consequently, selectr's code was
changed to rename the class to avoid any clashing. Because it was not
exported, this is purely an internal change. Thanks to
Francois Lemaire-Sicre for reporting the issue.
Large rewrite of internals to use the R6 OO system instead of Reference Classes. This does not affect any external facing code as the results should be identical to the previous implementation, which is why this change is marked as minor. Initial and crude performance testing (by running the test suite) indicates that the R6 implementation is approximately twice as fast at generating XPath as the Reference Classes implementation.
The minimum required version of R for selectr has been
increased from 2.15.2 to 3.0 as that is the minimum
required version of R6.
Minor performance enhancements have been made. Not only is
R6 faster than Reference Classes, the use of string formatting
has been replaced with string concatenation. Additionally dynamic
calling of methods via do.call() has been replaced with direct
method calls.
The issues in previous releases where methods can sometimes be missing should now be resolved. The bug appeared to lie in core Reference Classes code. By switching to R6, this type of issue should no longer be possible.
Improved method registration for XML and xml2 objects. Avoids checks on each use and is only performed once per dependent package load/unload.
In some environments, reference class methods were missing at
runtime. This appears to be due to some internal behaviour in them
methods package where methods are registered on an objects when
the $ operator is used for a field or method. Instead, when
a method is missing, they are manually bound to the object.
Enabled partial matching on the translator argument to
css_to_xpath(). Instead of defaulting to a generic translator,
a non-matching argument will be returned with an error.
Introduced many more unit tests via the covr package.
This enabled dead code to be trimmed and also identified areas of code
which needed improvement. Minor enhancements include: tolerate
whitespace within a :not(), more consistent results returned
from parser methods, improvements to argument parsing.
The |= attribute matching operator was not being parsed
correctly for the generic translator.
Handle scenario where a CSS comment is unclosed. Results in everything after the comment start to be removed (which may or may not result in a valid selector).
Added support for documents from the xml2 package.
selectr now also does not strictly depend on the XML
package. If either the XML or xml2 packages are
present (which are required for the querySelector methods
to work) then querySelector will begin to work for them.
This also enables selectr to be used for translation-only.
Improve support for nth-*(an+b) selectors. Ported from cssselect.
Code cleanup contributed by Kun Ren (#1).
Updated DESCRIPTION to include URL and BugReports fields. Also update email address.
Fix behaviour for nth-*(an+b) pseudo-class selectors for negative a's. Contributed to cssselect by Paul Tremberth, ported to R.
Escape delimiting characters to support new version of the stringr package. Probably should have been done in the first place. Reported by Hadley Wickham (#5).
Corrected licence to BSD 3 clause. This was the licence in use previously, but has now been made more explicit.
Removed 'Enhances' field because we import functions from XML. This choice is made because XML is a required package, rather than an optional package that can be worked with. This and the previous change have been made to keep up with recent changes in R-devel.
Added a 'CITATION' file which cites a technical report on the package.
show() methods are now available on internal objects,
making interactive extensibility and bug-fixing easier. This is
simply wrapping the repr() methods (mirroring the Python
source) that the same objects have.
Use the session character encoding to determine whether to run unicode tests. Tests break in non-unicode sessions otherwise.
Introduced new functions querySelectorNS() and
querySelectorAllNS() to ease the use of namespaces within a
document. Previously this would have required knowledge of XPath.
Fix meaning of :empty, whitespace is not empty.
Use lang() for XML documents with the :lang()
CSS selector.
|ident no longer produces a parsing error, but is now
equivalent to just 'ident'.
Now testing unicode only in non-Windows platforms on package check. Output should still be consistent, just depends on the current charset being unicode.
Initial port of the Python 'cssselect' package. Code is very literally ported, including the test suite.
Wrapped translation functionality into a single function,
css_to_xpath().
Created two convenience functions, querySelector() and
querySelectorAll(). These mirror the behaviour of the same
functions present in a web browser. querySelector() returns a
node, while querySelectorAll() returns a list of nodes.