SignalR with Ext.NET

I’ve just published a blog post over at Ext.NET about using SignalR with Ext.NET

What is SignalR? Checkout this great video from Scott Hanselman:

In the video there is an example there of a stock ticker built with SignalR. As that project is on GitHub, I forked it to use the same data but update an Ext.NET GridPanel instead.

So the table in this screenshot

Original stock ticker example using regular HTML tables

… becomes this:

Original stock ticker example converted to using an Ext.NET GridPanel

Look at the forked project on GitHub.

Read more details about this on the blog post I wrote over at Ext.NET

Ext JS TabPanel plugin for draggable tabs

Ext JS 4 comes with a plugin for TabPanels to allow reordering tabs using drag and drop. There isn’t an equivalent for Ext JS 3, however.

I came across a useful extension of the Ext JS 3.x TabPanel to re-order tabs via drag and drop. It is implemented as a subclass of Ext.TabPanel, as Ext.ux.panel.DDTabPanel and a version of it includes a useful reorder event that is fired once a tab is dragged to a new position in the TabPanel.

I refactored it from a subclass of Ext.TabPanel to a plugin so the original functionality is unchanged.

Demo

Here’s a simple screenshot of it in action

Tab 1 is being tragged in between Tab 2 and Tab 3

Or try out the demo. (View the demo source code to grab the code, or continue reading.)

Subclass or Plugin?

The original is a subclass of Ext.TabPanel. So to use for any of your existing subclasses of TabPanels, you have to change them to inherit this one.

However, I see this more as a feature of an Ext.TabPanel so it might be better provided as a plugin, rather than a subclass. Being a plugin means it could be reused in combination with various other plugins and potentially any manner of TabPanel subclasses.

For example, there may be other features you add to the TabPanel (via plugins) such as being able to rename a tab by double-clicking it (like Excel), or inserting an add tab button as the last tab, etc. These could all be plugins so you can choose which one(s) you want for your needs. But if they were all subclasses, trying to use a particular combination would mean you would need a particular subclass to represent the permutations. If you wanted another combination, you’d need another subclass, and so on. As a plugin, you can just pick and choose which other plugins you want as well.

So I attempted to refactor it as a plugin (without changing functionality).

The Code

The code involves 3 parts:

  1. The original subclass refactored into a plugin
  2. The accompanying DropTarget (only the constructor was changed)
  3. Example CSS to show the drop arrow (the blue one in the screenshot)

Original subclass refactored into a plugin

This is where the bulk of the changes are, but the functionality remains as is.

Ext.namespace('Ext.ux.panel');

/**
 * @class Ext.ux.panel.DDTabPanel
 * @author
 *     Original by
 *         <a href="http://extjs.com/forum/member.php?u=22731">thommy</a> and
 *         <a href="http://extjs.com/forum/member.php?u=37284">rizjoj</a><br />
 *     Published and polished by: Mattias Buelens (<a href="http://extjs.com/forum/member.php?u=41421">Matti</a>)<br />
 *     With help from: <a href="http://extjs.com/forum/member.php?u=1459">mystix</a>
 *     Polished and debugged by: Tobias Uhlig (info@internetsachen.com) 04-25-2009
 *     Ported to Ext-3.1.1 by: Tobias Uhlig (info@internetsachen.com) 02-14-2010
 *     Updated by <a href="http://www.sencha.com/forum/member.php?56442-brombs">brombs</a>
 *     to include reorder event
 *     Modified by <a href="http://www.onenaught.com">Anup Shah</a> to work as a plugin
 *     instead of subclass of TabPanel
 * @license Licensed under the terms of the Open Source <a href="http://www.gnu.org/licenses/lgpl.html">LGPL 3.0 license</a>.
 * Commercial use is permitted to the extent that the code/component(s) do NOT
 * become part of another Open Source or Commercially licensed development library
 * or toolkit without explicit permission.
 * @version 2.0.1 (Jan 11, 2013)
 */
Ext.ux.panel.DraggableTabs = Ext.extend(Object, {
	constructor: function (config) {
		if (config) {
			Ext.apply(this, config);
		}
	},
		
	init: function(tp) {
		if ((tp instanceof Ext.TabPanel) === false)
			return;

		// make these available onto the TabPanel as per original plugin, where used externally
		tp.arrowOffsetX = this.arrowOffsetX;
		tp.arrowOffsetY = this.arrowOffsetY;

		tp.addEvents('reorder');
			
		// TODO: check if ddGroupId can be left as a property of this plugin rather than on the TabPanel
		if (!tp.ddGroupId) {
			tp.ddGroupId = 'dd-tabpanel-group-' + tp.getId();
		}
			
		// New Event fired after drop tab. Is there a cleaner way to do this?
		tp.reorder = this.reorder;
		tp.oldinitTab = tp.initTab;
		tp.initTab = this.initTab;
		tp.onRemove = this.onRemove;

		tp.on('afterrender', this.afterRender, this);

		this.tabPanel = tp;
	},

	destroy: function () {
		tp.un('afterrender', this.afterRender, this);
		delete this.tabPanel;
		Ext.destroy(this.dd, this.arrow);
	},

	/**
	* @cfg {Number} arrowOffsetX The horizontal offset for the drop arrow indicator, in pixels (defaults to -9).
	*/
	arrowOffsetX: -9,
	/**
	* @cfg {Number} arrowOffsetY The vertical offset for the drop arrow indicator, in pixels (defaults to -8).
	*/
	arrowOffsetY: -8,

	reorder: function(tab) {
		this.fireEvent('reorder', this, tab);
	},
			
	// Declare the tab panel as a drop target
	/** @private */
	afterRender: function () {
		// Create a drop arrow indicator
		this.tabPanel.arrow = Ext.DomHelper.append(
			Ext.getBody(),
			'<div class="dd-arrow-down"></div>',
			true
		);
		this.tabPanel.arrow.hide();
		// Create a drop target for this tab panel
		var tabsDDGroup = this.tabPanel.ddGroupId;
		this.dd = new Ext.ux.panel.DraggableTabs.DropTarget(this, {
			ddGroup: tabsDDGroup
		});

		// needed for the onRemove-Listener
		this.move = false;
	},

	// Init the drag source after (!) rendering the tab
	/** @private */
	initTab: function (tab, index) {
		this.oldinitTab(tab, index);
			
		var id = this.id + '__' + tab.id;
		// Hotfix 3.2.0
		Ext.fly(id).on('click', function () { tab.ownerCt.setActiveTab(tab.id); });
		// Enable dragging on all tabs by default
		Ext.applyIf(tab, { allowDrag: true });

		// Extend the tab
		Ext.apply(tab, {
			// Make this tab a drag source
			ds: new Ext.dd.DragSource(id, {
				ddGroup: this.ddGroupId
				, dropEl: tab
				, dropElHeader: Ext.get(id, true)
				, scroll: false

				// Update the drag proxy ghost element
				, onStartDrag: function () {
					if (this.dropEl.iconCls) {

						var el = this.getProxy().getGhost().select(".x-tab-strip-text");
						el.addClass('x-panel-inline-icon');

						var proxyText = el.elements[0].innerHTML;
						proxyText = Ext.util.Format.stripTags(proxyText);
						el.elements[0].innerHTML = proxyText;

						el.applyStyles({
							paddingLeft: "20px"
						});
					}
				}

				// Activate this tab on mouse up
				// (Fixes bug which prevents a tab from being activated by clicking it)
				, onMouseUp: function (event) {
					if (this.dropEl.ownerCt.move) {
						if (!this.dropEl.disabled && this.dropEl.ownerCt.activeTab == null) {
							this.dropEl.ownerCt.setActiveTab(this.dropEl);
						}
						this.dropEl.ownerCt.move = false;
						return;
					}
					if (!this.dropEl.isVisible() && !this.dropEl.disabled) {
						this.dropEl.show();
					}
				}
			})
			// Method to enable dragging
			, enableTabDrag: function () {
				this.allowDrag = true;
				return this.ds.unlock();
			}
			// Method to disable dragging
			, disableTabDrag: function () {
				this.allowDrag = false;
				return this.ds.lock();
			}
		});

		// Initial dragging state
		if (tab.allowDrag) {
			tab.enableTabDrag();
		} else {
			tab.disableTabDrag();
		}
	}

	/** @private */
	, onRemove: function (c) {
		var te = Ext.get(c.tabEl);
		// check if the tabEl exists, it won't if the tab isn't rendered
		if (te) {
			// DragSource cleanup on removed tabs
			//Ext.destroy(c.ds.proxy, c.ds);
			te.select('a').removeAllListeners();
			Ext.destroy(te);
		}

		// ignore the remove-function of the TabPanel
		Ext.TabPanel.superclass.onRemove.call(this, c);

		this.stack.remove(c);
		delete c.tabEl;
		c.un('disable', this.onItemDisabled, this);
		c.un('enable', this.onItemEnabled, this);
		c.un('titlechange', this.onItemTitleChanged, this);
		c.un('iconchange', this.onItemIconChanged, this);
		c.un('beforeshow', this.onBeforeShowItem, this);

		// if this.move, the active tab stays the active one
		if (c == this.activeTab) {
			if (!this.move) {
				var next = this.stack.next();
				if (next) {
					this.setActiveTab(next);
				} else if (this.items.getCount() > 0) {
					this.setActiveTab(0);
				} else {
					this.activeTab = null;
				}
			}
			else {
				this.activeTab = null;
			}
		}
		if (!this.destroying) {
			this.delegateUpdates();
		}
	}
});

Ext.preg('draggabletabs', Ext.ux.panel.DraggableTabs);

The accompanying DropTarget to implement the drop behaviour

The only change from the original is the constructor:

// Ext.ux.panel.DraggableTabs.DropTarget
// Implements the drop behavior of the tab panel
/** @private */
Ext.ux.panel.DraggableTabs.DropTarget = Ext.extend(Ext.dd.DropTarget, {
	constructor: function (dd, config) {
		this.tabpanel = dd.tabPanel;
		// The drop target is the tab strip wrap
		Ext.ux.panel.DraggableTabs.DropTarget.superclass.constructor.call(this, this.tabpanel.stripWrap, config);
	}

	, notifyOver: function (dd, e, data) {
		var tabs = this.tabpanel.items;
		var last = tabs.length;

		if (!e.within(this.getEl()) || dd.dropEl == this.tabpanel) {
			return 'x-dd-drop-nodrop';
		}

		var larrow = this.tabpanel.arrow;

		// Getting the absolute Y coordinate of the tabpanel
		var tabPanelTop = this.el.getY();

		var left, prevTab, tab;
		var eventPosX = e.getPageX();

		for (var i = 0; i < last; i++) {
			prevTab = tab;
			tab = tabs.itemAt(i);
			// Is this tab target of the drop operation?
			var tabEl = tab.ds.dropElHeader;
			// Getting the absolute X coordinate of the tab
			var tabLeft = tabEl.getX();
			// Get the middle of the tab
			var tabMiddle = tabLeft + tabEl.dom.clientWidth / 2;

			if (eventPosX <= tabMiddle) {
				left = tabLeft;
				break;
			}
		}

		if (typeof left == 'undefined') {
			var lastTab = tabs.itemAt(last - 1);
			if (lastTab == dd.dropEl) return 'x-dd-drop-nodrop';
			var dom = lastTab.ds.dropElHeader.dom;
			left = (new Ext.Element(dom).getX() + dom.clientWidth) + 3;
		}

		else if (tab == dd.dropEl || prevTab == dd.dropEl) {
			this.tabpanel.arrow.hide();
			return 'x-dd-drop-nodrop';
		}

		larrow.setTop(tabPanelTop + this.tabpanel.arrowOffsetY).setLeft(left + this.tabpanel.arrowOffsetX).show();

		return 'x-dd-drop-ok';
	}

	, notifyDrop: function (dd, e, data) {
		this.tabpanel.arrow.hide();

		// no parent into child
		if (dd.dropEl == this.tabpanel) {
			return false;
		}
		var tabs = this.tabpanel.items;
		var eventPosX = e.getPageX();

		for (var i = 0; i < tabs.length; i++) {
			var tab = tabs.itemAt(i);
			// Is this tab target of the drop operation?
			var tabEl = tab.ds.dropElHeader;
			// Getting the absolute X coordinate of the tab
			var tabLeft = tabEl.getX();
			// Get the middle of the tab
			var tabMiddle = tabLeft + tabEl.dom.clientWidth / 2;
			if (eventPosX <= tabMiddle) break;
		}

		// do not insert at the same location
		if (tab == dd.dropEl || tabs.itemAt(i - 1) == dd.dropEl) {
			return false;
		}

		dd.proxy.hide();

		// if tab stays in the same tabPanel
		if (dd.dropEl.ownerCt == this.tabpanel) {
			if (i > tabs.indexOf(dd.dropEl)) i--;
		}

		this.tabpanel.move = true;
		var dropEl = dd.dropEl.ownerCt.remove(dd.dropEl, false);

		this.tabpanel.insert(i, dropEl);
		// Event drop
		this.tabpanel.fireEvent('drop', this.tabpanel);
		// Fire event reorder
		this.tabpanel.reorder(tabs.itemAt(i));

		return true;
	}

	, notifyOut: function (dd, e, data) {
		this.tabpanel.arrow.hide();
	}
});

CSS for the drop arrow

This of course can be customized. For example, you may prefer data URLs to inline the image (as the image is likely to be very small), but this illustrates what you’d need:

.dd-arrow-down.dd-arrow-down-invisible {
	display: none;
	visibility: hidden;
}

.dd-arrow-down {
	background-image: url(icons/tab-drop-arrow-down.png);
	display: block;
	visibility: visible;
	z-index: 20000;
	position: absolute;
	width: 16px;
	height: 16px;
	top: 0;
	left: 0;
}

Example usage

As a plugin, you simply add it to the plugins configuration when constructing an instance of an Ext.TabPanel (or a subclass, potentially):

plugins: new Ext.ux.panel.DraggableTabs(),

Here’s an example of how you might use it (including the use of the new reorder event):

var tabPanel = new Ext.TabPanel({
	plugins: new Ext.ux.panel.DraggableTabs(),
	renderTo: document.body,
	activeTab: 0,
	width:300,
	height:150,
	items:[{
		title: 'Tab 1',
		html: "Tab 1 contents"
	},{
		title: 'Tab 2',
		html: "Tab 2 contents"
	},{
		title: 'Tab 3',
		html: "Tab 3 contents"
	}],
	listeners: {
		reorder: {
			fn: function (tabPanel, movedTab) {
				// do something here
			}
		}
	}
});

The above example is simplistic. Drag and drop will work on disabled tabs, tabs that are dynamically added (the demo has an example of this), etc.

Limitations

Any limitations are listed in the original form post (I’ve not changed the functionality – at least not knowingly!)

The main limitation here is that the original class and this refactored plugin is for Ext JS 3.x, not the latest Ext JS 4.x

If anyone’s had the time to create one for Ext JS 4.x please do let me know!
Update: Turns out that for Ext JS 4, there is built in plugin. More further below.

What about Ext.NET 1.x?

This plugin can be used with Ext.NET 1.x. (For Ext.NET 2, which is based on Ext JS 4.*, see below). Assuming the JavaScript has been referenced, here is an example of how it can be used:

<ext:TabPanel runat="server" Height="150" Width="300" TabPosition="Bottom" >
	<Plugins>
		<ext:GenericPlugin runat="server" InstanceName="Ext.ux.panel.DraggableTabs" />
	</Plugins>
	<Items>
		<ext:Panel runat="server" Title="Tab1" Border="false" Closable="true" Html="Tab 1 contents" />
		<ext:Panel runat="server" Title="Tab2" Border="false" Closable="true" Html="Tab 2 contents" />
		<ext:Panel runat="server" Title="Tab3" Border="false" Closable="true" Html="Tab 3 contents" />
	</Items>
</ext:TabPanel>

What about Ext JS 4 and Ext.NET 2?

Turns out Ext JS 4 includes Ext.ux.TabReorderer which is also a plugin.

For Ext.NET 2, it is part of the BoxReorderer plugin so you only need this:

<ext:TabPanel runat="server" Height="150" Width="300">
	<Plugins>
		<ext:BoxReorderer />
	</Plugins>
	<Items>
		<ext:Panel runat="server" Title="Tab1" Border="false" Closable="true" Html="Tab 1 contents" />
		<ext:Panel runat="server" Title="Tab2" Border="false" Closable="true" Html="Tab 2 contents" />
		<ext:Panel runat="server" Title="Tab3" Border="false" Closable="true" Html="Tab 3 contents" />
	</Items>
</ext:TabPanel>

Credits

All credits go to the original authors for the useful functionality.

Help

Let me know if you spot any problems or better practices in the conversion to a plugin. If you have improvements to the actual functionality, please post it to the original thread on the Sencha forums.

Ext.NET 2 Released plus Ext.NET Book Discount

Ext.NET 2 released

Ext.NET 2 has just been officially released by Ext.NET. It is a great enhancement over Ext.NET 1. Nice site redesign too :)

Ext.NET is promoting my book

Geoffrey McGill, the founder of Ext.NET also asked me to write a blog post on their site about the book. That post has a bit more about the book, sample screenshots of some pages, and chapter summaries.

The book is also being quite heavily promoted on the Ext.NET site:

Free copy of the new book with any purchase of an Ext.NET license

Discount codes to purchase the book on its own

Finally, if you already have an Ext.NET license or just want to get the book, you can use one of these discount codes when purchasing directly from the publisher:

  • 15% off for Print + free eBook + free PacktLib access to the book: eoprly
  • 20% off for eBooks only: eoprlye

This offer lasts until December 31st, 2012.

More details on my original blog post about the book.

Book: Ext.NET Web Application Development

Ext.NET Web Application Development My book, Ext.NET Web Application Development has just been published by Packt Publishing.

The reviewers were none other than the Ext.NET team itself. They were excited about and dedicated to this book which would not have amounted to much without their involvement. They are also offering free copies with any purchases of Ext.NET!

View larger book cover

Get the book

The book comes in paper and various e-book formats (PDF, ePub, Kindle, etc). There are a number of ways you can get the book:

Discount codes for the book

Use one of these discount codes if purchasing directly from the publisher:

  • 15% off for Print + free eBook + free PacktLib access to the book: eoprly
  • 20% off for eBooks only: eoprlye

(Enter the code in the ‘Enter promotional code’ box on the Packt checkout page.)

The publisher is offering these discounts until 31st December 2012.

What is Ext.NET?

As mentioned on Ext.NET’s web site:

Ext.NET is an open source ASP.NET (WebForm + MVC) component framework integrating the cross-browser Sencha Ext JS JavaScript Library.

Ext.NET also includes components not found in Ext JS, extending various Ext JS classes where needed, thus providing its own JavaScript layer on top of Ext JS.

About the book

Update December 4, 2012: I’ve just published a post on the Ext.NET web site describing the book in further detail, including a summary of all the chapters, sample screenshots, and more. The chapter overviews are below, but see the previous post for further info too.

Chapter overviews

Here’s a summary of what each chapter contains:

Chapter 1, Getting Started with Ext.NET, provides an overview of what Ext.NET is and how it is related to Ext JS and ASP.NET. In addition, this chapter covers how you can obtain and set up your development environment ready for Ext.NET development.

Chapter 2, Ext.NET Controls Overview, introduces various types of controls available in Ext.NET. Using the Button control, we introduce many concepts common throughout the Ext.NET control suite. We also look at how client-side and server-side events can be set up. This chapter also introduces other more common components including Panels, Toolbars, Menus, Windows, and Tooltips. We also get a glimpse of some of the complex UIs that are possible by reusing these components.

Chapter 3, Layout with Ext.NET, covers the numerous layout options available in Ext.NET to help you organize your web applications. Topics covered include the Viewport, and specific layouts such as Border, Accordion, Fit, HBox, VBox, and more.

Chapter 4, AJAX with Ext.NET, looks at the powerful AJAX options Ext.NET supports. We cover the powerful DirectEvents and DirectMethods features, as well as AJAX options specific to certain controls. This is a powerful chapter that lays the foundation for slick and usable applications that are responsive to user interactions.

Chapter 5, Working with Data, looks at the powerful data handling techniques available in Ext.NET. We cover XTemplates, which allows you to define HTML templates to bind data to, and we explain the Stores, Models, and Proxies architecture that allows for powerful data-binding reuse across many Ext.NET components. The ComboBox and DataView are introduced as examples of controls that reuse this architecture.

Chapter 6, Introducing GridPanels, covers the popular and highly sophisticated GridPanel control. It is another control that reuses the Stores, Models, and Proxies architecture, but is given its own chapter. We look at various features of the GridPanel such as paging, filtering, sorting, grouping, column summaries, row expanding, and selection models. We also look at how grid editing can be enabled, including in-line grid editing at the row or cell level. As large as this chapter is, there are many other GridPanel capabilities that we have not been able to fit into this book, so many links to further examples and resources are also provided.

Chapter 7, Forms and Validation, looks at the numerous form controls available in Ext. NET, how to lay them out, and how to enable client and remote validation. We also look at how custom validators can be created. Lastly, we also see how Ext.NET’s data-binding capabilities can also be reused with forms.

Chapter 8, Trees and Tabs with Ext.NET, introduces the popular TreePanel and TabPanel controls. Due to limited space in the book, we cannot cover all the sophisticated possibilities that these controls offer, but we do provide an overview of how tree nodes can be loaded asynchronously and how to reuse the Store, Models, and Proxies architecture to bind data to trees. We also look at various ways TabPanels can be configured, including how to load content on-demand using various AJAX techniques supported by Ext.NET.

Chapter 9, Extending Ext.NET Controls – Custom Controls and Plugins, is perhaps the most powerful chapter in this book. It shows you how to extend Ext.NET controls in a variety of ways to support both ASP.NET Web Forms and ASP.NET MVC Razor templates, enabling you to create highly reusable components. Most of the chapter looks at how controls can be extended, but we also look at how you can use the available plugin mechanisms to reuse functionality across different types of components.

Chapter 10, Troubleshooting and Debugging, looks at how to debug your Ext.NET applications. In particular, we look at how to enable debug versions of Ext.NET and Ext JS JavaScript and what tools to use for cross-browser troubleshooting. This chapter also provides important tips on how to request help in the Ext.NET forums in a way that will increase your chances of receiving a quick response.

My personal favorite is chapter 9. Not only did I learn a lot myself when putting it together, I think it is also one of the most important; I wish I knew this about 3 or 4 years ago as it would have helped make my own apps even more reusable. For me it is worth getting the book for this chapter alone!

Samples photos

Update: December 15, 2012: A few days ago I received my author’s copy (a number of days after a work colleague received his, even though mine was sent a lot earlier to me!)

The book on my desk!

The book on my desk!

Sample page from Chapter 5, Working with Data

Sample page from Chapter 5, Working with Data

Sample page from Chapter 6, Introducing GridPanels

Sample page from Chapter 6, Introducing GridPanels

How I ended up writing a book on Ext.NET

I had been using ASP.NET for many years before coming across Ext.NET (when it was known as Coolite). I had been using ASP.NET for some high-profile, public-facing, web sites where the main challenges were CSS-based designs, W3C accessibility, web standards, and using JavaScript to progressively enhance e-commerce web sites.

Some of the frustrations with ASP.NET Web Forms in many of these areas in those days was overcome by using XSLT to generate the markup instead of ASP.NET Controls. (ASP.NET MVC had not been released at this time, which would have been a great alternative to the XSLT-based approach, which I still like very much!)

Shifting focus to highly interactive applications

As I shifted focus to web-based business applications, the technologies to use were the same, but the emphasis was very different: complex interactions (desktop-like), different user types, being able to mandate JavaScript, more support for IE6 (thanks to corporate environments being slow to uptake newer browsers), etc.

Ext.NET enables complex interaction design

From a usability point of view, as Alan Cooper explains in his excellent book About Face 3: The Essentials of Interaction Design, business applications may have beginner or novice users, but those users have incentives to become intermediate or advanced users quite quickly, so they can get their job done. The intermediate user should therefore be the prime target of such apps. This means that more sophisticated UI capabilities are needed with a far greater emphasis on JavaScript and AJAX interactions rather than traditional ASP.NET Post Backs.

Ext.NET and Ext JS cover this really well and saves a lot of time trying to create a sophisticated widget framework letting you concentrate on improving your application with highly reusable components. With Ext.NET’s important enhancements and integration with .NET it is a compelling choice for these types of applications.

Writing about Ext.NET

So around January 2012, when Packt asked me if I wanted to write a book on Ext.NET, despite having no spare time, it was hard to resist! I started around March or April, and had it not been for some some major personal interruptions in between, it might have finished around August. But the release times nicely with the Ext.NET 2.1 release.

It has been a great learning experience for me and I would have certainly liked to know some of the things mentioned in there when I first started out learning Ext.NET!

Blog restart

Apart from a handful of posts in 2010 and 2009 I haven’t really blogged much since 2008. Unfortunately it has been a bad combination of a number of personal tragedies, being too busy at work, and lack of spare time.

I am hoping I am through the worst of it now, and might see if I can restart this blog. I don’t expect to post frequently, but it may have a subtle change of focus. I’ve reset the theme, as the old one was a bit out of date. I’ve just used one of WordPress’s default themes, plus a few tweaks. I may do a proper theme at some point when I get some spare time.

Around 2007 or 2008 I started to focus a lot more on complex business web-application; really complex forms (200-300 fields for example), complex desktop-like interactions and so on.

I’ve shifted slightly from jquery/XSLT etc to heavy JavaScript/Ext.NET/Ext JS/AJAX etc. I still really like jQuery and XSLT and continue to use it, but as my focus has shifted to complex business apps, I am quite excited about Ext.NET, interaction design for complex desktop-like apps, and related technologies.

In fact, about a year ago I was asked to write a book about Ext.NET, which I started a few months after that… A follow-up post will talk about that a bit more, shortly…!

Firefox 4 change: input type image only submits x and y, not name when clicked. Keyboard as before

(This post is partly a note to self, and a place to further describe the Firefox bug I raised.)

Update: “not a bug, it’s a feature”! See below for more, but the gist of it is that Firefox’s behaviour here is right according to the HTML 5 specs, but not according to the HTML 4.01 specs…

The problem change

Firefox 4.0 beta — as well as IE and Opera — do not send name/value for input type=”image”; only .x and .y coordinates are sent.

Note, this is when clicking the input image with a mouse. When navigating using keyboard, focusing on it, and pressing enter, then the name does get submitted.

Earlier Firefox, Chrome (latest) do send name/value, which is what we’d expect…

Example

This form submits using a GET so you can see the submitted name/value pairs in the querystring:

Click this:

What does W3C spec say?

17.4.1 Control types created with INPUT says this for input type image:

When a pointing device is used to click on the image, the form is submitted and the click coordinates passed to the server. The x value is measured in pixels from the left of the image, and the y value in pixels from the top of the image. The submitted data includes name.x=x-value and name.y=y-value where “name” is the value of the name attribute, and x-value and y-value are the x and y coordinate values, respectively.

The previous quote implies name=value should not be sent for input type=image

But 17.4 The Input Element says this as part of the input name attribute formal definition:

 name        CDATA          #IMPLIED  -- submit as part of form --

That implies the name should be submitted too, even for input type=image (and assuming that, then the value of the input should be submitted too, which seems to be confirmed by section 17.13.2 Successful controls in the spec, which notes: A successful control is “valid” for submission. Every successful control has its control name paired with its current value as part of the submitted form data set.)

So, I think Firefox 4 has introduced a bug…?

Update: not a bug

The bug I raised has been updated and turns out that this is per HTML 5 spec, while I was comparing what I saw with the implementations in earlier versions of Firefox and with the HTML 4.01 spec…

IE (and Opera) also only send .x and .y, so I guess they settled on that given most sites are probably still catered for/assume IE as the predominant browser…

Workarounds

One possible workaround is this, but ugly:

  1. Detect the browsers to do this for (feature detection would be better…)
  2. Capture the form submit event and find the button that caused this, or capture the button click event
  3. Append a hidden input like this (jQuery example, assuming button is the button you have captured):

    $(form).append('<input type="hidden" name="' + button.name + '" value="' + button.name + '" />').submit();

If you’ve already been working around this for IE, as the problem has been there for ages, then apply this for firefox 4 too (annoying and ugly to do browser specific code, I know)

Third workaround: don’t use input type=image, use input type=submit + CSS, or button etc.

XSLT Performance tip: don’t indent output

Summary: turn off xsl output indenting

When transforming XML via XSLT, make sure the output setting for indenting is turned off and honoured by your code.

Turning it off will

  • Speed up the transform time
  • Reduce the output size

How is XSLT output indent turned off?

Indenting output should be off by default. If for whatever reason it is not, it can be turned off simply by using this:

<xsl:output indent="no" />

I have seen a lot of .NET XSLT ignore this important setting. Sometimes it is because the output is written to a stream or something else which doesn’t take the output settings from the XSLT.

When transforming to an XmlWriter in .NET, be sure to correctly create the XML Writer using the overload that takes in output settings obtained from a loaded XSLT. E.g:

XslCompiledTransform xslt = GetXslt(XsltPath);

// The 2nd argument honours XSLT the output settings!
using (XmlWriter w = XmlWriter.Create(sb, xslt.OutputSettings))
{
    XsltArgumentList args = GetXsltArgs();

    XPathNavigator navigator = _someXmlData.CreateNavigator();

    if (navigator == null)
        throw new NullReferenceException("navigator");

    if (w == null)
        throw new NullReferenceException("w");

    xslt.Transform(navigator, args, w);
}

(xsl:output has many other useful attributes worth looking into.)

What kind of savings do you get?

The savings will differ for many reasons (the XML document size, the structure of the XML, the types of transformation being done etc etc), so it is hard to give a definitive savings. But here’s an illustrative example:

A few weeks back, a colleague at work was having trouble with an XSLT that was taking a long time to transform.

We started to have a look; I was expecting to find inefficient XPath, or an XML document structure that wasn’t conducive to decent transformation speed, or something like that.

However, I noticed the first thing was his output was set to be indented, and the XML input was HUGE.

So, the first thing we did was turn it off.

That alone reduced a 12 minute transform (on a 300MB document) to just 1 minute!

In another scenario, a 70K XML document was taking about 0.25 seconds to transform. Turning off indenting shaved a few more milliseconds (can’t remember exact amount now) — and saved about 2K in the output HTML that it generated.

Why can this make such a difference?

I think the specifics may vary depending on the XSLT parser you are using, but I believe it is basically this:

  • Each newline/white space/tab(s) created for the indent requires an extra text node to contain these characters which requires extra memory (though at this point may not add that much time to the transformation).
  • When the transform is then saved to a file, or written out to an output stream strings are often involved. String processing can be expensive in many programming languages, so each of these indented text nodes needs handling. For very large documents (as above) this can require a lot of unnecessary processing.

But doesn’t this make the output harder to read?

The consumer of a transformation result is likely to be another process such as another XSLT in a pipeline, another process, or even a web browser.

None of these typically care about the extra white space, which also would require more processing when loading.

If you need to view the XML, it may be worth keeping the indent off and manually opening it in a text editor that has the ability to “pretty print” it for you. (Warning: some editors and IDEs, e.g. Visual Studio, can do automatically pretty print an XML document for you when you open it, making you think the XML itself had the indented output!)

Other savings are still possible

Turning off the indent is just one of many things you can look into. Other things include the following (though your mileage may vary):

  • Use attributes instead of elements (where possible; usually this is for simple values, such as numbers, dates, and very limited strings, and where the element is not expected to be indented)
  • Look at the XML structure to see if can be improved to make XSLT processing easier
  • Cache the XSLT Processor
  • Cache the output

I’ll try to expand on some of those in future posts.

Some more detailed XSLT performance tips which also created cleaner code were covered in an earlier post.

CSS: inner elements breaking border-radius

Browsers such as Firefox 2+ and Webkit-based browsers (Chrome, Safari) support the useful css border-radius feature (via -moz-border-radius and -webkit-border-radius, respectively).

Unfortunately inner elements can break rounded borders as Richard Rutter described when using <img> elements inside an element with border-radius. He also provided a useful solution which is webkit only, unfortunately.

The problem

Here’s an example (view in Firefox 2+ or Safari/Chrome) where border-radius gets broken:

(If you are reading this in an RSS feed reader, you might need to go to the page to see the example properly)

A div containing a blockquote and a paragraph to mention the source of the quote:

Fear is the path to the Dark Side… Fear leads to anger… anger leads to hate… hate leads to suffering…

— Yoda, Star Wars, Episode 1: The Phantom Menace

Notice the bottom corners aren’t right: the background of the source paragraph breaks through the rounded corners.

The rounded corners are achieved using something like this:

.blockquote-with-source {
  border:1px solid #e8eac8;
  border-radius:10px;
  -moz-border-radius:10px;
  -webkit-border-radius:10px;
}

(The above code is of course slightly more than necessary due to both mozilla and webkit implementing them via their rendering prefixes.)

The solution

[Updated: October 2012]

On the outer div, simple add overflow:hidden.

Fear is the path to the Dark Side… Fear leads to anger… anger leads to hate… hate leads to suffering…

— Yoda, Star Wars, Episode 1: The Phantom Menace

Thanks to @Flu in the comments for updating us with this.

Since this article was originally written back in 2009, the solution presented at the time (applying border-radius to some of the inner elements) no longer seems to apply, and has therefore been removed.

Note, you may also be able to get rid of the -moz and -webkit prefixes in your CSS depending on what support level you are aiming for.

Update: Translation to Ukranian

June 2012: This post was translated into Ukrainian at http://softdroid.net/css-vnutr by Eclipse Android.

Web Standards – a good thing, but won’t help with SEO

Update 30 June 2009: Added a couple of links at the end of this post for additional information from some recent research/experimenting.

Web standards is generally a good thing. When done well, it can help to create lean pages, improve markup quality, provide better accessibility and can be easier to maintain (even while supporting IE6!)

One claim often made in the past to promote web standards is that following web standards (using CSS-based layouts, proper use of heading elements in markup, etc) will improve SEO.

Though this might be desirable, it is not the case; most sites on the web don’t follow web standards and most rank very high in their areas. Search engines are not going to penalize the 90+ % of sites that don’t follow web standards; that’s like shooting themselves in the foot!

As I describe in an earlier post on search engine ranking vs indexing while there are technical things one can do to ensure the site gets indexed well (e.g. URL canonicalization, good use of the <title /> element, etc), for ranking it is mostly about having good content that people will link to.

But as Matt Cutts, head of Google’s web spam team notes in this very short video, using CSS-based layouts or table-based layouts will have no bearing on search engine friendliness:

There are many good reasons to follow web standards principles, to use CSS-based layout and progressive enhancement but SEO, unfortunately, is not one of them.

Update 30 June 2009: A couple of decent articles recently talk about similar things:

  1. SEO Best Practices: SEOmoz’s New Policies Based on Updated Correlation Data
  2. The Triviality of On-Page HTML Tag Optimization