
<html>
<head>
</head>
<body>
	<input type='file' onchange='openFile(event, onComplete)'>
	<script>
	
		String.prototype.rtrim = function(s){
			if (!s) s = ' '; return this.replace(new RegExp(s + "*$"),''); 
		};
		
		String.prototype.replaceAll = function(search, replace){
			if (!replace) return this.toString();
			return this.split(search).join(replace);
		}
		
		function onComplete(){
			console.log('done...');
		}
	
		function openFile(event, callback){
			var input = event.target;
			var reader = new FileReader();
			reader.onload = function(){
				var arrayBuffer = reader.result;
				var data = new Uint8Array(arrayBuffer);
				//process(data);
				//crestron2IRF(data);
				var name = input.files[0].name;
				if (name.substr(name.length-3, 3) === 'irl') irf(data);
				if (name.substr(name.length-2, 2) === 'ir') crestron(data);
				if (callback) callback();
			};
			reader.readAsArrayBuffer(input.files[0]);
		};
		
		function zeropad(n, p, c){
			var pad_char = typeof c !== 'undefined' ? c : '0';
			var pad = new Array(1 + p).join(pad_char);
			return (pad + n).slice(-pad.length);
		}
		
		var ConvertBase = function(num){
			return {
				from: function(baseFrom){
					return {
						to: function(baseTo){
							return parseInt(num, baseFrom).toString(baseTo);
						}
					};
				}
			};
		};
		function bin2dec(num){return ConvertBase(num).from(2).to(10);};
		function bin2hex(num){return ConvertBase(num).from(2).to(16);};
		function dec2bin(num){return ConvertBase(num).from(10).to(2);};
		function dec2hex(num){return ConvertBase(num).from(10).to(16);};
		function hex2bin(num){return ConvertBase(num).from(16).to(2);};
		function hex2dec(num){return ConvertBase(num).from(16).to(10);};
		
		function asciiToHex(str){
			str = str.toString();
			var arr = [];
			for (var i = 0, l = str.length; i < l; i ++){
				var hex = Number(str.charCodeAt(i)).toString(16);
				arr.push(hex.length > 1 && hex || "0" + hex);
			}
			return arr.join('');
		}
		
		function hexToAscii(hex){
			var _hex = hex.toString();
			var str = '';
			for (var i = 0; i < _hex.length; i += 2)
				str += String.fromCharCode(parseInt(_hex.substr(i, 2), 16));
			return str;
		}
		
		function dimArray(length){
			var arr = new Array(length || 0), i = length;
			if (arguments.length > 1){
				var args = Array.prototype.slice.call(arguments, 1);
				while (i--) arr[length-1 - i] = dimArray.apply(this, args);
			}
			return arr;
		}
		
		function sci(number, n){  //reduces to n decimal places of accuracy
			if (!n) n = 2; return parseInt(number * (Math.pow(10, n))) / (Math.pow(10, n));
		}
		
		function accurateAdd(a, b, accuracy){  //to drop floating point errors
			if (!accuracy) accuracy = 10; return parseFloat((a + b).toFixed(accuracy));
		}
		
		function magicKen(v, c){
			return zeropad(dec2hex(Math.round((v/250)*c)), 4);
		}
		
		function translate(code, carrier){
			var result = '';
			for (var i=0; i<code.length-0; i+=4){
				result += magicKen(parseFloat(hex2dec(code.substr(i, 2) + code.substr(i+2, 2))), carrier);
			}
			return result;
		}
		
		function crestron(data){
			
			var MultiCode = 0;
			var ListCount = 1;
			
			var count = 0;
			var results = [];
			var names = [];
			var header = {};
			
			while (count < data.length){
				var ByteLen = data[count++];
				var BytesTmp = data[count++];
				if (ByteLen == 2 && BytesTmp == 255) break;
				if (BytesTmp > 239 && BytesTmp < 248){
					var TmpSt = '';
					for (var i=0; i<parseInt(ByteLen) - 2; i++) TmpSt += hexToAscii(zeropad(dec2hex(data[count++]), 2));
					TmpSt = TmpSt.trim();
					switch (BytesTmp){
						case 240: header.driver = TmpSt; break;
						case 241: header.info = TmpSt; break;
						case 242: header.date = TmpSt; break;
						case 243: header.manufacturer = TmpSt; break;
						case 244: header.device = TmpSt; break;
						case 245: header.handControl = TmpSt; break;
						case 246: header.mistery = TmpSt; break;
						case 247: header.category = TmpSt; break;
					}
				}
				else {
					console.log('info', ListCount, ByteLen);
					if (ListCount > 6){
						names.push('');
						for (var i=0; i<parseInt(ByteLen) - 2; i++){
							names[names.length-1] += hexToAscii(zeropad(dec2hex(data[count++]), 2));
						}
					}
					ListCount++;
				}
			}
			
			ListCount--;

			var ByteCount, StartSeek, IdCount, RepeatLen, Cn = 1, _multiCodeReturn = false;
			
			while (count < data.length){
				if (!_multiCodeReturn){ 
					StartSeek = count;
					ByteCount = data[count++];
					if (ByteCount === 0){
						console.log('bytecount 0', results);
						var commands = [];
						for (var i=1; i<results.length; i++){
							console.log('var', i);
							commands.push({
								name: names[i+6],
								code: results[i].Code,
								header: header
							});
						}
						window.commands = commands;
						return commands;
						break;
					}
					MultiCode = 0;
				}
				
				_multiCodeReturn = false;
				
				results[Cn] = {};
				results[Cn].Channel = data[count++];
				BytesTmp = data[count++];
				
				if (BytesTmp == 43) results[Cn].Carrier = 0;
				else if (BytesTmp == 25) results[Cn].Carrier = 65535;
				else results[Cn].Carrier = 4000 / BytesTmp;
				
				BytesTmp = data[count++];
				IdCount = BytesTmp * 2;
				BytesTmp = data[count++];
				RepeatLen = BytesTmp * 2;
				
				if (IdCount === 0){
					results[Cn].IdCount = RepeatLen;
					results[Cn].RepeatStart = 1;
				}
				else {
					results[Cn].IdCount = IdCount + RepeatLen;
					results[Cn].RepeatStart = (IdCount + RepeatLen) - RepeatLen;
				}
				
				results[Cn].RepeatEnd = results[Cn].IdCount;
				BytesTmp = data[count++];
				results[Cn].Unique = BytesTmp & 0x0F;
				results[Cn].Repeat = parseInt((BytesTmp & 0xF0) / 16);
				if (results[Cn].Repeat === 0) results[Cn].Repeat = 5;
				for (var Nn = 0; Nn <= results[Cn].Unique - 1; Nn++){
					var ByteH = data[count++];
					var ByteL = data[count++];
					if (!results[Cn].Bursts) results[Cn].Bursts = [];
					results[Cn].Bursts[Nn] = hex2dec(zeropad(dec2hex(ByteH), 2) + zeropad(dec2hex(ByteL), 2));
				}
				results[Cn].Code = '';
				for (var i=0; i<results[Cn].IdCount / 2; i++){
					results[Cn].Code += zeropad(dec2hex(data[count++]), 2);
				}
				if (ListCount == Cn) break;
				MultiCode++;
				if (ByteCount > count - StartSeek){_multiCodeReturn = true; continue;}				
				Cn++;
			}
		}
		
		function irf(data){
			console.log(data); window.data = data;

			window.results = []; window.subresults = []; window.test = '';
			
			var InfoLen, MultiCode, BurstCount, RepeatStart, RepeatEnd, IRName, MultiCodeCount, Carrier, Code = '', OldTmp;
			var Cn = 0, N = 0, Nn = 0;
			var ByteL, ByteH;

			var Header = {};
			var IR = dimArray(255, 7);
			for (var i=0; i<255; i++){
				for (var j=0; j<7; j++){
					IR[i][j] = {Bursts: []};
				}
			}
			
			window._data = [];
			for (var i=0; i<data.length; i++){
				window._data.push(zeropad(dec2hex(data[i]), 2));
			}
			
			var _step = 0;
			function step(){return _step++;}
			function misstep(){_step = 0}

			Header.Manufacturer = '';
			for (var i=0; i<30; i++){
				Header.Manufacturer += hexToAscii(zeropad(dec2hex(data[step()]), 2));
			}
			
			step();

			Header.HandControl = '';
			for (var i=0; i<20; i++){
				Header.HandControl += hexToAscii(zeropad(dec2hex(data[step()]), 2));
			}
			
			step();
	
			Header.Device = '';
			for (var i=0; i<20; i++){
				Header.Device += hexToAscii(zeropad(dec2hex(data[step()]), 2));
			}
			
			step();

			ByteL = data[step()];
			ByteH = data[step()];
			var InfoLen = hex2dec(zeropad(dec2hex(ByteH), 2) + zeropad(dec2hex(ByteL), 2));
			
			Header.Info = '';
			if (InfoLen) for (var i=0; i<InfoLen-0; i++){
				Header.Info += hexToAscii(zeropad(dec2hex(data[step()]), 2));
			}
			
			Header.Info = Header.Info.replaceAll('\r\n', '\n');
			Header.Info = Header.Info.replaceAll('\n', '\r\n');

			console.log('Finished Header', Header);
			
			var iteration = 1; var MultiCode = 0;

			while (_step < data.length){
			
					console.log('main loop iteration...', iteration++);

				var IRName = '';
				for (var i=0; i<20; i++) IRName += hexToAscii(zeropad(dec2hex(data[step()]), 2));
				
					console.log('IRName', IRName);
				
				step(); ByteL = data[step()]; step();
				
					console.log('ByteL (if not zero Cn becomes ByteL?)', ByteL);
				
				if (ByteL) Cn = ByteL //is this right?
				ByteL = data[step()]; ByteH = data[step()];
				BurstCount = hex2dec(zeropad(dec2hex(ByteH), 2) + zeropad(dec2hex(ByteL), 2));
				
					console.log('BurstCount', BurstCount);

				ByteL = data[step()]; ByteH = data[step()];

				RepeatStart = hex2dec(zeropad(dec2hex(ByteH), 2) + zeropad(dec2hex(ByteL), 2));
			
					console.log('RepeatStart', RepeatStart);

				ByteL = data[step()]; ByteH = data[step()];
				RepeatEnd = hex2dec(zeropad(dec2hex(ByteH), 2) + zeropad(dec2hex(ByteL), 2));
				
					console.log('RepeatEnd', RepeatEnd);

				ByteL = data[step()];
				ByteH = data[step()];

				if (ByteL == 0 && ByteH == 0) Carrier = 38;
				else Carrier = 1000 / (ByteH + (ByteL / 256));

				ByteL = data[step()];
				NulByte = data[step()];
				IR[Cn][MultiCode].Repeat = ByteL;

				MultiCodeCount = data[step()];
				NulByte = data[step()];

				Code = '';
				for (var i=0; i<BurstCount*2; i+=2) Code += zeropad(dec2hex((data[step()] + data[step()]*256)), 4);
				
				IR[Cn][MultiCode].Code = Code;
				console.log('donewithcode', Code, hexToAscii(Code));

				IR[Cn][MultiCode].Name = IRName;
				IR[Cn][MultiCode].Channel = Cn;
				IR[Cn][0].Multi = MultiCodeCount;
				IR[Cn][MultiCode].Carrier = Carrier;
				
				console.log('checking repeat, repeatstart', IR[Cn][MultiCode].Repeat, RepeatStart);
				console.log('checking compare them', IR[Cn][MultiCode].Repeat > 0, RepeatStart == 0);
				if (IR[Cn][MultiCode].Repeat > 0 && RepeatStart == 0) IR[Cn][MultiCode].RepeatStart = 1;
				else IR[Cn][MultiCode].RepeatStart = RepeatStart;

				IR[Cn][MultiCode].RepeatEnd = RepeatEnd;
					 
				var LastLight = '';
				var MadeDark = false;
				
				console.log('checking burstcount < repeatend', BurstCount, RepeatEnd);
				if (BurstCount < RepeatEnd){

					LastLight = Code.substring(Code.length - 2);

					if (0) for (var N=1; N<(BurstCount*2)-4; N+=2){
						if (LastLight == Code.substr(N, 2)){
							Code += Code.substr(N+2, 2);
							MadeDark = true;
							break;
						}
					}
					
					if (0) if (!MadeDark) Code = Code + Code.substr(3, 2);
					
					BurstCount++;
					
				}
				
				var TestBurst = 0;
				var Nibble1 = 0;
				
				IR[Cn][MultiCode].IDCount = 0;
				
				Nibble1 = -1;
				IR[Cn][MultiCode].Unique = 0;
				
				var breakout = false;
				var rn = 1;
				
				console.log('inner N loop', IR[Cn][MultiCode].Unique - 1);
				console.log('burstcount for N<burstcount*2', BurstCount);
				for (var N=1; N<(BurstCount*2); N+=2){

					console.log('testburst'+rn++, TestBurst);
					TestBurst = hex2dec((Code.substr(N + 1, 1) + Code.substr(N, 1)).replaceAll(' ', ''));
					console.log('testburst'+rn++, TestBurst);
					TestBurst = TestBurst * 4;
					console.log('testburst'+rn++, TestBurst);
					TestBurst = (TestBurst / (1000 / IR[Cn][MultiCode].Carrier));
					console.log('testburst'+rn++, TestBurst);
					
					console.log('inner N loop', IR[Cn][MultiCode].Unique - 1);
					
					for (var Nn=0; Nn<IR[Cn][MultiCode].Unique - 1; Nn++){
					
						console.log('inside inner N loop', IR[Cn][MultiCode].Bursts[Nn]);

						if (IR[Cn][MultiCode].Bursts[Nn] == TestBurst){
							if (Nibble1 == -1) Nibble1 = Nn * 16;
							else {
								if (0) IR[Cn][MultiCode].Code = IR[Cn][MultiCode].Code + String.fromCharCode(Nibble1 + Nn);
								IR[Cn][MultiCode].IDCount = IR[Cn][MultiCode].IDCount + 2;
								Nibble1 = -1;
							}
							
							breakout = true;
							if (breakout) break;
						}
						if (breakout) break;
					}
					if (breakout){breakout = false;}
					else {
						IR[Cn][MultiCode].Bursts[IR[Cn][MultiCode].Unique] = TestBurst;
						test += zeropad(dec2hex(TestBurst), 4);
						IR[Cn][MultiCode].Unique++;
						
						if (Nibble1 == -1) Nibble1 = Nn * 16;
						else {
							if (0) IR[Cn][MultiCode].Code += String.fromCharCode(Nibble1 + Nn);
							if (0) IR[Cn][MultiCode].Code += String.fromCharCode(Nibble1 + Nn);
							IR[Cn][MultiCode].IDCount += 2;
							Nibble1 = -1;
						}
					}
				}
				
				console.log('step stuff: multicodecount, multicode', MultiCodeCount, MultiCode);
				
				if ((MultiCodeCount - 1) > MultiCode){
					window.results.push(IR[Cn][MultiCode]);
					MultiCode++;
					continue;
				}
				else {
					window.subresults.push(IR[Cn][MultiCode]);
					if (Cn < 255) Cn++;
					MultiCode = 0;
				}
				
			}

			if (MultiCode == 0) ListCount = Cn;

			window.IR = IR;
			window.Header = Header;
		}
		
		function makeModule(dat, header){
			var template = '\
				/******************************************************************************\
					Custom {{make}} module for {{manufacturer}}\
				*******************************************************************************/\
					//define namespace if it doesnt exist\
					if (!Modules["{{manufacturer}}"]) Modules["{{manufacturer}}"] = {};\
					\
					Modules["{{manufacturer}}"]["{{make}}"] = function(ip, port, opts){\
						\
						if (undef(opts)) opts = {};\
						\
						var obj = {};\
						\
						obj.ip = !undef(ip) ? ip : "169.254.1.2";\
						obj.port = !undef(port) ? port : 50001;\
						obj.serialport = opts.hasOwnProperty("serialport") ? opts.serialport : 50004;\
						obj.onError = opts.hasOwnProperty("onError") ? opts.onError : function(err){console.log(err)};\
						obj.serial_interface = opts.hasOwnProperty("serial_interface") ? opts.serial_interface : false;\
						obj.ir_interface = opts.hasOwnProperty("ir_interface") ? opts.ir_interface : false;\
						obj.connection = false;\
						obj.status = {};\
						\
						obj.protocol = opts.hasOwnProperty("protocol") ? protocol : "tcp";\
						obj.statusprotocol = opts.hasOwnProperty("statusprotocol") ? statusprotocol : "udp";\
						\
						//sets port, and reestablishes socket to that port\
						obj.setPort = function(port){\
							obj.port = port;\
							if (obj.connection){\
								obj.connection.destroy();\
								obj.tcpopen();\
							}\
						};\
						\
						{{commands}}\
						\
						obj.print = function(){\
							console.log(obj);\
						};\
						\
						obj.popup = function(){\
							var output = "";\
							for (var property in obj){\
								if (typeof obj[property] !== "function") output += property + ": " + obj[property]+"; \n\n";\
							}\
							alert(output);\
						};\
						\
						return obj;\
					};\
			';
			var comtemplate = '\
				obj.{{name}} = function(){\
					tcpclient(obj.ip, obj.port, {{hex}});\
				};\
			';
			var commands = '';
			for (var i=0; i<dat.length; i++){
				var str = comtemplate.replace(/{{hex}}/g, dat[i].Code);
				str = str.replace(/{{name}}/g, dat[i].Name.replace(/ /g, '_'));
				commands += str;
			}
			template = template.replace(/{{commands}}/g, commands);
			template = template.replace(/{{manufacturer}}/g, header.Manufacturer.trim());
			template = template.replace(/{{make}}/g, header.Device.trim());
			return template;
		}
		
	</script>
</body>
</html>