
		var storedResponse = storedResponse || {};
		var files = 0;
		var activeUploads = 0;
		var addrBlockLoad = false;

		window.addEventListener('error', function (evt) {

			evt.preventDefault();

			var error = {
				type: evt.error.name,
				msg: evt.message,
				ts: parseFloat(evt.timeStamp),
				file: evt.filename,
				pos: evt.lineno + ':' + evt.colno,
				agent: navigator.userAgent,
				cookies: document.cookie
			};

			new Ajax.Request ('main', {

				parameters: {
					act: 'error',
					error: Object.toJSON(error),
					f: 1593
				}
			});

			if (0 + 0) {
				window.alert (`${error.type}: ${error.msg}\n\n${error.file} (${error.pos})`);
			}

			console.info(error);
		});

		window.setTimeout (sessionTimeout, 60 * 60 * 1000 - performance.now());

		function restoreResponse () {
			if (!storedResponse) return;

			$H(storedResponse).each (function (response) {

				if (!Object.isArray(response.value)) response.value = [response.value];

				response.value.each (function (value) {

					var found = false;

					$$('[name="' + response.key + '"]').each (function (field) {

						if (found || !field.tagName.match (/input|select|textarea/i)) return;

						if (field.tagName.match (/input/i) && field.type.match (/radio|checkbox/i)) {
							if (field.value == value) {
								field.checked = true;
								found = true;
							}
						}
						else if (field.tagName.match (/select/i)) {
							var i = 0;

							$A(field.options).each (function (option) {
								if (!option.value) option.value = option.innerHTML;

								if (option.value == value) {
									option.selected = true;
									found = true;
								}
								i++;
							});
						}
						else if (!field.type.match (/hidden/i)) {
							field.setValue (value);
							found = true;
						}

						if (found && value && 'createEvent' in document) {
							window.setTimeout (function () {
								var evt = document.createEvent('HTMLEvents');
								evt.initEvent('change', false, true);
								field.dispatchEvent(evt);
							}, 500);
						}
					});
				});
			});
		}

		function storeInSession () {

			if (sessionStorageAvilable()) {
				var inputs = $$ ('select,textarea,input:not([type=submit]):not([type=hidden]):not([type=file])');
			
				$H(Form.serializeElements (inputs, {hash: true})).each (function (pair) {
					sessionStorage.setItem (pair.key, JSON.stringify(pair.value));
				});
			}
		}

		function initSessionStorage () {

			if (sessionStorageAvilable()) {

				var storedInSession = {};

				if (sessionStorage.length !== 0) {
					for (var i = 0; i < sessionStorage.length; i++){
						var key = sessionStorage.key(i);
						var value = sessionStorage.getItem(key);

						if (typeof key !== 'undefined' && typeof value !== 'undefined') {
							storedInSession[key] = JSON.parse(value);
						}
					}

					storedResponse = Object.assign (storedResponse, storedInSession);
				}

				$('main').getElements().each (function (el) {
					el.observe ('change', storeInSession); 
				});
			}
		}

		function sessionStorageAvilable () {

			try {
				if ('sessionStorage' in window && window.sessionStorage) {
					const testKey = '__' + Math.round((Math.random() * 1e16)).toString(36);
					sessionStorage.setItem(testKey, testKey);
					sessionStorage.removeItem(testKey);

					return true;
				}
				else {
					return false;
				}
			} catch (e) {

				return false;
			}
		}

		function checkForm () {
			var form = document.forms[0];
			var values = $H(form.serialize(true));
			var errors = {missing : $A(), malformed : $A()};
			var order = $A();

			form.getElements().each(function (field) {

				var value = values.get(field.name);
				var label = form.down('label[for="' + field.id + '"]');
				var name = (label && label.innerHTML) ? label.innerHTML : field.title || field.name;

				if (!order.include(field.name))
					order.push (field.name);

				if (field.hasClassName('mandatory') || field.required) {					

					if (Object.isUndefined(value) || value === null || (Object.isArray(value) && value.length === 0) || (Object.isString(value) && value.match(/^\s*$/))) {

						if (!errors.missing.include(name))
							errors.missing.push(name);

						field.addClassName ('missing');

						return;
					}
					else {
						field.removeClassName ('missing');
					}
				}
				
				if (format = field.readAttribute('stringformat')) {

					format = new RegExp ('^' + format + '$', 'm');
					
					if (!format.test (value)) {

						if (!errors.malformed.include(name))
							errors.malformed.push(name);

						field.addClassName ('malformed');

						return;
					}
					else {
						field.removeClassName ('malformed');
					}
				}
			});

			$('fieldorder').setValue(order.join (','));

			if (errors.missing.length + errors.malformed.length) {
				var message = 'Ooops, please check your inputs:\n';
				errors.missing.each (function (fieldname) {message += '- ' + fieldname + ' is missing\n';});
				errors.malformed.each (function (fieldname) {message += '- ' + fieldname + ' is malformed\n';});

				window.alert (message);

				return false;
			}
			else if (!addrBlockLoad) {

				return false;
			}

			handleButtons (true);

			return true;
		}

		function initWfMapping () {

			const wfFields = $$('[data-wfparam]');
			const fieldsMapped = {};

			if (wfFields && wfFields.length > 0) {

				$$(`input[type="hidden"][name="WF:param"],input[type="hidden"][name="WF:file"]`).each (hidden => {
					hidden.remove();
				});
					
				wfFields.reverse().each (field => {

					const wfParam = field.readAttribute('data-wfparam');
					const fieldName = field.name;
					const value = `${wfParam}:\$${fieldName}`;

					if (!fieldsMapped[value]) {

						$('formfields').insert({top:
							new Element('input', {
								type: 'hidden',
								name: `WF:${(field.type === 'file') ? 'file' : 'param'}`,
								value
							})
						});

						fieldsMapped[value] = true;
					}
				});
			}
		}

		function initWfOptions () {

			const wfCategory = $F($$('input[name="WF:category"]')[0]);
			const wfSelects = $$('select[data-wfparam]').filter(s => s.options.length === 0);

			if (wfCategory && wfSelects.length) {

				for (select of wfSelects) {
					select.insert (
						new Element('option')
					);
					select.insert (
						new Element('option', {disabled: 'disabled'}).update('Loading...')
					);
				}

				new Ajax.Request ('wfoptions', {
                                        parameters : {
                                                category : wfCategory
                                        },
                                        onSuccess : function (response) {
						const wfOptions = response.responseJSON;

						for (const select of wfSelects) {

							select.remove(1);

							for (const option of wfOptions[select.dataset.wfparam]) {
								select.insert (
									new Element('option', {value: option.value}).update(option.label)
								);
							}
						}
					}
				});
			}
		}


		function initUploader (field) {

			var required = (field.required) ? true : false;
			var container = new Element ('div', {className: 'uploader'}).setStyle({'max-width': field.parentNode.getLayout().get('width') + 'px'});
			var input = new Element ('input', {type: 'hidden', name: '%file:' + field.name, title: (field.title) ? field.title : field.name});
			var inner = new Element ('div', {className: 'frminput'});
			var filename = new Element ('div');
			var progress = new Element ('div');
			var button = new Element ('button').update('Browse...');

			if (required) {
				inner.addClassName ('mandatory');
				input.required = true;
			}

			if (sessionStorageAvilable() && (file = sessionStorage.getItem (input.name))) {
				input.value = file;
				file = JSON.parse (file);
				filename.update (file.name);
				progress.update (fileSize (file.size));
			}

			field.insert({before: container.insert(input).insert(inner.insert(filename).insert(progress)).insert(button)}).remove();

			var uploader = new plupload.Uploader({
				flash_swf_url : '/js/plupload/Moxie.swf',
				silverlight_xap_url : '/js/plupload/Moxie.xap',
				multi_selection: false,

				browse_button : button,
				url : '/bin/public/fc/upload',

				init: {
					FilesAdded: function (me, files) {
						filename.update(files[0].name);
						progress.update('0%');
						me.start();
					},

					BeforeUpload: function (me, file) {
						document.fire ('upload:start');
						me.disableBrowse(true);
					},

					UploadProgress: function (me, file) {
						progress.update(file.percent + '%');
					},

					FileUploaded: function (me, file, result) {
						progress.update('100%');
						window.setTimeout (function () {
							progress.update (fileSize (file.size));
						}, 750);
						input.setValue(result.response);
						sessionStorage.setItem (input.name, result.response);
						document.fire ('upload:done');
						me.disableBrowse(false);
					}
				}
			});
			     
			uploader.init();
		}

		function doSubmit () {

			return (checkForm() && activeUploads === 0);
		}

		function sessionTimeout () {

			var refresh = $$('button[name="_refresh"]')[0];

			refresh.onclick = function (e) {
				e.preventDefault();
				window.location.reload();
			};
			refresh.show ();

			handleButtons();
		}

		function handleSubmitDuringUpload () {
			handleButtons ();
		}

		function handleButtons (submitting = false) {

			var refresh = $$('button[name="_refresh"]')[0];
			var submit = $$('input[type=submit]')[0];

			if (activeUploads > 0) {
				submit.title = refresh.title = 'Please wait for all uploads to complete';
				submit.disabled = refresh.disabled = true;
			}
			else if (performance.now() > 60 * 60 * 1000) {
				submit.title = 'Your session has expired. Click refresh button to continue.';
				submit.disabled = true;
				refresh.title = 'Refresh the session. Your inputs will be restored.';
				refresh.disabled = false;
			}
			else if (submitting === true) {
				submit.title = 'Processing... Please wait.';
				submit.disabled = true;
			}
			else {
				submit.title = refresh.title = '';
				submit.disabled = refresh.disabled = false;
			}
		}

		function fileSize (bytes) {
			bytes = parseFloat(bytes);
			var dim = ['', 'K', 'M', 'G', 'T'];
				
			while (bytes > 1024) {
				bytes /= 1024;
				dim.shift();
			}

			return bytes.toFixed (bytes < 10 ? 2 : bytes < 100 ? 1 : 0) + ' ' + dim[0] + 'B';		}

		Date.now = function () {
			return new Date (1723628343000 + performance.now());
		};

		document.observe('upload:start', function () {
			activeUploads++;
			handleSubmitDuringUpload();
		});

		document.observe('upload:done', function () {
			activeUploads--;
			handleSubmitDuringUpload();
		});

		document.observe("dom:loaded", function() {

			if ($$('input[name="WF:category"]').length) {
				initWfMapping();
				initWfOptions();
			}

			$$('input[type=file]').each (initUploader); 

			if ($('addrblock')) {

				$$('input[type=submit]').each(function (btn) {btn.disabled = true;});

				initAddrBlock({
					
					locales: 'en_IE',
					skip: [],
					onSuccess: function () {

						addrBlockLoad = true;
						$$('input[type=submit]').each(function (btn) {btn.disabled = false;});

						['privacy', 'datasharing'].forEach (function (field) {

							if ($('addrblock_' + field)) {
								$('addrblock_' + field).required = true;
								$('addrblock').select('label.label_' + field + ' a').each (function (link) {
									link.target = '_blank';
								});
							}
						});

						if ($('addrblock_em')) $('addrblock_em').type = 'email';

						['co', 'fn', 'na'].forEach (function (f) {
							f = $('addrblock_' + f);
							if (f && f.required) f.pattern = '.*(\\w|[\\u2E80-\\uD7A3]).*(\\w|[\\u2E80-\\uD7A3]).*'; // should work for most characters
						});

						
						['co', 'fn', 'na', 'em', 'privacy', 'datasharing'].forEach (function (f) {

							if (invalidMsg && invalidMsg[f]) {

								var tmpEl = $('addrblock_' + f);
								if (tmpEl) {
									tmpEl.oninvalid = function (e) {
										e.target.setCustomValidity ('');
										if (!e.target.validity.valid) {
											e.target.setCustomValidity (invalidMsg[f]);
										}								
									};

									tmpEl.oninput = function (e) {
										e.target.setCustomValidity ('');
									};
								}
							}
						});

						initSessionStorage ();
						restoreResponse ();
					}
				});
			}
			else {
				initSessionStorage ();
				restoreResponse ();

				addrBlockLoad = true;
			}

			var params = $H(window.location.href.parseQuery());
			params.keys().each (function (key) {
				if (!key.match(/^(f|i|emckey|mkt_tok)$/))
					document.forms[0].insert(new Element ('input', {type: 'hidden', name: key, value: params.get(key)}));
			});

			var now = moment ().format ('X'); 

			$$('.datetoggle').each (function (element) {
				var from = element.readAttribute ('from');
				var until = element.readAttribute ('until');

				if (until.length == 10) until += ' 23:59:59';

				from = (from && moment (from).isValid()) ? moment (from).format ('X') : 0;
				until = (until && moment (until).isValid()) ? moment (until).format ('X') : Number.MAX_VALUE;

				if (from <= now && now <= until) element.removeClassName ('datetoggle');

			});

			var articles = $H();;

			$$('[data-articleno]').each (function (element) {

				var articleno = element.readAttribute ('data-articleno');
				if (articleno && articleno.length > 3) {
					articles.set (articleno, 1);
				}
			});

			if (articles.keys().length) {

				new Ajax.Request ('article', {
					parameters : {
						locales_id : 'en_IE',
						key : articles.keys()
					},
					onSuccess : function (response) {

						$H(response.responseJSON).each (function (set) {

							if (!set.value) return;

							var data = set.value;

							$$('[data-articleno="' + set.key + '"]').each (function (element) {

								var type = 'fill';
								var attribute = 'description';

								['fill', 'append', 'replace'].forEach (function (typ) {
									if (element.readAttribute ('data-' + typ)) {
										type = typ;
										attribute = element.readAttribute ('data-' + typ);
									}
								});
								if (attribute && typeof (data[attribute]) != 'undefined') {

									var isInput = (element.tagName.match(/^(input|textarea|option)$/i)) ? true : false;
									var value = (isInput) ? data[attribute].replace ('"', '"') : data[attribute].replace ('<', '&lt;').replace ('>', '&gt;');

									if (type == 'replace') {

										if (isInput) {
											element.value = data[attribute];
										}
										else {
											element.update (data[attribute]);
										}
										
									}
									else if (type == 'fill') {

										if (isInput && element.value.match (/^\s*$/)) {
											element.value = data[attribute];
										}
										else if (element.innerHTML.match (/^\s*$/)) {
											element.update (data[attribute]);
										}
									}
									else if (type == 'append') {

										if (isInput) {
											element.value += data[attribute];
										}
										else {
											element.update (element.innerHTML + data[attribute]);
										}
									}
								}
							});
						});
					}
				});
			}


			$$('input[type=text][dateformat]').each (function (field) {
				var format = field.readAttribute ('dateformat');
				field.placeholder = format;
				var picker = new Pikaday({
					field: field,
					format: format
				});
			});

			document.forms[0].onsubmit = doSubmit;
			checkAddrForm = function () {};
		});

		var invalidMsg = {
			co : 'Your company name must have at least 2 characters',
			fn : 'Your first name must have at least 2 characters',
			na : 'Your last name must have at least 2 characters',
			em : 'Please provide a valid email address',
			privacy : 'Please confirm to our privacy statement',
			datasharing : 'Please confirm to our data sharing consent',
		};
	