add ckeditor support via onchange plugin

This commit is contained in:
John Bintz 2012-11-04 11:07:03 -05:00
parent b1bd652fe9
commit 099d666fd2
6 changed files with 310 additions and 1 deletions

2
.gitignore vendored
View File

@ -15,3 +15,5 @@ spec/reports
test/tmp test/tmp
test/version_tmp test/version_tmp
tmp tmp
.DS_Store

View File

@ -0,0 +1,74 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>OnChange event plugin</title>
<link href="styles.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>On Change event Plugin for CKEditor</h1>
<h2>Introduction</h2>
<p>This is a plugin that tries to fire a 'change' event whenever the content of a <a href="http://www.ckeditor.com">CKEditor</a> instance is changed.</p>
<h3 id="contact">Author:</h3>
<p><a href="mailto:amla70@gmail.com">Alfonso Mart&iacute;nez de Lizarrondo</a></p>
<h3>Sponsored by:</h3>
<p>Falcana</p>
<h3>Version history: </h3>
<ol>
<li>1.0: 21-January-2011. First version.</li>
<li>1.1: 3-September-2011. Fixed issues with the UndoManager events. Detect changes in Source mode.</li>
<li>1.2: 18-September-2011. Avoid too many events in CKEditor 3.6.2. Filter keyboard to skip control and movement keys.</li>
<li>1.3: 22-December-2011 Avoid firing the event after the editor has been destroyed.</li>
<li>1.4: 7-September-2012 Don't fire events if the editor is readonly, thanks to Ulrich Gabor. Included code to use Mutation Observers.</li>
<li>1.5: 20-October-2012 Detect Cut and Paste for IE in source mode thanks to Jacki.</li>
</ol>
<h2>Installation</h2>
<h3>1. Copying the files</h3>
<p>Extract the contents of the zip in you plugins directory, so it ends up like
this<br>
<!--<img src="installation.png" alt="Screenshot of installation" width="311" height="346" longdesc="#install">-->
</p>
<pre id="--install">
ckeditor\
...
images\
lang\
plugins\
...
onchange\
plugin.js
docs\
install.html
...
skins\
themes\
</pre>
<h3>2. Adding it to CKEditor</h3>
<p>Now add the plugin in your <em>config.js</em> or custom js configuration
file:
<code>config.extraPlugins='onchange'; </code>
</p>
<h3>3. Configuration</h3>
<p>You can limit the minimum time between changes to avoid getting too many events fired:
<code>config.minimumChangeMilliseconds = 100; // 100 milliseconds (default value)
</code>.</p>
<h3>4. Use it</h3>
<p>Write your listener for the new 'change' event and perform whatever action you need there.
<code>editor.on( 'change', function(e) { console.log(e) });
</code>.</p>
<!--
<h2>Final notes</h2>
-->
<h2>Disclaimers</h2>
<p>CKEditor is &copy; CKSource.com</p>
</body>
</html>

View File

@ -0,0 +1,59 @@
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 90%;
}
h1 {
text-align:center;
font-size:180%;
}
h2 {
border-bottom:2px solid #CCC;
margin:1em 0 0.4em 0;
}
h3 {
margin-bottom:0.4em;
}
p {
margin:0 0 1em 1em;
text-align:justify;
}
ol {
margin:0 0 1.2em 1em;
padding:0;
list-style-type:none;
}
ol li {
margin:0.2em 0;
}
pre, code {
font-size:100%;
font-family:"Courier New", Courier, mono;
background-color: #CCCCCC;
border:1px solid #999;
padding:0.2em 1em;
margin: 0.4em 0;
display:block;
white-space: pre;
overflow: auto;
}
form {
margin:0 0 0 1em;
}
span.key {
color: #006600;
}
#install {
display:none
}
#languages ul {
display:inline;
list-style-type:none;
margin:0;
padding:0;
}
#languages li {
display:inline;
margin:0;
padding:0;
vertical-align:bottom;
}

View File

@ -0,0 +1,148 @@
/*
* @file change event plugin for CKEditor
* Copyright (C) 2011 Alfonso Martinez de Lizarrondo
*
* == BEGIN LICENSE ==
*
* Licensed under the terms of any of the following licenses at your
* choice:
*
* - GNU General Public License Version 2 or later (the "GPL")
* http://www.gnu.org/licenses/gpl.html
*
* - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
* http://www.gnu.org/licenses/lgpl.html
*
* - Mozilla Public License Version 1.1 or later (the "MPL")
* http://www.mozilla.org/MPL/MPL-1.1.html
*
* == END LICENSE ==
*
*/
// Keeps track of changes to the content and fires a "change" event
CKEDITOR.plugins.add( 'onchange',
{
init : function( editor )
{
// // Test:
// editor.on( 'change', function(e) { console.log( e ) });
var timer,
theMutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
observer;
// http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#mutation-observers
// http://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/
// Avoid firing the event too often
function somethingChanged()
{
// don't fire events if the editor is readOnly as they are false detections
if (editor.readOnly)
return;
if (timer)
return;
timer = setTimeout( function() {
timer = 0;
editor.fire( 'change' );
}, editor.config.minimumChangeMilliseconds || 100);
}
// Kill the timer on editor destroy
editor.on( 'destroy', function() { if ( timer ) clearTimeout( timer ); timer = null; });
// in theory this block should be enabled only for browsers that don't support MutationObservers,
// but it doesn't seem to fire correctly in all the situations. Maybe in the future...
{
// Set several listeners to watch for changes to the content
editor.on( 'saveSnapshot', function( evt )
{
if ( !evt.data || !evt.data.contentOnly )
somethingChanged();
});
var undoCmd = editor.getCommand('undo');
undoCmd && undoCmd.on( 'afterUndo', somethingChanged);
var redoCmd = editor.getCommand('redo');
redoCmd && redoCmd.on( 'afterRedo', somethingChanged);
editor.on( 'afterCommandExec', function( event )
{
if ( event.data.name == 'source' )
return;
if ( event.data.command.canUndo !== false )
somethingChanged();
} );
}
if ( theMutationObserver )
{
observer = new theMutationObserver( function( mutations ) {
somethingChanged();
} );
// To check that we are using a cool browser.
if (window.console && window.console.log)
console.log("Detecting changes using MutationObservers");
}
// Changes in WYSIWYG mode
editor.on( 'contentDom', function()
{
if ( observer )
{
// A notification is fired right now, but we don't want it so soon
setTimeout( function() {
observer.observe( editor.document.getBody().$, {
attributes: true,
childList: true,
characterData: true
});
}, 100);
}
editor.document.on( 'keydown', function( event )
{
// Do not capture CTRL hotkeys.
if ( event.data.$.ctrlKey ||event.data.$.metaKey )
return;
var keyCode = event.data.$.keyCode;
// Filter movement keys and related
if (keyCode==8 || keyCode == 13 || keyCode == 32 || ( keyCode >= 46 && keyCode <= 90) || ( keyCode >= 96 && keyCode <= 111) || ( keyCode >= 186 && keyCode <= 222) )
somethingChanged();
});
// Firefox OK
editor.document.on( 'drop', somethingChanged);
// IE OK
editor.document.getBody().on( 'drop', somethingChanged);
});
// Detect changes in source mode
editor.on( 'mode', function( e )
{
if ( editor.mode != 'source' )
return;
editor.textarea.on( 'keydown', function( event )
{
// Do not capture CTRL hotkeys.
if ( !event.data.$.ctrlKey && !event.data.$.metaKey )
somethingChanged();
});
editor.textarea.on( 'drop', somethingChanged);
editor.textarea.on( 'input', somethingChanged);
if (CKEDITOR.env.ie)
{
editor.textarea.on( 'cut', somethingChanged);
editor.textarea.on( 'paste', somethingChanged);
}
});
} //Init
} );

View File

@ -193,6 +193,23 @@
self.bindSaveDataOnChange( field, prefix ); self.bindSaveDataOnChange( field, prefix );
} }
} ); } );
if ( typeof window.CKEDITOR !== 'undefined' ) {
CKEDITOR.on( 'instanceReady', function(ev) {
if ( $.inArray( ev.editor.element.$, fieldsToProtect ) ) {
ev.editor.on( 'change', function(eev) {
eev.editor.updateElement();
var element = eev.editor.element.$;
if ( typeof element.oninput === 'undefined' ) {
element.onpropertychange();
} else {
element.oninput();
}
} );
}
} );
}
} ); } );
}, },
@ -205,6 +222,7 @@
*/ */
saveAllData: function() { saveAllData: function() {
var self = this; var self = this;
self.targets.each( function() { self.targets.each( function() {
var targetFormId = $( this ).attr( "id" ); var targetFormId = $( this ).attr( "id" );
var fieldsToProtect = $( this ).find( ":input" ).not( ":submit" ).not( ":reset" ).not( ":button" ).not( ":file" ); var fieldsToProtect = $( this ).find( ":input" ).not( ":submit" ).not( ":reset" ).not( ":button" ).not( ":file" );
@ -318,6 +336,7 @@
*/ */
bindSaveDataImmediately: function( field, prefix ) { bindSaveDataImmediately: function( field, prefix ) {
var self = this; var self = this;
if ( typeof $.browser.msie === 'undefined' ) { if ( typeof $.browser.msie === 'undefined' ) {
field.get(0).oninput = function() { field.get(0).oninput = function() {
self.saveToBrowserStorage( prefix, field.val() ); self.saveToBrowserStorage( prefix, field.val() );
@ -456,4 +475,4 @@
} }
}; };
} )(); } )();
} )( jQuery ); } )( jQuery );

View File

@ -0,0 +1,7 @@
//= require ckeditor/plugins/onchange/plugin.js
//= require_self
(function() {
CKEDITOR.config.extraPlugins += ",onchange";
})(this);