UNPKG

stew-select

Version:

CSS selectors that allow regular expressions. Stew is a meatier soup.

653 lines (500 loc) 36.8 kB
<!DOCTYPE html> <html> <head> <title>stew.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <ul id="jump_to"> <li> <a class="large" href="javascript:void(0);">Jump To &hellip;</a> <a class="small" href="javascript:void(0);">+</a> <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="dom-util.html"> dom-util.coffee </a> <a class="source" href="predicate-factory.html"> predicate-factory.coffee </a> <a class="source" href="stew.html"> stew.coffee </a> </div> </li> </ul> <ul class="sections"> <li id="title"> <div class="annotation"> <h1>stew.coffee</h1> </div> </li> <li id="section-1"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-1">&#182;</a> </div> </div> <div class="content"><div class='highlight'><pre>fs = require <span class="string">'fs'</span> path = require <span class="string">'path'</span> HOMEDIR = path.join(__dirname,<span class="string">'..'</span>) LIB_DIR = <span class="keyword">if</span> fs.existsSync(path.join(HOMEDIR,<span class="string">'lib-cov'</span>)) <span class="keyword">then</span> path.join(HOMEDIR,<span class="string">'lib-cov'</span>) <span class="keyword">else</span> path.join(HOMEDIR,<span class="string">'lib'</span>) DOMUtil = require(path.join(LIB_DIR,<span class="string">'dom-util'</span>)).DOMUtil PredicateFactory = require(path.join(LIB_DIR,<span class="string">'predicate-factory'</span>)).PredicateFactory</pre></div></div> </li> <li id="section-2"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-2">&#182;</a> </div> <p><strong>Stew</strong> is a DOM selection engine that supports the full CSS selector syntax as well as CSS selectors extended with regular expressions.</p> <p>Method names that start with <code>_</code> are subject to change without notice. Other methods may be considered a part of the public API.</p> </div> <div class="content"><div class='highlight'><pre><span class="class"><span class="keyword">class</span> <span class="title">Stew</span></span></pre></div></div> </li> <li id="section-3"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-3">&#182;</a> </div> <p><strong>The Stew constructor</strong> accepts an optional <code>DOMUtil</code> instance (allowing callers to configure the <code>DOMUtil</code> used by <code>Stew</code>).</p> </div> <div class="content"><div class='highlight'><pre> constructor:(dom_util)-&gt; <span class="property">@factory</span> = <span class="keyword">new</span> PredicateFactory() <span class="property">@dom_util</span> = dom_util ? <span class="keyword">new</span> DOMUtil()</pre></div></div> </li> <li id="section-4"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-4">&#182;</a> </div> <p><strong>select</strong> selects nodes from the given <code>dom</code> that match the given <code>selector</code>.</p> <p>If <code>selector</code> is a string, it will be parsed as described in the README. Otherwise <code>selector</code> is assumed to be a predicate function (like those generated by <code>PredicateFactory</code>).</p> <p>If <code>dom</code> is a string, it will be parsed as HTML (using <code>DOMUtil.parse_html</code>, which see). If <code>dom</code> is a single node, the given <code>selector</code> will be applied to it. If <code>dom</code> is an array of nodes, the given <code>selector</code> will be each element in turn.</p> <p>This results in an array of matching nodes.</p> <p>If a <code>callback</code> is provided, the resulting array is passed to it (assuming the signature <code>callback(err,nodeset)</code>). Otherwise the resulting array is returned by this function.</p> <p>Note that when <code>dom</code> is a string, a callback method <em>must</em> be provided. (Since our HTML parsing is asynchronous.) When <code>dom</code> is an object, the callback method is optional (but will be used when present)</p> </div> <div class="content"><div class='highlight'><pre> select:(dom,selector,callback)-&gt; <span class="keyword">if</span> <span class="keyword">typeof</span> selector <span class="keyword">is</span> <span class="string">'string'</span> selector = <span class="property">@_parse_selectors</span>(selector) <span class="keyword">if</span> <span class="keyword">typeof</span> dom <span class="keyword">is</span> <span class="string">'string'</span> <span class="keyword">if</span> callback? <span class="property">@dom_util</span>.parse_html dom, (err, dom)=&gt; <span class="keyword">if</span> err? callback(err) <span class="keyword">else</span> callback(<span class="literal">null</span>,<span class="property">@_unguarded_select</span>(dom,selector)) <span class="keyword">else</span> <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'When select is invoked on a string object, the `callback(err,nodeset)` parameter is required.'</span>) <span class="keyword">else</span> nodeset = <span class="property">@_unguarded_select</span>(dom,selector) callback?(<span class="literal">null</span>,nodeset) <span class="keyword">return</span> nodeset</pre></div></div> </li> <li id="section-5"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-5">&#182;</a> </div> <p><strong>_unguarded_select</strong> is the &quot;inner&quot; method for <code>select</code>. It assumes <code>dom</code> is a node or array of nodes and that <code>predicate</code> is a predicate function. It returns an array of matching nodes. (Generally this method will not be directly called by clients.)</p> </div> <div class="content"><div class='highlight'><pre> _unguarded_select:(dom,predicate)-&gt; result = [] <span class="function"><span class="title">visit</span></span> = (node,parent,path,siblings,sib_index)-&gt; <span class="keyword">if</span> predicate(node,parent,path,siblings,sib_index) result.push node <span class="keyword">return</span> { <span class="string">'continue'</span>:<span class="literal">true</span>, <span class="string">'visit_children'</span>:<span class="literal">true</span> } <span class="property">@dom_util</span>.walk_dom dom, visit:visit <span class="keyword">return</span> result</pre></div></div> </li> <li id="section-6"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-6">&#182;</a> </div> <p><strong>select_first</strong> selects the first node in the given <code>dom</code> that matches the given <code>selector</code>.</p> <p>It behaves exactly like <code>select</code> (which see) save that it aborts processing as soon as the first matching node is found, and returns a single node rather than an array of nodes.</p> </div> <div class="content"><div class='highlight'><pre> select_first:(dom,selector,callback)-&gt; <span class="keyword">if</span> <span class="keyword">typeof</span> selector <span class="keyword">is</span> <span class="string">'string'</span> selector = <span class="property">@_parse_selectors</span>(selector) <span class="keyword">if</span> <span class="keyword">typeof</span> dom <span class="keyword">is</span> <span class="string">'string'</span> <span class="keyword">if</span> callback? <span class="property">@dom_util</span>.parse_html dom, (err, dom)=&gt; <span class="keyword">if</span> err? callback(err) <span class="keyword">else</span> callback(<span class="literal">null</span>,<span class="property">@_unguarded_select_first</span>(dom,selector)) <span class="keyword">else</span> <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'When select_first is invoked on a string object, the `callback(err,node)` parameter is required.'</span>) <span class="keyword">else</span> node = <span class="property">@_unguarded_select_first</span>(dom,selector) callback?(<span class="literal">null</span>,node) <span class="keyword">return</span> node</pre></div></div> </li> <li id="section-7"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-7">&#182;</a> </div> <p><strong>_unguarded_select_first</strong> is the &quot;inner&quot; method for <code>select_first</code>. (Generally this method will not be directly called by clients.)</p> </div> <div class="content"><div class='highlight'><pre> _unguarded_select_first:(dom,predicate)-&gt; result = <span class="literal">null</span> <span class="function"><span class="title">visit</span></span> = (node,parent,path,siblings,sib_index)-&gt; <span class="keyword">if</span> predicate(node,parent,path,siblings,sib_index) result = node <span class="keyword">return</span> { <span class="string">'continue'</span>:<span class="literal">false</span>, <span class="string">'visit_children'</span>:<span class="literal">false</span> } <span class="keyword">else</span> <span class="keyword">return</span> { <span class="string">'continue'</span>:<span class="literal">true</span>, <span class="string">'visit_children'</span>:<span class="literal">true</span> } <span class="property">@dom_util</span>.walk_dom dom, visit:visit <span class="keyword">return</span> result</pre></div></div> </li> <li id="section-8"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-8">&#182;</a> </div> <p><strong>_SPLIT_ON_WS_REGEXP</strong> is regular expression that is used to split a string of CSS selectors into individual selectors. It is similiar to <code>str.split(/\s/)</code>, but: - treats &quot;quoted phrases&quot; (and <code>/regular expressions/</code>) as a single token - also splits on the CSS &quot;operators&quot; of <code>&gt;</code>, <code>+</code>, <code>,</code> and <code>~</code> (Shout-out to <a href="http://stackoverflow.com/questions/2817646/javascript-split-string-on-space-or-on-quotes-to-array">http://stackoverflow.com/questions/2817646/javascript-split-string-on-space-or-on-quotes-to-array</a> from which this expression was originally derived.)</p> </div> <div class="content"><div class='highlight'><pre> _SPLIT_ON_WS_REGEXP = <span class="regexp">/([^\"\/\s,\+&gt;]|(\"[^\"]+\")|(\/[^\/]+\/)|(\[[^\]]*\]))+|[,\+~&gt;]/g</span></pre></div></div> </li> <li id="section-9"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-9">&#182;</a> </div> <p><strong>_split_on_ws_respecting_quotes</strong> is used to split a string of CSS selectors into individual selectors.</p> </div> <div class="content"><div class='highlight'><pre> _split_on_ws_respecting_quotes:(selector)-&gt; result = [] <span class="keyword">while</span> <span class="literal">true</span> token = _SPLIT_ON_WS_REGEXP.exec(selector) <span class="keyword">if</span> token?[<span class="number">0</span>]? result.push(token[<span class="number">0</span>]) <span class="keyword">else</span> <span class="keyword">break</span> <span class="keyword">return</span> result</pre></div></div> </li> <li id="section-10"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-10">&#182;</a> </div> <p><strong>_parse_selectors</strong> accepts a string containing one or more CSS selectors and returns the corresponding predicate (a boolean-valued function with the signature <code>(node,node_metadata,all_metadata)</code>)</p> </div> <div class="content"><div class='highlight'><pre> _parse_selectors:(selectors)-&gt; result = [] <span class="keyword">if</span> <span class="keyword">typeof</span> selectors <span class="keyword">is</span> <span class="string">'string'</span> selectors = <span class="property">@_split_on_ws_respecting_quotes</span>(selectors) child_operator = <span class="literal">false</span> <span class="comment"># TODO there is probably a more elegant way to handle `&gt;`, `+` and `,` here.</span> adjacent_operator = <span class="literal">false</span> preceding_sibling_operator = <span class="literal">false</span> or_operator = <span class="literal">false</span> <span class="keyword">for</span> selector <span class="keyword">in</span> selectors <span class="keyword">if</span> selector <span class="keyword">is</span> <span class="string">'&gt;'</span> child_operator = <span class="literal">true</span> <span class="keyword">else</span> <span class="keyword">if</span> selector <span class="keyword">is</span> <span class="string">'+'</span> adjacent_operator = <span class="literal">true</span> <span class="keyword">else</span> <span class="keyword">if</span> selector <span class="keyword">is</span> <span class="string">'~'</span> preceding_sibling_operator = <span class="literal">true</span> <span class="keyword">else</span> <span class="keyword">if</span> selector <span class="keyword">is</span> <span class="string">','</span> or_operator = <span class="literal">true</span> <span class="keyword">else</span> predicate = <span class="property">@_parse_selector</span>(selector) <span class="keyword">if</span> child_operator result.push( <span class="property">@factory</span>.direct_descendant_predicate( result.pop(), predicate ) ) child_operator = <span class="literal">false</span> <span class="keyword">else</span> <span class="keyword">if</span> adjacent_operator result.push( <span class="property">@factory</span>.adjacent_sibling_predicate( result.pop(), predicate ) ) adjacent_operator = <span class="literal">false</span> <span class="keyword">else</span> <span class="keyword">if</span> preceding_sibling_operator result.push( <span class="property">@factory</span>.preceding_sibling_predicate( result.pop(), predicate ) ) preceding_sibling_operator = <span class="literal">false</span> <span class="keyword">else</span> <span class="keyword">if</span> or_operator result.push( <span class="property">@factory</span>.or_predicate( [ result.pop(), predicate ] ) ) or_operator = <span class="literal">false</span> <span class="keyword">else</span> result.push( predicate ) <span class="keyword">if</span> result.length &gt; <span class="number">0</span> result = <span class="property">@factory</span>.descendant_predicate(result) <span class="keyword">return</span> result</pre></div></div> </li> <li id="section-11"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-11">&#182;</a> </div> <p><strong>_CSS_SELECTOR_REGEXP</strong> is a regular expression for parsing an individual CSS selector (which might include a tag name, an ID, one or more classes, one or more attributes and a pseudo class).</p> <p><code>&quot;tag#id.class-one.class-two[name~=\&quot;value with spaces\&quot;]&quot;.match(_CSS_SELECTOR_REGEXP)</code></p> </div> <div class="content"><div class='highlight'><pre> <span class="comment">#{ TODO: Combine the `id` and `class` rules to make them order-indepedent? (I think CSS specifies the order, but still.)</span> <span class="comment">#{############################################################################################################################################################################################################################################################################</span> <span class="comment">#{ 11 1 11 11 1 112 2 2 2 2 2 22 22 3 33 3 3 3 #</span> <span class="comment">#{ 12 3 4 56 7 89 01 2 34 56 7 890 1 2 3 4 5 67 89 0 12 3 4 5 #</span> _CSS_SELECTOR_REGEXP: <span class="regexp">/((\/[^\/]*\/[gmi]*)|(\*|[\w-]+))?(\#((\/[^\/]*\/[gmi]*)|([\w-]+)))?((\.((\/[^\/]*\/[gmi]*)|([\w-]+)))*)((\[((\/[^\/]*\/[gmi]*)|([\w-]+))(((=)|(~=)|(\|=)|(\*=)|(\^=)|(\$=))(("(([^\\"]|(\\"))*)")|((\/[^\/]*\/[gmi]*)|([\w- :]+))))?\])*)(:([\w-]+))?/</span> <span class="comment">#</span> <span class="comment">#{ \-name--------------------------/|\-id-----------------------------/\-class(es)-----------------------/|| \-attr-name-----------------/|\-operator----------------------/\-value-----------------------------------------------/| | |\-pseduo--/ #</span> <span class="comment">#{ || \-operator-and-value---------------------------------------------------------------------/ | | #</span> <span class="comment">#{ |\-attr-clause-([])----------------------------------------------------------------------------------------------------------/ | #</span> <span class="comment">#{ \-attr-clauses-([][]...)-------------------------------------------------------------------------------------------------------/ #</span> <span class="comment">#{############################################################################################################################################################################################################################################################################</span></pre></div></div> </li> <li id="section-12"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-12">&#182;</a> </div> <p>Indices of the important captured groups.</p> </div> <div class="content"><div class='highlight'><pre> _NAME = <span class="number">1</span> _ID = <span class="number">4</span> _CLASSES = <span class="number">8</span> _ATTRIBUTES = <span class="number">13</span> _PSEUDO_CLASS = <span class="number">35</span></pre></div></div> </li> <li id="section-13"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-13">&#182;</a> </div> <p><strong>_ATTRIBUTE_CLAUSE_REGEXP</strong> is a regular expression used to split one or more <code>[&lt;name&gt; &lt;op&gt; &lt;value&gt;]</code> expressions into individual components.</p> </div> <div class="content"><div class='highlight'><pre> <span class="comment">#{###########################################################################################################################################################</span> <span class="comment">#{ 1 1 1 11 11 1 11 2 #</span> <span class="comment">#{ 1 23 4 567 8 9 0 1 2 34 56 7 89 0 #</span> _ATTRIBUTE_CLAUSE_REGEXP: <span class="regexp">/(\[((\/[^\/]*\/[gmi]*)|([\w-]+))(((=)|(~=)|(\|=)|(\*=)|(\^=)|(\$=))(("(([^\\"]|(\\"))*)")|((\/[^\/]*\/[gmi]*)|([\w- :]+))))?\])/g</span> <span class="comment">#</span> <span class="comment">#{ \-name----------------------/|\-operator-----------------------/\-value-----------------------------------------------/| #</span> <span class="comment">#{ \-operator-and-value----------------------------------------------------------------------/ #</span> <span class="comment">#{###########################################################################################################################################################</span></pre></div></div> </li> <li id="section-14"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-14">&#182;</a> </div> <p>Indices of the important captured groups.</p> </div> <div class="content"><div class='highlight'><pre> _ATTR_NAME = <span class="number">2</span> _OPERATOR = <span class="number">6</span> _DEQUOTED_ATTR_VALUE = <span class="number">15</span> _NEVERQUOTED_ATTR_VALUE = <span class="number">18</span></pre></div></div> </li> <li id="section-15"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-15">&#182;</a> </div> <p><strong>_parse_selector</strong> returns a (possibly compound) predicate that matches the provided <code>selector</code> (string).</p> </div> <div class="content"><div class='highlight'><pre> _parse_selector:(selector)-&gt; match = <span class="property">@_CSS_SELECTOR_REGEXP</span>.exec(selector) clauses = []</pre></div></div> </li> <li id="section-16"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-16">&#182;</a> </div> <p>The name part.</p> </div> <div class="content"><div class='highlight'><pre> <span class="keyword">if</span> match[_NAME]? <span class="keyword">if</span> match[_NAME] <span class="keyword">is</span> <span class="string">'*'</span> clauses.push(<span class="property">@factory</span>.any_tag_predicate()) <span class="keyword">else</span> clauses.push(<span class="property">@factory</span>.by_tag_predicate(<span class="property">@_to_string_or_regex</span>(match[_NAME])))</pre></div></div> </li> <li id="section-17"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-17">&#182;</a> </div> <p>The ID part.</p> </div> <div class="content"><div class='highlight'><pre> <span class="keyword">if</span> match[_ID]? clauses.push(<span class="property">@factory</span>.by_id_predicate(<span class="property">@_to_string_or_regex</span>(match[_ID].substring(<span class="number">1</span>))))</pre></div></div> </li> <li id="section-18"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-18">&#182;</a> </div> <p>One or more class parts.</p> </div> <div class="content"><div class='highlight'><pre> <span class="keyword">if</span> match[_CLASSES]?.length &gt; <span class="number">0</span> <span class="comment"># match[CLASSES] contains something like `.foo.bar`</span> cs = match[_CLASSES].split(<span class="string">'.'</span>) <span class="comment"># split the string into individual class names</span> cs.shift() <span class="comment"># and skip the first (empty) token that is included</span> <span class="keyword">for</span> c <span class="keyword">in</span> cs clauses.push(<span class="property">@factory</span>.by_class_predicate(<span class="property">@_to_string_or_regex</span>(c)))</pre></div></div> </li> <li id="section-19"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-19">&#182;</a> </div> <p>TODO FIXME Support for <code>*=</code>, <code>^=</code> and <code>$=</code> is kinda hacked-in here. Refactor to be more DRY. One or more attribute parts.</p> </div> <div class="content"><div class='highlight'><pre> <span class="keyword">if</span> match[_ATTRIBUTES]?.length &gt; <span class="number">0</span> <span class="comment"># match[_ATTRIBUTES] contains one or more `[name=value]` (or `[name]`) strings</span> attr_match = <span class="property">@_ATTRIBUTE_CLAUSE_REGEXP</span>.exec(match[_ATTRIBUTES]) <span class="keyword">while</span> attr_match? <span class="keyword">if</span> attr_match[_ATTR_NAME]? <span class="keyword">and</span> (<span class="keyword">not</span> attr_match[_OPERATOR]?) clauses.push(<span class="property">@factory</span>.by_attr_exists_predicate(<span class="property">@_to_string_or_regex</span>(attr_match[_ATTR_NAME]))) <span class="keyword">if</span> attr_match[_ATTR_NAME]? <span class="keyword">and</span> attr_match[_OPERATOR]? <span class="keyword">and</span> (attr_match[_DEQUOTED_ATTR_VALUE]? <span class="keyword">or</span> attr_match[_NEVERQUOTED_ATTR_VALUE]?) delim = <span class="literal">null</span> <span class="keyword">if</span> attr_match[_OPERATOR] <span class="keyword">is</span> <span class="string">'~='</span> delim = <span class="regexp">/\s+/</span> <span class="keyword">if</span> attr_match[_OPERATOR] <span class="keyword">is</span> <span class="string">'|='</span> clauses.push( <span class="property">@factory</span>.by_attr_value_pipe_equals( <span class="property">@_to_string_or_regex</span>(attr_match[_ATTR_NAME]), <span class="property">@_to_string_or_regex</span>(attr_match[_DEQUOTED_ATTR_VALUE] ? attr_match[_NEVERQUOTED_ATTR_VALUE]) ) ) <span class="keyword">else</span> <span class="keyword">if</span> attr_match[_OPERATOR] <span class="keyword">is</span> <span class="string">'^='</span> <span class="comment"># starts with</span> aval = <span class="property">@_to_string_or_regex</span>(attr_match[_DEQUOTED_ATTR_VALUE] ? attr_match[_NEVERQUOTED_ATTR_VALUE]) <span class="keyword">if</span> <span class="keyword">typeof</span> aval <span class="keyword">is</span> <span class="string">'string'</span> regexp_source = <span class="property">@factory</span>._escape_for_regexp(aval) aval = <span class="keyword">new</span> RegExp(<span class="string">"^<span class="subst">#{regexp_source}</span>"</span>) <span class="keyword">else</span> regexp_source = aval.source modifier = <span class="string">''</span> modifier += <span class="string">'i'</span> <span class="keyword">if</span> aval.ignoreCase modifier += <span class="string">'g'</span> <span class="keyword">if</span> aval.global modifier += <span class="string">'m'</span> <span class="keyword">if</span> aval.multiline <span class="keyword">unless</span> <span class="regexp">/^\^/</span>.test regexp_source aval = <span class="keyword">new</span> RegExp(<span class="string">"^<span class="subst">#{regexp_source}</span>"</span>) clauses.push(<span class="property">@factory</span>.by_attr_value_predicate(<span class="property">@_to_string_or_regex</span>(attr_match[_ATTR_NAME]),aval)) <span class="keyword">else</span> <span class="keyword">if</span> attr_match[_OPERATOR] <span class="keyword">is</span> <span class="string">'$='</span> <span class="comment"># ends with</span> aval = <span class="property">@_to_string_or_regex</span>(attr_match[_DEQUOTED_ATTR_VALUE] ? attr_match[_NEVERQUOTED_ATTR_VALUE]) <span class="keyword">if</span> <span class="keyword">typeof</span> aval <span class="keyword">is</span> <span class="string">'string'</span> regexp_source = <span class="property">@factory</span>._escape_for_regexp(aval) aval = <span class="keyword">new</span> RegExp(<span class="string">"<span class="subst">#{regexp_source}</span>$"</span>) <span class="keyword">else</span> regexp_source = aval.source modifier = <span class="string">''</span> modifier += <span class="string">'i'</span> <span class="keyword">if</span> aval.ignoreCase modifier += <span class="string">'g'</span> <span class="keyword">if</span> aval.global modifier += <span class="string">'m'</span> <span class="keyword">if</span> aval.multiline <span class="keyword">unless</span> <span class="regexp">/\$$/</span>.test regexp_source aval = <span class="keyword">new</span> RegExp(<span class="string">"<span class="subst">#{regexp_source}</span>$"</span>) clauses.push(<span class="property">@factory</span>.by_attr_value_predicate(<span class="property">@_to_string_or_regex</span>(attr_match[_ATTR_NAME]),aval)) <span class="keyword">else</span> <span class="keyword">if</span> attr_match[_OPERATOR] <span class="keyword">is</span> <span class="string">'*='</span> <span class="comment"># contains</span> aval = <span class="property">@_to_string_or_regex</span>(attr_match[_DEQUOTED_ATTR_VALUE] ? attr_match[_NEVERQUOTED_ATTR_VALUE]) <span class="keyword">if</span> <span class="keyword">typeof</span> aval <span class="keyword">is</span> <span class="string">'string'</span> regexp_source = <span class="property">@factory</span>._escape_for_regexp(aval) aval = <span class="keyword">new</span> RegExp(regexp_source) clauses.push(<span class="property">@factory</span>.by_attr_value_predicate(<span class="property">@_to_string_or_regex</span>(attr_match[_ATTR_NAME]),aval)) <span class="keyword">else</span> clauses.push( <span class="property">@factory</span>.by_attr_value_predicate( <span class="property">@_to_string_or_regex</span>(attr_match[_ATTR_NAME]), <span class="property">@_to_string_or_regex</span>(attr_match[_DEQUOTED_ATTR_VALUE] ? attr_match[_NEVERQUOTED_ATTR_VALUE]), delim ) ) attr_match = <span class="property">@_ATTRIBUTE_CLAUSE_REGEXP</span>.exec(match[_ATTRIBUTES])</pre></div></div> </li> <li id="section-20"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-20">&#182;</a> </div> <p>The pseudo-class part.</p> </div> <div class="content"><div class='highlight'><pre> <span class="keyword">if</span> match[_PSEUDO_CLASS]? <span class="keyword">if</span> match[_PSEUDO_CLASS] <span class="keyword">is</span> <span class="string">'first-child'</span> clauses.push(<span class="property">@factory</span>.first_child_predicate())</pre></div></div> </li> <li id="section-21"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-21">&#182;</a> </div> <p>Combine them with <code>and</code> if needed.</p> </div> <div class="content"><div class='highlight'><pre> <span class="keyword">if</span> clauses.length &gt; <span class="number">0</span> clauses = <span class="property">@factory</span>.and_predicate(clauses) <span class="keyword">return</span> clauses</pre></div></div> </li> <li id="section-22"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-22">&#182;</a> </div> <p><strong>_to_string_or_regex</strong> converts a string that starts and ends with <code>/</code> (with an optional <code>g</code>, <code>m</code> or <code>i</code> suffix) into a regular expression, and otherwise returns the original <code>str</code> value.</p> </div> <div class="content"><div class='highlight'><pre> _to_string_or_regex:(str)-&gt; match = str.match <span class="regexp">/^\/(.*)\/([gmi]*)$/</span> <span class="keyword">if</span> match?[<span class="number">1</span>]? <span class="keyword">return</span> <span class="keyword">new</span> RegExp(match[<span class="number">1</span>],match[<span class="number">2</span>]) <span class="keyword">else</span> <span class="keyword">return</span> str</pre></div></div> </li> <li id="section-23"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-23">&#182;</a> </div> <p>Public API includes <code>Stew</code> and <code>DOMUtil</code></p> </div> <div class="content"><div class='highlight'><pre>exports = exports ? <span class="keyword">this</span> exports.Stew = Stew exports.DOMUtil = DOMUtil</pre></div></div> </li> </ul> </div> </body> </html>