joermd commited on
Commit
8a638e8
·
verified ·
1 Parent(s): d79f8ec

Create script.js

Browse files
Files changed (1) hide show
  1. script.js +1226 -0
script.js ADDED
@@ -0,0 +1,1226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Global variables
2
+ let sourceText = "";
3
+ let targetText = "";
4
+ let sourceFile = null;
5
+ let targetFile = null;
6
+ let sourceFileType = "";
7
+ let targetFileType = "";
8
+ let documentType = "regular"; // Default document type: "regular" or "official"
9
+ let analysis = null;
10
+ let correctedSourceText = ""; // For storing corrected text in case of official documents
11
+ let detectedCountry = ""; // For storing detected country from document
12
+
13
+ // Initialize when DOM is loaded
14
+ document.addEventListener("DOMContentLoaded", function() {
15
+ initializeApp();
16
+ });
17
+
18
+ // Initialize application
19
+ function initializeApp() {
20
+ // Initialize document type selector
21
+ initDocumentTypeSelector();
22
+
23
+ // Initialize file upload listeners
24
+ initFileUploadListeners();
25
+
26
+ // Initialize text area listeners
27
+ initTextAreaListeners();
28
+
29
+ // Initialize button listeners
30
+ initButtonListeners();
31
+
32
+ // Initialize display options
33
+ initDisplayOptions();
34
+
35
+ // Initialize filter options
36
+ initFilterOptions();
37
+ }
38
+
39
+ // Initialize document type selector
40
+ function initDocumentTypeSelector() {
41
+ // Create document type selector (radio buttons)
42
+ const selectorHtml = `
43
+ <div class="document-type-selector mb-4">
44
+ <p class="font-bold mb-2">نوع الملف:</p>
45
+ <div class="flex gap-4">
46
+ <label class="flex items-center">
47
+ <input type="radio" name="documentType" value="regular" checked class="mr-2">
48
+ <span>ملف عادي</span>
49
+ </label>
50
+ <label class="flex items-center">
51
+ <input type="radio" name="documentType" value="official" class="mr-2">
52
+ <span>مستند رسمي</span>
53
+ </label>
54
+ </div>
55
+ </div>
56
+ `;
57
+
58
+ // Insert before source file upload section
59
+ const sourceFileSection = document.querySelector("#sourceFileSection");
60
+ sourceFileSection.insertAdjacentHTML('beforebegin', selectorHtml);
61
+
62
+ // Add event listeners to the radio buttons
63
+ document.querySelectorAll('input[name="documentType"]').forEach(radio => {
64
+ radio.addEventListener('change', function() {
65
+ documentType = this.value;
66
+ console.log(`Document type changed to: ${documentType}`);
67
+ });
68
+ });
69
+ }
70
+
71
+ // Initialize file upload listeners
72
+ function initFileUploadListeners() {
73
+ // Source file upload
74
+ const sourceFileInput = document.getElementById("sourceFileInput");
75
+ if (sourceFileInput) {
76
+ sourceFileInput.addEventListener("change", function(e) {
77
+ handleFileUpload(e, "source");
78
+ });
79
+ }
80
+
81
+ // Target file upload
82
+ const targetFileInput = document.getElementById("targetFileInput");
83
+ if (targetFileInput) {
84
+ targetFileInput.addEventListener("change", function(e) {
85
+ handleFileUpload(e, "target");
86
+ });
87
+ }
88
+
89
+ // Drag and drop zones
90
+ initDragAndDropZones();
91
+ }
92
+
93
+ // Initialize drag and drop zones
94
+ function initDragAndDropZones() {
95
+ const sourceDropZone = document.getElementById("sourceDropZone");
96
+ const targetDropZone = document.getElementById("targetDropZone");
97
+
98
+ // Source drop zone
99
+ if (sourceDropZone) {
100
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
101
+ sourceDropZone.addEventListener(eventName, preventDefaults, false);
102
+ });
103
+
104
+ ['dragenter', 'dragover'].forEach(eventName => {
105
+ sourceDropZone.addEventListener(eventName, function() {
106
+ this.classList.add('bg-blue-100');
107
+ }, false);
108
+ });
109
+
110
+ ['dragleave', 'drop'].forEach(eventName => {
111
+ sourceDropZone.addEventListener(eventName, function() {
112
+ this.classList.remove('bg-blue-100');
113
+ }, false);
114
+ });
115
+
116
+ sourceDropZone.addEventListener('drop', function(e) {
117
+ const dt = e.dataTransfer;
118
+ if (dt.files.length) {
119
+ document.getElementById('sourceFileInput').files = dt.files;
120
+ handleFileUpload({ target: { files: dt.files } }, "source");
121
+ }
122
+ }, false);
123
+ }
124
+
125
+ // Target drop zone
126
+ if (targetDropZone) {
127
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
128
+ targetDropZone.addEventListener(eventName, preventDefaults, false);
129
+ });
130
+
131
+ ['dragenter', 'dragover'].forEach(eventName => {
132
+ targetDropZone.addEventListener(eventName, function() {
133
+ this.classList.add('bg-blue-100');
134
+ }, false);
135
+ });
136
+
137
+ ['dragleave', 'drop'].forEach(eventName => {
138
+ targetDropZone.addEventListener(eventName, function() {
139
+ this.classList.remove('bg-blue-100');
140
+ }, false);
141
+ });
142
+
143
+ targetDropZone.addEventListener('drop', function(e) {
144
+ const dt = e.dataTransfer;
145
+ if (dt.files.length) {
146
+ document.getElementById('targetFileInput').files = dt.files;
147
+ handleFileUpload({ target: { files: dt.files } }, "target");
148
+ }
149
+ }, false);
150
+ }
151
+ }
152
+
153
+ // Prevent default behavior for drag and drop
154
+ function preventDefaults(e) {
155
+ e.preventDefault();
156
+ e.stopPropagation();
157
+ }
158
+
159
+ // Initialize text area listeners
160
+ function initTextAreaListeners() {
161
+ const sourceTextArea = document.getElementById("sourceTextArea");
162
+ const targetTextArea = document.getElementById("targetTextArea");
163
+
164
+ if (sourceTextArea) {
165
+ sourceTextArea.addEventListener("input", function() {
166
+ sourceText = this.value;
167
+ });
168
+ }
169
+
170
+ if (targetTextArea) {
171
+ targetTextArea.addEventListener("input", function() {
172
+ targetText = this.value;
173
+ });
174
+ }
175
+ }
176
+
177
+ // Initialize button listeners
178
+ function initButtonListeners() {
179
+ // Analyze button
180
+ const analyzeBtn = document.getElementById("analyzeBtn");
181
+ if (analyzeBtn) {
182
+ analyzeBtn.addEventListener("click", startAnalysis);
183
+ }
184
+
185
+ // Export report button
186
+ const exportReportBtn = document.getElementById("exportReportBtn");
187
+ if (exportReportBtn) {
188
+ exportReportBtn.addEventListener("click", exportReport);
189
+ }
190
+
191
+ // Clear all button
192
+ const clearAllBtn = document.getElementById("clearAllBtn");
193
+ if (clearAllBtn) {
194
+ clearAllBtn.addEventListener("click", clearAll);
195
+ }
196
+ }
197
+
198
+ // Initialize display options
199
+ function initDisplayOptions() {
200
+ // Remove classic display option as per requirements
201
+ const displayOptions = document.querySelectorAll('.display-option');
202
+ displayOptions.forEach(option => {
203
+ if (option.dataset.display === 'classic') {
204
+ option.remove();
205
+ }
206
+ });
207
+
208
+ // Set split view as default
209
+ const splitViewOption = document.querySelector('.display-option[data-display="split"]');
210
+ if (splitViewOption) {
211
+ splitViewOption.classList.add('active');
212
+ setDisplayMode('split');
213
+ }
214
+
215
+ // Add display option listeners
216
+ document.querySelectorAll('.display-option').forEach(option => {
217
+ option.addEventListener('click', function() {
218
+ document.querySelectorAll('.display-option').forEach(opt => opt.classList.remove('active'));
219
+ this.classList.add('active');
220
+ setDisplayMode(this.dataset.display);
221
+ });
222
+ });
223
+ }
224
+
225
+ // Initialize filter options
226
+ function initFilterOptions() {
227
+ document.querySelectorAll('.filter-option').forEach(option => {
228
+ option.addEventListener('click', function() {
229
+ this.classList.toggle('active');
230
+ applyFilters();
231
+ });
232
+ });
233
+ }
234
+
235
+ // Handle file upload
236
+ function handleFileUpload(event, type) {
237
+ const file = event.target.files[0];
238
+ if (!file) return;
239
+
240
+ // Store file
241
+ if (type === "source") {
242
+ sourceFile = file;
243
+ document.getElementById("sourceFileName").textContent = file.name;
244
+ } else {
245
+ targetFile = file;
246
+ document.getElementById("targetFileName").textContent = file.name;
247
+ }
248
+
249
+ // Process file based on type
250
+ processFile(file, type);
251
+ }
252
+
253
+ // Process file based on file extension
254
+ async function processFile(file, textType) {
255
+ const fileExtension = file.name.split('.').pop().toLowerCase();
256
+ const fileType = file.type;
257
+
258
+ // Show loading indicator
259
+ toggleLoading(true, textType);
260
+
261
+ try {
262
+ let extractedText = "";
263
+
264
+ // Process based on file extension/type
265
+ if (fileType.includes('pdf') || fileExtension === 'pdf') {
266
+ extractedText = await processPDF(file);
267
+ } else if (fileType.includes('image') || ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(fileExtension)) {
268
+ extractedText = await processImage(file);
269
+ } else if (fileExtension === 'docx' || fileExtension === 'doc') {
270
+ extractedText = await processDocx(file);
271
+ } else if (['xlsx', 'xls', 'csv'].includes(fileExtension)) {
272
+ extractedText = await processExcel(file);
273
+ } else if (fileType.includes('text') || fileExtension === 'txt') {
274
+ extractedText = await processTextFile(file);
275
+ } else {
276
+ throw new Error("Unsupported file format. Please use PDF, image, Word, Excel, or text files.");
277
+ }
278
+
279
+ // If document type is "official", correct the fixed terms
280
+ if (documentType === "official" && textType === "source") {
281
+ showNotification("جاري تصحيح الثوابت في المستند الرسمي...", "info");
282
+ correctedSourceText = await correctOfficialDocument(extractedText);
283
+
284
+ // Update extracted text with corrected text
285
+ extractedText = correctedSourceText;
286
+ showNotification("تم تصحيح الثوابت بنجاح", "success");
287
+ }
288
+
289
+ // Update UI with extracted text
290
+ if (textType === "source") {
291
+ sourceText = extractedText;
292
+ sourceFileType = fileExtension;
293
+ document.getElementById("sourceTextArea").value = extractedText;
294
+ } else {
295
+ targetText = extractedText;
296
+ targetFileType = fileExtension;
297
+ document.getElementById("targetTextArea").value = extractedText;
298
+ }
299
+
300
+ // Show preview if it's a PDF
301
+ if (fileType.includes('pdf') || fileExtension === 'pdf') {
302
+ showPDFPreview(file, textType);
303
+ }
304
+
305
+ // Show OCR results
306
+ showOCRResults(extractedText, textType);
307
+
308
+ // Enable analyze button if both texts are available
309
+ if (sourceText && targetText) {
310
+ document.getElementById("analyzeBtn").disabled = false;
311
+ }
312
+
313
+ } catch (error) {
314
+ console.error("Error processing file:", error);
315
+ showNotification(`خطأ في معالجة الملف: ${error.message}`, "error");
316
+ } finally {
317
+ // Hide loading indicator
318
+ toggleLoading(false, textType);
319
+ }
320
+ }
321
+
322
+ // Process PDF file
323
+ async function processPDF(file) {
324
+ return new Promise((resolve, reject) => {
325
+ const fileReader = new FileReader();
326
+
327
+ fileReader.onload = async function() {
328
+ try {
329
+ const typedArray = new Uint8Array(this.result);
330
+
331
+ // Load the PDF using pdf.js
332
+ const pdf = await pdfjsLib.getDocument({ data: typedArray }).promise;
333
+ let text = "";
334
+
335
+ // Extract text from all pages
336
+ for (let i = 1; i <= pdf.numPages; i++) {
337
+ const page = await pdf.getPage(i);
338
+ const textContent = await page.getTextContent();
339
+ const pageText = textContent.items.map(item => item.str).join(' ');
340
+ text += pageText + "\n";
341
+ }
342
+
343
+ resolve(text);
344
+ } catch (error) {
345
+ console.error("Error extracting text from PDF:", error);
346
+ reject(error);
347
+ }
348
+ };
349
+
350
+ fileReader.onerror = function() {
351
+ reject(new Error("Error reading the PDF file."));
352
+ };
353
+
354
+ fileReader.readAsArrayBuffer(file);
355
+ });
356
+ }
357
+
358
+ // Process image using OCR
359
+ async function processImage(file) {
360
+ return new Promise((resolve, reject) => {
361
+ const fileReader = new FileReader();
362
+
363
+ fileReader.onload = async function() {
364
+ try {
365
+ // Here we would typically call an OCR API
366
+ // For now, we'll simulate it with a placeholder
367
+ showNotification("جاري استخراج النص من الصورة...", "info");
368
+
369
+ // This is where you would call your OCR API
370
+ // For example, using Tesseract.js or a cloud OCR API
371
+ // Simulate OCR processing delay
372
+ await new Promise(res => setTimeout(res, 2000));
373
+
374
+ // Placeholder for OCR result
375
+ // In a real implementation, replace this with actual OCR API call
376
+ const ocrResult = "هذا نص تم استخراجه من الصورة باستخدام تقنية OCR.\nيمكنك استبدال هذا النص بنتائج OCR الفعلية من API الخاص بك.";
377
+
378
+ resolve(ocrResult);
379
+ } catch (error) {
380
+ console.error("Error processing image:", error);
381
+ reject(error);
382
+ }
383
+ };
384
+
385
+ fileReader.onerror = function() {
386
+ reject(new Error("Error reading the image file."));
387
+ };
388
+
389
+ fileReader.readAsDataURL(file);
390
+ });
391
+ }
392
+
393
+ // Process Word document
394
+ async function processDocx(file) {
395
+ return new Promise((resolve, reject) => {
396
+ const fileReader = new FileReader();
397
+
398
+ fileReader.onload = async function() {
399
+ try {
400
+ // Use mammoth.js to extract text from DOCX
401
+ const arrayBuffer = this.result;
402
+ const result = await mammoth.extractRawText({ arrayBuffer });
403
+ resolve(result.value);
404
+ } catch (error) {
405
+ console.error("Error extracting text from DOCX:", error);
406
+ reject(error);
407
+ }
408
+ };
409
+
410
+ fileReader.onerror = function() {
411
+ reject(new Error("Error reading the Word document."));
412
+ };
413
+
414
+ fileReader.readAsArrayBuffer(file);
415
+ });
416
+ }
417
+
418
+ // Process Excel file
419
+ async function processExcel(file) {
420
+ return new Promise((resolve, reject) => {
421
+ const fileReader = new FileReader();
422
+
423
+ fileReader.onload = async function() {
424
+ try {
425
+ const data = new Uint8Array(this.result);
426
+ const workbook = XLSX.read(data, { type: 'array' });
427
+
428
+ // Extract text from all sheets
429
+ let text = "";
430
+ workbook.SheetNames.forEach(sheetName => {
431
+ const worksheet = workbook.Sheets[sheetName];
432
+ const sheetText = XLSX.utils.sheet_to_txt(worksheet);
433
+ text += `[Sheet: ${sheetName}]\n${sheetText}\n\n`;
434
+ });
435
+
436
+ resolve(text);
437
+ } catch (error) {
438
+ console.error("Error extracting text from Excel:", error);
439
+ reject(error);
440
+ }
441
+ };
442
+
443
+ fileReader.onerror = function() {
444
+ reject(new Error("Error reading the Excel file."));
445
+ };
446
+
447
+ fileReader.readAsArrayBuffer(file);
448
+ });
449
+ }
450
+
451
+ // Process text file
452
+ async function processTextFile(file) {
453
+ return new Promise((resolve, reject) => {
454
+ const fileReader = new FileReader();
455
+
456
+ fileReader.onload = function() {
457
+ try {
458
+ resolve(this.result);
459
+ } catch (error) {
460
+ console.error("Error reading text file:", error);
461
+ reject(error);
462
+ }
463
+ };
464
+
465
+ fileReader.onerror = function() {
466
+ reject(new Error("Error reading the text file."));
467
+ };
468
+
469
+ fileReader.readAsText(file);
470
+ });
471
+ }
472
+
473
+ // Correct official document using DeepSeek API
474
+ async function correctOfficialDocument(text) {
475
+ try {
476
+ // First, detect the country from the document
477
+ detectedCountry = await detectCountryFromDocument(text);
478
+ console.log("Detected country:", detectedCountry);
479
+
480
+ // Prepare the prompt for DeepSeek API based on the detected country
481
+ const prompt = prepareFixedTermsPrompt(text, detectedCountry);
482
+
483
+ // Call DeepSeek API for correction
484
+ const correctionResponse = await callDeepSeekAPI(prompt, "correction");
485
+
486
+ // Extract the corrected text from the response
487
+ return extractCorrectedText(correctionResponse, text);
488
+ } catch (error) {
489
+ console.error("Error correcting official document:", error);
490
+ showNotification("حدث خطأ أثناء تصحيح المستند الرسمي", "error");
491
+ return text; // Return original text if correction fails
492
+ }
493
+ }
494
+
495
+ // Detect country from document text
496
+ async function detectCountryFromDocument(text) {
497
+ try {
498
+ const prompt = `
499
+ أنت خبير في تحليل المستندات الرسمية. قم بتحليل النص التالي وتحديد الدولة التي ينتمي إليها هذا المستند بناءً على المحتوى، الصياغة، المصطلحات المستخدمة، أو أي مؤشرات أخرى.
500
+ أعطني اسم الدولة فقط دون أي توضيح إضافي.
501
+
502
+ النص:
503
+ ${text}
504
+ `;
505
+
506
+ // Call DeepSeek API for country detection
507
+ const response = await callDeepSeekAPI(prompt, "country-detection");
508
+
509
+ // Process the response to extract just the country name
510
+ let country = response.trim();
511
+
512
+ // If multiple lines, take the first line
513
+ if (country.includes('\n')) {
514
+ country = country.split('\n')[0];
515
+ }
516
+
517
+ return country;
518
+ } catch (error) {
519
+ console.error("Error detecting country:", error);
520
+ return "غير محدد"; // Default if detection fails
521
+ }
522
+ }
523
+
524
+ // Prepare prompt for fixed terms correction based on country
525
+ function prepareFixedTermsPrompt(text, country) {
526
+ return `
527
+ أنت خبير في تصحيح المستندات الرسمية للدولة: ${country}.
528
+
529
+ مهمتك هي تصحيح المصطلحات الثابتة فقط في المستند الرسمي التالي، مع الحفاظ على البيانات المتغيرة كما هي تماماً.
530
+
531
+ قواعد التصحيح:
532
+ 1. صحح فقط المصطلحات والعبارات الثابتة مثل "الاسم"، "تاريخ الميلاد"، "الجنسية"، "الرقم القومي"، "رقم الهوية".
533
+ 2. لا تغير أبداً البيانات المتغيرة مثل أسماء الأشخاص، التواريخ، الأرقام، إلخ.
534
+ 3. حافظ على التنسيق الأصلي للنص.
535
+ 4. أعد النص كاملاً بعد التصحيح.
536
+
537
+ على سبيل المثال:
538
+ - إذا كان النص يحتوي على "الأثم: محمد أحمد"، قم بتصحيحه إلى "الاسم: محمد أحمد"
539
+ - إذا كان النص يحتوي على "تاريخ الميلات: 1990/05/15"، قم بتصحيحه إلى "تاريخ الميلاد: 1990/05/15"
540
+
541
+ النص المراد تصحيحه:
542
+ ${text}
543
+
544
+ أعد النص كاملاً بعد التصحيح دون أي توضيحات إضافية.
545
+ `;
546
+ }
547
+
548
+ // Extract corrected text from API response
549
+ function extractCorrectedText(response, originalText) {
550
+ // This function should parse the API response to extract just the corrected text
551
+ // If the API returns explanations or other content, strip those out
552
+
553
+ // For now, we'll just return the response as is, assuming it's clean
554
+ return response;
555
+ }
556
+
557
+ // Call DeepSeek API
558
+ async function callDeepSeekAPI(prompt, type) {
559
+ // This function would typically make an API call to DeepSeek
560
+ // For now, we'll simulate it with a placeholder response
561
+
562
+ console.log(`Calling DeepSeek API for ${type} with prompt:`, prompt);
563
+
564
+ // Simulate API delay
565
+ await new Promise(res => setTimeout(res, 3000));
566
+
567
+ // Simulate different responses based on the type of request
568
+ if (type === "correction") {
569
+ // Simulate a corrected document
570
+ return prompt.replace("الأثم:", "الاسم:")
571
+ .replace("تاريخ الميلات:", "تاريخ الميلاد:")
572
+ .replace("الجنسيه:", "الجنسية:")
573
+ .replace("رقم القومى:", "الرقم القومي:");
574
+ } else if (type === "country-detection") {
575
+ // Simulate country detection
576
+ return "مصر";
577
+ } else if (type === "analysis") {
578
+ // Simulate analysis response
579
+ return {
580
+ differences: [
581
+ {
582
+ sourceText: "هذا النص الأصلي يحتوي على خطأ.",
583
+ targetText: "هذا النص المترجم يحتوي على خطأ مختلف.",
584
+ errorType: "grammar",
585
+ explanation: "الجملة في النص المترجم تحتوي على خطأ نحوي.",
586
+ severity: "medium"
587
+ },
588
+ // More differences...
589
+ ],
590
+ summary: "تم العثور على 5 اختلافات بين النصين، منها 2 خطأ نحوي و3 أخطاء إملائية."
591
+ };
592
+ }
593
+
594
+ return "عذراً، حدث خطأ في معالجة طلبك.";
595
+ }
596
+
597
+ // Start analysis of texts
598
+ async function startAnalysis() {
599
+ if (!sourceText || !targetText) {
600
+ showNotification("يرجى إدخال أو تحميل النصين المصدر والهدف", "error");
601
+ return;
602
+ }
603
+
604
+ try {
605
+ // Show loading indicator
606
+ toggleLoading(true, "analysis");
607
+ showNotification("جاري تحليل النصوص...", "info");
608
+
609
+ // Prepare prompt for analysis
610
+ const analysisPrompt = `
611
+ قم بتحليل النصين التاليين ومقارنتهما لتحديد الاختلافات والأخطاء:
612
+
613
+ النص المصدر:
614
+ ${sourceText}
615
+
616
+ النص الهدف:
617
+ ${targetText}
618
+
619
+ قم بتحديد الاختلافات التالية:
620
+ 1. الأخطاء النحوية
621
+ 2. الأخطاء الإملائية
622
+ 3. الاختلافات في المعنى
623
+ 4. الكلمات أو العبارات المفقودة
624
+ 5. الإضافات غير الضرورية
625
+
626
+ لكل اختلاف، قدم:
627
+ - النص المصدر
628
+ - النص الهدف
629
+ - نوع الخطأ
630
+ - شرح الخطأ
631
+ - مستوى خطورة الخطأ (منخفض، متوسط، مرتفع)
632
+ `;
633
+
634
+ // Call DeepSeek API for analysis
635
+ const apiResponse = await callDeepSeekAPI(analysisPrompt, "analysis");
636
+
637
+ // Process the analysis results
638
+ analysis = apiResponse; // In a real implementation, this would parse the API response
639
+
640
+ // Display analysis results
641
+ displayAnalysisResults(analysis);
642
+
643
+ // Enable report export
644
+ document.getElementById("exportReportBtn").disabled = false;
645
+
646
+ showNotification("تم الانتهاء من التحليل بنجاح", "success");
647
+ } catch (error) {
648
+ console.error("Error during analysis:", error);
649
+ showNotification(`خطأ في التحليل: ${error.message}`, "error");
650
+ } finally {
651
+ // Hide loading indicator
652
+ toggleLoading(false, "analysis");
653
+ }
654
+ }
655
+
656
+ // Display analysis results with improved highlighting and explanation
657
+ function displayAnalysisResults(analysis) {
658
+ const resultsContainer = document.getElementById("resultsContainer");
659
+ resultsContainer.innerHTML = "";
660
+
661
+ // Create summary section
662
+ const summarySection = document.createElement("div");
663
+ summarySection.className = "mb-6 p-4 bg-gray-50 rounded-lg";
664
+ summarySection.innerHTML = `
665
+ <h3 class="text-xl font-bold mb-2">ملخص التحليل</h3>
666
+ <p>${analysis.summary}</p>
667
+ `;
668
+ resultsContainer.appendChild(summarySection);
669
+
670
+ // Create differences section with improved display
671
+ const differencesSection = document.createElement("div");
672
+ differencesSection.className = "differences-section";
673
+
674
+ // Group differences by paragraphs for better organization
675
+ const groupedDifferences = groupDifferencesByParagraph(analysis.differences);
676
+
677
+ // Create each paragraph section
678
+ let paragraphIndex = 1;
679
+ for (const [paragraph, differences] of Object.entries(groupedDifferences)) {
680
+ const paragraphSection = document.createElement("div");
681
+ paragraphSection.className = "paragraph-section mb-8 p-4 bg-white rounded-lg shadow";
682
+
683
+ paragraphSection.innerHTML = `
684
+ <h3 class="text-lg font-bold mb-4">فقرة ${paragraphIndex}</h3>
685
+ <div class="paragraph-content mb-4">
686
+ <p class="whitespace-pre-wrap">${highlightDifferencesInParagraph(paragraph, differences)}</p>
687
+ </div>
688
+ <div class="paragraph-differences">
689
+ <h4 class="font-bold mb-2">الاختلافات المكتشفة:</h4>
690
+ <ul class="differences-list">
691
+ ${differences.map(diff => `
692
+ <li class="difference-item mb-4 p-3 border-r-4 border-${getSeverityColor(diff.severity)} bg-${getSeverityColor(diff.severity)}-50">
693
+ <div class="flex justify-between">
694
+ <span class="font-bold">${getErrorTypeLabel(diff.errorType)}</span>
695
+ <span class="severity-badge bg-${getSeverityColor(diff.severity)}-200 text-${getSeverityColor(diff.severity)}-800 px-2 py-1 rounded text-sm">
696
+ ${getSeverityLabel(diff.severity)}
697
+ </span>
698
+ </div>
699
+ <div class="mt-2">
700
+ <div class="source-text mb-1">
701
+ <span class="font-bold ml-1">النص الأصلي:</span>
702
+ <span class="text-gray-800">${diff.sourceText}</span>
703
+ </div>
704
+ <div class="target-text mb-1">
705
+ <span class="font-bold ml-1">النص المترجم:</span>
706
+ <span class="text-gray-800">${diff.targetText}</span>
707
+ </div>
708
+ <div class="explanation mt-2 p-2 bg-gray-50 rounded">
709
+ <span class="font-bold">الشرح:</span>
710
+ <p>${diff.explanation}</p>
711
+ </div>
712
+ </div>
713
+ </li>
714
+ `).join('')}
715
+ </ul>
716
+ </div>
717
+ `;
718
+
719
+ differencesSection.appendChild(paragraphSection);
720
+ paragraphIndex++;
721
+ }
722
+
723
+ resultsContainer.appendChild(differencesSection);
724
+
725
+ // Show results container
726
+ document.getElementById("resultsSection").classList.remove("hidden");
727
+
728
+ // Add tooltip popups to highlighted differences
729
+ addTooltipsToHighlights();
730
+ }
731
+
732
+ // Group differences by paragraph for better organization
733
+ function groupDifferencesByParagraph(differences) {
734
+ const paragraphMap = {};
735
+
736
+ differences.forEach(diff => {
737
+ // Use the source text to determine the paragraph
738
+ const paragraph = extractParagraphFromText(diff.sourceText, sourceText);
739
+
740
+ if (!paragraphMap[paragraph]) {
741
+ paragraphMap[paragraph] = [];
742
+ }
743
+
744
+ paragraphMap[paragraph].push(diff);
745
+ });
746
+
747
+ return paragraphMap;
748
+ }
749
+
750
+ // Extract the paragraph containing the given text
751
+ function extractParagraphFromText(snippet, fullText) {
752
+ // Split the full text into paragraphs
753
+ const paragraphs = fullText.split(/\n\s*\n/);
754
+
755
+ // Find the paragraph containing the snippet
756
+ for (const paragraph of paragraphs) {
757
+ if (paragraph.includes(snippet)) {
758
+ return paragraph;
759
+ }
760
+ }
761
+
762
+ // If not found, return the snippet itself
763
+ return snippet;
764
+ }
765
+
766
+ // Highlight differences in paragraph
767
+ function highlightDifferencesInParagraph(paragraph, differences) {
768
+ let highlightedText = paragraph;
769
+
770
+ // Sort differences by their position in the paragraph (to handle overlaps correctly)
771
+ differences.sort((a, b) => {
772
+ const posA = paragraph.indexOf(a.sourceText);
773
+ const posB = paragraph.indexOf(b.sourceText);
774
+ return posA - posB;
775
+ });
776
+
777
+ // Apply highlights to each difference
778
+ let offset = 0;
779
+ differences.forEach(diff => {
780
+ const diffId = `diff-${Math.random().toString(36).substr(2, 9)}`;
781
+ const originalText = diff.sourceText;
782
+ const startPos = paragraph.indexOf(originalText, offset);
783
+
784
+ if (startPos !== -1) {
785
+ // Find the sentence containing the difference
786
+ const sentenceMatch = findSentenceContaining(paragraph, startPos, originalText.length);
787
+
788
+ if (sentenceMatch) {
789
+ const { start, end } = sentenceMatch;
790
+ const sentence = paragraph.substring(start, end);
791
+
792
+ // Create highlighted version of the sentence
793
+ const highlightedSentence = `<span class="highlighted-sentence bg-${getSeverityColor(diff.severity)}-100 cursor-pointer" data-diff-id="${diffId}">${sentence}</span>`;
794
+
795
+ // Replace the sentence in the text
796
+ highlightedText = highlightedText.substring(0, start) + highlightedSentence + highlightedText.substring(end);
797
+
798
+ // Update offset to account for the added HTML
799
+ offset = end + (highlightedSentence.length - sentence.length);
800
+
801
+ // Store difference details for the tooltip
802
+ window.diffDetails = window.diffDetails || {};
803
+ window.diffDetails[diffId] = diff;
804
+ }
805
+ }
806
+ });
807
+
808
+ return highlightedText;
809
+ }
810
+
811
+ // Find the sentence containing the difference
812
+ function findSentenceContaining(text, position, length) {
813
+ // Look for sentence boundaries (period, question mark, exclamation mark followed by space or newline)
814
+ const sentenceEnders = ['. ', '? ', '! ', '.\n', '?\n', '!\n'];
815
+
816
+ let start = position;
817
+ while (start > 0) {
818
+ let foundBoundary = false;
819
+
820
+ for (const ender of sentenceEnders) {
821
+ const endPos = text.lastIndexOf(ender, start);
822
+ if (endPos !== -1 && endPos + ender.length <= start) {
823
+ start = endPos + ender.length;
824
+ foundBoundary = true;
825
+ break;
826
+ }
827
+ }
828
+
829
+ if (foundBoundary || start === 0) break;
830
+ start--;
831
+ }
832
+
833
+ let end = position + length;
834
+ while (end < text.length) {
835
+ let foundBoundary = false;
836
+
837
+ for (const ender of sentenceEnders) {
838
+ const endPos = text.indexOf(ender, end - ender.length + 1);
839
+ if (endPos !== -1 && endPos >= end - ender.length + 1) {
840
+ end = endPos + ender.length;
841
+ foundBoundary = true;
842
+ break;
843
+ }
844
+ }
845
+
846
+ if (foundBoundary || end === text.length) break;
847
+ end++;
848
+ }
849
+
850
+ return { start, end };
851
+ }
852
+
853
+ // Add tooltips to highlighted differences
854
+ function addTooltipsToHighlights() {
855
+ document.querySelectorAll('.highlighted-sentence').forEach(el => {
856
+ el.addEventListener('click', function() {
857
+ const diffId = this.dataset.diffId;
858
+ const diff = window.diffDetails[diffId];
859
+
860
+ if (diff) {
861
+ showDifferencePopup(diff, this);
862
+ }
863
+ });
864
+ });
865
+ }
866
+
867
+ // Show popup with difference details
868
+ function showDifferencePopup(diff, element) {
869
+ // Remove any existing popups
870
+ const existingPopup = document.getElementById('difference-popup');
871
+ if (existingPopup) {
872
+ existingPopup.remove();
873
+ }
874
+
875
+ // Create popup element
876
+ const popup = document.createElement('div');
877
+ popup.id = 'difference-popup';
878
+ popup.className = 'fixed bg-white rounded-lg shadow-lg p-4 max-w-md z-50';
879
+ popup.style.maxWidth = '90vw';
880
+
881
+ // Create popup content
882
+ popup.innerHTML = `
883
+ <div class="flex justify-between items-center mb-2">
884
+ <h3 class="font-bold text-lg">${getErrorTypeLabel(diff.errorType)}</h3>
885
+ <span class="severity-badge bg-${getSeverityColor(diff.severity)}-200 text-${getSeverityColor(diff.severity)}-800 px-2 py-1 rounded text-sm">
886
+ ${getSeverityLabel(diff.severity)}
887
+ </span>
888
+ </div>
889
+ <div class="source-text mb-2">
890
+ <span class="font-bold block mb-1">النص الأصلي:</span>
891
+ <p class="bg-gray-50 p-2 rounded">${diff.sourceText}</p>
892
+ </div>
893
+ <div class="target-text mb-2">
894
+ <span class="font-bold block mb-1">النص المترجم:</span>
895
+ <p class="bg-gray-50 p-2 rounded">${diff.targetText}</p>
896
+ </div>
897
+ <div class="explanation mb-3">
898
+ <span class="font-bold block mb-1">الشرح:</span>
899
+ <p class="bg-gray-50 p-2 rounded">${diff.explanation}</p>
900
+ </div>
901
+ <button id="close-popup" class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-bold py-1 px-4 rounded">
902
+ إغلاق
903
+ </button>
904
+ `;
905
+
906
+ // Position the popup near the element
907
+ document.body.appendChild(popup);
908
+ positionPopup(popup, element);
909
+
910
+ // Add close button event listener
911
+ document.getElementById('close-popup').addEventListener('click', function() {
912
+ popup.remove();
913
+ });
914
+
915
+ // Close popup when clicking outside
916
+ document.addEventListener('click', function closePopup(e) {
917
+ if (!popup.contains(e.target) && e.target !== element) {
918
+ popup.remove();
919
+ document.removeEventListener('click', closePopup);
920
+ }
921
+ });
922
+ }
923
+
924
+ // Position popup near the target element
925
+ function positionPopup(popup, element) {
926
+ const rect = element.getBoundingClientRect();
927
+ const popupRect = popup.getBoundingClientRect();
928
+
929
+ // Default position below the element
930
+ let top = rect.bottom + window.scrollY + 10;
931
+ let left = rect.left + window.scrollX;
932
+
933
+ // Check if popup would go below viewport
934
+ if (top + popupRect.height > window.innerHeight + window.scrollY) {
935
+ // Position above the element instead
936
+ top = rect.top + window.scrollY - popupRect.height - 10;
937
+ }
938
+
939
+ // Ensure popup doesn't go off-screen horizontally
940
+ if (left + popupRect.width > window.innerWidth) {
941
+ left = window.innerWidth - popupRect.width - 10;
942
+ }
943
+
944
+ popup.style.top = `${top}px`;
945
+ popup.style.left = `${left}px`;
946
+ }
947
+
948
+ // Set display mode
949
+ function setDisplayMode(mode) {
950
+ const resultsContainer = document.getElementById("resultsContainer");
951
+
952
+ // Remove existing display mode classes
953
+ resultsContainer.classList.remove('display-classic', 'display-split', 'display-side-by-side');
954
+
955
+ // Add new display mode class
956
+ resultsContainer.classList.add(`display-${mode}`);
957
+ }
958
+
959
+ // Apply filters to displayed results
960
+ function applyFilters() {
961
+ const activeFilters = Array.from(document.querySelectorAll('.filter-option.active'))
962
+ .map(el => el.dataset.filter);
963
+
964
+ // Show all differences initially
965
+ document.querySelectorAll('.difference-item').forEach(item => {
966
+ item.classList.remove('hidden');
967
+ });
968
+
969
+ // If no filters are active, show all
970
+ if (activeFilters.length === 0) {
971
+ return;
972
+ }
973
+
974
+ // Hide differences that don't match active filters
975
+ document.querySelectorAll('.difference-item').forEach(item => {
976
+ const errorType = item.querySelector('.error-type').textContent;
977
+ const severity = item.querySelector('.severity-badge').textContent;
978
+
979
+ const matchesFilter = activeFilters.some(filter => {
980
+ if (filter.startsWith('type-')) {
981
+ return errorType === filter.replace('type-', '');
982
+ } else if (filter.startsWith('severity-')) {
983
+ return severity === filter.replace('severity-', '');
984
+ }
985
+ return false;
986
+ });
987
+
988
+ if (!matchesFilter) {
989
+ item.classList.add('hidden');
990
+ }
991
+ });
992
+ }
993
+
994
+ // Export analysis report
995
+ function exportReport() {
996
+ if (!analysis) {
997
+ showNotification("لا توجد نتائج تحليل للتصدير", "error");
998
+ return;
999
+ }
1000
+
1001
+ try {
1002
+ // Create report content
1003
+ const reportContent = generateReportContent();
1004
+
1005
+ // Create blob and download link
1006
+ const blob = new Blob([reportContent], { type: 'text/plain;charset=utf-8' });
1007
+ const url = URL.createObjectURL(blob);
1008
+
1009
+ const a = document.createElement('a');
1010
+ a.href = url;
1011
+ a.download = `تقرير_تحليل_الترجمة_${new Date().toISOString().slice(0, 10)}.txt`;
1012
+ a.click();
1013
+
1014
+ URL.revokeObjectURL(url);
1015
+
1016
+ showNotification("تم تصدير التقرير بنجاح", "success");
1017
+ } catch (error) {
1018
+ console.error("Error exporting report:", error);
1019
+ showNotification("حدث خطأ أثناء تصدير التقرير", "error");
1020
+ }
1021
+ }
1022
+
1023
+ // Generate report content
1024
+ function generateReportContent() {
1025
+ let content = "تقرير تحليل الترجمة\n";
1026
+ content += "===================\n\n";
1027
+
1028
+ // Add date and time
1029
+ content += `تاريخ التحليل: ${new Date().toLocaleString('ar-EG')}\n\n`;
1030
+
1031
+ // Add summary
1032
+ content += "ملخص التحليل:\n";
1033
+ content += "-------------\n";
1034
+ content += analysis.summary + "\n\n";
1035
+
1036
+ // Add detailed differences
1037
+ content += "تفاصيل الاختلافات:\n";
1038
+ content += "------------------\n\n";
1039
+
1040
+ analysis.differences.forEach((diff, index) => {
1041
+ content += `${index + 1}. ${getErrorTypeLabel(diff.errorType)} (${getSeverityLabel(diff.severity)})\n`;
1042
+ content += ` النص الأصلي: ${diff.sourceText}\n`;
1043
+ content += ` النص المترجم: ${diff.targetText}\n`;
1044
+ content += ` الشرح: ${diff.explanation}\n\n`;
1045
+ });
1046
+
1047
+ // Add footer
1048
+ content += "تم إنشاء هذا التقرير بواسطة نظام تحليل الترجمة - شركة الريحان للترجمة";
1049
+
1050
+ return content;
1051
+ }
1052
+
1053
+ // Clear all inputs and results
1054
+ function clearAll() {
1055
+ // Clear text areas
1056
+ document.getElementById("sourceTextArea").value = "";
1057
+ document.getElementById("targetTextArea").value = "";
1058
+
1059
+ // Clear file names
1060
+ document.getElementById("sourceFileName").textContent = "لم يتم اختيار ملف";
1061
+ document.getElementById("targetFileName").textContent = "لم يتم اختيار ملف";
1062
+
1063
+ // Clear OCR results
1064
+ document.getElementById("sourceOCRResults").innerHTML = "";
1065
+ document.getElementById("targetOCRResults").innerHTML = "";
1066
+
1067
+ // Clear analysis results
1068
+ document.getElementById("resultsContainer").innerHTML = "";
1069
+ document.getElementById("resultsSection").classList.add("hidden");
1070
+
1071
+ // Reset PDF previews
1072
+ document.getElementById("sourcePDFPreview").innerHTML = "";
1073
+ document.getElementById("targetPDFPreview").innerHTML = "";
1074
+ document.getElementById("sourcePDFSection").classList.add("hidden");
1075
+ document.getElementById("targetPDFSection").classList.add("hidden");
1076
+
1077
+ // Reset global variables
1078
+ sourceText = "";
1079
+ targetText = "";
1080
+ sourceFile = null;
1081
+ targetFile = null;
1082
+ sourceFileType = "";
1083
+ targetFileType = "";
1084
+ analysis = null;
1085
+ correctedSourceText = "";
1086
+ detectedCountry = "";
1087
+
1088
+ // Reset document type to "regular"
1089
+ document.querySelector('input[name="documentType"][value="regular"]').checked = true;
1090
+ documentType = "regular";
1091
+
1092
+ // Disable buttons
1093
+ document.getElementById("analyzeBtn").disabled = true;
1094
+ document.getElementById("exportReportBtn").disabled = true;
1095
+
1096
+ showNotification("تم مسح جميع البيانات", "info");
1097
+ }
1098
+
1099
+ // Show PDF preview
1100
+ function showPDFPreview(file, type) {
1101
+ const previewSection = document.getElementById(`${type}PDFSection`);
1102
+ const previewContainer = document.getElementById(`${type}PDFPreview`);
1103
+
1104
+ // Clear previous preview
1105
+ previewContainer.innerHTML = "";
1106
+
1107
+ // Create PDF viewer
1108
+ const viewer = document.createElement("div");
1109
+ viewer.className = "pdf-viewer";
1110
+
1111
+ // Create PDF embed
1112
+ const embed = document.createElement("embed");
1113
+ embed.src = URL.createObjectURL(file);
1114
+ embed.type = "application/pdf";
1115
+ embed.width = "100%";
1116
+ embed.height = "500px";
1117
+
1118
+ viewer.appendChild(embed);
1119
+ previewContainer.appendChild(viewer);
1120
+
1121
+ // Show preview section
1122
+ previewSection.classList.remove("hidden");
1123
+ }
1124
+
1125
+ // Show OCR results
1126
+ function showOCRResults(text, type) {
1127
+ const resultsContainer = document.getElementById(`${type}OCRResults`);
1128
+
1129
+ // Create results display
1130
+ resultsContainer.innerHTML = `
1131
+ <div class="ocr-results p-3 bg-gray-50 rounded max-h-60 overflow-y-auto">
1132
+ <pre class="whitespace-pre-wrap text-sm">${text}</pre>
1133
+ </div>
1134
+ `;
1135
+ }
1136
+
1137
+ // Show notification
1138
+ function showNotification(message, type = "info") {
1139
+ // Create notification element if it doesn't exist
1140
+ let notification = document.getElementById("notification");
1141
+ if (!notification) {
1142
+ notification = document.createElement("div");
1143
+ notification.id = "notification";
1144
+ notification.className = "fixed bottom-4 right-4 p-4 rounded-lg shadow-lg transform transition-opacity duration-300 opacity-0";
1145
+ document.body.appendChild(notification);
1146
+ }
1147
+
1148
+ // Set notification type
1149
+ notification.className = notification.className.replace(/bg-\w+-\d+/g, "");
1150
+ switch (type) {
1151
+ case "success":
1152
+ notification.classList.add("bg-green-500", "text-white");
1153
+ break;
1154
+ case "error":
1155
+ notification.classList.add("bg-red-500", "text-white");
1156
+ break;
1157
+ case "warning":
1158
+ notification.classList.add("bg-yellow-500", "text-white");
1159
+ break;
1160
+ default:
1161
+ notification.classList.add("bg-blue-500", "text-white");
1162
+ }
1163
+
1164
+ // Set message
1165
+ notification.textContent = message;
1166
+
1167
+ // Show notification
1168
+ notification.classList.replace("opacity-0", "opacity-100");
1169
+
1170
+ // Hide notification after 3 seconds
1171
+ setTimeout(() => {
1172
+ notification.classList.replace("opacity-100", "opacity-0");
1173
+ }, 3000);
1174
+ }
1175
+
1176
+ // Toggle loading indicator
1177
+ function toggleLoading(show, section) {
1178
+ const loadingIndicators = {
1179
+ "source": document.getElementById("sourceLoadingIndicator"),
1180
+ "target": document.getElementById("targetLoadingIndicator"),
1181
+ "analysis": document.getElementById("analysisLoadingIndicator")
1182
+ };
1183
+
1184
+ const indicator = loadingIndicators[section];
1185
+ if (indicator) {
1186
+ if (show) {
1187
+ indicator.classList.remove("hidden");
1188
+ } else {
1189
+ indicator.classList.add("hidden");
1190
+ }
1191
+ }
1192
+ }
1193
+
1194
+ // Helper functions for displaying error types and severity
1195
+ function getErrorTypeLabel(errorType) {
1196
+ const labels = {
1197
+ "grammar": "خطأ نحوي",
1198
+ "spelling": "خطأ إملائي",
1199
+ "meaning": "اختلاف في المعنى",
1200
+ "missing": "نص مفقود",
1201
+ "addition": "إضافة غير ضرورية",
1202
+ "terminology": "مصطلح غير دقيق"
1203
+ };
1204
+
1205
+ return labels[errorType] || errorType;
1206
+ }
1207
+
1208
+ function getSeverityLabel(severity) {
1209
+ const labels = {
1210
+ "low": "منخفض",
1211
+ "medium": "متوسط",
1212
+ "high": "مرتفع"
1213
+ };
1214
+
1215
+ return labels[severity] || severity;
1216
+ }
1217
+
1218
+ function getSeverityColor(severity) {
1219
+ const colors = {
1220
+ "low": "yellow",
1221
+ "medium": "orange",
1222
+ "high": "red"
1223
+ };
1224
+
1225
+ return colors[severity] || "gray";
1226
+ }