PageSourceSearch

https://www.wagrain-kleinarl.at/js/mw-core/mw-core-teasergroup.js

js wagrain-kleinarl.at collected 2026-09-24 22:04:15 UTC 23,429 bytes, 1,102 lines download raw bytes

1import mwCore from "/js/mw-core/mw-core.js";
2import "/js/mw-core/mw-core-teaser.js";
3import "/js/mw-core/mw-core-button.js";
4
5class mwCoreTeasergroup extends mwCore {
6	// livecycle methods
7
8	beforeFirstRender() {
9		// load swiper async if necessary
10
11		if (this.variant === "slider" || this.variant === "compact" || this.variant === "slider-textbox") {
12			import("/js/mw-core/swiper.js").then((_) => {
13				// load css
14
15				let styles = document.createElement("link");
16				styles.setAttribute("href", "/css/swiper.css");
17				styles.setAttribute("rel", "stylesheet");
18
19				this.shadowRoot.appendChild(styles);
20
21				// set swiper flag
22
23				this._swiperLoaded = true;
24				this._swiperInit = false;
25
26				// rerender layout
27
28				this.reRender();
29			});
30		}
31
32		let template;
33
34		// render data
35
36		try {
37			template = this.querySelector(`template[is="component-data"]`);
38
39			if (template) {
40				this._data = JSON.parse(template.content.textContent);
41
42				// remove template from DOM
43
44				template.remove();
45			}
46		} catch (error) {}
47
48		// render sub component (constants)
49
50		let constants = this.querySelector(`template[is="subcomponent-constants"]`);
51
52		if (constants) {
53			this._CONSTANTS = JSON.parse(constants.content.textContent);
54
55			// remove template from DOM
56
57			constants.remove();
58		}
59
60		// render sub component style
61
62		let style = this.querySelector(`template[is="subcomponent-style"]`);
63
64		if (style) {
65			this._teaserStyles = style.content.textContent;
66
67			// remove template from DOM
68
69			style.remove();
70		}
71	}
72
73	updated() {
74		if (
75			(this.variant === "slider" || this.variant === "compact" || this.variant === "slider-textbox") &&
76			this._swiperLoaded &&
77			!this._swiperInit
78		) {
79			let breakpoints = {};
80
81			if (this.variant === "compact") {
82				breakpoints = {
83					0: {
84						slidesPerView: 1,
85						spaceBetween: 8
86					},
87					500: {
88						slidesPerView: 2,
89						spaceBetween: 8
90					},
91					769: {
92						slidesPerView: 3,
93						spaceBetween: 24
94					},
95					980: {
96						slidesPerView: 4,
97						spaceBetween: 24
98					},
99					1024: {
100						slidesPerView: 5,
101						spaceBetween: 24
102					}
103				};
104			} else if (this.variant === "slider-textbox") {
105				breakpoints = {
106					0: {
107						slidesPerView: 1.2,
108						spaceBetween: 16
109					},
110					769: {
111						slidesPerView: 1.4,
112						spaceBetween: 32
113					},
114					980: {
115						slidesPerView: 1.2,
116						spaceBetween: 32
117					},
118					1200: {
119						slidesPerView: 2.2,
120						spaceBetween: 32
121					}
122				};
123			} else {
124				breakpoints = {
125					0: {
126						slidesPerView: 1.1,
127						spaceBetween: 8
128					},
129					550: {
130						slidesPerView: 1.8,
131						spaceBetween: 8
132					},
133					769: {
134						slidesPerView: 2.3,
135						spaceBetween: 32
136					},
137					980: {
138						slidesPerView: 2.6,
139						spaceBetween: 32
140					},
141					1024: {
142						slidesPerView: 3.2,
143						spaceBetween: 32
144					}
145				};
146			}
147
148			if (this.sliderbreakpoints) {
149				// get custom breakpoints (attribute or property)
150				// format px:slidesPerView
151				// example: 768:4
152
153				let customBreakpoints = this.sliderbreakpoints.split("|");
154
155				breakpoints = {};
156
157				for (let breakpoint of customBreakpoints) {
158					let pxWidth = parseFloat(breakpoint.split(":")[0]);
159					let spaceBetween = pxWidth >= 769 ? 32 : 8;
160
161					breakpoints[pxWidth] = {
162						slidesPerView: breakpoint.split(":")[1],
163						spaceBetween: spaceBetween
164					};
165				}
166			}
167
168			this._swiperInstance = new Swiper(this.shadowRoot.querySelector(".swiper-container"), {
169				pagination: {
170					el: this.shadowRoot.querySelector(".swiper-pagination"),
171					clickable: true
172				},
173				watchOverflow: true,
174				navigation: {
175					nextEl: this.shadowRoot.querySelector(".swiper-button-next"),
176					prevEl: this.shadowRoot.querySelector(".swiper-button-prev")
177				},
178				breakpoints: breakpoints
179			});
180		}
181
182		// cache DOM
183
184		this.cacheDOM();
185
186		// register eventListener
187
188		this.registerEventListener();
189	}
190
191	static get observedAttributes() {
192		return [
193			"teaserattributes",
194			"variant",
195			"sliderbreakpoints",
196			"entries",
197			"labelmore",
198			"button",
199			"grouptitle",
200			"grouptext",
201			"noentriesmessage"
202		];
203	}
204
205	// css
206
207	styles() {
208		return [
209			`<link href="/css/styleguide.css" rel="stylesheet">`,
210			`
211				/*** Global ***/
212				*:focus-visible {
213					outline: 2px solid black !important;
214					outline-offset: -2px !important;
215					transition: none !important;
216				}
217				* {
218					user-select: none;
219				}
220				a {
221					color: var(--mw-core-color-action-primary);
222					text-decoration: none;
223				}
224				a:hover {
225					text-decoration: underline;
226				}
227				.entry.invisible,
228				mw-core-teaser.invisible {
229					display: none;
230				}
231				.more-button {
232					display: flex;
233					justify-content: center;
234					margin: 20px 0 0 0;
235				}
236				.no-entries-message {
237					color: var(--mw-core-color-state-error);
238					display: table;
239					margin: 0 auto;
240					position: relative;
241				}
242				.no-entries-message p:before {
243					background: var(--mw-core-color-state-error);
244					bottom: 0;
245					content: "";
246					display: block;
247					left: 0;
248					opacity: 0.1;
249					position: absolute;
250					right: 0;
251					top: 0;
252					z-index: -1;
253				}
254				.no-entries-message p {
255					margin: 0;
256					padding: 8px 16px;
257					text-align: center;
258				}
259
260				/*** Default ***/
261
262				:host([variant="default"]) .container {
263					display: grid;
264					grid-gap: 16px;
265					grid-template-columns: repeat(1, 1fr);
266				}
267
268				/*** Swiper ***/
269
270				.container .swiper-container .swiper-slide {
271					height: auto;
272				}
273				.container .swiper-container .swiper-pagination {
274					align-items: flex-end;
275					bottom: auto;
276					display: flex;
277					height: 34px;
278					position: relative;
279				}
280				.container .swiper-container .swiper-pagination.swiper-pagination-lock {
281					display: none;
282				}
283				.container .swiper-container .swiper-pagination-bullet {
284					display: block;
285					height: 10px;
286					margin: 0 10px 0 0 !important;
287					outline: none;
288					width: 10px;
289				}
290				.container .swiper-container .swiper-pagination-bullet-active {
291					background: var(--mw-core-color-action-primary);
292				}
293				.container .swiper-button-disabled {
294					display: none !important;
295				}
296				.container .swiper-button-prev {
297					background: var(--mw-core-color-action-primary);
298					border-top-left-radius: 0;
299					border-bottom-left-radius: 0;
300					color: var(--mw-core-color-body-text-bright);
301					display: none;
302					height: 48px;
303					left: 0;
304					margin-top: -41px;
305					outline: none;
306					transition: 100ms ease background, 100ms ease color;
307					width: 48px;
308				}
309				.container .swiper-button-prev:hover {
310					background: var(--mw-core-color-action-primary-variant);
311				}
312				.container .swiper-button-prev:after {
313					content: "";
314				}
315				.container .swiper-button-next {
316					background: var(--mw-core-color-action-primary);
317					border-top-right-radius: 0;
318					border-bottom-right-radius: 0;
319					color: var(--mw-core-color-body-text-bright);
320					display: none;
321					height: 48px;
322					margin-top: -41px;
323					outline: none;
324					right: 0;
325					transition: 100ms ease background, 100ms ease color;
326					width: 48px;
327				}
328				.container .swiper-button-next:hover {
329					background: var(--mw-core-color-action-primary-variant);
330				}
331				.container .swiper-button-next:after {
332					content: "";
333				}
334
335				/*** Slider mit Textbox ***/
336
337				:host([variant="slider-textbox"]) .groupTitle {
338					margin: 0 0 16px;
339				}
340				:host([variant="slider-textbox"]) .groupText {
341					margin: 25px 0;
342				}
343				:host([variant="slider-textbox"]) .groupButton {
344					margin: 25px 0;
345				}
346				:host([variant="slider-textbox"]) .teaser-container {
347					position: relative;
348				}
349
350				/*** Grid ***/
351
352				:host([variant="grid"]) .grid-container {
353					margin: 5px 0;
354				}
355				:host([variant="grid"]) .grid-container mw-core-teaser {
356					margin: 5px 0;
357					overflow: hidden;
358				}
359
360				/*** Masonry ***/
361
362				:host([variant="masonry"]) .entry {
363					margin: 0 0 12px;
364					position: relative;
365				}
366				:host([variant="masonry"]) .entry mw-core-teaser {
367					bottom: 0;
368					left: 0;
369					right: 0;
370					position: absolute;
371					top: 0;
372				}
373				:host([variant="masonry"]) .column1 .entry:nth-child(3n + 1) {
374					padding: 100% 0 0 0;
375				}
376				:host([variant="masonry"]) .column1 .entry:nth-child(3n + 2) {
377					padding: 72% 0 0 0;
378				}
379				:host([variant="masonry"]) .column1 .entry:nth-child(3n + 3) {
380					padding: 137% 0 0 0;
381				}
382
383				:host([variant="masonry"]) .column2 .entry:nth-child(3n + 1) {
384					padding: 137% 0 0 0;
385				}
386				:host([variant="masonry"]) .column2 .entry:nth-child(3n + 2) {
387					padding: 100% 0 0 0;
388				}
389				:host([variant="masonry"]) .column2 .entry:nth-child(3n + 3) {
390					padding: 72% 0 0 0;
391				}
392
393				:host([variant="masonry"]) .column3 .entry:nth-child(3n + 1) {
394					padding: 72% 0 0 0;
395				}
396				:host([variant="masonry"]) .column3 .entry:nth-child(3n + 2) {
397					padding: 137% 0 0 0;
398				}
399				:host([variant="masonry"]) .column3 .entry:nth-child(3n + 3) {
400					padding: 100% 0 0 0;
401				}
402
403				/*** Singleteaser ***/
404
405				:host([variant="singleteaser"]) mw-core-teaser {
406					margin-bottom: 16px;
407				}
408
409				@media all and (min-width: 500px) {
410
411					/*** Default ***/
412
413					:host([variant="default"]) .container {
414						grid-template-columns: repeat(2, 1fr);
415					}
416
417					/*** Swiper ***/
418
419					.container .swiper-button-prev,
420					.container .swiper-button-next {
421						display: flex;
422					}
423
424					/*** Grid ***/
425
426					:host([variant="grid"]) .grid-container {
427						display: grid;
428						grid-gap: 12px;
429						grid-template-columns: repeat(3, 1fr);
430						grid-template-rows: auto;
431						margin: 12px 0;
432					}
433					:host([variant="grid"]) .grid-container mw-core-teaser {
434						margin: 0;
435					}
436					:host([variant="grid"]) .grid-container mw-core-teaser:nth-child(6n+1) {
437						grid-column: 1 / span 2;
438						grid-row: 1 / span 2;
439					}
440					:host([variant="grid"]) .grid-container mw-core-teaser:nth-child(6n+5) {
441						grid-row: 4 / span 1;
442					}
443					:host([variant="grid"]) .grid-container mw-core-teaser:nth-child(6n) {
444						grid-column: 2 / span 2;
445						grid-row: 3 / span 2;
446					}
447
448					/*** Masonry ***/
449
450					:host([variant="masonry"]) .masonry {
451						display: grid;
452						grid-gap: 32px;
453						grid-template-columns: repeat(3, 1fr);
454						grid-template-rows: auto;
455					}
456					:host([variant="masonry"]) .entry {
457						margin: 0 0 32px;
458					}
459
460				}
461
462				@media all and (min-width: 769px) {
463
464					/*** Default ***/
465
466					:host([variant="default"]) .container {
467						grid-gap: 32px;
468					}
469
470				}
471
472				@media all and (min-width: 980px) {
473
474					/*** Default ***/
475
476					:host([variant="default"]) .container {
477						grid-gap: 16px;
478						grid-template-columns: repeat(3, 1fr);
479					}
480
481					:host([variant="slider-textbox"]) .container {
482						display: flex;
483					}
484					:host([variant="slider-textbox"]) .container .textbox {
485						display: flex;
486						flex: 2;
487						flex-direction: column;
488						margin: 0 32px 0 0;
489						max-width: 368px;
490					}
491					:host([variant="slider-textbox"]) .groupButton {
492						margin: auto 0 34px 0;
493					}
494					:host([variant="slider-textbox"]) .teaser-container {
495						flex: 3;
496						overflow: hidden;
497						position: relative;
498					}
499
500					/*** Singleteaser ***/
501
502					:host([variant="singleteaser"]) mw-core-teaser {
503						margin-bottom: 32px;
504					}
505
506					@media all and (min-width: 1180px) {
507
508						/*** Default ***/
509	
510						:host([variant="default"]) .container {
511							grid-gap: 32px;
512						}
513	
514					}
515
516				}
517
518			`
519		];
520	}
521
522	// render template
523
524	render() {
525		// check data
526
527		if (!this._data && !this.noentriesmessage) {
528			console.error("no valid data");
529			return "";
530		} else if (!this._data && this.noentriesmessage) {
531			return `<div class="no-entries-message normal-text"><p>${this.noentriesmessage}</p></div>`;
532		}
533
534		if (!this.variant) {
535			this.variant = "default";
536		}
537
538		// detect layout
539
540		switch (this.variant) {
541			case "slider":
542				return this.layoutSlider();
543
544			case "slider-textbox":
545				return this.layoutSliderTextbox();
546
547			case "grid":
548				return this.layoutGrid();
549
550			case "masonry":
551				return this.layoutMasonry();
552
553			case "compact":
554				return this.layoutCompact();
555
556			default:
557				return this.layoutDefault();
558		}
559	}
560
561	// getter and setter
562
563	get teaserattributes() {
564		return this._getAttribute("teaserattributes");
565	}
566
567	set teaserattributes(val) {
568		this._setAttribute("teaserattributes", val);
569	}
570
571	get variant() {
572		return this._getAttribute("variant");
573	}
574
575	set variant(val) {
576		this._setAttribute("variant", val);
577	}
578
579	get sliderbreakpoints() {
580		return this._getAttribute("sliderbreakpoints");
581	}
582
583	set sliderbreakpoints(val) {
584		this._setAttribute("sliderbreakpoints", val);
585	}
586
587	get entries() {
588		return this._getAttribute("entries");
589	}
590
591	set entries(val) {
592		this._setAttribute("entries", val);
593	}
594
595	get labelmore() {
596		return this._getAttribute("labelmore");
597	}
598
599	set labelmore(val) {
600		this._setAttribute("labelmore", val);
601	}
602
603	get button() {
604		return this._getAttribute("button");
605	}
606
607	set button(val) {
608		this._setAttribute("button", val);
609	}
610
611	get grouptitle() {
612		return this._getAttribute("grouptitle");
613	}
614
615	set grouptitle(val) {
616		this._setAttribute("grouptitle", val);
617	}
618
619	get grouptext() {
620		return this._getAttribute("grouptext");
621	}
622
623	set grouptext(val) {
624		this._setAttribute("grouptext", val);
625	}
626
627	get noentriesmessage() {
628		return this._getAttribute("noentriesmessage");
629	}
630
631	set noentriesmessage(val) {
632		this._setAttribute("noentriesmessage", val);
633	}
634
635	// custom methods
636
637	cacheDOM() {
638		// cache dom elements for faster access
639
640		this._dom = {
641			morebutton: this.shadowRoot.querySelector("mw-core-button.more"),
642			teaser: this.shadowRoot.querySelectorAll("mw-core-teaser")
643		};
644	}
645
646	registerEventListener() {
647		// load further teaser
648
649		if (this._dom.morebutton) {
650			this._dom.morebutton.addEventListener("click", () => {
651				this.showMoreEntries();
652			});
653		}
654
655	}
656
657	htmlItem(data, css) {
658		// returns single teaser
659
660		return `
661			<mw-core-teaser ${this.teaserattributes ? this.teaserattributes : ``} ${css ? `class="${css}"` : ``}>
662				<template is="component-data">
663					<script type="application/json">
664						${JSON.stringify(data)}
665					</script>
666				</template>
667				${
668					this._CONSTANTS
669						? `
670					<template is="component-constants">
671						<script type="application/json">
672							${JSON.stringify(this._CONSTANTS)}
673						</script>
674					</template>
675				`
676						: ""
677				}
678				${
679					this._teaserStyles
680						? `
681					<template is="component-style">
682						<style>
683							${this._teaserStyles}
684						</style>
685					</template>
686				`
687						: ""
688				}
689			</mw-core-teaser>
690		`;
691	}
692
693	layoutDefault() {
694		let teaserEntries = ``;
695
696		// items
697
698		this._itemCounter = 0;
699
700		let counter = 0;
701
702		for (let item of this._data.items) {
703			// check counter
704
705			if (this.entries && this._itemCounter >= parseInt(this.entries)) {
706				teaserEntries += this.htmlItem(item, `invisible item${counter + 1}`);
707			} else {
708				teaserEntries += this.htmlItem(item, `item${counter + 1}`);
709				this._itemCounter++;
710			}
711
712			counter++;
713		}
714
715		return `
716
717			<div class="container">
718				${teaserEntries}
719			</div>
720
721			${
722				this.entries && parseInt(this.entries) && this._data.items.length > this._itemCounter
723					? `
724					<div class="more-button ">
725						<mw-core-button class="more" secondary label="${this.labelmore}"></mw-core-button>
726					</div>
727				`
728					: ""
729			}
730
731		`;
732	}
733
734	layoutSlider() {
735		// if swiper not loaded -> return placeholder
736
737		if (!this._swiperLoaded) {
738			return;
739		}
740
741		let teaserEntries = ``;
742
743		// items
744
745		this._itemCounter = 0;
746
747		for (let item of this._data.items) {
748			teaserEntries += this.htmlItem(item, `swiper-slide item${this._itemCounter + 1}`);
749
750			this._itemCounter++;
751		}
752
753		// swiper layout
754
755		return `
756			<div class="container">
757				<div class="teaser-container">
758					<div class="swiper-container">
759						<div class="swiper-wrapper">
760							${teaserEntries}
761						</div>
762						<div class="swiper-pagination"></div>
763					</div>
764					<div class="swiper-button-prev effect-border-radius">
765						<mw-core-icon size="m" name="2"></mw-core-icon>
766					</div>
767					<div class="swiper-button-next effect-border-radius">
768						<mw-core-icon size="m" name="3"></mw-core-icon>
769					</div>
770				</div>
771			</div>
772		`;
773	}
774
775	layoutSliderTextbox() {
776		// if swiper not loaded -> return placeholder
777
778		if (!this._swiperLoaded) {
779			return;
780		}
781
782		let teaserEntries = ``;
783
784		// items
785
786		this._itemCounter = 0;
787
788		for (let item of this._data.items) {
789			teaserEntries += this.htmlItem(item, `swiper-slide item${this._itemCounter + 1}`);
790
791			this._itemCounter++;
792		}
793
794		// button
795
796		let button = ``;
797
798		if (this.button) {
799			try {
800				let label = this.button.split(",")[0];
801				let link = this.button.split(",")[1];
802				let target = this.button.split(",")[2];
803
804				if (!link) {
805					link = `#`;
806				}
807
808				if (!target) {
809					target = ``;
810				} else {
811					target = `target="${target}"`;
812				}
813
814				button = `
815					<div class="groupButton">
816						<a href="${link}" ${target}>
817							<mw-core-button secondary label="${label}"></mw-core-button>
818						</a>
819					</div>
820				`;
821			} catch (error) {}
822		}
823
824		// set text
825
826		let textContent = ``;
827
828		if ((this.grouptitle && this.grouptitle !== true) || (this.grouptext && this.grouptext !== true)) {
829			textContent = `
830					<div class="textbox">
831						${this.grouptitle && this.grouptitle !== true ? `<p class="groupTitle headline3">${this.grouptitle}</p>` : ``}
832						${this.grouptext && this.grouptext !== true ? `<div class="groupText normal-text">${this.grouptext}</div>` : ``}
833						${button}
834					</div>
835				`;
836		}
837
838		// swiper layout
839
840		return `
841			<div class="container">
842				${textContent}
843				<div class="teaser-container">
844					<div class="swiper-container">
845						<div class="swiper-wrapper">
846							${teaserEntries}
847						</div>
848						<div class="swiper-pagination"></div>
849					</div>
850					<div class="swiper-button-prev effect-border-radius">
851						<mw-core-icon size="m" name="2"></mw-core-icon>
852					</div>
853					<div class="swiper-button-next effect-border-radius">
854						<mw-core-icon size="m" name="3"></mw-core-icon>
855					</div>
856				</div>
857			</div>
858		`;
859	}
860
861	layoutGrid() {
862		let gridRowArr = [];
863		let teaserEntries = ``;
864
865		// items
866
867		this._itemCounter = 0;
868		let counter = 0;
869
870		for (let item of this._data.items) {
871			// insert teaser in grid
872			// create arr every 6 entries
873
874			// create arr if arr not exists
875
876			if (!gridRowArr[Math.floor(counter / 6)]) {
877				gridRowArr[Math.floor(counter / 6)] = [];
878			}
879
880			// append json to arr
881
882			gridRowArr[Math.floor(counter / 6)][counter % 6] = item;
883
884			counter++;
885		}
886
887		// create grid and its entries
888
889		counter = 0;
890
891		for (let row of gridRowArr) {
892			teaserEntries += `<div class="grid-container">`;
893
894			for (let item of row) {
895				if (this.entries && this._itemCounter >= parseInt(this.entries)) {
896					teaserEntries += this.htmlItem(item, `invisible item${counter + 1}`);
897				} else {
898					teaserEntries += this.htmlItem(item, `item${counter + 1}`);
899					this._itemCounter++;
900				}
901
902				counter++;
903			}
904
905			teaserEntries += `</div>`;
906		}
907
908		return `
909
910			<div class="container">
911				${teaserEntries}
912			</div>
913
914			${
915				this.entries && parseInt(this.entries) && this._data.items.length > this._itemCounter
916					? `
917					<div class="more-button ">
918						<mw-core-button class="more" secondary label="${this.labelmore}"></mw-core-button>
919					</div>
920				`
921					: ""
922			}
923
924		`;
925	}
926
927	layoutMasonry() {
928		let teaserColumn1 = [];
929		let teaserColumn2 = [];
930		let teaserColumn3 = [];
931
932		// items
933
934		this._itemCounter = 0;
935		let counter = 0;
936
937		for (let item of this._data.items) {
938			// insert teaser in masonry
939
940			switch (counter % 3) {
941				case 0:
942					teaserColumn1.push({ content: item, counter: counter });
943					break;
944
945				case 1:
946					teaserColumn2.push({ content: item, counter: counter });
947					break;
948
949				case 2:
950					teaserColumn3.push({ content: item, counter: counter });
951					break;
952			}
953
954			counter++;
955		}
956
957		// create masonry and its entries
958
959		let teaserColumn1HTML = ``;
960
961		for (let item of teaserColumn1) {
962			if (this.entries && item.counter >= parseInt(this.entries)) {
963				teaserColumn1HTML += `
964					<div class="entry invisible item${item.counter + 1}">${this.htmlItem(item.content)}</div>
965				`;
966			} else {
967				teaserColumn1HTML += `
968					<div class="entry item${item.counter + 1}">${this.htmlItem(item.content)}</div>
969				`;
970				this._itemCounter++;
971			}
972		}
973
974		let teaserColumn2HTML = ``;
975
976		for (let item of teaserColumn2) {
977			if (this.entries && item.counter >= parseInt(this.entries)) {
978				teaserColumn2HTML += `
979					<div class="entry invisible item${item.counter + 1}">${this.htmlItem(item.content)}</div>
980				`;
981			} else {
982				teaserColumn2HTML += `
983					<div class="entry item${item.counter + 1}">${this.htmlItem(item.content)}</div>
984				`;
985				this._itemCounter++;
986			}
987		}
988
989		let teaserColumn3HTML = ``;
990
991		for (let item of teaserColumn3) {
992			if (this.entries && item.counter >= parseInt(this.entries)) {
993				teaserColumn3HTML += `
994					<div class="entry invisible item${item.counter + 1}">${this.htmlItem(item.content)}</div>
995				`;
996			} else {
997				teaserColumn3HTML += `
998					<div class="entry item${item.counter + 1}">${this.htmlItem(item.content)}</div>
999				`;
1000				this._itemCounter++;
1001			}
1002		}
1003
1004		// masonry layout
1005
1006		return `
1007			<div class="container">
1008
1009				<div class="masonry">
1010					<div class="column1">${teaserColumn1HTML}</div>
1011					<div class="column2">${teaserColumn2HTML}</div>
1012					<div class="column3">${teaserColumn3HTML}</div>
1013				</div>
1014
1015			</div>
1016
1017			${
1018				this.entries && parseInt(this.entries) && this._data.items.length > this._itemCounter
1019					? `
1020					<div class="more-button ">
1021						<mw-core-button class="more" secondary label="${this.labelmore}"></mw-core-button>
1022					</div>
1023				`
1024					: ""
1025			}
1026
1027		`;
1028	}
1029
1030	layoutCompact() {
1031		// if swiper not loaded -> return placeholder
1032
1033		if (!this._swiperLoaded) {
1034			return;
1035		}
1036
1037		let teaserEntries = ``;
1038
1039		// items
1040
1041		this._itemCounter = 0;
1042
1043		for (let item of this._data.items) {
1044			teaserEntries += this.htmlItem(item, `swiper-slide item${this._itemCounter + 1}`);
1045
1046			this._itemCounter++;
1047		}
1048
1049		// swiper layout
1050
1051		return `
1052			<div class="container">
1053				<div class="teaser-container">
1054					<div class="swiper-container">
1055						<div class="swiper-wrapper">
1056							${teaserEntries}
1057						</div>
1058						<div class="swiper-pagination"></div>
1059					</div>
1060					<div class="swiper-button-prev effect-border-radius">
1061						<mw-core-icon size="m" name="2"></mw-core-icon>
1062					</div>
1063					<div class="swiper-button-next effect-border-radius">
1064						<mw-core-icon size="m" name="3"></mw-core-icon>
1065					</div>
1066				</div>
1067			</div>
1068		`;
1069	}
1070
1071	showMoreEntries() {
1072		// show more entries
1073
1074		let start = this._itemCounter + 1;
1075
1076		for (let i = start; i < start + parseInt(this.entries); i++) {
1077			// in masonry use div.entry
1078			// else use mw-core-teaser
1079
1080			let $nextTeaser;
1081
1082			if (this.variant === "masonry") {
1083				$nextTeaser = this.shadowRoot.querySelectorAll(`.entry.item${i}`);
1084			} else {
1085				$nextTeaser = this.shadowRoot.querySelectorAll(`mw-core-teaser.item${i}`);
1086			}
1087
1088			if ($nextTeaser.length === 1) {
1089				$nextTeaser[0].classList.remove("invisible");
1090				this._itemCounter++;
1091			} else {
1092				break;
1093			}
1094		}
1095
1096		if (this._itemCounter >= this._data.items.length) {
1097			this._dom.morebutton.remove();
1098		}
1099	}
1100}
1101
1102customElements.define("mw-core-teasergroup", mwCoreTeasergroup);

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.