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..
336 // remove content editable. (blocks)
337 new Roo.htmleditor.FilterAttribute({node : bd, attrib_black: [ 'contenteditable' ] });
339 var html = bd.innerHTML;
341 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
342 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
344 html = '<div style="'+m[0]+'">' + html + '</div>';
347 html = this.cleanHtml(html);
348 // fix up the special chars.. normaly like back quotes in word...
349 // however we do not want to do this with chinese..
350 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
352 var cc = match.charCodeAt();
354 // Get the character value, handling surrogate pairs
355 if (match.length == 2) {
356 // It's a surrogate pair, calculate the Unicode code point
357 var high = match.charCodeAt(0) - 0xD800;
358 var low = match.charCodeAt(1) - 0xDC00;
359 cc = (high * 0x400) + low + 0x10000;
361 (cc >= 0x4E00 && cc < 0xA000 ) ||
362 (cc >= 0x3400 && cc < 0x4E00 ) ||
363 (cc >= 0xf900 && cc < 0xfb00 )
368 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
369 return "&#" + cc + ";";
376 if(this.owner.fireEvent('beforesync', this, html) !== false){
377 this.el.dom.value = html;
378 this.owner.fireEvent('sync', this, html);
384 * TEXTAREA -> EDITABLE
385 * Protected method that will not generally be called directly. Pushes the value of the textarea
386 * into the iframe editor.
388 pushValue : function()
390 if(this.initialized){
391 var v = this.el.dom.value.trim();
394 if(this.owner.fireEvent('beforepush', this, v) !== false){
395 var d = (this.doc.body || this.doc.documentElement);
397 //this.cleanUpPaste();
398 this.el.dom.value = d.innerHTML;
399 this.owner.fireEvent('push', this, v);
402 Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
403 var cls = Roo.htmleditor['Block' + Roo.get(e).attr('data-block')];
404 if (typeof(cls) == 'undefined') {
405 Roo.log("OOps missing block : " + 'Block' + Roo.get(e).attr('data-block'));
408 new cls(e); /// should trigger update element
416 deferFocus : function(){
417 this.focus.defer(10, this);
422 if(this.win && !this.sourceEditMode){
429 assignDocWin: function()
431 var iframe = this.iframe;
434 this.doc = iframe.contentWindow.document;
435 this.win = iframe.contentWindow;
437 // if (!Roo.get(this.frameId)) {
440 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
441 // this.win = Roo.get(this.frameId).dom.contentWindow;
443 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
447 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
448 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
453 initEditor : function(){
454 //console.log("INIT EDITOR");
459 this.doc.designMode="on";
461 this.doc.write(this.getDocMarkup());
464 var dbody = (this.doc.body || this.doc.documentElement);
465 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
466 // this copies styles from the containing element into thsi one..
467 // not sure why we need all of this..
468 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
470 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
471 //ss['background-attachment'] = 'fixed'; // w3c
472 dbody.bgProperties = 'fixed'; // ie
473 //Roo.DomHelper.applyStyles(dbody, ss);
474 Roo.EventManager.on(this.doc, {
475 //'mousedown': this.onEditorEvent,
476 'mouseup': this.onEditorEvent,
477 'dblclick': this.onEditorEvent,
478 'click': this.onEditorEvent,
479 'keyup': this.onEditorEvent,
480 'paste': this.onPasteEvent,
485 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
487 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
488 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
490 this.initialized = true;
493 // initialize special key events - enter
494 new Roo.htmleditor.KeyEnter({core : this});
498 this.owner.fireEvent('initialize', this);
502 onPasteEvent : function(e,v) {
503 // default behaveiour should be our local cleanup paste? (optional?)
504 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
505 this.owner.fireEvent('paste', e, v);
508 onDestroy : function(){
514 //for (var i =0; i < this.toolbars.length;i++) {
515 // // fixme - ask toolbars for heights?
516 // this.toolbars[i].onDestroy();
519 //this.wrap.dom.innerHTML = '';
520 //this.wrap.remove();
525 onFirstFocus : function(){
530 this.activated = true;
533 if(Roo.isGecko){ // prevent silly gecko errors
535 var s = this.win.getSelection();
536 if(!s.focusNode || s.focusNode.nodeType != 3){
537 var r = s.getRangeAt(0);
538 r.selectNodeContents((this.doc.body || this.doc.documentElement));
543 this.execCmd('useCSS', true);
544 this.execCmd('styleWithCSS', false);
547 this.owner.fireEvent('activate', this);
551 adjustFont: function(btn){
552 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
553 //if(Roo.isSafari){ // safari
556 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
557 if(Roo.isSafari){ // safari
558 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
559 v = (v < 10) ? 10 : v;
560 v = (v > 48) ? 48 : v;
561 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
566 v = Math.max(1, v+adjust);
568 this.execCmd('FontSize', v );
571 onEditorEvent : function(e)
573 this.owner.fireEvent('editorevent', this, e);
574 // this.updateToolbar();
575 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
578 insertTag : function(tg)
580 // could be a bit smarter... -> wrap the current selected tRoo..
581 if (tg.toLowerCase() == 'span' ||
582 tg.toLowerCase() == 'code' ||
583 tg.toLowerCase() == 'sup' ||
584 tg.toLowerCase() == 'sub'
587 range = this.createRange(this.getSelection());
588 var wrappingNode = this.doc.createElement(tg.toLowerCase());
589 wrappingNode.appendChild(range.extractContents());
590 range.insertNode(wrappingNode);
597 this.execCmd("formatblock", tg);
601 insertText : function(txt)
605 var range = this.createRange();
606 range.deleteContents();
607 //alert(Sender.getAttribute('label'));
609 range.insertNode(this.doc.createTextNode(txt));
615 * Executes a Midas editor command on the editor document and performs necessary focus and
616 * toolbar updates. <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 relayCmd : function(cmd, value){
622 this.execCmd(cmd, value);
623 this.owner.fireEvent('editorevent', this);
624 //this.updateToolbar();
625 this.owner.deferFocus();
629 * Executes a Midas editor command directly on the editor document.
630 * For visual commands, you should use {@link #relayCmd} instead.
631 * <b>This should only be called after the editor is initialized.</b>
632 * @param {String} cmd The Midas command
633 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
635 execCmd : function(cmd, value){
636 this.doc.execCommand(cmd, false, value === undefined ? null : value);
643 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
645 * @param {String} text | dom node..
647 insertAtCursor : function(text)
656 var r = this.doc.selection.createRange();
667 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
671 // from jquery ui (MIT licenced)
675 if (win.getSelection && win.getSelection().getRangeAt) {
676 range = win.getSelection().getRangeAt(0);
677 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
678 range.insertNode(node);
679 } else if (win.document.selection && win.document.selection.createRange) {
680 // no firefox support
681 var txt = typeof(text) == 'string' ? text : text.outerHTML;
682 win.document.selection.createRange().pasteHTML(txt);
684 // no firefox support
685 var txt = typeof(text) == 'string' ? text : text.outerHTML;
686 this.execCmd('InsertHTML', txt);
695 mozKeyPress : function(e){
697 var c = e.getCharCode(), cmd;
700 c = String.fromCharCode(c).toLowerCase();
714 // this.cleanUpPaste.defer(100, this);
730 fixKeys : function(){ // load time branching for fastest keydown performance
733 var k = e.getKey(), r;
736 r = this.doc.selection.createRange();
739 r.pasteHTML('    ');
746 r = this.doc.selection.createRange();
748 var target = r.parentElement();
749 if(!target || target.tagName.toLowerCase() != 'li'){
751 r.pasteHTML('<br/>');
757 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
758 // this.cleanUpPaste.defer(100, this);
764 }else if(Roo.isOpera){
770 this.execCmd('InsertHTML','    ');
773 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
774 // this.cleanUpPaste.defer(100, this);
779 }else if(Roo.isSafari){
785 this.execCmd('InsertText','\t');
789 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
790 // this.cleanUpPaste.defer(100, this);
798 getAllAncestors: function()
800 var p = this.getSelectedNode();
803 a.push(p); // push blank onto stack..
804 p = this.getParentElement();
808 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
812 a.push(this.doc.body);
819 getSelection : function()
822 return Roo.isIE ? this.doc.selection : this.win.getSelection();
825 getSelectedNode: function()
827 // this may only work on Gecko!!!
829 // should we cache this!!!!
834 var range = this.createRange(this.getSelection()).cloneRange();
837 var parent = range.parentElement();
839 var testRange = range.duplicate();
840 testRange.moveToElementText(parent);
841 if (testRange.inRange(range)) {
844 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
847 parent = parent.parentElement;
852 // is ancestor a text element.
853 var ac = range.commonAncestorContainer;
854 if (ac.nodeType == 3) {
858 var ar = ac.childNodes;
861 var other_nodes = [];
862 var has_other_nodes = false;
863 for (var i=0;i<ar.length;i++) {
864 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
867 // fullly contained node.
869 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
874 // probably selected..
875 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
876 other_nodes.push(ar[i]);
880 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
885 has_other_nodes = true;
887 if (!nodes.length && other_nodes.length) {
890 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
896 createRange: function(sel)
898 // this has strange effects when using with
899 // top toolbar - not sure if it's a great idea.
900 //this.editor.contentWindow.focus();
901 if (typeof sel != "undefined") {
903 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
905 return this.doc.createRange();
908 return this.doc.createRange();
911 getParentElement: function()
915 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
917 var range = this.createRange(sel);
920 var p = range.commonAncestorContainer;
921 while (p.nodeType == 3) { // text node
932 * Range intersection.. the hard stuff...
936 * [ -- selected range --- ]
940 * if end is before start or hits it. fail.
941 * if start is after end or hits it fail.
943 * if either hits (but other is outside. - then it's not
949 // @see http://www.thismuchiknow.co.uk/?p=64.
950 rangeIntersectsNode : function(range, node)
952 var nodeRange = node.ownerDocument.createRange();
954 nodeRange.selectNode(node);
956 nodeRange.selectNodeContents(node);
959 var rangeStartRange = range.cloneRange();
960 rangeStartRange.collapse(true);
962 var rangeEndRange = range.cloneRange();
963 rangeEndRange.collapse(false);
965 var nodeStartRange = nodeRange.cloneRange();
966 nodeStartRange.collapse(true);
968 var nodeEndRange = nodeRange.cloneRange();
969 nodeEndRange.collapse(false);
971 return rangeStartRange.compareBoundaryPoints(
972 Range.START_TO_START, nodeEndRange) == -1 &&
973 rangeEndRange.compareBoundaryPoints(
974 Range.START_TO_START, nodeStartRange) == 1;
978 rangeCompareNode : function(range, node)
980 var nodeRange = node.ownerDocument.createRange();
982 nodeRange.selectNode(node);
984 nodeRange.selectNodeContents(node);
988 range.collapse(true);
990 nodeRange.collapse(true);
992 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
993 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
995 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
997 var nodeIsBefore = ss == 1;
998 var nodeIsAfter = ee == -1;
1000 if (nodeIsBefore && nodeIsAfter) {
1003 if (!nodeIsBefore && nodeIsAfter) {
1004 return 1; //right trailed.
1007 if (nodeIsBefore && !nodeIsAfter) {
1008 return 2; // left trailed.
1014 // private? - in a new class?
1015 cleanUpPaste : function()
1017 // cleans up the whole document..
1018 Roo.log('cleanuppaste');
1020 this.cleanUpChild(this.doc.body);
1021 var clean = this.cleanWordChars(this.doc.body.innerHTML);
1022 if (clean != this.doc.body.innerHTML) {
1023 this.doc.body.innerHTML = clean;
1028 cleanWordChars : function(input) {// change the chars to hex code
1029 var he = Roo.HtmlEditorCore;
1032 Roo.each(he.swapCodes, function(sw) {
1033 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1035 output = output.replace(swapper, sw[1]);
1045 cleanUpChild : function (node)
1048 new Roo.htmleditor.FilterComment({node : node});
1049 new Roo.htmleditor.FilterAttributes({
1051 attrib_black : this.ablack,
1052 attrib_clean : this.aclean,
1053 style_white : this.cwhite,
1054 style_black : this.cblack
1056 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1057 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1063 * Clean up MS wordisms...
1064 * @deprecated - use filter directly
1066 cleanWord : function(node)
1068 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1075 * @deprecated - use filters
1077 cleanTableWidths : function(node)
1079 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1086 applyBlacklists : function()
1088 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1089 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1091 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1092 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1093 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1097 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1098 if (b.indexOf(tag) > -1) {
1101 this.white.push(tag);
1105 Roo.each(w, function(tag) {
1106 if (b.indexOf(tag) > -1) {
1109 if (this.white.indexOf(tag) > -1) {
1112 this.white.push(tag);
1117 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1118 if (w.indexOf(tag) > -1) {
1121 this.black.push(tag);
1125 Roo.each(b, function(tag) {
1126 if (w.indexOf(tag) > -1) {
1129 if (this.black.indexOf(tag) > -1) {
1132 this.black.push(tag);
1137 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1138 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1142 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1143 if (b.indexOf(tag) > -1) {
1146 this.cwhite.push(tag);
1150 Roo.each(w, function(tag) {
1151 if (b.indexOf(tag) > -1) {
1154 if (this.cwhite.indexOf(tag) > -1) {
1157 this.cwhite.push(tag);
1162 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1163 if (w.indexOf(tag) > -1) {
1166 this.cblack.push(tag);
1170 Roo.each(b, function(tag) {
1171 if (w.indexOf(tag) > -1) {
1174 if (this.cblack.indexOf(tag) > -1) {
1177 this.cblack.push(tag);
1182 setStylesheets : function(stylesheets)
1184 if(typeof(stylesheets) == 'string'){
1185 Roo.get(this.iframe.contentDocument.head).createChild({
1196 Roo.each(stylesheets, function(s) {
1201 Roo.get(_this.iframe.contentDocument.head).createChild({
1212 removeStylesheets : function()
1216 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1221 setStyle : function(style)
1223 Roo.get(this.iframe.contentDocument.head).createChild({
1232 // hide stuff that is not compatible
1250 * @cfg {String} fieldClass @hide
1253 * @cfg {String} focusClass @hide
1256 * @cfg {String} autoCreate @hide
1259 * @cfg {String} inputType @hide
1262 * @cfg {String} invalidClass @hide
1265 * @cfg {String} invalidText @hide
1268 * @cfg {String} msgFx @hide
1271 * @cfg {String} validateOnBlur @hide
1275 Roo.HtmlEditorCore.white = [
1276 'area', 'br', 'img', 'input', 'hr', 'wbr',
1278 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1279 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1280 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1281 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1282 'table', 'ul', 'xmp',
1284 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1287 'dir', 'menu', 'ol', 'ul', 'dl',
1293 Roo.HtmlEditorCore.black = [
1294 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1296 'base', 'basefont', 'bgsound', 'blink', 'body',
1297 'frame', 'frameset', 'head', 'html', 'ilayer',
1298 'iframe', 'layer', 'link', 'meta', 'object',
1299 'script', 'style' ,'title', 'xml' // clean later..
1301 Roo.HtmlEditorCore.clean = [
1302 'script', 'style', 'title', 'xml'
1304 Roo.HtmlEditorCore.tag_remove = [
1309 Roo.HtmlEditorCore.ablack = [
1313 Roo.HtmlEditorCore.aclean = [
1314 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1318 Roo.HtmlEditorCore.pwhite= [
1319 'http', 'https', 'mailto'
1322 // white listed style attributes.
1323 Roo.HtmlEditorCore.cwhite= [
1324 // 'text-align', /// default is to allow most things..
1330 // black listed style attributes.
1331 Roo.HtmlEditorCore.cblack= [
1332 // 'font-size' -- this can be set by the project
1336 Roo.HtmlEditorCore.swapCodes =[
1337 [ 8211, "–" ],
1338 [ 8212, "—" ],