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()
333 Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
334 if(this.initialized){
335 var bd = (this.doc.body || this.doc.documentElement);
336 //this.cleanUpPaste(); -- this is done else where and causes havoc..
338 var div = document.createElement('div');
339 div.innerHTML = bd.innerHTML;
340 // remove content editable. (blocks)
341 new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
343 var html = div.innerHTML;
345 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
346 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
348 html = '<div style="'+m[0]+'">' + html + '</div>';
351 html = this.cleanHtml(html);
352 // fix up the special chars.. normaly like back quotes in word...
353 // however we do not want to do this with chinese..
354 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
356 var cc = match.charCodeAt();
358 // Get the character value, handling surrogate pairs
359 if (match.length == 2) {
360 // It's a surrogate pair, calculate the Unicode code point
361 var high = match.charCodeAt(0) - 0xD800;
362 var low = match.charCodeAt(1) - 0xDC00;
363 cc = (high * 0x400) + low + 0x10000;
365 (cc >= 0x4E00 && cc < 0xA000 ) ||
366 (cc >= 0x3400 && cc < 0x4E00 ) ||
367 (cc >= 0xf900 && cc < 0xfb00 )
372 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
373 return "&#" + cc + ";";
380 if(this.owner.fireEvent('beforesync', this, html) !== false){
381 this.el.dom.value = html;
382 this.owner.fireEvent('sync', this, html);
388 * TEXTAREA -> EDITABLE
389 * Protected method that will not generally be called directly. Pushes the value of the textarea
390 * into the iframe editor.
392 pushValue : function()
394 Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
395 if(this.initialized){
396 var v = this.el.dom.value.trim();
399 if(this.owner.fireEvent('beforepush', this, v) !== false){
400 var d = (this.doc.body || this.doc.documentElement);
402 //this.cleanUpPaste();
403 this.el.dom.value = d.innerHTML;
404 this.owner.fireEvent('push', this, v);
407 Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
408 var cls = Roo.htmleditor['Block' + Roo.get(e).attr('data-block')];
409 if (typeof(cls) == 'undefined') {
410 Roo.log("OOps missing block : " + 'Block' + Roo.get(e).attr('data-block'));
413 new cls(e); /// should trigger update element
421 deferFocus : function(){
422 this.focus.defer(10, this);
427 if(this.win && !this.sourceEditMode){
434 assignDocWin: function()
436 var iframe = this.iframe;
439 this.doc = iframe.contentWindow.document;
440 this.win = iframe.contentWindow;
442 // if (!Roo.get(this.frameId)) {
445 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
446 // this.win = Roo.get(this.frameId).dom.contentWindow;
448 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
452 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
453 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
458 initEditor : function(){
459 //console.log("INIT EDITOR");
464 this.doc.designMode="on";
466 this.doc.write(this.getDocMarkup());
469 var dbody = (this.doc.body || this.doc.documentElement);
470 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
471 // this copies styles from the containing element into thsi one..
472 // not sure why we need all of this..
473 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
475 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
476 //ss['background-attachment'] = 'fixed'; // w3c
477 dbody.bgProperties = 'fixed'; // ie
478 //Roo.DomHelper.applyStyles(dbody, ss);
479 Roo.EventManager.on(this.doc, {
480 //'mousedown': this.onEditorEvent,
481 'mouseup': this.onEditorEvent,
482 'dblclick': this.onEditorEvent,
483 'click': this.onEditorEvent,
484 'keyup': this.onEditorEvent,
485 'paste': this.onPasteEvent,
490 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
492 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
493 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
495 this.initialized = true;
498 // initialize special key events - enter
499 new Roo.htmleditor.KeyEnter({core : this});
503 this.owner.fireEvent('initialize', this);
507 onPasteEvent : function(e,v) {
508 // default behaveiour should be our local cleanup paste? (optional?)
509 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
510 this.owner.fireEvent('paste', e, v);
513 onDestroy : function(){
519 //for (var i =0; i < this.toolbars.length;i++) {
520 // // fixme - ask toolbars for heights?
521 // this.toolbars[i].onDestroy();
524 //this.wrap.dom.innerHTML = '';
525 //this.wrap.remove();
530 onFirstFocus : function(){
535 this.activated = true;
538 if(Roo.isGecko){ // prevent silly gecko errors
540 var s = this.win.getSelection();
541 if(!s.focusNode || s.focusNode.nodeType != 3){
542 var r = s.getRangeAt(0);
543 r.selectNodeContents((this.doc.body || this.doc.documentElement));
548 this.execCmd('useCSS', true);
549 this.execCmd('styleWithCSS', false);
552 this.owner.fireEvent('activate', this);
556 adjustFont: function(btn){
557 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
558 //if(Roo.isSafari){ // safari
561 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
562 if(Roo.isSafari){ // safari
563 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
564 v = (v < 10) ? 10 : v;
565 v = (v > 48) ? 48 : v;
566 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
571 v = Math.max(1, v+adjust);
573 this.execCmd('FontSize', v );
576 onEditorEvent : function(e)
578 this.owner.fireEvent('editorevent', this, e);
579 // this.updateToolbar();
580 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
583 insertTag : function(tg)
585 // could be a bit smarter... -> wrap the current selected tRoo..
586 if (tg.toLowerCase() == 'span' ||
587 tg.toLowerCase() == 'code' ||
588 tg.toLowerCase() == 'sup' ||
589 tg.toLowerCase() == 'sub'
592 range = this.createRange(this.getSelection());
593 var wrappingNode = this.doc.createElement(tg.toLowerCase());
594 wrappingNode.appendChild(range.extractContents());
595 range.insertNode(wrappingNode);
602 this.execCmd("formatblock", tg);
606 insertText : function(txt)
610 var range = this.createRange();
611 range.deleteContents();
612 //alert(Sender.getAttribute('label'));
614 range.insertNode(this.doc.createTextNode(txt));
620 * Executes a Midas editor command on the editor document and performs necessary focus and
621 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
622 * @param {String} cmd The Midas command
623 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
625 relayCmd : function(cmd, value){
627 this.execCmd(cmd, value);
628 this.owner.fireEvent('editorevent', this);
629 //this.updateToolbar();
630 this.owner.deferFocus();
634 * Executes a Midas editor command directly on the editor document.
635 * For visual commands, you should use {@link #relayCmd} instead.
636 * <b>This should only be called after the editor is initialized.</b>
637 * @param {String} cmd The Midas command
638 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
640 execCmd : function(cmd, value){
641 this.doc.execCommand(cmd, false, value === undefined ? null : value);
648 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
650 * @param {String} text | dom node..
652 insertAtCursor : function(text)
661 var r = this.doc.selection.createRange();
672 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
676 // from jquery ui (MIT licenced)
680 if (win.getSelection && win.getSelection().getRangeAt) {
681 range = win.getSelection().getRangeAt(0);
682 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
683 range.insertNode(node);
684 } else if (win.document.selection && win.document.selection.createRange) {
685 // no firefox support
686 var txt = typeof(text) == 'string' ? text : text.outerHTML;
687 win.document.selection.createRange().pasteHTML(txt);
689 // no firefox support
690 var txt = typeof(text) == 'string' ? text : text.outerHTML;
691 this.execCmd('InsertHTML', txt);
700 mozKeyPress : function(e){
702 var c = e.getCharCode(), cmd;
705 c = String.fromCharCode(c).toLowerCase();
719 // this.cleanUpPaste.defer(100, this);
735 fixKeys : function(){ // load time branching for fastest keydown performance
738 var k = e.getKey(), r;
741 r = this.doc.selection.createRange();
744 r.pasteHTML('    ');
751 r = this.doc.selection.createRange();
753 var target = r.parentElement();
754 if(!target || target.tagName.toLowerCase() != 'li'){
756 r.pasteHTML('<br/>');
762 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
763 // this.cleanUpPaste.defer(100, this);
769 }else if(Roo.isOpera){
775 this.execCmd('InsertHTML','    ');
778 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
779 // this.cleanUpPaste.defer(100, this);
784 }else if(Roo.isSafari){
790 this.execCmd('InsertText','\t');
794 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
795 // this.cleanUpPaste.defer(100, this);
803 getAllAncestors: function()
805 var p = this.getSelectedNode();
808 a.push(p); // push blank onto stack..
809 p = this.getParentElement();
813 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
817 a.push(this.doc.body);
824 getSelection : function()
827 return Roo.isIE ? this.doc.selection : this.win.getSelection();
830 getSelectedNode: function()
832 // this may only work on Gecko!!!
834 // should we cache this!!!!
839 var range = this.createRange(this.getSelection()).cloneRange();
842 var parent = range.parentElement();
844 var testRange = range.duplicate();
845 testRange.moveToElementText(parent);
846 if (testRange.inRange(range)) {
849 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
852 parent = parent.parentElement;
857 // is ancestor a text element.
858 var ac = range.commonAncestorContainer;
859 if (ac.nodeType == 3) {
863 var ar = ac.childNodes;
866 var other_nodes = [];
867 var has_other_nodes = false;
868 for (var i=0;i<ar.length;i++) {
869 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
872 // fullly contained node.
874 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
879 // probably selected..
880 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
881 other_nodes.push(ar[i]);
885 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
890 has_other_nodes = true;
892 if (!nodes.length && other_nodes.length) {
895 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
901 createRange: function(sel)
903 // this has strange effects when using with
904 // top toolbar - not sure if it's a great idea.
905 //this.editor.contentWindow.focus();
906 if (typeof sel != "undefined") {
908 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
910 return this.doc.createRange();
913 return this.doc.createRange();
916 getParentElement: function()
920 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
922 var range = this.createRange(sel);
925 var p = range.commonAncestorContainer;
926 while (p.nodeType == 3) { // text node
937 * Range intersection.. the hard stuff...
941 * [ -- selected range --- ]
945 * if end is before start or hits it. fail.
946 * if start is after end or hits it fail.
948 * if either hits (but other is outside. - then it's not
954 // @see http://www.thismuchiknow.co.uk/?p=64.
955 rangeIntersectsNode : function(range, node)
957 var nodeRange = node.ownerDocument.createRange();
959 nodeRange.selectNode(node);
961 nodeRange.selectNodeContents(node);
964 var rangeStartRange = range.cloneRange();
965 rangeStartRange.collapse(true);
967 var rangeEndRange = range.cloneRange();
968 rangeEndRange.collapse(false);
970 var nodeStartRange = nodeRange.cloneRange();
971 nodeStartRange.collapse(true);
973 var nodeEndRange = nodeRange.cloneRange();
974 nodeEndRange.collapse(false);
976 return rangeStartRange.compareBoundaryPoints(
977 Range.START_TO_START, nodeEndRange) == -1 &&
978 rangeEndRange.compareBoundaryPoints(
979 Range.START_TO_START, nodeStartRange) == 1;
983 rangeCompareNode : function(range, node)
985 var nodeRange = node.ownerDocument.createRange();
987 nodeRange.selectNode(node);
989 nodeRange.selectNodeContents(node);
993 range.collapse(true);
995 nodeRange.collapse(true);
997 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
998 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
1000 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
1002 var nodeIsBefore = ss == 1;
1003 var nodeIsAfter = ee == -1;
1005 if (nodeIsBefore && nodeIsAfter) {
1008 if (!nodeIsBefore && nodeIsAfter) {
1009 return 1; //right trailed.
1012 if (nodeIsBefore && !nodeIsAfter) {
1013 return 2; // left trailed.
1019 // private? - in a new class?
1020 cleanUpPaste : function()
1022 // cleans up the whole document..
1023 Roo.log('cleanuppaste');
1025 this.cleanUpChild(this.doc.body);
1026 var clean = this.cleanWordChars(this.doc.body.innerHTML);
1027 if (clean != this.doc.body.innerHTML) {
1028 this.doc.body.innerHTML = clean;
1033 cleanWordChars : function(input) {// change the chars to hex code
1034 var he = Roo.HtmlEditorCore;
1037 Roo.each(he.swapCodes, function(sw) {
1038 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1040 output = output.replace(swapper, sw[1]);
1050 cleanUpChild : function (node)
1053 new Roo.htmleditor.FilterComment({node : node});
1054 new Roo.htmleditor.FilterAttributes({
1056 attrib_black : this.ablack,
1057 attrib_clean : this.aclean,
1058 style_white : this.cwhite,
1059 style_black : this.cblack
1061 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1062 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1068 * Clean up MS wordisms...
1069 * @deprecated - use filter directly
1071 cleanWord : function(node)
1073 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1080 * @deprecated - use filters
1082 cleanTableWidths : function(node)
1084 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1091 applyBlacklists : function()
1093 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1094 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1096 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1097 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1098 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1102 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1103 if (b.indexOf(tag) > -1) {
1106 this.white.push(tag);
1110 Roo.each(w, function(tag) {
1111 if (b.indexOf(tag) > -1) {
1114 if (this.white.indexOf(tag) > -1) {
1117 this.white.push(tag);
1122 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1123 if (w.indexOf(tag) > -1) {
1126 this.black.push(tag);
1130 Roo.each(b, function(tag) {
1131 if (w.indexOf(tag) > -1) {
1134 if (this.black.indexOf(tag) > -1) {
1137 this.black.push(tag);
1142 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1143 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1147 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1148 if (b.indexOf(tag) > -1) {
1151 this.cwhite.push(tag);
1155 Roo.each(w, function(tag) {
1156 if (b.indexOf(tag) > -1) {
1159 if (this.cwhite.indexOf(tag) > -1) {
1162 this.cwhite.push(tag);
1167 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1168 if (w.indexOf(tag) > -1) {
1171 this.cblack.push(tag);
1175 Roo.each(b, function(tag) {
1176 if (w.indexOf(tag) > -1) {
1179 if (this.cblack.indexOf(tag) > -1) {
1182 this.cblack.push(tag);
1187 setStylesheets : function(stylesheets)
1189 if(typeof(stylesheets) == 'string'){
1190 Roo.get(this.iframe.contentDocument.head).createChild({
1201 Roo.each(stylesheets, function(s) {
1206 Roo.get(_this.iframe.contentDocument.head).createChild({
1217 removeStylesheets : function()
1221 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1226 setStyle : function(style)
1228 Roo.get(this.iframe.contentDocument.head).createChild({
1237 // hide stuff that is not compatible
1255 * @cfg {String} fieldClass @hide
1258 * @cfg {String} focusClass @hide
1261 * @cfg {String} autoCreate @hide
1264 * @cfg {String} inputType @hide
1267 * @cfg {String} invalidClass @hide
1270 * @cfg {String} invalidText @hide
1273 * @cfg {String} msgFx @hide
1276 * @cfg {String} validateOnBlur @hide
1280 Roo.HtmlEditorCore.white = [
1281 'area', 'br', 'img', 'input', 'hr', 'wbr',
1283 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1284 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1285 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1286 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1287 'table', 'ul', 'xmp',
1289 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1292 'dir', 'menu', 'ol', 'ul', 'dl',
1298 Roo.HtmlEditorCore.black = [
1299 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1301 'base', 'basefont', 'bgsound', 'blink', 'body',
1302 'frame', 'frameset', 'head', 'html', 'ilayer',
1303 'iframe', 'layer', 'link', 'meta', 'object',
1304 'script', 'style' ,'title', 'xml' // clean later..
1306 Roo.HtmlEditorCore.clean = [
1307 'script', 'style', 'title', 'xml'
1309 Roo.HtmlEditorCore.tag_remove = [
1314 Roo.HtmlEditorCore.ablack = [
1318 Roo.HtmlEditorCore.aclean = [
1319 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1323 Roo.HtmlEditorCore.pwhite= [
1324 'http', 'https', 'mailto'
1327 // white listed style attributes.
1328 Roo.HtmlEditorCore.cwhite= [
1329 // 'text-align', /// default is to allow most things..
1335 // black listed style attributes.
1336 Roo.HtmlEditorCore.cblack= [
1337 // 'font-size' -- this can be set by the project
1341 Roo.HtmlEditorCore.swapCodes =[
1342 [ 8211, "–" ],
1343 [ 8212, "—" ],