qumra-engine 2.0.127 → 2.0.129
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.
|
@@ -18,445 +18,7 @@ exports.default = new (class SeoExtension {
|
|
|
18
18
|
${injectionCode.body}
|
|
19
19
|
`;
|
|
20
20
|
if (isIframe) {
|
|
21
|
-
scripts += `
|
|
22
|
-
<style>
|
|
23
|
-
|
|
24
|
-
/* مربع التحديد الاحترافي */
|
|
25
|
-
#selector-outline {
|
|
26
|
-
position: absolute;
|
|
27
|
-
border: 2px solid #2563eb;
|
|
28
|
-
background: transparent;
|
|
29
|
-
pointer-events: none;
|
|
30
|
-
display: none;
|
|
31
|
-
z-index: 9999;
|
|
32
|
-
border-radius: 6px;
|
|
33
|
-
box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.2), 0 4px 24px rgba(37, 99, 235, 0.15);
|
|
34
|
-
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/* شريط الأدوات العلوي */
|
|
38
|
-
#selector-toolbar {
|
|
39
|
-
position: absolute;
|
|
40
|
-
top: 5px;
|
|
41
|
-
left: 5px;
|
|
42
|
-
// transform: translateX(-50%);
|
|
43
|
-
background: rgba(255, 255, 255, 0.95);
|
|
44
|
-
backdrop-filter: blur(20px);
|
|
45
|
-
border-radius: 12px;
|
|
46
|
-
padding: 8px 12px;
|
|
47
|
-
display: flex;
|
|
48
|
-
gap: 8px;
|
|
49
|
-
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
|
|
50
|
-
border: 1px solid rgba(37, 99, 235, 0.2);
|
|
51
|
-
pointer-events: all;
|
|
52
|
-
opacity: 0;
|
|
53
|
-
animation: toolbarSlideIn 0.3s ease-out forwards;
|
|
54
|
-
z-index: 10;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
@keyframes toolbarSlideIn {
|
|
58
|
-
from {
|
|
59
|
-
opacity: 0;
|
|
60
|
-
transform: translateY(-10px);
|
|
61
|
-
}
|
|
62
|
-
to {
|
|
63
|
-
opacity: 1;
|
|
64
|
-
transform: translateY(0);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.control-btn {
|
|
69
|
-
width: 32px;
|
|
70
|
-
height: 32px;
|
|
71
|
-
border-radius: 8px;
|
|
72
|
-
border: none;
|
|
73
|
-
cursor: pointer;
|
|
74
|
-
display: flex;
|
|
75
|
-
align-items: center;
|
|
76
|
-
justify-content: center;
|
|
77
|
-
font-size: 14px;
|
|
78
|
-
transition: all 0.2s ease;
|
|
79
|
-
position: relative;
|
|
80
|
-
overflow: hidden;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.control-btn::before {
|
|
84
|
-
content: '';
|
|
85
|
-
position: absolute;
|
|
86
|
-
top: 0;
|
|
87
|
-
left: -100%;
|
|
88
|
-
width: 100%;
|
|
89
|
-
height: 100%;
|
|
90
|
-
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
|
|
91
|
-
transition: left 0.5s;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.control-btn:hover::before {
|
|
95
|
-
left: 100%;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.btn-edit {
|
|
99
|
-
background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
|
|
100
|
-
color: white;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.btn-edit:hover {
|
|
104
|
-
transform: scale(1.1);
|
|
105
|
-
box-shadow: 0 4px 16px rgba(37, 99, 235, 0.4);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.btn-delete {
|
|
109
|
-
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
|
110
|
-
color: white;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
.btn-delete:hover {
|
|
114
|
-
transform: scale(1.1);
|
|
115
|
-
box-shadow: 0 4px 16px rgba(239, 68, 68, 0.4);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
/* تأثير النبض اللطيف */
|
|
121
|
-
#selector-outline::before {
|
|
122
|
-
content: '';
|
|
123
|
-
position: absolute;
|
|
124
|
-
top: -2px;
|
|
125
|
-
left: -2px;
|
|
126
|
-
right: -2px;
|
|
127
|
-
bottom: -2px;
|
|
128
|
-
background: linear-gradient(45deg, #2563eb, #3b82f6, #60a5fa, #2563eb);
|
|
129
|
-
background-size: 400% 400%;
|
|
130
|
-
border-radius: 8px;
|
|
131
|
-
z-index: -1;
|
|
132
|
-
animation: gradientShift 3s ease infinite;
|
|
133
|
-
opacity: 0.6;
|
|
134
|
-
curser: pointer;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
@keyframes gradientShift {
|
|
138
|
-
0% {
|
|
139
|
-
background-position: 0 50%;
|
|
140
|
-
}
|
|
141
|
-
50% {
|
|
142
|
-
background-position: 100% 50%;
|
|
143
|
-
}
|
|
144
|
-
100% {
|
|
145
|
-
background-position: 0 50%;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/* مؤشرات الزوايا */
|
|
150
|
-
.corner-dot {
|
|
151
|
-
position: absolute;
|
|
152
|
-
width: 8px;
|
|
153
|
-
height: 8px;
|
|
154
|
-
background: #2563eb;
|
|
155
|
-
border: 2px solid rgba(255, 255, 255, 0.9);
|
|
156
|
-
border-radius: 50%;
|
|
157
|
-
box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3);
|
|
158
|
-
transition: all 0.2s ease;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
.corner-dot.top-left {
|
|
162
|
-
top: -4px;
|
|
163
|
-
left: -4px;
|
|
164
|
-
animation: cornerPulse 2s ease-in-out infinite;
|
|
165
|
-
}
|
|
166
|
-
.corner-dot.top-right {
|
|
167
|
-
top: -4px;
|
|
168
|
-
right: -4px;
|
|
169
|
-
animation: cornerPulse 2s ease-in-out infinite 0.5s;
|
|
170
|
-
}
|
|
171
|
-
.corner-dot.bottom-left {
|
|
172
|
-
bottom: -4px;
|
|
173
|
-
left: -4px;
|
|
174
|
-
animation: cornerPulse 2s ease-in-out infinite 1s;
|
|
175
|
-
}
|
|
176
|
-
.corner-dot.bottom-right {
|
|
177
|
-
bottom: -4px;
|
|
178
|
-
right: -4px;
|
|
179
|
-
animation: cornerPulse 2s ease-in-out infinite 1.5s;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
@keyframes cornerPulse {
|
|
183
|
-
0%,
|
|
184
|
-
100% {
|
|
185
|
-
transform: scale(1);
|
|
186
|
-
box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3);
|
|
187
|
-
}
|
|
188
|
-
50% {
|
|
189
|
-
transform: scale(1.2);
|
|
190
|
-
box-shadow: 0 4px 16px rgba(37, 99, 235, 0.5);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/* خطوط الحواف */
|
|
195
|
-
.edge-line {
|
|
196
|
-
position: absolute;
|
|
197
|
-
background: rgba(37, 99, 235, 0.4);
|
|
198
|
-
pointer-events: none;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
.edge-line.top {
|
|
202
|
-
top: -1px;
|
|
203
|
-
left: 20px;
|
|
204
|
-
right: 20px;
|
|
205
|
-
height: 2px;
|
|
206
|
-
background: linear-gradient(90deg, transparent, #2563eb, transparent);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.edge-line.bottom {
|
|
210
|
-
bottom: -1px;
|
|
211
|
-
left: 20px;
|
|
212
|
-
right: 20px;
|
|
213
|
-
height: 2px;
|
|
214
|
-
background: linear-gradient(90deg, transparent, #2563eb, transparent);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.edge-line.left {
|
|
218
|
-
left: -1px;
|
|
219
|
-
top: 20px;
|
|
220
|
-
bottom: 20px;
|
|
221
|
-
width: 2px;
|
|
222
|
-
background: linear-gradient(180deg, transparent, #2563eb, transparent);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
.edge-line.right {
|
|
226
|
-
right: -1px;
|
|
227
|
-
top: 20px;
|
|
228
|
-
bottom: 20px;
|
|
229
|
-
width: 2px;
|
|
230
|
-
background: linear-gradient(180deg, transparent, #2563eb, transparent);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/* تأثير التوهج الداخلي */
|
|
234
|
-
#selector-outline::after {
|
|
235
|
-
content: '';
|
|
236
|
-
position: absolute;
|
|
237
|
-
top: 2px;
|
|
238
|
-
left: 2px;
|
|
239
|
-
right: 2px;
|
|
240
|
-
bottom: 2px;
|
|
241
|
-
background: rgba(255, 255, 255, 0.05);
|
|
242
|
-
border-radius: 4px;
|
|
243
|
-
pointer-events: none;
|
|
244
|
-
}
|
|
245
|
-
</style>
|
|
246
|
-
<div id="selector-outline">
|
|
247
|
-
<div id="selector-toolbar">
|
|
248
|
-
<button class="control-btn btn-edit" title="تحرير" id="btn-edit">✏️</button>
|
|
249
|
-
<button class="control-btn btn-delete" title="حذف" id="btn-delete">🗑️</button>
|
|
250
|
-
</div>
|
|
251
|
-
<div class="corner-dot top-left"></div>
|
|
252
|
-
<div class="corner-dot top-right"></div>
|
|
253
|
-
<div class="corner-dot bottom-left"></div>
|
|
254
|
-
<div class="corner-dot bottom-right"></div>
|
|
255
|
-
<div class="edge-line top"></div>
|
|
256
|
-
<div class="edge-line bottom"></div>
|
|
257
|
-
<div class="edge-line left"></div>
|
|
258
|
-
<div class="edge-line right"></div>
|
|
259
|
-
</div>
|
|
260
|
-
|
|
261
|
-
<script>
|
|
262
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
263
|
-
const outline = document.getElementById('selector-outline');
|
|
264
|
-
const btnEdit = document.getElementById('btn-edit');
|
|
265
|
-
const btnDelete = document.getElementById('btn-delete');
|
|
266
|
-
let selectedElement = null;
|
|
267
|
-
let hoverTimeout = null;
|
|
268
|
-
|
|
269
|
-
const widgets = document.querySelectorAll('[widget-id]');
|
|
270
|
-
|
|
271
|
-
function showSelector(element) {
|
|
272
|
-
selectedElement = element;
|
|
273
|
-
const rect = element.getBoundingClientRect();
|
|
274
|
-
outline.style.display = 'block';
|
|
275
|
-
outline.style.top = \`\${rect.top + window.scrollY - 4}px\`;
|
|
276
|
-
outline.style.left = \`\${rect.left + window.scrollX - 4}px\`;
|
|
277
|
-
outline.style.width = \`\${rect.width + 8}px\`;
|
|
278
|
-
outline.style.height = \`\${rect.height + 8}px\`;
|
|
279
|
-
|
|
280
|
-
element.onclick = () => {
|
|
281
|
-
window.parent.postMessage(
|
|
282
|
-
{
|
|
283
|
-
type: "widgetAction",
|
|
284
|
-
action: "edit",
|
|
285
|
-
widgetId: element.getAttribute("widget-id"),
|
|
286
|
-
templateKey: element.getAttribute("template-key"),
|
|
287
|
-
templateId: element.getAttribute("template-id")
|
|
288
|
-
},
|
|
289
|
-
"*"
|
|
290
|
-
);
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
function hideSelector() {
|
|
296
|
-
outline.style.display = 'none';
|
|
297
|
-
selectedElement = null;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
widgets.forEach(el => {
|
|
301
|
-
el.addEventListener('mouseenter', () => {
|
|
302
|
-
clearTimeout(hoverTimeout);
|
|
303
|
-
showSelector(el);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
el.addEventListener('mouseleave', () => {
|
|
307
|
-
hoverTimeout = setTimeout(() => {
|
|
308
|
-
const hovered = document.querySelector(':hover');
|
|
309
|
-
if (!outline.contains(hovered) && hovered !== el) hideSelector();
|
|
310
|
-
}, 50);
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
outline.addEventListener('mouseenter', () => clearTimeout(hoverTimeout));
|
|
315
|
-
outline.addEventListener('mouseleave', () => {
|
|
316
|
-
hoverTimeout = setTimeout(hideSelector, 50);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
// أزرار التحكم
|
|
320
|
-
const btnActions = {
|
|
321
|
-
'btn-edit': el => {
|
|
322
|
-
window.parent.postMessage(
|
|
323
|
-
{
|
|
324
|
-
type: "widgetAction",
|
|
325
|
-
action: "edit",
|
|
326
|
-
widgetId: el.getAttribute("widget-id"),
|
|
327
|
-
templateKey: el.getAttribute("template-key"),
|
|
328
|
-
templateId: el.getAttribute("template-id"),
|
|
329
|
-
},
|
|
330
|
-
"*"
|
|
331
|
-
);
|
|
332
|
-
},
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
'btn-delete': el => {
|
|
336
|
-
window.parent.postMessage(
|
|
337
|
-
{
|
|
338
|
-
type: "widgetAction",
|
|
339
|
-
action: "delete",
|
|
340
|
-
widgetId: el.getAttribute("widget-id"),
|
|
341
|
-
templateKey: el.getAttribute("template-key"),
|
|
342
|
-
templateId: el.getAttribute("template-id"),
|
|
343
|
-
},
|
|
344
|
-
"*"
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
Object.entries(btnActions).forEach(([id, fn]) => {
|
|
350
|
-
const btn = document.getElementById(id);
|
|
351
|
-
btn?.addEventListener('click', e => {
|
|
352
|
-
e.stopPropagation();
|
|
353
|
-
if (selectedElement) fn(selectedElement);
|
|
354
|
-
});
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
// تحديث موقع outline عند scroll
|
|
358
|
-
window.addEventListener('scroll', () => {
|
|
359
|
-
if (!selectedElement || outline.style.display === 'none') return;
|
|
360
|
-
const rect = selectedElement.getBoundingClientRect();
|
|
361
|
-
outline.style.top = \`\${rect.top + window.scrollY - 4}px\`;
|
|
362
|
-
outline.style.left = \`\${rect.left + window.scrollX - 4}px\`;
|
|
363
|
-
outline.style.width = \`\${rect.width + 8}px\`;
|
|
364
|
-
outline.style.height = \`\${rect.height + 8}px\`;
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
// التعامل مع روابط iframe
|
|
368
|
-
function attachIframeLinkListeners(root = document) {
|
|
369
|
-
const links = root.querySelectorAll("a");
|
|
370
|
-
links.forEach(link => {
|
|
371
|
-
if (link.dataset._iframeHandled) return;
|
|
372
|
-
link.dataset._iframeHandled = "true";
|
|
373
|
-
|
|
374
|
-
link.addEventListener("click", function (e) {
|
|
375
|
-
const href = link.getAttribute("href");
|
|
376
|
-
if (!href || href === "#") return;
|
|
377
|
-
e.preventDefault();
|
|
378
|
-
e.stopPropagation();
|
|
379
|
-
window.parent.postMessage({ type: "iframeLinkClick", href }, "*");
|
|
380
|
-
});
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
attachIframeLinkListeners();
|
|
385
|
-
const observer = new MutationObserver(() => attachIframeLinkListeners());
|
|
386
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
387
|
-
});
|
|
388
|
-
</script>
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
<script>
|
|
392
|
-
// الاستماع لأي رسائل جاية من الـ parent
|
|
393
|
-
window.addEventListener("message", (event) => {
|
|
394
|
-
// يفضل تتحقق من الـ origin لو عايز أمان أكتر
|
|
395
|
-
// if (event.origin !== "https://dashboard.qumra.cloud") return;
|
|
396
|
-
|
|
397
|
-
const data = event.data;
|
|
398
|
-
|
|
399
|
-
if (data?.type === "reRender") {
|
|
400
|
-
console.log("📩 تم استقبال رسالة إعادة الرندر:", data);
|
|
401
|
-
// هنا تقدر تنفذ أي كود لإعادة الرندر أو تحديث الصفحة
|
|
402
|
-
// مثلا:
|
|
403
|
-
// location.reload();
|
|
404
|
-
} else {
|
|
405
|
-
console.log("📩 رسالة وصلت:", data);
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
</script>
|
|
409
|
-
|
|
410
|
-
<script>
|
|
411
|
-
window.addEventListener("message", (event) => {
|
|
412
|
-
// لو عايز تتحقق من الـ origin:
|
|
413
|
-
// if (event.origin !== "https://dashboard.qumra.cloud") return;
|
|
414
|
-
|
|
415
|
-
const data = event.data;
|
|
416
|
-
console.log("🚀 ~ run ~ data:", data)
|
|
417
|
-
|
|
418
|
-
if (data?.type === "reRender") {
|
|
419
|
-
console.log("📩 تم استقبال رسالة إعادة الرندر:", data);
|
|
420
|
-
|
|
421
|
-
const { html, widgetId } = data.data || {};
|
|
422
|
-
|
|
423
|
-
if (!widgetId || !html) {
|
|
424
|
-
console.warn("⚠️ لا يوجد widgetId أو html في الرسالة");
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// البحث عن الويدجت المطلوب
|
|
429
|
-
const widgetEl = document.querySelector(\`[widget-id="\${widgetId}"]\`);
|
|
430
|
-
|
|
431
|
-
if (widgetEl) {
|
|
432
|
-
widgetEl.innerHTML = html;
|
|
433
|
-
console.log(\`✅ تم تحديث محتوى الويدجت \${widgetId}\`);
|
|
434
|
-
} else {
|
|
435
|
-
console.warn(\`⚠️ لم يتم العثور على عنصر يحتوي على widget-id="\${widgetId}"\`);
|
|
436
|
-
}
|
|
437
|
-
} else if (data?.type === "fullRender"){
|
|
438
|
-
|
|
439
|
-
const { html } = data.data || {};
|
|
440
|
-
|
|
441
|
-
if !html) {
|
|
442
|
-
console.warn("⚠️ لا يوجد widgetId أو html في الرسالة");
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
document.open(); // فتح الصفحة الحالية لإعادة الكتابة
|
|
447
|
-
document.write(html); // كتابة HTML جديد
|
|
448
|
-
document.close(); // إغلاق الـ stream وتشغيل الـ HTML
|
|
449
|
-
|
|
450
|
-
} else {
|
|
451
|
-
console.log("📩 رسالة وصلت:", data);
|
|
452
|
-
}
|
|
453
|
-
});
|
|
454
|
-
</script>
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
`;
|
|
21
|
+
scripts += `https://cdn.qumra.cloud/lib/bridge/1.0.0/qumra-bridge.js`;
|
|
460
22
|
}
|
|
461
23
|
return new nunjucks_1.runtime.SafeString(scripts);
|
|
462
24
|
}
|