dante2-editor 0.2.0

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