1 /** |
|
2 * Quirks.js |
|
3 * |
|
4 * Copyright, Moxiecode Systems AB |
|
5 * Released under LGPL License. |
|
6 * |
|
7 * License: http://www.tinymce.com/license |
|
8 * Contributing: http://www.tinymce.com/contributing |
|
9 */ |
|
10 |
|
11 /** |
|
12 * This class contains various fixes for browsers. These issues can not be feature |
|
13 * detected since we have no direct control over the clipboard. However we might be able |
|
14 * to remove some of these fixes once the browsers gets updated/fixed. |
|
15 * |
|
16 * @class tinymce.pasteplugin.Quirks |
|
17 * @private |
|
18 */ |
|
19 define("tinymce/pasteplugin/Quirks", [ |
|
20 "tinymce/Env", |
|
21 "tinymce/util/Tools", |
|
22 "tinymce/pasteplugin/WordFilter", |
|
23 "tinymce/pasteplugin/Utils" |
|
24 ], function(Env, Tools, WordFilter, Utils) { |
|
25 "use strict"; |
|
26 |
|
27 return function(editor) { |
|
28 function addPreProcessFilter(filterFunc) { |
|
29 editor.on('BeforePastePreProcess', function(e) { |
|
30 e.content = filterFunc(e.content); |
|
31 }); |
|
32 } |
|
33 |
|
34 /** |
|
35 * Removes BR elements after block elements. IE9 has a nasty bug where it puts a BR element after each |
|
36 * block element when pasting from word. This removes those elements. |
|
37 * |
|
38 * This: |
|
39 * <p>a</p><br><p>b</p> |
|
40 * |
|
41 * Becomes: |
|
42 * <p>a</p><p>b</p> |
|
43 */ |
|
44 function removeExplorerBrElementsAfterBlocks(html) { |
|
45 // Only filter word specific content |
|
46 if (!WordFilter.isWordContent(html)) { |
|
47 return html; |
|
48 } |
|
49 |
|
50 // Produce block regexp based on the block elements in schema |
|
51 var blockElements = []; |
|
52 |
|
53 Tools.each(editor.schema.getBlockElements(), function(block, blockName) { |
|
54 blockElements.push(blockName); |
|
55 }); |
|
56 |
|
57 var explorerBlocksRegExp = new RegExp( |
|
58 '(?:<br> [\\s\\r\\n]+|<br>)*(<\\/?(' + blockElements.join('|') + ')[^>]*>)(?:<br> [\\s\\r\\n]+|<br>)*', |
|
59 'g' |
|
60 ); |
|
61 |
|
62 // Remove BR:s from: <BLOCK>X</BLOCK><BR> |
|
63 html = Utils.filter(html, [ |
|
64 [explorerBlocksRegExp, '$1'] |
|
65 ]); |
|
66 |
|
67 // IE9 also adds an extra BR element for each soft-linefeed and it also adds a BR for each word wrap break |
|
68 html = Utils.filter(html, [ |
|
69 [/<br><br>/g, '<BR><BR>'], // Replace multiple BR elements with uppercase BR to keep them intact |
|
70 [/<br>/g, ' '], // Replace single br elements with space since they are word wrap BR:s |
|
71 [/<BR><BR>/g, '<br>'] // Replace back the double brs but into a single BR |
|
72 ]); |
|
73 |
|
74 return html; |
|
75 } |
|
76 |
|
77 /** |
|
78 * WebKit has a nasty bug where the all computed styles gets added to style attributes when copy/pasting contents. |
|
79 * This fix solves that by simply removing the whole style attribute. |
|
80 * |
|
81 * The paste_webkit_styles option can be set to specify what to keep: |
|
82 * paste_webkit_styles: "none" // Keep no styles |
|
83 * paste_webkit_styles: "all", // Keep all of them |
|
84 * paste_webkit_styles: "font-weight color" // Keep specific ones |
|
85 * |
|
86 * @param {String} content Content that needs to be processed. |
|
87 * @return {String} Processed contents. |
|
88 */ |
|
89 function removeWebKitStyles(content) { |
|
90 // Passthrough all styles from Word and let the WordFilter handle that junk |
|
91 if (WordFilter.isWordContent(content)) { |
|
92 return content; |
|
93 } |
|
94 |
|
95 // Filter away styles that isn't matching the target node |
|
96 var webKitStyles = editor.settings.paste_webkit_styles; |
|
97 |
|
98 if (editor.settings.paste_remove_styles_if_webkit === false || webKitStyles == "all") { |
|
99 return content; |
|
100 } |
|
101 |
|
102 if (webKitStyles) { |
|
103 webKitStyles = webKitStyles.split(/[, ]/); |
|
104 } |
|
105 |
|
106 // Keep specific styles that doesn't match the current node computed style |
|
107 if (webKitStyles) { |
|
108 var dom = editor.dom, node = editor.selection.getNode(); |
|
109 |
|
110 content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, function(all, before, value, after) { |
|
111 var inputStyles = dom.parseStyle(value, 'span'), outputStyles = {}; |
|
112 |
|
113 if (webKitStyles === "none") { |
|
114 return before + after; |
|
115 } |
|
116 |
|
117 for (var i = 0; i < webKitStyles.length; i++) { |
|
118 var inputValue = inputStyles[webKitStyles[i]], currentValue = dom.getStyle(node, webKitStyles[i], true); |
|
119 |
|
120 if (/color/.test(webKitStyles[i])) { |
|
121 inputValue = dom.toHex(inputValue); |
|
122 currentValue = dom.toHex(currentValue); |
|
123 } |
|
124 |
|
125 if (currentValue != inputValue) { |
|
126 outputStyles[webKitStyles[i]] = inputValue; |
|
127 } |
|
128 } |
|
129 |
|
130 outputStyles = dom.serializeStyle(outputStyles, 'span'); |
|
131 if (outputStyles) { |
|
132 return before + ' style="' + outputStyles + '"' + after; |
|
133 } |
|
134 |
|
135 return before + after; |
|
136 }); |
|
137 } else { |
|
138 // Remove all external styles |
|
139 content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, '$1$3'); |
|
140 } |
|
141 |
|
142 // Keep internal styles |
|
143 content = content.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi, function(all, before, value, after) { |
|
144 return before + ' style="' + value + '"' + after; |
|
145 }); |
|
146 |
|
147 return content; |
|
148 } |
|
149 |
|
150 // Sniff browsers and apply fixes since we can't feature detect |
|
151 if (Env.webkit) { |
|
152 addPreProcessFilter(removeWebKitStyles); |
|
153 } |
|
154 |
|
155 if (Env.ie) { |
|
156 addPreProcessFilter(removeExplorerBrElementsAfterBlocks); |
|
157 } |
|
158 }; |
|
159 }); |
|