@mhmttosun/dnd 0.0.0-stage → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +327 -2
- package/css/answer-key.css +200 -0
- package/css/editor.css +1797 -0
- package/css/player.css +782 -0
- package/css/stage.css +490 -0
- package/dist/dnd-player-element.js +1758 -0
- package/dist/dnd.js +2041 -0
- package/dist/editor.css +3278 -0
- package/dist/editor.d.ts +23 -0
- package/dist/editor.js +8506 -0
- package/dist/index.d.ts +102 -0
- package/dist/player.css +782 -0
- package/dist/player.d.ts +1 -0
- package/dist/player.js +2041 -0
- package/dist/player.umd.cjs +1758 -0
- package/index.html +203 -0
- package/js/app.js +188 -0
- package/js/core/AnswerKeyManager.js +494 -0
- package/js/core/EditorCore.js +220 -0
- package/js/core/HistoryManager.js +194 -0
- package/js/core/LayerManager.js +86 -0
- package/js/core/ObjectManager.js +468 -0
- package/js/core/SelectionManager.js +459 -0
- package/js/core/StageManager.js +295 -0
- package/js/data/SampleProjects.js +883 -0
- package/js/engine/TransformEngine.js +632 -0
- package/js/engine/ValidationEngine.js +147 -0
- package/js/player/ActivityEngine.js +1272 -0
- package/js/player/ExportManager.js +820 -0
- package/js/ui/MiniToolbar.js +1546 -0
- package/js/ui/PropertyPanel.js +776 -0
- package/js/ui/UIManager.js +596 -0
- package/js/utils/AudioSynthesizer.js +111 -0
- package/package.json +69 -4
- package/src/ActivityPlayer.js +309 -0
- package/src/DNDEditor.js +217 -0
- package/src/components/DNDPlayerElement.js +191 -0
- package/src/index.js +12 -0
- package/src/player.js +13 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ValidationEngine - Proje JSON verisi, cevap anahtarı ve nesne tutarlılığı
|
|
3
|
+
* doğrulamalarını gerçekleştirir. Hatalı ve eksik durumları tespit eder.
|
|
4
|
+
*/
|
|
5
|
+
class ValidationEngine {
|
|
6
|
+
static get VALID_ACTIVITY_TYPES() {
|
|
7
|
+
return ['image-labeling', 'matching', 'categorization', 'sequencing'];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static get VALID_ROLES() {
|
|
11
|
+
return ['decorative', 'draggable', 'target', 'category', 'sequence_slot'];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Bir projenin şemasını doğrular
|
|
16
|
+
* @param {Object} projectData
|
|
17
|
+
* @returns {{ valid: boolean, errors: string[], warnings: string[] }}
|
|
18
|
+
*/
|
|
19
|
+
static validateProject(projectData) {
|
|
20
|
+
const errors = [];
|
|
21
|
+
const warnings = [];
|
|
22
|
+
|
|
23
|
+
if (!projectData || typeof projectData !== 'object') {
|
|
24
|
+
return { valid: false, errors: ['Proje verisi geçerli bir JSON nesnesi değil.'], warnings };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Sürüm kontrolü
|
|
28
|
+
if (!projectData.version) {
|
|
29
|
+
warnings.push('Proje sürüm bilgisi bulunamadı, varsayılan sürüm (1.0) kabul edildi.');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Proje başlığı ve türü
|
|
33
|
+
if (!projectData.project || typeof projectData.project !== 'object') {
|
|
34
|
+
errors.push('Proje meta verisi (project alanı) eksik.');
|
|
35
|
+
} else {
|
|
36
|
+
if (!projectData.project.name || typeof projectData.project.name !== 'string') {
|
|
37
|
+
warnings.push('Proje adı belirtilmemiş.');
|
|
38
|
+
}
|
|
39
|
+
if (!this.VALID_ACTIVITY_TYPES.includes(projectData.project.activityType)) {
|
|
40
|
+
errors.push(`Geçersiz etkinlik türü: ${projectData.project.activityType}. Desteklenenler: ${this.VALID_ACTIVITY_TYPES.join(', ')}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Sahne (Stage) kontrolleri
|
|
45
|
+
if (!projectData.stage || typeof projectData.stage !== 'object') {
|
|
46
|
+
errors.push('Sahne ayarları (stage alanı) eksik.');
|
|
47
|
+
} else {
|
|
48
|
+
if (typeof projectData.stage.width !== 'number' || projectData.stage.width <= 0) {
|
|
49
|
+
errors.push('Sahne genişliği pozitif bir sayı olmalıdır.');
|
|
50
|
+
}
|
|
51
|
+
if (typeof projectData.stage.height !== 'number' || projectData.stage.height <= 0) {
|
|
52
|
+
errors.push('Sahne yüksekliği pozitif bir sayı olmalıdır.');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Nesneler (Objects) kontrolleri
|
|
57
|
+
if (!Array.isArray(projectData.objects)) {
|
|
58
|
+
errors.push('Nesneler listesi (objects alanı) bir dizi olmalıdır.');
|
|
59
|
+
} else {
|
|
60
|
+
const idSet = new Set();
|
|
61
|
+
let draggablesCount = 0;
|
|
62
|
+
let targetsCount = 0;
|
|
63
|
+
|
|
64
|
+
projectData.objects.forEach((obj, idx) => {
|
|
65
|
+
if (!obj.id) {
|
|
66
|
+
errors.push(`Nesne #${idx + 1} için 'id' alanı eksik.`);
|
|
67
|
+
} else if (idSet.has(obj.id)) {
|
|
68
|
+
errors.push(`Mükerrer nesne ID'si tespit edildi: '${obj.id}'.`);
|
|
69
|
+
} else {
|
|
70
|
+
idSet.add(obj.id);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (typeof obj.x !== 'number' || typeof obj.y !== 'number') {
|
|
74
|
+
errors.push(`'${obj.id || idx}' nesnesinin X ve Y koordinatları sayı olmalıdır.`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (obj.role === 'draggable') draggablesCount++;
|
|
78
|
+
if (['target', 'category', 'sequence_slot'].includes(obj.role)) targetsCount++;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (draggablesCount === 0) {
|
|
82
|
+
warnings.push('Etkinlikte henüz hiç sürüklenebilir nesne bulunmuyor.');
|
|
83
|
+
}
|
|
84
|
+
if (targetsCount === 0) {
|
|
85
|
+
warnings.push('Etkinlikte henüz hiç hedef alan bulunmuyor.');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Cevap anahtarı kontrolü
|
|
89
|
+
const answerKey = projectData.answerKey || {};
|
|
90
|
+
for (const [dragId, targetId] of Object.entries(answerKey)) {
|
|
91
|
+
if (!idSet.has(dragId)) {
|
|
92
|
+
warnings.push(`Cevap anahtarındaki '${dragId}' ID'li sürüklenebilir nesne sahnede bulunamadı.`);
|
|
93
|
+
}
|
|
94
|
+
if (projectData.project && projectData.project.activityType !== 'sequencing') {
|
|
95
|
+
if (!idSet.has(targetId)) {
|
|
96
|
+
warnings.push(`Cevap anahtarındaki '${targetId}' ID'li hedef nesne sahnede bulunamadı.`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
valid: errors.length === 0,
|
|
104
|
+
errors,
|
|
105
|
+
warnings
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* HTML dışa aktarmadan önce cevap anahtarının oynanabilirlik durumunu kontrol eder
|
|
111
|
+
*/
|
|
112
|
+
static validatePlayability(projectData) {
|
|
113
|
+
const issues = [];
|
|
114
|
+
const objects = projectData.objects || [];
|
|
115
|
+
const answerKey = projectData.answerKey || {};
|
|
116
|
+
const draggables = objects.filter(o => o.role === 'draggable');
|
|
117
|
+
|
|
118
|
+
if (draggables.length === 0) {
|
|
119
|
+
issues.push('En az bir adet sürüklenebilir nesne eklemelisiniz.');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const unassignedDraggables = draggables.filter(d => !answerKey[d.id]);
|
|
123
|
+
if (unassignedDraggables.length > 0) {
|
|
124
|
+
const names = unassignedDraggables.map(d => d.name || d.text || d.id).join(', ');
|
|
125
|
+
issues.push(`Şu sürüklenebilir nesnelerin cevap anahtarında hedefi belirlenmemiş: ${names}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
ready: issues.length === 0,
|
|
130
|
+
issues
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* XSS ve zararlı HTML girişlerini engellemek için metin temizleme
|
|
136
|
+
*/
|
|
137
|
+
static sanitizeText(str) {
|
|
138
|
+
if (str === null || str === undefined) return '';
|
|
139
|
+
const temp = document.createElement('div');
|
|
140
|
+
temp.textContent = String(str);
|
|
141
|
+
return temp.innerHTML;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Global namespace
|
|
146
|
+
window.DND = window.DND || {};
|
|
147
|
+
window.DND.ValidationEngine = ValidationEngine;
|