dante2-editor 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
};
|