4
\$\begingroup\$

I am so sick of WYSIWYG editors being exclusively for the latest browser or being huge with every option under the sun. I tried to make my own, using code I found online as well as a little of my own handy work.

This is what I came up with:

http://jsfiddle.net/spadez/RRBHw/1/

/*
 * WYSIWYG EDITOR BASED ON JQUERY RTE
 */

// define the rte light plugin
(function ($) {

    if (typeof $.fn.rte === "undefined") {

        var defaults = {
            max_height: 350
        };

        $.fn.rte = function (options) {

            $.fn.rte.html = function (iframe) {
                return iframe.contentWindow.document.getElementsByTagName("body")[0].innerHTML;
            };

            // build main options before element iteration
            var opts = $.extend(defaults, options);

            // iterate and construct the RTEs
            return this.each(function () {
                var textarea = $(this);
                var iframe;
                var element_id = textarea.attr("id");

                // enable design mode
                function enableDesignMode() {

                    var content = textarea.val();

                    // Mozilla needs this to display caret
                    if ($.trim(content) === '') {
                        content = '<br />';
                    }

                    // already created? show/hide
                    if (iframe) {
                        console.log("already created");
                        textarea.hide();
                        $(iframe).contents().find("body").html(content);
                        $(iframe).show();
                        $("#toolbar-" + element_id).remove();
                        textarea.before(toolbar());
                        return true;
                    }

                    // for compatibility reasons, need to be created this way
                    iframe = document.createElement("iframe");
                    iframe.frameBorder = 0;
                    iframe.frameMargin = 0;
                    iframe.framePadding = 0;
                    iframe.height = 200;
                    if (textarea.attr('class')) iframe.className = textarea.attr('class');
                    if (textarea.attr('id')) iframe.id = element_id;
                    if (textarea.attr('name')) iframe.title = textarea.attr('name');

                    textarea.after(iframe);

                    var doc = "<html><head>" + "</head><body class='frameBody'>" + content + "</body></html>";
                    tryEnableDesignMode(doc, function () {
                        $("#toolbar-" + element_id).remove();
                        textarea.before(toolbar());
                        // hide textarea
                        textarea.hide();

                    });

                }

                function tryEnableDesignMode(doc, callback) {
                    if (!iframe) {
                        return false;
                    }

                    try {
                        iframe.contentWindow.document.open();
                        iframe.contentWindow.document.write(doc);
                        iframe.contentWindow.document.close();
                    } catch (error) {
                        console.log(error);
                    }
                    if (document.contentEditable) {
                        iframe.contentWindow.document.designMode = "On";
                        callback();
                        return true;
                    } else if (document.designMode !== null) {
                        try {
                            iframe.contentWindow.document.designMode = "on";
                            callback();
                            return true;
                        } catch (error) {
                            console.log(error);
                        }
                    }
                    setTimeout(function () {
                        tryEnableDesignMode(doc, callback);
                    }, 500);
                    return false;
                }

                function disableDesignMode(submit) {
                    var content = $(iframe).contents().find("body").html();

                    if ($(iframe).is(":visible")) {
                        textarea.val(content);
                    }

                    if (submit !== true) {
                        textarea.show();
                        $(iframe).hide();
                    }
                }

                // create toolbar and bind events to it's elements
                function toolbar() {
                    var tb = $("<div class='rte-toolbar' id='toolbar-" + element_id + "'><div>\
                <p>\
                    <a href='#' class='bold'>Bold</a>\
                    <a href='#' class='italic'>Italic</a>\
                    <a href='#' class='unorderedlist'>List</a>\
                </p></div></div>");

                    $('.bold', tb).click(function () {
                        formatText('bold');
                        return false;
                    });
                    $('.italic', tb).click(function () {
                        formatText('italic');
                        return false;
                    });
                    $('.unorderedlist', tb).click(function () {
                        formatText('insertunorderedlist');
                        return false;
                    });

                    $(iframe).parents('form').submit(function () {
                        disableDesignMode(true);
                    });


                    var iframeDoc = $(iframe.contentWindow.document);

                    var select = $('select', tb)[0];
                    iframeDoc.mouseup(function () {
                        setSelectedType(getSelectionElement(), select);
                        return true;
                    });

                    iframeDoc.keyup(function () {
                        setSelectedType(getSelectionElement(), select);
                        var body = $('body', iframeDoc);
                        if (body.scrollTop() > 0) {
                            var iframe_height = parseInt(iframe.style.height, 10);
                            if (isNaN(iframe_height)) iframe_height = 0;
                            var h = Math.min(opts.max_height, iframe_height + body.scrollTop()) + 'px';
                            iframe.style.height = h;
                        }
                        return true;
                    });
                    return tb;
                }

                function formatText(command, option) {
                    iframe.contentWindow.focus();
                    try {
                        iframe.contentWindow.document.execCommand(command, false, option);
                    } catch (e) {
                        console.log(e);
                    }
                    iframe.contentWindow.focus();
                }

                function setSelectedType(node, select) {
                    while (node.parentNode) {
                        var nName = node.nodeName.toLowerCase();
                        for (var i = 0; i < select.options.length; i++) {
                            if (nName == select.options[i].value) {
                                select.selectedIndex = i;
                                return true;
                            }
                        }
                        node = node.parentNode;
                    }
                    select.selectedIndex = 0;
                    return true;
                }

                function getSelectionElement() {
                    if (iframe.contentWindow.document.selection) {
                        // IE selections
                        selection = iframe.contentWindow.document.selection;
                        range = selection.createRange();
                        try {
                            node = range.parentElement();
                        } catch (e) {
                            return false;
                        }
                    } else {
                        // Mozilla selections
                        try {
                            selection = iframe.contentWindow.getSelection();
                            range = selection.getRangeAt(0);
                        } catch (e) {
                            return false;
                        }
                        node = range.commonAncestorContainer;
                    }
                    return node;
                }

                // enable design mode now
                enableDesignMode();

            }); //return this.each

        }; // rte

    } // if

    $(".rte-zone").rte({});

})(jQuery);

Can anyone give me some feedback on my work? Is there any room for improving or refactoring? It seems to work in every browser I tried which I am pretty happy with a degrades gracefully if JavaScript is not enabled.

\$\endgroup\$
4
+75
\$\begingroup\$

Just three quick notes about clean code, not a complete review:

  1. You could extract out some functions which would make some comments unnecessary, like:

    // IE selections
    // Mozilla selections
    

    Just create functions for them, like ieSelections and mozillaSelections.

  2. I'd introduce an explanatory variable here:

    if (iframe.contentWindow.document.selection) ...
    

    For example:

    var isInternetExplorer = iframe.contentWindow.document.selection;
    if (isInternetExplorer) ...
    

    Reference: Chapter 6. Composing Methods, Introduce Explaining Variable in Refactoring: Improving the Design of Existing Code by Martin Fowler:

    Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose.

    And Clean Code by Robert C. Martin, G19: Use Explanatory Variables.

  3. This comment also seems unnecessary:

    // enable design mode now
    enableDesignMode();
    

    The name of the function also says that.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.