From 54e82c6a1dc0329b33d3a01dc08ae488697d02e2 Mon Sep 17 00:00:00 2001 From: Raman Gupta Date: Tue, 21 Jul 2015 18:07:05 -0400 Subject: [PATCH] Fix copy regression with move to EditorStore 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. --- src/components/TextInput.js | 23 +++---- src/flux/EditorActions.js | 4 +- src/flux/EditorStore.js | 131 +++++++++++++++++++----------------- 3 files changed, 81 insertions(+), 77 deletions(-) diff --git a/src/components/TextInput.js b/src/components/TextInput.js index 1468d2a..48d9de1 100644 --- a/src/components/TextInput.js +++ b/src/components/TextInput.js @@ -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 @@ -301,17 +300,11 @@ 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) { @@ -319,12 +312,12 @@ export default React.createClass({ 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() }, diff --git a/src/flux/EditorActions.js b/src/flux/EditorActions.js index 72ecad8..3d25a3c 100644 --- a/src/flux/EditorActions.js +++ b/src/flux/EditorActions.js @@ -134,8 +134,8 @@ class EditorActions { this.dispatch() } - getSelection() { - this.dispatch() + copySelection(copyHandler) { + this.dispatch(copyHandler) } insertChars(value, attributes, atPosition) { diff --git a/src/flux/EditorStore.js b/src/flux/EditorStore.js index 1b6aaaf..043e4fb 100644 --- a/src/flux/EditorStore.js +++ b/src/flux/EditorStore.js @@ -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' @@ -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}) { @@ -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() } @@ -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