PageSourceSearch

https://saimaageopark.fi/wp-content/themes/wordpresstemplate3_9/tools/script_L.js

js saimaageopark.fi collected 2026-10-02 08:39:56 UTC 22,276 bytes, 688 lines download raw bytes

1"use strict";
2
3/*
4	Interactivity for L layout elements
5*/
6
7window.Lview = {
8	gallery: {
9		// !!! NOTE !!! if there's a weird random image size error in galleries, it may be because the width is set to something like XX.00000000001, which CSS rejects because it's too long. If that happens, more meticulous parsing of the number is required.
10		popup_currentGallery: 0,
11		popup_currentImage: 0,
12		popup_initialized: false,
13		get_popup_window: function () {
14			let popup = D.ID("gallerypopup");
15			if (popup) return popup;
16			
17			function close (event) {
18				let popup = this;
19				popup.classList.remove("visible");
20				setTimeout(function(){if (!popup.classList.contains("visible")) REMOVE(popup);},500);
21			}
22			function prev (event) {
23				event.stopPropagation();
24				Lview.gallery.popup_flip(-1);
25			}
26			function next (event) {
27				event.stopPropagation();
28				Lview.gallery.popup_flip(1);
29			}
30			function info (event) {
31				event.preventDefault();
32				event.stopPropagation();
33				let popup = D.ID("popup");
34				let infocontainer = popup.CLASS("infocontainer");
35				if (infocontainer.className.indexOf("opened") >= 0) {
36					infocontainer.classList.remove("opened");
37				}
38				else {
39					infocontainer.classList.add("opened");
40					
41					infocontainer.innerHTML = STRING(Lview.gallery.popup_currentImage.dataset.caption);
42				}
43				return false;
44			}
45			
46			popup = CREATE("div", {id:"popup", className:"popup", event:["click", close]}, [
47				CREATE("div", {className:"imagecontainer"}),
48				CREATE("div", {className:"prev", event:["click", prev], innerHTML:'<img src="'+wpinfo.url_template+'/res/icon_left.svg">'}),
49				CREATE("div", {className:"info", event:["click", info], innerHTML:'<img src="'+wpinfo.url_template+'/res/icon_info.png">'}),
50				CREATE("div", {className:"next", event:["click", next], innerHTML:'<img src="'+wpinfo.url_template+'/res/icon_right.svg">'}),
51				CREATE("div", {className:"infocontainer", event:["click", info]}),
52			]);
53			D.body.appendChild(popup);
54			return popup;
55		},
56		popup_flip: function (pos) {
57			// find next image
58			let img = Lview.gallery.popup_currentImage;
59			if (pos > 0) {
60				img = img.nextSibling;
61				if (!img) {
62					img = Lview.gallery.popup_currentImage.parentNode.CLASS("L-galleryimage");
63				}
64			}
65			else if (pos < 0) {
66				img = img.previousSibling;
67				if (!img) {
68					img = Lview.gallery.popup_currentImage.parentNode.CLASSES("L-galleryimage").LAST();
69				}
70			}
71			Lview.gallery.popup_currentImage = img;
72			
73			// transition images
74			let popup = D.ID("popup");
75			let imagecontainer = popup.CLASS("imagecontainer");
76			let old = imagecontainer.CLASS("current");
77			old.classList.remove("current");
78			let newimg = imagecontainer.appendChild(CREATE("img", {src:img.href, className:"current"}));
79			if (pos > 0) {
80				newimg.classList.add("inright");
81				if (old) old.classList.add("outleft");
82			}
83			else {
84				newimg.classList.add("inleft");
85				if (old) old.classList.add("outright");
86			}
87			setTimeout(function(){REMOVE(old);}, 1000);
88			
89			// caption
90			let infocontainer = popup.CLASS("infocontainer");
91			if (img.dataset.caption) {
92				infocontainer.innerHTML = img.dataset.caption;
93				popup.classList.remove("nocaption");
94			}
95			else {
96				infocontainer.innerHTML = "";
97				popup.classList.add("nocaption");
98			}
99		},
100		popup_initItem: function (item) {
101			function imageClicked(event) {
102				event.preventDefault();
103				event.stopPropagation();
104				
105				Lview.gallery.popup_currentGallery = this.PWC("L-type-gallery");
106				Lview.gallery.popup_currentImage = this;
107				
108				let popup = Lview.gallery.get_popup_window();
109				
110				let imagecontainer = popup.CLASS("imagecontainer");
111				imagecontainer.innerHTML = "";
112				if (this.dataset.iframe) {
113					imagecontainer.appendChild(CREATE("iframe", {src:this.href, className:"current"}));
114				}
115				else {
116					imagecontainer.appendChild(CREATE("img", {src:this.href, className:"current"}));
117				}
118				
119				if (Lview.gallery.popup_currentGallery && Lview.gallery.popup_currentGallery.CLASSES("L-galleryimage").length > 1) {
120					popup.classList.add("canflip");
121				}
122				else {
123					popup.classList.remove("canflip");
124				}
125				// info
126				if (this.dataset.caption) {
127					popup.classList.remove("nocaption");
128				}
129				else {
130					popup.classList.add("nocaption");
131				}
132				
133				// open popup display
134				requestAnimationFrame(function(){
135				requestAnimationFrame(function(){
136				requestAnimationFrame(function(){
137					popup.classList.add("visible");
138				});
139				});
140				});
141				return false;
142			}
143			item.addEventListener("click", imageClicked);
144		},
145		popup_init: function (gallery) {
146			// handle galleries
147			let items = gallery.CLASSES("L-galleryimage");
148			for (let item of items) {
149				Lview.gallery.popup_initItem(item);
150			}
151		},
152		
153		carousel_containers: [],
154		carousel_lastTime: 0,
155		carousel_looping: false,
156		carousel_loop: function() {
157			// loop through swapboxes, and switch the image if needed
158			let now = Date.now();
159			// var delta = now - scrolldisp.lasttime;
160			let delta = Math.min(1000, now - Lview.gallery.carousel_lastTime);
161			Lview.gallery.carousel_lastTime = now;
162			for (let me of Lview.gallery.carousel_containers) {
163				let cInner = me.element.CLASS("L-carouselinner");
164				let cContents = me.element.CLASS("L-carouselcontents");
165				
166				if (me.hovering) {
167					me.spdmod = Math.max(0, me.spdmod-0.05);
168				}
169				else{
170					me.spdmod = Math.min(1, me.spdmod+0.033);
171				}
172				// move things
173				me.posoffset -= me.speed*delta/1000*me.spdmod,
174				
175				cInner.style.left = Math.floor(me.posoffset) + "px";
176
177				// figure out if elements need to be deleted from front
178				let innerNodes = cInner.CLASSES("L-galleryimage");
179				let contentNodes = cContents.CLASSES("L-galleryimage");
180				if (innerNodes[0].getBoundingClientRect().right < me.element.getBoundingClientRect().left) {
181					me.posoffset = me.posoffset + innerNodes[0].offsetWidth;
182					cInner.style.left = Math.floor(me.posoffset) + "px";
183
184					cInner.appendChild(innerNodes[0]);
185				}
186
187				// fill displaycontainer if empty space has become visible
188				let failsafe = 32;
189				innerNodes = cInner.CLASSES("L-galleryimage");
190				while (
191						innerNodes.length === 0 ||
192						innerNodes[innerNodes.length-1].getBoundingClientRect().right < me.element.getBoundingClientRect().right
193					) {
194					// add nodes
195					for (let n=0; n<contentNodes.length; n++) {
196						let theelement = contentNodes[n].cloneNode(true);
197						cInner.appendChild(theelement);
198						Lview.gallery.popup_initItem(theelement);
199					}
200
201					failsafe --;
202					if (failsafe < 0) break;
203					innerNodes = cInner.CLASSES("L-galleryimage");
204				}
205			}
206			requestAnimationFrame(Lview.gallery.carousel_loop);
207		},
208		carousel_init: function (container) {
209			Lview.gallery.carousel_lastTime = Date.now();
210			
211			let cc = container.CLASSES("L-galleryimage");
212			if (cc.length) {
213				let cContents = CREATE("div", {className:"L-carouselcontents"});
214				let cInner = CREATE("div", {className:"L-carouselinner"});
215					for (let c of cc) {
216						cContents.appendChild(c);
217					}
218					container.appendChild(cInner);
219					container.appendChild(cContents);
220				let me = {
221					element: container,
222					speed: (!isNaN(Number(container.dataset.carouselspeed))) ? Number(container.dataset.carouselspeed) : 0,	// scrolling speed
223					posoffset: 0,		// automatic
224					hovering: false,	// automatic
225					spdmod: 1			// automatic
226				};
227				Lview.gallery.carousel_containers.push(me);
228				
229				// setup events and id
230				container.dataset.id = Lview.gallery.carousel_containers.length-1;
231				container.addEventListener("mouseover", function() {
232					let myid = Number(this.dataset.id);
233					Lview.gallery.carousel_containers[myid].hovering = true;
234				});
235				container.addEventListener("mouseout", function() {
236					let myid = Number(this.dataset.id);
237					Lview.gallery.carousel_containers[myid].hovering = false;
238				});
239
240				// fill displaycontainer, add every node, then add every node again if there's space left, repeat until full
241				let failsafe = 32;
242				let innerNodes = cInner.CLASSES("L-galleryimage");
243				let contentNodes = cContents.CLASSES("L-galleryimage");
244				while (
245						innerNodes.length === 0 ||
246						innerNodes[innerNodes.length-1].getBoundingClientRect().right < container.getBoundingClientRect().right
247					) {
248					// add nodes
249					for (let cn of contentNodes) {
250						let theelement = cn.cloneNode(true);
251						cInner.appendChild(theelement);
252						Lview.gallery.popup_initItem(theelement);
253					}
254
255					failsafe --;
256					if (failsafe < 0) break;
257					innerNodes = cInner.CLASSES("L-galleryimage");
258				}
259			}
260			
261			// start loop
262			if (!Lview.gallery.carousel_looping) {
263				Lview.gallery.carousel_looping = true;
264				requestAnimationFrame(Lview.gallery.carousel_loop);
265			}
266		},
267		
268		fit_refreshSquares: function (gallery) {
269			let images = gallery.CLASSES("L-galleryimage");
270			if (!images.length) return;
271			
272			let idealHeight = Number(gallery.dataset.height) || 200;
273			let padding = parseFloat(window.getComputedStyle(images[0], null).paddingLeft);
274			let containerWidth = gallery.offsetWidth;
275				//containerWidth -= parseFloat(window.getComputedStyle(gallery, null).paddingLeft) * 2;
276			
277			let sideBySideAmount = Math.max(1, Math.round(containerWidth / (idealHeight+padding*2)));
278			let size = 1/sideBySideAmount*100;
279			
280			for (let img of images) {
281				img.style.width = size+"%";
282			}
283		},
284		fit_refresh: function (gallery) {
285			let images = gallery.CLASSES("L-galleryimage");
286			if (!images.length) return;
287			
288			let idealHeight = Number(gallery.dataset.height) || 200;
289			let padding = parseFloat(window.getComputedStyle(images[0], null).paddingLeft)*2;
290			let containerWidth = gallery.offsetWidth;
291				//containerWidth -= parseFloat(window.getComputedStyle(gallery, null).paddingLeft) * 2;
292			
293			for (let i=0; i<images.length; ) {
294				let rowWidth = 0;
295				let ri = i;
296				
297				// find which images belong to row
298				for (ri; ri<images.length; ri++) {
299					let img = images[ri];
300					let ratio = Number(img.dataset.r);
301					
302					let targetOuterWidth = idealHeight*ratio + padding;
303					
304					if (rowWidth + targetOuterWidth > containerWidth) { // this image crosses the container width
305						// if image width is more than ideal height (can and maybe should use some other value as the limit), skip this image
306						if ( rowWidth > 0 && containerWidth - rowWidth < idealHeight+padding ) {
307							// skip this image (use for the next row)
308							ri --;
309						}
310						else {
311							// use this image in this row
312							rowWidth += targetOuterWidth+padding;
313						}
314						break;
315					}
316					rowWidth += targetOuterWidth+padding;
317					if (ri === images.length-1) break;
318				}
319				let rowImageCount = ri-i+1;
320				let spaceLeft = containerWidth - rowWidth;
321				let spaceForImages = containerWidth - rowImageCount*padding;
322				
323				// special case for last row, which is too short to be stretched.
324				if (ri == images.length-1 && Math.abs(spaceLeft) > idealHeight*1.5) {
325					for (let iii=i; iii<ri+1; iii++) {
326						let img = images[iii];
327						let inner = img.TAG("img");
328						let ratio = Number(img.dataset.r);
329						
330						let finalWidth = idealHeight*ratio;
331						
332						let w = Math.floor((finalWidth/spaceForImages*100) * 1000)/1000;
333						img.style.width = "calc("+ w + "% + "+padding+"px)";
334						
335						// set height (same for all items in this row)
336						inner.style.height = idealHeight+"px";
337					}
338				}
339				else {
340					// find average ratio
341					let ratioAvg = 0;
342					for (let iii=i; iii<ri+1; iii++) {
343						let img = images[iii];
344						let ratio = Number(img.dataset.r);
345						ratioAvg += ratio;
346					}
347					ratioAvg /= rowImageCount;
348					
349					// solve final size
350					let sharedHeight = 0;
351					let usedWidth = 0;
352					for (let iii=i; iii<ri+1; iii++) {
353						let img = images[iii];
354						let inner = img.TAG("img");
355						let ratio = Number(img.dataset.r);
356						
357						let finalWidth = spaceForImages/rowImageCount*(ratio/ratioAvg);
358						
359						let dw = 0;
360						if (iii < ri) {
361							dw = Math.round(finalWidth + padding);
362						}
363						else {
364							// do special trick to figure out size of last item, makes it less likely to be 1px too big or something and then wrap
365							dw = containerWidth-Math.ceil(usedWidth);
366						}
367						usedWidth += dw;
368						let w = Math.floor((dw/containerWidth*100) * 1000)/1000;
369						img.style.width = w + "%";
370						
371						// set height (same for all items in this row)
372						if (!sharedHeight) sharedHeight = Math.round(finalWidth/ratio);
373						inner.style.height = sharedHeight+"px";
374					}
375				}
376				i += rowImageCount;
377			}
378		},
379		fit_init: function (gallery) {
380			if (gallery.classList.contains("L-gshape-square")) {
381				let images = gallery.CLASSES("L-galleryimage");
382				for (let img of images) {
383					let inner = img.CLASS("arbox");
384					inner.style.width = "100%";
385					inner.style.height = "";
386				}
387			}
388			else if (gallery.classList.contains("L-gshape-default")) {
389				let images = gallery.CLASSES("L-galleryimage");
390				for (let img of images) {
391					let inner = img.TAG("img");
392					let w = NUMBER(img.dataset.w);
393					let h = NUMBER(img.dataset.h);
394					if (!w || !h) {
395						w = 10;
396						h = 10;
397					}
398					img.dataset.r = w/h;
399				}
400				
401				Lview.gallery.fit_refresh(gallery);
402			}
403		},
404		fit_carousel_init: function (gallery) {
405			let idealHeight = Number(gallery.dataset.height) || 200;
406			if (gallery.classList.contains("L-gshape-square")) {
407				let images = gallery.CLASSES("L-galleryimage");
408				for (let img of images) {
409					let inner = img.CLASS("arbox");
410					inner.style.width = "100%";
411					inner.style.height = "";
412				}
413			}
414			else if (gallery.classList.contains("L-gshape-default")) {
415				let images = gallery.CLASSES("L-galleryimage");
416				for (let img of images) {
417					let inner = img.TAG("img");
418					let w = NUMBER(img.dataset.w);
419					let h = NUMBER(img.dataset.h);
420					if (!w || !h) {
421						w = 10;
422						h = 10;
423					}
424					img.dataset.r = w/h;
425					inner.style.height = Math.round(idealHeight) + "px";
426					inner.style.width = Math.round(idealHeight * (w/h)) + "px";
427				}
428			}
429		},
430		
431		init: function (container) {
432			let galleries = container.CLASSES("L-type-gallery");
433			for (let gallery of galleries) {
434				if (gallery.classList.contains("L-gsize-fit") && !gallery.classList.contains("L-gallerycarousel")) {
435					Lview.gallery.fit_init(gallery);
436				}
437				else if (gallery.classList.contains("L-gsize-fit") && gallery.classList.contains("L-gallerycarousel")) {
438					Lview.gallery.fit_carousel_init(gallery);
439				}
440				if (gallery.classList.contains("L-gallerypopup")) {
441					Lview.gallery.popup_init(gallery);
442				}
443				if (gallery.classList.contains("L-gallerycarousel")) {
444					Lview.gallery.carousel_init(gallery);
445				}
446			}
447			
448			function fitAll() {
449				let galleries = D.CLASSES("L-gsize-fit");
450				for (let gallery of galleries) {
451					if (!gallery.classList.contains("L-gallerycarousel")) {
452						if (gallery.classList.contains("L-gshape-square")) {
453							Lview.gallery.fit_refreshSquares(gallery);
454						}
455						else {
456							Lview.gallery.fit_refresh(gallery);
457						}
458					}
459				}
460			}
461			requestAnimationFrame(fitAll);
462			window.addEventListener("resize", function(){
463				requestAnimationFrame(fitAll);
464			});
465			
466			// handle popup for stray images
467			let pics = container.CLASSES("indipop");
468			for (let pic of pics) {
469				Lview.gallery.popup_initItem(pic);
470			}
471		}
472	},
473	picswitcher: {
474		currentgallery: 0,
475		currentimage: 0,
476		idcounter: 0,
477		switchers: [],
478		looping: false,
479		switchTo: function(container, node) {
480			// set fadeouts
481			let items = container.element.CLASSES("L-switcherimage");
482			let nodeindex = 0;
483			for (let i=0; i<items.length; i++) {
484				let item = items[i];
485				// previously faded in (i.e. currently visible) gets faded out
486				if (item.classList.contains("L-picswitcherfadein") || item.classList.contains("L-picswitcherfadeinitial")) {
487					item.classList.add("L-picswitcherfadeout");
488					item.classList.remove("L-picswitcherfadenone");
489					item.classList.remove("L-picswitcherfadein");
490					item.classList.remove("L-picswitcherfadeinitial");
491				}
492				else {
493					item.classList.add("L-picswitcherfadenone");
494					item.classList.remove("L-picswitcherfadein");
495					item.classList.remove("L-picswitcherfadeout");
496				}
497				if (item === node) nodeindex = i; // for button
498			}
499			// target image gets faded in
500			node.classList.remove("L-picswitcherfadenone");
501			node.classList.remove("L-picswitcherfadeout");
502			node.classList.add("L-picswitcherfadein");
503			
504			// mod buttons
505			items = container.element.CLASSES("button");
506			for (let i=0; i<items.length; i++) {
507				items[i].classList.remove("active");
508			}
509			items[nodeindex].classList.add("active");
510			
511			if (node.dataset.isvideo) {
512				node.dataset.finished = 0;
513				let v = node.TAG("video");
514				v.play();
515			}
516			if (container.current.dataset.isvideo) {
517				let v = container.current.TAG("video");
518				v.pause();
519			}
520			
521			container.current = node;
522			container.swaptime = Date.now();
523		},
524		swapLoop: function(container) {
525			// loop through swapboxes, and switch the image if needed
526			for (let c=0; c<Lview.picswitcher.switchers.length; c++) {
527				let switcher = Lview.picswitcher.switchers[c];
528				if (D.hidden) {
529					switcher.swaptime = Date.now() - switcher.swapdelay/2;
530				}
531				else if (switcher.current.dataset.isvideo) {
532					if (switcher.current.dataset.finished == 1) {
533						switcher.current.dataset.finished = 0;
534						let v = switcher.current.TAG("video");
535						v.pause();
536						
537						let next = switcher.current.nextSibling;
538						while (next && (!next.tagName || !next.classList.contains("L-switcherimage"))) {
539							next = next.nextSibling;
540						}
541						if (!next) {
542							next = switcher.current.parentNode.CLASS("L-switcherimage");
543						}
544						Lview.picswitcher.switchTo(switcher, next);
545					}
546				}
547				else if (switcher.autoswap && switcher.swaptime+switcher.swapdelay < Date.now()) {
548					let next = switcher.current.nextSibling;
549					while (next && (!next.tagName || !next.classList.contains("L-switcherimage"))) {
550						next = next.nextSibling;
551					}
552					if (!next) {
553						next = switcher.current.parentNode.CLASS("L-switcherimage");
554					}
555					
556					Lview.picswitcher.switchTo(switcher, next);
557				}
558			};
559			requestAnimationFrame(Lview.picswitcher.swapLoop);
560		},
561		initSwitcher: function(gallery) {
562			let items = gallery.CLASSES("L-switcherimage");
563			let buttoncontainer = gallery.CLASS("L-picswitcherbuttons");
564			if (!buttoncontainer) gallery.appendChild(CREATE("div", {className:"L-picswitcherbuttons"}));
565			for (let i=0; i<items.length; i++) {
566				// handle video
567				let v = items[i].TAG("video");
568				if (v) {
569					// v.loop = 0;
570					items[i].dataset.isvideo = 1;
571					v.addEventListener('ended',function(){
572						this.parentNode.dataset.finished = 1;
573						this.play();
574					});
575					if (i == 0) {
576						v.addEventListener('loadeddata', function(){
577							this.play();
578						});
579					}
580				}
581				// crate button for me
582				let button = buttoncontainer.appendChild(
583					CREATE("div", {className:"button", style:{width:(1/items.length*100)+"%"}, dataset:{id:i, switcher:Lview.picswitcher.switchers.length}, event:["click", function() {
584							let item = this.PWC("L-picswitcher").CLASSES("L-switcherimage")[Number(this.dataset.id)];
585							let container = Lview.picswitcher.switchers[Number(this.dataset.switcher)];
586							Lview.picswitcher.switchTo(container, item);
587					}]})
588				);
589				// set classes
590				let item = items[i];
591				if (i === 0) {
592					button.classList.add("active");
593				}
594			}
595			let swapdelay = Number(gallery.dataset.swapdelay) || 5000;
596			let autoswap = Number(gallery.dataset.autoswap);
597			if (isNaN(autoswap)) autoswap = false;
598			let foo = {
599				element: gallery,
600				swaptime: Date.now(),
601				swapdelay: swapdelay,
602				autoswap: autoswap,
603				current: items[0]
604			};
605			Lview.picswitcher.switchers.push(foo);
606			if (items.length <= 1) {
607				// disable swapper since there's only 1 element
608				foo.autoswap = false;
609				buttoncontainer.style.display = "none";
610			}
611			
612			// start loop
613			if (!Lview.picswitcher.looping) {
614				Lview.picswitcher.looping = true;
615				requestAnimationFrame(Lview.picswitcher.swapLoop);
616			}
617		},
618		init: function(container) {
619			let galleries = container.CLASSES("L-picswitcher");
620			for (let g=0; g<galleries.length; g++) {
621				Lview.picswitcher.initSwitcher(galleries[g]);
622			}
623		}
624	},
625	dropboxes: {
626		initDropbox: function(dropbox) {
627			dropbox.classList.add("closed");
628			dropbox.CLASS("L-dropboxcontent").style.height = "0";
629			dropbox.CLASS("L-dropboxtitle").addEventListener("click", function() {
630				let dropbox = this.PWC("L-dropbox");
631				if (dropbox.classList.contains("closed")) {
632					// open
633					dropbox.classList.remove("closed");
634					
635					let content = dropbox.CLASS("L-dropboxcontent");
636					content.style = "height:auto;transition:none;";
637					let h = content.offsetHeight;
638					content.style = "height:0;transition:none;";
639					// I hate the web so fucking much
640					requestAnimationFrame(function() {
641					requestAnimationFrame(function() {
642					requestAnimationFrame(function() {
643						content.style = "height:0;";
644						requestAnimationFrame(function() {
645						requestAnimationFrame(function() {
646						requestAnimationFrame(function() {
647							content.style = "height:"+h+"px;";
648						});
649						});
650						});
651					});
652					});
653					});
654					setTimeout(function(){
655						if (!dropbox.classList.contains("closed")) {
656							content.style.height = "";
657						}
658					}, 1000);
659				}
660				else{
661					// close
662					let content = dropbox.CLASS("L-dropboxcontent");
663					content.style.height = content.offsetHeight + "px";
664					// I hate the web so fucking much
665					requestAnimationFrame(function() {
666					requestAnimationFrame(function() {
667					requestAnimationFrame(function() {
668						dropbox.classList.add("closed");
669						content.style.height = "0";
670					});
671					});
672					});
673				}
674			});
675		},
676		init: function(container) {
677			let droppers = container.CLASSES("L-dropbox");
678			for (let dropper of droppers) {
679				Lview.dropboxes.initDropbox(dropper);
680			}
681		}
682	},
683	init: function (container) {
684		Lview.gallery.init(container);
685		Lview.dropboxes.init(container);
686		Lview.picswitcher.init(container);
687	},
688};

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.