fix word filter on html editor
[roojs1] / Roo / htmleditor / FilterWord.js
1 /**
2  * @class Roo.htmleditor.FilterWord
3  * try and clean up all the mess that Word generates.
4  * 
5  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
6  
7  * @constructor
8  * Run a new Span Filter
9  * @param {Object} config Configuration options
10  */
11
12 Roo.htmleditor.FilterWord = function(cfg)
13 {
14     // no need to apply config.
15     this.replaceDocBullets(cfg.node);
16     
17    // this.walk(cfg.node);
18     
19     
20 }
21
22 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
23 {
24     tag: true,
25      
26     
27     /**
28      * Clean up MS wordisms...
29      */
30     replaceTag : function(node)
31     {
32          
33         // no idea what this does - span with text, replaceds with just text.
34         if(
35                 node.nodeName == 'SPAN' &&
36                 !node.hasAttributes() &&
37                 node.childNodes.length == 1 &&
38                 node.firstChild.nodeName == "#text"  
39         ) {
40             var textNode = node.firstChild;
41             node.removeChild(textNode);
42             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
43                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44             }
45             node.parentNode.insertBefore(textNode, node);
46             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
47                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
48             }
49             
50             node.parentNode.removeChild(node);
51             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
52         }
53         
54    
55         
56         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
57             node.parentNode.removeChild(node);
58             return false; // dont do chidlren
59         }
60         //Roo.log(node.tagName);
61         // remove - but keep children..
62         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
63             //Roo.log('-- removed');
64             while (node.childNodes.length) {
65                 var cn = node.childNodes[0];
66                 node.removeChild(cn);
67                 node.parentNode.insertBefore(cn, node);
68                 // move node to parent - and clean it..
69                 if (cn.nodeType == 1) {
70                     this.replaceTag(cn);
71                 }
72                 
73             }
74             node.parentNode.removeChild(node);
75             /// no need to iterate chidlren = it's got none..
76             //this.iterateChildren(node, this.cleanWord);
77             return false; // no need to iterate children.
78         }
79         // clean styles
80         if (node.className.length) {
81             
82             var cn = node.className.split(/\W+/);
83             var cna = [];
84             Roo.each(cn, function(cls) {
85                 if (cls.match(/Mso[a-zA-Z]+/)) {
86                     return;
87                 }
88                 cna.push(cls);
89             });
90             node.className = cna.length ? cna.join(' ') : '';
91             if (!cna.length) {
92                 node.removeAttribute("class");
93             }
94         }
95         
96         if (node.hasAttribute("lang")) {
97             node.removeAttribute("lang");
98         }
99         
100         if (node.hasAttribute("style")) {
101             
102             var styles = node.getAttribute("style").split(";");
103             var nstyle = [];
104             Roo.each(styles, function(s) {
105                 if (!s.match(/:/)) {
106                     return;
107                 }
108                 var kv = s.split(":");
109                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
110                     return;
111                 }
112                 // what ever is left... we allow.
113                 nstyle.push(s);
114             });
115             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
116             if (!nstyle.length) {
117                 node.removeAttribute('style');
118             }
119         }
120         return true; // do children
121         
122         
123         
124     },
125     
126     styleToObject: function(node)
127     {
128         var styles = (node.getAttribute("style") || '').split(";");
129         var ret = {};
130         Roo.each(styles, function(s) {
131             if (!s.match(/:/)) {
132                 return;
133             }
134             var kv = s.split(":");
135              
136             // what ever is left... we allow.
137             ret[kv[0]] = kv[1];
138         });
139         return ret;
140     },
141     
142     
143     replaceDocBullets : function(doc)
144     {
145         // this is a bit odd - but it appears some indents use ql-indent-1
146         
147         var listpara = doc.getElementsByClassName('ql-indent-1');
148         while(listpara.length) {
149             this.replaceDocBullet(listpara.item(0));
150         }
151         
152         var listpara = doc.getElementsByClassName('MsoListParagraph');
153         while(listpara.length) {
154             this.replaceDocBullet(listpara.item(0));
155         }
156     },
157     
158     replaceDocBullet : function(p)
159     {
160         // gather all the siblings.
161         var ns = p,
162             parent = p.parentNode,
163             doc = parent.ownerDocument,
164             items = []; 
165         while (ns) {
166             if (ns.nodeType != 1) {
167                 ns = ns.nextSibling;
168                 continue;
169             }
170             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
171                 break;
172             }
173             items.push(ns);
174             ns = ns.nextSibling;
175             
176         }
177         var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
178         parent.insertBefore(ul, p);
179         var lvl = 0;
180         var stack = [ ul ];
181         var last_li = false;
182         items.forEach(function(n) {
183             //Roo.log("got innertHMLT=" + n.innerHTML);
184             
185             var spans = n.getElementsByTagName('span');
186             if (!spans.length) {
187                 //Roo.log("No spans found");
188
189                 parent.removeChild(n);
190                 return; // skip it...
191             }
192            
193                 
194             
195             var style = {};
196             for(var i = 0; i < spans.length; i++) {
197             
198                 style = this.styleToObject(spans[i]);
199                 if (typeof(style['mso-list']) == 'undefined') {
200                     continue;
201                 }
202                 
203                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
204                 break;
205             }
206             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
207             style = this.styleToObject(n); // mo-list is from the parent node.
208             if (typeof(style['mso-list']) == 'undefined') {
209                 //Roo.log("parent is missing level");
210                 parent.removeChild(n);
211                 return;
212             }
213             
214             var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1;
215             if (nlvl > lvl && last_li) {
216                 //new indent
217                 var nul = doc.createElement('ul'); // what about number lists...
218                 last_li.appendChild(nul);
219                 stack[nlvl] = nul;
220             }
221             lvl = nlvl;
222             
223             var nli = stack[nlvl].appendChild(doc.createElement('li'));
224             last_li = nli;
225             nli.innerHTML = n.innerHTML;
226             //Roo.log("innerHTML = " + n.innerHTML);
227             parent.removeChild(n);
228             
229             // copy children of p into nli
230             /*while(n.firstChild) {
231                 var fc = n.firstChild;
232                 n.removeChild(fc);
233                 nli.appendChild(fc);
234             }*/
235              
236             
237         },this);
238         
239         
240         
241         
242     }
243     
244     
245     
246 });