// Call the CKEDITOR.event constructor to initialize this instance.
CKEDITOR.event.call( this );
- // Make a clone of the config object, to avoid having it touched by our code. (#9636)
+ // Make a clone of the config object, to avoid having it touched by our code. (http://dev.ckeditor.com/ticket/9636)
instanceConfig = instanceConfig && CKEDITOR.tools.clone( instanceConfig );
// if editor is created off one page element.
function updateCommandsContext( editor, path, forceRefresh ) {
// Commands cannot be refreshed without a path. In edge cases
// it may happen that there's no selection when this function is executed.
- // For example when active filter is changed in #10877.
+ // For example when active filter is changed in http://dev.ckeditor.com/ticket/10877.
if ( !path )
return;
} else {
// Load the custom configuration file.
// To resolve customConfig race conflicts, use scriptLoader#queue
- // instead of scriptLoader#load (#6504).
+ // instead of scriptLoader#load (http://dev.ckeditor.com/ticket/6504).
CKEDITOR.scriptLoader.queue( customConfig, function() {
// If the CKEDITOR.editorConfig function has been properly
// defined in the custom configuration file, cache it.
return editor.blockless ? CKEDITOR.ENTER_BR : enterMode;
}
- // Create DocumentFragment from specified ranges. For now it handles only tables in Firefox
- // and returns DocumentFragment from the 1. range for other cases. (#13884)
+ // Create DocumentFragment from specified ranges. For now it handles only tables
+ // and returns DocumentFragment from the 1. range for other cases. (http://dev.ckeditor.com/ticket/13884)
function createDocumentFragmentFromRanges( ranges, editable ) {
var docFragment = new CKEDITOR.dom.documentFragment(),
tableClone,
currentRow,
currentRowClone;
+ // We must handle two cases here:
+ // 1. <tr>[<td>Cell</td>]</tr> (IE9+, Edge, Chrome, Firefox)
+ // 2. <td>[Cell]</td> (IE8-, Safari)
+ function isSelectedCell( range ) {
+ var start = range.startContainer,
+ end = range.endContainer;
+
+ if ( start.is && ( start.is( 'tr' ) ||
+ ( start.is( 'td' ) && start.equals( end ) && range.endOffset === start.getChildCount() ) ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ function cloneCell( range ) {
+ var start = range.startContainer;
+
+ if ( start.is( 'tr' ) ) {
+ return range.cloneContents();
+ }
+
+ return start.clone( true );
+ }
+
for ( var i = 0; i < ranges.length; i++ ) {
var range = ranges[ i ],
- container = range.startContainer;
+ container = range.startContainer.getAscendant( 'tr', true );
- if ( container.getName && container.getName() == 'tr' ) {
+ if ( isSelectedCell( range ) ) {
if ( !tableClone ) {
tableClone = container.getAscendant( 'table' ).clone();
- tableClone.append( container.getAscendant( 'tbody' ).clone() );
+ tableClone.append( container.getAscendant( { thead: 1, tbody: 1, tfoot: 1 } ).clone() );
docFragment.append( tableClone );
- tableClone = tableClone.findOne( 'tbody' );
+ tableClone = tableClone.findOne( 'thead, tbody, tfoot' );
}
if ( !( currentRow && currentRow.equals( container ) ) ) {
tableClone.append( currentRowClone );
}
- currentRowClone.append( range.cloneContents() );
+ currentRowClone.append( cloneCell( range ) );
} else {
// If there was something else copied with table,
// append it to DocumentFragment.
// This guarantees that commands added before first editor#mode
// aren't immediately updated, but waits for editor#mode and that
// commands added later are immediately refreshed, even when added
- // before instanceReady. #10103, #10249
+ // before instanceReady. http://dev.ckeditor.com/ticket/10103, http://dev.ckeditor.com/ticket/10249
if ( this.mode )
updateCommand( this, cmd );
} );
}
- // Remove 'submit' events registered on form element before destroying.(#3988)
+ // Remove 'submit' events registered on form element before destroying.(http://dev.ckeditor.com/ticket/3988)
editor.on( 'destroy', function() {
form.removeListener( 'submit', onSubmit );
} );
function onSubmit( evt ) {
editor.updateElement();
- // #8031 If textarea had required attribute and editor is empty fire 'required' event and if
+ // http://dev.ckeditor.com/ticket/8031 If textarea had required attribute and editor is empty fire 'required' event and if
// it was cancelled, prevent submitting the form.
if ( editor._.required && !element.getValue() && editor.fire( 'required' ) === false ) {
// When user press save button event (evt) is undefined (see save plugin).
*
* editorInstance.execCommand( 'bold' );
*
- * @param {String} commandName The indentifier name of the command.
- * @param {Object} [data] The data to be passed to the command.
- * @returns {Boolean} `true` if the command was executed
- * successfully, otherwise `false`.
+ * @param {String} commandName The identifier name of the command.
+ * @param {Object} [data] The data to be passed to the command. It defaults to
+ * an empty object starting from 4.7.0.
+ * @returns {Boolean} `true` if the command was executed successfully, `false` otherwise.
* @see CKEDITOR.editor#addCommand
*/
execCommand: function( commandName, data ) {
var eventData = {
name: commandName,
- commandData: data,
+ commandData: data || {},
command: command
};
}
else {
// If we don't have a proper element, set data to an empty string,
- // as this method is expected to return a string. (#13385)
+ // as this method is expected to return a string. (http://dev.ckeditor.com/ticket/13385)
data = '';
}
}
this.readOnly = isReadOnly;
// Block or release BACKSPACE key according to current read-only
- // state to prevent browser's history navigation (#9761).
+ // state to prevent browser's history navigation (http://dev.ckeditor.com/ticket/9761).
this.keystrokeHandler.blockedKeystrokes[ 8 ] = +isReadOnly;
this.editable().setReadOnly( isReadOnly );
*/
extractSelectedHtml: function( toString, removeEmptyBlock ) {
var editable = this.editable(),
- ranges = this.getSelection().getRanges();
+ ranges = this.getSelection().getRanges(),
+ docFragment = new CKEDITOR.dom.documentFragment(),
+ i;
if ( !editable || ranges.length === 0 ) {
return null;
}
- var range = ranges[ 0 ],
- docFragment = editable.extractHtmlFromRange( range, removeEmptyBlock );
+ for ( i = 0; i < ranges.length; i++ ) {
+ docFragment.append( editable.extractHtmlFromRange( ranges[ i ], removeEmptyBlock ) );
+ }
if ( !removeEmptyBlock ) {
- this.getSelection().selectRanges( [ range ] );
+ this.getSelection().selectRanges( [ ranges[ 0 ] ] );
}
return toString ? docFragment.getHtml() : docFragment;
/**
* Returns the keystroke that is assigned to a specified {@link CKEDITOR.command}. If no keystroke is assigned,
- * it returns null.
+ * it returns `null`.
+ *
+ * Since version 4.7.0 this function also accepts a `command` parameter as a string.
*
* @since 4.6.0
- * @param {CKEDITOR.command} command
- * @returns {Number} The keystroke assigned to the provided command or null if there is no keystroke.
+ * @param {CKEDITOR.command/String} command The {@link CKEDITOR.command} instance or a string with the command name.
+ * @returns {Number/null} The keystroke assigned to the provided command or `null` if there is no keystroke.
*/
getCommandKeystroke: function( command ) {
- var commandName = command.name,
- keystrokes = this.keystrokeHandler.keystrokes,
- key;
+ var commandInstance = ( typeof command === 'string' ? this.getCommand( command ) : command );
- // Some commands have a fake keystroke - for example CUT/COPY/PASTE commands are handled natively.
- if ( command.fakeKeystroke ) {
- return command.fakeKeystroke;
- }
+ if ( commandInstance ) {
+ var commandName = CKEDITOR.tools.object.findKey( this.commands, commandInstance ),
+ keystrokes = this.keystrokeHandler.keystrokes,
+ key;
- for ( key in keystrokes ) {
- if ( keystrokes.hasOwnProperty( key ) && keystrokes[ key ] == commandName ) {
- return key;
+ // Some commands have a fake keystroke - for example CUT/COPY/PASTE commands are handled natively.
+ if ( commandInstance.fakeKeystroke ) {
+ return commandInstance.fakeKeystroke;
}
- }
+ for ( key in keystrokes ) {
+ if ( keystrokes.hasOwnProperty( key ) && keystrokes[ key ] == commandName ) {
+ return key;
+ }
+ }
+ }
return null;
},
* @member CKEDITOR.config
*/
- /**
+/**
* Customizes the {@link CKEDITOR.editor#title human-readable title} of this editor. This title is displayed in
* tooltips and impacts various [accessibility aspects](#!/guide/dev_a11y-section-announcing-the-editor-on-the-page),
* e.g. it is commonly used by screen readers for distinguishing editor instances and for navigation.