Skip to content

Commit d9c491e

Browse files
authored
Merge pull request #1911 from soma2000-lang/mini
File Zipper
2 parents 603e44a + 8878c2f commit d9c491e

7 files changed

Lines changed: 384 additions & 1 deletion

File tree

assets/Images/File_Zipper.jpeg

67.5 KB
Loading

assets/css/File_Zipper.css

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
html,
2+
body {
3+
height: 100%;
4+
}
5+
6+
.text_box {
7+
padding: 30px;
8+
width: 50%;
9+
height: 100%;
10+
border: 1px solid lightgray;
11+
display: flex;
12+
flex-wrap: wrap;
13+
align-content: center;
14+
}
15+
16+
#container{
17+
width: 100%;
18+
height: 70%;
19+
background-color: white;
20+
display: flex;
21+
margin: 0 auto;
22+
}
23+
24+
.center_buttons{
25+
margin:auto;
26+
display:block;
27+
}

assets/js/File_Zipper_js/heap.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* Created by aarnavjindal on 30/03/20.
3+
*/
4+
5+
export { BinaryHeap }
6+
7+
class BinaryHeap {
8+
9+
constructor() {
10+
this.heap = [];
11+
}
12+
13+
insert(value) {
14+
this.heap.push(value);
15+
this.bubbleUp();
16+
}
17+
18+
size() {
19+
return this.heap.length;
20+
}
21+
22+
empty(){
23+
return ( this.size()===0 );
24+
}
25+
26+
//using iterative approach
27+
bubbleUp() {
28+
let index = this.size() - 1;
29+
30+
while (index > 0) {
31+
let element = this.heap[index],
32+
parentIndex = Math.floor((index - 1) / 2),
33+
parent = this.heap[parentIndex];
34+
35+
if (parent[0] >= element[0]) break;
36+
this.heap[index] = parent;
37+
this.heap[parentIndex] = element;
38+
index = parentIndex
39+
}
40+
}
41+
42+
extractMax() {
43+
const max = this.heap[0];
44+
const tmp = this.heap.pop();
45+
if(!this.empty()) {
46+
this.heap[0] = tmp;
47+
this.sinkDown(0);
48+
}
49+
return max;
50+
}
51+
52+
sinkDown(index) {
53+
54+
let left = 2 * index + 1,
55+
right = 2 * index + 2,
56+
largest = index;
57+
const length = this.size();
58+
59+
// console.log(this.heap[left], left, length, this.heap[right], right, length, this.heap[largest]);
60+
61+
if (left < length && this.heap[left][0] > this.heap[largest][0]) {
62+
largest = left
63+
}
64+
if (right < length && this.heap[right][0] > this.heap[largest][0]) {
65+
largest = right
66+
}
67+
// swap
68+
if (largest !== index) {
69+
let tmp = this.heap[largest];
70+
this.heap[largest] = this.heap[index];
71+
this.heap[index] = tmp;
72+
this.sinkDown(largest)
73+
}
74+
}
75+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
2+
import { BinaryHeap } from './heap.js';
3+
4+
export { HuffmanCoder }
5+
6+
class HuffmanCoder{
7+
8+
stringify(node){
9+
if(typeof(node[1])==="string"){
10+
return '\''+node[1];
11+
}
12+
13+
return '0' + this.stringify(node[1][0]) + '1' + this.stringify(node[1][1]);
14+
}
15+
16+
display(node, modify, index=1){
17+
if(modify){
18+
node = ['',node];
19+
if(node[1].length===1)
20+
node[1] = node[1][0];
21+
}
22+
23+
if(typeof(node[1])==="string"){
24+
return String(index) + " = " + node[1];
25+
}
26+
27+
let left = this.display(node[1][0], modify, index*2);
28+
let right = this.display(node[1][1], modify, index*2+1);
29+
let res = String(index*2)+" <= "+index+" => "+String(index*2+1);
30+
return res + '\n' + left + '\n' + right;
31+
}
32+
destringify(data){
33+
let node = [];
34+
if(data[this.ind]==='\''){
35+
this.ind++;
36+
node.push(data[this.ind]);
37+
this.ind++;
38+
return node;
39+
}
40+
41+
this.ind++;
42+
let left = this.destringify(data);
43+
node.push(left);
44+
this.ind++;
45+
let right = this.destringify(data);
46+
node.push(right);
47+
48+
return node;
49+
}
50+
51+
52+
getMappings(node, path){
53+
if(typeof(node[1])==="string"){
54+
this.mappings[node[1]] = path;
55+
return;
56+
}
57+
58+
this.getMappings(node[1][0], path+"0");
59+
this.getMappings(node[1][1], path+"1");
60+
}
61+
62+
encode(data){
63+
64+
this.heap = new BinaryHeap();
65+
66+
const mp = new Map();
67+
for(let i=0;i<data.length;i++){
68+
if(data[i] in mp){
69+
mp[data[i]] = mp[data[i]] + 1;
70+
} else{
71+
mp[data[i]] = 1;
72+
}
73+
}
74+
75+
for(const key in mp){
76+
this.heap.insert([-mp[key], key]);
77+
}
78+
79+
while(this.heap.size() > 1){
80+
const node1 = this.heap.extractMax();
81+
const node2 = this.heap.extractMax();
82+
83+
const node = [node1[0]+node2[0],[node1,node2]];
84+
this.heap.insert(node);
85+
}
86+
const huffman_encoder = this.heap.extractMax();
87+
88+
this.mappings = {};
89+
this.getMappings(huffman_encoder, "");
90+
91+
let binary_string = "";
92+
for(let i=0;i<data.length;i++) {
93+
binary_string = binary_string + this.mappings[data[i]];
94+
}
95+
96+
let rem = (8 - binary_string.length%8)%8;
97+
let padding = "";
98+
for(let i=0;i<rem;i++)
99+
padding = padding + "0";
100+
binary_string = binary_string + padding;
101+
102+
let result = "";
103+
for(let i=0;i<binary_string.length;i+=8){
104+
let num = 0;
105+
for(let j=0;j<8;j++){
106+
num = num*2 + (binary_string[i+j]-"0");
107+
}
108+
result = result + String.fromCharCode(num);
109+
}
110+
111+
let final_res = this.stringify(huffman_encoder) + '\n' + rem + '\n' + result;
112+
let info = "Compression Ratio : " + data.length/final_res.length;
113+
info = "Compression complete and file sent for download" + '\n' + info;
114+
return [final_res, this.display(huffman_encoder, false), info];
115+
}
116+
117+
decode(data){
118+
data = data.split('\n');
119+
if(data.length===4){
120+
// Handling new line
121+
data[0] = data[0] + '\n' + data[1];
122+
data[1] = data[2];
123+
data[2] = data[3];
124+
data.pop();
125+
}
126+
127+
this.ind = 0;
128+
const huffman_decoder = this.destringify(data[0]);
129+
const text = data[2];
130+
131+
let binary_string = "";
132+
for(let i=0;i<text.length;i++){
133+
let num = text[i].charCodeAt(0);
134+
let bin = "";
135+
for(let j=0;j<8;j++){
136+
bin = num%2 + bin;
137+
num = Math.floor(num/2);
138+
}
139+
binary_string = binary_string + bin;
140+
}
141+
binary_string = binary_string.substring(0,binary_string.length-data[1]);
142+
143+
console.log(binary_string.length);
144+
145+
let res = "";
146+
let node = huffman_decoder;
147+
for(let i=0;i<binary_string.length;i++){
148+
if(binary_string[i]==='0'){
149+
node = node[0];
150+
} else{
151+
node = node[1];
152+
}
153+
154+
if(typeof(node[0])==="string"){
155+
res += node[0];
156+
node = huffman_decoder;
157+
}
158+
}
159+
let info = "Decompression complete and file sent for download";
160+
return [res, this.display(huffman_decoder, true), info];
161+
}
162+
}

assets/js/File_Zipper_js/script.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { HuffmanCoder } from './huffman.js';
2+
3+
4+
onload = function () {
5+
// Get reference to elements
6+
const treearea = document.getElementById('treearea');
7+
const encode = document.getElementById('encode');
8+
const decode = document.getElementById('decode');
9+
const temptext = document.getElementById('temptext');
10+
const upload = document.getElementById('uploadedFile');
11+
12+
const coder = new HuffmanCoder();
13+
14+
upload.addEventListener('change',()=>{ alert("File uploaded") });
15+
16+
encode.onclick = function () {
17+
18+
const uploadedFile = upload.files[0];
19+
if(uploadedFile===undefined){
20+
alert("No file uploaded !");
21+
return;
22+
}
23+
const fileReader = new FileReader();
24+
fileReader.onload = function(fileLoadedEvent){
25+
const text = fileLoadedEvent.target.result;
26+
if(text.length===0){
27+
alert("Text can not be empty ! Upload another file !");
28+
return;
29+
}
30+
let [encoded, tree_structure, info] = coder.encode(text);
31+
downloadFile(uploadedFile.name.split('.')[0] +'_encoded.txt', encoded);
32+
treearea.innerText = tree_structure;
33+
treearea.style.marginTop = '2000px';
34+
temptext.innerText = info;
35+
};
36+
fileReader.readAsText(uploadedFile, "UTF-8");
37+
};
38+
39+
decode.onclick = function () {
40+
41+
const uploadedFile = upload.files[0];
42+
if(uploadedFile===undefined){
43+
alert("No file uploaded !");
44+
return;
45+
}
46+
const fileReader = new FileReader();
47+
fileReader.onload = function(fileLoadedEvent){
48+
const text = fileLoadedEvent.target.result;
49+
if(text.length===0){
50+
alert("Text can not be empty ! Upload another file !");
51+
return;
52+
}
53+
let [decoded, tree_structure, info] = coder.decode(text);
54+
downloadFile(uploadedFile.name.split('.')[0] +'_decoded.txt', decoded);
55+
treearea.innerText = tree_structure;
56+
treearea.style.marginTop = '2000px';
57+
temptext.innerText = info;
58+
};
59+
fileReader.readAsText(uploadedFile, "UTF-8");
60+
};
61+
62+
};
63+
64+
function downloadFile(fileName, data){
65+
let a = document.createElement('a');
66+
a.href = "data:application/octet-stream,"+encodeURIComponent(data);
67+
a.download = fileName;
68+
a.click();
69+
}

public/File_Zipper.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Document</title>
8+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
9+
10+
<link rel="stylesheet" href='../assets/css/File_Zipper.css'>
11+
<script src='../assets/css/File_Zipper.css'></script>
12+
<script src="../assets/js/File_Zipper_js/script.js" type="module"></script>
13+
14+
</head>
15+
16+
<body>
17+
<nav class="navbar navbar-light " style="font-size: 25px; font-family: sans-serif; background-color: whitesmoke;">
18+
19+
20+
File Zipper(only .txt files can be encoded and decoded as of now)
21+
</nav>
22+
<div id="container">
23+
<div class="text_box" style="overflow-y: scroll">
24+
<span id="treearea" style="width: 100%; text-align: center; font-size: medium;">
25+
Tree Structure Will Be Displayed Here !!
26+
</span>
27+
</div>
28+
<div class="text_box" style="overflow-y: scroll">
29+
<span id="temptext" style="width: 100%; text-align: center; font-size: x-large;">
30+
Operation info will be shown here !!
31+
</span>
32+
</div>
33+
</div>
34+
35+
<div>
36+
<form method="post" enctype="multipart/form-data" style="display: inline-block;">
37+
<input type="file" id="uploadedFile" />
38+
</form>
39+
<br>
40+
<button type="button" class="btn btn-success center_buttons" id="encode">&nbsp;&nbsp;Encode&nbsp;&nbsp;</button>
41+
<br>
42+
<button type="button" class="btn btn-danger center_buttons" id="decode">&nbsp;&nbsp;Decode&nbsp;&nbsp;</button>
43+
</div>
44+
</body>
45+
46+
</html>

0 commit comments

Comments
 (0)