ftml/render/html/element/
link.rs

1/*
2 * render/html/element/link.rs
3 *
4 * ftml - Library to parse Wikidot text
5 * Copyright (C) 2019-2025 Wikijump Team
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21use super::prelude::*;
22use crate::tree::{
23    AnchorTarget, AttributeMap, Element, LinkLabel, LinkLocation, LinkType,
24};
25use crate::url::normalize_link;
26
27pub fn render_anchor(
28    ctx: &mut HtmlContext,
29    elements: &[Element],
30    attributes: &AttributeMap,
31    target: Option<AnchorTarget>,
32) {
33    debug!("Rendering anchor");
34
35    let target_value = match target {
36        Some(target) => target.html_attr(),
37        None => "",
38    };
39
40    ctx.html()
41        .a()
42        .attr(attr!(
43            "class" => "wj-anchor",
44            "target" => target_value; if target.is_some();;
45            attributes,
46        ))
47        .contents(elements);
48}
49
50pub fn render_link(
51    ctx: &mut HtmlContext,
52    link: &LinkLocation,
53    extra: Option<&str>,
54    label: &LinkLabel,
55    target: Option<AnchorTarget>,
56    ltype: LinkType,
57) {
58    debug!("Rendering link '{:?}' (type {})", link, ltype.name());
59    let handle = ctx.handle();
60
61    // Add to backlinks
62    ctx.add_link(link);
63
64    let url = normalize_link(link, ctx.handle());
65
66    let target_value = match target {
67        Some(target) => target.html_attr(),
68        None => "",
69    };
70
71    let css_class = match link {
72        LinkLocation::Url(url) if url == "javascript:;" => "wj-link-anchor",
73        LinkLocation::Url(url) if url.starts_with('#') => "wj-link-anchor",
74        LinkLocation::Url(url) if url.starts_with('/') => "wj-link-internal",
75        LinkLocation::Url(_) => "wj-link-external",
76        LinkLocation::Page(page) => {
77            if ctx.page_exists(page) {
78                "wj-link-internal"
79            } else {
80                "wj-link-internal wj-link-missing"
81            }
82        }
83    };
84
85    let interwiki_class = if ltype == LinkType::Interwiki {
86        " wj-link-interwiki"
87    } else {
88        ""
89    };
90
91    let site = ctx.info().site.as_ref().to_string();
92    let mut tag = ctx.html().a();
93    tag.attr(attr!(
94        "href" => &url extra.unwrap_or(""),
95        "target" => target_value; if target.is_some(),
96        "class" => "wj-link " css_class interwiki_class,
97        "data-link-type" => ltype.name(),
98    ));
99
100    // Add <a> internals, i.e. the link name
101    handle.get_link_label(&site, link, label, |label| {
102        tag.contents(label);
103    });
104}