dante-editor-seo 0.0.13
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 +40 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +140 -0
- data/Procfile +1 -0
- data/README.md +187 -0
- data/ROADMAP.md +10 -0
- data/TODO.md +30 -0
- data/app/assets/fonts/dante/dante.eot +0 -0
- data/app/assets/fonts/dante/dante.svg +14 -0
- data/app/assets/fonts/dante/dante.ttf +0 -0
- data/app/assets/fonts/dante/dante.woff +0 -0
- data/app/assets/fonts/dante/fontello.eot +0 -0
- data/app/assets/fonts/dante/fontello.svg +36 -0
- data/app/assets/fonts/dante/fontello.ttf +0 -0
- data/app/assets/fonts/dante/fontello.woff +0 -0
- data/app/assets/images/dante/media-loading-placeholder.png +0 -0
- data/app/assets/javascripts/dante/dante.js.coffee.erb +10 -0
- data/app/assets/javascripts/dante/editor.js.coffee +1250 -0
- data/app/assets/javascripts/dante/menu.js.coffee +216 -0
- data/app/assets/javascripts/dante/popover.js.coffee +75 -0
- data/app/assets/javascripts/dante/tooltip.js.coffee +82 -0
- data/app/assets/javascripts/dante/tooltip_widget.js.coffee +10 -0
- data/app/assets/javascripts/dante/tooltip_widgets/embed.js.coffee +60 -0
- data/app/assets/javascripts/dante/tooltip_widgets/extract.js.coffee +64 -0
- data/app/assets/javascripts/dante/tooltip_widgets/uploader.js.coffee +248 -0
- data/app/assets/javascripts/dante/utils.js.coffee +235 -0
- data/app/assets/javascripts/dante/view.js.coffee +101 -0
- data/app/assets/javascripts/dante.js +12 -0
- data/app/assets/stylesheets/dante/_animations.scss +54 -0
- data/app/assets/stylesheets/dante/_caption.scss +52 -0
- data/app/assets/stylesheets/dante/_debug.scss +11 -0
- data/app/assets/stylesheets/dante/_fonts.scss +17 -0
- data/app/assets/stylesheets/dante/_graf.scss +238 -0
- data/app/assets/stylesheets/dante/_icons.scss +57 -0
- data/app/assets/stylesheets/dante/_media.scss +39 -0
- data/app/assets/stylesheets/dante/_menu.scss +153 -0
- data/app/assets/stylesheets/dante/_needsorder.scss +209 -0
- data/app/assets/stylesheets/dante/_popover.scss +134 -0
- data/app/assets/stylesheets/dante/_post.scss +69 -0
- data/app/assets/stylesheets/dante/_scaffold.scss +20 -0
- data/app/assets/stylesheets/dante/_tooltip.scss +131 -0
- data/app/assets/stylesheets/dante/_utilities.scss +55 -0
- data/app/assets/stylesheets/dante/_variables.scss +46 -0
- data/app/assets/stylesheets/dante.scss +18 -0
- data/bower.json +44 -0
- data/config.rb +86 -0
- data/config.ru +42 -0
- data/dante-editor.gemspec +19 -0
- data/dist/css/dante-editor.css +1116 -0
- data/dist/fonts/dante/dante.eot +0 -0
- data/dist/fonts/dante/dante.svg +14 -0
- data/dist/fonts/dante/dante.ttf +0 -0
- data/dist/fonts/dante/dante.woff +0 -0
- data/dist/fonts/dante/fontello.eot +0 -0
- data/dist/fonts/dante/fontello.svg +36 -0
- data/dist/fonts/dante/fontello.ttf +0 -0
- data/dist/fonts/dante/fontello.woff +0 -0
- data/dist/images/dante/media-loading-placeholder.png +0 -0
- data/dist/js/dante-editor.js +2878 -0
- data/lib/dante-editor/rails.rb +4 -0
- data/lib/dante-editor/version.rb +5 -0
- data/lib/dante-editor.rb +5 -0
- data/license.md +22 -0
- data/rakefile +2 -0
- data/source/assets/images/dante-editor-logo.png +0 -0
- data/source/assets/images/github-logo.png +0 -0
- data/source/assets/javascripts/all.js +3 -0
- data/source/assets/javascripts/dante-editor.js +1 -0
- data/source/assets/javascripts/deps.js +4 -0
- data/source/assets/javascripts/examples/custom_toolbar.js.coffee +30 -0
- data/source/assets/javascripts/spec.js +2 -0
- data/source/assets/javascripts/specs/cleaner.js.coffee +8 -0
- data/source/assets/javascripts/specs/dante_view.js.coffee +74 -0
- data/source/assets/javascripts/specs/editor.js.coffee +78 -0
- data/source/assets/stylesheets/_layout.scss +51 -0
- data/source/assets/stylesheets/_scaffold.scss +8 -0
- data/source/assets/stylesheets/_tooltips.scss +216 -0
- data/source/assets/stylesheets/all.css.scss +5 -0
- data/source/assets/stylesheets/dante-editor.css.scss +1 -0
- data/source/assets/stylesheets/normalize.css +375 -0
- data/source/custom_toolbar.erb +29 -0
- data/source/embeds.html.erb +27 -0
- data/source/icons/dante.json +143 -0
- data/source/icons/embed.svg +13 -0
- data/source/icons/image.svg +13 -0
- data/source/icons/plus.svg +13 -0
- data/source/icons/video.svg +13 -0
- data/source/index.html.erb +18 -0
- data/source/layouts/layout.erb +26 -0
- data/source/layouts/spec.html.erb +22 -0
- data/source/lists.html.erb +18 -0
- data/source/partials/_content.erb +6 -0
- data/source/partials/_example_1.erb +45 -0
- data/source/partials/_example_2.erb +32 -0
- data/source/partials/_example_3.erb +4 -0
- data/source/partials/_lists.erb +13 -0
- data/source/partials/_readme.markdown +24 -0
- data/source/partials/test/_example_1.erb +39 -0
- data/source/tests/dante_view.html.erb +11 -0
- data/source/tests/index.html.erb +39 -0
- data/tmp/.gitkeep +0 -0
- metadata +151 -0
@@ -0,0 +1,248 @@
|
|
1
|
+
utils = Dante.utils
|
2
|
+
|
3
|
+
class Dante.View.TooltipWidget.Uploader extends Dante.View.TooltipWidget
|
4
|
+
|
5
|
+
initialize: (opts={})->
|
6
|
+
#super
|
7
|
+
#@name = "menu-image"
|
8
|
+
@icon = opts.icon || "icon-image"
|
9
|
+
@title = opts.title || "Add an image"
|
10
|
+
@action = opts.action || "menu-image"
|
11
|
+
@current_editor = opts.current_editor
|
12
|
+
|
13
|
+
handleClick: (ev)->
|
14
|
+
@imageSelect(ev)
|
15
|
+
|
16
|
+
insertTemplate: ()->
|
17
|
+
"<figure contenteditable='false' class='graf graf--figure is-defaultValue' name='#{utils.generateUniqueName()}' tabindex='0'>
|
18
|
+
<div style='' class='aspectRatioPlaceholder is-locked'>
|
19
|
+
<div style='padding-bottom: 100%;' class='aspect-ratio-fill'></div>
|
20
|
+
<img src='' data-height='' data-width='' data-image-id='' class='graf-image' data-delayed-src=''>
|
21
|
+
</div>
|
22
|
+
<figcaption contenteditable='true' data-default-value='Type caption for image (optional)' class='imageCaption'>
|
23
|
+
<span class='defaultValue'>Type caption for image (optional)</span>
|
24
|
+
<br>
|
25
|
+
</figcaption>
|
26
|
+
</figure>"
|
27
|
+
|
28
|
+
#UPLOADER
|
29
|
+
#replace existing img tag , and wrap it in insertTamplate
|
30
|
+
#TODO: take the url and upload it
|
31
|
+
uploadExistentImage: (image_element, opts = {})->
|
32
|
+
|
33
|
+
utils.log ("process image here!")
|
34
|
+
tmpl = $(@insertTemplate())
|
35
|
+
tmpl.find("img").attr('src', @current_editor.default_loading_placeholder )
|
36
|
+
#is a child element or a first level element ?
|
37
|
+
|
38
|
+
if $(image_element).parents(".graf").length > 0
|
39
|
+
#return if its already wrapped in graf--figure
|
40
|
+
if $(image_element).parents(".graf").hasClass("graf--figure")
|
41
|
+
return
|
42
|
+
utils.log "UNO"
|
43
|
+
tmpl.insertBefore( $(image_element).parents(".graf") )
|
44
|
+
node = @current_editor.getNode()
|
45
|
+
if node
|
46
|
+
@current_editor.preCleanNode($(node))
|
47
|
+
@current_editor.addClassesToElement(node)
|
48
|
+
else
|
49
|
+
utils.log "DOS"
|
50
|
+
img = $(image_element).parentsUntil(".section-inner").first()
|
51
|
+
$(img).replaceWith(tmpl)
|
52
|
+
|
53
|
+
utils.log $("[name='#{tmpl.attr('name')}']").attr("name")
|
54
|
+
@replaceImg(image_element, $("[name='#{tmpl.attr('name')}']"))
|
55
|
+
|
56
|
+
#in case we found that graf--image is nested element, unwrap * nested times
|
57
|
+
n = $("[name='#{tmpl.attr('name')}']").parentsUntil(".section-inner").length
|
58
|
+
unless n is 0
|
59
|
+
for i in [0..n-1] by 1
|
60
|
+
$("[name='#{tmpl.attr('name')}']").unwrap()
|
61
|
+
|
62
|
+
utils.log "FIG"
|
63
|
+
#utils.log $("[name='#{tmpl.attr('name')}']").attr("name")
|
64
|
+
|
65
|
+
replaceImg: (image_element, figure)->
|
66
|
+
utils.log figure.attr("name")
|
67
|
+
utils.log figure
|
68
|
+
$(image_element).remove()
|
69
|
+
img = new Image()
|
70
|
+
img.src = image_element.src
|
71
|
+
self = this
|
72
|
+
img.onload = ()->
|
73
|
+
utils.log "replace image with loaded info"
|
74
|
+
utils.log figure.attr("name")
|
75
|
+
utils.log(this.width + 'x' + this.height);
|
76
|
+
|
77
|
+
ar = self.getAspectRatio(this.width, this.height)
|
78
|
+
#debugger
|
79
|
+
figure.find(".aspectRatioPlaceholder").css
|
80
|
+
'max-width': ar.width
|
81
|
+
'max-height': ar.height
|
82
|
+
|
83
|
+
figure.find(".graf-image").attr
|
84
|
+
"data-height": this.height
|
85
|
+
"data-width": this.width
|
86
|
+
|
87
|
+
figure.find(".aspect-ratio-fill").css
|
88
|
+
"padding-bottom": "#{ar.ratio}%"
|
89
|
+
|
90
|
+
#TODO: upload file to server
|
91
|
+
#@uploadFile file, replaced_node
|
92
|
+
|
93
|
+
figure.find("img").attr("src", image_element.src)
|
94
|
+
|
95
|
+
displayAndUploadImages: (file)->
|
96
|
+
@displayCachedImage file
|
97
|
+
|
98
|
+
imageSelect: (ev)->
|
99
|
+
$selectFile = $('<input type="file" multiple="multiple">').click()
|
100
|
+
self = @
|
101
|
+
$selectFile.change ()->
|
102
|
+
t = this
|
103
|
+
self.uploadFiles(t.files)
|
104
|
+
|
105
|
+
displayCachedImage: (file)->
|
106
|
+
@current_editor.tooltip_view.hide()
|
107
|
+
|
108
|
+
reader = new FileReader()
|
109
|
+
reader.onload = (e)=>
|
110
|
+
img = new Image
|
111
|
+
img.src = e.target.result
|
112
|
+
node = @current_editor.getNode()
|
113
|
+
self = this
|
114
|
+
img.onload = ()->
|
115
|
+
new_tmpl = $(self.insertTemplate())
|
116
|
+
|
117
|
+
replaced_node = $( new_tmpl ).insertBefore($(node))
|
118
|
+
|
119
|
+
img_tag = new_tmpl.find('img.graf-image').attr('src', e.target.result)
|
120
|
+
img_tag.height = this.height
|
121
|
+
img_tag.width = this.width
|
122
|
+
|
123
|
+
utils.log "UPLOADED SHOW FROM CACHE"
|
124
|
+
|
125
|
+
ar = self.getAspectRatio(this.width, this.height)
|
126
|
+
|
127
|
+
replaced_node.find(".aspectRatioPlaceholder").css
|
128
|
+
'max-width': ar.width
|
129
|
+
'max-height': ar.height
|
130
|
+
|
131
|
+
replaced_node.find(".graf-image").attr
|
132
|
+
"data-height": this.height
|
133
|
+
"data-width": this.width
|
134
|
+
|
135
|
+
replaced_node.find(".aspect-ratio-fill").css
|
136
|
+
"padding-bottom": "#{ar.ratio}%"
|
137
|
+
|
138
|
+
self.uploadFile file, replaced_node
|
139
|
+
|
140
|
+
reader.readAsDataURL(file)
|
141
|
+
|
142
|
+
getAspectRatio: (w , h)->
|
143
|
+
maxWidth = 700
|
144
|
+
maxHeight = 700
|
145
|
+
ratio = 0
|
146
|
+
width = w # Current image width
|
147
|
+
height = h # Current image height
|
148
|
+
|
149
|
+
# Check if the current width is larger than the max
|
150
|
+
if width > maxWidth
|
151
|
+
ratio = maxWidth / width # get ratio for scaling image
|
152
|
+
height = height * ratio # Reset height to match scaled image
|
153
|
+
width = width * ratio # Reset width to match scaled image
|
154
|
+
|
155
|
+
# Check if current height is larger than max
|
156
|
+
else if height > maxHeight
|
157
|
+
ratio = maxHeight / height # get ratio for scaling image
|
158
|
+
width = width * ratio # Reset width to match scaled image
|
159
|
+
height = height * ratio # Reset height to match scaled image
|
160
|
+
|
161
|
+
fill_ratio = height / width * 100
|
162
|
+
result = { width: width, height: height, ratio: fill_ratio }
|
163
|
+
utils.log result
|
164
|
+
result
|
165
|
+
|
166
|
+
formatData: (file, upload_params)->
|
167
|
+
formData = new FormData()
|
168
|
+
formData.append('file', file)
|
169
|
+
formData.append('upload_params', upload_params)
|
170
|
+
return formData
|
171
|
+
|
172
|
+
uploadFiles: (files)=>
|
173
|
+
acceptedTypes =
|
174
|
+
"image/png": true
|
175
|
+
"image/jpeg": true
|
176
|
+
"image/gif": true
|
177
|
+
|
178
|
+
i = 0
|
179
|
+
while i < files.length
|
180
|
+
file = files[i]
|
181
|
+
if acceptedTypes[file.type] is true
|
182
|
+
$(@placeholder).append "<progress class=\"progress\" min=\"0\" max=\"100\" value=\"0\">0</progress>"
|
183
|
+
@displayAndUploadImages(file)
|
184
|
+
i++
|
185
|
+
|
186
|
+
uploadFile: (file, node)=>
|
187
|
+
n = node
|
188
|
+
handleUp = (jqxhr)=>
|
189
|
+
@uploadCompleted jqxhr, n
|
190
|
+
|
191
|
+
$.ajax
|
192
|
+
type: "post"
|
193
|
+
url: @current_editor.upload_url
|
194
|
+
xhr: =>
|
195
|
+
xhr = new XMLHttpRequest()
|
196
|
+
xhr.upload.onprogress = @updateProgressBar
|
197
|
+
xhr
|
198
|
+
cache: false
|
199
|
+
contentType: false
|
200
|
+
|
201
|
+
success: (response) =>
|
202
|
+
response = @current_editor.upload_callback(response) if @current_editor.upload_callback
|
203
|
+
handleUp(response)
|
204
|
+
return
|
205
|
+
error: (jqxhr)=>
|
206
|
+
utils.log("ERROR: got error uploading file #{jqxhr.responseText}")
|
207
|
+
|
208
|
+
processData: false
|
209
|
+
data: @formatData(file, @upload_params)
|
210
|
+
|
211
|
+
updateProgressBar: (e)=>
|
212
|
+
$progress = $('.progress:first', this.$el)
|
213
|
+
complete = ""
|
214
|
+
|
215
|
+
if (e.lengthComputable)
|
216
|
+
complete = e.loaded / e.total * 100
|
217
|
+
complete = complete ? complete : 0
|
218
|
+
#$progress.attr('value', complete)
|
219
|
+
#$progress.html(complete)
|
220
|
+
utils.log "complete"
|
221
|
+
utils.log complete
|
222
|
+
|
223
|
+
uploadCompleted: (url, node)=>
|
224
|
+
node.find("img").attr("src", url)
|
225
|
+
#return false
|
226
|
+
|
227
|
+
###
|
228
|
+
# Handles the behavior of deleting images when using the backspace key
|
229
|
+
#
|
230
|
+
# @param {Event} e - The backspace event that is being handled
|
231
|
+
# @param {Node} node - The node the backspace was used in, assumed to be from te editor's getNode() function
|
232
|
+
#
|
233
|
+
# @return {Boolean} true if this function handled the backspace event, otherwise false
|
234
|
+
###
|
235
|
+
handleBackspaceKey: (e, node) =>
|
236
|
+
|
237
|
+
#remove graf figure is is selected but not in range (not focus on caption)
|
238
|
+
if $(".is-selected").hasClass("graf--figure") && !anchor_node?
|
239
|
+
utils.log("Replacing selected node")
|
240
|
+
@current_editor.replaceWith("p", $(".is-selected"))
|
241
|
+
|
242
|
+
e.preventDefault() #without this line, the browser may interpret the backspace as a "go pack a page" command
|
243
|
+
|
244
|
+
@current_editor.setRangeAt($(".is-selected")[0])
|
245
|
+
return true
|
246
|
+
|
247
|
+
return false
|
248
|
+
|
@@ -0,0 +1,235 @@
|
|
1
|
+
String.prototype.killWhiteSpace = ()->
|
2
|
+
this.replace(/\s/g, '')
|
3
|
+
|
4
|
+
String.prototype.reduceWhiteSpace = ()->
|
5
|
+
this.replace(/\s+/g, ' ')
|
6
|
+
|
7
|
+
utils = {}
|
8
|
+
window.Dante.utils = utils
|
9
|
+
|
10
|
+
utils.log = (message, force) ->
|
11
|
+
if (window.debugMode || force)
|
12
|
+
#console.log('%cDANTE DEBUGGER: %c' + message, 'font-family:arial,sans-serif;color:#1abf89;line-height:2em;', 'font-family:cursor,monospace;color:#333;');
|
13
|
+
#console.log('%cDANTE DEBUGGER: %c', 'font-family:arial,sans-serif;color:#1abf89;line-height:2em;', 'font-family:cursor,monospace;color:#333;');
|
14
|
+
console.log( message );
|
15
|
+
|
16
|
+
utils.getBase64Image = (img) ->
|
17
|
+
canvas = document.createElement("canvas")
|
18
|
+
canvas.width = img.width
|
19
|
+
canvas.height = img.height
|
20
|
+
ctx = canvas.getContext("2d")
|
21
|
+
ctx.drawImage img, 0, 0
|
22
|
+
dataURL = canvas.toDataURL("image/png")
|
23
|
+
|
24
|
+
# escape data:image prefix
|
25
|
+
# dataURL.replace /^data:image\/(png|jpg);base64,/, ""
|
26
|
+
|
27
|
+
# or just return dataURL
|
28
|
+
return dataURL
|
29
|
+
|
30
|
+
utils.generateUniqueName = ()->
|
31
|
+
Math.random().toString(36).slice(8)
|
32
|
+
|
33
|
+
#http://stackoverflow.com/questions/5605401/insert-link-in-contenteditable-element
|
34
|
+
utils.saveSelection = ()->
|
35
|
+
if window.getSelection
|
36
|
+
sel = window.getSelection()
|
37
|
+
if sel.getRangeAt and sel.rangeCount
|
38
|
+
ranges = []
|
39
|
+
i = 0
|
40
|
+
len = sel.rangeCount
|
41
|
+
|
42
|
+
while i < len
|
43
|
+
ranges.push sel.getRangeAt(i)
|
44
|
+
++i
|
45
|
+
return ranges
|
46
|
+
else return document.selection.createRange() if document.selection and document.selection.createRange
|
47
|
+
null
|
48
|
+
|
49
|
+
utils.restoreSelection = (savedSel) ->
|
50
|
+
if savedSel
|
51
|
+
if window.getSelection
|
52
|
+
sel = window.getSelection()
|
53
|
+
sel.removeAllRanges()
|
54
|
+
i = 0
|
55
|
+
len = savedSel.length
|
56
|
+
|
57
|
+
while i < len
|
58
|
+
sel.addRange savedSel[i]
|
59
|
+
++i
|
60
|
+
else savedSel.select() if document.selection and savedSel.select
|
61
|
+
return
|
62
|
+
|
63
|
+
utils.getNode = ()->
|
64
|
+
range = undefined
|
65
|
+
sel = undefined
|
66
|
+
container = undefined
|
67
|
+
if document.selection and document.selection.createRange
|
68
|
+
|
69
|
+
# IE case
|
70
|
+
range = document.selection.createRange()
|
71
|
+
range.parentElement()
|
72
|
+
else if window.getSelection
|
73
|
+
sel = window.getSelection()
|
74
|
+
if sel.getRangeAt
|
75
|
+
range = sel.getRangeAt(0) if sel.rangeCount > 0
|
76
|
+
else
|
77
|
+
|
78
|
+
# Old WebKit selection object has no getRangeAt, so
|
79
|
+
# create a range from other selection properties
|
80
|
+
range = document.createRange()
|
81
|
+
range.setStart sel.anchorNode, sel.anchorOffset
|
82
|
+
range.setEnd sel.focusNode, sel.focusOffset
|
83
|
+
|
84
|
+
# Handle the case when the selection was selected backwards (from the end to the start in the document)
|
85
|
+
if range.collapsed isnt sel.isCollapsed
|
86
|
+
range.setStart sel.focusNode, sel.focusOffset
|
87
|
+
range.setEnd sel.anchorNode, sel.anchorOffset
|
88
|
+
if range
|
89
|
+
container = range.commonAncestorContainer
|
90
|
+
|
91
|
+
# Check if the container is a text node and return its parent if so
|
92
|
+
(if container.nodeType is 3 then container.parentNode else container)
|
93
|
+
|
94
|
+
#http://stackoverflow.com/questions/12603397/calculate-width-height-of-the-selected-text-javascript
|
95
|
+
utils.getSelectionDimensions = ->
|
96
|
+
sel = document.selection
|
97
|
+
range = undefined
|
98
|
+
width = 0
|
99
|
+
height = 0
|
100
|
+
left = 0
|
101
|
+
top = 0
|
102
|
+
if sel
|
103
|
+
unless sel.type is "Control"
|
104
|
+
range = sel.createRange()
|
105
|
+
width = range.boundingWidth
|
106
|
+
height = range.boundingHeight
|
107
|
+
else if window.getSelection
|
108
|
+
sel = window.getSelection()
|
109
|
+
if sel.rangeCount
|
110
|
+
range = sel.getRangeAt(0).cloneRange()
|
111
|
+
if range.getBoundingClientRect
|
112
|
+
rect = range.getBoundingClientRect()
|
113
|
+
width = rect.right - rect.left
|
114
|
+
height = rect.bottom - rect.top
|
115
|
+
|
116
|
+
width: width
|
117
|
+
height: height
|
118
|
+
top: rect.top
|
119
|
+
left: rect.left
|
120
|
+
|
121
|
+
#http://stackoverflow.com/questions/3972014/get-caret-position-in-contenteditable-div
|
122
|
+
utils.getCaretPosition = (editableDiv) ->
|
123
|
+
caretPos = 0
|
124
|
+
containerEl = null
|
125
|
+
sel = undefined
|
126
|
+
range = undefined
|
127
|
+
if window.getSelection
|
128
|
+
sel = window.getSelection()
|
129
|
+
if sel.rangeCount
|
130
|
+
range = sel.getRangeAt(0)
|
131
|
+
caretPos = range.endOffset if range.commonAncestorContainer.parentNode is editableDiv
|
132
|
+
else if document.selection and document.selection.createRange
|
133
|
+
range = document.selection.createRange()
|
134
|
+
if range.parentElement() is editableDiv
|
135
|
+
tempEl = document.createElement("span")
|
136
|
+
editableDiv.insertBefore tempEl, editableDiv.firstChild
|
137
|
+
tempRange = range.duplicate()
|
138
|
+
tempRange.moveToElementText tempEl
|
139
|
+
tempRange.setEndPoint "EndToEnd", range
|
140
|
+
caretPos = tempRange.text.length
|
141
|
+
caretPos
|
142
|
+
|
143
|
+
#http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
|
144
|
+
utils.isElementInViewport = (el) ->
|
145
|
+
#special bonus for those using jQuery
|
146
|
+
el = el[0] if typeof jQuery is "function" and el instanceof jQuery
|
147
|
+
rect = el.getBoundingClientRect()
|
148
|
+
#or $(window).height()
|
149
|
+
rect.top >= 0 and rect.left >= 0 and rect.bottom <= (window.innerHeight or document.documentElement.clientHeight) and rect.right <= (window.innerWidth or document.documentElement.clientWidth) #or $(window).width()
|
150
|
+
|
151
|
+
#http://brianmhunt.github.io/articles/taming-contenteditable/
|
152
|
+
|
153
|
+
LINE_HEIGHT = 20
|
154
|
+
|
155
|
+
is_caret_at_start_of_node = (node, range) ->
|
156
|
+
# See: http://stackoverflow.com/questions/7451468
|
157
|
+
pre_range = document.createRange()
|
158
|
+
pre_range.selectNodeContents(node)
|
159
|
+
pre_range.setEnd(range.startContainer, range.startOffset)
|
160
|
+
return pre_range.toString().trim().length == 0
|
161
|
+
|
162
|
+
is_caret_at_end_of_node = (node, range) ->
|
163
|
+
post_range = document.createRange()
|
164
|
+
post_range.selectNodeContents(node)
|
165
|
+
post_range.setStart(range.endContainer, range.endOffset)
|
166
|
+
return post_range.toString().trim().length == 0
|
167
|
+
|
168
|
+
$.fn.editableIsCaret = ->
|
169
|
+
return window.getSelection().type == 'Caret'
|
170
|
+
# alt test:
|
171
|
+
# return sel.rangeCount == 1 and sel.getRangeAt(0).collapsed
|
172
|
+
|
173
|
+
$.fn.editableRange = ->
|
174
|
+
# Return the range for the selection
|
175
|
+
sel = window.getSelection()
|
176
|
+
return unless sel.rangeCount > 0
|
177
|
+
return sel.getRangeAt(0)
|
178
|
+
|
179
|
+
$.fn.editableCaretRange = ->
|
180
|
+
return unless @editableIsCaret()
|
181
|
+
return @editableRange()
|
182
|
+
|
183
|
+
$.fn.editableSetRange = (range) ->
|
184
|
+
sel = window.getSelection()
|
185
|
+
sel.removeAllRanges() if sel.rangeCount > 0
|
186
|
+
sel.addRange(range)
|
187
|
+
|
188
|
+
$.fn.editableFocus = (at_start=true) ->
|
189
|
+
return unless @attr('contenteditable')
|
190
|
+
sel = window.getSelection()
|
191
|
+
sel.removeAllRanges() if sel.rangeCount > 0
|
192
|
+
range = document.createRange()
|
193
|
+
range.selectNodeContents(@[0])
|
194
|
+
range.collapse(at_start)
|
195
|
+
sel.addRange(range)
|
196
|
+
|
197
|
+
$.fn.editableCaretAtStart = ->
|
198
|
+
range = @editableRange()
|
199
|
+
return false unless range
|
200
|
+
return is_caret_at_start_of_node(@[0], range)
|
201
|
+
|
202
|
+
$.fn.editableCaretAtEnd = ->
|
203
|
+
range = @editableRange()
|
204
|
+
return false unless range
|
205
|
+
return is_caret_at_end_of_node(@[0], range)
|
206
|
+
|
207
|
+
$.fn.editableCaretOnFirstLine = ->
|
208
|
+
range = @editableRange()
|
209
|
+
return false unless range
|
210
|
+
# At the start of a node, the getClientRects() is [], so we have to
|
211
|
+
# use the getBoundingClientRect (which seems to work).
|
212
|
+
if is_caret_at_start_of_node(@[0], range)
|
213
|
+
return true
|
214
|
+
else if is_caret_at_end_of_node(@[0], range)
|
215
|
+
ctop = @[0].getBoundingClientRect().bottom - LINE_HEIGHT
|
216
|
+
else
|
217
|
+
ctop = range.getClientRects()[0].top
|
218
|
+
etop = @[0].getBoundingClientRect().top
|
219
|
+
return ctop < etop + LINE_HEIGHT
|
220
|
+
|
221
|
+
$.fn.editableCaretOnLastLine = ->
|
222
|
+
range = @editableRange()
|
223
|
+
return false unless range
|
224
|
+
if is_caret_at_end_of_node(@[0], range)
|
225
|
+
return true
|
226
|
+
else if is_caret_at_start_of_node(@[0], range)
|
227
|
+
# We are on the first line.
|
228
|
+
cbtm = @[0].getBoundingClientRect().top + LINE_HEIGHT
|
229
|
+
else
|
230
|
+
cbtm = range.getClientRects()[0].bottom
|
231
|
+
ebtm = @[0].getBoundingClientRect().bottom
|
232
|
+
return cbtm > ebtm - LINE_HEIGHT
|
233
|
+
|
234
|
+
$.fn.exists = ->
|
235
|
+
@.length > 0
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#a very light backbone.view like version
|
2
|
+
|
3
|
+
class Dante.View
|
4
|
+
|
5
|
+
constructor: (opts = {})->
|
6
|
+
@el = opts.el if opts.el
|
7
|
+
@._ensureElement()
|
8
|
+
@initialize.apply(@, arguments)
|
9
|
+
@._ensureEvents()
|
10
|
+
|
11
|
+
initialize: (opts={})->
|
12
|
+
|
13
|
+
events: ->
|
14
|
+
|
15
|
+
render: ()->
|
16
|
+
return @
|
17
|
+
|
18
|
+
remove: ()->
|
19
|
+
@._removeElement()
|
20
|
+
@.stopListening()
|
21
|
+
return @
|
22
|
+
|
23
|
+
_removeElement: ()->
|
24
|
+
@.$el.remove();
|
25
|
+
|
26
|
+
setElement: (element)->
|
27
|
+
#@.undelegateEvents()
|
28
|
+
@._setElement(element)
|
29
|
+
#@.delegateEvents()
|
30
|
+
return @
|
31
|
+
|
32
|
+
setEvent: (opts)->
|
33
|
+
if !_.isEmpty(opts)
|
34
|
+
_.each opts, (f, key)=>
|
35
|
+
key_arr = key.split(" ")
|
36
|
+
|
37
|
+
if _.isFunction(f)
|
38
|
+
func = f
|
39
|
+
else if _.isString(f)
|
40
|
+
func = @[f]
|
41
|
+
else
|
42
|
+
throw "error event needs a function or string"
|
43
|
+
|
44
|
+
element = if key_arr.length > 1 then key_arr.splice(1 , 3).join(" ") else null
|
45
|
+
|
46
|
+
$( @el ).on( key_arr[0], element, _.bind(func, this) )
|
47
|
+
|
48
|
+
_ensureElement: ()->
|
49
|
+
@.setElement(_.result(@, 'el'))
|
50
|
+
|
51
|
+
_ensureEvents: ()->
|
52
|
+
@.setEvent(_.result(@, 'events'))
|
53
|
+
|
54
|
+
_setElement: (el)->
|
55
|
+
@.$el = if el instanceof $ then el else $(el)
|
56
|
+
@.el = @.$el[0]
|
57
|
+
|
58
|
+
|
59
|
+
# Helper function to correctly set up the prototype chain, for subclasses.
|
60
|
+
# Similar to `goog.inherits`, but uses a hash of prototype properties and
|
61
|
+
# class properties to be extended.
|
62
|
+
|
63
|
+
# This is borrowed from Backbone .extend function
|
64
|
+
|
65
|
+
extend = (protoProps, staticProps) ->
|
66
|
+
parent = this
|
67
|
+
child = undefined
|
68
|
+
# The constructor function for the new subclass is either defined by you
|
69
|
+
# (the "constructor" property in your `extend` definition), or defaulted
|
70
|
+
# by us to simply call the parent's constructor.
|
71
|
+
if protoProps and _.has(protoProps, 'constructor')
|
72
|
+
child = protoProps.constructor
|
73
|
+
else
|
74
|
+
|
75
|
+
child = ->
|
76
|
+
parent.apply this, arguments
|
77
|
+
|
78
|
+
# Add static properties to the constructor function, if supplied.
|
79
|
+
_.extend child, parent, staticProps
|
80
|
+
# Set the prototype chain to inherit from `parent`, without calling
|
81
|
+
# `parent`'s constructor function.
|
82
|
+
|
83
|
+
Surrogate = ->
|
84
|
+
@constructor = child
|
85
|
+
return
|
86
|
+
|
87
|
+
Surrogate.prototype = parent.prototype
|
88
|
+
child.prototype = new Surrogate
|
89
|
+
# Add prototype properties (instance properties) to the subclass,
|
90
|
+
# if supplied.
|
91
|
+
if protoProps
|
92
|
+
_.extend child.prototype, protoProps
|
93
|
+
# Set a convenience property in case the parent's prototype is needed
|
94
|
+
# later.
|
95
|
+
child.__super__ = parent.prototype
|
96
|
+
child
|
97
|
+
|
98
|
+
#Set up inheritance for the model, collection, router, view and history.
|
99
|
+
#Dante.View.extend = utils.extend;
|
100
|
+
|
101
|
+
Dante.View.extend = extend
|
@@ -0,0 +1,12 @@
|
|
1
|
+
//Editor components
|
2
|
+
//= require dante/dante
|
3
|
+
//= require dante/utils
|
4
|
+
//= require dante/view
|
5
|
+
//= require dante/editor
|
6
|
+
//= require dante/tooltip_widget
|
7
|
+
//= require dante/tooltip_widgets/uploader
|
8
|
+
//= require dante/tooltip_widgets/embed
|
9
|
+
//= require dante/tooltip_widgets/extract
|
10
|
+
//= require dante/tooltip
|
11
|
+
//= require dante/popover
|
12
|
+
//= require dante/menu
|
@@ -0,0 +1,54 @@
|
|
1
|
+
@-webkit-keyframes pop-upwards {
|
2
|
+
0% {
|
3
|
+
-webkit-transform: matrix(.97, 0, 0, 1, 0, 12);
|
4
|
+
transform: matrix(.97, 0, 0, 1, 0, 12);
|
5
|
+
opacity: 0;
|
6
|
+
}
|
7
|
+
20% {
|
8
|
+
-webkit-transform: matrix(.99, 0, 0, 1, 0, 2);
|
9
|
+
transform: matrix(.99, 0, 0, 1, 0, 2);
|
10
|
+
opacity: .7;
|
11
|
+
}
|
12
|
+
40% {
|
13
|
+
-webkit-transform: matrix(1, 0, 0, 1, 0, -1);
|
14
|
+
transform: matrix(1, 0, 0, 1, 0, -1);
|
15
|
+
opacity: 1;
|
16
|
+
}
|
17
|
+
70% {
|
18
|
+
-webkit-transform: matrix(1, 0, 0, 1, 0, 0);
|
19
|
+
transform: matrix(1, 0, 0, 1, 0, 0);
|
20
|
+
opacity: 1;
|
21
|
+
}
|
22
|
+
100% {
|
23
|
+
-webkit-transform: matrix(1, 0, 0, 1, 0, 0);
|
24
|
+
transform: matrix(1, 0, 0, 1, 0, 0);
|
25
|
+
opacity: 1;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
@keyframes pop-upward {
|
29
|
+
0% {
|
30
|
+
-webkit-transform: matrix(.97, 0, 0, 1, 0, 12);
|
31
|
+
transform: matrix(.97, 0, 0, 1, 0, 12);
|
32
|
+
opacity: 0;
|
33
|
+
}
|
34
|
+
20% {
|
35
|
+
-webkit-transform: matrix(.99, 0, 0, 1, 0, 2);
|
36
|
+
transform: matrix(.99, 0, 0, 1, 0, 2);
|
37
|
+
opacity: .7;
|
38
|
+
}
|
39
|
+
40% {
|
40
|
+
-webkit-transform: matrix(1, 0, 0, 1, 0, -1);
|
41
|
+
transform: matrix(1, 0, 0, 1, 0, -1);
|
42
|
+
opacity: 1;
|
43
|
+
}
|
44
|
+
70% {
|
45
|
+
-webkit-transform: matrix(1, 0, 0, 1, 0, 0);
|
46
|
+
transform: matrix(1, 0, 0, 1, 0, 0);
|
47
|
+
opacity: 1;
|
48
|
+
}
|
49
|
+
100% {
|
50
|
+
-webkit-transform: matrix(1, 0, 0, 1, 0, 0);
|
51
|
+
transform: matrix(1, 0, 0, 1, 0, 0);
|
52
|
+
opacity: 1;
|
53
|
+
}
|
54
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
.imageCaption {
|
2
|
+
& {
|
3
|
+
position: absolute;
|
4
|
+
left: -172px;
|
5
|
+
width: 150px;
|
6
|
+
top: 0;
|
7
|
+
text-align: right;
|
8
|
+
margin-top: 0;
|
9
|
+
font-family: "freight-text-pro",Georgia,Cambria,"Times New Roman",Times,serif;
|
10
|
+
letter-spacing: 0.01rem;
|
11
|
+
font-weight: 400;
|
12
|
+
font-style: italic;
|
13
|
+
font-size: 14px;
|
14
|
+
line-height: 1.4;
|
15
|
+
color: rgba(0,0,0,0.6);
|
16
|
+
outline: 0;
|
17
|
+
z-index: 300;
|
18
|
+
}
|
19
|
+
&:before {
|
20
|
+
width: 25%;
|
21
|
+
margin-left: 75%;
|
22
|
+
border-top: 1px solid rgba(0,0,0,0.15);
|
23
|
+
display: block;
|
24
|
+
content: "";
|
25
|
+
margin-bottom: 10px;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
@media (max-width: 1200px) {
|
29
|
+
.imageCaption,
|
30
|
+
.postField--outsetCenterImage > .imageCaption {
|
31
|
+
position: relative;
|
32
|
+
width: 100%;
|
33
|
+
text-align: center;
|
34
|
+
left: 0;
|
35
|
+
margin-top: 10px;
|
36
|
+
}
|
37
|
+
.imageCaption:before {
|
38
|
+
display: none;
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
figure.is-defaultValue .imageCaption,
|
43
|
+
.graf--sectionCaption.is-defaultValue {
|
44
|
+
display: none;
|
45
|
+
}
|
46
|
+
|
47
|
+
.graf--figure.is-mediaFocused .imageCaption,
|
48
|
+
.graf--figure.is-defaultValue.is-selected .imageCaption,
|
49
|
+
section.is-mediaFocused .graf--sectionCaption,
|
50
|
+
.graf--sectionCaption.is-defaultValue.is-selected {
|
51
|
+
display: block;
|
52
|
+
}
|