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.