Merge branch 'MDL-47002-master' of git://github.com/merrill-oakland/moodle

This commit is contained in:
David Monllao
2015-03-23 14:59:05 +08:00
5 changed files with 396 additions and 12 deletions
@@ -298,7 +298,8 @@ Y.extend(Editor, Y.Base, {
* @chainable
*/
setupAutomaticPolling: function() {
this._registerEventHandle(this.editor.on(['keyup', 'paste', 'cut'], this.updateOriginal, this));
this._registerEventHandle(this.editor.on(['keyup', 'cut'], this.updateOriginal, this));
this._registerEventHandle(this.editor.on('paste', this.pasteCleanup, this));
// Call this.updateOriginal after dropped content has been processed.
this._registerEventHandle(this.editor.on('drop', this.updateOriginalDelayed, this));
@@ -1129,10 +1130,12 @@ EditorClean.prototype = {
{regex: /<span[^>]*>(&nbsp;|\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.
@@ -1155,6 +1158,131 @@ EditorClean.prototype = {
}
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;
}
};
File diff suppressed because one or more lines are too long
@@ -296,7 +296,8 @@ Y.extend(Editor, Y.Base, {
* @chainable
*/
setupAutomaticPolling: function() {
this._registerEventHandle(this.editor.on(['keyup', 'paste', 'cut'], this.updateOriginal, this));
this._registerEventHandle(this.editor.on(['keyup', 'cut'], this.updateOriginal, this));
this._registerEventHandle(this.editor.on('paste', this.pasteCleanup, this));
// Call this.updateOriginal after dropped content has been processed.
this._registerEventHandle(this.editor.on('drop', this.updateOriginalDelayed, this));
@@ -1119,10 +1120,12 @@ EditorClean.prototype = {
{regex: /<span[^>]*>(&nbsp;|\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.
@@ -1145,6 +1148,130 @@ EditorClean.prototype = {
}
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() {
// 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;
}
};
+129 -2
View File
@@ -105,10 +105,12 @@ EditorClean.prototype = {
{regex: /<span[^>]*>(&nbsp;|\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.
@@ -131,6 +133,131 @@ EditorClean.prototype = {
}
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;
}
};
+2 -1
View File
@@ -296,7 +296,8 @@ Y.extend(Editor, Y.Base, {
* @chainable
*/
setupAutomaticPolling: function() {
this._registerEventHandle(this.editor.on(['keyup', 'paste', 'cut'], this.updateOriginal, this));
this._registerEventHandle(this.editor.on(['keyup', 'cut'], this.updateOriginal, this));
this._registerEventHandle(this.editor.on('paste', this.pasteCleanup, this));
// Call this.updateOriginal after dropped content has been processed.
this._registerEventHandle(this.editor.on('drop', this.updateOriginalDelayed, this));