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);
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,
503 Roo.EventManager.on(this.doc, {
504 'paste': this.onPasteEvent,
508 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
510 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
511 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
513 this.initialized = true;
516 // initialize special key events - enter
517 new Roo.htmleditor.KeyEnter({core : this});
521 this.owner.fireEvent('initialize', this);
525 onPasteEvent : function(e,v)
527 // I think we better assume paste is going to be a dirty load of rubish from word..
529 // even pasting into a 'email version' of this widget will have to clean up that mess.
530 var cd = (e.browserEvent.clipboardData || window.clipboardData);
532 var html = cd.getData('text/html'); // clipboard event
533 html = this.cleanWordChars(html);
535 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
536 Roo.each(d.items, function(item) {
539 new Roo.htmleditor.FilterStyleToTag({ node : d });
540 new Roo.htmleditor.FilterAttributes({
542 attrib_white : ['href', 'src', 'name'],
543 attrib_clean : ['href', 'src', 'name']
545 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
547 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
548 new Roo.htmleditor.FilterParagraph({ node : d });
549 new Roo.htmleditor.FilterSpan({ node : d });
550 new Roo.htmleditor.FilterLongBr({ node : d });
554 this.insertAtCursor(d.innerHTML);
558 // default behaveiour should be our local cleanup paste? (optional?)
559 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
560 //this.owner.fireEvent('paste', e, v);
563 onDestroy : function(){
569 //for (var i =0; i < this.toolbars.length;i++) {
570 // // fixme - ask toolbars for heights?
571 // this.toolbars[i].onDestroy();
574 //this.wrap.dom.innerHTML = '';
575 //this.wrap.remove();
580 onFirstFocus : function(){
585 this.activated = true;
588 if(Roo.isGecko){ // prevent silly gecko errors
590 var s = this.win.getSelection();
591 if(!s.focusNode || s.focusNode.nodeType != 3){
592 var r = s.getRangeAt(0);
593 r.selectNodeContents((this.doc.body || this.doc.documentElement));
598 this.execCmd('useCSS', true);
599 this.execCmd('styleWithCSS', false);
602 this.owner.fireEvent('activate', this);
606 adjustFont: function(btn){
607 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
608 //if(Roo.isSafari){ // safari
611 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
612 if(Roo.isSafari){ // safari
613 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
614 v = (v < 10) ? 10 : v;
615 v = (v > 48) ? 48 : v;
616 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
621 v = Math.max(1, v+adjust);
623 this.execCmd('FontSize', v );
626 onEditorEvent : function(e)
628 this.owner.fireEvent('editorevent', this, e);
629 // this.updateToolbar();
630 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
633 insertTag : function(tg)
635 // could be a bit smarter... -> wrap the current selected tRoo..
636 if (tg.toLowerCase() == 'span' ||
637 tg.toLowerCase() == 'code' ||
638 tg.toLowerCase() == 'sup' ||
639 tg.toLowerCase() == 'sub'
642 range = this.createRange(this.getSelection());
643 var wrappingNode = this.doc.createElement(tg.toLowerCase());
644 wrappingNode.appendChild(range.extractContents());
645 range.insertNode(wrappingNode);
652 this.execCmd("formatblock", tg);
656 insertText : function(txt)
660 var range = this.createRange();
661 range.deleteContents();
662 //alert(Sender.getAttribute('label'));
664 range.insertNode(this.doc.createTextNode(txt));
670 * Executes a Midas editor command on the editor document and performs necessary focus and
671 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
672 * @param {String} cmd The Midas command
673 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
675 relayCmd : function(cmd, value){
677 this.execCmd(cmd, value);
678 this.owner.fireEvent('editorevent', this);
679 //this.updateToolbar();
680 this.owner.deferFocus();
684 * Executes a Midas editor command directly on the editor document.
685 * For visual commands, you should use {@link #relayCmd} instead.
686 * <b>This should only be called after the editor is initialized.</b>
687 * @param {String} cmd The Midas command
688 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
690 execCmd : function(cmd, value){
691 this.doc.execCommand(cmd, false, value === undefined ? null : value);
698 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
700 * @param {String} text | dom node..
702 insertAtCursor : function(text)
709 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
713 // from jquery ui (MIT licenced)
717 if (win.getSelection && win.getSelection().getRangeAt) {
719 // delete the existing?
721 this.createRange(this.getSelection()).deleteContents();
722 range = win.getSelection().getRangeAt(0);
723 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
724 range.insertNode(node);
725 } else if (win.document.selection && win.document.selection.createRange) {
726 // no firefox support
727 var txt = typeof(text) == 'string' ? text : text.outerHTML;
728 win.document.selection.createRange().pasteHTML(txt);
730 // no firefox support
731 var txt = typeof(text) == 'string' ? text : text.outerHTML;
732 this.execCmd('InsertHTML', txt);
741 mozKeyPress : function(e){
743 var c = e.getCharCode(), cmd;
746 c = String.fromCharCode(c).toLowerCase();
760 // this.cleanUpPaste.defer(100, this);
776 fixKeys : function(){ // load time branching for fastest keydown performance
779 var k = e.getKey(), r;
782 r = this.doc.selection.createRange();
785 r.pasteHTML('    ');
792 r = this.doc.selection.createRange();
794 var target = r.parentElement();
795 if(!target || target.tagName.toLowerCase() != 'li'){
797 r.pasteHTML('<br/>');
803 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
804 // this.cleanUpPaste.defer(100, this);
810 }else if(Roo.isOpera){
816 this.execCmd('InsertHTML','    ');
819 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
820 // this.cleanUpPaste.defer(100, this);
825 }else if(Roo.isSafari){
831 this.execCmd('InsertText','\t');
835 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
836 // this.cleanUpPaste.defer(100, this);
844 getAllAncestors: function()
846 var p = this.getSelectedNode();
849 a.push(p); // push blank onto stack..
850 p = this.getParentElement();
854 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
858 a.push(this.doc.body);
865 getSelection : function()
868 return Roo.isIE ? this.doc.selection : this.win.getSelection();
872 * @param {DomElement} node the node to select
874 selectNode : function(node)
877 var nodeRange = node.ownerDocument.createRange();
879 nodeRange.selectNode(node);
881 nodeRange.selectNodeContents(node);
883 //nodeRange.collapse(true);
884 var s = this.win.getSelection();
886 s.addRange(nodeRange);
889 getSelectedNode: function()
891 // this may only work on Gecko!!!
893 // should we cache this!!!!
898 var range = this.createRange(this.getSelection()).cloneRange();
901 var parent = range.parentElement();
903 var testRange = range.duplicate();
904 testRange.moveToElementText(parent);
905 if (testRange.inRange(range)) {
908 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
911 parent = parent.parentElement;
916 // is ancestor a text element.
917 var ac = range.commonAncestorContainer;
918 if (ac.nodeType == 3) {
922 var ar = ac.childNodes;
925 var other_nodes = [];
926 var has_other_nodes = false;
927 for (var i=0;i<ar.length;i++) {
928 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
931 // fullly contained node.
933 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
938 // probably selected..
939 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
940 other_nodes.push(ar[i]);
944 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
949 has_other_nodes = true;
951 if (!nodes.length && other_nodes.length) {
954 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
960 createRange: function(sel)
962 // this has strange effects when using with
963 // top toolbar - not sure if it's a great idea.
964 //this.editor.contentWindow.focus();
965 if (typeof sel != "undefined") {
967 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
969 return this.doc.createRange();
972 return this.doc.createRange();
975 getParentElement: function()
979 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
981 var range = this.createRange(sel);
984 var p = range.commonAncestorContainer;
985 while (p.nodeType == 3) { // text node
996 * Range intersection.. the hard stuff...
1000 * [ -- selected range --- ]
1004 * if end is before start or hits it. fail.
1005 * if start is after end or hits it fail.
1007 * if either hits (but other is outside. - then it's not
1013 // @see http://www.thismuchiknow.co.uk/?p=64.
1014 rangeIntersectsNode : function(range, node)
1016 var nodeRange = node.ownerDocument.createRange();
1018 nodeRange.selectNode(node);
1020 nodeRange.selectNodeContents(node);
1023 var rangeStartRange = range.cloneRange();
1024 rangeStartRange.collapse(true);
1026 var rangeEndRange = range.cloneRange();
1027 rangeEndRange.collapse(false);
1029 var nodeStartRange = nodeRange.cloneRange();
1030 nodeStartRange.collapse(true);
1032 var nodeEndRange = nodeRange.cloneRange();
1033 nodeEndRange.collapse(false);
1035 return rangeStartRange.compareBoundaryPoints(
1036 Range.START_TO_START, nodeEndRange) == -1 &&
1037 rangeEndRange.compareBoundaryPoints(
1038 Range.START_TO_START, nodeStartRange) == 1;
1042 rangeCompareNode : function(range, node)
1044 var nodeRange = node.ownerDocument.createRange();
1046 nodeRange.selectNode(node);
1048 nodeRange.selectNodeContents(node);
1052 range.collapse(true);
1054 nodeRange.collapse(true);
1056 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
1057 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
1059 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
1061 var nodeIsBefore = ss == 1;
1062 var nodeIsAfter = ee == -1;
1064 if (nodeIsBefore && nodeIsAfter) {
1067 if (!nodeIsBefore && nodeIsAfter) {
1068 return 1; //right trailed.
1071 if (nodeIsBefore && !nodeIsAfter) {
1072 return 2; // left trailed.
1078 cleanWordChars : function(input) {// change the chars to hex code
1081 [ 8211, "–" ],
1082 [ 8212, "—" ],
1091 Roo.each(swapCodes, function(sw) {
1092 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1094 output = output.replace(swapper, sw[1]);
1104 cleanUpChild : function (node)
1107 new Roo.htmleditor.FilterComment({node : node});
1108 new Roo.htmleditor.FilterAttributes({
1110 attrib_black : this.ablack,
1111 attrib_clean : this.aclean,
1112 style_white : this.cwhite,
1113 style_black : this.cblack
1115 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1116 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1122 * Clean up MS wordisms...
1123 * @deprecated - use filter directly
1125 cleanWord : function(node)
1127 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1134 * @deprecated - use filters
1136 cleanTableWidths : function(node)
1138 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1145 applyBlacklists : function()
1147 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1148 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1150 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1151 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1152 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1156 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1157 if (b.indexOf(tag) > -1) {
1160 this.white.push(tag);
1164 Roo.each(w, function(tag) {
1165 if (b.indexOf(tag) > -1) {
1168 if (this.white.indexOf(tag) > -1) {
1171 this.white.push(tag);
1176 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1177 if (w.indexOf(tag) > -1) {
1180 this.black.push(tag);
1184 Roo.each(b, function(tag) {
1185 if (w.indexOf(tag) > -1) {
1188 if (this.black.indexOf(tag) > -1) {
1191 this.black.push(tag);
1196 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1197 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1201 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1202 if (b.indexOf(tag) > -1) {
1205 this.cwhite.push(tag);
1209 Roo.each(w, function(tag) {
1210 if (b.indexOf(tag) > -1) {
1213 if (this.cwhite.indexOf(tag) > -1) {
1216 this.cwhite.push(tag);
1221 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1222 if (w.indexOf(tag) > -1) {
1225 this.cblack.push(tag);
1229 Roo.each(b, function(tag) {
1230 if (w.indexOf(tag) > -1) {
1233 if (this.cblack.indexOf(tag) > -1) {
1236 this.cblack.push(tag);
1241 setStylesheets : function(stylesheets)
1243 if(typeof(stylesheets) == 'string'){
1244 Roo.get(this.iframe.contentDocument.head).createChild({
1255 Roo.each(stylesheets, function(s) {
1260 Roo.get(_this.iframe.contentDocument.head).createChild({
1271 removeStylesheets : function()
1275 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1280 setStyle : function(style)
1282 Roo.get(this.iframe.contentDocument.head).createChild({
1291 // hide stuff that is not compatible
1309 * @cfg {String} fieldClass @hide
1312 * @cfg {String} focusClass @hide
1315 * @cfg {String} autoCreate @hide
1318 * @cfg {String} inputType @hide
1321 * @cfg {String} invalidClass @hide
1324 * @cfg {String} invalidText @hide
1327 * @cfg {String} msgFx @hide
1330 * @cfg {String} validateOnBlur @hide
1334 Roo.HtmlEditorCore.white = [
1335 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
1337 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
1338 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
1339 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
1340 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
1341 'TABLE', 'UL', 'XMP',
1343 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
1346 'DIR', 'MENU', 'OL', 'UL', 'DL',
1352 Roo.HtmlEditorCore.black = [
1353 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1355 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
1356 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
1357 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
1358 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
1359 //'FONT' // CLEAN LATER..
1360 'COLGROUP', 'COL' // messy tables.
1363 Roo.HtmlEditorCore.clean = [ // ?? needed???
1364 'SCRIPT', 'STYLE', 'TITLE', 'XML'
1366 Roo.HtmlEditorCore.tag_remove = [
1371 Roo.HtmlEditorCore.ablack = [
1375 Roo.HtmlEditorCore.aclean = [
1376 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1380 Roo.HtmlEditorCore.pwhite= [
1381 'http', 'https', 'mailto'
1384 // white listed style attributes.
1385 Roo.HtmlEditorCore.cwhite= [
1386 // 'text-align', /// default is to allow most things..
1392 // black listed style attributes.
1393 Roo.HtmlEditorCore.cblack= [
1394 // 'font-size' -- this can be set by the project