2 * @class Ext.form.field.HtmlEditor
3 * @extends Ext.Component
5 * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
6 * automatically hidden when needed. These are noted in the config options where appropriate.
8 * The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
9 * enabled by default unless the global {@link Ext.tip.QuickTipManager} singleton is {@link Ext.tip.QuickTipManager#init initialized}.
11 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
12 * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.
14 * {@img Ext.form.HtmlEditor/Ext.form.HtmlEditor1.png Ext.form.HtmlEditor component}
18 * {@img Ext.form.HtmlEditor/Ext.form.HtmlEditor2.png Ext.form.HtmlEditor component}
20 * // Simple example rendered with default options:
21 * Ext.tip.QuickTips.init(); // enable tooltips
22 * Ext.create('Ext.form.HtmlEditor', {
25 * renderTo: Ext.getBody()
28 * {@img Ext.form.HtmlEditor/Ext.form.HtmlEditor2.png Ext.form.HtmlEditor component}
30 * // Passed via xtype into a container and with custom options:
31 * Ext.tip.QuickTips.init(); // enable tooltips
32 * new Ext.panel.Panel({
33 * title: 'HTML Editor',
34 * renderTo: Ext.getBody(),
40 * xtype: 'htmleditor',
41 * enableColors: false,
42 * enableAlignments: false
47 * Create a new HtmlEditor
48 * @param {Object} config
51 Ext.define('Ext.form.field.HtmlEditor', {
52 extend:'Ext.Component',
54 labelable: 'Ext.form.Labelable',
55 field: 'Ext.form.field.Field'
57 alias: 'widget.htmleditor',
58 alternateClassName: 'Ext.form.HtmlEditor',
60 'Ext.tip.QuickTipManager',
63 'Ext.toolbar.Toolbar',
65 'Ext.layout.component.field.HtmlEditor'
69 '<div class="{toolbarWrapCls}"></div>',
70 '<textarea id="{id}" name="{name}" tabIndex="-1" class="{textareaCls}" ',
71 'style="{size}" autocomplete="off"></textarea>',
72 '<iframe name="{iframeName}" frameBorder="0" style="overflow:auto;{size}" src="{iframeSrc}"></iframe>',
80 * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
84 * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
86 enableFontSize : true,
88 * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
92 * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
94 enableAlignments : true,
96 * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
100 * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
102 enableSourceEdit : true,
104 * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
108 * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
112 * @cfg {String} createLinkText The default text for the create link prompt
114 createLinkText : 'Please enter the URL for the link:',
116 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
118 defaultLinkValue : 'http:/'+'/',
120 * @cfg {Array} fontFamilies An array of available font families
129 defaultFont: 'tahoma',
131 * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to   (Non-breaking space) in Opera and IE6, ​ (Zero-width space) in all other browsers).
133 defaultValue: (Ext.isOpera || Ext.isIE6) ? ' ' : '​',
135 fieldBodyCls: Ext.baseCSSPrefix + 'html-editor-wrap',
137 componentLayout: 'htmleditor',
139 // private properties
142 sourceEditMode : false,
149 initComponent : function(){
155 * Fires when the editor is fully initialized (including the iframe)
156 * @param {Ext.form.field.HtmlEditor} this
161 * Fires when the editor is first receives the focus. Any insertion must wait
162 * until after this event.
163 * @param {Ext.form.field.HtmlEditor} this
168 * Fires before the textarea is updated with content from the editor iframe. Return false
169 * to cancel the sync.
170 * @param {Ext.form.field.HtmlEditor} this
171 * @param {String} html
176 * Fires before the iframe editor is updated with content from the textarea. Return false
177 * to cancel the push.
178 * @param {Ext.form.field.HtmlEditor} this
179 * @param {String} html
184 * Fires when the textarea is updated with content from the editor iframe.
185 * @param {Ext.form.field.HtmlEditor} this
186 * @param {String} html
191 * Fires when the iframe editor is updated with content from the textarea.
192 * @param {Ext.form.field.HtmlEditor} this
193 * @param {String} html
197 * @event editmodechange
198 * Fires when the editor switches edit modes
199 * @param {Ext.form.field.HtmlEditor} this
200 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
205 me.callParent(arguments);
213 * Protected method that will not generally be called directly. It
214 * is called when the editor creates its toolbar. Override this method if you need to
215 * add custom toolbar buttons.
216 * @param {Ext.form.field.HtmlEditor} editor
218 createToolbar : function(editor){
221 tipsEnabled = Ext.tip.QuickTipManager && Ext.tip.QuickTipManager.isEnabled(),
222 baseCSSPrefix = Ext.baseCSSPrefix,
223 fontSelectItem, toolbar, undef;
225 function btn(id, toggle, handler){
228 cls : baseCSSPrefix + 'btn-icon',
229 iconCls: baseCSSPrefix + 'edit-'+id,
230 enableToggle:toggle !== false,
232 handler:handler||editor.relayBtnCmd,
233 clickEvent:'mousedown',
234 tooltip: tipsEnabled ? editor.buttonTips[id] || undef : undef,
235 overflowText: editor.buttonTips[id].title || undef,
241 if (me.enableFont && !Ext.isSafari2) {
242 fontSelectItem = Ext.widget('component', {
244 '<select class="{cls}">',
246 '<option value="{[values.toLowerCase()]}" style="font-family:{.}"<tpl if="values.toLowerCase()==parent.defaultFont"> selected</tpl>>{.}</option>',
251 cls: baseCSSPrefix + 'font-select',
252 fonts: me.fontFamilies,
253 defaultFont: me.defaultFont
258 onDisable: function() {
259 var selectEl = this.selectEl;
261 selectEl.dom.disabled = true;
263 Ext.Component.superclass.onDisable.apply(this, arguments);
265 onEnable: function() {
266 var selectEl = this.selectEl;
268 selectEl.dom.disabled = false;
270 Ext.Component.superclass.onEnable.apply(this, arguments);
280 if (me.enableFormat) {
288 if (me.enableFontSize) {
291 btn('increasefontsize', false, me.adjustFont),
292 btn('decreasefontsize', false, me.adjustFont)
296 if (me.enableColors) {
300 cls: baseCSSPrefix + 'btn-icon',
301 iconCls: baseCSSPrefix + 'edit-forecolor',
302 overflowText: editor.buttonTips.forecolor.title,
303 tooltip: tipsEnabled ? editor.buttonTips.forecolor || undef : undef,
305 menu : Ext.widget('menu', {
308 xtype: 'colorpicker',
313 clickEvent: 'mousedown',
314 handler: function(cp, color) {
315 me.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
317 this.up('menu').hide();
323 cls: baseCSSPrefix + 'btn-icon',
324 iconCls: baseCSSPrefix + 'edit-backcolor',
325 overflowText: editor.buttonTips.backcolor.title,
326 tooltip: tipsEnabled ? editor.buttonTips.backcolor || undef : undef,
328 menu : Ext.widget('menu', {
331 xtype: 'colorpicker',
336 clickEvent: 'mousedown',
337 handler: function(cp, color) {
339 me.execCmd('useCSS', false);
340 me.execCmd('hilitecolor', color);
341 me.execCmd('useCSS', true);
344 me.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
347 this.up('menu').hide();
355 if (me.enableAlignments) {
359 btn('justifycenter'),
364 if (!Ext.isSafari2) {
365 if (me.enableLinks) {
368 btn('createlink', false, me.createLink)
372 if (me.enableLists) {
375 btn('insertorderedlist'),
376 btn('insertunorderedlist')
379 if (me.enableSourceEdit) {
382 btn('sourceedit', true, function(btn){
383 me.toggleSourceEdit(!me.sourceEditMode);
390 toolbar = Ext.widget('toolbar', {
391 renderTo: me.toolbarWrap,
392 enableOverflow: true,
396 if (fontSelectItem) {
397 me.fontSelect = fontSelectItem.selectEl;
399 me.mon(me.fontSelect, 'change', function(){
400 me.relayCmd('fontname', me.fontSelect.dom.value);
406 me.mon(toolbar.el, 'click', function(e){
410 me.toolbar = toolbar;
413 onDisable: function() {
415 this.callParent(arguments);
418 onEnable: function() {
419 this.bodyEl.unmask();
420 this.callParent(arguments);
424 * Sets the read only state of this field.
425 * @param {Boolean} readOnly Whether the field should be read only.
427 setReadOnly: function(readOnly) {
429 textareaEl = me.textareaEl,
430 iframeEl = me.iframeEl,
433 me.readOnly = readOnly;
436 textareaEl.dom.readOnly = readOnly;
439 if (me.initialized) {
440 body = me.getEditorBody();
442 // Hide the iframe while setting contentEditable so it doesn't grab focus
443 iframeEl.setDisplayed(false);
444 body.contentEditable = !readOnly;
445 iframeEl.setDisplayed(true);
447 me.setDesignMode(!readOnly);
450 body.style.cursor = readOnly ? 'default' : 'text';
452 me.disableItems(readOnly);
457 * Protected method that will not generally be called directly. It
458 * is called when the editor initializes the iframe with HTML contents. Override this method if you
459 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
461 * Note: IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility.
462 * Also note that forcing IE7 mode works when the page is loaded normally, but if you are using IE's Web
463 * Developer Tools to manually set the document mode, that will take precedence and override what this
464 * code sets by default. This can be confusing when developing, but is not a user-facing issue.
466 getDocMarkup: function() {
468 h = me.iframeEl.getHeight() - me.iframePad * 2;
469 return Ext.String.format('<html><head><style type="text/css">body{border:0;margin:0;padding:{0}px;height:{1}px;cursor:text}</style></head><body></body></html>', me.iframePad, h);
473 getEditorBody: function() {
474 var doc = this.getDoc();
475 return doc.body || doc.documentElement;
480 return (!Ext.isIE && this.iframeEl.dom.contentDocument) || this.getWin().document;
485 return Ext.isIE ? this.iframeEl.dom.contentWindow : window.frames[this.iframeEl.dom.name];
489 onRender: function() {
491 renderSelectors = me.renderSelectors;
493 Ext.applyIf(renderSelectors, me.getLabelableSelectors());
495 Ext.applyIf(renderSelectors, {
496 toolbarWrap: 'div.' + Ext.baseCSSPrefix + 'html-editor-tb',
498 textareaEl: 'textarea'
501 me.callParent(arguments);
503 me.textareaEl.dom.value = me.value || '';
505 // Start polling for when the iframe document is ready to be manipulated
506 me.monitorTask = Ext.TaskManager.start({
507 run: me.checkDesignMode,
512 me.createToolbar(me);
513 me.disableItems(true);
516 initRenderTpl: function() {
518 if (!me.hasOwnProperty('renderTpl')) {
519 me.renderTpl = me.getTpl('labelableRenderTpl');
521 return me.callParent();
524 initRenderData: function() {
525 return Ext.applyIf(this.callParent(), this.getLabelableRenderData());
528 getSubTplData: function() {
529 var cssPrefix = Ext.baseCSSPrefix;
531 toolbarWrapCls: cssPrefix + 'html-editor-tb',
532 textareaCls: cssPrefix + 'hidden',
533 iframeName: Ext.id(),
534 iframeSrc: Ext.SSL_SECURE_URL,
535 size: 'height:100px;'
539 getSubTplMarkup: function() {
540 return this.getTpl('fieldSubTpl').apply(this.getSubTplData());
543 getBodyNaturalWidth: function() {
547 initFrameDoc: function() {
551 Ext.TaskManager.stop(me.monitorTask);
554 me.win = me.getWin();
557 doc.write(me.getDocMarkup());
560 task = { // must defer to wait for browser to be ready
562 var doc = me.getDoc();
563 if (doc.body || doc.readyState === 'complete') {
564 Ext.TaskManager.stop(task);
565 me.setDesignMode(true);
566 Ext.defer(me.initEditor, 10, me);
573 Ext.TaskManager.start(task);
576 checkDesignMode: function() {
579 if (doc && (!doc.editorInitialized || me.getDesignMode() !== 'on')) {
585 * set current design mode. To enable, mode can be true or 'on', off otherwise
587 setDesignMode: function(mode) {
594 doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';
599 getDesignMode: function() {
600 var doc = this.getDoc();
601 return !doc ? '' : String(doc.designMode).toLowerCase();
604 disableItems: function(disabled) {
605 this.getToolbar().items.each(function(item){
606 if(item.getItemId() !== 'sourceedit'){
607 item.setDisabled(disabled);
613 * Toggles the editor between standard and source edit mode.
614 * @param {Boolean} sourceEditMode (optional) True for source edit, false for standard
616 toggleSourceEdit: function(sourceEditMode) {
618 iframe = me.iframeEl,
619 textarea = me.textareaEl,
620 hiddenCls = Ext.baseCSSPrefix + 'hidden',
621 btn = me.getToolbar().getComponent('sourceedit');
623 if (!Ext.isBoolean(sourceEditMode)) {
624 sourceEditMode = !me.sourceEditMode;
626 me.sourceEditMode = sourceEditMode;
628 if (btn.pressed !== sourceEditMode) {
629 btn.toggle(sourceEditMode);
631 if (sourceEditMode) {
632 me.disableItems(true);
634 iframe.addCls(hiddenCls);
635 textarea.removeCls(hiddenCls);
636 textarea.dom.removeAttribute('tabIndex');
640 if (me.initialized) {
641 me.disableItems(me.readOnly);
644 iframe.removeCls(hiddenCls);
645 textarea.addCls(hiddenCls);
646 textarea.dom.setAttribute('tabIndex', -1);
649 me.fireEvent('editmodechange', me, sourceEditMode);
650 me.doComponentLayout();
653 // private used internally
654 createLink : function() {
655 var url = prompt(this.createLinkText, this.defaultLinkValue);
656 if (url && url !== 'http:/'+'/') {
657 this.relayCmd('createlink', url);
661 clearInvalid: Ext.emptyFn,
663 // docs inherit from Field
664 setValue: function(value) {
666 textarea = me.textareaEl;
667 me.mixins.field.setValue.call(me, value);
668 if (value === null || value === undefined) {
672 textarea.dom.value = value;
679 * Protected method that will not generally be called directly. If you need/want
680 * custom HTML cleanup, this is the method you should override.
681 * @param {String} html The HTML to be cleaned
682 * @return {String} The cleaned HTML
684 cleanHtml: function(html) {
686 if (Ext.isWebKit) { // strip safari nonsense
687 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
691 * Neat little hack. Strips out all the non-digit characters from the default
692 * value and compares it to the character code of the first character in the string
693 * because it can cause encoding issues when posted to the server.
695 if (html.charCodeAt(0) === this.defaultValue.replace(/\D/g, '')) {
696 html = html.substring(1);
702 * @protected method that will not generally be called directly. Syncs the contents
703 * of the editor iframe with the textarea.
705 syncValue : function(){
707 body, html, bodyStyle, match;
708 if (me.initialized) {
709 body = me.getEditorBody();
710 html = body.innerHTML;
712 bodyStyle = body.getAttribute('style'); // Safari puts text-align styles on the body element!
713 match = bodyStyle.match(/text-align:(.*?);/i);
714 if (match && match[1]) {
715 html = '<div style="' + match[0] + '">' + html + '</div>';
718 html = me.cleanHtml(html);
719 if (me.fireEvent('beforesync', me, html) !== false) {
720 me.textareaEl.dom.value = html;
721 me.fireEvent('sync', me, html);
726 //docs inherit from Field
727 getValue : function() {
730 if (!me.sourceEditMode) {
733 value = me.rendered ? me.textareaEl.dom.value : me.value;
739 * @protected method that will not generally be called directly. Pushes the value of the textarea
740 * into the iframe editor.
742 pushValue: function() {
746 v = me.textareaEl.dom.value || '';
747 if (!me.activated && v.length < 1) {
750 if (me.fireEvent('beforepush', me, v) !== false) {
751 me.getEditorBody().innerHTML = v;
753 // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
754 me.setDesignMode(false); //toggle off first
755 me.setDesignMode(true);
757 me.fireEvent('push', me, v);
763 deferFocus : function(){
764 this.focus(false, true);
767 getFocusEl: function() {
770 return win && !me.sourceEditMode ? win : me.textareaEl;
774 initEditor : function(){
775 //Destroying the component during/before initEditor can cause issues.
778 dbody = me.getEditorBody(),
779 ss = me.textareaEl.getStyles('font-size', 'font-family', 'background-image', 'background-repeat', 'background-color', 'color'),
783 ss['background-attachment'] = 'fixed'; // w3c
784 dbody.bgProperties = 'fixed'; // ie
786 Ext.core.DomHelper.applyStyles(dbody, ss);
792 Ext.EventManager.removeAll(doc);
797 * We need to use createDelegate here, because when using buffer, the delayed task is added
798 * as a property to the function. When the listener is removed, the task is deleted from the function.
799 * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors
800 * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.
802 fn = Ext.Function.bind(me.onEditorEvent, me);
803 Ext.EventManager.on(doc, {
811 // These events need to be relayed from the inner document (where they stop
812 // bubbling) up to the outer document. This has to be done at the DOM level so
813 // the event reaches listeners on elements like the document body. The effected
814 // mechanisms that depend on this bubbling behavior are listed to the right
816 fn = me.onRelayedEvent;
817 Ext.EventManager.on(doc, {
818 mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)
819 mousemove: fn, // window resize drag detection
820 mouseup: fn, // window resize termination
821 click: fn, // not sure, but just to be safe
822 dblclick: fn, // not sure again
827 Ext.EventManager.on(doc, 'keypress', me.applyCommand, me);
830 Ext.EventManager.on(doc, 'keydown', me.fixKeys, me);
833 // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK!
834 Ext.EventManager.on(window, 'unload', me.beforeDestroy, me);
835 doc.editorInitialized = true;
837 me.initialized = true;
839 me.setReadOnly(me.readOnly);
840 me.fireEvent('initialize', me);
847 beforeDestroy : function(){
849 monitorTask = me.monitorTask,
853 Ext.TaskManager.stop(monitorTask);
859 Ext.EventManager.removeAll(doc);
861 if (doc.hasOwnProperty(prop)) {
869 Ext.destroyMembers('tb', 'toolbarWrap', 'iframeEl', 'textareaEl');
875 onRelayedEvent: function (event) {
876 // relay event from the iframe's document to the document that owns the iframe...
878 var iframeEl = this.iframeEl,
879 iframeXY = iframeEl.getXY(),
880 eventXY = event.getXY();
882 // the event from the inner document has XY relative to that document's origin,
883 // so adjust it to use the origin of the iframe in the outer document:
884 event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]];
886 event.injectEvent(iframeEl); // blame the iframe for the event...
888 event.xy = eventXY; // restore the original XY (just for safety)
892 onFirstFocus : function(){
896 me.disableItems(me.readOnly);
897 if (Ext.isGecko) { // prevent silly gecko errors
899 selection = me.win.getSelection();
900 if (!selection.focusNode || selection.focusNode.nodeType !== 3) {
901 range = selection.getRangeAt(0);
902 range.selectNodeContents(me.getEditorBody());
903 range.collapse(true);
907 me.execCmd('useCSS', true);
908 me.execCmd('styleWithCSS', false);
913 me.fireEvent('activate', me);
917 adjustFont: function(btn) {
918 var adjust = btn.getItemId() === 'increasefontsize' ? 1 : -1,
919 size = this.getDoc().queryCommandValue('FontSize') || '2',
920 isPxSize = Ext.isString(size) && size.indexOf('px') !== -1,
922 size = parseInt(size, 10);
925 // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
929 else if (size <= 13) {
932 else if (size <= 16) {
935 else if (size <= 18) {
938 else if (size <= 24) {
944 size = Ext.Number.constrain(size, 1, 6);
946 isSafari = Ext.isSafari;
947 if (isSafari) { // safari
950 size = Math.max(1, size + adjust) + (isSafari ? 'px' : 0);
952 this.execCmd('FontSize', size);
956 onEditorEvent: function(e) {
957 this.updateToolbar();
961 * Protected method that will not generally be called directly. It triggers
962 * a toolbar update by reading the markup state of the current selection in the editor.
964 updateToolbar: function() {
966 btns, doc, name, fontSelect;
977 btns = me.getToolbar().items.map;
980 if (me.enableFont && !Ext.isSafari2) {
981 name = (doc.queryCommandValue('FontName') || me.defaultFont).toLowerCase();
982 fontSelect = me.fontSelect.dom;
983 if (name !== fontSelect.value) {
984 fontSelect.value = name;
988 function updateButtons() {
989 Ext.Array.forEach(Ext.Array.toArray(arguments), function(name) {
990 btns[name].toggle(doc.queryCommandState(name));
994 updateButtons('bold', 'italic', 'underline');
996 if(me.enableAlignments){
997 updateButtons('justifyleft', 'justifycenter', 'justifyright');
999 if(!Ext.isSafari2 && me.enableLists){
1000 updateButtons('insertorderedlist', 'insertunorderedlist');
1003 Ext.menu.Manager.hideAll();
1009 relayBtnCmd: function(btn) {
1010 this.relayCmd(btn.getItemId());
1014 * Executes a Midas editor command on the editor document and performs necessary focus and
1015 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
1016 * @param {String} cmd The Midas command
1017 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
1019 relayCmd: function(cmd, value) {
1020 Ext.defer(function() {
1023 me.execCmd(cmd, value);
1029 * Executes a Midas editor command directly on the editor document.
1030 * For visual commands, you should use {@link #relayCmd} instead.
1031 * <b>This should only be called after the editor is initialized.</b>
1032 * @param {String} cmd The Midas command
1033 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
1035 execCmd : function(cmd, value){
1039 doc.execCommand(cmd, false, value === undef ? null : value);
1044 applyCommand : function(e){
1047 c = e.getCharCode(), cmd;
1049 c = String.fromCharCode(c);
1072 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
1074 * @param {String} text
1076 insertAtCursor : function(text){
1083 range = me.getDoc().selection.createRange();
1085 range.pasteHTML(text);
1090 me.execCmd('InsertHTML', text);
1097 fixKeys: function() { // load time branching for fastest keydown performance
1106 range = doc.selection.createRange();
1108 range.collapse(true);
1109 range.pasteHTML(' ');
1113 else if (k === e.ENTER) {
1114 range = doc.selection.createRange();
1116 target = range.parentElement();
1117 if(!target || target.tagName.toLowerCase() !== 'li'){
1119 range.pasteHTML('<br />');
1120 range.collapse(false);
1131 if (e.getKey() === e.TAB) {
1134 me.execCmd('InsertHTML',' ');
1146 me.execCmd('InsertText','\t');
1149 else if (k === e.ENTER) {
1151 me.execCmd('InsertHtml','<br /><br />');
1157 return null; // not needed, so null
1161 * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>
1162 * @return {Ext.toolbar.Toolbar}
1164 getToolbar : function(){
1165 return this.toolbar;
1169 * Object collection of toolbar tooltips for the buttons in the editor. The key
1170 * is the command id associated with that button and the value is a valid QuickTips object.
1175 title: 'Bold (Ctrl+B)',
1176 text: 'Make the selected text bold.',
1177 cls: 'x-html-editor-tip'
1180 title: 'Italic (Ctrl+I)',
1181 text: 'Make the selected text italic.',
1182 cls: 'x-html-editor-tip'
1190 title: 'Bold (Ctrl+B)',
1191 text: 'Make the selected text bold.',
1192 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1195 title: 'Italic (Ctrl+I)',
1196 text: 'Make the selected text italic.',
1197 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1200 title: 'Underline (Ctrl+U)',
1201 text: 'Underline the selected text.',
1202 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1204 increasefontsize : {
1206 text: 'Increase the font size.',
1207 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1209 decreasefontsize : {
1210 title: 'Shrink Text',
1211 text: 'Decrease the font size.',
1212 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1215 title: 'Text Highlight Color',
1216 text: 'Change the background color of the selected text.',
1217 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1220 title: 'Font Color',
1221 text: 'Change the color of the selected text.',
1222 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1225 title: 'Align Text Left',
1226 text: 'Align text to the left.',
1227 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1230 title: 'Center Text',
1231 text: 'Center text in the editor.',
1232 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1235 title: 'Align Text Right',
1236 text: 'Align text to the right.',
1237 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1239 insertunorderedlist : {
1240 title: 'Bullet List',
1241 text: 'Start a bulleted list.',
1242 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1244 insertorderedlist : {
1245 title: 'Numbered List',
1246 text: 'Start a numbered list.',
1247 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1251 text: 'Make the selected text a hyperlink.',
1252 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1255 title: 'Source Edit',
1256 text: 'Switch to source editing mode.',
1257 cls: Ext.baseCSSPrefix + 'html-editor-tip'
1261 // hide stuff that is not compatible
1279 * @cfg {String} fieldCls @hide
1282 * @cfg {String} focusCls @hide
1285 * @cfg {String} autoCreate @hide
1288 * @cfg {String} inputType @hide
1291 * @cfg {String} invalidCls @hide
1294 * @cfg {String} invalidText @hide
1297 * @cfg {String} msgFx @hide
1300 * @cfg {Boolean} allowDomMove @hide
1303 * @cfg {String} applyTo @hide
1306 * @cfg {String} readOnly @hide
1309 * @cfg {String} tabIndex @hide