$ = window.$
immutable = require('immutable')
underscore = require('underscore')
misc = require('smc-util/misc')
{defaults, required} = misc
{webapp_client} = require('./webapp_client')
{synchronized_string} = require('./syncdoc')
{React, ReactDOM, rclass, rtypes, redux, Redux, Actions, Store} = require('./smc-react')
{Loading} = require('r_misc')
{Input} = require('react-bootstrap')
redux_name = (project_id, filename) ->
return "editor-#{project_id}-#{filename}"
class CodemirrorActions extends Actions
report_error: (mesg) =>
@setState(error:mesg)
sync: =>
@set_value(@syncstring.live())
set_style: (style) =>
@setState
style: misc.merge(style, @redux.getStore(@name).get('style').toJS())
set_value: (value) =>
if @redux.getStore(@name).get('value') != value
@setState(value: value)
@syncstring.live(value)
@syncstring.sync()
set_scroll_info: (scroll_info) =>
@setState(scroll_info: scroll_info)
set_codemirror_doc: (doc) =>
@setState(doc : doc)
default_store_state =
style :
border : '1px solid grey'
value : ''
options : {}
init_redux = (path, redux, project_id) ->
name = redux_name(project_id, path)
if redux.getActions(name)?
return
actions = redux.createActions(name, CodemirrorActions)
store = redux.createStore(name, default_store_state)
console.log("getting syncstring for '#{path}'")
synchronized_string
project_id : project_id
path : path
sync_interval : 100
cb : (err, syncstring) ->
if err
actions.report_error("unable to open #{@path}")
else
syncstring.on('sync', actions.sync)
store.syncstring = actions.syncstring = syncstring
actions.set_value(syncstring.live())
return name
remove_redux(path, redux, project_id) ->
name = redux_name(project_id, path)
store = redux.getStore(name)
if not store?
return
store.syncstring?.destroy()
delete store.state
redux.removeStore(name)
redux.removeActions(name)
return name
CodemirrorEditor = rclass ({name}) ->
reduxProps :
"
value : rtypes.string
options : rtypes.object
style : rtypes.object
scroll_info : rtypes.object
doc : rtypes.object
propTypes :
actions : rtypes.object
_cm_destroy: ->
if @cm?
@cm.toTextArea()
@cm.off('change', @_cm_change)
@cm.off('scroll', @_cm_scroll)
delete @cm
init_codemirror: (options, style, value) ->
@_cm_destroy()
node = $(ReactDOM.findDOMNode(@)).find("textarea")[0]
@cm = CodeMirror.fromTextArea(node, options)
if @props.doc?
@cm.swapDoc(@props.doc)
if value? and value != @props.doc?.getValue()
@cm.setValueNoJump(value)
if style?
$(@cm.getWrapperElement()).css(style)
if @props.scroll_info?
@cm.scrollTo(@props.scroll_info.left, @props.scroll_info.top)
@cm.on('change', @_cm_change)
@cm.on('scroll', @_cm_scroll)
_cm_change: ->
@_cm_set_value = @cm.getValue()
@props.actions.set_value(@_cm_set_value)
_cm_scroll: ->
@_cm_scroll_info = @cm.getScrollInfo()
componentDidMount: ->
@init_codemirror(@props.options, @props.style, @props.value)
componentWillReceiveProps: (newProps) ->
if not @cm? or not underscore.isEqual(@props.options, newProps.options) or not underscore.isEqual(@props.style, newProps.style)
@init_codemirror(newProps.options, newProps.style, newProps.value)
else if newProps.value != @props.value and newProps.value != @_cm_set_value
@cm?.setValueNoJump(newProps.value)
componentWillUnmount: ->
if @cm?
if @_cm_scroll_info?
@props.actions?.set_scroll_info(@_cm_scroll_info)
doc = @cm.getDoc()
delete doc.cm
@props.actions?.set_codemirror_doc(doc)
@_cm_destroy()
render_info: ->
if @props.value?
<span>Buffer length: {@props.value.length}</span>
render: ->
<div>
<h4>A React/Redux/Codemirror Editor</h4>
{@render_info()}
<textarea />
</div>
require('project_file').register_file_editor
ext : ['txt', '']
icon : 'file-code-o'
init : init_redux
component : CodemirrorEditor
remove : remove_redux