I've come up with a JavaScript solution, with a little help from JQueryUI and Knockout.
I realised that I wanted any adjustments made to be persisted across page refreshes. As soon as state came into it an MVVM approach such as that supported by Knockout seemed like a good choice. It also means I can subscribe to JQueryUI Resizable events for relevant values and do a recalculation of the 'editor' div height accordingly.
The navigation width can be changed, as can the details height. The editor div height adjusts accordingly. It also works on first page load and for window resizes. Lastly the adjustments survive page refreshes (I'm storing them in cookies).
The HTML and CSS have been simplified a great deal.
The result is in this jsFiddle, and also here:
HTML
<div id="header">
Header
</div>
<div id="container">
<div id="nav" data-bind="jqResizableWidth: navWidth, jqOptions: { handles: 'e', minWidth: 5 } ">
Navigation
</div>
<div id="main">
<div id="details" data-bind="jqResizableHeight: detailsHeight, jqOptions: { handles: 's', minHeight: 10 }">
Details
</div>
<div id="toolbar">
Button1 Button2 Button3 Button4 Button5 Button6 Button7
</div>
<div id="editor">
<div class="paragraph">
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Etiam quam sapien, volutpat ac iaculis ut, malesuada quis massa. Quisque quis risus eu tellus mattis sagittis vulputate at nisl. Donec at nibh non neque facilisis adipiscing. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec at condimentum est. Fusce gravida diam vel odio venenatis vitae cursus lorem cursus. Ut tristique, libero quis scelerisque semper, arcu velit faucibus dui, eu imperdiet nisl arcu id enim. Nullam eget placerat risus.
</div>
<div class="paragraph">
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Etiam quam sapien, volutpat ac iaculis ut, malesuada quis massa. Quisque quis risus eu tellus mattis sagittis vulputate at nisl. Donec at nibh non neque facilisis adipiscing. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec at condimentum est. Fusce gravida diam vel odio venenatis vitae cursus lorem cursus. Ut tristique, libero quis scelerisque semper, arcu velit faucibus dui, eu imperdiet nisl arcu id enim. Nullam eget placerat risus.
</div>
<div class="paragraph">
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Etiam quam sapien, volutpat ac iaculis ut, malesuada quis massa. Quisque quis risus eu tellus mattis sagittis vulputate at nisl. Donec at nibh non neque facilisis adipiscing. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec at condimentum est. Fusce gravida diam vel odio venenatis vitae cursus lorem cursus. Ut tristique, libero quis scelerisque semper, arcu velit faucibus dui, eu imperdiet nisl arcu id enim. Nullam eget placerat risus.
</div>
<div class="paragraph">
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Etiam quam sapien, volutpat ac iaculis ut, malesuada quis massa. Quisque quis risus eu tellus mattis sagittis vulputate at nisl. Donec at nibh non neque facilisis adipiscing. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec at condimentum est. Fusce gravida diam vel odio venenatis vitae cursus lorem cursus. Ut tristique, libero quis scelerisque semper, arcu velit faucibus dui, eu imperdiet nisl arcu id enim. Nullam eget placerat risus.
</div>
<div class="paragraph">
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Etiam quam sapien, volutpat ac iaculis ut, malesuada quis massa. Quisque quis risus eu tellus mattis sagittis vulputate at nisl. Donec at nibh non neque facilisis adipiscing. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec at condimentum est. Fusce gravida diam vel odio venenatis vitae cursus lorem cursus. Ut tristique, libero quis scelerisque semper, arcu velit faucibus dui, eu imperdiet nisl arcu id enim. Nullam eget placerat risus.
</div>
</div>
</div>
</div>
<div id="footer">
Footer
</div>
CSS
#header{
position: fixed;
top: 0px;
height: 30px;
}
#footer{
position: fixed;
bottom: 0px;
height: 30px;
}
#container{
position:fixed;
top:30px;
bottom:30px;
left:0;
right:0;
overflow:hidden;
z-index:-1;
background-color: #dddddd;
}
#nav
{
display: table-cell;
background-color: #ccffff;
}
#nav .ui-resizable-e {
background: #cccccc;
width:5px;
height: 100%;
}
#main
{
display: table-cell;
background-color: #ffffcc;
}
#details
{
padding-left: 5px;
background-color: #faccfa;
width: 100%;
}
#details .ui-resizable-s {
background: #cccccc;
width:100%;
height: 5px;
}
#toolbar
{
padding-top: 5px;
padding-left: 5px;
}
#editor
{
padding-left: 5px;
background-color: #fddfaf;
overflow-y: scroll;
}
.paragraph
{
min-height: 150px;
}
JavaScript
ko.bindingHandlers.jqResizableWidth = {
init: function(element, valueAccessor, allBindingsAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).width(value);
var options = allBindingsAccessor().jqOptions || {};
$(element).resizable(options);
ko.utils.registerEventHandler(element, "resize", function(event, ui) {
var observable = valueAccessor();
var value = ui.size.width;
observable(value);
// Have to include the next line because otherwise JQueryUI Resizable
// fixes the height to the currently resolved height.
$(element).height('100%');
});
}
};
function adjustEditor() {
// Extra 5px added because of 5px
var height = $(window).height() - $('#header').height() - $('#details').height() - $('#toolbar').height() - $('#details .ui-resizable-s').height() - $('#footer').height();
$('#editor').height(height);
}
ko.bindingHandlers.jqResizableHeight = {
init: function(element, valueAccessor, allBindingsAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).height(value);
var options = allBindingsAccessor().jqOptions || {};
$(element).resizable(options);
ko.utils.registerEventHandler(element, "resize", function(event, ui) {
var observable = valueAccessor();
var value = ui.size.height;
observable(value);
// Have to include the next line because otherwise JQueryUI Resizable
// fixes the width to the currently resolved width.
$(element).width('100%');
});
}
};
var viewModel = function(navWidth, detailsHeight) {
var self = this;
self.navWidth = ko.observable(navWidth);
self.detailsHeight = ko.observable(detailsHeight);
self.navWidth.subscribe(function(newValue) {
$.cookie('navWidth', newValue, {
expires: 7,
path: '/'
});
adjustEditor();
});
self.detailsHeight.subscribe(function(newValue) {
$.cookie('detailsHeight', newValue, {
expires: 7,
path: '/'
});
adjustEditor();
});
};
var navWidth = $.cookie('navWidth') || 80;
var detailsHeight = $.cookie('detailsHeight') || 50;
ko.applyBindings(new viewModel(navWidth, detailsHeight));
$(window).resize(function() {adjustEditor();});
$(window).resize();