-- Adds a bunch of fake HTML elements for easily making links to popular websites -- -- Supported elements: -- surrealist writer -- soupault -- soupault -- me on mastodon -- me on twitter -- HTTP RFC -- -- All elements also support a short form where the content becomes the link data: -- Philippe Soupault -- dmbaturin/soupault -- ~dmbaturin/soupault -- @dmbaturin@mastodon.social -- @dmbaturin -- "@" is optional -- RFC1945 -- yes, it can extract the number from this -- -- To run it, you need to add something like this to soupault.conf: -- [plugins.quick-links] -- file = "plugins/quick-links.lua" -- -- [widgets.convert-quick-links] -- widget = "quick-links" -- wikipedia_default_language = "fr" -- -- Author: Daniil Baturin -- License: MIT Plugin.require_version("1.10") wikipedia_default_lang = config["wikipedia_default_language"] if not wikipedia_default_lang then wikipedia_default_lang = "en" end -- Helper functions -- Gets data from specified attribute -- If that attribute is missing or empty, gets element content instead function get_link_data(element, attr_name) link_data = HTML.get_attribute(element, attr_name) if not link_data then link_data = HTML.strip_tags(element) end return link_data end -- Creates an element with given href, -- and content cloned from the original element function make_link(orig_elem, href) link = HTML.create_element("a") HTML.set_attribute(link, "href", href) content = HTML.clone_content(orig_elem) HTML.append_child(link, content) return link end -- Sets rel="me" if original element has a "me" attribute function set_rel_me(orig_elem, target_elem) rel = HTML.get_attribute(orig_elem, "me") if rel ~= nil then HTML.set_attribute(target_elem, "rel", "me") end end -- Generic link generator for URLs with only one parameter like Github or Twitter function make_simple_link(element, attr_name, url_format) link_data = get_link_data(element, attr_name) if not link_data then Log.warning(format("Found a <%s> element with no project attribute or content", HTML.get_tag_name(element))) return nil end href = format(url_format, link_data) real_link = make_link(element, href) set_rel_me(element, real_link) return real_link end -- Site-specific functions function make_wikipedia_link(element) wp_page = get_link_data(element, "page") if not wp_page then Log.warning("Found a element with no page attribute or content") else lang = HTML.get_attribute(element, "lang") if not lang then lang = wikipedia_default_lang end wp_page = String.trim(wp_page) wp_page = Regex.replace_all(wp_page, "\\s+", "_") href = format("https://%s.wikipedia.org/wiki/%s", lang, wp_page) real_link = make_link(element, href) return real_link end end function make_rfc_link(element) number = HTML.get_attribute(element, "number") if not number then content_data = HTML.strip_tags(element) int_matches = Regex.find_all(content_data, "([0-9]+)") number = int_matches[1] if not number then Log.warning("Found an element without a number attribute and nothing that looks like a number in its content") return nil end end href = format("https://tools.ietf.org/html/rfc%s", number) real_link = make_link(element, href) return real_link end function make_sidenote(element) name = HTML.get_attribute(element, "name") label = HTML.create_element("label") label_for = format("sn-%s", name) HTML.set_attribute(label, "for", label_for) HTML.add_class(label, "margin-toggle sidenote-number") input = HTML.create_element("input") HTML.set_attribute(input, "type", "checkbox") HTML.set_attribute(input, "id", label_for) HTML.add_class(input, "margin-toggle") sn_content = HTML.create_element("span") HTML.add_class(sn_content, "sidenote") HTML.append_child(sn_content, HTML.clone_content(element)) HTML.insert_after(element, sn_content) HTML.insert_after(element, input) -- HTML.replace(element, label) return label end -- -- -- -- As a side effect all change files with blake3 hash end with the letter `C`. -- function make_marginnote(element) name = HTML.get_attribute(element, "name") label = HTML.create_element("label", "⊕") label_for = format("mn-%s", name) HTML.set_attribute(label, "for", label_for) HTML.add_class(label, "margin-toggle") --HTML.append_child(label, HTML.create_text("⊕")) -- TODO this probably does not work input = HTML.create_element("input") HTML.set_attribute(input, "type", "checkbox") HTML.set_attribute(input, "id", label_for) HTML.add_class(input, "margin-toggle") mn_content = HTML.create_element("span") HTML.add_class(mn_content, "marginnote") HTML.append_child(mn_content, HTML.clone_content(element)) -- HTML.replace(element, label) HTML.insert_after(element, mn_content) HTML.insert_after(element, input) return label end function make_mastodon_link(element) user = get_link_data(element, "user") if not Regex.match(user, "@(.*)@(.*)") then Log.warning("Found a element without a valid mastodon id in content or user attribute. Example of a valid id: @user@example.com") return nil end -- Regex.split ignores the leading separator -- So the following will split "@user@mastodon.example.com" into ["user", "mastodon.example.com"] data = Regex.split(user, "@") href = format("https://%s/@%s", data[2], data[1]) real_link = make_link(element, href) set_rel_me(element, real_link) return real_link end function make_twitter_link(element) user = get_link_data(element, "user") if not user then Log.warning("Found a element with empty content and no user attribute") return nil end -- Remove the leading @ if necessary user = Regex.replace(user, "^@", "") href = format("https://twitter.com/%s", user) real_link = make_link(element, href) set_rel_me(element, real_link) return real_link end elements = HTML.select_all_of(page, { "wikipedia", "github", "sourcehut", "codeberg", "mastodon", "twitter", "linkedin", "rfc", "sidenote", "marginnote" }) local index = 1 while elements[index] do elem = elements[index] tag_name = HTML.get_tag_name(elem) if (tag_name == "wikipedia") then new_elem = make_wikipedia_link(elem) elseif (tag_name == "github") then new_elem = make_simple_link(elem, "project", "https://github.com/%s") elseif (tag_name == "sourcehut") then -- SourceHut supports multiple version control systems as well as -- having a project page without a subdomain. local vcs = HTML.get_attribute(elem, "vcs") if vcs then new_elem = make_simple_link(elem, "project", "https://" .. vcs .. ".sr.ht/%s") else new_elem = make_simple_link(elem, "project", "https://sr.ht/%s") end elseif (tag_name == "codeberg") then new_elem = make_simple_link(elem, "project", "https://codeberg.org/%s") elseif (tag_name == "mastodon") then new_elem = make_mastodon_link(elem) elseif (tag_name == "twitter") then new_elem = make_twitter_link(elem) elseif (tag_name == "linkedin") then new_elem = make_simple_link(elem, "user", "https://www.linkedin.com/in/%s") elseif (tag_name == "rfc") then new_elem = make_rfc_link(elem) elseif (tag_name == "sidenote") then new_elem = make_sidenote(elem) elseif (tag_name == "marginnote") then new_elem = make_marginnote(elem) end if new_elem then HTML.replace(elem, new_elem) else HTML.delete(elem) end index = index + 1 end