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();
680 }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
682 this.execCmd('InsertHTML', text);
687 mozKeyPress : function(e){
689 var c = e.getCharCode(), cmd;
692 c = String.fromCharCode(c).toLowerCase();
704 this.cleanUpPaste.defer(100, this);
720 fixKeys : function(){ // load time branching for fastest keydown performance
723 var k = e.getKey(), r;
726 r = this.doc.selection.createRange();
729 r.pasteHTML('    ');
736 r = this.doc.selection.createRange();
738 var target = r.parentElement();
739 if(!target || target.tagName.toLowerCase() != 'li'){
741 r.pasteHTML('<br />');
747 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
748 this.cleanUpPaste.defer(100, this);
754 }else if(Roo.isOpera){
760 this.execCmd('InsertHTML','    ');
763 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
764 this.cleanUpPaste.defer(100, this);
769 }else if(Roo.isSafari){
775 this.execCmd('InsertText','\t');
779 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
780 this.cleanUpPaste.defer(100, this);
788 getAllAncestors: function()
790 var p = this.getSelectedNode();
793 a.push(p); // push blank onto stack..
794 p = this.getParentElement();
798 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
802 a.push(this.doc.body);
809 getSelection : function()
812 return Roo.isIE ? this.doc.selection : this.win.getSelection();
815 getSelectedNode: function()
817 // this may only work on Gecko!!!
819 // should we cache this!!!!
824 var range = this.createRange(this.getSelection()).cloneRange();
827 var parent = range.parentElement();
829 var testRange = range.duplicate();
830 testRange.moveToElementText(parent);
831 if (testRange.inRange(range)) {
834 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
837 parent = parent.parentElement;
842 // is ancestor a text element.
843 var ac = range.commonAncestorContainer;
844 if (ac.nodeType == 3) {
848 var ar = ac.childNodes;
851 var other_nodes = [];
852 var has_other_nodes = false;
853 for (var i=0;i<ar.length;i++) {
854 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
857 // fullly contained node.
859 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
864 // probably selected..
865 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
866 other_nodes.push(ar[i]);
870 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
875 has_other_nodes = true;
877 if (!nodes.length && other_nodes.length) {
880 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
886 createRange: function(sel)
888 // this has strange effects when using with
889 // top toolbar - not sure if it's a great idea.
890 //this.editor.contentWindow.focus();
891 if (typeof sel != "undefined") {
893 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
895 return this.doc.createRange();
898 return this.doc.createRange();
901 getParentElement: function()
905 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
907 var range = this.createRange(sel);
910 var p = range.commonAncestorContainer;
911 while (p.nodeType == 3) { // text node
922 * Range intersection.. the hard stuff...
926 * [ -- selected range --- ]
930 * if end is before start or hits it. fail.
931 * if start is after end or hits it fail.
933 * if either hits (but other is outside. - then it's not
939 // @see http://www.thismuchiknow.co.uk/?p=64.
940 rangeIntersectsNode : function(range, node)
942 var nodeRange = node.ownerDocument.createRange();
944 nodeRange.selectNode(node);
946 nodeRange.selectNodeContents(node);
949 var rangeStartRange = range.cloneRange();
950 rangeStartRange.collapse(true);
952 var rangeEndRange = range.cloneRange();
953 rangeEndRange.collapse(false);
955 var nodeStartRange = nodeRange.cloneRange();
956 nodeStartRange.collapse(true);
958 var nodeEndRange = nodeRange.cloneRange();
959 nodeEndRange.collapse(false);
961 return rangeStartRange.compareBoundaryPoints(
962 Range.START_TO_START, nodeEndRange) == -1 &&
963 rangeEndRange.compareBoundaryPoints(
964 Range.START_TO_START, nodeStartRange) == 1;
968 rangeCompareNode : function(range, node)
970 var nodeRange = node.ownerDocument.createRange();
972 nodeRange.selectNode(node);
974 nodeRange.selectNodeContents(node);
978 range.collapse(true);
980 nodeRange.collapse(true);
982 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
983 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
985 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
987 var nodeIsBefore = ss == 1;
988 var nodeIsAfter = ee == -1;
990 if (nodeIsBefore && nodeIsAfter)
992 if (!nodeIsBefore && nodeIsAfter)
993 return 1; //right trailed.
995 if (nodeIsBefore && !nodeIsAfter)
996 return 2; // left trailed.
1001 // private? - in a new class?
1002 cleanUpPaste : function()
1004 // cleans up the whole document..
1005 Roo.log('cleanuppaste');
1006 this.cleanUpChildren(this.doc.body);
1007 var clean = this.cleanWordChars(this.doc.body.innerHTML);
1008 if (clean != this.doc.body.innerHTML) {
1009 this.doc.body.innerHTML = clean;
1014 cleanWordChars : function(input) {
1015 var he = Roo.form.HtmlEditor;
1018 Roo.each(he.swapCodes, function(sw) {
1020 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1021 output = output.replace(swapper, sw[1]);
1027 cleanUpChildren : function (n)
1029 if (!n.childNodes.length) {
1032 for (var i = n.childNodes.length-1; i > -1 ; i--) {
1033 this.cleanUpChild(n.childNodes[i]);
1040 cleanUpChild : function (node)
1042 //console.log(node);
1043 if (node.nodeName == "#text") {
1044 // clean up silly Windows -- stuff?
1047 if (node.nodeName == "#comment") {
1048 node.parentNode.removeChild(node);
1049 // clean up silly Windows -- stuff?
1053 if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
1055 node.parentNode.removeChild(node);
1060 var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
1062 // remove <a name=....> as rendering on yahoo mailer is bored with this.
1064 if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
1065 remove_keep_children = true;
1068 if (remove_keep_children) {
1069 this.cleanUpChildren(node);
1070 // inserts everything just before this node...
1071 while (node.childNodes.length) {
1072 var cn = node.childNodes[0];
1073 node.removeChild(cn);
1074 node.parentNode.insertBefore(cn, node);
1076 node.parentNode.removeChild(node);
1080 if (!node.attributes || !node.attributes.length) {
1081 this.cleanUpChildren(node);
1085 function cleanAttr(n,v)
1088 if (v.match(/^\./) || v.match(/^\//)) {
1091 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
1094 Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
1095 node.removeAttribute(n);
1099 function cleanStyle(n,v)
1101 if (v.match(/expression/)) { //XSS?? should we even bother..
1102 node.removeAttribute(n);
1107 var parts = v.split(/;/);
1108 Roo.each(parts, function(p) {
1109 p = p.replace(/\s+/g,'');
1113 var l = p.split(':').shift().replace(/\s+/g,'');
1115 // only allow 'c whitelisted system attributes'
1116 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
1117 Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
1118 node.removeAttribute(n);
1128 for (var i = node.attributes.length-1; i > -1 ; i--) {
1129 var a = node.attributes[i];
1131 if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
1132 node.removeAttribute(a.name);
1135 if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
1136 cleanAttr(a.name,a.value); // fixme..
1139 if (a.name == 'style') {
1140 cleanStyle(a.name,a.value);
1142 /// clean up MS crap..
1143 // tecnically this should be a list of valid class'es..
1146 if (a.name == 'class') {
1147 if (a.value.match(/^Mso/)) {
1148 node.className = '';
1151 if (a.value.match(/body/)) {
1152 node.className = '';
1162 this.cleanUpChildren(node);
1168 // hide stuff that is not compatible
1186 * @cfg {String} fieldClass @hide
1189 * @cfg {String} focusClass @hide
1192 * @cfg {String} autoCreate @hide
1195 * @cfg {String} inputType @hide
1198 * @cfg {String} invalidClass @hide
1201 * @cfg {String} invalidText @hide
1204 * @cfg {String} msgFx @hide
1207 * @cfg {String} validateOnBlur @hide
1211 Roo.form.HtmlEditor.white = [
1212 'area', 'br', 'img', 'input', 'hr', 'wbr',
1214 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1215 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1216 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1217 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1218 'table', 'ul', 'xmp',
1220 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1223 'dir', 'menu', 'ol', 'ul', 'dl',
1229 Roo.form.HtmlEditor.black = [
1230 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1232 'base', 'basefont', 'bgsound', 'blink', 'body',
1233 'frame', 'frameset', 'head', 'html', 'ilayer',
1234 'iframe', 'layer', 'link', 'meta', 'object',
1235 'script', 'style' ,'title', 'xml' // clean later..
1237 Roo.form.HtmlEditor.clean = [
1238 'script', 'style', 'title', 'xml'
1240 Roo.form.HtmlEditor.remove = [
1245 Roo.form.HtmlEditor.ablack = [
1249 Roo.form.HtmlEditor.aclean = [
1250 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1254 Roo.form.HtmlEditor.pwhite= [
1255 'http', 'https', 'mailto'
1258 // white listed style attributes.
1259 Roo.form.HtmlEditor.cwhite= [
1265 Roo.form.HtmlEditor.swapCodes =[