joermd commited on
Commit
368929a
·
verified ·
1 Parent(s): 48b50f8

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +344 -141
index.html CHANGED
@@ -4,8 +4,9 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>نظام المقارنة والتحليل المتقدم - شركة موندو لينجوا</title>
7
- <!-- استيراد مكتبات Tailwind وFont Awesome -->
8
  <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
 
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
10
  <style>
11
  /* ================================
@@ -34,14 +35,14 @@
34
  تنسيقات النص والتظليل
35
  ================================= */
36
  .text-comparison { line-height: 1.8; white-space: pre-wrap; }
37
- /* اختلاف بسيط */
38
  .highlight-minor {
39
  background-color: #FFF9C4; /* أصفر فاتح */
40
  padding: 0 4px;
41
  border-radius: 3px;
42
  font-weight: bold;
43
  }
44
- /* جملة مفقودة بالكامل */
45
  .highlight-missing {
46
  background-color: #FECACA; /* أحمر فاتح */
47
  color: #B91C1C;
@@ -49,7 +50,7 @@
49
  border-radius: 3px;
50
  font-style: italic;
51
  }
52
- /* جملة ناقصة */
53
  .highlight-partial {
54
  background-color: #FFCDD2; /* أحمر باهت */
55
  color: #B91C1C;
@@ -57,9 +58,9 @@
57
  border-radius: 3px;
58
  font-weight: bold;
59
  }
60
- /* تظليل إضافي للأرقام والتواريخ واختلاف المعنى وعلامات الشك */
61
  .highlight-number {
62
- background-color: #FDE68A;
63
  padding: 0 4px;
64
  border-radius: 3px;
65
  font-weight: bold;
@@ -71,12 +72,19 @@
71
  font-weight: bold;
72
  }
73
  .highlight-doubt {
74
- background-color: #DBEAFE;
75
  color: #1E3A8A;
76
  padding: 0 4px;
77
  border-radius: 3px;
78
  font-weight: bold;
79
  }
 
 
 
 
 
 
 
80
 
81
  /* ================================
82
  تنسيق عرض الفقرات وترقيمها
@@ -112,7 +120,9 @@
112
  /* ================================
113
  شاشة الأخطاء والنتائج
114
  ================================= */
115
- #errorsList div { margin-bottom: 0.5rem; }
 
 
116
  </style>
117
  </head>
118
  <body class="bg-gradient-to-br from-gray-100 via-blue-100 to-indigo-100 min-h-screen">
@@ -121,10 +131,10 @@
121
  <header class="bg-gradient-to-r from-indigo-700 via-purple-700 to-pink-700 animate-gradient text-white py-10 mb-10 shadow-xl">
122
  <div class="max-w-6xl mx-auto px-4 text-center">
123
  <h1 class="text-5xl font-bold mb-4 animate-scale">
124
- <i class="fas fa-chart-line icon"></i> نظام المقارنة والتحليل المتقدم
125
  </h1>
126
  <p class="text-xl opacity-90">
127
- <i class="fas fa-info-circle icon"></i> استخراج كافة الاختلافات مع التركيز على النصوص المفقودة في المصدر
128
  </p>
129
  </div>
130
  </header>
@@ -134,7 +144,7 @@
134
  <!-- قسم رفع الملفات -->
135
  <div class="card card-hover">
136
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
137
- <i class="fas fa-file-upload icon text-indigo-600"></i> تحميل الملفات
138
  </h2>
139
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
140
  <!-- ملف المصدر -->
@@ -142,7 +152,7 @@
142
  <label class="cursor-pointer block">
143
  <input type="file" id="sourceFile" accept=".docx,.pdf" class="hidden">
144
  <i class="fas fa-upload text-5xl text-indigo-500 mb-4"></i>
145
- <span class="text-lg text-indigo-600 group-hover:text-indigo-700">ملف المصدر</span>
146
  </label>
147
  </div>
148
  <!-- ملف الهدف -->
@@ -150,7 +160,7 @@
150
  <label class="cursor-pointer block">
151
  <input type="file" id="targetFile" accept=".docx,.pdf" class="hidden">
152
  <i class="fas fa-download text-5xl text-pink-500 mb-4"></i>
153
- <span class="text-lg text-pink-600 group-hover:text-pink-700">ملف الهدف</span>
154
  </label>
155
  </div>
156
  </div>
@@ -168,14 +178,14 @@
168
  <!-- النص المصدر -->
169
  <div class="group">
170
  <label class="block text-lg font-bold text-gray-700 mb-3">
171
- <i class="fas fa-language icon text-indigo-600"></i> النص المصدر
172
  </label>
173
  <textarea id="sourceText" dir="rtl" class="w-full px-6 py-4 border-2 border-gray-200 rounded-xl focus:ring-indigo-200 focus:border-indigo-400 transition-all resize-none text-lg" rows="8" placeholder="اكتب النص المصدر هنا..."></textarea>
174
  </div>
175
  <!-- النص الهدف -->
176
  <div class="group">
177
  <label class="block text-lg font-bold text-gray-700 mb-3">
178
- <i class="fas fa-language icon text-pink-600"></i> النص الهدف
179
  </label>
180
  <textarea id="targetText" dir="ltr" class="w-full px-6 py-4 border-2 border-gray-200 rounded-xl focus:ring-pink-200 focus:border-pink-400 transition-all resize-none text-lg" rows="8" placeholder="اكتب النص الهدف هنا..."></textarea>
181
  </div>
@@ -185,7 +195,7 @@
185
  <!-- قسم المصادر الإضافية -->
186
  <div class="card card-hover">
187
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
188
- <i class="fas fa-book-open icon text-green-600"></i> مصادر إضافية
189
  </h2>
190
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
191
  <!-- رفع ملف المصادر الإضافية -->
@@ -193,13 +203,13 @@
193
  <label class="cursor-pointer block">
194
  <input type="file" id="sourceExtraFile" accept=".docx,.pdf" class="hidden">
195
  <i class="fas fa-upload text-5xl text-green-500 mb-4"></i>
196
- <span class="text-lg text-green-600 group-hover:text-green-700">تحميل ملف المصدر</span>
197
  </label>
198
  </div>
199
  <!-- إدخال المصادر يدويًا -->
200
  <div class="group">
201
  <label class="block text-lg font-bold text-gray-700 mb-3">
202
- <i class="fas fa-edit icon text-green-600"></i> إدخال المصادر يدويًا
203
  </label>
204
  <textarea id="sourceExtraText" dir="rtl" class="w-full px-6 py-4 border-2 border-green-200 rounded-xl focus:ring-green-200 focus:border-green-400 transition-all resize-none text-lg" rows="8" placeholder="اكتب المصادر هنا..."></textarea>
205
  </div>
@@ -209,26 +219,26 @@
209
  <!-- زر التحليل والمراجعة -->
210
  <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 pulse-animation">
211
  <div class="flex items-center justify-center">
212
- <i class="fas fa-sync-alt icon ml-2"></i> تحليل ومراجعة النصوص
213
  </div>
214
  </button>
215
 
216
  <!-- قسم عرض نتائج التحليل -->
217
  <div id="resultSection" class="card hidden">
218
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
219
- <i class="fas fa-search icon text-green-600"></i> نتائج التحليل والمقارنة
220
  </h2>
221
  <div id="errorsList" class="space-y-3 mb-6"></div>
222
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
223
  <div>
224
  <h4 class="text-lg font-bold text-gray-700 mb-3">
225
- <i class="fas fa-file-alt icon text-indigo-600"></i> النص المصدر (مع التظليل)
226
  </h4>
227
  <div id="sourceTextReview" class="bg-indigo-50 rounded-xl p-6 min-h-[200px] border-2 border-indigo-100 text-comparison"></div>
228
  </div>
229
  <div>
230
  <h4 class="text-lg font-bold text-gray-700 mb-3">
231
- <i class="fas fa-file-alt icon text-pink-600"></i> النص الهدف (بدون تظليل)
232
  </h4>
233
  <div id="targetTextReview" class="bg-gray-50 rounded-xl p-6 min-h-[200px] border-2 border-gray-200 text-comparison"></div>
234
  </div>
@@ -238,7 +248,7 @@
238
  <!-- قسم الشرح التفصيلي للاختلافات -->
239
  <div id="explanationBox" class="card hidden">
240
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
241
- <i class="fas fa-info-circle icon text-green-600"></i> شرح الاختلافات المفصلة
242
  </h2>
243
  <div id="explanationText" class="text-lg text-gray-700"></div>
244
  </div>
@@ -246,100 +256,251 @@
246
  </div>
247
 
248
  <!-- ================================
249
- جافا سكريبت: التحليل المحلي للمقارنة
250
  ================================= -->
251
  <script>
252
- // دالة تقسيم النص إلى جمل (مع التأكد من انتهاء كل جملة بنقطة)
253
- function splitSentences(text) {
254
- // تقسيم النص بواسطة النقطة (يمكن تحسينها بتقسيم علامات الاستفهام والتعجب)
255
- return text.split('.').map(s => s.trim()).filter(s => s.length > 0).map(s => s + '.');
256
- }
 
 
 
 
 
 
 
 
 
 
257
 
258
- // دالة لحساب درجة التشابه بين جملتين باستخدام تشابه جاكارد (Jaccard similarity)
259
- function sentenceSimilarity(s1, s2) {
260
- const words1 = s1.toLowerCase().split(/\s+/);
261
- const words2 = s2.toLowerCase().split(/\s+/);
262
- const set1 = new Set(words1);
263
- const set2 = new Set(words2);
264
- const intersection = new Set([...set1].filter(x => set2.has(x)));
265
- const union = new Set([...set1, ...set2]);
266
- return intersection.size / union.size;
267
  }
268
-
269
- // دالة مقارنة الجمل بين المصدر والهدف
270
- function compareSentences(source, target) {
271
- const sourceSentences = splitSentences(source);
272
- const targetSentences = splitSentences(target);
273
- let results = [];
274
-
275
- sourceSentences.forEach(sentence => {
276
- let maxSim = 0;
277
- targetSentences.forEach(tSentence => {
278
- const sim = sentenceSimilarity(sentence, tSentence);
279
- if (sim > maxSim) maxSim = sim;
280
- });
281
- let type = 'match';
282
- // تحديد النوع بناءً على درجة التشابه
283
- if(maxSim < 0.4) {
284
- type = 'MISSING';
285
- } else if(maxSim < 0.8) {
286
- type = 'PARTIAL';
287
- } else if(maxSim < 0.95) {
288
- type = 'MINOR';
289
  }
290
- results.push({ sentence, similarity: maxSim, type });
291
- });
292
- return results;
 
 
 
 
293
  }
294
 
295
- // دالة بناء النص المصدر مع التظليل بناءً على النتائج
296
- function buildAnnotatedText(results) {
297
- return results.map((item, i) => {
298
- let annotated = item.sentence;
299
- if(item.type === 'MINOR') {
300
- annotated = `<span class="highlight-minor">${annotated} [MINOR]</span>`;
301
- } else if(item.type === 'PARTIAL') {
302
- annotated = `<span class="highlight-partial">${annotated} [PARTIAL]</span>`;
303
- } else if(item.type === 'MISSING') {
304
- annotated = `<span class="highlight-missing">${annotated} [MISSING]</span>`;
 
 
305
  }
306
- return `<div class="line-item"><span class="line-number">${i+1}:</span> <span class="line-text">${annotated}</span></div>`;
307
- }).join('');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  }
309
 
310
- // دالة توليد شرح تفصيلي لكل اختلاف
311
- function generateExplanation(results) {
 
 
312
  const iconMinor = `<i class="fas fa-edit text-orange-500 mr-1"></i>`;
313
  const iconMissing = `<i class="fas fa-exclamation-triangle text-red-500 mr-1"></i>`;
314
  const iconPartial = `<i class="fas fa-minus-circle text-red-500 mr-1"></i>`;
 
 
 
315
 
316
- let explanations = results.map((item, i) => {
317
- if(item.type === 'match') return "";
318
- let explanation = "";
319
- if(item.type === 'MINOR') {
320
- explanation = `${iconMinor} السطر ${i+1}: اختلاف بسيط (درجة التشابه: ${(item.similarity*100).toFixed(1)}%)`;
321
- } else if(item.type === 'PARTIAL') {
322
- explanation = `${iconPartial} السطر ${i+1}: الجملة ناقصة أو بها كلمات مفقودة (درجة التشابه: ${(item.similarity*100).toFixed(1)}%)`;
323
- } else if(item.type === 'MISSING') {
324
- explanation = `${iconMissing} السطر ${i+1}: الجملة مفقودة بالكامل في النص الهدف (درجة التشابه: ${(item.similarity*100).toFixed(1)}%)`;
325
  }
326
- return `<li>${explanation}</li>`;
327
- }).filter(exp => exp !== "");
328
-
329
- if(explanations.length === 0) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  return `<p class="text-green-700"><i class="fas fa-check-circle mr-2"></i> لا توجد اختلافات ملحوظة بين النصين.</p>`;
331
  }
332
- return `<ol class="list-decimal ml-6 space-y-2">${explanations.join('')}</ol>`;
333
  }
334
 
335
- // دالة عرض النص الهدف مع تقسيم الجمل (بدون تظليل)
336
- function buildTargetText(text) {
337
- return splitSentences(text).map((sentence, i) => {
338
- return `<div class="line-item"><span class="line-number">${i+1}:</span> <span class="line-text">${sentence}</span></div>`;
339
- }).join('');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  }
341
 
342
- // دالة عرض الأخطاء والرسائل (نفس الدالة السابقة)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
  function addError(message, type = 'error') {
344
  const errorsList = document.getElementById('errorsList');
345
  if (!errorsList) return;
@@ -352,8 +513,8 @@
352
  errorsList.appendChild(errorDiv);
353
  }
354
 
355
- // عند الضغط على زر التحليل يتم إجراء المقارنة وعرض النتائج
356
- document.getElementById('submitBtn').addEventListener('click', () => {
357
  const sourceText = document.getElementById('sourceText').value;
358
  const targetText = document.getElementById('targetText').value;
359
  document.getElementById('errorsList').innerHTML = '';
@@ -365,53 +526,95 @@
365
  return;
366
  }
367
 
368
- // تنبيه إذا اختلف عدد الكلمات (تحذير بسيط)
369
- const sourceWordCount = sourceText.trim().split(/\s+/).filter(w => w).length;
370
- const targetWordCount = targetText.trim().split(/\s+/).filter(w => w).length;
371
  if (sourceWordCount !== targetWordCount) {
372
  addError(`عدد كلمات النص المصدر (${sourceWordCount}) يختلف عن النص الهدف (${targetWordCount})`, 'warning');
373
  }
374
 
375
- // إظهار مؤشر التحليل
376
- const progressDiv = document.createElement('div');
377
- progressDiv.className = "bg-indigo-100 p-4 rounded-xl mb-4";
378
- progressDiv.innerHTML = `<div class="flex items-center">
379
- <div class="animate-spin h-6 w-6 border-4 border-indigo-600 rounded-full border-t-transparent mr-3"></div>
380
- <span>جارٍ التحليل...</span>
381
- </div>`;
382
- document.getElementById('errorsList').appendChild(progressDiv);
383
-
384
- // إجراء مقارنة الجمل
385
- const results = compareSentences(sourceText, targetText);
386
-
387
- // بناء النص المصدر المُعلّم
388
- const annotatedSource = buildAnnotatedText(results);
389
- document.getElementById('sourceTextReview').innerHTML = annotatedSource;
390
-
391
- // عرض النص الهدف كما هو مع تقسيم الجمل
392
- document.getElementById('targetTextReview').innerHTML = buildTargetText(targetText);
393
-
394
- // بناء الشرح التفصيلي للاختلافات
395
- const explanationHTML = generateExplanation(results);
396
- document.getElementById('explanationText').innerHTML = explanationHTML;
397
-
398
- // إزالة مؤشر التحليل
399
- progressDiv.remove();
400
-
401
- // عرض ملخص الاختلافات إن وُجدت
402
- const minorCount = results.filter(r => r.type === 'MINOR').length;
403
- const partialCount = results.filter(r => r.type === 'PARTIAL').length;
404
- const missingCount = results.filter(r => r.type === 'MISSING').length;
405
- if(minorCount || partialCount || missingCount) {
406
- const summaryDiv = document.createElement('div');
407
- summaryDiv.className = "p-4 rounded-xl bg-yellow-50 text-gray-800";
408
- let summaryText = '<div class="font-bold mb-2">ملخص الاختلافات:</div><ul class="list-disc ml-4 space-y-1">';
409
- if(minorCount) summaryText += `<li>اختلاف بسيط: ${minorCount}</li>`;
410
- if(partialCount) summaryText += `<li>جمل ناقصة: ${partialCount}</li>`;
411
- if(missingCount) summaryText += `<li>جمل مفقودة: ${missingCount}</li>`;
412
- summaryText += '</ul>';
413
- summaryDiv.innerHTML = summaryText;
414
- document.getElementById('errorsList').appendChild(summaryDiv);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
  }
416
  });
417
  </script>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>نظام المقارنة والتحليل المتقدم - شركة موندو لينجوا</title>
7
+ <!-- استيراد مكتبات Tailwind وFont Awesome وMammoth -->
8
  <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
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
  /* ================================
 
35
  تنسيقات النص والتظليل
36
  ================================= */
37
  .text-comparison { line-height: 1.8; white-space: pre-wrap; }
38
+ /* اختلافات بسيطة (نوع MINOR) */
39
  .highlight-minor {
40
  background-color: #FFF9C4; /* أصفر فاتح */
41
  padding: 0 4px;
42
  border-radius: 3px;
43
  font-weight: bold;
44
  }
45
+ /* جمل مفقودة بالكامل (نوع MISSING) */
46
  .highlight-missing {
47
  background-color: #FECACA; /* أحمر فاتح */
48
  color: #B91C1C;
 
50
  border-radius: 3px;
51
  font-style: italic;
52
  }
53
+ /* جمل ناقصة (نوع PARTIAL) */
54
  .highlight-partial {
55
  background-color: #FFCDD2; /* أحمر باهت */
56
  color: #B91C1C;
 
58
  border-radius: 3px;
59
  font-weight: bold;
60
  }
61
+ /* تظليل الاختلافات الأخرى (أرقام، تواريخ، اختلاف معاني، علامات شك) */
62
  .highlight-number {
63
+ background-color: #FDE68A; /* أصفر */
64
  padding: 0 4px;
65
  border-radius: 3px;
66
  font-weight: bold;
 
72
  font-weight: bold;
73
  }
74
  .highlight-doubt {
75
+ background-color: #DBEAFE; /* أزرق */
76
  color: #1E3A8A;
77
  padding: 0 4px;
78
  border-radius: 3px;
79
  font-weight: bold;
80
  }
81
+ /* يمكن إضافة تظليل خاص للنصوص الزائدة (أخضر) إذا لزم الأمر */
82
+ .highlight-extra {
83
+ background-color: #D1FAE5; /* أخضر فاتح */
84
+ padding: 0 4px;
85
+ border-radius: 3px;
86
+ font-weight: bold;
87
+ }
88
 
89
  /* ================================
90
  تنسيق عرض الفقرات وترقيمها
 
120
  /* ================================
121
  شاشة الأخطاء والنتائج
122
  ================================= */
123
+ #errorsList div {
124
+ margin-bottom: 0.5rem;
125
+ }
126
  </style>
127
  </head>
128
  <body class="bg-gradient-to-br from-gray-100 via-blue-100 to-indigo-100 min-h-screen">
 
131
  <header class="bg-gradient-to-r from-indigo-700 via-purple-700 to-pink-700 animate-gradient text-white py-10 mb-10 shadow-xl">
132
  <div class="max-w-6xl mx-auto px-4 text-center">
133
  <h1 class="text-5xl font-bold mb-4 animate-scale">
134
+ <i class="fas fa-chart-line icon"></i> نظام المقارنة والتحليل المتقدم.
135
  </h1>
136
  <p class="text-xl opacity-90">
137
+ <i class="fas fa-info-circle icon"></i> استخراج كافة المشاكل مع التركيز على النصوص المفقودة في المصدر.
138
  </p>
139
  </div>
140
  </header>
 
144
  <!-- قسم رفع الملفات -->
145
  <div class="card card-hover">
146
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
147
+ <i class="fas fa-file-upload icon text-indigo-600"></i> تحميل الملفات.
148
  </h2>
149
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
150
  <!-- ملف المصدر -->
 
152
  <label class="cursor-pointer block">
153
  <input type="file" id="sourceFile" accept=".docx,.pdf" class="hidden">
154
  <i class="fas fa-upload text-5xl text-indigo-500 mb-4"></i>
155
+ <span class="text-lg text-indigo-600 group-hover:text-indigo-700">ملف المصدر.</span>
156
  </label>
157
  </div>
158
  <!-- ملف الهدف -->
 
160
  <label class="cursor-pointer block">
161
  <input type="file" id="targetFile" accept=".docx,.pdf" class="hidden">
162
  <i class="fas fa-download text-5xl text-pink-500 mb-4"></i>
163
+ <span class="text-lg text-pink-600 group-hover:text-pink-700">ملف الهدف.</span>
164
  </label>
165
  </div>
166
  </div>
 
178
  <!-- النص المصدر -->
179
  <div class="group">
180
  <label class="block text-lg font-bold text-gray-700 mb-3">
181
+ <i class="fas fa-language icon text-indigo-600"></i> النص المصدر.
182
  </label>
183
  <textarea id="sourceText" dir="rtl" class="w-full px-6 py-4 border-2 border-gray-200 rounded-xl focus:ring-indigo-200 focus:border-indigo-400 transition-all resize-none text-lg" rows="8" placeholder="اكتب النص المصدر هنا..."></textarea>
184
  </div>
185
  <!-- النص الهدف -->
186
  <div class="group">
187
  <label class="block text-lg font-bold text-gray-700 mb-3">
188
+ <i class="fas fa-language icon text-pink-600"></i> النص الهدف.
189
  </label>
190
  <textarea id="targetText" dir="ltr" class="w-full px-6 py-4 border-2 border-gray-200 rounded-xl focus:ring-pink-200 focus:border-pink-400 transition-all resize-none text-lg" rows="8" placeholder="اكتب النص الهدف هنا..."></textarea>
191
  </div>
 
195
  <!-- قسم المصادر الإضافية -->
196
  <div class="card card-hover">
197
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
198
+ <i class="fas fa-book-open icon text-green-600"></i> مصادر إضافية.
199
  </h2>
200
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
201
  <!-- رفع ملف المصادر الإضافية -->
 
203
  <label class="cursor-pointer block">
204
  <input type="file" id="sourceExtraFile" accept=".docx,.pdf" class="hidden">
205
  <i class="fas fa-upload text-5xl text-green-500 mb-4"></i>
206
+ <span class="text-lg text-green-600 group-hover:text-green-700">تحميل ملف المصدر.</span>
207
  </label>
208
  </div>
209
  <!-- إدخال المصادر يدويًا -->
210
  <div class="group">
211
  <label class="block text-lg font-bold text-gray-700 mb-3">
212
+ <i class="fas fa-edit icon text-green-600"></i> إدخال المصادر يدويًا.
213
  </label>
214
  <textarea id="sourceExtraText" dir="rtl" class="w-full px-6 py-4 border-2 border-green-200 rounded-xl focus:ring-green-200 focus:border-green-400 transition-all resize-none text-lg" rows="8" placeholder="اكتب المصادر هنا..."></textarea>
215
  </div>
 
219
  <!-- زر التحليل والمراجعة -->
220
  <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 pulse-animation">
221
  <div class="flex items-center justify-center">
222
+ <i class="fas fa-sync-alt icon ml-2"></i> تحليل ومراجعة النصوص.
223
  </div>
224
  </button>
225
 
226
  <!-- قسم عرض نتائج التحليل -->
227
  <div id="resultSection" class="card hidden">
228
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
229
+ <i class="fas fa-search icon text-green-600"></i> نتائج التحليل والمقارنة.
230
  </h2>
231
  <div id="errorsList" class="space-y-3 mb-6"></div>
232
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
233
  <div>
234
  <h4 class="text-lg font-bold text-gray-700 mb-3">
235
+ <i class="fas fa-file-alt icon text-indigo-600"></i> النص المصدر (مع التظليل).
236
  </h4>
237
  <div id="sourceTextReview" class="bg-indigo-50 rounded-xl p-6 min-h-[200px] border-2 border-indigo-100 text-comparison"></div>
238
  </div>
239
  <div>
240
  <h4 class="text-lg font-bold text-gray-700 mb-3">
241
+ <i class="fas fa-file-alt icon text-pink-600"></i> النص الهدف (بدون تظليل).
242
  </h4>
243
  <div id="targetTextReview" class="bg-gray-50 rounded-xl p-6 min-h-[200px] border-2 border-gray-200 text-comparison"></div>
244
  </div>
 
248
  <!-- قسم الشرح التفصيلي للاختلافات -->
249
  <div id="explanationBox" class="card hidden">
250
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
251
+ <i class="fas fa-info-circle icon text-green-600"></i> شرح الاختلافات المفصلة.
252
  </h2>
253
  <div id="explanationText" class="text-lg text-gray-700"></div>
254
  </div>
 
256
  </div>
257
 
258
  <!-- ================================
259
+ جافا سكريبت: الوظائف والتحليل
260
  ================================= -->
261
  <script>
262
+ // إعدادات الـ API والبرومبت مع تعليمات استخدام العلامات الجديدة.
263
+ const API_URL = 'https://api.deepseek.com/chat/completions';
264
+ const API_KEY = 'sk-15606736ed9e4aea8b7cc11a195d2b01';
265
+ const ANALYSIS_PROMPT = أنت خبير لغوي وتقني متخصص في مراجعة الترجمة التقنية وتحليل النصوص بدقة عالية.
266
+ مهمتك مقارنة النص المصدر والنص الهدف واستخراج كافة المشاكل وفقاً للأنواع التالية:
267
+ 1. إذا كانت الجملة موجودة في النصين ولكن بها اختلاف بسيط (مثال: اختلاف كلمة أو تاريخ أو معنى بسيط) فاستخدم العلامة [MINOR] ... [/MINOR].
268
+ 2. إذا كانت الجملة مختلفة تماماً (أي بها 3 اختلافات أو أكثر) فتعتبر مفقودة في النص الهدف، فاستخدم العلامة [MISSING] ... [/MISSING].
269
+ 3. إذا كانت الجملة موجودة في النص المصدر ولكنها ناقصة أو بها كلمات مفقودة في النص الهدف، فاستخدم العلامة [PARTIAL] ... [/PARTIAL].
270
+ يُرجى إظهار العلامات في النص المصدر لتبيان مكان الخطأ.
271
+
272
+ النص المصدر:
273
+ {source}
274
+
275
+ النص الهدف:
276
+ {target};
277
 
278
+ // دوال مساعدة للتعامل مع النصوص.
279
+ function countWords(text) {
280
+ return text.trim().split(/\s+/).filter(word => word !== "").length;
 
 
 
 
 
 
281
  }
282
+ function escapeRegExp(string) {
283
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
284
+ }
285
+ function splitIntoLines(text) {
286
+ return text.split(/\n+/).map((line, i) => {
287
+ line = line.trim();
288
+ if(line && !line.endsWith('.')) {
289
+ line += '.';
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  }
291
+ return `<div class="line-item"><span class="line-number">${i+1}:</span> <span class="line-text">${line}</span></div>`;
292
+ }).join('');
293
+ }
294
+ function getLineNumber(text, substring) {
295
+ const index = text.indexOf(substring);
296
+ if (index === -1) return "غير محدد";
297
+ return text.substring(0, index).split("\n").length;
298
  }
299
 
300
+ // دالة تطبيق التظليل على النص المصدر باستخدام العلامات الجديدة.
301
+ function applyHighlights(originalText, analysisOutput) {
302
+ let highlightedText = originalText;
303
+ let match;
304
+ // اختلاف بسيط [MINOR].
305
+ const minorRegex = /\[MINOR\](.*?)\[\/MINOR\]/g;
306
+ while ((match = minorRegex.exec(analysisOutput)) !== null) {
307
+ const phrase = match[1].trim();
308
+ if (phrase) {
309
+ const replacement = `<span class="highlight-minor">${phrase}</span>`;
310
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
311
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
312
  }
313
+ }
314
+ // جملة مفقودة [MISSING].
315
+ const missingRegex = /\[MISSING\](.*?)\[\/MISSING\]/g;
316
+ while ((match = missingRegex.exec(analysisOutput)) !== null) {
317
+ const phrase = match[1].trim();
318
+ if (phrase) {
319
+ const replacement = `<span class="highlight-missing">${phrase}</span>`;
320
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
321
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
322
+ }
323
+ }
324
+ // جملة ناقصة [PARTIAL].
325
+ const partialRegex = /\[PARTIAL\](.*?)\[\/PARTIAL\]/g;
326
+ while ((match = partialRegex.exec(analysisOutput)) !== null) {
327
+ const phrase = match[1].trim();
328
+ if (phrase) {
329
+ const replacement = `<span class="highlight-partial">${phrase}</span>`;
330
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
331
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
332
+ }
333
+ }
334
+ // يمكن الإبقاء على التظليل السابق للأرقام والتواريخ واختلاف المعنى وعلامات الشك إن وجدت.
335
+ const numberRegex = /<([^<>]+)>/g;
336
+ while ((match = numberRegex.exec(analysisOutput)) !== null) {
337
+ const phrase = match[1].trim();
338
+ if (phrase) {
339
+ const replacement = `<span class="highlight-number">${phrase}</span>`;
340
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
341
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
342
+ }
343
+ }
344
+ const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
345
+ while ((match = meaningRegex.exec(analysisOutput)) !== null) {
346
+ const phrase = match[1].trim();
347
+ if (phrase) {
348
+ const replacement = `<span class="highlight-meaning">${phrase}</span>`;
349
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
350
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
351
+ }
352
+ }
353
+ const doubtRegex = /\[DOUBT\](.*?)\[\/DOUBT\]/g;
354
+ while ((match = doubtRegex.exec(analysisOutput)) !== null) {
355
+ const phrase = match[1].trim();
356
+ if (phrase) {
357
+ const replacement = `<span class="highlight-doubt">${phrase}</span>`;
358
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
359
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
360
+ }
361
+ }
362
+ return highlightedText;
363
  }
364
 
365
+ // دالة توليد شرح تفصيلي للاختلافات باستخدام العلامات الجديدة مع أيقونات توضيحية.
366
+ function generateExplanation(sourceText, analysisOutput) {
367
+ let steps = [];
368
+ let match;
369
  const iconMinor = `<i class="fas fa-edit text-orange-500 mr-1"></i>`;
370
  const iconMissing = `<i class="fas fa-exclamation-triangle text-red-500 mr-1"></i>`;
371
  const iconPartial = `<i class="fas fa-minus-circle text-red-500 mr-1"></i>`;
372
+ const iconNumber = `<i class="fas fa-hashtag text-yellow-600 mr-1"></i>`;
373
+ const iconMeaning = `<i class="fas fa-info-circle text-blue-600 mr-1"></i>`;
374
+ const iconDoubt = `<i class="fas fa-question-circle text-indigo-600 mr-1"></i>`;
375
 
376
+ // استخراج اختلاف بسيط [MINOR].
377
+ const minorRegex2 = /\[MINOR\](.*?)\[\/MINOR\]/g;
378
+ while ((match = minorRegex2.exec(analysisOutput)) !== null) {
379
+ const phrase = match[1].trim();
380
+ if(phrase) {
381
+ const lineNum = getLineNumber(sourceText, phrase);
382
+ steps.push(`<li>${iconMinor} في السطر ${lineNum}: اختلاف بسيط في الجملة <span class="highlight-minor">${phrase}</span>.</li>`);
 
 
383
  }
384
+ }
385
+ // استخراج جملة مفقودة [MISSING].
386
+ const missingRegex2 = /\[MISSING\](.*?)\[\/MISSING\]/g;
387
+ while ((match = missingRegex2.exec(analysisOutput)) !== null) {
388
+ const phrase = match[1].trim();
389
+ if(phrase) {
390
+ const lineNum = getLineNumber(sourceText, phrase);
391
+ steps.push(`<li>${iconMissing} في السطر ${lineNum}: جملة مفقودة <span class="highlight-missing">${phrase}</span> غير موجودة في النص الهدف.</li>`);
392
+ }
393
+ }
394
+ // استخراج جملة ناقصة [PARTIAL].
395
+ const partialRegex2 = /\[PARTIAL\](.*?)\[\/PARTIAL\]/g;
396
+ while ((match = partialRegex2.exec(analysisOutput)) !== null) {
397
+ const phrase = match[1].trim();
398
+ if(phrase) {
399
+ const lineNum = getLineNumber(sourceText, phrase);
400
+ steps.push(`<li>${iconPartial} في السطر ${lineNum}: جملة ناقصة أو بها كلمات مفقودة <span class="highlight-partial">${phrase}</span> في النص الهدف.</li>`);
401
+ }
402
+ }
403
+ // استخراج اختلافات الأرقام/التواريخ إن وجدت.
404
+ const numberRegex2 = /<([^<>]+)>/g;
405
+ while ((match = numberRegex2.exec(analysisOutput)) !== null) {
406
+ const phrase = match[1].trim();
407
+ if (phrase) {
408
+ const lineNum = getLineNumber(sourceText, phrase);
409
+ steps.push(`<li>${iconNumber} في السطر ${lineNum}: الرقم/التاريخ <span class="highlight-number">${phrase}</span> لا يتطابق بين المصدر والهدف.</li>`);
410
+ }
411
+ }
412
+ // استخراج اختلافات المعنى إن وجدت.
413
+ const meaningRegex2 = /\[MEANING\](.*?)\[\/MEANING\]/g;
414
+ while ((match = meaningRegex2.exec(analysisOutput)) !== null) {
415
+ const phrase = match[1].trim();
416
+ if (phrase) {
417
+ const lineNum = getLineNumber(sourceText, phrase);
418
+ steps.push(`<li>${iconMeaning} في السطر ${lineNum}: اختلاف في المعنى مع التعبير <span class="highlight-meaning">${phrase}</span>.</li>`);
419
+ }
420
+ }
421
+ // استخراج علامات الشك أو الأخطاء البسيطة إن وجدت.
422
+ const doubtRegex2 = /\[DOUBT\](.*?)\[\/DOUBT\]/g;
423
+ while ((match = doubtRegex2.exec(analysisOutput)) !== null) {
424
+ const phrase = match[1].trim();
425
+ if (phrase) {
426
+ const lineNum = getLineNumber(sourceText, phrase);
427
+ steps.push(`<li>${iconDoubt} في السطر ${lineNum}: علامة شك أو خطأ بسيط <span class="highlight-doubt">${phrase}</span> تحتاج مراجعة.</li>`);
428
+ }
429
+ }
430
+ if (steps.length === 0) {
431
  return `<p class="text-green-700"><i class="fas fa-check-circle mr-2"></i> لا توجد اختلافات ملحوظة بين النصين.</p>`;
432
  }
433
+ return `<ol class="list-decimal ml-6 space-y-2">${steps.join('')}</ol>`;
434
  }
435
 
436
+ // دالة معالجة الملفات (PDF و DOCX).
437
+ async function processFile(file) {
438
+ let text = "";
439
+ if (file.type === 'application/pdf') {
440
+ const form = new FormData();
441
+ form.append('image', file);
442
+ const response = await fetch('https://demo.api4ai.cloud/ocr/v1/results', {
443
+ method: 'POST',
444
+ body: form,
445
+ headers: { 'A4A-CLIENT-APP-ID': 'sample' }
446
+ });
447
+ const data = await response.json();
448
+ text = data.results[0].entities[0].objects[0].entities[0].text;
449
+ } else if (file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
450
+ const arrayBuffer = await file.arrayBuffer();
451
+ const result = await mammoth.extractRawText({ arrayBuffer });
452
+ text = result.value;
453
+ } else {
454
+ throw new Error('نوع الملف غير مدعوم');
455
+ }
456
+ return text;
457
  }
458
 
459
+ // رفع الملفات وإدخال النصوص.
460
+ document.getElementById('sourceFile')?.addEventListener('change', async (event) => {
461
+ const file = event.target.files[0];
462
+ if (!file) return;
463
+ document.getElementById('processStatus').classList.remove('hidden');
464
+ try {
465
+ const text = await processFile(file);
466
+ document.getElementById('sourceText').value = text;
467
+ } catch (error) {
468
+ console.error('Error processing source file:', error);
469
+ addError('خطأ في معالجة ملف المصدر');
470
+ } finally {
471
+ document.getElementById('processStatus').classList.add('hidden');
472
+ }
473
+ });
474
+ document.getElementById('targetFile')?.addEventListener('change', async (event) => {
475
+ const file = event.target.files[0];
476
+ if (!file) return;
477
+ document.getElementById('processStatus').classList.remove('hidden');
478
+ try {
479
+ const text = await processFile(file);
480
+ document.getElementById('targetText').value = text;
481
+ } catch (error) {
482
+ console.error('Error processing target file:', error);
483
+ addError('خطأ في معالجة ملف الهدف');
484
+ } finally {
485
+ document.getElementById('processStatus').classList.add('hidden');
486
+ }
487
+ });
488
+ document.getElementById('sourceExtraFile')?.addEventListener('change', async (event) => {
489
+ const file = event.target.files[0];
490
+ if (!file) return;
491
+ document.getElementById('processStatus').classList.remove('hidden');
492
+ try {
493
+ const text = await processFile(file);
494
+ document.getElementById('sourceExtraText').value = text;
495
+ } catch (error) {
496
+ console.error('Error processing extra source file:', error);
497
+ addError('خطأ في معالجة ملف المصدر الإضافي');
498
+ } finally {
499
+ document.getElementById('processStatus').classList.add('hidden');
500
+ }
501
+ });
502
+
503
+ // دالة عرض الأخطاء والرسائل.
504
  function addError(message, type = 'error') {
505
  const errorsList = document.getElementById('errorsList');
506
  if (!errorsList) return;
 
513
  errorsList.appendChild(errorDiv);
514
  }
515
 
516
+ // معالجة عملية التحليل عند الضغط على زر "تحليل ومراجعة النصوص".
517
+ document.getElementById('submitBtn').addEventListener('click', async () => {
518
  const sourceText = document.getElementById('sourceText').value;
519
  const targetText = document.getElementById('targetText').value;
520
  document.getElementById('errorsList').innerHTML = '';
 
526
  return;
527
  }
528
 
529
+ const sourceWordCount = countWords(sourceText);
530
+ const targetWordCount = countWords(targetText);
 
531
  if (sourceWordCount !== targetWordCount) {
532
  addError(`عدد كلمات النص المصدر (${sourceWordCount}) يختلف عن النص الهدف (${targetWordCount})`, 'warning');
533
  }
534
 
535
+ try {
536
+ const progressDiv = document.createElement('div');
537
+ progressDiv.className = "bg-indigo-100 p-4 rounded-xl mb-4";
538
+ progressDiv.innerHTML = `<div class="flex items-center">
539
+ <div class="animate-spin h-6 w-6 border-4 border-indigo-600 rounded-full border-t-transparent mr-3"></div>
540
+ <span>جارٍ التحليل...</span>
541
+ </div>`;
542
+ document.getElementById('errorsList').appendChild(progressDiv);
543
+
544
+ const prompt = ANALYSIS_PROMPT
545
+ .replace("{source}", sourceText)
546
+ .replace("{target}", targetText);
547
+
548
+ const payload = {
549
+ model: "deepseek-chat",
550
+ messages: [
551
+ { role: "system", content: "أنت خبير في تحليل ومراجعة النصوص بدقة عالية." },
552
+ { role: "user", content: prompt }
553
+ ],
554
+ temperature: 0.3,
555
+ max_tokens: 2048,
556
+ stream: false
557
+ };
558
+ const response = await fetch(API_URL, {
559
+ method: 'POST',
560
+ headers: {
561
+ 'Authorization': 'Bearer ' + API_KEY,
562
+ 'Content-Type': 'application/json'
563
+ },
564
+ body: JSON.stringify(payload)
565
+ });
566
+ if (!response.ok) {
567
+ throw new Error('حدث خطأ بالشبكة: ' + response.statusText);
568
+ }
569
+ const data = await response.json();
570
+ const analysisOutput = data.choices[0].message.content.trim();
571
+ progressDiv.remove();
572
+
573
+ // إذا كانت النصوص متطابقة تماماً.
574
+ if (analysisOutput.includes('[MATCH]')) {
575
+ const checkDiv = document.createElement('div');
576
+ checkDiv.className = "p-4 rounded-xl bg-green-50 text-green-700 flex items-center";
577
+ checkDiv.innerHTML = `<i class="fas fa-check-circle mr-2"></i> <span>النصوص متطابقة تماماً</span>`;
578
+ document.getElementById('errorsList').appendChild(checkDiv);
579
+ document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceText);
580
+ document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText);
581
+ document.getElementById('explanationText').innerHTML = `<p>النصوص متطابقة ولا توجد فروقات يجب الإشارة إليها.</p>`;
582
+ } else {
583
+ // تطبيق التظليل على النص المصدر فقط.
584
+ const sourceHighlighted = applyHighlights(sourceText, analysisOutput);
585
+ document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
586
+ // عرض النص الهدف بدون تظليل.
587
+ document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText);
588
+ // توليد شرح تفصيلي لكل مشكلة تم استخراجها باستخدام العلامات الجديدة.
589
+ const explanationHTML = generateExplanation(sourceText, analysisOutput);
590
+ document.getElementById('explanationText').innerHTML = explanationHTML;
591
+
592
+ // عرض ملخص للإختلافات بشكل عام إن وُجدت.
593
+ const numDiffCount = (analysisOutput.match(/<([^<>]+)>/g) || []).length;
594
+ const minorDiffCount = (analysisOutput.match(/\[MINOR\](.*?)\[\/MINOR\]/g) || []).length;
595
+ const missingDiffCount = (analysisOutput.match(/\[MISSING\](.*?)\[\/MISSING\]/g) || []).length;
596
+ const partialDiffCount = (analysisOutput.match(/\[PARTIAL\](.*?)\[\/PARTIAL\]/g) || []).length;
597
+ const meaningDiffCount = (analysisOutput.match(/\[MEANING\](.*?)\[\/MEANING\]/g) || []).length;
598
+ const doubtDiffCount = (analysisOutput.match(/\[DOUBT\](.*?)\[\/DOUBT\]/g) || []).length;
599
+
600
+ if (minorDiffCount || missingDiffCount || partialDiffCount || numDiffCount || meaningDiffCount || doubtDiffCount) {
601
+ const summaryDiv = document.createElement('div');
602
+ summaryDiv.className = "p-4 rounded-xl bg-yellow-50 text-gray-800";
603
+ let summaryText = '<div class="font-bold mb-2">ملخص الاختلافات:</div><ul class="list-disc ml-4 space-y-1">';
604
+ if (minorDiffCount) summaryText += `<li>اختلاف بسيط: ${minorDiffCount}</li>`;
605
+ if (missingDiffCount) summaryText += `<li>جمل مفقودة: ${missingDiffCount}</li>`;
606
+ if (partialDiffCount) summaryText += `<li>جمل ناقصة: ${partialDiffCount}</li>`;
607
+ if (numDiffCount) summaryText += `<li>اختلاف في الأرقام/التواريخ: ${numDiffCount}</li>`;
608
+ if (meaningDiffCount) summaryText += `<li>اختلاف في المعنى: ${meaningDiffCount}</li>`;
609
+ if (doubtDiffCount) summaryText += `<li>علامات الشك: ${doubtDiffCount}</li>`;
610
+ summaryText += '</ul>';
611
+ summaryDiv.innerHTML = summaryText;
612
+ document.getElementById('errorsList').appendChild(summaryDiv);
613
+ }
614
+ }
615
+ } catch (error) {
616
+ document.getElementById('errorsList').innerHTML = '';
617
+ addError(`خطأ في التحليل: ${error.message}`);
618
  }
619
  });
620
  </script>