PageSourceSearch

https://findmy.no/wp-content/themes/findmy-landing-theme/assets/js/findmy-offer.js?ver=1.78.0.1791195020

js findmy.no collected 2026-10-06 02:26:06 UTC 6,475 bytes, 260 lines download raw bytes

1/**
2 * The offer form's inline validation.
3 *
4 * The rules mirror findmy_offer_validate() so a farmer sees the same message
5 * the server would have sent back, without the round trip. The server still
6 * validates every submission — this only stops one that is certain to fail, and
7 * it never reports success on its own.
8 */
9( function () {
10	'use strict';
11
12	var EMAIL = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
13
14	function init( form ) {
15		var messages = {};
16
17		// The server rendered its own messages into these; reuse the wording.
18		form.querySelectorAll( '[data-fml-error]' ).forEach( function ( el ) {
19			messages[ el.getAttribute( 'data-fml-error' ) ] = el.dataset.fmlMessage || el.textContent.trim();
20		} );
21
22		function show( field, message ) {
23			var el = form.querySelector( '[data-fml-error="' + field + '"]' );
24
25			if ( ! el ) {
26				return;
27			}
28
29			if ( message ) {
30				el.textContent = message;
31				el.hidden = false;
32			} else {
33				el.hidden = true;
34			}
35		}
36
37		function value( name ) {
38			var nodes = form.querySelectorAll( '[name="' + name + '"]' );
39
40			if ( ! nodes.length ) {
41				return '';
42			}
43
44			if ( 'radio' === nodes[ 0 ].type ) {
45				var on = form.querySelector( '[name="' + name + '"]:checked' );
46
47				return on ? on.value : '';
48			}
49
50			return nodes[ 0 ].value;
51		}
52
53		function check() {
54			var errors = {};
55
56			if ( ! value( 'dyr' ) ) {
57				errors.dyr = messages.dyr;
58			}
59
60			if ( ! value( 'antall' ) || Number( value( 'antall' ) ) < 1 ) {
61				errors.antall = messages.antall;
62			}
63
64			if ( ! value( 'cov' ) ) {
65				errors.cov = messages.cov;
66			}
67
68			if ( ! value( 'navn' ).trim() ) {
69				errors.navn = messages.navn;
70			}
71
72			if ( ! EMAIL.test( value( 'epost' ).trim() ) ) {
73				errors.epost = messages.epost;
74			}
75
76			if ( ! value( 'land' ) ) {
77				errors.land = messages.land;
78			}
79
80			return errors;
81		}
82
83		form.addEventListener( 'submit', function ( event ) {
84			var errors = check();
85			var fields = Object.keys( errors );
86
87			[ 'dyr', 'antall', 'cov', 'navn', 'epost', 'land' ].forEach( function ( field ) {
88				show( field, errors[ field ] );
89			} );
90
91			if ( ! fields.length ) {
92				return;
93			}
94
95			event.preventDefault();
96
97			var first = form.querySelector( '[name="' + fields[ 0 ] + '"]' );
98
99			if ( first ) {
100				first.focus();
101				first.scrollIntoView( { block: 'center', behavior: 'smooth' } );
102			}
103		} );
104
105		// Clear a message as soon as the farmer fixes the field.
106		form.addEventListener( 'input', function ( event ) {
107			var name = event.target.name;
108
109			if ( name && messages.hasOwnProperty( name ) ) {
110				show( name, '' );
111			}
112		} );
113
114		form.addEventListener( 'change', function ( event ) {
115			var name = event.target.name;
116
117			if ( name && messages.hasOwnProperty( name ) ) {
118				show( name, '' );
119			}
120		} );
121	}
122
123	/*
124	 * The CF7 version of the form. CF7 validates on the server and drops a tip
125	 * into each field's wrapper; this names those fields under the button, takes
126	 * the farmer to the first one, and clears a tip as soon as it is fixed —
127	 * CF7 does not do that for the theme's own tiles and cards.
128	 */
129
130	// A field's label as the farmer saw it, without the * or "(valgfritt)".
131	function cf7Label( wrap ) {
132		var field = wrap.closest( '.fml-field' );
133		var group = wrap.closest( '.fml-offerform__group' );
134		var el    = field ? field.querySelector( ':scope > span' )
135			: group ? group.querySelector( '.fml-offerform__labels span' )
136			: null;
137
138		if ( ! el ) {
139			return '';
140		}
141
142		el = el.cloneNode( true );
143		el.querySelectorAll( 'abbr, em' ).forEach( function ( node ) {
144			node.remove();
145		} );
146
147		return el.textContent.trim();
148	}
149
150	function cf7Invalid( form ) {
151		return Array.prototype.map.call( form.querySelectorAll( '.wpcf7-not-valid-tip' ), function ( tip ) {
152			return tip.closest( '.wpcf7-form-control-wrap' );
153		} ).filter( Boolean );
154	}
155
156	function cf7Summary( form ) {
157		var output = form.querySelector( '.wpcf7-response-output' );
158
159		if ( ! output ) {
160			return;
161		}
162
163		var list  = output.querySelector( '.fml-offerform__missing' );
164		var names = cf7Invalid( form ).map( cf7Label ).filter( function ( name, i, all ) {
165			return name && all.indexOf( name ) === i;
166		} );
167
168		if ( ! names.length ) {
169			if ( list ) {
170				list.remove();
171			}
172			return;
173		}
174
175		if ( ! list ) {
176			list = document.createElement( 'span' );
177			list.className = 'fml-offerform__missing';
178			output.appendChild( list );
179		}
180
181		list.textContent = names.join( ', ' );
182	}
183
184	function cf7Fixed( control ) {
185		var wrap = control.closest( '.wpcf7-form-control-wrap' );
186
187		if ( 'radio' === control.type ) {
188			return !! wrap.querySelector( 'input:checked' );
189		}
190
191		if ( 'email' === control.type ) {
192			return EMAIL.test( control.value.trim() );
193		}
194
195		if ( 'number' === control.type ) {
196			return Number( control.value ) >= 1;
197		}
198
199		return '' !== control.value.trim();
200	}
201
202	function cf7Clear( event ) {
203		var control = event.target;
204		var form    = control.form;
205		var wrap    = control.closest( '.wpcf7-form-control-wrap' );
206
207		if ( ! form || ! wrap || ! wrap.querySelector( '.wpcf7-not-valid-tip' ) || ! cf7Fixed( control ) ) {
208			return;
209		}
210
211		wrap.querySelectorAll( '.wpcf7-not-valid-tip' ).forEach( function ( tip ) {
212			tip.remove();
213		} );
214		wrap.querySelectorAll( '.wpcf7-not-valid' ).forEach( function ( el ) {
215			el.classList.remove( 'wpcf7-not-valid' );
216			el.setAttribute( 'aria-invalid', 'false' );
217		} );
218
219		cf7Summary( form );
220	}
221
222	function cf7Init( form ) {
223		form.addEventListener( 'input', cf7Clear );
224		form.addEventListener( 'change', cf7Clear );
225
226		form.addEventListener( 'wpcf7invalid', function () {
227			// CF7 writes its own message into the response line after this event
228			// fires, so wait for it before adding the list.
229			setTimeout( function () {
230				var wrap = cf7Invalid( form )[ 0 ];
231
232				cf7Summary( form );
233
234				if ( ! wrap ) {
235					return;
236				}
237
238				var control = wrap.querySelector( 'input:checked, input, textarea, select' );
239				var target  = wrap.closest( '.fml-field' ) || wrap.closest( '.fml-offerform__group' ) || wrap;
240
241				target.scrollIntoView( { block: 'center', behavior: 'smooth' } );
242
243				if ( control ) {
244					control.focus( { preventScroll: true } );
245				}
246			}, 0 );
247		} );
248	}
249
250	function boot() {
251		document.querySelectorAll( '[data-fml-offer]' ).forEach( init );
252		document.querySelectorAll( '.fml-offerform__cf7 form' ).forEach( cf7Init );
253	}
254
255	if ( 'loading' === document.readyState ) {
256		document.addEventListener( 'DOMContentLoaded', boot );
257	} else {
258		boot();
259	}
260}() );

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.