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,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,56 @@
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]
@@ -0,0 +1,4 @@
1
+ //TODO: move dependencies to another file
2
+ //= require jquery/dist/jquery.js
3
+ //= require sanitize.js/lib/sanitize.js
4
+ //= require underscore/underscore.js
@@ -0,0 +1,2 @@
1
+
2
+ //= require_tree ./specs
@@ -0,0 +1,8 @@
1
+ window.editor = new Dante.Editor
2
+ upload_url: "/images.json"
3
+ el: "#editor1"
4
+
5
+ window.editor.start()
6
+
7
+ QUnit.test "should initialize editor", ( assert )->
8
+ assert.ok( _.isObject(window.editor), "Passed!" )
@@ -0,0 +1,74 @@
1
+
2
+ class TestView extends Dante.View
3
+
4
+ class TestViewWithEl extends Dante.View
5
+ el: "#view"
6
+ initialize: ()->
7
+ @model = {foo: "bar"}
8
+ super
9
+
10
+ class TestViewWithEvents extends Dante.View
11
+ el: "#view"
12
+ initialize: ->
13
+ @one = false
14
+ @two = false
15
+ @some_var = 1
16
+
17
+ events:
18
+ "click" : (ev)->
19
+ console.log("CLICKED ANYWHERE")
20
+ @anywhere = true
21
+ return false
22
+
23
+ "click .one" : (ev)->
24
+ @one = true
25
+ console.log("CLICKED .one YEAH, #{@one}")
26
+ return false
27
+
28
+ "click .two" : "handleClickTwo"
29
+
30
+ handleClickTwo: (ev)->
31
+ @two = true
32
+ console.log("CLICKED .two YEAH, #{@two}")
33
+ console.log(ev.currentTarget)
34
+
35
+ return false
36
+
37
+ class TestViewOpts extends Dante.View
38
+ el: "#view"
39
+ initialize: (opts={})->
40
+
41
+ @model = opts.model
42
+
43
+ window.view = new TestView
44
+
45
+ window.view2 = new TestView(el: "#view")
46
+ window.view3 = new TestViewWithEl
47
+
48
+ window.event_view = new TestViewWithEvents
49
+ window.opts_view = new TestViewOpts(model: 1234)
50
+
51
+ QUnit.test "should initialize view without el", ( assert )->
52
+ assert.ok( _.isObject(view), "Passed!" )
53
+ assert.ok( _.isUndefined(view.el), "Passed!" )
54
+
55
+ QUnit.test "should initialize view with el", ( assert )->
56
+ assert.ok( _.isElement(view2.el), "Passed!" )
57
+ assert.ok( _.isElement(view3.el), "Passed!" )
58
+
59
+ QUnit.test "should initialize view with @model", ( assert )->
60
+ assert.ok( !_.isEmpty(view3.model), "Passed!" )
61
+
62
+ QUnit.test "should set variable on click", ( assert )->
63
+ event_view.$el.find("a.one").trigger("click")
64
+ event_view.$el.find("a.two").trigger("click")
65
+ assert.ok( event_view.one, "Passed!" )
66
+ assert.ok( event_view.two, "Passed!" )
67
+
68
+ QUnit.test "should set vars on initialize", ( assert )->
69
+ assert.ok( event_view.some_var is 1, "Passed!" )
70
+
71
+ QUnit.test "should set windows.vars on initialize", ( assert )->
72
+ assert.ok( opts_view.model is 1234 , "Passed!" )
73
+
74
+
@@ -0,0 +1,57 @@
1
+
2
+
3
+ window.editor = new Dante.Editor
4
+ upload_url: "/images.json"
5
+ el: "#editor1"
6
+
7
+ window.editor.start()
8
+
9
+ window.editor2 = new Dante.Editor
10
+ el: "#editor2"
11
+
12
+ window.editor2.start()
13
+
14
+
15
+
16
+ QUnit.test "should initialize editor", ( assert )->
17
+ assert.ok( _.isObject(window.editor), "Passed!" )
18
+
19
+ QUnit.test "should init editor defaults", ( assert )->
20
+ assert.ok( !_.isEmpty(window.editor.title_placeholder), "Passed!" )
21
+ assert.ok( !_.isEmpty(window.editor.body_placeholder), "Passed!" )
22
+ assert.ok( !_.isEmpty(window.editor.embed_placeholder), "Passed!" )
23
+ assert.ok( !_.isEmpty(window.editor.extract_placeholder), "Passed!" )
24
+
25
+ assert.ok( !_.isEmpty(window.editor.upload_url), "Passed!" )
26
+ assert.ok( !_.isEmpty(window.editor.oembed_url), "Passed!" )
27
+ assert.ok( !_.isEmpty(window.editor.extract_url), "Passed!" )
28
+
29
+ QUnit.test "should init editor", ( assert )->
30
+ assert.ok( !_.isEmpty(window.editor), "Passed!" )
31
+ assert.ok( _.isObject(window.editor.tooltip_view), "Passed!" )
32
+ assert.ok( _.isObject(window.editor.editor_menu), "Passed!" )
33
+
34
+ QUnit.test "should build tooltip & menu", ( assert )->
35
+ assert.ok( !_.isEmpty( $(".inlineTooltip2") ), "Passed!" )
36
+ assert.ok( !_.isEmpty( $("#editor-menu") ), "Passed!" )
37
+
38
+ QUnit.test "should display placeholders when empty content", (assert)->
39
+ assert.ok $(editor.el).find("span.defaultValue").length is 2 , "Passed!"
40
+
41
+ ##CLEANER
42
+
43
+ QUnit.test "should clean spans", (assert)->
44
+ assert.ok !$(editor2.el).find("a:first span").exists(), "Passed!"
45
+ assert.ok !$(editor2.el).find(".section-inner div.class").exists(), "Passed!"
46
+ assert.ok !$(editor2.el).find(".section-inner span:not(.defaultValue)").exists(), "Passed!"
47
+ assert.ok !$(editor2.el).find(".section-inner p span").exists() , "Passed!"
48
+
49
+ QUnit.test "should detect existing images", (assert)->
50
+ fig = $(editor2.el).find(".section-inner figure")
51
+
52
+ assert.ok $(fig).exists() , "generate figure.graf--figure"
53
+ assert.ok $(fig).find("img").exists() , "figure have image"
54
+ assert.ok !_.isEmpty($(fig).find("img").attr('src')) , "and image src"
55
+
56
+ assert.ok $(fig).find("figcaption").exists() , "and caption"
57
+ assert.ok $(fig).find("figcaption span.defaultValue").exists() , "and caption span"
@@ -0,0 +1,4 @@
1
+ @charset "utf-8";
2
+
3
+ @import "normalize";
4
+ @import "dante";