X-Git-Url: https://scripts.mit.edu/gitweb/autoinstallsdev/wordpress.git/blobdiff_plain/9e77185fafaf4e60e2b73821e0e4b9b1a11fb85f..607b7e02d77e7326161e8ec15639052d2040f745:/wp-includes/js/tinymce/plugins/paste/plugin.js diff --git a/wp-includes/js/tinymce/plugins/paste/plugin.js b/wp-includes/js/tinymce/plugins/paste/plugin.js index 699096a3..8839e9ad 100644 --- a/wp-includes/js/tinymce/plugins/paste/plugin.js +++ b/wp-includes/js/tinymce/plugins/paste/plugin.js @@ -63,10 +63,12 @@ } function expose(ids) { - for (var i = 0; i < ids.length; i++) { - var target = exports; - var id = ids[i]; - var fragments = id.split(/[.\/]/); + var i, target, id, fragments, privateModules; + + for (i = 0; i < ids.length; i++) { + target = exports; + id = ids[i]; + fragments = id.split(/[.\/]/); for (var fi = 0; fi < fragments.length - 1; ++fi) { if (target[fragments[fi]] === undefined) { @@ -78,6 +80,21 @@ target[fragments[fragments.length - 1]] = modules[id]; } + + // Expose private modules for unit tests + if (exports.AMDLC_TESTS) { + privateModules = exports.privateModules || {}; + + for (id in modules) { + privateModules[id] = modules[id]; + } + + for (i = 0; i < ids.length; i++) { + delete privateModules[ids[i]]; + } + + exports.privateModules = privateModules; + } } // Included from: js/tinymce/plugins/paste/classes/Utils.js @@ -85,8 +102,8 @@ /** * Utils.js * - * Copyright, Moxiecode Systems AB * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing @@ -95,8 +112,7 @@ /** * This class contails various utility functions for the paste plugin. * - * @class tinymce.pasteplugin.Clipboard - * @private + * @class tinymce.pasteplugin.Utils */ define("tinymce/pasteplugin/Utils", [ "tinymce/util/Tools", @@ -200,16 +216,120 @@ define("tinymce/pasteplugin/Utils", [ /^[\s\S]*]*>\s*|\s*<\/body[^>]*>[\s\S]*$/g, // Remove anything but the contents within the BODY element /|/g, // Inner fragments (tables from excel on mac) [/( ?)\u00a0<\/span>( ?)/g, trimSpaces], + /
/g, /
$/i // Trailing BR elements ]); return html; } + // TODO: Should be in some global class + function createIdGenerator(prefix) { + var count = 0; + + return function() { + return prefix + (count++); + }; + } + return { filter: filter, innerText: innerText, - trimHtml: trimHtml + trimHtml: trimHtml, + createIdGenerator: createIdGenerator + }; +}); + +// Included from: js/tinymce/plugins/paste/classes/SmartPaste.js + +/** + * SmartPaste.js + * + * Released under LGPL License. + * Copyright (c) 1999-2016 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Tries to be smart depending on what the user pastes if it looks like an url + * it will make a link out of the current selection. If it's an image url that looks + * like an image it will check if it's an image and insert it as an image. + * + * @class tinymce.pasteplugin.SmartPaste + * @private + */ +define("tinymce/pasteplugin/SmartPaste", [ + "tinymce/util/Tools" +], function (Tools) { + var isAbsoluteUrl = function (url) { + return /^https?:\/\/[\w\?\-\/+=.&%]+$/i.test(url); + }; + + var isImageUrl = function (url) { + return isAbsoluteUrl(url) && /.(gif|jpe?g|png)$/.test(url); + }; + + var createImage = function (editor, url, pasteHtml) { + editor.undoManager.extra(function () { + pasteHtml(editor, url); + }, function () { + editor.insertContent(''); + }); + + return true; + }; + + var createLink = function (editor, url, pasteHtml) { + editor.undoManager.extra(function () { + pasteHtml(editor, url); + }, function () { + editor.execCommand('mceInsertLink', false, url); + }); + + return true; + }; + + var linkSelection = function (editor, html, pasteHtml) { + return editor.selection.isCollapsed() === false && isAbsoluteUrl(html) ? createLink(editor, html, pasteHtml) : false; + }; + + var insertImage = function (editor, html, pasteHtml) { + return isImageUrl(html) ? createImage(editor, html, pasteHtml) : false; + }; + + var pasteHtml = function (editor, html) { + editor.insertContent(html, { + merge: editor.settings.paste_merge_formats !== false, + paste: true + }); + + return true; + }; + + var smartInsertContent = function (editor, html) { + Tools.each([ + linkSelection, + insertImage, + pasteHtml + ], function (action) { + return action(editor, html, pasteHtml) !== true; + }); + }; + + var insertContent = function (editor, html) { + if (editor.settings.smart_paste === false) { + pasteHtml(editor, html); + } else { + smartInsertContent(editor, html); + } + }; + + return { + isImageUrl: isImageUrl, + isAbsoluteUrl: isAbsoluteUrl, + insertContent: insertContent }; }); @@ -218,8 +338,8 @@ define("tinymce/pasteplugin/Utils", [ /** * Clipboard.js * - * Copyright, Moxiecode Systems AB * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing @@ -246,12 +366,17 @@ define("tinymce/pasteplugin/Utils", [ */ define("tinymce/pasteplugin/Clipboard", [ "tinymce/Env", + "tinymce/dom/RangeUtils", "tinymce/util/VK", - "tinymce/pasteplugin/Utils" -], function(Env, VK, Utils) { + "tinymce/pasteplugin/Utils", + "tinymce/pasteplugin/SmartPaste", + "tinymce/util/Delay" +], function(Env, RangeUtils, VK, Utils, SmartPaste, Delay) { return function(editor) { var self = this, pasteBinElm, lastRng, keyboardPasteTimeStamp = 0, draggingInternally = false; var pasteBinDefaultContent = '%MCEPASTEBIN%', keyboardPastePlainTextState; + var mceInternalUrlPrefix = 'data:text/mce-internal,'; + var uniqueId = Utils.createIdGenerator("mceclip"); /** * Pastes the specified HTML. This means that the HTML is filtered and then @@ -280,7 +405,7 @@ define("tinymce/pasteplugin/Clipboard", [ } if (!args.isDefaultPrevented()) { - editor.insertContent(html, {merge: editor.settings.paste_merge_formats !== false}); + SmartPaste.insertContent(editor, html); } } } @@ -345,15 +470,63 @@ define("tinymce/pasteplugin/Clipboard", [ } } + /** + * Returns the rect of the current caret if the caret is in an empty block before a + * BR we insert a temporary invisible character that we get the rect this way we always get a proper rect. + * + * TODO: This might be useful in core. + */ + function getCaretRect(rng) { + var rects, textNode, node, container = rng.startContainer; + + rects = rng.getClientRects(); + if (rects.length) { + return rects[0]; + } + + if (!rng.collapsed || container.nodeType != 1) { + return; + } + + node = container.childNodes[lastRng.startOffset]; + + // Skip empty whitespace nodes + while (node && node.nodeType == 3 && !node.data.length) { + node = node.nextSibling; + } + + if (!node) { + return; + } + + // Check if the location is |
+ // TODO: Might need to expand this to say | + if (node.tagName == 'BR') { + textNode = dom.doc.createTextNode('\uFEFF'); + node.parentNode.insertBefore(textNode, node); + + rng = dom.createRng(); + rng.setStartBefore(textNode); + rng.setEndAfter(textNode); + + rects = rng.getClientRects(); + dom.remove(textNode); + } + + if (rects.length) { + return rects[0]; + } + } + // Calculate top cordinate this is needed to avoid scrolling to top of document // We want the paste bin to be as close to the caret as possible to avoid scrolling if (lastRng.getClientRects) { - var rects = lastRng.getClientRects(); + var rect = getCaretRect(lastRng); - if (rects.length) { + if (rect) { // Client rects gets us closes to the actual // caret location in for example a wrapped paragraph block - top = scrollTop + (rects[0].top - dom.getPos(body).y); + top = scrollTop + (rect.top - dom.getPos(body).y); } else { top = scrollTop; @@ -453,26 +626,28 @@ define("tinymce/pasteplugin/Clipboard", [ * @return {Object} Object with mime types and data for those mime types. */ function getDataTransferItems(dataTransfer) { - var data = {}; + var items = {}; if (dataTransfer) { // Use old WebKit/IE API if (dataTransfer.getData) { var legacyText = dataTransfer.getData('Text'); if (legacyText && legacyText.length > 0) { - data['text/plain'] = legacyText; + if (legacyText.indexOf(mceInternalUrlPrefix) == -1) { + items['text/plain'] = legacyText; + } } } if (dataTransfer.types) { for (var i = 0; i < dataTransfer.types.length; i++) { var contentType = dataTransfer.types[i]; - data[contentType] = dataTransfer.getData(contentType); + items[contentType] = dataTransfer.getData(contentType); } } } - return data; + return items; } /** @@ -486,43 +661,91 @@ define("tinymce/pasteplugin/Clipboard", [ return getDataTransferItems(clipboardEvent.clipboardData || editor.getDoc().dataTransfer); } + function hasHtmlOrText(content) { + return hasContentType(content, 'text/html') || hasContentType(content, 'text/plain'); + } + + function getBase64FromUri(uri) { + var idx; + + idx = uri.indexOf(','); + if (idx !== -1) { + return uri.substr(idx + 1); + } + + return null; + } + + function isValidDataUriImage(settings, imgElm) { + return settings.images_dataimg_filter ? settings.images_dataimg_filter(imgElm) : true; + } + + function pasteImage(rng, reader, blob) { + if (rng) { + editor.selection.setRng(rng); + rng = null; + } + + var dataUri = reader.result; + var base64 = getBase64FromUri(dataUri); + + var img = new Image(); + img.src = dataUri; + + // TODO: Move the bulk of the cache logic to EditorUpload + if (isValidDataUriImage(editor.settings, img)) { + var blobCache = editor.editorUpload.blobCache; + var blobInfo, existingBlobInfo; + + existingBlobInfo = blobCache.findFirst(function(cachedBlobInfo) { + return cachedBlobInfo.base64() === base64; + }); + + if (!existingBlobInfo) { + blobInfo = blobCache.create(uniqueId(), blob, base64); + blobCache.add(blobInfo); + } else { + blobInfo = existingBlobInfo; + } + + pasteHtml(''); + } else { + pasteHtml(''); + } + } + /** * Checks if the clipboard contains image data if it does it will take that data * and convert it into a data url image and paste that image at the caret location. * * @param {ClipboardEvent} e Paste/drop event object. - * @param {DOMRange} rng Optional rng object to move selection to. + * @param {DOMRange} rng Rng object to move selection to. * @return {Boolean} true/false if the image data was found or not. */ function pasteImageData(e, rng) { var dataTransfer = e.clipboardData || e.dataTransfer; function processItems(items) { - var i, item, reader; - - function pasteImage() { - if (rng) { - editor.selection.setRng(rng); - rng = null; - } - - pasteHtml(''); - } + var i, item, reader, hadImage = false; if (items) { for (i = 0; i < items.length; i++) { item = items[i]; - if (/^image\/(jpeg|png|gif)$/.test(item.type)) { + if (/^image\/(jpeg|png|gif|bmp)$/.test(item.type)) { + var blob = item.getAsFile ? item.getAsFile() : item; + reader = new FileReader(); - reader.onload = pasteImage; - reader.readAsDataURL(item.getAsFile ? item.getAsFile() : item); + reader.onload = pasteImage.bind(null, rng, reader, blob); + reader.readAsDataURL(blob); e.preventDefault(); - return true; + hadImage = true; } } } + + return hadImage; } if (editor.settings.paste_data_images && dataTransfer) { @@ -531,40 +754,19 @@ define("tinymce/pasteplugin/Clipboard", [ } /** - * Chrome on Andoid doesn't support proper clipboard access so we have no choice but to allow the browser default behavior. + * Chrome on Android doesn't support proper clipboard access so we have no choice but to allow the browser default behavior. * * @param {Event} e Paste event object to check if it contains any data. * @return {Boolean} true/false if the clipboard is empty or not. */ - function isBrokenAndoidClipboardEvent(e) { + function isBrokenAndroidClipboardEvent(e) { var clipboardData = e.clipboardData; return navigator.userAgent.indexOf('Android') != -1 && clipboardData && clipboardData.items && clipboardData.items.length === 0; } function getCaretRangeFromEvent(e) { - var doc = editor.getDoc(), rng, point; - - if (doc.caretPositionFromPoint) { - point = doc.caretPositionFromPoint(e.clientX, e.clientY); - rng = doc.createRange(); - rng.setStart(point.offsetNode, point.offset); - rng.collapse(true); - } else if (doc.caretRangeFromPoint) { - rng = doc.caretRangeFromPoint(e.clientX, e.clientY); - } else if (doc.body.createTextRange) { - rng = doc.body.createTextRange(); - - try { - rng.moveToPoint(e.clientX, e.clientY); - rng.collapse(true); - } catch (ex) { - // Append to top or bottom depending on drop location - rng.collapse(e.clientY < doc.body.clientHeight); - } - } - - return rng; + return RangeUtils.getCaretRangeFromPoint(e.clientX, e.clientY, editor.getDoc()); } function hasContentType(clipboardContent, mimeType) { @@ -619,6 +821,69 @@ define("tinymce/pasteplugin/Clipboard", [ } }); + function insertClipboardContent(clipboardContent, isKeyBoardPaste, plainTextMode) { + var content; + + // Grab HTML from Clipboard API or paste bin as a fallback + if (hasContentType(clipboardContent, 'text/html')) { + content = clipboardContent['text/html']; + } else { + content = getPasteBinHtml(); + + // If paste bin is empty try using plain text mode + // since that is better than nothing right + if (content == pasteBinDefaultContent) { + plainTextMode = true; + } + } + + content = Utils.trimHtml(content); + + // WebKit has a nice bug where it clones the paste bin if you paste from for example notepad + // so we need to force plain text mode in this case + if (pasteBinElm && pasteBinElm.firstChild && pasteBinElm.firstChild.id === 'mcepastebin') { + plainTextMode = true; + } + + removePasteBin(); + + // If we got nothing from clipboard API and pastebin then we could try the last resort: plain/text + if (!content.length) { + plainTextMode = true; + } + + // Grab plain text from Clipboard API or convert existing HTML to plain text + if (plainTextMode) { + // Use plain text contents from Clipboard API unless the HTML contains paragraphs then + // we should convert the HTML to plain text since works better when pasting HTML/Word contents as plain text + if (hasContentType(clipboardContent, 'text/plain') && content.indexOf('

') == -1) { + content = clipboardContent['text/plain']; + } else { + content = Utils.innerText(content); + } + } + + // If the content is the paste bin default HTML then it was + // impossible to get the cliboard data out. + if (content == pasteBinDefaultContent) { + if (!isKeyBoardPaste) { + editor.windowManager.alert('Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents.'); + } + + return; + } + + if (plainTextMode) { + pasteText(content); + } else { + pasteHtml(content); + } + } + + var getLastRng = function() { + return lastRng || editor.selection.getRng(); + }; + editor.on('paste', function(e) { // Getting content from the Clipboard can take some time var clipboardTimer = new Date().getTime(); @@ -630,12 +895,12 @@ define("tinymce/pasteplugin/Clipboard", [ keyboardPastePlainTextState = false; - if (e.isDefaultPrevented() || isBrokenAndoidClipboardEvent(e)) { + if (e.isDefaultPrevented() || isBrokenAndroidClipboardEvent(e)) { removePasteBin(); return; } - if (pasteImageData(e)) { + if (!hasHtmlOrText(clipboardContent) && pasteImageData(e, getLastRng())) { removePasteBin(); return; } @@ -657,143 +922,118 @@ define("tinymce/pasteplugin/Clipboard", [ clipboardContent["text/html"] = getPasteBinHtml(); } - setTimeout(function() { - var content; - - // Grab HTML from Clipboard API or paste bin as a fallback - if (hasContentType(clipboardContent, 'text/html')) { - content = clipboardContent['text/html']; - } else { - content = getPasteBinHtml(); - - // If paste bin is empty try using plain text mode - // since that is better than nothing right - if (content == pasteBinDefaultContent) { - plainTextMode = true; - } - } - - content = Utils.trimHtml(content); - - // WebKit has a nice bug where it clones the paste bin if you paste from for example notepad - // so we need to force plain text mode in this case - if (pasteBinElm && pasteBinElm.firstChild && pasteBinElm.firstChild.id === 'mcepastebin') { - plainTextMode = true; - } - - removePasteBin(); - - // If we got nothing from clipboard API and pastebin then we could try the last resort: plain/text - if (!content.length) { - plainTextMode = true; - } - - // Grab plain text from Clipboard API or convert existing HTML to plain text - if (plainTextMode) { - // Use plain text contents from Clipboard API unless the HTML contains paragraphs then - // we should convert the HTML to plain text since works better when pasting HTML/Word contents as plain text - if (hasContentType(clipboardContent, 'text/plain') && content.indexOf('

') == -1) { - content = clipboardContent['text/plain']; - } else { - content = Utils.innerText(content); - } - } - - // If the content is the paste bin default HTML then it was - // impossible to get the cliboard data out. - if (content == pasteBinDefaultContent) { - if (!isKeyBoardPaste) { - editor.windowManager.alert('Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents.'); - } - - return; - } - - if (plainTextMode) { - pasteText(content); - } else { - pasteHtml(content); - } - }, 0); + // If clipboard API has HTML then use that directly + if (hasContentType(clipboardContent, 'text/html')) { + e.preventDefault(); + insertClipboardContent(clipboardContent, isKeyBoardPaste, plainTextMode); + } else { + Delay.setEditorTimeout(editor, function() { + insertClipboardContent(clipboardContent, isKeyBoardPaste, plainTextMode); + }, 0); + } }); editor.on('dragstart dragend', function(e) { draggingInternally = e.type == 'dragstart'; }); + function isPlainTextFileUrl(content) { + return content['text/plain'].indexOf('file://') === 0; + } + editor.on('drop', function(e) { - var rng = getCaretRangeFromEvent(e); + var dropContent, rng; + + rng = getCaretRangeFromEvent(e); if (e.isDefaultPrevented() || draggingInternally) { return; } - if (pasteImageData(e, rng)) { + dropContent = getDataTransferItems(e.dataTransfer); + + if ((!hasHtmlOrText(dropContent) || isPlainTextFileUrl(dropContent)) && pasteImageData(e, rng)) { return; } if (rng && editor.settings.paste_filter_drop !== false) { - var dropContent = getDataTransferItems(e.dataTransfer); var content = dropContent['mce-internal'] || dropContent['text/html'] || dropContent['text/plain']; if (content) { e.preventDefault(); - editor.undoManager.transact(function() { - if (dropContent['mce-internal']) { - editor.execCommand('Delete'); - } + // FF 45 doesn't paint a caret when dragging in text in due to focus call by execCommand + Delay.setEditorTimeout(editor, function() { + editor.undoManager.transact(function() { + if (dropContent['mce-internal']) { + editor.execCommand('Delete'); + } - editor.selection.setRng(rng); + editor.selection.setRng(rng); - content = Utils.trimHtml(content); + content = Utils.trimHtml(content); - if (!dropContent['text/html']) { - pasteText(content); - } else { - pasteHtml(content); - } + if (!dropContent['text/html']) { + pasteText(content); + } else { + pasteHtml(content); + } + }); }); } } }); editor.on('dragover dragend', function(e) { - var i, dataTransfer = e.dataTransfer; - - if (editor.settings.paste_data_images && dataTransfer) { - for (i = 0; i < dataTransfer.types.length; i++) { - // Prevent default if we have files dragged into the editor since the pasteImageData handles that - if (dataTransfer.types[i] == "Files") { - e.preventDefault(); - return false; - } - } + if (editor.settings.paste_data_images) { + e.preventDefault(); } }); } self.pasteHtml = pasteHtml; self.pasteText = pasteText; + self.pasteImageData = pasteImageData; editor.on('preInit', function() { registerEventHandlers(); // Remove all data images from paste for example from Gecko // except internal images like video elements - editor.parser.addNodeFilter('img', function(nodes) { - if (!editor.settings.paste_data_images) { + editor.parser.addNodeFilter('img', function(nodes, name, args) { + function isPasteInsert(args) { + return args.data && args.data.paste === true; + } + + function remove(node) { + if (!node.attr('data-mce-object') && src !== Env.transparentSrc) { + node.remove(); + } + } + + function isWebKitFakeUrl(src) { + return src.indexOf("webkit-fake-url") === 0; + } + + function isDataUri(src) { + return src.indexOf("data:") === 0; + } + + if (!editor.settings.paste_data_images && isPasteInsert(args)) { var i = nodes.length; while (i--) { var src = nodes[i].attributes.map.src; - // Some browsers automatically produce data uris on paste + if (!src) { + continue; + } + // Safari on Mac produces webkit-fake-url see: https://bugs.webkit.org/show_bug.cgi?id=49141 - if (src && /^(data:image|webkit\-fake\-url)/.test(src)) { - if (!nodes[i].attr('data-mce-object') && src !== Env.transparentSrc) { - nodes[i].remove(); - } + if (isWebKitFakeUrl(src)) { + remove(nodes[i]); + } else if (!editor.settings.allow_html_data_urls && isDataUri(src)) { + remove(nodes[i]); } } } @@ -807,8 +1047,8 @@ define("tinymce/pasteplugin/Clipboard", [ /** * WordFilter.js * - * Copyright, Moxiecode Systems AB * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing @@ -817,7 +1057,7 @@ define("tinymce/pasteplugin/Clipboard", [ /** * This class parses word HTML into proper TinyMCE markup. * - * @class tinymce.pasteplugin.Quirks + * @class tinymce.pasteplugin.WordFilter * @private */ define("tinymce/pasteplugin/WordFilter", [ @@ -868,7 +1108,7 @@ define("tinymce/pasteplugin/WordFilter", [ } function isBulletList(text) { - return /^[\s\u00a0]*[\u2022\u00b7\u00a7\u00d8\u25CF]\s*/.test(text); + return /^[\s\u00a0]*[\u2022\u00b7\u00a7\u25CF]\s*/.test(text); } function WordFilter(editor) { @@ -877,6 +1117,10 @@ define("tinymce/pasteplugin/WordFilter", [ editor.on('BeforePastePreProcess', function(e) { var content = e.content, retainStyleProperties, validStyles; + // Remove google docs internal guid markers + content = content.replace(/]+id="?docs-internal-[^>]*>/gi, ''); + content = content.replace(/
/gi, ''); + retainStyleProperties = settings.paste_retain_style_properties; if (retainStyleProperties) { validStyles = Tools.makeMap(retainStyleProperties.split(/[, ]/)); @@ -980,14 +1224,26 @@ define("tinymce/pasteplugin/WordFilter", [ // Remove start of list item "1. " or "· " etc removeIgnoredNodes(paragraphNode); trimListStart(paragraphNode, /^\u00a0+/); - trimListStart(paragraphNode, /^\s*([\u2022\u00b7\u00a7\u00d8\u25CF]|\w+\.)/); + trimListStart(paragraphNode, /^\s*([\u2022\u00b7\u00a7\u25CF]|\w+\.)/); trimListStart(paragraphNode, /^\u00a0+/); } - var paragraphs = node.getAll('p'); + // Build a list of all root level elements before we start + // altering them in the loop below. + var elements = [], child = node.firstChild; + while (typeof child !== 'undefined' && child !== null) { + elements.push(child); + + child = child.walk(); + if (child !== null) { + while (typeof child !== 'undefined' && child.parent !== node) { + child = child.walk(); + } + } + } - for (var i = 0; i < paragraphs.length; i++) { - node = paragraphs[i]; + for (var i = 0; i < elements.length; i++) { + node = elements[i]; if (node.name == 'p' && node.firstChild) { // Find first text node in paragraph @@ -1002,7 +1258,7 @@ define("tinymce/pasteplugin/WordFilter", [ // Detect ordered lists 1., a. or ixv. if (isNumericList(nodeText)) { // Parse OL start number - var matches = /([0-9])\./.exec(nodeText); + var matches = /([0-9]+)\./.exec(nodeText); var start = 1; if (matches) { start = parseInt(matches[1], 10); @@ -1018,6 +1274,13 @@ define("tinymce/pasteplugin/WordFilter", [ continue; } + currentListNode = null; + } else { + // If the root level element isn't a p tag which can be + // processed by convertParagraphToLi, it interrupts the + // lists, causing a new list to start instead of having + // elements from the next list inserted above this tag. + prevListNode = currentListNode; currentListNode = null; } } @@ -1152,8 +1415,11 @@ define("tinymce/pasteplugin/WordFilter", [ var validElements = settings.paste_word_valid_elements; if (!validElements) { - validElements = '-strong/b,-em/i,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-p/div,' + - '-table[width],-tr,-td[colspan|rowspan|width],-th,-thead,-tfoot,-tbody,-a[href|name],sub,sup,strike,br,del'; + validElements = ( + '-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,' + + '-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,' + + 'td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody' + ); } // Setup strict schema @@ -1165,6 +1431,7 @@ define("tinymce/pasteplugin/WordFilter", [ // Add style/class attribute to all element rules since the user might have removed them from // paste_word_valid_elements config option and we need to check them for properties Tools.each(schema.elements, function(rule) { + /*eslint dot-notation:0*/ if (!rule.attributes["class"]) { rule.attributes["class"] = {}; rule.attributesOrder.push("class"); @@ -1202,7 +1469,7 @@ define("tinymce/pasteplugin/WordFilter", [ node = nodes[i]; className = node.attr('class'); - if (/^(MsoCommentReference|MsoCommentText|msoDel|MsoCaption)$/i.test(className)) { + if (/^(MsoCommentReference|MsoCommentText|msoDel)$/i.test(className)) { node.remove(); } @@ -1261,10 +1528,14 @@ define("tinymce/pasteplugin/WordFilter", [ var rootNode = domParser.parse(content); // Process DOM - convertFakeListsToProperLists(rootNode); + if (settings.paste_convert_word_fake_lists !== false) { + convertFakeListsToProperLists(rootNode); + } // Serialize DOM back to HTML - e.content = new Serializer({}, schema).serialize(rootNode); + e.content = new Serializer({ + validate: settings.validate + }, schema).serialize(rootNode); } }); } @@ -1279,8 +1550,8 @@ define("tinymce/pasteplugin/WordFilter", [ /** * Quirks.js * - * Copyright, Moxiecode Systems AB * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing @@ -1441,8 +1712,8 @@ define("tinymce/pasteplugin/Quirks", [ /** * Plugin.js * - * Copyright, Moxiecode Systems AB * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing @@ -1465,23 +1736,34 @@ define("tinymce/pasteplugin/Plugin", [ PluginManager.add('paste', function(editor) { var self = this, clipboard, settings = editor.settings; + function isUserInformedAboutPlainText() { + return userIsInformed || editor.settings.paste_plaintext_inform === false; + } + function togglePlainTextPaste() { if (clipboard.pasteFormat == "text") { this.active(false); clipboard.pasteFormat = "html"; + editor.fire('PastePlainTextToggle', {state: false}); } else { clipboard.pasteFormat = "text"; this.active(true); - if (!userIsInformed) { - editor.windowManager.alert( - 'Paste is now in plain text mode. Contents will now ' + - 'be pasted as plain text until you toggle this option off.' - ); + if (!isUserInformedAboutPlainText()) { + var message = editor.translate('Paste is now in plain text mode. Contents will now ' + + 'be pasted as plain text until you toggle this option off.'); + + editor.notificationManager.open({ + text: message, + type: 'info' + }); userIsInformed = true; + editor.fire('PastePlainTextToggle', {state: true}); } } + + editor.focus(); } self.clipboard = clipboard = new Clipboard(editor); @@ -1515,7 +1797,7 @@ define("tinymce/pasteplugin/Plugin", [ }); // Block all drag/drop events - if (editor.paste_block_drop) { + if (editor.settings.paste_block_drop) { editor.on('dragend dragover draggesture dragdrop drop drag', function(e) { e.preventDefault(); e.stopPropagation(); @@ -1549,5 +1831,5 @@ define("tinymce/pasteplugin/Plugin", [ }); }); -expose(["tinymce/pasteplugin/Utils","tinymce/pasteplugin/WordFilter"]); +expose(["tinymce/pasteplugin/Utils"]); })(this); \ No newline at end of file