]> git.immae.eu Git - perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git/blobdiff - sources/plugins/widgetselection/plugin.js
Add oembed
[perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git] / sources / plugins / widgetselection / plugin.js
diff --git a/sources/plugins/widgetselection/plugin.js b/sources/plugins/widgetselection/plugin.js
new file mode 100644 (file)
index 0000000..6c788cb
--- /dev/null
@@ -0,0 +1,366 @@
+/**\r
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.\r
+ * For licensing, see LICENSE.md or http://ckeditor.com/license\r
+ */\r
+\r
+/**\r
+ * @fileOverview A plugin created to handle ticket http://dev.ckeditor.com/ticket/11064. While the issue is caused by native WebKit/Blink behaviour,\r
+ * this plugin can be easily detached or modified when the issue is fixed in the browsers without changing the core.\r
+ * When Ctrl/Cmd + A is pressed to select all content it does not work due to a bug in\r
+ * Webkit/Blink if a non-editable element is at the beginning or the end of the content.\r
+ */\r
+\r
+( function() {\r
+       'use strict';\r
+\r
+       CKEDITOR.plugins.add( 'widgetselection', {\r
+\r
+               init: function( editor ) {\r
+                       if ( CKEDITOR.env.webkit ) {\r
+                               var widgetselection = CKEDITOR.plugins.widgetselection;\r
+\r
+                               editor.on( 'contentDom', function( evt ) {\r
+\r
+                                       var editor = evt.editor,\r
+                                               doc = editor.document,\r
+                                               editable = editor.editable();\r
+\r
+                                       editable.attachListener( doc, 'keydown', function( evt ) {\r
+                                               var data = evt.data.$;\r
+\r
+                                               // Ctrl/Cmd + A\r
+                                               if ( evt.data.getKey() == 65 && ( CKEDITOR.env.mac && data.metaKey || !CKEDITOR.env.mac && data.ctrlKey ) ) {\r
+\r
+                                                       // Defer the call so the selection is already changed by the pressed keys.\r
+                                                       CKEDITOR.tools.setTimeout( function() {\r
+\r
+                                                               // Manage filler elements on keydown. If there is no need\r
+                                                               // to add fillers, we need to check and clean previously used once.\r
+                                                               if ( !widgetselection.addFillers( editable ) ) {\r
+                                                                       widgetselection.removeFillers( editable );\r
+                                                               }\r
+                                                       }, 0 );\r
+                                               }\r
+                                       }, null, null, -1 );\r
+\r
+                                       // Check and clean previously used fillers.\r
+                                       editor.on( 'selectionCheck', function( evt ) {\r
+                                               widgetselection.removeFillers( evt.editor.editable() );\r
+                                       } );\r
+\r
+                                       // Remove fillers on paste before data gets inserted into editor.\r
+                                       editor.on( 'paste', function( evt ) {\r
+                                               evt.data.dataValue = widgetselection.cleanPasteData( evt.data.dataValue );\r
+                                       } );\r
+\r
+                                       if ( 'selectall' in editor.plugins ) {\r
+                                               widgetselection.addSelectAllIntegration( editor );\r
+                                       }\r
+                               } );\r
+                       }\r
+               }\r
+       } );\r
+\r
+       /**\r
+        * A set of helper methods for the Widget Selection plugin.\r
+        *\r
+        * @property widgetselection\r
+        * @member CKEDITOR.plugins\r
+        * @since 4.6.1\r
+        */\r
+       CKEDITOR.plugins.widgetselection = {\r
+\r
+               /**\r
+                * The start filler element reference.\r
+                *\r
+                * @property {CKEDITOR.dom.element}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               startFiller: null,\r
+\r
+               /**\r
+                * The end filler element reference.\r
+                *\r
+                * @property {CKEDITOR.dom.element}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               endFiller: null,\r
+\r
+               /**\r
+                * An attribute which identifies the filler element.\r
+                *\r
+                * @property {String}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               fillerAttribute: 'data-cke-filler-webkit',\r
+\r
+               /**\r
+                * The default content of the filler element. Note: The filler needs to have `visible` content.\r
+                * Unprintable elements or empty content do not help as a workaround.\r
+                *\r
+                * @property {String}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               fillerContent: ' ',\r
+\r
+               /**\r
+                * Tag name which is used to create fillers.\r
+                *\r
+                * @property {String}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               fillerTagName: 'div',\r
+\r
+               /**\r
+                * Adds a filler before or after a non-editable element at the beginning or the end of the `editable`.\r
+                *\r
+                * @param {CKEDITOR.editable} editable\r
+                * @returns {Boolean}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                */\r
+               addFillers: function( editable ) {\r
+                       var editor = editable.editor;\r
+\r
+                       // Whole content should be selected, if not fix the selection manually.\r
+                       if ( !this.isWholeContentSelected( editable ) && editable.getChildCount() > 0 ) {\r
+\r
+                               var firstChild = editable.getFirst( filterTempElements ),\r
+                                       lastChild = editable.getLast( filterTempElements );\r
+\r
+                               // Check if first element is editable. If not prepend with filler.\r
+                               if ( firstChild && firstChild.type == CKEDITOR.NODE_ELEMENT && !firstChild.isEditable() ) {\r
+                                       this.startFiller = this.createFiller();\r
+                                       editable.append( this.startFiller, 1 );\r
+                               }\r
+\r
+                               // Check if last element is editable. If not append filler.\r
+                               if ( lastChild && lastChild.type == CKEDITOR.NODE_ELEMENT && !lastChild.isEditable() ) {\r
+                                       this.endFiller = this.createFiller( true );\r
+                                       editable.append( this.endFiller, 0 );\r
+                               }\r
+\r
+                               // Reselect whole content after any filler was added.\r
+                               if ( this.hasFiller( editable ) ) {\r
+                                       var rangeAll = editor.createRange();\r
+                                       rangeAll.selectNodeContents( editable );\r
+                                       rangeAll.select();\r
+                                       return true;\r
+                               }\r
+                       }\r
+                       return false;\r
+               },\r
+\r
+               /**\r
+                * Removes filler elements or updates their references.\r
+                *\r
+                * It will **not remove** filler elements if the whole content is selected, as it would break the\r
+                * selection.\r
+                *\r
+                * @param {CKEDITOR.editable} editable\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                */\r
+               removeFillers: function( editable ) {\r
+                       // If startFiller or endFiller exists and not entire content is selected it means the selection\r
+                       // just changed from selected all. We need to remove fillers and set proper selection/content.\r
+                       if ( this.hasFiller( editable ) && !this.isWholeContentSelected( editable ) ) {\r
+\r
+                               var startFillerContent = editable.findOne( this.fillerTagName + '[' + this.fillerAttribute + '=start]' ),\r
+                                       endFillerContent = editable.findOne( this.fillerTagName + '[' + this.fillerAttribute + '=end]' );\r
+\r
+                               if ( this.startFiller && startFillerContent && this.startFiller.equals( startFillerContent ) ) {\r
+                                       this.removeFiller( this.startFiller, editable );\r
+                               } else {\r
+                                       // The start filler is still present but it is a different element than previous one. It means the\r
+                                       // undo recreating entirely selected content was performed. We need to update filler reference.\r
+                                       this.startFiller = startFillerContent;\r
+                               }\r
+\r
+                               if ( this.endFiller && endFillerContent && this.endFiller.equals( endFillerContent ) ) {\r
+                                       this.removeFiller( this.endFiller, editable );\r
+                               } else {\r
+                                       // Same as with start filler.\r
+                                       this.endFiller = endFillerContent;\r
+                               }\r
+                       }\r
+               },\r
+\r
+               /**\r
+                * Removes fillers from the paste data.\r
+                *\r
+                * @param {String} data\r
+                * @returns {String}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               cleanPasteData: function( data ) {\r
+                       if ( data && data.length ) {\r
+                               data = data\r
+                                       .replace( this.createFillerRegex(), '' )\r
+                                       .replace( this.createFillerRegex( true ), '' );\r
+                       }\r
+                       return data;\r
+               },\r
+\r
+               /**\r
+                * Checks if the entire content of the given editable is selected.\r
+                *\r
+                * @param {CKEDITOR.editable} editable\r
+                * @returns {Boolean}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               isWholeContentSelected: function( editable ) {\r
+\r
+                       var range = editable.editor.getSelection().getRanges()[ 0 ];\r
+                       if ( range ) {\r
+\r
+                               if ( range && range.collapsed ) {\r
+                                       return false;\r
+\r
+                               } else {\r
+                                       var rangeClone = range.clone();\r
+                                       rangeClone.enlarge( CKEDITOR.ENLARGE_ELEMENT );\r
+\r
+                                       return !!( rangeClone && editable && rangeClone.startContainer && rangeClone.endContainer &&\r
+                                               rangeClone.startOffset === 0 && rangeClone.endOffset === editable.getChildCount() &&\r
+                                               rangeClone.startContainer.equals( editable ) && rangeClone.endContainer.equals( editable ) );\r
+                               }\r
+                       }\r
+                       return false;\r
+               },\r
+\r
+               /**\r
+                *      Checks if there is any filler element in the given editable.\r
+                *\r
+                * @param {CKEDITOR.editable} editable\r
+                * @returns {Boolean}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               hasFiller: function( editable ) {\r
+                       return editable.find( this.fillerTagName + '[' + this.fillerAttribute + ']' ).count() > 0;\r
+               },\r
+\r
+               /**\r
+                * Creates a filler element.\r
+                *\r
+                * @param {Boolean} [onEnd] If filler will be placed on end or beginning of the content.\r
+                * @returns {CKEDITOR.dom.element}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               createFiller: function( onEnd ) {\r
+                       var filler = new CKEDITOR.dom.element( this.fillerTagName );\r
+                       filler.setHtml( this.fillerContent );\r
+                       filler.setAttribute( this.fillerAttribute, onEnd ? 'end' : 'start' );\r
+                       filler.setAttribute( 'data-cke-temp', 1 );\r
+                       filler.setStyles( {\r
+                               display: 'block',\r
+                               width: 0,\r
+                               height: 0,\r
+                               padding: 0,\r
+                               border: 0,\r
+                               margin: 0,\r
+                               position: 'absolute',\r
+                               top: 0,\r
+                               left: '-9999px',\r
+                               opacity: 0,\r
+                               overflow: 'hidden'\r
+                       } );\r
+\r
+                       return filler;\r
+               },\r
+\r
+               /**\r
+                * Removes the specific filler element from the given editable. If the filler contains any content (typed or pasted),\r
+                * it replaces the current editable content. If not, the caret is placed before the first or after the last editable\r
+                * element (depends if the filler was at the beginning or the end).\r
+                *\r
+                * @param {CKEDITOR.dom.element} filler\r
+                * @param {CKEDITOR.editable} editable\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               removeFiller: function( filler, editable ) {\r
+                       if ( filler ) {\r
+                               var editor = editable.editor,\r
+                                       currentRange = editable.editor.getSelection().getRanges()[ 0 ],\r
+                                       currentPath = currentRange.startPath(),\r
+                                       range = editor.createRange(),\r
+                                       insertedHtml,\r
+                                       fillerOnStart,\r
+                                       manuallyHandleCaret;\r
+\r
+                               if ( currentPath.contains( filler ) ) {\r
+                                       insertedHtml = filler.getHtml();\r
+                                       manuallyHandleCaret = true;\r
+                               }\r
+\r
+                               fillerOnStart = filler.getAttribute( this.fillerAttribute ) == 'start';\r
+                               filler.remove();\r
+                               filler = null;\r
+\r
+                               if ( insertedHtml && insertedHtml.length > 0 && insertedHtml != this.fillerContent ) {\r
+                                       editable.insertHtmlIntoRange( insertedHtml, editor.getSelection().getRanges()[ 0 ] );\r
+                                       range.setStartAt( editable.getChild( editable.getChildCount() - 1 ), CKEDITOR.POSITION_BEFORE_END );\r
+                                       editor.getSelection().selectRanges( [ range ] );\r
+\r
+                               } else if ( manuallyHandleCaret ) {\r
+                                       if ( fillerOnStart ) {\r
+                                               range.setStartAt( editable.getFirst().getNext(), CKEDITOR.POSITION_AFTER_START );\r
+                                       } else {\r
+                                               range.setEndAt( editable.getLast().getPrevious(), CKEDITOR.POSITION_BEFORE_END );\r
+                                       }\r
+                                       editable.editor.getSelection().selectRanges( [ range ] );\r
+                               }\r
+                       }\r
+               },\r
+\r
+               /**\r
+                * Creates a regular expression which will match the filler HTML in the text.\r
+                *\r
+                * @param {Boolean} [onEnd] Whether a regular expression should be created for the filler at the beginning or\r
+                * the end of the content.\r
+                * @returns {RegExp}\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                * @private\r
+                */\r
+               createFillerRegex: function( onEnd ) {\r
+                       var matcher = this.createFiller( onEnd ).getOuterHtml()\r
+                               .replace( /style="[^"]*"/gi, 'style="[^"]*"' )\r
+                               .replace( />[^<]*</gi, '>[^<]*<' );\r
+\r
+                       return new RegExp( ( !onEnd ? '^' : '' ) + matcher + ( onEnd ? '$' : '' ) );\r
+               },\r
+\r
+               /**\r
+                * Adds an integration for the [Select All](http://ckeditor.com/addon/selectall) plugin to the given `editor`.\r
+                *\r
+                * @private\r
+                * @param {CKEDITOR.editor} editor\r
+                * @member CKEDITOR.plugins.widgetselection\r
+                */\r
+               addSelectAllIntegration: function( editor ) {\r
+                       var widgetselection = this;\r
+\r
+                       editor.editable().attachListener( editor, 'beforeCommandExec', function( evt ) {\r
+                               var editable = editor.editable();\r
+\r
+                               if ( evt.data.name == 'selectAll' && editable ) {\r
+                                       widgetselection.addFillers( editable );\r
+                               }\r
+                       }, null, null, 9999 );\r
+               }\r
+       };\r
+\r
+\r
+       function filterTempElements( el ) {\r
+               return el.getName && !el.hasAttribute( 'data-cke-temp' );\r
+       }\r
+\r
+} )();\r