1function ENCardSlider(params) { 2 3 this.init = function(params) { 4 let me = this; 5 this.container = params.container; 6 if (params.maxColumnGap >= 0) { 7 this.maxColumnGap = params.maxColumnGap; 8 } else if (params.maxColumnGap && params.maxColumnGap.endsWith("px") && parseInt(params.maxColumnGap) >= 0) { 9 this.maxColumnGap = parseInt(params.maxColumnGap); 10 } 11 12 this.slideSingleCard = params.slideSingleCard; 13 14 this.allowManualPadding = params.allowManualPadding == 1 || params.allowManualPadding === true; 15 this.align = (params.align == "left" || params.align == "right") ? params.align : "center"; 16 this.showHiddenElements = params.showHiddenElements == 1 || params.showHiddenElements === true; 17 18 if (!this.container) { 19 throw("The <DIV> element for the container is not provided. (container=null)"); 20 } 21 22 if (this.container.nodeName != "DIV") { 23 throw("ENCardSlider's container must be a DIV element. " + this.container.nodeName + " is invalid."); 24 } 25 26 let i = this.container.getElementsByClassName("en-card-slider-inner-container"); 27 if (i.length == 0) { 28 throw("Inner container doesn't exist."); 29 } else if (i.length > 1) { 30 throw(i.length + " inner containers found. Only 1 is expected."); 31 } else if (i[0].parentNode != this.container) { 32 throw("Inner container's parentNode is not the main container."); 33 } 34 this.innerContainer = i[0]; 35 36 let contStyles = new Map(); 37 contStyles.set("overflow-x", "hidden"); 38 contStyles.set("padding", "0px"); 39 40 let innerContStyles = new Map(); 41 innerContStyles.set("position", "relative"); 42 innerContStyles.set("display", "flex"); 43 innerContStyles.set("flex-direction", "row"); 44 innerContStyles.set("border", "none"); 45 innerContStyles.set("padding", "0px"); 46 innerContStyles.set("margin-left", "0px"); 47 innerContStyles.set("margin-right", "0px"); 48 49 let childStyles = new Map(); 50 childStyles.set("overflow", "hidden"); 51 childStyles.set("flex-shrink", "0"); 52 childStyles.set("margin-left", "0px"); 53 childStyles.set("margin-right", "0px"); 54 childStyles.set("min-width", "auto"); 55 childStyles.set("max-width", "none"); 56 57 this.enforceStyles(this.container, contStyles); 58 this.enforceStyles(this.innerContainer, innerContStyles); 59 60 let innerContCompStyles = window.getComputedStyle(this.innerContainer); 61 62 let gap = innerContCompStyles.getPropertyValue("column-gap"); 63 if (gap.endsWith("px")) { 64 this.minColumnGap = parseInt(gap); 65 } else { 66 this.minColumnGap = 8; 67 } 68 69 let cardWidth; 70 71 if (this.innerContainer.children.length == 0) { 72 console.log("ENCardSlider Warning: No child element (card) exists in the inner container."); 73 } 74 75 for (let x = 0; x < this.innerContainer.children.length; x++) { 76 let c = this.innerContainer.children[x]; 77 this.enforceStyles(c, childStyles); 78 if (x == 0) { 79 cardWidth = c.offsetWidth; 80 if (cardWidth > this.innerContainer.clientWidth) { 81 throw("First card's width is smaller than the container's width. Container: " + this.innerContainer.clientWidth + "px - Card: " + cardWidth + "px"); 82 } 83 } else { 84 if (cardWidth != c.offsetWidth) { 85 console.log("ENCardSlider Warning: All cards must have the equal width. Width of the card #" + x + " is " + c.offsetWidth + "px. Expected: " + cardWidth + "px"); 86 } 87 } 88 89 try { 90 let childResizeObserver = new ResizeObserver(entries => { this.adjust(); }); 91 childResizeObserver.observe(c); 92 } catch (e) { 93 console.log("ENCardSlider Warning: Resize observer is not available. It's OK."); 94 } 95 } 96 97 let transition = innerContCompStyles.getPropertyValue("transition"); 98 if (!transition.includes("transform")) { 99 this.innerContainer.style['transition'] = "transform 0.6s ease-out"; 100 } 101 102 this.innerContainer.style["transform"] = "translateX(0px)"; 103 this.curIndex = 0; 104 105 try { 106 let resizeObserver = new ResizeObserver(entries => { this.adjust(); }); 107 resizeObserver.observe(this.container); 108 } catch (e) {
109 console.log("ENCardSlider Warning: Resize observer is not available. It's OK."); 110 } 111 112 113 if (params.prevButton) { 114 this.prevButton = params.prevButton; 115 this.prevButton.addEventListener("click", () => { 116 me.prev(); 117 }); 118 } 119 if (params.nextButton) { 120 this.nextButton = params.nextButton; 121 this.nextButton.addEventListener("click", () => { 122 me.next(); 123 }); 124 } 125 this.adjust(); 126 } 127 128 this.enforceStyles = function(element, styles) { 129 let computedStyles = window.getComputedStyle(element); 130 for (const [key, value] of styles.entries()) { 131 if (computedStyles.getPropertyValue(key) != value) { 132 this.setImportantStyle(element, key, value); 133 } 134 } 135 } 136 137 this.setImportantStyle = function(element, attribute, value) { 138 element.style[attribute] = value; 139 element.setAttribute("style", element.getAttribute("style").replace((attribute + ": " + value + ";"), (attribute + ": " + value + " !important;"))); 140 } 141 142 this.move = function(index) { 143 index = Math.max(0, Math.min(index, this.getMaxIndex())); 144 if (isNaN(index)) { 145 index = 0; 146 } 147 let cardWidth = this.innerContainer.children[0].offsetWidth; 148 let curGap = parseInt(window.getComputedStyle(this.innerContainer).getPropertyValue("column-gap")); 149 let x = index * (cardWidth + curGap); 150 if (!this.paddingOnSides) { 151 x += curGap; 152 } 153 this.innerContainer.style["transform"] = "translateX(-" + x + "px)"; 154 this.curIndex = index; 155 if (this.prevButton) { 156 if (this.curIndex == 0) { 157 this.prevButton.classList.add("en-card-slider-button-disabled"); 158 } else { 159 this.prevButton.classList.remove("en-card-slider-button-disabled"); 160 } 161 } 162 if (this.nextButton) { 163 if (this.curIndex >= this.getMaxIndex()) { 164 this.nextButton.classList.add("en-card-slider-button-disabled"); 165 } else { 166 this.nextButton.classList.remove("en-card-slider-button-disabled"); 167 } 168 } 169 // The leftover space travels with the visible window (as a margin on the first/last 170 // visible card, per the align setting) so the neighboring off-screen cards can never 171 // flow into it. In the stretch mode the margins stay on the overall first/last card, 172 // and with showHiddenElements the space is deliberately left open so the neighboring 173 // cards stay partially visible in it (styled via the hidden-card class in CSS). 174 let windowed = this.paddingOnSides && !this.showHiddenElements; 175 let marginLeftIndex = windowed ? this.curIndex : 0; 176 let marginRightIndex = windowed ? (this.curIndex + this.numberVisibleCards - 1) : (this.innerContainer.children.length - 1); 177 for (let i = 0; i < this.innerContainer.children.length; i++) { 178 let c = this.innerContainer.children[i]; 179 this.setImportantStyle(c, "margin-left", (i == marginLeftIndex ? this.windowLeftPad : 0) + "px"); 180 this.setImportantStyle(c, "margin-right", (i == marginRightIndex ? this.windowRightPad : 0) + "px"); 181 if (i < this.curIndex || i >= this.curIndex + this.numberVisibleCards) { 182 c.classList.add("en-card-slider-card-hidden"); 183 } else { 184 c.classList.remove("en-card-slider-card-hidden"); 185 } 186 } 187 } 188 189 this.prev = function() { 190 this.curIndex = Math.max(0, this.curIndex - this.numberCardsToSlide); 191 this.move(this.curIndex); 192 } 193 194 this.next = function() { 195 this.curIndex = Math.min(this.getMaxIndex(), this.curIndex + this.numberCardsToSlide); 196 this.move(this.curIndex); 197 } 198 199 this.getFitGap = function() { 200 if (this.allowManualPadding) { 201 let gap = parseInt(window.getComputedStyle(this.innerContainer).getPropertyValue("column-gap")); 202 return isNaN(gap) ? 0 : gap; 203 } 204 return this.minColumnGap; 205 } 206 207 this.getNumberVisibleCards = function() { 208 let availableWidth = this.container.clientWidth; 209 let cardWidth = this.innerContainer.children[0].offsetWidth; 210 let fitGap = this.getFitGap(); 211 return Math.max(1, Math.min(this.innerContainer.children.length, Math.round((availableWidth + fitGap) / (cardWidth + fitGap) - 0.5))); 212 } 213 214 this.getMaxIndex = function() { 215 return Math.max(0, this.innerContainer.children.length - this.getNumberVisibleCards()); 216 } 217 218 this.adjust = function() { 219 let numberCards = this.getNumberVisibleCards(); 220 this.numberVisibleCards = numberCards; 221 this.numberCardsToSlide = this.slideSingleCard ? 1 : numberCards; 222 223 let availableWidth = this.container.clientWidth; 224 let cardWidth = this.innerContainer.children[0].offsetWidth; 225 let remaining = availableWidth - numberCards * cardWidth; 226 227 let newGap; 228 if (this.allowManualPadding) { 229 newGap = this.getFitGap(); 230 this.paddingOnSides = true; 231 } else if (numberCards > 1) { 232 newGap = Math.round(remaining / (numberCards - 1) - 0.5); 233 if (this.maxColumnGap && newGap >
233 this.maxColumnGap) { 234 // Round up: rounding down leaves leftover pixels that let the edge of the 235 // neighboring off-screen card peek into the container. 236 newGap = (this.align == "center" && !this.showHiddenElements) ? Math.ceil(remaining / (numberCards + 1)) : this.maxColumnGap; 237 this.paddingOnSides = true; 238 } else { 239 this.paddingOnSides = false; 240 } 241 } else { 242 newGap = this.showHiddenElements ? this.getFitGap() : remaining; 243 this.paddingOnSides = true; 244 } 245 246 if (this.paddingOnSides) { 247 let leftover = remaining - (numberCards - 1) * newGap; 248 if (this.align == "left") { 249 this.windowLeftPad = 0; 250 } else if (this.align == "right") { 251 this.windowLeftPad = leftover; 252 } else { 253 this.windowLeftPad = Math.round(leftover / 2 - 0.5); 254 } 255 this.windowRightPad = this.showHiddenElements ? 0 : (leftover - this.windowLeftPad); 256 } else { 257 this.windowLeftPad = newGap; 258 this.windowRightPad = newGap; 259 } 260 261 if (!this.allowManualPadding) { 262 this.setImportantStyle(this.innerContainer, "column-gap", newGap + "px"); 263 } 264 this.move(this.curIndex); 265 } 266 267 try { 268 this.init(params); 269 } catch (e) { 270 console.error("Could not initialize ENCardSlider. Error: " + e); 271 } 272}
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.