Public
Edited
Nov 24, 2023
Paused
1 fork
Importers
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
// add <= at the bottom to change the top label orentation

// parametres du diagramme
const off = 80; // offset des leaves
const offDNMN = 32; // offset des leaves
let scale = 2.5; // echelle globale
const sizeDepth0 = 28; // font size
const sizeDepth1 = 21;
const sizeDepth2 = 16;
const sizeHeight0 = 8.5;
const sizeHeight1 = 12;
const size = 12;
const bulletDepth0 = 2.5; // bullet size
const bulletDepth1 = 2;
const bullet = 2;
const bulletHeight1 = 1.5;
const bulletHeight0 = 1.5;

// parametres angulaires
// let divisor = 1.035 + 0.008 - 0.004 + 0.005; // division of the full circle // 1.8
// let angleDeg = -177.3 + 1.4 - 0.6 + 0.8; // angle offset from midday position // -10 is interesting
// let angleRad = (Math.PI / 180) * angleDeg;
// let sepDeg = 2;
// let sepRad = (Math.PI / 180) * sepDeg;
let divisor = 1.035 + 0.008 - 0.004 - 0.007; // division of the full circle // 1.8
let angleDeg = -177.3 + 1.4 - 0.6 - 0.1; // angle offset from midday position // -10 is interesting
let angleRad = (Math.PI / 180) * angleDeg;
let sepDeg = 1.2;
let sepRad = (Math.PI / 180) * sepDeg;
// ajustements fins du diagramme

const root = tree(data);
// const str = "vaggasaṁyutta";
let heightSN = 800;

root.each(function (d) {
d.y = d.y * scale;
if (d.data.name == "Devatāsaṁyutta") heightSN = d.y;
if (
d
.ancestors()
.find(
(x) =>
x.data.name == "Nidānasaṁyutta" ||
x.data.name == "Khandhasaṁyutta" ||
x.data.name == "Saḷāyatanasaṁyutta"
) &&
d.height == 1
) {
d.y = 890;
d.data.spec = "fontSN";
}
// if (d.height == 1 || d.height == 0) d.y = 600;
// if (d.height == 2) d.y = 490;
//if (d.height == 1) d.y = 800;
if (d.height == 2) d.y = heightSN;
// if (d.height == 3) d.y = 280;
if (d.depth == 1) d.y = 160 + 100 + 25 + 5;
if (d.depth == 2 && !d.data.acro?.includes("DN")) d.y = 400 + 100 + 30;
//if (d.depth == 1) d.data.name = d.data.name.replace(str, "");
if (d.depth == 0) d.x = (Math.PI / 2 - angleRad) * divisor;
//if (d.depth == 0) d.x = (2 * Math.PI) / 2;
if (d.depth == 0) d.y = -70;
if (d.ancestors().find((x) => x.data.name == "Majjhimanikāya"))
d.x = d.x + sepRad;
if (d.ancestors().find((x) => x.data.name == "Saṁyuttanikāya"))
d.x = d.x + 2 * sepRad;
if (d.ancestors().find((x) => x.data.name == "Aṅguttaranikāya"))
d.x = d.x + 3 * sepRad;
if (d.data.id == "test") {
d.y = 340 + 60 - 30 - 31; // could be calculated exaclty fo kn branch
d.x = (Math.PI - angleRad) * divisor;
}
if (d.data.id == "test2") {
d.y = 340 + 60 + 60; // could be calculated exaclty fo kn branch
// d.y = 340 + 60 - 30 + 200; // could be calculated exaclty fo kn branch
d.x = (Math.PI - angleRad) * divisor;
}
});

// testing

// console.log(root);

// fonction d'apartenance à DN ou MN (utile pour les acros)

const isDNMN = (d) =>
d.data.acro?.includes("DN") || d.data.acro?.includes("MN");

// fonction pour la taille de font

let fontSize = (d) => {
if (d.height == 0) {
return sizeHeight0;
} else if (d.data.spec == "fontSN") {
return sizeHeight0;
} else if (d.data.id.slice(0, 2) == "sn" && d.height == 1) {
return sizeHeight1 - 1;
} else if (d.depth == 1) {
return sizeDepth1;
} else if (d.height == 1 && !d.data.acro.includes("AN")) {
return sizeHeight1;
} else if (d.depth == 2) {
return sizeDepth2;
} else if (d.depth == 0) {
return sizeDepth0;
} else return size;
};

// fonction pour la taille des bullets

let bulletSize = (d) => {
if (d.depth == 0) {
return bulletDepth0;
} else if (
d.data.acro == "SN 12" ||
d.data.acro == "SN 22" ||
d.data.acro == "SN 35"
) {
return bulletHeight1;
} else if (d.data.id == "test" || d.data.id == "test2") {
return 0;
} else if (d.depth == 1) {
return bulletDepth1;
} else if (d.height == 0) {
return bulletHeight0;
} else if (d.height == 1 && !d.data.acro.includes("AN")) {
return bulletHeight1;
} else return bullet;
};

// Fonction qui retourne le nom de l'ancestre de depth 1
// utile pour la colorisation

let oneDepth = (d, source = false) => {
let ancestors = {};
source == true
? (ancestors = d.target.ancestors()) // source <=> target for a different result
: (ancestors = d.ancestors());

const oneDepth = ancestors.find((d) => d.depth == 1);
const result = oneDepth == null ? d.data.name : oneDepth.data.name;
// for some reason it dosen't start with sutta pitaka anymore
return result;
// return oneDepth?.data.name;
};

let color = d3.scaleOrdinal(domain);
// let colorFaded = d3.scaleOrdinal(domainFaded);

let isLeaf = (d) => {
let bool = false;
if (d.height == 0) bool = true;
return bool;
};

const fade = (color) => {
const c = d3.color(color);
c.l += 0.25;
return c + "";
};

// création du diagramme //////////////////////////////////////////////////////////////////////////////////////////////

const svg = d3.create("svg");

const g = svg.append("g").attr("cursor", "grab");

// paths

g.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5)
.selectAll("path")
.data(root.links())
.join("path")
.attr(
"d",
d3
.linkRadial()
.angle((d) => d.x / divisor + angleRad)
.radius((d) => d.y)
)
.attr("stroke", (d) => color(oneDepth(d, true))); // colorisation

// bullet

g.append("g")
.selectAll("circle")
.data(root.descendants())
.join("circle")
.attr(
"transform",
(d) => `
rotate(${(d.x * 180) / divisor / Math.PI - 90 + angleDeg})
translate(${d.y},0)
`
)
.attr("r", (d) => bulletSize(d))
.attr("fill", (d) =>
isLeaf(d) ? fade(color(oneDepth(d))) : color(oneDepth(d))
); // colorisation

// text

g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll("text")
.data(root.descendants())
.join("text")
.attr("font-size", (d) => fontSize(d))
.style("fill", (d) => color(oneDepth(d))) // colorisation
.attr(
"transform",
(d) => `
rotate(${(d.x * 180) / divisor / Math.PI - 90 + angleDeg})
translate(${d.y},0)
rotate(${
d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor
? 180
: 0
})
`
)
.attr("dy", "0.31em")
.attr("x", (d) =>
(d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) ===
!d.children
? -6
: 6
)
.attr("text-anchor", (d) =>
(d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) ===
!d.children
? "end"
: "start"
)
.text((d) => {
if (d.depth == 0 || d.depth == 1) return d.data.name;
if (d.height != 0 && isDNMN(d)) return d.data.name;
if (d.data.acro == "") return d.data.name; // not usefull for KN
if (d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) {
return !d.children
? d.data.name + " : " + d.data.acro
: d.data.acro + " : " + d.data.name;
} else {
return !d.children
? d.data.acro + " : " + d.data.name
: d.data.name + " : " + d.data.acro;
}
})
.style("cursor", "pointer")
.on("click", function (d, i) {
// const link = "https://suttacentral.net/" + i.data.id + "/pli/ms";
let link = "https://suttacentral.net/" + i.data.id;
if (i.data.id == "suttaPitaka")
link = "https://suttacentral.net/pitaka/sutta";
window.open(link);
})
.clone(true)
.lower()
.attr("stroke", "white");

////////////////////////////////////////////////////// KN chart //////////////////////////////////////////////////////

/// add chart2 methode 1 ///

// const svg2 = chart2.node().innerHTML;
// g.append("g").attr("transform", "translate(0,1300)").html(svg2);

/// add chart2 methode 2 /// (to allow use of .on("click"))

scale = 3.5;

divisor = 1.5; // division of the full circle // 1.8
angleDeg = -30 + 90; // angle offset from midday position // -10 is interesting
angleRad = (Math.PI / 180) * angleDeg;
const translation = 1270;

data2.each(function (d) {
if (
d.ancestors().find(
(x) =>
x.data.id != "kn" &&
x.data.id != "kp" &&
x.data.id != "dhp" &&
x.data.id != "bv" &&
x.data.id != "mnd" &&
x.data.id != "mnd" &&
!x.data.id.slice(0, 3) == "cnd" &&
!x.data.id.slice(0, 2) == "cp" &&
!x.data.id.slice(0, 2) == "ps" &&
!x.data.id.slice(0, 2) == "ne" &&
// x.data.id != "ne" &&
x.data.id != "pe"
) &&
d.height == 1
)
d.children = null;
});

const root2 = tree(data2);

root2.each(function (d) {
d.y = d.y * scale;
if (d.height == 1 || d.height == 0) d.y = 600 + 230 + 100 + 40;
if (d.height == 2) d.y = 490 + 230 + 30 + 30;
if (d.depth == 1) d.y = 260 + 100 + 30;

if (d.depth == 0) d.x = Math.PI;
// if (d.depth == 0) d.x = (Math.PI / 2 - angleRad) * divisor;
if (d.depth == 0) d.y = -9 * 80 - 90 - 30 - 20;
if (d.data.name == "test") d.y = 800;
if (d.data.id.slice(0, 3) == "cnd" && d.height == 1)
d.y = 490 + 230 + 30 + 30;
if (d.data.id.slice(0, 3) == "cnd" && d.height == 1) d.data.acro = "";
// if (d.data.id.includes("cnd") && d.height == 2) d.y = 490 + 100;
if (d.data.id.slice(0, 2) == "cp" && d.height == 1)
d.y = 490 + 230 + 30 + 30;
if (d.data.id.slice(0, 2) == "cp" && d.height == 2) d.y = 490 + 100;
if (d.data.id.slice(0, 2) == "cp" && d.height == 2) d.data.acro = "";
if (d.data.id.slice(0, 2) == "ps" && d.height == 1)
d.y = 490 + 230 + 30 + 30;
if (d.data.id.slice(0, 2) == "ps" && d.height == 1) d.data.acro = "";
// if (d.data.id.includes("cp") && d.height == 3) d.y = 490;
if (d.data.id.slice(0, 2) == "ne" && d.height == 1)
d.y = 490 + 230 + 30 + 30;
if (d.data.id.slice(0, 2) == "ne" && d.height == 2) d.y = 490 + 100;
if (d.data.id.slice(0, 2) == "ne" && d.height == 1) d.data.acro = "";
// if (d.data.id.includes("ne") && d.height == 3) d.y = 490;
// if (data.id.slice(0, 3) == "cnd" && d.height == 1) d.y = 490 + 230 + 30;
});

// fonction pour la taille de font

fontSize = (d) => {
if (d.height == 0) {
return sizeHeight0;
} else if (d.data.id.slice(0, 2) == "ne" && d.height == 1) {
return sizeHeight1;
} else if (d.data.id.slice(0, 2) == "ps" && d.height == 1) {
return sizeHeight1;
} else if (d.data.id.slice(0, 3) == "cnd" && d.height == 1) {
return sizeHeight1;
} else if (d.data.id.slice(0, 2) == "cp" && d.height == 1) {
return sizeHeight1;
} else if (d.data.id.slice(0, 4) == "thag" && d.height == 2) {
return sizeHeight1 - 2;
} else if (d.data.id.slice(0, 2) == "ja" && d.height == 2) {
return sizeHeight1 - 1;
} else if (d.depth == 1) {
return sizeDepth2;
} else if (d.height == 1) {
return sizeHeight0;
} else if (d.depth == 2) {
return sizeHeight1;
} else if (d.depth == 0) {
return sizeDepth1;
} else return size;
};

// let bulletSize = (d) => {
// if (d.depth == 0) {
// return bulletDepth0;
// } else if (d.depth == 1) {
// return bulletDepth1;
// } else if (d.height == 0) {
// return bulletHeight0;
// } else if (d.height == 1 && !d.data.acro.includes("AN")) {
// return bulletHeight1;
// } else return bullet;
// };

oneDepth = (d, source = false) => {
let ancestors = {};
source == true
? (ancestors = d.target.ancestors()) // source <=> target for a different result
: (ancestors = d.ancestors());

const oneDepth = ancestors.find((d) => d.depth == 0);
const result = oneDepth == null ? d.data.name : oneDepth.data.name;
// for some reason it dosen't start with sutta pitaka anymore
return result;
// return oneDepth?.data.name;
};

isLeaf = (d) => {
let bool = false;
if (d.height == 0) bool = true;
if (
d.height == 1 &&
d.data.id.slice(0, 2) != "cp" &&
d.data.id.slice(0, 2) != "kp" &&
d.data.id.slice(0, 3) != "dhp" &&
d.data.id.slice(0, 3) != "cnd" &&
d.data.id.slice(0, 2) != "ne" &&
d.data.id.slice(0, 3) != "mnd" &&
d.data.id.slice(0, 2) != "ps" &&
d.data.id.slice(0, 2) != "bv" &&
d.data.id.slice(0, 2) != "pe"
)
bool = true;
// if (d.height )
return bool;
};

// fonction pour la taille des bullets

bulletSize = (d) => {
if (d.depth == 0) {
return bulletDepth1;
} else if (d.depth == 1) {
return bulletDepth1;
} else {
return bulletHeight0;
}
// } else if (d.data.id.slice(0, 2) == "ne" && d.height == 0) {
// return bulletHeight0;
// } else if (d.data.id.slice(0, 2) == "ps" && d.height == 0) {
// return bulletHeight0;
// } else if (d.data.id.slice(0, 3) == "cnd" && d.height == 0) {
// return bulletHeight0;
// } else if (d.data.id.slice(0, 2) == "cp" && d.height == 0) {
// return bulletHeight0;
// } else if (d.data.id.slice(0, 4) == "thag" && d.height == 2) {
// return sizeHeight1 - 2;
// } else if (d.data.id.slice(0, 2) == "ja" && d.height == 2) {
// return sizeHeight1 - 1;
// } else if (d.height == 0 || d.height == 1) {
// return bulletHeight0;
// } else return bulletHeight1;
};

// const color = () => col1;
color = d3.scaleOrdinal(domain);
color("1");
color("2");
color("3");
color("4");
color("5");
color("6");

//////////////////// diagrame KN ////////////////

const g2 = g
.append("g")
.attr("transform", "translate(0," + translation + ")");

// paths

g2.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 0.8)
.selectAll("path")
.data(root2.links())
.join("path")
.attr(
"d",
d3
.linkRadial()
.angle((d) => d.x / divisor + angleRad)
.radius((d) => d.y)
)
.attr("stroke", (d) => color(oneDepth(d, true))); // colorisation

// bullet

g2.append("g")
.selectAll("circle")
.data(root2.descendants())
.join("circle")
.attr(
"transform",
(d) => `
rotate(${(d.x * 180) / divisor / Math.PI - 90 + angleDeg})
translate(${d.y},0)
`
)
.attr("fill", (d) => (d.children ? "#555" : "#999"))
.attr("r", (d) => bulletSize(d))
.attr("fill", (d) => color(oneDepth(d))) // colorisation
// .attr("opacity", (d) => (isLeaf(d) ? 0.5 : 1)); // colorisation
.attr("fill", (d) =>
isLeaf(d) ? fade(color(oneDepth(d))) : color(oneDepth(d))
); // colorisation
// text

g2.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll("text")
.data(root2.descendants())
.join("text")
.attr("font-size", (d) => fontSize(d))
.style("fill", (d) => color(oneDepth(d))) // colorisation
.attr(
"transform",
(d) => `
rotate(${(d.x * 180) / divisor / Math.PI - 90 + angleDeg})
translate(${d.y},0)
rotate(${
d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor
? 180
: 0
})
`
)
.attr("dy", "0.31em")
.attr("x", (d) =>
(d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) ===
!d.children
? -6
: 6
)
.attr("text-anchor", (d) =>
(d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) ===
!d.children
? "end"
: "start"
)
.text((d) => {
if (d.depth == 0 || d.depth == 1) return d.data.name;
if (d.height != 0 && isDNMN(d)) return d.data.name;
if (d.data.acro == "") return d.data.name; // not usefull for KN
if (d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) {
return !d.children
? d.data.name + " : " + d.data.acro
: d.data.acro + " : " + d.data.name;
} else {
return !d.children
? d.data.acro + " : " + d.data.name
: d.data.name + " : " + d.data.acro;
}
})
.style("cursor", "pointer")
.on("click", function (d, i) {
let link = "https://suttacentral.net/" + i.data.id;
if (i.data.id == "suttaPitaka")
link = "https://suttacentral.net/pitaka/sutta";
window.open(link);
})
.clone(true)
.lower()
.attr("stroke", "white");

////////////////////////////////////////////////////// End of KN chart ///////////////////////////////////////////////
////////////////////////////////////////////////////// Start of License ///////////////////////////////////////////////

const fontName = "Poppins";

GFontToDataURI(
"https://fonts.googleapis.com/css2?family=Poppins&display=swap"
)
.then((cssRules) => {
let fontRules = cssRules.join("\n");
d3.select("svg")
.append("defs")
.append("style")
.attr("type", "text/css")
.text(fontRules);
console.log("Added Font");
})
.catch((reason) => console.log(reason));

if (license) {
svg
.append("svg:image")
.attr("x", xLicense - 88)
.attr("y", yLicense - 65)
// .attr("width", 88)
// .attr("height", 31)
.attr(
"xlink:href",
// "https://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg"
await FileAttachment("cc0gris@3.svg").url()
);

const text = svg
.append("text")
.attr("x", xLicense)
.attr("y", yLicense)
.style("font-weight", "bold")
.style("fill", "#828282")
.attr("font-family", fontName)
.attr("font-size", fLicense)
.text("DhammaCharts.org")
.attr("text-anchor", "end")
.attr("dy", "-1.1em")
.clone(true)
.text("SuttaCentral.net")
.attr("dy", "0.25em");
// .clone(true)
// .text("No Right reserved")
// .attr("dy", "1.6em");
}

if (licenseNoLogo) {
// svg
// .append("svg:image")
// .attr("x", xLicense - 88)
// .attr("y", yLicense - 65)
// // .attr("width", 88)
// // .attr("height", 31)
// .attr(
// "xlink:href",
// // "https://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg"
// await FileAttachment("cc0gris@3.svg").url()
// );

const text = svg
.append("text")
.attr("x", xLicense)
.attr("y", yLicense)
.style("font-weight", "bold")
.style("fill", "#828282")
.attr("font-family", fontName)
.attr("font-size", fLicense)
.text("CC0 - Public Domain")
.attr("text-anchor", "end")
.attr("dy", "-2.45em")
.clone(true)
.text("DhammaCharts.org")
.attr("dy", "-1.1em")
.clone(true)
.text("SuttaCentral.net")
.attr("dy", "0.25em");
}

////////////////////////////////////////////////////// End of License ///////////////////////////////////////////////

// zoom

if (zoompan) {
g.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);

// zooming + pan;

svg.call(
d3
.zoom()
.extent([
[0, 0],
[width, height]
])
.scaleExtent([1, 8])
.on("zoom", zoomed)
);

function dragstarted() {
d3.select(this).raise();
g.attr("cursor", "grabbing");
}

function dragged(event, d) {
d3.select(this)
.attr("cx", (d.x = event.x))
.attr("cy", (d.y = event.y));
}

function dragended() {
g.attr("cursor", "grab");
}

function zoomed({ transform }) {
g.attr("transform", transform);
}
}

return svg.attr("viewBox", autoBox).node();
}
Insert cell
FileAttachment("cc0gris@3.svg")
Insert cell
FileAttachment("cc0gris@3.svg").html().innerHTML
Insert cell
// {
// var wrapper = document.createElement("div");
// wrapper.innerHTML = logoHTML;
// return wrapper.firstChild;
// }
Insert cell
// logoHTML = fetch(
// "https://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg"
// )
// .then((response) => response.text())
// // .then((svg) => document.body.insertAdjacentHTML("afterbegin", svg))
Insert cell
Insert cell
Insert cell
Insert cell
// chart2.attr("viewBox", autoBox).node()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
domain2 = [col5, col2, col3, col4, col1, col6]
Insert cell
d3.scaleOrdinal(domain)("test")
Insert cell
domain = colors2
Insert cell
domainFaded = colors2Faded
Insert cell
Insert cell
// the old color funciton


Insert cell
function autoBox() {
document.body.appendChild(this);
const {x, y, width, height} = this.getBBox();
document.body.removeChild(this);
return [x, y, width, height];
}
Insert cell
data = {
return d3.stratify()(SuttaPitakaTree3);
}
Insert cell
data2 = {
return d3.stratify()(kn3);
}
Insert cell
// data.each((d) => console.log(d.data.name))
Insert cell
// import { SuttaPitakaTree } from "eb4984f02c934460"
import { SuttaPitakaTree3 } from "eb4984f02c934460"
Insert cell
import { kn3 } from "eb4984f02c934460"
Insert cell
width = 975
Insert cell
height = 600
Insert cell
radius = width / 2
Insert cell
tree = d3.cluster().size([2 * Math.PI, radius - 100])
Insert cell
Insert cell
d3 = require("d3@6")
Insert cell
import { colorPicker } from "@shaunlebron/color-picker"
Insert cell
colors2 = subdivs(72, 378, divs).map((x) => color2(x + colorShift, s, l))
Insert cell
colors2Faded = subdivs(72, 378, divs).map((x) => color2(x + colorShift, s, l))
Insert cell
color2 = (h, s = 50, l = 50) => "hsl(" + h + "," + s + "%," + l + "%)"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// from https://stackoverflow.com/questions/63288390/font-style-of-text-in-svg-not-propagating-to-png-on-download

/*
Only tested on a really limited set of fonts, can very well not work
This should be taken as an proof of concept rather than a solid script.
@Params : an url pointing to an embed Google Font stylesheet
@Returns : a Promise, fulfiled with all the cssRules converted to dataURI as an Array
*/
function GFontToDataURI(url) {
return fetch(url) // first fecth the embed stylesheet page
.then((resp) => resp.text()) // we only need the text of it
.then((text) => {
// now we need to parse the CSSruleSets contained
// but chrome doesn't support styleSheets in DOMParsed docs...
let s = document.createElement("style");
s.innerHTML = text;
document.head.appendChild(s);
let styleSheet = s.sheet;

// this will help us to keep track of the rules and the original urls
let FontRule = (rule) => {
let src =
rule.style.getPropertyValue("src") ||
rule.style.cssText.match(/url\(.*?\)/g)[0];
if (!src) return null;
let url = src.split("url(")[1].split(")")[0];
return {
rule: rule,
src: src,
url: url.replace(/\"/g, "")
};
};
let fontRules = [],
fontProms = [];

// iterate through all the cssRules of the embedded doc
// Edge doesn't make CSSRuleList enumerable...
for (let i = 0; i < styleSheet.cssRules.length; i++) {
let r = styleSheet.cssRules[i];
let fR = FontRule(r);
if (!fR) {
continue;
}
fontRules.push(fR);
fontProms.push(
fetch(fR.url) // fetch the actual font-file (.woff)
.then((resp) => resp.blob())
.then((blob) => {
return new Promise((resolve) => {
// we have to return it as a dataURI
// because for whatever reason,
// browser are afraid of blobURI in <img> too...
let f = new FileReader();
f.onload = (e) => resolve(f.result);
f.readAsDataURL(blob);
});
})
.then((dataURL) => {
// now that we have our dataURI version,
// we can replace the original URI with it
// and we return the full rule's cssText
return fR.rule.cssText.replace(fR.url, dataURL);
})
);
}
document.head.removeChild(s); // clean up
return Promise.all(fontProms); // wait for all this has been done
});
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more