From 99d89b1b14cca04d92b36ae461ef810c8fa0a37e Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Wed, 20 Apr 2016 10:21:07 -0400 Subject: [PATCH] Editor: Prevent enter keypress or multi-line paste in title textarea --- client/post-editor/editor-title/index.jsx | 65 +++++++++++++++++++---- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/client/post-editor/editor-title/index.jsx b/client/post-editor/editor-title/index.jsx index 255bfc85d3c199..584d52a787a234 100644 --- a/client/post-editor/editor-title/index.jsx +++ b/client/post-editor/editor-title/index.jsx @@ -50,24 +50,31 @@ const EditorTitle = React.createClass( { const { isNew, site } = this.props; if ( ( isNew && ! prevProps.isNew ) || ( isNew && get( prevProps.site, 'ID' ) !== get( site, 'ID' ) ) ) { - ReactDom.findDOMNode( this.refs.titleInput ).focus(); + const input = ReactDom.findDOMNode( this.refs.titleInput ); + input.focus(); + } + + // If value updated via paste, restore caret location + if ( this.selectionStart ) { + const input = ReactDom.findDOMNode( this.refs.titleInput ); + input.setSelectionRange( this.selectionStart, this.selectionStart ); + delete this.selectionStart; } }, - onChange( event ) { - const { post, onChange } = this.props; + setTitle( title ) { + // TODO: REDUX - remove flux actions when whole post-editor is reduxified + PostActions.edit( { title } ); + this.props.setTitle( title ); + }, - if ( ! post ) { + onChange( event ) { + if ( ! this.props.post ) { return; } - // TODO: REDUX - remove flux actions when whole post-editor is reduxified - PostActions.edit( { - title: event.target.value - } ); - - this.props.setTitle( event.target.value ); - onChange( event ); + this.setTitle( event.target.value ); + this.props.onChange( event ); }, recordChangeStats() { @@ -80,6 +87,40 @@ const EditorTitle = React.createClass( { this.onChange( event ); }, + stripPasteNewlines( event ) { + // `window.clipboardData` deprecated as of Microsoft Edge + // See: https://msdn.microsoft.com/library/ms535220(v=vs.85).aspx + const clipboard = event.clipboardData || window.clipboardData; + if ( ! clipboard ) { + return; + } + + event.preventDefault(); + + let text = clipboard.getData( 'Text' ) || clipboard.getData( 'text/plain' ); + if ( ! text ) { + return; + } + + // Replace any whitespace (including newlines) with a single space + text = text.replace( /\s+/g, ' ' ).trim(); + + // Splice trimmed text into current title selection + const { value, selectionStart, selectionEnd } = event.target; + const valueChars = value.split( '' ); + valueChars.splice( selectionStart, selectionEnd - selectionStart, text ); + const newTitle = valueChars.join( '' ); + + this.setTitle( newTitle ); + this.selectionStart = selectionStart + text.length; + }, + + preventEnterKeyPress( event ) { + if ( 'Enter' === event.key || 13 === event.charCode ) { + event.preventDefault(); + } + }, + render() { const { post, site, isNew } = this.props; const isPermalinkEditable = SiteUtils.isPermalinkEditable( site ); @@ -103,6 +144,8 @@ const EditorTitle = React.createClass( { placeholder={ this.translate( 'Title' ) } onChange={ this.onChange } onBlur={ this.onBlur } + onPaste={ this.stripPasteNewlines } + onKeyPress={ this.preventEnterKeyPress } autoFocus={ isNew && ! isMobile() } value={ post ? post.title : '' } aria-label={ this.translate( 'Edit title' ) }