pols commited on
Commit
350a7cf
·
1 Parent(s): d9abb7e

Upload 2 files

Browse files
Files changed (2) hide show
  1. script.js +359 -0
  2. styles.css +136 -0
script.js ADDED
@@ -0,0 +1,359 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const inputText = document.getElementById('input-text');
2
+ const bubbleContainer = document.getElementById('bubble-container');
3
+ const replaceSpacesButton = document.getElementById('replace-spaces-button');
4
+ const replaceUnderscoreButton = document.getElementById('replace-underscore-button');
5
+ const revertUnderscoreButton = document.getElementById('revert-underscore-button');
6
+ const replaceLineBreaksButton = document.getElementById('replace-linebreaks-button');
7
+ const replaceCommasButton = document.getElementById('replace-commas-button');
8
+ const removeDuplicatesButton = document.getElementById('remove-duplicates-button');
9
+ const revertSpacesButton = document.getElementById('revert-spaces-button');
10
+ const copyButton = document.getElementById('copy-button');
11
+ const undoButton = document.getElementById('undo-button');
12
+ const redoButton = document.getElementById('redo-button');
13
+ const deleteModeButton = document.getElementById('delete-mode-button');
14
+ const emphasisModeButton = document.getElementById('emphasis-mode-button');
15
+ const mitigateModeButton = document.getElementById('mitigate-mode-button');
16
+
17
+ const bubbleColors = {};
18
+
19
+ let history = [];
20
+ let historyIndex = -1;
21
+
22
+ let deleteModeEnabled = false;
23
+
24
+ let emphasisModeEnabled = false;
25
+
26
+ let mitigateModeEnabled = false;
27
+
28
+ inputText.addEventListener('input', handleTextChange);
29
+ replaceSpacesButton.addEventListener('click', replaceSpaces);
30
+ replaceUnderscoreButton.addEventListener('click', replaceUnderscores);
31
+ revertUnderscoreButton.addEventListener('click', revertUnderscores);
32
+ replaceLineBreaksButton.addEventListener('click', replaceLineBreaks);
33
+ replaceCommasButton.addEventListener('click', replaceCommas);
34
+ removeDuplicatesButton.addEventListener('click', removeDuplicates);
35
+ revertSpacesButton.addEventListener('click', revertSpaces);
36
+ copyButton.addEventListener('click', copyToClipboard);
37
+ undoButton.addEventListener('click', undoChanges);
38
+ redoButton.addEventListener('click', redoChanges);
39
+ deleteModeButton.addEventListener('click', toggleDeleteMode);
40
+ emphasisModeButton.addEventListener('click', toggleEmphasisMode);
41
+ mitigateModeButton.addEventListener('click', toggleMitigateMode);
42
+
43
+ const sortable = new Sortable(bubbleContainer, {
44
+ animation: 250,
45
+ ghostClass: 'ghost',
46
+ onEnd: handleBubbleChange,
47
+ disabled: deleteModeEnabled || emphasisModeEnabled || mitigateModeEnabled
48
+ });
49
+
50
+ function handleTextChange() {
51
+ const text = inputText.value;
52
+
53
+ if (history.length === 0) {
54
+ history.push('');
55
+ historyIndex++;
56
+ }
57
+
58
+ if (text !== history[historyIndex]) {
59
+ if (historyIndex < history.length - 1) {
60
+
61
+ history.splice(historyIndex + 1);
62
+ }
63
+
64
+ history.push(text);
65
+ historyIndex++;
66
+
67
+ updateBubbles();
68
+ updateButtonStates();
69
+ }
70
+ }
71
+
72
+ function handleBubbleChange() {
73
+ const conceptList = Array.from(bubbleContainer.getElementsByClassName('bubble')).map(bubble => bubble.textContent.trim());
74
+ inputText.value = conceptList.join(', ');
75
+ handleTextChange();
76
+ }
77
+
78
+ function updateBubbles() {
79
+ bubbleContainer.innerHTML = '';
80
+ const text = inputText.value.trim();
81
+
82
+ if (text === '') return;
83
+
84
+ const concepts = text.split(', ');
85
+
86
+ concepts.forEach(concept => {
87
+ const bubble = createBubble(concept);
88
+ bubbleContainer.appendChild(bubble);
89
+ });
90
+ }
91
+
92
+ function createBubble(text) {
93
+ const bubble = document.createElement('div');
94
+ bubble.classList.add('bubble');
95
+
96
+ if (!bubbleColors.hasOwnProperty(text)) {
97
+ bubbleColors[text] = getRandomColor();
98
+ }
99
+
100
+ bubble.style.backgroundColor = bubbleColors[text];
101
+
102
+ const bubbleText = document.createElement('span');
103
+ bubbleText.classList.add('bubble-text');
104
+ bubbleText.innerText = text;
105
+
106
+ bubble.appendChild(bubbleText);
107
+
108
+ if (deleteModeEnabled) {
109
+ bubble.classList.add('delete-mode');
110
+ bubble.addEventListener('click', deleteBubble);
111
+ }
112
+
113
+ if (emphasisModeEnabled) {
114
+ bubble.classList.add('emphasis-mode');
115
+ bubble.addEventListener('click', emphasizeBubble);
116
+ }
117
+
118
+ if (mitigateModeEnabled) {
119
+ bubble.classList.add('mitigate-mode');
120
+ bubble.addEventListener('click', mitigateBubble);
121
+ }
122
+
123
+ return bubble;
124
+ }
125
+
126
+ function getRandomColor() {
127
+ const h = Math.floor(Math.random() * 360);
128
+ const s = Math.floor(Math.random() * 30) + 50;
129
+ const l = Math.floor(Math.random() * 40) + 30;
130
+
131
+ return `hsl(${h}, ${s}%, ${l}%)`;
132
+ }
133
+
134
+ function updateButtonStates() {
135
+ undoButton.disabled = historyIndex === 0;
136
+ redoButton.disabled = historyIndex === history.length - 1;
137
+ }
138
+
139
+ function replaceSpaces() {
140
+ const text = inputText.value;
141
+ const replacedText = text.replace(/ /g, ', ');
142
+ inputText.value = replacedText;
143
+ handleTextChange();
144
+ }
145
+
146
+ function revertSpaces() {
147
+ const text = inputText.value;
148
+ const replacedText = text.replace(/, /g, ' ');
149
+ inputText.value = replacedText;
150
+ handleTextChange();
151
+ }
152
+
153
+ function replaceUnderscores() {
154
+ const text = inputText.value;
155
+ const replacedText = text.replace(/_/g, ' ');
156
+ inputText.value = replacedText;
157
+ handleTextChange();
158
+ }
159
+
160
+ function revertUnderscores() {
161
+ const text = inputText.value;
162
+ const replacedText = text.replace(/([^,]) /g, '$1_');
163
+ inputText.value = replacedText;
164
+ handleTextChange();
165
+ }
166
+
167
+ function replaceLineBreaks() {
168
+ const text = inputText.value;
169
+ const replacedText = text.replace(/\n/g, ', ');
170
+ inputText.value = replacedText;
171
+ handleTextChange();
172
+ }
173
+
174
+ function replaceCommas() {
175
+ const text = inputText.value;
176
+ const step1 = text.replace(/,,/g, ',');
177
+ const step2 = step1.replace(/, ,/g, ', ');
178
+ const step3 = step2.trimRight();
179
+ const step4 = step3.replace(/,$/, '');
180
+ const step5 = step4.replace(/^,/, '');
181
+ const step6 = step5.replace(/, /g, ', ');
182
+ const replacedText = step6.replace(/^ /, '');
183
+
184
+ inputText.value = replacedText;
185
+ handleTextChange();
186
+ }
187
+
188
+ function removeDuplicates() {
189
+ const text = inputText.value;
190
+ const concepts = text.split(', ');
191
+
192
+ const uniqueConcepts = [...new Set(concepts)];
193
+
194
+ inputText.value = uniqueConcepts.join(', ');
195
+ handleTextChange();
196
+ }
197
+
198
+
199
+ function copyToClipboard() {
200
+ inputText.select();
201
+ inputText.setSelectionRange(0, 99999);
202
+ document.execCommand('copy');
203
+ }
204
+
205
+ function undoChanges() {
206
+ if (historyIndex > 0) {
207
+ historyIndex--;
208
+ inputText.value = history[historyIndex];
209
+ updateBubbles();
210
+ updateButtonStates();
211
+ }
212
+ }
213
+
214
+ function redoChanges() {
215
+ if (historyIndex < history.length - 1) {
216
+ historyIndex++;
217
+ inputText.value = history[historyIndex];
218
+ updateBubbles();
219
+ updateButtonStates();
220
+ }
221
+ }
222
+
223
+ function toggleDeleteMode() {
224
+ deleteModeEnabled = !deleteModeEnabled;
225
+ deleteModeButton.classList.toggle('active');
226
+
227
+ if (deleteModeEnabled) {
228
+ emphasisModeEnabled = false;
229
+ emphasisModeButton.classList.remove('active');
230
+ mitigateModeEnabled = false;
231
+ mitigateModeButton.classList.remove('active');
232
+ }
233
+
234
+ sortable.option('disabled', deleteModeEnabled);
235
+ bubbleContainer.classList.toggle('delete-mode-container');
236
+
237
+ const bubbles = bubbleContainer.getElementsByClassName('bubble');
238
+
239
+ for (let i = 0; i < bubbles.length; i++) {
240
+ const bubble = bubbles[i];
241
+ bubble.classList.remove('emphasis-mode');
242
+ bubble.classList.remove('mitigate-mode');
243
+ if (deleteModeEnabled) {
244
+ bubble.classList.add('delete-mode');
245
+ bubble.removeEventListener('click', emphasizeBubble);
246
+ bubble.removeEventListener('click', mitigateBubble);
247
+ bubble.addEventListener('click', deleteBubble);
248
+ } else {
249
+ bubble.classList.remove('delete-mode');
250
+ bubble.removeEventListener('click', deleteBubble);
251
+ }
252
+ }
253
+ }
254
+
255
+ function toggleEmphasisMode() {
256
+ emphasisModeEnabled = !emphasisModeEnabled;
257
+ emphasisModeButton.classList.toggle('active');
258
+
259
+ if (emphasisModeEnabled) {
260
+ deleteModeEnabled = false;
261
+ deleteModeButton.classList.remove('active');
262
+ mitigateModeEnabled = false;
263
+ mitigateModeButton.classList.remove('active');
264
+ }
265
+
266
+ sortable.option('disabled', emphasisModeEnabled);
267
+ bubbleContainer.classList.toggle('emphasis-mode-container');
268
+
269
+ const bubbles = bubbleContainer.getElementsByClassName('bubble');
270
+
271
+ for (let i = 0; i < bubbles.length; i++) {
272
+ const bubble = bubbles[i];
273
+ bubble.classList.remove('delete-mode');
274
+ bubble.classList.remove('mitigate-mode');
275
+ if (emphasisModeEnabled) {
276
+ bubble.classList.add('emphasis-mode');
277
+ bubble.removeEventListener('click', deleteBubble);
278
+ bubble.removeEventListener('click', mitigateBubble);
279
+ bubble.addEventListener('click', emphasizeBubble);
280
+ } else {
281
+ bubble.classList.remove('emphasis-mode');
282
+ bubble.removeEventListener('click', emphasizeBubble);
283
+ }
284
+ }
285
+ }
286
+
287
+ function toggleMitigateMode() {
288
+ mitigateModeEnabled = !mitigateModeEnabled;
289
+ mitigateModeButton.classList.toggle('active');
290
+
291
+ if (mitigateModeEnabled) {
292
+ deleteModeEnabled = false;
293
+ deleteModeButton.classList.remove('active');
294
+ emphasisModeEnabled = false;
295
+ emphasisModeButton.classList.remove('active');
296
+ }
297
+
298
+ sortable.option('disabled', mitigateModeEnabled);
299
+ bubbleContainer.classList.toggle('mitigate-mode-container');
300
+
301
+ const bubbles = bubbleContainer.getElementsByClassName('bubble');
302
+
303
+ for (let i = 0; i < bubbles.length; i++) {
304
+ const bubble = bubbles[i];
305
+ bubble.classList.remove('delete-mode');
306
+ bubble.classList.remove('emphasis-mode');
307
+ if (mitigateModeEnabled) {
308
+ bubble.classList.add('mitigate-mode');
309
+ bubble.removeEventListener('click', deleteBubble);
310
+ bubble.removeEventListener('click', emphasizeBubble);
311
+ bubble.addEventListener('click', mitigateBubble);
312
+ } else {
313
+ bubble.classList.remove('mitigate-mode');
314
+ bubble.removeEventListener('click', mitigateBubble);
315
+ }
316
+ }
317
+ }
318
+
319
+ function deleteBubble(event) {
320
+ const bubble = event.target.closest('.bubble');
321
+ const bubbleText = bubble.querySelector('.bubble-text');
322
+ const nextComma = bubbleText.nextSibling;
323
+
324
+ bubble.remove();
325
+
326
+ if (nextComma && nextComma.nodeType === Node.TEXT_NODE && nextComma.textContent.trim() === ',') {
327
+ nextComma.remove();
328
+ }
329
+
330
+ handleBubbleChange();
331
+ }
332
+
333
+ function emphasizeBubble(event) {
334
+ const bubble = event.target.closest('.bubble');
335
+ const bubbleText = bubble.querySelector('.bubble-text');
336
+ const text = bubbleText.innerText.trim();
337
+
338
+ if (text.startsWith('[') && text.endsWith(']')) {
339
+ bubbleText.innerText = text.slice(1, -1);
340
+ } else {
341
+ bubbleText.innerText = `(${text})`;
342
+ }
343
+
344
+ handleBubbleChange();
345
+ }
346
+
347
+ function mitigateBubble(event) {
348
+ const bubble = event.target.closest('.bubble');
349
+ const bubbleText = bubble.querySelector('.bubble-text');
350
+ const text = bubbleText.innerText.trim();
351
+
352
+ if (text.startsWith('(') && text.endsWith(')')) {
353
+ bubbleText.innerText = text.slice(1, -1);
354
+ } else {
355
+ bubbleText.innerText = `[${text}]`;
356
+ }
357
+
358
+ handleBubbleChange();
359
+ }
styles.css ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ background-color: #1C1C1C;
3
+ font-family: "Signika", sans-serif;
4
+ user-select: none;
5
+ }
6
+
7
+ .buttons {
8
+ padding: 4px 10px;
9
+ border: none;
10
+ border-radius: 20px;
11
+ font-size: 15px;
12
+ font-family: "Arial", sans-serif;
13
+ color: black;
14
+ cursor: pointer;
15
+ }
16
+
17
+ .delete-mode-button {
18
+ font-size: 25px;
19
+ padding: 2px 10px;
20
+ position: relative;
21
+ top: 3px;
22
+ }
23
+
24
+ .container {
25
+ display: flex;
26
+ flex-direction: column;
27
+ align-items: center;
28
+ }
29
+
30
+ #input-text {
31
+ width: 100%;
32
+ height: 100px;
33
+ color: white;
34
+ font-size: 14px;
35
+ margin-bottom: -8px;
36
+ box-sizing: border-box;
37
+ }
38
+
39
+ .text-container {
40
+ border-radius: 20px;
41
+ padding: 10px 10px;
42
+ border: none;
43
+ outline: none;
44
+ background-color: black;
45
+ }
46
+
47
+ .bubble {
48
+ display: inline-block;
49
+ padding: 10px 10px;
50
+ border-radius: 20px;
51
+ margin-bottom: 5px;
52
+ margin-right: 3px;
53
+ cursor: move;
54
+ }
55
+
56
+ .bubble-text {
57
+ color: white;
58
+ text-shadow: 0px 0px 8px black;
59
+ user-select: none;
60
+ }
61
+
62
+ .bubble-container {
63
+ display: flex;
64
+ flex-wrap: wrap;
65
+ user-select: none;
66
+ }
67
+
68
+ .ghost {
69
+ opacity: 0;
70
+ }
71
+
72
+ .mitigate-mode {
73
+ opacity: 0.66;
74
+ }
75
+
76
+ .bubble.mitigate-mode {
77
+ cursor: crosshair;
78
+ }
79
+
80
+ .bubble.mitigate-mode:hover {
81
+ opacity: 1;
82
+ filter: saturate(0);
83
+ }
84
+
85
+ .delete-mode {
86
+ opacity: 0.66;
87
+ }
88
+
89
+ .bubble.delete-mode {
90
+ cursor: crosshair;
91
+ }
92
+
93
+ .bubble.delete-mode:hover {
94
+ opacity: 0.33;
95
+ }
96
+
97
+ .emphasis-mode {
98
+ opacity: 0.66;
99
+ }
100
+
101
+ .bubble.emphasis-mode {
102
+ cursor: crosshair;
103
+ }
104
+
105
+ .bubble.emphasis-mode:hover {
106
+ opacity: 1;
107
+ filter: saturate(3);
108
+ }
109
+
110
+ .text-container:focus {
111
+ background-color: #3a3a3a;
112
+ }
113
+
114
+ #mitigate-mode-button.active {
115
+ background-color: #4299e3;
116
+ }
117
+
118
+ #delete-mode-button.active {
119
+ background-color: #e3a442;
120
+ color: white;
121
+ }
122
+
123
+ #emphasis-mode-button.active {
124
+ background-color: #e34242;
125
+ }
126
+
127
+ .tooltip {
128
+ position: relative;
129
+ display: inline-block;
130
+ cursor: help;
131
+ }
132
+
133
+ .tooltip:hover .tooltiptext {
134
+ visibility: visible;
135
+ opacity: 1;
136
+ }