PageSourceSearch

https://korzh.com/assets/js/index.js

js korzh.com collected 2026-10-03 20:39:11 UTC 28,291 bytes, 615 lines download raw bytes

1document.addEventListener("DOMContentLoaded", () => {
2
3    const reCaptchaSiteKey = "6LeNDYMsAAAAALRDQNC4MOiETC9uD8gIj8AdRNjd";
4
5    // ==========================================
6    // EasyQuery Logic (from easyquery.html)
7    // ==========================================
8
9    const eqAction = window.location.href.split("#")[1];
10
11    const mobileDialogStyle = document.createElement("style");
12    mobileDialogStyle.innerHTML = `
13        @media screen and (max-width: 768px) {
14            .eq-mobile-fullscreen {
15                width: 100vw !important;
16                height: 100vh !important;
17                max-width: 100vw !important;
18                max-height: 100vh !important;
19                margin: 0 !important;
20                top: 0 !important;
21                left: 0 !important;
22                transform: none !important;
23                border-radius: 0 !important;
24                overflow-x: hidden !important;
25                overflow-y: auto !important;
26                border: none !important;
27            }
28        }
29    `;
30    document.head.appendChild(mobileDialogStyle);
31
32    if (eqAction === 'get-trial') {
33        getTrial();
34    } else {
35        const editions = ['asp-core-edition', 'asp-net-edition', 'winforms-edition', 'silverlight-edition', 'wpf-edition', 'delphi-edition'];
36        if (editions.includes(eqAction)) {
37            const licensingEl = document.getElementById("licensing");
38            if (licensingEl) {
39                licensingEl.scrollIntoView();
40            }
41            if (typeof $ !== 'undefined' && typeof Metro !== 'undefined') {
42                Metro.getPlugin("#license-price", 'tabs').open(editions.indexOf(eqAction) + 1);
43            }
44        }
45    }
46
47    // Attach click handler to "Get Trial" button
48    const getTrialBtn = document.getElementById("btn-get-trial");
49    if (getTrialBtn) {
50        getTrialBtn.addEventListener("click", (e) => {
51            e.preventDefault();
52            getTrial();
53        });
54    }
55
56    if (typeof $ !== 'undefined') {
57        $("body").on("click", "a[slow-loading]", function () {
58            showLoadIndicator();
59        });
60    }
61
62    function trialChangeOptions(val) {
63        if (typeof $ !== 'undefined') {
64            $(".trial-sub-options > form").hide();
65            $("#trial-option-" + val).show();
66        }
67    }
68
69    function getTrial() {
70        if (typeof Metro === 'undefined') return;
71
72        if (window.KorzhAnalytics) {
73            // the family tag: which EasyQuery product it is follows from the app type the visitor picks
74            window.KorzhAnalytics.trackEvent('trial.dialog.opened', { ptag: 'EQN' });
75        }
76
77        Metro.dialog.create({
78            title: "<span class='mif-magic-wand fg-primary pr-2'></span> Get EasyQuery Trial",
79            content: `
80            <h4 class="text-bold">Select your application type</h4>
81            <div class="mb-4 text-leader2" style="font-size: 1.1rem; line-height: 1.6;">
82                EasyQuery framework can be used on different platforms and with different types of applications. So, to give you the most relevant installation instructions we need to know more about your project first.
83            </div>
84            <select id="select-apptype" class="mt-4 mb-4" data-role="select" data-filter="false">
85                <option value="asp-net-core-razor">ASP.NET Core + MVC or Razor pages</option>
86                <option value="asp-net-core-spa">ASP.NET Core + SPA Frontend (Angular, Razor, Vue, etc)</option>
87                <option value="asp-net-4-mvc">ASP.NET 4 MVC</option>
88                <option value="asp-net-4-webforms">ASP.NET 4 WebForms</option>
89                <option value="net-winforms">.NET (Core) Windows Forms</option>
90                <option value="net-wpf">.NET (Core) WPF</option>
91                <option value="webapp-other">Web application on other platform (Java, Node.js, PHP, Python, etc) </option>
92                <option value="other">Any other type of application</option>
93            </select>
94            <div class="trial-sub-options">
95                <form id="trial-option-asp-net-core-razor" class="trial-option p-4 border bd-default mb-4">
96                    <input type="radio" data-role="radio" name="viewEngine" data-caption="MVC" value="mvc" checked/>
97                    <input type="radio" data-role="radio" name="viewEngine" data-caption="Razor Pages" value="razor-pages"/>
98                </form>
99                <form id="trial-option-asp-net-core-spa" name="trial-option-asp-net-core-spa" class="trial-option p-4 border bd-default mb-4">
100                    <input type="radio" data-role="radio" name="frontend" data-caption="Angular" value="angular"/>
101                    <input type="radio" data-role="radio" name="frontend" data-caption="React" value="react"/>
102                    <input type="radio" data-role="radio" name="frontend" data-caption="Vue" value="vue"/>
103                    <input type="radio" data-role="radio" name="frontend" data-caption="Other" value="other" checked/>
104                </form>
105                <form id="trial-option-asp-net-4-mvc" class="trial-option"></form>
106                <form id="trial-option-asp-net-4-webforms" class="trial-option"></form>
107                <form id="trial-option-net-winforms" class="trial-option"></form>
108                <form id="trial-option-net-wpf" class="trial-option"></form>
109                <form id="trial-option-webapp-other" class="trial-option p-4 border bd-default mb-4">
110                    <input type="text" data-role="input" name="backend" data-label="Please, specify:"/>
111                </form>
112                <form id="trial-option-other" class="trial-option p-4 border bd-default mb-4">
113                    <input type="text" data-role="input" name="apptype" data-label="Please, specify:"/>
114                </form>
115            </div>
116
117            <h4 class="text-bold mt-4">Enter your email <span class="text-muted text-normal">(optional)</span></h4>
118            <div class="mb-4 text-leader2" style="font-size: 1.1rem; line-height: 1.6;">
119                We will provide the detailed instructions on how to get the things worked.
120            </div>
121            <input id="trial-email" type="email" placeholder="[email protected]" data-role="input" data-prepend="<span class='mif-envelop'></span>" name="email"/>
122        `,
123            clsDialog: "eq-mobile-fullscreen",
124            closeButton: true,
125            defaultActions: false,
126            overlay: true,
127            overlayClickClose: false,
128            customButtons: [
129                {
130                    id: "get-trial-btn-in-dialog",
131                    text: "Get Trial",
132                    cls: "info rounded px-6 js-get-trial-action-btn",
133                    onclick: async function () {
134                        const app = document.getElementById("select-apptype").value;
135                        const email = document.getElementById("trial-email").value;
136
137                        closeDialogs();
138
139                        setTimeout(() => {
140                            if (typeof Metro !== 'undefined') {
141                                Metro.dialog.create({
142                                    title: "",
143                                    clsDialog: "eq-processing-dialog shadow-large eq-mobile-fullscreen",
144                                    content: `
145                                        <div style="position: relative;">
146                                            <!-- Ghost container to pre-stretch the dialog to exact destination size -->
147                                            <div style="visibility: hidden; pointer-events: none; opacity: 0; padding-top: 2rem; padding-bottom: 1rem;">
148                                                <div class="mx-auto" style="max-width: 400px; padding: 1rem; margin-bottom: 1.5rem; border: none;">
149                                                    <h3 class="text-bold">Your download will start shortly in <span class="fg-green text-leader">5</span> seconds...</h3>
150                                                    <h3 class="mt-2 text-muted">If your download does not start automatically, click here.</h3>
151                                                </div>
152                                            </div>
153
154                                            <div id="eq-processing-state" class="text-center" style="position: absolute; top:0; left:0; right:0; bottom:0; display:flex; flex-direction:column; justify-content:center; align-items:center; z-index: 2;">
155                                                <div class="mb-4">
156                                                    <div data-role="activity" data-type="cycle" data-style="color" style="margin: 0 auto; transform: scale(1.5);"></div>
157                                                </div>
158                                                <h3 class="text-light mt-4 mb-0">Assembling a sample project for you...</h3>
159                                            </div>
160
161                                            <div id="eq-result-state" style="position: absolute; top:0; left:0; right:0; bottom:0; z-index: 3; display:flex; flex-direction:column; justify-content:center; opacity: 0; pointer-events: none; visibility: hidden;"></div>
162                                        </div>
163                                    `,
164                                    closeButton: true,
165                                    defaultActions: false,
166                                    overlay: true,
167                                    overlayClickClose: false,
168                                    customButtons: [
169                                        {
170                                            text: "Close",
171                                            cls: "js-dialog-close flat fg-gray",
172                                            onclick: function () {
173                                            }
174                                        }
175                                    ],
176                                    onOpen: function () {
177                                        if (typeof grecaptcha !== 'undefined') {
178                                            grecaptcha.ready(function () {
179                                                grecaptcha
180                                                    .execute(reCaptchaSiteKey, { action: "eq_trial" })
181                                                    .then(function (token) {
182                                                        return processTrialRequest
182(app, email, token);
183                                                    })
184                                                    .catch(function (error) {
185                                                        console.error("reCAPTCHA error:", error);
186                                                        closeDialogs();
187                                                    });
188                                            });
189                                        } else {
190                                            console.warn("grecaptcha is not defined");
191                                            processTrialRequest(app, email, "dummy_token");
192                                        }
193                                    }
194                                });
195                            }
196                        }, 50);
197                    }
198                },
199                {
200                    text: "Cancel",
201                    cls: "js-dialog-close flat fg-gray",
202                    onclick: function () {
203                    }
204                }
205            ],
206            onOpen: function () {
207                const selectApptype = document.getElementById("select-apptype");
208                if (selectApptype) {
209                    selectApptype.addEventListener("change", function () {
210                        trialChangeOptions(this.value);
211                    });
212                    trialChangeOptions(selectApptype.value);
213                }
214            }
215        });
216    }
217
218    function closeDialogs() {
219        if (typeof document !== 'undefined') {
220            const existingDialogs = document.querySelectorAll('.dialog');
221            existingDialogs.forEach(d => {
222                if (typeof Metro !== 'undefined') {
223                    const instance = Metro.getPlugin(d, 'dialog');
224                    if (instance && typeof instance.close === 'function') {
225                        instance.close();
226                        return;
227                    }
228                }
229                d.remove();
230                const overlay = document.querySelector('.overlay');
231                if (overlay) overlay.remove();
232            });
233        }
234    }
235
236    // The samples, by the Uri of their download item on account.korzh.com and the file behind it.
237    // The site links through /d/{uri}, which records who downloads a sample and then redirects to
238    // the file; the file itself stays the fallback when the analytics script is not there.
239    const SAMPLES = {
240        'aspnetcore-razor-mvc': 'https://cdn.korzh.com/dot-net-samples/AspNetCore-Razor-Mvc.zip',
241        'aspnetcore-angular': 'https://cdn.korzh.com/dot-net-samples/AspNetCore-Angular.zip',
242        'aspnetcore-react': 'https://cdn.korzh.com/dot-net-samples/AspNetCore-React.zip',
243        'aspnetcore-vue': 'https://cdn.korzh.com/dot-net-samples/AspNetCore-Vue3.zip',
244        'aspnet4-mvc': 'https://cdn.korzh.com/dot-net-samples/AspNet4-Mvc.zip',
245        'aspnet4-webforms': 'https://cdn.korzh.com/dot-net-samples/AspNet4-WebForms.zip',
246        'winforms': 'https://cdn.korzh.com/dot-net-samples/WinForms.zip',
247        'wpf': 'https://cdn.korzh.com/dot-net-samples/Wpf.zip',
248        'ersk-community': 'https://cdn.korzh.com/download/ersk_community.zip'
249    };
250
251    function sampleUrl(name) {
252        const fileUrl = SAMPLES[name];
253        if (!fileUrl) return null;
254
255        return window.KorzhAnalytics
256            ? window.KorzhAnalytics.downloadUrl('samples/' + name, fileUrl)
257            : fileUrl;
258    }
259
260    async function processTrialRequest(apptype, email, token) {
261        const url = `https://account.korzh.com/api/account/register`;
262
263        let nextHref = 'https://korzh.com/easyquery/docs/getting-started';
264        let downloadUrl = null;
265        switch (apptype) {
266            case 'asp-net-core-razor':
267                downloadUrl = sampleUrl('aspnetcore-razor-mvc');
268                break;
269            case 'asp-net-core-spa':
270                const form = document.forms["trial-option-asp-net-core-spa"];
271                if (form && form.elements.frontend) {
272                    const spaType = form.elements.frontend.value;
273                    switch (spaType) {
274                        case 'angular':
275                            downloadUrl = sampleUrl('aspnetcore-angular');
276                            break;
277                        case 'react':
278                            downloadUrl = sampleUrl('aspnetcore-react');
279                            break;
280                        case 'vue':
281                            downloadUrl = sampleUrl('aspnetcore-vue');
282                            break;
283                        case 'other':
284                            break;
285                    }
286                }
287                break;
288            case 'asp-net-4-mvc':
289                downloadUrl = sampleUrl('aspnet4-mvc');
290                break;
291            case 'asp-net-4-webforms':
292                downloadUrl = sampleUrl('aspnet4-webforms');
293                break;
294            case 'net-winforms':
295                downloadUrl = sampleUrl('winforms');
296                break;
297            case 'net-wpf':
298                downloadUrl = sampleUrl('wpf');
299                break;
300            case 'webapp-other':
301            case 'other':
302                break;
303        }
304
305        const trialData = {
306            email,
307            captchaToken: token,
308            data: window.KorzhAnalytics
309                ? window.KorzhAnalytics.requestData({ intent: "get-trial", apptype })
310                : { intent: "get-trial", apptype }
311        };
312
313        try {
314            const response = await fetch(url, {
315                method: 'POST',
316                headers: {
317                    'Content-Type': 'application/json'
318                },
319                body: JSON.stringify(trialData)
320            });
321
322            if (!response.ok) {
323                console.error("[EQ] Server error: " + response.status);
324                closeDialogs();
325                if (typeof Metro !== 'undefined' && Metro.toast && typeof Metro.toast.create === 'function') {
326                    Metro.toast.create("Verification failed. Please try again.", {
327                        clsToast: "alert",
328                        position: "top"
329                    });
330                } else {
331                    alert("Verification failed. Please try again.");
332                }
333                return;
334            }
335        } catch (error) {
336            console.error("[EQ] API error:", error);
337        }
338
339        let dialogContent = '';
340
341        if (downloadUrl) {
342            dialogContent += `
343                <div class="mx-auto">
344                    <div class="p-4 bd-default mb-6">
345                        <h3 id="download-countdown-text" class="text-bold text-center">
346                            The sample project download will start in <span id="download-countdown" class="fg-green text-leader">5</span> seconds...
347                        </h3>
348                        <h4  class="mt-2 text-muted text-center" style="line-height: 1.5;">
349                            <span id="manual-download-text">If the download does not start automatically,</span> <a href="${downloadUrl}" id="manual-download-link" class="fg-primary">click here</a>.
350                        </h4>
351                    </div>
352                </div>
353            `;
354        }
355
356        dialogContent += `
357                <div class="mt-2 text-left">
358                    <a href="https://korzh.com/easyquery/docs/getting-started/from-sample-to-your-own-project" target="_blank" class="fg-primary">
359                        <span class="mif-books pr-2"></span> Getting started with the sample
360                    </a>
361                </div>
362                <div class="mt-2 text-left">
363                    <a href="${nextHref}" target="_blank" class="fg-primary">
364                        <span class="mif-books pr-2"></span> EasyQuery documentation
365                    </a>
366                </div>
367        `;
368
369        const processingState = document.getElementById("eq-processing-state");
370        const resultState = document.getElementById("eq-result-state");
371        let downloadInterval = null;
372
373        if (processingState) processingState.style.display = "none";
374
375        if (resultState) {
376            resultState.innerHTML = dialogContent;
377            resultState.style.opacity = "1";
378            resultState.style.pointerEvents = "auto";
379            resultState.style.visibility = "visible";
380
381            if (downloadUrl) {
382                let count = 5;
383
384                const manualLink = document.getElementById('manual-download-link');
385                if (manualLink) {
386                    manualLink.addEventListener('click', () => {
387                        if (downloadInterval) clearInterval(downloadInterval);
388                        const countdownText = document.getElementById('download-countdown-text');
389                        if (countdownText) countdownText.style.display = 'none';
390                    });
391                }
392
393                downloadInterval = setInterval(() => {
394                    if (!document.getElementById('eq-result-state')) {
395                        clearInterval(downloadInterval);
396                        return;
397                    }
398
399                    count--;
400                    const counterEl = document.getElementById('download-countdown');
401                    if (counterEl) counterEl.innerText = count;
402                    if (count <= 0) {
403                        clearInterval(downloadInterval);
404                        const countdownText = document.getElementById('download-countdown-text');
405                        if (countdownText) countdownText.style.display = 'none';
406                        const manualDownloadText = document.getElementById('manual-download-text');
407                        if (manualDownloadText) manualDownloadText.innerText = 'The sample project download should have started automatically.\nIf not,';
408                        window.location.href = downloadUrl;
409                    }
410                }, 1000);
411            }
412        }
413    }
414
415    window.addEventListener("pageshow", function (event) {
416        if (event.persisted) {
417            window.location.reload();
418        }
419    });
420
421    function showLoadIndicator() {
422        if (typeof Metro !== 'undefined' && Metro.activity) {
423            Metro.activity.open({
424                type: "cycle",
425                overlayColor: "#fff",
426                overlayAlpha: .6,
427                text: '<div class="mt-2 text-small">Loading...</div>'
428            });
429        }
430    }
431
432
433    // ==========================================
434    // ERSK Logic (from easy-report-starter-kit.html)
435    // ==========================================
436
437    let getCommunityDialog = null;
438
439    const btnGetErskCommunity = document.getElementsByClassName("get-ersk-community-btn");
440    if (btnGetErskCommunity.length > 0) {
441        Array.from(btnGetErskCommunity).forEach(btn => {
442            btn.addEventListener("click", getERSKCommunity);
443        });
444    }
445
446    function getERSKCommunity() {
447        if (typeof Metro === 'undefined') return;
448
449        if (window.KorzhAnalytics) {
450            window.KorzhAnalytics.trackEvent('perk.dialog.opened', { ptag: 'ERSK' });
451        }
452
453        getCommunityDialog = Metro.dialog.create({
454            title: "Get ERSK Community",
455            content: `
456              <div>
457                  <p>When you download ERSK Community Edition, you'll get access to:</p>
458                  <ul class="unstyled-list">
459                      <li><span class="mif-checkmark fg-green mr-1"></span> Report Management</li>
460                      <li><span class="mif-checkmark fg-green mr-1"></span> User Management</li>
461                      <li><span class="mif-checkmark fg-green mr-1"></span> Report Sharing</li>
462                      <li><span class="mif-checkmark fg-green mr-1"></span> Data visualization with tables</li>
463                  </ul>
464                  <p class="mt-4">You can optionally provide your email to receive setup instructions and updates.</p>
465              </div>
466              <form id="ersk-community-edition-form">
467                  <div class="ersk-community-edition box">
468                      <label>Email (optional):</label>
469                      <input
470                          type="email"
471                          name="email"
472                          id="ersk-email-input"
473                          data-role="input"
474                          data-validate="email"
475                          placeholder="Enter your email..."
476                      >
477                  </div>
478                  <div class="form-actions mt-4 d-flex flex-justify-right gap-2">
479                      <button type="button" class="button js-dialog-close">Cancel</button>
480                      <button
481                          type="button"
482                          id="ersk-download-btn"
483                          class="button info"
484                      >Download ERSK Community</button>
485                  </div>
486              </form>
487          `,
488            clsDialog: "get-community-dialog shadow-large",
489            closeButton: true,
490            defaultActions: false,
491            customButtons: [],
492            onOpen: function () {
493                const downloadBtn = document.getElementById("ersk-download-btn");
494                if (downloadBtn) {
495                    downloadBtn.addEventListener("click", handleERSKDownload);
496                }
497            }
498        });
499    }
500
501    function setErskButtonState(state) {
502        const button = document.getElementById("ersk-download-btn");
503        if (!button) return;
504        if (state === "loading") {
505            button.disabled = true;
506            button.textContent = "Processing...";
507        } else {
508            button.disabled = false;
509            button.textContent = "Download ERSK Community";
510        }
511    }
512
513    function handleERSKDownload() {
514        const emailInput = document.getElementById("ersk-email-input");
515        const email = emailInput?.value.trim() || "";
516        const downloadUrl = sampleUrl('ersk-community');
517
518        // Перевірка валідності email
519        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
520        if (email && !emailRegex.test(email)) {
521            if (typeof Metro !== 'undefined' && Metro.toast) {
522                Metro.toast.create("Please enter a valid email address", null, 3000, "alert");
523            }
524            return;
525        }
526
527        const btn = document.getElementById("ersk-download-btn");
528        const originalText = btn ? btn.textContent : "Download ERSK Community";
529
530        // Блокуємо кнопку під час обробки
531        setErskButtonState("loading");
532
533        // Викликаємо reCAPTCHA v3
534        if (typeof grecaptcha !== 'undefined') {
535            grecaptcha.ready(function () {
536                grecaptcha
537                    .execute(reCaptchaSiteKey, { action: "download_ersk" })
538                    .then(function (token) {
539                        console.log("OK");
540                        return submitERSKData(email, token, downloadUrl);
541                    })
542                    .catch(function (error) {
543                        console.error("reCAPTCHA error:", error);
544                        if (btn) {
545                            btn.disabled = false;
546                            btn.textContent = originalText;
547                        }
548                    });
549            });
550        } else {
551            console.warn("grecaptcha is not defined");
552            submitERSKData(email, "dummy_token", downloadUrl);
553        }
554    }
555
556    async function submitERSKData(email, recaptchaToken, downloadUrl) {
557        const nextUrl = "https://korzh.com/easy-report-starter-kit/docs/setup-first-launch";
558        const apiAuthoring = "https://account.korzh.com/api/account/register";
559
560        const erskData = {
561            intent: "get-perk", //register | get-trial | get-perk
562            ptag: "ERSK", //The unique identifier of the product the user interested in.
563            apptype: "ersk-community", //The type of the application the user going to use.
564        };
565
566        const data = {
567            email,
568            captchaToken: recaptchaToken, //The token of the Google CAPTCHA
569            data: window.KorzhAnalytics ? window.KorzhAnalytics.requestData(erskData) : erskData,
570        };
571
572        try {
573            const response = await fetch(apiAuthoring, {
574                method: "POST",
575                headers: {
576                    "Content-Type": "application/json",
577                },
578                body: JSON.stringify(data),
579            });
580
581            if (!response.ok) {
582                console.error("[ERSK] Server error: " + response.status);
583                return;
584            }
585
586            const result = await response.json();
587
588            if (result?.status === 21) {
589                // user already registered
590            } else {
591                if (typeof Metro !== 'undefined' && Metro.toast) {
592                    Metro.toast.create("Your account has ben created! Thank you for using our products!", {
593                        clsToast: "success",
594                        position: "top",
595                    });
596                }
597            }
598
599            if (getCommunityDialog && typeof Metro !== 'undefined') {
600                Metro.dialog.close(getCommunityDialog);
601            }
602        } catch (error) {
603            console.error("[ERSK] API error:", error);
604        }
605
606        // Відкриваємо завантаження в новому вікні
607        window.open(downloadUrl, "_blank");
608        setTimeout(() => {
609            window.location.href = nextUrl;
610        }, 100);
611
612        return null;
613    }
614
615});

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.