<commit_before>{div, h3, a, i, textarea, form, label, button, span, ul, li, input} = React.DOM
classer = React.addons.classSet

FilePicker = require './file_picker'
MailsInput = require './mails_input'

AccountPicker = require './account_picker'

{ComposeActions} = require '../constants/app_constants'

MessageUtils = require '../utils/message_utils'

LayoutActionCreator  = require '../actions/layout_action_creator'
MessageActionCreator = require '../actions/message_action_creator'

RouterMixin = require '../mixins/router_mixin'

module.exports = Compose = React.createClass
    displayName: 'Compose'

    mixins: [
        RouterMixin,
        React.addons.LinkedStateMixin # two-way data binding
    ]

    propTypes:
        selectedAccount: React.PropTypes.object.isRequired
        layout:          React.PropTypes.string.isRequired
        accounts:        React.PropTypes.object.isRequired
        message:         React.PropTypes.object
        action:          React.PropTypes.string
        callback:        React.PropTypes.func
        settings:        React.PropTypes.object.isRequired

    shouldComponentUpdate: (nextProps, nextState) ->
        return not(_.isEqual(nextState, @state)) or not (_.isEqual(nextProps, @props))

    render: ->

        return unless @props.accounts

        expandUrl = @buildUrl
            direction: 'first'
            action: 'compose'
            fullWidth: true

        collapseUrl = @buildUrl
            firstPanel:
                action: 'account.mailbox.messages'
                parameters: @state.accountID
            secondPanel:
                action: 'compose'

        cancelUrl = @buildUrl
            direction: 'first'
            action: 'default'
            fullWidth: true

        closeUrl = @buildClosePanelUrl @props.layout

        classLabel = 'compose-label'
        classInput = 'compose-input'
        classCc    = if @state.cc.length is 0 then '' else ' shown'
        classBcc   = if @state.bcc.length is 0 then '' else ' shown'

        labelSend = if @state.sending then t 'compose action sending' else t 'compose action send'

        div id: 'email-compose',
            if @props.layout isnt 'full'
                a href: expandUrl, className: 'expand pull-right',
                    i className: 'fa fa-arrows-h'
            else
                a href: collapseUrl, className: 'close-email pull-right',
                    i className:'fa fa-compress'
            h3 null,
                t 'compose'
            form className: '',
                div className: 'form-group',
                    label
                        htmlFor: 'compose-from',
                        className: classLabel,
                        t "compose from"
                    div className: classInput,
                        div
                            className: 'btn-toolbar compose-toggle',
                            role: 'toolbar',
                                div null
                                    a
                                        className: 'compose-toggle-cc',
                                        onClick: @onToggleCc,
                                        t 'compose toggle cc'
                                    a
                                        className: 'compose-toggle-bcc',
                                        onClick: @onToggleBcc,
                                        t 'compose toggle bcc'

                        AccountPicker
                            accounts: @props.accounts
                            valueLink: @linkState 'accountID'
                            type: 'address'
                div className: 'clearfix', null

                MailsInput
                    id: 'compose-to'
                    valueLink: @linkState 'to'
                    label: t 'compose to'
                    ref: 'to'

                MailsInput
                    id: 'compose-cc'
                    className: 'compose-cc' + classCc
                    valueLink: @linkState 'cc'
                    label: t 'compose cc'
                    placeholder: t 'compose cc help'

                MailsInput
                    id: 'compose-bcc'
                    className: 'compose-bcc' + classBcc
                    valueLink: @linkState 'bcc'
                    label: t 'compose bcc'
                    placeholder: t 'compose bcc help'

                div className: 'form-group',
                    label
                        htmlFor: 'compose-subject',
                        className: classLabel,
                        t "compose subject"
                    div className: classInput,
                        input
                            id: 'compose-subject',
                            name: 'compose-subject',
                            ref: 'subject',
                            valueLink: @linkState('subject'),
                            type: 'text',
                            className: 'form-control',
                            placeholder: t "compose subject help"
                div className: '',
                    label
                        htmlFor: 'compose-subject',
                        className: classLabel,
                        t "compose content"
                    if @state.composeInHTML
                        div
                            className: 'rt-editor form-control',
                            ref: 'html',
                            contentEditable: true,
                            onKeyDown: @onKeyDown,
                            dangerouslySetInnerHTML: {
                                __html: @linkState('html').value
                            }
                    else
                        textarea
                            className: 'editor',
                            ref: 'content',
                            onKeyDown: @onKeyDown,
                            defaultValue: @linkState('text').value

                div className: 'attachements',
                    FilePicker
                        className: ''
                        editable: true
                        valueLink: @linkState 'attachments'

                div className: 'composeToolbox',
                    div className: 'btn-toolbar', role: 'toolbar',
                        div className: '',
                            button
                                className: 'btn btn-cozy',
                                type: 'button',
                                disable: if @state.sending then true else null
                                onClick: @onSend,
                                    span
                                        className: 'fa fa-send'
                                    span null, labelSend
                            button
                                className: 'btn btn-cozy',
                                type: 'button', onClick: @onDraft,
                                    span className: 'fa fa-save'
                                    span null, t 'compose action draft'
                            if @props.message?
                                button
                                    className: 'btn btn-cozy-non-default',
                                    type: 'button',
                                    onClick: @onDelete,
                                        span className: 'fa fa-trash-o'
                                        span null, t 'compose action delete'
                            a
                                href: cancelUrl,
                                className: 'btn btn-cozy-non-default',
                                t 'app cancel'
                div className: 'clearfix', null

    _initCompose: ->

        if @_saveInterval
            window.clearInterval @_saveInterval
        @_saveInterval = window.setInterval @_autosave, 30000

        # scroll compose window into view
        @getDOMNode().scrollIntoView()
        if @state.composeInHTML
            if Array.isArray(@state.to) and @state.to.length > 0 and @state.subject isnt ''
                node = @refs.html.getDOMNode()
                jQuery(node).focus()
                if not @props.settings.get 'composeOnTop'
                    node = node.lastChild
                    if node?
                        # move cursor to the bottom
                        node.scrollIntoView(false)
                        node.innerHTML = "<br \>"
                        s = window.getSelection()
                        r = document.createRange()
                        r.selectNodeContents(node)
                        s.removeAllRanges()
                        s.addRange(r)
                        document.execCommand('delete', false, null)
            else
                document.getElementById('compose-to').focus()

            # Some DOM manipulation when replying inside the message.
            # When inserting a new line, we must close all blockquotes,
            # insert a blank line and then open again blockquotes
            jQuery('#email-compose .rt-editor').on('keypress', (e) ->
                if e.keyCode is 13
                    # timeout to let the editor perform its own stuff
                    setTimeout ->
                        matchesSelector = document.documentElement.matches or
                              document.documentElement.matchesSelector or
                              document.documentElement.webkitMatchesSelector or
                              document.documentElement.mozMatchesSelector or
                              document.documentElement.oMatchesSelector or
                              document.documentElement.msMatchesSelector

                        target = document.getSelection().anchorNode
                        if not target?
                            return
                        if matchesSelector? and not matchesSelector.call(target, '.rt-editor blockquote *')
                            # we are not inside a blockquote, nothing to do
                            return

                        if target.lastChild
                            target = target.lastChild.previousElementSibling
                        parent = target

                        # alternative 1
                        # we create 2 ranges, one from the begining of message
                        # to the caret position, the second from caret to the
                        # end. We then create fragments from the ranges and
                        # override message with first fragment, a blank line
                        # and second fragment
                        process = ->
                            current = parent
                            parent = parent?.parentNode
                        process()
                        process() while (parent? and
                            not parent.classList.contains 'rt-editor')
                        rangeBefore = document.createRange()
                        rangeBefore.setEnd target, 0
                        rangeBefore.setStartBefore parent.firstChild
                        rangeAfter = document.createRange()
                        if target.nextSibling?
                            # remove the BR the <enter> key probably inserted
                            rangeAfter.setStart target.nextSibling, 0
                        else
                            rangeAfter.setStart target, 0
                        rangeAfter.setEndAfter parent.lastChild
                        before = rangeBefore.cloneContents()
                        after = rangeAfter.cloneContents()
                        inserted = document.createElement 'p'
                        inserted.innerHTML = "<br />"
                        parent.innerHTML = ""
                        parent.appendChild before
                        parent.appendChild inserted
                        parent.appendChild after

                        ###
                        # alternative 2
                        # We move every node from the caret to the end of the
                        # message to a new DOM tree, then insert a blank line
                        # and the new tree
                        parent = target
                        p2 = null
                        p3 = null
                        process = ->
                            p3 = p2
                            current = parent
                            parent = parent.parentNode
                            p2 = parent.cloneNode false
                            if p3?
                                p2.appendChild p3
                            s = current.nextSibling
                            while s?
                                p2.appendChild(s.cloneNode(true))
                                s2 = s.nextSibling
                                parent.removeChild s
                                s = s2
                        process()
                        process() while (parent.parentNode? and
                            not parent.parentNode.classList.contains 'rt-editor')
                        after = p2
                        inserted = document.createElement 'p'
                        inserted.innerHTML = "<br />"
                        if parent.nextSibling
                            parent.parentNode.insertBefore inserted, parent.nextSibling
                            parent.parentNode.insertBefore after, parent.nextSibling
                        else
                            parent.parentNode.appendChild inserted
                            parent.parentNode.appendChild after
                        ###

                        inserted.focus()
                        sel = window.getSelection()
                        sel.collapse inserted, 0

                    , 0
            )
        else
            # Text message
            if Array.isArray(@state.to) and @state.to.length > 0 and @state.subject isnt ''
                node = @refs.content.getDOMNode()
                if not @props.settings.get 'composeOnTop'
                    rect = node.getBoundingClientRect()
                    node.scrollTop = node.scrollHeight - rect.height
                    if (typeof node.selectionStart is "number")
                        node.selectionStart = node.selectionEnd = node.value.length
                    else if (typeof node.createTextRange isnt "undefined")
                        node.focus()
                        range = node.createTextRange()
                        range.collapse(false)
                        range.select()
                node.focus()
            else
                document.getElementById('compose-to').focus()

    componentDidMount: ->
        @_initCompose()

    #componentDidUpdate: ->
    #    @_initCompose()

    componentWillUnmount: ->
        if @_saveInterval
            window.clearInterval @_saveInterval
        if @state.isDraft and @state.id?
            if not window.confirm(t 'compose confirm keep draft')
                MessageActionCreator.delete @state.id, (error) ->
                    if error?
                        LayoutActionCreator.alertError "#{t("message action delete ko")} #{error}"
                    else
                        LayoutActionCreator.notify t('compose draft deleted')

    getInitialState: (forceDefault) ->

        # edition of an existing draft
        if message = @props.message
            state =
                composeInHTML: message.get('html')?

            # TODO : smarter ?
            state[key] = value for key, value of message.toJS()
            # we want the immutable attachments
            state.attachments = message.get 'attachments'

        # new draft
        else
            state = MessageUtils.makeReplyMessage @props.inReplyTo, @props.action,
                @props.settings.get 'composeInHTML'
            state.accountID ?= @props.selectedAccount.get 'id'

        state.sending = false
        return state

    componentWillReceiveProps: (nextProps) ->
        if nextProps.message isnt @props.message
            @props.message = nextProps.message
            @setState @getInitialState()

    onDraft: (args) ->
        @_doSend true

    onSend: (args) ->
        @_doSend false

    _doSend: (isDraft) ->

        account = @props.accounts.get @state.accountID

        from =
            name: account?.get('name') or undefined
            address: account.get('login')

        unless ~from.address.indexOf '@'
            from.address += '@' + account.get('imapServer')

        message =
            id          : @state.id
            accountID   : @state.accountID
            mailboxIDs  : @state.mailboxIDs
            from        : [from]
            to          : @state.to
            cc          : @state.cc
            bcc         : @state.bcc
            subject     : @state.subject
            isDraft     : isDraft
            attachments : @state.attachments

        valid = true
        if not isDraft
            if @state.to.length is 0 and @state.cc.length is 0 and @state.bcc.length is 0
                valid = false
                LayoutActionCreator.alertError t "compose error no dest"
                document.getElementById('compose-to').focus()
            else if @state.subject is ''
                valid = false
                LayoutActionCreator.alertError t "compose error no subject"
                @refs.subject.getDOMNode().focus()

        if valid
            node = @refs.html.getDOMNode()
            if @state.composeInHTML
                message.html    = node.innerHTML
                try
                    message.text = toMarkdown(message.html)
                catch
                    message.text = node.textContent or node.innerText
            else
                message.text = node.value.trim()

            callback = @props.callback

            if not isDraft and @_saveInterval
                window.clearInterval @_saveInterval

            if not isDraft
                @setState sending: true

            MessageActionCreator.send message, (error, message) =>
                @setState sending: false
                if isDraft
                    msgKo = t "message action draft ko"
                    msgOk = t "message action draft ok"
                else
                    msgKo = t "message action sent ko"
                    msgOk = t "message action sent ok"
                if error?
                    LayoutActionCreator.alertError "#{msgKo} :  error"
                else
                    LayoutActionCreator.notify msgOk
                    @setState message

                    if callback?
                        callback error
                    else if not isDraft
                        # mail sent close the pane
                        @redirect @buildClosePanelUrl @props.layout


    _autosave: ->
        @_doSend true

    onDelete: (args) ->
        if window.confirm(t 'mail confirm delete', {subject: @props.message.get('subject')})
            MessageActionCreator.delete @props.message, (error) =>
                if error?
                    LayoutActionCreator.alertError "#{t("message action delete ko")} #{error}"
                else
                    @redirect
                        direction: 'first'
                        action: 'account.mailbox.messages'
                        parameters: [@props.selectedAccount.get('id'), @props.selectedMailboxID, 1]
                        fullWidth: true
    onToggleCc: (e) -><commit_msg>Minor compose fixes<commit_after>{div, h3, a, i, textarea, form, label, button, span, ul, li, input} = React.DOM
classer = React.addons.classSet

FilePicker = require './file_picker'
MailsInput = require './mails_input'

AccountPicker = require './account_picker'

{ComposeActions} = require '../constants/app_constants'

MessageUtils = require '../utils/message_utils'

LayoutActionCreator  = require '../actions/layout_action_creator'
MessageActionCreator = require '../actions/message_action_creator'

RouterMixin = require '../mixins/router_mixin'

module.exports = Compose = React.createClass
    displayName: 'Compose'

    mixins: [
        RouterMixin,
        React.addons.LinkedStateMixin # two-way data binding
    ]

    propTypes:
        selectedAccount: React.PropTypes.object.isRequired
        layout:          React.PropTypes.string.isRequired
        accounts:        React.PropTypes.object.isRequired
        message:         React.PropTypes.object
        action:          React.PropTypes.string
        callback:        React.PropTypes.func
        settings:        React.PropTypes.object.isRequired

    shouldComponentUpdate: (nextProps, nextState) ->
        return not(_.isEqual(nextState, @state)) or not (_.isEqual(nextProps, @props))

    render: ->

        return unless @props.accounts

        expandUrl = @buildUrl
            direction: 'first'
            action: 'compose'
            fullWidth: true

        collapseUrl = @buildUrl
            firstPanel:
                action: 'account.mailbox.messages'
                parameters: @state.accountID
            secondPanel:
                action: 'compose'

        cancelUrl = @buildUrl
            direction: 'first'
            action: 'default'
            fullWidth: true

        closeUrl = @buildClosePanelUrl @props.layout

        classLabel = 'compose-label'
        classInput = 'compose-input'
        classCc    = if @state.cc.length is 0 then '' else ' shown'
        classBcc   = if @state.bcc.length is 0 then '' else ' shown'

        labelSend = if @state.sending then t 'compose action sending' else t 'compose action send'

        div id: 'email-compose',
            if @props.layout isnt 'full'
                a href: expandUrl, className: 'expand pull-right',
                    i className: 'fa fa-arrows-h'
            else
                a href: collapseUrl, className: 'close-email pull-right',
                    i className:'fa fa-compress'
            h3 null,
                t 'compose'
            form className: '',
                div className: 'form-group',
                    label
                        htmlFor: 'compose-from',
                        className: classLabel,
                        t "compose from"
                    div className: classInput,
                        div
                            className: 'btn-toolbar compose-toggle',
                            role: 'toolbar',
                                div null
                                    a
                                        className: 'compose-toggle-cc',
                                        onClick: @onToggleCc,
                                        t 'compose toggle cc'
                                    a
                                        className: 'compose-toggle-bcc',
                                        onClick: @onToggleBcc,
                                        t 'compose toggle bcc'

                        AccountPicker
                            accounts: @props.accounts
                            valueLink: @linkState 'accountID'
                            type: 'address'
                div className: 'clearfix', null

                MailsInput
                    id: 'compose-to'
                    valueLink: @linkState 'to'
                    label: t 'compose to'
                    ref: 'to'

                MailsInput
                    id: 'compose-cc'
                    className: 'compose-cc' + classCc
                    valueLink: @linkState 'cc'
                    label: t 'compose cc'
                    placeholder: t 'compose cc help'

                MailsInput
                    id: 'compose-bcc'
                    className: 'compose-bcc' + classBcc
                    valueLink: @linkState 'bcc'
                    label: t 'compose bcc'
                    placeholder: t 'compose bcc help'

                div className: 'form-group',
                    label
                        htmlFor: 'compose-subject',
                        className: classLabel,
                        t "compose subject"
                    div className: classInput,
                        input
                            id: 'compose-subject',
                            name: 'compose-subject',
                            ref: 'subject',
                            valueLink: @linkState('subject'),
                            type: 'text',
                            className: 'form-control',
                            placeholder: t "compose subject help"
                div className: '',
                    label
                        htmlFor: 'compose-subject',
                        className: classLabel,
                        t "compose content"
                    if @state.composeInHTML
                        div
                            className: 'rt-editor form-control',
                            ref: 'html',
                            contentEditable: true,
                            onKeyDown: @onKeyDown,
                            dangerouslySetInnerHTML: {
                                __html: @linkState('html').value
                            }
                    else
                        textarea
                            className: 'editor',
                            ref: 'content',
                            onKeyDown: @onKeyDown,
                            defaultValue: @linkState('text').value

                div className: 'attachements',
                    FilePicker
                        className: ''
                        editable: true
                        valueLink: @linkState 'attachments'

                div className: 'composeToolbox',
                    div className: 'btn-toolbar', role: 'toolbar',
                        div className: '',
                            button
                                className: 'btn btn-cozy',
                                type: 'button',
                                disable: if @state.sending then true else null
                                onClick: @onSend,
                                    span
                                        className: 'fa fa-send'
                                    span null, labelSend
                            button
                                className: 'btn btn-cozy',
                                type: 'button', onClick: @onDraft,
                                    span className: 'fa fa-save'
                                    span null, t 'compose action draft'
                            if @props.message?
                                button
                                    className: 'btn btn-cozy-non-default',
                                    type: 'button',
                                    onClick: @onDelete,
                                        span className: 'fa fa-trash-o'
                                        span null, t 'compose action delete'
                            a
                                href: cancelUrl,
                                className: 'btn btn-cozy-non-default',
                                t 'app cancel'
                div className: 'clearfix', null

    _initCompose: ->

        if @_saveInterval
            window.clearInterval @_saveInterval
        @_saveInterval = window.setInterval @_autosave, 30000

        # scroll compose window into view
        @getDOMNode().scrollIntoView()
        if @state.composeInHTML
            if Array.isArray(@state.to) and @state.to.length > 0 and @state.subject isnt ''
                node = @refs.html?.getDOMNode()
                if not node?
                    return
                jQuery(node).focus()
                if not @props.settings.get 'composeOnTop'
                    node = node.lastChild
                    if node?
                        # move cursor to the bottom
                        node.scrollIntoView(false)
                        node.innerHTML = "<br \>"
                        s = window.getSelection()
                        r = document.createRange()
                        r.selectNodeContents(node)
                        s.removeAllRanges()
                        s.addRange(r)
                        document.execCommand('delete', false, null)
            else
                document.getElementById('compose-to').focus()

            # Some DOM manipulation when replying inside the message.
            # When inserting a new line, we must close all blockquotes,
            # insert a blank line and then open again blockquotes
            jQuery('#email-compose .rt-editor').on('keypress', (e) ->
                if e.keyCode is 13
                    # timeout to let the editor perform its own stuff
                    setTimeout ->
                        matchesSelector = document.documentElement.matches or
                              document.documentElement.matchesSelector or
                              document.documentElement.webkitMatchesSelector or
                              document.documentElement.mozMatchesSelector or
                              document.documentElement.oMatchesSelector or
                              document.documentElement.msMatchesSelector

                        target = document.getSelection().anchorNode
                        if not target?
                            return
                        if matchesSelector? and not matchesSelector.call(target, '.rt-editor blockquote *')
                            # we are not inside a blockquote, nothing to do
                            return

                        if target.lastChild
                            target = target.lastChild.previousElementSibling
                        parent = target

                        # alternative 1
                        # we create 2 ranges, one from the begining of message
                        # to the caret position, the second from caret to the
                        # end. We then create fragments from the ranges and
                        # override message with first fragment, a blank line
                        # and second fragment
                        process = ->
                            current = parent
                            parent = parent?.parentNode
                        process()
                        process() while (parent? and
                            not parent.classList.contains 'rt-editor')
                        rangeBefore = document.createRange()
                        rangeBefore.setEnd target, 0
                        rangeBefore.setStartBefore parent.firstChild
                        rangeAfter = document.createRange()
                        if target.nextSibling?
                            # remove the BR the <enter> key probably inserted
                            rangeAfter.setStart target.nextSibling, 0
                        else
                            rangeAfter.setStart target, 0
                        rangeAfter.setEndAfter parent.lastChild
                        before = rangeBefore.cloneContents()
                        after = rangeAfter.cloneContents()
                        inserted = document.createElement 'p'
                        inserted.innerHTML = "<br />"
                        parent.innerHTML = ""
                        parent.appendChild before
                        parent.appendChild inserted
                        parent.appendChild after

                        ###
                        # alternative 2
                        # We move every node from the caret to the end of the
                        # message to a new DOM tree, then insert a blank line
                        # and the new tree
                        parent = target
                        p2 = null
                        p3 = null
                        process = ->
                            p3 = p2
                            current = parent
                            parent = parent.parentNode
                            p2 = parent.cloneNode false
                            if p3?
                                p2.appendChild p3
                            s = current.nextSibling
                            while s?
                                p2.appendChild(s.cloneNode(true))
                                s2 = s.nextSibling
                                parent.removeChild s
                                s = s2
                        process()
                        process() while (parent.parentNode? and
                            not parent.parentNode.classList.contains 'rt-editor')
                        after = p2
                        inserted = document.createElement 'p'
                        inserted.innerHTML = "<br />"
                        if parent.nextSibling
                            parent.parentNode.insertBefore inserted, parent.nextSibling
                            parent.parentNode.insertBefore after, parent.nextSibling
                        else
                            parent.parentNode.appendChild inserted
                            parent.parentNode.appendChild after
                        ###

                        inserted.focus()
                        sel = window.getSelection()
                        sel.collapse inserted, 0

                    , 0
            )
        else
            # Text message
            if Array.isArray(@state.to) and @state.to.length > 0 and @state.subject isnt ''
                node = @refs.content.getDOMNode()
                if not @props.settings.get 'composeOnTop'
                    rect = node.getBoundingClientRect()
                    node.scrollTop = node.scrollHeight - rect.height
                    if (typeof node.selectionStart is "number")
                        node.selectionStart = node.selectionEnd = node.value.length
                    else if (typeof node.createTextRange isnt "undefined")
                        node.focus()
                        range = node.createTextRange()
                        range.collapse(false)
                        range.select()
                node.focus()
            else
                document.getElementById('compose-to').focus()

    componentDidMount: ->
        @_initCompose()

    #componentDidUpdate: ->
    #    @_initCompose()

    componentWillUnmount: ->
        if @_saveInterval
            window.clearInterval @_saveInterval
        #if @state.isDraft and @state.id?
        #    if not window.confirm(t 'compose confirm keep draft')
        #        MessageActionCreator.delete @state.id, (error) ->
        #            if error?
        #                LayoutActionCreator.alertError "#{t("message action delete ko")} #{error}"
        #            else
        #                LayoutActionCreator.notify t('compose draft deleted')

    getInitialState: (forceDefault) ->

        # edition of an existing draft
        if message = @props.message
            state =
                composeInHTML: message.get('html')?

            # TODO : smarter ?
            state[key] = value for key, value of message.toJS()
            # we want the immutable attachments
            state.attachments = message.get 'attachments'

        # new draft
        else
            state = MessageUtils.makeReplyMessage @props.inReplyTo, @props.action,
                @props.settings.get 'composeInHTML'
            state.accountID ?= @props.selectedAccount.get 'id'

        state.sending = false
        return state

    componentWillReceiveProps: (nextProps) ->
        if nextProps.message isnt @props.message
            @props.message = nextProps.message
            @setState @getInitialState()

    onDraft: (args) ->
        @_doSend true

    onSend: (args) ->
        @_doSend false

    _doSend: (isDraft) ->

        account = @props.accounts.get @state.accountID

        from =
            name: account?.get('name') or undefined
            address: account.get('login')

        unless ~from.address.indexOf '@'
            from.address += '@' + account.get('imapServer')

        message =
            id          : @state.id
            accountID   : @state.accountID
            mailboxIDs  : @state.mailboxIDs
            from        : [from]
            to          : @state.to
            cc          : @state.cc
            bcc         : @state.bcc
            subject     : @state.subject
            isDraft     : isDraft
            attachments : @state.attachments

        valid = true
        if not isDraft
            if @state.to.length is 0 and @state.cc.length is 0 and @state.bcc.length is 0
                valid = false
                LayoutActionCreator.alertError t "compose error no dest"
                document.getElementById('compose-to').focus()
            else if @state.subject is ''
                valid = false
                LayoutActionCreator.alertError t "compose error no subject"
                @refs.subject.getDOMNode().focus()

        if valid
            node = @refs.html?.getDOMNode()
            if node?
                if @state.composeInHTML
                    message.html = node.innerHTML
                    try
                        message.text = toMarkdown(message.html)
                    catch
                        message.text = node.textContent or node.innerText
                else
                    message.text = node.value.trim()

            if not isDraft and @_saveInterval
                window.clearInterval @_saveInterval

            if not isDraft
                @setState sending: true

            MessageActionCreator.send message, (error, message) =>
                @setState sending: false
                if isDraft
                    msgKo = t "message action draft ko"
                    msgOk = t "message action draft ok"
                else
                    msgKo = t "message action sent ko"
                    msgOk = t "message action sent ok"
                if error?
                    LayoutActionCreator.alertError "#{msgKo} :  error"
                else
                    LayoutActionCreator.notify msgOk
                    @setState message

                    if not isDraft
                        if @props.callback?
                            @props.callback error
                        else
                            @redirect @buildClosePanelUrl @props.layout

    _autosave: ->
        @_doSend true

    onDelete: (args) ->
        if window.confirm(t 'mail confirm delete', {subject: @props.message.get('subject')})
            MessageActionCreator.delete @props.message, (error) =>
                if error?
                    LayoutActionCreator.alertError "#{t("message action delete ko")} #{error}"
                else
                    if @props.callback
                        @props.callback()
                    else
                        @redirect
                            direction: 'first'
                            action: 'account.mailbox.messages'
                            parameters: [@props.selectedAccount.get('id'), @props.selectedMailboxID, 1]
                            fullWidth: true

    onToggleCc: (e) ->
{'fraction_non_alphanumeric': 0.07035137605362039, 'fraction_numerical': 0.001878744795369148, 'mean_word_length': 1.031303181888505, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 60, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7540394', 'n_tokens_mistral': 8859, 'n_tokens_neox': 8316, 'n_words': 2245}