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);
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();
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();
703 this.cleanUpPaste.defer(100, this);
719 fixKeys : function(){ // load time branching for fastest keydown performance
722 var k = e.getKey(), r;
725 r = this.doc.selection.createRange();
728 r.pasteHTML('    ');
735 r = this.doc.selection.createRange();
737 var target = r.parentElement();
738 if(!target || target.tagName.toLowerCase() != 'li'){
740 r.pasteHTML('<br />');
746 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
747 this.cleanUpPaste.defer(100, this);
753 }else if(Roo.isOpera){
759 this.execCmd('InsertHTML','    ');
762 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
763 this.cleanUpPaste.defer(100, this);
768 }else if(Roo.isSafari){
774 this.execCmd('InsertText','\t');
778 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
779 this.cleanUpPaste.defer(100, this);
787 getAllAncestors: function()
789 var p = this.getSelectedNode();
792 a.push(p); // push blank onto stack..
793 p = this.getParentElement();
797 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
801 a.push(this.doc.body);
808 getSelection : function()
811 return Roo.isIE ? this.doc.selection : this.win.getSelection();
814 getSelectedNode: function()
816 // this may only work on Gecko!!!
818 // should we cache this!!!!
823 var range = this.createRange(this.getSelection()).cloneRange();
826 var parent = range.parentElement();
828 var testRange = range.duplicate();
829 testRange.moveToElementText(parent);
830 if (testRange.inRange(range)) {
833 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
836 parent = parent.parentElement;
841 // is ancestor a text element.
842 var ac = range.commonAncestorContainer;
843 if (ac.nodeType == 3) {
847 var ar = ac.childNodes;
850 var other_nodes = [];
851 var has_other_nodes = false;
852 for (var i=0;i<ar.length;i++) {
853 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
856 // fullly contained node.
858 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
863 // probably selected..
864 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
865 other_nodes.push(ar[i]);
869 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
874 has_other_nodes = true;
876 if (!nodes.length && other_nodes.length) {
879 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
885 createRange: function(sel)
887 // this has strange effects when using with
888 // top toolbar - not sure if it's a great idea.
889 //this.editor.contentWindow.focus();
890 if (typeof sel != "undefined") {
892 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
894 return this.doc.createRange();
897 return this.doc.createRange();
900 getParentElement: function()
904 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
906 var range = this.createRange(sel);
909 var p = range.commonAncestorContainer;
910 while (p.nodeType == 3) { // text node
921 * Range intersection.. the hard stuff...
925 * [ -- selected range --- ]
929 * if end is before start or hits it. fail.
930 * if start is after end or hits it fail.
932 * if either hits (but other is outside. - then it's not
938 // @see http://www.thismuchiknow.co.uk/?p=64.
939 rangeIntersectsNode : function(range, node)
941 var nodeRange = node.ownerDocument.createRange();
943 nodeRange.selectNode(node);
945 nodeRange.selectNodeContents(node);
948 var rangeStartRange = range.cloneRange();
949 rangeStartRange.collapse(true);
951 var rangeEndRange = range.cloneRange();
952 rangeEndRange.collapse(false);
954 var nodeStartRange = nodeRange.cloneRange();
955 nodeStartRange.collapse(true);
957 var nodeEndRange = nodeRange.cloneRange();
958 nodeEndRange.collapse(false);
960 return rangeStartRange.compareBoundaryPoints(
961 Range.START_TO_START, nodeEndRange) == -1 &&
962 rangeEndRange.compareBoundaryPoints(
963 Range.START_TO_START, nodeStartRange) == 1;
967 rangeCompareNode : function(range, node)
969 var nodeRange = node.ownerDocument.createRange();
971 nodeRange.selectNode(node);
973 nodeRange.selectNodeContents(node);
977 range.collapse(true);
979 nodeRange.collapse(true);
981 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
982 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
984 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
986 var nodeIsBefore = ss == 1;
987 var nodeIsAfter = ee == -1;
989 if (nodeIsBefore && nodeIsAfter)
991 if (!nodeIsBefore && nodeIsAfter)
992 return 1; //right trailed.
994 if (nodeIsBefore && !nodeIsAfter)
995 return 2; // left trailed.
1000 // private? - in a new class?
1001 cleanUpPaste : function()
1003 // cleans up the whole document..
1004 // console.log('cleanuppaste');
1005 this.cleanUpChildren(this.doc.body);
1006 this.doc.body.innerHTML = this.cleanWordChars(this.doc.body.innerHTML);
1010 cleanWordChars : function(input) {
1011 var he = Roo.form.HtmlEditor;
1014 Roo.each(he.swapCodes, function(sw) {
1016 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1017 output = output.replace(swapper, sw[1]);
1023 cleanUpChildren : function (n)
1025 if (!n.childNodes.length) {
1028 for (var i = n.childNodes.length-1; i > -1 ; i--) {
1029 this.cleanUpChild(n.childNodes[i]);
1036 cleanUpChild : function (node)
1038 //console.log(node);
1039 if (node.nodeName == "#text") {
1040 // clean up silly Windows -- stuff?
1043 if (node.nodeName == "#comment") {
1044 node.parentNode.removeChild(node);
1045 // clean up silly Windows -- stuff?
1049 if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
1051 node.parentNode.removeChild(node);
1056 var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
1058 // remove <a name=....> as rendering on yahoo mailer is bored with this.
1060 if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
1061 remove_keep_children = true;
1064 if (remove_keep_children) {
1065 this.cleanUpChildren(node);
1066 // inserts everything just before this node...
1067 while (node.childNodes.length) {
1068 var cn = node.childNodes[0];
1069 node.removeChild(cn);
1070 node.parentNode.insertBefore(cn, node);
1072 node.parentNode.removeChild(node);
1076 if (!node.attributes || !node.attributes.length) {
1077 this.cleanUpChildren(node);
1081 function cleanAttr(n,v)
1084 if (v.match(/^\./) || v.match(/^\//)) {
1087 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
1090 Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
1091 node.removeAttribute(n);
1095 function cleanStyle(n,v)
1097 if (v.match(/expression/)) { //XSS?? should we even bother..
1098 node.removeAttribute(n);
1103 var parts = v.split(/;/);
1104 Roo.each(parts, function(p) {
1105 p = p.replace(/\s+/g,'');
1109 var l = p.split(':').shift().replace(/\s+/g,'');
1111 // only allow 'c whitelisted system attributes'
1112 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
1113 Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
1114 node.removeAttribute(n);
1124 for (var i = node.attributes.length-1; i > -1 ; i--) {
1125 var a = node.attributes[i];
1127 if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
1128 node.removeAttribute(a.name);
1131 if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
1132 cleanAttr(a.name,a.value); // fixme..
1135 if (a.name == 'style') {
1136 cleanStyle(a.name,a.value);
1138 /// clean up MS crap..
1139 // tecnically this should be a list of valid class'es..
1142 if (a.name == 'class') {
1143 if (a.value.match(/^Mso/)) {
1144 node.className = '';
1147 if (a.value.match(/body/)) {
1148 node.className = '';
1158 this.cleanUpChildren(node);
1164 // hide stuff that is not compatible
1182 * @cfg {String} fieldClass @hide
1185 * @cfg {String} focusClass @hide
1188 * @cfg {String} autoCreate @hide
1191 * @cfg {String} inputType @hide
1194 * @cfg {String} invalidClass @hide
1197 * @cfg {String} invalidText @hide
1200 * @cfg {String} msgFx @hide
1203 * @cfg {String} validateOnBlur @hide
1207 Roo.form.HtmlEditor.white = [
1208 'area', 'br', 'img', 'input', 'hr', 'wbr',
1210 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1211 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1212 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1213 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1214 'table', 'ul', 'xmp',
1216 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1219 'dir', 'menu', 'ol', 'ul', 'dl',
1225 Roo.form.HtmlEditor.black = [
1226 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1228 'base', 'basefont', 'bgsound', 'blink', 'body',
1229 'frame', 'frameset', 'head', 'html', 'ilayer',
1230 'iframe', 'layer', 'link', 'meta', 'object',
1231 'script', 'style' ,'title', 'xml' // clean later..
1233 Roo.form.HtmlEditor.clean = [
1234 'script', 'style', 'title', 'xml'
1236 Roo.form.HtmlEditor.remove = [
1241 Roo.form.HtmlEditor.ablack = [
1245 Roo.form.HtmlEditor.aclean = [
1246 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1250 Roo.form.HtmlEditor.pwhite= [
1251 'http', 'https', 'mailto'
1254 // white listed style attributes.
1255 Roo.form.HtmlEditor.cwhite= [
1261 Roo.form.HtmlEditor.swapCodes =[