1(function () { 2 const { mergeObjects, debounce, getCurrentBreakpoint, onResize } = BreakdanceFrontend.utils; 3 4 class BreakdanceGallery { 5 defaultOptions = { 6 type: "grid", 7 mode: "single", 8 row_height: { 9 breakpoint_base: { 10 number: 300 11 } 12 }, 13 columns: { 14 breakpoint_base: 3, 15 breakpoint_phone_portrait: 1, 16 }, 17 slider: { 18 settings: null, 19 pagination: null, 20 }, 21 defaultActiveIndex: 0 22 }; 23 24 items = []; 25 filterActiveClass = "is-active"; 26 27 constructor(selector, options) { 28 this.relayout = this.relayout.bind(this); 29 this.resize = this.resize.bind(this); 30 this.update = debounce(this._update, 100); 31 this.onImagesLoaded = this.onImagesLoaded.bind(this); 32 this.onFilterChange = this.onFilterChange.bind(this); 33 this.selector = selector; 34 this.options = mergeObjects(this.defaultOptions, options); 35 this.init(); 36 } 37 38 checkForImagesLoaded() { 39 return this.options.type === "masonry"; 40 } 41 42 getIsotopeSettings(layoutInstant = true) { 43 return { 44 layoutMode: this.getLayoutMode(), 45 layoutInstant, 46 packery: { gutter: '.ee-gallery-gutter' }, 47 masonry: { gutter: '.ee-gallery-gutter' } 48 }; 49 } 50 51 /* 52 * Turn DOM Elements into Isotope-ready Items 53 * This is required in order to make Isotope work in the Builder. 54 */ 55 itemize(items) { 56 return items.map((elem) => new Isotope.Item(elem, this.iso)); 57 } 58 59 /* 60 * Force Isotope to accept our DOM Elements. 61 * This is required due to a bug in the library. 62 * "elem instanceof HTMLCollection" always return false inside same-origin iframes 63 */ 64 appendItems(elems) { 65 const items = this.itemize(elems); 66 67 if (!items.length) return; 68 69 this.iso.items = items; 70 this.iso.filteredItems = items; 71 } 72 73 relayout() { 74 if (!this.iso) return; 75 this.iso.layout(); 76 } 77 78 getItemElems(elem) { 79 return [].slice.call(elem.querySelectorAll(".ee-gallery-item")); 80 } 81 82 onImagesLoaded() { 83 this.relayout(); 84 } 85 86 determineFilter(category) { 87 return category === "all" ? "*" : `[data-category="${category}"]`; 88 } 89 90 // Filters 91 filterBy(node) { 92 const category = (node.value || node.id).split("-").pop(); 93 94 if (this.options.type === "slider") { 95 this.filterSlider(category); 96 } else { 97 const filter = this.determineFilter(category); 98 this.iso.arrange({ 99 layoutInstant: false, 100 filter, 101 }); 102 } 103 104 const button = this.filterButtons.find((button) => button.id === node.id); 105 106 if (button) this.setActiveButton(button); 107 108 const customEvent = new CustomEvent("breakdance_gallery_filtered", { 109 detail: { 110 category, 111 }, 112 }); 113 114 this.element.dispatchEvent(customEvent); 115 } 116 117 filterSlider(category) { 118 const swiperEl = this.element.querySelector(".swiper"); 119 120 const itemsToShow = category === "all" ? this.items : this.items.filter((item) => { 121 return item.dataset.category === category 122 }); 123 const itemsToHide = this.items.filter(x => !itemsToShow.includes(x)); 124 125 itemsToShow.forEach(item => item.style.display = null); 126 itemsToHide.forEach(item => item.style.display = "none"); 127 128 swiperEl.swiper.update(); 129 swiperEl.swiper.slideTo(0, 0); 130 } 131 132 setActiveButton(activeButton) { 133 this.filterButtons.forEach((button) => { 134 button.classList.remove(this.filterActiveClass); 135 }); 136 137 activeButton.classList.add(this.filterActiveClass); 138 } 139 140 onFilterChange(event) { 141 event.preventDefault(); 142 143 const target = event.currentTarget; 144 this.filterBy(target); 145 this.maybeJustifyItems(); 146 } 147 148 getAll(selector) { 149 return [].slice.call(this.element.querySelectorAll(selector)); 150 } 151 152 // The filter bar is only available when the gallery type is "multiple". 153 initFilters() { 154 const mobileFilter = this.element.querySelector(".js-tab-select"); 155 156 this.filterButtons = this.getAll(".js-tab"); 157 158 this.filterButtons.forEach((button, index) => { 159 if (index === this.options.defaultActiveIndex) { 160 this.filterBy(button); 161 } 162 163 button.addEventListener("click", this.onFilterChange); 164 }); 165 166 if (mobileFilter) { 167 mobileFilter.addEventListener("change", this.onFilterChange); 168 } 169 } 170 171 maybeJustifyItems() { 172 if (this.options.type !== "justified") { 173 return this.unjustifyItems(); 174 } 175 176 const justifiedLayout = require("justified-layout"); // TODO: Remove dependency on browserify. 177 178 const items = this.iso.filteredItems; 179 180 const geometry = items.map((item) => { 181 const [width, height] = item.element.dataset.lgSize.split("-"); 182 return { width, height }; 183 }); 184 185 const config = { 186 containerWidth: this.galleryEl.clientWidth, 187 containerPadding: 0, 188 boxSpacing: this.getResponsiveUnit("gap"), 189 targetRowHeight: this.getResponsiveUnit("row_height"), 190 }; 191 192 console.log(config); 193 194 const output = justifiedLayout(geometry, config); 195
196 items.forEach((item, index) => { 197 const box = output.boxes[index]; 198 item.element.style.width = `${box.width}px`; 199 item.element.style.height = `${box.height}px`; 200 }); 201 202 this.relayout(); 203 } 204 205 unjustifyItems() { 206 if (!this.iso) return; 207 208 const items = this.iso.filteredItems; 209 items.forEach((item, index) => { 210 item.element.style.removeProperty("width"); 211 item.element.style.removeProperty("height"); 212 }); 213 } 214 215 getResponsiveUnit(path) { 216 if (this.options[path] === undefined) return 0; 217 218 const availableBreakpoints = Object.keys(this.options[path]); 219 const currBreakpoint = getCurrentBreakpoint(availableBreakpoints); 220 221 // version >= 1.1 - backwards compatibility for non-breakpoint values. 222 const value = this.options[path][currBreakpoint.id]?.number || this.options[path].number || 0; 223 224 return Math.max(value, 0); 225 } 226 227 getLayoutMode() { 228 const type = this.options.type || "grid"; 229 230 const layoutModes = { 231 grid: "masonry", 232 masonry: "masonry", 233 justified: "packery", 234 }; 235 236 return layoutModes[type]; 237 } 238 239 refresh(options = {}) { 240 this.options = mergeObjects(this.defaultOptions, options); 241 this.maybeJustifyItems(); 242 243 const config = this.getIsotopeSettings(); 244 this.iso.arrange(config); 245 this.init(); 246 } 247 248 _update(options = {}) { 249 this.options = mergeObjects(this.defaultOptions, options); 250 this.destroy(); 251 this.init(); 252 } 253 254 // Fires on resize and when the element becomes visible after removing display: none 255 resize() { 256 this.maybeJustifyItems(); 257 this.relayout(); 258 } 259 260 destroy() { 261 this.destroyIsotope(); 262 this.disconnect(); 263 } 264 265 destroyIsotope() { 266 if (!this.iso) return; 267 268 this.iso.destroy(); 269 this.iso = null; 270 271 if (this.checkForImagesLoaded()) { 272 imagesLoaded(this.element).off("progress", this.onImagesLoaded); 273 } 274 } 275 276 initIsotope(layoutInstant) { 277 const config = this.getIsotopeSettings(layoutInstant); 278 279 this.iso = new Isotope(this.galleryEl, config); 280 this.appendItems(this.items); 281 282 this.relayout(); 283 this.maybeJustifyItems(); 284 285 if (this.checkForImagesLoaded()) { 286 imagesLoaded(this.element).on("progress", this.onImagesLoaded); 287 } 288 } 289 290 init(layoutInstant) { 291 this.element = document.querySelector(this.selector); 292 this.galleryEl = this.element.querySelector(".ee-gallery"); 293 this.items = this.getItemElems(this.element); 294 295 // For performance purposes, the combination of "single" mode and "grid" type is css-only and does not rely on Isotope. 296 const cssOnlyMode = this.options.mode === "single" && this.options.type === "grid"; 297 const isSlider = this.options.type === "slider"; 298 299 if (!isSlider && !cssOnlyMode) { 300 this.initIsotope(layoutInstant); 301 } 302 303 this.initFilters(); 304 305 this.disconnect = onResize(this.resize); 306 } 307 } 308 309 window.BreakdanceGallery = BreakdanceGallery; 310})();
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.