<form>
Input ze souboru <input type="file" onchange="CES.func.previewFile();">
<img src="" height="200" alt="Nahled obrazku, kdyz je soubor obrazek...">
<br>Input<br> <textarea id="inp"></textarea>
<br><input type="button" value="compress" onclick="CES.func.compress();">
<br>Output<br> <textarea id="out"></textarea>
<div id="stat"></div>
</form>

<!--script src='zlib.pretty.js'></script-->
<!--script src='zlib.min.js'></script-->
<!--script src='zip.min.js'></script-->

<script>
// ces.pas - myslim si, ze to deli byte po 2 bitech, spocita pocet opakovani a podle toho vytvori mapy true/false. Ktere navic zasifruje huffmanem.

// jeste jsem to moc neupravoval!!! 13.10.2020
// 14.10. nedokonceno


var CES = {};
CES.vars = {};
CES.vars.input = '';
CES.vars.input_count = 0;
CES.vars.tab = {};
CES.vars.map = '';
CES.vars.map_count;	// jenom slouzi ke kontrole, zda js spravne ulozil znak do mapy
CES.vars.map_bites;
CES.vars.map_byte;
CES.vars.map_list;
CES.vars.out_info = [];
CES.func = {};

CES.func.base64Encode = function (data)
	{
	return window.btoa(data);	// mam pocit, ze s UTF-8 + diakritika si btoa neporadila, ale na soubory to snad bude ok
	}

CES.func.base64Decode = function (data)
	{
	return window.atob(data);
	}

CES.func.previewFile = function()
	{
	var preview = document.querySelector('img');
	var file    = document.querySelector('input[type=file]').files[0];
	var reader  = new FileReader();
	reader.addEventListener("load", function ()
		{
		data = CES.func.base64Encode(reader.result);
		document.getElementById('inp').value = data;
		preview.src = reader.result;
		}, false);
	if (file)
		{
		reader.readAsBinaryString(file)
		}
	}

CES.func.tablesCreate = function ()
	{
	var i, tab;
	tab = {};
	tab.ord  = {};
	tab.chr  = [];
	tab.freq = {};
	// ord(char), chr(ord), freq(char)
	for (i=0;i<256;i++)
		{
		chr = String.fromCharCode(i);
		tab.ord[chr]  = i;	// byte = ord(char)
		tab.chr[i]    = chr;	// char = chr(byte)
		tab.freq[chr] = [0, 0, 0, 0];
		tab.freq[chr][(i)    & 0x03] += 1;
		tab.freq[chr][(i>>2) & 0x03] += 1;
		tab.freq[chr][(i>>4) & 0x03] += 1;
		tab.freq[chr][(i>>6) & 0x03] += 1;
		}
	return tab;
	}

CES.func.frequencyDetect = function (data)
	{
	var i, end_i, freq, freq_byte, chr;
	freq  = [0, 0, 0, 0];
	end_i = data.length;
	for (i=0;i<end_i;i++)
		{
		chr = input.charAt(i);
		freq_byte = CES.vars.tab_freq[chr];
		freq[0] += freq_byte[0];
		freq[1] += freq_byte[1];
		freq[2] += freq_byte[2];
		freq[3] += freq_byte[3];
		}
	return freq;
	}

//CES.func.mapAdd = function(bool)
//	{
//	CES.vars.map += bool ? 1 : 0;
//	}

CES.func.mapAddReset = function()
	{
	CES.vars.map_byte  = 0;
	CES.vars.map_bites = 7;
	}

CES.func.mapAddByte = function()
	{
	CES.vars.map += CES.func.chr(CES.vars.map_byte);
	CES.vars.map_count++;
	CES.func.mapAddReset();
	}

CES.func.mapAdd = function(bite)	//,n
	{
	var b;	//,n
	b = CES.vars.map_bites;
	//bite = (bool * 1);
	CES.vars.map_byte = CES.vars.map_byte | (bite << b);
	if (b>0)
		{
		b--;
		CES.vars.map_bites = b;
		return;
		}
	CES.func.mapAddByte();
	}
/*
var m_b:byte;
begin
  if ovl_bit=0 then mapa[ovl_byte]:=0;
  if co_dat=true then
    begin
      m_b:=1;
      m_b:=m_b shl ovl_bit;
      mapa[ovl_byte]:=mapa[ovl_byte]+m_b;
    end;
  inc(ovl_bit);
  if ovl_bit=8 then begin ovl_bit:=0;inc(ovl_byte);end;
*/

CES.func.mapAddAll = function(m, n)
	{
	if (m==0)
		{
		CES.func.mapAdd(n==0);
		}
	else {if (m==1)
		{
		if (n==1)
			{
			CES.func.mapAdd(true);
			}
		if (n==2 || n==3)
			{
			CES.func.mapAdd(false);
			}
		}
	else {if (m==2)
		{
		if (n==2)
			{
			CES.func.mapAdd(true);
			}
		if (n==3)
			{
			CES.func.mapAdd(false);
			}
		}}}
	}


CES.func.huffMapEncodeOne = function (n)
	{
	if (n==0)
		{
		CES.func.mapAdd(1);
		return;
		}
	else {if (n==1)
		{
		CES.func.mapAdd(0);
		CES.func.mapAdd(0);
		return;
		}
	else {if (n==2)
		{
		CES.func.mapAdd(0);
		CES.func.mapAdd(1);
		CES.func.mapAdd(0);
		return;
		}}}
	CES.func.mapAdd(0);
	CES.func.mapAdd(0);
	CES.func.mapAdd(0);
	}

CES.func.huffEncode = function (input)
	{
	var freq, order, map,i,end_i,j,end_j, m,n;
	freq  = CES.func.frequencyDetect(input);
	order = [0,1,2,3];
	order.sort(function(b,a){return freq[a]-freq[b];});	//desc
	CES.func.mapAddReset();
	CES.vars.map = '';
	CES.vars.map_count = 0;
	end_i = input.length;
	for (i=0;i<end_i;i++)
		{
		m = CES.func.ord(input[i]);
		n = m & 0x03; m >>= 2; CES.func.huffEncodeOne(order[n]);
		n = m & 0x03; m >>= 2; CES.func.huffEncodeOne(order[n]);
		n = m & 0x03; m >>= 2; CES.func.huffEncodeOne(order[n]);
		n = m & 0x03;          CES.func.huffEncodeOne(order[n]);
		}
	if (CES.vars.map_bites>0)
		{
		CES.func.mapAddByte();
		}
	return CES.vars.map;
	}

CES.func.inputToMap = function (freq,input)
	{
	var order, map,i,end_i,j,end_j, m,n;
//var x;
	order = [0,1,2,3];
	order.sort(function(b,a){return freq[a]-freq[b];});	//desc
//	alert([freq,order]);
	end_i = input.length;
//old_x = 0;
//	end_j = freq.length - 1;
	end_j = freq.length - 1 - 1;
	for (j=0;j<end_j;j++)
		{
		for (i=0;i<end_i;i++)
			{
			m = CES.func.ord(input[i]);
			n = m & 0x03; m >>= 2; CES.func.mapAddAll(j,order[n]);
			n = m & 0x03; m >>= 2; CES.func.mapAddAll(j,order[n]);
			n = m & 0x03; m >>= 2; CES.func.mapAddAll(j,order[n]);
			n = m & 0x03;          CES.func.mapAddAll(j,order[n]);
			}
//	CES.vars.map += '<br>';
		}
//	CES.vars.map += ' '+(CES.vars.map.length-3)+'/'+(input.length<<3) + ' '+ input.length;
	if (CES.vars.map_bites>0)
		{
		CES.func.mapAddByte();
		}

	CES.vars.mapx = CES.vars.map;
	CES.func.mapAddReset();
	CES.vars.map = '';
	end_j = freq.length - 1;
	for (j=2;j<end_j;j++)
		{
		for (i=0;i<end_i;i++)
			{
			m = CES.func.ord(input[i]);
			n = m & 0x03; m >>= 2; CES.func.mapAddAll(j,order[n]);
			n = m & 0x03; m >>= 2; CES.func.mapAddAll(j,order[n]);
			n = m & 0x03; m >>= 2; CES.func.mapAddAll(j,order[n]);
			n = m & 0x03;          CES.func.mapAddAll(j,order[n]);
			}
		}
/*
	CES.vars.mapy = CES.vars.map;
	CES.func.mapAddReset();
	CES.vars.map = '';
	end_j = freq.length - 1;
	for (j=2;j<end_j;j++)
		{
		for (i=0;i<end_i;i++)
			{
			m = CES.func.ord(input[i]);
			n = m & 0x03; m >>= 2; CES.func.mapAddAll(j,order[n]);
			n = m & 0x03; m >>= 2; CES.func.mapAddAll(j,order[n]);
			n = m & 0x03; m >>= 2; CES.func.mapAddAll(j,order[n]);
			n = m & 0x03;          CES.func.mapAddAll(j,order[n]);
			}
		}
*/
	return CES.vars.map;
	}

// compress 2


CES.func.maplistReset = function()
	{
	var list;
	list = {};
	list[0] = {map: '', count: 0, bites: 7, byte: 0};
	list[1] = {map: '', count: 0, bites: 7, byte: 0};
	list[2] = {map: '', count: 0, bites: 7, byte: 0};
	list[3] = {map: '', count: 0, bites: 7, byte: 0};
	CES.vars.map_list = list;
	}

CES.func.maplistExport = function()
	{
//alert(JSON.stringify(CES.vars.map_list));
	var list = CES.vars.map_list;
	return list[0].map + list[1].map + list[2].map + list[3].map ;
	}


CES.func.maplistAddReset = function(m)
	{
	CES.vars.map_list[m].byte  = 0;
	CES.vars.map_list[m].bites = 7;
	}

CES.func.maplistAddByte = function(m, byte)
	{
	CES.vars.map_list[m].map += CES.func.chr(byte);
	CES.vars.map_list[m].count++;
	CES.func.maplistAddReset();
	}

CES.func.maplistAdd = function(m, bite)	//,n
	{
	var b;	//,n
	b = CES.vars.map_list[m].bites;
	//bite = (bool * 1);
	CES.vars.map_byte = CES.vars.map_list[m].byte | (bite << b);
	if (b>0)
		{
		b--;
		CES.vars.map_list[m].bites = b;
		return;
		}
	CES.func.maplistAddByte(m, CES.vars.map_byte);
	}

CES.func.maplistAddAll = function(value, freq_list)
	{
	if (value==freq_list[0])
		{
		CES.func.maplistAdd(0, 0);
		}
	else { if (value==freq_list[1])
		{
		CES.func.maplistAdd(0, 1);
		CES.func.maplistAdd(1, 0);
		}
	else { if (value==freq_list[2])
		{
		CES.func.maplistAdd(0, 1);
		CES.func.maplistAdd(1, 1);
		CES.func.maplistAdd(2, 0);
		}
	else	{
		CES.func.maplistAdd(0, 1);
		CES.func.maplistAdd(1, 1);
		CES.func.maplistAdd(2, 1);
		}}}
	}

/*
CES.func.mapAddAllHuff = function(value)	// map=bite2
	{
	if (value==0)
		{
		CES.func.maplistAdd(0, 0);
		}
	else	{	// m==1 || m==2 || m==3
		CES.func.maplistAdd(value>>1);
		CES.func.maplistAdd(value & 0x01);
		}
	}
*/
CES.func.huffEncode2 = function (data, order)
	{
	var fn, freq, order, map,i,end_i,j,end_j, m,n;
	fn = CES.func;
//	freq  = CES.func.frequencyDetect(input);
//	order = [0,1,2,3];
//	order.sort(function(b,a){return freq[a]-freq[b];});	//desc
	fn.mapAddReset();
	CES.vars.map = '';
	CES.vars.map_count = 0;
	end_i = data.length;
	for (i=0;i<end_i;i++)
		{
		m = CES.func.ord(data[i]);
		n = m & 0x03; m >>= 2; fn.huffEncodeOne(order[n]);
		n = m & 0x03; m >>= 2; fn.huffEncodeOne(order[n]);
		n = m & 0x03; m >>= 2; fn.huffEncodeOne(order[n]);
		n = m & 0x03;          fn.huffEncodeOne(order[n]);
		}
	if (CES.vars.map_bites!==7)
		{
		CES.func.mapAddByte();
		}
	return CES.vars.map;
	}

CES.func.compressEncode = function (data)	// vynechavam predchozi kod a radeji si napisi vlastni novou compress
	{
	var fn, map, i, i_end, m, m_end, value, m, n, new_data;
	fn = CES.func;
	fn.maplistReset();
	order = [0, 1, 2, 3]; //	{0:0, 1:1, 2:2, 3:3}
//	order.sort(function(b,a){return freq[a]-freq[b];});	//desc
//	alert([freq, order]);
	// mapping
	end_i = data.length;
	for (i=0; i<i_end; i++)
		{
		value = fn.ord(data[i]);
		CES.func.maplistAddAll(value, freq_list);
		n = value & 0x03; value >>= 2; fn.maplistAddAll(n, freq_list);
		n = value & 0x03; value >>= 2; fn.maplistAddAll(n, freq_list);
		n = value & 0x03; value >>= 2; fn.maplistAddAll(n, freq_list);
		n = value & 0x03;              fn.maplistAddAll(n, freq_list);
		}
	// add last byte
	end_m = 4;
	for (m=0; m<m_end; m++)
		{
		if (CES.vars.map_list[m].bites!==7)
			{
			fn.maplistAddByte(m, CES.vars.map_list[m].byte);
			}
		}
	// huffman code maps
	for (m=0; m<m_end; m++)
		{
		order = [0, 1, 2, 3];
		data  = CES.vars.map_list[m].map;
		end_i = data.length;
		for (i=0; i<i_end; i++)
			{
			new_data = fn.huffEncode2(data, order);
			}
		if (new_data.length<end_i)
			{
			CES.vars.out_info[m+1] = 1;
			CES.vars.map_list[m].map = data;
			}
		else	{
			CES.vars.out_info[m+1] = 0;
			}
		}	
	return fn.maplistExport(); // + info
	}

CES.func.compress = function ()
	{
	var freq, out, inp, el_inp, el_out, el_stat;
	// input read
	el_inp  = document.getElementById('inp');
	el_out  = document.getElementById('out');
	el_stat = document.getElementById('stat');
	inp     = el_inp.value;
	CES.vars.out_info[0] = inp.length;	// out info
	inp     = CES.func.base64Decode(inp);

//	CES.vars.input = input;
//	freq = CES.func.frequencyDetect(inp);
//	out = CES.func.inputToMap(freq, inp)
//	out = CES.func.huff(out);
//	out += CES.func.huff(CES.vars.mapx);
//	out += CES.func.huff(CES.vars.mapy);

CES.vars.map_list
	out = CES.func.compressEncode(inp);

	// output write
	out = CES.func.base64Encode(out);
	el_out.value  = out;
	el_stat.innerHTML  = 'out/inp = ' + out.length + ' / ' + inp.length + ' | '+(Math.floor((out.length/inp.length)*100)/10000)+'%';
//	el_stat.innerHTML += 'out = ' + out.length + ' / ' + inp.length + ' = inp; '+(Math.floor((out.length/inp.length)*100)/10000)+'%';
	}


CES.func.init = function ()
	{
	CES.vars.tab = CES.func.tablesCreate();
	document.getElementById('inp').value = 'R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7'; // image star.gif
	}

var input, data_star;
CES.func.init();
CES.func.compress();
</script>

