/*
function errorHandler( msg, url, line ) {
	alert( 'Error: ' + msg + '\nURL: ' + url + '\nLine: ' + line );
	return true;
}
onerror = errorHandler;
*/
/*extern KIC, AjaxUpdater, dhtmlHistory, tinyMCE  */

///////////////////////////////////////////////////////////////////////////
// CONSTANTS
///////////////////////////////////////////////////////////////////////////
var PRINT_FEATURES = 'height=400,width=600,scrollbars=yes,resizable=yes';
var WEBPREVIEW_FEATURES = 'height=400,width=600,scrollbars=yes,resizable=yes';
var CSRPREVIEW_FEATURES = 'height=400,width=600,scrollbars=yes,resizable=yes';
var FILE_FEATURES = 'height=400,width=600,scrollbars=yes,resizable=yes';

var DATE_DISPLAY_FORMAT = "%m/%d/%Y";
var DATETIME_DISPLAY_FORMAT = "%m/%d/%Y %I:%M %p";

var SECTION_DIVIDER='###SECTION_DIVIDER###';

//////////
//	debugging/logging functions
//////////
var show_debug_div = false;			// display debug div
var debug_add_to_queue = false;		// save a list of debug messages/objects
var debug_request_objects = false;	// debug JSON request objects
var debug_response_objects = false;	// debug JSON response objects

var debug_queue = [];				// store array of messages sent to debug() or debugObj() 
									// to make investigating errors easier

// turn on all debugging options
function dev_options() {
	show_debug_div = true;
	debug_add_to_queue = true;
	debug_request_objects = true;
	debug_response_objects = true;
}

function debug( msg ) { 
	// save to queue
	if (debug_add_to_queue) {
		debug_queue.push( { call: 'debug', value: msg } );
	}

	// log to Firebug
	try { 
		console.log(msg); 
	} catch (e) { 
	} 

	// if option is turned on, display message in a <div>
	// this is intended for development only, so the option
	// is set to false in production.  It can be turned on
	// in FireFox using the Firebug command line.
	var debug_div, debug_container, t;
	if (show_debug_div) {
		debug_div = $('debug'); 
		if (debug_div) { 
			debug_container = $('debug_container');
			if (debug_container) {
				debug_container.style.display = '';
			} else {
				debug_div.style.display = '';
			}
			// append message to other messages present
			t = debug_div.innerHTML;
			debug_div.innerHTML = ( t + msg + '<br>' );
		}
	}
}

function debugObj( obj ) {
	// save to queue
	if (debug_add_to_queue) {
		debug_queue.push( { call: 'debugObj', value: KIC.utils.cloneObject(obj) } );
	}

	// log to Firebug
	try { 
		console.dir(obj); 
	} catch (e) { 
	} 
}

//////////
//	general purpose functions
//////////
function $(id) { return document.getElementById(id); }

function swap( a, b ) {
	var c = a;
	a = b;
	b = c;
}

/////////////////////////////////////////////////////////////////////
//	get or set the value of a form input field
/////////////////////////////////////////////////////////////////////
function getSelectOption( id ) {
	var elem = $(id);
	if (elem) {
		if (elem.selectedIndex >= 0 && elem.selectedIndex < elem.options.length) {
			return elem.options[ elem.selectedIndex ];
		}
	}
	return null;
}
function getSelectValue( id ) {
	var sel_option = getSelectOption(id);
	if (sel_option) {
		return sel_option.value;
	}
}

function setSelectValue( id, val ) {
	var select = $(id);
	for ( var i = 0; i < select.options.length; i++ ) {
		if ( select.options[i].value == val ) {
			select.selectedIndex = i;
			return;
		}
	}
}

function getCheckboxListValues( id ) {
	var i = 0;
	var list = [];
	while ( $(id+'_'+i) ) {
		if ( $(id+'_'+i).checked ) {
			list.push( $(id+'_'+i).value );
		}
		i++;
	}

	return list;
}

function setCheckboxListValues( id, vals ) {
	var i = 0;
	while ( $(id+'_'+i) ) {
		$(id+'_'+i).checked = false;
		i++;
	}
	for ( i = 0; i < vals.length; i++ ) {
		var val = vals[i];
		var j = 0;
		while ( $(id+'_'+j) ) {
			if ( $(id+'_'+j).value == val ) {
				$(id+'_'+j).checked = true;
			}
			j++;
		}
	}
}

function getRadioListValue( id ) {
	var i = 0;
	var elem;
	while ( $(id+'_'+i) ) {
		elem = $(id+'_'+i);
		if ( elem.checked ) {
			return elem.value;
		}
		i++;
	}

	return '';
}

function setRadioListValue( id, val ) {
	var i = 0;
	var elem;
	while ( $(id+'_'+i) ) {
		elem = $(id+'_'+i);
		elem.checked = (elem.value == val ? true : false);
		i++;
	}
}

function clearRadioList( id ) {
	var i = 0;
	var elem;
	while ( $(id+'_'+i) ) {
		elem = $(id+'_'+i);
		elem.checked = false;
		i++;
	}
}

function getTinyMCEValue( id, foo ) {
	var editor = KIC.UI.getEditorInstance( id );
	if ( editor && !editor.isHidden() ) {
		return editor.getContent();
	}
	else {
		var elem = $(id);
		if (typeof elem !== "undefined") {
			return elem.value;
		}
	}
//	throw "TinyMCE editor instance undefined or not enabled";
}

function setTinyMCEValue( id, val ) {
	var editor = KIC.UI.getEditorInstance(id);
	if ( editor ) {
		editor.setContent(val);
	}
	else {
		$(id).value = val;
	}
}

//////////
//	array functions
//////////
function arrayIntersection( a, b ) {
	var list = [];
	var i, j;
	var found = false;
	for ( i = 0; i < a.length; i++ ) {
		for ( j = 0; j < b.length; j++ ) {
			if ( a[i] == b[j] ) {
				found = true;
			}
		}
		if ( !found ) {
			list.push( a[i] );
		}
	}
	for ( i = 0; i < b.length; i++ ) {
		for ( j = 0; j < a.length; j++ ) {
			if ( b[i] == a[j] ) {
				found = true;
			}
		}
		if ( !found ) {
			list.push( b[i] );
		}
	}

	return list;
}

function contains( list, x ) {
	if (list) {
		for ( var i = 0; i < list.length; i++ ) {
			if ( list[i] == x ) {
				return true;
			}
		}
	}
	return false;
}

//////////
//	string functions
//////////
function ifnull( x ) {
	if ( x === null ) {
		return '';
	}
	return x;
}

//////////
//	Date timezone functions
//////////

// UTC to local
function getLocalDate( d ) {
	if (d === null || d === undefined) {
		return d;
	}
	return new Date( d.valueOf() - (d.getTimezoneOffset()*60*1000) );
}

// local to UTC
function getAdjustedDate( d ) {
	if (d === null || d === undefined) {
		return d;
	}
	return new Date( d.valueOf() + (d.getTimezoneOffset()*60*1000) );
}

function truncateDate( d ) {
	d.setHours( 0, 0, 0, 0 ); // set hours, minutes, seconds, and ms to zero
}

/////////////////////////////////////////////////////////////////////
//	KIC UI SelectorNav
//		check the status of a field; 
//		if it has data, section.IsPopulated = true;
//		if it's data has changed, section.IsEdited = true;
/////////////////////////////////////////////////////////////////////
function checkEditedText( section, id, value ) {
	var content = $(id).value;
	content = content.replace(/\r/g,'');
	if ( content !== '' ) {
		section.IsPopulated = true;
	}
	var s_value = new String(ifnull(value));		// jslint complains, just ignore
													// must use, otherwise calling .replace fails 9or is it .valueOf?)
	s_value = s_value.replace(/\r/g,'');
	if ( content != s_value.valueOf() ) {
		section.IsEdited = true;
	}
}

/*
 *	NOTE: checkEditedTinyMCE is very slow in IE when the text in tinyMCE editor is very long.
 *		[for example, Script - Minor Ingredients]
 *	This is because it gets called each keypress, and does some cleaning up of bad html.
 *
 *	I tried making a tinyMCE.getContentQuick function that bypassed that, and then doing 
 *	some more basic cleanup, but that didn't seem to be worth the effort.
 */
function checkEditedTinyMCE( section, id, value ) {
	// check first that this editor has been enabled
	var editor = KIC.UI.getEditorInstance(id);
	if ( editor && !editor.isHidden() ) {		// no enabled flag in newer tinyMCE versions
		var content = editor.getContent();
		if ( content !== '' ) {
			section.IsPopulated = true;
		}
		// tinyMCE editor compresses whitespace, so compensate for that
		value = ifnull(value).replace(/\s+/g, " ");
		content = ifnull(content).replace(/\s+/g, " ");
		// could possibly handle <em> and <strong> tags here too
		if ( value != content ) {
			section.IsEdited = true;
		}
	}
	else {
		checkEditedText( section, id, value );
	}
}

function checkEditedCheckboxList( section, id, values ) {
	var i = 0;
	var elem;
	while ( $(id+'_'+i) ) {
		elem = $(id+'_'+i);
		if ( elem.checked ) {
			section.IsPopulated = true;
			if ( !contains(values, elem.value) ) {
				section.IsEdited = true;
				return;
			}
		}
		else {
			if ( contains(values, elem.value) ) {
				section.IsEdited = true;
				return;
			}
		}
		i++;
	}
}

function checkEditedSelect( section, id, value ) {
	var content = getSelectValue(id);
	if ( content !== '' ) {
		section.IsPopulated = true;
	}
	if ( content != ifnull(value) ) {
		section.IsEdited = true;
	}
}

function checkEditedNonZeroInt( section, id, value ) {
	var content = $(id).value;
	if ( content !== "" &&  content !== "0" && content !== 0 ) {
		section.IsPopulated = true;
	}
	if ( content != ifnull(value) ) {
		section.IsEdited = true;
	}
}

function checkEditedRadioList( section, id, value ) {
	var content = getRadioListValue(id);
	if ( content !== '' ) {
		section.IsPopulated = true;
	}
	if ( content != ifnull(value) ) {
		section.IsEdited = true;
	}
}

// not same as checkEditedDate anymore
function checkEditedDateTime( section, id, dateObj ) {
	var wasEdited = section.IsEdited;
	var elem = $(id);
	if (elem) {
		if ( elem.value !== '' ) {
			section.IsPopulated = true;
		}
		if ( typeof dateObj === "undefined" || dateObj === null || dateObj === '' ) {
			if ( elem.value !== '' && elem.value !== undefined ) {
				section.IsEdited = true;
			}
		}
		else {
			// compare dates as milliseconds since reference date
			if ( Date.parse(elem.value) != dateObj.valueOf() ) {
				section.IsEdited = true;
//				debug( 'IsEdited = true' );
			}
		}
	}
	else {
//		debug("Unknown element id in checkEditedDate: " + id);
	}
}

// adjust date before calling checkEditedDateTime
function checkEditedDate( section, id, dateObj ) {
	checkEditedDateTime( section, id, getAdjustedDate(dateObj) );
}

/* this shouldn't be needed any more */
function checkEditedDateString( section, id, value ) {
	var formValue = $(id).value;
	if ( formValue !== '' ) {
		section.IsPopulated = true;
		if ( value === '' || Date.parse(formValue) != Date.parse(value) ) {
			section.IsEdited = true;
		}
	}
	else {
		if ( ifnull(value) !== '' ) {
			section.IsEdited = true;
		}
	}
}

/*
 *	a function to check whether an association list has changed from the DB values
 *	takes the section to check and a tuple in the form (listManager, tableManager, hiddenFormField)
 */
function checkEditedAssociation( section, asscManagerTuple )
{
	// tuple is [ObjectListManager, TableManager]
	if (asscManagerTuple)
	{
		var listManager = asscManagerTuple[0];
		
		section.IsEdited = listManager.hasChanged();
		if (listManager.count() > 0) {
			section.IsPopulated = true;
		}
	}
}

//////////
//	tinyMCE-related methods
//////////
function exceedsTinyMCELength(id, len)
{
	var content = getTinyMCEValue(id);
	if (content === null) {
		return false;
	}
	if (content.length <= len) {
		return false;
	}
	return true;
}

function editorReady( id ) {
	var editor = KIC.UI.getEditorInstance(id);
	if (editor && editor._isFinishedLoading) {
		return true;
	} else {
		return false;
	}
}

//////////
//	get_form_params
//		returns array of objects with id/value pair
//////////
function get_form_params( form )
{
	var params_list = [];	// new Array
	var i, element, id, param;
	
	for ( i = 0; i < form.elements.length; i++ ) 
	{
		element = form.elements[i];
		id = element.name;
		if (id === "") {
			id = element.id;
		}
		if (id === "__EVENTVALIDATION" || id === "__VIEWSTATE") {
			// skip the ASP.Net created form fields
			continue;
		}
				
		if ( element.type === 'text' || element.type === 'hidden' || element.type === 'textarea' ) 
		{
			
			params_list.push( { 'id': id, 'value': element.value } );
		} 
		else if ( element.type === 'select-multiple' || element.type === 'select-one' ) 
		{
			for ( var j = 0; j < element.options.length; j++ ) 
			{
				var option = element.options[j];
				if ( option.selected ) 
				{
					params_list.push( { 'id': id, 'value': option.value } );
				}
			}
		} 
		else if ( element.type === 'submit' || element.type === 'reset' || element.type === 'button' ) 
		{
			// ignore
		}
		else if ( element.type === 'checkbox' || element.type === 'radio') 
		{
			if (element.checked) {
				params_list.push( { 'id': id, 'value': element.value } );
			}
		}
		else
		{
			alert( "Don't know what to do with " + element.type );
			return false;
		}
	}

	return params_list;
}

function format_form_params( form, additional_params )
{
	var param_list = [];
	var param_object_list = get_form_params(form);
	var i, param, id, val;

	for (i = 0; i < param_object_list.length; i++)
	{
		param = param_object_list[i];
		id = param.id;
		val = param.value;
		param_list.push(encodeURIComponent(id) + '=' + encodeURIComponent(val));
	}

	// add additional parameters from another source
	if (additional_params && additional_params !== "") {
		param_list.push(additional_params);
	}
	
	return param_list.join('&');
}

/*
	convert a form into a POST request
 */
function submit_ajax( form, str_url, func_handler, additional_params ) 
{
	if (!str_url) {
		// url not provided, use 'action' attribute from form
		str_url   = form.attributes.action.value;
	}

	// get parameters as "id=value&id2=value2" string
	var str_param = format_form_params( form, additional_params );
	
	// make request
	AjaxUpdater.updateWithPost( str_url, str_param, func_handler );

	// return false so that we don't switch web pages
	return false;
}

// modified from http://onlinetools.org/articles/unobtrusivejavascript/cssjsseparation.html
function css(elem, action, classname, classname2)
{
	if (action === 'swap') {
		elem.className=!css(elem,'check',classname)? elem.className.replace(classname2,classname) : elem.className.replace(classname,classname2);
	} else if (action === 'add') {
		if(!css(elem,'check',classname)){elem.className+=elem.className?' '+classname:classname;}
	} else if (action === 'remove') {
		var rep=elem.className.match(' '+classname)?' '+classname:classname;
		elem.className=elem.className.replace(rep,'');
	} else if (action === 'check') {
		return new RegExp('\\b'+classname+'\\b').test(elem.className);
	}
}

// returns string with all property name/value pairs
// used for debugging
function dump_props(obj, obj_name) {
	var result = "";
	if (obj_name) {
		result = obj_name + " = [\n<br>\n";
	}

	var functions  = [];
	var properties = [];
		
	for (var i in obj) {
		if ((typeof obj[i]) === 'function') {
			functions.push(i);
		} else {
			properties.push(i);
		}
	}
	properties.sort();
	functions.sort();

	// print out properties
	for (i = 0; i < properties.length; i++) {
		var property_name = properties[i];
		result += "\t" + property_name + " = " + obj[property_name] + " (" + typeof obj[property_name] + ")<br>\n";
	}

	// print out functions
	for (i = 0; i < functions.length; i++) {
		var function_name = functions[i];
		result += "\t" + function_name + " = " + obj[function_name] + "<br>\n";
	}

	// print closing bracket
	if (obj_name) {
		result += "]\n";
	}

	return result;
}

/////////////////////////////////////////////////////////////////////
//	Clean up objects so they are more suitable for sending in a JSON request
/////////////////////////////////////////////////////////////////////

// opposite of ifnull(x) function
function emptyStringToNull( text ) {
	return text === '' ? null : text;
}

function stringToNullableDate( text ) {
	if ( text === '' ) {
		return null;
	}
	return new Date( text );
}

function stringToNullableDateAdjusted( text ) {
	return getAdjustedDate( stringToNullableDate( text ) );
}

function numberToBoolean( num ) {
	// use strict equals [===], because using regular equals converts '' to 0
	if (num === '') { return null; }
	if (num === 0 || num === '0') { return false; }
	if (num === 1 || num === '1') { return true; }
	// default to null (for nullable boolean values)
	return null;
}

function stopEventPropagation(event)
{
	if (typeof event === "undefined") {
		return;
	}
	if (event.stopPropagation) {
		// W3 standard
		// see http://developer.mozilla.org/en/docs/DOM:event.stopPropagation
		 event.stopPropagation();
	} else {
		// IE
		event.cancelBubble = true;
	}
}

function cloneSelectOption( option ) {
	return new Option(
		option.text,
		option.value,
		option.defaultSelected,
		option.selected );
}

function sortSelect( select ) {
	var i;
	var temp = []; // new array new Array( select.options.length );
	for ( i = 0; i < select.options.length; i++ ) {
		temp[i] = cloneSelectOption( select.options[i] );
	}
	temp.sort( function( a, b ) {
		var x = a.text.toUpperCase();
		var y = b.text.toUpperCase();
		return ((x < y) ? -1 : ((x > y) ? 1 : 0));
	} );
	select.options.length = 0;
	for ( i = 0; i < temp.length; i++ ) {
		select.options[i] = temp[i];
	}
}

function deleteSelectOption( select, value ) {
	var newOptions = [];
	var i = 0;
	for ( var j = 0; j < select.options.length; j++ ) {
		if ( select.options[j].value != value ) {
			newOptions.push( cloneSelectOption(select.options[j]) );
		}
	}
	select.options.length = 0;
	for ( i = 0; i < newOptions.length; i++ ) {
		select.options[i] = newOptions[i];
	}
}

function fileSize( size ) {
	var sizeLabels = [' Bytes', ' KiB', ' MiB', ' GiB']; 
	// followed by terabyte TB, petabyte PB (or tebibyte TiB, pebibyte PiB); 
	// if someone is uploading more that a gigabyte, there will be major db issues

	var sizeIndex = 0;
	while (size > 1024 && sizeIndex < 3) {
		sizeIndex++;
		size = Math.round(size/1024);
	}
	return (size + sizeLabels[sizeIndex]);
}

// this version is for Session related JSON services (login, reset password)
function parseJSON( strObject ) {
	return eval( '(' + strObject + ')' );		// evil but necessary
	// can't use String's parseJSON() method because the NewtonSoft JSON.Net library 
	// returns dates like this => "StatusWhen": new Date(1107388800000)
}

// get the response object, and immediately check the LoggedIn flag
function parseJSONWithLogin( strObject ) {
	var response = parseJSON(strObject);

	// for development/debugging
	if (debug_response_objects) {
		debugObj(response);
	}
	
	// check for login status set to true
	if (response.LoggedIn !== true) {
		var from = dhtmlHistory.getCurrentLocation();
		KIC.UI.showError(response.Message, function() { window.location = "/Login.aspx?nav=" + from; } );
	}
	return response;
}

//
// Attempt to eliminate source of memory leaks in IE
// see: http://javascript.crockford.com/memory/leak.html
// doesn't work fast enough to use
//
function purge(d) {
    var a = d.attributes, i, l, n;
    if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            n = a[i].name;
            if (typeof d[n] === 'function') {
                d[n] = null;
            }
        }
    }
    a = d.childNodes;
    if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            purge(d.childNodes[i]);
        }
    }
}

function setInnerHTML( elem, text ) {
	if (elem) {
//		debug("purging " + elem.Id);
//		purge(elem);
		if (text) {
			elem.innerHTML = text;
		} else {
			elem.innerHTML = "";
		}
	}
}

// new functions get added to KIC.utils namespace
if(typeof KIC       === "undefined") { var KIC = {}; }
if(typeof KIC.utils === "undefined") { KIC.utils = {}; }

KIC.utils.cloneObject = function(x) {
	var p;		// property
	var n = {};	// new object
	
	// what to do if x is an array, string, function, etc?
	if (typeof x === "object")
	{
		for (p in x) if (x.hasOwnProperty(p)) {
			n[p] = x[p];
		}
	}
	return n;
};

KIC.utils.resetDateString = function(id, dateObj)
{
	if (dateObj) {
		$(id).innerHTML = dateObj.toDateString();
	}
};

KIC.utils.getFrameContent = function(frame)
{
	// frame document is different for IE vs. FF
	var doc = (frame.contentWindow ? frame.contentWindow.document : frame.contentDocument);
	var text;
	if (doc.body.firstChild.tagName === "PRE") {
		// both IE and FireFox wrap the JSON text in a <pre> tag
		text = doc.body.firstChild.innerHTML;
	} else {
		text = doc.body.innerHTML;
	}
	//debugObj(doc.body);
	//debug(text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g,"&gt;"));
	//debug(doc.body.textContent);
	return text;
};

