magic! now it works on web kit
This commit is contained in:
		
							parent
							
								
									33416e8963
								
							
						
					
					
						commit
						3a4eb87ca0
					
				
					 17 changed files with 164 additions and 116 deletions
				
			
		| 
						 | 
				
			
			@ -527,7 +527,8 @@ where
 | 
			
		|||
                                escape_html(&mut self.writer, &branch.attributes.id)?;
 | 
			
		||||
                                self.writer.write_str("\">")?;
 | 
			
		||||
                            }
 | 
			
		||||
                            self.writer.write_str("<img is=\"th-emoji\" title=\":")?;
 | 
			
		||||
                            self.writer
 | 
			
		||||
                                .write_str("<img data-cast=\"emoji\" title=\":")?;
 | 
			
		||||
                            escape_html(&mut self.writer, name)?;
 | 
			
		||||
                            self.writer.write_str(":\" src=\"")?;
 | 
			
		||||
                            escape_html(&mut self.writer, &self.config.site)?;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,9 +45,9 @@ pub fn branch_to_html(
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    let component = if let Content::Link(_) = branch.attributes.content {
 | 
			
		||||
        "th-b-linked"
 | 
			
		||||
        "b-linked"
 | 
			
		||||
    } else {
 | 
			
		||||
        "th-b"
 | 
			
		||||
        "b"
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let linked_branch = if let Content::Link(link) = &branch.attributes.content {
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +64,7 @@ pub fn branch_to_html(
 | 
			
		|||
 | 
			
		||||
    write!(
 | 
			
		||||
        s,
 | 
			
		||||
        "<li is=\"{component}\" class=\"{class}\" id=\"{}\"{linked_branch}{do_not_persist}>",
 | 
			
		||||
        "<li data-cast=\"{component}\" class=\"{class}\" id=\"{}\"{linked_branch}{do_not_persist}>",
 | 
			
		||||
        EscapeAttribute(&branch.html_id)
 | 
			
		||||
    )
 | 
			
		||||
    .unwrap();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -107,7 +107,7 @@ pub enum Content {
 | 
			
		|||
 | 
			
		||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)]
 | 
			
		||||
pub struct Classes {
 | 
			
		||||
    /// Classes to append to the branch itself (<li is="th-b">).
 | 
			
		||||
    /// Classes to append to the branch itself (<li data-cast="b">).
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    pub branch: String,
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
            "tairu/*": [
 | 
			
		||||
                "./components/tairu/*"
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
        },
 | 
			
		||||
        "target": "ES2020",
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -447,7 +447,7 @@ h1.page-title {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/* Style the `new` link on the homepage */
 | 
			
		||||
a[is="th-new"] {
 | 
			
		||||
a[data-cast~="new"] {
 | 
			
		||||
    flex-shrink: 0;
 | 
			
		||||
    color: var(--text-color);
 | 
			
		||||
    opacity: 50%;
 | 
			
		||||
| 
						 | 
				
			
			@ -509,7 +509,7 @@ footer {
 | 
			
		|||
 | 
			
		||||
/* Style emojis to be readable */
 | 
			
		||||
 | 
			
		||||
img[is="th-emoji"] {
 | 
			
		||||
img[data-cast~="emoji"] {
 | 
			
		||||
    max-width: 1.5em;
 | 
			
		||||
    max-height: 1.5em;
 | 
			
		||||
    vertical-align: bottom;
 | 
			
		||||
| 
						 | 
				
			
			@ -777,7 +777,7 @@ th-literate-program[data-mode="output"] {
 | 
			
		|||
 | 
			
		||||
/* Style settings sections */
 | 
			
		||||
 | 
			
		||||
section[is="th-settings"] {
 | 
			
		||||
section[data-cast~="settings"] {
 | 
			
		||||
    /* Don't display settings when JavaScript is disabled.
 | 
			
		||||
       JS overrides this value on the element itself. */
 | 
			
		||||
    display: none;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,7 +64,7 @@ section {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section[is="th-settings"] {
 | 
			
		||||
section.settings {
 | 
			
		||||
    & h3 {
 | 
			
		||||
        display: inline;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -131,4 +131,4 @@ section[is="th-settings"] {
 | 
			
		|||
            border-color: white;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,13 @@
 | 
			
		|||
// Emoji zoom-in functionality.
 | 
			
		||||
 | 
			
		||||
import { addSpell } from "treehouse/spells.js";
 | 
			
		||||
 | 
			
		||||
class EmojiTooltip extends HTMLElement {
 | 
			
		||||
    constructor(emoji, { onClosed }) {
 | 
			
		||||
    constructor(emoji, element, { onClosed }) {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        this.emoji = emoji;
 | 
			
		||||
        this.emojiElement = element;
 | 
			
		||||
        this.onClosed = onClosed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -12,12 +15,12 @@ class EmojiTooltip extends HTMLElement {
 | 
			
		|||
        this.role = "tooltip";
 | 
			
		||||
 | 
			
		||||
        this.image = new Image();
 | 
			
		||||
        this.image.src = this.emoji.src;
 | 
			
		||||
        this.image.src = this.emojiElement.src;
 | 
			
		||||
 | 
			
		||||
        this.description = document.createElement("p");
 | 
			
		||||
        this.description.textContent = `${this.emoji.emojiName}`;
 | 
			
		||||
 | 
			
		||||
        let emojiBoundingBox = this.emoji.getBoundingClientRect();
 | 
			
		||||
        let emojiBoundingBox = this.emojiElement.getBoundingClientRect();
 | 
			
		||||
        this.style.left = `${emojiBoundingBox.left + emojiBoundingBox.width / 2}px`;
 | 
			
		||||
        this.style.top = `calc(${emojiBoundingBox.top}px + 1.5em)`;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,8 +71,8 @@ class EmojiTooltips extends HTMLElement {
 | 
			
		|||
        this.abortController.abort();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    openTooltip(emoji) {
 | 
			
		||||
        let tooltip = new EmojiTooltip(emoji, {
 | 
			
		||||
    openTooltip(emoji, element) {
 | 
			
		||||
        let tooltip = new EmojiTooltip(emoji, element, {
 | 
			
		||||
            onClosed: () => {
 | 
			
		||||
                this.removeChild(tooltip);
 | 
			
		||||
                this.tooltips.delete(tooltip);
 | 
			
		||||
| 
						 | 
				
			
			@ -95,21 +98,21 @@ class EmojiTooltips extends HTMLElement {
 | 
			
		|||
 | 
			
		||||
customElements.define("th-emoji-tooltips", EmojiTooltips);
 | 
			
		||||
 | 
			
		||||
class Emoji extends HTMLImageElement {
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        this.emojiName = this.title;
 | 
			
		||||
class Emoji {
 | 
			
		||||
    constructor(element) {
 | 
			
		||||
        this.emojiName = element.title;
 | 
			
		||||
 | 
			
		||||
        // title makes the browser add a tooltip. We replace browser tooltips with our own,
 | 
			
		||||
        // so remove the title.
 | 
			
		||||
        this.title = "";
 | 
			
		||||
        element.title = "";
 | 
			
		||||
 | 
			
		||||
        this.addEventListener("mouseenter", () => this.openTooltip());
 | 
			
		||||
        this.addEventListener("mouseleave", () => this.closeTooltip());
 | 
			
		||||
        this.addEventListener("scroll", () => this.closeTooltip());
 | 
			
		||||
        element.addEventListener("mouseenter", () => this.openTooltip(element));
 | 
			
		||||
        element.addEventListener("mouseleave", () => this.closeTooltip());
 | 
			
		||||
        element.addEventListener("scroll", () => this.closeTooltip());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    openTooltip() {
 | 
			
		||||
        this.tooltip = emojiTooltips.openTooltip(this);
 | 
			
		||||
    openTooltip(element) {
 | 
			
		||||
        this.tooltip = emojiTooltips.openTooltip(this, element);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    closeTooltip() {
 | 
			
		||||
| 
						 | 
				
			
			@ -118,4 +121,4 @@ class Emoji extends HTMLImageElement {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("th-emoji", Emoji, { extends: "img" });
 | 
			
		||||
addSpell("emoji", Emoji);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
// news.js because new.js makes the TypeScript language server flip out.
 | 
			
		||||
// Likely because `new` is a keyword, but also, what the fuck.
 | 
			
		||||
 | 
			
		||||
import { addSpell, spell } from "treehouse/spells.js";
 | 
			
		||||
import { getSettingValue } from "treehouse/settings.js";
 | 
			
		||||
import { Branch } from "treehouse/tree.js";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -15,15 +16,17 @@ function saveSeenStates() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
function markAsRead(branch) {
 | 
			
		||||
    if (!seenStates.has(branch.namedID) && seenCount > 0) {
 | 
			
		||||
    let branchData = spell(branch, Branch);
 | 
			
		||||
 | 
			
		||||
    if (!seenStates.has(branchData.namedID) && seenCount > 0) {
 | 
			
		||||
        let badge = document.createElement("span");
 | 
			
		||||
        badge.classList.add("badge", "red", "before-content");
 | 
			
		||||
        badge.textContent = "new";
 | 
			
		||||
 | 
			
		||||
        branch.branchContent.firstChild.insertBefore(badge, branch.branchContent.firstChild.firstChild);
 | 
			
		||||
        branchData.branchContent.firstChild.insertBefore(badge, branchData.branchContent.firstChild.firstChild);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    seenStates.add(branch.namedID);
 | 
			
		||||
    seenStates.add(branchData.namedID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function initNewsPage() {
 | 
			
		||||
| 
						 | 
				
			
			@ -43,8 +46,8 @@ export function markAllAsUnread() {
 | 
			
		|||
    localStorage.removeItem(seenStatesKey);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class New extends HTMLAnchorElement {
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
addSpell("new", class New {
 | 
			
		||||
    constructor(element) {
 | 
			
		||||
        // Do not show the badge to people who have never seen any news.
 | 
			
		||||
        // It's just annoying in that case.
 | 
			
		||||
        // In case you do not wish to see the badge anymore, go to the news page and uncheck the
 | 
			
		||||
| 
						 | 
				
			
			@ -54,17 +57,15 @@ class New extends HTMLAnchorElement {
 | 
			
		|||
        if (userSawNews && userWantsToSeeNews && unseenCount > 0) {
 | 
			
		||||
            this.newText = document.createElement("span");
 | 
			
		||||
            this.newText.classList.add("new-text");
 | 
			
		||||
            this.newText.textContent = this.textContent;
 | 
			
		||||
            this.textContent = "";
 | 
			
		||||
            this.appendChild(this.newText);
 | 
			
		||||
            this.newText.textContent = element.textContent;
 | 
			
		||||
            element.textContent = "";
 | 
			
		||||
            element.appendChild(this.newText);
 | 
			
		||||
 | 
			
		||||
            this.badge = document.createElement("span");
 | 
			
		||||
            this.badge.classList.add("badge", "red");
 | 
			
		||||
            this.badge.textContent = unseenCount.toString();
 | 
			
		||||
            this.appendChild(this.badge);
 | 
			
		||||
            this.classList.add("has-news");
 | 
			
		||||
            element.appendChild(this.badge);
 | 
			
		||||
            element.classList.add("has-news");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("th-new", New, { extends: "a" });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
import { addSpell } from "treehouse/spells.js";
 | 
			
		||||
 | 
			
		||||
const settingsKey = "treehouse.settings";
 | 
			
		||||
const settings = JSON.parse(localStorage.getItem(settingsKey)) || {};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -13,23 +15,15 @@ export function getSettingValue(setting) {
 | 
			
		|||
    return settings[setting] ?? defaultSettingValues[setting];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SettingCheckbox extends HTMLInputElement {
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        this.checked = getSettingValue(this.id);
 | 
			
		||||
class SettingCheckbox {
 | 
			
		||||
    constructor(element) {
 | 
			
		||||
        element.checked = getSettingValue(element.id);
 | 
			
		||||
 | 
			
		||||
        this.addEventListener("change", () => {
 | 
			
		||||
            settings[this.id] = this.checked;
 | 
			
		||||
        element.addEventListener("change", () => {
 | 
			
		||||
            settings[element.id] = element.checked;
 | 
			
		||||
            saveSettings();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("th-setting-checkbox", SettingCheckbox, { extends: "input" });
 | 
			
		||||
 | 
			
		||||
class Settings extends HTMLElement {
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        this.style.display = "block";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("th-settings", Settings, { extends: "section" });
 | 
			
		||||
addSpell("setting-checkbox", SettingCheckbox);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										85
									
								
								static/js/spells.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								static/js/spells.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,85 @@
 | 
			
		|||
// Spells are a simplistic, composition-based replacement for the is="" attribute, which is not
 | 
			
		||||
// supported on Webkit due to Apple's engineering being a bunch of obstinate idiots who explicitly
 | 
			
		||||
// choose not to follow web standards.
 | 
			
		||||
 | 
			
		||||
// Limitations:
 | 
			
		||||
//   - The data-cast attribute cannot be added dynamically.
 | 
			
		||||
//   - There is no disconnectedCallback. Only a constructor which initializes a spell.
 | 
			
		||||
 | 
			
		||||
let spells = new Map();
 | 
			
		||||
let elementsWithUnknownSpells = new Map();
 | 
			
		||||
 | 
			
		||||
const sSpells = Symbol("spells");
 | 
			
		||||
 | 
			
		||||
function castSpellOnElement(spellName, element) {
 | 
			
		||||
    element[sSpells] ??= new Map();
 | 
			
		||||
    if (!elementsWithUnknownSpells.has(spellName)) {
 | 
			
		||||
        elementsWithUnknownSpells.set(spellName, new Set());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let Spell = spells.get(spellName);
 | 
			
		||||
    if (Spell != null && !element[sSpells].has(Spell)) {
 | 
			
		||||
        element[sSpells].set(Spell, new Spell(element));
 | 
			
		||||
        elementsWithUnknownSpells.get(spellName).delete(element);
 | 
			
		||||
    } else {
 | 
			
		||||
        elementsWithUnknownSpells.get(spellName).add(element);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function applySpells(elements) {
 | 
			
		||||
    for (let element of elements) {
 | 
			
		||||
        if (element instanceof Element) {
 | 
			
		||||
            let spellListString = element.getAttribute("data-cast");
 | 
			
		||||
            if (spellListString != null) {
 | 
			
		||||
                let spellList = spellListString.split(' ');
 | 
			
		||||
                for (let spellName of spellList) {
 | 
			
		||||
                    castSpellOnElement(spellName, element);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function addSpell(name, spell) {
 | 
			
		||||
    spells.set(name, spell);
 | 
			
		||||
    let elementsWithThisSpell = elementsWithUnknownSpells.get(name);
 | 
			
		||||
    if (elementsWithThisSpell != null) {
 | 
			
		||||
        for (let element of elementsWithThisSpell) {
 | 
			
		||||
            castSpellOnElement(name, element);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns a spell's data. Gotchas: the spell needs to already be on the element.
 | 
			
		||||
// Therefore, if this is used from within a spell, the requested spell must have already been
 | 
			
		||||
// applied by this point.
 | 
			
		||||
// Someday I may change this to an async function that resumes whenever the spell is available to
 | 
			
		||||
// iron over this limitation. But today is not that day.
 | 
			
		||||
export function spell(element, spell) {
 | 
			
		||||
    return element[sSpells].get(spell);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Apply spells to elements which have them and have been loaded so far.
 | 
			
		||||
let loadedSoFar = document.querySelectorAll("[data-cast]");
 | 
			
		||||
applySpells(loadedSoFar);
 | 
			
		||||
 | 
			
		||||
// For all other elements, add a mutation observer that will handle them.
 | 
			
		||||
let mutationObserver = new MutationObserver(records => {
 | 
			
		||||
    for (let record of records) {
 | 
			
		||||
        let mutatedNodes = new Set();
 | 
			
		||||
        // NOTE: Added nodes may contain children which also need to be processed.
 | 
			
		||||
        // Collect those that have [data-cast] on them and apply spells to them.
 | 
			
		||||
        for (let addedNode of record.addedNodes) {
 | 
			
		||||
            addedNode.querySelectorAll("[data-cast]").forEach(element => mutatedNodes.add(element));
 | 
			
		||||
        }
 | 
			
		||||
        applySpells(mutatedNodes);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
mutationObserver.observe(document, { subtree: true, childList: true });
 | 
			
		||||
 | 
			
		||||
// ------------ Common spells ------------
 | 
			
		||||
 | 
			
		||||
// js makes things visible only when JavaScript is enabled.
 | 
			
		||||
addSpell("js", function (element) {
 | 
			
		||||
    element.style.display = "block";
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -1,21 +0,0 @@
 | 
			
		|||
// Detect if we can have crucial functionality (ie. custom elements call constructors).
 | 
			
		||||
// This doesn't seem to happen in Epiphany, and also other Webkit-based browsers.
 | 
			
		||||
let works = false;
 | 
			
		||||
class WebkitMoment extends HTMLLIElement {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
        works = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("th-webkit-moment", WebkitMoment, { extends: "li" });
 | 
			
		||||
 | 
			
		||||
let willItWorkOrWillItNot = document.createElement("div");
 | 
			
		||||
willItWorkOrWillItNot.innerHTML = `<li is="th-webkit-moment"></li>`;
 | 
			
		||||
 | 
			
		||||
// If my takeoff fails
 | 
			
		||||
// tell my mother I'm sorry
 | 
			
		||||
let box = document.getElementById("webkit-makes-me-go-insane");
 | 
			
		||||
if (!works) {
 | 
			
		||||
    box.style = "display: block";
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
// This is definitely not a three.js ripoff.
 | 
			
		||||
 | 
			
		||||
import { addSpell } from "treehouse/spells.js";
 | 
			
		||||
import { navigationMap } from "/navmap.js";
 | 
			
		||||
import * as ulid from "treehouse/ulid.js";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -17,18 +18,20 @@ function branchIsOpen(branchID) {
 | 
			
		|||
    return branchState[branchID];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class Branch extends HTMLLIElement {
 | 
			
		||||
export class Branch {
 | 
			
		||||
    static branchesByNamedID = new Map();
 | 
			
		||||
    static onAdded = [];
 | 
			
		||||
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        this.isLeaf = this.classList.contains("leaf");
 | 
			
		||||
    constructor(element) {
 | 
			
		||||
        this.element = element;
 | 
			
		||||
 | 
			
		||||
        this.details = this.childNodes[0];
 | 
			
		||||
        this.isLeaf = element.classList.contains("leaf");
 | 
			
		||||
 | 
			
		||||
        this.details = element.childNodes[0];
 | 
			
		||||
        this.innerUL = this.details.childNodes[1];
 | 
			
		||||
 | 
			
		||||
        if (this.isLeaf) {
 | 
			
		||||
            this.contentContainer = this.childNodes[0];
 | 
			
		||||
            this.contentContainer = element.childNodes[0];
 | 
			
		||||
        } else {
 | 
			
		||||
            this.contentContainer = this.details.childNodes[0];
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -36,19 +39,19 @@ export class Branch extends HTMLLIElement {
 | 
			
		|||
        this.branchContent = this.contentContainer.childNodes[1];
 | 
			
		||||
        this.buttonBar = this.contentContainer.childNodes[2];
 | 
			
		||||
 | 
			
		||||
        let doPersist = !this.hasAttribute("data-th-do-not-persist");
 | 
			
		||||
        let isOpen = branchIsOpen(this.id);
 | 
			
		||||
        let doPersist = !element.hasAttribute("data-th-do-not-persist");
 | 
			
		||||
        let isOpen = branchIsOpen(element.id);
 | 
			
		||||
        if (doPersist && isOpen !== undefined) {
 | 
			
		||||
            this.details.open = isOpen;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.isLeaf) {
 | 
			
		||||
            this.details.addEventListener("toggle", _ => {
 | 
			
		||||
                saveBranchIsOpen(this.id, this.details.open);
 | 
			
		||||
                saveBranchIsOpen(element.id, this.details.open);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.namedID = this.id.split(':')[1];
 | 
			
		||||
        Branch.branchesByNamedID.set(this.namedID, this);
 | 
			
		||||
        this.namedID = element.id.split(':')[1];
 | 
			
		||||
        Branch.branchesByNamedID.set(this.namedID, element);
 | 
			
		||||
 | 
			
		||||
        if (ulid.isCanonicalUlid(this.namedID)) {
 | 
			
		||||
            let timestamp = ulid.getTimestamp(this.namedID);
 | 
			
		||||
| 
						 | 
				
			
			@ -59,22 +62,22 @@ export class Branch extends HTMLLIElement {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        for (let callback of Branch.onAdded) {
 | 
			
		||||
            callback(this);
 | 
			
		||||
            callback(element, this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("th-b", Branch, { extends: "li" });
 | 
			
		||||
addSpell("b", Branch);
 | 
			
		||||
 | 
			
		||||
/* Linked branches */
 | 
			
		||||
 | 
			
		||||
class LinkedBranch extends Branch {
 | 
			
		||||
    static byLink = new Map();
 | 
			
		||||
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        super.connectedCallback();
 | 
			
		||||
    constructor(element) {
 | 
			
		||||
        super(element);
 | 
			
		||||
 | 
			
		||||
        this.linkedTree = this.getAttribute("data-th-link");
 | 
			
		||||
        this.linkedTree = element.getAttribute("data-th-link");
 | 
			
		||||
        LinkedBranch.byLink.set(this.linkedTree, this);
 | 
			
		||||
 | 
			
		||||
        this.loadingText = document.createElement("p");
 | 
			
		||||
| 
						 | 
				
			
			@ -115,11 +118,10 @@ class LinkedBranch extends Branch {
 | 
			
		|||
            let styles = main.getElementsByTagName("link");
 | 
			
		||||
            let scripts = main.getElementsByTagName("script");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            this.loadingText.remove();
 | 
			
		||||
            this.innerUL.innerHTML = ul.innerHTML;
 | 
			
		||||
 | 
			
		||||
            this.append(...styles);
 | 
			
		||||
            this.element.append(...styles);
 | 
			
		||||
            for (let script of scripts) {
 | 
			
		||||
                // No need to await for the import because we don't use the resulting module.
 | 
			
		||||
                // Just fire and forger 💀
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +141,7 @@ class LinkedBranch extends Branch {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("th-b-linked", LinkedBranch, { extends: "li" });
 | 
			
		||||
addSpell("b-linked", LinkedBranch);
 | 
			
		||||
 | 
			
		||||
/* Fragment navigation */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,12 +7,3 @@ document.addEventListener("click", event => {
 | 
			
		|||
        event.preventDefault();
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// Certain words don't make sense if scripts are disabled.
 | 
			
		||||
class YesScript extends HTMLElement {
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        this.classList.add("yes-indeed");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("th-yesscript", YesScript);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,13 +11,12 @@
 | 
			
		|||
 | 
			
		||||
    {{!-- For /index, include a "new" link that goes to the curated news feed page. --}}
 | 
			
		||||
    {{#if (eq page.tree_path "index")}}
 | 
			
		||||
    <a href="{{ config.site }}/treehouse/new.html" is="th-new">new</a>
 | 
			
		||||
    <a href="{{ config.site }}/treehouse/new.html" data-cast="new">new</a>
 | 
			
		||||
    {{/if}}
 | 
			
		||||
 | 
			
		||||
    {{/ components/_nav.hbs }}
 | 
			
		||||
 | 
			
		||||
    {{> components/_noscript.hbs }}
 | 
			
		||||
    {{> components/_webkit.hbs }}
 | 
			
		||||
 | 
			
		||||
    <section>
 | 
			
		||||
        <p>welcome!</p>
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +32,7 @@
 | 
			
		|||
        </p>
 | 
			
		||||
    </section>
 | 
			
		||||
 | 
			
		||||
    <section is="th-settings">
 | 
			
		||||
    <section class="settings" data-cast="js">
 | 
			
		||||
        <details>
 | 
			
		||||
            <summary>
 | 
			
		||||
                settings
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +40,7 @@
 | 
			
		|||
            <section>
 | 
			
		||||
                <p>if you find the newsfeed annoying, you can customize some aspects of it.</p>
 | 
			
		||||
                <p>
 | 
			
		||||
                    <input type="checkbox" is="th-setting-checkbox" id="showNewPostIndicator">
 | 
			
		||||
                    <input type="checkbox" data-cast="setting-checkbox" id="showNewPostIndicator">
 | 
			
		||||
                    <label for="showNewPostIndicator">show the <span class="badge red">1</span> badge on the homepage
 | 
			
		||||
                        for
 | 
			
		||||
                        new posts you haven't read yet</label>
 | 
			
		||||
| 
						 | 
				
			
			@ -70,4 +69,4 @@
 | 
			
		|||
    </script>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,13 +11,12 @@
 | 
			
		|||
 | 
			
		||||
    {{!-- For /index, include a "new" link that goes to the curated news feed page. --}}
 | 
			
		||||
    {{#if (eq page.tree_path "index")}}
 | 
			
		||||
    <a href="{{ config.site }}/treehouse/new.html" is="th-new">new</a>
 | 
			
		||||
    <a href="{{ config.site }}/treehouse/new.html" data-cast="new">new</a>
 | 
			
		||||
    {{/if}}
 | 
			
		||||
 | 
			
		||||
    {{/ components/_nav.hbs }}
 | 
			
		||||
 | 
			
		||||
    {{> components/_noscript.hbs }}
 | 
			
		||||
    {{> components/_webkit.hbs }}
 | 
			
		||||
 | 
			
		||||
    {{!--
 | 
			
		||||
    NOTE: ~ because components/_tree.hbss must not include any extra indentation, because it may
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,12 +20,12 @@
 | 
			
		|||
    const TREEHOUSE_NEWS_COUNT = {{ len feeds.news.branches }};
 | 
			
		||||
</script>
 | 
			
		||||
<script type="module" src="{{ config.site }}/navmap.js"></script>
 | 
			
		||||
<script type="module" src="{{ config.site }}/static/js/spells.js"></script>
 | 
			
		||||
<script type="module" src="{{ config.site }}/static/js/ulid.js"></script>
 | 
			
		||||
<script type="module" src="{{ config.site }}/static/js/usability.js"></script>
 | 
			
		||||
<script type="module" src="{{ config.site }}/static/js/settings.js"></script>
 | 
			
		||||
<script type="module" src="{{ config.site }}/static/js/tree.js"></script>
 | 
			
		||||
<script type="module" src="{{ config.site }}/static/js/emoji.js"></script>
 | 
			
		||||
<script type="module" src="{{ config.site }}/static/js/thanks-webkit.js"></script>
 | 
			
		||||
<script type="module" src="{{ config.site }}/static/js/news.js"></script>
 | 
			
		||||
 | 
			
		||||
<meta property="og:site_name" content="{{ config.user.title }}">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +0,0 @@
 | 
			
		|||
<div id="webkit-makes-me-go-insane" class="noscript" role="note">
 | 
			
		||||
    <p>hey! looks like you're using a weird or otherwise quirky web browser. this basically means, the website will
 | 
			
		||||
        not work for you correctly. I might fix it in the future but I have very limited time to work on this
 | 
			
		||||
        website and so don't have an estimate on when that might happen.</p>
 | 
			
		||||
    <p>in the meantime I suggest switching to <a href="https://firefox.com">something more modern.</a></p>
 | 
			
		||||
    <p>sorry for the inconvenience!</p>
 | 
			
		||||
</div>
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue