PageSourceSearch

https://www.meierijstad.nl/js/simpleform.js

js meierijstad.nl collected 2026-10-02 08:06:34 UTC 23,446 bytes, 589 lines download raw bytes

1// Tracks whether the visitor has made unsaved changes to a simpleform, so we can warn
2// before navigating away (back button, closing the tab, following a link, refreshing).
3// Reset to false on every intentional submit within applySimpleforms below, so paging
4// through the wizard (Volgende/Vorige/Verzenden/Ga naar pagina) never triggers the warning.
5var simpleFormDirty = false;
6
7window.addEventListener('beforeunload', function(event) {
8	if (simpleFormDirty) {
9		// Standard-compliant browsers require preventing the default action.
10		event.preventDefault();
11		// Included for legacy support in older browsers. The actual text is ignored by
12		// modern browsers, which show their own fixed "changes may not be saved" message.
13		event.returnValue = '';
14	}
15});
16
17var SimpleForm = Class.create({
18	form: null,
19	questionSelector: '.question',
20	inputFieldSelector: '.inputfield',
21	hintElementSelector: 'span.hint',
22	initialize: function(form) {
23		this.form = $(form);
24		this.prepareForm();
25		this.form.select(this.questionSelector).each(this.prepareQuestion.bind(this));
26		// focus the right element:
27		try {
28			var errorContainer;
29			if( (errorContainer = this.form.down(this.questionSelector + '.error')) ) {
30				errorContainer.down(this.inputFieldSelector).activate();
31			}
32		} catch(e) { }
33	},
34	prepareForm: function prepareForm() {
35		var warningp = new Element('h2', { class: "warning", role:"alert" }).update("U heeft een vraag / aantal vragen niet of niet correct ingevuld.");
36		var warningul = new Element('ul', { class: "warningslist" });
37		var warnings = this.form.select("ul.warnings");
38		if(warnings.length > 0) {
39			this.form.insert({before: warningp});
40			this.form.insert({before: warningul});
41
42			warnings.each(function (element) {
43
44        var questionWrapper = element.up(".question");
45        var questionText = questionWrapper.querySelector('.labeltitle').innerHTML;
46        if(questionText)  {
47          var hintElem = questionWrapper.down("span.hint");
48          if(hintElem){
49            hintElem.removeClassName("hidden").addClassName("donthide")
50				}
51				}
52				element.select("li").each(function (elementLi) {
53					var topWarningElement = elementLi.clone(true);
54					topWarningElement.update(questionText + ": " + topWarningElement.innerHTML);
55					warningul.insert(topWarningElement);
56				});
57			});
58		}
59	},
60	prepareQuestion: function prepareQuestion(container) {
61		var hint, firstElement, inputField = container.down(this.inputFieldSelector), readOnly = false,
62		hideHint = function() { hint.addClassName('hidden'); }
63		if(inputField) {
64			readOnly = inputField.readAttribute('readonly') === 'readonly';
65		}
66		if(!container.next(this.questionSelector)) {
67			container.addClassName('last-child');
68		}
69		if( (hint = container.down(this.hintElementSelector)) ) {
70			firstElement = container.down(this.inputFieldSelector);
71			// IE triggers click when focus() is called:
72			hint.ignoreClick = false;
73			if(!hint.hasClassName("donthide")) {
74				hint.addClassName('hidden').observe('click', hideHint);
75			}
76
77			container.select(this.inputFieldSelector)
78				.invoke('observe', 'click', function() { if(!hint.ignoreClick) { hideHint(); } hint.ignoreClick = false; })
79				.invoke('observe', 'blur', hideHint)
80				.invoke('observe', 'focus', hideHint);
81			
82			// Place the hint text after the correct html tag.
83			var tag;
84			if (container.hasClassName("checkfields")) {
85				tag = "legend";
86			} else if (container.hasClassName("timefield")) {
87				tag = "legend";
88			} else {
89				tag = "label";
90			}
91			
92			var labelText = container.querySelector('.labeltitle').innerHTML;
93			container.down(tag).after(
94				new Element('button', {href: '#' + firstElement.identify(), 'aria-expanded':"false"}).addClassName('hint-toggle').update('<span>Toon de helptekst voor de vraag: '+labelText+'</span>').observe('click', function showHint(event) {
95					var anchor = event.findElement('button'), offsetLeft = 0, checkedElement;
96					event.stop();
97					if(!event.defaultPrevented) {
98						anchor.href = '#';
99					}
100					var ariatoggle = anchor.getAttribute("aria-expanded");
101					if (ariatoggle === "true") {
102						ariatoggle = "false";
103						anchor.removeAttribute("aria-describedby");
104						hint.addClassName('hidden');
105					} else {
106						ariatoggle = "true";
107						anchor.setAttribute("aria-describedby", 'hint-' + firstElement.identify()) ;
108						hint.removeClassName('hidden');
109					}
110					anchor.setAttribute("aria-expanded", ariatoggle);
111				}.bind(this)).observe('blur', function hideHint(event) {
112					var anchor = event.findElement('button'), offsetLeft = 0, checkedElement;
113					event.stop();
114					anchor.setAttribute("aria-expanded", "false");
115					anchor.removeAttribute("aria-describedby");
116					hint.addClassName('hidden');
117				}.bind(this))
118			);
119		}
120		if(container.hasClassName('change-submit')) {
121			container.select(this.inputFieldSelector).invoke('observe', 'change', function submitForm(event) {
122				this.form.submit();
123			}.bind(this));
124		}
125		if(container.hasClassName('password') && container.hasClassName('strength-indicator') && !readOnly) {
126			this.addStrengthIndicator(inputField);
127		}
128		if(container.hasClassName('datefield') && !readOnly) {
129				// rs_reserve prevents ReadSpeaker altering the code.
130				container.down('input').addClassName('rs_preserve');
131				this.addCalendar(container);
132			}
133		if(container.hasClassName('datefield_week') && !readOnly) {
134				this.addCalendar_week(container);
135			}
136		if(container.hasClassName('timefield') && !readOnly) {
137			this.addTime(container);
138		}
139		if(container.hasClassName('geofield')) {
140			this.addMap(container);
141		}
142		if(container.hasClassName('webeditor')) {
143			this.addWebeditor.bind(this).defer(container);
144		}
145		// convert select to calendar:
146		if(container.hasClassName('calendarselect')) {
147			new Calendar({
148				targetInput: container.down(this.inputFieldSelector),
149				container: container,
150				hideTargetInput: true,
151				sourceFormat: 'dd-MM-yyyy',
152				showWeeks: false,
153				className: 'inline-calendar'
154			});
155		}
156	},
157	/**
158	 * Based on the implementation of Jeff Todnem (http://www.todnem.com/)
159	 */
160	calculatePasswordStrength: function calculatePasswordStrength(pwd) {
161		var nLength = pwd.length, nAlphaUC = 0, nAlphaLC = 0, nNumber = 0, nSymbol = 0, nMidChar = 0,
162		nAlphasOnly = 0, nNumbersOnly = 0, nUnqChar = 0, nRepChar = 0, nRepInc = 0, nConsecAlphaUC = 0,
163		nConsecAlphaLC = 0, nConsecNumber = 0, nConsecSymbol = 0, nConsecCharType = 0, nSeqAlp
163ha = 0,
164		nSeqNumber = 0, nSeqSymbol = 0, nSeqChar = 0, nMultMidChar = 2, nMultConsecAlphaUC = 2,
165		nMultConsecAlphaLC = 2, nMultConsecNumber = 2, nMultSeqAlpha = 3, nMultSeqNumber = 3,
166		nMultSeqSymbol = 3, nMultLength = 4, nMultNumber = 4, nMultSymbol = 6, nTmpAlphaUC = "",
167		nTmpAlphaLC = "", nTmpNumber = "", nTmpSymbol = "", nScore = parseInt(nLength * nMultLength),
168		sAlphas = "abcdefghijklmnopqrstuvwxyz", sNumerics = "01234567890", sSymbols = ")!@#$%^&*()",
169		arrPwd = pwd.replace(/\s+/g,"").split(/\s*/), arrPwdLen = arrPwd.length, a, s, sFwd, sRev,
170		strReverse = function(string) {
171			var newstring = "", s;
172			for (s = 0; s < string.length; s++) {
173				newstring = string.charAt(s) + newstring;
174			}
175			return newstring;
176		};
177
178		/* Loop through password to check for Symbol, Numeric, Lowercase and Uppercase pattern matches */
179		for (a = 0; a < arrPwdLen; a++) {
180			if (arrPwd[a].match(/[A-Z]/g)) {
181				if (nTmpAlphaUC !== "") { if ((nTmpAlphaUC + 1) == a) { nConsecAlphaUC++; nConsecCharType++; } }
182				nTmpAlphaUC = a;
183				nAlphaUC++;
184			}
185			else if (arrPwd[a].match(/[a-z]/g)) {
186				if (nTmpAlphaLC !== "") { if ((nTmpAlphaLC + 1) == a) { nConsecAlphaLC++; nConsecCharType++; } }
187				nTmpAlphaLC = a;
188				nAlphaLC++;
189			}
190			else if (arrPwd[a].match(/[0-9]/g)) {
191				if (a > 0 && a < (arrPwdLen - 1)) { nMidChar++; }
192				if (nTmpNumber !== "") { if ((nTmpNumber + 1) == a) { nConsecNumber++; nConsecCharType++; } }
193				nTmpNumber = a;
194				nNumber++;
195			}
196			else if (arrPwd[a].match(/[^a-zA-Z0-9_]/g)) {
197				if (a > 0 && a < (arrPwdLen - 1)) { nMidChar++; }
198				if (nTmpSymbol !== "") { if ((nTmpSymbol + 1) == a) { nConsecSymbol++; nConsecCharType++; } }
199				nTmpSymbol = a;
200				nSymbol++;
201			}
202			/* Internal loop through password to check for repeat characters */
203			var bCharExists = false;
204			for (var b=0; b < arrPwdLen; b++) {
205				if (arrPwd[a] == arrPwd[b] && a != b) { /* repeat character exists */
206					bCharExists = true;
207					nRepInc += Math.abs(arrPwdLen/(b-a));
208				}
209			}
210			if (bCharExists) {
211				nRepChar++;
212				nUnqChar = arrPwdLen-nRepChar;
213				nRepInc = (nUnqChar) ? Math.ceil(nRepInc/nUnqChar) : Math.ceil(nRepInc);
214			}
215		}
216
217		/* Check for sequential alpha string patterns (forward and reverse) */
218		for (s = 0; s < 23; s++) {
219			sFwd = sAlphas.substring(s,parseInt(s+3));
220			sRev = strReverse(sFwd);
221			if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { nSeqAlpha++; nSeqChar++;}
222		}
223
224		/* Check for sequential numeric string patterns (forward and reverse) */
225		for (s = 0; s < 8; s++) {
226			sFwd = sNumerics.substring(s,parseInt(s+3));
227			sRev = strReverse(sFwd);
228			if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { nSeqNumber++; nSeqChar++;}
229		}
230
231		/* Check for sequential symbol string patterns (forward and reverse) */
232		for (s = 0; s < 8; s++) {
233			sFwd = sSymbols.substring(s,parseInt(s+3));
234			sRev = strReverse(sFwd);
235			if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { nSeqSymbol++; nSeqChar++;}
236		}
237
238	/* Modify overall score value based on usage vs requirements */
239
240		/* General point assignment */
241		if (nAlphaUC > 0 && nAlphaUC < nLength) {
242			nScore = parseInt(nScore + ((nLength - nAlphaUC) * 2));
243		}
244		if (nAlphaLC > 0 && nAlphaLC < nLength) {
245			nScore = parseInt(nScore + ((nLength - nAlphaLC) * 2));
246		}
247		if (nNumber > 0 && nNumber < nLength) {
248			nScore = parseInt(nScore + (nNumber * nMultNumber));
249		}
250		if (nSymbol > 0) {
251			nScore = parseInt(nScore + (nSymbol * nMultSymbol));
252		}
253		if (nMidChar > 0) {
254			nScore = parseInt(nScore + (nMidChar * nMultMidChar));
255		}
256
257		/* Point deductions for poor practices */
258		if ((nAlphaLC > 0 || nAlphaUC > 0) && nSymbol === 0 && nNumber === 0) {  // Only Letters
259			nScore = parseInt(nScore - nLength);
260			nAlphasOnly = nLength;
261		}
262		if (nAlphaLC === 0 && nAlphaUC === 0 && nSymbol === 0 && nNumber > 0) {  // Only Numbers
263			nScore = parseInt(nScore - nLength);
264			nNumbersOnly = nLength;
265		}
266		if (nRepChar > 0) {  // Same character exists more than once
267			nScore = parseInt(nScore - nRepInc);
268		}
269		if (nConsecAlphaUC > 0) {  // Consecutive Uppercase Letters exist
270			nScore = parseInt(nScore - (nConsecAlphaUC * nMultConsecAlphaUC));
271		}
272		if (nConsecAlphaLC > 0) {  // Consecutive Lowercase Letters exist
273			nScore = parseInt(nScore - (nConsecAlphaLC * nMultConsecAlphaLC));
274		}
275		if (nConsecNumber > 0) {  // Consecutive Numbers exist
276			nScore = parseInt(nScore - (nConsecNumber * nMultConsecNumber));
277		}
278		if (nSeqAlpha > 0) {  // Sequential alpha strings exist (3 characters or more)
279			nScore = parseInt(nScore - (nSeqAlpha * nMultSeqAlpha));
280		}
281		if (nSeqNumber > 0) {  // Sequential numeric strings exist (3 characters or more)
282			nScore = parseInt(nScore - (nSeqNumber * nMultSeqNumber));
283		}
284		if (nSeqSymbol > 0) {  // Sequential symbol strings exist (3 characters or more)
285			nScore = parseInt(nScore - (nSeqSymbol * nMultSeqSymbol));
286		}
287		if (nScore > 100) { nScore = 100; } else if (nScore < 0) { nScore = 0; }
288		return nScore;
289	},
290	addStrengthIndicator: function addStrengthIndicator(inputField) {
291		var indicator = new Element('span').addClassName('strength invisible').writeAttribute("aria-live", "polite").writeAttribute("role", "live").update('-'),
292		strengths = {
293			veryweak: 'Zeer zwak',
294			weak: 'Zwak',
295			good: 'Goed',
296			strong: 'Sterk',
297			verystrong: 'Zeer sterk'
298		};
299		inputField.insert({ after: indicator })
300		.observe('keyup', function updateStrength() {
301			var value = $F(inputField), score, strength;
302			if(value.empty()) {
303				indicator.className = 'strength invisible';
304			} else {
305				score = this.calculatePasswordStrength(value);
306				if (score >= 0 && score < 20) { strength = 'veryweak'; }
307				else if (score >= 20 && score < 40) { strength = 'weak'; }
308				else if (score >= 40 && score < 60) { strength = 'good'; }
309				else if (score >= 60 && score < 80) { strength = 'strong'; }
310				else if (score >= 80 && score <= 100) { strength = 'verystrong'; }
311				if(score === 0) { score = 1; }
312				indicator.setStyle({ width: score + '%' }).update(strengths[strength]).className = 'strength ' + strength;
313			}
314		}.bind(this));
315	},
316	addCalendar: function addCalendar(container) {
317		var anchor = container.down('.calendar_button'),
318			target = container.down(this.inputFieldSelector);
319		if (!anchor) {
320			// rs_reserve prevents ReadSpeaker altering the code.
321			anchor = new Element('button', { 'class': 'calendar-button rs_preserve', href: 'calendar:show' }).update('Kalender')
322			target.insert({
323				after: anchor
324			});
325		}
326		new Calendar({
327			container: container,
328			anchor: anchor,
329			targetInput: target,
330			returnFormat: 'dd-MM-yyyy',
331			hideTargetInput: false,
332			showWeeks: false,
333			className: 'popup-calendar'
334		});
335	},
336	addCalendar_week: function addCalendar_week(container) {
337		var anchor = container.down('.calendar_button'),
338			target = container.down(this.inputFieldSelector);
339		if (!anchor) {
340			// rs_reserve prevents ReadSpeaker altering the code.
341			anchor = new Element('button', { 'class': 'calendar-button rs_preserve', href: 'calendar:show' }).update('Kalender')
342			target.insert({
343				after: anchor
344			});
345		}
346		new WeekSelectCalendar({
347			container: container,
348			anchor: anchor,
349			targetInput: target,
350			sourceFormat: 'dd-MM-yyyy',
351			returnFormat: 'dd-MM-yyyy',
352			hideTargetInput: false,
353			showWeeks: true,
354			className: 'popup-calendar'
355		});
356	},
357	addTime: function addTime(container) {
358		// rs_reserve prevents ReadSpeaker altering the code.
359		container.down('div').insert(new Element('button').addClassName('set rs_preserve').writeAttribute("tabindex", "0").update('Nu').observe('click', function setNow(event) {
360			event.stop();
361			var selects = event.findElement(this.questionSelector).select('select')
362				date = new Date();
363			if (selects.size() === 2) {
364				[date.getHours(), date.getMinutes()].each(function (value, index) {
365					selects[index].setValue(value);
366				});
367			}
368		}.bind(this)));
369	},
370/*	addWebeditor: function addWebeditor(container) {
371		var textarea = container.down('textarea');
372		if (typeof CKEDITOR != 'undefined') {
373			if (!CKEDITOR.instances[textarea.name]) {
374				CKEDITOR.replace(textarea, {
375					customConfig: '', // don't load the external config.js file
376					contentsCss: [stylesPath + '/content.css.jsp', contextpath + '/js/ckeditor/gv-contents.css'],
377					baseFloatZIndex: 200005, // the dsedit modal mask is z-indexed at 200000
378					toolbar: 'simpleform',
379					height: 200,
380					skin: 'moono-lisa',
381					// language
382					language: 'nl',
383					// toolbar presets
384					toolbar_simpleform: [
385						{ name: 'clipboard', items: ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo'] },
386						{ name: 'editing', items: ['Find', 'Replace'] },
387						{ name: 'insert', items: ['Image', 'Table', 'SpecialChar'] },
388						{ name: 'styles', items: ['Styles', 'Format', 'gvLanguageSelect'] },
389						'/',
390						{ name: 'basicstyles', items: ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', 'Abbr', '-', 'RemoveFormat'] },
391						{ name: 'links', items: ['Link', 'Unlink', 'Anchor'] },
392						{ name: 'paragraph', items: ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl'] }
393					],
394					// style select presets:
395					stylesSet: [
396						{ name: 'Call-to-action',element:'a', attributes: { 'class': 'calltoaction' }},
397						{ name: 'CTA nieuw venster + ander domein',element:'a', attributes: { 'class': 'calltoaction new-window external' }},
398						{ name: 'CTA nieuw venster',element:'a', attributes: { 'class': 'calltoaction new-window' }},
399						{ name: 'CTA ander domein',element:'a', attributes: { 'class': 'calltoaction external' }},
400						{ name: 'Strong Emphasis', element: 'strong' },
401						{ name: 'Emphasis', element: 'em' },
402						//{ name: 'Computer Code', element: 'code' },
403						//{ name: 'Keyboard Phrase', element: 'kbd' },
404						//{ name: 'Sample Text', element: 'samp' },
405						//{ name: 'Variable', element: 'var' },
406						//{ name: 'Deleted Text', element: 'del' },
407						//{ name: 'Inserted Text', element: 'ins' },
408						{ name: 'Cited Work', element: 'cite' },
409						{ name: 'Inline Quotation', element: 'q' }
410					],
411					// adjustments for valid (and semantic) output:
412					coreStyles_bold: { element: 'span', attributes: { 'class': 'bold' }},
413					coreStyles_italic: { element: 'span', attributes: { 'class': 'italic' }},
414					coreStyles_underline: { element: 'span', attributes: { 'class': 'underline' }},
415					coreStyles_strike: { element: 'span', attributes: { 'class': 'strike' }},
416					// add abbr and acronym to the tags to be removed by removeFormat:
417					removeFormatTags: CKEDITOR.config.removeFormatTags + ',abbr,acronym',
418					disableObjectResizing: true,
419					// editor resizing:
420					resize_dir: 'vertical',
421					toolbarCanCollapse: false
422				});
423			}
424		} else {
425			throw "CK Editor is not loaded.";
426		}
427	}, */
428	addMap: function addMap(container) {
429		var mapElement = container.down('div.google-map'),
430		questionId = null,
431		markerData = $A(),
432		map = new SimpleformMap(mapElement, {
433			displayElement: container.down('p.display'),
434			addressInput: container.down('input.address'),
435			centerInput: container.down('input.center'),
436			valueInput: container.down('input.values'),
437			zoomInput: container.down('input.zoom'),
438			onCreate: function onCreate() {
439				var isEditable = true, originalCenter = this.getCenter(), originalZoom = this.getZoom(), zoomRevert = null, centerRevert = null;
440				if(this.valueInput.readAttribute('readonly')) {
441					isEditable = false;
442				}
443				if(isEditable) {
444					// click observer:
445					google.maps.event.addListener(this.map, 'click', function mapClickHandler(gEvent) {
446						if(this.addSimpleFormMarker(gEvent.latLng)) {
447							this.geocodelatLng(gEvent.latLng, this.displayAddress.bind(this));
448						}
449					}.bind(this));
450					// zoom observer:
451					google.maps.event.addListener(this.map, 'zoom_changed', function saveCenter() {
452						this.zoomInput.setValue(this.getZoom());
453					}.bind(this));
454					// center observer:
455					google.maps.event.addListener(this.map, 'center_changed', function saveCenter() {
456						this.centerInput.setValue(this.getCenter().toString());
457					}.bind(this));
458				} else {
459					google.maps.event.addListener(this.map, 'zoom_changed', function saveCenter() {
460						if(zoomRevert) { clearTimeout(zoomRevert); }
461						zoomRevert = this.setZoom.bind(this).delay(2, originalZoom);
462					}.bind(this));
463					google.maps.event.addListener(this.map, 'dragend', function saveCenter() {
464						if(centerRevert) { clearTimeout(centerRevert); }
465						centerRevert = this.map.panTo.bind(this.map).delay(2, originalCenter);
466					}.bind(this));
467				}
468
469				if( (questionId = mapElement.readAttribute('id')) && window.simpleFormMaps[questionId]) {
470					markerData = window.simpleFormMaps[questionId];
471				}
472				// load data:
473				markerData.each(function loadMarker(m) {
474					var draggable = m.selected;
475					if(!isEditable) {
476						draggable = false;
477					}
478					this.addSimpleFormMarker(this.strToLatLng(m.coords), m.text, draggable);
479				}.bind(this));
480			}
481		});
482	}
483});
484
485/**
486 * Google Map class specificly for Simpleforms
487 */
488var SimpleformMap = Class.create(GoogleMap, {
489	maxMarkers: -1,
490	initialize: function initialize($super, container, params) {
491		$super(container, params);
492		this.maxMarkers = (e = this.container.down('span.max-markers')) ? parseInt(e.hide().innerHTML) : this.maxMarkers;
493	},
494	// display geocoding results:
495	displayAddress: function displayAddress(results) {
496		if(results[0]) {
497			var display = results[0].formatted_address;
498			this.displayElement.update(this.displayElement.readAttribute('title') + ': ' + display);
499			this.addressInput.setValue(display);
500		}
501	},
502	// saves the marker data:
503	saveMarkers: function saveMarkers() {
504		var data = $A();
505		this.markers.each(function getSaveData(m) {
506			data.push(m.position.toString() + ';' + m.getTitle() + ';' + m.getDraggable());
507		});
508		this.valueInput.setValue( data.join('|') );
509	},
510	addSimpleFormMarker: function addSimpleFormMarker(latLng, title, draggable) {
511		if(this.maxMarkers === -1 || this.markers.size() < this.maxMarkers) {
512			if(typeof draggable === "undefined") {
513				draggable = true;
514			}
515			var marker = this.addMarker(latLng, {
516				title: title || '',
517				draggable: draggable
518			});
519
520			if(draggable) {
521				// click marker observer (show address)
522				google.maps.event.addListener(marker, 'click', function clickHandler(gEvent) {
523					this.geocodelatLng(gEvent.latLng, this.displayAddress.bind(this));
524				}.bind(this));
525
526				// double click marker observer (remove marker and save)
527				google.maps.event.addListener(marker, 'dblclick', function clickHandler(gEvent) {
528					for(var i = 0; i < this.markers.size(); i++) {
529						if(this.markers[i].position.equals(gEvent.latLng)) {
530							this.markers[i].setMap(null);
531							this.markers[i] = null;
532						}
533					}
534					this.markers = this.markers.compact();
535					this.saveMarkers();
536				}.bind(this));
537
538				// drag marker observer (show address and save)
539				google.maps.event.addListener(marker, 'dragend', function dragEndHandler(gEvent) {
540					this.geocodelatLng(gEvent.latLng, this.displayAddress.bind(this));
541					this.saveMarkers();
542				}.bind(this));
543			}
544
545			this.saveMarkers();
546			return true;
547		} else {
548			if(this.errorWindow) {
549				this.errorWindow.close();
550			}
551			this.errorWindow = new google.maps.InfoWindow({
552				content: "<p style='display: block; height: 50px;'>- Verwijder punten door er op te dubbel klikken.<br />- Klik en versleep om het punt te verplaatsen.<br />- U kunt maximaal <strong>" + this.maxMarkers + "</strong> punt(en) toevoegen.<br /></p>",
553				position: latLng
554			});
555			this.errorWindow.open(this.map);
556			return false;
557		}
558	}
559});
560document.observe('dom:loaded', function applySimpleforms() {
561	$$('form.simpleform').each(function(form) {
562		if (!form.hasClassName('extended')) {
563			new SimpleForm(form);
564			// Any intentional submit (Volgende/Vorige/Verzenden/Ga naar pagina) is a legitimate
565			// navigation, not data loss - clear the flag so it doesn't trigger the warning.
566			form.observe('submit', function() {
567				simpleFormDirty = false;
568			});
569			// The "ga naar pagina"/"terug naar" history form (simpleform's .formhistory class,
570			// decisiontree's identical select[name=goto] form) is page navigation, not form data
571			// - exclude it from dirty-tracking regardless of which template rendered it.
572			if (!form.hasClassName('formhistory') && !form.down('select[name="goto"]')) {
573				form.observe('input', function() {
574					simpleFormDirty = true;
575				}).observe('change', function() {
576					simpleFormDirty = true;
577				});
578			}
579		}
580	});
581	var link;
582		if( (link = $$("body")[0].down('.simpleform.end .target-link a[rel~=autovisit]')) ) {
583		(function(url) {
584			if(url) {
585				window.location.href = url;
586			}
587		}.curry(link.readAttribute('href'))).delay(1);
588	}
589});

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.