/* **************************************** Pricing Module Functions ********************************** */

document.ajurl = "https://web165.secure-secure.co.uk/folders4u.co.uk/pricemod.php";

/* **************************************** Common Utility Functions ********************************* */

// converts response lines to associative array
function responseToAssoc(lines){
  var list = new Array();
  for (var i=1; i<lines.length; i++){
    if (lines[i]=="") continue;
    list[i-1] = new Array();
    var entries = lines[i].split("|");
	for (var j=0; j<entries.length; j++){
	  var name= entries[j].replace(/\=.*/, '');
	  var val = entries[j].replace(/.*\=/, '');
	  list[i-1][name]=val;
	}
  }
  return list;
}

// find absolute position of object
function findPos(obj){
    var curleft = curtop = 0;
    if (obj.offsetParent) {
	    do {
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop;
		} while (obj = obj.offsetParent);
	}	
    return [curleft, curtop];
}

// show specified main section
function show(secid){
  var secs = new Array('price_components', 'price_defaults', 'product_pricing', 'price_tables');
  for (var i = 0; i<secs.length; i++){
    var id = secs[i];
	var vis = (id==secid)? "block" : "none";
	var clas = (id==secid)? "sel" : "";
	document.getElementById(id).style.display=vis;
	document.getElementById("show"+i).className = clas;
  }
}

// converts price associative array from SQL table query to a tree array
function priceAssocToTree(assoc){
    var tree = new Array();
	document.alloption2s=new Array();
    for (var i in assoc){
	    var entry = assoc[i];
	    var qty = entry['qty'];
		var op1 = entry['option1'];
		var op2 = entry['option2'];
		if (!document.alloption2s[op2]) document.alloption2s[op2]=1;
	    if (!tree[op1]) tree[op1]=new Array();
		if (!tree[op1][op2]) tree[op1][op2]=new Array();
		tree[op1][op2][qty] = parseFloat(entry['price']);
	}
	return tree;
}

// gets the span of a tree (ie number of table columns needed)
function getTreeSpan(tree){
    if (!(tree instanceof Array)) return 1;    
    var myspan = 0;
	for (var i in tree) myspan += (getTreeSpan(tree[i]));
    return myspan;
}

// show properties of an array
function arrProps(arr, ind){
    if (!ind) ind="";
    if (!(arr instanceof Array)) return arr;
	var s = "\r\n"+ind+"[";
	var sep="";
	for (var i in arr){
	  s = s + (sep+i+" = "+(arrProps(arr[i], ind+"  ")));
	  sep=",\r\n"+ind;
	}
	s=s+"]";
	return s;
}

// shows/hides waiting (by changing classname of first <h1> element
function showWaiting(show){
  var classn=(show)? "waiting" : "";
  var h1s = document.getElementsByTagName("H1");
  h1s[0].className = classn;
  document.body.style.cursor = (show)? "wait" : "default";
}

/* **************************************** Price Component Functions ********************************* */

// list components
function listComps(){
  aj_setHandler("pricehandler", document.ajurl, true);
  document.getElementById("component_list").innerHTML="<h3>Updating...</h3>";
  var params = new Object();
  aj_startHandler("pricehandler", "ListComponents", "gotCompList", params);  
  showWaiting(true);
}

// Response Handler: display components list
function gotCompList(){
  showWaiting(false);
  var lines=aj_getResponseLines("pricehandler");
  if (lines[0]=="Error"){
    alert(aj_getResponse("pricehandler"));
	return;
  }
  document.componentOpNames = new Array();
  var list = responseToAssoc(lines);  
  var htm = "<table class='PC_listing'><tr>";
  for (var i in list[0]) htm += "<th>"+(i.replace(/_/g, " "))+"</th>";  
  htm += "<th colspan='1'>&nbsp;</th></tr>";
  var pricesmissing = false;
  for (var i=0; i<list.length; i++){
	list[i]['no_charge'] = (list[i]['no_charge']==0)? "-" : "Y";
	list[i]['priced'] = (list[i]['priced']==0)? "-" : "Y";	
	var tdc = (list[i]['no_charge']=="-" && list[i]['priced']=="-")? " class='PC_alert'" : "";  
	if (tdc!="") pricesmissing=true;
    htm += "<tr id='compline_"+i+"'>";
	document.componentOpNames[list[i]['code']] = new Array(list[i]['name_option_1'], list[i]['name_option_2']);
	for (var j in list[i]) htm += "<td"+tdc+">"+list[i][j]+"</td>";
	htm += "<td><input type='button' class='linebut' name='del' value='delete' onclick='deleteComponent(\""+list[i]['code']+"\");' />";
	htm += "&nbsp;<input type='button' class='linebut' name='edi' value='edit' onclick='editComponent(\""+list[i]['code']+"\");' />";	
	htm += "&nbsp;<input type='button' class='linebut' name='ops' value='options' onclick='showOptions(\""+list[i]['code']+"\", \"compline_"+i+"\");' /></td>";		
	htm += "</tr>\r\n";
  }
  htm += "</table>";
  document.getElementById("component_list").innerHTML = htm;
  aj_flush("pricehandler");  
  if (pricesmissing) alert("Some component options are missing price data. See Price Components for details");
}

// hide component editor
function cancelComponent(){
  var fields = Array('code', 'name', 'description', 'name_option_1', 'name_option_2');
  for(var i=0; i<fields.length; i++){
    var id = "ce_"+fields[i];
	document.getElementById(id).value="";
  }
  document.getElementById("component_editor").style.display="none";
}

// submit component editor
function submitComponent(){
  var fields = Array('code', 'name', 'description', 'name_option_1', 'name_option_2');
  var params = new Object();
  for(var i=0; i<fields.length; i++){
    var id = "ce_"+fields[i];
	params["PC_"+fields[i]]=document.getElementById(id).value;
  }
  params["PC_no_charge"] = document.getElementById("ce_no_charge").checked? 1 : 0;
  var cmd = document.getElementById("ce_command").value;
  
  aj_setHandler("pricehandler", document.ajurl, true);
  aj_startHandler("pricehandler", cmd, "compupdated", params);
  document.getElementById("component_editor").style.display="none";  
  showWaiting(true);
}

// handle delete component request
function deleteComponent(code){
  if (!confirm("Delete component: '"+code+"'?")) return;
  var params = new Object();
  params['PC_code']=code;
  aj_setHandler("pricehandler", document.ajurl, true);
  aj_startHandler("pricehandler", "DeleteComponent", "compupdated", params);  
  showWaiting(true);
}

// Response Handler: components changed - update list
function compupdated(){
  showWaiting(false);
  var resp = aj_getResponseLines("pricehandler");
  if (resp[0]=="OK"){
	cancelComponent();
    listComps();
  }	
  else alert(resp[0]+"\r\n"+resp[1]);
  aj_flush("pricehandler");  
  updatePriceComponents();
}

// show add component dialogue
function showAddComponent(){
  document.getElementById("ce_command").value="CreateComponent";
  document.getElementById("ce_header").innerHTML = "Create Pricing Component";  
  document.getElementById("ce_code").disabled = false;  
  document.getElementById("component_editor").style.display="block";
}

// handle edit component request
function editComponent(comp){
  aj_setHandler("pricehandler", document.ajurl, true);
  var params = new Object();
  params['PC_code'] = comp;
  aj_startHandler("pricehandler", "GetComponent", "showEditComponent", params);
  showWaiting(true);
}

// Response Handler: show edit component dialogue
function showEditComponent(){
  showWaiting(false);
  var lines = aj_getResponseLines("pricehandler");
  if (lines[0]=="Error"){
    alert(aj_getResponse("pricehandler"));
    aj_flush("pricehandler");	
	return;
  }
  var entries = responseToAssoc(lines);  
  var entry = entries[0];
  document.getElementById("ce_code").value=entry['code'];
  document.getElementById("ce_code").disabled = true;
  document.getElementById("ce_name").value=entry['name'];
  document.getElementById("ce_description").value=entry['description'];
  document.getElementById("ce_name_option_1").value=entry['name_option_1'];
  document.getElementById("ce_name_option_2").value=entry['name_option_2'];
  document.getElementById("ce_no_charge").checked = (entry['no_charge']==1);
  document.getElementById("ce_command").value="UpdateComponent";
  document.getElementById("ce_header").innerHTML = "Edit Pricing Component";
  document.getElementById("component_editor").style.display="block";  
  aj_flush("pricehandler");  
  var ced = document.getElementById("component_editor");
  makeDraggable(ced, 1, 1, ced);    
}

// handle show options for component request
function showOptions(code, lineref){
  document.oplineref = lineref;
  document.getElementById(lineref).className="sel";
  aj_setHandler("pricehandler", document.ajurl, true);
  var params = new Object();
  params['PC_component'] = code;
  document.selcode = code;
  aj_startHandler("pricehandler", "ListOptions", "gotOptionList", params);
  document.getElementById("options_list").innerHTML = "<h3>Updating...</h3>";
  showWaiting(true);  
}

// Response Handler: show options list
function gotOptionList(){
  showWaiting(false);
  var res = aj_getResponseLines("pricehandler");
  if (res[0]=="ERROR"){
    alert(aj_getResponse("pricehandler"));
	aj_flush("pricehandler");
	return;
  }  
  var ops = responseToAssoc(res);
  var opdiv = document.getElementById("options_list");
  var compline = document.getElementById(document.oplineref);
  var cpos = findPos(compline);
  var htm = "<table class='PC_listing'><tr><th>Option</th><th>Description</th><th>Priced</th><th>&nbsp</th></tr>\r\n";
  for (var i=0; i<ops.length; i++){
      var tdc = (ops[i]['priced']==0)? " class='PC_alert'" : "";	  
      htm += "<tr>";
	  htm += "<td"+tdc+">"+ops[i]['option']+"</td><td"+tdc+">"+ops[i]['description']+"</td>";
	  var priced = (ops[i]['priced']==1)? "Y" : "-";
	  htm += "<td"+tdc+">"+priced+"</td>";	  
	  htm += "<td><input type='button' class='linebut' name='delete' value='Delete' onclick='deleteOption(\""+
	         ops[i]['component']+"\", \""+ops[i]['option']+"\");' />&nbsp;";			 
	  htm += "<input type='button' class='linebut' name='edit' value='Edit' onclick='editOption(\""+
	         ops[i]['component']+"\", \""+ops[i]['option']+"\");' /></td>\r\n";
	  htm += "</tr>\r\n";
  }
  htm += "</table>\r\n";
  if (ops.length==0) htm = "<h3>No entries</h3>";
  htm += "<div id='buttons'><input type='button' name='canc' value='Close' onclick='closeOptions()' />\r\n";
  htm += "<input type='button' name='add' value='Add Option' onclick='showAddOption(\""+document.selcode+"\")' /></div>\r\n"
  opdiv.innerHTML = htm;
  opdiv.style.left = (cpos[0]+25) + "px";
// component_list  scroll offset
  var cldiv = document.getElementById("component_list");
  opdiv.style.top = (cpos[1]+compline.offsetHeight-cldiv.scrollTop) + "px";
  opdiv.style.display="block";  
  makeDraggable(opdiv, 1, 1, opdiv);    
}

// handle close options list request
function closeOptions(){
  lineref = document.oplineref;
  document.getElementById(lineref).className="";
  document.getElementById("options_list").style.display="none";
}

// show add option dialogue
function showAddOption(){
  document.getElementById("op_component").value = document.selcode;
  document.getElementById("op_command").value = "CreateOption";
  document.getElementById("op_header").innerHTML = "Add Option for '"+document.selcode+"'";
  document.getElementById("op_option").value = "";  
  document.getElementById("op_description").value = "";    
  document.getElementById("op_option").disabled = false;
  var opos = findPos(document.getElementById("options_list"));
  var oped = document.getElementById("option_editor");
  oped.style.left = (opos[0]+25)+"px";
  oped.style.top = (opos[1]+25)+"px";    
  oped.style.display="block";  
  makeDraggable(oped, 1, 1, oped);  
}

// handle edit option request
function editOption(component, option){
  document.selcode = component;
  aj_setHandler("pricehandler", document.ajurl, true);
  var params = new Object();
  params['PC_component'] = component;
  params['PC_option'] = option;  
  aj_startHandler("pricehandler", "GetOption", "showEditOption", params);  
  showWaiting(true);  
}

// Response Handler: show edit option dialogue
function showEditOption(){
  showWaiting(false);
  var lines = aj_getResponseLines("pricehandler");
  if (lines[0]=="Error"){
    alert(aj_getResponse("pricehandler"));
    aj_flush("pricehandler");	
	return;
  }
  var entries = responseToAssoc(lines);  
  var entry = entries[0];
  document.getElementById("op_component").value=entry['component'];
  document.getElementById("op_option").disabled = true;
  document.getElementById("op_option").value=entry['option'];
  document.getElementById("op_description").value=entry['description'];
  document.getElementById("op_command").value="UpdateOption";
  document.getElementById("op_header").innerHTML = "Edit Option: "+entry['option'];
  var opos = findPos(document.getElementById("options_list"));
  var oped = document.getElementById("option_editor");
  oped.style.left = (opos[0]+25)+"px";
  oped.style.top = (opos[1]+25)+"px";    
  oped.style.display="block";  
  aj_flush("pricehandler");  
  makeDraggable(oped, 1, 1, oped);    
}

// close edit option dialogue
function closeEditOption(){
  document.getElementById("option_editor").style.display="none";  
}

// handle deletion option request
function deleteOption(component, option){
  if (!confirm("Delete option: '"+option+"'?")) return;
  document.selcode = component;
  aj_setHandler("pricehandler", document.ajurl, true);
  var params = new Object();
  params['PC_component'] = component;
  params['PC_option'] = option;  
  aj_startHandler("pricehandler", "DeleteOption", "opsupdated", params);  
  showWaiting(true);  
}

// submit edit/add option dialogue
function submitOption(){
  var fields = Array('component', 'option', 'description');
  var params = new Object();
  for(var i=0; i<fields.length; i++){
    var id = "op_"+fields[i];
	params["PC_"+fields[i]]=document.getElementById(id).value;
  }
  var cmd = document.getElementById("op_command").value;
  
  aj_setHandler("pricehandler", document.ajurl, true);
  aj_startHandler("pricehandler", cmd, "opsupdated", params);
  document.getElementById("option_editor").style.display="none";
  showWaiting(true);  
}

// Response Handler: refresh options list
function opsupdated(){
  showWaiting(false);
  var resp = aj_getResponseLines("pricehandler");
  if (resp[0]=="OK"){
	closeEditOption();
    showOptions(document.selcode, document.oplineref);
  }	
  else alert(resp[0]+"\r\n"+resp[1]);
  aj_flush("pricehandler");  
}

// clears the quote structure
function clearQuote(){
  if (!confirm("Really clear quote?")) return;
  params = new Object();
  aj_setHandler("pricehandler", document.ajurl, true);
  aj_startHandler("pricehandler", "ClearQuote", "quoteCleared", params);
  document.getElementById("option_editor").style.display="none";
  showWaiting(true);    
}
function quoteCleared(){
  showWaiting(false);
  var resp = aj_getResponseLines("pricehandler");
  if (resp[0]=="OK") selectProdCode();
  else alert(resp[0]+"\r\n"+resp[1]);
  aj_flush("pricehandler");    
}

// shows save quote dialog
function saveQuote(){
  buildQuoteDialogs();
  var pms = document.getElementById("pm_savequote");
  pms.style.position="absolute";
  pms.style.zIndex="9999";
  pms.style.left="20px";
  pms.style.top="20px";
  pms.style.width="400px";
  pms.style.height="300px";
  pms.style.display="block";
}

function doSaveQuote(conf){
  if (conf==0){
	var pms = document.getElementById("pm_savequote");
    pms.style.display="none";
	return;
  }
  alert("OK, saving quote");
}

// shows load quote dialog
function loadQuote(){
}

function buildQuoteDialogs(){
  if (document.getElementById("pm_savequote")) return;
  // build save dialog
  var pms = document.createElement("div");
  pms.id="pm_savequote";
  pms.innerHTML = "<h3 style='margin: 0px 0px 5px 0px;'>Save quote</h3>"+
                  "<label for='qjobref' style='width: 90px; display: block; float: left;'>Job Ref</label>"+
				  "<input id='qjobref' type='text' name='qjobref' value='' style='width: 280px;'/><br />"+
                  "<label for='qcontact' style='width: 90px; display: block; float: left;'>Contact</label>"+
				  "<input id='qcontact' type='text' name='qcontact' value='' style='width: 280px;'/><br />"+
                  "<label for='qcompany' style='width: 90px; display: block; float: left;'>Company</label>"+
				  "<input id='qcompany' type='text' name='qcompany' value='' style='width: 280px;'/><br />"+
                  "<label for='qphone' style='width: 90px; display: block; float: left;'>Phone</label>"+
				  "<input id='qphone' type='text' name='qphone' value='' style='width: 280px;'/><br />"+
                  "<label for='qemail' style='width: 90px; display: block; float: left;'>Email</label>"+
				  "<input id='qemail' type='text' name='qemail' value='' style='width: 280px;'/><br />"+
                  "<label for='qdescript' style='width: 90px; display: block; float: left;'>Description</label>"+
				  "<textarea id='qdescript' name='qdescript' style='width: 280px; height: 150px'></textarea>"+
                  "<input type='button' onclick='doSaveQuote(1);' value='Save' />&nbsp;&nbsp;"+
                  "<input type='button' onclick='doSaveQuote(0);' value='Cancel' />";
  pms.style.border="1px solid #333333";
  pms.style.backgroundColor="#ffffff";
  pms.style.display="none";
  pms.style.padding="5px";
  document.body.appendChild(pms);				  
  // build load dialog
  var pml = document.createElement("div");
  pml.innerHTML = "<h3>Load quote</h3>"+
                  "<div id='qsearch'>"+
                  "<label for='qjobref'>Job Ref</label><input id='qjobref' type='text' name='qjobref' value='' />"+
                  "<label for='qcompany'>Company</label><input id='qcompany' type='text' name='qcompany' value='' />"+
                  "<input type='button' onclick='quoteSearch();'>Search</input>"+
				  "</div>"+
				  "<div id='qfound'><div id='qsearchresults'></div>"+
				  "<input type='button' onclick='doLoadQuote();' value='Load' /></div>";
  pml.style.display="none";
  document.body.appendChild(pml);
}


/* **************************************** Product Type Default Price Structure Functions ********************************* */

// get product types
function listProdTypes(){
  aj_setHandler("structhandler", document.ajurl, true);
  params = new Object();
  aj_startHandler("structhandler", "ListProductTypes", "gotProdTypes", params);
  showWaiting(true);  
}

// Response Handler: show product type selector
function gotProdTypes(){
  showWaiting(false);
  var res = aj_getResponseLines("structhandler");
  if (res[0]=="OK"){
	var htm="<label for='prod_type_sel'>Product Type:</label>&nbsp;";
	htm += "<select id='prod_type_sel' name='prod_type_sel' onchange='selectProdType()'>";
	for (var i=1; i<res.length; i++){
	  if (res[i]=="") continue;
	  htm += "<option id='"+res[i]+"' value='"+res[i]+"'>"+res[i]+"</option>\r\n";
	}
	htm += "</select>\r\n";    
	document.getElementById("prod_type_selector").innerHTML = htm;
  }	
  else alert(aj_getResponse("structhandler"));
  aj_flush("structhandler");    
}

// handle product type selection event
function selectProdType(){
	var val = document.getElementById('prod_type_sel').value;
	document.getElementById("prod_type_selector").style.display="none";
	document.getElementById("def_price_struct").innerHTML = "<h2>Updating...</h2>";
	document.product_type = val;
    aj_setHandler("structhandler", document.ajurl, true);
    params = new Object();
	params['PC_default']=1;
	params['PC_code']=val;	
    aj_startHandler("structhandler", "ListPriceStruct", "gotDefaultStruct", params);	
    showWaiting(true);	
}

// Response Handler: update default price structure
function gotDefaultStruct(){
  showWaiting(false);
    document.getElementById("edit_def_struct").style.display="none";
    var res = aj_getResponseLines("structhandler");
    if (res[0]=="ERROR"){
	  alert(aj_getResponse("structhandler"));
	  aj_flush("structhandler");
	  return;
	}
      var assoc = responseToAssoc(res);
	  var htm="<table class='PC_listing'><tr><th>component</th><th>title</th><th class='ac'>fixed</th>"+
	          "<th>option</th><th>priced</th><th>&nbsp;</th></tr>\r\n";
	  var missing=false;
	  for (var i=0; i<assoc.length; i++){
          var tdc = (assoc[i]['priced']==0)? " class='PC_alert'" : "";	  
		  if (tdc!="") missing=true;
	      htm += "<tr>";
		  var hip = "<input type='hidden' id='dseq_"+i+"' value='"+assoc[i]['order_seq']+"' />";
		  htm += "<td"+tdc+">"+hip+assoc[i]['order_seq']+". "+assoc[i]['component']+"</td>";
		  htm += "<td"+tdc+">"+assoc[i]['name']+"</td>";		  
		  htm += "<td"+tdc+">"+((assoc[i]['fixed']==1)? "Y" : "-")+"</td>";		  
		  htm += "<td"+tdc+">"+assoc[i]['default_value']+"</td>";		  		  
		  htm += "<td"+tdc+">"+((assoc[i]['priced']==1)? "Y" : "-")+"</td>";		  
		  var ref = '"'+assoc[i]['component']+'", '+assoc[i]['order_seq']+', "'+assoc[i]['default_value']+'", '+assoc[i]['fixed'];
		  htm += "<td><input type='button' class='linebut' name='delete' value='Delete' onclick='deleteDefComp("+ref+")' />";
//		  htm += "&nbsp;<input type='button' class='linebut' name='up' value='Up' onclick='defCompUp("+ref+")' />";
//		  htm += "&nbsp;<input type='button' class='linebut' name='down' value='Down' onclick='defCompDown("+ref+")' />";
		  htm += "&nbsp;<input type='button' class='linebut' name='edi' value='Edit' onclick='showEditDefComp("+ref+")' /></td>";
		  htm += "</tr>\r\n";
	  }
	  htm += "</table>\r\n";    
	  if (assoc.length==0) htm = "<h3>No entries</h3>";
	  htm += "<br /><input type='button' name='showAddComp' id='showAddComp' value='Add Component' onclick='showEditDefComp();' />\r\n";
	  document.getElementById("def_price_struct").innerHTML = htm;  	   
      document.getElementById("prod_type_selector").style.display="block";  
	  var tab = document.getElementById("def_price_struct").childNodes[0];
	  makeReorderable(tab, "defstrucmove");	  
      aj_flush("structhandler");    	
      if (missing) alert("Some component options are missing price data. See 'Price Defaults'");
}

// shows add/edit default component dialogue
function showEditDefComp(component, seq, default_value, req){
  document.getElementById('showAddComp').disabled = true;
  var head = (default_value)? "Edit Component Entry" : "Add Component";
  document.getElementById("struct_header").innerHTML = head;
  document.getElementById("def_comp_selec").innerHTML = "<p><em>Loading...</em></p>";  
  document.defEditingSeq = seq;
  if (default_value) document.sel_default = default_value;
  else if (document.sel_default) delete document.sel_default;
  if (req===undefined && document.sel_req) delete document.sel_req;
  else document.sel_req = req;    
  if (component) document.sel_component = component;
  else {
    if (document.sel_component) delete document.sel_component;
  }	
  var prodtype = document.product_type;
  var cmd="UpdateDefComponent";
  if (!component){
    // add component
	cmd="AddDefComponent";
  }
  document.getElementById("def_def_selec").style.visibility = "hidden";  
  document.getElementById("def_command").value=cmd;
  document.getElementById("edit_def_struct").style.display="block";
  var eds = document.getElementById("edit_def_struct");
  makeDraggable(eds, 1, 1, eds);
      
  aj_setHandler("structhandler", document.ajurl, true);
  params = new Object();
  if (component){
    params['PC_component'] = component;
    aj_startHandler("structhandler", "ListOptions", "gotDefaultOptions", params);	
  }	
  else {
    aj_startHandler("structhandler", "ListComponents", "gotDefaultComponents", params);
  }  
  showWaiting(true);
}

// Response Handler: shows default option selector and fixed box for the specified component
function gotDefaultOptions(notEditing){
  showWaiting(false);
    var res = aj_getResponseLines("structhandler");
    if (res[0]=="ERROR"){
	  alert(aj_getResponse("structhandler"));
	  aj_flush("structhandler");
	  return;
	}
    // insert into def_def_selec
	if (!notEditing){
	  var htm="<label for='editDefComp'>Component:</label>&nbsp;<select id='editDefComp' name='editDefComp' disabled='true'>"+
	          "<option value='"+document.sel_component+"' selected='selected'>"+document.sel_component+"</option></select>\r\n";
      document.getElementById("def_comp_selec").innerHTML = htm;
	}  
	if (document.sel_req === undefined) document.sel_req = 1;	
	var chkd = (document.sel_req==1)? " checked='checked'" : "";
	htm = "<label for='defreqcb'>fixed</label>&nbsp;";
	htm += "<input type='checkbox' name='defreqcb' id='defreqcb' "+chkd+" /><br /><br />\r\n";
	var assoc = responseToAssoc(res);
	htm+="<label for='editDefOp'>Option:</label>&nbsp;<select id='editDefOp' name='editDefOp'>\r\n";
	var seld = (document.sel_default)? document.sel_default : "-%-%"; 
	for (var i=0; i<assoc.length; i++){
	    var sel = (seld==assoc[i]['option'])? " selected" : "";
	    htm += "<option value='"+assoc[i]['option']+"'"+sel+">"+assoc[i]['option']+"</option>\r\n";
	}			
	htm += "</select>\r\n";
	document.getElementById("def_def_selec").innerHTML = htm;
	document.getElementById("def_def_selec").style.visibility = "visible";	
	document.getElementById('subdefok').disabled = false;
    document.getElementById("edit_def_struct").style.display="block";    	
    var eds = document.getElementById("edit_def_struct");
    makeDraggable(eds, 1, 1, eds);	
	aj_flush("structhandler");
}

// Response Handler: shows component selector for the product type
function gotDefaultComponents(){
  showWaiting(false);
    var res = aj_getResponseLines("structhandler");
    if (res[0]=="ERROR"){
	  alert(aj_getResponse("structhandler"));
	  aj_flush("structhandler");
	  return;
	}
    // insert into def_comp_selec
	var assoc = responseToAssoc(res);
	var htm = "<label for='editDefComp'>Component:</label>&nbsp;"+
	          "<select id='editDefComp' name='editDefComp' onchange='showDefOptions();' >\r\n";
	for (var i=0; i<assoc.length; i++) htm += "<option value='"+assoc[i]['code']+"'>"+assoc[i]['code']+"</option>\r\n";
	htm += "</select>\r\n";
    document.getElementById("def_comp_selec").innerHTML = htm;
	document.getElementById('subdefok').disabled = true;
	aj_flush("structhandler");	   
	// select default/init
	if (assoc.length>0){
	  document.getElementById("editDefComp").selectedIndex=0;
	  showDefOptions();  
    }	  	 
}

// show options for component
function showDefOptions(){
    var component = document.getElementById("editDefComp").value;
	document.sel_component = component;
    aj_setHandler("structhandler", document.ajurl, true);
    params = new Object();
    params['PC_component'] = component;
    aj_startHandler("structhandler", "ListOptions", "gotDefaultCompOptions", params);		
    showWaiting(true);	
}

// Response Handler: show options (without resetting component selector)
function gotDefaultCompOptions(){
    showWaiting(false);
    gotDefaultOptions(true);
}

// close add/edit default structure component dialogue
function closeDefEditor(){
  document.getElementById("edit_def_struct").style.display="none";
  document.getElementById("showAddComp").disabled=false;
}

// submit add/edit default structure component dialogue
function submitDefEditor(){
    aj_setHandler("structhandler", document.ajurl, true);
    params = new Object();
    params['PC_product_type'] = document.getElementById("prod_type_sel").value;	
    params['PC_component'] = document.getElementById("editDefComp").value;
    params['PC_fixed'] = ((document.getElementById("defreqcb").checked)? 1 : 0);
    params['PC_default_value'] = document.getElementById("editDefOp").value;
    params['PC_order_seq'] = document.defEditingSeq;
	var cmd = document.getElementById("def_command").value;
    aj_startHandler("structhandler", cmd, "defStructUpdated", params);
    showWaiting(true);	
}

// Response Handler: handle structure updated
function defStructUpdated(){
    showWaiting(false);
    var res = aj_getResponseLines("structhandler");
	if (res[0]=="ERROR"){
	  alert(aj_getResponse("structhandler"));
      aj_flush("structhandler");
	  return;
	}
    aj_flush("structhandler");
	selectProdType();
}

// request delete of specified component entry
function deleteDefComp(component, seq, value, req){
    if (!confirm("Delete entry for '"+component+"'?")) return;
    aj_setHandler("structhandler", document.ajurl, true);
    params = new Object();
    params['PC_product_type'] = document.getElementById("prod_type_sel").value;	
    params['PC_component'] = component;
    params['PC_order_seq'] = seq;
    aj_startHandler("structhandler", "deleteDefComp", "defCompUpdated", params);
    showWaiting(true);	
}

// Response Handler: default components updated
function defCompUpdated(){
    showWaiting(false);
    var res = aj_getResponseLines("structhandler");
	if (res[0]=="ERROR"){
	  alert(aj_getResponse("structhandler"));
      aj_flush("structhandler");
	  return;
	}
    aj_flush("structhandler");
	selectProdType();   
}

// handle 'up' request
function defCompUp(component, seq, value, req){
    defCompSwap(component, seq, "defCompUp");
}

// handle 'down' request
function defCompDown(component, seq, value, req){
    defCompSwap(component, seq, "defCompDown");
}

// handle component swap request
function defCompSwap(component, seq, cmd){
    aj_setHandler("structhandler", document.ajurl, true);
    params = new Object();
    params['PC_product_type'] = document.getElementById("prod_type_sel").value;	
    params['PC_component'] = component;
    params['PC_order_seq'] = seq;
    aj_startHandler("structhandler", cmd, "defCompUpdated", params);
    showWaiting(true);	
}


/* **************************************** Product Price Structure Functions ********************************* */

// get products
function listProducts(){
  aj_setHandler("prodstructhandler", document.ajurl, true);
  params = new Object();
  aj_startHandler("prodstructhandler", "ListProducts", "gotProducts", params);
  showWaiting(true);  
}

// Response Handler: show products selector
function gotProducts(){
  showWaiting(false);
  document.allProdCodes = new Array();
  var res = aj_getResponseLines("prodstructhandler");
  if (res[0]=="OK"){
	var htm="<label for='prod_code_sel'>Products</label>&nbsp;";
	htm += "<select id='prod_code_sel' name='prod_code_sel' onchange='selectProdCode()'>";
	for (var i=1; i<res.length; i++){
	  if (res[i]=="") continue;
	  htm += "<option id='"+res[i]+"' value='"+res[i]+"'>"+res[i]+"</option>\r\n";
	  document.allProdCodes[document.allProdCodes.length] = res[i];
	}
	htm += "</select>\r\n";    
	document.getElementById("prod_code_selector").innerHTML = htm;
  }	
  else alert(aj_getResponse("prodstructhandler"));
  aj_flush("prodstructhandler");    
}

// handle product code selection event
function selectProdCode(){
	document.getElementById("prod_code_selector").style.display="none";
	document.getElementById("prod_price_struct").innerHTML = "<h2>Updating...</h2>";
    var val = document.getElementById('prod_code_sel').value;	
	document.product_code = val;
    aj_setHandler("structhandler", document.ajurl, true);
    params = new Object();
	params['PC_default']=0;
	params['PC_code']=val;	
    aj_startHandler("structhandler", "ListPriceStruct", "gotProdStruct", params);	
    showWaiting(true);		
}

// Response Handler: update default price structure
function gotProdStruct(){
    showWaiting(false);
    document.getElementById("edit_prod_struct").style.display="none";
    var res = aj_getResponseLines("structhandler");
    if (res[0]=="ERROR"){
	  alert(aj_getResponse("structhandler"));
	  aj_flush("structhandler");
	  return;
	}
    var assoc = responseToAssoc(res);
	var htm="<table class='PC_listing'><tr><th>component</th><th>title</th><th class='ac'>fixed</th><th>option</th><th>priced</th><th>&nbsp;</th></tr>\r\n";
    var missing = false;
	for (var i=0; i<assoc.length; i++){
          var tdc = (assoc[i]['priced']==0)? " class='PC_alert'" : "";
		  if (tdc != "") missing=true;
	      htm += "<tr>";
		  var hip = "<input type='hidden' id='seq_"+i+"' value='"+assoc[i]['order_seq']+"' />";		  		  
		  htm += "<td"+tdc+">"+hip+assoc[i]['order_seq']+". "+assoc[i]['component']+"</td>";
		  htm += "<td"+tdc+">"+assoc[i]['name']+"</td>";		  		  		  
		  htm += "<td"+tdc+">"+((assoc[i]['fixed']==1)? "Y" : "-")+"</td>";		  
		  htm += "<td"+tdc+">"+assoc[i]['value']+"</td>";		  		  
		  htm += "<td"+tdc+">"+((assoc[i]['priced']==1)? "Y" : "-")+"</td>";		  		  
		  var ref = '"'+assoc[i]['component']+'", '+assoc[i]['order_seq']+', "'+assoc[i]['value']+'", '+assoc[i]['fixed'];
		  htm += "<td><input type='button' class='linebut' name='delete' value='Delete' onclick='deleteProdComp("+ref+")' />";
		  htm += "&nbsp;<input type='button' class='linebut' name='edi' value='Edit' onclick='showEditProdComp("+ref+")' /></td>";
		  htm += "</tr>\r\n";
    }
	htm += "</table>\r\n";    
	if (assoc.length==0) htm = "<h3>No entries</h3>";
	htm += "<div id='zvatr' style='display: none;'></div>";
	htm += "<br /><input type='button' name='showAddProdComp' id='showAddProdComp' value='Add Component' onclick='showEditProdComp();' />\r\n";
	htm += "&nbsp;&nbsp;<a href='javascript:showTable();'>show table</a>\r\n";
    if (assoc.length==0){  // enable duplication only if no structure found
	  htm += "&nbsp;&nbsp;<input type='button' name='setDefButton' id='setDefButton' value='Default Settings' onclick='setProductToDefault();' />\r\n";
      htm += "&nbsp;&nbsp;Duplicate from:&nbsp;<select name='dup_code' id='dup_code' onchange='duplicateStructure();'>";
	  for (var i=0; i<document.allProdCodes.length; i++){
	    var dcode = document.allProdCodes[i];
	    if (dcode!=document.product_code) htm+="<option value='"+dcode+"'>"+dcode+"</option>\r\n";
	  }
	  htm += "</select>\r\n";
	}
	else {
	  if (document.product_code=="_quote_") 
	    htm += "&nbsp;|&nbsp;<a href='javascript:clearQuote();'>clear quote</a>"+
		       "&nbsp;|&nbsp;<a href='javascript:saveQuote();'>save quote</a>"+
			   "&nbsp;|&nbsp;<a href='javascript:loadQuote();'>load quote</a>";
	}  
	document.getElementById("prod_price_struct").innerHTML = htm;    
	var tab = document.getElementById("prod_price_struct").childNodes[0];
	makeReorderable(tab, "prodstrucmove");
    document.getElementById("prod_code_selector").style.display="block";  
    document.strucrow = new Array(assoc.length);
    for (var i=0; i<assoc.length; i++){
	      var hid = document.getElementById('seq_'+i);
		  while (hid.nodeName!="TR") hid = hid.parentNode;
		  document.strucrow[i] = hid;
	}

    aj_flush("structhandler");    
  if (missing) alert("Some component options are missing price data. See 'Product Pricing'");
  getVatZeroRated(document.product_code);  // get VAT zero rating
  var prodcode = document.product_code;
  PC_getStartTable(prodcode, false);  
}

// duplicates price structure from another product
function duplicateStructure(){
  var dupcode = document.getElementById('dup_code').value;
  var prodcode = document.product_code;
  if (!confirm("Really copy price structure from product '"+dupcode+"' to product '"+prodcode+"'?")) return;
  // do the call
  var params = new Object();
  params['PC_prod_code'] = prodcode;
  params['PC_dup_code'] = dupcode;
  aj_setHandler("structhandler", document.ajurl, true);
  aj_startHandler("structhandler", "DuplicatePriceStructure", "prodStructUpdated", params);
  showWaiting(true);
}

// get vat zero rated info
function getVatZeroRated(){
    if (!document.product_code) return;
    aj_setHandler("vathandler", document.ajurl, true);
    params = new Object();
	params['PC_prod_code']=document.product_code;
    aj_startHandler("vathandler", "GetVatZeroRated", "gotVatZeroRated", params);	
    showWaiting(true);  
}

// sets the VAT zero rated info from checkbox
function setVatZeroRated(){
	var cb = document.getElementById("vat_zero_rated");
	if (!cb) return;
	var is_zero_rated = (cb.checked)? 1 : 0;
	var zvr = document.getElementById("zvatr");
	while (zvr.firstChild) zvr.removeChild(zvr.firstChild);
	var sp = document.createElement("span");
	sp.className="pricemodupdating";	
	sp.appendChild(document.createTextNode("VAT info updating..."));
	zvr.appendChild(sp);
    aj_setHandler("vathandler", document.ajurl, true);
    params = new Object();
	params['PC_prod_code']=document.product_code;
	params['PC_is_zero_rated']=is_zero_rated;
    aj_startHandler("vathandler", "SetVatZeroRated", "gotVatZeroRated", params);	
    showWaiting(true);  
}

// show the vat zero rated info
function gotVatZeroRated(){
    showWaiting(false); 
    var res = aj_getResponseLines("vathandler");
    if (res[0]=="ERROR"){
	  alert(aj_getResponse("vathandler"));
	  aj_flush("vathandler");
	  return;
	}
	rate = res[1];
    var zvatr = document.getElementById("zvatr");
	if (zvatr){
	  while (zvatr.firstChild) zvatr.removeChild(zvatr.firstChild);
	  var cb = document.createElement("input");
	  cb.type = "checkbox";
	  cb.value = 1;
	  cb.checked = (rate=="1");
	  cb.id = "vat_zero_rated";
	  zvatr.appendChild(document.createTextNode("VAT zero rated: "));
	  zvatr.appendChild(cb);
	  zvatr.style.display="block";
	  cb.onclick = function(){ setVatZeroRated(); };	  
	}
}

function setProductToDefault(){
  if (!confirm("Really set to product type defaults?")) return;
  var params = new Object();
  params['PC_prod_code'] = document.getElementById("prod_code_sel").value;  
  aj_setHandler("structhandler", document.ajurl, true);
  aj_startHandler("structhandler", "setProdToDefault", "prodStructUpdated", params);	  
  showWaiting(true);  
}

// shows add/edit default component dialogue
function showEditProdComp(component, seq, value, req){
  document.getElementById('showAddProdComp').disabled = true;
  if (sdb = document.getElementById('setDefButton')) sdb.disabled = true;  
  var head = (value)? "Edit Component Entry" : "Add Component";
  document.getElementById("prod_struct_header").innerHTML = head;
  document.getElementById("prod_comp_selec").innerHTML = "<p><em>Loading...</em></p>";  
  document.prodEditingSeq = seq;  
  if (value) document.sel_value = value;
  else if (document.sel_value) delete document.sel_value;
  if (req===undefined && document.prodReg) delete document.prodReq;
  else document.prodReq = req;    
  if (component) document.sel_prodcomponent = component;
  else {
    if (document.sel_prodcomponent) delete document.sel_prodcomponent;
  }	
  var prodcode = document.product_code;
  var cmd="UpdateProdComponent";
  if (!component){
    // add component
	cmd="AddProdComponent";
  }
  document.getElementById("prod_def_selec").style.visibility = "hidden";  
  document.getElementById("prod_command").value=cmd;
  document.getElementById("edit_prod_struct").style.display="block";
      
  aj_setHandler("structhandler", document.ajurl, true);
  params = new Object();
  if (component){
    params['PC_component'] = component;
    aj_startHandler("structhandler", "ListOptions", "gotProdOptions", params);	
  }	
  else {
    aj_startHandler("structhandler", "ListComponents", "gotProdComponents", params);
  }  
  showWaiting(true);
}

// close add/edit default structure component dialogue
function closeProdEditor(){
  document.getElementById("edit_prod_struct").style.display="none";
  document.getElementById("showAddProdComp").disabled=false;  
  document.getElementById("setDefButton").disabled=false;    
}

// Response Handler: shows product option selector and fixed box for the specified component
function gotProdOptions(notEditing){
    showWaiting(false);
    var res = aj_getResponseLines("structhandler");
    if (res[0]=="ERROR"){
	  alert(aj_getResponse("structhandler"));
	  aj_flush("structhandler");
	  return;
	}
    // insert into prod_def_selec
	if (!notEditing){
	  var htm="<label for='editProdComp'>Component:</label>&nbsp;<select id='editProdComp' name='editProdComp' disabled='true'>"+
	          "<option value='"+document.sel_prodcomponent+"' selected='selected'>"+document.sel_prodcomponent+"</option></select>\r\n";
      document.getElementById("prod_comp_selec").innerHTML = htm;
	}  
	if (document.prodReq === undefined) document.prodReq = 1;	
	var chkd = (document.prodReq==1)? " checked='checked'" : "";
	htm = "<label for='prodreqcb'>fixed</label>&nbsp;";
	htm += "<input type='checkbox' name='prodreqcb' id='prodreqcb' "+chkd+" /><br /><br />\r\n";
	var assoc = responseToAssoc(res);
	htm+="<label for='editProdOp'>Option:</label>&nbsp;<select id='editProdOp' name='editProdOp'>\r\n";
	var selv = (document.sel_value)? document.sel_value : "-%-%"; 
	for (var i=0; i<assoc.length; i++){
	    var sel = (selv==assoc[i]['option'])? " selected" : "";
	    htm += "<option value='"+assoc[i]['option']+"'"+sel+">"+assoc[i]['option']+"</option>\r\n";
	}			
	htm += "</select>\r\n";
	document.getElementById("prod_def_selec").innerHTML = htm;
	document.getElementById("prod_def_selec").style.visibility = "visible";	
	document.getElementById('subprodok').disabled = false;
    document.getElementById("edit_prod_struct").style.display="block";    	
	aj_flush("structhandler");
}

// Response Handler: shows component selector for the product type
function gotProdComponents(){
    showWaiting(false);
    var res = aj_getResponseLines("structhandler");
    if (res[0]=="ERROR"){
	  alert(aj_getResponse("structhandler"));
	  aj_flush("structhandler");
	  return;
	}
    // insert into prod_comp_selec
	var assoc = responseToAssoc(res);
	var htm = "<label for='editProdComp'>Component:</label>&nbsp;"+
	          "<select id='editProdComp' name='editProdComp' onchange='showProdOptions();' >\r\n";
	for (var i=0; i<assoc.length; i++) htm += "<option value='"+assoc[i]['code']+"'>"+assoc[i]['code']+"</option>\r\n";
	htm += "</select>\r\n";
    document.getElementById("prod_comp_selec").innerHTML = htm;
	document.getElementById('subprodok').disabled = true;
	aj_flush("structhandler");	 
	// select default/init
	if (assoc.length>0){
	  document.getElementById("editProdComp").selectedIndex=0;
	  showProdOptions();  
    }	  
}

// Response Handler: handle structure updated
function prodStructUpdated(){
    showWaiting(false);
    var res = aj_getResponseLines("structhandler");
	if (res[0]=="ERROR"){
	  alert(aj_getResponse("structhandler"));
      aj_flush("structhandler");
	  return;
	}
    aj_flush("structhandler");
	selectProdCode();
}

// request delete of specified component entry
function deleteProdComp(component, seq, value, req){
    if (!confirm("Delete entry for '"+component+"'?")) return;
    aj_setHandler("structhandler", document.ajurl, true);
    params = new Object();
    params['PC_prod_code'] = document.getElementById("prod_code_sel").value;	
    params['PC_component'] = component;
    params['PC_order_seq'] = seq;
    aj_startHandler("structhandler", "deleteProdComp", "prodCompUpdated", params);
    showWaiting(true);	
}

// Response Handler: default components updated
function prodCompUpdated(){
    showWaiting(false);
    var res = aj_getResponseLines("structhandler");
	if (res[0]=="ERROR"){
	  alert(aj_getResponse("structhandler"));
      aj_flush("structhandler");
	  return;
	}
    aj_flush("structhandler");
	selectProdCode();   
}

// handle 'up' request
function prodCompUp(component, seq, value, req){
    prodCompSwap(component, seq, "prodCompUp");
}

// handle 'down' request
function prodCompDown(component, seq, value, req){
    prodCompSwap(component, seq, "prodCompDown");
}

// handle component swap request
function prodCompSwap(component, seq, cmd){
    aj_setHandler("structhandler", document.ajurl, true);
    params = new Object();
    params['PC_prod_code'] = document.getElementById("prod_code_sel").value;	
    params['PC_component'] = component;
    params['PC_order_seq'] = seq;
    aj_startHandler("structhandler", cmd, "prodCompUpdated", params);
    showWaiting(true);
}

// show options for component
function showProdOptions(){
    var component = document.getElementById("editProdComp").value;	
	document.sel_prodcomponent = component;
    aj_setHandler("structhandler", document.ajurl, true);
    params = new Object();
    params['PC_component'] = component;
    aj_startHandler("structhandler", "ListOptions", "gotProdCompOptions", params);
    showWaiting(true);	
}

// Response Handler: show options (without resetting component selector)
function gotProdCompOptions(){
    showWaiting(false);
    gotProdOptions(true);
}

// submit add/edit default structure component dialogue
function submitProdEditor(){
    aj_setHandler("structhandler", document.ajurl, true);
    params = new Object();
    params['PC_prod_code'] = document.getElementById("prod_code_sel").value;	
    params['PC_component'] = document.getElementById("editProdComp").value;
    params['PC_fixed'] = ((document.getElementById("prodreqcb").checked)? 1 : 0);
    params['PC_value'] = document.getElementById("editProdOp").value;
    params['PC_order_seq'] = document.prodEditingSeq;	
	var cmd = document.getElementById("prod_command").value;
    aj_startHandler("structhandler", cmd, "prodStructUpdated", params);
    showWaiting(true);	
}


/* ************************************ Price Table Functions ************************************** */

// show/hide prices in price table
function showHidePrices(show){
  var vis = (show==1)? "visible" : "hidden";
  var table = document.getElementById("ep_table");
  var cells = table.getElementsByTagName("TD");
  for (var i=0; i<cells.length; i++){
    var cell = cells[i];
	cell.style.visibility = vis;
  }
}

// print blank pricetable
function printBlanks(){
  showHidePrices(0);
  window.print();
  setTimeout("showHidePrices(1);", 2000);
}

// update component selector
function updatePriceComponents(){
  var etsel = document.getElementById("etable_select");
  if (etsel == null) return;
  aj_setHandler("ptablehandler", document.ajurl, true);  
  document.getElementById("etable_select").innerHTML="<h3>Updating...</h3>";
  var params = new Object();
  aj_startHandler("ptablehandler", "ListComponents", "gotPriceComponents", params);      
  showWaiting(true);  
}

// Response Handler: display components list
function gotPriceComponents(){
  showWaiting(false);
  var lines=aj_getResponseLines("ptablehandler");
  if (lines[0]=="Error"){
    alert(aj_getResponse("ptablehandler"));
	return;
  }
  var rehtm = "<div id='etable_compselect'>\r\n</div>\r\n<div id='etable_opselect'>\r\n</div>"+
              "<div id='etable_versions'></div>\r\n";
  document.getElementById("etable_select").innerHTML = rehtm;
  var list = responseToAssoc(lines);  
  var htm = "<label for='pricecompsel'>Component</label>&nbsp;"+
            "<select name='pricecompsel' id='pricecompsel' onchange='updatePriceCompOptions();'>\r\n";
  for (var i in list){
      htm += "<option value='"+list[i]['code']+"'>"+list[i]['code']+" ("+list[i]['name']+")</option>\r\n";
  }
  htm += "</select>\r\n";
  document.getElementById("etable_compselect").innerHTML = htm;
}

// updates price component options list
function updatePriceCompOptions(){
  var selcomp = document.getElementById("pricecompsel").value;
  aj_setHandler("ptablehandler", document.ajurl, true);
  document.getElementById("etable_opselect").innerHTML="<h3>Updating...</h3>";
  var params = new Object();
  params['PC_component']=selcomp;
  aj_startHandler("ptablehandler", "ListOptions", "gotOpList", params);    
  showWaiting(true);  
}

// show/hide increase all
function showInc(sh, suff){
  var ddis = (sh>0)? "block" : "none";
  var sdis = (sh>0)? "none" : "block";
  var d = document.getElementById("inc"+suff+"div");
  var s = document.getElementById("inc"+suff+"divshow");
  if (d) d.style.display = ddis;
  if (s) s.style.display = sdis;
}

function increase(toall){
  var isall = (toall==1);
  var compsel = document.getElementById("pricecompsel").value;
  var opsel = document.getElementById("priceopsel").value;  
  // get percentage
  var percid = (isall)? 'priceincreaseall' : 'priceincrease';
  var perc = document.getElementById(percid).value;
  // validate perc;
  if (isNaN(perc)){
    alert("invalid number entry");
	return;
  }
  if ((perc > 100) || (perc < -100)) {
    alert("invalid percentage");
	return;
  }
  if (perc==0){
    alert("0% - no change!");
	return;
  }
  var items = compsel + ((isall)? ": All options" : (": "+opsel));
  if (!confirm("Increase "+items+" by "+perc+"%?")) return;
  
  // ajax call
  aj_setHandler("ptablehandler", document.ajurl, true);
  document.getElementById("etable_versions").innerHTML="<h3>Updating...</h3>";
  var params = new Object();
  params['PC_component']=compsel;
  if (!isall) params['PC_option']=opsel;
  params['PC_percentage']=perc;
  aj_startHandler("ptablehandler", "increase", "increaseDone", params);    
  showWaiting(true);    
}

function increaseDone(){
alert(aj_getResponse("ptablehandler"));
  var lines=aj_getResponseLines("ptablehandler");
  if (lines[0]=="Error"){
    alert(aj_getResponse("ptablehandler"));
	return;
  }
  updateVersionList();
}

// response handler: got options list
function gotOpList(){
  showWaiting(false);
  var lines=aj_getResponseLines("ptablehandler");
  if (lines[0]=="Error"){
    alert(aj_getResponse("ptablehandler"));
	return;
  }
  var list = responseToAssoc(lines);
  // percentage price increase
  var selcomp = document.getElementById("pricecompsel").value;  
  var htm = "<p id='incalldivshow'><a href='javascript:showInc(1, \"all\");'>Increase</a></p>";
  htm += "<div id='incalldiv' style='display: none;'><p><strong>"+selcomp+": All options</strong></p>"+
         "<input type='text' name='priceincreaseall' id='priceincreaseall' style='width: 30px;' />"+
         "&nbsp;% increase &nbsp;"+
         "<input type='button' name='incr' value='Apply' onclick='increase(1);' />"+
		 "<br /><a href='javascript:showInc(0, \"all\");'>close</a></div>\r\n";
  htm += "<label for='priceopsel'>Option</label>&nbsp;"+
         "<select name='priceopsel' id='priceopsel' onchange='updateVersionList();'>\r\n";
  for (var i in list){
      htm += "<option value='"+list[i]['option']+"'>"+list[i]['option']+"</option>\r\n";
  }
  htm += "</select>\r\n";
  document.getElementById("etable_opselect").innerHTML = htm;
  if (list.length==0) document.getElementById("etable_versions").innerHTML = "";    
  else {
    document.getElementById("priceopsel").selectedIndex = 0;
	updateVersionList();
  }
}

function updateVersionList(){
  var selcomp = document.getElementById("pricecompsel").value;
  var selop = document.getElementById("priceopsel").value;  
  aj_setHandler("ptablehandler", document.ajurl, true);
  document.getElementById("etable_versions").innerHTML="<h3>Updating...</h3>";
  var params = new Object();
  params['PC_component']=selcomp;
  params['PC_option']=selop;  
  aj_startHandler("ptablehandler", "listPriceVersions", "gotVersionsList", params);      
  showWaiting(true);  
}

// response handler: show versions list
function gotVersionsList(){  
  showWaiting(false);
  var lines=aj_getResponseLines("ptablehandler");
  if (lines[0]=="Error"){
    alert(aj_getResponse("ptablehandler"));
	return;
  }
  var list = responseToAssoc(lines);
  if (list.length==0){
      editVersion(0);
  }

  // insert the percentage thingy here
  var htm = "<p id='incdivshow'><a href='javascript:showInc(1, \"\");'>Increase</a></p>";
  var selcomp = document.getElementById("pricecompsel").value;
  var selop = document.getElementById("priceopsel").value;
  htm += "<div id='incdiv' style='display: none;'><p><strong>"+selcomp+": "+selop+"</strong></p>\r\n"+
         "<input type='text' name='priceincrease' id='priceincrease' style='width: 30px;' />"+
         "&nbsp;% increase &nbsp;"+
         "<input type='button' name='incr' value='Apply' onclick='increase(0);' />"+
		 "<br /><a href='javascript:showInc(0, \"\");'>close</a></div>\r\n";
  
  htm += "<table class='PC_listing'><tr><th>Version</th><th>Modified</th><th colspan='2'></tr>\r\n";
  document.PC_published = false;  
  for (var i in list){
    var sel = (list[i]['published']==1)? " class='sel'" : "";
    htm += "<tr"+sel+"><td>"+list[i]['version']+"</td><td>"+list[i]['last_mod']+"</td><td>";
	htm += "<input type='button' onclick='editVersion("+list[i]['version']+");' name='edv' value='edit' />\r\n";
	if (list[i]['published']==0)
	  htm += "<input type='button' onclick='publishVersion("+list[i]['version']+");' name='pub' value='publish' />\r\n";
    else {	  
	  htm += "<input type='button' onclick='publishVersion("+list[i]['version']+", true);' name='withd' value='withdraw' />\r\n";
	  document.PC_published = list[i]['version'];
	}  
	htm += "</td></tr>\r\n";
  }
  htm += "</table>";
  document.getElementById("etable_versions").innerHTML = htm;
  document.getElementById("etable_select").style.display="block";  
  document.getElementById("etable_div").style.display="none";  
}

// edit the specified version
function editVersion(version){
    var selcomp = document.getElementById("pricecompsel").value;
    var selop = document.getElementById("priceopsel").value;        
    getPricesFor(selcomp, selop, version);
}

// publishes the specified version
function publishVersion(version, unpublish){
    var selcomp = document.getElementById("pricecompsel").value;
    var selop = document.getElementById("priceopsel").value;        
    aj_setHandler("ptablehandler", document.ajurl, true);
    document.getElementById("etable_versions").innerHTML="<h3>Updating...</h3>";
    var params = new Object();
    params['PC_component']=selcomp;
    params['PC_option']=selop;  
    params['PC_version']=version;
	params['PC_publish']=(unpublish)? 0: 1;
    aj_startHandler("ptablehandler", "publishVersion", "publishedVersion", params);      
    showWaiting(true);	
}

// response handler: update versions list
function publishedVersion(){
  showWaiting(false);
  var lines=aj_getResponseLines("ptablehandler");
  if (lines[0]=="Error"){
    alert(aj_getResponse("ptablehandler"));
	return;
  }
  alert(aj_getResponse("ptablehandler"));

	updateVersionList();
}

function getPricesFor(component, option, version){
//    document.pricesCallback = callback;
	document.pricesFor = component;
	document.pricesForOption = option;
	document.pricesForVersion = version;		
	aj_setHandler("ptablehandler", document.ajurl, true);
	params = new Object();
	params['PC_component']=component;
	params['PC_option']=option;
	params['PC_version']=version;	
	aj_startHandler("ptablehandler", "getPrices", "gotPrices", params);
    showWaiting(true);	
}

// response handler: got prices
function gotPrices(){
    showWaiting(false);
    var res = aj_getResponseLines("ptablehandler");
	if (res[0]=="ERROR"){
	  alert(aj_getResponse("ptablehandler"));
	  aj_flush("ptablehandler");
	  document.pricesFor = false;	  
	  return;
	}
	var assoc = responseToAssoc(res);
	showPriceEditor(responseToAssoc(res));
	aj_flush("ptablehandler");	
}

// shows price editor for the associative array price data
function showPriceEditor(priceAssoc){
    document.pricetree = priceAssocToTree(priceAssoc);
	updatePriceTable();
}

// updates display of editable price table
function updatePriceTable(){
    // clear all selections
    document.selectedOp1 = false;
	document.selectedOp2 = false;
	document.selectedQty = false;		
	// build table
	var tree = document.pricetree;
    var ops1 = new Array();
	var kops = new Array();
	var ops2 = new Array();
	// get option 1 list
	for (var i in tree) {
	    if (!kops[i]){
		    kops[i]=1;  
			ops1[ops1.length] = i;
		}	
    }	
	ops1.sort(); // sort alphanum
	document.ops1 = ops1;
		
	// get option 2 list	
	for (var j in document.alloption2s){
				ops2[ops2.length] = j;
    }
	ops2.sort();  // sort alphanum
	document.ops2 = ops2;
	var nops1 = ops1.length;
	var nops2 = ops2.length;
	// build head
	var head = "<table id='ep_table'>\r\n<tr>\r\n";
	var colspan = (nops2==0)? 1 : nops2;
	var compsel = document.getElementById("pricecompsel").value;
	head += "<th>"+document.componentOpNames[compsel][0]+"</th>\r\n";
	for (var i in ops1) head += "<th rowspan='1' colspan='"+colspan+"' onclick='selectOp1(\""+ops1[i]+"\")' >"+
	                            ops1[i]+"</th>\r\n";  // options1
	// add option button
	head += "<th class='button'><input type='button' name='newop1but' id='newop1but' value='New Option' onclick='showNewOp1();' />"+
	        "<input type='text' value='' name='newop1' id='newop1' style='display: none;' onblur='blurNewOp1();' /></th>\r\n</tr>\r\n<tr>\r\n";
    // options2
	head += "<th>"+document.componentOpNames[compsel][1]+"</th>\r\n";	
	for (var i in ops1){
	  if (nops2==0) head+="<th>-</th>\r\n";  // no option2 - show blank
	  for (var j in ops2){
	    head+="<th onclick='selectOp2(\""+ops2[j]+"\");'>"+ops2[j]+"</th>\r\n";
	  }
	}
	// add option 2 button
	var newop2="<th class='button'><input type='button' name='newop2but' id='newop2but' value='New Option' onclick='showNewOp2();' />"+
	        "<input type='text' value='' name='newop2' id='newop2' style='display: none;' onblur='blurNewOp2();' /></th>\r\n";
	head += newop2+"</tr>\r\n";
	
	// body - qty & blank
	// extract qtys
	var ptbody = "";
	var qtys = new Array();
	for (var i in tree){
	  for (var j in tree[i]){
	    for (var q in tree[i][j]){
		  if (!qtys[q]) qtys[q]=1;
		}
	  }
	}
	var qtykeys = new Array();
	for (var i in qtys) qtykeys[qtykeys.length]=i;
	qtykeys.sort(function(a, b){return a-b; });
	document.qtykeys = qtykeys;
	// show rows
	var r=0;
	for (qi in qtykeys){
	  var q = qtykeys[qi];
	  ptbody += "<tr><th onclick='selectQty(\""+q+"\");'>"+q+"</th>";
	  var c=0;
	  for (var i in ops1){
	    for (var j in ops2){
		  ptbody += "<td>\r\n";
		  if (typeof tree[ops1[i]][ops2[j]][q] != "number") tree[ops1[i]][ops2[j]][q]="-";
		  var id = "eps_"+r+"_"+c;
		  ptbody += "<input class='pricecell' id='"+id+"' value='"+tree[ops1[i]][ops2[j]][q]+"' onchange='validateEntry(\""+id+"\");' />";
		  ptbody += "</td>\r\n";
		  c++;
	    }
	  }
	  if (qi==0) ptbody += "<th rowspan='"+qtykeys.length+"'>&nbsp;</th>\r\n";
	  ptbody += "</tr>\r\n";
	  r++;
	}
	// add qty button
	var span = nops1*colspan;
	var footspace = (span>0)? "<th colspan='"+span+"'>&nbsp;</th>" : "";
	ptbody += "<tr>\r\n<th class='button'><input type='button' name='newqtybutton' id='newqtybutton' value='New Qty' onclick='newqty();' />"+
	          "<input type='text' class='pricecell' value='' name='newqty' id='newqty' style='display: none;' onblur='blurNewQty();' />"+
	          "</th>\r\n"+footspace+"<th class='button'>"+
              "<input type='button' name='delsel' value='delete' onclick='deleteSelection();' /></th>\r\n</tr>\r\n";
    buts = "<p>";
    buts += "<input type='button' name='copysel' value='copy table' onclick='copyTable();' />";	
    buts += "<input type='button' name='pastesel' value='paste table' onclick='pasteTable();' />";
	buts += "</p>\r\n";
	buts += "<p><input type='button' name='psave' value='save' onclick='savePrices();' />\r\n";
	buts += "<input type='button' name='psavecopy' value='save copy' onclick='savePrices(true);' />\r\n";	
	buts += "<input type='button' name='pcanc' value='cancel' onclick='cancelPriceEdit();' /></p>\r\n";	
    buts += "<p><a href='javascript:printBlanks();'>Print Blank Table</a></p>\r\n";

	// build from rows
	var vers = document.pricesForVersion;
	document.getElementById("etable_div").innerHTML = "<p><strong>Editing</strong></p><h3 class='noindent'>"+
	                                                  document.pricesFor+" / "+document.pricesForOption+"</h3>"+
													  "<p>version: "+vers+"</p>\r\n"+head+ptbody+"</table>\r\n"+buts;
    document.getElementById("etable_div").style.display="block";
    document.getElementById("etable_select").style.display="none";															  
}

// cancels the price edits
function cancelPriceEdit(){
    if (!confirm("Really cancel? All changes will be lost")) return;
    document.getElementById("etable_div").style.display="none";
    document.getElementById("etable_select").style.display="block";															  	
}



// shows new option1 field
function showNewOp1(){
  document.getElementById("newop1but").style.display="none";
  document.getElementById("newop1").style.display="block";
  document.getElementById("newop1").focus();    
}

// handle change to new option 1 input
function blurNewOp1(){
  var val = document.getElementById('newop1').value;
  var tree = document.pricetree;  
  if (val=="" || tree[val]){
    document.getElementById("newop1but").style.display="block";
    document.getElementById("newop1").style.display="none";  
	return;
  }
  tree[val] = new Array();
  for (var i in document.alloption2s) {
    alert(i);
    tree[val][i]=new Array();
  }	
  updatePriceTable();  
}

// shows new option2 field
function showNewOp2(){
  document.getElementById("newop2but").style.display="none";
  document.getElementById("newop2").style.display="block";
  document.getElementById("newop2").focus();    
}

// handle change to new option 2 input
function blurNewOp2(){
  var val = document.getElementById('newop2').value;
  var tree = document.pricetree; 
  var i=null;
  for (i in tree){} 
    if ((!i) || val=="" || tree[i][val]){
	  alert("ERROR: Could not add new option");
      document.getElementById("newop2but").style.display="block";
      document.getElementById("newop2").style.display="none";  
	  return;
    }
  for (var i in tree){  
    tree[i][val]=new Array();
	if (!document.alloption2s[val]) document.alloption2s[val]=1;
  }	
  updatePriceTable();
}

function newqty(){
  document.getElementById("newqtybutton").style.display="none";
  document.getElementById("newqty").style.display="block";
  document.getElementById("newqty").focus();    
}

function blurNewQty(){
  var val = document.getElementById('newqty').value;  
  if (val=="") {
      updatePriceTable();
	  return;
  }
  var fval = parseInt(val);
  if (!fval || fval==0){
    alert("ERROR: invalid numerical entry");
	return;
  }
  var tree = document.pricetree;
  for (var i in tree){
    for (var j in tree[i]){
	  if (tree[i][j][val]){
	    alert("ERROR: Quantity line exists!");
		return;
	  }
	  else tree[i][j][val]=0;
	}
  }
//  alert(arrProps(tree, ""));  
  updatePriceTable();
}

// validate the entry is a valid numerical value
function validateEntry(id){
  var inp = document.getElementById(id);
  var val = inp.value;
  var fval = val;
  var pass = false;
  if (val=="0") pass=true;
  if (val=="0.0") pass=true;
  if (val=="0.00") pass=true;
  if (!pass){
      fval = parseFloat(val);
      if (!fval){
          alert("ERROR - invalid numerical entry");
	      inp.focus();
	      inp.selectAll();
	      document.ptvalid = false;
          return;
      }
  }	
  document.ptvalid = true;
  inp.value = fval;

  // store value in tree object
  var posit=id.split("_");
  var row=posit[1];
  var col=posit[2];	  	
  var ind2 = col % document.ops2.length;
  var ind1 = (col-ind2)/document.ops2.length;
  var op1 = document.ops1[ind1];
  var op2 = document.ops2[ind2];
  var qty = document.qtykeys[row];
  document.pricetree[op1][op2][qty]=fval;
}

// select the specified option 1 column
function selectOp1(op1, unsel){
	if (op1 == document.selectedOp1) unsel=true;
    if (!unsel && document.selectedOp1) selectOp1(document.selectedOp1, true); // unselect if nec
    if (!unsel && document.selectedOp2) selectOp2(document.selectedOp2, true); // unselect if nec
    if (!unsel && document.selectedQty) selectQty(document.selectedQty, true); // unselect if nec	
    var clas = (unsel)? "" : "ep_colsel";
    var tree = document.pricetree;
	var ind = 0;
    for (var opn in document.ops1){
	    if (document.ops1[opn]==op1) break;
		ind++;
	}
	var opn = document.ops1[opn];
	document.selectedOp2=false;
	document.selectedOp1=opn;
    var nop2s = document.ops2.length;
	// highlight in table
	var table = document.getElementById("ep_table");
	var rows = table.getElementsByTagName("TR");
	var cells = rows[0].getElementsByTagName("TH");
	cells[1+ind].className=clas;
	for (var r=1; r<rows.length-1; r++){
	  var tag = (r==1)? "TH" : "TD";
	  var cells2 = rows[r].getElementsByTagName(tag);
	  var offs = (r==1)? 1 : 0;
	  for (var i=offs; i<nop2s+offs; i++){
	    cells2[(ind*nop2s)+i].className=clas;  // skip rowspan=2 qty cell and th linestart cell
      }
	}  
	if (unsel) document.selectedOp1=false;	
}

// select the specified option2 columns
function selectOp2(op2, unsel){
	if (op2 == document.selectedOp2) unsel=true;
    if (!unsel && document.selectedOp1) selectOp1(document.selectedOp1, true); // unselect if nec
    if (!unsel && document.selectedOp2) selectOp2(document.selectedOp2, true); // unselect if nec
    if (!unsel && document.selectedQty) selectQty(document.selectedQty, true); // unselect if nec	
    var clas = (unsel)? "" : "ep_colsel";
    var tree = document.pricetree;
	var ind = 0;
    for (var opn in document.ops2){
	    if (document.ops2[opn]==op2) break;
		ind++;
	}
	var opn = document.ops2[opn];
	document.selectedOp1=false;
	document.selectedOp2=opn;
	var nop2s=document.ops2.length;

	// highlight in table
	var table = document.getElementById("ep_table");
	var rows = table.getElementsByTagName("TR");
	for (var r=1; r<rows.length-1; r++){
	  var tag = (r==1)? "TH" : "TD";
	  var cells2 = rows[r].getElementsByTagName(tag);
	  var offs = (r==1)? 1 : 0;
	  for (var i=offs; i<cells2.length-offs; i++){
	    if ((i-offs) % nop2s == ind) cells2[i].className=clas;
      }
	}  
	if (unsel) document.selectedOp2=false;
}

// select the specified quantity row
function selectQty(qty, unsel){
	if (qty == document.selectedQty) unsel=true;
    if (!unsel && document.selectedOp1) selectOp1(document.selectedOp1, true); // unselect if nec
    if (!unsel && document.selectedOp2) selectOp2(document.selectedOp2, true); // unselect if nec
    if (!unsel && document.selectedQty) selectQty(document.selectedQty, true); // unselect if nec	
    if (!unsel) document.selectedQty = qty;
    var clas = (unsel)? "" : "ep_colsel";
    var tree = document.pricetree;
	// highlight in table
	var table = document.getElementById("ep_table");
	var rows = table.getElementsByTagName("TR");    
	var cells = false;
	for (var r=2; r<rows.length-1; r++){
      var qcell = rows[r].getElementsByTagName("TH")[0];
	  if (qcell.innerHTML==qty) {
	    qcell.className = clas;
		cells = rows[r].getElementsByTagName("TD");
		break;
      }		
	}
	if (!cells){
	  alert("ERROR: selection failed");
	  document.selectedQty = false;
	  return;
	}
    for (var i=0; i<cells.length; i++){
	    cells[i].className = clas;
	}	
	if (unsel) document.selectedQty=false;	
}

// delete the selected row/cols
function deleteSelection(){
   if (!(document.selectedOp1 || document.selectedOp2 || document.selectedQty)){
     alert("Nothing selected!");
	 return;
   }
   if (!confirm("Delete selection?")) return;  // prompt for OK
   if (document.selectedOp1){  // remove option 1 entries
      delete document.pricetree[document.selectedOp1];
	  document.selectedOp1=false;
   }
   if (document.selectedOp2){  // remove option 2 entries
       for (var i in document.pricetree){
	      delete document.pricetree[i][document.selectedOp2];
	   }
	   delete document.alloption2s[document.selectedOp2];
	   document.selectedOp2 = false;
   }
   if (document.selectedQty){  // remove qty entries
       for (var i in document.pricetree){
	     for (var j in document.pricetree[i]){
		   delete document.pricetree[i][j][document.selectedQty];
		 }
	   }
	   document.selectedQty = false;
    }
    updatePriceTable();  // update the price table display
}

// copies the current table to the pasteboard
function copyTable(){
    window.pastetree = recCopy(document.pricetree);
	alert("OK");
}

// copies an array recursively
function recCopy(array1){
    if (typeof array1 != 'object'){	  // don't copy primitives
	  return array1;
	}
	var array2 = new Array();
    for (var i in array1){
	  array2[i] = recCopy(array1[i]);
	}
	return array2;
}

// pastes the table from the pasteboard
function pasteTable(){
    if (!window.pastetree){  // any data?
	    alert("No data on pasteboard");
		return;
	}
    if (!confirm("Paste table data? Existing table will be replaced")) return;  // RU Shaw?
	// do it:
	document.pricetree = recCopy(window.pastetree);
	// rebuild all option 2s
	document.alloption2s=new Array();
    for (var i in document.pricetree){
	    for (var j in document.pricetree[i]){
		    if (!document.alloption2s[j]) document.alloption2s[j]=1;
		}	
	}	
	updatePriceTable();  // update display
}

function savePrices(newversion){
    if (!confirm("Are you sure?")) return;
    var ptarray = new Array();
	for (var i in document.pricetree){
	  for (var j in document.pricetree[i]){
	    for (var q in document.pricetree[i][j]){
		  var entry = new Array();
		  entry['component']=document.pricesFor;
		  entry['option']=document.pricesForOption;
		  entry['option1']=i;
		  entry['option2']=j;
		  entry['qty']=q;
		  entry['price']=document.pricetree[i][j][q];
		  ptarray[ptarray.length] = entry;
		}
	  }
	}
	ajSavePrices(ptarray, newversion);
}

function ajSavePrices(ptarray, newversion){
    var params = new Object();
	var fields = new Array("component", "option", "option1", "option2", "qty", "price");
	for (var i in fields) params['h_'+i] = fields[i];
	for (var r in ptarray){
	  for (var i in fields){
	    params['e_'+r+'_'+i]=ptarray[r][fields[i]];
	  }
	}
	params['PC_entries']=ptarray.length;
	params['PC_fields']=fields.length;
	params['PC_component']=document.pricesFor;		
	params['PC_option']=document.pricesForOption;				
	params['PC_version']=document.pricesForVersion;		
	params['PC_published']=(document.PC_published==document.pricesForVersion)? 1 : 0;
	params['PC_newversion']=(newversion)? 1: 0;		
	aj_setHandler("ptablehandler", document.ajurl, true);
	aj_startHandler("ptablehandler", "savePrices", "savedPrices", params);
    showWaiting(true);		
}

// Response: save prices 
function savedPrices(){
    showWaiting(false);	
    var res = aj_getResponseLines("ptablehandler");
	if (res[0]=="ERROR") alert(aj_getResponse("ptablehandler"));	
	else alert("Saved OK");
	aj_flush("ptablehandler");	
    updateVersionList();    
}

function showTable(){
    document.getElementById("pricetable_div").style.display="block";
}
function hideTable(){
    document.getElementById("pricetable_div").style.display="none";
}


/* //////////////////////////////////////////////// Movable Object methods ///////////////////////////////////////// */

document.onmousemove = mouseMove;
document.onmouseup   = mouseUp;
var dragObject  = null;
var dragObjectResizes  = null;
var mouseOffset = null;

function mouseCoords(ev){
	if(ev.pageX || ev.pageY){
		return {x:ev.pageX, y:ev.pageY};
	}
	if (document.body.scrollLeft && document.body.clientLeft) return {
		x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
		y:ev.clientY + document.body.scrollTop  - document.body.clientTop
	}
	return { x:ev.clientX, y:ev.clientY }
}
function getMouseOffset(target, ev){
	ev = ev || window.event;

	var docPos    = getPosition(target);
	var mousePos  = mouseCoords(ev);
	return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
}
function getContains(target, ev){
	ev = ev || window.event;
	var docPos    = getPositionAbs(target);
	var mousePos  = mouseCoords(ev);
	if (mousePos.x < docPos.x) return false;
	if (mousePos.y < docPos.y) return false;
	if (mousePos.x > docPos.x + target.offsetWidth) return false;
	if (mousePos.y > docPos.y + target.offsetHeight) return false;
	return true;
}
function getPosition(e){
	var left = e.offsetLeft;
	var top  = e.offsetTop;	
	return {x:left, y:top};
}
function getPositionAbs(e){
    var left = 0;
	var top = 0;
	if (e.offsetParent){
	  do {
	    left += e.offsetLeft;
		top += e.offsetTop;
	  }
	  while (e = e.offsetParent)
	}
	return {x:left, y:top};
}
function mouseMove(ev){
	ev           = ev || window.event;
	var mousePos = mouseCoords(ev);

	if(dragObject){  // handle dragging object
		dragObject.style.position = 'absolute';
		if (document.horiz == 1)
		  dragObject.style.top      = (mousePos.y - mouseOffset.y)+"px";
		if (document.vert == 1)
		  dragObject.style.left     = (mousePos.x - mouseOffset.x)+"px";
		if (dragObjectResizes)
		  resize(dragObject, dragObjectResizes);
		else
		  fixCorner(dragObject);
		return false;
	}
	if (document.movingRow){  // handle dragging table row
        var tab = document.movingRow.parentNode.parentNode;	
		var rows = tab.getElementsByTagName("TR");
		var i=-1;
		var d=0;
		for (j=0; j < rows.length; j++){
            if (getContains(rows[j], ev)) i=j;
			if (rows[j]==document.movingRow) d=j;
		}
		var rowPos = getPositionAbs(rows[0]);
		if (i==-1) i= (mousePos.y < rowPos.y)? 0: rows.length-1;  // handle above/below table
		if (i==d || i==0) return;
		var offset = (d < i)? 1 : 0;
		var nrow = tab.insertRow(i+offset);
		rebuild(nrow, document.movingRow);
		if (d>i) d++;
		tab.deleteRow(d);
		document.movingRow = nrow;
        for (var i=0; i<document.movingRow.childNodes.length; i++)
        document.movingRow.className="movingRow";
	}
}
// rebuild table row (because IE doesn't let you use innerHTML on tableRows !!)
function rebuild(row, source){
    var i = source.cells.length;
	for (var j=0; j<i; j++){
	  var c = row.insertCell(j);
	  c.innerHTML = source.cells[j].innerHTML;
	}
}
function mouseUp(){
	dragObject = null;
	dragObjectResizes = null;
	if (document.movingRow){
	    stopmoving();
	}	
}
// resize resizable object
function resize(){
  var e = document.getElementById(dragObjectResizes);
  if (e==null) return;
  var nwidth = (dragObject.offsetLeft +dragObject.offsetWidth - e.offsetLeft);
  var nheight = (dragObject.offsetTop +dragObject.offsetHeight - e.offsetTop);
  var wmin = 50;
  var hmin = 50;
  wmin = (dragObject.offsetWidth > wmin)? dragObject.offsetWidth : wmin;
  hmin = (dragObject.offsetHeight > hmin)? dragObject.offsetWidth : hmin;
  if (nwidth < wmin) nwidth = 50;
  if (nheight < hmin) nheight = 50;
  e.style.width = nwidth + "px";
  e.style.height = nheight + "px";
  dragObject.style.left = (e.offsetLeft + e.offsetWidth - dragObject.offsetWidth) + "px";
  dragObject.style.top = (e.offsetTop + e.offsetHeight - dragObject.offsetHeight) + "px";  
}
// make object draggable
// ditem = draggable item, horiz, vert = 1 (draggable) 0 (not draggable), resizes = other object that this resizes (or self)
function makeDraggable(ditem, horiz, vert, resizes){
	if(!ditem) return;
	  var corn = document.getElementById("_corner");
    if (!corn) {
      var corn = document.createElement("DIV");
      corn.id='_corner';
      corn.style.width="8px";
      corn.style.height="8px";
      corn.style.fontSize = "2px";
      corn.style.backgroundColor = "#000000";
      corn.style.border="none";
      corn.style.zIndex = 9999;
      corn.style.display="none";
      corn.style.position="absolute";
	  corn.style.margin="0";
	  corn.style.padding="0";
      document.body.appendChild(corn);
    }  	
	if (!resizes){  // main object drag function
	  ditem.onmousedown = function(ev){
		  dragObject  = this;
		  dragObjectResizes = null;
		  mouseOffset = getMouseOffset(this, ev);
		  if (downOnInput(this, ev)) return;
		  this.style.cursor="move";		  
		  document.horiz = horiz;
		  document.vert = vert;		  
		  fixCorner(this);
		  return false;
	  } 
	  ditem.onmouseup = function(ev){
	      this.style.cursor="default";
	  }
    makeDraggable(corn, 1, 1, ditem.id);
	}
	else {  // corner drag function
	  ditem.onmousedown = function(ev){
		  if (downOnInput(this, ev)) return;
		  dragObject  = this;
		  this.style.cursor="move";		  		  
		  dragObjectResizes = resizes;		  
		  mouseOffset = getMouseOffset(this, ev);
		  document.horiz = horiz;
		  document.vert = vert;
		  return false;
	  }
	  ditem.onmouseup = function(ev){
		  this.style.cursor="default";		  	  
	  }
	} 
	//  

}
// is mousedown inside an input element?
function downOnInput(obj, ev){
    if (!obj.nodeName) return false;          // discount if not DOM element
    if (obj.nodeName=="#text") return false;   // discount if text element
	if (!getContains(obj, ev)) return false;  // discount if not clicked on
    if (obj.nodeName=="INPUT" || obj.nodeName=="TEXTAREA") {
	  return true;  // got an input
	}  
	for (var i=0; i<obj.childNodes.length; i++){    // check child objects
	    var kid = obj.childNodes[i];
		if (downOnInput(kid, ev)) return true;
    }
	return false;
}
function fixCorner(ditem){
  var corn = document.getElementById("_corner");
  var cleft = ditem.offsetLeft + ditem.offsetWidth - 8;
  var ctop = ditem.offsetTop + ditem.offsetHeight - 8;      
  corn.style.left = cleft + "px";
  corn.style.top = ctop + "px";
  corn.style.display = "block";
  corn.style.position = "absolute";
}
function hideCorner(){
  var corn = document.getElementById("_corner");
  if (corn) corn.style.display="none";
}
// make a table reorderable, specifying the callback function to execute when row move complete
function makeReorderable(tab, callback){
  tab.style.cursor="default";
  if (!document.callbacks) document.callbacks = new Object();
  document.callbacks[tab] = callback;
  tab.onselectstart = function() { return false; }
  tab.onmousedown = function() { return false; }  
  var rows = tab.getElementsByTagName("TR");
  for (var i=0; i<rows.length; i++){
      rows[i].onmousedown = function(ev){
          mouseOffset = getMouseOffset(this, ev);
	      startmoving(this);
	  }
  }
}
// process: mouse started moving (row move)
function startmoving(row){
  document.movingRow = row;
  document.movingHTM = row.innerHTML;
  row.className="movingRow";
  document.body.style.cursor="move";
  row.parentNode.parentNode.style.cursor="move";
}
// process: mouse stopped moving (after row move)
function stopmoving(){
  document.movingRow.className="rowWait";
  var tab = document.movingRow.parentNode.parentNode;
  document.movedRow = document.movingRow;
  document.movingRow = false; 
  document.body.style.cursor="wait"; 
  var fn = document.callbacks[tab]+"()";
  eval(fn);
}
// AJAX call to modify structure & reload table after row move
function prodstrucmove(def){
  var order_seq = document.movedRow.childNodes[0].childNodes[0].value;
  var tbod = document.movedRow.parentNode;
  var before_seq = 0;  
  if (document.movedRow.rowIndex>1){
    var pre = document.movedRow.previousSibling;
    while (pre.nodeName!="TR") pre = pre.previousSibling;
    before_seq = pre.childNodes[0].childNodes[0].value;
  }	
  var dest = parseInt(before_seq);
  if (dest < order_seq) dest++;
  if (dest==order_seq){
    document.movedRow.className="";
    var tab = document.movedRow.parentNode.parentNode;	
	tab.style.cursor="default";
	showWaiting(false);
	makeReorderable(tab, document.callbacks[tab]);	
    return;
  }  
    aj_setHandler("structhandler", document.ajurl, true);
	var selid = (def)? 'prod_type_sel' : 'prod_code_sel';
 	var val = document.getElementById(selid).value;
    params = new Object();
	params['PC_default']=(def)? 1: 0;
	params['PC_code']=val;
	params['src']=order_seq;
	params['dest']=dest;
	var callback = (def)? "defStructUpdated" : "prodStructUpdated";	
    aj_startHandler("structhandler", "structMove", callback, params);		
    showWaiting(true);  
}
// modify default structure after row move
function defstrucmove(){
  prodstrucmove(true);
}
