1(function () { 2 "use strict"; 3 4 const minimumDesktopWidthInPixels = 1024; 5 6 if (window.innerWidth < minimumDesktopWidthInPixels) { 7 return; 8 } 9 10 const initializationFlagPropertyName = "__tableFullscreenExpanderInitialized"; 11 const styleElementIdentifier = "table-fullscreen-expander-style"; 12 13 const visibleClassName = "table-expander-visible"; 14 const modalOpenClassName = "table-expander-open"; 15 16 const buttonSizeInPixels = 38; 17 const buttonMarginInPixels = 8; 18 const hideDelayInMilliseconds = 80; 19 20 if (window[initializationFlagPropertyName] === true) { 21 return; 22 } 23 24 window[initializationFlagPropertyName] = true; 25 26 let activeTableElement = null; 27 let floatingButtonElement = null; 28 let modalElement = null; 29 let modalContentElement = null; 30 let modalCloseButtonElement = null; 31 32 let hideTimeoutIdentifier = 0; 33 let positionAnimationFrameIdentifier = 0; 34 35 let isPointerInsideFloatingButton = false; 36 37 let previousDocumentOverflowValue = ""; 38 let previousBodyOverflowValue = ""; 39 let previouslyFocusedElement = null; 40 41 function initializeTableFullscreenExpander() { 42 injectStyles(); 43 44 floatingButtonElement = createFloatingButtonElement(); 45 modalElement = createModalElement(); 46 47 document.body.appendChild(floatingButtonElement); 48 document.body.appendChild(modalElement); 49 50 document.addEventListener("pointerover", handleDocumentPointerOver, true); 51 document.addEventListener("pointerout", handleDocumentPointerOut, true); 52 document.addEventListener("keydown", handleDocumentKeyDown, true); 53 54 window.addEventListener("scroll", requestFloatingButtonPositionUpdate, true); 55 window.addEventListener("resize", handleWindowResize); 56 } 57 58 function handleWindowResize() { 59 if (window.innerWidth < minimumDesktopWidthInPixels) { 60 hideFloatingButton(); 61 closeExpansionModal(); 62 return; 63 } 64 65 requestFloatingButtonPositionUpdate(); 66 } 67 68 function injectStyles() { 69 if (document.getElementById(styleElementIdentifier) !== null) { 70 return; 71 } 72 73 const styleElement = document.createElement("style"); 74 styleElement.id = styleElementIdentifier; 75 styleElement.textContent = ` 76 @media (min-width: ${minimumDesktopWidthInPixels}px) { 77 .table-expander-floating-button { 78 position: fixed; 79 left: -9999px; 80 top: -9999px; 81 z-index: 2147483646; 82 width: ${buttonSizeInPixels}px; 83 height: ${buttonSizeInPixels}px; 84 display: none; 85 align-items: center; 86 justify-content: center; 87 box-sizing: border-box; 88 border: 0; 89 border-radius: 8px; 90 background: #111827; 91 color: #ffffff; 92 font: 20px/1 Arial, sans-serif; 93 cursor: pointer; 94 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); 95 opacity: 0.92; 96 user-select: none; 97 touch-action: manipulation; 98 } 99 100 .table-expander-floating-button:hover { 101 opacity: 1; 102 } 103 104 .table-expander-floating-button.${visibleClassName} { 105 display: flex; 106 } 107 108 .table-expander-modal { 109 position: fixed; 110 inset: 0; 111 z-index: 2147483647; 112 display: none; 113 background: #ffffff; 114 } 115 116 .table-expander-modal.${modalOpenClassName} { 117 display: block; 118 } 119 120 .table-expander-window { 121 width: 100vw; 122 height: 100vh; 123 display: flex; 124 flex-direction: column; 125 background: #ffffff; 126 } 127 128 .table-expander-header { 129 flex: 0 0 auto; 130 min-height: 52px; 131 display: flex; 132 align-items: center; 133 justify-content: space-between; 134 gap: 16px; 135 padding: 0 16px; 136 box-sizing: border-box; 137 border-bottom: 1px solid #e5e7eb; 138 background: #ffffff; 139 } 140 141 .table-expander-title { 142 font: 600 16px/1.2 Arial, sans-serif; 143 color: #111827; 144 } 145 146 .table-expander-close-button { 147 width: 40px; 148 height: 40px; 149 border: 0; 150 background: transparent; 151 color: #111827; 152 font: 32px/1 Arial, sans-serif; 153 cursor: pointer; 154 } 155 156 .table-expander-content { 157 flex: 1 1 auto; 158 overflow: auto; 159 padding: 16px; 160 box-sizing: border-box; 161 background: #ffffff; 162 } 163 164 .table-expander-expanded-table { 165 width: max-content !important; 166 min-width: 100% !important; 167 max-width: none !important; 168 height: auto !important; 169 } 170 } 171 `; 172 173 document.head.appendChild(styleElement); 174 } 175 176 function createFloatingButtonElement() { 177 const createdButtonElement = document.createElement("button"); 178 179 createdButtonElement.type = "button"; 180 createdButtonElement.className = "table-expander-floating-button"; 181 createdButtonElement.textContent = "â¶"; 182 createdButtonElement.title = "РозгоÑнÑÑи ÑаблиÑÑ"; 183 createdButtonElement.setAttribute("aria-label", "РозгоÑнÑÑи ÑаблиÑÑ Ð½Ð° веÑÑ ÐµÐºÑан"); 184 185 createdButtonElement.addEventListener("click", handleFloatingButtonClick); 186 187 createdButtonElement.addEventListener("pointerenter", function () { 188 isPointerInsideFloatingButton = true; 189 clearScheduledHide(); 190 }); 191 192 createdButtonElement.addEventListener("pointerleave", function () { 193 isPointerInsideFloatingButton = false; 194 scheduleFloatingButtonHide(); 195 }); 196 197 return createdButtonElement; 198 } 199 200 function createModalElement() { 201 const createdModalElement = document.createElement("div"); 202 203 createdModalElement.className = "table-expander-modal"; 204 createdModalElement.setAttribute("role", "dialog"); 205 createdModalElement.setAttribute("aria-modal", "true"); 206 createdModalElement.setAttribute("aria-label", "РозгоÑнÑÑа ÑаблиÑÑ"); 207 208 createdModalElement.innerHTML = ` 209 <div class="table-expander-window"> 210 <div class="table-expander-header"> 211 <div class="table-expander-title">РозгоÑнÑÑа ÑаблиÑÑ</div> 212 <button class="table-expander-close-button" type="button" aria-label="ÐакÑиÑи">Ã</button> 213 </div> 214 <div class="table-expander-content"></div> 215 </div> 216 `; 217 218 modalContentElement = createdModalElement.querySelector(".table-expander-content"); 219 modalCloseButtonElement = createdModalElement.querySelector(".table-expander-close-button"); 220 221 modalCloseButtonElement.addEventListener("click", closeExpansionModal); 222 223 return createdModalElement; 224 } 225 226 function handleDocumentPointerOver(event) { 227 if (window.innerWidth < minimumDesktopWidthInPixels) { 228 return; 229 } 230 231 const tableElement = findTableElementFromEventTarget(event.target); 232 233 if (tableElement === null) { 234 return; 235 } 236 237 activeTableElement = tableElement; 238 clearScheduledHide(); 239 showFloatingButton(); 240 } 241 242 function handleDocumentPointerOut(event) { 243 if (activeTableElement === null || floatingButtonElement === null) { 244 return; 245 } 246 247 const targetElement = event.target instanceof Element ? event.target : null; 248 const relatedElement = event.relatedTarget instanceof Element ? event.relatedTarget : null; 249 250 if (targetElement === null || !activeTableElement.contains(targetElement)) { 251 return; 252 } 253 254 if ( 255 relatedElement !== null && 256 ( 257 activeTableElement.contains(relatedElement) || 258 floatingButtonElement.contains(relatedElement) 259 ) 260 ) { 261 return; 262 } 263 264 scheduleFloatingButtonHide(); 265 } 266 267 function handleFloatingButtonClick() { 268 if (activeTableElement === null) { 269 return; 270 } 271 272 openExpansionModal(activeTableElement); 273 hideFloatingButton(); 274 } 275 276 function handleDocumentKeyDown(event) { 277 if (event.key !== "Escape") { 278 return; 279 } 280 281 if (modalElement !== null && modalElement.classList.contains(modalOpenClassName)) { 282 closeExpansionModal(); 283 } 284 } 285 286 function findTableElementFromEventTarget(eventTarget) { 287 if (!(eventTarget instanceof Element)) { 288 return null; 289 } 290 291 if (modalElement !== null && modalElement.contains(eventTarget)) { 292 return null; 293 } 294 295 if (eventTarget.closest("[data-table-expander-ignore]") !== null) { 296 return null; 297 } 298 299 const tableElement = eventTarget.closest("table"); 300 301 if (!(tableElement instanceof HTMLTableElement)) { 302 return null; 303 } 304 305 return tableElement; 306 } 307 308 function showFloatingButton() { 309 if (floatingButtonElement === null) { 310 return; 311 } 312 313 floatingButtonElement.classList.add(visibleClassName); 314 updateFloatingButtonPosition(); 315 } 316 317 function hideFloatingButton() { 318 if (floatingButtonElement === null) { 319 return; 320 } 321 322 floatingButtonElement.classList.remove(visibleClassName); 323 activeTableElement = null; 324 } 325 326 function scheduleFloatingButtonHide() { 327 clearScheduledHide(); 328 329 hideTimeoutIdentifier = window.setTimeout(function () { 330 if (!isPointerInsideFloatingButton) { 331 hideFloatingButton(); 332 } 333 }, hideDelayInMilliseconds); 334 } 335 336 function clearScheduledHide() { 337 if (hideTimeoutIdentifier !== 0) { 338 window.clearTimeout(hideTimeoutIdentifier); 339 hideTimeoutIdentifier = 0; 340 } 341 } 342 343 function requestFloatingButtonPositionUpdate() { 344 if (positionAnimationFrameIdentifier !== 0) { 345 return; 346 } 347 348 positionAnimationFrameIdentifier = window.requestAnimationFrame(function () { 349 positionAnimationFrameIdentifier = 0; 350 updateFloatingButtonPosition(); 351 }); 352 } 353 354 function updateFloatingButtonPosition() { 355 if (activeTableElement === null || floatingButtonElement === null) { 356 return; 357 } 358 359 if (!document.documentElement.contains(activeTableElement)) { 360 hideFloatingButton(); 361 return; 362 } 363 364 const visibleRectangle = calculateVisibleRectangle(activeTableElement); 365 366 if (visibleRectangle.width <= 0 || visibleRectangle.height <= 0) { 367 floatingButtonElement.classList.remove(visibleClassName); 368 return; 369 } 370 371 const actualButtonWidth = floatingButtonElement.offsetWidth || buttonSizeInPixels; 372 const actualButtonHeight = floatingButtonElement.offsetHeight || buttonSizeInPixels; 373 374 const leftPosition = clampNumber( 375 visibleRectangle.right - actualButtonWidth - buttonMarginInPixels, 376 buttonMarginInPixels, 377 window.innerWidth - actualButtonWidth - buttonMarginInPixels 378 ); 379 380 const topPosition = clampNumber( 381 visibleRectangle.top + visibleRectangle.height / 2 - actualButtonHeight / 2, 382 buttonMarginInPixels, 383 window.innerHeight - actualButtonHeight - buttonMarginInPixels 384 ); 385 386 floatingButtonElement.style.left = `${Math.round(leftPosition)}px`; 387 floatingButtonElement.style.top = `${Math.round(topPosition)}px`; 388 } 389 390 function calculateVisibleRectangle(element) { 391 const elementRectangle = element.getBoundingClientRect(); 392 393 let visibleTop = Math.max(elementRectangle.top, 0); 394 let visibleRight = Math.min(elementRectangle.right, window.innerWidth);
395 let visibleBottom = Math.min(elementRectangle.bottom, window.innerHeight); 396 let visibleLeft = Math.max(elementRectangle.left, 0); 397 398 let currentAncestorElement = element.parentElement; 399 400 while ( 401 currentAncestorElement !== null && 402 currentAncestorElement !== document.body && 403 currentAncestorElement !== document.documentElement 404 ) { 405 const computedStyle = window.getComputedStyle(currentAncestorElement); 406 407 if (doesElementClipContent(computedStyle)) { 408 const ancestorRectangle = currentAncestorElement.getBoundingClientRect(); 409 410 visibleTop = Math.max(visibleTop, ancestorRectangle.top); 411 visibleRight = Math.min(visibleRight, ancestorRectangle.right); 412 visibleBottom = Math.min(visibleBottom, ancestorRectangle.bottom); 413 visibleLeft = Math.max(visibleLeft, ancestorRectangle.left); 414 } 415 416 currentAncestorElement = currentAncestorElement.parentElement; 417 } 418 419 return { 420 top: visibleTop, 421 right: visibleRight, 422 bottom: visibleBottom, 423 left: visibleLeft, 424 width: Math.max(0, visibleRight - visibleLeft), 425 height: Math.max(0, visibleBottom - visibleTop) 426 }; 427 } 428 429 function doesElementClipContent(computedStyle) { 430 const overflowValue = [ 431 computedStyle.overflow, 432 computedStyle.overflowX, 433 computedStyle.overflowY 434 ].join(" "); 435 436 return /auto|scroll|hidden|clip|overlay/.test(overflowValue); 437 } 438 439 function clampNumber(value, minimumValue, maximumValue) { 440 return Math.min(Math.max(value, minimumValue), maximumValue); 441 } 442 443 function openExpansionModal(sourceTableElement) { 444 if ( 445 modalElement === null || 446 modalContentElement === null || 447 modalCloseButtonElement === null 448 ) { 449 return; 450 } 451 452 const expandedTableElement = createExpandedTableClone(sourceTableElement); 453 454 modalContentElement.replaceChildren(expandedTableElement); 455 456 previouslyFocusedElement = document.activeElement instanceof HTMLElement 457 ? document.activeElement 458 : null; 459 460 previousDocumentOverflowValue = document.documentElement.style.overflow; 461 previousBodyOverflowValue = document.body.style.overflow; 462 463 document.documentElement.style.overflow = "hidden"; 464 document.body.style.overflow = "hidden"; 465 466 modalElement.classList.add(modalOpenClassName); 467 modalCloseButtonElement.focus({ preventScroll: true }); 468 } 469 470 function closeExpansionModal() { 471 if (modalElement === null || modalContentElement === null) { 472 return; 473 } 474 475 modalElement.classList.remove(modalOpenClassName); 476 modalContentElement.replaceChildren(); 477 478 document.documentElement.style.overflow = previousDocumentOverflowValue; 479 document.body.style.overflow = previousBodyOverflowValue; 480 481 if ( 482 previouslyFocusedElement !== null && 483 document.documentElement.contains(previouslyFocusedElement) 484 ) { 485 previouslyFocusedElement.focus({ preventScroll: true }); 486 } 487 488 previouslyFocusedElement = null; 489 } 490 491 function createExpandedTableClone(sourceTableElement) { 492 const expandedTableElement = sourceTableElement.cloneNode(true); 493 494 expandedTableElement.removeAttribute("id"); 495 496 expandedTableElement.querySelectorAll("[id]").forEach(function (elementWithIdentifier) { 497 elementWithIdentifier.removeAttribute("id"); 498 }); 499 500 expandedTableElement.classList.add("table-expander-expanded-table"); 501 502 return expandedTableElement; 503 } 504 505 if (document.readyState === "loading") { 506 document.addEventListener("DOMContentLoaded", initializeTableFullscreenExpander, { once: true }); 507 } else { 508 initializeTableFullscreenExpander(); 509 } 510})();
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.