filter word documents - add support for converting bullet points
[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         var listpara = doc.getElementsByClassName('MsoListParagraph');
146         while(listpara.length) {
147             this.replaceDocBullet(listpara.item(0));
148             //code
149         }
150     },
151     
152     replaceDocBullet : function(p)
153     {
154         // gather all the siblings.
155         var ns = p,
156             parent = p.parentNode,
157             doc = parent.ownerDocument,
158             items = []; 
159         while (ns) {
160             if (ns.nodeType != 1) {
161                 ns = ns.nextSibling;
162                 continue;
163             }
164             if (!ns.className.match(/MsoListParagraph/i)) {
165                 break;
166             }
167             items.push(ns);
168             ns = ns.nextSibling;
169             
170         }
171         var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
172         parent.insertBefore(ul, p);
173         var lvl = 0;
174         var stack = [ ul ];
175         var last_li = false;
176         items.forEach(function(n) {
177             parent.removeChild(n);
178             var spans = n.getElementsByTagName('span');
179             if (!spans.length || !n.isEqualNode(spans.item(0).parentNode)) {
180                 return; // skip it...
181             }
182             
183             var style = this.styleToObject(n);
184             if (typeof(style['mso-list']) == 'undefined') {
185                 return; // skip it.
186             }
187             n.removeChild(spans.item(0)); // remove the fake bullet.
188             var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1;
189             if (nlvl > lvl) {
190                 //new indent
191                 var nul = doc.createElement('ul'); // what about number lists...
192                 last_li.appendChild(nul);
193                 stack[nlvl] = nul;
194             }
195             lvl = nlvl;
196             
197             var nli = stack[nlvl].appendChild(doc.createElement('li'));
198             last_li = nli;
199             // copy children of p into nli
200             while(n.firstChild) {
201                 var fc = n.firstChild;
202                 n.removeChild(fc);
203                 nli.appendChild(fc);
204             }
205              
206             
207         },this);
208         
209         
210         
211         
212     }
213     
214     
215     
216 });