From 70bf41e3b280e3f28d014ca5b3179ccecd3bade3 Mon Sep 17 00:00:00 2001 From: Hendrik Eeckhaut Date: Wed, 20 Sep 2023 09:14:31 +0200 Subject: [PATCH] Experiment with dark diagram switch #43 --- diagrams/convert_all.sh | 2 - src/diagrams/data_provenance_none.svg | 2 +- src/diagrams/data_provenance_oauth.svg | 2 +- src/diagrams/data_provenance_tlsn.svg | 2 +- src/diagrams/data_provenance_ultimate.svg | 2 +- src/diagrams/overview_notary.svg | 2 +- src/diagrams/overview_prover_verifier.svg | 2 +- src/intro.md | 4 +- src/motivation.md | 6 +- theme/book.js | 685 ++++++++++++++++++++++ theme/index.hbs | 365 ++++++++++++ 11 files changed, 1061 insertions(+), 13 deletions(-) create mode 100644 theme/book.js create mode 100644 theme/index.hbs diff --git a/diagrams/convert_all.sh b/diagrams/convert_all.sh index b59c153..1946328 100755 --- a/diagrams/convert_all.sh +++ b/diagrams/convert_all.sh @@ -28,8 +28,6 @@ for file in *.drawio; do # Only convert if the source is more recent than the target if [[ "$SOURCE" -nt "${TARGET}" ]]; then "$DRAW_IO" --export --format ${FORMAT} --scale 2.5 -o "${TARGET}" "$SOURCE" - # add styling for dark mode (https://github.com/jgraph/drawio-github/blob/master/DARK-MODE.md) - sed -i -e "s$$$" "${TARGET}" fi done popd >/dev/null diff --git a/src/diagrams/data_provenance_none.svg b/src/diagrams/data_provenance_none.svg index c8fcb6b..83c861a 100644 --- a/src/diagrams/data_provenance_none.svg +++ b/src/diagrams/data_provenance_none.svg @@ -1,3 +1,3 @@ -
No Data Provenance
No Data Provenance
Server
Server
Alice
Alice
Bob
Bob
TLS
TLS

signed
data
⁉️
signed...
  • Origin ⁉️
  • Integrity ⁉️
  • Too much info ⁉️
Origin ⁉️Integrity ⁉️Too m...
🔐
signed
data
🔐signed...
Text is not SVG - cannot display
\ No newline at end of file +
No Data Provenance
No Data Provenance
Server
Server
Alice
Alice
Bob
Bob
TLS
TLS

signed
data
⁉️
signed...
  • Origin ⁉️
  • Integrity ⁉️
  • Too much info ⁉️
Origin ⁉️Integrity ⁉️Too m...
🔐
signed
data
🔐signed...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/src/diagrams/data_provenance_oauth.svg b/src/diagrams/data_provenance_oauth.svg index 52cfe7d..a3d24de 100644 --- a/src/diagrams/data_provenance_oauth.svg +++ b/src/diagrams/data_provenance_oauth.svg @@ -1,3 +1,3 @@ -
Status Quo 
Status Quo 
  • Origin ✅
  • Integrity ✅
  • Too much info ⁉️
Origin ✅Integrity ✅Too m...
Server
Server
TLS
TLS
Alice
Alice
Bob
Bob
grant access
to Bob
grant access...
delegate
access control
delegate...
OAuth
Server
OAuth...
  • censoring possible ⁉️
  • sees all requests ⁉️
censoring possible ⁉️sees...
🔐
signed
data
🔐signed...
access token
access token
Text is not SVG - cannot display
\ No newline at end of file +
Status Quo 
Status Quo 
  • Origin ✅
  • Integrity ✅
  • Too much info ⁉️
Origin ✅Integrity ✅Too m...
Server
Server
TLS
TLS
Alice
Alice
Bob
Bob
grant access
to Bob
grant access...
delegate
access control
delegate...
OAuth
Server
OAuth...
  • censoring possible ⁉️
  • sees all requests ⁉️
censoring possible ⁉️sees...
🔐
signed
data
🔐signed...
access token
access token
Text is not SVG - cannot display
\ No newline at end of file diff --git a/src/diagrams/data_provenance_tlsn.svg b/src/diagrams/data_provenance_tlsn.svg index 996540a..59ce7b3 100644 --- a/src/diagrams/data_provenance_tlsn.svg +++ b/src/diagrams/data_provenance_tlsn.svg @@ -1,3 +1,3 @@ -
TLSNotary
TLSNotary
Server
Server
Prover
Alice
Prover...
Verifier
Bob
Verifier...
Selective disclosure
Selective disclosure
🔐
signed
data
🔐signed...
🔐redacted
signed
data
🔐redacted...
  • Origin ✅
  • Integrity ✅
  • Only required info ✅
Origin ✅Integrity ✅Only requi...
MPC-TLS
MPC-TLS
Text is not SVG - cannot display
\ No newline at end of file +
TLSNotary
TLSNotary
Server
Server
Prover
Alice
Prover...
Verifier
Bob
Verifier...
Selective disclosure
Selective disclosure
🔐
signed
data
🔐signed...
🔐redacted
signed
data
🔐redacted...
  • Origin ✅
  • Integrity ✅
  • Only required info ✅
Origin ✅Integrity ✅Only requi...
MPC-TLS
MPC-TLS
Text is not SVG - cannot display
\ No newline at end of file diff --git a/src/diagrams/data_provenance_ultimate.svg b/src/diagrams/data_provenance_ultimate.svg index 2059c82..772f5f1 100644 --- a/src/diagrams/data_provenance_ultimate.svg +++ b/src/diagrams/data_provenance_ultimate.svg @@ -1,3 +1,3 @@ -
Ultimate solution
Ultimate solution
Server
Server
Alice
Alice
Bob
Bob
ZKP
ZKP
Improved TLS
Improved TLS
🔐
signed
data
🔐signed...
🔐redacted
signed
data
🔐redacted...
  • Authenticity ✅
  • Integrity ✅
  • Only required info ✅
Authenticity ✅Integrity ✅Only...
Text is not SVG - cannot display
\ No newline at end of file +
Ultimate solution
Ultimate solution
Server
Server
Alice
Alice
Bob
Bob
ZKP
ZKP
Improved TLS
Improved TLS
🔐
signed
data
🔐signed...
🔐redacted
signed
data
🔐redacted...
  • Authenticity ✅
  • Integrity ✅
  • Only required info ✅
Authenticity ✅Integrity ✅Only...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/src/diagrams/overview_notary.svg b/src/diagrams/overview_notary.svg index d949f01..60d7d85 100644 --- a/src/diagrams/overview_notary.svg +++ b/src/diagrams/overview_notary.svg @@ -1,3 +1,3 @@ -
Prover
Prover
Server
Server
Notary
Notary
blindly
signed
data
blindly...
Verifier
Verifier
TLS
TLS
notarize
notarize
redacted signed
data
redacted...
selective disclosure
selectiv...
Multi party computation
Multi party computati...
1
1
2
2
3
3
4
4
verify
verify
Text is not SVG - cannot display
\ No newline at end of file +
Prover
Prover
Server
Server
Notary
Notary
blindly
signed
data
blindly...
Verifier
Verifier
TLS
TLS
notarize
notarize
redacted signed
data
redacted...
selective disclosure
selectiv...
Multi party computation
Multi party computati...
1
1
2
2
3
3
4
4
verify
verify
Text is not SVG - cannot display
\ No newline at end of file diff --git a/src/diagrams/overview_prover_verifier.svg b/src/diagrams/overview_prover_verifier.svg index 2b77813..d64eefc 100644 --- a/src/diagrams/overview_prover_verifier.svg +++ b/src/diagrams/overview_prover_verifier.svg @@ -1,3 +1,3 @@ -
Prover
Prover
Server
Server
Verifier
Verifier
TLS
TLS
MPC-TLS
MPC-TLS
1
1
3
3
2
2
Verify
Verify
Selective disclosure
Selective disclosure
1
1
Text is not SVG - cannot display
\ No newline at end of file +
Prover
Prover
Server
Server
Verifier
Verifier
TLS
TLS
MPC-TLS
MPC-TLS
1
1
3
3
2
2
Verify
Verify
Selective disclosure
Selective disclosure
1
1
Text is not SVG - cannot display
\ No newline at end of file diff --git a/src/intro.md b/src/intro.md index c57a658..81205f0 100644 --- a/src/intro.md +++ b/src/intro.md @@ -15,7 +15,7 @@ The TLSNotary protocol consists of 3 steps: 2. The `Prover` **selectively discloses** the data to the `Verifier`. 3. The `Verifier` **verifies** the data. -![](./diagrams/overview_prover_verifier.svg) +{{#include ./diagrams/overview_prover_verifier.svg}} ### ① Multi-party TLS Request @@ -37,7 +37,7 @@ The `Verifier` now validates the proof received from the `Prover`. The data orig Since the validation of the TLS traffic neither reveals anything about the plaintext of the TLS session nor about the `Server`, it is possible to outsource the MPC-TLS verification ① to a general-purpose TLS verifier, which we term a `Notary`. This `Notary` can sign (aka *notarize*) ② the data, making it portable. The `Prover` can then take this signed data and selectively disclose ③ sections to an application-specific `Verifier`, who then verifies the data ④. -![](./diagrams/overview_notary.svg) +{{#include ./diagrams/overview_notary.svg}} In this setup, the `Notary` cryptographically signs commitments to the data and the server's identity. The `Prover` can store this signed data, redact it, and share it with any `Verifier` as they see fit, making the signed data both reusable and portable. diff --git a/src/motivation.md b/src/motivation.md index 334c96d..0a1f65d 100644 --- a/src/motivation.md +++ b/src/motivation.md @@ -8,7 +8,7 @@ Data provenance will make data truly portable, empowering users to share it with ## Non-repudiation: TLS is not enough -![](diagrams/data_provenance_none.svg) +{{#include ./diagrams/data_provenance_none.svg}} Transport Layer Security (TLS) plays a crucial role in digital security. TLS protects communication against eavesdropping and tampering. It ensures that the data received by a user (_"Alice"_) indeed originated from the `Server` and was not changed. The `Server`'s identity is verified by Alice through trusted Certificate Authorities (CAs). Data integrity is maintained by transmitting a cryptographic hash (called Message Authentication Code or MAC in TLS) alongside the data, which safeguards against deliberate alterations. @@ -20,7 +20,7 @@ While the concept seems straightforward, enabling servers to sign data is not a ## Status Quo: delegate access -![](diagrams/data_provenance_oauth.svg) +{{#include ./diagrams/data_provenance_oauth.svg}} Currently, when Alice wants to share data from a `Server` with another party, OAuth can be used to facilitate this if the application supports it. In this way, the other party receives the data directly from the `Server`, ensuring authentic and unchanged data. However, applications often do not provide fine-grained control over which data to share, leading to the other party gaining access to more information than strictly necessary. @@ -30,7 +30,7 @@ It's worth noting that in many instances, OAuth is not even presented as an opti ## TLSNotary: data provenance and privacy with secure multi-party computation -![](diagrams/data_provenance_tlsn.svg) +{{#include ./diagrams/data_provenance_tlsn.svg}} TLSNotary operates by executing the TLS communication using **multi-party computation** (MPC). MPC allows Alice and Bob to jointly manage the TLS connection. With TLSNotary, Alice can selectively prove the authenticity of arbitrary portions of the data to Bob. Since Bob participated in the MPC-TLS communication, he is guaranteed that the data is authentic. diff --git a/theme/book.js b/theme/book.js new file mode 100644 index 0000000..d32c274 --- /dev/null +++ b/theme/book.js @@ -0,0 +1,685 @@ +"use strict"; + +// Fix back button cache problem +window.onunload = function () { }; + +// Global variable, shared between modules +function playground_text(playground, hidden = true) { + let code_block = playground.querySelector("code"); + + if (window.ace && code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + return editor.getValue(); + } else if (hidden) { + return code_block.textContent; + } else { + return code_block.innerText; + } +} + +(function codeSnippets() { + function fetch_with_timeout(url, options, timeout = 6000) { + return Promise.race([ + fetch(url, options), + new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)) + ]); + } + + var playgrounds = Array.from(document.querySelectorAll(".playground")); + if (playgrounds.length > 0) { + fetch_with_timeout("https://play.rust-lang.org/meta/crates", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + }) + .then(response => response.json()) + .then(response => { + // get list of crates available in the rust playground + let playground_crates = response.crates.map(item => item["id"]); + playgrounds.forEach(block => handle_crate_list_update(block, playground_crates)); + }); + } + + function handle_crate_list_update(playground_block, playground_crates) { + // update the play buttons after receiving the response + update_play_button(playground_block, playground_crates); + + // and install on change listener to dynamically update ACE editors + if (window.ace) { + let code_block = playground_block.querySelector("code"); + if (code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + editor.addEventListener("change", function (e) { + update_play_button(playground_block, playground_crates); + }); + // add Ctrl-Enter command to execute rust code + editor.commands.addCommand({ + name: "run", + bindKey: { + win: "Ctrl-Enter", + mac: "Ctrl-Enter" + }, + exec: _editor => run_rust_code(playground_block) + }); + } + } + } + + // updates the visibility of play button based on `no_run` class and + // used crates vs ones available on https://play.rust-lang.org + function update_play_button(pre_block, playground_crates) { + var play_button = pre_block.querySelector(".play-button"); + + // skip if code is `no_run` + if (pre_block.querySelector('code').classList.contains("no_run")) { + play_button.classList.add("hidden"); + return; + } + + // get list of `extern crate`'s from snippet + var txt = playground_text(pre_block); + var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g; + var snippet_crates = []; + var item; + while (item = re.exec(txt)) { + snippet_crates.push(item[1]); + } + + // check if all used crates are available on play.rust-lang.org + var all_available = snippet_crates.every(function (elem) { + return playground_crates.indexOf(elem) > -1; + }); + + if (all_available) { + play_button.classList.remove("hidden"); + } else { + play_button.classList.add("hidden"); + } + } + + function run_rust_code(code_block) { + var result_block = code_block.querySelector(".result"); + if (!result_block) { + result_block = document.createElement('code'); + result_block.className = 'result hljs language-bash'; + + code_block.append(result_block); + } + + let text = playground_text(code_block); + let classes = code_block.querySelector('code').classList; + let edition = "2015"; + if (classes.contains("edition2018")) { + edition = "2018"; + } else if (classes.contains("edition2021")) { + edition = "2021"; + } + var params = { + version: "stable", + optimize: "0", + code: text, + edition: edition + }; + + if (text.indexOf("#![feature") !== -1) { + params.version = "nightly"; + } + + result_block.innerText = "Running..."; + + fetch_with_timeout("https://play.rust-lang.org/evaluate.json", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + body: JSON.stringify(params) + }) + .then(response => response.json()) + .then(response => { + if (response.result.trim() === '') { + result_block.innerText = "No output"; + result_block.classList.add("result-no-output"); + } else { + result_block.innerText = response.result; + result_block.classList.remove("result-no-output"); + } + }) + .catch(error => result_block.innerText = "Playground Communication: " + error.message); + } + + // Syntax highlighting Configuration + hljs.configure({ + tabReplace: ' ', // 4 spaces + languages: [], // Languages used for auto-detection + }); + + let code_nodes = Array + .from(document.querySelectorAll('code')) + // Don't highlight `inline code` blocks in headers. + .filter(function (node) { return !node.parentElement.classList.contains("header"); }); + + if (window.ace) { + // language-rust class needs to be removed for editable + // blocks or highlightjs will capture events + code_nodes + .filter(function (node) { return node.classList.contains("editable"); }) + .forEach(function (block) { block.classList.remove('language-rust'); }); + + code_nodes + .filter(function (node) { return !node.classList.contains("editable"); }) + .forEach(function (block) { hljs.highlightBlock(block); }); + } else { + code_nodes.forEach(function (block) { hljs.highlightBlock(block); }); + } + + // Adding the hljs class gives code blocks the color css + // even if highlighting doesn't apply + code_nodes.forEach(function (block) { block.classList.add('hljs'); }); + + Array.from(document.querySelectorAll("code.hljs")).forEach(function (block) { + + var lines = Array.from(block.querySelectorAll('.boring')); + // If no lines were hidden, return + if (!lines.length) { return; } + block.classList.add("hide-boring"); + + var buttons = document.createElement('div'); + buttons.className = 'buttons'; + buttons.innerHTML = ""; + + // add expand button + var pre_block = block.parentNode; + pre_block.insertBefore(buttons, pre_block.firstChild); + + pre_block.querySelector('.buttons').addEventListener('click', function (e) { + if (e.target.classList.contains('fa-eye')) { + e.target.classList.remove('fa-eye'); + e.target.classList.add('fa-eye-slash'); + e.target.title = 'Hide lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.remove('hide-boring'); + } else if (e.target.classList.contains('fa-eye-slash')) { + e.target.classList.remove('fa-eye-slash'); + e.target.classList.add('fa-eye'); + e.target.title = 'Show hidden lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.add('hide-boring'); + } + }); + }); + + if (window.playground_copyable) { + Array.from(document.querySelectorAll('pre code')).forEach(function (block) { + var pre_block = block.parentNode; + if (!pre_block.classList.contains('playground')) { + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var clipButton = document.createElement('button'); + clipButton.className = 'fa fa-copy clip-button'; + clipButton.title = 'Copy to clipboard'; + clipButton.setAttribute('aria-label', clipButton.title); + clipButton.innerHTML = ''; + + buttons.insertBefore(clipButton, buttons.firstChild); + } + }); + } + + // Process playground code blocks + Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) { + // Add play button + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var runCodeButton = document.createElement('button'); + runCodeButton.className = 'fa fa-play play-button'; + runCodeButton.hidden = true; + runCodeButton.title = 'Run this code'; + runCodeButton.setAttribute('aria-label', runCodeButton.title); + + buttons.insertBefore(runCodeButton, buttons.firstChild); + runCodeButton.addEventListener('click', function (e) { + run_rust_code(pre_block); + }); + + if (window.playground_copyable) { + var copyCodeClipboardButton = document.createElement('button'); + copyCodeClipboardButton.className = 'fa fa-copy clip-button'; + copyCodeClipboardButton.innerHTML = ''; + copyCodeClipboardButton.title = 'Copy to clipboard'; + copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title); + + buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild); + } + + let code_block = pre_block.querySelector("code"); + if (window.ace && code_block.classList.contains("editable")) { + var undoChangesButton = document.createElement('button'); + undoChangesButton.className = 'fa fa-history reset-button'; + undoChangesButton.title = 'Undo changes'; + undoChangesButton.setAttribute('aria-label', undoChangesButton.title); + + buttons.insertBefore(undoChangesButton, buttons.firstChild); + + undoChangesButton.addEventListener('click', function () { + let editor = window.ace.edit(code_block); + editor.setValue(editor.originalCode); + editor.clearSelection(); + }); + } + }); +})(); + +(function themes() { + var html = document.querySelector('html'); + var themeToggleButton = document.getElementById('theme-toggle'); + var themePopup = document.getElementById('theme-list'); + var themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); + var stylesheets = { + ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"), + tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"), + highlight: document.querySelector("[href$='highlight.css']"), + }; + + function showThemes() { + themePopup.style.display = 'block'; + themeToggleButton.setAttribute('aria-expanded', true); + themePopup.querySelector("button#" + get_theme()).focus(); + } + + function updateThemeSelected() { + themePopup.querySelectorAll('.theme-selected').forEach(function (el) { + el.classList.remove('theme-selected'); + }); + themePopup.querySelector("button#" + get_theme()).classList.add('theme-selected'); + } + + function hideThemes() { + themePopup.style.display = 'none'; + themeToggleButton.setAttribute('aria-expanded', false); + themeToggleButton.focus(); + } + + function get_theme() { + var theme; + try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { } + if (theme === null || theme === undefined) { + return default_theme; + } else { + return theme; + } + } + + function set_theme(theme, store = true) { + let ace_theme; + + if (theme == 'coal' || theme == 'navy') { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = false; + stylesheets.highlight.disabled = true; + + ace_theme = "ace/theme/tomorrow_night"; + } else if (theme == 'ayu') { + stylesheets.ayuHighlight.disabled = false; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = true; + ace_theme = "ace/theme/tomorrow_night"; + } else { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = false; + ace_theme = "ace/theme/dawn"; + } + + setTimeout(function () { + themeColorMetaTag.content = getComputedStyle(document.documentElement).backgroundColor; + }, 1); + + if (window.ace && window.editors) { + window.editors.forEach(function (editor) { + editor.setTheme(ace_theme); + }); + } + + var previousTheme = get_theme(); + + if (store) { + try { localStorage.setItem('mdbook-theme', theme); } catch (e) { } + } + + html.classList.remove(previousTheme); + html.classList.add(theme); + updateThemeSelected(); + } + + // Set theme + var theme = get_theme(); + + set_theme(theme, false); + + themeToggleButton.addEventListener('click', function () { + if (themePopup.style.display === 'block') { + hideThemes(); + } else { + showThemes(); + } + }); + + themePopup.addEventListener('click', function (e) { + var theme; + if (e.target.className === "theme") { + theme = e.target.id; + } else if (e.target.parentElement.className === "theme") { + theme = e.target.parentElement.id; + } else { + return; + } + set_theme(theme); + + //refresh to update svg styling + location.reload() + }); + + themePopup.addEventListener('focusout', function (e) { + // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) + if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) { + hideThemes(); + } + }); + + // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 + document.addEventListener('click', function (e) { + if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) { + hideThemes(); + } + }); + + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (!themePopup.contains(e.target)) { return; } + + switch (e.key) { + case 'Escape': + e.preventDefault(); + hideThemes(); + break; + case 'ArrowUp': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.previousElementSibling) { + li.previousElementSibling.querySelector('button').focus(); + } + break; + case 'ArrowDown': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.nextElementSibling) { + li.nextElementSibling.querySelector('button').focus(); + } + break; + case 'Home': + e.preventDefault(); + themePopup.querySelector('li:first-child button').focus(); + break; + case 'End': + e.preventDefault(); + themePopup.querySelector('li:last-child button').focus(); + break; + } + }); +})(); + +(function sidebar() { + var html = document.querySelector("html"); + var sidebar = document.getElementById("sidebar"); + var sidebarLinks = document.querySelectorAll('#sidebar a'); + var sidebarToggleButton = document.getElementById("sidebar-toggle"); + var sidebarResizeHandle = document.getElementById("sidebar-resize-handle"); + var firstContact = null; + + function showSidebar() { + html.classList.remove('sidebar-hidden') + html.classList.add('sidebar-visible'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', 0); + }); + sidebarToggleButton.setAttribute('aria-expanded', true); + sidebar.setAttribute('aria-hidden', false); + try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { } + } + + + var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle'); + + function toggleSection(ev) { + ev.currentTarget.parentElement.classList.toggle('expanded'); + } + + Array.from(sidebarAnchorToggles).forEach(function (el) { + el.addEventListener('click', toggleSection); + }); + + function hideSidebar() { + html.classList.remove('sidebar-visible') + html.classList.add('sidebar-hidden'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', -1); + }); + sidebarToggleButton.setAttribute('aria-expanded', false); + sidebar.setAttribute('aria-hidden', true); + try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { } + } + + // Toggle sidebar + sidebarToggleButton.addEventListener('click', function sidebarToggle() { + if (html.classList.contains("sidebar-hidden")) { + var current_width = parseInt( + document.documentElement.style.getPropertyValue('--sidebar-width'), 10); + if (current_width < 150) { + document.documentElement.style.setProperty('--sidebar-width', '150px'); + } + showSidebar(); + } else if (html.classList.contains("sidebar-visible")) { + hideSidebar(); + } else { + if (getComputedStyle(sidebar)['transform'] === 'none') { + hideSidebar(); + } else { + showSidebar(); + } + } + }); + + sidebarResizeHandle.addEventListener('mousedown', initResize, false); + + function initResize(e) { + window.addEventListener('mousemove', resize, false); + window.addEventListener('mouseup', stopResize, false); + html.classList.add('sidebar-resizing'); + } + function resize(e) { + var pos = (e.clientX - sidebar.offsetLeft); + if (pos < 20) { + hideSidebar(); + } else { + if (html.classList.contains("sidebar-hidden")) { + showSidebar(); + } + pos = Math.min(pos, window.innerWidth - 100); + document.documentElement.style.setProperty('--sidebar-width', pos + 'px'); + } + } + //on mouseup remove windows functions mousemove & mouseup + function stopResize(e) { + html.classList.remove('sidebar-resizing'); + window.removeEventListener('mousemove', resize, false); + window.removeEventListener('mouseup', stopResize, false); + } + + document.addEventListener('touchstart', function (e) { + firstContact = { + x: e.touches[0].clientX, + time: Date.now() + }; + }, { passive: true }); + + document.addEventListener('touchmove', function (e) { + if (!firstContact) + return; + + var curX = e.touches[0].clientX; + var xDiff = curX - firstContact.x, + tDiff = Date.now() - firstContact.time; + + if (tDiff < 250 && Math.abs(xDiff) >= 150) { + if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) + showSidebar(); + else if (xDiff < 0 && curX < 300) + hideSidebar(); + + firstContact = null; + } + }, { passive: true }); +})(); + +(function chapterNavigation() { + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (window.search && window.search.hasFocus()) { return; } + + switch (e.key) { + case 'ArrowRight': + e.preventDefault(); + var nextButton = document.querySelector('.nav-chapters.next'); + if (nextButton) { + window.location.href = nextButton.href; + } + break; + case 'ArrowLeft': + e.preventDefault(); + var previousButton = document.querySelector('.nav-chapters.previous'); + if (previousButton) { + window.location.href = previousButton.href; + } + break; + } + }); +})(); + +(function clipboard() { + var clipButtons = document.querySelectorAll('.clip-button'); + + function hideTooltip(elem) { + elem.firstChild.innerText = ""; + elem.className = 'fa fa-copy clip-button'; + } + + function showTooltip(elem, msg) { + elem.firstChild.innerText = msg; + elem.className = 'fa fa-copy tooltipped'; + } + + var clipboardSnippets = new ClipboardJS('.clip-button', { + text: function (trigger) { + hideTooltip(trigger); + let playground = trigger.closest("pre"); + return playground_text(playground, false); + } + }); + + Array.from(clipButtons).forEach(function (clipButton) { + clipButton.addEventListener('mouseout', function (e) { + hideTooltip(e.currentTarget); + }); + }); + + clipboardSnippets.on('success', function (e) { + e.clearSelection(); + showTooltip(e.trigger, "Copied!"); + }); + + clipboardSnippets.on('error', function (e) { + showTooltip(e.trigger, "Clipboard error!"); + }); +})(); + +(function scrollToTop() { + var menuTitle = document.querySelector('.menu-title'); + + menuTitle.addEventListener('click', function () { + document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); + }); +})(); + +(function controllMenu() { + var menu = document.getElementById('menu-bar'); + + (function controllPosition() { + var scrollTop = document.scrollingElement.scrollTop; + var prevScrollTop = scrollTop; + var minMenuY = -menu.clientHeight - 50; + // When the script loads, the page can be at any scroll (e.g. if you reforesh it). + menu.style.top = scrollTop + 'px'; + // Same as parseInt(menu.style.top.slice(0, -2), but faster + var topCache = menu.style.top.slice(0, -2); + menu.classList.remove('sticky'); + var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster + document.addEventListener('scroll', function () { + scrollTop = Math.max(document.scrollingElement.scrollTop, 0); + // `null` means that it doesn't need to be updated + var nextSticky = null; + var nextTop = null; + var scrollDown = scrollTop > prevScrollTop; + var menuPosAbsoluteY = topCache - scrollTop; + if (scrollDown) { + nextSticky = false; + if (menuPosAbsoluteY > 0) { + nextTop = prevScrollTop; + } + } else { + if (menuPosAbsoluteY > 0) { + nextSticky = true; + } else if (menuPosAbsoluteY < minMenuY) { + nextTop = prevScrollTop + minMenuY; + } + } + if (nextSticky === true && stickyCache === false) { + menu.classList.add('sticky'); + stickyCache = true; + } else if (nextSticky === false && stickyCache === true) { + menu.classList.remove('sticky'); + stickyCache = false; + } + if (nextTop !== null) { + menu.style.top = nextTop + 'px'; + topCache = nextTop; + } + prevScrollTop = scrollTop; + }, { passive: true }); + })(); + (function controllBorder() { + function updateBorder() { + if (menu.offsetTop === 0) { + menu.classList.remove('bordered'); + } else { + menu.classList.add('bordered'); + } + } + updateBorder(); + document.addEventListener('scroll', updateBorder, { passive: true }); + })(); +})(); diff --git a/theme/index.hbs b/theme/index.hbs new file mode 100644 index 0000000..2a8b883 --- /dev/null +++ b/theme/index.hbs @@ -0,0 +1,365 @@ + + + + + + + {{ title }} + {{#if is_print }} + + {{/if}} + {{#if base_url}} + + {{/if}} + + + + {{> head}} + + + + + + {{#if favicon_svg}} + + {{/if}} + {{#if favicon_png}} + + {{/if}} + + + + {{#if print_enable}} + + {{/if}} + + + + {{#if copy_fonts}} + + {{/if}} + + + + + + + + {{#each additional_css}} + + {{/each}} + + {{#if mathjax_support}} + + + {{/if}} + + + +
+ + + + + + + + + + + + + + + + + +
+ +
+ {{> header}} + + + + {{#if search_enabled}} + + {{/if}} + + + + +
+
+ {{{ content }}} +
+ + +
+
+ + + +
+ + {{#if live_reload_endpoint}} + + + {{/if}} + + {{#if google_analytics}} + + + {{/if}} + + {{#if playground_line_numbers}} + + {{/if}} + + {{#if playground_copyable}} + + {{/if}} + + {{#if playground_js}} + + + + + + {{/if}} + + {{#if search_js}} + + + + {{/if}} + + + + + + + {{#each additional_js}} + + {{/each}} + + {{#if is_print}} + {{#if mathjax_support}} + + {{else}} + + {{/if}} + {{/if}} + +
+ + + \ No newline at end of file