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 ) {
485 // default behaveiour should be our local cleanup paste? (optional?)
486 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
487 this.owner.fireEvent('paste', e, v);
491 this.owner.fireEvent('initialize', this);
495 onPasteEvent : function(e,v) {
496 this.owner.fireEvent('paste', e, v);
499 onDestroy : function(){
505 //for (var i =0; i < this.toolbars.length;i++) {
506 // // fixme - ask toolbars for heights?
507 // this.toolbars[i].onDestroy();
510 //this.wrap.dom.innerHTML = '';
511 //this.wrap.remove();
516 onFirstFocus : function(){
521 this.activated = true;
524 if(Roo.isGecko){ // prevent silly gecko errors
526 var s = this.win.getSelection();
527 if(!s.focusNode || s.focusNode.nodeType != 3){
528 var r = s.getRangeAt(0);
529 r.selectNodeContents((this.doc.body || this.doc.documentElement));
534 this.execCmd('useCSS', true);
535 this.execCmd('styleWithCSS', false);
538 this.owner.fireEvent('activate', this);
542 adjustFont: function(btn){
543 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
544 //if(Roo.isSafari){ // safari
547 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
548 if(Roo.isSafari){ // safari
549 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
550 v = (v < 10) ? 10 : v;
551 v = (v > 48) ? 48 : v;
552 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
557 v = Math.max(1, v+adjust);
559 this.execCmd('FontSize', v );
562 onEditorEvent : function(e)
564 this.owner.fireEvent('editorevent', this, e);
565 // this.updateToolbar();
566 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
569 insertTag : function(tg)
571 // could be a bit smarter... -> wrap the current selected tRoo..
572 if (tg.toLowerCase() == 'span' ||
573 tg.toLowerCase() == 'code' ||
574 tg.toLowerCase() == 'sup' ||
575 tg.toLowerCase() == 'sub'
578 range = this.createRange(this.getSelection());
579 var wrappingNode = this.doc.createElement(tg.toLowerCase());
580 wrappingNode.appendChild(range.extractContents());
581 range.insertNode(wrappingNode);
588 this.execCmd("formatblock", tg);
592 insertText : function(txt)
596 var range = this.createRange();
597 range.deleteContents();
598 //alert(Sender.getAttribute('label'));
600 range.insertNode(this.doc.createTextNode(txt));
606 * Executes a Midas editor command on the editor document and performs necessary focus and
607 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
608 * @param {String} cmd The Midas command
609 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
611 relayCmd : function(cmd, value){
613 this.execCmd(cmd, value);
614 this.owner.fireEvent('editorevent', this);
615 //this.updateToolbar();
616 this.owner.deferFocus();
620 * Executes a Midas editor command directly on the editor document.
621 * For visual commands, you should use {@link #relayCmd} instead.
622 * <b>This should only be called after the editor is initialized.</b>
623 * @param {String} cmd The Midas command
624 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
626 execCmd : function(cmd, value){
627 this.doc.execCommand(cmd, false, value === undefined ? null : value);
634 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
636 * @param {String} text | dom node..
638 insertAtCursor : function(text)
647 var r = this.doc.selection.createRange();
658 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
662 // from jquery ui (MIT licenced)
666 if (win.getSelection && win.getSelection().getRangeAt) {
667 range = win.getSelection().getRangeAt(0);
668 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
669 range.insertNode(node);
670 } else if (win.document.selection && win.document.selection.createRange) {
671 // no firefox support
672 var txt = typeof(text) == 'string' ? text : text.outerHTML;
673 win.document.selection.createRange().pasteHTML(txt);
675 // no firefox support
676 var txt = typeof(text) == 'string' ? text : text.outerHTML;
677 this.execCmd('InsertHTML', txt);
686 mozKeyPress : function(e){
688 var c = e.getCharCode(), cmd;
691 c = String.fromCharCode(c).toLowerCase();
705 // this.cleanUpPaste.defer(100, this);
721 fixKeys : function(){ // load time branching for fastest keydown performance
724 var k = e.getKey(), r;
727 r = this.doc.selection.createRange();
730 r.pasteHTML('    ');
737 r = this.doc.selection.createRange();
739 var target = r.parentElement();
740 if(!target || target.tagName.toLowerCase() != 'li'){
742 r.pasteHTML('<br/>');
748 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
749 // this.cleanUpPaste.defer(100, this);
755 }else if(Roo.isOpera){
761 this.execCmd('InsertHTML','    ');
764 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
765 // this.cleanUpPaste.defer(100, this);
770 }else if(Roo.isSafari){
776 this.execCmd('InsertText','\t');
780 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
781 // this.cleanUpPaste.defer(100, this);
789 getAllAncestors: function()
791 var p = this.getSelectedNode();
794 a.push(p); // push blank onto stack..
795 p = this.getParentElement();
799 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
803 a.push(this.doc.body);
810 getSelection : function()
813 return Roo.isIE ? this.doc.selection : this.win.getSelection();
816 getSelectedNode: function()
818 // this may only work on Gecko!!!
820 // should we cache this!!!!
825 var range = this.createRange(this.getSelection()).cloneRange();
828 var parent = range.parentElement();
830 var testRange = range.duplicate();
831 testRange.moveToElementText(parent);
832 if (testRange.inRange(range)) {
835 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
838 parent = parent.parentElement;
843 // is ancestor a text element.
844 var ac = range.commonAncestorContainer;
845 if (ac.nodeType == 3) {
849 var ar = ac.childNodes;
852 var other_nodes = [];
853 var has_other_nodes = false;
854 for (var i=0;i<ar.length;i++) {
855 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
858 // fullly contained node.
860 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
865 // probably selected..
866 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
867 other_nodes.push(ar[i]);
871 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
876 has_other_nodes = true;
878 if (!nodes.length && other_nodes.length) {
881 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
887 createRange: function(sel)
889 // this has strange effects when using with
890 // top toolbar - not sure if it's a great idea.
891 //this.editor.contentWindow.focus();
892 if (typeof sel != "undefined") {
894 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
896 return this.doc.createRange();
899 return this.doc.createRange();
902 getParentElement: function()
906 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
908 var range = this.createRange(sel);
911 var p = range.commonAncestorContainer;
912 while (p.nodeType == 3) { // text node
923 * Range intersection.. the hard stuff...
927 * [ -- selected range --- ]
931 * if end is before start or hits it. fail.
932 * if start is after end or hits it fail.
934 * if either hits (but other is outside. - then it's not
940 // @see http://www.thismuchiknow.co.uk/?p=64.
941 rangeIntersectsNode : function(range, node)
943 var nodeRange = node.ownerDocument.createRange();
945 nodeRange.selectNode(node);
947 nodeRange.selectNodeContents(node);
950 var rangeStartRange = range.cloneRange();
951 rangeStartRange.collapse(true);
953 var rangeEndRange = range.cloneRange();
954 rangeEndRange.collapse(false);
956 var nodeStartRange = nodeRange.cloneRange();
957 nodeStartRange.collapse(true);
959 var nodeEndRange = nodeRange.cloneRange();
960 nodeEndRange.collapse(false);
962 return rangeStartRange.compareBoundaryPoints(
963 Range.START_TO_START, nodeEndRange) == -1 &&
964 rangeEndRange.compareBoundaryPoints(
965 Range.START_TO_START, nodeStartRange) == 1;
969 rangeCompareNode : function(range, node)
971 var nodeRange = node.ownerDocument.createRange();
973 nodeRange.selectNode(node);
975 nodeRange.selectNodeContents(node);
979 range.collapse(true);
981 nodeRange.collapse(true);
983 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
984 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
986 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
988 var nodeIsBefore = ss == 1;
989 var nodeIsAfter = ee == -1;
991 if (nodeIsBefore && nodeIsAfter) {
994 if (!nodeIsBefore && nodeIsAfter) {
995 return 1; //right trailed.
998 if (nodeIsBefore && !nodeIsAfter) {
999 return 2; // left trailed.
1005 // private? - in a new class?
1006 cleanUpPaste : function()
1008 // cleans up the whole document..
1009 Roo.log('cleanuppaste');
1011 this.cleanUpChild(this.doc.body);
1012 var clean = this.cleanWordChars(this.doc.body.innerHTML);
1013 if (clean != this.doc.body.innerHTML) {
1014 this.doc.body.innerHTML = clean;
1019 cleanWordChars : function(input) {// change the chars to hex code
1020 var he = Roo.HtmlEditorCore;
1023 Roo.each(he.swapCodes, function(sw) {
1024 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1026 output = output.replace(swapper, sw[1]);
1036 cleanUpChild : function (node)
1039 new Roo.htmleditor.FilterComment({node : node});
1040 new Roo.htmleditor.FilterAttributes({
1042 attrib_black : this.ablack,
1043 attrib_clean : this.aclean,
1044 style_white : this.cwhite,
1045 style_black : this.cblack
1047 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1048 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1054 * Clean up MS wordisms...
1055 * @deprecated - use filter directly
1057 cleanWord : function(node)
1059 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1066 * @deprecated - use filters
1068 cleanTableWidths : function(node)
1070 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1077 applyBlacklists : function()
1079 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1080 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1082 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1083 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1084 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1088 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1089 if (b.indexOf(tag) > -1) {
1092 this.white.push(tag);
1096 Roo.each(w, function(tag) {
1097 if (b.indexOf(tag) > -1) {
1100 if (this.white.indexOf(tag) > -1) {
1103 this.white.push(tag);
1108 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1109 if (w.indexOf(tag) > -1) {
1112 this.black.push(tag);
1116 Roo.each(b, function(tag) {
1117 if (w.indexOf(tag) > -1) {
1120 if (this.black.indexOf(tag) > -1) {
1123 this.black.push(tag);
1128 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1129 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1133 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1134 if (b.indexOf(tag) > -1) {
1137 this.cwhite.push(tag);
1141 Roo.each(w, function(tag) {
1142 if (b.indexOf(tag) > -1) {
1145 if (this.cwhite.indexOf(tag) > -1) {
1148 this.cwhite.push(tag);
1153 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1154 if (w.indexOf(tag) > -1) {
1157 this.cblack.push(tag);
1161 Roo.each(b, function(tag) {
1162 if (w.indexOf(tag) > -1) {
1165 if (this.cblack.indexOf(tag) > -1) {
1168 this.cblack.push(tag);
1173 setStylesheets : function(stylesheets)
1175 if(typeof(stylesheets) == 'string'){
1176 Roo.get(this.iframe.contentDocument.head).createChild({
1187 Roo.each(stylesheets, function(s) {
1192 Roo.get(_this.iframe.contentDocument.head).createChild({
1203 removeStylesheets : function()
1207 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1212 setStyle : function(style)
1214 Roo.get(this.iframe.contentDocument.head).createChild({
1223 // hide stuff that is not compatible
1241 * @cfg {String} fieldClass @hide
1244 * @cfg {String} focusClass @hide
1247 * @cfg {String} autoCreate @hide
1250 * @cfg {String} inputType @hide
1253 * @cfg {String} invalidClass @hide
1256 * @cfg {String} invalidText @hide
1259 * @cfg {String} msgFx @hide
1262 * @cfg {String} validateOnBlur @hide
1266 Roo.HtmlEditorCore.white = [
1267 'area', 'br', 'img', 'input', 'hr', 'wbr',
1269 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1270 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1271 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1272 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1273 'table', 'ul', 'xmp',
1275 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1278 'dir', 'menu', 'ol', 'ul', 'dl',
1284 Roo.HtmlEditorCore.black = [
1285 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1287 'base', 'basefont', 'bgsound', 'blink', 'body',
1288 'frame', 'frameset', 'head', 'html', 'ilayer',
1289 'iframe', 'layer', 'link', 'meta', 'object',
1290 'script', 'style' ,'title', 'xml' // clean later..
1292 Roo.HtmlEditorCore.clean = [
1293 'script', 'style', 'title', 'xml'
1295 Roo.HtmlEditorCore.tag_remove = [
1300 Roo.HtmlEditorCore.ablack = [
1304 Roo.HtmlEditorCore.aclean = [
1305 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1309 Roo.HtmlEditorCore.pwhite= [
1310 'http', 'https', 'mailto'
1313 // white listed style attributes.
1314 Roo.HtmlEditorCore.cwhite= [
1315 // 'text-align', /// default is to allow most things..
1321 // black listed style attributes.
1322 Roo.HtmlEditorCore.cblack= [
1323 // 'font-size' -- this can be set by the project
1327 Roo.HtmlEditorCore.swapCodes =[
1328 [ 8211, "–" ],
1329 [ 8212, "—" ],