aboutsummaryrefslogtreecommitdiff
path: root/sources/core/editor.js
diff options
context:
space:
mode:
Diffstat (limited to 'sources/core/editor.js')
-rw-r--r--sources/core/editor.js111
1 files changed, 72 insertions, 39 deletions
diff --git a/sources/core/editor.js b/sources/core/editor.js
index 8dfce7f..640cec0 100644
--- a/sources/core/editor.js
+++ b/sources/core/editor.js
@@ -29,7 +29,7 @@
29 // Call the CKEDITOR.event constructor to initialize this instance. 29 // Call the CKEDITOR.event constructor to initialize this instance.
30 CKEDITOR.event.call( this ); 30 CKEDITOR.event.call( this );
31 31
32 // Make a clone of the config object, to avoid having it touched by our code. (#9636) 32 // Make a clone of the config object, to avoid having it touched by our code. (http://dev.ckeditor.com/ticket/9636)
33 instanceConfig = instanceConfig && CKEDITOR.tools.clone( instanceConfig ); 33 instanceConfig = instanceConfig && CKEDITOR.tools.clone( instanceConfig );
34 34
35 // if editor is created off one page element. 35 // if editor is created off one page element.
@@ -223,7 +223,7 @@
223 function updateCommandsContext( editor, path, forceRefresh ) { 223 function updateCommandsContext( editor, path, forceRefresh ) {
224 // Commands cannot be refreshed without a path. In edge cases 224 // Commands cannot be refreshed without a path. In edge cases
225 // it may happen that there's no selection when this function is executed. 225 // it may happen that there's no selection when this function is executed.
226 // For example when active filter is changed in #10877. 226 // For example when active filter is changed in http://dev.ckeditor.com/ticket/10877.
227 if ( !path ) 227 if ( !path )
228 return; 228 return;
229 229
@@ -270,7 +270,7 @@
270 } else { 270 } else {
271 // Load the custom configuration file. 271 // Load the custom configuration file.
272 // To resolve customConfig race conflicts, use scriptLoader#queue 272 // To resolve customConfig race conflicts, use scriptLoader#queue
273 // instead of scriptLoader#load (#6504). 273 // instead of scriptLoader#load (http://dev.ckeditor.com/ticket/6504).
274 CKEDITOR.scriptLoader.queue( customConfig, function() { 274 CKEDITOR.scriptLoader.queue( customConfig, function() {
275 // If the CKEDITOR.editorConfig function has been properly 275 // If the CKEDITOR.editorConfig function has been properly
276 // defined in the custom configuration file, cache it. 276 // defined in the custom configuration file, cache it.
@@ -654,24 +654,49 @@
654 return editor.blockless ? CKEDITOR.ENTER_BR : enterMode; 654 return editor.blockless ? CKEDITOR.ENTER_BR : enterMode;
655 } 655 }
656 656
657 // Create DocumentFragment from specified ranges. For now it handles only tables in Firefox 657 // Create DocumentFragment from specified ranges. For now it handles only tables
658 // and returns DocumentFragment from the 1. range for other cases. (#13884) 658 // and returns DocumentFragment from the 1. range for other cases. (http://dev.ckeditor.com/ticket/13884)
659 function createDocumentFragmentFromRanges( ranges, editable ) { 659 function createDocumentFragmentFromRanges( ranges, editable ) {
660 var docFragment = new CKEDITOR.dom.documentFragment(), 660 var docFragment = new CKEDITOR.dom.documentFragment(),
661 tableClone, 661 tableClone,
662 currentRow, 662 currentRow,
663 currentRowClone; 663 currentRowClone;
664 664
665 // We must handle two cases here:
666 // 1. <tr>[<td>Cell</td>]</tr> (IE9+, Edge, Chrome, Firefox)
667 // 2. <td>[Cell]</td> (IE8-, Safari)
668 function isSelectedCell( range ) {
669 var start = range.startContainer,
670 end = range.endContainer;
671
672 if ( start.is && ( start.is( 'tr' ) ||
673 ( start.is( 'td' ) && start.equals( end ) && range.endOffset === start.getChildCount() ) ) ) {
674 return true;
675 }
676
677 return false;
678 }
679
680 function cloneCell( range ) {
681 var start = range.startContainer;
682
683 if ( start.is( 'tr' ) ) {
684 return range.cloneContents();
685 }
686
687 return start.clone( true );
688 }
689
665 for ( var i = 0; i < ranges.length; i++ ) { 690 for ( var i = 0; i < ranges.length; i++ ) {
666 var range = ranges[ i ], 691 var range = ranges[ i ],
667 container = range.startContainer; 692 container = range.startContainer.getAscendant( 'tr', true );
668 693
669 if ( container.getName && container.getName() == 'tr' ) { 694 if ( isSelectedCell( range ) ) {
670 if ( !tableClone ) { 695 if ( !tableClone ) {
671 tableClone = container.getAscendant( 'table' ).clone(); 696 tableClone = container.getAscendant( 'table' ).clone();
672 tableClone.append( container.getAscendant( 'tbody' ).clone() ); 697 tableClone.append( container.getAscendant( { thead: 1, tbody: 1, tfoot: 1 } ).clone() );
673 docFragment.append( tableClone ); 698 docFragment.append( tableClone );
674 tableClone = tableClone.findOne( 'tbody' ); 699 tableClone = tableClone.findOne( 'thead, tbody, tfoot' );
675 } 700 }
676 701
677 if ( !( currentRow && currentRow.equals( container ) ) ) { 702 if ( !( currentRow && currentRow.equals( container ) ) ) {
@@ -680,7 +705,7 @@
680 tableClone.append( currentRowClone ); 705 tableClone.append( currentRowClone );
681 } 706 }
682 707
683 currentRowClone.append( range.cloneContents() ); 708 currentRowClone.append( cloneCell( range ) );
684 } else { 709 } else {
685 // If there was something else copied with table, 710 // If there was something else copied with table,
686 // append it to DocumentFragment. 711 // append it to DocumentFragment.
@@ -717,7 +742,7 @@
717 // This guarantees that commands added before first editor#mode 742 // This guarantees that commands added before first editor#mode
718 // aren't immediately updated, but waits for editor#mode and that 743 // aren't immediately updated, but waits for editor#mode and that
719 // commands added later are immediately refreshed, even when added 744 // commands added later are immediately refreshed, even when added
720 // before instanceReady. #10103, #10249 745 // before instanceReady. http://dev.ckeditor.com/ticket/10103, http://dev.ckeditor.com/ticket/10249
721 if ( this.mode ) 746 if ( this.mode )
722 updateCommand( this, cmd ); 747 updateCommand( this, cmd );
723 748
@@ -761,7 +786,7 @@
761 } ); 786 } );
762 } 787 }
763 788
764 // Remove 'submit' events registered on form element before destroying.(#3988) 789 // Remove 'submit' events registered on form element before destroying.(http://dev.ckeditor.com/ticket/3988)
765 editor.on( 'destroy', function() { 790 editor.on( 'destroy', function() {
766 form.removeListener( 'submit', onSubmit ); 791 form.removeListener( 'submit', onSubmit );
767 } ); 792 } );
@@ -771,7 +796,7 @@
771 function onSubmit( evt ) { 796 function onSubmit( evt ) {
772 editor.updateElement(); 797 editor.updateElement();
773 798
774 // #8031 If textarea had required attribute and editor is empty fire 'required' event and if 799 // http://dev.ckeditor.com/ticket/8031 If textarea had required attribute and editor is empty fire 'required' event and if
775 // it was cancelled, prevent submitting the form. 800 // it was cancelled, prevent submitting the form.
776 if ( editor._.required && !element.getValue() && editor.fire( 'required' ) === false ) { 801 if ( editor._.required && !element.getValue() && editor.fire( 'required' ) === false ) {
777 // When user press save button event (evt) is undefined (see save plugin). 802 // When user press save button event (evt) is undefined (see save plugin).
@@ -860,10 +885,10 @@
860 * 885 *
861 * editorInstance.execCommand( 'bold' ); 886 * editorInstance.execCommand( 'bold' );
862 * 887 *
863 * @param {String} commandName The indentifier name of the command. 888 * @param {String} commandName The identifier name of the command.
864 * @param {Object} [data] The data to be passed to the command. 889 * @param {Object} [data] The data to be passed to the command. It defaults to
865 * @returns {Boolean} `true` if the command was executed 890 * an empty object starting from 4.7.0.
866 * successfully, otherwise `false`. 891 * @returns {Boolean} `true` if the command was executed successfully, `false` otherwise.
867 * @see CKEDITOR.editor#addCommand 892 * @see CKEDITOR.editor#addCommand
868 */ 893 */
869 execCommand: function( commandName, data ) { 894 execCommand: function( commandName, data ) {
@@ -871,7 +896,7 @@
871 896
872 var eventData = { 897 var eventData = {
873 name: commandName, 898 name: commandName,
874 commandData: data, 899 commandData: data || {},
875 command: command 900 command: command
876 }; 901 };
877 902
@@ -967,7 +992,7 @@
967 } 992 }
968 else { 993 else {
969 // If we don't have a proper element, set data to an empty string, 994 // If we don't have a proper element, set data to an empty string,
970 // as this method is expected to return a string. (#13385) 995 // as this method is expected to return a string. (http://dev.ckeditor.com/ticket/13385)
971 data = ''; 996 data = '';
972 } 997 }
973 } 998 }
@@ -1069,7 +1094,7 @@
1069 this.readOnly = isReadOnly; 1094 this.readOnly = isReadOnly;
1070 1095
1071 // Block or release BACKSPACE key according to current read-only 1096 // Block or release BACKSPACE key according to current read-only
1072 // state to prevent browser's history navigation (#9761). 1097 // state to prevent browser's history navigation (http://dev.ckeditor.com/ticket/9761).
1073 this.keystrokeHandler.blockedKeystrokes[ 8 ] = +isReadOnly; 1098 this.keystrokeHandler.blockedKeystrokes[ 8 ] = +isReadOnly;
1074 1099
1075 this.editable().setReadOnly( isReadOnly ); 1100 this.editable().setReadOnly( isReadOnly );
@@ -1198,17 +1223,20 @@
1198 */ 1223 */
1199 extractSelectedHtml: function( toString, removeEmptyBlock ) { 1224 extractSelectedHtml: function( toString, removeEmptyBlock ) {
1200 var editable = this.editable(), 1225 var editable = this.editable(),
1201 ranges = this.getSelection().getRanges(); 1226 ranges = this.getSelection().getRanges(),
1227 docFragment = new CKEDITOR.dom.documentFragment(),
1228 i;
1202 1229
1203 if ( !editable || ranges.length === 0 ) { 1230 if ( !editable || ranges.length === 0 ) {
1204 return null; 1231 return null;
1205 } 1232 }
1206 1233
1207 var range = ranges[ 0 ], 1234 for ( i = 0; i < ranges.length; i++ ) {
1208 docFragment = editable.extractHtmlFromRange( range, removeEmptyBlock ); 1235 docFragment.append( editable.extractHtmlFromRange( ranges[ i ], removeEmptyBlock ) );
1236 }
1209 1237
1210 if ( !removeEmptyBlock ) { 1238 if ( !removeEmptyBlock ) {
1211 this.getSelection().selectRanges( [ range ] ); 1239 this.getSelection().selectRanges( [ ranges[ 0 ] ] );
1212 } 1240 }
1213 1241
1214 return toString ? docFragment.getHtml() : docFragment; 1242 return toString ? docFragment.getHtml() : docFragment;
@@ -1325,28 +1353,33 @@
1325 1353
1326 /** 1354 /**
1327 * Returns the keystroke that is assigned to a specified {@link CKEDITOR.command}. If no keystroke is assigned, 1355 * Returns the keystroke that is assigned to a specified {@link CKEDITOR.command}. If no keystroke is assigned,
1328 * it returns null. 1356 * it returns `null`.
1357 *
1358 * Since version 4.7.0 this function also accepts a `command` parameter as a string.
1329 * 1359 *
1330 * @since 4.6.0 1360 * @since 4.6.0
1331 * @param {CKEDITOR.command} command 1361 * @param {CKEDITOR.command/String} command The {@link CKEDITOR.command} instance or a string with the command name.
1332 * @returns {Number} The keystroke assigned to the provided command or null if there is no keystroke. 1362 * @returns {Number/null} The keystroke assigned to the provided command or `null` if there is no keystroke.
1333 */ 1363 */
1334 getCommandKeystroke: function( command ) { 1364 getCommandKeystroke: function( command ) {
1335 var commandName = command.name, 1365 var commandInstance = ( typeof command === 'string' ? this.getCommand( command ) : command );
1336 keystrokes = this.keystrokeHandler.keystrokes,
1337 key;
1338 1366
1339 // Some commands have a fake keystroke - for example CUT/COPY/PASTE commands are handled natively. 1367 if ( commandInstance ) {
1340 if ( command.fakeKeystroke ) { 1368 var commandName = CKEDITOR.tools.object.findKey( this.commands, commandInstance ),
1341 return command.fakeKeystroke; 1369 keystrokes = this.keystrokeHandler.keystrokes,
1342 } 1370 key;
1343 1371
1344 for ( key in keystrokes ) { 1372 // Some commands have a fake keystroke - for example CUT/COPY/PASTE commands are handled natively.
1345 if ( keystrokes.hasOwnProperty( key ) && keystrokes[ key ] == commandName ) { 1373 if ( commandInstance.fakeKeystroke ) {
1346 return key; 1374 return commandInstance.fakeKeystroke;
1347 } 1375 }
1348 }
1349 1376
1377 for ( key in keystrokes ) {
1378 if ( keystrokes.hasOwnProperty( key ) && keystrokes[ key ] == commandName ) {
1379 return key;
1380 }
1381 }
1382 }
1350 return null; 1383 return null;
1351 }, 1384 },
1352 1385
@@ -1539,7 +1572,7 @@ CKEDITOR.ELEMENT_MODE_INLINE = 3;
1539 * @member CKEDITOR.config 1572 * @member CKEDITOR.config
1540 */ 1573 */
1541 1574
1542 /** 1575/**
1543 * Customizes the {@link CKEDITOR.editor#title human-readable title} of this editor. This title is displayed in 1576 * Customizes the {@link CKEDITOR.editor#title human-readable title} of this editor. This title is displayed in
1544 * tooltips and impacts various [accessibility aspects](#!/guide/dev_a11y-section-announcing-the-editor-on-the-page), 1577 * tooltips and impacts various [accessibility aspects](#!/guide/dev_a11y-section-announcing-the-editor-on-the-page),
1545 * e.g. it is commonly used by screen readers for distinguishing editor instances and for navigation. 1578 * e.g. it is commonly used by screen readers for distinguishing editor instances and for navigation.