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,
499 'paste': this.onPasteEvent,
504 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
506 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
507 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
509 this.initialized = true;
512 // initialize special key events - enter
513 new Roo.htmleditor.KeyEnter({core : this});
517 this.owner.fireEvent('initialize', this);
521 onPasteEvent : function(e,v)
523 // I think we better assume paste is going to be a dirty load of rubish from word..
525 // even pasting into a 'email version' of this widget will have to clean up that mess.
527 var txt = e.browserEvent.clipboardData.getData('Text'); // clipboard event
528 var d = document.createElement('div');
530 new Roo.htmleditor.FilterStyleToTag({ node : d });
531 new Roo.htmleditor.FilterAttributes({
533 attrib_white : ['href'],
536 this.insertAtCursor(d.innerHTML);
540 // default behaveiour should be our local cleanup paste? (optional?)
541 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
542 //this.owner.fireEvent('paste', e, v);
545 onDestroy : function(){
551 //for (var i =0; i < this.toolbars.length;i++) {
552 // // fixme - ask toolbars for heights?
553 // this.toolbars[i].onDestroy();
556 //this.wrap.dom.innerHTML = '';
557 //this.wrap.remove();
562 onFirstFocus : function(){
567 this.activated = true;
570 if(Roo.isGecko){ // prevent silly gecko errors
572 var s = this.win.getSelection();
573 if(!s.focusNode || s.focusNode.nodeType != 3){
574 var r = s.getRangeAt(0);
575 r.selectNodeContents((this.doc.body || this.doc.documentElement));
580 this.execCmd('useCSS', true);
581 this.execCmd('styleWithCSS', false);
584 this.owner.fireEvent('activate', this);
588 adjustFont: function(btn){
589 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
590 //if(Roo.isSafari){ // safari
593 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
594 if(Roo.isSafari){ // safari
595 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
596 v = (v < 10) ? 10 : v;
597 v = (v > 48) ? 48 : v;
598 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
603 v = Math.max(1, v+adjust);
605 this.execCmd('FontSize', v );
608 onEditorEvent : function(e)
610 this.owner.fireEvent('editorevent', this, e);
611 // this.updateToolbar();
612 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
615 insertTag : function(tg)
617 // could be a bit smarter... -> wrap the current selected tRoo..
618 if (tg.toLowerCase() == 'span' ||
619 tg.toLowerCase() == 'code' ||
620 tg.toLowerCase() == 'sup' ||
621 tg.toLowerCase() == 'sub'
624 range = this.createRange(this.getSelection());
625 var wrappingNode = this.doc.createElement(tg.toLowerCase());
626 wrappingNode.appendChild(range.extractContents());
627 range.insertNode(wrappingNode);
634 this.execCmd("formatblock", tg);
638 insertText : function(txt)
642 var range = this.createRange();
643 range.deleteContents();
644 //alert(Sender.getAttribute('label'));
646 range.insertNode(this.doc.createTextNode(txt));
652 * Executes a Midas editor command on the editor document and performs necessary focus and
653 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
654 * @param {String} cmd The Midas command
655 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
657 relayCmd : function(cmd, value){
659 this.execCmd(cmd, value);
660 this.owner.fireEvent('editorevent', this);
661 //this.updateToolbar();
662 this.owner.deferFocus();
666 * Executes a Midas editor command directly on the editor document.
667 * For visual commands, you should use {@link #relayCmd} instead.
668 * <b>This should only be called after the editor is initialized.</b>
669 * @param {String} cmd The Midas command
670 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
672 execCmd : function(cmd, value){
673 this.doc.execCommand(cmd, false, value === undefined ? null : value);
680 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
682 * @param {String} text | dom node..
684 insertAtCursor : function(text)
693 var r = this.doc.selection.createRange();
704 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
708 // from jquery ui (MIT licenced)
712 if (win.getSelection && win.getSelection().getRangeAt) {
713 range = win.getSelection().getRangeAt(0);
714 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
715 range.insertNode(node);
716 } else if (win.document.selection && win.document.selection.createRange) {
717 // no firefox support
718 var txt = typeof(text) == 'string' ? text : text.outerHTML;
719 win.document.selection.createRange().pasteHTML(txt);
721 // no firefox support
722 var txt = typeof(text) == 'string' ? text : text.outerHTML;
723 this.execCmd('InsertHTML', txt);
732 mozKeyPress : function(e){
734 var c = e.getCharCode(), cmd;
737 c = String.fromCharCode(c).toLowerCase();
751 // this.cleanUpPaste.defer(100, this);
767 fixKeys : function(){ // load time branching for fastest keydown performance
770 var k = e.getKey(), r;
773 r = this.doc.selection.createRange();
776 r.pasteHTML('    ');
783 r = this.doc.selection.createRange();
785 var target = r.parentElement();
786 if(!target || target.tagName.toLowerCase() != 'li'){
788 r.pasteHTML('<br/>');
794 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
795 // this.cleanUpPaste.defer(100, this);
801 }else if(Roo.isOpera){
807 this.execCmd('InsertHTML','    ');
810 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
811 // this.cleanUpPaste.defer(100, this);
816 }else if(Roo.isSafari){
822 this.execCmd('InsertText','\t');
826 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
827 // this.cleanUpPaste.defer(100, this);
835 getAllAncestors: function()
837 var p = this.getSelectedNode();
840 a.push(p); // push blank onto stack..
841 p = this.getParentElement();
845 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
849 a.push(this.doc.body);
856 getSelection : function()
859 return Roo.isIE ? this.doc.selection : this.win.getSelection();
863 * @param {DomElement} node the node to select
865 selectNode : function(node)
868 var nodeRange = node.ownerDocument.createRange();
870 nodeRange.selectNode(node);
872 nodeRange.selectNodeContents(node);
874 //nodeRange.collapse(true);
875 var s = this.win.getSelection();
877 s.addRange(nodeRange);
880 getSelectedNode: function()
882 // this may only work on Gecko!!!
884 // should we cache this!!!!
889 var range = this.createRange(this.getSelection()).cloneRange();
892 var parent = range.parentElement();
894 var testRange = range.duplicate();
895 testRange.moveToElementText(parent);
896 if (testRange.inRange(range)) {
899 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
902 parent = parent.parentElement;
907 // is ancestor a text element.
908 var ac = range.commonAncestorContainer;
909 if (ac.nodeType == 3) {
913 var ar = ac.childNodes;
916 var other_nodes = [];
917 var has_other_nodes = false;
918 for (var i=0;i<ar.length;i++) {
919 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
922 // fullly contained node.
924 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
929 // probably selected..
930 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
931 other_nodes.push(ar[i]);
935 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
940 has_other_nodes = true;
942 if (!nodes.length && other_nodes.length) {
945 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
951 createRange: function(sel)
953 // this has strange effects when using with
954 // top toolbar - not sure if it's a great idea.
955 //this.editor.contentWindow.focus();
956 if (typeof sel != "undefined") {
958 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
960 return this.doc.createRange();
963 return this.doc.createRange();
966 getParentElement: function()
970 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
972 var range = this.createRange(sel);
975 var p = range.commonAncestorContainer;
976 while (p.nodeType == 3) { // text node
987 * Range intersection.. the hard stuff...
991 * [ -- selected range --- ]
995 * if end is before start or hits it. fail.
996 * if start is after end or hits it fail.
998 * if either hits (but other is outside. - then it's not
1004 // @see http://www.thismuchiknow.co.uk/?p=64.
1005 rangeIntersectsNode : function(range, node)
1007 var nodeRange = node.ownerDocument.createRange();
1009 nodeRange.selectNode(node);
1011 nodeRange.selectNodeContents(node);
1014 var rangeStartRange = range.cloneRange();
1015 rangeStartRange.collapse(true);
1017 var rangeEndRange = range.cloneRange();
1018 rangeEndRange.collapse(false);
1020 var nodeStartRange = nodeRange.cloneRange();
1021 nodeStartRange.collapse(true);
1023 var nodeEndRange = nodeRange.cloneRange();
1024 nodeEndRange.collapse(false);
1026 return rangeStartRange.compareBoundaryPoints(
1027 Range.START_TO_START, nodeEndRange) == -1 &&
1028 rangeEndRange.compareBoundaryPoints(
1029 Range.START_TO_START, nodeStartRange) == 1;
1033 rangeCompareNode : function(range, node)
1035 var nodeRange = node.ownerDocument.createRange();
1037 nodeRange.selectNode(node);
1039 nodeRange.selectNodeContents(node);
1043 range.collapse(true);
1045 nodeRange.collapse(true);
1047 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
1048 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
1050 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
1052 var nodeIsBefore = ss == 1;
1053 var nodeIsAfter = ee == -1;
1055 if (nodeIsBefore && nodeIsAfter) {
1058 if (!nodeIsBefore && nodeIsAfter) {
1059 return 1; //right trailed.
1062 if (nodeIsBefore && !nodeIsAfter) {
1063 return 2; // left trailed.
1069 cleanWordChars : function(input) {// change the chars to hex code
1070 var he = Roo.HtmlEditorCore;
1073 Roo.each(he.swapCodes, function(sw) {
1074 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1076 output = output.replace(swapper, sw[1]);
1086 cleanUpChild : function (node)
1089 new Roo.htmleditor.FilterComment({node : node});
1090 new Roo.htmleditor.FilterAttributes({
1092 attrib_black : this.ablack,
1093 attrib_clean : this.aclean,
1094 style_white : this.cwhite,
1095 style_black : this.cblack
1097 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1098 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1104 * Clean up MS wordisms...
1105 * @deprecated - use filter directly
1107 cleanWord : function(node)
1109 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1116 * @deprecated - use filters
1118 cleanTableWidths : function(node)
1120 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1127 applyBlacklists : function()
1129 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1130 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1132 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1133 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1134 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1138 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1139 if (b.indexOf(tag) > -1) {
1142 this.white.push(tag);
1146 Roo.each(w, function(tag) {
1147 if (b.indexOf(tag) > -1) {
1150 if (this.white.indexOf(tag) > -1) {
1153 this.white.push(tag);
1158 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1159 if (w.indexOf(tag) > -1) {
1162 this.black.push(tag);
1166 Roo.each(b, function(tag) {
1167 if (w.indexOf(tag) > -1) {
1170 if (this.black.indexOf(tag) > -1) {
1173 this.black.push(tag);
1178 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1179 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1183 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1184 if (b.indexOf(tag) > -1) {
1187 this.cwhite.push(tag);
1191 Roo.each(w, function(tag) {
1192 if (b.indexOf(tag) > -1) {
1195 if (this.cwhite.indexOf(tag) > -1) {
1198 this.cwhite.push(tag);
1203 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1204 if (w.indexOf(tag) > -1) {
1207 this.cblack.push(tag);
1211 Roo.each(b, function(tag) {
1212 if (w.indexOf(tag) > -1) {
1215 if (this.cblack.indexOf(tag) > -1) {
1218 this.cblack.push(tag);
1223 setStylesheets : function(stylesheets)
1225 if(typeof(stylesheets) == 'string'){
1226 Roo.get(this.iframe.contentDocument.head).createChild({
1237 Roo.each(stylesheets, function(s) {
1242 Roo.get(_this.iframe.contentDocument.head).createChild({
1253 removeStylesheets : function()
1257 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1262 setStyle : function(style)
1264 Roo.get(this.iframe.contentDocument.head).createChild({
1273 // hide stuff that is not compatible
1291 * @cfg {String} fieldClass @hide
1294 * @cfg {String} focusClass @hide
1297 * @cfg {String} autoCreate @hide
1300 * @cfg {String} inputType @hide
1303 * @cfg {String} invalidClass @hide
1306 * @cfg {String} invalidText @hide
1309 * @cfg {String} msgFx @hide
1312 * @cfg {String} validateOnBlur @hide
1316 Roo.HtmlEditorCore.white = [
1317 'area', 'br', 'img', 'input', 'hr', 'wbr',
1319 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1320 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1321 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1322 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1323 'table', 'ul', 'xmp',
1325 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1328 'dir', 'menu', 'ol', 'ul', 'dl',
1334 Roo.HtmlEditorCore.black = [
1335 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1337 'base', 'basefont', 'bgsound', 'blink', 'body',
1338 'frame', 'frameset', 'head', 'html', 'ilayer',
1339 'iframe', 'layer', 'link', 'meta', 'object',
1340 'script', 'style' ,'title', 'xml' // clean later..
1342 Roo.HtmlEditorCore.clean = [
1343 'script', 'style', 'title', 'xml'
1345 Roo.HtmlEditorCore.tag_remove = [
1350 Roo.HtmlEditorCore.ablack = [
1354 Roo.HtmlEditorCore.aclean = [
1355 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1359 Roo.HtmlEditorCore.pwhite= [
1360 'http', 'https', 'mailto'
1363 // white listed style attributes.
1364 Roo.HtmlEditorCore.cwhite= [
1365 // 'text-align', /// default is to allow most things..
1371 // black listed style attributes.
1372 Roo.HtmlEditorCore.cblack= [
1373 // 'font-size' -- this can be set by the project
1377 Roo.HtmlEditorCore.swapCodes =[
1378 [ 8211, "–" ],
1379 [ 8212, "—" ],