PageSourceSearch

https://brusartsi.com/inc/themes/default/assets/internal/brochures-setup.js?v=876a094ca81205db56181e3ee7421a8d

js brusartsi.com collected 2026-10-02 20:10:44 UTC 19,439 bytes, 561 lines download raw bytes

1$(function ()
2{
3    let $allBrochureLinks = $(".brochure-init-link");
4    let isMobile = $(window).width() < 768;
5    let zoomLevel = isMobile
6        ? 4
7        : 2;
8    let lastTapTime = 0;
9    let doubleTapDelay = 300;
10
11    $allBrochureLinks.each(function (index, brochureLink)
12    {
13        let $brochureLink = $(brochureLink);
14        let $brochure = buildInitialHtml($brochureLink);
15        initBrochure($brochure);
16    });
17
18    function buildInitialHtml($link)
19    {
20        // Toolbar
21        let $linkClone = $link
22            .clone()
23            .text("")
24            .attr("download", "")
25            .addClass("btn download-button")
26            .append($("<i/>").addClass("fa fa-download").attr("aria-hidden", "true"));
27        let $printButton = $("<button/>")
28            .addClass("btn print-button")
29            .append($("<i/>").addClass("fa fa-print").attr("aria-hidden", "true"));
30        let $zoomButton = $("<button/>")
31            .addClass("btn zoom-button")
32            .append($("<i/>").addClass("fa fa-search-plus").attr("aria-hidden", "true"));
33        let $toolbar = $("<div/>")
34            .addClass("col-10 d-flex justify-content-center")
35            .css("gap", "1rem")
36            .append($linkClone)
37            .append($printButton)
38            .append($zoomButton);
39        let $toolbarRow = $("<div/>")
40            .css("visibility", "hidden")
41            .addClass("row")
42            .append($("<div/>").addClass("col-1"))
43            .append($toolbar)
44            .append($("<div/>").addClass("col-1"));
45        // Flipbook
46        let $prevButton = $("<button/>")
47            .addClass("btn previous-button")
48            .text("<");
49        let $nextButton = $("<button/>")
50            .addClass("btn next-button")
51            .text(">");
52        let $book = $("<div/>")
53            .addClass("col-10 book-wrapper")
54            .append($("<div/>").addClass("row")
55                .append($("<div/>").addClass("col-12")
56                    .append($("<div/>").addClass("zoom-layer")
57                        .append($("<div/>").addClass("flipbook"))
58                    )
59                )
60            );
61        let $bookRow = $("<div/>")
62            .css("visibility", "hidden")
63            .addClass("row")
64            .append($("<div/>").addClass("col-1").append($prevButton))
65            .append($book)
66            .append($("<div/>").addClass("col-1").append($nextButton));
67        // Slider
68        let $sliderInput = $("<input/>")
69            .attr({
70                "id": "flipbook-page-slider",
71                "type": "range",
72                "min": "1",
73                "value": "1",
74                "step": "1"
75            })
76            .addClass("form-control form-control-range page-slider");
77        let $sliderLabel = $("<label/>")
78            //.text("1")
79            .attr("for", "flipbook-page-slider")
80            .addClass("page-slider-label text-center d-block w-100");
81        let $slider = $("<div/>")
82            .addClass("col-10")
83            .append($sliderInput)
84            .append($sliderLabel);
85        let $sliderRow = $("<div/>")
86            .css("visibility", "hidden")
87            .addClass("row")
88            .append($("<div/>").addClass("col-1"))
89            .append($slider)
90            .append($("<div/>").addClass("col-1"));
91        // Loading
92        let $loading = $("<div/>")
93            .addClass("alert alert-primary")
94            .append($("<span/>")
95                .addClass("spinner-border spinner-border-sm mr-2 d-inline-block")
96                .attr({
97                    "role": "status",
98                    "aria-hidden": "true"
99                }))
100            .append($("<span/>").html("Зарежда се PDF документ <span class='loading-info'>...</span>"))
101            .append($("<div/>").addClass("pdf-loader-bar"));
102        // Final Container
103        let $brochure = $("<div/>")
104            .addClass("container-fluid brochure")
105            .append($loading)
106            .append($toolbarRow)
107            .append($bookRow)
108            .append($sliderRow);
109        $link.after($brochure);
110        $link.remove();
111        return $brochure;
112    }
113
114    function initBrochure($brochure)
115    {
116        let url = $brochure.find(".download-button").attr("href");
117
118        if (!url)
119            return;
120
121        fetch(url)
122            .then(response =>
123            {
124                if (!response.ok)
125                    throw new Error(`Failed to load PDF for URL ${url}`);
126                return response.blob();
127            })
128            .then(blob =>
129            {
130                setupPrint($brochure, blob);
131                return blob.arrayBuffer();
132            })
133            .then(buffer => pdfjsLib.getDocument(buffer).promise)
134            .then(pdf => buildPageStructure($brochure, pdf))
135            .then(pdf => setupFlipBook($brochure, pdf))
136            .catch(err => console.error(err));
137    }
138
139    function setupPrint($brochure, blob)
140    {
141        $brochure.find(".print-button").on("click", function ()
142        {
143            let $printFrame = $brochure.find(".print-frame");
144
145            if (!$printFrame.length)
146            {
147                let blobUrl = URL.createObjectURL(blob);
148                $printFrame = $("<iframe/>")
149                    .attr("src", blobUrl)
150                    .addClass("d-none print-frame");
151                $printFrame.on("load", function ()
152                {
153                    setTimeout(function ()
154                    {
155                        $printFrame.focus();
156                        let window = $printFrame[0].contentWindow;
157                        window.print();
158                    }, 1000);
159                });
160                $brochure.append($printFrame);
161            }
162            else
163            {
164                setTimeout(function ()
165                {
166                    $printFrame.focus();
167                    let window = $printFrame[0].contentWindow;
168                    window.print();
169                }, 1000);
170            }
171        });
172    }
173
174    function buildPageStructure($brochure, pdf)
175    {
176        let $flipbook = $brochure.find(".flipbook");
177        let chain = Promise.resolve();
178
179        $brochure.find(".alert").fadeOut();
180        $brochure.find(".row").css("visibility", "visible");
181
182        for (let i = 1; i <= pdf.numPages; ++i)
183        {
184            chain = chain.then(() => pdf.getPage(i).then(function (page)
185                {
186                    let pageInfo = getPageInfo(page, $flipbook);
187
188                    let $canvas = $("<canvas/>")
189                        .attr({
190                            "width": pageInfo.canvasHtmlWidth,
191                            "height": pageInfo.canvasHtmlHeight
192                        })
193                        .css({
194                            "width": pageInfo.canvasCssWidth + "px",
195                            "height": pageInfo.canvasCssHeight + "px"
196                        })
197                        .addClass(`original-${i}`)
198                        .attr("data-original", i);
199
200                    let $pageDiv = $("<div/>")
201                        .addClass("cpage")
202                        .css({
203                            width: pageInfo.canvasCssWidth + "px",
204                            height: pageInfo.canvasCssHeight + "px",
205                            overflow: "hidden"
206                        })
207                        .append($canvas);
208
209                    $flipbook.append($pageDiv);
210
211                    if (pageInfo.isLandscape)
212                    {
213                        $pageDiv.addClass("double");
214                        canvasScissor($pageDiv, false);
215                    }
216                }));
217        }
218
219        return chain.then(function ()
220        {
221            let $doubles = $brochure.find(".double");
222            let $pages = $brochure.find(".cpage");
223
224            if ($doubles.length && $pages.length % 2 === 0)
225            {
226                $pages
227                    .first()
228                    .after($("<div/>")
229                        .addClass("cpage blank")
230                        .css({
231                            "width": ($flipbook.width() / 2) + "px",
232                            "height": "100%", "text-align": "center",
233                            "padding-top": "2rem"
234                        })
235                        .text("Тази страница е нарочно празна"));
236            }
237            return pdf;
238        });
239    }
240
241    function canvasScissor($obj, copyGraphicsData)
242    {
243        $obj.each(function ()
244        {
245            let element = $(this);
246
247            if (element.is(".scissored"))
248                return; // continue
249
250            element.addClass("scissored");
251
252            let pageProperties =
253                {
254                    width: element.width() / 2,
255                    height: element.height(),
256                    overflow: "hidden"
257                };
258
259            let newElement = element.clone(true);
260            let leftPage = $("<div/>", { css: pageProperties });
261            let rightPage = $("<div/>", { css: pageProperties });
262            let originalCanvas = element.find("canvas")[0];
263            let clonedCanvas = cloneCanvasWithData(originalCanvas, copyGraphicsData);
264
265            newElement.find("canvas").remove();
266            newElement.append(clonedCanvas);
267            element.after(leftPage);
268            leftPage.after(rightPage);
269
270            element.css("margin-left", 0).appendTo(leftPage);
271            newElement.css("margin-left", -pageProperties.width + "px").appendTo(rightPage);
272        });
273
274        return $obj;
275    }
276
277    function cloneCanvasWithData(canvas, copyGfxData)
278    {
279        let clone = canvas.cloneNode(true);
280        clone.width = canvas.width;
281        clone.height = canvas.height;
282        clone.style.width = canvas.style.width;
283        clone.style.height = canvas.style.height;
284
285        if (copyGfxData)
286        {
287            let context = clone.getContext("2d");
288            context.drawImage(canvas, 0, 0);
289        }
290
291        return clone;
292    }
293
294    function getPageInfo(page, $flipbook)
295    {
296        let viewport = page.getViewport({ scale: 1 });
297        let isLandscape = viewport.width > viewport.height;
298        let pixelRatio = Math.max(window.devicePixelRatio || 1, 2);
299        let desiredWidth = (isMobile
300            ? $flipbook.width()
301            : $flipbook.width() / 2);
302        let scale = isLandscape
303            ? desiredWidth / (viewport.width / 2)
304            : desiredWidth / viewport.width;
305        let scaledViewport = page.getViewport({ scale: scale });
306        let transform = [pixelRatio, 0, 0, pixelRatio, 0, 0];
307
308        return {
309            canvasHtmlWidth: Math.floor(scaledViewport.width * pixelRatio),
310            canvasHtmlHeight: Math.floor(scaledViewport.height * pixelRatio),
311            canvasCssWidth: Math.floor(scaledViewport.width),
312            canvasCssHeight: Math.floor(scaledViewport.height),
313            transform: transform,
314            isLandscape: isLandscape,
315            scaledViewport: scaledViewport,
316            pixelRatio: pixelRatio
317        };
318    }
319
320    function setupFlipBook($brochure, pdf)
321    {
322        let $flipbook = $brochure.find(".flipbook");
323        let $slider = $brochure.find(".page-slider");
324        let $pages = $brochure.find(".cpage");
325        let $firstPage = $pages.first();
326
327        $flipbook.turn({
328            width: $firstPage.width() * 2,
329            height: $firstPage.height(),
330            autoCenter: true,
331            display: isMobile
332                ? "single"
333                : "double"
334        });
335
336        $slider.attr("max", $pages.length);
337
338        setupEvents(pdf, $brochure, $flipbook, $slider);
339
340        $brochure.find(".loading-info").text(`с ${pdf.numPages} страници. Това може да отнеме няколко секунди...`);
341
342        renderAroundPage(pdf, 1, $flipbook);
343    }
344
345    function setupEvents(pdf, $brochure, $flipbook, $slider)
346    {
347        let $wrapper = $brochure.find(".book-wrapper");
348        //let $sliderLabel = $brochure.find(".page-slider-label");
349        let isDragging = false;
350        let startX, startY;
351        let scrollLeft, scrollTop;
352
353        // Shutdown their touch events so they don't interfere with ours.
354        $flipbook.off("touchstart touchmove touchend");
355        $brochure.find(".next-button").on("click", function ()
356        {
357            $flipbook.turn("next");
358        });
359        $brochure.find(".previous-button").on("click", function ()
360        {
361            $flipbook.turn("previous");
362        });
363        $flipbook.bind("turned", function (event, page)
364        {
365            renderAroundPage(pdf, page, $flipbook);
366            $slider.val(page);
367            //$sliderLabel.text(page);
368        });
369        $slider.on("input", function ()
370        {
371            let pageNum = parseInt($slider.val());
372            $flipbook.turn("page", pageNum);
373            renderAroundPage(pdf, pageNum, $flipbook);
374            //$sliderLabel.text(pageNum);
375        });
376        $brochure.find(".zoom-button").on("click", function ()
377        {
378            applyZoom($brochure, $wrapper, $flipbook);
379        });
380        // start drag
381        $wrapper.on("mousedown touchstart", function (e)
382        {
383            if (!isZoomed($flipbook))
384                return;
385
386            e.preventDefault();
387            isDragging = true;
388            $wrapper.css("cursor", "grabbing");
389
390            let coords = getEventCoords(e);
391            startX = coords.x;
392            startY = coords.y;
393
394            scrollLeft = $wrapper.scrollLeft();
395            scrollTop = $wrapper.scrollTop();
396        });
397        // pan
398        $wrapper.on("mousemove touchmove", function (e)
399        {
400            if (!isDragging)
401                return;
402
403            e.preventDefault();
404
405            let coords = getEventCoords(e);
406            let deltaX = coords.x - startX;
407            let deltaY = coords.y - startY;
408
409            $wrapper.scrollLeft(scrollLeft - deltaX);
410            $wrapper.scrollTop(scrollTop - deltaY);
411        });
412        // stop drag
413        $wrapper.on("mouseup touchend touchcancel", function ()
414        {
415            if (!isDragging)
416                return;
417
418            isDragging = false;
419            $wrapper.css("cursor", "default");
420        });
421        $wrapper.on("touchend", function (e) {
422            let now = new Date().getTime();
423
424            if (now - lastTapTime < doubleTapDelay) {
425                // double tap detected
426                // get tab coords to zoom to
427                let coords = getEventCoords(e);
428                let offset = $wrapper.offset();
429                let x = coords.x - offset.left;
430                let y = coords.y - offset.top;
431                let oldScale;
432                let newScale;
433                if (!isZoomed($flipbook))
434                {
435                    oldScale = 1;
436                    newScale = zoomLevel;
437                } else {
438                    oldScale = zoomLevel;
439                    newScale = 1;
440                }
441                applyZoom($brochure, $wrapper, $flipbook);
442                $wrapper.scrollLeft(
443                    ($wrapper.scrollLeft() + x) * newScale / oldScale - x
444                );
445                $wrapper.scrollTop(
446                    ($wrapper.scrollTop() + y) * newScale / oldScale - y
447                );
448                e.preventDefault();
449            } else {
450                lastTapTime = now;
451            }
452        });
453    }
454
455    function getEventCoords(e)
456    {
457        if (e.type.startsWith("touch"))
458        {
459            return {
460                x: e.originalEvent.touches.length > 0
461                    ? e.originalEvent.touches[0].pageX
462                    : e.originalEvent.changedTouches[0].pageX,
463                y: e.originalEvent.touches.length > 0
464                    ? e.originalEvent.touches[0].pageY
465                    : e.originalEvent.changedTouches[0].pageY,
466            };
467        }
468        else
469        {
470            return {
471                x: e.pageX,
472                y: e.pageY
473            };
474        }
475    }
476
477    function applyZoom($brochure, $wrapper, $flipbook)
478    {
479        if (!isZoomed($flipbook))
480        {
481            $flipbook.parent().css("transform", `scale(${zoomLevel})`);
482            $brochure.find(".zoom-button").find(".fa").removeClass("fa-search-plus").addClass("fa-search-minus");
483            $wrapper.css("overflow", "auto");
484        }
485        else
486        {
487            $flipbook.parent().css("transform", "none");
488            $brochure.find(".zoom-button").find(".fa").removeClass("fa-search-minus").addClass("fa-search-plus");
489            $wrapper.css("overflow", "hidden");
490        }
491    }
492
493    function isZoomed($flipbook)
494    {
495        return $flipbook.parent().css("transform") !== "none";
496    }
497
498    function renderAroundPage(pdf, pageNum, $flipbook)
499    {
500        let pageClasses = [
501            ".p" + (pageNum - 3),
502            ".p" + (pageNum - 2),
503            ".p" + (pageNum - 1),
504            ".p" + (pageNum),
505            ".p" + (pageNum + 1),
506            ".p" + (pageNum + 2),
507            ".p" + (pageNum + 3)
508        ];
509        let $pagesToRender = $flipbook.find(pageClasses.join(","));
510        let chain = Promise.resolve();
511
512        $pagesToRender.each(function (index, page)
513        {
514            chain = chain.then(() => renderPage(pdf, $(page), $flipbook));
515        });
516    }
517
518    function renderPage(pdf, $page, $flipbook)
519    {
520        if ($page.is(".blank"))
521            return;
522
523        let $canvas = $page.find("canvas");
524        let originalPageIndex = $canvas.first().data("original");
525        let $canvases = $flipbook.find(`.original-${originalPageIndex}`);
526
527        if ($canvases.length === $canvases.find(".rendered").length)
528            return;
529
530        return pdf.getPage(originalPageIndex).then(page =>
531        {
532            let pageInfo = getPageInfo(page, $flipbook);
533            let tasks = [];
534
535            $canvases.each(function (index, canvas)
536            {
537                let $canvas = $(canvas);
538                $canvas.parent().css("height", pageInfo.canvasCssHeight + "px");
539
540                if ($canvas.is(".rendered") || $canvas.is(".rendering"))
541                    return; // continue
542
543                $canvas.addClass("rendering");
544                let context = canvas.getContext("2d");
545                let renderObject =
546                    {
547                        canvasContext: context,
548                        viewport: pageInfo.scaledViewport,
549                        transform: pageInfo.transform
550                    };
551                let task = page
552                    .render(renderObject)
553                    .promise
554                    .then(() => $canvas.removeClass("rendering").addClass("rendered"));
555                tasks.push(task);
556            })
557
558            return Promise.all(tasks);
559        });
560    }
561});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.