Update index.html
Browse files- index.html +79 -155
index.html
CHANGED
@@ -9,9 +9,9 @@
|
|
9 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"></script>
|
10 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
11 |
<style>
|
12 |
-
/*
|
13 |
الحركات والتأثيرات
|
14 |
-
|
15 |
@keyframes gradient {
|
16 |
0% { background-position: 0% 50%; }
|
17 |
50% { background-position: 100% 50%; }
|
@@ -26,52 +26,43 @@
|
|
26 |
.animate-scale:hover { transform: scale(1.02); }
|
27 |
.pulse-animation { animation: pulse 2s infinite; }
|
28 |
@keyframes pulse {
|
29 |
-
0% { box-shadow: 0 0 0 0 rgba(
|
30 |
-
70% { box-shadow: 0 0 0 10px rgba(
|
31 |
-
100% { box-shadow: 0 0 0 0 rgba(
|
32 |
}
|
33 |
|
34 |
-
/*
|
35 |
-
تنسيقات النص والتظليل
|
36 |
-
|
37 |
.text-comparison { line-height: 1.8; white-space: pre-wrap; }
|
38 |
-
.highlight-number {
|
39 |
-
background-color: #FDE68A;
|
40 |
-
padding: 0 4px;
|
41 |
-
border-radius: 3px;
|
42 |
-
font-weight: bold;
|
43 |
-
}
|
44 |
.highlight-missing {
|
45 |
-
background-color: #
|
46 |
-
color: #
|
47 |
-
padding: 0 4px;
|
48 |
-
border-radius: 3px;
|
49 |
-
font-style: italic;
|
50 |
-
}
|
51 |
-
.highlight-extra {
|
52 |
-
background-color: #D1FAE5;
|
53 |
-
color: #065F46;
|
54 |
padding: 0 4px;
|
|
|
55 |
border-radius: 3px;
|
56 |
font-weight: bold;
|
57 |
}
|
58 |
-
.highlight-
|
59 |
-
background-color: #
|
|
|
60 |
padding: 0 4px;
|
|
|
61 |
border-radius: 3px;
|
62 |
font-weight: bold;
|
63 |
}
|
64 |
-
.highlight-
|
65 |
-
background-color: #
|
66 |
-
color: #
|
67 |
padding: 0 4px;
|
|
|
68 |
border-radius: 3px;
|
69 |
font-weight: bold;
|
70 |
}
|
71 |
|
72 |
-
/*
|
73 |
تنسيق الفقرات وترقيمها
|
74 |
-
|
75 |
.line-item {
|
76 |
margin-bottom: 1rem;
|
77 |
padding: 0.5rem;
|
@@ -84,18 +75,18 @@
|
|
84 |
}
|
85 |
.line-text { display: inline-block; }
|
86 |
|
87 |
-
/*
|
88 |
تصميم البطاقات والأيقونات
|
89 |
-
|
90 |
.card {
|
91 |
background-color: #fff;
|
92 |
border-radius: 1rem;
|
93 |
-
box-shadow: 0 10px 25px -5px rgba(
|
94 |
padding: 2rem;
|
95 |
border: 1px solid #f3f4f6;
|
96 |
}
|
97 |
.card-hover:hover {
|
98 |
-
box-shadow: 0 10px 25px -5px rgba(
|
99 |
transform: translateY(-3px);
|
100 |
}
|
101 |
.icon { margin-right: 0.5rem; }
|
@@ -110,7 +101,7 @@
|
|
110 |
<i class="fas fa-chart-line icon"></i> نظام المقارنة والتحليل المتقدم
|
111 |
</h1>
|
112 |
<p class="text-xl opacity-90">
|
113 |
-
<i class="fas fa-info-circle icon"></i>
|
114 |
</p>
|
115 |
</div>
|
116 |
</header>
|
@@ -228,14 +219,6 @@
|
|
228 |
</h2>
|
229 |
<div id="explanationText" class="text-lg text-gray-700"></div>
|
230 |
</div>
|
231 |
-
|
232 |
-
<!-- قسم الكلمات المفقودة (الكلمات التي لم تُترجم) -->
|
233 |
-
<div id="missingWordsSection" class="card hidden">
|
234 |
-
<h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
|
235 |
-
<i class="fas fa-exclamation-triangle icon text-red-500"></i> الكلمات المفقودة
|
236 |
-
</h2>
|
237 |
-
<div id="missingWordsList" class="text-lg text-red-700"></div>
|
238 |
-
</div>
|
239 |
</main>
|
240 |
</div>
|
241 |
|
@@ -245,20 +228,28 @@
|
|
245 |
<script>
|
246 |
(function() {
|
247 |
"use strict";
|
248 |
-
|
249 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
250 |
const API_URL = 'https://api.deepseek.com/chat/completions';
|
251 |
const API_KEY = 'sk-15606736ed9e4aea8b7cc11a195d2b01';
|
252 |
-
const ANALYSIS_PROMPT = `أنت خبير لغوي وتقني متخصص في مراجعة الترجمة التقنية وتحليل النصوص بدقة.
|
253 |
-
|
254 |
-
1.
|
255 |
-
2. لا
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
|
263 |
النص المصدر:
|
264 |
{source}
|
@@ -266,9 +257,9 @@
|
|
266 |
النص الهدف:
|
267 |
{target}`;
|
268 |
|
269 |
-
/*
|
270 |
دوال مساعدة عامة
|
271 |
-
|
272 |
const countWords = text =>
|
273 |
text.trim().split(/\s+/).filter(word => word !== "").length;
|
274 |
|
@@ -290,43 +281,33 @@
|
|
290 |
return text.substring(0, index).split("\n").length;
|
291 |
};
|
292 |
|
293 |
-
/*
|
294 |
-
دوال تظليل الاختلافات
|
295 |
-
|
296 |
const applyHighlights = (originalText, analysisOutput) => {
|
297 |
let highlightedText = originalText;
|
298 |
let match;
|
299 |
-
//
|
300 |
-
const numberRegex = /<([^<>]+)>/g;
|
301 |
-
while ((match = numberRegex.exec(analysisOutput)) !== null) {
|
302 |
-
const phrase = match[1].trim();
|
303 |
-
if (phrase) {
|
304 |
-
const replacement = `<span class="highlight-number">${phrase}</span>`;
|
305 |
-
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
306 |
-
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
307 |
-
}
|
308 |
-
}
|
309 |
-
// النصوص المفقودة
|
310 |
const missingRegex = /__(.*?)__/g;
|
311 |
while ((match = missingRegex.exec(analysisOutput)) !== null) {
|
312 |
const phrase = match[1].trim();
|
313 |
if (phrase) {
|
314 |
-
const replacement = `<span class="highlight-missing"
|
315 |
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
316 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
317 |
}
|
318 |
}
|
319 |
-
//
|
320 |
-
const
|
321 |
-
while ((match =
|
322 |
const phrase = match[1].trim();
|
323 |
if (phrase) {
|
324 |
-
const replacement = `<span class="highlight-
|
325 |
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
326 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
327 |
}
|
328 |
}
|
329 |
-
//
|
330 |
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
|
331 |
while ((match = meaningRegex.exec(analysisOutput)) !== null) {
|
332 |
const phrase = match[1].trim();
|
@@ -336,101 +317,53 @@
|
|
336 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
337 |
}
|
338 |
}
|
339 |
-
// علامات الشك أو الأخطاء البسيطة
|
340 |
-
const doubtRegex = /\[DOUBT\](.*?)\[\/DOUBT\]/g;
|
341 |
-
while ((match = doubtRegex.exec(analysisOutput)) !== null) {
|
342 |
-
const phrase = match[1].trim();
|
343 |
-
if (phrase) {
|
344 |
-
const replacement = `<span class="highlight-doubt">${phrase}</span>`;
|
345 |
-
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
346 |
-
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
347 |
-
}
|
348 |
-
}
|
349 |
return highlightedText;
|
350 |
};
|
351 |
|
352 |
-
// توليد شرح تفصيلي للاختلافات مع أيقونات
|
353 |
const generateExplanation = (sourceText, analysisOutput) => {
|
354 |
let steps = [];
|
355 |
let match;
|
356 |
-
const iconMissing = `<i class="fas fa-exclamation-triangle
|
357 |
-
const iconNumber = `<i class="fas fa-hashtag
|
358 |
-
const
|
359 |
-
const iconMeaning = `<i class="fas fa-info-circle text-blue-600 mr-1"></i>`;
|
360 |
-
const iconDoubt = `<i class="fas fa-question-circle text-indigo-600 mr-1"></i>`;
|
361 |
|
362 |
-
// النصوص المفقودة
|
363 |
const missingRegex = /__(.*?)__/g;
|
364 |
while ((match = missingRegex.exec(analysisOutput)) !== null) {
|
365 |
const phrase = match[1].trim();
|
366 |
if (phrase) {
|
367 |
const lineNum = getLineNumber(sourceText, phrase);
|
368 |
-
steps.push(`<li>${iconMissing} في السطر ${lineNum}، النص المفقود: <span class="highlight-missing"
|
369 |
}
|
370 |
}
|
371 |
-
//
|
372 |
const numberRegex = /<([^<>]+)>/g;
|
373 |
while ((match = numberRegex.exec(analysisOutput)) !== null) {
|
374 |
const phrase = match[1].trim();
|
375 |
if (phrase) {
|
376 |
const lineNum = getLineNumber(sourceText, phrase);
|
377 |
-
steps.push(`<li>${iconNumber} في السطر ${lineNum}، الرقم/التاريخ: <span class="highlight-number">${phrase}</span
|
378 |
}
|
379 |
}
|
380 |
-
//
|
381 |
-
const extraRegex = /<<EXTRA>>(.*?)<<\/EXTRA>>/g;
|
382 |
-
while ((match = extraRegex.exec(analysisOutput)) !== null) {
|
383 |
-
const phrase = match[1].trim();
|
384 |
-
if (phrase) {
|
385 |
-
const lineNum = getLineNumber(sourceText, phrase);
|
386 |
-
steps.push(`<li>${iconExtra} في السطر ${lineNum}، النص الزائد: <span class="highlight-extra">${phrase}</span> موجود بشكل غير متوقع في النص الهدف.</li>`);
|
387 |
-
}
|
388 |
-
}
|
389 |
-
// اختلافات المعنى
|
390 |
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
|
391 |
while ((match = meaningRegex.exec(analysisOutput)) !== null) {
|
392 |
const phrase = match[1].trim();
|
393 |
if (phrase) {
|
394 |
const lineNum = getLineNumber(sourceText, phrase);
|
395 |
-
steps.push(`<li>${iconMeaning} في السطر ${lineNum}، اختلاف
|
396 |
-
}
|
397 |
-
}
|
398 |
-
// علامات الشك أو الأخطاء البسيطة
|
399 |
-
const doubtRegex = /\[DOUBT\](.*?)\[\/DOUBT\]/g;
|
400 |
-
while ((match = doubtRegex.exec(analysisOutput)) !== null) {
|
401 |
-
const phrase = match[1].trim();
|
402 |
-
if (phrase) {
|
403 |
-
const lineNum = getLineNumber(sourceText, phrase);
|
404 |
-
steps.push(`<li>${iconDoubt} في السطر ${lineNum}، علامة شك: <span class="highlight-doubt">${phrase}</span> قد تشير إلى غموض أو خطأ بسيط يحتاج مراجعة.</li>`);
|
405 |
}
|
406 |
}
|
407 |
if (steps.length === 0) {
|
408 |
-
return `<p
|
409 |
}
|
410 |
return `<ol class="list-decimal ml-6 space-y-2">${steps.join('')}</ol>`;
|
411 |
};
|
412 |
|
413 |
-
/*
|
414 |
-
دالة اكتشاف الكلمات المفقودة (غير المُترجمة)
|
415 |
-
يتم التطبيع (إزالة علامات الترقيم وتحويل الأحرف لحروف صغيرة)
|
416 |
-
ثم مقارنة كلمات المصدر مع كلمات الهدف
|
417 |
-
======================================= */
|
418 |
-
const detectMissingWords = (source, target) => {
|
419 |
-
// دالة لتطبيع النص (إزالة علامات الترقيم وتحويل النص لحروف صغيرة)
|
420 |
-
const normalize = text =>
|
421 |
-
text.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()؟،؛"']/g, '').toLowerCase();
|
422 |
-
const sourceTokens = normalize(source).split(/\s+/).filter(word => word);
|
423 |
-
const targetTokens = normalize(target).split(/\s+/).filter(word => word);
|
424 |
-
const targetSet = new Set(targetTokens);
|
425 |
-
// الكلمات المفقودة هي التي تظهر في المصدر وغير موجودة في الهدف
|
426 |
-
const missing = sourceTokens.filter(word => !targetSet.has(word));
|
427 |
-
// إزالة التكرارات إن وُجدت
|
428 |
-
return [...new Set(missing)];
|
429 |
-
};
|
430 |
-
|
431 |
-
/* ===============================
|
432 |
دالة معالجة الملفات (PDF و DOCX)
|
433 |
-
|
434 |
const processFile = async file => {
|
435 |
let text = "";
|
436 |
try {
|
@@ -458,9 +391,9 @@
|
|
458 |
return text;
|
459 |
};
|
460 |
|
461 |
-
/*
|
462 |
التعامل مع رفع الملفات وإدخال النصوص
|
463 |
-
|
464 |
const processFileInput = (inputId, targetTextAreaId, errorMsg) => {
|
465 |
document.getElementById(inputId)?.addEventListener('change', async (event) => {
|
466 |
const file = event.target.files[0];
|
@@ -482,9 +415,9 @@
|
|
482 |
processFileInput('targetFile', 'targetText', 'خطأ في معالجة ملف الهدف');
|
483 |
processFileInput('sourceExtraFile', 'sourceExtraText', 'خطأ في معالجة ملف المصدر الإضافي');
|
484 |
|
485 |
-
/*
|
486 |
دالة عرض الأخطاء
|
487 |
-
|
488 |
const addError = (message, type = 'error') => {
|
489 |
const errorsList = document.getElementById('errorsList');
|
490 |
if (!errorsList) return;
|
@@ -497,9 +430,9 @@
|
|
497 |
errorsList.appendChild(errorDiv);
|
498 |
};
|
499 |
|
500 |
-
/*
|
501 |
التعامل مع عملية التحليل والمقارنة
|
502 |
-
|
503 |
document.getElementById('submitBtn').addEventListener('click', async () => {
|
504 |
const sourceText = document.getElementById('sourceText').value;
|
505 |
const targetText = document.getElementById('targetText').value;
|
@@ -558,6 +491,7 @@
|
|
558 |
const analysisOutput = data.choices[0].message.content.trim();
|
559 |
progressDiv.remove();
|
560 |
|
|
|
561 |
if (analysisOutput.includes('[MATCH]')) {
|
562 |
const checkDiv = document.createElement('div');
|
563 |
checkDiv.className = "p-4 rounded-xl bg-green-50 text-green-700 flex items-center";
|
@@ -567,6 +501,7 @@
|
|
567 |
document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText);
|
568 |
document.getElementById('explanationText').innerHTML = `<p>النصوص متطابقة ولا يوجد اختلاف يجب الإشارة إليه.</p>`;
|
569 |
} else {
|
|
|
570 |
const sourceHighlighted = applyHighlights(sourceText, analysisOutput);
|
571 |
const targetHighlighted = applyHighlights(targetText, analysisOutput);
|
572 |
document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
|
@@ -574,17 +509,6 @@
|
|
574 |
const explanationHTML = generateExplanation(sourceText, analysisOutput);
|
575 |
document.getElementById('explanationText').innerHTML = explanationHTML;
|
576 |
}
|
577 |
-
|
578 |
-
// اكتشاف الكلمات المفقودة التي لم تُترجم من المصدر (تظهر في المصدر وغير موجودة في الهدف)
|
579 |
-
const missingWords = detectMissingWords(sourceText, targetText);
|
580 |
-
if (missingWords.length > 0) {
|
581 |
-
const missingWordsSection = document.getElementById('missingWordsSection');
|
582 |
-
const missingWordsList = document.getElementById('missingWordsList');
|
583 |
-
missingWordsList.innerHTML = `<ul class="list-disc ml-4">${missingWords.map(word => `<li>${word}</li>`).join('')}</ul>`;
|
584 |
-
missingWordsSection.classList.remove('hidden');
|
585 |
-
} else {
|
586 |
-
document.getElementById('missingWordsSection').classList.add('hidden');
|
587 |
-
}
|
588 |
} catch (error) {
|
589 |
document.getElementById('errorsList').innerHTML = '';
|
590 |
addError(`خطأ في التحليل: ${error.message}`);
|
|
|
9 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"></script>
|
10 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
11 |
<style>
|
12 |
+
/* -------------------------------
|
13 |
الحركات والتأثيرات
|
14 |
+
------------------------------- */
|
15 |
@keyframes gradient {
|
16 |
0% { background-position: 0% 50%; }
|
17 |
50% { background-position: 100% 50%; }
|
|
|
26 |
.animate-scale:hover { transform: scale(1.02); }
|
27 |
.pulse-animation { animation: pulse 2s infinite; }
|
28 |
@keyframes pulse {
|
29 |
+
0% { box-shadow: 0 0 0 0 rgba(0,0,0,0.2); }
|
30 |
+
70% { box-shadow: 0 0 0 10px rgba(0,0,0,0); }
|
31 |
+
100% { box-shadow: 0 0 0 0 rgba(0,0,0,0); }
|
32 |
}
|
33 |
|
34 |
+
/* -------------------------------
|
35 |
+
تنسيقات النص والتظليل (ألوان بيضاء)
|
36 |
+
------------------------------- */
|
37 |
.text-comparison { line-height: 1.8; white-space: pre-wrap; }
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
.highlight-missing {
|
39 |
+
background-color: #ffffff;
|
40 |
+
color: #000;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
padding: 0 4px;
|
42 |
+
border: 1px solid #ccc;
|
43 |
border-radius: 3px;
|
44 |
font-weight: bold;
|
45 |
}
|
46 |
+
.highlight-number {
|
47 |
+
background-color: #ffffff;
|
48 |
+
color: #000;
|
49 |
padding: 0 4px;
|
50 |
+
border: 1px solid #ccc;
|
51 |
border-radius: 3px;
|
52 |
font-weight: bold;
|
53 |
}
|
54 |
+
.highlight-meaning {
|
55 |
+
background-color: #ffffff;
|
56 |
+
color: #000;
|
57 |
padding: 0 4px;
|
58 |
+
border: 1px solid #ccc;
|
59 |
border-radius: 3px;
|
60 |
font-weight: bold;
|
61 |
}
|
62 |
|
63 |
+
/* -------------------------------
|
64 |
تنسيق الفقرات وترقيمها
|
65 |
+
------------------------------- */
|
66 |
.line-item {
|
67 |
margin-bottom: 1rem;
|
68 |
padding: 0.5rem;
|
|
|
75 |
}
|
76 |
.line-text { display: inline-block; }
|
77 |
|
78 |
+
/* -------------------------------
|
79 |
تصميم البطاقات والأيقونات
|
80 |
+
------------------------------- */
|
81 |
.card {
|
82 |
background-color: #fff;
|
83 |
border-radius: 1rem;
|
84 |
+
box-shadow: 0 10px 25px -5px rgba(0,0,0,0.1);
|
85 |
padding: 2rem;
|
86 |
border: 1px solid #f3f4f6;
|
87 |
}
|
88 |
.card-hover:hover {
|
89 |
+
box-shadow: 0 10px 25px -5px rgba(0,0,0,0.2);
|
90 |
transform: translateY(-3px);
|
91 |
}
|
92 |
.icon { margin-right: 0.5rem; }
|
|
|
101 |
<i class="fas fa-chart-line icon"></i> نظام المقارنة والتحليل المتقدم
|
102 |
</h1>
|
103 |
<p class="text-xl opacity-90">
|
104 |
+
<i class="fas fa-info-circle icon"></i> تحليل دقيق لاكتشاف النصوص المفقودة والأرقام/التواريخ واختلاف المعنى
|
105 |
</p>
|
106 |
</div>
|
107 |
</header>
|
|
|
219 |
</h2>
|
220 |
<div id="explanationText" class="text-lg text-gray-700"></div>
|
221 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
</main>
|
223 |
</div>
|
224 |
|
|
|
228 |
<script>
|
229 |
(function() {
|
230 |
"use strict";
|
231 |
+
|
232 |
+
/* تحديث برومبت التحليل لتحديد ثلاثة بنود فقط:
|
233 |
+
1. النصوص المفقودة (الكلمات التي لم تُترجم أبداً)
|
234 |
+
2. الأرقام والتواريخ غير المتطا��قة
|
235 |
+
3. اختلاف المعنى إن وجد
|
236 |
+
يتم تمييز:
|
237 |
+
- النصوص المفقودة باستخدام __الكلمة__
|
238 |
+
- الأرقام والتواريخ باستخدام <الرقم/التاريخ>
|
239 |
+
- اختلاف المعنى باستخدام [MEANING] الاختلاف [/MEANING]
|
240 |
+
*/
|
241 |
const API_URL = 'https://api.deepseek.com/chat/completions';
|
242 |
const API_KEY = 'sk-15606736ed9e4aea8b7cc11a195d2b01';
|
243 |
+
const ANALYSIS_PROMPT = `أنت خبير لغوي وتقني متخصص في مراجعة الترجمة التقنية وتحليل النصوص بدقة.
|
244 |
+
مهمتك مقارنة النص المصدر والنص الهدف واستخراج الآتي:
|
245 |
+
1. **النصوص المفقودة:** الكلمات التي لم تُترجم أبداً من النص المصدر (لا يوجد لها مقابل في النص الهدف).
|
246 |
+
2. **الأرقام والتواريخ:** التي لا تتطابق بين المصدر والهدف.
|
247 |
+
3. **اختلاف المعنى:** في حال وجود اختلاف واضح.
|
248 |
+
يرجى تمييز:
|
249 |
+
- النصوص المفقودة بـ __الكلمة__
|
250 |
+
- الأرقام والتواريخ بـ <الرقم/التاريخ>
|
251 |
+
- اختلاف المعنى بـ [MEANING] الاختلاف [/MEANING]
|
252 |
+
قدم ناتجك في شكل قائمة تفصيلية مع شرح مختصر لكل اختلاف.
|
253 |
|
254 |
النص المصدر:
|
255 |
{source}
|
|
|
257 |
النص الهدف:
|
258 |
{target}`;
|
259 |
|
260 |
+
/* -------------------------------
|
261 |
دوال مساعدة عامة
|
262 |
+
------------------------------- */
|
263 |
const countWords = text =>
|
264 |
text.trim().split(/\s+/).filter(word => word !== "").length;
|
265 |
|
|
|
281 |
return text.substring(0, index).split("\n").length;
|
282 |
};
|
283 |
|
284 |
+
/* -------------------------------
|
285 |
+
دوال تظليل الاختلافات (العلامات: __ للنص المفقود، < > للأرقام/التواريخ، [MEANING] اختلاف المعنى)
|
286 |
+
------------------------------- */
|
287 |
const applyHighlights = (originalText, analysisOutput) => {
|
288 |
let highlightedText = originalText;
|
289 |
let match;
|
290 |
+
// تظليل النصوص المفقودة
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
const missingRegex = /__(.*?)__/g;
|
292 |
while ((match = missingRegex.exec(analysisOutput)) !== null) {
|
293 |
const phrase = match[1].trim();
|
294 |
if (phrase) {
|
295 |
+
const replacement = `<span class="highlight-missing">${phrase}</span>`;
|
296 |
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
297 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
298 |
}
|
299 |
}
|
300 |
+
// تظليل الأرقام والتواريخ
|
301 |
+
const numberRegex = /<([^<>]+)>/g;
|
302 |
+
while ((match = numberRegex.exec(analysisOutput)) !== null) {
|
303 |
const phrase = match[1].trim();
|
304 |
if (phrase) {
|
305 |
+
const replacement = `<span class="highlight-number">${phrase}</span>`;
|
306 |
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
307 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
308 |
}
|
309 |
}
|
310 |
+
// تظليل اختلاف المعنى
|
311 |
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
|
312 |
while ((match = meaningRegex.exec(analysisOutput)) !== null) {
|
313 |
const phrase = match[1].trim();
|
|
|
317 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
318 |
}
|
319 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
return highlightedText;
|
321 |
};
|
322 |
|
323 |
+
// توليد شرح تفصيلي للاختلافات مع أيقونات بسيطة
|
324 |
const generateExplanation = (sourceText, analysisOutput) => {
|
325 |
let steps = [];
|
326 |
let match;
|
327 |
+
const iconMissing = `<i class="fas fa-exclamation-triangle mr-1"></i>`;
|
328 |
+
const iconNumber = `<i class="fas fa-hashtag mr-1"></i>`;
|
329 |
+
const iconMeaning = `<i class="fas fa-info-circle mr-1"></i>`;
|
|
|
|
|
330 |
|
331 |
+
// النصوص المفقودة
|
332 |
const missingRegex = /__(.*?)__/g;
|
333 |
while ((match = missingRegex.exec(analysisOutput)) !== null) {
|
334 |
const phrase = match[1].trim();
|
335 |
if (phrase) {
|
336 |
const lineNum = getLineNumber(sourceText, phrase);
|
337 |
+
steps.push(`<li>${iconMissing} في السطر ${lineNum}، النص المفقود: <span class="highlight-missing">${phrase}</span></li>`);
|
338 |
}
|
339 |
}
|
340 |
+
// الأرقام والتواريخ
|
341 |
const numberRegex = /<([^<>]+)>/g;
|
342 |
while ((match = numberRegex.exec(analysisOutput)) !== null) {
|
343 |
const phrase = match[1].trim();
|
344 |
if (phrase) {
|
345 |
const lineNum = getLineNumber(sourceText, phrase);
|
346 |
+
steps.push(`<li>${iconNumber} في السطر ${lineNum}، الرقم/التاريخ: <span class="highlight-number">${phrase}</span></li>`);
|
347 |
}
|
348 |
}
|
349 |
+
// اختلاف المعنى
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
350 |
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
|
351 |
while ((match = meaningRegex.exec(analysisOutput)) !== null) {
|
352 |
const phrase = match[1].trim();
|
353 |
if (phrase) {
|
354 |
const lineNum = getLineNumber(sourceText, phrase);
|
355 |
+
steps.push(`<li>${iconMeaning} في السطر ${lineNum}، اختلاف المعنى: <span class="highlight-meaning">${phrase}</span></li>`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
356 |
}
|
357 |
}
|
358 |
if (steps.length === 0) {
|
359 |
+
return `<p><i class="fas fa-check-circle mr-2"></i> لا توجد اختلافات ملحوظة بين النصين.</p>`;
|
360 |
}
|
361 |
return `<ol class="list-decimal ml-6 space-y-2">${steps.join('')}</ol>`;
|
362 |
};
|
363 |
|
364 |
+
/* -------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
365 |
دالة معالجة الملفات (PDF و DOCX)
|
366 |
+
------------------------------- */
|
367 |
const processFile = async file => {
|
368 |
let text = "";
|
369 |
try {
|
|
|
391 |
return text;
|
392 |
};
|
393 |
|
394 |
+
/* -------------------------------
|
395 |
التعامل مع رفع الملفات وإدخال النصوص
|
396 |
+
------------------------------- */
|
397 |
const processFileInput = (inputId, targetTextAreaId, errorMsg) => {
|
398 |
document.getElementById(inputId)?.addEventListener('change', async (event) => {
|
399 |
const file = event.target.files[0];
|
|
|
415 |
processFileInput('targetFile', 'targetText', 'خطأ في معالجة ملف الهدف');
|
416 |
processFileInput('sourceExtraFile', 'sourceExtraText', 'خطأ في معالجة ملف المصدر الإضافي');
|
417 |
|
418 |
+
/* -------------------------------
|
419 |
دالة عرض الأخطاء
|
420 |
+
------------------------------- */
|
421 |
const addError = (message, type = 'error') => {
|
422 |
const errorsList = document.getElementById('errorsList');
|
423 |
if (!errorsList) return;
|
|
|
430 |
errorsList.appendChild(errorDiv);
|
431 |
};
|
432 |
|
433 |
+
/* -------------------------------
|
434 |
التعامل مع عملية التحليل والمقارنة
|
435 |
+
------------------------------- */
|
436 |
document.getElementById('submitBtn').addEventListener('click', async () => {
|
437 |
const sourceText = document.getElementById('sourceText').value;
|
438 |
const targetText = document.getElementById('targetText').value;
|
|
|
491 |
const analysisOutput = data.choices[0].message.content.trim();
|
492 |
progressDiv.remove();
|
493 |
|
494 |
+
// إذا كان الناتج يحتوي على علامة التطابق التام
|
495 |
if (analysisOutput.includes('[MATCH]')) {
|
496 |
const checkDiv = document.createElement('div');
|
497 |
checkDiv.className = "p-4 rounded-xl bg-green-50 text-green-700 flex items-center";
|
|
|
501 |
document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText);
|
502 |
document.getElementById('explanationText').innerHTML = `<p>النصوص متطابقة ولا يوجد اختلاف يجب الإشارة إليه.</p>`;
|
503 |
} else {
|
504 |
+
// تطبيق التظليل على النصوص بناءً على العلامات المستخرجة
|
505 |
const sourceHighlighted = applyHighlights(sourceText, analysisOutput);
|
506 |
const targetHighlighted = applyHighlights(targetText, analysisOutput);
|
507 |
document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
|
|
|
509 |
const explanationHTML = generateExplanation(sourceText, analysisOutput);
|
510 |
document.getElementById('explanationText').innerHTML = explanationHTML;
|
511 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
512 |
} catch (error) {
|
513 |
document.getElementById('errorsList').innerHTML = '';
|
514 |
addError(`خطأ في التحليل: ${error.message}`);
|