/**
 * Used by the site on all pages
 *
 * @author Centricity Systems, Inc.
 * @company Centricity Systems, Inc.
 * @created 2008-Jun-10
 * @modified 2008-Jun-10
 * @version 1.0
 */

/* global variable used in all class object calls to site routines */
var vySite;
/* global variable used by AJAX routines */
var vyAjax;

/* global non-class-object API utility functions */
/**
 * Returns true if empty, otherwise false.
 * @param {String} val The value to test if empty
 * @param {Boolean} [trim_val] {optional} set to true to trim the
 * val prior to testing for empty. This eliminates values with all
 * spaces or other non-printable characters
 * @return {Boolean}
 */
function vyIsEmpty(val) {
	var _args = vyIsEmpty.arguments;
	var trim_val = (_args.length>1&&_args[1]===true?true:false);
	if ( trim_val ) { val = vyTrimString(val); }
	return (val==null||val==""?true:false);
}
/**
 * Trims all non-printable characters from beginning and end of
 * string (val).
 * @param {String} val The string value to trim.
 * @return {String} Returns trimmed string.
 */
function vyTrimString(val) {
	if ( val == null || val == "" || typeof(val) != 'string' ) { return val; }
	return(val.replace(/^\s+/,'').replace(/\s+$/,''));
}
/**
 * @class vyWebSite
 * This class contains all utility methods for managing
 * the website context
 */
function vyWebSite() {
	this.context = null;
	this.tags = null;
	/**
	 * Sets the initial context for the website page loading.
	 * This gets called in the header of all website pages.
	 * @param {String} company_name The name of the logged in users company
	 * @param {String} record_id The record ID of the logged in user's company record.
	 * @param {String} ip_address The IP address of the user's location.
	 * @param {String} url The current called URL
	 * @param {String} user_email The email address of the logged in user.
	 * @param {String} item_id The record ID of the page item (item, info item,
	 * solution, etc), otherwise null.
	 * @param {String} category_id The current category being viewed, otherwise null.
	 * @param {String} referrer_url The referrer URL that called the current page.
	 * @return {void}
	 */
	this.set_site_context = function() {
		this.context = new vySiteContext(this.set_site_context.arguments);
	}
	/**
	 * Collects all special &lt;VYINPUT/&gt; tags in the content
	 * and processes them.
	 * @return {Boolean{ Returns true if one or more tags were located,
	 * otherwise false.
	 */
	this.set_tags = function() {
		this.tags = new vyInputTags();
	}
	
}	// end class vyWebSite

/**
 * @class vySiteContext
 * Contains the context information for the site
 * @param {String} company_name The name of the logged in users company
 * @param {String} record_id The record ID of the logged in user's company record.
 * @param {String} ip_address The IP address of the user's location.
 * @param {String} query string The current called query string
 * @param {String} user_email The email address of the logged in user.
 * @param {String} item_id The record ID of the page item (item, info item,
 * solution, etc), otherwise null.
 * @param {String} category_id The current category being viewed, otherwise null.
 * @param {String} referrer_url The referrer URL that called the current page.
 */
function vySiteContext(_argsArray) {
	this.company;
	this.cust_id;
	this.ip;
	this.query_str;
	this.get;
	this.email;
	this.item_id;
	this.cat_id;
	this.current_url;
	this.referrer_url;
	/**
	 * Initialization routine
	 * @param {Array} _args An array of arguments passed into the class
	 * @return {void}
	 */
	this.initialize = function(_args) {
		this.company = (_args.length>0?_args[0]:null);
		this.cust_id = (_args.length>1?_args[1]:null);
		this.ip = (_args.length>2?_args[2]:null);
		this.query_str = (_args.length>3?_args[3]:null);
		this.email = (_args.length>4?_args[4]:null);
		this.item_id = (_args.length>5?_args[5]:null);
		this.cat_id = (_args.length>6?_args[6]:null);
		this.referrer_url = (_args.length>7?_args[7]:null);
		this.current_url = location.href;
		this.parse_query_string();
	}
	/**
	 * 
	 */
	this.parse_query_string = function() {
		this.get = new vyAssocArray();
		if ( vyIsEmpty(this.query_str) ) { return; }
		var queries = this.query_str.split('&');
		var query;
		for (var i=0; i<queries.length; i++) {
			query = queries[i].split('=');
			this.get[query[0]] = (query.length>1?query[1]:'');
		}
	}
	/**
	 * Returns the URL query string parameter
	 * @param {String} name The name of the parameter
	 * @param {String} [default] The default value if the parameter is not
	 * available.
	 * @return {String}
	 */
	this.get_query = function(name) {
		var dflt = (this.get_query.arguments.length>1?this.get_query.arguments[1]:'');
		return (!this.get.isset(name)?dflt:this.get[name]);
	}
	/**
	 * Returns the company name of the current logged in user.
	 * @return {String}
	 */
	this.get_company_name = function() { return this.company; }
	/**
	 * Returns the record ID of the current logged in user's company record.
	 * @return {String}
	 */
	this.get_customer_id = function() { return this.cust_id; }
	/**
	 * Returns the IP address of the current logged in user.
	 * @return {String}
	 */
	this.get_ip_address = function() { return this.ip; }
	/**
	 * Returns the URL of the page currently being called.
	 * @return {String}
	 */
	this.get_current_url = function() { return this.current_url; }
	/**
	 * Returns the Referrer URL of the page currently being called.
	 * @return {String}
	 */
	this.get_referrer_url = function() { return this.referrer_url; }
	/**
	 * Returns the email address of the current logged in user.
	 * @return {String}
	 */
	this.get_user_email = function() { return this.email; }
	/**
	 * Returns the item ID currently being viewed.
	 * @return {String}
	 */
	this.get_item_id = function() { return this.item_id; }
	/**
	 * Returns the category ID currently being viewed.
	 * @return {String}
	 */
	this.get_category_id = function() { return this.cat_id; }

	/* call initialize on class instantiation */
	this.initialize(_argsArray);	
}	// end vySiteContext class

/**
 * @class vyInputTags
 * Collects and processes all &lt;VYINPUT/&gt; tags on the
 * current page during loading.
 */
function vyInputTags() {
	this.tags = null;
	this.processed_count = 0;
	
	this.initialize = function() {
		this.init_input_tags();
		this.process_tags();
	}
	
	this.init_input_tags = function() {
		var tags = document.getElementsByTagName('VYINPUT');
		if ( !tags || tags.length == 0 ) { return; }
		// process each tag
		this.tags = new Array();
		for(var i=0; i<tags.length; i++ ) {
			this.tags[i] = new vyInputTag(tags[i]);
			this.processed_count += 1;
		}
	}
	
	this.get_count = function() {
		return (!this.tags?0:this.tags.length);
	}
	
	this.get_processed = function() { return this.processed_count; }
	
	this.process_tags = function() {
		if ( this.get_processed() == 0 ) { return; }
		for(var i=0; i<this.tags.length; i++) {
			this.tags[i].process();
		}
	}
	
	/* call initialization routine */
	this.initialize();
}	// end vyInputTags class

function vyInputTag(node) {
	this.attributes;
	this.id;
	this.node;
	this.parent;
	
	this.initialize = function(_args) {
		this.node = _args[0];
		if ( !this.node ) { return; }
		this.set_tag_attributes();
		if ( !this.attributes.isset('id') ) { return; }
		this.id = this.attributes['id'].toLowerCase();
		this.parent = this.node.offsetParent;
	}
	/**
	 * Sets an array of attributes as an associative array
	 * @return {void}
	 */
	this.set_tag_attributes = function() {
		this.attributes = new vyAssocArray();
		var name = null;
		for (var i=0; i<this.node.attributes.length; i++) {
			name = ""+this.node.attributes[i].name;
			this.attributes[name.toLowerCase()] = this.node.attributes[i].value;
		}
	}
	/**
	 * Retrieves the named attribute or returns default value
	 * @param {String} name The name of the attribute
	 * @param {String} [default] The default value to return if this
	 * attribute is missing
	 * @return {String} Returns the attribute value or default.
	 */
	this.get_attribute = function(name) {
		var _args = this.get_attributes.arguments;
		var dflt = (_args.length>1?_args[1]:'');
		return (this.attributes.isset(name)?this.attributes[name]:dflt);
	}
	/**
	 * Processes the tags
	 */
	this.process = function() {
		var tag;
		switch(this.id) {
			case 'feedbackform'		:
			case 'feedback form'	:
				tag = new vyFeedbackForm(this);
				break;
		}
	}
	/* initialize */
	this.initialize(vyInputTag.arguments);
}

function vyFeedbackForm(_this) {
	this.attributes;
	this.parent;
	this.form_html;
	this.action_url;
	this.default_comment;
	this.returnXml;
	
	this.init = function(_this) {
		this.returnXml = false;
		this.attributes = _this.attributes;
		this.parent = _this.parent;
		this.form_html = "";
		this.action_url = "";
		this.default_comment = "";
	}
	
	this.execute = function(_this) {
		if ( this.form_submitted() ) { return; }
		this.init(_this);
		var fields = 'custrecord_pref_sol_fb_dflt_comments,custrecord_pref_sol_fb_form_template,custrecord_pref_sol_fb_form_url';
		// get preferences suitelet URL
		var url = 'http://www2.vyatta.com/app/site/hosting/scriptlet.nl?script=12&deploy=1&returntype='+(this.returnXml?'xml':'text')+'&fields='+fields;
		var response = new csapiHttpClient().make_request(url,"GET",null,false);
		this.parse_xml(response);
		// create node filling
		this.set_node();
	}
	
	this.form_submitted = function() {
		var url = vySite.context.get_current_url();
		return (url.indexOf('vyr=T')>0?true:false); 
	}
	
	this.set_node = function() {
		if ( !this.parent ) { return; }
		var div = document.createElement('div');
		div.innerHTML = this.form_html;
		this.parent.appendChild(div);
	}
	
	this.parse_xml = function(xml) {
		this.default_comment = "";
		this.action_url = "";
		this.form_html = "";
		if ( vyIsEmpty(xml) || xml === false ) { return; }
		var dom = new csapiXmlParser().stringToXml(xml);
		if ( !dom ) { return; }
		var fields = dom.getElementsByTagName('cs_field');
		var name;
		var val;
		for (var i=0;i<fields.length;i++) {
			name = fields[i].getAttribute("name");
			val = "";
			for (var j=0; j<fields[i].childNodes.length;j++) {
        if (fields[i].childNodes[j].nodeType == 4) {
        	val = fields[i].childNodes[j].nodeValue;
        }
      }
			switch(name) {
				case 'custrecord_pref_sol_fb_dflt_comments'	:
					this.default_comment = val;
					break;
				case 'custrecord_pref_sol_fb_form_template'	:
					this.form_html = val;
					break;
				case 'custrecord_pref_sol_fb_form_url'			:
					this.action_url = val;
					break;
			}
		}
		// set form html variables
		this.set_form_variables();
	}
	
	this.parse_text = function(txt) {
		if ( vyIsEmpty(txt) || txt === false ) { return; }
		var fields = txt.split('~~|~~');
		this.default_comment = "";
		this.action_url = "";
		this.form_html = "";
		var nv; var pos;
		for (var i=0;i<fields.length;i++) {
			if ( (pos=fields[i].indexOf('=')) < 0 ) { continue; }
			nv = new Array(fields[i].substring(0,pos),fields[i].substring(pos+1));
			if ( nv.length < 2 ) { continue; }
			switch(nv[0]) {
				case 'custrecord_pref_sol_fb_dflt_comments'	:
					this.default_comment = nv[1];
					break;
				case 'custrecord_pref_sol_fb_form_template'	:
					this.form_html = nv[1];
					break;
				case 'custrecord_pref_sol_fb_form_url'			:
					alert('pos = '+pos+"\nval = "+nv[1]);
					this.action_url = nv[1];
					break;
			}
		}
		// set form html variables
		this.set_form_variables();
	}
	
	this.set_form_variables = function() {
		// replace valid fields with current values
		var id = vySite.context.get_item_id();
		if ( vyIsEmpty(id) ) { id = vySite.context.get_query('id'); }
		this.form_html = this.form_html.replace('<VYINSERT_CUSTRECORD_SOL_FB_DFLT_COMMENTS/>',this.default_comment);
		this.form_html = this.form_html.replace('<VYINSERT_CUSTRECORD_SOL_FB_SOLUTION_ID/>',id);
		this.form_html = this.form_html.replace('<VYINSERT_CUSTRECORD_SOL_FB_CUSTOMER_ID/>',vySite.context.get_customer_id());
		this.form_html = this.form_html.replace('<VYINSERT_CUSTRECORD_SOL_FB_FORM_URL/>',this.action_url);
		this.form_html = this.form_html.replace('<VYINSERT_REFERRER_URL/>',vySite.context.get_current_url());
	}
	
	/* run - execute */
	this.execute(_this);
}

/**
 * @class Associative Array object. Use similar to array accept allows
 * text indexes.
 * @example:<code>
 * // setting up an empty associative array and then populating
 * var arr = new vyAssocArray();
 * arr["myindex"] = "myvalue";
 * arr["mynextindex"] = "mynextval";
 * var cnt = arr.length();	// returns length/count. This example returns 2.
 * var cnt2 = arr.count();	// returns length/count. This example returns 2.
 * var indexSet = arr.isset("myindex");	// returns boolean true.
 * var indexSet = arr.isset("notindex");	// returns boolean false.
 * <br/><br/>
 * // setting up an associative array and adding indexes at the same time.
 * // note the =&gt; sign used to separate the index from the value all in
 * // a single string.
 * var arr = new vyAssocArray("myindex=>myvalue","mynextindex=>mynextval");
 * var cnt = arr.length();	// returns length/count. This example returns 2.
 * var cnt2 = arr.count();	// returns length/count. This example returns 2.
 * var indexSet = arr.isset("myindex");	// returns boolean true.
 * var indexSet = arr.isset("notindex");	// returns boolean false.
 * </code>
 * @constructor
 * Creates a new Associative Array object
 * @return {Object} Returns an vyAssocArray object
 */
function vyAssocArray() {
	var _functions = "|length|count|getIndexName|getIndexValue|isset|unset|join|explode|";
	/**
	 * Returns number of indexes for this Associative Array object.
	 * Identical to {@link vyAssocArray.count()}. Note this is a method "()" not a property.
	 * @return {Integer} Returns 0 if empty, otherwise the number of indexes.
	 * @see vyAssocArray.count()
	 */
	this.length = function() {
		var idx;
		var cnt = 0-8;	// removes the other methods and properties from count
		for (var idx in this) { cnt += 1; }
		return (cnt<=0?0:cnt);
	}
	/**
	 * Returns the index name at the position specified. If position is out
	 * of range, then null is returned.
	 * @param {Integer} pos Zero-based position of the name to return
	 * @return {String} Returns the name at the index position specified or null
	 * if out of range.
	 */
	this.getIndexName = function(pos) {
		var i = 0;
		if ( pos >= this.length() || pos < 0 ) { return null; }
		for (var idx in this) {
			if ( _functions.indexOf('|'+idx+'|') >= 0 ) { continue; }
			if (i++ == pos) { return idx; }
		}
	}
	/**
	 * Returns the index value at the position specified. If position is out
	 * of range, then null is returned.
	 * @param {Integer} pos Zero-based position of the name to return
	 * @return {String} Returns the value at the index position specified or null
	 * if out of range.
	 */
	this.getIndexValue = function(pos) {
		var i = 0;
		if ( pos >= this.length() || pos < 0 ) { return null; }
		for (var idx in this) {
			if ( _functions.indexOf('|'+idx+'|') >= 0 ) { continue; }
			if (i++ == pos) { return this[idx]; }
		}
	}
	/**
	 * Returns number of indexes for this Associative Array object.
	 * Identical to {@link vyAssocArray.length()}. Note this is a method "()" not a property.
	 * @return {Integer} Returns 0 if empty, otherwise the number of indexes.
	 * @see vyAssocArray.length()
	 */
	this.count = function() { return this.length(); }
	/**
	 * Identifies if a particular index has been set in the object.
	 * @param {String} indextext The index text value of the associative array
	 * to test.
	 * @return {Boolean} Returns true if exists, otherwise false.
	 */
	this.isset = function(indextext) {
		if ( indextext == null || indextext == "" ) { return false; }
		return (eval("typeof(this."+indextext+")")=='undefined'?false:true);
	}
	/**
	 * Removes an existing index from the associative array.
	 * @param {String} indextext The index text value of the associative array
	 * to remove/unset.
	 * @return {void}
	 */
	this.unset = function(indextext) {
		if ( this.isset(indextext) ) { eval("delete this."+indextext+";"); }
	}
	/**
	 * Returns a string separated by param::separator, or if empty separated by comma,
	 * with the values shown as name=&gt;value.
	 * @param {String} separator <i>optional</i> the char or chars used to separate
	 * each name/value pair. If not provided, comma is used.
	 * @return {String} Returns the delimited string with name/value pairs between delimiter
	 * in the form name=&gt;value.
	 */
	this.join = function() {
		var sep = (this.join.arguments.length>0?this.join.arguments[0]:",");
		var str = "";
		for (var idx in this) {
			if ( _functions.indexOf('|'+idx+'|') >= 0 ) { continue; }
			str += (str.length==0?"":sep)+idx+"=>"+this[idx];
		}
		return str;
	}
	/**
	 * Returns an array of name/value pairs in the format of name=&gt;value for each element.
	 * @return {Array} Returns an array of name/value pairs in format: name=&gt;value
	 */
	this.explode = function() {
		var arr = new Array();
		for (var idx in this) {
			if ( _functions.indexOf('|'+idx+'|') >= 0 ) { continue; }
			arr[arr.length] = new Array(idx,this[idx]);
		}
		return str;
	}
	/*
	 * Used to set indexes passed in as parameters
	 * during constructor stage. Note that these parameters must
	 * use the =&gt; as a separator of the index and value. If
	 * this separator is not present, the whole string is set as
	 * the index and the value is set to null.
	 */
	for(var i=0; i<vyAssocArray.arguments.length; i++) {
		var sp = vyAssocArray.arguments[i];
		var s = sp.split("=>");
		if ( s.length == 1 ) { s[1] = null; }
		this[s[0]] = s[1];
	}
}	// end vyAssocArray

/**
 * @class csapiXmlParser
 * Used for cross-platform parsing of text to XML
 */
function csapiXmlParser() {
	this.xml_string;
	this.xmlDom;

	this.stringToXml = function(str) {
		this.xml_string = str;
		if ( !this.isXmlString() ) { return null; }
		try {
			// IE test
			this.xmlDom = new ActiveXObject("Microsoft.XMLDOM");
			this.xmlDom.async = "false";
			this.xmlDom.loadXML(this.xml_string);
		} catch(e) {
			try {
				// other browsers
				var parser = new DOMParser();
				this.xmlDom = parser.parseFromString(this.xml_string,"text/xml");
			} catch(e) { this.xmlDom = false; }
		}
		return this.xmlDom;
	}
	
	this.isXmlString = function() {
		if ( this.xml_string == null || this.xml_string == "" ) { return false; }
		var pos;
		if ( (pos=this.xml_string.indexOf('<?xml')) >= 0 ) { return true; }
		return false;
	}
}
/**
 * @class General XML AJAX class
 */
function csapiHttpClient() {
	this.error;
	this.request;
	this.response;
	this.isIE;
	this.isWC3;
	/* util functions */
	this.empty = function(v) { return (v==null||v==""?true:false); }
	this.clear_error = function() { this.error = false; }
	this.is_error = function() { return (!this.empty(this.error)?true:false); }
	this.get_error_message = function() { return this.error; }
	
	this.initialize = function() {
		this.error = null;
		this.request = null;
		this.response = null;
		this.isIE = false;
		this.isWC3 = false;
	}
	
	this.setHttpRequest = function() {
		this.clear_error();
		this.request = null;
		this.isIE = false;
		this.isWC3 = false;
		try {
			this.request = new XMLHttpRequest();
			this.isWC3 = true;
		} catch(e) {
			var ieObjs = new Array('MSXML2.XMLHTTP.5.0','MSXML2.XMLHTTP.4.0','MSXML2.XMLHTTP.3.0','MSXML2.XMLHTTP','Microsoft.XMLHTTP');
			for(var i=0; i<ieObjs.length; i++) {
				try {
					this.request = new ActiveXObject(ieObjs[i]);
					i = ieObjs.length;
					this.isIE = true;
				} catch(e) {}
			}
		}
		// set readystate function
		if ( this.request ) {
			var _self = this;
			this.request.onreadystatechange = function() { _self.stateChanged() }
		} else {
			this.error = "Could not establish connection";
		}
		return (!this.request?false:true);
	}
	
	this.stateChanged = function() {
		switch(this.request.readyState) {
			case 1:	// The request has been set up
				break;
			case 2: // The request has been sent
				break;
			case 3: // The request is in process
				break;
			case 4: // The request is complete
				// check to make sure it is good data
				if ( this.request.status == 200 ) {
					this.response = this.request.responseText;
					this.error = false;
				} else {
					this.response = false;
					this.error = "An error was generated while processing the request ["+this.request.status+"] - "+this.request.statusText+".";
				}
				break;
		}
	}
	
	this.get_response = function() { return this.response; }
	/**
	 * @param {String} url The URL to get or post
	 * @param {String} [method] Pass either GET or POST. If not set, the GET is assumed.
	 * @param {String} [postData] String of data to POST. Valid only for POSTs and ignored
	 * otherwise.
	 * @param {Boolean} [async] Asynchronous or Synchronous call. Defaults to Asynchronous.
	 * @param {Boolean} [getXml] Set to true to return the response as an XML object.
	 * Default is false.
	 * @return {String|DOMNode} Returns the response text or a document object if
	 * XML requested.
	 * Check {@link this.is_error()} for an error message
	 */
	this.make_request = function(url) {
		this.response = false;
		this.clear_error();
		if ( this.empty(url) ) {
			this.error = "URL is required and missing.";
			return false;
		}
		var _args = this.make_request.arguments;
		var method = (_args.length>1&&_args[1]!=null&&_args[1].toLowerCase()=='post'?"POST":"GET");
		var post_data = (method=="POST"&&_args.length>2?_args[2]:'');
		var async = (_args.length>3&&_args[3]===false?false:true);
		var getXml = (_args.length>4&&_args[4]===true?true:false);
		if ( !this.setHttpRequest() ) { return false; }
		if ( !this.process_request(url,method,post_data,async,getXml) ) { return false; }
		if ( async ) { return true; }
		return this.response;
	}
	
	this.process_request = function(url,method,data,async,getXml) {
		this.request.open(method,url,async);
		if ( !data ) { data = ""; }
		if ( method == "POST" ) {
			this.request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			this.request.setRequestHeader("Content-length", data.length);
			this.request.setRequestHeader("Connection", "close");
		} else {
			data = null;
		}
		this.request.send(data);
		if ( !this.isIE && !async && this.request.onreadystatechange == null ) {
			// for Firefox bug when FireBug is not installed
			this.stateChanged();
		}
		return true;
	}

	// init call
	this.initialize();
	
}	// end general XML AJAX class
