franco-upflowy commited on
Commit
c3c6390
·
verified ·
1 Parent(s): 157b468

include string diff

Browse files
Files changed (1) hide show
  1. index.html +151 -44
index.html CHANGED
@@ -1,10 +1,9 @@
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>Pagesets Labelling</title>
8
  <style>
9
  body {
10
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
@@ -13,31 +12,27 @@
13
  padding: 20px;
14
  background: #f5f5f5;
15
  }
16
-
17
  .title {
18
  font-size: 24px;
19
  font-weight: bold;
20
  margin-bottom: 20px;
21
  color: #1a1a1a;
22
  }
23
-
24
  .controls {
25
  margin-bottom: 20px;
26
  padding: 15px;
27
  background: white;
28
  border-radius: 8px;
29
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
30
  display: flex;
31
  justify-content: space-between;
32
  align-items: center;
33
  }
34
-
35
  .file-controls {
36
  display: flex;
37
  align-items: center;
38
  gap: 10px;
39
  }
40
-
41
  .tab-list {
42
  display: flex;
43
  gap: 10px;
@@ -45,7 +40,6 @@
45
  overflow-x: auto;
46
  padding-bottom: 10px;
47
  }
48
-
49
  .tab {
50
  padding: 8px 16px;
51
  border: none;
@@ -54,33 +48,28 @@
54
  cursor: pointer;
55
  white-space: nowrap;
56
  }
57
-
58
  .tab.active {
59
  background: #2196F3;
60
  color: white;
61
  }
62
-
63
  .card {
64
  background: white;
65
  padding: 20px;
66
  margin-bottom: 15px;
67
  border-radius: 8px;
68
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
69
  }
70
-
71
  .text-field {
72
  background: #f5f5f5;
73
  padding: 10px;
74
  margin: 5px 0;
75
  border-radius: 4px;
76
  }
77
-
78
  .label {
79
  font-weight: 600;
80
  margin-bottom: 5px;
81
  display: block;
82
  }
83
-
84
  .thumbs-down {
85
  padding: 8px;
86
  background: none;
@@ -89,12 +78,10 @@
89
  cursor: pointer;
90
  margin-right: 10px;
91
  }
92
-
93
  .thumbs-down.active {
94
  color: red;
95
  border-color: red;
96
  }
97
-
98
  .correction-input {
99
  width: 100%;
100
  padding: 8px;
@@ -103,7 +90,6 @@
103
  border-radius: 4px;
104
  box-sizing: border-box;
105
  }
106
-
107
  .button {
108
  padding: 8px 16px;
109
  background: #2196F3;
@@ -113,31 +99,37 @@
113
  cursor: pointer;
114
  margin-left: 10px;
115
  }
116
-
117
  .button:hover {
118
  background: #1976D2;
119
  }
120
-
121
  .reset-button {
122
  background: #dc3545;
123
  }
124
-
125
  .reset-button:hover {
126
  background: #c82333;
127
  }
128
-
129
  .correction-container {
130
  margin-top: 10px;
131
  }
132
-
133
  .hidden {
134
  display: none;
135
  }
 
 
 
 
 
 
 
 
 
 
 
 
136
  </style>
137
  </head>
138
-
139
  <body>
140
- <h1 class="title">Pagesets Labelling</h1>
141
  <div class="controls">
142
  <div class="file-controls">
143
  <input type="file" id="fileInput" accept=".json">
@@ -153,6 +145,115 @@
153
  let activeTab = '';
154
  let originalFileName = '';
155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  document.getElementById('fileInput').addEventListener('change', handleFileUpload);
157
  document.getElementById('downloadBtn').addEventListener('click', handleDownload);
158
  document.getElementById('resetBtn').addEventListener('click', handleReset);
@@ -183,11 +284,11 @@
183
  }
184
 
185
  function getVariantIds() {
186
- return Object.keys(data).filter(key =>
187
- !key.includes('_feedback') &&
188
- !key.includes('_correct') &&
189
- key !== 'node' &&
190
- key !== 'textValue' &&
191
  key !== 'variantNodeId'
192
  );
193
  }
@@ -200,7 +301,7 @@
200
  function renderTabs() {
201
  const tabContainer = document.getElementById('tabContainer');
202
  tabContainer.innerHTML = '';
203
-
204
  getVariantIds().forEach(variantId => {
205
  const tab = document.createElement('button');
206
  tab.className = `tab ${activeTab === variantId ? 'active' : ''}`;
@@ -222,28 +323,35 @@
222
  Object.keys(data.textValue || {}).forEach(rowIndex => {
223
  const card = document.createElement('div');
224
  card.className = 'card';
225
-
226
  // Original text
227
  const originalText = document.createElement('div');
228
  originalText.innerHTML = `
229
  <span class="label">Original Text:</span>
230
  <div class="text-field">${data.textValue[rowIndex]}</div>
231
  `;
232
-
233
- // Variant text
234
  const variantText = document.createElement('div');
 
 
 
 
 
 
 
235
  variantText.innerHTML = `
236
  <span class="label">Variant Text:</span>
237
- <div class="text-field">${data[activeTab][rowIndex]}</div>
238
  `;
239
-
240
  // Feedback section
241
  const feedbackSection = document.createElement('div');
242
  const thumbsDown = document.createElement('button');
243
  thumbsDown.className = `thumbs-down ${data[`${activeTab}_feedback`]?.[rowIndex] === false ? 'active' : ''}`;
244
  thumbsDown.innerHTML = '👎';
245
  thumbsDown.onclick = () => handleThumbsDown(rowIndex);
246
-
247
  // Correction input
248
  const correctionContainer = document.createElement('div');
249
  correctionContainer.className = `correction-container ${data[`${activeTab}_feedback`]?.[rowIndex] === false ? '' : 'hidden'}`;
@@ -256,19 +364,19 @@
256
  placeholder="Enter correction here"
257
  >
258
  `;
259
-
260
  // Add event listener to correction input
261
  const correctionInput = correctionContainer.querySelector('input');
262
  correctionInput.onchange = (e) => handleCorrection(rowIndex, e.target.value);
263
-
264
  feedbackSection.appendChild(thumbsDown);
265
  feedbackSection.appendChild(correctionContainer);
266
-
267
  // Append all elements to card
268
  card.appendChild(originalText);
269
  card.appendChild(variantText);
270
  card.appendChild(feedbackSection);
271
-
272
  contentContainer.appendChild(card);
273
  });
274
  }
@@ -278,14 +386,14 @@
278
  data[`${activeTab}_feedback`] = {};
279
  }
280
  data[`${activeTab}_feedback`][rowIndex] = !data[`${activeTab}_feedback`][rowIndex];
281
-
282
  if (!data[`${activeTab}_correct`]) {
283
  data[`${activeTab}_correct`] = {};
284
  }
285
  if (!data[`${activeTab}_feedback`][rowIndex]) {
286
  data[`${activeTab}_correct`][rowIndex] = data[activeTab][rowIndex];
287
  }
288
-
289
  renderContent();
290
  }
291
 
@@ -298,7 +406,7 @@
298
 
299
  function handleDownload() {
300
  if (!originalFileName) return;
301
-
302
  const jsonString = JSON.stringify(data, null, 2);
303
  const blob = new Blob([jsonString], { type: 'application/json' });
304
  const url = URL.createObjectURL(blob);
@@ -312,5 +420,4 @@
312
  }
313
  </script>
314
  </body>
315
-
316
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Pagsets Labelling</title>
7
  <style>
8
  body {
9
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
 
12
  padding: 20px;
13
  background: #f5f5f5;
14
  }
 
15
  .title {
16
  font-size: 24px;
17
  font-weight: bold;
18
  margin-bottom: 20px;
19
  color: #1a1a1a;
20
  }
 
21
  .controls {
22
  margin-bottom: 20px;
23
  padding: 15px;
24
  background: white;
25
  border-radius: 8px;
26
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
27
  display: flex;
28
  justify-content: space-between;
29
  align-items: center;
30
  }
 
31
  .file-controls {
32
  display: flex;
33
  align-items: center;
34
  gap: 10px;
35
  }
 
36
  .tab-list {
37
  display: flex;
38
  gap: 10px;
 
40
  overflow-x: auto;
41
  padding-bottom: 10px;
42
  }
 
43
  .tab {
44
  padding: 8px 16px;
45
  border: none;
 
48
  cursor: pointer;
49
  white-space: nowrap;
50
  }
 
51
  .tab.active {
52
  background: #2196F3;
53
  color: white;
54
  }
 
55
  .card {
56
  background: white;
57
  padding: 20px;
58
  margin-bottom: 15px;
59
  border-radius: 8px;
60
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
61
  }
 
62
  .text-field {
63
  background: #f5f5f5;
64
  padding: 10px;
65
  margin: 5px 0;
66
  border-radius: 4px;
67
  }
 
68
  .label {
69
  font-weight: 600;
70
  margin-bottom: 5px;
71
  display: block;
72
  }
 
73
  .thumbs-down {
74
  padding: 8px;
75
  background: none;
 
78
  cursor: pointer;
79
  margin-right: 10px;
80
  }
 
81
  .thumbs-down.active {
82
  color: red;
83
  border-color: red;
84
  }
 
85
  .correction-input {
86
  width: 100%;
87
  padding: 8px;
 
90
  border-radius: 4px;
91
  box-sizing: border-box;
92
  }
 
93
  .button {
94
  padding: 8px 16px;
95
  background: #2196F3;
 
99
  cursor: pointer;
100
  margin-left: 10px;
101
  }
 
102
  .button:hover {
103
  background: #1976D2;
104
  }
 
105
  .reset-button {
106
  background: #dc3545;
107
  }
 
108
  .reset-button:hover {
109
  background: #c82333;
110
  }
 
111
  .correction-container {
112
  margin-top: 10px;
113
  }
 
114
  .hidden {
115
  display: none;
116
  }
117
+ .highlight {
118
+ background-color: #fff3cd;
119
+ padding: 2px 4px;
120
+ border-radius: 2px;
121
+ }
122
+ .differences {
123
+ margin-top: 10px;
124
+ padding: 10px;
125
+ background: #e3f2fd;
126
+ border-radius: 4px;
127
+ font-size: 0.9em;
128
+ }
129
  </style>
130
  </head>
 
131
  <body>
132
+ <h1 class="title">Pagsets Labelling</h1>
133
  <div class="controls">
134
  <div class="file-controls">
135
  <input type="file" id="fileInput" accept=".json">
 
145
  let activeTab = '';
146
  let originalFileName = '';
147
 
148
+ // String difference visualization functions
149
+ function markBlueprintTextDifferences(
150
+ blueprintText,
151
+ locationText,
152
+ startMarker = '<up-markup>',
153
+ endMarker = '</up-markup>'
154
+ ) {
155
+ const cleanBlueprintText = blueprintText
156
+ .trim()
157
+ .replace(/\s+/g, ' ')
158
+ .replace('<up-markup>', '')
159
+ .replace('</up-markup>', '');
160
+ const cleanLocationText = locationText
161
+ .trim()
162
+ .replace(/\s+/g, ' ')
163
+ .replace('<up-markup>', '')
164
+ .replace('</up-markup>', '');
165
+
166
+ if (cleanBlueprintText === cleanLocationText) {
167
+ return cleanLocationText;
168
+ }
169
+
170
+ const blueprintWords = cleanBlueprintText.split(' ');
171
+ const locationWords = cleanLocationText.split(' ');
172
+
173
+ function findLongestCommonSubsequence(arr1, arr2) {
174
+ const m = arr1.length;
175
+ const n = arr2.length;
176
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
177
+
178
+ for (let i = 1; i <= m; i++) {
179
+ for (let j = 1; j <= n; j++) {
180
+ if (arr1[i - 1] === arr2[j - 1]) {
181
+ dp[i][j] = dp[i - 1][j - 1] + 1;
182
+ } else {
183
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
184
+ }
185
+ }
186
+ }
187
+
188
+ const lcs = [];
189
+ let i = m, j = n;
190
+ while (i > 0 && j > 0) {
191
+ if (arr1[i - 1] === arr2[j - 1]) {
192
+ lcs.unshift(arr1[i - 1]);
193
+ i--;
194
+ j--;
195
+ } else if (dp[i - 1][j] > dp[i][j - 1]) {
196
+ i--;
197
+ } else {
198
+ j--;
199
+ }
200
+ }
201
+ return lcs;
202
+ }
203
+
204
+ const commonWords = findLongestCommonSubsequence(blueprintWords, locationWords);
205
+ if (commonWords.length === 0) {
206
+ return `${startMarker}${locationText}${endMarker}`;
207
+ }
208
+
209
+ const result = [];
210
+ let blueprintIndex = 0;
211
+ let locationIndex = 0;
212
+ let markerOpen = false;
213
+
214
+ while (locationIndex < locationWords.length) {
215
+ const currentWord = locationWords[locationIndex];
216
+ if (commonWords[0] === currentWord) {
217
+ if (markerOpen) {
218
+ result.push(endMarker);
219
+ markerOpen = false;
220
+ }
221
+ result.push(result.length > 0 ? ' ' + currentWord : currentWord);
222
+ blueprintIndex = blueprintWords.indexOf(currentWord) + 1;
223
+ commonWords.shift();
224
+ locationIndex++;
225
+ } else {
226
+ if (!markerOpen) {
227
+ result.push(result.length > 0 ? ' ' + startMarker + currentWord : startMarker + currentWord);
228
+ markerOpen = true;
229
+ } else {
230
+ result.push(' ' + currentWord);
231
+ }
232
+ locationIndex++;
233
+ }
234
+ }
235
+
236
+ if (markerOpen) {
237
+ result.push(endMarker);
238
+ }
239
+ return result.join('');
240
+ }
241
+
242
+ function escapeHtml(text) {
243
+ return text
244
+ .replace(/&/g, "&amp;")
245
+ .replace(/</g, "&lt;")
246
+ .replace(/>/g, "&gt;")
247
+ .replace(/"/g, "&quot;")
248
+ .replace(/'/g, "&#039;");
249
+ }
250
+
251
+ function renderMarkerText(text) {
252
+ return text
253
+ .replace(/&lt;up-markup&gt;/g, '<span class="highlight">')
254
+ .replace(/&lt;\/up-markup&gt;/g, '</span>');
255
+ }
256
+
257
  document.getElementById('fileInput').addEventListener('change', handleFileUpload);
258
  document.getElementById('downloadBtn').addEventListener('click', handleDownload);
259
  document.getElementById('resetBtn').addEventListener('click', handleReset);
 
284
  }
285
 
286
  function getVariantIds() {
287
+ return Object.keys(data).filter(key =>
288
+ !key.includes('_feedback') &&
289
+ !key.includes('_correct') &&
290
+ key !== 'node' &&
291
+ key !== 'textValue' &&
292
  key !== 'variantNodeId'
293
  );
294
  }
 
301
  function renderTabs() {
302
  const tabContainer = document.getElementById('tabContainer');
303
  tabContainer.innerHTML = '';
304
+
305
  getVariantIds().forEach(variantId => {
306
  const tab = document.createElement('button');
307
  tab.className = `tab ${activeTab === variantId ? 'active' : ''}`;
 
323
  Object.keys(data.textValue || {}).forEach(rowIndex => {
324
  const card = document.createElement('div');
325
  card.className = 'card';
326
+
327
  // Original text
328
  const originalText = document.createElement('div');
329
  originalText.innerHTML = `
330
  <span class="label">Original Text:</span>
331
  <div class="text-field">${data.textValue[rowIndex]}</div>
332
  `;
333
+
334
+ // Variant text with differences
335
  const variantText = document.createElement('div');
336
+ const markedDifferences = markBlueprintTextDifferences(
337
+ data.textValue[rowIndex],
338
+ data[activeTab][rowIndex]
339
+ );
340
+ const escapedDifferences = escapeHtml(markedDifferences);
341
+ const renderedDifferences = renderMarkerText(escapedDifferences);
342
+
343
  variantText.innerHTML = `
344
  <span class="label">Variant Text:</span>
345
+ <div class="text-field">${renderedDifferences}</div>
346
  `;
347
+
348
  // Feedback section
349
  const feedbackSection = document.createElement('div');
350
  const thumbsDown = document.createElement('button');
351
  thumbsDown.className = `thumbs-down ${data[`${activeTab}_feedback`]?.[rowIndex] === false ? 'active' : ''}`;
352
  thumbsDown.innerHTML = '👎';
353
  thumbsDown.onclick = () => handleThumbsDown(rowIndex);
354
+
355
  // Correction input
356
  const correctionContainer = document.createElement('div');
357
  correctionContainer.className = `correction-container ${data[`${activeTab}_feedback`]?.[rowIndex] === false ? '' : 'hidden'}`;
 
364
  placeholder="Enter correction here"
365
  >
366
  `;
367
+
368
  // Add event listener to correction input
369
  const correctionInput = correctionContainer.querySelector('input');
370
  correctionInput.onchange = (e) => handleCorrection(rowIndex, e.target.value);
371
+
372
  feedbackSection.appendChild(thumbsDown);
373
  feedbackSection.appendChild(correctionContainer);
374
+
375
  // Append all elements to card
376
  card.appendChild(originalText);
377
  card.appendChild(variantText);
378
  card.appendChild(feedbackSection);
379
+
380
  contentContainer.appendChild(card);
381
  });
382
  }
 
386
  data[`${activeTab}_feedback`] = {};
387
  }
388
  data[`${activeTab}_feedback`][rowIndex] = !data[`${activeTab}_feedback`][rowIndex];
389
+
390
  if (!data[`${activeTab}_correct`]) {
391
  data[`${activeTab}_correct`] = {};
392
  }
393
  if (!data[`${activeTab}_feedback`][rowIndex]) {
394
  data[`${activeTab}_correct`][rowIndex] = data[activeTab][rowIndex];
395
  }
396
+
397
  renderContent();
398
  }
399
 
 
406
 
407
  function handleDownload() {
408
  if (!originalFileName) return;
409
+
410
  const jsonString = JSON.stringify(data, null, 2);
411
  const blob = new Blob([jsonString], { type: 'application/json' });
412
  const url = URL.createObjectURL(blob);
 
420
  }
421
  </script>
422
  </body>
 
423
  </html>