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.FilterWord(d);
531 this.insertAtCursor(d.innerHTML);
535 // default behaveiour should be our local cleanup paste? (optional?)
536 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
537 //this.owner.fireEvent('paste', e, v);
540 onDestroy : function(){
546 //for (var i =0; i < this.toolbars.length;i++) {
547 // // fixme - ask toolbars for heights?
548 // this.toolbars[i].onDestroy();
551 //this.wrap.dom.innerHTML = '';
552 //this.wrap.remove();
557 onFirstFocus : function(){
562 this.activated = true;
565 if(Roo.isGecko){ // prevent silly gecko errors
567 var s = this.win.getSelection();
568 if(!s.focusNode || s.focusNode.nodeType != 3){
569 var r = s.getRangeAt(0);
570 r.selectNodeContents((this.doc.body || this.doc.documentElement));
575 this.execCmd('useCSS', true);
576 this.execCmd('styleWithCSS', false);
579 this.owner.fireEvent('activate', this);
583 adjustFont: function(btn){
584 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
585 //if(Roo.isSafari){ // safari
588 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
589 if(Roo.isSafari){ // safari
590 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
591 v = (v < 10) ? 10 : v;
592 v = (v > 48) ? 48 : v;
593 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
598 v = Math.max(1, v+adjust);
600 this.execCmd('FontSize', v );
603 onEditorEvent : function(e)
605 this.owner.fireEvent('editorevent', this, e);
606 // this.updateToolbar();
607 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
610 insertTag : function(tg)
612 // could be a bit smarter... -> wrap the current selected tRoo..
613 if (tg.toLowerCase() == 'span' ||
614 tg.toLowerCase() == 'code' ||
615 tg.toLowerCase() == 'sup' ||
616 tg.toLowerCase() == 'sub'
619 range = this.createRange(this.getSelection());
620 var wrappingNode = this.doc.createElement(tg.toLowerCase());
621 wrappingNode.appendChild(range.extractContents());
622 range.insertNode(wrappingNode);
629 this.execCmd("formatblock", tg);
633 insertText : function(txt)
637 var range = this.createRange();
638 range.deleteContents();
639 //alert(Sender.getAttribute('label'));
641 range.insertNode(this.doc.createTextNode(txt));
647 * Executes a Midas editor command on the editor document and performs necessary focus and
648 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
649 * @param {String} cmd The Midas command
650 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
652 relayCmd : function(cmd, value){
654 this.execCmd(cmd, value);
655 this.owner.fireEvent('editorevent', this);
656 //this.updateToolbar();
657 this.owner.deferFocus();
661 * Executes a Midas editor command directly on the editor document.
662 * For visual commands, you should use {@link #relayCmd} instead.
663 * <b>This should only be called after the editor is initialized.</b>
664 * @param {String} cmd The Midas command
665 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
667 execCmd : function(cmd, value){
668 this.doc.execCommand(cmd, false, value === undefined ? null : value);
675 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
677 * @param {String} text | dom node..
679 insertAtCursor : function(text)
688 var r = this.doc.selection.createRange();
699 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
703 // from jquery ui (MIT licenced)
707 if (win.getSelection && win.getSelection().getRangeAt) {
708 range = win.getSelection().getRangeAt(0);
709 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
710 range.insertNode(node);
711 } else if (win.document.selection && win.document.selection.createRange) {
712 // no firefox support
713 var txt = typeof(text) == 'string' ? text : text.outerHTML;
714 win.document.selection.createRange().pasteHTML(txt);
716 // no firefox support
717 var txt = typeof(text) == 'string' ? text : text.outerHTML;
718 this.execCmd('InsertHTML', txt);
727 mozKeyPress : function(e){
729 var c = e.getCharCode(), cmd;
732 c = String.fromCharCode(c).toLowerCase();
746 // this.cleanUpPaste.defer(100, this);
762 fixKeys : function(){ // load time branching for fastest keydown performance
765 var k = e.getKey(), r;
768 r = this.doc.selection.createRange();
771 r.pasteHTML('    ');
778 r = this.doc.selection.createRange();
780 var target = r.parentElement();
781 if(!target || target.tagName.toLowerCase() != 'li'){
783 r.pasteHTML('<br/>');
789 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
790 // this.cleanUpPaste.defer(100, this);
796 }else if(Roo.isOpera){
802 this.execCmd('InsertHTML','    ');
805 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
806 // this.cleanUpPaste.defer(100, this);
811 }else if(Roo.isSafari){
817 this.execCmd('InsertText','\t');
821 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
822 // this.cleanUpPaste.defer(100, this);
830 getAllAncestors: function()
832 var p = this.getSelectedNode();
835 a.push(p); // push blank onto stack..
836 p = this.getParentElement();
840 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
844 a.push(this.doc.body);
851 getSelection : function()
854 return Roo.isIE ? this.doc.selection : this.win.getSelection();
858 * @param {DomElement} node the node to select
860 selectNode : function(node)
863 var nodeRange = node.ownerDocument.createRange();
865 nodeRange.selectNode(node);
867 nodeRange.selectNodeContents(node);
869 //nodeRange.collapse(true);
870 var s = this.win.getSelection();
872 s.addRange(nodeRange);
875 getSelectedNode: function()
877 // this may only work on Gecko!!!
879 // should we cache this!!!!
884 var range = this.createRange(this.getSelection()).cloneRange();
887 var parent = range.parentElement();
889 var testRange = range.duplicate();
890 testRange.moveToElementText(parent);
891 if (testRange.inRange(range)) {
894 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
897 parent = parent.parentElement;
902 // is ancestor a text element.
903 var ac = range.commonAncestorContainer;
904 if (ac.nodeType == 3) {
908 var ar = ac.childNodes;
911 var other_nodes = [];
912 var has_other_nodes = false;
913 for (var i=0;i<ar.length;i++) {
914 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
917 // fullly contained node.
919 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
924 // probably selected..
925 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
926 other_nodes.push(ar[i]);
930 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
935 has_other_nodes = true;
937 if (!nodes.length && other_nodes.length) {
940 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
946 createRange: function(sel)
948 // this has strange effects when using with
949 // top toolbar - not sure if it's a great idea.
950 //this.editor.contentWindow.focus();
951 if (typeof sel != "undefined") {
953 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
955 return this.doc.createRange();
958 return this.doc.createRange();
961 getParentElement: function()
965 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
967 var range = this.createRange(sel);
970 var p = range.commonAncestorContainer;
971 while (p.nodeType == 3) { // text node
982 * Range intersection.. the hard stuff...
986 * [ -- selected range --- ]
990 * if end is before start or hits it. fail.
991 * if start is after end or hits it fail.
993 * if either hits (but other is outside. - then it's not
999 // @see http://www.thismuchiknow.co.uk/?p=64.
1000 rangeIntersectsNode : function(range, node)
1002 var nodeRange = node.ownerDocument.createRange();
1004 nodeRange.selectNode(node);
1006 nodeRange.selectNodeContents(node);
1009 var rangeStartRange = range.cloneRange();
1010 rangeStartRange.collapse(true);
1012 var rangeEndRange = range.cloneRange();
1013 rangeEndRange.collapse(false);
1015 var nodeStartRange = nodeRange.cloneRange();
1016 nodeStartRange.collapse(true);
1018 var nodeEndRange = nodeRange.cloneRange();
1019 nodeEndRange.collapse(false);
1021 return rangeStartRange.compareBoundaryPoints(
1022 Range.START_TO_START, nodeEndRange) == -1 &&
1023 rangeEndRange.compareBoundaryPoints(
1024 Range.START_TO_START, nodeStartRange) == 1;
1028 rangeCompareNode : function(range, node)
1030 var nodeRange = node.ownerDocument.createRange();
1032 nodeRange.selectNode(node);
1034 nodeRange.selectNodeContents(node);
1038 range.collapse(true);
1040 nodeRange.collapse(true);
1042 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
1043 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
1045 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
1047 var nodeIsBefore = ss == 1;
1048 var nodeIsAfter = ee == -1;
1050 if (nodeIsBefore && nodeIsAfter) {
1053 if (!nodeIsBefore && nodeIsAfter) {
1054 return 1; //right trailed.
1057 if (nodeIsBefore && !nodeIsAfter) {
1058 return 2; // left trailed.
1064 cleanWordChars : function(input) {// change the chars to hex code
1065 var he = Roo.HtmlEditorCore;
1068 Roo.each(he.swapCodes, function(sw) {
1069 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
1071 output = output.replace(swapper, sw[1]);
1081 cleanUpChild : function (node)
1084 new Roo.htmleditor.FilterComment({node : node});
1085 new Roo.htmleditor.FilterAttributes({
1087 attrib_black : this.ablack,
1088 attrib_clean : this.aclean,
1089 style_white : this.cwhite,
1090 style_black : this.cblack
1092 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
1093 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
1099 * Clean up MS wordisms...
1100 * @deprecated - use filter directly
1102 cleanWord : function(node)
1104 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
1111 * @deprecated - use filters
1113 cleanTableWidths : function(node)
1115 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
1122 applyBlacklists : function()
1124 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
1125 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
1127 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
1128 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
1129 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
1133 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
1134 if (b.indexOf(tag) > -1) {
1137 this.white.push(tag);
1141 Roo.each(w, function(tag) {
1142 if (b.indexOf(tag) > -1) {
1145 if (this.white.indexOf(tag) > -1) {
1148 this.white.push(tag);
1153 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
1154 if (w.indexOf(tag) > -1) {
1157 this.black.push(tag);
1161 Roo.each(b, function(tag) {
1162 if (w.indexOf(tag) > -1) {
1165 if (this.black.indexOf(tag) > -1) {
1168 this.black.push(tag);
1173 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
1174 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
1178 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
1179 if (b.indexOf(tag) > -1) {
1182 this.cwhite.push(tag);
1186 Roo.each(w, function(tag) {
1187 if (b.indexOf(tag) > -1) {
1190 if (this.cwhite.indexOf(tag) > -1) {
1193 this.cwhite.push(tag);
1198 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
1199 if (w.indexOf(tag) > -1) {
1202 this.cblack.push(tag);
1206 Roo.each(b, function(tag) {
1207 if (w.indexOf(tag) > -1) {
1210 if (this.cblack.indexOf(tag) > -1) {
1213 this.cblack.push(tag);
1218 setStylesheets : function(stylesheets)
1220 if(typeof(stylesheets) == 'string'){
1221 Roo.get(this.iframe.contentDocument.head).createChild({
1232 Roo.each(stylesheets, function(s) {
1237 Roo.get(_this.iframe.contentDocument.head).createChild({
1248 removeStylesheets : function()
1252 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
1257 setStyle : function(style)
1259 Roo.get(this.iframe.contentDocument.head).createChild({
1268 // hide stuff that is not compatible
1286 * @cfg {String} fieldClass @hide
1289 * @cfg {String} focusClass @hide
1292 * @cfg {String} autoCreate @hide
1295 * @cfg {String} inputType @hide
1298 * @cfg {String} invalidClass @hide
1301 * @cfg {String} invalidText @hide
1304 * @cfg {String} msgFx @hide
1307 * @cfg {String} validateOnBlur @hide
1311 Roo.HtmlEditorCore.white = [
1312 'area', 'br', 'img', 'input', 'hr', 'wbr',
1314 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
1315 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
1316 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
1317 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
1318 'table', 'ul', 'xmp',
1320 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
1323 'dir', 'menu', 'ol', 'ul', 'dl',
1329 Roo.HtmlEditorCore.black = [
1330 // 'embed', 'object', // enable - backend responsiblity to clean thiese
1332 'base', 'basefont', 'bgsound', 'blink', 'body',
1333 'frame', 'frameset', 'head', 'html', 'ilayer',
1334 'iframe', 'layer', 'link', 'meta', 'object',
1335 'script', 'style' ,'title', 'xml' // clean later..
1337 Roo.HtmlEditorCore.clean = [
1338 'script', 'style', 'title', 'xml'
1340 Roo.HtmlEditorCore.tag_remove = [
1345 Roo.HtmlEditorCore.ablack = [
1349 Roo.HtmlEditorCore.aclean = [
1350 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
1354 Roo.HtmlEditorCore.pwhite= [
1355 'http', 'https', 'mailto'
1358 // white listed style attributes.
1359 Roo.HtmlEditorCore.cwhite= [
1360 // 'text-align', /// default is to allow most things..
1366 // black listed style attributes.
1367 Roo.HtmlEditorCore.cblack= [
1368 // 'font-size' -- this can be set by the project
1372 Roo.HtmlEditorCore.swapCodes =[
1373 [ 8211, "–" ],
1374 [ 8212, "—" ],