function notebook(cells, imports = {}) {
if (typeof cells === 'string') {
let cell_asts = parser.parseModule(cells).cells;
return notebook(cell_asts, imports);
}
if (cells.raw) {
let string_parts = cells;
if (string_parts.length > 1) {
throw new Error(`Don't use interpolations in your template, use the imports object instead`);
}
let source = string_parts[0];
let cell_asts = parser.parseModule(source).cells;
return notebook(cell_asts, imports);
}
if (!Array.isArray(cells)) {
let imports = cells;
return (string_parts, interpolations) => {
return notebook(string_parts, imports);
};
}
let definitions = cells.map(cell => cell_to_definition(cell));
return (name, replacements = {}, visual = true) => {
if (visual === false || replacements === false) {
if (typeof name !== 'string') {
visual = replacements;
replacements = name;
name = null;
}
return execute_notebook_direct(definitions, imports, replacements);
}
let parsed_cell_name = null;
try {
parsed_cell_name = parse_stack();
} catch (error) {}
if (typeof name === 'object' || name == null) {
replacements = name || {};
try {
name = parse_stack() || null;
} catch (err) {
name = null;
}
}
const runtime = new ObservableRuntime();
const main = runtime.module();
for (let [key, value] of Object.entries(imports)) {
main.define(key, [], () => value);
}
let inputs = [];
for (let [key, value] of Object.entries(replacements)) {
let output_container = document.createElement('div');
main
.variable(new Inspector(output_container))
.define(key, [], () => value);
inputs.push(dedent(html)`
<div class="cell">
<div class="line">${output_container}</div>
</div>
`);
}
let outputs = [];
for (let cell of definitions) {
if (replacements[cell.name]) {
continue;
}
let output_container = document.createElement('div');
let variable = main.variable(new Inspector(output_container));
let clear_exports = activate_cells_on_module({
module: main,
variable: variable,
cell: cell
});
if (cell.code) {
let textarea = CodeEditor({
cell: cell,
onChange: new_cell => {
clear_exports();
clear_exports = activate_cells_on_module({
module: main,
variable: variable,
cell: new_cell
});
cell_fragment.classList.toggle(
"changed",
!ast_equal(new_cell.ast, cell.ast)
);
}
});
let cell_fragment = dedent(html)`
<div class="cell collapsed">
<div
class="toggle"
onClick=${event => {
event.target.closest('.cell').classList.toggle("collapsed");
}}
></div>
<div class="line">${output_container}</div>
<code class="line">${textarea}</code>
</div>
`;
outputs.push(cell_fragment);
}
}
let START_EXPANDED = true;
let notebook_element = dedent(html)`
<div class="sub-notebook ${START_EXPANDED ? "" : "collapsed-notebook"}">
${
inputs.length > 0
? html.fragment`
${inputs}
<div class="delimiter"></div>
${outputs}
`
: html.fragment`
${outputs}
`
}
</div>
`;
let names = definitions
.filter(x => x.name)
.flatMap(x => [x.name, ...(x.exports || []).map(x => x.name)]);
let inspecting_element = document.createElement('div');
let small_inspect_variable = main
.variable(new Inspector(inspecting_element))
.define(names, (...args) => Object.fromEntries(_.zip(names, args)));
let downsized_caret = dedent(html)`
<svg width="8" height="8" class="observablehq--caret">
<path d="M4 7L0 1h8z" fill="currentColor"></path>
</svg>
`;
let inspecting_element_container = dedent(html)`
<div style="pointer-events: none">
${inspecting_element}
</div>
`;
let container = dedent(html)`
<div>
${
name
? dedent(html)`
<div
class="small-inspect"
onClick=${event => {
if (
event.currentTarget.contains(inspecting_element_container)
) {
inspecting_element_container.replaceWith(downsized_caret);
notebook_element.classList.toggle(
"collapsed-notebook",
false
);
} else {
downsized_caret.replaceWith(inspecting_element_container);
notebook_element.classList.toggle(
"collapsed-notebook",
true
);
}
}}
>
<span class="observablehq--cellname observablehq--inspect">${name} = </span>
${
START_EXPANDED
? downsized_caret
: inspecting_element_container
}
</div>
`
: html`<div class="notebook-spacer"></div>`
}
${notebook_element}
<div class="notebook-spacer"></div>
</div>
`;
let variable = main.variable({
fulfilled: result => {
container.value = result;
container.dispatchEvent(new CustomEvent('input'));
},
rejected: error => {
console.log('Error:', error);
container.value = error;
container.dispatchEvent(new CustomEvent('input'));
}
});
variable.define(names, (...args) => Object.fromEntries(_.zip(names, args)));
return container;
};
}