1 //<script type="text/javascript">
5 * Copyright(c) 2006-2007, Ext JS, LLC.
8 * http://www.extjs.com/license
14 * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
15 * - IE ? - no idea how much works there.
23 * @class Ext.form.HtmlEditor
24 * @extends Ext.form.Field
25 * Provides a lightweight HTML Editor component.
26 * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
28 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
29 * supported by this editor.</b><br/><br/>
30 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
31 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
33 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
35 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
39 * @cfg {String} createLinkText The default text for the create link prompt
41 createLinkText : 'Please enter the URL for the link:',
43 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45 defaultLinkValue : 'http:/'+'/',
48 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
53 * @cfg {Number} height (in pixels)
57 * @cfg {Number} width (in pixels)
62 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
71 validationEvent : false,
75 sourceEditMode : false,
76 onFocus : Roo.emptyFn,
80 defaultAutoCreate : { // modified by initCompnoent..
82 style:"width:500px;height:300px;",
87 initComponent : function(){
91 * Fires when the editor is fully initialized (including the iframe)
92 * @param {HtmlEditor} this
97 * Fires when the editor is first receives the focus. Any insertion must wait
98 * until after this event.
99 * @param {HtmlEditor} this
104 * Fires before the textarea is updated with content from the editor iframe. Return false
105 * to cancel the sync.
106 * @param {HtmlEditor} this
107 * @param {String} html
112 * Fires before the iframe editor is updated with content from the textarea. Return false
113 * to cancel the push.
114 * @param {HtmlEditor} this
115 * @param {String} html
120 * Fires when the textarea is updated with content from the editor iframe.
121 * @param {HtmlEditor} this
122 * @param {String} html
127 * Fires when the iframe editor is updated with content from the textarea.
128 * @param {HtmlEditor} this
129 * @param {String} html
133 * @event editmodechange
134 * Fires when the editor switches edit modes
135 * @param {HtmlEditor} this
136 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
138 editmodechange: true,
141 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
142 * @param {HtmlEditor} this
146 this.defaultAutoCreate = {
148 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
154 * Protected method that will not generally be called directly. It
155 * is called when the editor creates its toolbar. Override this method if you need to
156 * add custom toolbar buttons.
157 * @param {HtmlEditor} editor
159 createToolbar : function(editor){
160 if (!editor.toolbars || !editor.toolbars.length) {
161 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
164 for (var i =0 ; i < editor.toolbars.length;i++) {
165 editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
166 editor.toolbars[i].init(editor);
173 * Protected method that will not generally be called directly. It
174 * is called when the editor initializes the iframe with HTML contents. Override this method if you
175 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
177 getDocMarkup : function(){
180 if (this.stylesheets === false) {
182 Roo.get(document.head).select('style').each(function(node) {
183 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
186 Roo.get(document.head).select('link').each(function(node) {
187 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
190 } else if (!this.stylesheets.length) {
192 st = '<style type="text/css">' +
193 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
196 Roo.each(this.stylesheets, function(s) {
197 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
202 return '<html><head>' + st +
203 //<style type="text/css">' +
204 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
206 ' </head><body></body></html>';
210 onRender : function(ct, position)
213 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
214 this.el.dom.style.border = '0 none';
215 this.el.dom.setAttribute('tabIndex', -1);
216 this.el.addClass('x-hidden');
217 if(Roo.isIE){ // fix IE 1px bogus margin
218 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
220 this.wrap = this.el.wrap({
221 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
224 if (this.resizable) {
225 this.resizeEl = new Roo.Resizable(this.wrap, {
229 minHeight : this.height,
231 handles : this.resizable,
234 resize : function(r, w, h) {
235 _t.onResize(w,h); // -something
242 this.frameId = Roo.id();
244 this.createToolbar(this);
248 var iframe = this.wrap.createChild({
253 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
257 // console.log(iframe);
258 //this.wrap.dom.appendChild(iframe);
260 this.iframe = iframe.dom;
264 this.doc.designMode = 'on';
267 this.doc.write(this.getDocMarkup());
271 var task = { // must defer to wait for browser to be ready
273 //console.log("run task?" + this.doc.readyState);
275 if(this.doc.body || this.doc.readyState == 'complete'){
277 this.doc.designMode="on";
281 Roo.TaskMgr.stop(task);
282 this.initEditor.defer(10, this);
289 Roo.TaskMgr.start(task);
292 this.setSize(this.wrap.getSize());
295 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
296 // should trigger onReize..
301 onResize : function(w, h)
303 //Roo.log('resize: ' +w + ',' + h );
304 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
305 if(this.el && this.iframe){
306 if(typeof w == 'number'){
307 var aw = w - this.wrap.getFrameWidth('lr');
308 this.el.setWidth(this.adjustWidth('textarea', aw));
309 this.iframe.style.width = aw + 'px';
311 if(typeof h == 'number'){
313 for (var i =0; i < this.toolbars.length;i++) {
314 // fixme - ask toolbars for heights?
315 tbh += this.toolbars[i].tb.el.getHeight();
316 if (this.toolbars[i].footer) {
317 tbh += this.toolbars[i].footer.el.getHeight();
324 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
325 ah -= 5; // knock a few pixes off for look..
326 this.el.setHeight(this.adjustWidth('textarea', ah));
327 this.iframe.style.height = ah + 'px';
329 (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
336 * Toggles the editor between standard and source edit mode.
337 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
339 toggleSourceEdit : function(sourceEditMode){
341 this.sourceEditMode = sourceEditMode === true;
343 if(this.sourceEditMode){
346 this.iframe.className = 'x-hidden';
347 this.el.removeClass('x-hidden');
348 this.el.dom.removeAttribute('tabIndex');
353 this.iframe.className = '';
354 this.el.addClass('x-hidden');
355 this.el.dom.setAttribute('tabIndex', -1);
358 this.setSize(this.wrap.getSize());
359 this.fireEvent('editmodechange', this, this.sourceEditMode);
362 // private used internally
363 createLink : function(){
364 var url = prompt(this.createLinkText, this.defaultLinkValue);
365 if(url && url != 'http:/'+'/'){
366 this.relayCmd('createlink', url);
370 // private (for BoxComponent)
371 adjustSize : Roo.BoxComponent.prototype.adjustSize,
373 // private (for BoxComponent)
374 getResizeEl : function(){
378 // private (for BoxComponent)
379 getPositionEl : function(){
384 initEvents : function(){
385 this.originalValue = this.getValue();
389 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
392 markInvalid : Roo.emptyFn,
394 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
397 clearInvalid : Roo.emptyFn,
399 setValue : function(v){
400 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
405 * Protected method that will not generally be called directly. If you need/want
406 * custom HTML cleanup, this is the method you should override.
407 * @param {String} html The HTML to be cleaned
408 * return {String} The cleaned HTML
410 cleanHtml : function(html){
413 if(Roo.isSafari){ // strip safari nonsense
414 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
417 if(html == ' '){
424 * Protected method that will not generally be called directly. Syncs the contents
425 * of the editor iframe with the textarea.
427 syncValue : function(){
428 if(this.initialized){
429 var bd = (this.doc.body || this.doc.documentElement);
430 //this.cleanUpPaste();
431 var html = bd.innerHTML;
433 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
434 var m = bs.match(/text-align:(.*?);/i);
436 html = '<div style="'+m[0]+'">' + html + '</div>';
439 html = this.cleanHtml(html);
440 if(this.fireEvent('beforesync', this, html) !== false){
441 this.el.dom.value = html;
442 this.fireEvent('sync', this, html);
448 * Protected method that will not generally be called directly. Pushes the value of the textarea
449 * into the iframe editor.
451 pushValue : function(){
452 if(this.initialized){
453 var v = this.el.dom.value;
458 if(this.fireEvent('beforepush', this, v) !== false){
459 var d = (this.doc.body || this.doc.documentElement);
462 this.el.dom.value = d.innerHTML;
463 this.fireEvent('push', this, v);
469 deferFocus : function(){
470 this.focus.defer(10, this);
475 if(this.win && !this.sourceEditMode){
482 assignDocWin: function()
484 var iframe = this.iframe;
487 this.doc = iframe.contentWindow.document;
488 this.win = iframe.contentWindow;
490 if (!Roo.get(this.frameId)) {
493 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
494 this.win = Roo.get(this.frameId).dom.contentWindow;
499 initEditor : function(){
500 //console.log("INIT EDITOR");
505 this.doc.designMode="on";
507 this.doc.write(this.getDocMarkup());
510 var dbody = (this.doc.body || this.doc.documentElement);
511 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
512 // this copies styles from the containing element into thsi one..
513 // not sure why we need all of this..
514 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
515 ss['background-attachment'] = 'fixed'; // w3c
516 dbody.bgProperties = 'fixed'; // ie
517 Roo.DomHelper.applyStyles(dbody, ss);
518 Roo.EventManager.on(this.doc, {
519 //'mousedown': this.onEditorEvent,
520 'mouseup': this.onEditorEvent,
521 'dblclick': this.onEditorEvent,
522 'click': this.onEditorEvent,
523 'keyup': this.onEditorEvent,
528 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
530 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
531 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
533 this.initialized = true;
535 this.fireEvent('initialize', this);
540 onDestroy : function(){
546 for (var i =0; i < this.toolbars.length;i++) {
547 // fixme - ask toolbars for heights?
548 this.toolbars[i].onDestroy();
551 this.wrap.dom.innerHTML = '';
557 onFirstFocus : function(){
562 this.activated = true;
563 for (var i =0; i < this.toolbars.length;i++) {
564 this.toolbars[i].onFirstFocus();
567 if(Roo.isGecko){ // prevent silly gecko errors
569 var s = this.win.getSelection();
570 if(!s.focusNode || s.focusNode.nodeType != 3){
571 var r = s.getRangeAt(0);
572 r.selectNodeContents((this.doc.body || this.doc.documentElement));
577 this.execCmd('useCSS', true);
578 this.execCmd('styleWithCSS', false);
581 this.fireEvent('activate', this);
585 adjustFont: function(btn){
586 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
587 //if(Roo.isSafari){ // safari
590 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
591 if(Roo.isSafari){ // safari
592 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
593 v = (v < 10) ? 10 : v;
594 v = (v > 48) ? 48 : v;
595 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
600 v = Math.max(1, v+adjust);
602 this.execCmd('FontSize', v );
605 onEditorEvent : function(e){
606 this.fireEvent('editorevent', this, e);
607 // this.updateToolbar();
608 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
611 insertTag : function(tg)
613 // could be a bit smarter... -> wrap the current selected tRoo..
615 this.execCmd("formatblock", tg);
619 insertText : function(txt)
623 range = this.createRange();
624 range.deleteContents();
625 //alert(Sender.getAttribute('label'));
627 range.insertNode(this.doc.createTextNode(txt));
631 relayBtnCmd : function(btn){
632 this.relayCmd(btn.cmd);
636 * Executes a Midas editor command on the editor document and performs necessary focus and
637 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
638 * @param {String} cmd The Midas command
639 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
641 relayCmd : function(cmd, value){
643 this.execCmd(cmd, value);
644 this.fireEvent('editorevent', this);
645 //this.updateToolbar();
650 * Executes a Midas editor command directly on the editor document.
651 * For visual commands, you should use {@link #relayCmd} instead.
652 * <b>This should only be called after the editor is initialized.</b>
653 * @param {String} cmd The Midas command
654 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
656 execCmd : function(cmd, value){
657 this.doc.execCommand(cmd, false, value === undefined ? null : value);
663 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
665 * @param {String} text
667 insertAtCursor : function(text){
673 var r = this.doc.selection.createRange();
684 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
689 if (win.getSelection && win.getSelection().getRangeAt) {
690 range = win.getSelection().getRangeAt(0);
691 node = range.createContextualFragment(text);
692 range.insertNode(node);
693 } else if (win.document.selection && win.document.selection.createRange) {
694 win.document.selection.createRange().pasteHTML(text);
696 this.execCmd('InsertHTML', text);
711 mozKeyPress : function(e){
713 var c = e.getCharCode(), cmd;
716 c = String.fromCharCode(c).toLowerCase();
728 this.cleanUpPaste.defer(100, this);
744 fixKeys : function(){ // load time branching for fastest keydown performance
747 var k = e.getKey(), r;
750 r = this.doc.selection.createRange();
753 r.pasteHTML('    ');
760 r = this.doc.selection.createRange();
762 var target = r.parentElement();
763 if(!target || target.tagName.toLowerCase() != 'li'){
765 r.pasteHTML('<br />');
771 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
772 this.cleanUpPaste.defer(100, this);
778 }else if(Roo.isOpera){
784 this.execCmd('InsertHTML','    ');
787 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
788 this.cleanUpPaste.defer(100, this);
793 }else if(Roo.isSafari){
799 this.execCmd('InsertText','\t');
803 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
804 this.cleanUpPaste.defer(100, this);
812 getAllAncestors: function()
814 var p = this.getSelectedNode();
817 a.push(p); // push blank onto stack..
818 p = this.getParentElement();
822 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
826 a.push(this.doc.body);
833 getSelection : function()
836 return Roo.isIE ? this.doc.selection : this.win.getSelection();
839 getSelectedNode: function()
841 // this may only work on Gecko!!!
843 // should we cache this!!!!
848 var range = this.createRange(this.getSelection()).cloneRange();
851 var parent = range.parentElement();
853 var testRange = range.duplicate();
854 testRange.moveToElementText(parent);
855 if (testRange.inRange(range)) {
858 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
861 parent = parent.parentElement;
866 // is ancestor a text element.
867 var ac = range.commonAncestorContainer;
868 if (ac.nodeType == 3) {
872 var ar = ac.childNodes;
875 var other_nodes = [];
876 var has_other_nodes = false;
877 for (var i=0;i<ar.length;i++) {
878 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
881 // fullly contained node.
883 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
888 // probably selected..
889 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
890 other_nodes.push(ar[i]);
894 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
899 has_other_nodes = true;
901 if (!nodes.length && other_nodes.length) {
904 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
910 createRange: function(sel)
912 // this has strange effects when using with
913 // top toolbar - not sure if it's a great idea.
914 //this.editor.contentWindow.focus();
915 if (typeof sel != "undefined") {
917 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
919 return this.doc.createRange();
922 return this.doc.createRange();
925 getParentElement: function()
929 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
931 var range = this.createRange(sel);
934 var p = range.commonAncestorContainer;
935 while (p.nodeType == 3) { // text node
946 * Range intersection.. the hard stuff...
950 * [ -- selected range --- ]
954 * if end is before start or hits it. fail.
955 * if start is after end or hits it fail.
957 * if either hits (but other is outside. - then it's not
963 // @see http://www.thismuchiknow.co.uk/?p=64.
964 rangeIntersectsNode : function(range, node)
966 var nodeRange = node.ownerDocument.createRange();
968 nodeRange.selectNode(node);
970 nodeRange.selectNodeContents(node);
973 var rangeStartRange = range.cloneRange();
974 rangeStartRange.collapse(true);
976 var rangeEndRange = range.cloneRange();
977 rangeEndRange.collapse(false);
979 var nodeStartRange = nodeRange.cloneRange();
980 nodeStartRange.collapse(true);
982 var nodeEndRange = nodeRange.cloneRange();
983 nodeEndRange.collapse(false);
985 return rangeStartRange.compareBoundaryPoints(
986 Range.START_TO_START, nodeEndRange) == -1 &&
987 rangeEndRange.compareBoundaryPoints(
988 Range.START_TO_START, nodeStartRange) == 1;
992 rangeCompareNode : function(range, node)
994 var nodeRange = node.ownerDocument.createRange();
996 nodeRange.selectNode(node);
998 nodeRange.selectNodeContents(node);
1002 range.collapse(true);
1004 nodeRange.collapse(true);
1006 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
1007 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
1009 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
1011 var nodeIsBefore = ss == 1;
1012 var nodeIsAfter = ee == -1;
1014 if (nodeIsBefore && nodeIsAfter)
1016 if (!nodeIsBefore && nodeIsAfter)
1017 return 1; //right trailed.
1019 if (nodeIsBefore && !nodeIsAfter)
1020 return 2; // left trailed.
1025 // private? - in a new class?
1026 cleanUpPaste : function()
1028 // cleans up the whole document..
1029 Roo.log('cleanuppaste');
1030 this.cleanUpChildren(this.doc.body);
1031 var clean = this.cleanWordChars(this.doc.body.innerHTML);
1032 if (clean != this.doc.body.innerHTML) {
1033 this.doc.body.innerHTML = clean;
1038 cleanWordChars : function(input) {
1039 var he = Roo.form.HtmlEditor;
1042 Roo.each(he.swapCodes, function(sw) {
1044 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1045 output = output.replace(swapper, sw[1]);
1051 cleanUpChildren : function (n)
1053 if (!n.childNodes.length) {
1056 for (var i = n.childNodes.length-1; i > -1 ; i--) {
1057 this.cleanUpChild(n.childNodes[i]);
1064 cleanUpChild : function (node)
1066 //console.log(node);
1067 if (node.nodeName == "#text") {
1068 // clean up silly Windows -- stuff?
1071 if (node.nodeName == "#comment") {
1072 node.parentNode.removeChild(node);
1073 // clean up silly Windows -- stuff?
1077 if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
1079 node.parentNode.removeChild(node);
1084 var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
1086 // remove <a name=....> as rendering on yahoo mailer is bored with this.
1088 if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
1089 remove_keep_children = true;
1092 if (remove_keep_children) {
1093 this.cleanUpChildren(node);
1094 // inserts everything just before this node...
1095 while (node.childNodes.length) {
1096 var cn = node.childNodes[0];
1097 node.removeChild(cn);
1098 node.parentNode.insertBefore(cn, node);
1100 node.parentNode.removeChild(node);
1104 if (!node.attributes || !node.attributes.length) {
1105 this.cleanUpChildren(node);
1109 function cleanAttr(n,v)
1112 if (v.match(/^\./) || v.match(/^\//)) {
1115 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
1118 Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
1119 node.removeAttribute(n);
1123 function cleanStyle(n,v)
1125 if (v.match(/expression/)) { //XSS?? should we even bother..
1126 node.removeAttribute(n);
1131 var parts = v.split(/;/);
1132 Roo.each(parts, function(p) {
1133 p = p.replace(/\s+/g,'');
1137 var l = p.split(':').shift().replace(/\s+/g,'');
1139 // only allow 'c whitelisted system attributes'
1140 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
1141 Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
1142 node.removeAttribute(n);
1152 for (var i = node.attributes.length-1; i > -1 ; i--) {
1153 var a = node.attributes[i];
1155 if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
1156 node.removeAttribute(a.name);
1159 if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
1160 cleanAttr(a.name,a.value); // fixme..
1163 if (a.name == 'style') {
1164 cleanStyle(a.name,a.value);
1166 /// clean up MS crap..
1167 // tecnically this should be a list of valid class'es..
1170 if (a.name == 'class') {
1171 if (a.value.match(/^Mso/)) {
1172 node.className = '';
1175 if (a.value.match(/body/)) {
1176 node.className = '';
1186 this.cleanUpChildren(node);
1192 // hide stuff that is not compatible
1210 * @cfg {String} fieldClass @hide
1213 * @cfg {String} focusClass @hide
1216 * @cfg {String} autoCreate @hide
1219 * @cfg {String} inputType @hide
1222 * @cfg {String} invalidClass @hide
1225 * @cfg {String} invalidText @hide
1228 * @cfg {String} msgFx @hide
1231 * @cfg {String} validateOnBlur @hide
1235 Roo.form.HtmlEditor.white = [
1236 'area', 'br', 'img', 'input', 'hr', 'wbr',
1238 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1239 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1240 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1241 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1242 'table', 'ul', 'xmp',
1244 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1247 'dir', 'menu', 'ol', 'ul', 'dl',
1253 Roo.form.HtmlEditor.black = [
1254 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1256 'base', 'basefont', 'bgsound', 'blink', 'body',
1257 'frame', 'frameset', 'head', 'html', 'ilayer',
1258 'iframe', 'layer', 'link', 'meta', 'object',
1259 'script', 'style' ,'title', 'xml' // clean later..
1261 Roo.form.HtmlEditor.clean = [
1262 'script', 'style', 'title', 'xml'
1264 Roo.form.HtmlEditor.remove = [
1269 Roo.form.HtmlEditor.ablack = [
1273 Roo.form.HtmlEditor.aclean = [
1274 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1278 Roo.form.HtmlEditor.pwhite= [
1279 'http', 'https', 'mailto'
1282 // white listed style attributes.
1283 Roo.form.HtmlEditor.cwhite= [
1289 Roo.form.HtmlEditor.swapCodes =[