Commit b82b64fe authored by julien's avatar julien

Merge branch 'target-text-update' into 'master'

Target text update

See merge request !71
parents 3b893392 0bc1a786
Pipeline #330 passed with stage
in 6 minutes and 16 seconds
......@@ -4,3 +4,4 @@ dist
lib
__diff_output__
specs/**/*.pdf
issues
\ No newline at end of file
<!DOCTYPE html PUBLIC>
<html lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<title>
targer-counter
</title>
<script src="../../../dist/paged.polyfill.js"></script>
<style>
:root {
font-size: 18px;
}
@page {
size: 6in 8in;
margin: 20mm 20mm;
/* border: solid 1px black; */
@bottom-left {
content: counter(page);
}
}
section {
break-before: page;
}
p {
line-height: 22px;
}
h1 {
font-size: 24px;
margin-top: 0;
}
a {
text-decoration: none;
color: currentColor;
}
a::after {
color: blueviolet;
display: block;
margin-bottom: 3em;
}
nav li:nth-of-type(1) a[href]::after {
content: target-text(attr(href url));
}
nav li:nth-of-type(2) a[href]::after {
content: target-text(attr(href url), content);
}
nav li:nth-of-type(3) a[href]::after {
content: target-text(attr(href url), first-letter);
}
nav li:nth-of-type(4) a[href]::after {
content: target-text(attr(href url), before);
}
nav li:nth-of-type(5) a[href]::after {
content: target-text(attr(href url), after);
}
/* before and after */
#chap3::before {
content: " This is inside the before: ";
color: orange;
}
#chap4::after {
content: ": This is inside the after ";
color: orange;
}
</style>
<style>
/* interface */
@media screen {
body {
background: whitesmoke;
}
.pagedjs_page {
background: white;
margin-bottom: 10px;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
}
.pagedjs_pages {
width: calc(var(--pagedjs-width) * 2);
display: flex;
flex-direction: column;
margin: 0 auto;
}
}
</style>
</head>
<body>
<section>
<nav>
<h1>Table of contents</h1>
<ul>
<li><a href="#chap0">target-text: </a></li>
<li><a href="#chap1">target-text content: </a></li>
<li><a href="#chap2">target-text first-letter: </a></li>
<li><a href="#chap3">target-text:: before: </a></li>
<li><a href="#chap4">target-text:: after: </a></li>
</ul>
</nav>
</section>
<section>
<h1 id="chap0">Lorem ipsum dolor sit amet</h1>
</section>
<section>
<h1 id="chap1">Lorem ipsum dolor sit amet</h1>
</section>
<section>
<h1 id="chap2">Praesent placerat lectus</h1>
</section>
<section>
<h1 id="chap3">Aliquam eget massa loborti</h1>
</section>
<section>
<h1 id="chap4">Aliquam eget massa loborti</h1>
</section>
</body>
</html>
\ No newline at end of file
const TIMEOUT = 10000; // Some book might take longer than this to renderer
describe('target-text', async () => {
let page;
let rendered;
beforeAll(async () => {
page = await loadPage('target/target-text/target-text.html')
return page.rendered;
}, TIMEOUT)
afterAll(async () => {
if (!DEBUG) {
await page.close();
}
})
it('Table of content should include chapter titles', async () => {
let text = await page.$eval("nav li:nth-of-type(1) a" , (r) => window.getComputedStyle(r, '::after').content);
expect(text).toContain("Lorem ipsum dolor sit amet");
})
it('Table of content should include chapter titles', async () => {
let text = await page.$eval("nav li:nth-of-type(2) a" , (r) => window.getComputedStyle(r, '::after').content);
expect(text).toContain("Lorem ipsum dolor sit amet");
})
it('Table of content should include first-letter of the chapter title', async () => {
let text = await page.$eval("nav li:nth-of-type(3) a" , (r) => window.getComputedStyle(r, '::after').content);
expect(text).toContain("P");
})
it('Table of content should include the content of the before pseudo element', async () => {
let text = await page.$eval("nav li:nth-of-type(4) a" , (r) => window.getComputedStyle(r, '::after').content);
expect(text).toContain("This is inside the before: ");
})
it('Table of content should include the content of the before pseudo element', async () => {
let text = await page.$eval("nav li:nth-of-type(5) a" , (r) => window.getComputedStyle(r, '::after').content);
expect(text).toContain(": This is inside the after");
})
if (!DEBUG) {
it('should create a pdf', async () => {
let pdf = await page.pdf(PDF_SETTINGS);
expect(pdf).toMatchPDFSnapshot(1);
})
}
}
)
import Handler from "../handler";
import { UUID, attr, querySelectorEscape } from "../../utils/utils";
import csstree from "css-tree";
import { nodeAfter } from "../../utils/dom";
class TargetText extends Handler {
constructor(chunker, polisher, caller) {
......@@ -8,11 +9,14 @@ class TargetText extends Handler {
this.styleSheet = polisher.styleSheet;
this.textTargets = {};
this.beforeContent = "";
this.afterContent = "";
this.selector = {};
}
onContent(funcNode, fItem, fList, declaration, rule) {
if (funcNode.name === "target-text") {
let selector = csstree.generate(rule.ruleNode.prelude);
this.selector = csstree.generate(rule.ruleNode.prelude);
let first = funcNode.children.first();
let last = funcNode.children.last();
let func = first.name;
......@@ -21,7 +25,7 @@ class TargetText extends Handler {
let args = [];
first.children.forEach((child) => {
first.children.forEach(child => {
if (child.type === "Identifier") {
args.push(child.name);
}
......@@ -34,14 +38,14 @@ class TargetText extends Handler {
let variable = "--pagedjs-" + UUID();
selector.split(",").forEach((s) => {
this.selector.split(",").forEach(s => {
this.textTargets[s] = {
func: func,
args: args,
value: value,
style: style || "content",
selector: s,
fullSelector: selector,
fullSelector: this.selector,
variable: variable
};
});
......@@ -57,8 +61,34 @@ class TargetText extends Handler {
}
}
// parse this on the ONCONTENT : get all before and after and replace the value with a variable
onPseudoSelector(pseudoNode, pItem, pList, selector, rule) {
// console.log(pseudoNode);
// console.log(rule);
rule.ruleNode.block.children.forEach(properties => {
if (pseudoNode.name === "before" && properties.property === "content") {
let beforeVariable = "--pagedjs-" + UUID();
let contenu = properties.value.children;
console.log(contenu);
contenu.forEach(prop => {
if (prop.type === "String") {
this.beforeContent = prop.value;
}
});
} else if (pseudoNode.name === "after" && properties.property === "content") {
let content = properties.value.children.forEach(prop => {
if (prop.type === "String") {
this.afterContent = prop.value;
}
});
}
});
}
afterParsed(fragment) {
Object.keys(this.textTargets).forEach((name) => {
Object.keys(this.textTargets).forEach(name => {
let target = this.textTargets[name];
let split = target.selector.split("::");
let query = split[0];
......@@ -68,29 +98,79 @@ class TargetText extends Handler {
let element = fragment.querySelector(querySelectorEscape(val));
if (element) {
if (target.style === "content") {
let selector = UUID();
selected.setAttribute("data-target-text", selector);
this.selector = UUID();
selected.setAttribute("data-target-text", this.selector);
let psuedo = "";
if (split.length > 1) {
psuedo += "::" + split[1];
}
let textContent = element.textContent.trim().replace(/["']/g, (match) => {
return "\\" + match;
}).replace(/[\n]/g, (match) => {
return "\\00000A";
});
let textContent = element.textContent
.trim()
.replace(/["']/g, match => {
return "\\" + match;
})
.replace(/[\n]/g, match => {
return "\\00000A";
});
// this.styleSheet.insertRule(`[data-target-text="${selector}"]${psuedo} { content: "${element.textContent}" }`, this.styleSheet.cssRules.length);
this.styleSheet.insertRule(`[data-target-text="${selector}"]${psuedo} { ${target.variable}: "${textContent}" }`, this.styleSheet.cssRules.length);
this.styleSheet.insertRule(`[data-target-text="${this.selector}"]${psuedo} { ${target.variable}: "${textContent}" }`);
}
// first-letter
else if (target.style === "first-letter") {
this.selector = UUID();
selected.setAttribute("data-target-text", this.selector);
let psuedo = "";
if (split.length > 1) {
psuedo += "::" + split[1];
}
let textContent = element.textContent
.trim()
.replace(/["']/g, match => {
return "\\" + match;
})
.replace(/[\n]/g, match => {
return "\\00000A";
});
this.styleSheet.insertRule(`[data-target-text="${this.selector}"]${psuedo} { ${target.variable}: "${textContent.charAt(0)}" }`);
}
// before
else if (target.style === "before") {
selected.setAttribute("data-target-text", this.selector);
let psuedo = "";
if (split.length > 1) {
psuedo += "::" + split[1];
}
let textContent = this.beforeContent.trim().replace(/["']/g, "");
this.styleSheet.insertRule(`[data-target-text="${this.selector}"]${psuedo} { ${target.variable}: "${textContent}" }`);
}
// after
else if (target.style === "after") {
selected.setAttribute("data-target-text", this.selector);
let psuedo = "";
if (split.length > 1) {
psuedo += "::" + split[1];
}
let textContent = this.afterContent.trim().replace(/["']/g, "");
this.styleSheet.insertRule(`[data-target-text="${this.selector}"]${psuedo} { ${target.variable}: "${textContent}" }`);
} else {
console.warn("missed target", val);
}
} else {
console.warn("missed target", val);
}
});
});
}
}
......
......@@ -15,6 +15,9 @@ class Polisher {
this.hooks.onRule = new Hook(this);
this.hooks.onDeclaration = new Hook(this);
this.hooks.onContent = new Hook(this);
this.hooks.onSelector = new Hook(this);
this.hooks.onPseudoSelector = new Hook(this);
this.hooks.onImport = new Hook(this);
this.hooks.beforeTreeParse = new Hook(this);
......
......@@ -14,6 +14,9 @@ class Sheet {
this.hooks.onAtMedia = new Hook(this);
this.hooks.onRule = new Hook(this);
this.hooks.onDeclaration = new Hook(this);
this.hooks.onSelector = new Hook(this);
this.hooks.onPseudoSelector = new Hook(this);
this.hooks.onContent = new Hook(this);
this.hooks.onImport = new Hook(this);
......@@ -65,6 +68,8 @@ class Sheet {
return this.ast;
}
insertRule(rule) {
let inserted = this.ast.children.appendData(rule);
inserted.forEach((item) => {
......@@ -114,6 +119,8 @@ class Sheet {
this.hooks.onRule.trigger(ruleNode, ruleItem, rulelist);
this.declarations(ruleNode, ruleItem, rulelist);
this.onSelector(ruleNode, ruleItem, rulelist);
}
});
}
......@@ -139,6 +146,30 @@ class Sheet {
});
}
// add pseudo elements to parser
onSelector(ruleNode, ruleItem, rulelist) {
csstree.walk(ruleNode, {
visit: "Selector",
enter: (selectNode, selectItem, selectList) => {
// console.log(selectNode);
this.hooks.onSelector.trigger(selectNode, selectItem, selectList, {ruleNode, ruleItem, rulelist});
if (selectNode.children.forEach(node => {if (node.type === "PseudoElementSelector") {
csstree.walk(node, {
visit: "PseudoElementSelector",
enter: (pseudoNode, pItem, pList) => {
this.hooks.onPseudoSelector.trigger(pseudoNode, pItem, pList, {selectNode, selectItem, selectList}, {ruleNode, ruleItem, rulelist});
}
});
}}));
// else {
// console.log("dommage");
// }
}
});
}
replaceUrls(ast) {
csstree.walk(ast, {
visit: "Url",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment