Skip to content

Commit 35d12e3

Browse files
authored
Add support for straddled paragraphs
Add support for straddled paragraphs See: <https://html.spec.whatwg.org/multipage/dom.html#paragraphs>. Closes GH-34. Closes GH-51. Reviewed-by: Christian Murphy <[email protected]>
1 parent 72449a7 commit 35d12e3

File tree

6 files changed

+238
-17
lines changed

6 files changed

+238
-17
lines changed

lib/handlers/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,13 @@ exports.frameset = ignore
5555
exports.isindex = ignore
5656
exports.keygen = ignore
5757
exports.link = ignore
58-
exports.map = ignore
5958
exports.math = ignore
6059
exports.menu = ignore
6160
exports.menuitem = ignore
6261
exports.meta = ignore
6362
exports.nextid = ignore
6463
exports.noembed = ignore
6564
exports.noframes = ignore
66-
exports.object = ignore
6765
exports.optgroup = ignore
6866
exports.option = ignore
6967
exports.param = ignore
@@ -92,10 +90,12 @@ exports.dfn = all
9290
exports.font = all
9391
exports.ins = all
9492
exports.label = all
93+
exports.map = all
9594
exports.marquee = all
9695
exports.meter = all
9796
exports.nobr = all
9897
exports.noscript = all
98+
exports.object = all
9999
exports.output = all
100100
exports.progress = all
101101
exports.rb = all

lib/util/wrap.js

+108-15
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,39 @@ module.exports = wrap
44

55
wrap.needed = needed
66

7+
var extend = require('extend')
78
var phrasing = require('mdast-util-phrasing')
89

9-
// Wrap all runs of mdast phrasing content in `paragraph` nodes.
1010
function wrap(nodes) {
11+
return runs(nodes, onphrasing)
12+
13+
function onphrasing(nodes) {
14+
var head = nodes[0]
15+
16+
if (
17+
nodes.length === 1 &&
18+
head.type === 'text' &&
19+
(head.value === ' ' || head.value === '\n')
20+
) {
21+
return []
22+
}
23+
24+
return {type: 'paragraph', children: nodes}
25+
}
26+
}
27+
28+
// Wrap all runs of mdast phrasing content in `paragraph` nodes.
29+
function runs(nodes, onphrasing, onnonphrasing) {
30+
var nonphrasing = onnonphrasing || identity
1131
var result = []
12-
var length = nodes.length
32+
var flattened = flatten(nodes)
33+
var length = flattened.length
1334
var index = -1
1435
var node
1536
var queue
1637

1738
while (++index < length) {
18-
node = nodes[index]
39+
node = flattened[index]
1940

2041
if (phrasing(node)) {
2142
if (queue === undefined) {
@@ -24,42 +45,114 @@ function wrap(nodes) {
2445

2546
queue.push(node)
2647
} else {
27-
flush()
28-
result.push(node)
48+
add()
49+
result = result.concat(nonphrasing(node))
2950
}
3051
}
3152

32-
flush()
53+
add()
3354

3455
return result
3556

36-
function flush() {
57+
function add() {
3758
if (queue !== undefined) {
38-
if (
39-
queue.length !== 1 ||
40-
queue[0].type !== 'text' ||
41-
(queue[0].value !== ' ' && queue[0].value !== '\n')
42-
) {
43-
result.push({type: 'paragraph', children: queue})
44-
}
59+
result = result.concat(onphrasing(queue))
4560
}
4661

4762
queue = undefined
4863
}
4964
}
5065

66+
// Flatten a list of nodes.
67+
function flatten(nodes) {
68+
var length = nodes.length
69+
var index = -1
70+
var flattened = []
71+
var node
72+
73+
while (++index < length) {
74+
node = nodes[index]
75+
76+
// Straddling: some elements are *weird*.
77+
// Namely: `map`, `ins`, `del`, and `a`, as they are hybrid elements.
78+
// See: <https://html.spec.whatwg.org/#paragraphs>.
79+
// Paragraphs are the weirdest of them all.
80+
// See the straddling fixture for more info!
81+
// `ins` is ignored in mdast, so we don’t need to worry about that.
82+
// `map` maps to its content, so we don’t need to worry about that either.
83+
// `del` maps to `delete` and `a` to `link`, so we do handle those.
84+
// What we’ll do is split `node` over each of its children.
85+
if (
86+
(node.type === 'delete' || node.type === 'link') &&
87+
needed(node.children)
88+
) {
89+
flattened = flattened.concat(split(node))
90+
} else {
91+
flattened.push(node)
92+
}
93+
}
94+
95+
return flattened
96+
}
97+
5198
// Check if there are non-phrasing mdast nodes returned.
5299
// This is needed if a fragment is given, which could just be a sentence, and
53100
// doesn’t need a wrapper paragraph.
54101
function needed(nodes) {
55102
var length = nodes.length
56103
var index = -1
104+
var node
105+
var children
57106

58107
while (++index < length) {
59-
if (!phrasing(nodes[index])) {
108+
node = nodes[index]
109+
children = node.children
110+
111+
if (!phrasing(node) || (children && needed(children))) {
60112
return true
61113
}
62114
}
63115

64116
return false
65117
}
118+
119+
function split(node) {
120+
return runs(node.children, onphrasing, onnonphrasing)
121+
122+
// Use `child`, add `parent` as its first child, put the original children
123+
// into `parent`.
124+
function onnonphrasing(child) {
125+
var parent = extend(true, {}, shallow(node))
126+
var copy = shallow(child)
127+
128+
copy.children = [parent]
129+
parent.children = child.children
130+
131+
return copy
132+
}
133+
134+
// Use `parent`, put the phrasing run inside it.
135+
function onphrasing(nodes) {
136+
var parent = extend(true, {}, shallow(node))
137+
parent.children = nodes
138+
return parent
139+
}
140+
}
141+
142+
// Shallow copy of a node, excluding its children.
143+
function shallow(node) {
144+
var copy = {}
145+
var key
146+
147+
for (key in node) {
148+
if (key !== 'children') {
149+
copy[key] = node[key]
150+
}
151+
}
152+
153+
return copy
154+
}
155+
156+
function identity(n) {
157+
return n
158+
}

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"lib"
2424
],
2525
"dependencies": {
26+
"extend": "^3.0.2",
2627
"hast-util-has-property": "^1.0.0",
2728
"hast-util-is-element": "^1.0.0",
2829
"hast-util-to-text": "^1.0.0",

test/fixtures/straddling/index.html

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<h1>Straddling</h1>
2+
3+
<h2>Example of paragraphs</h2>
4+
This is the <em>first</em> paragraph in this example.
5+
<p>This is the second.</p>
6+
7+
<ins><h2>Example of paragraphs with ins and del</h2>
8+
This is the <em>first</em> paragraph in</ins> this example<del>.
9+
<p>This is the second.</p></del>
10+
11+
<h2>Implicit paragraphs</h2>
12+
13+
<header>
14+
Welcome!
15+
<a href=about.html>
16+
This is home of...
17+
<h1>The Falcons!</h1>
18+
The Lockheed Martin multirole jet fighter aircraft!
19+
</a>
20+
This page discusses the F-16 Fighting Falcon’s innermost secrets.
21+
</header>
22+
23+
<h2>Even harder implicit paragraphs</h2>
24+
25+
Alpha!
26+
<a href=about.html>
27+
Bravo <em>emphasis</em> charlie <strong>importance</strong>...
28+
<h1>delta!</h1>
29+
</a>
30+
31+
<h2>Explicit paragraphs</h2>
32+
33+
<header>
34+
<p>Welcome! <a href=about.html>This is home of...</a></p>
35+
<h1><a href=about.html>The Falcons!</a></h1>
36+
<p><a href=about.html>The Lockheed Martin multirole jet
37+
fighter aircraft!</a> This page discusses the F-16 Fighting
38+
Falcon’s innermost secrets.</p>
39+
</header>
40+
41+
<section>
42+
<h2>Overlap implicit</h2>
43+
You can play with my cat simulator.
44+
<object data=cats.sim>
45+
To see the cat simulator, use one of the following links:
46+
<ul>
47+
<li><a href=cats.sim>Download simulator file</a>
48+
<li><a href="https://sims.example.com/watch?v=LYds5xY4INU">Use online simulator</a>
49+
</ul>
50+
Alternatively, upgrade to the Mellblom Browser.
51+
</object>
52+
I’m quite proud of it.
53+
</section>
54+
55+
<section>
56+
<h2>Overlap explicit</h2>
57+
<p>You can play with my cat simulator.</p>
58+
<object data=cats.sim>
59+
<p>To see the cat simulator, use one of the following links:</p>
60+
<ul>
61+
<li><a href=cats.sim>Download simulator file</a>
62+
<li><a href="https://sims.example.com/watch?v=LYds5xY4INU">Use online simulator</a>
63+
</ul>
64+
<p>Alternatively, upgrade to the Mellblom Browser.</p>
65+
</object>
66+
<p>I’m quite proud of it.</p>
67+
</section>

test/fixtures/straddling/index.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"fragment": true
3+
}

test/fixtures/straddling/index.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Straddling
2+
3+
## Example of paragraphs
4+
5+
This is the _first_ paragraph in this example.
6+
7+
This is the second.
8+
9+
## Example of paragraphs with ins and del
10+
11+
This is the _first_ paragraph in this example~~.~~
12+
13+
~~This is the second.~~
14+
15+
## Implicit paragraphs
16+
17+
Welcome! [This is home of...](about.html)
18+
19+
# [The Falcons!](about.html)
20+
21+
[The Lockheed Martin multirole jet fighter aircraft!](about.html) This page discusses the F-16 Fighting Falcon’s innermost secrets.
22+
23+
## Even harder implicit paragraphs
24+
25+
Alpha! [Bravo _emphasis_ charlie **importance**...](about.html)
26+
27+
# [delta!](about.html)
28+
29+
## Explicit paragraphs
30+
31+
Welcome! [This is home of...](about.html)
32+
33+
# [The Falcons!](about.html)
34+
35+
[The Lockheed Martin multirole jet fighter aircraft!](about.html) This page discusses the F-16 Fighting Falcon’s innermost secrets.
36+
37+
## Overlap implicit
38+
39+
You can play with my cat simulator. To see the cat simulator, use one of the following links:
40+
41+
- [Download simulator file](cats.sim)
42+
- [Use online simulator](https://sims.example.com/watch?v=LYds5xY4INU)
43+
44+
Alternatively, upgrade to the Mellblom Browser. I’m quite proud of it.
45+
46+
## Overlap explicit
47+
48+
You can play with my cat simulator.
49+
50+
To see the cat simulator, use one of the following links:
51+
52+
- [Download simulator file](cats.sim)
53+
- [Use online simulator](https://sims.example.com/watch?v=LYds5xY4INU)
54+
55+
Alternatively, upgrade to the Mellblom Browser.
56+
57+
I’m quite proud of it.

0 commit comments

Comments
 (0)