// Javascript Functions for TMIC Dental Forms

// Add onload event
eventer(window, 'load', formInit, false);
// Global Variables
var isIE = document.all;
var steps = new Array();
var cats = new Array();
var currentStep;
var currentStepNum;
var ids = new Array('geninfo', 'pracinfo', 'profliacov', 'proc', 'refcom');
var reviewStep, reviewTable, sendingStep, thankyouStep;
var mainForm;
// Functions
function formInit() {
    // Create Categories and Steps
    for (var i=0; i<ids.length; i++) {
	cats.push(new Category(nodeOf(ids[i])));
	steps.push(new Step(i, i+1, cats[i]));
	foster(nodeOf('steps'), steps[i].link);
    }
    reviewStep = nodeOf('review');
    reviewTable = nodeOf('review-table');
    sendingStep = nodeOf('sending');
    thankyouStep = nodeOf('thanks');
    // Stylize and Add functionality to navigation buttons
    nodeOf('nav_back').className = 'navigation';
    eventer(nodeOf('nav_back'), 'click', navigate, false);
    nodeOf('nav_next').className = 'navigation';
    eventer(nodeOf('nav_next'), 'click', navigate, false);
    nodeOf('nav_submit').className = 'navigation';
    eventer(nodeOf('nav_submit'), 'click', navigate, false);
    nodeOf('nav_submit').style.display = 'none';
    // Display 1st Step
    currentStepNum = 0;
    steps[currentStepNum].display();
    nodeOf('nav_back').disabled = true;
    // Get node of form
    mainForm = document.forms[0];
}
function navigate(e) {
    e = new Evt(e);
    var node = e.source;
    var stepNum, errors;
    if (nodeOf('review').style.display != 'block') {
	stepNum = currentStep.num;
    } else {
	stepNum = steps.length;
    }
    switch (node.id) {
    case 'nav_back':
	if (stepNum > 0) {
	    steps[stepNum-1].display();
	}
	break;
    case 'nav_next':
	errors = currentStep.checkFields();
	if (errors) return;
	if (stepNum < (steps.length-1)) {
	    steps[stepNum+1].display();
	}
	break;
    case 'nav_submit':
	if (node.value == 'submit') {
	    thanks();
	} else {
	    for (var i=0; i<steps.length; i++) {
		errors = steps[i].checkFields();
		if (errors) {
		    return;
		}
	    }
	    nodeOf('nav_next').disabled = true;
	    if (node.value.match(/review/i)) {
		node.value = 'submit';
		review();
	    }
	}
    }
}
function updateNavButtons() {
    hideReviewStep();
    if (currentStep.num == 0) {
	nodeOf('nav_back').disabled = true;
    } else {
	nodeOf('nav_back').disabled = false;
    }
    if (currentStep.num == steps.length-1) {
	nodeOf('nav_next').disabled = true;
	nodeOf('nav_submit').style.display = 'inline';
	nodeOf('nav_submit').value = 'review';
    } else {
	nodeOf('nav_next').disabled = false;
    }
}
function displayStep(e) {
    e = new Evt(e);
    e.source.parentObject.display();
}
function hideSteps() {
    for (var i=0; i<steps.length; i++) {
	steps[i].hide();
    }
}
function hideReviewStep() {
    reviewStep.style.display = 'none';
    nodeOf('nav_submit').value = 'review';
}
function review() {
    hideSteps();
    while (reviewTable.hasChildNodes()) {
	reviewTable.removeChild(reviewTable.childNodes[0]);
    }
    reviewStep.style.display = 'block';
    var reviewBody = elementize('tbody');
    foster(reviewTable, reviewBody);
    for (var i=0; i<steps.length; i++) {
	foster(reviewBody, steps[i].review());
    }
}
function thanks() {
    // Hide Steps, Navigation, and Review page
    hideSteps();
    hideReviewStep();
    nodeOf('stepsHolder').style.display = 'none';
    nodeOf('navigation').style.display = 'none';
    sendingStep.style.display = 'block';
    // Build post variable
    var the_call = 
	'email_to=' + encodeURIComponent(nodeOf('e_mail').value) +
	'&quote=' + encodeURIComponent(reviewTable.innerHTML);
    // Send emails and Display Thank You page
    var client = createClient();
    if (client && the_call) {
        client.onreadystatechange = function() {
            if (client.readyState == 4 && client.status == 200) {
		sendingStep.style.display = 'none';
		if (client.responseText == 'Ok') {
		    thankyouStep.style.display = 'block';
		} else {
		    reviewStep.style.display = 'block';
		    nodeOf('navigation').style.display = 'block';
		    nodeOf('nav_submit').value = 'submit';
		    nodeOf('stepsHolder').style.display = 'table-cell';
		    alert(client.responseText);
		}
            }
        };
        client.open('POST', 'sendquote.php', true);
        client.setRequestHeader("Content-type",
                                "application/x-www-form-urlencoded");
        client.setRequestHeader("Content-length", the_call.length);
        client.setRequestHeader("Connection", "close");
        client.send(the_call);
    }
}
// Function checks date format validity
function checkDate(node) {
    node.value = node.value.replace(/[a-zA-Z\s]/g, '');
    var date = node.value.split(/[\/-]/);
    for (var i=0; i<date.length; i++) {
	date[i] = parseInt(date[i], 10);
    }
    if (!node.value.match(/^(\d{2}[\/-]){2}\d{4}$/) ||
	date[0] < 1 || date[0] > 12 ||
	date[1] < 1 || date[1] > 31) {
	var msg = 'Please use this date format: mm/dd/yyyy';
	node.parentObject.reportErrors(node, msg);
    } else {
	return node.value;
    }
}
// Function checks for email validity
function checkEmail(node) {
    var re = new RegExp(/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/);
    if (!node.value.match(re)) {
	node.parentObject.reportErrors(node, 
				       'Please enter a valid email address');
    } else {
	return node.value;
    }
}
// Function checks field for a certain number of names
function checkFullName(node) {
    node.value = node.value.replace(/\d/g, '');
    var names = node.value.split(' ');
    if (names.length < 2) {
	node.parentObject.reportErrors(node, 'Please enter your full name');
    } else {
	node.value = node.value.replace(/\b([a-z])(\w*)\b/g, function(s,t,u) { 
	    return (t.toUpperCase() + u);
	});
	return node.value;
    }
}
// Function checks field for a name
function checkName(node) {
    node.value = node.value.replace(/\d/g, '');
    if (!node.value) {
	var msg = 'Please fill in the ' + node.name + ' field';
	node.parentObject.reportErrors(node, msg);
    }
    node.value = node.value.replace(/\b([a-z])(\w*)\b/g, function(s,t,u) { 
	return (t.toUpperCase() + u);
    });
    return node.value;
}
// Function checks field for a certain number of digits
function checkNumber(node, args) {
    node.value = node.value.replace(/\D/g, '');
    var digNum = args.length ? args[0] : node.maxLength;
    var len = node.value.length;
    if (len < digNum) {
	node.parentObject.reportErrors(node, 'Please enter a ' + digNum + 
				       '-digit number for ' + node.name);
    } else {
	return node.value;
    }
}
/*
 * Function checks selected option for 'Other' and reads in 'other' input
 * field
 */
function checkMultipleChoice(node) {
    var msg;
    if (node.nodeName == 'SELECT') {
	if (!node.value) {
	    msg = 'Please choose an option for ' + node.name;
	    node.parentObject.reportErrors(node, msg);
	} else if (node.value.match(/other/i) && 
		   !nodeOf(node.id + '_other').value) {
	    msg = 'Please fill in the ' + nodeOf(node.id + '_other').name + 
		' field';
	    node.parentObject.reportErrors(node, msg);
	} else {
	    var retval = node.value;
	    if (node.value.match(/other/i)) {
		retval += ': ' + nodeOf(node.id + '_other').value;
	    }
	    return retval;
	}
    } else if (node.nodeName == 'INPUT') {
	var values = 
	    getCheckedValue(node.parentObject.fields[node.name].nodes);
	if (!values.length) {
	    msg = 'Please select at least one option for ' + node.name;
	    node.parentObject.reportErrors(node, msg);
	} else {
	    var m = values.match(/other/i);
	    if (m != -1 && !nodeOf(node.id + '_other').value) {
		msg = 'Please fill in the ' + 
		    nodeOf(node.id + '_other').name + ' field';
		node.parentObject.reportErrors(node, msg);
	    } else {
		if (m > -1)
		    values[m] += ': ' + nodeOf(node.id + '_other').value;
		return values.join("<br />\n");
	    }
	}
    }
}
function checkEitherOrOld(node, args) {
    var values1 = getCheckedValue(node.parentObject.fields[node.name].nodes);
    var values2 = getCheckedValue(node.parentObject.fields[args[0]].nodes);
    if (!(values1.length || values2.length) ||
	(values1.length && values2.length)) {
	msg = 'Please select at least one option from either this field ' +
	    ' or from ' + args[0];
	node.parentObject.reportErrors(node, msg);
    } else if (values1.length) {
	return values1.join("<br />\n");
    }
}
//modified function to allow both options to be selected
function checkEitherOr(node, args) {
    var values1 = getCheckedValue(node.parentObject.fields[node.name].nodes);
    var values2 = getCheckedValue(node.parentObject.fields[args[0]].nodes);
    if (!(values1.length || values2.length)) {
	msg = 'Please select at least one option from either this field ' +
	    ' or from ' + args[0];
	node.parentObject.reportErrors(node, msg);
    } else if (values1.length) {
	return values1.join("<br />\n");
    }
}
function getCheckedValue(nodes) {
    var values = new Array();
    for (var i=0; i<nodes.length; i++) {
	if (nodes[i].checked) {
	    values.push(nodes[i].value);
	}
    }
    return values;
}
function hasValue(node) {
    if (!node.value) {
	var msg = 'Please fill in the ' + node.name + ' field';
	if (node.nodeName == 'SELECT') {
	    msg = 'Please choose an option for ' + node.name;
	}
	node.parentObject.reportErrors(node, msg);
    } else {
	return node.value;
    }
}
// Objects
function Step(i, n) {
    this.num = i;
    this.name = n;
    this.link = elementize('td', 'id=form-step_' + n, 'className=form-step',
			   'align=center');
    texit(this.link, n);
    this.link.parentObject = this;
    this.cats = new Array();
    var args = arguments;
    for (var i=2; i<args.length; i++) {
	this.cats.push(args[i]);
    }
}
Step.prototype.addCat = function(c) {
    this.cats.push(c);
};
Step.prototype.checkFields = function() {
    var firstError = 0;
    var errors = 0;
    // Testing purposes only: skip field checking
    //if (this.num < 5) return 0;
    this.display();
    for (var i=0; i<this.cats.length; i++) {
	errors += this.cats[i].checkFields();
	if (this.cats[i].errors.length && !firstError) {
	    firstError = this.cats[i].errors[0];
	}
    }
    if (firstError) {
	firstError.focus();
    }
    return errors;	
};
Step.prototype.display = function() {
    if (currentStep) {
	currentStep.hide();
    }
    currentStep = this;
    for (var i=0; i<this.cats.length; i++) {
	this.cats[i].display();
    }
    this.cats[0].focusOn(0,0);
    eventer(this.link, 'click', displayStep, false);
    this.link.className = 'form-step-selectable';
    this.link.style.backgroundColor = '#0080bd';
    updateNavButtons();
};
Step.prototype.hide = function() {
    for (var i=0; i<this.cats.length; i++) {
	this.cats[i].hide();
    }
    this.link.style.backgroundColor = '#2af';
};
Step.prototype.post = function() {
    var the_call;
    for (var i=0; i<this.cats.length; i++) {
	the_call += this.cats[i].post();
    }
    return the_call;
};
Step.prototype.review = function() {
    var table = elementize('table');
    var body = elementize('tbody');
    for (var i=0; i<this.cats.length; i++) {
	foster(body, this.cats[i].review());
    }
    foster(table, body);
    return foster(elementize('tr'), foster(elementize('td'), table));
};
//
function Category(n) {
    // Overall node containing all the labels and fields
    this.node = n;
    // Get legend from node and set as title
    this.title = tags(this.node, 'legend')[0].innerHTML;
    // Array to hold all the valid field names
    this.names = new Array();
    // Object to hold all the data fields' names and nodes
    this.fields = new Object();
    // Object to hold all the data fields' values
    this.values = new Object();
    // Array to hold error fields
    this.errors = new Array();
    // Initialize Category
    this.init();
}
Category.prototype.checkFields = function() {
    this.clearErrors();
    for (var i in this.fields) {
	this.checkField(i);
    }
    return this.errors.length;
};
Category.prototype.checkField = function(n) {
    /*
     * errorCheck defined function only on first node is necessary:
     *  - Each text or select data are singular
     *  - Only radio buttons and checkboxes may have multiple values for
     *    one field.  Still the errorCheck'ing method only needs the name
     *    of the field, not the nodes.
     */
    var field = this.fields[n];
    var func = field.errorCheck.func;
    var args = field.errorCheck.args;
    var node = field.nodes[0];
    var reqIf = field.reqIf;
    if (reqIf && !this.values[reqIf]) {
	this.values[n] = null;
    } else {
	this.values[n] = func(node, args);
    }
};
Category.prototype.clearErrors = function() {
    for (var i=0; i<this.errors.length; i++) {
	var node = this.errors[i];
	// Remove all error messages
	node.offsetParent.removeChild(nodeOf(node.id + '-error'));//offsetParent.lastChild);
	// Reset border colors
	node.style.borderColor = '';
    }
    // Reset errors array
    this.errors = new Array();
};
Category.prototype.display = function() {
    this.node.style.display = 'block';
};
Category.prototype.focusOn = function(i,j) {
    if (typeof(i) == 'number') {
	i = this.names[i];
    }
    this.fields[i].nodes[j].focus();
};
Category.prototype.hide = function() {
    this.node.style.display = 'none';
};
Category.prototype.init = function() {
    /*
     * First retrieve fields container:
     *  - Here holder is a row but code can be modified to adapt to other 
     *    types of tags.
     *  - for each row, the first column would be the label and the second is
     *    the data field(s)
     */ 
    var req = false;
    var rows = tags(this.node, 'tr');
    for (var i=0; i<rows.length; i++) {
	req = false;
	var cols = tags(rows[i], 'td');
	if (cols.length < 2) continue;
	var label = cols[0];
	var data = cols[1];
	// Stylize labels
	label.align = 'right';
	label.vAlign = 'top';
	if (label.innerHTML.match(/^\s*\*|\*\s*$/)) {
	    label.className = 'required';
	    req = true;
	}
	// Retrieve relevant tag(s) from data, i.e. the field
	/*
	var fields = tags(data, 'input');
	if (!fields.length) {
	    fields = tags(data, 'select');
	}
	*/
	var fields = tags(data, 'input', 'select', 'textarea');
	if (fields['input'].length) {
	    fields = fields['input'];
	} else if (fields['select'].length) {
	    fields = fields['select'];
	} else {
	    fields = fields['textarea'];
	}
	// Collect inputs, select boxes
	for (var j=0; j<fields.length; j++) {
	    var f = fields[j];
	    var name;
	    if (!f.name) {
		name = label.innerHTML.replace(/\*/g, '');
		name = name.replace(/^\s*|\s*$/g, '');
		if (isIE && f.type == 'radio') {
		    var op = f.offsetParent;
		    var opc = op.childNodes;
		    var before = new Array();
		    var after = new Array();
		    var found = false;
		    var html = f.outerHTML.replace(/\/?>$/, 
						   ' name="' + name + '" />');
		    while (opc.length) {
			if (opc[0] == f) {
			    op.removeChild(f);
			    found = true;
			} else {
			    if (found) {
				after.push(opc[0]);
			    } else {
				before.push(opc[0]);
			    }
			    op.removeChild(opc[0]);
			}
		    }
		    f = document.createElement(html);
		    for (var k=0; k<before.length; k++) {
			op.appendChild(before[k]);
		    }
		    op.appendChild(f);
		    for (var k=0; k<after.length; k++) {
			op.appendChild(after[k]);
		    }
		} else {
		    f.name = name;
		}
	    }
	    if (!this.fields[f.name]) {
		this.names.push(f.name);
		this.fields[f.name] = new Object();
		this.fields[f.name].nodes = new Array();
		try {
		    if(f.getAttribute('required') != null) {
			field_req = eval(f.getAttribute('required'));
		    } else {
			field_req = req;
		    }
		} catch(e) {
		    field_req = req;
		}
		this.fields[f.name].required = field_req;
		var func;
		try {
		    var args = f.getAttribute('errorCheck').split('|');
		    func = eval(args.shift());
		    if (typeof(func) != 'function') {
			func = hasValue;
		    }
		} catch(e) {
		    func = hasValue;
		}
		this.fields[f.name].errorCheck = new Object();
		this.fields[f.name].errorCheck.func = func;
		this.fields[f.name].errorCheck.args = args;
		var reqIf;
		try {
		    reqIf = f.getAttribute('requiredIf');
		} catch(e) {
		    reqIf = null;
		}
		this.fields[f.name].reqIf = reqIf;
		this.fields[f.name].label = label.innerHTML;
	    }
	    if (!f.id) {
		f.id = 
		    f.name.toLowerCase().replace(/\W/g, '');
		var sfx = this.fields[f.name].nodes.length;
		if (sfx)
		    f.id += '_' + sfx;
	    }
	    this.fields[f.name].nodes.push(f);
	    if (f.id.match(/phone|fax/i)) {
		f.style.textAlign = 'center';
	    }
	    f.parentObject = this;
	}
    }
    this.node.style.display = 'none';
};
Category.prototype.post = function() {
    var the_call;
    for (var i in this.fields) {
	var label = elementize('td');
	texit(label, this.fields[i].label + ': ');
	var value = elementize('td');
	var func = this.fields[i].errorCheck.func;
	var args = this.fields[i].errorCheck.args;
	var node = this.fields[i].nodes[0];
	var v = func(node, args);
	value.innerHTML = v ? v : '';
	foster(body, foster(row, label, value));
    }
    foster(table, body);
    return foster(elementize('tr'), foster(elementize('td'), table));
};
Category.prototype.reportErrors = function(node, msg) {
    if (reviewStep.style.display == 'block') return;
    if (!this.fields[node.name].required) return;
    node.style.borderColor = '#f55';
    var err = elementize('div', 'id=' + node.id + '-error',
			 'className=form-error');
    texit(err, msg);
    foster(node.offsetParent, err);
    this.errors.push(node);
};
Category.prototype.review = function() {
    var table = elementize('table', 'style.fontFamily=arial');
    var body = elementize('tbody');
    var title = foster(elementize('tr'), 
		       elementize('th', 'align=left',
				  'innerHTML=' + this.title));
    foster(body, title);
    for (var i in this.values) {
	var row = elementize('tr');
	var label = elementize('td');
	texit(label, i + ': ');
	var value = elementize('td', 'style.color=#0080bd');
	var v = this.values[i];
	value.innerHTML = v ? v : '';
	foster(body, foster(row, label, value));
    }
    foster(table, body);
    return foster(elementize('tr'), foster(elementize('td'), table));
};

function nodeOf() {
  var a = arguments;
  var r = new Array();
  for (var i=0; i<a.length; i++) {
    var o = a[i];
    if (typeof(o) == 'string') r.push(document.getElementById(o));
    else r.push(o);
  }
  if (r.length == 1) return r[0];
  return r;
}

function tags() {
    var a = arguments;
    var r = new Array();
    for (var i=1; i<a.length; i++)
	r[a[i]] = a[0].getElementsByTagName(a[i]);

    if (i == 2) return r[a[1]];
    return r;
}

function tagValue(node, tag)
{
    return tags(node, tag)[0].firstChild.nodeValue;
}

function elementize() {
  var type = arguments[0];
  var nel;
  nel = document.createElement(type);
  for (var i=1; i<arguments.length; i++) {
    var attr = arguments[i].split(/=/);
    eval("nel." + attr[0] + " = '" + attr[1] + "';");
  }  
  return nel;
}

function texit(node, tex) {
    var texel = document.createTextNode(tex);
    foster(node, texel);
}

function foster() {
  var parent = arguments[0];
  for (var i=1; i<arguments.length; i++) {
    parent.appendChild(arguments[i]);
  }
  return parent;
}

function createClient() {
    var client;
    try {
        client = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
    } catch (e) {
        alert("Sorry, your browser is not AJAX-enabled!");
    }
    return client;
}

function eventer (target,type,func,bubbles) {
    if (document.addEventListener) {
        target.addEventListener(type,func,bubbles);
    } else if (document.attachEvent) {
        target.attachEvent("on"+type,func,bubbles);
    } else {
        target["on"+type] = func;
    }
}

function Evt(evt) {
    this.evt = evt ? evt : window.event; 
    this.source = evt.target ? evt.target : evt.srcElement;
    this.x = evt.pageX ? evt.pageX : evt.clientX;
    this.y = evt.pageY ? evt.pageY : evt.clientY;
}

if (!Array.prototype.match) {
    Array.prototype.match = function(re) {
	var len = this.length;

	var from = Number(arguments[1]) || 0;
	from = (from < 0) ? Math.ceil(from) : Math.floor(from);
	if (from < 0) from += len;

	for (; from < len; from++) {
	    if (from in this && this[from].match(re))
		return from;
	    }
	return -1;
    };
}
