<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"
 	xmlns:x2="http://www.w3.org/TR/xhtml2"
	xmlns:role="http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#"	
	xmlns:state="http://www.w3.org/2005/07/aaa"
	xmlns:testRole="http://cita.uiuc.edu/2006/06/role"	
	xmlns:testState="http://cita.uiuc.edu/2006/06/state">
<head>

<link ref="meta" type="application/rdf+xml" title="RDF Definition for the checkboxgroup extended widget" href="http://cita.disability.uiuc.edu/software/mozilla/test/dhtml/src/rdf/exclusivecheckbox_reference/exclusivecheckbox_roles.php" />
<link ref="meta" type="application/rdf+xml" title="RDF Definition for some extended states" href="http://cita.disability.uiuc.edu/software/mozilla/test/dhtml/src/rdf/exclusivecheckbox_reference/exclusivecheckbox_states.php" />

<!--  This would be a better definition
<link ref="meta namespace http://cita.uiuc.edu/2006/06/role" type="application/rdf+xml" title="RDF Definition for the checkboxgroup extended widget" href="http://cita.disability.uiuc.edu/software/mozilla/test/dhtml/src/rdf/exclusivecheckbox_reference/exclusivecheckbox_roles.php" />
<link ref="meta namespace http://cita.uiuc.edu/2006/06/state" type="application/rdf+xml" title="RDF Definition for some extended states" href="http://cita.disability.uiuc.edu/software/mozilla/test/dhtml/src/rdf/exclusivecheckbox_reference/exclusivecheckbox_states.php" />
-->
<title>Complex RDF Widget Extension</title>

<script type="text/javascript" >
<![CDATA[

// Returns true if checkable event 
function isCheckable(event) {
	if ((event.type == "click" && event.button == 0) 
		|| (event.type == "keypress" && event.charCode == 32)
		|| ( (event.type == "keydown" || event.type == "keyup") && event.keyCode == 32 ) ) {
		
		if( event.target == event.currentTarget ) { 
			return true;
		}
	}
	return false;
}

function checkboxCheck(node) {
	node.setAttributeNS("http://www.w3.org/2005/07/aaa", "checked", "true");
}

// Sets the "checked" attribute to false
function checkboxUnCheck(node) {
	node.setAttributeNS("http://www.w3.org/2005/07/aaa", "checked", "false");
}

// Returns true if a node has the aaa state "checked" set
function isChecked(node) {
	if (node.getAttributeNS("http://www.w3.org/2005/07/aaa", "checked") == "true") {
		return true;
	}
	return false;
}

// returns true if node has role=radio
function isCheckbox(node) {
	// Attempt to match last part of string because of unrecognized-namespace-in-value issues
	if( node.getAttributeNS("http://www.w3.org/TR/xhtml2", "role").search(/checkbox$/) > -1 ) {
		return true;
	}
	return false;
}

// Adds the given className string on given DOM node
function addStyleClass(node, className) {
	var classStr = node.getAttribute("class");
	
	classStr = classStr + " " + className;
	node.setAttribute("class",classStr);
}

// Removes the given className string on given DOM node
function removeStyleClass(node, className) {
	var classStr = node.getAttribute("class");
	
	if(classStr == "" || classStr == null) {
		return;
	}
	
	classNameRegExp = new RegExp("^" + className + "\\s*", "g");
	classStr = classStr.replace(classNameRegExp,"");
	classNameRegExp = new RegExp("\\s+" + className, "g");
	classStr = classStr.replace(classNameRegExp,"");

	node.setAttribute("class",classStr);
}

// Handles click and keyboard events for the exclusive checkbox/checkboxgroup
function exclusiveCheckboxEvent(event) {
	if( isCheckable(event) ) {
		exclusiveCheck(event.target, flashConflicts);
		return false;  // Don't continue propagating event
	}
	return true;  // Browser can still use event
}

// If the maximum number of checks in this checkboxgroup has not been exceeded and 
// ... if no other checkbox in exclusivegroup is checked, this checkbox is checked.
// Elements that cause conflicts are passed in an array to the given conflict handling function 
// ... as well as the node under consideration.  Pass null if no handleConflicts function is to be used.
function exclusiveCheck(node, handleConflictsFunction) {
	if(isChecked(node)) {
		checkboxUnCheck(node);		
		return;
	}
	
	// compare number in group checked with max number
	var chboxGroup = getCheckboxParentGroup(node);
	if( getCheckedMax(chboxGroup) <= getNumChecked(chboxGroup) ) {
		// don't allow it
		if(handleConflictsFunction != null)	{
			handleConflictsFunction(node, new Array());  // empty array signifies a blameless conflict
		}
		return;
	}

	var groups = getExclusiveGroupIds(node);
	var members;
	var conflicts = new Array();
	chboxGroupMembers = getCheckboxGroupArray(chboxGroup, new Array());
	for( var i in groups ) {
		members = getExclusiveGroupMembers(chboxGroupMembers, groups[i]);
		for( var j in members ) {
			if( isChecked(members[j]) ) {
				conflicts.push(members[j]);
			}
		}
	}

	if( conflicts.length > 0 ) {
		if( handleConflictsFunction != null ) {
			handleConflictsFunction(node, conflicts);
			return;
		}
	}
	
	// If we made it to here, no conflicts.
	checkboxCheck(node);
}

// Returns an a array of checkbox controls in a group given a DOM checkboxgroup node
// checkboxArray is used in the recursion, the root call should pass in "new Array()"
function getCheckboxGroupArray(checkboxGroupNode, checkboxArray) {
	// Recursively search the tree below this node unless they are a checkboxgroup node
	for(var i=0; i<checkboxGroupNode.childNodes.length; i++) {
		childNode = checkboxGroupNode.childNodes[i];
		if(childNode.nodeType != childNode.ELEMENT_NODE) {
			continue;
		}

		if(isCheckboxGroup(childNode)) {
			return radioArray; // Don't descend
		} else if(isCheckbox(childNode)) {
			checkboxArray.push(childNode);
		}
		getCheckboxGroupArray(childNode, checkboxArray); // Descend
	}
	return checkboxArray;
}

// Returns the element with role "checkboxgroup" that contains this element or null if none found
function getCheckboxParentGroup(node) {
	if( node.parentNode == null ) {
		return null;
	} 
	node = node.parentNode;	

	if( isCheckboxGroup(node) ) {
		return(node);
	}

	return getCheckboxParentGroup(node);
}

// returns true if node has role=radio
function isCheckboxGroup(node) {
	// Attempt to match last part of string because of unrecognized-namespace-in-value issues
	if( node.getAttributeNS("http://www.w3.org/TR/xhtml2", "role").search(/checkboxgroup$/) > -1 ) {
		return true;
	}
	return false;
}

// Add the styling class "flash" to each conflicted element in the given array 
// ... for some time, then remove.
function flashConflicts(node, conflicts) {
	for( var i in conflicts ) {
		addStyleClass(conflicts[i], "flash");
	}
	
	// 1 second delay
	window.setTimeout(
		function() {
			for( var i in conflicts ) {
				removeStyleClass(conflicts[i], "flash");
			}
		}, 1000
	);
}

// returns the value of the checkedmax extended state on the extended checkboxgroup node
// the empty string is returned if the state is not provided or is empty.
function getCheckedMax(checkboxGroupNode) {
	// Attempt to match last part of string because of unrecognized-namespace-in-value issues
	return checkboxGroupNode.getAttributeNS("http://cita.uiuc.edu/2006/06/state", "checkedmax");
}

// gets all checkbox widgets in the given checkboxgroup (and not in 
// ... any other sub-checkboxgroup) and returns the total number checked.
function getNumChecked(checkboxGroupNode) {
	var checkboxes = getCheckboxGroupArray(checkboxGroupNode, new Array());
	
	var numChecked = 0;
	for(var i in checkboxes) {
		if( isChecked(checkboxes[i])) {
			numChecked++;
		}
	}
	return numChecked;
}

// Returns an array of strings that are the space-seperated exclusivegroup memberships
function getExclusiveGroupIds(node) {
	var ids = node.getAttributeNS("http://cita.uiuc.edu/2006/06/state", "exclusivegroup");
	return ids.split(" ");
}

// returns the members in the intersection of the given exclusivegroup id and 
// ... the exclusivegroup membership of the given array of elements
// An empty array is returned if there is no element with the exclusivegroup id
function getExclusiveGroupMembers(elementArray, id) {
	if(id=="") {return;} // don't count the empty string as a valid id

	var intersection = new Array();
	for( var i in elementArray ) {
		groupIds = getExclusiveGroupIds(elementArray[i]);
		for( var j in groupIds ) {
			if(groupIds[j]=="") {continue;} // don't count the empty string as a valid id
			if( id == groupIds[j] ) {
				intersection.push(elementArray[i]);
				break;
			}
		}
	}
	return intersection;
}

]]>
</script>

<style type="text/css">
@namespace x2    url("http://www.w3.org/TR/xhtml2");
@namespace role  url("http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#");
@namespace state url("http://www.w3.org/2005/07/aaa");
ul.hlist {
	list-style: none;
	margin: 1em;
	padding: 0;
	display: inline;
}

ul.hlist li	{
	display: inline;
	margin: 1em;
	padding: 0;	
}

p.inline {
	display: inline;
	margin: 1em;	
}

div#content {
	position: absolute;
	top: 0;
	left: 0;
}

.hide {
/*
	visibility: hidden;
	height: 0;
	width: 0;
	padding: 0;
	margin: 0;
	display: inline;
	*/
	display: none;
}

li.testItem {
	margin: 0;
}

iframe.short {
	margin-top: 1em;
	height: 3em;
}

iframe.textfieldShort {
	margin-top: 1em;
	height: 8em;
}

iframe.comboboxShort {
	margin-top: 1em;
	height: 11em;
}

iframe.rdfShort {
	margin-top: 1em;
	height: 14em;	
}

iframe.treeShort {
	margin-top: 1em;
	height: 20em;	
}

iframe.sliderShort {
	margin-top: 1em;
	height: 5em;	
}

h3.info {
	display: inline;
	margin: 0;
	padding: 0;
}

.checkbox[state|checked="true"]:before {
	content: url('../../images/checked.gif')
}

.checkbox:before {
	content: url('../../images/unchecked.gif')
}

.flash {
	background-color: #FF9999;
}</style>
</head>

<body>

<div id="content">

<h1 class="">Complex RDF Widget Extension</h1>
<h2>Test Information</h2>
<p class="short">
A more complex RDF description of widgets and extending the checkbox functionality.
</p>
<p class="long">
This test explores a more exhaustive extension of the checkbox, radio, and radiogroup widgets and states, through RDF.
The desired effect is a list of checkboxes, where the total number of checked checkboxes may be limited, and where 
certain groups of checkboxes will be exclusive.</p>
<p class="long">
The new widget and states are subclasses of widgets in the W3C spec.
"checkboxgroup" extends "select", has a relatedConcept of "radiogroup".
It serves to hold zero or more checkboxes and is multiselectable.  
It can have one new state, "checkedmax" which is related to "valuemax" 
and is the maximum number of checked allowed, within.
</p>
<p class="long">
Another state is introduced: "exclusivegroup".  
This is useful when there are many checkable options that are exclusive (i.e. "A is true, A is false").  
It's value is of the IDREFS format, a string of space seperated identifiers.  Only one checkbox in any 
exclusivegroup is allowed to be checked.  
</p>
<p class="long">
NOTE: "exclusivegroup" is not considered a good extension of the states and properties.  
It introduces complications and it is unlikely that any AT could support this in a meaningful way if it supports the 
standard set of states and widgets.  In most situations, the script could just as easily set the "disabled" state on 
widgets which were not allowed to be checked.
It is presented as a well-formed state extension and as a warning to future developers.
</p>
<p class="long">
Pressing space on a focused widget or clicking on a checkbox may toggle its checked state.
Attempting to check a second widget in an exclusivegroup will cause the existing checked widget to 
become temporarily styled ("flash") and no state changes will occur.
A valid check will be indicated visually with a checked checkbox image.
</p>
<ul class="instructions">
	<li>Verify that the new RDF widgets are identified as such on the page</li>
	<li>Verify information may be extracted through subClassOf, relatedConcept, and description relationships</li>
	<li>Tab to one of the checkbox widgets</li>
	<li>Press space, verify the image and checked state changes</li>
	<li>Press space again, verify the image and checked state changes</li>	
	<li>Check a number of the checkboxes and verify one may only check a certain number at one time 
		(test {Tuesday Morning, Tuesday Evening, Thursday Morning, Saturday Morning})</li>
	<li>Check a number of the checkboxes and verify the stated exclusive relationships hold
		(Test two weekday evenings, Test two weekend mornings, Test one weekend morning, one weekday evening, and then one weekend evening).</li>
	<li>Use the mouse, verify the same</li>
</ul>


<h2>Test</h2>


<div id="test">

<p id="instructions">Please select your work schedule preferences below.  
Each employee can work up to 3 shifts, only one shift on the weekend, and 
only one evening shift.</p>

<ul class="checkgroup" id="cgroup1" 
	x2:role="testRole:checkboxgroup"
	state:multiselectable="true"
	testState:checkedmax="3"
	state:describedby="instructions">
	
	<li class="checkbox" id="chbox1" x2:role="role:checkbox"
		state:checked="false"
		testState:exclusivegroup=""
		tabindex="0"
		onclick="exclusiveCheckboxEvent(event)" onkeypress="exclusiveCheckboxEvent(event)"
	>Tuesday Morning</li>

	<li class="checkbox" id="chbox2" x2:role="role:checkbox"
		state:checked="false"
		testState:exclusivegroup="evening"
		tabindex="0"
		onclick="exclusiveCheckboxEvent(event)" onkeypress="exclusiveCheckboxEvent(event)"
	>Tuesday Evening</li>
	
	<li class="checkbox" id="chbox3" x2:role="role:checkbox"
		state:checked="false"
		testState:exclusivegroup=""
		tabindex="0"
		onclick="exclusiveCheckboxEvent(event)" onkeypress="exclusiveCheckboxEvent(event)"
	>Thursday Morning</li>
	
	<li class="checkbox" id="chbox4" x2:role="role:checkbox"
		state:checked="false"
		testState:exclusivegroup="evening"
		tabindex="0"
		onclick="exclusiveCheckboxEvent(event)" onkeypress="exclusiveCheckboxEvent(event)"
	>Thursday Evening</li>
	
	<li class="checkbox" id="chbox5" x2:role="role:checkbox"
		state:checked="false"
		testState:exclusivegroup="weekend"
		tabindex="0"
		onclick="exclusiveCheckboxEvent(event)" onkeypress="exclusiveCheckboxEvent(event)"
	>Saturday Morning</li>
	
	<li class="checkbox" id="chbox6" x2:role="role:checkbox"
		state:checked="false"
		testState:exclusivegroup="weekend evening"
		tabindex="0"
		onclick="exclusiveCheckboxEvent(event)" onkeypress="exclusiveCheckboxEvent(event)"
	>Saturday Evening</li>
	
	<li class="checkbox" id="chbox7" x2:role="role:checkbox"
		state:checked="false"
		testState:exclusivegroup="weekend"
		tabindex="0"
		onclick="exclusiveCheckboxEvent(event)" onkeypress="exclusiveCheckboxEvent(event)"
	>Sunday Morning</li>
	
	<li class="checkbox" id="chbox8" x2:role="role:checkbox"
		state:checked="false"
		testState:exclusivegroup="weekend evening"
		tabindex="0"
		onclick="exclusiveCheckboxEvent(event)" onkeypress="exclusiveCheckboxEvent(event)"
	>Sunday Evening</li>
</ul>		

</div>

</div>

</body>
</html>


