// Metawidget 3.4 minified (licensed under LGPL)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

// Metawidget 3.4 (licensed under LGPL)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

var metawidget = metawidget || {};

( function() {

	'use strict';

	/**
	 * @namespace InspectionResultProcessors.
	 */

	metawidget.inspectionresultprocessor = metawidget.inspectionresultprocessor || {};
	
} )();// Metawidget 3.4 (licensed under LGPL)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

var metawidget = metawidget || {};

( function() {

	'use strict';

	/**
	 * @namespace Inspectors.
	 *            <p>
	 *            Inspectors must implement an interface:
	 *            </p>
	 *            <tt>function( toInspect, type, names )</tt>
	 *            <p>
	 *            Each Inspector must look to the 'type' parameter and the
	 *            'names' array. These form a path into the business object
	 *            domain model. For example the 'type' may be 'person' and the
	 *            'names' may be [ 'address', 'street' ]. This would form a path
	 *            into the domain model of 'person/address/street' (i.e. return
	 *            information on the 'street' property within the 'address'
	 *            property of the 'person' type).
	 *            </p>
	 */

	metawidget.inspector = metawidget.inspector || {};

	/**
	 * @class Delegates inspection to one or more sub-inspectors, then combines
	 *        the resulting metadata using
	 *        <tt>metawidget.util.combineInspectionResults</tt>.
	 *        <p>
	 *        The combining algorithm should be suitable for most use cases, but
	 *        one benefit of having a separate CompositeInspector is that
	 *        developers can replace it with their own version, with its own
	 *        combining algorithm, if required.
	 *        <p>
	 *        Note: the name <em>Composite</em>Inspector refers to the
	 *        Composite design pattern.
	 */

	metawidget.inspector.CompositeInspector = function( config ) {

		if ( ! ( this instanceof metawidget.inspector.CompositeInspector ) ) {
			throw new Error( 'Constructor called as a function' );
		}

		var _inspectors;

		if ( config.inspectors !== undefined ) {
			_inspectors = config.inspectors.slice( 0 );
		} else {
			_inspectors = config.slice( 0 );
		}

		this.inspect = function( toInspect, type, names ) {

			var compositeInspectionResult = {};

			for ( var ins = 0, insLength = _inspectors.length; ins < insLength; ins++ ) {

				var inspectionResult;
				var inspector = _inspectors[ins];

				if ( inspector.inspect !== undefined ) {
					inspectionResult = inspector.inspect( toInspect, type, names );
				} else {
					inspectionResult = inspector( toInspect, type, names );
				}

				metawidget.util.combineInspectionResults( compositeInspectionResult, inspectionResult );
			}

			return compositeInspectionResult;
		};
	};

	/**
	 * @class Inspects JavaScript objects for their property names and types.
	 *        <p>
	 *        In principal, ordering of property names within JavaScript objects
	 *        is not guaranteed. In practice, most browsers respect the original
	 *        order that properties were defined in. However you may want to
	 *        combine PropertyTypeInspector with a custom Inspector that imposes
	 *        a defined ordering using 'propertyOrder' attributes.
	 */

	metawidget.inspector.PropertyTypeInspector = function() {

		if ( ! ( this instanceof metawidget.inspector.PropertyTypeInspector ) ) {
			throw new Error( 'Constructor called as a function' );
		}
	};

	metawidget.inspector.PropertyTypeInspector.prototype.inspect = function( toInspect, type, names ) {

		// Traverse names

		toInspect = metawidget.util.traversePath( toInspect, names );

		var inspectionResult = {};

		// Inspect root node. Important if the Metawidget is
		// pointed directly at a primitive type

		if ( names !== undefined && names.length > 0 ) {
			inspectionResult.name = names[names.length - 1];
		} else {

			// Nothing useful to return?

			if ( toInspect === undefined ) {
				return;
			}
		}

		if ( toInspect !== undefined ) {

			inspectionResult.type = _getTypeOf( toInspect );

			if ( inspectionResult.type === undefined ) {

				inspectionResult.properties = {};

				for ( var property in toInspect ) {

					inspectionResult.properties[property] = {
						type: _getTypeOf( toInspect[property] )
					};
				}
			}
		}

		return inspectionResult;

		//
		// Private methods
		//

		/**
		 * Inspect the type of the property as best we can.
		 */

		function _getTypeOf( value ) {

			// JSON Schema primitive types are: 'array', 'boolean',
			// 'integer', 'number', 'null', 'object' and 'string'

			if ( value instanceof Array ) {

				// typeof never returns 'array', even though JavaScript has
				// a built-in Array type

				return 'array';

			} else if ( value instanceof Date ) {

				// typeof never returns 'date', even though JavaScript has a
				// built-in Date type

				return 'date';

			} else {

				var typeOfProperty = typeof ( value );

				// type 'object' doesn't convey much, and can override a
				// more descriptive inspection result from a previous
				// Inspector. If you leave it off, Metawidget's default
				// behaviour is to recurse into the object anyway

				if ( typeOfProperty !== 'object' ) {
					return typeOfProperty;
				}
			}
		}
	};

	/**
	 * @class Inspects JSON Schemas for their properties.
	 *        <p>
	 *        Because Metawidget <em>already</em> uses JSON Schema internally
	 *        as its inspection result format, this Inspector does not need to
	 *        do much. However it adds support for JSON schemas that contain
	 *        nested schemas (by traversing the given 'names' array).
	 */

	metawidget.inspector.JsonSchemaInspector = function( config ) {

		if ( ! ( this instanceof metawidget.inspector.JsonSchemaInspector ) ) {
			throw new Error( 'Constructor called as a function' );
		}

		var _schema;

		if ( config.schema !== undefined ) {
			_schema = config.schema;
		} else {
			_schema = config;
		}

		this.inspect = function( toInspect, type, names ) {

			// Restrict by type (best practice)

			if ( _schema.type !== undefined && _schema.type !== type ) {
				return undefined;
			}

			// Traverse names using 'properties' intermediate name

			var traversed = metawidget.util.traversePath( _schema, names, 'properties' );

			if ( traversed === undefined ) {
				return undefined;
			}

			// Copy values

			var inspectionResult = {};
			if ( names !== undefined ) {
				inspectionResult.name = names[names.length - 1];
			}
			metawidget.util.combineInspectionResults( inspectionResult, traversed );
			return inspectionResult;
		};
	};
} )();// Metawidget 3.4 (licensed under LGPL)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

var metawidget = metawidget || {};

( function() {

	'use strict';

	/**
	 * @namespace Layouts.
	 */

	metawidget.layout = metawidget.layout || {};

	/**
	 * @class Layout to simply output components one after another, with no
	 *        labels and no structure. This Layout is suited to rendering single
	 *        components, or for rendering components whose layout relies
	 *        entirely on CSS.
	 */

	metawidget.layout.SimpleLayout = function() {

		if ( ! ( this instanceof metawidget.layout.SimpleLayout ) ) {
			throw new Error( 'Constructor called as a function' );
		}
	};

	metawidget.layout.SimpleLayout.prototype.layoutWidget = function( widget, elementName, attributes, container, mw ) {

		if ( widget.tagName === 'STUB' && !metawidget.util.hasChildElements( widget ) ) {
			return;
		}

		container.appendChild( widget );
	};

	/**
	 * @class Layout to arrange widgets using div tags.
	 */

	metawidget.layout.DivLayout = function( config ) {

		if ( ! ( this instanceof metawidget.layout.DivLayout ) ) {
			throw new Error( 'Constructor called as a function' );
		}

		var _divStyleClasses = config !== undefined ? config.divStyleClasses : undefined;
		var _labelStyleClass = config !== undefined ? config.labelStyleClass : undefined;
		var _labelSuffix = config !== undefined && config.labelSuffix !== undefined ? config.labelSuffix : ':';

		this.layoutWidget = function( widget, elementName, attributes, container, mw ) {

			if ( widget.tagName === 'STUB' && !metawidget.util.hasChildElements( widget ) ) {
				return;
			}

			var outerDiv = metawidget.util.createElement( mw, 'div' );
			if ( _divStyleClasses !== undefined && _divStyleClasses[0] !== undefined ) {
				outerDiv.setAttribute( 'class', _divStyleClasses[0] );
			}

			// Label

			this.layoutLabel( outerDiv, widget, elementName, attributes, mw );

			// Widget

			var widgetDiv = metawidget.util.createElement( mw, 'div' );
			if ( _divStyleClasses !== undefined && _divStyleClasses[2] !== undefined ) {
				widgetDiv.setAttribute( 'class', _divStyleClasses[2] );
			}

			widgetDiv.appendChild( widget );
			outerDiv.appendChild( widgetDiv );

			container.appendChild( outerDiv );
		};
		
		this.layoutLabel = function( outerDiv, widget, elementName, attributes, mw ){
			
			if ( elementName === 'action' ) {
				return;
			}
			
			if ( attributes.name === undefined && attributes.title === undefined ) {
				return;
			}

			var labelDiv = metawidget.util.createElement( mw, 'div' );
			if ( _divStyleClasses !== undefined && _divStyleClasses[1] !== undefined ) {
				labelDiv.setAttribute( 'class', _divStyleClasses[1] );
			}

			var label = metawidget.util.createElement( mw, 'label' );
			if ( widget.getAttribute( 'id' ) !== null ) {
				label.setAttribute( 'for', widget.getAttribute( 'id' ) );
			}

			if ( _labelStyleClass !== undefined ) {
				label.setAttribute( 'class', _labelStyleClass );
			}

			label.innerHTML = metawidget.util.getLabelString( attributes, mw ) + _labelSuffix;

			labelDiv.appendChild( label );
			outerDiv.appendChild( labelDiv );
		};
	};

	/**
	 * @class Layout to arrange widgets in a table, with one column for the
	 *        label and another for the widget.
	 */

	metawidget.layout.TableLayout = function( config ) {

		if ( ! ( this instanceof metawidget.layout.TableLayout ) ) {
			throw new Error( 'Constructor called as a function' );
		}

		var _tableStyleClass = config !== undefined ? config.tableStyleClass : undefined;
		var _columnStyleClasses = config !== undefined ? config.columnStyleClasses : undefined;
		var _headerStyleClass = config !== undefined ? config.headerStyleClass : undefined;
		var _footerStyleClass = config !== undefined ? config.footerStyleClass : undefined;
		var _numberOfColumns = config !== undefined && config.numberOfColumns ? config.numberOfColumns : 1;
		var _currentColumn = 0;

		this.startContainerLayout = function( container, mw ) {

			var table = metawidget.util.createElement( mw, 'table' );
			if ( mw.path !== undefined ) {
				var id = metawidget.util.getId( "property", {}, mw );
				if ( id !== undefined ) {
					table.setAttribute( 'id', 'table-' + id );
				}
			}

			if ( _tableStyleClass !== undefined ) {
				table.setAttribute( 'class', _tableStyleClass );
			}

			container.appendChild( table );

			// Facets

			if ( mw.overriddenNodes !== undefined ) {
				for ( var loop1 = 0, length1 = mw.overriddenNodes.length; loop1 < length1; loop1++ ) {

					var child = mw.overriddenNodes[loop1];

					if ( child.tagName !== 'FACET' ) {
						continue;
					}

					// thead or tfoot

					var parent;

					if ( child.getAttribute( 'name' ) === 'header' ) {
						parent = metawidget.util.createElement( mw, 'thead' );
					} else if ( child.getAttribute( 'name' ) === 'footer' ) {
						parent = metawidget.util.createElement( mw, 'tfoot' );
					} else {
						continue;
					}

					table.appendChild( parent );
					var tr = metawidget.util.createElement( mw, 'tr' );
					parent.appendChild( tr );
					var td = metawidget.util.createElement( mw, 'td' );
					td.setAttribute( 'colspan', _numberOfColumns * 2 );

					if ( child.getAttribute( 'name' ) === 'header' ) {
						if ( _headerStyleClass !== undefined ) {
							td.setAttribute( 'class', _headerStyleClass );
						}
					} else {
						if ( _footerStyleClass !== undefined ) {
							td.setAttribute( 'class', _footerStyleClass );
						}
					}

					tr.appendChild( td );

					// Append children, so as to unwrap the 'facet' tag

					while ( child.childNodes.length > 0 ) {
						td.appendChild( child.removeChild( child.childNodes[0] ) );
					}
				}
			}

			// tbody

			table.appendChild( metawidget.util.createElement( mw, 'tbody' ) );
		};

		this.layoutWidget = function( widget, elementName, attributes, container, mw ) {

			// Do not render empty stubs

			if ( widget.tagName === 'STUB' && !metawidget.util.hasChildElements( widget ) ) {
				return;
			}

			// Special support for large components

			var spanAllColumns = metawidget.util.isSpanAllColumns( attributes );

			if ( spanAllColumns === true && _currentColumn > 0 ) {
				_currentColumn = 0;
			}

			// Id

			var table = container.childNodes[container.childNodes.length - 1];
			var idPrefix = undefined;

			if ( attributes.name !== undefined ) {
				if ( table.hasAttribute( 'id' ) ) {
					idPrefix = table.getAttribute( 'id' );
				}

				if ( idPrefix !== undefined ) {
					if ( elementName !== 'entity' ) {
						if ( idPrefix.charAt( idPrefix.length - 1 ) !== '-' ) {
							idPrefix += metawidget.util.capitalize( attributes.name );
						} else {
							idPrefix += attributes.name;
						}
					}
				} else {
					idPrefix = 'table-' + attributes.name;
				}
			}

			// Start column

			var tbody = table.childNodes[table.childNodes.length - 1];
			var tr;

			if ( _currentColumn === 0 ) {
				tr = metawidget.util.createElement( mw, 'tr' );
				if ( idPrefix !== undefined ) {
					tr.setAttribute( 'id', idPrefix + '-row' );
				}
				tbody.appendChild( tr );
			} else {
				tr = tbody.childNodes[tbody.childNodes.length - 1];
			}

			// Label
			
			this.layoutLabel( tr, idPrefix, widget, elementName, attributes, mw );

			// Widget

			var td = metawidget.util.createElement( mw, 'td' );

			if ( idPrefix !== undefined ) {
				td.setAttribute( 'id', idPrefix + '-cell' );
			}

			if ( _columnStyleClasses !== undefined && _columnStyleClasses[1] !== undefined ) {
				td.setAttribute( 'class', _columnStyleClasses[1] );
			}

			if ( spanAllColumns === true ) {
				td.setAttribute( 'colspan', ( ( _numberOfColumns * 3 ) - 1 ) - tr.childNodes.length );
			} else if ( tr.childNodes.length < 1 ) {
				td.setAttribute( 'colspan', 2 - tr.childNodes.length );
			}

			td.appendChild( widget );
			tr.appendChild( td );

			// Required

			this.layoutRequired( tr, attributes, mw );
			
			// Next column

			if ( spanAllColumns === true ) {
				_currentColumn = _numberOfColumns - 1;
			}

			_currentColumn = ( _currentColumn + 1 ) % _numberOfColumns;
		};
		
		this.layoutLabel = function( tr, idPrefix, widget, elementName, attributes, mw ) {
			
			if ( attributes.name === undefined && attributes.title === undefined ) {
				return;
			}

			// Label

			var th = metawidget.util.createElement( mw, 'th' );

			if ( idPrefix !== undefined ) {
				th.setAttribute( 'id', idPrefix + '-label-cell' );
			}

			if ( _columnStyleClasses !== undefined && _columnStyleClasses[0] !== undefined ) {
				th.setAttribute( 'class', _columnStyleClasses[0] );
			}

			if ( elementName !== 'action' ) {

				var label = metawidget.util.createElement( mw, 'label' );
	
				if ( widget.hasAttribute( 'id' ) ) {
					label.setAttribute( 'for', widget.getAttribute( 'id' ) );
				}
	
				if ( idPrefix !== undefined ) {
					label.setAttribute( 'id', idPrefix + '-label' );
				}
	
				label.innerHTML = metawidget.util.getLabelString( attributes, mw ) + ':';
	
				th.appendChild( label );
			}
			
			tr.appendChild( th );
		};

		this.layoutRequired = function( tr, attributes, mw ) {
			
			var td = metawidget.util.createElement( mw, 'td' );

			if ( _columnStyleClasses !== undefined && _columnStyleClasses[2] !== undefined ) {
				td.setAttribute( 'class', _columnStyleClasses[2] );
			}

			if ( !metawidget.util.isTrueOrTrueString( attributes.readOnly ) && metawidget.util.isTrueOrTrueString( attributes.required )) {
				td.innerHTML = '*';
			}

			tr.appendChild( td );
		};		
	};

	//
	// LayoutDecorator
	//

	/**
	 * Augment the given 'decorator' with methods suitable for making section
	 * separator LayoutDecorators.
	 * <p>
	 * This includes implementing <tt>onStartBuild</tt>,
	 * <tt>startContainerLayout</tt>, <tt>endContainerLayout</tt> and
	 * <tt>onEndBuild</tt> methods.
	 */

	metawidget.layout._createSectionLayoutDecorator = function( config, decorator, decoratorName ) {

		var _delegate;

		if ( config.delegate !== undefined ) {
			_delegate = config.delegate;
		} else {
			_delegate = config;
		}

		/**
		 * Read-only getter.
		 * <p>
		 * Dangerous to add a public 'delegate' property, because can conflict
		 * with 'config.delegate'.
		 */

		decorator.getDelegate = function() {

			return _delegate;
		};

		decorator.onStartBuild = function( mw ) {

			if ( decorator.getDelegate().onStartBuild !== undefined ) {
				decorator.getDelegate().onStartBuild( mw );
			}
		};

		decorator.startContainerLayout = function( container, mw ) {

			container[decoratorName] = {};

			if ( decorator.getDelegate().startContainerLayout !== undefined ) {
				decorator.getDelegate().startContainerLayout( container, mw );
			}
		};

		decorator.endContainerLayout = function( container, mw ) {

			if ( decorator.getDelegate().endContainerLayout !== undefined ) {
				decorator.getDelegate().endContainerLayout( container, mw );
			}

			container[decoratorName] = {};
		};

		decorator.onEndBuild = function( mw ) {

			if ( decorator.getDelegate().onEndBuild !== undefined ) {
				decorator.getDelegate().onEndBuild( mw );
			}
		};
	};

	/**
	 * Augment the given 'decorator' with methods suitable for making flat (as
	 * opposed to nested) section separator LayoutDecorators.
	 * <p>
	 * This includes an implementation of the <tt>layoutWidget</tt> method and
	 * a declaration of a <tt>addSectionWidget</tt> method.
	 */

	metawidget.layout.createFlatSectionLayoutDecorator = function( config, decorator, decoratorName ) {

		if ( this instanceof metawidget.layout.createFlatSectionLayoutDecorator ) {
			throw new Error( 'Function called as a Constructor' );
		}

		metawidget.layout._createSectionLayoutDecorator( config, decorator, decoratorName );

		decorator.layoutWidget = function( widget, elementName, attributes, container, mw ) {

			// If our delegate is itself a NestedSectionLayoutDecorator, strip
			// the section

			if ( decorator.getDelegate().nestedSectionLayoutDecorator === true ) {

				// Stay where we are?

				var section = metawidget.util.stripSection( attributes );

				if ( section === undefined || section === container[decoratorName].currentSection ) {
					return decorator.getDelegate().layoutWidget( widget, elementName, attributes, container, mw );
				}

				// End nested LayoutDecorator's current section

				if ( container[decoratorName].currentSection !== undefined ) {
					decorator.getDelegate().endContainerLayout( container, mw );
				}

				container[decoratorName].currentSection = section;

				// Add a heading

				if ( section !== '' ) {
					decorator.addSectionWidget( section, 0, attributes, container, mw );
				}
			} else {

				// Stay where we are?

				if ( attributes.section === undefined || attributes.section === container[decoratorName].currentSection ) {
					return decorator.getDelegate().layoutWidget( widget, elementName, attributes, container, mw );
				}

				// For each of the new sections...

				var sections = attributes.section;

				if ( ! ( sections instanceof Array ) ) {
					sections = [ sections ];
				}

				var currentSections;

				if ( container[decoratorName].currentSection !== undefined ) {
					currentSections = container[decoratorName].currentSection;
				} else {
					currentSections = [];
				}

				for ( var level = 0; level < sections.length; level++ ) {
					var section = sections[level];

					// ...that are different from our current...

					if ( section === '' ) {
						continue;
					}

					if ( level < currentSections.length && section === currentSections[level] ) {
						continue;
					}

					// ...add a heading
					//
					// Note: we cannot stop/start the delegate layout here. It
					// is tempting, but remember addSectionWidget needs to use
					// the delegate. If you stop/add section heading/start the
					// delegate, who is laying out the section heading?

					decorator.addSectionWidget( section, level, attributes, container, mw );
				}

				container[decoratorName].currentSection = sections;
			}

			// Add component as normal

			decorator.getDelegate().layoutWidget( widget, elementName, attributes, container, mw );
		};
	};

	/**
	 * Augment the given 'decorator' with methods suitable for making nested (as
	 * opposed to flat) section separator LayoutDecorators.
	 * <p>
	 * This includes an implementation of the <tt>layoutWidget</tt> method and
	 * a declaration of a <tt>createSectionWidget</tt> method.
	 */

	metawidget.layout.createNestedSectionLayoutDecorator = function( config, decorator, decoratorName ) {

		if ( this instanceof metawidget.layout.createNestedSectionLayoutDecorator ) {
			throw new Error( 'Function called as a Constructor' );
		}

		metawidget.layout._createSectionLayoutDecorator( config, decorator, decoratorName );

		// Tag this NestedSectionLayoutDecorator so that
		// FlatSectionLayoutDecorator can recognize it

		decorator.nestedSectionLayoutDecorator = true;

		decorator.layoutWidget = function( widget, elementName, attributes, container, mw ) {

			// Stay where we are?

			var section = metawidget.util.stripSection( attributes );

			if ( section === undefined || section === container[decoratorName].currentSection ) {
				if ( container[decoratorName].currentSectionWidget ) {
					return decorator.getDelegate().layoutWidget( widget, elementName, attributes, container[decoratorName].currentSectionWidget, mw );
				}
				return decorator.getDelegate().layoutWidget( widget, elementName, attributes, container, mw );
			}

			// End current section

			if ( container[decoratorName].currentSectionWidget !== undefined ) {
				decorator.endContainerLayout( container[decoratorName].currentSectionWidget, mw );
			}

			container[decoratorName].currentSection = section;
			var previousSectionWidget = container[decoratorName].currentSectionWidget;
			delete container[decoratorName].currentSectionWidget;

			// No new section?

			if ( section === '' ) {
				decorator.getDelegate().layoutWidget( widget, elementName, attributes, container, mw );
				return;
			}

			// Start new section

			container[decoratorName].currentSectionWidget = decorator.createSectionWidget( previousSectionWidget, section, attributes, container, mw );
			decorator.startContainerLayout( container[decoratorName].currentSectionWidget, mw );

			// Add component to new section

			decorator.getDelegate().layoutWidget( widget, elementName, attributes, container[decoratorName].currentSectionWidget, mw );
		};

		var _superEndContainerLayout = decorator.endContainerLayout;

		decorator.endContainerLayout = function( container, mw ) {

			// End hanging layouts

			if ( container[decoratorName].currentSectionWidget !== undefined ) {
				decorator.endContainerLayout( container[decoratorName].currentSectionWidget, mw );
			}

			_superEndContainerLayout( container, mw );
		};
	};

	/**
	 * @class LayoutDecorator to decorate widgets from different sections using
	 *        an HTML heading tag (i.e. <tt>h1</tt>, <tt>h2</tt> etc).
	 */

	metawidget.layout.HeadingTagLayoutDecorator = function( config ) {

		if ( ! ( this instanceof metawidget.layout.HeadingTagLayoutDecorator ) ) {
			throw new Error( 'Constructor called as a function' );
		}

		metawidget.layout.createFlatSectionLayoutDecorator( config, this, 'headingTagLayoutDecorator' );
	};

	metawidget.layout.HeadingTagLayoutDecorator.prototype.addSectionWidget = function( section, level, attributes, container, mw ) {

		var h1 = metawidget.util.createElement( mw, 'h' + ( level + 1 ) );
		h1.innerHTML = section;

		this.getDelegate().layoutWidget( h1, "property", {
			wide: 'true'
		}, container, mw );
	};

	/**
	 * @class LayoutDecorator to decorate widgets from different sections using
	 *        nested <tt>div</tt> tags.
	 */

	metawidget.layout.DivLayoutDecorator = function( config ) {

		if ( ! ( this instanceof metawidget.layout.DivLayoutDecorator ) ) {
			throw new Error( 'Constructor called as a function' );
		}

		metawidget.layout.createNestedSectionLayoutDecorator( config, this, 'divLayoutDecorator' );
	};

	metawidget.layout.DivLayoutDecorator.prototype.createSectionWidget = function( previousSectionWidget, section, attributes, container, mw ) {

		var div = metawidget.util.createElement( mw, 'div' );
		div.setAttribute( 'title', section );
		this.getDelegate().layoutWidget( div, "property", {
			wide: 'true'
		}, container, mw );

		return div;
	};
} )();// Metawidget 3.4 (licensed under LGPL)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

var metawidget = metawidget || {};

( function() {

	'use strict';

	/**
	 * @namespace Utilities.
	 */

	metawidget.util = metawidget.util || {};

	/**
	 * Returns a label for the given set of attributes.
	 * <p>
	 * The label is determined using the following algorithm:
	 * <p>
	 * <ul>
	 * <li> if <tt>attributes.title</tt> exists...
	 * <ul>
	 * <li>if the given <tt>mw</tt> has a property <tt>l10n</tt>, then
	 * <tt>attributes.title</tt> is camel-cased and used as a lookup into
	 * <tt>mw.i10n[camelCasedTitle]</tt>. This means developers can initially
	 * build their UIs without worrying about localization, then turn it on
	 * later</li>
	 * <li>if no such lookup exists (or <tt>mw.l10n</tt> does not exist),
	 * return <tt>attributes.title</tt>
	 * </ul>
	 * </li>
	 * <li> if <tt>attributes.title</tt> does not exist...
	 * <ul>
	 * <li>if the given <tt>mw</tt> has a property <tt>l10n</tt>, then
	 * <tt>attributes.name</tt> is used as a lookup into
	 * <tt>mw.i10n[attributes.name]</tt></li>
	 * <li>if no such lookup exists (or <tt>mw.l10n</tt> does not exist),
	 * return <tt>attributes.name</tt>
	 * </ul>
	 * </li>
	 * </ul>
	 */

	metawidget.util.getLabelString = function( attributes, mw ) {

		// Explicit title

		if ( attributes.title !== undefined ) {

			// (localize if possible)

			var camelCased = metawidget.util.camelCase( attributes.title );

			if ( mw.l10n !== undefined && mw.l10n[camelCased] !== undefined ) {
				return mw.l10n[camelCased];
			}

			return attributes.title;
		}

		// Localize if possible

		var name = attributes.name;

		if ( mw.l10n !== undefined && mw.l10n[name] !== undefined ) {
			return mw.l10n[name];
		}

		// Default name, uncamel case (e.g. from 'fooBarBaz1' to 'Foo Bar Baz
		// 1'). Ported from StringUtils.uncamelCase

		var uncamelCasedName = '';
		var first = true;
		var lastChar = ' ';

		for ( var loop = 0; loop < name.length; loop++ ) {
			var c = name[loop];

			if ( first === true ) {
				uncamelCasedName += c.toUpperCase();
				first = false;
			} else if ( _isUpperCase( c ) && ( !_isUpperCase( lastChar ) || ( loop < name.length - 1 && name[loop + 1] != ' ' && !_isUpperCase( name[loop + 1] ) ) ) ) {
				if ( lastChar !== ' ' ) {
					uncamelCasedName += ' ';
				}

				// Don't do: if ( loop + 1 < length && !_isUpperCase( chars[loop
				// + 1] ) )
				// uncamelCasedName += _toLowerCase( c );
				//
				// It's ambiguous if we should lowercase the letter following a
				// space, but in
				// general it looks nicer most of the time not to. The exception
				// is 'joining' words
				// such as 'of' in 'Date of Birth'

				uncamelCasedName += c;
			} else if ( _isDigit( c ) && _isLetter( lastChar ) && lastChar != ' ' ) {
				uncamelCasedName += ' ' + c;
			} else {
				uncamelCasedName += c;
			}

			lastChar = c;
		}

		return uncamelCasedName;

		//
		// Private methods
		//

		function _isDigit( c ) {

			var charCode = c.charCodeAt( 0 );
			return ( charCode >= 48 && charCode <= 57 );
		}

		function _isUpperCase( c ) {

			var charCode = c.charCodeAt( 0 );
			return ( charCode >= 65 && charCode <= 90 );
		}

		function _isLetter( c ) {

			var charCode = c.charCodeAt( 0 );
			return ( charCode >= 65 && charCode <= 90 ) || ( charCode >= 97 && charCode <= 122 );
		}
	};

	/**
	 * Capitalizes the first letter of the given name (e.g. from 'fooBarBaz' to
	 * 'FooBarBaz').
	 */

	metawidget.util.capitalize = function( name ) {

		return name.charAt( 0 ).toUpperCase() + name.slice( 1 );
	};

	/**
	 * @return true if the value is boolean true or string 'true', but false for
	 *         any other value (including other JavaScript 'truthy' values)
	 */

	metawidget.util.isTrueOrTrueString = function( value ) {

		return ( value === 'true' || value === true );
	};

	/**
	 * Camel cases the given array of names (e.g. from ['foo','bar','baz'] to
	 * 'fooBarBaz'). Note the second and third names are capitalized. However no
	 * attempt is made to <em>de</em>capitalize the first name, because that
	 * gets very ambiguous with names like 'URL', 'ID' etc.
	 * <p>
	 * If <tt>names</tt> is not an array, first calls
	 * <tt>names.split( ' ' )</tt>.
	 * 
	 * @return the camel cased name. Or an empty string if no name
	 */

	metawidget.util.camelCase = function( names ) {

		if ( ! ( names instanceof Array ) ) {
			names = names.split( ' ' );
		}

		var toString = '';
		var length = names.length;

		if ( length > 0 ) {
			toString += names[0];
		}

		for ( var loop = 1; loop < length; loop++ ) {
			toString += metawidget.util.capitalize( names[loop] );
		}

		return toString;
	};

	/**
	 * Gets a camelCased id based on the given attributes.name and the given
	 * mw.path.
	 */

	metawidget.util.getId = function( elementName, attributes, mw ) {

		if ( mw.path !== undefined ) {
			var splitPath = mw.path.split( '.' );

			if ( splitPath[0] === 'object' ) {
				splitPath = splitPath.slice( 1 );
			}

			if ( attributes.name && elementName !== 'entity' ) {
				splitPath.push( attributes.name );
			} else if ( splitPath.length == 0 ) {
				return undefined;
			}

			var id = metawidget.util.camelCase( splitPath );

			// Strip array qualifiers

			id = id.replace( /[\[\]]/g, '' );

			return id;
		}

		if ( attributes !== undefined ) {
			return attributes.name;
		}
	};

	/**
	 * Returns true if the given node has child <em>elements</em>. That is,
	 * their <tt>nodeType === 1</tt>. Ignores other sorts of child nodes,
	 * such as text nodes.
	 */

	metawidget.util.hasChildElements = function( node ) {

		var childNodes = node.childNodes;

		for ( var loop = 0, length = childNodes.length; loop < length; loop++ ) {

			if ( childNodes[loop].nodeType === 1 ) {
				return true;
			}
		}

		return false;
	};

	/**
	 * @true if the given attributes define 'large' or 'wide'.
	 */

	metawidget.util.isSpanAllColumns = function( attributes ) {

		if ( attributes === undefined ) {
			return false;
		}

		if ( metawidget.util.isTrueOrTrueString( attributes.large ) ) {
			return true;
		}

		if ( metawidget.util.isTrueOrTrueString( attributes.wide ) ) {
			return true;
		}

		return false;
	};

	/**
	 * Splits the given path into its type and an array of names (e.g.
	 * 'foo.bar['baz']' into type 'foo' and names ['bar','baz']).
	 * 
	 * @returns an object with properties 'type' and 'names' (provided there is
	 *          at least 1 name)
	 */

	metawidget.util.splitPath = function( path ) {

		var splitPath = {};

		if ( path !== undefined ) {

			// Match at every '.' and '[' boundary

			var pathArray = path.match( /([^\.\[\]]*)/g );
			splitPath.type = pathArray[0];

			for ( var loop = 1, length = pathArray.length; loop < length; loop++ ) {

				// Ignore empty matches

				if ( pathArray[loop] === '' ) {
					continue;
				}

				if ( splitPath.names === undefined ) {
					splitPath.names = [];
				}

				// Strip surrounding spaces and quotes (eg. foo[ 'bar' ])

				var stripQuotes = pathArray[loop].match( /^(?:\s*(?:\'|\"))([^\']*)(?:(?:\'|\")\s*)$/ );

				if ( stripQuotes !== null && stripQuotes[1] !== undefined ) {
					pathArray[loop] = stripQuotes[1];
				}

				splitPath.names.push( pathArray[loop] );
			}
		}

		return splitPath;
	};

	/**
	 * Appends the 'path' property from the given Metawidget to the 'name'
	 * property in the given attributes.
	 */

	metawidget.util.appendPath = function( attributes, mw ) {

		if ( mw.path !== undefined ) {
			return mw.path + '.' + attributes.name;
		}

		if ( mw.toInspect !== undefined ) {
			return typeof ( mw.toInspect ) + '.' + attributes.name;
		}

		return 'object.' + attributes.name;
	};

	/**
	 * Traverses the given 'toInspect' along properties defined by the array of
	 * 'names'.
	 * 
	 * @param intermediateName
	 *            optional intermediate name to traverse at each step. Useful
	 *            for traversing JSON Schemas (e.g.
	 *            toInspect->properties->name1->properties->name2)
	 */

	metawidget.util.traversePath = function( toInspect, names, intermediateName ) {

		if ( toInspect === undefined ) {
			return undefined;
		}

		if ( names !== undefined ) {
			for ( var loop = 0, length = names.length; loop < length; loop++ ) {

				if ( intermediateName !== undefined ) {
					toInspect = toInspect[intermediateName];

					if ( toInspect === undefined ) {
						return undefined;
					}
				}

				toInspect = toInspect[names[loop]];

				// We don't need to worry about array indexes here: they should
				// have been parsed out by splitPath

				if ( toInspect === undefined ) {
					return undefined;
				}
			}
		}

		return toInspect;
	};

	/**
	 * Return an array of the given inspection result's properties, sorted by
	 * 'propertyOrder' (if any).
	 * <p>
	 * See: https://github.com/json-stylesheet/json-stylesheet/issues/1
	 * https://github.com/json-schema/json-schema/issues/87
	 */

	metawidget.util.getSortedInspectionResultProperties = function( inspectionResult ) {

		// Extract the given inspection result's properties into an array...

		var sortedProperties = [];

		for ( var propertyName in inspectionResult.properties ) {

			var properties = inspectionResult.properties[propertyName];
			sortedProperties.push( properties );

			properties.name = propertyName;
			properties._syntheticOrder = sortedProperties.length;
		}

		// ...sort the array...

		sortedProperties.sort( function( a, b ) {

			if ( a.propertyOrder === undefined ) {
				if ( b.propertyOrder === undefined ) {
					return ( a._syntheticOrder - b._syntheticOrder );
				}
				return 1;
			}

			if ( b.propertyOrder === undefined ) {
				return -1;
			}

			var diff = ( a.propertyOrder - b.propertyOrder );

			if ( diff === 0 ) {
				return ( a._syntheticOrder - b._syntheticOrder );
			}

			return diff;
		} );

		// ...and return it

		return sortedProperties;
	};

	/**
	 * Combines the given first inspection result with the given second
	 * inspection result.
	 * <p>
	 * Inspection results are expected to be JSON Schema objects. They are
	 * combined based on their property name. If no elements match, new
	 * properties are appended.
	 */

	metawidget.util.combineInspectionResults = function( existingInspectionResult, newInspectionResult ) {

		// Inspector may return undefined

		if ( newInspectionResult === undefined ) {
			return;
		}

		// Combine based on propertyName

		_copyPrimitives( newInspectionResult, existingInspectionResult );

		if ( newInspectionResult.properties === undefined ) {
			return;
		}

		existingInspectionResult.properties = existingInspectionResult.properties || {};

		for ( var propertyName in newInspectionResult.properties ) {

			existingInspectionResult.properties[propertyName] = existingInspectionResult.properties[propertyName] || {};
			_copyPrimitives( newInspectionResult.properties[propertyName], existingInspectionResult.properties[propertyName] );
		}

		//
		// Private methods
		//

		function _copyPrimitives( from, to ) {

			for ( var propertyName in from ) {

				var propertyValue = from[propertyName];

				if ( propertyValue instanceof Array ) {
					to[propertyName] = propertyValue.slice( 0 );
					continue;
				}

				if ( propertyValue instanceof Object ) {
					continue;
				}

				to[propertyName] = from[propertyName];
			}
		}
	};

	/**
	 * Strips the first section off the section attribute (if any).
	 */

	metawidget.util.stripSection = function( attributes ) {

		var section = attributes.section;

		// (undefined means 'no change to current section')

		if ( section === undefined ) {
			return undefined;
		}

		if ( ! ( section instanceof Array ) ) {
			delete attributes.section;
			return section;
		}

		switch ( section.length ) {

			// (empty String means 'end current section')
			case 0:
				delete attributes.section;
				return '';

			case 1:
				delete attributes.section;
				return section[0];

			case 2:
				attributes.section = section.slice( 1 );
				return section[0];
		}
	};

	/**
	 * Sets the given 'toAppend' to the given widget's 'attributeName'. If the
	 * given widget already has a value for 'attributeName', appends a space and
	 * then adds 'toAppend'.
	 */

	metawidget.util.appendToAttribute = function( widget, attributeName, toAppend ) {

		var existingAttribute = widget.getAttribute( attributeName );

		if ( existingAttribute === null ) {
			widget.setAttribute( attributeName, toAppend );
		} else {
			widget.setAttribute( attributeName, existingAttribute + ' ' + toAppend );
		}
	};

	/**
	 * Creates an element by calling <tt>mw.getElement().ownerDocument</tt>
	 * rather than simply <tt>document</tt>. This stops us relying on a
	 * global <tt>document</tt> variable.
	 */

	metawidget.util.createElement = function( mw, element ) {

		return mw.getElement().ownerDocument.createElement( element );
	};

	/**
	 * Creates a text node by calling <tt>mw.getElement().ownerDocument</tt>
	 * rather than simply <tt>document</tt>. This stops us relying on a
	 * global <tt>document</tt> variable.
	 */

	metawidget.util.createTextNode = function( mw, text ) {

		return mw.getElement().ownerDocument.createTextNode( text );
	};
} )();// Metawidget 3.4 (licensed under LGPL)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

var metawidget = metawidget || {};

( function() {

	'use strict';

	/**
	 * @namespace WidgetBuilders.
	 */

	metawidget.widgetbuilder = metawidget.widgetbuilder || {};

	/**
	 * @class Delegates widget building to one or more sub-WidgetBuilders.
	 *        <p>
	 *        Each sub-WidgetBuilder in the list is invoked, in order, calling
	 *        its <code>buildWidget</code> method. The first result is
	 *        returned. If all sub-WidgetBuilders return undefined, undefined is
	 *        returned (the parent Metawidget will generally instantiate a
	 *        nested Metawidget in this case).
	 *        <p>
	 *        Note: the name <em>Composite</em>WidgetBuilder refers to the
	 *        Composite design pattern.
	 */

	metawidget.widgetbuilder.CompositeWidgetBuilder = function( config ) {

		if ( ! ( this instanceof metawidget.widgetbuilder.CompositeWidgetBuilder ) ) {
			throw new Error( 'Constructor called as a function' );
		}

		var _widgetBuilders;

		if ( config.widgetBuilders !== undefined ) {
			_widgetBuilders = config.widgetBuilders.slice( 0 );
		} else {
			_widgetBuilders = config.slice( 0 );
		}

		this.onStartBuild = function() {

			for ( var loop = 0, length = _widgetBuilders.length; loop < length; loop++ ) {

				var widgetBuilder = _widgetBuilders[loop];

				if ( widgetBuilder.onStartBuild !== undefined ) {
					widgetBuilder.onStartBuild();
				}
			}
		};

		this.buildWidget = function( elementName, attributes, mw ) {

			for ( var loop = 0, length = _widgetBuilders.length; loop < length; loop++ ) {

				var widget;
				var widgetBuilder = _widgetBuilders[loop];

				if ( widgetBuilder.buildWidget !== undefined ) {
					widget = widgetBuilder.buildWidget( elementName, attributes, mw );
				} else {
					widget = widgetBuilder( elementName, attributes, mw );
				}

				if ( widget !== undefined ) {
					return widget;
				}
			}
		};

		this.onEndBuild = function() {

			for ( var loop = 0, length = _widgetBuilders.length; loop < length; loop++ ) {

				var widgetBuilder = _widgetBuilders[loop];

				if ( widgetBuilder.onEndBuild !== undefined ) {
					widgetBuilder.onEndBuild();
				}
			}
		};
	};

	/**
	 * @class WidgetBuilder to override widgets based on
	 *        <tt>mw.overriddenNodes</tt>.
	 *        <p>
	 *        Widgets are overridden based on id, not name, because name is not
	 *        legal syntax for many nodes (e.g. <tt>table</tt>).
	 */

	metawidget.widgetbuilder.OverriddenWidgetBuilder = function() {

		if ( ! ( this instanceof metawidget.widgetbuilder.OverriddenWidgetBuilder ) ) {
			throw new Error( 'Constructor called as a function' );
		}
	};

	metawidget.widgetbuilder.OverriddenWidgetBuilder.prototype.buildWidget = function( elementName, attributes, mw ) {

		if ( mw.overriddenNodes === undefined ) {
			return;
		}

		var overrideId = metawidget.util.getId( elementName, attributes, mw );

		for ( var loop = 0, length = mw.overriddenNodes.length; loop < length; loop++ ) {

			var child = mw.overriddenNodes[loop];
			if ( child.nodeType === 1 && child.getAttribute( 'id' ) === overrideId ) {
				child.overridden = true;
				mw.overriddenNodes.splice( loop, 1 );
				return child;
			}
		}
	};

	/**
	 * @class WidgetBuilder for read-only widgets in HTML 5 environments.
	 */

	metawidget.widgetbuilder.ReadOnlyWidgetBuilder = function() {

		if ( ! ( this instanceof metawidget.widgetbuilder.ReadOnlyWidgetBuilder ) ) {
			throw new Error( 'Constructor called as a function' );
		}
	};

	metawidget.widgetbuilder.ReadOnlyWidgetBuilder.prototype.buildWidget = function( elementName, attributes, mw ) {

		// Not read-only?

		if ( !metawidget.util.isTrueOrTrueString( attributes.readOnly ) ) {
			return;
		}

		// Hidden

		if ( metawidget.util.isTrueOrTrueString( attributes.hidden ) || attributes.type === 'function' ) {
			return metawidget.util.createElement( mw, 'stub' );
		}

		if ( attributes['enum'] !== undefined || attributes.type === 'string' || attributes.type === 'boolean' || attributes.type === 'number' || attributes.type === 'date' ) {

			if ( metawidget.util.isTrueOrTrueString( attributes.masked ) ) {

				// Masked (return a couple of nested Stubs, so that we DO still
				// render a label)

				var stub = metawidget.util.createElement( mw, 'stub' );
				stub.appendChild( metawidget.util.createElement( mw, 'stub' ) );
				return stub;
			}

			return metawidget.util.createElement( mw, 'output' );
		}

		// Not simple, but don't expand

		if ( metawidget.util.isTrueOrTrueString( attributes.dontExpand ) ) {
			return metawidget.util.createElement( mw, 'output' );
		}
	};

	/**
	 * WidgetBuilder for pure JavaScript environments.
	 * <p>
	 * Creates native HTML 5 widgets, such as <code>input</code> and
	 * <code>select</code>, to suit the inspected fields.
	 */

	metawidget.widgetbuilder.HtmlWidgetBuilder = function() {

		if ( ! ( this instanceof metawidget.widgetbuilder.HtmlWidgetBuilder ) ) {
			throw new Error( 'Constructor called as a function' );
		}
	};

	metawidget.widgetbuilder.HtmlWidgetBuilder.prototype.buildWidget = function( elementName, attributes, mw ) {

		// Hidden

		if ( metawidget.util.isTrueOrTrueString( attributes.hidden ) ) {
			return metawidget.util.createElement( mw, 'stub' );
		}

		// Select box

		if ( attributes['enum'] !== undefined ) {

			// Multi-select and radio buttons

			if ( attributes.type === 'array' || attributes.componentType !== undefined ) {

				var div = metawidget.util.createElement( mw, 'div' );

				for ( var loop = 0, length = attributes['enum'].length; loop < length; loop++ ) {

					// Uses 'implicit label association':
					// http://www.w3.org/TR/html4/interact/forms.html#h-17.9.1

					var label = metawidget.util.createElement( mw, 'label' );
					var option = metawidget.util.createElement( mw, 'input' );

					if ( attributes.componentType !== undefined ) {
						option.setAttribute( 'type', attributes.componentType );
					} else {
						option.setAttribute( 'type', 'checkbox' );
					}
					option.setAttribute( 'value', attributes['enum'][loop] );
					label.appendChild( option );

					if ( attributes.enumTitles !== undefined && attributes.enumTitles[loop] !== undefined ) {
						label.appendChild( metawidget.util.createTextNode( mw, attributes.enumTitles[loop] ) );
					} else {
						label.appendChild( metawidget.util.createTextNode( mw, attributes['enum'][loop] ) );
					}

					div.appendChild( label );
				}

				return div;
			}

			// Single-select

			var select = metawidget.util.createElement( mw, 'select' );

			if ( !metawidget.util.isTrueOrTrueString( attributes.required ) ) {
				select.appendChild( metawidget.util.createElement( mw, 'option' ) );
			}

			for ( var loop = 0, length = attributes['enum'].length; loop < length; loop++ ) {
				var option = metawidget.util.createElement( mw, 'option' );

				// HtmlUnit needs an 'option' to have a 'value', even if the
				// same as the innerHTML

				option.setAttribute( 'value', attributes['enum'][loop] );

				if ( attributes.enumTitles !== undefined && attributes.enumTitles[loop] !== undefined ) {
					option.innerHTML = attributes.enumTitles[loop];
				} else {
					option.innerHTML = attributes['enum'][loop];
				}

				select.appendChild( option );
			}
			return select;
		}

		// Button

		if ( attributes.type === 'function' ) {
			var button = metawidget.util.createElement( mw, 'button' );
			button.innerHTML = metawidget.util.getLabelString( attributes, mw );
			return button;
		}

		// Number

		if ( attributes.type === 'number' ) {

			if ( attributes.minimum !== undefined && attributes.maximum !== undefined ) {
				var range = metawidget.util.createElement( mw, 'input' );
				range.setAttribute( 'type', 'range' );
				range.setAttribute( 'min', attributes.minimum );
				range.setAttribute( 'max', attributes.maximum );
				return range;
			}

			var number = metawidget.util.createElement( mw, 'input' );
			number.setAttribute( 'type', 'number' );
			
			if ( attributes.minimum !== undefined  ) {
				number.setAttribute( 'min', attributes.minimum );
			} else if ( attributes.maximum !== undefined  ) {
				number.setAttribute( 'max', attributes.maximum );
			}

			return number;
		}

		// Boolean

		if ( attributes.type === 'boolean' ) {
			var checkbox = metawidget.util.createElement( mw, 'input' );
			checkbox.setAttribute( 'type', 'checkbox' );
			return checkbox;
		}

		// Date

		if ( attributes.type === 'date' ) {
			var date = metawidget.util.createElement( mw, 'input' );
			date.setAttribute( 'type', 'date' );
			return date;
		}

		// String

		if ( attributes.type === 'string' ) {

			if ( metawidget.util.isTrueOrTrueString( attributes.masked ) ) {
				var password = metawidget.util.createElement( mw, 'input' );
				password.setAttribute( 'type', 'password' );

				if ( attributes.maxLength !== undefined ) {
					password.setAttribute( 'maxlength', attributes.maxLength );
				}

				return password;
			}

			if ( metawidget.util.isTrueOrTrueString( attributes.large ) ) {
				return metawidget.util.createElement( mw, 'textarea' );
			}

			var text = metawidget.util.createElement( mw, 'input' );
			text.setAttribute( 'type', 'text' );

			if ( attributes.maxLength !== undefined ) {
				text.setAttribute( 'maxlength', attributes.maxLength );
			}

			return text;
		}

		// Collection

		if ( attributes.type === 'array' ) {
			return this.createTable( elementName, attributes, mw );
		}

		// Not simple, but don't expand

		if ( metawidget.util.isTrueOrTrueString( attributes.dontExpand ) ) {
			var text = metawidget.util.createElement( mw, 'input' );
			text.setAttribute( 'type', 'text' );
			return text;
		}
	};

	/**
	 * Create a table populated with the contents of an array property.
	 * <p>
	 * Subclasses may override this method to customize table creation.
	 * Alternatively, they could override one of the sub-methods
	 * <tt>addHeader</tt> or <tt>addColumn</tt>.
	 */

	metawidget.widgetbuilder.HtmlWidgetBuilder.prototype.createTable = function( elementName, attributes, mw ) {

		var table = metawidget.util.createElement( mw, 'table' );

		// Lookup the actual value

		var value;
		var typeAndNames = metawidget.util.splitPath( mw.path );
		var toInspect = metawidget.util.traversePath( mw.toInspect, typeAndNames.names );

		if ( elementName !== 'entity' && toInspect !== undefined ) {
			value = toInspect[attributes.name];
		} else {
			value = toInspect;
		}

		if ( value !== undefined && value.length > 0 ) {

			var firstValue = value[0];
			var inspectionResult = mw.inspect( firstValue );
			
			if ( inspectionResult.properties === undefined ) {
				
				// Simple, single-column table
	
				var tbody = metawidget.util.createElement( mw, 'tbody' );
				table.appendChild( tbody );
	
				for ( var row = 0, rows = value.length; row < rows; row++ ) {
	
					var tr = metawidget.util.createElement( mw, 'tr' );
					tbody.appendChild( tr );
					this.addColumn( tr, value[row], inspectionResult, mw );
				}

			} else {
				var inspectionResultProperties = metawidget.util.getSortedInspectionResultProperties( inspectionResult );
	
				// Create headers
	
				var thead = metawidget.util.createElement( mw, 'thead' );
				table.appendChild( thead );
				var tr = metawidget.util.createElement( mw, 'tr' );
				thead.appendChild( tr );
	
				var columnAttributes = [];
	
				for ( var loop = 0, length = inspectionResultProperties.length; loop < length; loop++ ) {
	
					var columnAttribute = inspectionResultProperties[loop];
	
					if ( this.addHeader( tr, columnAttribute, mw ) ) {
						columnAttributes.push( columnAttribute );
					}
				}
	
				// Create body
	
				var tbody = metawidget.util.createElement( mw, 'tbody' );
				table.appendChild( tbody );
	
				for ( var row = 0, rows = value.length; row < rows; row++ ) {
	
					var tr = metawidget.util.createElement( mw, 'tr' );
					tbody.appendChild( tr );
	
					for ( var loop = 0, length = columnAttributes.length; loop < length; loop++ ) {
	
						this.addColumn( tr, value[row], columnAttributes[loop], mw );
					}
				}
			}
		}

		return table;
	};

	/**
	 * Add a header to the given row, displaying the given value. Subclasses may
	 * override this method to suppress certain columns.
	 * 
	 * @returns true if a header was added, false otherwise
	 */

	metawidget.widgetbuilder.HtmlWidgetBuilder.prototype.addHeader = function( tr, attributes, mw ) {

		var th = metawidget.util.createElement( mw, 'th' );
		th.innerHTML = metawidget.util.getLabelString( attributes, mw );
		tr.appendChild( th );

		return true;
	};

	/**
	 * Add a column to the given row, displaying the given value. Subclasses may
	 * override this method to modify the column contents (for example, to wrap
	 * them in an anchor tag).
	 * 
	 * @return the added column, or undefined if no column was added. This can
	 *         be useful for subclasses
	 */

	metawidget.widgetbuilder.HtmlWidgetBuilder.prototype.addColumn = function( tr, value, attributes, mw ) {

		var td = metawidget.util.createElement( mw, 'td' );
		
		if ( attributes.name === undefined ) {
			td.innerHTML = value;
		} else {
			td.innerHTML = value[attributes.name];
		}
		tr.appendChild( td );

		return td;
	};
} )();// Metawidget 3.4 (licensed under LGPL)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

var metawidget = metawidget || {};

( function() {

	'use strict';

	/**
	 * @namespace WidgetProcessors.
	 */

	metawidget.widgetprocessor = metawidget.widgetprocessor || {};

	/**
	 * @class WidgetProcessor that sets the HTML 'id' attribute.
	 */

	metawidget.widgetprocessor.IdProcessor = function() {

		if ( ! ( this instanceof metawidget.widgetprocessor.IdProcessor ) ) {
			throw new Error( 'Constructor called as a function' );
		}
	};

	metawidget.widgetprocessor.IdProcessor.prototype.processWidget = function( widget, elementName, attributes, mw ) {

		// Dangerous to reassign an id. For example, some JQuery UI widgets
		// assign temporary ids when they wrap widgets

		if ( !widget.hasAttribute( 'id' ) ) {
			var id = metawidget.util.getId( elementName, attributes, mw );

			if ( id !== undefined ) {
				widget.setAttribute( 'id', id );
			}
		}

		return widget;
	};

	/**
	 * @class WidgetProcessor that sets the HTML 5 'required' attribute.
	 */

	metawidget.widgetprocessor.RequiredAttributeProcessor = function() {

		if ( ! ( this instanceof metawidget.widgetprocessor.RequiredAttributeProcessor ) ) {
			throw new Error( 'Constructor called as a function' );
		}
	};

	metawidget.widgetprocessor.RequiredAttributeProcessor.prototype.processWidget = function( widget, elementName, attributes, mw ) {

		if ( metawidget.util.isTrueOrTrueString( attributes.required ) ) {
			widget.setAttribute( 'required', 'required' );
		}

		return widget;
	};

	/**
	 * @class Simple data/action binding implementation. Frameworks that supply
	 *        their own data-binding mechanisms (such as Angular JS) should
	 *        override this with their own WidgetProcessor.
	 */

	metawidget.widgetprocessor.SimpleBindingProcessor = function() {

		if ( ! ( this instanceof metawidget.widgetprocessor.SimpleBindingProcessor ) ) {
			throw new Error( 'Constructor called as a function' );
		}
	};

	metawidget.widgetprocessor.SimpleBindingProcessor.prototype.onStartBuild = function( mw ) {

		mw._simpleBindingProcessor = {};
	};

	metawidget.widgetprocessor.SimpleBindingProcessor.prototype.processWidget = function( widget, elementName, attributes, mw ) {

		if ( widget.tagName === 'BUTTON' ) {
			widget.onclick = function() {

				try {
					return mw.toInspect[attributes.name]();
				} catch ( e ) {
					if ( alert ) {
						alert( e );
					} else {
						throw e;
					}
				}
			};

			return widget;
		}

		var value;
		var typeAndNames = metawidget.util.splitPath( mw.path );

		if ( elementName === 'entity' ) {

			value = metawidget.util.traversePath( mw.toInspect, typeAndNames.names );

			// We cannot typically save to a top-level value. However if
			// we are using a path, we can navigate to its parent

			if ( typeAndNames.names !== undefined ) {
				elementName = 'property';
				mw._simpleBindingProcessor.topLevel = true;
			}
		} else {
			var toInspect = metawidget.util.traversePath( mw.toInspect, typeAndNames.names );

			if ( toInspect !== undefined ) {
				value = toInspect[attributes.name];
			} else {
				value = undefined;
			}
		}

		var isBindable = ( widget.tagName === 'INPUT' || widget.tagName === 'SELECT' || widget.tagName === 'TEXTAREA' );

		if ( isBindable === true && widget.hasAttribute( 'id' ) ) {

			// Standard HTML needs 'name', not 'id', for binding

			widget.setAttribute( 'name', widget.getAttribute( 'id' ) );
		}

		// Check 'not undefined', rather than 'if value', in case value is a
		// boolean of false
		//
		// Note: this is a general convention throughout Metawidget, as
		// JavaScript has a surprisingly large number of 'falsy' values)

		if ( value !== undefined ) {
			if ( widget.tagName === 'OUTPUT' || widget.tagName === 'TEXTAREA' ) {

				widget.innerHTML = value;

				// Special support for enumTitles

				if ( attributes.enumTitles !== undefined ) {
					var indexOf = attributes['enum'].indexOf( value );

					if ( indexOf !== -1 && indexOf < attributes.enumTitles.length ) {
						widget.innerHTML = attributes.enumTitles[indexOf];
					}
				}
			} else if ( widget.tagName === 'INPUT' && widget.getAttribute( 'type' ) === 'checkbox' ) {
				widget.checked = value;
			} else if ( isBindable === true ) {
				widget.value = value;
			}
		}

		if ( elementName !== 'entity' ) {
			if ( isBindable === true || widget.metawidget !== undefined ) {
				mw._simpleBindingProcessor.bindings = mw._simpleBindingProcessor.bindings || [];
				mw._simpleBindingProcessor.bindings[attributes.name] = widget;
			}
		}

		return widget;
	};

	/**
	 * Save the bindings associated with the given Metawidget.
	 */

	metawidget.widgetprocessor.SimpleBindingProcessor.prototype.save = function( mw ) {

		var toInspect;

		// Traverse to the parent...

		var typeAndNames = metawidget.util.splitPath( mw.path );

		if ( typeAndNames.names === undefined ) {
			toInspect = metawidget.util.traversePath( mw.toInspect );
		} else {
			var namesToParent = typeAndNames.names.slice( 0, typeAndNames.names.length - 1 );
			var parent = metawidget.util.traversePath( mw.toInspect, namesToParent );

			// ...then to the child...

			if ( mw._simpleBindingProcessor.topLevel === true ) {
				toInspect = parent;
			} else {
				var childName = typeAndNames.names[typeAndNames.names.length - 1];
				toInspect = parent[childName];

				// ...create the child 'just in time' if necessary...

				if ( toInspect === undefined ) {
					toInspect = {};
					parent[childName] = toInspect;
				}
			}
		}

		// ...and populate it

		for ( var name in mw._simpleBindingProcessor.bindings ) {

			var widget = mw._simpleBindingProcessor.bindings[name];

			if ( widget.metawidget !== undefined ) {
				this.save( widget.metawidget );
				continue;
			}

			if ( widget.getAttribute( 'type' ) === 'checkbox' ) {
				toInspect[name] = widget.checked;
				continue;
			}

			toInspect[name] = widget.value;
		}
	};
} )();// Metawidget 3.4 (licensed under LGPL)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

/**
 * @namespace Metawidget for pure JavaScript environments.
 */

var metawidget = metawidget || {};

( function() {

	'use strict';

	/**
	 * Pure JavaScript Metawidget.
	 * 
	 * @param element
	 *            the element to populate with UI components matching the
	 *            properties of the business object
	 * @param config
	 *            optional configuration object (see
	 *            metawidget.Pipeline.configure)
	 * @returns {metawidget.Metawidget}
	 */

	metawidget.Metawidget = function( element, config ) {

		if ( ! ( this instanceof metawidget.Metawidget ) ) {
			throw new Error( 'Constructor called as a function' );
		}

		// Pipeline (private)

		var _pipeline = new metawidget.Pipeline( element );
		_pipeline.buildNestedMetawidget = function( attributes, mw ) {

			var nestedWidget = element.ownerDocument.createElement( 'div' );

			// Duck-type our 'pipeline' as the 'config' of the nested
			// Metawidget. This neatly passes everything down, including a
			// decremented 'maximumInspectionDepth'

			var nestedMetawidget = new metawidget.Metawidget( nestedWidget, _pipeline );
			nestedMetawidget.toInspect = mw.toInspect;
			nestedMetawidget.path = metawidget.util.appendPath( attributes, mw );
			nestedMetawidget.readOnly = mw.readOnly || metawidget.util.isTrueOrTrueString( attributes.readOnly );

			// Attach ourselves as a property of the tag, rather than try to
			// 'extend' the built-in HTML tags. This is used by
			// SimpleBindingProcessor, among others

			nestedWidget.metawidget = nestedMetawidget;
			nestedMetawidget.buildWidgets();

			return nestedWidget;
		};

		// Configure defaults

		_pipeline.inspector = new metawidget.inspector.PropertyTypeInspector();
		_pipeline.widgetBuilder = new metawidget.widgetbuilder.CompositeWidgetBuilder( [ new metawidget.widgetbuilder.OverriddenWidgetBuilder(), new metawidget.widgetbuilder.ReadOnlyWidgetBuilder(),
				new metawidget.widgetbuilder.HtmlWidgetBuilder() ] );
		_pipeline.widgetProcessors = [ new metawidget.widgetprocessor.IdProcessor(), new metawidget.widgetprocessor.RequiredAttributeProcessor(),
				new metawidget.widgetprocessor.SimpleBindingProcessor() ];
		_pipeline.layout = new metawidget.layout.HeadingTagLayoutDecorator( new metawidget.layout.TableLayout() );
		_pipeline.configure( config );

		// First time in, capture the contents of the Metawidget, if any
		// (private)

		var _overriddenNodes = [];

		while ( element.childNodes.length > 0 ) {
			var childNode = element.childNodes[0];
			element.removeChild( childNode );

			if ( childNode.nodeType === 1 ) {
				_overriddenNodes.push( childNode );
			}
		}

		//
		// Public methods
		//

		this.getWidgetProcessor = function( testInstanceOf ) {

			return _pipeline.getWidgetProcessor( testInstanceOf );
		};

		this.setLayout = function( layout ) {

			_pipeline.layout = layout;
		};

		/**
		 * Useful for WidgetBuilders to perform nested inspections (eg. for
		 * Collections).
		 */

		this.inspect = function( toInspect, type, names ) {

			return _pipeline.inspect( toInspect, type, names, this );
		};

		this.buildWidgets = function( inspectionResult ) {

			// Defensive copy

			this.overriddenNodes = [];

			for ( var loop = 0, length = _overriddenNodes.length; loop < length; loop++ ) {
				this.overriddenNodes.push( _overriddenNodes[loop].cloneNode( true ) );
			}

			// Inspect (if necessary)

			if ( inspectionResult === undefined ) {

				// Safeguard against improperly implementing:
				// http://blog.kennardconsulting.com/2013/02/metawidget-and-rest.html

				if ( arguments.length > 0 ) {
					throw new Error( "Calling buildWidgets( undefined ) may cause infinite loop. Check your argument, or pass no arguments instead" );
				}

				var splitPath = metawidget.util.splitPath( this.path );
				inspectionResult = _pipeline.inspect( this.toInspect, splitPath.type, splitPath.names, this );
			}

			// Build widgets

			_pipeline.buildWidgets( inspectionResult, this );
		};

		/**
		 * Returns the element this Metawidget is attached to.
		 */

		this.getElement = function() {

			return _pipeline.element;
		};
	};

	/**
	 * @class Convenience implementation for implementing pipelines (see
	 *        http://metawidget.org/doc/reference/en/html/ch02.html).
	 *        <p>
	 *        Specifically, BasePipeline provides support for:
	 *        </p>
	 *        <ul>
	 *        <li>Inspectors, InspectionResultProcessors, WidgetBuilders,
	 *        WidgetProcessors and Layouts</li>
	 *        <li>single/compound widgets</li>
	 *        <li>stubs/stub attributes</li>
	 *        <li>read-only/active widgets</li>
	 *        <li>maximum inspection depth</li>
	 *        </ul>
	 *        <p>
	 *        Clients should override 'buildNestedMetawidget'.
	 *        </p>
	 */

	metawidget.Pipeline = function( element ) {

		if ( ! ( this instanceof metawidget.Pipeline ) ) {
			throw new Error( 'Constructor called as a function' );
		}

		this.inspectionResultProcessors = [];
		this.widgetProcessors = [];
		this.element = element;
		this.maximumInspectionDepth = 10;
	};

	/**
	 * Configures the pipeline using the given config object.
	 * <p>
	 * This method is separate to the constructor, so that subclasses can set
	 * defaults. The following configuration properties are supported:
	 * <ul>
	 * <li>inspector - an Inspector</li>
	 * <li>inspectionResultProcessors - an array of InspectionResultProcessors</li>
	 * <li>widgetBuilder - a WidgetBuilder</li>
	 * <li>widgetProcessors - an array of WidgetProcessors</li>
	 * <li>layout - a Layout</li>
	 * </ul>
	 * 
	 * @param config
	 *            the config object to use. This can be an array, in which case
	 *            multiple configs will be applied (in the order they appear in
	 *            the array)
	 */

	metawidget.Pipeline.prototype.configure = function( config ) {

		if ( config === undefined ) {
			return;
		}

		// Support arrays of configs

		if ( config instanceof Array ) {
			for ( var loop = 0, length = config.length; loop < length; loop++ ) {
				this.configure( config[loop] );
			}
			return;
		}
		if ( config.inspector !== undefined ) {
			this.inspector = config.inspector;
		}
		if ( config.inspectionResultProcessors !== undefined ) {
			this.inspectionResultProcessors = config.inspectionResultProcessors.slice( 0 );
		}

		// Support adding to the existing array of InspectionResultProcessors
		// (it may be hard for clients to redefine the originals)

		if ( config.addInspectionResultProcessors !== undefined ) {
			for ( var loop = 0, length = config.addInspectionResultProcessors.length; loop < length; loop++ ) {
				this.inspectionResultProcessors.push( config.addInspectionResultProcessors[loop] );
			}
		}
		if ( config.widgetBuilder !== undefined ) {
			this.widgetBuilder = config.widgetBuilder;
		}
		if ( config.widgetProcessors !== undefined ) {
			this.widgetProcessors = config.widgetProcessors.slice( 0 );
		}

		// Support prepending/adding to the existing array of WidgetProcessors
		// (it may be hard for clients to redefine the originals)

		if ( config.prependWidgetProcessors !== undefined ) {
			for ( var loop = 0, length = config.prependWidgetProcessors.length; loop < length; loop++ ) {
				this.widgetProcessors.splice( loop, 0, config.prependWidgetProcessors[loop] );
			}
		}
		if ( config.addWidgetProcessors !== undefined ) {
			for ( var loop = 0, length = config.addWidgetProcessors.length; loop < length; loop++ ) {
				this.widgetProcessors.push( config.addWidgetProcessors[loop] );
			}
		}
		if ( config.layout !== undefined ) {
			this.layout = config.layout;
		}

		// Safeguard against infinite recursion

		if ( config.maximumInspectionDepth !== undefined ) {
			this.maximumInspectionDepth = config.maximumInspectionDepth - 1;
		}
	};

	/**
	 * Searches the pipeline's current list of WidgetProcessors and matches each
	 * against the given function
	 * 
	 * @param testInstanceOf
	 *            a function that accepts a WidgetProcessor and will perform an
	 *            'instanceof' test on it
	 */

	metawidget.Pipeline.prototype.getWidgetProcessor = function( testInstanceOf ) {

		for ( var loop = 0, length = this.widgetProcessors.length; loop < length; loop++ ) {

			var widgetProcessor = this.widgetProcessors[loop];

			if ( testInstanceOf( widgetProcessor ) ) {
				return widgetProcessor;
			}
		}
	};

	/**
	 * Inspect the 'toInspect' according to its 'type' and 'names', and return
	 * the result as a JSON String.
	 * <p>
	 * This method mirrors the <code>Inspector</code> interface. Internally it
	 * looks up the Inspector to use. It is a useful hook for subclasses wishing
	 * to inspect different Objects using our same <code>Inspector</code>.
	 * <p>
	 * In addition, this method runs the <code>InspectionResultProcessors</code>.
	 */

	metawidget.Pipeline.prototype.inspect = function( toInspect, type, names, mw ) {

		// Inspector

		var inspectionResult;

		if ( this.inspector.inspect !== undefined ) {
			inspectionResult = this.inspector.inspect( toInspect, type, names );
		} else {
			inspectionResult = this.inspector( toInspect, type, names );
		}

		// Inspector may return undefined

		if ( inspectionResult === undefined ) {
			return;
		}

		// InspectionResultProcessors

		for ( var loop = 0, length = this.inspectionResultProcessors.length; loop < length; loop++ ) {

			var inspectionResultProcessor = this.inspectionResultProcessors[loop];

			if ( inspectionResultProcessor.processInspectionResult !== undefined ) {
				inspectionResult = inspectionResultProcessor.processInspectionResult( inspectionResult, mw, toInspect, type, names );
			} else {
				inspectionResult = inspectionResultProcessor( inspectionResult, mw, toInspect, type, names );
			}

			// InspectionResultProcessor may return undefined

			if ( inspectionResult === undefined ) {
				return;
			}
		}

		return inspectionResult;
	};

	/**
	 * Build widgets from the given JSON inspection result.
	 * <p>
	 * Note: the Pipeline expects the JSON to be passed in externally, rather
	 * than fetching it itself, because some JSON inspections may be
	 * asynchronous.
	 * 
	 * @param inspectionResult
	 *            array of metadata to base widgets on.
	 * @param mw
	 *            Metawidget instance that will be passed down the pipeline
	 *            (WidgetBuilders, WidgetProcessors etc). Expected to have
	 *            'toInspect', 'path' and 'readOnly'.
	 */

	metawidget.Pipeline.prototype.buildWidgets = function( inspectionResult, mw ) {

		// Clear existing contents

		while ( this.element.childNodes.length > 0 ) {
			this.element.removeChild( this.element.childNodes[0] );
		}

		_startBuild( this, mw );

		// Build top-level widget...

		if ( inspectionResult !== undefined ) {

			var copiedAttributes = _forceReadOnly( inspectionResult, mw, 'properties' );
			var elementName = "entity";
			var widget = _buildWidget( this, elementName, copiedAttributes, mw );

			if ( widget !== undefined ) {

				widget = _processWidget( this, widget, elementName, copiedAttributes, mw );

				if ( widget !== undefined ) {
					this.layoutWidget( widget, elementName, copiedAttributes, this.element, mw );
				}

			} else {

				// ...or try compound widget

				var inspectionResultProperties = metawidget.util.getSortedInspectionResultProperties( inspectionResult );

				for ( var loop = 0, length = inspectionResultProperties.length; loop < length; loop++ ) {

					copiedAttributes = _forceReadOnly( inspectionResultProperties[loop], mw );
					var elementName;

					if ( copiedAttributes.type === 'function' ) {
						elementName = "action";
					} else {
						elementName = "property";
					}

					var widget = _buildWidget( this, elementName, copiedAttributes, mw );

					if ( widget === undefined ) {

						if ( this.maximumInspectionDepth <= 0 ) {
							continue;
						}

						widget = this.buildNestedMetawidget( copiedAttributes, mw );

						if ( widget === undefined ) {
							continue;
						}
					}

					widget = _processWidget( this, widget, elementName, copiedAttributes, mw );

					if ( widget !== undefined ) {
						this.layoutWidget( widget, elementName, copiedAttributes, this.element, mw );
					}
				}
			}
		}

		// Even if no inspectors match, we still call startBuild()/endBuild()
		// because you can use a Metawidget purely for layout, with no
		// inspection

		_endBuild( this, mw );
		return;

		//
		// Private methods
		//

		/**
		 * Defensively copies the attributes (in case something like
		 * stripSection changes them) and adds 'readOnly' if the given
		 * Metawidget is readOnly.
		 */

		function _forceReadOnly( attributes, mw, excludes ) {

			var copiedAttributes = {};

			for ( var name in attributes ) {

				if ( excludes !== undefined && excludes.indexOf( name ) !== -1 ) {
					continue;
				}

				copiedAttributes[name] = attributes[name];
			}

			// Try to keep the exact nature of the 'readOnly' mechanism (i.e.
			// set on attribute, or set on overall Metawidget) out of the
			// WidgetBuilders/WidgetProcessors/Layouts. This is because not
			// everybody will need/want a Metawidget-level 'setReadOnly'

			if ( mw.readOnly === true ) {
				copiedAttributes.readOnly = 'true';
			}

			return copiedAttributes;
		}

		function _startBuild( pipeline, mw ) {

			if ( pipeline.widgetBuilder.onStartBuild !== undefined ) {
				pipeline.widgetBuilder.onStartBuild( mw );
			}

			for ( var loop = 0, length = pipeline.widgetProcessors.length; loop < length; loop++ ) {

				var widgetProcessor = pipeline.widgetProcessors[loop];

				if ( widgetProcessor.onStartBuild !== undefined ) {
					widgetProcessor.onStartBuild( mw );
				}
			}

			if ( pipeline.layout.onStartBuild !== undefined ) {
				pipeline.layout.onStartBuild( mw );
			}

			if ( pipeline.layout.startContainerLayout !== undefined ) {
				pipeline.layout.startContainerLayout( pipeline.element, mw );
			}
		}

		function _buildWidget( pipeline, elementName, attributes, mw ) {

			if ( pipeline.widgetBuilder.buildWidget !== undefined ) {
				return pipeline.widgetBuilder.buildWidget( elementName, attributes, mw );
			}

			return pipeline.widgetBuilder( elementName, attributes, mw );
		}

		function _processWidget( pipeline, widget, elementName, attributes, mw ) {

			for ( var loop = 0, length = pipeline.widgetProcessors.length; loop < length; loop++ ) {

				var widgetProcessor = pipeline.widgetProcessors[loop];

				if ( widgetProcessor.processWidget !== undefined ) {
					widget = widgetProcessor.processWidget( widget, elementName, attributes, mw );
				} else {
					widget = widgetProcessor( widget, elementName, attributes, mw );
				}

				if ( widget === undefined ) {
					return;
				}
			}

			return widget;
		}

		function _endBuild( pipeline, mw ) {

			if ( mw.onEndBuild !== undefined ) {
				mw.onEndBuild();
			} else {
				while ( mw.overriddenNodes.length > 0 ) {

					var child = mw.overriddenNodes[0];
					mw.overriddenNodes.splice( 0, 1 );

					// Unused facets don't count

					if ( child.tagName === 'FACET' ) {
						continue;
					}

					// Stubs can supply their own metadata (such as 'label')

					var childAttributes = {
						section: ''
					};

					if ( child.tagName === 'STUB' ) {
						for ( var loop = 0, length = child.attributes.length; loop < length; loop++ ) {
							var prop = child.attributes[loop];
							childAttributes[prop.nodeName] = prop.nodeValue;
						}
					}

					// Manually created components default to no section

					pipeline.layoutWidget( child, "property", childAttributes, pipeline.element, mw );
				}
			}

			// End all stages of the pipeline

			if ( pipeline.layout.endContainerLayout !== undefined ) {
				pipeline.layout.endContainerLayout( pipeline.element, mw );
			}

			if ( pipeline.layout.onEndBuild !== undefined ) {
				pipeline.layout.onEndBuild( mw );
			}

			for ( var loop = 0, length = pipeline.widgetProcessors.length; loop < length; loop++ ) {

				var widgetProcessor = pipeline.widgetProcessors[loop];

				if ( widgetProcessor.onEndBuild !== undefined ) {
					widgetProcessor.onEndBuild( mw );
				}
			}

			if ( pipeline.widgetBuilder.onEndBuild !== undefined ) {
				pipeline.widgetBuilder.onEndBuild( mw );
			}
		}
	};

	metawidget.Pipeline.prototype.layoutWidget = function( widget, elementName, attributes, container, mw ) {

		if ( this.layout.layoutWidget !== undefined ) {
			this.layout.layoutWidget( widget, elementName, attributes, container, mw );
			return;
		}

		this.layout( widget, elementName, attributes, container, mw );
	};

	/**
	 * Subclasses should override this method to create a nested Metawidget,
	 * using their preferred widget creation methodology.
	 */

	metawidget.Pipeline.prototype.buildNestedMetawidget = function( attributes, mw ) {

		return undefined;
	};
} )();module.exports = metawidget;