dante-editor 0.0.1

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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.travis.yml +4 -0
  4. data/Gemfile +23 -0
  5. data/Gemfile.lock +146 -0
  6. data/Procfile +1 -0
  7. data/README.md +129 -0
  8. data/TODO.md +45 -0
  9. data/bower.json +10 -0
  10. data/config.rb +82 -0
  11. data/config.ru +31 -0
  12. data/dante-editor.gemspec +19 -0
  13. data/lib/dante-editor.rb +3 -0
  14. data/lib/dante-editor/version.rb +5 -0
  15. data/license.md +22 -0
  16. data/rakefile +2 -0
  17. data/source/assets/fonts/fontello.eot +0 -0
  18. data/source/assets/fonts/fontello.svg +36 -0
  19. data/source/assets/fonts/fontello.ttf +0 -0
  20. data/source/assets/fonts/fontello.woff +0 -0
  21. data/source/assets/images/background.png +0 -0
  22. data/source/assets/images/icon-logo.png +0 -0
  23. data/source/assets/images/icon.png +0 -0
  24. data/source/assets/images/media-loading-placeholder.png +0 -0
  25. data/source/assets/images/middleman.png +0 -0
  26. data/source/assets/javascripts/all.js +3 -0
  27. data/source/assets/javascripts/dante.js +7 -0
  28. data/source/assets/javascripts/dante/dante.js.coffee.erb +7 -0
  29. data/source/assets/javascripts/dante/editor.js.coffee +917 -0
  30. data/source/assets/javascripts/dante/menu.js.coffee +202 -0
  31. data/source/assets/javascripts/dante/tooltip.js.coffee +302 -0
  32. data/source/assets/javascripts/dante/utils.js.coffee +235 -0
  33. data/source/assets/javascripts/dante/view.js.coffee +56 -0
  34. data/source/assets/javascripts/deps.js +4 -0
  35. data/source/assets/javascripts/spec.js +2 -0
  36. data/source/assets/javascripts/specs/cleaner.js.coffee +8 -0
  37. data/source/assets/javascripts/specs/dante_view.js.coffee +74 -0
  38. data/source/assets/javascripts/specs/editor.js.coffee +57 -0
  39. data/source/assets/stylesheets/all.css.scss +4 -0
  40. data/source/assets/stylesheets/dante.css.scss +3 -0
  41. data/source/assets/stylesheets/dante/base.css.scss +57 -0
  42. data/source/assets/stylesheets/dante/editor.css.scss +662 -0
  43. data/source/assets/stylesheets/dante/fonts.css.scss +106 -0
  44. data/source/assets/stylesheets/normalize.css +375 -0
  45. data/source/embeds.html.erb +29 -0
  46. data/source/index.html.erb +28 -0
  47. data/source/layouts/layout.erb +21 -0
  48. data/source/layouts/spec.html.erb +24 -0
  49. data/source/partials/_example_1.erb +30 -0
  50. data/source/partials/_example_2.erb +33 -0
  51. data/source/partials/_example_3.erb +17 -0
  52. data/source/partials/_readme.markdown +78 -0
  53. data/source/partials/test/_example_1.erb +18 -0
  54. data/source/readme.html.erb +28 -0
  55. data/source/tests/dante_view.html.erb +11 -0
  56. data/source/tests/index.html.erb +32 -0
  57. data/tmp/.gitkeep +0 -0
  58. metadata +99 -0
@@ -0,0 +1,202 @@
1
+ utils = Dante.utils
2
+
3
+ class Dante.Editor.Menu extends Dante.View
4
+ el: "#dante-menu"
5
+
6
+ events:
7
+ "mousedown i" : "handleClick"
8
+ #"click .dante-icon" : "handleClick"
9
+ "mouseenter" : "handleOver"
10
+ "mouseleave" : "handleOut"
11
+ "keypress input": "handleInputEnter"
12
+
13
+ initialize: (opts={})=>
14
+ @config = opts.buttons || @default_config()
15
+ @current_editor = opts.editor
16
+
17
+ @commandsReg = {
18
+ block: /^(?:p|h[1-6]|blockquote|pre)$/
19
+ inline: /^(?:bold|italic|underline|insertorderedlist|insertunorderedlist|indent|outdent)$/,
20
+ source: /^(?:insertimage|createlink|unlink)$/
21
+ insert: /^(?:inserthorizontalrule|insert)$/
22
+ wrap: /^(?:code)$/
23
+ }
24
+
25
+ @lineBreakReg = /^(?:blockquote|pre|div|p)$/i;
26
+
27
+ @effectNodeReg = /(?:[pubia]|h[1-6]|blockquote|[uo]l|li)/i;
28
+
29
+ @strReg =
30
+ whiteSpace: /(^\s+)|(\s+$)/g,
31
+ mailTo: /^(?!mailto:|.+\/|.+#|.+\?)(.*@.*\..+)$/,
32
+ http: /^(?!\w+?:\/\/|mailto:|\/|\.\/|\?|#)(.*)$/
33
+
34
+ default_config: ()->
35
+ ###
36
+ buttons: [
37
+ 'blockquote', 'h2', 'h3', 'p', 'code', 'insertorderedlist', 'insertunorderedlist', 'inserthorizontalrule',
38
+ 'indent', 'outdent', 'bold', 'italic', 'underline', 'createlink'
39
+ ]
40
+ ###
41
+
42
+ buttons: ['blockquote', 'h2', 'h3', 'bold', 'italic', 'createlink']
43
+
44
+ template: ()=>
45
+ html = "<input class='dante-input' placeholder='http://' style='display: none;'>"
46
+ _.each @config.buttons, (item)->
47
+ html += "<i class=\"dante-icon icon-#{item}\" data-action=\"#{item}\"></i>"
48
+ html
49
+
50
+ render: ()=>
51
+ $(@el).html(@template())
52
+ @show()
53
+ #@delegateEvents()
54
+
55
+ handleClick: (ev)->
56
+ element = $(ev.currentTarget)
57
+ action = element.data("action")
58
+ input = $(@el).find("input.dante-input")
59
+ utils.log("menu #{action} item clicked!")
60
+ @savedSel = utils.saveSelection()
61
+
62
+ if /(?:createlink)/.test(action)
63
+ input.show()
64
+ input.focus()
65
+ else
66
+ @menuApply action
67
+
68
+ return false
69
+
70
+ handleInputEnter: (e)=>
71
+ if (e.which is 13)
72
+ utils.restoreSelection(@savedSel)
73
+ return @createlink( $(e.target) )
74
+
75
+ createlink: (input) =>
76
+ input.hide()
77
+ if input.val()
78
+ inputValue = input.val()
79
+ .replace(@strReg.whiteSpace, "")
80
+ .replace(@strReg.mailTo, "mailto:$1")
81
+ .replace(@strReg.http, "http://$1")
82
+ return @menuApply("createlink", inputValue)
83
+ action = "unlink"
84
+ @menuApply action
85
+
86
+ menuApply: (action, value)->
87
+
88
+ if @commandsReg.block.test(action)
89
+ utils.log "block here"
90
+ @commandBlock action
91
+ else if @commandsReg.inline.test(action) or @commandsReg.source.test(action)
92
+ utils.log "overall here"
93
+ @commandOverall action, value
94
+ else if @commandsReg.insert.test(action)
95
+ utils.log "insert here"
96
+ @commandInsert action
97
+ else if @commandsReg.wrap.test(action)
98
+ utils.log "wrap here"
99
+ @commandWrap action
100
+ else
101
+ utils.log "can't find command function for action: " + action
102
+
103
+ return false
104
+
105
+ setupInsertedElement: (element)->
106
+ n = @current_editor.addClassesToElement(element)
107
+ @current_editor.setElementName(n)
108
+ @current_editor.markAsSelected(n)
109
+
110
+ cleanContents: ()->
111
+ @current_editor.cleanContents()
112
+
113
+ commandOverall: (cmd, val) ->
114
+ message = " to exec 「" + cmd + "」 command" + ((if val then (" with value: " + val) else ""))
115
+
116
+ if document.execCommand(cmd, false, val)
117
+ utils.log "success" + message
118
+ n = @current_editor.getNode()
119
+ @current_editor.setupLinks($(n).find("a"))
120
+ @displayHighlights()
121
+ else
122
+ utils.log "fail" + message, true
123
+ return
124
+
125
+ commandInsert: (name) ->
126
+ node = @current_editor.current_node
127
+ return unless node
128
+ @current_editor.current_range.selectNode node
129
+ @current_editor.current_range.collapse false
130
+ @commandOverall node, name
131
+
132
+ commandBlock: (name) ->
133
+ node = @current_editor.current_node
134
+ list = @effectNode(@current_editor.getNode(node), true)
135
+ name = "p" if list.indexOf(name) isnt -1
136
+ @commandOverall "formatblock", name
137
+
138
+ commandWrap: (tag) ->
139
+ node = @current_editor.current_node
140
+ val = "<" + tag + ">" + selection + "</" + tag + ">"
141
+ @commandOverall "insertHTML", val
142
+
143
+ # node effects
144
+ effectNode: (el, returnAsNodeName) ->
145
+ nodes = []
146
+ el = el or @current_editor.$el[0]
147
+ while el isnt @current_editor.$el[0]
148
+ if el.nodeName.match(@effectNodeReg)
149
+ nodes.push (if returnAsNodeName then el.nodeName.toLowerCase() else el)
150
+ el = el.parentNode
151
+ nodes
152
+
153
+ handleOut: ()->
154
+ selected_menu = false
155
+
156
+ handleOver: ()->
157
+ selected_menu = true
158
+
159
+ displayHighlights: ()->
160
+ #remove all active links
161
+ $(@el).find(".active").removeClass("active")
162
+
163
+ nodes = @effectNode(utils.getNode())
164
+ utils.log(nodes)
165
+ _.each nodes, (node)=>
166
+ tag = node.nodeName.toLowerCase()
167
+ switch tag
168
+ when "a"
169
+ menu.querySelector("input").value = item.getAttribute("href")
170
+ tag = "createlink"
171
+ when "img"
172
+ menu.querySelector("input").value = item.getAttribute("src")
173
+ tag = "insertimage"
174
+ when "i"
175
+ tag = "italic"
176
+ when "u"
177
+ tag = "underline"
178
+ when "b"
179
+ tag = "bold"
180
+ when "code"
181
+ tag = "code"
182
+ when "ul"
183
+ tag = "insertunorderedlist"
184
+ when "ol"
185
+ tag = "insertorderedlist"
186
+ when "li"
187
+ tag = "indent"
188
+ utils.log "nothing to select"
189
+
190
+ @highlight(tag)
191
+
192
+ highlight: (tag)->
193
+ $(".icon-#{tag}").addClass("active")
194
+
195
+ show: ()->
196
+ $(@el).css("opacity", 1)
197
+ $(@el).css('visibility', 'visible')
198
+ @displayHighlights()
199
+
200
+ hide: ()->
201
+ $(@el).css("opacity", 0)
202
+ $(@el).css('visibility', 'hidden')
@@ -0,0 +1,302 @@
1
+ utils = Dante.utils
2
+
3
+ class Dante.Editor.Tooltip extends Dante.View
4
+ el: ".inlineTooltip2"
5
+
6
+ events:
7
+ "click .button--inlineTooltipControl" : "toggleOptions"
8
+ "click .inlineTooltip2-menu .button" : "handleClick"
9
+
10
+ initialize: (opts = {})=>
11
+ @current_editor = opts.editor
12
+ @buttons = [
13
+ {icon: "fa-camera", title: "Add an image", action: "image"},
14
+ {icon: "fa-play", title: "Add a video", action: "embed"},
15
+ {icon: "fa-code", title: "Add an embed", action: "embed-extract"}
16
+ ]
17
+ #TODO: include section splitter
18
+ #icon: "fa-minus", title: "Add a new part", action: "hr"
19
+
20
+ template: ()->
21
+ menu = ""
22
+ _.each @buttons, (b)->
23
+ data_action_value = if b.action_value then "data-action-value='#{b.action_value}'" else ""
24
+ menu += "<button class='button button--small button--circle button--neutral button--scale u-transitionSeries' title='#{b.title}' data-action='inline-menu-#{b.action}' #{data_action_value}>
25
+ <span class='fa #{b.icon}'></span>
26
+ </button>"
27
+
28
+ "<button class='button button--small button--circle button--neutral button--inlineTooltipControl' title='Close Menu' data-action='inline-menu'>
29
+ <span class='fa fa-plus'></span>
30
+ </button>
31
+ <div class='inlineTooltip2-menu'>
32
+ #{menu}
33
+ </div>"
34
+
35
+ insertTemplate: ()->
36
+ "<figure contenteditable='false' class='graf graf--figure is-defaultValue' name='#{utils.generateUniqueName()}' tabindex='0'>
37
+ <div style='' class='aspectRatioPlaceholder is-locked'>
38
+ <div style='padding-bottom: 100%;' class='aspect-ratio-fill'></div>
39
+ <img src='' data-height='375' data-width='600' data-image-id='' class='graf-image' data-delayed-src=''>
40
+ </div>
41
+ <figcaption contenteditable='true' data-default-value='Type caption for image (optional)' class='imageCaption'>
42
+ <span class='defaultValue'>Type caption for image (optional)</span>
43
+ <br>
44
+ </figcaption>
45
+ </figure>"
46
+
47
+ extractTemplate: ()->
48
+ "<div class='graf graf--mixtapeEmbed is-selected' name=''>
49
+ <a target='_blank' data-media-id='' class='js-mixtapeImage mixtapeImage mixtapeImage--empty u-ignoreBlock' href=''>
50
+ </a>
51
+ <a data-tooltip-type='link' data-tooltip-position='bottom' data-tooltip='' title='' class='markup--anchor markup--mixtapeEmbed-anchor' data-href='' href='' target='_blank'>
52
+ <strong class='markup--strong markup--mixtapeEmbed-strong'></strong>
53
+ <em class='markup--em markup--mixtapeEmbed-em'></em>
54
+ </a>
55
+ </div>"
56
+
57
+ embedTemplate: ()->
58
+ "<figure contenteditable='false' class='graf--figure graf--iframe graf--first' name='504e' tabindex='0'>
59
+ <div class='iframeContainer'>
60
+ <iframe frameborder='0' width='700' height='393' data-media-id='' src='' data-height='480' data-width='854'>
61
+ </iframe>
62
+ </div>
63
+ <figcaption contenteditable='true' data-default-value='Type caption for embed (optional)' class='imageCaption'>
64
+ <a rel='nofollow' class='markup--anchor markup--figure-anchor' data-href='' href='' target='_blank'>
65
+
66
+ </a>
67
+ </figcaption>
68
+ </figure>"
69
+
70
+ render: ()=>
71
+ $(@el).html(@template())
72
+ $(@el).show()
73
+
74
+ toggleOptions: ()=>
75
+ utils.log "Toggle Options!!"
76
+ $(@el).toggleClass("is-active is-scaled")
77
+
78
+ move: (coords)->
79
+ $(@el).offset(coords)
80
+
81
+ handleClick: (ev)->
82
+ name = $(ev.currentTarget).data('action')
83
+ utils.log name
84
+ switch name
85
+ when "inline-menu-image"
86
+ @placeholder = "<p>PLACEHOLDER</p>"
87
+ @imageSelect(ev)
88
+ when "inline-menu-embed"
89
+ @displayEmbedPlaceHolder()
90
+ when "inline-menu-embed-extract"
91
+ @displayExtractPlaceHolder()
92
+ when "inline-menu-hr"
93
+ @splitSection()
94
+
95
+ #UPLOADER
96
+
97
+ #replace existing img tag , and wrap it in insertTamplate
98
+ #TODO: take the url and upload it
99
+ uploadExistentImage: (image_element, opts = {})->
100
+
101
+ utils.log ("process image here!")
102
+ tmpl = $(@insertTemplate())
103
+ tmpl.find("img").attr('src', @current_editor.default_loading_placeholder )
104
+ #is a child element or a first level element ?
105
+ if $(image_element).parents(".graf").length > 0
106
+ #return if its already wrapped in graf--figure
107
+ if $(image_element).parents(".graf").hasClass("graf--figure")
108
+ return
109
+
110
+ utils.log "UNO"
111
+ tmpl.insertBefore( $(image_element).parents(".graf") )
112
+ node = @current_editor.getNode()
113
+ @current_editor.preCleanNode($(node))
114
+ @current_editor.addClassesToElement(node)
115
+ else
116
+ utils.log "DOS"
117
+ $(image_element).replaceWith(tmpl)
118
+
119
+ utils.log tmpl.attr('name')
120
+ @replaceImg(image_element, $("[name='#{tmpl.attr('name')}']"))
121
+
122
+ replaceImg: (image_element, figure)->
123
+ #set image dimensions
124
+ #TODO: maybe limit with default max-heigth ?
125
+ utils.log figure.attr("name")
126
+ utils.log figure
127
+
128
+ $(image_element).remove()
129
+
130
+ img = new Image()
131
+ img.onload = ()->
132
+ console.log "and here comes the water!"
133
+ console.log(figure)
134
+ console.log(this.width + 'x' + this.height);
135
+ figure.find(".aspectRatioPlaceholder").css
136
+ 'max-width': this.width
137
+ 'max-height': this.height
138
+ 'height': this.height
139
+ figure.find("img").attr({'data-height': this.height, 'data-width': this.width})
140
+ figure.find("img").attr('src', image_element.src )
141
+ img.src = image_element.src
142
+
143
+ displayAndUploadImages: (file)->
144
+ @displayCachedImage file
145
+
146
+ imageSelect: (ev)->
147
+ $selectFile = $('<input type="file" multiple="multiple">').click()
148
+ self = @
149
+ $selectFile.change ()->
150
+ t = this
151
+ self.uploadFiles(t.files)
152
+
153
+ displayCachedImage: (file)->
154
+ @node = @current_editor.getNode()
155
+ @current_editor.tooltip_view.hide()
156
+
157
+ reader = new FileReader()
158
+ reader.onload = (e)=>
159
+ i = new Image
160
+ i.src = e.target.result
161
+
162
+ new_tmpl = $(@insertTemplate())
163
+
164
+ replaced_node = $( new_tmpl ).insertBefore($(@node))
165
+
166
+ img_tag = new_tmpl.find('img.graf-image').attr('src', e.target.result)
167
+ img_tag.height = i.height
168
+ img_tag.width = i.width
169
+ unless i.width is 0 || i.height is 0
170
+ utils.log "UPLOADED SHOW FROM CACHE"
171
+
172
+ replaced_node.find(".aspectRatioPlaceholder").css
173
+ 'max-width': i.width
174
+ 'max-height': i.height
175
+
176
+ @uploadFile file, replaced_node
177
+
178
+ reader.readAsDataURL(file)
179
+
180
+ formatData: (file)->
181
+ formData = new FormData()
182
+ formData.append('file', file)
183
+ return formData
184
+
185
+ uploadFiles: (files)=>
186
+ acceptedTypes =
187
+ "image/png": true
188
+ "image/jpeg": true
189
+ "image/gif": true
190
+
191
+ i = 0
192
+ while i < files.length
193
+ file = files[i]
194
+ if acceptedTypes[file.type] is true
195
+ $(@placeholder).append "<progress class=\"progress\" min=\"0\" max=\"100\" value=\"0\">0</progress>"
196
+ @displayAndUploadImages(file)
197
+ i++
198
+
199
+ uploadFile: (file, node)=>
200
+ n = node
201
+ handleUp = (jqxhr)=>
202
+ @uploadCompleted jqxhr, n
203
+
204
+ $.ajax
205
+ type: "post"
206
+ url: @current_editor.upload_url
207
+ xhr: =>
208
+ xhr = new XMLHttpRequest()
209
+ xhr.upload.onprogress = @updateProgressBar
210
+ xhr
211
+ cache: false
212
+ contentType: false
213
+
214
+ success: (response) =>
215
+ handleUp(response)
216
+ return
217
+ error: (jqxhr)=>
218
+ utils.log("ERROR: got error uploading file #{jqxhr.responseText}")
219
+
220
+ processData: false
221
+ data: @formatData(file)
222
+
223
+ updateProgressBar: (e)=>
224
+ $progress = $('.progress:first', this.$el)
225
+ complete = ""
226
+
227
+ if (e.lengthComputable)
228
+ complete = e.loaded / e.total * 100
229
+ complete = complete ? complete : 0
230
+ #$progress.attr('value', complete)
231
+ #$progress.html(complete)
232
+ utils.log "complete"
233
+ utils.log complete
234
+
235
+ uploadCompleted: (url, node)=>
236
+ node.find("img").attr("src", url)
237
+ #return false
238
+
239
+ ## EMBED
240
+ displayEmbedPlaceHolder: ()->
241
+ ph = @current_editor.embed_placeholder
242
+ @node = @current_editor.getNode()
243
+ $(@node).html(ph).addClass("is-embedable")
244
+
245
+ @current_editor.setRangeAt(@node)
246
+ @hide()
247
+ false
248
+
249
+ getEmbedFromNode: (node)=>
250
+ @node_name = $(node).attr("name")
251
+ $.getJSON("#{@current_editor.oembed_url}#{$(@node).text()}").success (data)=>
252
+ @node = $("[name=#{@node_name}]")
253
+ iframe_src = $(data.html).prop("src")
254
+ tmpl = $(@embedTemplate())
255
+ tmpl.attr("name", @node.attr("name"))
256
+ $(@node).replaceWith(tmpl)
257
+ replaced_node = $(".graf--iframe[name=#{@node.attr("name")}]")
258
+ replaced_node.find("iframe").attr("src", iframe_src)
259
+ url = data.url || data.author_url
260
+ utils.log "URL IS #{url}"
261
+ replaced_node.find(".markup--anchor").attr("href", url ).text(url)
262
+ @hide()
263
+
264
+ ##EXTRACT
265
+ displayExtractPlaceHolder: ()->
266
+ ph = @current_editor.extract_placeholder
267
+ @node = @current_editor.getNode()
268
+ $(@node).html(ph).addClass("is-extractable")
269
+
270
+ @current_editor.setRangeAt(@node)
271
+ @hide()
272
+ false
273
+
274
+ getExtractFromNode: (node)=>
275
+ @node_name = $(node).attr("name")
276
+ $.getJSON("#{@current_editor.extract_url}#{$(@node).text()}").success (data)=>
277
+ @node = $("[name=#{@node_name}]")
278
+ iframe_src = $(data.html).prop("src")
279
+ tmpl = $(@extractTemplate())
280
+ tmpl.attr("name", @node.attr("name"))
281
+ $(@node).replaceWith(tmpl)
282
+ replaced_node = $(".graf--mixtapeEmbed[name=#{@node.attr("name")}]")
283
+ replaced_node.find("strong").text(data.title)
284
+ replaced_node.find("em").text(data.description)
285
+ replaced_node.append(data.provider_url)
286
+ replaced_node.find(".markup--anchor").attr("href", data.url )
287
+ unless _.isEmpty data.images
288
+ image_node = replaced_node.find(".mixtapeImage")
289
+ image_node.css("background-image", "url(#{data.images[0].url})")
290
+ image_node.removeClass("mixtapeImage--empty u-ignoreBlock")
291
+ @hide()
292
+
293
+ getExtract: (url)=>
294
+ $.getJSON("#{@current_editor.extract_url}#{url}").done (data)->
295
+ utils.log(data)
296
+
297
+ cleanOperationClasses: (node)->
298
+ node.removeClass("is-embedable is-extractable")
299
+
300
+ hide: ()=>
301
+ $(@el).hide()
302
+ $(@el).removeClass("is-active is-scaled")