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 says:
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
December 14, 2009, 6:47 pmErzsebet says:
I think the problem is with line 139 n.findParentNode() i think you should use n.findParentElement(), but i’m not completely sure
December 14, 2009, 7:34 pmadmin says:
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
December 14, 2009, 7:41 pmErzsebet says:
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…
December 16, 2009, 2:09 pmcaribu says:
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.
December 16, 2009, 2:16 pmNikolas Hagelstein says:
Well the quickest way to fix that \ss is undefined\ thing, is to change :
this.getBlocklevelElement(n.findParentNode());
to
this.getBlocklevelElement(n.findParentNode(‘*’));
Cheers,
October 25, 2010, 7:57 pmNikolas
Jangla says:
Is there a way of tweaking this code so I could actually apply, say, a div with a predefined class to the HTML?
March 10, 2011, 2:49 pmadmin says:
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.
March 10, 2011, 2:59 pmI 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.