PDA

View Full Version : HtmlEditor & Toolbar Extended for Extensibility



dangreenfield
29 Nov 2007, 2:56 AM
I have created new extensions to the Ext.form.HtmlEditor and Ext.Toolbar components that I believe are better suited for handling plugins. As Jack et al have commented that the HtmlEditor was designed to be 'lightweight', it follows that the tool should at least be fit for the easy integration of plugins. I found it wasn't, so I have extended it to cater for them.

The code attached is only a suggestion of the way the editor should function in future. I'm open to anyone suggesting it should work another way. Use it if you feel it is of value, or not, if you don't.

I'll start with the toolbar. I called it Ext.ux.HTMLEditorToolbar, as I felt that the regular toolbar worked sufficiently for non-editor needs, so I wrote this one solely with the editor in mind.

The main problem with the existing toolbar was that the editor added the tools directly into it, both sequentially and at the time of render. I wanted the tools to be added prior to the toolbar being rendered. I also wanted the tools to be inserted, if needed, rather than just added to the end.


// Ext.ux.HTMLEditorToolbar
// extension of Ext.Toolbar to cater for extensibility
Ext.ux.HTMLEditorToolbar = Ext.extend(Ext.Toolbar, {

// overrides Ext.Toolbar.initComponent
// first function to be called upon creation of toolbar
initComponent: function() {

// call Ext.Toolbar.initComponent
Ext.ux.HTMLEditorToolbar.superclass.initComponent.call(this);

// unable to use existing items collection for pre-render
// configuration as it's updated by Ext.Toolbar during render
this.tools = new Ext.util.MixedCollection(false, function(tool) {
return tool.itemId || tool.id || Ext.id();
});

},

// add tools (pre-render)
addTools: function(tools) {
tools = (tools instanceof Array) ? tools : [tools];
for (var i = 0, len = tools.length; i < len; i++) {
this.tools.add(tools[i]);
}
},

// insert tools (pre-render)
insertTools: function(index, tools) {
tools = (tools instanceof Array) ? tools : [tools];
for (var i = 0, len = tools.length; i < len; i++) {
this.tools.insert(index + i, tools[i]);
}
},

// insert tools before another tool (pre-render)
insertToolsBefore: function(itemId, tools) {
var index = this.tools.indexOfKey(itemId);
this.insertTools(index, tools);
},

// insert tools after another tool (pre-render)
insertToolsAfter: function(itemId, tools) {
var index = this.tools.indexOfKey(itemId) + 1;
this.insertTools(index, tools);
},

// render tools (performed after tools/plugins have been configured/reordered)
renderTool: function(tool) {

// cater for new tbcombo component
// created to split configuration from render
if (typeof tool == "object" && tool.xtype && tool.xtype == "tbcombo") {

// not catered for in Ext.Toolbar.add function
// as it defaults to addField instead of addItem
this.addItem(Ext.ComponentMgr.create(tool));

}
else {

// else use existing Ext.Toolbar.add function
// to render tools
this.add(tool);

}

},

// overrides Ext.Toolbar.onRender
onRender: function(ct, position) {

// call Ext.Toolbar.onRender
Ext.ux.HTMLEditorToolbar.superclass.onRender.call(this, ct, position);

// loop through pre-configured/reordered tools and render each accordingly
this.tools.each(this.renderTool, this);

}

});


I also added a new component to the toolbar to handle comboboxes. The existing fontnames combobox could only be created at the time of render, so adding a new component meant that I could configure it in memory prior to it being rendered to the toolbar.


// Ext.ux.HTMLEditorToolbar.ComboBox
// created to handle the pre-configuration of a combobox (pre-render)
Ext.ux.HTMLEditorToolbar.ComboBox = function(config) {

Ext.apply(this, config);

// create combobox in memory before render
var selEl = document.createElement("select");
selEl.className = this.cls;
for (var i = 0, len = this.opts.length; i < len; i++) {
var opt = this.opts[i];
var optEl = document.createElement('option');
optEl.text = opt.text;
optEl.value = opt.value;
if (opt.selected) {
optEl.selected = true;
this.defaultValue = opt.value;
}
selEl.options.add(optEl);
}
if (! this.defaultValue) {
this.defaultValue = this.opts[0].value;
}

// call Ext.Toolbar.Item constructor passing combobox
Ext.ux.HTMLEditorToolbar.ComboBox.superclass.constructor.call(this, selEl);

}

// Ext.ux.HTMLEditorToolbar.ComboBox
// extension of Ext.Toolbar.Item
Ext.extend(Ext.ux.HTMLEditorToolbar.ComboBox, Ext.Toolbar.Item, {

// overrides Ext.Toolbar.Item.render
render: function(td) {

// call Ext.Toolbar.Item.render
Ext.ux.HTMLEditorToolbar.ComboBox.superclass.render.call(this, td);

// add handler for combobox change event
Ext.EventManager.on(this.el, 'change', this.handler, this.scope);

}

});

// register Ext.ux.HTMLEditorToolbar.ComboBox as a new component
Ext.ComponentMgr.registerType('tbcombo', Ext.ux.HTMLEditorToolbar.ComboBox);


Now, to the HtmlEditor. I have called the extended version Ext.ux.HTMLEditor. It has changed significantly.

I no longer use the enable... flags to include the tools, but use an array, called toolbarItems, that can be overwritten in the config file (I also included a config array called toolbarItemExcludes, as an alternative, to exclude tools contained in the standard toolbarItems array). Using this array means that you can display the tools in any order.

Once the editor is initialized, the toolbar will have all the tools configured, meaning that any plugins can then manipulate the tools list by adding or inserting new tools as needed. This can all happen prior to the toolbar being rendered.


// Ext.ux.HTMLEditor
// extends Ext.form.HtmlEditor to provide extensibility
Ext.ux.HTMLEditor = Ext.extend(Ext.form.HtmlEditor, {

// using the enable... flags to define content meant that items
// were always added in the same order.
// using the toolbarItems list instead allows the user to override
// the order of items, and even exclude items not wanted.
// the enable... flags are now no longer used
toolbarItems: [
'fonts',
'allformats',
'allfontsizes',
'allcolors',
'allalignments',
'alllinks',
'alllists',
'sourceedit'
],

// as an alternative, the toolbarItemExcludes list can be used to
// exclude items from the toolbarItem list
toolbarItemExcludes: [],

// overrides Ext.form.HtmlEditor.initComponent
// first function to be called upon creation of the editor
initComponent: function() {

// call Ext.form.HtmlEditor.initComponent
Ext.ux.HTMLEditor.superclass.initComponent.call(this);

// add important event missing from Ext.form.HtmlEditor
this.addEvents({
editorevent: true
});

// remove any toolbarItemExcludes from the toolbarItems array
for (var i = 0, iMax = this.toolbarItemExcludes.length; i < iMax; i++) {
var item = this.toolbarItemExcludes[i].toLowerCase();
for (var j = 0, jMax = this.toolbarItems.length; j < jMax; j++) {
if (this.toolbarItems[j] == item) {
this.toolbarItems.splice(j, 1);
break;
}
}
}

// create the editor toolbar
this.tb = new Ext.ux.HTMLEditorToolbar();

// create the toolbar items
this.createTools(this.toolbarItems);

},

// overrides Ext.form.HtmlEditor.createFontOptions
createFontOptions: function() {
var opts = [], ffs = this.fontFamilies, ff;
for (var i = 0, len = ffs.length; i < len; i++) {
ff = ffs[i];
fflc = ff.toLowerCase();
var opt = {text: ff, value: fflc};
if (fflc == this.defaultFont) opt.selected = true;
opts.push(opt);
}
return opts;
},

// create default button config
btn: function(id, toggle, queryState, handler) {
return {
itemId: id,
cls: 'x-btn-icon x-edit-' + id,
enableToggle: toggle !== false,
queryState: queryState !== false,
handler: handler || this.relayBtnCmd,
scope: this,
clickEvent: 'mousedown',
tooltip: this.buttonTips[id] || undefined,
tabIndex: -1
};
},

// create known tools based on the passed item list (initially
// from the toolbarItems list) and add it to the tools collection.
// this function allows random tool allocation as opposed
// to the old version that added tools sequentially
createTools: function(toolbarItems) {

// convert single items to a list
toolbarItems = (toolbarItems instanceof Array) ? toolbarItems : [toolbarItems];

// loop through the item list
for (var i = 0, len = toolbarItems.length; i < len; i++) {

//add the item to the toolbar
var item = toolbarItems[i];
switch (item) {

// add the fonts combobox
case 'fonts':
if (! Ext.isSafari) {
this.tb.addTools({
itemId: 'fontname',
xtype: 'tbcombo',
cls: 'x-font-select',
opts: this.createFontOptions(),
queryValue: true,
handler: function(event, el) {
this.relayCmd('fontname', el.value);
this.deferFocus();
},
scope: this
});
}
break;

// add the bold button
case 'bold':
this.tb.addTools(this.btn('bold'));
break;

// add the italic button
case 'italic':
this.tb.addTools(this.btn('italic'));
break;

// add the underline button
case 'underline':
this.tb.addTools(this.btn('underline'));
break;

// add all format buttons (with a leading separator)
case 'allformats':
this.createTools(['-', 'bold', 'italic', 'underline']);
break;

// add the increasefontsize button
case 'increasefontsize':
this.tb.addTools(this.btn('increasefontsize', false, false, this.adjustFont));
break;

// add the decreasefontsize button
case 'decreasefontsize':
this.tb.addTools(this.btn('decreasefontsize', false, false, this.adjustFont));
break;

// add both fontsize buttons (with a leading separator)
case 'allfontsizes':
this.createTools(['-', 'increasefontsize', 'decreasefontsize']);
break;

// add the forecolor button and associated menu
case 'forecolor':
this.tb.addTools({
itemId: 'forecolor',
cls: 'x-btn-icon x-edit-forecolor',
clickEvent: 'mousedown',
tooltip: this.buttonTips['forecolor'],
tabIndex: -1,
menu: new Ext.menu.ColorMenu({
allowReselect: true,
focus: Ext.emptyFn,
value: '000000',
plain: true,
selectHandler: function(cp, color) {
this.execCmd('forecolor', Ext.isSafari || Ext.isIE ? '#' + color : color);
this.deferFocus();
},
scope: this,
clickEvent:'mousedown'
})
});
break;

// add the backcolor button and associated menu
case 'backcolor':
this.tb.addTools({
itemId: 'backcolor',
cls: 'x-btn-icon x-edit-backcolor',
clickEvent: 'mousedown',
tooltip: this.buttonTips['backcolor'],
tabIndex: -1,
menu: new Ext.menu.ColorMenu({
focus: Ext.emptyFn,
value: 'FFFFFF',
plain: true,
allowReselect: true,
selectHandler: function(cp, color) {
if (Ext.isGecko) {
this.execCmd('useCSS', false);
this.execCmd('hilitecolor', color);
this.execCmd('useCSS', true);
this.deferFocus();
}
else {
this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor',
Ext.isSafari || Ext.isIE ? '#' + color : color);
this.deferFocus();
}
},
scope: this,
clickEvent: 'mousedown'
})
});
break;

// add both color buttons (with a leading separator)
case 'allcolors':
this.createTools(['-', 'forecolor', 'backcolor']);
break;

// add the justifyleft button
case 'justifyleft':
this.tb.addTools(this.btn('justifyleft'));
break;

// add the justifycenter button
case 'justifycenter':
this.tb.addTools(this.btn('justifycenter'));
break;

// add the justifyright button
case 'justifyright':
this.tb.addTools(this.btn('justifyright'));
break;

// add all alignment buttons (with a leading separator)
case 'allalignments':
this.createTools(['-', 'justifyleft', 'justifycenter', 'justifyright']);
break;

// add the link button
case 'link':
if (! Ext.isSafari) {
this.tb.addTools(this.btn('createlink', false, false, this.createLink));
}
break;

// add the link button (with a leading separator)
case 'alllinks':
if (! Ext.isSafari) {
this.createTools(['-', 'link']);
}
break;

// add the orderedlist button
case 'orderedlist':
if (! Ext.isSafari) {
this.tb.addTools(this.btn('insertorderedlist'));
}
break;

// add the unorderedlist button
case 'unorderedlist':
if (! Ext.isSafari) {
this.tb.addTools(this.btn('insertunorderedlist'));
}
break;

// add both list buttons (with a leading separator)
case 'alllists':
if (! Ext.isSafari) {
this.createTools(['-', 'orderedlist', 'unorderedlist']);
}
break;

// add the sourceedit button
case 'sourceedit':
if (! Ext.isSafari) {
this.tb.addTools(this.btn('sourceedit', true, false, function(btn) {
this.toggleSourceEdit(btn.pressed);
}));
}
break;

// allows for '-', 'separator', ' ', '->', labels, or other item types
default:
this.tb.addTools(item);

}
}

},

// overrides Ext.form.HtmlEditor.createToolbar
// most functionality has been removed as this is called
// upon render
createToolbar: function() {

// render toolbar
this.tb.render(this.wrap.dom.firstChild);

// inherited
this.tb.el.on('click', function(e) {
e.preventDefault();
});

},

// overrides Ext.form.HtmlEditor.getDocMarkup
// provides ability to include stylesheets in the editor document
// created by bpjohnson (see http://extjs.com/forum/showthread.php?t=9588)
getDocMarkup: function() {
var markup = '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style>';
if (this.styles) {
for (var i = 0; i < this.styles.length; i++) {
markup = markup + '<link rel="stylesheet" type="text/css" href="' + this.styles[i] + '" />';
}
}
markup = markup + '</head><body></body></html>';
return markup;
},

// overrides Ext.form.HtmlEditor.onEditorEvent
onEditorEvent: function(e) {

// call Ext.form.HtmlEditor.onEditorEvent
Ext.ux.HTMLEditor.superclass.onEditorEvent.call(this, e);

// fire new editorevent to tell plugins that an event occurred
// in the editor.
// this saves plugins from having to monitor multiple events
// i.e. 'click', 'keyup', etc.
this.fireEvent('editorevent', this, e);

},

// overrides Ext.form.HtmlEditor.updateToolbar
// does not call superclass function as much of it was no
// longer needed, but duplicates some code
updateToolbar: function() {

// inherited
if (! this.activated) {
this.onFirstFocus();
return;
}

// loop through toolbar items and update status based on
// query values return from the browser (if configured)
this.tb.items.each(function(item) {
if (item.queryState) {
item.toggle(this.doc.queryCommandState(item.itemId));
}
else if (item.queryEnabled) {
item.setDisabled(! this.doc.queryCommandEnabled(item.itemId));
}
else if (item.xtype = "tbcombo" && item.queryValue) {
var value = (this.doc.queryCommandValue(item.itemId) || item.defaultValue).toLowerCase();
if (value != item.el.value) {
item.el.value = value;
}
}
}, this);

// inherited
Ext.menu.MenuMgr.hideAll();

// inherited
this.syncValue();

}

});

I have attached a demo of the functionality, including an updated example of my HTMLEditorStyles plugin, which now utilises the new functionality.

For those who requested a live demo, click here (http://coder.4realhost.com/htmleditor/).

ralf
30 Nov 2007, 4:28 AM
Hi Dan,

this works like a charm. Thank you for sharing!

I extended your code a little bit since I needed to insert more than one style combo box and to toggle the visibility at runtime:

htmleditorstyles.js


// extended contructor to configure id and added parameter styleId
Ext.ux.HTMLEditorStyles = function(id, styles) {
this.styleId= id;
...
// use styleId instead of 'style'
this.editor.tb.insertToolsBefore('fontname', [{
itemId: this.styleId,
xtype: 'tbcombo',
....
// use styleId instead of 'style'
this.onEditorEvent = function() {
var element = Ext.isIE ? this.editor.doc.selection.createRange().parentElement() :
this.editor.win.getSelection().anchorNode;
var parent = getParentStyleElement(element);
var style = parent ? parent.className : "none";
if (this.editor.tb.items.map[this.styleId].el.value != style) {
this.editor.tb.items.map[this.styleId].el.value = style;
}
}


htmleditor.js


....
//hide and show single toolbar items at runtime
hideTool: function(id) {
this.tb.items.map[id].hide();
},
showTool: function(id) {
this.tb.items.map[id].show();
}

dangreenfield
30 Nov 2007, 12:57 PM
Hi Ralf. You made the mod perfectly. I would have done it the same way!

NOTE: I had never previously discovered a best practice way to coding in javascript, so my code, up until this point, was pretty much just thrown together. Since discovering this great article in the Community Manual on Basic Application Design (see http://extjs.com/learn/Manual:Basic_Application_Design), I've decided to code all my scripts using this format (ie, separating private from public functions). Because of this, the HTMLEditorStyles script, included in the first post above, has changed. If you wish to view the old code to understand Ralf's comments above then please see the old HTMLEditorStyles version below.

stever
18 Dec 2007, 4:12 PM
Is this your latest? I'm just now converting my stuff to use this plugin model where appropriate and so would like to start from the latest.

dangreenfield
23 Dec 2007, 1:50 PM
Is this your latest? I'm just now converting my stuff to use this plugin model where appropriate and so would like to start from the latest.
Yes, the first post contains the latest version. I'm happy with my code as it stands and noone else has suggested any changes. Good luck.

mykes
14 Feb 2008, 5:58 PM
It's an awesome enhancement, thanks!

I have a question/suggestion, though.

When you get enough buttons on the toolbar, you really want to make the toolbar two rows, so you can fit then in a smallish window neatly. Or to organize the buttons by functions.

For an example, just look at this message board's Reply to Thread editor - it has two rows of buttons - one with font selection/size, etc., on the top row, the bottom row has bold, italics, etc.

The simple editor is sufficient to do almost all things, since you can directly edit the HTML, but if people want to really turn this into something more advanced for their purpose/applications, they might want to add "create table," "choose style (h1, h2, etc.)," "insert video," "insert smilie," and several other buttons...

I figure as long as you've extended the HTMLEditor toolbar class, you might want to take my suggestions into consideration.

Regards

stever
14 Feb 2008, 6:01 PM
There is work going on now to make all toolbars into containers. Then you could have a single-line layout, two-line layout, ribbon-layout, etc. I think it would be worth waiting to see if that gets put into the main Ext codebase, since that would make this a lot easier...

Colter
15 Feb 2008, 12:20 AM
Do you have a screen shot or demo to view? Thanks

vlados
15 Feb 2008, 12:29 AM
I agree, let's see it ;)

mykes
15 Feb 2008, 1:17 PM
I've been examining the code closely and the undo/redo plugin references the editor.tb toolbar directly. Are you concerned that turning everything into containers is going to break a lot of code like I just mentioned?

stever
16 Feb 2008, 11:19 AM
I've been examining the code closely and the undo/redo plugin references the editor.tb toolbar directly. Are you concerned that turning everything into containers is going to break a lot of code like I just mentioned?

Only testing would show, but I don't think the changes would have to be that big of a deal. I'm looking at the ManagedIFrame to add DesignMode stuff to it, so we could separate that out for use in the HTMLEditor and for CodePress. When I get around to that, I'll give the other stuff a try too...

dangreenfield
14 Mar 2008, 12:14 PM
Do you have a screen shot or demo to view? Thanks
I agree, let's see it ;)

Click here to see a live demo (http://danwgreenfield.free-web-hosting.biz/htmleditor/).

mabello
14 Mar 2008, 2:19 PM
I really like the extension, I think it could be really useful.
Keep up the good work!

dddu88
7 Apr 2008, 6:26 PM
From the live demo, I don't see any font size selection, also I like to have 'smilies', any suggestion about how to get them in?

Thanks
Dave

dangreenfield
7 Apr 2008, 6:58 PM
No there are no font-sizes. Sorry, I can't help you with the smilies either. :D

dddu88
8 Apr 2008, 6:02 AM
So the only difference between this one and the standard one in the library is that this one has a combo box to select 'standard header'. any more difference?

Thanks
Davd

dangreenfield
8 Apr 2008, 11:17 AM
Dave, the purpose of this thread was to provide a version of the HtmlEditor that allowed for greater plugin integration. The 'Styles' plugin was only included as an example.

There is no advanced version of the HtmlEditor, and I don't have the time or resources to create one myself, so I provided this version to allow other developers to create the advanced functionality, one plugin at a time.

I have created only two so far...



HtmlEditor Styles Plugin (http://extjs.com/forum/showthread.php?t=19059)

HtmlEditor Undo/Redo Plugin (http://extjs.com/forum/showthread.php?t=19754)


But I am in the process of publishing a third that allows you to insert and edit images, and will add this to the list soon. As far as I know, no one else has published any others using my tool, but check out the forum using the Search function as others may have hardcoded other functions.

escalade
1 May 2008, 11:23 AM
Hi dangreenfield,
Thanks for the nice extensions. Is there any easy way to apply multiple plugins to one htmleditor? For example, undo-redo and image plugins apply to one editor.

Thank you.

dangreenfield
1 May 2008, 12:34 PM
Is there any easy way to apply multiple plugins to one htmleditor? For example, undo-redo and image plugins apply to one editor.

Yes, simply add both plugins to the config object when defining the Ext.ux.HTMLEditor:


plugins: [
new Ext.ux.HTMLEditorUndoRedo(),
new Ext.ux.HTMLEditorImage()
]

escalade
1 May 2008, 6:39 PM
Yes, simply add both plugins to the config object when defining the Ext.ux.HTMLEditor:


plugins: [
new Ext.ux.HTMLEditorUndoRedo(),
new Ext.ux.HTMLEditorImage()
]

That's great. Thank you.

michelrods
22 May 2008, 6:31 AM
Hi dangreenfield!!!!
It's an awesome enhancement, Thx!!!

I have a small question

dangreenfield
22 May 2008, 1:17 PM
There's been some discussion on the forum regarding multiple rows on toolbars but, to date, no-one appears to have done it successfully. It requires a big change to the way the toolbar works, as well as the background images, etc. Some solutions have been proposed that add multiple toolbars, but this may not be what you need and may be overkill in terms of code.

Feel free to be the first to come up with a working solution! ;)

calverte
28 May 2008, 6:25 AM
Hi dangreenfield!!!!

First off thanks for all the blood sweat and tears that you put into this. Quick question for you. One of the many things that I have found the native Ext JS HtmlEditor not to have is the ability to access key events -- i.e. onkeypress, onkeyup, etc. Are there any plans to add this capability to this plugin?

dangreenfield
28 May 2008, 11:57 AM
One of the many things that I have found the native Ext JS HtmlEditor not to have is the ability to access key events -- i.e. onkeypress, onkeyup, etc. Are there any plans to add this capability to this plugin?

The original Ext.form.HtmlEditor class handles these in the initEditor function...


Ext.EventManager.on(this.doc, {
'mousedown': this.onEditorEvent,
'dblclick': this.onEditorEvent,
'click': this.onEditorEvent,
'keyup': this.onEditorEvent,
buffer:100,
scope: this
});
if(Ext.isGecko){
Ext.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
}
if(Ext.isIE || Ext.isSafari || Ext.isOpera){
Ext.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
}

You can override this method if need be, but it may not be the best place to do it. It depends on what you're trying to capture and where. If you look at my Undo/Redo plugin, I capture the keyup event to record any changes...


// record changed data when in source edit mode
editor.el.on('keyup', this.record, this);

Have a good think about what you want capture and where you want to capture it.

calverte
29 May 2008, 1:17 PM
Dangreenfield,

Thanks for the reply.


Have a good think about what you want capture and where you want to capture it.

I want to perform an update/insert after a specified time frame has passed to capture the data that has been input by a user in conjunction with a keyup/keydown event.

An example of this is the way that yahoo and google webmail work, as well as the "Customizing the Look and Feel (http://extjs.com/deploy/dev/examples/statusbar/statusbar-demo.html)" Status Bar example.

I had create the following function in conjunction with the htmlEditor's push event which works but not the way the interaction is intended to:


function(e) {

// Start a simple clock task that updates a div once every 20 seconds
var task = {
run: function(){
Ext.fly('draft_saved').update(new Date().format('g:i:s A'));
},
interval: 20000 //1 second
}
var runner = new Ext.util.TaskRunner();
runner.start(task);
console.log('started');
}

Using the push event will not allow me to 'stop' the 'started' task once the update/insert is complete. Basically once a keyup event has been captured and the specified time has passed the update task should execute. Once the update task is complete it should be stopped until the keyup event is capture again, etc...

Knowing all of this, where might you suggest that implement my override to capture this event?


Thanks,

~Calvert

dangreenfield
29 May 2008, 3:34 PM
If this is something that you intend to include in every app then I would put it in the HTMLEditor extension and override the initEditor function.

If it's just an option, then I would create a plugin and add it to the HTMLEditor. Look at the Ext.ux.HTMLEditorUndoRedo (http://extjs.com/forum/showthread.php?t=19754) code as an example. From here, you can then manipulate the HTMLEditor to fit your requirements.

Good luck!

george.antoniadis
2 Jul 2008, 1:05 AM
There seem to be some changes in the way the buttons are created in Ext 3 (and probably 2.1+??).

The new buttons have a new markup and the fileuploadbutton.js dies.

This is the new markup...


<div style="position: absolute; right: 0pt;" id="ext-gen1071">
<table cellspacing="0" class="x-btn x-btn-text-icon" id="add">
<tbody class="x-btn-small x-btn-icon-small-left">
<tr>
<td class="x-btn-tl">
<i> </i>
</td>
<td class="x-btn-tc"/>
<td class="x-btn-tr">
<i> </i>
</td>
</tr>
<tr>
<td class="x-btn-ml">
<i> </i>
</td>
<td class="x-btn-mc">
<em unselectable="on" class="">
<button type="button" class="x-btn-text add-image" id="ext-gen1065">Upload</button>
</em>
</td>
<td class="x-btn-mr">
<i> </i>
</td>
</tr>
<tr>
<td class="x-btn-bl">
<i> </i>
</td>
<td class="x-btn-bc"/>
<td class="x-btn-br">
<i> </i>
</td>
</tr>
</tbody>
</table>
</div>
From what I can figure out this should be changed for the uploadbutton to play nice in latest svn builds.

File: fileuploadbutton.js
Lines: 24-27

From:


var width = btnCt.child("td.x-btn-left").getWidth() +
btnCt.child("button.x-btn-text").getWidth() +
btnCt.child("td.x-btn-center").getPadding("lr") +
btnCt.child("td.x-btn-right").getWidth();
To:


var width = btnCt.child("td.x-btn-tc").getWidth() +
btnCt.child("button.x-btn-text").getWidth() +
btnCt.child("td.x-btn-tc").getPadding("lr") +
btnCt.child("td.x-btn-tr").getWidth();
There also seems to be a problem with the loadingindicator but I have removed it for now, I'll check it later and repost :)

Thanks for an AWESOME plugin!!!!

stever
2 Jul 2008, 1:19 PM
There seem to be some changes in the way the buttons are created in Ext 3 (and probably 2.1+??).

It is Ext 3, FYI. Buttons went through big changes to be able to do all the different sizes.

dangreenfield
2 Jul 2008, 2:28 PM
Thanks George. I don't seem to have enough time these days to work on this, so please feel free to take over the reigns to produce a working Ext 3 version. Cheers!! =D>

george.antoniadis
3 Jul 2008, 12:57 AM
Dan I will need to extend the htmleditor to hell and beyond for a project I'm working on so the ux-s you've wrote will be of great value :)

I'm currently changing the upload button you've included with the htmleditorimage to play nice with jack's file-upload form control.

Would you be interested in creating an SVN repo for html editor plugins and uxs?

dangreenfield
3 Jul 2008, 1:09 AM
Hi George. I'm not going to be free to work on anything EXT related for some time. Feel free to take the reigns entirely if you want. My code is your code. When I come back, I'll probably just work on something else. Thanks and good luck! :)

stever
3 Jul 2008, 11:07 AM
Would you be interested in creating an SVN repo for html editor plugins and uxs?

I was thinking of something like that. The HtmlEditor definitely needs a rework, and I was thinking of using this thread as a start. There are some things that need working on for Ext3 in particular.

My idea of a starting point would be to merge these classes with the original rather than overriding, and focusing on a more extensible HtmlEditor where most of the actual features come from plugins.

And if it is OK with everyone, submit it back to Ext for inclusion as part of Ext3. From a licensing point of view, I think we would need the approval of at least Jack/Ext and dangreenfield, then setup a google code project. Any approvals or disapprovals?

dangreenfield
3 Jul 2008, 3:44 PM
You have my full approval! :)

george.antoniadis
3 Jul 2008, 10:06 PM
Approved! :)

rojaro
2 Sep 2008, 6:33 AM
Any progress here?

jelt
2 Sep 2008, 6:38 AM
live demo is dead

stever
2 Sep 2008, 8:42 AM
It's a top priority for me, but I have certain deadlines in the next week, after which I'm going to re-weigh my todo list. More news after that...

rojaro
15 Sep 2008, 5:32 AM
Any news?

michelrods
15 Sep 2008, 12:17 PM
Hi guys,
there's a long time since the last time i wrote a post in this thread :)

Last time i wrote was to ask how to implement two icon bars in the editor


well, i am attaching a zip file with an extended editor. (:-/ just extract the folder called"htmleditor" into examples folder)
maybe it is not the best and cleanest implementation, but it works and coz a matter of time i couldn't finish, but maybe in the forum somebody could wind up this implementation.


in this implementation you will find some "new features" like:
2 icon bars
image menu
link menu (re designed)
table menu
html menu
(most of features work in IE and FireFox, it should be great if somebody could fix the code to make everything work in both :-?)


hope this effort could help some body.
I just would like to ask 1 thing:
plz, if you make any improvement to the code plz let us know to the extJS community so everybody could use it again (the same thing i am doing with my code)


Best regards to all people who have worked on this editor, this is a very nice piece of code =D>


Have a great day,
Michel ;)

hoggy
16 Sep 2008, 2:57 AM
Michel - add this to the <style> section of index.html to make buttons on 2nd row appear:


.x-html-editor-tb .x-btn-text {
background-image:url(tb-spriteBKK.gif)
}






Hi guys,
there's a long time since the last time i wrote a post in this thread :)

Last time i wrote was to ask how to implement two icon bars in the editor


well, i am attaching a zip file with an extended editor. (:-/ just extract the folder called"htmleditor" into examples folder)
maybe it is not the best and cleanest implementation, but it works and coz a matter of time i couldn't finish, but maybe in the forum somebody could wind up this implementation.


in this implementation you will find some "new features" like:
2 icon bars
image menu
link menu (re designed)
table menu
html menu
(most of features work in IE and FireFox, it should be great if somebody could fix the code to make everything work in both :-?)


hope this effort could help some body.
I just would like to ask 1 thing:
plz, if you make any improvement to the code plz let us know to the extJS community so everybody could use it again (the same thing i am doing with my code)


Best regards to all people who have worked on this editor, this is a very nice piece of code =D>


Have a great day,
Michel ;)

michelrods
16 Sep 2008, 12:34 PM
Thx so much hoggy =D>

i am attaching the new index.html, just replace the old one for this....
hope you like it!!!

Michel :D

ccvcd
7 Oct 2008, 10:37 PM
where is this demo ?

ccvcd
7 Oct 2008, 11:01 PM
Hi guys,

there's a long time since the last time i wrote a post in this thread :)


Last time i wrote was to ask how to implement two icon bars in the editor



well, i am attaching a zip file with an extended editor. (:-/ just extract the folder called"htmleditor" into examples folder)
maybe it is not the best and cleanest implementation, but it works and coz a matter of time i couldn't finish, but maybe in the forum somebody could wind up this implementation.



in this implementation you will find some "new features" like:
2 icon bars
image menu
link menu (re designed)
table menu
html menu
(most of features work in IE and FireFox, it should be great if somebody could fix the code to make everything work in both :-?)



hope this effort could help some body.
I just would like to ask 1 thing:
plz, if you make any improvement to the code plz let us know to the extJS community so everybody could use it again (the same thing i am doing with my code)



Best regards to all people who have worked on this editor, this is a very nice piece of code =D>



Have a great day,
Michel ;)

when i run this demo ,it error:
tip is undefined

tip.register.apply(tip, arguments);

why?
thanks

EiWeB
13 Oct 2008, 5:16 AM
hi ccvcd (http://extjs.com/forum/member.php?u=39610)

I am running michelrods' example with ext-2.1 and it works pretty good!
AWESOME work mich!!!! =D>

EiWeB
13 Oct 2008, 5:28 AM
ccvcv:

you can find the attachments at:

http://extjs.com/forum/attachment.php?attachmentid=9423&d=1221509676
this is the complete example, as i mentioned i am running it with ext 2.1 and works pretty good

http://extjs.com/forum/attachment.php?attachmentid=9450&d=1221597251
this is the latest index file, it includes an style in order to show the second bar icons

hope this can help you!

markkl
21 Oct 2008, 5:27 AM
when i run this demo ,it error:
tip is undefined

tip.register.apply(tip, arguments);

why?
thanks

I am having the same prob is it becouse i am using 2.2 ?

vladok
9 Nov 2008, 1:58 PM
Hi, I have a problem with Insert window focus. It appears under the main window:


var win2 = new Ext.Window({
id: 'news-edit',
title: 'Adding News',
width: 900,
height:500,
minWidth: 300,
minHeight: 250,
pageX: 500,
pageY:500,
modal: 'false',
layout: 'fit',
autoScroll:true,
plain: 'false',
bodyStyle:'padding:5px;',
buttonAlign:'center',
autoLoad: {
url: '<?=$this->basePath?>/adminajax/getnodedata',
params: {
url: 'admin/incs/news_edit',
passParams : Ext.encode({
loadData: ''
}),
callaction: 'getNewsData'
},
scripts:true
}
});
win2.show();
}

Here is the window I want to appear on top:


win = new Ext.Window({
title: 'Insert/Edit Image',
closable: true,
//modal: true,
closeAction: 'hide',
width: 400,
height: 350,
plain: true,
layout: 'fit',
border: false,
items: tabs,
buttons: [{
text: 'Insert',
id: 'insert-btn',
disabled: true,
handler: function() {
win.hide();
insertImage();
}
}, {
text: 'Close',
handler: function() {
win.hide();
}
}],
listeners: {
'show': function() {
tabs.form.reset();
var element = getSelectedImage();
if (element) {

// still working on this!!!
// need to fix image source as it is changed
// from a relative url to an absolute url
tabs.form.findField('src').setValue(element.src);
tabs.form.findField('alt').setValue(element.alt);
tabs.form.findField('width').setValue(element.style.width);
tabs.form.findField('height').setValue(element.style.height);
tabs.form.findField('constrain').setValue(true);
}
}
}
}

sksoft
29 Dec 2008, 6:37 AM
Does anybody fixed exception in Ext.QuickTips.register() on adding tool in Ext.ux.HTMLEditorToolbar.renderTool (line 66)?

goodboy
30 Dec 2008, 1:02 PM
tip is undefined

jasondeegan
7 Jan 2009, 10:37 AM
I was trying to extend the example provided but could not get my code to fire on a keyup event:



var editor = new Ext.ux.HTMLEditor({
width: 650,
height: 300,
styles: ['htmleditor.css'],
enableKeyEvents: true,
el: 'htmleditor'
});
editor.on('keyup', function(){
Ext.MessageBox.alert('SUBJECT', 'BODY', e);
});
editor.render();

bradyisom
15 Jan 2009, 8:53 AM
Does anybody fixed exception in Ext.QuickTips.register() on adding tool in Ext.ux.HTMLEditorToolbar.renderTool (line 66)?

Open up editorCreation.js and wrap the contents in Ext.onReady. For example:


Ext.onReady(function() {

Ext.QuickTips.init();

. . .

editor.render();
editor.addListener('contextmenu',myHandler);
});

smokeman
29 Jan 2009, 12:42 PM
I use google chrome quite a bit nowdays, and there are some problems with this extension and chrome. Anyone looked at that?

I'm using the original code from post 1.

smokeman
30 Jan 2009, 9:58 AM
I was able to modify dangreenfield's editorstyles plugin, and use it to insert data tags(which I use for templating an e-mail)
I will then do string replacing to insert database values.

// Ext.ux.HTMLEditorDataTags
// a plugin that add the styles list to the Ext.ux.HtmlEditor toolbar
Ext.ux.HTMLEditorDataTags = function(s) {

// PRIVATE

// pointer to Ext.ux.HTMLEditor
var editor;

// styles list
var tags = s;

// ensure styles is an array
if (! tags instanceof Array) {
tags = [tags];
}

// editor.relayCmd('inserthtml', html);

// include the No Style option
//styles = [{text: "No Style", value: "none"}].concat(styles);

// update the styles combobox (runs when editorevent occurs in the editor)
var updateCombo = function() {
var element = Ext.isIE ? editor.doc.selection.createRange().parentElement() :
editor.win.getSelection().anchorNode;
var parent = getParentStyleElement(element);
var tag = parent ? parent.className : "none";
if (editor.tb.items.map.tag.el.value != tag) {
editor.tb.items.map.tag.el.value = tag;
}
}
var insertTag = function(event, el){
var tag = el.value;
if(! Ext.isIE){
editor.relayCmd('inserthtml', tag);
}
else{
editor.relayCmd('Paste',tag);
}
}


// PUBLIC

return {

// Ext.ux.HTMLEditorStyles.init
// called upon instantiation
init: function(htmlEditor) {
editor = htmlEditor;

// doesn't work for safari so exclude


// add the styles combo and spacer to the toolbar.
// insert before the fontname combo
editor.tb.insertToolsBefore('fontname', [{
itemId: 'tags',
xtype: 'tbcombo',
cls: 'x-font-select',
opts: tags,
handler: insertTag,
scope: this
}, ' ']);

// add listener to editorevent when editor is rendered
editor.on('render', function() {

// if editorevent occurs then update the styles combobox
editor.on("editorevent", updateCombo, this);
}, this);

}
}
}


and paramaters passed like this:

xtype: 'HTMLEditor',
fieldLabel: 'Template Text',
name: 'lastChange',
height:200,
anchor:'98%',
toolbarItemExcludes: ['fonts','allformats'],
plugins: [
new Ext.ux.HTMLEditorDataTags([
{text: 'Contact Name', value: '[ContactName]'},
{text: 'Username', value: '[Username]'},
{text: 'Password', value: '[Password]'},
{text: 'Bank Name', value: '[BankName]'},
{text: 'Site Url', value: '[URL]'},
{text: 'Confirmation Req. ID', value: '[ReqID]'},
{text: 'DirectURL', value: '[DirectURL]'},
{text: 'Requesting Auditor', value: '[From]'}
])

razvanpandor
11 Feb 2009, 8:18 AM
hello,
I'm trying to add a "paste" button on a htmleditor- that opens a new window with a textarea where people "paste" text containing HTML tags and when submited the text is added to the htmleditor without tags; I started of the "add image " plugin and got it workng. What i don't quite get is : how do I adapt the code from index.html


Ext.onReady(function() {
Ext.QuickTips.init();
var editor = new Ext.ux.HTMLEditor({
width: 650,
height: 300,

plugins: new Ext.ux.HTMLEditorPaste(),
el: 'htmleditor'
});
editor.render();


to something like this:


items:
[{
xtype: "htmleditorpaste",
id:"abc",
plugins: new Ext.ux.HTMLEditorPaste()
}]


I registered Ext.ux.HTMLEditor as "htmleditorpaste" and there's the plugin that opens the paste window. What seems to be wrong is that the html field is beeing rendered but not entirely.
bye, Raz

vince
10 Mar 2009, 2:05 AM
Is there a solution to edit link when you hightlight it ?
Thank you for your answer

IT100
19 Jul 2009, 5:58 AM
Has someone made it possible for Ext.ux.HTMLEditor to hide the toolbar and set the edit area to readonly?
this would be nice so a administration version can be switched to a userversion without worrying about layout, css and other issues

regards,
C
.

IT100
20 Jul 2009, 5:24 AM
Managed to hide the toolbar:




toolbarItemExcludes: [
'fonts',
'allformats',
'allfontsizes',
'allcolors',
'allalignments',
'alllinks',
'alllists',
'sourceedit'
]



found the part where textarea is replaced(x-hidden) by iframe, but was not yet successful in setting it read-only

Anyone?
Regards,
Chris.

Joyce
24 Sep 2009, 1:54 PM
I am having issues getting this to work with Ext 3.0. Anyone got it to work?