/******************************************************************************
 * Copyright (C) 2007, by QOOP, Inc.
 * All Rights Reserved
 ******************************************************************************/

/* This class is used to represent options in the selection boxes:
 */
function JavascriptOption(n, v)
	{
	this.option_name = n;
	this.option_value = v;
	}

/* This class represents a collection of selection boxes and contains functionality related to
 * saving data in the boxes and navigating to related pages.
 */
function JavascriptOptionBoxes()
	{
	this.tiers = new Array();
	this.values = new Array();
	this.keys_to_check = new Array();
	this.unsaved_changes = new Array();
	}

JavascriptOptionBoxes.prototype.add_key_to_check = function(key)
	{
	/* Save the value into the array listing all unique_ids that we need to check when validating options:
	 */
	this.keys_to_check[this.keys_to_check.length] = key;

	/* By default, make sure the unsaved_changes array is set to false for all keys:
	 */
	this.unsaved_changes[key] = false;
	}

/* Given a reference to an option array, this method determines whether or not the option array can apply to
 * the book with the specified page count.
 */
JavascriptOptionBoxes.prototype.option_applies = function(option, page_count)
	{
	/* Only check page-related issues if we've been given a page count; -1 means ignore.
	 */
	if (page_count > -1)
		{
		/* If a minimum exists and is not met, fail:
		 */
		if (option['min_pages'] > -1 && page_count < option['min_pages'])
			return false;

		/* If a maximum exists and is not met, fail:
		 */
		if (option['max_pages'] > -1 && page_count > option['max_pages'])
			return false;
		}

	/* If we haven't failed out by now, we're okay -- the option applies:
	 */
	return true;
	}

/* Indicate that the book identified by unique_id has been changed:
 */
JavascriptOptionBoxes.prototype.mark_unsaved_changes = function(unique_id)
	{
	this.unsaved_changes[unique_id] = true;
	}

/* Given the unique ID identifying a specific set of option boxes and a pointer to the box that was
 * just changed, this method will update the available values in subsequent boxes:
 */
JavascriptOptionBoxes.prototype.set_option = function(unique_id, option_obj)
	{
	/* Look up the current page count for this book (may change as user toggles TOC on and off):
	 */
	var page_count = get_final_pages(unique_id);
		
	/* If this function was called, the user changed an option; set a flag so we can auto-save their changes:
	 */
	this.mark_unsaved_changes(unique_id);

	/* Simple variables to use as object pointers and loop counters:
	 */
	var o;
	var x;
	var y;

	/* Loop through all the tiers and figure out which tiers are children of the currently selected
	 * tier and which values to fill in to those tiers.
	 */
	var value_pointer = this.values;
	var found_selected_tier = false;
	var children = new Array();
	var first_child_values = new Array();
	for (x = 0; x < this.tiers.length; x++)
		{
		/* If we've already found the selected tier, any subsequent tiers are children and must be stored:
		 */
		if (found_selected_tier)
			children[children.length] = x;

		/* If we've just found the first child, we need to save the values that apply to that child:
		 */
		if (children.length == 1)
			{
			for (y in value_pointer)
				{
				/* Make sure we are adding only string (this gets around the moo tools functions for extending the array class)
				 */
				if (typeof(value_pointer[y]) == "function")
					continue;
				/* Only save options that apply to the current page count:
				 */
				if (this.option_applies(value_pointer[y], page_count))
					{
					first_child_values[first_child_values.length] =  new JavascriptOption(y, value_pointer[y]['description']);
					}
				}
			}

		/* Have we found the selected tier?  If so, set a flag so we can start collecting children.
		 */
		if (option_obj.name == unique_id + this.tiers[x]['code'])
			found_selected_tier = true;

		/* Navigate down the values tree.  Note that if we can't find any further values, we just set the
		 * pointer to an empty array.  This indicates that the user has either hit the end of the tiers
		 * or has selected an invalid option (i.e. choosing "step 3" before filling in a value for step 2).
		 */
		o = document.getElementById(unique_id + this.tiers[x]['code']);
		if (!o)
			break;
		if (value_pointer[o.value])
			value_pointer = value_pointer[o.value]['sub_values'];
		else
			value_pointer = new Array();
		}

	/* Process the next child, then recurse to handle further children if necessary:
	 */
	var current_tier;
	var current_values;
	if (children.length > 0)
		{
		/* Get the option box corresponding with the current tier, and break the loop if this fails:
		 */
		current_tier = children[0];
		o = document.getElementById(unique_id + this.tiers[current_tier]['code']);
		if (o)
			{
			/* Get the previous selected value for the box:
			 */
			if (o.selectedIndex >= 0 && o.selectedIndex < o.options.length)
				var old_selected_value = o.options[o.selectedIndex].value;
			else
				var old_selected_value = "no match";
				
			/* Clear the options for this box:
			 */
			for (y = o.options.length - 1; y >= 0; y--)
				o.options[y] = null;
	
			/* Now fill in new options for the box -- if it's the first child and has at least one valid option,
			 * use the options determined above; otherwise, fill in a dummy value since we have to wait for the
			 * user's next choice.
			 */
			if (first_child_values.length > 0)
				{
				current_values = first_child_values;
				var new_selected_index = 0;
				for (y = 0; y < current_values.length; y++)
					{
					o.options[y] = new Option(current_values[y].option_value);
					o.options[y].value = current_values[y].option_name;
					if (o.options[y].value == old_selected_value)
						new_selected_index = y;
					}
				o.selectedIndex = new_selected_index;
				
				/* Recurse to the next box:
				 */
				this.set_option(unique_id, o);
				}
			}
		}
	}

/* Validate the options in the boxes identified by unique_id, returning true if they are all filled in,
 * false otherwise.  If check_title is true, we also check for a title in a special field.  If show_msg
 * is true, we pop up an error message when applicable.
 */
JavascriptOptionBoxes.prototype.validate_options = function(unique_id, check_title, show_msg)
	{
    var error_count = 0;
    var error_message = "";
    var o;

    /* Loop through all tiers and search for missing values:
     */
	for (x = 0; x < this.tiers.length; x++)
		{
		o = document.getElementById(unique_id + this.tiers[x]['code']);
		if (!o || !o.value || o.value == "")
			{
			error_count++;
			error_message += "Please choose a " + this.tiers[x]['description'] + " before continuing.\n";
			}
		}

    /* If requested, also check for a missing title:
     */
    if (check_title)
    	{
	    o = document.getElementById(unique_id + "Option27");
		if(!o || o.value.trim().length < 1)
			{
			error_count++;
			error_message += "Please input a title before continuing.\n";
			}
    	}

    /* If we have no errors, return true; otherwise display error if necessary and return false:
     */
	if (error_count == 0)
		return true;
	else
		{
		if(show_msg)
			alert(error_message);
		return false;
		}
	}

/* Save the options in the box identified by unique_id:
* unsaved_list and index are optional paramaters
* unsaved_list is an array of item ids that need to saved after your item
* index is the current number we are on.
* frm_params An associate object of index => value for form items that need to be set to 
* 			  a current  value before the form is submitted
* frm_action The action/url where the from is going to be submitted to
* 
* frm_name The form we are going to submit to the frm_action variable.
* 
 */
JavascriptOptionBoxes.prototype.save = function(unique_id,unsaved_list,index,frm_params,frm_action,frm_name)
	{
	/* Store the ID of the options we are working with in the form:
	 */
	var o = document.getElementById("sumValue");
	o.value = unique_id;

	/* Only save if the options are valid:
	 */
	if (this.validate_options(unique_id, true, true))
		{
		/* This sucks but if there is an order document thing we need to get the index and make it into an array
		 * that we can read and save.
		 */
		var doc_order = document.all ? document.all[unique_id + "options_tbl_doc_order"] :
			document.getElementById(unique_id + "options_tbl_doc_order");

		if (doc_order != "undefined" && doc_order != null)
			{
			var	hideInfo = document.all ? document.all["hideInfo"] :
				document.getElementById("hideInfo");
			var doc_order_html = "";
			for (var i = 0;i < doc_order.length; i++)
				doc_order_html += "<input type='hidden' name='doc_order[]' value='" + doc_order.options[i]['value'] + "'>";

			hideInfo.style.display = "none";
			hideInfo.innerHTML = doc_order_html;
			}

		/* Change the description to loading text while we save...:
		 */
		var descript = unique_id + "_discript";
		document.getElementById(descript).innerHTML =
			"<font color='red'>Saving your options<br>Please wait...<br><br><br><br></font>";
		document.getElementById('move').value = '';


		//If we have three arguments that means we have to save an array of items and there are more items to be saved
		//The array and index it is currently one are passed in and we have to detect if that is the case
		//before going into the function for onComplete of the javascript
		var another_save = (arguments.length == 4 ? true : false);
		var sub_on_complete = (arguments.length == 6 ? true : false);

		/* Save the options using AJAX if possible:
		 */
		var o = document.getElementById('mainForm');
		if (o)
			{
			if (can_support_ajax())
				{
				$('mainForm').action = 'save_opt.php';
				//o.ajform_submit();
				$('mainForm').send(
					{
					data: $('mainForm'),
					onComplete: function()
						{
						document.getElementById("hideInfo").innerHTML = "";
						var el = document.getElementById("sumValue");
						var descript = el.value + "_discript";
						var n_title = el.value + "_title";
//						$(n_title).setStyle('font-weight','bold');
						$(n_title).setText(document.getElementById(el.value + 'Option27').value);
						document.getElementById(descript).innerHTML = get_options_page_details(el.value, get_final_pages(el.value));
						document.getElementById('mainForm').action = 'save_opt.php';
						if (another_save)
    						{
					        save_multiple_items(unsaved_list,parseInt(index + 1),frm_params);
							}
						else if(sub_on_complete)
							{
							if($type(frm_params) == "object")
								{
								$each(frm_params,function(index_item,item_frm_param)
									{
									$(index_item).value = item_frm_param;
									});
								}
							$(frm_name).action = frm_action;
							$(frm_name).submit();	
							}
						}
					}
					);
				}
			else
				{
				o.submit();
				}
			}

		/* Flag the fact that we saved the changes:
		 */
		this.unsaved_changes[unique_id] = false;

		return true;
		}

	return false;
	}

/* Save the options in the box identified by unique_id, then close the box:
 */
JavascriptOptionBoxes.prototype.save_and_close = function(unique_id)
	{
	if (this.save(unique_id))
		{
		hide_tbl(unique_id + "options_tbl");
		return true;
		}
	return false;
	}

JavascriptOptionBoxes.prototype.user_ready_to_move = function(check_unsaved, validate_options,direction)
	{
	/* If we're supposed to validate the current options, do so now:
	 */
	if (validate_options)
		{
		var errormsg = "";
		for (var n in this.keys_to_check)
			{
			/*Make sure we are adding only string ( this gets arount the moo tools for extending the array class)
			*/
			if (typeof(this.keys_to_check[n]) == "function")
					continue;
			if (!this.validate_options(this.keys_to_check[n], true, false))
				{
				errormsg += "Item #" + (parseInt(this.keys_to_check[n]) + 1) + " has an error.\n";
				}
			}

		if (errormsg.length > 0)
			{
			alert(errormsg);
			return false;
			}
		}

	/* If we're supposed to check for unsaved options, do so now:
	 */
	if (check_unsaved)
		{
		var unsaved_list = new Array();
		var unsaved_count = 0;
		for (var n in this.keys_to_check)
			{
			/*Make sure we are adding only string ( this gets arount the moo tools for extending the array class)
			*/
			if (typeof(this.keys_to_check[n]) == "function")
					continue;
					
			//We only want to save items that have not been canceled ( ie the options are not hidden) 
			if (this.unsaved_changes[n] == true && $(n + "options_tbl_div").style.display != "none")
				{
//				if (unsaved_count > 0)
//					unsaved_list += ", ";
				unsaved_list.extend([parseInt(this.keys_to_check[n])]);
				unsaved_count++;
				}
			}

		if (unsaved_count > 0)
			{
//			alert(unsaved_count);
			//Save All the items that are not saved start at the top and go to threw the array
			//var Animals = ['Cat', 'Dog', 'Coala', 'Lizard'];
			//var Speech = ['Miao', 'Bau', 'Fruuu', 'Mute'];
			var frm_params = ['move'].associate([direction]);
			//frm_params = 
//			frm_params.length;
			save_multiple_items(unsaved_list,0,frm_params);
			
			//We are returning false because we need to save multi items in an asyncronus matter we can not do that in this function and it is handled in the save method 
			return false;
			}
		}

	return true;
	}

/* Move to the specified page in the UI:
 *
 * direction = "forward" or "back"
 * check_unsaved = boolean (do we pop up a box if the user has not saved all changes?)
 * validate_options = boolean (do we validate user options before continuing?)
 */
JavascriptOptionBoxes.prototype.move = function(direction, check_unsaved, validate_options)
	{
	/* Code to debug form validation errors in Opera:
	var e = document.getElementsByTagName('input');
	for (var n in e)
		{
		if (e[n].validity.valid == false)
			{
			var err = e[n].name + "\n";
			for (var n2 in e[n].validity)
				err += n2 + " = " + e[n].validity[n2] + "\n";
			alert(err);
			}
		}
	return;
	 */

	/* Don't allow the user to move forward if there are problems on the page:
	 */
	var problems = parseInt(document.getElementById('problems').value);
	if (problems > 0 && direction == "forward")
		{
		if (problems == 1)
			alert("There is a problem on this page.  Please fix it before continuing.");
		else
			alert("There are problems on this page.  Please fix them before continuing.");
		return false;
		}
	
	
	var ready_to_move = this.user_ready_to_move(check_unsaved, validate_options,direction);
	if (ready_to_move)
		{
		document.getElementById('move').value = direction;
		//document.getElementById("hideInfo").innerHTML = form_options;
		var o = document.getElementById('mainForm');
		if (o)
			{
			o.action = "displayOptsMulti.php";
			o.submit();
			}
		}
	return ready_to_move;
	}

JavascriptOptionBoxes.prototype.get_tier_display_suffix = function(i)
	{
	return this.tiers[i]['display_suffix'];
	}

JavascriptOptionBoxes.prototype.process_toc_checkbox = function(perform_toggle, unique_id)
	{
	/* Toggle the checkbox if the onclick is triggered by something other than the box itself
	 * (if triggered by the box, the toggle will already have happened):
	 */
	if (perform_toggle == true)
		{
		var o = document.getElementById('has_toc_' + unique_id);
		if (o)
			o.checked = !o.checked;
		}
		
	/* Redraw the available options in case they have changed due to the updated page count caused
	 * by toggling the TOC option:
	 */
	this.set_option(unique_id, document.getElementById(unique_id + this.tiers[0]['code']));
	}
	
JavascriptOptionBoxes.prototype.process_pagenum_checkbox = function(perform_toggle, unique_id)
	{
	/* Toggle the checkbox if the onclick is triggered by something other than the box itself
	 * (if triggered by the box, the toggle will already have happened):
	 */
	if (perform_toggle == true)
		{
		var o = document.getElementById('number_pages_' + unique_id);
		if (o)
			o.checked = !o.checked;
		}
		
	/* The user changed an option; set a flag so we can auto-save their changes:
	 */
	this.mark_unsaved_changes(unique_id);
	}

JavascriptOptionBoxes.prototype.get_options_by_tier = function(selections)
	{
	var current_index = 0;
	var current_tier = this.values;
	var options = new Array();

	while(true)
		{
		/* Give up if we've hit the end of the line:
		 */
		var current_tier_length = 0;
		if (current_tier)
			{
			for (var counter in current_tier)
				{
				/*Make sure we are adding only string ( this gets arount the moo tools for extending the array class)
				*/
				if (typeof(current_tier[counter]) == "function")
					continue;
				current_tier_length++;
				}
			}
		if (current_tier_length == 0)
			break;

		/* Get the options for the current level:
		 */
		var keys = new Array();
		for(var current_key in current_tier)
			{
			keys[keys.length] = current_key;
			if (!options[current_index])
				options[current_index] = new Array();
			options[current_index][current_key] = current_tier[current_key]['description'];
			}

		/* Figure out which path to follow to the next tier -- first try selection if valid, then default
		 * to first available option if necessary.
		 */
		indexes_to_check = new Array(selections[current_index], keys[0]);
		for(var i in indexes_to_check)
			{
			var ind = indexes_to_check[i];
			if (ind && ind.length > 0 && current_tier[ind])
				{
				current_tier = current_tier[ind]['sub_values'];
				break;
				}
			}

		current_index++;
		}

	return options;
	}


/* Move the selected item up a space in the specified select list (used for organizing merge list):
 *before m_tools we had to do the following
 *     //var old_item = drop_down.options[new_index];
 *   //var new_item = drop_down.options[selected_index];
 *   //curr_options[new_index] = new Option(new_item["text"],new_item['value']);
 *	//curr_options[new_index]['title'] = new_item['title'];
 *	//curr_options[selected_index] = new Option(old_item["text"],old_item['value']);
 *	//curr_options[selected_index]['title'] = old_item['title'];
 *
 */
function swap_list_up(element_id_name)
	{
	var drop_down = document.getElementById(element_id_name);
	var selected_index = drop_down.selectedIndex;
	if(selected_index == 0 )
		return;
	var new_index = selected_index - 1;
	var curr_options = drop_down.options;
   	swap_list_items(selected_index,new_index,drop_down);

   	//Fix a bug in Opera and Safari
	setTimeout("set_swap_list_index('" + element_id_name + "',"+ new_index +")",1);
	}

function set_swap_list_index(element_id_name,new_index)
	{
	$(element_id_name).getChildren().each(function(item, index){
			if(index == new_index)
				{
				//alert(item.text);
				}
			else
				{
				item.selected = null;
				}
		});
	}


/*Function to save mutliple boxes with one function call
*/
function save_multiple_items(unsaved_list,index,frm_params)
	{
	var item = unsaved_list[index];

	/*If we are not at the end of the list then we don't want to return and we want to get called again
	 *to do this we only send in 4 paramaters that can be used to call this function... If we are at the last index 
	 * in the list then we don't need to come back here and we give the save function enough paramaters so that it can submit the form
	 */
	if(unsaved_list.length > parseInt(index + 1))
		{
		top.jopt_boxes.save(item,unsaved_list,index,frm_params);
		}
	else
		{
		top.jopt_boxes.save(item,null,null,frm_params,"displayOptsMulti.php",'mainForm');
		}
	}


/*Mootools methods for swapping items
 */
function swap_list_items(selected_index,new_index,drop_down)
	{
	var clone_new = $(drop_down.options[selected_index]).clone();
    var clone_old = $(drop_down.options[new_index]).clone();
    $(drop_down.options[new_index]).replaceWith(clone_new);
    $(drop_down.options[selected_index]).replaceWith(clone_old);
    drop_down.selectedIndex = new_index;
	}

/* Move the selected item down a space in the specified select list (used for organizing merge list):
 */
function swap_list_down(element_id_name)
	{
	var drop_down = document.getElementById(element_id_name);
	var selected_index = drop_down.selectedIndex;
	if(selected_index >=  (drop_down.length - 1))
		return;
	var new_index = selected_index + 1;
	var curr_options = drop_down.options;
    swap_list_items(selected_index,new_index,drop_down);
	}

 /** constructor

      @param duration integer seconds
      @param <optional> function to run while waiting.
   */
function Pause(duration, busy)
	{
    this.duration= duration * 1000;
    this.busywork = null; // function to call while waiting.
    this.runner = 0;
    if (arguments.length == 2)
    	{
        this.busywork = busy;
		}
      this.pause(this.duration);

	}
	// Pause class
   /** pause method
       @param duration: integer in seconds
    */
Pause.prototype.pause = function(duration)
	{
	if ( (duration == null) || (duration < 0))
		{
      	return;
   	  	}
    var later = (new Date()).getTime() + duration;
	while(true)
		{
       	if ((new Date()).getTime() > later)
       		{
          	break;
       		}
		this.runner++;
    	if (this.busywork != null)
    		{
	   		this.busywork(this.runner);
    	    }
		} // while
	} // pause method