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']); //FIXME - what's the BS styles for these
296 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
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(){
332 if(this.initialized){
333 var bd = (this.doc.body || this.doc.documentElement);
334 //this.cleanUpPaste(); -- this is done else where and causes havoc..
335 var html = bd.innerHTML;
337 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
338 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
340 html = '<div style="'+m[0]+'">' + html + '</div>';
343 html = this.cleanHtml(html);
344 // fix up the special chars.. normaly like back quotes in word...
345 // however we do not want to do this with chinese..
346 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
348 var cc = match.charCodeAt();
350 // Get the character value, handling surrogate pairs
351 if (match.length == 2) {
352 // It's a surrogate pair, calculate the Unicode code point
353 var high = match.charCodeAt(0) - 0xD800;
354 var low = match.charCodeAt(1) - 0xDC00;
355 cc = (high * 0x400) + low + 0x10000;
357 (cc >= 0x4E00 && cc < 0xA000 ) ||
358 (cc >= 0x3400 && cc < 0x4E00 ) ||
359 (cc >= 0xf900 && cc < 0xfb00 )
364 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
365 return "&#" + cc + ";";
372 if(this.owner.fireEvent('beforesync', this, html) !== false){
373 this.el.dom.value = html;
374 this.owner.fireEvent('sync', this, html);
380 * Protected method that will not generally be called directly. Pushes the value of the textarea
381 * into the iframe editor.
383 pushValue : function(){
384 if(this.initialized){
385 var v = this.el.dom.value.trim();
391 if(this.owner.fireEvent('beforepush', this, v) !== false){
392 var d = (this.doc.body || this.doc.documentElement);
395 this.el.dom.value = d.innerHTML;
396 this.owner.fireEvent('push', this, v);
402 deferFocus : function(){
403 this.focus.defer(10, this);
408 if(this.win && !this.sourceEditMode){
415 assignDocWin: function()
417 var iframe = this.iframe;
420 this.doc = iframe.contentWindow.document;
421 this.win = iframe.contentWindow;
423 // if (!Roo.get(this.frameId)) {
426 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
427 // this.win = Roo.get(this.frameId).dom.contentWindow;
429 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
433 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
434 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
439 initEditor : function(){
440 //console.log("INIT EDITOR");
445 this.doc.designMode="on";
447 this.doc.write(this.getDocMarkup());
450 var dbody = (this.doc.body || this.doc.documentElement);
451 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
452 // this copies styles from the containing element into thsi one..
453 // not sure why we need all of this..
454 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
456 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
457 //ss['background-attachment'] = 'fixed'; // w3c
458 dbody.bgProperties = 'fixed'; // ie
459 //Roo.DomHelper.applyStyles(dbody, ss);
460 Roo.EventManager.on(this.doc, {
461 //'mousedown': this.onEditorEvent,
462 'mouseup': this.onEditorEvent,
463 'dblclick': this.onEditorEvent,
464 'click': this.onEditorEvent,
465 'keyup': this.onEditorEvent,
470 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
472 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
473 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
475 this.initialized = true;
478 // initialize special key events - enter
479 new Roo.htmleditor.KeyEnter({core : this});
484 this.owner.fireEvent('initialize', this);
489 onDestroy : function(){
495 //for (var i =0; i < this.toolbars.length;i++) {
496 // // fixme - ask toolbars for heights?
497 // this.toolbars[i].onDestroy();
500 //this.wrap.dom.innerHTML = '';
501 //this.wrap.remove();
506 onFirstFocus : function(){
511 this.activated = true;
514 if(Roo.isGecko){ // prevent silly gecko errors
516 var s = this.win.getSelection();
517 if(!s.focusNode || s.focusNode.nodeType != 3){
518 var r = s.getRangeAt(0);
519 r.selectNodeContents((this.doc.body || this.doc.documentElement));
524 this.execCmd('useCSS', true);
525 this.execCmd('styleWithCSS', false);
528 this.owner.fireEvent('activate', this);
532 adjustFont: function(btn){
533 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
534 //if(Roo.isSafari){ // safari
537 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
538 if(Roo.isSafari){ // safari
539 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
540 v = (v < 10) ? 10 : v;
541 v = (v > 48) ? 48 : v;
542 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
547 v = Math.max(1, v+adjust);
549 this.execCmd('FontSize', v );
552 onEditorEvent : function(e)
554 this.owner.fireEvent('editorevent', this, e);
555 // this.updateToolbar();
556 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
559 insertTag : function(tg)
561 // could be a bit smarter... -> wrap the current selected tRoo..
562 if (tg.toLowerCase() == 'span' ||
563 tg.toLowerCase() == 'code' ||
564 tg.toLowerCase() == 'sup' ||
565 tg.toLowerCase() == 'sub'
568 range = this.createRange(this.getSelection());
569 var wrappingNode = this.doc.createElement(tg.toLowerCase());
570 wrappingNode.appendChild(range.extractContents());
571 range.insertNode(wrappingNode);
578 this.execCmd("formatblock", tg);
582 insertText : function(txt)
586 var range = this.createRange();
587 range.deleteContents();
588 //alert(Sender.getAttribute('label'));
590 range.insertNode(this.doc.createTextNode(txt));
596 * Executes a Midas editor command on the editor document and performs necessary focus and
597 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
598 * @param {String} cmd The Midas command
599 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
601 relayCmd : function(cmd, value){
603 this.execCmd(cmd, value);
604 this.owner.fireEvent('editorevent', this);
605 //this.updateToolbar();
606 this.owner.deferFocus();
610 * Executes a Midas editor command directly on the editor document.
611 * For visual commands, you should use {@link #relayCmd} instead.
612 * <b>This should only be called after the editor is initialized.</b>
613 * @param {String} cmd The Midas command
614 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
616 execCmd : function(cmd, value){
617 this.doc.execCommand(cmd, false, value === undefined ? null : value);
624 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
626 * @param {String} text | dom node..
628 insertAtCursor : function(text)
637 var r = this.doc.selection.createRange();
648 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
652 // from jquery ui (MIT licenced)
656 if (win.getSelection && win.getSelection().getRangeAt) {
657 range = win.getSelection().getRangeAt(0);
658 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
659 range.insertNode(node);
660 } else if (win.document.selection && win.document.selection.createRange) {
661 // no firefox support
662 var txt = typeof(text) == 'string' ? text : text.outerHTML;
663 win.document.selection.createRange().pasteHTML(txt);
665 // no firefox support
666 var txt = typeof(text) == 'string' ? text : text.outerHTML;
667 this.execCmd('InsertHTML', txt);
676 mozKeyPress : function(e){
678 var c = e.getCharCode(), cmd;
681 c = String.fromCharCode(c).toLowerCase();
695 this.cleanUpPaste.defer(100, this);
711 fixKeys : function(){ // load time branching for fastest keydown performance
714 var k = e.getKey(), r;
717 r = this.doc.selection.createRange();
720 r.pasteHTML('    ');
727 r = this.doc.selection.createRange();
729 var target = r.parentElement();
730 if(!target || target.tagName.toLowerCase() != 'li'){
732 r.pasteHTML('<br />');
738 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
739 this.cleanUpPaste.defer(100, this);
745 }else if(Roo.isOpera){
751 this.execCmd('InsertHTML','    ');
754 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
755 this.cleanUpPaste.defer(100, this);
760 }else if(Roo.isSafari){
766 this.execCmd('InsertText','\t');
770 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
771 this.cleanUpPaste.defer(100, this);
779 getAllAncestors: function()
781 var p = this.getSelectedNode();
784 a.push(p); // push blank onto stack..
785 p = this.getParentElement();
789 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
793 a.push(this.doc.body);
800 getSelection : function()
803 return Roo.isIE ? this.doc.selection : this.win.getSelection();
806 getSelectedNode: function()
808 // this may only work on Gecko!!!
810 // should we cache this!!!!
815 var range = this.createRange(this.getSelection()).cloneRange();
818 var parent = range.parentElement();
820 var testRange = range.duplicate();
821 testRange.moveToElementText(parent);
822 if (testRange.inRange(range)) {
825 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
828 parent = parent.parentElement;
833 // is ancestor a text element.
834 var ac = range.commonAncestorContainer;
835 if (ac.nodeType == 3) {
839 var ar = ac.childNodes;
842 var other_nodes = [];
843 var has_other_nodes = false;
844 for (var i=0;i<ar.length;i++) {
845 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
848 // fullly contained node.
850 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
855 // probably selected..
856 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
857 other_nodes.push(ar[i]);
861 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
866 has_other_nodes = true;
868 if (!nodes.length && other_nodes.length) {
871 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
877 createRange: function(sel)
879 // this has strange effects when using with
880 // top toolbar - not sure if it's a great idea.
881 //this.editor.contentWindow.focus();
882 if (typeof sel != "undefined") {
884 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
886 return this.doc.createRange();
889 return this.doc.createRange();
892 getParentElement: function()
896 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
898 var range = this.createRange(sel);
901 var p = range.commonAncestorContainer;
902 while (p.nodeType == 3) { // text node
913 * Range intersection.. the hard stuff...
917 * [ -- selected range --- ]
921 * if end is before start or hits it. fail.
922 * if start is after end or hits it fail.
924 * if either hits (but other is outside. - then it's not
930 // @see http://www.thismuchiknow.co.uk/?p=64.
931 rangeIntersectsNode : function(range, node)
933 var nodeRange = node.ownerDocument.createRange();
935 nodeRange.selectNode(node);
937 nodeRange.selectNodeContents(node);
940 var rangeStartRange = range.cloneRange();
941 rangeStartRange.collapse(true);
943 var rangeEndRange = range.cloneRange();
944 rangeEndRange.collapse(false);
946 var nodeStartRange = nodeRange.cloneRange();
947 nodeStartRange.collapse(true);
949 var nodeEndRange = nodeRange.cloneRange();
950 nodeEndRange.collapse(false);
952 return rangeStartRange.compareBoundaryPoints(
953 Range.START_TO_START, nodeEndRange) == -1 &&
954 rangeEndRange.compareBoundaryPoints(
955 Range.START_TO_START, nodeStartRange) == 1;
959 rangeCompareNode : function(range, node)
961 var nodeRange = node.ownerDocument.createRange();
963 nodeRange.selectNode(node);
965 nodeRange.selectNodeContents(node);
969 range.collapse(true);
971 nodeRange.collapse(true);
973 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
974 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
976 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
978 var nodeIsBefore = ss == 1;
979 var nodeIsAfter = ee == -1;
981 if (nodeIsBefore && nodeIsAfter) {
984 if (!nodeIsBefore && nodeIsAfter) {
985 return 1; //right trailed.
988 if (nodeIsBefore && !nodeIsAfter) {
989 return 2; // left trailed.
995 // private? - in a new class?
996 cleanUpPaste : function()
998 // cleans up the whole document..
999 Roo.log('cleanuppaste');
1001 this.cleanUpChild(this.doc.body);
1002 var clean = this.cleanWordChars(this.doc.body.innerHTML);
1003 if (clean != this.doc.body.innerHTML) {
1004 this.doc.body.innerHTML = clean;
1009 cleanWordChars : function(input) {// change the chars to hex code
1010 var he = Roo.HtmlEditorCore;
1013 Roo.each(he.swapCodes, function(sw) {
1014 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1016 output = output.replace(swapper, sw[1]);
1026 cleanUpChild : function (node)
1029 new Roo.htmleditor.FilterComment({node : node});
1030 new Roo.htmleditor.FilterAttributes({
1032 attrib_black : this.ablack,
1033 attrib_clean : this.aclean,
1034 style_white : this.cwhite,
1035 style_black : this.cblack
1037 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1038 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1044 * Clean up MS wordisms...
1045 * @deprecated - use filter directly
1047 cleanWord : function(node)
1049 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1056 * @deprecated - use filters
1058 cleanTableWidths : function(node)
1060 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1067 /* == used on tidy feature */
1068 domToHTML : function(currentElement, depth, nopadtext) {
1071 nopadtext = nopadtext || false;
1073 if (!currentElement) {
1074 return this.domToHTML(this.doc.body);
1077 //Roo.log(currentElement);
1079 var allText = false;
1080 var nodeName = currentElement.nodeName;
1081 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
1083 if (nodeName == '#text') {
1085 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
1090 if (nodeName != 'BODY') {
1093 // Prints the node tagName, such as <A>, <IMG>, etc
1096 for(i = 0; i < currentElement.attributes.length;i++) {
1098 var aname = currentElement.attributes.item(i).name;
1099 if (!currentElement.attributes.item(i).value.length) {
1102 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
1105 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
1114 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
1117 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
1122 // Traverse the tree
1124 var currentElementChild = currentElement.childNodes.item(i);
1128 while (currentElementChild) {
1129 // Formatting code (indent the tree so it looks nice on the screen)
1130 var nopad = nopadtext;
1131 if (lastnode == 'SPAN') {
1135 if (currentElementChild.nodeName == '#text') {
1136 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
1137 toadd = nopadtext ? toadd : toadd.trim();
1138 if (!nopad && toadd.length > 80) {
1139 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
1144 currentElementChild = currentElement.childNodes.item(i);
1150 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
1152 // Recursively traverse the tree structure of the child node
1153 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
1154 lastnode = currentElementChild.nodeName;
1156 currentElementChild=currentElement.childNodes.item(i);
1162 // The remaining code is mostly for formatting the tree
1163 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
1168 ret+= "</"+tagName+">";
1174 applyBlacklists : function()
1176 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1177 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1179 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1180 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1181 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1185 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1186 if (b.indexOf(tag) > -1) {
1189 this.white.push(tag);
1193 Roo.each(w, function(tag) {
1194 if (b.indexOf(tag) > -1) {
1197 if (this.white.indexOf(tag) > -1) {
1200 this.white.push(tag);
1205 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1206 if (w.indexOf(tag) > -1) {
1209 this.black.push(tag);
1213 Roo.each(b, function(tag) {
1214 if (w.indexOf(tag) > -1) {
1217 if (this.black.indexOf(tag) > -1) {
1220 this.black.push(tag);
1225 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1226 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1230 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1231 if (b.indexOf(tag) > -1) {
1234 this.cwhite.push(tag);
1238 Roo.each(w, function(tag) {
1239 if (b.indexOf(tag) > -1) {
1242 if (this.cwhite.indexOf(tag) > -1) {
1245 this.cwhite.push(tag);
1250 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1251 if (w.indexOf(tag) > -1) {
1254 this.cblack.push(tag);
1258 Roo.each(b, function(tag) {
1259 if (w.indexOf(tag) > -1) {
1262 if (this.cblack.indexOf(tag) > -1) {
1265 this.cblack.push(tag);
1270 setStylesheets : function(stylesheets)
1272 if(typeof(stylesheets) == 'string'){
1273 Roo.get(this.iframe.contentDocument.head).createChild({
1284 Roo.each(stylesheets, function(s) {
1289 Roo.get(_this.iframe.contentDocument.head).createChild({
1300 removeStylesheets : function()
1304 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1309 setStyle : function(style)
1311 Roo.get(this.iframe.contentDocument.head).createChild({
1320 // hide stuff that is not compatible
1338 * @cfg {String} fieldClass @hide
1341 * @cfg {String} focusClass @hide
1344 * @cfg {String} autoCreate @hide
1347 * @cfg {String} inputType @hide
1350 * @cfg {String} invalidClass @hide
1353 * @cfg {String} invalidText @hide
1356 * @cfg {String} msgFx @hide
1359 * @cfg {String} validateOnBlur @hide
1363 Roo.HtmlEditorCore.white = [
1364 'area', 'br', 'img', 'input', 'hr', 'wbr',
1366 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1367 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1368 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1369 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1370 'table', 'ul', 'xmp',
1372 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1375 'dir', 'menu', 'ol', 'ul', 'dl',
1381 Roo.HtmlEditorCore.black = [
1382 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1384 'base', 'basefont', 'bgsound', 'blink', 'body',
1385 'frame', 'frameset', 'head', 'html', 'ilayer',
1386 'iframe', 'layer', 'link', 'meta', 'object',
1387 'script', 'style' ,'title', 'xml' // clean later..
1389 Roo.HtmlEditorCore.clean = [
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
1424 Roo.HtmlEditorCore.swapCodes =[
1425 [ 8211, "–" ],
1426 [ 8212, "—" ],