/*** checkBoss. A Multi-Role Master Checkbox Controller

Demo and further info: Scripterlative.com

Usage:  checkBoss( master, ranges, options )

THESE INSTRUCTIONS MAY BE REMOVED, EXCEPT THE ABOVE TEXT.

Meaning of Parameters
~~~~~~~~~~~~~~~~~~~~~
master = A reference to the master checkbox or null.

 Other input types may be used as the master.
 The null option is used for calling the function via control types other than 'checkbox' or
 'radio', or from a script.
 If null is passed, the function responds as it would to a checked checkbox.

ranges = The associated slave checkbox(es) specified as a /reference/ to either:
           A single slave checkbox
           A group of like-named slave checkboxes
           An array of slave checkbox references

options = An optional space-delimited string, consisting of a list of action specifiers as listed below

Values for 'options' parameter (Multiple values may be specified in any order/case)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

These modify the response state of the master checkbox

(The default behaviour is: Act only when checked)

 unchecked = Act only when unchecked

 toggle  =  Act on both checked states

-------------------------------------------------------

These modify the effect of the master checkbox

 invert = Invert the current checked state of all ranges

 oppose = The checked state is set to the inverse of the master's state ('invert' overrides)
 
 doppose = The disabled state is set to the inverse of the master's state ('invert' overrides)

 default = Set all ranges to their default checked state.

 disable = Use Disable mode instead of Check mode.
           All parameters work in the same sense. 'default' has no effect.
           
 nocheck = Do not alter the checked state. Use when changing disabled state only.
                     
-------------------------------------------------------

Installation
~~~~~~~~~~~~
Save this text/file as 'checkboss.js' and place it in a suitable folder.
In the <HEAD> section of your HTML document, add the following:

<script type='text/javascript' src='checkboss.js'></script>

Configuration
~~~~~~~~~~~~~
With this script there's probably little to be gained from unobtrusive configuration, so examples
are given using inline event handlers.

Usage Examples
~~~~~~~~~~~~~~

A form contains a group of checkboxes named 'cb1', and a separate group named 'cb2'.

A separate checkbox in the same form is used as a master control in the following different ways:

 When checked will check the group 'cb1', but have no effect when unchecked:

  <input type='checkbox' onclick="checkBoss(this, cb1)">

 When unchecked will check the group 'cb1', but have no effect when checked:

  <input type='checkbox' onclick="checkBoss(this, cb1, 'unchecked')">

 When changed will set the 'cb1' group to match its own state:

  <input type='checkbox' onclick="checkBoss(this, cb1, 'toggle')">

 When changed will set the 'cb1' group to the inverse of its own state:

  <input type='checkbox' onclick="checkBoss(this, cb1, 'oppose toggle')">

 When changed will invert the current individual checked state of each checkbox the 'cb1' group:

  <input type='checkbox' onclick="checkBoss(this, cb1, 'invert toggle')">

 When checked, disable and uncheck boxes 0, 2 & 4 of the 'cb1' range, uncheck to re-enable/check:
 
  <input type='checkbox' onclick="checkBoss( this, [ cb1[0], cb1[2],  cb1[4] ], 'doppose oppose toggle')">  
  

'radio' type inputs can be used in the same role.

 A 'button' type input is used to reset the cb1 & cb2 groups to their default checked states:

  <input type='button' onclick="checkBoss( null, [cb1,cb2], 'default toggle')">

Element References
~~~~~~~~~~~~~~~~~~
Within the scope of a form, as in all the above usage examples, elements of the form can be
referenced directly by their name attribute. However if the checkBoss function is called from code
outside the scope of the form, the master and all slave elements are properly referenced thus:

 document.forms.myForm.myElement

Clearly this can lead to very long-winded syntax, particularly if several slave elements are to be
specified.

This example function call inverts the checked state of two individual checkboxes in a group named
"cb1" within a form named 'formA':

 with( document.forms.formA )
  checkBoss( null, [ cb1[1], cb1[3] ], 'invert' );
  
The equivalent function call activated by a link would be:
      
 <a href='#' onclick='with(document.forms.formA){ return checkBoss(null, [cb1[1], cb1[3]], "invert" ) }'>!</a>
 
Unobtrusive Event Handler Assignment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
In form "formA", set a checkbox named 'masterBox' to check or uncheck the group 'cb1':

 with( document.forms.formA )
  masterBox.onclick=function(){ checkBoss( this, this.form.cb1, 'toggle' ) };
 

GratuityWare
~~~~~~~~~~~~
This code is free for private non-commercial use. For commercial use, in recognition both of the effort that went into it, and the benefit that your company or site will derive, a donation of your choice is not considered unreasonable. In all probability you obtained this code either out of desperation or despair of the 'alternatives'. 'Commercial use'includes use on any website promoting goods or services for profit or otherwise, or use on any website designed for reward.

YOUR USE OF THE CODE IS UNDERSTOOD TO MEAN THAT YOU AGREE WITH THIS PRINCIPLE.

You may donate at www.scripterlative.com, stating the URL to which the donation applies.

*** DO NOT EDIT BELOW THIS LINE ***/

function checkBoss(masterBox, ranges, actions)
{
  var options = actions||"",
      state = masterBox ? masterBox.checked : true,
      reset = /\bdefault\b/i.test(options),
      toggle = /\btoggle\b/i.test(options),
      unChecked = /\bunchecked\b/i.test(options) && !toggle,
      invert = /\binvert\b/i.test(options),
      oppose = /\boppose\b/i.test(options) && !invert,
      dOppose= /\bdoppose\b/i.test(options) && !invert,
      disable = dOppose || /\bdisable\b/i.test(options),
      boxCheck = !/\bnocheck\b/i.test(options) /*&& !disable*/;
   
  if( toggle || (unChecked ? !state : state) )
  {
    if( ranges.length )
    {
      for(var i=0, elem, bLen=ranges.length; i<bLen; i++)
      {
        elem = ranges[i];
        if(elem.length)
        {
         for(var j=0, eLen=elem.length; j<eLen; j++)
          actOn(elem[j]);
        }
        else
         actOn(elem);
      }
    }
    else
     actOn(ranges);
  }

  function actOn( box /*28432953637269707465726C61746976652E636F6D*/)
  {  
   if(boxCheck)
     box.checked = reset ? box.defaultChecked 
                         : invert ? !box.checked 
                                  : oppose ? !state 
                                           : state;
  
   if(disable)
   {
    box.disabled = invert ? (!box.disabled) 
                          : dOppose ? state 
                                    : (!state);    
   }
  }
  
  return false;
}
/** End **/