CKeditor, Stop Altering Elements!!!

CKEditor is very opinionated on what needs to be altered/removed. It parses and filters html too strict for our needs, so this post will cover how to loosen up CKeditor to stop removing/altering html.

Before we move on, lets clarify that we can alter CKEditor behavior in 3 ways:

  • From HTML – by adding attributes provided by CKEditor such as data-cke-survive, data-cke-filter, etc
  • From Configurations – by changing CKEditor configurations in CKEDITOR.dtd
  • From Implementation – by implementing additional custom processor,filter,rules and attaching it to CKEditor mechanism

We will not cover the “From Implementation” in this post. Our perfect solution is to accomplish our task from configuration but we will also show how its done from HTML with CKEditor provided attributes.

Stop Removing Empty Anchor Tag in CKEditor

CKEditor is removing empty anchor tags such as the following:

<a data-original-title="youtube" class="youtube" href="#"> </a>
  • From HTML
    To stop removing anchors if the html is altered as following:

    <a data-original-title="youtube" class="youtube" href="#" data-cke-survive="true"> </a>
    //--OR--
    <a data-original-title="youtube" class="youtube"> </a>
    

    So, by adding new attribute of data-cke-survive or by removing attribute href, the empty anchor elements are not being removed

  • From Configurations
    We like to stop removing empty anchor tags from configurations as following:

    if(window.CKEDITOR){
                    CKEDITOR.on('instanceCreated', function (ev) {
                        CKEDITOR.dtd.$removeEmpty['a'] = 0;
                   }
    }
    

    This works if we alter the CKeditor library by changing function RemoveEmpty to

    function isRemoveEmpty( node ) {
    		// Keep marked element event if it is empty.
    		if ( node.attributes[ 'data-cke-survive' ] )
    			return false;
    
    		// Empty link is to be removed when empty but not anchor. (#7894)
    		//return node.name == 'a' && node.attributes.href || CKEDITOR.dtd.$removeEmpty[ node.name ];
    		return CKEDITOR.dtd.$removeEmpty[ node.name ];
    	}
    

    We realize this is not often times acceptable to update CKEditor library, however. This was only way we know to accomplish this from configurations. If you know how to do that without altering CKEditor library, please, let us know. We have also asked at the CKEditor forums:
    http://ckeditor.com/forums/CKEditor/How-to-stop-removing-empty-anchor-element

Stop Removing ANY Empty Tag in CKEditor

There is a defined list of tags that is going to be removed if empty(see dtd.js and $removeEmpty or run CKEDITOR.dtd.$removeEmpty from console).

  • From HTmL
    To ensure the certain empty tag are not being removed, add attribute ‘data-cke-survive’:

    <span data-cke-survive="true" ></span>
    
  • From Configurations
    Or you can configure the particular tag from not be removed:

    if(window.CKEDITOR){
                    CKEDITOR.on('instanceCreated', function (ev) {
                        CKEDITOR.dtd.$removeEmpty['span'] = 0;
                        CKEDITOR.dtd.$removeEmpty['TAG-NAME'] = 0;
                   }
    }
    

By setting an element to 0 in the CKEDITOR.dtd.$removeEmpty, it prevents the empty tags from being removed by CKEditor.

Stop Auto Wrap Tags in CKeditor

The default behavior is for CKEditor to wrap any inline elements into paragraph tags. To stop that globally:

if(window.CKEDITOR){
                CKEDITOR.on('instanceCreated', function (ev) {
                    CKEDITOR.config['autoParagraph'] = false
                    
                    //or for certain editor instance
                    if (ev.editor.name == textId) {
                        ev.editor.on('customConfigLoaded', function () {
                            ev.editor.config.autoParagraph = false;
                        });
                    }
               }
}
      

This will stop automatically wrapping every element into paragraph tag.
To stop wrap a certain element such as anchor tag into paragraph tag:

if(window.CKEDITOR){
                CKEDITOR.on('instanceCreated', function (ev) {
                      delete CKEDITOR.dtd.$inline['a'];
                }
}

This will change the declaration of the anchor tag to not be an inline element, thus, no any wrapping takes place

Perhaps, you like to wrap inline elements, but instead of paragraph tags, you like to wrap into ‘div’, ‘br’ or any tag of your choice, then:

CKEDITOR.config['entermode'] = CKEDITOR.ENTER_BR;//or config.entermode=2 in config.js
CKEDITOR.config['entermode'] = CKEDITOR.ENTER_DIV;//or config.entermode=23 in config.js

This will add BR or DIV tag to every inline element

Stop Wrap UL around LI in CKEditor

By default, CKeditor is wrapping UL element around LI if one is not present. To stop:

if(window.CKEDITOR){
                CKEDITOR.on('instanceCreated', function (ev) {
                  //make sure LI is not wrapped within UL
                    delete CKEDITOR.dtd.$listItem['li'];
                    delete CKEDITOR.dtd.$intermediate['li'];    
                }
}

By removing the LI form the list, you ensure that CKEditor is not wrapping LI elements with additional “UL”.

Stop Filtering in CKEditor

By default, CKEditor runs its processors and filters when outputing content.

  • From Html – To disable the filter or processing on a particular tag and children add the the attribute as following:
    • data-cke-filter=”off”
    • data-cke-processor=”off”
    • autocomplete=”off”

    The data-cke-filter stops the filering, the data-cke-processor stops the processor and the autocomplete for both. This will apply for the tag and all of its children

  • From Configuration – To disable processing/filtering from configurations, add the following:
    if(window.CKEDITOR){
                    CKEDITOR.on('instanceCreated', function (ev) {
                        //disable filters
                        ev.editor.on('customConfigLoaded', function () {
                            ev.editor.config.allowedContent = true;
                        });
                    }
    }
    

Here by setting configuration allowedContent to true, it disables the filtering/processing editor content output

Taking CKEditor a Apart

In this post, we dive into the code of CKEditor to learn internals. We needed to do so to understand how the empty tags are being removed by default. This was not acceptable behavior because frameworks like Twitter Bootstrap or Foundation use empty tags part of the layout.

The dev version none minimized CKEditor are located at https://github.com/ckeditor/ckeditor-dev

Boot from Console

When troubleshooting or learning, it always easer to move everything else out of the way but the thing of your interest. In our case, we decided to run CKEditor without Drupal in the browser console for quick, easy changes. In the following are steps to boot CKEditor from console:

1. Disable Drupal

To take Drupal out of way, we disable the CKEditor module

2. Import CKEditor

There were several ways to load CKEditor all of which are listed here

A)From HTML Layout
By inserting the following in top of your page within ‘head’ tag

<script src="http://dev-ckeditor/sites/all/libraries/ckeditor/ckeditor.js?n83a0r"></script>

B) From Drupal API
By executing the following code in hook_preprocess_page() or any other appropriate place for that matter

drupal_add_js('http://dev-ckeditor/sites/all/libraries/ckeditor/ckeditor.js', array('scope' =>; 'header', 'type' =>; 'external'));

C)From JS Console
By calling the following, ckeditor library will be loaded

jQuery.getScript('http://dev-ckeditor/ckeditor/ckeditor.js');

NOTE: In console, I run into errors when ckeditor.js was trying to load its dependences such ad load.js due to unable configure base path. I believe it should work if using the production ckeditor.js instead the development version that is not compiled into one

This will import the library as well as initialize CKeditor after which making the global variable CKEDITOR available and ready, so we can call CKEditor API

Let’s check the status

CKEDITOR.status

It gives status – ‘loaded’ on success and its ready to start ckeditor
There are 4 different status – loaded, basic_load, ???

4. Load Into Textarea

To start up CKEditor in the textarea is as simple as initializing it that can be done from console as following:

CKEDITOR.replaceAll();
//or particular element with certain id
CKEDITOR.replace('id_name');

Here, we start up all of the textareas by calling replaceAll(). In the second, we enable CKEditor on certain element not necessaryly ‘textarea’ with id of ‘id_name’. This should bring up the text editor

Other Stuff

1. Editor Instances
CKEDITOR.instances

This will display all the ckeditor instances

2. See Editor Configuration

From console:

var editor = CKEDITOR.instances.editor1;
alert( editor.config.skin ); // e.g. 'moono'
3. Filters

To see what input is about to be filtered:

CKEDITOR.instances['edit-body-value'].element.$

Setting custom filters:

var filter = new CKEDITOR.filter( 'p strong em br' );
editor.setActiveFilter( filter );
...
...
// Create standalone filter passing 'p' and 'b' elements.
		 *		var filter = new CKEDITOR.filter( 'p b' ),
		 *			// Parse HTML string to pseudo DOM structure.
		 *			fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<p><b>foo</b> <i>bar</i></p>' ),
		 *			writer = new CKEDITOR.htmlParser.basicWriter();
		 *
		 *		filter.applyTo( fragment );
		 *		fragment.writeHtml( writer );
		 *		writer.getHtml(); // -> '<p><b>foo</b> bar</p>'

For hooking into all enable active filters:

editor.on( 'activeFilterChange', function() {
     if ( editor.activeFilter.check( 'cite' ) )
        // Do something when <cite> was enabled - e.g. enable a button.
     else
        // Otherwise do something else.
   } );

To disable filters:

//before the editor is initiated
 CKEDITOR.config.allowedContent = true;
textarea_settings = Drupal.ckeditorLoadPlugins(textarea_settings);
CKEDITOR.replace(textarea_id, textarea_settings);

Here, the textarea_id is the ‘id’ of the element(textarea or div). I am not sure the textarea_settigns???

5. Drupal and CKEditor

In the modules/ckeditor/ckeditor.utils.js, is where the CKEditor instances are created and attached to textarea.
From ckeditor.utils.js, the function CKEditor.replace is called that is defined in the themedui.js that calls CKEDITOR.editor from editor.js

6. Fragmenting in CKEditor
var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<p>foo<b>bar</b>bom</p>' );
 fragment.forEach( function( node ) {
 console.log( node );
} );
		 *		// Will log:
		 *		// 1. document fragment,
		 *		// 2. <p> element,
		 *		// 3. "foo" text node,
		 *		// 4. <b> element,
		 *		// 5. "bar" text node,
		 *		// 6. "bom" text node.
		 *
		 * @since 4.1
		 * @param {Function} callback Function to be executed on every node.
		 * **Since 4.3** if `callback` returned `false` descendants of current node will be ignored.
		 * @param {CKEDITOR.htmlParser.node} callback.node Node passed as argument.
		 * @param {Number} [type] If specified `callback` will be executed only on nodes of this type.
		 * @param {Boolean} [skipRoot] Don't execute `callback` on this fragment.
7. Stop Removing Empty Tags

There is defined list of tags that is going to be removed if empty(see dtd.js and $removeEmpty). To ensure the certain empty tag are not being removed, add attribute ‘data-cke-survive’:

<span data-cke-survive="true" ></span>

or pass the following to the CKEditor configuration file:

CKEDITOR.dtd.$removeEmpty['span'] = 0;
CKEDITOR.dtd.$removeEmpty['TAG-NAME'] = 0;

This make the empty tags to not being removed
NOTE: If you working with the production CKEditor.js, you will have to alter section starting “CKEDITOR.dtd” as following:

 CKEDITOR.dtd=function(){var a=CKEDITOR.tools.extend,e=function(a,d){for(var [...]
span:0//set the 
TAG:0
..

You will have to grep the CKEditor.js file for section starting “CKEDITOR.dtd” and then change value from 1 to 0 for each tags you like to not be removed if empty(see above example for ‘span’ tag)

8. Update Styles

Here is an example for console to update styles:

var editor = CKEDITOR.instances['edit-body-und-0-value'];
editor.document.getBody().setStyle('font-size','30px');

Here, we start by retrieving the CKEditor Instance with id “edit-body-und-0-value”. Afterwards, we set the font-size.

if you like append the whole style sheet than:

editor.document.appendStyleSheet('/sites/all/path/to/css/bootstrap.min.css')
9. Stop Auto Wrap

The default behavior is for CKEditor to wrap text into paragraph tags. To stop that:

//from console
CKEDITOR.config['autoParagraph'] = false

–OR–
In the ckeditor.config.js file:

config.autoParagraph = false

This will stop automatically wrapping every element into paragraph tag. You can also set to add ‘br’ or ‘div’ instead of pargraph tags as following:

CKEDITOR.config['entermode'] = CKEDITOR.ENTER_BR;//or config.entermode=2 in config.js
CKEDITOR.config['entermode'] = CKEDITOR.ENTER_DIV;//or config.entermode=23 in config.js

This will add BR or DIV tag to every element

To stop wrap a certain element such as anchor tag into paragraph tag:

                delete CKEDITOR.dtd.$inline['a'];

This will change the declaration of the anchor tag to not be an inline tag, thus, no any wrapping takes place

Stop remove Empty Anchor tags

To stop removing empty tags, add the attribute – “data-cke-survive” to the anchor tag as following:

<a href="#" data-original-title="github" class="github" data-cke-survive="true">

Or ensure attribute “href” is not present as following:

<a data-original-title="linkedin" class="linkedin">
Stop Wrap UL around LI

By default, CKeditor is wrapping UL element around LI if one is not present. To stop alter CKEDITOR.dtd.$listItem in DTD.js

//OLD:$listItem: { dd: 1, dt: 1, li: 1 },
        $listItem: { dd: 1, dt: 1},

–OR–
By editing CKEditor directly will cause problems sooner or later. Instead, hook into CKeditor and update from outside

if(window.CKEDITOR){
                CKEDITOR.on('instanceCreated', function (ev) {
                  //make sure LI is not wrapped within UL
                    delete CKEDITOR.dtd.$listItem['li'];
                    delete CKEDITOR.dtd.$intermediate['li'];    
                }
}

Here, we hook into the event triggered when CKEditor instance is created to remove “li’ tag from inline tags list.
By removing the LI form the list, you ensure that CKEditor is not wrapping LI elements with additional “UL”. For production, you will have to grep ckeditor.js file to make the change since everything is compiled into one JS file

Hook Into Events

There are lot of events available either on CKEDITOR App variable – CKEDITOR or on each editor instance itself

To listen for event on CKEDITOR APP variable

<script>
CKEDITOR.on( 'instanceReady', function( ev ) {
    editor = ev.editor;
    if(editor.name == 'edit-body-value'){
         console.log( 'appending style - sites/all/themes/metronic/assets/drupal/custom_metronic.css' );
         editor.document.appendStyleSheet('/path/custom_metronic.css');
}
}
</script>      

Here, we sign up for event “InstanceReady” to append custom stylesheets.

To listen for event on editor instance itself

CKEDITOR.on( 'instanceCreated', function( ev ) {
    if(ev.editor.name == 'edit-body-value'){
       ev.editor.on( 'contentDom', function(){
         var ck_dom = ev.editor.document.$;
         var s0= ck_dom.createElement( 'script' );
         s0.type = 'text/javascript';
         s0.onload = function() {
             console.log( 'script /path/jquery-1.11.0.min.js loaded' );
         }
         s0.src = '/path/jquery-1.11.0.min.js';
         s0.$ = s0;
         ck_dom.head.appendChild(s0);
      } );
   }
 });   

Here, we first use event on the CKEDITOR App itself to get handle of the editor obj. Once we got the editor object, we sign up for an event “contentDom” that is triggered once the DOM of editor content is constructed. During this event we are importing JQuery lib

See What’s Going On

To see what is filtered, processed and actually sitting in the editor content(DOM) at any give time from console:

var editor = CKEDITOR.instances['edit-body-value'];
editor.document.$.body //to see what is within BODY tags
editor.document.$.head //to see what is within HEAD tags

Here, we first retrieve the particular textarea of our interest and then navigate to the BODY or HEAD. It will display the DOM in real time

How To Find Which Config.js CKEditor Is Using

To see which config.js the CKEditor is actually using, once the instance is up, go to console and type:

CKEDITOR.config.customConfig
CKEDITOR.instances['edit-body-value'].config.customConfig

This will print out the path and the name of config file CKEditor is using. Note, there may be different config files used at the same time. The CKEDITOR app using one and then each different editor instance(i.e.CKEDITOR.instances.editor1) using another one(perhaps one loaded from the theme folder,etc)

Some Tags are Removed

you can disable the filter with allowedContent configuration as described above. You can also disable processing on a particular tag as following:

  • data-cke-filter=”off”
  • data-cke-processor=”off”
  • autocomplete=”off”

This will apply for the tag and all of its children

Manipulate Editor Content

To remove some element from the the editor:

var element = ev.editor.document.getById( 'ckeditor-wrapper-end' );
element.remove(true);

This will remove element with id ckeditor-wrapper-end while keeping parent and children elements intact.
The better way API for manipulated data of editor is available on “editable” object:

var editable = CKEDITOR.instances['.textId'].editor.editable();
if(editable.find('ckeditor-wrapper-end').count()) editable.find('.ckeditor-wrapper-end').remove();

As you can see, the “editable” API has similar API as jQuery. Please, reference editable.js for complete list of function available.

Update Editor

To update editor content:

var editor = CKEDITOR.instances['TEXTAREA-ID'];
var newHtml = '<p>Hello World!</p>";
editor.setData(newHtml,{
       callback: function() {
         this.updateElement();
        }
});
$('textarea#TEXTAREA-ID').html(newHtml);

Here, we first retrieve editor and then update its content via setData() API. At last, the textarea itself is updated in the DOM

Summary of Events

Event Scope comments
loaded editor triggered once editor is loaded before initiating the editor content
instanceLoaded CKEDITOR triggered once CKEDITOR is loaded
customConfigLoaded editor
configLoaded editor
destroy editor
save editor
key editor
dblclick editor
doubleclick editor
click editor
beforeGetData editor Handle the load/read of editor data/snapshot.
getSnapshot editor sets event data from the editor data
saveSnapshot editor ???
lockSnapshot editor ???
unlockSnapshot editor ???
afterSetData editor sets the data from editor data
loadSnapshot editor takes event data and sets it to the editor data
beforeFocus editor Delegate editor focus/blur to editable.
insertHtml editor takes the data from event and inserts in CKEDITOR.DOM via insertHTML()
insertElement editor takes the data from event and inserts in CKEDITOR.DOM via insertElement()
insertText editor takes the data from event and inserts in CKEDITOR.DOM via insertText()
beforeSetMode editor Before any mode is set on the Editor(i.e. editor.mode)(see plugin.js)
beforeModeUnload editor after mode is set on the editor(i.e. editor.mode)(see plugin.js)
beforeGetModeData editor before getting data after setting the mode(see plugin.js)
afterModeUnload editor passing data to be able update at last step in mode setup(see plugin.js)
toHtml editor at the time data is process including filters applied(see htmlprocessor.js)
dataFiltered editor after some filter applied(see htmldataprocessor.js)
dataReady editor when data is ready before processing and filtering(see htmldataprocessor.js)

To fire any of the events:

editor.fire( 'saveSnapshot' );

Here, we trigger one of the events – saveSnapshot attached to the editor.
To listen an event:

			editor.on( 'saveSnapshot', function( evt ) {
                                //do something with evt.data or evt.data.contentOnly
			} );

Here we listen of event – save Snapshot and then grab the new data via evt.data or evt.data.contentOnly

Encode and Decode

To decode or encode a string:

var result = CKEDITOR.tools.htmlDecode(editor.getData());
var encodedText = CKEDITOR.tools.htmlEncode(result);

Here, the editor data is decoded and encoded. See CKEDITOR.tools for much more utilities

Troubleshooting

1.Resource interpreted as Script but transferred with MIME type text/html: “http://dev-ckeditor/node/add/core/loader.js”

This happened when I was trying to load CKEditor library from console and the CKEditor was trying to load its dependencies. The dependency URL(path) came up wrong resulting in this error. I tried to set CKEditor Base path but it didn’t work because the base path needed to be set before the CKEdiotr library is loaded. I tried that and it didn’t work

The changes doesn’t take an effect

Make sure you clear browser cache as well as Drupal cache for the most current config.js loaded

Adding CKEditor Plugins Manually in Drupal

CKEditor has a lot of new good plugins that doesn’t have supporting Drupal module for easy integration. In this post, we go over how to integrate any native CKEditor plugin without supporting Drupal module present. Specifically, we cover how to install the plugin as part of CKEditor module and how to do the same but as separate custom module. In this post, we have CKEditor installed as a stand alone module not part of Drupals WYSIWYG install. How to install stand alone editor such as CKEditor, please, see post Wysiwyg With Ckeditor In Drupal

To make it more practical, lets install CKEditor plugin – Leaflet that adds map functionality to the editor

Add Plugin Part of CKEditor Module

1.) Download and unzip the plugins here (depending on your setup):

“sites/all/modules/ckeditor/plugins” or “sites/all/modules/contrib/ckeditor/plugins”.

After that, you should have the following folder structures:
“ckeditor/plugins/leaflet”
“ckeditor/plugins/lineutils”
“ckeditor/plugins/widget”

2.) We should now activate the new plugins and add them to the toolbar.

This is done by configuring the Ckeditor Profile Settings, in which by default is located in:
“admin/config/content/ckeditor/edit/Advanced”
“admin/config/content/ckeditor/edit/Full”

A. Activate the Plugins
In EDITOR APPEARANCE >>> Plugins section:
Enable the corresponding checkboxes for the Leaflet, Line Utilities, and Widget plugins.
These are the texts displayed adjacent to their checkboxes:
“Plugin file: leaflet”
“Plugin file: lineutils”
“Plugin file: widget”

B. Add them to the Toolbar
We should make the activated plugins visible in the toolbar,
skipping this step will make the Leaflet plugin inaccessible in the toolbar.

In EDITOR APPEARANCE >>> Toolbar section:
Drag the Leaflet Maps icon (black-colored) from the ‘All Buttons’ section to the ‘Used Buttons’ section.

We need to configure the Leaflet Maps icon only since the Line Utilities and Widget plugins
have no toolbar icons and they will just load in the background.

3.) Then, click the Save button. Clear the Drupal’s overall cache AND clear the browser’s cache.

Clearing the browser’s cache is also very important since the CKEditor’s JS and CSS assets/components are cached also in the browser.

As indicated above, using the “sites/all/modules/ckeditor/plugins” or “sites/all/modules/contrib/ckeditor/plugins”
will work with no additional custom hooks programming since by default CKEditor utilize that folder
in the CKEditor Global Profile Settings: “admin/config/content/ckeditor/editg”.

Adding Plugins As Stand Alone Module

The above approach requires the plugins to be part of the CKEditor Module, but it may not be desirable especially in case for distributing your custom module that requires one of the CKEditor plugin. In that case, its good idea to keep CKEditor plugins in stand alone module part of your custom module perhaps. Here is how to do so:

Lets say your custom module name is ‘custom-module’ and you are going to keep CKEditor plugins in sub-folder ‘ckeditor-plugins’, then the destination location would be:
“sites/all/modules/custom-module/ckeditor-plugins”

After that, you should have the following folder structures:
“sites/all/modules/custom-module/ckeditor-plugins/leaflet”
“sites/all/modules/custom-module/ckeditor-plugins/lineutils”
“sites/all/modules/custom-module/ckeditor-plugins/widget”

2.) Hook Plugins Into CKEditor

Next, lets hook the plugins into CKEditor via hook_ckeditor_plugin as following in custom-module.module file:

/********CKEditor plugins********/
function custom-module_ckeditor_plugin()
{
    $module_path = drupal_get_path('module', 'custom-module');
    $plugins     = array();

    $plugins['leaflet'] = array(
        'name'    => 'leaflet',
        'desc'    => t('DesignsSquare.com: CKEditor: Leaflet plugin'),
        'path'    => $module_path . '/ckeditor-plugins/leaflet/',
        'default' => 't',
        'buttons' => array(
            'leaflet' => array(
                'label' => 'Leaflet Map',
                'icon'  => 'icons/leaflet.png',
            ),
        ),
    );

    $plugins['lineutils'] = array(
        'name'    => 'lineutils',
        'desc'    => t('DesignsSquare.com: CKEditor: LineUtils plugin'),
        'path'    => $module_path . '/ckeditor-plugins/lineutils/',
        'default' => 't'
    );

    $plugins['widget'] = array(
        'name'    => 'widget',
        'desc'    => t('DesignsSquare.com: CKEditor: Widget plugin'),
        'path'    => $module_path . '/ckeditor-plugins/widget/',
        'default' => 't'
    );

    return $plugins;
}

Here, the variable ‘name’ is as specified in the plugin.js that is part of all CKEditor Plugins (look for – CKEDITOR.plugins.add(“NAME”‘) – in the plugin.js for each plugin). The button index(highlight line) has to be exactly as specified in the plugin.js(look for ‘ui.addButton(“BUTTON-NAME”). The variable ‘icon’ specifies the location for the image of the button. Ensure it is there.

Afterwards, the plugins will show up in the CKEditor Configuration section(admin/config/content/ckeditor/edit/Full(or Advance). The rest is the same from the first approach where you would check the plugins in ‘Plugins’ section at admin/config/content/ckeditor/edit/Full(or Advance) and then move the icon into Toolbar.

Troubleshooting

1. Unable To Add Icons in Toolbar

Turn off the toolbar and add icons manually. To turn off the toolbar go to the admin/config/content/ckeditor/editg and select ‘disable’ for ‘Use toolbar Drag&Drop feature’. I am guessing that the toolbar misbehaves because CKEditor Module imports JQuery UI sortable lib that is already imported once by the core. As result, it causes js fatal errors. I am out of time to verify.

2. Uncaught TypeError: Cannot read property ‘icons’ of null

Ensure the name of the plugin as specified in the plugin.js(i.e. CKEDITOR.plugins.add(‘Here-comes-name’,) is the same used in the hook_ckeditor_plugin(). Set a break point with condition of ‘s === null’ where s is the variable the attribute ‘icons’ is called on(i.e s.icons) to help identified which plugin is causing issue in ckeditor.js

References:

  • http://docs.cksource.com/CKEditor_for_Drupal/Enterprise/Drupal_7/Plugins
  • https://github.com/ranelpadon/ckeditor-leaflet/blob/master/Installation%20Guide.txt