-
Notifications
You must be signed in to change notification settings - Fork 0
/
split.js
123 lines (99 loc) · 3.26 KB
/
split.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { dom, article } from '/app/index.js';
import Plugin from '/app/src/plugin.js'
// N.B. for .split-lines this completely resets contents after a resize so
// might cause compatibility issues with other plugins
export class Split extends Plugin {
constructor(args) {
super(args);
let classes = this.el.className.split(' ');
this.delimiter = classes.find(c => /^split-/.test(c)).slice(6);
// line breaks can change on resize
if (this.delimiter === 'lines') {
this.originalHTML = this.el.innerHTML;
this.bind({resize: 'resize'});
}
// split(this.el, this.delimiter)
setTimeout(() => split(this.el, this.delimiter), 100);
}
resize() {
this.el.innerHTML = this.originalHTML;
split(this.el, this.delimiter);
}
};
article.register('[class*=" split-"], [class^="split-"]', Split);
export default function split(el, delimiter) {
if (delimiter === 'words') {
dom.textNodes(el).forEach(text => {
let pos = text.data.search(/\w[\s-]/);
while (pos >= 0) {
let remaining = text.splitText(pos + 1);
let word = dom.create('<span class=word>');
text.parentNode.insertBefore(word, text);
word.appendChild(text);
text = remaining
pos = text.data.search(/[\s-]/);
}
});
}
else if (delimiter === 'lines') {
let range = document.createRange();
let moreThanOneLine = () => {
let rects = range.getClientRects();
if (rects.length === 1)
return false;
else {
let last = rects[rects.length - 1];
return rects[0].bottom < last.top;
}
}
let makeLine = () => {
if (!/[^\s]/.test(range.toString()))
return;
let line = dom.create('<span class=line>');
//console.log('line created', line, range.toString());
line.appendChild(range.extractContents());
range.insertNode(line)
range.collapse();
}
// gets called recusively for every block element
let splitBlockElement = el => {
range.setStart(el, 0);
Array.from(el.childNodes).forEach((node, i) => {
if (node instanceof Element &&
dom(node).css('display') === 'block') {
splitBlockElement(node);
range.setEnd(el, i);
range.collapse();
}
else {
// called recursively for all inline elements and text nodes
let splitNode = node => {
// it's an element, so recurse with childNodes
if (node.nodeType === 1)
Array.from(node.childNodes).forEach(splitNode);
// text node, where real work gets done
else if (node.nodeType === 3) {
let re = /[^\s]($|[\s-])/g;
let lastPos = 0;
let result;
while (result = re.exec(node.data)) {
let pos = Math.min(node.data.length, result.index + 1);
range.setEnd(node, pos);
if (moreThanOneLine()) {
range.setEnd(node, lastPos);
makeLine();
re.lastIndex = 0;
}
lastPos = pos;
}
}
};
splitNode(node);
}
});
// finish existing line
makeLine();
};
splitBlockElement(el);
}
}