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,
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 // default behaveiour should be our local cleanup paste? (optional?)
485 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
486 this.owner.fireEvent('paste', e, v);
490 this.owner.fireEvent('initialize', this);
495 onDestroy : function(){
501 //for (var i =0; i < this.toolbars.length;i++) {
502 // // fixme - ask toolbars for heights?
503 // this.toolbars[i].onDestroy();
506 //this.wrap.dom.innerHTML = '';
507 //this.wrap.remove();
512 onFirstFocus : function(){
517 this.activated = true;
520 if(Roo.isGecko){ // prevent silly gecko errors
522 var s = this.win.getSelection();
523 if(!s.focusNode || s.focusNode.nodeType != 3){
524 var r = s.getRangeAt(0);
525 r.selectNodeContents((this.doc.body || this.doc.documentElement));
530 this.execCmd('useCSS', true);
531 this.execCmd('styleWithCSS', false);
534 this.owner.fireEvent('activate', this);
538 adjustFont: function(btn){
539 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
540 //if(Roo.isSafari){ // safari
543 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
544 if(Roo.isSafari){ // safari
545 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
546 v = (v < 10) ? 10 : v;
547 v = (v > 48) ? 48 : v;
548 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
553 v = Math.max(1, v+adjust);
555 this.execCmd('FontSize', v );
558 onEditorEvent : function(e)
560 this.owner.fireEvent('editorevent', this, e);
561 // this.updateToolbar();
562 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
565 insertTag : function(tg)
567 // could be a bit smarter... -> wrap the current selected tRoo..
568 if (tg.toLowerCase() == 'span' ||
569 tg.toLowerCase() == 'code' ||
570 tg.toLowerCase() == 'sup' ||
571 tg.toLowerCase() == 'sub'
574 range = this.createRange(this.getSelection());
575 var wrappingNode = this.doc.createElement(tg.toLowerCase());
576 wrappingNode.appendChild(range.extractContents());
577 range.insertNode(wrappingNode);
584 this.execCmd("formatblock", tg);
588 insertText : function(txt)
592 var range = this.createRange();
593 range.deleteContents();
594 //alert(Sender.getAttribute('label'));
596 range.insertNode(this.doc.createTextNode(txt));
602 * Executes a Midas editor command on the editor document and performs necessary focus and
603 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
604 * @param {String} cmd The Midas command
605 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
607 relayCmd : function(cmd, value){
609 this.execCmd(cmd, value);
610 this.owner.fireEvent('editorevent', this);
611 //this.updateToolbar();
612 this.owner.deferFocus();
616 * Executes a Midas editor command directly on the editor document.
617 * For visual commands, you should use {@link #relayCmd} instead.
618 * <b>This should only be called after the editor is initialized.</b>
619 * @param {String} cmd The Midas command
620 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
622 execCmd : function(cmd, value){
623 this.doc.execCommand(cmd, false, value === undefined ? null : value);
630 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
632 * @param {String} text | dom node..
634 insertAtCursor : function(text)
643 var r = this.doc.selection.createRange();
654 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
658 // from jquery ui (MIT licenced)
662 if (win.getSelection && win.getSelection().getRangeAt) {
663 range = win.getSelection().getRangeAt(0);
664 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
665 range.insertNode(node);
666 } else if (win.document.selection && win.document.selection.createRange) {
667 // no firefox support
668 var txt = typeof(text) == 'string' ? text : text.outerHTML;
669 win.document.selection.createRange().pasteHTML(txt);
671 // no firefox support
672 var txt = typeof(text) == 'string' ? text : text.outerHTML;
673 this.execCmd('InsertHTML', txt);
682 mozKeyPress : function(e){
684 var c = e.getCharCode(), cmd;
687 c = String.fromCharCode(c).toLowerCase();
701 // this.cleanUpPaste.defer(100, this);
717 fixKeys : function(){ // load time branching for fastest keydown performance
720 var k = e.getKey(), r;
723 r = this.doc.selection.createRange();
726 r.pasteHTML('    ');
733 r = this.doc.selection.createRange();
735 var target = r.parentElement();
736 if(!target || target.tagName.toLowerCase() != 'li'){
738 r.pasteHTML('<br/>');
744 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
745 // this.cleanUpPaste.defer(100, this);
751 }else if(Roo.isOpera){
757 this.execCmd('InsertHTML','    ');
760 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
761 // this.cleanUpPaste.defer(100, this);
766 }else if(Roo.isSafari){
772 this.execCmd('InsertText','\t');
776 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
777 // this.cleanUpPaste.defer(100, this);
785 getAllAncestors: function()
787 var p = this.getSelectedNode();
790 a.push(p); // push blank onto stack..
791 p = this.getParentElement();
795 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
799 a.push(this.doc.body);
806 getSelection : function()
809 return Roo.isIE ? this.doc.selection : this.win.getSelection();
812 getSelectedNode: function()
814 // this may only work on Gecko!!!
816 // should we cache this!!!!
821 var range = this.createRange(this.getSelection()).cloneRange();
824 var parent = range.parentElement();
826 var testRange = range.duplicate();
827 testRange.moveToElementText(parent);
828 if (testRange.inRange(range)) {
831 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
834 parent = parent.parentElement;
839 // is ancestor a text element.
840 var ac = range.commonAncestorContainer;
841 if (ac.nodeType == 3) {
845 var ar = ac.childNodes;
848 var other_nodes = [];
849 var has_other_nodes = false;
850 for (var i=0;i<ar.length;i++) {
851 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
854 // fullly contained node.
856 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
861 // probably selected..
862 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
863 other_nodes.push(ar[i]);
867 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
872 has_other_nodes = true;
874 if (!nodes.length && other_nodes.length) {
877 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
883 createRange: function(sel)
885 // this has strange effects when using with
886 // top toolbar - not sure if it's a great idea.
887 //this.editor.contentWindow.focus();
888 if (typeof sel != "undefined") {
890 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
892 return this.doc.createRange();
895 return this.doc.createRange();
898 getParentElement: function()
902 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
904 var range = this.createRange(sel);
907 var p = range.commonAncestorContainer;
908 while (p.nodeType == 3) { // text node
919 * Range intersection.. the hard stuff...
923 * [ -- selected range --- ]
927 * if end is before start or hits it. fail.
928 * if start is after end or hits it fail.
930 * if either hits (but other is outside. - then it's not
936 // @see http://www.thismuchiknow.co.uk/?p=64.
937 rangeIntersectsNode : function(range, node)
939 var nodeRange = node.ownerDocument.createRange();
941 nodeRange.selectNode(node);
943 nodeRange.selectNodeContents(node);
946 var rangeStartRange = range.cloneRange();
947 rangeStartRange.collapse(true);
949 var rangeEndRange = range.cloneRange();
950 rangeEndRange.collapse(false);
952 var nodeStartRange = nodeRange.cloneRange();
953 nodeStartRange.collapse(true);
955 var nodeEndRange = nodeRange.cloneRange();
956 nodeEndRange.collapse(false);
958 return rangeStartRange.compareBoundaryPoints(
959 Range.START_TO_START, nodeEndRange) == -1 &&
960 rangeEndRange.compareBoundaryPoints(
961 Range.START_TO_START, nodeStartRange) == 1;
965 rangeCompareNode : function(range, node)
967 var nodeRange = node.ownerDocument.createRange();
969 nodeRange.selectNode(node);
971 nodeRange.selectNodeContents(node);
975 range.collapse(true);
977 nodeRange.collapse(true);
979 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
980 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
982 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
984 var nodeIsBefore = ss == 1;
985 var nodeIsAfter = ee == -1;
987 if (nodeIsBefore && nodeIsAfter) {
990 if (!nodeIsBefore && nodeIsAfter) {
991 return 1; //right trailed.
994 if (nodeIsBefore && !nodeIsAfter) {
995 return 2; // left trailed.
1001 // private? - in a new class?
1002 cleanUpPaste : function()
1004 // cleans up the whole document..
1005 Roo.log('cleanuppaste');
1007 this.cleanUpChild(this.doc.body);
1008 var clean = this.cleanWordChars(this.doc.body.innerHTML);
1009 if (clean != this.doc.body.innerHTML) {
1010 this.doc.body.innerHTML = clean;
1015 cleanWordChars : function(input) {// change the chars to hex code
1016 var he = Roo.HtmlEditorCore;
1019 Roo.each(he.swapCodes, function(sw) {
1020 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1022 output = output.replace(swapper, sw[1]);
1032 cleanUpChild : function (node)
1035 new Roo.htmleditor.FilterComment({node : node});
1036 new Roo.htmleditor.FilterAttributes({
1038 attrib_black : this.ablack,
1039 attrib_clean : this.aclean,
1040 style_white : this.cwhite,
1041 style_black : this.cblack
1043 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1044 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1050 * Clean up MS wordisms...
1051 * @deprecated - use filter directly
1053 cleanWord : function(node)
1055 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1062 * @deprecated - use filters
1064 cleanTableWidths : function(node)
1066 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1073 applyBlacklists : function()
1075 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1076 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1078 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1079 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1080 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1084 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1085 if (b.indexOf(tag) > -1) {
1088 this.white.push(tag);
1092 Roo.each(w, function(tag) {
1093 if (b.indexOf(tag) > -1) {
1096 if (this.white.indexOf(tag) > -1) {
1099 this.white.push(tag);
1104 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1105 if (w.indexOf(tag) > -1) {
1108 this.black.push(tag);
1112 Roo.each(b, function(tag) {
1113 if (w.indexOf(tag) > -1) {
1116 if (this.black.indexOf(tag) > -1) {
1119 this.black.push(tag);
1124 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1125 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1129 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1130 if (b.indexOf(tag) > -1) {
1133 this.cwhite.push(tag);
1137 Roo.each(w, function(tag) {
1138 if (b.indexOf(tag) > -1) {
1141 if (this.cwhite.indexOf(tag) > -1) {
1144 this.cwhite.push(tag);
1149 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1150 if (w.indexOf(tag) > -1) {
1153 this.cblack.push(tag);
1157 Roo.each(b, function(tag) {
1158 if (w.indexOf(tag) > -1) {
1161 if (this.cblack.indexOf(tag) > -1) {
1164 this.cblack.push(tag);
1169 setStylesheets : function(stylesheets)
1171 if(typeof(stylesheets) == 'string'){
1172 Roo.get(this.iframe.contentDocument.head).createChild({
1183 Roo.each(stylesheets, function(s) {
1188 Roo.get(_this.iframe.contentDocument.head).createChild({
1199 removeStylesheets : function()
1203 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1208 setStyle : function(style)
1210 Roo.get(this.iframe.contentDocument.head).createChild({
1219 // hide stuff that is not compatible
1237 * @cfg {String} fieldClass @hide
1240 * @cfg {String} focusClass @hide
1243 * @cfg {String} autoCreate @hide
1246 * @cfg {String} inputType @hide
1249 * @cfg {String} invalidClass @hide
1252 * @cfg {String} invalidText @hide
1255 * @cfg {String} msgFx @hide
1258 * @cfg {String} validateOnBlur @hide
1262 Roo.HtmlEditorCore.white = [
1263 'area', 'br', 'img', 'input', 'hr', 'wbr',
1265 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1266 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1267 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1268 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1269 'table', 'ul', 'xmp',
1271 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1274 'dir', 'menu', 'ol', 'ul', 'dl',
1280 Roo.HtmlEditorCore.black = [
1281 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1283 'base', 'basefont', 'bgsound', 'blink', 'body',
1284 'frame', 'frameset', 'head', 'html', 'ilayer',
1285 'iframe', 'layer', 'link', 'meta', 'object',
1286 'script', 'style' ,'title', 'xml' // clean later..
1288 Roo.HtmlEditorCore.clean = [
1289 'script', 'style', 'title', 'xml'
1291 Roo.HtmlEditorCore.tag_remove = [
1296 Roo.HtmlEditorCore.ablack = [
1300 Roo.HtmlEditorCore.aclean = [
1301 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1305 Roo.HtmlEditorCore.pwhite= [
1306 'http', 'https', 'mailto'
1309 // white listed style attributes.
1310 Roo.HtmlEditorCore.cwhite= [
1311 // 'text-align', /// default is to allow most things..
1317 // black listed style attributes.
1318 Roo.HtmlEditorCore.cblack= [
1319 // 'font-size' -- this can be set by the project
1323 Roo.HtmlEditorCore.swapCodes =[
1324 [ 8211, "–" ],
1325 [ 8212, "—" ],