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 var images = (new Roo.rtf.Parser())
534 .parse(cd.getData('text/rtf'))
535 .filter(function(g) { return g.type == 'pict'; })
536 .map(function(g) { return g.toDataURL(); });
539 html = this.cleanWordChars(html);
541 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
544 Roo.log(cd.getData('text/rtf'));
545 Roo.log(cd.getData('text/richtext'));
547 Roo.each(cd.items, function(item) {
550 new Roo.htmleditor.FilterStyleToTag({ node : d });
551 new Roo.htmleditor.FilterAttributes({
553 attrib_white : ['href', 'src', 'name'],
554 attrib_clean : ['href', 'src', 'name']
556 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
558 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
559 new Roo.htmleditor.FilterParagraph({ node : d });
560 new Roo.htmleditor.FilterSpan({ node : d });
561 new Roo.htmleditor.FilterLongBr({ node : d });
565 this.insertAtCursor(d.innerHTML);
569 // default behaveiour should be our local cleanup paste? (optional?)
570 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
571 //this.owner.fireEvent('paste', e, v);
574 onDestroy : function(){
580 //for (var i =0; i < this.toolbars.length;i++) {
581 // // fixme - ask toolbars for heights?
582 // this.toolbars[i].onDestroy();
585 //this.wrap.dom.innerHTML = '';
586 //this.wrap.remove();
591 onFirstFocus : function(){
596 this.activated = true;
599 if(Roo.isGecko){ // prevent silly gecko errors
601 var s = this.win.getSelection();
602 if(!s.focusNode || s.focusNode.nodeType != 3){
603 var r = s.getRangeAt(0);
604 r.selectNodeContents((this.doc.body || this.doc.documentElement));
609 this.execCmd('useCSS', true);
610 this.execCmd('styleWithCSS', false);
613 this.owner.fireEvent('activate', this);
617 adjustFont: function(btn){
618 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
619 //if(Roo.isSafari){ // safari
622 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
623 if(Roo.isSafari){ // safari
624 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
625 v = (v < 10) ? 10 : v;
626 v = (v > 48) ? 48 : v;
627 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
632 v = Math.max(1, v+adjust);
634 this.execCmd('FontSize', v );
637 onEditorEvent : function(e)
639 this.owner.fireEvent('editorevent', this, e);
640 // this.updateToolbar();
641 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
644 insertTag : function(tg)
646 // could be a bit smarter... -> wrap the current selected tRoo..
647 if (tg.toLowerCase() == 'span' ||
648 tg.toLowerCase() == 'code' ||
649 tg.toLowerCase() == 'sup' ||
650 tg.toLowerCase() == 'sub'
653 range = this.createRange(this.getSelection());
654 var wrappingNode = this.doc.createElement(tg.toLowerCase());
655 wrappingNode.appendChild(range.extractContents());
656 range.insertNode(wrappingNode);
663 this.execCmd("formatblock", tg);
667 insertText : function(txt)
671 var range = this.createRange();
672 range.deleteContents();
673 //alert(Sender.getAttribute('label'));
675 range.insertNode(this.doc.createTextNode(txt));
681 * Executes a Midas editor command on the editor document and performs necessary focus and
682 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
683 * @param {String} cmd The Midas command
684 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
686 relayCmd : function(cmd, value){
688 this.execCmd(cmd, value);
689 this.owner.fireEvent('editorevent', this);
690 //this.updateToolbar();
691 this.owner.deferFocus();
695 * Executes a Midas editor command directly on the editor document.
696 * For visual commands, you should use {@link #relayCmd} instead.
697 * <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 execCmd : function(cmd, value){
702 this.doc.execCommand(cmd, false, value === undefined ? null : value);
709 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
711 * @param {String} text | dom node..
713 insertAtCursor : function(text)
720 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
724 // from jquery ui (MIT licenced)
728 if (win.getSelection && win.getSelection().getRangeAt) {
730 // delete the existing?
732 this.createRange(this.getSelection()).deleteContents();
733 range = win.getSelection().getRangeAt(0);
734 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
735 range.insertNode(node);
736 } else if (win.document.selection && win.document.selection.createRange) {
737 // no firefox support
738 var txt = typeof(text) == 'string' ? text : text.outerHTML;
739 win.document.selection.createRange().pasteHTML(txt);
741 // no firefox support
742 var txt = typeof(text) == 'string' ? text : text.outerHTML;
743 this.execCmd('InsertHTML', txt);
752 mozKeyPress : function(e){
754 var c = e.getCharCode(), cmd;
757 c = String.fromCharCode(c).toLowerCase();
771 // this.cleanUpPaste.defer(100, this);
787 fixKeys : function(){ // load time branching for fastest keydown performance
790 var k = e.getKey(), r;
793 r = this.doc.selection.createRange();
796 r.pasteHTML('    ');
803 r = this.doc.selection.createRange();
805 var target = r.parentElement();
806 if(!target || target.tagName.toLowerCase() != 'li'){
808 r.pasteHTML('<br/>');
814 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
815 // this.cleanUpPaste.defer(100, this);
821 }else if(Roo.isOpera){
827 this.execCmd('InsertHTML','    ');
830 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
831 // this.cleanUpPaste.defer(100, this);
836 }else if(Roo.isSafari){
842 this.execCmd('InsertText','\t');
846 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
847 // this.cleanUpPaste.defer(100, this);
855 getAllAncestors: function()
857 var p = this.getSelectedNode();
860 a.push(p); // push blank onto stack..
861 p = this.getParentElement();
865 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
869 a.push(this.doc.body);
876 getSelection : function()
879 return Roo.isIE ? this.doc.selection : this.win.getSelection();
883 * @param {DomElement} node the node to select
885 selectNode : function(node)
888 var nodeRange = node.ownerDocument.createRange();
890 nodeRange.selectNode(node);
892 nodeRange.selectNodeContents(node);
894 //nodeRange.collapse(true);
895 var s = this.win.getSelection();
897 s.addRange(nodeRange);
900 getSelectedNode: function()
902 // this may only work on Gecko!!!
904 // should we cache this!!!!
909 var range = this.createRange(this.getSelection()).cloneRange();
912 var parent = range.parentElement();
914 var testRange = range.duplicate();
915 testRange.moveToElementText(parent);
916 if (testRange.inRange(range)) {
919 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
922 parent = parent.parentElement;
927 // is ancestor a text element.
928 var ac = range.commonAncestorContainer;
929 if (ac.nodeType == 3) {
933 var ar = ac.childNodes;
936 var other_nodes = [];
937 var has_other_nodes = false;
938 for (var i=0;i<ar.length;i++) {
939 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
942 // fullly contained node.
944 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
949 // probably selected..
950 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
951 other_nodes.push(ar[i]);
955 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
960 has_other_nodes = true;
962 if (!nodes.length && other_nodes.length) {
965 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
971 createRange: function(sel)
973 // this has strange effects when using with
974 // top toolbar - not sure if it's a great idea.
975 //this.editor.contentWindow.focus();
976 if (typeof sel != "undefined") {
978 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
980 return this.doc.createRange();
983 return this.doc.createRange();
986 getParentElement: function()
990 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
992 var range = this.createRange(sel);
995 var p = range.commonAncestorContainer;
996 while (p.nodeType == 3) { // text node
1007 * Range intersection.. the hard stuff...
1011 * [ -- selected range --- ]
1015 * if end is before start or hits it. fail.
1016 * if start is after end or hits it fail.
1018 * if either hits (but other is outside. - then it's not
1024 // @see http://www.thismuchiknow.co.uk/?p=64.
1025 rangeIntersectsNode : function(range, node)
1027 var nodeRange = node.ownerDocument.createRange();
1029 nodeRange.selectNode(node);
1031 nodeRange.selectNodeContents(node);
1034 var rangeStartRange = range.cloneRange();
1035 rangeStartRange.collapse(true);
1037 var rangeEndRange = range.cloneRange();
1038 rangeEndRange.collapse(false);
1040 var nodeStartRange = nodeRange.cloneRange();
1041 nodeStartRange.collapse(true);
1043 var nodeEndRange = nodeRange.cloneRange();
1044 nodeEndRange.collapse(false);
1046 return rangeStartRange.compareBoundaryPoints(
1047 Range.START_TO_START, nodeEndRange) == -1 &&
1048 rangeEndRange.compareBoundaryPoints(
1049 Range.START_TO_START, nodeStartRange) == 1;
1053 rangeCompareNode : function(range, node)
1055 var nodeRange = node.ownerDocument.createRange();
1057 nodeRange.selectNode(node);
1059 nodeRange.selectNodeContents(node);
1063 range.collapse(true);
1065 nodeRange.collapse(true);
1067 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
1068 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
1070 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
1072 var nodeIsBefore = ss == 1;
1073 var nodeIsAfter = ee == -1;
1075 if (nodeIsBefore && nodeIsAfter) {
1078 if (!nodeIsBefore && nodeIsAfter) {
1079 return 1; //right trailed.
1082 if (nodeIsBefore && !nodeIsAfter) {
1083 return 2; // left trailed.
1089 cleanWordChars : function(input) {// change the chars to hex code
1092 [ 8211, "–" ],
1093 [ 8212, "—" ],
1102 Roo.each(swapCodes, function(sw) {
1103 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1105 output = output.replace(swapper, sw[1]);
1115 cleanUpChild : function (node)
1118 new Roo.htmleditor.FilterComment({node : node});
1119 new Roo.htmleditor.FilterAttributes({
1121 attrib_black : this.ablack,
1122 attrib_clean : this.aclean,
1123 style_white : this.cwhite,
1124 style_black : this.cblack
1126 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1127 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1133 * Clean up MS wordisms...
1134 * @deprecated - use filter directly
1136 cleanWord : function(node)
1138 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1145 * @deprecated - use filters
1147 cleanTableWidths : function(node)
1149 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1156 applyBlacklists : function()
1158 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1159 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1161 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1162 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1163 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1167 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1168 if (b.indexOf(tag) > -1) {
1171 this.white.push(tag);
1175 Roo.each(w, function(tag) {
1176 if (b.indexOf(tag) > -1) {
1179 if (this.white.indexOf(tag) > -1) {
1182 this.white.push(tag);
1187 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1188 if (w.indexOf(tag) > -1) {
1191 this.black.push(tag);
1195 Roo.each(b, function(tag) {
1196 if (w.indexOf(tag) > -1) {
1199 if (this.black.indexOf(tag) > -1) {
1202 this.black.push(tag);
1207 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1208 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1212 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1213 if (b.indexOf(tag) > -1) {
1216 this.cwhite.push(tag);
1220 Roo.each(w, function(tag) {
1221 if (b.indexOf(tag) > -1) {
1224 if (this.cwhite.indexOf(tag) > -1) {
1227 this.cwhite.push(tag);
1232 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1233 if (w.indexOf(tag) > -1) {
1236 this.cblack.push(tag);
1240 Roo.each(b, function(tag) {
1241 if (w.indexOf(tag) > -1) {
1244 if (this.cblack.indexOf(tag) > -1) {
1247 this.cblack.push(tag);
1252 setStylesheets : function(stylesheets)
1254 if(typeof(stylesheets) == 'string'){
1255 Roo.get(this.iframe.contentDocument.head).createChild({
1266 Roo.each(stylesheets, function(s) {
1271 Roo.get(_this.iframe.contentDocument.head).createChild({
1282 removeStylesheets : function()
1286 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1291 setStyle : function(style)
1293 Roo.get(this.iframe.contentDocument.head).createChild({
1302 // hide stuff that is not compatible
1320 * @cfg {String} fieldClass @hide
1323 * @cfg {String} focusClass @hide
1326 * @cfg {String} autoCreate @hide
1329 * @cfg {String} inputType @hide
1332 * @cfg {String} invalidClass @hide
1335 * @cfg {String} invalidText @hide
1338 * @cfg {String} msgFx @hide
1341 * @cfg {String} validateOnBlur @hide
1345 Roo.HtmlEditorCore.white = [
1346 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
1348 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
1349 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
1350 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
1351 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
1352 'TABLE', 'UL', 'XMP',
1354 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
1357 'DIR', 'MENU', 'OL', 'UL', 'DL',
1363 Roo.HtmlEditorCore.black = [
1364 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1366 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
1367 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
1368 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
1369 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
1370 //'FONT' // CLEAN LATER..
1371 'COLGROUP', 'COL' // messy tables.
1374 Roo.HtmlEditorCore.clean = [ // ?? needed???
1375 'SCRIPT', 'STYLE', 'TITLE', 'XML'
1377 Roo.HtmlEditorCore.tag_remove = [
1382 Roo.HtmlEditorCore.ablack = [
1386 Roo.HtmlEditorCore.aclean = [
1387 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1391 Roo.HtmlEditorCore.pwhite= [
1392 'http', 'https', 'mailto'
1395 // white listed style attributes.
1396 Roo.HtmlEditorCore.cwhite= [
1397 // 'text-align', /// default is to allow most things..
1403 // black listed style attributes.
1404 Roo.HtmlEditorCore.cblack= [
1405 // 'font-size' -- this can be set by the project