Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    tinyMCE Integration

    Hi,

    I've had some success integrating tinyMCE as a form control using CanvasItem on Firefox 3 and 3.5, but not on Chrome or Safari 4. All browsers on the mac. Chrome is still in development on mac, but it failed identically to Safari, so I thought it was worth noting. I used this thread as a starting point. I had to implement two hacks for the following problems -

    1. When editing a record, CanvasItem.setValue() is called before the editor is ready, and the value is lost. This is because editor creation is asynchronous, and SC thinks the item is ready before it is.
    2. When calling saveData() on the form, the value of the CanvasItem appears to be accessed directly by SC, rather than through getValue() - so the content in the form control is not retrieved correctly.

    Here is the code

    Code:
    // HACK 1 - variable to hold setValue() calls made before the editor is ready
    var tinyMCEInitValue = null;
    
    function handleMCEExecCompletion(inst) {
    	isc.Log.logInfo("mceAddControl completed - editor_id:" + inst.editorId);
    	if(tinyMCEInitValue) {
    		inst.setContent(tinyMCEInitValue);
    		tinyMCEInitValue = null;
    	}
    }
    
    tinyMCE.init({
    	mode : "none",
    	theme : "simple",
            plugins: "safari",
    	init_instance_callback : "handleMCEExecCompletion",
    	setup : function(ed) {
                   isc.Log.logInfo("Setting up editor:" + ed.id);
            }
    });
    	
    // We'll use this in CanvasItem
    isc.defineClass("MyTextEditor","Canvas").addProperties({
        overflow: "visible",
        canDragResize: true,
        redrawOnResize: true,
        //zIndex:0,
        getInnerHTML : function () {
            return "<textarea style='width:100%;height:100%' id=" + this.getID() + "_ta></textarea>";
        },
    	  
        redrawOnResize:false,
    
        draw : function() {
            this.Super("draw", arguments);
    	this.loadEditor();
            return this;
        },
    	    
        loadEditor: function() {
          if (this.mceLoaded == null) {
            isc.Log.logInfo("Creating editor:" + this.getID() + "_ta");
            tinyMCE.execCommand("mceAddControl", true, this.getID() + "_ta");
            isc.Log.logInfo("Editor Setup initiated - ed:" + tinyMCE.get(this.getID() + "_ta"));
            this.mceLoaded = true;
          }
        }
    });
    
    isc.defineClass("MyTextEditorItem", "CanvasItem").addProperties({
    	canvasConstructor: "MyTextEditor",
    		
    	getValue : function() {
    	    var superValue = this.Super("getValue", arguments);
    	    var editorValue = null;
    	    isc.Log.logInfo("Super Value: " + superValue);
    	    if(tinyMCE.get(this.canvas.getID() + "_ta")) {
    		editorValue = tinyMCE.get(this.canvas.getID() + "_ta").getContent();
    		isc.Log.logInfo("Editor value:" + editorValue);
    		// HACK 2 - we need to work out a better way of keeping the underlying item's value in sync
    		//        - this could be a SC bug - why not calling getValue() - using direct access instead?
    		if(superValue != editorValue) this.Super("setValue", editorValue);
    	        return editorValue;
    	    } else return superValue;
            },
    
    	setValue : function(value) {
    	    isc.Log.logInfo("Setting value:" + value);
    	    // HACK 1 - setValue is called before the editor is ready, so we need to keep hold of the value until it is loaded.
    	    if(tinyMCE.get(this.canvas.getID() + "_ta")) tinyMCE.get(this.canvas.getID() + "_ta").setContent(value);
    	    else tinyMCEInitValue = value;
    	    isc.Log.logInfo("MyTextEditorItem, setValue - calling super...");
                return this.Super("setValue", arguments);
            }
    });
    I suspect there are probably better ways to resolve both hacks, especially #2 - I feel like I should not be needing to call super() for setValue() and getValue().

    Safari and Chrome are close - all the editor controls render and appear to operate as expected, but the actual text area is broken. Here is a screenshot of the failure (Safari 4.0 and Chrome latest dev), and the success (Firefox 3 & 3.5).

    Note that tinyMCE is listed as compatible with Safari 4, and all the examples work with both Safari 4 and Chrome on the mac.

    It would be great if isomorphic were able to comment on the hacks and the issue in safari and chrome - thanks,

    Colin
    Last edited by hawkettc; 4 Sep 2009, 21:19.

    #2
    As an additional issue, any controls that utilise drop-downs do not appear to function - i.e. the dropdown does not show. At the moment I am guessing that it is a z-index issue, but I have been unable to find a solution.

    Comment


      #3
      There are a few threads, such as this one, in which complete working code has been posted for fckEditor. You might consider switching your choice of editor to embed, if not, the approaches used for fckEditor may also apply to TinyMCE.

      Comment


        #4
        Hi,

        As linked in the first paragraph of my original post, that thread is the one I based my tinyMCE integration on. However, I'll probably go the way you suggest if nothing obvious jumps out at you related to those issues. Worth noting that FCKEditor has been superseded by CKEditor, and the instructions in that post are not valid for the newer implementation. CKEditor uses a model that is much closer to that of the tinyMCE implementation. Thanks,

        Colin
        Last edited by hawkettc; 6 Sep 2009, 21:49.

        Comment


          #5
          CKEditor Integration

          Ok - so CKEditor is so similar to tinyMCE in its config/setup that it has taken the time since the last post to workout how to integrate it and then write it up. Summary - the same hacks and incompatibilities - works in Firefox, doesn't work in Safari or Chrome. Safari 4 and Chrome exhibit identical failure - although it is not the same as tinyMCE. The editor renders & works, however when the form is hidden, the editor contained in the CanvasItem remains rendered - i.e. it is not hidden.

          Here is the code

          Code:
          // HACK 1 - variable to hold setValue() calls made before the editor is ready
          var ckInitValue = null;
          
          CKEDITOR.on( 'instanceReady', function( ev ) {
          	isc.Log.logInfo("Editor Ready!: " + ev.editor.id);
          	if(ckInitValue) {
          		ev.editor.setData(ckInitValue);
          		ckInitValue = null;
          	}
          });
            
          isc.defineClass("MyTextEditor","Canvas").addProperties({
          	overflow: "visible",
          	canDragResize: true,
          	redrawOnResize:true,
          	zIndex:0,
          	getInnerHTML : function () {
          	    return "<textarea STYLE='width:100%;height:100%' ID=" + this.getID() + "_ta></textarea>";
          	},
          	redrawOnResize:false,
          
          	draw : function() {
          	    this.Super("draw",arguments);     
          	    this.loadEditor();
          	    return this;
          	},
          	    
          	loadEditor: function() {
          	    if (this.ckLoaded == null) {
          	      	isc.Log.logInfo("Creating editor:" + this.getID() + "_ta");
                          CKEDITOR.replace(this.getID() + "_ta", {
                    	     toolbar : [
                                  ['Styles', 'Format'],
                                  ['Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link']
                               ]       
                          });
                          isc.Log.logInfo("Editor Setup initiated...");
                          this.ckLoaded = true;
          	    }
          	}
          });
          
          isc.defineClass("MyTextEditorItem", "CanvasItem").addProperties({
              canvasConstructor: "MyTextEditor",
              
              getValue : function() {
                var superValue = this.Super("getValue", arguments);
                var editorValue = null;
                isc.Log.logInfo("Super Value: " + superValue);
                if(CKEDITOR.instances[this.canvas.getID() + "_ta"].getData()) {
                  editorValue = CKEDITOR.instances[this.canvas.getID() + "_ta"].getData();
                  isc.Log.logInfo("Editor value:" + editorValue);
                  // HACK 2 - we need to work out a better way of keeping the underlying item's value in sync
          	//        - this could be a SC bug - why not calling getValue() - using direct access instead?
                  if(superValue != editorValue) this.Super("setValue", editorValue);
                  return editorValue;
                } else return superValue;
              },
          
              setValue : function(value) {
                isc.Log.logInfo("Setting value:" + value);
                // HACK 1 - setValue is called before the editor is ready, so we need to keep hold of the value until it is loaded.
                if(CKEDITOR.instances[this.canvas.getID() + "_ta"]) CKEDITOR.instances[this.canvas.getID() + "_ta"].setData(value);
                else ckInitValue = value;
                return this.Super("setValue", arguments);
              }
            });
          Also, to solve the z-index problem in CKEditor (popup menus not appearing) - edit config.js

          Code:
          config.baseFloatZIndex = 1000000;
          In tinyMCE, the z-index problem is solved by hacking the tiny_mce.js file - replace all instances of 'z-index:200000' with 'z-index:1000000'.

          So at this point, in both tinyMCE and CKEditor, we have the following (in order of importance)

          1. Rendering issues in Safari and Chrome when included as an SC FormItem.
          2. Hack 2 to manage what appears to be an issue with SC calling MyTextEditorItem.value directly instead of MyTextEditorItem.getValue()
          3. Hack 1 to manage the asynch nature of the editor initialisation

          I'm thinking that it might be possible to solve #1 for CKEditor by manually hiding the CKEditor window when the SC form is hidden.

          #2 really strikes me as a possible SC bug.

          Any input you might have for these issues would be welcome. Thanks,

          Colin
          Last edited by hawkettc; 6 Sep 2009, 22:59.

          Comment


            #6
            Hello,

            I am having the same issue that you have on hack 2 but with another custom field ( innovaEditor ).

            Currently, i have the following code:

            Code:
            isc.defineClass("DynTextEdit", "CanvasItem").addProperties({
            	canvasConstructor: "InnovaCanvas",
            	getValue: function() {
              		if ( document.getElementById(this.getID() + "_ta") != null) {
              			editorValue = oEdit1.getHTML();
              			this.Super("setValue", value);
              		}
              		else
              		{
              			editorValue = null;
              		}
              		
              		return editorValue;
                },
                setValue: function( value ) {
                	if ( document.getElementById(this.getID() + "_ta") != null) {
                		oEdit1.loadHTML( value );
                		return this.Super("setValue", arguments);
                	}
                }
            });
            But when integrating it with a Dynamicform + Datasource, i don't get any value from this field.

            Do you mind taking a look and telling me what i should do? I tried to reproduce your code but unfortunately it is not working for me.

            Comment


              #7
              Hi,

              Can you also post your Canvas implementation (InnovaCanvas)? Thanks,

              Colin

              Comment


                #8
                Hello,

                Thanks for your reply. Here it is:

                Code:
                isc.defineClass("InnovaCanvas","Canvas").addProperties({
                	  width: 600,
                	  height: 400,
                	  getInnerHTML : function () {
                	  		return "<textarea STYLE='width:100%; height: "+ this.getHeight() +"px;' ID=" + this.getID() + "_ta></textarea><div id=" + this.getID() + "_div></div>";
                	  },
                	  loadEditor: function() {
                	  		if ( document.getElementById(this.getID() + "_ta") != null) {
                			  	oEdit1.features=["FullScreen","Preview","Print", "Search","SpellCheck",
                				                 "Table","Guidelines","Absolute",
                				                 "Form","Characters","ClearAll","XHTMLSource","BRK",
                				                 "Cut","Copy","Paste","PasteWord","PasteText",
                				                 "Undo","Redo","Hyperlink","Bookmark","Image",
                				                 "JustifyLeft","JustifyCenter","JustifyRight","JustifyFull",
                				                 "Numbering","Bullets","Indent","Outdent", "LTR","RTL",
                				                 "Line","RemoveFormat","BRK",
                				                 "StyleAndFormatting","TextFormatting","ListFormatting",
                				                 "BoxFormatting","ParagraphFormatting","CssText","Styles",
                				                 "CustomTag","Paragraph","FontName","FontSize",
                				                 "Bold","Italic","Underline","Strikethrough", "Superscript","Subscript",
                				                 "ForeColor","BackColor"];
                				oEdit1.useTab = false;
                				oEdit1.width = this.getWidth();
                				oEdit1.height = this.getHeight();
                			    oEdit1.REPLACE(this.getID() + "_ta",this.getID() + "_div");
                	    	}
                	  },
                	  draw: function() {
                		  this.Super("draw",arguments);     
                		  this.loadEditor();
                		  return this;
                	  }
                });

                Comment


                  #9
                  There are a couple of things to consider - I'm not familiar with innovaEditor though -

                  1. Your loadEditor() function is testing for the presence of the textarea - document.getElementById(this.getID() + "_ta") - it looks to me however like the innovaEditor is replacing this textarea with a div - oEdit1.REPLACE(this.getID() + "_ta",this.getID() + "_div") - so testing for the presence of the textarea is not the same as testing for the presence of the div created by innovaEditor. My initial thought would be that the loadEditor function would be executed in full every time the component is drawn. That's the reason for the ckLoaded variable in my code (and in the original fckEditor thread)

                  2. In your getValue() function you are again testing for the presence of the textarea, not the editor, so this doesn't look like a reliable way to determine if the editor exists.

                  3. There is no situation in which your getValue() will return the super value. You should do this if the editor has not been created.

                  4. In your setValue(), you fail to set the super value if the textarea doesn't exist. Also, as noted in the previous points, you should be checking for the editor, not the textarea.

                  So - my main point would be that you haven't used the pattern for any of loadEditor(), getValue() or setValue().

                  All that said, it really would be worthwhile hearing from Isomorphic as to why it is not sufficient to override getValue() - why must the super value be maintained? It really looks like some Isomorphic code is accessing the value directly from the CanvasItem, rather than calling getValue(). Thanks,

                  Colin

                  Comment


                    #10
                    On getValue() - it's a notification pattern (the item notifies the form of change) rather than a polling pattern (eg the form always calls getValue()). Right now you call setValue() to trigger notification of the form. This does produce some awkwardness with needing to override setValue() to receive notification of changes from the form but also needing to call it to update the form, so a separate API will be introduced.

                    On these editors not working in Safari / Chrome, unsure what that could be. Do they ever work in Safari / Chrome?

                    Comment


                      #11
                      Ok, that makes a little more sense - I guess in some ways then, you might say the editor is violating the pattern by changing the content without calling setValue().

                      In terms of chrome/safari not working - tinyMCE supports both (as noted), and ckEditor supports Safari. The failure in tinyMCE seems little more serious than ckEditor. I'm now working with ckEditor, so I haven't got any more info on that. In ckEditor the problem was that when displaying the editor in a modal window, it would not hide when the window was hidden - except in FF on the mac. It turned out to also fail in FF on windows and linux, which is odd. In any case it looked to be some problem with the visibility attribute, and my current hack is to just force visible/hidden manually when the window is shown and hidden.

                      Code:
                      var ckEditorDiv = document.getElementById("cke_" + myForm.getField("content").canvas.getID() + "_ta")
                      if(ckEditorDiv) ckEditorDiv.style.visibility = "visible";
                      This is probably not the most efficient solution at a number of levels, however.

                      Comment


                        #12
                        Generally speaking you / ckEditor should be using the value "inherit" for visibility as "visible" is a CSS oddity that means "be visible even if my parent is not", which would explain your results.

                        A number of people would probably appreciate seeing your final code, complete with Safari workaround(s).

                        Comment


                          #13
                          Ok - that makes some sense, except that inherit is supposed to be the default value, so I shouldn't need to set anything - firebug wasn't showing the visibility style as set on the ckeditor.

                          I'm pretty much leaving what I have for the time being - the visibility hack is my fix for the chrome/safari problem, at least in the short term. Cheers,

                          Colin

                          Comment


                            #14
                            I am new to smartclient and ckeditor. We are trying to upgrade from fckeditor to ckeditor v 3.6.1. I am confused about the changes I need to make in the smartclient js file. When I make the changes and then call up the page, where the editor chould be I get a page not found error. Our old code looked like
                            Code:
                            function createEditor() {
                                fck = new FCKeditor("myFCKeditor");
                                fck.BasePath = "/ACES/CKEditor/";
                                fck.ToolbarSet = "BOE";
                                fck.Height = "100%";
                                fck.Width = "100%";
                            }
                            ....
                            createEditor();
                            isc.Canvas.create( {
                                 ID : "Editor",
                                 autoDraw : false,
                                 contents : fck.CreateHtml(),
                                 height : "100%",
                                 width : "100%"
                            } );
                            All the configuration is in the config.js file. I have update this file with the changes for 3.6.1, but am unsure how to change the smartclient js file.
                            From what I read on the ckeditor documentation, all I should need, since all configuration is in the config file, is
                            Code:
                            <%@ taglib uri="http://ckeditor.com" prefix="ckeditor" %>
                            
                            <ckeditor:editor  basePath="/ACES/CKEditor_3.6.1/" editor="editor1" >
                            I know that thisis asking a lot, but the only person who knew the fckeditor code is gone and I have been thrown this. Any help is greatly appreciated.

                            Comment

                            Working...
                            X