From bbd4e6c8ba41b6278bff85e94a3ab24709a9943b Mon Sep 17 00:00:00 2001 From: M Sarmad Qadeer Date: Sun, 11 Jun 2023 11:52:55 +0500 Subject: [PATCH] website: glossary feature (#2529) * web: quick fixes in glossary.md * website: update header tags * website: add glossary feature * website: add & style the tooltips & glossary terms * website: add overlay for glossary definition * website: add list styling & update links --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- docs/GLOSSARY.md | 4 +- website/.eleventy.js | 128 +++++++++- website/package.json | 5 +- website/src/_data/glossary.json | 110 ++++++++ website/src/_includes/components/macro.njk | 2 +- website/src/_includes/contact_page.html | 12 +- website/src/_includes/hero.html | 8 +- website/src/_includes/layouts/main.html | 2 +- .../src/_includes/sections/join_simplex.html | 2 +- .../_includes/sections/simplex_unique.html | 6 +- website/src/_includes/simplex_explained.html | 2 +- website/src/blog.html | 2 +- website/src/css/style.css | 238 +++++++++++++++++- website/src/index.html | 20 +- website/src/js/script.js | 129 +++++++++- 15 files changed, 626 insertions(+), 44 deletions(-) create mode 100644 website/src/_data/glossary.json diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md index f7bf411735..e68508ccc3 100644 --- a/docs/GLOSSARY.md +++ b/docs/GLOSSARY.md @@ -30,7 +30,7 @@ In a more narrow sense, particularly in media, blockchain is used to refer speci Centralized networks are provided or controlled by a single entity. The examples are Threema, Signal, WhatsApp and Telegram. The advantage of that design is that the provider can innovate faster, and has a centralized approach to security. But the disadvantage is that the provider can change or discontinue the service, and leak, sell or disclose in some other way all users' data, including who they are connected with. -## Content padding +## Content padding [Message padding](#message-padding). @@ -149,7 +149,7 @@ Generalizing [the definition](https://csrc.nist.gov/glossary/term/pairwise_pseud In the context of SimpleX network, these are the identifiers generated by SMP relays to access anonymous messaging queues, with a separate identifier (and access credential) for each accessing party: recipient, sender and and optional notifications subscriber. The same approach is used by XFTP relays to access file chunks, with separate identifiers (and access credentials) for sender and each recipient. -## Peer-to-peer +## Peer-to-peer Peer-to-peer (P2P) is the network architecture when participants have equal rights and communicate directly via a general purpose transport or overlay network. Unlike client-server architecture, all peers in a P2P network both provide and consume the resources. In the context of messaging, P2P architecture usually means that the messages are sent between peers, without user accounts or messages being stored on any servers. Examples are Tox, Briar, Cwtch and many others. diff --git a/website/.eleventy.js b/website/.eleventy.js index bf0bf8131f..28709bcf41 100644 --- a/website/.eleventy.js +++ b/website/.eleventy.js @@ -3,10 +3,52 @@ const markdownItAnchor = require("markdown-it-anchor") const markdownItReplaceLink = require('markdown-it-replace-link') const slugify = require("slugify") const uri = require('fast-uri') -const i18n = require('eleventy-plugin-i18n'); -const fs = require("fs"); -const path = require("path"); -const pluginRss = require('@11ty/eleventy-plugin-rss'); +const i18n = require('eleventy-plugin-i18n') +const fs = require("fs") +const path = require("path") +const pluginRss = require('@11ty/eleventy-plugin-rss') +const { JSDOM } = require('jsdom') + + +// The implementation of Glossary feature +const md = new markdownIt() +const glossaryMarkdownContent = fs.readFileSync(path.resolve(__dirname, '../docs/GLOSSARY.md'), 'utf8') +const glossaryHtmlContent = md.render(glossaryMarkdownContent) +const glossaryDOM = new JSDOM(glossaryHtmlContent) +const glossaryDocument = glossaryDOM.window.document +const glossary = require('./src/_data/glossary.json') + +glossary.forEach(item => { + const headers = Array.from(glossaryDocument.querySelectorAll("h2")) + const matchingHeader = headers.find(header => header.textContent.trim() === item.definition) + + if (matchingHeader) { + let sibling = matchingHeader.nextElementSibling + let definition = '' + let firstParagraph = '' + let paragraphCount = 0 + + while (sibling && sibling.tagName !== 'H2') { + if (sibling.tagName === 'P') { + Array.from(sibling.getElementsByTagName('a')).forEach(a => { + if (a.getAttribute('href').startsWith('#')) { + a.setAttribute('href', '/docs/glossary.html' + a.getAttribute('href')) + } + }) + paragraphCount += 1 + if (firstParagraph === '') { + firstParagraph = sibling.innerHTML + } + } + definition += sibling.outerHTML || sibling.textContent + sibling = sibling.nextElementSibling + } + + item.definition = definition + item.tooltip = firstParagraph + item.hasMultipleParagraphs = paragraphCount > 1 + } +}) const globalConfig = { @@ -55,6 +97,82 @@ module.exports = function (ty) { } }) + ty.addFilter('applyGlossary', function (content) { + const dom = new JSDOM(content) + const { document } = dom.window + const body = document.querySelector('body') + const allContentNodes = document.querySelectorAll('p, td, a, h1, h2, h3, h4') + + glossary.forEach((term, index) => { + let changeNoted = false + const id = `glossary-${index}` + + allContentNodes.forEach((node) => { + const regex = new RegExp(`(?${term.term}` + const beforeContent = node.innerHTML + node.innerHTML = node.innerHTML.replace(regex, replacement) + if (beforeContent !== node.innerHTML && !changeNoted) { + changeNoted = true + } + }) + + if (changeNoted) { + const definitionTooltipDiv = document.createElement('div') + definitionTooltipDiv.id = id + definitionTooltipDiv.className = "glossary-tooltip" + const titleH4 = document.createElement('h4') + titleH4.innerHTML = term.term + titleH4.className = "tooltip-title" + const p = document.createElement('p') + p.innerHTML = term.tooltip + const innerDiv = document.createElement('div') + innerDiv.appendChild(titleH4) + innerDiv.appendChild(p) + if (term.hasMultipleParagraphs) { + const readMoreBtn = document.createElement('button') + readMoreBtn.innerHTML = "Read more" + readMoreBtn.className = "read-more-btn open-overlay-btn" + readMoreBtn.setAttribute('data-show-overlay', `overlay-${id}`) + innerDiv.appendChild(readMoreBtn) + + const overlayDiv = document.createElement('div') + overlayDiv.id = `overlay-${id}` + overlayDiv.className = "overlay glossary-overlay hidden" + const overlayCardDiv = document.createElement('div') + overlayCardDiv.className = "overlay-card" + const overlayTitleH1 = document.createElement('h1') + overlayTitleH1.className = "overlay-title" + overlayTitleH1.innerHTML = term.term + const overlayContent = document.createElement('div') + overlayContent.className = "overlay-content" + overlayContent.innerHTML = term.definition + const crossSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg") + crossSVG.setAttribute('class', 'close-overlay-btn') + crossSVG.setAttribute('id', 'cross') + crossSVG.setAttribute('width', '16') + crossSVG.setAttribute('height', '16') + crossSVG.setAttribute('viewBox', '0 0 13 13') + crossSVG.setAttribute('xmlns', 'http://www.w3.org/2000/svg') + const crossPath = document.createElementNS("http://www.w3.org/2000/svg", "path") + crossPath.setAttribute('d', 'M12.7973 11.5525L7.59762 6.49833L12.7947 1.44675C13.055 1.19371 13.0658 0.771991 12.8188 0.505331C12.5718 0.238674 12.1602 0.227644 11.8999 0.480681L6.65343 5.58028L1.09979 0.182228C0.805 0.002228 0.430001 0.002228 0.135211 0.182228C-0.159579 0.362228 -0.159579 0.697228 0.135211 0.877228L5.68885 6.27528L0.4918 11.3295C0.231501 11.5825 0.220703 12.0042 0.467664 12.2709C0.714625 12.5376 1.12625 12.5486 1.38655 12.2956L6.63302 7.196L12.1867 12.5941C12.4815 12.7741 12.8565 12.7741 13.1513 12.5941C13.4461 12.4141 13.4461 12.0791 13.1513 11.8991L12.7973 11.5525Z') + crossSVG.appendChild(crossPath) + + overlayCardDiv.appendChild(overlayTitleH1) + overlayCardDiv.appendChild(overlayContent) + overlayCardDiv.appendChild(crossSVG) + overlayDiv.appendChild(overlayCardDiv) + body.appendChild(overlayDiv) + } + innerDiv.className = "tooltip-content" + definitionTooltipDiv.appendChild(innerDiv) + body.appendChild(definitionTooltipDiv) + } + }) + + return dom.serialize() + }) + ty.addShortcode("completeRoute", (obj) => { const urlParts = obj.url.split("/") @@ -88,7 +206,7 @@ module.exports = function (ty) { } }) - ty.addPlugin(pluginRss); + ty.addPlugin(pluginRss) ty.addPlugin(i18n, { translations, diff --git a/website/package.json b/website/package.json index c85dd3c00e..5eddf63031 100644 --- a/website/package.json +++ b/website/package.json @@ -29,6 +29,9 @@ }, "dependencies": { "eleventy-plugin-i18n": "^0.1.3", - "gray-matter": "^4.0.3" + "fs": "^0.0.1-security", + "gray-matter": "^4.0.3", + "jsdom": "^22.1.0", + "markdown-it": "^13.0.1" } } diff --git a/website/src/_data/glossary.json b/website/src/_data/glossary.json new file mode 100644 index 0000000000..fd420ccaa6 --- /dev/null +++ b/website/src/_data/glossary.json @@ -0,0 +1,110 @@ +[ + { + "term": "Address portability", + "definition": "Address portability" + }, + { + "term": "Anonymous credentials", + "definition": "Anonymous credentials" + }, + { + "term": "Blockchain", + "definition": "Blockchain" + }, + { + "term": "Break-in recovery", + "definition": "Post-compromise security" + }, + { + "term": "Centralized network", + "definition": "Centralized network" + }, + { + "term": "Content padding", + "definition": "Message padding" + }, + { + "term": "Decentralized network", + "definition": "Decentralized network" + }, + { + "term": "Defense in depth", + "definition": "Defense in depth" + }, + { + "term": "Double ratchet algorithm", + "definition": "Double ratchet algorithm" + }, + { + "term": "End-to-end encryption", + "definition": "End-to-end encryption" + }, + { + "term": "Federated network", + "definition": "Federated network" + }, + { + "term": "Forward secrecy", + "definition": "Forward secrecy" + }, + { + "term": "Key agreement protocol", + "definition": "Key agreement protocol" + }, + { + "term": "Key exchange", + "definition": "Key agreement protocol" + }, + { + "term": "Man-in-the-middle attack", + "definition": "Man-in-the-middle attack" + }, + { + "term": "Merkle directed acyclic graph", + "definition": "Merkle directed acyclic graph" + }, + { + "term": "Message padding", + "definition": "Message padding" + }, + { + "term": "Onion routing", + "definition": "Onion routing" + }, + { + "term": "Overlay network", + "definition": "Overlay network" + }, + { + "term": "Pairwise pseudonymous identifier", + "definition": "Pairwise pseudonymous identifier" + }, + { + "term": "Peer-to-peer", + "definition": "Peer-to-peer" + }, + { + "term": "Perfect forward secrecy", + "definition": "Forward secrecy" + }, + { + "term": "Post-compromise security", + "definition": "Post-compromise security" + }, + { + "term": "Post-quantum cryptography", + "definition": "Post-quantum cryptography" + }, + { + "term": "Proxied peer-to-peer", + "definition": "Proxied peer-to-peer" + }, + { + "term": "Recovery from compromise", + "definition": "Post-compromise security" + }, + { + "term": "User identity", + "definition": "User identity" + } +] \ No newline at end of file diff --git a/website/src/_includes/components/macro.njk b/website/src/_includes/components/macro.njk index bfcbb6a435..0f19b0f369 100644 --- a/website/src/_includes/components/macro.njk +++ b/website/src/_includes/components/macro.njk @@ -22,7 +22,7 @@ {% endif %} - + diff --git a/website/src/_includes/contact_page.html b/website/src/_includes/contact_page.html index eba6190ab9..e37dcddd0d 100644 --- a/website/src/_includes/contact_page.html +++ b/website/src/_includes/contact_page.html @@ -7,8 +7,8 @@
-

{{ header | i18n({}, lang ) | safe }}

-

{{ "contact-hero-subheader" | i18n({}, lang ) | safe }}

+

{{ header | i18n({}, lang ) | safe }}

+

{{ "contact-hero-subheader" | i18n({}, lang ) | safe }}

{{ "contact-hero-p-1" | i18n({}, lang ) | safe }}

@@ -43,7 +43,7 @@
-

{{ header | i18n({}, lang ) | safe }}

+

{{ header | i18n({}, lang ) | safe }}

{{ "to-make-a-connection" | i18n({}, lang ) | safe }}

@@ -88,7 +88,7 @@
- {{ content | safe }} + {{ content | applyGlossary | safe }} {% include "footer.html" %} diff --git a/website/src/_includes/sections/join_simplex.html b/website/src/_includes/sections/join_simplex.html index 5b00d171d5..edd7148067 100644 --- a/website/src/_includes/sections/join_simplex.html +++ b/website/src/_includes/sections/join_simplex.html @@ -1,7 +1,7 @@ {# join simplex #}
-

{{ "join" | i18n({}, lang ) | safe }} SimpleX

+

{{ "join" | i18n({}, lang ) | safe }} SimpleX

{{ "we-invite-you-to-join-the-conversation" | i18n({}, lang ) | safe }}

diff --git a/website/src/_includes/sections/simplex_unique.html b/website/src/_includes/sections/simplex_unique.html index aee8272379..6759e3a5cb 100644 --- a/website/src/_includes/sections/simplex_unique.html +++ b/website/src/_includes/sections/simplex_unique.html @@ -1,6 +1,6 @@
-

{{ "why-simplex-is" | i18n({}, lang ) | safe }} {{ "unique" | i18n({}, lang ) | safe }}

+

{{ "why-simplex-is" | i18n({}, lang ) | safe }} {{ "unique" | i18n({}, lang ) | safe }}

@@ -15,8 +15,8 @@
-

#{{ section.id }}

-

{{ section.title | i18n({}, lang ) | safe }}

+

#{{ section.id }}

+

{{ section.title | i18n({}, lang ) | safe }}

diff --git a/website/src/_includes/simplex_explained.html b/website/src/_includes/simplex_explained.html index f7f906a23d..15bd8623c5 100644 --- a/website/src/_includes/simplex_explained.html +++ b/website/src/_includes/simplex_explained.html @@ -25,7 +25,7 @@
-

{{ "simplex-explained" | i18n({}, lang ) | safe }}

+

{{ "simplex-explained" | i18n({}, lang ) | safe }}