slide = {
function slide() {
const container = document.createElement("div");
container.className = "slide";
container.appendChild(md.apply(this, arguments));
return container;
}
function iframe(strings) {
const container = document.createElement("div");
container.innerHTML =
"<iframe style='position:absolute;display:block; top:0px; left:-8px; bottom:0px; right:0px; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;' frameBorder='0' src='" +
strings +
"'></iframe>";
container.className = "slide";
return container;
}
slide.iframe = iframe;
slide.html = (strings) => {
const container = document.createElement("div");
container.innerHTML = strings;
container.className = "slide slide--html";
return container;
};
function htmldark(strings) {
const container = document.createElement("div");
container.innerHTML = strings;
container.className = "slide slide--html dark";
return container;
}
slide.htmldark = htmldark;
function code(strings) {
const container = document.createElement("div");
const pre = container.appendChild(document.createElement("pre"));
const code = pre.appendChild(document.createElement("code"));
let string = strings[0] + "",
i = 0,
n = arguments.length;
while (++i < n) string += arguments[i] + "" + strings[i];
code.textContent = string.trim();
container.className = "slide slide--code";
return container;
}
slide.code = code;
slide.img = async function (hrefOrImg, opts = { scale: "contain" }) {
let img;
if (typeof hrefOrImg == "string") {
img = new Image();
img.src = hrefOrImg.trim();
} else if (hrefOrImg instanceof FileAttachment) {
img = await hrefOrImg.image();
}
img.className = `slide slide--img slide--img--${opts.scale}`;
return img;
};
slide.customAnimation = (
fn,
{
numBuilds = 1,
animationDuration = 1000,
className = "",
init = () => ({})
} = {}
) => {
const container = document.createElement("div");
container.className = "slide " + className;
let currentIdx = -1;
let t = 0;
let maxIds = numBuilds - 1;
let prevTimestamp = null;
const state = init(container);
const animate = (timestamp) => {
if (prevTimestamp == null) {
t = 0;
} else {
const delta = timestamp - prevTimestamp;
t += delta / animationDuration;
}
t = Math.min(t, 1);
prevTimestamp = timestamp;
fn(state, t, currentIdx);
if (t < 1) requestAnimationFrame(animate);
};
const forward = () => {
currentIdx++;
t = 0;
prevTimestamp = null;
requestAnimationFrame(animate);
};
container.canGoForward = () => currentIdx < maxIds;
container.forward = forward;
container.resetBuild = () => {
currentIdx = -1;
forward();
};
forward();
return animationPlayer(container);
};
slide.withBuild = (fn) => {
const container = document.createElement("div");
container.className = "slide";
let currentIdx = 0;
let maxIds = 0;
const conditional = (indexOpts, trueContent, falseContent) => {
let appearIndex =
typeof indexOpts == "number" ? indexOpts : indexOpts.appear;
let hideIndex = indexOpts.hide;
maxIds = Math.max(appearIndex, hideIndex || 0, maxIds);
if (appearIndex <= currentIdx && (!hideIndex || currentIdx < hideIndex)) {
return trueContent;
} else {
return falseContent;
}
};
const animator =
(contentRenderer) =>
(indexOpts, ...args) => {
if (indexOpts.raw) {
return contentRenderer(indexOpts, ...args);
}
let appearIndex =
typeof indexOpts == "number" ? indexOpts : indexOpts.appear;
let hideIndex = indexOpts.hide;
let { transitionIn, transitionOut } = indexOpts;
maxIds = Math.max(appearIndex, hideIndex || 0, maxIds);
return (...args) => {
if (
currentIdx >= appearIndex &&
(!hideIndex ||
(transitionOut
? currentIdx <= hideIndex
: currentIdx < hideIndex))
) {
let result = contentRenderer(...args);
if (currentIdx == appearIndex) result.classList.add(transitionIn);
else result.classList.remove(transitionIn);
if (currentIdx == hideIndex) result.classList.add(transitionOut);
else result.classList.remove(transitionOut);
return result;
} else {
return html`<!-- -->`;
}
};
};
const highlight = (options, content) =>
conditional(
typeof options == "number"
? { appear: options, hide: options + 1 }
: options,
htl.html`<span class=${options.class || "highlight"}>${content}</span>`,
content
);
const build = {
md: animator(md),
html: animator(html),
svg: animator(svg),
conditional,
highlight,
animateChildren: (child) => {
autoAnimate(child);
return child;
}
};
let contents = fn(build);
container.appendChild(contents);
const forward = () => {
currentIdx++;
let newContents = fn(build);
let p = diff.diff(contents, newContents);
//container.replaceChild(newContents, contents);
//contents = newContents;
diff.apply(contents, p);
};
container.canGoForward = () => currentIdx < maxIds;
container.forward = forward;
container.resetBuild = () => {
currentIdx = 0;
let newContents = fn(build);
container.replaceChild(newContents, contents);
contents = newContents;
};
return animationPlayer(container);
};
slide.plotteus = (steps) => {
return slide.customAnimation(
(chart, t, currentIdx) => {
chart.render(steps[currentIdx].key, d3.easeCubicInOut(t));
},
{
numBuilds: steps.length,
className: "slide--plotteus",
init: (container) => {
container.style.height = "400px";
return plotteus.makeStory(container, steps);
}
}
);
};
slide.mdWithBuildLists = (strings) => {
return slide.withBuild((build) => {
const result = md(strings);
let idx = 1;
for (let el of Array.from(result.getElementsByTagName("LI"))) {
el.id = DOM.uid();
el.parentElement.replaceChild(
build.html(idx++)`${el.cloneNode(true)}`,
el
);
}
for (let el of Array.from(result.getElementsByTagName("UL"))) {
autoAnimate(el);
}
for (let el of Array.from(result.getElementsByTagName("OL"))) {
autoAnimate(el);
}
return clean(result);
});
};
slide.plot = function (plotOpts) {
const pl = Plot.plot({
style: isFullscreen
? { background: "rgb(247, 247, 249)", fontSize: 20 }
: {},
width,
...(isFullscreen ? { height: window.outerHeight * 0.6 } : {}),
...plotOpts
});
const container = document.createElement("div");
const wrapper = document.createElement("div");
wrapper.appendChild(pl);
container.appendChild(wrapper);
container.className = "slide slide--plot";
return container;
};
return slide;
}