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);
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,
470 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
472 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
473 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
475 this.initialized = true;
478 // initialize special key events - enter
479 new Roo.htmleditor.KeyEnter({core : this});
483 this.doc.on('paste', function(e,v ) {
484 this.owner.fireEvent('paste', e, v);
488 this.owner.fireEvent('initialize', this);
493 onDestroy : function(){
499 //for (var i =0; i < this.toolbars.length;i++) {
500 // // fixme - ask toolbars for heights?
501 // this.toolbars[i].onDestroy();
504 //this.wrap.dom.innerHTML = '';
505 //this.wrap.remove();
510 onFirstFocus : function(){
515 this.activated = true;
518 if(Roo.isGecko){ // prevent silly gecko errors
520 var s = this.win.getSelection();
521 if(!s.focusNode || s.focusNode.nodeType != 3){
522 var r = s.getRangeAt(0);
523 r.selectNodeContents((this.doc.body || this.doc.documentElement));
528 this.execCmd('useCSS', true);
529 this.execCmd('styleWithCSS', false);
532 this.owner.fireEvent('activate', this);
536 adjustFont: function(btn){
537 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
538 //if(Roo.isSafari){ // safari
541 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
542 if(Roo.isSafari){ // safari
543 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
544 v = (v < 10) ? 10 : v;
545 v = (v > 48) ? 48 : v;
546 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
551 v = Math.max(1, v+adjust);
553 this.execCmd('FontSize', v );
556 onEditorEvent : function(e)
558 this.owner.fireEvent('editorevent', this, e);
559 // this.updateToolbar();
560 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
563 insertTag : function(tg)
565 // could be a bit smarter... -> wrap the current selected tRoo..
566 if (tg.toLowerCase() == 'span' ||
567 tg.toLowerCase() == 'code' ||
568 tg.toLowerCase() == 'sup' ||
569 tg.toLowerCase() == 'sub'
572 range = this.createRange(this.getSelection());
573 var wrappingNode = this.doc.createElement(tg.toLowerCase());
574 wrappingNode.appendChild(range.extractContents());
575 range.insertNode(wrappingNode);
582 this.execCmd("formatblock", tg);
586 insertText : function(txt)
590 var range = this.createRange();
591 range.deleteContents();
592 //alert(Sender.getAttribute('label'));
594 range.insertNode(this.doc.createTextNode(txt));
600 * Executes a Midas editor command on the editor document and performs necessary focus and
601 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
602 * @param {String} cmd The Midas command
603 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
605 relayCmd : function(cmd, value){
607 this.execCmd(cmd, value);
608 this.owner.fireEvent('editorevent', this);
609 //this.updateToolbar();
610 this.owner.deferFocus();
614 * Executes a Midas editor command directly on the editor document.
615 * For visual commands, you should use {@link #relayCmd} instead.
616 * <b>This should only be called after the editor is initialized.</b>
617 * @param {String} cmd The Midas command
618 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
620 execCmd : function(cmd, value){
621 this.doc.execCommand(cmd, false, value === undefined ? null : value);
628 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
630 * @param {String} text | dom node..
632 insertAtCursor : function(text)
641 var r = this.doc.selection.createRange();
652 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
656 // from jquery ui (MIT licenced)
660 if (win.getSelection && win.getSelection().getRangeAt) {
661 range = win.getSelection().getRangeAt(0);
662 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
663 range.insertNode(node);
664 } else if (win.document.selection && win.document.selection.createRange) {
665 // no firefox support
666 var txt = typeof(text) == 'string' ? text : text.outerHTML;
667 win.document.selection.createRange().pasteHTML(txt);
669 // no firefox support
670 var txt = typeof(text) == 'string' ? text : text.outerHTML;
671 this.execCmd('InsertHTML', txt);
680 mozKeyPress : function(e){
682 var c = e.getCharCode(), cmd;
685 c = String.fromCharCode(c).toLowerCase();
699 this.cleanUpPaste.defer(100, this);
715 fixKeys : function(){ // load time branching for fastest keydown performance
718 var k = e.getKey(), r;
721 r = this.doc.selection.createRange();
724 r.pasteHTML('    ');
731 r = this.doc.selection.createRange();
733 var target = r.parentElement();
734 if(!target || target.tagName.toLowerCase() != 'li'){
736 r.pasteHTML('<br />');
742 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
743 this.cleanUpPaste.defer(100, this);
749 }else if(Roo.isOpera){
755 this.execCmd('InsertHTML','    ');
758 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
759 this.cleanUpPaste.defer(100, this);
764 }else if(Roo.isSafari){
770 this.execCmd('InsertText','\t');
774 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
775 this.cleanUpPaste.defer(100, this);
783 getAllAncestors: function()
785 var p = this.getSelectedNode();
788 a.push(p); // push blank onto stack..
789 p = this.getParentElement();
793 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
797 a.push(this.doc.body);
804 getSelection : function()
807 return Roo.isIE ? this.doc.selection : this.win.getSelection();
810 getSelectedNode: function()
812 // this may only work on Gecko!!!
814 // should we cache this!!!!
819 var range = this.createRange(this.getSelection()).cloneRange();
822 var parent = range.parentElement();
824 var testRange = range.duplicate();
825 testRange.moveToElementText(parent);
826 if (testRange.inRange(range)) {
829 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
832 parent = parent.parentElement;
837 // is ancestor a text element.
838 var ac = range.commonAncestorContainer;
839 if (ac.nodeType == 3) {
843 var ar = ac.childNodes;
846 var other_nodes = [];
847 var has_other_nodes = false;
848 for (var i=0;i<ar.length;i++) {
849 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
852 // fullly contained node.
854 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
859 // probably selected..
860 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
861 other_nodes.push(ar[i]);
865 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
870 has_other_nodes = true;
872 if (!nodes.length && other_nodes.length) {
875 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
881 createRange: function(sel)
883 // this has strange effects when using with
884 // top toolbar - not sure if it's a great idea.
885 //this.editor.contentWindow.focus();
886 if (typeof sel != "undefined") {
888 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
890 return this.doc.createRange();
893 return this.doc.createRange();
896 getParentElement: function()
900 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
902 var range = this.createRange(sel);
905 var p = range.commonAncestorContainer;
906 while (p.nodeType == 3) { // text node
917 * Range intersection.. the hard stuff...
921 * [ -- selected range --- ]
925 * if end is before start or hits it. fail.
926 * if start is after end or hits it fail.
928 * if either hits (but other is outside. - then it's not
934 // @see http://www.thismuchiknow.co.uk/?p=64.
935 rangeIntersectsNode : function(range, node)
937 var nodeRange = node.ownerDocument.createRange();
939 nodeRange.selectNode(node);
941 nodeRange.selectNodeContents(node);
944 var rangeStartRange = range.cloneRange();
945 rangeStartRange.collapse(true);
947 var rangeEndRange = range.cloneRange();
948 rangeEndRange.collapse(false);
950 var nodeStartRange = nodeRange.cloneRange();
951 nodeStartRange.collapse(true);
953 var nodeEndRange = nodeRange.cloneRange();
954 nodeEndRange.collapse(false);
956 return rangeStartRange.compareBoundaryPoints(
957 Range.START_TO_START, nodeEndRange) == -1 &&
958 rangeEndRange.compareBoundaryPoints(
959 Range.START_TO_START, nodeStartRange) == 1;
963 rangeCompareNode : function(range, node)
965 var nodeRange = node.ownerDocument.createRange();
967 nodeRange.selectNode(node);
969 nodeRange.selectNodeContents(node);
973 range.collapse(true);
975 nodeRange.collapse(true);
977 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
978 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
980 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
982 var nodeIsBefore = ss == 1;
983 var nodeIsAfter = ee == -1;
985 if (nodeIsBefore && nodeIsAfter) {
988 if (!nodeIsBefore && nodeIsAfter) {
989 return 1; //right trailed.
992 if (nodeIsBefore && !nodeIsAfter) {
993 return 2; // left trailed.
999 // private? - in a new class?
1000 cleanUpPaste : function()
1002 // cleans up the whole document..
1003 Roo.log('cleanuppaste');
1005 this.cleanUpChild(this.doc.body);
1006 var clean = this.cleanWordChars(this.doc.body.innerHTML);
1007 if (clean != this.doc.body.innerHTML) {
1008 this.doc.body.innerHTML = clean;
1013 cleanWordChars : function(input) {// change the chars to hex code
1014 var he = Roo.HtmlEditorCore;
1017 Roo.each(he.swapCodes, function(sw) {
1018 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1020 output = output.replace(swapper, sw[1]);
1030 cleanUpChild : function (node)
1033 new Roo.htmleditor.FilterComment({node : node});
1034 new Roo.htmleditor.FilterAttributes({
1036 attrib_black : this.ablack,
1037 attrib_clean : this.aclean,
1038 style_white : this.cwhite,
1039 style_black : this.cblack
1041 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1042 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1048 * Clean up MS wordisms...
1049 * @deprecated - use filter directly
1051 cleanWord : function(node)
1053 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1060 * @deprecated - use filters
1062 cleanTableWidths : function(node)
1064 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1071 applyBlacklists : function()
1073 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1074 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1076 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1077 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1078 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1082 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1083 if (b.indexOf(tag) > -1) {
1086 this.white.push(tag);
1090 Roo.each(w, function(tag) {
1091 if (b.indexOf(tag) > -1) {
1094 if (this.white.indexOf(tag) > -1) {
1097 this.white.push(tag);
1102 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1103 if (w.indexOf(tag) > -1) {
1106 this.black.push(tag);
1110 Roo.each(b, function(tag) {
1111 if (w.indexOf(tag) > -1) {
1114 if (this.black.indexOf(tag) > -1) {
1117 this.black.push(tag);
1122 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1123 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1127 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1128 if (b.indexOf(tag) > -1) {
1131 this.cwhite.push(tag);
1135 Roo.each(w, function(tag) {
1136 if (b.indexOf(tag) > -1) {
1139 if (this.cwhite.indexOf(tag) > -1) {
1142 this.cwhite.push(tag);
1147 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1148 if (w.indexOf(tag) > -1) {
1151 this.cblack.push(tag);
1155 Roo.each(b, function(tag) {
1156 if (w.indexOf(tag) > -1) {
1159 if (this.cblack.indexOf(tag) > -1) {
1162 this.cblack.push(tag);
1167 setStylesheets : function(stylesheets)
1169 if(typeof(stylesheets) == 'string'){
1170 Roo.get(this.iframe.contentDocument.head).createChild({
1181 Roo.each(stylesheets, function(s) {
1186 Roo.get(_this.iframe.contentDocument.head).createChild({
1197 removeStylesheets : function()
1201 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1206 setStyle : function(style)
1208 Roo.get(this.iframe.contentDocument.head).createChild({
1217 // hide stuff that is not compatible
1235 * @cfg {String} fieldClass @hide
1238 * @cfg {String} focusClass @hide
1241 * @cfg {String} autoCreate @hide
1244 * @cfg {String} inputType @hide
1247 * @cfg {String} invalidClass @hide
1250 * @cfg {String} invalidText @hide
1253 * @cfg {String} msgFx @hide
1256 * @cfg {String} validateOnBlur @hide
1260 Roo.HtmlEditorCore.white = [
1261 'area', 'br', 'img', 'input', 'hr', 'wbr',
1263 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1264 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1265 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1266 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1267 'table', 'ul', 'xmp',
1269 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1272 'dir', 'menu', 'ol', 'ul', 'dl',
1278 Roo.HtmlEditorCore.black = [
1279 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1281 'base', 'basefont', 'bgsound', 'blink', 'body',
1282 'frame', 'frameset', 'head', 'html', 'ilayer',
1283 'iframe', 'layer', 'link', 'meta', 'object',
1284 'script', 'style' ,'title', 'xml' // clean later..
1286 Roo.HtmlEditorCore.clean = [
1287 'script', 'style', 'title', 'xml'
1289 Roo.HtmlEditorCore.tag_remove = [
1294 Roo.HtmlEditorCore.ablack = [
1298 Roo.HtmlEditorCore.aclean = [
1299 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1303 Roo.HtmlEditorCore.pwhite= [
1304 'http', 'https', 'mailto'
1307 // white listed style attributes.
1308 Roo.HtmlEditorCore.cwhite= [
1309 // 'text-align', /// default is to allow most things..
1315 // black listed style attributes.
1316 Roo.HtmlEditorCore.cblack= [
1317 // 'font-size' -- this can be set by the project
1321 Roo.HtmlEditorCore.swapCodes =[
1322 [ 8211, "–" ],
1323 [ 8212, "—" ],