dante2-editor 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +33 -0
- data/Gemfile +25 -0
- data/Gemfile.lock +42 -0
- data/README.md +107 -0
- data/app/assets/index.html +55 -0
- data/app/assets/license.html +52 -0
- data/app/assets/options.html +57 -0
- data/app/assets/store.json +1 -0
- data/app/components/App.cjsx +1083 -0
- data/app/components/blocks/embed.cjsx +109 -0
- data/app/components/blocks/image.cjsx +316 -0
- data/app/components/blocks/placeholder.cjsx +60 -0
- data/app/components/blocks/video.cjsx +75 -0
- data/app/components/debug.cjsx +96 -0
- data/app/components/decorators/link.cjsx +61 -0
- data/app/components/popovers/addButton.cjsx +247 -0
- data/app/components/popovers/image.cjsx +160 -0
- data/app/components/popovers/link.cjsx +87 -0
- data/app/components/popovers/toolTip.cjsx +326 -0
- data/app/data/poc.js +15 -0
- data/app/demo.js +7 -0
- data/app/images/dante/media-loading-placeholder.png +0 -0
- data/app/images/site/dante-demo.png +0 -0
- data/app/images/site/dante-editor-logo.png +0 -0
- data/app/images/site/github-logo.png +0 -0
- data/app/initialize.js +4 -0
- data/app/model/index.js +194 -0
- data/app/styles/dante.scss +22 -0
- data/app/styles/dante/_animations.scss +54 -0
- data/app/styles/dante/_caption.scss +61 -0
- data/app/styles/dante/_debug.scss +124 -0
- data/app/styles/dante/_fonts.scss +17 -0
- data/app/styles/dante/_graf.scss +242 -0
- data/app/styles/dante/_icons.scss +62 -0
- data/app/styles/dante/_media.scss +39 -0
- data/app/styles/dante/_menu.scss +201 -0
- data/app/styles/dante/_needsorder.scss +209 -0
- data/app/styles/dante/_popover.scss +212 -0
- data/app/styles/dante/_post.scss +67 -0
- data/app/styles/dante/_scaffold.scss +24 -0
- data/app/styles/dante/_tooltip.scss +130 -0
- data/app/styles/dante/_utilities.scss +59 -0
- data/app/styles/dante/_variables.scss +96 -0
- data/app/styles/dante/blame.scss +246 -0
- data/app/styles/draft.css +297 -0
- data/app/styles/fonts/dante/dante.eot +0 -0
- data/app/styles/fonts/dante/dante.svg +18 -0
- data/app/styles/fonts/dante/dante.ttf +0 -0
- data/app/styles/fonts/dante/dante.woff +0 -0
- data/app/styles/fonts/dante/fontello.eot +0 -0
- data/app/styles/fonts/dante/fontello.svg +36 -0
- data/app/styles/fonts/dante/fontello.ttf +0 -0
- data/app/styles/fonts/dante/fontello.woff +0 -0
- data/app/styles/layout/layout.scss +64 -0
- data/app/styles/layout/normalize.css +375 -0
- data/app/styles/layout/scaffold.scss +8 -0
- data/app/styles/layout/tooltips.scss +216 -0
- data/app/utils/find_entities.coffee +20 -0
- data/app/utils/html2content.coffee +120 -0
- data/app/utils/logger.coffee +0 -0
- data/app/utils/save_content.coffee +63 -0
- data/app/utils/selection.js +53 -0
- data/config.ru +64 -0
- data/dante2.gemspec +19 -0
- data/docs/app.css +2 -0
- data/docs/app.css.map +1 -0
- data/docs/app.js +3 -0
- data/docs/app.js.map +1 -0
- data/docs/dante-vendors.js +28 -0
- data/docs/dante-vendors.js.map +1 -0
- data/docs/dante.css +2 -0
- data/docs/dante.css.map +1 -0
- data/docs/dante.js +4 -0
- data/docs/dante.js.map +1 -0
- data/docs/doc.html +57 -0
- data/docs/fonts/dante.eot +0 -0
- data/docs/fonts/dante.svg +18 -0
- data/docs/fonts/dante.ttf +0 -0
- data/docs/fonts/dante.woff +0 -0
- data/docs/fonts/fontello.eot +0 -0
- data/docs/fonts/fontello.svg +36 -0
- data/docs/fonts/fontello.ttf +0 -0
- data/docs/fonts/fontello.woff +0 -0
- data/docs/images/dante-editor-logo.png +0 -0
- data/docs/images/github-logo.png +0 -0
- data/docs/index.html +55 -0
- data/docs/license.html +52 -0
- data/lib/dante2-editor.rb +5 -0
- data/lib/dante2-editor/rails.rb +16 -0
- data/lib/dante2-editor/version.rb +5 -0
- data/package.json +61 -0
- data/rakefile +1 -0
- data/webpack.config.js +148 -0
- data/yarn.lock +4704 -0
- metadata +139 -0
@@ -0,0 +1,216 @@
|
|
1
|
+
/**
|
2
|
+
* Tooltips!
|
3
|
+
*/
|
4
|
+
|
5
|
+
$tooltip-arrow-size: 6px;
|
6
|
+
$tooltip-arrow-height: $tooltip-arrow-size * 2;
|
7
|
+
|
8
|
+
/* Base styles for the element that has a tooltip */
|
9
|
+
[data-tooltip],
|
10
|
+
.tooltip {
|
11
|
+
position: relative;
|
12
|
+
cursor: pointer;
|
13
|
+
}
|
14
|
+
|
15
|
+
/* Base styles for the entire tooltip */
|
16
|
+
[data-tooltip]:before,
|
17
|
+
[data-tooltip]:after,
|
18
|
+
.tooltip:before,
|
19
|
+
.tooltip:after {
|
20
|
+
position: absolute;
|
21
|
+
visibility: hidden;
|
22
|
+
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
23
|
+
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
|
24
|
+
opacity: 0;
|
25
|
+
-webkit-transition:
|
26
|
+
opacity 0.2s ease-in-out,
|
27
|
+
visibility 0.2s ease-in-out,
|
28
|
+
-webkit-transform 0.2s cubic-bezier(0.71, 1.7, 0.77, 1.24);
|
29
|
+
-moz-transition:
|
30
|
+
opacity 0.2s ease-in-out,
|
31
|
+
visibility 0.2s ease-in-out,
|
32
|
+
-moz-transform 0.2s cubic-bezier(0.71, 1.7, 0.77, 1.24);
|
33
|
+
transition:
|
34
|
+
opacity 0.2s ease-in-out,
|
35
|
+
visibility 0.2s ease-in-out,
|
36
|
+
transform 0.2s cubic-bezier(0.71, 1.7, 0.77, 1.24);
|
37
|
+
-webkit-transform: translate3d(0, 0, 0);
|
38
|
+
-moz-transform: translate3d(0, 0, 0);
|
39
|
+
transform: translate3d(0, 0, 0);
|
40
|
+
pointer-events: none;
|
41
|
+
}
|
42
|
+
|
43
|
+
/* Show the entire tooltip on hover and focus */
|
44
|
+
[data-tooltip]:hover:before,
|
45
|
+
[data-tooltip]:hover:after,
|
46
|
+
[data-tooltip]:focus:before,
|
47
|
+
[data-tooltip]:focus:after,
|
48
|
+
.tooltip:hover:before,
|
49
|
+
.tooltip:hover:after,
|
50
|
+
.tooltip:focus:before,
|
51
|
+
.tooltip:focus:after {
|
52
|
+
visibility: visible;
|
53
|
+
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
|
54
|
+
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
|
55
|
+
opacity: 1;
|
56
|
+
}
|
57
|
+
|
58
|
+
/* Base styles for the tooltip's directional arrow */
|
59
|
+
.tooltip:before,
|
60
|
+
[data-tooltip]:before {
|
61
|
+
z-index: 1001;
|
62
|
+
border: $tooltip-arrow-size solid transparent;
|
63
|
+
background: transparent;
|
64
|
+
content: "";
|
65
|
+
}
|
66
|
+
|
67
|
+
/* Base styles for the tooltip's content area */
|
68
|
+
.tooltip:after,
|
69
|
+
[data-tooltip]:after {
|
70
|
+
white-space: nowrap;
|
71
|
+
z-index: 1000;
|
72
|
+
padding: 7px 8px;
|
73
|
+
background-color: #000;
|
74
|
+
background-color: hsla(0, 0%, 20%, 0.9);
|
75
|
+
color: #fff;
|
76
|
+
content: attr(data-tooltip);
|
77
|
+
font-size: 12px;
|
78
|
+
line-height: 1.2;
|
79
|
+
border-radius: 3px;
|
80
|
+
}
|
81
|
+
|
82
|
+
/* Directions */
|
83
|
+
|
84
|
+
/* Top (default) */
|
85
|
+
[data-tooltip]:before,
|
86
|
+
[data-tooltip]:after,
|
87
|
+
.tooltip:before,
|
88
|
+
.tooltip:after,
|
89
|
+
.tooltip-top:before,
|
90
|
+
.tooltip-top:after {
|
91
|
+
bottom: 100%;
|
92
|
+
left: 50%;
|
93
|
+
}
|
94
|
+
|
95
|
+
[data-tooltip]:before,
|
96
|
+
.tooltip:before,
|
97
|
+
.tooltip-top:before {
|
98
|
+
margin-left: -$tooltip-arrow-size;
|
99
|
+
margin-bottom: -$tooltip-arrow-height;
|
100
|
+
border-top-color: #000;
|
101
|
+
border-top-color: hsla(0, 0%, 20%, 0.9);
|
102
|
+
}
|
103
|
+
|
104
|
+
/* Horizontally align top/bottom tooltips */
|
105
|
+
[data-tooltip]:after,
|
106
|
+
.tooltip:after,
|
107
|
+
.tooltip-top:after {
|
108
|
+
margin-left: -80px;
|
109
|
+
}
|
110
|
+
|
111
|
+
[data-tooltip]:hover:before,
|
112
|
+
[data-tooltip]:hover:after,
|
113
|
+
[data-tooltip]:focus:before,
|
114
|
+
[data-tooltip]:focus:after,
|
115
|
+
.tooltip:hover:before,
|
116
|
+
.tooltip:hover:after,
|
117
|
+
.tooltip:focus:before,
|
118
|
+
.tooltip:focus:after,
|
119
|
+
.tooltip-top:hover:before,
|
120
|
+
.tooltip-top:hover:after,
|
121
|
+
.tooltip-top:focus:before,
|
122
|
+
.tooltip-top:focus:after {
|
123
|
+
-webkit-transform: translateY(-$tooltip-arrow-height);
|
124
|
+
-moz-transform: translateY(-$tooltip-arrow-height);
|
125
|
+
transform: translateY(-$tooltip-arrow-height);
|
126
|
+
}
|
127
|
+
|
128
|
+
/* Left */
|
129
|
+
.tooltip-left:before,
|
130
|
+
.tooltip-left:after {
|
131
|
+
right: 100%;
|
132
|
+
bottom: 50%;
|
133
|
+
left: auto;
|
134
|
+
}
|
135
|
+
|
136
|
+
.tooltip-left:before {
|
137
|
+
margin-left: 0;
|
138
|
+
margin-right: -$tooltip-arrow-height;
|
139
|
+
margin-bottom: 0;
|
140
|
+
border-top-color: transparent;
|
141
|
+
border-left-color: #000;
|
142
|
+
border-left-color: hsla(0, 0%, 20%, 0.9);
|
143
|
+
}
|
144
|
+
|
145
|
+
.tooltip-left:hover:before,
|
146
|
+
.tooltip-left:hover:after,
|
147
|
+
.tooltip-left:focus:before,
|
148
|
+
.tooltip-left:focus:after {
|
149
|
+
-webkit-transform: translateX(-$tooltip-arrow-height);
|
150
|
+
-moz-transform: translateX(-$tooltip-arrow-height);
|
151
|
+
transform: translateX(-$tooltip-arrow-height);
|
152
|
+
}
|
153
|
+
|
154
|
+
/* Bottom */
|
155
|
+
.tooltip-bottom:before,
|
156
|
+
.tooltip-bottom:after {
|
157
|
+
top: 100%;
|
158
|
+
bottom: auto;
|
159
|
+
left: 50%;
|
160
|
+
}
|
161
|
+
|
162
|
+
.tooltip-bottom:before {
|
163
|
+
margin-top: -12px;
|
164
|
+
margin-bottom: 0;
|
165
|
+
border-top-color: transparent;
|
166
|
+
border-bottom-color: #000;
|
167
|
+
border-bottom-color: hsla(0, 0%, 20%, 0.9);
|
168
|
+
}
|
169
|
+
|
170
|
+
.tooltip-bottom:hover:before,
|
171
|
+
.tooltip-bottom:hover:after,
|
172
|
+
.tooltip-bottom:focus:before,
|
173
|
+
.tooltip-bottom:focus:after {
|
174
|
+
-webkit-transform: translateY($tooltip-arrow-height);
|
175
|
+
-moz-transform: translateY($tooltip-arrow-height);
|
176
|
+
transform: translateY($tooltip-arrow-height);
|
177
|
+
}
|
178
|
+
|
179
|
+
/* Right */
|
180
|
+
.tooltip-right:before,
|
181
|
+
.tooltip-right:after {
|
182
|
+
bottom: 50%;
|
183
|
+
left: 100%;
|
184
|
+
}
|
185
|
+
|
186
|
+
.tooltip-right:before {
|
187
|
+
margin-bottom: 0;
|
188
|
+
margin-left: -$tooltip-arrow-height;
|
189
|
+
border-top-color: transparent;
|
190
|
+
border-right-color: #000;
|
191
|
+
border-right-color: hsla(0, 0%, 20%, 0.9);
|
192
|
+
}
|
193
|
+
|
194
|
+
.tooltip-right:hover:before,
|
195
|
+
.tooltip-right:hover:after,
|
196
|
+
.tooltip-right:focus:before,
|
197
|
+
.tooltip-right:focus:after {
|
198
|
+
-webkit-transform: translateX($tooltip-arrow-height);
|
199
|
+
-moz-transform: translateX($tooltip-arrow-height);
|
200
|
+
transform: translateX($tooltip-arrow-height);
|
201
|
+
}
|
202
|
+
|
203
|
+
/* Move directional arrows down a bit for left/right tooltips */
|
204
|
+
.tooltip-left:before,
|
205
|
+
.tooltip-right:before {
|
206
|
+
top: 50%;
|
207
|
+
margin-top: -$tooltip-arrow-size;
|
208
|
+
bottom: auto;
|
209
|
+
}
|
210
|
+
|
211
|
+
/* Vertically center tooltip content for left/right tooltips */
|
212
|
+
.tooltip-left:after,
|
213
|
+
.tooltip-right:after {
|
214
|
+
margin-left: 0;
|
215
|
+
margin-bottom: -16px;
|
216
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{ Entity } = require('draft-js')
|
2
|
+
|
3
|
+
findEntities = (entityType, instance, contentBlock, callback)->
|
4
|
+
#TODO pass editor prop to link!!!!!
|
5
|
+
contentBlock.findEntityRanges (character) =>
|
6
|
+
entityKey = character.getEntity()
|
7
|
+
return (
|
8
|
+
res = entityKey isnt null &&
|
9
|
+
Entity.get(entityKey).getType() is entityType
|
10
|
+
if res
|
11
|
+
opts =
|
12
|
+
showPopLinkOver: instance.showPopLinkOver
|
13
|
+
hidePopLinkOver: instance.hidePopLinkOver
|
14
|
+
Entity.mergeData(entityKey, opts)
|
15
|
+
res
|
16
|
+
);
|
17
|
+
,
|
18
|
+
callback
|
19
|
+
|
20
|
+
module.exports = findEntities
|
@@ -0,0 +1,120 @@
|
|
1
|
+
{ ContentState
|
2
|
+
genKey
|
3
|
+
Entity
|
4
|
+
CharacterMetadata
|
5
|
+
ContentBlock
|
6
|
+
convertFromHTML
|
7
|
+
getSafeBodyFromHTML
|
8
|
+
} = require('draft-js')
|
9
|
+
|
10
|
+
{ List
|
11
|
+
OrderedSet
|
12
|
+
Repeat
|
13
|
+
fromJS
|
14
|
+
} = require('immutable')
|
15
|
+
|
16
|
+
|
17
|
+
# { compose
|
18
|
+
# } = require('underscore')
|
19
|
+
|
20
|
+
# underscore compose function
|
21
|
+
compose = ->
|
22
|
+
args = arguments
|
23
|
+
start = args.length - 1
|
24
|
+
->
|
25
|
+
i = start
|
26
|
+
result = args[start].apply(this, arguments)
|
27
|
+
while i--
|
28
|
+
result = args[i].call(this, result)
|
29
|
+
result
|
30
|
+
|
31
|
+
# from https://gist.github.com/N1kto/6702e1c2d89a33a15a032c234fc4c34e
|
32
|
+
|
33
|
+
###
|
34
|
+
# Helpers
|
35
|
+
###
|
36
|
+
|
37
|
+
# Prepares img meta data object based on img attributes
|
38
|
+
getBlockSpecForElement = (imgElement)=>
|
39
|
+
contentType: 'image',
|
40
|
+
imgSrc: imgElement.getAttribute('src')
|
41
|
+
|
42
|
+
# Wraps meta data in HTML element which is 'understandable' by Draft, I used <blockquote />.
|
43
|
+
wrapBlockSpec = (blockSpec)=>
|
44
|
+
if blockSpec == null
|
45
|
+
return null
|
46
|
+
|
47
|
+
tempEl = document.createElement('blockquote')
|
48
|
+
# stringify meta data and insert it as text content of temp HTML element. We will later extract
|
49
|
+
# and parse it.
|
50
|
+
tempEl.innerText = JSON.stringify(blockSpec)
|
51
|
+
return tempEl
|
52
|
+
|
53
|
+
# Replaces <img> element with our temp element
|
54
|
+
replaceElement = (oldEl, newEl)=>
|
55
|
+
if !(newEl instanceof HTMLElement)
|
56
|
+
return
|
57
|
+
|
58
|
+
upEl = getUpEl(oldEl)
|
59
|
+
#parentNode = oldEl.parentNode
|
60
|
+
#return parentNode.replaceChild(newEl, oldEl)
|
61
|
+
return upEl.parentNode.insertBefore(newEl, upEl);
|
62
|
+
|
63
|
+
getUpEl = (el)=>
|
64
|
+
original_el = el
|
65
|
+
while el.parentNode
|
66
|
+
if el.parentNode.tagName isnt 'BODY'
|
67
|
+
el = el.parentNode
|
68
|
+
return el if el.parentNode.tagName is 'BODY'
|
69
|
+
|
70
|
+
elementToBlockSpecElement = compose(wrapBlockSpec, getBlockSpecForElement)
|
71
|
+
|
72
|
+
imgReplacer = (imgElement)=>
|
73
|
+
replaceElement(imgElement, elementToBlockSpecElement(imgElement))
|
74
|
+
|
75
|
+
###
|
76
|
+
# Main function
|
77
|
+
###
|
78
|
+
|
79
|
+
# takes HTML string and returns DraftJS ContentState
|
80
|
+
customHTML2Content = (HTML, blockRn)->
|
81
|
+
tempDoc = new DOMParser().parseFromString(HTML, 'text/html')
|
82
|
+
# replace all <img /> with <blockquote /> elements
|
83
|
+
|
84
|
+
a = tempDoc.querySelectorAll('img').forEach( (item)->
|
85
|
+
return imgReplacer(item)
|
86
|
+
)
|
87
|
+
|
88
|
+
# use DraftJS converter to do initial conversion. I don't provide DOMBuilder and
|
89
|
+
# blockRenderMap arguments here since it should fall back to its default ones, which are fine
|
90
|
+
console.log tempDoc.body.innerHTML
|
91
|
+
contentBlocks = convertFromHTML(tempDoc.body.innerHTML,
|
92
|
+
getSafeBodyFromHTML,
|
93
|
+
blockRn
|
94
|
+
)
|
95
|
+
|
96
|
+
# now replace <blockquote /> ContentBlocks with 'atomic' ones
|
97
|
+
contentBlocks = contentBlocks.map (block)->
|
98
|
+
console.log "CHECK BLOCK", block.getType()
|
99
|
+
if (block.getType() isnt 'blockquote')
|
100
|
+
return block
|
101
|
+
|
102
|
+
json = ""
|
103
|
+
try
|
104
|
+
json = JSON.parse(block.getText())
|
105
|
+
catch
|
106
|
+
return block
|
107
|
+
|
108
|
+
newBlock = block.merge({
|
109
|
+
type: "image",
|
110
|
+
text: ""
|
111
|
+
data:
|
112
|
+
url: json.imgSrc
|
113
|
+
forceUpload: true
|
114
|
+
});
|
115
|
+
|
116
|
+
tempDoc = null
|
117
|
+
return ContentState.createFromBlockArray(contentBlocks)
|
118
|
+
|
119
|
+
|
120
|
+
module.exports = customHTML2Content
|
File without changes
|
@@ -0,0 +1,63 @@
|
|
1
|
+
axios = require("axios")
|
2
|
+
Immutable = require('immutable')
|
3
|
+
|
4
|
+
class SaveBehavior
|
5
|
+
constructor: (options)->
|
6
|
+
@getLocks = options.getLocks
|
7
|
+
@config = options.config
|
8
|
+
@editorContent = options.editorContent
|
9
|
+
@editorState = options.editorState
|
10
|
+
|
11
|
+
handleStore: (ev)->
|
12
|
+
@store()
|
13
|
+
|
14
|
+
store: (content)->
|
15
|
+
return unless @config.data_storage.url
|
16
|
+
return if @getLocks() > 0
|
17
|
+
|
18
|
+
clearTimeout(@timeout)
|
19
|
+
|
20
|
+
@timeout = setTimeout =>
|
21
|
+
@checkforStore(content)
|
22
|
+
, @config.data_storage.interval
|
23
|
+
|
24
|
+
getTextFromEditor: (content)->
|
25
|
+
content.blocks.map (o)=>
|
26
|
+
o.text
|
27
|
+
.join("\n")
|
28
|
+
|
29
|
+
getUrl: ->
|
30
|
+
url = @config.data_storage.url
|
31
|
+
if typeof(url) is "function" then url() else url
|
32
|
+
|
33
|
+
getMethod: ->
|
34
|
+
method = @config.data_storage.method
|
35
|
+
if typeof(method) is "function" then method() else method
|
36
|
+
|
37
|
+
|
38
|
+
checkforStore: (content)->
|
39
|
+
# ENTER DATA STORE
|
40
|
+
isChanged = !Immutable.is(Immutable.fromJS(@.editorContent), Immutable.fromJS(content))
|
41
|
+
# console.log("CONTENT CHANGED:", isChanged)
|
42
|
+
|
43
|
+
return unless isChanged
|
44
|
+
|
45
|
+
@config.xhr.before_handler() if @config.xhr.before_handler
|
46
|
+
# console.log "SAVING TO: #{@getMethod()} #{@getUrl()}"
|
47
|
+
|
48
|
+
axios
|
49
|
+
method: @getMethod()
|
50
|
+
url: @getUrl()
|
51
|
+
data:
|
52
|
+
editor_content: JSON.stringify(content)
|
53
|
+
text_content: @getTextFromEditor(content)
|
54
|
+
.then (result)=>
|
55
|
+
# console.log "STORING CONTENT", result
|
56
|
+
@config.data_storage.success_handler(result) if @config.data_storage.success_handler
|
57
|
+
@config.xhr.success_handler(result) if @config.xhr.success_handler
|
58
|
+
.catch (error)=>
|
59
|
+
# console.log("ERROR: got error saving content at #{@config.data_storage.url} - #{error}")
|
60
|
+
@config.xhr.failure_handler(error) if @config.xhr.failure_handler
|
61
|
+
|
62
|
+
|
63
|
+
module.exports = SaveBehavior
|
@@ -0,0 +1,53 @@
|
|
1
|
+
/*
|
2
|
+
Returns the `boundingClientRect` of the passed selection.
|
3
|
+
*/
|
4
|
+
export const getSelectionRect = (selected) => {
|
5
|
+
const _rect = selected.getRangeAt(0).getBoundingClientRect();
|
6
|
+
// selected.getRangeAt(0).getBoundingClientRect()
|
7
|
+
let rect = _rect && _rect.top ? _rect : selected.getRangeAt(0).getClientRects()[0];
|
8
|
+
if (!rect) {
|
9
|
+
if (selected.anchorNode && selected.anchorNode.getBoundingClientRect) {
|
10
|
+
rect = selected.anchorNode.getBoundingClientRect();
|
11
|
+
rect.isEmptyline = true;
|
12
|
+
} else {
|
13
|
+
return null;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
return rect;
|
17
|
+
};
|
18
|
+
|
19
|
+
/*
|
20
|
+
Returns the native selection node.
|
21
|
+
*/
|
22
|
+
export const getSelection = (root) => {
|
23
|
+
let t = null;
|
24
|
+
if (root.getSelection) {
|
25
|
+
t = root.getSelection();
|
26
|
+
} else if (root.document.getSelection) {
|
27
|
+
t = root.document.getSelection();
|
28
|
+
} else if (root.document.selection) {
|
29
|
+
t = root.document.selection.createRange().text;
|
30
|
+
}
|
31
|
+
return t;
|
32
|
+
};
|
33
|
+
|
34
|
+
/*
|
35
|
+
Recursively finds the DOM Element of the block where the cursor is currently present.
|
36
|
+
If not found, returns null.
|
37
|
+
*/
|
38
|
+
export const getSelectedBlockNode = (root) => {
|
39
|
+
const selection = root.getSelection();
|
40
|
+
if (selection.rangeCount === 0) {
|
41
|
+
return null;
|
42
|
+
}
|
43
|
+
let node = selection.getRangeAt(0).startContainer;
|
44
|
+
// console.log(node);
|
45
|
+
do {
|
46
|
+
if (node.getAttribute && node.getAttribute('data-block') === 'true') {
|
47
|
+
return node;
|
48
|
+
}
|
49
|
+
node = node.parentNode;
|
50
|
+
// console.log(node);
|
51
|
+
} while (node !== null);
|
52
|
+
return null;
|
53
|
+
};
|