This plugin provides a format-block for the extjs HtmlEditor. It is based on the work of Shea Frederick and some code from TinyMCE. This plugin has sofar only been tested in Firefox, so if anybody uses it and finds it to be buggy, please drop me a line and a patch if possible. Save the following code to a file, add this to your html before extjs-all.js and install it the same way as the plugins by Shea Frederick, so go and take a look there. Since I took some code from TinyMCE this code is licensed under the LGPL.
Ext.ux.form.HtmlEditor.Formatblock = Ext.extend(Ext.util.Observable , { // private init: function(cmp){ this.cmp = cmp; this.cmd = 'FormatBlock'; this.store = new Ext.data.SimpleStore({ fields: ['tag', 'name'], data : [ ['p', 'Paragraph'], ['div', 'Div'], ['h1', 'Header 1'], ['h2', 'Header 2'], ['h3', 'Header 3'], ['h4', 'Header 4'], ['address', 'Address'], ['blockquote', 'Blockquote'], ['pre', 'Preformated'] ] }); this.cmp.on('render', this.onRender, this); this.cmp.on('initialize', this.onInit, this, {delay:100, single: true}); }, // private onInit: function(){ Ext.EventManager.on(this.cmp.getDoc(), { 'mousedown': this.onEditorEvent, 'dblclick': this.onEditorEvent, 'click': this.onEditorEvent, 'keyup': this.onEditorEvent, buffer:100, scope: this }); }, // private onRender: function() { var tb = this.cmp.getToolbar(); this.formatSelector = this.createFormatSelector(); tb.add(this.formatSelector); }, //private createFormatSelector: function(){ var combo = new Ext.form.ComboBox({ store: this.store, displayField:'name', valueField:'tag', typeAhead: true, mode: 'local', width: 80, triggerAction: 'all', emptyText:'Select a format ...', valueNotFoundText: 'Select a format ...', forceSelection: true, editable: false, listWidth: 120, selectOnFocus:true, listeners:{ scope: this, 'select': function(combo, record, index){ tag = record.data.tag; this.insertFormatblock(tag); this.cmp.getToolbar().focus(); this.cmp.deferFocus(); this.formatSelector.reset(); } } }); return combo; }, insertFormatblock : function(val) { if (val.indexOf('<') == -1) val = '<' + val + '>'; if (Ext.isGecko) val = val.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi, '$1'); this.cmp.relayCmd('FormatBlock', val); }, getSelection : function() { win = this.cmp.getWin(); return win.getSelection ? win.getSelection() : win.document.selection; }, getRange : function() { var win = this.cmp.getWin(), s, r; try { if (s = this.getSelection()) r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : win.document.createRange()); } catch (ex) { } // No range found then create an empty one // This can occur when the editor is placed in a hidden container element on Gecko // Or on IE when there was an exception if (!r) r = Exi.isIE ? win.document.body.createTextRange() : win.document.createRange(); return r; }, getNode: function(){ var elem, e, r = this.getRange(), s = this.getSelection(); if (!Ext.isIE) { // Range maybe lost after the editor is made visible again if (!r) return this.cmp.getDoc().dom.getRoot(); e = r.commonAncestorContainer; // Handle selection a image or other control like element such as anchors if (!r.collapsed) { // If the anchor node is a element instead of a text node then return this element if (Ext.isWebKit && s.anchorNode && s.anchorNode.nodeType == 1) return s.anchorNode.childNodes[s.anchorOffset]; if (r.startContainer == r.endContainer) { if (r.startOffset - r.endOffset < 2) { if (r.startContainer.hasChildNodes()) e = r.startContainer.childNodes[r.startOffset]; } } } elem = e.parentNode; } else { if (r.item) elem = r.item(0); else elem = r.parentElement(); } return Ext.get(elem); }, getBlocklevelElement: function(n){ if(n){ if (/^(P|DIV|H[1-6]|ADDRESS|BODY|BLOCKQUOTE|PRE)$/.test(n.dom.nodeName)){ return n; } else { return this.getBlocklevelElement(n.findParentNode()); } } return null; }, // private onEditorEvent: function(){ var fs = this.formatSelector, r, p; p = this.getBlocklevelElement(this.getNode()); if (p && p.dom && p.dom.nodeName != 'BODY'){ fs.setValue(p.dom.nodeName.toLowerCase()); } else { fs.reset(); } } });
Erzsebet
Hi,
I’ve tried your extension and its really cool but i get an error everytime i try to select text. The error is “ss is undefined” trying to do “ss = ss.replace(trimRe, “”);” and it’s located on line 139 -return this.getBlocklevelElement(n.findParentNode()); – and on line 148 -p = this.getBlocklevelElement(this.getNode()); – If u know whats the problem please, contact me. Thank u
Erzsebet
I think the problem is with line 139 n.findParentNode() i think you should use n.findParentElement(), but i’m not completely sure
admin
I’ve seen that error you mention as well but so far haven’t bothered looking for the cause because it ususally continued to work just fine.
your solution sounds reasonable. have you tried it?
Best
Erzsebet
I’ve tried but it doesnt work 🙁 And on the crappy ie6 sometimes this error doesnt allow to select text anymore.
On the other hand i’ve seen that if you include any span,b or any tag which arent at the combobox of the formatblock, or you paste any html code with those tags the combobox gets lost and doest know what to show…
caribu
too bad. and I don’t have time to really look into it right now. I was already suspecting that this code would not work in ie. and I don’t use windows, so it is a bit of a hassle to test it on ie.
the combobox is only supposed to show something, if an ancestor of the current selection is one of ‘p’, ‘div’, ‘h1 – 4′,’address’, ‘blockquote’, ‘pre’. So it won’t show anything if that is not the case. So maybe what you are describing in your last post is actually expected behaviour.
Nikolas Hagelstein
Well the quickest way to fix that \ss is undefined\ thing, is to change :
this.getBlocklevelElement(n.findParentNode());
to
this.getBlocklevelElement(n.findParentNode(‘*’));
Cheers,
Nikolas
Jangla
Is there a way of tweaking this code so I could actually apply, say, a div with a predefined class to the HTML?
admin
that is not so easy because this plugin is using execCommand to insert the html tags. and execCommand doesn’t have a feature to include css classes.
I wrote another somewhat awkward plugin to insert css-classes. It allows the user to include and remove css clases on arbitrary tags. It is awkward because the UI is not very pretty. If you are interested in that I’ll post it in the next few days.
Tobias Hochguertel
Thanks to “Nikolas Hagelstein”, for the fix of the Error which belongs to “ss = ss.replace(trimRe, “”);”. With this.getBlocklevelElement(n.findParentNode(‘*’)); it works pretty!
Thank you for the Plugin which also works with current Ext JS 4.2.1 Version.