-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
location_mapper.coffee
156 lines (122 loc) · 4.24 KB
/
location_mapper.coffee
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
{elementContainsNode, findChildIndexOfNode, findClosestElementFromNode, findNodeFromContainerAndOffset,
nodeIsBlockStart, nodeIsBlockStartComment, nodeIsBlockContainer, nodeIsCursorTarget,
nodeIsEmptyTextNode, nodeIsTextNode, nodeIsAttachmentElement, tagName, walkTree} = Trix
class Trix.LocationMapper
constructor: (@element) ->
findLocationFromContainerAndOffset: (container, offset, {strict} = strict: true) ->
childIndex = 0
foundBlock = false
location = index: 0, offset: 0
if attachmentElement = @findAttachmentElementParentForNode(container)
container = attachmentElement.parentNode
offset = findChildIndexOfNode(attachmentElement)
walker = walkTree(@element, usingFilter: rejectAttachmentContents)
while walker.nextNode()
node = walker.currentNode
if node is container and nodeIsTextNode(container)
unless nodeIsCursorTarget(node)
location.offset += offset
break
else
if node.parentNode is container
break if childIndex++ is offset
else unless elementContainsNode(container, node)
break if childIndex > 0
if nodeIsBlockStart(node, {strict})
location.index++ if foundBlock
location.offset = 0
foundBlock = true
else
location.offset += nodeLength(node)
location
findContainerAndOffsetFromLocation: (location) ->
if location.index is 0 and location.offset is 0
container = @element
offset = 0
while container.firstChild
container = container.firstChild
if nodeIsBlockContainer(container)
offset = 1
break
return [container, offset]
[node, nodeOffset] = @findNodeAndOffsetFromLocation(location)
return unless node
if nodeIsTextNode(node)
container = node
string = node.textContent
offset = location.offset - nodeOffset
else
container = node.parentNode
unless nodeIsBlockStart(node.previousSibling)
unless nodeIsBlockContainer(container)
while node is container.lastChild
node = container
container = container.parentNode
break if nodeIsBlockContainer(container)
offset = findChildIndexOfNode(node)
offset++ unless location.offset is 0
[container, offset]
findNodeAndOffsetFromLocation: (location) ->
offset = 0
for currentNode in @getSignificantNodesForIndex(location.index)
length = nodeLength(currentNode)
if location.offset <= offset + length
if nodeIsTextNode(currentNode)
node = currentNode
nodeOffset = offset
break if location.offset is nodeOffset and nodeIsCursorTarget(node)
else if not node
node = currentNode
nodeOffset = offset
offset += length
break if offset > location.offset
[node, nodeOffset]
# Private
findAttachmentElementParentForNode: (node) ->
while node and node isnt @element
return node if nodeIsAttachmentElement(node)
node = node.parentNode
getSignificantNodesForIndex: (index) ->
nodes = []
walker = walkTree(@element, usingFilter: acceptSignificantNodes)
recordingNodes = false
while walker.nextNode()
node = walker.currentNode
if nodeIsBlockStartComment(node)
if blockIndex?
blockIndex++
else
blockIndex = 0
if blockIndex is index
recordingNodes = true
else if recordingNodes
break
else if recordingNodes
nodes.push(node)
nodes
nodeLength = (node) ->
if node.nodeType is Node.TEXT_NODE
if nodeIsCursorTarget(node)
0
else
string = node.textContent
string.length
else if tagName(node) is "br" or nodeIsAttachmentElement(node)
1
else
0
acceptSignificantNodes = (node) ->
if rejectEmptyTextNodes(node) is NodeFilter.FILTER_ACCEPT
rejectAttachmentContents(node)
else
NodeFilter.FILTER_REJECT
rejectEmptyTextNodes = (node) ->
if nodeIsEmptyTextNode(node)
NodeFilter.FILTER_REJECT
else
NodeFilter.FILTER_ACCEPT
rejectAttachmentContents = (node) ->
if nodeIsAttachmentElement(node.parentNode)
NodeFilter.FILTER_REJECT
else
NodeFilter.FILTER_ACCEPT