joermd commited on
Commit
babba69
·
verified ·
1 Parent(s): 6aefe85

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +136 -166
index.html CHANGED
@@ -36,20 +36,34 @@
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: #BFDBFE;
 
46
  padding: 0 4px;
47
  border-radius: 3px;
48
  font-style: italic;
49
  }
 
 
 
 
 
 
 
50
  .highlight-meaning {
51
- background-color: #fecaca;
52
- color: #B91C1C;
 
 
 
 
 
 
53
  padding: 0 4px;
54
  border-radius: 3px;
55
  font-weight: bold;
@@ -178,7 +192,7 @@
178
  <!-- زر التحليل -->
179
  <button id="submitBtn" class="w-full bg-gradient-to-r from-indigo-600 to-pink-600 hover:from-indigo-700 hover:to-pink-700 text-white font-bold py-5 px-8 rounded-xl transition-all transform hover:scale-105 focus:ring-indigo-200 text-xl shadow-lg hover:shadow-xl mb-8 pulse-animation">
180
  <div class="flex items-center justify-center">
181
- <i class="fas fa-sync-alt ml-2"></i> تحليل النصوص
182
  </div>
183
  </button>
184
 
@@ -213,30 +227,6 @@
213
  </h2>
214
  <div id="explanationText" class="text-lg text-gray-700"></div>
215
  </div>
216
-
217
- <!-- زر إعادة المراجعة -->
218
- <button id="reviewAgainBtn" class="w-full bg-gray-600 hover:bg-gray-700 text-white font-bold py-3 px-6 rounded-xl transition-all transform hover:scale-105 focus:ring-gray-200 text-xl shadow-lg hover:shadow-xl mb-8">
219
- <div class="flex items-center justify-center">
220
- <i class="fas fa-redo ml-2"></i> إعادة المراجعة
221
- </div>
222
- </button>
223
-
224
- <!-- قسم إعادة المراجعة: قائمة منسدلة لتحديد نوع المراجعة -->
225
- <div id="reviewAgainSection" class="hidden bg-white rounded-2xl shadow-lg p-8 border border-gray-100 hover:shadow-xl transition-all animate-scale mb-8 card-hover">
226
- <h2 class="text-2xl font-bold mb-6 text-gray-800 border-b pb-3 flex items-center">
227
- <i class="fas fa-redo text-blue-600 ml-2"></i> إعادة المراجعة - اختيار النوع
228
- </h2>
229
- <div class="flex items-center space-x-4">
230
- <select id="reviewType" class="px-4 py-2 border rounded-xl focus:outline-none">
231
- <option value="all">جميع الاختلافات</option>
232
- <option value="number">مراجعة رقمية فقط</option>
233
- <option value="meaning">مراجعة المعنى فقط</option>
234
- <option value="missing">مراجعة النص المفقود فقط</option>
235
- </select>
236
- <button id="confirmReviewBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-xl">تنفيذ إعادة المراجعة</button>
237
- </div>
238
- </div>
239
-
240
  </main>
241
  </div>
242
 
@@ -244,54 +234,76 @@
244
  جافا سكريبت: الوظائف والمعالجة
245
  ================================= -->
246
  <script>
247
- // متغيرات لتخزين النصوص ونتيجة التحليل لإعادة المراجعة
248
- let storedAnalysisOutput = "";
249
- let storedSourceText = "";
250
- let storedTargetText = "";
251
-
252
  // إعدادات API
253
  const API_URL = 'https://api.deepseek.com/chat/completions';
254
  const API_KEY = 'sk-15606736ed9e4aea8b7cc11a195d2b01';
255
 
256
- const ANALYSIS_PROMPT = `أنت خبير لغوي متخصص في مراجعة الترجمة التقنية. مهمتك مقارنة النص المصدر والنص الهدف بدقة عالية مع العلم أن النصوص مليانة بالأخطاء والنواقص.
 
 
 
 
 
 
 
 
 
257
  لا تقم بإزالة أو تعديل العلامات التالية:
258
- الأرقام: تحافظ على علامات < و >.
259
  • النصوص المفقودة: تحافظ على علامات __ و __.
 
260
  • اختلافات المعنى: تحافظ على علامات [MEANING] و [/MEANING].
 
261
 
262
  اعتمد النص المصدر كأساس للمقارنة، وقم بتحديد:
263
- 1. اختلافات الأرقام باستخدام <الرقم_في_المصدر> → <الرقم_في_الهدف>.
264
  2. النصوص المفقودة كما هي بين علامتي __.
265
- 3. اختلافات المعنى باستخدام [MEANING] مع الحفاظ على التعليم.
266
-
 
 
267
  النص المصدر:
268
  {source}
269
-
270
  النص الهدف:
271
  {target}`;
272
 
273
- // دوال مساعدة للتعامل مع النصوص
 
 
 
274
  function countWords(text) {
275
  return text.trim().split(/\s+/).filter(word => word !== "").length;
276
  }
 
277
  function escapeRegExp(string) {
278
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
279
  }
 
280
  function splitIntoLines(text) {
281
- return text.split('\n').map((line, i) => `<div class="line-item"><span class="line-number">${i+1}:</span> <span class="line-text">${line}</span></div>`).join('');
 
 
 
 
 
 
282
  }
 
283
  function getLineNumber(text, substring) {
284
  const index = text.indexOf(substring);
285
  if (index === -1) return "غير محدد";
286
  return text.substring(0, index).split("\n").length;
287
  }
288
 
289
- // دوال التمييز (Highlighting)
 
 
290
  function applyHighlights(originalText, analysisOutput) {
291
  let highlightedText = originalText;
292
- // اختلافات الأرقام
293
- const numberRegex = /<([^<>]+)>/g;
294
  let match;
 
 
295
  while ((match = numberRegex.exec(analysisOutput)) !== null) {
296
  const phrase = match[1].trim();
297
  if (phrase) {
@@ -300,7 +312,7 @@
300
  highlightedText = highlightedText.replace(phraseRegex, replacement);
301
  }
302
  }
303
- // النصوص المفقودة
304
  const missingRegex = /__(.*?)__/g;
305
  while ((match = missingRegex.exec(analysisOutput)) !== null) {
306
  const phrase = match[1].trim();
@@ -310,7 +322,17 @@
310
  highlightedText = highlightedText.replace(phraseRegex, replacement);
311
  }
312
  }
313
- // اختلافات المعنى
 
 
 
 
 
 
 
 
 
 
314
  const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
315
  while ((match = meaningRegex.exec(analysisOutput)) !== null) {
316
  const phrase = match[1].trim();
@@ -320,56 +342,28 @@
320
  highlightedText = highlightedText.replace(phraseRegex, replacement);
321
  }
322
  }
323
- return highlightedText;
324
- }
325
-
326
- // تطبيق التمييز بحسب نوع المراجعة
327
- function applyHighlightsByType(originalText, analysisOutput, reviewType) {
328
- let highlightedText = originalText;
329
- if (reviewType === 'meaning') {
330
- const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
331
- let match;
332
- while ((match = meaningRegex.exec(analysisOutput)) !== null) {
333
- const phrase = match[1].trim();
334
- if (phrase) {
335
- const replacement = `<span class="highlight-meaning">${phrase}</span>`;
336
- const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
337
- highlightedText = highlightedText.replace(phraseRegex, replacement);
338
- }
339
- }
340
- } else if (reviewType === 'number') {
341
- const numberRegex = /<([^<>]+)>/g;
342
- let match;
343
- while ((match = numberRegex.exec(analysisOutput)) !== null) {
344
- const phrase = match[1].trim();
345
- if (phrase) {
346
- const replacement = `<span class="highlight-number">${phrase}</span>`;
347
- const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
348
- highlightedText = highlightedText.replace(phraseRegex, replacement);
349
- }
350
- }
351
- } else if (reviewType === 'missing') {
352
- const missingRegex = /__(.*?)__/g;
353
- let match;
354
- while ((match = missingRegex.exec(analysisOutput)) !== null) {
355
- const phrase = match[1].trim();
356
- if (phrase) {
357
- const replacement = `<span class="highlight-missing">__${phrase}__</span>`;
358
- const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
359
- highlightedText = highlightedText.replace(phraseRegex, replacement);
360
- }
361
  }
362
- } else {
363
- highlightedText = applyHighlights(originalText, analysisOutput);
364
  }
365
  return highlightedText;
366
  }
367
 
368
- // توليد الشرح التفصيلي
 
 
 
369
  function generateExplanation(sourceText, analysisOutput) {
370
  let steps = [];
371
- const missingRegex = /__(.*?)__/g;
372
  let match;
 
 
373
  while ((match = missingRegex.exec(analysisOutput)) !== null) {
374
  const phrase = match[1].trim();
375
  if (phrase) {
@@ -377,71 +371,52 @@
377
  steps.push(`<li><strong>الخطوة 1:</strong> في السطر ${lineNum}، الجزء "<span class="highlight-missing">__${phrase}__</span>" من النص المصدر مفقود في النص الهدف. تأكد من إضافته لتحسين الدقة.</li>`);
378
  }
379
  }
 
380
  const numberRegex = /<([^<>]+)>/g;
381
  while ((match = numberRegex.exec(analysisOutput)) !== null) {
382
  const phrase = match[1].trim();
383
  if (phrase) {
384
  const lineNum = getLineNumber(sourceText, phrase);
385
- steps.push(`<li><strong>الخطوة 2:</strong> في السطر ${lineNum}، الرقم "<span class="highlight-number">${phrase}</span>" في المصدر لا يتطابق مع الرقم في الهدف. يرجى المراجعة.</li>`);
 
 
 
 
 
 
 
 
 
386
  }
387
  }
 
388
  const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
389
  while ((match = meaningRegex.exec(analysisOutput)) !== null) {
390
  const phrase = match[1].trim();
391
  if (phrase) {
392
  const lineNum = getLineNumber(sourceText, phrase);
393
- steps.push(`<li><strong>الخطوة 3:</strong> في السطر ${lineNum}، تم العثور على اختلاف في المعنى مع التعبير "<span class="highlight-meaning">${phrase}</span>". تحقق من الدقة.</li>`);
394
  }
395
  }
396
- if (steps.length === 0) {
397
- return `<p>النصوص متطابقة تماماً ولا توجد فروقات تحتاج للتنبيه.</p>`;
398
- }
399
- return `<ol class="list-decimal ml-6 space-y-2">${steps.join('')}</ol>`;
400
- }
401
-
402
- // توليد الشرح بحسب نوع إعادة المراجعة
403
- function generateExplanationByType(sourceText, analysisOutput, reviewType) {
404
- let steps = [];
405
- if (reviewType === 'meaning') {
406
- const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
407
- let match;
408
- while ((match = meaningRegex.exec(analysisOutput)) !== null) {
409
- const phrase = match[1].trim();
410
- if (phrase) {
411
- const lineNum = getLineNumber(sourceText, phrase);
412
- steps.push(`<li><strong>مراجعة المعنى:</strong> في السطر ${lineNum}، التعبير "<span class="highlight-meaning">${phrase}</span>" يختلف. تحقق من الدقة.</li>`);
413
- }
414
- }
415
- } else if (reviewType === 'number') {
416
- const numberRegex = /<([^<>]+)>/g;
417
- let match;
418
- while ((match = numberRegex.exec(analysisOutput)) !== null) {
419
- const phrase = match[1].trim();
420
- if (phrase) {
421
- const lineNum = getLineNumber(sourceText, phrase);
422
- steps.push(`<li><strong>مراجعة رقمية:</strong> في السطر ${lineNum}، الرقم "<span class="highlight-number">${phrase}</span>" لا يتطابق. يرجى المراجعة.</li>`);
423
- }
424
- }
425
- } else if (reviewType === 'missing') {
426
- const missingRegex = /__(.*?)__/g;
427
- let match;
428
- while ((match = missingRegex.exec(analysisOutput)) !== null) {
429
- const phrase = match[1].trim();
430
- if (phrase) {
431
- const lineNum = getLineNumber(sourceText, phrase);
432
- steps.push(`<li><strong>مراجعة النص المفقود:</strong> في السطر ${lineNum}، النص "<span class="highlight-missing">__${phrase}__</span>" مفقود أو مختلف. يرجى المراجعة.</li>`);
433
- }
434
  }
435
- } else {
436
- return generateExplanation(sourceText, analysisOutput);
437
  }
 
438
  if (steps.length === 0) {
439
- return `<p>لا توجد اختلافات من النوع المحدد.</p>`;
440
  }
441
  return `<ol class="list-decimal ml-6 space-y-2">${steps.join('')}</ol>`;
442
  }
443
 
444
- // دالة معالجة الملفات (PDF و DOCX)
 
 
445
  async function processFile(file) {
446
  let text = "";
447
  if (file.type === 'application/pdf') {
@@ -464,7 +439,10 @@
464
  return text;
465
  }
466
 
467
- // رفع الملفات وإدخال النصوص
 
 
 
468
  document.getElementById('sourceFile')?.addEventListener('change', async (event) => {
469
  const file = event.target.files[0];
470
  if (!file) return;
@@ -479,6 +457,7 @@
479
  document.getElementById('processStatus').classList.add('hidden');
480
  }
481
  });
 
482
  document.getElementById('targetFile')?.addEventListener('change', async (event) => {
483
  const file = event.target.files[0];
484
  if (!file) return;
@@ -493,6 +472,7 @@
493
  document.getElementById('processStatus').classList.add('hidden');
494
  }
495
  });
 
496
  document.getElementById('sourceExtraFile')?.addEventListener('change', async (event) => {
497
  const file = event.target.files[0];
498
  if (!file) return;
@@ -508,7 +488,9 @@
508
  }
509
  });
510
 
511
- // عرض الأخطاء والرسائل
 
 
512
  function addError(message, type = 'error') {
513
  const errorsList = document.getElementById('errorsList');
514
  if (!errorsList) return;
@@ -521,10 +503,13 @@
521
  errorsList.appendChild(errorDiv);
522
  }
523
 
524
- // تنفيذ التحليل عند الضغط على زر التحليل
 
 
525
  document.getElementById('submitBtn').addEventListener('click', async () => {
526
  const sourceText = document.getElementById('sourceText').value;
527
  const targetText = document.getElementById('targetText').value;
 
528
  document.getElementById('errorsList').innerHTML = '';
529
  document.getElementById('resultSection').classList.remove('hidden');
530
  document.getElementById('explanationBox').classList.remove('hidden');
@@ -534,6 +519,7 @@
534
  return;
535
  }
536
 
 
537
  const sourceWordCount = countWords(sourceText);
538
  const targetWordCount = countWords(targetText);
539
  if (sourceWordCount !== targetWordCount) {
@@ -541,6 +527,7 @@
541
  }
542
 
543
  try {
 
544
  const progressDiv = document.createElement('div');
545
  progressDiv.className = "bg-indigo-100 p-4 rounded-xl mb-4";
546
  progressDiv.innerHTML = `<div class="flex items-center">
@@ -549,10 +536,12 @@
549
  </div>`;
550
  document.getElementById('errorsList').appendChild(progressDiv);
551
 
 
552
  const prompt = ANALYSIS_PROMPT
553
  .replace("{source}", sourceText)
554
  .replace("{target}", targetText);
555
 
 
556
  const payload = {
557
  model: "deepseek-chat",
558
  messages: [
@@ -578,11 +567,7 @@
578
  const analysisOutput = data.choices[0].message.content.trim();
579
  progressDiv.remove();
580
 
581
- // تخزين النصوص والنتيجة لإعادة المراجعة
582
- storedAnalysisOutput = analysisOutput;
583
- storedSourceText = sourceText;
584
- storedTargetText = targetText;
585
-
586
  if (analysisOutput.includes('[MATCH]')) {
587
  const checkDiv = document.createElement('div');
588
  checkDiv.className = "p-4 rounded-xl bg-green-50 text-green-700 flex items-center";
@@ -592,23 +577,30 @@
592
  document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText);
593
  document.getElementById('explanationText').innerHTML = `<p>النصوص متطابقة ولا يوجد اختلاف يجب الإشارة إليه.</p>`;
594
  } else {
 
595
  const sourceHighlighted = applyHighlights(sourceText, analysisOutput);
596
  const targetHighlighted = applyHighlights(targetText, analysisOutput);
597
  document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
598
  document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetHighlighted);
 
599
  const explanationHTML = generateExplanation(sourceText, analysisOutput);
600
  document.getElementById('explanationText').innerHTML = explanationHTML;
601
 
 
602
  const numDiffCount = (analysisOutput.match(/<([^<>]+)>/g) || []).length;
603
  const missingDiffCount = (analysisOutput.match(/__(.*?)__/g) || []).length;
 
604
  const meaningDiffCount = (analysisOutput.match(/\[MEANING\](.*?)\[\/MEANING\]/g) || []).length;
605
- if (numDiffCount > 0 || missingDiffCount > 0 || meaningDiffCount > 0) {
 
606
  const summaryDiv = document.createElement('div');
607
  summaryDiv.className = "p-4 rounded-xl bg-yellow-50 text-gray-800";
608
  let summaryText = '<div class="font-bold mb-2">ملخص الاختلافات:</div><ul class="list-disc mr-6 space-y-1">';
609
- if (numDiffCount > 0) summaryText += `<li>اختلاف في الأرقام: ${numDiffCount}</li>`;
610
  if (missingDiffCount > 0) summaryText += `<li>النصوص المفقودة: ${missingDiffCount}</li>`;
 
611
  if (meaningDiffCount > 0) summaryText += `<li>اختلاف في المعنى: ${meaningDiffCount}</li>`;
 
612
  summaryText += '</ul>';
613
  summaryDiv.innerHTML = summaryText;
614
  document.getElementById('errorsList').appendChild(summaryDiv);
@@ -619,28 +611,6 @@
619
  addError(`خطأ في التحليل: ${error.message}`);
620
  }
621
  });
622
-
623
- // زر إعادة المراجعة: إخفاء النتائج القديمة وعرض قسم اختيار النوع
624
- document.getElementById('reviewAgainBtn').addEventListener('click', () => {
625
- document.getElementById('resultSection').classList.add('hidden');
626
- document.getElementById('explanationBox').classList.add('hidden');
627
- document.getElementById('errorsList').innerHTML = '';
628
- document.getElementById('reviewAgainSection').classList.remove('hidden');
629
- });
630
-
631
- // تنفيذ إعادة المراجعة بناءً على النوع المحدد
632
- document.getElementById('confirmReviewBtn').addEventListener('click', () => {
633
- const reviewType = document.getElementById('reviewType').value;
634
- const explanationHTML = generateExplanationByType(storedSourceText, storedAnalysisOutput, reviewType);
635
- document.getElementById('explanationText').innerHTML = explanationHTML;
636
- const sourceHighlighted = applyHighlightsByType(storedSourceText, storedAnalysisOutput, reviewType);
637
- const targetHighlighted = applyHighlightsByType(storedTargetText, storedAnalysisOutput, reviewType);
638
- document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
639
- document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetHighlighted);
640
- document.getElementById('reviewAgainSection').classList.add('hidden');
641
- document.getElementById('resultSection').classList.remove('hidden');
642
- document.getElementById('explanationBox').classList.remove('hidden');
643
- });
644
  </script>
645
  </body>
646
- </html>
 
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: #FECACA; /* أحمر للنصوص الناقصة */
46
+ color: #B91C1C;
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-meaning {
59
+ background-color: #E0E7FF;
60
+ padding: 0 4px;
61
+ border-radius: 3px;
62
+ font-weight: bold;
63
+ }
64
+ .highlight-doubt {
65
+ background-color: #DBEAFE; /* أزرق لعلامات الشك أو الأخطاء البسيطة */
66
+ color: #1E3A8A;
67
  padding: 0 4px;
68
  border-radius: 3px;
69
  font-weight: bold;
 
192
  <!-- زر التحليل -->
193
  <button id="submitBtn" class="w-full bg-gradient-to-r from-indigo-600 to-pink-600 hover:from-indigo-700 hover:to-pink-700 text-white font-bold py-5 px-8 rounded-xl transition-all transform hover:scale-105 focus:ring-indigo-200 text-xl shadow-lg hover:shadow-xl mb-8 pulse-animation">
194
  <div class="flex items-center justify-center">
195
+ <i class="fas fa-sync-alt ml-2"></i> تحليل ومراجعة النصوص
196
  </div>
197
  </button>
198
 
 
227
  </h2>
228
  <div id="explanationText" class="text-lg text-gray-700"></div>
229
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  </main>
231
  </div>
232
 
 
234
  جافا سكريبت: الوظائف والمعالجة
235
  ================================= -->
236
  <script>
 
 
 
 
 
237
  // إعدادات API
238
  const API_URL = 'https://api.deepseek.com/chat/completions';
239
  const API_KEY = 'sk-15606736ed9e4aea8b7cc11a195d2b01';
240
 
241
+ /*
242
+ البرومبت الجديد:
243
+ - تحليل النص كفقرات مع التأكد من انتهاء كل فقرة بنقطة.
244
+ - التحقق من النصوص الناقصة، النصوص الزائدة، اختلافات المعنى،
245
+ اختلافات الأرقام/التواريخ وعلامات الشك أو الأخطاء البسيطة.
246
+ - تُحاط النصوص الناقصة بعلامتي __، النصوص الزائدة بـ <<EXTRA>> و <<\/EXTRA>>,
247
+ اختلافات المعنى بـ [MEANING] و [/MEANING]،
248
+ والأخطاء أو علامات الشك بـ [DOUBT] و [/DOUBT].
249
+ */
250
+ const ANALYSIS_PROMPT = `أنت خبير لغوي متخصص في مراجعة الترجمة التقنية. مهمتك مقارنة النص المصدر والنص الهدف بدقة عالية وتحليل النص كفقرات منفصلة مع التأكد من أن كل فقرة تنتهي بنقطة. النصوص مليئة بالأخطاء والنواقص.
251
  لا تقم بإزالة أو تعديل العلامات التالية:
252
+ الأرقام والتواريخ: تحافظ على علامات < و >.
253
  • النصوص المفقودة: تحافظ على علامات __ و __.
254
+ • النصوص الزائدة: ضعها بين <<EXTRA>> و <<\/EXTRA>>.
255
  • اختلافات المعنى: تحافظ على علامات [MEANING] و [/MEANING].
256
+ • علامات الشك أو الأخطاء البسيطة: ضعها بين [DOUBT] و [/DOUBT].
257
 
258
  اعتمد النص المصدر كأساس للمقارنة، وقم بتحديد:
259
+ 1. اختلافات الأرقام/التواريخ باستخدام <الرقم/التاريخ_في_المصدر> → <الرقم/التاريخ_في_الهدف>.
260
  2. النصوص المفقودة كما هي بين علامتي __.
261
+ 3. النصوص الزائدة كما هي بين <<EXTRA>> و <<\/EXTRA>>.
262
+ 4. اختلافات المعنى باستخدام [MEANING] مع الحفاظ على التعليم.
263
+ 5. علامات الشك أو الأخطاء البسيطة باستخدام [DOUBT] مع ضرورة مراجعة دقيقة لتفادي التظليل الوهمي.
264
+
265
  النص المصدر:
266
  {source}
267
+
268
  النص الهدف:
269
  {target}`;
270
 
271
+ /* =====================================
272
+ دوال مساعدة للتعامل مع النصوص
273
+ ===================================== */
274
+ // حساب عدد الكلمات في النص
275
  function countWords(text) {
276
  return text.trim().split(/\s+/).filter(word => word !== "").length;
277
  }
278
+ // هروب أحرف Regex الخاصة
279
  function escapeRegExp(string) {
280
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
281
  }
282
+ // تقسيم النص إلى فقرات مع التأكد من انتهاء كل فقرة بنقطة وترقيمها
283
  function splitIntoLines(text) {
284
+ return text.split(/\n+/).map((line, i) => {
285
+ line = line.trim();
286
+ if(line && !line.endsWith('.')) {
287
+ line += '.';
288
+ }
289
+ return `<div class="line-item"><span class="line-number">${i+1}:</span> <span class="line-text">${line}</span></div>`;
290
+ }).join('');
291
  }
292
+ // الحصول على رقم السطر لظهور عبارة معينة
293
  function getLineNumber(text, substring) {
294
  const index = text.indexOf(substring);
295
  if (index === -1) return "غير محدد";
296
  return text.substring(0, index).split("\n").length;
297
  }
298
 
299
+ /* =====================================
300
+ دوال التمييز (Highlighting) للنتائج
301
+ ===================================== */
302
  function applyHighlights(originalText, analysisOutput) {
303
  let highlightedText = originalText;
 
 
304
  let match;
305
+ // تمييز اختلافات الأرقام/التواريخ
306
+ const numberRegex = /<([^<>]+)>/g;
307
  while ((match = numberRegex.exec(analysisOutput)) !== null) {
308
  const phrase = match[1].trim();
309
  if (phrase) {
 
312
  highlightedText = highlightedText.replace(phraseRegex, replacement);
313
  }
314
  }
315
+ // تمييز النصوص المفقودة
316
  const missingRegex = /__(.*?)__/g;
317
  while ((match = missingRegex.exec(analysisOutput)) !== null) {
318
  const phrase = match[1].trim();
 
322
  highlightedText = highlightedText.replace(phraseRegex, replacement);
323
  }
324
  }
325
+ // تمييز النصوص الزائدة
326
+ const extraRegex = /<<EXTRA>>(.*?)<<\/EXTRA>>/g;
327
+ while ((match = extraRegex.exec(analysisOutput)) !== null) {
328
+ const phrase = match[1].trim();
329
+ if (phrase) {
330
+ const replacement = `<span class="highlight-extra">${phrase}</span>`;
331
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
332
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
333
+ }
334
+ }
335
+ // تمييز اختلافات المعنى
336
  const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
337
  while ((match = meaningRegex.exec(analysisOutput)) !== null) {
338
  const phrase = match[1].trim();
 
342
  highlightedText = highlightedText.replace(phraseRegex, replacement);
343
  }
344
  }
345
+ // تمييز علامات الشك أو الأخطاء البسيطة
346
+ const doubtRegex = /\[DOUBT\](.*?)\[\/DOUBT\]/g;
347
+ while ((match = doubtRegex.exec(analysisOutput)) !== null) {
348
+ const phrase = match[1].trim();
349
+ if (phrase) {
350
+ const replacement = `<span class="highlight-doubt">${phrase}</span>`;
351
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
352
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  }
 
 
354
  }
355
  return highlightedText;
356
  }
357
 
358
+ /* =====================================
359
+ دالة توليد الشرح التفصيلي Organized Explanation
360
+ يتم تقسيم الشرح إلى خطوات منظمة
361
+ ===================================== */
362
  function generateExplanation(sourceText, analysisOutput) {
363
  let steps = [];
 
364
  let match;
365
+ // الخطوة 1: النصوص المفقودة
366
+ const missingRegex = /__(.*?)__/g;
367
  while ((match = missingRegex.exec(analysisOutput)) !== null) {
368
  const phrase = match[1].trim();
369
  if (phrase) {
 
371
  steps.push(`<li><strong>الخطوة 1:</strong> في السطر ${lineNum}، الجزء "<span class="highlight-missing">__${phrase}__</span>" من النص المصدر مفقود في النص الهدف. تأكد من إضافته لتحسين الدقة.</li>`);
372
  }
373
  }
374
+ // الخطوة 2: اختلافات الأرقام/التواريخ
375
  const numberRegex = /<([^<>]+)>/g;
376
  while ((match = numberRegex.exec(analysisOutput)) !== null) {
377
  const phrase = match[1].trim();
378
  if (phrase) {
379
  const lineNum = getLineNumber(sourceText, phrase);
380
+ steps.push(`<li><strong>الخطوة 2:</strong> في السطر ${lineNum}، الرقم أو التاريخ "<span class="highlight-number">${phrase}</span>" في المصدر لا يتطابق مع الهدف. يرجى المراجعة.</li>`);
381
+ }
382
+ }
383
+ // الخطوة 3: النصوص الزائدة
384
+ const extraRegex = /<<EXTRA>>(.*?)<<\/EXTRA>>/g;
385
+ while ((match = extraRegex.exec(analysisOutput)) !== null) {
386
+ const phrase = match[1].trim();
387
+ if (phrase) {
388
+ const lineNum = getLineNumber(sourceText, phrase);
389
+ steps.push(`<li><strong>تنبيه إضافي:</strong> في السطر ${lineNum}، النص "<span class="highlight-extra">${phrase}</span>" موجود كإضافة غير متوقعة في النص الهدف. يرجى المراجعة.</li>`);
390
  }
391
  }
392
+ // الخطوة 4: اختلافات المعنى
393
  const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
394
  while ((match = meaningRegex.exec(analysisOutput)) !== null) {
395
  const phrase = match[1].trim();
396
  if (phrase) {
397
  const lineNum = getLineNumber(sourceText, phrase);
398
+ steps.push(`<li><strong>الخطوة 4:</strong> في السطر ${lineNum}، تم العثور على اختلاف في المعنى مع التعبير "<span class="highlight-meaning">${phrase}</span>". تحقق من الدقة.</li>`);
399
  }
400
  }
401
+ // الخطوة 5: علامات الشك أو الأخطاء البسيطة
402
+ const doubtRegex = /\[DOUBT\](.*?)\[\/DOUBT\]/g;
403
+ while ((match = doubtRegex.exec(analysisOutput)) !== null) {
404
+ const phrase = match[1].trim();
405
+ if (phrase) {
406
+ const lineNum = getLineNumber(sourceText, phrase);
407
+ steps.push(`<li><strong>تنبيه:</strong> في السطر ${lineNum}، تم العثور على علامة شك أو خطأ بسيط "<span class="highlight-doubt">${phrase}</span>". يُنصح بمراجعة علامات الترقيم والأخطاء البسيطة.</li>`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
  }
 
 
409
  }
410
+ // إذا لم يكن هناك اختلافات
411
  if (steps.length === 0) {
412
+ return `<p>النصوص متطابقة تماماً ولا توجد فروقات تحتاج للتنبيه.</p>`;
413
  }
414
  return `<ol class="list-decimal ml-6 space-y-2">${steps.join('')}</ol>`;
415
  }
416
 
417
+ /* =====================================
418
+ دالة معالجة الملفات (PDF و DOCX)
419
+ ===================================== */
420
  async function processFile(file) {
421
  let text = "";
422
  if (file.type === 'application/pdf') {
 
439
  return text;
440
  }
441
 
442
+ /* =====================================
443
+ دوال رفع الملفات وإدخال النصوص
444
+ ===================================== */
445
+ // رفع ملف السورس
446
  document.getElementById('sourceFile')?.addEventListener('change', async (event) => {
447
  const file = event.target.files[0];
448
  if (!file) return;
 
457
  document.getElementById('processStatus').classList.add('hidden');
458
  }
459
  });
460
+ // رفع ملف التارجت
461
  document.getElementById('targetFile')?.addEventListener('change', async (event) => {
462
  const file = event.target.files[0];
463
  if (!file) return;
 
472
  document.getElementById('processStatus').classList.add('hidden');
473
  }
474
  });
475
+ // رفع ملف المصادر الإضافية
476
  document.getElementById('sourceExtraFile')?.addEventListener('change', async (event) => {
477
  const file = event.target.files[0];
478
  if (!file) return;
 
488
  }
489
  });
490
 
491
+ /* =====================================
492
+ دالة عرض الأخطاء والرسائل
493
+ ===================================== */
494
  function addError(message, type = 'error') {
495
  const errorsList = document.getElementById('errorsList');
496
  if (!errorsList) return;
 
503
  errorsList.appendChild(errorDiv);
504
  }
505
 
506
+ /* =====================================
507
+ معالجة عملية التحليل عند الضغط على الزر
508
+ ===================================== */
509
  document.getElementById('submitBtn').addEventListener('click', async () => {
510
  const sourceText = document.getElementById('sourceText').value;
511
  const targetText = document.getElementById('targetText').value;
512
+ // مسح الرسائل السابقة وإظهار النتائج
513
  document.getElementById('errorsList').innerHTML = '';
514
  document.getElementById('resultSection').classList.remove('hidden');
515
  document.getElementById('explanationBox').classList.remove('hidden');
 
519
  return;
520
  }
521
 
522
+ // مقارنة عدد الكلمات وتنبيه إن وجد اختلاف
523
  const sourceWordCount = countWords(sourceText);
524
  const targetWordCount = countWords(targetText);
525
  if (sourceWordCount !== targetWordCount) {
 
527
  }
528
 
529
  try {
530
+ // عرض مؤشر التقدم
531
  const progressDiv = document.createElement('div');
532
  progressDiv.className = "bg-indigo-100 p-4 rounded-xl mb-4";
533
  progressDiv.innerHTML = `<div class="flex items-center">
 
536
  </div>`;
537
  document.getElementById('errorsList').appendChild(progressDiv);
538
 
539
+ // بناء الـ prompt باستخدام النصين المُدخلين
540
  const prompt = ANALYSIS_PROMPT
541
  .replace("{source}", sourceText)
542
  .replace("{target}", targetText);
543
 
544
+ // استدعاء DeepSeek API
545
  const payload = {
546
  model: "deepseek-chat",
547
  messages: [
 
567
  const analysisOutput = data.choices[0].message.content.trim();
568
  progressDiv.remove();
569
 
570
+ // إذا كانت النصوص متطابقة تمامًا
 
 
 
 
571
  if (analysisOutput.includes('[MATCH]')) {
572
  const checkDiv = document.createElement('div');
573
  checkDiv.className = "p-4 rounded-xl bg-green-50 text-green-700 flex items-center";
 
577
  document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText);
578
  document.getElementById('explanationText').innerHTML = `<p>النصوص متطابقة ولا يوجد اختلاف يجب الإشارة إليه.</p>`;
579
  } else {
580
+ // تطبيق التحديد على النصوص
581
  const sourceHighlighted = applyHighlights(sourceText, analysisOutput);
582
  const targetHighlighted = applyHighlights(targetText, analysisOutput);
583
  document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
584
  document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetHighlighted);
585
+ // توليد شرح منظم على شكل خطوات
586
  const explanationHTML = generateExplanation(sourceText, analysisOutput);
587
  document.getElementById('explanationText').innerHTML = explanationHTML;
588
 
589
+ // عرض ملخص الاختلافات في الأسفل (اختياري)
590
  const numDiffCount = (analysisOutput.match(/<([^<>]+)>/g) || []).length;
591
  const missingDiffCount = (analysisOutput.match(/__(.*?)__/g) || []).length;
592
+ const extraDiffCount = (analysisOutput.match(/<<EXTRA>>(.*?)<<\/EXTRA>>/g) || []).length;
593
  const meaningDiffCount = (analysisOutput.match(/\[MEANING\](.*?)\[\/MEANING\]/g) || []).length;
594
+ const doubtDiffCount = (analysisOutput.match(/\[DOUBT\](.*?)\[\/DOUBT\]/g) || []).length;
595
+ if (numDiffCount > 0 || missingDiffCount > 0 || extraDiffCount > 0 || meaningDiffCount > 0 || doubtDiffCount > 0) {
596
  const summaryDiv = document.createElement('div');
597
  summaryDiv.className = "p-4 rounded-xl bg-yellow-50 text-gray-800";
598
  let summaryText = '<div class="font-bold mb-2">ملخص الاختلافات:</div><ul class="list-disc mr-6 space-y-1">';
599
+ if (numDiffCount > 0) summaryText += `<li>اختلاف في الأرقام/التواريخ: ${numDiffCount}</li>`;
600
  if (missingDiffCount > 0) summaryText += `<li>النصوص المفقودة: ${missingDiffCount}</li>`;
601
+ if (extraDiffCount > 0) summaryText += `<li>النصوص الزائدة: ${extraDiffCount}</li>`;
602
  if (meaningDiffCount > 0) summaryText += `<li>اختلاف في المعنى: ${meaningDiffCount}</li>`;
603
+ if (doubtDiffCount > 0) summaryText += `<li>علامات الشك أو الأخطاء البسيطة: ${doubtDiffCount}</li>`;
604
  summaryText += '</ul>';
605
  summaryDiv.innerHTML = summaryText;
606
  document.getElementById('errorsList').appendChild(summaryDiv);
 
611
  addError(`خطأ في التحليل: ${error.message}`);
612
  }
613
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
  </script>
615
  </body>
616
+ </html>