Skip to content

Commit d20c978

Browse files
committed
Merge branch 't/10280' into major
2 parents 8ac6a22 + 9ce223f commit d20c978

File tree

5 files changed

+184
-102
lines changed

5 files changed

+184
-102
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ CKEditor 4 Changelog
1111
* [#10027](http://dev.ckeditor.com/ticket/10027): Separated list and block indentation.
1212
* [#8244](http://dev.ckeditor.com/ticket/8244): Use (SHIFT+)TAB to indent and outdent lists.
1313
* [#8031](http://dev.ckeditor.com/ticket/8031): Handle `required` attributes on `textareas` - introduced `editor#required` event.
14+
* [#10280](http://dev.ckeditor.com/ticket/10280): Ability to replace `textarea` with inline editor.
1415

1516
## CKEditor 4.1.3
1617

core/creators/inline.js

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
* ...
1616
* CKEDITOR.inline( 'content' );
1717
*
18+
* It is also possible to create inline editor from textarea element. If you do so
19+
* an additional `div` with editable content will be created directly after the
20+
* `textarea` element and that `textarea` element will be hidden.
21+
*
1822
* @param {Object/String} element The DOM element or its ID.
1923
* @param {Object} [instanceConfig] The specific configurations to apply to this editor instance.
2024
* See {@link CKEDITOR.config}.
@@ -30,11 +34,30 @@
3034
if ( element.getEditor() )
3135
throw 'The editor instance "' + element.getEditor().name + '" is already attached to the provided element.';
3236

33-
var editor = new CKEDITOR.editor( instanceConfig, element, CKEDITOR.ELEMENT_MODE_INLINE );
34-
35-
// Initial editor data is simply loaded from the page element content to make
36-
// data retrieval possible immediately after the editor creation.
37-
editor.setData( element.getHtml(), null, true );
37+
var editor = new CKEDITOR.editor( instanceConfig, element, CKEDITOR.ELEMENT_MODE_INLINE ),
38+
textarea = element.is( 'textarea' ) ? element : null;
39+
40+
if ( textarea ) {
41+
editor.setData( textarea.getValue(), null, true );
42+
43+
//Change element from textarea to div
44+
element = CKEDITOR.dom.element.createFromHtml(
45+
'<div contenteditable="' + !!editor.readOnly + '" class="cke_textarea_inline">' +
46+
textarea.getValue() +
47+
'</div>',
48+
CKEDITOR.document );
49+
50+
element.insertAfter( textarea );
51+
textarea.hide();
52+
53+
// Attaching the concrete form.
54+
if ( textarea.$.form )
55+
editor._attachToForm();
56+
} else {
57+
// Initial editor data is simply loaded from the page element content to make
58+
// data retrieval possible immediately after the editor creation.
59+
editor.setData( element.getHtml(), null, true );
60+
}
3861

3962
// Once the editor is loaded, start the UI.
4063
editor.on( 'loaded', function() {
@@ -53,6 +76,7 @@
5376
editor.resetDirty();
5477

5578
editor.fire( 'contentDom' );
79+
5680
// Inline editing defaults to "wysiwyg" mode, so plugins don't
5781
// need to make special handling for this "mode-less" environment.
5882
editor.mode = 'wysiwyg';
@@ -68,9 +92,18 @@
6892

6993
// Handle editor destroying.
7094
editor.on( 'destroy', function() {
95+
// Remove container from DOM if inline-textarea editor.
96+
// Show <textarea> back again.
97+
if ( textarea ) {
98+
editor.container.clearCustomData();
99+
editor.container.remove();
100+
textarea.show();
101+
}
102+
71103
editor.element.clearCustomData();
104+
72105
delete editor.element;
73-
});
106+
} );
74107

75108
return editor;
76109
};
@@ -94,7 +127,9 @@
94127
// the instance settings and eventually cancel the creation.
95128

96129
data = {
97-
element: el, config: {} };
130+
element: el,
131+
config: {}
132+
};
98133

99134
if ( CKEDITOR.fire( 'inline', data ) !== false )
100135
CKEDITOR.inline( el, data.config );
@@ -105,10 +140,9 @@
105140

106141
CKEDITOR.domReady( function() {
107142
!CKEDITOR.disableAutoInline && CKEDITOR.inlineAll();
108-
});
143+
} );
109144
})();
110145

111-
112146
/**
113147
* Avoid creating editor automatically on element which has attribute
114148
* `contenteditable` set to the value `true`.

core/creators/themedui.js

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,8 @@ CKEDITOR.replaceClass = 'ckeditor';
262262
loadTheme( editor );
263263

264264
if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE && editor.config.autoUpdateElement )
265-
attachToForm( editor );
265+
if ( element.$.form )
266+
editor._attachToForm();
266267

267268
editor.setMode( editor.config.startupMode, function() {
268269
// Clean on startup.
@@ -372,48 +373,6 @@ CKEDITOR.replaceClass = 'ckeditor';
372373
editor.fireOnce( 'uiReady' );
373374
}
374375

375-
function attachToForm( editor ) {
376-
var element = editor.element;
377-
378-
// If are replacing a textarea, we must
379-
if ( editor.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE && element.is( 'textarea' ) ) {
380-
var form = element.$.form && new CKEDITOR.dom.element( element.$.form );
381-
if ( form ) {
382-
function onSubmit( evt ) {
383-
editor.updateElement();
384-
385-
// #8031 If textarea had required attribute and editor is empty fire 'required' event and if
386-
// it was cancelled, prevent submitting the form.
387-
if ( editor._.required && !element.getValue() && editor.fire( 'required' ) === false )
388-
evt.data.preventDefault();
389-
}
390-
form.on( 'submit', onSubmit );
391-
392-
// Setup the submit function because it doesn't fire the
393-
// "submit" event.
394-
if ( !form.$.submit.nodeName && !form.$.submit.length ) {
395-
form.$.submit = CKEDITOR.tools.override( form.$.submit, function( originalSubmit ) {
396-
return function( evt ) {
397-
onSubmit( new CKEDITOR.dom.event( evt ) );
398-
399-
// For IE, the DOM submit function is not a
400-
// function, so we need third check.
401-
if ( originalSubmit.apply )
402-
originalSubmit.apply( this, arguments );
403-
else
404-
originalSubmit();
405-
};
406-
});
407-
}
408-
409-
// Remove 'submit' events registered on form element before destroying.(#3988)
410-
editor.on( 'destroy', function() {
411-
form.removeListener( 'submit', onSubmit );
412-
});
413-
}
414-
}
415-
}
416-
417376
// Replace all textareas with the default class name.
418377
CKEDITOR.domReady( function() {
419378
CKEDITOR.replaceClass && CKEDITOR.replaceAll( CKEDITOR.replaceClass );

core/editor.js

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,12 @@
4040
else if ( !mode )
4141
throw new Error( 'One of the element modes must be specified.' );
4242

43-
if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && mode == CKEDITOR.ELEMENT_MODE_INLINE ) {
43+
if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && mode == CKEDITOR.ELEMENT_MODE_INLINE )
4444
throw new Error( 'Inline element mode is not supported on IE quirks.' );
45-
}
4645

47-
// Asserting element DTD depending on mode.
48-
if ( mode == CKEDITOR.ELEMENT_MODE_INLINE && !element.is( CKEDITOR.dtd.$editable ) || mode == CKEDITOR.ELEMENT_MODE_REPLACE && element.is( CKEDITOR.dtd.$nonBodyContent ) )
46+
if ( !isSupportedElement( element, mode ) )
4947
throw new Error( 'The specified element mode is not supported on element: "' + element.getName() + '".' );
5048

51-
5249
/**
5350
* The original host page element upon which the editor is created, it's only
5451
* supposed to be provided by the concrete editor creator and is not subjected to
@@ -194,6 +191,15 @@
194191
return name;
195192
}
196193

194+
// Asserting element DTD depending on mode.
195+
function isSupportedElement( element, mode ) {
196+
if ( mode == CKEDITOR.ELEMENT_MODE_INLINE )
197+
return element.is( CKEDITOR.dtd.$editable ) || element.is( 'textarea' );
198+
else if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE )
199+
return !element.is( CKEDITOR.dtd.$nonBodyContent );
200+
return 1;
201+
}
202+
197203
function updateCommands() {
198204
var commands = this.commands,
199205
name;
@@ -312,7 +318,20 @@
312318
* @property {Boolean}
313319
* @see CKEDITOR.editor#setReadOnly
314320
*/
315-
editor.readOnly = !!( editor.config.readOnly || ( editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE ? editor.element.isReadOnly() : editor.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ? editor.element.getAttribute( 'disabled' ) : false ) );
321+
editor.readOnly = !!(
322+
editor.config.readOnly || (
323+
editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE ?
324+
editor.element.is( 'textarea' ) ?
325+
editor.element.hasAttribute( 'disabled' )
326+
:
327+
editor.element.isReadOnly()
328+
:
329+
editor.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ?
330+
editor.element.hasAttribute( 'disabled' )
331+
:
332+
false
333+
)
334+
);
316335

317336
/**
318337
* Indicates that the editor is running into an environment where
@@ -321,7 +340,9 @@
321340
* @readonly
322341
* @property {Boolean}
323342
*/
324-
editor.blockless = editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE && !CKEDITOR.dtd[ editor.element.getName() ][ 'p' ];
343+
editor.blockless = editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE ?
344+
!( editor.element.is( 'textarea' ) || CKEDITOR.dtd[ editor.element.getName() ][ 'p' ] ) :
345+
false;
325346

326347
/**
327348
* The [tabbing navigation](http://en.wikipedia.org/wiki/Tabbing_navigation) order determined for this editor instance.
@@ -636,6 +657,56 @@
636657
return this.commands[ commandName ] = cmd;
637658
},
638659

660+
/**
661+
* Attaches editor to form to call {@link #updateElement} before form submit.
662+
* This method is called by both creators ({@link CKEDITOR#replace replace} and
663+
* {@link CKEDITOR#inline inline}) so there is no reason to call it manually.
664+
*
665+
* @private
666+
*/
667+
_attachToForm: function() {
668+
var editor = this,
669+
element = editor.element,
670+
form = new CKEDITOR.dom.element( element.$.form );
671+
672+
// If are replacing a textarea, we must
673+
if ( element.is( 'textarea' ) ) {
674+
if ( form ) {
675+
function onSubmit( evt ) {
676+
editor.updateElement();
677+
678+
// #8031 If textarea had required attribute and editor is empty fire 'required' event and if
679+
// it was cancelled, prevent submitting the form.
680+
if ( editor._.required && !element.getValue() && editor.fire( 'required' ) === false )
681+
evt.data.preventDefault();
682+
}
683+
form.on( 'submit', onSubmit );
684+
685+
// Setup the submit function because it doesn't fire the
686+
// "submit" event.
687+
if ( !form.$.submit.nodeName && !form.$.submit.length ) {
688+
form.$.submit = CKEDITOR.tools.override( form.$.submit, function( originalSubmit ) {
689+
return function( evt ) {
690+
onSubmit( new CKEDITOR.dom.event( evt ) );
691+
692+
// For IE, the DOM submit function is not a
693+
// function, so we need third check.
694+
if ( originalSubmit.apply )
695+
originalSubmit.apply( this, arguments );
696+
else
697+
originalSubmit();
698+
};
699+
} );
700+
}
701+
702+
// Remove 'submit' events registered on form element before destroying.(#3988)
703+
editor.on( 'destroy', function() {
704+
form.removeListener( 'submit', onSubmit );
705+
} );
706+
}
707+
}
708+
},
709+
639710
/**
640711
* Destroys the editor instance, releasing all resources used by it.
641712
* If the editor replaced an element, the element will be recovered.
@@ -966,7 +1037,8 @@
9661037
* the current data available in the editor.
9671038
*
9681039
* **Note:** This method will only affect those editor instances created
969-
* with {@link CKEDITOR#ELEMENT_MODE_REPLACE} element mode.
1040+
* with {@link CKEDITOR#ELEMENT_MODE_REPLACE} element mode or inline instances
1041+
* bound to `<textarea>` elements.
9701042
*
9711043
* CKEDITOR.instances.editor1.updateElement();
9721044
* alert( document.getElementById( 'editor1' ).value ); // The current editor data.

0 commit comments

Comments
 (0)