Creating Fake Elements for FCKEditor 2.x

I've been working on a project last week that involves creating a custom plugin for FCKEditor. I needed a way to inject custom code into the HTML output (custom Magento templating stuff, actually). I wanted the code to be hidden from the user but easily managed by the user through a dialog box. This way, the user can easily change this without worrying about the underlying code (or FCKEditor corrupting it).

I spent hours searching the web and API documentaion but found nothing. I ended up checking out the FCKEditor source and digging through that. I found it was much easier to accomplish this - everything I needed was built in, just poorly documented. This article will outline how I achieved this, along with some sample code you can adapt to fit your own needs. I'll start with a quick overview of how it works and then dive into sample code, showing you where to place which pieces of code:

Quick Review

When FCKEditor parses through the HTML, it calls any hooks you've defined as such:

FCKDocumentProcessor.AppendNew().ProcessDocument = function( document ) {}

Inside this function, you'll parse through the document object, creating a FakeImage which conceals the HTML code from the user and safeguards it from the editor:

var oImg = FCKDocumentProcessor_CreateFakeImage('FCK__MyPlugin', matchingElementObject.cloneNode(true));

Inside your dialog's JS, you want to grab the selected element, verify it's a fake image, and extract the concealed HTML:

var dialog = window.parent ;
var oEditor = dialog.InnerDialogLoaded() ;

var eSelected = oEditor.FCKSelection.GetSelectedElement() ;
var eOriginalElement;

window.onload = function()
{
	if ( !eSelected )
		return ;

	if ( eSelected.tagName == 'IMG' && $(eSelected).attr('_fck_myplugin') )
	{
		eOriginalElement = oEditor.FCK.GetRealElement(eSelected);
}

If the dialog will be creating new objects in the editor, simply insert them as the proper HTML. Your document processor hook will take care of converting them to a 'Fake Element'.

Let look at some sample code now:

Step 1: Define your custom object

Unfortunately I can't show you the sample code I've been using, so let's make something up :) Say we have a custom CMS using FCKEditor which lets you embed advertisements in your content. This ad plugin expects to see code that looks like this:

[[ad type="block" id="7"]]

To make this easily findable/parsable by our plugin, we're going to wrap this in a div (for convenience):

[[ad type="block" id="7"]]

Step 2: Create the plugin

You'll need to create a new folder inside of /plugins with a file named fckplugin.js - we'll call our's "myplguin". This is the file FCKEditor loads when the editor is initialized, allowing you to add toolbar items, register listeners on events, etc. Start with something like this:

// Register the related command. 
// RegisterCommand takes the following arguments: CommandName, DialogCommand 
// FCKDialogCommand takes the following arguments: CommandName, 
//    Dialog Title, Path to HTML file, Width, Height
FCKCommands.RegisterCommand( 
    'MyPlugin', 
    new FCKDialogCommand( 
        'MyPlugin', 
        'Clicking this button shows a dialog box',
        FCKPlugins.Items['myplugin'].Path + 'dialog.php',
        300,
        300) 
);

// Create the toolbar button. 
// FCKToolbarButton takes the following arguments: CommandName, Button Caption 

var oMyPluginButton = new FCKToolbarButton( 'MyPlugin', 'Insert a product block' ) ; 
oMyPluginButton .IconPath = FCKPlugins.Items['myplugin'].Path + 'icon.png' ; 
FCKToolbarItems.RegisterItem( 'MyPlugin', oMyPluginButton ) ;

We'll also hook into the Document Processor - this is called when the editor loads raw HTML into the editor. We will be adding a hook to find all DIVs with class "myplugin" and convert them into "Fake Elements":

// Parse the document for product blocks
FCKDocumentProcessor.AppendNew().ProcessDocument = function( document )
{
	var matching = getElementsByClassName(document, 'div', 'myplugin');
	
	var oMatch;
	var i = matching.length - 1;
	while ( i>= 0 && ( oPB = matching[i--] ) )
	{
		// You can optionally verify that this div.myplugin element is valid (maybe with a regex?)
		// We'll just set this to 'true' to keep our code clean.
		if(true)
		{
			// Create the new FakeImage (looks like an image in the editor, but contains our div)
			var oImg = FCKDocumentProcessor_CreateFakeImage('FCK__MyPlugin', oMatch.cloneNode(true));
			oImg.setAttribute('_fck_myplugin', true, 0);
			
			// Place the FakeImage right where the div used to be
			oMatch.parentNode.insertBefore(oImg, oMatch);
			oMatch.parentNode.removeChild(oMatch);
		}
	}
}

Step 3: Create your dialog box

You'll want to create a file called dialog.php (or .html or whatever you prefer) which will load inside the 300x300 popup we defined earlier. You'll want to customize this with any fields your plugin needs.

Step 4: Create JS for working with the dialog and Fake Element

Name this whatever you'd like. This will be included into your dialog.php. My dialog also uses jQuery, so you'll see some of that sprinkled in. Here's some sample code to get you started:

var dialog = window.parent ;
var oEditor = dialog.InnerDialogLoaded() ;

window.onload = function ()
{
	dialog.SetOkButton(true);

	LoadSelected() ;
	
	if(eProductBlock && $(eProductBlock).attr('rel'))
	{
		do_search('pid=' + $(eProductBlock).attr('rel'), function(){
			$('#results li:first').trigger('click');
		});
	}
	
}

var eSelected = oEditor.FCKSelection.GetSelectedElement() ;
var eAdObject;

function LoadSelected()
{
	// Is there anything selected?
	if ( !eSelected )
		return ;

	// If the selected item is our Fake Image, have FCK return the "RealElement"
	if ( eSelected.tagName == 'IMG' && $(eSelected).attr('_fck_myplugin') )
		eAdObject = oEditor.FCK.GetRealElement(eSelected);
	else
		eSelected = eAdObject = null ;
}

function Ok()
{
	// Called when user clicks "OK" button.  Will insert that custom CMS code into the editor.

	var ad_type = "block";
	var ad_id = 7;

	oEditor.FCK.InsertHtml('[[ad type="'+ad_type+'" id="'+ad_id+'"]]');

	return true;
}

Step 5: Modify as needed

You'll want to take this sample code and change it to meet your needs. It should give you the right foundation to start building your own plugin using these Fake Element/Image features of FCKEditor. I hope this helps others like myself who couldn't find a single resource on this subject!

Oh, and a brief disclaimer: I simply pulled relevant code bits from my plugin and modified some of the variable names, strings, etc. Although my plugin is working beautifully, I haven't tested the code above! I hope that it works as posted - if not, at least you'll have all the core functions needed to work with this feature.

About the Author

Colin O'Dell first started programming on an old Apple II-e at the age of 7. Within four years, he mastered several BASIC variants and sold his first commercial application. After tinkering with different languages and technologies for a few...

 
blog rss banner
 

Categories

View All

Testimonials

"Unleashed Technologies has done a phenomenal job with two different major and ongoing projects.

Director of Marketing, Recruiting Firm

Mike and his team have taken over a web site that was limping along, with an extensive list of problems and a weak design.  They jumped into the task and are rapidly disposing of issues that had...

Laura Perry, Marketing Director
Whiteford | Taylor | Preston