diff --git a/CHANGES.md b/CHANGES.md index 8a15e0b7b30..9816814f9a6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ CKEditor 4 Changelog ## CKEditor 4.1 * [#9907](http://dev.ckeditor.com/ticket/9907): Added contentPreview event for preview data manipulation. +* [#9713](http://dev.ckeditor.com/ticket/9713): Introduced `sourcedialog` plugin that brings raw HTML editing for inline editor instances. ## CKEditor 4.0.2 diff --git a/core/dom/node.js b/core/dom/node.js index c08bcd3eb85..f991518aa18 100644 --- a/core/dom/node.js +++ b/core/dom/node.js @@ -560,7 +560,11 @@ CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, { if ( $.nodeName && ( name = $.nodeName.toLowerCase(), ( typeof reference == 'string' ? name == reference : name in reference ) ) ) return new CKEDITOR.dom.node( $ ); - $ = $.parentNode; + try { + $ = $.parentNode; + } catch( e ) { + $ = null; + } } return null; }, diff --git a/core/selection.js b/core/selection.js index fd501bbff4e..3827fb208f9 100644 --- a/core/selection.js +++ b/core/selection.js @@ -1288,7 +1288,7 @@ if ( restore ) { // Saved selection may be outdated (e.g. anchored in offline nodes). // Avoid getting broken by such. - var common = selectedElement || ranges[ 0 ].getCommonAncestor(); + var common = selectedElement || ranges[ 0 ] && ranges[ 0 ].getCommonAncestor(); if ( !( common && common.getAscendant( 'body', 1 ) ) ) return; diff --git a/plugins/dialogui/plugin.js b/plugins/dialogui/plugin.js index bb66d9d3355..3eb78b1c536 100644 --- a/plugins/dialogui/plugin.js +++ b/plugins/dialogui/plugin.js @@ -279,13 +279,15 @@ CKEDITOR.plugins.add( 'dialogui', { attributes.rows = elementDefinition.rows || 5; attributes.cols = elementDefinition.cols || 20; + attributes[ 'class' ] = 'cke_dialog_ui_input_textarea ' + ( elementDefinition[ 'class' ] || '' ); + if ( typeof elementDefinition.inputStyle != 'undefined' ) attributes.style = elementDefinition.inputStyle; var innerHTML = function() { attributes[ 'aria-labelledby' ] = this._.labelId; this._.required && ( attributes[ 'aria-required' ] = this._.required ); - var html = [ '
' ); diff --git a/plugins/sourcedialog/dialogs/sourcedialog.js b/plugins/sourcedialog/dialogs/sourcedialog.js new file mode 100755 index 00000000000..e3b566b5e24 --- /dev/null +++ b/plugins/sourcedialog/dialogs/sourcedialog.js @@ -0,0 +1,76 @@ +/** + * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.html or http://ckeditor.com/license + */ + +CKEDITOR.dialog.add( 'sourcedialog', function( editor ) { + var size = CKEDITOR.document.getWindow().getViewPaneSize(); + + // Make it maximum 800px wide, but still fully visible in the viewport. + var width = Math.min( size.width - 70, 800); + + // Make it use 2/3 of the viewport height. + var height = size.height / 1.5; + + // Store old editor data to avoid unnecessary setData. + var oldData; + + return { + title: editor.lang.sourcedialog.title, + minWidth: 100, + minHeight: 100, + + onShow: function() { + this.setValueOf( 'main', 'data', oldData = editor.getData() ); + }, + + onOk: (function() { + function setData( newData ) { + var that = this; + + editor.setData( newData, function() { + that.hide(); + + // Ensure correct selection. + var range = editor.createRange(); + range.moveToElementEditStart( editor.editable() ); + range.select(); + } ); + } + + return function( event ) { + // Remove CR from input data for reliable comparison with editor data. + var newData = this.getValueOf( 'main', 'data' ).replace( /\r/g, '' ); + + // Avoid unnecessary setData. Also preserve selection + // when user changed his mind and goes back to wysiwyg editing. + if ( newData === oldData ) + return true; + + // Set data asynchronously to avoid errors in IE. + CKEDITOR.env.ie ? + CKEDITOR.tools.setTimeout( setData, 0, this, newData ) + : + setData.call( this, newData ); + + // Don't let the dialog close before setData is over. + return false; + }; + })(), + + contents: [{ + id: 'main', + label: editor.lang.sourcedialog.title, + elements: [{ + type: 'textarea', + type: 'textarea', + id: 'data', + inputStyle: 'cursor:auto;' + + 'width:' + width + 'px;' + + 'height:' + height + 'px;' + + 'tab-size:4;', + 'class': 'cke_source' + }] + }] + }; +}); diff --git a/plugins/sourcedialog/icons/sourcedialog-rtl.png b/plugins/sourcedialog/icons/sourcedialog-rtl.png new file mode 100644 index 00000000000..2efa25dd584 Binary files /dev/null and b/plugins/sourcedialog/icons/sourcedialog-rtl.png differ diff --git a/plugins/sourcedialog/icons/sourcedialog.png b/plugins/sourcedialog/icons/sourcedialog.png new file mode 100644 index 00000000000..ffd830df39c Binary files /dev/null and b/plugins/sourcedialog/icons/sourcedialog.png differ diff --git a/plugins/sourcedialog/lang/en.js b/plugins/sourcedialog/lang/en.js new file mode 100644 index 00000000000..d41d8f70223 --- /dev/null +++ b/plugins/sourcedialog/lang/en.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.setLang( 'sourcedialog', 'en', { + toolbar: 'Source', + title: 'Source' +}); diff --git a/plugins/sourcedialog/plugin.js b/plugins/sourcedialog/plugin.js new file mode 100644 index 00000000000..38ebe1ec3d8 --- /dev/null +++ b/plugins/sourcedialog/plugin.js @@ -0,0 +1,26 @@ +/** + * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.html or http://ckeditor.com/license + */ + +CKEDITOR.plugins.add( 'sourcedialog', { + lang: 'en', // %REMOVE_LINE_CORE% + icons: 'sourcedialog,sourcedialog-rtl', // %REMOVE_LINE_CORE% + + init: function( editor ) { + // Register the "source" command, which simply opens the "source" dialog. + editor.addCommand( 'sourcedialog', new CKEDITOR.dialogCommand( 'sourcedialog' ) ); + + // Register the "source" dialog. + CKEDITOR.dialog.add( 'sourcedialog', this.path + 'dialogs/sourcedialog.js' ); + + // If the toolbar is available, create the "Source" button. + if ( editor.ui.addButton ) { + editor.ui.addButton( 'Sourcedialog', { + label: editor.lang.sourcedialog.toolbar, + command: 'sourcedialog', + toolbar: 'mode,10' + }); + } + } +}); diff --git a/plugins/sourcedialog/samples/sourcedialog.html b/plugins/sourcedialog/samples/sourcedialog.html new file mode 100644 index 00000000000..aa2a1172d69 --- /dev/null +++ b/plugins/sourcedialog/samples/sourcedialog.html @@ -0,0 +1,118 @@ + + + + ++ Sourcedialog plugin provides an easy way to edit raw HTML content + of an editor, similarly to what is possible with Sourcearea + plugin for framed instances but using dialogs. Thanks to that, it's also possible + to manipulate raw content of inline editor instances. +
+
+ This plugin extends the toolbar with a button,
+ which opens a dialog window with a source code editor. It works with both framed
+ and inline instances. To enable this
+ plugin, basically add extraPlugins: 'sourcedialog'
to editor's
+ config:
+
+// Inline editor. +CKEDITOR.inline( 'editable', { + extraPlugins: 'sourcedialog' +}); + +// Framed editor. +CKEDITOR.replace( 'textarea_id', { + extraPlugins: 'sourcedialog', + removePlugins: 'sourcearea' +}); ++
+ Note that you may want to include removePlugins: 'sourcearea'
+ in your config when using Sourcedialog in framed instances.
+ This prevents feature redundancy.
+
+ Note that editable
in the code above is the id
+ attribute of the <div>
element to be converted into an inline instance.
+
+ Note that textarea_id
in the code above is the id
attribute of
+ the <textarea>
element to be replaced with CKEditor.
+
This is some sample text. You are using CKEditor.
+