dante2-editor 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/.babelrc +20 -0
  3. data/.eslint +13 -0
  4. data/.gitignore +15 -0
  5. data/README.md +5 -1
  6. data/dante2.gemspec +2 -4
  7. data/{app → demo}/assets/index.html +2 -2
  8. data/{app → demo}/assets/license.html +1 -1
  9. data/{app → demo}/assets/options.html +1 -5
  10. data/{app → demo}/assets/store.json +0 -0
  11. data/demo/data/poc.js +9 -0
  12. data/demo/demo.js +9 -0
  13. data/demo/initialize.js +7 -0
  14. data/{app → demo}/styles/layout/layout.scss +0 -0
  15. data/{app → demo}/styles/layout/normalize.css +0 -0
  16. data/{app → demo}/styles/layout/scaffold.scss +0 -0
  17. data/{app → demo}/styles/layout/tooltips.scss +0 -0
  18. data/dist/Dante2.js +2995 -0
  19. data/dist/Dante2.min.js +3 -0
  20. data/dist/Dante2.min.js.map +1 -0
  21. data/dist/DanteStyles.css +2 -0
  22. data/dist/DanteStyles.css.map +1 -0
  23. data/dist/DanteStyles.js +102 -0
  24. data/dist/DanteStyles.min.js +2 -0
  25. data/dist/DanteStyles.min.js.map +1 -0
  26. data/dist/dante-vendors.js +48316 -0
  27. data/dist/dante-vendors.min.js +29 -0
  28. data/dist/dante-vendors.min.js.map +1 -0
  29. data/{app/styles/fonts/dante → dist/fonts}/dante.eot +0 -0
  30. data/{app/styles/fonts/dante → dist/fonts}/dante.svg +0 -0
  31. data/{app/styles/fonts/dante → dist/fonts}/dante.ttf +0 -0
  32. data/{app/styles/fonts/dante → dist/fonts}/dante.woff +0 -0
  33. data/{app/styles/fonts/dante → dist/fonts}/fontello.eot +0 -0
  34. data/{app/styles/fonts/dante → dist/fonts}/fontello.svg +0 -0
  35. data/{app/styles/fonts/dante → dist/fonts}/fontello.ttf +0 -0
  36. data/{app/styles/fonts/dante → dist/fonts}/fontello.woff +0 -0
  37. data/docs/app.css +430 -1
  38. data/docs/app.css.map +1 -1
  39. data/docs/app.js +62 -2
  40. data/docs/app.js.map +1 -1
  41. data/docs/dante-vendors.js +49513 -22
  42. data/docs/dante-vendors.js.map +1 -1
  43. data/docs/dante.css +1602 -1
  44. data/docs/dante.css.map +1 -1
  45. data/docs/dante.js +3310 -3
  46. data/docs/dante.js.map +1 -1
  47. data/docs/doc.html +3 -7
  48. data/docs/index.html +4 -4
  49. data/docs/license.html +3 -3
  50. data/docs/options.html +53 -0
  51. data/package.json +47 -17
  52. data/rb_lib/dante2-editor/rails.rb +15 -0
  53. data/{lib → rb_lib}/dante2-editor/version.rb +1 -1
  54. data/{lib → rb_lib}/dante2-editor.rb +0 -0
  55. data/src/components/blocks/embed.js +126 -0
  56. data/src/components/blocks/image.js +377 -0
  57. data/src/components/blocks/placeholder.js +68 -0
  58. data/src/components/blocks/video.js +80 -0
  59. data/src/components/dante.js +291 -0
  60. data/src/components/dante_editor.js +928 -0
  61. data/src/components/debug.js +104 -0
  62. data/src/components/decorators/link.js +64 -0
  63. data/src/components/popovers/addButton.js +318 -0
  64. data/src/components/popovers/image.js +188 -0
  65. data/src/components/popovers/link.js +107 -0
  66. data/src/components/popovers/toolTip.js +402 -0
  67. data/{app/utils/logger.coffee → src/components/wrapper.js} +0 -0
  68. data/{app → src}/images/dante/media-loading-placeholder.png +0 -0
  69. data/{app → src}/images/site/dante-demo.png +0 -0
  70. data/{app → src}/images/site/dante-editor-logo.png +0 -0
  71. data/{app → src}/images/site/github-logo.png +0 -0
  72. data/src/index.js +9 -0
  73. data/{app → src}/model/index.js +0 -0
  74. data/src/style.js +3 -0
  75. data/{app → src}/styles/dante/_animations.scss +0 -0
  76. data/{app → src}/styles/dante/_caption.scss +0 -0
  77. data/{app → src}/styles/dante/_debug.scss +0 -0
  78. data/{app → src}/styles/dante/_fonts.scss +0 -0
  79. data/{app → src}/styles/dante/_graf.scss +0 -0
  80. data/{app → src}/styles/dante/_icons.scss +0 -0
  81. data/{app → src}/styles/dante/_media.scss +0 -0
  82. data/{app → src}/styles/dante/_menu.scss +0 -0
  83. data/{app → src}/styles/dante/_needsorder.scss +0 -0
  84. data/{app → src}/styles/dante/_popover.scss +0 -0
  85. data/{app → src}/styles/dante/_post.scss +0 -0
  86. data/{app → src}/styles/dante/_scaffold.scss +0 -0
  87. data/{app → src}/styles/dante/_tooltip.scss +0 -0
  88. data/{app → src}/styles/dante/_utilities.scss +0 -0
  89. data/{app → src}/styles/dante/_variables.scss +0 -0
  90. data/{app → src}/styles/dante/blame.scss +0 -0
  91. data/{app → src}/styles/dante.scss +0 -0
  92. data/{app → src}/styles/draft.css +0 -0
  93. data/src/styles/fonts/dante/dante.eot +0 -0
  94. data/src/styles/fonts/dante/dante.svg +18 -0
  95. data/src/styles/fonts/dante/dante.ttf +0 -0
  96. data/src/styles/fonts/dante/dante.woff +0 -0
  97. data/src/styles/fonts/dante/fontello.eot +0 -0
  98. data/src/styles/fonts/dante/fontello.svg +36 -0
  99. data/src/styles/fonts/dante/fontello.ttf +0 -0
  100. data/src/styles/fonts/dante/fontello.woff +0 -0
  101. data/src/utils/find_entities.js +17 -0
  102. data/src/utils/html2content.js +127 -0
  103. data/src/utils/save_content.js +80 -0
  104. data/{app → src}/utils/selection.js +0 -0
  105. data/tools/.eslintrc +9 -0
  106. data/tools/amd/README.md +7 -0
  107. data/tools/amd/bower.json +19 -0
  108. data/tools/amd/build.js +36 -0
  109. data/tools/build-cli.js +46 -0
  110. data/tools/build.js +30 -0
  111. data/tools/buildBabel.js +32 -0
  112. data/tools/constants.js +10 -0
  113. data/tools/dist/build.js +13 -0
  114. data/tools/docs/build.js +12 -0
  115. data/tools/es/build.js +27 -0
  116. data/tools/exec.js +50 -0
  117. data/tools/fs-utils.js +35 -0
  118. data/tools/lib/build.js +14 -0
  119. data/tools/promisify.js +14 -0
  120. data/webpack/base.config.js +89 -0
  121. data/webpack/docs.config.js +147 -0
  122. data/webpack/webpack.config.js +78 -0
  123. data/webpack.config-doc.js +5 -0
  124. data/webpack.config.js +3 -147
  125. data/yarn.lock +973 -718
  126. metadata +108 -64
  127. data/app/components/App.cjsx +0 -1083
  128. data/app/components/blocks/embed.cjsx +0 -109
  129. data/app/components/blocks/image.cjsx +0 -316
  130. data/app/components/blocks/placeholder.cjsx +0 -60
  131. data/app/components/blocks/video.cjsx +0 -75
  132. data/app/components/debug.cjsx +0 -96
  133. data/app/components/decorators/link.cjsx +0 -61
  134. data/app/components/popovers/addButton.cjsx +0 -247
  135. data/app/components/popovers/image.cjsx +0 -160
  136. data/app/components/popovers/link.cjsx +0 -87
  137. data/app/components/popovers/toolTip.cjsx +0 -326
  138. data/app/data/poc.js +0 -15
  139. data/app/demo.js +0 -7
  140. data/app/initialize.js +0 -4
  141. data/app/utils/find_entities.coffee +0 -20
  142. data/app/utils/html2content.coffee +0 -120
  143. data/app/utils/save_content.coffee +0 -63
  144. data/lib/dante2-editor/rails.rb +0 -16
@@ -1,1083 +0,0 @@
1
-
2
- React = require('react')
3
- ReactDOM = require('react-dom')
4
- Immutable = require('immutable')
5
- { Map, fromJS } = require('immutable')
6
- {
7
- convertToRaw
8
- convertFromRaw
9
- CompositeDecorator
10
- getVisibleSelectionRect
11
- getDefaultKeyBinding
12
- getSelectionOffsetKeyForNode
13
- KeyBindingUtil
14
- ContentState
15
- Editor
16
- EditorState
17
- Entity
18
- RichUtils
19
- DefaultDraftBlockRenderMap
20
- SelectionState
21
- Modifier
22
- BlockMapBuilder
23
- getSafeBodyFromHTML
24
- } = require('draft-js')
25
-
26
- DraftPasteProcessor = require('draft-js/lib/DraftPasteProcessor')
27
-
28
- # {stateToHTML} = require('draft-js-export-html')
29
- {
30
- convertToHTML
31
- convertFromHTML
32
- } = require('draft-convert')
33
-
34
- isSoftNewlineEvent = require('draft-js/lib/isSoftNewlineEvent')
35
-
36
- {
37
- addNewBlock
38
- resetBlockWithType
39
- updateDataOfBlock
40
- updateTextOfBlock
41
- getCurrentBlock
42
- addNewBlockAt
43
- updateDataOfBlock
44
- } = require('../model/index.js')
45
-
46
- DanteImagePopover = require('./popovers/image')
47
- DanteAnchorPopover = require('./popovers/link')
48
-
49
- {
50
- getSelectionRect
51
- getSelection
52
- } = require("../utils/selection.js")
53
- DanteInlineTooltip = require('./popovers/addButton.cjsx')
54
- DanteTooltip = require('./popovers/toolTip.cjsx')
55
- Link = require('./decorators/link.cjsx')
56
-
57
- Debug = require('./debug.cjsx')
58
- findEntities = require('../utils/find_entities.coffee')
59
- ImageBlock = require('./blocks/image.cjsx')
60
- EmbedBlock = require('./blocks/embed.cjsx')
61
- VideoBlock = require('./blocks/video.cjsx')
62
- PlaceholderBlock = require('./blocks/placeholder.cjsx')
63
-
64
- SaveBehavior = require('../utils/save_content.coffee')
65
- customHTML2Content = require('../utils/html2content.coffee')
66
-
67
- class Dante
68
- constructor: (options={})->
69
- console.log "init editor Dante!"
70
-
71
- # deep merge on config
72
- config = Map(fromJS(@defaultOptions(options)))
73
-
74
- @options = config.mergeDeep(options).toJS()
75
- console.log @options
76
-
77
- defaultOptions: (options={})->
78
- # default options
79
- defaultOptions = {}
80
- defaultOptions.el = 'app'
81
- defaultOptions.content = ""
82
- defaultOptions.read_only = false
83
- defaultOptions.spellcheck = false
84
- defaultOptions.title_placeholder = "Title"
85
- defaultOptions.body_placeholder = "Write your story"
86
- # @defaultOptions.api_key = "86c28a410a104c8bb58848733c82f840"
87
-
88
- defaultOptions.widgets = [
89
- {
90
- title: 'add an image'
91
- icon: 'image'
92
- type: 'image'
93
- block: 'ImageBlock'
94
- editable: true
95
- renderable: true
96
- breakOnContinuous: true
97
- wrapper_class: "graf graf--figure"
98
- selected_class: "is-selected is-mediaFocused"
99
- selectedFn: (block)=>
100
- direction = block.getData().toJS().direction
101
- switch direction
102
- when "left" then "graf--layoutOutsetLeft"
103
- when "center" then ""
104
- when "wide" then "sectionLayout--fullWidth"
105
- when "fill" then "graf--layoutFillWidth"
106
- else
107
- handleEnterWithoutText: (ctx, block)->
108
- editorState = ctx.state.editorState
109
- ctx.onChange(addNewBlockAt(editorState, block.getKey()))
110
- handleEnterWithText: (ctx, block)->
111
- editorState = ctx.state.editorState
112
- ctx.onChange(addNewBlockAt(editorState, block.getKey()))
113
- widget_options:
114
- displayOnInlineTooltip: true
115
- insertion: "upload"
116
- insert_block: "image"
117
- options:
118
- upload_url: options.upload_url
119
- upload_callback: options.image_upload_callback
120
- image_delete_callback: options.image_delete_callback
121
- image_caption_placeholder: options.image_caption_placeholder
122
- }
123
- {
124
- icon: 'embed'
125
- title: 'insert embed'
126
- type: 'embed'
127
- block: 'EmbedBlock'
128
- editable: true
129
- renderable: true
130
- breakOnContinuous: true
131
- wrapper_class: "graf graf--mixtapeEmbed"
132
- selected_class: "is-selected is-mediaFocused"
133
- widget_options:
134
- displayOnInlineTooltip: true
135
- insertion: "placeholder"
136
- insert_block: "embed"
137
- options:
138
- endpoint: "//api.embed.ly/1/extract?key=#{options.api_key}&url="
139
- placeholder: 'Paste a link to embed content from another site (e.g. Twitter) and press Enter'
140
- handleEnterWithoutText: (ctx, block)->
141
- editorState = ctx.state.editorState
142
- ctx.onChange(addNewBlockAt(editorState, block.getKey()))
143
- handleEnterWithText: (ctx, block)->
144
- editorState = ctx.state.editorState
145
- ctx.onChange(addNewBlockAt(editorState, block.getKey()))
146
- }
147
- {
148
- icon: 'video'
149
- title: 'insert video'
150
- editable: true
151
- type: 'video'
152
- block: 'VideoBlock'
153
- renderable: true
154
- breakOnContinuous: true
155
- wrapper_class: "graf--figure graf--iframe"
156
- selected_class:" is-selected is-mediaFocused"
157
- widget_options:
158
- displayOnInlineTooltip: true
159
- insertion: "placeholder"
160
- insert_block: "video"
161
- options:
162
- endpoint: "//api.embed.ly/1/oembed?key=#{options.api_key}&url="
163
- placeholder: 'Paste a YouTube, Vine, Vimeo, or other video link, and press Enter'
164
- caption: 'Type caption for embed (optional)'
165
-
166
- handleEnterWithoutText: (ctx, block)->
167
- editorState = ctx.state.editorState
168
- ctx.onChange(addNewBlockAt(editorState, block.getKey()))
169
-
170
- handleEnterWithText: (ctx, block)->
171
- editorState = ctx.state.editorState
172
- ctx.onChange(addNewBlockAt(editorState, block.getKey()))
173
- }
174
- {
175
- renderable: true
176
- editable: true
177
- block: 'PlaceholderBlock'
178
- type: 'placeholder'
179
- wrapper_class: "is-embedable"
180
- selected_class:" is-selected is-mediaFocused"
181
- widget_options:
182
- displayOnInlineTooltip: false
183
- handleEnterWithText: (ctx, block)->
184
- editorState = ctx.state.editorState
185
- data =
186
- provisory_text: block.getText()
187
- endpoint: block.getData().get('endpoint')
188
- type: block.getData().get('type')
189
-
190
- ctx.onChange(resetBlockWithType(
191
- editorState,
192
- data.type,
193
- data)
194
- )
195
- }
196
- ]
197
-
198
- defaultOptions.tooltips = [
199
- {
200
- ref: 'insert_tooltip'
201
- component: DanteTooltip
202
- displayOnSelection: true
203
- selectionElements: [
204
- "unstyled"
205
- "blockquote"
206
- "ordered-list"
207
- "unordered-list"
208
- "unordered-list-item"
209
- "ordered-list-item"
210
- "code-block"
211
- 'header-one'
212
- 'header-two'
213
- 'header-three'
214
- 'header-four'
215
- ]
216
- widget_options:
217
- block_types: [
218
- # {label: 'p', style: 'unstyled'},
219
- {label: 'h2', style: 'header-one', type: "block"},
220
- {label: 'h3', style: 'header-two', type: "block"},
221
- {label: 'h4', style: 'header-three', type: "block"},
222
- {label: 'blockquote', style: 'blockquote', type: "block"},
223
- {label: 'insertunorderedlist', style: 'unordered-list-item', type: "block"},
224
- {label: 'insertorderedlist', style: 'ordered-list-item', type: "block"},
225
- {label: 'code', style: 'code-block', type: "block"}
226
- {label: 'bold', style: 'BOLD', type: "inline"},
227
- {label: 'italic', style: 'ITALIC', type: "inline"},
228
- # {label: 'underline', style: 'UNDERLINE', type: "inline"},
229
- # {label: 'monospace', style: 'CODE', type: "inline"},
230
- # {label: 'strikethrough', style: 'STRIKETHROUGH', type: "inline"}
231
- ]
232
- }
233
- {
234
- ref: 'add_tooltip'
235
- component: DanteInlineTooltip
236
- }
237
- {
238
- ref: 'anchor_popover'
239
- component: DanteAnchorPopover
240
- }
241
- {
242
- ref: 'image_popover'
243
- component: DanteImagePopover
244
- }
245
- ]
246
-
247
- defaultOptions.xhr = {
248
- before_handler: null
249
- success_handler: null
250
- error_handler: null
251
- }
252
-
253
- defaultOptions.data_storage = {
254
- url: null
255
- method: "POST"
256
- success_handler: null
257
- failure_handler: null
258
- interval: 1500
259
- }
260
-
261
- defaultOptions.default_wrappers = [
262
- {className: 'graf--p', block: 'unstyled'},
263
- {className: 'graf--h2', block: 'header-one'},
264
- {className: 'graf--h3', block: 'header-two'},
265
- {className: 'graf--h4', block: 'header-three'},
266
- {className: 'graf--blockquote', block: 'blockquote'},
267
- {className: 'graf--insertunorderedlist', block: 'unordered-list-item'},
268
- {className: 'graf--insertorderedlist', block: 'ordered-list-item'},
269
- {className: 'graf--code', block: 'code-block'}
270
- {className: 'graf--bold', block: 'BOLD'},
271
- {className: 'graf--italic', block: 'ITALIC'},
272
- ]
273
-
274
- defaultOptions.continuousBlocks = [
275
- "unstyled"
276
- "blockquote"
277
- "ordered-list"
278
- "unordered-list"
279
- "unordered-list-item"
280
- "ordered-list-item"
281
- "code-block"
282
- ]
283
-
284
- defaultOptions.key_commands = {
285
- "alt-shift": [
286
- { key: 65, cmd: 'add-new-block'}
287
- ],
288
- "alt-cmd": [
289
- { key: 49, cmd: 'toggle_block:header-one'}
290
- { key: 50, cmd: 'toggle_block:header-two'}
291
- { key: 53, cmd: 'toggle_block:blockquote'}
292
- ],
293
- "cmd": [
294
- { key: 66, cmd: 'toggle_inline:BOLD'}
295
- { key: 73, cmd: 'toggle_inline:ITALIC'}
296
- { key: 75, cmd: 'insert:link'}
297
- ]
298
- }
299
-
300
- defaultOptions.character_convert_mapping = {
301
- '> ': "blockquote",
302
- '*.': "unordered-list-item",
303
- '* ': "unordered-list-item",
304
- '- ': "unordered-list-item",
305
- '1.': "ordered-list-item",
306
- '# ': 'header-one',
307
- '##': 'header-two',
308
- '==': "unstyled",
309
- '` ': "code-block",
310
- }
311
-
312
- defaultOptions
313
-
314
- getContent: ->
315
- #console.log @options.content
316
- #console.log "IS POC DATA?", @options.content is PocData
317
- #return PocData if @options.poc
318
- #console.log @options.content , PocData
319
- #PocData
320
- @options.content
321
-
322
- render: ->
323
- ReactDOM.render(<DanteEditor content={@getContent()}
324
- config={@options}/>, document.getElementById(@options.el))
325
-
326
- class DanteEditor extends React.Component
327
- constructor: (props) ->
328
- super props
329
- #window.main_editor = @
330
-
331
- @decorator = new CompositeDecorator([
332
- {
333
- strategy: findEntities.bind(null, 'LINK', @),
334
- component: Link
335
- }
336
- ])
337
-
338
- @.blockRenderMap = Map({
339
- "image":
340
- element: 'figure'
341
- "video":
342
- element: 'figure'
343
- "embed":
344
- element: 'div'
345
- 'unstyled':
346
- wrapper: null
347
- element: 'div'
348
- 'paragraph':
349
- wrapper: null
350
- element: 'div'
351
- 'placeholder':
352
- wrapper: null
353
- element: 'div'
354
-
355
- })
356
-
357
- @extendedBlockRenderMap = DefaultDraftBlockRenderMap.merge(@blockRenderMap);
358
-
359
- @state =
360
- editorState: @initializeState()
361
- read_only: @props.config.read_only
362
- blockRenderMap: @extendedBlockRenderMap
363
- locks: 0
364
- debug: @props.config.debug
365
-
366
- @widgets = @props.config.widgets
367
- @tooltips = @props.config.tooltips
368
-
369
- @key_commands = @props.config.key_commands
370
-
371
- @continuousBlocks = @props.config.continuousBlocks
372
-
373
- @block_types = @props.config.block_types
374
-
375
- @default_wrappers = @props.config.default_wrappers
376
-
377
- @character_convert_mapping = @props.config.character_convert_mapping
378
-
379
- @save = new SaveBehavior
380
- getLocks: @getLocks
381
- config:
382
- xhr: @props.config.xhr
383
- data_storage: @props.config.data_storage
384
- editorState: @state.editorState
385
- editorContent: @emitSerializedOutput()
386
-
387
- initializeState: ()=>
388
- if @.props.content #and @.props.content.trim() isnt ""
389
- @decodeEditorContent(@props.content)
390
- else
391
- EditorState.createEmpty(@decorator)
392
-
393
- refreshSelection: (newEditorState)=>
394
- editorState = @.state.editorState
395
- # Setting cursor position after inserting to content
396
- s = @.state.editorState.getSelection()
397
- c = editorState.getCurrentContent()
398
-
399
- selectionState = SelectionState.createEmpty(s.getAnchorKey())
400
- focusOffset = s.getFocusOffset()
401
- anchorKey = s.getAnchorKey()
402
- # console.log anchorKey, focusOffset
403
- selectionState = selectionState.merge({
404
- anchorOffset: focusOffset,
405
- focusKey: anchorKey,
406
- focusOffset: focusOffset,
407
- })
408
-
409
- newState = EditorState.forceSelection(newEditorState, selectionState)
410
-
411
- @onChange(newState)
412
-
413
- forceRender: (editorState)=>
414
- selection = @.state.editorState.getSelection()
415
- content = editorState.getCurrentContent()
416
- newEditorState = EditorState.createWithContent(content, @decorator)
417
-
418
- @refreshSelection(newEditorState)
419
-
420
- onChange: (editorState) =>
421
- @setPreContent()
422
-
423
- # console.log "BB", editorState.toJS()
424
- @.setState({editorState})
425
- #console.log "changed at", editorState.getSelection().getAnchorKey()
426
- #console.log "collapsed?", editorState.getSelection().isCollapsed()
427
-
428
- currentBlock = getCurrentBlock(@state.editorState);
429
- blockType = currentBlock.getType()
430
-
431
- if (!editorState.getSelection().isCollapsed())
432
-
433
- tooltip = @tooltipsWithProp('displayOnSelection')[0]
434
- return unless @tooltipHasSelectionElement(tooltip, blockType)
435
-
436
- @handleTooltipDisplayOn('displayOnSelection')
437
- else
438
- @handleTooltipDisplayOn('displayOnSelection', false)
439
-
440
- setTimeout ()=>
441
- @relocateTooltips()
442
- , 0
443
-
444
- @dispatchChangesToSave()
445
-
446
- dispatchChangesToSave: =>
447
- clearTimeout @saveTimeout
448
- @saveTimeout = setTimeout =>
449
- @save.store(@emitSerializedOutput())
450
- , 100
451
-
452
- setPreContent: =>
453
- content = @emitSerializedOutput()
454
- # console.log "SET PRE CONTENT", content
455
- @save.editorContent = content
456
-
457
- focus: () =>
458
- #@props.refs.richEditor.focus()
459
-
460
- getEditorState: =>
461
- @state.editorState
462
-
463
- emitSerializedOutput: =>
464
- #s = @state.editorState.getCurrentContent()
465
-
466
- raw = convertToRaw( @state.editorState.getCurrentContent() )
467
- # console.log raw
468
-
469
- #raw_as_json = JSON.stringify(raw)
470
- #console.log raw_as_json
471
- #raw_as_json
472
- raw
473
-
474
- decodeEditorContent: (raw_as_json)=>
475
- # console.log "CONTENT", raw_as_json
476
- # new_content = convertFromRaw(JSON.parse(raw_as_json))
477
- new_content = convertFromRaw(raw_as_json)
478
- editorState = EditorState.createWithContent(new_content, @decorator)
479
-
480
- ## title utils
481
- getTextFromEditor: =>
482
- c = @.state.editorState.getCurrentContent()
483
- out = c.getBlocksAsArray().map (o)=>
484
- o.getText()
485
- .join("\n")
486
-
487
- console.log out
488
- return out
489
-
490
- emitHTML2: ()->
491
-
492
- html = convertToHTML(
493
- entityToHTML: (entity, originalText) =>
494
- if entity.type is 'LINK'
495
- return "<a href=\"#{entity.data.url}\">#{originalText}</a>"
496
- else
497
- return originalText
498
-
499
- )(@.state.editorState.getCurrentContent())
500
- #html = convertToHTML(@.state.editorState.getCurrentContent())
501
-
502
- getLocks: =>
503
- @state.locks
504
-
505
- addLock: =>
506
- @setState
507
- locks: @state.locks +=1
508
-
509
- removeLock: =>
510
- #return unless @state.locks < 0
511
- @setState
512
- locks: @state.locks -=1
513
-
514
- renderableBlocks: =>
515
- @widgets.filter (o)->
516
- o.renderable
517
- .map (o)->
518
- o.type
519
-
520
- defaultWrappers: (blockType)=>
521
- @default_wrappers.filter (o)=>
522
- o.block is blockType
523
- .map (o)->
524
- o.className
525
-
526
- blockRenderer: (block)=>
527
-
528
- switch block.getType()
529
-
530
- when "atomic"
531
-
532
- entity = block.getEntityAt(0)
533
-
534
- #return null unless entity
535
- entity_type = Entity.get(entity).getType()
536
-
537
- if @renderableBlocks().includes(block.getType())
538
- return @handleBlockRenderer(block)
539
-
540
- return null;
541
-
542
- handleBlockRenderer: (block)=>
543
- dataBlock = @getDataBlock(block)
544
- return null unless dataBlock
545
-
546
- read_only = if @state.read_only then false else null
547
- editable = if read_only isnt null then read_only else dataBlock.editable
548
- # console.log "blockrender for #{block.getType()} #{editable}"
549
-
550
- return (
551
- component: eval(dataBlock.block)
552
- editable: editable
553
- props:
554
- data: block.getData()
555
- getEditorState: @getEditorState
556
- setEditorState: @onChange
557
- addLock: @addLock
558
- removeLock: @removeLock
559
- getLocks: @getLocks
560
- config: dataBlock.options
561
- )
562
-
563
- return null
564
-
565
- blockStyleFn: (block)=>
566
- currentBlock = getCurrentBlock(@.state.editorState)
567
- is_selected = if currentBlock.getKey() is block.getKey() then "is-selected" else ""
568
-
569
- if @renderableBlocks().includes(block.getType())
570
- return @styleForBlock(block, currentBlock, is_selected)
571
-
572
- defaultBlockClass = @defaultWrappers(block.getType())
573
- if defaultBlockClass.length > 0
574
- return "graf #{defaultBlockClass[0]} #{is_selected}"
575
- else
576
- return "graf nana #{is_selected}"
577
-
578
- getDataBlock: (block)=>
579
- @widgets.find (o)=>
580
- o.type is block.getType()
581
-
582
- styleForBlock: (block, currentBlock, is_selected)=>
583
- dataBlock = @getDataBlock(block)
584
-
585
- return null unless dataBlock
586
-
587
- selectedFn = if dataBlock.selectedFn then dataBlock.selectedFn(block) else null
588
- selected_class = if is_selected then dataBlock.selected_class else ''
589
-
590
- return "#{dataBlock.wrapper_class} #{selected_class} #{selectedFn}"
591
-
592
- handleTooltipDisplayOn: (prop, display=true)->
593
- setTimeout =>
594
- items = @tooltipsWithProp(prop)
595
- items.map (o)=>
596
- @refs[o.ref].display(display)
597
- @refs[o.ref].relocate()
598
- , 20
599
-
600
- handlePasteText: (text, html)=>
601
-
602
- # https://github.com/facebook/draft-js/issues/685
603
- ###
604
- html = "<p>chao</p>
605
- <avv>aaa</avv>
606
- <p>oli</p>
607
- <img src='x'/>"
608
- ###
609
-
610
- # if not html then fallback to default handler
611
-
612
- return @handleTXTPaste(text, html) unless html
613
- return @handleHTMLPaste(text, html) if html
614
-
615
- handleTXTPaste: (text, html)=>
616
- currentBlock = getCurrentBlock(@state.editorState)
617
-
618
- editorState = @state.editorState
619
-
620
- switch currentBlock.getType()
621
- when "image", "video", "placeholder"
622
- newContent = Modifier.replaceText(
623
- editorState.getCurrentContent(),
624
- new SelectionState({
625
- anchorKey: currentBlock.getKey(),
626
- anchorOffset: 0,
627
- focusKey: currentBlock.getKey(),
628
- focusOffset: 2
629
- }),
630
- text)
631
-
632
- editorState = EditorState.push(
633
- editorState,
634
- newContent,
635
- 'replace-text'
636
- );
637
-
638
- @onChange(editorState)
639
-
640
- return true
641
- else
642
- return false
643
-
644
- handleHTMLPaste: (text, html)=>
645
-
646
- currentBlock = getCurrentBlock(@state.editorState)
647
-
648
- # TODO: make this configurable
649
- switch currentBlock.getType()
650
- when "image", "video", "placeholder"
651
- return @handleTXTPaste(text, html)
652
- else
653
- # keep going
654
-
655
- newContentState = customHTML2Content(html, @extendedBlockRenderMap)
656
-
657
- selection = @state.editorState.getSelection();
658
- endKey = selection.getEndKey()
659
-
660
- content = @state.editorState.getCurrentContent();
661
- blocksBefore = content.blockMap.toSeq().takeUntil((v) => (v.key is endKey))
662
- blocksAfter = content.blockMap.toSeq().skipUntil((v) => (v.key is endKey)).rest()
663
-
664
- newBlockKey = newContentState.blockMap.first().getKey()
665
-
666
- newBlockMap = blocksBefore.concat(
667
- newContentState.blockMap,
668
- blocksAfter
669
- ).toOrderedMap();
670
-
671
- newContent = content.merge({
672
- blockMap: newBlockMap,
673
- selectionBefore: selection,
674
- selectionAfter: selection.merge({
675
- anchorKey: newBlockKey,
676
- anchorOffset: 0,
677
- focusKey: newBlockKey,
678
- focusOffset: 0,
679
- isBackward: false,
680
- })
681
- });
682
-
683
- pushedContentState = EditorState.push(
684
- @state.editorState,
685
- newContent,
686
- 'insert-fragment'
687
- )
688
-
689
- @onChange(pushedContentState)
690
-
691
- true
692
-
693
- handlePasteImage: (files)=>
694
- #TODO: check file types
695
- files.map (file)=>
696
- opts =
697
- url: URL.createObjectURL(file)
698
- file: file
699
-
700
- @onChange(addNewBlock(@state.editorState, 'image', opts))
701
-
702
- handleDroppedFiles: (state, files)=>
703
- files.map (file)=>
704
- opts =
705
- url: URL.createObjectURL(file)
706
- file: file
707
-
708
- @onChange(addNewBlock(@state.editorState, 'image', opts))
709
-
710
- handleUpArrow: (e)=>
711
- setTimeout =>
712
- @forceRender(@state.editorState)
713
- , 10
714
-
715
- handleDownArrow: (e)=>
716
- setTimeout =>
717
- @forceRender(@state.editorState)
718
- , 10
719
-
720
- handleReturn: (e) =>
721
- if this.props.handleReturn
722
- if this.props.handleReturn()
723
- return true;
724
-
725
- editorState = @state.editorState
726
- ###
727
- #if (isSoftNewlineEvent(e)) {
728
- # this.onChange(RichUtils.insertSoftNewline(editorState));
729
- # return true;
730
- #}
731
- ###
732
- if !e.altKey && !e.metaKey && !e.ctrlKey
733
- currentBlock = getCurrentBlock(editorState)
734
- blockType = currentBlock.getType()
735
- selection = editorState.getSelection()
736
-
737
- config_block = @getDataBlock(currentBlock)
738
-
739
- #if blockType.indexOf('atomic') is 0
740
- # @.onChange(addNewBlockAt(editorState, currentBlock.getKey()))
741
- # return true;
742
-
743
- if currentBlock.getText().length is 0
744
-
745
- if config_block && config_block.handleEnterWithoutText
746
- config_block.handleEnterWithText(@, currentBlock)
747
- @closePopOvers()
748
- return true
749
-
750
- #TODO turn this in configurable
751
- switch (blockType)
752
- when "header-one"
753
- @.onChange(resetBlockWithType(editorState, "unstyled"));
754
- return true;
755
- else
756
- return false;
757
-
758
- if currentBlock.getText().length > 0
759
-
760
- if blockType is "unstyled"
761
- # hack hackety hack
762
- # https://github.com/facebook/draft-js/issues/304
763
- newContent = Modifier.splitBlock(
764
- @state.editorState.getCurrentContent(),
765
- @state.editorState.getSelection()
766
- )
767
-
768
- newEditorState = EditorState.push(@state.editorState, newContent, 'insert-characters')
769
- @.onChange(newEditorState)
770
-
771
- setTimeout =>
772
- #TODO: check is element is in viewport
773
- a = document.getElementsByClassName("is-selected")
774
- pos = a[0].getBoundingClientRect()
775
- window.scrollTo(0, pos.top + window.scrollY - 100)
776
- , 0
777
-
778
- return true
779
-
780
- if config_block && config_block.handleEnterWithText
781
- config_block.handleEnterWithText(@, currentBlock)
782
- @closePopOvers()
783
- return true
784
-
785
- if ( currentBlock.getLength() is selection.getStartOffset())
786
- if @continuousBlocks.indexOf(blockType) < 0
787
- @.onChange(addNewBlockAt(editorState, currentBlock.getKey()))
788
- return true
789
-
790
- return false
791
-
792
- # selection.isCollapsed() and # should we check collapsed here?
793
- if ( currentBlock.getLength() is selection.getStartOffset()) #or (config_block && config_block.breakOnContinuous))
794
- # it will match the unstyled for custom blocks
795
- if @continuousBlocks.indexOf(blockType) < 0
796
- @.onChange(addNewBlockAt(editorState, currentBlock.getKey()))
797
- return true
798
- return false
799
-
800
- return false
801
-
802
- #return false
803
-
804
- # TODO: make this configurable
805
- handleBeforeInput: (chars)=>
806
- currentBlock = getCurrentBlock(@state.editorState)
807
- blockType = currentBlock.getType()
808
- selection = @.state.editorState.getSelection()
809
- editorState = @state.editorState
810
-
811
- # close popovers
812
- if currentBlock.getText().length isnt 0
813
- #@closeInlineButton()
814
- @closePopOvers()
815
-
816
- # handle space on link
817
- endOffset = selection.getEndOffset()
818
- endKey = currentBlock.getEntityAt(endOffset - 1)
819
- endEntityType = endKey && Entity.get(endKey).getType()
820
- afterEndKey = currentBlock.getEntityAt(endOffset)
821
- afterEndEntityType = afterEndKey && Entity.get(afterEndKey).getType()
822
-
823
- # will insert blank space when link found
824
- if (chars is ' ' && endEntityType is 'LINK' && afterEndEntityType isnt 'LINK')
825
- newContentState = Modifier.insertText(
826
- editorState.getCurrentContent(),
827
- selection,
828
- ' '
829
- )
830
- newEditorState = EditorState.push(editorState, newContentState, 'insert-characters')
831
- @.onChange(newEditorState)
832
- return true
833
-
834
- # block transform
835
- if blockType.indexOf('atomic') is 0
836
- return false;
837
-
838
- blockLength = currentBlock.getLength()
839
- if selection.getAnchorOffset() > 1 || blockLength > 1
840
- return false
841
-
842
- blockTo = @character_convert_mapping[currentBlock.getText() + chars]
843
-
844
- console.log "BLOCK TO SHOW: #{blockTo}"
845
-
846
- if !blockTo
847
- return false
848
-
849
- @onChange(resetBlockWithType(editorState, blockTo))
850
-
851
- return true
852
-
853
- # TODO: make this configurable
854
- handleKeyCommand: (command)=>
855
- editorState = @.state.editorState
856
-
857
- if @.props.handleKeyCommand && @.props.handleKeyCommand(command)
858
- return true
859
-
860
- if command is 'add-new-block'
861
- @.onChange(addNewBlock(editorState, 'blockquote'))
862
- return true
863
-
864
- block = getCurrentBlock(editorState);
865
-
866
- if command.indexOf('toggle_inline:') is 0
867
- newBlockType = command.split(':')[1]
868
- currentBlockType = block.getType()
869
- @onChange(
870
- RichUtils.toggleInlineStyle(editorState, newBlockType)
871
- )
872
- return true
873
-
874
- if command.indexOf('toggle_block:') is 0
875
- newBlockType = command.split(':')[1]
876
- currentBlockType = block.getType()
877
-
878
- @onChange(
879
- RichUtils.toggleBlockType(editorState, newBlockType)
880
- )
881
- return true;
882
-
883
- newState = RichUtils.handleKeyCommand(@state.editorState, command);
884
- if newState
885
- @.onChange(newState);
886
- return true;
887
-
888
- return false;
889
-
890
- findCommandKey: (opt, command)=>
891
- # console.log "COMMAND find: #{opt} #{command}"
892
- @key_commands[opt].find (o)->
893
- o.key is command
894
-
895
- KeyBindingFn: (e)=>
896
-
897
- #⌘ + B / Ctrl + B Bold
898
- #⌘ + I / Ctrl + I Italic
899
- #⌘ + K / Ctrl + K Turn into link
900
- #⌘ + Alt + 1 / Ctrl + Alt + 1 Header
901
- #⌘ + Alt + 2 / Ctrl + Alt + 2 Sub-Header
902
- #⌘ + Alt + 5 / Ctrl + Alt + 5 Quote (Press once for a block quote, again for a pull quote and a third time to turn off quote)
903
-
904
- # console.log "CTRL #{e.ctrlKey} #{e.which} alt: #{e.altKey} shift #{e.shiftKey}"
905
- # console.log('in window::'+ e.ctrlKey+'in mac os'+e.metaKey+'####'+e.META_MASK+'##$&&'+e.CTRL_MASK);
906
-
907
- if (e.altKey)
908
- if (e.shiftKey)
909
- cmd = @findCommandKey("alt-shift", e.which)
910
- return cmd.cmd if cmd
911
-
912
- return getDefaultKeyBinding(e)
913
-
914
- if e.ctrlKey or e.metaKey
915
- cmd = @findCommandKey("alt-cmd", e.which)
916
- return cmd.cmd if cmd
917
- return getDefaultKeyBinding(e)
918
-
919
- else if e.ctrlKey or e.metaKey
920
- cmd = @findCommandKey("cmd", e.which)
921
- return cmd.cmd if cmd
922
- return getDefaultKeyBinding(e)
923
-
924
- return getDefaultKeyBinding(e)
925
-
926
- # will update block state todo: movo to utils
927
- updateBlockData: (block, options)=>
928
- data = block.getData()
929
- newData = data.merge(options)
930
- newState = updateDataOfBlock(@state.editorState, block, newData)
931
- # this fixes enter from image caption
932
- @forceRender(newState)
933
-
934
- setDirection: (direction_type)=>
935
-
936
- contentState = @state.editorState.getCurrentContent()
937
- selectionState = @state.editorState.getSelection()
938
- block = contentState.getBlockForKey(selectionState.anchorKey);
939
-
940
- @updateBlockData(block, {direction: direction_type})
941
-
942
- ## read only utils
943
- toggleEditable: =>
944
- @closePopOvers()
945
-
946
- @setState
947
- read_only: !@state.read_only
948
- , @testEmitAndDecode
949
-
950
- closePopOvers: ()=>
951
- @tooltips.map (o)=>
952
- @refs[o.ref].hide()
953
-
954
- relocateTooltips: ()=>
955
- @tooltips.map (o)=>
956
- @refs[o.ref].relocate()
957
-
958
- tooltipsWithProp: (prop)=>
959
- @tooltips.filter (o)=>
960
- o[prop]
961
-
962
- tooltipHasSelectionElement: (tooltip, element)=>
963
- tooltip.selectionElements.includes(element)
964
-
965
- #################################
966
- # TODO: this methods belongs to popovers/link
967
- #################################
968
-
969
- handleShowPopLinkOver: (e)=>
970
- @showPopLinkOver()
971
-
972
- handleHidePopLinkOver: (e)=>
973
- @hidePopLinkOver()
974
-
975
- showPopLinkOver: (el)=>
976
- # handles popover display
977
- # using anchor or from popover
978
-
979
- # set url first in order to calculate popover width
980
- @refs.anchor_popover.setState
981
- url: if el then el.href else @refs.anchor_popover.state.url
982
-
983
- parent_el = ReactDOM.findDOMNode(@);
984
- coords = @refs.anchor_popover.relocate(
985
- el
986
- ) if el
987
-
988
- @refs.anchor_popover.setPosition coords if coords
989
-
990
- @refs.anchor_popover.setState
991
- show: true
992
-
993
- @isHover = true
994
- @cancelHide()
995
-
996
- hidePopLinkOver: ()=>
997
- @hideTimeout = setTimeout ()=>
998
- @refs.anchor_popover.hide()
999
- , 300
1000
-
1001
- cancelHide: ()->
1002
- # console.log "Cancel Hide"
1003
- clearTimeout @hideTimeout
1004
-
1005
- ###############################
1006
-
1007
- render: =>
1008
-
1009
- return (
1010
- <div id="content" suppressContentEditableWarning={true}>
1011
-
1012
- <article className="postArticle">
1013
- <div className="postContent">
1014
- <div className="notesSource">
1015
- <div id="editor"
1016
- className="postField postField--body">
1017
-
1018
- <section className="section--first section--last">
1019
- <div className="section-divider layoutSingleColumn">
1020
- <hr className="section-divider"/>
1021
- </div>
1022
-
1023
- <div className="section-content">
1024
- <div ref="richEditor"
1025
- className="section-inner layoutSingleColumn"
1026
- onClick={@.focus}>
1027
- <Editor
1028
- blockRendererFn={@.blockRenderer}
1029
- editorState={@state.editorState}
1030
- onChange={@onChange}
1031
- onUpArrow={@handleUpArrow}
1032
- onDownArrow={@handleDownArrow}
1033
- handleReturn={@handleReturn}
1034
- blockRenderMap={@state.blockRenderMap}
1035
- blockStyleFn={@.blockStyleFn}
1036
- handlePastedText={@handlePasteText}
1037
- handlePastedFiles={@handlePasteImage}
1038
- handleDroppedFiles={@handleDroppedFiles}
1039
- handleKeyCommand={@.handleKeyCommand}
1040
- keyBindingFn={@KeyBindingFn}
1041
- handleBeforeInput={@.handleBeforeInput}
1042
- readOnly={@state.read_only}
1043
- placeholder={@props.config.body_placeholder}
1044
- ref="editor"
1045
- />
1046
- </div>
1047
- </div>
1048
- </section>
1049
-
1050
- </div>
1051
- </div>
1052
- </div>
1053
- </article>
1054
-
1055
- {
1056
- @tooltips.map (o, i)=>
1057
- <o.component
1058
- ref={o.ref}
1059
- key={i}
1060
- editor={@}
1061
- editorState={@state.editorState}
1062
- onChange={@onChange}
1063
- configTooltip={o}
1064
- widget_options={o.widget_options}
1065
-
1066
- showPopLinkOver={@showPopLinkOver}
1067
- hidePopLinkOver={@hidePopLinkOver}
1068
- handleOnMouseOver={@handleShowPopLinkOver}
1069
- handleOnMouseOut={@handleHidePopLinkOver}
1070
- />
1071
- }
1072
-
1073
- {
1074
- if @state.debug
1075
- <Debug locks={@state.locks}
1076
- editor={@}
1077
- />
1078
- }
1079
-
1080
- </div>
1081
- )
1082
-
1083
- module.exports = Dante