/******************************************************************************
@owner: SVSi
@author: Arthur Johnson
@functions: Various Utility Functions and Fillers/Shims
@noop
@v3
@undef
@clamp
@Object.create
@Array.isArray
@arrayContains
@removeFromArray
@cancelEvent
@getQueryStrings
@allDescendantsItemChild
@getAabb
@aabbOverlap
@runTimeout
@runInterval
@childOf
@deleteElementById
@addEventHandler
@bindToEventHandler
@exportPNG
*******************************************************************************/

var blankimg = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
var allhex = 
'\
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f \
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f \
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f \
30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f \
40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f \
50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f \
60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f \
70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f \
80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f \
90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f \
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af \
b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf \
c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf \
d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df \
e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef \
f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff \
';

(function($){
	$.fn.exists = function(){
		return this.length !== 0;
	};
	$.fn.outerHTML = function(){
		return $(this).clone().wrap('<div></div>').parent().html();
	};
	$.fn.center = function(){
		this.css("position","absolute");
		this.css("top", Math.max(0, 
			((window.innerHeight - $(this).outerHeight()) / 2) + $(window).scrollTop()) + "px");
		this.css("left", Math.max(0, 
			((window.innerWidth - $(this).outerWidth()) / 2) + $(window).scrollLeft()) + "px");
		return this;
	};
	$.fn.cssAnim = function(anim, callback, time){
		for (var i=0; i<this.length; i++){
			if ($(this[i]).hasClass('animated')){
				$(this[i]).removeClass('animated');
				$(this[i]).off('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend');
			}
			$(this[i]).addClass('animated '+anim);
			if (time) $(this[i]).css({
				'-webkit-animation-duration': time+'s',
				'animation-duration': time+'s'
			});
			if (i==0) $(this[i]).one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', 
				function(){
					$(this).removeClass('animated '+anim);
					if (callback) callback.call(this);
					callback = false;
				}.bind(this[i]));
		}
	};
	$.fn.textfill = function(maxFontSize){
		maxFontSize = -1;
		return this.each(function(){
			var ourText = $("span", this),
			maxHeight = parseInt(parent.style.height),
			maxWidth = parseInt(parent.style.width),
			fontSize = parseInt(ourText.css("fontSize"), 10),
			multiplier = maxWidth/ourText.width(),
			newSize = (fontSize*(multiplier-0.1));
			ourText.css("fontSize",
				(maxFontSize > 0 && newSize > maxFontSize) ? maxFontSize : newSize);
		})
	};
	$.fn.textfill2 = function(options){
        var fontSize = options.maxFontPixels;
        var padding = options.hasOwnProperty('padding') ? options.padding : 0;
        var ourText = $('span:visible:first', this);
        var maxHeight = $(this).height();
        var maxWidth = $(this).width();
        var textHeight;
        var textWidth;
        do {
            ourText.css('font-size', fontSize);
            textHeight = ourText.height();
            textWidth = ourText.width();
            fontSize = fontSize - 1;
        } while ((textHeight > maxHeight - padding || textWidth > maxWidth - padding) && fontSize > 3);
        return this;
    };
	$.fn.hasScroll = function(axis){
		var overflow = this.css("overflow"),
			overflowAxis;

		if(typeof axis == "undefined" || axis == "y") overflowAxis = this.css("overflow-y");
		else overflowAxis = this.css("overflow-x");

		var bShouldScroll = this.get(0).scrollHeight > this.innerHeight();

		var bAllowedScroll = (overflow == "auto" || overflow == "visible") ||
							 (overflowAxis == "auto" || overflowAxis == "visible");

		var bOverrideScroll = overflow == "scroll" || overflowAxis == "scroll";

		return (bShouldScroll && bAllowedScroll) || bOverrideScroll;
	};
})(jQuery);

function time(){
	return Math.round(new Date().getTime() / 1000);
}

function millitime(){
	return new Date().getTime();
}

function randomString(length){
    var text = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    for(var i=0; i < length; i++)
        text += possible.charAt(Math.floor(Math.random()*possible.length));
    return text;
}

function nvrStatus(ip, onComplete){
	if (!ip || !onComplete) return;
	websocket.emit('message', {command: 'request', request: 'get', 
		url: 'http://'+ip+'/nseriesGetParams.php'}, onComplete);
}

function xmlToJson(xml){
	// Create the return object
	var obj = {};
	if (xml.nodeType == 1){ // element
		// do attributes
		if (xml.attributes.length > 0){
		obj["@attributes"] = {};
			for (var j = 0; j < xml.attributes.length; j++){
				var attribute = xml.attributes.item(j);
				obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
			}
		}
	} else if (xml.nodeType == 3){ // text
		obj = xml.nodeValue;
	}
	// do children
	if (xml.hasChildNodes()){
		for(var i = 0; i < xml.childNodes.length; i++){
			var item = xml.childNodes.item(i);
			var nodeName = item.nodeName;
			if (typeof(obj[nodeName]) == "undefined"){
				obj[nodeName] = xmlToJson(item);
			} else {
				if (typeof(obj[nodeName].push) == "undefined"){
					var old = obj[nodeName];
					obj[nodeName] = [];
					obj[nodeName].push(old);
				}
				obj[nodeName].push(xmlToJson(item));
			}
		}
	}
	return obj;
};

function getStatusOfUnit(obj, onComplete, delay){
	tcpclient(obj.ip, obj.port, asciiToHex('getStatus\r'), function(data){
		if (data == 'Socket Error!'){
			console.log('Get Status Socket Error!', obj);
			if (obj.onError) obj.onError(data);
			return;
		}
		//keys to convert to float values
		var numberKeys = [
			//N4000
			"againl", "againr", "dm_cgain", "dm_fgain", "dm_slgain",
			"dm_srgain", "gpihighevntdly", "gratarpint", "headphonevol_l", "headphonevol_r", "inputgainleft",
			"inputgainright", "lineoutvol_l", "lineoutvol_r", "rxaudiodelay", "outstream", "txstream",
			"rxstream", "stream", "txaudiodelay", "txsample", "unsolstatusint",
			//N_Series
			"baud", "discoveryintervalsec", "discoveryport", "dvicevtdly", "dvidevtdly", "garpint",
			"nd_a_drop", "nd_a_drop1s", "nd_f_drop", "nd_f_drop1s", "nd_v_drop", "nd_v_drop1s", "playlist",
			"streamaudio", "unsolstint" ,
			//NVR
			"copypct", "dvrmode", "free", "mpegpcnt", "s_play0a", "s_play0b", "s_play1a", "s_play2a", "s_play3a",
			"s_play4a", "s_play5a", "s_play6a", "s_play7a", "s_play8a", "s_play9a", "s_reca0a", "s_reca0b",
			"s_reca1a", "s_reca2a", "s_reca3a", "s_reca4a", "s_reca5a", "s_reca6a", "s_reca7a", "s_reca8a",
			"s_reca9a", "s_recv0a", "s_recv0b", "s_recv1a", "s_recv2a", "s_recv3a", "s_recv4a", "s_recv5a",
			"s_recv6a", "s_recv7a", "s_recv8a", "s_recv9a", "stream", "ttl",
			//WP
			"audiostream", "w0stream", "w1stream", "w2stream", "w3stream"
		];
		
		var keyList = ["stream"];
		obj.status = {};
		obj.rawstatus = data;
		var str = obj.rawstatus;
		while (str.indexOf('\r') != -1){
			var key = str.substring(0, str.indexOf(':')).toLowerCase();
			var valueString = str.substring(str.indexOf(':')+1, str.indexOf('\r'));
			var valueFloat = parseFloat(valueString);
			if (numberKeys.indexOf(key) != -1) obj.status[key] = valueFloat;	
			else obj.status[key] = valueString;
			keyList.push(key);
			str = str.substring(str.indexOf('\r')+1);
		}
		//keyList.sort();
		//console.log(keyList); //use this to get a quick array of keys
		if (obj.rawstatus === '') obj.error = 'Empty response returned. Maybe no connection to unit?';
		if (obj.error && obj.onError) obj.onError(data, obj.error);
		if (onComplete) onComplete(data, obj.status);
	});
}

function getStreamFromUnit(name){
	for (var i=0; i<unitlist.length; i++){
		if (unitlist[i].ip == name) return unitlist[i].videostream;
	}
	for (var i=0; i<unitlist.length; i++){
		if (unitlist[i].name == name) return unitlist[i].videostream;
	}
	for (var i=0; i<unitlist.length; i++){
		if (unitlist[i].mac.toUpperCase() == name.toUpperCase()) return unitlist[i].videostream;
	}
	return false;
}

function getAudioStreamFromUnit(name){
	for (var i=0; i<unitlist.length; i++){
		if (unitlist[i].ip == name) return unitlist[i].audiostream;
	}
	for (var i=0; i<unitlist.length; i++){
		if (unitlist[i].name == name) return unitlist[i].audiostream;
	}
	for (var i=0; i<unitlist.length; i++){
		if (unitlist[i].mac.toUpperCase() == name.toUpperCase()) return unitlist[i].audiostream;
	}
	return false;
}

function sendFile(file, opts, callback){
	//console.log('sendfile', file, opts, callback);
	if (undef(opts)) opts = {};
	if (undef(file)){console.log('sendFile: no file.'); return;};
	if (undef(opts.command)) opts.command = 'image';
	if (undef(callback)) callback = noop;
	if (!window.callbacks) window.callbacks = [];
	var callback_id = randomString(16);
	window.callbacks.push({callback_id: callback_id, callback: callback});
	if (file.name) opts.filename = file.name.replace(/ /g, '');
	if (file.size) opts.size = file.size;
	opts.callback_id = callback_id;
	console.log('sendFile', file, opts, callback);
	delete opts.puid;
	//opts._puid = parseInt(pb.project.core.puid);
	//if (typeof opts.puid !== 'number') opts.puid = false;
	opts.puid = false;
	//if (!opts.puid){
		if (window.wizardproject && wizardproject.puid) opts.puid = wizardproject.puid.toString();
		if (window.pb && pb.project && pb.project.core && pb.project.core.puid) opts.puid = pb.project.core.puid.toString();
	//}
	opts.__id = opts.puid;
	
	binsocket.send(file, opts);
}
var fileSend = sendFile;

function noop(){
  //no operation :)
}

function v3(x,y,z){
	if (!oldIE) return new THREE.Vector3(x,y,z);
	else return {x:x,y:y,z:z};
}

function undef(obj){
	return (typeof obj === 'undefined');
}

function clamp(value, bottom, top){
	if (value >= top) return top;
	else if (value <= bottom) return bottom;
	else return value;
}

if (typeof Object.create !== 'function'){
    Object.create = function(o){
        function F(){};
        F.prototype = o;
        return new F();
    };
}

if (!Array.isArray){
	Array.isArray = function(vArg){
		return Object.prototype.toString.call(vArg) === "[object Array]";
	};
}

function arrayJump(arr1, arr2, item){
	removeFromArray(arr1, item);
	arr2.push(item);
}

function waitfor(cond, func, delay){
	if (!delay) delay = 300;
	if (cond()) func();
	else setTimeout(function(){waitfor(cond, func, delay);}, delay);
}

String.prototype.replaceLast = function(what, replacement){
    return this.split(' ').reverse().join(' ')
		.replace(new RegExp(what), replacement).split(' ').reverse().join(' ');
};

function capsule(total, callback){
	var self = this;
	this.total = total;
	this.callback = callback;
	this.finished = 0;
	this.results = [];
	this.push = function(obj){
		self.finished++;
		self.results.push(obj);
		if (self.finished == self.total) self.callback(self.results);
	}
}

function cssToObj(css){
	var result = {},  attributes = style.split(';'),
        firstIndexOfColon, i, key, value;
    for (i=0; i<attributes.length; i++){
        firstIndexOfColon = attributes[i].indexOf(":");
        key = attributes[i].substring(0, firstIndexOfColon);
        value = attributes[i].substring(firstIndexOfColon + 1);
        key = key.replace(/ /g, "");
        if (key.length < 1) continue;
        if (value[0] === " ") value = value.substring(1);
        if (value[value.length - 1] === " ") value = value.substring(0, value.length - 1);
        result[key] = value;
    }
    return result;
}

function objToCss(obj){
	return JSON.stringify(obj).replaceAll('"','').replaceAll('{','').replaceAll('}','');
}

function cssAnim(selector, anim, callback){
	$(selector).addClass('animated '+anim);
	$(selector).one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', 
		function(){
			$(selector).removeClass('animated '+anim);
			if (callback) callback();
		});
}

Function.prototype.bindArgs = function(){
	if (typeof this !== "function")
        throw new TypeError("Function.prototype.bindArgs needs to be called on a function");   
	var slice = Array.prototype.slice,
        args = slice.call(arguments), 
        fn = this, 
        partial = function() {
            return fn.apply(this, args.concat(slice.call(arguments)));
        };
		
    partial.prototype = Object.create(this.prototype);	
    return partial;	
};

function arrayContains(a, obj){
    var i = a.length;
    while (i--){
		if (a[i] === obj){
			return true;
		}
    }
    return false;
}

function removeFromArray(array, item){
    for (var i in array){
        if (array[i]==item){
            array.splice(i,1);
            break;
        }
    }
}

function objToArray(obj){
	var temp = $.map(obj, function(value, index){
		return [value];
	});
	return temp;
}

function stringToNumber(str){
	return Number(str.replace('px','').replace('%',''));
}

function truncateString(str, len){
	if (str.length <= len) return str;
	var tstr = str.substring(0, len-3) + '...';
	return tstr;
}

function allChildren(node, func){
	if (node && func) func(node);
	for (var i=0; i<node.childNodes.length; i++){
		var child = node.childNodes[i];
		allChildren(child);
		if (func && child) func(child);
	}
}

function nindexOf(str, ch, nth){
    var first_index = str.indexOf(ch);
    var length_up_to_first_index = first_index + 1;
    if (nth == 1) return first_index;
	else {
        var string_after_first_occurrence = str.slice(length_up_to_first_index);
        var next_occurrence = nindexOf(string_after_first_occurrence, ch, nth - 1);
        if (next_occurrence === -1) return -1;
        else return length_up_to_first_index + next_occurrence;  
    }
}

function rgb2hex(rgb){
     if (rgb.search("rgb") == -1) return rgb;
	 else {
          rgb = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))?\)$/);
          function hex(x){return ("0" + parseInt(x).toString(16)).slice(-2);}
          return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]); 
     }
}

function addHexColor(c1, c2) {
	c1 = c1.substring(1,c1.length);
	c2 = c2.substring(1,c2.length);
	var hexStr = (parseInt(c1, 16) + parseInt(c2, 16)).toString(16);
	while (hexStr.length > 6) { hexStr = 'FFFFFF'; } // F pad.
	return '#' + hexStr;
}

function calculator(str){
	str = str.replace(/[^-()\d/*+.]/g, '');
	console.log(eval(str));
	return eval(str);
}

function subHexColor(c1, c2) {
	c1 = c1.substring(1,c1.length);
	c2 = c2.substring(1,c2.length);
	var hexStr = (parseInt(c1, 16) - parseInt(c2, 16)).toString(16);
	while (hexStr.length > 6) { hexStr = hexStr.substring(1,hexStr.length-0); }
	while (hexStr.length < 6) { hexStr = '0' + hexStr; } // Zero pad.
	return '#' + hexStr;
}

function cancelEvent(e){
	if (e.preventDefault) e.preventDefault(); 
	return false;
}

function getQueryStrings() { 
	var assoc  = {};
	var decode = function (s) { return decodeURIComponent(s.replace(/\+/g, " ")); };
	var queryString = location.search.substring(1); 
	var keyValues = queryString.split('&'); 

	for(var i in keyValues) { 
	var key = keyValues[i].split('=');
	if (key.length > 1) {
	  assoc[decode(key[0])] = decode(key[1]);
	}
	} 

	return assoc; 
} 

function getSelectionHTML(){
    var html = "";
    if (typeof window.getSelection != "undefined") {
        var sel = window.getSelection();
        if (sel.rangeCount) {
            var container = document.createElement("div");
            for (var i = 0, len = sel.rangeCount; i < len; ++i) {
                container.appendChild(sel.getRangeAt(i).cloneContents());
            }
            html = container.innerHTML;
        }
    } else if (typeof document.selection != "undefined") {
        if (document.selection.type == "Text") {
            html = document.selection.createRange().htmlText;
        }
    }
    return html;
}

function allDescendantsItemChild(node) {
	for (var i = 0; i < node.childNodes.length; i++) {
	  var child = node.childNodes[i];
	  allDescendantsItemChild(child);
	  if (child.setAttribute) child.setAttribute('class', 'item_child');
	}
}

function allDescendantsAddClass(node, className) {
	for (var i = 0; i < node.childNodes.length; i++) {
	  var child = node.childNodes[i];
	  allDescendantsAddClass(child, className);
	  if (child.setAttribute) child.setAttribute('class', child.className + ' ' + className);
	}
}

function allDescendantsRemoveClass(node, className) {
	for (var i = 0; i < node.childNodes.length; i++) {
	  var child = node.childNodes[i];
	  allDescendantsRemoveClass(child, className);
	  if (child.setAttribute) child.setAttribute('class', child.className.replace(className, ''));
	}
}

function allDescendantsAddToId(node, id) {
	for (var i = 0; i < node.childNodes.length; i++) {
	  var child = node.childNodes[i];
	  allDescendantsAddToId(child, id);
	  if (child.setAttribute) child.setAttribute('id', child.id + id);
	}
}

function isDescendant(parent, child) {
     var node = child.parentNode;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}

function getSelectionParentElement() {
    var parentEl = null, sel;
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.rangeCount) {
            parentEl = sel.getRangeAt(0).commonAncestorContainer;
            if (parentEl.nodeType != 1) {
                parentEl = parentEl.parentNode;
            }
        }
    } else if ( (sel = document.selection) && sel.type != "Control") {
        parentEl = sel.createRange().parentElement();
    }
    return parentEl;
}

function imageToBase64(url, callback, outputFormat){
	var canvas = document.createElement('CANVAS');
	var ctx = canvas.getContext('2d');
	var img = new Image;
	img.crossOrigin = 'Anonymous';
	img.onload = function(){
		canvas.height = img.height;
		canvas.width = img.width;
	  	ctx.drawImage(img,0,0);
	  	var dataURL = canvas.toDataURL(outputFormat || 'image/png');
	  	callback.call(this, dataURL);
	  	canvas = null; 
	};
	img.src = url;
}

function stripHTML(str){
	if (undef(str) || str == '') return;
	var stra, strb;
	while (str.indexOf('<') != -1){
		stra = str.substring(0, str.indexOf('<'));
		strb = str.substring(str.indexOf('>')+1, str.length+1);
		str = stra + strb;
	}
	return str;
}

String.prototype.replaceAll = function(search, replace){
    if (!replace) return this.toString();
    return this.split(search).join(replace);
}

function getAabb(bbox){
	var t = {}; t.r = []; t.c = [];
	t.c[0] = ((bbox.right - bbox.left)/2) + bbox.left;
	t.c[1] = ((bbox.bottom - bbox.top)/2) + bbox.top;
	t.r[0] = (bbox.right - bbox.left)/2;
	t.r[1] = (bbox.bottom - bbox.top)/2;
	return t;
}

function aabbOverlap(a, b){
    var t;
    if (Math.abs(a.c[0] - b.c[0]) > (a.r[0] + b.r[0])) return false;
    if (Math.abs(a.c[1] - b.c[1]) > (a.r[1] + b.r[1])) return false;
    //if (Math.abs(a.c[2] - b.c[2]) > (a.r[2] + b.r[2]) return false;
    return true;
};

function dumpError(err){
	if (typeof err === 'object'){
		if (err.stack) console.log('\nStacktrace:', err.stack);
	} 
	else console.log('Error: ', err);
}

function runTimeout(func, time, delta){
    if (undef(delta)) delta = 100;
    var tid = setInterval(function(){
        if (window.blurred) return; 
        time -= delta;
        if (time <= 0){
            clearInterval(tid);
            func();
        }
    }, delta);
    return tid;
}

function runInterval(func, delta){
	if (undef(delta)) delta = 100;
    var tid = setInterval(function(){
        if (window.blurred) return;   
        else func();
    }, delta);
    return tid; 
}

function sendScript(script, php){
	
	if (undef(php)) php = "/scripts/index.php";
	
	var modifiedscript = script;
	for (var t=0; t<thispanel.widgets.length; t++){
		modifiedscript.replace(thispanel.widgets[t].id+'.value', thispanel.widgets[t].value);
	}
	
	if (!undef(modifiedscript)) if (modifiedscript.indexOf('goto ') != -1){							
		var trimmedscript = modifiedscript.substring(0, modifiedscript.indexOf('goto '));
		var gotoscripta = modifiedscript.substr(modifiedscript.indexOf('goto ')+5, 100);
		var gotoscriptb = parseInt(gotoscripta);
		$.post(php, {executescript: "yes", script: trimmedscript},
			function(data) { 
				console.log('Script Engine: ', data);
			});
		window.location = '../panels/?project='+qs.project+'&panel='+(gotoscriptb-0);
	}
	else $.post(php, {executescript: "yes", script: modifiedscript},
			function(data){
				console.log('Script Engine: ', data);
			});
}

function getDateTime(){
	var currentdate = new Date(); 
	var returndate = {};
	returndate.year = currentdate.getFullYear();
	returndate._month = currentdate.getMonth()+1;
	switch (returndate._month){
		case 1:
			returndate.month = 'January';
			break;
		case 2:
			returndate.month = 'February';
			break;
		case 3:
			returndate.month = 'March';
			break;
		case 4:
			returndate.month = 'April';
			break;
		case 5:
			returndate.month = 'May';
			break;
		case 6:
			returndate.month = 'June';
			break;
		case 7:
			returndate.month = 'July';
			break;
		case 8:
			returndate.month = 'August';
			break;
		case 9:
			returndate.month = 'September';
			break;
		case 10:
			returndate.month = 'October';
			break;
		case 11:
			returndate.month = 'November';
			break;
		case 12:
			returndate.month = 'December';
			break;
	}
	returndate.day = currentdate.getDate();
	returndate._weekday = currentdate.getDay();
	returndate.weekday = '';
	switch (returndate._weekday){
		case 0:
			returndate.weekday = 'Sunday';
			break;
		case 1:
			returndate.weekday = 'Monday';
			break;
		case 2:
			returndate.weekday = 'Tuesday';
			break;
		case 3:
			returndate.weekday = 'Wednesday';
			break;
		case 4:
			returndate.weekday = 'Thursday';
			break;
		case 5:
			returndate.weekday = 'Friday';
			break;
		case 6:
			returndate.weekday = 'Saturday';
			break;
	}
	returndate._hour = currentdate.getHours();
	returndate.hour = returndate._hour > 12 ? returndate._hour - 12 : returndate._hour;
	returndate.ampm = returndate._hour >= 12 ? 'pm' : 'am';
	returndate.AMPM = returndate._hour >= 12 ? 'PM' : 'AM';
	returndate.minute = currentdate.getMinutes();
	if (returndate.minute < 10){
		returndate.minute = '0' + returndate.minute;
	}
	returndate.second = currentdate.getSeconds();
	if (returndate.second < 10){
		returndate.second = '0' + returndate.second;
	}
	returndate.timestring = returndate.hour + ':' + returndate.minute;
	returndate.fulltimestring = returndate.hour + ':' + returndate.minute + ' ' + returndate.AMPM;
	returndate.fulltimesecondstring = returndate.hour + ':' + returndate.minute + ':' + returndate.second + ' ' + returndate.AMPM;
	returndate.datestring = returndate.weekday + ', ' + returndate.month + ' ' + returndate.day;
	returndate.dateyearstring = returndate.datestring + ' ' + returndate.year;
	returndate.fullstring = returndate.fulltimestring + ' ' + returndate.datestring;
	returndate.fullyearstring = returndate.fulltimestring + ' ' + returndate.dateyearstring;
	returndate.fullyearsecondstring = returndate.fulltimesecondstring + ' ' + returndate.dateyearstring;
	return returndate;
}

function makeElement(type, innerhtml){
	var newele = document.createElement(type);
	newele.innerHTML = innerhtml;
	return newele;
}

function childOf(c, p){while((c=c.parentNode) && c!==p); return !!c;}

function deleteElementById(id){
	var element = document.getElementById(id);
	element.parentNode.removeChild(element);
}

function addEventHandler(obj, evt, handler){
	if (obj.addEventListener) obj.addEventListener(evt, handler, false);
	else if (obj.attachEvent) obj.attachEvent('on'+evt, handler);
	else obj['on'+evt] = handler;
}
	
Function.prototype.bindToEventHandler = function(){
	var handler = this;
	var boundParameters = Array.prototype.slice.call(arguments);
	return function(e){
		e = e || window.event;
		boundParameters.unshift(e);
		handler.apply(this, boundParameters);
	}
}

Math.radians = function(degrees) {
  return degrees * Math.PI / 180;
};
Math.degrees = function(radians) {
	return radians * 180 / Math.PI;
};

function calcAngle(x, y, x1, y1){
    return Math.degrees(Math.atan2(x1-x, y-y1));
}

function distance2d(pointa, pointb){
   return Math.sqrt(Math.pow((pointa.x - pointb.x),2) + Math.pow((pointa.y - pointb.y),2));
}

function getLeftCenter(offset){
	return (window.innerWidth/2)-offset;
}
function getLeftRight(){
	return (window.innerWidth);
}

function getClosestSquareRoot(number){
	var tnum = Math.sqrt(number);
	tnum = Math.round(tnum);
	return tnum;
}

function addStyleSheet(css){
	if (undef(css)) return;
	var	head = document.getElementsByTagName('head')[0],
		style = document.createElement('style');
	style.type = 'text/css';
	if (style.styleSheet) style.styleSheet.cssText = css;
	else style.appendChild(document.createTextNode(css));
	head.appendChild(style);
}

function swapElements(obj1, obj2) {
    var temp = document.createElement("div");
    obj1.parentNode.insertBefore(temp, obj1);
    obj2.parentNode.insertBefore(obj1, obj2);
    temp.parentNode.insertBefore(obj2, temp);
    temp.parentNode.removeChild(temp);
}

var RECT = function(){
	this.top = 0;
	this.left = 0;
	this.bottom = 0;
	this.right = 0;
}
	
function inRect(rect, x, y){
	return (y >= rect.top && y <= rect.bottom) &&
		(x >= rect.left && x <= rect.right)
}

var scrollSize = 18;

function inScrollRange(event){
	var x = event.pageX,
		y = event.pageY,
		e = $(event.target),
		hasY = e.hasScroll(),
		hasX = e.hasScroll("x"),
		rX = null,
		rY = null,
		bInX = false,
		bInY = false

	if(hasY){
		rY = new RECT();
		rY.top = e.offset().top;
		rY.right = e.offset().left + e.width();
		rY.bottom = rY.top +e.height();
		rY.left = rY.right - scrollSize;
		bInY = inRect(rY, x, y);
	}

	if(hasX){
		rX = new RECT();
		rX.bottom = e.offset().top + e.height();
		rX.left = e.offset().left;
		rX.top = rX.bottom - scrollSize;
		rX.right = rX.left + e.width();
		bInX = inRect(rX, x, y);
	}

	return bInX || bInY;
}

var exportPNG = function(button, stateindex) {

	if(!$('#export_canvas').length) {
		$('<canvas>', {id: 'export_canvas'}).hide().appendTo('body');
	}
	var c = $('#export_canvas')[0];

	c.width = svgCanvas.contentW; //may need to set this to button.states[stateindex].svgwidth
	c.height = svgCanvas.contentH;
	
	canvg(c, button.states[stateindex].svg, {renderCallback: function() {
		var datauri = c.toDataURL('image/png');
		this.states[stateindex].svgimage = datauri;
		//console.log(this, datauri);
		/*this.svgimage = new Image();
		this.svgimage.src = datauri;
		this.svgimage.style.width = '100%';
		this.svgimage.style.height = '100%';*/
	}.bind(button)});
};

function deepCopy(obj){
    if (obj === null || typeof obj !== "object") return obj;
    if (obj instanceof Date){
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array){
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++){
            copy[i] = deepCopy(obj[i]);
        }
        return copy;
    }
    if (obj instanceof Object){
        var copy = {};
        for (var attr in obj){
            if (obj.hasOwnProperty(attr)) copy[attr] = deepCopy(obj[attr]);
        }
        return copy;
    }
    return false;
}
var clone = deepCopy;

function validName(thename){
	var asciireg = /^[a-z0-9]+$/i;
	var testname = $.trim(thename);
	var removeUnderscores = testname;
	removeUnderscores = removeUnderscores.replace(/_/g, "");
	removeUnderscores = removeUnderscores.replace(/ /g, "");
	if (!testname.length){
		alert("Please enter a name.");
		return false;
	}
	if (testname.length > 20){
		alert("Name must be less than 20 characters.");
		return false;
	}
	if (!asciireg.test(removeUnderscores)){
		alert("Only letters, numbers, underscores, and spaces are allowed in name.");
		return false;        
	}
	if ($.isNumeric(testname)){
		alert("Name cannot be just a number.");
		return false;        
	}
	return true;
}

function retrieve_zip(callback){
	try {if (!window.google){google = 0;}} catch(err){google = 0;}
	if (navigator.geolocation){
		navigator.geolocation.getCurrentPosition(function(position){
			zip_from_latlng(position.coords.latitude,position.coords.longitude,callback);
		});
	}
	else if (google && google.gears){
		var geloc = google.gears.factory.create('beta.geolocation');
		geloc.getPermission();
		geloc.getCurrentPosition(function(position){
			zip_from_latlng(position.latitude,position.longitude,callback);
		},function(err){console.log(err);});
	}
}
function zip_from_latlng(latitude,longitude){
	var point = new google.maps.LatLng(latitude, longitude);  
	new google.maps.Geocoder().geocode({'latLng': point}, function(res, status){  
		var zip = res[0].formatted_address.match(/,\s\w{2}\s(\d{5})/);  
		if (undef(pb.location)) pb.location = {};
		pb.location.zip = zip[1];  
	});
}
function locate(){
	retrieve_zip("location_callback");
}
function geolocate(){
	navigator.geolocation.getCurrentPosition(function(position){
			pb._userlocation = position;
			console.log("Location found: ", position);
		},
		function(error){
			console.log("Something went wrong: ", error);
		}, 
		{
			timeout: 5000,
			maximumAge: (1000 * 60 * 15),
			enableHighAccuracy: true
		}
	);
	navigator.geolocation.watchPosition(function(position){
		pb._userlocation = position;
		console.log("Location found: ", position);
	});
}

Function.prototype.bindToEventHandler = function bindToEventHandler(){
	var handler = this;
	var boundParameters = Array.prototype.slice.call(arguments);
	return function(e) {
		e = e || window.event;   
		boundParameters.unshift(e);
		handler.apply(this, boundParameters);
	}
};

function stringifyFunction(func){
	return '('+func+').call(self);';
}
function generateWorker(code){
	windowURL = window.URL || window.webkitURL;   
	var blob, worker;
	var stringified = 
		'(function(){\
			this.onmessage = function(e){' +
				(''+code).replace('function (e){','')
					.replaceLast('}','') +
			'}\
		}).call(self);';
	
    try {
		blob = new Blob([stringified], {type: 'application/javascript'});
	} 
	catch (e){
		window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
		blob = new BlobBuilder();
		blob.append(stringified);
		blob = blob.getBlob();
	}
	
	if ("Worker" in window){
		worker = new Worker(windowURL.createObjectURL(blob));
	} 
	else {
		FakeWorkerCode = function(code, worker){
			code.call(this);
			this.worker = worker;
		}
		FakeWorkerCode.prototype.postMessage = function(e){
			this.worker.onmessage({data: e});
		}
		FakeWorker = function(code){
			this.code = new FakeWorkerCode(code, this);
		}
		FakeWorker.prototype.postMessage = function(e){
			this.code.onmessage({data: e});
		}
		worker = new FakeWorker(code);
	}
	return worker;
}

function thread(code, onMessage){
	var worker = generateWorker(code);
	if (onMessage) worker.onmessage = onMessage;
	worker.post = worker.postMessage;
	return worker;
}
