Current section

Files

Jump to
microformats2 lib items.ex
Raw

lib/items.ex

defmodule Microformats2.Items do
@moduledoc false
import Microformats2.Helpers
alias Microformats2.Items
alias Microformats2.Items.Implied
alias Microformats2.ParserState
def parse(nodes, doc, url, opts, items \\ [])
def parse([head | tail], doc, url, opts, items) when is_bitstring(head), do: parse(tail, doc, url, opts, items)
def parse([head | tail], doc, url, opts, items), do: parse(tail, doc, url, opts, parse(head, doc, url, opts, items))
def parse([], _, _, _, items), do: items
def parse({_, _, children} = root, doc, url, opts, items) do
root_classes =
[root]
|> attr_list()
|> Enum.filter(&is_rootlevel?/1)
|> Enum.sort()
if not Enum.empty?(root_classes) do
item =
%{normalized_key("type", opts) => root_classes, normalized_key("properties", opts) => %{}}
|> maybe_put_id(root, opts)
entry =
parse_sub(children, doc, url, opts, item, %ParserState{})
|> Implied.parse(root, url, doc, opts)
items ++ [entry]
else
parse(children, doc, url, opts, items)
end
end
defp maybe_put_id(item, root, opts) do
id = Floki.attribute([root], "id") |> List.first()
if present?(id),
do: Map.put(item, normalized_key("id", opts), id),
else: item
end
defp parse_sub([], _, _, _, item, state), do: {item, state}
defp parse_sub([child | children], doc, url, opts, item, state) when is_bitstring(child),
do: parse_sub(children, doc, url, opts, item, state)
defp parse_sub([child = {_, _, child_children} | children], doc, url, opts, item, state) do
p =
if has_a?([child], "h"),
do: parse(child, doc, url, opts, []) |> List.first(),
else: []
classes =
[child]
|> attr_list()
|> Enum.filter(&non_h_type?/1)
{props, state} = gen_prop(child, classes, item, p, doc, url, opts, state)
{n_item, new_state} =
if is_rootlevel?(child),
do: {props, state},
else: parse_sub(child_children, doc, url, opts, props, state)
parse_sub(children, doc, url, opts, n_item, new_state)
end
defp maybe_parse_prop(type, child, doc, url, opts, state) do
if valid_mf2_name?(type),
do: parse_prop(type, child, doc, url, opts, state),
else: {nil, state}
end
def parse_prop("p-" <> _, child, doc, url, _, state),
do: Items.PProp.parsed_prop(child, doc, url, state)
def parse_prop("u-" <> _, child, doc, url, opts, state),
do: Items.UProp.parsed_prop(child, doc, url, opts, state)
def parse_prop("dt-" <> _, child, _, _, _, state),
do: Items.DtProp.parsed_prop(child, state)
def parse_prop("e-" <> _, {_, _, children}, doc, url, opts, state) do
updated_tree =
Floki.traverse_and_update(children, fn element ->
element
|> update_url_attr("href", doc, url)
|> update_url_attr("src", doc, url)
end)
{%{
normalized_key("html", opts) => updated_tree |> Floki.raw_html() |> stripped_or_nil(),
normalized_key("value", opts) =>
updated_tree |> cleanup_html() |> text_content(doc, url, &replaced_img_by_alt_or_src/3) |> stripped_or_nil()
}, state}
end
def parse_prop(_, _, _, _, _, state), do: {nil, state}
defp update_url_attr({element, attributes, children}, attr, doc, doc_url) do
updated_attributes =
Enum.map(attributes, fn
{^attr, uri} -> {attr, abs_uri(uri, doc_url, doc)}
v -> v
end)
{element, updated_attributes, children}
end
defp update_url_attr(node, _, _, _), do: node
defp gen_prop(child, classes, item, p, doc, url, opts, state) do
{props, new_state} =
Enum.reduce(classes, {item[normalized_key("properties", opts)], state}, fn class, {acc, state} ->
{prop, new_state} =
if is_rootlevel?(child) do
{value, new_state} = Items.Value.get_value(class, p, child, doc, url, opts, state)
{Map.put(p, normalized_key("value", opts), value), new_state}
else
maybe_parse_prop(class, child, doc, url, opts, state)
end
key = normalized_key(strip_prefix(class), opts)
{Map.update(acc, key, [prop], &(&1 ++ [prop])), new_state}
end)
retval =
if blank?(classes) and present?(p) and is_rootlevel?(child),
do: Map.update(item, normalized_key("children", opts), [p], &(&1 ++ [p])),
else: Map.put(item, normalized_key("properties", opts), props)
{retval, new_state}
end
defp strip_prefix("p-" <> rest), do: rest
defp strip_prefix("u-" <> rest), do: rest
defp strip_prefix("dt-" <> rest), do: rest
defp strip_prefix("e-" <> rest), do: rest
defp strip_prefix(rest), do: rest
def img_prop(img, url, doc, opts) do
alt = Floki.attribute([img], "alt") |> List.first()
src = Floki.attribute([img], "src") |> List.first()
if present?(alt) and present?(src),
do: %{normalized_key("alt", opts) => alt, normalized_key("value", opts) => abs_uri(src, url, doc)},
else: abs_uri(src, url, doc)
end
end