{regex: /<span[^>]*>( |\s)*<\/span>/gi, replace: ""},
// Remove class="Msoblah"
{regex: /class="Mso[^"]*"/gi, replace: ""},
+ // Remove any open HTML comment opens that are not followed by a close. This can completely break page layout.
+ {regex: /<!--(?![\s\S]*?-->)/gi, replace: ""},
// Source: "http://www.codinghorror.com/blog/2006/01/cleaning-words-nasty-html.html"
- // Remove forbidden tags for content, title, meta, style, st0-9, head, font, html, body.
- {regex: /<(\/?title|\/?meta|\/?style|\/?st\d|\/?head|\/?font|\/?html|\/?body|!\[)[^>]*?>/gi, replace: ""},
+ // Remove forbidden tags for content, title, meta, style, st0-9, head, font, html, body, link.
+ {regex: /<(\/?title|\/?meta|\/?style|\/?st\d|\/?head|\/?font|\/?html|\/?body|\/?link|!\[)[^>]*?>/gi, replace: ""},
// Source: "http://www.tim-jarrett.com/labs_javascript_scrub_word.php"
// Replace extended chars with simple text.
}
return content;
+ },
+
+ /**
+ * Intercept and clean html paste events.
+ *
+ * @method pasteCleanup
+ * @param {Object} sourceEvent The YUI EventFacade object
+ * @return {Boolean} True if the passed event should continue, false if not.
+ */
+ pasteCleanup: function(sourceEvent) {
+ // We only expect paste events, but we will check anyways.
+ if (sourceEvent.type === 'paste') {
+ // The YUI event wrapper doesn't provide paste event info, so we need the underlying event.
+ var event = sourceEvent._event;
+ // Check if we have a valid clipboardData object in the event.
+ // IE has a clipboard object at window.clipboardData, but as of IE 11, it does not provide HTML content access.
+ if (event && event.clipboardData && event.clipboardData.getData) {
+ // Check if there is HTML type to be pasted, this is all we care about.
+ var types = event.clipboardData.types;
+ var isHTML = false;
+ // Different browsers use different things to hold the types, so test various functions.
+ if (!types) {
+ isHTML = false;
+ } else if (typeof types.contains === 'function') {
+ isHTML = types.contains('text/html');
+ } else if (typeof types.indexOf === 'function') {
+ isHTML = (types.indexOf('text/html') > -1);
+ if (!isHTML) {
+ if ((types.indexOf('com.apple.webarchive') > -1) || (types.indexOf('com.apple.iWork.TSPNativeData') > -1)) {
+ // This is going to be a specialized Apple paste paste. We cannot capture this, so clean everything.
+ this.fallbackPasteCleanupDelayed();
+ return true;
+ }
+ }
+ } else {
+ // We don't know how to handle the clipboard info, so wait for the clipboard event to finish then fallback.
+ this.fallbackPasteCleanupDelayed();
+ return true;
+ }
+
+ if (isHTML) {
+ // Get the clipboard content.
+ var content;
+ try {
+ content = event.clipboardData.getData('text/html');
+ } catch (error) {
+ // Something went wrong. Fallback.
+ this.fallbackPasteCleanupDelayed();
+ return true;
+ }
+
+ // Stop the original paste.
+ sourceEvent.preventDefault();
+
+ // Scrub the paste content.
+ content = this._cleanHTML(content);
+
+ // Save the current selection.
+ // Using saveSelection as it produces a more consistent experience.
+ var selection = window.rangy.saveSelection();
+
+ // Insert the content.
+ this.insertContentAtFocusPoint(content);
+
+ // Restore the selection, and collapse to end.
+ window.rangy.restoreSelection(selection);
+ window.rangy.getSelection().collapseToEnd();
+
+ // Update the text area.
+ this.updateOriginal();
+ return false;
+ } else {
+ // This is a non-html paste event, we can just let this continue on and call updateOriginalDelayed.
+ this.updateOriginalDelayed();
+ return true;
+ }
+ } else {
+ // If we reached a here, this probably means the browser has limited (or no) clipboard support.
+ // Wait for the clipboard event to finish then fallback.
+ this.fallbackPasteCleanupDelayed();
+ return true;
+ }
+ }
+
+ // We should never get here - we must have received a non-paste event for some reason.
+ // Um, just call updateOriginalDelayed() - it's safe.
+ this.updateOriginalDelayed();
+ return true;
+ },
+
+ /**
+ * Cleanup code after a paste event if we couldn't intercept the paste content.
+ *
+ * @method fallbackPasteCleanup
+ * @chainable
+ */
+ fallbackPasteCleanup: function() {
+ Y.log('Using fallbackPasteCleanup for atto cleanup', 'debug', LOGNAME);
+
+ // Save the current selection (cursor position).
+ var selection = window.rangy.saveSelection();
+
+ // Get, clean, and replace the content in the editable.
+ var content = this.editor.get('innerHTML');
+ this.editor.set('innerHTML', this._cleanHTML(content));
+
+ // Update the textarea.
+ this.updateOriginal();
+
+ // Restore the selection (cursor position).
+ window.rangy.restoreSelection(selection);
+
+ return this;
+ },
+
+ /**
+ * Calls fallbackPasteCleanup on a short timer to allow the paste event handlers to complete.
+ *
+ * @method fallbackPasteCleanupDelayed
+ * @chainable
+ */
+ fallbackPasteCleanupDelayed: function() {
+ Y.soon(Y.bind(this.fallbackPasteCleanup, this));
+
+ return this;
}
};