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

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +364 -394
index.html CHANGED
@@ -10,7 +10,7 @@
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%; }
@@ -35,32 +35,22 @@
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;
49
  padding: 0 4px;
50
  border-radius: 3px;
51
  font-style: italic;
52
  }
53
- /* جمل ناقصة (نوع PARTIAL) */
54
- .highlight-partial {
55
- background-color: #FFCDD2; /* أحمر باهت */
56
- color: #B91C1C;
57
- padding: 0 4px;
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,26 +62,19 @@
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
- تنسيق عرض الفقرات وترقيمها
91
  ================================= */
92
  .line-item {
93
  margin-bottom: 1rem;
94
- padding: 0.75rem;
95
  border-bottom: 1px dashed #ccc;
96
  }
97
  .line-number {
@@ -102,7 +85,7 @@
102
  .line-text { display: inline-block; }
103
 
104
  /* ================================
105
- تصميم البطاقات والعناصر
106
  ================================= */
107
  .card {
108
  background-color: #fff;
@@ -116,13 +99,6 @@
116
  transform: translateY(-3px);
117
  }
118
  .icon { margin-right: 0.5rem; }
119
-
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,10 +107,10 @@
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,7 +120,7 @@
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,7 +128,7 @@
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,7 +136,7 @@
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,16 +154,16 @@
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>
192
  </div>
193
  </div>
@@ -195,7 +171,7 @@
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,15 +179,15 @@
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>
216
  </div>
217
  </div>
@@ -219,36 +195,36 @@
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>
245
  </div>
246
  </div>
247
 
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>
@@ -259,364 +235,358 @@
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;
507
- const errorDiv = document.createElement('div');
508
- errorDiv.className = `p-4 rounded-xl ${type === 'error' ? 'bg-red-50 text-red-700' : 'bg-yellow-50 text-yellow-700'}`;
509
- errorDiv.innerHTML = `<div class="flex items-center">
510
- <i class="fas fa-${type === 'error' ? 'exclamation-circle' : 'info-circle'} mr-2"></i>
511
- <span>${message}</span>
512
- </div>`;
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 = '';
521
- document.getElementById('resultSection').classList.remove('hidden');
522
- document.getElementById('explanationBox').classList.remove('hidden');
523
-
524
- if (!sourceText || !targetText) {
525
- addError('يرجى إدخال كلا النصين المصدر والهدف');
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>
621
  </body>
622
  </html>
 
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%; }
 
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: #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;
 
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;
70
  }
 
 
 
 
 
 
 
71
 
72
  /* ================================
73
+ تنسيق الفقرات وترقيمها
74
  ================================= */
75
  .line-item {
76
  margin-bottom: 1rem;
77
+ padding: 0.5rem;
78
  border-bottom: 1px dashed #ccc;
79
  }
80
  .line-number {
 
85
  .line-text { display: inline-block; }
86
 
87
  /* ================================
88
+ تحسين تصميم البطاقات والأيقونات
89
  ================================= */
90
  .card {
91
  background-color: #fff;
 
99
  transform: translateY(-3px);
100
  }
101
  .icon { margin-right: 0.5rem; }
 
 
 
 
 
 
 
102
  </style>
103
  </head>
104
  <body class="bg-gradient-to-br from-gray-100 via-blue-100 to-indigo-100 min-h-screen">
 
107
  <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">
108
  <div class="max-w-6xl mx-auto px-4 text-center">
109
  <h1 class="text-5xl font-bold mb-4 animate-scale">
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>
 
120
  <!-- قسم رفع الملفات -->
121
  <div class="card card-hover">
122
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
123
+ <i class="fas fa-file-upload icon text-indigo-600"></i> تحميل الملفات
124
  </h2>
125
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
126
  <!-- ملف المصدر -->
 
128
  <label class="cursor-pointer block">
129
  <input type="file" id="sourceFile" accept=".docx,.pdf" class="hidden">
130
  <i class="fas fa-upload text-5xl text-indigo-500 mb-4"></i>
131
+ <span class="text-lg text-indigo-600 group-hover:text-indigo-700">ملف المصدر</span>
132
  </label>
133
  </div>
134
  <!-- ملف الهدف -->
 
136
  <label class="cursor-pointer block">
137
  <input type="file" id="targetFile" accept=".docx,.pdf" class="hidden">
138
  <i class="fas fa-download text-5xl text-pink-500 mb-4"></i>
139
+ <span class="text-lg text-pink-600 group-hover:text-pink-700">ملف الهدف</span>
140
  </label>
141
  </div>
142
  </div>
 
154
  <!-- النص المصدر -->
155
  <div class="group">
156
  <label class="block text-lg font-bold text-gray-700 mb-3">
157
+ <i class="fas fa-language icon text-indigo-600"></i> النص المصدر
158
  </label>
159
+ <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="6" placeholder="اكتب النص المصدر هنا..."></textarea>
160
  </div>
161
  <!-- النص الهدف -->
162
  <div class="group">
163
  <label class="block text-lg font-bold text-gray-700 mb-3">
164
+ <i class="fas fa-language icon text-pink-600"></i> النص الهدف
165
  </label>
166
+ <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="6" placeholder="اكتب النص الهدف هنا..."></textarea>
167
  </div>
168
  </div>
169
  </div>
 
171
  <!-- قسم المصادر الإضافية -->
172
  <div class="card card-hover">
173
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
174
+ <i class="fas fa-book-open icon text-green-600"></i> مصادر إضافية
175
  </h2>
176
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
177
  <!-- رفع ملف المصادر الإضافية -->
 
179
  <label class="cursor-pointer block">
180
  <input type="file" id="sourceExtraFile" accept=".docx,.pdf" class="hidden">
181
  <i class="fas fa-upload text-5xl text-green-500 mb-4"></i>
182
+ <span class="text-lg text-green-600 group-hover:text-green-700">تحميل ملف المصدر</span>
183
  </label>
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-edit icon text-green-600"></i> إدخال المصادر يدويًا
189
  </label>
190
+ <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="6" placeholder="اكتب المصادر هنا..."></textarea>
191
  </div>
192
  </div>
193
  </div>
 
195
  <!-- زر التحليل والمراجعة -->
196
  <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">
197
  <div class="flex items-center justify-center">
198
+ <i class="fas fa-sync-alt icon ml-2"></i> تحليل ومراجعة النصوص
199
  </div>
200
  </button>
201
 
202
+ <!-- نتائج التحليل -->
203
  <div id="resultSection" class="card hidden">
204
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
205
+ <i class="fas fa-search icon text-green-600"></i> نتائج التحليل والمقارنة
206
  </h2>
207
  <div id="errorsList" class="space-y-3 mb-6"></div>
208
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
209
  <div>
210
  <h4 class="text-lg font-bold text-gray-700 mb-3">
211
+ <i class="fas fa-file-alt icon text-indigo-600"></i> النص المصدر (مع التعليم)
212
  </h4>
213
  <div id="sourceTextReview" class="bg-indigo-50 rounded-xl p-6 min-h-[200px] border-2 border-indigo-100 text-comparison"></div>
214
  </div>
215
  <div>
216
  <h4 class="text-lg font-bold text-gray-700 mb-3">
217
+ <i class="fas fa-file-alt icon text-pink-600"></i> النص الهدف (مع التعليم)
218
  </h4>
219
  <div id="targetTextReview" class="bg-gray-50 rounded-xl p-6 min-h-[200px] border-2 border-gray-200 text-comparison"></div>
220
  </div>
221
  </div>
222
  </div>
223
 
224
+ <!-- شرح تفصيلي للاختلافات -->
225
  <div id="explanationBox" class="card hidden">
226
  <h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6">
227
+ <i class="fas fa-info-circle icon text-green-600"></i> شرح الاختلافات
228
  </h2>
229
  <div id="explanationText" class="text-lg text-gray-700"></div>
230
  </div>
 
235
  جافا سكريبت: الوظائف والتحليل
236
  ================================= -->
237
  <script>
238
+ // تغليف الكود داخل دالة IIFE لمنع تسرب المتغيرات إلى النطاق العام
239
+ (function() {
240
+ "use strict";
241
+
242
+ // تنبيه: يجب عدم تضمين مفتاح API في الكود العام؛ يُفضل تخزينه على الخادم واستخدام واجهة وسيطة
243
+ const API_URL = 'https://api.deepseek.com/chat/completions';
244
+ const API_KEY = 'sk-15606736ed9e4aea8b7cc11a195d2b01';
245
+ const ANALYSIS_PROMPT = `أنت خبير لغوي وتقني متخصص في مراجعة الترجمة التقنية وتحليل النصوص بدقة. مهمتك مقارنة النص المصدر والنص الهدف واستخراج الاختلافات بدقة مع تحسين التظليل والتعليم.
246
+ تعليمات:
247
+ 1. قم بتحليل النص إلى فقرات منفصلة، مع التأكد من انتهاء كل فقرة بنقطة.
248
+ 2. لا تقم بتعديل العلامات التالية:
249
+ - الأرقام والتواريخ محاطة بـ < و >.
250
+ - النصوص المفقودة محاطة بـ __ و __.
251
+ - النصوص الزائدة محاطة بـ <<EXTRA>> و <<\/EXTRA>>.
252
+ - اختلافات المعنى محاطة بـ [MEANING] و [/MEANING].
253
+ - علامات الشك أو الأخطاء البسيطة محاطة بـ [DOUBT] و [/DOUBT].
254
+ 3. يجب أن يقدم التحليل تعليمات دقيقة مع أيقونات توضيحية لكل نوع من الاختلافات.
255
+
256
  النص المصدر:
257
  {source}
258
+
259
  النص الهدف:
260
+ {target}`;
261
+
262
+ /* ===============================
263
+ دوال مساعدة عامة
264
+ =============================== */
265
+ const countWords = text =>
266
+ text.trim().split(/\s+/).filter(word => word !== "").length;
267
+
268
+ const escapeRegExp = string =>
269
+ string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
270
+
271
+ // تقسيم النص إلى فقرات مرقمة
272
+ const splitIntoLines = text =>
273
+ text.split(/\n+/).map((line, i) => {
274
+ line = line.trim();
275
+ if(line && !line.endsWith('.')) {
276
+ line += '.';
277
+ }
278
+ return `<div class="line-item"><span class="line-number">${i+1}:</span><span class="line-text">${line}</span></div>`;
279
+ }).join('');
280
+
281
+ // الحصول على رقم السطر الذي يحتوي على عبارة معينة
282
+ const getLineNumber = (text, substring) => {
283
+ const index = text.indexOf(substring);
284
+ if (index === -1) return "غير محدد";
285
+ return text.substring(0, index).split("\n").length;
286
+ };
287
+
288
+ /* ===============================
289
+ دوال تظليل الاختلافات
290
+ =============================== */
291
+ const applyHighlights = (originalText, analysisOutput) => {
292
+ let highlightedText = originalText;
293
+ let match;
294
+ // اختلافات الأرقام/التواريخ
295
+ const numberRegex = /<([^<>]+)>/g;
296
+ while ((match = numberRegex.exec(analysisOutput)) !== null) {
297
+ const phrase = match[1].trim();
298
+ if (phrase) {
299
+ const replacement = `<span class="highlight-number">${phrase}</span>`;
300
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
301
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
302
+ }
303
  }
304
+ // النصوص المفقودة
305
+ const missingRegex = /__(.*?)__/g;
306
+ while ((match = missingRegex.exec(analysisOutput)) !== null) {
307
+ const phrase = match[1].trim();
308
+ if (phrase) {
309
+ const replacement = `<span class="highlight-missing">__${phrase}__</span>`;
310
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
311
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
312
+ }
313
  }
314
+ // النصوص الزائدة
315
+ const extraRegex = /<<EXTRA>>(.*?)<<\/EXTRA>>/g;
316
+ while ((match = extraRegex.exec(analysisOutput)) !== null) {
317
+ const phrase = match[1].trim();
318
+ if (phrase) {
319
+ const replacement = `<span class="highlight-extra">${phrase}</span>`;
320
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
321
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
322
+ }
323
  }
324
+ // اختلافات المعنى
325
+ const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
326
+ while ((match = meaningRegex.exec(analysisOutput)) !== null) {
327
+ const phrase = match[1].trim();
328
+ if (phrase) {
329
+ const replacement = `<span class="highlight-meaning">${phrase}</span>`;
330
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
331
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
332
+ }
333
  }
334
+ // علامات الشك أو الأخطاء البسيطة
335
+ const doubtRegex = /\[DOUBT\](.*?)\[\/DOUBT\]/g;
336
+ while ((match = doubtRegex.exec(analysisOutput)) !== null) {
337
+ const phrase = match[1].trim();
338
+ if (phrase) {
339
+ const replacement = `<span class="highlight-doubt">${phrase}</span>`;
340
+ const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
341
+ highlightedText = highlightedText.replace(phraseRegex, replacement);
342
+ }
343
  }
344
+ return highlightedText;
345
+ };
346
+
347
+ // توليد شرح تفصيلي للاختلافات مع أيقونات توضيحية
348
+ const generateExplanation = (sourceText, analysisOutput) => {
349
+ let steps = [];
350
+ let match;
351
+ const iconMissing = `<i class="fas fa-exclamation-triangle text-red-500 mr-1"></i>`;
352
+ const iconNumber = `<i class="fas fa-hashtag text-yellow-600 mr-1"></i>`;
353
+ const iconExtra = `<i class="fas fa-plus-circle text-green-600 mr-1"></i>`;
354
+ const iconMeaning = `<i class="fas fa-info-circle text-blue-600 mr-1"></i>`;
355
+ const iconDoubt = `<i class="fas fa-question-circle text-indigo-600 mr-1"></i>`;
356
+
357
+ // النصوص المفقودة
358
+ const missingRegex = /__(.*?)__/g;
359
+ while ((match = missingRegex.exec(analysisOutput)) !== null) {
360
+ const phrase = match[1].trim();
361
+ if (phrase) {
362
+ const lineNum = getLineNumber(sourceText, phrase);
363
+ steps.push(`<li>${iconMissing} في السطر ${lineNum}، النص المفقود: <span class="highlight-missing">__${phrase}__</span> غير موجود في النص الهدف.</li>`);
364
+ }
365
  }
366
+ // اختلافات الأرقام/التواريخ
367
+ const numberRegex = /<([^<>]+)>/g;
368
+ while ((match = numberRegex.exec(analysisOutput)) !== null) {
369
+ const phrase = match[1].trim();
370
+ if (phrase) {
371
+ const lineNum = getLineNumber(sourceText, phrase);
372
+ steps.push(`<li>${iconNumber} في السطر ${lineNum}، الرقم/التاريخ: <span class="highlight-number">${phrase}</span> لا يتطابق بين المصدر والهدف.</li>`);
373
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  }
375
+ // النصوص الزائدة
376
+ const extraRegex = /<<EXTRA>>(.*?)<<\/EXTRA>>/g;
377
+ while ((match = extraRegex.exec(analysisOutput)) !== null) {
378
+ const phrase = match[1].trim();
379
+ if (phrase) {
380
+ const lineNum = getLineNumber(sourceText, phrase);
381
+ steps.push(`<li>${iconExtra} في السطر ${lineNum النص الزائد: <span class="highlight-extra">${phrase}</span> موجود بشكل غير متوقع في النص الهدف.</li>`);
382
+ }
383
  }
384
+ // اختلافات المعنى
385
+ const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
386
+ while ((match = meaningRegex.exec(analysisOutput)) !== null) {
387
+ const phrase = match[1].trim();
388
+ if (phrase) {
389
+ const lineNum = getLineNumber(sourceText, phrase);
390
+ steps.push(`<li>${iconMeaning} في السطر ${lineNum اختلاف في المعنى: <span class="highlight-meaning">${phrase}</span> بين المصدر والهدف.</li>`);
391
+ }
392
  }
393
+ // علامات الشك أو الأخطاء البسيطة
394
+ const doubtRegex = /\[DOUBT\](.*?)\[\/DOUBT\]/g;
395
+ while ((match = doubtRegex.exec(analysisOutput)) !== null) {
396
+ const phrase = match[1].trim();
397
+ if (phrase) {
398
+ const lineNum = getLineNumber(sourceText, phrase);
399
+ steps.push(`<li>${iconDoubt} في السطر ${lineNum علامة شك: <span class="highlight-doubt">${phrase}</span> قد تشير إلى غموض أو خطأ بسيط يحتاج مراجعة.</li>`);
400
+ }
401
  }
402
+ if (steps.length === 0) {
403
+ return `<p class="text-green-700"><i class="fas fa-check-circle mr-2"></i> لا توجد اختلافات ملحوظة بين النصين.</p>`;
 
 
 
 
 
 
404
  }
405
+ return `<ol class="list-decimal ml-6 space-y-2">${steps.join('')}</ol>`;
406
+ };
407
+
408
+ /* ===============================
409
+ دالة معالجة الملفات (PDF و DOCX)
410
+ =============================== */
411
+ const processFile = async file => {
412
+ let text = "";
413
+ try {
414
+ if (file.type === 'application/pdf') {
415
+ // يُنصح بمعالجة ملفات PDF على خادم مخصص بدلاً من العميل لتحسين الأداء والأمان
416
+ const form = new FormData();
417
+ form.append('image', file);
418
+ const response = await fetch('https://demo.api4ai.cloud/ocr/v1/results', {
419
+ method: 'POST',
420
+ body: form,
421
+ headers: { 'A4A-CLIENT-APP-ID': 'sample' }
422
+ });
423
+ const data = await response.json();
424
+ // تأكد من التحقق من بنية البيانات قبل الوصول إليها
425
+ text = data.results?.[0]?.entities?.[0]?.objects?.[0]?.entities?.[0]?.text || "";
426
+ } else if (file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
427
+ const arrayBuffer = await file.arrayBuffer();
428
+ const result = await mammoth.extractRawText({ arrayBuffer });
429
+ text = result.value;
430
+ } else {
431
+ throw new Error('نوع الملف غير مدعوم');
432
+ }
433
+ } catch (error) {
434
+ console.error('Error processing file:', error);
435
+ throw error;
436
  }
437
+ return text;
438
+ };
439
+
440
+ /* ===============================
441
+ التعامل مع رفع الملفات وإدخال النصوص
442
+ =============================== */
443
+ const processFileInput = (inputId, targetTextAreaId, errorMsg) => {
444
+ document.getElementById(inputId)?.addEventListener('change', async (event) => {
445
+ const file = event.target.files[0];
446
+ if (!file) return;
447
+ document.getElementById('processStatus').classList.remove('hidden');
448
+ try {
449
+ const text = await processFile(file);
450
+ document.getElementById(targetTextAreaId).value = text;
451
+ } catch (error) {
452
+ addError(errorMsg);
453
+ } finally {
454
+ document.getElementById('processStatus').classList.add('hidden');
455
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  });
457
+ };
458
+
459
+ // التعامل مع رفع ملفات المصدر، الهدف والمصادر الإضافية
460
+ processFileInput('sourceFile', 'sourceText', 'خطأ في معالجة ملف المصدر');
461
+ processFileInput('targetFile', 'targetText', 'خطأ في معالجة ملف الهدف');
462
+ processFileInput('sourceExtraFile', 'sourceExtraText', 'خطأ في معالجة ملف المصدر الإضافي');
463
+
464
+ /* ===============================
465
+ دالة عرض الأخطاء
466
+ =============================== */
467
+ const addError = (message, type = 'error') => {
468
+ const errorsList = document.getElementById('errorsList');
469
+ if (!errorsList) return;
470
+ const errorDiv = document.createElement('div');
471
+ errorDiv.className = `p-4 rounded-xl ${type === 'error' ? 'bg-red-50 text-red-700' : 'bg-yellow-50 text-yellow-700'}`;
472
+ errorDiv.innerHTML = `<div class="flex items-center">
473
+ <i class="fas fa-${type === 'error' ? 'exclamation-circle' : 'info-circle'} mr-2"></i>
474
+ <span>${message}</span>
475
+ </div>`;
476
+ errorsList.appendChild(errorDiv);
477
+ };
478
+
479
+ /* ===============================
480
+ التعامل مع عملية التحليل والمقارنة
481
+ =============================== */
482
+ document.getElementById('submitBtn').addEventListener('click', async () => {
483
+ const sourceText = document.getElementById('sourceText').value;
484
+ const targetText = document.getElementById('targetText').value;
485
+ // إعادة تعيين قائمة الأخطاء
486
+ document.getElementById('errorsList').innerHTML = '';
487
+ document.getElementById('resultSection').classList.remove('hidden');
488
+ document.getElementById('explanationBox').classList.remove('hidden');
489
+
490
+ if (!sourceText || !targetText) {
491
+ addError('يرجى إدخال كلا النصين المصدر والهدف');
492
+ return;
493
  }
494
+
495
+ // مقارنة عدد الكلمات وتحذير المستخدم إن كان هناك اختلاف
496
+ const sourceWordCount = countWords(sourceText);
497
+ const targetWordCount = countWords(targetText);
498
+ if (sourceWordCount !== targetWordCount) {
499
+ addError(`عدد كلمات النص المصدر (${sourceWordCount}) يختلف عن النص الهدف (${targetWordCount})`, 'warning');
500
+ }
501
+
502
+ try {
503
+ // عرض مؤشر التحميل أثناء عملية التحليل
504
+ const progressDiv = document.createElement('div');
505
+ progressDiv.className = "bg-indigo-100 p-4 rounded-xl mb-4";
506
+ progressDiv.innerHTML = `<div class="flex items-center">
507
+ <div class="animate-spin h-6 w-6 border-4 border-indigo-600 rounded-full border-t-transparent mr-3"></div>
508
+ <span>جارٍ التحليل...</span>
509
+ </div>`;
510
+ document.getElementById('errorsList').appendChild(progressDiv);
511
+
512
+ // تجهيز البرومبت مع استبدال النصوص المصدر والهدف
513
+ const prompt = ANALYSIS_PROMPT
514
+ .replace("{source}", sourceText)
515
+ .replace("{target}", targetText);
516
+
517
+ const payload = {
518
+ model: "deepseek-chat",
519
+ messages: [
520
+ { role: "system", content: "أنت خبير في تحليل ومراجعة النصوص بدقة عالية." },
521
+ { role: "user", content: prompt }
522
+ ],
523
+ temperature: 0.3,
524
+ max_tokens: 2048,
525
+ stream: false
526
+ };
527
+
528
+ const response = await fetch(API_URL, {
529
+ method: 'POST',
530
+ headers: {
531
+ 'Authorization': 'Bearer ' + API_KEY,
532
+ 'Content-Type': 'application/json'
533
+ },
534
+ body: JSON.stringify(payload)
535
+ });
536
+
537
+ if (!response.ok) {
538
+ throw new Error('حدث خطأ بالشبكة: ' + response.statusText);
539
  }
540
+ const data = await response.json();
541
+ const analysisOutput = data.choices[0].message.content.trim();
542
+ progressDiv.remove();
543
+
544
+ // في حالة التطابق التام
545
+ if (analysisOutput.includes('[MATCH]')) {
546
+ const checkDiv = document.createElement('div');
547
+ checkDiv.className = "p-4 rounded-xl bg-green-50 text-green-700 flex items-center";
548
+ checkDiv.innerHTML = `<i class="fas fa-check-circle mr-2"></i> <span>النصوص متطابقة تماماً</span>`;
549
+ document.getElementById('errorsList').appendChild(checkDiv);
550
+ document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceText);
551
+ document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText);
552
+ document.getElementById('explanationText').innerHTML = `<p>النصوص متطابقة ولا يوجد اختلاف يجب الإشارة إليه.</p>`;
553
+ } else {
554
+ // تطبيق التظليل على النصوص المصدر والهدف
555
+ const sourceHighlighted = applyHighlights(sourceText, analysisOutput);
556
+ const targetHighlighted = applyHighlights(targetText, analysisOutput);
557
+ document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
558
+ document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetHighlighted);
559
+ // توليد شرح تفصيلي للاختلافات
560
+ const explanationHTML = generateExplanation(sourceText, analysisOutput);
561
+ document.getElementById('explanationText').innerHTML = explanationHTML;
562
+
563
+ // عرض ملخص للاختلافات إن وُجدت
564
+ const numDiffCount = (analysisOutput.match(/<([^<>]+)>/g) || []).length;
565
+ const missingDiffCount = (analysisOutput.match(/__(.*?)__/g) || []).length;
566
+ const extraDiffCount = (analysisOutput.match(/<<EXTRA>>(.*?)<<\/EXTRA>>/g) || []).length;
567
+ const meaningDiffCount = (analysisOutput.match(/\[MEANING\](.*?)\[\/MEANING\]/g) || []).length;
568
+ const doubtDiffCount = (analysisOutput.match(/\[DOUBT\](.*?)\[\/DOUBT\]/g) || []).length;
569
+ if (numDiffCount || missingDiffCount || extraDiffCount || meaningDiffCount || doubtDiffCount) {
570
+ const summaryDiv = document.createElement('div');
571
+ summaryDiv.className = "p-4 rounded-xl bg-yellow-50 text-gray-800";
572
+ let summaryText = '<div class="font-bold mb-2">ملخص الاختلافات:</div><ul class="list-disc ml-4 space-y-1">';
573
+ if (numDiffCount) summaryText += `<li>اختلاف في الأرقام/التواريخ: ${numDiffCount}</li>`;
574
+ if (missingDiffCount) summaryText += `<li>النصوص المفقودة: ${missingDiffCount}</li>`;
575
+ if (extraDiffCount) summaryText += `<li>النصوص الزائدة: ${extraDiffCount}</li>`;
576
+ if (meaningDiffCount) summaryText += `<li>اختلاف في المعنى: ${meaningDiffCount}</li>`;
577
+ if (doubtDiffCount) summaryText += `<li>علامات الشك: ${doubtDiffCount}</li>`;
578
+ summaryText += '</ul>';
579
+ summaryDiv.innerHTML = summaryText;
580
+ document.getElementById('errorsList').appendChild(summaryDiv);
581
+ }
582
+ }
583
+ } catch (error) {
584
+ document.getElementById('errorsList').innerHTML = '';
585
+ addError(`خطأ في التحليل: ${error.message}`);
586
  }
587
+ });
588
+
589
+ })();
 
 
590
  </script>
591
  </body>
592
  </html>