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.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/Gemfile +25 -0
  4. data/Gemfile.lock +42 -0
  5. data/README.md +107 -0
  6. data/app/assets/index.html +55 -0
  7. data/app/assets/license.html +52 -0
  8. data/app/assets/options.html +57 -0
  9. data/app/assets/store.json +1 -0
  10. data/app/components/App.cjsx +1083 -0
  11. data/app/components/blocks/embed.cjsx +109 -0
  12. data/app/components/blocks/image.cjsx +316 -0
  13. data/app/components/blocks/placeholder.cjsx +60 -0
  14. data/app/components/blocks/video.cjsx +75 -0
  15. data/app/components/debug.cjsx +96 -0
  16. data/app/components/decorators/link.cjsx +61 -0
  17. data/app/components/popovers/addButton.cjsx +247 -0
  18. data/app/components/popovers/image.cjsx +160 -0
  19. data/app/components/popovers/link.cjsx +87 -0
  20. data/app/components/popovers/toolTip.cjsx +326 -0
  21. data/app/data/poc.js +15 -0
  22. data/app/demo.js +7 -0
  23. data/app/images/dante/media-loading-placeholder.png +0 -0
  24. data/app/images/site/dante-demo.png +0 -0
  25. data/app/images/site/dante-editor-logo.png +0 -0
  26. data/app/images/site/github-logo.png +0 -0
  27. data/app/initialize.js +4 -0
  28. data/app/model/index.js +194 -0
  29. data/app/styles/dante.scss +22 -0
  30. data/app/styles/dante/_animations.scss +54 -0
  31. data/app/styles/dante/_caption.scss +61 -0
  32. data/app/styles/dante/_debug.scss +124 -0
  33. data/app/styles/dante/_fonts.scss +17 -0
  34. data/app/styles/dante/_graf.scss +242 -0
  35. data/app/styles/dante/_icons.scss +62 -0
  36. data/app/styles/dante/_media.scss +39 -0
  37. data/app/styles/dante/_menu.scss +201 -0
  38. data/app/styles/dante/_needsorder.scss +209 -0
  39. data/app/styles/dante/_popover.scss +212 -0
  40. data/app/styles/dante/_post.scss +67 -0
  41. data/app/styles/dante/_scaffold.scss +24 -0
  42. data/app/styles/dante/_tooltip.scss +130 -0
  43. data/app/styles/dante/_utilities.scss +59 -0
  44. data/app/styles/dante/_variables.scss +96 -0
  45. data/app/styles/dante/blame.scss +246 -0
  46. data/app/styles/draft.css +297 -0
  47. data/app/styles/fonts/dante/dante.eot +0 -0
  48. data/app/styles/fonts/dante/dante.svg +18 -0
  49. data/app/styles/fonts/dante/dante.ttf +0 -0
  50. data/app/styles/fonts/dante/dante.woff +0 -0
  51. data/app/styles/fonts/dante/fontello.eot +0 -0
  52. data/app/styles/fonts/dante/fontello.svg +36 -0
  53. data/app/styles/fonts/dante/fontello.ttf +0 -0
  54. data/app/styles/fonts/dante/fontello.woff +0 -0
  55. data/app/styles/layout/layout.scss +64 -0
  56. data/app/styles/layout/normalize.css +375 -0
  57. data/app/styles/layout/scaffold.scss +8 -0
  58. data/app/styles/layout/tooltips.scss +216 -0
  59. data/app/utils/find_entities.coffee +20 -0
  60. data/app/utils/html2content.coffee +120 -0
  61. data/app/utils/logger.coffee +0 -0
  62. data/app/utils/save_content.coffee +63 -0
  63. data/app/utils/selection.js +53 -0
  64. data/config.ru +64 -0
  65. data/dante2.gemspec +19 -0
  66. data/docs/app.css +2 -0
  67. data/docs/app.css.map +1 -0
  68. data/docs/app.js +3 -0
  69. data/docs/app.js.map +1 -0
  70. data/docs/dante-vendors.js +28 -0
  71. data/docs/dante-vendors.js.map +1 -0
  72. data/docs/dante.css +2 -0
  73. data/docs/dante.css.map +1 -0
  74. data/docs/dante.js +4 -0
  75. data/docs/dante.js.map +1 -0
  76. data/docs/doc.html +57 -0
  77. data/docs/fonts/dante.eot +0 -0
  78. data/docs/fonts/dante.svg +18 -0
  79. data/docs/fonts/dante.ttf +0 -0
  80. data/docs/fonts/dante.woff +0 -0
  81. data/docs/fonts/fontello.eot +0 -0
  82. data/docs/fonts/fontello.svg +36 -0
  83. data/docs/fonts/fontello.ttf +0 -0
  84. data/docs/fonts/fontello.woff +0 -0
  85. data/docs/images/dante-editor-logo.png +0 -0
  86. data/docs/images/github-logo.png +0 -0
  87. data/docs/index.html +55 -0
  88. data/docs/license.html +52 -0
  89. data/lib/dante2-editor.rb +5 -0
  90. data/lib/dante2-editor/rails.rb +16 -0
  91. data/lib/dante2-editor/version.rb +5 -0
  92. data/package.json +61 -0
  93. data/rakefile +1 -0
  94. data/webpack.config.js +148 -0
  95. data/yarn.lock +4704 -0
  96. metadata +139 -0
@@ -0,0 +1,75 @@
1
+
2
+ React = require('react')
3
+ ReactDOM = require('react-dom')
4
+
5
+ {
6
+ Entity,
7
+ RichUtils
8
+ AtomicBlockUtils
9
+ EditorBlock
10
+ } = require('draft-js')
11
+
12
+ { updateDataOfBlock } = require('../../model/index.js')
13
+
14
+ axios = require("axios")
15
+
16
+ class VideoBlock extends React.Component
17
+ constructor: (props) ->
18
+ super props
19
+ #api_key = "86c28a410a104c8bb58848733c82f840"
20
+
21
+ @state =
22
+ embed_data: @defaultData()
23
+
24
+ defaultData: ->
25
+ existing_data = @.props.block.getData().toJS()
26
+ existing_data.embed_data || {}
27
+
28
+ # will update block state
29
+ updateData: =>
30
+ blockProps = @.props.blockProps
31
+ block = @.props.block
32
+ getEditorState = @props.blockProps.getEditorState
33
+ setEditorState = @props.blockProps.setEditorState
34
+ data = block.getData();
35
+ newData = data.merge(@state)
36
+ setEditorState(updateDataOfBlock(getEditorState(), block, newData))
37
+
38
+ dataForUpdate: =>
39
+ @.props.blockProps.data.toJS()
40
+
41
+ componentDidMount: ->
42
+
43
+ return unless @.props.blockProps.data
44
+ # ensure data isnt already loaded
45
+ return unless @dataForUpdate().endpoint or @dataForUpdate().provisory_text
46
+
47
+ axios
48
+ method: 'get'
49
+ url: "#{@.dataForUpdate().endpoint}#{@.dataForUpdate().provisory_text}&scheme=https"
50
+ .then (result)=>
51
+ @setState
52
+ embed_data: result.data #JSON.parse(data.responseText)
53
+ , @updateData
54
+ .catch (error)=>
55
+ console.log("TODO: error")
56
+
57
+ classForImage: ->
58
+ if @state.embed_data.thumbnail_url then "" else "mixtapeImage--empty u-ignoreBlock"
59
+
60
+ render: ->
61
+ return(
62
+ <figure
63
+ className='graf--figure graf--iframe graf--first' tabIndex='0'>
64
+ <div className='iframeContainer' dangerouslySetInnerHTML={__html: @state.embed_data.html}>
65
+ </div>
66
+ <figcaption
67
+ className='imageCaption'>
68
+ <EditorBlock {...@props}
69
+ className="imageCaption"
70
+ />
71
+ </figcaption>
72
+ </figure>
73
+ )
74
+
75
+ module.exports = VideoBlock
@@ -0,0 +1,96 @@
1
+ React = require('react')
2
+
3
+ class Debug extends React.Component
4
+
5
+ constructor: (options={})->
6
+ @state =
7
+ output: ""
8
+ display: "none"
9
+
10
+ handleToggleReadOnly: (e)=>
11
+ e.preventDefault()
12
+ @props.editor.toggleEditable()
13
+ return false
14
+
15
+ handleTestEmitAndDecode: (e)=>
16
+ e.preventDefault()
17
+ @.testEmitAndDecode()
18
+
19
+ handleTestEmitTEXT: (e)=>
20
+ e.preventDefault()
21
+ @.testEmitTEXT()
22
+
23
+ testEmitAndDecode: (e)=>
24
+ raw_as_json = @props.editor.emitSerializedOutput()
25
+ @props.editor.setState
26
+ editorState: @props.editor.decodeEditorContent(raw_as_json)
27
+ , @logState(JSON.stringify(raw_as_json))
28
+ false
29
+
30
+ testEmitTEXT: ()=>
31
+ text = @props.editor.getTextFromEditor()
32
+ @logState(text)
33
+
34
+ logState: (raw)=>
35
+ @setState
36
+ output: raw
37
+ , @open
38
+
39
+ toggleDisplay: (e)=>
40
+ e.preventDefault()
41
+ d = if @state.display is "block" then "none" else @state.display
42
+ @setState
43
+ display: d
44
+
45
+ open: =>
46
+ @setState
47
+ display: "block"
48
+
49
+ render: =>
50
+ return (
51
+
52
+ <div>
53
+ <div className="debugControls">
54
+ <ul>
55
+ <li>LOCKS: {@props.editor.state.locks}</li>
56
+ <li>
57
+ <a href="#" onClick={@handleToggleReadOnly}>
58
+ EDITABLE: {if @props.editor.state.read_only then 'NO' else 'YES'}
59
+ </a>
60
+ </li>
61
+ <li>
62
+ <a href="#" onClick={@handleTestEmitTEXT}>
63
+ EDITOR TEXT
64
+ </a>
65
+ </li>
66
+
67
+ <li>
68
+ <a href="#" onClick={@handleTestEmitAndDecode}>
69
+ EDITOR STATE
70
+ </a>
71
+ </li>
72
+
73
+ </ul>
74
+ </div>
75
+
76
+ <div className="debugZone" style={display: @state.display}>
77
+ <a href="#"
78
+ className="dante-debug-close close"
79
+ onClick={@toggleDisplay}
80
+ />
81
+
82
+ <div className="debugOutput">
83
+ <h2>EDITOR OUTPUT</h2>
84
+ {
85
+ if @state.output.length > 0
86
+ <pre>
87
+ {@state.output}
88
+ </pre>
89
+ }
90
+ </div>
91
+ </div>
92
+ </div>
93
+ )
94
+
95
+
96
+ module.exports = Debug
@@ -0,0 +1,61 @@
1
+ React = require('react')
2
+ {Entity} = require('draft-js')
3
+
4
+ ###
5
+ Link = (props) ->
6
+ data = Entity.get(props.entityKey).getData();
7
+ console.log props
8
+ #onMouseOver={@props.showPopLinkOver}>
9
+ #onMouseOut={@props.hidePopLinkOver}>
10
+ return (
11
+ <a href={data.url} className="markup--anchor">
12
+ {props.children}
13
+ </a>
14
+ );
15
+ ###
16
+
17
+ class Link extends React.Component
18
+
19
+ constructor: (props) ->
20
+ super props
21
+ @isHover = false
22
+
23
+ _validateLink: ()=>
24
+ pattern = new RegExp('^(https?:\/\/)?'+ # protocol
25
+ '((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|'+ # domain name
26
+ '((\d{1,3}\.){3}\d{1,3}))'+ # OR ip (v4) address
27
+ '(\:\d+)?(\/[-a-z\d%_.~+]*)*'+ # port and path
28
+ '(\?[;&a-z\d%_.~+=-]*)?'+ # query string
29
+ '(\#[-a-z\d_]*)?$','i') # fragment locater
30
+ if !pattern.test(str)
31
+ alert("Please enter a valid URL.")
32
+ return false
33
+ else
34
+ return true
35
+
36
+ _checkProtocol: ()=>
37
+ console.log "xcvd"
38
+
39
+
40
+ _showPopLinkOver: (e)=>
41
+ return unless @data.showPopLinkOver
42
+ @data.showPopLinkOver(@refs.link)
43
+
44
+ _hidePopLinkOver: (e)=>
45
+ return unless @data.hidePopLinkOver
46
+ @data.hidePopLinkOver()
47
+
48
+ render: ->
49
+ @data = Entity.get(@props.entityKey).getData();
50
+
51
+ return (
52
+ <a ref="link"
53
+ href={@data.url}
54
+ className="markup--anchor"
55
+ onMouseOver={@_showPopLinkOver}
56
+ onMouseOut={@_hidePopLinkOver}>
57
+ {@props.children}
58
+ </a>
59
+ );
60
+
61
+ module.exports = Link
@@ -0,0 +1,247 @@
1
+ React = require('react')
2
+ ReactDOM = require('react-dom')
3
+
4
+ {
5
+ Entity
6
+ RichUtils
7
+ AtomicBlockUtils
8
+ EditorState
9
+ } = require('draft-js')
10
+
11
+ {
12
+ addNewBlock
13
+ resetBlockWithType
14
+ updateDataOfBlock
15
+ getCurrentBlock
16
+ getNode
17
+ } = require('../../model/index.js')
18
+
19
+ {
20
+ getSelectionRect
21
+ getSelection
22
+ } = require("../../utils/selection.js")
23
+
24
+ class DanteInlineTooltip extends React.Component
25
+
26
+ constructor: (props) ->
27
+ super props
28
+
29
+ @state =
30
+ position: {top: 0, left:0}
31
+ show: false
32
+ scaled: false
33
+
34
+ display: (b)=>
35
+ if b then @show() else @hide()
36
+
37
+ show: =>
38
+ @setState
39
+ show: true
40
+
41
+ hide: =>
42
+ @setState
43
+ show: false
44
+
45
+ setPosition: (coords)->
46
+ @setState
47
+ position: coords
48
+
49
+ _toggleScaled: (ev)=>
50
+ if @state.scaled then @collapse() else @scale()
51
+
52
+ scale: =>
53
+ @setState
54
+ scaled: true
55
+
56
+ collapse: =>
57
+ @setState
58
+ scaled: false
59
+
60
+ componentWillReceiveProps: (newProps)=>
61
+ @collapse()
62
+
63
+ activeClass: ->
64
+ #if @props.show then "is-active" else ""
65
+ if @isActive() then "is-active" else ""
66
+
67
+ isActive: ->
68
+ @state.show
69
+
70
+ scaledClass: ->
71
+ if @state.scaled then "is-scaled" else ""
72
+
73
+ scaledWidth: ->
74
+ if @state.scaled then "124" else "0"
75
+
76
+ clickOnFileUpload: =>
77
+ @.refs.fileInput.click()
78
+ @collapse()
79
+ @hide()
80
+
81
+ handlePlaceholder: (input)=>
82
+ opts =
83
+ type: input.widget_options.insert_block
84
+ placeholder: input.options.placeholder
85
+ endpoint: input.options.endpoint
86
+
87
+ @props.onChange(resetBlockWithType(@props.editorState, 'placeholder', opts));
88
+
89
+ insertImage: (file) =>
90
+ opts =
91
+ url: URL.createObjectURL(file)
92
+ file: file
93
+
94
+ @props.onChange(addNewBlock(@props.editorState, 'image', opts));
95
+
96
+ handleFileInput: (e)=>
97
+ fileList = e.target.files
98
+ # TODO: support multiple file uploads
99
+ ###
100
+ Object.keys(fileList).forEach (o)=>
101
+ @.insertImage(fileList[0])
102
+ ###
103
+ @.insertImage(fileList[0])
104
+
105
+ widgets: =>
106
+ @.props.editor.widgets
107
+
108
+ clickHandler: (e, type)=>
109
+ request_block = @widgets().find (o)->
110
+ o.icon is type
111
+
112
+ switch request_block.widget_options.insertion
113
+ when "upload"
114
+ @clickOnFileUpload(e, request_block)
115
+ when "placeholder"
116
+ @handlePlaceholder(request_block)
117
+ else
118
+ console.log "WRONG TYPE FOR #{request_block.widget_options.insertion}"
119
+
120
+ getItems: ->
121
+ @widgets().filter (o)=>
122
+ o.widget_options.displayOnInlineTooltip
123
+
124
+ isDescendant: (parent, child)->
125
+ node = child.parentNode
126
+ while node != null
127
+ if (node is parent)
128
+ return true
129
+ node = node.parentNode
130
+ return false
131
+
132
+ relocate: ()=>
133
+ editorState = @props.editorState
134
+
135
+ if editorState.getSelection().isCollapsed()
136
+
137
+ currentBlock = getCurrentBlock(editorState)
138
+ blockType = currentBlock.getType()
139
+
140
+ contentState = editorState.getCurrentContent()
141
+ selectionState = editorState.getSelection()
142
+
143
+ block = contentState.getBlockForKey(selectionState.anchorKey);
144
+
145
+ nativeSelection = getSelection(window);
146
+ if !nativeSelection.rangeCount
147
+ return;
148
+
149
+ node = getNode()
150
+
151
+ selectionBoundary = getSelectionRect(nativeSelection);
152
+ coords = selectionBoundary #utils.getSelectionDimensions(node)
153
+
154
+ parent = ReactDOM.findDOMNode(@props.editor);
155
+ parentBoundary = parent.getBoundingClientRect();
156
+
157
+ # hide if selected node is not in editor
158
+ # debugger
159
+ #console.log @isDescendant(parent, nativeSelection.anchorNode)
160
+
161
+ if !@isDescendant(parent, nativeSelection.anchorNode)
162
+ @hide()
163
+ return
164
+
165
+ # checkeamos si esta vacio
166
+ @display block.getText().length is 0 and blockType is "unstyled"
167
+ @setPosition
168
+ top: coords.top + window.scrollY
169
+ left: coords.left + window.scrollX - 60
170
+
171
+ ###
172
+ @refs.image_popover.display(blockType is "image")
173
+
174
+ if blockType is "image"
175
+ selectionBoundary = node.anchorNode.parentNode.parentNode.parentNode.getBoundingClientRect()
176
+ #el = document.querySelector("#dante_image_popover")
177
+ el = @refs.image_popover.refs.image_popover
178
+ padd = el.offsetWidth / 2
179
+ @refs.image_popover.setPosition
180
+ top: selectionBoundary.top - parentBoundary.top + 60
181
+ left: selectionBoundary.left + (selectionBoundary.width / 2) - padd
182
+
183
+
184
+ #@setState
185
+ # image_popover_position:
186
+ # top: selectionBoundary.top - parentBoundary.top + 60
187
+ # left: selectionBoundary.left + (selectionBoundary.width / 2) - padd
188
+ #
189
+ ###
190
+
191
+ else
192
+ @hide()
193
+
194
+ render: ->
195
+ return (
196
+
197
+ <div className="inlineTooltip #{@activeClass()} #{@scaledClass()}"
198
+ style={@state.position}>
199
+
200
+ <button className="inlineTooltip-button control"
201
+ title="Close Menu"
202
+ data-action="inline-menu"
203
+ onClick={@_toggleScaled}
204
+ >
205
+ <span className="tooltip-icon dante-icon-plus"></span>
206
+ </button>
207
+
208
+ <div className="inlineTooltip-menu" style={{width: "#{@scaledWidth()}px"}}>
209
+
210
+ {
211
+ @getItems().map (item, i)=>
212
+ <InlineTooltipItem
213
+ item={item}
214
+ key={i}
215
+ clickHandler={@clickHandler}
216
+ />
217
+ }
218
+
219
+ <input type="file"
220
+ style={{display: 'none'}}
221
+ ref="fileInput"
222
+ multiple="multiple"
223
+ onChange={@.handleFileInput}
224
+ />
225
+
226
+ </div>
227
+
228
+ </div>
229
+
230
+ )
231
+
232
+ class InlineTooltipItem extends React.Component
233
+
234
+ clickHandler: (e)=>
235
+ e.preventDefault()
236
+ @props.clickHandler(e, @props.item.icon)
237
+
238
+ render: ->
239
+ return (
240
+ <button className="inlineTooltip-button scale"
241
+ title={@props.title} onMouseDown={@clickHandler}>
242
+ <span className="tooltip-icon dante-icon-#{@props.item.icon}">
243
+ </span>
244
+ </button>
245
+ )
246
+
247
+ module.exports = DanteInlineTooltip