-
Notifications
You must be signed in to change notification settings - Fork 0
/
script.js
172 lines (145 loc) · 4.99 KB
/
script.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
"use strict";
class CharFreq {
constructor(char, count) {
this.char = char;
this.count = count;
}
}
class HuffmanCode {
constructor(char, code) {
this.char = char;
this.code = code;
}
}
class HuffmanNode {
constructor(char, count) {
this.char = char;
this.count = count;
this.leftNode = null;
this.rightNode = null;
}
}
class HuffmanEncoding {
constructor(str) {
this.str = str;
}
// ### Create a frequency map which is basically a list of all characters and how many times they are present in the text ### //
get createFreqMap() {
let freqMap = [];
for (let char of this.str) {
let isFound = false;
for (let i = 0; i < freqMap.length; i++) {
if (freqMap[i].char === char) {
freqMap[i].count++;
isFound = true;
}
}
if (!isFound) freqMap.push(new CharFreq(char, 1));
}
freqMap.sort((a, b) => a.count - b.count);
return freqMap;
}
// ### Builds the Huffman Tree from the frequency map ### //
buildHuffmanTree(freqMap = this.createFreqMap) {
let priorityQueue = [];
freqMap.forEach(curr => priorityQueue.push(new HuffmanNode(curr.char, curr.count)));
while(priorityQueue.length > 1) {
const leftNode = priorityQueue.shift();
const rightNode = priorityQueue.shift();
const parentNode = new HuffmanNode("*", leftNode.count + rightNode.count);
parentNode.leftNode = leftNode;
parentNode.rightNode = rightNode;
priorityQueue.push(parentNode);
priorityQueue.sort((a, b) => a.count - b.count);
}
return priorityQueue[0];
}
// ### Generate the encode for all characters in text from the huffman tree ### //
generateHuffmanCodes(node, code = "", codes = []) {
if (node) {
if(!node.leftNode && !node.rightNode) {
codes.push(new HuffmanCode(node.char, code));
} else {
this.generateHuffmanCodes(node.leftNode, code + "0", codes);
this.generateHuffmanCodes(node.rightNode, code + "1", codes);
}
}
return codes;
}
// ### Encode the text ### //
encode() {
let freqMap = this.createFreqMap;
let huffmanTree = this.buildHuffmanTree(freqMap);
let huffmanCodes = this.generateHuffmanCodes(huffmanTree);
let encodedText = "";
for (let char of this.str) {
encodedText += huffmanCodes.filter(curr => curr.char === char).map(curr => curr.code).toString();
}
return encodedText;
}
// ### Decode the text from a encoded text and a huffman tree ### //
static decode(encodedText, rootNode) {
let decodedText = "";
let currentNode = rootNode;
for (let bit of encodedText) {
if (bit === "0") currentNode = currentNode.leftNode;
if (bit === "1") currentNode = currentNode.rightNode;
if (!currentNode.leftNode && !currentNode.rightNode) {
decodedText += currentNode.char;
currentNode = rootNode;
}
}
return decodedText;
}
static compare(str, code) {
let bitCount = 0;
for (let _char in str) bitCount += 8;
let encodeBit = 0;
for (let _bit in code) encodeBit++;
return [bitCount, encodeBit];
}
}
// ### HTML element references ### //
const inputMessage = document.getElementById("input-message");
const encodeBtn = document.getElementById("encode-btn");
const encodedMessege = document.getElementById("encoded-text");
const comparedRes = document.getElementById('compare');
const huffmanTree = document.getElementById("huffman-tree");
const inputEncode = document.getElementById("input-encode");
const inputHFTree = document.getElementById("input-hf-tree");
const decodeBtn = document.getElementById("decode-btn");
const decodedMessege = document.getElementById("decoded-text");
const toast = document.getElementById('toast');
// ### Encode on click ### //
encodeBtn.addEventListener("click", () => {
let message = new HuffmanEncoding(inputMessage.value);
let hfTree = message.buildHuffmanTree();
let encodedData = message.encode();
encodedMessege.textContent = encodedData;
let comparision = HuffmanEncoding.compare(inputMessage.value, encodedData);
comparedRes.textContent = `[ ${comparision[0]}bit → ${comparision[1]}bit]`;
huffmanTree.textContent = JSON.stringify(hfTree);
});
encodedMessege.addEventListener("click", () => {
navigator.clipboard.writeText(encodedMessege.textContent);
showToast();
});
huffmanTree.addEventListener("click", () => {
navigator.clipboard.writeText(huffmanTree.textContent);
showToast();
});
// ### Decode on click from the input encode and input huffman tree ### //
decodeBtn.addEventListener("click", () => {
let encodedText = inputEncode.value;
let rootNode = JSON.parse(inputHFTree.value);
let decodedText = HuffmanEncoding.decode(encodedText, rootNode);
decodedMessege.textContent = decodedText;
});
decodedMessege.addEventListener("click", () => {
navigator.clipboard.writeText(decodedMessege.textContent);
showToast();
});
function showToast() {
toast.classList.remove('hide');
setTimeout(() => toast.classList.add('hide'), 1000);
}