1 //<script type="text/javascript">
4 * Based Ext JS Library 1.1.1
5 * Copyright(c) 2006-2007, Ext JS, LLC.
11 * @class Roo.HtmlEditorCore
12 * @extends Roo.Component
13 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
15 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18 Roo.HtmlEditorCore = function(config){
21 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25 * Fires when the editor is fully initialized (including the iframe)
26 * @param {Roo.HtmlEditorCore} this
31 * Fires when the editor is first receives the focus. Any insertion must wait
32 * until after this event.
33 * @param {Roo.HtmlEditorCore} this
38 * Fires before the textarea is updated with content from the editor iframe. Return false
40 * @param {Roo.HtmlEditorCore} this
41 * @param {String} html
46 * Fires before the iframe editor is updated with content from the textarea. Return false
48 * @param {Roo.HtmlEditorCore} this
49 * @param {String} html
54 * Fires when the textarea is updated with content from the editor iframe.
55 * @param {Roo.HtmlEditorCore} this
56 * @param {String} html
61 * Fires when the iframe editor is updated with content from the textarea.
62 * @param {Roo.HtmlEditorCore} this
63 * @param {String} html
69 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
70 * @param {Roo.HtmlEditorCore} this
78 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
82 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
88 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
93 * @cfg {Number} height (in pixels)
97 * @cfg {Number} width (in pixels)
102 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
110 // private properties
111 validationEvent : false,
115 sourceEditMode : false,
116 onFocus : Roo.emptyFn,
124 * Protected method that will not generally be called directly. It
125 * is called when the editor initializes the iframe with HTML contents. Override this method if you
126 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
128 getDocMarkup : function(){
131 Roo.log(this.stylesheets);
133 // inherit styels from page...??
134 if (this.stylesheets === false) {
136 Roo.get(document.head).select('style').each(function(node) {
137 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
140 Roo.get(document.head).select('link').each(function(node) {
141 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
144 } else if (!this.stylesheets.length) {
146 st = '<style type="text/css">' +
147 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
150 Roo.each(this.stylesheets, function(s) {
151 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
156 st += '<style type="text/css">' +
157 'IMG { cursor: pointer } ' +
161 return '<html><head>' + st +
162 //<style type="text/css">' +
163 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
165 ' </head><body class="roo-htmleditor-body"></body></html>';
169 onRender : function(ct, position)
172 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
173 this.el = this.owner.el;
176 this.el.dom.style.border = '0 none';
177 this.el.dom.setAttribute('tabIndex', -1);
178 this.el.addClass('x-hidden');
182 if(Roo.isIE){ // fix IE 1px bogus margin
183 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
187 this.frameId = Roo.id();
191 var iframe = this.owner.wrap.createChild({
196 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
201 this.iframe = iframe.dom;
205 this.doc.designMode = 'on';
208 this.doc.write(this.getDocMarkup());
212 var task = { // must defer to wait for browser to be ready
214 //console.log("run task?" + this.doc.readyState);
216 if(this.doc.body || this.doc.readyState == 'complete'){
218 this.doc.designMode="on";
222 Roo.TaskMgr.stop(task);
223 this.initEditor.defer(10, this);
230 Roo.TaskMgr.start(task);
237 onResize : function(w, h)
239 //Roo.log('resize: ' +w + ',' + h );
240 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
244 if(typeof w == 'number'){
246 this.iframe.style.width = w + 'px';
248 if(typeof h == 'number'){
250 this.iframe.style.height = h + 'px';
252 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
259 * Toggles the editor between standard and source edit mode.
260 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
262 toggleSourceEdit : function(sourceEditMode){
264 this.sourceEditMode = sourceEditMode === true;
266 if(this.sourceEditMode){
268 this.iframe.className = 'x-hidden'; //FIXME - what's the BS styles for these
272 this.iframe.className = '';
275 //this.setSize(this.owner.wrap.getSize());
276 //this.fireEvent('editmodechange', this, this.sourceEditMode);
283 * Protected method that will not generally be called directly. If you need/want
284 * custom HTML cleanup, this is the method you should override.
285 * @param {String} html The HTML to be cleaned
286 * return {String} The cleaned HTML
288 cleanHtml : function(html){
291 if(Roo.isSafari){ // strip safari nonsense
292 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
295 if(html == ' '){
302 * HTML Editor -> Textarea
303 * Protected method that will not generally be called directly. Syncs the contents
304 * of the editor iframe with the textarea.
306 syncValue : function(){
307 if(this.initialized){
308 var bd = (this.doc.body || this.doc.documentElement);
309 //this.cleanUpPaste(); -- this is done else where and causes havoc..
310 var html = bd.innerHTML;
312 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
313 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
315 html = '<div style="'+m[0]+'">' + html + '</div>';
318 html = this.cleanHtml(html);
319 // fix up the special chars.. normaly like back quotes in word...
320 // however we do not want to do this with chinese..
321 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
322 var cc = b.charCodeAt();
324 (cc >= 0x4E00 && cc < 0xA000 ) ||
325 (cc >= 0x3400 && cc < 0x4E00 ) ||
326 (cc >= 0xf900 && cc < 0xfb00 )
332 if(this.owner.fireEvent('beforesync', this, html) !== false){
333 this.el.dom.value = html;
334 this.owner.fireEvent('sync', this, html);
340 * Protected method that will not generally be called directly. Pushes the value of the textarea
341 * into the iframe editor.
343 pushValue : function(){
344 if(this.initialized){
345 var v = this.el.dom.value;
351 if(this.owner.fireEvent('beforepush', this, v) !== false){
352 var d = (this.doc.body || this.doc.documentElement);
355 this.el.dom.value = d.innerHTML;
356 this.owner.fireEvent('push', this, v);
362 deferFocus : function(){
363 this.focus.defer(10, this);
368 if(this.win && !this.sourceEditMode){
375 assignDocWin: function()
377 var iframe = this.iframe;
380 this.doc = iframe.contentWindow.document;
381 this.win = iframe.contentWindow;
383 if (!Roo.get(this.frameId)) {
386 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
387 this.win = Roo.get(this.frameId).dom.contentWindow;
392 initEditor : function(){
393 //console.log("INIT EDITOR");
398 this.doc.designMode="on";
400 this.doc.write(this.getDocMarkup());
403 var dbody = (this.doc.body || this.doc.documentElement);
404 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
405 // this copies styles from the containing element into thsi one..
406 // not sure why we need all of this..
407 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
408 ss['background-attachment'] = 'fixed'; // w3c
409 dbody.bgProperties = 'fixed'; // ie
410 Roo.DomHelper.applyStyles(dbody, ss);
411 Roo.EventManager.on(this.doc, {
412 //'mousedown': this.onEditorEvent,
413 'mouseup': this.onEditorEvent,
414 'dblclick': this.onEditorEvent,
415 'click': this.onEditorEvent,
416 'keyup': this.onEditorEvent,
421 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
423 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
424 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
426 this.initialized = true;
428 this.owner.fireEvent('initialize', this);
433 onDestroy : function(){
439 //for (var i =0; i < this.toolbars.length;i++) {
440 // // fixme - ask toolbars for heights?
441 // this.toolbars[i].onDestroy();
444 //this.wrap.dom.innerHTML = '';
445 //this.wrap.remove();
450 onFirstFocus : function(){
455 this.activated = true;
458 if(Roo.isGecko){ // prevent silly gecko errors
460 var s = this.win.getSelection();
461 if(!s.focusNode || s.focusNode.nodeType != 3){
462 var r = s.getRangeAt(0);
463 r.selectNodeContents((this.doc.body || this.doc.documentElement));
468 this.execCmd('useCSS', true);
469 this.execCmd('styleWithCSS', false);
472 this.owner.fireEvent('activate', this);
476 adjustFont: function(btn){
477 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
478 //if(Roo.isSafari){ // safari
481 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
482 if(Roo.isSafari){ // safari
483 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
484 v = (v < 10) ? 10 : v;
485 v = (v > 48) ? 48 : v;
486 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
491 v = Math.max(1, v+adjust);
493 this.execCmd('FontSize', v );
496 onEditorEvent : function(e){
497 this.owner.fireEvent('editorevent', this, e);
498 // this.updateToolbar();
499 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
502 insertTag : function(tg)
504 // could be a bit smarter... -> wrap the current selected tRoo..
505 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
507 range = this.createRange(this.getSelection());
508 var wrappingNode = this.doc.createElement(tg.toLowerCase());
509 wrappingNode.appendChild(range.extractContents());
510 range.insertNode(wrappingNode);
517 this.execCmd("formatblock", tg);
521 insertText : function(txt)
525 var range = this.createRange();
526 range.deleteContents();
527 //alert(Sender.getAttribute('label'));
529 range.insertNode(this.doc.createTextNode(txt));
535 * Executes a Midas editor command on the editor document and performs necessary focus and
536 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
537 * @param {String} cmd The Midas command
538 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
540 relayCmd : function(cmd, value){
542 this.execCmd(cmd, value);
543 this.owner.fireEvent('editorevent', this);
544 //this.updateToolbar();
545 this.owner.deferFocus();
549 * Executes a Midas editor command directly on the editor document.
550 * For visual commands, you should use {@link #relayCmd} instead.
551 * <b>This should only be called after the editor is initialized.</b>
552 * @param {String} cmd The Midas command
553 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
555 execCmd : function(cmd, value){
556 this.doc.execCommand(cmd, false, value === undefined ? null : value);
563 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
565 * @param {String} text | dom node..
567 insertAtCursor : function(text)
578 var r = this.doc.selection.createRange();
589 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
593 // from jquery ui (MIT licenced)
597 if (win.getSelection && win.getSelection().getRangeAt) {
598 range = win.getSelection().getRangeAt(0);
599 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
600 range.insertNode(node);
601 } else if (win.document.selection && win.document.selection.createRange) {
602 // no firefox support
603 var txt = typeof(text) == 'string' ? text : text.outerHTML;
604 win.document.selection.createRange().pasteHTML(txt);
606 // no firefox support
607 var txt = typeof(text) == 'string' ? text : text.outerHTML;
608 this.execCmd('InsertHTML', txt);
617 mozKeyPress : function(e){
619 var c = e.getCharCode(), cmd;
622 c = String.fromCharCode(c).toLowerCase();
636 this.cleanUpPaste.defer(100, this);
652 fixKeys : function(){ // load time branching for fastest keydown performance
655 var k = e.getKey(), r;
658 r = this.doc.selection.createRange();
661 r.pasteHTML('    ');
668 r = this.doc.selection.createRange();
670 var target = r.parentElement();
671 if(!target || target.tagName.toLowerCase() != 'li'){
673 r.pasteHTML('<br />');
679 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
680 this.cleanUpPaste.defer(100, this);
686 }else if(Roo.isOpera){
692 this.execCmd('InsertHTML','    ');
695 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
696 this.cleanUpPaste.defer(100, this);
701 }else if(Roo.isSafari){
707 this.execCmd('InsertText','\t');
711 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
712 this.cleanUpPaste.defer(100, this);
720 getAllAncestors: function()
722 var p = this.getSelectedNode();
725 a.push(p); // push blank onto stack..
726 p = this.getParentElement();
730 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
734 a.push(this.doc.body);
741 getSelection : function()
744 return Roo.isIE ? this.doc.selection : this.win.getSelection();
747 getSelectedNode: function()
749 // this may only work on Gecko!!!
751 // should we cache this!!!!
756 var range = this.createRange(this.getSelection()).cloneRange();
759 var parent = range.parentElement();
761 var testRange = range.duplicate();
762 testRange.moveToElementText(parent);
763 if (testRange.inRange(range)) {
766 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
769 parent = parent.parentElement;
774 // is ancestor a text element.
775 var ac = range.commonAncestorContainer;
776 if (ac.nodeType == 3) {
780 var ar = ac.childNodes;
783 var other_nodes = [];
784 var has_other_nodes = false;
785 for (var i=0;i<ar.length;i++) {
786 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
789 // fullly contained node.
791 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
796 // probably selected..
797 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
798 other_nodes.push(ar[i]);
802 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
807 has_other_nodes = true;
809 if (!nodes.length && other_nodes.length) {
812 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
818 createRange: function(sel)
820 // this has strange effects when using with
821 // top toolbar - not sure if it's a great idea.
822 //this.editor.contentWindow.focus();
823 if (typeof sel != "undefined") {
825 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
827 return this.doc.createRange();
830 return this.doc.createRange();
833 getParentElement: function()
837 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
839 var range = this.createRange(sel);
842 var p = range.commonAncestorContainer;
843 while (p.nodeType == 3) { // text node
854 * Range intersection.. the hard stuff...
858 * [ -- selected range --- ]
862 * if end is before start or hits it. fail.
863 * if start is after end or hits it fail.
865 * if either hits (but other is outside. - then it's not
871 // @see http://www.thismuchiknow.co.uk/?p=64.
872 rangeIntersectsNode : function(range, node)
874 var nodeRange = node.ownerDocument.createRange();
876 nodeRange.selectNode(node);
878 nodeRange.selectNodeContents(node);
881 var rangeStartRange = range.cloneRange();
882 rangeStartRange.collapse(true);
884 var rangeEndRange = range.cloneRange();
885 rangeEndRange.collapse(false);
887 var nodeStartRange = nodeRange.cloneRange();
888 nodeStartRange.collapse(true);
890 var nodeEndRange = nodeRange.cloneRange();
891 nodeEndRange.collapse(false);
893 return rangeStartRange.compareBoundaryPoints(
894 Range.START_TO_START, nodeEndRange) == -1 &&
895 rangeEndRange.compareBoundaryPoints(
896 Range.START_TO_START, nodeStartRange) == 1;
900 rangeCompareNode : function(range, node)
902 var nodeRange = node.ownerDocument.createRange();
904 nodeRange.selectNode(node);
906 nodeRange.selectNodeContents(node);
910 range.collapse(true);
912 nodeRange.collapse(true);
914 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
915 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
917 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
919 var nodeIsBefore = ss == 1;
920 var nodeIsAfter = ee == -1;
922 if (nodeIsBefore && nodeIsAfter)
924 if (!nodeIsBefore && nodeIsAfter)
925 return 1; //right trailed.
927 if (nodeIsBefore && !nodeIsAfter)
928 return 2; // left trailed.
933 // private? - in a new class?
934 cleanUpPaste : function()
936 // cleans up the whole document..
937 Roo.log('cleanuppaste');
938 this.cleanUpChildren(this.doc.body);
939 var clean = this.cleanWordChars(this.doc.body.innerHTML);
940 if (clean != this.doc.body.innerHTML) {
941 this.doc.body.innerHTML = clean;
946 cleanWordChars : function(input) {// change the chars to hex code
947 var he = Roo.HtmlEditorCore;
950 Roo.each(he.swapCodes, function(sw) {
951 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
953 output = output.replace(swapper, sw[1]);
960 cleanUpChildren : function (n)
962 if (!n.childNodes.length) {
965 for (var i = n.childNodes.length-1; i > -1 ; i--) {
966 this.cleanUpChild(n.childNodes[i]);
973 cleanUpChild : function (node)
977 if (node.nodeName == "#text") {
978 // clean up silly Windows -- stuff?
981 if (node.nodeName == "#comment") {
982 node.parentNode.removeChild(node);
983 // clean up silly Windows -- stuff?
987 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
989 node.parentNode.removeChild(node);
994 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
996 // remove <a name=....> as rendering on yahoo mailer is borked with this.
997 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
999 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
1000 // remove_keep_children = true;
1003 if (remove_keep_children) {
1004 this.cleanUpChildren(node);
1005 // inserts everything just before this node...
1006 while (node.childNodes.length) {
1007 var cn = node.childNodes[0];
1008 node.removeChild(cn);
1009 node.parentNode.insertBefore(cn, node);
1011 node.parentNode.removeChild(node);
1015 if (!node.attributes || !node.attributes.length) {
1016 this.cleanUpChildren(node);
1020 function cleanAttr(n,v)
1023 if (v.match(/^\./) || v.match(/^\//)) {
1026 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
1029 if (v.match(/^#/)) {
1032 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
1033 node.removeAttribute(n);
1037 function cleanStyle(n,v)
1039 if (v.match(/expression/)) { //XSS?? should we even bother..
1040 node.removeAttribute(n);
1043 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
1044 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
1047 var parts = v.split(/;/);
1050 Roo.each(parts, function(p) {
1051 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
1055 var l = p.split(':').shift().replace(/\s+/g,'');
1056 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
1059 if ( cblack.indexOf(l) > -1) {
1060 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
1061 //node.removeAttribute(n);
1065 // only allow 'c whitelisted system attributes'
1066 if ( cwhite.length && cwhite.indexOf(l) < 0) {
1067 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
1068 //node.removeAttribute(n);
1079 node.setAttribute(n, clean.join(';'));
1081 node.removeAttribute(n);
1087 for (var i = node.attributes.length-1; i > -1 ; i--) {
1088 var a = node.attributes[i];
1091 if (a.name.toLowerCase().substr(0,2)=='on') {
1092 node.removeAttribute(a.name);
1095 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
1096 node.removeAttribute(a.name);
1099 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
1100 cleanAttr(a.name,a.value); // fixme..
1103 if (a.name == 'style') {
1104 cleanStyle(a.name,a.value);
1107 /// clean up MS crap..
1108 // tecnically this should be a list of valid class'es..
1111 if (a.name == 'class') {
1112 if (a.value.match(/^Mso/)) {
1113 node.className = '';
1116 if (a.value.match(/body/)) {
1117 node.className = '';
1128 this.cleanUpChildren(node);
1134 // hide stuff that is not compatible
1152 * @cfg {String} fieldClass @hide
1155 * @cfg {String} focusClass @hide
1158 * @cfg {String} autoCreate @hide
1161 * @cfg {String} inputType @hide
1164 * @cfg {String} invalidClass @hide
1167 * @cfg {String} invalidText @hide
1170 * @cfg {String} msgFx @hide
1173 * @cfg {String} validateOnBlur @hide
1177 Roo.HtmlEditorCore.white = [
1178 'area', 'br', 'img', 'input', 'hr', 'wbr',
1180 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1181 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1182 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1183 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1184 'table', 'ul', 'xmp',
1186 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1189 'dir', 'menu', 'ol', 'ul', 'dl',
1195 Roo.HtmlEditorCore.black = [
1196 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1198 'base', 'basefont', 'bgsound', 'blink', 'body',
1199 'frame', 'frameset', 'head', 'html', 'ilayer',
1200 'iframe', 'layer', 'link', 'meta', 'object',
1201 'script', 'style' ,'title', 'xml' // clean later..
1203 Roo.HtmlEditorCore.clean = [
1204 'script', 'style', 'title', 'xml'
1206 Roo.HtmlEditorCore.remove = [
1211 Roo.HtmlEditorCore.ablack = [
1215 Roo.HtmlEditorCore.aclean = [
1216 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1220 Roo.HtmlEditorCore.pwhite= [
1221 'http', 'https', 'mailto'
1224 // white listed style attributes.
1225 Roo.HtmlEditorCore.cwhite= [
1226 // 'text-align', /// default is to allow most things..
1232 // black listed style attributes.
1233 Roo.HtmlEditorCore.cblack= [
1234 // 'font-size' -- this can be set by the project
1238 Roo.HtmlEditorCore.swapCodes =[