Current section

86 Versions

Jump to

Compare versions

7 files changed
+77 additions
-28 deletions
  @@ -2,7 +2,7 @@
2 2 [![Floki version](https://img.shields.io/hexpm/v/floki.svg)](https://hex.pm/packages/floki)
3 3 [![Hex.pm](https://img.shields.io/hexpm/dt/floki.svg)](https://hex.pm/packages/floki)
4 4 [![Inline docs](https://inch-ci.org/github/philss/floki.svg?branch=master)](https://inch-ci.org/github/philss/floki)
5 - [![Ebert](https://ebertapp.io/github/philss/floki.svg)](https://ebertapp.io/github/philss/floki)
5 + [![SourceLevel](https://app.sourcelevel.io/github/philss/floki.svg)](https://app.sourcelevel.io/github/philss/floki)
6 6
7 7 <img src="assets/images/floki-logo-with-type.svg" width="500" alt="Floki logo">
  @@ -4,23 +4,23 @@
4 4 <<"Floki is a simple HTML parser that enables search for nodes using CSS selectors.">>}.
5 5 {<<"elixir">>,<<"~> 1.5">>}.
6 6 {<<"files">>,
7 - [<<"lib">>,<<"lib/floki">>,<<"lib/floki/html_parser.ex">>,
8 - <<"lib/floki/html_parser">>,<<"lib/floki/html_parser/mochiweb.ex">>,
9 - <<"lib/floki/html_parser/html5ever.ex">>,<<"lib/floki/selector">>,
10 - <<"lib/floki/selector/functional.ex">>,<<"lib/floki/selector/parser.ex">>,
7 + [<<"lib">>,<<"lib/floki">>,<<"lib/floki/html_parser">>,
8 + <<"lib/floki/html_parser/html5ever.ex">>,
9 + <<"lib/floki/html_parser/mochiweb.ex">>,<<"lib/floki/traversal.ex">>,
10 + <<"lib/floki/raw_html.ex">>,<<"lib/floki/selector.ex">>,
11 + <<"lib/floki/selector">>,<<"lib/floki/selector/combinator.ex">>,
11 12 <<"lib/floki/selector/attribute_selector.ex">>,
12 - <<"lib/floki/selector/pseudo_class.ex">>,
13 - <<"lib/floki/selector/tokenizer.ex">>,
14 - <<"lib/floki/selector/combinator.ex">>,<<"lib/floki/deep_text.ex">>,
15 - <<"lib/floki/filter_out.ex">>,<<"lib/floki/flat_text.ex">>,
16 - <<"lib/floki/finder.ex">>,<<"lib/floki/traversal.ex">>,
17 - <<"lib/floki/raw_html.ex">>,<<"lib/floki/html_tree">>,
13 + <<"lib/floki/selector/tokenizer.ex">>,<<"lib/floki/selector/parser.ex">>,
14 + <<"lib/floki/selector/functional.ex">>,
15 + <<"lib/floki/selector/pseudo_class.ex">>,<<"lib/floki/html_tree">>,
18 16 <<"lib/floki/html_tree/id_seeder.ex">>,<<"lib/floki/html_tree/comment.ex">>,
19 - <<"lib/floki/html_tree/text.ex">>,<<"lib/floki/html_tree/html_node.ex">>,
20 - <<"lib/floki/selector.ex">>,<<"lib/floki/html_tree.ex">>,<<"lib/floki.ex">>,
21 - <<"src/floki_selector_lexer.xrl">>,<<"src/floki_mochi_html.erl">>,
22 - <<"mix.exs">>,<<"README.md">>,<<"LICENSE">>,<<"CODE_OF_CONDUCT.md">>,
23 - <<"CONTRIBUTING.md">>]}.
17 + <<"lib/floki/html_tree/html_node.ex">>,<<"lib/floki/html_tree/text.ex">>,
18 + <<"lib/floki/finder.ex">>,<<"lib/floki/filter_out.ex">>,
19 + <<"lib/floki/html_tree.ex">>,<<"lib/floki/html_parser.ex">>,
20 + <<"lib/floki/deep_text.ex">>,<<"lib/floki/flat_text.ex">>,
21 + <<"lib/floki.ex">>,<<"src/floki_selector_lexer.xrl">>,
22 + <<"src/floki_mochi_html.erl">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>,
23 + <<"CODE_OF_CONDUCT.md">>,<<"CONTRIBUTING.md">>]}.
24 24 {<<"licenses">>,[<<"MIT">>]}.
25 25 {<<"links">>,[{<<"GitHub">>,<<"https://github.com/philss/floki">>}]}.
26 26 {<<"name">>,<<"floki">>}.
  @@ -29,5 +29,5 @@
29 29 {<<"name">>,<<"html_entities">>},
30 30 {<<"optional">>,false},
31 31 {<<"repository">>,<<"hexpm">>},
32 - {<<"requirement">>,<<"~> 0.4.0">>}]]}.
33 - {<<"version">>,<<"0.23.0">>}.
32 + {<<"requirement">>,<<"~> 0.5.0">>}]]}.
33 + {<<"version">>,<<"0.23.1">>}.
  @@ -302,6 +302,37 @@ defmodule Floki do
302 302 end
303 303 end
304 304
305 + @doc """
306 + Returns the direct child nodes of a HTML tree.
307 +
308 + By default, it will also include all texts. You can disable this behaviour
309 + by using the option `include_text` to `false`
310 +
311 + ## Examples
312 +
313 + iex> Floki.children({"div", [], ["text", {"span", [], []}]})
314 + ["text", {"span", [], []}]
315 +
316 + iex> Floki.children({"div", [], ["text", {"span", [], []}]}, include_text: false)
317 + [{"span", [], []}]
318 +
319 + """
320 +
321 + @spec children(html_tree, Keyword.t()) :: html_tree
322 +
323 + def children(html, opts \\ [include_text: true]) do
324 + case html do
325 + {_, _, subtree} ->
326 + filter_children(subtree, opts[:include_text])
327 +
328 + _ ->
329 + nil
330 + end
331 + end
332 +
333 + defp filter_children(children, false), do: Enum.filter(children, &is_tuple(&1))
334 + defp filter_children(children, _), do: children
335 +
305 336 @doc """
306 337 Returns a list with attribute values for a given selector.
  @@ -34,6 +34,7 @@ defmodule Floki.RawHTML do
34 34 end
35 35
36 36 defp build_raw_html([], html, _encoder), do: html
37 +
37 38 defp build_raw_html(string, _html, encoder) when is_binary(string), do: encoder.(string)
38 39
39 40 defp build_raw_html(tuple, html, encoder) when is_tuple(tuple),
  @@ -88,11 +89,7 @@ defmodule Floki.RawHTML do
88 89 end
89 90
90 91 defp build_attrs({attr, <<value::binary>>}, attrs) do
91 - if String.contains?(value, "\"") do
92 - ~s(#{attrs} #{attr}='#{value}')
93 - else
94 - ~s(#{attrs} #{attr}="#{value}")
95 - end
92 + "#{attrs} #{attr}=\"#{html_escape_attribute_value(value)}\""
96 93 end
97 94
98 95 defp build_attrs(<<attr::binary>>, attrs), do: "#{attrs} #{attr}"
  @@ -116,4 +113,20 @@ defmodule Floki.RawHTML do
116 113 & &1
117 114 end
118 115 end
116 +
117 + # html_escape
118 +
119 + def html_escape_attribute_value(attribute_value) do
120 + html_escape_chars(attribute_value, ~r/&|"/)
121 + end
122 +
123 + defp html_escape_chars(subject, escaped_chars_regex) do
124 + Regex.replace(escaped_chars_regex, subject, &html_escape_char/1)
125 + end
126 +
127 + defp html_escape_char("<"), do: "&lt;"
128 + defp html_escape_char(">"), do: "&gt;"
129 + defp html_escape_char("&"), do: "&amp;"
130 + defp html_escape_char("\""), do: "&quot;"
131 + defp html_escape_char("'"), do: "&#39;"
119 132 end
  @@ -38,7 +38,7 @@ defmodule Floki.Selector.AttributeSelector do
38 38 def match?(attributes, s = %AttributeSelector{match_type: :includes}) do
39 39 value = get_value(s.attribute, attributes)
40 40
41 - whitespace_values = String.split(value, " ")
41 + whitespace_values = String.split(value, ~r/\s+/)
42 42
43 43 Enum.any?(whitespace_values, fn v -> v == s.value end)
44 44 end
Loading more files…