1(function () { 2 const { mergeObjects, isBuilder } = BreakdanceFrontend.utils; 3 4 class BreakdanceContentReveal { 5 defaultOptions = { 6 hideBy: "height", 7 maxHeight: { unit: "px", value: "250", style: "250px" }, 8 maxLines: 10, 9 scrollToTop: false, 10 }; 11 12 isTransitioning = false; 13 visibleClass = "is-visible"; 14 hiddenClass = "is-hidden"; 15 transitionClass = "is-transitioning"; 16 hideOverlayClass = "hide-overlay"; 17 18 constructor(selector, options) { 19 this.show = this.show.bind(this); 20 this.hide = this.hide.bind(this); 21 this.toggle = this.toggle.bind(this); 22 this.onShowComplete = this.onShowComplete.bind(this); 23 this.onHideComplete = this.onHideComplete.bind(this); 24 25 this.selector = selector; 26 this.options = mergeObjects(this.defaultOptions, options); 27 this.isBuilder = isBuilder(); 28 29 this.init(); 30 } 31 32 isVisible() { 33 return this.container.classList.contains(this.visibleClass); 34 } 35 36 show() { 37 if (this.isTransitioning || this.isVisible()) { 38 return; 39 } 40 41 this.buttonContent.textContent = this.i18nLess; 42 43 this.container.classList.remove(this.hiddenClass); 44 this.container.classList.add(this.transitionClass); 45 this.container.classList.add(this.hideOverlayClass); 46 47 this.isTransitioning = true; 48 49 // Start height 50 this.setHiddenHeight(); 51 52 this.content.offsetHeight; // force repaint 53 54 this.onTransitionComplete() 55 .then(this.onShowComplete); 56 57 // End height 58 this.setVisibleHeight(); 59 } 60 61 hide() { 62 if (this.isTransitioning || !this.isVisible()) { 63 return; 64 } 65 66 this.buttonContent.textContent = this.i18nMore; 67 68 this.container.classList.remove(this.visibleClass); 69 this.container.classList.add(this.transitionClass); 70 this.container.classList.remove(this.hideOverlayClass); 71 72 this.isTransitioning = true; 73 74 this.setVisibleHeight(); 75 76 this.content.offsetHeight; // force repaint 77 78 this.onTransitionComplete() 79 .then(this.onHideComplete); 80 81 // End height 82 this.setHiddenHeight(); 83 } 84 85 onTransitionComplete() { 86 const duration = window.getComputedStyle(this.content).getPropertyValue("transition-duration"); 87 88 // If duration is 0s, transitionend will not fire. 89 if (duration === "0s") { 90 return Promise.resolve(); 91 } 92 93 return new Promise((resolve) => { 94 this.content.addEventListener("transitionend", resolve, { once: true }); 95 }); 96 } 97 98 onShowComplete(event) { 99 this.isTransitioning = false; 100 this.container.classList.remove(this.transitionClass); 101 this.container.classList.add(this.visibleClass); 102 } 103 104 onHideComplete(event) { 105 this.isTransitioning = false; 106 this.container.classList.remove(this.transitionClass); 107 this.container.classList.add(this.hiddenClass); 108 109 if (this.options.scrollToTop) { 110 this.scrollToTop(); 111 } 112 } 113 114 forceHide() { 115 this.container.classList.add(this.hiddenClass); 116 this.container.classList.remove(this.visibleClass); 117 this.container.classList.remove(this.transitionClass); 118 this.setHiddenHeight(); 119 } 120 121 toggle() { 122 if (this.isVisible()) { 123 this.hide(); 124 } else { 125 this.show(); 126 } 127 } 128 129 setHiddenHeight() { 130 if (this.options.hideBy === "height") { 131 this.setHeight(this.options.maxHeight.style); 132 } else if (this.options.hideBy === "lines") { 133 const lineHeight = this.calculateLineHeight(); 134 this.setHeight(`${lineHeight}px`); 135 } 136 } 137 138 setVisibleHeight() { 139 this.setHeight(`${this.content.scrollHeight}px`); 140 } 141 142 calculateLineHeight() { 143 const element = this.content; 144 const rawLineHeight = window.getComputedStyle(element).getPropertyValue("line-height"); 145 const lineHeight = Number(rawLineHeight.replace("px", "")); 146 const maxLines = this.options.maxLines; 147 const padding = 10; 148 149 if (isNaN(lineHeight)) { 150 let clone = element.cloneNode(); 151 clone.innerHTML = "<br>"; 152 element.appendChild(clone); 153 let singleLineHeight = clone.offsetHeight; 154 clone.innerHTML = "<br><br>"; 155 let doubleLineHeight = clone.offsetHeight; 156 element.removeChild(clone); 157 return (doubleLineHeight - singleLineHeight) * maxLines + padding; 158 } 159 160 return lineHeight * maxLines + padding; 161 } 162 163 setHeight(newHeight) { 164 this.element.style.setProperty("--bde-content-reveal-height", newHeight); 165 } 166 167 init() { 168 this.element = document.querySelector(this.selector); 169 this.container = this.element.querySelector(".bde-content-reveal__container"); 170 this.content = this.element.querySelector(".bde-content-reveal__content"); 171 this.revealButton = this.element.querySelector(".bde-content-reveal__button"); 172 this.buttonContent = this.revealButton.querySelector(".button-atom__text"); 173 174 this.i18nMore = this.revealButton.dataset.i18nMore; 175 this.i18nLess = this.revealButton.dataset.i18nLess; 176 177 if (isBuilder()) { 178 // Force builder to show the correct button text on property change. 179 // This is a workaround for Vue not updating the button text. 180 this.buttonContent.textContent = this.i18nMore; 181 } 182 183 this.revealButton.addEventListener("click", this.toggle); 184 185 this.forceHide(); 186 } 187 188 update(options) { 189 if (options) { 190 this.options = mergeObjects(this.defaultOptions, options); 191 } 192 193 this.destroy(); 194 this.init(); 195 } 196 197 destroy() { 198 this.container.classList.remove(this.hideOverlayClass); 199 this.revealButton.removeEventListener("click", this.toggle); 200 } 201 202 scrollToTop() { 203 this.element.scrollIntoView({ behavior: "smooth", block: "center" }); 204 } 205 } 206 207 window.BreakdanceContentReveal = BreakdanceContentReveal; 208})();
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.