Current section

86 Versions

Jump to

Compare versions

4 files changed
+52 additions
-4 deletions
  @@ -22,6 +22,7 @@ Assuming that you have the following HTML:
22 22 <section id="content">
23 23 <p class="headline">Floki</p>
24 24 <a href="http://github.com/philss/floki">Github page</a>
25 + <span data-model="user">philss</span>
25 26 </section>
26 27 <a href="https://hex.pm/packages/floki">Hex package</a>
27 28 </body>
  @@ -50,6 +51,10 @@ Floki.find(html, "#content a")
50 51 # => [{"a", [{"href", "http://github.com/philss/floki"}], ["Github page"]}]
51 52
52 53
54 + Floki.find(html, "[data-model=user]")
55 + # => [{"span", [{"data-model", "user"}], ["philss"]}]
56 +
57 +
53 58 Floki.find(html, ".headline, a")
54 59 # => [{"p", [{"class", "headline"}], ["Floki"]},
55 60 # => {"a", [{"href", "http://github.com/philss/floki"}], ["Github page"]},
  @@ -17,4 +17,4 @@
17 17 [{<<"app">>,<<"mochiweb">>},
18 18 {<<"optional">>,nil},
19 19 {<<"requirement">>,<<"~> 2.12.2">>}]}]}.
20 - {<<"version">>,<<"0.2.1">>}.
20 + {<<"version">>,<<"0.3.0">>}.
  @@ -16,6 +16,7 @@ defmodule Floki do
16 16 <section id="content">
17 17 <p class="headline">Floki</p>
18 18 <a href="http://github.com/philss/floki">Github page</a>
19 + <span data-model="user">philss</span>
19 20 </section>
20 21 </body>
21 22 </html>
  @@ -25,6 +26,7 @@ defmodule Floki do
25 26 * Floki.find(html, "#content") : returns the section with all children;
26 27 * Floki.find(html, ".headline") : returns a list with the `p` element;
27 28 * Floki.find(html, "a") : returns a list with the `a` element;
29 + * Floki.find(html, "[data-model=user]") : returns a list with elements that match that data attribute;
28 30 * Floki.find(html, "#content a") # returns all links inside content section;
29 31 * Floki.find(html, ".headline, a") # returns the .headline elements and links.
30 32
  @@ -71,8 +73,9 @@ defmodule Floki do
71 73 def parse(html) do
72 74 html = "<#{@floki_root_node}>#{html}</#{@floki_root_node}>"
73 75 {@floki_root_node, [], parsed} = :mochiweb_html.parse(html)
76 +
74 77 if length(parsed) == 1, do: hd(parsed), else: parsed
75 - end
78 + end
76 79
77 80 @doc """
78 81 Finds elements inside a HTML tree or string.
  @@ -104,7 +107,16 @@ defmodule Floki do
104 107 find(html_tree, selector)
105 108 end
106 109
110 + def find(html_tree, selector) when is_tuple(selector) do
111 + {:ok, nodes} = find_by_selector(selector, html_tree, &attr_matcher/3, {:ok, []})
112 +
113 + Enum.reverse(nodes)
114 + end
115 +
107 116 def find(html_tree, selector) do
117 + tag_attr_val_regex = ~r/(?'tag'.+)\[(?'attr'.+)=(?'val'.+)\]/
118 + attr_val_regex = ~r/\[(?'attr'.+)=(?'val'.+)\]/
119 +
108 120 cond do
109 121 String.contains?(selector, ",") ->
110 122 selectors = String.split(selector, ",")
  @@ -134,6 +146,16 @@ defmodule Floki do
134 146 {_status, nodes} = find_by_selector(id, html_tree, &id_matcher/3, {:ok, []})
135 147
136 148 List.first(nodes)
149 + Regex.match?(attr_val_regex, selector) ->
150 + %{"attr" => attr, "val" => val} = Regex.named_captures(attr_val_regex, selector)
151 + {:ok, nodes} = find_by_selector({attr, val}, html_tree, &attr_matcher/3, {:ok, []})
152 +
153 + Enum.reverse(nodes)
154 + Regex.match?(tag_attr_val_regex, selector) ->
155 + %{"tag" => tag, "attr" => attr, "val" => val} = Regex.named_captures(attr_val_regex, selector)
156 + {:ok, nodes} = find_by_selector({tag, attr, val}, html_tree, &attr_matcher/3, {:ok, []})
157 +
158 + Enum.reverse(nodes)
137 159 true ->
138 160 {:ok, nodes} = find_by_selector(selector, html_tree, &tag_matcher/3, {:ok, []})
139 161
  @@ -259,7 +281,28 @@ defmodule Floki do
259 281 end
260 282 end
261 283
262 - Enum.reverse values
284 + Enum.reverse(values)
285 + end
286 +
287 + defp attr_matcher({attr, value}, node, acc) do
288 + {_, attributes, _} = node
289 + {:ok, acc_nodes} = acc
290 +
291 + if attribute_match?(attributes, attr, value) do
292 + acc = {:ok, [node|acc_nodes]}
293 + end
294 +
295 + acc
296 + end
297 + defp attr_matcher({tag_name, attr, value}, node, acc) do
298 + {tag, attributes, _} = node
299 + {:ok, acc_nodes} = acc
300 +
301 + if tag == tag_name and attribute_match?(attributes, attr, value) do
302 + acc = {:ok, [node|acc_nodes]}
303 + end
304 +
305 + acc
263 306 end
264 307
265 308 defp class_matcher(class_name, node, acc) do
  @@ -3,7 +3,7 @@ defmodule Floki.Mixfile do
3 3
4 4 def project do
5 5 [app: :floki,
6 - version: "0.2.1",
6 + version: "0.3.0",
7 7 elixir: ">= 1.0.0",
8 8 package: package,
9 9 description: description,