gitpub-py 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,741 @@
1
+ // --------------------------------------------------------
2
+ // MULTI FLOWS IN PAGED.JS v 0.1d
3
+ // Think grid on steroid (but a bit more complex than grid for now)
4
+ //
5
+ // This script allows for multi flows in paged.js to allow multi lang and or multi things to say (ho hi *house of leaves* or anything from Danielewski).
6
+ //
7
+ // How does it works:
8
+ //
9
+ // - use css to define the parallel elements.
10
+ // - find the longest bit
11
+ // - generate the longest flow
12
+ // - generate all the other flows, checking for element that will impact the layout `--parallel-impact`
13
+ //
14
+ //
15
+ // CONFIGURATION
16
+ //
17
+ // to put the content on the same spread
18
+ // this.flowLocation = "samespread";
19
+
20
+ // you can add one page if there are differences in page numbers between flows [only work with 2 flows]
21
+ // this.flowSpreadAddWhite = true;
22
+
23
+ // to put the content on the same page
24
+ // this.flowLocation = "samepage";
25
+ //
26
+ //
27
+ // USE WITH CSS:
28
+ //
29
+ // The CSS is the following:
30
+ //
31
+ // ```
32
+ // .something {parallel-flow: alpha}
33
+ // .something-else {parallel-flow: alpha}
34
+ // ```
35
+ //
36
+ // If two elements have the same flow name, they will share a page or a spread, depending on the configuration below.
37
+ //
38
+ //
39
+ // USE WITH PARALLEL IMPACT
40
+ //
41
+ // if an element has a `--parallel-impact: all`, the script will try to find the element on the page, and create overflown empty and transparent objects to push the content around using float and shape outside element.
42
+ //
43
+ // WARNING: The element need to have an ID, as the overlapping clone will be called ${id}-overlap,
44
+ //
45
+ //
46
+ // credits: code written by julien taquet (julien at lesvoisinsdustudio dot ch)
47
+ // beta tested over the EPE workshop n march 2025
48
+ // @ju don’t forget to add the student names who test it in there.
49
+
50
+ const htmlTemplate = (
51
+ customPageClass,
52
+ customPageFirst,
53
+ ) => `<div id="parallel-removeme" class="pagedjs_pages">
54
+ <div class="pagedjs_page ${customPageClass ? customPageClass : ""} ${customPageFirst ? customPageFirst : ""}">
55
+ <div class="pagedjs_sheet">
56
+ <div class="pagedjs_pagebox">
57
+ <div class="pagedjs_area">
58
+ <div class="pagedjs_page_content">
59
+ <div class="testingzone"></div>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ </div>`;
66
+
67
+ class multilang extends Paged.Handler {
68
+ constructor(chunker, polisher, caller) {
69
+ super(chunker, polisher, caller);
70
+
71
+ // to put the content on the same spread
72
+ // this.flowLocation = "samespread";
73
+
74
+ // you can add one page if there are differences in page numbers between flows [only work with 2 flows]
75
+ // this.flowSpreadAddWhite = true;
76
+
77
+ // to put the content on the same page
78
+ this.flowLocation = "samepage";
79
+
80
+ // this.parallelFlows will find the flows from the css;
81
+ this.parallelFlows = [];
82
+
83
+ // this.tracker will keep tack of the flows
84
+ this.flowTracker = [];
85
+
86
+ this.parallelImpacts = [];
87
+
88
+ // this will find elements to sync in the flow:
89
+ // let say you want to start one specific element of a flow at the same time another element is layout out (or more likely, you want to wait for the lang to finish before starting the next one) you need to set that info in the css. Those will be markedup as parallelSync
90
+ this.parallelSync = [];
91
+ }
92
+
93
+ onDeclaration(declaration, dItem, dList, rule) {
94
+ // if flow location is not set , make it same spread by default
95
+ if (!this.flowLocation == "samespread" || !this.flowLocation == "samepage")
96
+ this.flowLocation = "samespread";
97
+
98
+ // if you find a parallel-flow
99
+ if (declaration.property == "--parallel-flow") {
100
+ let sel = csstree.generate(rule.ruleNode.prelude);
101
+ sel = sel.replaceAll('[data-id="', "#");
102
+ sel = sel.replaceAll('"]', "");
103
+ let itemsList = sel.split(",");
104
+
105
+ itemsList.forEach((el) => {
106
+ let flow = this.parallelFlows.find((a) => {
107
+ return a.flow == declaration.value.value.trim();
108
+ });
109
+ if (flow) {
110
+ flow.selectors.push({ selector: el, height: 0 });
111
+ } else {
112
+ this.parallelFlows.push({
113
+ flow: declaration.value.value.trim(),
114
+ selectors: [{ selector: el, height: 0 }],
115
+ });
116
+ }
117
+ });
118
+ }
119
+ if (declaration.property == "--parallel-impact") {
120
+ let sel = csstree.generate(rule.ruleNode.prelude);
121
+ sel = sel.replaceAll('[data-id="', "#");
122
+ sel = sel.replaceAll('"]', "");
123
+ let itemsList = sel.split(",");
124
+ itemsList.forEach((el) => {
125
+ this.parallelImpacts.push(el);
126
+ });
127
+ }
128
+
129
+ // get parallel sync
130
+ if (declaration.property == "--paged-parallel-sync") {
131
+ let sel = csstree.generate(rule.ruleNode.prelude).trim();
132
+ sel = sel.replaceAll('[data-id="', "#");
133
+ sel = sel.replaceAll('"]', "");
134
+ let itemsList = sel.split(",");
135
+
136
+ let values = declaration.value.value.trim().split(" ");
137
+
138
+ itemsList.forEach((el) => {
139
+ this.parallelSync.push({
140
+ flow: values[0],
141
+ syncmarks: { main: el, second: values[1] },
142
+ });
143
+ });
144
+ }
145
+ }
146
+
147
+ async beforeParsed(content, chunker) {
148
+ // this.parallelFlows.push({
149
+ // flow: "find the name of the flow",
150
+ // selectors: [{ selector: #id of each block, height: 0 }],
151
+ // });
152
+ //for each flows, find if there is a need for a sync
153
+ this.parallelSync.forEach((sync) => {
154
+ let flowtoupdate = this.parallelFlows.findIndex(
155
+ (flow) => flow.flow === sync.flow,
156
+ );
157
+
158
+ if (flowtoupdate !== -1) {
159
+ this.parallelFlows[flowtoupdate].syncmarks = sync.syncmarks;
160
+ }
161
+ });
162
+
163
+ // console.log(this.parallelFlows);
164
+ this.parallelFlows.forEach((parallelFlow, flowindex) => {
165
+ let newParallelFlows = [];
166
+ if (parallelFlow.syncmarks) {
167
+ let results = [];
168
+ let flowname = parallelFlow.flow;
169
+
170
+ let newSelectors = [];
171
+ parallelFlow.selectors.forEach((flowEl, selectorIndex) => {
172
+ // pour chaque flow, retrouver les separations
173
+ //
174
+ // et pour chaque separation, créer un nouveau flow à rajouter aux parallelflows
175
+
176
+ let { html, ids } = splitHtmlByDelimiter(
177
+ //htmlstring, splitselector
178
+ content.querySelector(flowEl.selector).innerHTML,
179
+ parallelFlow.syncmarks.main,
180
+ `${flowname}-${selectorIndex + 1}`,
181
+ content.querySelector(flowEl.selector).className,
182
+ content.querySelector(flowEl.selector).id,
183
+ content.querySelector(flowEl.selector).tagName,
184
+ );
185
+
186
+ content.querySelector(flowEl.selector).innerHTML = html;
187
+ content
188
+ .querySelector(flowEl.selector)
189
+ .insertAdjacentHTML("afterend", html);
190
+ content.querySelector(flowEl.selector).remove();
191
+
192
+ ids.forEach((sel) => {
193
+ sel = [{ flow: sel, height: 0 }];
194
+ });
195
+
196
+ newSelectors.push(ids);
197
+
198
+ // remix the selectors to build the newwest parallellines
199
+ results = mixArrays(...newSelectors);
200
+ });
201
+
202
+ //new flow
203
+ // new flow = {flow, selectors: [{selector: newSelector, height:0}]}
204
+
205
+ // console.log(this.parallelFlows);
206
+ // console.log(results);
207
+
208
+ results.forEach((res, index) => {
209
+ let flowName = `new${parallelFlow.flow}${index}`;
210
+ let selectors = [];
211
+
212
+ res.forEach((thin, index) => {
213
+ selectors.push({ selector: [`.${thin}`], height: 0 });
214
+ });
215
+
216
+ // console.log("sel", selectors);
217
+
218
+ newParallelFlows.push({
219
+ oldflow: parallelFlow.flow,
220
+ flow: flowName,
221
+ selectors: selectors,
222
+ });
223
+ });
224
+
225
+ let flowIndex = this.parallelFlows.findIndex(
226
+ (i) => i.flow == newParallelFlows[0].oldflow,
227
+ );
228
+
229
+ this.parallelFlows.splice(flowIndex, 1, ...newParallelFlows);
230
+
231
+ //remove all syncmark from parallelflows obj
232
+ // ou plutôt, remplace le par les nouveaux flows
233
+ // this.parallelFlows = this.parallelFlows.filter((el) => !el.syncmarks);
234
+ }
235
+ });
236
+
237
+ // set the parallel impacts
238
+ this.parallelImpacts.forEach((sel) => {
239
+ content.querySelectorAll(sel).forEach((el) => {
240
+ el.classList.add("parallel-impact");
241
+ });
242
+ });
243
+
244
+ //set the rest
245
+ this.parallelFlows.forEach((flow, index) => {
246
+ if (flow.selectors.length < 2) {
247
+ delete this.parallelFlows[index];
248
+ }
249
+ flow.selectors = flow.selectors.filter((e) =>
250
+ content.querySelector(e.selector),
251
+ );
252
+ });
253
+ document.body.insertAdjacentHTML(
254
+ "beforeend",
255
+ htmlTemplate("find-if-its-a-named-page", "find-if-its-the-first-one"),
256
+ );
257
+
258
+ // render the parallel flows
259
+ this.parallelFlows.forEach((flows, flowsIndex) => {
260
+ //name of the flow
261
+ let flowName = this.parallelFlows[flowsIndex].flow;
262
+
263
+ // flow selectors
264
+ flows.selectors.forEach((flow, selectorIndex) => {
265
+ // find the longest flow.
266
+ let theZone = document.querySelector(".testingzone");
267
+
268
+ // check if the flow exist
269
+
270
+ let testedFlow = content.querySelector(flow.selector).cloneNode(true);
271
+
272
+ theZone.insertAdjacentElement("beforeend", testedFlow);
273
+
274
+ if (selectorIndex == 0) {
275
+ content.querySelector(flow.selector).dataset.flowstart =
276
+ `start-${flowsIndex}`;
277
+ }
278
+
279
+ content
280
+ .querySelector(flow.selector)
281
+ .insertAdjacentHTML("afterbegin", `<span class="flow-start"></span>`);
282
+
283
+ content
284
+ .querySelector(flow.selector)
285
+ .classList.add(`parallel-obj-${flowName}`);
286
+
287
+ content.querySelector(flow.selector).dataset.height =
288
+ testedFlow.offsetHeight;
289
+
290
+ content.querySelector(flow.selector).dataset.flowId =
291
+ `parallel-obj-${flowName}-${selectorIndex}`;
292
+
293
+ content.querySelector(flow.selector).dataset.flowName = `${flowName}`;
294
+
295
+ flow.height = testedFlow.offsetHeight;
296
+ testedFlow.classList.add(`parallel-obj-${flowName}`);
297
+ testedFlow.dataset.height = testedFlow.offsetHeight;
298
+ testedFlow.dataset.flowId = `parallel-obj-${flowName}-${selectorIndex}`;
299
+ testedFlow.dataset.flowName = `${flowName}`;
300
+ });
301
+
302
+ let parallelElements = Array.from(
303
+ document.querySelectorAll(`.parallel-obj-${flowName}`),
304
+ );
305
+ let biggestHeightId = parallelElements.reduce(
306
+ (max, el) =>
307
+ parseInt(el.dataset.height) > parseInt(max.dataset.height) ? el : max,
308
+ parallelElements[0],
309
+ ).dataset.flowId;
310
+
311
+ content.querySelector(
312
+ `[data-flow-id="${biggestHeightId}"]`,
313
+ ).dataset.mainObjInFlow = "true";
314
+
315
+ content
316
+ .querySelector(`[data-flow-id="${biggestHeightId}"]`)
317
+ .classList.add("main-parallel-flow");
318
+
319
+ // debugger;
320
+ content
321
+ .querySelector(`[data-flowstart="start-${flowsIndex}"]`)
322
+ .insertAdjacentElement(
323
+ "beforebegin",
324
+ content.querySelector(`[data-flow-id="${biggestHeightId}"]`),
325
+ );
326
+ // problem is how the content is sent in the wrong order at the end.
327
+
328
+ // return;
329
+ parallelElements.forEach((el) => {
330
+ if (el.dataset.flowId != biggestHeightId) {
331
+ content.append(
332
+ content.querySelector(`[data-flow-id="${el.dataset.flowId}"]`),
333
+ );
334
+ }
335
+ });
336
+ });
337
+ document.querySelector("#parallel-removeme").remove();
338
+
339
+ //find the sync element in the parallel
340
+ this.parallelSync.forEach((e) => {
341
+ e.synchro?.forEach((sync) => {
342
+ this.parallelFlows.forEach((plflow) => {
343
+ plflow.selectors.forEach((el) => {
344
+ content
345
+ .querySelectorAll(`${el.selector} ${sync[0]}`)
346
+ .forEach((elemented, index) => {
347
+ elemented.classList.add("syncmark");
348
+ elemented.classList.add(`sync-${index}`);
349
+ });
350
+ });
351
+ });
352
+ });
353
+
354
+ // let main = e.synchro[0];
355
+ // let secondary = e.synchro[1];
356
+ });
357
+ }
358
+
359
+ renderNode(node) {
360
+ if (this.flowLocation != "samepage") {
361
+ return;
362
+ console.log(
363
+ "we dont put the things on the same page, we’re on same spread",
364
+ );
365
+ }
366
+ if (node.nodeType == 1) {
367
+ if (node.closest("[data-main-obj-in-flow]")) return;
368
+
369
+ if (node.dataset.flowStart) {
370
+ // console.log(node);
371
+ return;
372
+ //console.log(`the flow start now`);
373
+ }
374
+
375
+ if (node.className.includes("flow-start")) {
376
+ return;
377
+ }
378
+
379
+ if (node.closest("[data-flow-name]")) {
380
+ let flowName = node.closest("[data-flow-name]").dataset.flowName;
381
+ let flowId = node.closest("[data-flow-name]").dataset.flowId;
382
+
383
+ const pageNumber = getPageNumber(node);
384
+
385
+ if (!document.querySelector(`[data-flow-id="${flowId}"] .flow-start`)) {
386
+ return;
387
+ console.log(`the flow really didnt started there , like the party`);
388
+ }
389
+ let firstPageOfThisFlow = getPageNumber(
390
+ document.querySelector(`[data-flow-id="${flowId}"] .flow-start`),
391
+ );
392
+
393
+ let map = pageNumber - firstPageOfThisFlow;
394
+
395
+ let pagesInTrackers = this.flowTracker.find(
396
+ (a) => a.flow == flowName,
397
+ ).pages;
398
+
399
+ if (pagesInTrackers.length > 0) {
400
+
401
+ // this is the part I added in, preventing the entire code from breaking when tracker 1 page is missing
402
+ let trackerPage = pagesInTrackers[map];
403
+
404
+ if (!trackerPage) {
405
+ console.warn(
406
+ "Parallel Flows: missing tracker page",
407
+ {
408
+ pageNumber,
409
+ firstPageOfThisFlow,
410
+ map,
411
+ pagesInTrackers
412
+ }
413
+ );
414
+ return;
415
+ }
416
+
417
+ if (!trackerPage.elements) {
418
+ console.warn(
419
+ "Parallel Flows: tracker page has no elements",
420
+ trackerPage
421
+ );
422
+ return;
423
+ }
424
+
425
+ // now back to the given function, with using the proof previously it was:
426
+ // pagesInTrackers[map].elements.forEach((el) => {
427
+ trackerPage.elements.forEach((el) => {
428
+ let clone = document.querySelector(`#${el.id}`).cloneNode(true);
429
+ if (
430
+ !node.closest(".pagedjs_page").querySelector(`#${el.id}-clone`)
431
+ ) {
432
+ clone.id = el.id + "-clone";
433
+ clone.dataset.id = clone.id;
434
+ clone.dataset.ref = "unset";
435
+ clone.setAttribute(
436
+ "style",
437
+ `width: ${el.offsetWidth}px ; height: ${el.offsetHeight}px ; top: ${el.offsetTop}px; left: ${el.offsetLeft}px; position: absolute; margin: 0`,
438
+ );
439
+ clone.style.position = "absolute;";
440
+
441
+ node
442
+ .closest(".pagedjs_page_content")
443
+ .querySelector("div")
444
+ .insertAdjacentElement("afterbegin", clone);
445
+
446
+ console.log(
447
+ "there is a clonable element on page",
448
+ pageNumber,
449
+ firstPageOfThisFlow,
450
+ map,
451
+ );
452
+ }
453
+ node.closest(`[data-flow-name]`).style.minHeight = "100%";
454
+
455
+ if (node.closest(`.pagedjs_page`).querySelector(`#${clone.id}`)) {
456
+ let overlap = checkOverlap(
457
+ node
458
+ .closest(`.pagedjs_page`)
459
+ .querySelector(`#${clone.id}`)
460
+ .getBoundingClientRect(),
461
+ node.closest(`[data-flow-name]`).getBoundingClientRect(),
462
+ );
463
+
464
+ if (overlap) {
465
+ createOverlapElement(
466
+ overlap,
467
+ node.closest("[data-flow-name]"),
468
+ 20,
469
+ `${el.id}-overlap`,
470
+ );
471
+ }
472
+ }
473
+ });
474
+ }
475
+ }
476
+ }
477
+ }
478
+
479
+ async finalizePage(page) {
480
+ if (page.querySelector("[data-main-obj-in-flow]")) {
481
+ let impacts = page.querySelectorAll(".parallel-impact");
482
+
483
+ let flowName = page.querySelector("[data-flow-name]").dataset.flowName;
484
+
485
+ let flowtracker = this.flowTracker.find((a) => a.flow == flowName);
486
+
487
+ if (!flowtracker) {
488
+ flowtracker = this.flowTracker.push({
489
+ flow: flowName,
490
+ pages: [],
491
+ });
492
+ }
493
+
494
+ let elementsToTakeIntoAccount = [];
495
+
496
+ impacts.forEach((element) => {
497
+ elementsToTakeIntoAccount.push({
498
+ id: element.id,
499
+ offsetTop: element.offsetTop,
500
+ offsetLeft: element.offsetLeft,
501
+ offsetWidth: element.offsetWidth,
502
+ offsetHeight: element.offsetHeight,
503
+ });
504
+ });
505
+
506
+ this.flowTracker
507
+ .find((a) => a.flow == flowName)
508
+ .pages.push({
509
+ page: page.id.replace("page-", ""),
510
+ elements: elementsToTakeIntoAccount,
511
+ });
512
+ }
513
+
514
+ if (this.flowSpreadAddWhite) {
515
+ if (page.querySelector("[data-main-obj-in-flow]")) {
516
+ this.parallelFlows.forEach(async (pflow) => {
517
+ let newpage = this.chunker.addPage();
518
+ newpage.element?.classList.add("addedpage");
519
+ newpage.element?.classList.add("pagedjs_named_page");
520
+ newpage.element?.classList.add("pagedjs_pagedjs-fullpage_page");
521
+
522
+ await this.chunker.hooks.afterPageLayout.trigger(
523
+ newpage.element,
524
+ newpage,
525
+ undefined,
526
+ this.chunker,
527
+ );
528
+ await this.chunker.hooks.finalizePage.trigger(
529
+ newpage.element,
530
+ newpage,
531
+ undefined,
532
+ this.chunker,
533
+ );
534
+ this.chunker.emit("renderedPage", newpage);
535
+ });
536
+ }
537
+ }
538
+ }
539
+
540
+ afterRendered(pages) {
541
+ console.warn(
542
+ "pagedjs is finished, any error here with next sibling or whatever will not impact us, it’s just a bit of complexity to find where / why is that come from \n\n If you use reload in place you may have a problem with the blur",
543
+ );
544
+
545
+ this.parallelFlows.forEach((pflow) => {
546
+ let hostId = getBiggestHeight(pflow.selectors);
547
+ let hostObj = document.querySelectorAll(hostId.selector);
548
+ let guestIds = getAllButBiggestHeight(pflow.selectors);
549
+ let guestsObj = [];
550
+
551
+ guestIds.forEach((gu) => {
552
+ guestsObj.push(document.querySelectorAll(gu.selector));
553
+ });
554
+
555
+ let guests = [];
556
+
557
+ guestIds.forEach((selectors) => {
558
+ guests = [...document.querySelectorAll(hostId.selector)];
559
+ });
560
+
561
+ if (this.flowLocation == "samepage") {
562
+ guestsObj.forEach((guests) => {
563
+ //1. find the multiple sync marker
564
+ //2. check if the page contains the sync marker you’re looking for
565
+
566
+ for (let i = 0; i < hostObj.length; i++) {
567
+ if (hostObj[i] && guests[i]) {
568
+ let obj = guests[i];
569
+
570
+ let pageToRemove = guests[i].closest(".pagedjs_page");
571
+
572
+ obj.style.left = `${obj.offsetLeft}px`;
573
+ obj.style.width = `${obj.offsetWidth}px`;
574
+ obj.style.top = `${obj.offsetTop}px`;
575
+ obj.style.height = `${obj.offsetHeight}px`;
576
+ obj.style.position = "absolute";
577
+ obj.style.margin = "0";
578
+
579
+ hostObj[i]
580
+ .closest(".pagedjs_page_content")
581
+ .querySelector("div")
582
+ .insertAdjacentElement("beforeend", obj);
583
+
584
+ // remove unused page
585
+ pageToRemove.remove();
586
+ }
587
+ }
588
+ });
589
+ } else if (this.flowLocation == "samespread") {
590
+ let diff = hostObj.length - guestsObj.length;
591
+
592
+ guestsObj.forEach((guests) => {
593
+ for (let i = 0; i < hostObj.length; i++) {
594
+ if (hostObj[i] && guests[i]) {
595
+ if (this.flowSpreadAddWhite) {
596
+ hostObj[i]
597
+ .closest(".pagedjs_page")
598
+ .nextElementSibling.querySelector(".pagedjs_page_content")
599
+ .insertAdjacentHTML("afterbegin", `<div></div>`);
600
+ let previousPage = guests[i].closest(".pagedjs_page");
601
+ hostObj[i]
602
+ .closest(".pagedjs_page")
603
+ .nextElementSibling.querySelector(".pagedjs_page_content div")
604
+ .insertAdjacentElement("afterbegin", guests[i]);
605
+ // remove the empty page at the end.
606
+ previousPage.remove();
607
+ } else {
608
+ // bring the content back;
609
+ hostObj[i]
610
+ .closest(".pagedjs_page")
611
+ .insertAdjacentElement(
612
+ "beforebegin",
613
+ guests[i].closest(".pagedjs_page"),
614
+ );
615
+ }
616
+ }
617
+ }
618
+ });
619
+ }
620
+ });
621
+ }
622
+ }
623
+ Paged.registerHandlers(multilang);
624
+
625
+ function getBiggestHeight(arr) {
626
+ return arr.reduce(
627
+ (max, obj) => (obj.height > max.height ? obj : max),
628
+ arr[0],
629
+ );
630
+ }
631
+
632
+ function getAllButBiggestHeight(arr) {
633
+ const biggest = getBiggestHeight(arr);
634
+ return arr.filter((obj) => obj !== biggest);
635
+ }
636
+ function getPageNumber(obj) {
637
+ if (!obj) return undefined;
638
+ return obj.closest(".pagedjs_page").id.replace("page-", "");
639
+ }
640
+
641
+ function checkOverlap(rect1, rect2) {
642
+ if (!rect1 || !rect2) return console.log("there is no rect 1 or rect 2");
643
+ // Check if the rectangles intersect
644
+ const xOverlap = rect1.left < rect2.right && rect1.right > rect2.left;
645
+ const yOverlap = rect1.top < rect2.bottom && rect1.bottom > rect2.top;
646
+
647
+ if (xOverlap && yOverlap) {
648
+ const overlapX = Math.max(rect1.left, rect2.left);
649
+ const overlapY = Math.max(rect1.top, rect2.top);
650
+ const overlapWidth = Math.min(rect1.right, rect2.right) - overlapX;
651
+ const overlapHeight = Math.min(rect1.bottom, rect2.bottom) - overlapY;
652
+
653
+ return {
654
+ x: overlapX - rect2.left,
655
+ y: overlapY - rect2.top,
656
+ width: overlapWidth,
657
+ height: overlapHeight,
658
+ };
659
+ }
660
+
661
+ return null; // No overlap
662
+ }
663
+
664
+ function createOverlapElement(overlap, box2element, offset = 16, id) {
665
+ const overlapElement = document.createElement("div");
666
+ overlapElement.classList.add("overlap");
667
+
668
+ overlapElement.style.width = `${overlap.width + offset}px`;
669
+ overlapElement.style.height = `${overlap.height + offset}px`;
670
+ overlapElement.style.marginTop = `${overlap.y - offset / 2}px`;
671
+ overlapElement.style.marginLeft = `${overlap.x - offset / 2}px`;
672
+ overlapElement.style.float = `left`;
673
+ overlapElement.style.shapeOutside = `content-box`;
674
+ overlapElement.id = id;
675
+ overlapElement.dataset.id = id;
676
+
677
+ box2element.insertAdjacentElement("afterbegin", overlapElement);
678
+ }
679
+
680
+ function splitHtmlByDelimiter(
681
+ htmlString,
682
+ splitSelector,
683
+ flowName = "group",
684
+ classes,
685
+ dataId,
686
+ elementtype = "section",
687
+ ) {
688
+ const parser = new DOMParser();
689
+ const doc = parser.parseFromString(`<div>${htmlString}</div>`, "text/html");
690
+ const container = doc.body.firstChild;
691
+
692
+ const children = Array.from(container.childNodes);
693
+ const result = [];
694
+ let currentGroup = [];
695
+
696
+ children.forEach((child) => {
697
+ if (child.nodeType === Node.ELEMENT_NODE && child.matches(splitSelector)) {
698
+ if (currentGroup.length > 0) {
699
+ result.push(currentGroup);
700
+ }
701
+ currentGroup = [child]; // start new group with delimiter
702
+ } else {
703
+ currentGroup.push(child);
704
+ }
705
+ });
706
+
707
+ if (currentGroup.length > 0) {
708
+ result.push(currentGroup);
709
+ }
710
+
711
+ const outputContainer = document.createElement("div");
712
+ const generatedIds = [];
713
+
714
+ result.forEach((group, index) => {
715
+ const wrapper = document.createElement(elementtype);
716
+ const id = `${flowName}-${index}`;
717
+ wrapper.className = classes ? classes : "";
718
+ wrapper.dataset.id = dataId ? dataId : "";
719
+ wrapper.classList.add(id);
720
+ generatedIds.push(id);
721
+ group.forEach((node) => wrapper.appendChild(node));
722
+ outputContainer.appendChild(wrapper);
723
+ });
724
+
725
+ return {
726
+ html: outputContainer.innerHTML,
727
+ ids: generatedIds,
728
+ };
729
+ }
730
+
731
+ function mixArrays(...arrays) {
732
+ const length = arrays[0].length;
733
+ const result = [];
734
+
735
+ for (let i = 0; i < length; i++) {
736
+ const group = arrays.map((arr) => arr[i]);
737
+ result.push(group);
738
+ }
739
+
740
+ return result;
741
+ }