extjs3.3 htmleditor各种修正和扩充

转载地址:http://floydd.iteye.com/blog/1326338

1.editor的iframe window的keydown事件绑定

由于htmleditor本身提供的specialkey event不给力,所以自己手动在init时增加更加精确的keydown事件来弥补

需要注意的是:chrome的事件必须绑定在body上,否则ENTER这种特殊的键无法触发

 

 

Javascript代码 
  1. var win = Ext.isIE ? ed.getDoc() : ed.getWin();  
  2. ...  
  3. Ext.EventManager.on(!Ext.isWebKit ? win : win.document.body,  
  4.                     'keydown'function(e) {  
  5.                         if (e.isSpecialKey())  
  6.                             this.fireEvent('_specialkey'this, e)  
  7.                     }, ed);  

 

 

2.chrome下htmleditor回车会有问题(光标位置不正确),发现源代码中extjs官网是加了2个br,结果是不对的,修正方法是在fixKeys方法中修改webkit部分,回车时插入\n(chrome会自动变成\n<br>),getvalue时干掉所有\n<br>,比较纠结的方法,但是能解决问题,代码 如下

 

 

Javascript代码 
  1. fixKeys : function() {  
  2.         if (Ext.isIE) {  
  3.             ...  
  4.                } else if (Ext.isOpera) {  
  5.             ...  
  6.                } else if (Ext.isWebKit) {  
  7.             return function(e) {  
  8.                 var k = e.getKey();  
  9.                 if (k == e.TAB) {  
  10.                     e.stopEvent();  
  11.                     this.execCmd('InsertText''\t');  
  12.                     this.deferFocus();  
  13.                 } else if (k == e.ENTER) {  
  14.                     e.stopEvent();  
  15.                     // fix chrome ENTER double pressed bug  
  16.                     <strong>this.execCmd('InsertText''\n');  
  17. </strong>                 this.deferFocus();  
  18.                 }  
  19.             };  
  20.         }  
  21.     }(),  
  22. getValue : function() {  
  23.   var ret = FloatHtmlEditor.superclass.getValue.apply(this, arguments);  
  24.   if (ret) {  
  25.    // fix edit grid panel compare startvalue & nowvalue  
  26.    // fix '\n' patch for webkit(chrome) when ENTER pressed  
  27.    ret = ret.replace(/^(&nbsp;|<br>|\s)*|(&nbsp;|<br>|\s)*$/ig, '')  
  28.             ret = ret.replace(/<div>(.+?)<\/div>/ig,'<br>$1')  
  29.   }  
  30.   if (!ret) {  
  31.    ret = '';  
  32.   }  
  33.   return ret;  
  34.  },  

 

 

FloatHtmlEditor 是一个用于嵌入在grid中的editor,主要是修改了edit的bar,能够浮动出来,节约grid中的编辑区域

其他扩展特性:

* copy/paste 简化所有copy内容为纯文本,包括word/excel等复杂的格式

* 能够自适应文字大小,自动扩展编辑区域

* 修正多个平台下的focus的问题(htmleditor默认的focus和其他field有区别,不能定位的文字末尾)

完整源码:

 

 

Javascript代码 
  1. var FloatHtmlEditor = Ext.extend(Ext.form.HtmlEditor, {  
  2.     fixKeys : function() {  
  3.         if (Ext.isIE) {  
  4.             return function(e) {  
  5.                 var k = e.getKey(), doc = this.getDoc(), r;  
  6.                 if (k == e.TAB) {  
  7.                     e.stopEvent();  
  8.                     r = doc.selection.createRange();  
  9.                     if (r) {  
  10.                         r.collapse(true);  
  11.                         r.pasteHTML('');  
  12.                         this.deferFocus();  
  13.                     }  
  14.                 } else if (k == e.ENTER) {  
  15.                     r = doc.selection.createRange();  
  16.                     if (r) {  
  17.                         var target = r.parentElement();  
  18.                         if (!target || target.tagName.toLowerCase() != 'li') {  
  19.                             e.stopEvent();  
  20.                             r.pasteHTML('<br />');  
  21.                             r.collapse(false);  
  22.                             r.select();  
  23.                         }  
  24.                     }  
  25.                 }  
  26.             };  
  27.         } else if (Ext.isOpera) {  
  28.             return function(e) {  
  29.                 var k = e.getKey();  
  30.                 if (k == e.TAB) {  
  31.                     e.stopEvent();  
  32.                     this.win.focus();  
  33.                     this.execCmd('InsertHTML''');  
  34.                     this.deferFocus();  
  35.                 }  
  36.             };  
  37.         } else if (Ext.isWebKit) {  
  38.             return function(e) {  
  39.                 var k = e.getKey();  
  40.                 if (k == e.TAB) {  
  41.                     e.stopEvent();  
  42.                     this.execCmd('InsertText''\t');  
  43.                     this.deferFocus();  
  44.                 } else if (k == e.ENTER) {  
  45.                     e.stopEvent();  
  46.                     // fix chrome ENTER double pressed bug  
  47.                     this.execCmd('InsertHTML''\n<br>');  
  48.                     this.deferFocus();  
  49.                 }  
  50.             };  
  51.         }  
  52.     }(),  
  53.     _measureDiv : null,  
  54.     _getFrame : function() {  
  55.         if (!this._frm)  
  56.             this._frm = this.getEl().parent().query('iframe')[0];  
  57.         return this._frm  
  58.     },  
  59.     _adjustHeight : function() {  
  60.         var t = Ext.fly(this._getFrame());  
  61.         var div = this._measureDiv  
  62.         if (!div) {  
  63.             div = this._measureDiv = this.__measureDiv = document  
  64.                     .createElement('div');  
  65.             div.style.position = 'absolute';  
  66.             // div.style.border = '1px solid red'  
  67.             div.style.top = '-1px';  
  68.             div.style.left = '-10000px'  
  69.             div.style.whiteSpace = 'normal';  
  70.             div.style.wordWrap = 'break-word';  
  71.             div.style.lineHeight = '15px'  
  72.             document.body.appendChild(div)  
  73.         }  
  74.         if (t.dom.contentWindow.document.body.innerHTML == '') {  
  75.             t.setHeight(this._min_h);  
  76.             return  
  77.         }  
  78.         if (div.innerHTML == t.dom.contentWindow.document.body.innerHTML)  
  79.             return;  
  80.         var width = parseInt(t.getWidth());  
  81.         Ext.isIE ? width -= 25 : width -= 7  
  82.         div.style.width = width + 'px'  
  83.         div.innerHTML = t.dom.contentWindow.document.body.innerHTML;  
  84.         this._adjust_h = div.offsetHeight  
  85.         if (this._adjust_h < this._min_h) {  
  86.             this._adjust_h = this._min_h;  
  87.         }  
  88.         t.setHeight(this._adjust_h);  
  89.     },  
  90.     onEditorEvent : function(e) {  
  91.         if (this.onSpecialKey) {  
  92.             this.onSpecialKey(e);  
  93.         }  
  94.         return FloatHtmlEditor.superclass.onEditorEvent.apply(this, arguments)  
  95.     },  
  96.     getValue : function() {  
  97.         var ret = FloatHtmlEditor.superclass.getValue.apply(this, arguments);  
  98.         if (ret) {  
  99.             // fix edit grid panel compare startvalue & nowvalue  
  100.             // fix '\n' patch for webkit(chrome) when ENTER pressed  
  101.             ret = ret.replace(/^(|<br>|\s)*|(|<br>|\s)*$/ig, '')  
  102.                         ret = ret.replace(/<div>(.+?)<\/div>/ig,'<br>$1')  
  103.         }  
  104.         if (!ret) {  
  105.             ret = '';  
  106.         }  
  107.         return ret;  
  108.     },  
  109.     markTeamMember : function() {  
  110.         this.teamCmp = true  
  111.     },  
  112.     initComponent : function() {  
  113.         this.valueEditToolbarId = Ext.id()  
  114.         this.onBlur = Ext.form.Field.prototype.onBlur;  
  115.         FloatHtmlEditor.superclass.initComponent.call(this);  
  116.         this.addEvents('_specialkey')  
  117.         this.on('afterrender'function() {  
  118.                     this.getToolbar().hide();  
  119.                 }, this)  
  120.         this.on('show'function() {  
  121.                     this._adjust_h = 0;  
  122.                     this._adjustHeight.defer(100, this);  
  123.                     this.focus()  
  124.                 }, this)  
  125.         this.on('initialize'function(ed) {  
  126.             var toolbarWin = this.toolbarWin = new Ext.Window({  
  127.                         html : '<div id="' + this.valueEditToolbarId  
  128.                                 + '" class=""></div>',  
  129.                         height : 255,  
  130.                         width : 50,  
  131.                         closeAction : 'hide',  
  132.                         resizable : false,  
  133.                         iconCls : 'icon-toolbox'  
  134.                     })  
  135.             toolbarWin.show();  
  136.             var xy = this.getHoverTarget().getEl().getXY();  
  137.             toolbarWin.setPagePosition(xy[0] + this.getHoverTarget().getWidth()  
  138.                             - toolbarWin.getWidth(), xy[1]);  
  139.             var el = this.getToolbar().getEl();  
  140.             var appentToEl = Ext.getDom(this.valueEditToolbarId);  
  141.             appentToEl.className = el.dom.parentNode.className;  
  142.             el.appendTo(appentToEl);  
  143.             this.getToolbar().show();  
  144.             var tbarRawRow = el.query('.x-toolbar-left-row')[0];  
  145.             var btnNodes = Ext.Element.fly(tbarRawRow).query('.x-toolbar-cell');  
  146.             Ext.each(btnNodes, function(btnNode) {  
  147.                         var btnEl = Ext.Element.fly(btnNode);  
  148.                         if (btnEl.query('.xtb-sep').length) {  
  149.                             btnEl.remove();  
  150.                         } else {  
  151.                             var newRow = Ext.Element.fly(tbarRawRow)  
  152.                                     .insertSibling({  
  153.                                                 tag : 'tr',  
  154.                                                 cls : 'x-toolbar-left-row'  
  155.                                             });  
  156.                             newRow.appendChild(btnNode);  
  157.                             var nel = Ext.Element.fly(btnNode);  
  158.                             nel.query('button')[0].setAttribute('unselectable',  
  159.                                     'on')  
  160.                             nel.on('click'function() {  
  161.                                         this.fireEvent('styleBtnClick')  
  162.                                     }, this)  
  163.                         }  
  164.                     }, this)  
  165.             toolbarWin.hide();  
  166.             if (!Ext.isIE) {  
  167.                 toolbarWin.on('move'function() {  
  168.                             ed.markTeamMember()  
  169.                         })  
  170.             }  
  171.             this.getEl().findParent("div", 2, true).query('.x-html-editor-tb')[0].style.display = 'none'  
  172.             Ext.EventManager.on(ed.getWin(), 'blur'function() {  
  173.                         if (this.adjustTimer) {  
  174.                             clearInterval(this.adjustTimer);  
  175.                             delete this.adjustTimer  
  176.                         }  
  177.                         if (this._measureDiv) {  
  178.                             this._measureDiv.innerHTML = ''  
  179.                         }  
  180.                         this._adjust_h = 0;  
  181.             (function   () {  
  182.                             var teamCmp = ed.teamCmp;  
  183.                             teamCmp ? delete ed.teamCmp : ed.onBlur.apply(ed,  
  184.                                     arguments)  
  185.                         }).defer(10, ed, arguments);  
  186.                     }, ed);  
  187.             ed.getWin().document.body.style.overflow = 'hidden'  
  188.             ed.getWin().document.body.style.whiteSpace = 'normal';  
  189.             ed.getWin().document.body.style.wordWrap = 'break-word';  
  190.             ed.getWin().document.body.style.lineHeight = '15px';  
  191.             Ext.EventManager.on(ed.getWin(), 'focus'function() {  
  192.                         if (this.teamCmp)  
  193.                             delete this.teamCmp  
  194.                     }.dg(this), ed);  
  195.             var win = Ext.isIE ? ed.getDoc() : ed.getWin()  
  196.             Ext.EventManager.on(Ext.isIE ? ed.getDoc().documentElement : ed  
  197.                             .getWin(), 'paste'function(e) {  
  198.                         var html = ed.getDoc().body.innerHTML;  
  199.                         if (html.length >= 4  
  200.                                 && html.substring(html.length - 4) == '<br>') {  
  201.                             ed._beforePasteHTMLPos = html.length - 4;  
  202.                         } else {  
  203.                             ed._beforePasteHTMLPos = html.length  
  204.                         }  
  205.                     })  
  206.             // fix chrome keydown problem  
  207.             Ext.EventManager.on(!Ext.isWebKit ? win : win.document.body,  
  208.                     'keydown'function(e) {  
  209.                         if (!this.adjustTimer) {  
  210.                             this.adjustTimer = setInterval(function() {  
  211.                                         this._adjustHeight();  
  212.                                     }.dg(this), 200);  
  213.                         }  
  214.                         if (e.isSpecialKey())  
  215.                             this.fireEvent('_specialkey'this, e)  
  216.                     }, ed);  
  217.             // fix IE first range select  
  218.             this.moveCursorToEnd()  
  219.         }, this)  
  220.     },  
  221.     // public >>>  
  222.     getHoverTarget : Ext.emptyFn,  
  223.     toolbarWin : null,  
  224.     valueEditToolbarId : null,  
  225.     // public <<<  
  226.     _adjust_h : 0,  
  227.     _min_h : 34,  
  228.     setSize : function(w, h) {  
  229.         if (typeof w == 'object') {  
  230.             h = w.height  
  231.             w = w.width  
  232.         }  
  233.         if (this._adjust_h == 0)  
  234.             this._min_h = h - 4;  
  235.         FloatHtmlEditor.superclass.setSize.apply(this, arguments);  
  236.         var t = Ext.Element.fly(this.getEl().parent().query('iframe')[0]);  
  237.         t.setHeight(this._adjust_h ? this._adjust_h : this._min_h);  
  238.     },  
  239.     focus : function(st, delay) {  
  240.         var t = this.getWin();  
  241.         if (this._focus_delay)  
  242.             clearTimeout(this._focus_delay)  
  243.         this._focus_delay = window.setTimeout(function() {  
  244.                     t.focus();  
  245.                 }, 10);  
  246.     },  
  247.     moveCursorToEnd : function() {  
  248.         this.focusPatch()  
  249.     },  
  250.     focusPatch : function() {  
  251.         var ed = this;  
  252.         if (!ed.win)  
  253.             return  
  254.         var doc = ed.getDoc();  
  255.         ed.win.focus();  
  256.         if (Ext.isIE) {  
  257.             var r = doc.selection.createRange();  
  258.             if (r) {  
  259.                 r.moveStart('character', 1000);  
  260.                 r.collapse(true);  
  261.                 r.select();  
  262.             }  
  263.         } else {  
  264.             var contentDoc = doc;  
  265.             var range = contentDoc.createRange();  
  266.             var lastNode = contentDoc.body.childNodes[contentDoc.body.childNodes.length  
  267.                     - 1];  
  268.             var end = 0;  
  269.             var selection = ed.getWin().getSelection();  
  270.             range.setStart(contentDoc.body.firstChild, 0);  
  271.             if (lastNode.nodeType == 3) {  
  272.                 end = lastNode.textContent.length;  
  273.                 range.setEnd(lastNode, end);  
  274.             } else {  
  275.                 range.setEndAfter(lastNode)  
  276.             }  
  277.             selection.removeAllRanges();  
  278.             // chorme need the range collapsed before add to selection  
  279.             range.collapse(false);  
  280.             selection.addRange(range);  
  281.         }  
  282.     },  
  283.     showTBWin : function() {  
  284.         var tbwin = this.toolbarWin  
  285.         if (tbwin) {  
  286.             var a = tbwin.toFront  
  287.             tbwin.toFront = function() {  
  288.             }  
  289.             tbwin.show()  
  290.             tbwin.toFront = a  
  291.         }  
  292.     },  
  293.     // clean all tags  
  294.     syncValue : function() {  
  295.         if (this.initialized) {  
  296.             var bd = this.getEditorBody();  
  297.             var len = bd.innerHTML.length;  
  298.             FloatHtmlEditor.superclass.syncValue.call(this);  
  299.             if (this._beforePasteHTMLPos != null) {  
  300.                 this.syncValue1();  
  301.                 var pastehtml = bd.innerHTML  
  302.                 // .substring(this._beforePasteHTMLPos);  
  303.                 // replace all tags,only plain text from clipboard  
  304.                 var adjustpastehtml = pastehtml  
  305.                         // trim spaces between tags  
  306.                         // replace \n for excel paste  
  307.                         .replace(/\s+/ig, '')  
  308.                         // replace tag except for <br>  
  309.                         .replace(/<(?!br).*?>/ig, '')  
  310.                 if (pastehtml != adjustpastehtml) {  
  311.                     bd.innerHTML = adjustpastehtml;  
  312.                 }  
  313.                 delete this._beforePasteHTMLPos;  
  314.             }  
  315.         }  
  316.     },  
  317.     destroy : function() {  
  318.         if (this._measureDiv) {  
  319.             this._measureDiv.parentNode.removeChild(this._measureDiv);  
  320.             delete this._measureDiv  
  321.         }  
  322.         if (this.adjustTimer) {  
  323.             clearInterval(this.adjustTimer)  
  324.             delete this.adjustTimer  
  325.         }  
  326.         FloatHtmlEditor.superclass.destroy.call(this)  
  327.     },  
  328.     enableAlignments : false,  
  329.     enableFont : false,  
  330.     enableLinks : false,  
  331.     enableSourceEdit : true,  
  332.     // 清理excel,word copy paste的内容,code from HtmlLintEditor  
  333.     dirtyHtmlTags : [  
  334.             // http://stackoverflow.com/questions/2875027/clean-microsoft-word-pasted-text-using-javascript  
  335.             // http://stackoverflow.com/questions/1068280/javascript-regex-multiline-flag-doesnt-work  
  336.             {  
  337.         regex : /<!--[\s\S]*?-->/gi,  
  338.         replaceVal : ""  
  339.     },  
  340.             // http://www.1stclassmedia.co.uk/developers/clean-ms-word-formatting.php  
  341.             {  
  342.                 regex : /<\\?\?xml[^>]*>/gi,  
  343.                 replaceVal : ""  
  344.             }, {  
  345.                 regex : /<\/?\w+:[^>]*>/gi,  
  346.                 replaceVal : ""  
  347.             }, // e.g. <o:p...  
  348.             {  
  349.                 regex : /\s*MSO[-:][^;"']*/gi,  
  350.                 replaceVal : ""  
  351.             }, {  
  352.                 regex : /\s*MARGIN[-:][^;"']*/gi,  
  353.                 replaceVal : ""  
  354.             }, {  
  355.                 regex : /\s*PAGE[-:][^;"']*/gi,  
  356.                 replaceVal : ""  
  357.             }, {  
  358.                 regex : /\s*TAB[-:][^;"']*/gi,  
  359.                 replaceVal : ""  
  360.             }, {  
  361.                 regex : /\s*LINE[-:][^;"']*/gi,  
  362.                 replaceVal : ""  
  363.             }, {  
  364.                 regex : /\s*FONT-SIZE[^;"']*/gi,  
  365.                 replaceVal : ""  
  366.             }, {  
  367.                 regex : /\s*LANG=(["'])[^"']*?\1/gi,  
  368.                 replaceVal : ""  
  369.             },  
  370.             // keep new line  
  371.             {  
  372.                 regex : /<(P)[^>]*>([\s\S]*?)<\/\1>/gi,  
  373.                 replaceVal : "$2<br>"  
  374.             }, {  
  375.                 regex : /<(H\d)[^>]*>([\s\S]*?)<\/\1>/gi,  
  376.                 replaceVal : "$2"  
  377.             },  
  378.             {  
  379.                 regex : /\s*\w+=(["'])((|\s|;)*|\s*;+[^"']*?|[^"']*?;{2,})\1/gi,  
  380.                 replaceVal : ""  
  381.             }, {  
  382.                 regex : /<span[^>]*>(|\s)*<\/span>/gi,  
  383.                 replaceVal : ""  
  384.             },  
  385.             // {regex: /<([^\s>]+)[^>]*>(|\s)*<\/\1>/gi, replaceVal: ""},  
  386.             // http://www.codinghorror.com/blog/2006/01/cleaning-words-nasty-html.html  
  387.             {  
  388.                 regex : /<(\/?title|\/?meta|\/?style|\/?st\d|\/?head|\/?html|\/?body|\/?link|!\[)[^>]*?>/gi,  
  389.                 replaceVal : ""  
  390.             }, {  
  391.                 regex : /(\n(\r)?){2,}/gi,  
  392.                 replaceVal : ""  
  393.             }],  
  394.     syncValue1 : function() {  
  395.         if (this.initialized) {  
  396.             var bd = this.getEditorBody();  
  397.             var html = bd.innerHTML;  
  398.             if (this.hasDirtyHtmlTags(html)) {  
  399.                 // Note: the selection will be lost...  
  400.                 bd.innerHTML = this.cleanHtml(html);  
  401.                 if (Ext.isGecko) {  
  402.                     // Gecko hack, see:  
  403.                     // https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8  
  404.                     this.setDesignMode(false); // toggle off first  
  405.                     this.setDesignMode(true);  
  406.                 }  
  407.             }  
  408.         }  
  409.     },  
  410.     hasDirtyHtmlTags : function(html) {  
  411.         if (!html)  
  412.             return;  
  413.         var hasDirtyHtmlTags = false;  
  414.         Ext.each(this.dirtyHtmlTags, function(tag, idx) {  
  415.                     return !(hasDirtyHtmlTags = html.match(tag.regex));  
  416.                 });  
  417.         return hasDirtyHtmlTags;  
  418.     },  
  419.     cleanHtml : function(html) {  
  420.         if (!html)  
  421.             return;  
  422.         Ext.each(this.dirtyHtmlTags, function(tag, idx) {  
  423.                     html = html.replace(tag.regex, tag.replaceVal);  
  424.                 });  
  425.         // http://www.tim-jarrett.com/labs_javascript_scrub_word.php  
  426.         html = html.replace(new RegExp(String.fromCharCode(8220), 'gi'), '"'); // “  
  427.         html = html.replace(new RegExp(String.fromCharCode(8221), 'gi'), '"'); // ”  
  428.         html = html.replace(new RegExp(String.fromCharCode(8216), 'gi'), "'"); // ‘  
  429.         html = html.replace(new RegExp(String.fromCharCode(8217), 'gi'), "'"); // ‘  
  430.         html = html.replace(new RegExp(String.fromCharCode(8211), 'gi'), "-"); // –  
  431.         html = html.replace(new RegExp(String.fromCharCode(8212), 'gi'), "--"); // —  
  432.         html = html.replace(new RegExp(String.fromCharCode(189), 'gi'), "1/2"); // ½  
  433.         html = html.replace(new RegExp(String.fromCharCode(188), 'gi'), "1/4"); // ¼  
  434.         html = html.replace(new RegExp(String.fromCharCode(190), 'gi'), "3/4"); // ¾  
  435.         html = html.replace(new RegExp(String.fromCharCode(169), 'gi'), "(C)"); // ©  
  436.         html = html.replace(new RegExp(String.fromCharCode(174), 'gi'), "(R)"); // ®  
  437.         html = html.replace(new RegExp(String.fromCharCode(8230), 'gi'), "..."); // …  
  438.         return FloatHtmlEditor.superclass.cleanHtml.call(this, html);  
  439.     }  
  440. })  

 

你可能感兴趣的:(editor)