fix html editor
[roojs1] / Roo / form / HtmlEditor.js
index 4cfde26..bdba027 100644 (file)
@@ -57,6 +57,13 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
      * @cfg {Number} width (in pixels)
      */   
     width: 500,
+    
+    /**
+     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+     * 
+     */
+    stylesheets: false,
+    
     // id of frame..
     frameId: false,
     
@@ -168,7 +175,35 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
      */
     getDocMarkup : function(){
-        return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
+        // body styles..
+        var st = '';
+        if (this.stylesheets === false) {
+            
+            Roo.get(document.head).select('style').each(function(node) {
+                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
+            });
+            
+            Roo.get(document.head).select('link').each(function(node) { 
+                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
+            });
+            
+        } else if (!this.stylesheets.length) {
+                // simple..
+                st = '<style type="text/css">' +
+                    'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+                   '</style>';
+        } else {
+            Roo.each(this.stylesheets, function(s) {
+                st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
+            });
+            
+        }
+        
+        return '<html><head>' + st  +
+            //<style type="text/css">' +
+            //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+            //'</style>' +
+            ' </head><body></body></html>';
     },
 
     // private
@@ -392,7 +427,7 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
     syncValue : function(){
         if(this.initialized){
             var bd = (this.doc.body || this.doc.documentElement);
-            this.cleanUpPaste();
+            //this.cleanUpPaste();
             var html = bd.innerHTML;
             if(Roo.isSafari){
                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
@@ -570,7 +605,7 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
     onEditorEvent : function(e){
         this.fireEvent('editorevent', this, e);
       //  this.updateToolbar();
-        this.syncValue();
+        this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
     },
 
     insertTag : function(tg)
@@ -664,6 +699,7 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
                     break;
                     case 'u':
                         cmd = 'underline';
+                        break;
                     case 'v':
                         this.cleanUpPaste.defer(100, this);
                         return;
@@ -785,7 +821,7 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
         
         
          
-        var range = this.createRange(this.getSelection());
+        var range = this.createRange(this.getSelection()).cloneRange();
         
         if (Roo.isIE) {
             var parent = range.parentElement();
@@ -803,8 +839,13 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
             return parent;
         }
         
+        // is ancestor a text element.
+        var ac =  range.commonAncestorContainer;
+        if (ac.nodeType == 3) {
+            ac = ac.parentNode;
+        }
         
-        var ar = range.commonAncestorContainer.childNodes;
+        var ar = ac.childNodes;
          
         var nodes = [];
         var other_nodes = [];
@@ -825,6 +866,7 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
                 other_nodes.push(ar[i]);
                 continue;
             }
+            // outer..
             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
                 continue;
             }
@@ -894,24 +936,32 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
      **/
     
     
-    // BC Hacks - cause I cant work out what i was trying to do..
+    // @see http://www.thismuchiknow.co.uk/?p=64.
     rangeIntersectsNode : function(range, node)
     {
-        var nodeRange = this.createRange(node);
+        var nodeRange = node.ownerDocument.createRange();
         try {
             nodeRange.selectNode(node);
-        }
-        catch (e) {
+        } catch (e) {
             nodeRange.selectNodeContents(node);
         }
-        // [ -- selected range --- ]
-        //                 [node]
-        // node start inside?
-     
-        var es = range.compareBoundaryPoints(Range.END_TO_START, nodeRange) ;
-        var se = range.compareBoundaryPoints(Range.START_TO_END, nodeRange) ;
-        // good = es=-1, se = 1
-        return   es = -1 & se =1 ;  
+    
+        var rangeStartRange = range.cloneRange();
+        rangeStartRange.collapse(true);
+    
+        var rangeEndRange = range.cloneRange();
+        rangeEndRange.collapse(false);
+    
+        var nodeStartRange = nodeRange.cloneRange();
+        nodeStartRange.collapse(true);
+    
+        var nodeEndRange = nodeRange.cloneRange();
+        nodeEndRange.collapse(false);
+    
+        return rangeStartRange.compareBoundaryPoints(
+                 Range.START_TO_START, nodeEndRange) == -1 &&
+               rangeEndRange.compareBoundaryPoints(
+                 Range.START_TO_START, nodeStartRange) == 1;
         
          
     },
@@ -923,16 +973,28 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
         } catch (e) {
             nodeRange.selectNodeContents(node);
         }
-        var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
-        var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
-
-        if (nodeIsBefore && !nodeIsAfter)
-            return 0;
-        if (!nodeIsBefore && nodeIsAfter)
-            return 1;
+        
+        
+        range.collapse(true);
+    
+        nodeRange.collapse(true);
+     
+        var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
+        var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
+         
+        //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
+        
+        var nodeIsBefore   =  ss == 1;
+        var nodeIsAfter    = ee == -1;
+        
         if (nodeIsBefore && nodeIsAfter)
-            return 2;
-
+            return 0; // outer
+        if (!nodeIsBefore && nodeIsAfter)
+            return 1; //right trailed.
+        
+        if (nodeIsBefore && !nodeIsAfter)
+            return 2;  // left trailed.
+        // fully contined.
         return 3;
     },
 
@@ -940,11 +1002,28 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
     cleanUpPaste :  function()
     {
         // cleans up the whole document..
-      //  console.log('cleanuppaste');
+         Roo.log('cleanuppaste');
         this.cleanUpChildren(this.doc.body);
+        var clean = this.cleanWordChars(this.doc.body.innerHTML);
+        if (clean != this.doc.body.innerHTML) {
+            this.doc.body.innerHTML = clean;
+        }
         
+    },
+    
+    cleanWordChars : function(input) {
+        var he = Roo.form.HtmlEditor;
+    
+        var output = input;
+        Roo.each(he.swapCodes, function(sw) { 
         
+            var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
+            output = output.replace(swapper, sw[1]);
+        });
+        return output;
     },
+    
+    
     cleanUpChildren : function (n)
     {
         if (!n.childNodes.length) {
@@ -977,7 +1056,16 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
             return;
             
         }
-        if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
+        
+        var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
+        
+        // remove <a name=....> as rendering on yahoo mailer is bored with this.
+        
+        if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
+            remove_keep_children = true;
+        }
+        
+        if (remove_keep_children) {
             this.cleanUpChildren(node);
             // inserts everything just before this node...
             while (node.childNodes.length) {
@@ -1024,6 +1112,7 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
                 }
                 var l = p.split(':').shift().replace(/\s+/g,'');
                 
+                // only allow 'c whitelisted system attributes'
                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
                     node.removeAttribute(n);
@@ -1051,10 +1140,17 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
                 cleanStyle(a.name,a.value);
             }
             /// clean up MS crap..
+            // tecnically this should be a list of valid class'es..
+            
+            
             if (a.name == 'class') {
                 if (a.value.match(/^Mso/)) {
                     node.className = '';
                 }
+                
+                if (a.value.match(/body/)) {
+                    node.className = '';
+                }
             }
             
             // style cleanup!?
@@ -1159,8 +1255,22 @@ Roo.form.HtmlEditor.pwhite= [
         'http',  'https',  'mailto'
 ];
 
+// white listed style attributes.
 Roo.form.HtmlEditor.cwhite= [
         'text-align',
         'font-size'
 ];
 
+
+Roo.form.HtmlEditor.swapCodes   =[ 
+    [    8211, "--" ], 
+    [    8212, "--" ], 
+    [    8216,  "'" ],  
+    [    8217, "'" ],  
+    [    8220, '"' ],  
+    [    8221, '"' ],  
+    [    8226, "*" ],  
+    [    8230, "..." ]
+]; 
+
+    
\ No newline at end of file