dangreenfield
11-24-2007, 05:30 AM
I am currently working on a plugin to allow the selection of a Style from a combo box on the HtmlEditor Toolbar that will apply it to the selected range. I realise that the browsers don't currently support this through their RTE controls and that I will need to create the feature programatically. I will put my code in this forum entry as I develop it, along with any issues and requests for help that I can't find addressed elsewhere.
The first issue I'd like to raise is a modification request to the Toolbar code. I'm not currently a premium member, nor can I afford to be one right now, although I intend to be one in the near future, so I'll put the request here and hope that it gets viewed.
The ability to insert buttons on the toolbar, as opposed to adding them to the end, is a must if the HtmlEditor is to be a truly plugin-friendly tool, which Jack has expressed is his intention. Although all this needed code could actually be included in the plugin itself, it seems to me that the correct place would actually be within the toolbar itself. I realise that the current desire is that the code not be bulky, like other editor offerings, but at the very least, it should be capable of supporting plugins fully, making it easy on those who wish to extend it.
I notice that the toolbar code caters for the insert of a button (Ext.Toolbar.insertButton), but nothing else has been included. I thought the first start would be to have an insertItem function, and then for the add... functions to use insertItem instead of the addItem if a valid insert index is passed, such as what follows...
insertItem: function(index, item) {
var td = document.createElement("td");
this.tr.insertBefore(td, this.tr.childNodes[index]);
this.initMenuTracking(item);
item.render(td);
this.items.insert(index, item);
return item;
}
However, I realised that not all add... functions used addItem and several had their own add routines. The next best idea, I felt, was to have each individual add... function cater for the insert index as the first (or only) parameter and if the index value == -1 then it performed an add, otherwise it performed an insert, similar to what follows...
addItem: function(index, item) {
// var td = this.nextBlock(); <-- old code
var td = document.createElement("td");
if (index < 0) {
this.tr.appendChild(td);
} else {
this.tr.insertBefore(td, this.tr.childNodes[index]);
}
this.initMenuTracking(item);
item.render(td);
// this.items.add(item); <-- old code
// code below would be shorter if MixedCollection.insert
// accepted an index of -1 to mean add
if (index < 0) {
this.items.add(item);
} else {
this.items.insert(index, item);
}
return item;
},
The last problem arises when you look at the general Ext.Toolbar.add function, which only takes an array of item objects, and leaves no room for an insert index for each item. My suggestion is to modify the code to cater for a new xtype of 'indexed' (or something more appropriate), so that the user can pass a wrapper object holding the item object and the insert index (i.e, {xtype: "indexed", index: 0, item: "-"}).
add: function() {
var a = arguments, l = a.length;
for (var i = 0; i < l; i++) {
var el = a[i];
var index = -1;
if (typeof el == "object" && el.xtype && el.xtype == "indexed") {
index = el.index;
el = el.item;
}
if (el.isFormField) { // some kind of form field
this.addField(index, el);
} else if (el.render) { // some kind of Toolbar.Item
this.addItem(index, el);
} else if (typeof el == "string") { // string
if (el == "separator" || el == "-") {
this.addSeparator(index);
} else if (el == " ") {
this.addSpacer(index);
} else if (el == "->") {
this.addFill(index);
} else {
this.addText(index, el);
}
} else if(el.tagName) { // element
this.addElement(index, el);
} else if (typeof el == "object") { // must be button config?
if (el.xtype) {
this.addField(index, Ext.ComponentMgr.create(el, 'button'));
} else {
this.addButton(index, el);
}
}
}
},
The reason why I want this flexibility is because the Styles combo box simply looks and feels best placed before the Font Family combo box.
My plugin currently only adds the combo box, which is a perfect start. From here on out, I'll be working on the functionality.
Ext.ux.HTMLEditorStyles = function(cfg) {
cfg = cfg || [];
if (! cfg instanceof Array) {
cfg = [cfg];
}
cfg = {styles: [{name: "No Style", value: ""}].concat(cfg)};
Ext.apply(this, cfg, {enableStyle: true});
this.init = function(htmlEditor) {
this.editor = htmlEditor;
this.editor.on('render', onRender, this);
};
this.createStyleOptions = function() {
var styles = this.styles;
var buf = [];
for (var i = 0, len = styles.length; i < len; i++) {
style = styles[i];
buf.push(
'<option value="', style.value.toLowerCase(), '">', style.name, '</option>'
);
}
return buf.join('');
};
this.insertItem = function(index, item) {
var tb = this.editor.tb;
var td = document.createElement("td");
tb.tr.insertBefore(td, tb.tr.childNodes[index]);
tb.initMenuTracking(item);
item.render(td);
tb.items.insert(index, item);
}
function onRender() {
if (this.enableStyle && ! Ext.isSafari) {
this.styleSelect = this.editor.tb.el.createChild({
tag: 'select',
cls: 'x-font-select',
html: this.createStyleOptions()
});
this.styleSelect.on('change', function() {
var style = this.styleSelect.dom.value;
// styling code goes here
}, this);
this.insertItem(0, new Ext.Toolbar.Spacer());
this.insertItem(0, new Ext.Toolbar.Item(this.styleSelect.dom));
}
}
}
The plugin is added to the HtmlEditor config as follows.
plugins: new Ext.ux.HTMLEditorStyles([
{name: "Section Heading", value: "section_head"}
])
Let me know if anything obvious sticks out on how I'm coding this. I'll add the functionality once it's complete, or as issues arise.
The first issue I'd like to raise is a modification request to the Toolbar code. I'm not currently a premium member, nor can I afford to be one right now, although I intend to be one in the near future, so I'll put the request here and hope that it gets viewed.
The ability to insert buttons on the toolbar, as opposed to adding them to the end, is a must if the HtmlEditor is to be a truly plugin-friendly tool, which Jack has expressed is his intention. Although all this needed code could actually be included in the plugin itself, it seems to me that the correct place would actually be within the toolbar itself. I realise that the current desire is that the code not be bulky, like other editor offerings, but at the very least, it should be capable of supporting plugins fully, making it easy on those who wish to extend it.
I notice that the toolbar code caters for the insert of a button (Ext.Toolbar.insertButton), but nothing else has been included. I thought the first start would be to have an insertItem function, and then for the add... functions to use insertItem instead of the addItem if a valid insert index is passed, such as what follows...
insertItem: function(index, item) {
var td = document.createElement("td");
this.tr.insertBefore(td, this.tr.childNodes[index]);
this.initMenuTracking(item);
item.render(td);
this.items.insert(index, item);
return item;
}
However, I realised that not all add... functions used addItem and several had their own add routines. The next best idea, I felt, was to have each individual add... function cater for the insert index as the first (or only) parameter and if the index value == -1 then it performed an add, otherwise it performed an insert, similar to what follows...
addItem: function(index, item) {
// var td = this.nextBlock(); <-- old code
var td = document.createElement("td");
if (index < 0) {
this.tr.appendChild(td);
} else {
this.tr.insertBefore(td, this.tr.childNodes[index]);
}
this.initMenuTracking(item);
item.render(td);
// this.items.add(item); <-- old code
// code below would be shorter if MixedCollection.insert
// accepted an index of -1 to mean add
if (index < 0) {
this.items.add(item);
} else {
this.items.insert(index, item);
}
return item;
},
The last problem arises when you look at the general Ext.Toolbar.add function, which only takes an array of item objects, and leaves no room for an insert index for each item. My suggestion is to modify the code to cater for a new xtype of 'indexed' (or something more appropriate), so that the user can pass a wrapper object holding the item object and the insert index (i.e, {xtype: "indexed", index: 0, item: "-"}).
add: function() {
var a = arguments, l = a.length;
for (var i = 0; i < l; i++) {
var el = a[i];
var index = -1;
if (typeof el == "object" && el.xtype && el.xtype == "indexed") {
index = el.index;
el = el.item;
}
if (el.isFormField) { // some kind of form field
this.addField(index, el);
} else if (el.render) { // some kind of Toolbar.Item
this.addItem(index, el);
} else if (typeof el == "string") { // string
if (el == "separator" || el == "-") {
this.addSeparator(index);
} else if (el == " ") {
this.addSpacer(index);
} else if (el == "->") {
this.addFill(index);
} else {
this.addText(index, el);
}
} else if(el.tagName) { // element
this.addElement(index, el);
} else if (typeof el == "object") { // must be button config?
if (el.xtype) {
this.addField(index, Ext.ComponentMgr.create(el, 'button'));
} else {
this.addButton(index, el);
}
}
}
},
The reason why I want this flexibility is because the Styles combo box simply looks and feels best placed before the Font Family combo box.
My plugin currently only adds the combo box, which is a perfect start. From here on out, I'll be working on the functionality.
Ext.ux.HTMLEditorStyles = function(cfg) {
cfg = cfg || [];
if (! cfg instanceof Array) {
cfg = [cfg];
}
cfg = {styles: [{name: "No Style", value: ""}].concat(cfg)};
Ext.apply(this, cfg, {enableStyle: true});
this.init = function(htmlEditor) {
this.editor = htmlEditor;
this.editor.on('render', onRender, this);
};
this.createStyleOptions = function() {
var styles = this.styles;
var buf = [];
for (var i = 0, len = styles.length; i < len; i++) {
style = styles[i];
buf.push(
'<option value="', style.value.toLowerCase(), '">', style.name, '</option>'
);
}
return buf.join('');
};
this.insertItem = function(index, item) {
var tb = this.editor.tb;
var td = document.createElement("td");
tb.tr.insertBefore(td, tb.tr.childNodes[index]);
tb.initMenuTracking(item);
item.render(td);
tb.items.insert(index, item);
}
function onRender() {
if (this.enableStyle && ! Ext.isSafari) {
this.styleSelect = this.editor.tb.el.createChild({
tag: 'select',
cls: 'x-font-select',
html: this.createStyleOptions()
});
this.styleSelect.on('change', function() {
var style = this.styleSelect.dom.value;
// styling code goes here
}, this);
this.insertItem(0, new Ext.Toolbar.Spacer());
this.insertItem(0, new Ext.Toolbar.Item(this.styleSelect.dom));
}
}
}
The plugin is added to the HtmlEditor config as follows.
plugins: new Ext.ux.HTMLEditorStyles([
{name: "Section Heading", value: "section_head"}
])
Let me know if anything obvious sticks out on how I'm coding this. I'll add the functionality once it's complete, or as issues arise.