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.doc.on('paste', function(e,v ) {
486 this.owner.fireEvent('paste', e, v);
490 this.owner.fireEvent('initialize', this);
494 onPasteEvent : function(e,v) {
495 // default behaveiour should be our local cleanup paste? (optional?)
496 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
497 this.owner.fireEvent('paste', e, v);
500 onDestroy : function(){
506 //for (var i =0; i < this.toolbars.length;i++) {
507 // // fixme - ask toolbars for heights?
508 // this.toolbars[i].onDestroy();
511 //this.wrap.dom.innerHTML = '';
512 //this.wrap.remove();
517 onFirstFocus : function(){
522 this.activated = true;
525 if(Roo.isGecko){ // prevent silly gecko errors
527 var s = this.win.getSelection();
528 if(!s.focusNode || s.focusNode.nodeType != 3){
529 var r = s.getRangeAt(0);
530 r.selectNodeContents((this.doc.body || this.doc.documentElement));
535 this.execCmd('useCSS', true);
536 this.execCmd('styleWithCSS', false);
539 this.owner.fireEvent('activate', this);
543 adjustFont: function(btn){
544 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
545 //if(Roo.isSafari){ // safari
548 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
549 if(Roo.isSafari){ // safari
550 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
551 v = (v < 10) ? 10 : v;
552 v = (v > 48) ? 48 : v;
553 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
558 v = Math.max(1, v+adjust);
560 this.execCmd('FontSize', v );
563 onEditorEvent : function(e)
565 this.owner.fireEvent('editorevent', this, e);
566 // this.updateToolbar();
567 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
570 insertTag : function(tg)
572 // could be a bit smarter... -> wrap the current selected tRoo..
573 if (tg.toLowerCase() == 'span' ||
574 tg.toLowerCase() == 'code' ||
575 tg.toLowerCase() == 'sup' ||
576 tg.toLowerCase() == 'sub'
579 range = this.createRange(this.getSelection());
580 var wrappingNode = this.doc.createElement(tg.toLowerCase());
581 wrappingNode.appendChild(range.extractContents());
582 range.insertNode(wrappingNode);
589 this.execCmd("formatblock", tg);
593 insertText : function(txt)
597 var range = this.createRange();
598 range.deleteContents();
599 //alert(Sender.getAttribute('label'));
601 range.insertNode(this.doc.createTextNode(txt));
607 * Executes a Midas editor command on the editor document and performs necessary focus and
608 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
609 * @param {String} cmd The Midas command
610 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
612 relayCmd : function(cmd, value){
614 this.execCmd(cmd, value);
615 this.owner.fireEvent('editorevent', this);
616 //this.updateToolbar();
617 this.owner.deferFocus();
621 * Executes a Midas editor command directly on the editor document.
622 * For visual commands, you should use {@link #relayCmd} instead.
623 * <b>This should only be called after the editor is initialized.</b>
624 * @param {String} cmd The Midas command
625 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
627 execCmd : function(cmd, value){
628 this.doc.execCommand(cmd, false, value === undefined ? null : value);
635 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
637 * @param {String} text | dom node..
639 insertAtCursor : function(text)
648 var r = this.doc.selection.createRange();
659 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
663 // from jquery ui (MIT licenced)
667 if (win.getSelection && win.getSelection().getRangeAt) {
668 range = win.getSelection().getRangeAt(0);
669 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
670 range.insertNode(node);
671 } else if (win.document.selection && win.document.selection.createRange) {
672 // no firefox support
673 var txt = typeof(text) == 'string' ? text : text.outerHTML;
674 win.document.selection.createRange().pasteHTML(txt);
676 // no firefox support
677 var txt = typeof(text) == 'string' ? text : text.outerHTML;
678 this.execCmd('InsertHTML', txt);
687 mozKeyPress : function(e){
689 var c = e.getCharCode(), cmd;
692 c = String.fromCharCode(c).toLowerCase();
706 // this.cleanUpPaste.defer(100, this);
722 fixKeys : function(){ // load time branching for fastest keydown performance
725 var k = e.getKey(), r;
728 r = this.doc.selection.createRange();
731 r.pasteHTML('    ');
738 r = this.doc.selection.createRange();
740 var target = r.parentElement();
741 if(!target || target.tagName.toLowerCase() != 'li'){
743 r.pasteHTML('<br/>');
749 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
750 // this.cleanUpPaste.defer(100, this);
756 }else if(Roo.isOpera){
762 this.execCmd('InsertHTML','    ');
765 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
766 // this.cleanUpPaste.defer(100, this);
771 }else if(Roo.isSafari){
777 this.execCmd('InsertText','\t');
781 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
782 // this.cleanUpPaste.defer(100, this);
790 getAllAncestors: function()
792 var p = this.getSelectedNode();
795 a.push(p); // push blank onto stack..
796 p = this.getParentElement();
800 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
804 a.push(this.doc.body);
811 getSelection : function()
814 return Roo.isIE ? this.doc.selection : this.win.getSelection();
817 getSelectedNode: function()
819 // this may only work on Gecko!!!
821 // should we cache this!!!!
826 var range = this.createRange(this.getSelection()).cloneRange();
829 var parent = range.parentElement();
831 var testRange = range.duplicate();
832 testRange.moveToElementText(parent);
833 if (testRange.inRange(range)) {
836 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
839 parent = parent.parentElement;
844 // is ancestor a text element.
845 var ac = range.commonAncestorContainer;
846 if (ac.nodeType == 3) {
850 var ar = ac.childNodes;
853 var other_nodes = [];
854 var has_other_nodes = false;
855 for (var i=0;i<ar.length;i++) {
856 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
859 // fullly contained node.
861 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
866 // probably selected..
867 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
868 other_nodes.push(ar[i]);
872 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
877 has_other_nodes = true;
879 if (!nodes.length && other_nodes.length) {
882 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
888 createRange: function(sel)
890 // this has strange effects when using with
891 // top toolbar - not sure if it's a great idea.
892 //this.editor.contentWindow.focus();
893 if (typeof sel != "undefined") {
895 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
897 return this.doc.createRange();
900 return this.doc.createRange();
903 getParentElement: function()
907 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
909 var range = this.createRange(sel);
912 var p = range.commonAncestorContainer;
913 while (p.nodeType == 3) { // text node
924 * Range intersection.. the hard stuff...
928 * [ -- selected range --- ]
932 * if end is before start or hits it. fail.
933 * if start is after end or hits it fail.
935 * if either hits (but other is outside. - then it's not
941 // @see http://www.thismuchiknow.co.uk/?p=64.
942 rangeIntersectsNode : function(range, node)
944 var nodeRange = node.ownerDocument.createRange();
946 nodeRange.selectNode(node);
948 nodeRange.selectNodeContents(node);
951 var rangeStartRange = range.cloneRange();
952 rangeStartRange.collapse(true);
954 var rangeEndRange = range.cloneRange();
955 rangeEndRange.collapse(false);
957 var nodeStartRange = nodeRange.cloneRange();
958 nodeStartRange.collapse(true);
960 var nodeEndRange = nodeRange.cloneRange();
961 nodeEndRange.collapse(false);
963 return rangeStartRange.compareBoundaryPoints(
964 Range.START_TO_START, nodeEndRange) == -1 &&
965 rangeEndRange.compareBoundaryPoints(
966 Range.START_TO_START, nodeStartRange) == 1;
970 rangeCompareNode : function(range, node)
972 var nodeRange = node.ownerDocument.createRange();
974 nodeRange.selectNode(node);
976 nodeRange.selectNodeContents(node);
980 range.collapse(true);
982 nodeRange.collapse(true);
984 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
985 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
987 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
989 var nodeIsBefore = ss == 1;
990 var nodeIsAfter = ee == -1;
992 if (nodeIsBefore && nodeIsAfter) {
995 if (!nodeIsBefore && nodeIsAfter) {
996 return 1; //right trailed.
999 if (nodeIsBefore && !nodeIsAfter) {
1000 return 2; // left trailed.
1006 // private? - in a new class?
1007 cleanUpPaste : function()
1009 // cleans up the whole document..
1010 Roo.log('cleanuppaste');
1012 this.cleanUpChild(this.doc.body);
1013 var clean = this.cleanWordChars(this.doc.body.innerHTML);
1014 if (clean != this.doc.body.innerHTML) {
1015 this.doc.body.innerHTML = clean;
1020 cleanWordChars : function(input) {// change the chars to hex code
1021 var he = Roo.HtmlEditorCore;
1024 Roo.each(he.swapCodes, function(sw) {
1025 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1027 output = output.replace(swapper, sw[1]);
1037 cleanUpChild : function (node)
1040 new Roo.htmleditor.FilterComment({node : node});
1041 new Roo.htmleditor.FilterAttributes({
1043 attrib_black : this.ablack,
1044 attrib_clean : this.aclean,
1045 style_white : this.cwhite,
1046 style_black : this.cblack
1048 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1049 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1055 * Clean up MS wordisms...
1056 * @deprecated - use filter directly
1058 cleanWord : function(node)
1060 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1067 * @deprecated - use filters
1069 cleanTableWidths : function(node)
1071 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1078 applyBlacklists : function()
1080 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1081 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1083 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1084 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1085 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1089 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1090 if (b.indexOf(tag) > -1) {
1093 this.white.push(tag);
1097 Roo.each(w, function(tag) {
1098 if (b.indexOf(tag) > -1) {
1101 if (this.white.indexOf(tag) > -1) {
1104 this.white.push(tag);
1109 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1110 if (w.indexOf(tag) > -1) {
1113 this.black.push(tag);
1117 Roo.each(b, function(tag) {
1118 if (w.indexOf(tag) > -1) {
1121 if (this.black.indexOf(tag) > -1) {
1124 this.black.push(tag);
1129 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1130 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1134 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1135 if (b.indexOf(tag) > -1) {
1138 this.cwhite.push(tag);
1142 Roo.each(w, function(tag) {
1143 if (b.indexOf(tag) > -1) {
1146 if (this.cwhite.indexOf(tag) > -1) {
1149 this.cwhite.push(tag);
1154 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1155 if (w.indexOf(tag) > -1) {
1158 this.cblack.push(tag);
1162 Roo.each(b, function(tag) {
1163 if (w.indexOf(tag) > -1) {
1166 if (this.cblack.indexOf(tag) > -1) {
1169 this.cblack.push(tag);
1174 setStylesheets : function(stylesheets)
1176 if(typeof(stylesheets) == 'string'){
1177 Roo.get(this.iframe.contentDocument.head).createChild({
1188 Roo.each(stylesheets, function(s) {
1193 Roo.get(_this.iframe.contentDocument.head).createChild({
1204 removeStylesheets : function()
1208 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1213 setStyle : function(style)
1215 Roo.get(this.iframe.contentDocument.head).createChild({
1224 // hide stuff that is not compatible
1242 * @cfg {String} fieldClass @hide
1245 * @cfg {String} focusClass @hide
1248 * @cfg {String} autoCreate @hide
1251 * @cfg {String} inputType @hide
1254 * @cfg {String} invalidClass @hide
1257 * @cfg {String} invalidText @hide
1260 * @cfg {String} msgFx @hide
1263 * @cfg {String} validateOnBlur @hide
1267 Roo.HtmlEditorCore.white = [
1268 'area', 'br', 'img', 'input', 'hr', 'wbr',
1270 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1271 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1272 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1273 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1274 'table', 'ul', 'xmp',
1276 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1279 'dir', 'menu', 'ol', 'ul', 'dl',
1285 Roo.HtmlEditorCore.black = [
1286 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1288 'base', 'basefont', 'bgsound', 'blink', 'body',
1289 'frame', 'frameset', 'head', 'html', 'ilayer',
1290 'iframe', 'layer', 'link', 'meta', 'object',
1291 'script', 'style' ,'title', 'xml' // clean later..
1293 Roo.HtmlEditorCore.clean = [
1294 'script', 'style', 'title', 'xml'
1296 Roo.HtmlEditorCore.tag_remove = [
1301 Roo.HtmlEditorCore.ablack = [
1305 Roo.HtmlEditorCore.aclean = [
1306 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1310 Roo.HtmlEditorCore.pwhite= [
1311 'http', 'https', 'mailto'
1314 // white listed style attributes.
1315 Roo.HtmlEditorCore.cwhite= [
1316 // 'text-align', /// default is to allow most things..
1322 // black listed style attributes.
1323 Roo.HtmlEditorCore.cblack= [
1324 // 'font-size' -- this can be set by the project
1328 Roo.HtmlEditorCore.swapCodes =[
1329 [ 8211, "–" ],
1330 [ 8212, "—" ],