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 // not sure if this is really the place for this
339 // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
340 // this has to update attributes that get duped.. like alt and caption..
342 Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
343 Roo.htmleditor.Block.factory(e);
347 var div = document.createElement('div');
348 div.innerHTML = bd.innerHTML;
349 // remove content editable. (blocks)
353 new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
355 var html = div.innerHTML;
357 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
358 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
360 html = '<div style="'+m[0]+'">' + html + '</div>';
363 html = this.cleanHtml(html);
364 // fix up the special chars.. normaly like back quotes in word...
365 // however we do not want to do this with chinese..
366 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
368 var cc = match.charCodeAt();
370 // Get the character value, handling surrogate pairs
371 if (match.length == 2) {
372 // It's a surrogate pair, calculate the Unicode code point
373 var high = match.charCodeAt(0) - 0xD800;
374 var low = match.charCodeAt(1) - 0xDC00;
375 cc = (high * 0x400) + low + 0x10000;
377 (cc >= 0x4E00 && cc < 0xA000 ) ||
378 (cc >= 0x3400 && cc < 0x4E00 ) ||
379 (cc >= 0xf900 && cc < 0xfb00 )
384 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
385 return "&#" + cc + ";";
392 if(this.owner.fireEvent('beforesync', this, html) !== false){
393 this.el.dom.value = html;
394 this.owner.fireEvent('sync', this, html);
400 * TEXTAREA -> EDITABLE
401 * Protected method that will not generally be called directly. Pushes the value of the textarea
402 * into the iframe editor.
404 pushValue : function()
406 Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
407 if(this.initialized){
408 var v = this.el.dom.value.trim();
411 if(this.owner.fireEvent('beforepush', this, v) !== false){
412 var d = (this.doc.body || this.doc.documentElement);
414 //this.cleanUpPaste();
415 this.el.dom.value = d.innerHTML;
416 this.owner.fireEvent('push', this, v);
419 Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
421 Roo.htmleditor.Block.factory(e);
424 var lc = this.doc.body.lastChild;
425 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
426 // add an extra line at the end.
427 this.doc.body.appendChild(this.doc.createElement('br'));
435 deferFocus : function(){
436 this.focus.defer(10, this);
441 if(this.win && !this.sourceEditMode){
448 assignDocWin: function()
450 var iframe = this.iframe;
453 this.doc = iframe.contentWindow.document;
454 this.win = iframe.contentWindow;
456 // if (!Roo.get(this.frameId)) {
459 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
460 // this.win = Roo.get(this.frameId).dom.contentWindow;
462 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
466 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
467 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
472 initEditor : function(){
473 //console.log("INIT EDITOR");
478 this.doc.designMode="on";
480 this.doc.write(this.getDocMarkup());
483 var dbody = (this.doc.body || this.doc.documentElement);
484 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
485 // this copies styles from the containing element into thsi one..
486 // not sure why we need all of this..
487 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
489 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
490 //ss['background-attachment'] = 'fixed'; // w3c
491 dbody.bgProperties = 'fixed'; // ie
492 //Roo.DomHelper.applyStyles(dbody, ss);
493 Roo.EventManager.on(this.doc, {
494 //'mousedown': this.onEditorEvent,
495 'mouseup': this.onEditorEvent,
496 'dblclick': this.onEditorEvent,
497 'click': this.onEditorEvent,
498 'keyup': this.onEditorEvent,
499 'paste': this.onPasteEvent,
504 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
506 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
507 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
509 this.initialized = true;
512 // initialize special key events - enter
513 new Roo.htmleditor.KeyEnter({core : this});
517 this.owner.fireEvent('initialize', this);
521 onPasteEvent : function(e,v) {
522 // default behaveiour should be our local cleanup paste? (optional?)
523 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
524 this.owner.fireEvent('paste', e, v);
527 onDestroy : function(){
533 //for (var i =0; i < this.toolbars.length;i++) {
534 // // fixme - ask toolbars for heights?
535 // this.toolbars[i].onDestroy();
538 //this.wrap.dom.innerHTML = '';
539 //this.wrap.remove();
544 onFirstFocus : function(){
549 this.activated = true;
552 if(Roo.isGecko){ // prevent silly gecko errors
554 var s = this.win.getSelection();
555 if(!s.focusNode || s.focusNode.nodeType != 3){
556 var r = s.getRangeAt(0);
557 r.selectNodeContents((this.doc.body || this.doc.documentElement));
562 this.execCmd('useCSS', true);
563 this.execCmd('styleWithCSS', false);
566 this.owner.fireEvent('activate', this);
570 adjustFont: function(btn){
571 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
572 //if(Roo.isSafari){ // safari
575 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
576 if(Roo.isSafari){ // safari
577 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
578 v = (v < 10) ? 10 : v;
579 v = (v > 48) ? 48 : v;
580 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
585 v = Math.max(1, v+adjust);
587 this.execCmd('FontSize', v );
590 onEditorEvent : function(e)
592 this.owner.fireEvent('editorevent', this, e);
593 // this.updateToolbar();
594 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
597 insertTag : function(tg)
599 // could be a bit smarter... -> wrap the current selected tRoo..
600 if (tg.toLowerCase() == 'span' ||
601 tg.toLowerCase() == 'code' ||
602 tg.toLowerCase() == 'sup' ||
603 tg.toLowerCase() == 'sub'
606 range = this.createRange(this.getSelection());
607 var wrappingNode = this.doc.createElement(tg.toLowerCase());
608 wrappingNode.appendChild(range.extractContents());
609 range.insertNode(wrappingNode);
616 this.execCmd("formatblock", tg);
620 insertText : function(txt)
624 var range = this.createRange();
625 range.deleteContents();
626 //alert(Sender.getAttribute('label'));
628 range.insertNode(this.doc.createTextNode(txt));
634 * Executes a Midas editor command on the editor document and performs necessary focus and
635 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
636 * @param {String} cmd The Midas command
637 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
639 relayCmd : function(cmd, value){
641 this.execCmd(cmd, value);
642 this.owner.fireEvent('editorevent', this);
643 //this.updateToolbar();
644 this.owner.deferFocus();
648 * Executes a Midas editor command directly on the editor document.
649 * For visual commands, you should use {@link #relayCmd} instead.
650 * <b>This should only be called after the editor is initialized.</b>
651 * @param {String} cmd The Midas command
652 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
654 execCmd : function(cmd, value){
655 this.doc.execCommand(cmd, false, value === undefined ? null : value);
662 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
664 * @param {String} text | dom node..
666 insertAtCursor : function(text)
675 var r = this.doc.selection.createRange();
686 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
690 // from jquery ui (MIT licenced)
694 if (win.getSelection && win.getSelection().getRangeAt) {
695 range = win.getSelection().getRangeAt(0);
696 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
697 range.insertNode(node);
698 } else if (win.document.selection && win.document.selection.createRange) {
699 // no firefox support
700 var txt = typeof(text) == 'string' ? text : text.outerHTML;
701 win.document.selection.createRange().pasteHTML(txt);
703 // no firefox support
704 var txt = typeof(text) == 'string' ? text : text.outerHTML;
705 this.execCmd('InsertHTML', txt);
714 mozKeyPress : function(e){
716 var c = e.getCharCode(), cmd;
719 c = String.fromCharCode(c).toLowerCase();
733 // this.cleanUpPaste.defer(100, this);
749 fixKeys : function(){ // load time branching for fastest keydown performance
752 var k = e.getKey(), r;
755 r = this.doc.selection.createRange();
758 r.pasteHTML('    ');
765 r = this.doc.selection.createRange();
767 var target = r.parentElement();
768 if(!target || target.tagName.toLowerCase() != 'li'){
770 r.pasteHTML('<br/>');
776 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
777 // this.cleanUpPaste.defer(100, this);
783 }else if(Roo.isOpera){
789 this.execCmd('InsertHTML','    ');
792 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
793 // this.cleanUpPaste.defer(100, this);
798 }else if(Roo.isSafari){
804 this.execCmd('InsertText','\t');
808 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
809 // this.cleanUpPaste.defer(100, this);
817 getAllAncestors: function()
819 var p = this.getSelectedNode();
822 a.push(p); // push blank onto stack..
823 p = this.getParentElement();
827 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
831 a.push(this.doc.body);
838 getSelection : function()
841 return Roo.isIE ? this.doc.selection : this.win.getSelection();
845 * @param {DomElement} node the node to select
847 selectNode : function(node)
850 var nodeRange = node.ownerDocument.createRange();
852 nodeRange.selectNode(node);
854 nodeRange.selectNodeContents(node);
856 //nodeRange.collapse(true);
857 var s = this.win.getSelection();
859 s.addRange(nodeRange);
862 getSelectedNode: function()
864 // this may only work on Gecko!!!
866 // should we cache this!!!!
871 var range = this.createRange(this.getSelection()).cloneRange();
874 var parent = range.parentElement();
876 var testRange = range.duplicate();
877 testRange.moveToElementText(parent);
878 if (testRange.inRange(range)) {
881 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
884 parent = parent.parentElement;
889 // is ancestor a text element.
890 var ac = range.commonAncestorContainer;
891 if (ac.nodeType == 3) {
895 var ar = ac.childNodes;
898 var other_nodes = [];
899 var has_other_nodes = false;
900 for (var i=0;i<ar.length;i++) {
901 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
904 // fullly contained node.
906 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
911 // probably selected..
912 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
913 other_nodes.push(ar[i]);
917 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
922 has_other_nodes = true;
924 if (!nodes.length && other_nodes.length) {
927 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
933 createRange: function(sel)
935 // this has strange effects when using with
936 // top toolbar - not sure if it's a great idea.
937 //this.editor.contentWindow.focus();
938 if (typeof sel != "undefined") {
940 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
942 return this.doc.createRange();
945 return this.doc.createRange();
948 getParentElement: function()
952 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
954 var range = this.createRange(sel);
957 var p = range.commonAncestorContainer;
958 while (p.nodeType == 3) { // text node
969 * Range intersection.. the hard stuff...
973 * [ -- selected range --- ]
977 * if end is before start or hits it. fail.
978 * if start is after end or hits it fail.
980 * if either hits (but other is outside. - then it's not
986 // @see http://www.thismuchiknow.co.uk/?p=64.
987 rangeIntersectsNode : function(range, node)
989 var nodeRange = node.ownerDocument.createRange();
991 nodeRange.selectNode(node);
993 nodeRange.selectNodeContents(node);
996 var rangeStartRange = range.cloneRange();
997 rangeStartRange.collapse(true);
999 var rangeEndRange = range.cloneRange();
1000 rangeEndRange.collapse(false);
1002 var nodeStartRange = nodeRange.cloneRange();
1003 nodeStartRange.collapse(true);
1005 var nodeEndRange = nodeRange.cloneRange();
1006 nodeEndRange.collapse(false);
1008 return rangeStartRange.compareBoundaryPoints(
1009 Range.START_TO_START, nodeEndRange) == -1 &&
1010 rangeEndRange.compareBoundaryPoints(
1011 Range.START_TO_START, nodeStartRange) == 1;
1015 rangeCompareNode : function(range, node)
1017 var nodeRange = node.ownerDocument.createRange();
1019 nodeRange.selectNode(node);
1021 nodeRange.selectNodeContents(node);
1025 range.collapse(true);
1027 nodeRange.collapse(true);
1029 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
1030 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
1032 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
1034 var nodeIsBefore = ss == 1;
1035 var nodeIsAfter = ee == -1;
1037 if (nodeIsBefore && nodeIsAfter) {
1040 if (!nodeIsBefore && nodeIsAfter) {
1041 return 1; //right trailed.
1044 if (nodeIsBefore && !nodeIsAfter) {
1045 return 2; // left trailed.
1051 // private? - in a new class?
1052 cleanUpPaste : function()
1054 // cleans up the whole document..
1055 Roo.log('cleanuppaste');
1057 this.cleanUpChild(this.doc.body);
1058 var clean = this.cleanWordChars(this.doc.body.innerHTML);
1059 if (clean != this.doc.body.innerHTML) {
1060 this.doc.body.innerHTML = clean;
1065 cleanWordChars : function(input) {// change the chars to hex code
1066 var he = Roo.HtmlEditorCore;
1069 Roo.each(he.swapCodes, function(sw) {
1070 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1072 output = output.replace(swapper, sw[1]);
1082 cleanUpChild : function (node)
1085 new Roo.htmleditor.FilterComment({node : node});
1086 new Roo.htmleditor.FilterAttributes({
1088 attrib_black : this.ablack,
1089 attrib_clean : this.aclean,
1090 style_white : this.cwhite,
1091 style_black : this.cblack
1093 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1094 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1100 * Clean up MS wordisms...
1101 * @deprecated - use filter directly
1103 cleanWord : function(node)
1105 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1112 * @deprecated - use filters
1114 cleanTableWidths : function(node)
1116 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1123 applyBlacklists : function()
1125 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1126 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1128 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1129 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1130 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1134 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1135 if (b.indexOf(tag) > -1) {
1138 this.white.push(tag);
1142 Roo.each(w, function(tag) {
1143 if (b.indexOf(tag) > -1) {
1146 if (this.white.indexOf(tag) > -1) {
1149 this.white.push(tag);
1154 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1155 if (w.indexOf(tag) > -1) {
1158 this.black.push(tag);
1162 Roo.each(b, function(tag) {
1163 if (w.indexOf(tag) > -1) {
1166 if (this.black.indexOf(tag) > -1) {
1169 this.black.push(tag);
1174 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1175 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1179 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1180 if (b.indexOf(tag) > -1) {
1183 this.cwhite.push(tag);
1187 Roo.each(w, function(tag) {
1188 if (b.indexOf(tag) > -1) {
1191 if (this.cwhite.indexOf(tag) > -1) {
1194 this.cwhite.push(tag);
1199 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1200 if (w.indexOf(tag) > -1) {
1203 this.cblack.push(tag);
1207 Roo.each(b, function(tag) {
1208 if (w.indexOf(tag) > -1) {
1211 if (this.cblack.indexOf(tag) > -1) {
1214 this.cblack.push(tag);
1219 setStylesheets : function(stylesheets)
1221 if(typeof(stylesheets) == 'string'){
1222 Roo.get(this.iframe.contentDocument.head).createChild({
1233 Roo.each(stylesheets, function(s) {
1238 Roo.get(_this.iframe.contentDocument.head).createChild({
1249 removeStylesheets : function()
1253 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1258 setStyle : function(style)
1260 Roo.get(this.iframe.contentDocument.head).createChild({
1269 // hide stuff that is not compatible
1287 * @cfg {String} fieldClass @hide
1290 * @cfg {String} focusClass @hide
1293 * @cfg {String} autoCreate @hide
1296 * @cfg {String} inputType @hide
1299 * @cfg {String} invalidClass @hide
1302 * @cfg {String} invalidText @hide
1305 * @cfg {String} msgFx @hide
1308 * @cfg {String} validateOnBlur @hide
1312 Roo.HtmlEditorCore.white = [
1313 'area', 'br', 'img', 'input', 'hr', 'wbr',
1315 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1316 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1317 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1318 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1319 'table', 'ul', 'xmp',
1321 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1324 'dir', 'menu', 'ol', 'ul', 'dl',
1330 Roo.HtmlEditorCore.black = [
1331 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1333 'base', 'basefont', 'bgsound', 'blink', 'body',
1334 'frame', 'frameset', 'head', 'html', 'ilayer',
1335 'iframe', 'layer', 'link', 'meta', 'object',
1336 'script', 'style' ,'title', 'xml' // clean later..
1338 Roo.HtmlEditorCore.clean = [
1339 'script', 'style', 'title', 'xml'
1341 Roo.HtmlEditorCore.tag_remove = [
1346 Roo.HtmlEditorCore.ablack = [
1350 Roo.HtmlEditorCore.aclean = [
1351 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1355 Roo.HtmlEditorCore.pwhite= [
1356 'http', 'https', 'mailto'
1359 // white listed style attributes.
1360 Roo.HtmlEditorCore.cwhite= [
1361 // 'text-align', /// default is to allow most things..
1367 // black listed style attributes.
1368 Roo.HtmlEditorCore.cblack= [
1369 // 'font-size' -- this can be set by the project
1373 Roo.HtmlEditorCore.swapCodes =[
1374 [ 8211, "–" ],
1375 [ 8212, "—" ],