Forum closed. New forum available at http://community.wymeditor.org/

Custom pluggable containers

Discuss features, code, contributions, ideas, suggestions, ...
For bugs, patches and feature requests, please post on the Trac:
http://trac.wymeditor.org/

Custom pluggable containers

Postby strykker on Thu Jan 29, 2009 6:54 am

For my app, I have needed to add custom containers (block tags [more or less]) which use the same container 'framework' but insert custom container code. To do this, I have needed to modify my WYMeditor.editor.prototype.container function. This seems like it could be a common requirement and there should be a pluggable way of integrating custom container logic.

Barring any response, I will try to come up with something soon and post here, but I wondered if anyone has already faced/tackled this problem or perhaps there is something already in the works for the 0.6 release.

- Eric
strykker
 
Posts: 6
Joined: Wed Jan 21, 2009 6:49 am

Re: Custom pluggable containers

Postby strykker on Thu Jan 29, 2009 7:31 pm

I've added basic 'pluggability' to the containers. I have tested it with several plugged containers that I am using but I'm sure it could use some additional testing (for example I'm not sure about the "TH" container logic as I'm currently not using it). Hopefully this can provide the basis for pluggable containers in the next version (if it's not being done already)...

The pluggable containers work simply by adding a customizable 'injector' callback function to each containerItem object. For most normal containers (P,H1,H2 etc), this function doesn;t need to be implemented, and the container() function simply switches to the new container (with switchTo()) - theres no custom injection. For containers with custom injection functionality (such as 'BLOCKQOUTE, and 'TH'), the 'injector' is implemented to perform the custom injection logic (such as they already were in the container() method). Basically, this is simply modularizing what was already being done in the container() method and allowing you to add your own. This means that the 'containersItems' object now looks like:

Code: Select all
containersItems: [
        {'name': 'P', 'title': 'Paragraph', 'css': 'wym_containers_p'},
        {'name': 'H1', 'title': 'Heading_1', 'css': 'wym_containers_h1'},
        {'name': 'H2', 'title': 'Heading_2', 'css': 'wym_containers_h2'},
        {'name': 'H3', 'title': 'Heading_3', 'css': 'wym_containers_h3'},
        {'name': 'H4', 'title': 'Heading_4', 'css': 'wym_containers_h4'},
        {'name': 'H5', 'title': 'Heading_5', 'css': 'wym_containers_h5'},
        {'name': 'H6', 'title': 'Heading_6', 'css': 'wym_containers_h6'},
        {'name': 'PRE', 'title': 'Preformatted', 'css': 'wym_containers_pre'},
        {'name': 'BLOCKQUOTE', 'title': 'Blockquote','css': 'wym_containers_blockquote', 'injector' : function(wym, container) {
   
         var blockquote = wym.findUp(wym.container(), WYMeditor.BLOCKQUOTE);
                  
         if(blockquote == null) {
       
            newNode = wym._doc.createElement('BLOCKQUOTE');
            container.parentNode.insertBefore(newNode,container);
            newNode.appendChild(container);
            wym.setFocusToNode(newNode.firstChild);
         
         } else {
       
            var nodes = blockquote.childNodes;
            var lgt = nodes.length;
            var firstNode = null;
            
            if(lgt > 0) firstNode = nodes.item(0);
            for(var x=0; x<lgt; x++) {
              blockquote.parentNode.insertBefore(nodes.item(0),blockquote);
            }
            blockquote.parentNode.removeChild(blockquote);
            if(firstNode) wym.setFocusToNode(firstNode);
         }
         
         return true;
      }},
        {'name': 'TH', 'title': 'Table_Header', 'css': 'wym_containers_th', 'injector': function(wym, container) {
         //find the TD or TH container
         container = wym.container();
         
         switch(container.tagName.toLowerCase()) {
           case WYMeditor.TD: case WYMeditor.TH:
            break;
           default:
            var aTypes = new Array(WYMeditor.TD,WYMeditor.TH);
            container = wym.findUp(wym.container(), aTypes);
            break;
         }
          
         //if it exists, switch
         if(container!=null) {
            sType = (container.tagName.toLowerCase() == WYMeditor.TD)? WYMeditor.TH: WYMeditor.TD;
            wym.switchTo(container,'TH');
            return true;
         }
         
         return false;
      }}
    ],


Implement the pluggable containers as follows:

1. Add the following editor protoype function
Code: Select all
/*
* injector is a callback function that should be implemented to perform container-specific injection logic
* injector = function(wym,container) is passed reference to the wym editor as arg 1 and reference to container as arg 2
* returns true if successful (if editor should be updated) or false if not.
*/
WYMeditor.editor.prototype.registerContainer = function(name, injector) {

   if(!this._containers) { this._containers = []; }
   
   if(name && jQuery.isFunction(injector)) {
      this._containers[name.toLowerCase()] = injector;
   }
}



2. Replace the container() prototype function with the following
Code: Select all
/* @name container
* @description Get/Set the selected container
*/
WYMeditor.editor.prototype.container = function(sType) {
          
   if(sType) {
 
       var container = this.findUp(this.container(), WYMeditor.MAIN_CONTAINERS);
   
      if(container) {
      
         var injector = this._containers[sType.toLowerCase()];
         var isUpdate = true;
         
         if(injector) {
      
            isUpdate = injector(this, container);
         
         } else {
            
            this.switchTo(container,sType);
         }
         
         if(isUpdate) {
            this.update();
            //Note, I use the following code below (eliminating the xhtml() parser for performance and some parser issues since we're in control of the validity of the HTML injected anyway
            //var html = this.html();
            //jQuery(this._element).val(html);
            //jQuery(this._box).find(this._options.htmlValSelector).val(html);
         }
      }
   }  else return(this.selected());
}
(Note I'm using WYMeditor.MAIN_CONTAINERS as the list of valid containers.
I suppose this is fine for now but it should be made into an option as custom container code may need to specify custom container types to allow for cutomized container nesting - I do)


3. Add the following line to the init function where it constructs the containers list
Code: Select all
[b]add new line:[/b]
        this.registerContainer(oContainer.name, oContainer.injector);
[b]after existing lines:[/b]
       sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_CLASS, oContainer.css);
       sContainers += sContainer;


4. Update the containersItems object as I described earlier to:
Code: Select all
containersItems: [
        {'name': 'P', 'title': 'Paragraph', 'css': 'wym_containers_p'},
        {'name': 'H1', 'title': 'Heading_1', 'css': 'wym_containers_h1'},
        {'name': 'H2', 'title': 'Heading_2', 'css': 'wym_containers_h2'},
        {'name': 'H3', 'title': 'Heading_3', 'css': 'wym_containers_h3'},
        {'name': 'H4', 'title': 'Heading_4', 'css': 'wym_containers_h4'},
        {'name': 'H5', 'title': 'Heading_5', 'css': 'wym_containers_h5'},
        {'name': 'H6', 'title': 'Heading_6', 'css': 'wym_containers_h6'},
        {'name': 'PRE', 'title': 'Preformatted', 'css': 'wym_containers_pre'},
        {'name': 'BLOCKQUOTE', 'title': 'Blockquote','css': 'wym_containers_blockquote', 'injector' : function(wym, container) { //blockquotes must contain a block level element
   
         var blockquote = wym.findUp(wym.container(), WYMeditor.BLOCKQUOTE);
                  
         if(blockquote == null) {
       
            newNode = wym._doc.createElement('BLOCKQUOTE');
            container.parentNode.insertBefore(newNode,container);
            newNode.appendChild(container);
            wym.setFocusToNode(newNode.firstChild);
         
         } else {
       
            var nodes = blockquote.childNodes;
            var lgt = nodes.length;
            var firstNode = null;
            
            if(lgt > 0) firstNode = nodes.item(0);
            for(var x=0; x<lgt; x++) {
              blockquote.parentNode.insertBefore(nodes.item(0),blockquote);
            }
            blockquote.parentNode.removeChild(blockquote);
            if(firstNode) wym.setFocusToNode(firstNode);
         }
         
         return true;
      }},
        {'name': 'TH', 'title': 'Table_Header', 'css': 'wym_containers_th', 'injector': function(wym, container) {
         //find the TD or TH container
         container = wym.container();
         
         switch(container.tagName.toLowerCase()) {
           case WYMeditor.TD: case WYMeditor.TH:
            break;
           default:
            var aTypes = new Array(WYMeditor.TD,WYMeditor.TH);
            container = wym.findUp(wym.container(), aTypes);
            break;
         }
          
         //if it exists, switch
         if(container!=null) {
            sType = (container.tagName.toLowerCase() == WYMeditor.TD)? WYMeditor.TH: WYMeditor.TD;
            wym.switchTo(container,'TH');
            return true;
         }
         
         return false;
      }}
    ],
(note: 'TH' injector function not tested)


That's it! (hope i haven't forgotten something)
Now, we can add any additional containers with custom injector logic in the preInit () function. For example I could have:

Code: Select all
   preInit: function(wym) {
         
         wym._options.containersItems = wym._options.containersItems.concat(
         [
               {'name': 'CUSTOM_CONTAINER', 'title': 'Custom_Container', 'css': 'wym_containers_cutom', 'injector' : function(wym, container) {   
               
               var jContainer = jQuery(container);
               
               var jNode = jQuery("<div class='customContainer'><a id='interactive'>My container link</a></div>");
                        
               var html = jContainer.html();
               jContainer.replaceWith(jNode);
               jNode.find("div.interactive").append(html);   
                                 
               wym.setFocusToNode(jNode [0]);
              }},
         ]);             
      }


As I said, not very well tested. But a start and all I have time for at the moment...
- Eric
strykker
 
Posts: 6
Joined: Wed Jan 21, 2009 6:49 am


Return to Developers

Who is online

Users browsing this forum: No registered users and 5 guests