1

Topic: editable regions(a better tinymce google pages clone)

Hi,
Someone requested a google pages clone just to type in. I made it but it was very limited and people complained when copying and pasting in different browsers and the undo feature being broken in ie><.
So i found tiny mce which was fantastic. the non editable regions is a bit of a pain. If you delete something between 2 non editable regions u can no longer insert content.
I quickly modified it to have editable regions. I turned of designmode and contentEditable and then turn on contentEditable for a class(i am well aware that before FF3 it is not supported. 100% of users apparently use IE and firefox users are smart enough to upgrade software).

It works quite well. Except the paste plain text and from word(most important) will cut through the document. closeing all tags, Pasting in the body,then reopen all the tags below it which destroys the idea of it.
Also the styles in the page sometimes dissapear.

anyhelp is appreciated. Here is the code

/**
 * $Id: editor_plugin_src.js 743 2008-03-23 17:47:33Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 * Modified by Andrew Green in 2008
 */

(function() {
    var Event = tinymce.dom.Event;

    tinymce.create('tinymce.plugins.EditableRegionsPlugin', {
        init : function(ed, url) {
            var t = this, editClass, nonEditClass;

            t.editor = ed;
            editClass = ed.getParam("editableregions_editable_class", "mceEditable");
            nonEditClass = ed.getParam("editableregions_noneditable_class", "mceEditableRegions");
            
            ed.onInit.addToTop(function(ed, cm, n){
                ed.serializer.addRules("style,body,title,head,html");
            });
            
            ed.onSetContent.addToTop(function(ed, cm, n){
                if (ed.getBody().contentEditable) {
                    ed.getBody().contentEditable = false;
                }else{
                    ed.getDoc().designMode = 'Off';
                }
                
                var mceEdit = t._getElementsByClassName('*',ed.getParam("editableregions_editable_class", "mceEditable"),ed.getWin());

                for(var i=0;i<mceEdit.length;i++){
                    mceEdit[i].contentEditable=true;

                }

                
            });
            ed.onNodeChange.addToTop(function(ed, cm, n) {
                var sc, ec;

                // Block if start or end is inside a non editable element
                sc = ed.dom.getParent(ed.selection.getStart(), function(n) {
                    return ed.dom.hasClass(n, editClass);
                });

                ec = ed.dom.getParent(ed.selection.getEnd(), function(n) {
                    return ed.dom.hasClass(n, editClass);
                });

                // Block or unblock
                if (!sc || !ec) {
                    t._setDisabled(1);
                    return false;
                } else
                    t._setDisabled(0);
            });
        },

        getInfo : function() {
            return {
                longname : 'editable regions elements',
                author : 'Moxiecode Systems AB(modified by Andrew Green)',
                authorurl : 'http://tinymce.moxiecode.com',
                infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/editableregions',
                version : tinymce.majorVersion + "." + tinymce.minorVersion
            };
        },

        _block : function(ed, e) {
            var k = e.keyCode;

            // Don't block arrow keys, pg up/down, and F1-F12
            if ((k > 32 && k < 41) || (k > 111 && k < 124))
                return;

            return Event.cancel(e);
        },

        _setDisabled : function(s) {
            var t = this, ed = t.editor;

            tinymce.each(ed.controlManager.controls, function(c) {
                c.setDisabled(s);
            });

            if (s !== t.disabled) {
                if (s) {
                    ed.onKeyDown.addToTop(t._block);
                    ed.onKeyPress.addToTop(t._block);
                    ed.onKeyUp.addToTop(t._block);
                    ed.onPaste.addToTop(t._block);
                } else {
                    ed.onKeyDown.remove(t._block);
                    ed.onKeyPress.remove(t._block);
                    ed.onKeyUp.remove(t._block);
                    ed.onPaste.remove(t._block);
                }

                t.disabled = s;
            }
        },
        _getElementsByClassName : function(strTagName, strClassName,_Win)
        {
            if(!_Win)
                _Win=window;
            _Doc=_Win.document;
            var arrElements = (strTagName == "*" && _Doc.all)? _Doc.all : _Doc.getElementsByTagName(strTagName);
            var arrReturnElements = new Array();
            strClassName = strClassName.replace(/\-/g, "\\-");
            var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
            var oElement;
            for(var i=0; i<arrElements.length; i++){
                oElement = arrElements[i];
                if(oRegExp.test(oElement.className)){
                    arrReturnElements.push(oElement);
                }
            }
            return (arrReturnElements);
            
        }
    });

    // Register plugin
    tinymce.PluginManager.add('editableregions', tinymce.plugins.EditableRegionsPlugin);
})();

2

Re: editable regions(a better tinymce google pages clone)

Is this the right place to post about help developing?
Or is it that no one knows how the cut and paste work?
Or how it problems you get when passing HTML documents(style,body html,ect)?
Or any problems with the parser passing something with contentEditable set?

plus it will not show the html page when i set it on chrome or safari.

Or am i going about this completely wrong? can individual editors re size to the size of there content?

sorry about the rant. I just would like some help.

3

Re: editable regions(a better tinymce google pages clone)

The style sometimes gets clobbered usually when undoing. Does not seem consistently reproducible.
I thought i told it to leave Style alone, but occasionally it clobbers it.

Last edited by greenie (2008-12-11 06:40:24)

4

Re: editable regions(a better tinymce google pages clone)

Think i've got everything worked out.
The reason it was not working with chrome is because safari and chrome(webkit  think) do not decode entities into textarea(that is a big glitch in webkit><), but it works.

i moved the styles out of the content and instead put them in a css sheet and added it properly in the tinymce api. Works perfectly now.

The text and from word paste work fine now. I don't know why but works fine now.

There is still the glitch in fire fox if a div is empty and you press enter it makes a copy of that div which screws this up(who would have thought. works perfect in ie and not FF).

The editable template uses no tables.(and was basically ripped from google pages with very little changes).

It works on ie, safari, chrome, FF(3+).

any comments, or suggestions would be appreciated. As it feels like, posting here was a waste of time ><.

http://img234.imageshack.us/img234/9040/tinypagesiu2.jpg

Here is the code as i have it.

/**
 * $Id: editor_plugin_src.js 743 2008-03-23 17:47:33Z spocke $
 *
 * @author Moxiecode
 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved.
 * Modified by Andrew Green in 2008
 */

(function() {
    var Event = tinymce.dom.Event;

    tinymce.create('tinymce.plugins.EditableRegionsPlugin', {
        init : function(ed, url) {
            var t = this, editClass, nonEditClass;

            t.editor = ed;
            editClass = ed.getParam("editableregions_editable_class", "mceEditable");
            nonEditClass = ed.getParam("editableregions_noneditable_class", "mceEditableRegions");
            
            ed.onInit.addToTop(function(ed, cm, n){
                ed.serializer.addRules("style[*],body[*],title[*],head[*],html[*]");
            });
            
            ed.onSetContent.addToTop(function(ed, cm, n){
                if (ed.getBody().contentEditable) {
                    ed.getBody().contentEditable = false;
                }else if(ed.getDoc().designMode){
                    ed.getDoc().designMode = '';
                }
                
                var mceEdit = t._getElementsByClassName('*',ed.getParam("editableregions_editable_class", "mceEditable"),ed.getWin());

                for(var i=0;i<mceEdit.length;i++){
                    mceEdit[i].contentEditable=true;

                }

                
            });
            ed.onNodeChange.addToTop(function(ed, cm, n) {
                var sc, ec;

                // Block if start or end is inside a non editable element
                sc = ed.dom.getParent(ed.selection.getStart(), function(n) {
                    return ed.dom.hasClass(n, editClass);
                });

                ec = ed.dom.getParent(ed.selection.getEnd(), function(n) {
                    return ed.dom.hasClass(n, editClass);
                });

                // Block or unblock
                if (!sc || !ec) {
                    t._setDisabled(1);
                    return false;
                } else
                    t._setDisabled(0);
            });
        },

        getInfo : function() {
            return {
                longname : 'editable regions elements',
                author : 'Moxiecode Systems AB(modified by Andrew Green)',
                authorurl : 'http://tinymce.moxiecode.com',
                infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/editableregions',
                version : tinymce.majorVersion + "." + tinymce.minorVersion
            };
        },

        _block : function(ed, e) {
            var k = e.keyCode;

            // Don't block arrow keys, pg up/down, and F1-F12
            if ((k > 32 && k < 41) || (k > 111 && k < 124))
                return;

            return Event.cancel(e);
        },

        _setDisabled : function(s) {
            var t = this, ed = t.editor;
            
            
            
            tinymce.each(ed.controlManager.controls, function(c) {
            
                switch(c.settings.cmd){
                    case "mceFullScreen":
                    case "mceHelp":
                    case "mceSave":
                    case "mceCleanup":
                    case "mcePreview":
                    case "Undo":
                    case "Redo":
                    case "Redo":
                    case "":
                    
                        break;
                    default:
                    switch(c.settings.title){
                        case "Exit":
                        case "Template":
                        case "Edit HTML Source":
                            break;
                        default:
                            c.setDisabled(s);
                    }
                }
                
            });

            if (s !== t.disabled) {
                if (s) {
                    ed.onKeyDown.addToTop(t._block);
                    ed.onKeyPress.addToTop(t._block);
                    ed.onKeyUp.addToTop(t._block);
                    ed.onPaste.addToTop(t._block);
                } else {
                    ed.onKeyDown.remove(t._block);
                    ed.onKeyPress.remove(t._block);
                    ed.onKeyUp.remove(t._block);
                    ed.onPaste.remove(t._block);
                }

                t.disabled = s;
            }
        },
        _getElementsByClassName : function(strTagName, strClassName,_Win)
        {
            if(!_Win)
                _Win=window;
            _Doc=_Win.document;
            var arrElements = (strTagName == "*" && _Doc.all)? _Doc.all : _Doc.getElementsByTagName(strTagName);
            var arrReturnElements = new Array();
            strClassName = strClassName.replace(/\-/g, "\\-");
            var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
            var oElement;
            for(var i=0; i<arrElements.length; i++){
                oElement = arrElements[i];
                if(oRegExp.test(oElement.className)){
                    arrReturnElements.push(oElement);
                }
            }
            return (arrReturnElements);
            
        }
    });

    // Register plugin
    tinymce.PluginManager.add('editableregions', tinymce.plugins.EditableRegionsPlugin);
})();

5

Re: editable regions(a better tinymce google pages clone)

Do you have a complete example of this plugin running somewhere?

6

Re: editable regions(a better tinymce google pages clone)

Thank you! This was just what I needed.

I did some modifications to "make sure" (lol ... can you be sure of anything?) that the content is not user editable outside of editable regions. Look at the end of the init functioin:

        init : function(ed, url) {
            var t = this, editClass, nonEditClass;

            t.editor = ed;
            editClass = ed.getParam("editableregions_editable_class", "mceEditable");
            nonEditClass = ed.getParam("editableregions_noneditable_class", "mceEditableRegions");
            
            ed.onInit.addToTop(function(ed, cm, n){
                ed.serializer.addRules("style[*],body[*],title[*],head[*],html[*]");
            });
            
            ed.onSetContent.addToTop(function(ed, cm, n){
                if (ed.getBody().contentEditable) {
                    ed.getBody().contentEditable = false;
                }else if(ed.getDoc().designMode){
                    ed.getDoc().designMode = '';
                }
                
                var mceEdit = t._getElementsByClassName('*',ed.getParam("editableregions_editable_class", "mceEditable"),ed.getWin());

                for(var i=0;i<mceEdit.length;i++){
                    mceEdit[i].contentEditable=true;

                }

                
            });
            var editOrNot = function (ed, e) {
                var sc, ec, d = ed.getDoc();

                // Block if start or end is inside a non editable element
                sc = ed.dom.getParent(ed.selection.getStart(), function(n) {
                    return ed.dom.hasClass(n, editClass);
                });

                ec = ed.dom.getParent(ed.selection.getEnd(), function(n) {
                    return ed.dom.hasClass(n, editClass);
                });

                // Block or unblock
                if (!sc || !ec) {
                    d.designMode = 'Off';
                    t._setDisabled(1);
                    return false;
                } else {
                    d.designMode = 'On';
                    t._setDisabled(0);
                }
            }
            ed.onNodeChange.addToTop(function(ed, cm, n) {
                return editOrNot(ed);
            });
            ed.onContextMenu.addToTop(function(ed, e) {
                Event.cancel(e);
                return editOrNot(ed);
            });
            ed.onMouseDown.addToTop(function(ed, e) {
                d = ed.getDoc();
                d.designMode = 'Off';
            });
        },

It disables context menu and also turn off design mode (to prevent from resizing elements) outside the editable regions.
The drawback of turning off desing mode, is that sometimes links outside of editableregions are clickable and get you out of your page. An improvement will be to see if an link element was clicked to cancel the click event or something.

It works, but I didn't test it very well.