Opened 9 years ago

Closed 5 years ago

#349 closed defect (inactive)

Paste support for Firefox!

Reported by: paulb Owned by: paulb
Priority: normal Milestone: Version 1.0
Component: Xinha Core Version:
Severity: normal Keywords:
Cc:

Description

FCKEditor has a brilliant solution to the paste problem on firefox: popup a window and allow the user to paste into it. I've shamelessly stolen their code and implemented a WordPaste? plugin. It will remove all of MSWord's junk and then some (it may remove too much for general use - feel free to make changes). I've attached the plugin code, and there is one modification required in htmlarea.js:

line 3479:

  case "cut":
  case "copy":
  case "paste":
    try {
      this._doc.execCommand(cmdID, UI, param);
    } catch (e) { }
    if (this.config.killWordOnPaste) {
      if(typeof WordPaste == 'undefined')
      {
        HTMLArea.loadPlugin("WordPaste", function() { editor.generate(); } );
        editor.registerPlugin('WordPaste');
      }
      editor.plugins['WordPaste'].instance._buttonPress(null, null);
    }
    break;

I'm not sure if all of that is necessary, I just copied how it was done in another part of the program and it seemed to work.

Also, when you load your editor, you must enable the "WordPaste?" plugin.

This is solving two problems at once: pasting from FireFox?, and MSWord clean. Probably most people will want to configure these things individually, and so this code will have to be modified to accommodate that.

Attachments (5)

WordPaste.zip (3.7 KB) - added by paulb 9 years ago.
.2 (0 bytes) - added by anonymous 9 years ago.
mozilla_paste.html (3.6 KB) - added by paulb 9 years ago.
Put this in the popups directory
WordPaste.2.zip (3.7 KB) - added by paulb 9 years ago.
New WordPaste? plugin - should replace the previous one
paste.html (1.3 KB) - added by paulb 9 years ago.
Version 3 of the popup - put it in your "popups" directory.

Download all attachments as: .zip

Change History (22)

comment:1 Changed 9 years ago by paulb

Ok, I went ahead and fixed it so that it honors the killWordOnPaste config variable. Also fixed it so that the popup will not come up in Firefox if the user has fixed their config so as to disable to security settings.

Attachment is updated.

Here is the new code for htmlarea.js:

      case "cut":
      case "copy":
      case "paste":
        doPastePopup = false;
        try {
          this._doc.execCommand(cmdID, UI, param);
        } catch (e) {
          if (HTMLArea.is_gecko) {
              doPastePopup = true;
          }
        }
        if (this.config.killWordOnPaste || doPastePopup) {
          if(typeof WordPaste == 'undefined') {
              HTMLArea.loadPlugin("WordPaste", function() { editor.generate(); } );
              editor.registerPlugin('WordPaste');
          }
          if(typeof WordPaste == 'function') {
              editor.plugins['WordPaste'].instance._buttonPress(doPastePopup);
          }
        } 

Changed 9 years ago by paulb

comment:2 Changed 9 years ago by g2010a

Can you post a little bit more of the context for the code added to htmlarea.js? If someone has a modified version of it, it may be hard to know where to put the new code. Thnx.

comment:3 Changed 9 years ago by g2010a

Also, Word has a tendency to specify font faces and sizes... something which some users might want to remove. Just add

// Remove all FONT tags
html = html.replace(/<\/?\s*FONT[^>]*>/gi, "" );

to the class file.

comment:4 Changed 9 years ago by mharrisonline

If someone has the joyful job of cleaning up a glob of bad code someone already pasted from Word, the UnFormat? plugin uploaded in ticket 305 has the ability to remove not just formating, but all HTML from the selected area, or the entire page, leaving only the plain text (when it doubt, throw it out... :D ).

Changed 9 years ago by anonymous

comment:5 Changed 9 years ago by anonymous

  • Milestone set to Version 1.0

sdfew
fwefwe

comment:6 Changed 9 years ago by anonymous

gtrerg

comment:7 Changed 9 years ago by gocher

I'm cleaning up my Code with:

  var html = this.getInnerHTML();
  // Remove HTML comments
  html = html.replace(/<!--[\w\s\d@{}:.;,'"%!#_=&|?~()[*+\/\-\]]*-->/gi, "");
  html = html.replace(/<!--[^\0]*-->/gi, '');
  // Remove all HTML tags
  html = html.replace(/<\/?\s*HTML[^>]*>/gi, "");
  // Remove all BODY tags
  html = html.replace(/<\/?\s*BODY[^>]*>/gi, "");
  // Remove all META tags
  html = html.replace(/<\/?\s*META[^>]*>/gi, "");
  // Remove all SPAN tags
  html = html.replace(/<\/?\s*SPAN[^>]*>/gi, "");
  // Remove all FONT tags
  html = html.replace(/<\/?\s*FONT[^>]*>/gi, "");
  // Remove all IFRAME tags.
  html = html.replace(/<\/?\s*IFRAME[^>]*>/gi, "");
  // Remove all STYLE tags & content
  html = html.replace(/<\/?\s*STYLE[^>]*>(.|[\n\r\t])*<\/\s*STYLE\s*>/gi, "");
  // Remove all TITLE tags & content
  html = html.replace(/<\s*TITLE[^>]*>(.|[\n\r\t])*<\/\s*TITLE\s*>/gi, "");
  // Remove javascript
  html = html.replace(/<\s*SCRIPT[^>]*>[^\0]*<\/\s*SCRIPT\s*>/gi, "");
  // Remove all HEAD tags & content
  html = html.replace(/<\s*HEAD[^>]*>(.|[\n\r\t])*<\/\s*HEAD\s*>/gi, "" );
  // Remove Class attributes
  html = html.replace(/<\s*(\w[^>]*) class=([^ |>]*)([^>]*)/gi, "<$1$3");
  // Remove Style attributes
  html = html.replace(/<\s*(\w[^>]*) style="([^"]*)"([^>]*)/gi, "<$1$3");
  // Remove Lang attributes
  html = html.replace(/<\s*(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3");
  // Remove XML elements and declarations
  html = html.replace(/<\\?\?xml[^>]*>/gi, "");
  // Remove Tags with XML namespace declarations: <o:p></o:p>
  html = html.replace(/<\/?\w+:[^>]*>/gi, "");
  // Replace the  
  //html = html.replace(/ /, " " );
  // Transform <P> to <DIV>
  //html = html.replace(/<\s*p[^>]*>/gi, "<div>");
  //html = html.replace(/<\/\s*p[^>]*>/gi, "</div>");

  // Add attribute quotes
  //html = html.replace(/\s*=\s*(['"])?(([^>" ]| (?=[^"=]+['"]))+)\1?/gi, '="$2"');
  // Terminate singlet tags
  //html = html.replace(/<(br|hr|img|input|link|meta|param|embed)([^>]*)>/gi, "<$1$2 />");
  // Strip _moz attributes
  //html = html.replace(/(\S*\s*=\s*)?_moz[^=>]*(=\s*[^>]*)?/gi, " ");
  // Remove multi whitespaces
  html = html.replace(/[ \t]{2,}/gi, " ");
  // Remove Empty Lines
  html = html.replace(/[\n\r]{2,}/gi, "\n");
  // Remove starting blanks
  html = html.replace(/\n /gi, "\n");
  // Replace empty paragraph
  html = html.replace(/<p \/>/gi, "<p> </p>");

  html = html.replace(/<(\/?)STRONG>/gi, "<$1B>");    // replace strong
  html = html.replace(/<(\/?)EM>/gi, "<$1I>");        // replace em
  html = html.replace(/<(\/?)STRIKE>/gi, "<$1S>");    // replace strike
  html = html.replace(/<HR[^>]*?>/gi, '<hr size="0.5" />'); // replace hr

  html = html.trim();
  
  this.setHTML(html); 


comment:8 Changed 9 years ago by paulb

  • Owner changed from gogo to paulb
  • Status changed from new to assigned

Ok, I've reworked things a bit. Now the popup window is part of standard xinha, and you can apply a series of filters to the pasted text.

There is a inconsistancy with IE. When you paste with IE, "wordClean()" is run on the ENTIRE document. I would suggest that if "killWordOnPaste" is on, then IE should popup a window as well so that we will only filter the pasted text. Also, I would suggest that we deprecate "killWordOnPaste" and use the "pasteFilters" instead.

htmlarea.js, about line 272:

  // A set of functions that will filter any pasted text.
  // Each function takes a single argument, which is the text to be filtered.
  // This allows multiple plugins to change the text before it is inserted
  // into the editor.
  this.pasteFilters = [ ];

htmlarea.js, about line 3485:

      case "cut":
      case "copy":
      case "paste":
        try {
          this._doc.execCommand(cmdID, UI, param);
          if (this.config.killWordOnPaste) {
            this._wordClean();
          }
          for (var i = 0; i < this.config.pasteFilters.length; i++) {
            this._doc.body = this.config.pasteFilters[i](this._doc.body);
          }
        } catch (e) {
          if (HTMLArea.is_gecko) {
            // Mozilla has a security problem with paste.
            // Popup window to get the text.
            pasteEditor = this;
            this._popupDialog( "mozilla_paste.html", 
                function( html ) 
                {
                    if ( !html ) {  
                        //user must have pressed Cancel
                        return false;
                    }
                    for (var i = 0; i < pasteEditor.config.pasteFilters.length; i++) {
                        html = pasteEditor.config.pasteFilters[i](html);
                    }
                    pasteEditor.insertHTML(html);
                }, // anonymous function
                null);
          }
        } // catch

Changed 9 years ago by paulb

Put this in the popups directory

Changed 9 years ago by paulb

New WordPaste? plugin - should replace the previous one

comment:9 Changed 9 years ago by niko

nice, works great for firefox!

you might solve the problem that wordClean runs on the whole document in IE by using this to get the clipboard-data:
http://msdn.microsoft.com/workshop/author/dhtml/reference/objects/clipboarddata.asp
(i haven't tested it, and i don't know if you get HTML-code out of it)

and another thing: are we allowed to use fckeditors code?

comment:10 Changed 9 years ago by gocher

mozilla_paste.html

<html>
<head>
  <title>Paste</title>
  <link rel="stylesheet" type="text/css" href="popup.css" />
  <script type="text/javascript" src="popup.js"></script>
<script type="text/javascript">

function Init() {
  HTMLArea = window.opener.HTMLArea;
  __dlg_translate('mozilla_paste');
  __dlg_init();
  window.resizeTo(400, 300);
  document.getElementById('pasteData').contentDocument.designMode = 'on';
}

function onOK() {
  var sHtml = document.getElementById('pasteData').contentDocument.body.innerHTML ;
  __dlg_close(sHtml);
  return true;
}

function onCancel() {
  __dlg_close(null);
  return false;
}
</script>

</head>
<body class="dialog" onload="Init()">
  <p>The editor was not able to automaticaly execute pasting because of the <br />
     <strong>security settings</strong> of your browser.<br>
     Please paste inside the following box using the keyboard (<strong>Ctrl+V</strong>) and hit <strong>OK</strong>.</p>
	<iframe id="pasteData" src="blank.html" height="160px" width="370px" style="BACKGROUND-COLOR: #ffffff"></iframe>
  <div id="buttons">
  <button type="button" name="ok" onclick="return onOK();">OK</button>
  <button type="button" name="cancel" onclick="return onCancel();">Cancel</button>
  </div>
</body>
</html>

comment:11 Changed 9 years ago by gocher

window.clipboardData.getData requires String that specifies one of the following data format values:
Text Retrieves data formatted as text.
URL Retrieves data formatted as a URL.


window.clipboardData.getData doesn't works with HTML / formated Text!

comment:12 Changed 9 years ago by paulb

gocher, Thanks for the revised paste popup!

Ok, I think this might be a candidate for checking in. Please let me know your thoughts:

  1. In IE, behavior is normal if there are no paste filters (text is pasted directly into the editor).
  1. If the user is using Mozilla or has paste filters turned on, they will be given the popup. When they "OK" the popup, the contents will be filtered and then inserted.

I've revised the popup to work in IE and Mozilla browsers. Please download "paste.html" (attached) and put it in your "popups" directory if you want to try this out.

I have tested this on both IE and Firefox and it works.

Can I check it in? :)

Here is the code for htmlarea.js, about line 3485:

      case "cut":
      case "copy":
      case "paste":
	doPopup = false;
        try {
          if (this.config.pasteFilters.length <= 0) {
	    this._doc.execCommand(cmdID, UI, param);
	  }
	  else {
	    doPopup = true;
	  }
        } catch (e) {
          if (HTMLArea.is_gecko) {
	    doPopup = true;
          }
        } // catch

        // Popup window to get the text.
	// We do the popup if:
        // 1) we're in a Mozilla browser
	// 2) There are paste filters in use
	if (doPopup) {
          pasteEditor = this;
          this._popupDialog( "paste.html", 
                function( html ) 
                {
                    if ( !html ) {  
                        //user must have pressed Cancel
                        return false;
                    }
                    for (var i = 0; i < pasteEditor.config.pasteFilters.length; i++) {
                        html = pasteEditor.config.pasteFilters[i](html);
                    }
                    pasteEditor.insertHTML(html);
                }, // anonymous function
                null);
	}

Changed 9 years ago by paulb

Version 3 of the popup - put it in your "popups" directory.

comment:13 Changed 9 years ago by niko

my thoughts:
would it be a problem to clean alway ALL html-code on paste?
not only the pasted code?
imho this popup has a bad usability (sure, for firefox its great - as it is the alternative for no paste at all)

and, do we really need this WordPaste?-plugin - that does just clean word-html? why not modify the internal function? of course it should not be too agressive as its enabled by default (i haven't tested it)
the config.pasteFilters we could still implement.

thinking about that... why not implement some kind of pasteFilters for the SuperClean plugin? so the user can select how agressive the cleaning should happen?
(but that would be a new ticket :D)

comment:14 Changed 9 years ago by paulb

I would think that destroying all the formatting when you paste something in IE would make for angry users. Yes, a popup isnt so great, but it works properly, that is, it only cleans the pasted code. It will only be enabled in IE when you want to filter the code you paste.

Yes, the wordClean function could be modified to be a pasteFilter instead, as well as SuperClean and any other plugins that are supposed to do similar things.

comment:15 Changed 8 years ago by gogo

This is going back a long way. But I think it should be implemented as a plugin "AdvancedPaste?" or something, including both the pasteFilters idea and the popup for Gecko. If the case for cut/copy/paste were split out into a function in the Xinha prototype, then the plugin can override that to work it's magic.

comment:16 Changed 6 years ago by guest

Firefox has a hidden preference which can be edited to allow pasting formatted text to certain sites. There's also an AllowClipboard extension that implements user-friendly editing.

I think the default Cut/Copy/Paste? buttons should not simply blacklist Gecko-based browsers, explaining Firefox users about Ctrl+V, but at least try to access the clipboard first? I think it's enough that in the default configuration these buttons are simple hidden.

comment:17 Changed 5 years ago by gogo

  • Resolution set to inactive
  • Status changed from assigned to closed

Inactive, closing. If this is still wanted/needed, somebody create a clean plugin in a zip, attach it, and comment to me and I'll commit it.

Note: See TracTickets for help on using tickets.