diff --git a/hyphens/index-preview.html b/hyphens/index-preview.html new file mode 100644 index 0000000000000000000000000000000000000000..76e1a505c5b7a4a60b8933416011a62c1ad8a5b5 --- /dev/null +++ b/hyphens/index-preview.html @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + +
+
+
Running chapter
+
Running part
+

introduction of Peru’s CCT program, Juntos, a World Bank–supported program intended to tackle the acute impacts of poverty.

+

CCTs originated in Latin America, and today they are among the most evaluated social programs on the planet. The majority of evidence on CCT impacts comes from the Mexican program now called Progresa, which is one of the earliest, and now largest, CCTs. Regular evaluations were built into the program administration at the outset, and this set a significant precedent. Today, we have a robust body of evidence that policy makers draw on to maintain and expand existing programs and to support implementation of new initiatives. Most of the available evidence derives from quantitative research, especially experimental methods such as randomized control trials and quasi-experimental methods (e.g., regression discontinuity, propensity score matching, instrumental variable, and difference-in- differences; Lagarde et al. 2007; Leroy et al. 2009; Kabeer and Waddington 2015). This literature is largely concerned with measuring primary program objectives related to household consumption and the uptake of health and education services. While acknowledging some variation related to program design, the existing quantitative evidence tells us that CCTs are, overall, effective and efficient mechanisms for altering the health- and education-seeking behavior of poor households.

+

For instance, regarding health and nutrition, we know that CCTs are effectiveat increasing utilization of health services (Gertler 2000; Attanasio et al. 2005; Levy and Ohls 2007; Galasso 2011) and increasing household food consumption (Hoddinott and Skoufias 2004; Angelucci and Attanasio 2009; Resende and Oliveira 2008; Handa et al. 2009). Where CCT programs have been implemented with the goal of reducing maternal mortality, they have effectively increased pregnant women’s use of health services, including antenatal care and in-facility births (Lim et al. 2010; Glassman et al. 2013). CCTs have been linked to a reduction in neonatal, infant, and child mortality and, in particular, deaths attributable to poverty-related causes such as malnutrition and diarrhea (Barham 2011; Rasella et al. 2013). CCTs have been shown to produce better growth outcomes in children (i.e., reduction in stunting; Gertler 2004; Fernald et al. 2010; Andersen et al. 2015; Kandpal et al. 2016) and improvement in children’s motor skills and cognitive development (Fernald et al. 2008). Both outcomes are likely related to uptake of health services and increased household consumption. CCTs have also been successfully deployed to increase vaccination rates for such diseases as tuberculosis, measles, diphtheria, pertussis, tetanus, and polio (Morris et al. 2004; Barham 2005; Barham and Maluccio 2009).

+

Regarding the aim of building human capital through education, studies show that CCTs are effective at increasing school enrollment (Schultz 2004; Sadoulet et al. 2004; Behrman et al. 2005; Cardoso and Souza 2003; Dammert 2009; Attanasio et al. 2010). As is the case with health service usage, there is some variability related to gender, age, ethnicity, and location, but overall the evidence indicates a positive

+
+
+
+
1
+

Of the Bookshelf
Long title might look like

+
Of the Bookshelf
+
A few of my own
+
+

At the most general level, comparison is not a special method, or in any way unique to anthropology. Comparison is implicit in any method of deriving understanding through explanation.

+
+ + diff --git a/hyphens/index.html b/hyphens/index.html index 2fd028f2ea1bdd399512e454ee0071cb3fa6d184..600ed3878040dc86ad3bcca3d3dcf01355a1194c 100644 --- a/hyphens/index.html +++ b/hyphens/index.html @@ -8,41 +8,35 @@ + - + -
Running chapter
@@ -61,12 +55,5 @@

At the most general level, comparison is not a special method, or in any way unique to anthropology. Comparison is implicit in any method of deriving understanding through explanation.

- - \ No newline at end of file + diff --git a/hyphens/js/paged.js b/hyphens/js/paged.js index 01d805404c8867a7e6026ad24104952e481808bc..defc88e7965a8312a9193db9d6b44a55e975831c 100644 --- a/hyphens/js/paged.js +++ b/hyphens/js/paged.js @@ -1,4 +1,3 @@ -(function(l, i, v, e) { v = l.createElement(i); v.async = 1; v.src = '//' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; e = l.getElementsByTagName(i)[0]; e.parentNode.insertBefore(v, e)})(document, 'script'); (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : @@ -45,6 +44,14 @@ } } + function isElement(node) { + return node && node.nodeType === 1; + } + + function isText(node) { + return node && node.nodeType === 3; + } + function *walk(start, limiter) { let node = start; @@ -112,19 +119,6 @@ return after; } - function stackChildren(currentNode, stacked) { - let stack = stacked || []; - - stack.unshift(currentNode); - - let children = currentNode.children; - for (var i = 0, length = children.length; i < length; i++) { - stackChildren(children[i], stack); - } - - return stack; - } - function rebuildAncestors(node) { let parent, ancestor; let ancestors = []; @@ -174,6 +168,55 @@ return fragment; } + /* + export function split(bound, cutElement, breakAfter) { + let needsRemoval = []; + let index = indexOf(cutElement); + + if (!breakAfter && index === 0) { + return; + } + + if (breakAfter && index === (cutElement.parentNode.children.length - 1)) { + return; + } + + // Create a fragment with rebuilt ancestors + let fragment = rebuildAncestors(cutElement); + + // Clone cut + if (!breakAfter) { + let clone = cutElement.cloneNode(true); + let ref = cutElement.parentNode.getAttribute('data-ref'); + let parent = fragment.querySelector("[data-ref='" + ref + "']"); + parent.appendChild(clone); + needsRemoval.push(cutElement); + } + + // Remove all after cut + let next = nodeAfter(cutElement, bound); + while (next) { + let clone = next.cloneNode(true); + let ref = next.parentNode.getAttribute('data-ref'); + let parent = fragment.querySelector("[data-ref='" + ref + "']"); + parent.appendChild(clone); + needsRemoval.push(next); + next = nodeAfter(next, bound); + } + + // Remove originals + needsRemoval.forEach((node) => { + if (node) { + node.remove(); + } + }); + + // Insert after bounds + bound.parentNode.insertBefore(fragment, bound.nextSibling); + return [bound, bound.nextSibling]; + } + */ + function needsBreakBefore(node) { if( typeof node !== "undefined" && typeof node.dataset !== "undefined" && @@ -219,6 +262,195 @@ return false; } + function *words(node) { + let currentText = node.nodeValue; + let max = currentText.length; + let currentOffset = 0; + let currentLetter; + + let range; + + while(currentOffset < max) { + currentLetter = currentText[currentOffset]; + if (/^\S$/.test(currentLetter)) { + if (!range) { + range = document.createRange(); + range.setStart(node, currentOffset); + } + } else { + if (range) { + range.setEnd(node, currentOffset); + yield range; + range = undefined; + } + } + + currentOffset += 1; + } + + if (range) { + range.setEnd(node, currentOffset); + yield range; + range = undefined; + } + } + + function *letters(wordRange) { + let currentText = wordRange.startContainer; + let max = currentText.length; + let currentOffset = wordRange.startOffset; + let currentLetter; + + let range; + + while(currentOffset < max) { + currentLetter = currentText[currentOffset]; + range = document.createRange(); + range.setStart(currentText, currentOffset); + range.setEnd(currentText, currentOffset+1); + + yield range; + + currentOffset += 1; + } + } + + function isContainer(node) { + let container; + + if (typeof node.tagName === "undefined") { + return true; + } + + if (node.style.display === "none") { + return false; + } + + switch (node.tagName) { + // Inline + case "A": + case "ABBR": + case "ACRONYM": + case "B": + case "BDO": + case "BIG": + case "BR": + case "BUTTON": + case "CITE": + case "CODE": + case "DFN": + case "EM": + case "I": + case "IMG": + case "INPUT": + case "KBD": + case "LABEL": + case "MAP": + case "OBJECT": + case "Q": + case "SAMP": + case "SCRIPT": + case "SELECT": + case "SMALL": + case "SPAN": + case "STRONG": + case "SUB": + case "SUP": + case "TEXTAREA": + case "TIME": + case "TT": + case "VAR": + // Content + case "P": + case "H1": + case "H2": + case "H3": + case "H4": + case "H5": + case "H6": + case "FIGCAPTION": + case "BLOCKQUOTE": + case "PRE": + case "LI": + case "TR": + case "DT": + case "DD": + case "VIDEO": + case "CANVAS": + container = false; + break; + default: + container = true; + } + + return container; + } + + function cloneNode(n, deep=false) { + return n.cloneNode(deep); + } + + function findElement(node, doc) { + const ref = node.getAttribute("data-ref"); + return findRef(ref, doc); + } + + function findRef(ref, doc) { + return doc.querySelector(`[data-ref='${ref}']`); + } + + function validNode(node) { + if (isText(node)) { + return true; + } + + if (isElement(node) && node.dataset.ref) { + return true; + } + + return false; + } + + function prevValidNode(node) { + while (!validNode(node)) { + if (node.previousSibling) { + node = node.previousSibling; + } else { + node = node.parentNode; + } + + if (!node) { + break; + } + } + + return node; + } + + + function indexOf(node) { + let parent = node.parentNode; + if (!parent) { + return 0; + } + return Array.prototype.indexOf.call(parent.childNodes, node); + } + + function child(node, index) { + return node.childNodes[index]; + } + + function isVisible(node) { + if (isElement(node) && window.getComputedStyle(node).display !== "none") { + return true; + } else if (isText(node) && + node.textContent.trim().length && + window.getComputedStyle(node.parentNode).display !== "none") { + return true; + } + return false; + } + function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } @@ -317,10 +549,10 @@ return (str.contains("dwa") === true) && (str.contains("foo") === false); }; - var indexOf = String.prototype.indexOf; + var indexOf$1 = String.prototype.indexOf; var shim$2 = function (searchString/*, position*/) { - return indexOf.call(this, searchString, arguments[1]) > -1; + return indexOf$1.call(this, searchString, arguments[1]) > -1; }; var contains = isImplemented$2() @@ -588,8 +820,6 @@ } } - const _requestIdleCallback = 'requestIdleCallback' in window ? requestIdleCallback : requestAnimationFrame; - const PER_PAGE_CHECK = 4; /** @@ -598,250 +828,142 @@ */ class Layout { - constructor(element, wrapper, hooks) { + constructor(element, hooks) { this.element = element; - this.wrapper = wrapper; - let space = this.element.getBoundingClientRect(); - this.width = Math.round(space.width); + this.bounds = this.element.getBoundingClientRect(); if (hooks) { this.hooks = hooks; } else { this.hooks = {}; + this.hooks.layout = new Hook(); this.hooks.renderNode = new Hook(); this.hooks.layoutNode = new Hook(); this.hooks.overflow = new Hook(); } - } - - getStart(content, breakToken) { - let start = content; - let node = breakToken.node; - - if (node) { - start = node; - } - return start; } - isContainer(node) { - let container; - - if (typeof node.tagName === "undefined") { - return true; - } - - if (node.style.display === "none") { - return false; - } - - switch (node.tagName) { - // Inline - case "A": - case "ABBR": - case "ACRONYM": - case "B": - case "BDO": - case "BIG": - case "BR": - case "BUTTON": - case "CITE": - case "CODE": - case "DFN": - case "EM": - case "I": - case "IMG": - case "INPUT": - case "KBD": - case "LABEL": - case "MAP": - case "OBJECT": - case "Q": - case "SAMP": - case "SCRIPT": - case "SELECT": - case "SMALL": - case "SPAN": - case "STRONG": - case "SUB": - case "SUP": - case "TEXTAREA": - case "TIME": - case "TT": - case "VAR": - // Content - case "P": - case "H1": - case "H2": - case "H3": - case "H4": - case "H5": - case "H6": - case "FIGCAPTION": - case "BLOCKQUOTE": - case "PRE": - case "LI": - case "TR": - case "DT": - case "DD": - case "VIDEO": - case "CANVAS": - container = false; - break; - default: - container = true; - } - - return container; - } - - layout(space, content, styleMap, edges, breakToken) { - let start = content; - if (breakToken.node) { - start = this.getStart(content, breakToken); - } - - let walker = walk(start, content); + renderTo(wrapper, source, breakToken, bounds=this.bounds) { + let start = this.getStart(source, breakToken); + let walker = walk(start, source); let node; let done; let next; - let hasOverflow = false; + let hasContent = false; let newBreakToken; - let after; let check = 0; - let dest = document.createDocumentFragment(); - - let rendered; - let shallow = true; - - while (!done && !hasOverflow) { + while (!done && !newBreakToken) { next = walker.next(); node = next.value; done = next.done; - if (node) { - this.hooks.layoutNode.trigger(node); - - // Check if the rendered element has a breakBefore set - if (hasContent && (needsBreakBefore(node) || needsPreviousBreakAfter(node) || needsPageBreak(node))) { - // Check for overflow - hasOverflow = this.hasOverflow(); - - if (hasOverflow) { - let overflow = this.overflow(this.element); + if (!node) { + newBreakToken = this.findBreakToken(wrapper, source, bounds); + return newBreakToken; + } - if (overflow) { + /* + let exists; + if (isElement(node)) { + exists = findElement(node, wrapper); + } else { + exists = false; + } - newBreakToken = this.findBreakToken(overflow, content); + if (exists) { + console.log("found", exists); + break; + } + */ - if (newBreakToken && newBreakToken.node) { - this.removeOverflow(overflow); - } + this.hooks && this.hooks.layoutNode.trigger(node); - break; + // Check if the rendered element has a break set + if (hasContent && this.shouldBreak(node)) { + newBreakToken = this.findBreakToken(wrapper, source, bounds); - } - } else { - // Break layout with current node - newBreakToken = { - node: node, - offset: 0 - }; - break; - } + if (!newBreakToken) { + newBreakToken = this.breakAt(node); } - shallow = this.isContainer(node); + break; + } - rendered = this.render(node, this.wrapper, breakToken, shallow); + // Should the Node be a shallow or deep clone + let shallow = isContainer(node); - if (!hasContent) { - // Only register visible content - if (rendered.nodeType === 1 && window.getComputedStyle(rendered).display !== "none") { - hasContent = true; - } else if (rendered.nodeType === 3 && - rendered.textContent.trim().length && - window.getComputedStyle(rendered.parentNode).display !== "none") { - hasContent = true; - } - } - - if (!shallow) { - after = nodeAfter(node, content); - walker = walk(after, content); - } + let rendered = this.append(node, wrapper, breakToken, shallow); + // Check if layout has content yet + if (!hasContent) { + hasContent = isVisible(node); + } - } else { - check = 1000; // Force check + // Skip to the next node if a deep clone was rendered + if (!shallow) { + walker = walk(nodeAfter(node, source), source); } - // Only check every 4 elements + // Only check every few elements if (check >= PER_PAGE_CHECK) { check = 0; - hasOverflow = this.hasOverflow(); - } - - if (hasOverflow) { - let overflow = this.overflow(this.element); - - if (overflow) { - - newBreakToken = this.findBreakToken(overflow, content); - - if (newBreakToken && newBreakToken.node) { - this.removeOverflow(overflow); - } - - break; - - } else { - // Underflow - hasOverflow = false; - } + this.hooks && this.hooks.layout.trigger(wrapper, this); + newBreakToken = this.findBreakToken(wrapper, source, bounds); } check += 1; } - _requestIdleCallback(() => { - this.listened = this.listeners(); - }); - - return newBreakToken; + } + breakAt(node, offset=0) { + return { + node, + offset + } } - hasOverflow() { - let width = Math.max(Math.floor(this.wrapper.getBoundingClientRect().width), this.element.scrollWidth); - return this.width < width; + shouldBreak(node) { + return needsBreakBefore(node) || needsPreviousBreakAfter(node) || needsPageBreak(node); } - render(node, dest, breakToken, shallow=true, rebuild=true) { + getStart(source, breakToken) { + let start; + let node = breakToken && breakToken.node; - let clone = this.createDOMNode(node, !shallow); + if (node) { + start = node; + } else { + start = source.firstChild; + } - this.hooks.renderNode.trigger(clone); + return start; + } + + append(node, dest, breakToken, shallow=true, rebuild=true) { - if (node.parentNode && node.parentNode.nodeType === 1) { - let parent = dest.querySelector("[data-ref='" + node.parentNode.getAttribute("data-ref") + "']"); + let clone = cloneNode(node, !shallow); + if (node.parentNode && isElement(node.parentNode)) { + let parent = findElement(node.parentNode, dest); // Rebuild chain if (parent) { parent.appendChild(clone); } else if (rebuild) { let fragment = rebuildAncestors(node); - parent = fragment.querySelector("[data-ref='" + node.parentNode.getAttribute("data-ref") + "']"); - if (breakToken && breakToken.node.nodeType === 3 && breakToken.offset > 0) { + parent = findElement(node.parentNode, fragment); + if (!parent) { + dest.appendChild(clone); + } else if (breakToken && isText(breakToken.node) && breakToken.offset > 0) { clone.textContent = clone.textContent.substring(breakToken.offset); parent.appendChild(clone); } else { @@ -858,51 +980,76 @@ dest.appendChild(clone); } - // if (!shallow && node.childNodes) { - // for (let child of node.childNodes) { - // this.render(child, dest, breakToken, shallow, rebuild); - // } - // } + this.hooks && this.hooks.renderNode.trigger(clone); return clone; } - createDOMNode(n, deep=false) { - let node = n.cloneNode(deep); + avoidBreakInside(node, limiter) { + let breakNode; - return node; + if (node === limiter) { + return; + } + + while (node.parentNode) { + node = node.parentNode; + + if (node === limiter) { + break; + } + + if(window.getComputedStyle(node)["break-inside"] === "avoid") { + breakNode = node; + break; + } + + } + return breakNode; } - findBreakToken(overflow, content) { + createBreakToken(overflow, rendered, source) { + let container = overflow.startContainer; let offset = overflow.startOffset; - let node, renderedNode, ref, parent, index, temp, startOffset; + let node, renderedNode, parent, index, temp, startOffset; + + if (isElement(container)) { + temp = child(container, offset); - if (overflow.startContainer.nodeType === 1) { - // node = children.querySelector("[data-ref='" + overflow.startContainer.childNodes[offset].getAttribute("data-ref") + "']"); - temp = overflow.startContainer.childNodes[offset]; + if (isElement(temp)) { + renderedNode = findElement(temp, rendered); - if (temp.nodeType === 1) { - ref = temp.getAttribute("data-ref"); - // node = this.parser.find(ref); - renderedNode = this.wrapper.querySelector("[data-ref='" + ref + "']"); - node = content.querySelector("[data-ref='"+ renderedNode.getAttribute("data-ref") +"']"); + if (!renderedNode) { + // Find closest element with data-ref + renderedNode = findElement(prevValidNode(temp), rendered); + return; + } + + node = findElement(renderedNode, source); offset = 0; } else { - index = Array.prototype.indexOf.call(overflow.startContainer.childNodes, temp); - ref = overflow.startContainer.getAttribute("data-ref"); - renderedNode = this.wrapper.querySelector("[data-ref='" + ref + "']"); - parent = content.querySelector("[data-ref='"+ renderedNode.getAttribute("data-ref") +"']"); - node = parent.childNodes[index]; + renderedNode = findElement(container, rendered); + + if (!renderedNode) { + renderedNode = findElement(prevValidNode(container), rendered); + } + + parent = findElement(renderedNode, source); + index = indexOf(temp); + node = child(parent, index); offset = 0; } } else { - index = Array.prototype.indexOf.call(overflow.startContainer.parentNode.childNodes, overflow.startContainer); - // let parent = children.querySelector("[data-ref='" + overflow.startContainer.parentNode.getAttribute("data-ref") + "']"); - ref = overflow.startContainer.parentNode.getAttribute("data-ref"); - renderedNode = this.wrapper.querySelector("[data-ref='" + ref + "']"); - parent = content.querySelector("[data-ref='"+ renderedNode.getAttribute("data-ref") +"']"); - node = parent.childNodes[index]; - startOffset = overflow.startContainer.textContent.slice(offset); + renderedNode = findElement(container.parentNode, rendered); + + if (!renderedNode) { + renderedNode = findElement(prevValidNode(container.parentNode), rendered); + } + + parent = findElement(renderedNode, source); + index = indexOf(container); + node = child(parent, index); + startOffset = container.textContent.slice(offset); offset = parent.textContent.indexOf(startOffset); } @@ -917,116 +1064,42 @@ } - removeOverflow(overflow) { + findBreakToken(rendered, source, bounds=this.bounds, extract=true) { + let overflow = this.findOverflow(rendered, bounds); + let breakToken; - if (overflow.startContainer.nodeType === 3 && overflow.startOffset > 0) { - let startText = overflow.startContainer.textContent; - let startOffset = overflow.startOffset; - let prevLetter = startText[startOffset-1]; - - // Add a hyphen if previous character is a letter or soft hyphen - if (/^\w|\u00AD$/.test(prevLetter)) { - overflow.startContainer.textContent = startText.slice(0, startOffset) + "\u2010"; - overflow.setStart(overflow.startContainer, startOffset + 1); - } - } - - - // _requestIdleCallback(() => this.removeEmpty()); - return overflow.extractContents(); - } - - removeEmpty() { - // Clean Empty Nodes - let stack = stackChildren(this.wrapper); - - stack.forEach((currentNode) => { - - if (!currentNode) { - return; - } + if (overflow) { + breakToken = this.createBreakToken(overflow, rendered, source); - if (currentNode.childNodes.length === 0) { - // Check in original - let original = currentNode.getAttribute("data-children"); - if (original != 0) { - currentNode.remove(); - } - } else if (currentNode.textContent.trim().length === 0) { - let original = currentNode.getAttribute("data-text"); - if (original != 0) { - currentNode.remove(); - } + if (breakToken && breakToken.node && extract) { + this.removeOverflow(overflow); } - }); - - stack = undefined; - _requestIdleCallback(() => this.floats()); - - } - - floats(area) { - let bounds = getBoundingClientRect(this.element); - - let start = Math.round(bounds.left); - let end = Math.round(bounds.right); - - let range; - - let walker = walk(this.wrapper.firstChild, this.wrapper); - let next, done, node; - while (!done) { - next = walker.next(); - done = next.done; - node = next.value; - - if (node) { - let pos = getBoundingClientRect(node); - let left = Math.floor(pos.left); - let right = Math.floor(pos.right); - - if (left >= end) { - range = document.createRange(); - range.selectNode(node); - // let extracted = range.extractContents(); - let extracted = this.removeOverflow(range); - this._onOverflow && this._onOverflow(extracted); - } - } } - - } - - onEnter(node) { - - } - - onExit(node) { - - } - - onOverflow(func) { - this._onOverflow = func; + return breakToken; } - onUnderflow(func) { - this._onUnderflow = func; + hasOverflow(element, bounds=this.bounds) { + let constrainingElement = element.parentNode; // this gets the element, instead of the wrapper for the width workaround + let { width } = element.getBoundingClientRect(); + let { scrollWidth } = constrainingElement; + return Math.max(Math.floor(width), scrollWidth) > Math.round(bounds.width); } - overflow(area) { - let bounds = getBoundingClientRect(this.element); + findOverflow(rendered, bounds=this.bounds) { + if (!this.hasOverflow(rendered, bounds)) return; let start = Math.round(bounds.left); let end = Math.round(bounds.right); let range; - let walker = walk(this.wrapper.firstChild, this.wrapper); - let next, done, node, offset; + let walker = walk(rendered.firstChild, rendered); + let next, done, node, offset, skip; while (!done) { next = walker.next(); done = next.done; node = next.value; + skip = false; if (node) { let pos = getBoundingClientRect(node); @@ -1036,18 +1109,19 @@ if (!range && left >= end) { // Check if it is a float let isFloat = false; - if (node.nodeType === 1) { + if (isElement(node)) { let styles = window.getComputedStyle(node); isFloat = styles.getPropertyValue("float") !== "none"; + skip = window.getComputedStyle(node)["break-inside"] === "avoid"; } - if (!isFloat && node.nodeType === 1) { + if (!isFloat && isElement(node)) { range = document.createRange(); range.setStartBefore(node); break; } - if (node.nodeType === 3 && node.textContent.trim().length) { + if (isText(node) && node.textContent.trim().length) { range = document.createRange(); range.setStartBefore(node); break; @@ -1055,7 +1129,9 @@ } - if (!range && node.nodeType === 3 && right > end && node.textContent.trim().length) { + if (!range && isText(node) && + right > end && node.textContent.trim().length && + window.getComputedStyle(node.parentNode)["break-inside"] !== "avoid") { range = document.createRange(); offset = this.textBreak(node, start, end); if (!offset) { @@ -1067,263 +1143,142 @@ } // Skip children - if (right < end) { + if (skip || right < end) { - next = nodeAfter(node, this.wrapper); + next = nodeAfter(node, rendered); if (next) { - walker = walk(next, this.wrapper); + walker = walk(next, rendered); } } } } - if (range) { - range.setEndAfter(this.wrapper.lastChild); - - this.hooks.overflow.trigger(range); - - return range; - } - - } - - textBreak(node, start, end) { - let wordwalker = this.words(node); - let left = 0; - let right = 0; - let word, next, done, pos; - let offset; - while (!done) { - next = wordwalker.next(); - word = next.value; - done = next.done; - - if (!word) { - break; - } - - pos = getBoundingClientRect(word); - - left = Math.floor(pos.left); - right = Math.floor(pos.right); - - if (left >= end) { - offset = word.startOffset; - break; - } - - if (right > end) { - let letterwalker = this.letters(word); - let letter, nextLetter, doneLetter; - - while (!doneLetter) { - nextLetter = letterwalker.next(); - letter = nextLetter.value; - doneLetter = nextLetter.done; - - if (!letter) { - break; - } - - pos = getBoundingClientRect(letter); - left = Math.floor(pos.left); - - if (left >= end) { - offset = letter.startOffset; - done = true; - - break; - } - } - } - - } - - return offset; - } - - *words(node) { - let currentText = node.nodeValue; - let max = currentText.length; - let currentOffset = 0; - let currentLetter; - - let range; - - while(currentOffset < max) { - currentLetter = currentText[currentOffset]; - if (/^\S$/.test(currentLetter)) { - if (!range) { - range = document.createRange(); - range.setStart(node, currentOffset); - } - } else { - if (range) { - range.setEnd(node, currentOffset); - yield range; - range = undefined; - } - } - - currentOffset += 1; - } + // Find End if (range) { - range.setEnd(node, currentOffset); - yield range; - range = undefined; - } - } - - *letters(wordRange) { - let currentText = wordRange.startContainer; - let max = currentText.length; - let currentOffset = wordRange.startOffset; - let currentLetter; - - let range; - - while(currentOffset < max) { - currentLetter = currentText[currentOffset]; - range = document.createRange(); - range.setStart(currentText, currentOffset); - range.setEnd(currentText, currentOffset+1); - - yield range; - - currentOffset += 1; - } - } - - prepend(fragment, rebuild=true) { - if (!fragment) { - return; - } - let walker = walk(fragment.firstChild, this.wrapper); - let next, node, done; - let parent; - while (!done) { - next = walker.next(); - node = next.value; - done = next.done; - - if (!node) { - break; - } - - let exists = false; - - if (node.nodeType === 1) { - exists = this.wrapper.querySelector("[data-ref='" + node.getAttribute("data-ref") + "']"); - } - - if (exists) { - parent = exists; - } else { - if(parent) { - parent.insertBefore(node, parent.firstChild); - break; - } else { - this.wrapper.insertBefore(node, this.wrapper.firstChild); - } - } + range.setEndAfter(rendered.lastChild); + return range; } - let hasOverflow = this.hasOverflow(); + } - if (hasOverflow) { + findEndToken(rendered, source, bounds=this.bounds) { + if (rendered.childNodes.length === 0) { + return; + } - let overflow = this.overflow(this.element); + let lastChild = rendered.lastChild; - if (overflow) { - // let extracted = overflow.extractContents(); - let extracted = this.removeOverflow(overflow); - this._onOverflow && this._onOverflow(extracted); + let lastNodeIndex; + while (lastChild && lastChild.lastChild) { + if (!validNode(lastChild)) { + // Only get elements with refs + lastChild = lastChild.previousSibling; + } else if(!validNode(lastChild.lastChild)) { + // Deal with invalid dom items + lastChild = prevValidNode(lastChild.lastChild); + break; + } else { + lastChild = lastChild.lastChild; } } - if (!this.listened) { - this.listened = this.listeners(); + if (isText(lastChild)) { + + if (lastChild.parentNode.dataset.ref) { + lastNodeIndex = indexOf(lastChild); + lastChild = lastChild.parentNode; + } else { + lastChild = lastChild.previousSibling; + } } - } - getOverflow() { - let overflow = this.overflow(this.element); + let original = findElement(lastChild, source); - if (overflow) { - // let extracted = overflow.extractContents(); - let extracted = this.removeOverflow(overflow); - this._onOverflow && this._onOverflow(extracted); + if (lastNodeIndex) { + original = original.childNodes[lastNodeIndex]; } - } - getUnderflow() { - this._onUnderflow && this._onUnderflow(); - } + let after = nodeAfter(original); - listeners() { - if (typeof ResizeObserver !== "undefined") { - this.addResizeObserver(); - } else { - this.element.addEventListener("overflow", this.getOverflow.bind(this), false); - this.element.addEventListener("underflow", this.getUnderflow.bind(this), false); - } - // TODO: fall back to mutation observer? + return this.breakAt(after); + } + textBreak(node, start, end) { + let wordwalker = words(node); + let left = 0; + let right = 0; + let word, next, done, pos; + let offset; + while (!done) { + next = wordwalker.next(); + word = next.value; + done = next.done; - // Key scroll width from changing - this.element.addEventListener("scroll", () => { - this.element.scrollLeft = 0; - }); + if (!word) { + break; + } - return true; - } + pos = getBoundingClientRect(word); - addResizeObserver() { - let wrapper = this.wrapper; - let prevHeight = wrapper.getBoundingClientRect().height; - this.ro = new ResizeObserver( entries => { - for (let entry of entries) { - const cr = entry.contentRect; + left = Math.floor(pos.left); + right = Math.floor(pos.right); - if (cr.height > prevHeight) { - let hasOverflow = this.hasOverflow(); + if (left >= end) { + offset = word.startOffset; + break; + } - if (hasOverflow) { + if (right > end) { + let letterwalker = letters(word); + let letter, nextLetter, doneLetter; - let overflow = this.overflow(this.element); + while (!doneLetter) { + nextLetter = letterwalker.next(); + letter = nextLetter.value; + doneLetter = nextLetter.done; - if (overflow) { - // let extracted = overflow.extractContents(); - let extracted = this.removeOverflow(overflow); - this._onOverflow && this._onOverflow(extracted); - prevHeight = wrapper.getBoundingClientRect().height; - } - } else { - prevHeight = cr.height; + if (!letter) { + break; } - } else if (cr.height < prevHeight ) { // TODO: calc line height && (prevHeight - cr.height) >= 22 - this._onUnderflow && this._onUnderflow(); - prevHeight = cr.height; + pos = getBoundingClientRect(letter); + left = Math.floor(pos.left); + + if (left >= end) { + offset = letter.startOffset; + done = true; + + break; + } } } - }); - this.ro.observe(wrapper); - } + } - destroy() { - this.element.removeEventListener("overflow", this.getOverflow.bind(this), false); - this.element.removeEventListener("underflow", this.getUnderflow.bind(this), false); + return offset; + } - this.ro.disconnect(); + removeOverflow(overflow) { + this.hyphenateAtBreak(overflow); - this.element = element; - this.wrapper = wrapper; + return overflow.extractContents(); } + hyphenateAtBreak(overflow) { + if (isText(overflow.startContainer) && overflow.startOffset > 0) { + let startText = overflow.startContainer.textContent; + let startOffset = overflow.startOffset; + let prevLetter = startText[startOffset-1]; + + // Add a hyphen if previous character is a letter or soft hyphen + if (/^\w|\u00AD$/.test(prevLetter)) { + overflow.startContainer.textContent = startText.slice(0, startOffset) + "\u2010"; + overflow.setStart(overflow.startContainer, startOffset + 1); + } + } + } } eventEmitter(Layout.prototype); @@ -1379,15 +1334,17 @@ this.element = page; this.area = area; + return page; + } + + createWrapper() { let wrapper = document.createElement("div"); - // wrapper.setAttribute("contenteditable", true); - // wrapper.style.outline = "none"; this.area.appendChild(wrapper); this.wrapper = wrapper; - return page; + return wrapper; } index(pgnum) { @@ -1438,18 +1395,24 @@ layout(contents, breakToken) { // console.log("layout page", this.id); - let size = this.area.getBoundingClientRect(); - this.l = new Layout(this.area, this.wrapper, this.hooks); + this.clear(); - this.l.onOverflow((overflow) => { - this._onOverflow && this._onOverflow(overflow); - }); + this.layoutMethod = new Layout(this.area, this.hooks); - this.l.onUnderflow((overflow) => { - this._onUnderflow && this._onUnderflow(overflow); - }); + breakToken = this.layoutMethod.renderTo(this.wrapper, contents, breakToken); - breakToken = this.l.layout(size, contents, {}, {}, breakToken); + this.addListeners(contents); + + return breakToken; + } + + append(contents, breakToken) { + + if (!this.layoutMethod) { + return this.layout(contents, breakToken); + } + + breakToken = this.layoutMethod.renderTo(this.wrapper, contents, breakToken); return breakToken; } @@ -1473,29 +1436,104 @@ this._onUnderflow = func; } - prepend(fragment) { - if (!this.l) { - this.l = new Layout(this.area, this.wrapper, this.hooks); + clear() { + this.removeListeners(); + this.wrapper && this.wrapper.remove(); + this.createWrapper(); + } + + addListeners(contents) { + if (typeof ResizeObserver !== "undefined") { + this.addResizeObserver(contents); + } else { + this.element.addEventListener("overflow", this.checkOverflowAfterResize.bind(this, contents), false); + this.element.addEventListener("underflow", this.checkOverflowAfterResize.bind(this, contents), false); + } + // TODO: fall back to mutation observer? - this.l.onOverflow((overflow) => { - this._onOverflow && this._onOverflow(overflow); - }); - this.l.onUnderflow((overflow) => { - this._onUnderflow && this._onUnderflow(overflow); - }); + // Key scroll width from changing + this.element.addEventListener("scroll", () => { + if(this.listening) { + this.element.scrollLeft = 0; + } + }); + + this.listening = true; + + return true; + } + + removeListeners() { + this.listening = false; + // clearTimeout(this.timeoutAfterResize); + + if (this.element) { + this.element.removeEventListener("overflow", this.checkOverflowAfterResize.bind(this), false); + this.element.removeEventListener("underflow", this.checkOverflowAfterResize.bind(this), false); + } + + if (this.ro) { + this.ro.disconnect(); } + } + + addResizeObserver(contents) { + let wrapper = this.wrapper; + let prevHeight = wrapper.getBoundingClientRect().height; + this.ro = new ResizeObserver( entries => { - this.l.prepend(fragment); + if (!this.listening) { + return; + } + + for (let entry of entries) { + const cr = entry.contentRect; + + if (cr.height > prevHeight) { + this.checkOverflowAfterResize(contents); + prevHeight = wrapper.getBoundingClientRect().height; + } else if (cr.height < prevHeight ) { // TODO: calc line height && (prevHeight - cr.height) >= 22 + this.checkUnderflowAfterResize(contents); + prevHeight = cr.height; + } + } + }); + + this.ro.observe(wrapper); } - append() { + checkOverflowAfterResize(contents) { + if (!this.listening || !this.layoutMethod) { + return; + } + + let newBreakToken = this.layoutMethod.findBreakToken(this.wrapper, contents); + if (newBreakToken) { + this._onOverflow && this._onOverflow(newBreakToken); + } } + checkUnderflowAfterResize(contents) { + if (!this.listening || !this.layoutMethod) { + return; + } + + let endToken = this.layoutMethod.findEndToken(this.wrapper, contents); + + // let newBreakToken = this.layoutMethod.findBreakToken(this.wrapper, contents); + + if (endToken) { + this._onUnderflow && this._onUnderflow(endToken); + } + } destroy() { + this.removeListeners(); + this.element = undefined; + this.wrapper = undefined; } } @@ -1555,15 +1593,19 @@ let node; while(node = treeWalker.nextNode()) { - let uuid = UUID(); - node.setAttribute("data-ref", uuid); + if (!node.hasAttribute("data-ref")) { + let uuid = UUID(); + node.setAttribute("data-ref", uuid); + } + if (node.id) { node.setAttribute("data-id", node.id); } - node.setAttribute("data-children", node.childNodes.length); - node.setAttribute("data-text", node.textContent.trim().length); + // node.setAttribute("data-children", node.childNodes.length); + + // node.setAttribute("data-text", node.textContent.trim().length); } } @@ -1672,6 +1714,7 @@ // this.preview = preview; this.hooks = {}; + this.hooks.beforeParsed = new Hook(this); this.hooks.afterParsed = new Hook(this); this.hooks.beforePageLayout = new Hook(this); this.hooks.layout = new Hook(this); @@ -1706,7 +1749,13 @@ } async flow(content, renderTo) { - let parsed = new ContentParser(content); + let parsed; + + await this.hooks.beforeParsed.trigger(content, this); + + parsed = new ContentParser(content); + + this.source = parsed; this.setup(renderTo); @@ -1792,7 +1841,7 @@ if (page) { await this.hooks.beforePageLayout.trigger(page, undefined, undefined, this); this.emit("page", page); - await this.hooks.layout.trigger(page.element, page, undefined, this); + // await this.hooks.layout.trigger(page.element, page, undefined, this); await this.hooks.afterPageLayout.trigger(page.element, page, undefined, this); this.emit("renderedPage", page); } @@ -1817,7 +1866,7 @@ // Layout content in the page, starting from the breakToken breakToken = page.layout(content, breakToken); - await this.hooks.layout.trigger(page.element, page, breakToken, this); + // await this.hooks.layout.trigger(page.element, page, breakToken, this); await this.hooks.afterPageLayout.trigger(page.element, page, breakToken, this); this.emit("renderedPage", page); @@ -1843,24 +1892,26 @@ if (!blank) { // Listen for page overflow - page.onOverflow((overflow) => { - _requestIdleCallback$1(() => { - let index = this.pages.indexOf(page) + 1; - if (index < this.pages.length && - (this.pages[index].breakBefore || this.pages[index].previousBreakAfter)) { - let newPage = this.insertPage(index - 1); - newPage.prepend(overflow); - } else if (index < this.pages.length) { - this.pages[index].prepend(overflow); - } else { - let newPage = this.addPage(); - newPage.prepend(overflow); - } - }); + page.onOverflow((overflowToken) => { + // console.log("overflow on", page.id, overflowToken); + let index = this.pages.indexOf(page) + 1; + if (index < this.pages.length && + (this.pages[index].breakBefore || this.pages[index].previousBreakAfter)) { + let newPage = this.insertPage(index - 1); + newPage.layout(this.source, overflowToken); + } else if (index < this.pages.length) { + this.pages[index].layout(this.source, overflowToken); + } else { + let newPage = this.addPage(); + newPage.layout(this.source, overflowToken); + } }); - page.onUnderflow(() => { - // console.log("underflow on", page.id); + page.onUnderflow((overflowToken) => { + // console.log("underflow on", page.id, overflowToken); + + // page.append(this.source, overflowToken); + }); } @@ -1887,15 +1938,13 @@ if (!blank) { // Listen for page overflow - page.onOverflow((overflow) => { - _requestIdleCallback$1(() => { - if (total < this.pages.length) { - this.pages[total].prepend(overflow); - } else { - let newPage = this.addPage(); - newPage.prepend(overflow); - } - }); + page.onOverflow((overflowToken) => { + if (total < this.pages.length) { + this.pages[total].layout(this.source, overflowToken); + } else { + let newPage = this.addPage(); + newPage.layout(this.source, overflowToken); + } }); page.onUnderflow(() => { @@ -2452,7 +2501,7 @@ this.remove(oldItem); }; - var list = List; + var List_1 = List; var createCustomError = function createCustomError(name, message) { // use Object.create(), because some VMs prevent setting line/column otherwise @@ -3916,6 +3965,7 @@ var findIdentifierEnd$2 = utils.findIdentifierEnd; var findNumberEnd$2 = utils.findNumberEnd; var findDecimalNumberEnd$1 = utils.findDecimalNumberEnd; + var findStringEnd$2 = utils.findStringEnd; var isHex$1 = utils.isHex; var SYMBOL_TYPE$2 = _const.SYMBOL_TYPE; @@ -3923,6 +3973,8 @@ var PLUSSIGN$2 = _const.TYPE.PlusSign; var HYPHENMINUS$3 = _const.TYPE.HyphenMinus; var NUMBERSIGN = _const.TYPE.NumberSign; + var APOSTROPHE$1 = _const.TYPE.Apostrophe; + var QUOTATIONMARK$1 = _const.TYPE.QuotationMark; var PERCENTAGE = { '%': true @@ -4094,6 +4146,19 @@ return findIdentifierEnd$2(str, offset + 1); } + function ident(token, addTokenToMatch) { + if (token === null) { + return false; + } + + if (consumeIdentifier(token.value, 0) !== token.value.length) { + return false; + } + + addTokenToMatch(); + return true; + } + function astNode(type) { return function(token, addTokenToMatch) { if (token === null || token.node.type !== type) { @@ -4105,6 +4170,25 @@ }; } + function string(token, addTokenToMatch) { + if (token === null || token.value.length < 2) { + return false; + } + + var quote = token.value.charCodeAt(0); + + if (quote !== APOSTROPHE$1 && quote !== QUOTATIONMARK$1) { + return false; + } + + if (findStringEnd$2(token.value, 1, quote) !== token.value.length) { + return false; + } + + addTokenToMatch(); + return true; + } + function dimension(type) { return function(token, addTokenToMatch, getNextToken) { if (calc(token, addTokenToMatch, getNextToken)) { @@ -4308,7 +4392,7 @@ 'flex': dimension(FLEX), 'hex-color': hexColor, 'id-selector': idSelector, // element( ) - 'ident': astNode('Identifier'), + 'ident': ident, 'integer': integer, 'length': zeroUnitlessDimension(LENGTH), 'number': number, @@ -4318,7 +4402,7 @@ 'positive-integer': positiveInteger, 'resolution': dimension(RESOLUTION), 'semitones': dimension(SEMITONES), - 'string': astNode('String'), + 'string': string, 'time': dimension(TIME), 'unicode-range': astNode('UnicodeRange'), 'url': url, @@ -4409,7 +4493,7 @@ var EXCLAMATIONMARK$1 = 33; // ! var NUMBERSIGN$1 = 35; // # var AMPERSAND = 38; // & - var APOSTROPHE$1 = 39; // ' + var APOSTROPHE$2 = 39; // ' var LEFTPARENTHESIS$1 = 40; // ( var RIGHTPARENTHESIS$1 = 41; // ) var ASTERISK = 42; // * @@ -4614,11 +4698,11 @@ var name; tokenizer.eat(LESSTHANSIGN$1); - tokenizer.eat(APOSTROPHE$1); + tokenizer.eat(APOSTROPHE$2); name = scanWord(tokenizer); - tokenizer.eat(APOSTROPHE$1); + tokenizer.eat(APOSTROPHE$2); tokenizer.eat(GREATERTHANSIGN$1); return maybeMultiplied(tokenizer, { @@ -4798,7 +4882,7 @@ return maybeMultiplied(tokenizer, readGroup(tokenizer)); case LESSTHANSIGN$1: - return tokenizer.nextCharCode() === APOSTROPHE$1 + return tokenizer.nextCharCode() === APOSTROPHE$2 ? readProperty(tokenizer) : readType(tokenizer); @@ -4827,7 +4911,7 @@ type: 'Comma' }; - case APOSTROPHE$1: + case APOSTROPHE$2: return maybeMultiplied(tokenizer, { type: 'String', value: scanString(tokenizer) @@ -6104,9 +6188,9 @@ var start = getFirstMatchNode(matchNode); var end = getLastMatchNode(matchNode); - lexer.syntax.walk(ast, function(node, item, list$$1) { + lexer.syntax.walk(ast, function(node, item, list) { if (node === start) { - var nodes = new list(); + var nodes = new List_1(); do { nodes.appendData(item.data); @@ -6119,7 +6203,7 @@ } while (item !== null); fragments.push({ - parent: list$$1, + parent: list, nodes: nodes }); } @@ -6219,7 +6303,7 @@ if (typeof fieldType === 'string') { valid = node[key] && node[key].type === fieldType; } else if (Array.isArray(fieldType)) { - valid = node[key] instanceof list; + valid = node[key] instanceof List_1; } } } @@ -6775,16 +6859,16 @@ readSequence: sequence, createList: function() { - return new list(); + return new List_1(); }, createSingleNodeList: function(node) { - return new list().appendData(node); + return new List_1().appendData(node); }, - getFirstListNode: function(list$$1) { - return list$$1 && list$$1.first(); + getFirstListNode: function(list) { + return list && list.first(); }, - getLastListNode: function(list$$1) { - return list$$1.last(); + getLastListNode: function(list) { + return list.last(); }, parseWithFallback: function(consumer, fallback) { @@ -6818,10 +6902,10 @@ return null; }, - getLocationFromList: function(list$$1) { + getLocationFromList: function(list) { if (this.needPositions) { - var head = this.getFirstListNode(list$$1); - var tail = this.getLastListNode(list$$1); + var head = this.getFirstListNode(list); + var tail = this.getLastListNode(list); return this.scanner.getLocationRange( head !== null ? head.loc.start.offset - this.scanner.startOffset : this.scanner.tokenStart, tail !== null ? tail.loc.end.offset - this.scanner.startOffset : this.scanner.tokenStart, @@ -10075,8 +10159,8 @@ fromPlainObject: function(ast) { walk(ast, { enter: function(node) { - if (node.children && node.children instanceof list === false) { - node.children = new list().fromArray(node.children); + if (node.children && node.children instanceof List_1 === false) { + node.children = new List_1().fromArray(node.children); } } }); @@ -10086,7 +10170,7 @@ toPlainObject: function(ast) { walk(ast, { leave: function(node) { - if (node.children && node.children instanceof list) { + if (node.children && node.children instanceof List_1) { node.children = node.children.toArray(); } } @@ -10319,7 +10403,7 @@ var value = node[key]; if (value) { - if (Array.isArray(value) || value instanceof list) { + if (Array.isArray(value) || value instanceof List_1) { value = value.map(clone); } else if (value.constructor === Object) { value = clone(value); @@ -10442,7 +10526,7 @@ var convert = create$3(walk); var syntax = { - List: list, + List: List_1, Tokenizer: tokenizer, Lexer: Lexer_1, @@ -18403,7 +18487,7 @@ syntax: "space-between | space-around | space-evenly | stretch" }, "content-list": { - syntax: "[ | contents | | | | ]+" + syntax: "[ | contents | | | | | ]+" }, "content-position": { syntax: "center | start | end | flex-start | flex-end" @@ -19747,6 +19831,50 @@ "pseudo-page": { comment: "syntax is incorrect and can't be parsed, drop for now", syntax: null + }, + "id-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "attribute-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "wq-name": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "ns-prefix": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "class-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "compound-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "type-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "subclass-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "pseudo-class-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "pseudo-element-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "compound-selector-list": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null } }; var patch = { @@ -22220,7 +22348,7 @@ // https://drafts.csswg.org/css-images-4/#element-notation // https://developer.mozilla.org/en-US/docs/Web/CSS/element - var element$1 = function() { + var element = function() { this.scanner.skipSC(); var children = this.createSingleNodeList( @@ -22283,8 +22411,8 @@ var value = { getNode: _default, - '-moz-element': element$1, - 'element': element$1, + '-moz-element': element, + 'element': element, 'expression': expression$1, 'var': _var }; @@ -23310,7 +23438,7 @@ class Handler { constructor(chunker, polisher, caller) { - let hooks = Object.assign({}, chunker && chunker.hooks, polisher && polisher.hooks); + let hooks = Object.assign({}, chunker && chunker.hooks, polisher && polisher.hooks, caller && caller.hooks); this.chunker = chunker; this.polisher = polisher; this.caller = caller; @@ -24393,7 +24521,7 @@ marginGroup.style["grid-template-columns"] = leftWidth + " 1fr " + leftWidth; } }else{ - if(rightWidth !== "none" && rightWidth !== "auto"){ + if(rightWidth !== "none" && rightWidth !== "auto"){ marginGroup.style["grid-template-columns"] = rightWidth + " 1fr " + rightWidth; }else{ marginGroup.style["grid-template-columns"] = "auto auto 1fr"; @@ -24431,7 +24559,7 @@ right.style["white-space"] = "normal"; center.style["white-space"] = "normal"; } - } + } }else if(centerWidth !== "none" && centerWidth !== "auto"){ if(leftContent && leftWidth !== "none" && leftWidth !== "auto"){ marginGroup.style["grid-template-columns"] = leftWidth + " " + centerWidth + " 1fr"; @@ -24446,14 +24574,14 @@ }else{ if(leftContent){ if(!rightContent){ - marginGroup.style["grid-template-columns"] = "1fr 0 0"; + marginGroup.style["grid-template-columns"] = "1fr 0 0"; }else{ if(leftWidth !== "none" && leftWidth !== "auto"){ if(rightWidth !== "none" && rightWidth !== "auto"){ marginGroup.style["grid-template-columns"] = leftWidth + " 1fr " + rightWidth; }else{ marginGroup.style["grid-template-columns"] = leftWidth + " 0 1fr"; - } + } }else{ if(rightWidth !== "none" && rightWidth !== "auto"){ marginGroup.style["grid-template-columns"] = "1fr 0 " + rightWidth; @@ -24468,16 +24596,16 @@ marginGroup.style["grid-template-columns"] = "minmax(16.66%, " + newLeftWidth + "%) 0 1fr"; left.style["white-space"] = "normal"; right.style["white-space"] = "normal"; - } - } + } + } } }else{ if(rightWidth !== "none" && rightWidth !== "auto"){ marginGroup.style["grid-template-columns"] = "1fr 0 " + rightWidth; }else{ marginGroup.style["grid-template-columns"] = "0 0 1fr"; - } - } + } + } } }); @@ -24518,7 +24646,7 @@ marginGroup.style["grid-template-rows"] = topHeight + " calc(100% - " + topHeight + "*2) " + topHeight; } }else{ - if(bottomHeight !== "none" && bottomHeight !== "auto"){ + if(bottomHeight !== "none" && bottomHeight !== "auto"){ marginGroup.style["grid-template-rows"] = bottomHeight + " calc(100% - " + bottomHeight + "*2) " + bottomHeight; } } @@ -24527,7 +24655,7 @@ if(bottomHeight !== "none" && bottomHeight !== "auto"){ marginGroup.style["grid-template-rows"] = bottomHeight + " calc(100% - " + bottomHeight + "*2) " + bottomHeight; } - } + } }else{ if(topContent && topHeight !== "none" && topHeight !== "auto"){ marginGroup.style["grid-template-rows"] = topHeight +" " + middleHeight + " calc(100% - (" + topHeight + " + " + middleHeight + "))"; @@ -24542,33 +24670,33 @@ }else{ if(topContent){ if(!bottomContent){ - marginGroup.style["grid-template-rows"] = "1fr 0 0"; + marginGroup.style["grid-template-rows"] = "1fr 0 0"; }else{ if(topHeight !== "none" && topHeight !== "auto"){ if(bottomHeight !== "none" && bottomHeight !== "auto"){ marginGroup.style["grid-template-rows"] = topHeight + " 1fr " + bottomHeight; }else{ marginGroup.style["grid-template-rows"] = topHeight + " 0 1fr"; - } + } }else{ if(bottomHeight !== "none" && bottomHeight !== "auto"){ marginGroup.style["grid-template-rows"] = "1fr 0 " + bottomHeight; }else{ marginGroup.style["grid-template-rows"] = "1fr 0 1fr"; - } - } + } + } } }else{ if(bottomHeight !== "none" && bottomHeight !== "auto"){ marginGroup.style["grid-template-rows"] = "1fr 0 " + bottomHeight; }else{ marginGroup.style["grid-template-rows"] = "0 0 1fr"; - } - } + } + } } - + }); } @@ -24781,8 +24909,8 @@ property === "page-break-before" || property === "page-break-after" ) { - let child = declaration.value.children.first(); - let value = child.name; + let child$$1 = declaration.value.children.first(); + let value = child$$1.name; let selector = lib.generate(rule.ruleNode.prelude); if (property === "page-break-before") { @@ -24887,7 +25015,7 @@ } } - layout(pageElement, page) { + afterLayout(pageElement, page) { this.addBreakAttributes(page); } } @@ -25797,14 +25925,14 @@ ? Number.isNaN : shim$5; - var indexOf$1 = Array.prototype.indexOf + var indexOf$2 = Array.prototype.indexOf , objHasOwnProperty = Object.prototype.hasOwnProperty , abs$1 = Math.abs , floor$1 = Math.floor; var eIndexOf = function (searchElement /*, fromIndex*/) { var i, length, fromIndex, val; - if (!isNan(searchElement)) return indexOf$1.apply(this, arguments); + if (!isNan(searchElement)) return indexOf$2.apply(this, arguments); length = toPosInteger(validValue(this).length); fromIndex = arguments[1]; @@ -25919,6 +26047,11 @@ // Chunk contents this.chunker = new Chunker(); + // Hooks + this.hooks = {}; + this.hooks.beforePolishing = new Hook(this); + this.hooks.beforeChunking = new Hook(this); + // default size this.size = { width: { @@ -26014,14 +26147,14 @@ } async preview(content, stylesheets, renderTo) { - if (!stylesheets) { - stylesheets = this.removeStyles(); - } - if (!content) { content = this.wrapContent(); } + if (!stylesheets) { + stylesheets = this.removeStyles(); + } + this.polisher.setup(); let handlers = this.initializeHandlers(); diff --git a/hyphens/js/paged.polyfill.js b/hyphens/js/paged.polyfill.js index 777ba107e86d6012c7437384e6a3ca58d176a6b7..b3eb20d3ef69e074350ae8648428f98aa59317a0 100644 --- a/hyphens/js/paged.polyfill.js +++ b/hyphens/js/paged.polyfill.js @@ -1,8 +1,7 @@ -(function(l, i, v, e) { v = l.createElement(i); v.async = 1; v.src = '//' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; e = l.getElementsByTagName(i)[0]; e.parentNode.insertBefore(v, e)})(document, 'script'); (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : - (factory()); + (global.PagedPolyfill = factory()); }(this, (function () { 'use strict'; function createCommonjsModule(fn, module) { @@ -310,6 +309,70 @@ }); var eventEmitter_1 = eventEmitter.methods; + /** + * Hooks allow for injecting functions that must all complete in order before finishing + * They will execute in parallel but all must finish before continuing + * Functions may return a promise if they are asycn. + * From epubjs/src/utils/hooks + * @param {any} context scope of this + * @example this.content = new Hook(this); + */ + class Hook { + constructor(context){ + this.context = context || this; + this.hooks = []; + } + + /** + * Adds a function to be run before a hook completes + * @example this.content.register(function(){...}); + */ + register(){ + for(var i = 0; i < arguments.length; ++i) { + if (typeof arguments[i] === "function") { + this.hooks.push(arguments[i]); + } else { + // unpack array + for(var j = 0; j < arguments[i].length; ++j) { + this.hooks.push(arguments[i][j]); + } + } + } + } + + /** + * Triggers a hook to run all functions + * @example this.content.trigger(args).then(function(){...}); + */ + trigger(){ + var args = arguments; + var context = this.context; + var promises = []; + + this.hooks.forEach(function(task) { + var executing = task.apply(context, args); + + if(executing && typeof executing["then"] === "function") { + // Task is a function that returns a promise + promises.push(executing); + } + // Otherwise Task resolves immediately, continue + }); + + + return Promise.all(promises); + } + + // Adds a function to be run before a hook completes + list(){ + return this.hooks; + } + + clear(){ + return this.hooks = []; + } + } + function getBoundingClientRect(element) { if (!element) { return; @@ -350,6 +413,14 @@ } } + function isElement(node) { + return node && node.nodeType === 1; + } + + function isText(node) { + return node && node.nodeType === 3; + } + function *walk(start, limiter) { let node = start; @@ -417,19 +488,6 @@ return after; } - function stackChildren(currentNode, stacked) { - let stack = stacked || []; - - stack.unshift(currentNode); - - let children = currentNode.children; - for (var i = 0, length = children.length; i < length; i++) { - stackChildren(children[i], stack); - } - - return stack; - } - function rebuildAncestors(node) { let parent, ancestor; let ancestors = []; @@ -479,6 +537,55 @@ return fragment; } + /* + export function split(bound, cutElement, breakAfter) { + let needsRemoval = []; + let index = indexOf(cutElement); + + if (!breakAfter && index === 0) { + return; + } + + if (breakAfter && index === (cutElement.parentNode.children.length - 1)) { + return; + } + + // Create a fragment with rebuilt ancestors + let fragment = rebuildAncestors(cutElement); + + // Clone cut + if (!breakAfter) { + let clone = cutElement.cloneNode(true); + let ref = cutElement.parentNode.getAttribute('data-ref'); + let parent = fragment.querySelector("[data-ref='" + ref + "']"); + parent.appendChild(clone); + needsRemoval.push(cutElement); + } + + // Remove all after cut + let next = nodeAfter(cutElement, bound); + while (next) { + let clone = next.cloneNode(true); + let ref = next.parentNode.getAttribute('data-ref'); + let parent = fragment.querySelector("[data-ref='" + ref + "']"); + parent.appendChild(clone); + needsRemoval.push(next); + next = nodeAfter(next, bound); + } + + // Remove originals + needsRemoval.forEach((node) => { + if (node) { + node.remove(); + } + }); + + // Insert after bounds + bound.parentNode.insertBefore(fragment, bound.nextSibling); + return [bound, bound.nextSibling]; + } + */ + function needsBreakBefore(node) { if( typeof node !== "undefined" && typeof node.dataset !== "undefined" && @@ -524,71 +631,194 @@ return false; } - /** - * Hooks allow for injecting functions that must all complete in order before finishing - * They will execute in parallel but all must finish before continuing - * Functions may return a promise if they are asycn. - * From epubjs/src/utils/hooks - * @param {any} context scope of this - * @example this.content = new Hook(this); - */ - class Hook { - constructor(context){ - this.context = context || this; - this.hooks = []; + function *words(node) { + let currentText = node.nodeValue; + let max = currentText.length; + let currentOffset = 0; + let currentLetter; + + let range; + + while(currentOffset < max) { + currentLetter = currentText[currentOffset]; + if (/^\S$/.test(currentLetter)) { + if (!range) { + range = document.createRange(); + range.setStart(node, currentOffset); + } + } else { + if (range) { + range.setEnd(node, currentOffset); + yield range; + range = undefined; + } + } + + currentOffset += 1; } - /** - * Adds a function to be run before a hook completes - * @example this.content.register(function(){...}); - */ - register(){ - for(var i = 0; i < arguments.length; ++i) { - if (typeof arguments[i] === "function") { - this.hooks.push(arguments[i]); - } else { - // unpack array - for(var j = 0; j < arguments[i].length; ++j) { - this.hooks.push(arguments[i][j]); - } - } - } + if (range) { + range.setEnd(node, currentOffset); + yield range; + range = undefined; } + } - /** - * Triggers a hook to run all functions - * @example this.content.trigger(args).then(function(){...}); - */ - trigger(){ - var args = arguments; - var context = this.context; - var promises = []; + function *letters(wordRange) { + let currentText = wordRange.startContainer; + let max = currentText.length; + let currentOffset = wordRange.startOffset; + let currentLetter; - this.hooks.forEach(function(task) { - var executing = task.apply(context, args); + let range; - if(executing && typeof executing["then"] === "function") { - // Task is a function that returns a promise - promises.push(executing); - } - // Otherwise Task resolves immediately, continue - }); + while(currentOffset < max) { + currentLetter = currentText[currentOffset]; + range = document.createRange(); + range.setStart(currentText, currentOffset); + range.setEnd(currentText, currentOffset+1); + yield range; - return Promise.all(promises); + currentOffset += 1; } + } - // Adds a function to be run before a hook completes - list(){ - return this.hooks; + function isContainer(node) { + let container; + + if (typeof node.tagName === "undefined") { + return true; + } + + if (node.style.display === "none") { + return false; + } + + switch (node.tagName) { + // Inline + case "A": + case "ABBR": + case "ACRONYM": + case "B": + case "BDO": + case "BIG": + case "BR": + case "BUTTON": + case "CITE": + case "CODE": + case "DFN": + case "EM": + case "I": + case "IMG": + case "INPUT": + case "KBD": + case "LABEL": + case "MAP": + case "OBJECT": + case "Q": + case "SAMP": + case "SCRIPT": + case "SELECT": + case "SMALL": + case "SPAN": + case "STRONG": + case "SUB": + case "SUP": + case "TEXTAREA": + case "TIME": + case "TT": + case "VAR": + // Content + case "P": + case "H1": + case "H2": + case "H3": + case "H4": + case "H5": + case "H6": + case "FIGCAPTION": + case "BLOCKQUOTE": + case "PRE": + case "LI": + case "TR": + case "DT": + case "DD": + case "VIDEO": + case "CANVAS": + container = false; + break; + default: + container = true; + } + + return container; + } + + function cloneNode(n, deep=false) { + return n.cloneNode(deep); + } + + function findElement(node, doc) { + const ref = node.getAttribute("data-ref"); + return findRef(ref, doc); + } + + function findRef(ref, doc) { + return doc.querySelector(`[data-ref='${ref}']`); + } + + function validNode(node) { + if (isText(node)) { + return true; } - clear(){ - return this.hooks = []; + if (isElement(node) && node.dataset.ref) { + return true; + } + + return false; + } + + function prevValidNode(node) { + while (!validNode(node)) { + if (node.previousSibling) { + node = node.previousSibling; + } else { + node = node.parentNode; + } + + if (!node) { + break; + } } + + return node; + } + + + function indexOf$1(node) { + let parent = node.parentNode; + if (!parent) { + return 0; + } + return Array.prototype.indexOf.call(parent.childNodes, node); + } + + function child(node, index) { + return node.childNodes[index]; } - const _requestIdleCallback = 'requestIdleCallback' in window ? requestIdleCallback : requestAnimationFrame; + function isVisible(node) { + if (isElement(node) && window.getComputedStyle(node).display !== "none") { + return true; + } else if (isText(node) && + node.textContent.trim().length && + window.getComputedStyle(node.parentNode).display !== "none") { + return true; + } + return false; + } const PER_PAGE_CHECK = 4; @@ -598,250 +828,142 @@ */ class Layout { - constructor(element, wrapper, hooks) { + constructor(element, hooks) { this.element = element; - this.wrapper = wrapper; - let space = this.element.getBoundingClientRect(); - this.width = Math.round(space.width); + this.bounds = this.element.getBoundingClientRect(); if (hooks) { this.hooks = hooks; } else { this.hooks = {}; + this.hooks.layout = new Hook(); this.hooks.renderNode = new Hook(); this.hooks.layoutNode = new Hook(); this.hooks.overflow = new Hook(); } - } - - getStart(content, breakToken) { - let start = content; - let node = breakToken.node; - - if (node) { - start = node; - } - - return start; - } - - isContainer(node) { - let container; - - if (typeof node.tagName === "undefined") { - return true; - } - - if (node.style.display === "none") { - return false; - } - switch (node.tagName) { - // Inline - case "A": - case "ABBR": - case "ACRONYM": - case "B": - case "BDO": - case "BIG": - case "BR": - case "BUTTON": - case "CITE": - case "CODE": - case "DFN": - case "EM": - case "I": - case "IMG": - case "INPUT": - case "KBD": - case "LABEL": - case "MAP": - case "OBJECT": - case "Q": - case "SAMP": - case "SCRIPT": - case "SELECT": - case "SMALL": - case "SPAN": - case "STRONG": - case "SUB": - case "SUP": - case "TEXTAREA": - case "TIME": - case "TT": - case "VAR": - // Content - case "P": - case "H1": - case "H2": - case "H3": - case "H4": - case "H5": - case "H6": - case "FIGCAPTION": - case "BLOCKQUOTE": - case "PRE": - case "LI": - case "TR": - case "DT": - case "DD": - case "VIDEO": - case "CANVAS": - container = false; - break; - default: - container = true; - } - - return container; } - layout(space, content, styleMap, edges, breakToken) { - let start = content; - if (breakToken.node) { - start = this.getStart(content, breakToken); - } - - let walker = walk(start, content); + renderTo(wrapper, source, breakToken, bounds=this.bounds) { + let start = this.getStart(source, breakToken); + let walker = walk(start, source); let node; let done; let next; - let hasOverflow = false; + let hasContent = false; let newBreakToken; - let after; let check = 0; - let dest = document.createDocumentFragment(); - - let rendered; - let shallow = true; - - while (!done && !hasOverflow) { + while (!done && !newBreakToken) { next = walker.next(); node = next.value; done = next.done; - if (node) { - this.hooks.layoutNode.trigger(node); - - // Check if the rendered element has a breakBefore set - if (hasContent && (needsBreakBefore(node) || needsPreviousBreakAfter(node) || needsPageBreak(node))) { - // Check for overflow - hasOverflow = this.hasOverflow(); - - if (hasOverflow) { - let overflow = this.overflow(this.element); + if (!node) { + newBreakToken = this.findBreakToken(wrapper, source, bounds); + return newBreakToken; + } - if (overflow) { + /* + let exists; + if (isElement(node)) { + exists = findElement(node, wrapper); + } else { + exists = false; + } - newBreakToken = this.findBreakToken(overflow, content); + if (exists) { + console.log("found", exists); + break; + } + */ - if (newBreakToken && newBreakToken.node) { - this.removeOverflow(overflow); - } + this.hooks && this.hooks.layoutNode.trigger(node); - break; + // Check if the rendered element has a break set + if (hasContent && this.shouldBreak(node)) { + newBreakToken = this.findBreakToken(wrapper, source, bounds); - } - } else { - // Break layout with current node - newBreakToken = { - node: node, - offset: 0 - }; - break; - } + if (!newBreakToken) { + newBreakToken = this.breakAt(node); } - shallow = this.isContainer(node); - - rendered = this.render(node, this.wrapper, breakToken, shallow); + break; + } - if (!hasContent) { - // Only register visible content - if (rendered.nodeType === 1 && window.getComputedStyle(rendered).display !== "none") { - hasContent = true; - } else if (rendered.nodeType === 3 && - rendered.textContent.trim().length && - window.getComputedStyle(rendered.parentNode).display !== "none") { - hasContent = true; - } - } + // Should the Node be a shallow or deep clone + let shallow = isContainer(node); - if (!shallow) { - after = nodeAfter(node, content); - walker = walk(after, content); - } + let rendered = this.append(node, wrapper, breakToken, shallow); + // Check if layout has content yet + if (!hasContent) { + hasContent = isVisible(node); + } - } else { - check = 1000; // Force check + // Skip to the next node if a deep clone was rendered + if (!shallow) { + walker = walk(nodeAfter(node, source), source); } - // Only check every 4 elements + // Only check every few elements if (check >= PER_PAGE_CHECK) { check = 0; - hasOverflow = this.hasOverflow(); - } - - if (hasOverflow) { - - let overflow = this.overflow(this.element); - - if (overflow) { - newBreakToken = this.findBreakToken(overflow, content); - - if (newBreakToken && newBreakToken.node) { - this.removeOverflow(overflow); - } - - break; - - } else { - // Underflow - hasOverflow = false; - } + this.hooks && this.hooks.layout.trigger(wrapper, this); + newBreakToken = this.findBreakToken(wrapper, source, bounds); } check += 1; } - _requestIdleCallback(() => { - this.listened = this.listeners(); - }); - - return newBreakToken; + } + breakAt(node, offset=0) { + return { + node, + offset + } } - hasOverflow() { - let width = Math.max(Math.floor(this.wrapper.getBoundingClientRect().width), this.element.scrollWidth); - return this.width < width; + shouldBreak(node) { + return needsBreakBefore(node) || needsPreviousBreakAfter(node) || needsPageBreak(node); } - render(node, dest, breakToken, shallow=true, rebuild=true) { + getStart(source, breakToken) { + let start; + let node = breakToken && breakToken.node; - let clone = this.createDOMNode(node, !shallow); + if (node) { + start = node; + } else { + start = source.firstChild; + } + + return start; + } - this.hooks.renderNode.trigger(clone); + append(node, dest, breakToken, shallow=true, rebuild=true) { - if (node.parentNode && node.parentNode.nodeType === 1) { - let parent = dest.querySelector("[data-ref='" + node.parentNode.getAttribute("data-ref") + "']"); + let clone = cloneNode(node, !shallow); + if (node.parentNode && isElement(node.parentNode)) { + let parent = findElement(node.parentNode, dest); // Rebuild chain if (parent) { parent.appendChild(clone); } else if (rebuild) { let fragment = rebuildAncestors(node); - parent = fragment.querySelector("[data-ref='" + node.parentNode.getAttribute("data-ref") + "']"); - if (breakToken && breakToken.node.nodeType === 3 && breakToken.offset > 0) { + parent = findElement(node.parentNode, fragment); + if (!parent) { + dest.appendChild(clone); + } else if (breakToken && isText(breakToken.node) && breakToken.offset > 0) { clone.textContent = clone.textContent.substring(breakToken.offset); parent.appendChild(clone); } else { @@ -858,51 +980,76 @@ dest.appendChild(clone); } - // if (!shallow && node.childNodes) { - // for (let child of node.childNodes) { - // this.render(child, dest, breakToken, shallow, rebuild); - // } - // } + this.hooks && this.hooks.renderNode.trigger(clone); return clone; } - createDOMNode(n, deep=false) { - let node = n.cloneNode(deep); + avoidBreakInside(node, limiter) { + let breakNode; - return node; + if (node === limiter) { + return; + } + + while (node.parentNode) { + node = node.parentNode; + + if (node === limiter) { + break; + } + + if(window.getComputedStyle(node)["break-inside"] === "avoid") { + breakNode = node; + break; + } + + } + return breakNode; } - findBreakToken(overflow, content) { + createBreakToken(overflow, rendered, source) { + let container = overflow.startContainer; let offset = overflow.startOffset; - let node, renderedNode, ref, parent, index, temp, startOffset; + let node, renderedNode, parent, index, temp, startOffset; + + if (isElement(container)) { + temp = child(container, offset); - if (overflow.startContainer.nodeType === 1) { - // node = children.querySelector("[data-ref='" + overflow.startContainer.childNodes[offset].getAttribute("data-ref") + "']"); - temp = overflow.startContainer.childNodes[offset]; + if (isElement(temp)) { + renderedNode = findElement(temp, rendered); - if (temp.nodeType === 1) { - ref = temp.getAttribute("data-ref"); - // node = this.parser.find(ref); - renderedNode = this.wrapper.querySelector("[data-ref='" + ref + "']"); - node = content.querySelector("[data-ref='"+ renderedNode.getAttribute("data-ref") +"']"); + if (!renderedNode) { + // Find closest element with data-ref + renderedNode = findElement(prevValidNode(temp), rendered); + return; + } + + node = findElement(renderedNode, source); offset = 0; } else { - index = Array.prototype.indexOf.call(overflow.startContainer.childNodes, temp); - ref = overflow.startContainer.getAttribute("data-ref"); - renderedNode = this.wrapper.querySelector("[data-ref='" + ref + "']"); - parent = content.querySelector("[data-ref='"+ renderedNode.getAttribute("data-ref") +"']"); - node = parent.childNodes[index]; + renderedNode = findElement(container, rendered); + + if (!renderedNode) { + renderedNode = findElement(prevValidNode(container), rendered); + } + + parent = findElement(renderedNode, source); + index = indexOf$1(temp); + node = child(parent, index); offset = 0; } } else { - index = Array.prototype.indexOf.call(overflow.startContainer.parentNode.childNodes, overflow.startContainer); - // let parent = children.querySelector("[data-ref='" + overflow.startContainer.parentNode.getAttribute("data-ref") + "']"); - ref = overflow.startContainer.parentNode.getAttribute("data-ref"); - renderedNode = this.wrapper.querySelector("[data-ref='" + ref + "']"); - parent = content.querySelector("[data-ref='"+ renderedNode.getAttribute("data-ref") +"']"); - node = parent.childNodes[index]; - startOffset = overflow.startContainer.textContent.slice(offset); + renderedNode = findElement(container.parentNode, rendered); + + if (!renderedNode) { + renderedNode = findElement(prevValidNode(container.parentNode), rendered); + } + + parent = findElement(renderedNode, source); + index = indexOf$1(container); + node = child(parent, index); + startOffset = container.textContent.slice(offset); offset = parent.textContent.indexOf(startOffset); } @@ -917,116 +1064,42 @@ } - removeOverflow(overflow) { - - if (overflow.startContainer.nodeType === 3 && overflow.startOffset > 0) { - let startText = overflow.startContainer.textContent; - let startOffset = overflow.startOffset; - let prevLetter = startText[startOffset-1]; - - // Add a hyphen if previous character is a letter or soft hyphen - if (/^\w|\u00AD$/.test(prevLetter)) { - overflow.startContainer.textContent = startText.slice(0, startOffset) + "\u2010"; - overflow.setStart(overflow.startContainer, startOffset + 1); - } - } - - - // _requestIdleCallback(() => this.removeEmpty()); - return overflow.extractContents(); - } - - removeEmpty() { - // Clean Empty Nodes - let stack = stackChildren(this.wrapper); - - stack.forEach((currentNode) => { + findBreakToken(rendered, source, bounds=this.bounds, extract=true) { + let overflow = this.findOverflow(rendered, bounds); + let breakToken; - if (!currentNode) { - return; - } + if (overflow) { + breakToken = this.createBreakToken(overflow, rendered, source); - if (currentNode.childNodes.length === 0) { - // Check in original - let original = currentNode.getAttribute("data-children"); - if (original != 0) { - currentNode.remove(); - } - } else if (currentNode.textContent.trim().length === 0) { - let original = currentNode.getAttribute("data-text"); - if (original != 0) { - currentNode.remove(); - } + if (breakToken && breakToken.node && extract) { + this.removeOverflow(overflow); } - }); - - stack = undefined; - _requestIdleCallback(() => this.floats()); - - } - - floats(area) { - let bounds = getBoundingClientRect(this.element); - - let start = Math.round(bounds.left); - let end = Math.round(bounds.right); - - let range; - - let walker = walk(this.wrapper.firstChild, this.wrapper); - let next, done, node; - while (!done) { - next = walker.next(); - done = next.done; - node = next.value; - - if (node) { - let pos = getBoundingClientRect(node); - let left = Math.floor(pos.left); - let right = Math.floor(pos.right); - if (left >= end) { - range = document.createRange(); - range.selectNode(node); - // let extracted = range.extractContents(); - let extracted = this.removeOverflow(range); - this._onOverflow && this._onOverflow(extracted); - } - - } } - - } - - onEnter(node) { - - } - - onExit(node) { - - } - - onOverflow(func) { - this._onOverflow = func; + return breakToken; } - onUnderflow(func) { - this._onUnderflow = func; + hasOverflow(element, bounds=this.bounds) { + let constrainingElement = element.parentNode; // this gets the element, instead of the wrapper for the width workaround + let { width } = element.getBoundingClientRect(); + let { scrollWidth } = constrainingElement; + return Math.max(Math.floor(width), scrollWidth) > Math.round(bounds.width); } - overflow(area) { - let bounds = getBoundingClientRect(this.element); + findOverflow(rendered, bounds=this.bounds) { + if (!this.hasOverflow(rendered, bounds)) return; let start = Math.round(bounds.left); let end = Math.round(bounds.right); let range; - let walker = walk(this.wrapper.firstChild, this.wrapper); - let next, done, node, offset; + let walker = walk(rendered.firstChild, rendered); + let next, done, node, offset, skip; while (!done) { next = walker.next(); done = next.done; node = next.value; + skip = false; if (node) { let pos = getBoundingClientRect(node); @@ -1036,18 +1109,19 @@ if (!range && left >= end) { // Check if it is a float let isFloat = false; - if (node.nodeType === 1) { + if (isElement(node)) { let styles = window.getComputedStyle(node); isFloat = styles.getPropertyValue("float") !== "none"; + skip = window.getComputedStyle(node)["break-inside"] === "avoid"; } - if (!isFloat && node.nodeType === 1) { + if (!isFloat && isElement(node)) { range = document.createRange(); range.setStartBefore(node); break; } - if (node.nodeType === 3 && node.textContent.trim().length) { + if (isText(node) && node.textContent.trim().length) { range = document.createRange(); range.setStartBefore(node); break; @@ -1055,7 +1129,9 @@ } - if (!range && node.nodeType === 3 && right > end && node.textContent.trim().length) { + if (!range && isText(node) && + right > end && node.textContent.trim().length && + window.getComputedStyle(node.parentNode)["break-inside"] !== "avoid") { range = document.createRange(); offset = this.textBreak(node, start, end); if (!offset) { @@ -1067,263 +1143,142 @@ } // Skip children - if (right < end) { + if (skip || right < end) { - next = nodeAfter(node, this.wrapper); + next = nodeAfter(node, rendered); if (next) { - walker = walk(next, this.wrapper); + walker = walk(next, rendered); } } } } - if (range) { - range.setEndAfter(this.wrapper.lastChild); - - this.hooks.overflow.trigger(range); - - return range; - } - - } - - textBreak(node, start, end) { - let wordwalker = this.words(node); - let left = 0; - let right = 0; - let word, next, done, pos; - let offset; - while (!done) { - next = wordwalker.next(); - word = next.value; - done = next.done; - - if (!word) { - break; - } - - pos = getBoundingClientRect(word); - - left = Math.floor(pos.left); - right = Math.floor(pos.right); - - if (left >= end) { - offset = word.startOffset; - break; - } - - if (right > end) { - let letterwalker = this.letters(word); - let letter, nextLetter, doneLetter; - - while (!doneLetter) { - nextLetter = letterwalker.next(); - letter = nextLetter.value; - doneLetter = nextLetter.done; - - if (!letter) { - break; - } - - pos = getBoundingClientRect(letter); - left = Math.floor(pos.left); - - if (left >= end) { - offset = letter.startOffset; - done = true; - - break; - } - } - } - - } - - return offset; - } - - *words(node) { - let currentText = node.nodeValue; - let max = currentText.length; - let currentOffset = 0; - let currentLetter; - - let range; - - while(currentOffset < max) { - currentLetter = currentText[currentOffset]; - if (/^\S$/.test(currentLetter)) { - if (!range) { - range = document.createRange(); - range.setStart(node, currentOffset); - } - } else { - if (range) { - range.setEnd(node, currentOffset); - yield range; - range = undefined; - } - } - - currentOffset += 1; - } + // Find End if (range) { - range.setEnd(node, currentOffset); - yield range; - range = undefined; + range.setEndAfter(rendered.lastChild); + return range; } - } - - *letters(wordRange) { - let currentText = wordRange.startContainer; - let max = currentText.length; - let currentOffset = wordRange.startOffset; - let currentLetter; - - let range; - while(currentOffset < max) { - currentLetter = currentText[currentOffset]; - range = document.createRange(); - range.setStart(currentText, currentOffset); - range.setEnd(currentText, currentOffset+1); - - yield range; - - currentOffset += 1; - } } - prepend(fragment, rebuild=true) { - if (!fragment) { + findEndToken(rendered, source, bounds=this.bounds) { + if (rendered.childNodes.length === 0) { return; } - let walker = walk(fragment.firstChild, this.wrapper); - let next, node, done; - let parent; - while (!done) { - next = walker.next(); - node = next.value; - done = next.done; - if (!node) { - break; - } - - let exists = false; + let lastChild = rendered.lastChild; - if (node.nodeType === 1) { - exists = this.wrapper.querySelector("[data-ref='" + node.getAttribute("data-ref") + "']"); - } - - if (exists) { - parent = exists; + let lastNodeIndex; + while (lastChild && lastChild.lastChild) { + if (!validNode(lastChild)) { + // Only get elements with refs + lastChild = lastChild.previousSibling; + } else if(!validNode(lastChild.lastChild)) { + // Deal with invalid dom items + lastChild = prevValidNode(lastChild.lastChild); + break; } else { - if(parent) { - parent.insertBefore(node, parent.firstChild); - break; - } else { - this.wrapper.insertBefore(node, this.wrapper.firstChild); - } + lastChild = lastChild.lastChild; } } - let hasOverflow = this.hasOverflow(); + if (isText(lastChild)) { - if (hasOverflow) { - - let overflow = this.overflow(this.element); - - if (overflow) { - // let extracted = overflow.extractContents(); - let extracted = this.removeOverflow(overflow); - this._onOverflow && this._onOverflow(extracted); + if (lastChild.parentNode.dataset.ref) { + lastNodeIndex = indexOf$1(lastChild); + lastChild = lastChild.parentNode; + } else { + lastChild = lastChild.previousSibling; } } - if (!this.listened) { - this.listened = this.listeners(); - } - } - - getOverflow() { - let overflow = this.overflow(this.element); - - if (overflow) { - // let extracted = overflow.extractContents(); - let extracted = this.removeOverflow(overflow); - this._onOverflow && this._onOverflow(extracted); - } - } - - getUnderflow() { - this._onUnderflow && this._onUnderflow(); - } - - listeners() { - if (typeof ResizeObserver !== "undefined") { - this.addResizeObserver(); - } else { - this.element.addEventListener("overflow", this.getOverflow.bind(this), false); - this.element.addEventListener("underflow", this.getUnderflow.bind(this), false); - } - // TODO: fall back to mutation observer? - + let original = findElement(lastChild, source); - // Key scroll width from changing - this.element.addEventListener("scroll", () => { - this.element.scrollLeft = 0; - }); + if (lastNodeIndex) { + original = original.childNodes[lastNodeIndex]; + } - return true; + let after = nodeAfter(original); + + return this.breakAt(after); } - addResizeObserver() { - let wrapper = this.wrapper; - let prevHeight = wrapper.getBoundingClientRect().height; - this.ro = new ResizeObserver( entries => { - for (let entry of entries) { - const cr = entry.contentRect; + textBreak(node, start, end) { + let wordwalker = words(node); + let left = 0; + let right = 0; + let word, next, done, pos; + let offset; + while (!done) { + next = wordwalker.next(); + word = next.value; + done = next.done; - if (cr.height > prevHeight) { - let hasOverflow = this.hasOverflow(); + if (!word) { + break; + } - if (hasOverflow) { + pos = getBoundingClientRect(word); - let overflow = this.overflow(this.element); + left = Math.floor(pos.left); + right = Math.floor(pos.right); - if (overflow) { - // let extracted = overflow.extractContents(); - let extracted = this.removeOverflow(overflow); - this._onOverflow && this._onOverflow(extracted); - prevHeight = wrapper.getBoundingClientRect().height; - } - } else { - prevHeight = cr.height; + if (left >= end) { + offset = word.startOffset; + break; + } + + if (right > end) { + let letterwalker = letters(word); + let letter, nextLetter, doneLetter; + + while (!doneLetter) { + nextLetter = letterwalker.next(); + letter = nextLetter.value; + doneLetter = nextLetter.done; + + if (!letter) { + break; } - } else if (cr.height < prevHeight ) { // TODO: calc line height && (prevHeight - cr.height) >= 22 - this._onUnderflow && this._onUnderflow(); - prevHeight = cr.height; + pos = getBoundingClientRect(letter); + left = Math.floor(pos.left); + + if (left >= end) { + offset = letter.startOffset; + done = true; + + break; + } } } - }); - this.ro.observe(wrapper); - } + } - destroy() { - this.element.removeEventListener("overflow", this.getOverflow.bind(this), false); - this.element.removeEventListener("underflow", this.getUnderflow.bind(this), false); + return offset; + } - this.ro.disconnect(); + removeOverflow(overflow) { + this.hyphenateAtBreak(overflow); - this.element = element; - this.wrapper = wrapper; + return overflow.extractContents(); } + hyphenateAtBreak(overflow) { + if (isText(overflow.startContainer) && overflow.startOffset > 0) { + let startText = overflow.startContainer.textContent; + let startOffset = overflow.startOffset; + let prevLetter = startText[startOffset-1]; + + // Add a hyphen if previous character is a letter or soft hyphen + if (/^\w|\u00AD$/.test(prevLetter)) { + overflow.startContainer.textContent = startText.slice(0, startOffset) + "\u2010"; + overflow.setStart(overflow.startContainer, startOffset + 1); + } + } + } } eventEmitter(Layout.prototype); @@ -1379,15 +1334,17 @@ this.element = page; this.area = area; + return page; + } + + createWrapper() { let wrapper = document.createElement("div"); - // wrapper.setAttribute("contenteditable", true); - // wrapper.style.outline = "none"; this.area.appendChild(wrapper); this.wrapper = wrapper; - return page; + return wrapper; } index(pgnum) { @@ -1438,18 +1395,24 @@ layout(contents, breakToken) { // console.log("layout page", this.id); - let size = this.area.getBoundingClientRect(); - this.l = new Layout(this.area, this.wrapper, this.hooks); + this.clear(); - this.l.onOverflow((overflow) => { - this._onOverflow && this._onOverflow(overflow); - }); + this.layoutMethod = new Layout(this.area, this.hooks); - this.l.onUnderflow((overflow) => { - this._onUnderflow && this._onUnderflow(overflow); - }); + breakToken = this.layoutMethod.renderTo(this.wrapper, contents, breakToken); + + this.addListeners(contents); + + return breakToken; + } - breakToken = this.l.layout(size, contents, {}, {}, breakToken); + append(contents, breakToken) { + + if (!this.layoutMethod) { + return this.layout(contents, breakToken); + } + + breakToken = this.layoutMethod.renderTo(this.wrapper, contents, breakToken); return breakToken; } @@ -1473,29 +1436,104 @@ this._onUnderflow = func; } - prepend(fragment) { - if (!this.l) { - this.l = new Layout(this.area, this.wrapper, this.hooks); + clear() { + this.removeListeners(); + this.wrapper && this.wrapper.remove(); + this.createWrapper(); + } - this.l.onOverflow((overflow) => { - this._onOverflow && this._onOverflow(overflow); - }); + addListeners(contents) { + if (typeof ResizeObserver !== "undefined") { + this.addResizeObserver(contents); + } else { + this.element.addEventListener("overflow", this.checkOverflowAfterResize.bind(this, contents), false); + this.element.addEventListener("underflow", this.checkOverflowAfterResize.bind(this, contents), false); + } + // TODO: fall back to mutation observer? - this.l.onUnderflow((overflow) => { - this._onUnderflow && this._onUnderflow(overflow); - }); + + // Key scroll width from changing + this.element.addEventListener("scroll", () => { + if(this.listening) { + this.element.scrollLeft = 0; + } + }); + + this.listening = true; + + return true; + } + + removeListeners() { + this.listening = false; + // clearTimeout(this.timeoutAfterResize); + + if (this.element) { + this.element.removeEventListener("overflow", this.checkOverflowAfterResize.bind(this), false); + this.element.removeEventListener("underflow", this.checkOverflowAfterResize.bind(this), false); + } + + if (this.ro) { + this.ro.disconnect(); } + } + + addResizeObserver(contents) { + let wrapper = this.wrapper; + let prevHeight = wrapper.getBoundingClientRect().height; + this.ro = new ResizeObserver( entries => { + + if (!this.listening) { + return; + } + + for (let entry of entries) { + const cr = entry.contentRect; + + if (cr.height > prevHeight) { + this.checkOverflowAfterResize(contents); + prevHeight = wrapper.getBoundingClientRect().height; + } else if (cr.height < prevHeight ) { // TODO: calc line height && (prevHeight - cr.height) >= 22 + this.checkUnderflowAfterResize(contents); + prevHeight = cr.height; + } + } + }); - this.l.prepend(fragment); + this.ro.observe(wrapper); } - append() { + checkOverflowAfterResize(contents) { + if (!this.listening || !this.layoutMethod) { + return; + } + + let newBreakToken = this.layoutMethod.findBreakToken(this.wrapper, contents); + if (newBreakToken) { + this._onOverflow && this._onOverflow(newBreakToken); + } } + checkUnderflowAfterResize(contents) { + if (!this.listening || !this.layoutMethod) { + return; + } + + let endToken = this.layoutMethod.findEndToken(this.wrapper, contents); + + // let newBreakToken = this.layoutMethod.findBreakToken(this.wrapper, contents); + + if (endToken) { + this._onUnderflow && this._onUnderflow(endToken); + } + } destroy() { + this.removeListeners(); + this.element = undefined; + this.wrapper = undefined; } } @@ -1555,15 +1593,19 @@ let node; while(node = treeWalker.nextNode()) { - let uuid = UUID(); - node.setAttribute("data-ref", uuid); + if (!node.hasAttribute("data-ref")) { + let uuid = UUID(); + node.setAttribute("data-ref", uuid); + } + if (node.id) { node.setAttribute("data-id", node.id); } - node.setAttribute("data-children", node.childNodes.length); - node.setAttribute("data-text", node.textContent.trim().length); + // node.setAttribute("data-children", node.childNodes.length); + + // node.setAttribute("data-text", node.textContent.trim().length); } } @@ -1672,6 +1714,7 @@ // this.preview = preview; this.hooks = {}; + this.hooks.beforeParsed = new Hook(this); this.hooks.afterParsed = new Hook(this); this.hooks.beforePageLayout = new Hook(this); this.hooks.layout = new Hook(this); @@ -1706,7 +1749,13 @@ } async flow(content, renderTo) { - let parsed = new ContentParser(content); + let parsed; + + await this.hooks.beforeParsed.trigger(content, this); + + parsed = new ContentParser(content); + + this.source = parsed; this.setup(renderTo); @@ -1792,7 +1841,7 @@ if (page) { await this.hooks.beforePageLayout.trigger(page, undefined, undefined, this); this.emit("page", page); - await this.hooks.layout.trigger(page.element, page, undefined, this); + // await this.hooks.layout.trigger(page.element, page, undefined, this); await this.hooks.afterPageLayout.trigger(page.element, page, undefined, this); this.emit("renderedPage", page); } @@ -1817,7 +1866,7 @@ // Layout content in the page, starting from the breakToken breakToken = page.layout(content, breakToken); - await this.hooks.layout.trigger(page.element, page, breakToken, this); + // await this.hooks.layout.trigger(page.element, page, breakToken, this); await this.hooks.afterPageLayout.trigger(page.element, page, breakToken, this); this.emit("renderedPage", page); @@ -1843,24 +1892,26 @@ if (!blank) { // Listen for page overflow - page.onOverflow((overflow) => { - _requestIdleCallback$1(() => { - let index = this.pages.indexOf(page) + 1; - if (index < this.pages.length && - (this.pages[index].breakBefore || this.pages[index].previousBreakAfter)) { - let newPage = this.insertPage(index - 1); - newPage.prepend(overflow); - } else if (index < this.pages.length) { - this.pages[index].prepend(overflow); - } else { - let newPage = this.addPage(); - newPage.prepend(overflow); - } - }); + page.onOverflow((overflowToken) => { + // console.log("overflow on", page.id, overflowToken); + let index = this.pages.indexOf(page) + 1; + if (index < this.pages.length && + (this.pages[index].breakBefore || this.pages[index].previousBreakAfter)) { + let newPage = this.insertPage(index - 1); + newPage.layout(this.source, overflowToken); + } else if (index < this.pages.length) { + this.pages[index].layout(this.source, overflowToken); + } else { + let newPage = this.addPage(); + newPage.layout(this.source, overflowToken); + } }); - page.onUnderflow(() => { - // console.log("underflow on", page.id); + page.onUnderflow((overflowToken) => { + // console.log("underflow on", page.id, overflowToken); + + // page.append(this.source, overflowToken); + }); } @@ -1887,15 +1938,13 @@ if (!blank) { // Listen for page overflow - page.onOverflow((overflow) => { - _requestIdleCallback$1(() => { - if (total < this.pages.length) { - this.pages[total].prepend(overflow); - } else { - let newPage = this.addPage(); - newPage.prepend(overflow); - } - }); + page.onOverflow((overflowToken) => { + if (total < this.pages.length) { + this.pages[total].layout(this.source, overflowToken); + } else { + let newPage = this.addPage(); + newPage.layout(this.source, overflowToken); + } }); page.onUnderflow(() => { @@ -2452,7 +2501,7 @@ this.remove(oldItem); }; - var list = List; + var List_1 = List; var createCustomError = function createCustomError(name, message) { // use Object.create(), because some VMs prevent setting line/column otherwise @@ -3916,6 +3965,7 @@ var findIdentifierEnd$2 = utils.findIdentifierEnd; var findNumberEnd$2 = utils.findNumberEnd; var findDecimalNumberEnd$1 = utils.findDecimalNumberEnd; + var findStringEnd$2 = utils.findStringEnd; var isHex$1 = utils.isHex; var SYMBOL_TYPE$2 = _const.SYMBOL_TYPE; @@ -3923,6 +3973,8 @@ var PLUSSIGN$2 = _const.TYPE.PlusSign; var HYPHENMINUS$3 = _const.TYPE.HyphenMinus; var NUMBERSIGN = _const.TYPE.NumberSign; + var APOSTROPHE$1 = _const.TYPE.Apostrophe; + var QUOTATIONMARK$1 = _const.TYPE.QuotationMark; var PERCENTAGE = { '%': true @@ -4094,6 +4146,19 @@ return findIdentifierEnd$2(str, offset + 1); } + function ident(token, addTokenToMatch) { + if (token === null) { + return false; + } + + if (consumeIdentifier(token.value, 0) !== token.value.length) { + return false; + } + + addTokenToMatch(); + return true; + } + function astNode(type) { return function(token, addTokenToMatch) { if (token === null || token.node.type !== type) { @@ -4105,6 +4170,25 @@ }; } + function string(token, addTokenToMatch) { + if (token === null || token.value.length < 2) { + return false; + } + + var quote = token.value.charCodeAt(0); + + if (quote !== APOSTROPHE$1 && quote !== QUOTATIONMARK$1) { + return false; + } + + if (findStringEnd$2(token.value, 1, quote) !== token.value.length) { + return false; + } + + addTokenToMatch(); + return true; + } + function dimension(type) { return function(token, addTokenToMatch, getNextToken) { if (calc(token, addTokenToMatch, getNextToken)) { @@ -4308,7 +4392,7 @@ 'flex': dimension(FLEX), 'hex-color': hexColor, 'id-selector': idSelector, // element( ) - 'ident': astNode('Identifier'), + 'ident': ident, 'integer': integer, 'length': zeroUnitlessDimension(LENGTH), 'number': number, @@ -4318,7 +4402,7 @@ 'positive-integer': positiveInteger, 'resolution': dimension(RESOLUTION), 'semitones': dimension(SEMITONES), - 'string': astNode('String'), + 'string': string, 'time': dimension(TIME), 'unicode-range': astNode('UnicodeRange'), 'url': url, @@ -4409,7 +4493,7 @@ var EXCLAMATIONMARK$1 = 33; // ! var NUMBERSIGN$1 = 35; // # var AMPERSAND = 38; // & - var APOSTROPHE$1 = 39; // ' + var APOSTROPHE$2 = 39; // ' var LEFTPARENTHESIS$1 = 40; // ( var RIGHTPARENTHESIS$1 = 41; // ) var ASTERISK = 42; // * @@ -4614,11 +4698,11 @@ var name; tokenizer.eat(LESSTHANSIGN$1); - tokenizer.eat(APOSTROPHE$1); + tokenizer.eat(APOSTROPHE$2); name = scanWord(tokenizer); - tokenizer.eat(APOSTROPHE$1); + tokenizer.eat(APOSTROPHE$2); tokenizer.eat(GREATERTHANSIGN$1); return maybeMultiplied(tokenizer, { @@ -4798,7 +4882,7 @@ return maybeMultiplied(tokenizer, readGroup(tokenizer)); case LESSTHANSIGN$1: - return tokenizer.nextCharCode() === APOSTROPHE$1 + return tokenizer.nextCharCode() === APOSTROPHE$2 ? readProperty(tokenizer) : readType(tokenizer); @@ -4827,7 +4911,7 @@ type: 'Comma' }; - case APOSTROPHE$1: + case APOSTROPHE$2: return maybeMultiplied(tokenizer, { type: 'String', value: scanString(tokenizer) @@ -6104,9 +6188,9 @@ var start = getFirstMatchNode(matchNode); var end = getLastMatchNode(matchNode); - lexer.syntax.walk(ast, function(node, item, list$$1) { + lexer.syntax.walk(ast, function(node, item, list) { if (node === start) { - var nodes = new list(); + var nodes = new List_1(); do { nodes.appendData(item.data); @@ -6119,7 +6203,7 @@ } while (item !== null); fragments.push({ - parent: list$$1, + parent: list, nodes: nodes }); } @@ -6219,7 +6303,7 @@ if (typeof fieldType === 'string') { valid = node[key] && node[key].type === fieldType; } else if (Array.isArray(fieldType)) { - valid = node[key] instanceof list; + valid = node[key] instanceof List_1; } } } @@ -6775,16 +6859,16 @@ readSequence: sequence, createList: function() { - return new list(); + return new List_1(); }, createSingleNodeList: function(node) { - return new list().appendData(node); + return new List_1().appendData(node); }, - getFirstListNode: function(list$$1) { - return list$$1 && list$$1.first(); + getFirstListNode: function(list) { + return list && list.first(); }, - getLastListNode: function(list$$1) { - return list$$1.last(); + getLastListNode: function(list) { + return list.last(); }, parseWithFallback: function(consumer, fallback) { @@ -6818,10 +6902,10 @@ return null; }, - getLocationFromList: function(list$$1) { + getLocationFromList: function(list) { if (this.needPositions) { - var head = this.getFirstListNode(list$$1); - var tail = this.getLastListNode(list$$1); + var head = this.getFirstListNode(list); + var tail = this.getLastListNode(list); return this.scanner.getLocationRange( head !== null ? head.loc.start.offset - this.scanner.startOffset : this.scanner.tokenStart, tail !== null ? tail.loc.end.offset - this.scanner.startOffset : this.scanner.tokenStart, @@ -10075,8 +10159,8 @@ fromPlainObject: function(ast) { walk(ast, { enter: function(node) { - if (node.children && node.children instanceof list === false) { - node.children = new list().fromArray(node.children); + if (node.children && node.children instanceof List_1 === false) { + node.children = new List_1().fromArray(node.children); } } }); @@ -10086,7 +10170,7 @@ toPlainObject: function(ast) { walk(ast, { leave: function(node) { - if (node.children && node.children instanceof list) { + if (node.children && node.children instanceof List_1) { node.children = node.children.toArray(); } } @@ -10319,7 +10403,7 @@ var value = node[key]; if (value) { - if (Array.isArray(value) || value instanceof list) { + if (Array.isArray(value) || value instanceof List_1) { value = value.map(clone); } else if (value.constructor === Object) { value = clone(value); @@ -10442,7 +10526,7 @@ var convert = create$3(walk); var syntax = { - List: list, + List: List_1, Tokenizer: tokenizer, Lexer: Lexer_1, @@ -18403,7 +18487,7 @@ syntax: "space-between | space-around | space-evenly | stretch" }, "content-list": { - syntax: "[ | contents | | | | ]+" + syntax: "[ | contents | | | | | ]+" }, "content-position": { syntax: "center | start | end | flex-start | flex-end" @@ -19747,6 +19831,50 @@ "pseudo-page": { comment: "syntax is incorrect and can't be parsed, drop for now", syntax: null + }, + "id-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "attribute-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "wq-name": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "ns-prefix": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "class-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "compound-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "type-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "subclass-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "pseudo-class-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "pseudo-element-selector": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null + }, + "compound-selector-list": { + comment: "temporary omit due lack of nested syntax definition(s)", + syntax: null } }; var patch = { @@ -22220,7 +22348,7 @@ // https://drafts.csswg.org/css-images-4/#element-notation // https://developer.mozilla.org/en-US/docs/Web/CSS/element - var element$1 = function() { + var element = function() { this.scanner.skipSC(); var children = this.createSingleNodeList( @@ -22283,8 +22411,8 @@ var value = { getNode: _default, - '-moz-element': element$1, - 'element': element$1, + '-moz-element': element, + 'element': element, 'expression': expression$1, 'var': _var }; @@ -23310,7 +23438,7 @@ class Handler { constructor(chunker, polisher, caller) { - let hooks = Object.assign({}, chunker && chunker.hooks, polisher && polisher.hooks); + let hooks = Object.assign({}, chunker && chunker.hooks, polisher && polisher.hooks, caller && caller.hooks); this.chunker = chunker; this.polisher = polisher; this.caller = caller; @@ -24393,7 +24521,7 @@ marginGroup.style["grid-template-columns"] = leftWidth + " 1fr " + leftWidth; } }else{ - if(rightWidth !== "none" && rightWidth !== "auto"){ + if(rightWidth !== "none" && rightWidth !== "auto"){ marginGroup.style["grid-template-columns"] = rightWidth + " 1fr " + rightWidth; }else{ marginGroup.style["grid-template-columns"] = "auto auto 1fr"; @@ -24431,7 +24559,7 @@ right.style["white-space"] = "normal"; center.style["white-space"] = "normal"; } - } + } }else if(centerWidth !== "none" && centerWidth !== "auto"){ if(leftContent && leftWidth !== "none" && leftWidth !== "auto"){ marginGroup.style["grid-template-columns"] = leftWidth + " " + centerWidth + " 1fr"; @@ -24446,14 +24574,14 @@ }else{ if(leftContent){ if(!rightContent){ - marginGroup.style["grid-template-columns"] = "1fr 0 0"; + marginGroup.style["grid-template-columns"] = "1fr 0 0"; }else{ if(leftWidth !== "none" && leftWidth !== "auto"){ if(rightWidth !== "none" && rightWidth !== "auto"){ marginGroup.style["grid-template-columns"] = leftWidth + " 1fr " + rightWidth; }else{ marginGroup.style["grid-template-columns"] = leftWidth + " 0 1fr"; - } + } }else{ if(rightWidth !== "none" && rightWidth !== "auto"){ marginGroup.style["grid-template-columns"] = "1fr 0 " + rightWidth; @@ -24468,16 +24596,16 @@ marginGroup.style["grid-template-columns"] = "minmax(16.66%, " + newLeftWidth + "%) 0 1fr"; left.style["white-space"] = "normal"; right.style["white-space"] = "normal"; - } - } + } + } } }else{ if(rightWidth !== "none" && rightWidth !== "auto"){ marginGroup.style["grid-template-columns"] = "1fr 0 " + rightWidth; }else{ marginGroup.style["grid-template-columns"] = "0 0 1fr"; - } - } + } + } } }); @@ -24518,7 +24646,7 @@ marginGroup.style["grid-template-rows"] = topHeight + " calc(100% - " + topHeight + "*2) " + topHeight; } }else{ - if(bottomHeight !== "none" && bottomHeight !== "auto"){ + if(bottomHeight !== "none" && bottomHeight !== "auto"){ marginGroup.style["grid-template-rows"] = bottomHeight + " calc(100% - " + bottomHeight + "*2) " + bottomHeight; } } @@ -24527,7 +24655,7 @@ if(bottomHeight !== "none" && bottomHeight !== "auto"){ marginGroup.style["grid-template-rows"] = bottomHeight + " calc(100% - " + bottomHeight + "*2) " + bottomHeight; } - } + } }else{ if(topContent && topHeight !== "none" && topHeight !== "auto"){ marginGroup.style["grid-template-rows"] = topHeight +" " + middleHeight + " calc(100% - (" + topHeight + " + " + middleHeight + "))"; @@ -24542,33 +24670,33 @@ }else{ if(topContent){ if(!bottomContent){ - marginGroup.style["grid-template-rows"] = "1fr 0 0"; + marginGroup.style["grid-template-rows"] = "1fr 0 0"; }else{ if(topHeight !== "none" && topHeight !== "auto"){ if(bottomHeight !== "none" && bottomHeight !== "auto"){ marginGroup.style["grid-template-rows"] = topHeight + " 1fr " + bottomHeight; }else{ marginGroup.style["grid-template-rows"] = topHeight + " 0 1fr"; - } + } }else{ if(bottomHeight !== "none" && bottomHeight !== "auto"){ marginGroup.style["grid-template-rows"] = "1fr 0 " + bottomHeight; }else{ marginGroup.style["grid-template-rows"] = "1fr 0 1fr"; - } - } + } + } } }else{ if(bottomHeight !== "none" && bottomHeight !== "auto"){ marginGroup.style["grid-template-rows"] = "1fr 0 " + bottomHeight; }else{ marginGroup.style["grid-template-rows"] = "0 0 1fr"; - } - } + } + } } - + }); } @@ -24781,8 +24909,8 @@ property === "page-break-before" || property === "page-break-after" ) { - let child = declaration.value.children.first(); - let value = child.name; + let child$$1 = declaration.value.children.first(); + let value = child$$1.name; let selector = lib.generate(rule.ruleNode.prelude); if (property === "page-break-before") { @@ -24887,7 +25015,7 @@ } } - layout(pageElement, page) { + afterLayout(pageElement, page) { this.addBreakAttributes(page); } } @@ -25797,14 +25925,14 @@ ? Number.isNaN : shim$5; - var indexOf$1 = Array.prototype.indexOf + var indexOf$2 = Array.prototype.indexOf , objHasOwnProperty = Object.prototype.hasOwnProperty , abs$1 = Math.abs , floor$1 = Math.floor; var eIndexOf = function (searchElement /*, fromIndex*/) { var i, length, fromIndex, val; - if (!isNan(searchElement)) return indexOf$1.apply(this, arguments); + if (!isNan(searchElement)) return indexOf$2.apply(this, arguments); length = toPosInteger(validValue(this).length); fromIndex = arguments[1]; @@ -25919,6 +26047,11 @@ // Chunk contents this.chunker = new Chunker(); + // Hooks + this.hooks = {}; + this.hooks.beforePolishing = new Hook(this); + this.hooks.beforeChunking = new Hook(this); + // default size this.size = { width: { @@ -26014,14 +26147,14 @@ } async preview(content, stylesheets, renderTo) { - if (!stylesheets) { - stylesheets = this.removeStyles(); - } - if (!content) { content = this.wrapContent(); } + if (!stylesheets) { + stylesheets = this.removeStyles(); + } + this.polisher.setup(); let handlers = this.initializeHandlers(); @@ -26058,6 +26191,8 @@ initializeHandlers: initializeHandlers }); + window.Paged = Paged; + let ready = new Promise(function(resolve, reject){ if (document.readyState === "interactive" || document.readyState === "complete") { resolve(document.readyState); @@ -26071,13 +26206,34 @@ }; }); - let previewer = new Previewer(); + let config = window.PagedConfig || { + auto: true, + before: undefined, + after: undefined, + content: undefined, + stylesheets: undefined, + renderTo: undefined + }; + + let previewer = new Previewer(config.content, config.stylesheets, config.renderTo); - window.PagedPolyfill = previewer; - window.Paged = Paged; ready.then(async function () { - await previewer.preview(); + let done; + if (config.before) { + await config.before(); + } + + if(config.auto !== false) { + done = await previewer.preview(); + } + + + if (config.after) { + await config.after(done); + } }); + return previewer; + })));