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 // check what type of paste - if it's an image, then handle it differently.
533 if (cd.files.length > 0) {
535 var urlAPI = (window.createObjectURL && window) ||
536 (window.URL && URL.revokeObjectURL && URL) ||
537 (window.webkitURL && webkitURL);
539 var url = urlAPI.createObjectURL( cd.files[0]);
540 this.insertAtCursor('<img src=" + url + ">');
544 var html = cd.getData('text/html'); // clipboard event
545 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
546 var images = parser.doc.getElementsByType('pict');
550 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl)/); }) // ignore headers
551 .map(function(g) { return g.toDataURL(); });
554 html = this.cleanWordChars(html);
556 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
558 if (images.length > 0) {
559 Roo.each(d.getElementsByTagName('img'), function(img, i) {
560 img.setAttribute('src', images[i]);
565 new Roo.htmleditor.FilterStyleToTag({ node : d });
566 new Roo.htmleditor.FilterAttributes({
568 attrib_white : ['href', 'src', 'name', 'align'],
569 attrib_clean : ['href', 'src' ]
571 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
573 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
574 new Roo.htmleditor.FilterParagraph({ node : d });
575 new Roo.htmleditor.FilterSpan({ node : d });
576 new Roo.htmleditor.FilterLongBr({ node : d });
580 this.insertAtCursor(d.innerHTML);
584 // default behaveiour should be our local cleanup paste? (optional?)
585 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
586 //this.owner.fireEvent('paste', e, v);
589 onDestroy : function(){
595 //for (var i =0; i < this.toolbars.length;i++) {
596 // // fixme - ask toolbars for heights?
597 // this.toolbars[i].onDestroy();
600 //this.wrap.dom.innerHTML = '';
601 //this.wrap.remove();
606 onFirstFocus : function(){
611 this.activated = true;
614 if(Roo.isGecko){ // prevent silly gecko errors
616 var s = this.win.getSelection();
617 if(!s.focusNode || s.focusNode.nodeType != 3){
618 var r = s.getRangeAt(0);
619 r.selectNodeContents((this.doc.body || this.doc.documentElement));
624 this.execCmd('useCSS', true);
625 this.execCmd('styleWithCSS', false);
628 this.owner.fireEvent('activate', this);
632 adjustFont: function(btn){
633 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
634 //if(Roo.isSafari){ // safari
637 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
638 if(Roo.isSafari){ // safari
639 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
640 v = (v < 10) ? 10 : v;
641 v = (v > 48) ? 48 : v;
642 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
647 v = Math.max(1, v+adjust);
649 this.execCmd('FontSize', v );
652 onEditorEvent : function(e)
654 this.owner.fireEvent('editorevent', this, e);
655 // this.updateToolbar();
656 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
659 insertTag : function(tg)
661 // could be a bit smarter... -> wrap the current selected tRoo..
662 if (tg.toLowerCase() == 'span' ||
663 tg.toLowerCase() == 'code' ||
664 tg.toLowerCase() == 'sup' ||
665 tg.toLowerCase() == 'sub'
668 range = this.createRange(this.getSelection());
669 var wrappingNode = this.doc.createElement(tg.toLowerCase());
670 wrappingNode.appendChild(range.extractContents());
671 range.insertNode(wrappingNode);
678 this.execCmd("formatblock", tg);
682 insertText : function(txt)
686 var range = this.createRange();
687 range.deleteContents();
688 //alert(Sender.getAttribute('label'));
690 range.insertNode(this.doc.createTextNode(txt));
696 * Executes a Midas editor command on the editor document and performs necessary focus and
697 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
698 * @param {String} cmd The Midas command
699 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
701 relayCmd : function(cmd, value){
703 this.execCmd(cmd, value);
704 this.owner.fireEvent('editorevent', this);
705 //this.updateToolbar();
706 this.owner.deferFocus();
710 * Executes a Midas editor command directly on the editor document.
711 * For visual commands, you should use {@link #relayCmd} instead.
712 * <b>This should only be called after the editor is initialized.</b>
713 * @param {String} cmd The Midas command
714 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
716 execCmd : function(cmd, value){
717 this.doc.execCommand(cmd, false, value === undefined ? null : value);
724 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
726 * @param {String} text | dom node..
728 insertAtCursor : function(text)
735 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
739 // from jquery ui (MIT licenced)
743 if (win.getSelection && win.getSelection().getRangeAt) {
745 // delete the existing?
747 this.createRange(this.getSelection()).deleteContents();
748 range = win.getSelection().getRangeAt(0);
749 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
750 range.insertNode(node);
751 } else if (win.document.selection && win.document.selection.createRange) {
752 // no firefox support
753 var txt = typeof(text) == 'string' ? text : text.outerHTML;
754 win.document.selection.createRange().pasteHTML(txt);
756 // no firefox support
757 var txt = typeof(text) == 'string' ? text : text.outerHTML;
758 this.execCmd('InsertHTML', txt);
767 mozKeyPress : function(e){
769 var c = e.getCharCode(), cmd;
772 c = String.fromCharCode(c).toLowerCase();
786 // this.cleanUpPaste.defer(100, this);
802 fixKeys : function(){ // load time branching for fastest keydown performance
805 var k = e.getKey(), r;
808 r = this.doc.selection.createRange();
811 r.pasteHTML('    ');
818 r = this.doc.selection.createRange();
820 var target = r.parentElement();
821 if(!target || target.tagName.toLowerCase() != 'li'){
823 r.pasteHTML('<br/>');
829 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
830 // this.cleanUpPaste.defer(100, this);
836 }else if(Roo.isOpera){
842 this.execCmd('InsertHTML','    ');
845 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
846 // this.cleanUpPaste.defer(100, this);
851 }else if(Roo.isSafari){
857 this.execCmd('InsertText','\t');
861 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
862 // this.cleanUpPaste.defer(100, this);
870 getAllAncestors: function()
872 var p = this.getSelectedNode();
875 a.push(p); // push blank onto stack..
876 p = this.getParentElement();
880 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
884 a.push(this.doc.body);
891 getSelection : function()
894 return Roo.isIE ? this.doc.selection : this.win.getSelection();
898 * @param {DomElement} node the node to select
900 selectNode : function(node)
903 var nodeRange = node.ownerDocument.createRange();
905 nodeRange.selectNode(node);
907 nodeRange.selectNodeContents(node);
909 //nodeRange.collapse(true);
910 var s = this.win.getSelection();
912 s.addRange(nodeRange);
915 getSelectedNode: function()
917 // this may only work on Gecko!!!
919 // should we cache this!!!!
924 var range = this.createRange(this.getSelection()).cloneRange();
927 var parent = range.parentElement();
929 var testRange = range.duplicate();
930 testRange.moveToElementText(parent);
931 if (testRange.inRange(range)) {
934 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
937 parent = parent.parentElement;
942 // is ancestor a text element.
943 var ac = range.commonAncestorContainer;
944 if (ac.nodeType == 3) {
948 var ar = ac.childNodes;
951 var other_nodes = [];
952 var has_other_nodes = false;
953 for (var i=0;i<ar.length;i++) {
954 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
957 // fullly contained node.
959 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
964 // probably selected..
965 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
966 other_nodes.push(ar[i]);
970 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
975 has_other_nodes = true;
977 if (!nodes.length && other_nodes.length) {
980 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
986 createRange: function(sel)
988 // this has strange effects when using with
989 // top toolbar - not sure if it's a great idea.
990 //this.editor.contentWindow.focus();
991 if (typeof sel != "undefined") {
993 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
995 return this.doc.createRange();
998 return this.doc.createRange();
1001 getParentElement: function()
1004 this.assignDocWin();
1005 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
1007 var range = this.createRange(sel);
1010 var p = range.commonAncestorContainer;
1011 while (p.nodeType == 3) { // text node
1022 * Range intersection.. the hard stuff...
1026 * [ -- selected range --- ]
1030 * if end is before start or hits it. fail.
1031 * if start is after end or hits it fail.
1033 * if either hits (but other is outside. - then it's not
1039 // @see http://www.thismuchiknow.co.uk/?p=64.
1040 rangeIntersectsNode : function(range, node)
1042 var nodeRange = node.ownerDocument.createRange();
1044 nodeRange.selectNode(node);
1046 nodeRange.selectNodeContents(node);
1049 var rangeStartRange = range.cloneRange();
1050 rangeStartRange.collapse(true);
1052 var rangeEndRange = range.cloneRange();
1053 rangeEndRange.collapse(false);
1055 var nodeStartRange = nodeRange.cloneRange();
1056 nodeStartRange.collapse(true);
1058 var nodeEndRange = nodeRange.cloneRange();
1059 nodeEndRange.collapse(false);
1061 return rangeStartRange.compareBoundaryPoints(
1062 Range.START_TO_START, nodeEndRange) == -1 &&
1063 rangeEndRange.compareBoundaryPoints(
1064 Range.START_TO_START, nodeStartRange) == 1;
1068 rangeCompareNode : function(range, node)
1070 var nodeRange = node.ownerDocument.createRange();
1072 nodeRange.selectNode(node);
1074 nodeRange.selectNodeContents(node);
1078 range.collapse(true);
1080 nodeRange.collapse(true);
1082 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
1083 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
1085 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
1087 var nodeIsBefore = ss == 1;
1088 var nodeIsAfter = ee == -1;
1090 if (nodeIsBefore && nodeIsAfter) {
1093 if (!nodeIsBefore && nodeIsAfter) {
1094 return 1; //right trailed.
1097 if (nodeIsBefore && !nodeIsAfter) {
1098 return 2; // left trailed.
1104 cleanWordChars : function(input) {// change the chars to hex code
1107 [ 8211, "–" ],
1108 [ 8212, "—" ],
1117 Roo.each(swapCodes, function(sw) {
1118 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1120 output = output.replace(swapper, sw[1]);
1130 cleanUpChild : function (node)
1133 new Roo.htmleditor.FilterComment({node : node});
1134 new Roo.htmleditor.FilterAttributes({
1136 attrib_black : this.ablack,
1137 attrib_clean : this.aclean,
1138 style_white : this.cwhite,
1139 style_black : this.cblack
1141 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1142 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1148 * Clean up MS wordisms...
1149 * @deprecated - use filter directly
1151 cleanWord : function(node)
1153 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1160 * @deprecated - use filters
1162 cleanTableWidths : function(node)
1164 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1171 applyBlacklists : function()
1173 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1174 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1176 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1177 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1178 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1182 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1183 if (b.indexOf(tag) > -1) {
1186 this.white.push(tag);
1190 Roo.each(w, function(tag) {
1191 if (b.indexOf(tag) > -1) {
1194 if (this.white.indexOf(tag) > -1) {
1197 this.white.push(tag);
1202 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1203 if (w.indexOf(tag) > -1) {
1206 this.black.push(tag);
1210 Roo.each(b, function(tag) {
1211 if (w.indexOf(tag) > -1) {
1214 if (this.black.indexOf(tag) > -1) {
1217 this.black.push(tag);
1222 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1223 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1227 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1228 if (b.indexOf(tag) > -1) {
1231 this.cwhite.push(tag);
1235 Roo.each(w, function(tag) {
1236 if (b.indexOf(tag) > -1) {
1239 if (this.cwhite.indexOf(tag) > -1) {
1242 this.cwhite.push(tag);
1247 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1248 if (w.indexOf(tag) > -1) {
1251 this.cblack.push(tag);
1255 Roo.each(b, function(tag) {
1256 if (w.indexOf(tag) > -1) {
1259 if (this.cblack.indexOf(tag) > -1) {
1262 this.cblack.push(tag);
1267 setStylesheets : function(stylesheets)
1269 if(typeof(stylesheets) == 'string'){
1270 Roo.get(this.iframe.contentDocument.head).createChild({
1281 Roo.each(stylesheets, function(s) {
1286 Roo.get(_this.iframe.contentDocument.head).createChild({
1297 removeStylesheets : function()
1301 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1306 setStyle : function(style)
1308 Roo.get(this.iframe.contentDocument.head).createChild({
1317 // hide stuff that is not compatible
1335 * @cfg {String} fieldClass @hide
1338 * @cfg {String} focusClass @hide
1341 * @cfg {String} autoCreate @hide
1344 * @cfg {String} inputType @hide
1347 * @cfg {String} invalidClass @hide
1350 * @cfg {String} invalidText @hide
1353 * @cfg {String} msgFx @hide
1356 * @cfg {String} validateOnBlur @hide
1360 Roo.HtmlEditorCore.white = [
1361 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
1363 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
1364 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
1365 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
1366 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
1367 'TABLE', 'UL', 'XMP',
1369 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
1372 'DIR', 'MENU', 'OL', 'UL', 'DL',
1378 Roo.HtmlEditorCore.black = [
1379 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1381 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
1382 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
1383 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
1384 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
1385 //'FONT' // CLEAN LATER..
1386 'COLGROUP', 'COL' // messy tables.
1389 Roo.HtmlEditorCore.clean = [ // ?? needed???
1390 'SCRIPT', 'STYLE', 'TITLE', 'XML'
1392 Roo.HtmlEditorCore.tag_remove = [
1397 Roo.HtmlEditorCore.ablack = [
1401 Roo.HtmlEditorCore.aclean = [
1402 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1406 Roo.HtmlEditorCore.pwhite= [
1407 'http', 'https', 'mailto'
1410 // white listed style attributes.
1411 Roo.HtmlEditorCore.cwhite= [
1412 // 'text-align', /// default is to allow most things..
1418 // black listed style attributes.
1419 Roo.HtmlEditorCore.cblack= [
1420 // 'font-size' -- this can be set by the project