|
@@ -58,7 +58,7 @@ export function createNotebookGenerator(
|
|
|
generate: panel => {
|
|
|
let headings: INotebookHeading[] = [];
|
|
|
let numberingDict: { [level: number]: number } = {};
|
|
|
- let currentCollapseLevel = -1;
|
|
|
+ let collapseLevel = -1;
|
|
|
// Keep track of the previous heading, so it can be
|
|
|
// marked as having a child if one is discovered
|
|
|
let prevHeading: INotebookHeading | null = null;
|
|
@@ -98,7 +98,7 @@ export function createNotebookGenerator(
|
|
|
headings,
|
|
|
renderedHeading,
|
|
|
prevHeading,
|
|
|
- currentCollapseLevel,
|
|
|
+ collapseLevel,
|
|
|
options.filtered
|
|
|
);
|
|
|
}
|
|
@@ -110,7 +110,7 @@ export function createNotebookGenerator(
|
|
|
if (!htmlData.length) {
|
|
|
continue;
|
|
|
}
|
|
|
- // Generate the heading
|
|
|
+ // If MD/HTML generate the heading and add to headings if applicable
|
|
|
const outputWidget = (cell as CodeCell).outputArea.widgets[j];
|
|
|
const onClickFactory = (el: Element) => {
|
|
|
return () => {
|
|
@@ -130,35 +130,21 @@ export function createNotebookGenerator(
|
|
|
numbering,
|
|
|
cell
|
|
|
);
|
|
|
- // If the heading is MD and it's appropriate, add to headings
|
|
|
- if (
|
|
|
- renderedHeading &&
|
|
|
- renderedHeading.type === 'markdown' &&
|
|
|
- options.showMarkdown
|
|
|
- ) {
|
|
|
- [headings, prevHeading] = Private.addMDOrCode(
|
|
|
- headings,
|
|
|
- renderedHeading,
|
|
|
- prevHeading,
|
|
|
- currentCollapseLevel,
|
|
|
- options.filtered
|
|
|
- );
|
|
|
- } else if (renderedHeading && renderedHeading.type === 'header') {
|
|
|
- [headings, prevHeading, currentCollapseLevel] = Private.addHeader(
|
|
|
- headings,
|
|
|
- renderedHeading,
|
|
|
- prevHeading,
|
|
|
- currentCollapseLevel,
|
|
|
- options.filtered,
|
|
|
- collapsed
|
|
|
- );
|
|
|
- }
|
|
|
+ [headings, prevHeading, collapseLevel] = Private.processMD(
|
|
|
+ renderedHeading,
|
|
|
+ options.showMarkdown,
|
|
|
+ headings,
|
|
|
+ prevHeading,
|
|
|
+ collapseLevel,
|
|
|
+ options.filtered,
|
|
|
+ collapsed
|
|
|
+ );
|
|
|
}
|
|
|
} else if (model.type === 'markdown') {
|
|
|
- // If the cell is rendered, generate the ToC items from
|
|
|
- // the HTML. If it is not rendered, generate them from
|
|
|
- // the text of the cell.
|
|
|
let mdCell = cell as MarkdownCell;
|
|
|
+ let renderedHeading: INotebookHeading | undefined = undefined;
|
|
|
+ let lastLevel = Private.getLastLevel(headings);
|
|
|
+ // If the cell is rendered, generate the ToC items from the HTML
|
|
|
if (mdCell.rendered && !mdCell.inputHidden) {
|
|
|
const onClickFactory = (el: Element) => {
|
|
|
return () => {
|
|
@@ -172,40 +158,16 @@ export function createNotebookGenerator(
|
|
|
}
|
|
|
};
|
|
|
};
|
|
|
- let numbering = options.numbering;
|
|
|
- let lastLevel = Private.getLastLevel(headings);
|
|
|
- let renderedHeading: INotebookHeading | undefined;
|
|
|
renderedHeading = Private.getRenderedHTMLHeading(
|
|
|
cell.node,
|
|
|
onClickFactory,
|
|
|
sanitizer,
|
|
|
numberingDict,
|
|
|
lastLevel,
|
|
|
- numbering,
|
|
|
+ options.numbering,
|
|
|
cell
|
|
|
);
|
|
|
- if (
|
|
|
- renderedHeading &&
|
|
|
- renderedHeading.type === 'markdown' &&
|
|
|
- options.showMarkdown
|
|
|
- ) {
|
|
|
- [headings, prevHeading] = Private.addMDOrCode(
|
|
|
- headings,
|
|
|
- renderedHeading,
|
|
|
- prevHeading,
|
|
|
- currentCollapseLevel,
|
|
|
- options.filtered
|
|
|
- );
|
|
|
- } else if (renderedHeading && renderedHeading.type === 'header') {
|
|
|
- [headings, prevHeading, currentCollapseLevel] = Private.addHeader(
|
|
|
- headings,
|
|
|
- renderedHeading,
|
|
|
- prevHeading,
|
|
|
- currentCollapseLevel,
|
|
|
- options.filtered,
|
|
|
- collapsed
|
|
|
- );
|
|
|
- }
|
|
|
+ // If not rendered, generate ToC items from the text of the cell
|
|
|
} else {
|
|
|
const onClickFactory = (line: number) => {
|
|
|
return () => {
|
|
@@ -213,96 +175,24 @@ export function createNotebookGenerator(
|
|
|
cell.node.scrollIntoView();
|
|
|
};
|
|
|
};
|
|
|
- let lastLevel = Private.getLastLevel(headings);
|
|
|
- let renderedHeading: INotebookHeading | null = null;
|
|
|
- if (cell) {
|
|
|
- renderedHeading = Private.getMarkdownHeading(
|
|
|
- model!.value.text,
|
|
|
- onClickFactory,
|
|
|
- numberingDict,
|
|
|
- lastLevel,
|
|
|
- cell
|
|
|
- );
|
|
|
- }
|
|
|
- if (
|
|
|
- renderedHeading &&
|
|
|
- renderedHeading.type === 'markdown' &&
|
|
|
- options.showMarkdown
|
|
|
- ) {
|
|
|
- [headings, prevHeading] = Private.addMDOrCode(
|
|
|
- headings,
|
|
|
- renderedHeading,
|
|
|
- prevHeading,
|
|
|
- currentCollapseLevel,
|
|
|
- options.filtered
|
|
|
- );
|
|
|
- } else if (renderedHeading && renderedHeading.type === 'header') {
|
|
|
- // Determine whether the heading has children
|
|
|
- if (
|
|
|
- prevHeading &&
|
|
|
- prevHeading.type === 'header' &&
|
|
|
- prevHeading.level < renderedHeading.level &&
|
|
|
- !Private.headingIsFilteredOut(renderedHeading, options.filtered)
|
|
|
- ) {
|
|
|
- for (let j = headings.length - 1; j >= 0; j--) {
|
|
|
- if (headings[j] === prevHeading) {
|
|
|
- headings[j].hasChild = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- // Do not put the item in TOC if its header is collapsed
|
|
|
- // or filtered out by tags
|
|
|
- if (
|
|
|
- (currentCollapseLevel >= renderedHeading.level ||
|
|
|
- currentCollapseLevel < 0) &&
|
|
|
- !Private.headingIsFilteredOut(renderedHeading, options.filtered)
|
|
|
- ) {
|
|
|
- headings.push(renderedHeading);
|
|
|
- if (collapsed) {
|
|
|
- currentCollapseLevel = renderedHeading.level;
|
|
|
- } else {
|
|
|
- currentCollapseLevel = -1;
|
|
|
- }
|
|
|
- } else if (
|
|
|
- Private.headingIsFilteredOut(
|
|
|
- renderedHeading,
|
|
|
- options.filtered
|
|
|
- ) &&
|
|
|
- prevHeading &&
|
|
|
- renderedHeading.level <= prevHeading.level
|
|
|
- ) {
|
|
|
- let k = headings.length - 1;
|
|
|
- let parentHeading = false;
|
|
|
- while (k >= 0 && parentHeading === false) {
|
|
|
- if (headings[k].level < renderedHeading.level) {
|
|
|
- prevHeading = headings[k];
|
|
|
- parentHeading = true;
|
|
|
- }
|
|
|
- k--;
|
|
|
- }
|
|
|
- if (!parentHeading) {
|
|
|
- prevHeading = null;
|
|
|
- currentCollapseLevel = -1;
|
|
|
- } else {
|
|
|
- let parentCollapsed = headings[
|
|
|
- k + 1
|
|
|
- ].cellRef.model.metadata.get('toc-hr-collapsed') as boolean;
|
|
|
- parentCollapsed =
|
|
|
- parentCollapsed !== undefined ? parentCollapsed : false;
|
|
|
- if (parentCollapsed) {
|
|
|
- currentCollapseLevel = headings[k + 1].level;
|
|
|
- } else {
|
|
|
- currentCollapseLevel = -1;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (
|
|
|
- !Private.headingIsFilteredOut(renderedHeading, options.filtered)
|
|
|
- ) {
|
|
|
- prevHeading = renderedHeading;
|
|
|
- }
|
|
|
- }
|
|
|
+ renderedHeading = Private.getMarkdownHeading(
|
|
|
+ model!.value.text,
|
|
|
+ onClickFactory,
|
|
|
+ numberingDict,
|
|
|
+ lastLevel,
|
|
|
+ cell
|
|
|
+ );
|
|
|
}
|
|
|
+ // Add to headings if applicable
|
|
|
+ [headings, prevHeading, collapseLevel] = Private.processMD(
|
|
|
+ renderedHeading,
|
|
|
+ options.showMarkdown,
|
|
|
+ headings,
|
|
|
+ prevHeading,
|
|
|
+ collapseLevel,
|
|
|
+ options.filtered,
|
|
|
+ collapsed
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
return headings;
|
|
@@ -310,13 +200,9 @@ export function createNotebookGenerator(
|
|
|
};
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * A private namespace for miscellaneous things.
|
|
|
- */
|
|
|
namespace Private {
|
|
|
/**
|
|
|
- * Given a heading and the tags user selected,
|
|
|
- * determine whether the heading is filtered out by these tags.
|
|
|
+ * Determine whether a heading is filtered out by selected tags.
|
|
|
*/
|
|
|
export function headingIsFilteredOut(
|
|
|
heading: INotebookHeading,
|
|
@@ -337,9 +223,7 @@ namespace Private {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- return true;
|
|
|
}
|
|
|
- return true;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
@@ -357,11 +241,47 @@ namespace Private {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ export function processMD(
|
|
|
+ renderedHeading: INotebookHeading | undefined,
|
|
|
+ showMarkdown: boolean,
|
|
|
+ headings: INotebookHeading[],
|
|
|
+ prevHeading: INotebookHeading | null,
|
|
|
+ collapseLevel: number,
|
|
|
+ filtered: string[],
|
|
|
+ collapsed: boolean
|
|
|
+ ): [INotebookHeading[], INotebookHeading | null, number] {
|
|
|
+ // If the heading is MD and MD is shown, add to headings
|
|
|
+ if (
|
|
|
+ renderedHeading &&
|
|
|
+ renderedHeading.type === 'markdown' &&
|
|
|
+ showMarkdown
|
|
|
+ ) {
|
|
|
+ [headings, prevHeading] = Private.addMDOrCode(
|
|
|
+ headings,
|
|
|
+ renderedHeading,
|
|
|
+ prevHeading,
|
|
|
+ collapseLevel,
|
|
|
+ filtered
|
|
|
+ );
|
|
|
+ // Otherwise, if the heading is a header, add to headings
|
|
|
+ } else if (renderedHeading && renderedHeading.type === 'header') {
|
|
|
+ [headings, prevHeading, collapseLevel] = Private.addHeader(
|
|
|
+ headings,
|
|
|
+ renderedHeading,
|
|
|
+ prevHeading,
|
|
|
+ collapseLevel,
|
|
|
+ filtered,
|
|
|
+ collapsed
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return [headings, prevHeading, collapseLevel];
|
|
|
+ }
|
|
|
+
|
|
|
export function addMDOrCode(
|
|
|
headings: INotebookHeading[],
|
|
|
renderedHeading: INotebookHeading,
|
|
|
prevHeading: INotebookHeading | null,
|
|
|
- currentCollapseLevel: number,
|
|
|
+ collapseLevel: number,
|
|
|
filtered: string[]
|
|
|
): [INotebookHeading[], INotebookHeading | null] {
|
|
|
if (
|
|
@@ -377,7 +297,7 @@ namespace Private {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- if (currentCollapseLevel < 0) {
|
|
|
+ if (collapseLevel < 0) {
|
|
|
headings.push(renderedHeading);
|
|
|
}
|
|
|
prevHeading = renderedHeading;
|
|
@@ -389,39 +309,34 @@ namespace Private {
|
|
|
headings: INotebookHeading[],
|
|
|
renderedHeading: INotebookHeading,
|
|
|
prevHeading: INotebookHeading | null,
|
|
|
- currentCollapseLevel: number,
|
|
|
+ collapseLevel: number,
|
|
|
filtered: string[],
|
|
|
collapsed: boolean
|
|
|
): [INotebookHeading[], INotebookHeading | null, number] {
|
|
|
- let filter = Private.headingIsFilteredOut(renderedHeading, filtered);
|
|
|
- if (
|
|
|
- prevHeading &&
|
|
|
- prevHeading.type === 'header' &&
|
|
|
- prevHeading.level < renderedHeading.level &&
|
|
|
- !filter
|
|
|
- ) {
|
|
|
- for (let j = headings.length - 1; j >= 0; j--) {
|
|
|
- if (headings[j] === prevHeading) {
|
|
|
- headings[j].hasChild = true;
|
|
|
+ if (!Private.headingIsFilteredOut(renderedHeading, filtered)) {
|
|
|
+ // if the previous heading is a header of a higher level,
|
|
|
+ // find it and mark it as having a child
|
|
|
+ if (
|
|
|
+ prevHeading &&
|
|
|
+ prevHeading.type === 'header' &&
|
|
|
+ prevHeading.level < renderedHeading.level
|
|
|
+ ) {
|
|
|
+ for (let j = headings.length - 1; j >= 0; j--) {
|
|
|
+ if (headings[j] === prevHeading) {
|
|
|
+ headings[j].hasChild = true;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- if (
|
|
|
- (currentCollapseLevel >= renderedHeading.level ||
|
|
|
- currentCollapseLevel < 0) &&
|
|
|
- !filter
|
|
|
- ) {
|
|
|
- headings.push(renderedHeading);
|
|
|
- if (collapsed) {
|
|
|
- currentCollapseLevel = renderedHeading.level;
|
|
|
- } else {
|
|
|
- currentCollapseLevel = -1;
|
|
|
+ // if the collapse level doesn't include the header, or if there is no
|
|
|
+ // collapsing, add to headings and adjust the collapse level appropriately
|
|
|
+ if (collapseLevel >= renderedHeading.level || collapseLevel < 0) {
|
|
|
+ headings.push(renderedHeading);
|
|
|
+ collapseLevel = collapsed ? renderedHeading.level : -1;
|
|
|
}
|
|
|
- } else if (
|
|
|
- filter &&
|
|
|
- prevHeading &&
|
|
|
- renderedHeading.level <= prevHeading.level
|
|
|
- ) {
|
|
|
+ prevHeading = renderedHeading;
|
|
|
+ } else if (prevHeading && renderedHeading.level <= prevHeading.level) {
|
|
|
+ // If header is filtered out and has a previous heading of smaller level, go
|
|
|
+ // back through headings to determine if it has a parent
|
|
|
let k = headings.length - 1;
|
|
|
let parentHeading = false;
|
|
|
while (k >= 0 && parentHeading === false) {
|
|
@@ -431,26 +346,20 @@ namespace Private {
|
|
|
}
|
|
|
k--;
|
|
|
}
|
|
|
+ // If there is no parent, set prevHeading to null and reset collapsing
|
|
|
if (!parentHeading) {
|
|
|
prevHeading = null;
|
|
|
- currentCollapseLevel = -1;
|
|
|
+ collapseLevel = -1;
|
|
|
+ // Otherwise, reset collapsing appropriately
|
|
|
} else {
|
|
|
- let parentCollapsed = headings[k + 1].cellRef.model.metadata.get(
|
|
|
+ let parentState = headings[k + 1].cellRef.model.metadata.get(
|
|
|
'toc-hr-collapsed'
|
|
|
) as boolean;
|
|
|
- parentCollapsed =
|
|
|
- parentCollapsed !== undefined ? parentCollapsed : false;
|
|
|
- if (parentCollapsed) {
|
|
|
- currentCollapseLevel = headings[k + 1].level;
|
|
|
- } else {
|
|
|
- currentCollapseLevel = -1;
|
|
|
- }
|
|
|
+ parentState = parentState !== undefined ? parentState : false;
|
|
|
+ collapseLevel = parentState ? headings[k + 1].level : -1;
|
|
|
}
|
|
|
}
|
|
|
- if (!filter) {
|
|
|
- prevHeading = renderedHeading;
|
|
|
- }
|
|
|
- return [headings, prevHeading, currentCollapseLevel];
|
|
|
+ return [headings, prevHeading, collapseLevel];
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -607,7 +516,7 @@ namespace Private {
|
|
|
);
|
|
|
}
|
|
|
let html = sanitizer.sanitize(heading.innerHTML, sanitizerOptions);
|
|
|
- html = html.replace('¶', ''); // Remove the anchor symbol.
|
|
|
+ html = html.replace('¶', '');
|
|
|
const onClick = onClickFactory(heading);
|
|
|
let numbering = generateNumbering(numberingDict, level);
|
|
|
let numDOM = '';
|