// $Id: FnWebSECheckboxMenuHandler.js,v 1.9 2007/06/15 14:50:41 awf rlse $ 
/**
*The FnWebSECheckboxMenuHandler class automates the communication between HTML components on a
* WebSE search result page using a checkbox collection. This class uses listeners to synchronize
* the state of certain HTML components when the appropriate event is triggered. For example:
* <ul>
* <li>Automatically disable/enable a set of menu items when no records/at least one record in the WebSE collection is selected.</li>
* <li>Automatically check/uncheck all checkboxes on the page, when the check all checkbox is checked/unchecked and enable/disable the menu items accordingly.</li>
* <li>Automatically check/uncheck all checkboxes in the WebSE collection when the "Select All"/"Select None" menu item is pressed and enable/disable the menu items accordingly.</li>
* </ul>
* <p>
* The following code snippet illustrates how to instantiate and use this class:
* <code>
*	WebSECheckboxMenuHandler = new FnWebSECheckboxMenuHandler(" . $WebSE->getRequestId() . ", " . $WebSE->getRows() . ", " . $WebSE->getCheckedCount() . ");
*	WebSECheckboxMenuHandler.addCheckboxClickListenersByName('" . URL_ROW_CHECK . "[]', true);
* WebSECheckboxMenuHandler.defineEnableDisableCallback('checkboxMenuCallback');
* WebSECheckboxMenuHandler.registerMenuItem('mi_displayAsPDF');
* WebSECheckboxMenuHandler.addSelectPageListenerByID('all_rows', true);
* WebSECheckboxMenuHandler.addSelectAllListenerByID('mi_select_all');
* WebSECheckboxMenuHandler.addSelectNoneListenerByID('mi_select_none');
* </code>
* <dl>
* <dt>addCheckboxClickListenersByName</dt>
* <dd>Mandatory method call initializing a listener for each checkbox in the collection specified by the collection name.</dd>
* <dt>defineEnableDisableCallback</dt>
* <dd>Defines the name callback function to call when one of two events occur in the checkbox control collection.
*     Event 1: None of the checkboxes in the collection are checked.
*     Event 2: At least one of the checkboxes in the collection are checked.
*     This method can be used with or replace the registerMenuItem method</dd>
* <dt>registerMenuItem</dt>
* <dd>Add a menu item (via it's ID attribute) to a collection.  When one of the events described above are triggered
*     each menu item in the collection will automatically be enabled/disabled accordingly.  This method can be used
*     with or replace the defineEnableDisableCallback method</dd>
* <dt>addSelectPageListenerByID</dt>
* <dd>This method initializes a listener for the "select all checkboxes on page" checkbox.  When this control is 
*     pressed an event is triggered which checks/unchecks all checkboxes on the page and potentially triggers
*     one of the two enable/disable menu item events.</dd>
* <dt>addSelectAllListenerByID</dt>
* <dd>This method initializes a listener for the "Select All" menu item, which is used to select all the records
*     in WebSE.  With the menu item is pressed a event is triggered which checks all checkboxes on the page and
*     executes an AJAX call to update WebSE on the server-side to mark all rows in the recordset as checked.</dd>
* <dt>addSelectNoneListenerByID</dt>
* <dd>This method initializes a listener for the "Select None" menu item, which is used to deselect all the records
*     in WebSE.  With the menu item is pressed a event is triggered which uncheck all checkboxes on the page and
*     executes an AJAX call to update WebSE on the server-side to mark all rows in the recordset as unchecked.</dd>
* </dl>
* 
* @param integer setID          WebSE's set ID obtained by $WebSE->getRequestId().
* @param integer checkboxCount  WebSE's total row count obtained by $WebSE->getRows().
* @param integer checkedCount   WebSE's total checked row count obtained by $WebSE->getCheckedCount().
* @author Marc Theriault
* @date   2006-09-13
* @version 0.1
*
* @revision Bug id 1097. Extended FnWebSECheckboxMenuHandler to also listen and handle radio buttons.

  2006-dec-28, 11:45, awf
  Bug0001287: 'document.getElementById(...)' is null or not an object
  Updated mi_selectNone to be mi_select_none

	2007-mar-30, 17:00, awf
	Updated ajaxCheckAll() to call mode 1 for select all, not 3 (which is remove all
	
	2007-jun-13, 17:15, awf
	Bug0001555: Select All / Select None
	Enabling the screen when the select all or select none is checked
	
	2007-jun-15, 11:00, awf
	Found true issue with Bug0001555 (spaces in the settings file), so I removed
	the temporary enableScreen();
	

*/
function FnWebSECheckboxMenuHandler(setID, checkboxCount, checkedCount) {
	var self = this;
	var ERROR_1 = 'FnWebSECheckboxMenuHandler encountered an error because a checkbox collection was not specified. Please specify one using the addCheckboxClickListenersByName(checkboxCollectionNameAttribute) method.';

	this.checkboxes = null;
	this.setID = setID;
	this.checkboxCount = checkboxCount;
	this.checkedCount = checkedCount;
	this.callbackFunction = null;
	this.selectPageCheckbox = null;
	this.registeredMenuItems = new Array();


	/**
	* The PRIVATE triggerCallback method is responsible for calling the
	* user-defined callback function and/or enable/disable any menu items
	* in the registered menu item collection when the 'no checkboxes are checked'
	* and 'at least one checkbox is checked' events are triggered.
	*
	*/
	this.triggerCallback = function() {
		if(this.registeredMenuItems.length > 0) {
			if(this.checkedCount <= 0) {
				var action = 'disableItem';
			} else {
				var action = 'enableItem';
			}

			for(i in this.registeredMenuItems) {
				eval(action + "('" + this.registeredMenuItems[i] + "');");
			}
		}
		
		if(this.callbackFunction != null && eval('document.' + this.callbackFunction) != 'undefined') {
			if(this.checkedCount <= 0) {
				return eval('parent.' + this.callbackFunction + '(false)');
			} else {
				return eval('parent.' + this.callbackFunction + '(true)');
			}
		}
	}


	/**
	* The defineEnableDisableCallback method is configures the
	* user-defined callback function which is called when the 
	* 'no checkboxes are checked' and 'at least one checkbox
	* is checked' events are triggered.
	*
	* @param string callbackFunction  the name of the callback function
	*/
	this.defineEnableDisableCallback = function(callbackFunction) {
		this.callbackFunction = callbackFunction;
		this.triggerCallback();
	}


	/**
	* The registerMenuItem method is adds the ID of a menu item
	* to the registered menu item collection.
	* Each menu item in the collection is disabled/enabled 
	* automatically when the 'no checkboxes are checked' and
	* 'at least one checkbox is checked' events are triggered.
	*
	* @param string id    the id attribute of a menu item to enable/disable
	*/
	this.registerMenuItem = function(id) {
		this.registeredMenuItems[this.registeredMenuItems.length] = id;
		this.triggerCallback();
	}


	/**
	* The PRIVATE checkboxHandler method is assists in tracking
	* the number of checkboxes that are checked not only on the page,
	* but throughout the entire WebSE recodset.
	* This method is called automatically when a checkbox in
	* the specified collection is clicked.
	* <p>
	* When a change is made this method makes a call to the
	* triggerCallback method.
	*
	* @param object event    The event object that triggered this handler
	*/
	this.checkboxHandler = function(event) {
		var target = (event['target']) ? 'target' : 'srcElement';
		if(event[target].checked) {
			self.checkedCount++;
		} else {
			self.checkedCount--;
			if(self.selectPageCheckbox != null) {
				self.selectPageCheckbox.checked = false;
			}
		}
		self.triggerCallback();
	}


	/**
	* The PRIVATE checkboxRadioParentHandler method is assists in tracking
	* the number of checkboxes/radio buttons that are checked not only on the page,
	* but throughout the entire WebSE recodset.
	* This method is called automatically when a TD cell containing
	* a checkbox/radio button in the specified collection is clicked.
	* <p>
	* When a change is made this method makes a call to the
	* triggerCallback method.
	*
	* @param object event    The event object that triggered this handler
	*/
	this.checkboxRadioParentHandler = function(event) {
		var target = (event['target']) ? 'target' : 'srcElement';
		var Checkbox = null;

		if(event[target].tagName != 'TD') {
			return;
		}

        Checkbox = null;
		for(i=0; i<event[target].childNodes.length; i++) {
			if( (event[target].childNodes[i].type == 'checkbox') ||
			     (event[target].childNodes[i].type == 'radio') ){
				Checkbox = event[target].childNodes[i];
				break;
			}
		}

        if (null != Checkbox){
		   Checkbox.checked = !Checkbox.checked;
		   if(Checkbox.checked) {
			   self.checkedCount++;
		   } else {
			   self.checkedCount--;
			   if(self.selectPageCheckbox != null) {
				   self.selectPageCheckbox.checked = false;
			   }
		   }
		   self.triggerCallback();
		}
	}


	/**
	* The PRIVATE selectPageHandler method is assists in tracking
	* the number of checkboxes that are checked not only on the page,
	* but throughout the entire WebSE recodset.
	* This method is called automatically when the select page
	* checkbox is clicked.
	* <p>
	* When a change is made this method updates all checkboxes
	* in the collection accordingly and makes a call to the
	* triggerCallback method.
	*
	* @param object event    The event object that triggered this handler
	* @throws error(ERROR_1)  If the checkbox collection is null.
	*/
	this.selectPageHandler = function(event) {
		var target = (event['target']) ? 'target' : 'srcElement';

		if(!self.checkboxes) {
			error(ERROR_1);
			return false;
		}

		for(i=0; i<self.checkboxes.length; i++) {
			if(self.checkboxes[i].checked != event[target].checked) {
				self.checkboxes[i].checked = event[target].checked;
				if(self.checkboxes[i].checked == true) {
					self.checkedCount++;
				} else {
					self.checkedCount--;
				}
			}
		}
		self.triggerCallback();
	}


	/**
	* The PRIVATE selectPageHandler method is assists in tracking
	* the number of checkboxes that are checked not only on the page,
	* but throughout the entire WebSE recodset.
	* This method is called automatically when the TH or TD cell
	* containing the select page checkbox is clicked.
	* <p>
	* When a change is made this method updates all checkboxes
	* in the collection accordingly and makes a call to the
	* triggerCallback method.
	*
	* @param object event    The event object that triggered this handler
	* @throws error(ERROR_1)  If the checkbox collection is null.
	*/
	this.selectPageParentHandler = function(event) {
		var target = (event['target']) ? 'target' : 'srcElement';
		var Checkbox = null;

		if(!self.checkboxes) {
			error(ERROR_1);
			return false;
		}

		if(event[target].tagName != 'TD' && event[target].tagName != 'TH') {
			return;
		}

		for(i=0; i<event[target].childNodes.length; i++) {
			if(event[target].childNodes[i].type == 'checkbox') {
				Checkbox = event[target].childNodes[i];
				break;
			}
		}

		Checkbox.checked = !Checkbox.checked;

		for(i=0; i<self.checkboxes.length; i++) {
			if(self.checkboxes[i].checked != Checkbox.checked) {
				self.checkboxes[i].checked = Checkbox.checked;
				if(self.checkboxes[i].checked == true) {
					self.checkedCount++;
				} else {
					self.checkedCount--;
				}
			}
		}
		self.triggerCallback();
	}


	/**
	* The PRIVATE selectAllHandler method is assists in tracking
	* the number of checkboxes that are checked not only on the page,
	* but throughout the entire WebSE recodset and performing
	* an AJAX call to update the state of the checked rows in WebSE
	* on the server.
	* This method is called automatically when the "Select All" menu
	* item is pressed and checkes all checkboxes in the collection
	* as well as all the rows in the WebSE collection via AJAX.
	*
	* @param object event    The event object that triggered this handler
	* @throws error(ERROR_1)  If the checkbox collection is null.
	*/
	this.selectAllHandler = function(event) {
		if(!self.checkboxes) {
			error(ERROR_1);
			return false;
		}
  	
		waitCursor();
		disableScreen();
    
    SELECT_ALL = 1;
		ajaxCheckAll(self.setID, SELECT_ALL);

		self.checkedCount = self.checkboxCount;
		for(i=0; i<self.checkboxes.length; i++) {
			self.checkboxes[i].checked = true;
		}

		if(self.selectPageCheckbox != null) {
			self.selectPageCheckbox.checked = true;
		}

		defaultCursor();
		self.triggerCallback();
	}


	/**
	* The PRIVATE selectNoneHandler method is assists in tracking
	* the number of checkboxes that are checked not only on the page,
	* but throughout the entire WebSE recodset and performing
	* an AJAX call to update the state of the checked rows in WebSE
	* on the server.
	* This method is called automatically when the "Select None" menu
	* item is pressed and uncheckes all checkboxes in the collection
	* as well as all the rows in the WebSE collection via AJAX.	*
	*
	* @param object event    The event object that triggered this handler
	* @throws error(ERROR_1)  If the checkbox collection is null.
	*/
	this.selectNoneHandler = function(event) {
		if(!self.checkboxes) {
			error(ERROR_1);
			return false;
		}

		waitCursor();
		disableScreen();

    REMOVE_ALL = 3;
		ajaxCheckAll(self.setID, REMOVE_ALL);

		self.checkedCount = 0;
		for(i=0; i<self.checkboxes.length; i++) {
			self.checkboxes[i].checked = false;
		}
		
		if(self.selectPageCheckbox != null) {
			self.selectPageCheckbox.checked = false;
		}
		

		defaultCursor();
		self.triggerCallback();
	}


	/**
	* The addCheckboxClickListenersByName method initializes a listener
	* for each checkbox/radio button in the specified collection.
	* When any checkbox/radio button in the collection is clicked, the listener triggers
	* a call to the checkboxHandler and/or checkboxRadioParentHandler method(s).
	*
	* @param string name                  The name attribute of the checkbox collection.
	* @param boolean listenToParentBool   If true, a listener will be added to the parent TD cell of each checkbox in the collection.
	*/
	this.addCheckboxClickListenersByName = function(name, listenToParentBool) {
		this.checkboxes = document.getElementsByName(name);

		if(window.addEventListener) { // Mozilla, Netscape, Firefox
			for(i=0; i<this.checkboxes.length; i++) {
				if(listenToParentBool) {
					this.checkboxes[i].parentNode.addEventListener('click', this.checkboxRadioParentHandler, false);
				}
				this.checkboxes[i].addEventListener('click', this.checkboxHandler, false);
			}
		} else { // IE
			for(i=0; i<this.checkboxes.length; i++) {
				if(listenToParentBool) {
					this.checkboxes[i].parentNode.attachEvent('onclick', this.checkboxRadioParentHandler, false);
				}
				this.checkboxes[i].attachEvent('onclick', this.checkboxHandler, false);
			}
		}
	}


	/**
	* The addSelectPageListenerByID method initializes a listener
	* to the 'select all items on page' checkbox.
	* When this checkbox, the listener triggers a call to the
	* selectPageHandler and/or selectPageParentHandler method(s).
	*
	* @param string id                    The id attribute of the 'select all items on page' checkbox.
	* @param boolean listenToParentBool   If true, a listener will be added to the parent TD cell of each checkbox in the collection.
	*/
	this.addSelectPageListenerByID = function(id, listenToParentBool) {
		if(!document.getElementById(id)) {
			return false;
		}
		this.selectPageCheckbox = document.getElementById(id)

		if(window.addEventListener) {
			if(listenToParentBool) {
				document.getElementById(id).parentNode.addEventListener('click', this.selectPageParentHandler, false);
			}
			document.getElementById(id).addEventListener('click', this.selectPageHandler, false);
		} else {
			if(listenToParentBool) {
				document.getElementById(id).parentNode.attachEvent('onclick', this.selectPageParentHandler, false);
			}
			document.getElementById(id).attachEvent('onclick', this.selectPageHandler, false);
		}

		if(this.checkboxCount == this.checkedCount) {
			this.selectPageCheckbox.checked = true;
		}
	}


	/**
	* The addSelectAllListenerByID method initializes a listener
	* to the 'Select All' menu item.
	* When this menu item is pressed, the listener triggers a call to the
	* selectAllHandler method.
	*
	* @param string id                    The id attribute of the 'Select All' menu item.
	*/
	this.addSelectAllListenerByID = function(id) {
		if(window.addEventListener) {
			document.getElementById(id).addEventListener('click', this.selectAllHandler, false);
		} else {
			document.getElementById(id).attachEvent('onclick', this.selectAllHandler, false);
		}
	}


	/**
	* The addSelectNoneListenerByID method initializes a listener
	* to the 'Select All' menu item.
	* When this menu item is pressed, the listener triggers a call to the
	* selectNoneHandler method.
	*
	* @param string id                    The id attribute of the 'Select None' menu item.
	*/
	this.addSelectNoneListenerByID = function(id) {
		if(window.addEventListener) {
			document.getElementById(id).addEventListener('click', this.selectNoneHandler, false);
		} else {
			document.getElementById(id).attachEvent('onclick', this.selectNoneHandler, false);
		}
	}

}
