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);
435 Roo.log('got here!!!!!!!!!!!!!!!!!!!!!!!!!!!!1');
439 onDestroy : function(){
445 //for (var i =0; i < this.toolbars.length;i++) {
446 // // fixme - ask toolbars for heights?
447 // this.toolbars[i].onDestroy();
450 //this.wrap.dom.innerHTML = '';
451 //this.wrap.remove();
456 onFirstFocus : function(){
461 this.activated = true;
464 if(Roo.isGecko){ // prevent silly gecko errors
466 var s = this.win.getSelection();
467 if(!s.focusNode || s.focusNode.nodeType != 3){
468 var r = s.getRangeAt(0);
469 r.selectNodeContents((this.doc.body || this.doc.documentElement));
474 this.execCmd('useCSS', true);
475 this.execCmd('styleWithCSS', false);
478 this.owner.fireEvent('activate', this);
482 adjustFont: function(btn){
483 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
484 //if(Roo.isSafari){ // safari
487 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
488 if(Roo.isSafari){ // safari
489 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
490 v = (v < 10) ? 10 : v;
491 v = (v > 48) ? 48 : v;
492 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
497 v = Math.max(1, v+adjust);
499 this.execCmd('FontSize', v );
502 onEditorEvent : function(e){
503 this.owner.fireEvent('editorevent', this, e);
504 // this.updateToolbar();
505 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
508 insertTag : function(tg)
510 // could be a bit smarter... -> wrap the current selected tRoo..
511 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
513 range = this.createRange(this.getSelection());
514 var wrappingNode = this.doc.createElement(tg.toLowerCase());
515 wrappingNode.appendChild(range.extractContents());
516 range.insertNode(wrappingNode);
523 this.execCmd("formatblock", tg);
527 insertText : function(txt)
531 var range = this.createRange();
532 range.deleteContents();
533 //alert(Sender.getAttribute('label'));
535 range.insertNode(this.doc.createTextNode(txt));
541 * Executes a Midas editor command on the editor document and performs necessary focus and
542 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
543 * @param {String} cmd The Midas command
544 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
546 relayCmd : function(cmd, value){
548 this.execCmd(cmd, value);
549 this.owner.fireEvent('editorevent', this);
550 //this.updateToolbar();
551 this.owner.deferFocus();
555 * Executes a Midas editor command directly on the editor document.
556 * For visual commands, you should use {@link #relayCmd} instead.
557 * <b>This should only be called after the editor is initialized.</b>
558 * @param {String} cmd The Midas command
559 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
561 execCmd : function(cmd, value){
562 this.doc.execCommand(cmd, false, value === undefined ? null : value);
569 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
571 * @param {String} text | dom node..
573 insertAtCursor : function(text)
584 var r = this.doc.selection.createRange();
595 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
599 // from jquery ui (MIT licenced)
603 if (win.getSelection && win.getSelection().getRangeAt) {
604 range = win.getSelection().getRangeAt(0);
605 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
606 range.insertNode(node);
607 } else if (win.document.selection && win.document.selection.createRange) {
608 // no firefox support
609 var txt = typeof(text) == 'string' ? text : text.outerHTML;
610 win.document.selection.createRange().pasteHTML(txt);
612 // no firefox support
613 var txt = typeof(text) == 'string' ? text : text.outerHTML;
614 this.execCmd('InsertHTML', txt);
623 mozKeyPress : function(e){
625 var c = e.getCharCode(), cmd;
628 c = String.fromCharCode(c).toLowerCase();
642 this.cleanUpPaste.defer(100, this);
658 fixKeys : function(){ // load time branching for fastest keydown performance
661 var k = e.getKey(), r;
664 r = this.doc.selection.createRange();
667 r.pasteHTML('    ');
674 r = this.doc.selection.createRange();
676 var target = r.parentElement();
677 if(!target || target.tagName.toLowerCase() != 'li'){
679 r.pasteHTML('<br />');
685 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
686 this.cleanUpPaste.defer(100, this);
692 }else if(Roo.isOpera){
698 this.execCmd('InsertHTML','    ');
701 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
702 this.cleanUpPaste.defer(100, this);
707 }else if(Roo.isSafari){
713 this.execCmd('InsertText','\t');
717 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
718 this.cleanUpPaste.defer(100, this);
726 getAllAncestors: function()
728 var p = this.getSelectedNode();
731 a.push(p); // push blank onto stack..
732 p = this.getParentElement();
736 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
740 a.push(this.doc.body);
747 getSelection : function()
750 return Roo.isIE ? this.doc.selection : this.win.getSelection();
753 getSelectedNode: function()
755 // this may only work on Gecko!!!
757 // should we cache this!!!!
762 var range = this.createRange(this.getSelection()).cloneRange();
765 var parent = range.parentElement();
767 var testRange = range.duplicate();
768 testRange.moveToElementText(parent);
769 if (testRange.inRange(range)) {
772 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
775 parent = parent.parentElement;
780 // is ancestor a text element.
781 var ac = range.commonAncestorContainer;
782 if (ac.nodeType == 3) {
786 var ar = ac.childNodes;
789 var other_nodes = [];
790 var has_other_nodes = false;
791 for (var i=0;i<ar.length;i++) {
792 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
795 // fullly contained node.
797 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
802 // probably selected..
803 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
804 other_nodes.push(ar[i]);
808 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
813 has_other_nodes = true;
815 if (!nodes.length && other_nodes.length) {
818 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
824 createRange: function(sel)
826 // this has strange effects when using with
827 // top toolbar - not sure if it's a great idea.
828 //this.editor.contentWindow.focus();
829 if (typeof sel != "undefined") {
831 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
833 return this.doc.createRange();
836 return this.doc.createRange();
839 getParentElement: function()
843 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
845 var range = this.createRange(sel);
848 var p = range.commonAncestorContainer;
849 while (p.nodeType == 3) { // text node
860 * Range intersection.. the hard stuff...
864 * [ -- selected range --- ]
868 * if end is before start or hits it. fail.
869 * if start is after end or hits it fail.
871 * if either hits (but other is outside. - then it's not
877 // @see http://www.thismuchiknow.co.uk/?p=64.
878 rangeIntersectsNode : function(range, node)
880 var nodeRange = node.ownerDocument.createRange();
882 nodeRange.selectNode(node);
884 nodeRange.selectNodeContents(node);
887 var rangeStartRange = range.cloneRange();
888 rangeStartRange.collapse(true);
890 var rangeEndRange = range.cloneRange();
891 rangeEndRange.collapse(false);
893 var nodeStartRange = nodeRange.cloneRange();
894 nodeStartRange.collapse(true);
896 var nodeEndRange = nodeRange.cloneRange();
897 nodeEndRange.collapse(false);
899 return rangeStartRange.compareBoundaryPoints(
900 Range.START_TO_START, nodeEndRange) == -1 &&
901 rangeEndRange.compareBoundaryPoints(
902 Range.START_TO_START, nodeStartRange) == 1;
906 rangeCompareNode : function(range, node)
908 var nodeRange = node.ownerDocument.createRange();
910 nodeRange.selectNode(node);
912 nodeRange.selectNodeContents(node);
916 range.collapse(true);
918 nodeRange.collapse(true);
920 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
921 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
923 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
925 var nodeIsBefore = ss == 1;
926 var nodeIsAfter = ee == -1;
928 if (nodeIsBefore && nodeIsAfter)
930 if (!nodeIsBefore && nodeIsAfter)
931 return 1; //right trailed.
933 if (nodeIsBefore && !nodeIsAfter)
934 return 2; // left trailed.
939 // private? - in a new class?
940 cleanUpPaste : function()
942 // cleans up the whole document..
943 Roo.log('cleanuppaste');
944 this.cleanUpChildren(this.doc.body);
945 var clean = this.cleanWordChars(this.doc.body.innerHTML);
946 if (clean != this.doc.body.innerHTML) {
947 this.doc.body.innerHTML = clean;
952 cleanWordChars : function(input) {// change the chars to hex code
953 var he = Roo.HtmlEditorCore;
956 Roo.each(he.swapCodes, function(sw) {
957 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
959 output = output.replace(swapper, sw[1]);
966 cleanUpChildren : function (n)
968 if (!n.childNodes.length) {
971 for (var i = n.childNodes.length-1; i > -1 ; i--) {
972 this.cleanUpChild(n.childNodes[i]);
979 cleanUpChild : function (node)
983 if (node.nodeName == "#text") {
984 // clean up silly Windows -- stuff?
987 if (node.nodeName == "#comment") {
988 node.parentNode.removeChild(node);
989 // clean up silly Windows -- stuff?
993 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
995 node.parentNode.removeChild(node);
1000 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
1002 // remove <a name=....> as rendering on yahoo mailer is borked with this.
1003 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
1005 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
1006 // remove_keep_children = true;
1009 if (remove_keep_children) {
1010 this.cleanUpChildren(node);
1011 // inserts everything just before this node...
1012 while (node.childNodes.length) {
1013 var cn = node.childNodes[0];
1014 node.removeChild(cn);
1015 node.parentNode.insertBefore(cn, node);
1017 node.parentNode.removeChild(node);
1021 if (!node.attributes || !node.attributes.length) {
1022 this.cleanUpChildren(node);
1026 function cleanAttr(n,v)
1029 if (v.match(/^\./) || v.match(/^\//)) {
1032 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
1035 if (v.match(/^#/)) {
1038 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
1039 node.removeAttribute(n);
1043 function cleanStyle(n,v)
1045 if (v.match(/expression/)) { //XSS?? should we even bother..
1046 node.removeAttribute(n);
1049 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
1050 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
1053 var parts = v.split(/;/);
1056 Roo.each(parts, function(p) {
1057 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
1061 var l = p.split(':').shift().replace(/\s+/g,'');
1062 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
1065 if ( cblack.indexOf(l) > -1) {
1066 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
1067 //node.removeAttribute(n);
1071 // only allow 'c whitelisted system attributes'
1072 if ( cwhite.length && cwhite.indexOf(l) < 0) {
1073 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
1074 //node.removeAttribute(n);
1085 node.setAttribute(n, clean.join(';'));
1087 node.removeAttribute(n);
1093 for (var i = node.attributes.length-1; i > -1 ; i--) {
1094 var a = node.attributes[i];
1097 if (a.name.toLowerCase().substr(0,2)=='on') {
1098 node.removeAttribute(a.name);
1101 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
1102 node.removeAttribute(a.name);
1105 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
1106 cleanAttr(a.name,a.value); // fixme..
1109 if (a.name == 'style') {
1110 cleanStyle(a.name,a.value);
1113 /// clean up MS crap..
1114 // tecnically this should be a list of valid class'es..
1117 if (a.name == 'class') {
1118 if (a.value.match(/^Mso/)) {
1119 node.className = '';
1122 if (a.value.match(/body/)) {
1123 node.className = '';
1134 this.cleanUpChildren(node);
1140 // hide stuff that is not compatible
1158 * @cfg {String} fieldClass @hide
1161 * @cfg {String} focusClass @hide
1164 * @cfg {String} autoCreate @hide
1167 * @cfg {String} inputType @hide
1170 * @cfg {String} invalidClass @hide
1173 * @cfg {String} invalidText @hide
1176 * @cfg {String} msgFx @hide
1179 * @cfg {String} validateOnBlur @hide
1183 Roo.HtmlEditorCore.white = [
1184 'area', 'br', 'img', 'input', 'hr', 'wbr',
1186 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1187 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1188 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1189 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1190 'table', 'ul', 'xmp',
1192 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1195 'dir', 'menu', 'ol', 'ul', 'dl',
1201 Roo.HtmlEditorCore.black = [
1202 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1204 'base', 'basefont', 'bgsound', 'blink', 'body',
1205 'frame', 'frameset', 'head', 'html', 'ilayer',
1206 'iframe', 'layer', 'link', 'meta', 'object',
1207 'script', 'style' ,'title', 'xml' // clean later..
1209 Roo.HtmlEditorCore.clean = [
1210 'script', 'style', 'title', 'xml'
1212 Roo.HtmlEditorCore.remove = [
1217 Roo.HtmlEditorCore.ablack = [
1221 Roo.HtmlEditorCore.aclean = [
1222 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1226 Roo.HtmlEditorCore.pwhite= [
1227 'http', 'https', 'mailto'
1230 // white listed style attributes.
1231 Roo.HtmlEditorCore.cwhite= [
1232 // 'text-align', /// default is to allow most things..
1238 // black listed style attributes.
1239 Roo.HtmlEditorCore.cblack= [
1240 // 'font-size' -- this can be set by the project
1244 Roo.HtmlEditorCore.swapCodes =[