Skip to content

Commit

Permalink
Fix copy regression with move to EditorStore
Browse files Browse the repository at this point in the history
Using the flux store does not allow return values from store methods.
However, the copy/paste requires a bunch of UI interaction to workaround
IE limitations. Therefore, simply pass callbacks to the store to have the
store do the right thing.
  • Loading branch information
rocketraman committed Jul 21, 2015
1 parent 62c39a7 commit 54e82c6
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 77 deletions.
23 changes: 8 additions & 15 deletions src/components/TextInput.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import 'babel/polyfill'

import _ from 'lodash'
import React from 'react/addons'
import getEventKey from 'react/lib/getEventKey'
import Mousetrap from 'mousetrap'

import EditorActions from '../flux/EditorActions'
import parseHtml from '../core/htmlparser'
import writeHtml from '../core/htmlwriter'
import writeText from '../core/textwriter'
import {emptyNode} from '../core/dom'

const T = React.PropTypes
Expand Down Expand Up @@ -301,30 +300,24 @@ export default React.createClass({
},

_onCopy(e) {
let selectionChunks = EditorActions.getSelection()

if(selectionChunks && selectionChunks.length > 0) {
let copiedText = writeText(selectionChunks)
let copiedHtml = writeHtml(selectionChunks)
if(window.clipboardData) {
this._handleIeCopy(copiedText, copiedHtml)
} else {
this._handleNormalCopy(e, selectionChunks, copiedText, copiedHtml)
}
}
let copyHandler = window.clipboardData ?
_.partial(this._handleIeCopy) :
_.partial(this._handleNormalCopy, e)

EditorActions.copySelection(copyHandler)
},

_onCut(e) {
this._onCopy(e)
EditorActions.eraseSelection()
},

_handleNormalCopy(e, selectionChunks, copiedText, copiedHtml) {
_handleNormalCopy(e, copiedText, copiedHtml, copiedRich) {
e.clipboardData.setData(MIME_TYPE_TEXT_PLAIN, copiedText)
e.clipboardData.setData(MIME_TYPE_TEXT_HTML, copiedHtml)
// some browsers e.g. Chrome support arbitrary MIME types, makes paste way more efficient
// Firefox allows setting the type, but not pasting it -- see https://bugzilla.mozilla.org/show_bug.cgi?id=860857
e.clipboardData.setData(MIME_TYPE_RITZY_RICH_TEXT, JSON.stringify(selectionChunks))
e.clipboardData.setData(MIME_TYPE_RITZY_RICH_TEXT, JSON.stringify(copiedRich))
e.preventDefault()
e.stopPropagation()
},
Expand Down
4 changes: 2 additions & 2 deletions src/flux/EditorActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ class EditorActions {
this.dispatch()
}

getSelection() {
this.dispatch()
copySelection(copyHandler) {
this.dispatch(copyHandler)
}

insertChars(value, attributes, atPosition) {
Expand Down
131 changes: 71 additions & 60 deletions src/flux/EditorStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import EditorActions from './EditorActions'
import { BASE_CHAR, EOF } from 'RichText'
import { pushArray/*, logInGroup*/ } from 'utils'
import { default as tokenizer, isWhitespace } from 'tokenizer'
import writeHtml from '../core/htmlwriter'
import writeText from '../core/textwriter'
import TextFontMetrics from '../core/TextFontMetrics'
import { lineContainingChar } from '../core/EditorUtils'

Expand Down Expand Up @@ -271,66 +273,13 @@ class EditorStore {
}
}

getSelection() {
let selectionChunks = []

if(!this.state.selectionActive) {
return selectionChunks
}

let currentChunk = {
chars: [],
attributes: null,

reset() {
this.chars = []
this.attributes = null
},

pushChar(c) {
if(!this.attributes) {
this.attributes = c.attributes
}
// push newlines as separate chunks for ease of parsing paragraphs and breaks from chunks
if(c.char === '\n') {
// previous chunk
this.pushChunk()
}
this.chars.push(c.char)
if(c.char === '\n') {
// newline chunk
this.pushChunk()
}
},

pushChunk() {
if(this.chars.length > 0) {
selectionChunks.push({
text: this.chars.join(''),
attrs: this.attributes
})
}
this.reset()
}
}

let processChar = (c) => {
if (!attributesEqual(currentChunk.attributes, c.attributes)) {
currentChunk.pushChunk()
}
currentChunk.pushChar(c, this)
copySelection(copyHandler) {
let selectionChunks = this._getSelection()
if(selectionChunks && selectionChunks.length > 0) {
let copiedText = writeText(selectionChunks)
let copiedHtml = writeHtml(selectionChunks)
copyHandler(copiedText, copiedHtml, selectionChunks)
}

let selectionChars = this.replica.getTextRange(this.state.selectionLeftChar, this.state.selectionRightChar)
let contentIterator = selectionChars[Symbol.iterator]()
let e
while(!(e = contentIterator.next()).done) {
processChar(e.value)
}
// last chunk
currentChunk.pushChunk()

return selectionChunks
}

insertChars({value, attributes, atPosition, reflow}) {
Expand All @@ -340,7 +289,7 @@ class EditorStore {
insertCharsBatch(chunks) {
let insertPosition = null
chunks.forEach(c => {
insertPosition = this._insertChars(c.text, c.attrs, insertPosition, false)
insertPosition = this._insertChars(c.text, c.attrs || {}, insertPosition, false)
})
this._flow()
}
Expand Down Expand Up @@ -778,6 +727,68 @@ class EditorStore {
|| (this._lastLine().isEof() && this.replica.charEq(this.state.position, this._lastLine().start))
}

_getSelection() {
let selectionChunks = []

if(!this.state.selectionActive) {
return selectionChunks
}

let currentChunk = {
chars: [],
attributes: null,

reset() {
this.chars = []
this.attributes = null
},

pushChar(c) {
if(!this.attributes) {
this.attributes = c.attributes
}
// push newlines as separate chunks for ease of parsing paragraphs and breaks from chunks
if(c.char === '\n') {
// previous chunk
this.pushChunk()
}
this.chars.push(c.char)
if(c.char === '\n') {
// newline chunk
this.pushChunk()
}
},

pushChunk() {
if(this.chars.length > 0) {
selectionChunks.push({
text: this.chars.join(''),
attrs: this.attributes
})
}
this.reset()
}
}

let processChar = (c) => {
if (!attributesEqual(currentChunk.attributes, c.attributes)) {
currentChunk.pushChunk()
}
currentChunk.pushChar(c, this)
}

let selectionChars = this.replica.getTextRange(this.state.selectionLeftChar, this.state.selectionRightChar)
let contentIterator = selectionChars[Symbol.iterator]()
let e
while(!(e = contentIterator.next()).done) {
processChar(e.value)
}
// last chunk
currentChunk.pushChunk()

return selectionChunks
}

_modifySelection(toChar, positionEolStart, resetUpDown) {
if(!toChar) return
if(_.isUndefined(resetUpDown)) resetUpDown = true
Expand Down

0 comments on commit 54e82c6

Please sign in to comment.