datatypes w = "http://whattf.org/datatype-draft"

# #####################################################################
##  RELAX NG Schema for HTML 5: Web Forms 1.0 markup                  #
# #####################################################################

## Shared attributes for form controls

	common-form.attrs =
		(	common-form.attrs.name?
		&	common-form.attrs.disabled?
		)
		
	common-form.attrs.name = 
		attribute name {
			form.data.nonemptystring
		}

	common-form.attrs.disabled = 
		attribute disabled {
			w:string "disabled" | w:string ""
		}

	shared-form.attrs.readonly = 
		attribute readonly {
			w:string "readonly" | w:string ""
		}

	shared-form.attrs.maxlength = 
		attribute maxlength {
			common.data.integer.non-negative
		}

	shared-form.attrs.size = 
		attribute size {
			common.data.integer.positive
		}
	
	# REVISIT tabindex goes in common.attrs

## Shared attributes for <input>
	
	input.attrs.checked = 
		attribute checked {
			w:string "checked" | w:string ""
		}

## Text Field: <input type='text'>

	input.text.elem = 
		element input { input.text.attrs }
	input.text.attrs = 
		(	common.attrs
		&	common-form.attrs
		&	input.text.attrs.type?
		&	shared-form.attrs.maxlength? 
		&	shared-form.attrs.readonly?
		&	shared-form.attrs.size?
		&	input.text.attrs.value? 
		&	(	common.attrs.aria.implicit.textbox
			|	common.attrs.aria.role.textbox
			|	common.attrs.aria.role.combobox
			|	common.attrs.aria.role.searchbox
			|	common.attrs.aria.role.spinbutton
			)?
		)
		input.text.attrs.type = 
			attribute type {
				w:string "text"
			}
		input.text.attrs.value =
			attribute value {
				form.data.stringwithoutlinebreaks
			}
		
	input.elem = input.text.elem

## Password Field: <input type='password'>

	input.password.elem = 
		element input { input.password.attrs }
	input.password.attrs = 
		(	common.attrs
		&	common-form.attrs
		&	input.password.attrs.type
		&	shared-form.attrs.maxlength? 
		&	shared-form.attrs.readonly? 
		&	shared-form.attrs.size?
		&	input.password.attrs.value? 
		)
		input.password.attrs.type = 
			attribute type {
				w:string "password"
			}
		input.password.attrs.value =
			attribute value {
				form.data.stringwithoutlinebreaks
			}
		
	input.elem |= input.password.elem
		
## Checkbox: <input type='checkbox'>

	input.checkbox.elem = 
		element input { input.checkbox.attrs }
	input.checkbox.attrs = 
		(	common.attrs
		&	common-form.attrs
		&	input.checkbox.attrs.type
		&	input.attrs.checked? 
		&	input.checkbox.attrs.value? 
		&	(	common.attrs.aria.implicit.checkbox
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.menuitemcheckbox-checked-not-required
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.switch-checked-not-required
			)?
		)
		input.checkbox.attrs.type = 
			attribute type {
				w:string "checkbox"
			}
		input.checkbox.attrs.value =
			attribute value {
				string #REVISIT require non-empty value?
			}
		
	input.elem |= input.checkbox.elem
	
## Radiobutton: <input type='radio'>

	input.radio.elem = 
		element input { input.radio.attrs }
	input.radio.attrs = 
		(	common.attrs
		&	common-form.attrs
		&	input.radio.attrs.type
		&	input.attrs.checked? 
		&	input.radio.attrs.value? 
		&	(	common.attrs.aria.implicit.radio
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.menuitemradio-checked-not-required
			)?
		)
		input.radio.attrs.type = 
			attribute type {
				w:string "radio"
			}
		input.radio.attrs.value =
			attribute value {
				string #REVISIT require non-empty value?
			}
		
	input.elem |= input.radio.elem
	
## Scripting Hook Button: <input type='button'>

	input.button.elem = 
		element input { input.button.attrs }
	input.button.attrs = 
		(	common.attrs
		&	common-form.attrs
		&	input.button.attrs.type
		&	input.button.attrs.value? 
		&	button.attrs.popover-controls
		&	(	common.attrs.aria.implicit.button
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.combobox
			|	common.attrs.aria.role.link
			|	common.attrs.aria.role.menuitem
			|	common.attrs.aria.role.menuitemcheckbox
			|	common.attrs.aria.role.menuitemradio
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.separator
			|	common.attrs.aria.role.switch
			|	common.attrs.aria.role.tab
			|	common.attrs.aria.role.gridcell
			|	common.attrs.aria.role.slider
			|	common.attrs.aria.role.treeitem
			)?
		)
		input.button.attrs.type = 
			attribute type {
				w:string "button"
			}
		input.button.attrs.value =
			attribute value {
				string #REVISIT require non-empty value?
			}
		
	input.elem |= input.button.elem
	#REVISIT should this be enabled by a scripting module only?
	
## Submit Button: <input type='submit'>

	input.submit.elem = 
		element input { input.submit.attrs }
	input.submit.attrs = 
		(	common.attrs
		&	common-form.attrs
		&	input.submit.attrs.type
		&	input.submit.attrs.value? 
		&	button.attrs.popover-controls
		&	(	common.attrs.aria.implicit.button
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.combobox
			|	common.attrs.aria.role.gridcell
			|	common.attrs.aria.role.link
			|	common.attrs.aria.role.menuitem
			|	common.attrs.aria.role.menuitemcheckbox
			|	common.attrs.aria.role.menuitemradio
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.slider
			|	common.attrs.aria.role.switch
			|	common.attrs.aria.role.tab
			|	common.attrs.aria.role.treeitem
			)?
		)
		input.submit.attrs.type = 
			attribute type {
				w:string "submit"
			}
		input.submit.attrs.value =
			attribute value {
				string #REVISIT require non-empty value?
			}
		
	input.elem |= input.submit.elem
		
## Reset Button: <input type='reset'>

	input.reset.elem = 
		element input { input.reset.attrs }
	input.reset.attrs = 
		(	common.attrs
		&	common-form.attrs
		&	input.reset.attrs.type
		&	input.reset.attrs.value? 
		&	button.attrs.popover-controls
		&	(	common.attrs.aria.implicit.button
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.combobox
			|	common.attrs.aria.role.gridcell
			|	common.attrs.aria.role.link
			|	common.attrs.aria.role.menuitem
			|	common.attrs.aria.role.menuitemcheckbox
			|	common.attrs.aria.role.menuitemradio
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.slider
			|	common.attrs.aria.role.switch
			|	common.attrs.aria.role.tab
			|	common.attrs.aria.role.treeitem
			)?
		)
		input.reset.attrs.type = 
			attribute type {
				w:string "reset"
			}
		input.reset.attrs.value =
			attribute value {
				string #REVISIT require non-empty value?
			}
		
	input.elem |= input.reset.elem
	# REVISIT does reset make sense outside a form?
		
## File Upload: <input type='file'>

	input.file.elem = 
		element input { input.file.attrs }
	input.file.attrs = 
		(	common.attrs
		&	common-form.attrs
		&	input.file.attrs.type
		&	input.file.attrs.accept?
		)
		input.file.attrs.type = 
			attribute type {
				w:string "file"
			}
		input.file.attrs.accept = 
			attribute accept {
				form.data.mimetypelist
			}

	input.elem |= input.file.elem
	
## Hidden String: <input type='hidden'>

	input.hidden.elem = 
		element input { input.hidden.attrs }
	input.hidden.attrs = 
		(	common.attrs
		&	common-form.attrs
		&	input.hidden.attrs.type
		&	input.hidden.attrs.value? 
		)
		input.hidden.attrs.type = 
			attribute type {
				w:string "hidden"
			}
		input.hidden.attrs.value =
			attribute value {
				string
			}
		
	input.elem |= input.hidden.elem
		
## Image Submit Button: <input type='image'>

	input.image.elem = 
		element input { input.image.attrs }
	input.image.attrs = 
		(	common.attrs
		&	common-form.attrs
		&	input.image.attrs.type
		&	input.image.attrs.alt 
		&	input.image.attrs.src? 
		&	button.attrs.popover-controls
		&	(	common.attrs.aria.implicit.button
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.combobox
			|	common.attrs.aria.role.gridcell
			|	common.attrs.aria.role.link
			|	common.attrs.aria.role.menuitem
			|	common.attrs.aria.role.menuitemcheckbox
			|	common.attrs.aria.role.menuitemradio
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.separator
			|	common.attrs.aria.role.slider
			|	common.attrs.aria.role.switch
			|	common.attrs.aria.role.tab
			|	common.attrs.aria.role.treeitem
			)?
		)	
		input.image.attrs.type = 
			attribute type {
				w:string "image"
			}
		input.image.attrs.alt = 
			attribute alt {
				form.data.nonemptystring
			}
		input.image.attrs.src = 
			attribute src {
				common.data.uri.non-empty
			}
	
	input.elem |= input.image.elem
	
	common.elem.phrasing |= input.elem

## Text Area: <textarea>

	textarea.elem =
		element textarea { textarea.inner & textarea.attrs }
	textarea.attrs =
		(	common.attrs
		&	common-form.attrs
		&	shared-form.attrs.readonly?
		&	textarea.attrs.rows-and-cols-wf1
		&	(	common.attrs.aria.implicit.textbox
			|	common.attrs.aria.role.textbox
			)?
		#FIXME onfocus, onblur, onselect,onchange
		)
		# This is ugly. 
		textarea.attrs.rows-and-cols-wf1 = 
			textarea.attrs.rows-and-cols-wf1.inner	
		textarea.attrs.rows-and-cols-wf1.inner =
			(	textarea.attrs.cols
			&	textarea.attrs.rows
			)
		textarea.attrs.cols = 
			attribute cols {
				common.data.integer.positive
			}
		textarea.attrs.rows = 
			attribute rows {
				common.data.integer.positive
			}
	textarea.inner =
		( text )

	common.elem.phrasing |= textarea.elem

# Due to limitations with interleave, handling single/multiple selection
# enforcement in RELAX NG seems to be possible but really awkward.
# Tried it. Leaving it to Schematron.

## Select menu option: <option selected>

	option.elem =
		element option { option.inner & option.attrs }
	option.attrs =
		(	common.attrs
		&	common-form.attrs.disabled?
		&	option.attrs.selected?
		&	option.attrs.label?
		&	option.attrs.value?
		&	(	common.attrs.aria.implicit.option
			|	common.attrs.aria.role.option
			)?
		)
		option.attrs.selected =
			attribute selected {
				w:string "selected" | w:string ""
			}
		option.attrs.label =
			attribute label {
				form.data.nonemptystring
			}		
		option.attrs.value =
			attribute value {
				string
			}		
	option.inner =
		(	common.inner.phrasing
		&	div.elem*
		)

## Option Group: <optgroup>

	optgroup.elem =
		element optgroup { optgroup.inner & optgroup.attrs }
	optgroup.attrs = 
		(	common.attrs
		&	optgroup.attrs.label?
		&	common-form.attrs.disabled?
		&	(	common.attrs.aria.implicit.group
			|	common.attrs.aria.role.group
			)?
		)
		optgroup.attrs.label =
			attribute label {
				string
			}
	optgroup.inner =
		(	legend.elem?
		&	option.elem*
		&	optgroupdiv.elem*
		&	noscript.elem.flow*
		&	common.elem.script-supporting*
		)

	optgroupdiv.elem =
		element div { optgroupdiv.inner & div.attrs }
	optgroupdiv.inner =
		(	option.elem*
		&	optgroupdiv.elem*
		&	noscript.elem.flow*
		&	common.elem.script-supporting*
		)

## Selection Menu: <select>

	select.elem =
		element select { select.inner & select.attrs }
	select.attrs =
		(	common.attrs
		&	common-form.attrs
		&	select.attrs.size?
		&	select.attrs.multiple?
		# FIXME onfocus, onblur, onchange
		&	(	common.attrs.aria.role.listbox # aria-multiselectable depends on "multiple" value; check in assertions
			|	common.attrs.aria.role.combobox
			|	common.attrs.aria.role.menu
			|	common.attrs.aria.implicit.combobox
			|	common.attrs.aria.implicit.listbox
			)?
		)
		select.attrs.size =
			attribute size {
				common.data.integer.positive
			}
		select.attrs.multiple =
			attribute multiple {
				w:string "multiple" | w:string ""
			}
	select.inner =
		(	button.elem?
		&	optgroup.elem*
		&	option.elem*
		&	hr.elem*
		&	selectdiv.elem*
		&	noscript.elem.flow*
		&	common.elem.script-supporting*
		)

	selectdiv.elem =
		element div { selectdiv.inner & div.attrs }
	selectdiv.inner =
		(	optgroup.elem*
		&	option.elem*
		&	hr.elem*
		&	selectdiv.elem*
		&	noscript.elem.flow*
		&	common.elem.script-supporting*
		)

	common.elem.phrasing |= select.elem

## Selected option content: <selectedcontent>

	selectedcontent.elem =
		element selectedcontent { selectedcontent.inner & selectedcontent.attrs }
	selectedcontent.attrs =
		(	common.attrs
		&	common.attrs.aria?
		)
	selectedcontent.inner =
		( empty )

	common.elem.phrasing |= selectedcontent.elem

## Shared Definitions for Complex Button
	
	button.attrs.command =
		attribute command {
			(w:string "toggle-popover"
			| w:string "show-popover"
			| w:string "hide-popover"
			| w:string "close"
			| w:string "request-close"
			| w:string "show-modal"
			| xsd:string {
				pattern = "--.*"
			}
			)
		}
	button.attrs.commandfor =
		attribute commandfor {
			common.data.idref
		}
	button.attrs.value =
		attribute value {
			string
		}
	button.attrs.popover-controls =
		(	button.attrs.popovertarget?
		&	button.attrs.popovertargetaction?
		)
	button.attrs.popovertarget =
		attribute popovertarget {
			common.data.idref
		}
	button.attrs.popovertargetaction =
		attribute popovertargetaction {
			w:string "hide" | w:string "show" | w:string "toggle"
		}
	button.inner = 
		( common.inner.phrasing )
	
## Complex Submit Button: <button type='submit'>

	button.submit.elem =
		element button { button.inner & button.submit.attrs }	
	button.submit.attrs =
		(	common.attrs
		&	common-form.attrs
		&	button.submit.attrs.type?
		&	button.attrs.command?
		&	button.attrs.commandfor?
		&	button.attrs.value?
		&	button.attrs.popover-controls
		&	(	common.attrs.aria.implicit.button
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.combobox
			|	common.attrs.aria.role.link
			|	common.attrs.aria.role.menuitem
			|	common.attrs.aria.role.menuitemcheckbox
			|	common.attrs.aria.role.menuitemradio
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.separator
			|	common.attrs.aria.role.tab
			|	common.attrs.aria.role.gridcell
			|	common.attrs.aria.role.slider
			|	common.attrs.aria.role.treeitem
			)?
		)
		button.submit.attrs.type =
			attribute type {
				w:string "submit"
			}

	button.elem = button.submit.elem
	
## Complex Reset Button: <button type='reset'>

	button.reset.elem =
		element button { button.inner & button.reset.attrs }	
	button.reset.attrs =
		(	common.attrs
		&	common-form.attrs
		&	button.reset.attrs.type
		&	button.attrs.command?
		&	button.attrs.commandfor?
		&	button.attrs.value? #REVISIT I guess this still affects the DOM
		&	button.attrs.popover-controls
		&	(	common.attrs.aria.implicit.button
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.combobox
			|	common.attrs.aria.role.link
			|	common.attrs.aria.role.menuitem
			|	common.attrs.aria.role.menuitemcheckbox
			|	common.attrs.aria.role.menuitemradio
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.separator
			|	common.attrs.aria.role.switch
			|	common.attrs.aria.role.gridcell
			|	common.attrs.aria.role.slider
			|	common.attrs.aria.role.treeitem
			)?
		)
		button.reset.attrs.type =
			attribute type {
				w:string "reset"
			}

	button.elem |= button.reset.elem
	
## Complex Push Button: <button type='button'>

	button.button.elem =
		element button { button.inner & button.button.attrs }	
	button.button.attrs =
		(	common.attrs
		&	common-form.attrs
		&	button.button.attrs.type
		&	button.attrs.command?
		&	button.attrs.commandfor?
		&	button.attrs.value? #REVISIT I guess this still affects the DOM
		&	button.attrs.popover-controls
		&	(	common.attrs.aria.implicit.button
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.combobox
			|	common.attrs.aria.role.link
			|	common.attrs.aria.role.menuitem
			|	common.attrs.aria.role.menuitemcheckbox
			|	common.attrs.aria.role.menuitemradio
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.separator
			|	common.attrs.aria.role.switch
			|	common.attrs.aria.role.tab
			|	common.attrs.aria.role.gridcell
			|	common.attrs.aria.role.slider
			|	common.attrs.aria.role.treeitem
			)?
		)
		button.button.attrs.type =
			attribute type {
				w:string "button"
			}

	button.elem |= button.button.elem

	common.elem.phrasing |= button.elem

## Form: <form>

	form.elem =
		element form { form.inner & form.attrs }
	form.attrs =
		(	common.attrs
		&	form.attrs.action? #REVISIT Should this be required anyway?
		&	form.attrs.method?
		&	form.attrs.enctype?
		&	common-form.attrs.name?
		&	form.attrs.accept-charset?
		&	(	common.attrs.aria.implicit.form
			|	common.attrs.aria.landmark.form
			|	common.attrs.aria.landmark.search
			|	common.attrs.aria.role.presentation
			)?
		)
		form.attrs.action =
			attribute action {
				common.data.uri.non-empty
			}
		form.attrs.method =
			attribute method {
				form.attrs.method.data
			}
			form.attrs.method.data = 
				( w:string "get"
				| w:string "post"
				| w:string "dialog"
				)
		form.attrs.enctype =
			attribute enctype {
				form.attrs.enctype.data
			}
			form.attrs.enctype.data = 
				(	w:string "application/x-www-form-urlencoded" 
				|	w:string "multipart/form-data"
				)
		form.attrs.accept-charset =
			attribute accept-charset {
				string
			}	
	form.inner =
		( common.inner.flow )

	common.elem.flow |= form.elem

## Fieldset: <fieldset>

	fieldset.elem =
		element fieldset { fieldset.inner & fieldset.attrs }
	fieldset.attrs =
		(	common.attrs
		&	(	common.attrs.aria.implicit.group
			|	common.attrs.aria.role.group
			|	common.attrs.aria.role.radiogroup
			|	common.attrs.aria.role.presentation
			)?
		)
	fieldset.inner =
		legend.elem?, common.inner.flow

	common.elem.flow |= fieldset.elem
	
## Label: <label>

	label.elem =
		element label { label.inner & label.attrs }
	label.attrs =
		(	common.attrs
		&	common.attrs.aria?
		&	label.attrs.for?
		)
		label.attrs.for =
			attribute for {
				common.data.idref
			}
	label.inner =
		( common.inner.phrasing ) #REVISIT making obvious guess

	common.elem.phrasing |= label.elem

