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.log(cd.getData('text/rtf'));
537 Roo.log(cd.getData('text/richtext'));
539 Roo.each(cd.items, function(item) {
542 new Roo.htmleditor.FilterStyleToTag({ node : d });
543 new Roo.htmleditor.FilterAttributes({
545 attrib_white : ['href', 'src', 'name'],
546 attrib_clean : ['href', 'src', 'name']
548 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
550 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
551 new Roo.htmleditor.FilterParagraph({ node : d });
552 new Roo.htmleditor.FilterSpan({ node : d });
553 new Roo.htmleditor.FilterLongBr({ node : d });
557 this.insertAtCursor(d.innerHTML);
561 // default behaveiour should be our local cleanup paste? (optional?)
562 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
563 //this.owner.fireEvent('paste', e, v);
566 onDestroy : function(){
572 //for (var i =0; i < this.toolbars.length;i++) {
573 // // fixme - ask toolbars for heights?
574 // this.toolbars[i].onDestroy();
577 //this.wrap.dom.innerHTML = '';
578 //this.wrap.remove();
583 onFirstFocus : function(){
588 this.activated = true;
591 if(Roo.isGecko){ // prevent silly gecko errors
593 var s = this.win.getSelection();
594 if(!s.focusNode || s.focusNode.nodeType != 3){
595 var r = s.getRangeAt(0);
596 r.selectNodeContents((this.doc.body || this.doc.documentElement));
601 this.execCmd('useCSS', true);
602 this.execCmd('styleWithCSS', false);
605 this.owner.fireEvent('activate', this);
609 adjustFont: function(btn){
610 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
611 //if(Roo.isSafari){ // safari
614 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
615 if(Roo.isSafari){ // safari
616 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
617 v = (v < 10) ? 10 : v;
618 v = (v > 48) ? 48 : v;
619 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
624 v = Math.max(1, v+adjust);
626 this.execCmd('FontSize', v );
629 onEditorEvent : function(e)
631 this.owner.fireEvent('editorevent', this, e);
632 // this.updateToolbar();
633 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
636 insertTag : function(tg)
638 // could be a bit smarter... -> wrap the current selected tRoo..
639 if (tg.toLowerCase() == 'span' ||
640 tg.toLowerCase() == 'code' ||
641 tg.toLowerCase() == 'sup' ||
642 tg.toLowerCase() == 'sub'
645 range = this.createRange(this.getSelection());
646 var wrappingNode = this.doc.createElement(tg.toLowerCase());
647 wrappingNode.appendChild(range.extractContents());
648 range.insertNode(wrappingNode);
655 this.execCmd("formatblock", tg);
659 insertText : function(txt)
663 var range = this.createRange();
664 range.deleteContents();
665 //alert(Sender.getAttribute('label'));
667 range.insertNode(this.doc.createTextNode(txt));
673 * Executes a Midas editor command on the editor document and performs necessary focus and
674 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
675 * @param {String} cmd The Midas command
676 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
678 relayCmd : function(cmd, value){
680 this.execCmd(cmd, value);
681 this.owner.fireEvent('editorevent', this);
682 //this.updateToolbar();
683 this.owner.deferFocus();
687 * Executes a Midas editor command directly on the editor document.
688 * For visual commands, you should use {@link #relayCmd} instead.
689 * <b>This should only be called after the editor is initialized.</b>
690 * @param {String} cmd The Midas command
691 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
693 execCmd : function(cmd, value){
694 this.doc.execCommand(cmd, false, value === undefined ? null : value);
701 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
703 * @param {String} text | dom node..
705 insertAtCursor : function(text)
712 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
716 // from jquery ui (MIT licenced)
720 if (win.getSelection && win.getSelection().getRangeAt) {
722 // delete the existing?
724 this.createRange(this.getSelection()).deleteContents();
725 range = win.getSelection().getRangeAt(0);
726 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
727 range.insertNode(node);
728 } else if (win.document.selection && win.document.selection.createRange) {
729 // no firefox support
730 var txt = typeof(text) == 'string' ? text : text.outerHTML;
731 win.document.selection.createRange().pasteHTML(txt);
733 // no firefox support
734 var txt = typeof(text) == 'string' ? text : text.outerHTML;
735 this.execCmd('InsertHTML', txt);
744 mozKeyPress : function(e){
746 var c = e.getCharCode(), cmd;
749 c = String.fromCharCode(c).toLowerCase();
763 // this.cleanUpPaste.defer(100, this);
779 fixKeys : function(){ // load time branching for fastest keydown performance
782 var k = e.getKey(), r;
785 r = this.doc.selection.createRange();
788 r.pasteHTML('    ');
795 r = this.doc.selection.createRange();
797 var target = r.parentElement();
798 if(!target || target.tagName.toLowerCase() != 'li'){
800 r.pasteHTML('<br/>');
806 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
807 // this.cleanUpPaste.defer(100, this);
813 }else if(Roo.isOpera){
819 this.execCmd('InsertHTML','    ');
822 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
823 // this.cleanUpPaste.defer(100, this);
828 }else if(Roo.isSafari){
834 this.execCmd('InsertText','\t');
838 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
839 // this.cleanUpPaste.defer(100, this);
847 getAllAncestors: function()
849 var p = this.getSelectedNode();
852 a.push(p); // push blank onto stack..
853 p = this.getParentElement();
857 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
861 a.push(this.doc.body);
868 getSelection : function()
871 return Roo.isIE ? this.doc.selection : this.win.getSelection();
875 * @param {DomElement} node the node to select
877 selectNode : function(node)
880 var nodeRange = node.ownerDocument.createRange();
882 nodeRange.selectNode(node);
884 nodeRange.selectNodeContents(node);
886 //nodeRange.collapse(true);
887 var s = this.win.getSelection();
889 s.addRange(nodeRange);
892 getSelectedNode: function()
894 // this may only work on Gecko!!!
896 // should we cache this!!!!
901 var range = this.createRange(this.getSelection()).cloneRange();
904 var parent = range.parentElement();
906 var testRange = range.duplicate();
907 testRange.moveToElementText(parent);
908 if (testRange.inRange(range)) {
911 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
914 parent = parent.parentElement;
919 // is ancestor a text element.
920 var ac = range.commonAncestorContainer;
921 if (ac.nodeType == 3) {
925 var ar = ac.childNodes;
928 var other_nodes = [];
929 var has_other_nodes = false;
930 for (var i=0;i<ar.length;i++) {
931 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
934 // fullly contained node.
936 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
941 // probably selected..
942 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
943 other_nodes.push(ar[i]);
947 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
952 has_other_nodes = true;
954 if (!nodes.length && other_nodes.length) {
957 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
963 createRange: function(sel)
965 // this has strange effects when using with
966 // top toolbar - not sure if it's a great idea.
967 //this.editor.contentWindow.focus();
968 if (typeof sel != "undefined") {
970 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
972 return this.doc.createRange();
975 return this.doc.createRange();
978 getParentElement: function()
982 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
984 var range = this.createRange(sel);
987 var p = range.commonAncestorContainer;
988 while (p.nodeType == 3) { // text node
999 * Range intersection.. the hard stuff...
1003 * [ -- selected range --- ]
1007 * if end is before start or hits it. fail.
1008 * if start is after end or hits it fail.
1010 * if either hits (but other is outside. - then it's not
1016 // @see http://www.thismuchiknow.co.uk/?p=64.
1017 rangeIntersectsNode : function(range, node)
1019 var nodeRange = node.ownerDocument.createRange();
1021 nodeRange.selectNode(node);
1023 nodeRange.selectNodeContents(node);
1026 var rangeStartRange = range.cloneRange();
1027 rangeStartRange.collapse(true);
1029 var rangeEndRange = range.cloneRange();
1030 rangeEndRange.collapse(false);
1032 var nodeStartRange = nodeRange.cloneRange();
1033 nodeStartRange.collapse(true);
1035 var nodeEndRange = nodeRange.cloneRange();
1036 nodeEndRange.collapse(false);
1038 return rangeStartRange.compareBoundaryPoints(
1039 Range.START_TO_START, nodeEndRange) == -1 &&
1040 rangeEndRange.compareBoundaryPoints(
1041 Range.START_TO_START, nodeStartRange) == 1;
1045 rangeCompareNode : function(range, node)
1047 var nodeRange = node.ownerDocument.createRange();
1049 nodeRange.selectNode(node);
1051 nodeRange.selectNodeContents(node);
1055 range.collapse(true);
1057 nodeRange.collapse(true);
1059 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
1060 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
1062 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
1064 var nodeIsBefore = ss == 1;
1065 var nodeIsAfter = ee == -1;
1067 if (nodeIsBefore && nodeIsAfter) {
1070 if (!nodeIsBefore && nodeIsAfter) {
1071 return 1; //right trailed.
1074 if (nodeIsBefore && !nodeIsAfter) {
1075 return 2; // left trailed.
1081 cleanWordChars : function(input) {// change the chars to hex code
1084 [ 8211, "–" ],
1085 [ 8212, "—" ],
1094 Roo.each(swapCodes, function(sw) {
1095 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1097 output = output.replace(swapper, sw[1]);
1107 cleanUpChild : function (node)
1110 new Roo.htmleditor.FilterComment({node : node});
1111 new Roo.htmleditor.FilterAttributes({
1113 attrib_black : this.ablack,
1114 attrib_clean : this.aclean,
1115 style_white : this.cwhite,
1116 style_black : this.cblack
1118 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1119 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1125 * Clean up MS wordisms...
1126 * @deprecated - use filter directly
1128 cleanWord : function(node)
1130 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1137 * @deprecated - use filters
1139 cleanTableWidths : function(node)
1141 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1148 applyBlacklists : function()
1150 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1151 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1153 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1154 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1155 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1159 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1160 if (b.indexOf(tag) > -1) {
1163 this.white.push(tag);
1167 Roo.each(w, function(tag) {
1168 if (b.indexOf(tag) > -1) {
1171 if (this.white.indexOf(tag) > -1) {
1174 this.white.push(tag);
1179 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1180 if (w.indexOf(tag) > -1) {
1183 this.black.push(tag);
1187 Roo.each(b, function(tag) {
1188 if (w.indexOf(tag) > -1) {
1191 if (this.black.indexOf(tag) > -1) {
1194 this.black.push(tag);
1199 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1200 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1204 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1205 if (b.indexOf(tag) > -1) {
1208 this.cwhite.push(tag);
1212 Roo.each(w, function(tag) {
1213 if (b.indexOf(tag) > -1) {
1216 if (this.cwhite.indexOf(tag) > -1) {
1219 this.cwhite.push(tag);
1224 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1225 if (w.indexOf(tag) > -1) {
1228 this.cblack.push(tag);
1232 Roo.each(b, function(tag) {
1233 if (w.indexOf(tag) > -1) {
1236 if (this.cblack.indexOf(tag) > -1) {
1239 this.cblack.push(tag);
1244 setStylesheets : function(stylesheets)
1246 if(typeof(stylesheets) == 'string'){
1247 Roo.get(this.iframe.contentDocument.head).createChild({
1258 Roo.each(stylesheets, function(s) {
1263 Roo.get(_this.iframe.contentDocument.head).createChild({
1274 removeStylesheets : function()
1278 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1283 setStyle : function(style)
1285 Roo.get(this.iframe.contentDocument.head).createChild({
1294 // hide stuff that is not compatible
1312 * @cfg {String} fieldClass @hide
1315 * @cfg {String} focusClass @hide
1318 * @cfg {String} autoCreate @hide
1321 * @cfg {String} inputType @hide
1324 * @cfg {String} invalidClass @hide
1327 * @cfg {String} invalidText @hide
1330 * @cfg {String} msgFx @hide
1333 * @cfg {String} validateOnBlur @hide
1337 Roo.HtmlEditorCore.white = [
1338 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
1340 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
1341 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
1342 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
1343 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
1344 'TABLE', 'UL', 'XMP',
1346 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
1349 'DIR', 'MENU', 'OL', 'UL', 'DL',
1355 Roo.HtmlEditorCore.black = [
1356 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1358 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
1359 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
1360 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
1361 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
1362 //'FONT' // CLEAN LATER..
1363 'COLGROUP', 'COL' // messy tables.
1366 Roo.HtmlEditorCore.clean = [ // ?? needed???
1367 'SCRIPT', 'STYLE', 'TITLE', 'XML'
1369 Roo.HtmlEditorCore.tag_remove = [
1374 Roo.HtmlEditorCore.ablack = [
1378 Roo.HtmlEditorCore.aclean = [
1379 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1383 Roo.HtmlEditorCore.pwhite= [
1384 'http', 'https', 'mailto'
1387 // white listed style attributes.
1388 Roo.HtmlEditorCore.cwhite= [
1389 // 'text-align', /// default is to allow most things..
1395 // black listed style attributes.
1396 Roo.HtmlEditorCore.cblack= [
1397 // 'font-size' -- this can be set by the project