]>
git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blob - sources/plugins/widgetselection/plugin.js
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
7 * @fileOverview A plugin created to handle ticket http://dev.ckeditor.com/ticket/11064. While the issue is caused by native WebKit/Blink behaviour,
8 * this plugin can be easily detached or modified when the issue is fixed in the browsers without changing the core.
9 * When Ctrl/Cmd + A is pressed to select all content it does not work due to a bug in
10 * Webkit/Blink if a non-editable element is at the beginning or the end of the content.
16 CKEDITOR
.plugins
.add( 'widgetselection', {
18 init: function( editor
) {
19 if ( CKEDITOR
.env
.webkit
) {
20 var widgetselection
= CKEDITOR
.plugins
.widgetselection
;
22 editor
.on( 'contentDom', function( evt
) {
24 var editor
= evt
.editor
,
25 doc
= editor
.document
,
26 editable
= editor
.editable();
28 editable
.attachListener( doc
, 'keydown', function( evt
) {
29 var data
= evt
.data
.$;
32 if ( evt
.data
.getKey() == 65 && ( CKEDITOR
.env
.mac
&& data
.metaKey
|| !CKEDITOR
.env
.mac
&& data
.ctrlKey
) ) {
34 // Defer the call so the selection is already changed by the pressed keys.
35 CKEDITOR
.tools
.setTimeout( function() {
37 // Manage filler elements on keydown. If there is no need
38 // to add fillers, we need to check and clean previously used once.
39 if ( !widgetselection
.addFillers( editable
) ) {
40 widgetselection
.removeFillers( editable
);
46 // Check and clean previously used fillers.
47 editor
.on( 'selectionCheck', function( evt
) {
48 widgetselection
.removeFillers( evt
.editor
.editable() );
51 // Remove fillers on paste before data gets inserted into editor.
52 editor
.on( 'paste', function( evt
) {
53 evt
.data
.dataValue
= widgetselection
.cleanPasteData( evt
.data
.dataValue
);
56 if ( 'selectall' in editor
.plugins
) {
57 widgetselection
.addSelectAllIntegration( editor
);
65 * A set of helper methods for the Widget Selection plugin.
67 * @property widgetselection
68 * @member CKEDITOR.plugins
71 CKEDITOR
.plugins
.widgetselection
= {
74 * The start filler element reference.
76 * @property {CKEDITOR.dom.element}
77 * @member CKEDITOR.plugins.widgetselection
83 * The end filler element reference.
85 * @property {CKEDITOR.dom.element}
86 * @member CKEDITOR.plugins.widgetselection
92 * An attribute which identifies the filler element.
95 * @member CKEDITOR.plugins.widgetselection
98 fillerAttribute: 'data-cke-filler-webkit',
101 * The default content of the filler element. Note: The filler needs to have `visible` content.
102 * Unprintable elements or empty content do not help as a workaround.
105 * @member CKEDITOR.plugins.widgetselection
108 fillerContent: ' ',
111 * Tag name which is used to create fillers.
114 * @member CKEDITOR.plugins.widgetselection
117 fillerTagName: 'div',
120 * Adds a filler before or after a non-editable element at the beginning or the end of the `editable`.
122 * @param {CKEDITOR.editable} editable
124 * @member CKEDITOR.plugins.widgetselection
126 addFillers: function( editable
) {
127 var editor
= editable
.editor
;
129 // Whole content should be selected, if not fix the selection manually.
130 if ( !this.isWholeContentSelected( editable
) && editable
.getChildCount() > 0 ) {
132 var firstChild
= editable
.getFirst( filterTempElements
),
133 lastChild
= editable
.getLast( filterTempElements
);
135 // Check if first element is editable. If not prepend with filler.
136 if ( firstChild
&& firstChild
.type
== CKEDITOR
.NODE_ELEMENT
&& !firstChild
.isEditable() ) {
137 this.startFiller
= this.createFiller();
138 editable
.append( this.startFiller
, 1 );
141 // Check if last element is editable. If not append filler.
142 if ( lastChild
&& lastChild
.type
== CKEDITOR
.NODE_ELEMENT
&& !lastChild
.isEditable() ) {
143 this.endFiller
= this.createFiller( true );
144 editable
.append( this.endFiller
, 0 );
147 // Reselect whole content after any filler was added.
148 if ( this.hasFiller( editable
) ) {
149 var rangeAll
= editor
.createRange();
150 rangeAll
.selectNodeContents( editable
);
159 * Removes filler elements or updates their references.
161 * It will **not remove** filler elements if the whole content is selected, as it would break the
164 * @param {CKEDITOR.editable} editable
165 * @member CKEDITOR.plugins.widgetselection
167 removeFillers: function( editable
) {
168 // If startFiller or endFiller exists and not entire content is selected it means the selection
169 // just changed from selected all. We need to remove fillers and set proper selection/content.
170 if ( this.hasFiller( editable
) && !this.isWholeContentSelected( editable
) ) {
172 var startFillerContent
= editable
.findOne( this.fillerTagName
+ '[' + this.fillerAttribute
+ '=start]' ),
173 endFillerContent
= editable
.findOne( this.fillerTagName
+ '[' + this.fillerAttribute
+ '=end]' );
175 if ( this.startFiller
&& startFillerContent
&& this.startFiller
.equals( startFillerContent
) ) {
176 this.removeFiller( this.startFiller
, editable
);
178 // The start filler is still present but it is a different element than previous one. It means the
179 // undo recreating entirely selected content was performed. We need to update filler reference.
180 this.startFiller
= startFillerContent
;
183 if ( this.endFiller
&& endFillerContent
&& this.endFiller
.equals( endFillerContent
) ) {
184 this.removeFiller( this.endFiller
, editable
);
186 // Same as with start filler.
187 this.endFiller
= endFillerContent
;
193 * Removes fillers from the paste data.
195 * @param {String} data
197 * @member CKEDITOR.plugins.widgetselection
200 cleanPasteData: function( data
) {
201 if ( data
&& data
.length
) {
203 .replace( this.createFillerRegex(), '' )
204 .replace( this.createFillerRegex( true ), '' );
210 * Checks if the entire content of the given editable is selected.
212 * @param {CKEDITOR.editable} editable
214 * @member CKEDITOR.plugins.widgetselection
217 isWholeContentSelected: function( editable
) {
219 var range
= editable
.editor
.getSelection().getRanges()[ 0 ];
222 if ( range
&& range
.collapsed
) {
226 var rangeClone
= range
.clone();
227 rangeClone
.enlarge( CKEDITOR
.ENLARGE_ELEMENT
);
229 return !!( rangeClone
&& editable
&& rangeClone
.startContainer
&& rangeClone
.endContainer
&&
230 rangeClone
.startOffset
=== 0 && rangeClone
.endOffset
=== editable
.getChildCount() &&
231 rangeClone
.startContainer
.equals( editable
) && rangeClone
.endContainer
.equals( editable
) );
238 * Checks if there is any filler element in the given editable.
240 * @param {CKEDITOR.editable} editable
242 * @member CKEDITOR.plugins.widgetselection
245 hasFiller: function( editable
) {
246 return editable
.find( this.fillerTagName
+ '[' + this.fillerAttribute
+ ']' ).count() > 0;
250 * Creates a filler element.
252 * @param {Boolean} [onEnd] If filler will be placed on end or beginning of the content.
253 * @returns {CKEDITOR.dom.element}
254 * @member CKEDITOR.plugins.widgetselection
257 createFiller: function( onEnd
) {
258 var filler
= new CKEDITOR
.dom
.element( this.fillerTagName
);
259 filler
.setHtml( this.fillerContent
);
260 filler
.setAttribute( this.fillerAttribute
, onEnd
? 'end' : 'start' );
261 filler
.setAttribute( 'data-cke-temp', 1 );
269 position: 'absolute',
280 * Removes the specific filler element from the given editable. If the filler contains any content (typed or pasted),
281 * it replaces the current editable content. If not, the caret is placed before the first or after the last editable
282 * element (depends if the filler was at the beginning or the end).
284 * @param {CKEDITOR.dom.element} filler
285 * @param {CKEDITOR.editable} editable
286 * @member CKEDITOR.plugins.widgetselection
289 removeFiller: function( filler
, editable
) {
291 var editor
= editable
.editor
,
292 currentRange
= editable
.editor
.getSelection().getRanges()[ 0 ],
293 currentPath
= currentRange
.startPath(),
294 range
= editor
.createRange(),
299 if ( currentPath
.contains( filler
) ) {
300 insertedHtml
= filler
.getHtml();
301 manuallyHandleCaret
= true;
304 fillerOnStart
= filler
.getAttribute( this.fillerAttribute
) == 'start';
308 if ( insertedHtml
&& insertedHtml
.length
> 0 && insertedHtml
!= this.fillerContent
) {
309 editable
.insertHtmlIntoRange( insertedHtml
, editor
.getSelection().getRanges()[ 0 ] );
310 range
.setStartAt( editable
.getChild( editable
.getChildCount() - 1 ), CKEDITOR
.POSITION_BEFORE_END
);
311 editor
.getSelection().selectRanges( [ range
] );
313 } else if ( manuallyHandleCaret
) {
314 if ( fillerOnStart
) {
315 range
.setStartAt( editable
.getFirst().getNext(), CKEDITOR
.POSITION_AFTER_START
);
317 range
.setEndAt( editable
.getLast().getPrevious(), CKEDITOR
.POSITION_BEFORE_END
);
319 editable
.editor
.getSelection().selectRanges( [ range
] );
325 * Creates a regular expression which will match the filler HTML in the text.
327 * @param {Boolean} [onEnd] Whether a regular expression should be created for the filler at the beginning or
328 * the end of the content.
330 * @member CKEDITOR.plugins.widgetselection
333 createFillerRegex: function( onEnd
) {
334 var matcher
= this.createFiller( onEnd
).getOuterHtml()
335 .replace( /style="[^"]*"/gi, 'style="[^"]*"' )
336 .replace( />[^<]*</gi, '>[^<]*<' );
338 return new RegExp( ( !onEnd
? '^' : '' ) + matcher
+ ( onEnd
? '$' : '' ) );
342 * Adds an integration for the [Select All](http://ckeditor.com/addon/selectall) plugin to the given `editor`.
345 * @param {CKEDITOR.editor} editor
346 * @member CKEDITOR.plugins.widgetselection
348 addSelectAllIntegration: function( editor
) {
349 var widgetselection
= this;
351 editor
.editable().attachListener( editor
, 'beforeCommandExec', function( evt
) {
352 var editable
= editor
.editable();
354 if ( evt
.data
.name
== 'selectAll' && editable
) {
355 widgetselection
.addFillers( editable
);
357 }, null, null, 9999 );
362 function filterTempElements( el
) {
363 return el
.getName
&& !el
.hasAttribute( 'data-cke-temp' );