1$(document).ready(function() { 2 window.AppLocale = $("body").data("current-locale") || "ru"; 3 $("img[data-fallback-src]").each(function() { 4 var img = this; 5 var fallbackSrc = img.getAttribute("data-fallback-src"); 6 if (!fallbackSrc) { 7 return; 8 } 9 img.addEventListener("error", function() { 10 if (img.src.indexOf(fallbackSrc) !== -1) { 11 return; 12 } 13 img.src = fallbackSrc; 14 }); 15 if (img.complete && img.naturalWidth === 0) { 16 img.src = fallbackSrc; 17 } 18 }); 19 try { 20 if (window.Intl && Intl.DateTimeFormat) { 21 var tz = Intl.DateTimeFormat().resolvedOptions().timeZone; 22 if (tz && typeof tz === "string") { 23 document.cookie = "time_zone=" + encodeURIComponent(tz) + "; path=/; max-age=31536000; samesite=lax"; 24 } 25 } 26 } catch (e) {} 27 $(document).on("click", ".js-locale-switch-link", function() { 28 var locale = ($(this).data("locale") || "").toString().toLowerCase(); 29 if (!locale || [ "ru", "ua", "en" ].indexOf(locale) === -1) { 30 return; 31 } 32 var endpoint = "/locale/preference/" + encodeURIComponent(locale); 33 try { 34 if (navigator.sendBeacon) { 35 var blob = new Blob([ "1" ], { 36 type: "text/plain;charset=UTF-8" 37 }); 38 navigator.sendBeacon(endpoint, blob); 39 return; 40 } 41 } catch (e) {} 42 try { 43 fetch(endpoint, { 44 method: "POST", 45 credentials: "same-origin", 46 keepalive: true 47 }); 48 } catch (e) {} 49 }); 50 if ($(window).width() > 992) { 51 (function() { 52 var a = document.querySelector("#fixblock"), b = null, K = null, Z = 0, P = 0, N = 20; 53 if (a != null) { 54 window.addEventListener("scroll", Ascroll, false); 55 document.body.addEventListener("scroll", Ascroll, false); 56 } 57 function Ascroll() { 58 var Ra = a.getBoundingClientRect(), R1bottom = document.querySelector("#main-content-block").getBoundingClientRect().bottom - 40; 59 if (Ra.bottom < R1bottom) { 60 if (b == null) { 61 var Sa = getComputedStyle(a, ""), s = ""; 62 for (var i = 0; i < Sa.length; i++) { 63 if (Sa[i].indexOf("overflow") == 0 || Sa[i].indexOf("padding") == 0 || Sa[i].indexOf("border") == 0 || Sa[i].indexOf("outline") == 0 || Sa[i].indexOf("box-shadow") == 0 || Sa[i].indexOf("background") == 0) { 64 s += Sa[i] + ": " + Sa.getPropertyValue(Sa[i]) + "; "; 65 } 66 } 67 b = document.createElement("div"); 68 b.className = "stop"; 69 b.style.cssText = s + " box-sizing: border-box; width: " + a.offsetWidth + "px;"; 70 a.insertBefore(b, a.firstChild); 71 var l = a.childNodes.length; 72 for (var i = 1; i < l; i++) { 73 b.appendChild(a.childNodes[1]); 74 } 75 a.style.height = b.getBoundingClientRect().height + "px"; 76 a.style.padding = "0"; 77 a.style.border = "0"; 78 } 79 var Rb = b.getBoundingClientRect(), Rh = Ra.top + Rb.height, W = window.innerHeight, R1 = Math.round(Rh - R1bottom), R2 = Math.round(Rh - W); 80 if (Rb.height > W) { 81 if (Ra.top < K) { 82 if (R2 + N > R1) { 83 if (Rb.bottom - W + N - 20 <= 0) { 84 b.className = "stickyy"; 85 b.style.top = W - Rb.height - N + "px"; 86 Z = N + Ra.top + Rb.height - W; 87 } else { 88 b.className = "stop"; 89 b.style.top = -Z + "px"; 90 } 91 } else { 92 b.className = "stop"; 93 b.style.top = -R1 + "px"; 94 Z = R1; 95 } 96 } else { 97 if (Ra.top - P < 0) { 98 if (Rb.top - P >= 0) { 99 b.className = "stickyy"; 100 b.style.top = P + "px"; 101 Z = Ra.top - P; 102 } else { 103 b.className = "stop"; 104 b.style.top = -Z + "px"; 105 } 106 } else { 107 b.className = ""; 108 b.style.top = ""; 109 Z = 0; 110 } 111 } 112 K = Ra.top; 113 } else { 114 if (Ra.top - P <= 0) { 115 if (Ra.top - P <= R1) { 116 b.className = "stop"; 117 b.style.top = -R1 + "px"; 118 } else { 119 b.className = "stickyy"; 120 b.style.top = P + "px"; 121 } 122 } else { 123 b.className = ""; 124 b.style.top = ""; 125 } 126 } 127 window.addEventListener("resize", function() { 128 a.children[0].style.width = getComputedStyle(a, "").width; 129 }, false); 130 } 131 } 132 })(); 133 } 134 window.AppUi = window.AppUi || {}; 135 window.AppUi.initTooltips = function(root) { 136 if (!window.bootstrap || !window.bootstrap.Tooltip) { 137 return; 138 } 139 var scope = root && root.querySelectorAll ? root : document; 140 scope.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function(el) { 141 window.bootstrap.Tooltip.getOrCreateInstance(el); 142 }); 143 }; 144 window.AppUi.showModal = function(modal) { 145 var el = typeof modal === "string" ? document.querySelector(modal) : modal; 146 if (!el || !window.bootstrap || !window.bootstrap.Modal) { 147 return; 148 } 149 window.bootstrap.Modal.getOrCreateInstance(el).show(); 150 }; 151 window.AppUi.hideModal = function(modal) { 152 var el = typeof modal === "string" ? document.querySelector(modal) : modal; 153 if (!el || !window.bootstrap || !window.bootstrap.Modal) { 154 return; 155 } 156 window.bootstrap.Modal.getOrCreateInstance(el).hide(); 157 }; 158 $(function() { 159 window.AppUi.initTooltips(document); 160 }); 161 $(document).on("click", ".js-password-toggle", function() { 162 var button = this; 163 var targetId = button.getAttribute("aria-controls"); 164 var input = targetId ? document.getElementById(targetId) : null; 165 if (!input) {
166 var field = button.closest ? button.closest(".auth-password-field") : null; 167 input = field ? field.querySelector("input") : null; 168 } 169 if (!input) { 170 return; 171 } 172 var shouldShow = input.type === "password"; 173 input.type = shouldShow ? "text" : "password"; 174 var label = shouldShow ? button.getAttribute("data-hide-label") : button.getAttribute("data-show-label"); 175 if (label) { 176 button.setAttribute("aria-label", label); 177 } 178 var icon = button.querySelector(".fa-regular, .fa-solid"); 179 if (icon) { 180 icon.classList.toggle("fa-eye", !shouldShow); 181 icon.classList.toggle("fa-eye-slash", shouldShow); 182 } 183 }); 184 function authMessages(form) { 185 var d = form.dataset || {}; 186 return { 187 required: d.msgRequired || "ÐаполниÑе ÑÑо поле", 188 email: d.msgEmail || "ÐведиÑе коÑÑекÑнÑй email", 189 minlength: d.msgMinlength || "СлиÑком коÑоÑкое знаÑение" 190 }; 191 } 192 function authFieldError(input, msgs) { 193 var v = input.validity; 194 if (!v || v.valid) { 195 return ""; 196 } 197 if (v.valueMissing) { 198 return msgs.required; 199 } 200 if (v.typeMismatch) { 201 return msgs.email; 202 } 203 if (v.tooShort) { 204 return msgs.minlength; 205 } 206 if (v.patternMismatch) { 207 return msgs.email; 208 } 209 return msgs.required; 210 } 211 function authGroupOf(input) { 212 return input.closest ? input.closest(".auth-form__group") : null; 213 } 214 function setAuthFieldError(input, message) { 215 var group = authGroupOf(input); 216 if (!group) { 217 return; 218 } 219 var box = group.querySelector(".auth-field-error"); 220 if (message) { 221 if (!box) { 222 box = document.createElement("div"); 223 box.className = "auth-field-error"; 224 box.setAttribute("role", "alert"); 225 box.setAttribute("aria-live", "polite"); 226 group.appendChild(box); 227 } 228 box.textContent = message; 229 input.classList.add("is-invalid"); 230 input.setAttribute("aria-invalid", "true"); 231 } else { 232 if (box && box.parentNode) { 233 box.parentNode.removeChild(box); 234 } 235 input.classList.remove("is-invalid"); 236 input.removeAttribute("aria-invalid"); 237 } 238 } 239 function authValidatableInputs(form) { 240 return Array.prototype.slice.call(form.querySelectorAll('input[required], input[minlength], input[type="email"]')).filter(function(i) { 241 return i.type !== "hidden" && !i.disabled; 242 }); 243 } 244 function validateAuthForm(form) { 245 var msgs = authMessages(form); 246 var firstInvalid = null; 247 authValidatableInputs(form).forEach(function(input) { 248 var err = authFieldError(input, msgs); 249 setAuthFieldError(input, err); 250 if (err && !firstInvalid) { 251 firstInvalid = input; 252 } 253 }); 254 if (firstInvalid) { 255 firstInvalid.focus(); 256 } 257 return !firstInvalid; 258 } 259 $(document).on("focusout", "form[data-auth-form] input", function() { 260 if (!this.form) { 261 return; 262 } 263 setAuthFieldError(this, authFieldError(this, authMessages(this.form))); 264 }); 265 $(document).on("input", "form[data-auth-form] input.is-invalid", function() { 266 if (!this.form) { 267 return; 268 } 269 setAuthFieldError(this, authFieldError(this, authMessages(this.form))); 270 }); 271 $(document).on("submit", "form[data-auth-form]", function(e) { 272 var form = this; 273 if (!validateAuthForm(form)) { 274 e.preventDefault(); 275 return false; 276 } 277 if (form.dataset.submitting === "1") { 278 e.preventDefault(); 279 return false; 280 } 281 form.dataset.submitting = "1"; 282 var button = form.querySelector('button[type="submit"]'); 283 if (button) { 284 button.classList.add("is-loading"); 285 button.disabled = true; 286 } 287 }); 288 $(document).on("click", ".js-copy-link", function(e) { 289 e.preventDefault(); 290 var $btn = $(this); 291 var url = $btn.data("url") || window.location.href; 292 function ok() { 293 $btn.addClass("is-copied");
294 var copiedLabel = $btn.data("copied-label") || "СкопиÑовано"; 295 var $hint = $('<span class="copy-hint" role="status" aria-live="polite"></span>').text(copiedLabel); 296 $btn.after($hint); 297 setTimeout(function() { 298 $btn.removeClass("is-copied"); 299 $hint.fadeOut(150, function() { 300 $(this).remove(); 301 }); 302 }, 1500); 303 } 304 if (navigator.clipboard && navigator.clipboard.writeText) { 305 navigator.clipboard.writeText(url).then(ok).catch(function() { 306 try { 307 var ta = document.createElement("textarea"); 308 ta.value = url; 309 document.body.appendChild(ta); 310 ta.select(); 311 document.execCommand("copy"); 312 document.body.removeChild(ta); 313 ok(); 314 } catch (err) {} 315 }); 316 } else { 317 try { 318 var ta = document.createElement("textarea"); 319 ta.value = url; 320 document.body.appendChild(ta); 321 ta.select(); 322 document.execCommand("copy"); 323 document.body.removeChild(ta); 324 ok(); 325 } catch (err) {} 326 } 327 return false; 328 }); 329 (function() { 330 var track = document.getElementById("mobileGalleryTrack"); 331 if (!track) return; 332 var slides = Array.prototype.slice.call(track.querySelectorAll(".gallery-slide")); 333 var thumbs = Array.prototype.slice.call(document.querySelectorAll(".gallery-thumb")); 334 var dots = Array.prototype.slice.call(document.querySelectorAll(".gallery-dot")); 335 var prevBtn = document.getElementById("galleryPrevBtn"); 336 var nextBtn = document.getElementById("galleryNextBtn"); 337 function clamp(i) { 338 if (i < 0) return 0; 339 if (i >= slides.length) return slides.length - 1; 340 return i; 341 } 342 function setActive(index) { 343 thumbs.forEach(function(t) { 344 t.classList.toggle("is-active", parseInt(t.getAttribute("data-index")) === index); 345 }); 346 dots.forEach(function(d) { 347 var on = parseInt(d.getAttribute("data-index")) === index; 348 d.classList.toggle("is-active", on); 349 d.setAttribute("aria-pressed", on ? "true" : "false"); 350 }); 351 if (prevBtn) prevBtn.disabled = index <= 0; 352 if (nextBtn) nextBtn.disabled = index >= slides.length - 1; 353 } 354 function scrollToIndex(index) { 355 index = clamp(index); 356 var slide = slides[index]; 357 if (!slide) return; 358 if (typeof slide.scrollIntoView === "function") { 359 slide.scrollIntoView({ 360 behavior: "smooth", 361 inline: "center", 362 block: "nearest" 363 }); 364 } else { 365 var left = slide.offsetLeft - 8; 366 $(track).animate({ 367 scrollLeft: left 368 }, 200); 369 }
370 setActive(index); 371 } 372 $(document).on("click", ".gallery-thumb", function() { 373 var idx = parseInt(this.getAttribute("data-index")) || 0; 374 scrollToIndex(idx); 375 }); 376 $(document).on("click", ".gallery-dot", function() { 377 var idx = parseInt(this.getAttribute("data-index")) || 0; 378 scrollToIndex(idx); 379 }); 380 if (prevBtn) { 381 prevBtn.addEventListener("click", function() { 382 var current = thumbs.findIndex(function(t) { 383 return t.classList.contains("is-active"); 384 }); 385 if (current < 0) current = 0; 386 scrollToIndex(current - 1); 387 }); 388 } 389 if (nextBtn) { 390 nextBtn.addEventListener("click", function() { 391 var current = thumbs.findIndex(function(t) { 392 return t.classList.contains("is-active"); 393 }); 394 if (current < 0) current = 0; 395 scrollToIndex(current + 1); 396 }); 397 } 398 var raf = null; 399 function onScroll() { 400 if (raf) return; 401 raf = requestAnimationFrame(function() { 402 raf = null; 403 var trackRect = track.getBoundingClientRect(); 404 var center = trackRect.left + trackRect.width / 2; 405 var best = 0, bestDist = Infinity; 406 for (var i = 0; i < slides.length; i++) { 407 var r = slides[i].getBoundingClientRect(); 408 var slideCenter = r.left + r.width / 2; 409 var dist = Math.abs(slideCenter - center); 410 if (dist < bestDist) { 411 bestDist = dist; 412 best = i; 413 } 414 }
415 setActive(best); 416 }); 417 } 418 track.addEventListener("scroll", onScroll, { 419 passive: true 420 }); 421 setTimeout(function() { 422 onScroll(); 423 }, 300); 424 })(); 425 $(document).on("show.bs.modal", "#reportModal", function(e) { 426 var $modal = $(this); 427 var $url = $modal.find("#report-url"); 428 if ($url.length) { 429 $url.val(window.location.href); 430 } 431 var $type = $modal.find("#report-type"); 432 var trigger = e.relatedTarget; 433 if ($type.length && trigger) { 434 var $trigger = $(trigger); 435 var desired = $trigger.data("report"); 436 if (desired) { 437 $type.val(desired).trigger("change"); 438 } 439 } 440 }); 441 $(document).on("submit", ".js-add-comment-form", function(e) { 442 e.preventDefault(); 443 var $form = $(this); 444 var url = $form.data("url") || "/comments/add"; 445 var $btn = $("#send_new_comment"); 446 var $textarea = $("#new_comment"); 447 var $error = $form.find(".js-comment-error"); 448 var requiredMsg = $form.data("required-msg") || "Ðоле обÑзаÑелÑно Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ"; 449 var text = ($textarea.val() || "").trim(); 450 var fileInput = $form.find(".js-attach-input").get(0); 451 var hasFile = !!(fileInput && fileInput.files && fileInput.files.length > 0); 452 // ÐÑÑÑой ÑекÑÑ Ð´Ð¾Ð¿ÑÑÑим, еÑли пÑикÑеплена каÑÑинка (ÑообÑение = ÑолÑко ÑоÑо). 453 if (!text && !hasFile) { 454 if ($error.length) { 455 $error.text(requiredMsg); 456 } 457 $textarea.focus(); 458 return; 459 } 460 if ($btn.prop("disabled") || $btn.hasClass("is-loading")) { 461 return; 462 } 463 $btn.prop("disabled", true).addClass("is-loading"); 464 if ($error.length) { 465 $error.text(""); 466 } 467 var ajaxOpts = { url: url, type: "POST", dataType: "json" }; 468 if (hasFile) { 469 // multipart â инаÑе Ñайл не ÑÐµÐ´ÐµÑ 470 ajaxOpts.data = new FormData($form.get(0)); 471 ajaxOpts.processData = false; 472 ajaxOpts.contentType = false;
473 } else { 474 ajaxOpts.data = $form.serialize(); 475 } 476 $.ajax(ajaxOpts).done(function(resp, status, xhr) { 477 if (resp && resp.result) { 478 if (window.CommentEditor && window.CommentEditor.isEnhanced($textarea.get(0))) { 479 window.CommentEditor.clear($textarea.get(0)); 480 } else { 481 $textarea.val(""); 482 } 483 $("#comment_length").text("0/2000"); 484 $("#reply_parent_id").val(""); 485 // ÑбÑоÑиÑÑ Ð¿ÑикÑеплÑннÑÑ ÐºÐ°ÑÑÐ¸Ð½ÐºÑ 486 if (fileInput) { fileInput.value = ""; } 487 var $preview = $form.find(".js-attach-preview"); 488 if ($preview.length) { 489 $preview.attr("hidden", "hidden").find(".ua-attach-preview__img").attr("src", ""); 490 } 491 if ($error.length) { 492 $error.text(""); 493 } 494 if (window.GuestCommentDraft) { 495 window.GuestCommentDraft.clearFor($form); 496 } 497 var $count = $(".js-comments-count"); 498 $count.text(parseInt($count.text(), 10) + 1); 499 var html = resp.html || ""; 500 var id = typeof resp.id !== "undefined" ? resp.id : resp.data && resp.data.id ? resp.data.id : ""; 501 if (html) { 502 var $tmp = $("<div/>").html(html.trim()); 503 var $item = $tmp.children().first(); 504 $item.addClass("comment-just-added"); 505 $(".comments_block").append($item); 506 } else { 507 $(".comments_block").append('<div class="comments comment-just-added"><div class="comment_text" id="' + id + '">' + (resp.data && resp.data.text ? resp.data.text : "") + "</div></div>"); 508 } 509 var $lastComment = $(".comments_block .comments").last(); 510 $lastComment.on("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function() { 511 $(this).removeClass("comment-just-added"); 512 }); 513 if (window.history && window.history.replaceState) { 514 var hash = "#" + id; 515 window.history.replaceState({}, document.title, window.location.pathname + window.location.search + hash); 516 } 517 } else { 518 var msg = resp && resp.msg ? resp.msg : "ÐÑибка оÑпÑавки"; 519 if ($error.length) { 520 $error.text(msg); 521 } else { 522 alert(msg); 523 } 524 } 525 }).fail(function(xhr) { 526 var msg = "СеÑÑ Ð½ÐµÐ´Ð¾ÑÑÑпна. ÐопÑобÑйÑе позже"; 527 if (xhr && xhr.responseJSON && xhr.responseJSON.msg) { 528 msg = xhr.responseJSON.msg; 529 } 530 if ($error.length) { 531 $error.text(msg); 532 } else { 533 alert(msg); 534 } 535 }).always(function() { 536 $btn.prop("disabled", false).removeClass("is-loading"); 537 }); 538 }); 539 function showPurchaseAccessModal(err) { 540 var t = window.AppTranslations || {}; 541 var data = err || {}; 542 var isLoginRequired = data.type === "login_required"; 543 var title = data.title || t.purchaseAccessTitle || "ÐокÑпка лоÑа"; 544 var message = data.message || t.purchaseAccessUnavailable || "ÐейÑÑвие ÑейÑÐ°Ñ Ð½ÐµÐ´Ð¾ÑÑÑпно."; 545 var loginUrl = data.loginUrl || "/login"; 546 var registerUrl = data.registerUrl || "/registration"; 547 $("#purchaseErrorTitle").text(title); 548 $("#purchaseErrorMessage").html(message); 549 if (isLoginRequired) { 550 $("#purchaseErrorHint").removeClass("d-none").text(t.purchaseAccessHint || "ÐоÑле Ð²Ñ Ð¾Ð´Ð° Ð²Ñ ÑможеÑе ÑÑÐ°Ð·Ñ Ð¿ÑодолжиÑÑ.").show(); 551 $("#purchaseErrorLoginBtn").removeClass("d-none").data("redirectUrl", loginUrl).show(); 552 $("#purchaseErrorRegisterBtn").removeClass("d-none").data("redirectUrl", registerUrl).show(); 553 $("#purchaseErrorCloseBtn").removeClass("btn-primary").addClass("btn-link").text(t.modalLater || "Ðозже"); 554 } else { 555 $("#purchaseErrorHint").addClass("d-none").hide().text(""); 556 $("#purchaseErrorLoginBtn").addClass("d-none").hide().removeData("redirectUrl"); 557 $("#purchaseErrorRegisterBtn").addClass("d-none").hide().removeData("redirectUrl"); 558 $("#purchaseErrorCloseBtn").removeClass("btn-link").addClass("btn-primary").text(t.modalOk || "ÐонÑÑно"); 559 } 560 window.AppUi.showModal("#purchaseErrorModal"); 561 } 562 $(document).on("click", "#purchaseErrorLoginBtn, #purchaseErrorRegisterBtn", function() { 563 var url = $(this).data("redirectUrl"); 564 if (url) { 565 window.location.href = url; 566 } 567 }); 568 function escapeHtml(value) { 569 return String(value == null ? "" : value).replace(/[&<>"']/g, function(ch) { 570 return { 571 "&": "&", 572 "<": "<", 573 ">": ">", 574 "\"": """, 575 "'": "'" 576 }[ch]; 577 }); 578 } 579 function buildPurchaseConfirmMessage(title, price) { 580 var t = window.AppTranslations || {}; 581 var intro = t.purchaseConfirmIntro || "ÐÑ Ð¿Ð¾ÐºÑпаеÑе"; 582 var note = t.purchaseConfirmNote || "ÐоÑле подÑвеÑÐ¶Ð´ÐµÐ½Ð¸Ñ Ð±ÑÐ´ÐµÑ Ñоздана Ñделка. Ðалее заполниÑе даннÑе доÑÑавки и оплаÑÑ."; 583 var priceLabel = t.purchaseConfirmPrice || "Цена"; 584 585 return [ 586 '<div class="purchase-confirm">', 587 '<div class="purchase-confirm__label">' + escapeHtml(intro) + '</div>', 588 '<div class="purchase-confirm__title">«' + escapeHtml(title) + '»</div>', 589 '<div class="purchase-confirm__price-row">', 590 '<span class="purchase-confirm__price-label">' + escapeHtml(priceLabel) + '</span>', 591 '<strong class="purchase-confirm__price">' + escapeHtml(price) + '</strong>', 592 '</div>', 593 '<div class="purchase-confirm__note">' + escapeHtml(note) + '</div>', 594 '</div>' 595 ].join(""); 596 } 597 $(document).on("submit", ".buy-form:not(.cart-add-form):not(.favorite-toggle-form)", function(e) { 598 e.preventDefault(); 599 var $form = $(this); 600 var $btn = $form.find(".buy-btn"); 601 var actionUrl = $form.attr("action"); 602 var advertId = actionUrl.match(/\/(\d+)$/)[1]; 603 if ($btn.prop("disabled") || $btn.hasClass("is-loading")) { 604 return; 605 } 606 if (window.PurchasePermissions && !window.PurchasePermissions.canBuy) { 607 showPurchaseAccessModal(window.PurchasePermissions.error || {}); 608 return; 609 } 610 $btn.prop("disabled", true).addClass("is-loading"); 611 var price = $form.data("buy-price") || $(".sale-price").text().trim(); 612 var advertTitle = $form.data("buy-title") || $("h1").text().trim(); 613 var purchaseConfirm = window.AppTranslations && window.AppTranslations.purchaseConfirm ? window.AppTranslations.purchaseConfirm : "ÐодÑвеÑждение покÑпки"; 614 window.showConfirm({ 615 title: purchaseConfirm,
616 messageHtml: buildPurchaseConfirmMessage(advertTitle, price) 617 }).then(function(ok) { 618 if (ok) { 619 var $tempForm = $("<form>", { 620 method: "POST", 621 action: $form.attr("action") 622 }); 623 $tempForm.append($form.find("input").clone()); 624 $tempForm.appendTo("body").submit(); 625 } else { 626 $btn.prop("disabled", false).removeClass("is-loading"); 627 } 628 }).catch(function() { 629 $btn.prop("disabled", false).removeClass("is-loading"); 630 }); 631 }); 632 $(document).on("submit", ".cart-add-form", function(e) { 633 e.preventDefault(); 634 var $form = $(this); 635 var $btn = $form.find(".buy-btn"); 636 var cartUrl = $btn.data("cart-url") || "/cart"; 637 var inCartLabel = $btn.data("label-in-cart") || "РкоÑзине"; 638 if ($form.hasClass("is-in-cart")) { 639 window.location.href = cartUrl; 640 return; 641 } 642 if ($btn.prop("disabled") || $btn.hasClass("is-loading")) { 643 return; 644 } 645 if (window.PurchasePermissions && !window.PurchasePermissions.canBuy) { 646 showPurchaseAccessModal(window.PurchasePermissions.error || {}); 647 return; 648 } 649 $btn.prop("disabled", true).addClass("is-loading"); 650 $.ajax({ 651 url: $form.attr("action"), 652 method: "POST", 653 data: $form.serialize() 654 }).done(function() { 655 $form.addClass("is-in-cart"); 656 $btn.removeClass("is-loading").addClass("buy-btn--in-cart"); 657 $btn.find(".buy-btn__label").text(inCartLabel); 658 $btn.prop("disabled", false); 659 }).fail(function() { 660 $btn.prop("disabled", false).removeClass("is-loading"); 661 alert("Ðе ÑдалоÑÑ Ð´Ð¾Ð±Ð°Ð²Ð¸ÑÑ Ð² коÑзинÑ. ÐопÑобÑйÑе еÑе Ñаз."); 662 }); 663 }); 664 $(document).on("submit", ".favorite-toggle-form", function(e) { 665 e.preventDefault(); 666 var $form = $(this); 667 var $btn = $form.find(".favorite-btn"); 668 var $icon = $btn.find(".fa-heart"); 669 var $count = $form.find(".favorite-count"); 670 if ($btn.prop("disabled") || $btn.hasClass("is-loading")) { 671 return; 672 } 673 if (window.PurchasePermissions && !window.PurchasePermissions.canBuy) { 674 showPurchaseAccessModal(window.PurchasePermissions.error || {}); 675 return; 676 } 677 $btn.prop("disabled", true).addClass("is-loading"); 678 $.ajax({ 679 url: $form.attr("action"), 680 method: "POST", 681 data: $form.serialize() 682 }).done(function(resp) { 683 if (!resp || !resp.ok) { 684 throw new Error("bad_response"); 685 } 686 var isFavorite = !!resp.isFavorite; 687 var count = parseInt(resp.count, 10); 688 if (isNaN(count) || count < 0) { 689 count = 0; 690 } 691 $form.toggleClass("is-favorite", isFavorite); 692 $btn.toggleClass("is-active", isFavorite); 693 $icon.toggleClass("fa-solid", isFavorite).toggleClass("fa-regular", !isFavorite); 694 var t = window.AppTranslations || {}; 695 var addTitle = t.addToFavorites || "ÐобавиÑÑ Ð² избÑанное"; 696 var removeTitle = t.removeFromFavorites || "УбÑаÑÑ Ð¸Ð· избÑанного"; 697 var nextTitle = isFavorite ? removeTitle : addTitle; 698 $btn.attr("title", nextTitle).attr("data-original-title", nextTitle); 699 var $label = $btn.find(".favorite-btn__label"); 700 if ($label.length) { 701 var addLabel = t.favoriteLabelAdd || "РизбÑанное"; 702 var activeLabel = t.favoriteLabelActive || "РизбÑанном"; 703 $label.text(isFavorite ? activeLabel : addLabel); 704 } 705 $count.text(count); 706 if (count > 0 || isFavorite) { 707 $count.removeClass("is-hidden"); 708 } else { 709 $count.addClass("is-hidden"); 710 } 711 }).fail(function() { 712 alert("Ðе ÑдалоÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð¸ÑÑ Ð¸Ð·Ð±Ñанное. ÐопÑобÑйÑе еÑе Ñаз."); 713 }).always(function() { 714 $btn.prop("disabled", false).removeClass("is-loading"); 715 }); 716 }); 717 $(".comments_block").on("click", ".remove-comment", function() { 718 var $btn = $(this); 719 var isRestore = !!$btn.closest(".comments").find(".restore-comment").length; 720 if (isRestore) { 721 removeComment($btn); 722 return false;
723 } 724 var confirmMessage = getCommentI18n("delete-confirm", "ÐейÑÑвиÑелÑно ÑдалиÑÑ ÑÑÐ¾Ñ ÐºÐ¾Ð¼Ð¼ÐµÐ½ÑаÑий?"); 725 if (window.showConfirm) { 726 window.showConfirm({ 727 title: getCommentI18n("delete-title", "ÐодÑвеÑждение ÑдалениÑ"), 728 messageHtml: confirmMessage 729 }).then(function(ok) { 730 if (ok) { 731 removeComment($btn); 732 } 733 }); 734 } else if (confirm(confirmMessage)) { 735 removeComment($btn); 736 } 737 return false; 738 }); 739 $(".comments_block").on("click", ".restore-comment", function() { 740 removeComment($(this)); 741 return false; 742 }); 743 (function() { 744 var $btn = $("#emoji_btn"); 745 var $panel = $("#emoji_panel"); 746 var $ta = $("#new_comment"); 747 if (!$btn.length || !$panel.length || !$ta.length) { 748 return; 749 } 750 var isMobile = window.matchMedia && window.matchMedia("(max-width: 768px)").matches; 751 if (isMobile) { 752 if (window.bootstrap && window.bootstrap.Tooltip && $btn[0]) { 753 var emojiTooltip = window.bootstrap.Tooltip.getInstance($btn[0]); 754 if (emojiTooltip) { 755 emojiTooltip.dispose(); 756 } 757 } 758 } 759 function closePanel() { 760 $panel.hide().attr("aria-hidden", "true"); 761 } 762 function openPanel() { 763 $panel.show().attr("aria-hidden", "false"); 764 } 765 $btn.on("click", function(e) { 766 e.preventDefault(); 767 if ($panel.is(":visible")) { 768 closePanel(); 769 } else { 770 openPanel(); 771 } 772 }); 773 function insertAtCursor(emoji) { 774 var el = $ta.get(0); 775 if (!el) { 776 return; 777 } 778 // ÐÑли поле пÑевÑаÑено в WYSIWYG-ÑедакÑÐ¾Ñ â вÑÑавлÑем в него. 779 if (window.CommentEditor && window.CommentEditor.insert(el, emoji)) { 780 return; 781 } 782 el.focus(); 783 var start = el.selectionStart || 0; 784 var end = el.selectionEnd || 0; 785 var value = el.value || ""; 786 el.value = value.substring(0, start) + emoji + value.substring(end); 787 var pos = start + emoji.length; 788 if (el.setSelectionRange) { 789 el.setSelectionRange(pos, pos); 790 } 791 $ta.trigger("input"); 792 } 793 $panel.on("click", ".emoji-item", function(e) { 794 e.preventDefault(); 795 var emoji = $(this).text(); 796 insertAtCursor(emoji); 797 closePanel(); 798 }); 799 $(document).on("click", function(e) { 800 if (!$panel.is(":visible")) { 801 return; 802 } 803 var $t = $(e.target); 804 if ($t.closest("#emoji_panel").length === 0 && $t.closest("#emoji_btn").length === 0) { 805 closePanel(); 806 } 807 }); 808 $(document).on("keydown", function(e) { 809 if (e.key === "Escape") { 810 closePanel(); 811 } 812 }); 813 $(window).on("resize", function() { 814 var mobile = window.matchMedia && window.matchMedia("(max-width: 768px)").matches; 815 if (mobile) { 816 closePanel(); 817 } 818 }); 819 })(); 820 $(".carousel").carousel(); 821 $("[id^=carousel-selector-]").click(function() { 822 var id_selector = $(this).attr("id"); 823 var id = id_selector.substr(id_selector.length - 1); 824 id = parseInt(id); 825 $(".carousel").carousel(id); 826 $("[id^=carousel-selector-]").removeClass("selected"); 827 $(this).addClass("selected"); 828 }); 829 $(".carousel").on("slid", function(e) { 830 var id = $(".item.active").data("slide-number"); 831 id = parseInt(id); 832 $("[id^=carousel-selector-]").removeClass("selected"); 833 $("[id=carousel-selector-" + id + "]").addClass("selected"); 834 }); 835 $(".comments_block").on("click", ".reply", function(e) { 836 e.preventDefault(); 837 var $commentsContainer = $(this).closest(".comments-container"); 838 var $addCommentBlock = $commentsContainer.find(".add-comment-block"); 839 var $textarea = $addCommentBlock.find("#new_comment"); 840 if (!$textarea.length) { 841 var $guestCompose = $addCommentBlock.find(".guest-comment-compose"); 842 var $target = $guestCompose.length ? $guestCompose : $addCommentBlock; 843 if ($target.length) { 844 $([ document.documentElement, document.body ]).animate({ 845 scrollTop: Math.max($target.offset().top - 16, 0) 846 }, 500).promise().done(function() { 847 if ($guestCompose.length) { 848 $guestCompose.removeClass("guest-comment-compose--prompted"); 849 $guestCompose[0].offsetHeight; 850 $guestCompose.addClass("guest-comment-compose--prompted"); 851 window.setTimeout(function() { 852 $guestCompose.removeClass("guest-comment-compose--prompted"); 853 }, 1400); 854 } 855 var $loginLink = $target.find("a[href]").first(); 856 if ($loginLink.length) { 857 try { 858 $loginLink[0].focus({ 859 preventScroll: true 860 }); 861 } catch (error) { 862 $loginLink[0].focus(); 863 } 864 } 865 }); 866 } 867 return; 868 }
869 var parentId = $(this).data("comment-id"); 870 $addCommentBlock.find("#reply_parent_id").val(parentId || ""); 871 var comment = $textarea.val() || ""; 872 var user_name = $(this).data("user-name"); 873 var user_name_txt = user_name + ", "; 874 var has_string = comment.indexOf(user_name_txt) >= 0; 875 if (has_string == false) { 876 $textarea.val(user_name_txt + comment); 877 if (window.CommentEditor) { 878 window.CommentEditor.pull($textarea.get(0)); 879 } 880 } 881 $([ document.documentElement, document.body ]).animate({ 882 scrollTop: Math.max($addCommentBlock.offset().top - 16, 0) 883 }, 500).promise().done(function() { 884 var rich = $textarea.get(0) && $textarea.get(0)._rich; 885 (rich || $textarea.get(0)).focus(); 886 }); 887 }); 888 $(".comments_block").on("click", ".comment-edit-trigger", function(e) { 889 e.preventDefault(); 890 openInlineCommentEditor($(this).closest(".comments")); 891 }); 892 $(".comments_block").on("click", ".comment-edit-cancel", function(e) { 893 e.preventDefault(); 894 var $wrap = $(this).closest(".comment-inline-editor-wrap"); 895 var $text = $wrap.siblings(".comment-text-content"); 896 var $votes = $wrap.siblings(".comment-vote-box"); 897 $wrap.remove(); 898 $text.show(); 899 $votes.show(); 900 }); 901 $(".comments_block").on("click", ".comment-edit-save", function(e) { 902 e.preventDefault(); 903 saveInlineCommentEdit($(this).closest(".comments")); 904 }); 905 $(".comments_block").on("keydown", ".comment-inline-editor, .ua-rich-editor--inline", function(e) { 906 if ((e.ctrlKey || e.metaKey) && e.key === "Enter") { 907 e.preventDefault(); 908 saveInlineCommentEdit($(this).closest(".comments")); 909 } 910 }); 911 $("#report-error").on("click", function() { 912 var form = $(this).closest("form"); 913 var textField = form.find("#report-text"); 914 var urlField = form.find("#report-url"); 915 clearInvalid(textField); 916 clearInvalid(urlField); 917 var text = textField.val(); 918 var url = urlField.val(); 919 var hasError = false; 920 if (!text || text.length >= 1e3) { 921 setInvalid(textField); 922 hasError = true; 923 } 924 if (!url || url.length >= 250) { 925 setInvalid(urlField); 926 hasError = true; 927 } 928 if (hasError) { 929 return false; 930 } 931 reportError(form); 932 return false; 933 }); 934 function setInvalid(field) { 935 field.closest(".form-group").addClass("is-invalid"); 936 } 937 function clearInvalid(field) { 938 field.closest(".form-group").removeClass("is-invalid"); 939 } 940 $("#report-text, #report-url").on("input", function() { 941 $(this).closest(".form-group").removeClass("is-invalid"); 942 }); 943 $("body").on("click", ".like-button", function() { 944 $(this).removeClass("like-button"); 945 likeComment($(this)); 946 return false; 947 }); 948 new_comment = document.getElementById("new_comment"); 949 if (new_comment) { 950 new_comment.addEventListener("input", function() { 951 comment_length.innerText = new_comment.value.length + "/2000"; 952 var err = document.querySelector(".js-comment-error"); 953 if (err && new_comment.value.trim().length > 0) { 954 err.textContent = ""; 955 } 956 }); 957 } 958 (function initGuestCommentDraft() { 959 var PREFIX = "uacoins_comment_draft:"; 960 var PENDING = "uacoins_comment_draft_pending"; 961 var TTL = 24 * 60 * 60 * 1e3; 962 var MAX_LEN = 2e3; 963 function hasLS() { 964 try { 965 var k = "__uacoins_t"; 966 localStorage.setItem(k, "1"); 967 localStorage.removeItem(k); 968 return true; 969 } catch (e) { 970 return false; 971 } 972 } 973 var LS = hasLS(); 974 function keyFor(model, id) { 975 return PREFIX + model + ":" + id; 976 } 977 function save(model, id, text) { 978 if (!LS) return; 979 try { 980 if (text && text.trim()) { 981 localStorage.setItem(keyFor(model, id), JSON.stringify({ 982 text: String(text).slice(0, MAX_LEN), 983 ts: Date.now() 984 })); 985 } else { 986 localStorage.removeItem(keyFor(model, id)); 987 } 988 } catch (e) {} 989 } 990 function read(model, id) { 991 if (!LS) return ""; 992 try { 993 var raw = localStorage.getItem(keyFor(model, id)); 994 if (!raw) return ""; 995 var obj = JSON.parse(raw); 996 if (!obj || !obj.text) return ""; 997 if (obj.ts && Date.now() - obj.ts > TTL) { 998 localStorage.removeItem(keyFor(model, id)); 999 return ""; 1000 } 1001 return String(obj.text).slice(0, MAX_LEN); 1002 } catch (e) { 1003 return ""; 1004 } 1005 } 1006 window.GuestCommentDraft = { 1007 clearFor: function($form) { 1008 if (!LS) return; 1009 try { 1010 var m = $form.find('input[name="model"]').val(); 1011 var mid = $form.find('input[name="model_id"]').val(); 1012 if (m && mid) { 1013 localStorage.removeItem(keyFor(m, mid)); 1014 } 1015 localStorage.removeItem(PENDING); 1016 } catch (e) {} 1017 } 1018 }; 1019 var $guest = $(".js-guest-comment"); 1020 if ($guest.length) { 1021 var gModel = String($guest.data("model")); 1022 var gId = String($guest.data("model-id")); 1023 var $text = $guest.find(".js-guest-comment-text"); 1024 var $len = $("#guest_comment_length"); 1025 function updLen() { 1026 if ($len.length) { 1027 $len.text(($text.val() || "").length + "/2000"); 1028 } 1029 } 1030 var existing = read(gModel, gId); 1031 if (existing && $text.length && !$text.val()) { 1032 $text.val(existing); 1033 } 1034 updLen(); 1035 $text.on("input", function() { 1036 save(gModel, gId, $text.val()); 1037 updLen(); 1038 }); 1039 $guest.on("click", ".js-guest-comment-auth", function(e) { 1040 var base = $(this).data("href"); 1041 if (!base) { 1042 return; 1043 } 1044 e.preventDefault(); 1045 var text = ($text.val() || "").trim(); 1046 if (text) { 1047 save(gModel, gId, text); 1048 try { 1049 localStorage.setItem(PENDING, keyFor(gModel, gId)); 1050 } catch (err) {} 1051 } 1052 var target = window.location.pathname + window.location.search + "#comment_form"; 1053 var url = base + (base.indexOf("?") > -1 ? "&" : "?") + "_target_path=" + encodeURIComponent(target); 1054 window.location.href = url; 1055 }); 1056 } 1057 var $form = $(".js-add-comment-form"); 1058 var $new = $("#new_comment"); 1059 if ($form.length && $new.length && LS) { 1060 var lModel = String($form.data("model") || $form.find('input[name="model"]').val() || ""); 1061 var lId = String($form.data("model-id") || $form.find('input[name="model_id"]').val() || ""); 1062 if (lModel && lId) { 1063 var draft = read(lModel, lId); 1064 var pending = ""; 1065 try { 1066 pending = localStorage.getItem(PENDING) || ""; 1067 } catch (e) {} 1068 var justLoggedIn = pending && pending === keyFor(lModel, lId); 1069 if (draft && !$new.val()) { 1070 var newEl = $new[0]; 1071 newEl.value = draft; 1072 newEl.dispatchEvent(new Event("input", { 1073 bubbles: true 1074 })); 1075 if (justLoggedIn) { 1076 var hint = document.getElementById("comment_restored_hint"); 1077 if (hint) { 1078 hint.hidden = false;
1079 } 1080 var anchor = document.getElementById("comment_form"); 1081 if (anchor && anchor.scrollIntoView) { 1082 anchor.scrollIntoView({ 1083 behavior: "smooth", 1084 block: "center" 1085 }); 1086 } 1087 try { 1088 $new.focus(); 1089 var v = $new.val(); 1090 $new.val("").val(v); 1091 } catch (e) {} 1092 } 1093 } 1094 if (justLoggedIn) { 1095 try { 1096 localStorage.removeItem(PENDING); 1097 } catch (e) {} 1098 } 1099 } 1100 } 1101 })(); 1102 new_feedback = document.getElementById("new_feedback"); 1103 if (new_feedback) { 1104 new_feedback.addEventListener("input", function() { 1105 feedback_length.innerText = new_feedback.value.length + "/500"; 1106 }); 1107 } 1108 var shareLocale = document.body ? document.body.getAttribute("data-current-locale") : "ru"; 1109 var shareLabel = shareLocale === "ua" ? "ÐодÑлиÑиÑÑ" : shareLocale === "en" ? "Share" : "ÐоделиÑÑÑÑ"; 1110 var shareCopyLabel = shareLocale === "ua" ? "ÐопÑÑваÑи поÑиланнÑ" : shareLocale === "en" ? "Copy link" : "ÐопиÑоваÑÑ ÑÑÑлкÑ"; 1111 var shareCopiedLabel = shareLocale === "ua" ? "СкопÑйовано" : shareLocale === "en" ? "Copied" : "СкопиÑовано"; 1112 function fallbackCopy(text) { 1113 var input = document.createElement("input"); 1114 input.value = text; 1115 input.setAttribute("readonly", "readonly"); 1116 input.className = "ua-offscreen-copy-input"; 1117 document.body.appendChild(input); 1118 input.select(); 1119 document.execCommand("copy"); 1120 document.body.removeChild(input); 1121 } 1122 function copyTextToClipboard(text, done) { 1123 if (navigator.clipboard && navigator.clipboard.writeText) { 1124 navigator.clipboard.writeText(text).then(done).catch(function() { 1125 fallbackCopy(text); 1126 done(); 1127 }); 1128 return; 1129 } 1130 fallbackCopy(text); 1131 done(); 1132 } 1133 function getFancyboxShareData(carousel) { 1134 var page = carousel && carousel.getPage ? carousel.getPage() : null; 1135 var slide = page && page.slides && page.slides.length ? page.slides[0] : null; 1136 var url = window.location.href; 1137 var title = document.title; 1138 var media = ""; 1139 if (slide) { 1140 title = normalizeShareText(slide.caption || slide.alt || title); 1141 media = makeAbsoluteUrl(slide.src || ""); 1142 } 1143 return { 1144 url: url, 1145 title: normalizeShareText(title), 1146 media: media 1147 }; 1148 } 1149 function normalizeShareText(value) { 1150 if (value instanceof HTMLElement) { 1151 return value.textContent || ""; 1152 } 1153 return $("<div>").html(String(value || "")).text(); 1154 } 1155 function makeAbsoluteUrl(url) { 1156 if (!url) { 1157 return ""; 1158 } 1159 try { 1160 return new URL(url, window.location.href).href; 1161 } catch (e) { 1162 return url; 1163 } 1164 } 1165 function openShareWindow(url) { 1166 window.open(url, "", "left=0,top=0,width=640,height=420,menubar=no,toolbar=no,resizable=yes,scrollbars=yes"); 1167 } 1168 function closeFancyboxShareMenu(container) { 1169 if (!container) { 1170 return; 1171 } 1172 var existingMenu = container.querySelector(".ua-fancybox-share-menu"); 1173 if (existingMenu) { 1174 existingMenu.remove(); 1175 } 1176 Array.prototype.forEach.call(container.querySelectorAll('[data-ua-fancybox-share][aria-expanded="true"]'), function(button) { 1177 button.setAttribute("aria-expanded", "false"); 1178 }); 1179 } 1180 function openFancyboxShareMenu(carousel, event) { 1181 var container = carousel && carousel.getContainer ? carousel.getContainer() : null; 1182 var root = container ? container.closest(".fancybox__container") : null; 1183 if (!root) { 1184 return; 1185 } 1186 var currentMenu = root.querySelector(".ua-fancybox-share-menu"); 1187 if (currentMenu) { 1188 currentMenu.remove(); 1189 if (event && event.currentTarget) { 1190 event.currentTarget.setAttribute("aria-expanded", "false"); 1191 } 1192 return; 1193 } 1194 var data = getFancyboxShareData(carousel); 1195 var encodedUrl = encodeURIComponent(data.url); 1196 var encodedTitle = encodeURIComponent(data.title); 1197 var encodedShareText = encodeURIComponent((data.title ? data.title + " " : "") + data.url); 1198 var menu = document.createElement("div"); 1199 menu.className = "ua-fancybox-share-menu"; 1200 menu.setAttribute("role", "menu"); 1201 menu.innerHTML = "" + '<button class="ua-fancybox-share-menu__item js-fancybox-share-copy" type="button" role="menuitem">' + shareCopyLabel + "</button>" + '<a class="ua-fancybox-share-menu__item" role="menuitem" href="https://t.me/share/url?url=' + encodedUrl + "&text=" + encodedTitle + '">Telegram</a>' + '<a class="ua-fancybox-share-menu__item" role="menuitem" href="viber://forward?text=' + encodedShareText + '">Viber</a>' + '<a class="ua-fancybox-share-menu__item" role="menuitem" href="https://www.facebook.com/sharer/sharer.php?u=' + encodedUrl + '">Facebook</a>' + '<a class="ua-fancybox-share-menu__item" role="menuitem" href="https://api.whatsapp.com/se
1201nd?text=' + encodedShareText + '">WhatsApp</a>'; 1202 menu.querySelector(".js-fancybox-share-copy").addEventListener("click", function() { 1203 var button = this; 1204 copyTextToClipboard(data.url, function() { 1205 button.textContent = shareCopiedLabel; 1206 setTimeout(function() { 1207 button.textContent = shareCopyLabel; 1208 }, 1600); 1209 }); 1210 }); 1211 Array.prototype.forEach.call(menu.querySelectorAll("a"), function(link) { 1212 link.addEventListener("click", function(clickEvent) { 1213 clickEvent.preventDefault(); 1214 openShareWindow(link.href); 1215 }); 1216 }); 1217 root.appendChild(menu); 1218 if (event && event.currentTarget) { 1219 event.currentTarget.setAttribute("aria-expanded", "true"); 1220 } 1221 } 1222 $(document).on("click", function(event) { 1223 var target = event.target; 1224 if (!target || !target.closest || target.closest(".ua-fancybox-share-menu") || target.closest("[data-ua-fancybox-share]")) { 1225 return; 1226 } 1227 closeFancyboxShareMenu(document); 1228 }); 1229 if (window.Fancybox && window.Fancybox.bind) { 1230 window.Fancybox.bind("[data-fancybox]", { 1231 l10n: { 1232 SHARE: shareLabel 1233 }, 1234 wheel: "slide", 1235 Carousel: { 1236 infinite: true, 1237 Toolbar: { 1238 absolute: true, 1239 items: { 1240 share: { 1241 tpl: '<button data-ua-fancybox-share class="f-button" title="{{SHARE}}" aria-label="{{SHARE}}" aria-expanded="false"><svg><path d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7"/><path d="M12 16V4"/><path d="m7 9 5-5 5 5"/></svg></button>', 1242 click: openFancyboxShareMenu 1243 } 1244 }, 1245 display: { 1246 left: [ "counter" ], 1247 middle: [ "zoomIn", "zoomOut", "toggle1to1" ], 1248 right: [ "share", "autoplay", "fullscreen", "thumbs", "close" ] 1249 } 1250 }, 1251 on: { 1252 change: function(carousel) { 1253 var container = carousel && carousel.getContainer ? carousel.getContainer() : null; 1254 var root = container ? container.closest(".fancybox__container") : null; 1255 closeFancyboxShareMenu(root); 1256 } 1257 } 1258 }, 1259 on: { 1260 close: function(fancybox) { 1261 closeFancyboxShareMenu(fancybox && fancybox.getContainer ? fancybox.getContainer() : document); 1262 } 1263 } 1264 }); 1265 } 1266 (function() { 1267 var form = document.getElementById("deal-chat-form"); 1268 if (!form) { 1269 return; 1270 } 1271 var list = document.getElementById("deal-chat-list"); 1272 var scrollToBottom = function(force) { 1273 if (!list) { 1274 return; 1275 } 1276 var nearBottom = list.scrollHeight - list.scrollTop - list.clientHeight < 140; 1277 if (force || nearBottom) { 1278 list.scrollTop = list.scrollHeight; 1279 } 1280 }; 1281 scrollToBottom(true); 1282 setTimeout(function() { 1283 scrollToBottom(true); 1284 }, 0); 1285 setTimeout(function() { 1286 scrollToBottom(true); 1287 }, 120); 1288 window.addEventListener("load", function() { 1289 scrollToBottom(true); 1290 }); 1291 if (list) { 1292 Array.prototype.forEach.call(list.querySelectorAll("img"), function(img) { 1293 if (img.complete) { 1294 return; 1295 } 1296 img.addEventListener("load", function() { 1297 scrollToBottom(true); 1298 }, { 1299 once: true 1300 }); 1301 }); 1302 } 1303 var ta = form.querySelector(".deal-chat__input"); 1304 var sendBtn = form.querySelector(".deal-chat__send");
1305 var fileInput = document.getElementById("deal-chat-photos"); 1306 var captionInput = document.getElementById("deal-chat-photo-caption"); 1307 var sharedCaptionInput = document.getElementById("deal-chat-upload-shared-caption"); 1308 var photoModal = document.getElementById("deal-chat-photos-modal"); 1309 var photoGrid = document.getElementById("deal-chat-upload-grid"); 1310 var photoCancelBtn = document.getElementById("deal-chat-photo-cancel"); 1311 var photoSendBtn = document.getElementById("deal-chat-photo-send"); 1312 var sendingStatus = document.getElementById("deal-chat-sending"); 1313 var draftKey = form.getAttribute("data-draft-key") || ""; 1314 var draftStorage = null; 1315 var draftSaveTimer = null; 1316 var draftClearedForSubmit = false; 1317 var previewUrls = []; 1318 var isSubmitting = false; 1319 if (draftKey) { 1320 try { 1321 draftStorage = window.localStorage || null; 1322 } catch (e) { 1323 draftStorage = null; 1324 } 1325 } 1326 var getTextValue = function() { 1327 return ta ? (ta.value || "").trim() : ""; 1328 }; 1329 var isTextValid = function() { 1330 return getTextValue().length > 0; 1331 }; 1332 var submitForm = function() { 1333 if (typeof form.requestSubmit === "function") { 1334 form.requestSubmit(); 1335 } else { 1336 var submitEvent = document.createEvent("Event"); 1337 submitEvent.initEvent("submit", true, true); 1338 if (form.dispatchEvent(submitEvent)) { 1339 form.submit(); 1340 } 1341 } 1342 }; 1343 var setSubmittingState = function(state) { 1344 isSubmitting = state; 1345 form.dataset.submitting = state ? "1" : "0"; 1346 if (ta) { 1347 ta.readOnly = state; 1348 } 1349 if (photoSendBtn) { 1350 photoSendBtn.disabled = state; 1351 } 1352 if (photoCancelBtn) { 1353 photoCancelBtn.disabled = state; 1354 } 1355 if (sendBtn) { 1356 sendBtn.disabled = state || !isTextValid(); 1357 sendBtn.classList.toggle("is-loading", state); 1358 } 1359 if (sendingStatus) { 1360 if (state) { 1361 sendingStatus.textContent = form.getAttribute("data-sending-label") || "ÐÑпÑавка..."; 1362 sendingStatus.classList.add("active"); 1363 } else { 1364 sendingStatus.textContent = ""; 1365 sendingStatus.classList.remove("active"); 1366 } 1367 } 1368 }; 1369 var updateSendAvailability = function() { 1370 if (!sendBtn || isSubmitting) { 1371 return; 1372 } 1373 sendBtn.disabled = !isTextValid(); 1374 }; 1375 var saveDraft = function() { 1376 if (draftClearedForSubmit || !draftStorage || !draftKey || !ta) { 1377 return; 1378 } 1379 try { 1380 var value = ta.value || ""; 1381 if (value.trim().length > 0) { 1382 draftStorage.setItem(draftKey, value); 1383 } else { 1384 draftStorage.removeItem(draftKey); 1385 } 1386 } catch (e) {} 1387 }; 1388 var scheduleDraftSave = function() { 1389 window.clearTimeout(draftSaveTimer); 1390 draftSaveTimer = window.setTimeout(saveDraft, 200); 1391 }; 1392 var clearDraft = function() { 1393 window.clearTimeout(draftSaveTimer); 1394 draftClearedForSubmit = true; 1395 if (!draftStorage || !draftKey) { 1396 return; 1397 } 1398 try { 1399 draftStorage.removeItem(draftKey); 1400 } catch (e) {} 1401 }; 1402 if (ta) { 1403 var autoGrow = function() { 1404 this.style.height = "auto"; 1405 this.style.height = Math.min(this.scrollHeight, 160) + "px"; 1406 }; 1407 if (draftStorage && draftKey && !ta.value) { 1408 try { 1409 var savedDraft = draftStorage.getItem(draftKey); 1410 if (savedDraft) { 1411 ta.value = savedDraft; 1412 } 1413 } catch (e) {} 1414 } 1415 ta.addEventListener("input", autoGrow); 1416 ta.addEventListener("input", updateSendAvailability); 1417 ta.addEventListener("input", scheduleDraftSave); 1418 ta.addEventListener("keydown", function(e) { 1419 if (e.key !== "Enter" || e.shiftKey || e.isComposing) { 1420 return; 1421 } 1422 e.preventDefault(); 1423 if (isSubmitting || !isTextValid()) { 1424 if (!isTextValid()) { 1425 alert(form.getAttribute("data-empty-required-msg") || "СообÑение или ÑоÑо обÑзаÑелÑно"); 1426 } 1427 return; 1428 }
1429 submitForm(); 1430 }); 1431 autoGrow.call(ta); 1432 window.addEventListener("pagehide", saveDraft); 1433 document.addEventListener("visibilitychange", function() { 1434 if (document.hidden) { 1435 saveDraft(); 1436 } 1437 }); 1438 } 1439 var closePhotoModal = function() { 1440 if (photoModal && window.AppUi) { 1441 window.AppUi.hideModal(photoModal); 1442 } 1443 }; 1444 var openPhotoModal = function() { 1445 if (photoModal && window.AppUi) { 1446 window.AppUi.showModal(photoModal); 1447 } 1448 }; 1449 var clearPreviewUrls = function() { 1450 previewUrls.forEach(function(url) { 1451 URL.revokeObjectURL(url); 1452 }); 1453 previewUrls = []; 1454 }; 1455 var resetPhotoSelection = function() { 1456 if (fileInput) { 1457 fileInput.value = ""; 1458 } 1459 if (captionInput) { 1460 captionInput.value = ""; 1461 } 1462 if (sharedCaptionInput) { 1463 sharedCaptionInput.value = ""; 1464 } 1465 if (photoGrid) { 1466 photoGrid.innerHTML = ""; 1467 } 1468 form.dataset.photosConfirmed = "0"; 1469 clearPreviewUrls(); 1470 }; 1471 var cancelPhotoSelection = function() { 1472 closePhotoModal(); 1473 resetPhotoSelection(); 1474 }; 1475 var buildPhotoPreview = function(files) { 1476 if (!photoGrid) { 1477 return; 1478 } 1479 photoGrid.innerHTML = ""; 1480 clearPreviewUrls(); 1481 Array.prototype.forEach.call(files, function(file, idx) { 1482 var url = URL.createObjectURL(file); 1483 previewUrls.push(url); 1484 var item = document.createElement("div"); 1485 item.className = "deal-chat-upload__item"; 1486 var img = document.createElement("img"); 1487 img.className = "deal-chat-upload__thumb"; 1488 img.src = url; 1489 img.alt = "photo-" + (idx + 1); 1490 item.appendChild(img); 1491 photoGrid.appendChild(item); 1492 }); 1493 }; 1494 var storeCaption = function() { 1495 if (!captionInput) { 1496 return; 1497 } 1498 captionInput.value = sharedCaptionInput ? (sharedCaptionInput.value || "").trim() : ""; 1499 }; 1500 if (fileInput) { 1501 fileInput.addEventListener("change", function() { 1502 if (isSubmitting) { 1503 this.value = ""; 1504 return; 1505 } 1506 var remaining = parseInt(this.getAttribute("data-remaining") || "0", 10); 1507 if (this.files && this.files.length > remaining) { 1508 var maxPhotosMsgTpl = form.getAttribute("data-max-photos-msg") || "Ðожно вÑбÑаÑÑ Ð½Ðµ более %count% ÑоÑо Ð´Ð»Ñ ÑÑого ÑаÑа"; 1509 alert(maxPhotosMsgTpl.replace("__COUNT__", String(remaining)).replace("%count%", String(remaining))); 1510 this.value = ""; 1511 return; 1512 } 1513 if (this.files && this.files.length > 0) { 1514 buildPhotoPreview(this.files); 1515 openPhotoModal(); 1516 } 1517 }); 1518 } 1519 form.addEventListener("submit", function(e) { 1520 if (isSubmitting || form.dataset.submitting === "1") { 1521 e.preventDefault(); 1522 return; 1523 } 1524 var hasFiles = fileInput && fileInput.files && fileInput.files.length > 0; 1525 if (!isTextValid() && !hasFiles) { 1526 e.preventDefault(); 1527 if (ta) { 1528 ta.focus(); 1529 } 1530 alert(form.getAttribute("data-empty-required-msg") || "СообÑение или ÑоÑо обÑзаÑелÑно"); 1531 return; 1532 } 1533 if (hasFiles && form.dataset.photosConfirmed !== "1") { 1534 e.preventDefault(); 1535 buildPhotoPreview(fileInput.files); 1536 openPhotoModal(); 1537 return; 1538 } 1539 if (!hasFiles && captionInput) { 1540 captionInput.value = ""; 1541 } 1542 if (hasFiles) { 1543 storeCaption(); 1544 } 1545 clearDraft(); 1546 setSubmittingState(true); 1547 }); 1548 if (photoSendBtn) { 1549 photoSendBtn.addEventListener("click", function() { 1550 if (!fileInput || !fileInput.files || fileInput.files.length === 0) { 1551 return; 1552 } 1553 if (isSubmitting) { 1554 return; 1555 } 1556 storeCaption(); 1557 form.dataset.photosConfirmed = "1"; 1558 closePhotoModal();
1559 submitForm(); 1560 }); 1561 } 1562 if (sharedCaptionInput) { 1563 sharedCaptionInput.addEventListener("keydown", function(e) { 1564 if (e.key !== "Enter" || e.shiftKey || e.isComposing) { 1565 return; 1566 } 1567 e.preventDefault(); 1568 if (photoSendBtn && !photoSendBtn.disabled) { 1569 photoSendBtn.click(); 1570 } 1571 }); 1572 } 1573 if (photoCancelBtn) { 1574 photoCancelBtn.addEventListener("click", cancelPhotoSelection); 1575 } 1576 if (photoModal) { 1577 Array.prototype.forEach.call(photoModal.querySelectorAll("[data-chat-photo-cancel]"), function(el) { 1578 el.addEventListener("click", cancelPhotoSelection); 1579 }); 1580 photoModal.addEventListener("hidden.bs.modal", function() { 1581 if (!isSubmitting && form.dataset.photosConfirmed !== "1") { 1582 resetPhotoSelection(); 1583 } 1584 }); 1585 } 1586 document.addEventListener("keydown", function(e) { 1587 if (e.key === "Escape" && photoModal && photoModal.classList.contains("open")) { 1588 cancelPhotoSelection(); 1589 } 1590 }); 1591 updateSendAvailability(); 1592 })(); 1593 // ÐÑгкий поллинг ÑаÑа Ñделки: новÑе ÑообÑÐµÐ½Ð¸Ñ Ð¿Ð¾ÑвлÑÑÑÑÑ Ð±ÐµÐ· пеÑезагÑÑзки. 1594 // УмнÑй, не «ÑÑпой»: паÑза в Ñоне (document.hidden), мгновеннÑй дозапÑÐ¾Ñ Ð¿Ñи 1595 // возвÑаÑе к вкладке/окнÑ, заÑиÑа Ð¾Ñ Ð½Ð°Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿ÑоÑов, деÑÑвÑй ÑндпоинÑ. 1596 (function() { 1597 var list = document.getElementById("deal-chat-list"); 1598 if (!list) { return; } 1599 var pollUrl = list.getAttribute("data-poll-url"); 1600 if (!pollUrl) { return; } 1601 var lastId = parseInt(list.getAttribute("data-last-id"), 10) || 0; 1602 var readTitle = list.getAttribute("data-read-title") || ""; 1603 var POLL_MS = 6000; // вкладка акÑивна â оÑзÑвÑиво 1604 var POLL_HIDDEN_MS = 60000; // вкладка в Ñоне â Ñаз в минÑÑÑ: минимÑм нагÑÑзки, пÑи возвÑаÑе догонÑем ÑÑÐ°Ð·Ñ 1605 var pollTimer = null; 1606 var inFlight = false; 1607 // Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾ новÑÑ ÑообÑениÑÑ ÑобеÑедника: коÑоÑкий звÑк + ÑÑÑÑÑик во вкладке. 1608 var originalTitle = document.title; 1609 var unreadTitle = 0; 1610 var audioCtx = null; 1611 var soundOn = list.getAttribute("data-sound") !== "0"; 1612 // РазблокиÑовка аÑдио по пеÑÐ²Ð¾Ð¼Ñ Ð¶ÐµÑÑÑ Ð¿Ð¾Ð»ÑзоваÑÐµÐ»Ñ (полиÑика авÑÐ¾Ð¿Ð»ÐµÑ Ð±ÑаÑзеÑов). 1613 function unlockAudio() { 1614 try { 1615 var Ctx = window.AudioContext || window.webkitAudioContext; 1616 if (Ctx && !audioCtx) { audioCtx = new Ctx(); } 1617 if (audioCtx && audioCtx.state === "suspended") { audioCtx.resume(); } 1618 } catch (e) {} 1619 document.removeEventListener("pointerdown", unlockAudio); 1620 document.removeEventListener("keydown", unlockAudio); 1621 } 1622 document.addEventListener("pointerdown", unlockAudio); 1623 document.addEventListener("keydown", unlockAudio); 1624 function playDing() { 1625 if (!soundOn) { return; } 1626 try { 1627 var Ctx = window.AudioContext || window.webkitAudioContext; 1628 if (!Ctx) { return; } 1629 if (!audioCtx) { audioCtx = new Ctx(); } 1630 if (audioCtx.state === "suspended") { audioCtx.resume(); } 1631 var now = audioCtx.currentTime; 1632 // Ðва коÑоÑÐºÐ¸Ñ Ð¼ÑÐ³ÐºÐ¸Ñ Ñона â ненавÑзÑивÑй «динÑ». 1633 [[880, 0], [1174.7, 0.09]].forEach(function(pair) { 1634 var osc = audioCtx.createOscillator(); 1635 var gain = audioCtx.createGain(); 1636 osc.type = "sine"; 1637 osc.frequency.value = pair[0]; 1638 var t = now + pair[1]; 1639 gain.gain.setValueAtTime(0.0001, t); 1640 gain.gain.exponentialRampToValueAtTime(0.14, t + 0.012); 1641 gain.gain.exponentialRampToValueAtTime(0.0001, t + 0.19); 1642 osc.connect(gain); 1643 gain.connect(audioCtx.destination); 1644 osc.start(t); 1645 osc.stop(t + 0.22); 1646 }); 1647 } catch (e) {} 1648 } 1649 // ÐодÑвеÑка вкладки: помимо ÑÑÑÑÑика в заголовке â кÑаÑнÑй бейдж повеÑÑ favicon. 1650 var faviconLinks = Array.prototype.slice.call( 1651 document.querySelectorAll('link[rel="icon"], link[rel="shortcut icon"]') 1652 ); 1653 var faviconOriginal = faviconLinks.map(function(l) { return l.getAttribute("href"); }); 1654 var faviconBadged = false;
1655 var dangerColor = (getComputedStyle(document.documentElement) 1656 .getPropertyValue("--ua-danger") || "").trim() || "#CC3F3F"; 1657 function setFaviconBadge(count) { 1658 if (!faviconLinks.length) { return; } 1659 try { 1660 var size = 32; 1661 var canvas = document.createElement("canvas"); 1662 canvas.width = size; 1663 canvas.height = size; 1664 var ctx = canvas.getContext("2d"); 1665 ctx.beginPath(); 1666 ctx.arc(size / 2, size / 2, size / 2 - 1, 0, Math.PI * 2); 1667 ctx.fillStyle = dangerColor; 1668 ctx.fill(); 1669 var label = count > 9 ? "9+" : String(count); 1670 ctx.fillStyle = "#ffffff"; 1671 ctx.font = "bold " + (count > 9 ? 15 : 20) + "px Arial, sans-serif"; 1672 ctx.textAlign = "center"; 1673 ctx.textBaseline = "middle"; 1674 ctx.fillText(label, size / 2, size / 2 + 1); 1675 var url = canvas.toDataURL("image/png"); 1676 faviconLinks.forEach(function(l) { l.setAttribute("href", url); }); 1677 faviconBadged = true; 1678 } catch (e) {} 1679 } 1680 function restoreFavicon() { 1681 if (!faviconBadged) { return; } 1682 faviconLinks.forEach(function(l, i) { 1683 if (faviconOriginal[i] != null) { l.setAttribute("href", faviconOriginal[i]); } 1684 }); 1685 faviconBadged = false; 1686 } 1687 function bumpTitle(n) { 1688 unreadTitle += n; 1689 document.title = "(" + unreadTitle + ") " + originalTitle; 1690 setFaviconBadge(unreadTitle); 1691 } 1692 function resetTitle() { 1693 if (unreadTitle === 0) { restoreFavicon(); return; } 1694 unreadTitle = 0; 1695 document.title = originalTitle; 1696 restoreFavicon(); 1697 } 1698 // СÑиÑаем ÑолÑко ÑеалÑнÑе ÑообÑÐµÐ½Ð¸Ñ ÑобеÑедника (не ÑиÑÑемнÑе/аÑбиÑÑаж/оÑзÑвÑ). 1699 function countPeerRows(rows) { 1700 var c = 0; 1701 rows.forEach(function(row) { 1702 var cl = row.classList; 1703 if (cl && cl.contains("theirs") 1704 && !cl.contains("system-notification") 1705 && !cl.contains("admin-intervention") 1706 && !cl.contains("review-notification")) { 1707 c += 1; 1708 } 1709 }); 1710 return c; 1711 } 1712 function nearBottom() { 1713 return list.scrollHeight - list.scrollTop - list.clientHeight < 140; 1714 } 1715 function scrollDown() { 1716 list.scrollTop = list.scrollHeight; 1717 } 1718 function appendHtml(html) { 1719 var wasNearBottom = nearBottom(); 1720 var empty = list.querySelector(".deal-chat__empty"); 1721 if (empty && empty.parentNode) { 1722 empty.parentNode.removeChild(empty); 1723 } 1724 var tmp = document.createElement("div"); 1725 tmp.innerHTML = html; 1726 var added = []; 1727 while (tmp.firstChild) { 1728 var node = tmp.firstChild; 1729 list.appendChild(node); 1730 if (node.nodeType === 1) { 1731 added.push(node); 1732 } 1733 } 1734 if (wasNearBottom) { 1735 scrollDown(); 1736 } 1737 added.forEach(function(row) { 1738 Array.prototype.forEach.call(row.querySelectorAll("img"), function(img) { 1739 if (img.complete) { return; } 1740 img.addEventListener("load", function() { 1741 if (wasNearBottom) { scrollDown(); } 1742 }, { once: true }); 1743 }); 1744 }); 1745 return added; 1746 } 1747 function applyReadReceipts(ids) { 1748 ids.forEach(function(id) { 1749 var row = list.querySelector('.deal-chat__row[data-message-id="' + id + '"]'); 1750 if (!row) { return; } 1751 var mark = row.querySelector('.deal-chat__read[data-read="0"]'); 1752 if (!mark) { return; } 1753 mark.setAttribute("data-read", "1"); 1754 if (readTitle) { mark.setAttribute("title", readTitle); } 1755 }); 1756 } 1757 function scheduleNext() { 1758 window.clearTimeout(pollTimer); 1759 // Ð Ñоне опÑаÑиваем Ñеже (нагÑÑзка мала), но ÐРвÑклÑÑаем â инаÑе не бÑÐ´ÐµÑ Ð·Ð²Ñка/ÑÑÑÑÑика. 1760 pollTimer = window.setTimeout(poll, document.hidden ? POLL_HIDDEN_MS : POLL_MS); 1761 } 1762 function pollNow() { 1763 window.clearTimeout(pollTimer); 1764 poll(); 1765 } 1766 function poll() { 1767 if (inFlight) { return; } 1768 inFlight = true; 1769 var url = pollUrl + (pollUrl.indexOf("?") === -1 ? "?" : "&") + "after=" + encodeURIComponent(lastId); 1770 fetch(url, { 1771 headers: { "X-Requested-With": "XMLHttpRequest" }, 1772 credentials: "same-origin" 1773 }).then(function(resp) { 1774 return resp.ok ? resp.json() : null; 1775 }).then(function(data) { 1776 if (data && data.reload) { 1777 // ÐоÑÑÑп к ÑаÑÑ Ð·Ð°ÐºÑÑлÑÑ (напÑимеÑ, баÐ
1777»Ð°Ð½Ñ ÑÑÑл в минÑÑ) â показÑваем пÑиÑинÑ. 1778 window.location.reload(); 1779 return; 1780 } 1781 if (data && data.ok) { 1782 if (typeof data.lastId === "number" && data.lastId > lastId) { 1783 lastId = data.lastId; 1784 list.setAttribute("data-last-id", String(lastId)); 1785 } 1786 if (data.html) { 1787 var addedRows = appendHtml(data.html); 1788 var peerCount = countPeerRows(addedRows); 1789 if (peerCount > 0) { 1790 playDing(); 1791 if (document.hidden) { bumpTitle(peerCount); } 1792 } 1793 } 1794 if (data.readReceipts && data.readReceipts.length) { 1795 applyReadReceipts(data.readReceipts); 1796 } 1797 } 1798 }).catch(function() {}).then(function() { 1799 inFlight = false; 1800 scheduleNext(); 1801 }); 1802 } 1803 document.addEventListener("visibilitychange", function() { 1804 if (!document.hidden) { resetTitle(); pollNow(); } 1805 else { scheduleNext(); } 1806 }); 1807 window.addEventListener("focus", function() { resetTitle(); pollNow(); }); 1808 pollNow(); 1809 })(); 1810}); 1811 1812(function() { 1813 function ready(fn) { 1814 if (document.readyState !== "loading") { 1815 fn(); 1816 } else { 1817 document.addEventListener("DOMContentLoaded", fn); 1818 } 1819 } 1820 ready(function() { 1821 var modalEl = document.getElementById("appConfirmModal"); 1822 var titleEl = document.getElementById("appConfirmTitle"); 1823 var msgEl = document.getElementById("appConfirmMessage"); 1824 var okBtn = document.getElementById("appConfirmOkBtn"); 1825 var cancelBtn = document.getElementById("appConfirmCancelBtn"); 1826 var defaultCancelLabel = cancelBtn ? cancelBtn.textContent.trim() : "ÐÑмена"; 1827 var defaultConfirmLabel = okBtn ? okBtn.textContent.replace(/^[^\S\r\n]*[\u2713ââ\s]*\s*/, "").trim() : "ÐодÑвеÑдиÑÑ"; 1828 function hasBootstrapModal() { 1829 return !!(window.jQuery && typeof jQuery.fn.modal === "function" && modalEl); 1830 } 1831 window.showConfirm = function(opts) { 1832 opts = opts || {}; 1833 var title = opts.title || (titleEl ? titleEl.textContent : "ÐодÑвеÑждение"); 1834 var messageHtml = opts.messageHtml || ""; 1835 var confirmLabel = opts.confirmLabel || defaultConfirmLabel; 1836 var cancelLabel = opts.cancelLabel || defaultCancelLabel; 1837 var variant = opts.variant || "default"; 1838 if (!hasBootstrapModal()) { 1839 var plain = messageHtml.replace(/<[^>]+>/g, ""); 1840 return Promise.resolve(window.confirm(plain)); 1841 } 1842 return new Promise(function(resolve) { 1843 if (titleEl) titleEl.textContent = title; 1844 if (msgEl) msgEl.innerHTML = messageHtml; 1845 if (okBtn) okBtn.innerHTML = '<span class="fa-solid ' + (variant === "danger" ? "fa-trash-can" : "fa-check") + '" aria-hidden="true"></span> ' + confirmLabel; 1846 if (cancelBtn) cancelBtn.textContent = cancelLabel; 1847 modalEl.classList.remove("app-confirm-modal--danger"); 1848 if (variant === "danger") { 1849 modalEl.classList.add("app-confirm-modal--danger"); 1850 } 1851 var $m = jQuery(modalEl); 1852 var resolved = false; 1853 function cleanup() { 1854 $m.off("hidden.bs.modal", onHide); 1855 if (okBtn) okBtn.removeEventListener("click", onOk); 1856 modalEl.classList.remove("app-confirm-modal--danger"); 1857 } 1858 function onHide() { 1859 cleanup(); 1860 if (!resolved) resolve(false); 1861 } 1862 function onOk() { 1863 resolved = true; 1864 window.AppUi.hideModal($m[0]); 1865 cleanup(); 1866 resolve(true); 1867 } 1868 $m.on("hidden.bs.modal", onHide); 1869 if (okBtn) okBtn.addEventListener("click", onOk, { 1870 once: true 1871 }); 1872 window.AppUi.showModal($m[0]); 1873 }); 1874 }; 1875 document.addEventListener("click", function(e) { 1876 var trigger = e.target.closest("[data-confirm]"); 1877 if (!trigger) return; 1878 var isButton = trigger.matches('button, input[type="submit"], .btn'); 1879 var isLink = trigger.matches("a[href]"); 1880 var form = trigger.closest("form"); 1881 e.preventDefault(); 1882 var message = trigger.getAttribute("data-confirm") || ""; 1883 var title = trigger.getAttribute("data-confirm-title") || undefined; 1884 var confirmLabel = trigger.getAttribute("data-confirm-label") || undefined; 1885 var cancelLabel = trigger.getAttribute("data-confirm-cancel-label") || undefined; 1886 window.showConfirm({ 1887 title: title, 1888 messageHtml: message, 1889 confirmLabel: confirmLabel, 1890 cancelLabel: cancelLabel 1891 }).then(function(ok) { 1892 if (!ok) return; 1893 if (isLink) { 1894 window.location.href = trigger.getAttribute("href"); 1895 return; 1896 } 1897 if (form) { 1898 form.submit(); 1899 return; 1900 } 1901 }); 1902 }); 1903 document.addEventListener("submit", function(e) { 1904 var form = e.target; 1905 if (!(form instanceof HTMLFormElement)) return; 1906 if (!form.hasAttribute("data-confirm")) return; 1907 e.preventDefault(); 1908 var message = form.getAttribute("data-confirm") || ""; 1909 var title = form.getAttribute("data-confirm-title") || undefined; 1910 var confirmLabel = form.getAttribute("data-confirm-label") || undefined; 1911 var cancelLabel = form.getAttribute("data-confirm-cancel-label") || undefined; 1912 window.showConfirm({ 1913 title: title, 1914 messageHtml: message, 1915 confirmLabel: confirmLabel, 1916 cancelLabel: cancelLabel 1917 }).then(function(ok) { 1918 if (ok) form.submit(); 1919 }); 1920 }); 1921 }); 1922})(); 1923 1924(function() { 1925 function ready(fn) { 1926 if (document.readyState !== "loading") { 1927 fn(); 1928 } else { 1929 document.addEventListener("DOMContentLoaded", fn); 1930 } 1931 } 1932 ready(function() { 1933 var form = document.querySelector(".js-deal-delivery-form"); 1934 if (!form) { 1935 return; 1936 } 1937 var method = form.querySelector("#delivery_method"); 1938 var paymentGroup = form.querySelector("[data-payment-method-group]"); 1939 var paymentInput = form.querySelector("#payment_method"); 1940 var npGroup = form.querySelector("#np_branch_group"); 1941 var ukrGroup = form.querySelector("#ukrposhta_index_group"); 1942 var npInput = form.querySelector("#delivery_np_branch"); 1943 var ukrInput = form.querySelector("#delivery_ukrposhta_index"); 1944 var submitBtn = form.querySelector(".deal-delivery__submit"); 1945 var submitLabel = submitBtn ? submitBtn.querySelector(".deal-delivery__submit-label") : null; 1946 var initialSubmitLabel = submitLabel ? submitLabel.textContent : ""; 1947 var savingLabel = form.getAttribute("data-saving-label") || initialSubmitLabel;
1948 if (!method) { 1949 return; 1950 } 1951 function toggleGroup(group, visible) { 1952 if (!group) { 1953 return; 1954 } 1955 group.hidden = !visible; 1956 group.classList.toggle("is-hidden", !visible); 1957 group.setAttribute("aria-hidden", visible ? "false" : "true"); 1958 } 1959 function syncVisibility() { 1960 var value = method.value; 1961 var isNp = value === "nova_poshta"; 1962 var isUkr = value === "ukrposhta"; 1963 var needsPayment = value !== "" && value !== "meetup"; 1964 toggleGroup(npGroup, isNp); 1965 toggleGroup(ukrGroup, isUkr); 1966 if (npInput) { 1967 npInput.required = isNp; 1968 } 1969 if (ukrInput) { 1970 ukrInput.required = isUkr; 1971 } 1972 toggleGroup(paymentGroup, needsPayment); 1973 if (paymentInput) { 1974 paymentInput.required = needsPayment; 1975 paymentInput.disabled = !needsPayment; 1976 } 1977 } 1978 method.addEventListener("change", syncVisibility); 1979 syncVisibility(); 1980 form.addEventListener("submit", function() { 1981 if (!submitBtn || submitBtn.classList.contains("is-loading")) { 1982 return; 1983 } 1984 submitBtn.classList.add("is-loading"); 1985 submitBtn.disabled = true; 1986 if (submitLabel && savingLabel) { 1987 submitLabel.textContent = savingLabel; 1988 } 1989 }); 1990 }); 1991})(); 1992 1993/* ÐÑенка Ñделки (deals/{id}/review): ÑоÑÑоÑние по вÑбÑанной оÑенке, бÑÑÑÑÑе ÑÑазÑ, ÑÑÑÑÑик, пÑовеÑка пеÑед оÑпÑавкой. 1994 Ðид (Ð¿Ð°Ð½ÐµÐ»Ñ Ð½ÐµÐ³Ð°Ñива, Ð½Ð°Ð±Ð¾Ñ ÑÑаз, подпиÑи кнопки) пеÑеклÑÑаеÑÑÑ CSS по data-score ÑоÑмÑ. */ 1995(function() { 1996 function ready(fn) { 1997 if (document.readyState !== "loading") { 1998 fn(); 1999 } else { 2000 document.addEventListener("DOMContentLoaded", fn); 2001 } 2002 } 2003 function escapeRegExp(value) { 2004 return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); 2005 } 2006 // Ðлина в ÑÐ¸Ð¼Ð²Ð¾Ð»Ð°Ñ , как ÐµÑ ÑÑиÑÐ°ÐµÑ ÑеÑÐ²ÐµÑ (mb_strlen), а не в UTF-16-единиÑÐ°Ñ . 2007 function charLength(value) { 2008 return Array.from(value).length; 2009 } 2010 ready(function() { 2011 var form = document.querySelector(".js-deal-review"); 2012 if (!form) { 2013 return; 2014 } 2015 var radios = form.querySelectorAll(".js-review-score"); 2016 var text = form.querySelector(".js-review-text"); 2017 var chips = form.querySelectorAll(".js-review-chip"); 2018 var counter = form.querySelector(".js-review-counter"); 2019 var requirement = form.querySelector(".js-review-requirement"); 2020 var live = form.querySelector(".js-review-live"); 2021 var scoreField = form.querySelector(".deal-review__field--score"); 2022 var scoreError = form.querySelector(".js-review-score-error"); 2023 var textError = form.querySelector(".js-review-text-error"); 2024 var submitBtn = form.querySelector(".js-review-submit"); 2025 if (!text || !submitBtn || !radios.length) { 2026 return; 2027 } 2028 var maxLength = parseInt(text.getAttribute("maxlength"), 10) || 255; 2029 var negativeMin = parseInt(form.getAttribute("data-negative-min-length"), 10) || 0; 2030 var defaultPlaceholder = text.getAttribute("data-placeholder-default") || ""; 2031 var lastAnnouncedRemaining = null; 2032 2033 // ÐÑовеÑÐºÑ Ð²ÐµÐ´Ñм Ñами: наÑивнÑе пÑзÑÑи не ÑмеÑÑ Â«Ð¾Ð±ÑзаÑелÑно ÑолÑко Ð´Ð»Ñ Ð½ÐµÐ³Ð°Ñива». 2034 form.noValidate = true; 2035 form.classList.add("is-enhanced"); 2036 2037 function checkedRadio() { 2038 for (var i = 0; i < radios.length; i++) { 2039 if (radios[i].checked) { 2040 return radios[i]; 2041 } 2042 } 2043 return null; 2044 } 2045 function scoreKey() { 2046 var radio = checkedRadio(); 2047 return radio ? radio.getAttribute("data-key") : ""; 2048 } 2049 function showError(box, message, target) { 2050 if (!box) { 2051 return; 2052 } 2053 var textNode = box.querySelector(".js-review-error-text"); 2054 if (textNode) { 2055 textNode.textContent = message; 2056 } 2057 box.hidden = false;
2058 if (target) { 2059 target.setAttribute("aria-describedby", box.id); 2060 } 2061 if (box.parentElement) { 2062 box.parentElement.classList.add("is-invalid"); 2063 } 2064 } 2065 function hideError(box, target) { 2066 if (!box || box.hidden) { 2067 return; 2068 } 2069 box.hidden = true; 2070 if (target) { 2071 target.removeAttribute("aria-describedby"); 2072 } 2073 if (box.parentElement) { 2074 box.parentElement.classList.remove("is-invalid"); 2075 } 2076 } 2077 function negativeTextMissing() { 2078 return scoreKey() === "neg" && charLength(text.value.trim()) < negativeMin; 2079 } 2080 function syncChips() { 2081 Array.prototype.forEach.call(chips, function(chip) { 2082 var phrase = chip.getAttribute("data-phrase") || ""; 2083 chip.setAttribute("aria-pressed", phrase && text.value.indexOf(phrase) !== -1 ? "true" : "false"); 2084 }); 2085 } 2086 function syncCounter() { 2087 var length = text.value.length; 2088 var remaining = maxLength - length; 2089 if (counter) { 2090 counter.textContent = length + " / " + maxLength; 2091 counter.classList.toggle("is-near", remaining <= 20 && remaining > 0); 2092 counter.classList.toggle("is-full", remaining <= 0); 2093 } 2094 if (requirement) { 2095 requirement.classList.toggle("is-met", charLength(text.value.trim()) >= negativeMin); 2096 } 2097 // СкÑинÑидеÑÑ ÑообÑаем ÑолÑко о пÑиближении к пÑеделÑ, а не о каждом Ñимволе. 2098 var bucket = remaining <= 0 ? 0 : (remaining <= 20 ? 20 : null); 2099 if (live && bucket !== lastAnnouncedRemaining) { 2100 lastAnnouncedRemaining = bucket; 2101 if (bucket !== null) { 2102 live.textContent = (form.getAttribute("data-live-remaining") || "").replace("%count%", String(Math.max(remaining, 0))); 2103 } 2104 } 2105 } 2106 function syncScore() { 2107 var radio = checkedRadio(); 2108 var key = radio ? radio.getAttribute("data-key") : ""; 2109 form.setAttribute("data-score", key); 2110 text.placeholder = radio ? (radio.getAttribute("data-placeholder") || defaultPlaceholder) : defaultPlaceholder; 2111 text.required = key === "neg"; 2112 if (key) { 2113 hideError(scoreError, scoreField); 2114 } 2115 if (!negativeTextMissing()) { 2116 hideError(textError, text); 2117 text.removeAttribute("aria-invalid"); 2118 } 2119 } 2120 function addPhrase(value, phrase) { 2121 var base = value.replace(/\s+$/, ""); 2122 if (!base) { 2123 return phrase + "."; 2124 } 2125 return base + (/[.!?â¦]$/.test(base) ? " " : ". ") + phrase + "."; 2126 } 2127 function removePhrase(value, phrase) { 2128 return value 2129 .replace(new RegExp("\\s*" + escapeRegExp(phrase) + "[.!?â¦]?"), "") 2130 .replace(/^\s+/, ""); 2131 } 2132 2133 Array.prototype.forEach.call(radios, function(radio) { 2134 radio.addEventListener("change", syncScore); 2135 }); 2136 2137 Array.prototype.forEach.call(chips, function(chip) { 2138 chip.addEventListener("click", function() { 2139 var phrase = chip.getAttribute("data-phrase") || ""; 2140 if (!phrase) { 2141 return; 2142 } 2143 var next = text.value.indexOf(phrase) !== -1 ? removePhrase(text.value, phrase) : addPhrase(text.value, phrase); 2144 if (next.length > maxLength) { 2145 if (live) { 2146 live.textContent = form.getAttribute("data-error-no-room") || ""; 2147 } 2148 if (counter) { 2149 counter.classList.remove("is-shake"); 2150 void counter.offsetWidth; 2151 counter.classList.add("is-shake"); 2152 } 2153 return; 2154 } 2155 text.value = next; 2156 text.dispatchEvent(new Event("input", { bubbles: true })); 2157 }); 2158 }); 2159 2160 text.addEventListener("input", function() { 2161 syncChips(); 2162 syncCounter(); 2163 if (!negativeTextMissing()) { 2164 hideError(textError, text); 2165 text.removeAttribute("aria-invalid"); 2166 } 2167 }); 2168 2169 form.addEventListener("submit", function(event) { 2170 if (submitBtn.classList.contains("is-loading")) { 2171 event.preventDefault(); 2172 return; 2173 } 2174 if (!scoreKey()) { 2175 event.preventDefault(); 2176 showError(scoreError, form.getAttribute("data-error-score") || "", scoreField); 2177 radios[0].focus(); 2178 return; 2179 } 2180 if (negativeTextMissing()) { 2181 event.preventDefault(); 2182 showError(textError, form.getAttribute("data-error-negative") || "", text); 2183 text.setAttribute("aria-invalid", "true"); 2184 text.focus(); 2185 return; 2186 } 2187 submitBtn.classList.add("is-loading"); 2188 submitBtn.setAttribute("aria-busy", "true"); 2189 // ÐÐ½Ð¾Ð¿ÐºÑ Ð±Ð»Ð¾ÐºÐ¸ÑÑем поÑле наÑала оÑпÑавки, ÑÑÐ¾Ð±Ñ ÑоÑма ÑÑпела ÑйÑи. 2190 window.setTimeout(function() { 2191 submitBtn.disabled = true; 2192 }, 0); 2193 }); 2194 2195 // ÐозвÑÐ°Ñ Â«Ð½Ð°Ð·Ð°Ð´Â» из иÑÑоÑии Ð¿Ð¾Ð´Ð½Ð¸Ð¼Ð°ÐµÑ ÑÑÑаниÑÑ Ð¸Ð· bfcache Ñ Ð·Ð°Ð²Ð¸ÑÑей загÑÑзкой â ÑбÑаÑÑваем. 2196 window.addEventListener("pageshow", function(event) { 2197 if (event.persisted) { 2198 submitBtn.classList.remove("is-loading"); 2199 submitBtn.removeAttribute("aria-busy"); 2200 submitBtn.disabled = false;
2201 } 2202 }); 2203 2204 syncScore(); 2205 syncChips(); 2206 syncCounter(); 2207 }); 2208})(); 2209 2210function removeComment(el) { 2211 var comment_id = el.data("comment-id"); 2212 var $container = el.closest(".comments-container"); 2213 stopDeletedCommentCountdown(comment_id); 2214 $.ajax({ 2215 type: "POST", 2216 url: $container.data("remove-url") || "/comments/remove-comment", 2217 data: { 2218 comment_id: comment_id, 2219 locale: getCommentLocale(), 2220 _token: $container.data("token") || "" 2221 }, 2222 dataType: "json", 2223 success: function(response) { 2224 if (response.success) { 2225 replaceCommentBlockFromHtml(comment_id, response.html, "comment-updated"); 2226 } else if (response && response.msg) { 2227 alert(response.msg); 2228 } 2229 } 2230 }); 2231} 2232 2233function getCommentI18n(key, fallbackValue) { 2234 var i18n = $("#comment-i18n"); 2235 if (!i18n.length) { 2236 return fallbackValue; 2237 } 2238 return i18n.data(key) || fallbackValue; 2239} 2240 2241function getCommentLocale() { 2242 return getCommentI18n("locale", window.AppLocale || "ru"); 2243} 2244 2245function replaceCommentBlockFromHtml(commentId, html, className) { 2246 if (!html) { 2247 return; 2248 } 2249 stopDeletedCommentCountdown(commentId); 2250 var $new = $("<div/>").html($.trim(html)).children().first(); 2251 if (!$new.length) { 2252 return; 2253 } 2254 if (className) { 2255 $new.addClass(className); 2256 $new.on("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function() { 2257 $(this).removeClass(className); 2258 }); 2259 } 2260 var $current = $('.comments_block .comments[data-comment-id="' + commentId + '"]'); 2261 if ($current.length) { 2262 $current.replaceWith($new); 2263 window.AppUi.initTooltips($new[0]); 2264 if ($new.hasClass("comments-deleted")) { 2265 startDeletedCommentCountdown($new, commentId); 2266 } 2267 } 2268} 2269 2270var commentDeleteCountdownTimers = {}; 2271 2272function stopDeletedCommentCountdown(commentId) { 2273 var id = String(commentId || ""); 2274 if (!id) { 2275 return; 2276 } 2277 if (commentDeleteCountdownTimers[id]) { 2278 clearInterval(commentDeleteCountdownTimers[id]); 2279 delete commentDeleteCountdownTimers[id]; 2280 } 2281} 2282 2283function startDeletedCommentCountdown($comment, commentId) { 2284 if (!$comment || !$comment.length) { 2285 return; 2286 } 2287 var id = String(commentId || $comment.data("comment-id") || ""); 2288 if (!id) { 2289 return; 2290 } 2291 stopDeletedCommentCountdown(id); 2292 var $line = $comment.find(".comment-deleted-line").first(); 2293 if (!$line.length) { 2294 return; 2295 } 2296 var $counter = $line.find(".comment-delete-countdown"); 2297 if (!$counter.length) { 2298 $counter = $('<span class="comment-delete-countdown" aria-live="polite"></span>'); 2299 $line.append($counter); 2300 } 2301 var secondsLeft = 9; 2302 $counter.text(secondsLeft); 2303 commentDeleteCountdownTimers[id] = setInterval(function() { 2304 secondsLeft -= 1; 2305 if (secondsLeft < 0) { 2306 stopDeletedCommentCountdown(id); 2307 $comment.slideUp(180, function() { 2308 $(this).remove(); 2309 }); 2310 return; 2311 } 2312 $counter.text(secondsLeft); 2313 }, 1e3); 2314} 2315 2316function openInlineCommentEditor(commentBlock) { 2317 if (!commentBlock || !commentBlock.length) { 2318 return; 2319 } 2320 if (commentBlock.find(".comment-inline-editor-wrap").length) { 2321 return; 2322 } 2323 var textNode = commentBlock.find(".comment-text-content").first(); 2324 if (!textNode.length) { 2325 return; 2326 } 2327 // data-raw Ñ ÑÐ°Ð½Ð¸Ñ Ð¸ÑÑ Ð¾Ð´Ð½Ñй ÑекÑÑ Ñ Ð¼Ð°ÑкеÑами/пеÑеноÑами ÑÑÑок; инаÑе .text() 2328 // поÑеÑÑÐµÑ ÑоÑмаÑиÑование (Ñеги) и пеÑеноÑÑ (<br>) пÑи ÑедакÑиÑовании. 2329 var rawAttr = textNode.attr("data-raw"); 2330 var textRaw = typeof rawAttr === "string" ? rawAttr : $.trim(textNode.text()); 2331 var votes = commentBlock.find(".comment-vote-box"); 2332 textNode.hide(); 2333 votes.hide(); 2334 var editorFormatAttr = getCommentI18n("can-format", "") ? ' data-format-enabled="1"' : ""; 2335 var editorHtml = [ '<div class="comment-inline-editor-wrap">', '<textarea class="comment-inline-editor form-control" maxlength="2000"' + editorFormatAttr + "></textarea>", '<div class="comment-inline-editor-actions">', '<button type="button" class="btn btn-outline-secondary btn-sm comment-edit-cancel">' + getCommentI18n("edit-cancel", "ÐÑмена") + "</button>", '<button type="button" class="btn btn-primary btn-sm comment-edit-save">' + getCommentI18n("edit-save", "Ð¡Ð¾Ñ ÑаниÑÑ") + "</button>", "</div>", '<div class="comment-inline-editor-error"></div>', "</div>" ].join(""); 2336 var editorWrap = $(editorHtml); 2337 textNode.after(editorWrap); 2338 var editorEl = editorWrap.find(".comment-inline-editor").val(textRaw).get(0); 2339 if (editorEl) { 2340 // ÐÑемиÑм: пÑевÑаÑаем в WYSIWYG-ÑедакÑоÑ; инаÑе обÑÑнÑй textarea. 2341 var rich = window.CommentEditor ? window.CommentEditor.enhance(editorEl) : null; 2342 if (rich) { 2343 rich.focus(); 2344 // каÑеÑка в ÐºÐ¾Ð½ÐµÑ 2345 var range = document.createRange(); 2346 range.selectNodeContents(rich); 2347 range.collapse(false); 2348 var sel = window.getSelection(); 2349 sel.removeAllRanges(); 2350 sel.addRange(range); 2351 } else {
2352 editorEl.focus(); 2353 var caret = editorEl.value.length; 2354 editorEl.setSelectionRange(caret, caret); 2355 } 2356 } 2357} 2358 2359function saveInlineCommentEdit(commentBlock) { 2360 if (!commentBlock || !commentBlock.length) { 2361 return; 2362 } 2363 var commentId = commentBlock.data("comment-id"); 2364 var $container = commentBlock.closest(".comments-container"); 2365 var editorWrap = commentBlock.find(".comment-inline-editor-wrap"); 2366 var textarea = editorWrap.find(".comment-inline-editor"); 2367 var saveBtn = editorWrap.find(".comment-edit-save"); 2368 var errorBox = editorWrap.find(".comment-inline-editor-error"); 2369 var text = $.trim(textarea.val() || ""); 2370 if (!text) { 2371 errorBox.text(getCommentI18n("edit-required", "ÐведиÑе ÑекÑÑ ÑообÑениÑ")); 2372 textarea.focus(); 2373 return; 2374 } 2375 if (saveBtn.prop("disabled")) { 2376 return; 2377 } 2378 saveBtn.prop("disabled", true).addClass("is-loading"); 2379 errorBox.text(""); 2380 $.ajax({ 2381 type: "POST", 2382 url: $container.data("edit-url") || "/comments/edit-comment", 2383 data: { 2384 comment_id: commentId, 2385 text: text, 2386 locale: getCommentLocale(), 2387 _token: $container.data("token") || "" 2388 }, 2389 dataType: "json", 2390 success: function(response) { 2391 if (response && response.success) { 2392 replaceCommentBlockFromHtml(commentId, response.html, "comment-updated"); 2393 } else { 2394 errorBox.text(response && response.msg ? response.msg : getCommentI18n("edit-save-failed", "Ðе ÑдалоÑÑ ÑÐ¾Ñ ÑаниÑÑ ÐºÐ¾Ð¼Ð¼ÐµÐ½ÑаÑий")); 2395 } 2396 }, 2397 error: function(xhr) { 2398 var msg = getCommentI18n("edit-save-failed", "Ðе ÑдалоÑÑ ÑÐ¾Ñ ÑаниÑÑ ÐºÐ¾Ð¼Ð¼ÐµÐ½ÑаÑий"); 2399 if (xhr && xhr.responseJSON && xhr.responseJSON.msg) { 2400 msg = xhr.responseJSON.msg; 2401 } 2402 errorBox.text(msg); 2403 }, 2404 complete: function() { 2405 saveBtn.prop("disabled", false).removeClass("is-loading"); 2406 } 2407 }); 2408} 2409 2410function reportError(el) { 2411 var url = el.find("#report-url").val(); 2412 var type = el.find("#report-type").val(); 2413 var text = el.find("#report-text").val(); 2414 el.find("#report-error").prop("disabled", true).addClass("is-loading"); 2415 $.ajax({ 2416 type: "POST", 2417 url: "/comments/report-error", 2418 data: { 2419 url: url, 2420 type: type, 2421 text: text 2422 }, 2423 dataType: "json", 2424 success: function(response) { 2425 if (response.result) { 2426 el.hide(); 2427 var container = el.closest(".modal-content"); 2428 if (container && container.length) { 2429 container.find("#report-success").css("display", "flex"); 2430 } 2431 var modal = el.closest(".modal"); 2432 if (modal && modal.length) { 2433 modal.off("hidden.bs.modal.reportReset"); 2434 modal.on("hidden.bs.modal.reportReset", function() { 2435 el.show(); 2436 var cont = el.closest(".modal-content"); 2437 if (cont && cont.length) { 2438 cont.find("#report-success").hide(); 2439 } 2440 el.find("#report-text").val(""); 2441 el.find("#report-error").prop("disabled", false).removeClass("is-loading"); 2442 }); 2443 } 2444 } else { 2445 el.find("#report-error").prop("disabled", false).removeClass("is-loading"); 2446 } 2447 }, 2448 error: function() { 2449 el.find("#report-error").prop("disabled", false).removeClass("is-loading"); 2450 } 2451 }); 2452} 2453 2454function likeComment(el) { 2455 var comment_block = el.closest(".comment_text"); 2456 var comment_id = comment_block.attr("id"); 2457 var type = "like"; 2458 if (el.hasClass("dislike_comment")) { 2459 type = "dislike"; 2460 } 2461 $.ajax({ 2462 type: "POST", 2463 url: "/comments/like-comment", 2464 data: { 2465 comment_id: comment_id, 2466 type: type 2467 }, 2468 dataType: "json", 2469 success: function(response) { 2470 if (response.result) { 2471 el.addClass("like-button"); 2472 comment_block.find(".sum_comment_vote").text(response.sum_vote); 2473 comment_block.find(".like-button").removeClass("selected_vote"); 2474 if (response.vote == 1) { 2475 comment_block.find(".like_comment").addClass("selected_vote"); 2476 } else if (response.vote == -1) { 2477 comment_block.find(".dislike_comment").addClass("selected_vote"); 2478 } 2479 } 2480 } 2481 }); 2482} 2483 2484document.addEventListener("click", e => { 2485 const wrapper = e.target.closest(".premium-star-wrapper"); 2486 document.querySelectorAll(".premium-star-wrapper.is-premium-hint-open").forEach(el => el.classList.remove("is-premium-hint-open")); 2487 if (wrapper) { 2488 wrapper.classList.add("is-premium-hint-open"); 2489 setTimeout(() => wrapper.classList.remove("is-premium-hint-open"), 1200); 2490 } 2491}); 2492 2493(function() { 2494 "use strict"; 2495 function formatGrn(cents) {
2496 return (cents / 100).toLocaleString("ru-RU", { 2497 minimumFractionDigits: 2, 2498 maximumFractionDigits: 2 2499 }); 2500 } 2501 function initAuctionForm() { 2502 var auctionFields = document.getElementById("auction-fields"); 2503 if (!auctionFields) { 2504 return; 2505 } 2506 var typeRadios = document.querySelectorAll('input[name$="[listing_type]"]'); 2507 var modeRadios = document.querySelectorAll('input[name$="[auction_duration_mode]"]'); 2508 var fixedOnly = document.querySelectorAll("[data-fixed-only]"); 2509 var presetBlock = auctionFields.querySelector("[data-duration-preset]"); 2510 var customBlock = auctionFields.querySelector("[data-duration-custom]"); 2511 var startInput = auctionFields.querySelector("[data-auction-start]"); 2512 var reserveInput = auctionFields.querySelector("[data-auction-reserve]"); 2513 var stepInput = auctionFields.querySelector('input[name$="[auction_price_step]"]'); 2514 var endsInput = auctionFields.querySelector("[data-auction-ends]"); 2515 var summary = document.getElementById("auction-charge-summary"); 2516 var theForm = auctionFields.closest("form"); 2517 var priceInput = document.querySelector('input[name$="[price]"]'); 2518 var qtyInput = document.querySelector('input[name$="[quantity]"]'); 2519 var balanceCents = parseInt(auctionFields.getAttribute("data-balance-cents"), 10) || 0; 2520 var customFeeCents = parseInt(auctionFields.getAttribute("data-custom-fee-cents"), 10) || 100; 2521 var locked = auctionFields.getAttribute("data-locked") === "1"; 2522 function selectedType() { 2523 var checked = document.querySelector('input[name$="[listing_type]"]:checked'); 2524 return checked ? checked.value : "fixed_price"; 2525 } 2526 function selectedMode() { 2527 var checked = document.querySelector('input[name$="[auction_duration_mode]"]:checked'); 2528 return checked ? checked.value : "preset"; 2529 } 2530 function updateChargeSummary() { 2531 if (!summary) { 2532 return; 2533 } 2534 var customDate = selectedMode() === "custom"; 2535 var reserveGrn = reserveInput ? parseInt(reserveInput.value, 10) || 0 : 0; 2536 var totalCents = (customDate ? customFeeCents : 0) + Math.round(reserveGrn * 100 * .01); 2537 if (selectedType() !== "auction" || totalCents <= 0) { 2538 summary.classList.add("is-hidden"); 2539 summary.innerHTML = ""; 2540 return; 2541 } 2542 summary.classList.remove("is-hidden"); 2543 var insufficient = totalCents > balanceCents; 2544 var html = '<div class="auction-charge-summary__row"><span>' + summary.getAttribute("data-label-total") + ":</span> <strong>" + formatGrn(totalCents) + " гÑн.</strong></div>"; 2545 html += '<div class="auction-charge-summary__row"><span>' + summary.getAttribute("data-label-balance") + ":</span> " + formatGrn(balanceCents) + " гÑн.</div>"; 2546 if (insufficient) { 2547 html += '<div class="auction-charge-summary__warn">' + summary.getAttribute("data-label-insufficient") + "</div>"; 2548 } 2549 summary.innerHTML = html; 2550 summary.classList.toggle("is-insufficient", insufficient); 2551 } 2552 function applyType() { 2553 var isAuction = selectedType() === "auction"; 2554 auctionFields.classList.toggle("is-hidden", !isAuction); 2555 fixedOnly.forEach(function(el) { 2556 el.classList.toggle("is-hidden", isAuction); 2557 }); 2558 if (isAuction) { 2559 if (qtyInput) { 2560 qtyInput.value = 1; 2561 } 2562 syncPriceFromStart(); 2563 } 2564 updateChargeSummary(); 2565 } 2566 function applyMode() { 2567 var custom = selectedMode() === "custom"; 2568 if (presetBlock) { 2569 presetBlock.classList.toggle("is-hidden", custom); 2570 } 2571 if (customBlock) { 2572 customBlock.classList.toggle("is-hidden", !custom); 2573 } 2574 updateChargeSummary(); 2575 } 2576 function syncPriceFromStart() { 2577 if (selectedType() !== "auction" || !priceInput || !startInput) { 2578 return; 2579 } 2580 var v = parseInt(startInput.value, 10); 2581 priceInput.value = v && v > 0 ? v : 1; 2582 } 2583 typeRadios.forEach(function(r) { 2584 r.addEventListener("change", applyType); 2585 }); 2586 if (!locked) { 2587 modeRadios.forEach(function(r) { 2588 r.addEventListener("change", applyMode); 2589 }); 2590 if (startInput) { 2591 startInput.addEventListener("input", function() { 2592 syncPriceFromStart(); 2593 updateChargeSummary(); 2594 }); 2595 } 2596 if (reserveInput) { 2597 reserveInput.addEventListener("input", updateChargeSummary); 2598 } 2599 if (endsInput) { 2600 endsInput.addEventListener("click", function() { 2601 if (typeof endsInput.showPicker === "function") { 2602 try { 2603 endsInput.showPicker(); 2604 } catch (err) {} 2605 } 2606 }); 2607 } 2608 } 2609 applyType(); 2610 applyMode(); 2611 } 2612 function initAuctionTimer() { 2613 var card = document.querySelector("[data-auction]"); 2614 if (!card) { 2615 return; 2616 } 2617 var endsAtRaw = card.getAttribute("data-ends-at"); 2618 var timerEl = card.querySelector("[data-auction-timer]"); 2619 if (!endsAtRaw || !timerEl) { 2620 return; 2621 } 2622 if (isNaN(new Date(endsAtRaw).getTime())) { 2623 return; 2624 } 2625 var endedLabel = card.getAttribute("data-auction-ended-label") || ""; 2626 var timeUpSent = false;
2627 function tick() { 2628 // ÐÐ°Ð½ÐµÐ»Ñ Ñже ÑменилаÑÑ Ð¸Ñогом ÑоÑгов â ÑаймеÑÑ Ð±Ð¾Ð»ÑÑе неÑего показÑваÑÑ. 2629 if (!timerEl.isConnected) { 2630 return; 2631 } 2632 // ÐÑÑибÑÑ ÑиÑаем каждÑй Ñик: анÑиÑнайпинг мог пÑодлиÑÑ ÑоÑги, 2633 // и живое обновление Ñже положило ÑÑда новое вÑемÑ. 2634 var endsAt = new Date(card.getAttribute("data-ends-at")).getTime(); 2635 if (isNaN(endsAt)) { 2636 return; 2637 } 2638 var diff = endsAt - Date.now(); 2639 if (diff <= 0) { 2640 timerEl.textContent = endedLabel || "00:00:00"; 2641 // ÐÑÐµÐ¼Ñ Ð²ÑÑло: пÑÑÑÑ Ð¶Ð¸Ð²Ð¾Ðµ обновление ÑÑÐ°Ð·Ñ ÑпÑоÑÐ¸Ñ Ð¸Ñог, не дожидаÑÑÑ Ñвоего опÑоÑа. 2642 if (!timeUpSent) { 2643 timeUpSent = true; 2644 card.dispatchEvent(new CustomEvent("auction:timeup")); 2645 } 2646 window.setTimeout(tick, 1e3); 2647 return; 2648 } 2649 timeUpSent = false; 2650 var d = Math.floor(diff / 864e5); 2651 var h = Math.floor(diff % 864e5 / 36e5); 2652 var m = Math.floor(diff % 36e5 / 6e4); 2653 var s = Math.floor(diff % 6e4 / 1e3); 2654 function pad(n) { 2655 return n < 10 ? "0" + n : "" + n; 2656 } 2657 timerEl.textContent = (d > 0 ? d + "д " : "") + pad(h) + ":" + pad(m) + ":" + pad(s); 2658 window.setTimeout(tick, 1e3); 2659 } 2660 tick(); 2661 } 2662 // Ðивое ÑоÑÑоÑние аÑкÑиона: Ñена, ÑиÑло ÑÑавок, ÑÐ°Ð¹Ð¼ÐµÑ Ð¸ зона дейÑÑÐ²Ð¸Ñ 2663 // (Ð¿Ð°Ð½ÐµÐ»Ñ Ð»Ð¸Ð´ÐµÑа / ÑоÑма ÑÑавки) обновлÑÑÑÑÑ Ñами, как ÑÐ°Ñ Ñделки. 2664 // ÐпÑÐ¾Ñ Ð´ÐµÑÑвÑй: ÑеÑÐ²ÐµÑ Ð¾ÑдаÑÑ ÐºÐ»ÑÑ ÑоÑÑоÑÐ½Ð¸Ñ Ð¸, пока он не изменилÑÑ, 2665 // не ÑендеÑÐ¸Ñ ÑазмеÑÐºÑ Ð²Ð¾Ð²Ñе. 2666 function initAuctionLiveState() { 2667 var card = document.querySelector("[data-auction]"); 2668 var url = card ? card.getAttribute("data-auction-state-url") : ""; 2669 if (!url) { 2670 return; 2671 } 2672 // У завеÑÑÑннÑÑ ÑоÑгов ÑÐµÐ½Ñ Ð¸ ÑоÑÐ¼Ñ ÑÑавки Ñже Ð½ÐµÑ â ÑолÑко Ð¿Ð°Ð½ÐµÐ»Ñ Ð¸Ñога. 2673 var priceBlock = card.querySelector(".auction-lot-panel__price"); 2674 var btn = priceBlock ? priceBlock.querySelector("[data-auction-refresh]") : null; 2675 var priceEl = priceBlock ? priceBlock.querySelector("[data-auction-price]") : null; 2676 var bidsEl = priceBlock ? priceBlock.querySelector("[data-auction-bids]") : null; 2677 var captionEl = priceBlock ? priceBlock.querySelector("[data-auction-caption]") : null; 2678 var reserveEl = priceBlock ? priceBlock.querySelector("[data-auction-reserve]") : null; 2679 var actionEl = document.querySelector("[data-auction-action]"); 2680 var noteEl = document.querySelector("[data-auction-live-note]"); 2681 var noteTextEl = noteEl ? noteEl.querySelector("[data-auction-live-note-text]") : null; 2682 var noteApplyBtn = noteEl ? noteEl.querySelector("[data-auction-live-apply]") : null; 2683 var announcer = document.querySelector("[data-auction-announcer]"); 2684 2685 var POLL_MS = 5000; // вкладка акÑивна â обновлÑем Ñаз в 5 ÑекÑнд 2686 var POLL_HIDDEN_MS = 60000; // в Ñоне Ñеже: Ð»Ð¾Ñ Ñам ÑÐµÐ±Ñ Ð½Ðµ ÑоÑгÑÐµÑ 2687 var POLL_CLOSING_MS = 30000; // иÑог Ñже показан, ждÑм, пока кÑон закÑÐ¾ÐµÑ ÑоÑги и ÑоздаÑÑ ÑÐ´ÐµÐ»ÐºÑ 2688 var SPIN_MS = 600; // минималÑное вÑÐµÐ¼Ñ Ð²ÑаÑениÑ, ÑÑÐ¾Ð±Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ðµ бÑло замеÑно 2689 2690 var pollTimer = null; 2691 var inFlight = false; 2692 var stopped = false; 2693 // ÐлÑÑ ÑоÑÑоÑниÑ, Ñ ÐºÐ¾ÑоÑÑм оÑÑиÑована ÑÑÑаниÑа: пеÑвÑй опÑÐ¾Ñ Ð½Ðµ пеÑеÑиÑÑÐµÑ Ñо же Ñамое. 2694 var stateKey = card.getAttribute("data-auction-state-key") || ""; 2695 var pendingHtml = null; 2696 var awaitingClose = !!card.querySelector("[data-auction-result]"); 2697 2698 function setSpinning(on) { 2699 if (btn) { 2700 btn.classList.toggle("is-refreshing", on); 2701 } 2702 } 2703 2704 // Ðока Ñеловек Ð²Ð²Ð¾Ð´Ð¸Ñ ÑÑавкÑ, подменÑÑÑ ÑоÑÐ¼Ñ Ð¿Ð¾Ð´ ÑÑками Ð
2704½ÐµÐ»ÑзÑ. 2705 function actionBusy() { 2706 if (!actionEl) { 2707 return false; 2708 } 2709 var active = document.activeElement; 2710 if (active && actionEl.contains(active)) { 2711 return true; 2712 } 2713 if (actionEl.querySelector(".auction-bid-confirm-panel")) { 2714 return true; 2715 } 2716 var inputs = actionEl.querySelectorAll('input[name="amount"], input[name="max_amount"]'); 2717 for (var i = 0; i < inputs.length; i++) { 2718 if (inputs[i].getAttribute("data-touched") === "1") { 2719 return true; 2720 } 2721 } 2722 return false; 2723 } 2724 2725 function markTouchedOnInput() { 2726 if (!actionEl) { 2727 return; 2728 } 2729 actionEl.addEventListener("input", function(e) { 2730 var target = e.target; 2731 if (target && target.name && (target.name === "amount" || target.name === "max_amount")) { 2732 target.setAttribute("data-touched", "1"); 2733 } 2734 }); 2735 } 2736 2737 function currentFormMin() { 2738 if (!actionEl) { 2739 return 0; 2740 } 2741 var input = actionEl.querySelector('input[name="amount"]'); 2742 if (!input) { 2743 return 0; 2744 } 2745 var raw = input.getAttribute("data-bid-min") || input.getAttribute("min") || input.value; 2746 var num = parseInt(raw, 10); 2747 return isNaN(num) ? 0 : num; 2748 } 2749 2750 function hideNote() { 2751 if (!noteEl) { 2752 return; 2753 } 2754 noteEl.hidden = true; 2755 noteEl.classList.add("is-hidden"); 2756 } 2757 2758 function showNote(minNextBid) { 2759 if (!noteEl || !noteTextEl) { 2760 return; 2761 } 2762 var tpl = noteEl.getAttribute("data-note-template") || "СÑавки изменилиÑÑ: минималÑÐ½Ð°Ñ ÑÑавка %min% гÑн"; 2763 noteTextEl.textContent = tpl.replace("%min%", Number(minNextBid || 0).toLocaleString("ru-RU")); 2764 noteEl.hidden = false; 2765 noteEl.classList.remove("is-hidden"); 2766 } 2767 2768 function applyActionHtml(html) { 2769 if (!actionEl || !html) { 2770 return; 2771 } 2772 actionEl.innerHTML = html; 2773 pendingHtml = null; 2774 hideNote(); 2775 initAuctionBidStepper(); 2776 } 2777 2778 // ТоÑги завеÑÑилиÑÑ: иÑог Ñеликом заменÑÐµÑ Ð¿Ð°Ð½ÐµÐ»Ñ â ÑаймеÑ, ÑенÑ, ÑоÑÐ¼Ñ ÑÑавки, избÑанное. 2779 // ФоÑÐ¼Ñ Ð½Ðµ беÑежÑм даже под ÑÑками: ÑÑÐ°Ð²ÐºÑ Ð½Ð° завеÑÑÑннÑй Ð»Ð¾Ñ Ð²ÑÑ Ñавно не пÑинÑÑÑ. 2780 function showResult(html) { 2781 var tpl = document.createElement("template"); 2782 tpl.innerHTML = html; 2783 var next = tpl.content.querySelector("[data-auction-result]"); 2784 var current = card.querySelector("[data-auction-result]"); 2785 // ÐÑон закÑÑл ÑоÑги, но Ð´Ð»Ñ ÑÑого зÑиÑÐµÐ»Ñ Ð² панели ниÑего не поменÑлоÑÑ â не мигаем и не обÑÑвлÑем повÑоÑно. 2786 if (next && current && next.getAttribute("data-result-key") === current.getAttribute("data-result-key")) { 2787 return; 2788 } 2789 card.innerHTML = html; 2790 card.classList.add("auction-lot-panel--finished"); 2791 pendingHtml = null; 2792 var result = card.querySelector("[data-auction-result]"); 2793 if (!result) { 2794 return; 2795 } 2796 result.classList.add("is-revealed"); 2797 if (announcer) { 2798 announcer.textContent = result.getAttribute("data-announce") || ""; 2799 } 2800 } 2801 2802 function applyState(data) { 2803 if (typeof data.resultHtml === "string" && data.resultHtml) { 2804 showResult(data.resultHtml); 2805 awaitingClose = !!data.awaitingClose; 2806 if (!awaitingClose) { 2807 stop(); 2808 } 2809 return; 2810 } 2811 if (priceEl && data.priceFormatted) { 2812 var nextPrice = data.priceFormatted; 2813 if (nextPrice !== priceEl.textContent) { 2814 priceEl.textContent = nextPrice; 2815 priceEl.classList.remove("is-bumped"); 2816 void priceEl.offsetWidth; 2817 priceEl.classList.add("is-bumped"); 2818 } 2819 } 2820 if (bidsEl && typeof data.bidsText === "string") {
2821 bidsEl.textContent = "(" + data.bidsText + ")"; 2822 } 2823 if (captionEl && data.caption) { 2824 captionEl.textContent = data.caption; 2825 } 2826 // ÐÑо ÑезеÑв говоÑим ÑолÑко поÑле завеÑÑÐµÐ½Ð¸Ñ ÑоÑгов и ÑолÑко еÑли он не доÑÑигнÑÑ: 2827 // ÑекÑÑ Ð´Ð°ÑÑ ÑеÑвеÑ, здеÑÑ Ð´Ð¾ÑÑаÑоÑно показаÑÑ Ð¸Ð»Ð¸ ÑпÑÑÑаÑÑ Ð·Ð°Ð¼ÐµÑкÑ. 2828 if (reserveEl) { 2829 var reserveVisible = !!data.reserveText; 2830 reserveEl.textContent = reserveVisible ? data.reserveText : ""; 2831 reserveEl.hidden = !reserveVisible; 2832 reserveEl.classList.toggle("is-hidden", !reserveVisible); 2833 } 2834 // ÐнÑиÑнайпинг мог ÑдвинÑÑÑ Ð¾ÐºÐ¾Ð½Ñание â ÑÐ°Ð¹Ð¼ÐµÑ ÑиÑÐ°ÐµÑ Ð°ÑÑибÑÑ Ð½Ð° каждом Ñике. 2835 if (card && data.endsAt) { 2836 card.setAttribute("data-ends-at", data.endsAt); 2837 } 2838 2839 if (typeof data.actionHtml === "string") { 2840 if (actionBusy()) { 2841 // ФоÑÐ¼Ñ Ð´ÐµÑÐ¶Ð¸Ñ Ñеловек: подменÑÑÑ ÐµÑ Ð½ÐµÐ»ÑзÑ. Ðо дÑÑгаÑÑ ÐµÐ³Ð¾ замеÑкой ÑÑÐ¾Ð¸Ñ 2842 // ÑолÑко Ñогда, когда вÑÑоÑла минималÑÐ½Ð°Ñ ÑÑавка â инаÑе его ÑÑмма вÑÑ ÐµÑÑ 2843 // валидна, и ÑÐ²ÐµÐ¶Ð°Ñ ÑоÑма пÑоÑÑо ждÑÑ Ñвободного моменÑа. 2844 pendingHtml = data.actionHtml; 2845 var nextMin = parseInt(data.minNextBid, 10); 2846 if (!isNaN(nextMin) && nextMin > currentFormMin()) { 2847 showNote(nextMin); 2848 } else { 2849 hideNote(); 2850 } 2851 } else { 2852 applyActionHtml(data.actionHtml); 2853 } 2854 } 2855 2856 if (data.ended && !data.awaitingClose) { 2857 stop(); 2858 } 2859 } 2860 2861 function scheduleNext() { 2862 if (stopped) { 2863 return; 2864 } 2865 window.clearTimeout(pollTimer); 2866 pollTimer = window.setTimeout(poll, document.hidden ? POLL_HIDDEN_MS : (awaitingClose ? POLL_CLOSING_MS : POLL_MS)); 2867 } 2868 2869 function stop() { 2870 stopped = true; 2871 window.clearTimeout(pollTimer); 2872 } 2873 2874 function poll() { 2875 if (inFlight || stopped) { 2876 return; 2877 } 2878 inFlight = true; 2879 setSpinning(true); 2880 2881 var requestUrl = url + (url.indexOf("?") === -1 ? "?" : "&") + "state=" + encodeURIComponent(stateKey); 2882 var minSpin = new Promise(function(resolve) { 2883 window.setTimeout(resolve, SPIN_MS); 2884 }); 2885 var request = fetch(requestUrl, { 2886 headers: { 2887 "X-Requested-With": "XMLHttpRequest" 2888 }, 2889 credentials: "same-origin" 2890 }).then(function(resp) { 2891 return resp.ok ? resp.json() : null; 2892 }).catch(function() { 2893 return null; 2894 }); 2895 2896 Promise.all([ request, minSpin ]).then(function(results) { 2897 var data = results[0]; 2898 if (data && data.ok) { 2899 if (typeof data.stateKey === "string") { 2900 stateKey = data.stateKey; 2901 } 2902 if (data.changed) { 2903 applyState(data); 2904 } else if (data.ended && !data.awaitingClose) { 2905 stop(); 2906 } 2907 } 2908 setSpinning(false); 2909 inFlight = false; 2910 scheduleNext(); 2911 }); 2912 } 2913 2914 function pollNow() { 2915 if (stopped) { 2916 return; 2917 } 2918 window.clearTimeout(pollTimer); 2919 poll(); 2920 } 2921 2922 if (btn) { 2923 btn.addEventListener("click", pollNow); 2924 } 2925 if (noteApplyBtn) { 2926 noteApplyBtn.addEventListener("click", function() { 2927 applyActionHtml(pendingHtml); 2928 }); 2929 } 2930 markTouchedOnInput(); 2931 card.addEventListener("auction:timeup", pollNow); 2932 document.addEventListener("visibilitychange", function() { 2933 if (!document.hidden) { 2934 pollNow(); 2935 } else { 2936 scheduleNext(); 2937 } 2938 }); 2939 window.addEventListener("focus", pollNow); 2940 scheduleNext(); 2941 } 2942 function initAuctionBidStepper() { 2943 var stepper = document.querySelector("[data-bid-stepper]"); 2944 if (!stepper) { 2945 return; 2946 } 2947 var input = stepper.querySelector("[data-bid-amount]"); 2948 if (!input) { 2949 return; 2950 } 2951 var min = parseInt(input.getAttribute("data-bid-min"), 10); 2952 var step = parseInt(input.getAttribute("data-bid-step-value"), 10); 2953 if (isNaN(min)) { 2954 min = 1; 2955 } 2956 if (isNaN(step) || step < 1) { 2957 step = 1; 2958 } 2959 var decBtn = stepper.querySelector(".bid-stepper__btn--dec"); 2960 // Шаг ÑÐµÐ½Ñ â ÑÑо минималÑное повÑÑение, а не ÑеÑка допÑÑÑимÑÑ ÑÑмм:
2961 // пÑи Ñаге 50 ÑÑавка 1060 Ñак же законна, как 1051. Ðнопки ± Ñ Ð¾Ð´ÑÑ Ñагом, 2962 // ÑÑками можно ввеÑÑи лÑбÑÑ ÑÑÐ¼Ð¼Ñ Ð½Ðµ ниже минималÑной. 2963 function sanitize(value) { 2964 if (isNaN(value) || value <= min) { 2965 return min; 2966 } 2967 return Math.round(value); 2968 } 2969 function currentValue() { 2970 return parseInt(input.value, 10); 2971 } 2972 function setValue(value) { 2973 if (value < min) { 2974 value = min; 2975 } 2976 input.value = value; 2977 if (decBtn) { 2978 decBtn.disabled = value <= min; 2979 } 2980 } 2981 function nudge(direction) { 2982 var v = currentValue(); 2983 if (isNaN(v)) { 2984 setValue(min); 2985 return; 2986 } 2987 if (direction > 0) { 2988 setValue(sanitize(v) + step); 2989 } else { 2990 setValue(Math.max(min, sanitize(v) - step)); 2991 } 2992 } 2993 stepper.querySelectorAll("[data-bid-step]").forEach(function(btn) { 2994 btn.addEventListener("click", function() { 2995 nudge(parseInt(btn.getAttribute("data-bid-step"), 10) || 0); 2996 }); 2997 }); 2998 input.addEventListener("change", function() { 2999 setValue(sanitize(currentValue())); 3000 }); 3001 input.addEventListener("blur", function() { 3002 setValue(sanitize(currentValue())); 3003 }); 3004 setValue(sanitize(currentValue())); 3005 } 3006 function initAuctionBidConfirm() { 3007 function escapeHtml(value) { 3008 var div = document.createElement("div"); 3009 div.textContent = value == null ? "" : String(value); 3010 return div.innerHTML; 3011 } 3012 function removeExistingConfirm(form) { 3013 var existing = form.parentNode ? form.parentNode.querySelector(".auction-bid-confirm-panel") : null; 3014 if (existing) { 3015 existing.remove(); 3016 } 3017 } 3018 function submitConfirmed(form) { 3019 form.setAttribute("data-confirmed", "1"); 3020 form.submit(); 3021 } 3022 function attachSlider(panel, form) { 3023 var slider = panel.querySelector("[data-bid-confirm-slider]"); 3024 var handle = panel.querySelector("[data-bid-confirm-handle]"); 3025 var fill = panel.querySelector("[data-bid-confirm-fill]"); 3026 var idleLabel = panel.querySelector(".auction-slide-confirm__label-idle"); 3027 var cancelBtn = panel.querySelector("[data-bid-confirm-cancel]"); 3028 if (!slider || !handle || !fill) { 3029 return; 3030 } 3031 var dragging = false; 3032 var progress = 0; 3033 var confirmed = false; 3034 var confirmThreshold = .92; 3035 var coverThreshold = .6; 3036 function setCancelEnabled(enabled) { 3037 if (cancelBtn) { 3038 cancelBtn.disabled = !enabled; 3039 } 3040 } 3041 function setProgress(nextProgress) { 3042 progress = Math.max(0, Math.min(1, nextProgress)); 3043 var maxLeft = Math.max(0, slider.clientWidth - handle.offsetWidth); 3044 var left = Math.round(maxLeft * progress); 3045 handle.style.transform = "translateX(" + left + "px)"; 3046 fill.style.width = Math.round(progress * 100) + "%"; 3047 slider.classList.toggle("is-ready", progress >= confirmThreshold); 3048 if (idleLabel && !confirmed) { 3049 idleLabel.style.opacity = Math.max(0, Math.min(1, 1 - progress * 1.7)); 3050 } 3051 slider.classList.toggle("is-covering", !confirmed && progress >= coverThreshold); 3052 } 3053 function updateFromClientX(clientX) { 3054 var rect = slider.getBoundingClientRect(); 3055 var maxLeft = Math.max(1, rect.width - handle.offsetWidth); 3056 setProgress((clientX - rect.left - handle.offsetWidth / 2) / maxLeft); 3057 } 3058 function resetSlider() { 3059 if (confirmed) { 3060 return; 3061 } 3062 slider.classList.remove("is-dragging", "is-ready", "is-confirming", "is-covering"); 3063 slider.classList.add("is-teasing"); 3064 setProgress(0); 3065 setCancelEnabled(true); 3066 } 3067 function confirmSlider() { 3068 if (confirmed) { 3069 return; 3070 } 3071 confirmed = true; 3072 setCancelEnabled(false); 3073 slider.classList.remove("is-dragging", "is-teasing", "is-covering"); 3074 slider.classList.add("is-confirming", "is-confirmed"); 3075 if (idleLabel) { 3076 idleLabel.style.opacity = ""; 3077 } 3078 setProgress(1); 3079 window.setTimeout(function() { 3080 submitConfirmed(form); 3081 }, 220); 3082 } 3083 function onPointerMove(event) { 3084 if (!dragging) { 3085 return; 3086 } 3087 event.preventDefault(); 3088 updateFromClientX(event.clientX); 3089 if (progress >= confirmThreshold) { 3090 dragging = false;
3091 try { 3092 handle.releasePointerCapture(event.pointerId); 3093 } catch (err) {} 3094 confirmSlider(); 3095 } 3096 } 3097 function onPointerUp(event) { 3098 if (!dragging) { 3099 return; 3100 } 3101 updateFromClientX(event.clientX); 3102 dragging = false; 3103 slider.classList.remove("is-dragging"); 3104 try { 3105 handle.releasePointerCapture(event.pointerId); 3106 } catch (err) {} 3107 if (progress >= confirmThreshold) { 3108 confirmSlider(); 3109 return; 3110 } 3111 resetSlider(); 3112 } 3113 handle.addEventListener("pointerdown", function(event) { 3114 if (confirmed) { 3115 return; 3116 } 3117 event.preventDefault(); 3118 dragging = true; 3119 setCancelEnabled(false); 3120 slider.classList.remove("is-teasing"); 3121 slider.classList.add("is-dragging"); 3122 try { 3123 handle.setPointerCapture(event.pointerId); 3124 } catch (err) {} 3125 updateFromClientX(event.clientX); 3126 }); 3127 handle.addEventListener("pointermove", onPointerMove); 3128 handle.addEventListener("pointerup", onPointerUp); 3129 handle.addEventListener("pointercancel", resetSlider); 3130 handle.addEventListener("keydown", function(event) { 3131 if (event.key !== "Enter" && event.key !== " ") { 3132 return; 3133 } 3134 event.preventDefault(); 3135 confirmSlider(); 3136 }); 3137 setProgress(0); 3138 } 3139 document.addEventListener("submit", function(e) { 3140 var form = e.target; 3141 if (!form || !form.matches || !form.matches("[data-bid-confirm]")) { 3142 return; 3143 } 3144 if (form.getAttribute("data-confirmed") === "1") { 3145 return; 3146 } 3147 e.preventDefault(); 3148 e.stopPropagation(); 3149 var amountInput = form.querySelector('input[name="amount"]'); 3150 var amount = amountInput ? parseInt(amountInput.value, 10) : 0; 3151 var maxInput = form.querySelector('input[name="max_amount"]'); 3152 var maxAmount = maxInput ? parseInt(maxInput.value, 10) : 0; 3153 if (isNaN(maxAmount) || maxAmount < 1) { 3154 maxAmount = 0; 3155 } 3156 // ÐÑедел ниже ÑобÑÑвенной ÑÑавки â не авÑоÑоÑг, а обÑÑÐ½Ð°Ñ ÑÑавка: 3157 // поле не ÑÑогаем (вдÑÑг Ñеловек пеÑедÑмаеÑ), ÑеÑÐ²ÐµÑ Ñакой пÑедел пÑоÑÑо не ÑÐ¾Ñ ÑаниÑ. 3158 if (maxAmount && amount && maxAmount < amount) { 3159 maxAmount = 0; 3160 } 3161 var autoOnly = form.hasAttribute("data-bid-auto-only"); 3162 // С ÑказаннÑм пÑеделом Ñеловек беÑÑÑ Ð½Ð° ÑÐµÐ±Ñ Ð¾Ð±ÑзаÑелÑÑÑво до ÑÑой ÑÑÐ¼Ð¼Ñ â 3163 // ÐµÑ Ð¸ показÑваем кÑÑпно, но Ñ Ð¿ÑиÑÑавкой «до», ÑÑÐ¾Ð±Ñ Ð½Ðµ ÑиÑалоÑÑ ÐºÐ°Ðº Ð½ÐµÐ¼ÐµÐ´Ð»ÐµÐ½Ð½Ð°Ñ Ñена. 3164 var withAuto = autoOnly || (maxAmount > amount); 3165 var headline = withAuto ? maxAmount : amount; 3166 if (!headline || headline < 1) { 3167 form.submit(); 3168 return; 3169 } 3170 var formattedAmount = headline.toLocaleString("ru-RU") + " гÑн."; 3171 var pricePrefix = withAuto ? (form.getAttribute("data-auto-price-prefix") || "") : ""; 3172 if (pricePrefix) { 3173 formattedAmount = pricePrefix + " " + formattedAmount; 3174 } 3175 var autoNote = ""; 3176 if (!autoOnly && withAuto) { 3177 autoNote = (form.getAttribute("data-auto-note") || "СейÑÐ°Ñ Ð²Ð°Ñа ÑÑавка â %bid% гÑн.")
3178 .replace("%bid%", amount.toLocaleString("ru-RU")) 3179 .replace("%max%", maxAmount.toLocaleString("ru-RU")); 3180 } 3181 function text(autoAttr, attr, fallback) { 3182 return (withAuto ? form.getAttribute(autoAttr) : null) || form.getAttribute(attr) || fallback; 3183 } 3184 var question = text("data-auto-confirm-question", "data-confirm-question", "ÐÑ Ð¿Ð¾Ð´ÑвеÑждаеÑе ÑÑавкÑ?"); 3185 var description = text("data-auto-confirm-description", "data-confirm-description", ""); 3186 var title = text("data-auto-confirm-title", "data-confirm-title", "ÐодÑвеÑждение ÑÑавки"); 3187 var slideLabel = form.getAttribute("data-slide-label") || "ÐеÑеÑаÑиÑе, ÑÑÐ¾Ð±Ñ Ð¿Ð¾Ð´ÑвеÑдиÑÑ"; 3188 var slideDoneLabel = text("data-auto-slide-done-label", "data-slide-done-label", "СÑавка пÑинÑÑа"); 3189 var cancelLabel = form.getAttribute("data-cancel-label") || "ÐÑмена"; 3190 removeExistingConfirm(form); 3191 form.insertAdjacentHTML("afterend", '<div class="auction-bid-confirm-panel">' + '<div class="auction-bid-confirm-panel__title">' + escapeHtml(title) + "</div>" + '<div class="auction-bid-confirm-panel__question">' + escapeHtml(question) + "</div>" + '<div class="auction-bid-confirm-panel__price"><span>' + escapeHtml(formattedAmount) + "</span></div>" + (autoNote ? '<div class="auction-bid-confirm-panel__auto">' + escapeHtml(autoNote) + "</div>" : "") + (description ? '<div class="auction-bid-confirm-panel__description">' + escapeHtml(description) + "</div>" : "") + '<div class="auction-bid-confirm-panel__actions">' + '<div class="auction-slide-confirm is-teasing" data-bid-confirm-slider>' + '<div class="auction-slide-confirm__fill" data-bid-confirm-fill></div>' + '<div class="auction-slide-confirm__label">' + '<span class="auction-slide-confirm__label-idle">' + escapeHtml(slideLabel) + "</span>" + '<span class="auction-slide-confirm__label-done">' + escapeHtml(slideDoneLabel) + "</span>" + "</div>" + '<button type="button" class="auction-slide-confirm__handle" data-bid-confirm-handle aria-label="' + escapeHtml(slideLabel) + '">âº</button>' + "</div>" + '<button type="button" class="auction-bid-confirm-panel__cancel" data-bid-confirm-cancel>' + escapeHtml(cancelLabel) + "</button>" + "</div>" + "</div>"); 3192 var panel = form.parentNode.querySelector(".auction-bid-confirm-panel"); 3193 if (!panel) { 3194 return; 3195 } 3196 attachSlider(panel, form); 3197 panel.querySelector("[data-bid-confirm-cancel]").addEventListener("click", function() { 3198 panel.remove(); 3199 }); 3200 panel.scrollIntoView({ 3201 behavior: "smooth", 3202 block: "nearest" 3203 }); 3204 }, true); 3205 } 3206 if (document.readyState === "loading") { 3207 document.addEventListener("DOMContentLoaded", function() { 3208 initAuctionForm(); 3209 initAuctionTimer(); 3210 initAuctionBidConfirm(); 3211 initAuctionLiveState(); 3212 initAuctionBidStepper(); 3213 }); 3214 } else { 3215 initAuctionForm(); 3216 initAuctionTimer(); 3217 initAuctionBidConfirm(); 3218 initAuctionLiveState(); 3219 initAuctionBidStepper(); 3220 } 3221})(); 3222 3223(function() { 3224 function markEmptyMonths(board) { 3225 var rows = board.querySelectorAll(".table-plan > tbody > tr"); 3226 var currentMonth = null; 3227 var changedInMonth = 0; 3228 function flush() { 3229 if (currentMonth) { 3230 currentMonth.classList.toggle("is-empty", changedInMonth === 0); 3231 } 3232 } 3233 rows.forEach(function(row) { 3234 if (row.classList.contains("plan-month")) { 3235 flush(); 3236 currentMonth = row; 3237 changedInMonth = 0; 3238 } else if (row.classList.contains("plan-total")) {} else if (row.classList.contains("is-changed")) { 3239 changedInMonth++; 3240 } 3241 }); 3242 flush(); 3243 } 3244 function initBoard(board) { 3245 var buttons = board.querySelectorAll(".js-plan-filter"); 3246 if (!buttons.length) { 3247 return; 3248 }
3249 buttons.forEach(function(btn) { 3250 btn.addEventListener("click", function() { 3251 var mode = btn.getAttribute("data-filter"); 3252 var changesOnly = mode === "changes"; 3253 if (changesOnly) { 3254 markEmptyMonths(board); 3255 } 3256 board.classList.toggle("is-changes-only", changesOnly); 3257 buttons.forEach(function(b) { 3258 var active = b === btn; 3259 b.classList.toggle("is-active", active); 3260 b.setAttribute("aria-pressed", active ? "true" : "false"); 3261 }); 3262 }); 3263 }); 3264 } 3265 function init() { 3266 document.querySelectorAll(".js-plan-board").forEach(initBoard); 3267 } 3268 if (document.readyState === "loading") { 3269 document.addEventListener("DOMContentLoaded", init); 3270 } else { 3271 init(); 3272 } 3273})(); 3274 3275(function() { 3276 var scriptPromises = {}; 3277 var chartAssetsPromise = null; 3278 var PRIMARY = "#00A0DF"; 3279 var GRID = "#eef4f8"; 3280 var AXIS_TEXT = "#687884"; 3281 var DAY_MS = 24 * 60 * 60 * 1e3; 3282 function cssVar(scope, name, fallback) { 3283 if (!scope || !window.getComputedStyle) { 3284 return fallback; 3285 } 3286 var value = window.getComputedStyle(scope).getPropertyValue(name).trim(); 3287 return value || fallback; 3288 } 3289 function cssAlpha(scope, name, fallback) { 3290 var value = parseFloat(cssVar(scope, name, fallback)); 3291 return Number.isFinite(value) ? value : fallback; 3292 } 3293 function chartTheme(root) { 3294 var mutedRgb = cssVar(root, "--ua-muted-rgb", "104, 120, 132"); 3295 var gridAlpha = cssAlpha(root, "--ua-chart-grid-alpha", .14); 3296 return { 3297 primary: cssVar(root, "--ua-primary", PRIMARY), 3298 grid: "rgba(" + mutedRgb + ", " + gridAlpha + ")", 3299 axisText: cssVar(root, "--ua-muted", AXIS_TEXT) 3300 }; 3301 } 3302 function loadScript(src, globalName) { 3303 if (globalName && window[globalName]) { 3304 return Promise.resolve(window[globalName]); 3305 } 3306 if (scriptPromises[src]) { 3307 return scriptPromises[src]; 3308 } 3309 scriptPromises[src] = new Promise(function(resolve, reject) { 3310 var script = document.createElement("script"); 3311 script.src = src; 3312 script.async = true; 3313 script.onload = function() { 3314 resolve(globalName ? window[globalName] : true); 3315 }; 3316 script.onerror = reject; 3317 document.head.appendChild(script); 3318 }); 3319 return scriptPromises[src]; 3320 } 3321 function loadChartAssets() { 3322 if (chartAssetsPromise) { 3323 return chartAssetsPromise; 3324 } 3325 chartAssetsPromise = loadScript("https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js", "Chart").then(function() { 3326 return loadScript("https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js", "luxon"); 3327 }).then(function() { 3328 return loadScript("https://cdn.jsdelivr.net/npm/[email protected]/dist/chartjs-adapter-luxon.umd.min.js"); 3329 }); 3330 return chartAssetsPromise; 3331 } 3332 function hexToRgba(hex, alpha) { 3333 var n = parseInt(hex.slice(1), 16); 3334 return "rgba(" + (n >> 16 & 255) + ", " + (n >> 8 & 255) + ", " + (n & 255) + ", " + alpha + ")"; 3335 } 3336 function areaFill(hex) { 3337 return function(ctx) { 3338 var chart = ctx.chart; 3339 var area = chart.chartArea; 3340 if (!area) { 3341 return hexToRgba(hex, .12); 3342 } 3343 var gradient = chart.ctx.createLinearGradient(0, area.top, 0, area.bottom); 3344 gradient.addColorStop(0, hexToRgba(hex, .26)); 3345 gradient.addColorStop(1, hexToRgba(hex, 0)); 3346 return gradient; 3347 }; 3348 } 3349 function numberLocale(language) { 3350 if (language === "uk" || language === "ua") { 3351 return "uk-UA"; 3352 } 3353 if (language === "en") { 3354 return "en-US"; 3355 } 3356 return "ru-RU"; 3357 } 3358 function formatNumber(value, language) {
3359 return Number(value || 0).toLocaleString(numberLocale(language), { 3360 maximumFractionDigits: 0 3361 }); 3362 } 3363 function formatDate(timestamp, language) { 3364 var locale = language === "ua" ? "uk" : language || "ru"; 3365 return new Intl.DateTimeFormat(locale, { 3366 day: "2-digit", 3367 month: "2-digit", 3368 year: "numeric" 3369 }).format(new Date(timestamp)); 3370 } 3371 function toPoints(rows) { 3372 return rows.map(function(row) { 3373 return { 3374 x: new Date(String(row.date || "")).getTime(), 3375 y: parseFloat(row.price) 3376 }; 3377 }).filter(function(point) { 3378 return Number.isFinite(point.x) && Number.isFinite(point.y) && point.y > 0; 3379 }).sort(function(a, b) { 3380 return a.x - b.x; 3381 }); 3382 } 3383 function filterByRange(points, range) { 3384 if (range === "all" || points.length === 0) { 3385 return points; 3386 } 3387 var days = parseInt(range, 10); 3388 if (!Number.isFinite(days) || days <= 0) { 3389 return points; 3390 } 3391 var latest = points[points.length - 1].x; 3392 var from = latest - days * DAY_MS; 3393 var filtered = points.filter(function(point) { 3394 return point.x >= from; 3395 }); 3396 return filtered.length ? filtered : points; 3397 } 3398 function configurePeriodButtons(root, points) { 3399 var periods = root.querySelector(".market-price-chart__periods"); 3400 var buttons = Array.prototype.slice.call(root.querySelectorAll(".js-market-price-period")); 3401 if (!periods || !buttons.length || points.length < 2) { 3402 if (periods) { 3403 periods.hidden = true; 3404 } 3405 return; 3406 } 3407 var spanDays = (points[points.length - 1].x - points[0].x) / DAY_MS; 3408 var visibleCount = 0; 3409 var allButton = null; 3410 buttons.forEach(function(button) { 3411 var range = button.getAttribute("data-range"); 3412 var hide = false;
3413 if (range === "all") { 3414 allButton = button; 3415 } else { 3416 var days = parseInt(range, 10); 3417 hide = Number.isFinite(days) && spanDays <= days; 3418 } 3419 button.hidden = hide; 3420 if (!hide) { 3421 visibleCount++; 3422 } 3423 }); 3424 periods.hidden = visibleCount <= 1; 3425 if (!root.querySelector(".js-market-price-period.is-active:not([hidden])") && allButton && !allButton.hidden) { 3426 setActivePeriod(root, allButton); 3427 } 3428 } 3429 function roundToStep(value, step, direction) { 3430 if (!Number.isFinite(value) || !Number.isFinite(step) || step <= 0) { 3431 return value; 3432 } 3433 return direction === "up" ? Math.ceil(value / step) * step : Math.floor(value / step) * step; 3434 } 3435 function choosePriceStep(span) { 3436 if (span >= 1e5) { 3437 return 1e4; 3438 } 3439 if (span >= 5e4) { 3440 return 5e3; 3441 } 3442 if (span >= 1e4) { 3443 return 1e3; 3444 } 3445 if (span >= 5e3) { 3446 return 500; 3447 } 3448 if (span >= 1e3) { 3449 return 100; 3450 } 3451 if (span >= 500) { 3452 return 50; 3453 } 3454 if (span >= 100) { 3455 return 20; 3456 } 3457 if (span >= 20) { 3458 return 5; 3459 } 3460 return 1; 3461 } 3462 function calculateYBounds(points) { 3463 if (!points.length) { 3464 return {}; 3465 } 3466 var values = points.map(function(point) { 3467 return point.y; 3468 }); 3469 var min = Math.min.apply(Math, values); 3470 var max = Math.max.apply(Math, values); 3471 var center = (min + max) / 2; 3472 var reference = Math.max(Math.abs(center), Math.abs(points[points.length - 1].y), 1); 3473 var dataSpan = Math.max(max - min, 0); 3474 var targetSpan = Math.max(dataSpan * 1.35, reference * .12); 3475 var rawMin = center - targetSpan / 2; 3476 var rawMax = center + targetSpan / 2; 3477 if (rawMin < 0 && min >= 0) { 3478 rawMin = 0; 3479 } 3480 var step = choosePriceStep(targetSpan); 3481 return { 3482 min: roundToStep(rawMin, step, "down"), 3483 max: roundToStep(rawMax, step, "up") 3484 }; 3485 } 3486 function setState(root, name) { 3487 var canvasWrap = root.querySelector(".js-market-price-chart-canvas-wrap"); 3488 var states = root.querySelectorAll(".js-market-price-chart-state"); 3489 if (canvasWrap) { 3490 canvasWrap.hidden = name !== null; 3491 } 3492 states.forEach(function(state) { 3493 state.hidden = state.getAttribute("data-role") !== name; 3494 }); 3495 } 3496 function updateStats(root, points, language, currencyLabel) { 3497 var stats = root.querySelector(".js-market-price-chart-stats"); 3498 var summary = root.querySelector(".js-market-price-chart-summary"); 3499 var periodStat = root.querySelector(".js-market-price-chart-period-stat"); 3500 var hasStaticStats = !!root.querySelector("[data-market-chart-static-stat]"); 3501 if (!points.length) { 3502 root.classList.toggle("has-market-price-stats", hasStaticStats); 3503 if (stats) { 3504 stats.hidden = !hasStaticStats; 3505 } 3506 if (periodStat) { 3507 periodStat.hidden = true; 3508 } 3509 return; 3510 } 3511 var first = points[0]; 3512 var last = points[points.length - 1]; 3513 var values = points.map(function(point) { 3514 return point.y; 3515 }); 3516 var min = Math.min.apply(Math, values); 3517 var max = Math.max.apply(Math, values); 3518 var change = last.y - first.y; 3519 var changePercent = first.y > 0 ? change / first.y * 100 : 0; 3520 var changeText = (change > 0 ? "+" : "") + formatNumber(change, language) + " " + currencyLabel + " (" + (changePercent > 0 ? "+" : "") + changePercent.toLocaleString(numberLocale(language), { 3521 maximumFractionDigits: 1 3522 }) + "%)"; 3523 var statMap = { 3524 min: formatNumber(min, language) + " " + currencyLabel, 3525 max: formatNumber(max, language) + " " + currencyLabel, 3526 change: changeText, 3527 range: (root.getAttribute("data-min-label") || "ÐинимÑм") + " " + formatNumber(min, language) + " · " + (root.getAttribute("data-max-label") || "ÐакÑимÑм") + " " + formatNumber(max, language) 3528 };
3529 Object.keys(statMap).forEach(function(key) { 3530 var node = root.querySelector('[data-market-chart-stat="' + key + '"]'); 3531 if (node) { 3532 node.textContent = statMap[key]; 3533 if (key === "change") { 3534 node.classList.toggle("is-up", change > 0); 3535 node.classList.toggle("is-down", change < 0); 3536 } 3537 } 3538 }); 3539 if (summary) { 3540 summary.textContent = formatDate(first.x, language) + " - " + formatDate(last.x, language); 3541 } 3542 if (stats) { 3543 stats.hidden = false; 3544 root.classList.add("has-market-price-stats"); 3545 } 3546 if (periodStat) { 3547 periodStat.hidden = false; 3548 } 3549 } 3550 function drawChart(root, points) { 3551 var canvas = root.querySelector(".js-market-price-chart-canvas"); 3552 if (!canvas) { 3553 return; 3554 } 3555 var language = root.getAttribute("data-language") || "ru"; 3556 var priceLabel = root.getAttribute("data-price-label") || "Цена"; 3557 var currencyLabel = root.getAttribute("data-currency-label") || "гÑн"; 3558 var reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; 3559 var existing = root._marketPriceChart || null; 3560 var yBounds = calculateYBounds(points); 3561 var theme = chartTheme(root); 3562 updateStats(root, points, language, currencyLabel); 3563 if (existing) { 3564 existing.data.datasets[0].data = points; 3565 existing.data.datasets[0].borderColor = theme.primary; 3566 existing.data.datasets[0].backgroundColor = areaFill(theme.primary); 3567 existing.options.scales.y.min = yBounds.min; 3568 existing.options.scales.y.max = yBounds.max; 3569 existing.options.scales.x.grid.color = theme.grid; 3570 existing.options.scales.x.border.color = theme.grid; 3571 existing.options.scales.x.ticks.color = theme.axisText; 3572 existing.options.scales.y.grid.color = theme.grid; 3573 existing.options.scales.y.border.color = theme.grid; 3574 existing.options.scales.y.ticks.color = theme.axisText; 3575 existing.update(); 3576 return; 3577 } 3578 root._marketPriceChart = new Chart(canvas, { 3579 type: "line", 3580 data: { 3581 datasets: [ { 3582 label: priceLabel, 3583 data: points, 3584 borderColor: theme.primary, 3585 borderWidth: 2, 3586 tension: .25, 3587 pointRadius: 0, 3588 pointHitRadius: 14, 3589 pointHoverRadius: 4, 3590 fill: true, 3591 backgroundColor: areaFill(theme.primary) 3592 } ] 3593 }, 3594 options: { 3595 responsive: true, 3596 maintainAspectRatio: false, 3597 animation: { 3598 duration: reducedMotion ? 0 : 300 3599 }, 3600 parsing: false, 3601 normalized: true, 3602 interaction: { 3603 mode: "nearest", 3604 axis: "xy", 3605 intersect: false 3606 }, 3607 scales: { 3608 x: { 3609 type: "time", 3610 adapters: { 3611 date: { 3612 locale: language === "ua" ? "uk" : language 3613 } 3614 }, 3615 time: { 3616 tooltipFormat: "dd.MM.yyyy", 3617 minUnit: "day" 3618 }, 3619 grid: { 3620 color: theme.grid, 3621 drawTicks: false 3622 }, 3623 border: { 3624 color: theme.grid 3625 }, 3626 ticks: { 3627 maxRotation: 0, 3628 autoSkip: true, 3629 color: theme.axisText 3630 } 3631 }, 3632 y: { 3633 min: yBounds.min,
3634 max: yBounds.max, 3635 grid: { 3636 color: theme.grid, 3637 drawTicks: false 3638 }, 3639 border: { 3640 color: theme.grid 3641 }, 3642 ticks: { 3643 color: theme.axisText, 3644 callback: function(value) { 3645 return formatNumber(value, language); 3646 } 3647 } 3648 } 3649 }, 3650 plugins: { 3651 legend: { 3652 display: false 3653 }, 3654 decimation: { 3655 enabled: false 3656 }, 3657 tooltip: { 3658 callbacks: { 3659 label: function(item) { 3660 return formatNumber(item.parsed.y, language) + " " + currencyLabel; 3661 } 3662 } 3663 } 3664 } 3665 } 3666 }); 3667 } 3668 function setActivePeriod(root, activeButton) { 3669 root.querySelectorAll(".js-market-price-period").forEach(function(button) { 3670 var isActive = button === activeButton; 3671 button.classList.toggle("is-active", isActive); 3672 button.setAttribute("aria-pressed", isActive ? "true" : "false"); 3673 }); 3674 } 3675 function initPriceChart(root) { 3676 var pricesUrl = root.getAttribute("data-prices-url"); 3677 var errorLabel = root.getAttribute("data-error-label") || "ÐÑибка загÑÑзки даннÑÑ "; 3678 var summary = root.querySelector(".js-market-price-chart-summary"); 3679 var allPoints = []; 3680 if (!pricesUrl) { 3681 return; 3682 } 3683 function renderCurrentRange() { 3684 var active = root.querySelector(".js-market-price-period.is-active"); 3685 var range = active ? active.getAttribute("data-range") : "all"; 3686 var points = filterByRange(allPoints, range || "all"); 3687 setState(root, points.length ? null : "empty"); 3688 if (points.length) { 3689 drawChart(root, points); 3690 } 3691 } 3692 root.querySelectorAll(".js-market-price-period").forEach(function(button) { 3693 button.addEventListener("click", function() { 3694 if (button.classList.contains("is-active")) { 3695 return; 3696 }
3697 setActivePeriod(root, button); 3698 renderCurrentRange(); 3699 }); 3700 }); 3701 setState(root, "loading"); 3702 loadChartAssets().then(function() { 3703 return fetch(pricesUrl, { 3704 headers: { 3705 "X-Requested-With": "XMLHttpRequest" 3706 } 3707 }); 3708 }).then(function(response) { 3709 if (!response.ok) { 3710 throw new Error("HTTP " + response.status); 3711 } 3712 return response.json(); 3713 }).then(function(rows) { 3714 allPoints = toPoints(Array.isArray(rows) ? rows : []); 3715 if (!allPoints.length) { 3716 setState(root, "empty"); 3717 return; 3718 } 3719 configurePeriodButtons(root, allPoints); 3720 renderCurrentRange(); 3721 }).catch(function(error) { 3722 if (window.console && console.error) { 3723 console.error("ÐÑибка загÑÑзки даннÑÑ :", error); 3724 } 3725 if (summary) { 3726 summary.textContent = errorLabel; 3727 } 3728 setState(root, "error"); 3729 }); 3730 } 3731 function initCoinPriceCharts() { 3732 document.querySelectorAll(".js-coin-price-chart").forEach(initPriceChart); 3733 } 3734 if (document.readyState === "loading") { 3735 document.addEventListener("DOMContentLoaded", initCoinPriceCharts); 3736 } else { 3737 initCoinPriceCharts(); 3738 } 3739})(); 3740 3741(function() { 3742 function showSoldStamp(card) { 3743 var wrap = card.querySelector(".lot-image-wrap"); 3744 if (!wrap) { 3745 return; 3746 } 3747 var old = wrap.querySelector(".sold-stamp"); 3748 if (old) { 3749 old.remove(); 3750 } 3751 var stamp = document.createElement("span"); 3752 stamp.className = "sold-stamp"; 3753 stamp.textContent = card.getAttribute("data-sold-label") || "ÐÑодано"; 3754 stamp.addEventListener("animationend", function() { 3755 stamp.remove(); 3756 }); 3757 wrap.appendChild(stamp); 3758 } 3759 document.addEventListener("click", function(e) { 3760 var card = e.target.closest("[data-sold-card]"); 3761 if (!card) { 3762 return; 3763 } 3764 if (e.target.closest("a")) { 3765 return; 3766 } 3767 e.preventDefault(); 3768 showSoldStamp(card); 3769 }); 3770})(); 3771 3772/** 3773 * WYSIWYG-ÑедакÑÐ¾Ñ ÐºÐ¾Ð¼Ð¼ÐµÐ½ÑаÑиев (ÑÑÐ¸Ð»Ñ Telegram). 3774 * Ðоле ввода Ñ data-format-enabled (пÑемиÑм-авÑоÑÑ) подменÑеÑÑÑ Ð½Ð° 3775 * contenteditable: жиÑнÑй/кÑÑÑив/подÑÑÑкнÑÑÑй/заÑÑÑкнÑÑÑй Ð²Ð¸Ð´Ð½Ñ ÑÑÐ°Ð·Ñ Ð¿Ñи набоÑе. 3776 * ÐÑÑ Ð¾Ð´Ð½Ñй <textarea> оÑÑаÑÑÑÑ ÑкÑÑÑÑм полем ÑоÑÐ¼Ñ â в него на каждÑй ввод 3777 * ÑеÑиализÑÑÑÑÑ Ð¼Ð°ÑкеÑÑ [b]..[/b] / [i] / [u] / [s], коÑоÑÑе ÑеÑÐ²ÐµÑ ÑазвоÑаÑÐ¸Ð²Ð°ÐµÑ 3778 * в Ñеги на вÑводе. ÐÑделение ÑекÑÑа Ð¿Ð¾Ð´Ð½Ð¸Ð¼Ð°ÐµÑ Ð¿Ð»Ð°Ð²Ð°ÑÑÑÑ Ð¿Ð°Ð½ÐµÐ»Ñ Ð/Ð/Ð/Ð. 3779 * API: window.CommentEditor { enhance, pull, insert, clear, isEnhanced }. 3780 */ 3781(function() { 3782 var ENABLED = "textarea[data-format-enabled]"; 3783 3784 function i18n(key, fb) { 3785 var b = document.getElementById("comment-i18n"); 3786 if (!b) { return fb; } 3787 return b.getAttribute("data-" + key) || fb; 3788 } 3789 3790 // ---------- маÑкеÑÑ <-> html ---------- 3791 function escapeHtml(s) { 3792 return s.replace(/[&<>"]/g, function(c) { 3793 return { "&": "&", "<": "<", ">": ">", '"': """ }[c]; 3794 }); 3795 } 3796 3797 function markersToHtml(text) { 3798 return escapeHtml(text) 3799 .replace(/\[b\]([\s\S]*?)\[\/b\]/gi, "<b>$1</b>") 3800 .replace(/\[i\]([\s\S]*?)\[\/i\]/gi, "<i>$1</i>") 3801 .replace(/\[u\]([\s\S]*?)\[\/u\]/gi, "<u>$1</u>") 3802 .replace(/\[s\]([\s\S]*?)\[\/s\]/gi, "<s>$1</s>") 3803 .replace(/\n/g, "<br>"); 3804 } 3805 3806 var TAG_MARK = { b: "b", strong: "b", i: "i", em: "i", u: "u", s: "s", strike: "s", del: "s" }; 3807 3808 function serialize(node) { 3809 var out = ""; 3810 var children = node.childNodes; 3811 for (var i = 0; i < children.length; i++) { 3812 var ch = children[i]; 3813 if (ch.nodeType === 3) { 3814 out += ch.nodeValue; 3815 } else if (ch.nodeType === 1) { 3816 var tag = ch.tagName.toLowerCase(); 3817 if (tag === "br") { 3818 out += "\n"; 3819 } else if (TAG_MARK[tag]) { 3820 var m = TAG_MARK[tag]; 3821 var inner = serialize(ch); 3822 out += inner ? "[" + m + "]" + inner + "[/" + m + "]" : ""; 3823 } else if (tag === "div" || tag === "p") { 3824 if (out && out.slice(-1) !== "\n") { out += "\n"; } 3825 out += serialize(ch); 3826 } else { 3827 out += serialize(ch); 3828 } 3829 } 3830 } 3831 return out; 3832 } 3833 3834 function normalize(s) { 3835 return s.replace(/ /g, " ") 3836 .replace(/[ \t]+\n/g, "\n") 3837 .replace(/\n{3,}/g, "\n\n") 3838 .replace(/\s+$/, ""); 3839 } 3840 3841 // ---------- ÑÐ¸Ð½Ñ ÑонизаÑÐ¸Ñ ---------- 3842 function updateEmpty(ed) { 3843 var empty = ed.textContent.replace(/[â \s]/g, "").length === 0; 3844 ed.classList.toggle("is-empty", empty); 3845 } 3846 3847 function syncFromEditable(ed) { 3848 ed._ta.value = normalize(serialize(ed)); 3849 updateEmpty(ed); 3850 ed._ta.dispatchEvent(new Event("input", { bubbles: true })); 3851 } 3852 3853 function loadIntoEditable(ed) { 3854 ed.innerHTML = markersToHtml(ed._ta.value || ""); 3855 updateEmpty(ed); 3856 } 3857 3858 // ---------- пÑевÑаÑение textarea в ÑедакÑÐ¾Ñ ---------- 3859 function enhance(ta) { 3860 if (!ta || ta._richEnhanced || !ta.matches || !ta.matches(ENABLED)) { 3861 return ta && ta._rich ? ta._rich : null; 3862 } 3863 var isInline = ta.className.indexOf("comment-inline-editor") >= 0; 3864 var ed = document.createElement("div"); 3865 // NB: не даÑм ÑедакÑоÑÑ ÐºÐ»Ð°ÑÑ comment-inline-editor â по Ð½ÐµÐ¼Ñ save/keydown 3866 // иÑÑÑ Ð¸ÑÑ Ð¾Ð´Ð½Ñй textarea; ÐºÐ¾Ð»Ð»Ð¸Ð·Ð¸Ñ Ð»Ð¾Ð¼Ð°Ð»Ð° Ð±Ñ ÑÐ¾Ñ Ñанение. 3867 ed.className = "form-control ua-rich-editor" + (isInline ? " ua-rich-editor--inline" : ""); 3868 ed.setAttribute("contenteditable", "true"); 3869 ed.setAttribute("role", "textbox"); 3870 ed.setAttribute("aria-multiline", "true"); 3871 ed.setAttribute("data-placeholder", ta.getAttribute("placeholder") || ""); 3872 var lbl = ta.getAttribute("aria-label"); 3873 if (lbl) { ed.setAttribute("aria-label", lbl); } 3874 ed._ta = ta; 3875 ta._rich = ed;
3876 ta._richEnhanced = true; 3877 ta.classList.add("ua-rich-source"); 3878 ta.setAttribute("tabindex", "-1"); 3879 ta.setAttribute("aria-hidden", "true"); 3880 ta.parentNode.insertBefore(ed, ta); 3881 loadIntoEditable(ed); 3882 3883 ed.addEventListener("input", function() { syncFromEditable(ed); }); 3884 ed.addEventListener("paste", function(e) { 3885 e.preventDefault(); 3886 var text = (e.clipboardData || window.clipboardData).getData("text/plain"); 3887 document.execCommand("insertText", false, text); 3888 }); 3889 ed.addEventListener("beforeinput", function(e) { 3890 if (e.inputType && e.inputType.indexOf("insert") === 0) { 3891 var sel = window.getSelection(); 3892 var selLen = sel && !sel.isCollapsed ? String(sel).length : 0; 3893 var add = e.data ? e.data.length : 1; 3894 if (ed.textContent.length - selLen + add > 2000) { 3895 e.preventDefault(); 3896 } 3897 } 3898 }); 3899 ed.addEventListener("focus", function() { updateEmpty(ed); }); 3900 3901 // Ðнопки ÑоÑмаÑиÑованиÑ: докнÑÑÐ°Ñ Ð¿Ð°Ð½ÐµÐ»Ñ Ð¿Ð¾Ð´ полем. У оÑновного композеÑа 3902 // еÑÑÑ Ð³Ð¾ÑовÑй ÑÐ»Ð¾Ñ [data-format-slot] в ÑоÑме; Ñ Ð¸Ð½Ð»Ð°Ð¹Ð½-ÑедакÑоÑа ÑоздаÑм 3903 // ÑÐ²Ð¾Ñ Ð¿Ð°Ð½ÐµÐ»ÑÐºÑ ÑÑдом. 3904 var slot = ta.form ? ta.form.querySelector("[data-format-slot]") : null; 3905 if (slot) { 3906 buildFormatButtons(ed, slot); 3907 } else { 3908 var bar = document.createElement("div"); 3909 bar.className = "ua-compose-toolbar ua-compose-toolbar--inline"; 3910 buildFormatButtons(ed, bar); 3911 ed.parentNode.insertBefore(bar, ed.nextSibling); 3912 } 3913 3914 return ed; 3915 } 3916 3917 // ---------- ÑоÑмаÑиÑование ---------- 3918 var MARK_CMD = { b: "bold", i: "italic", u: "underline", s: "strikeThrough" }; 3919 var FMT_BUTTONS = [ 3920 { marker: "b", label: "Ð", cls: "fmt-b", title: i18n("fmt-bold", "ÐиÑнÑй"), key: "b" }, 3921 { marker: "i", label: "Ð", cls: "fmt-i", title: i18n("fmt-italic", "ÐÑÑÑив"), key: "i" }, 3922 { marker: "u", label: "Ð", cls: "fmt-u", title: i18n("fmt-underline", "ÐодÑÑÑкнÑÑÑй"), key: "u" }, 3923 { marker: "s", label: "Ð", cls: "fmt-s", title: i18n("fmt-strike", "ÐаÑÑÑкнÑÑÑй"), key: null } 3924 ]; 3925 3926 function format(ed, marker) { 3927 if (!ed) { return; } 3928 ed.focus(); 3929 try { document.execCommand("styleWithCSS", false, false); } catch (_) {} 3930 document.execCommand(MARK_CMD[marker], false, null); 3931 syncFromEditable(ed); 3932 updateActiveStates(ed); 3933 } 3934 3935 function buildFormatButtons(ed, container) { 3936 ed._fmtButtons = []; 3937 FMT_BUTTONS.forEach(function(b) { 3938 var el = document.createElement("button"); 3939 el.type = "button"; 3940 el.className = "ua-compose-btn ua-format-btn " + b.cls; 3941 el.textContent = b.label; 3942 el.title = b.title; 3943 el.setAttribute("aria-label", b.title); 3944 el.setAttribute("aria-pressed", "false"); 3945 el.addEventListener("mousedown", function(e) { 3946 e.preventDefault(); // ÑÐ¾Ñ ÑаниÑÑ Ð²Ñделение в ÑедакÑоÑе 3947 format(ed, b.marker); 3948 }); 3949 container.appendChild(el); 3950 ed._fmtButtons.push({ marker: b.marker, el: el }); 3951 }); 3952 } 3953 3954 // ÐодÑвеÑка кнопок: ÑÑо Ñже пÑименено в ÑекÑÑем вÑделении/каÑеÑке 3955 // (queryCommandState â Ñодной ÑпÑÑник execCommand). ЧаÑÑиÑное вÑделение 3956 // обÑÑно даÑÑ false â ÑÑо ок. 3957 function updateActiveStates(ed) { 3958 if (!ed || !ed._fmtButtons) { return; } 3959 ed._fmtButtons.forEach(function(b) { 3960 var active = false; 3961 try { active = document.queryCommandState(MARK_CMD[b.marker]); } catch (_) {} 3962 b.el.classList.toggle("is-active", active); 3963 b.el.setAttribute("aria-pressed", active ? "true" : "false"); 3964 }); 3965 } 3966 3967 function editableFromSelection() { 3968 var sel = window.getSelection(); 3969 if (!sel || sel.rangeCount === 0) { return null; } 3970 var node = sel.anchorNode; 3971 while (node) { 3972 if (node.nodeType === 1 && node.classList && node.classList.contains("ua-rich-editor")) { 3973 return node; 3974 } 3975 node = node.parentNode; 3976 } 3977 return null; 3978 } 3979 3980 document.addEventListener("selectionchange", function() { 3981 var ed = editableFromSelection(); 3982 if (ed) { updateActiveStates(ed); } 3983 }); 3984 3985 document.addEventListener("keydown", function(e) { 3986 var el = document.activeElement; 3987 if (!el || !el.classList || !el.classList.contains("ua-rich-editor")) { return; } 3988 if (!(e.ctrlKey || e.metaKey) || e.altKey) { return; } 3989 var btn = FMT_BUTTONS.filter(function(b) { return b.key === e.key.toLowerCase(); })[0]; 3990 if (btn) { 3991 e.preventDefault(); 3992 format(el, btn.marker); 3993 } 3994 }); 3995 3996 // ---------- пÑблиÑнÑй API Ð´Ð»Ñ Ð¸Ð½ÑегÑаÑии Ñ ÑÑÑеÑÑвÑÑÑими обÑабоÑÑиками ---------- 3997 window.CommentEditor = { 3998 enhance: enhance, 3999 pull: function(ta) { if (ta && ta._rich) { loadIntoEditable(ta._rich); } }, 4000 insert: function(ta, text) { 4001 if (ta && ta._rich) { 4002 ta._rich.focus(); 4003 document.execCommand("insertText", false, text); 4004 return true; 4005 } 4006 return false;
4007 }, 4008 clear: function(ta) { 4009 if (!ta) { return; } 4010 ta.value = ""; 4011 if (ta._rich) { loadIntoEditable(ta._rich); } 4012 }, 4013 isEnhanced: function(ta) { return !!(ta && ta._richEnhanced); } 4014 }; 4015 4016 function initAll() { 4017 Array.prototype.forEach.call(document.querySelectorAll(ENABLED), function(ta) { enhance(ta); }); 4018 } 4019 if (document.readyState === "loading") { 4020 document.addEventListener("DOMContentLoaded", initAll); 4021 } else { 4022 initAll(); 4023 } 4024})(); 4025 4026/** 4027 * ÐÑикÑепление одной каÑÑинки к комменÑаÑÐ¸Ñ (пÑемиÑм): вÑÐ±Ð¾Ñ Ñайла, клиенÑÑÐºÐ°Ñ 4028 * валидаÑÐ¸Ñ Ñипа/ÑазмеÑа, пÑевÑÑ Ñ ÐºÐ½Ð¾Ð¿ÐºÐ¾Ð¹ ÑдалениÑ. ÐÑпÑавка â в multipart-веÑке 4029 * submit-обÑабоÑÑика ÑоÑÐ¼Ñ ÐºÐ¾Ð¼Ð¼ÐµÐ½ÑаÑиÑ. 4030 */ 4031(function() { 4032 var ALLOWED = ["image/jpeg", "image/png", "image/webp", "image/gif"]; 4033 var MAX_BYTES = 8 * 1024 * 1024; 4034 4035 function i18n(key, fb) { 4036 var b = document.getElementById("comment-i18n"); 4037 if (!b) { return fb; } 4038 return b.getAttribute("data-" + key) || fb; 4039 } 4040 4041 function hidePreview(form) { 4042 if (!form) { return; } 4043 var preview = form.querySelector(".js-attach-preview"); 4044 if (preview) { 4045 preview.setAttribute("hidden", "hidden"); 4046 var img = preview.querySelector(".ua-attach-preview__img"); 4047 if (img) { img.src = ""; } 4048 } 4049 } 4050 4051 function resetAttach(form) { 4052 if (!form) { return; } 4053 var input = form.querySelector(".js-attach-input"); 4054 if (input) { input.value = ""; } 4055 hidePreview(form); 4056 } 4057 4058 function showPreview(form, file) { 4059 if (!form) { return; } 4060 var preview = form.querySelector(".js-attach-preview"); 4061 if (!preview) { return; } 4062 var img = preview.querySelector(".ua-attach-preview__img"); 4063 var reader = new FileReader(); 4064 reader.onload = function() { 4065 if (img) { img.src = reader.result; } 4066 preview.removeAttribute("hidden"); 4067 }; 4068 reader.readAsDataURL(file); 4069 } 4070 4071 document.addEventListener("click", function(e) { 4072 var btn = e.target.closest && e.target.closest(".js-attach-image"); 4073 if (btn) { 4074 e.preventDefault(); 4075 var wrap = btn.closest("[data-format-toolbar]"); 4076 var input = wrap && wrap.querySelector(".js-attach-input"); 4077 if (input) { input.click(); } 4078 return; 4079 } 4080 var rm = e.target.closest && e.target.closest(".js-attach-remove"); 4081 if (rm) { 4082 e.preventDefault(); 4083 resetAttach(rm.closest("form")); 4084 } 4085 }); 4086 4087 document.addEventListener("change", function(e) { 4088 var input = e.target; 4089 if (!input.classList || !input.classList.contains("js-attach-input")) { return; } 4090 var form = input.closest("form"); 4091 var errBox = form ? form.querySelector(".js-comment-error") : null; 4092 var file = input.files && input.files[0]; 4093 if (!file) { hidePreview(form); return; } 4094 if (ALLOWED.indexOf(file.type) < 0) { 4095 if (errBox) { errBox.textContent = i18n("attach-bad-type", "ÐопÑÑÑимÑе ÑоÑмаÑÑ: JPG, PNG, WEBP, GIF"); } 4096 resetAttach(form); 4097 return; 4098 } 4099 if (file.size > MAX_BYTES) { 4100 if (errBox) { errBox.textContent = i18n("attach-too-big", "Ð Ð°Ð·Ð¼ÐµÑ ÐºÐ°ÑÑинки не должен пÑевÑÑаÑÑ 8 ÐÐ"); } 4101 resetAttach(form); 4102 return; 4103 } 4104 if (errBox) { errBox.textContent = ""; } 4105 showPreview(form, file); 4106 }); 4107})(); 4108 4109/* ÐоиÑк в Ñапке: одно поле на вÑе ÑиÑинÑ, вÑегда вплоÑнÑÑ Ñлева Ð¾Ñ Ð°ÐºÐºÐ°ÑнÑа или бÑÑгеÑа */ 4110(function () { 4111 var navbar = document.querySelector(".ua-main-navbar"); 4112 var form = navbar && navbar.querySelector("[data-navbar-search]"); 4113 if (!navbar || !form) { return; } 4114 4115 var container = navbar.querySelector(".container"); 4116 var collapse = navbar.querySelector("#navbar"); 4117 var userNav = collapse && collapse.querySelector(".navbar-nav.ms-auto"); 4118 var toggle = navbar.querySelector("[data-navbar-search-toggle]"); 4119 var closeBtn = form.querySelector("[data-navbar-search-close]"); 4120 var clearBtn = form.querySelector("[data-search-clear]"); 4121 var field = form.querySelector(".ua-search"); 4122 var input = form.querySelector(".ua-search__input"); 4123 var burger = navbar.querySelector(".navbar-toggler"); 4124 var wide = window.matchMedia("(min-width: 1200px)"); 4125 var menuExpanded = window.matchMedia("(min-width: 992px)"); 4126 4127 function syncFilled() { 4128 if (!field || !input) { return; } 4129 field.classList.toggle("is-filled", input.value.trim() !== ""); 4130 } 4131 4132 function closeSearch() { 4133 navbar.classList.remove("is-search-open"); 4134 if (toggle) { toggle.setAttribute("aria-expanded", "false"); } 4135 } 4136 4137 function openSearch() { 4138 if (collapse && collapse.classList.contains("show") && window.bootstrap) { 4139 window.bootstrap.Collapse.getOrCreateInstance(collapse).hide(); 4140 } 4141 navbar.classList.add("is-search-open"); 4142 if (toggle) { toggle.setAttribute("aria-expanded", "true"); } 4143 if (input) { input.focus(); } 4144 } 4145 4146 /* ÐоиÑк вÑегда ÑÑÐ¾Ð¸Ñ Ð²Ð¿Ð»Ð¾ÑнÑÑ Ñлева Ð¾Ñ Ð¿Ñавого блока Ñапки: Ð¾Ñ Ð±ÑÑгеÑа на ÑелеÑоне, 4147 Ð¾Ñ Ð¼ÐµÐ½Ñ Ð¿Ð¾Ð»ÑзоваÑÐµÐ»Ñ Ð½Ð° оÑÑалÑнÑÑ ÑиÑÐ¸Ð½Ð°Ñ . ÐенÑеÑÑÑ ÑолÑко ÑоÑма â лÑпа или поле. */ 4148 function place() { 4149 if (wide.matches) { 4150 if (userNav && form.parentNode !== collapse) { 4151 userNav.parentNode.insertBefore(form, userNav); 4152 } 4153 closeSearch(); 4154 } else if (container && form.parentNode !== container) { 4155 container.appendChild(form); 4156 } 4157 4158 if (!toggle) { return; } 4159 if (menuExpanded.matches) { 4160 if (userNav && toggle.parentNode !== collapse) { 4161 userNav.parentNode.insertBefore(toggle, userNav); 4162 } 4163 } else if (burger && toggle.nextElementSibling !== burger) { 4164 container.insertBefore(toggle, burger); 4165 } 4166 } 4167 4168 if (toggle) { 4169 toggle.addEventListener("click", function () { 4170 if (navbar.classList.contains("is-search-open")) { closeSearch(); } else { openSearch(); } 4171 }); 4172 } 4173 4174 if (closeBtn) { 4175 closeBtn.addEventListener("click", closeSearch); 4176 } 4177 4178 if (clearBtn && input) { 4179 clearBtn.addEventListener("click", function () { 4180 input.value = ""; 4181 syncFilled(); 4182 input.focus(); 4183 }); 4184 } 4185 4186 if (input) { 4187 input.addEventListener("input", syncFilled); 4188 } 4189 4190 document.addEventListener("keydown", function (e) { 4191 if (e.key === "Escape" && navbar.classList.contains("is-search-open")) { closeSearch(); } 4192 }); 4193
4194 [wide, menuExpanded].forEach(function (mq) { 4195 if (mq.addEventListener) { 4196 mq.addEventListener("change", place); 4197 } else if (mq.addListener) { 4198 mq.addListener(place); 4199 } 4200 }); 4201 4202 place(); 4203 syncFilled(); 4204})(); 4205 4206/* ÐеÑеклÑÑение оÑоÑÐ¼Ð»ÐµÐ½Ð¸Ñ ÑайÑа: ÑвеÑÐ»Ð°Ñ / как в ÑиÑÑеме / ÑÑмнаÑ. 4207 ÐÑÐ±Ð¾Ñ Ñ ÑаниÑÑÑ Ð² cookie ua_theme, поÑÑÐ¾Ð¼Ñ ÑеÑÐ²ÐµÑ Ð¾ÑдаÑÑ Ð½ÑжнÑÑ ÑÐµÐ¼Ñ ÑÑазÑ, 4208 без вÑпÑÑки ÑвеÑлого Ñона. ÐÑименÑеÑÑÑ Ð¼Ð³Ð½Ð¾Ð²ÐµÐ½Ð½Ð¾, без пеÑезагÑÑзки. */ 4209(function () { 4210 var COOKIE = "ua_theme"; 4211 var VALUES = ["light", "system", "dark"]; 4212 var root = document.documentElement; 4213 var media = window.matchMedia ? window.matchMedia("(prefers-color-scheme: dark)") : null; 4214 4215 /* ÐоÑÑÑм Ñема не пÑименÑеÑÑÑ: ÑÐ°Ð¹Ñ Ð´Ð»Ñ Ð½Ð¸Ñ Ð²Ñегда ÑвеÑлÑй. 4216 СеÑÐ²ÐµÑ Ð¿Ð¾Ð¼ÐµÑÐ°ÐµÑ ÑазÑеÑение аÑÑибÑÑом data-theme-allowed. */ 4217 function isAllowed() { 4218 return root.getAttribute("data-theme-allowed") === "1"; 4219 } 4220 4221 function readPref() { 4222 if (!isAllowed()) { return "light"; } 4223 /* СеÑÐ²ÐµÑ Ñже подÑÑавил знаÑение из аккаÑнÑа â оно главнее локалÑной cookie, 4224 инаÑе вÑбоÑ, ÑделаннÑй на дÑÑгом ÑÑÑÑойÑÑве, ÑÑда не доедеÑ. */ 4225 var value = root.getAttribute("data-theme-pref"); 4226 if (VALUES.indexOf(value) === -1) { 4227 var m = document.cookie.match(/(?:^|;\s*)ua_theme=([^;]*)/); 4228 value = m ? decodeURIComponent(m[1]) : null; 4229 } 4230 return VALUES.indexOf(value) === -1 ? "light" : value; 4231 } 4232 4233 function savePref(value) { 4234 document.cookie = COOKIE + "=" + value + ";path=/;max-age=31536000;samesite=lax"; 4235 4236 /* Рв аккаÑÐ½Ñ â ÑÑÐ¾Ð±Ñ Ð²ÑÐ±Ð¾Ñ ÑабоÑал в лÑбом бÑаÑзеÑе полÑзоваÑелÑ. */ 4237 try { 4238 fetch("/theme/preference/" + value, { method: "POST", credentials: "same-origin", keepalive: true }); 4239 } catch (e) { /* Ñема Ñже пÑименена локалÑно, оÑибка ÑеÑи не кÑиÑиÑна */ } 4240 } 4241 4242 function isDark(pref) { 4243 return pref === "dark" || (pref === "system" && !!(media && media.matches)); 4244 } 4245 4246 function apply(pref) { 4247 root.setAttribute("data-theme-pref", pref); 4248 if (isDark(pref)) { 4249 root.setAttribute("data-bs-theme", "dark"); 4250 } else { 4251 root.removeAttribute("data-bs-theme"); 4252 } 4253 4254 var meta = document.querySelector('meta[name="theme-color"]'); 4255 if (meta) { 4256 meta.setAttribute("content", isDark(pref) ? "#0E1116" : "#00a0df"); 4257 } 4258 4259 document.querySelectorAll(".js-theme-option").forEach(function (btn) { 4260 var active = btn.getAttribute("data-theme-value") === pref; 4261 btn.classList.toggle("is-active", active); 4262 btn.setAttribute("aria-checked", active ? "true" : "false"); 4263 }); 4264 } 4265 4266 var initial = readPref(); 4267 apply(initial); 4268 if (isAllowed()) { 4269 document.cookie = COOKIE + "=" + initial + ";path=/;max-age=31536000;samesite=lax"; 4270 } 4271 4272 document.addEventListener("click", function (e) { 4273 var btn = e.target.closest ? e.target.closest(".js-theme-option") : null; 4274 if (!btn) { return; } 4275 e.preventDefault(); 4276 var pref = btn.getAttribute("data-theme-value"); 4277 if (VALUES.indexOf(pref) === -1 || !isAllowed()) { return; } 4278 savePref(pref); 4279 apply(pref); 4280 }); 4281 4282 if (media) { 4283 var onChange = function () { 4284 if (readPref() === "system") { apply("system"); } 4285 }; 4286 if (media.addEventListener) { 4287 media.addEventListener("change", onChange); 4288 } else if (media.addListener) { 4289 media.addListener(onChange); 4290 } 4291 } 4292})(); 4293 4294/* ÐдноÑазовÑй за ÑеÑÑÐ¸Ñ Ð·Ð°Ð¼ÐµÑ Ñого, какое оÑоÑмление ÑÑÐ¾Ð¸Ñ Ñ Ð¿Ð¾Ð»ÑзоваÑÐµÐ»Ñ Ð² ÐС: 4295 нÑжен, ÑÑÐ¾Ð±Ñ ÑеÑаÑÑ Ð¿Ñо ÑÐµÐ¼Ñ ÑайÑа по Ñвоим ÑиÑÑам. ТолÑко Ð´Ð»Ñ Ð°Ð²ÑоÑизованнÑÑ . */ 4296(function () { 4297 if (document.documentElement.getAttribute("data-theme-allowed") !== "1") { return; } 4298 4299 try { 4300 if (sessionStorage.getItem("ua_scheme_probe") === "1") { return; } 4301 } catch (e) { return; } 4302 4303 var media = window.matchMedia ? window.matchMedia("(prefers-color-scheme: dark)") : null; 4304 var scheme = media ? (media.matches ? "dark" : "light") : "unknown"; 4305 var url = "/ux/scheme/" + scheme; 4306 4307 var send = function () { 4308 try { 4309 if (navigator.sendBeacon) { 4310 navigator.sendBeacon(url); 4311 } else { 4312 fetch(url, { method: "POST", keepalive: true, credentials: "same-origin" }); 4313 } 4314 sessionStorage.setItem("ua_scheme_probe", "1"); 4315 } catch (e) { /* Ð·Ð°Ð¼ÐµÑ Ð½Ðµ должен меÑаÑÑ ÑайÑÑ */ } 4316 }; 4317 4318 if ("requestIdleCallback" in window) { 4319 requestIdleCallback(send, { timeout: 3000 }); 4320 } else { 4321 setTimeout(send, 1500); 4322 } 4323})();
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.