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);
27 * Fires when the editor is fully initialized (including the iframe)
28 * @param {Roo.HtmlEditorCore} this
33 * Fires when the editor is first receives the focus. Any insertion must wait
34 * until after this event.
35 * @param {Roo.HtmlEditorCore} this
40 * Fires before the textarea is updated with content from the editor iframe. Return false
42 * @param {Roo.HtmlEditorCore} this
43 * @param {String} html
48 * Fires before the iframe editor is updated with content from the textarea. Return false
50 * @param {Roo.HtmlEditorCore} this
51 * @param {String} html
56 * Fires when the textarea is updated with content from the editor iframe.
57 * @param {Roo.HtmlEditorCore} this
58 * @param {String} html
63 * Fires when the iframe editor is updated with content from the textarea.
64 * @param {Roo.HtmlEditorCore} this
65 * @param {String} html
71 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
72 * @param {Roo.HtmlEditorCore} this
78 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
80 // defaults : white / black...
81 this.applyBlacklists();
88 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
92 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
98 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
103 * @cfg {Number} height (in pixels)
107 * @cfg {Number} width (in pixels)
112 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
118 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
120 allowComments: false,
124 // private properties
125 validationEvent : false,
129 sourceEditMode : false,
130 onFocus : Roo.emptyFn,
136 // blacklist + whitelisted elements..
143 * Protected method that will not generally be called directly. It
144 * is called when the editor initializes the iframe with HTML contents. Override this method if you
145 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
147 getDocMarkup : function(){
151 // inherit styels from page...??
152 if (this.stylesheets === false) {
154 Roo.get(document.head).select('style').each(function(node) {
155 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
158 Roo.get(document.head).select('link').each(function(node) {
159 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
162 } else if (!this.stylesheets.length) {
164 st = '<style type="text/css">' +
165 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
168 for (var i in this.stylesheets) {
169 if (typeof(this.stylesheets[i]) != 'string') {
172 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
177 st += '<style type="text/css">' +
178 'IMG { cursor: pointer } ' +
181 var cls = 'roo-htmleditor-body';
183 if(this.bodyCls.length){
184 cls += ' ' + this.bodyCls;
187 return '<html><head>' + st +
188 //<style type="text/css">' +
189 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
191 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
195 onRender : function(ct, position)
198 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
199 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
202 this.el.dom.style.border = '0 none';
203 this.el.dom.setAttribute('tabIndex', -1);
204 this.el.addClass('x-hidden hide');
208 if(Roo.isIE){ // fix IE 1px bogus margin
209 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
213 this.frameId = Roo.id();
217 var iframe = this.owner.wrap.createChild({
219 cls: 'form-control', // bootstrap..
223 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
228 this.iframe = iframe.dom;
232 this.doc.designMode = 'on';
235 this.doc.write(this.getDocMarkup());
239 var task = { // must defer to wait for browser to be ready
241 //console.log("run task?" + this.doc.readyState);
243 if(this.doc.body || this.doc.readyState == 'complete'){
245 this.doc.designMode="on";
249 Roo.TaskMgr.stop(task);
250 this.initEditor.defer(10, this);
257 Roo.TaskMgr.start(task);
262 onResize : function(w, h)
264 Roo.log('resize: ' +w + ',' + h );
265 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
269 if(typeof w == 'number'){
271 this.iframe.style.width = w + 'px';
273 if(typeof h == 'number'){
275 this.iframe.style.height = h + 'px';
277 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
284 * Toggles the editor between standard and source edit mode.
285 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
287 toggleSourceEdit : function(sourceEditMode){
289 this.sourceEditMode = sourceEditMode === true;
291 if(this.sourceEditMode){
293 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
296 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
297 //this.iframe.className = '';
300 //this.setSize(this.owner.wrap.getSize());
301 //this.fireEvent('editmodechange', this, this.sourceEditMode);
308 * Protected method that will not generally be called directly. If you need/want
309 * custom HTML cleanup, this is the method you should override.
310 * @param {String} html The HTML to be cleaned
311 * return {String} The cleaned HTML
313 cleanHtml : function(html){
316 if(Roo.isSafari){ // strip safari nonsense
317 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
320 if(html == ' '){
327 * HTML Editor -> Textarea
328 * Protected method that will not generally be called directly. Syncs the contents
329 * of the editor iframe with the textarea.
331 syncValue : function(){
332 if(this.initialized){
333 var bd = (this.doc.body || this.doc.documentElement);
334 //this.cleanUpPaste(); -- this is done else where and causes havoc..
335 var html = bd.innerHTML;
337 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
338 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
340 html = '<div style="'+m[0]+'">' + html + '</div>';
343 html = this.cleanHtml(html);
344 // fix up the special chars.. normaly like back quotes in word...
345 // however we do not want to do this with chinese..
346 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
348 var cc = match.charCodeAt();
350 // Get the character value, handling surrogate pairs
351 if (match.length == 2) {
352 // It's a surrogate pair, calculate the Unicode code point
353 var high = match.charCodeAt(0) - 0xD800;
354 var low = match.charCodeAt(1) - 0xDC00;
355 cc = (high * 0x400) + low + 0x10000;
357 (cc >= 0x4E00 && cc < 0xA000 ) ||
358 (cc >= 0x3400 && cc < 0x4E00 ) ||
359 (cc >= 0xf900 && cc < 0xfb00 )
364 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
365 return "&#" + cc + ";";
372 if(this.owner.fireEvent('beforesync', this, html) !== false){
373 this.el.dom.value = html;
374 this.owner.fireEvent('sync', this, html);
380 * Protected method that will not generally be called directly. Pushes the value of the textarea
381 * into the iframe editor.
383 pushValue : function(){
384 if(this.initialized){
385 var v = this.el.dom.value.trim();
391 if(this.owner.fireEvent('beforepush', this, v) !== false){
392 var d = (this.doc.body || this.doc.documentElement);
394 //this.cleanUpPaste();
395 this.el.dom.value = d.innerHTML;
396 this.owner.fireEvent('push', this, v);
402 deferFocus : function(){
403 this.focus.defer(10, this);
408 if(this.win && !this.sourceEditMode){
415 assignDocWin: function()
417 var iframe = this.iframe;
420 this.doc = iframe.contentWindow.document;
421 this.win = iframe.contentWindow;
423 // if (!Roo.get(this.frameId)) {
426 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
427 // this.win = Roo.get(this.frameId).dom.contentWindow;
429 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
433 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
434 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
439 initEditor : function(){
440 //console.log("INIT EDITOR");
445 this.doc.designMode="on";
447 this.doc.write(this.getDocMarkup());
450 var dbody = (this.doc.body || this.doc.documentElement);
451 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
452 // this copies styles from the containing element into thsi one..
453 // not sure why we need all of this..
454 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
456 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
457 //ss['background-attachment'] = 'fixed'; // w3c
458 dbody.bgProperties = 'fixed'; // ie
459 //Roo.DomHelper.applyStyles(dbody, ss);
460 Roo.EventManager.on(this.doc, {
461 //'mousedown': this.onEditorEvent,
462 'mouseup': this.onEditorEvent,
463 'dblclick': this.onEditorEvent,
464 'click': this.onEditorEvent,
465 'keyup': this.onEditorEvent,
466 'paste': this.onPasteEvent,
471 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
473 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
474 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
476 this.initialized = true;
479 // initialize special key events - enter
480 new Roo.htmleditor.KeyEnter({core : this});
484 this.owner.fireEvent('initialize', this);
488 onPasteEvent : function(e,v) {
489 // default behaveiour should be our local cleanup paste? (optional?)
490 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
491 this.owner.fireEvent('paste', e, v);
494 onDestroy : function(){
500 //for (var i =0; i < this.toolbars.length;i++) {
501 // // fixme - ask toolbars for heights?
502 // this.toolbars[i].onDestroy();
505 //this.wrap.dom.innerHTML = '';
506 //this.wrap.remove();
511 onFirstFocus : function(){
516 this.activated = true;
519 if(Roo.isGecko){ // prevent silly gecko errors
521 var s = this.win.getSelection();
522 if(!s.focusNode || s.focusNode.nodeType != 3){
523 var r = s.getRangeAt(0);
524 r.selectNodeContents((this.doc.body || this.doc.documentElement));
529 this.execCmd('useCSS', true);
530 this.execCmd('styleWithCSS', false);
533 this.owner.fireEvent('activate', this);
537 adjustFont: function(btn){
538 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
539 //if(Roo.isSafari){ // safari
542 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
543 if(Roo.isSafari){ // safari
544 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
545 v = (v < 10) ? 10 : v;
546 v = (v > 48) ? 48 : v;
547 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
552 v = Math.max(1, v+adjust);
554 this.execCmd('FontSize', v );
557 onEditorEvent : function(e)
559 this.owner.fireEvent('editorevent', this, e);
560 // this.updateToolbar();
561 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
564 insertTag : function(tg)
566 // could be a bit smarter... -> wrap the current selected tRoo..
567 if (tg.toLowerCase() == 'span' ||
568 tg.toLowerCase() == 'code' ||
569 tg.toLowerCase() == 'sup' ||
570 tg.toLowerCase() == 'sub'
573 range = this.createRange(this.getSelection());
574 var wrappingNode = this.doc.createElement(tg.toLowerCase());
575 wrappingNode.appendChild(range.extractContents());
576 range.insertNode(wrappingNode);
583 this.execCmd("formatblock", tg);
587 insertText : function(txt)
591 var range = this.createRange();
592 range.deleteContents();
593 //alert(Sender.getAttribute('label'));
595 range.insertNode(this.doc.createTextNode(txt));
601 * Executes a Midas editor command on the editor document and performs necessary focus and
602 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
603 * @param {String} cmd The Midas command
604 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
606 relayCmd : function(cmd, value){
608 this.execCmd(cmd, value);
609 this.owner.fireEvent('editorevent', this);
610 //this.updateToolbar();
611 this.owner.deferFocus();
615 * Executes a Midas editor command directly on the editor document.
616 * For visual commands, you should use {@link #relayCmd} instead.
617 * <b>This should only be called after the editor is initialized.</b>
618 * @param {String} cmd The Midas command
619 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
621 execCmd : function(cmd, value){
622 this.doc.execCommand(cmd, false, value === undefined ? null : value);
629 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
631 * @param {String} text | dom node..
633 insertAtCursor : function(text)
642 var r = this.doc.selection.createRange();
653 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
657 // from jquery ui (MIT licenced)
661 if (win.getSelection && win.getSelection().getRangeAt) {
662 range = win.getSelection().getRangeAt(0);
663 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
664 range.insertNode(node);
665 } else if (win.document.selection && win.document.selection.createRange) {
666 // no firefox support
667 var txt = typeof(text) == 'string' ? text : text.outerHTML;
668 win.document.selection.createRange().pasteHTML(txt);
670 // no firefox support
671 var txt = typeof(text) == 'string' ? text : text.outerHTML;
672 this.execCmd('InsertHTML', txt);
681 mozKeyPress : function(e){
683 var c = e.getCharCode(), cmd;
686 c = String.fromCharCode(c).toLowerCase();
700 // this.cleanUpPaste.defer(100, this);
716 fixKeys : function(){ // load time branching for fastest keydown performance
719 var k = e.getKey(), r;
722 r = this.doc.selection.createRange();
725 r.pasteHTML('    ');
732 r = this.doc.selection.createRange();
734 var target = r.parentElement();
735 if(!target || target.tagName.toLowerCase() != 'li'){
737 r.pasteHTML('<br/>');
743 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
744 // this.cleanUpPaste.defer(100, this);
750 }else if(Roo.isOpera){
756 this.execCmd('InsertHTML','    ');
759 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
760 // this.cleanUpPaste.defer(100, this);
765 }else if(Roo.isSafari){
771 this.execCmd('InsertText','\t');
775 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
776 // this.cleanUpPaste.defer(100, this);
784 getAllAncestors: function()
786 var p = this.getSelectedNode();
789 a.push(p); // push blank onto stack..
790 p = this.getParentElement();
794 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
798 a.push(this.doc.body);
805 getSelection : function()
808 return Roo.isIE ? this.doc.selection : this.win.getSelection();
811 getSelectedNode: function()
813 // this may only work on Gecko!!!
815 // should we cache this!!!!
820 var range = this.createRange(this.getSelection()).cloneRange();
823 var parent = range.parentElement();
825 var testRange = range.duplicate();
826 testRange.moveToElementText(parent);
827 if (testRange.inRange(range)) {
830 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
833 parent = parent.parentElement;
838 // is ancestor a text element.
839 var ac = range.commonAncestorContainer;
840 if (ac.nodeType == 3) {
844 var ar = ac.childNodes;
847 var other_nodes = [];
848 var has_other_nodes = false;
849 for (var i=0;i<ar.length;i++) {
850 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
853 // fullly contained node.
855 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
860 // probably selected..
861 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
862 other_nodes.push(ar[i]);
866 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
871 has_other_nodes = true;
873 if (!nodes.length && other_nodes.length) {
876 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
882 createRange: function(sel)
884 // this has strange effects when using with
885 // top toolbar - not sure if it's a great idea.
886 //this.editor.contentWindow.focus();
887 if (typeof sel != "undefined") {
889 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
891 return this.doc.createRange();
894 return this.doc.createRange();
897 getParentElement: function()
901 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
903 var range = this.createRange(sel);
906 var p = range.commonAncestorContainer;
907 while (p.nodeType == 3) { // text node
918 * Range intersection.. the hard stuff...
922 * [ -- selected range --- ]
926 * if end is before start or hits it. fail.
927 * if start is after end or hits it fail.
929 * if either hits (but other is outside. - then it's not
935 // @see http://www.thismuchiknow.co.uk/?p=64.
936 rangeIntersectsNode : function(range, node)
938 var nodeRange = node.ownerDocument.createRange();
940 nodeRange.selectNode(node);
942 nodeRange.selectNodeContents(node);
945 var rangeStartRange = range.cloneRange();
946 rangeStartRange.collapse(true);
948 var rangeEndRange = range.cloneRange();
949 rangeEndRange.collapse(false);
951 var nodeStartRange = nodeRange.cloneRange();
952 nodeStartRange.collapse(true);
954 var nodeEndRange = nodeRange.cloneRange();
955 nodeEndRange.collapse(false);
957 return rangeStartRange.compareBoundaryPoints(
958 Range.START_TO_START, nodeEndRange) == -1 &&
959 rangeEndRange.compareBoundaryPoints(
960 Range.START_TO_START, nodeStartRange) == 1;
964 rangeCompareNode : function(range, node)
966 var nodeRange = node.ownerDocument.createRange();
968 nodeRange.selectNode(node);
970 nodeRange.selectNodeContents(node);
974 range.collapse(true);
976 nodeRange.collapse(true);
978 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
979 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
981 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
983 var nodeIsBefore = ss == 1;
984 var nodeIsAfter = ee == -1;
986 if (nodeIsBefore && nodeIsAfter) {
989 if (!nodeIsBefore && nodeIsAfter) {
990 return 1; //right trailed.
993 if (nodeIsBefore && !nodeIsAfter) {
994 return 2; // left trailed.
1000 // private? - in a new class?
1001 cleanUpPaste : function()
1003 // cleans up the whole document..
1004 Roo.log('cleanuppaste');
1006 this.cleanUpChild(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) {// change the chars to hex code
1015 var he = Roo.HtmlEditorCore;
1018 Roo.each(he.swapCodes, function(sw) {
1019 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1021 output = output.replace(swapper, sw[1]);
1031 cleanUpChild : function (node)
1034 new Roo.htmleditor.FilterComment({node : node});
1035 new Roo.htmleditor.FilterAttributes({
1037 attrib_black : this.ablack,
1038 attrib_clean : this.aclean,
1039 style_white : this.cwhite,
1040 style_black : this.cblack
1042 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1043 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1049 * Clean up MS wordisms...
1050 * @deprecated - use filter directly
1052 cleanWord : function(node)
1054 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1061 * @deprecated - use filters
1063 cleanTableWidths : function(node)
1065 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1072 applyBlacklists : function()
1074 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1075 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1077 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1078 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1079 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1083 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1084 if (b.indexOf(tag) > -1) {
1087 this.white.push(tag);
1091 Roo.each(w, function(tag) {
1092 if (b.indexOf(tag) > -1) {
1095 if (this.white.indexOf(tag) > -1) {
1098 this.white.push(tag);
1103 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1104 if (w.indexOf(tag) > -1) {
1107 this.black.push(tag);
1111 Roo.each(b, function(tag) {
1112 if (w.indexOf(tag) > -1) {
1115 if (this.black.indexOf(tag) > -1) {
1118 this.black.push(tag);
1123 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1124 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1128 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1129 if (b.indexOf(tag) > -1) {
1132 this.cwhite.push(tag);
1136 Roo.each(w, function(tag) {
1137 if (b.indexOf(tag) > -1) {
1140 if (this.cwhite.indexOf(tag) > -1) {
1143 this.cwhite.push(tag);
1148 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1149 if (w.indexOf(tag) > -1) {
1152 this.cblack.push(tag);
1156 Roo.each(b, function(tag) {
1157 if (w.indexOf(tag) > -1) {
1160 if (this.cblack.indexOf(tag) > -1) {
1163 this.cblack.push(tag);
1168 setStylesheets : function(stylesheets)
1170 if(typeof(stylesheets) == 'string'){
1171 Roo.get(this.iframe.contentDocument.head).createChild({
1182 Roo.each(stylesheets, function(s) {
1187 Roo.get(_this.iframe.contentDocument.head).createChild({
1198 removeStylesheets : function()
1202 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1207 setStyle : function(style)
1209 Roo.get(this.iframe.contentDocument.head).createChild({
1218 // hide stuff that is not compatible
1236 * @cfg {String} fieldClass @hide
1239 * @cfg {String} focusClass @hide
1242 * @cfg {String} autoCreate @hide
1245 * @cfg {String} inputType @hide
1248 * @cfg {String} invalidClass @hide
1251 * @cfg {String} invalidText @hide
1254 * @cfg {String} msgFx @hide
1257 * @cfg {String} validateOnBlur @hide
1261 Roo.HtmlEditorCore.white = [
1262 'area', 'br', 'img', 'input', 'hr', 'wbr',
1264 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1265 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1266 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1267 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1268 'table', 'ul', 'xmp',
1270 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1273 'dir', 'menu', 'ol', 'ul', 'dl',
1279 Roo.HtmlEditorCore.black = [
1280 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1282 'base', 'basefont', 'bgsound', 'blink', 'body',
1283 'frame', 'frameset', 'head', 'html', 'ilayer',
1284 'iframe', 'layer', 'link', 'meta', 'object',
1285 'script', 'style' ,'title', 'xml' // clean later..
1287 Roo.HtmlEditorCore.clean = [
1288 'script', 'style', 'title', 'xml'
1290 Roo.HtmlEditorCore.tag_remove = [
1295 Roo.HtmlEditorCore.ablack = [
1299 Roo.HtmlEditorCore.aclean = [
1300 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1304 Roo.HtmlEditorCore.pwhite= [
1305 'http', 'https', 'mailto'
1308 // white listed style attributes.
1309 Roo.HtmlEditorCore.cwhite= [
1310 // 'text-align', /// default is to allow most things..
1316 // black listed style attributes.
1317 Roo.HtmlEditorCore.cblack= [
1318 // 'font-size' -- this can be set by the project
1322 Roo.HtmlEditorCore.swapCodes =[
1323 [ 8211, "–" ],
1324 [ 8212, "—" ],