1 /** |
1 (function () { |
2 * Compiled inline version. (Library mode) |
2 var paste = (function () { |
3 */ |
3 'use strict'; |
4 |
4 |
5 /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */ |
5 var Cell = function (initial) { |
6 /*globals $code */ |
6 var value = initial; |
7 |
7 var get = function () { |
8 (function(exports, undefined) { |
8 return value; |
9 "use strict"; |
9 }; |
10 |
10 var set = function (v) { |
11 var modules = {}; |
11 value = v; |
12 |
12 }; |
13 function require(ids, callback) { |
13 var clone = function () { |
14 var module, defs = []; |
14 return Cell(get()); |
15 |
15 }; |
16 for (var i = 0; i < ids.length; ++i) { |
16 return { |
17 module = modules[ids[i]] || resolve(ids[i]); |
17 get: get, |
18 if (!module) { |
18 set: set, |
19 throw 'module definition dependecy not found: ' + ids[i]; |
19 clone: clone |
20 } |
20 }; |
21 |
21 }; |
22 defs.push(module); |
22 |
23 } |
23 var global = tinymce.util.Tools.resolve('tinymce.PluginManager'); |
24 |
24 |
25 callback.apply(null, defs); |
25 var hasProPlugin = function (editor) { |
26 } |
26 if (/(^|[ ,])powerpaste([, ]|$)/.test(editor.settings.plugins) && global.get('powerpaste')) { |
27 |
27 if (typeof window.console !== 'undefined' && window.console.log) { |
28 function define(id, dependencies, definition) { |
28 window.console.log('PowerPaste is incompatible with Paste plugin! Remove \'paste\' from the \'plugins\' option.'); |
29 if (typeof id !== 'string') { |
29 } |
30 throw 'invalid module definition, module id must be defined and be a string'; |
30 return true; |
31 } |
31 } else { |
32 |
32 return false; |
33 if (dependencies === undefined) { |
33 } |
34 throw 'invalid module definition, dependencies must be specified'; |
34 }; |
35 } |
35 var $_15bf6siejjgwect1 = { hasProPlugin: hasProPlugin }; |
36 |
36 |
37 if (definition === undefined) { |
37 var get = function (clipboard, quirks) { |
38 throw 'invalid module definition, definition function must be specified'; |
38 return { |
39 } |
39 clipboard: clipboard, |
40 |
40 quirks: quirks |
41 require(dependencies, function() { |
41 }; |
42 modules[id] = definition.apply(null, arguments); |
42 }; |
43 }); |
43 var $_6gtliyigjjgwecte = { get: get }; |
44 } |
44 |
45 |
45 var firePastePreProcess = function (editor, html, internal, isWordHtml) { |
46 function defined(id) { |
46 return editor.fire('PastePreProcess', { |
47 return !!modules[id]; |
47 content: html, |
48 } |
48 internal: internal, |
49 |
49 wordContent: isWordHtml |
50 function resolve(id) { |
50 }); |
51 var target = exports; |
51 }; |
52 var fragments = id.split(/[.\/]/); |
52 var firePastePostProcess = function (editor, node, internal, isWordHtml) { |
53 |
53 return editor.fire('PastePostProcess', { |
54 for (var fi = 0; fi < fragments.length; ++fi) { |
54 node: node, |
55 if (!target[fragments[fi]]) { |
55 internal: internal, |
56 return; |
56 wordContent: isWordHtml |
57 } |
57 }); |
58 |
58 }; |
59 target = target[fragments[fi]]; |
59 var firePastePlainTextToggle = function (editor, state) { |
60 } |
60 return editor.fire('PastePlainTextToggle', { state: state }); |
61 |
61 }; |
62 return target; |
62 var firePaste = function (editor, ieFake) { |
63 } |
63 return editor.fire('paste', { ieFake: ieFake }); |
64 |
64 }; |
65 function expose(ids) { |
65 var $_8tki3zijjjgwectj = { |
66 for (var i = 0; i < ids.length; i++) { |
66 firePastePreProcess: firePastePreProcess, |
67 var target = exports; |
67 firePastePostProcess: firePastePostProcess, |
68 var id = ids[i]; |
68 firePastePlainTextToggle: firePastePlainTextToggle, |
69 var fragments = id.split(/[.\/]/); |
69 firePaste: firePaste |
70 |
70 }; |
71 for (var fi = 0; fi < fragments.length - 1; ++fi) { |
71 |
72 if (target[fragments[fi]] === undefined) { |
72 var shouldPlainTextInform = function (editor) { |
73 target[fragments[fi]] = {}; |
73 return editor.getParam('paste_plaintext_inform', true); |
74 } |
74 }; |
75 |
75 var shouldBlockDrop = function (editor) { |
76 target = target[fragments[fi]]; |
76 return editor.getParam('paste_block_drop', false); |
77 } |
77 }; |
78 |
78 var shouldPasteDataImages = function (editor) { |
79 target[fragments[fragments.length - 1]] = modules[id]; |
79 return editor.getParam('paste_data_images', false); |
80 } |
80 }; |
81 } |
81 var shouldFilterDrop = function (editor) { |
82 |
82 return editor.getParam('paste_filter_drop', true); |
83 // Included from: js/tinymce/plugins/paste/classes/Utils.js |
83 }; |
84 |
84 var getPreProcess = function (editor) { |
85 /** |
85 return editor.getParam('paste_preprocess'); |
86 * Utils.js |
86 }; |
87 * |
87 var getPostProcess = function (editor) { |
88 * Copyright, Moxiecode Systems AB |
88 return editor.getParam('paste_postprocess'); |
89 * Released under LGPL License. |
89 }; |
90 * |
90 var getWebkitStyles = function (editor) { |
91 * License: http://www.tinymce.com/license |
91 return editor.getParam('paste_webkit_styles'); |
92 * Contributing: http://www.tinymce.com/contributing |
92 }; |
93 */ |
93 var shouldRemoveWebKitStyles = function (editor) { |
94 |
94 return editor.getParam('paste_remove_styles_if_webkit', true); |
95 /** |
95 }; |
96 * This class contails various utility functions for the paste plugin. |
96 var shouldMergeFormats = function (editor) { |
97 * |
97 return editor.getParam('paste_merge_formats', true); |
98 * @class tinymce.pasteplugin.Utils |
98 }; |
99 */ |
99 var isSmartPasteEnabled = function (editor) { |
100 define("tinymce/pasteplugin/Utils", [ |
100 return editor.getParam('smart_paste', true); |
101 "tinymce/util/Tools", |
101 }; |
102 "tinymce/html/DomParser", |
102 var isPasteAsTextEnabled = function (editor) { |
103 "tinymce/html/Schema" |
103 return editor.getParam('paste_as_text', false); |
104 ], function(Tools, DomParser, Schema) { |
104 }; |
105 function filter(content, items) { |
105 var getRetainStyleProps = function (editor) { |
106 Tools.each(items, function(v) { |
106 return editor.getParam('paste_retain_style_properties'); |
107 if (v.constructor == RegExp) { |
107 }; |
108 content = content.replace(v, ''); |
108 var getWordValidElements = function (editor) { |
109 } else { |
109 var defaultValidElements = '-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'; |
110 content = content.replace(v[0], v[1]); |
110 return editor.getParam('paste_word_valid_elements', defaultValidElements); |
111 } |
111 }; |
112 }); |
112 var shouldConvertWordFakeLists = function (editor) { |
113 |
113 return editor.getParam('paste_convert_word_fake_lists', true); |
114 return content; |
114 }; |
115 } |
115 var shouldUseDefaultFilters = function (editor) { |
116 |
116 return editor.getParam('paste_enable_default_filters', true); |
117 /** |
117 }; |
118 * Gets the innerText of the specified element. It will handle edge cases |
118 var $_xr8b0ikjjgwectl = { |
119 * and works better than textContent on Gecko. |
119 shouldPlainTextInform: shouldPlainTextInform, |
120 * |
120 shouldBlockDrop: shouldBlockDrop, |
121 * @param {String} html HTML string to get text from. |
121 shouldPasteDataImages: shouldPasteDataImages, |
122 * @return {String} String of text with line feeds. |
122 shouldFilterDrop: shouldFilterDrop, |
123 */ |
123 getPreProcess: getPreProcess, |
124 function innerText(html) { |
124 getPostProcess: getPostProcess, |
125 var schema = new Schema(), domParser = new DomParser({}, schema), text = ''; |
125 getWebkitStyles: getWebkitStyles, |
126 var shortEndedElements = schema.getShortEndedElements(); |
126 shouldRemoveWebKitStyles: shouldRemoveWebKitStyles, |
127 var ignoreElements = Tools.makeMap('script noscript style textarea video audio iframe object', ' '); |
127 shouldMergeFormats: shouldMergeFormats, |
128 var blockElements = schema.getBlockElements(); |
128 isSmartPasteEnabled: isSmartPasteEnabled, |
129 |
129 isPasteAsTextEnabled: isPasteAsTextEnabled, |
130 function walk(node) { |
130 getRetainStyleProps: getRetainStyleProps, |
131 var name = node.name, currentNode = node; |
131 getWordValidElements: getWordValidElements, |
132 |
132 shouldConvertWordFakeLists: shouldConvertWordFakeLists, |
133 if (name === 'br') { |
133 shouldUseDefaultFilters: shouldUseDefaultFilters |
134 text += '\n'; |
134 }; |
135 return; |
135 |
136 } |
136 var shouldInformUserAboutPlainText = function (editor, userIsInformedState) { |
137 |
137 return userIsInformedState.get() === false && $_xr8b0ikjjgwectl.shouldPlainTextInform(editor); |
138 // img/input/hr |
138 }; |
139 if (shortEndedElements[name]) { |
139 var displayNotification = function (editor, message) { |
140 text += ' '; |
140 editor.notificationManager.open({ |
141 } |
141 text: editor.translate(message), |
142 |
142 type: 'info' |
143 // Ingore script, video contents |
143 }); |
144 if (ignoreElements[name]) { |
144 }; |
145 text += ' '; |
145 var togglePlainTextPaste = function (editor, clipboard, userIsInformedState) { |
146 return; |
146 if (clipboard.pasteFormat.get() === 'text') { |
147 } |
147 clipboard.pasteFormat.set('html'); |
148 |
148 $_8tki3zijjjgwectj.firePastePlainTextToggle(editor, false); |
149 if (node.type == 3) { |
149 } else { |
150 text += node.value; |
150 clipboard.pasteFormat.set('text'); |
151 } |
151 $_8tki3zijjjgwectj.firePastePlainTextToggle(editor, true); |
152 |
152 if (shouldInformUserAboutPlainText(editor, userIsInformedState)) { |
153 // Walk all children |
153 displayNotification(editor, 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.'); |
154 if (!node.shortEnded) { |
154 userIsInformedState.set(true); |
155 if ((node = node.firstChild)) { |
155 } |
156 do { |
156 } |
157 walk(node); |
157 editor.focus(); |
158 } while ((node = node.next)); |
158 }; |
159 } |
159 var $_2j7vw7iijjgwecti = { togglePlainTextPaste: togglePlainTextPaste }; |
160 } |
160 |
161 |
161 var register = function (editor, clipboard, userIsInformedState) { |
162 // Add \n or \n\n for blocks or P |
162 editor.addCommand('mceTogglePlainTextPaste', function () { |
163 if (blockElements[name] && currentNode.next) { |
163 $_2j7vw7iijjgwecti.togglePlainTextPaste(editor, clipboard, userIsInformedState); |
164 text += '\n'; |
164 }); |
165 |
165 editor.addCommand('mceInsertClipboardContent', function (ui, value) { |
166 if (name == 'p') { |
166 if (value.content) { |
167 text += '\n'; |
167 clipboard.pasteHtml(value.content, value.internal); |
168 } |
168 } |
169 } |
169 if (value.text) { |
170 } |
170 clipboard.pasteText(value.text); |
171 |
171 } |
172 html = filter(html, [ |
172 }); |
173 /<!\[[^\]]+\]>/g // Conditional comments |
173 }; |
174 ]); |
174 var $_fldd1mihjjgwecth = { register: register }; |
175 |
175 |
176 walk(domParser.parse(html)); |
176 var global$1 = tinymce.util.Tools.resolve('tinymce.Env'); |
177 |
177 |
178 return text; |
178 var global$2 = tinymce.util.Tools.resolve('tinymce.util.Delay'); |
179 } |
179 |
180 |
180 var global$3 = tinymce.util.Tools.resolve('tinymce.util.Tools'); |
181 /** |
181 |
182 * Trims the specified HTML by removing all WebKit fragments, all elements wrapping the body trailing BR elements etc. |
182 var global$4 = tinymce.util.Tools.resolve('tinymce.util.VK'); |
183 * |
183 |
184 * @param {String} html Html string to trim contents on. |
184 var internalMimeType = 'x-tinymce/html'; |
185 * @return {String} Html contents that got trimmed. |
185 var internalMark = '<!-- ' + internalMimeType + ' -->'; |
186 */ |
186 var mark = function (html) { |
187 function trimHtml(html) { |
187 return internalMark + html; |
188 function trimSpaces(all, s1, s2) { |
188 }; |
189 // WebKit meant to preserve multiple spaces but instead inserted around all inline tags, |
189 var unmark = function (html) { |
190 // including the spans with inline styles created on paste |
190 return html.replace(internalMark, ''); |
191 if (!s1 && !s2) { |
191 }; |
192 return ' '; |
192 var isMarked = function (html) { |
193 } |
193 return html.indexOf(internalMark) !== -1; |
194 |
194 }; |
195 return '\u00a0'; |
195 var $_4x13hjirjjgwecu1 = { |
196 } |
196 mark: mark, |
197 |
197 unmark: unmark, |
198 html = filter(html, [ |
198 isMarked: isMarked, |
199 /^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/g, // Remove anything but the contents within the BODY element |
199 internalHtmlMime: function () { |
200 /<!--StartFragment-->|<!--EndFragment-->/g, // Inner fragments (tables from excel on mac) |
200 return internalMimeType; |
201 [/( ?)<span class="Apple-converted-space">\u00a0<\/span>( ?)/g, trimSpaces], |
201 } |
202 /<br>$/i // Trailing BR elements |
202 }; |
203 ]); |
203 |
204 |
204 var global$5 = tinymce.util.Tools.resolve('tinymce.html.Entities'); |
205 return html; |
205 |
206 } |
206 var isPlainText = function (text) { |
207 |
207 return !/<(?:\/?(?!(?:div|p|br|span)>)\w+|(?:(?!(?:span style="white-space:\s?pre;?">)|br\s?\/>))\w+\s[^>]+)>/i.test(text); |
208 return { |
208 }; |
209 filter: filter, |
209 var toBRs = function (text) { |
210 innerText: innerText, |
210 return text.replace(/\r?\n/g, '<br>'); |
211 trimHtml: trimHtml |
211 }; |
212 }; |
212 var openContainer = function (rootTag, rootAttrs) { |
213 }); |
213 var key; |
214 |
214 var attrs = []; |
215 // Included from: js/tinymce/plugins/paste/classes/Clipboard.js |
215 var tag = '<' + rootTag; |
216 |
216 if (typeof rootAttrs === 'object') { |
217 /** |
217 for (key in rootAttrs) { |
218 * Clipboard.js |
218 if (rootAttrs.hasOwnProperty(key)) { |
219 * |
219 attrs.push(key + '="' + global$5.encodeAllRaw(rootAttrs[key]) + '"'); |
220 * Copyright, Moxiecode Systems AB |
220 } |
221 * Released under LGPL License. |
221 } |
222 * |
222 if (attrs.length) { |
223 * License: http://www.tinymce.com/license |
223 tag += ' ' + attrs.join(' '); |
224 * Contributing: http://www.tinymce.com/contributing |
224 } |
225 */ |
225 } |
226 |
226 return tag + '>'; |
227 /** |
227 }; |
228 * This class contains logic for getting HTML contents out of the clipboard. |
228 var toBlockElements = function (text, rootTag, rootAttrs) { |
229 * |
229 var blocks = text.split(/\n\n/); |
230 * We need to make a lot of ugly hacks to get the contents out of the clipboard since |
230 var tagOpen = openContainer(rootTag, rootAttrs); |
231 * the W3C Clipboard API is broken in all browsers that have it: Gecko/WebKit/Blink. |
231 var tagClose = '</' + rootTag + '>'; |
232 * We might rewrite this the way those API:s stabilize. Browsers doesn't handle pasting |
232 var paragraphs = global$3.map(blocks, function (p) { |
233 * from applications like Word the same way as it does when pasting into a contentEditable area |
233 return p.split(/\n/).join('<br />'); |
234 * so we need to do lots of extra work to try to get to this clipboard data. |
234 }); |
235 * |
235 var stitch = function (p) { |
236 * Current implementation steps: |
236 return tagOpen + p + tagClose; |
237 * 1. On keydown with paste keys Ctrl+V or Shift+Insert create |
237 }; |
238 * a paste bin element and move focus to that element. |
238 return paragraphs.length === 1 ? paragraphs[0] : global$3.map(paragraphs, stitch).join(''); |
239 * 2. Wait for the browser to fire a "paste" event and get the contents out of the paste bin. |
239 }; |
240 * 3. Check if the paste was successful if true, process the HTML. |
240 var convert = function (text, rootTag, rootAttrs) { |
241 * (4). If the paste was unsuccessful use IE execCommand, Clipboard API, document.dataTransfer old WebKit API etc. |
241 return rootTag ? toBlockElements(text, rootTag, rootAttrs) : toBRs(text); |
242 * |
242 }; |
243 * @class tinymce.pasteplugin.Clipboard |
243 var $_4h3hnrisjjgwecu2 = { |
244 * @private |
244 isPlainText: isPlainText, |
245 */ |
245 convert: convert, |
246 define("tinymce/pasteplugin/Clipboard", [ |
246 toBRs: toBRs, |
247 "tinymce/Env", |
247 toBlockElements: toBlockElements |
248 "tinymce/dom/RangeUtils", |
248 }; |
249 "tinymce/util/VK", |
249 |
250 "tinymce/pasteplugin/Utils" |
250 var global$6 = tinymce.util.Tools.resolve('tinymce.html.DomParser'); |
251 ], function(Env, RangeUtils, VK, Utils) { |
251 |
252 return function(editor) { |
252 var global$7 = tinymce.util.Tools.resolve('tinymce.html.Node'); |
253 var self = this, pasteBinElm, lastRng, keyboardPasteTimeStamp = 0, draggingInternally = false; |
253 |
254 var pasteBinDefaultContent = '%MCEPASTEBIN%', keyboardPastePlainTextState; |
254 var global$8 = tinymce.util.Tools.resolve('tinymce.html.Schema'); |
255 var mceInternalUrlPrefix = 'data:text/mce-internal,'; |
255 |
256 |
256 var global$9 = tinymce.util.Tools.resolve('tinymce.html.Serializer'); |
257 /** |
257 |
258 * Pastes the specified HTML. This means that the HTML is filtered and then |
258 function filter(content, items) { |
259 * inserted at the current selection in the editor. It will also fire paste events |
259 global$3.each(items, function (v) { |
260 * for custom user filtering. |
260 if (v.constructor === RegExp) { |
261 * |
261 content = content.replace(v, ''); |
262 * @param {String} html HTML code to paste into the current selection. |
262 } else { |
263 */ |
263 content = content.replace(v[0], v[1]); |
264 function pasteHtml(html) { |
264 } |
265 var args, dom = editor.dom; |
265 }); |
266 |
266 return content; |
267 args = editor.fire('BeforePastePreProcess', {content: html}); // Internal event used by Quirks |
267 } |
268 args = editor.fire('PastePreProcess', args); |
268 function innerText(html) { |
269 html = args.content; |
269 var schema = global$8(); |
270 |
270 var domParser = global$6({}, schema); |
271 if (!args.isDefaultPrevented()) { |
271 var text = ''; |
272 // User has bound PastePostProcess events then we need to pass it through a DOM node |
272 var shortEndedElements = schema.getShortEndedElements(); |
273 // This is not ideal but we don't want to let the browser mess up the HTML for example |
273 var ignoreElements = global$3.makeMap('script noscript style textarea video audio iframe object', ' '); |
274 // some browsers add to P tags etc |
274 var blockElements = schema.getBlockElements(); |
275 if (editor.hasEventListeners('PastePostProcess') && !args.isDefaultPrevented()) { |
275 function walk(node) { |
276 // We need to attach the element to the DOM so Sizzle selectors work on the contents |
276 var name$$1 = node.name, currentNode = node; |
277 var tempBody = dom.add(editor.getBody(), 'div', {style: 'display:none'}, html); |
277 if (name$$1 === 'br') { |
278 args = editor.fire('PastePostProcess', {node: tempBody}); |
278 text += '\n'; |
279 dom.remove(tempBody); |
279 return; |
280 html = args.node.innerHTML; |
280 } |
281 } |
281 if (shortEndedElements[name$$1]) { |
282 |
282 text += ' '; |
283 if (!args.isDefaultPrevented()) { |
283 } |
284 editor.insertContent(html, {merge: editor.settings.paste_merge_formats !== false}); |
284 if (ignoreElements[name$$1]) { |
285 } |
285 text += ' '; |
286 } |
286 return; |
287 } |
287 } |
288 |
288 if (node.type === 3) { |
289 /** |
289 text += node.value; |
290 * Pastes the specified text. This means that the plain text is processed |
290 } |
291 * and converted into BR and P elements. It will fire paste events for custom filtering. |
291 if (!node.shortEnded) { |
292 * |
292 if (node = node.firstChild) { |
293 * @param {String} text Text to paste as the current selection location. |
293 do { |
294 */ |
294 walk(node); |
295 function pasteText(text) { |
295 } while (node = node.next); |
296 text = editor.dom.encode(text).replace(/\r\n/g, '\n'); |
296 } |
297 |
297 } |
298 var startBlock = editor.dom.getParent(editor.selection.getStart(), editor.dom.isBlock); |
298 if (blockElements[name$$1] && currentNode.next) { |
299 |
299 text += '\n'; |
300 // Create start block html for example <p attr="value"> |
300 if (name$$1 === 'p') { |
301 var forcedRootBlockName = editor.settings.forced_root_block; |
301 text += '\n'; |
302 var forcedRootBlockStartHtml; |
302 } |
303 if (forcedRootBlockName) { |
303 } |
304 forcedRootBlockStartHtml = editor.dom.createHTML(forcedRootBlockName, editor.settings.forced_root_block_attrs); |
304 } |
305 forcedRootBlockStartHtml = forcedRootBlockStartHtml.substr(0, forcedRootBlockStartHtml.length - 3) + '>'; |
305 html = filter(html, [/<!\[[^\]]+\]>/g]); |
306 } |
306 walk(domParser.parse(html)); |
307 |
307 return text; |
308 if ((startBlock && /^(PRE|DIV)$/.test(startBlock.nodeName)) || !forcedRootBlockName) { |
308 } |
309 text = Utils.filter(text, [ |
309 function trimHtml(html) { |
310 [/\n/g, "<br>"] |
310 function trimSpaces(all, s1, s2) { |
311 ]); |
311 if (!s1 && !s2) { |
312 } else { |
312 return ' '; |
313 text = Utils.filter(text, [ |
313 } |
314 [/\n\n/g, "</p>" + forcedRootBlockStartHtml], |
314 return '\xA0'; |
315 [/^(.*<\/p>)(<p>)$/, forcedRootBlockStartHtml + '$1'], |
315 } |
316 [/\n/g, "<br />"] |
316 html = filter(html, [ |
317 ]); |
317 /^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/ig, |
318 |
318 /<!--StartFragment-->|<!--EndFragment-->/g, |
319 if (text.indexOf('<p>') != -1) { |
319 [ |
320 text = forcedRootBlockStartHtml + text; |
320 /( ?)<span class="Apple-converted-space">\u00a0<\/span>( ?)/g, |
321 } |
321 trimSpaces |
322 } |
322 ], |
323 |
323 /<br class="Apple-interchange-newline">/g, |
324 pasteHtml(text); |
324 /<br>$/i |
325 } |
325 ]); |
326 |
326 return html; |
327 /** |
327 } |
328 * Creates a paste bin element as close as possible to the current caret location and places the focus inside that element |
328 function createIdGenerator(prefix) { |
329 * so that when the real paste event occurs the contents gets inserted into this element |
329 var count = 0; |
330 * instead of the current editor selection element. |
330 return function () { |
331 */ |
331 return prefix + count++; |
332 function createPasteBin() { |
332 }; |
333 var dom = editor.dom, body = editor.getBody(); |
333 } |
334 var viewport = editor.dom.getViewPort(editor.getWin()), scrollTop = viewport.y, top = 20; |
334 var isMsEdge = function () { |
335 var scrollContainer; |
335 return navigator.userAgent.indexOf(' Edge/') !== -1; |
336 |
336 }; |
337 lastRng = editor.selection.getRng(); |
337 var $_4bi2o9j0jjgwecui = { |
338 |
338 filter: filter, |
339 if (editor.inline) { |
339 innerText: innerText, |
340 scrollContainer = editor.selection.getScrollContainer(); |
340 trimHtml: trimHtml, |
341 |
341 createIdGenerator: createIdGenerator, |
342 // Can't always rely on scrollTop returning a useful value. |
342 isMsEdge: isMsEdge |
343 // It returns 0 if the browser doesn't support scrollTop for the element or is non-scrollable |
343 }; |
344 if (scrollContainer && scrollContainer.scrollTop > 0) { |
344 |
345 scrollTop = scrollContainer.scrollTop; |
345 function isWordContent(content) { |
346 } |
346 return /<font face="Times New Roman"|class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i.test(content) || /class="OutlineElement/.test(content) || /id="?docs\-internal\-guid\-/.test(content); |
347 } |
347 } |
348 |
348 function isNumericList(text) { |
349 /** |
349 var found, patterns; |
350 * Returns the rect of the current caret if the caret is in an empty block before a |
350 patterns = [ |
351 * BR we insert a temporary invisible character that we get the rect this way we always get a proper rect. |
351 /^[IVXLMCD]{1,2}\.[ \u00a0]/, |
352 * |
352 /^[ivxlmcd]{1,2}\.[ \u00a0]/, |
353 * TODO: This might be useful in core. |
353 /^[a-z]{1,2}[\.\)][ \u00a0]/, |
354 */ |
354 /^[A-Z]{1,2}[\.\)][ \u00a0]/, |
355 function getCaretRect(rng) { |
355 /^[0-9]+\.[ \u00a0]/, |
356 var rects, textNode, node, container = rng.startContainer; |
356 /^[\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d]+\.[ \u00a0]/, |
357 |
357 /^[\u58f1\u5f10\u53c2\u56db\u4f0d\u516d\u4e03\u516b\u4e5d\u62fe]+\.[ \u00a0]/ |
358 rects = rng.getClientRects(); |
358 ]; |
359 if (rects.length) { |
359 text = text.replace(/^[\u00a0 ]+/, ''); |
360 return rects[0]; |
360 global$3.each(patterns, function (pattern) { |
361 } |
361 if (pattern.test(text)) { |
362 |
362 found = true; |
363 if (!rng.collapsed || container.nodeType != 1) { |
363 return false; |
364 return; |
364 } |
365 } |
365 }); |
366 |
366 return found; |
367 node = container.childNodes[lastRng.startOffset]; |
367 } |
368 |
368 function isBulletList(text) { |
369 // Skip empty whitespace nodes |
369 return /^[\s\u00a0]*[\u2022\u00b7\u00a7\u25CF]\s*/.test(text); |
370 while (node && node.nodeType == 3 && !node.data.length) { |
370 } |
371 node = node.nextSibling; |
371 function convertFakeListsToProperLists(node) { |
372 } |
372 var currentListNode, prevListNode, lastLevel = 1; |
373 |
373 function getText(node) { |
374 if (!node) { |
374 var txt = ''; |
375 return; |
375 if (node.type === 3) { |
376 } |
376 return node.value; |
377 |
377 } |
378 // Check if the location is |<br> |
378 if (node = node.firstChild) { |
379 // TODO: Might need to expand this to say |<table> |
379 do { |
380 if (node.tagName == 'BR') { |
380 txt += getText(node); |
381 textNode = dom.doc.createTextNode('\uFEFF'); |
381 } while (node = node.next); |
382 node.parentNode.insertBefore(textNode, node); |
382 } |
383 |
383 return txt; |
384 rng = dom.createRng(); |
384 } |
385 rng.setStartBefore(textNode); |
385 function trimListStart(node, regExp) { |
386 rng.setEndAfter(textNode); |
386 if (node.type === 3) { |
387 |
387 if (regExp.test(node.value)) { |
388 rects = rng.getClientRects(); |
388 node.value = node.value.replace(regExp, ''); |
389 dom.remove(textNode); |
389 return false; |
390 } |
390 } |
391 |
391 } |
392 if (rects.length) { |
392 if (node = node.firstChild) { |
393 return rects[0]; |
393 do { |
394 } |
394 if (!trimListStart(node, regExp)) { |
395 } |
395 return false; |
396 |
396 } |
397 // Calculate top cordinate this is needed to avoid scrolling to top of document |
397 } while (node = node.next); |
398 // We want the paste bin to be as close to the caret as possible to avoid scrolling |
398 } |
399 if (lastRng.getClientRects) { |
399 return true; |
400 var rect = getCaretRect(lastRng); |
400 } |
401 |
401 function removeIgnoredNodes(node) { |
402 if (rect) { |
402 if (node._listIgnore) { |
403 // Client rects gets us closes to the actual |
403 node.remove(); |
404 // caret location in for example a wrapped paragraph block |
404 return; |
405 top = scrollTop + (rect.top - dom.getPos(body).y); |
405 } |
406 } else { |
406 if (node = node.firstChild) { |
407 top = scrollTop; |
407 do { |
408 |
408 removeIgnoredNodes(node); |
409 // Check if we can find a closer location by checking the range element |
409 } while (node = node.next); |
410 var container = lastRng.startContainer; |
410 } |
411 if (container) { |
411 } |
412 if (container.nodeType == 3 && container.parentNode != body) { |
412 function convertParagraphToLi(paragraphNode, listName, start) { |
413 container = container.parentNode; |
413 var level = paragraphNode._listLevel || lastLevel; |
414 } |
414 if (level !== lastLevel) { |
415 |
415 if (level < lastLevel) { |
416 if (container.nodeType == 1) { |
416 if (currentListNode) { |
417 top = dom.getPos(container, scrollContainer || body).y; |
417 currentListNode = currentListNode.parent.parent; |
418 } |
418 } |
419 } |
419 } else { |
420 } |
420 prevListNode = currentListNode; |
421 } |
421 currentListNode = null; |
422 |
422 } |
423 // Create a pastebin |
423 } |
424 pasteBinElm = dom.add(editor.getBody(), 'div', { |
424 if (!currentListNode || currentListNode.name !== listName) { |
425 id: "mcepastebin", |
425 prevListNode = prevListNode || currentListNode; |
426 contentEditable: true, |
426 currentListNode = new global$7(listName, 1); |
427 "data-mce-bogus": "all", |
427 if (start > 1) { |
428 style: 'position: absolute; top: ' + top + 'px;' + |
428 currentListNode.attr('start', '' + start); |
429 'width: 10px; height: 10px; overflow: hidden; opacity: 0' |
429 } |
430 }, pasteBinDefaultContent); |
430 paragraphNode.wrap(currentListNode); |
431 |
431 } else { |
432 // Move paste bin out of sight since the controlSelection rect gets displayed otherwise on IE and Gecko |
432 currentListNode.append(paragraphNode); |
433 if (Env.ie || Env.gecko) { |
433 } |
434 dom.setStyle(pasteBinElm, 'left', dom.getStyle(body, 'direction', true) == 'rtl' ? 0xFFFF : -0xFFFF); |
434 paragraphNode.name = 'li'; |
435 } |
435 if (level > lastLevel && prevListNode) { |
436 |
436 prevListNode.lastChild.append(currentListNode); |
437 // Prevent focus events from bubbeling fixed FocusManager issues |
437 } |
438 dom.bind(pasteBinElm, 'beforedeactivate focusin focusout', function(e) { |
438 lastLevel = level; |
439 e.stopPropagation(); |
439 removeIgnoredNodes(paragraphNode); |
440 }); |
440 trimListStart(paragraphNode, /^\u00a0+/); |
441 |
441 trimListStart(paragraphNode, /^\s*([\u2022\u00b7\u00a7\u25CF]|\w+\.)/); |
442 pasteBinElm.focus(); |
442 trimListStart(paragraphNode, /^\u00a0+/); |
443 editor.selection.select(pasteBinElm, true); |
443 } |
444 } |
444 var elements = []; |
445 |
445 var child = node.firstChild; |
446 /** |
446 while (typeof child !== 'undefined' && child !== null) { |
447 * Removes the paste bin if it exists. |
447 elements.push(child); |
448 */ |
448 child = child.walk(); |
449 function removePasteBin() { |
449 if (child !== null) { |
450 if (pasteBinElm) { |
450 while (typeof child !== 'undefined' && child.parent !== node) { |
451 var pasteBinClone; |
451 child = child.walk(); |
452 |
452 } |
453 // WebKit/Blink might clone the div so |
453 } |
454 // lets make sure we remove all clones |
454 } |
455 // TODO: Man o man is this ugly. WebKit is the new IE! Remove this if they ever fix it! |
455 for (var i = 0; i < elements.length; i++) { |
456 while ((pasteBinClone = editor.dom.get('mcepastebin'))) { |
456 node = elements[i]; |
457 editor.dom.remove(pasteBinClone); |
457 if (node.name === 'p' && node.firstChild) { |
458 editor.dom.unbind(pasteBinClone); |
458 var nodeText = getText(node); |
459 } |
459 if (isBulletList(nodeText)) { |
460 |
460 convertParagraphToLi(node, 'ul'); |
461 if (lastRng) { |
461 continue; |
462 editor.selection.setRng(lastRng); |
462 } |
463 } |
463 if (isNumericList(nodeText)) { |
464 } |
464 var matches = /([0-9]+)\./.exec(nodeText); |
465 |
465 var start = 1; |
466 pasteBinElm = lastRng = null; |
466 if (matches) { |
467 } |
467 start = parseInt(matches[1], 10); |
468 |
468 } |
469 /** |
469 convertParagraphToLi(node, 'ol', start); |
470 * Returns the contents of the paste bin as a HTML string. |
470 continue; |
471 * |
471 } |
472 * @return {String} Get the contents of the paste bin. |
472 if (node._listLevel) { |
473 */ |
473 convertParagraphToLi(node, 'ul', 1); |
474 function getPasteBinHtml() { |
474 continue; |
475 var html = '', pasteBinClones, i, clone, cloneHtml; |
475 } |
476 |
476 currentListNode = null; |
477 // Since WebKit/Chrome might clone the paste bin when pasting |
477 } else { |
478 // for example: <img style="float: right"> we need to check if any of them contains some useful html. |
478 prevListNode = currentListNode; |
479 // TODO: Man o man is this ugly. WebKit is the new IE! Remove this if they ever fix it! |
479 currentListNode = null; |
480 pasteBinClones = editor.dom.select('div[id=mcepastebin]'); |
480 } |
481 for (i = 0; i < pasteBinClones.length; i++) { |
481 } |
482 clone = pasteBinClones[i]; |
482 } |
483 |
483 function filterStyles(editor, validStyles, node, styleValue) { |
484 // Pasting plain text produces pastebins in pastebinds makes sence right!? |
484 var outputStyles = {}, matches; |
485 if (clone.firstChild && clone.firstChild.id == 'mcepastebin') { |
485 var styles = editor.dom.parseStyle(styleValue); |
486 clone = clone.firstChild; |
486 global$3.each(styles, function (value, name) { |
487 } |
487 switch (name) { |
488 |
488 case 'mso-list': |
489 cloneHtml = clone.innerHTML; |
489 matches = /\w+ \w+([0-9]+)/i.exec(styleValue); |
490 if (html != pasteBinDefaultContent) { |
490 if (matches) { |
491 html += cloneHtml; |
491 node._listLevel = parseInt(matches[1], 10); |
492 } |
492 } |
493 } |
493 if (/Ignore/i.test(value) && node.firstChild) { |
494 |
494 node._listIgnore = true; |
495 return html; |
495 node.firstChild._listIgnore = true; |
496 } |
496 } |
497 |
497 break; |
498 /** |
498 case 'horiz-align': |
499 * Gets various content types out of a datatransfer object. |
499 name = 'text-align'; |
500 * |
500 break; |
501 * @param {DataTransfer} dataTransfer Event fired on paste. |
501 case 'vert-align': |
502 * @return {Object} Object with mime types and data for those mime types. |
502 name = 'vertical-align'; |
503 */ |
503 break; |
504 function getDataTransferItems(dataTransfer) { |
504 case 'font-color': |
505 var data = {}; |
505 case 'mso-foreground': |
506 |
506 name = 'color'; |
507 if (dataTransfer) { |
507 break; |
508 // Use old WebKit/IE API |
508 case 'mso-background': |
509 if (dataTransfer.getData) { |
509 case 'mso-highlight': |
510 var legacyText = dataTransfer.getData('Text'); |
510 name = 'background'; |
511 if (legacyText && legacyText.length > 0) { |
511 break; |
512 if (legacyText.indexOf(mceInternalUrlPrefix) == -1) { |
512 case 'font-weight': |
513 data['text/plain'] = legacyText; |
513 case 'font-style': |
514 } |
514 if (value !== 'normal') { |
515 } |
515 outputStyles[name] = value; |
516 } |
516 } |
517 |
517 return; |
518 if (dataTransfer.types) { |
518 case 'mso-element': |
519 for (var i = 0; i < dataTransfer.types.length; i++) { |
519 if (/^(comment|comment-list)$/i.test(value)) { |
520 var contentType = dataTransfer.types[i]; |
520 node.remove(); |
521 data[contentType] = dataTransfer.getData(contentType); |
521 return; |
522 } |
522 } |
523 } |
523 break; |
524 } |
524 } |
525 |
525 if (name.indexOf('mso-comment') === 0) { |
526 return data; |
526 node.remove(); |
527 } |
527 return; |
528 |
528 } |
529 /** |
529 if (name.indexOf('mso-') === 0) { |
530 * Gets various content types out of the Clipboard API. It will also get the |
530 return; |
531 * plain text using older IE and WebKit API:s. |
531 } |
532 * |
532 if ($_xr8b0ikjjgwectl.getRetainStyleProps(editor) === 'all' || validStyles && validStyles[name]) { |
533 * @param {ClipboardEvent} clipboardEvent Event fired on paste. |
533 outputStyles[name] = value; |
534 * @return {Object} Object with mime types and data for those mime types. |
534 } |
535 */ |
535 }); |
536 function getClipboardContent(clipboardEvent) { |
536 if (/(bold)/i.test(outputStyles['font-weight'])) { |
537 return getDataTransferItems(clipboardEvent.clipboardData || editor.getDoc().dataTransfer); |
537 delete outputStyles['font-weight']; |
538 } |
538 node.wrap(new global$7('b', 1)); |
539 |
539 } |
540 /** |
540 if (/(italic)/i.test(outputStyles['font-style'])) { |
541 * Checks if the clipboard contains image data if it does it will take that data |
541 delete outputStyles['font-style']; |
542 * and convert it into a data url image and paste that image at the caret location. |
542 node.wrap(new global$7('i', 1)); |
543 * |
543 } |
544 * @param {ClipboardEvent} e Paste/drop event object. |
544 outputStyles = editor.dom.serializeStyle(outputStyles, node.name); |
545 * @param {DOMRange} rng Optional rng object to move selection to. |
545 if (outputStyles) { |
546 * @return {Boolean} true/false if the image data was found or not. |
546 return outputStyles; |
547 */ |
547 } |
548 function pasteImageData(e, rng) { |
548 return null; |
549 var dataTransfer = e.clipboardData || e.dataTransfer; |
549 } |
550 |
550 var filterWordContent = function (editor, content) { |
551 function processItems(items) { |
551 var retainStyleProperties, validStyles; |
552 var i, item, reader, hadImage = false; |
552 retainStyleProperties = $_xr8b0ikjjgwectl.getRetainStyleProps(editor); |
553 |
553 if (retainStyleProperties) { |
554 function pasteImage(reader) { |
554 validStyles = global$3.makeMap(retainStyleProperties.split(/[, ]/)); |
555 if (rng) { |
555 } |
556 editor.selection.setRng(rng); |
556 content = $_4bi2o9j0jjgwecui.filter(content, [ |
557 rng = null; |
557 /<br class="?Apple-interchange-newline"?>/gi, |
558 } |
558 /<b[^>]+id="?docs-internal-[^>]*>/gi, |
559 |
559 /<!--[\s\S]+?-->/gi, |
560 pasteHtml('<img src="' + reader.result + '">'); |
560 /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi, |
561 } |
561 [ |
562 |
562 /<(\/?)s>/gi, |
563 if (items) { |
563 '<$1strike>' |
564 for (i = 0; i < items.length; i++) { |
564 ], |
565 item = items[i]; |
565 [ |
566 |
566 / /gi, |
567 if (/^image\/(jpeg|png|gif|bmp)$/.test(item.type)) { |
567 '\xA0' |
568 reader = new FileReader(); |
568 ], |
569 reader.onload = pasteImage.bind(null, reader); |
569 [ |
570 reader.readAsDataURL(item.getAsFile ? item.getAsFile() : item); |
570 /<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi, |
571 |
571 function (str, spaces) { |
572 e.preventDefault(); |
572 return spaces.length > 0 ? spaces.replace(/./, ' ').slice(Math.floor(spaces.length / 2)).split('').join('\xA0') : ''; |
573 hadImage = true; |
573 } |
574 } |
574 ] |
575 } |
575 ]); |
576 } |
576 var validElements = $_xr8b0ikjjgwectl.getWordValidElements(editor); |
577 |
577 var schema = global$8({ |
578 return hadImage; |
578 valid_elements: validElements, |
579 } |
579 valid_children: '-li[p]' |
580 |
580 }); |
581 if (editor.settings.paste_data_images && dataTransfer) { |
581 global$3.each(schema.elements, function (rule) { |
582 return processItems(dataTransfer.items) || processItems(dataTransfer.files); |
582 if (!rule.attributes.class) { |
583 } |
583 rule.attributes.class = {}; |
584 } |
584 rule.attributesOrder.push('class'); |
585 |
585 } |
586 /** |
586 if (!rule.attributes.style) { |
587 * Chrome on Android doesn't support proper clipboard access so we have no choice but to allow the browser default behavior. |
587 rule.attributes.style = {}; |
588 * |
588 rule.attributesOrder.push('style'); |
589 * @param {Event} e Paste event object to check if it contains any data. |
589 } |
590 * @return {Boolean} true/false if the clipboard is empty or not. |
590 }); |
591 */ |
591 var domParser = global$6({}, schema); |
592 function isBrokenAndroidClipboardEvent(e) { |
592 domParser.addAttributeFilter('style', function (nodes) { |
593 var clipboardData = e.clipboardData; |
593 var i = nodes.length, node; |
594 |
594 while (i--) { |
595 return navigator.userAgent.indexOf('Android') != -1 && clipboardData && clipboardData.items && clipboardData.items.length === 0; |
595 node = nodes[i]; |
596 } |
596 node.attr('style', filterStyles(editor, validStyles, node, node.attr('style'))); |
597 |
597 if (node.name === 'span' && node.parent && !node.attributes.length) { |
598 function getCaretRangeFromEvent(e) { |
598 node.unwrap(); |
599 return RangeUtils.getCaretRangeFromPoint(e.clientX, e.clientY, editor.getDoc()); |
599 } |
600 } |
600 } |
601 |
601 }); |
602 function hasContentType(clipboardContent, mimeType) { |
602 domParser.addAttributeFilter('class', function (nodes) { |
603 return mimeType in clipboardContent && clipboardContent[mimeType].length > 0; |
603 var i = nodes.length, node, className; |
604 } |
604 while (i--) { |
605 |
605 node = nodes[i]; |
606 function isKeyboardPasteEvent(e) { |
606 className = node.attr('class'); |
607 return (VK.metaKeyPressed(e) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45); |
607 if (/^(MsoCommentReference|MsoCommentText|msoDel)$/i.test(className)) { |
608 } |
608 node.remove(); |
609 |
609 } |
610 function registerEventHandlers() { |
610 node.attr('class', null); |
611 editor.on('keydown', function(e) { |
611 } |
612 function removePasteBinOnKeyUp(e) { |
612 }); |
613 // Ctrl+V or Shift+Insert |
613 domParser.addNodeFilter('del', function (nodes) { |
614 if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) { |
614 var i = nodes.length; |
615 removePasteBin(); |
615 while (i--) { |
616 } |
616 nodes[i].remove(); |
617 } |
617 } |
618 |
618 }); |
619 // Ctrl+V or Shift+Insert |
619 domParser.addNodeFilter('a', function (nodes) { |
620 if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) { |
620 var i = nodes.length, node, href, name; |
621 keyboardPastePlainTextState = e.shiftKey && e.keyCode == 86; |
621 while (i--) { |
622 |
622 node = nodes[i]; |
623 // Edge case on Safari on Mac where it doesn't handle Cmd+Shift+V correctly |
623 href = node.attr('href'); |
624 // it fires the keydown but no paste or keyup so we are left with a paste bin |
624 name = node.attr('name'); |
625 if (keyboardPastePlainTextState && Env.webkit && navigator.userAgent.indexOf('Version/') != -1) { |
625 if (href && href.indexOf('#_msocom_') !== -1) { |
626 return; |
626 node.remove(); |
627 } |
627 continue; |
628 |
628 } |
629 // Prevent undoManager keydown handler from making an undo level with the pastebin in it |
629 if (href && href.indexOf('file://') === 0) { |
630 e.stopImmediatePropagation(); |
630 href = href.split('#')[1]; |
631 |
631 if (href) { |
632 keyboardPasteTimeStamp = new Date().getTime(); |
632 href = '#' + href; |
633 |
633 } |
634 // IE doesn't support Ctrl+Shift+V and it doesn't even produce a paste event |
634 } |
635 // so lets fake a paste event and let IE use the execCommand/dataTransfer methods |
635 if (!href && !name) { |
636 if (Env.ie && keyboardPastePlainTextState) { |
636 node.unwrap(); |
637 e.preventDefault(); |
637 } else { |
638 editor.fire('paste', {ieFake: true}); |
638 if (name && !/^_?(?:toc|edn|ftn)/i.test(name)) { |
639 return; |
639 node.unwrap(); |
640 } |
640 continue; |
641 |
641 } |
642 removePasteBin(); |
642 node.attr({ |
643 createPasteBin(); |
643 href: href, |
644 |
644 name: name |
645 // Remove pastebin if we get a keyup and no paste event |
645 }); |
646 // For example pasting a file in IE 11 will not produce a paste event |
646 } |
647 editor.once('keyup', removePasteBinOnKeyUp); |
647 } |
648 editor.once('paste', function() { |
648 }); |
649 editor.off('keyup', removePasteBinOnKeyUp); |
649 var rootNode = domParser.parse(content); |
650 }); |
650 if ($_xr8b0ikjjgwectl.shouldConvertWordFakeLists(editor)) { |
651 } |
651 convertFakeListsToProperLists(rootNode); |
652 }); |
652 } |
653 |
653 content = global$9({ validate: editor.settings.validate }, schema).serialize(rootNode); |
654 editor.on('paste', function(e) { |
654 return content; |
655 // Getting content from the Clipboard can take some time |
655 }; |
656 var clipboardTimer = new Date().getTime(); |
656 var preProcess = function (editor, content) { |
657 var clipboardContent = getClipboardContent(e); |
657 return $_xr8b0ikjjgwectl.shouldUseDefaultFilters(editor) ? filterWordContent(editor, content) : content; |
658 var clipboardDelay = new Date().getTime() - clipboardTimer; |
658 }; |
659 |
659 var $_dfatuiivjjgwecu8 = { |
660 var isKeyBoardPaste = (new Date().getTime() - keyboardPasteTimeStamp - clipboardDelay) < 1000; |
660 preProcess: preProcess, |
661 var plainTextMode = self.pasteFormat == "text" || keyboardPastePlainTextState; |
661 isWordContent: isWordContent |
662 |
662 }; |
663 keyboardPastePlainTextState = false; |
663 |
664 |
664 var processResult = function (content, cancelled) { |
665 if (e.isDefaultPrevented() || isBrokenAndroidClipboardEvent(e)) { |
665 return { |
666 removePasteBin(); |
666 content: content, |
667 return; |
667 cancelled: cancelled |
668 } |
668 }; |
669 |
669 }; |
670 if (pasteImageData(e)) { |
670 var postProcessFilter = function (editor, html, internal, isWordHtml) { |
671 removePasteBin(); |
671 var tempBody = editor.dom.create('div', { style: 'display:none' }, html); |
672 return; |
672 var postProcessArgs = $_8tki3zijjjgwectj.firePastePostProcess(editor, tempBody, internal, isWordHtml); |
673 } |
673 return processResult(postProcessArgs.node.innerHTML, postProcessArgs.isDefaultPrevented()); |
674 |
674 }; |
675 // Not a keyboard paste prevent default paste and try to grab the clipboard contents using different APIs |
675 var filterContent = function (editor, content, internal, isWordHtml) { |
676 if (!isKeyBoardPaste) { |
676 var preProcessArgs = $_8tki3zijjjgwectj.firePastePreProcess(editor, content, internal, isWordHtml); |
677 e.preventDefault(); |
677 if (editor.hasEventListeners('PastePostProcess') && !preProcessArgs.isDefaultPrevented()) { |
678 } |
678 return postProcessFilter(editor, preProcessArgs.content, internal, isWordHtml); |
679 |
679 } else { |
680 // Try IE only method if paste isn't a keyboard paste |
680 return processResult(preProcessArgs.content, preProcessArgs.isDefaultPrevented()); |
681 if (Env.ie && (!isKeyBoardPaste || e.ieFake)) { |
681 } |
682 createPasteBin(); |
682 }; |
683 |
683 var process = function (editor, html, internal) { |
684 editor.dom.bind(pasteBinElm, 'paste', function(e) { |
684 var isWordHtml = $_dfatuiivjjgwecu8.isWordContent(html); |
685 e.stopPropagation(); |
685 var content = isWordHtml ? $_dfatuiivjjgwecu8.preProcess(editor, html) : html; |
686 }); |
686 return filterContent(editor, content, internal, isWordHtml); |
687 |
687 }; |
688 editor.getDoc().execCommand('Paste', false, null); |
688 var $_3scw66iujjgwecu4 = { process: process }; |
689 clipboardContent["text/html"] = getPasteBinHtml(); |
689 |
690 } |
690 var pasteHtml = function (editor, html) { |
691 |
691 editor.insertContent(html, { |
692 setTimeout(function() { |
692 merge: $_xr8b0ikjjgwectl.shouldMergeFormats(editor), |
693 var content; |
693 paste: true |
694 |
694 }); |
695 // Grab HTML from Clipboard API or paste bin as a fallback |
695 return true; |
696 if (hasContentType(clipboardContent, 'text/html')) { |
696 }; |
697 content = clipboardContent['text/html']; |
697 var isAbsoluteUrl = function (url) { |
698 } else { |
698 return /^https?:\/\/[\w\?\-\/+=.&%@~#]+$/i.test(url); |
699 content = getPasteBinHtml(); |
699 }; |
700 |
700 var isImageUrl = function (url) { |
701 // If paste bin is empty try using plain text mode |
701 return isAbsoluteUrl(url) && /.(gif|jpe?g|png)$/.test(url); |
702 // since that is better than nothing right |
702 }; |
703 if (content == pasteBinDefaultContent) { |
703 var createImage = function (editor, url, pasteHtmlFn) { |
704 plainTextMode = true; |
704 editor.undoManager.extra(function () { |
705 } |
705 pasteHtmlFn(editor, url); |
706 } |
706 }, function () { |
707 |
707 editor.insertContent('<img src="' + url + '">'); |
708 content = Utils.trimHtml(content); |
708 }); |
709 |
709 return true; |
710 // WebKit has a nice bug where it clones the paste bin if you paste from for example notepad |
710 }; |
711 // so we need to force plain text mode in this case |
711 var createLink = function (editor, url, pasteHtmlFn) { |
712 if (pasteBinElm && pasteBinElm.firstChild && pasteBinElm.firstChild.id === 'mcepastebin') { |
712 editor.undoManager.extra(function () { |
713 plainTextMode = true; |
713 pasteHtmlFn(editor, url); |
714 } |
714 }, function () { |
715 |
715 editor.execCommand('mceInsertLink', false, url); |
716 removePasteBin(); |
716 }); |
717 |
717 return true; |
718 // If we got nothing from clipboard API and pastebin then we could try the last resort: plain/text |
718 }; |
719 if (!content.length) { |
719 var linkSelection = function (editor, html, pasteHtmlFn) { |
720 plainTextMode = true; |
720 return editor.selection.isCollapsed() === false && isAbsoluteUrl(html) ? createLink(editor, html, pasteHtmlFn) : false; |
721 } |
721 }; |
722 |
722 var insertImage = function (editor, html, pasteHtmlFn) { |
723 // Grab plain text from Clipboard API or convert existing HTML to plain text |
723 return isImageUrl(html) ? createImage(editor, html, pasteHtmlFn) : false; |
724 if (plainTextMode) { |
724 }; |
725 // Use plain text contents from Clipboard API unless the HTML contains paragraphs then |
725 var smartInsertContent = function (editor, html) { |
726 // we should convert the HTML to plain text since works better when pasting HTML/Word contents as plain text |
726 global$3.each([ |
727 if (hasContentType(clipboardContent, 'text/plain') && content.indexOf('</p>') == -1) { |
727 linkSelection, |
728 content = clipboardContent['text/plain']; |
728 insertImage, |
729 } else { |
729 pasteHtml |
730 content = Utils.innerText(content); |
730 ], function (action) { |
731 } |
731 return action(editor, html, pasteHtml) !== true; |
732 } |
732 }); |
733 |
733 }; |
734 // If the content is the paste bin default HTML then it was |
734 var insertContent = function (editor, html) { |
735 // impossible to get the cliboard data out. |
735 if ($_xr8b0ikjjgwectl.isSmartPasteEnabled(editor) === false) { |
736 if (content == pasteBinDefaultContent) { |
736 pasteHtml(editor, html); |
737 if (!isKeyBoardPaste) { |
737 } else { |
738 editor.windowManager.alert('Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents.'); |
738 smartInsertContent(editor, html); |
739 } |
739 } |
740 |
740 }; |
741 return; |
741 var $_d8pzpej1jjgwecum = { |
742 } |
742 isImageUrl: isImageUrl, |
743 |
743 isAbsoluteUrl: isAbsoluteUrl, |
744 if (plainTextMode) { |
744 insertContent: insertContent |
745 pasteText(content); |
745 }; |
746 } else { |
746 |
747 pasteHtml(content); |
747 var pasteHtml$1 = function (editor, html, internalFlag) { |
748 } |
748 var internal = internalFlag ? internalFlag : $_4x13hjirjjgwecu1.isMarked(html); |
749 }, 0); |
749 var args = $_3scw66iujjgwecu4.process(editor, $_4x13hjirjjgwecu1.unmark(html), internal); |
750 }); |
750 if (args.cancelled === false) { |
751 |
751 $_d8pzpej1jjgwecum.insertContent(editor, args.content); |
752 editor.on('dragstart dragend', function(e) { |
752 } |
753 draggingInternally = e.type == 'dragstart'; |
753 }; |
754 }); |
754 var pasteText = function (editor, text) { |
755 |
755 text = editor.dom.encode(text).replace(/\r\n/g, '\n'); |
756 editor.on('drop', function(e) { |
756 text = $_4h3hnrisjjgwecu2.convert(text, editor.settings.forced_root_block, editor.settings.forced_root_block_attrs); |
757 var rng = getCaretRangeFromEvent(e); |
757 pasteHtml$1(editor, text, false); |
758 |
758 }; |
759 if (e.isDefaultPrevented() || draggingInternally) { |
759 var getDataTransferItems = function (dataTransfer) { |
760 return; |
760 var items = {}; |
761 } |
761 var mceInternalUrlPrefix = 'data:text/mce-internal,'; |
762 |
762 if (dataTransfer) { |
763 if (pasteImageData(e, rng)) { |
763 if (dataTransfer.getData) { |
764 return; |
764 var legacyText = dataTransfer.getData('Text'); |
765 } |
765 if (legacyText && legacyText.length > 0) { |
766 |
766 if (legacyText.indexOf(mceInternalUrlPrefix) === -1) { |
767 if (rng && editor.settings.paste_filter_drop !== false) { |
767 items['text/plain'] = legacyText; |
768 var dropContent = getDataTransferItems(e.dataTransfer); |
768 } |
769 var content = dropContent['mce-internal'] || dropContent['text/html'] || dropContent['text/plain']; |
769 } |
770 |
770 } |
771 if (content) { |
771 if (dataTransfer.types) { |
772 e.preventDefault(); |
772 for (var i = 0; i < dataTransfer.types.length; i++) { |
773 |
773 var contentType = dataTransfer.types[i]; |
774 editor.undoManager.transact(function() { |
774 try { |
775 if (dropContent['mce-internal']) { |
775 items[contentType] = dataTransfer.getData(contentType); |
776 editor.execCommand('Delete'); |
776 } catch (ex) { |
777 } |
777 items[contentType] = ''; |
778 |
778 } |
779 editor.selection.setRng(rng); |
779 } |
780 |
780 } |
781 content = Utils.trimHtml(content); |
781 } |
782 |
782 return items; |
783 if (!dropContent['text/html']) { |
783 }; |
784 pasteText(content); |
784 var getClipboardContent = function (editor, clipboardEvent) { |
785 } else { |
785 var content = getDataTransferItems(clipboardEvent.clipboardData || editor.getDoc().dataTransfer); |
786 pasteHtml(content); |
786 return $_4bi2o9j0jjgwecui.isMsEdge() ? global$3.extend(content, { 'text/html': '' }) : content; |
787 } |
787 }; |
788 }); |
788 var hasContentType = function (clipboardContent, mimeType) { |
789 } |
789 return mimeType in clipboardContent && clipboardContent[mimeType].length > 0; |
790 } |
790 }; |
791 }); |
791 var hasHtmlOrText = function (content) { |
792 |
792 return hasContentType(content, 'text/html') || hasContentType(content, 'text/plain'); |
793 editor.on('dragover dragend', function(e) { |
793 }; |
794 if (editor.settings.paste_data_images) { |
794 var getBase64FromUri = function (uri) { |
795 e.preventDefault(); |
795 var idx; |
796 } |
796 idx = uri.indexOf(','); |
797 }); |
797 if (idx !== -1) { |
798 } |
798 return uri.substr(idx + 1); |
799 |
799 } |
800 self.pasteHtml = pasteHtml; |
800 return null; |
801 self.pasteText = pasteText; |
801 }; |
802 |
802 var isValidDataUriImage = function (settings, imgElm) { |
803 editor.on('preInit', function() { |
803 return settings.images_dataimg_filter ? settings.images_dataimg_filter(imgElm) : true; |
804 registerEventHandlers(); |
804 }; |
805 |
805 var extractFilename = function (editor, str) { |
806 // Remove all data images from paste for example from Gecko |
806 var m = str.match(/([\s\S]+?)\.(?:jpeg|jpg|png|gif)$/i); |
807 // except internal images like video elements |
807 return m ? editor.dom.encode(m[1]) : null; |
808 editor.parser.addNodeFilter('img', function(nodes) { |
808 }; |
809 if (!editor.settings.paste_data_images) { |
809 var uniqueId = $_4bi2o9j0jjgwecui.createIdGenerator('mceclip'); |
810 var i = nodes.length; |
810 var pasteImage = function (editor, rng, reader, blob) { |
811 |
811 if (rng) { |
812 while (i--) { |
812 editor.selection.setRng(rng); |
813 var src = nodes[i].attributes.map.src; |
813 rng = null; |
814 |
814 } |
815 // Some browsers automatically produce data uris on paste |
815 var dataUri = reader.result; |
816 // Safari on Mac produces webkit-fake-url see: https://bugs.webkit.org/show_bug.cgi?id=49141 |
816 var base64 = getBase64FromUri(dataUri); |
817 if (src && /^(data:image|webkit\-fake\-url)/.test(src)) { |
817 var id = uniqueId(); |
818 if (!nodes[i].attr('data-mce-object') && src !== Env.transparentSrc) { |
818 var name$$1 = editor.settings.images_reuse_filename && blob.name ? extractFilename(editor, blob.name) : id; |
819 nodes[i].remove(); |
819 var img = new Image(); |
820 } |
820 img.src = dataUri; |
821 } |
821 if (isValidDataUriImage(editor.settings, img)) { |
822 } |
822 var blobCache = editor.editorUpload.blobCache; |
823 } |
823 var blobInfo = void 0, existingBlobInfo = void 0; |
824 }); |
824 existingBlobInfo = blobCache.findFirst(function (cachedBlobInfo) { |
825 }); |
825 return cachedBlobInfo.base64() === base64; |
826 }; |
826 }); |
827 }); |
827 if (!existingBlobInfo) { |
828 |
828 blobInfo = blobCache.create(id, blob, base64, name$$1); |
829 // Included from: js/tinymce/plugins/paste/classes/WordFilter.js |
829 blobCache.add(blobInfo); |
830 |
830 } else { |
831 /** |
831 blobInfo = existingBlobInfo; |
832 * WordFilter.js |
832 } |
833 * |
833 pasteHtml$1(editor, '<img src="' + blobInfo.blobUri() + '">', false); |
834 * Copyright, Moxiecode Systems AB |
834 } else { |
835 * Released under LGPL License. |
835 pasteHtml$1(editor, '<img src="' + dataUri + '">', false); |
836 * |
836 } |
837 * License: http://www.tinymce.com/license |
837 }; |
838 * Contributing: http://www.tinymce.com/contributing |
838 var isClipboardEvent = function (event$$1) { |
839 */ |
839 return event$$1.type === 'paste'; |
840 |
840 }; |
841 /** |
841 var pasteImageData = function (editor, e, rng) { |
842 * This class parses word HTML into proper TinyMCE markup. |
842 var dataTransfer = isClipboardEvent(e) ? e.clipboardData : e.dataTransfer; |
843 * |
843 function processItems(items) { |
844 * @class tinymce.pasteplugin.WordFilter |
844 var i, item, reader, hadImage = false; |
845 * @private |
845 if (items) { |
846 */ |
846 for (i = 0; i < items.length; i++) { |
847 define("tinymce/pasteplugin/WordFilter", [ |
847 item = items[i]; |
848 "tinymce/util/Tools", |
848 if (/^image\/(jpeg|png|gif|bmp)$/.test(item.type)) { |
849 "tinymce/html/DomParser", |
849 var blob = item.getAsFile ? item.getAsFile() : item; |
850 "tinymce/html/Schema", |
850 reader = new window.FileReader(); |
851 "tinymce/html/Serializer", |
851 reader.onload = pasteImage.bind(null, editor, rng, reader, blob); |
852 "tinymce/html/Node", |
852 reader.readAsDataURL(blob); |
853 "tinymce/pasteplugin/Utils" |
853 e.preventDefault(); |
854 ], function(Tools, DomParser, Schema, Serializer, Node, Utils) { |
854 hadImage = true; |
855 /** |
855 } |
856 * Checks if the specified content is from any of the following sources: MS Word/Office 365/Google docs. |
856 } |
857 */ |
857 } |
858 function isWordContent(content) { |
858 return hadImage; |
859 return ( |
859 } |
860 (/<font face="Times New Roman"|class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i).test(content) || |
860 if (editor.settings.paste_data_images && dataTransfer) { |
861 (/class="OutlineElement/).test(content) || |
861 return processItems(dataTransfer.items) || processItems(dataTransfer.files); |
862 (/id="?docs\-internal\-guid\-/.test(content)) |
862 } |
863 ); |
863 }; |
864 } |
864 var isBrokenAndroidClipboardEvent = function (e) { |
865 |
865 var clipboardData = e.clipboardData; |
866 /** |
866 return navigator.userAgent.indexOf('Android') !== -1 && clipboardData && clipboardData.items && clipboardData.items.length === 0; |
867 * Checks if the specified text starts with "1. " or "a. " etc. |
867 }; |
868 */ |
868 var isKeyboardPasteEvent = function (e) { |
869 function isNumericList(text) { |
869 return global$4.metaKeyPressed(e) && e.keyCode === 86 || e.shiftKey && e.keyCode === 45; |
870 var found, patterns; |
870 }; |
871 |
871 var registerEventHandlers = function (editor, pasteBin, pasteFormat) { |
872 patterns = [ |
872 var keyboardPasteTimeStamp = 0; |
873 /^[IVXLMCD]{1,2}\.[ \u00a0]/, // Roman upper case |
873 var keyboardPastePlainTextState; |
874 /^[ivxlmcd]{1,2}\.[ \u00a0]/, // Roman lower case |
874 editor.on('keydown', function (e) { |
875 /^[a-z]{1,2}[\.\)][ \u00a0]/, // Alphabetical a-z |
875 function removePasteBinOnKeyUp(e) { |
876 /^[A-Z]{1,2}[\.\)][ \u00a0]/, // Alphabetical A-Z |
876 if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) { |
877 /^[0-9]+\.[ \u00a0]/, // Numeric lists |
877 pasteBin.remove(); |
878 /^[\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d]+\.[ \u00a0]/, // Japanese |
878 } |
879 /^[\u58f1\u5f10\u53c2\u56db\u4f0d\u516d\u4e03\u516b\u4e5d\u62fe]+\.[ \u00a0]/ // Chinese |
879 } |
880 ]; |
880 if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) { |
881 |
881 keyboardPastePlainTextState = e.shiftKey && e.keyCode === 86; |
882 text = text.replace(/^[\u00a0 ]+/, ''); |
882 if (keyboardPastePlainTextState && global$1.webkit && navigator.userAgent.indexOf('Version/') !== -1) { |
883 |
883 return; |
884 Tools.each(patterns, function(pattern) { |
884 } |
885 if (pattern.test(text)) { |
885 e.stopImmediatePropagation(); |
886 found = true; |
886 keyboardPasteTimeStamp = new Date().getTime(); |
887 return false; |
887 if (global$1.ie && keyboardPastePlainTextState) { |
888 } |
888 e.preventDefault(); |
889 }); |
889 $_8tki3zijjjgwectj.firePaste(editor, true); |
890 |
890 return; |
891 return found; |
891 } |
892 } |
892 pasteBin.remove(); |
893 |
893 pasteBin.create(); |
894 function isBulletList(text) { |
894 editor.once('keyup', removePasteBinOnKeyUp); |
895 return /^[\s\u00a0]*[\u2022\u00b7\u00a7\u25CF]\s*/.test(text); |
895 editor.once('paste', function () { |
896 } |
896 editor.off('keyup', removePasteBinOnKeyUp); |
897 |
897 }); |
898 function WordFilter(editor) { |
898 } |
899 var settings = editor.settings; |
899 }); |
900 |
900 function insertClipboardContent(clipboardContent, isKeyBoardPaste, plainTextMode, internal) { |
901 editor.on('BeforePastePreProcess', function(e) { |
901 var content, isPlainTextHtml; |
902 var content = e.content, retainStyleProperties, validStyles; |
902 if (hasContentType(clipboardContent, 'text/html')) { |
903 |
903 content = clipboardContent['text/html']; |
904 // Remove google docs internal guid markers |
904 } else { |
905 content = content.replace(/<b[^>]+id="?docs-internal-[^>]*>/gi, ''); |
905 content = pasteBin.getHtml(); |
906 content = content.replace(/<br class="?Apple-interchange-newline"?>/gi, ''); |
906 internal = internal ? internal : $_4x13hjirjjgwecu1.isMarked(content); |
907 |
907 if (pasteBin.isDefaultContent(content)) { |
908 retainStyleProperties = settings.paste_retain_style_properties; |
908 plainTextMode = true; |
909 if (retainStyleProperties) { |
909 } |
910 validStyles = Tools.makeMap(retainStyleProperties.split(/[, ]/)); |
910 } |
911 } |
911 content = $_4bi2o9j0jjgwecui.trimHtml(content); |
912 |
912 pasteBin.remove(); |
913 /** |
913 isPlainTextHtml = internal === false && $_4h3hnrisjjgwecu2.isPlainText(content); |
914 * Converts fake bullet and numbered lists to real semantic OL/UL. |
914 if (!content.length || isPlainTextHtml) { |
915 * |
915 plainTextMode = true; |
916 * @param {tinymce.html.Node} node Root node to convert children of. |
916 } |
917 */ |
917 if (plainTextMode) { |
918 function convertFakeListsToProperLists(node) { |
918 if (hasContentType(clipboardContent, 'text/plain') && isPlainTextHtml) { |
919 var currentListNode, prevListNode, lastLevel = 1; |
919 content = clipboardContent['text/plain']; |
920 |
920 } else { |
921 function getText(node) { |
921 content = $_4bi2o9j0jjgwecui.innerText(content); |
922 var txt = ''; |
922 } |
923 |
923 } |
924 if (node.type === 3) { |
924 if (pasteBin.isDefaultContent(content)) { |
925 return node.value; |
925 if (!isKeyBoardPaste) { |
926 } |
926 editor.windowManager.alert('Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents.'); |
927 |
927 } |
928 if ((node = node.firstChild)) { |
928 return; |
929 do { |
929 } |
930 txt += getText(node); |
930 if (plainTextMode) { |
931 } while ((node = node.next)); |
931 pasteText(editor, content); |
932 } |
932 } else { |
933 |
933 pasteHtml$1(editor, content, internal); |
934 return txt; |
934 } |
935 } |
935 } |
936 |
936 var getLastRng = function () { |
937 function trimListStart(node, regExp) { |
937 return pasteBin.getLastRng() || editor.selection.getRng(); |
938 if (node.type === 3) { |
938 }; |
939 if (regExp.test(node.value)) { |
939 editor.on('paste', function (e) { |
940 node.value = node.value.replace(regExp, ''); |
940 var clipboardTimer = new Date().getTime(); |
941 return false; |
941 var clipboardContent = getClipboardContent(editor, e); |
942 } |
942 var clipboardDelay = new Date().getTime() - clipboardTimer; |
943 } |
943 var isKeyBoardPaste = new Date().getTime() - keyboardPasteTimeStamp - clipboardDelay < 1000; |
944 |
944 var plainTextMode = pasteFormat.get() === 'text' || keyboardPastePlainTextState; |
945 if ((node = node.firstChild)) { |
945 var internal = hasContentType(clipboardContent, $_4x13hjirjjgwecu1.internalHtmlMime()); |
946 do { |
946 keyboardPastePlainTextState = false; |
947 if (!trimListStart(node, regExp)) { |
947 if (e.isDefaultPrevented() || isBrokenAndroidClipboardEvent(e)) { |
948 return false; |
948 pasteBin.remove(); |
949 } |
949 return; |
950 } while ((node = node.next)); |
950 } |
951 } |
951 if (!hasHtmlOrText(clipboardContent) && pasteImageData(editor, e, getLastRng())) { |
952 |
952 pasteBin.remove(); |
953 return true; |
953 return; |
954 } |
954 } |
955 |
955 if (!isKeyBoardPaste) { |
956 function removeIgnoredNodes(node) { |
956 e.preventDefault(); |
957 if (node._listIgnore) { |
957 } |
958 node.remove(); |
958 if (global$1.ie && (!isKeyBoardPaste || e.ieFake) && !hasContentType(clipboardContent, 'text/html')) { |
959 return; |
959 pasteBin.create(); |
960 } |
960 editor.dom.bind(pasteBin.getEl(), 'paste', function (e) { |
961 |
961 e.stopPropagation(); |
962 if ((node = node.firstChild)) { |
962 }); |
963 do { |
963 editor.getDoc().execCommand('Paste', false, null); |
964 removeIgnoredNodes(node); |
964 clipboardContent['text/html'] = pasteBin.getHtml(); |
965 } while ((node = node.next)); |
965 } |
966 } |
966 if (hasContentType(clipboardContent, 'text/html')) { |
967 } |
967 e.preventDefault(); |
968 |
968 if (!internal) { |
969 function convertParagraphToLi(paragraphNode, listName, start) { |
969 internal = $_4x13hjirjjgwecu1.isMarked(clipboardContent['text/html']); |
970 var level = paragraphNode._listLevel || lastLevel; |
970 } |
971 |
971 insertClipboardContent(clipboardContent, isKeyBoardPaste, plainTextMode, internal); |
972 // Handle list nesting |
972 } else { |
973 if (level != lastLevel) { |
973 global$2.setEditorTimeout(editor, function () { |
974 if (level < lastLevel) { |
974 insertClipboardContent(clipboardContent, isKeyBoardPaste, plainTextMode, internal); |
975 // Move to parent list |
975 }, 0); |
976 if (currentListNode) { |
976 } |
977 currentListNode = currentListNode.parent.parent; |
977 }); |
978 } |
978 }; |
979 } else { |
979 var registerEventsAndFilters = function (editor, pasteBin, pasteFormat) { |
980 // Create new list |
980 registerEventHandlers(editor, pasteBin, pasteFormat); |
981 prevListNode = currentListNode; |
981 var src; |
982 currentListNode = null; |
982 editor.parser.addNodeFilter('img', function (nodes, name$$1, args) { |
983 } |
983 var isPasteInsert = function (args) { |
984 } |
984 return args.data && args.data.paste === true; |
985 |
985 }; |
986 if (!currentListNode || currentListNode.name != listName) { |
986 var remove = function (node) { |
987 prevListNode = prevListNode || currentListNode; |
987 if (!node.attr('data-mce-object') && src !== global$1.transparentSrc) { |
988 currentListNode = new Node(listName, 1); |
988 node.remove(); |
989 |
989 } |
990 if (start > 1) { |
990 }; |
991 currentListNode.attr('start', '' + start); |
991 var isWebKitFakeUrl = function (src) { |
992 } |
992 return src.indexOf('webkit-fake-url') === 0; |
993 |
993 }; |
994 paragraphNode.wrap(currentListNode); |
994 var isDataUri = function (src) { |
995 } else { |
995 return src.indexOf('data:') === 0; |
996 currentListNode.append(paragraphNode); |
996 }; |
997 } |
997 if (!editor.settings.paste_data_images && isPasteInsert(args)) { |
998 |
998 var i = nodes.length; |
999 paragraphNode.name = 'li'; |
999 while (i--) { |
1000 |
1000 src = nodes[i].attributes.map.src; |
1001 // Append list to previous list if it exists |
1001 if (!src) { |
1002 if (level > lastLevel && prevListNode) { |
1002 continue; |
1003 prevListNode.lastChild.append(currentListNode); |
1003 } |
1004 } |
1004 if (isWebKitFakeUrl(src)) { |
1005 |
1005 remove(nodes[i]); |
1006 lastLevel = level; |
1006 } else if (!editor.settings.allow_html_data_urls && isDataUri(src)) { |
1007 |
1007 remove(nodes[i]); |
1008 // Remove start of list item "1. " or "· " etc |
1008 } |
1009 removeIgnoredNodes(paragraphNode); |
1009 } |
1010 trimListStart(paragraphNode, /^\u00a0+/); |
1010 } |
1011 trimListStart(paragraphNode, /^\s*([\u2022\u00b7\u00a7\u25CF]|\w+\.)/); |
1011 }); |
1012 trimListStart(paragraphNode, /^\u00a0+/); |
1012 }; |
1013 } |
1013 |
1014 |
1014 var getPasteBinParent = function (editor) { |
1015 // Build a list of all root level elements before we start |
1015 return global$1.ie && editor.inline ? document.body : editor.getBody(); |
1016 // altering them in the loop below. |
1016 }; |
1017 var elements = [], child = node.firstChild; |
1017 var isExternalPasteBin = function (editor) { |
1018 while (typeof child !== 'undefined' && child !== null) { |
1018 return getPasteBinParent(editor) !== editor.getBody(); |
1019 elements.push(child); |
1019 }; |
1020 |
1020 var delegatePasteEvents = function (editor, pasteBinElm) { |
1021 child = child.walk(); |
1021 if (isExternalPasteBin(editor)) { |
1022 if (child !== null) { |
1022 editor.dom.bind(pasteBinElm, 'paste keyup', function (e) { |
1023 while (typeof child !== 'undefined' && child.parent !== node) { |
1023 setTimeout(function () { |
1024 child = child.walk(); |
1024 editor.fire('paste'); |
1025 } |
1025 }, 0); |
1026 } |
1026 }); |
1027 } |
1027 } |
1028 |
1028 }; |
1029 for (var i = 0; i < elements.length; i++) { |
1029 var create = function (editor, lastRngCell, pasteBinDefaultContent) { |
1030 node = elements[i]; |
1030 var dom = editor.dom, body = editor.getBody(); |
1031 |
1031 var pasteBinElm; |
1032 if (node.name == 'p' && node.firstChild) { |
1032 lastRngCell.set(editor.selection.getRng()); |
1033 // Find first text node in paragraph |
1033 pasteBinElm = editor.dom.add(getPasteBinParent(editor), 'div', { |
1034 var nodeText = getText(node); |
1034 'id': 'mcepastebin', |
1035 |
1035 'class': 'mce-pastebin', |
1036 // Detect unordered lists look for bullets |
1036 'contentEditable': true, |
1037 if (isBulletList(nodeText)) { |
1037 'data-mce-bogus': 'all', |
1038 convertParagraphToLi(node, 'ul'); |
1038 'style': 'position: fixed; top: 50%; width: 10px; height: 10px; overflow: hidden; opacity: 0' |
1039 continue; |
1039 }, pasteBinDefaultContent); |
1040 } |
1040 if (global$1.ie || global$1.gecko) { |
1041 |
1041 dom.setStyle(pasteBinElm, 'left', dom.getStyle(body, 'direction', true) === 'rtl' ? 65535 : -65535); |
1042 // Detect ordered lists 1., a. or ixv. |
1042 } |
1043 if (isNumericList(nodeText)) { |
1043 dom.bind(pasteBinElm, 'beforedeactivate focusin focusout', function (e) { |
1044 // Parse OL start number |
1044 e.stopPropagation(); |
1045 var matches = /([0-9]+)\./.exec(nodeText); |
1045 }); |
1046 var start = 1; |
1046 delegatePasteEvents(editor, pasteBinElm); |
1047 if (matches) { |
1047 pasteBinElm.focus(); |
1048 start = parseInt(matches[1], 10); |
1048 editor.selection.select(pasteBinElm, true); |
1049 } |
1049 }; |
1050 |
1050 var remove = function (editor, lastRngCell) { |
1051 convertParagraphToLi(node, 'ol', start); |
1051 if (getEl(editor)) { |
1052 continue; |
1052 var pasteBinClone = void 0; |
1053 } |
1053 var lastRng = lastRngCell.get(); |
1054 |
1054 while (pasteBinClone = editor.dom.get('mcepastebin')) { |
1055 // Convert paragraphs marked as lists but doesn't look like anything |
1055 editor.dom.remove(pasteBinClone); |
1056 if (node._listLevel) { |
1056 editor.dom.unbind(pasteBinClone); |
1057 convertParagraphToLi(node, 'ul', 1); |
1057 } |
1058 continue; |
1058 if (lastRng) { |
1059 } |
1059 editor.selection.setRng(lastRng); |
1060 |
1060 } |
1061 currentListNode = null; |
1061 } |
1062 } else { |
1062 lastRngCell.set(null); |
1063 // If the root level element isn't a p tag which can be |
1063 }; |
1064 // processed by convertParagraphToLi, it interrupts the |
1064 var getEl = function (editor) { |
1065 // lists, causing a new list to start instead of having |
1065 return editor.dom.get('mcepastebin'); |
1066 // elements from the next list inserted above this tag. |
1066 }; |
1067 prevListNode = currentListNode; |
1067 var getHtml = function (editor) { |
1068 currentListNode = null; |
1068 var pasteBinElm, pasteBinClones, i, dirtyWrappers, cleanWrapper; |
1069 } |
1069 var copyAndRemove = function (toElm, fromElm) { |
1070 } |
1070 toElm.appendChild(fromElm); |
1071 } |
1071 editor.dom.remove(fromElm, true); |
1072 |
1072 }; |
1073 function filterStyles(node, styleValue) { |
1073 pasteBinClones = global$3.grep(getPasteBinParent(editor).childNodes, function (elm) { |
1074 var outputStyles = {}, matches, styles = editor.dom.parseStyle(styleValue); |
1074 return elm.id === 'mcepastebin'; |
1075 |
1075 }); |
1076 Tools.each(styles, function(value, name) { |
1076 pasteBinElm = pasteBinClones.shift(); |
1077 // Convert various MS styles to W3C styles |
1077 global$3.each(pasteBinClones, function (pasteBinClone) { |
1078 switch (name) { |
1078 copyAndRemove(pasteBinElm, pasteBinClone); |
1079 case 'mso-list': |
1079 }); |
1080 // Parse out list indent level for lists |
1080 dirtyWrappers = editor.dom.select('div[id=mcepastebin]', pasteBinElm); |
1081 matches = /\w+ \w+([0-9]+)/i.exec(styleValue); |
1081 for (i = dirtyWrappers.length - 1; i >= 0; i--) { |
1082 if (matches) { |
1082 cleanWrapper = editor.dom.create('div'); |
1083 node._listLevel = parseInt(matches[1], 10); |
1083 pasteBinElm.insertBefore(cleanWrapper, dirtyWrappers[i]); |
1084 } |
1084 copyAndRemove(cleanWrapper, dirtyWrappers[i]); |
1085 |
1085 } |
1086 // Remove these nodes <span style="mso-list:Ignore">o</span> |
1086 return pasteBinElm ? pasteBinElm.innerHTML : ''; |
1087 // Since the span gets removed we mark the text node and the span |
1087 }; |
1088 if (/Ignore/i.test(value) && node.firstChild) { |
1088 var getLastRng = function (lastRng) { |
1089 node._listIgnore = true; |
1089 return lastRng.get(); |
1090 node.firstChild._listIgnore = true; |
1090 }; |
1091 } |
1091 var isDefaultContent = function (pasteBinDefaultContent, content) { |
1092 |
1092 return content === pasteBinDefaultContent; |
1093 break; |
1093 }; |
1094 |
1094 var isPasteBin = function (elm) { |
1095 case "horiz-align": |
1095 return elm && elm.id === 'mcepastebin'; |
1096 name = "text-align"; |
1096 }; |
1097 break; |
1097 var isDefault = function (editor, pasteBinDefaultContent) { |
1098 |
1098 var pasteBinElm = getEl(editor); |
1099 case "vert-align": |
1099 return isPasteBin(pasteBinElm) && isDefaultContent(pasteBinDefaultContent, pasteBinElm.innerHTML); |
1100 name = "vertical-align"; |
1100 }; |
1101 break; |
1101 var PasteBin = function (editor) { |
1102 |
1102 var lastRng = Cell(null); |
1103 case "font-color": |
1103 var pasteBinDefaultContent = '%MCEPASTEBIN%'; |
1104 case "mso-foreground": |
1104 return { |
1105 name = "color"; |
1105 create: function () { |
1106 break; |
1106 return create(editor, lastRng, pasteBinDefaultContent); |
1107 |
1107 }, |
1108 case "mso-background": |
1108 remove: function () { |
1109 case "mso-highlight": |
1109 return remove(editor, lastRng); |
1110 name = "background"; |
1110 }, |
1111 break; |
1111 getEl: function () { |
1112 |
1112 return getEl(editor); |
1113 case "font-weight": |
1113 }, |
1114 case "font-style": |
1114 getHtml: function () { |
1115 if (value != "normal") { |
1115 return getHtml(editor); |
1116 outputStyles[name] = value; |
1116 }, |
1117 } |
1117 getLastRng: function () { |
1118 return; |
1118 return getLastRng(lastRng); |
1119 |
1119 }, |
1120 case "mso-element": |
1120 isDefault: function () { |
1121 // Remove track changes code |
1121 return isDefault(editor, pasteBinDefaultContent); |
1122 if (/^(comment|comment-list)$/i.test(value)) { |
1122 }, |
1123 node.remove(); |
1123 isDefaultContent: function (content) { |
1124 return; |
1124 return isDefaultContent(pasteBinDefaultContent, content); |
1125 } |
1125 } |
1126 |
1126 }; |
1127 break; |
1127 }; |
1128 } |
1128 |
1129 |
1129 var Clipboard = function (editor, pasteFormat) { |
1130 if (name.indexOf('mso-comment') === 0) { |
1130 var pasteBin = PasteBin(editor); |
1131 node.remove(); |
1131 editor.on('preInit', function () { |
1132 return; |
1132 return registerEventsAndFilters(editor, pasteBin, pasteFormat); |
1133 } |
1133 }); |
1134 |
1134 return { |
1135 // Never allow mso- prefixed names |
1135 pasteFormat: pasteFormat, |
1136 if (name.indexOf('mso-') === 0) { |
1136 pasteHtml: function (html, internalFlag) { |
1137 return; |
1137 return pasteHtml$1(editor, html, internalFlag); |
1138 } |
1138 }, |
1139 |
1139 pasteText: function (text) { |
1140 // Output only valid styles |
1140 return pasteText(editor, text); |
1141 if (retainStyleProperties == "all" || (validStyles && validStyles[name])) { |
1141 }, |
1142 outputStyles[name] = value; |
1142 pasteImageData: function (e, rng) { |
1143 } |
1143 return pasteImageData(editor, e, rng); |
1144 }); |
1144 }, |
1145 |
1145 getDataTransferItems: getDataTransferItems, |
1146 // Convert bold style to "b" element |
1146 hasHtmlOrText: hasHtmlOrText, |
1147 if (/(bold)/i.test(outputStyles["font-weight"])) { |
1147 hasContentType: hasContentType |
1148 delete outputStyles["font-weight"]; |
1148 }; |
1149 node.wrap(new Node("b", 1)); |
1149 }; |
1150 } |
1150 |
1151 |
1151 var noop = function () { |
1152 // Convert italic style to "i" element |
1152 }; |
1153 if (/(italic)/i.test(outputStyles["font-style"])) { |
1153 var hasWorkingClipboardApi = function (clipboardData) { |
1154 delete outputStyles["font-style"]; |
1154 return global$1.iOS === false && clipboardData !== undefined && typeof clipboardData.setData === 'function' && $_4bi2o9j0jjgwecui.isMsEdge() !== true; |
1155 node.wrap(new Node("i", 1)); |
1155 }; |
1156 } |
1156 var setHtml5Clipboard = function (clipboardData, html, text) { |
1157 |
1157 if (hasWorkingClipboardApi(clipboardData)) { |
1158 // Serialize the styles and see if there is something left to keep |
1158 try { |
1159 outputStyles = editor.dom.serializeStyle(outputStyles, node.name); |
1159 clipboardData.clearData(); |
1160 if (outputStyles) { |
1160 clipboardData.setData('text/html', html); |
1161 return outputStyles; |
1161 clipboardData.setData('text/plain', text); |
1162 } |
1162 clipboardData.setData($_4x13hjirjjgwecu1.internalHtmlMime(), html); |
1163 |
1163 return true; |
1164 return null; |
1164 } catch (e) { |
1165 } |
1165 return false; |
1166 |
1166 } |
1167 if (settings.paste_enable_default_filters === false) { |
1167 } else { |
1168 return; |
1168 return false; |
1169 } |
1169 } |
1170 |
1170 }; |
1171 // Detect is the contents is Word junk HTML |
1171 var setClipboardData = function (evt, data, fallback, done) { |
1172 if (isWordContent(e.content)) { |
1172 if (setHtml5Clipboard(evt.clipboardData, data.html, data.text)) { |
1173 e.wordContent = true; // Mark it for other processors |
1173 evt.preventDefault(); |
1174 |
1174 done(); |
1175 // Remove basic Word junk |
1175 } else { |
1176 content = Utils.filter(content, [ |
1176 fallback(data.html, done); |
1177 // Word comments like conditional comments etc |
1177 } |
1178 /<!--[\s\S]+?-->/gi, |
1178 }; |
1179 |
1179 var fallback = function (editor) { |
1180 // Remove comments, scripts (e.g., msoShowComment), XML tag, VML content, |
1180 return function (html, done) { |
1181 // MS Office namespaced tags, and a few other tags |
1181 var markedHtml = $_4x13hjirjjgwecu1.mark(html); |
1182 /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi, |
1182 var outer = editor.dom.create('div', { |
1183 |
1183 'contenteditable': 'false', |
1184 // Convert <s> into <strike> for line-though |
1184 'data-mce-bogus': 'all' |
1185 [/<(\/?)s>/gi, "<$1strike>"], |
1185 }); |
1186 |
1186 var inner = editor.dom.create('div', { contenteditable: 'true' }, markedHtml); |
1187 // Replace nsbp entites to char since it's easier to handle |
1187 editor.dom.setStyles(outer, { |
1188 [/ /gi, "\u00a0"], |
1188 position: 'fixed', |
1189 |
1189 top: '0', |
1190 // Convert <span style="mso-spacerun:yes">___</span> to string of alternating |
1190 left: '-3000px', |
1191 // breaking/non-breaking spaces of same length |
1191 width: '1000px', |
1192 [/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi, |
1192 overflow: 'hidden' |
1193 function(str, spaces) { |
1193 }); |
1194 return (spaces.length > 0) ? |
1194 outer.appendChild(inner); |
1195 spaces.replace(/./, " ").slice(Math.floor(spaces.length / 2)).split("").join("\u00a0") : ""; |
1195 editor.dom.add(editor.getBody(), outer); |
1196 } |
1196 var range = editor.selection.getRng(); |
1197 ] |
1197 inner.focus(); |
1198 ]); |
1198 var offscreenRange = editor.dom.createRng(); |
1199 |
1199 offscreenRange.selectNodeContents(inner); |
1200 var validElements = settings.paste_word_valid_elements; |
1200 editor.selection.setRng(offscreenRange); |
1201 if (!validElements) { |
1201 setTimeout(function () { |
1202 validElements = ( |
1202 editor.selection.setRng(range); |
1203 '-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,' + |
1203 outer.parentNode.removeChild(outer); |
1204 '-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,' + |
1204 done(); |
1205 'td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody' |
1205 }, 0); |
1206 ); |
1206 }; |
1207 } |
1207 }; |
1208 |
1208 var getData = function (editor) { |
1209 // Setup strict schema |
1209 return { |
1210 var schema = new Schema({ |
1210 html: editor.selection.getContent({ contextual: true }), |
1211 valid_elements: validElements, |
1211 text: editor.selection.getContent({ format: 'text' }) |
1212 valid_children: '-li[p]' |
1212 }; |
1213 }); |
1213 }; |
1214 |
1214 var cut = function (editor) { |
1215 // Add style/class attribute to all element rules since the user might have removed them from |
1215 return function (evt) { |
1216 // paste_word_valid_elements config option and we need to check them for properties |
1216 if (editor.selection.isCollapsed() === false) { |
1217 Tools.each(schema.elements, function(rule) { |
1217 setClipboardData(evt, getData(editor), fallback(editor), function () { |
1218 /*eslint dot-notation:0*/ |
1218 setTimeout(function () { |
1219 if (!rule.attributes["class"]) { |
1219 editor.execCommand('Delete'); |
1220 rule.attributes["class"] = {}; |
1220 }, 0); |
1221 rule.attributesOrder.push("class"); |
1221 }); |
1222 } |
1222 } |
1223 |
1223 }; |
1224 if (!rule.attributes.style) { |
1224 }; |
1225 rule.attributes.style = {}; |
1225 var copy = function (editor) { |
1226 rule.attributesOrder.push("style"); |
1226 return function (evt) { |
1227 } |
1227 if (editor.selection.isCollapsed() === false) { |
1228 }); |
1228 setClipboardData(evt, getData(editor), fallback(editor), noop); |
1229 |
1229 } |
1230 // Parse HTML into DOM structure |
1230 }; |
1231 var domParser = new DomParser({}, schema); |
1231 }; |
1232 |
1232 var register$1 = function (editor) { |
1233 // Filter styles to remove "mso" specific styles and convert some of them |
1233 editor.on('cut', cut(editor)); |
1234 domParser.addAttributeFilter('style', function(nodes) { |
1234 editor.on('copy', copy(editor)); |
1235 var i = nodes.length, node; |
1235 }; |
1236 |
1236 var $_32blojj3jjgwecv4 = { register: register$1 }; |
1237 while (i--) { |
1237 |
1238 node = nodes[i]; |
1238 var global$10 = tinymce.util.Tools.resolve('tinymce.dom.RangeUtils'); |
1239 node.attr('style', filterStyles(node, node.attr('style'))); |
1239 |
1240 |
1240 var getCaretRangeFromEvent = function (editor, e) { |
1241 // Remove pointess spans |
1241 return global$10.getCaretRangeFromPoint(e.clientX, e.clientY, editor.getDoc()); |
1242 if (node.name == 'span' && node.parent && !node.attributes.length) { |
1242 }; |
1243 node.unwrap(); |
1243 var isPlainTextFileUrl = function (content) { |
1244 } |
1244 var plainTextContent = content['text/plain']; |
1245 } |
1245 return plainTextContent ? plainTextContent.indexOf('file://') === 0 : false; |
1246 }); |
1246 }; |
1247 |
1247 var setFocusedRange = function (editor, rng) { |
1248 // Check the class attribute for comments or del items and remove those |
1248 editor.focus(); |
1249 domParser.addAttributeFilter('class', function(nodes) { |
1249 editor.selection.setRng(rng); |
1250 var i = nodes.length, node, className; |
1250 }; |
1251 |
1251 var setup = function (editor, clipboard, draggingInternallyState) { |
1252 while (i--) { |
1252 if ($_xr8b0ikjjgwectl.shouldBlockDrop(editor)) { |
1253 node = nodes[i]; |
1253 editor.on('dragend dragover draggesture dragdrop drop drag', function (e) { |
1254 |
1254 e.preventDefault(); |
1255 className = node.attr('class'); |
1255 e.stopPropagation(); |
1256 if (/^(MsoCommentReference|MsoCommentText|msoDel)$/i.test(className)) { |
1256 }); |
1257 node.remove(); |
1257 } |
1258 } |
1258 if (!$_xr8b0ikjjgwectl.shouldPasteDataImages(editor)) { |
1259 |
1259 editor.on('drop', function (e) { |
1260 node.attr('class', null); |
1260 var dataTransfer = e.dataTransfer; |
1261 } |
1261 if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { |
1262 }); |
1262 e.preventDefault(); |
1263 |
1263 } |
1264 // Remove all del elements since we don't want the track changes code in the editor |
1264 }); |
1265 domParser.addNodeFilter('del', function(nodes) { |
1265 } |
1266 var i = nodes.length; |
1266 editor.on('drop', function (e) { |
1267 |
1267 var dropContent, rng; |
1268 while (i--) { |
1268 rng = getCaretRangeFromEvent(editor, e); |
1269 nodes[i].remove(); |
1269 if (e.isDefaultPrevented() || draggingInternallyState.get()) { |
1270 } |
1270 return; |
1271 }); |
1271 } |
1272 |
1272 dropContent = clipboard.getDataTransferItems(e.dataTransfer); |
1273 // Keep some of the links and anchors |
1273 var internal = clipboard.hasContentType(dropContent, $_4x13hjirjjgwecu1.internalHtmlMime()); |
1274 domParser.addNodeFilter('a', function(nodes) { |
1274 if ((!clipboard.hasHtmlOrText(dropContent) || isPlainTextFileUrl(dropContent)) && clipboard.pasteImageData(e, rng)) { |
1275 var i = nodes.length, node, href, name; |
1275 return; |
1276 |
1276 } |
1277 while (i--) { |
1277 if (rng && $_xr8b0ikjjgwectl.shouldFilterDrop(editor)) { |
1278 node = nodes[i]; |
1278 var content_1 = dropContent['mce-internal'] || dropContent['text/html'] || dropContent['text/plain']; |
1279 href = node.attr('href'); |
1279 if (content_1) { |
1280 name = node.attr('name'); |
1280 e.preventDefault(); |
1281 |
1281 global$2.setEditorTimeout(editor, function () { |
1282 if (href && href.indexOf('#_msocom_') != -1) { |
1282 editor.undoManager.transact(function () { |
1283 node.remove(); |
1283 if (dropContent['mce-internal']) { |
1284 continue; |
1284 editor.execCommand('Delete'); |
1285 } |
1285 } |
1286 |
1286 setFocusedRange(editor, rng); |
1287 if (href && href.indexOf('file://') === 0) { |
1287 content_1 = $_4bi2o9j0jjgwecui.trimHtml(content_1); |
1288 href = href.split('#')[1]; |
1288 if (!dropContent['text/html']) { |
1289 if (href) { |
1289 clipboard.pasteText(content_1); |
1290 href = '#' + href; |
1290 } else { |
1291 } |
1291 clipboard.pasteHtml(content_1, internal); |
1292 } |
1292 } |
1293 |
1293 }); |
1294 if (!href && !name) { |
1294 }); |
1295 node.unwrap(); |
1295 } |
1296 } else { |
1296 } |
1297 // Remove all named anchors that aren't specific to TOC, Footnotes or Endnotes |
1297 }); |
1298 if (name && !/^_?(?:toc|edn|ftn)/i.test(name)) { |
1298 editor.on('dragstart', function (e) { |
1299 node.unwrap(); |
1299 draggingInternallyState.set(true); |
1300 continue; |
1300 }); |
1301 } |
1301 editor.on('dragover dragend', function (e) { |
1302 |
1302 if ($_xr8b0ikjjgwectl.shouldPasteDataImages(editor) && draggingInternallyState.get() === false) { |
1303 node.attr({ |
1303 e.preventDefault(); |
1304 href: href, |
1304 setFocusedRange(editor, getCaretRangeFromEvent(editor, e)); |
1305 name: name |
1305 } |
1306 }); |
1306 if (e.type === 'dragend') { |
1307 } |
1307 draggingInternallyState.set(false); |
1308 } |
1308 } |
1309 }); |
1309 }); |
1310 |
1310 }; |
1311 // Parse into DOM structure |
1311 var $_b4etj0j4jjgwecv7 = { setup: setup }; |
1312 var rootNode = domParser.parse(content); |
1312 |
1313 |
1313 var setup$1 = function (editor) { |
1314 // Process DOM |
1314 var plugin = editor.plugins.paste; |
1315 if (settings.paste_convert_word_fake_lists !== false) { |
1315 var preProcess = $_xr8b0ikjjgwectl.getPreProcess(editor); |
1316 convertFakeListsToProperLists(rootNode); |
1316 if (preProcess) { |
1317 } |
1317 editor.on('PastePreProcess', function (e) { |
1318 |
1318 preProcess.call(plugin, plugin, e); |
1319 // Serialize DOM back to HTML |
1319 }); |
1320 e.content = new Serializer({}, schema).serialize(rootNode); |
1320 } |
1321 } |
1321 var postProcess = $_xr8b0ikjjgwectl.getPostProcess(editor); |
1322 }); |
1322 if (postProcess) { |
1323 } |
1323 editor.on('PastePostProcess', function (e) { |
1324 |
1324 postProcess.call(plugin, plugin, e); |
1325 WordFilter.isWordContent = isWordContent; |
1325 }); |
1326 |
1326 } |
1327 return WordFilter; |
1327 }; |
1328 }); |
1328 var $_c5bihmj6jjgwecva = { setup: setup$1 }; |
1329 |
1329 |
1330 // Included from: js/tinymce/plugins/paste/classes/Quirks.js |
1330 function addPreProcessFilter(editor, filterFunc) { |
1331 |
1331 editor.on('PastePreProcess', function (e) { |
1332 /** |
1332 e.content = filterFunc(editor, e.content, e.internal, e.wordContent); |
1333 * Quirks.js |
1333 }); |
1334 * |
1334 } |
1335 * Copyright, Moxiecode Systems AB |
1335 function addPostProcessFilter(editor, filterFunc) { |
1336 * Released under LGPL License. |
1336 editor.on('PastePostProcess', function (e) { |
1337 * |
1337 filterFunc(editor, e.node); |
1338 * License: http://www.tinymce.com/license |
1338 }); |
1339 * Contributing: http://www.tinymce.com/contributing |
1339 } |
1340 */ |
1340 function removeExplorerBrElementsAfterBlocks(editor, html) { |
1341 |
1341 if (!$_dfatuiivjjgwecu8.isWordContent(html)) { |
1342 /** |
1342 return html; |
1343 * This class contains various fixes for browsers. These issues can not be feature |
1343 } |
1344 * detected since we have no direct control over the clipboard. However we might be able |
1344 var blockElements = []; |
1345 * to remove some of these fixes once the browsers gets updated/fixed. |
1345 global$3.each(editor.schema.getBlockElements(), function (block, blockName) { |
1346 * |
1346 blockElements.push(blockName); |
1347 * @class tinymce.pasteplugin.Quirks |
1347 }); |
1348 * @private |
1348 var explorerBlocksRegExp = new RegExp('(?:<br> [\\s\\r\\n]+|<br>)*(<\\/?(' + blockElements.join('|') + ')[^>]*>)(?:<br> [\\s\\r\\n]+|<br>)*', 'g'); |
1349 */ |
1349 html = $_4bi2o9j0jjgwecui.filter(html, [[ |
1350 define("tinymce/pasteplugin/Quirks", [ |
1350 explorerBlocksRegExp, |
1351 "tinymce/Env", |
1351 '$1' |
1352 "tinymce/util/Tools", |
1352 ]]); |
1353 "tinymce/pasteplugin/WordFilter", |
1353 html = $_4bi2o9j0jjgwecui.filter(html, [ |
1354 "tinymce/pasteplugin/Utils" |
1354 [ |
1355 ], function(Env, Tools, WordFilter, Utils) { |
1355 /<br><br>/g, |
1356 "use strict"; |
1356 '<BR><BR>' |
1357 |
1357 ], |
1358 return function(editor) { |
1358 [ |
1359 function addPreProcessFilter(filterFunc) { |
1359 /<br>/g, |
1360 editor.on('BeforePastePreProcess', function(e) { |
1360 ' ' |
1361 e.content = filterFunc(e.content); |
1361 ], |
1362 }); |
1362 [ |
1363 } |
1363 /<BR><BR>/g, |
1364 |
1364 '<br>' |
1365 /** |
1365 ] |
1366 * Removes BR elements after block elements. IE9 has a nasty bug where it puts a BR element after each |
1366 ]); |
1367 * block element when pasting from word. This removes those elements. |
1367 return html; |
1368 * |
1368 } |
1369 * This: |
1369 function removeWebKitStyles(editor, content, internal, isWordHtml) { |
1370 * <p>a</p><br><p>b</p> |
1370 if (isWordHtml || internal) { |
1371 * |
1371 return content; |
1372 * Becomes: |
1372 } |
1373 * <p>a</p><p>b</p> |
1373 var webKitStylesSetting = $_xr8b0ikjjgwectl.getWebkitStyles(editor); |
1374 */ |
1374 var webKitStyles; |
1375 function removeExplorerBrElementsAfterBlocks(html) { |
1375 if ($_xr8b0ikjjgwectl.shouldRemoveWebKitStyles(editor) === false || webKitStylesSetting === 'all') { |
1376 // Only filter word specific content |
1376 return content; |
1377 if (!WordFilter.isWordContent(html)) { |
1377 } |
1378 return html; |
1378 if (webKitStylesSetting) { |
1379 } |
1379 webKitStyles = webKitStylesSetting.split(/[, ]/); |
1380 |
1380 } |
1381 // Produce block regexp based on the block elements in schema |
1381 if (webKitStyles) { |
1382 var blockElements = []; |
1382 var dom_1 = editor.dom, node_1 = editor.selection.getNode(); |
1383 |
1383 content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, function (all, before, value, after) { |
1384 Tools.each(editor.schema.getBlockElements(), function(block, blockName) { |
1384 var inputStyles = dom_1.parseStyle(dom_1.decode(value)); |
1385 blockElements.push(blockName); |
1385 var outputStyles = {}; |
1386 }); |
1386 if (webKitStyles === 'none') { |
1387 |
1387 return before + after; |
1388 var explorerBlocksRegExp = new RegExp( |
1388 } |
1389 '(?:<br> [\\s\\r\\n]+|<br>)*(<\\/?(' + blockElements.join('|') + ')[^>]*>)(?:<br> [\\s\\r\\n]+|<br>)*', |
1389 for (var i = 0; i < webKitStyles.length; i++) { |
1390 'g' |
1390 var inputValue = inputStyles[webKitStyles[i]], currentValue = dom_1.getStyle(node_1, webKitStyles[i], true); |
1391 ); |
1391 if (/color/.test(webKitStyles[i])) { |
1392 |
1392 inputValue = dom_1.toHex(inputValue); |
1393 // Remove BR:s from: <BLOCK>X</BLOCK><BR> |
1393 currentValue = dom_1.toHex(currentValue); |
1394 html = Utils.filter(html, [ |
1394 } |
1395 [explorerBlocksRegExp, '$1'] |
1395 if (currentValue !== inputValue) { |
1396 ]); |
1396 outputStyles[webKitStyles[i]] = inputValue; |
1397 |
1397 } |
1398 // IE9 also adds an extra BR element for each soft-linefeed and it also adds a BR for each word wrap break |
1398 } |
1399 html = Utils.filter(html, [ |
1399 outputStyles = dom_1.serializeStyle(outputStyles, 'span'); |
1400 [/<br><br>/g, '<BR><BR>'], // Replace multiple BR elements with uppercase BR to keep them intact |
1400 if (outputStyles) { |
1401 [/<br>/g, ' '], // Replace single br elements with space since they are word wrap BR:s |
1401 return before + ' style="' + outputStyles + '"' + after; |
1402 [/<BR><BR>/g, '<br>'] // Replace back the double brs but into a single BR |
1402 } |
1403 ]); |
1403 return before + after; |
1404 |
1404 }); |
1405 return html; |
1405 } else { |
1406 } |
1406 content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, '$1$3'); |
1407 |
1407 } |
1408 /** |
1408 content = content.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi, function (all, before, value, after) { |
1409 * WebKit has a nasty bug where the all computed styles gets added to style attributes when copy/pasting contents. |
1409 return before + ' style="' + value + '"' + after; |
1410 * This fix solves that by simply removing the whole style attribute. |
1410 }); |
1411 * |
1411 return content; |
1412 * The paste_webkit_styles option can be set to specify what to keep: |
1412 } |
1413 * paste_webkit_styles: "none" // Keep no styles |
1413 function removeUnderlineAndFontInAnchor(editor, root) { |
1414 * paste_webkit_styles: "all", // Keep all of them |
1414 editor.$('a', root).find('font,u').each(function (i, node) { |
1415 * paste_webkit_styles: "font-weight color" // Keep specific ones |
1415 editor.dom.remove(node, true); |
1416 * |
1416 }); |
1417 * @param {String} content Content that needs to be processed. |
1417 } |
1418 * @return {String} Processed contents. |
1418 var setup$2 = function (editor) { |
1419 */ |
1419 if (global$1.webkit) { |
1420 function removeWebKitStyles(content) { |
1420 addPreProcessFilter(editor, removeWebKitStyles); |
1421 // Passthrough all styles from Word and let the WordFilter handle that junk |
1421 } |
1422 if (WordFilter.isWordContent(content)) { |
1422 if (global$1.ie) { |
1423 return content; |
1423 addPreProcessFilter(editor, removeExplorerBrElementsAfterBlocks); |
1424 } |
1424 addPostProcessFilter(editor, removeUnderlineAndFontInAnchor); |
1425 |
1425 } |
1426 // Filter away styles that isn't matching the target node |
1426 }; |
1427 var webKitStyles = editor.settings.paste_webkit_styles; |
1427 var $_36tmgyj7jjgwecvc = { setup: setup$2 }; |
1428 |
1428 |
1429 if (editor.settings.paste_remove_styles_if_webkit === false || webKitStyles == "all") { |
1429 var curry = function (f) { |
1430 return content; |
1430 var x = []; |
1431 } |
1431 for (var _i = 1; _i < arguments.length; _i++) { |
1432 |
1432 x[_i - 1] = arguments[_i]; |
1433 if (webKitStyles) { |
1433 } |
1434 webKitStyles = webKitStyles.split(/[, ]/); |
1434 var args = new Array(arguments.length - 1); |
1435 } |
1435 for (var i = 1; i < arguments.length; i++) |
1436 |
1436 args[i - 1] = arguments[i]; |
1437 // Keep specific styles that doesn't match the current node computed style |
1437 return function () { |
1438 if (webKitStyles) { |
1438 var x = []; |
1439 var dom = editor.dom, node = editor.selection.getNode(); |
1439 for (var _i = 0; _i < arguments.length; _i++) { |
1440 |
1440 x[_i] = arguments[_i]; |
1441 content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, function(all, before, value, after) { |
1441 } |
1442 var inputStyles = dom.parseStyle(value, 'span'), outputStyles = {}; |
1442 var newArgs = new Array(arguments.length); |
1443 |
1443 for (var j = 0; j < newArgs.length; j++) |
1444 if (webKitStyles === "none") { |
1444 newArgs[j] = arguments[j]; |
1445 return before + after; |
1445 var all = args.concat(newArgs); |
1446 } |
1446 return f.apply(null, all); |
1447 |
1447 }; |
1448 for (var i = 0; i < webKitStyles.length; i++) { |
1448 }; |
1449 var inputValue = inputStyles[webKitStyles[i]], currentValue = dom.getStyle(node, webKitStyles[i], true); |
1449 |
1450 |
1450 var stateChange = function (editor, clipboard, e) { |
1451 if (/color/.test(webKitStyles[i])) { |
1451 var ctrl = e.control; |
1452 inputValue = dom.toHex(inputValue); |
1452 ctrl.active(clipboard.pasteFormat.get() === 'text'); |
1453 currentValue = dom.toHex(currentValue); |
1453 editor.on('PastePlainTextToggle', function (e) { |
1454 } |
1454 ctrl.active(e.state); |
1455 |
1455 }); |
1456 if (currentValue != inputValue) { |
1456 }; |
1457 outputStyles[webKitStyles[i]] = inputValue; |
1457 var register$2 = function (editor, clipboard) { |
1458 } |
1458 var postRender = curry(stateChange, editor, clipboard); |
1459 } |
1459 editor.addButton('pastetext', { |
1460 |
1460 active: false, |
1461 outputStyles = dom.serializeStyle(outputStyles, 'span'); |
1461 icon: 'pastetext', |
1462 if (outputStyles) { |
1462 tooltip: 'Paste as text', |
1463 return before + ' style="' + outputStyles + '"' + after; |
1463 cmd: 'mceTogglePlainTextPaste', |
1464 } |
1464 onPostRender: postRender |
1465 |
1465 }); |
1466 return before + after; |
1466 editor.addMenuItem('pastetext', { |
1467 }); |
1467 text: 'Paste as text', |
1468 } else { |
1468 selectable: true, |
1469 // Remove all external styles |
1469 active: clipboard.pasteFormat, |
1470 content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, '$1$3'); |
1470 cmd: 'mceTogglePlainTextPaste', |
1471 } |
1471 onPostRender: postRender |
1472 |
1472 }); |
1473 // Keep internal styles |
1473 }; |
1474 content = content.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi, function(all, before, value, after) { |
1474 var $_g9yhwdj8jjgwecvf = { register: register$2 }; |
1475 return before + ' style="' + value + '"' + after; |
1475 |
1476 }); |
1476 global.add('paste', function (editor) { |
1477 |
1477 if ($_15bf6siejjgwect1.hasProPlugin(editor) === false) { |
1478 return content; |
1478 var userIsInformedState = Cell(false); |
1479 } |
1479 var draggingInternallyState = Cell(false); |
1480 |
1480 var pasteFormat = Cell($_xr8b0ikjjgwectl.isPasteAsTextEnabled(editor) ? 'text' : 'html'); |
1481 // Sniff browsers and apply fixes since we can't feature detect |
1481 var clipboard = Clipboard(editor, pasteFormat); |
1482 if (Env.webkit) { |
1482 var quirks = $_36tmgyj7jjgwecvc.setup(editor); |
1483 addPreProcessFilter(removeWebKitStyles); |
1483 $_g9yhwdj8jjgwecvf.register(editor, clipboard); |
1484 } |
1484 $_fldd1mihjjgwecth.register(editor, clipboard, userIsInformedState); |
1485 |
1485 $_c5bihmj6jjgwecva.setup(editor); |
1486 if (Env.ie) { |
1486 $_32blojj3jjgwecv4.register(editor); |
1487 addPreProcessFilter(removeExplorerBrElementsAfterBlocks); |
1487 $_b4etj0j4jjgwecv7.setup(editor, clipboard, draggingInternallyState); |
1488 } |
1488 return $_6gtliyigjjgwecte.get(clipboard, quirks); |
1489 }; |
1489 } |
1490 }); |
1490 }); |
1491 |
1491 function Plugin () { |
1492 // Included from: js/tinymce/plugins/paste/classes/Plugin.js |
1492 } |
1493 |
1493 |
1494 /** |
1494 return Plugin; |
1495 * Plugin.js |
1495 |
1496 * |
1496 }()); |
1497 * Copyright, Moxiecode Systems AB |
1497 })(); |
1498 * Released under LGPL License. |
|
1499 * |
|
1500 * License: http://www.tinymce.com/license |
|
1501 * Contributing: http://www.tinymce.com/contributing |
|
1502 */ |
|
1503 |
|
1504 /** |
|
1505 * This class contains the tinymce plugin logic for the paste plugin. |
|
1506 * |
|
1507 * @class tinymce.pasteplugin.Plugin |
|
1508 * @private |
|
1509 */ |
|
1510 define("tinymce/pasteplugin/Plugin", [ |
|
1511 "tinymce/PluginManager", |
|
1512 "tinymce/pasteplugin/Clipboard", |
|
1513 "tinymce/pasteplugin/WordFilter", |
|
1514 "tinymce/pasteplugin/Quirks" |
|
1515 ], function(PluginManager, Clipboard, WordFilter, Quirks) { |
|
1516 var userIsInformed; |
|
1517 |
|
1518 PluginManager.add('paste', function(editor) { |
|
1519 var self = this, clipboard, settings = editor.settings; |
|
1520 |
|
1521 function togglePlainTextPaste() { |
|
1522 if (clipboard.pasteFormat == "text") { |
|
1523 this.active(false); |
|
1524 clipboard.pasteFormat = "html"; |
|
1525 } else { |
|
1526 clipboard.pasteFormat = "text"; |
|
1527 this.active(true); |
|
1528 |
|
1529 if (!userIsInformed) { |
|
1530 editor.windowManager.alert( |
|
1531 'Paste is now in plain text mode. Contents will now ' + |
|
1532 'be pasted as plain text until you toggle this option off.' |
|
1533 ); |
|
1534 |
|
1535 userIsInformed = true; |
|
1536 } |
|
1537 } |
|
1538 } |
|
1539 |
|
1540 self.clipboard = clipboard = new Clipboard(editor); |
|
1541 self.quirks = new Quirks(editor); |
|
1542 self.wordFilter = new WordFilter(editor); |
|
1543 |
|
1544 if (editor.settings.paste_as_text) { |
|
1545 self.clipboard.pasteFormat = "text"; |
|
1546 } |
|
1547 |
|
1548 if (settings.paste_preprocess) { |
|
1549 editor.on('PastePreProcess', function(e) { |
|
1550 settings.paste_preprocess.call(self, self, e); |
|
1551 }); |
|
1552 } |
|
1553 |
|
1554 if (settings.paste_postprocess) { |
|
1555 editor.on('PastePostProcess', function(e) { |
|
1556 settings.paste_postprocess.call(self, self, e); |
|
1557 }); |
|
1558 } |
|
1559 |
|
1560 editor.addCommand('mceInsertClipboardContent', function(ui, value) { |
|
1561 if (value.content) { |
|
1562 self.clipboard.pasteHtml(value.content); |
|
1563 } |
|
1564 |
|
1565 if (value.text) { |
|
1566 self.clipboard.pasteText(value.text); |
|
1567 } |
|
1568 }); |
|
1569 |
|
1570 // Block all drag/drop events |
|
1571 if (editor.paste_block_drop) { |
|
1572 editor.on('dragend dragover draggesture dragdrop drop drag', function(e) { |
|
1573 e.preventDefault(); |
|
1574 e.stopPropagation(); |
|
1575 }); |
|
1576 } |
|
1577 |
|
1578 // Prevent users from dropping data images on Gecko |
|
1579 if (!editor.settings.paste_data_images) { |
|
1580 editor.on('drop', function(e) { |
|
1581 var dataTransfer = e.dataTransfer; |
|
1582 |
|
1583 if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { |
|
1584 e.preventDefault(); |
|
1585 } |
|
1586 }); |
|
1587 } |
|
1588 |
|
1589 editor.addButton('pastetext', { |
|
1590 icon: 'pastetext', |
|
1591 tooltip: 'Paste as text', |
|
1592 onclick: togglePlainTextPaste, |
|
1593 active: self.clipboard.pasteFormat == "text" |
|
1594 }); |
|
1595 |
|
1596 editor.addMenuItem('pastetext', { |
|
1597 text: 'Paste as text', |
|
1598 selectable: true, |
|
1599 active: clipboard.pasteFormat, |
|
1600 onclick: togglePlainTextPaste |
|
1601 }); |
|
1602 }); |
|
1603 }); |
|
1604 |
|
1605 expose(["tinymce/pasteplugin/Utils"]); |
|
1606 })(this); |
|