dante-editor 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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")