Forcing Paste-as-Plain-Text in TinyMCE

Highlight a portion of a webpage and paste it into a WYSIWYG editor like TinyMCE or FCKeditor. Looks good, right? Now view the source code. It’s usually a mess. Now try the same thing with content copied from a Word document. It’s even worse, oftentimes a sheer nightmare. Sometimes, usually in the case of novice users, I just wish I could make CTRL+V paste plain, unformatted text into these editors. This would encourage users to reformat copied content using the editor, resulting in cleaner, more semantic markup.

Of course, we could attempt to train users to use the built-in Paste-as-Plain-Text or Paste From Word features. Let’s be honest, thought, in most cases it’s simply not going to happen. In even more cases, it’s not going to be appropriate to copy and paste more than just text from a Word document or another webpage. The bottom line is, sometimes forcing plain text is necessary when it comes to JavaScript-based WYSIWYG editors.

The developer of the FCKEditor evidently thought it was a good idea, too, and as of version 1.0 there’s been an option to enable this functionality in the configuration file. Unfortunately, if you Google for this in regard to TinyMCE, it’s hard to come across a clean cut answer to the question.

What you will find is people arguing about why you would want to do this. Others argue that it’s best to clean up TinyMCE’s code on the server side. For security reasons, I totally agree with that, but the majority of posts I’ve read indicate to me that most people are simply missing the point here:

We want to prevent novice users from butchering webpages when they copy and paste formatted content.

The emphasis on prevent is to indicate that you really can’t force a Paste-as-Plain-Text action for all users, so to speak. What you can do, however, is encourage them to Paste-as-Plain-Text using JavaScript, and it’s actually really easy. Here is the code that you’ll need to show the Paste-as-Plain-Text dialog whenever someone presses CTRL+V in the TinyMCE editor:

tinyMCE.init({
    mode: "textareas",
    theme : "advanced",
    // (more init options here)

    setup: function(ed) {

        // Force Paste-as-Plain-Text
        ed.onPaste.add( function(ed, e, o) {
            ed.execCommand('mcePasteText', true);
            return tinymce.dom.Event.cancel(e);
        });

    }
});

As with virtually everything JavaScript, there are a few drawbacks. First off, it relies on the onPaste JavaScript event, which is supported in Internet Explorer, Safari (and other Webkit browsers), and Firefox 3 (but not 2). It is not supported in Opera. For folks with unsupportive browsers, they will still be able to paste as usual (including unwanted, messy markup).

Note: You may want to remove the normal Paste button from the toolbar so people don’t think that it doesn’t work properly.

Of course, it’s important to run your code through a server side filter upon submission, but hopefully this will, at least in the mainstream browsers, limit the number of copy and paste nightmares that have plagued WYSIWYG editors since day one.

If you enjoyed this article, please share it with a friend!

15 Responses to Forcing Paste-as-Plain-Text in TinyMCE

  1. Vivekanand says:

    Instead of using all these, we can use directly the web tool called “wordoff” (http://wordoff.org/), directly copy from a word document or any other document, on a single press of a button it will remove all the unnecessary junk and will generate clean HTML.

  2. Cory LaViska says:

    @Vivekanand Great tool, thanks for the link. Alas, this isn’t something that novice users will benefit from, as most of them simply don’t understand why they can’t just “copy and paste” content from Word and other sources.

  3. Cool, seems to work. Would be nice if it just did the paste without poping up the paste dialog window…..
    Should stop my clients complaining about ugly formatting (here’s hoping anyway).

  4. Jim says:

    It would be nice if the popup “paste from text” was pre-filled with the current buffer

  5. mondfish says:

    thanks a lot,
    nice trick…

  6. Elijah Grey says:

    For the onPaste trouble, I rely on checking if Ctrl is pressed and hasn’t been unpressed (via keycodes) and listen for V’s keycode onkeypress.

  7. This isn’t working in my case. I’ve tried it in both Chrome and FF3 and when ( CTRL + V nothing happens. FF shows 2 errors when tinyMCE loads, but for some reason doesn’t show me line numbers or reasons.

    Here’s my TinyMCE init code:

    // initialize the tinyMCE textarea
    tinyMCE.init({
    mode : “textareas”,
    theme : “simple”,
    width : “500″,
    height : “230″,
    setup: function(ed) {

    // Force Paste-as-Plain-Text
    ed.onPaste.add( function(ed, e, o) {
    ed.execCommand(‘mcePasteText’, true);
    return tinymce.dom.Event.cancel(e);
    });

    }
    });

    Anyone see any problems?

  8. Just FYI, I’ve ruled out the errors as part of this problem. Apparently they were there before I started working on the “clean up word” issue.

  9. Andy Matthews, I don’t know if you have found a solution or not, but it worked for me after I added the paste plugin in my init, i.e. including e.g.

    plugins: “safari,paste”,

    in your init function.

  10. Cory LaViska says:

    @Thomas, good call. I neglected to mention that this ONLY works with the paste plugin enabled. You can also use the inlinepopups plugin if you don’t want the dialog to be in a separate window.

  11. Simon Lawrence says:

    Thanks Cory, this is just what I was looking for!

    I agree that the best approach is to prevent users from entering rubbish HTML in the first place rather than trying to clean it up afterwards and can be confusing when all your formatting disappears.

    But I do use a server side clean up function as a last resort. I have posted the PHP code for those interested as I couldn’t find anything like this out there so if might save someone some time. It is based on the code at http://www.1stclassmedia.co.uk/developers/clean-ms-word-formatting.php

    function clean_word_html($input) {
    $input = preg_replace(‘/\s*< \/o:p>/’, ”, $input);
    $input = preg_replace(‘/
    .*?< \/o:p>/’, ‘ ’, $input);
    $input = preg_replace(‘/\s*mso-[^:]+:[^;"]+;?/i’, ”, $input);
    $input = preg_replace(‘/\s*MARGIN: 0cm 0cm 0pt\s*;/i’, ”, $input);
    $input = preg_replace(‘/\s*MARGIN: 0cm 0cm 0pt\s*”/i’, ‘\”‘, $input);
    $input = preg_replace(‘/\s*TEXT-INDENT: 0cm\s*;/i’, ”, $input);
    $input = preg_replace(‘/\s*TEXT-INDENT: 0cm\s*”/i’, ‘\”‘, $input);
    $input = preg_replace(‘/\s*TEXT-ALIGN: [^\s;]+;?”/i’, ‘\”‘, $input);
    $input = preg_replace(‘/\s*PAGE-BREAK-BEFORE: [^\s;]+;?”/i’, ‘\”‘, $input);
    $input = preg_replace(‘/\s*FONT-VARIANT: [^\s;]+;?”/i’, ‘\”‘, $input);
    $input = preg_replace(‘/\s*tab-stops:[^;"]*;?/i’, ”, $input);
    $input = preg_replace(‘/\s*tab-stops:[^"]*/i’, ”, $input);
    $input = preg_replace(‘/\s*face=”[^"]*”/i’, ”, $input);
    $input = preg_replace(‘/\s*face=[^ >]*/i’, ”, $input);
    $input = preg_replace(‘/\s*FONT-FAMILY:[^;"]*;?/i’, ”, $input);
    $input = preg_replace(‘/\s*FONT-SIZE: x-small\s*;/i’, ”, $input);
    $input = preg_replace(‘/\s*color:[^;"]*;?/i’, ”, $input);
    $input = preg_replace(‘/\s*background:[^;"]*;?/i’, ”, $input);
    $input = preg_replace(‘/< (\w[^>]*) class=([^ |>]*)([^>]*)/i’, ‘< $1$3', $input);
    $input = preg_replace('/\s*style="\s*"/i', '', $input);
    $input = preg_replace('/]*>\s* \s*< \/SPAN>/i’, ‘ ’, $input);
    $input = preg_replace(‘/]*>< \/SPAN>/i’, ”, $input);
    $input = preg_replace(‘/< (\w[^>]*) lang=([^ |>]*)([^>]*)/i’, ‘< $1$3', $input);
    $input = preg_replace('/(.*?)< \/SPAN>/i’, ‘$1′, $input);
    $input = preg_replace(‘/(.*?)< \/FONT>/i’, ‘$1′, $input);
    $input = preg_replace(‘/< \\?\?xml[^>]*>/i’, ”, $input);
    $input = preg_replace(‘/< \/?\w+:[^>]*>/i’, ”, $input);
    $input = preg_replace(‘/< (B|b)> < \/\b|B>/’, ”, $input);
    $input = preg_replace(‘/

    ]*>\s* \s*< \/P>/i’, ”, $input);
    $input = preg_replace(‘/>\s* \s*< \//i', '>]+)[^>]*>\s*< \/\1>/’, ”, $input);
    return $input;
    }

  12. Cory LaViska says:

    It’s worth mentioning that TinyMCE 3.2.3 was released on April 23, 2009. This new version includes a completely rewritten version of the Paste From Word plugin, which no longer requires users to click the toolbar and paste their Word content into a dialog.

    I’ve done some pretty thorough testing and it looks like the new version of this plugin is doing its job extremely well. Although it’s not a replacement for Paste-as-plain-text functionality, if you were considering forcing your users to paste as plain text because of problems with MS Word code, you might simply try upgrading TinyMCE to the latest version instead.

    More info: http://tinymce.moxiecode.com/changelog.php

  13. Job says:

    thanks, just what i wanted.
    The popup is a bit slow, but thats something i guess i have to live with..

  14. Your code works for me, but doesn’t seem to offer any functionality over the config option

    paste_auto_cleanup_on_paste : true

    However, I’ve found that both solutions render the keyboard Delete button in the editor window useless. That is, when I select text and hit Delete, nothing happens. Have you encountered this problem, and is there a solution?

  15. Mark says:

    I offered this code when I found that people were simply not using the editor clean-up functionality which comes bundled with TinyMCE.

    Glad some people found a use for it, it was essentially a butchered version of earlier MS-Word clean up scripts, but seemed to work for me.