Hello fellow CKEditor users,
I've been working on developing my first plugin for CKEditor and my goal is to create a replacement for the source view editor using ACE Editor.
I've got my plugin working on IE and FireFox, but for some reason Chrome and Safari are having issues with one aspect of the plugin.
Here's the problem:
in Chrome and Safari, when you've switched over to "source" view and you hit the "backspace" key, you can no longer continue typing until you click away from the editor and then click back inside. I've ben able to trace the problem to the following line of code:
// Set the data of the CKEditor to the value of ACE Editor editor.setData( aceEditor.getSession().getValue(), function() { console.log('change saved'); }, false);
This line of code gets called in the function updateCKEditor() which gets called every time there is a change in Ace editor:
// when the ace editor changes, update CKEditor's source code aceEditor.getSession().on('change', updateCKEditor );
However, the weird thing is that if I switch the internal trigger "false" to "true" Chrome and Safari don't experience the odd "unable to type after hitting backspace" issue but then the value of ACE Editor doesn't get set to CKEditor and the function above silently fails. See below:
// Set the data of the CKEditor to the value of ACE Editor editor.setData( aceEditor.getSession().getValue(), function() { console.log('change saved'); }, true);
This leads me to believe that somehow the CKEditor setData() function is causing some kind of key block in ACE editor.
I'm not exactly sure how to go about debugging this type of error and I've been wrestling with this issue for about 3 days straight. If anyone could please lend a hand it would be most appreciated.
If you want to see a live demo of the issue you can go here. To reproduce the issue:
1. Open FireFox (make sure you have firebug and/or debugging console open)
2. Load the web site and click "source" in CKEditor.
3. Type something. Hit backspace. Type some more
4. Open Chrome (make sure you have debugging console open)
5. Load the web site and click "source" in CKEditor
6. Type something. Hit backspace. Type some more. You should not be able to type until you click away from the editor and back in.
Here is the code for the full plugin in its current state:
/** * The "ace" plugin. * This plugin is designed to enhance the source view editor for CKEditor 4 * and implement ACE Editor as a replacement. http://ace.c9.io * This plugin is based on the initial work on the CodeMirror plugin here: http://marijn.haverbeke.nl/codemirror/ * plugin is using. */ CKEDITOR.plugins.add( 'ace', { requires : [ 'sourcearea' ], // Initialize the plugin init : function( editor ) { console.log('initializing ACE plugin'); // When the event "mode" is called in CKEditor it means someone clicked the "source" button. editor.on( 'mode', function() { console.log('editor mode: ' + editor.mode); // if the user selected the "source" view if ( editor.mode == 'source' ) { // define some variables for the textarea that CKEditor is using, the parent element which is the container for CKEditor. var sourceAreaElement = $('textarea','.' + editor.id); var holderElement = sourceAreaElement.parent(); // hide the textarea sourceAreaElement.hide(); // START ACE var editorID = editor.id; var editorName = editor.name; $('.' + editorID).after('<div id="aceEditor_container_' + editorID + '" class="aceEditor_container" style=" background-color:white; position:absolute;">\ <div id="aceEditor_' + editorID + '" style="width:100%; height:100%;"></div></div>'); // make the editor container fill the space. $('#aceEditor_container_' + editorID).css( holderElement.position() ).width( holderElement.width() ).height( holderElement.height() ); // launch the editor and set the theme var aceEditor = ace.edit("aceEditor_" + editorID); aceEditor.setTheme("ace/theme/dreamweaver"); aceEditor.getSession().setMode("ace/mode/html"); aceEditor.setShowPrintMargin( 0 ); aceEditor.getSession().setValue( editor.getData() ); aceEditor.getSession().setUseWrapMode(false); // set the z-index for ACEEditor really high $('#aceEditor_container_' + editorID).css('z-index','9997'); // CUSTOM FUNCTIONS // this function checks to see if we are returning to design view. If so, purge all the ACE stuff function returnToDesignView(e) { console.log('Before Command Exec: ' + e.data.name); if ( e.data.name == 'source') { // set the value of the editor console.log('going back to CKEditor Design view'); // Set the data of the CKEditor to the value of ACE Editor editor.setData( aceEditor.getSession().getValue(), function() { console.log('change saved'); }, false); // destroy the editor aceEditor.destroy(); //remove the container $('#aceEditor_container_' + editorID).remove(); // Remove the listeners editor.removeListener('beforeCommandExec', returnToDesignView ); editor.removeListener('resize', resizeACE ); editor.removeListener('afterCommandExec', maximizeACE ); editor.fire( 'dataReady' ); } } // this function will update the z-index of the ACE Editor based on whether we are maximized or not function maximizeACE(e) { console.log('After Command Exec: ' + e.data.name); // if they are maximizing it if (e.data.name == 'maximize'){ // if maximixed if (e.data.command.state == 1 ) { $('#aceEditor_container_' + editorID).css('z-index','9997'); } else { $('#aceEditor_container_' + editorID).css('z-index','auto'); } } // if we are still in source view, remove this listener //if (e.data.name == 'source'){ //e.removeListener(); //} } // resizeACE // this function will resize ACE editor to match the holderElement object's position function resizeACE() { console.log('resizing ace'); // make the editor container fill the space. $('#aceEditor_container_' + editorID).css( holderElement.position() ).width( holderElement.width() ).height( holderElement.height() ); aceEditor.resize(); } // updateCKEditor // this function updates the value of CKeditor function updateCKEditor(){ console.log('change detected'); // Set the data of the CKEditor to the value of ACE Editor editor.setData( aceEditor.getSession().getValue(), function() { console.log('change saved'); }, true); return false; }; // BIND EVENTS // when the ace editor changes, update CKEditor's source code aceEditor.getSession().on('change', updateCKEditor ); // Commit source data back into 'source' mode. editor.on( 'beforeCommandExec', returnToDesignView ); // When the editor fires the 'resize' event call the resize function. editor.on('resize', resizeACE); // run this after a command exeecutes in CKEditor editor.on('afterCommandExec', maximizeACE ); } }); // If we are sending them back to the WYSIWYG editor. editor.on( 'instanceReady', function(e) { console.log('instance ready'); e.removeListener(); if ( editor.mode == 'wysiwyg' ) { var thisData = editor.getData().indexOf('<?php'); if (thisData !== -1) { editor.execCommand('source'); }; } }); // in case we want to do anything when CKEditor fires the 'dataReady' event editor.on( 'dataReady', function(e) { console.log('data ready'); }); } });
That's a whopper... You might
That's a whopper... Thanks for taking the time and getting it this far. You might need additional help to get this done because if it's not working on Safari and Chrome (and probably IE), you have a long way to go before it's ready. Have you also posted this on Stackoverflow? They love these kinds of challenges.
Customer and Community Manager, CKSource
Follow us on: Facebook, Twitter, LinkedIn
If you think you found a bug in CKEditor, read this!